client.go raw
1 package auroradns
2
3 import (
4 "encoding/json"
5 "fmt"
6 "io"
7 "net/http"
8 "net/url"
9 )
10
11 const defaultBaseURL = "https://api.auroradns.eu"
12
13 const (
14 contentTypeHeader = "Content-Type"
15 contentTypeJSON = "application/json"
16 )
17
18 // ErrorResponse A representation of an API error message.
19 // Deprecated: use ResponseError instead.
20 type ErrorResponse = ResponseError
21
22 // ResponseError A representation of an API error message.
23 type ResponseError struct {
24 ErrorCode string `json:"error"`
25 Message string `json:"errormsg"`
26 }
27
28 func (e *ResponseError) Error() string {
29 return fmt.Sprintf("%s - %s", e.ErrorCode, e.Message)
30 }
31
32 // Option Type of a client option.
33 type Option func(*Client) error
34
35 // Client The API client.
36 type Client struct {
37 baseURL *url.URL
38 UserAgent string
39 httpClient *http.Client
40 }
41
42 // NewClient Creates a new client.
43 func NewClient(httpClient *http.Client, opts ...Option) (*Client, error) {
44 if httpClient == nil {
45 httpClient = http.DefaultClient
46 }
47
48 baseURL, _ := url.Parse(defaultBaseURL)
49
50 client := &Client{
51 baseURL: baseURL,
52 httpClient: httpClient,
53 }
54
55 for _, opt := range opts {
56 err := opt(client)
57 if err != nil {
58 return nil, err
59 }
60 }
61
62 return client, nil
63 }
64
65 func (c *Client) newRequest(method, resource string, body io.Reader) (*http.Request, error) {
66 u, err := c.baseURL.Parse(resource)
67 if err != nil {
68 return nil, err
69 }
70
71 req, err := http.NewRequest(method, u.String(), body)
72 if err != nil {
73 return nil, err
74 }
75
76 req.Header.Set(contentTypeHeader, contentTypeJSON)
77
78 if c.UserAgent != "" {
79 req.Header.Set("User-Agent", c.UserAgent)
80 }
81
82 return req, nil
83 }
84
85 func (c *Client) do(req *http.Request, v interface{}) (*http.Response, error) {
86 resp, err := c.httpClient.Do(req)
87 if err != nil {
88 return nil, err
89 }
90 defer func() { _ = resp.Body.Close() }()
91
92 if err = checkResponse(resp); err != nil {
93 return resp, err
94 }
95
96 if v == nil {
97 return resp, nil
98 }
99
100 raw, err := io.ReadAll(resp.Body)
101 if err != nil {
102 return resp, fmt.Errorf("failed to read body: %w", err)
103 }
104
105 if err = json.Unmarshal(raw, v); err != nil {
106 return resp, fmt.Errorf("unmarshaling %T error: %w: %s", v, err, string(raw))
107 }
108
109 return resp, nil
110 }
111
112 func checkResponse(resp *http.Response) error {
113 if c := resp.StatusCode; 200 <= c && c <= 299 {
114 return nil
115 }
116
117 data, err := io.ReadAll(resp.Body)
118 if err == nil && data != nil {
119 errorResponse := new(ResponseError)
120 err = json.Unmarshal(data, errorResponse)
121 if err != nil {
122 return fmt.Errorf("unmarshaling ErrorResponse error: %w: %s", err, string(data))
123 }
124
125 return errorResponse
126 }
127
128 return fmt.Errorf("status code: %d %s", resp.StatusCode, resp.Status)
129 }
130
131 // WithBaseURL Allows to define a custom base URL.
132 func WithBaseURL(rawBaseURL string) func(*Client) error {
133 return func(client *Client) error {
134 if rawBaseURL == "" {
135 return nil
136 }
137
138 baseURL, err := url.Parse(rawBaseURL)
139 if err != nil {
140 return err
141 }
142
143 client.baseURL = baseURL
144
145 return nil
146 }
147 }
148