namesilo.go raw
1 // Package namesilo A Go client library for accessing the Namesilo API.
2 package namesilo
3
4 import (
5 "context"
6 "encoding/xml"
7 "errors"
8 "fmt"
9 "io"
10 "net/http"
11 "net/url"
12 "time"
13
14 querystring "github.com/google/go-querystring/query"
15 )
16
17 const (
18 // DefaultAPIEndpoint The default API endpoint.
19 DefaultAPIEndpoint = "https://www.namesilo.com/api"
20
21 // SandboxAPIEndpoint The sandbox API endpoint.
22 SandboxAPIEndpoint = "https://sandbox.namesilo.com/api"
23
24 // OTEAPIEndpoint The OTE sandbox API endpoint.
25 OTEAPIEndpoint = "https://ote.namesilo.com/api"
26 )
27
28 // Response Codes.
29 const (
30 SuccessfulAPIOperation = "300"
31 SuccessfulRegistration = "301"
32 SuccessfulOrder = "302"
33 )
34
35 // Client the Namesilo client.
36 type Client struct {
37 apiKey string
38
39 Endpoint *url.URL
40 HTTPClient *http.Client
41 }
42
43 // NewClient Creates a Namesilo client.
44 func NewClient(apiKey string) *Client {
45 endpoint, _ := url.Parse(DefaultAPIEndpoint)
46
47 return &Client{
48 apiKey: apiKey,
49 Endpoint: endpoint,
50 HTTPClient: &http.Client{Timeout: 10 * time.Second},
51 }
52 }
53
54 func (c *Client) do(ctx context.Context, name string, params, op any) error {
55 endpoint := c.Endpoint.JoinPath(name)
56
57 query, err := querystring.Values(params)
58 if err != nil {
59 return err
60 }
61
62 query.Set("version", "1")
63 query.Set("type", "xml")
64 query.Set("key", c.apiKey)
65
66 endpoint.RawQuery = query.Encode()
67
68 req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint.String(), http.NoBody)
69 if err != nil {
70 return err
71 }
72
73 resp, err := c.HTTPClient.Do(req)
74 if err != nil {
75 return err
76 }
77
78 defer func() { _ = resp.Body.Close() }()
79
80 if resp.StatusCode != http.StatusOK {
81 data, _ := io.ReadAll(resp.Body)
82
83 return fmt.Errorf("error: %d: %s", resp.StatusCode, string(data))
84 }
85
86 data, err := io.ReadAll(resp.Body)
87 if err != nil {
88 return err
89 }
90
91 err = xml.Unmarshal(data, op)
92 if err != nil {
93 return fmt.Errorf("failed to decode: %w: %s", err, data)
94 }
95
96 return nil
97 }
98
99 func GetEndpoint(prod, ote bool) (*url.URL, error) {
100 if prod && ote {
101 return nil, errors.New("prod and ote are mutually exclusive")
102 }
103
104 if prod {
105 return url.Parse(DefaultAPIEndpoint)
106 }
107
108 if ote {
109 return url.Parse(OTEAPIEndpoint)
110 }
111
112 return url.Parse(SandboxAPIEndpoint)
113 }
114
115 type replyGetter interface {
116 getCode() string
117 getDetail() string
118 }
119
120 func checkReply(reply replyGetter) error {
121 switch reply.getCode() {
122 case SuccessfulAPIOperation:
123 // Successful API operation
124 return nil
125 case SuccessfulRegistration:
126 // Successful registration, but not all provided hosts were valid resulting in our nameservers being used
127 return nil
128 case SuccessfulOrder:
129 // Successful order, but there was an error with the contact information provided so your account default contact profile was used
130 return nil
131 default:
132 // error
133 return fmt.Errorf("code: %s, details: %s", reply.getCode(), reply.getDetail())
134 }
135 }
136