Skip to content

Commit

Permalink
feat: always throw sdk.Error from all Fingerprint API methods
Browse files Browse the repository at this point in the history
  • Loading branch information
TheUnderScorer committed Nov 18, 2024
1 parent b665145 commit 3657096
Show file tree
Hide file tree
Showing 17 changed files with 122 additions and 87 deletions.
5 changes: 5 additions & 0 deletions .changeset/healthy-socks-camp.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"fingerprint-pro-server-api-go-sdk": major
---

Always throw `sdk.Error` from all Fingerprint API methods
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ func main() {
if errors.As(err, &tooManyRequestsError) {
log.Fatalf("Too many requests, retry after %d seconds", tooManyRequestsError.RetryAfter())
} else {
// You can also use err.Model() and err.Body() to get more details about the error
log.Printf("Error %s: %v", err.Code(), err)
log.Fatal(err)
}
}
Expand All @@ -84,12 +86,13 @@ func main() {

event, httpRes, err := client.FingerprintApi.GetEvent(auth, requestId)
if err != nil {
// You can also use err.Model() and err.Body() to get more details about the error
log.Printf("Error %s: %v", err.Code(), err)
log.Fatal(err)
}
}

if event.Products.Identification != nil {
fmt.Printf("Got response with Identification: %v", event.Products.Identification)

}
}
```
Expand Down
4 changes: 2 additions & 2 deletions example/getRelatedVisitors.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ func main() {
}

// Print full response as JSON
responseJsonData, err := json.MarshalIndent(response, "", " ")
if err != nil {
responseJsonData, jsonErr := json.MarshalIndent(response, "", " ")
if jsonErr != nil {
fmt.Println("Error:", err)
return
}
Expand Down
6 changes: 3 additions & 3 deletions example/getVisits.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,9 @@ func main() {
fmt.Printf("Got response with visitorId: %s", response.VisitorId)

// Print full response as JSON
responseJsonData, err := json.MarshalIndent(response, "", " ")
if err != nil {
fmt.Println("Error:", err)
responseJsonData, jsonErr := json.MarshalIndent(response, "", " ")
if jsonErr != nil {
fmt.Println("Error:", jsonErr)
return
}
fmt.Println(string(responseJsonData))
Expand Down
37 changes: 34 additions & 3 deletions sdk/api_error.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,22 @@
package sdk

import "errors"

// Error defines base interface of all errors returned by this SDK
type Error interface {
error

// Body returns the raw bytes of the response, if available.
Body() []byte

// Code returns the error code.
Code() ErrorCode

// Model returns the unpacked model of the error. When error was created with WrapApiError, it returns the original error.
// If error was thrown after we get the HTTP Response, it contains model parsed from response body.
Model() any
}

// ApiError Provides access to the body, error and model on returned errors.
type ApiError struct {
body []byte
Expand All @@ -8,22 +25,36 @@ type ApiError struct {
code ErrorCode
}

func WrapApiError(err error) *ApiError {
if err == nil {
return nil
}

var apiError *ApiError
if errors.As(err, &apiError) {
return apiError
}

return &ApiError{
error: err.Error(),
code: FAILED,
model: err,
}
}

// Error returns non-empty string if there was an error.
func (e ApiError) Error() string {
return e.error
}

// Body returns the raw bytes of the response
func (e ApiError) Body() []byte {
return e.body
}

// Model returns the unpacked model of the error
func (e ApiError) Model() any {
return e.model
}

// Code returns the error code
func (e ApiError) Code() ErrorCode {
return e.code
}
10 changes: 5 additions & 5 deletions sdk/api_fingerprint.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ type FingerprintApiServiceInterface interface {
* @param visitorId The [visitor ID](https://dev.fingerprint.com/reference/get-function#visitorid) you want to delete.
*/
DeleteVisitorData(ctx context.Context, visitorId string) (*http.Response, error)
DeleteVisitorData(ctx context.Context, visitorId string) (*http.Response, Error)

/*
FingerprintApiService Get event by request ID
Expand All @@ -34,7 +34,7 @@ type FingerprintApiServiceInterface interface {
* @param requestId The unique [identifier](https://dev.fingerprint.com/reference/get-function#requestid) of each identification request.
@return EventsGetResponse
*/
GetEvent(ctx context.Context, requestId string) (EventsGetResponse, *http.Response, error)
GetEvent(ctx context.Context, requestId string) (EventsGetResponse, *http.Response, Error)

/*
FingerprintApiService Get Related Visitors
Expand All @@ -43,7 +43,7 @@ type FingerprintApiServiceInterface interface {
* @param visitorId The [visitor ID](https://dev.fingerprint.com/reference/get-function#visitorid) for which you want to find the other visitor IDs that originated from the same mobile device.
@return RelatedVisitorsResponse
*/
GetRelatedVisitors(ctx context.Context, visitorId string) (RelatedVisitorsResponse, *http.Response, error)
GetRelatedVisitors(ctx context.Context, visitorId string) (RelatedVisitorsResponse, *http.Response, Error)

/*
FingerprintApiService Get visits by visitor ID
Expand All @@ -58,7 +58,7 @@ type FingerprintApiServiceInterface interface {
* @param "Before" (int64) - ⚠️ Deprecated pagination method, please use `paginationKey` instead. Timestamp (in milliseconds since epoch) used to paginate results.
@return VisitorsGetResponse
*/
GetVisits(ctx context.Context, visitorId string, opts *FingerprintApiGetVisitsOpts) (VisitorsGetResponse, *http.Response, error)
GetVisits(ctx context.Context, visitorId string, opts *FingerprintApiGetVisitsOpts) (VisitorsGetResponse, *http.Response, Error)

/*
FingerprintApiService Update an event with a given request ID
Expand All @@ -68,7 +68,7 @@ type FingerprintApiServiceInterface interface {
* @param requestId The unique event [identifier](https://dev.fingerprint.com/reference/get-function#requestid).
*/
UpdateEvent(ctx context.Context, body EventsUpdateRequest, requestId string) (*http.Response, error)
UpdateEvent(ctx context.Context, body EventsUpdateRequest, requestId string) (*http.Response, Error)
}

type requestDefinition struct {
Expand Down
32 changes: 16 additions & 16 deletions sdk/api_fingerprint_impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ type apiRequest struct {
body io.Reader
}

func (f *FingerprintApiService) DeleteVisitorData(ctx context.Context, visitorId string) (*http.Response, error) {
func (f *FingerprintApiService) DeleteVisitorData(ctx context.Context, visitorId string) (*http.Response, Error) {
request := apiRequest{
definition: createDeleteVisitorDataDefinition(),
pathParams: []string{visitorId},
Expand All @@ -32,7 +32,7 @@ func (f *FingerprintApiService) DeleteVisitorData(ctx context.Context, visitorId
return f.doRequest(ctx, request, nil)
}

func (f *FingerprintApiService) GetEvent(ctx context.Context, requestId string) (EventsGetResponse, *http.Response, error) {
func (f *FingerprintApiService) GetEvent(ctx context.Context, requestId string) (EventsGetResponse, *http.Response, Error) {
request := apiRequest{
definition: createGetEventDefinition(),
pathParams: []string{requestId},
Expand All @@ -46,10 +46,10 @@ func (f *FingerprintApiService) GetEvent(ctx context.Context, requestId string)
return eventResponse, response, err
}

func (f *FingerprintApiService) UpdateEvent(ctx context.Context, body EventsUpdateRequest, requestId string) (*http.Response, error) {
func (f *FingerprintApiService) UpdateEvent(ctx context.Context, body EventsUpdateRequest, requestId string) (*http.Response, Error) {
bodyBytes, err := json.Marshal(body)
if err != nil {
return nil, err
return nil, WrapApiError(err)
}

request := apiRequest{
Expand All @@ -59,12 +59,12 @@ func (f *FingerprintApiService) UpdateEvent(ctx context.Context, body EventsUpda
method: http.MethodPut,
}

httpResponse, err := f.doRequest(ctx, request, nil)
httpResponse, apiError := f.doRequest(ctx, request, nil)

return httpResponse, err
return httpResponse, apiError
}

func (f *FingerprintApiService) GetVisits(ctx context.Context, visitorId string, opts *FingerprintApiGetVisitsOpts) (VisitorsGetResponse, *http.Response, error) {
func (f *FingerprintApiService) GetVisits(ctx context.Context, visitorId string, opts *FingerprintApiGetVisitsOpts) (VisitorsGetResponse, *http.Response, Error) {
request := apiRequest{
definition: createGetVisitsDefinition(),
pathParams: []string{visitorId},
Expand All @@ -78,7 +78,7 @@ func (f *FingerprintApiService) GetVisits(ctx context.Context, visitorId string,
return response, httpResponse, err
}

func (f *FingerprintApiService) GetRelatedVisitors(ctx context.Context, visitorId string) (RelatedVisitorsResponse, *http.Response, error) {
func (f *FingerprintApiService) GetRelatedVisitors(ctx context.Context, visitorId string) (RelatedVisitorsResponse, *http.Response, Error) {
request := apiRequest{
definition: createGetRelatedVisitorsDefinition(),
queryParams: map[string]any{
Expand Down Expand Up @@ -111,11 +111,11 @@ func (f *FingerprintApiService) getPath(definition requestDefinition, params ...
return f.cfg.basePath + definition.GetPath(params...)
}

func (f *FingerprintApiService) doRequest(ctx context.Context, apiRequest apiRequest, result any) (*http.Response, error) {
func (f *FingerprintApiService) doRequest(ctx context.Context, apiRequest apiRequest, result any) (*http.Response, Error) {
path := f.getPath(apiRequest.definition, apiRequest.pathParams...)
requestUrl, err := url.Parse(path)
if err != nil {
return nil, err
return nil, WrapApiError(err)
}

query := requestUrl.Query()
Expand All @@ -130,28 +130,28 @@ func (f *FingerprintApiService) doRequest(ctx context.Context, apiRequest apiReq

request, err := f.prepareRequest(ctx, requestUrl, apiRequest.method, apiRequest.body)
if err != nil {
return nil, err
return nil, WrapApiError(err)
}

httpResponse, err := f.cfg.HTTPClient.Do(request)
if err != nil {
return httpResponse, err
return httpResponse, WrapApiError(err)
}

body, err := io.ReadAll(httpResponse.Body)
if err != nil {
return httpResponse, err
return httpResponse, WrapApiError(err)
}

if isResponseOk(httpResponse) {
if result != nil {
err = json.Unmarshal(body, &result)
}

return httpResponse, err
return httpResponse, WrapApiError(err)
}

err = handleErrorResponse(body, httpResponse, apiRequest.definition)
apiError := handleErrorResponse(body, httpResponse, apiRequest.definition)

return httpResponse, handlePotentialTooManyRequestsResponse(httpResponse, err)
return httpResponse, handlePotentialTooManyRequestsResponse(httpResponse, apiError)
}
14 changes: 7 additions & 7 deletions sdk/request_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@ import (
"strconv"
)

func handlePotentialTooManyRequestsResponse(httpResponse *http.Response, err error) error {
func handlePotentialTooManyRequestsResponse(httpResponse *http.Response, err Error) Error {
if err == nil {
return err
return nil
}

var e ApiError
var e *ApiError

if httpResponse.StatusCode != http.StatusTooManyRequests {
return err
return WrapApiError(err)
}

if errors.As(err, &e) {
Expand Down Expand Up @@ -67,7 +67,7 @@ func addIntegrationInfoToQuery(query *url.Values) {
query.Add("ii", IntegrationInfo)
}

func handleErrorResponse(body []byte, httpResponse *http.Response, definition requestDefinition) error {
func handleErrorResponse(body []byte, httpResponse *http.Response, definition requestDefinition) *ApiError {
apiError := ApiError{
body: body,
error: httpResponse.Status,
Expand All @@ -84,7 +84,7 @@ func handleErrorResponse(body []byte, httpResponse *http.Response, definition re
if err != nil {
apiError.error = err.Error()

return apiError
return &apiError
}

switch v := model.(type) {
Expand All @@ -99,7 +99,7 @@ func handleErrorResponse(body []byte, httpResponse *http.Response, definition re
apiError.model = model
}

return apiError
return &apiError
}

func isResponseOk(httpResponse *http.Response) bool {
Expand Down
7 changes: 5 additions & 2 deletions template/README.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ func main() {
if errors.As(err, &tooManyRequestsError) {
log.Fatalf("Too many requests, retry after %d seconds", tooManyRequestsError.RetryAfter())
} else {
// You can also use err.Model() and err.Body() to get more details about the error
log.Printf("Error %s: %v", err.Code(), err)
log.Fatal(err)
}
}
Expand All @@ -92,12 +94,13 @@ func main() {

event, httpRes, err := client.FingerprintApi.GetEvent(auth, requestId)
if err != nil {
// You can also use err.Model() and err.Body() to get more details about the error
log.Printf("Error %s: %v", err.Code(), err)
log.Fatal(err)
}
}

if event.Products.Identification != nil {
fmt.Printf("Got response with Identification: %v", event.Products.Identification)
}
}
```
Expand Down
2 changes: 1 addition & 1 deletion template/api.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ type {{classname}}ServiceInterface interface {
{{/required}}{{/allParams}}{{/hasOptionalParams}}
{{#returnType}}@return {{{returnType}}}{{/returnType}}
*/
{{{nickname}}}(ctx context.Context{{#hasParams}}, {{/hasParams}}{{#allParams}}{{#required}}{{paramName}} {{{dataType}}}{{#hasMore}}, {{/hasMore}}{{/required}}{{/allParams}}{{#hasOptionalParams}}opts *{{{classname}}}{{{nickname}}}Opts{{/hasOptionalParams}}) ({{#returnType}}{{{returnType}}}, {{/returnType}}*http.Response, error)
{{{nickname}}}(ctx context.Context{{#hasParams}}, {{/hasParams}}{{#allParams}}{{#required}}{{paramName}} {{{dataType}}}{{#hasMore}}, {{/hasMore}}{{/required}}{{/allParams}}{{#hasOptionalParams}}opts *{{{classname}}}{{{nickname}}}Opts{{/hasOptionalParams}}) ({{#returnType}}{{{returnType}}}, {{/returnType}}*http.Response, Error)

{{/operation}}
}
Expand Down
8 changes: 4 additions & 4 deletions test/DeleteVisitorData_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func TestDeleteVisitorData(t *testing.T) {

res, err := client.FingerprintApi.DeleteVisitorData(ctx, "123")

assert.NoError(t, err)
assert.Nil(t, err)
assert.NotNil(t, res)
assert.Equal(t, res.StatusCode, 200)
})
Expand Down Expand Up @@ -91,7 +91,7 @@ func TestDeleteVisitorData(t *testing.T) {
assert.NotNil(t, res)
assert.Equal(t, res.StatusCode, 404)

errorModel := err.(sdk.ApiError).Model().(*sdk.ErrorResponse)
errorModel := err.(*sdk.ApiError).Model().(*sdk.ErrorResponse)
assert.IsType(t, errorModel, &sdk.ErrorResponse{})
assert.Equal(t, errorModel, &mockResponse)
})
Expand Down Expand Up @@ -190,7 +190,7 @@ func TestDeleteVisitorData(t *testing.T) {
assert.NotNil(t, res)
assert.Equal(t, res.StatusCode, 400)

errorModel := err.(sdk.ApiError).Model().(*sdk.ErrorResponse)
errorModel := err.(*sdk.ApiError).Model().(*sdk.ErrorResponse)
assert.IsType(t, errorModel, &sdk.ErrorResponse{})
assert.Equal(t, errorModel, &mockResponse)
})
Expand Down Expand Up @@ -237,7 +237,7 @@ func TestDeleteVisitorData(t *testing.T) {
assert.NotNil(t, res)
assert.Equal(t, res.StatusCode, 403)

errorModel := err.(sdk.ApiError).Model().(*sdk.ErrorResponse)
errorModel := err.(*sdk.ApiError).Model().(*sdk.ErrorResponse)
assert.IsType(t, errorModel, &sdk.ErrorResponse{})
assert.Equal(t, errorModel, &mockResponse)
})
Expand Down
Loading

0 comments on commit 3657096

Please sign in to comment.