/
client.go
150 lines (126 loc) · 4.2 KB
/
client.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
package population
import (
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"strings"
"github.com/pkg/errors"
dperrors "github.com/ONSdigital/dp-api-clients-go/v2/errors"
"github.com/ONSdigital/dp-api-clients-go/v2/headers"
"github.com/ONSdigital/dp-api-clients-go/v2/health"
"github.com/ONSdigital/dp-healthcheck/healthcheck"
"github.com/ONSdigital/log.go/v2/log"
)
const service = "population-types-api"
// Client is a Cantabular Population Types API client
type Client struct {
hcCli *health.Client
baseURL *url.URL
}
// NewClient creates a new instance of Client with a given Population Type API URL
func NewClient(apiURL string) (*Client, error) {
client := health.NewClient(service, apiURL)
return NewWithHealthClient(client)
}
// NewWithHealthClient creates a new instance of Client,
// reusing the URL and Clienter from the provided health check client
func NewWithHealthClient(hcCli *health.Client) (*Client, error) {
client := health.NewClientWithClienter(service, hcCli.URL, hcCli.Client)
baseURL, err := url.Parse(client.URL)
if err != nil {
return nil, errors.Wrap(err, "error parsing URL")
}
// The Parse method on `url.URL` uses a trailing slash to determine
// how relative URLs are joined.
if !strings.HasSuffix(baseURL.Path, "/") {
baseURL.Path = baseURL.Path + "/"
}
return &Client{hcCli: client, baseURL: baseURL}, nil
}
// Checker calls recipe api health endpoint and returns a check object to the caller
func (c *Client) Checker(ctx context.Context, check *healthcheck.CheckState) error {
return c.hcCli.Checker(ctx, check)
}
func (c *Client) createGetRequest(ctx context.Context, userAuthToken, serviceAuthToken, urlPath string, urlValues url.Values) (*http.Request, error) {
populationURL, err := c.baseURL.Parse(urlPath)
if err != nil {
return &http.Request{}, dperrors.New(
errors.Wrap(err, "failed to parse areas URL"),
http.StatusInternalServerError,
log.Data{},
)
}
populationURL.RawQuery = urlValues.Encode()
reqURL := populationURL.String()
req, err := newRequest(ctx, http.MethodGet, reqURL, nil, userAuthToken, serviceAuthToken)
if err != nil {
return &http.Request{}, dperrors.New(
errors.Wrap(err, "failed to create request"),
http.StatusBadRequest,
log.Data{},
)
}
return req, nil
}
func (c *Client) createGetDimensionsDescriptionRequest(ctx context.Context, userAuthToken, serviceAuthToken, urlPath string, urlValues url.Values) (*http.Request, error) {
populationURL, err := c.baseURL.Parse(urlPath)
if err != nil {
return &http.Request{}, dperrors.New(
errors.Wrap(err, "failed to parse areas URL"),
http.StatusInternalServerError,
log.Data{},
)
}
populationURL.RawQuery = urlValues.Encode()
reqURL := populationURL.String()
req, err := newRequest(ctx, http.MethodGet, reqURL, nil, userAuthToken, serviceAuthToken)
if err != nil {
return &http.Request{}, dperrors.New(
errors.Wrap(err, "failed to create request"),
http.StatusBadRequest,
log.Data{},
)
}
return req, nil
}
func checkGetResponse(resp *http.Response) error {
if resp.StatusCode != http.StatusOK {
b, err := io.ReadAll(resp.Body)
if err != nil {
return fmt.Errorf("failed to read error response body: %w", err)
}
var errorResp ErrorResp
if err := json.Unmarshal(b, &errorResp); err != nil {
return dperrors.New(
fmt.Errorf("failed to unmarshal response body: %w", err),
resp.StatusCode,
log.Data{
"response_body": string(b),
},
)
}
return dperrors.New(
fmt.Errorf("error response from Population Type API: %w", errorResp),
resp.StatusCode,
nil,
)
}
return nil
}
// newRequest creates a new http.Request with auth headers
func newRequest(ctx context.Context, method, url string, body io.Reader, userAuthToken, serviceAuthToken string) (*http.Request, error) {
req, err := http.NewRequestWithContext(ctx, method, url, body)
if err != nil {
return nil, errors.Wrap(err, "failed to create request")
}
if err := headers.SetAuthToken(req, userAuthToken); err != nil {
return nil, errors.Wrap(err, "failed to set auth token header")
}
if err := headers.SetServiceAuthToken(req, serviceAuthToken); err != nil {
return nil, errors.Wrap(err, "failed to set service token header")
}
return req, nil
}