diff --git a/smithy-aws-protocol-tests/model/awsJson1_0/empty-input-output.smithy b/smithy-aws-protocol-tests/model/awsJson1_0/empty-input-output.smithy index 2e5eb3e7e0b..cc98fe692d1 100644 --- a/smithy-aws-protocol-tests/model/awsJson1_0/empty-input-output.smithy +++ b/smithy-aws-protocol-tests/model/awsJson1_0/empty-input-output.smithy @@ -16,8 +16,14 @@ operation NoInputAndNoOutput {} apply NoInputAndNoOutput @httpRequestTests([ { - id: "AwsJson10NoInputAndNoOutput", - documentation: "No input serializes no payload", + id: "AwsJson10MustAlwaysSendEmptyJsonPayload", + documentation: """ + Clients must always send an empty JSON object payload for + operations with no input (that is, `{}`). While AWS service + implementations support requests with no payload or requests + that send `{}`, always sending `{}` from the client is + preferred for forward compatibility in case input is ever + added to an operation.""", protocol: awsJson1_0, method: "POST", headers: { @@ -25,19 +31,82 @@ apply NoInputAndNoOutput @httpRequestTests([ "X-Amz-Target": "JsonRpc10.NoInputAndNoOutput", }, uri: "/", - } + body: "{}", + bodyMediaType: "application/json" + }, + { + id: "AwsJson10ServiceSupportsNoPayloadForNoInput", + documentation: """ + Service implementations must support no payload or an empty + object payload for operations that define no input. However, + despite the lack of a payload, a Content-Type header is still + required in order for the service to properly detect the + protocol.""", + protocol: awsJson1_0, + method: "POST", + headers: { + "Content-Type": "application/x-amz-json-1.0", + "X-Amz-Target": "JsonRpc10.NoInputAndNoOutput", + }, + uri: "/", + body: "", + appliesTo: "server" + }, ]) apply NoInputAndNoOutput @httpResponseTests([ - { - id: "AwsJson10NoInputAndNoOutput", - documentation: "No output serializes no payload", + { + id: "AwsJson10HandlesEmptyOutputShape", protocol: awsJson1_0, + documentation: """ + When no output is defined, the service is expected to return + an empty payload, however, client must ignore a JSON payload + if one is returned. This ensures that if output is added later, + then it will not break the client.""", + body: "{}", + bodyMediaType: "application/json", headers: { "Content-Type": "application/x-amz-json-1.0", }, code: 200, - } + // Service implementations must ignore this test. + appliesTo: "client" + }, + { + id: "AwsJson10HandlesUnexpectedJsonOutput", + protocol: awsJson1_0, + documentation: """ + This client-only test builds on handles_empty_output_shape, + by including unexpected fields in the JSON. A client + needs to ignore JSON output that is empty or that contains + JSON object data.""", + body: """ + { + "foo": true + }""", + bodyMediaType: "application/json", + headers: { + "Content-Type": "application/x-amz-json-1.0", + }, + code: 200, + // Service implementations must ignore this test. + appliesTo: "client" + }, + { + id: "AwsJson10ServiceRespondsWithNoPayload", + protocol: awsJson1_0, + documentation: """ + When no output is defined, the service is expected to return + an empty payload. Despite the lack of a payload, the service + is expected to always send a Content-Type header. Clients must + handle cases where a service returns a JSON object and where + a service returns no JSON at all.""", + body: "", + headers: { + "Content-Type": "application/x-amz-json-1.0", + }, + code: 200 + } ]) /// The example tests how requests and responses are serialized when there's @@ -51,26 +120,30 @@ operation NoInputAndOutput { apply NoInputAndOutput @httpRequestTests([ { id: "AwsJson10NoInputAndOutput", - documentation: "No input serializes no payload", + documentation: "A client should always send and empty JSON object payload.", protocol: awsJson1_0, method: "POST", headers: { "Content-Type": "application/x-amz-json-1.0", "X-Amz-Target": "JsonRpc10.NoInputAndOutput", }, - uri: "/" + uri: "/", + body: "{}", + bodyMediaType: "application/json", } ]) apply NoInputAndOutput @httpResponseTests([ { id: "AwsJson10NoInputAndOutput", - documentation: "Empty output serializes no payload", + documentation: "Empty output always serializes an empty object payload.", protocol: awsJson1_0, headers: { "Content-Type": "application/x-amz-json-1.0" }, - code: 200 + code: 200, + body: "{}", + bodyMediaType: "application/json", } ]) @@ -88,37 +161,30 @@ operation EmptyInputAndEmptyOutput { apply EmptyInputAndEmptyOutput @httpRequestTests([ { id: "AwsJson10EmptyInputAndEmptyOutput", - documentation: "Empty input serializes no payload", + documentation: "Clients must always send an empty object if input is modeled.", protocol: awsJson1_0, method: "POST", uri: "/", - body: "", + body: "{}", + bodyMediaType: "application/json", headers: { "Content-Type": "application/x-amz-json-1.0", "X-Amz-Target": "JsonRpc10.EmptyInputAndEmptyOutput", - }, - bodyMediaType: "application/json" + } }, ]) apply EmptyInputAndEmptyOutput @httpResponseTests([ { - id: "AwsJson10EmptyInputAndEmptyOutput", - documentation: "Empty output serializes no payload", - protocol: awsJson1_0, - code: 200, - body: "", - headers: {"Content-Type": "application/x-amz-json-1.0"}, - bodyMediaType: "application/json" - }, - { - id: "AwsJson10EmptyInputAndEmptyJsonObjectOutput", - documentation: "Empty output serializes no payload", + id: "AwsJson10EmptyInputAndEmptyOutputSendJsonObject", + documentation: "A service will always return a JSON object for operations with modeled output.", protocol: awsJson1_0, code: 200, body: "{}", - headers: {"Content-Type": "application/x-amz-json-1.0"}, - bodyMediaType: "application/json" + bodyMediaType: "application/json", + headers: { + "Content-Type": "application/x-amz-json-1.0" + }, }, ]) diff --git a/smithy-aws-protocol-tests/model/awsJson1_1/empty-operation.smithy b/smithy-aws-protocol-tests/model/awsJson1_1/empty-operation.smithy index fbfb1f1ce1b..97130511673 100644 --- a/smithy-aws-protocol-tests/model/awsJson1_1/empty-operation.smithy +++ b/smithy-aws-protocol-tests/model/awsJson1_1/empty-operation.smithy @@ -25,18 +25,95 @@ use smithy.test#httpResponseTests method: "POST", uri: "/", }, + { + id: "json_1_1_client_sends_empty_payload_for_no_input_shape", + protocol: awsJson1_1, + documentation: """ + Clients must always send an empty JSON object payload for + operations with no input (that is, `{}`). While AWS service + implementations support requests with no payload or requests + that send `{}`, always sending `{}` from the client is + preferred for forward compatibility in case input is ever + added to an operation.""", + body: "{}", + bodyMediaType: "application/json", + headers: { + "Content-Type": "application/x-amz-json-1.1", + }, + method: "POST", + uri: "/" + }, + { + id: "json_1_1_service_supports_empty_payload_for_no_input_shape", + protocol: awsJson1_1, + documentation: """ + Service implementations must support no payload or an empty + object payload for operations that define no input. However, + despite the lack of a payload, a Content-Type header is still + required in order for the service to properly detect the + protocol.""", + body: "", + headers: { + "Content-Type": "application/x-amz-json-1.1", + }, + method: "POST", + uri: "/", + // Client implementations must ignore this test. + appliesTo: "server" + }, ]) @httpResponseTests([ { id: "handles_empty_output_shape", protocol: awsJson1_1, - documentation: "Handles empty output shapes", + documentation: """ + When no output is defined, the service is expected to return + an empty payload, however, client must ignore a JSON payload + if one is returned. This ensures that if output is added later, + then it will not break the client.""", body: "{}", bodyMediaType: "application/json", headers: { "Content-Type": "application/x-amz-json-1.1", }, code: 200, + // Service implementations must ignore this test. + appliesTo: "client" + }, + { + id: "handles_unexpected_json_output", + protocol: awsJson1_1, + documentation: """ + This client-only test builds on handles_empty_output_shape, + by including unexpected fields in the JSON. A client + needs to ignore JSON output that is empty or that contains + JSON object data.""", + body: """ + { + "foo": true + }""", + bodyMediaType: "application/json", + headers: { + "Content-Type": "application/x-amz-json-1.1", + }, + code: 200, + // Service implementations must ignore this test. + appliesTo: "client" + }, + { + id: "json_1_1_service_responds_with_no_payload", + protocol: awsJson1_1, + documentation: """ + When no output is defined, the service is expected to return + an empty payload. Despite the lack of a payload, the service + is expected to always send a Content-Type header. Clients must + handle cases where a service returns a JSON object and where + a service returns no JSON at all.""", + body: "", + headers: { + "Content-Type": "application/x-amz-json-1.1", + }, + code: 200 }, ]) operation EmptyOperation {} diff --git a/smithy-aws-protocol-tests/model/restJson1/empty-input-output.smithy b/smithy-aws-protocol-tests/model/restJson1/empty-input-output.smithy index 161707ec48a..6fc537a60b6 100644 --- a/smithy-aws-protocol-tests/model/restJson1/empty-input-output.smithy +++ b/smithy-aws-protocol-tests/model/restJson1/empty-input-output.smithy @@ -20,19 +20,27 @@ operation NoInputAndNoOutput {} apply NoInputAndNoOutput @httpRequestTests([ { id: "RestJsonNoInputAndNoOutput", - documentation: "No input serializes no payload", + documentation: """ + No input serializes no payload. When clients do not need to + serialize any data in the payload, they should omit a payload + altogether.""", protocol: restJson1, method: "POST", - uri: "/NoInputAndNoOutput" + uri: "/NoInputAndNoOutput", + body: "" } ]) apply NoInputAndNoOutput @httpResponseTests([ { id: "RestJsonNoInputAndNoOutput", - documentation: "No output serializes no payload", + documentation: """ + When an operation does not define output, the service will respond + with an empty payload, and may optionally include the content-type + header.""", protocol: restJson1, - code: 200 + code: 200, + body: "" } ]) @@ -48,20 +56,42 @@ operation NoInputAndOutput { apply NoInputAndOutput @httpRequestTests([ { id: "RestJsonNoInputAndOutput", - documentation: "No input serializes no payload", + documentation: """ + No input serializes no payload. When clients do not need to + serialize any data in the payload, they should omit a payload + altogether.""", protocol: restJson1, method: "POST", - uri: "/NoInputAndOutputOutput" + uri: "/NoInputAndOutputOutput", + body: "" } ]) apply NoInputAndOutput @httpResponseTests([ { - id: "RestJsonNoInputAndOutput", - documentation: "Empty output serializes no payload", + id: "RestJsonNoInputAndOutputWithJson", + documentation: """ + Operations that define output and do not bind anything to + the payload return a JSON object in the response.""", protocol: restJson1, - code: 200 - } + code: 200, + body: "{}", + bodyMediaType: "application/json", + headers: { + "Content-Type": "application/json" + }, + }, + { + id: "RestJsonNoInputAndOutputNoPayload", + documentation: """ + This test is similar to RestJsonNoInputAndOutputWithJson, but + it ensures that clients can gracefully handle responses that + omit a JSON payload.""", + protocol: restJson1, + code: 200, + body: "", + appliesTo: "client", + } ]) structure NoInputAndOutputOutput {} @@ -79,31 +109,56 @@ operation EmptyInputAndEmptyOutput { apply EmptyInputAndEmptyOutput @httpRequestTests([ { id: "RestJsonEmptyInputAndEmptyOutput", - documentation: "Empty input serializes no payload", + documentation: """ + Clients should not serialize a JSON payload when no parameters + are given that are sent in the body. A service will tolerate + clients that omit a payload or that send a JSON object.""", protocol: restJson1, method: "POST", uri: "/EmptyInputAndEmptyOutput", body: "", - bodyMediaType: "application/json" + }, + { + id: "RestJsonEmptyInputAndEmptyOutputWithJson", + documentation: """ + Similar to RestJsonEmptyInputAndEmptyOutput, but ensures that + services gracefully handles receiving a JSON object.""", + protocol: restJson1, + method: "POST", + uri: "/EmptyInputAndEmptyOutput", + headers: { + "Content-Type": "application/json", + }, + body: "{}", + bodyMediaType: "application/json", + appliesTo: "server", }, ]) apply EmptyInputAndEmptyOutput @httpResponseTests([ { id: "RestJsonEmptyInputAndEmptyOutput", - documentation: "Empty output serializes no payload", + documentation: """ + As of January 2021, server implementations are expected to + respond with a JSON object regardless of if the output + parameters are empty.""", protocol: restJson1, code: 200, - body: "", - bodyMediaType: "application/json" + headers: { + "Content-Type": "application/json", + }, + body: "{}", + bodyMediaType: "application/json", }, { - id: "RestJsonEmptyInputAndEmptyJsonObjectOutput", - documentation: "Empty output serializes no payload", + id: "RestJsonEmptyInputAndEmptyOutputJsonObjectOutput", + documentation: """ + This test ensures that clients can gracefully handle + situations where a service omits a JSON payload entirely.""", protocol: restJson1, code: 200, - body: "{}", - bodyMediaType: "application/json" + body: "", + appliesTo: "client", }, ]) diff --git a/smithy-aws-protocol-tests/model/restJson1/errors.smithy b/smithy-aws-protocol-tests/model/restJson1/errors.smithy index f6498153b36..84723c39f32 100644 --- a/smithy-aws-protocol-tests/model/restJson1/errors.smithy +++ b/smithy-aws-protocol-tests/model/restJson1/errors.smithy @@ -28,22 +28,40 @@ operation GreetingWithErrors { apply GreetingWithErrors @httpResponseTests([ { id: "RestJsonGreetingWithErrors", - documentation: "Ensures that operations with errors successfully know how to deserialize the successful response", + documentation: """ + Ensures that operations with errors successfully know how + to deserialize a successful response. As of January 2021, + server implementations are expected to respond with a + JSON object regardless of if the output parameters are + empty.""", protocol: restJson1, code: 200, - body: """ - { - "greeting": "Hello" - }""", + body: "{}", bodyMediaType: "application/json", headers: { - "Content-Type": "application/json", "X-Greeting": "Hello", }, params: { greeting: "Hello" } - } + }, + { + id: "RestJsonGreetingWithErrorsNoPayload", + documentation: """ + This test is similar to RestJsonGreetingWithErrors, but it + ensures that clients can gracefully deal with a server + omitting a response payload.""", + protocol: restJson1, + code: 200, + body: "", + headers: { + "X-Greeting": "Hello", + }, + params: { + greeting: "Hello" + }, + appliesTo: "client" + }, ]) structure GreetingWithErrorsOutput { diff --git a/smithy-aws-protocol-tests/model/restJson1/http-payload.smithy b/smithy-aws-protocol-tests/model/restJson1/http-payload.smithy index a21ae653160..cd0973e9a8c 100644 --- a/smithy-aws-protocol-tests/model/restJson1/http-payload.smithy +++ b/smithy-aws-protocol-tests/model/restJson1/http-payload.smithy @@ -29,6 +29,7 @@ apply HttpPayloadTraits @httpRequestTests([ uri: "/HttpPayloadTraits", body: "blobby blob blob", headers: { + "Content-Type": "application/octet-stream", "X-Foo": "Foo" }, requireHeaders: [ diff --git a/smithy-aws-protocol-tests/model/restJson1/http-query.smithy b/smithy-aws-protocol-tests/model/restJson1/http-query.smithy index aa650764ced..d03a2441da2 100644 --- a/smithy-aws-protocol-tests/model/restJson1/http-query.smithy +++ b/smithy-aws-protocol-tests/model/restJson1/http-query.smithy @@ -246,17 +246,32 @@ operation IgnoreQueryParamsInResponse { apply IgnoreQueryParamsInResponse @httpResponseTests([ { id: "RestJsonIgnoreQueryParamsInResponse", - documentation: "Query parameters must be ignored when serializing the output of an operation", + documentation: """ + Query parameters must be ignored when serializing the output + of an operation. As of January 2021, server implementations + are expected to respond with a JSON object regardless of + if the output parameters are empty.""", protocol: restJson1, code: 200, headers: { "Content-Type": "application/json" }, + body: "{}", + bodyMediaType: "application/json", + params: {} + }, + { + id: "RestJsonIgnoreQueryParamsInResponseNoPayload", + documentation: """ + This test is similar to RestJsonIgnoreQueryParamsInResponse, + but it ensures that clients gracefully handle responses from + the server that do not serialize an empty JSON object.""", + protocol: restJson1, + code: 200, body: "", - bodyMediaType: "json", - params: { - } - } + params: {}, + appliesTo: "client", + }, ]) structure IgnoreQueryParamsInResponseOutput { diff --git a/smithy-aws-protocol-tests/model/restJson1/http-response-code.smithy b/smithy-aws-protocol-tests/model/restJson1/http-response-code.smithy index 476ed90de4d..bc69b9031a9 100644 --- a/smithy-aws-protocol-tests/model/restJson1/http-response-code.smithy +++ b/smithy-aws-protocol-tests/model/restJson1/http-response-code.smithy @@ -22,16 +22,35 @@ structure HttpResponseCodeOutput { apply HttpResponseCode @httpResponseTests([ { id: "RestJsonHttpResponseCode", - documentation: "Binds the http response code to an output structure.", + documentation: """ + Binds the http response code to an output structure. Note that + even though all members are bound outside of the payload, an + empty JSON object is serialized in the response. However, + clients should be able to handle an empty JSON object or an + empty payload without failing to deserialize a response.""", protocol: restJson1, code: 201, headers: { - "Content-Type": "application/json" + "Content-Type": "application/json", }, - "body": "", - "bodyMediaType": "json", + body: "{}", + bodyMediaType: "application/json", params: { Status: 201, } - } + }, + { + id: "RestJsonHttpResponseCodeWithNoPayload", + documentation: """ + This test ensures that clients gracefully handle cases where + the service responds with no payload rather than an empty JSON + object.""", + protocol: restJson1, + code: 201, + body: "", + params: { + Status: 201, + }, + appliesTo: "client" + }, ])