From 9fba8d7f23b9d6f0701a991c81ef3a6cfd4c3bb1 Mon Sep 17 00:00:00 2001 From: Denis Kirillov Date: Thu, 7 Jul 2022 15:59:38 +0300 Subject: [PATCH] [#15] Use extended error response Signed-off-by: Denis Kirillov --- cmd/neofs-rest-gw/integration_test.go | 2 +- gen/models/error_response.go | 131 ++++++++++++++++++ gen/models/error_type.go | 78 +++++++++++ gen/restapi/embedded_spec.go | 96 +++++++++---- gen/restapi/operations/auth_responses.go | 14 +- .../operations/delete_container_responses.go | 14 +- .../operations/delete_object_responses.go | 14 +- .../get_container_e_acl_responses.go | 14 +- .../operations/get_container_responses.go | 14 +- .../operations/get_object_info_responses.go | 14 +- .../operations/list_containers_responses.go | 14 +- .../put_container_e_acl_responses.go | 14 +- .../operations/put_container_responses.go | 14 +- .../operations/put_object_responses.go | 14 +- .../operations/search_objects_responses.go | 14 +- handlers/api.go | 7 + handlers/auth.go | 7 +- handlers/containers.go | 58 ++++---- handlers/objects.go | 78 ++++++----- internal/util/transformers.go | 25 ++++ internal/util/transformers_test.go | 27 ++++ spec/rest.yaml | 39 ++++-- 22 files changed, 528 insertions(+), 174 deletions(-) create mode 100644 gen/models/error_response.go create mode 100644 gen/models/error_type.go create mode 100644 internal/util/transformers_test.go diff --git a/cmd/neofs-rest-gw/integration_test.go b/cmd/neofs-rest-gw/integration_test.go index 05ef6b0..4cd04ee 100644 --- a/cmd/neofs-rest-gw/integration_test.go +++ b/cmd/neofs-rest-gw/integration_test.go @@ -60,7 +60,7 @@ const ( // tests configuration. useWalletConnect = false - useLocalEnvironment = false + useLocalEnvironment = true ) func TestIntegration(t *testing.T) { diff --git a/gen/models/error_response.go b/gen/models/error_response.go new file mode 100644 index 0000000..981f129 --- /dev/null +++ b/gen/models/error_response.go @@ -0,0 +1,131 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + + "github.com/go-openapi/errors" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" + "github.com/go-openapi/validate" +) + +// ErrorResponse error response +// +// swagger:model ErrorResponse +type ErrorResponse struct { + + // code + Code int64 `json:"code,omitempty"` + + // message + // Required: true + Message *string `json:"message"` + + // type + // Required: true + Type *ErrorType `json:"type"` +} + +// Validate validates this error response +func (m *ErrorResponse) Validate(formats strfmt.Registry) error { + var res []error + + if err := m.validateMessage(formats); err != nil { + res = append(res, err) + } + + if err := m.validateType(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *ErrorResponse) validateMessage(formats strfmt.Registry) error { + + if err := validate.Required("message", "body", m.Message); err != nil { + return err + } + + return nil +} + +func (m *ErrorResponse) validateType(formats strfmt.Registry) error { + + if err := validate.Required("type", "body", m.Type); err != nil { + return err + } + + if err := validate.Required("type", "body", m.Type); err != nil { + return err + } + + if m.Type != nil { + if err := m.Type.Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("type") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("type") + } + return err + } + } + + return nil +} + +// ContextValidate validate this error response based on the context it is used +func (m *ErrorResponse) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + var res []error + + if err := m.contextValidateType(ctx, formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *ErrorResponse) contextValidateType(ctx context.Context, formats strfmt.Registry) error { + + if m.Type != nil { + if err := m.Type.ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("type") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("type") + } + return err + } + } + + return nil +} + +// MarshalBinary interface implementation +func (m *ErrorResponse) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *ErrorResponse) UnmarshalBinary(b []byte) error { + var res ErrorResponse + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/gen/models/error_type.go b/gen/models/error_type.go new file mode 100644 index 0000000..b2ac34c --- /dev/null +++ b/gen/models/error_type.go @@ -0,0 +1,78 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "encoding/json" + + "github.com/go-openapi/errors" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/validate" +) + +// ErrorType error type +// +// swagger:model ErrorType +type ErrorType string + +func NewErrorType(value ErrorType) *ErrorType { + return &value +} + +// Pointer returns a pointer to a freshly-allocated ErrorType. +func (m ErrorType) Pointer() *ErrorType { + return &m +} + +const ( + + // ErrorTypeGW captures enum value "GW" + ErrorTypeGW ErrorType = "GW" + + // ErrorTypeAPI captures enum value "API" + ErrorTypeAPI ErrorType = "API" +) + +// for schema +var errorTypeEnum []interface{} + +func init() { + var res []ErrorType + if err := json.Unmarshal([]byte(`["GW","API"]`), &res); err != nil { + panic(err) + } + for _, v := range res { + errorTypeEnum = append(errorTypeEnum, v) + } +} + +func (m ErrorType) validateErrorTypeEnum(path, location string, value ErrorType) error { + if err := validate.EnumCase(path, location, value, errorTypeEnum, true); err != nil { + return err + } + return nil +} + +// Validate validates this error type +func (m ErrorType) Validate(formats strfmt.Registry) error { + var res []error + + // value enum + if err := m.validateErrorTypeEnum("", "body", m); err != nil { + return err + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +// ContextValidate validates this error type based on context it is used +func (m ErrorType) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} diff --git a/gen/restapi/embedded_spec.go b/gen/restapi/embedded_spec.go index 0ac4a4d..a194038 100644 --- a/gen/restapi/embedded_spec.go +++ b/gen/restapi/embedded_spec.go @@ -82,7 +82,7 @@ func init() { "400": { "description": "Bad request", "schema": { - "$ref": "#/definitions/Error" + "$ref": "#/definitions/ErrorResponse" } } } @@ -128,7 +128,7 @@ func init() { "400": { "description": "Bad request", "schema": { - "$ref": "#/definitions/Error" + "$ref": "#/definitions/ErrorResponse" } } } @@ -200,7 +200,7 @@ func init() { "400": { "description": "Bad request", "schema": { - "$ref": "#/definitions/Error" + "$ref": "#/definitions/ErrorResponse" } } } @@ -221,7 +221,7 @@ func init() { "400": { "description": "Bad request", "schema": { - "$ref": "#/definitions/Error" + "$ref": "#/definitions/ErrorResponse" } } } @@ -250,7 +250,7 @@ func init() { "400": { "description": "Bad request", "schema": { - "$ref": "#/definitions/Error" + "$ref": "#/definitions/ErrorResponse" } } } @@ -276,7 +276,7 @@ func init() { "400": { "description": "Bad request", "schema": { - "$ref": "#/definitions/Error" + "$ref": "#/definitions/ErrorResponse" } } } @@ -314,7 +314,7 @@ func init() { "400": { "description": "Bad request", "schema": { - "$ref": "#/definitions/Error" + "$ref": "#/definitions/ErrorResponse" } } } @@ -356,7 +356,7 @@ func init() { "400": { "description": "Bad request", "schema": { - "$ref": "#/definitions/Error" + "$ref": "#/definitions/ErrorResponse" } } } @@ -414,7 +414,7 @@ func init() { "400": { "description": "Bad request", "schema": { - "$ref": "#/definitions/Error" + "$ref": "#/definitions/ErrorResponse" } } } @@ -469,7 +469,7 @@ func init() { "400": { "description": "Bad request", "schema": { - "$ref": "#/definitions/Error" + "$ref": "#/definitions/ErrorResponse" } } } @@ -487,7 +487,7 @@ func init() { "400": { "description": "Bad request", "schema": { - "$ref": "#/definitions/Error" + "$ref": "#/definitions/ErrorResponse" } } } @@ -671,8 +671,30 @@ func init() { } } }, - "Error": { - "type": "string" + "ErrorResponse": { + "type": "object", + "required": [ + "type", + "message" + ], + "properties": { + "code": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "type": { + "$ref": "#/definitions/ErrorType" + } + } + }, + "ErrorType": { + "type": "string", + "enum": [ + "GW", + "API" + ] }, "Filter": { "type": "object", @@ -1154,7 +1176,7 @@ func init() { "400": { "description": "Bad request", "schema": { - "$ref": "#/definitions/Error" + "$ref": "#/definitions/ErrorResponse" } } } @@ -1201,7 +1223,7 @@ func init() { "400": { "description": "Bad request", "schema": { - "$ref": "#/definitions/Error" + "$ref": "#/definitions/ErrorResponse" } } } @@ -1285,7 +1307,7 @@ func init() { "400": { "description": "Bad request", "schema": { - "$ref": "#/definitions/Error" + "$ref": "#/definitions/ErrorResponse" } } } @@ -1306,7 +1328,7 @@ func init() { "400": { "description": "Bad request", "schema": { - "$ref": "#/definitions/Error" + "$ref": "#/definitions/ErrorResponse" } } } @@ -1347,7 +1369,7 @@ func init() { "400": { "description": "Bad request", "schema": { - "$ref": "#/definitions/Error" + "$ref": "#/definitions/ErrorResponse" } } } @@ -1377,7 +1399,7 @@ func init() { "400": { "description": "Bad request", "schema": { - "$ref": "#/definitions/Error" + "$ref": "#/definitions/ErrorResponse" } } } @@ -1427,7 +1449,7 @@ func init() { "400": { "description": "Bad request", "schema": { - "$ref": "#/definitions/Error" + "$ref": "#/definitions/ErrorResponse" } } } @@ -1473,7 +1495,7 @@ func init() { "400": { "description": "Bad request", "schema": { - "$ref": "#/definitions/Error" + "$ref": "#/definitions/ErrorResponse" } } } @@ -1544,7 +1566,7 @@ func init() { "400": { "description": "Bad request", "schema": { - "$ref": "#/definitions/Error" + "$ref": "#/definitions/ErrorResponse" } } } @@ -1617,7 +1639,7 @@ func init() { "400": { "description": "Bad request", "schema": { - "$ref": "#/definitions/Error" + "$ref": "#/definitions/ErrorResponse" } } } @@ -1635,7 +1657,7 @@ func init() { "400": { "description": "Bad request", "schema": { - "$ref": "#/definitions/Error" + "$ref": "#/definitions/ErrorResponse" } } } @@ -1839,8 +1861,30 @@ func init() { } } }, - "Error": { - "type": "string" + "ErrorResponse": { + "type": "object", + "required": [ + "type", + "message" + ], + "properties": { + "code": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "type": { + "$ref": "#/definitions/ErrorType" + } + } + }, + "ErrorType": { + "type": "string", + "enum": [ + "GW", + "API" + ] }, "Filter": { "type": "object", diff --git a/gen/restapi/operations/auth_responses.go b/gen/restapi/operations/auth_responses.go index f7f3406..d3d9d29 100644 --- a/gen/restapi/operations/auth_responses.go +++ b/gen/restapi/operations/auth_responses.go @@ -72,7 +72,7 @@ type AuthBadRequest struct { /* In: Body */ - Payload models.Error `json:"body,omitempty"` + Payload *models.ErrorResponse `json:"body,omitempty"` } // NewAuthBadRequest creates AuthBadRequest with default headers values @@ -82,13 +82,13 @@ func NewAuthBadRequest() *AuthBadRequest { } // WithPayload adds the payload to the auth bad request response -func (o *AuthBadRequest) WithPayload(payload models.Error) *AuthBadRequest { +func (o *AuthBadRequest) WithPayload(payload *models.ErrorResponse) *AuthBadRequest { o.Payload = payload return o } // SetPayload sets the payload to the auth bad request response -func (o *AuthBadRequest) SetPayload(payload models.Error) { +func (o *AuthBadRequest) SetPayload(payload *models.ErrorResponse) { o.Payload = payload } @@ -96,8 +96,10 @@ func (o *AuthBadRequest) SetPayload(payload models.Error) { func (o *AuthBadRequest) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { rw.WriteHeader(400) - payload := o.Payload - if err := producer.Produce(rw, payload); err != nil { - panic(err) // let the recovery middleware deal with this + if o.Payload != nil { + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } } } diff --git a/gen/restapi/operations/delete_container_responses.go b/gen/restapi/operations/delete_container_responses.go index 01cbd85..4b5c2de 100644 --- a/gen/restapi/operations/delete_container_responses.go +++ b/gen/restapi/operations/delete_container_responses.go @@ -69,7 +69,7 @@ type DeleteContainerBadRequest struct { /* In: Body */ - Payload models.Error `json:"body,omitempty"` + Payload *models.ErrorResponse `json:"body,omitempty"` } // NewDeleteContainerBadRequest creates DeleteContainerBadRequest with default headers values @@ -79,13 +79,13 @@ func NewDeleteContainerBadRequest() *DeleteContainerBadRequest { } // WithPayload adds the payload to the delete container bad request response -func (o *DeleteContainerBadRequest) WithPayload(payload models.Error) *DeleteContainerBadRequest { +func (o *DeleteContainerBadRequest) WithPayload(payload *models.ErrorResponse) *DeleteContainerBadRequest { o.Payload = payload return o } // SetPayload sets the payload to the delete container bad request response -func (o *DeleteContainerBadRequest) SetPayload(payload models.Error) { +func (o *DeleteContainerBadRequest) SetPayload(payload *models.ErrorResponse) { o.Payload = payload } @@ -93,8 +93,10 @@ func (o *DeleteContainerBadRequest) SetPayload(payload models.Error) { func (o *DeleteContainerBadRequest) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { rw.WriteHeader(400) - payload := o.Payload - if err := producer.Produce(rw, payload); err != nil { - panic(err) // let the recovery middleware deal with this + if o.Payload != nil { + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } } } diff --git a/gen/restapi/operations/delete_object_responses.go b/gen/restapi/operations/delete_object_responses.go index fc4c292..c21c10b 100644 --- a/gen/restapi/operations/delete_object_responses.go +++ b/gen/restapi/operations/delete_object_responses.go @@ -69,7 +69,7 @@ type DeleteObjectBadRequest struct { /* In: Body */ - Payload models.Error `json:"body,omitempty"` + Payload *models.ErrorResponse `json:"body,omitempty"` } // NewDeleteObjectBadRequest creates DeleteObjectBadRequest with default headers values @@ -79,13 +79,13 @@ func NewDeleteObjectBadRequest() *DeleteObjectBadRequest { } // WithPayload adds the payload to the delete object bad request response -func (o *DeleteObjectBadRequest) WithPayload(payload models.Error) *DeleteObjectBadRequest { +func (o *DeleteObjectBadRequest) WithPayload(payload *models.ErrorResponse) *DeleteObjectBadRequest { o.Payload = payload return o } // SetPayload sets the payload to the delete object bad request response -func (o *DeleteObjectBadRequest) SetPayload(payload models.Error) { +func (o *DeleteObjectBadRequest) SetPayload(payload *models.ErrorResponse) { o.Payload = payload } @@ -93,8 +93,10 @@ func (o *DeleteObjectBadRequest) SetPayload(payload models.Error) { func (o *DeleteObjectBadRequest) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { rw.WriteHeader(400) - payload := o.Payload - if err := producer.Produce(rw, payload); err != nil { - panic(err) // let the recovery middleware deal with this + if o.Payload != nil { + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } } } diff --git a/gen/restapi/operations/get_container_e_acl_responses.go b/gen/restapi/operations/get_container_e_acl_responses.go index 061e340..87eb5bd 100644 --- a/gen/restapi/operations/get_container_e_acl_responses.go +++ b/gen/restapi/operations/get_container_e_acl_responses.go @@ -69,7 +69,7 @@ type GetContainerEACLBadRequest struct { /* In: Body */ - Payload models.Error `json:"body,omitempty"` + Payload *models.ErrorResponse `json:"body,omitempty"` } // NewGetContainerEACLBadRequest creates GetContainerEACLBadRequest with default headers values @@ -79,13 +79,13 @@ func NewGetContainerEACLBadRequest() *GetContainerEACLBadRequest { } // WithPayload adds the payload to the get container e Acl bad request response -func (o *GetContainerEACLBadRequest) WithPayload(payload models.Error) *GetContainerEACLBadRequest { +func (o *GetContainerEACLBadRequest) WithPayload(payload *models.ErrorResponse) *GetContainerEACLBadRequest { o.Payload = payload return o } // SetPayload sets the payload to the get container e Acl bad request response -func (o *GetContainerEACLBadRequest) SetPayload(payload models.Error) { +func (o *GetContainerEACLBadRequest) SetPayload(payload *models.ErrorResponse) { o.Payload = payload } @@ -93,8 +93,10 @@ func (o *GetContainerEACLBadRequest) SetPayload(payload models.Error) { func (o *GetContainerEACLBadRequest) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { rw.WriteHeader(400) - payload := o.Payload - if err := producer.Produce(rw, payload); err != nil { - panic(err) // let the recovery middleware deal with this + if o.Payload != nil { + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } } } diff --git a/gen/restapi/operations/get_container_responses.go b/gen/restapi/operations/get_container_responses.go index 9287c94..307fe0d 100644 --- a/gen/restapi/operations/get_container_responses.go +++ b/gen/restapi/operations/get_container_responses.go @@ -69,7 +69,7 @@ type GetContainerBadRequest struct { /* In: Body */ - Payload models.Error `json:"body,omitempty"` + Payload *models.ErrorResponse `json:"body,omitempty"` } // NewGetContainerBadRequest creates GetContainerBadRequest with default headers values @@ -79,13 +79,13 @@ func NewGetContainerBadRequest() *GetContainerBadRequest { } // WithPayload adds the payload to the get container bad request response -func (o *GetContainerBadRequest) WithPayload(payload models.Error) *GetContainerBadRequest { +func (o *GetContainerBadRequest) WithPayload(payload *models.ErrorResponse) *GetContainerBadRequest { o.Payload = payload return o } // SetPayload sets the payload to the get container bad request response -func (o *GetContainerBadRequest) SetPayload(payload models.Error) { +func (o *GetContainerBadRequest) SetPayload(payload *models.ErrorResponse) { o.Payload = payload } @@ -93,8 +93,10 @@ func (o *GetContainerBadRequest) SetPayload(payload models.Error) { func (o *GetContainerBadRequest) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { rw.WriteHeader(400) - payload := o.Payload - if err := producer.Produce(rw, payload); err != nil { - panic(err) // let the recovery middleware deal with this + if o.Payload != nil { + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } } } diff --git a/gen/restapi/operations/get_object_info_responses.go b/gen/restapi/operations/get_object_info_responses.go index 0d18347..26b534e 100644 --- a/gen/restapi/operations/get_object_info_responses.go +++ b/gen/restapi/operations/get_object_info_responses.go @@ -69,7 +69,7 @@ type GetObjectInfoBadRequest struct { /* In: Body */ - Payload models.Error `json:"body,omitempty"` + Payload *models.ErrorResponse `json:"body,omitempty"` } // NewGetObjectInfoBadRequest creates GetObjectInfoBadRequest with default headers values @@ -79,13 +79,13 @@ func NewGetObjectInfoBadRequest() *GetObjectInfoBadRequest { } // WithPayload adds the payload to the get object info bad request response -func (o *GetObjectInfoBadRequest) WithPayload(payload models.Error) *GetObjectInfoBadRequest { +func (o *GetObjectInfoBadRequest) WithPayload(payload *models.ErrorResponse) *GetObjectInfoBadRequest { o.Payload = payload return o } // SetPayload sets the payload to the get object info bad request response -func (o *GetObjectInfoBadRequest) SetPayload(payload models.Error) { +func (o *GetObjectInfoBadRequest) SetPayload(payload *models.ErrorResponse) { o.Payload = payload } @@ -93,8 +93,10 @@ func (o *GetObjectInfoBadRequest) SetPayload(payload models.Error) { func (o *GetObjectInfoBadRequest) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { rw.WriteHeader(400) - payload := o.Payload - if err := producer.Produce(rw, payload); err != nil { - panic(err) // let the recovery middleware deal with this + if o.Payload != nil { + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } } } diff --git a/gen/restapi/operations/list_containers_responses.go b/gen/restapi/operations/list_containers_responses.go index 3ef0789..0bab3e7 100644 --- a/gen/restapi/operations/list_containers_responses.go +++ b/gen/restapi/operations/list_containers_responses.go @@ -69,7 +69,7 @@ type ListContainersBadRequest struct { /* In: Body */ - Payload models.Error `json:"body,omitempty"` + Payload *models.ErrorResponse `json:"body,omitempty"` } // NewListContainersBadRequest creates ListContainersBadRequest with default headers values @@ -79,13 +79,13 @@ func NewListContainersBadRequest() *ListContainersBadRequest { } // WithPayload adds the payload to the list containers bad request response -func (o *ListContainersBadRequest) WithPayload(payload models.Error) *ListContainersBadRequest { +func (o *ListContainersBadRequest) WithPayload(payload *models.ErrorResponse) *ListContainersBadRequest { o.Payload = payload return o } // SetPayload sets the payload to the list containers bad request response -func (o *ListContainersBadRequest) SetPayload(payload models.Error) { +func (o *ListContainersBadRequest) SetPayload(payload *models.ErrorResponse) { o.Payload = payload } @@ -93,8 +93,10 @@ func (o *ListContainersBadRequest) SetPayload(payload models.Error) { func (o *ListContainersBadRequest) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { rw.WriteHeader(400) - payload := o.Payload - if err := producer.Produce(rw, payload); err != nil { - panic(err) // let the recovery middleware deal with this + if o.Payload != nil { + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } } } diff --git a/gen/restapi/operations/put_container_e_acl_responses.go b/gen/restapi/operations/put_container_e_acl_responses.go index 137d618..396f891 100644 --- a/gen/restapi/operations/put_container_e_acl_responses.go +++ b/gen/restapi/operations/put_container_e_acl_responses.go @@ -69,7 +69,7 @@ type PutContainerEACLBadRequest struct { /* In: Body */ - Payload models.Error `json:"body,omitempty"` + Payload *models.ErrorResponse `json:"body,omitempty"` } // NewPutContainerEACLBadRequest creates PutContainerEACLBadRequest with default headers values @@ -79,13 +79,13 @@ func NewPutContainerEACLBadRequest() *PutContainerEACLBadRequest { } // WithPayload adds the payload to the put container e Acl bad request response -func (o *PutContainerEACLBadRequest) WithPayload(payload models.Error) *PutContainerEACLBadRequest { +func (o *PutContainerEACLBadRequest) WithPayload(payload *models.ErrorResponse) *PutContainerEACLBadRequest { o.Payload = payload return o } // SetPayload sets the payload to the put container e Acl bad request response -func (o *PutContainerEACLBadRequest) SetPayload(payload models.Error) { +func (o *PutContainerEACLBadRequest) SetPayload(payload *models.ErrorResponse) { o.Payload = payload } @@ -93,8 +93,10 @@ func (o *PutContainerEACLBadRequest) SetPayload(payload models.Error) { func (o *PutContainerEACLBadRequest) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { rw.WriteHeader(400) - payload := o.Payload - if err := producer.Produce(rw, payload); err != nil { - panic(err) // let the recovery middleware deal with this + if o.Payload != nil { + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } } } diff --git a/gen/restapi/operations/put_container_responses.go b/gen/restapi/operations/put_container_responses.go index 5830ad0..e244dc6 100644 --- a/gen/restapi/operations/put_container_responses.go +++ b/gen/restapi/operations/put_container_responses.go @@ -69,7 +69,7 @@ type PutContainerBadRequest struct { /* In: Body */ - Payload models.Error `json:"body,omitempty"` + Payload *models.ErrorResponse `json:"body,omitempty"` } // NewPutContainerBadRequest creates PutContainerBadRequest with default headers values @@ -79,13 +79,13 @@ func NewPutContainerBadRequest() *PutContainerBadRequest { } // WithPayload adds the payload to the put container bad request response -func (o *PutContainerBadRequest) WithPayload(payload models.Error) *PutContainerBadRequest { +func (o *PutContainerBadRequest) WithPayload(payload *models.ErrorResponse) *PutContainerBadRequest { o.Payload = payload return o } // SetPayload sets the payload to the put container bad request response -func (o *PutContainerBadRequest) SetPayload(payload models.Error) { +func (o *PutContainerBadRequest) SetPayload(payload *models.ErrorResponse) { o.Payload = payload } @@ -93,8 +93,10 @@ func (o *PutContainerBadRequest) SetPayload(payload models.Error) { func (o *PutContainerBadRequest) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { rw.WriteHeader(400) - payload := o.Payload - if err := producer.Produce(rw, payload); err != nil { - panic(err) // let the recovery middleware deal with this + if o.Payload != nil { + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } } } diff --git a/gen/restapi/operations/put_object_responses.go b/gen/restapi/operations/put_object_responses.go index 4bd2745..e584b3b 100644 --- a/gen/restapi/operations/put_object_responses.go +++ b/gen/restapi/operations/put_object_responses.go @@ -69,7 +69,7 @@ type PutObjectBadRequest struct { /* In: Body */ - Payload models.Error `json:"body,omitempty"` + Payload *models.ErrorResponse `json:"body,omitempty"` } // NewPutObjectBadRequest creates PutObjectBadRequest with default headers values @@ -79,13 +79,13 @@ func NewPutObjectBadRequest() *PutObjectBadRequest { } // WithPayload adds the payload to the put object bad request response -func (o *PutObjectBadRequest) WithPayload(payload models.Error) *PutObjectBadRequest { +func (o *PutObjectBadRequest) WithPayload(payload *models.ErrorResponse) *PutObjectBadRequest { o.Payload = payload return o } // SetPayload sets the payload to the put object bad request response -func (o *PutObjectBadRequest) SetPayload(payload models.Error) { +func (o *PutObjectBadRequest) SetPayload(payload *models.ErrorResponse) { o.Payload = payload } @@ -93,8 +93,10 @@ func (o *PutObjectBadRequest) SetPayload(payload models.Error) { func (o *PutObjectBadRequest) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { rw.WriteHeader(400) - payload := o.Payload - if err := producer.Produce(rw, payload); err != nil { - panic(err) // let the recovery middleware deal with this + if o.Payload != nil { + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } } } diff --git a/gen/restapi/operations/search_objects_responses.go b/gen/restapi/operations/search_objects_responses.go index afbe4a7..e1e6bda 100644 --- a/gen/restapi/operations/search_objects_responses.go +++ b/gen/restapi/operations/search_objects_responses.go @@ -69,7 +69,7 @@ type SearchObjectsBadRequest struct { /* In: Body */ - Payload models.Error `json:"body,omitempty"` + Payload *models.ErrorResponse `json:"body,omitempty"` } // NewSearchObjectsBadRequest creates SearchObjectsBadRequest with default headers values @@ -79,13 +79,13 @@ func NewSearchObjectsBadRequest() *SearchObjectsBadRequest { } // WithPayload adds the payload to the search objects bad request response -func (o *SearchObjectsBadRequest) WithPayload(payload models.Error) *SearchObjectsBadRequest { +func (o *SearchObjectsBadRequest) WithPayload(payload *models.ErrorResponse) *SearchObjectsBadRequest { o.Payload = payload return o } // SetPayload sets the payload to the search objects bad request response -func (o *SearchObjectsBadRequest) SetPayload(payload models.Error) { +func (o *SearchObjectsBadRequest) SetPayload(payload *models.ErrorResponse) { o.Payload = payload } @@ -93,8 +93,10 @@ func (o *SearchObjectsBadRequest) SetPayload(payload models.Error) { func (o *SearchObjectsBadRequest) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { rw.WriteHeader(400) - payload := o.Payload - if err := producer.Produce(rw, payload); err != nil { - panic(err) // let the recovery middleware deal with this + if o.Payload != nil { + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } } } diff --git a/handlers/api.go b/handlers/api.go index e102d5f..8d8e372 100644 --- a/handlers/api.go +++ b/handlers/api.go @@ -11,6 +11,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neofs-rest-gw/gen/models" "github.com/nspcc-dev/neofs-rest-gw/gen/restapi/operations" + "github.com/nspcc-dev/neofs-rest-gw/internal/util" "github.com/nspcc-dev/neofs-sdk-go/pool" "go.uber.org/zap" ) @@ -113,3 +114,9 @@ func (a *API) setupGlobalMiddleware(handler http.Handler) http.Handler { handler.ServeHTTP(w, r.WithContext(ctx)) }) } + +func (a *API) logAndGetErrorResponse(msg string, err error, fields ...zap.Field) *models.ErrorResponse { + fields = append(fields, zap.Error(err)) + a.log.Error(msg, fields...) + return util.NewErrorResponse(fmt.Errorf("%s: %w", msg, err)) +} diff --git a/handlers/auth.go b/handlers/auth.go index cf29057..09cbc32 100644 --- a/handlers/auth.go +++ b/handlers/auth.go @@ -71,13 +71,14 @@ func (a *API) PostAuth(params operations.AuthParams) middleware.Responder { response := make([]*models.TokenResponse, len(params.Tokens)) for i, token := range params.Tokens { if _, ok := tokenNames[token.Name]; ok { - return operations.NewAuthBadRequest().WithPayload(models.Error(fmt.Sprintf("duplicated token name '%s'", token.Name))) + err := fmt.Errorf("duplicated token name '%s'", token.Name) + return operations.NewAuthBadRequest().WithPayload(util.NewErrorResponse(err)) } tokenNames[token.Name] = struct{}{} isObject, err := IsObjectToken(token) if err != nil { - return operations.NewAuthBadRequest().WithPayload(models.Error(err.Error())) + return operations.NewAuthBadRequest().WithPayload(util.NewErrorResponse(err)) } if isObject { @@ -88,7 +89,7 @@ func (a *API) PostAuth(params operations.AuthParams) middleware.Responder { response[i], err = prepareContainerTokens(ctx, prm, a.pool, a.key.PublicKey()) } if err != nil { - return operations.NewAuthBadRequest().WithPayload(models.Error(err.Error())) + return operations.NewAuthBadRequest().WithPayload(util.NewErrorResponse(err)) } } diff --git a/handlers/containers.go b/handlers/containers.go index 3e87e08..47d3f75 100644 --- a/handlers/containers.go +++ b/handlers/containers.go @@ -43,14 +43,16 @@ func (a *API) PutContainers(params operations.PutContainerParams, principal *mod } stoken, err := prepareSessionToken(bt, *params.WalletConnect) if err != nil { - return wrapError(err) + resp := a.logAndGetErrorResponse("invalid session token", err) + return operations.NewPutContainerBadRequest().WithPayload(resp) } userAttributes := prepareUserAttributes(params.HTTPRequest.Header) cnrID, err := createContainer(params.HTTPRequest.Context(), a.pool, stoken, ¶ms, userAttributes) if err != nil { - return wrapError(err) + resp := a.logAndGetErrorResponse("create container", err) + return operations.NewPutContainerBadRequest().WithPayload(resp) } var resp operations.PutContainerOKBody @@ -63,7 +65,8 @@ func (a *API) PutContainers(params operations.PutContainerParams, principal *mod func (a *API) GetContainer(params operations.GetContainerParams) middleware.Responder { cnr, err := getContainer(params.HTTPRequest.Context(), a.pool, params.ContainerID) if err != nil { - return wrapError(err) + resp := a.logAndGetErrorResponse("get container", err) + return operations.NewPutContainerBadRequest().WithPayload(resp) } attrs := make([]*models.Attribute, len(cnr.Attributes())) @@ -90,8 +93,8 @@ func (a *API) GetContainer(params operations.GetContainerParams) middleware.Resp func (a *API) PutContainerEACL(params operations.PutContainerEACLParams, principal *models.Principal) middleware.Responder { cnrID, err := parseContainerID(params.ContainerID) if err != nil { - a.log.Error("invalid container id", zap.Error(err)) - return operations.NewPutContainerEACLBadRequest().WithPayload("invalid container id") + resp := a.logAndGetErrorResponse("invalid container id", err) + return operations.NewPutContainerEACLBadRequest().WithPayload(resp) } bt := &BearerToken{ @@ -101,12 +104,13 @@ func (a *API) PutContainerEACL(params operations.PutContainerEACLParams, princip } stoken, err := prepareSessionToken(bt, *params.WalletConnect) if err != nil { - return wrapError(err) + resp := a.logAndGetErrorResponse("invalid session token", err) + return operations.NewPutContainerEACLBadRequest().WithPayload(resp) } if err = setContainerEACL(params.HTTPRequest.Context(), a.pool, cnrID, stoken, params.Eacl); err != nil { - a.log.Error("failed set container eacl", zap.Error(err)) - return operations.NewPutContainerEACLBadRequest().WithPayload(util.NewError(err)) + resp := a.logAndGetErrorResponse("failed set container eacl", err) + return operations.NewPutContainerEACLBadRequest().WithPayload(resp) } return operations.NewPutContainerEACLOK().WithPayload(util.NewSuccessResponse()) @@ -116,14 +120,14 @@ func (a *API) PutContainerEACL(params operations.PutContainerEACLParams, princip func (a *API) GetContainerEACL(params operations.GetContainerEACLParams) middleware.Responder { cnrID, err := parseContainerID(params.ContainerID) if err != nil { - a.log.Error("invalid container id", zap.Error(err)) - return operations.NewGetContainerEACLBadRequest().WithPayload("invalid container id") + resp := a.logAndGetErrorResponse("invalid container id", err) + return operations.NewGetContainerEACLBadRequest().WithPayload(resp) } resp, err := getContainerEACL(params.HTTPRequest.Context(), a.pool, cnrID) if err != nil { - a.log.Error("failed to get container eacl", zap.Error(err)) - return operations.NewGetContainerEACLBadRequest().WithPayload("failed to get container eacl") + errResp := a.logAndGetErrorResponse("failed to get container eacl", err) + return operations.NewGetContainerEACLBadRequest().WithPayload(errResp) } return operations.NewGetContainerEACLOK().WithPayload(resp) @@ -135,8 +139,8 @@ func (a *API) ListContainer(params operations.ListContainersParams) middleware.R var ownerID owner.ID if err := ownerID.Parse(params.OwnerID); err != nil { - a.log.Error("invalid owner id", zap.Error(err)) - return operations.NewListContainersBadRequest().WithPayload("invalid owner id") + resp := a.logAndGetErrorResponse("invalid owner id", err) + return operations.NewListContainersBadRequest().WithPayload(resp) } var prm pool.PrmContainerList @@ -144,8 +148,8 @@ func (a *API) ListContainer(params operations.ListContainersParams) middleware.R ids, err := a.pool.ListContainers(ctx, prm) if err != nil { - a.log.Error("list containers", zap.Error(err)) - return operations.NewListContainersBadRequest().WithPayload("failed to get containers") + resp := a.logAndGetErrorResponse("list containers", err) + return operations.NewListContainersBadRequest().WithPayload(resp) } offset := int(*params.Offset) @@ -171,8 +175,8 @@ func (a *API) ListContainer(params operations.ListContainersParams) middleware.R for _, id := range ids[offset : offset+size] { baseInfo, err := getContainerBaseInfo(ctx, a.pool, id) if err != nil { - a.log.Error("get container", zap.String("cid", id.String()), zap.Error(err)) - return operations.NewListContainersBadRequest().WithPayload("failed to get container") + resp := a.logAndGetErrorResponse("get container", err, zap.String("cid", id.String())) + return operations.NewListContainersBadRequest().WithPayload(resp) } res.Containers = append(res.Containers, baseInfo) } @@ -189,14 +193,14 @@ func (a *API) DeleteContainer(params operations.DeleteContainerParams, principal } stoken, err := prepareSessionToken(bt, *params.WalletConnect) if err != nil { - a.log.Error("failed parse session token", zap.Error(err)) - return operations.NewDeleteContainerBadRequest().WithPayload(util.NewError(err)) + resp := a.logAndGetErrorResponse("invalid session token", err) + return operations.NewDeleteContainerBadRequest().WithPayload(resp) } cnrID, err := parseContainerID(params.ContainerID) if err != nil { - a.log.Error("failed get container id", zap.Error(err)) - return operations.NewDeleteContainerBadRequest().WithPayload(util.NewError(err)) + resp := a.logAndGetErrorResponse("invalid container id", err) + return operations.NewDeleteContainerBadRequest().WithPayload(resp) } var prm pool.PrmContainerDelete @@ -204,8 +208,8 @@ func (a *API) DeleteContainer(params operations.DeleteContainerParams, principal prm.SetSessionToken(*stoken) if err = a.pool.DeleteContainer(params.HTTPRequest.Context(), prm); err != nil { - a.log.Error("failed delete container", zap.String("container", params.ContainerID), zap.Error(err)) - return operations.NewDeleteContainerBadRequest().WithPayload(util.NewError(err)) + resp := a.logAndGetErrorResponse("delete container", err, zap.String("container", params.ContainerID)) + return operations.NewDeleteContainerBadRequest().WithPayload(resp) } return operations.NewDeleteContainerOK().WithPayload(util.NewSuccessResponse()) @@ -280,7 +284,7 @@ func getContainerEACL(ctx context.Context, p *pool.Pool, cnrID *cid.ID) (*models table, err := p.GetEACL(ctx, prm) if err != nil { - return nil, err + return nil, fmt.Errorf("get eacl: %w", err) } tableResp := &models.Eacl{ @@ -397,7 +401,3 @@ func prepareSessionToken(bt *BearerToken, isWalletConnect bool) (*session.Token, return stoken, err } - -func wrapError(err error) middleware.Responder { - return operations.NewPutContainerBadRequest().WithPayload(models.Error(err.Error())) -} diff --git a/handlers/objects.go b/handlers/objects.go index ec54232..49a7296 100644 --- a/handlers/objects.go +++ b/handlers/objects.go @@ -5,6 +5,7 @@ import ( "crypto/ecdsa" "encoding/base64" "encoding/hex" + "errors" "fmt" "io" "strings" @@ -33,18 +34,20 @@ func (a *API) PutObjects(params operations.PutObjectParams, principal *models.Pr btoken, err := getBearerToken(principal, params.XBearerSignature, params.XBearerSignatureKey, *params.WalletConnect) if err != nil { - return errorResponse.WithPayload(models.Error(err.Error())) + resp := a.logAndGetErrorResponse("invalid bearer token", err) + return errorResponse.WithPayload(resp) } var cnrID cid.ID if err = cnrID.Parse(*params.Object.ContainerID); err != nil { - a.log.Error("invalid container id", zap.Error(err)) - return errorResponse.WithPayload("invalid container id") + resp := a.logAndGetErrorResponse("invalid container id", err) + return errorResponse.WithPayload(resp) } payload, err := base64.StdEncoding.DecodeString(params.Object.Payload) if err != nil { - return errorResponse.WithPayload(models.Error(err.Error())) + resp := a.logAndGetErrorResponse("invalid object payload", err) + return errorResponse.WithPayload(resp) } prm := PrmAttributes{ @@ -53,7 +56,8 @@ func (a *API) PutObjects(params operations.PutObjectParams, principal *models.Pr } attributes, err := GetObjectAttributes(ctx, a.pool, params.Object.Attributes, prm) if err != nil { - return errorResponse.WithPayload(models.Error(err.Error())) + resp := a.logAndGetErrorResponse("failed to get object attributes", err) + return errorResponse.WithPayload(resp) } obj := object.New() @@ -68,8 +72,8 @@ func (a *API) PutObjects(params operations.PutObjectParams, principal *models.Pr objID, err := a.pool.PutObject(ctx, prmPut) if err != nil { - a.log.Error("put object", zap.Error(err)) - return errorResponse.WithPayload(util.NewError(err)) + resp := a.logAndGetErrorResponse("put object", err) + return errorResponse.WithPayload(resp) } var resp models.Address @@ -86,14 +90,14 @@ func (a *API) GetObjectInfo(params operations.GetObjectInfoParams, principal *mo addr, err := parseAddress(params.ContainerID, params.ObjectID) if err != nil { - a.log.Error("invalid address", zap.Error(err)) - return errorResponse.WithPayload("invalid address") + resp := a.logAndGetErrorResponse("invalid address", err) + return errorResponse.WithPayload(resp) } btoken, err := getBearerToken(principal, params.XBearerSignature, params.XBearerSignatureKey, *params.WalletConnect) if err != nil { - a.log.Error("get bearer token", zap.Error(err)) - return errorResponse.WithPayload(util.NewError(err)) + resp := a.logAndGetErrorResponse("get bearer token", err) + return errorResponse.WithPayload(resp) } var prm pool.PrmObjectHead @@ -102,8 +106,8 @@ func (a *API) GetObjectInfo(params operations.GetObjectInfoParams, principal *mo objInfo, err := a.pool.HeadObject(ctx, prm) if err != nil { - a.log.Error("head object", zap.Error(err)) - return errorResponse.WithPayload(util.NewError(err)) + resp := a.logAndGetErrorResponse("head object", err) + return errorResponse.WithPayload(resp) } var resp models.ObjectInfo @@ -128,8 +132,8 @@ func (a *API) GetObjectInfo(params operations.GetObjectInfoParams, principal *mo var offset, length uint64 if params.RangeOffset != nil || params.RangeLength != nil { if params.RangeOffset == nil || params.RangeLength == nil { - a.log.Error("both offset and length must be provided") - return errorResponse.WithPayload(util.NewError(fmt.Errorf("both offset and length must be provided"))) + errResp := a.logAndGetErrorResponse("invalid range param", errors.New("both offset and length musded")) + return errorResponse.WithPayload(errResp) } offset = uint64(*params.RangeOffset) length = uint64(*params.RangeLength) @@ -145,8 +149,8 @@ func (a *API) GetObjectInfo(params operations.GetObjectInfoParams, principal *mo rangeRes, err := a.pool.ObjectRange(ctx, prmRange) if err != nil { - a.log.Error("range object", zap.Error(err)) - return errorResponse.WithPayload(util.NewError(err)) + errResp := a.logAndGetErrorResponse("range object", err) + return errorResponse.WithPayload(errResp) } defer func() { @@ -159,12 +163,12 @@ func (a *API) GetObjectInfo(params operations.GetObjectInfoParams, principal *mo encoder := base64.NewEncoder(base64.StdEncoding, sb) payloadSize, err := io.Copy(encoder, rangeRes) if err != nil { - a.log.Error("encode object payload", zap.Error(err)) - return errorResponse.WithPayload(util.NewError(err)) + errResp := a.logAndGetErrorResponse("encode object payload", err) + return errorResponse.WithPayload(errResp) } if err = encoder.Close(); err != nil { - a.log.Error("close encoder", zap.Error(err)) - return errorResponse.WithPayload(util.NewError(err)) + errResp := a.logAndGetErrorResponse("close encoder", err) + return errorResponse.WithPayload(errResp) } resp.Payload = sb.String() @@ -180,14 +184,14 @@ func (a *API) DeleteObject(params operations.DeleteObjectParams, principal *mode addr, err := parseAddress(params.ContainerID, params.ObjectID) if err != nil { - a.log.Error("invalid address", zap.Error(err)) - return errorResponse.WithPayload("invalid address") + resp := a.logAndGetErrorResponse("invalid address", err) + return errorResponse.WithPayload(resp) } btoken, err := getBearerToken(principal, params.XBearerSignature, params.XBearerSignatureKey, *params.WalletConnect) if err != nil { - a.log.Error("failed to get bearer token", zap.Error(err)) - return errorResponse.WithPayload(util.NewError(err)) + resp := a.logAndGetErrorResponse("failed to get bearer token", err) + return errorResponse.WithPayload(resp) } var prm pool.PrmObjectDelete @@ -195,8 +199,8 @@ func (a *API) DeleteObject(params operations.DeleteObjectParams, principal *mode prm.UseBearer(btoken) if err = a.pool.DeleteObject(ctx, prm); err != nil { - a.log.Error("failed to delete object", zap.Error(err)) - return errorResponse.WithPayload(util.NewError(err)) + resp := a.logAndGetErrorResponse("failed to delete object", err) + return errorResponse.WithPayload(resp) } return operations.NewDeleteObjectOK().WithPayload(util.NewSuccessResponse()) @@ -209,20 +213,20 @@ func (a *API) SearchObjects(params operations.SearchObjectsParams, principal *mo var cnrID cid.ID if err := cnrID.Parse(params.ContainerID); err != nil { - a.log.Error("invalid container id", zap.Error(err)) - return errorResponse.WithPayload("invalid container id") + resp := a.logAndGetErrorResponse("invalid container id", err) + return errorResponse.WithPayload(resp) } btoken, err := getBearerToken(principal, params.XBearerSignature, params.XBearerSignatureKey, *params.WalletConnect) if err != nil { - a.log.Error("failed to get bearer token", zap.Error(err)) - return errorResponse.WithPayload(util.NewError(err)) + resp := a.logAndGetErrorResponse("failed to get bearer token", err) + return errorResponse.WithPayload(resp) } filters, err := util.ToNativeFilters(params.SearchFilters) if err != nil { - a.log.Error("failed to transform to native", zap.Error(err)) - return errorResponse.WithPayload(util.NewError(err)) + resp := a.logAndGetErrorResponse("failed to transform to native", err) + return errorResponse.WithPayload(resp) } var prm pool.PrmObjectSearch @@ -232,8 +236,8 @@ func (a *API) SearchObjects(params operations.SearchObjectsParams, principal *mo resSearch, err := a.pool.SearchObjects(ctx, prm) if err != nil { - a.log.Error("failed to search objects", zap.Error(err)) - return errorResponse.WithPayload(util.NewError(err)) + resp := a.logAndGetErrorResponse("failed to search objects", err) + return errorResponse.WithPayload(resp) } offset := int(*params.Offset) @@ -262,8 +266,8 @@ func (a *API) SearchObjects(params operations.SearchObjectsParams, principal *mo err = iterateErr } if err != nil { - a.log.Error("failed to search objects", zap.Error(err)) - return errorResponse.WithPayload(util.NewError(err)) + resp := a.logAndGetErrorResponse("failed to search objects", err) + return errorResponse.WithPayload(resp) } list := &models.ObjectList{ diff --git a/internal/util/transformers.go b/internal/util/transformers.go index 600c6f3..d2605b5 100644 --- a/internal/util/transformers.go +++ b/internal/util/transformers.go @@ -2,10 +2,12 @@ package util import ( "encoding/hex" + "errors" "fmt" sessionv2 "github.com/nspcc-dev/neofs-api-go/v2/session" "github.com/nspcc-dev/neofs-rest-gw/gen/models" + apistatus "github.com/nspcc-dev/neofs-sdk-go/client/status" cid "github.com/nspcc-dev/neofs-sdk-go/container/id" "github.com/nspcc-dev/neofs-sdk-go/eacl" "github.com/nspcc-dev/neofs-sdk-go/object" @@ -456,3 +458,26 @@ func NewSuccessResponse() *models.SuccessResponse { Success: NewBool(true), } } + +// NewErrorResponse forms model.ErrorResponse. +func NewErrorResponse(err error) *models.ErrorResponse { + var code int64 + t := models.ErrorTypeGW + if status, ok := unwrapErr(err).(apistatus.StatusV2); ok { + code = int64(status.ToStatusV2().Code()) + t = models.ErrorTypeAPI + } + + return &models.ErrorResponse{ + Code: code, + Message: NewString(err.Error()), + Type: models.NewErrorType(t), + } +} + +func unwrapErr(err error) error { + for e := errors.Unwrap(err); e != nil; e = errors.Unwrap(err) { + err = e + } + return err +} diff --git a/internal/util/transformers_test.go b/internal/util/transformers_test.go new file mode 100644 index 0000000..1be25da --- /dev/null +++ b/internal/util/transformers_test.go @@ -0,0 +1,27 @@ +package util + +import ( + "encoding/json" + "errors" + "fmt" + "testing" + + apistatus "github.com/nspcc-dev/neofs-sdk-go/client/status" + "github.com/stretchr/testify/require" +) + +func TestErrors(t *testing.T) { + apiErr := fmt.Errorf("some context: %w", apistatus.ContainerNotFound{}) + + resp := NewErrorResponse(apiErr) + data, err := json.Marshal(resp) + require.NoError(t, err) + require.Equal(t, `{"code":3072,"message":"some context: status: code = 3072","type":"API"}`, string(data)) + + gwErr := fmt.Errorf("some context: %w", errors.New("sanity check error")) + + resp = NewErrorResponse(gwErr) + data, err = json.Marshal(resp) + require.NoError(t, err) + require.Equal(t, `{"message":"some context: sanity check error","type":"GW"}`, string(data)) +} diff --git a/spec/rest.yaml b/spec/rest.yaml index cfa011e..87e7812 100644 --- a/spec/rest.yaml +++ b/spec/rest.yaml @@ -91,7 +91,7 @@ paths: 400: description: Bad request schema: - $ref: '#/definitions/Error' + $ref: '#/definitions/ErrorResponse' /objects: parameters: @@ -120,7 +120,7 @@ paths: 400: description: Bad request schema: - $ref: '#/definitions/Error' + $ref: '#/definitions/ErrorResponse' /objects/{containerId}/search: parameters: @@ -159,7 +159,7 @@ paths: 400: description: Bad request schema: - $ref: '#/definitions/Error' + $ref: '#/definitions/ErrorResponse' /objects/{containerId}/{objectId}: parameters: @@ -197,7 +197,7 @@ paths: 400: description: Bad request schema: - $ref: '#/definitions/Error' + $ref: '#/definitions/ErrorResponse' delete: operationId: deleteObject summary: Remove object from NeoFS @@ -209,7 +209,7 @@ paths: 400: description: Bad request schema: - $ref: '#/definitions/Error' + $ref: '#/definitions/ErrorResponse' /containers: put: @@ -256,7 +256,7 @@ paths: 400: description: Bad request schema: - $ref: '#/definitions/Error' + $ref: '#/definitions/ErrorResponse' get: operationId: listContainers summary: Get list of containers @@ -288,7 +288,7 @@ paths: 400: description: Bad request schema: - $ref: '#/definitions/Error' + $ref: '#/definitions/ErrorResponse' /containers/{containerId}: parameters: @@ -305,7 +305,7 @@ paths: 400: description: Bad request schema: - $ref: '#/definitions/Error' + $ref: '#/definitions/ErrorResponse' delete: operationId: deleteContainer summary: Delete container by id @@ -321,7 +321,7 @@ paths: 400: description: Bad request schema: - $ref: '#/definitions/Error' + $ref: '#/definitions/ErrorResponse' /containers/{containerId}/eacl: parameters: - $ref: '#/parameters/containerId' @@ -346,7 +346,7 @@ paths: 400: description: Bad request schema: - $ref: '#/definitions/Error' + $ref: '#/definitions/ErrorResponse' get: operationId: getContainerEACL summary: Get container EACL by id @@ -359,7 +359,7 @@ paths: 400: description: Bad request schema: - $ref: '#/definitions/Error' + $ref: '#/definitions/ErrorResponse' definitions: Bearer: @@ -710,8 +710,23 @@ definitions: - value Principal: type: string - Error: + ErrorType: type: string + enum: + - GW + - API + ErrorResponse: + type: object + properties: + type: + $ref: '#/definitions/ErrorType' + code: + type: integer + message: + type: string + required: + - type + - message SuccessResponse: type: object properties: