From 93134df4fd5d1cdb72dd122a850b54d414792d71 Mon Sep 17 00:00:00 2001 From: Jason Del Ponte <961963+jasdel@users.noreply.github.com> Date: Tue, 15 Feb 2022 10:42:13 -0800 Subject: [PATCH] codegen: Update API client generation to not support new JSONValue members (#4269) Updates the SDK's code generation to stop supporting new API modeled JSONValue parameters. The SDK's JSONValue type is only compatible with JSON documents with a top level JSON Object. JSON Lists, Strings, Scalars, are not compatible. This prevents JSON Value working with some APIs such as Amazon Lex Runtime Service's operations. This update also introduces the breaking change to service/lexruntimeservice package by changing the following parameters from a JSONValue to string type. * PostContentInput.ActiveContexts * PutContentOutput.AlternativeIntents * PutContentOutput.ActiveContexts * PutSessionOutput.ActiveContexts --- CHANGELOG_PENDING.md | 8 + private/model/api/api.go | 3 + private/model/api/customization_passes.go | 71 +++++ private/model/api/legacy_jsonvalue.go | 257 ++++++++++++++++++ private/model/api/load.go | 5 + private/model/api/passes.go | 27 +- private/model/api/shape.go | 11 + private/model/cli/gen-protocol-tests/main.go | 7 +- private/protocol/rest/build.go | 4 + private/protocol/rest/unmarshal.go | 7 + service/lexruntimeservice/api.go | 36 ++- .../lexruntimeservice/cust_jsonvalue_test.go | 65 +++++ 12 files changed, 476 insertions(+), 25 deletions(-) create mode 100644 private/model/api/legacy_jsonvalue.go create mode 100644 service/lexruntimeservice/cust_jsonvalue_test.go diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 8a1927a39ca..6ec7e8190de 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -1,5 +1,13 @@ ### SDK Features +* `codegen`: Updates the SDK's code generation to stop supporting new API modeled JSONValue parameters. The SDK's JSONValue type is only compatible with JSON documents with a top level JSON Object. JSON Lists, Strings, Scalars, are not compatible. This prevents JSON Value working with some APIs such as Amazon Lex Runtime Service's operations. + * Related to [#4264](https://github.com/aws/aws-sdk-go/pull/4264) and [#4258](https://github.com/aws/aws-sdk-go/issues/4258) ### SDK Enhancements ### SDK Bugs +* `service/lexruntimeservice`: Introduces a breaking change for following parameters from a JSONValue to string type, because the SDKs JSONValue is not compatible with JSON documents of lists. + * PostContentInput.ActiveContexts + * PutContentOutput.AlternativeIntents + * PutContentOutput.ActiveContexts + * PutSessionOutput.ActiveContexts + * Fixes [#4258](https://github.com/aws/aws-sdk-go/issues/4258) diff --git a/private/model/api/api.go b/private/model/api/api.go index d0f7f08f2c2..4d52db33390 100644 --- a/private/model/api/api.go +++ b/private/model/api/api.go @@ -51,6 +51,9 @@ type API struct { // Set to true to not generate struct field accessors NoGenStructFieldAccessors bool + // Set to not remove unsupported (non-legacy) JSON from API, (for generated tests). + NoRemoveUnsupportedJSONValue bool + BaseImportPath string initialized bool diff --git a/private/model/api/customization_passes.go b/private/model/api/customization_passes.go index 6acabeefc51..de7708bb589 100644 --- a/private/model/api/customization_passes.go +++ b/private/model/api/customization_passes.go @@ -6,6 +6,7 @@ package api import ( "fmt" "io/ioutil" + "log" "os" "path/filepath" "strings" @@ -397,3 +398,73 @@ func backfillAuthType(typ AuthType, opNames ...string) func(*API) error { return nil } } + +// Must be invoked with the original shape name +func removeUnsupportedJSONValue(a *API) error { + for shapeName, shape := range a.Shapes { + switch shape.Type { + case "structure": + for refName, ref := range shape.MemberRefs { + if !ref.JSONValue { + continue + } + if err := removeUnsupportedShapeRefJSONValue(a, shapeName, refName, ref); err != nil { + return fmt.Errorf("failed remove unsupported JSONValue from %v.%v, %v", + shapeName, refName, err) + } + } + case "list": + if !shape.MemberRef.JSONValue { + continue + } + if err := removeUnsupportedShapeRefJSONValue(a, shapeName, "", &shape.MemberRef); err != nil { + return fmt.Errorf("failed remove unsupported JSONValue from %v, %v", + shapeName, err) + } + case "map": + if !shape.ValueRef.JSONValue { + continue + } + if err := removeUnsupportedShapeRefJSONValue(a, shapeName, "", &shape.ValueRef); err != nil { + return fmt.Errorf("failed remove unsupported JSONValue from %v, %v", + shapeName, err) + } + } + } + + return nil +} + +func removeUnsupportedShapeRefJSONValue(a *API, parentName, refName string, ref *ShapeRef) (err error) { + var found bool + + defer func() { + if !found && err == nil { + log.Println("removing JSONValue", a.PackageName(), parentName, refName) + ref.JSONValue = false + ref.SuppressedJSONValue = true + } + }() + + legacyShapes, ok := legacyJSONValueShapes[a.PackageName()] + if !ok { + return nil + } + + legacyShape, ok := legacyShapes[parentName] + if !ok { + return nil + } + + switch legacyShape.Type { + case "structure": + _, ok = legacyShape.StructMembers[refName] + found = ok + case "list": + found = legacyShape.ListMemberRef + case "map": + found = legacyShape.MapValueRef + } + + return nil +} diff --git a/private/model/api/legacy_jsonvalue.go b/private/model/api/legacy_jsonvalue.go new file mode 100644 index 00000000000..84f19aa09c0 --- /dev/null +++ b/private/model/api/legacy_jsonvalue.go @@ -0,0 +1,257 @@ +//go:build codegen +// +build codegen + +package api + +type legacyJSONValues struct { + Type string + StructMembers map[string]struct{} + ListMemberRef bool + MapValueRef bool +} + +var legacyJSONValueShapes = map[string]map[string]legacyJSONValues{ + "braket": map[string]legacyJSONValues{ + "CreateQuantumTaskRequest": legacyJSONValues{ + Type: "structure", + StructMembers: map[string]struct{}{ + "action": struct{}{}, + "deviceParameters": struct{}{}, + }, + }, + "GetDeviceResponse": legacyJSONValues{ + Type: "structure", + StructMembers: map[string]struct{}{ + "deviceCapabilities": struct{}{}, + }, + }, + "GetQuantumTaskResponse": legacyJSONValues{ + Type: "structure", + StructMembers: map[string]struct{}{ + "deviceParameters": struct{}{}, + }, + }, + }, + "cloudwatchevidently": map[string]legacyJSONValues{ + "EvaluateFeatureRequest": legacyJSONValues{ + Type: "structure", + StructMembers: map[string]struct{}{ + "evaluationContext": struct{}{}, + }, + }, + "EvaluateFeatureResponse": legacyJSONValues{ + Type: "structure", + StructMembers: map[string]struct{}{ + "details": struct{}{}, + }, + }, + "EvaluationRequest": legacyJSONValues{ + Type: "structure", + StructMembers: map[string]struct{}{ + "evaluationContext": struct{}{}, + }, + }, + "EvaluationResult": legacyJSONValues{ + Type: "structure", + StructMembers: map[string]struct{}{ + "details": struct{}{}, + }, + }, + "Event": legacyJSONValues{ + Type: "structure", + StructMembers: map[string]struct{}{ + "data": struct{}{}, + }, + }, + "ExperimentReport": legacyJSONValues{ + Type: "structure", + StructMembers: map[string]struct{}{ + "content": struct{}{}, + }, + }, + "MetricDefinition": legacyJSONValues{ + Type: "structure", + StructMembers: map[string]struct{}{ + "eventPattern": struct{}{}, + }, + }, + "MetricDefinitionConfig": legacyJSONValues{ + Type: "structure", + StructMembers: map[string]struct{}{ + "eventPattern": struct{}{}, + }, + }, + }, + "cloudwatchrum": map[string]legacyJSONValues{ + "RumEvent": legacyJSONValues{ + Type: "structure", + StructMembers: map[string]struct{}{ + "details": struct{}{}, + "metadata": struct{}{}, + }, + }, + }, + "lexruntimeservice": map[string]legacyJSONValues{ + "PostContentRequest": legacyJSONValues{ + Type: "structure", + StructMembers: map[string]struct{}{ + "requestAttributes": struct{}{}, + //"ActiveContexts": struct{}{}, - Disabled because JSON List + "sessionAttributes": struct{}{}, + }, + }, + "PostContentResponse": legacyJSONValues{ + Type: "structure", + StructMembers: map[string]struct{}{ + // "alternativeIntents": struct{}{}, - Disabled because JSON List + "sessionAttributes": struct{}{}, + "nluIntentConfidence": struct{}{}, + "slots": struct{}{}, + //"activeContexts": struct{}{}, - Disabled because JSON List + }, + }, + "PutSessionResponse": legacyJSONValues{ + Type: "structure", + StructMembers: map[string]struct{}{ + // "activeContexts": struct{}{}, - Disabled because JSON List + "slots": struct{}{}, + "sessionAttributes": struct{}{}, + }, + }, + }, + "lookoutequipment": map[string]legacyJSONValues{ + "DatasetSchema": legacyJSONValues{ + Type: "structure", + StructMembers: map[string]struct{}{ + "InlineDataSchema": struct{}{}, + }, + }, + "DescribeDatasetResponse": legacyJSONValues{ + Type: "structure", + StructMembers: map[string]struct{}{ + "Schema": struct{}{}, + }, + }, + "DescribeModelResponse": legacyJSONValues{ + Type: "structure", + StructMembers: map[string]struct{}{ + "Schema": struct{}{}, + "ModelMetrics": struct{}{}, + }, + }, + }, + "networkmanager": map[string]legacyJSONValues{ + "CoreNetworkPolicy": legacyJSONValues{ + Type: "structure", + StructMembers: map[string]struct{}{ + "PolicyDocument": struct{}{}, + }, + }, + "GetResourcePolicyResponse": legacyJSONValues{ + Type: "structure", + StructMembers: map[string]struct{}{ + "PolicyDocument": struct{}{}, + }, + }, + "PutCoreNetworkPolicyRequest": legacyJSONValues{ + Type: "structure", + StructMembers: map[string]struct{}{ + "PolicyDocument": struct{}{}, + }, + }, + "PutResourcePolicyRequest": legacyJSONValues{ + Type: "structure", + StructMembers: map[string]struct{}{ + "PolicyDocument": struct{}{}, + }, + }, + }, + "personalizeevents": map[string]legacyJSONValues{ + "Event": legacyJSONValues{ + Type: "structure", + StructMembers: map[string]struct{}{ + "properties": struct{}{}, + }, + }, + "Item": legacyJSONValues{ + Type: "structure", + StructMembers: map[string]struct{}{ + "properties": struct{}{}, + }, + }, + "User": legacyJSONValues{ + Type: "structure", + StructMembers: map[string]struct{}{ + "properties": struct{}{}, + }, + }, + }, + "pricing": map[string]legacyJSONValues{ + "PriceList": legacyJSONValues{ + Type: "list", + ListMemberRef: true, + }, + }, + "rekognition": map[string]legacyJSONValues{ + "HumanLoopActivationOutput": legacyJSONValues{ + Type: "structure", + StructMembers: map[string]struct{}{ + "HumanLoopActivationConditionsEvaluationResults": struct{}{}, + }, + }, + }, + "sagemaker": map[string]legacyJSONValues{ + "HumanLoopActivationConditionsConfig": legacyJSONValues{ + Type: "structure", + StructMembers: map[string]struct{}{ + "HumanLoopActivationConditions": struct{}{}, + }, + }, + }, + "schemas": map[string]legacyJSONValues{ + "GetResourcePolicyResponse": legacyJSONValues{ + Type: "structure", + StructMembers: map[string]struct{}{ + "Policy": struct{}{}, + }, + }, + "PutResourcePolicyRequest": legacyJSONValues{ + Type: "structure", + StructMembers: map[string]struct{}{ + "Policy": struct{}{}, + }, + }, + "PutResourcePolicyResponse": legacyJSONValues{ + Type: "structure", + StructMembers: map[string]struct{}{ + "Policy": struct{}{}, + }, + }, + "GetResourcePolicyOutput": legacyJSONValues{ + Type: "structure", + StructMembers: map[string]struct{}{ + "Policy": struct{}{}, + }, + }, + "PutResourcePolicyInput": legacyJSONValues{ + Type: "structure", + StructMembers: map[string]struct{}{ + "Policy": struct{}{}, + }, + }, + "PutResourcePolicyOutput": legacyJSONValues{ + Type: "structure", + StructMembers: map[string]struct{}{ + "Policy": struct{}{}, + }, + }, + }, + "textract": map[string]legacyJSONValues{ + "HumanLoopActivationOutput": legacyJSONValues{ + Type: "structure", + StructMembers: map[string]struct{}{ + "HumanLoopActivationConditionsEvaluationResults": struct{}{}, + }, + }, + }, +} diff --git a/private/model/api/load.go b/private/model/api/load.go index 7b7d4ad2bb4..dca116bf5cb 100644 --- a/private/model/api/load.go +++ b/private/model/api/load.go @@ -203,6 +203,11 @@ func (a *API) Setup() error { if err := a.validateNoDocumentShapes(); err != nil { return err } + if !a.NoRemoveUnsupportedJSONValue { + if err := removeUnsupportedJSONValue(a); err != nil { + return fmt.Errorf("failed to remove unsupported JSONValue from API, %v", err) + } + } a.setServiceAliaseName() a.setMetadataEndpointsKey() a.writeShapeNames() diff --git a/private/model/api/passes.go b/private/model/api/passes.go index c60921a1d15..6a5b579d16c 100644 --- a/private/model/api/passes.go +++ b/private/model/api/passes.go @@ -151,16 +151,8 @@ func (r *referenceResolver) resolveReference(ref *ShapeRef) { } if ref.JSONValue { - ref.ShapeName = "JSONValue" - if _, ok := r.API.Shapes[ref.ShapeName]; !ok { - r.API.Shapes[ref.ShapeName] = &Shape{ - API: r.API, - ShapeName: "JSONValue", - Type: "jsonvalue", - ValueRef: ShapeRef{ - JSONValue: true, - }, - } + if err := setTargetAsJSONValue(r.API, "", "", ref); err != nil { + panic(fmt.Sprintf("failed to set reference as JSONValue, %v", err)) } } @@ -178,6 +170,21 @@ func (r *referenceResolver) resolveReference(ref *ShapeRef) { r.resolveShape(shape) } +func setTargetAsJSONValue(a *API, parentName, refName string, ref *ShapeRef) error { + ref.ShapeName = "JSONValue" + if _, ok := a.Shapes[ref.ShapeName]; !ok { + a.Shapes[ref.ShapeName] = &Shape{ + API: a, + ShapeName: "JSONValue", + Type: "jsonvalue", + ValueRef: ShapeRef{ + JSONValue: true, + }, + } + } + return nil +} + // resolveShape resolves a shape's Member Key Value, and nested member // shape references. func (r *referenceResolver) resolveShape(shape *Shape) { diff --git a/private/model/api/shape.go b/private/model/api/shape.go index d8d0b2354c0..f4652864852 100644 --- a/private/model/api/shape.go +++ b/private/model/api/shape.go @@ -70,6 +70,9 @@ type ShapeRef struct { // Flag whether the member reference is a Account ID when endpoint shape ARN is present AccountIDMemberWithARN bool + + // Flags that the member was modeled as JSONValue but suppressed by the SDK. + SuppressedJSONValue bool `json:"-"` } // A Shape defines the definition of a shape type @@ -468,12 +471,16 @@ func (s ShapeTags) String() string { func (ref *ShapeRef) GoTags(toplevel bool, isRequired bool) string { tags := append(ShapeTags{}, ref.CustomTags...) + var location string if ref.Location != "" { tags = append(tags, ShapeTag{"location", ref.Location}) + location = ref.Location } else if ref.Shape.Location != "" { tags = append(tags, ShapeTag{"location", ref.Shape.Location}) + location = ref.Shape.Location } else if ref.IsEventHeader { tags = append(tags, ShapeTag{"location", "header"}) + location = "header" } if ref.LocationName != "" { @@ -517,6 +524,10 @@ func (ref *ShapeRef) GoTags(toplevel bool, isRequired bool) string { }) } } + // Value that is encoded as a header that needs to be base64 encoded + if ref.SuppressedJSONValue && location == "header" { + tags = append(tags, ShapeTag{"suppressedJSONValue", "true"}) + } if ref.Shape.Flattened || ref.Flattened { tags = append(tags, ShapeTag{"flattened", "true"}) diff --git a/private/model/cli/gen-protocol-tests/main.go b/private/model/cli/gen-protocol-tests/main.go index 7c025654f0d..fcaee3e8ac3 100644 --- a/private/model/cli/gen-protocol-tests/main.go +++ b/private/model/cli/gen-protocol-tests/main.go @@ -449,9 +449,10 @@ func generateTestSuite(filename string) string { } suite.Type = getType(inout) - suite.API.NoInitMethods = true // don't generate init methods - suite.API.NoStringerMethods = true // don't generate stringer methods - suite.API.NoConstServiceNames = true // don't generate service names + suite.API.NoInitMethods = true // don't generate init methods + suite.API.NoStringerMethods = true // don't generate stringer methods + suite.API.NoConstServiceNames = true // don't generate service names + suite.API.NoRemoveUnsupportedJSONValue = true // don't remove JSON values suite.API.Setup() suite.API.Metadata.EndpointPrefix = suite.API.PackageName() suite.API.Metadata.EndpointsID = suite.API.Metadata.EndpointPrefix diff --git a/private/protocol/rest/build.go b/private/protocol/rest/build.go index fb35fee5fe7..5f13d4acb87 100644 --- a/private/protocol/rest/build.go +++ b/private/protocol/rest/build.go @@ -272,6 +272,9 @@ func convertType(v reflect.Value, tag reflect.StructTag) (str string, err error) switch value := v.Interface().(type) { case string: + if tag.Get("suppressedJSONValue") == "true" && tag.Get("location") == "header" { + value = base64.StdEncoding.EncodeToString([]byte(value)) + } str = value case []byte: str = base64.StdEncoding.EncodeToString(value) @@ -306,5 +309,6 @@ func convertType(v reflect.Value, tag reflect.StructTag) (str string, err error) err := fmt.Errorf("unsupported value for param %v (%s)", v.Interface(), v.Type()) return "", err } + return str, nil } diff --git a/private/protocol/rest/unmarshal.go b/private/protocol/rest/unmarshal.go index c26fbfa5aed..cdef403e219 100644 --- a/private/protocol/rest/unmarshal.go +++ b/private/protocol/rest/unmarshal.go @@ -204,6 +204,13 @@ func unmarshalHeader(v reflect.Value, header string, tag reflect.StructTag) erro switch v.Interface().(type) { case *string: + if tag.Get("suppressedJSONValue") == "true" && tag.Get("location") == "header" { + b, err := base64.StdEncoding.DecodeString(header) + if err != nil { + return fmt.Errorf("failed to decode JSONValue, %v", err) + } + header = string(b) + } v.Set(reflect.ValueOf(&header)) case []byte: b, err := base64.StdEncoding.DecodeString(header) diff --git a/service/lexruntimeservice/api.go b/service/lexruntimeservice/api.go index cc74082b9f2..f5660c05d44 100644 --- a/service/lexruntimeservice/api.go +++ b/service/lexruntimeservice/api.go @@ -2180,7 +2180,11 @@ type PostContentInput struct { // If you don't specify a list of contexts, Amazon Lex will use the current // list of contexts for the session. If you specify an empty list, all contexts // for the session are cleared. - ActiveContexts aws.JSONValue `location:"header" locationName:"x-amz-lex-active-contexts" type:"jsonvalue"` + // + // ActiveContexts is a sensitive parameter and its value will be + // replaced with "sensitive" in string returned by PostContentInput's + // String and GoString methods. + ActiveContexts *string `location:"header" locationName:"x-amz-lex-active-contexts" type:"string" suppressedJSONValue:"true" sensitive:"true"` // Alias of the Amazon Lex bot. // @@ -2334,8 +2338,8 @@ func (s *PostContentInput) SetAccept(v string) *PostContentInput { } // SetActiveContexts sets the ActiveContexts field's value. -func (s *PostContentInput) SetActiveContexts(v aws.JSONValue) *PostContentInput { - s.ActiveContexts = v +func (s *PostContentInput) SetActiveContexts(v string) *PostContentInput { + s.ActiveContexts = &v return s } @@ -2389,14 +2393,18 @@ type PostContentOutput struct { // // You can use a context to control the intents that can follow up an intent, // or to modify the operation of your application. - ActiveContexts aws.JSONValue `location:"header" locationName:"x-amz-lex-active-contexts" type:"jsonvalue"` + // + // ActiveContexts is a sensitive parameter and its value will be + // replaced with "sensitive" in string returned by PostContentOutput's + // String and GoString methods. + ActiveContexts *string `location:"header" locationName:"x-amz-lex-active-contexts" type:"string" suppressedJSONValue:"true" sensitive:"true"` // One to four alternative intents that may be applicable to the user's intent. // // Each alternative includes a score that indicates how confident Amazon Lex // is that the intent matches the user's intent. The intents are sorted by the // confidence score. - AlternativeIntents aws.JSONValue `location:"header" locationName:"x-amz-lex-alternative-intents" type:"jsonvalue"` + AlternativeIntents *string `location:"header" locationName:"x-amz-lex-alternative-intents" type:"string" suppressedJSONValue:"true"` // The prompt (or statement) to convey to the user. This is based on the bot // configuration and context. For example, if Amazon Lex did not understand @@ -2610,14 +2618,14 @@ func (s PostContentOutput) GoString() string { } // SetActiveContexts sets the ActiveContexts field's value. -func (s *PostContentOutput) SetActiveContexts(v aws.JSONValue) *PostContentOutput { - s.ActiveContexts = v +func (s *PostContentOutput) SetActiveContexts(v string) *PostContentOutput { + s.ActiveContexts = &v return s } // SetAlternativeIntents sets the AlternativeIntents field's value. -func (s *PostContentOutput) SetAlternativeIntents(v aws.JSONValue) *PostContentOutput { - s.AlternativeIntents = v +func (s *PostContentOutput) SetAlternativeIntents(v string) *PostContentOutput { + s.AlternativeIntents = &v return s } @@ -3422,7 +3430,11 @@ type PutSessionOutput struct { _ struct{} `type:"structure" payload:"AudioStream"` // A list of active contexts for the session. - ActiveContexts aws.JSONValue `location:"header" locationName:"x-amz-lex-active-contexts" type:"jsonvalue"` + // + // ActiveContexts is a sensitive parameter and its value will be + // replaced with "sensitive" in string returned by PutSessionOutput's + // String and GoString methods. + ActiveContexts *string `location:"header" locationName:"x-amz-lex-active-contexts" type:"string" suppressedJSONValue:"true" sensitive:"true"` // The audio version of the message to convey to the user. AudioStream io.ReadCloser `locationName:"audioStream" type:"blob"` @@ -3531,8 +3543,8 @@ func (s PutSessionOutput) GoString() string { } // SetActiveContexts sets the ActiveContexts field's value. -func (s *PutSessionOutput) SetActiveContexts(v aws.JSONValue) *PutSessionOutput { - s.ActiveContexts = v +func (s *PutSessionOutput) SetActiveContexts(v string) *PutSessionOutput { + s.ActiveContexts = &v return s } diff --git a/service/lexruntimeservice/cust_jsonvalue_test.go b/service/lexruntimeservice/cust_jsonvalue_test.go new file mode 100644 index 00000000000..dbfa390a2f2 --- /dev/null +++ b/service/lexruntimeservice/cust_jsonvalue_test.go @@ -0,0 +1,65 @@ +//go:build go1.7 +// +build go1.7 + +package lexruntimeservice_test + +import ( + "context" + "encoding/base64" + "io/ioutil" + "net/http" + "strings" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/request" + "github.com/aws/aws-sdk-go/awstesting/unit" + "github.com/aws/aws-sdk-go/service/lexruntimeservice" +) + +func TestLexRunTimeService_suppressedJSONValue(t *testing.T) { + sess := unit.Session.Copy() + + client := lexruntimeservice.New(sess, &aws.Config{ + DisableParamValidation: aws.Bool(true), + }) + expectBase64ActiveContexts := base64.StdEncoding.EncodeToString([]byte(`{}`)) + + var actualInputActiveContexts string + result, err := client.PostContentWithContext(context.Background(), + &lexruntimeservice.PostContentInput{ + ActiveContexts: aws.String(`{}`), + }, + func(r *request.Request) { + r.Handlers.Send.Clear() + r.Handlers.Send.PushBack(func(r *request.Request) { + actualInputActiveContexts = r.HTTPRequest.Header.Get("x-amz-lex-active-contexts") + r.HTTPResponse = &http.Response{ + StatusCode: 200, + Header: func() http.Header { + h := http.Header{} + h.Set("x-amz-lex-active-contexts", expectBase64ActiveContexts) + return h + }(), + ContentLength: 2, + Body: ioutil.NopCloser(strings.NewReader(`{}`)), + } + }) + }, + ) + if err != nil { + t.Fatalf("failed to invoke operation, %v", err) + } + + if e, a := expectBase64ActiveContexts, actualInputActiveContexts; e != a { + t.Errorf("expect %v input active contexts, got %v", e, a) + } + + if result.ActiveContexts == nil { + t.Errorf("expect active contexts, got none") + } + if e, a := `{}`, *result.ActiveContexts; e != a { + t.Errorf("expect %v output active contexts, got %v", e, a) + } + +}