From ad0b53634e08a720009bbbf997da3bcff9affa7d Mon Sep 17 00:00:00 2001 From: TJ Hoplock Date: Thu, 4 Apr 2024 14:40:13 -0400 Subject: [PATCH 1/3] feat: add support for setting URL parameters on client requests Several NS1 API endpoints allow setting URL parameters to alter the behavior of the endpoint. For instance, a `limit` parameter can be set on the `/account/activity` endpoint to control how many events are returned in the response. This would be useful in #233. This commit adds support for URL parameters by adding a new struct `Param` to the `rest` package, and updating `rest.client.Do()` and `rest.client.DoWithPagination()` to accept a variable amount of params. In order for higher level services based off of the common service to set url params, they will need to pass them through when invoking the client's `Do()` method, like so: ```golang resp, err := s.client.Do(req, &al, params...) ``` --- mockns1/testcase.go | 7 +++++++ rest/client.go | 16 +++++++++++++--- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/mockns1/testcase.go b/mockns1/testcase.go index d6c3d56..b64f740 100644 --- a/mockns1/testcase.go +++ b/mockns1/testcase.go @@ -8,6 +8,7 @@ import ( "strings" "github.com/stretchr/testify/assert" + api "gopkg.in/ns1/ns1-go.v2/rest" ) type testCase struct { @@ -29,6 +30,7 @@ func (s *Service) AddTestCase( method, uri string, returnStatus int, requestHeaders, responseHeaders http.Header, requestBody, responseBody interface{}, + params ...api.Param, ) error { s.stopTimer() defer s.startTimer() @@ -36,6 +38,11 @@ func (s *Service) AddTestCase( if !strings.HasPrefix(uri, "/v1/") { uri = "/v1/" + uri } + + for _, p := range params { + uri = fmt.Sprintf("%s?%s=%s", uri, p.Key, p.Value) + } + uri = strings.Replace(uri, "//", "/", -1) tc := &testCase{ diff --git a/rest/client.go b/rest/client.go index 038b9d7..f6acf45 100644 --- a/rest/client.go +++ b/rest/client.go @@ -185,10 +185,20 @@ func SetDDIAPI() func(*Client) { return func(c *Client) { c.DDI = true } } +type Param struct { + Key, Value string +} + // Do satisfies the Doer interface. resp will be nil if a non-HTTP error // occurs, otherwise it is available for inspection when the error reflects a // non-2XX response. -func (c Client) Do(req *http.Request, v interface{}) (*http.Response, error) { +func (c Client) Do(req *http.Request, v interface{}, params ...Param) (*http.Response, error) { + q := req.URL.Query() + for _, p := range params { + q.Set(p.Key, p.Value) + } + req.URL.RawQuery = q.Encode() + resp, err := c.httpClient.Do(req) if err != nil { return nil, err @@ -228,8 +238,8 @@ type NextFunc func(v *interface{}, uri string) (*http.Response, error) // Response is from the last URI visited - either the last page, or one that // responded with a non-2XX status. If a non-HTTP error occurs, resp will be // nil. -func (c Client) DoWithPagination(req *http.Request, v interface{}, f NextFunc) (*http.Response, error) { - resp, err := c.Do(req, v) +func (c Client) DoWithPagination(req *http.Request, v interface{}, f NextFunc, params ...Param) (*http.Response, error) { + resp, err := c.Do(req, v, params...) if err != nil { return resp, err } From 654e27915df3965c0a55823e3f293a877bb5bb89 Mon Sep 17 00:00:00 2001 From: TJ Hoplock Date: Thu, 4 Apr 2024 15:50:24 -0400 Subject: [PATCH 2/3] test: fix testcase handling of multiple url params Signed-off-by: TJ Hoplock --- mockns1/testcase.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/mockns1/testcase.go b/mockns1/testcase.go index b64f740..06ef323 100644 --- a/mockns1/testcase.go +++ b/mockns1/testcase.go @@ -39,8 +39,12 @@ func (s *Service) AddTestCase( uri = "/v1/" + uri } - for _, p := range params { - uri = fmt.Sprintf("%s?%s=%s", uri, p.Key, p.Value) + if len(params) > 0 { + uri = fmt.Sprintf("%s?%s=%s", uri, params[0].Key, params[0].Value) + + for _, p := range params[1:] { + uri = fmt.Sprintf("%s&%s=%s", uri, p.Key, p.Value) + } } uri = strings.Replace(uri, "//", "/", -1) From e9d50c49f5b429c906794f9e7eeed70d531ae93e Mon Sep 17 00:00:00 2001 From: TJ Hoplock Date: Thu, 4 Apr 2024 15:58:36 -0400 Subject: [PATCH 3/3] chore: add doc comments to `rest.Param`, update `rest.client.Do()` Updating doc comments to reflect what these URL parameter configs are/do. Signed-off-by: TJ Hoplock --- rest/client.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/rest/client.go b/rest/client.go index f6acf45..eed8f70 100644 --- a/rest/client.go +++ b/rest/client.go @@ -185,13 +185,15 @@ func SetDDIAPI() func(*Client) { return func(c *Client) { c.DDI = true } } +// Param is a container struct which holds a `Key` and `Value` field corresponding to the values of a URL parameter. type Param struct { Key, Value string } // Do satisfies the Doer interface. resp will be nil if a non-HTTP error // occurs, otherwise it is available for inspection when the error reflects a -// non-2XX response. +// non-2XX response. It accepts a variadic number of optional URL parameters to +// supply to the request. URL parameters are of type `rest.Param`. func (c Client) Do(req *http.Request, v interface{}, params ...Param) (*http.Response, error) { q := req.URL.Query() for _, p := range params { @@ -237,7 +239,9 @@ type NextFunc func(v *interface{}, uri string) (*http.Response, error) // DoWithPagination Does, and follows Link headers for pagination. The returned // Response is from the last URI visited - either the last page, or one that // responded with a non-2XX status. If a non-HTTP error occurs, resp will be -// nil. +// nil. It accepts a variadic number of optional URL parameters to supply to +// the underlying `.Do()` method request(s). URL parameters are of type +// `rest.Param`. func (c Client) DoWithPagination(req *http.Request, v interface{}, f NextFunc, params ...Param) (*http.Response, error) { resp, err := c.Do(req, v, params...) if err != nil {