diff options
Diffstat (limited to 'clients/netbox/http.go')
-rw-r--r-- | clients/netbox/http.go | 174 |
1 files changed, 174 insertions, 0 deletions
diff --git a/clients/netbox/http.go b/clients/netbox/http.go new file mode 100644 index 0000000..21a2b4b --- /dev/null +++ b/clients/netbox/http.go | |||
@@ -0,0 +1,174 @@ | |||
1 | package netbox | ||
2 | |||
3 | import ( | ||
4 | "bytes" | ||
5 | "context" | ||
6 | "encoding/json" | ||
7 | "fmt" | ||
8 | "net/http" | ||
9 | "net/url" | ||
10 | ) | ||
11 | |||
12 | // NetboxHttpClient is an HTTP client for the Netbox API. It is very | ||
13 | // low-level and should not be consumed by most clients. Instead use a | ||
14 | // client implementing NetboxClient. | ||
15 | type NetboxHttpClient struct { | ||
16 | ApiKey string | ||
17 | Endpoint url.URL | ||
18 | } | ||
19 | |||
20 | func MustNewNetboxHttpClient(apiKey, endpoint string) *NetboxHttpClient { | ||
21 | u, err := url.Parse(endpoint) | ||
22 | if err != nil { | ||
23 | panic(err) | ||
24 | } | ||
25 | return &NetboxHttpClient{apiKey, *u} | ||
26 | } | ||
27 | |||
28 | func (c *NetboxHttpClient) Do(ctx context.Context, r NetboxRequest, out any) error { | ||
29 | u := c.Endpoint // Store a local copy, requests may mutate it | ||
30 | req, err := r.BuildRequest(ctx, &u) | ||
31 | if err != nil { | ||
32 | return err | ||
33 | } | ||
34 | |||
35 | req.Header.Add("Authorization", fmt.Sprintf("Token %s", c.ApiKey)) | ||
36 | |||
37 | res, err := http.DefaultClient.Do(req) | ||
38 | if err != nil { | ||
39 | return err | ||
40 | } | ||
41 | defer res.Body.Close() | ||
42 | |||
43 | if res.StatusCode != http.StatusOK { | ||
44 | apiError := &ApiError{Status: res.StatusCode} | ||
45 | if err = json.NewDecoder(res.Body).Decode(apiError); err != nil { | ||
46 | return fmt.Errorf("Netbox JSON decode error while parsing error with status %d: %w", res.StatusCode, err) | ||
47 | } | ||
48 | return apiError | ||
49 | } | ||
50 | |||
51 | if out != nil { | ||
52 | return json.NewDecoder(res.Body).Decode(out) | ||
53 | } | ||
54 | |||
55 | return nil | ||
56 | } | ||
57 | |||
58 | type NetboxRequest interface { | ||
59 | BuildRequest(context.Context, *url.URL) (*http.Request, error) | ||
60 | } | ||
61 | |||
62 | type NetboxGraphQLRequest struct { | ||
63 | Query string | ||
64 | } | ||
65 | |||
66 | var _ NetboxRequest = (*NetboxGraphQLRequest)(nil) | ||
67 | |||
68 | func (r *NetboxGraphQLRequest) BuildRequest(ctx context.Context, host *url.URL) (*http.Request, error) { | ||
69 | q, err := json.Marshal(map[string]string{"query": r.Query}) | ||
70 | if err != nil { | ||
71 | return nil, err | ||
72 | } | ||
73 | |||
74 | host.Path = "/graphql/" | ||
75 | |||
76 | req, err := http.NewRequestWithContext( | ||
77 | ctx, | ||
78 | http.MethodPost, | ||
79 | host.String(), | ||
80 | bytes.NewBuffer(q), | ||
81 | ) | ||
82 | if err != nil { | ||
83 | return nil, err | ||
84 | } | ||
85 | |||
86 | req.Header.Add("Content-Type", "application/json") | ||
87 | req.Header.Add("Accept", "application/json") | ||
88 | |||
89 | return req, nil | ||
90 | } | ||
91 | |||
92 | type NetboxGetRequest struct { | ||
93 | url.Values | ||
94 | Path string | ||
95 | } | ||
96 | |||
97 | var _ NetboxRequest = (*NetboxGetRequest)(nil) | ||
98 | |||
99 | func NewNetboxGetRequest(path string) *NetboxGetRequest { | ||
100 | return &NetboxGetRequest{url.Values{}, path} | ||
101 | } | ||
102 | |||
103 | func (r *NetboxGetRequest) BuildRequest(ctx context.Context, host *url.URL) (*http.Request, error) { | ||
104 | host.Path = r.Path | ||
105 | host.RawQuery = r.Encode() | ||
106 | |||
107 | req, err := http.NewRequestWithContext( | ||
108 | ctx, | ||
109 | http.MethodGet, | ||
110 | host.String(), | ||
111 | nil, | ||
112 | ) | ||
113 | if err != nil { | ||
114 | return nil, err | ||
115 | } | ||
116 | |||
117 | req.Header.Add("Accept", "application/json") | ||
118 | |||
119 | return req, nil | ||
120 | } | ||
121 | |||
122 | type NetboxRawGet struct { | ||
123 | Url string | ||
124 | } | ||
125 | |||
126 | var _ NetboxRequest = (*NetboxRawGet)(nil) | ||
127 | |||
128 | func (r *NetboxRawGet) BuildRequest(ctx context.Context, host *url.URL) (*http.Request, error) { | ||
129 | req, err := http.NewRequestWithContext( | ||
130 | ctx, | ||
131 | http.MethodGet, | ||
132 | r.Url, | ||
133 | nil, | ||
134 | ) | ||
135 | if err != nil { | ||
136 | return nil, err | ||
137 | } | ||
138 | |||
139 | req.Header.Add("Accept", "application/json") | ||
140 | |||
141 | return req, nil | ||
142 | } | ||
143 | |||
144 | type NetboxJsonRequest struct { | ||
145 | Path string | ||
146 | Method string | ||
147 | Body any | ||
148 | } | ||
149 | |||
150 | var _ NetboxRequest = (*NetboxJsonRequest)(nil) | ||
151 | |||
152 | func (r *NetboxJsonRequest) BuildRequest(ctx context.Context, host *url.URL) (*http.Request, error) { | ||
153 | host.Path = r.Path | ||
154 | |||
155 | body, err := json.Marshal(r.Body) | ||
156 | if err != nil { | ||
157 | return nil, err | ||
158 | } | ||
159 | |||
160 | req, err := http.NewRequestWithContext( | ||
161 | ctx, | ||
162 | r.Method, | ||
163 | host.String(), | ||
164 | bytes.NewBuffer(body), | ||
165 | ) | ||
166 | if err != nil { | ||
167 | return nil, err | ||
168 | } | ||
169 | |||
170 | req.Header.Add("Content-Type", "application/json") | ||
171 | req.Header.Add("Accept", "application/json") | ||
172 | |||
173 | return req, nil | ||
174 | } | ||