diff --git a/.changeset/large-badgers-cheer.md b/.changeset/large-badgers-cheer.md new file mode 100644 index 00000000000..b15033610a7 --- /dev/null +++ b/.changeset/large-badgers-cheer.md @@ -0,0 +1,6 @@ +--- +"@smithy/types": minor +"@smithy/core": minor +--- + +add MetricsRecorder support to clients: a call-site recorder passed to client.send is placed on the handler execution context for middleware to consume diff --git a/packages/core/src/submodules/client/smithy-client/command.spec.ts b/packages/core/src/submodules/client/smithy-client/command.spec.ts index 0651cfcbd50..e5bbb08fb97 100644 --- a/packages/core/src/submodules/client/smithy-client/command.spec.ts +++ b/packages/core/src/submodules/client/smithy-client/command.spec.ts @@ -90,4 +90,32 @@ describe(Command.name, () => { requestTimeout: 5000, }); }); + + it("places the call-site recorder on the handler execution context", async () => { + let capturedContext: any; + + class MyCommand extends Command.classBuilder() + .m(function () { + return []; + }) + .s("MyClient", "MyOp", {}) + .n("MyClient", "MyOp") + .f() + .ser(async (_) => ({ ..._, headers: {}, method: "POST", protocol: "https:", hostname: "localhost", path: "/" })) + .de(async (_) => ({ $metadata: {} })) + .build() {} + + const recorder = { addCount: vi.fn() }; + const cmd = new MyCommand({}); + cmd.resolveMiddleware( + { concat: () => ({ resolve: (fn: any, ctx: any) => ((capturedContext = ctx), fn) }) } as any, + { + logger: {} as any, + requestHandler: { handle: vi.fn().mockResolvedValue({ response: {} }) }, + }, + { recorder } + ); + + expect(capturedContext.recorder).toBe(recorder); + }); }); diff --git a/packages/core/src/submodules/client/smithy-client/command.ts b/packages/core/src/submodules/client/smithy-client/command.ts index c7d885f8a3b..a0c7ccfb3fd 100644 --- a/packages/core/src/submodules/client/smithy-client/command.ts +++ b/packages/core/src/submodules/client/smithy-client/command.ts @@ -91,6 +91,7 @@ export abstract class Command< ...smithyContext, }, ...additionalContext, + recorder: options?.recorder, }; const { requestHandler } = configuration; let requestOptions = options ?? {}; diff --git a/packages/types/src/middleware.ts b/packages/types/src/middleware.ts index f6933158795..e74ff5c7e3b 100644 --- a/packages/types/src/middleware.ts +++ b/packages/types/src/middleware.ts @@ -4,6 +4,7 @@ import type { Command } from "./command"; import type { EndpointV2 } from "./endpoint"; import type { SmithyFeatures } from "./feature-ids"; import type { Logger } from "./logger"; +import type { MetricsRecorder } from "./metrics"; import type { UserAgent } from "./util"; /** @@ -584,6 +585,14 @@ export interface HandlerExecutionContext { [key: string]: unknown; }; + /** + * A per-request {@link MetricsRecorder} supplied at the `client.send(command, { recorder })` + * call site. When present, a metrics middleware records into this recorder instead of one + * minted from a factory, letting a caller (e.g. a server handler) fold the client's request + * metrics into its own. + */ + recorder?: MetricsRecorder; + /** * Set by some operations which instructs the retry behavior to backoff * after a failed request even when no further retry (of the same request) is expected. diff --git a/private/my-local-model-schema/src/XYZService.ts b/private/my-local-model-schema/src/XYZService.ts index 970f569f923..f8b8391627d 100644 --- a/private/my-local-model-schema/src/XYZService.ts +++ b/private/my-local-model-schema/src/XYZService.ts @@ -2,6 +2,7 @@ import { type WaiterResult, createAggregatedClient } from "@smithy/core/client"; import type { HttpHandlerOptions as __HttpHandlerOptions, + MetricsRecorder as __MetricsRecorder, PaginationConfiguration, Paginator, WaiterConfiguration, @@ -58,13 +59,20 @@ const waiters = { waitUntilNumbersWhatDoTheyDoAnyway, }; +/** + * @public + */ +export interface XYZServiceRequestOptions extends __HttpHandlerOptions { + recorder?: __MetricsRecorder; +} + export interface XYZService { /** * @see {@link HttpLabelCommandCommand} */ httpLabelCommand( args: HttpLabelCommandCommandInput, - options?: __HttpHandlerOptions + options?: XYZServiceRequestOptions ): Promise; httpLabelCommand( args: HttpLabelCommandCommandInput, @@ -72,7 +80,7 @@ export interface XYZService { ): void; httpLabelCommand( args: HttpLabelCommandCommandInput, - options: __HttpHandlerOptions, + options: XYZServiceRequestOptions, cb: (err: any, data?: HttpLabelCommandCommandOutput) => void ): void; @@ -82,7 +90,7 @@ export interface XYZService { camelCaseOperation(): Promise; camelCaseOperation( args: CamelCaseOperationCommandInput, - options?: __HttpHandlerOptions + options?: XYZServiceRequestOptions ): Promise; camelCaseOperation( args: CamelCaseOperationCommandInput, @@ -90,7 +98,7 @@ export interface XYZService { ): void; camelCaseOperation( args: CamelCaseOperationCommandInput, - options: __HttpHandlerOptions, + options: XYZServiceRequestOptions, cb: (err: any, data?: CamelCaseOperationCommandOutput) => void ): void; @@ -100,7 +108,7 @@ export interface XYZService { getNumbers(): Promise; getNumbers( args: GetNumbersCommandInput, - options?: __HttpHandlerOptions + options?: XYZServiceRequestOptions ): Promise; getNumbers( args: GetNumbersCommandInput, @@ -108,7 +116,7 @@ export interface XYZService { ): void; getNumbers( args: GetNumbersCommandInput, - options: __HttpHandlerOptions, + options: XYZServiceRequestOptions, cb: (err: any, data?: GetNumbersCommandOutput) => void ): void; @@ -117,7 +125,7 @@ export interface XYZService { */ hostPrefixOperation( args: HostPrefixOperationCommandInput, - options?: __HttpHandlerOptions + options?: XYZServiceRequestOptions ): Promise; hostPrefixOperation( args: HostPrefixOperationCommandInput, @@ -125,7 +133,7 @@ export interface XYZService { ): void; hostPrefixOperation( args: HostPrefixOperationCommandInput, - options: __HttpHandlerOptions, + options: XYZServiceRequestOptions, cb: (err: any, data?: HostPrefixOperationCommandOutput) => void ): void; @@ -135,7 +143,7 @@ export interface XYZService { tradeEventStream(): Promise; tradeEventStream( args: TradeEventStreamCommandInput, - options?: __HttpHandlerOptions + options?: XYZServiceRequestOptions ): Promise; tradeEventStream( args: TradeEventStreamCommandInput, @@ -143,7 +151,7 @@ export interface XYZService { ): void; tradeEventStream( args: TradeEventStreamCommandInput, - options: __HttpHandlerOptions, + options: XYZServiceRequestOptions, cb: (err: any, data?: TradeEventStreamCommandOutput) => void ): void; diff --git a/private/my-local-model/src/XYZService.ts b/private/my-local-model/src/XYZService.ts index 970f569f923..f8b8391627d 100644 --- a/private/my-local-model/src/XYZService.ts +++ b/private/my-local-model/src/XYZService.ts @@ -2,6 +2,7 @@ import { type WaiterResult, createAggregatedClient } from "@smithy/core/client"; import type { HttpHandlerOptions as __HttpHandlerOptions, + MetricsRecorder as __MetricsRecorder, PaginationConfiguration, Paginator, WaiterConfiguration, @@ -58,13 +59,20 @@ const waiters = { waitUntilNumbersWhatDoTheyDoAnyway, }; +/** + * @public + */ +export interface XYZServiceRequestOptions extends __HttpHandlerOptions { + recorder?: __MetricsRecorder; +} + export interface XYZService { /** * @see {@link HttpLabelCommandCommand} */ httpLabelCommand( args: HttpLabelCommandCommandInput, - options?: __HttpHandlerOptions + options?: XYZServiceRequestOptions ): Promise; httpLabelCommand( args: HttpLabelCommandCommandInput, @@ -72,7 +80,7 @@ export interface XYZService { ): void; httpLabelCommand( args: HttpLabelCommandCommandInput, - options: __HttpHandlerOptions, + options: XYZServiceRequestOptions, cb: (err: any, data?: HttpLabelCommandCommandOutput) => void ): void; @@ -82,7 +90,7 @@ export interface XYZService { camelCaseOperation(): Promise; camelCaseOperation( args: CamelCaseOperationCommandInput, - options?: __HttpHandlerOptions + options?: XYZServiceRequestOptions ): Promise; camelCaseOperation( args: CamelCaseOperationCommandInput, @@ -90,7 +98,7 @@ export interface XYZService { ): void; camelCaseOperation( args: CamelCaseOperationCommandInput, - options: __HttpHandlerOptions, + options: XYZServiceRequestOptions, cb: (err: any, data?: CamelCaseOperationCommandOutput) => void ): void; @@ -100,7 +108,7 @@ export interface XYZService { getNumbers(): Promise; getNumbers( args: GetNumbersCommandInput, - options?: __HttpHandlerOptions + options?: XYZServiceRequestOptions ): Promise; getNumbers( args: GetNumbersCommandInput, @@ -108,7 +116,7 @@ export interface XYZService { ): void; getNumbers( args: GetNumbersCommandInput, - options: __HttpHandlerOptions, + options: XYZServiceRequestOptions, cb: (err: any, data?: GetNumbersCommandOutput) => void ): void; @@ -117,7 +125,7 @@ export interface XYZService { */ hostPrefixOperation( args: HostPrefixOperationCommandInput, - options?: __HttpHandlerOptions + options?: XYZServiceRequestOptions ): Promise; hostPrefixOperation( args: HostPrefixOperationCommandInput, @@ -125,7 +133,7 @@ export interface XYZService { ): void; hostPrefixOperation( args: HostPrefixOperationCommandInput, - options: __HttpHandlerOptions, + options: XYZServiceRequestOptions, cb: (err: any, data?: HostPrefixOperationCommandOutput) => void ): void; @@ -135,7 +143,7 @@ export interface XYZService { tradeEventStream(): Promise; tradeEventStream( args: TradeEventStreamCommandInput, - options?: __HttpHandlerOptions + options?: XYZServiceRequestOptions ): Promise; tradeEventStream( args: TradeEventStreamCommandInput, @@ -143,7 +151,7 @@ export interface XYZService { ): void; tradeEventStream( args: TradeEventStreamCommandInput, - options: __HttpHandlerOptions, + options: XYZServiceRequestOptions, cb: (err: any, data?: TradeEventStreamCommandOutput) => void ): void; diff --git a/private/smithy-rpcv2-cbor-schema/src/RpcV2Protocol.ts b/private/smithy-rpcv2-cbor-schema/src/RpcV2Protocol.ts index b1d7c3955eb..86c0bd805c2 100644 --- a/private/smithy-rpcv2-cbor-schema/src/RpcV2Protocol.ts +++ b/private/smithy-rpcv2-cbor-schema/src/RpcV2Protocol.ts @@ -1,6 +1,6 @@ // smithy-typescript generated code import { createAggregatedClient } from "@smithy/core/client"; -import type { HttpHandlerOptions as __HttpHandlerOptions } from "@smithy/types"; +import type { HttpHandlerOptions as __HttpHandlerOptions, MetricsRecorder as __MetricsRecorder } from "@smithy/types"; import { type EmptyInputOutputCommandInput, @@ -81,6 +81,13 @@ const commands = { SparseNullsOperationCommand, }; +/** + * @public + */ +export interface RpcV2ProtocolRequestOptions extends __HttpHandlerOptions { + recorder?: __MetricsRecorder; +} + export interface RpcV2Protocol { /** * @see {@link EmptyInputOutputCommand} @@ -88,7 +95,7 @@ export interface RpcV2Protocol { emptyInputOutput(): Promise; emptyInputOutput( args: EmptyInputOutputCommandInput, - options?: __HttpHandlerOptions + options?: RpcV2ProtocolRequestOptions ): Promise; emptyInputOutput( args: EmptyInputOutputCommandInput, @@ -96,7 +103,7 @@ export interface RpcV2Protocol { ): void; emptyInputOutput( args: EmptyInputOutputCommandInput, - options: __HttpHandlerOptions, + options: RpcV2ProtocolRequestOptions, cb: (err: any, data?: EmptyInputOutputCommandOutput) => void ): void; @@ -106,7 +113,7 @@ export interface RpcV2Protocol { float16(): Promise; float16( args: Float16CommandInput, - options?: __HttpHandlerOptions + options?: RpcV2ProtocolRequestOptions ): Promise; float16( args: Float16CommandInput, @@ -114,7 +121,7 @@ export interface RpcV2Protocol { ): void; float16( args: Float16CommandInput, - options: __HttpHandlerOptions, + options: RpcV2ProtocolRequestOptions, cb: (err: any, data?: Float16CommandOutput) => void ): void; @@ -124,7 +131,7 @@ export interface RpcV2Protocol { fractionalSeconds(): Promise; fractionalSeconds( args: FractionalSecondsCommandInput, - options?: __HttpHandlerOptions + options?: RpcV2ProtocolRequestOptions ): Promise; fractionalSeconds( args: FractionalSecondsCommandInput, @@ -132,7 +139,7 @@ export interface RpcV2Protocol { ): void; fractionalSeconds( args: FractionalSecondsCommandInput, - options: __HttpHandlerOptions, + options: RpcV2ProtocolRequestOptions, cb: (err: any, data?: FractionalSecondsCommandOutput) => void ): void; @@ -142,7 +149,7 @@ export interface RpcV2Protocol { greetingWithErrors(): Promise; greetingWithErrors( args: GreetingWithErrorsCommandInput, - options?: __HttpHandlerOptions + options?: RpcV2ProtocolRequestOptions ): Promise; greetingWithErrors( args: GreetingWithErrorsCommandInput, @@ -150,7 +157,7 @@ export interface RpcV2Protocol { ): void; greetingWithErrors( args: GreetingWithErrorsCommandInput, - options: __HttpHandlerOptions, + options: RpcV2ProtocolRequestOptions, cb: (err: any, data?: GreetingWithErrorsCommandOutput) => void ): void; @@ -160,7 +167,7 @@ export interface RpcV2Protocol { noInputOutput(): Promise; noInputOutput( args: NoInputOutputCommandInput, - options?: __HttpHandlerOptions + options?: RpcV2ProtocolRequestOptions ): Promise; noInputOutput( args: NoInputOutputCommandInput, @@ -168,7 +175,7 @@ export interface RpcV2Protocol { ): void; noInputOutput( args: NoInputOutputCommandInput, - options: __HttpHandlerOptions, + options: RpcV2ProtocolRequestOptions, cb: (err: any, data?: NoInputOutputCommandOutput) => void ): void; @@ -178,7 +185,7 @@ export interface RpcV2Protocol { operationWithDefaults(): Promise; operationWithDefaults( args: OperationWithDefaultsCommandInput, - options?: __HttpHandlerOptions + options?: RpcV2ProtocolRequestOptions ): Promise; operationWithDefaults( args: OperationWithDefaultsCommandInput, @@ -186,7 +193,7 @@ export interface RpcV2Protocol { ): void; operationWithDefaults( args: OperationWithDefaultsCommandInput, - options: __HttpHandlerOptions, + options: RpcV2ProtocolRequestOptions, cb: (err: any, data?: OperationWithDefaultsCommandOutput) => void ): void; @@ -196,7 +203,7 @@ export interface RpcV2Protocol { optionalInputOutput(): Promise; optionalInputOutput( args: OptionalInputOutputCommandInput, - options?: __HttpHandlerOptions + options?: RpcV2ProtocolRequestOptions ): Promise; optionalInputOutput( args: OptionalInputOutputCommandInput, @@ -204,7 +211,7 @@ export interface RpcV2Protocol { ): void; optionalInputOutput( args: OptionalInputOutputCommandInput, - options: __HttpHandlerOptions, + options: RpcV2ProtocolRequestOptions, cb: (err: any, data?: OptionalInputOutputCommandOutput) => void ): void; @@ -214,7 +221,7 @@ export interface RpcV2Protocol { recursiveShapes(): Promise; recursiveShapes( args: RecursiveShapesCommandInput, - options?: __HttpHandlerOptions + options?: RpcV2ProtocolRequestOptions ): Promise; recursiveShapes( args: RecursiveShapesCommandInput, @@ -222,7 +229,7 @@ export interface RpcV2Protocol { ): void; recursiveShapes( args: RecursiveShapesCommandInput, - options: __HttpHandlerOptions, + options: RpcV2ProtocolRequestOptions, cb: (err: any, data?: RecursiveShapesCommandOutput) => void ): void; @@ -232,7 +239,7 @@ export interface RpcV2Protocol { rpcV2CborDenseMaps(): Promise; rpcV2CborDenseMaps( args: RpcV2CborDenseMapsCommandInput, - options?: __HttpHandlerOptions + options?: RpcV2ProtocolRequestOptions ): Promise; rpcV2CborDenseMaps( args: RpcV2CborDenseMapsCommandInput, @@ -240,7 +247,7 @@ export interface RpcV2Protocol { ): void; rpcV2CborDenseMaps( args: RpcV2CborDenseMapsCommandInput, - options: __HttpHandlerOptions, + options: RpcV2ProtocolRequestOptions, cb: (err: any, data?: RpcV2CborDenseMapsCommandOutput) => void ): void; @@ -250,7 +257,7 @@ export interface RpcV2Protocol { rpcV2CborLists(): Promise; rpcV2CborLists( args: RpcV2CborListsCommandInput, - options?: __HttpHandlerOptions + options?: RpcV2ProtocolRequestOptions ): Promise; rpcV2CborLists( args: RpcV2CborListsCommandInput, @@ -258,7 +265,7 @@ export interface RpcV2Protocol { ): void; rpcV2CborLists( args: RpcV2CborListsCommandInput, - options: __HttpHandlerOptions, + options: RpcV2ProtocolRequestOptions, cb: (err: any, data?: RpcV2CborListsCommandOutput) => void ): void; @@ -268,7 +275,7 @@ export interface RpcV2Protocol { rpcV2CborSparseMaps(): Promise; rpcV2CborSparseMaps( args: RpcV2CborSparseMapsCommandInput, - options?: __HttpHandlerOptions + options?: RpcV2ProtocolRequestOptions ): Promise; rpcV2CborSparseMaps( args: RpcV2CborSparseMapsCommandInput, @@ -276,7 +283,7 @@ export interface RpcV2Protocol { ): void; rpcV2CborSparseMaps( args: RpcV2CborSparseMapsCommandInput, - options: __HttpHandlerOptions, + options: RpcV2ProtocolRequestOptions, cb: (err: any, data?: RpcV2CborSparseMapsCommandOutput) => void ): void; @@ -286,7 +293,7 @@ export interface RpcV2Protocol { simpleScalarProperties(): Promise; simpleScalarProperties( args: SimpleScalarPropertiesCommandInput, - options?: __HttpHandlerOptions + options?: RpcV2ProtocolRequestOptions ): Promise; simpleScalarProperties( args: SimpleScalarPropertiesCommandInput, @@ -294,7 +301,7 @@ export interface RpcV2Protocol { ): void; simpleScalarProperties( args: SimpleScalarPropertiesCommandInput, - options: __HttpHandlerOptions, + options: RpcV2ProtocolRequestOptions, cb: (err: any, data?: SimpleScalarPropertiesCommandOutput) => void ): void; @@ -304,7 +311,7 @@ export interface RpcV2Protocol { sparseNullsOperation(): Promise; sparseNullsOperation( args: SparseNullsOperationCommandInput, - options?: __HttpHandlerOptions + options?: RpcV2ProtocolRequestOptions ): Promise; sparseNullsOperation( args: SparseNullsOperationCommandInput, @@ -312,7 +319,7 @@ export interface RpcV2Protocol { ): void; sparseNullsOperation( args: SparseNullsOperationCommandInput, - options: __HttpHandlerOptions, + options: RpcV2ProtocolRequestOptions, cb: (err: any, data?: SparseNullsOperationCommandOutput) => void ): void; } diff --git a/private/smithy-rpcv2-cbor/src/RpcV2Protocol.ts b/private/smithy-rpcv2-cbor/src/RpcV2Protocol.ts index b1d7c3955eb..86c0bd805c2 100644 --- a/private/smithy-rpcv2-cbor/src/RpcV2Protocol.ts +++ b/private/smithy-rpcv2-cbor/src/RpcV2Protocol.ts @@ -1,6 +1,6 @@ // smithy-typescript generated code import { createAggregatedClient } from "@smithy/core/client"; -import type { HttpHandlerOptions as __HttpHandlerOptions } from "@smithy/types"; +import type { HttpHandlerOptions as __HttpHandlerOptions, MetricsRecorder as __MetricsRecorder } from "@smithy/types"; import { type EmptyInputOutputCommandInput, @@ -81,6 +81,13 @@ const commands = { SparseNullsOperationCommand, }; +/** + * @public + */ +export interface RpcV2ProtocolRequestOptions extends __HttpHandlerOptions { + recorder?: __MetricsRecorder; +} + export interface RpcV2Protocol { /** * @see {@link EmptyInputOutputCommand} @@ -88,7 +95,7 @@ export interface RpcV2Protocol { emptyInputOutput(): Promise; emptyInputOutput( args: EmptyInputOutputCommandInput, - options?: __HttpHandlerOptions + options?: RpcV2ProtocolRequestOptions ): Promise; emptyInputOutput( args: EmptyInputOutputCommandInput, @@ -96,7 +103,7 @@ export interface RpcV2Protocol { ): void; emptyInputOutput( args: EmptyInputOutputCommandInput, - options: __HttpHandlerOptions, + options: RpcV2ProtocolRequestOptions, cb: (err: any, data?: EmptyInputOutputCommandOutput) => void ): void; @@ -106,7 +113,7 @@ export interface RpcV2Protocol { float16(): Promise; float16( args: Float16CommandInput, - options?: __HttpHandlerOptions + options?: RpcV2ProtocolRequestOptions ): Promise; float16( args: Float16CommandInput, @@ -114,7 +121,7 @@ export interface RpcV2Protocol { ): void; float16( args: Float16CommandInput, - options: __HttpHandlerOptions, + options: RpcV2ProtocolRequestOptions, cb: (err: any, data?: Float16CommandOutput) => void ): void; @@ -124,7 +131,7 @@ export interface RpcV2Protocol { fractionalSeconds(): Promise; fractionalSeconds( args: FractionalSecondsCommandInput, - options?: __HttpHandlerOptions + options?: RpcV2ProtocolRequestOptions ): Promise; fractionalSeconds( args: FractionalSecondsCommandInput, @@ -132,7 +139,7 @@ export interface RpcV2Protocol { ): void; fractionalSeconds( args: FractionalSecondsCommandInput, - options: __HttpHandlerOptions, + options: RpcV2ProtocolRequestOptions, cb: (err: any, data?: FractionalSecondsCommandOutput) => void ): void; @@ -142,7 +149,7 @@ export interface RpcV2Protocol { greetingWithErrors(): Promise; greetingWithErrors( args: GreetingWithErrorsCommandInput, - options?: __HttpHandlerOptions + options?: RpcV2ProtocolRequestOptions ): Promise; greetingWithErrors( args: GreetingWithErrorsCommandInput, @@ -150,7 +157,7 @@ export interface RpcV2Protocol { ): void; greetingWithErrors( args: GreetingWithErrorsCommandInput, - options: __HttpHandlerOptions, + options: RpcV2ProtocolRequestOptions, cb: (err: any, data?: GreetingWithErrorsCommandOutput) => void ): void; @@ -160,7 +167,7 @@ export interface RpcV2Protocol { noInputOutput(): Promise; noInputOutput( args: NoInputOutputCommandInput, - options?: __HttpHandlerOptions + options?: RpcV2ProtocolRequestOptions ): Promise; noInputOutput( args: NoInputOutputCommandInput, @@ -168,7 +175,7 @@ export interface RpcV2Protocol { ): void; noInputOutput( args: NoInputOutputCommandInput, - options: __HttpHandlerOptions, + options: RpcV2ProtocolRequestOptions, cb: (err: any, data?: NoInputOutputCommandOutput) => void ): void; @@ -178,7 +185,7 @@ export interface RpcV2Protocol { operationWithDefaults(): Promise; operationWithDefaults( args: OperationWithDefaultsCommandInput, - options?: __HttpHandlerOptions + options?: RpcV2ProtocolRequestOptions ): Promise; operationWithDefaults( args: OperationWithDefaultsCommandInput, @@ -186,7 +193,7 @@ export interface RpcV2Protocol { ): void; operationWithDefaults( args: OperationWithDefaultsCommandInput, - options: __HttpHandlerOptions, + options: RpcV2ProtocolRequestOptions, cb: (err: any, data?: OperationWithDefaultsCommandOutput) => void ): void; @@ -196,7 +203,7 @@ export interface RpcV2Protocol { optionalInputOutput(): Promise; optionalInputOutput( args: OptionalInputOutputCommandInput, - options?: __HttpHandlerOptions + options?: RpcV2ProtocolRequestOptions ): Promise; optionalInputOutput( args: OptionalInputOutputCommandInput, @@ -204,7 +211,7 @@ export interface RpcV2Protocol { ): void; optionalInputOutput( args: OptionalInputOutputCommandInput, - options: __HttpHandlerOptions, + options: RpcV2ProtocolRequestOptions, cb: (err: any, data?: OptionalInputOutputCommandOutput) => void ): void; @@ -214,7 +221,7 @@ export interface RpcV2Protocol { recursiveShapes(): Promise; recursiveShapes( args: RecursiveShapesCommandInput, - options?: __HttpHandlerOptions + options?: RpcV2ProtocolRequestOptions ): Promise; recursiveShapes( args: RecursiveShapesCommandInput, @@ -222,7 +229,7 @@ export interface RpcV2Protocol { ): void; recursiveShapes( args: RecursiveShapesCommandInput, - options: __HttpHandlerOptions, + options: RpcV2ProtocolRequestOptions, cb: (err: any, data?: RecursiveShapesCommandOutput) => void ): void; @@ -232,7 +239,7 @@ export interface RpcV2Protocol { rpcV2CborDenseMaps(): Promise; rpcV2CborDenseMaps( args: RpcV2CborDenseMapsCommandInput, - options?: __HttpHandlerOptions + options?: RpcV2ProtocolRequestOptions ): Promise; rpcV2CborDenseMaps( args: RpcV2CborDenseMapsCommandInput, @@ -240,7 +247,7 @@ export interface RpcV2Protocol { ): void; rpcV2CborDenseMaps( args: RpcV2CborDenseMapsCommandInput, - options: __HttpHandlerOptions, + options: RpcV2ProtocolRequestOptions, cb: (err: any, data?: RpcV2CborDenseMapsCommandOutput) => void ): void; @@ -250,7 +257,7 @@ export interface RpcV2Protocol { rpcV2CborLists(): Promise; rpcV2CborLists( args: RpcV2CborListsCommandInput, - options?: __HttpHandlerOptions + options?: RpcV2ProtocolRequestOptions ): Promise; rpcV2CborLists( args: RpcV2CborListsCommandInput, @@ -258,7 +265,7 @@ export interface RpcV2Protocol { ): void; rpcV2CborLists( args: RpcV2CborListsCommandInput, - options: __HttpHandlerOptions, + options: RpcV2ProtocolRequestOptions, cb: (err: any, data?: RpcV2CborListsCommandOutput) => void ): void; @@ -268,7 +275,7 @@ export interface RpcV2Protocol { rpcV2CborSparseMaps(): Promise; rpcV2CborSparseMaps( args: RpcV2CborSparseMapsCommandInput, - options?: __HttpHandlerOptions + options?: RpcV2ProtocolRequestOptions ): Promise; rpcV2CborSparseMaps( args: RpcV2CborSparseMapsCommandInput, @@ -276,7 +283,7 @@ export interface RpcV2Protocol { ): void; rpcV2CborSparseMaps( args: RpcV2CborSparseMapsCommandInput, - options: __HttpHandlerOptions, + options: RpcV2ProtocolRequestOptions, cb: (err: any, data?: RpcV2CborSparseMapsCommandOutput) => void ): void; @@ -286,7 +293,7 @@ export interface RpcV2Protocol { simpleScalarProperties(): Promise; simpleScalarProperties( args: SimpleScalarPropertiesCommandInput, - options?: __HttpHandlerOptions + options?: RpcV2ProtocolRequestOptions ): Promise; simpleScalarProperties( args: SimpleScalarPropertiesCommandInput, @@ -294,7 +301,7 @@ export interface RpcV2Protocol { ): void; simpleScalarProperties( args: SimpleScalarPropertiesCommandInput, - options: __HttpHandlerOptions, + options: RpcV2ProtocolRequestOptions, cb: (err: any, data?: SimpleScalarPropertiesCommandOutput) => void ): void; @@ -304,7 +311,7 @@ export interface RpcV2Protocol { sparseNullsOperation(): Promise; sparseNullsOperation( args: SparseNullsOperationCommandInput, - options?: __HttpHandlerOptions + options?: RpcV2ProtocolRequestOptions ): Promise; sparseNullsOperation( args: SparseNullsOperationCommandInput, @@ -312,7 +319,7 @@ export interface RpcV2Protocol { ): void; sparseNullsOperation( args: SparseNullsOperationCommandInput, - options: __HttpHandlerOptions, + options: RpcV2ProtocolRequestOptions, cb: (err: any, data?: SparseNullsOperationCommandOutput) => void ): void; } diff --git a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/ServiceAggregatedClientGenerator.java b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/ServiceAggregatedClientGenerator.java index b2c0cf9f144..b8c0bb76d00 100644 --- a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/ServiceAggregatedClientGenerator.java +++ b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/ServiceAggregatedClientGenerator.java @@ -135,6 +135,22 @@ public void run() { writer.write(""); + // Per-request options accepted by the aggregated client convenience methods. + // Superset of the transport options that also carries a call-site MetricsRecorder. + String requestOptionsType = aggregateClientName + "RequestOptions"; + writer.addTypeImport("MetricsRecorder", "__MetricsRecorder", TypeScriptDependency.SMITHY_TYPES); + writer.writeDocs("@public"); + writer.openBlock( + "export interface $L extends $T {", + "}", + requestOptionsType, + applicationProtocol.getOptionsType(), + () -> { + writer.write("recorder?: __MetricsRecorder;"); + } + ); + writer.write(""); + // Generate an aggregated client interface. writer.openBlock("export interface $L {", "}", aggregateClientName, () -> { for (OperationShape operation : containedOperations) { @@ -157,7 +173,7 @@ public void run() { """ $1L( args: $2T, - options?: $3T + options?: $3L ): Promise<$4T>; $1L( args: $2T, @@ -165,12 +181,12 @@ public void run() { ): void; $1L( args: $2T, - options: $3T, + options: $3L, cb: (err: any, data?: $4T) => void ): void;""", methodName, input, - applicationProtocol.getOptionsType(), + requestOptionsType, output ); writer.write(""); diff --git a/smithy-typescript-codegen/src/test/java/software/amazon/smithy/typescript/codegen/ServiceAggregatedClientGeneratorTest.java b/smithy-typescript-codegen/src/test/java/software/amazon/smithy/typescript/codegen/ServiceAggregatedClientGeneratorTest.java new file mode 100644 index 00000000000..388971f7d0c --- /dev/null +++ b/smithy-typescript-codegen/src/test/java/software/amazon/smithy/typescript/codegen/ServiceAggregatedClientGeneratorTest.java @@ -0,0 +1,50 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +package software.amazon.smithy.typescript.codegen; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; + +import org.junit.jupiter.api.Test; +import software.amazon.smithy.build.MockManifest; +import software.amazon.smithy.build.PluginContext; +import software.amazon.smithy.model.Model; +import software.amazon.smithy.model.node.Node; + +public class ServiceAggregatedClientGeneratorTest { + + @Test + public void operationMethodsAcceptRequestOptionsWithRecorder() { + MockManifest manifest = new MockManifest(); + PluginContext context = PluginContext.builder() + .pluginClassLoader(getClass().getClassLoader()) + .model( + Model.assembler() + .addImport(getClass().getResource("output-structure.smithy")) + .discoverModels() + .assemble() + .unwrap() + ) + .fileManifest(manifest) + .settings( + Node.objectNodeBuilder() + .withMember("service", Node.from("smithy.example#Example")) + .withMember("package", Node.from("example")) + .withMember("packageVersion", Node.from("1.0.0")) + .build() + ) + .build(); + + new TypeScriptCodegenPlugin().execute(context); + String contents = manifest.getFileString(CodegenUtils.SOURCE_FOLDER + "//Example.ts").get(); + + // A named, readable options type is generated (superset of the transport options). + assertThat(contents, containsString("export interface ExampleRequestOptions extends __HttpHandlerOptions")); + assertThat(contents, containsString("recorder?: __MetricsRecorder;")); + // Convenience methods reference the named type rather than an inline composed type. + assertThat(contents, containsString("options?: ExampleRequestOptions")); + assertThat(contents, containsString("options: ExampleRequestOptions")); + } +}