Skip to content

Commit

Permalink
Domain contact management API (#241)
Browse files Browse the repository at this point in the history
This PR introduces 5 new endpoints that are part of the domain contact management API that was introduced in dnsimple/dnsimple-developer#501.

The following endpoints have been added:
- **listRegistrantChanges**: GET `/{account}/registrar/registrant_changes`
- **createRegistrantChange**: POST `/{account}/registrar/registrant_changes`
- **checkRegistrantChange**: POST `/{account}/registrar/registrant_changes/check`
- **getRegistrantChange**: GET `/{account}/registrar/registrant_changes/{registrantchange}`
- **deleteRegistrantChange**: DELETE `/{account}/registrar/registrant_changes/{registrantchange}`

Belongs to dnsimple/dnsimple-business#1729
Belongs to https://github.com/dnsimple/dnsimple-external-integrations/issues/18
  • Loading branch information
DXTimer authored Dec 7, 2023
1 parent 9e76ce2 commit 1c72bfb
Show file tree
Hide file tree
Showing 13 changed files with 417 additions and 1 deletion.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## main

FEATURES:

- NEW: Added `Dnsimple.Registrar.check_registrant_change` to retrieves the requirements of a registrant change. (dnsimple/dnsimple-elixir#241)
- NEW: Added `Dnsimple.Registrar.get_registrant_change` to retrieves the details of an existing registrant change. (dnsimple/dnsimple-elixir#241)
- NEW: Added `Dnsimple.Registrar.create_registrant_change` to start registrant change. (dnsimple/dnsimple-elixir#241)
- NEW: Added `Dnsimple.Registrar.list_registrant_changes` to lists the registrant changes for a domain. (dnsimple/dnsimple-elixir#241)
- NEW: Added `Dnsimple.Registrar.delete_registrant_change` to cancel an ongoing registrant change from the account. (dnsimple/dnsimple-elixir#241)

## 3.5.0

FEATURES:
Expand Down
25 changes: 25 additions & 0 deletions lib/dnsimple/registrant_change.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
defmodule Dnsimple.RegistrantChange do
@moduledoc """
Represents a registrant change.
Developer preview: this API is currently in open beta and subject to change.
"""
@moduledoc section: :data_types

@type t :: %__MODULE__{
id: integer,
account_id: integer,
contact_id: integer,
domain_id: integer,
state: String.t,
extended_attributes: map(),
registry_owner_change: boolean,
irt_lock_lifted_by: String.t,
created_at: String.t,
updated_at: String.t,
}

defstruct ~w(id account_id contact_id domain_id state extended_attributes
registry_owner_change irt_lock_lifted_by created_at updated_at)a

end
18 changes: 18 additions & 0 deletions lib/dnsimple/registrant_change_check.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
defmodule Dnsimple.RegistrantChangeCheck do
@moduledoc """
Represents a registrant change check response.
Developer preview: this API is currently in open beta and subject to change.
"""
@moduledoc section: :data_types

@type t :: %__MODULE__{
contact_id: integer,
domain_id: integer,
extended_attributes: [map()],
registry_owner_change: boolean
}

defstruct ~w(contact_id domain_id extended_attributes registry_owner_change)a

end
103 changes: 103 additions & 0 deletions lib/dnsimple/registrar.ex
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ defmodule Dnsimple.Registrar do
alias Dnsimple.WhoisPrivacy
alias Dnsimple.WhoisPrivacyRenewal
alias Dnsimple.VanityNameServer
alias Dnsimple.RegistrantChangeCheck
alias Dnsimple.RegistrantChange

@doc """
Checks if a domain name is available to be registered and whether premium
Expand Down Expand Up @@ -484,4 +486,105 @@ defmodule Dnsimple.Registrar do
|> Response.parse(nil)
end

@doc """
Retrieves the requirements of a registrant change
## Examples:
client = %Dnsimple.Client{access_token: "a1b2c3d4"}
{:ok, response} = Dnsimple.Registrar.check_registrant_change(client, account_id = 1010, %{
contact_id: 1,
domain_id: "example.com",
})
"""
@spec check_registrant_change(Client.t, integer | String.t, Keyword.t, Keyword.t) :: {:ok|:error, Response.t}
def check_registrant_change(client, account_id, attributes, options \\ []) do
url = Client.versioned("/#{account_id}/registrar/registrant_changes/check")

Client.post(client, url, attributes, options)
|> Response.parse(%{"data" => %RegistrantChangeCheck{}})
end

@doc """
Retrieves the details of an existing registrant change.
## Examples:
client = %Dnsimple.Client{access_token: "a1b2c3d4"}
{:ok, response} = Dnsimple.Registrar.get_registrant_change(client, account_id = 1010, registrant_change_id = 1)
"""
@spec get_registrant_change(Client.t, integer | String.t, integer, Keyword.t) :: {:ok|:error, Response.t}
def get_registrant_change(client, account_id, registrant_change_id, options \\ []) do
url = Client.versioned("/#{account_id}/registrar/registrant_changes/#{registrant_change_id}")

Client.get(client, url, options)
|> Response.parse(%{"data" => %RegistrantChange{}})
end

@doc """
Start registrant change.
## Examples:
client = %Dnsimple.Client{access_token: "a1b2c3d4"}
{:ok, response} = Dnsimple.Registrar.create_registrant_change(client, account_id = 1010, %{
contact_id: 1,
domain_id: "example.com",
extended_attributes: %{
"x-fake-attribute" => "value",
}
})
"""
@spec create_registrant_change(Client.t, integer | String.t, Keyword.t, Keyword.t) :: {:ok|:error, Response.t}
def create_registrant_change(client, account_id, attributes, options \\ []) do
url = Client.versioned("/#{account_id}/registrar/registrant_changes")

Client.post(client, url, attributes, options)
|> Response.parse(%{"data" => %RegistrantChange{}})
end

@doc """
List registrant changes in the account.
## Examples
client = %Dnsimple.Client{access_token: "a1b2c3d4"}
{:ok, response} = Dnsimple.Registrar.list_registrant_changes(client, account_id = 1010)
"""
@spec list_registrant_changes(Client.t, integer | String.t, Keyword.t) :: {:ok|:error, Response.t}
def list_registrant_changes(client, account_id, options \\ []) do
url = Client.versioned("/#{account_id}/registrar/registrant_changes")

Client.get(client, url, options)
|> Response.parse(%{"data" => [%RegistrantChange{}]})
end

@doc """
Cancel an ongoing registrant change from the account.
## Examples
client = %Dnsimple.Client{access_token: "a1b2c3d4"}
{:ok, response} = Dnsimple.Registrar.delete_registrant_change(client, account_id = 1010, registrant_change_id = 1)
"""
@spec delete_registrant_change(Client.t, integer | String.t, integer, Keyword.t) :: {:ok|:error, Response.t}
def delete_registrant_change(client, account_id, registrant_change_id, options \\ []) do
url = Client.versioned("/#{account_id}/registrar/registrant_changes/#{registrant_change_id}")

{state, response } = Client.delete(client, url, options)
cond do
response.status_code == 204 ->
{state, response} |> Response.parse(nil)
response.status_code == 202 ->
{state, response} |> Response.parse(%{"data" => %RegistrantChange{}})
true ->
{state, response} |> Response.parse(nil)
end
end

end
149 changes: 148 additions & 1 deletion test/dnsimple/registrar_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ defmodule Dnsimple.RegistrarTest do
end
end
end

describe ".get_domain_renewal" do
test "returns the domain renewal in a Dnsimple.Response" do
url = "#{@client.base_url}/v2/#{@account_id}/registrar/domains/example.com/renewals/1"
Expand Down Expand Up @@ -484,4 +484,151 @@ defmodule Dnsimple.RegistrarTest do
end
end

describe ".check_registrant_change" do
test "returns the registrant change check in a Dnsimple.Response" do
url = "#{@client.base_url}/v2/#{@account_id}/registrar/registrant_changes/check"
method = "post"
fixture = "checkRegistrantChange/success.http"
attributes = %{domain_id: @domain_id, contact_id: 101}
{:ok, body} = Poison.encode(attributes)

Check warning on line 493 in test/dnsimple/registrar_test.exs

View workflow job for this annotation

GitHub Actions / Elixir 1.11 / OTP 24.0

variable "body" is unused (if the variable is not meant to be used, prefix it with an underscore)

Check warning on line 493 in test/dnsimple/registrar_test.exs

View workflow job for this annotation

GitHub Actions / Elixir 1.12 / OTP 24.0

variable "body" is unused (if the variable is not meant to be used, prefix it with an underscore)

Check warning on line 493 in test/dnsimple/registrar_test.exs

View workflow job for this annotation

GitHub Actions / Elixir 1.13 / OTP 24.0

variable "body" is unused (if the variable is not meant to be used, prefix it with an underscore)

Check warning on line 493 in test/dnsimple/registrar_test.exs

View workflow job for this annotation

GitHub Actions / Elixir 1.14 / OTP 24.0

variable "body" is unused (if the variable is not meant to be used, prefix it with an underscore)

Check warning on line 493 in test/dnsimple/registrar_test.exs

View workflow job for this annotation

GitHub Actions / Elixir 1.14 / OTP 25.0

variable "body" is unused (if the variable is not meant to be used, prefix it with an underscore)

Check warning on line 493 in test/dnsimple/registrar_test.exs

View workflow job for this annotation

GitHub Actions / Elixir 1.13 / OTP 25.0

variable "body" is unused (if the variable is not meant to be used, prefix it with an underscore)

Check warning on line 493 in test/dnsimple/registrar_test.exs

View workflow job for this annotation

GitHub Actions / Elixir 1.11 / OTP 23.0

variable "body" is unused (if the variable is not meant to be used, prefix it with an underscore)

Check warning on line 493 in test/dnsimple/registrar_test.exs

View workflow job for this annotation

GitHub Actions / Elixir 1.13 / OTP 23.0

variable "body" is unused (if the variable is not meant to be used, prefix it with an underscore)

Check warning on line 493 in test/dnsimple/registrar_test.exs

View workflow job for this annotation

GitHub Actions / Elixir 1.12 / OTP 23.0

variable "body" is unused (if the variable is not meant to be used, prefix it with an underscore)

Check warning on line 493 in test/dnsimple/registrar_test.exs

View workflow job for this annotation

GitHub Actions / Elixir 1.14 / OTP 23.0

variable "body" is unused (if the variable is not meant to be used, prefix it with an underscore)

Check warning on line 493 in test/dnsimple/registrar_test.exs

View workflow job for this annotation

GitHub Actions / Elixir 1.11 / OTP 21.0

variable "body" is unused (if the variable is not meant to be used, prefix it with an underscore)

Check warning on line 493 in test/dnsimple/registrar_test.exs

View workflow job for this annotation

GitHub Actions / Elixir 1.13 / OTP 22.0

variable "body" is unused (if the variable is not meant to be used, prefix it with an underscore)

Check warning on line 493 in test/dnsimple/registrar_test.exs

View workflow job for this annotation

GitHub Actions / Elixir 1.12 / OTP 22.0

variable "body" is unused (if the variable is not meant to be used, prefix it with an underscore)

Check warning on line 493 in test/dnsimple/registrar_test.exs

View workflow job for this annotation

GitHub Actions / Elixir 1.11 / OTP 22.0

variable "body" is unused (if the variable is not meant to be used, prefix it with an underscore)

use_cassette :stub, ExvcrUtils.response_fixture(fixture, method: method, url: url) do
{:ok, response} = @module.check_registrant_change(@client, @account_id, attributes)
assert response.__struct__ == Dnsimple.Response

data = response.data
assert data.__struct__ == Dnsimple.RegistrantChangeCheck
assert data.contact_id == 101
assert data.domain_id == 101
assert data.extended_attributes == []
assert data.registry_owner_change == true
end
end
end

describe ".get_registrant_change" do
test "returns the registrant change in a Dnsimple.Response" do
registrant_change_id = 1
url = "#{@client.base_url}/v2/#{@account_id}/registrar/registrant_changes/#{registrant_change_id}"
method = "get"
fixture = "getRegistrantChange/success.http"

use_cassette :stub, ExvcrUtils.response_fixture(fixture, method: method, url: url) do
{:ok, response} = @module.get_registrant_change(@client, @account_id, registrant_change_id)
assert response.__struct__ == Dnsimple.Response

data = response.data
assert data.__struct__ == Dnsimple.RegistrantChange
assert data.id == 101
assert data.account_id == 101
assert data.domain_id == 101
assert data.contact_id == 101
assert data.state == "new"
assert data.extended_attributes == %{}
assert data.registry_owner_change == true
assert data.irt_lock_lifted_by == nil
assert data.created_at == "2017-02-03T17:43:22Z"
assert data.updated_at == "2017-02-03T17:43:22Z"
end
end
end

describe ".create_registrant_change" do
test "returns the registrant change in a Dnsimple.Response" do
url = "#{@client.base_url}/v2/#{@account_id}/registrar/registrant_changes"
method = "post"
fixture = "createRegistrantChange/success.http"
attributes = %{domain_id: @domain_id, contact_id: 101}
{:ok, body} = Poison.encode(attributes)

Check warning on line 542 in test/dnsimple/registrar_test.exs

View workflow job for this annotation

GitHub Actions / Elixir 1.11 / OTP 24.0

variable "body" is unused (if the variable is not meant to be used, prefix it with an underscore)

Check warning on line 542 in test/dnsimple/registrar_test.exs

View workflow job for this annotation

GitHub Actions / Elixir 1.12 / OTP 24.0

variable "body" is unused (if the variable is not meant to be used, prefix it with an underscore)

Check warning on line 542 in test/dnsimple/registrar_test.exs

View workflow job for this annotation

GitHub Actions / Elixir 1.13 / OTP 24.0

variable "body" is unused (if the variable is not meant to be used, prefix it with an underscore)

Check warning on line 542 in test/dnsimple/registrar_test.exs

View workflow job for this annotation

GitHub Actions / Elixir 1.14 / OTP 24.0

variable "body" is unused (if the variable is not meant to be used, prefix it with an underscore)

Check warning on line 542 in test/dnsimple/registrar_test.exs

View workflow job for this annotation

GitHub Actions / Elixir 1.14 / OTP 25.0

variable "body" is unused (if the variable is not meant to be used, prefix it with an underscore)

Check warning on line 542 in test/dnsimple/registrar_test.exs

View workflow job for this annotation

GitHub Actions / Elixir 1.13 / OTP 25.0

variable "body" is unused (if the variable is not meant to be used, prefix it with an underscore)

Check warning on line 542 in test/dnsimple/registrar_test.exs

View workflow job for this annotation

GitHub Actions / Elixir 1.11 / OTP 23.0

variable "body" is unused (if the variable is not meant to be used, prefix it with an underscore)

Check warning on line 542 in test/dnsimple/registrar_test.exs

View workflow job for this annotation

GitHub Actions / Elixir 1.13 / OTP 23.0

variable "body" is unused (if the variable is not meant to be used, prefix it with an underscore)

Check warning on line 542 in test/dnsimple/registrar_test.exs

View workflow job for this annotation

GitHub Actions / Elixir 1.12 / OTP 23.0

variable "body" is unused (if the variable is not meant to be used, prefix it with an underscore)

Check warning on line 542 in test/dnsimple/registrar_test.exs

View workflow job for this annotation

GitHub Actions / Elixir 1.14 / OTP 23.0

variable "body" is unused (if the variable is not meant to be used, prefix it with an underscore)

Check warning on line 542 in test/dnsimple/registrar_test.exs

View workflow job for this annotation

GitHub Actions / Elixir 1.11 / OTP 21.0

variable "body" is unused (if the variable is not meant to be used, prefix it with an underscore)

Check warning on line 542 in test/dnsimple/registrar_test.exs

View workflow job for this annotation

GitHub Actions / Elixir 1.13 / OTP 22.0

variable "body" is unused (if the variable is not meant to be used, prefix it with an underscore)

Check warning on line 542 in test/dnsimple/registrar_test.exs

View workflow job for this annotation

GitHub Actions / Elixir 1.12 / OTP 22.0

variable "body" is unused (if the variable is not meant to be used, prefix it with an underscore)

Check warning on line 542 in test/dnsimple/registrar_test.exs

View workflow job for this annotation

GitHub Actions / Elixir 1.11 / OTP 22.0

variable "body" is unused (if the variable is not meant to be used, prefix it with an underscore)

use_cassette :stub, ExvcrUtils.response_fixture(fixture, method: method, url: url) do
{:ok, response} = @module.create_registrant_change(@client, @account_id, attributes)
assert response.__struct__ == Dnsimple.Response

data = response.data
assert data.__struct__ == Dnsimple.RegistrantChange
assert data.id == 101
assert data.account_id == 101
assert data.domain_id == 101
assert data.contact_id == 101
assert data.state == "new"
assert data.extended_attributes == %{}
assert data.registry_owner_change == true
assert data.irt_lock_lifted_by == nil
assert data.created_at == "2017-02-03T17:43:22Z"
assert data.updated_at == "2017-02-03T17:43:22Z"
end
end
end

describe ".list_registrant_changes" do
test "returns the registrant changes in a Dnsimple.Response" do
url = "#{@client.base_url}/v2/#{@account_id}/registrar/registrant_changes"
method = "get"
fixture = "listRegistrantChanges/success.http"

use_cassette :stub, ExvcrUtils.response_fixture(fixture, method: method, url: url) do
{:ok, response} = @module.list_registrant_changes(@client, @account_id)
assert response.__struct__ == Dnsimple.Response

data = response.data
assert is_list(data)
assert length(data) == 1

[first | _] = data
assert first.__struct__ == Dnsimple.RegistrantChange
assert first.id == 101
assert first.account_id == 101
assert first.domain_id == 101
assert first.contact_id == 101
assert first.state == "new"
assert first.extended_attributes == %{}
assert first.registry_owner_change == true
assert first.irt_lock_lifted_by == nil
assert first.created_at == "2017-02-03T17:43:22Z"
assert first.updated_at == "2017-02-03T17:43:22Z"
end
end
end

describe ".delete_registrant_change" do
test "returns nil for successful sync cancallation response wrapped in a Dnsimple.Response" do
registrant_change_id = 1
url = "#{@client.base_url}/v2/#{@account_id}/registrar/registrant_changes/#{registrant_change_id}"
method = "delete"
fixture = "deleteRegistrantChange/success.http"

use_cassette :stub, ExvcrUtils.response_fixture(fixture, method: method, url: url) do
{:ok, response} = @module.delete_registrant_change(@client, @account_id, registrant_change_id)
assert response.__struct__ == Dnsimple.Response

assert is_nil(response.data)
end
end

test "returns registrant change object for async cancellation in a Dnsimple.Response" do
registrant_change_id = 1
url = "#{@client.base_url}/v2/#{@account_id}/registrar/registrant_changes/#{registrant_change_id}"
method = "delete"
fixture = "deleteRegistrantChange/success_async.http"

use_cassette :stub, ExvcrUtils.response_fixture(fixture, method: method, url: url) do
{:ok, response} = @module.delete_registrant_change(@client, @account_id, registrant_change_id)
assert response.__struct__ == Dnsimple.Response

data = response.data
assert data.__struct__ == Dnsimple.RegistrantChange
assert data.id == 101
assert data.account_id == 101
assert data.domain_id == 101
assert data.contact_id == 101
assert data.state == "cancelling"
assert data.extended_attributes == %{}
assert data.registry_owner_change == true
assert data.irt_lock_lifted_by == nil
assert data.created_at == "2017-02-03T17:43:22Z"
assert data.updated_at == "2017-02-03T17:43:22Z"
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
HTTP/1.1 404
server: nginx
date: Tue, 22 Aug 2023 13:59:02 GMT
content-type: application/json; charset=utf-8
X-RateLimit-Limit: 2400
X-RateLimit-Remaining: 2395
X-RateLimit-Reset: 1692705338
x-work-with-us: Love automation? So do we! https://dnsimple.com/jobs
cache-control: no-cache
x-request-id: b1dd3f42-ebb9-42fd-a121-d595de96f667
x-runtime: 0.019122
strict-transport-security: max-age=63072000

{"message":"Contact `21` not found"}
15 changes: 15 additions & 0 deletions test/fixtures.http/checkRegistrantChange/error-domainnotfound.http
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
HTTP/1.1 404
server: nginx
date: Tue, 22 Aug 2023 11:09:40 GMT
content-type: application/json; charset=utf-8
X-RateLimit-Limit: 2400
X-RateLimit-Remaining: 2395
X-RateLimit-Reset: 1692705338
x-work-with-us: Love automation? So do we! https://dnsimple.com/jobs
etag: W/"cef1e7d85d0b9bfd25e81b812891d34f"
cache-control: max-age=0, private, must-revalidate
x-request-id: 5b0d8bfb-7b6a-40b5-a079-b640fd817e34
x-runtime: 3.066249
strict-transport-security: max-age=63072000

{"message":"Domain `dnsimple-rraform.bio` not found"}
15 changes: 15 additions & 0 deletions test/fixtures.http/checkRegistrantChange/success.http
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
HTTP/1.1 200
server: nginx
date: Tue, 22 Aug 2023 11:09:40 GMT
content-type: application/json; charset=utf-8
X-RateLimit-Limit: 2400
X-RateLimit-Remaining: 2395
X-RateLimit-Reset: 1692705338
x-work-with-us: Love automation? So do we! https://dnsimple.com/jobs
etag: W/"cef1e7d85d0b9bfd25e81b812891d34f"
cache-control: max-age=0, private, must-revalidate
x-request-id: 5b0d8bfb-7b6a-40b5-a079-b640fd817e34
x-runtime: 3.066249
strict-transport-security: max-age=63072000

{"data":{"domain_id":101,"contact_id":101,"extended_attributes":[],"registry_owner_change":true}}
14 changes: 14 additions & 0 deletions test/fixtures.http/createRegistrantChange/success.http
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
HTTP/1.1 202
server: nginx
date: Tue, 22 Aug 2023 11:11:00 GMT
content-type: application/json; charset=utf-8
X-RateLimit-Limit: 2400
X-RateLimit-Remaining: 2395
X-RateLimit-Reset: 1692705338
x-work-with-us: Love automation? So do we! https://dnsimple.com/jobs
cache-control: no-cache
x-request-id: 26bf7ff9-2075-42b0-9431-1778c825b6b0
x-runtime: 3.408950
strict-transport-security: max-age=63072000

{"data":{"id":101,"account_id":101,"domain_id":101,"contact_id":101,"state":"new","extended_attributes":{},"registry_owner_change":true,"irt_lock_lifted_by":null,"created_at":"2017-02-03T17:43:22Z","updated_at":"2017-02-03T17:43:22Z"}}
Loading

0 comments on commit 1c72bfb

Please sign in to comment.