From 8000da0b55cf095085715246ec87d74709fe8783 Mon Sep 17 00:00:00 2001 From: Adrien CABARBAYE Date: Mon, 1 Dec 2025 17:31:38 +0000 Subject: [PATCH 1/3] :camel: Upgrade to use latest golang-utils --- changes/20251201172918.bugfix | 1 + utils/api/api.go | 127 +++------------------------------- utils/api/api_test.go | 20 ------ utils/errors/mapping.go | 96 ++----------------------- utils/go.mod | 8 +-- utils/go.sum | 20 +++--- 6 files changed, 28 insertions(+), 244 deletions(-) create mode 100644 changes/20251201172918.bugfix diff --git a/changes/20251201172918.bugfix b/changes/20251201172918.bugfix new file mode 100644 index 0000000..3935577 --- /dev/null +++ b/changes/20251201172918.bugfix @@ -0,0 +1 @@ +:camel: Upgrade dependencies diff --git a/utils/api/api.go b/utils/api/api.go index 82d6e70..d4061ed 100644 --- a/utils/api/api.go +++ b/utils/api/api.go @@ -8,146 +8,37 @@ package api import ( "context" - "fmt" "net/http" - "reflect" - "strings" - - "github.com/perimeterx/marshmallow" "github.com/ARM-software/embedded-development-services-client-utils/utils/errors" - "github.com/ARM-software/golang-utils/utils/commonerrors" - "github.com/ARM-software/golang-utils/utils/parallelisation" - "github.com/ARM-software/golang-utils/utils/reflection" - "github.com/ARM-software/golang-utils/utils/safeio" + "github.com/ARM-software/golang-utils/utils/http/api" ) -const requiredFieldError = "no value given for required property" - // IsCallSuccessful determines whether an API response is successful or not +// Deprecated: Use github.com/ARM-software/golang-utils/utils/http/api instead func IsCallSuccessful(r *http.Response) bool { - if r == nil { - return false - } - return r.StatusCode >= http.StatusOK && r.StatusCode < http.StatusMultipleChoices + return api.IsCallSuccessful(r) } // CheckAPICallSuccess verifies whether an API response is successful or not and if not, populates an error with all the information needed. // errorContext corresponds to the description of what led to the error if error there is e.g. `Failed adding a user`. // resp corresponds to the HTTP response from a certain endpoint. The body of such response is not closed by this function. // apiErr corresponds to the error which may be returned by the HTTP client when calling the endpoint. -func CheckAPICallSuccess(ctx context.Context, errorContext string, resp *http.Response, apiErr error) (err error) { - err = parallelisation.DetermineContextError(ctx) - if err != nil { - return - } - if !IsCallSuccessful(resp) { - statusCode := 0 - errorMessage := strings.Builder{} - respErr := commonerrors.ErrUnexpected - if resp != nil { - statusCode = resp.StatusCode - respErr = errors.MapErrorToHTTPResponseCode(statusCode) - if respErr == nil { - respErr = commonerrors.ErrUnexpected - } - errorDetails, subErr := errors.FetchAPIErrorDescriptionWithContext(ctx, resp) - if commonerrors.Ignore(subErr, commonerrors.ErrMarshalling) != nil { - err = commonerrors.Join(commonerrors.New(respErr, errorContext), subErr) - return - } - if !reflection.IsEmpty(errorDetails) { - errorMessage.WriteString(errorDetails) - } - _ = resp.Body.Close() - } - extra := "" - if apiErr != nil { - extra = fmt.Sprintf("; %v", apiErr.Error()) - } - err = commonerrors.Newf(respErr, "%v (%d): %v%v", errorContext, statusCode, errorMessage.String(), extra) - } - return +func CheckAPICallSuccess(ctx context.Context, errorContext string, resp *http.Response, apiErr error) error { + return api.CheckAPICallSuccess(ctx, errorContext, errors.FetchAPIErrorDescriptionWithContext, resp, apiErr) } // CallAndCheckSuccess is a wrapper for making an API call and then checking success with `CheckAPICallSuccess` // errorContext corresponds to the description of what led to the error if error there is e.g. `Failed adding a user`. // apiCallFunc corresponds to a generic function that will be called to make the API call -func CallAndCheckSuccess[T any](ctx context.Context, errorContext string, apiCallFunc func(ctx context.Context) (*T, *http.Response, error)) (result *T, err error) { - if err = parallelisation.DetermineContextError(ctx); err != nil { - return - } - - result, resp, apiErr := apiCallFunc(ctx) - if resp != nil && resp.Body != nil { - defer func() { - if resp != nil && resp.Body != nil { - _ = resp.Body.Close() - } - }() - } - - err = checkResponse(ctx, apiErr, resp, result, errorContext) - return -} - -func checkResponse(ctx context.Context, apiErr error, resp *http.Response, result any, errorContext string) (err error) { - err = CheckAPICallSuccess(ctx, errorContext, resp, apiErr) - if err != nil { - return - } - - if apiErr != nil { - err = commonerrors.WrapError(commonerrors.ErrMarshalling, apiErr, "API call was successful but an error occurred during response marshalling") - if commonerrors.CorrespondTo(apiErr, requiredFieldError) { - return - } - if resp == nil || resp.Body == nil { - return - } - // At this point, the marshalling problem may be due to the present of unknown fields in the response due to an API extension. - // See https://github.com/OpenAPITools/openapi-generator/issues/21446 - var respB []byte - respB, err = safeio.ReadAll(ctx, resp.Body) - if err != nil { - return - } - _, err = marshmallow.Unmarshal(respB, result, marshmallow.WithSkipPopulateStruct(false), marshmallow.WithExcludeKnownFieldsFromMap(true)) - if err != nil { - err = commonerrors.WrapError(commonerrors.ErrMarshalling, err, "API call was successful but an error occurred during response marshalling") - return - } - } - if reflection.IsEmpty(result) { - err = commonerrors.New(commonerrors.ErrMarshalling, "unmarshalled response is empty") - return - } - return +func CallAndCheckSuccess[T any](ctx context.Context, errorContext string, apiCallFunc func(ctx context.Context) (*T, *http.Response, error)) (*T, error) { + return api.CallAndCheckSuccess[T](ctx, errorContext, errors.FetchAPIErrorDescriptionWithContext, apiCallFunc) } // GenericCallAndCheckSuccess is similar to CallAndCheckSuccess but for function returning interfaces rather than concrete types. // T must be an interface. // errorContext corresponds to the description of what led to the error if error there is e.g. `Failed adding a user`. // apiCallFunc corresponds to a generic function that will be called to make the API call -func GenericCallAndCheckSuccess[T any](ctx context.Context, errorContext string, apiCallFunc func(ctx context.Context) (T, *http.Response, error)) (result T, err error) { - if err = parallelisation.DetermineContextError(ctx); err != nil { - return - } - - result, resp, apiErr := apiCallFunc(ctx) - if resp != nil && resp.Body != nil { - _ = resp.Body.Close() - } - - err = checkResponse(ctx, apiErr, resp, result, errorContext) - if err != nil { - return - } - - if reflect.ValueOf(result).Kind() != reflect.Ptr { - err = commonerrors.Newf(commonerrors.ErrConflict, "result of the call is of type [%T] and so, not a pointer as expected", result) - return - } - - return +func GenericCallAndCheckSuccess[T any](ctx context.Context, errorContext string, apiCallFunc func(ctx context.Context) (T, *http.Response, error)) (T, error) { + return api.GenericCallAndCheckSuccess[T](ctx, errorContext, errors.FetchAPIErrorDescriptionWithContext, apiCallFunc) } diff --git a/utils/api/api_test.go b/utils/api/api_test.go index bed08d5..52d5581 100644 --- a/utils/api/api_test.go +++ b/utils/api/api_test.go @@ -24,26 +24,6 @@ import ( "github.com/ARM-software/golang-utils/utils/field" ) -func TestIsAPICallSuccessful(t *testing.T) { - t.Run("api call successful", func(t *testing.T) { - resp := _http.Response{StatusCode: 200} - isSuccessful := IsCallSuccessful(&resp) - assert.True(t, isSuccessful) - }) - - t.Run("api call unsuccessful", func(t *testing.T) { - resp := _http.Response{StatusCode: 400} - isSuccessful := IsCallSuccessful(&resp) - assert.False(t, isSuccessful) - }) - - t.Run("api call returns nothing", func(t *testing.T) { - resp := _http.Response{} - isSuccessful := IsCallSuccessful(&resp) - assert.False(t, isSuccessful) - }) -} - func TestCheckAPICallSuccess(t *testing.T) { t.Run("context cancelled", func(t *testing.T) { errMessage := "context cancelled" diff --git a/utils/errors/mapping.go b/utils/errors/mapping.go index c5ca3cf..9704f75 100644 --- a/utils/errors/mapping.go +++ b/utils/errors/mapping.go @@ -5,99 +5,11 @@ package errors import ( - "net/http" - - "github.com/ARM-software/golang-utils/utils/commonerrors" + "github.com/ARM-software/golang-utils/utils/http/errors" ) -// MapErrorToHTTPResponseCode maps a response status code to a common error. +// Deprecated: MapErrorToHTTPResponseCode maps a response status code to a common error. +// Use github.com/ARM-software/golang-utils/utils/http/errors instead func MapErrorToHTTPResponseCode(statusCode int) error { - if statusCode < http.StatusBadRequest { - return nil - } - switch statusCode { - case http.StatusBadRequest: - return commonerrors.ErrInvalid - case http.StatusUnauthorized: - return commonerrors.ErrUnauthorised - case http.StatusPaymentRequired: - return commonerrors.ErrUnknown - case http.StatusForbidden: - return commonerrors.ErrForbidden - case http.StatusNotFound: - return commonerrors.ErrNotFound - case http.StatusMethodNotAllowed: - return commonerrors.ErrNotFound - case http.StatusNotAcceptable: - return commonerrors.ErrUnsupported - case http.StatusProxyAuthRequired: - return commonerrors.ErrUnauthorised - case http.StatusRequestTimeout: - return commonerrors.ErrTimeout - case http.StatusConflict: - return commonerrors.ErrConflict - case http.StatusGone: - return commonerrors.ErrNotFound - case http.StatusLengthRequired: - return commonerrors.ErrInvalid - case http.StatusPreconditionFailed: - return commonerrors.ErrCondition - case http.StatusRequestEntityTooLarge: - return commonerrors.ErrTooLarge - case http.StatusRequestURITooLong: - return commonerrors.ErrTooLarge - case http.StatusUnsupportedMediaType: - return commonerrors.ErrUnsupported - case http.StatusRequestedRangeNotSatisfiable: - return commonerrors.ErrOutOfRange - case http.StatusExpectationFailed: - return commonerrors.ErrUnsupported - case http.StatusTeapot: - return commonerrors.ErrUnknown - case http.StatusMisdirectedRequest: - return commonerrors.ErrUnsupported - case http.StatusUnprocessableEntity: - return commonerrors.ErrMarshalling - case http.StatusLocked: - return commonerrors.ErrLocked - case http.StatusFailedDependency: - return commonerrors.ErrFailed - case http.StatusTooEarly: - return commonerrors.ErrUnexpected - case http.StatusUpgradeRequired: - return commonerrors.ErrUnsupported - case http.StatusPreconditionRequired: - return commonerrors.ErrCondition - case http.StatusTooManyRequests: - return commonerrors.ErrUnavailable - case http.StatusRequestHeaderFieldsTooLarge: - return commonerrors.ErrTooLarge - case http.StatusUnavailableForLegalReasons: - return commonerrors.ErrUnavailable - - case http.StatusInternalServerError: - return commonerrors.ErrUnexpected - case http.StatusNotImplemented: - return commonerrors.ErrNotImplemented - case http.StatusBadGateway: - return commonerrors.ErrUnavailable - case http.StatusServiceUnavailable: - return commonerrors.ErrUnavailable - case http.StatusGatewayTimeout: - return commonerrors.ErrTimeout - case http.StatusHTTPVersionNotSupported: - return commonerrors.ErrUnsupported - case http.StatusVariantAlsoNegotiates: - return commonerrors.ErrUnexpected - case http.StatusInsufficientStorage: - return commonerrors.ErrUnexpected - case http.StatusLoopDetected: - return commonerrors.ErrUnexpected - case http.StatusNotExtended: - return commonerrors.ErrUnexpected - case http.StatusNetworkAuthenticationRequired: - return commonerrors.ErrUnauthorised - default: - return commonerrors.ErrUnexpected - } + return errors.MapErrorToHTTPResponseCode(statusCode) } diff --git a/utils/go.mod b/utils/go.mod index 63fddb6..3030afe 100644 --- a/utils/go.mod +++ b/utils/go.mod @@ -3,8 +3,8 @@ module github.com/ARM-software/embedded-development-services-client-utils/utils go 1.25 require ( - github.com/ARM-software/embedded-development-services-client/client v1.82.0 - github.com/ARM-software/golang-utils/utils v1.130.0 + github.com/ARM-software/embedded-development-services-client/client v1.99.0 + github.com/ARM-software/golang-utils/utils v1.136.0 github.com/go-faker/faker/v4 v4.7.0 github.com/go-logr/logr v1.4.3 github.com/perimeterx/marshmallow v1.1.5 @@ -12,7 +12,7 @@ require ( go.uber.org/atomic v1.11.0 go.uber.org/goleak v1.3.0 go.uber.org/mock v0.6.0 - golang.org/x/sync v0.17.0 + golang.org/x/sync v0.18.0 ) require ( @@ -79,7 +79,7 @@ require ( github.com/yusufpapurcu/wmi v1.2.4 // indirect github.com/zalando/go-keyring v0.2.6 // indirect go.uber.org/multierr v1.10.0 // indirect - go.uber.org/zap v1.27.0 // indirect + go.uber.org/zap v1.27.1 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/crypto v0.41.0 // indirect golang.org/x/exp v0.0.0-20250718183923-645b1fa84792 // indirect diff --git a/utils/go.sum b/utils/go.sum index 658f3d0..84b9540 100644 --- a/utils/go.sum +++ b/utils/go.sum @@ -1,10 +1,10 @@ al.essio.dev/pkg/shellescape v1.5.1 h1:86HrALUujYS/h+GtqoB26SBEdkWfmMI6FubjXlsXyho= al.essio.dev/pkg/shellescape v1.5.1/go.mod h1:6sIqp7X2P6mThCQ7twERpZTuigpr6KbZWtls1U8I890= bitbucket.org/creachadair/stringset v0.0.9/go.mod h1:t+4WcQ4+PXTa8aQdNKe40ZP6iwesoMFWAxPGd3UGjyY= -github.com/ARM-software/embedded-development-services-client/client v1.82.0 h1:in5rwsgAlk7LYQROvU7BmSpoXCn4GA3V85W9/ODE0UM= -github.com/ARM-software/embedded-development-services-client/client v1.82.0/go.mod h1:jGywz6vB+i3RkF6J7LXSs0l4g6xHuGfbhW+iWMEODkY= -github.com/ARM-software/golang-utils/utils v1.130.0 h1:1LBLweH1qgJOxynOrcjbPeeoaiaPll8fYCa3ugLyyy4= -github.com/ARM-software/golang-utils/utils v1.130.0/go.mod h1:l1W+4uRhZCVEoDzozfomDG6erB6kZz63Q+DCn6xNGM8= +github.com/ARM-software/embedded-development-services-client/client v1.99.0 h1:to7sVrB6mkXmu+hIs3sa06FEB6bqhkv8zlKNL31fzNU= +github.com/ARM-software/embedded-development-services-client/client v1.99.0/go.mod h1:TPvvUCQkSffqCuP/eCN5ANpsyRKphbSwxaDqP7c5RBk= +github.com/ARM-software/golang-utils/utils v1.136.0 h1:/zYpzcXCG8Q/ycLVRwNMauzr0PFHqT/AK9K+DQOCFv0= +github.com/ARM-software/golang-utils/utils v1.136.0/go.mod h1:253UeNJdOTDnvpEDfJwki4MfgqAR8CaB1FzXFDzjseo= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/DeRuina/timberjack v1.3.9 h1:6UXZ1I7ExPGTX/1UNYawR58LlOJUHKBPiYC7WQ91eBo= github.com/DeRuina/timberjack v1.3.9/go.mod h1:RLoeQrwrCGIEF8gO5nV5b/gMD0QIy7bzQhBUgpp1EqE= @@ -74,8 +74,8 @@ github.com/godbus/dbus v4.1.0+incompatible/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZ github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/gofrs/uuid/v5 v5.3.2 h1:2jfO8j3XgSwlz/wHqemAEugfnTlikAYHhnqQ8Xh4fE0= -github.com/gofrs/uuid/v5 v5.3.2/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8= +github.com/gofrs/uuid/v5 v5.4.0 h1:EfbpCTjqMuGyq5ZJwxqzn3Cbr2d0rUZU7v5ycAk/e/0= +github.com/gofrs/uuid/v5 v5.4.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8= github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f h1:3BSP1Tbs2djlpprl7wCLuiqMaUh5SJkkzI2gDs+FgLs= github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f/go.mod h1:Pcatq5tYkCW2Q6yrR2VRHlbHpZ/R4/7qyL1TCF7vl14= github.com/golang/glog v0.0.0-20210429001901-424d2337a529/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= @@ -224,8 +224,8 @@ go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= -go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc= +go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -244,8 +244,8 @@ golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= -golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= +golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= From c6a5de64c8a097561e3d5c51161fc9f820233944 Mon Sep 17 00:00:00 2001 From: acabarbaye Date: Mon, 1 Dec 2025 17:33:31 +0000 Subject: [PATCH 2/3] =?UTF-8?q?:sparkles:=20Automatic=20changes=20->=20?= =?UTF-8?q?=F0=9F=91=B7=20Golang=20version=20updates=20[ci=20skip]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- utils/go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/go.mod b/utils/go.mod index 3030afe..5a09d27 100644 --- a/utils/go.mod +++ b/utils/go.mod @@ -7,7 +7,6 @@ require ( github.com/ARM-software/golang-utils/utils v1.136.0 github.com/go-faker/faker/v4 v4.7.0 github.com/go-logr/logr v1.4.3 - github.com/perimeterx/marshmallow v1.1.5 github.com/stretchr/testify v1.11.1 go.uber.org/atomic v1.11.0 go.uber.org/goleak v1.3.0 @@ -57,6 +56,7 @@ require ( github.com/mattn/go-isatty v0.0.20 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect + github.com/perimeterx/marshmallow v1.1.5 // indirect github.com/petermattis/goid v0.0.0-20250813065127-a731cc31b4fe // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect From 9ad55df2073e5ea48b5d500a0c85148cb1d79527 Mon Sep 17 00:00:00 2001 From: Adrien CABARBAYE Date: Mon, 1 Dec 2025 17:35:57 +0000 Subject: [PATCH 3/3] :green_heart: linting --- utils/api/api.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/api/api.go b/utils/api/api.go index d4061ed..28abeca 100644 --- a/utils/api/api.go +++ b/utils/api/api.go @@ -14,8 +14,8 @@ import ( "github.com/ARM-software/golang-utils/utils/http/api" ) -// IsCallSuccessful determines whether an API response is successful or not // Deprecated: Use github.com/ARM-software/golang-utils/utils/http/api instead +// IsCallSuccessful determines whether an API response is successful or not func IsCallSuccessful(r *http.Response) bool { return api.IsCallSuccessful(r) }