From 656fc7478b7080b42d2e4bbb99f001c50c7ca9da Mon Sep 17 00:00:00 2001 From: Eddy Ilangovan Date: Sun, 23 Oct 2022 19:24:52 -0700 Subject: [PATCH 01/14] Add AWSQueryCompatible support that returns a AWSQuery compatible error code if the query error code header is present --- .../models/awsquerycompatible/api-2.json | 72 +++++ .../service/awsquerycompatible/api.go | 290 ++++++++++++++++++ .../awsquerycompatible_test.go | 103 +++++++ .../awsquerycompatibleiface/interface.go | 68 ++++ .../service/awsquerycompatible/doc.go | 26 ++ .../service/awsquerycompatible/errors.go | 23 ++ .../service/awsquerycompatible/service.go | 107 +++++++ private/protocol/jsonrpc/unmarshal_error.go | 18 ++ 8 files changed, 707 insertions(+) create mode 100644 private/model/api/codegentest/models/awsquerycompatible/api-2.json create mode 100644 private/model/api/codegentest/service/awsquerycompatible/api.go create mode 100644 private/model/api/codegentest/service/awsquerycompatible/awsquerycompatible_test.go create mode 100644 private/model/api/codegentest/service/awsquerycompatible/awsquerycompatibleiface/interface.go create mode 100644 private/model/api/codegentest/service/awsquerycompatible/doc.go create mode 100644 private/model/api/codegentest/service/awsquerycompatible/errors.go create mode 100644 private/model/api/codegentest/service/awsquerycompatible/service.go diff --git a/private/model/api/codegentest/models/awsquerycompatible/api-2.json b/private/model/api/codegentest/models/awsquerycompatible/api-2.json new file mode 100644 index 0000000000..83b0139f7b --- /dev/null +++ b/private/model/api/codegentest/models/awsquerycompatible/api-2.json @@ -0,0 +1,72 @@ + + { + "version":"2.0", + "metadata":{ + "apiVersion":"2012-11-05", + "endpointPrefix":"awsquerycompatible", + "jsonVersion":"1.1", + "protocol":"json", + "serviceAbbreviation":"AwsQueryCompatible", + "serviceFullName":"AWSQuery Compatible Service", + "serviceId":"AWSQueryCompatible", + "signatureVersion":"v4", + "uid":"awsquerycompatible-2012-11-05", + "xmlNamespace":"http://queue.amazonaws.com/doc/2012-11-05/" + }, + "operations":{ + "CreateQueue":{ + "name":"CreateQueue", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"CreateQueueRequest"}, + "output":{ + "shape":"CreateQueueResult", + "resultWrapper":"CreateQueueResult" + }, + "errors":[ + {"shape":"QueueDeletedRecently"}, + {"shape":"QueueNameExists"} + ] + } + }, + "shapes":{ + "CreateQueueRequest":{ + "type":"structure", + "required":["QueueName"], + "members":{ + "QueueName":{"shape":"String"} + } + }, + "CreateQueueResult":{ + "type":"structure", + "members":{ + "QueueUrl":{"shape":"String"} + } + }, + "QueueDeletedRecently":{ + "type":"structure", + "members":{ + }, + "error":{ + "code":"AWS.SimpleQueueService.QueueDeletedRecently", + "httpStatusCode":400, + "senderFault":true + }, + "exception":true + }, + "QueueNameExists":{ + "type":"structure", + "members":{ + }, + "error":{ + "code":"QueueAlreadyExists", + "httpStatusCode":400, + "senderFault":true + }, + "exception":true + }, + "String":{"type":"string"} + } +} diff --git a/private/model/api/codegentest/service/awsquerycompatible/api.go b/private/model/api/codegentest/service/awsquerycompatible/api.go new file mode 100644 index 0000000000..b1f8e1730b --- /dev/null +++ b/private/model/api/codegentest/service/awsquerycompatible/api.go @@ -0,0 +1,290 @@ +// Code generated by private/model/cli/gen-api/main.go. DO NOT EDIT. + +package awsquerycompatible + +import ( + "fmt" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/awsutil" + "github.com/aws/aws-sdk-go/aws/request" + "github.com/aws/aws-sdk-go/private/protocol" +) + +const opCreateQueue = "CreateQueue" + +// CreateQueueRequest generates a "aws/request.Request" representing the +// client's request for the CreateQueue operation. The "output" return +// value will be populated with the request's response once the request completes +// successfully. +// +// Use "Send" method on the returned Request to send the API call to the service. +// the "output" return value is not valid until after Send returns without error. +// +// See CreateQueue for more information on using the CreateQueue +// API call, and error handling. +// +// This method is useful when you want to inject custom logic or configuration +// into the SDK's request lifecycle. Such as custom headers, or retry logic. +// +// +// // Example sending a request using the CreateQueueRequest method. +// req, resp := client.CreateQueueRequest(params) +// +// err := req.Send() +// if err == nil { // resp is now filled +// fmt.Println(resp) +// } +// +// See also, https://docs.aws.amazon.com/goto/WebAPI/awsquerycompatible-2012-11-05/CreateQueue +func (c *AwsQueryCompatible) CreateQueueRequest(input *CreateQueueInput) (req *request.Request, output *CreateQueueOutput) { + op := &request.Operation{ + Name: opCreateQueue, + HTTPMethod: "POST", + HTTPPath: "/", + } + + if input == nil { + input = &CreateQueueInput{} + } + + output = &CreateQueueOutput{} + req = c.newRequest(op, input, output) + return +} + +// CreateQueue API operation for AWSQuery Compatible Service. +// +// Returns awserr.Error for service API and SDK errors. Use runtime type assertions +// with awserr.Error's Code and Message methods to get detailed information about +// the error. +// +// See the AWS API reference guide for AWSQuery Compatible Service's +// API operation CreateQueue for usage and error information. +// +// Returned Error Types: +// * QueueDeletedRecently +// +// * QueueNameExists +// +// See also, https://docs.aws.amazon.com/goto/WebAPI/awsquerycompatible-2012-11-05/CreateQueue +func (c *AwsQueryCompatible) CreateQueue(input *CreateQueueInput) (*CreateQueueOutput, error) { + req, out := c.CreateQueueRequest(input) + return out, req.Send() +} + +// CreateQueueWithContext is the same as CreateQueue with the addition of +// the ability to pass a context and additional request options. +// +// See CreateQueue for details on how to use this API operation. +// +// The context must be non-nil and will be used for request cancellation. If +// the context is nil a panic will occur. In the future the SDK may create +// sub-contexts for http.Requests. See https://golang.org/pkg/context/ +// for more information on using Contexts. +func (c *AwsQueryCompatible) CreateQueueWithContext(ctx aws.Context, input *CreateQueueInput, opts ...request.Option) (*CreateQueueOutput, error) { + req, out := c.CreateQueueRequest(input) + req.SetContext(ctx) + req.ApplyOptions(opts...) + return out, req.Send() +} + +type CreateQueueInput struct { + _ struct{} `type:"structure"` + + // QueueName is a required field + QueueName *string `type:"string" required:"true"` +} + +// String returns the string representation. +// +// API parameter values that are decorated as "sensitive" in the API will not +// be included in the string output. The member name will be present, but the +// value will be replaced with "sensitive". +func (s CreateQueueInput) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation. +// +// API parameter values that are decorated as "sensitive" in the API will not +// be included in the string output. The member name will be present, but the +// value will be replaced with "sensitive". +func (s CreateQueueInput) GoString() string { + return s.String() +} + +// Validate inspects the fields of the type to determine if they are valid. +func (s *CreateQueueInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "CreateQueueInput"} + if s.QueueName == nil { + invalidParams.Add(request.NewErrParamRequired("QueueName")) + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil +} + +// SetQueueName sets the QueueName field's value. +func (s *CreateQueueInput) SetQueueName(v string) *CreateQueueInput { + s.QueueName = &v + return s +} + +type CreateQueueOutput struct { + _ struct{} `type:"structure"` + + QueueUrl *string `type:"string"` +} + +// String returns the string representation. +// +// API parameter values that are decorated as "sensitive" in the API will not +// be included in the string output. The member name will be present, but the +// value will be replaced with "sensitive". +func (s CreateQueueOutput) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation. +// +// API parameter values that are decorated as "sensitive" in the API will not +// be included in the string output. The member name will be present, but the +// value will be replaced with "sensitive". +func (s CreateQueueOutput) GoString() string { + return s.String() +} + +// SetQueueUrl sets the QueueUrl field's value. +func (s *CreateQueueOutput) SetQueueUrl(v string) *CreateQueueOutput { + s.QueueUrl = &v + return s +} + +type QueueDeletedRecently struct { + _ struct{} `type:"structure"` + RespMetadata protocol.ResponseMetadata `json:"-" xml:"-"` + + Message_ *string `locationName:"message" type:"string"` +} + +// String returns the string representation. +// +// API parameter values that are decorated as "sensitive" in the API will not +// be included in the string output. The member name will be present, but the +// value will be replaced with "sensitive". +func (s QueueDeletedRecently) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation. +// +// API parameter values that are decorated as "sensitive" in the API will not +// be included in the string output. The member name will be present, but the +// value will be replaced with "sensitive". +func (s QueueDeletedRecently) GoString() string { + return s.String() +} + +func newErrorQueueDeletedRecently(v protocol.ResponseMetadata) error { + return &QueueDeletedRecently{ + RespMetadata: v, + } +} + +// Code returns the exception type name. +func (s *QueueDeletedRecently) Code() string { + return "QueueDeletedRecently" +} + +// Message returns the exception's message. +func (s *QueueDeletedRecently) Message() string { + if s.Message_ != nil { + return *s.Message_ + } + return "" +} + +// OrigErr always returns nil, satisfies awserr.Error interface. +func (s *QueueDeletedRecently) OrigErr() error { + return nil +} + +func (s *QueueDeletedRecently) Error() string { + return fmt.Sprintf("%s: %s", s.Code(), s.Message()) +} + +// Status code returns the HTTP status code for the request's response error. +func (s *QueueDeletedRecently) StatusCode() int { + return s.RespMetadata.StatusCode +} + +// RequestID returns the service's response RequestID for request. +func (s *QueueDeletedRecently) RequestID() string { + return s.RespMetadata.RequestID +} + +type QueueNameExists struct { + _ struct{} `type:"structure"` + RespMetadata protocol.ResponseMetadata `json:"-" xml:"-"` + + Message_ *string `locationName:"message" type:"string"` +} + +// String returns the string representation. +// +// API parameter values that are decorated as "sensitive" in the API will not +// be included in the string output. The member name will be present, but the +// value will be replaced with "sensitive". +func (s QueueNameExists) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation. +// +// API parameter values that are decorated as "sensitive" in the API will not +// be included in the string output. The member name will be present, but the +// value will be replaced with "sensitive". +func (s QueueNameExists) GoString() string { + return s.String() +} + +func newErrorQueueNameExists(v protocol.ResponseMetadata) error { + return &QueueNameExists{ + RespMetadata: v, + } +} + +// Code returns the exception type name. +func (s *QueueNameExists) Code() string { + return "QueueNameExists" +} + +// Message returns the exception's message. +func (s *QueueNameExists) Message() string { + if s.Message_ != nil { + return *s.Message_ + } + return "" +} + +// OrigErr always returns nil, satisfies awserr.Error interface. +func (s *QueueNameExists) OrigErr() error { + return nil +} + +func (s *QueueNameExists) Error() string { + return fmt.Sprintf("%s: %s", s.Code(), s.Message()) +} + +// Status code returns the HTTP status code for the request's response error. +func (s *QueueNameExists) StatusCode() int { + return s.RespMetadata.StatusCode +} + +// RequestID returns the service's response RequestID for request. +func (s *QueueNameExists) RequestID() string { + return s.RespMetadata.RequestID +} diff --git a/private/model/api/codegentest/service/awsquerycompatible/awsquerycompatible_test.go b/private/model/api/codegentest/service/awsquerycompatible/awsquerycompatible_test.go new file mode 100644 index 0000000000..c8809497e9 --- /dev/null +++ b/private/model/api/codegentest/service/awsquerycompatible/awsquerycompatible_test.go @@ -0,0 +1,103 @@ +//go:build go1.13 +// +build go1.13 + +package awsquerycompatible + +import ( + "errors" + "io" + "io/ioutil" + "net/http" + "strings" + "testing" + + "github.com/aws/aws-sdk-go/aws/awserr" + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/corehandlers" + "github.com/aws/aws-sdk-go/aws/request" + "github.com/aws/aws-sdk-go/awstesting/unit" +) + +func TestAWSQuery(t *testing.T) { + cases := map[string]struct { + statusCode int + responseBody interface { + io.Reader + Len() int + } + headers http.Header + expectErrorCode string + }{ + "when header is present": { + statusCode: 500, + responseBody: strings.NewReader(`{"__type":"com.amazonaws.awsquerycompatible#QueueDeletedRecently", "message":"Some user-visible message"}`), + expectErrorCode: "AWS.SimpleQueueService.QueueDeletedRecently", + headers: http.Header{"x-amzn-query-error": []string{"AWS.SimpleQueueService.QueueDeletedRecently;Sender"}}, + }, + "for unmodlled error code": { + statusCode: 400, + responseBody: strings.NewReader(`{"__type":"com.amazonaws.awsquerycompatible#AccessDeniedException", "message":"Some user-visible message"}`), + expectErrorCode: "AccessDenied", + headers: http.Header{"x-amzn-query-error": []string{"AccessDenied;Sender"}}, + }, + "when header is not present": { + statusCode: 400, + responseBody: strings.NewReader(`{"__type":"com.amazonaws.awsquerycompatible#AccessDeniedException", "message":"Some user-visible message"}`), + expectErrorCode: "AccessDeniedException", + headers: http.Header{}, + }, + "when header is nil": { + statusCode: 400, + responseBody: strings.NewReader(`{"__type":"com.amazonaws.awsquerycompatible#AccessDeniedException", "message":"Some user-visible message"}`), + expectErrorCode: "AccessDeniedException", + headers: nil, + }, + "when header is malformed": { + statusCode: 500, + responseBody: strings.NewReader(`{"__type":"com.amazonaws.awsquerycompatible#QueueDeletedRecently", "message":"Some user-visible message"}`), + expectErrorCode: "AWS.SimpleQueueService.QueueDeletedRecently", + headers: http.Header{"x-amzn-query-error": []string{"AWS.SimpleQueueService.QueueDeletedRecently-Sender"}}, + }, + } + + for name, c := range cases { + t.Run(name, func(t *testing.T) { + client := New(unit.Session.Copy(), &aws.Config{ + MaxRetries: aws.Int(0), + }) + + client.Handlers.Send.Swap(corehandlers.SendHandler.Name, request.NamedHandler{ + Name: corehandlers.SendHandler.Name, + Fn: func(r *request.Request) { + r.HTTPResponse = &http.Response{ + StatusCode: c.statusCode, + ContentLength: func() int64 { + if c.responseBody == nil { + return 0 + } + return int64(c.responseBody.Len()) + }(), + Header: c.headers, + Body: ioutil.NopCloser(c.responseBody), + } + }, + }) + + _, err := client.CreateQueue(&CreateQueueInput{ + QueueName: aws.String("queueName"), + }) + if err == nil { + t.Fatalf("expect error, got none") + } + + var awsErr awserr.RequestFailure + if !errors.As(err, &awsErr) { + t.Fatalf("expect RequestFailure error, got %#v", err) + } + + if e, a := c.expectErrorCode, awsErr.Code(); e != a { + t.Errorf("expect %v code, got %v", e, a) + } + }) + } +} diff --git a/private/model/api/codegentest/service/awsquerycompatible/awsquerycompatibleiface/interface.go b/private/model/api/codegentest/service/awsquerycompatible/awsquerycompatibleiface/interface.go new file mode 100644 index 0000000000..fb12da6579 --- /dev/null +++ b/private/model/api/codegentest/service/awsquerycompatible/awsquerycompatibleiface/interface.go @@ -0,0 +1,68 @@ +// Code generated by private/model/cli/gen-api/main.go. DO NOT EDIT. + +// Package awsquerycompatibleiface provides an interface to enable mocking the AWSQuery Compatible Service service client +// for testing your code. +// +// It is important to note that this interface will have breaking changes +// when the service model is updated and adds new API operations, paginators, +// and waiters. +package awsquerycompatibleiface + +import ( + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/request" + "github.com/aws/aws-sdk-go/service/awsquerycompatible" +) + +// AwsQueryCompatibleAPI provides an interface to enable mocking the +// awsquerycompatible.AwsQueryCompatible service client's API operation, +// paginators, and waiters. This make unit testing your code that calls out +// to the SDK's service client's calls easier. +// +// The best way to use this interface is so the SDK's service client's calls +// can be stubbed out for unit testing your code with the SDK without needing +// to inject custom request handlers into the SDK's request pipeline. +// +// // myFunc uses an SDK service client to make a request to +// // AWSQuery Compatible Service. +// func myFunc(svc awsquerycompatibleiface.AwsQueryCompatibleAPI) bool { +// // Make svc.CreateQueue request +// } +// +// func main() { +// sess := session.New() +// svc := awsquerycompatible.New(sess) +// +// myFunc(svc) +// } +// +// In your _test.go file: +// +// // Define a mock struct to be used in your unit tests of myFunc. +// type mockAwsQueryCompatibleClient struct { +// awsquerycompatibleiface.AwsQueryCompatibleAPI +// } +// func (m *mockAwsQueryCompatibleClient) CreateQueue(input *awsquerycompatible.CreateQueueInput) (*awsquerycompatible.CreateQueueOutput, error) { +// // mock response/functionality +// } +// +// func TestMyFunc(t *testing.T) { +// // Setup Test +// mockSvc := &mockAwsQueryCompatibleClient{} +// +// myfunc(mockSvc) +// +// // Verify myFunc's functionality +// } +// +// It is important to note that this interface will have breaking changes +// when the service model is updated and adds new API operations, paginators, +// and waiters. Its suggested to use the pattern above for testing, or using +// tooling to generate mocks to satisfy the interfaces. +type AwsQueryCompatibleAPI interface { + CreateQueue(*awsquerycompatible.CreateQueueInput) (*awsquerycompatible.CreateQueueOutput, error) + CreateQueueWithContext(aws.Context, *awsquerycompatible.CreateQueueInput, ...request.Option) (*awsquerycompatible.CreateQueueOutput, error) + CreateQueueRequest(*awsquerycompatible.CreateQueueInput) (*request.Request, *awsquerycompatible.CreateQueueOutput) +} + +var _ AwsQueryCompatibleAPI = (*awsquerycompatible.AwsQueryCompatible)(nil) diff --git a/private/model/api/codegentest/service/awsquerycompatible/doc.go b/private/model/api/codegentest/service/awsquerycompatible/doc.go new file mode 100644 index 0000000000..703d1f0357 --- /dev/null +++ b/private/model/api/codegentest/service/awsquerycompatible/doc.go @@ -0,0 +1,26 @@ +// Code generated by private/model/cli/gen-api/main.go. DO NOT EDIT. + +// Package awsquerycompatible provides the client and types for making API +// requests to AWSQuery Compatible Service. +// +// See https://docs.aws.amazon.com/goto/WebAPI/awsquerycompatible-2012-11-05 for more information on this service. +// +// See awsquerycompatible package documentation for more information. +// https://docs.aws.amazon.com/sdk-for-go/api/service/awsquerycompatible/ +// +// Using the Client +// +// To contact AWSQuery Compatible Service with the SDK use the New function to create +// a new service client. With that client you can make API requests to the service. +// These clients are safe to use concurrently. +// +// See the SDK's documentation for more information on how to use the SDK. +// https://docs.aws.amazon.com/sdk-for-go/api/ +// +// See aws.Config documentation for more information on configuring SDK clients. +// https://docs.aws.amazon.com/sdk-for-go/api/aws/#Config +// +// See the AWSQuery Compatible Service client AwsQueryCompatible for more +// information on creating client for this service. +// https://docs.aws.amazon.com/sdk-for-go/api/service/awsquerycompatible/#New +package awsquerycompatible diff --git a/private/model/api/codegentest/service/awsquerycompatible/errors.go b/private/model/api/codegentest/service/awsquerycompatible/errors.go new file mode 100644 index 0000000000..3c49cb3bbf --- /dev/null +++ b/private/model/api/codegentest/service/awsquerycompatible/errors.go @@ -0,0 +1,23 @@ +// Code generated by private/model/cli/gen-api/main.go. DO NOT EDIT. + +package awsquerycompatible + +import ( + "github.com/aws/aws-sdk-go/private/protocol" +) + +const ( + + // ErrCodeQueueDeletedRecently for service response error code + // "QueueDeletedRecently". + ErrCodeQueueDeletedRecently = "QueueDeletedRecently" + + // ErrCodeQueueNameExists for service response error code + // "QueueNameExists". + ErrCodeQueueNameExists = "QueueNameExists" +) + +var exceptionFromCode = map[string]func(protocol.ResponseMetadata) error{ + "QueueDeletedRecently": newErrorQueueDeletedRecently, + "QueueNameExists": newErrorQueueNameExists, +} diff --git a/private/model/api/codegentest/service/awsquerycompatible/service.go b/private/model/api/codegentest/service/awsquerycompatible/service.go new file mode 100644 index 0000000000..e347c8c550 --- /dev/null +++ b/private/model/api/codegentest/service/awsquerycompatible/service.go @@ -0,0 +1,107 @@ +// Code generated by private/model/cli/gen-api/main.go. DO NOT EDIT. + +package awsquerycompatible + +import ( + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/client" + "github.com/aws/aws-sdk-go/aws/client/metadata" + "github.com/aws/aws-sdk-go/aws/request" + "github.com/aws/aws-sdk-go/aws/signer/v4" + "github.com/aws/aws-sdk-go/private/protocol" + "github.com/aws/aws-sdk-go/private/protocol/jsonrpc" +) + +// AwsQueryCompatible provides the API operation methods for making requests to +// AWSQuery Compatible Service. See this package's package overview docs +// for details on the service. +// +// AwsQueryCompatible methods are safe to use concurrently. It is not safe to +// modify mutate any of the struct's properties though. +type AwsQueryCompatible struct { + *client.Client +} + +// Used for custom client initialization logic +var initClient func(*client.Client) + +// Used for custom request initialization logic +var initRequest func(*request.Request) + +// Service information constants +const ( + ServiceName = "AWSQueryCompatible" // Name of service. + EndpointsID = "awsquerycompatible" // ID to lookup a service endpoint with. + ServiceID = "AWSQueryCompatible" // ServiceID is a unique identifier of a specific service. +) + +// New creates a new instance of the AwsQueryCompatible client with a session. +// If additional configuration is needed for the client instance use the optional +// aws.Config parameter to add your extra config. +// +// Example: +// mySession := session.Must(session.NewSession()) +// +// // Create a AwsQueryCompatible client from just a session. +// svc := awsquerycompatible.New(mySession) +// +// // Create a AwsQueryCompatible client with additional configuration +// svc := awsquerycompatible.New(mySession, aws.NewConfig().WithRegion("us-west-2")) +func New(p client.ConfigProvider, cfgs ...*aws.Config) *AwsQueryCompatible { + c := p.ClientConfig(EndpointsID, cfgs...) + if c.SigningNameDerived || len(c.SigningName) == 0 { + c.SigningName = EndpointsID + // No Fallback + } + return newClient(*c.Config, c.Handlers, c.PartitionID, c.Endpoint, c.SigningRegion, c.SigningName, c.ResolvedRegion) +} + +// newClient creates, initializes and returns a new service client instance. +func newClient(cfg aws.Config, handlers request.Handlers, partitionID, endpoint, signingRegion, signingName, resolvedRegion string) *AwsQueryCompatible { + svc := &AwsQueryCompatible{ + Client: client.New( + cfg, + metadata.ClientInfo{ + ServiceName: ServiceName, + ServiceID: ServiceID, + SigningName: signingName, + SigningRegion: signingRegion, + PartitionID: partitionID, + Endpoint: endpoint, + APIVersion: "2012-11-05", + ResolvedRegion: resolvedRegion, + JSONVersion: "1.1", + }, + handlers, + ), + } + + // Handlers + svc.Handlers.Sign.PushBackNamed(v4.SignRequestHandler) + svc.Handlers.Build.PushBackNamed(jsonrpc.BuildHandler) + svc.Handlers.Unmarshal.PushBackNamed(jsonrpc.UnmarshalHandler) + svc.Handlers.UnmarshalMeta.PushBackNamed(jsonrpc.UnmarshalMetaHandler) + svc.Handlers.UnmarshalError.PushBackNamed( + protocol.NewUnmarshalErrorHandler(jsonrpc.NewUnmarshalTypedError(exceptionFromCode)).NamedHandler(), + ) + + // Run custom client initialization if present + if initClient != nil { + initClient(svc.Client) + } + + return svc +} + +// newRequest creates a new request for a AwsQueryCompatible operation and runs any +// custom request initialization. +func (c *AwsQueryCompatible) newRequest(op *request.Operation, params, data interface{}) *request.Request { + req := c.NewRequest(op, params, data) + + // Run custom request initialization if present + if initRequest != nil { + initRequest(req) + } + + return req +} diff --git a/private/protocol/jsonrpc/unmarshal_error.go b/private/protocol/jsonrpc/unmarshal_error.go index c0c52e2db0..2ef5c1fc95 100644 --- a/private/protocol/jsonrpc/unmarshal_error.go +++ b/private/protocol/jsonrpc/unmarshal_error.go @@ -13,6 +13,10 @@ import ( "github.com/aws/aws-sdk-go/private/protocol/json/jsonutil" ) +const ( + awsQueryError = "x-amzn-query-error" +) + // UnmarshalTypedError provides unmarshaling errors API response errors // for both typed and untyped errors. type UnmarshalTypedError struct { @@ -50,6 +54,20 @@ func (u *UnmarshalTypedError) UnmarshalError( code := codeParts[len(codeParts)-1] msg := jsonErr.Message + if (resp.Header != nil) { + queryCodeHeader := resp.Header[awsQueryError] + if len(queryCodeHeader) > 0 { + queryCodeParts := strings.Split(queryCodeHeader[0], ";") + if queryCodeParts != nil && len(queryCodeParts) == 2 { + return awserr.NewRequestFailure( + awserr.New(queryCodeParts[0], msg, nil), + respMeta.StatusCode, + respMeta.RequestID, + ), nil + } + } +} + if fn, ok := u.exceptions[code]; ok { // If exception code is know, use associated constructor to get a value // for the exception that the JSON body can be unmarshaled into. From d79ea2aa70ebb0eabf89fd8b46be11c9aaa2136d Mon Sep 17 00:00:00 2001 From: Eddy Ilangovan Date: Sun, 23 Oct 2022 19:28:04 -0700 Subject: [PATCH 02/14] Fix indentation --- private/protocol/jsonrpc/unmarshal_error.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/private/protocol/jsonrpc/unmarshal_error.go b/private/protocol/jsonrpc/unmarshal_error.go index 2ef5c1fc95..0a8e2b8021 100644 --- a/private/protocol/jsonrpc/unmarshal_error.go +++ b/private/protocol/jsonrpc/unmarshal_error.go @@ -63,10 +63,10 @@ func (u *UnmarshalTypedError) UnmarshalError( awserr.New(queryCodeParts[0], msg, nil), respMeta.StatusCode, respMeta.RequestID, - ), nil + ), nil + } } - } -} + } if fn, ok := u.exceptions[code]; ok { // If exception code is know, use associated constructor to get a value From ef45b90e61b7e30a78a81a6eaf83898b7db5098d Mon Sep 17 00:00:00 2001 From: Eddy Ilangovan Date: Sun, 23 Oct 2022 22:28:15 -0700 Subject: [PATCH 03/14] Fix build failure --- .../awsquerycompatible/awsquerycompatibleiface/interface.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/private/model/api/codegentest/service/awsquerycompatible/awsquerycompatibleiface/interface.go b/private/model/api/codegentest/service/awsquerycompatible/awsquerycompatibleiface/interface.go index fb12da6579..f46fc1120f 100644 --- a/private/model/api/codegentest/service/awsquerycompatible/awsquerycompatibleiface/interface.go +++ b/private/model/api/codegentest/service/awsquerycompatible/awsquerycompatibleiface/interface.go @@ -11,7 +11,7 @@ package awsquerycompatibleiface import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/request" - "github.com/aws/aws-sdk-go/service/awsquerycompatible" + "github.com/aws/aws-sdk-go/private/model/api/codegentest/service/awsquerycompatible" ) // AwsQueryCompatibleAPI provides an interface to enable mocking the From 6bebd63ba1cf623ec2f42b79d4c1f22876549293 Mon Sep 17 00:00:00 2001 From: Eddy Ilangovan Date: Sun, 23 Oct 2022 22:44:48 -0700 Subject: [PATCH 04/14] Fix test failure --- .../service/awsquerycompatible/awsquerycompatible_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/private/model/api/codegentest/service/awsquerycompatible/awsquerycompatible_test.go b/private/model/api/codegentest/service/awsquerycompatible/awsquerycompatible_test.go index c8809497e9..f43c79f6fb 100644 --- a/private/model/api/codegentest/service/awsquerycompatible/awsquerycompatible_test.go +++ b/private/model/api/codegentest/service/awsquerycompatible/awsquerycompatible_test.go @@ -55,7 +55,7 @@ func TestAWSQuery(t *testing.T) { "when header is malformed": { statusCode: 500, responseBody: strings.NewReader(`{"__type":"com.amazonaws.awsquerycompatible#QueueDeletedRecently", "message":"Some user-visible message"}`), - expectErrorCode: "AWS.SimpleQueueService.QueueDeletedRecently", + expectErrorCode: "QueueDeletedRecently", headers: http.Header{"x-amzn-query-error": []string{"AWS.SimpleQueueService.QueueDeletedRecently-Sender"}}, }, } From 039e58b0f3ac2f973484fc99ff1e3d76218e7e11 Mon Sep 17 00:00:00 2001 From: Eddy Ilangovan Date: Mon, 24 Oct 2022 12:04:23 -0700 Subject: [PATCH 05/14] Fix query header population in tests --- .../awsquerycompatible_test.go | 20 ++++++++++++----- private/protocol/jsonrpc/unmarshal_error.go | 22 +++++++++---------- 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/private/model/api/codegentest/service/awsquerycompatible/awsquerycompatible_test.go b/private/model/api/codegentest/service/awsquerycompatible/awsquerycompatible_test.go index f43c79f6fb..dc032e6be0 100644 --- a/private/model/api/codegentest/service/awsquerycompatible/awsquerycompatible_test.go +++ b/private/model/api/codegentest/service/awsquerycompatible/awsquerycompatible_test.go @@ -25,26 +25,26 @@ func TestAWSQuery(t *testing.T) { io.Reader Len() int } - headers http.Header + headers map[string]string expectErrorCode string }{ "when header is present": { statusCode: 500, responseBody: strings.NewReader(`{"__type":"com.amazonaws.awsquerycompatible#QueueDeletedRecently", "message":"Some user-visible message"}`), expectErrorCode: "AWS.SimpleQueueService.QueueDeletedRecently", - headers: http.Header{"x-amzn-query-error": []string{"AWS.SimpleQueueService.QueueDeletedRecently;Sender"}}, + headers: map[string]string{"x-amzn-query-error": "AWS.SimpleQueueService.QueueDeletedRecently;Sender"}, }, "for unmodlled error code": { statusCode: 400, responseBody: strings.NewReader(`{"__type":"com.amazonaws.awsquerycompatible#AccessDeniedException", "message":"Some user-visible message"}`), expectErrorCode: "AccessDenied", - headers: http.Header{"x-amzn-query-error": []string{"AccessDenied;Sender"}}, + headers: map[string]string{"x-amzn-query-error": "AccessDenied;Sender"}, }, "when header is not present": { statusCode: 400, responseBody: strings.NewReader(`{"__type":"com.amazonaws.awsquerycompatible#AccessDeniedException", "message":"Some user-visible message"}`), expectErrorCode: "AccessDeniedException", - headers: http.Header{}, + headers: map[string]string{}, }, "when header is nil": { statusCode: 400, @@ -56,7 +56,7 @@ func TestAWSQuery(t *testing.T) { statusCode: 500, responseBody: strings.NewReader(`{"__type":"com.amazonaws.awsquerycompatible#QueueDeletedRecently", "message":"Some user-visible message"}`), expectErrorCode: "QueueDeletedRecently", - headers: http.Header{"x-amzn-query-error": []string{"AWS.SimpleQueueService.QueueDeletedRecently-Sender"}}, + headers: map[string]string{"x-amzn-query-error": "AWS.SimpleQueueService.QueueDeletedRecently-Sender"}, }, } @@ -77,7 +77,15 @@ func TestAWSQuery(t *testing.T) { } return int64(c.responseBody.Len()) }(), - Header: c.headers, + Header: func() http.Header { + h := http.Header{} + if c.headers != nil { + for key, value := range c.headers { + h.Set(key, value) + } + } + return h + }(), Body: ioutil.NopCloser(c.responseBody), } }, diff --git a/private/protocol/jsonrpc/unmarshal_error.go b/private/protocol/jsonrpc/unmarshal_error.go index 0a8e2b8021..ba7246df59 100644 --- a/private/protocol/jsonrpc/unmarshal_error.go +++ b/private/protocol/jsonrpc/unmarshal_error.go @@ -54,18 +54,16 @@ func (u *UnmarshalTypedError) UnmarshalError( code := codeParts[len(codeParts)-1] msg := jsonErr.Message - if (resp.Header != nil) { - queryCodeHeader := resp.Header[awsQueryError] - if len(queryCodeHeader) > 0 { - queryCodeParts := strings.Split(queryCodeHeader[0], ";") - if queryCodeParts != nil && len(queryCodeParts) == 2 { - return awserr.NewRequestFailure( - awserr.New(queryCodeParts[0], msg, nil), - respMeta.StatusCode, - respMeta.RequestID, - ), nil - } - } + queryCodeHeader := resp.Header.Get(awsQueryError) + if queryCodeHeader != "" { + queryCodeParts := strings.Split(queryCodeHeader, ";") + if queryCodeParts != nil && len(queryCodeParts) == 2 { + return awserr.NewRequestFailure( + awserr.New(queryCodeParts[0], msg, nil), + respMeta.StatusCode, + respMeta.RequestID, + ), nil + } } if fn, ok := u.exceptions[code]; ok { From fa12a7dacaf08366d7147c5a71dbb87b5f193391 Mon Sep 17 00:00:00 2001 From: Eddy Ilangovan Date: Mon, 24 Oct 2022 16:46:02 -0700 Subject: [PATCH 06/14] Add AWSQueryCompatible trait support --- private/model/api/api.go | 7 +++++++ .../models/awsquerycompatible/api-2.json | 3 +++ .../awsquerycompatibleiface/interface.go | 2 +- .../service/awsquerycompatible/service.go | 2 +- private/protocol/jsonrpc/unmarshal_error.go | 18 +++++++++++++++++- 5 files changed, 29 insertions(+), 3 deletions(-) diff --git a/private/model/api/api.go b/private/model/api/api.go index b3bf35728b..d3f93cfe37 100644 --- a/private/model/api/api.go +++ b/private/model/api/api.go @@ -96,8 +96,11 @@ type Metadata struct { ServiceID string NoResolveEndpoint bool + AWSQueryCompatible *awsQueryCompatible } +type awsQueryCompatible struct {} + // ProtocolSettings define how the SDK should handle requests in the context // of of a protocol. type ProtocolSettings struct { @@ -639,7 +642,11 @@ func newClient(cfg aws.Config, handlers request.Handlers, partitionID, endpoint, {{- if and $.WithGeneratedTypedErrors (gt (len $.ShapeListErrors) 0) }} {{- $_ := $.AddSDKImport "private/protocol" }} svc.Handlers.UnmarshalError.PushBackNamed( + {{- if .Metadata.AWSQueryCompatible }} + protocol.NewUnmarshalErrorHandler({{ .ProtocolPackage }}.NewUnmarshalTypedErrorWithOptions(exceptionFromCode, {{ .ProtocolPackage }}.WithQueryCompatibility())).NamedHandler(), + {{- else }} protocol.NewUnmarshalErrorHandler({{ .ProtocolPackage }}.NewUnmarshalTypedError(exceptionFromCode)).NamedHandler(), + {{- end}} ) {{- else }} svc.Handlers.UnmarshalError.PushBackNamed({{ .ProtocolPackage }}.UnmarshalErrorHandler) diff --git a/private/model/api/codegentest/models/awsquerycompatible/api-2.json b/private/model/api/codegentest/models/awsquerycompatible/api-2.json index 83b0139f7b..fe4bd3cd02 100644 --- a/private/model/api/codegentest/models/awsquerycompatible/api-2.json +++ b/private/model/api/codegentest/models/awsquerycompatible/api-2.json @@ -3,6 +3,9 @@ "version":"2.0", "metadata":{ "apiVersion":"2012-11-05", + "awsQueryCompatible": { + + }, "endpointPrefix":"awsquerycompatible", "jsonVersion":"1.1", "protocol":"json", diff --git a/private/model/api/codegentest/service/awsquerycompatible/awsquerycompatibleiface/interface.go b/private/model/api/codegentest/service/awsquerycompatible/awsquerycompatibleiface/interface.go index f46fc1120f..fb12da6579 100644 --- a/private/model/api/codegentest/service/awsquerycompatible/awsquerycompatibleiface/interface.go +++ b/private/model/api/codegentest/service/awsquerycompatible/awsquerycompatibleiface/interface.go @@ -11,7 +11,7 @@ package awsquerycompatibleiface import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/request" - "github.com/aws/aws-sdk-go/private/model/api/codegentest/service/awsquerycompatible" + "github.com/aws/aws-sdk-go/service/awsquerycompatible" ) // AwsQueryCompatibleAPI provides an interface to enable mocking the diff --git a/private/model/api/codegentest/service/awsquerycompatible/service.go b/private/model/api/codegentest/service/awsquerycompatible/service.go index e347c8c550..49f0790479 100644 --- a/private/model/api/codegentest/service/awsquerycompatible/service.go +++ b/private/model/api/codegentest/service/awsquerycompatible/service.go @@ -82,7 +82,7 @@ func newClient(cfg aws.Config, handlers request.Handlers, partitionID, endpoint, svc.Handlers.Unmarshal.PushBackNamed(jsonrpc.UnmarshalHandler) svc.Handlers.UnmarshalMeta.PushBackNamed(jsonrpc.UnmarshalMetaHandler) svc.Handlers.UnmarshalError.PushBackNamed( - protocol.NewUnmarshalErrorHandler(jsonrpc.NewUnmarshalTypedError(exceptionFromCode)).NamedHandler(), + protocol.NewUnmarshalErrorHandler(jsonrpc.NewUnmarshalTypedErrorWithOptions(exceptionFromCode, jsonrpc.WithQueryCompatibility())).NamedHandler(), ) // Run custom client initialization if present diff --git a/private/protocol/jsonrpc/unmarshal_error.go b/private/protocol/jsonrpc/unmarshal_error.go index ba7246df59..4a781162fa 100644 --- a/private/protocol/jsonrpc/unmarshal_error.go +++ b/private/protocol/jsonrpc/unmarshal_error.go @@ -21,6 +21,7 @@ const ( // for both typed and untyped errors. type UnmarshalTypedError struct { exceptions map[string]func(protocol.ResponseMetadata) error + parseQueryError bool } // NewUnmarshalTypedError returns an UnmarshalTypedError initialized for the @@ -28,6 +29,21 @@ type UnmarshalTypedError struct { func NewUnmarshalTypedError(exceptions map[string]func(protocol.ResponseMetadata) error) *UnmarshalTypedError { return &UnmarshalTypedError{ exceptions: exceptions, + parseQueryError: false, + } +} + +func NewUnmarshalTypedErrorWithOptions(exceptions map[string]func(protocol.ResponseMetadata) error, optFns ...func(*UnmarshalTypedError)) *UnmarshalTypedError { + err := NewUnmarshalTypedError(exceptions) + if len(optFns) == 1 { + optFns[0](err) + } + return err +} + +func WithQueryCompatibility() func(*UnmarshalTypedError) { + return func(typedError *UnmarshalTypedError) { + typedError.parseQueryError = true } } @@ -55,7 +71,7 @@ func (u *UnmarshalTypedError) UnmarshalError( msg := jsonErr.Message queryCodeHeader := resp.Header.Get(awsQueryError) - if queryCodeHeader != "" { + if queryCodeHeader != "" && u.parseQueryError { queryCodeParts := strings.Split(queryCodeHeader, ";") if queryCodeParts != nil && len(queryCodeParts) == 2 { return awserr.NewRequestFailure( From a0910dcffaf5c3cb04e4d454224e94cfec1a054d Mon Sep 17 00:00:00 2001 From: Eddy Ilangovan Date: Mon, 24 Oct 2022 17:01:51 -0700 Subject: [PATCH 07/14] Fix import --- .../awsquerycompatible/awsquerycompatibleiface/interface.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/private/model/api/codegentest/service/awsquerycompatible/awsquerycompatibleiface/interface.go b/private/model/api/codegentest/service/awsquerycompatible/awsquerycompatibleiface/interface.go index fb12da6579..f46fc1120f 100644 --- a/private/model/api/codegentest/service/awsquerycompatible/awsquerycompatibleiface/interface.go +++ b/private/model/api/codegentest/service/awsquerycompatible/awsquerycompatibleiface/interface.go @@ -11,7 +11,7 @@ package awsquerycompatibleiface import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/request" - "github.com/aws/aws-sdk-go/service/awsquerycompatible" + "github.com/aws/aws-sdk-go/private/model/api/codegentest/service/awsquerycompatible" ) // AwsQueryCompatibleAPI provides an interface to enable mocking the From 0622cb0943b47cb8c20e6cb22f3c852e63faba58 Mon Sep 17 00:00:00 2001 From: Eddy Ilangovan Date: Fri, 18 Nov 2022 18:16:08 -0800 Subject: [PATCH 08/14] Review comments --- private/protocol/jsonrpc/unmarshal_error.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/private/protocol/jsonrpc/unmarshal_error.go b/private/protocol/jsonrpc/unmarshal_error.go index 4a781162fa..cbe6a30327 100644 --- a/private/protocol/jsonrpc/unmarshal_error.go +++ b/private/protocol/jsonrpc/unmarshal_error.go @@ -35,8 +35,8 @@ func NewUnmarshalTypedError(exceptions map[string]func(protocol.ResponseMetadata func NewUnmarshalTypedErrorWithOptions(exceptions map[string]func(protocol.ResponseMetadata) error, optFns ...func(*UnmarshalTypedError)) *UnmarshalTypedError { err := NewUnmarshalTypedError(exceptions) - if len(optFns) == 1 { - optFns[0](err) + for _, fn := range optFns { + fn(err) } return err } From 1740546bc797c46a6d19853672e693fd9815f135 Mon Sep 17 00:00:00 2001 From: Eddy Ilangovan Date: Fri, 18 Nov 2022 18:58:06 -0800 Subject: [PATCH 09/14] Rename type error variable --- private/protocol/jsonrpc/unmarshal_error.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/private/protocol/jsonrpc/unmarshal_error.go b/private/protocol/jsonrpc/unmarshal_error.go index cbe6a30327..d74f3da116 100644 --- a/private/protocol/jsonrpc/unmarshal_error.go +++ b/private/protocol/jsonrpc/unmarshal_error.go @@ -34,11 +34,11 @@ func NewUnmarshalTypedError(exceptions map[string]func(protocol.ResponseMetadata } func NewUnmarshalTypedErrorWithOptions(exceptions map[string]func(protocol.ResponseMetadata) error, optFns ...func(*UnmarshalTypedError)) *UnmarshalTypedError { - err := NewUnmarshalTypedError(exceptions) + unmarshaledError := NewUnmarshalTypedError(exceptions) for _, fn := range optFns { - fn(err) + fn(unmarshaledError) } - return err + return unmarshaledError } func WithQueryCompatibility() func(*UnmarshalTypedError) { From 87493d86315cc7e9c014dedf745cb78662d243aa Mon Sep 17 00:00:00 2001 From: Eddy Ilangovan Date: Mon, 12 Dec 2022 21:14:35 -0800 Subject: [PATCH 10/14] Preserve error type and response metadata --- private/model/api/api.go | 9 +++- .../service/awsquerycompatible/api.go | 20 ++++++++ .../service/awsquerycompatible/errors.go | 4 ++ .../service/awsquerycompatible/service.go | 2 +- private/model/api/shape.go | 17 +++++++ private/protocol/jsonrpc/unmarshal_error.go | 46 ++++++++++++------- 6 files changed, 79 insertions(+), 19 deletions(-) diff --git a/private/model/api/api.go b/private/model/api/api.go index d3f93cfe37..e888979cd1 100644 --- a/private/model/api/api.go +++ b/private/model/api/api.go @@ -643,7 +643,7 @@ func newClient(cfg aws.Config, handlers request.Handlers, partitionID, endpoint, {{- $_ := $.AddSDKImport "private/protocol" }} svc.Handlers.UnmarshalError.PushBackNamed( {{- if .Metadata.AWSQueryCompatible }} - protocol.NewUnmarshalErrorHandler({{ .ProtocolPackage }}.NewUnmarshalTypedErrorWithOptions(exceptionFromCode, {{ .ProtocolPackage }}.WithQueryCompatibility())).NamedHandler(), + protocol.NewUnmarshalErrorHandler({{ .ProtocolPackage }}.NewUnmarshalTypedErrorWithOptions(exceptionFromCode, {{ .ProtocolPackage }}.WithQueryCompatibility(queryExceptionFromCode))).NamedHandler(), {{- else }} protocol.NewUnmarshalErrorHandler({{ .ProtocolPackage }}.NewUnmarshalTypedError(exceptionFromCode)).NamedHandler(), {{- end}} @@ -940,6 +940,13 @@ const ( "{{ $s.ErrorName }}": newError{{ $s.ShapeName }}, {{- end }} } + {{- if .Metadata.AWSQueryCompatible }} + var queryExceptionFromCode = map[string]func(protocol.ResponseMetadata, string)error { + {{- range $_, $s := $.ShapeListErrors }} + "{{ $s.ErrorName }}": newQueryCompatibleError{{ $s.ShapeName }}, + {{- end }} + } + {{- end }} {{- end }} `)) diff --git a/private/model/api/codegentest/service/awsquerycompatible/api.go b/private/model/api/codegentest/service/awsquerycompatible/api.go index b1f8e1730b..923a3e2609 100644 --- a/private/model/api/codegentest/service/awsquerycompatible/api.go +++ b/private/model/api/codegentest/service/awsquerycompatible/api.go @@ -166,6 +166,7 @@ func (s *CreateQueueOutput) SetQueueUrl(v string) *CreateQueueOutput { type QueueDeletedRecently struct { _ struct{} `type:"structure"` RespMetadata protocol.ResponseMetadata `json:"-" xml:"-"` + code string Message_ *string `locationName:"message" type:"string"` } @@ -193,9 +194,18 @@ func newErrorQueueDeletedRecently(v protocol.ResponseMetadata) error { RespMetadata: v, } } +func newQueryCompatibleErrorQueueDeletedRecently(v protocol.ResponseMetadata, code string) error { + return &QueueDeletedRecently{ + RespMetadata: v, + code: code, + } +} // Code returns the exception type name. func (s *QueueDeletedRecently) Code() string { + if s.code != "" { + return s.code + } return "QueueDeletedRecently" } @@ -229,6 +239,7 @@ func (s *QueueDeletedRecently) RequestID() string { type QueueNameExists struct { _ struct{} `type:"structure"` RespMetadata protocol.ResponseMetadata `json:"-" xml:"-"` + code string Message_ *string `locationName:"message" type:"string"` } @@ -256,9 +267,18 @@ func newErrorQueueNameExists(v protocol.ResponseMetadata) error { RespMetadata: v, } } +func newQueryCompatibleErrorQueueNameExists(v protocol.ResponseMetadata, code string) error { + return &QueueNameExists{ + RespMetadata: v, + code: code, + } +} // Code returns the exception type name. func (s *QueueNameExists) Code() string { + if s.code != "" { + return s.code + } return "QueueNameExists" } diff --git a/private/model/api/codegentest/service/awsquerycompatible/errors.go b/private/model/api/codegentest/service/awsquerycompatible/errors.go index 3c49cb3bbf..574b909ffb 100644 --- a/private/model/api/codegentest/service/awsquerycompatible/errors.go +++ b/private/model/api/codegentest/service/awsquerycompatible/errors.go @@ -21,3 +21,7 @@ var exceptionFromCode = map[string]func(protocol.ResponseMetadata) error{ "QueueDeletedRecently": newErrorQueueDeletedRecently, "QueueNameExists": newErrorQueueNameExists, } +var queryExceptionFromCode = map[string]func(protocol.ResponseMetadata, string) error{ + "QueueDeletedRecently": newQueryCompatibleErrorQueueDeletedRecently, + "QueueNameExists": newQueryCompatibleErrorQueueNameExists, +} diff --git a/private/model/api/codegentest/service/awsquerycompatible/service.go b/private/model/api/codegentest/service/awsquerycompatible/service.go index 49f0790479..9e30c9f265 100644 --- a/private/model/api/codegentest/service/awsquerycompatible/service.go +++ b/private/model/api/codegentest/service/awsquerycompatible/service.go @@ -82,7 +82,7 @@ func newClient(cfg aws.Config, handlers request.Handlers, partitionID, endpoint, svc.Handlers.Unmarshal.PushBackNamed(jsonrpc.UnmarshalHandler) svc.Handlers.UnmarshalMeta.PushBackNamed(jsonrpc.UnmarshalMetaHandler) svc.Handlers.UnmarshalError.PushBackNamed( - protocol.NewUnmarshalErrorHandler(jsonrpc.NewUnmarshalTypedErrorWithOptions(exceptionFromCode, jsonrpc.WithQueryCompatibility())).NamedHandler(), + protocol.NewUnmarshalErrorHandler(jsonrpc.NewUnmarshalTypedErrorWithOptions(exceptionFromCode, jsonrpc.WithQueryCompatibility(queryExceptionFromCode))).NamedHandler(), ) // Run custom client initialization if present diff --git a/private/model/api/shape.go b/private/model/api/shape.go index e4795ce885..319394b74e 100644 --- a/private/model/api/shape.go +++ b/private/model/api/shape.go @@ -783,6 +783,9 @@ type {{ $.ShapeName }} struct { {{- if $.Exception }} {{- $_ := $.API.AddSDKImport "private/protocol" }} RespMetadata protocol.ResponseMetadata` + "`json:\"-\" xml:\"-\"`" + ` + {{- if $.API.Metadata.AWSQueryCompatible }} + code string + {{- end }} {{- end }} {{- if $.OutputEventStreamAPI }} @@ -922,8 +925,22 @@ func newError{{ $.ShapeName }}(v protocol.ResponseMetadata) error { } } +{{- if $.API.Metadata.AWSQueryCompatible }} +func newQueryCompatibleError{{ $.ShapeName }}(v protocol.ResponseMetadata, code string) error { + return &{{ $.ShapeName }}{ + RespMetadata: v, + code: code, + } +} +{{- end }} + // Code returns the exception type name. func (s *{{ $.ShapeName }}) Code() string { + {{- if $.API.Metadata.AWSQueryCompatible }} + if s.code != "" { + return s.code + } + {{- end }} return "{{ $.ErrorName }}" } diff --git a/private/protocol/jsonrpc/unmarshal_error.go b/private/protocol/jsonrpc/unmarshal_error.go index d74f3da116..84e2aebcab 100644 --- a/private/protocol/jsonrpc/unmarshal_error.go +++ b/private/protocol/jsonrpc/unmarshal_error.go @@ -21,7 +21,7 @@ const ( // for both typed and untyped errors. type UnmarshalTypedError struct { exceptions map[string]func(protocol.ResponseMetadata) error - parseQueryError bool + queryExceptions map[string]func(protocol.ResponseMetadata, string) error } // NewUnmarshalTypedError returns an UnmarshalTypedError initialized for the @@ -29,7 +29,7 @@ type UnmarshalTypedError struct { func NewUnmarshalTypedError(exceptions map[string]func(protocol.ResponseMetadata) error) *UnmarshalTypedError { return &UnmarshalTypedError{ exceptions: exceptions, - parseQueryError: false, + queryExceptions: map[string]func(protocol.ResponseMetadata, string) error{}, } } @@ -41,9 +41,9 @@ func NewUnmarshalTypedErrorWithOptions(exceptions map[string]func(protocol.Respo return unmarshaledError } -func WithQueryCompatibility() func(*UnmarshalTypedError) { +func WithQueryCompatibility(queryExceptions map[string]func(protocol.ResponseMetadata, string) error) func(*UnmarshalTypedError) { return func(typedError *UnmarshalTypedError) { - typedError.parseQueryError = true + typedError.queryExceptions = queryExceptions } } @@ -70,30 +70,33 @@ func (u *UnmarshalTypedError) UnmarshalError( code := codeParts[len(codeParts)-1] msg := jsonErr.Message - queryCodeHeader := resp.Header.Get(awsQueryError) - if queryCodeHeader != "" && u.parseQueryError { - queryCodeParts := strings.Split(queryCodeHeader, ";") - if queryCodeParts != nil && len(queryCodeParts) == 2 { - return awserr.NewRequestFailure( - awserr.New(queryCodeParts[0], msg, nil), - respMeta.StatusCode, - respMeta.RequestID, - ), nil - } - } + queryCodeParts := queryCodeParts(resp, u) if fn, ok := u.exceptions[code]; ok { // If exception code is know, use associated constructor to get a value // for the exception that the JSON body can be unmarshaled into. - v := fn(respMeta) + var v error + queryErrFn, queryExceptionsFound := u.queryExceptions[code] + if queryCodeParts != nil && len(queryCodeParts) == 2 && queryExceptionsFound { + v = queryErrFn(respMeta, queryCodeParts[0]) + } else { + v = fn(respMeta) + } err := jsonutil.UnmarshalJSONCaseInsensitive(v, body) if err != nil { return nil, err } - return v, nil } + if queryCodeParts != nil && len(queryCodeParts) == 2 { + return awserr.NewRequestFailure( + awserr.New(queryCodeParts[0], msg, nil), + respMeta.StatusCode, + respMeta.RequestID, + ), nil + } + // fallback to unmodeled generic exceptions return awserr.NewRequestFailure( awserr.New(code, msg, nil), @@ -102,6 +105,15 @@ func (u *UnmarshalTypedError) UnmarshalError( ), nil } +func queryCodeParts(resp *http.Response, u *UnmarshalTypedError) []string { + queryCodeHeader := resp.Header.Get(awsQueryError) + var queryCodeParts []string + if queryCodeHeader != "" && len(u.queryExceptions) > 0 { + queryCodeParts = strings.Split(queryCodeHeader, ";") + } + return queryCodeParts +} + // UnmarshalErrorHandler is a named request handler for unmarshaling jsonrpc // protocol request errors var UnmarshalErrorHandler = request.NamedHandler{ From 85ca501b1639cdbd44a4d0c6670764580a94eef8 Mon Sep 17 00:00:00 2001 From: Eddy Ilangovan Date: Wed, 14 Dec 2022 15:03:58 -0800 Subject: [PATCH 11/14] Rename code variable and type --- .../service/awsquerycompatible/api.go | 16 ++++++++-------- private/model/api/shape.go | 8 ++++---- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/private/model/api/codegentest/service/awsquerycompatible/api.go b/private/model/api/codegentest/service/awsquerycompatible/api.go index 923a3e2609..f7323ddae7 100644 --- a/private/model/api/codegentest/service/awsquerycompatible/api.go +++ b/private/model/api/codegentest/service/awsquerycompatible/api.go @@ -166,7 +166,7 @@ func (s *CreateQueueOutput) SetQueueUrl(v string) *CreateQueueOutput { type QueueDeletedRecently struct { _ struct{} `type:"structure"` RespMetadata protocol.ResponseMetadata `json:"-" xml:"-"` - code string + Code_ *string Message_ *string `locationName:"message" type:"string"` } @@ -197,14 +197,14 @@ func newErrorQueueDeletedRecently(v protocol.ResponseMetadata) error { func newQueryCompatibleErrorQueueDeletedRecently(v protocol.ResponseMetadata, code string) error { return &QueueDeletedRecently{ RespMetadata: v, - code: code, + Code_: &code, } } // Code returns the exception type name. func (s *QueueDeletedRecently) Code() string { - if s.code != "" { - return s.code + if s.Code_ != nil { + return *s.Code_ } return "QueueDeletedRecently" } @@ -239,7 +239,7 @@ func (s *QueueDeletedRecently) RequestID() string { type QueueNameExists struct { _ struct{} `type:"structure"` RespMetadata protocol.ResponseMetadata `json:"-" xml:"-"` - code string + Code_ *string Message_ *string `locationName:"message" type:"string"` } @@ -270,14 +270,14 @@ func newErrorQueueNameExists(v protocol.ResponseMetadata) error { func newQueryCompatibleErrorQueueNameExists(v protocol.ResponseMetadata, code string) error { return &QueueNameExists{ RespMetadata: v, - code: code, + Code_: &code, } } // Code returns the exception type name. func (s *QueueNameExists) Code() string { - if s.code != "" { - return s.code + if s.Code_ != nil { + return *s.Code_ } return "QueueNameExists" } diff --git a/private/model/api/shape.go b/private/model/api/shape.go index 319394b74e..f46cff9a84 100644 --- a/private/model/api/shape.go +++ b/private/model/api/shape.go @@ -784,7 +784,7 @@ type {{ $.ShapeName }} struct { {{- $_ := $.API.AddSDKImport "private/protocol" }} RespMetadata protocol.ResponseMetadata` + "`json:\"-\" xml:\"-\"`" + ` {{- if $.API.Metadata.AWSQueryCompatible }} - code string + Code_ *string {{- end }} {{- end }} @@ -929,7 +929,7 @@ func newError{{ $.ShapeName }}(v protocol.ResponseMetadata) error { func newQueryCompatibleError{{ $.ShapeName }}(v protocol.ResponseMetadata, code string) error { return &{{ $.ShapeName }}{ RespMetadata: v, - code: code, + Code_: &code, } } {{- end }} @@ -937,8 +937,8 @@ func newQueryCompatibleError{{ $.ShapeName }}(v protocol.ResponseMetadata, code // Code returns the exception type name. func (s *{{ $.ShapeName }}) Code() string { {{- if $.API.Metadata.AWSQueryCompatible }} - if s.code != "" { - return s.code + if s.Code_ != nil { + return *s.Code_ } {{- end }} return "{{ $.ErrorName }}" From ef33e605a1733c8aca21c80f106dc8603111f05e Mon Sep 17 00:00:00 2001 From: Eddy Ilangovan Date: Wed, 14 Dec 2022 15:33:16 -0800 Subject: [PATCH 12/14] simplify generic errors --- private/protocol/jsonrpc/unmarshal_error.go | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/private/protocol/jsonrpc/unmarshal_error.go b/private/protocol/jsonrpc/unmarshal_error.go index 84e2aebcab..76342ab8d3 100644 --- a/private/protocol/jsonrpc/unmarshal_error.go +++ b/private/protocol/jsonrpc/unmarshal_error.go @@ -90,11 +90,7 @@ func (u *UnmarshalTypedError) UnmarshalError( } if queryCodeParts != nil && len(queryCodeParts) == 2 { - return awserr.NewRequestFailure( - awserr.New(queryCodeParts[0], msg, nil), - respMeta.StatusCode, - respMeta.RequestID, - ), nil + code = queryCodeParts[0] } // fallback to unmodeled generic exceptions From 9adcdc0e96d651ba88b83cd74ae9d6fe3d6355a3 Mon Sep 17 00:00:00 2001 From: Eddy Ilangovan Date: Thu, 15 Dec 2022 22:39:16 -0800 Subject: [PATCH 13/14] Review comments --- .../codegentest/models/awsquerycompatible/api-2.json | 2 +- .../awsquerycompatible/awsquerycompatible_test.go | 6 +++--- .../service/awsquerycompatible/service.go | 6 +++--- private/protocol/jsonrpc/unmarshal_error.go | 12 +++++++++--- 4 files changed, 16 insertions(+), 10 deletions(-) diff --git a/private/model/api/codegentest/models/awsquerycompatible/api-2.json b/private/model/api/codegentest/models/awsquerycompatible/api-2.json index fe4bd3cd02..eaca1fb20b 100644 --- a/private/model/api/codegentest/models/awsquerycompatible/api-2.json +++ b/private/model/api/codegentest/models/awsquerycompatible/api-2.json @@ -6,7 +6,7 @@ "awsQueryCompatible": { }, - "endpointPrefix":"awsquerycompatible", + "endpointPrefix":"awsqc-exampleendpoint", "jsonVersion":"1.1", "protocol":"json", "serviceAbbreviation":"AwsQueryCompatible", diff --git a/private/model/api/codegentest/service/awsquerycompatible/awsquerycompatible_test.go b/private/model/api/codegentest/service/awsquerycompatible/awsquerycompatible_test.go index dc032e6be0..d924141f81 100644 --- a/private/model/api/codegentest/service/awsquerycompatible/awsquerycompatible_test.go +++ b/private/model/api/codegentest/service/awsquerycompatible/awsquerycompatible_test.go @@ -29,12 +29,12 @@ func TestAWSQuery(t *testing.T) { expectErrorCode string }{ "when header is present": { - statusCode: 500, + statusCode: 400, responseBody: strings.NewReader(`{"__type":"com.amazonaws.awsquerycompatible#QueueDeletedRecently", "message":"Some user-visible message"}`), expectErrorCode: "AWS.SimpleQueueService.QueueDeletedRecently", headers: map[string]string{"x-amzn-query-error": "AWS.SimpleQueueService.QueueDeletedRecently;Sender"}, }, - "for unmodlled error code": { + "for unmodeled error code": { statusCode: 400, responseBody: strings.NewReader(`{"__type":"com.amazonaws.awsquerycompatible#AccessDeniedException", "message":"Some user-visible message"}`), expectErrorCode: "AccessDenied", @@ -53,7 +53,7 @@ func TestAWSQuery(t *testing.T) { headers: nil, }, "when header is malformed": { - statusCode: 500, + statusCode: 400, responseBody: strings.NewReader(`{"__type":"com.amazonaws.awsquerycompatible#QueueDeletedRecently", "message":"Some user-visible message"}`), expectErrorCode: "QueueDeletedRecently", headers: map[string]string{"x-amzn-query-error": "AWS.SimpleQueueService.QueueDeletedRecently-Sender"}, diff --git a/private/model/api/codegentest/service/awsquerycompatible/service.go b/private/model/api/codegentest/service/awsquerycompatible/service.go index 9e30c9f265..1e8cdacf4a 100644 --- a/private/model/api/codegentest/service/awsquerycompatible/service.go +++ b/private/model/api/codegentest/service/awsquerycompatible/service.go @@ -30,9 +30,9 @@ var initRequest func(*request.Request) // Service information constants const ( - ServiceName = "AWSQueryCompatible" // Name of service. - EndpointsID = "awsquerycompatible" // ID to lookup a service endpoint with. - ServiceID = "AWSQueryCompatible" // ServiceID is a unique identifier of a specific service. + ServiceName = "AWSQueryCompatible" // Name of service. + EndpointsID = "awsqc-exampleendpoint" // ID to lookup a service endpoint with. + ServiceID = "AWSQueryCompatible" // ServiceID is a unique identifier of a specific service. ) // New creates a new instance of the AwsQueryCompatible client with a session. diff --git a/private/protocol/jsonrpc/unmarshal_error.go b/private/protocol/jsonrpc/unmarshal_error.go index 76342ab8d3..4f933f2a1c 100644 --- a/private/protocol/jsonrpc/unmarshal_error.go +++ b/private/protocol/jsonrpc/unmarshal_error.go @@ -15,6 +15,8 @@ import ( const ( awsQueryError = "x-amzn-query-error" + // A valid header example - "x-amzn-query-error": ";" + awsQueryErrorPartsCount = 2 ) // UnmarshalTypedError provides unmarshaling errors API response errors @@ -73,11 +75,14 @@ func (u *UnmarshalTypedError) UnmarshalError( queryCodeParts := queryCodeParts(resp, u) if fn, ok := u.exceptions[code]; ok { - // If exception code is know, use associated constructor to get a value + // If query-compatible exceptions are found and query-error-header is found, + // then use associated constructor to get exception with query error code. + // + // If exception code is known, use associated constructor to get a value // for the exception that the JSON body can be unmarshaled into. var v error queryErrFn, queryExceptionsFound := u.queryExceptions[code] - if queryCodeParts != nil && len(queryCodeParts) == 2 && queryExceptionsFound { + if len(queryCodeParts) == awsQueryErrorPartsCount && queryExceptionsFound { v = queryErrFn(respMeta, queryCodeParts[0]) } else { v = fn(respMeta) @@ -89,7 +94,7 @@ func (u *UnmarshalTypedError) UnmarshalError( return v, nil } - if queryCodeParts != nil && len(queryCodeParts) == 2 { + if len(queryCodeParts) == awsQueryErrorPartsCount && len(u.queryExceptions) > 0 { code = queryCodeParts[0] } @@ -101,6 +106,7 @@ func (u *UnmarshalTypedError) UnmarshalError( ), nil } +// A valid header example - "x-amzn-query-error": ";" func queryCodeParts(resp *http.Response, u *UnmarshalTypedError) []string { queryCodeHeader := resp.Header.Get(awsQueryError) var queryCodeParts []string From 3f9c4f2f732eab34743712e39ec30eb1d93142ba Mon Sep 17 00:00:00 2001 From: Isaiah Vita Date: Wed, 28 Dec 2022 16:55:49 +0000 Subject: [PATCH 14/14] add changelog file --- .changelog/6b25cc1a38cf4e229728e8e169b9152b.json | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .changelog/6b25cc1a38cf4e229728e8e169b9152b.json diff --git a/.changelog/6b25cc1a38cf4e229728e8e169b9152b.json b/.changelog/6b25cc1a38cf4e229728e8e169b9152b.json new file mode 100644 index 0000000000..d8558a439b --- /dev/null +++ b/.changelog/6b25cc1a38cf4e229728e8e169b9152b.json @@ -0,0 +1,8 @@ +{ + "id": "6b25cc1a-38cf-4e22-9728-e8e169b9152b", + "type": "feature", + "description": "Add awsQueryCompatible error code translation support", + "modules": [ + "." + ] +} \ No newline at end of file