diff --git a/dns.go b/dns.go index 726bf30..c5f19f0 100644 --- a/dns.go +++ b/dns.go @@ -7,9 +7,10 @@ import ( ) const ( - domainsDNSGetHosts = "namecheap.domains.dns.getHosts" - domainsDNSSetHosts = "namecheap.domains.dns.setHosts" - domainsDNSSetCustom = "namecheap.domains.dns.setCustom" + domainsDNSGetHosts = "namecheap.domains.dns.getHosts" + domainsDNSSetHosts = "namecheap.domains.dns.setHosts" + domainsDNSSetCustom = "namecheap.domains.dns.setCustom" + domainsDNSSetDefault = "namecheap.domains.dns.setDefault" ) type DomainDNSGetHostsResult struct { @@ -99,3 +100,24 @@ func (client *Client) DomainDNSSetCustom(sld, tld, nameservers string) (*DomainD } return resp.DomainDNSSetCustom, nil } + +type DomainDNSSetDefaultResult struct { + Domain string `xml:"Domain,attr"` + Update bool `xml:"Updated,attr"` +} + +func (client *Client) DomainDNSSetDefault(sld, tld string) (*DomainDNSSetDefaultResult, error) { + requestInfo := &ApiRequest{ + command: domainsDNSSetDefault, + method: "POST", + params: url.Values{}, + } + requestInfo.params.Set("SLD", sld) + requestInfo.params.Set("TLD", tld) + + resp, err := client.do(requestInfo) + if err != nil { + return nil, err + } + return resp.DomainDNSSetDefault, nil +} diff --git a/dns_test.go b/dns_test.go index 8944239..c41455e 100644 --- a/dns_test.go +++ b/dns_test.go @@ -169,3 +169,45 @@ func TestDomainsDNSSetCustom(t *testing.T) { t.Errorf("DomainsDNSSetCustom returned %+v, want %+v", result, want) } } + +func TestDomainsDNSSetDefault(t *testing.T) { + setup() + defer teardown() + + respXML := ` + + + + namecheap.domains.dns.setDefault + + + + SERVER-NAME + +5 + 32.76 +` + + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + correctParams := fillDefaultParams(url.Values{}) + correctParams.Set("Command", "namecheap.domains.dns.setDefault") + correctParams.Set("SLD", "domain") + correctParams.Set("TLD", "com") + testBody(t, r, correctParams) + testMethod(t, r, "POST") + fmt.Fprint(w, respXML) + }) + + result, err := client.DomainDNSSetDefault("domain", "com") + if err != nil { + t.Errorf("DomainDNSSetDefault returned error: %v", err) + } + + want := &DomainDNSSetDefaultResult{ + Domain: "domain.com", + Update: true, + } + + if !reflect.DeepEqual(result, want) { + t.Errorf("DomainsDNSSetDefault returned %+v, want %+v", result, want) + } +} diff --git a/domain.go b/domain.go index aefb601..cb1bb69 100644 --- a/domain.go +++ b/domain.go @@ -8,12 +8,14 @@ import ( ) const ( - domainsGetList = "namecheap.domains.getList" - domainsGetInfo = "namecheap.domains.getInfo" - domainsCheck = "namecheap.domains.check" - domainsCreate = "namecheap.domains.create" - domainsTLDList = "namecheap.domains.getTldList" - domainsRenew = "namecheap.domains.renew" + domainsGetList = "namecheap.domains.getList" + domainsGetInfo = "namecheap.domains.getInfo" + domainsCheck = "namecheap.domains.check" + domainsCreate = "namecheap.domains.create" + domainsTLDList = "namecheap.domains.getTldList" + domainsRenew = "namecheap.domains.renew" + domainsReactivate = "namecheap.domains.reactivate" + domainsSetContacts = "namecheap.domains.setContacts" ) // DomainGetListResult represents the data returned by 'domains.getList' @@ -50,9 +52,10 @@ type DNSDetails struct { } type Whoisguard struct { - Enabled bool `xml:"Enabled,attr"` - ID int64 `xml:"ID"` - ExpiredDate string `xml:"ExpiredDate"` + EnabledString string `xml:"Enabled,attr"` + Enabled bool + ID int64 `xml:"ID"` + ExpiredDate string `xml:"ExpiredDate"` } type DomainCheckResult struct { @@ -91,6 +94,19 @@ type DomainRenewResult struct { ExpireDate string `xml:"DomainDetails>ExpiredDate"` } +type DomainReactivateResult struct { + Name string `xml:"Domain,attr"` + Reactivated bool `xml:"IsSuccess,attr"` + ChargedAmount float64 `xml:"ChargedAmount,attr"` + OrderID int `xml:"OrderID,attr"` + TransactionID int `xml:"TransactionID,attr"` +} + +type DomainSetContactsResult struct { + Name string `xml:"Domain,attr"` + ContactsChanged bool `xml:"IsSuccess,attr"` +} + type DomainCreateOption struct { AddFreeWhoisguard bool WGEnabled bool @@ -126,6 +142,11 @@ func (client *Client) DomainGetInfo(domainName string) (*DomainInfo, error) { return nil, err } + switch strings.ToLower(resp.DomainInfo.Whoisguard.EnabledString) { + case "1", "true": + resp.DomainInfo.Whoisguard.Enabled = true + } + return resp.DomainInfo, nil } @@ -212,3 +233,40 @@ func (client *Client) DomainRenew(domainName string, years int) (*DomainRenewRes return resp.DomainRenew, nil } + +func (client *Client) DomainReactivate(domainName string, years int) (*DomainReactivateResult, error) { + requestInfo := &ApiRequest{ + command: domainsReactivate, + method: "POST", + params: url.Values{}, + } + requestInfo.params.Set("DomainName", domainName) + requestInfo.params.Set("Years", strconv.Itoa(years)) + + resp, err := client.do(requestInfo) + if err != nil { + return nil, err + } + + return resp.DomainReactivate, nil +} + +func (client *Client) DomainSetContacts(domainName string, registrant *Registrant) (*DomainSetContactsResult, error) { + requestInfo := &ApiRequest{ + command: domainsSetContacts, + method: "POST", + params: url.Values{}, + } + requestInfo.params.Set("DomainName", domainName) + + if err := registrant.addValues(requestInfo.params); err != nil { + return nil, err + } + + resp, err := client.do(requestInfo) + if err != nil { + return nil, err + } + + return resp.DomainSetContacts, nil +} diff --git a/domain_test.go b/domain_test.go index 9224b88..a649730 100644 --- a/domain_test.go +++ b/domain_test.go @@ -139,9 +139,94 @@ func TestDomainGetInfo(t *testing.T) { }, }, Whoisguard: Whoisguard{ - Enabled: true, - ID: 53536, - ExpiredDate: "11/04/2015", + Enabled: true, + EnabledString: "True", + ID: 53536, + ExpiredDate: "11/04/2015", + }, + } + + if !reflect.DeepEqual(domain, want) { + t.Errorf("DomainGetInfo returned %+v, want %+v", domain, want) + } +} + +func TestDomainGetInfoNotAlloted(t *testing.T) { + setup() + defer teardown() + + respXML := ` + + + + namecheap.domains.getinfo + + + + 07/25/2019 + 07/25/2020 + 0 + + + + 0 + + + false + -1 + 0001-01-01T00:00:00 + 0001-01-01T00:00:00 + false + + + dns1.registrar-servers.com + dns2.registrar-servers.com + + + + + PHX01APIEXT01 + --4:00 + 0.018 +` + + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + correctParams := fillDefaultParams(url.Values{}) + correctParams.Set("Command", "namecheap.domains.getInfo") + correctParams.Set("DomainName", "example.com") + testBody(t, r, correctParams) + testMethod(t, r, "POST") + fmt.Fprint(w, respXML) + }) + + domain, err := client.DomainGetInfo("example.com") + + if err != nil { + t.Errorf("DomainGetInfo returned error: %v", err) + } + + // DomainInfo we expect, given the respXML above + want := &DomainInfo{ + ID: 37103805, + Name: "example.com", + Owner: "anUser", + Created: "07/25/2019", + Expires: "07/25/2020", + IsExpired: false, + IsLocked: false, + AutoRenew: false, + DNSDetails: DNSDetails{ + ProviderType: "FREE", + IsUsingOurDNS: true, + Nameservers: []string{ + "dns1.registrar-servers.com", + "dns2.registrar-servers.com", + }, + }, + Whoisguard: Whoisguard{ + Enabled: false, + EnabledString: "NotAlloted", + ID: 0, }, } @@ -321,3 +406,104 @@ func TestDomainsRenew(t *testing.T) { t.Errorf("DomainRenew returned %+v, want %+v", result, want) } } + +func TestDomainsReactivate(t *testing.T) { + setup() + defer teardown() + + respXML := ` + + + + namecheap.domains.reactivate + + + + SERVER-NAME + +5:00 + 12.915 +` + + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + correctParams := fillDefaultParams(url.Values{}) + correctParams.Set("Command", "namecheap.domains.reactivate") + correctParams.Set("DomainName", "domain1.com") + correctParams.Set("Years", "1") + testBody(t, r, correctParams) + testMethod(t, r, "POST") + fmt.Fprint(w, respXML) + }) + + result, err := client.DomainReactivate("domain1.com", 1) + if err != nil { + t.Errorf("DomainReactivate returned error: %v", err) + } + + // DomainReactivateResult we expect, given the respXML above + want := &DomainReactivateResult{ + Name: "domain1.com", + Reactivated: true, + ChargedAmount: 650, + TransactionID: 25080, + OrderID: 23569, + } + if !reflect.DeepEqual(result, want) { + t.Errorf("DomainsReactivate returned %+v, want %+v", result, want) + } +} + +func TestDomainsSetContacts(t *testing.T) { + setup() + defer teardown() + + respXML := ` + + + namecheap.domains.setContacts + + + + SERVER-NAME + +5 + 0.078 +` + + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + correctParams := fillDefaultParams(url.Values{}) + fillInfo := func(prefix string) { + correctParams.Set(prefix+"FirstName", "John") + correctParams.Set(prefix+"LastName", "Smith") + correctParams.Set(prefix+"Address1", "8939 S.cross Blvd") + correctParams.Set(prefix+"StateProvince", "CA") + correctParams.Set(prefix+"PostalCode", "90045") + correctParams.Set(prefix+"Country", "US") + correctParams.Set(prefix+"Phone", "+1.6613102107") + correctParams.Set(prefix+"EmailAddress", "john@gmail.com") + correctParams.Set(prefix+"City", "CA") + } + correctParams.Set("Command", "namecheap.domains.setContacts") + correctParams.Set("DomainName", "domain1.com") + fillInfo("AuxBilling") + fillInfo("Tech") + fillInfo("Admin") + fillInfo("Registrant") + testBody(t, r, correctParams) + testMethod(t, r, "POST") + fmt.Fprint(w, respXML) + }) + + registrant := NewRegistrant("John", "Smith", "8939 S.cross Blvd", "", "CA", "CA", "90045", "US", "+1.6613102107", "john@gmail.com") + result, err := client.DomainSetContacts("domain1.com", registrant) + if err != nil { + t.Errorf("DomainSetContacts returned error: %v", err) + } + + // DomainSetContactsResult we expect, given the respXML above + want := &DomainSetContactsResult{ + Name: "domain1.com", + ContactsChanged: true, + } + if !reflect.DeepEqual(result, want) { + t.Errorf("DomainSetContacts returned %+v, want %+v", result, want) + } +} diff --git a/namecheap.go b/namecheap.go index 1a3961c..3083e6c 100644 --- a/namecheap.go +++ b/namecheap.go @@ -12,9 +12,11 @@ import ( "net/url" "strconv" "strings" + "time" ) const defaultBaseURL = "https://api.namecheap.com/xml.response" +const defaultTimeout = 0 // Client represents a client used to make calls to the Namecheap API. type Client struct { @@ -23,6 +25,10 @@ type Client struct { UserName string HttpClient *http.Client + // Timeout specifies a time limit for requests made by this + // Client. Default value is zero, which means no timeout. + Timeout time.Duration + // Base URL for API requests. // Defaults to the public Namecheap API, // but can be set to a different endpoint (e.g. the sandbox). @@ -39,27 +45,31 @@ type ApiRequest struct { } type ApiResponse struct { - Status string `xml:"Status,attr"` - Command string `xml:"RequestedCommand"` - TLDList []TLDListResult `xml:"CommandResponse>Tlds>Tld"` - Domains []DomainGetListResult `xml:"CommandResponse>DomainGetListResult>Domain"` - DomainInfo *DomainInfo `xml:"CommandResponse>DomainGetInfoResult"` - DomainDNSHosts *DomainDNSGetHostsResult `xml:"CommandResponse>DomainDNSGetHostsResult"` - DomainDNSSetHosts *DomainDNSSetHostsResult `xml:"CommandResponse>DomainDNSSetHostsResult"` - DomainCreate *DomainCreateResult `xml:"CommandResponse>DomainCreateResult"` - DomainRenew *DomainRenewResult `xml:"CommandResponse>DomainRenewResult"` - DomainsCheck []DomainCheckResult `xml:"CommandResponse>DomainCheckResult"` - DomainNSInfo *DomainNSInfoResult `xml:"CommandResponse>DomainNSInfoResult"` - DomainDNSSetCustom *DomainDNSSetCustomResult `xml:"CommandResponse>DomainDNSSetCustomResult"` - SslActivate *SslActivateResult `xml:"CommandResponse>SSLActivateResult"` - SslCreate *SslCreateResult `xml:"CommandResponse>SSLCreateResult"` - SslCertificates []SslGetListResult `xml:"CommandResponse>SSLListResult>SSL"` - UsersGetPricing []UsersGetPricingResult `xml:"CommandResponse>UserGetPricingResult>ProductType"` - WhoisguardList []WhoisguardGetListResult `xml:"CommandResponse>WhoisguardGetListResult>Whoisguard"` - WhoisguardEnable whoisguardEnableResult `xml:"CommandResponse>WhoisguardEnableResult"` - WhoisguardDisable whoisguardDisableResult `xml:"CommandResponse>WhoisguardDisableResult"` - WhoisguardRenew *WhoisguardRenewResult `xml:"CommandResponse>WhoisguardRenewResult"` - Errors ApiErrors `xml:"Errors>Error"` + Status string `xml:"Status,attr"` + Command string `xml:"RequestedCommand"` + TLDList []TLDListResult `xml:"CommandResponse>Tlds>Tld"` + Domains []DomainGetListResult `xml:"CommandResponse>DomainGetListResult>Domain"` + DomainInfo *DomainInfo `xml:"CommandResponse>DomainGetInfoResult"` + DomainDNSHosts *DomainDNSGetHostsResult `xml:"CommandResponse>DomainDNSGetHostsResult"` + DomainDNSSetHosts *DomainDNSSetHostsResult `xml:"CommandResponse>DomainDNSSetHostsResult"` + DomainCreate *DomainCreateResult `xml:"CommandResponse>DomainCreateResult"` + DomainRenew *DomainRenewResult `xml:"CommandResponse>DomainRenewResult"` + DomainReactivate *DomainReactivateResult `xml:"CommandResponse>DomainReactivateResult"` + DomainSetContacts *DomainSetContactsResult `xml:"CommandResponse>DomainSetContactResult"` + DomainsCheck []DomainCheckResult `xml:"CommandResponse>DomainCheckResult"` + DomainNSInfo *DomainNSInfoResult `xml:"CommandResponse>DomainNSInfoResult"` + DomainDNSSetCustom *DomainDNSSetCustomResult `xml:"CommandResponse>DomainDNSSetCustomResult"` + DomainDNSSetDefault *DomainDNSSetDefaultResult `xml:"CommandResponse>DomainDNSSetDefaultResult"` + SslActivate *SslActivateResult `xml:"CommandResponse>SSLActivateResult"` + SslCreate *SslCreateResult `xml:"CommandResponse>SSLCreateResult"` + SslCertificates []SslGetListResult `xml:"CommandResponse>SSLListResult>SSL"` + UsersGetPricing []UsersGetPricingResult `xml:"CommandResponse>UserGetPricingResult>ProductType"` + UsersGetBalances *UsersGetBalancesResult `xml:"CommandResponse>UserGetBalancesResult"` + WhoisguardList []WhoisguardGetListResult `xml:"CommandResponse>WhoisguardGetListResult>Whoisguard"` + WhoisguardEnable whoisguardEnableResult `xml:"CommandResponse>WhoisguardEnableResult"` + WhoisguardDisable whoisguardDisableResult `xml:"CommandResponse>WhoisguardDisableResult"` + WhoisguardRenew *WhoisguardRenewResult `xml:"CommandResponse>WhoisguardRenewResult"` + Errors ApiErrors `xml:"Errors>Error"` } // ApiError is the format of the error returned in the api responses. @@ -90,6 +100,7 @@ func NewClient(apiUser, apiToken, userName string) *Client { UserName: userName, HttpClient: http.DefaultClient, BaseURL: defaultBaseURL, + Timeout: defaultTimeout, } } @@ -100,7 +111,7 @@ func (client *Client) NewRegistrant( city, state, postalCode, country, phone, email string, ) { - client.Registrant = newRegistrant( + client.Registrant = NewRegistrant( firstName, lastName, addr1, addr2, city, state, postalCode, country, @@ -161,6 +172,8 @@ func (client *Client) sendRequest(request *ApiRequest) ([]byte, int, error) { return nil, 0, err } + client.HttpClient.Timeout = client.Timeout + resp, err := client.HttpClient.Do(req) if err != nil { return nil, 0, err diff --git a/registrant.go b/registrant.go index 5b8b303..885114e 100644 --- a/registrant.go +++ b/registrant.go @@ -34,9 +34,9 @@ type Registrant struct { AuxBillingPhone, AuxBillingEmailAddress string } -// newRegistrant return a new registrant where all the required fields are the same. +// NewRegistrant return a new registrant where all the required fields are the same. // Feel free to change them as needed -func newRegistrant( +func NewRegistrant( firstName, lastName, addr1, addr2, city, state, postalCode, country, diff --git a/registrant_test.go b/registrant_test.go index 6e34a82..ca231b4 100644 --- a/registrant_test.go +++ b/registrant_test.go @@ -6,7 +6,7 @@ import ( ) func TestAddValues(t *testing.T) { - reg := newRegistrant( + reg := NewRegistrant( "r", "m", "10 Park Ave.", "Apt. 3F", diff --git a/users.go b/users.go index 7d8e0f0..a7db45f 100644 --- a/users.go +++ b/users.go @@ -5,7 +5,8 @@ import ( ) const ( - usersGetPricing = "namecheap.users.getPricing" + usersGetPricing = "namecheap.users.getPricing" + usersGetBalances = "namecheap.users.getBalances" ) type UsersGetPricingResult struct { @@ -27,6 +28,15 @@ type UsersGetPricingResult struct { } `xml:"ProductCategory"` } +type UsersGetBalancesResult struct { + Currency string `xml:"Currency,attr"` + AvailableBalance float64 `xml:"AvailableBalance,attr"` + AccountBalance float64 `xml:"AccountBalance,attr"` + EarnedAmount float64 `xml:"EarnedAmount,attr"` + WithdrawableAmount float64 `xml:"WithdrawableAmount,attr"` + FundsRequiredForAutoRenew float64 `xml:"FundsRequiredForAutoRenew,attr"` +} + func (client *Client) UsersGetPricing(productType string) ([]UsersGetPricingResult, error) { requestInfo := &ApiRequest{ command: usersGetPricing, @@ -42,3 +52,18 @@ func (client *Client) UsersGetPricing(productType string) ([]UsersGetPricingResu return resp.UsersGetPricing, nil } + +func (client *Client) UsersGetBalances() (*UsersGetBalancesResult, error) { + requestInfo := &ApiRequest{ + command: usersGetBalances, + method: "POST", + params: url.Values{}, + } + + resp, err := client.do(requestInfo) + if err != nil { + return nil, err + } + + return resp.UsersGetBalances, nil +} diff --git a/users_test.go b/users_test.go new file mode 100644 index 0000000..f1fc9d4 --- /dev/null +++ b/users_test.go @@ -0,0 +1,53 @@ +package namecheap + +import ( + "fmt" + "net/http" + "net/url" + "reflect" + "testing" +) + +func TestUsersGetBalances(t *testing.T) { + setup() + defer teardown() + respXML := ` + + + namecheap.users.getBalances + + + + SERVER-NAME + +5 + 0.024 +` + + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + correctParams := fillDefaultParams(url.Values{}) + correctParams.Set("Command", "namecheap.users.getBalances") + testBody(t, r, correctParams) + testMethod(t, r, "POST") + fmt.Fprint(w, respXML) + }) + + balances, err := client.UsersGetBalances() + + if err != nil { + t.Errorf("UsersGetBalances returned error: %v", err) + } + + // Balances response we want, given the above respXML + want := &UsersGetBalancesResult{ + Currency: "USD", + AvailableBalance: 4932.96, + AccountBalance: 4932.96, + EarnedAmount: 381.70, + WithdrawableAmount: 1243.36, + FundsRequiredForAutoRenew: 0.00, + } + + if !reflect.DeepEqual(balances, want) { + t.Errorf("UsersGetBalances returned %+v, want %+v", balances, want) + } +}