diff --git a/codegen/protocol-test-codegen/build.gradle.kts b/codegen/protocol-test-codegen/build.gradle.kts index 0af182c5e6e7..57f0c6b75b83 100644 --- a/codegen/protocol-test-codegen/build.gradle.kts +++ b/codegen/protocol-test-codegen/build.gradle.kts @@ -20,7 +20,7 @@ plugins { } dependencies { - implementation("software.amazon.smithy:smithy-aws-protocol-tests:[1.6.0, 1.7.0[") + implementation("software.amazon.smithy:smithy-aws-protocol-tests:[1.7.0, 1.8.0[") compile(project(":smithy-aws-typescript-codegen")) } diff --git a/codegen/smithy-aws-typescript-codegen/build.gradle.kts b/codegen/smithy-aws-typescript-codegen/build.gradle.kts index 37e25672e47a..7a6d6eb5d686 100644 --- a/codegen/smithy-aws-typescript-codegen/build.gradle.kts +++ b/codegen/smithy-aws-typescript-codegen/build.gradle.kts @@ -18,8 +18,8 @@ extra["displayName"] = "Smithy :: AWS :: Typescript :: Codegen" extra["moduleName"] = "software.amazon.smithy.aws.typescript.codegen" dependencies { - api("software.amazon.smithy:smithy-aws-traits:[1.6.0, 1.7.0[") - api("software.amazon.smithy:smithy-waiters:[1.6.0, 1.7.0[") - api("software.amazon.smithy:smithy-aws-iam-traits:[1.6.0, 1.7.0[") + api("software.amazon.smithy:smithy-aws-traits:[1.7.0, 1.8.0[") + api("software.amazon.smithy:smithy-waiters:[1.7.0, 1.8.0[") + api("software.amazon.smithy:smithy-aws-iam-traits:[1.7.0, 1.8.0[") api("software.amazon.smithy:smithy-typescript-codegen:0.3.0") } diff --git a/protocol_tests/aws-restjson/RestJsonProtocol.ts b/protocol_tests/aws-restjson/RestJsonProtocol.ts index a382f855b069..d839910af3d1 100644 --- a/protocol_tests/aws-restjson/RestJsonProtocol.ts +++ b/protocol_tests/aws-restjson/RestJsonProtocol.ts @@ -144,6 +144,16 @@ import { QueryIdempotencyTokenAutoFillCommandInput, QueryIdempotencyTokenAutoFillCommandOutput, } from "./commands/QueryIdempotencyTokenAutoFillCommand"; +import { + QueryParamsAsStringListMapCommand, + QueryParamsAsStringListMapCommandInput, + QueryParamsAsStringListMapCommandOutput, +} from "./commands/QueryParamsAsStringListMapCommand"; +import { + QueryPrecedenceCommand, + QueryPrecedenceCommandInput, + QueryPrecedenceCommandOutput, +} from "./commands/QueryPrecedenceCommand"; import { RecursiveShapesCommand, RecursiveShapesCommandInput, @@ -1238,6 +1248,64 @@ export class RestJsonProtocol extends RestJsonProtocolClient { } } + public queryParamsAsStringListMap( + args: QueryParamsAsStringListMapCommandInput, + options?: __HttpHandlerOptions + ): Promise; + public queryParamsAsStringListMap( + args: QueryParamsAsStringListMapCommandInput, + cb: (err: any, data?: QueryParamsAsStringListMapCommandOutput) => void + ): void; + public queryParamsAsStringListMap( + args: QueryParamsAsStringListMapCommandInput, + options: __HttpHandlerOptions, + cb: (err: any, data?: QueryParamsAsStringListMapCommandOutput) => void + ): void; + public queryParamsAsStringListMap( + args: QueryParamsAsStringListMapCommandInput, + optionsOrCb?: __HttpHandlerOptions | ((err: any, data?: QueryParamsAsStringListMapCommandOutput) => void), + cb?: (err: any, data?: QueryParamsAsStringListMapCommandOutput) => void + ): Promise | void { + const command = new QueryParamsAsStringListMapCommand(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); + } + } + + public queryPrecedence( + args: QueryPrecedenceCommandInput, + options?: __HttpHandlerOptions + ): Promise; + public queryPrecedence( + args: QueryPrecedenceCommandInput, + cb: (err: any, data?: QueryPrecedenceCommandOutput) => void + ): void; + public queryPrecedence( + args: QueryPrecedenceCommandInput, + options: __HttpHandlerOptions, + cb: (err: any, data?: QueryPrecedenceCommandOutput) => void + ): void; + public queryPrecedence( + args: QueryPrecedenceCommandInput, + optionsOrCb?: __HttpHandlerOptions | ((err: any, data?: QueryPrecedenceCommandOutput) => void), + cb?: (err: any, data?: QueryPrecedenceCommandOutput) => void + ): Promise | void { + const command = new QueryPrecedenceCommand(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); + } + } + /** * Recursive shapes */ diff --git a/protocol_tests/aws-restjson/RestJsonProtocolClient.ts b/protocol_tests/aws-restjson/RestJsonProtocolClient.ts index dcb7345be2f3..56382a0e5ead 100644 --- a/protocol_tests/aws-restjson/RestJsonProtocolClient.ts +++ b/protocol_tests/aws-restjson/RestJsonProtocolClient.ts @@ -85,6 +85,11 @@ import { QueryIdempotencyTokenAutoFillCommandInput, QueryIdempotencyTokenAutoFillCommandOutput, } from "./commands/QueryIdempotencyTokenAutoFillCommand"; +import { + QueryParamsAsStringListMapCommandInput, + QueryParamsAsStringListMapCommandOutput, +} from "./commands/QueryParamsAsStringListMapCommand"; +import { QueryPrecedenceCommandInput, QueryPrecedenceCommandOutput } from "./commands/QueryPrecedenceCommand"; import { RecursiveShapesCommandInput, RecursiveShapesCommandOutput } from "./commands/RecursiveShapesCommand"; import { SimpleScalarPropertiesCommandInput, @@ -188,6 +193,8 @@ export type ServiceInputTypes = | NullAndEmptyHeadersServerCommandInput | OmitsNullSerializesEmptyStringCommandInput | QueryIdempotencyTokenAutoFillCommandInput + | QueryParamsAsStringListMapCommandInput + | QueryPrecedenceCommandInput | RecursiveShapesCommandInput | SimpleScalarPropertiesCommandInput | StreamingTraitsCommandInput @@ -229,6 +236,8 @@ export type ServiceOutputTypes = | NullAndEmptyHeadersServerCommandOutput | OmitsNullSerializesEmptyStringCommandOutput | QueryIdempotencyTokenAutoFillCommandOutput + | QueryParamsAsStringListMapCommandOutput + | QueryPrecedenceCommandOutput | RecursiveShapesCommandOutput | SimpleScalarPropertiesCommandOutput | StreamingTraitsCommandOutput diff --git a/protocol_tests/aws-restjson/commands/QueryParamsAsStringListMapCommand.ts b/protocol_tests/aws-restjson/commands/QueryParamsAsStringListMapCommand.ts new file mode 100644 index 000000000000..ef1dd87ac2cb --- /dev/null +++ b/protocol_tests/aws-restjson/commands/QueryParamsAsStringListMapCommand.ts @@ -0,0 +1,80 @@ +import { RestJsonProtocolClientResolvedConfig, ServiceInputTypes, ServiceOutputTypes } from "../RestJsonProtocolClient"; +import { QueryParamsAsStringListMapInput } from "../models/models_0"; +import { + deserializeAws_restJson1QueryParamsAsStringListMapCommand, + serializeAws_restJson1QueryParamsAsStringListMapCommand, +} from "../protocols/Aws_restJson1"; +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, + MiddlewareStack, + HttpHandlerOptions as __HttpHandlerOptions, + MetadataBearer as __MetadataBearer, + SerdeContext as __SerdeContext, +} from "@aws-sdk/types"; + +export type QueryParamsAsStringListMapCommandInput = QueryParamsAsStringListMapInput; +export type QueryParamsAsStringListMapCommandOutput = __MetadataBearer; + +export class QueryParamsAsStringListMapCommand extends $Command< + QueryParamsAsStringListMapCommandInput, + QueryParamsAsStringListMapCommandOutput, + RestJsonProtocolClientResolvedConfig +> { + // Start section: command_properties + // End section: command_properties + + constructor(readonly input: QueryParamsAsStringListMapCommandInput) { + // 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 = "QueryParamsAsStringListMapCommand"; + const handlerExecutionContext: HandlerExecutionContext = { + logger, + clientName, + commandName, + inputFilterSensitiveLog: QueryParamsAsStringListMapInput.filterSensitiveLog, + outputFilterSensitiveLog: (output: any) => output, + }; + const { requestHandler } = configuration; + return stack.resolve( + (request: FinalizeHandlerArguments) => + requestHandler.handle(request.request as __HttpRequest, options || {}), + handlerExecutionContext + ); + } + + private serialize(input: QueryParamsAsStringListMapCommandInput, context: __SerdeContext): Promise<__HttpRequest> { + return serializeAws_restJson1QueryParamsAsStringListMapCommand(input, context); + } + + private deserialize( + output: __HttpResponse, + context: __SerdeContext + ): Promise { + return deserializeAws_restJson1QueryParamsAsStringListMapCommand(output, context); + } + + // Start section: command_body_extra + // End section: command_body_extra +} diff --git a/protocol_tests/aws-restjson/commands/QueryPrecedenceCommand.ts b/protocol_tests/aws-restjson/commands/QueryPrecedenceCommand.ts new file mode 100644 index 000000000000..404dee92b9d8 --- /dev/null +++ b/protocol_tests/aws-restjson/commands/QueryPrecedenceCommand.ts @@ -0,0 +1,77 @@ +import { RestJsonProtocolClientResolvedConfig, ServiceInputTypes, ServiceOutputTypes } from "../RestJsonProtocolClient"; +import { QueryPrecedenceInput } from "../models/models_0"; +import { + deserializeAws_restJson1QueryPrecedenceCommand, + serializeAws_restJson1QueryPrecedenceCommand, +} from "../protocols/Aws_restJson1"; +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, + MiddlewareStack, + HttpHandlerOptions as __HttpHandlerOptions, + MetadataBearer as __MetadataBearer, + SerdeContext as __SerdeContext, +} from "@aws-sdk/types"; + +export type QueryPrecedenceCommandInput = QueryPrecedenceInput; +export type QueryPrecedenceCommandOutput = __MetadataBearer; + +export class QueryPrecedenceCommand extends $Command< + QueryPrecedenceCommandInput, + QueryPrecedenceCommandOutput, + RestJsonProtocolClientResolvedConfig +> { + // Start section: command_properties + // End section: command_properties + + constructor(readonly input: QueryPrecedenceCommandInput) { + // 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 = "QueryPrecedenceCommand"; + const handlerExecutionContext: HandlerExecutionContext = { + logger, + clientName, + commandName, + inputFilterSensitiveLog: QueryPrecedenceInput.filterSensitiveLog, + outputFilterSensitiveLog: (output: any) => output, + }; + const { requestHandler } = configuration; + return stack.resolve( + (request: FinalizeHandlerArguments) => + requestHandler.handle(request.request as __HttpRequest, options || {}), + handlerExecutionContext + ); + } + + private serialize(input: QueryPrecedenceCommandInput, context: __SerdeContext): Promise<__HttpRequest> { + return serializeAws_restJson1QueryPrecedenceCommand(input, context); + } + + private deserialize(output: __HttpResponse, context: __SerdeContext): Promise { + return deserializeAws_restJson1QueryPrecedenceCommand(output, context); + } + + // Start section: command_body_extra + // End section: command_body_extra +} diff --git a/protocol_tests/aws-restjson/index.ts b/protocol_tests/aws-restjson/index.ts index a8d87b4346ea..8d7567bbe343 100644 --- a/protocol_tests/aws-restjson/index.ts +++ b/protocol_tests/aws-restjson/index.ts @@ -33,6 +33,8 @@ export * from "./commands/NullAndEmptyHeadersClientCommand"; export * from "./commands/NullAndEmptyHeadersServerCommand"; export * from "./commands/OmitsNullSerializesEmptyStringCommand"; export * from "./commands/QueryIdempotencyTokenAutoFillCommand"; +export * from "./commands/QueryParamsAsStringListMapCommand"; +export * from "./commands/QueryPrecedenceCommand"; export * from "./commands/RecursiveShapesCommand"; export * from "./commands/SimpleScalarPropertiesCommand"; export * from "./commands/StreamingTraitsCommand"; diff --git a/protocol_tests/aws-restjson/models/models_0.ts b/protocol_tests/aws-restjson/models/models_0.ts index d80e32d50f5b..bd78bbef95e9 100644 --- a/protocol_tests/aws-restjson/models/models_0.ts +++ b/protocol_tests/aws-restjson/models/models_0.ts @@ -33,6 +33,7 @@ export interface AllQueryStringTypesInput { queryTimestampList?: Date[]; queryEnum?: FooEnum | string; queryEnumList?: (FooEnum | string)[]; + queryParamsMapOfStrings?: { [key: string]: string }; } export namespace AllQueryStringTypesInput { @@ -434,6 +435,16 @@ export namespace JsonTimestampsInputOutput { }); } +export interface RenamedGreeting { + salutation?: string; +} + +export namespace RenamedGreeting { + export const filterSensitiveLog = (obj: RenamedGreeting): any => ({ + ...obj, + }); +} + /** * A union with a representative set of types for members. */ @@ -444,6 +455,7 @@ export type MyUnion = | MyUnion.ListValueMember | MyUnion.MapValueMember | MyUnion.NumberValueMember + | MyUnion.RenamedStructureValueMember | MyUnion.StringValueMember | MyUnion.StructureValueMember | MyUnion.TimestampValueMember @@ -460,6 +472,7 @@ export namespace MyUnion { listValue?: never; mapValue?: never; structureValue?: never; + renamedStructureValue?: never; $unknown?: never; } @@ -473,6 +486,7 @@ export namespace MyUnion { listValue?: never; mapValue?: never; structureValue?: never; + renamedStructureValue?: never; $unknown?: never; } @@ -486,6 +500,7 @@ export namespace MyUnion { listValue?: never; mapValue?: never; structureValue?: never; + renamedStructureValue?: never; $unknown?: never; } @@ -499,6 +514,7 @@ export namespace MyUnion { listValue?: never; mapValue?: never; structureValue?: never; + renamedStructureValue?: never; $unknown?: never; } @@ -512,6 +528,7 @@ export namespace MyUnion { listValue?: never; mapValue?: never; structureValue?: never; + renamedStructureValue?: never; $unknown?: never; } @@ -525,6 +542,7 @@ export namespace MyUnion { listValue?: never; mapValue?: never; structureValue?: never; + renamedStructureValue?: never; $unknown?: never; } @@ -538,6 +556,7 @@ export namespace MyUnion { listValue: string[]; mapValue?: never; structureValue?: never; + renamedStructureValue?: never; $unknown?: never; } @@ -551,6 +570,7 @@ export namespace MyUnion { listValue?: never; mapValue: { [key: string]: string }; structureValue?: never; + renamedStructureValue?: never; $unknown?: never; } @@ -564,6 +584,21 @@ export namespace MyUnion { listValue?: never; mapValue?: never; structureValue: GreetingStruct; + renamedStructureValue?: never; + $unknown?: never; + } + + export interface RenamedStructureValueMember { + stringValue?: never; + booleanValue?: never; + numberValue?: never; + blobValue?: never; + timestampValue?: never; + enumValue?: never; + listValue?: never; + mapValue?: never; + structureValue?: never; + renamedStructureValue: RenamedGreeting; $unknown?: never; } @@ -577,6 +612,7 @@ export namespace MyUnion { listValue?: never; mapValue?: never; structureValue?: never; + renamedStructureValue?: never; $unknown: [string, any]; } @@ -590,6 +626,7 @@ export namespace MyUnion { listValue: (value: string[]) => T; mapValue: (value: { [key: string]: string }) => T; structureValue: (value: GreetingStruct) => T; + renamedStructureValue: (value: RenamedGreeting) => T; _: (name: string, value: any) => T; } @@ -603,6 +640,7 @@ export namespace MyUnion { if (value.listValue !== undefined) return visitor.listValue(value.listValue); if (value.mapValue !== undefined) return visitor.mapValue(value.mapValue); if (value.structureValue !== undefined) return visitor.structureValue(value.structureValue); + if (value.renamedStructureValue !== undefined) return visitor.renamedStructureValue(value.renamedStructureValue); return visitor._(value.$unknown[0], value.$unknown[1]); }; @@ -617,6 +655,8 @@ export namespace MyUnion { if (obj.mapValue !== undefined) return { mapValue: obj.mapValue }; if (obj.structureValue !== undefined) return { structureValue: GreetingStruct.filterSensitiveLog(obj.structureValue) }; + if (obj.renamedStructureValue !== undefined) + return { renamedStructureValue: RenamedGreeting.filterSensitiveLog(obj.renamedStructureValue) }; if (obj.$unknown !== undefined) return { [obj.$unknown[0]]: "UNKNOWN" }; }; } @@ -699,6 +739,28 @@ export namespace QueryIdempotencyTokenAutoFillInput { }); } +export interface QueryParamsAsStringListMapInput { + qux?: string; + foo?: { [key: string]: string[] }; +} + +export namespace QueryParamsAsStringListMapInput { + export const filterSensitiveLog = (obj: QueryParamsAsStringListMapInput): any => ({ + ...obj, + }); +} + +export interface QueryPrecedenceInput { + foo?: string; + baz?: { [key: string]: string }; +} + +export namespace QueryPrecedenceInput { + export const filterSensitiveLog = (obj: QueryPrecedenceInput): any => ({ + ...obj, + }); +} + export interface SimpleScalarPropertiesInputOutput { foo?: string; stringValue?: string; diff --git a/protocol_tests/aws-restjson/protocols/Aws_restJson1.ts b/protocol_tests/aws-restjson/protocols/Aws_restJson1.ts index 4fe1ab945934..dfcbb556ff7c 100644 --- a/protocol_tests/aws-restjson/protocols/Aws_restJson1.ts +++ b/protocol_tests/aws-restjson/protocols/Aws_restJson1.ts @@ -85,6 +85,11 @@ import { QueryIdempotencyTokenAutoFillCommandInput, QueryIdempotencyTokenAutoFillCommandOutput, } from "../commands/QueryIdempotencyTokenAutoFillCommand"; +import { + QueryParamsAsStringListMapCommandInput, + QueryParamsAsStringListMapCommandOutput, +} from "../commands/QueryParamsAsStringListMapCommand"; +import { QueryPrecedenceCommandInput, QueryPrecedenceCommandOutput } from "../commands/QueryPrecedenceCommand"; import { RecursiveShapesCommandInput, RecursiveShapesCommandOutput } from "../commands/RecursiveShapesCommand"; import { SimpleScalarPropertiesCommandInput, @@ -114,6 +119,7 @@ import { NestedPayload, RecursiveShapesInputOutputNested1, RecursiveShapesInputOutputNested2, + RenamedGreeting, StructureListMember, } from "../models/models_0"; import { @@ -144,6 +150,7 @@ export const serializeAws_restJson1AllQueryStringTypesCommand = async ( const headers: any = {}; let resolvedPath = "/AllQueryStringTypesInput"; const query: any = { + ...(input.queryParamsMapOfStrings !== undefined && input.queryParamsMapOfStrings), ...(input.queryString !== undefined && { String: input.queryString }), ...(input.queryStringList !== undefined && { StringList: (input.queryStringList || []).map((_entry) => _entry) }), ...(input.queryStringSet !== undefined && { @@ -1220,6 +1227,54 @@ export const serializeAws_restJson1QueryIdempotencyTokenAutoFillCommand = async }); }; +export const serializeAws_restJson1QueryParamsAsStringListMapCommand = async ( + input: QueryParamsAsStringListMapCommandInput, + context: __SerdeContext +): Promise<__HttpRequest> => { + const headers: any = {}; + let resolvedPath = "/StringListMap"; + const query: any = { + ...(input.foo !== undefined && input.foo), + ...(input.qux !== undefined && { corge: input.qux }), + }; + let body: any; + const { hostname, protocol = "https", port } = await context.endpoint(); + return new __HttpRequest({ + protocol, + hostname, + port, + method: "POST", + headers, + path: resolvedPath, + query, + body, + }); +}; + +export const serializeAws_restJson1QueryPrecedenceCommand = async ( + input: QueryPrecedenceCommandInput, + context: __SerdeContext +): Promise<__HttpRequest> => { + const headers: any = {}; + let resolvedPath = "/Precedence"; + const query: any = { + ...(input.baz !== undefined && input.baz), + ...(input.foo !== undefined && { bar: input.foo }), + }; + let body: any; + const { hostname, protocol = "https", port } = await context.endpoint(); + return new __HttpRequest({ + protocol, + hostname, + port, + method: "POST", + headers, + path: resolvedPath, + query, + body, + }); +}; + export const serializeAws_restJson1RecursiveShapesCommand = async ( input: RecursiveShapesCommandInput, context: __SerdeContext @@ -3114,6 +3169,92 @@ const deserializeAws_restJson1QueryIdempotencyTokenAutoFillCommandError = async return Promise.reject(Object.assign(new Error(message), response)); }; +export const deserializeAws_restJson1QueryParamsAsStringListMapCommand = async ( + output: __HttpResponse, + context: __SerdeContext +): Promise => { + if (output.statusCode !== 200 && output.statusCode >= 300) { + return deserializeAws_restJson1QueryParamsAsStringListMapCommandError(output, context); + } + const contents: QueryParamsAsStringListMapCommandOutput = { + $metadata: deserializeMetadata(output), + }; + await collectBody(output.body, context); + return Promise.resolve(contents); +}; + +const deserializeAws_restJson1QueryParamsAsStringListMapCommandError = 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: string = "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_restJson1QueryPrecedenceCommand = async ( + output: __HttpResponse, + context: __SerdeContext +): Promise => { + if (output.statusCode !== 200 && output.statusCode >= 300) { + return deserializeAws_restJson1QueryPrecedenceCommandError(output, context); + } + const contents: QueryPrecedenceCommandOutput = { + $metadata: deserializeMetadata(output), + }; + await collectBody(output.body, context); + return Promise.resolve(contents); +}; + +const deserializeAws_restJson1QueryPrecedenceCommandError = 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: string = "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_restJson1RecursiveShapesCommand = async ( output: __HttpResponse, context: __SerdeContext @@ -3580,6 +3721,9 @@ const serializeAws_restJson1MyUnion = (input: MyUnion, context: __SerdeContext): listValue: (value) => ({ listValue: serializeAws_restJson1StringList(value, context) }), mapValue: (value) => ({ mapValue: serializeAws_restJson1StringMap(value, context) }), numberValue: (value) => ({ numberValue: value }), + renamedStructureValue: (value) => ({ + renamedStructureValue: serializeAws_restJson1RenamedGreeting(value, context), + }), stringValue: (value) => ({ stringValue: value }), structureValue: (value) => ({ structureValue: serializeAws_restJson1GreetingStruct(value, context) }), timestampValue: (value) => ({ timestampValue: Math.round(value.getTime() / 1000) }), @@ -3677,6 +3821,12 @@ const serializeAws_restJson1StructureListMember = (input: StructureListMember, c }; }; +const serializeAws_restJson1RenamedGreeting = (input: RenamedGreeting, context: __SerdeContext): any => { + return { + ...(input.salutation !== undefined && input.salutation !== null && { salutation: input.salutation }), + }; +}; + const serializeAws_restJson1BooleanList = (input: boolean[], context: __SerdeContext): any => { return input .filter((e: any) => e != null) @@ -3911,6 +4061,11 @@ const deserializeAws_restJson1MyUnion = (output: any, context: __SerdeContext): numberValue: output.numberValue, }; } + if (output.renamedStructureValue !== undefined && output.renamedStructureValue !== null) { + return { + renamedStructureValue: deserializeAws_restJson1RenamedGreeting(output.renamedStructureValue, context), + }; + } if (output.stringValue !== undefined && output.stringValue !== null) { return { stringValue: output.stringValue, @@ -4019,6 +4174,12 @@ const deserializeAws_restJson1StructureListMember = (output: any, context: __Ser } as any; }; +const deserializeAws_restJson1RenamedGreeting = (output: any, context: __SerdeContext): RenamedGreeting => { + return { + salutation: output.salutation !== undefined && output.salutation !== null ? output.salutation : undefined, + } as any; +}; + const deserializeAws_restJson1BooleanList = (output: any, context: __SerdeContext): boolean[] => { return (output || []) .filter((e: any) => e != null) diff --git a/protocol_tests/aws-restjson/tests/functional/restjson1.spec.ts b/protocol_tests/aws-restjson/tests/functional/restjson1.spec.ts index 76354411af00..92fd76480a3f 100644 --- a/protocol_tests/aws-restjson/tests/functional/restjson1.spec.ts +++ b/protocol_tests/aws-restjson/tests/functional/restjson1.spec.ts @@ -31,6 +31,8 @@ import { NoInputAndOutputCommand } from "../../commands/NoInputAndOutputCommand" import { NullAndEmptyHeadersClientCommand } from "../../commands/NullAndEmptyHeadersClientCommand"; import { OmitsNullSerializesEmptyStringCommand } from "../../commands/OmitsNullSerializesEmptyStringCommand"; import { QueryIdempotencyTokenAutoFillCommand } from "../../commands/QueryIdempotencyTokenAutoFillCommand"; +import { QueryParamsAsStringListMapCommand } from "../../commands/QueryParamsAsStringListMapCommand"; +import { QueryPrecedenceCommand } from "../../commands/QueryPrecedenceCommand"; import { RecursiveShapesCommand } from "../../commands/RecursiveShapesCommand"; import { SimpleScalarPropertiesCommand } from "../../commands/SimpleScalarPropertiesCommand"; import { StreamingTraitsCommand } from "../../commands/StreamingTraitsCommand"; @@ -213,6 +215,12 @@ it("RestJsonAllQueryStringTypes:Request", async () => { queryEnum: "Foo", queryEnumList: ["Foo", "Baz", "Bar"], + + queryParamsMapOfStrings: { + QueryParamsStringKeyA: "Foo", + + QueryParamsStringKeyB: "Bar", + } as any, } as any); try { await client.send(command); @@ -262,6 +270,8 @@ it("RestJsonAllQueryStringTypes:Request", async () => { expect(queryString).toContain("EnumList=Foo"); expect(queryString).toContain("EnumList=Baz"); expect(queryString).toContain("EnumList=Bar"); + expect(queryString).toContain("QueryParamsStringKeyA=Foo"); + expect(queryString).toContain("QueryParamsStringKeyB=Bar"); expect(r.body).toBeFalsy(); } @@ -1076,7 +1086,7 @@ it("RestJsonHttpPayloadTraitsWithBlob:Request", async () => { expect(r.body).toBeDefined(); const bodyString = `blobby blob blob`; - const unequalParts: any = compareEquivalentUnknownTypeBodies(client.config, bodyString, r.body); + const unequalParts: any = compareEquivalentOctetStreamBodies(client.config, bodyString, r.body); expect(unequalParts).toBeUndefined(); } }); @@ -1226,7 +1236,7 @@ it("RestJsonHttpPayloadTraitsWithMediaTypeWithBlob:Request", async () => { expect(r.body).toBeDefined(); const bodyString = `blobby blob blob`; - const unequalParts: any = compareEquivalentUnknownTypeBodies(client.config, bodyString, r.body); + const unequalParts: any = compareEquivalentOctetStreamBodies(client.config, bodyString, r.body); expect(unequalParts).toBeUndefined(); } }); @@ -4050,6 +4060,51 @@ it("RestJsonSerializeStructureUnionValue:Request", async () => { } }); +/** + * Serializes a renamed structure union value + */ +it("RestJsonSerializeRenamedStructureUnionValue:Request", async () => { + const client = new RestJsonProtocolClient({ + ...clientParams, + requestHandler: new RequestSerializationTestHandler(), + }); + + const command = new JsonUnionsCommand({ + contents: { + renamedStructureValue: { + salutation: "hello!", + } as any, + } 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("PUT"); + expect(r.path).toBe("/JsonUnions"); + + expect(r.headers["content-type"]).toBeDefined(); + expect(r.headers["content-type"]).toBe("application/json"); + + expect(r.body).toBeDefined(); + const bodyString = `{ + \"contents\": { + \"renamedStructureValue\": { + \"salutation\": \"hello!\" + } + } + }`; + const unequalParts: any = compareEquivalentJsonBodies(bodyString, r.body.toString()); + expect(unequalParts).toBeUndefined(); + } +}); + /** * Deserializes a string union value */ @@ -4701,9 +4756,9 @@ it("RestJsonNullAndEmptyHeaders:Request", async () => { }); /** - * Serializes empty query strings but omits null + * Omits null query values */ -it("RestJsonOmitsNullSerializesEmptyString:Request", async () => { +it("RestJsonOmitsNullQuery:Request", async () => { const client = new RestJsonProtocolClient({ ...clientParams, requestHandler: new RequestSerializationTestHandler(), @@ -4711,7 +4766,34 @@ it("RestJsonOmitsNullSerializesEmptyString:Request", async () => { const command = new OmitsNullSerializesEmptyStringCommand({ nullValue: null, + } 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("/OmitsNullSerializesEmptyString"); + expect(r.body).toBeFalsy(); + } +}); + +/** + * Serializes empty query strings + */ +it("RestJsonSerializesEmptyQueryValue:Request", async () => { + const client = new RestJsonProtocolClient({ + ...clientParams, + requestHandler: new RequestSerializationTestHandler(), + }); + + const command = new OmitsNullSerializesEmptyStringCommand({ emptyString: "", } as any); try { @@ -4798,6 +4880,83 @@ it("RestJsonQueryIdempotencyTokenAutoFillIsSet:Request", async () => { } }); +/** + * Serialize query params from map of list strings + */ +it("RestJsonQueryParamsStringListMap:Request", async () => { + const client = new RestJsonProtocolClient({ + ...clientParams, + requestHandler: new RequestSerializationTestHandler(), + }); + + const command = new QueryParamsAsStringListMapCommand({ + qux: "named", + + foo: { + baz: ["bar", "qux"], + } 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("/StringListMap"); + + const queryString = buildQueryString(r.query); + expect(queryString).toContain("corge=named"); + expect(queryString).toContain("baz=bar"); + expect(queryString).toContain("baz=qux"); + + expect(r.body).toBeFalsy(); + } +}); + +/** + * Prefer named query parameters when serializing + */ +it("RestJsonQueryPrecedence:Request", async () => { + const client = new RestJsonProtocolClient({ + ...clientParams, + requestHandler: new RequestSerializationTestHandler(), + }); + + const command = new QueryPrecedenceCommand({ + foo: "named", + + baz: { + bar: "fromMap", + + qux: "alsoFromMap", + } 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("/Precedence"); + + const queryString = buildQueryString(r.query); + expect(queryString).toContain("bar=named"); + expect(queryString).toContain("qux=alsoFromMap"); + + expect(r.body).toBeFalsy(); + } +}); + /** * Serializes recursive structures */ @@ -5020,7 +5179,7 @@ it("RestJsonDoesntSerializeNullStructureValues:Request", async () => { expect(r.body).toBeDefined(); const bodyString = `{}`; - const unequalParts: any = compareEquivalentUnknownTypeBodies(client.config, bodyString, r.body); + const unequalParts: any = compareEquivalentJsonBodies(bodyString, r.body.toString()); expect(unequalParts).toBeUndefined(); } }); @@ -5157,7 +5316,7 @@ it("RestJsonStreamingTraitsWithBlob:Request", async () => { expect(r.body).toBeDefined(); const bodyString = `blobby blob blob`; - const unequalParts: any = compareEquivalentUnknownTypeBodies(client.config, bodyString, r.body); + const unequalParts: any = compareEquivalentOctetStreamBodies(client.config, bodyString, r.body); expect(unequalParts).toBeUndefined(); } }); @@ -5318,7 +5477,7 @@ it("RestJsonStreamingTraitsRequireLengthWithBlob:Request", async () => { expect(r.body).toBeDefined(); const bodyString = `blobby blob blob`; - const unequalParts: any = compareEquivalentUnknownTypeBodies(client.config, bodyString, r.body); + const unequalParts: any = compareEquivalentOctetStreamBodies(client.config, bodyString, r.body); expect(unequalParts).toBeUndefined(); } }); @@ -5478,7 +5637,7 @@ it("RestJsonStreamingTraitsWithMediaTypeWithBlob:Request", async () => { expect(r.body).toBeDefined(); const bodyString = `blobby blob blob`; - const unequalParts: any = compareEquivalentUnknownTypeBodies(client.config, bodyString, r.body); + const unequalParts: any = compareEquivalentOctetStreamBodies(client.config, bodyString, r.body); expect(unequalParts).toBeUndefined(); } }); @@ -5656,15 +5815,9 @@ const compareEquivalentJsonBodies = (expectedBody: string, generatedBody: string * Returns a map of key names that were un-equal to value objects showing the * discrepancies between the components. */ -const compareEquivalentUnknownTypeBodies = ( - config: any, - expectedBody: string, - generatedBody: string | Uint8Array -): Object => { +const compareEquivalentOctetStreamBodies = (config: any, expectedBody: string, generatedBody: Uint8Array): Object => { const expectedParts = { Value: expectedBody }; - const generatedParts = { - Value: generatedBody instanceof Uint8Array ? config.utf8Encoder(generatedBody) : generatedBody, - }; + const generatedParts = { Value: config.utf8Encoder(generatedBody) }; return compareParts(expectedParts, generatedParts); }; diff --git a/protocol_tests/aws-restxml/RestXmlProtocol.ts b/protocol_tests/aws-restxml/RestXmlProtocol.ts index 7733fdc270de..20d05239c04a 100644 --- a/protocol_tests/aws-restxml/RestXmlProtocol.ts +++ b/protocol_tests/aws-restxml/RestXmlProtocol.ts @@ -149,6 +149,16 @@ import { QueryIdempotencyTokenAutoFillCommandInput, QueryIdempotencyTokenAutoFillCommandOutput, } from "./commands/QueryIdempotencyTokenAutoFillCommand"; +import { + QueryParamsAsStringListMapCommand, + QueryParamsAsStringListMapCommandInput, + QueryParamsAsStringListMapCommandOutput, +} from "./commands/QueryParamsAsStringListMapCommand"; +import { + QueryPrecedenceCommand, + QueryPrecedenceCommandInput, + QueryPrecedenceCommandOutput, +} from "./commands/QueryPrecedenceCommand"; import { RecursiveShapesCommand, RecursiveShapesCommandInput, @@ -1201,6 +1211,64 @@ export class RestXmlProtocol extends RestXmlProtocolClient { } } + public queryParamsAsStringListMap( + args: QueryParamsAsStringListMapCommandInput, + options?: __HttpHandlerOptions + ): Promise; + public queryParamsAsStringListMap( + args: QueryParamsAsStringListMapCommandInput, + cb: (err: any, data?: QueryParamsAsStringListMapCommandOutput) => void + ): void; + public queryParamsAsStringListMap( + args: QueryParamsAsStringListMapCommandInput, + options: __HttpHandlerOptions, + cb: (err: any, data?: QueryParamsAsStringListMapCommandOutput) => void + ): void; + public queryParamsAsStringListMap( + args: QueryParamsAsStringListMapCommandInput, + optionsOrCb?: __HttpHandlerOptions | ((err: any, data?: QueryParamsAsStringListMapCommandOutput) => void), + cb?: (err: any, data?: QueryParamsAsStringListMapCommandOutput) => void + ): Promise | void { + const command = new QueryParamsAsStringListMapCommand(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); + } + } + + public queryPrecedence( + args: QueryPrecedenceCommandInput, + options?: __HttpHandlerOptions + ): Promise; + public queryPrecedence( + args: QueryPrecedenceCommandInput, + cb: (err: any, data?: QueryPrecedenceCommandOutput) => void + ): void; + public queryPrecedence( + args: QueryPrecedenceCommandInput, + options: __HttpHandlerOptions, + cb: (err: any, data?: QueryPrecedenceCommandOutput) => void + ): void; + public queryPrecedence( + args: QueryPrecedenceCommandInput, + optionsOrCb?: __HttpHandlerOptions | ((err: any, data?: QueryPrecedenceCommandOutput) => void), + cb?: (err: any, data?: QueryPrecedenceCommandOutput) => void + ): Promise | void { + const command = new QueryPrecedenceCommand(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); + } + } + /** * Recursive shapes */ diff --git a/protocol_tests/aws-restxml/RestXmlProtocolClient.ts b/protocol_tests/aws-restxml/RestXmlProtocolClient.ts index b860a67cb312..9613cd2d0a3d 100644 --- a/protocol_tests/aws-restxml/RestXmlProtocolClient.ts +++ b/protocol_tests/aws-restxml/RestXmlProtocolClient.ts @@ -94,6 +94,11 @@ import { QueryIdempotencyTokenAutoFillCommandInput, QueryIdempotencyTokenAutoFillCommandOutput, } from "./commands/QueryIdempotencyTokenAutoFillCommand"; +import { + QueryParamsAsStringListMapCommandInput, + QueryParamsAsStringListMapCommandOutput, +} from "./commands/QueryParamsAsStringListMapCommand"; +import { QueryPrecedenceCommandInput, QueryPrecedenceCommandOutput } from "./commands/QueryPrecedenceCommand"; import { RecursiveShapesCommandInput, RecursiveShapesCommandOutput } from "./commands/RecursiveShapesCommand"; import { SimpleScalarPropertiesCommandInput, @@ -202,6 +207,8 @@ export type ServiceInputTypes = | NullAndEmptyHeadersServerCommandInput | OmitsNullSerializesEmptyStringCommandInput | QueryIdempotencyTokenAutoFillCommandInput + | QueryParamsAsStringListMapCommandInput + | QueryPrecedenceCommandInput | RecursiveShapesCommandInput | SimpleScalarPropertiesCommandInput | TimestampFormatHeadersCommandInput @@ -251,6 +258,8 @@ export type ServiceOutputTypes = | NullAndEmptyHeadersServerCommandOutput | OmitsNullSerializesEmptyStringCommandOutput | QueryIdempotencyTokenAutoFillCommandOutput + | QueryParamsAsStringListMapCommandOutput + | QueryPrecedenceCommandOutput | RecursiveShapesCommandOutput | SimpleScalarPropertiesCommandOutput | TimestampFormatHeadersCommandOutput diff --git a/protocol_tests/aws-restxml/commands/QueryParamsAsStringListMapCommand.ts b/protocol_tests/aws-restxml/commands/QueryParamsAsStringListMapCommand.ts new file mode 100644 index 000000000000..0c8a9a6aea28 --- /dev/null +++ b/protocol_tests/aws-restxml/commands/QueryParamsAsStringListMapCommand.ts @@ -0,0 +1,80 @@ +import { RestXmlProtocolClientResolvedConfig, ServiceInputTypes, ServiceOutputTypes } from "../RestXmlProtocolClient"; +import { QueryParamsAsStringListMapInput } from "../models/models_0"; +import { + deserializeAws_restXmlQueryParamsAsStringListMapCommand, + serializeAws_restXmlQueryParamsAsStringListMapCommand, +} from "../protocols/Aws_restXml"; +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, + MiddlewareStack, + HttpHandlerOptions as __HttpHandlerOptions, + MetadataBearer as __MetadataBearer, + SerdeContext as __SerdeContext, +} from "@aws-sdk/types"; + +export type QueryParamsAsStringListMapCommandInput = QueryParamsAsStringListMapInput; +export type QueryParamsAsStringListMapCommandOutput = __MetadataBearer; + +export class QueryParamsAsStringListMapCommand extends $Command< + QueryParamsAsStringListMapCommandInput, + QueryParamsAsStringListMapCommandOutput, + RestXmlProtocolClientResolvedConfig +> { + // Start section: command_properties + // End section: command_properties + + constructor(readonly input: QueryParamsAsStringListMapCommandInput) { + // Start section: command_constructor + super(); + // End section: command_constructor + } + + /** + * @internal + */ + resolveMiddleware( + clientStack: MiddlewareStack, + configuration: RestXmlProtocolClientResolvedConfig, + options?: __HttpHandlerOptions + ): Handler { + this.middlewareStack.use(getSerdePlugin(configuration, this.serialize, this.deserialize)); + + const stack = clientStack.concat(this.middlewareStack); + + const { logger } = configuration; + const clientName = "RestXmlProtocolClient"; + const commandName = "QueryParamsAsStringListMapCommand"; + const handlerExecutionContext: HandlerExecutionContext = { + logger, + clientName, + commandName, + inputFilterSensitiveLog: QueryParamsAsStringListMapInput.filterSensitiveLog, + outputFilterSensitiveLog: (output: any) => output, + }; + const { requestHandler } = configuration; + return stack.resolve( + (request: FinalizeHandlerArguments) => + requestHandler.handle(request.request as __HttpRequest, options || {}), + handlerExecutionContext + ); + } + + private serialize(input: QueryParamsAsStringListMapCommandInput, context: __SerdeContext): Promise<__HttpRequest> { + return serializeAws_restXmlQueryParamsAsStringListMapCommand(input, context); + } + + private deserialize( + output: __HttpResponse, + context: __SerdeContext + ): Promise { + return deserializeAws_restXmlQueryParamsAsStringListMapCommand(output, context); + } + + // Start section: command_body_extra + // End section: command_body_extra +} diff --git a/protocol_tests/aws-restxml/commands/QueryPrecedenceCommand.ts b/protocol_tests/aws-restxml/commands/QueryPrecedenceCommand.ts new file mode 100644 index 000000000000..a12138a2eb94 --- /dev/null +++ b/protocol_tests/aws-restxml/commands/QueryPrecedenceCommand.ts @@ -0,0 +1,77 @@ +import { RestXmlProtocolClientResolvedConfig, ServiceInputTypes, ServiceOutputTypes } from "../RestXmlProtocolClient"; +import { QueryPrecedenceInput } from "../models/models_0"; +import { + deserializeAws_restXmlQueryPrecedenceCommand, + serializeAws_restXmlQueryPrecedenceCommand, +} from "../protocols/Aws_restXml"; +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, + MiddlewareStack, + HttpHandlerOptions as __HttpHandlerOptions, + MetadataBearer as __MetadataBearer, + SerdeContext as __SerdeContext, +} from "@aws-sdk/types"; + +export type QueryPrecedenceCommandInput = QueryPrecedenceInput; +export type QueryPrecedenceCommandOutput = __MetadataBearer; + +export class QueryPrecedenceCommand extends $Command< + QueryPrecedenceCommandInput, + QueryPrecedenceCommandOutput, + RestXmlProtocolClientResolvedConfig +> { + // Start section: command_properties + // End section: command_properties + + constructor(readonly input: QueryPrecedenceCommandInput) { + // Start section: command_constructor + super(); + // End section: command_constructor + } + + /** + * @internal + */ + resolveMiddleware( + clientStack: MiddlewareStack, + configuration: RestXmlProtocolClientResolvedConfig, + options?: __HttpHandlerOptions + ): Handler { + this.middlewareStack.use(getSerdePlugin(configuration, this.serialize, this.deserialize)); + + const stack = clientStack.concat(this.middlewareStack); + + const { logger } = configuration; + const clientName = "RestXmlProtocolClient"; + const commandName = "QueryPrecedenceCommand"; + const handlerExecutionContext: HandlerExecutionContext = { + logger, + clientName, + commandName, + inputFilterSensitiveLog: QueryPrecedenceInput.filterSensitiveLog, + outputFilterSensitiveLog: (output: any) => output, + }; + const { requestHandler } = configuration; + return stack.resolve( + (request: FinalizeHandlerArguments) => + requestHandler.handle(request.request as __HttpRequest, options || {}), + handlerExecutionContext + ); + } + + private serialize(input: QueryPrecedenceCommandInput, context: __SerdeContext): Promise<__HttpRequest> { + return serializeAws_restXmlQueryPrecedenceCommand(input, context); + } + + private deserialize(output: __HttpResponse, context: __SerdeContext): Promise { + return deserializeAws_restXmlQueryPrecedenceCommand(output, context); + } + + // Start section: command_body_extra + // End section: command_body_extra +} diff --git a/protocol_tests/aws-restxml/index.ts b/protocol_tests/aws-restxml/index.ts index 3ca864c73e05..7b2979267bd6 100644 --- a/protocol_tests/aws-restxml/index.ts +++ b/protocol_tests/aws-restxml/index.ts @@ -30,6 +30,8 @@ export * from "./commands/NullAndEmptyHeadersClientCommand"; export * from "./commands/NullAndEmptyHeadersServerCommand"; export * from "./commands/OmitsNullSerializesEmptyStringCommand"; export * from "./commands/QueryIdempotencyTokenAutoFillCommand"; +export * from "./commands/QueryParamsAsStringListMapCommand"; +export * from "./commands/QueryPrecedenceCommand"; export * from "./commands/RecursiveShapesCommand"; export * from "./commands/SimpleScalarPropertiesCommand"; export * from "./commands/TimestampFormatHeadersCommand"; diff --git a/protocol_tests/aws-restxml/models/models_0.ts b/protocol_tests/aws-restxml/models/models_0.ts index 264724d0449e..e4f13de0880a 100644 --- a/protocol_tests/aws-restxml/models/models_0.ts +++ b/protocol_tests/aws-restxml/models/models_0.ts @@ -28,6 +28,7 @@ export interface AllQueryStringTypesInput { queryTimestampList?: Date[]; queryEnum?: FooEnum | string; queryEnumList?: (FooEnum | string)[]; + queryParamsMapOfStrings?: { [key: string]: string }; } export namespace AllQueryStringTypesInput { @@ -426,6 +427,28 @@ export namespace QueryIdempotencyTokenAutoFillInput { }); } +export interface QueryParamsAsStringListMapInput { + qux?: string; + foo?: { [key: string]: string[] }; +} + +export namespace QueryParamsAsStringListMapInput { + export const filterSensitiveLog = (obj: QueryParamsAsStringListMapInput): any => ({ + ...obj, + }); +} + +export interface QueryPrecedenceInput { + foo?: string; + baz?: { [key: string]: string }; +} + +export namespace QueryPrecedenceInput { + export const filterSensitiveLog = (obj: QueryPrecedenceInput): any => ({ + ...obj, + }); +} + export interface SimpleScalarPropertiesInputOutput { foo?: string; stringValue?: string; diff --git a/protocol_tests/aws-restxml/protocols/Aws_restXml.ts b/protocol_tests/aws-restxml/protocols/Aws_restXml.ts index e7c698f9c169..f6ab60130f7e 100644 --- a/protocol_tests/aws-restxml/protocols/Aws_restXml.ts +++ b/protocol_tests/aws-restxml/protocols/Aws_restXml.ts @@ -94,6 +94,11 @@ import { QueryIdempotencyTokenAutoFillCommandInput, QueryIdempotencyTokenAutoFillCommandOutput, } from "../commands/QueryIdempotencyTokenAutoFillCommand"; +import { + QueryParamsAsStringListMapCommandInput, + QueryParamsAsStringListMapCommandOutput, +} from "../commands/QueryParamsAsStringListMapCommand"; +import { QueryPrecedenceCommandInput, QueryPrecedenceCommandOutput } from "../commands/QueryPrecedenceCommand"; import { RecursiveShapesCommandInput, RecursiveShapesCommandOutput } from "../commands/RecursiveShapesCommand"; import { SimpleScalarPropertiesCommandInput, @@ -168,6 +173,7 @@ export const serializeAws_restXmlAllQueryStringTypesCommand = async ( const headers: any = {}; let resolvedPath = "/AllQueryStringTypesInput"; const query: any = { + ...(input.queryParamsMapOfStrings !== undefined && input.queryParamsMapOfStrings), ...(input.queryString !== undefined && { String: input.queryString }), ...(input.queryStringList !== undefined && { StringList: (input.queryStringList || []).map((_entry) => _entry) }), ...(input.queryStringSet !== undefined && { @@ -1136,6 +1142,54 @@ export const serializeAws_restXmlQueryIdempotencyTokenAutoFillCommand = async ( }); }; +export const serializeAws_restXmlQueryParamsAsStringListMapCommand = async ( + input: QueryParamsAsStringListMapCommandInput, + context: __SerdeContext +): Promise<__HttpRequest> => { + const headers: any = {}; + let resolvedPath = "/StringListMap"; + const query: any = { + ...(input.foo !== undefined && input.foo), + ...(input.qux !== undefined && { corge: input.qux }), + }; + let body: any; + const { hostname, protocol = "https", port } = await context.endpoint(); + return new __HttpRequest({ + protocol, + hostname, + port, + method: "POST", + headers, + path: resolvedPath, + query, + body, + }); +}; + +export const serializeAws_restXmlQueryPrecedenceCommand = async ( + input: QueryPrecedenceCommandInput, + context: __SerdeContext +): Promise<__HttpRequest> => { + const headers: any = {}; + let resolvedPath = "/Precedence"; + const query: any = { + ...(input.baz !== undefined && input.baz), + ...(input.foo !== undefined && { bar: input.foo }), + }; + let body: any; + const { hostname, protocol = "https", port } = await context.endpoint(); + return new __HttpRequest({ + protocol, + hostname, + port, + method: "POST", + headers, + path: resolvedPath, + query, + body, + }); +}; + export const serializeAws_restXmlRecursiveShapesCommand = async ( input: RecursiveShapesCommandInput, context: __SerdeContext @@ -3397,6 +3451,92 @@ const deserializeAws_restXmlQueryIdempotencyTokenAutoFillCommandError = async ( return Promise.reject(Object.assign(new Error(message), response)); }; +export const deserializeAws_restXmlQueryParamsAsStringListMapCommand = async ( + output: __HttpResponse, + context: __SerdeContext +): Promise => { + if (output.statusCode !== 200 && output.statusCode >= 300) { + return deserializeAws_restXmlQueryParamsAsStringListMapCommandError(output, context); + } + const contents: QueryParamsAsStringListMapCommandOutput = { + $metadata: deserializeMetadata(output), + }; + await collectBody(output.body, context); + return Promise.resolve(contents); +}; + +const deserializeAws_restXmlQueryParamsAsStringListMapCommandError = 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: string = "UnknownError"; + errorCode = loadRestXmlErrorCode(output, parsedOutput.body); + switch (errorCode) { + default: + const parsedBody = parsedOutput.body; + errorCode = parsedBody.Error.code || parsedBody.Error.Code || errorCode; + response = { + ...parsedBody.Error, + name: `${errorCode}`, + message: parsedBody.Error.message || parsedBody.Error.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_restXmlQueryPrecedenceCommand = async ( + output: __HttpResponse, + context: __SerdeContext +): Promise => { + if (output.statusCode !== 200 && output.statusCode >= 300) { + return deserializeAws_restXmlQueryPrecedenceCommandError(output, context); + } + const contents: QueryPrecedenceCommandOutput = { + $metadata: deserializeMetadata(output), + }; + await collectBody(output.body, context); + return Promise.resolve(contents); +}; + +const deserializeAws_restXmlQueryPrecedenceCommandError = 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: string = "UnknownError"; + errorCode = loadRestXmlErrorCode(output, parsedOutput.body); + switch (errorCode) { + default: + const parsedBody = parsedOutput.body; + errorCode = parsedBody.Error.code || parsedBody.Error.Code || errorCode; + response = { + ...parsedBody.Error, + name: `${errorCode}`, + message: parsedBody.Error.message || parsedBody.Error.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_restXmlRecursiveShapesCommand = async ( output: __HttpResponse, context: __SerdeContext diff --git a/protocol_tests/aws-restxml/tests/functional/restxml.spec.ts b/protocol_tests/aws-restxml/tests/functional/restxml.spec.ts index d3a9109ed4b1..052b5408af1b 100644 --- a/protocol_tests/aws-restxml/tests/functional/restxml.spec.ts +++ b/protocol_tests/aws-restxml/tests/functional/restxml.spec.ts @@ -28,6 +28,8 @@ import { NoInputAndOutputCommand } from "../../commands/NoInputAndOutputCommand" import { NullAndEmptyHeadersClientCommand } from "../../commands/NullAndEmptyHeadersClientCommand"; import { OmitsNullSerializesEmptyStringCommand } from "../../commands/OmitsNullSerializesEmptyStringCommand"; import { QueryIdempotencyTokenAutoFillCommand } from "../../commands/QueryIdempotencyTokenAutoFillCommand"; +import { QueryParamsAsStringListMapCommand } from "../../commands/QueryParamsAsStringListMapCommand"; +import { QueryPrecedenceCommand } from "../../commands/QueryPrecedenceCommand"; import { RecursiveShapesCommand } from "../../commands/RecursiveShapesCommand"; import { SimpleScalarPropertiesCommand } from "../../commands/SimpleScalarPropertiesCommand"; import { TimestampFormatHeadersCommand } from "../../commands/TimestampFormatHeadersCommand"; @@ -222,6 +224,12 @@ it("AllQueryStringTypes:Request", async () => { queryEnum: "Foo", queryEnumList: ["Foo", "Baz", "Bar"], + + queryParamsMapOfStrings: { + QueryParamsStringKeyA: "Foo", + + QueryParamsStringKeyB: "Bar", + } as any, } as any); try { await client.send(command); @@ -271,6 +279,8 @@ it("AllQueryStringTypes:Request", async () => { expect(queryString).toContain("EnumList=Foo"); expect(queryString).toContain("EnumList=Baz"); expect(queryString).toContain("EnumList=Bar"); + expect(queryString).toContain("QueryParamsStringKeyA=Foo"); + expect(queryString).toContain("QueryParamsStringKeyB=Bar"); expect(r.body).toBeFalsy(); } @@ -2407,9 +2417,9 @@ it("NullAndEmptyHeaders:Request", async () => { }); /** - * Serializes empty query strings but omits null + * Omits null query values */ -it("OmitsNullSerializesEmptyString:Request", async () => { +it("RestXmlOmitsNullQuery:Request", async () => { const client = new RestXmlProtocolClient({ ...clientParams, requestHandler: new RequestSerializationTestHandler(), @@ -2417,7 +2427,34 @@ it("OmitsNullSerializesEmptyString:Request", async () => { const command = new OmitsNullSerializesEmptyStringCommand({ nullValue: null, + } 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("/OmitsNullSerializesEmptyString"); + expect(r.body).toBeFalsy(); + } +}); + +/** + * Serializes empty query strings + */ +it("RestXmlSerializesEmptyString:Request", async () => { + const client = new RestXmlProtocolClient({ + ...clientParams, + requestHandler: new RequestSerializationTestHandler(), + }); + + const command = new OmitsNullSerializesEmptyStringCommand({ emptyString: "", } as any); try { @@ -2504,6 +2541,83 @@ it("QueryIdempotencyTokenAutoFillIsSet:Request", async () => { } }); +/** + * Serialize query params from map of list strings + */ +it("RestXmlQueryParamsStringListMap:Request", async () => { + const client = new RestXmlProtocolClient({ + ...clientParams, + requestHandler: new RequestSerializationTestHandler(), + }); + + const command = new QueryParamsAsStringListMapCommand({ + qux: "named", + + foo: { + baz: ["bar", "qux"], + } 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("/StringListMap"); + + const queryString = buildQueryString(r.query); + expect(queryString).toContain("corge=named"); + expect(queryString).toContain("baz=bar"); + expect(queryString).toContain("baz=qux"); + + expect(r.body).toBeFalsy(); + } +}); + +/** + * Prefer named query parameters when serializing + */ +it("RestXmlQueryPrecedence:Request", async () => { + const client = new RestXmlProtocolClient({ + ...clientParams, + requestHandler: new RequestSerializationTestHandler(), + }); + + const command = new QueryPrecedenceCommand({ + foo: "named", + + baz: { + bar: "fromMap", + + qux: "alsoFromMap", + } 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("/Precedence"); + + const queryString = buildQueryString(r.query); + expect(queryString).toContain("bar=named"); + expect(queryString).toContain("qux=alsoFromMap"); + + expect(r.body).toBeFalsy(); + } +}); + /** * Serializes recursive structures */