diff --git a/codegen/build.gradle.kts b/codegen/build.gradle.kts index 56582681b218..92481cdc64e0 100644 --- a/codegen/build.gradle.kts +++ b/codegen/build.gradle.kts @@ -31,7 +31,7 @@ allprojects { version = "0.7.0" } -extra["smithyVersion"] = "[1.12.0,1.13.0[" +extra["smithyVersion"] = "[1.14.0,1.15.0[" // The root project doesn't produce a JAR. tasks["jar"].enabled = false diff --git a/codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AwsProtocolUtils.java b/codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AwsProtocolUtils.java index 0b5a401713a6..d21486584563 100644 --- a/codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AwsProtocolUtils.java +++ b/codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AwsProtocolUtils.java @@ -301,6 +301,14 @@ private static boolean filterProtocolTests( if (testCase.getId().equals("RestJsonNoInputAndOutput")) { return true; } + // TODO: Remove when server protocol tests are fixed in + // https://github.com/aws/aws-sdk-js-v3/issues/3058 + // TODO: Move to filter specific to server protocol tests if added in + // https://github.com/awslabs/smithy-typescript/issues/470 + if (testCase.getId().equals("RestJsonTestPayloadStructure") + || testCase.getId().equals("RestJsonHttpWithHeadersButNoPayload")) { + return true; + } return false; } @@ -331,11 +339,6 @@ private static boolean filterMalformedRequestTests( if (testCase.getId().equals("RestJsonMalformedPatternReDOSString")) { return true; } - //TODO: broken in Smithy 1.12.0, remove after next Smithy release - if (testCase.getId().equals("RestJsonMalformedLengthBlobOverride_case1") - || testCase.getId().equals("RestJsonMalformedRequiredQueryNoValue")) { - return true; - } return false; } diff --git a/private/aws-protocoltests-restjson/src/RestJsonProtocol.ts b/private/aws-protocoltests-restjson/src/RestJsonProtocol.ts index d1dbf27965a8..9b686b583138 100644 --- a/private/aws-protocoltests-restjson/src/RestJsonProtocol.ts +++ b/private/aws-protocoltests-restjson/src/RestJsonProtocol.ts @@ -370,6 +370,26 @@ import { StreamingTraitsWithMediaTypeCommandInput, StreamingTraitsWithMediaTypeCommandOutput, } from "./commands/StreamingTraitsWithMediaTypeCommand"; +import { + TestBodyStructureCommand, + TestBodyStructureCommandInput, + TestBodyStructureCommandOutput, +} from "./commands/TestBodyStructureCommand"; +import { + TestNoPayloadCommand, + TestNoPayloadCommandInput, + TestNoPayloadCommandOutput, +} from "./commands/TestNoPayloadCommand"; +import { + TestPayloadBlobCommand, + TestPayloadBlobCommandInput, + TestPayloadBlobCommandOutput, +} from "./commands/TestPayloadBlobCommand"; +import { + TestPayloadStructureCommand, + TestPayloadStructureCommandInput, + TestPayloadStructureCommandOutput, +} from "./commands/TestPayloadStructureCommand"; import { TimestampFormatHeadersCommand, TimestampFormatHeadersCommandInput, @@ -2762,6 +2782,155 @@ export class RestJsonProtocol extends RestJsonProtocolClient { } } + /** + * This example operation serializes a structure in the HTTP body. + * + * It should ensure Content-Type: application/json is + * used in all requests and that an "empty" body is + * an empty JSON document ({}). + * + */ + public testBodyStructure( + args: TestBodyStructureCommandInput, + options?: __HttpHandlerOptions + ): Promise; + public testBodyStructure( + args: TestBodyStructureCommandInput, + cb: (err: any, data?: TestBodyStructureCommandOutput) => void + ): void; + public testBodyStructure( + args: TestBodyStructureCommandInput, + options: __HttpHandlerOptions, + cb: (err: any, data?: TestBodyStructureCommandOutput) => void + ): void; + public testBodyStructure( + args: TestBodyStructureCommandInput, + optionsOrCb?: __HttpHandlerOptions | ((err: any, data?: TestBodyStructureCommandOutput) => void), + cb?: (err: any, data?: TestBodyStructureCommandOutput) => void + ): Promise | void { + const command = new TestBodyStructureCommand(args); + if (typeof optionsOrCb === "function") { + this.send(command, optionsOrCb); + } else if (typeof cb === "function") { + if (typeof optionsOrCb !== "object") throw new Error(`Expect http options but get ${typeof optionsOrCb}`); + this.send(command, optionsOrCb || {}, cb); + } else { + return this.send(command, optionsOrCb); + } + } + + /** + * This example operation serializes a request without an HTTP body. + * + * These tests are to ensure we do not attach a body or related headers + * (Content-Length, Content-Type) to operations that semantically + * cannot produce an HTTP body. + * + */ + public testNoPayload( + args: TestNoPayloadCommandInput, + options?: __HttpHandlerOptions + ): Promise; + public testNoPayload( + args: TestNoPayloadCommandInput, + cb: (err: any, data?: TestNoPayloadCommandOutput) => void + ): void; + public testNoPayload( + args: TestNoPayloadCommandInput, + options: __HttpHandlerOptions, + cb: (err: any, data?: TestNoPayloadCommandOutput) => void + ): void; + public testNoPayload( + args: TestNoPayloadCommandInput, + optionsOrCb?: __HttpHandlerOptions | ((err: any, data?: TestNoPayloadCommandOutput) => void), + cb?: (err: any, data?: TestNoPayloadCommandOutput) => void + ): Promise | void { + const command = new TestNoPayloadCommand(args); + if (typeof optionsOrCb === "function") { + this.send(command, optionsOrCb); + } else if (typeof cb === "function") { + if (typeof optionsOrCb !== "object") throw new Error(`Expect http options but get ${typeof optionsOrCb}`); + this.send(command, optionsOrCb || {}, cb); + } else { + return this.send(command, optionsOrCb); + } + } + + /** + * This example operation serializes a payload targeting a blob. + * + * The Blob shape is not structured content and we cannot + * make assumptions about what data will be sent. This test ensures + * only a generic "Content-Type: application/octet-stream" header + * is used, and that we are not treating an empty body as an + * empty JSON document. + * + */ + public testPayloadBlob( + args: TestPayloadBlobCommandInput, + options?: __HttpHandlerOptions + ): Promise; + public testPayloadBlob( + args: TestPayloadBlobCommandInput, + cb: (err: any, data?: TestPayloadBlobCommandOutput) => void + ): void; + public testPayloadBlob( + args: TestPayloadBlobCommandInput, + options: __HttpHandlerOptions, + cb: (err: any, data?: TestPayloadBlobCommandOutput) => void + ): void; + public testPayloadBlob( + args: TestPayloadBlobCommandInput, + optionsOrCb?: __HttpHandlerOptions | ((err: any, data?: TestPayloadBlobCommandOutput) => void), + cb?: (err: any, data?: TestPayloadBlobCommandOutput) => void + ): Promise | void { + const command = new TestPayloadBlobCommand(args); + if (typeof optionsOrCb === "function") { + this.send(command, optionsOrCb); + } else if (typeof cb === "function") { + if (typeof optionsOrCb !== "object") throw new Error(`Expect http options but get ${typeof optionsOrCb}`); + this.send(command, optionsOrCb || {}, cb); + } else { + return this.send(command, optionsOrCb); + } + } + + /** + * This example operation serializes a payload targeting a structure. + * + * This enforces the same requirements as TestBodyStructure + * but with the body specified by the @httpPayload trait. + * + */ + public testPayloadStructure( + args: TestPayloadStructureCommandInput, + options?: __HttpHandlerOptions + ): Promise; + public testPayloadStructure( + args: TestPayloadStructureCommandInput, + cb: (err: any, data?: TestPayloadStructureCommandOutput) => void + ): void; + public testPayloadStructure( + args: TestPayloadStructureCommandInput, + options: __HttpHandlerOptions, + cb: (err: any, data?: TestPayloadStructureCommandOutput) => void + ): void; + public testPayloadStructure( + args: TestPayloadStructureCommandInput, + optionsOrCb?: __HttpHandlerOptions | ((err: any, data?: TestPayloadStructureCommandOutput) => void), + cb?: (err: any, data?: TestPayloadStructureCommandOutput) => void + ): Promise | void { + const command = new TestPayloadStructureCommand(args); + if (typeof optionsOrCb === "function") { + this.send(command, optionsOrCb); + } else if (typeof cb === "function") { + if (typeof optionsOrCb !== "object") throw new Error(`Expect http options but get ${typeof optionsOrCb}`); + this.send(command, optionsOrCb || {}, cb); + } else { + return this.send(command, optionsOrCb); + } + } + /** * This example tests how timestamp request and response headers are serialized. */ diff --git a/private/aws-protocoltests-restjson/src/RestJsonProtocolClient.ts b/private/aws-protocoltests-restjson/src/RestJsonProtocolClient.ts index 7526f599fad4..b23262bd9421 100644 --- a/private/aws-protocoltests-restjson/src/RestJsonProtocolClient.ts +++ b/private/aws-protocoltests-restjson/src/RestJsonProtocolClient.ts @@ -257,6 +257,13 @@ import { StreamingTraitsWithMediaTypeCommandInput, StreamingTraitsWithMediaTypeCommandOutput, } from "./commands/StreamingTraitsWithMediaTypeCommand"; +import { TestBodyStructureCommandInput, TestBodyStructureCommandOutput } from "./commands/TestBodyStructureCommand"; +import { TestNoPayloadCommandInput, TestNoPayloadCommandOutput } from "./commands/TestNoPayloadCommand"; +import { TestPayloadBlobCommandInput, TestPayloadBlobCommandOutput } from "./commands/TestPayloadBlobCommand"; +import { + TestPayloadStructureCommandInput, + TestPayloadStructureCommandOutput, +} from "./commands/TestPayloadStructureCommand"; import { TimestampFormatHeadersCommandInput, TimestampFormatHeadersCommandOutput, @@ -342,6 +349,10 @@ export type ServiceInputTypes = | StreamingTraitsCommandInput | StreamingTraitsRequireLengthCommandInput | StreamingTraitsWithMediaTypeCommandInput + | TestBodyStructureCommandInput + | TestNoPayloadCommandInput + | TestPayloadBlobCommandInput + | TestPayloadStructureCommandInput | TimestampFormatHeadersCommandInput; export type ServiceOutputTypes = @@ -423,6 +434,10 @@ export type ServiceOutputTypes = | StreamingTraitsCommandOutput | StreamingTraitsRequireLengthCommandOutput | StreamingTraitsWithMediaTypeCommandOutput + | TestBodyStructureCommandOutput + | TestNoPayloadCommandOutput + | TestPayloadBlobCommandOutput + | TestPayloadStructureCommandOutput | TimestampFormatHeadersCommandOutput; export interface ClientDefaults extends Partial<__SmithyResolvedConfiguration<__HttpHandlerOptions>> { diff --git a/private/aws-protocoltests-restjson/src/commands/TestBodyStructureCommand.ts b/private/aws-protocoltests-restjson/src/commands/TestBodyStructureCommand.ts new file mode 100644 index 000000000000..35af7a0854fe --- /dev/null +++ b/private/aws-protocoltests-restjson/src/commands/TestBodyStructureCommand.ts @@ -0,0 +1,100 @@ +import { getSerdePlugin } from "@aws-sdk/middleware-serde"; +import { HttpRequest as __HttpRequest, HttpResponse as __HttpResponse } from "@aws-sdk/protocol-http"; +import { Command as $Command } from "@aws-sdk/smithy-client"; +import { + FinalizeHandlerArguments, + Handler, + HandlerExecutionContext, + HttpHandlerOptions as __HttpHandlerOptions, + MetadataBearer as __MetadataBearer, + MiddlewareStack, + SerdeContext as __SerdeContext, +} from "@aws-sdk/types"; + +import { TestBodyStructureInputOutput } from "../models/models_0"; +import { + deserializeAws_restJson1TestBodyStructureCommand, + serializeAws_restJson1TestBodyStructureCommand, +} from "../protocols/Aws_restJson1"; +import { RestJsonProtocolClientResolvedConfig, ServiceInputTypes, ServiceOutputTypes } from "../RestJsonProtocolClient"; + +export interface TestBodyStructureCommandInput extends TestBodyStructureInputOutput {} +export interface TestBodyStructureCommandOutput extends TestBodyStructureInputOutput, __MetadataBearer {} + +/** + * This example operation serializes a structure in the HTTP body. + * + * It should ensure Content-Type: application/json is + * used in all requests and that an "empty" body is + * an empty JSON document ({}). + * + * @example + * Use a bare-bones client and the command you need to make an API call. + * ```javascript + * import { RestJsonProtocolClient, TestBodyStructureCommand } from "@aws-sdk/aws-protocoltests-restjson"; // ES Modules import + * // const { RestJsonProtocolClient, TestBodyStructureCommand } = require("@aws-sdk/aws-protocoltests-restjson"); // CommonJS import + * const client = new RestJsonProtocolClient(config); + * const command = new TestBodyStructureCommand(input); + * const response = await client.send(command); + * ``` + * + * @see {@link TestBodyStructureCommandInput} for command's `input` shape. + * @see {@link TestBodyStructureCommandOutput} for command's `response` shape. + * @see {@link RestJsonProtocolClientResolvedConfig | config} for RestJsonProtocolClient's `config` shape. + * + */ +export class TestBodyStructureCommand extends $Command< + TestBodyStructureCommandInput, + TestBodyStructureCommandOutput, + RestJsonProtocolClientResolvedConfig +> { + // Start section: command_properties + // End section: command_properties + + constructor(readonly input: TestBodyStructureCommandInput) { + // Start section: command_constructor + super(); + // End section: command_constructor + } + + /** + * @internal + */ + resolveMiddleware( + clientStack: MiddlewareStack, + configuration: RestJsonProtocolClientResolvedConfig, + options?: __HttpHandlerOptions + ): Handler { + this.middlewareStack.use(getSerdePlugin(configuration, this.serialize, this.deserialize)); + + const stack = clientStack.concat(this.middlewareStack); + + const { logger } = configuration; + const clientName = "RestJsonProtocolClient"; + const commandName = "TestBodyStructureCommand"; + const handlerExecutionContext: HandlerExecutionContext = { + logger, + clientName, + commandName, + inputFilterSensitiveLog: TestBodyStructureInputOutput.filterSensitiveLog, + outputFilterSensitiveLog: TestBodyStructureInputOutput.filterSensitiveLog, + }; + const { requestHandler } = configuration; + return stack.resolve( + (request: FinalizeHandlerArguments) => + requestHandler.handle(request.request as __HttpRequest, options || {}), + handlerExecutionContext + ); + } + + private serialize(input: TestBodyStructureCommandInput, context: __SerdeContext): Promise<__HttpRequest> { + return serializeAws_restJson1TestBodyStructureCommand(input, context); + } + + private deserialize(output: __HttpResponse, context: __SerdeContext): Promise { + return deserializeAws_restJson1TestBodyStructureCommand(output, context); + } + + // Start section: command_body_extra + // End section: command_body_extra +} diff --git a/private/aws-protocoltests-restjson/src/commands/TestNoPayloadCommand.ts b/private/aws-protocoltests-restjson/src/commands/TestNoPayloadCommand.ts new file mode 100644 index 000000000000..8de50fed889b --- /dev/null +++ b/private/aws-protocoltests-restjson/src/commands/TestNoPayloadCommand.ts @@ -0,0 +1,100 @@ +import { getSerdePlugin } from "@aws-sdk/middleware-serde"; +import { HttpRequest as __HttpRequest, HttpResponse as __HttpResponse } from "@aws-sdk/protocol-http"; +import { Command as $Command } from "@aws-sdk/smithy-client"; +import { + FinalizeHandlerArguments, + Handler, + HandlerExecutionContext, + HttpHandlerOptions as __HttpHandlerOptions, + MetadataBearer as __MetadataBearer, + MiddlewareStack, + SerdeContext as __SerdeContext, +} from "@aws-sdk/types"; + +import { TestNoPayloadInputOutput } from "../models/models_0"; +import { + deserializeAws_restJson1TestNoPayloadCommand, + serializeAws_restJson1TestNoPayloadCommand, +} from "../protocols/Aws_restJson1"; +import { RestJsonProtocolClientResolvedConfig, ServiceInputTypes, ServiceOutputTypes } from "../RestJsonProtocolClient"; + +export interface TestNoPayloadCommandInput extends TestNoPayloadInputOutput {} +export interface TestNoPayloadCommandOutput extends TestNoPayloadInputOutput, __MetadataBearer {} + +/** + * This example operation serializes a request without an HTTP body. + * + * These tests are to ensure we do not attach a body or related headers + * (Content-Length, Content-Type) to operations that semantically + * cannot produce an HTTP body. + * + * @example + * Use a bare-bones client and the command you need to make an API call. + * ```javascript + * import { RestJsonProtocolClient, TestNoPayloadCommand } from "@aws-sdk/aws-protocoltests-restjson"; // ES Modules import + * // const { RestJsonProtocolClient, TestNoPayloadCommand } = require("@aws-sdk/aws-protocoltests-restjson"); // CommonJS import + * const client = new RestJsonProtocolClient(config); + * const command = new TestNoPayloadCommand(input); + * const response = await client.send(command); + * ``` + * + * @see {@link TestNoPayloadCommandInput} for command's `input` shape. + * @see {@link TestNoPayloadCommandOutput} for command's `response` shape. + * @see {@link RestJsonProtocolClientResolvedConfig | config} for RestJsonProtocolClient's `config` shape. + * + */ +export class TestNoPayloadCommand extends $Command< + TestNoPayloadCommandInput, + TestNoPayloadCommandOutput, + RestJsonProtocolClientResolvedConfig +> { + // Start section: command_properties + // End section: command_properties + + constructor(readonly input: TestNoPayloadCommandInput) { + // Start section: command_constructor + super(); + // End section: command_constructor + } + + /** + * @internal + */ + resolveMiddleware( + clientStack: MiddlewareStack, + configuration: RestJsonProtocolClientResolvedConfig, + options?: __HttpHandlerOptions + ): Handler { + this.middlewareStack.use(getSerdePlugin(configuration, this.serialize, this.deserialize)); + + const stack = clientStack.concat(this.middlewareStack); + + const { logger } = configuration; + const clientName = "RestJsonProtocolClient"; + const commandName = "TestNoPayloadCommand"; + const handlerExecutionContext: HandlerExecutionContext = { + logger, + clientName, + commandName, + inputFilterSensitiveLog: TestNoPayloadInputOutput.filterSensitiveLog, + outputFilterSensitiveLog: TestNoPayloadInputOutput.filterSensitiveLog, + }; + const { requestHandler } = configuration; + return stack.resolve( + (request: FinalizeHandlerArguments) => + requestHandler.handle(request.request as __HttpRequest, options || {}), + handlerExecutionContext + ); + } + + private serialize(input: TestNoPayloadCommandInput, context: __SerdeContext): Promise<__HttpRequest> { + return serializeAws_restJson1TestNoPayloadCommand(input, context); + } + + private deserialize(output: __HttpResponse, context: __SerdeContext): Promise { + return deserializeAws_restJson1TestNoPayloadCommand(output, context); + } + + // Start section: command_body_extra + // End section: command_body_extra +} diff --git a/private/aws-protocoltests-restjson/src/commands/TestPayloadBlobCommand.ts b/private/aws-protocoltests-restjson/src/commands/TestPayloadBlobCommand.ts new file mode 100644 index 000000000000..2c78349b815e --- /dev/null +++ b/private/aws-protocoltests-restjson/src/commands/TestPayloadBlobCommand.ts @@ -0,0 +1,102 @@ +import { getSerdePlugin } from "@aws-sdk/middleware-serde"; +import { HttpRequest as __HttpRequest, HttpResponse as __HttpResponse } from "@aws-sdk/protocol-http"; +import { Command as $Command } from "@aws-sdk/smithy-client"; +import { + FinalizeHandlerArguments, + Handler, + HandlerExecutionContext, + HttpHandlerOptions as __HttpHandlerOptions, + MetadataBearer as __MetadataBearer, + MiddlewareStack, + SerdeContext as __SerdeContext, +} from "@aws-sdk/types"; + +import { TestPayloadBlobInputOutput } from "../models/models_0"; +import { + deserializeAws_restJson1TestPayloadBlobCommand, + serializeAws_restJson1TestPayloadBlobCommand, +} from "../protocols/Aws_restJson1"; +import { RestJsonProtocolClientResolvedConfig, ServiceInputTypes, ServiceOutputTypes } from "../RestJsonProtocolClient"; + +export interface TestPayloadBlobCommandInput extends TestPayloadBlobInputOutput {} +export interface TestPayloadBlobCommandOutput extends TestPayloadBlobInputOutput, __MetadataBearer {} + +/** + * This example operation serializes a payload targeting a blob. + * + * The Blob shape is not structured content and we cannot + * make assumptions about what data will be sent. This test ensures + * only a generic "Content-Type: application/octet-stream" header + * is used, and that we are not treating an empty body as an + * empty JSON document. + * + * @example + * Use a bare-bones client and the command you need to make an API call. + * ```javascript + * import { RestJsonProtocolClient, TestPayloadBlobCommand } from "@aws-sdk/aws-protocoltests-restjson"; // ES Modules import + * // const { RestJsonProtocolClient, TestPayloadBlobCommand } = require("@aws-sdk/aws-protocoltests-restjson"); // CommonJS import + * const client = new RestJsonProtocolClient(config); + * const command = new TestPayloadBlobCommand(input); + * const response = await client.send(command); + * ``` + * + * @see {@link TestPayloadBlobCommandInput} for command's `input` shape. + * @see {@link TestPayloadBlobCommandOutput} for command's `response` shape. + * @see {@link RestJsonProtocolClientResolvedConfig | config} for RestJsonProtocolClient's `config` shape. + * + */ +export class TestPayloadBlobCommand extends $Command< + TestPayloadBlobCommandInput, + TestPayloadBlobCommandOutput, + RestJsonProtocolClientResolvedConfig +> { + // Start section: command_properties + // End section: command_properties + + constructor(readonly input: TestPayloadBlobCommandInput) { + // Start section: command_constructor + super(); + // End section: command_constructor + } + + /** + * @internal + */ + resolveMiddleware( + clientStack: MiddlewareStack, + configuration: RestJsonProtocolClientResolvedConfig, + options?: __HttpHandlerOptions + ): Handler { + this.middlewareStack.use(getSerdePlugin(configuration, this.serialize, this.deserialize)); + + const stack = clientStack.concat(this.middlewareStack); + + const { logger } = configuration; + const clientName = "RestJsonProtocolClient"; + const commandName = "TestPayloadBlobCommand"; + const handlerExecutionContext: HandlerExecutionContext = { + logger, + clientName, + commandName, + inputFilterSensitiveLog: TestPayloadBlobInputOutput.filterSensitiveLog, + outputFilterSensitiveLog: TestPayloadBlobInputOutput.filterSensitiveLog, + }; + const { requestHandler } = configuration; + return stack.resolve( + (request: FinalizeHandlerArguments) => + requestHandler.handle(request.request as __HttpRequest, options || {}), + handlerExecutionContext + ); + } + + private serialize(input: TestPayloadBlobCommandInput, context: __SerdeContext): Promise<__HttpRequest> { + return serializeAws_restJson1TestPayloadBlobCommand(input, context); + } + + private deserialize(output: __HttpResponse, context: __SerdeContext): Promise { + return deserializeAws_restJson1TestPayloadBlobCommand(output, context); + } + + // Start section: command_body_extra + // End section: command_body_extra +} diff --git a/private/aws-protocoltests-restjson/src/commands/TestPayloadStructureCommand.ts b/private/aws-protocoltests-restjson/src/commands/TestPayloadStructureCommand.ts new file mode 100644 index 000000000000..feeeb62a284e --- /dev/null +++ b/private/aws-protocoltests-restjson/src/commands/TestPayloadStructureCommand.ts @@ -0,0 +1,99 @@ +import { getSerdePlugin } from "@aws-sdk/middleware-serde"; +import { HttpRequest as __HttpRequest, HttpResponse as __HttpResponse } from "@aws-sdk/protocol-http"; +import { Command as $Command } from "@aws-sdk/smithy-client"; +import { + FinalizeHandlerArguments, + Handler, + HandlerExecutionContext, + HttpHandlerOptions as __HttpHandlerOptions, + MetadataBearer as __MetadataBearer, + MiddlewareStack, + SerdeContext as __SerdeContext, +} from "@aws-sdk/types"; + +import { TestPayloadStructureInputOutput } from "../models/models_0"; +import { + deserializeAws_restJson1TestPayloadStructureCommand, + serializeAws_restJson1TestPayloadStructureCommand, +} from "../protocols/Aws_restJson1"; +import { RestJsonProtocolClientResolvedConfig, ServiceInputTypes, ServiceOutputTypes } from "../RestJsonProtocolClient"; + +export interface TestPayloadStructureCommandInput extends TestPayloadStructureInputOutput {} +export interface TestPayloadStructureCommandOutput extends TestPayloadStructureInputOutput, __MetadataBearer {} + +/** + * This example operation serializes a payload targeting a structure. + * + * This enforces the same requirements as TestBodyStructure + * but with the body specified by the @httpPayload trait. + * + * @example + * Use a bare-bones client and the command you need to make an API call. + * ```javascript + * import { RestJsonProtocolClient, TestPayloadStructureCommand } from "@aws-sdk/aws-protocoltests-restjson"; // ES Modules import + * // const { RestJsonProtocolClient, TestPayloadStructureCommand } = require("@aws-sdk/aws-protocoltests-restjson"); // CommonJS import + * const client = new RestJsonProtocolClient(config); + * const command = new TestPayloadStructureCommand(input); + * const response = await client.send(command); + * ``` + * + * @see {@link TestPayloadStructureCommandInput} for command's `input` shape. + * @see {@link TestPayloadStructureCommandOutput} for command's `response` shape. + * @see {@link RestJsonProtocolClientResolvedConfig | config} for RestJsonProtocolClient's `config` shape. + * + */ +export class TestPayloadStructureCommand extends $Command< + TestPayloadStructureCommandInput, + TestPayloadStructureCommandOutput, + RestJsonProtocolClientResolvedConfig +> { + // Start section: command_properties + // End section: command_properties + + constructor(readonly input: TestPayloadStructureCommandInput) { + // Start section: command_constructor + super(); + // End section: command_constructor + } + + /** + * @internal + */ + resolveMiddleware( + clientStack: MiddlewareStack, + configuration: RestJsonProtocolClientResolvedConfig, + options?: __HttpHandlerOptions + ): Handler { + this.middlewareStack.use(getSerdePlugin(configuration, this.serialize, this.deserialize)); + + const stack = clientStack.concat(this.middlewareStack); + + const { logger } = configuration; + const clientName = "RestJsonProtocolClient"; + const commandName = "TestPayloadStructureCommand"; + const handlerExecutionContext: HandlerExecutionContext = { + logger, + clientName, + commandName, + inputFilterSensitiveLog: TestPayloadStructureInputOutput.filterSensitiveLog, + outputFilterSensitiveLog: TestPayloadStructureInputOutput.filterSensitiveLog, + }; + const { requestHandler } = configuration; + return stack.resolve( + (request: FinalizeHandlerArguments) => + requestHandler.handle(request.request as __HttpRequest, options || {}), + handlerExecutionContext + ); + } + + private serialize(input: TestPayloadStructureCommandInput, context: __SerdeContext): Promise<__HttpRequest> { + return serializeAws_restJson1TestPayloadStructureCommand(input, context); + } + + private deserialize(output: __HttpResponse, context: __SerdeContext): Promise { + return deserializeAws_restJson1TestPayloadStructureCommand(output, context); + } + + // Start section: command_body_extra + // End section: command_body_extra +} diff --git a/private/aws-protocoltests-restjson/src/commands/index.ts b/private/aws-protocoltests-restjson/src/commands/index.ts index 039dfc6816fc..847abe864b3f 100644 --- a/private/aws-protocoltests-restjson/src/commands/index.ts +++ b/private/aws-protocoltests-restjson/src/commands/index.ts @@ -76,4 +76,8 @@ export * from "./SimpleScalarPropertiesCommand"; export * from "./StreamingTraitsCommand"; export * from "./StreamingTraitsRequireLengthCommand"; export * from "./StreamingTraitsWithMediaTypeCommand"; +export * from "./TestBodyStructureCommand"; +export * from "./TestNoPayloadCommand"; +export * from "./TestPayloadBlobCommand"; +export * from "./TestPayloadStructureCommand"; export * from "./TimestampFormatHeadersCommand"; diff --git a/private/aws-protocoltests-restjson/src/models/models_0.ts b/private/aws-protocoltests-restjson/src/models/models_0.ts index bfe279dd3bec..c9d883060914 100644 --- a/private/aws-protocoltests-restjson/src/models/models_0.ts +++ b/private/aws-protocoltests-restjson/src/models/models_0.ts @@ -1364,6 +1364,19 @@ export namespace OmitsNullSerializesEmptyStringInput { }); } +export interface PayloadConfig { + data?: number; +} + +export namespace PayloadConfig { + /** + * @internal + */ + export const filterSensitiveLog = (obj: PayloadConfig): any => ({ + ...obj, + }); +} + export interface QueryIdempotencyTokenAutoFillInput { token?: string; } @@ -1469,6 +1482,74 @@ export namespace StreamingTraitsWithMediaTypeInputOutput { }); } +export interface TestConfig { + timeout?: number; +} + +export namespace TestConfig { + /** + * @internal + */ + export const filterSensitiveLog = (obj: TestConfig): any => ({ + ...obj, + }); +} + +export interface TestBodyStructureInputOutput { + testId?: string; + testConfig?: TestConfig; +} + +export namespace TestBodyStructureInputOutput { + /** + * @internal + */ + export const filterSensitiveLog = (obj: TestBodyStructureInputOutput): any => ({ + ...obj, + }); +} + +export interface TestNoPayloadInputOutput { + testId?: string; +} + +export namespace TestNoPayloadInputOutput { + /** + * @internal + */ + export const filterSensitiveLog = (obj: TestNoPayloadInputOutput): any => ({ + ...obj, + }); +} + +export interface TestPayloadBlobInputOutput { + contentType?: string; + data?: Uint8Array; +} + +export namespace TestPayloadBlobInputOutput { + /** + * @internal + */ + export const filterSensitiveLog = (obj: TestPayloadBlobInputOutput): any => ({ + ...obj, + }); +} + +export interface TestPayloadStructureInputOutput { + testId?: string; + payloadConfig?: PayloadConfig; +} + +export namespace TestPayloadStructureInputOutput { + /** + * @internal + */ + export const filterSensitiveLog = (obj: TestPayloadStructureInputOutput): any => ({ + ...obj, + }); +} + export interface TimestampFormatHeadersIO { memberEpochSeconds?: Date; memberHttpDate?: Date; diff --git a/private/aws-protocoltests-restjson/src/protocols/Aws_restJson1.ts b/private/aws-protocoltests-restjson/src/protocols/Aws_restJson1.ts index 8b5b7e8f07a2..81ad3efa0626 100644 --- a/private/aws-protocoltests-restjson/src/protocols/Aws_restJson1.ts +++ b/private/aws-protocoltests-restjson/src/protocols/Aws_restJson1.ts @@ -255,6 +255,13 @@ import { StreamingTraitsWithMediaTypeCommandInput, StreamingTraitsWithMediaTypeCommandOutput, } from "../commands/StreamingTraitsWithMediaTypeCommand"; +import { TestBodyStructureCommandInput, TestBodyStructureCommandOutput } from "../commands/TestBodyStructureCommand"; +import { TestNoPayloadCommandInput, TestNoPayloadCommandOutput } from "../commands/TestNoPayloadCommand"; +import { TestPayloadBlobCommandInput, TestPayloadBlobCommandOutput } from "../commands/TestPayloadBlobCommand"; +import { + TestPayloadStructureCommandInput, + TestPayloadStructureCommandOutput, +} from "../commands/TestPayloadStructureCommand"; import { TimestampFormatHeadersCommandInput, TimestampFormatHeadersCommandOutput, @@ -268,11 +275,13 @@ import { InvalidGreeting, MyUnion, NestedPayload, + PayloadConfig, RecursiveShapesInputOutputNested1, RecursiveShapesInputOutputNested2, RenamedGreeting, SimpleUnion, StructureListMember, + TestConfig, } from "../models/models_0"; export const serializeAws_restJson1AllQueryStringTypesCommand = async ( @@ -2656,6 +2665,107 @@ export const serializeAws_restJson1StreamingTraitsWithMediaTypeCommand = async ( }); }; +export const serializeAws_restJson1TestBodyStructureCommand = async ( + input: TestBodyStructureCommandInput, + context: __SerdeContext +): Promise<__HttpRequest> => { + const { hostname, protocol = "https", port, path: basePath } = await context.endpoint(); + const headers: any = { + "content-type": "application/json", + ...(isSerializableHeaderValue(input.testId) && { "x-amz-test-id": input.testId! }), + }; + const resolvedPath = `${basePath?.endsWith("/") ? basePath.slice(0, -1) : basePath || ""}` + "/body"; + let body: any; + body = JSON.stringify({ + ...(input.testConfig !== undefined && + input.testConfig !== null && { testConfig: serializeAws_restJson1TestConfig(input.testConfig, context) }), + }); + return new __HttpRequest({ + protocol, + hostname, + port, + method: "POST", + headers, + path: resolvedPath, + body, + }); +}; + +export const serializeAws_restJson1TestNoPayloadCommand = async ( + input: TestNoPayloadCommandInput, + context: __SerdeContext +): Promise<__HttpRequest> => { + const { hostname, protocol = "https", port, path: basePath } = await context.endpoint(); + const headers: any = { + ...(isSerializableHeaderValue(input.testId) && { "x-amz-test-id": input.testId! }), + }; + const resolvedPath = `${basePath?.endsWith("/") ? basePath.slice(0, -1) : basePath || ""}` + "/no_payload"; + let body: any; + return new __HttpRequest({ + protocol, + hostname, + port, + method: "GET", + headers, + path: resolvedPath, + body, + }); +}; + +export const serializeAws_restJson1TestPayloadBlobCommand = async ( + input: TestPayloadBlobCommandInput, + context: __SerdeContext +): Promise<__HttpRequest> => { + const { hostname, protocol = "https", port, path: basePath } = await context.endpoint(); + const headers: any = { + "content-type": "application/octet-stream", + ...(isSerializableHeaderValue(input.contentType) && { "content-type": input.contentType! }), + }; + const resolvedPath = `${basePath?.endsWith("/") ? basePath.slice(0, -1) : basePath || ""}` + "/blob_payload"; + let body: any; + if (input.data !== undefined) { + body = input.data; + } + return new __HttpRequest({ + protocol, + hostname, + port, + method: "POST", + headers, + path: resolvedPath, + body, + }); +}; + +export const serializeAws_restJson1TestPayloadStructureCommand = async ( + input: TestPayloadStructureCommandInput, + context: __SerdeContext +): Promise<__HttpRequest> => { + const { hostname, protocol = "https", port, path: basePath } = await context.endpoint(); + const headers: any = { + "content-type": "application/json", + ...(isSerializableHeaderValue(input.testId) && { "x-amz-test-id": input.testId! }), + }; + const resolvedPath = `${basePath?.endsWith("/") ? basePath.slice(0, -1) : basePath || ""}` + "/payload"; + let body: any; + if (input.payloadConfig !== undefined) { + body = serializeAws_restJson1PayloadConfig(input.payloadConfig, context); + } + if (body === undefined) { + body = {}; + } + body = JSON.stringify(body); + return new __HttpRequest({ + protocol, + hostname, + port, + method: "POST", + headers, + path: resolvedPath, + body, + }); +}; + export const serializeAws_restJson1TimestampFormatHeadersCommand = async ( input: TimestampFormatHeadersCommandInput, context: __SerdeContext @@ -6433,6 +6543,202 @@ const deserializeAws_restJson1StreamingTraitsWithMediaTypeCommandError = async ( return Promise.reject(Object.assign(new Error(message), response)); }; +export const deserializeAws_restJson1TestBodyStructureCommand = async ( + output: __HttpResponse, + context: __SerdeContext +): Promise => { + if (output.statusCode !== 200 && output.statusCode >= 300) { + return deserializeAws_restJson1TestBodyStructureCommandError(output, context); + } + const contents: TestBodyStructureCommandOutput = { + $metadata: deserializeMetadata(output), + testConfig: undefined, + testId: undefined, + }; + if (output.headers["x-amz-test-id"] !== undefined) { + contents.testId = output.headers["x-amz-test-id"]; + } + const data: { [key: string]: any } = __expectNonNull(__expectObject(await parseBody(output.body, context)), "body"); + if (data.testConfig !== undefined && data.testConfig !== null) { + contents.testConfig = deserializeAws_restJson1TestConfig(data.testConfig, context); + } + return Promise.resolve(contents); +}; + +const deserializeAws_restJson1TestBodyStructureCommandError = async ( + output: __HttpResponse, + context: __SerdeContext +): Promise => { + const parsedOutput: any = { + ...output, + body: await parseBody(output.body, context), + }; + let response: __SmithyException & __MetadataBearer & { [key: string]: any }; + let errorCode = "UnknownError"; + errorCode = loadRestJsonErrorCode(output, parsedOutput.body); + switch (errorCode) { + default: + const parsedBody = parsedOutput.body; + errorCode = parsedBody.code || parsedBody.Code || errorCode; + response = { + ...parsedBody, + name: `${errorCode}`, + message: parsedBody.message || parsedBody.Message || errorCode, + $fault: "client", + $metadata: deserializeMetadata(output), + } as any; + } + const message = response.message || response.Message || errorCode; + response.message = message; + delete response.Message; + return Promise.reject(Object.assign(new Error(message), response)); +}; + +export const deserializeAws_restJson1TestNoPayloadCommand = async ( + output: __HttpResponse, + context: __SerdeContext +): Promise => { + if (output.statusCode !== 200 && output.statusCode >= 300) { + return deserializeAws_restJson1TestNoPayloadCommandError(output, context); + } + const contents: TestNoPayloadCommandOutput = { + $metadata: deserializeMetadata(output), + testId: undefined, + }; + if (output.headers["x-amz-test-id"] !== undefined) { + contents.testId = output.headers["x-amz-test-id"]; + } + await collectBody(output.body, context); + return Promise.resolve(contents); +}; + +const deserializeAws_restJson1TestNoPayloadCommandError = async ( + output: __HttpResponse, + context: __SerdeContext +): Promise => { + const parsedOutput: any = { + ...output, + body: await parseBody(output.body, context), + }; + let response: __SmithyException & __MetadataBearer & { [key: string]: any }; + let errorCode = "UnknownError"; + errorCode = loadRestJsonErrorCode(output, parsedOutput.body); + switch (errorCode) { + default: + const parsedBody = parsedOutput.body; + errorCode = parsedBody.code || parsedBody.Code || errorCode; + response = { + ...parsedBody, + name: `${errorCode}`, + message: parsedBody.message || parsedBody.Message || errorCode, + $fault: "client", + $metadata: deserializeMetadata(output), + } as any; + } + const message = response.message || response.Message || errorCode; + response.message = message; + delete response.Message; + return Promise.reject(Object.assign(new Error(message), response)); +}; + +export const deserializeAws_restJson1TestPayloadBlobCommand = async ( + output: __HttpResponse, + context: __SerdeContext +): Promise => { + if (output.statusCode !== 200 && output.statusCode >= 300) { + return deserializeAws_restJson1TestPayloadBlobCommandError(output, context); + } + const contents: TestPayloadBlobCommandOutput = { + $metadata: deserializeMetadata(output), + contentType: undefined, + data: undefined, + }; + if (output.headers["content-type"] !== undefined) { + contents.contentType = output.headers["content-type"]; + } + const data: any = await collectBody(output.body, context); + contents.data = data; + return Promise.resolve(contents); +}; + +const deserializeAws_restJson1TestPayloadBlobCommandError = async ( + output: __HttpResponse, + context: __SerdeContext +): Promise => { + const parsedOutput: any = { + ...output, + body: await parseBody(output.body, context), + }; + let response: __SmithyException & __MetadataBearer & { [key: string]: any }; + let errorCode = "UnknownError"; + errorCode = loadRestJsonErrorCode(output, parsedOutput.body); + switch (errorCode) { + default: + const parsedBody = parsedOutput.body; + errorCode = parsedBody.code || parsedBody.Code || errorCode; + response = { + ...parsedBody, + name: `${errorCode}`, + message: parsedBody.message || parsedBody.Message || errorCode, + $fault: "client", + $metadata: deserializeMetadata(output), + } as any; + } + const message = response.message || response.Message || errorCode; + response.message = message; + delete response.Message; + return Promise.reject(Object.assign(new Error(message), response)); +}; + +export const deserializeAws_restJson1TestPayloadStructureCommand = async ( + output: __HttpResponse, + context: __SerdeContext +): Promise => { + if (output.statusCode !== 200 && output.statusCode >= 300) { + return deserializeAws_restJson1TestPayloadStructureCommandError(output, context); + } + const contents: TestPayloadStructureCommandOutput = { + $metadata: deserializeMetadata(output), + payloadConfig: undefined, + testId: undefined, + }; + if (output.headers["x-amz-test-id"] !== undefined) { + contents.testId = output.headers["x-amz-test-id"]; + } + const data: { [key: string]: any } | undefined = __expectObject(await parseBody(output.body, context)); + contents.payloadConfig = deserializeAws_restJson1PayloadConfig(data, context); + return Promise.resolve(contents); +}; + +const deserializeAws_restJson1TestPayloadStructureCommandError = async ( + output: __HttpResponse, + context: __SerdeContext +): Promise => { + const parsedOutput: any = { + ...output, + body: await parseBody(output.body, context), + }; + let response: __SmithyException & __MetadataBearer & { [key: string]: any }; + let errorCode = "UnknownError"; + errorCode = loadRestJsonErrorCode(output, parsedOutput.body); + switch (errorCode) { + default: + const parsedBody = parsedOutput.body; + errorCode = parsedBody.code || parsedBody.Code || errorCode; + response = { + ...parsedBody, + name: `${errorCode}`, + message: parsedBody.message || parsedBody.Message || errorCode, + $fault: "client", + $metadata: deserializeMetadata(output), + } as any; + } + const message = response.message || response.Message || errorCode; + response.message = message; + delete response.Message; + return Promise.reject(Object.assign(new Error(message), response)); +}; + export const deserializeAws_restJson1TimestampFormatHeadersCommand = async ( output: __HttpResponse, context: __SerdeContext @@ -6651,6 +6957,12 @@ const serializeAws_restJson1NestedPayload = (input: NestedPayload, context: __Se }; }; +const serializeAws_restJson1PayloadConfig = (input: PayloadConfig, context: __SerdeContext): any => { + return { + ...(input.data !== undefined && input.data !== null && { data: input.data }), + }; +}; + const serializeAws_restJson1RecursiveShapesInputOutputNested1 = ( input: RecursiveShapesInputOutputNested1, context: __SerdeContext @@ -6788,6 +7100,12 @@ const serializeAws_restJson1StructureListMember = (input: StructureListMember, c }; }; +const serializeAws_restJson1TestConfig = (input: TestConfig, context: __SerdeContext): any => { + return { + ...(input.timeout !== undefined && input.timeout !== null && { timeout: input.timeout }), + }; +}; + const serializeAws_restJson1RenamedGreeting = (input: RenamedGreeting, context: __SerdeContext): any => { return { ...(input.salutation !== undefined && input.salutation !== null && { salutation: input.salutation }), @@ -7062,6 +7380,12 @@ const deserializeAws_restJson1NestedPayload = (output: any, context: __SerdeCont } as any; }; +const deserializeAws_restJson1PayloadConfig = (output: any, context: __SerdeContext): PayloadConfig => { + return { + data: __expectInt32(output.data), + } as any; +}; + const deserializeAws_restJson1RecursiveShapesInputOutputNested1 = ( output: any, context: __SerdeContext @@ -7157,6 +7481,12 @@ const deserializeAws_restJson1StructureListMember = (output: any, context: __Ser } as any; }; +const deserializeAws_restJson1TestConfig = (output: any, context: __SerdeContext): TestConfig => { + return { + timeout: __expectInt32(output.timeout), + } as any; +}; + const deserializeAws_restJson1RenamedGreeting = (output: any, context: __SerdeContext): RenamedGreeting => { return { salutation: __expectString(output.salutation), diff --git a/private/aws-protocoltests-restjson/test/functional/restjson1.spec.ts b/private/aws-protocoltests-restjson/test/functional/restjson1.spec.ts index 879c3fd9d52f..aedb736f655f 100644 --- a/private/aws-protocoltests-restjson/test/functional/restjson1.spec.ts +++ b/private/aws-protocoltests-restjson/test/functional/restjson1.spec.ts @@ -48,6 +48,10 @@ import { SimpleScalarPropertiesCommand } from "../../src/commands/SimpleScalarPr import { StreamingTraitsCommand } from "../../src/commands/StreamingTraitsCommand"; import { StreamingTraitsRequireLengthCommand } from "../../src/commands/StreamingTraitsRequireLengthCommand"; import { StreamingTraitsWithMediaTypeCommand } from "../../src/commands/StreamingTraitsWithMediaTypeCommand"; +import { TestBodyStructureCommand } from "../../src/commands/TestBodyStructureCommand"; +import { TestNoPayloadCommand } from "../../src/commands/TestNoPayloadCommand"; +import { TestPayloadBlobCommand } from "../../src/commands/TestPayloadBlobCommand"; +import { TestPayloadStructureCommand } from "../../src/commands/TestPayloadStructureCommand"; import { TimestampFormatHeadersCommand } from "../../src/commands/TimestampFormatHeadersCommand"; import { ComplexError, FooError, InvalidGreeting } from "../../src/models/models_0"; import { RestJsonProtocolClient } from "../../src/RestJsonProtocolClient"; @@ -6177,9 +6181,6 @@ it.skip("RestJsonNoInputAndOutput:Request", async () => { expect(r.method).toBe("POST"); expect(r.path).toBe("/NoInputAndOutputOutput"); - expect(r.headers["accept"]).toBeDefined(); - expect(r.headers["accept"]).toBe("application/json"); - expect(r.body).toBeFalsy(); } }); @@ -7466,6 +7467,330 @@ it("RestJsonStreamingTraitsWithMediaTypeWithBlob:Response", async () => { }); }); +/** + * Serializes a structure + */ +it("RestJsonTestBodyStructure:Request", async () => { + const client = new RestJsonProtocolClient({ + ...clientParams, + requestHandler: new RequestSerializationTestHandler(), + }); + + const command = new TestBodyStructureCommand({ + testConfig: { + timeout: 10, + } as any, + } as any); + try { + await client.send(command); + fail("Expected an EXPECTED_REQUEST_SERIALIZATION_ERROR to be thrown"); + return; + } catch (err) { + if (!(err instanceof EXPECTED_REQUEST_SERIALIZATION_ERROR)) { + fail(err); + return; + } + const r = err.request; + expect(r.method).toBe("POST"); + expect(r.path).toBe("/body"); + expect(r.headers["content-length"]).toBeDefined(); + + expect(r.headers["content-type"]).toBeDefined(); + expect(r.headers["content-type"]).toBe("application/json"); + + expect(r.body).toBeDefined(); + const utf8Encoder = client.config.utf8Encoder; + const bodyString = `{\"testConfig\": + {\"timeout\": 10} + }`; + const unequalParts: any = compareEquivalentJsonBodies(bodyString, r.body.toString()); + expect(unequalParts).toBeUndefined(); + } +}); + +/** + * Serializes an empty structure in the body + */ +it("RestJsonHttpWithEmptyBody:Request", async () => { + const client = new RestJsonProtocolClient({ + ...clientParams, + requestHandler: new RequestSerializationTestHandler(), + }); + + const command = new TestBodyStructureCommand({} as any); + try { + await client.send(command); + fail("Expected an EXPECTED_REQUEST_SERIALIZATION_ERROR to be thrown"); + return; + } catch (err) { + if (!(err instanceof EXPECTED_REQUEST_SERIALIZATION_ERROR)) { + fail(err); + return; + } + const r = err.request; + expect(r.method).toBe("POST"); + expect(r.path).toBe("/body"); + expect(r.headers["content-length"]).toBeDefined(); + + expect(r.headers["content-type"]).toBeDefined(); + expect(r.headers["content-type"]).toBe("application/json"); + + expect(r.body).toBeDefined(); + const utf8Encoder = client.config.utf8Encoder; + const bodyString = `{}`; + const unequalParts: any = compareEquivalentJsonBodies(bodyString, r.body.toString()); + expect(unequalParts).toBeUndefined(); + } +}); + +/** + * Serializes a GET request with no modeled body + */ +it("RestJsonHttpWithNoModeledBody:Request", async () => { + const client = new RestJsonProtocolClient({ + ...clientParams, + requestHandler: new RequestSerializationTestHandler(), + }); + + const command = new TestNoPayloadCommand({} as any); + try { + await client.send(command); + fail("Expected an EXPECTED_REQUEST_SERIALIZATION_ERROR to be thrown"); + return; + } catch (err) { + if (!(err instanceof EXPECTED_REQUEST_SERIALIZATION_ERROR)) { + fail(err); + return; + } + const r = err.request; + expect(r.method).toBe("GET"); + expect(r.path).toBe("/no_payload"); + + expect(r.headers["content-length"]).toBeUndefined(); + expect(r.headers["content-type"]).toBeUndefined(); + + expect(r.body).toBeFalsy(); + } +}); + +/** + * Serializes a GET request with header member but no modeled body + */ +it("RestJsonHttpWithHeaderMemberNoModeledBody:Request", async () => { + const client = new RestJsonProtocolClient({ + ...clientParams, + requestHandler: new RequestSerializationTestHandler(), + }); + + const command = new TestNoPayloadCommand({ + testId: "t-12345", + } as any); + try { + await client.send(command); + fail("Expected an EXPECTED_REQUEST_SERIALIZATION_ERROR to be thrown"); + return; + } catch (err) { + if (!(err instanceof EXPECTED_REQUEST_SERIALIZATION_ERROR)) { + fail(err); + return; + } + const r = err.request; + expect(r.method).toBe("GET"); + expect(r.path).toBe("/no_payload"); + + expect(r.headers["content-length"]).toBeUndefined(); + expect(r.headers["content-type"]).toBeUndefined(); + + expect(r.headers["x-amz-test-id"]).toBeDefined(); + expect(r.headers["x-amz-test-id"]).toBe("t-12345"); + + expect(r.body).toBeFalsy(); + } +}); + +/** + * Serializes a payload targeting an empty blob + */ +it("RestJsonHttpWithEmptyBlobPayload:Request", async () => { + const client = new RestJsonProtocolClient({ + ...clientParams, + requestHandler: new RequestSerializationTestHandler(), + }); + + const command = new TestPayloadBlobCommand({} as any); + try { + await client.send(command); + fail("Expected an EXPECTED_REQUEST_SERIALIZATION_ERROR to be thrown"); + return; + } catch (err) { + if (!(err instanceof EXPECTED_REQUEST_SERIALIZATION_ERROR)) { + fail(err); + return; + } + const r = err.request; + expect(r.method).toBe("POST"); + expect(r.path).toBe("/blob_payload"); + + expect(r.headers["content-type"]).toBeDefined(); + expect(r.headers["content-type"]).toBe("application/octet-stream"); + + expect(r.body).toBeFalsy(); + } +}); + +/** + * Serializes a payload targeting a blob + */ +it("RestJsonTestPayloadBlob:Request", async () => { + const client = new RestJsonProtocolClient({ + ...clientParams, + requestHandler: new RequestSerializationTestHandler(), + }); + + const command = new TestPayloadBlobCommand({ + contentType: "image/jpg", + + data: Uint8Array.from("1234", (c) => c.charCodeAt(0)), + } as any); + try { + await client.send(command); + fail("Expected an EXPECTED_REQUEST_SERIALIZATION_ERROR to be thrown"); + return; + } catch (err) { + if (!(err instanceof EXPECTED_REQUEST_SERIALIZATION_ERROR)) { + fail(err); + return; + } + const r = err.request; + expect(r.method).toBe("POST"); + expect(r.path).toBe("/blob_payload"); + expect(r.headers["content-length"]).toBeDefined(); + + expect(r.headers["content-type"]).toBeDefined(); + expect(r.headers["content-type"]).toBe("image/jpg"); + + expect(r.body).toBeDefined(); + const utf8Encoder = client.config.utf8Encoder; + const bodyString = `1234`; + const unequalParts: any = compareEquivalentUnknownTypeBodies(utf8Encoder, bodyString, r.body); + expect(unequalParts).toBeUndefined(); + } +}); + +/** + * Serializes a payload targeting an empty structure + */ +it("RestJsonHttpWithEmptyStructurePayload:Request", async () => { + const client = new RestJsonProtocolClient({ + ...clientParams, + requestHandler: new RequestSerializationTestHandler(), + }); + + const command = new TestPayloadStructureCommand({} as any); + try { + await client.send(command); + fail("Expected an EXPECTED_REQUEST_SERIALIZATION_ERROR to be thrown"); + return; + } catch (err) { + if (!(err instanceof EXPECTED_REQUEST_SERIALIZATION_ERROR)) { + fail(err); + return; + } + const r = err.request; + expect(r.method).toBe("POST"); + expect(r.path).toBe("/payload"); + expect(r.headers["content-length"]).toBeDefined(); + + expect(r.headers["content-type"]).toBeDefined(); + expect(r.headers["content-type"]).toBe("application/json"); + + expect(r.body).toBeDefined(); + const utf8Encoder = client.config.utf8Encoder; + const bodyString = `{}`; + const unequalParts: any = compareEquivalentJsonBodies(bodyString, r.body.toString()); + expect(unequalParts).toBeUndefined(); + } +}); + +/** + * Serializes a payload targeting a structure + */ +it.skip("RestJsonTestPayloadStructure:Request", async () => { + const client = new RestJsonProtocolClient({ + ...clientParams, + requestHandler: new RequestSerializationTestHandler(), + }); + + const command = new TestPayloadStructureCommand({ + testId: "t-12345", + + payloadConfig: { + data: 25, + } as any, + } as any); + try { + await client.send(command); + fail("Expected an EXPECTED_REQUEST_SERIALIZATION_ERROR to be thrown"); + return; + } catch (err) { + if (!(err instanceof EXPECTED_REQUEST_SERIALIZATION_ERROR)) { + fail(err); + return; + } + const r = err.request; + expect(r.method).toBe("POST"); + expect(r.path).toBe("/payload"); + expect(r.headers["content-length"]).toBeDefined(); + + expect(r.headers["content-type"]).toBeDefined(); + expect(r.headers["content-type"]).toBe("application/json"); + + expect(r.body).toBeDefined(); + const utf8Encoder = client.config.utf8Encoder; + const bodyString = `{\"data\": 25 + }`; + const unequalParts: any = compareEquivalentJsonBodies(bodyString, r.body.toString()); + expect(unequalParts).toBeUndefined(); + } +}); + +/** + * Serializes an request with header members but no payload + */ +it.skip("RestJsonHttpWithHeadersButNoPayload:Request", async () => { + const client = new RestJsonProtocolClient({ + ...clientParams, + requestHandler: new RequestSerializationTestHandler(), + }); + + const command = new TestPayloadStructureCommand({ + testId: "t-12345", + } as any); + try { + await client.send(command); + fail("Expected an EXPECTED_REQUEST_SERIALIZATION_ERROR to be thrown"); + return; + } catch (err) { + if (!(err instanceof EXPECTED_REQUEST_SERIALIZATION_ERROR)) { + fail(err); + return; + } + const r = err.request; + expect(r.method).toBe("POST"); + expect(r.path).toBe("/payload"); + expect(r.headers["content-length"]).toBeDefined(); + + expect(r.headers["content-type"]).toBeDefined(); + expect(r.headers["content-type"]).toBe("application/json"); + + expect(r.body).toBeDefined(); + const utf8Encoder = client.config.utf8Encoder; + const bodyString = `{}`; + const unequalParts: any = compareEquivalentJsonBodies(bodyString, r.body.toString()); + expect(unequalParts).toBeUndefined(); + } +}); + /** * Tests how timestamp request headers are serialized */