1 package rest
2 3 import (
4 "bytes"
5 "encoding/json"
6 "fmt"
7 "io"
8 "net/http"
9 "net/url"
10 )
11 12 const (
13 contentType = "application/json"
14 )
15 16 // Request will be used by all repositories and the client.
17 // The Request struct can be transformed to a http request with method, url and optional body.
18 type Request struct {
19 // Endpoint is the api endpoint, without the server, which we will receive the request,
20 // like: '/products'
21 Endpoint string
22 // Parameters is a map of strings (url.Values) that will be used to add
23 // http query strings to the request, like '/domains?tags=123'
24 Parameters url.Values
25 // Body is left as an interface because the Request is not coupled to any specific Request.Body struct
26 Body interface{}
27 // TestMode is used when users want to tinker with the api without touching their real data
28 TestMode bool
29 }
30 31 // GetJSONBody returns the request object as a json byte array
32 func (r *Request) GetJSONBody() ([]byte, error) {
33 return json.Marshal(r.Body)
34 }
35 36 // GetBodyReader returns an io.Reader for the json marshalled body of this request
37 // this will be used by the writer used in the client.
38 func (r *Request) GetBodyReader() (io.Reader, error) {
39 // try to get the marshalled body
40 body, err := r.GetJSONBody()
41 if err != nil {
42 return nil, fmt.Errorf("error when marshaling request: %w", err)
43 }
44 45 return bytes.NewReader(body), nil
46 }
47 48 // GetHTTPRequest generates and returns a http.Request object.
49 // It does this with the Request struct and the basePath and method,
50 // that are provided by the client itself.
51 func (r *Request) GetHTTPRequest(basePath string, method string) (*http.Request, error) {
52 requestURL := basePath + r.Endpoint
53 54 var bodyReader io.Reader
55 if r.Body != nil {
56 reader, err := r.GetBodyReader()
57 if err != nil {
58 return nil, err
59 }
60 61 bodyReader = reader
62 }
63 64 request, err := http.NewRequest(method, requestURL, bodyReader)
65 if err != nil {
66 return nil, err
67 }
68 69 // set json headers, because our this library sends and expects that
70 request.Header.Set("Content-Type", contentType)
71 request.Header.Set("Accept", contentType)
72 73 // if TestMode is true we always add a test=1 http query string to the url,
74 // this is used when users want to tinker with the api without changing their production data
75 if r.TestMode {
76 // if Parameters is not set yet, we create a new url.Values and set it
77 if r.Parameters == nil {
78 r.Parameters = url.Values{}
79 }
80 81 r.Parameters.Add("test", "1")
82 }
83 84 // set the custom parameters on the rawquery
85 request.URL.RawQuery = r.Parameters.Encode()
86 87 return request, nil
88 }
89