From 6b918f45e88477e66189d099e5b0ed3706f24086 Mon Sep 17 00:00:00 2001 From: brookatlas Date: Sat, 17 Feb 2024 17:06:44 +0200 Subject: [PATCH] add support for "domains.ns.getInfo" api method Signed-off-by: brookatlas --- namecheap/domains_ns.go | 7 + namecheap/domains_ns_get_info.go | 51 ++++++ namecheap/domains_ns_get_info_test.go | 214 ++++++++++++++++++++++++++ namecheap/namecheap.go | 9 +- 4 files changed, 278 insertions(+), 3 deletions(-) create mode 100644 namecheap/domains_ns.go create mode 100644 namecheap/domains_ns_get_info.go create mode 100644 namecheap/domains_ns_get_info_test.go diff --git a/namecheap/domains_ns.go b/namecheap/domains_ns.go new file mode 100644 index 0000000..1270240 --- /dev/null +++ b/namecheap/domains_ns.go @@ -0,0 +1,7 @@ +package namecheap + +// DomainsNSService includes the following methods: +// DomainsNSService.getInfo - gets info about a registered nameserver + +// Namecheap doc: https://www.namecheap.com/support/api/methods/domains-ns/ +type DomainsNSService service diff --git a/namecheap/domains_ns_get_info.go b/namecheap/domains_ns_get_info.go new file mode 100644 index 0000000..654f7bf --- /dev/null +++ b/namecheap/domains_ns_get_info.go @@ -0,0 +1,51 @@ +package namecheap + +import ( + "encoding/xml" + "fmt" +) + +type NameserversGetInfoResponse struct { + XMLName *xml.Name `xml:"ApiResponse"` + Errors *[]struct { + Message *string `xml:",chardata"` + Number *string `xml:"Number,attr"` + } `xml:"Errors>Error"` + CommandResponse *NameserversGetInfoCommandResponse `xml:"CommandResponse"` +} + +type NameserversGetInfoCommandResponse struct { + DomainNameserverInfoResult *DomainNSInfoResult `xml:"DomainNSInfoResult"` +} + +type DomainNSInfoResult struct { + Domain *string `xml:"Domain,attr"` + Nameserver *string `xml:"Nameserver,attr"` + IP *string `xml:"IP,attr"` + NameserverStatuses struct { + Nameservers *[]string `xml:"Status"` + } `xml:"NameserverStatuses"` +} + +func (s *DomainsNSService) GetInfo(SLD string, TLD string, Nameserver string) (*NameserversGetInfoCommandResponse, error) { + var response NameserversGetInfoResponse + + params := map[string]string{ + "Command": "namecheap.domains.ns.getInfo", + "SLD": SLD, + "TLD": TLD, + "Nameserver": Nameserver, + } + + _, err := s.client.DoXML(params, &response) + if err != nil { + return nil, err + } + + if response.Errors != nil && len(*response.Errors) > 0 { + apiErr := (*response.Errors)[0] + return nil, fmt.Errorf("%s (%s)", *apiErr.Message, *apiErr.Number) + } + + return response.CommandResponse, nil +} diff --git a/namecheap/domains_ns_get_info_test.go b/namecheap/domains_ns_get_info_test.go new file mode 100644 index 0000000..b72bb01 --- /dev/null +++ b/namecheap/domains_ns_get_info_test.go @@ -0,0 +1,214 @@ +package namecheap + +import ( + "io/ioutil" + "net/http" + "net/http/httptest" + "net/url" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestDomainNameserversGetInfo(t *testing.T) { + fakeResponse := ` + + + + namecheap.domains.ns.getInfo + + + + OK + Linked + + + + SERVER-NAME + +5 + 32.76 + + ` + + t.Run("request_command", func(t *testing.T) { + var sentBody url.Values + + mockServer := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + body, _ := ioutil.ReadAll(request.Body) + query, _ := url.ParseQuery(string(body)) + sentBody = query + _, _ = writer.Write([]byte(fakeResponse)) + })) + defer mockServer.Close() + + client := setupClient(nil) + client.BaseURL = mockServer.URL + + _, err := client.DomainsNS.GetInfo("specific-sld", "specific-tld", "ns1.domain.com") + if err != nil { + t.Fatal("Unable to get domain nameserver", err) + } + + assert.Equal(t, "namecheap.domains.ns.getInfo", sentBody.Get("Command")) + }) + + t.Run("server_empty_response", func(t *testing.T) { + fakeLocalResponse := "" + + mockServer := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + _, _ = writer.Write([]byte(fakeLocalResponse)) + })) + defer mockServer.Close() + + client := setupClient(nil) + client.BaseURL = mockServer.URL + + _, err := client.DomainsNS.GetInfo("specific-sld", "specific-tld", "ns1.domain.com") + + assert.EqualError(t, err, "unable to parse server response: EOF") + }) + + t.Run("server_non_xml_response", func(t *testing.T) { + fakeLocalResponse := "non-xml response" + + mockServer := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + _, _ = writer.Write([]byte(fakeLocalResponse)) + })) + defer mockServer.Close() + + client := setupClient(nil) + client.BaseURL = mockServer.URL + + _, err := client.DomainsNS.GetInfo("specific-sld", "specific-tld", "ns1.domain.com") + + assert.EqualError(t, err, "unable to parse server response: EOF") + }) + + t.Run("server_broken_xml_response", func(t *testing.T) { + fakeLocalResponse := "" + + mockServer := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + _, _ = writer.Write([]byte(fakeLocalResponse)) + })) + defer mockServer.Close() + + client := setupClient(nil) + client.BaseURL = mockServer.URL + + _, err := client.DomainsNS.GetInfo("specific-sld", "specific-tld", "ns1.domain.com") + + assert.EqualError(t, err, "unable to parse server response: expected element type but have ") + }) + + t.Run("server_respond_with_domain_not_found_error", func(t *testing.T) { + fakeLocalResponse := ` + + + + Domain not found + + + namecheap.domains.ns.getInfo + PHX01SBAPIEXT05 + --4:00 + 0.011 + + ` + + mockServer := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + _, _ = writer.Write([]byte(fakeLocalResponse)) + })) + defer mockServer.Close() + + client := setupClient(nil) + client.BaseURL = mockServer.URL + + _, err := client.DomainsNS.GetInfo("specific-sld", "specific-tld", "ns1.domain.com") + + assert.EqualError(t, err, "Domain not found (2019166)") + }) + + t.Run("server_respond_with_domain_not_associated_with_account_error", func(t *testing.T) { + fakeLocalResponse := ` + + + + Domain is not associated with your account + + + namecheap.domains.ns.getInfo + PHX01SBAPIEXT05 + --4:00 + 0.011 + + ` + + mockServer := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + _, _ = writer.Write([]byte(fakeLocalResponse)) + })) + defer mockServer.Close() + + client := setupClient(nil) + client.BaseURL = mockServer.URL + + _, err := client.DomainsNS.GetInfo("specific-sld", "specific-tld", "ns1.domain.com") + + assert.EqualError(t, err, "Domain is not associated with your account (2016166)") + }) + + t.Run("server_respond_with_error_from_enom", func(t *testing.T) { + fakeLocalResponse := ` + + + + Error From Enom when Errorcount <> 0 + + + namecheap.domains.ns.getInfo + PHX01SBAPIEXT05 + --4:00 + 0.011 + + ` + + mockServer := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + _, _ = writer.Write([]byte(fakeLocalResponse)) + })) + defer mockServer.Close() + + client := setupClient(nil) + client.BaseURL = mockServer.URL + + _, err := client.DomainsNS.GetInfo("specific-sld", "specific-tld", "ns1.domain.com") + + assert.EqualError(t, err, "Error From Enom when Errorcount <> 0 (3031510)") + }) + + t.Run("server_respond_with_unknown_error_from_enom", func(t *testing.T) { + fakeLocalResponse := ` + + + + Unknown error from Enom + + + namecheap.domains.ns.getInfo + PHX01SBAPIEXT05 + --4:00 + 0.011 + + ` + + mockServer := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + _, _ = writer.Write([]byte(fakeLocalResponse)) + })) + defer mockServer.Close() + + client := setupClient(nil) + client.BaseURL = mockServer.URL + + _, err := client.DomainsNS.GetInfo("specific-sld", "specific-tld", "ns1.domain.com") + + assert.EqualError(t, err, "Unknown error from Enom (3050900)") + }) +} diff --git a/namecheap/namecheap.go b/namecheap/namecheap.go index 30e8013..5204861 100644 --- a/namecheap/namecheap.go +++ b/namecheap/namecheap.go @@ -5,14 +5,15 @@ import ( "encoding/xml" "errors" "fmt" - "github.com/hashicorp/go-cleanhttp" - "github.com/namecheap/go-namecheap-sdk/v2/namecheap/internal/syncretry" - "github.com/weppos/publicsuffix-go/publicsuffix" "io" "net/http" "net/url" "regexp" "strconv" + + "github.com/hashicorp/go-cleanhttp" + "github.com/namecheap/go-namecheap-sdk/v2/namecheap/internal/syncretry" + "github.com/weppos/publicsuffix-go/publicsuffix" ) const ( @@ -37,6 +38,7 @@ type Client struct { BaseURL string Domains *DomainsService + DomainsNS *DomainsNSService DomainsDNS *DomainsDNSService } @@ -61,6 +63,7 @@ func NewClient(options *ClientOptions) *Client { client.common.client = client client.Domains = (*DomainsService)(&client.common) client.DomainsDNS = (*DomainsDNSService)(&client.common) + client.DomainsNS = (*DomainsNSService)(&client.common) return client }