From b05f9298eded4a3877e1240f189a459b25bbaad0 Mon Sep 17 00:00:00 2001 From: George Fu Date: Fri, 21 Mar 2025 16:15:26 -0400 Subject: [PATCH 1/2] fix: enforce singular object custody in config resolver stack --- .changeset/weak-poems-tease.md | 14 ++++++++ .../resolveCustomEndpointsConfig.spec.ts | 25 +++++++++----- .../resolveCustomEndpointsConfig.ts | 11 +++--- .../resolveEndpointsConfig.spec.ts | 11 ++++-- .../endpointsConfig/resolveEndpointsConfig.ts | 9 +++-- .../regionConfig/resolveRegionConfig.spec.ts | 7 ++++ .../src/regionConfig/resolveRegionConfig.ts | 5 ++- .../src/EventStreamSerdeConfig.spec.ts | 7 ++++ .../src/EventStreamSerdeConfig.ts | 8 ++--- .../src/index.spec.ts | 13 +++++-- .../src/resolveCompressionConfig.spec.ts | 8 +++++ .../src/resolveCompressionConfig.ts | 34 ++++++++++--------- .../src/resolveEndpointConfig.spec.ts | 17 ++++++++++ .../src/resolveEndpointConfig.ts | 11 +++--- .../src/configurations.spec.ts | 7 ++++ .../middleware-retry/src/configurations.ts | 12 +++---- .../extensions/httpExtensionConfiguration.ts | 9 +++-- packages/smithy-client/src/client.ts | 10 ++++++ .../smithy-client/src/extensions/checksum.ts | 5 ++- .../defaultExtensionConfiguration.ts | 10 ++---- .../smithy-client/src/extensions/retry.ts | 5 ++- packages/types/src/extensions/checksum.ts | 5 ++- .../extensions/defaultClientConfiguration.ts | 8 ++--- .../util-stream/src/createBufferedReadable.ts | 2 +- .../src/createBufferedReadableStream.ts | 9 ++--- .../src/RpcV2ProtocolClient.ts | 3 +- .../src/auth/httpAuthSchemeProvider.ts | 4 +-- .../src/runtimeExtensions.ts | 24 ++++++------- .../test/functional/rpcv2cbor.spec.ts | 2 +- .../codegen/RuntimeExtensionsGenerator.java | 4 +-- .../ServiceBareBonesClientGenerator.java | 6 ++-- .../integration/AddHttpAuthSchemePlugin.java | 17 ++++------ .../endpointsV2/EndpointsV2Generator.java | 3 +- .../resolveRuntimeExtensions1.template | 6 ++-- .../resolveRuntimeExtensions2.template | 5 ++- .../codegen/runtimeExtensions2.template | 5 ++- .../endpointsV2/EndpointsV2GeneratorTest.java | 5 ++- 37 files changed, 205 insertions(+), 141 deletions(-) create mode 100644 .changeset/weak-poems-tease.md create mode 100644 packages/middleware-endpoint/src/resolveEndpointConfig.spec.ts diff --git a/.changeset/weak-poems-tease.md b/.changeset/weak-poems-tease.md new file mode 100644 index 00000000000..5c7d557b52f --- /dev/null +++ b/.changeset/weak-poems-tease.md @@ -0,0 +1,14 @@ +--- +"@smithy/eventstream-serde-config-resolver": minor +"@smithy/middleware-apply-body-checksum": minor +"@smithy/middleware-compression": minor +"@smithy/middleware-endpoint": minor +"@smithy/middleware-retry": minor +"@smithy/config-resolver": minor +"@smithy/protocol-http": minor +"@smithy/smithy-client": minor +"@smithy/util-stream": minor +"@smithy/types": minor +--- + +enforce singular config object during client instantiation diff --git a/packages/config-resolver/src/endpointsConfig/resolveCustomEndpointsConfig.spec.ts b/packages/config-resolver/src/endpointsConfig/resolveCustomEndpointsConfig.spec.ts index 5a0d9e2af9a..2b1479dbab7 100644 --- a/packages/config-resolver/src/endpointsConfig/resolveCustomEndpointsConfig.spec.ts +++ b/packages/config-resolver/src/endpointsConfig/resolveCustomEndpointsConfig.spec.ts @@ -20,7 +20,7 @@ describe(resolveCustomEndpointsConfig.name, () => { beforeEach(() => { vi.mocked(normalizeProvider).mockImplementation((input) => - typeof input === "function" ? input : () => Promise.resolve(input) + typeof input === "function" ? (input as any) : () => Promise.resolve(input) ); }); @@ -28,6 +28,11 @@ describe(resolveCustomEndpointsConfig.name, () => { vi.clearAllMocks(); }); + it("maintains object custody", () => { + const input = { ...mockInput }; + expect(resolveCustomEndpointsConfig(input)).toBe(input); + }); + describe("tls", () => { afterEach(() => { expect(normalizeProvider).toHaveBeenCalledTimes(2); @@ -44,7 +49,7 @@ describe(resolveCustomEndpointsConfig.name, () => { }); it("returns true for isCustomEndpoint", () => { - expect(resolveCustomEndpointsConfig(mockInput).isCustomEndpoint).toStrictEqual(true); + expect(resolveCustomEndpointsConfig({ ...mockInput }).isCustomEndpoint).toStrictEqual(true); }); it("returns false when useDualstackEndpoint is not defined", async () => { @@ -56,23 +61,25 @@ describe(resolveCustomEndpointsConfig.name, () => { }); describe("returns normalized endpoint", () => { - afterEach(() => { - expect(normalizeProvider).toHaveBeenCalledTimes(2); - expect(normalizeProvider).toHaveBeenNthCalledWith(1, mockInput.endpoint); - expect(normalizeProvider).toHaveBeenNthCalledWith(2, mockInput.useDualstackEndpoint); - }); - it("calls urlParser endpoint is of type string", async () => { const mockEndpointString = "http://localhost/"; const endpoint = await resolveCustomEndpointsConfig({ ...mockInput, endpoint: mockEndpointString }).endpoint(); expect(endpoint).toStrictEqual(mockEndpoint); expect(mockInput.urlParser).toHaveBeenCalledWith(mockEndpointString); + + expect(normalizeProvider).toHaveBeenCalledTimes(2); + expect(normalizeProvider).toHaveBeenNthCalledWith(1, mockInput.endpoint); + expect(normalizeProvider).toHaveBeenNthCalledWith(2, mockInput.useDualstackEndpoint); }); it("passes endpoint to normalize if not string", async () => { - const endpoint = await resolveCustomEndpointsConfig(mockInput).endpoint(); + const endpoint = await resolveCustomEndpointsConfig({ ...mockInput }).endpoint(); expect(endpoint).toStrictEqual(mockEndpoint); expect(mockInput.urlParser).not.toHaveBeenCalled(); + + expect(normalizeProvider).toHaveBeenCalledTimes(2); + expect(normalizeProvider).toHaveBeenNthCalledWith(1, mockInput.endpoint); + expect(normalizeProvider).toHaveBeenNthCalledWith(2, mockInput.useDualstackEndpoint); }); }); }); diff --git a/packages/config-resolver/src/endpointsConfig/resolveCustomEndpointsConfig.ts b/packages/config-resolver/src/endpointsConfig/resolveCustomEndpointsConfig.ts index eb0e4ed4ebe..a0c639eb6bb 100644 --- a/packages/config-resolver/src/endpointsConfig/resolveCustomEndpointsConfig.ts +++ b/packages/config-resolver/src/endpointsConfig/resolveCustomEndpointsConfig.ts @@ -37,12 +37,11 @@ export interface CustomEndpointsResolvedConfig extends EndpointsResolvedConfig { export const resolveCustomEndpointsConfig = ( input: T & CustomEndpointsInputConfig & PreviouslyResolved ): T & CustomEndpointsResolvedConfig => { - const { endpoint, urlParser } = input; - return { - ...input, - tls: input.tls ?? true, + const { tls, endpoint, urlParser, useDualstackEndpoint } = input; + return Object.assign(input, { + tls: tls ?? true, endpoint: normalizeProvider(typeof endpoint === "string" ? urlParser(endpoint) : endpoint), isCustomEndpoint: true, - useDualstackEndpoint: normalizeProvider(input.useDualstackEndpoint ?? false), - }; + useDualstackEndpoint: normalizeProvider(useDualstackEndpoint ?? false), + } as CustomEndpointsResolvedConfig); }; diff --git a/packages/config-resolver/src/endpointsConfig/resolveEndpointsConfig.spec.ts b/packages/config-resolver/src/endpointsConfig/resolveEndpointsConfig.spec.ts index a13580ae8bd..6653c16a5f5 100644 --- a/packages/config-resolver/src/endpointsConfig/resolveEndpointsConfig.spec.ts +++ b/packages/config-resolver/src/endpointsConfig/resolveEndpointsConfig.spec.ts @@ -25,7 +25,7 @@ describe(resolveEndpointsConfig.name, () => { beforeEach(() => { vi.mocked(getEndpointFromRegion).mockResolvedValueOnce(mockEndpoint); vi.mocked(normalizeProvider).mockImplementation((input) => - typeof input === "function" ? input : () => Promise.resolve(input) + typeof input === "function" ? (input as any) : () => Promise.resolve(input) ); }); @@ -33,6 +33,11 @@ describe(resolveEndpointsConfig.name, () => { vi.clearAllMocks(); }); + it("maintains object custody", () => { + const input = { ...mockInput }; + expect(resolveEndpointsConfig(input)).toBe(input); + }); + describe("tls", () => { afterEach(() => { expect(normalizeProvider).toHaveBeenNthCalledWith(1, mockInput.useDualstackEndpoint); @@ -53,7 +58,7 @@ describe(resolveEndpointsConfig.name, () => { }); it("returns true when endpoint is defined", () => { - expect(resolveEndpointsConfig(mockInput).isCustomEndpoint).toStrictEqual(true); + expect(resolveEndpointsConfig({ ...mockInput }).isCustomEndpoint).toStrictEqual(true); }); it("returns false when endpoint is not defined", () => { @@ -90,7 +95,7 @@ describe(resolveEndpointsConfig.name, () => { }); it("passes endpoint to normalize if not string", async () => { - const endpoint = await resolveEndpointsConfig(mockInput).endpoint(); + const endpoint = await resolveEndpointsConfig({ ...mockInput }).endpoint(); expect(endpoint).toStrictEqual(mockEndpoint); expect(mockInput.urlParser).not.toHaveBeenCalled(); }); diff --git a/packages/config-resolver/src/endpointsConfig/resolveEndpointsConfig.ts b/packages/config-resolver/src/endpointsConfig/resolveEndpointsConfig.ts index 8a8a810d2d0..f08ebefca8d 100644 --- a/packages/config-resolver/src/endpointsConfig/resolveEndpointsConfig.ts +++ b/packages/config-resolver/src/endpointsConfig/resolveEndpointsConfig.ts @@ -65,14 +65,13 @@ export const resolveEndpointsConfig = ( input: T & EndpointsInputConfig & PreviouslyResolved ): T & EndpointsResolvedConfig => { const useDualstackEndpoint = normalizeProvider(input.useDualstackEndpoint ?? false); - const { endpoint, useFipsEndpoint, urlParser } = input; - return { - ...input, - tls: input.tls ?? true, + const { endpoint, useFipsEndpoint, urlParser, tls } = input; + return Object.assign(input, { + tls: tls ?? true, endpoint: endpoint ? normalizeProvider(typeof endpoint === "string" ? urlParser(endpoint) : endpoint) : () => getEndpointFromRegion({ ...input, useDualstackEndpoint, useFipsEndpoint }), isCustomEndpoint: !!endpoint, useDualstackEndpoint, - }; + }); }; diff --git a/packages/config-resolver/src/regionConfig/resolveRegionConfig.spec.ts b/packages/config-resolver/src/regionConfig/resolveRegionConfig.spec.ts index c788007dbf6..6b8ce06efc5 100644 --- a/packages/config-resolver/src/regionConfig/resolveRegionConfig.spec.ts +++ b/packages/config-resolver/src/regionConfig/resolveRegionConfig.spec.ts @@ -21,6 +21,13 @@ describe("RegionConfig", () => { vi.clearAllMocks(); }); + it("maintains object custody", () => { + const input = { + region: "us-east-1", + }; + expect(resolveRegionConfig(input)).toBe(input); + }); + describe("region", () => { it("return normalized value with real region if passed as a string", async () => { const resolvedRegionConfig = resolveRegionConfig({ region: mockRegion, useFipsEndpoint: mockUseFipsEndpoint }); diff --git a/packages/config-resolver/src/regionConfig/resolveRegionConfig.ts b/packages/config-resolver/src/regionConfig/resolveRegionConfig.ts index b0e2289984e..86b18771c93 100644 --- a/packages/config-resolver/src/regionConfig/resolveRegionConfig.ts +++ b/packages/config-resolver/src/regionConfig/resolveRegionConfig.ts @@ -45,8 +45,7 @@ export const resolveRegionConfig = (input: T & RegionInputConfig & Previously throw new Error("Region is missing"); } - return { - ...input, + return Object.assign(input, { region: async () => { if (typeof region === "string") { return getRealRegion(region); @@ -61,5 +60,5 @@ export const resolveRegionConfig = (input: T & RegionInputConfig & Previously } return typeof useFipsEndpoint !== "function" ? Promise.resolve(!!useFipsEndpoint) : useFipsEndpoint(); }, - }; + }); }; diff --git a/packages/eventstream-serde-config-resolver/src/EventStreamSerdeConfig.spec.ts b/packages/eventstream-serde-config-resolver/src/EventStreamSerdeConfig.spec.ts index bb2ec2bf78e..c09b22e06e0 100644 --- a/packages/eventstream-serde-config-resolver/src/EventStreamSerdeConfig.spec.ts +++ b/packages/eventstream-serde-config-resolver/src/EventStreamSerdeConfig.spec.ts @@ -9,6 +9,13 @@ describe("resolveEventStreamSerdeConfig", () => { vi.clearAllMocks(); }); + it("maintains object custody", () => { + const input = { + eventStreamSerdeProvider: vi.fn(), + }; + expect(resolveEventStreamSerdeConfig(input)).toBe(input); + }); + it("sets value returned by eventStreamSerdeProvider", () => { const mockReturn = "mockReturn"; eventStreamSerdeProvider.mockReturnValueOnce(mockReturn); diff --git a/packages/eventstream-serde-config-resolver/src/EventStreamSerdeConfig.ts b/packages/eventstream-serde-config-resolver/src/EventStreamSerdeConfig.ts index 9510dac9dcd..e96f296d43e 100644 --- a/packages/eventstream-serde-config-resolver/src/EventStreamSerdeConfig.ts +++ b/packages/eventstream-serde-config-resolver/src/EventStreamSerdeConfig.ts @@ -28,7 +28,7 @@ interface PreviouslyResolved { */ export const resolveEventStreamSerdeConfig = ( input: T & PreviouslyResolved & EventStreamSerdeInputConfig -): T & EventStreamSerdeResolvedConfig => ({ - ...input, - eventStreamMarshaller: input.eventStreamSerdeProvider(input), -}); +): T & EventStreamSerdeResolvedConfig => + Object.assign(input, { + eventStreamMarshaller: input.eventStreamSerdeProvider(input), + }); diff --git a/packages/middleware-apply-body-checksum/src/index.spec.ts b/packages/middleware-apply-body-checksum/src/index.spec.ts index ff1fb34342a..09cdf854295 100644 --- a/packages/middleware-apply-body-checksum/src/index.spec.ts +++ b/packages/middleware-apply-body-checksum/src/index.spec.ts @@ -1,8 +1,17 @@ -import { describe, expect, test as it } from "vitest"; +import { describe, expect, test as it, vi } from "vitest"; -import { applyMd5BodyChecksumMiddleware } from "./index"; +import { applyMd5BodyChecksumMiddleware, resolveMd5BodyChecksumConfig } from "./index"; describe("middleware-apply-body-checksum package exports", () => { + it("maintains object custody", () => { + const input = { + md5: vi.fn(), + base64Encoder: vi.fn(), + streamHasher: vi.fn(), + }; + expect(resolveMd5BodyChecksumConfig(input)).toBe(input); + }); + it("applyMd5BodyChecksumMiddleware", () => { expect(typeof applyMd5BodyChecksumMiddleware).toBe("function"); }); diff --git a/packages/middleware-compression/src/resolveCompressionConfig.spec.ts b/packages/middleware-compression/src/resolveCompressionConfig.spec.ts index db62bd4a61b..6923c9bc731 100644 --- a/packages/middleware-compression/src/resolveCompressionConfig.spec.ts +++ b/packages/middleware-compression/src/resolveCompressionConfig.spec.ts @@ -9,6 +9,14 @@ describe(resolveCompressionConfig.name, () => { requestMinCompressionSizeBytes: 0, }; + it("maintains object custody", () => { + const input = { + disableRequestCompression: false, + requestMinCompressionSizeBytes: 10_000, + }; + expect(resolveCompressionConfig(input)).toBe(input); + }); + it("should throw an error if requestMinCompressionSizeBytes is less than 0", async () => { const requestMinCompressionSizeBytes = -1; const resolvedConfig = resolveCompressionConfig({ ...mockConfig, requestMinCompressionSizeBytes }); diff --git a/packages/middleware-compression/src/resolveCompressionConfig.ts b/packages/middleware-compression/src/resolveCompressionConfig.ts index 67aa028c150..cfc9c8b2e03 100644 --- a/packages/middleware-compression/src/resolveCompressionConfig.ts +++ b/packages/middleware-compression/src/resolveCompressionConfig.ts @@ -7,21 +7,23 @@ import { CompressionInputConfig, CompressionResolvedConfig } from "./configurati */ export const resolveCompressionConfig = ( input: T & Required -): T & CompressionResolvedConfig => ({ - ...input, - disableRequestCompression: normalizeProvider(input.disableRequestCompression), - requestMinCompressionSizeBytes: async () => { - const requestMinCompressionSizeBytes = await normalizeProvider(input.requestMinCompressionSizeBytes)(); +): T & CompressionResolvedConfig => { + const { disableRequestCompression, requestMinCompressionSizeBytes: _requestMinCompressionSizeBytes } = input; + return Object.assign(input, { + disableRequestCompression: normalizeProvider(disableRequestCompression), + requestMinCompressionSizeBytes: async () => { + const requestMinCompressionSizeBytes = await normalizeProvider(_requestMinCompressionSizeBytes)(); - // The requestMinCompressionSizeBytes should be less than the upper limit for API Gateway - // https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-openapi-minimum-compression-size.html - if (requestMinCompressionSizeBytes < 0 || requestMinCompressionSizeBytes > 10485760) { - throw new RangeError( - "The value for requestMinCompressionSizeBytes must be between 0 and 10485760 inclusive. " + - `The provided value ${requestMinCompressionSizeBytes} is outside this range."` - ); - } + // The requestMinCompressionSizeBytes should be less than the upper limit for API Gateway + // https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-openapi-minimum-compression-size.html + if (requestMinCompressionSizeBytes < 0 || requestMinCompressionSizeBytes > 10485760) { + throw new RangeError( + "The value for requestMinCompressionSizeBytes must be between 0 and 10485760 inclusive. " + + `The provided value ${requestMinCompressionSizeBytes} is outside this range."` + ); + } - return requestMinCompressionSizeBytes; - }, -}); + return requestMinCompressionSizeBytes; + }, + }); +}; diff --git a/packages/middleware-endpoint/src/resolveEndpointConfig.spec.ts b/packages/middleware-endpoint/src/resolveEndpointConfig.spec.ts new file mode 100644 index 00000000000..c3a9fd014b1 --- /dev/null +++ b/packages/middleware-endpoint/src/resolveEndpointConfig.spec.ts @@ -0,0 +1,17 @@ +import { describe, expect, test as it, vi } from "vitest"; + +import { resolveEndpointConfig } from "./resolveEndpointConfig"; + +describe(resolveEndpointConfig.name, () => { + it("maintains object custody", () => { + const input = { + tls: true, + useFipsEndpoint: true, + useDualstackEndpoint: true, + endpointProvider: vi.fn(), + urlParser: vi.fn(), + region: async () => "us-east-1", + }; + expect(resolveEndpointConfig(input)).toBe(input); + }); +}); diff --git a/packages/middleware-endpoint/src/resolveEndpointConfig.ts b/packages/middleware-endpoint/src/resolveEndpointConfig.ts index 30c88309a94..5935fad1c3d 100644 --- a/packages/middleware-endpoint/src/resolveEndpointConfig.ts +++ b/packages/middleware-endpoint/src/resolveEndpointConfig.ts @@ -121,21 +121,20 @@ export const resolveEndpointConfig = & PreviouslyResolved

): T & EndpointResolvedConfig

=> { const tls = input.tls ?? true; - const { endpoint } = input; + const { endpoint, useDualstackEndpoint, useFipsEndpoint } = input; const customEndpointProvider = endpoint != null ? async () => toEndpointV1(await normalizeProvider(endpoint)()) : undefined; const isCustomEndpoint = !!endpoint; - const resolvedConfig = { - ...input, + const resolvedConfig = Object.assign(input, { endpoint: customEndpointProvider, tls, isCustomEndpoint, - useDualstackEndpoint: normalizeProvider(input.useDualstackEndpoint ?? false), - useFipsEndpoint: normalizeProvider(input.useFipsEndpoint ?? false), - } as T & EndpointResolvedConfig

; + useDualstackEndpoint: normalizeProvider(useDualstackEndpoint ?? false), + useFipsEndpoint: normalizeProvider(useFipsEndpoint ?? false), + }) as T & EndpointResolvedConfig

; let configuredEndpointPromise: undefined | Promise = undefined; resolvedConfig.serviceConfiguredEndpoint = async () => { diff --git a/packages/middleware-retry/src/configurations.spec.ts b/packages/middleware-retry/src/configurations.spec.ts index 98d8a185bd1..4131c1c18b3 100644 --- a/packages/middleware-retry/src/configurations.spec.ts +++ b/packages/middleware-retry/src/configurations.spec.ts @@ -26,6 +26,13 @@ describe(resolveRetryConfig.name, () => { vi.clearAllMocks(); }); + it("maintains object custody", () => { + const input = { + retryMode: "STANDARD", + }; + expect(resolveRetryConfig(input)).toBe(input); + }); + describe("maxAttempts", () => { it.each([1, 2, 3])("assigns provided value %s", async (maxAttempts) => { const output = await resolveRetryConfig({ maxAttempts, retryMode }).maxAttempts(); diff --git a/packages/middleware-retry/src/configurations.ts b/packages/middleware-retry/src/configurations.ts index f4c42e0964b..5a98a313a09 100644 --- a/packages/middleware-retry/src/configurations.ts +++ b/packages/middleware-retry/src/configurations.ts @@ -86,22 +86,22 @@ export interface RetryResolvedConfig { * @internal */ export const resolveRetryConfig = (input: T & PreviouslyResolved & RetryInputConfig): T & RetryResolvedConfig => { - const { retryStrategy } = input; - const maxAttempts = normalizeProvider(input.maxAttempts ?? DEFAULT_MAX_ATTEMPTS); - return { - ...input, + const { retryStrategy, retryMode: _retryMode, maxAttempts: _maxAttempts } = input; + const maxAttempts = normalizeProvider(_maxAttempts ?? DEFAULT_MAX_ATTEMPTS); + + return Object.assign(input, { maxAttempts, retryStrategy: async () => { if (retryStrategy) { return retryStrategy; } - const retryMode = await normalizeProvider(input.retryMode)(); + const retryMode = await normalizeProvider(_retryMode)(); if (retryMode === RETRY_MODES.ADAPTIVE) { return new AdaptiveRetryStrategy(maxAttempts); } return new StandardRetryStrategy(maxAttempts); }, - }; + }); }; /** diff --git a/packages/protocol-http/src/extensions/httpExtensionConfiguration.ts b/packages/protocol-http/src/extensions/httpExtensionConfiguration.ts index e002a8156a7..e4ba3b3ef71 100644 --- a/packages/protocol-http/src/extensions/httpExtensionConfiguration.ts +++ b/packages/protocol-http/src/extensions/httpExtensionConfiguration.ts @@ -25,19 +25,18 @@ export type HttpHandlerExtensionConfigType = export const getHttpHandlerExtensionConfiguration = ( runtimeConfig: HttpHandlerExtensionConfigType ) => { - let httpHandler = runtimeConfig.httpHandler!; return { setHttpHandler(handler: HttpHandler): void { - httpHandler = handler; + runtimeConfig.httpHandler = handler; }, httpHandler(): HttpHandler { - return httpHandler; + return runtimeConfig.httpHandler!; }, updateHttpClientConfig(key: keyof HandlerConfig, value: HandlerConfig[typeof key]): void { - httpHandler.updateHttpClientConfig(key, value); + runtimeConfig.httpHandler?.updateHttpClientConfig(key, value); }, httpHandlerConfigs(): HandlerConfig { - return httpHandler.httpHandlerConfigs(); + return runtimeConfig.httpHandler!.httpHandlerConfigs(); }, }; }; diff --git a/packages/smithy-client/src/client.ts b/packages/smithy-client/src/client.ts index aa12da841ac..bd05e65faa7 100644 --- a/packages/smithy-client/src/client.ts +++ b/packages/smithy-client/src/client.ts @@ -63,6 +63,16 @@ export class Client< > implements IClient { public middlewareStack: MiddlewareStack = constructStack(); + + /** + * Holds an object reference to the initial configuration object. + * Used to check that the config resolver stack does not create + * dangling instances of an intermediate form of the configuration object. + * + * @internal + */ + public initConfig?: object; + /** * May be used to cache the resolved handler function for a Command class. */ diff --git a/packages/smithy-client/src/extensions/checksum.ts b/packages/smithy-client/src/extensions/checksum.ts index 46bf5842efb..3c205c32d96 100644 --- a/packages/smithy-client/src/extensions/checksum.ts +++ b/packages/smithy-client/src/extensions/checksum.ts @@ -32,12 +32,11 @@ export const getChecksumConfiguration = (runtimeConfig: PartialChecksumRuntimeCo } return { - _checksumAlgorithms: checksumAlgorithms, addChecksumAlgorithm(algo: ChecksumAlgorithm): void { - this._checksumAlgorithms.push(algo); + checksumAlgorithms.push(algo); }, checksumAlgorithms(): ChecksumAlgorithm[] { - return this._checksumAlgorithms; + return checksumAlgorithms; }, }; }; diff --git a/packages/smithy-client/src/extensions/defaultExtensionConfiguration.ts b/packages/smithy-client/src/extensions/defaultExtensionConfiguration.ts index 1ca86487232..5572294f841 100644 --- a/packages/smithy-client/src/extensions/defaultExtensionConfiguration.ts +++ b/packages/smithy-client/src/extensions/defaultExtensionConfiguration.ts @@ -14,10 +14,7 @@ export type DefaultExtensionRuntimeConfigType = PartialRetryRuntimeConfigType & * Helper function to resolve default extension configuration from runtime config */ export const getDefaultExtensionConfiguration = (runtimeConfig: DefaultExtensionRuntimeConfigType) => { - return { - ...getChecksumConfiguration(runtimeConfig), - ...getRetryConfiguration(runtimeConfig), - }; + return Object.assign(getChecksumConfiguration(runtimeConfig), getRetryConfiguration(runtimeConfig)); }; /** @@ -36,8 +33,5 @@ export const getDefaultClientConfiguration = getDefaultExtensionConfiguration; export const resolveDefaultRuntimeConfig = ( config: DefaultExtensionConfiguration ): DefaultExtensionRuntimeConfigType => { - return { - ...resolveChecksumRuntimeConfig(config), - ...resolveRetryRuntimeConfig(config), - }; + return Object.assign(resolveChecksumRuntimeConfig(config), resolveRetryRuntimeConfig(config)); }; diff --git a/packages/smithy-client/src/extensions/retry.ts b/packages/smithy-client/src/extensions/retry.ts index bd19aea1d44..a7734d9e00e 100644 --- a/packages/smithy-client/src/extensions/retry.ts +++ b/packages/smithy-client/src/extensions/retry.ts @@ -9,13 +9,12 @@ export type PartialRetryRuntimeConfigType = Partial<{ retryStrategy: Provider { - let _retryStrategy = runtimeConfig.retryStrategy!; return { setRetryStrategy(retryStrategy: Provider): void { - _retryStrategy = retryStrategy; + runtimeConfig.retryStrategy = retryStrategy; }, retryStrategy(): Provider { - return _retryStrategy; + return runtimeConfig.retryStrategy!; }, }; }; diff --git a/packages/types/src/extensions/checksum.ts b/packages/types/src/extensions/checksum.ts index 43f34c1a66e..9861e90f638 100644 --- a/packages/types/src/extensions/checksum.ts +++ b/packages/types/src/extensions/checksum.ts @@ -77,12 +77,11 @@ export const getChecksumConfiguration: GetChecksumConfigurationType = ( } return { - _checksumAlgorithms: checksumAlgorithms, addChecksumAlgorithm(algo: ChecksumAlgorithm): void { - this._checksumAlgorithms.push(algo); + checksumAlgorithms.push(algo); }, checksumAlgorithms(): ChecksumAlgorithm[] { - return this._checksumAlgorithms; + return checksumAlgorithms; }, }; }; diff --git a/packages/types/src/extensions/defaultClientConfiguration.ts b/packages/types/src/extensions/defaultClientConfiguration.ts index f10b92806d4..892fad85b9d 100644 --- a/packages/types/src/extensions/defaultClientConfiguration.ts +++ b/packages/types/src/extensions/defaultClientConfiguration.ts @@ -21,9 +21,7 @@ type GetDefaultConfigurationType = (runtimeConfig: any) => DefaultClientConfigur * */ export const getDefaultClientConfiguration: GetDefaultConfigurationType = (runtimeConfig: any) => { - return { - ...getChecksumConfiguration(runtimeConfig), - }; + return getChecksumConfiguration(runtimeConfig); }; /** @@ -38,7 +36,5 @@ type ResolveDefaultRuntimeConfigType = (clientConfig: DefaultClientConfiguration * Helper function to resolve runtime config from default client configuration */ export const resolveDefaultRuntimeConfig: ResolveDefaultRuntimeConfigType = (config: DefaultClientConfiguration) => { - return { - ...resolveChecksumRuntimeConfig(config), - }; + return resolveChecksumRuntimeConfig(config); }; diff --git a/packages/util-stream/src/createBufferedReadable.ts b/packages/util-stream/src/createBufferedReadable.ts index 00d70592e9b..9c34b0db21f 100644 --- a/packages/util-stream/src/createBufferedReadable.ts +++ b/packages/util-stream/src/createBufferedReadable.ts @@ -36,7 +36,7 @@ export function createBufferedReadable( let mode: Modes | -1 = -1; upstream.on("data", (chunk) => { - const chunkMode = modeOf(chunk); + const chunkMode = modeOf(chunk, true); if (mode !== chunkMode) { if (mode >= 0) { downstream.push(flush(buffers, mode)); diff --git a/packages/util-stream/src/createBufferedReadableStream.ts b/packages/util-stream/src/createBufferedReadableStream.ts index b5e09029bef..23f7929cf09 100644 --- a/packages/util-stream/src/createBufferedReadableStream.ts +++ b/packages/util-stream/src/createBufferedReadableStream.ts @@ -10,7 +10,7 @@ export type Modes = 0 | 1 | 2; * @internal * @param upstream - any ReadableStream. * @param size - byte or character length minimum. Buffering occurs when a chunk fails to meet this value. - * @param onBuffer - for emitting warnings when buffering occurs. + * @param logger - for emitting warnings when buffering occurs. * @returns another stream of the same data, but buffers chunks until * the minimum size is met, except for the last chunk. */ @@ -35,7 +35,7 @@ export function createBufferedReadableStream(upstream: ReadableStream, size: num } controller.close(); } else { - const chunkMode = modeOf(chunk); + const chunkMode = modeOf(chunk, false); if (mode !== chunkMode) { if (mode >= 0) { controller.enqueue(flush(buffers, mode)); @@ -135,10 +135,11 @@ export function sizeOf(chunk?: { byteLength?: number; length?: number }): number /** * @internal * @param chunk - from upstream Readable. + * @param allowBuffer - allow mode 2 (Buffer), otherwise Buffer will return mode 1. * @returns type index of the chunk. */ -export function modeOf(chunk: BufferUnion): Modes | -1 { - if (typeof Buffer !== "undefined" && chunk instanceof Buffer) { +export function modeOf(chunk: BufferUnion, allowBuffer = true): Modes | -1 { + if (allowBuffer && typeof Buffer !== "undefined" && chunk instanceof Buffer) { return 2; } if (chunk instanceof Uint8Array) { diff --git a/private/smithy-rpcv2-cbor/src/RpcV2ProtocolClient.ts b/private/smithy-rpcv2-cbor/src/RpcV2ProtocolClient.ts index d978341a000..05e3283f2ea 100644 --- a/private/smithy-rpcv2-cbor/src/RpcV2ProtocolClient.ts +++ b/private/smithy-rpcv2-cbor/src/RpcV2ProtocolClient.ts @@ -247,11 +247,12 @@ export class RpcV2ProtocolClient extends __Client< constructor(...[configuration]: __CheckOptionalClientConfig) { let _config_0 = __getRuntimeConfig(configuration || {}); + super(_config_0 as any); + this.initConfig = _config_0; let _config_1 = resolveCustomEndpointsConfig(_config_0); let _config_2 = resolveRetryConfig(_config_1); let _config_3 = resolveHttpAuthSchemeConfig(_config_2); let _config_4 = resolveRuntimeExtensions(_config_3, configuration?.extensions || []); - super(_config_4); this.config = _config_4; this.middlewareStack.use(getRetryPlugin(this.config)); this.middlewareStack.use(getContentLengthPlugin(this.config)); diff --git a/private/smithy-rpcv2-cbor/src/auth/httpAuthSchemeProvider.ts b/private/smithy-rpcv2-cbor/src/auth/httpAuthSchemeProvider.ts index f9925d5944b..55bbd627a21 100644 --- a/private/smithy-rpcv2-cbor/src/auth/httpAuthSchemeProvider.ts +++ b/private/smithy-rpcv2-cbor/src/auth/httpAuthSchemeProvider.ts @@ -104,7 +104,5 @@ export interface HttpAuthSchemeResolvedConfig { export const resolveHttpAuthSchemeConfig = ( config: T & HttpAuthSchemeInputConfig ): T & HttpAuthSchemeResolvedConfig => { - return { - ...config, - } as T & HttpAuthSchemeResolvedConfig; + return Object.assign(config, {}) as T & HttpAuthSchemeResolvedConfig; }; diff --git a/private/smithy-rpcv2-cbor/src/runtimeExtensions.ts b/private/smithy-rpcv2-cbor/src/runtimeExtensions.ts index 275f5d03689..22f43123e01 100644 --- a/private/smithy-rpcv2-cbor/src/runtimeExtensions.ts +++ b/private/smithy-rpcv2-cbor/src/runtimeExtensions.ts @@ -18,24 +18,22 @@ export interface RuntimeExtensionsConfig { extensions: RuntimeExtension[]; } -const asPartial = >(t: T) => t; - /** * @internal */ export const resolveRuntimeExtensions = (runtimeConfig: any, extensions: RuntimeExtension[]) => { - const extensionConfiguration: RpcV2ProtocolExtensionConfiguration = { - ...asPartial(getDefaultExtensionConfiguration(runtimeConfig)), - ...asPartial(getHttpHandlerExtensionConfiguration(runtimeConfig)), - ...asPartial(getHttpAuthExtensionConfiguration(runtimeConfig)), - }; + const extensionConfiguration: RpcV2ProtocolExtensionConfiguration = Object.assign( + getDefaultExtensionConfiguration(runtimeConfig), + getHttpHandlerExtensionConfiguration(runtimeConfig), + getHttpAuthExtensionConfiguration(runtimeConfig) + ); extensions.forEach((extension) => extension.configure(extensionConfiguration)); - return { - ...runtimeConfig, - ...resolveDefaultRuntimeConfig(extensionConfiguration), - ...resolveHttpHandlerRuntimeConfig(extensionConfiguration), - ...resolveHttpAuthRuntimeConfig(extensionConfiguration), - }; + return Object.assign( + runtimeConfig, + resolveDefaultRuntimeConfig(extensionConfiguration), + resolveHttpHandlerRuntimeConfig(extensionConfiguration), + resolveHttpAuthRuntimeConfig(extensionConfiguration) + ); }; diff --git a/private/smithy-rpcv2-cbor/test/functional/rpcv2cbor.spec.ts b/private/smithy-rpcv2-cbor/test/functional/rpcv2cbor.spec.ts index ebe6175a9d5..937bb9928a6 100644 --- a/private/smithy-rpcv2-cbor/test/functional/rpcv2cbor.spec.ts +++ b/private/smithy-rpcv2-cbor/test/functional/rpcv2cbor.spec.ts @@ -829,7 +829,7 @@ it.skip("RpcV2CborClientPopulatesDefaultValuesInInput:Request", async () => { expect(r.headers["smithy-protocol"]).toBe("rpc-v2-cbor"); expect(r.body).toBeDefined(); - const bodyString = `v2hkZWZhdWx0c79tZGVmYXVsdFN0cmluZ2JoaW5kZWZhdWx0Qm9vbGVhbvVrZGVmYXVsdExpc3Sf/3BkZWZhdWx0VGltZXN0YW1wwQBrZGVmYXVsdEJsb2JjYWJja2RlZmF1bHRCeXRlAWxkZWZhdWx0U2hvcnQBbmRlZmF1bHRJbnRlZ2VyCmtkZWZhdWx0TG9uZxhkbGRlZmF1bHRGbG9hdPo/gAAAbWRlZmF1bHREb3VibGX6P4AAAGpkZWZhdWx0TWFwv/9rZGVmYXVsdEVudW1jRk9PbmRlZmF1bHRJbnRFbnVtAWtlbXB0eVN0cmluZ2BsZmFsc2VCb29sZWFu9GllbXB0eUJsb2JgaHplcm9CeXRlAGl6ZXJvU2hvcnQAa3plcm9JbnRlZ2VyAGh6ZXJvTG9uZwBpemVyb0Zsb2F0+gAAAABqemVyb0RvdWJsZfoAAAAA//8=`; + const bodyString = `v2hkZWZhdWx0c79tZGVmYXVsdFN0cmluZ2JoaW5kZWZhdWx0Qm9vbGVhbvVrZGVmYXVsdExpc3Sf/3BkZWZhdWx0VGltZXN0YW1wwQBrZGVmYXVsdEJsb2JDYWJja2RlZmF1bHRCeXRlAWxkZWZhdWx0U2hvcnQBbmRlZmF1bHRJbnRlZ2VyCmtkZWZhdWx0TG9uZxhkbGRlZmF1bHRGbG9hdPo/gAAAbWRlZmF1bHREb3VibGX6P4AAAGpkZWZhdWx0TWFwv/9rZGVmYXVsdEVudW1jRk9PbmRlZmF1bHRJbnRFbnVtAWtlbXB0eVN0cmluZ2BsZmFsc2VCb29sZWFu9GllbXB0eUJsb2JAaHplcm9CeXRlAGl6ZXJvU2hvcnQAa3plcm9JbnRlZ2VyAGh6ZXJvTG9uZwBpemVyb0Zsb2F0+gAAAABqemVyb0RvdWJsZfoAAAAA//8`; const unequalParts: any = compareEquivalentCborBodies(bodyString, r.body); expect(unequalParts).toBeUndefined(); } diff --git a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/RuntimeExtensionsGenerator.java b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/RuntimeExtensionsGenerator.java index 03b37c5c348..c6bc8351793 100644 --- a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/RuntimeExtensionsGenerator.java +++ b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/RuntimeExtensionsGenerator.java @@ -86,7 +86,7 @@ void generate() { writer.indent().onSection("getPartialExtensionConfigurations", original -> { for (TypeScriptIntegration integration : integrations) { integration.getExtensionConfigurationInterfaces(model, settings).forEach(configurationInterface -> { - writer.indent(2).write("...asPartial($L(runtimeConfig)),", + writer.indent(2).write("$L(runtimeConfig),", configurationInterface.getExtensionConfigurationFn().left); writer.dedent(2); }); @@ -97,7 +97,7 @@ void generate() { writer.indent().onSection("resolvePartialRuntimeConfigs", original -> { for (TypeScriptIntegration integration : integrations) { integration.getExtensionConfigurationInterfaces(model, settings).forEach(configurationInterface -> { - writer.indent(2).write("...$L(extensionConfiguration),", + writer.indent(2).write("$L(extensionConfiguration),", configurationInterface.resolveRuntimeConfigFn().left); writer.dedent(2); }); diff --git a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/ServiceBareBonesClientGenerator.java b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/ServiceBareBonesClientGenerator.java index 6be0ef279c1..1659b43bb47 100644 --- a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/ServiceBareBonesClientGenerator.java +++ b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/ServiceBareBonesClientGenerator.java @@ -375,8 +375,11 @@ private void generateConstructor() { .build()); int configVariable = 0; + String initialConfigVar = generateConfigVariable(configVariable); writer.write("let $L = __getRuntimeConfig(configuration || {});", - generateConfigVariable(configVariable)); + initialConfigVar); + writer.write("super($L as any);", initialConfigVar); + writer.write("this.initConfig = $L;", initialConfigVar); if (service.hasTrait(EndpointRuleSetTrait.class)) { configVariable++; @@ -429,7 +432,6 @@ private void generateConstructor() { generateConfigVariable(configVariable), generateConfigVariable(configVariable - 1)); - writer.write("super($L);", generateConfigVariable(configVariable)); writer.write("this.config = $L;", generateConfigVariable(configVariable)); // Add runtime plugins that contain middleware to the middleware stack diff --git a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/auth/http/integration/AddHttpAuthSchemePlugin.java b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/auth/http/integration/AddHttpAuthSchemePlugin.java index 78f73069aac..e45f7638dec 100644 --- a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/auth/http/integration/AddHttpAuthSchemePlugin.java +++ b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/auth/http/integration/AddHttpAuthSchemePlugin.java @@ -85,9 +85,6 @@ public List getClientPlugins() { .namespace(AuthUtils.HTTP_AUTH_SCHEME_PROVIDER_MODULE, "/") .name("resolveHttpAuthSchemeConfig") .build()) - .additionalResolveFunctionParamsSupplier((m, s, o) -> Map.of( - "client", Symbol.builder().name("() => this").build() - )) .build() ); } @@ -347,13 +344,12 @@ private void generateHttpAuthSchemeResolvedConfigInterface( const region = config.region ? normalizeProvider(config.region) : undefined; const apiKey = memoizeIdentityProvider(config.apiKey, isIdentityExpired, doesIdentityRequireRefresh); const token = memoizeIdentityProvider(config.token, isIdentityExpired, doesIdentityRequireRefresh); - return { - ...config, + return Object.assign(config, { credentials, region, apiKey, token, - } as HttpAuthSchemeResolvedConfig; + }) as HttpAuthSchemeResolvedConfig; }; */ private void generateResolveHttpAuthSchemeConfigFunction( @@ -368,7 +364,7 @@ private void generateResolveHttpAuthSchemeConfigFunction( .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); w.writeDocs("@internal"); w.writeInline(""" - export const resolveHttpAuthSchemeConfig = (config: T & HttpAuthSchemeInputConfig"""); + export const resolveHttpAuthSchemeConfig = (config: T & HttpAuthSchemeInputConfig"""); if (!previousResolvedFunctions.isEmpty()) { w.writeInline(" & "); Iterator iter = previousResolvedFunctions.values().iterator(); @@ -381,7 +377,6 @@ private void generateResolveHttpAuthSchemeConfigFunction( } } w.write(""" - , { client }: { client: () => { config: R } } ): T & HttpAuthSchemeResolvedConfig => {"""); w.indent(); w.pushState(ResolveHttpAuthSchemeConfigFunctionConfigFieldsCodeSection.builder() @@ -423,16 +418,16 @@ private void generateResolveHttpAuthSchemeConfigFunction( configName = "config_" + i; i++; } - w.write("return {"); + w.write("return Object.assign("); w.indent(); - w.write("...$L,", configName); + w.write("$L, {", configName); for (ConfigField configField : configFields.values()) { if (configField.configFieldWriter().isPresent()) { w.write("$L,", configField.name()); } } w.dedent(); - w.write("} as T & HttpAuthSchemeResolvedConfig;"); + w.write("}) as T & HttpAuthSchemeResolvedConfig;"); w.popState(); w.dedent(); w.write("};"); diff --git a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/endpointsV2/EndpointsV2Generator.java b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/endpointsV2/EndpointsV2Generator.java index 81cde408eff..d93222c7fbf 100644 --- a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/endpointsV2/EndpointsV2Generator.java +++ b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/endpointsV2/EndpointsV2Generator.java @@ -146,8 +146,7 @@ private void generateEndpointParameters() { + "): T & ClientResolvedEndpointParameters => {", "}", () -> { - writer.openBlock("return {", "}", () -> { - writer.write("...options,"); + writer.openBlock("return Object.assign(options, {", "});", () -> { ObjectNode ruleSet = endpointRuleSetTrait.getRuleSet().expectObjectNode(); ruleSet.getObjectMember("parameters").ifPresent(parameters -> { parameters.accept(new RuleSetParametersVisitor(writer, true)); diff --git a/smithy-typescript-codegen/src/main/resources/software/amazon/smithy/typescript/codegen/resolveRuntimeExtensions1.template b/smithy-typescript-codegen/src/main/resources/software/amazon/smithy/typescript/codegen/resolveRuntimeExtensions1.template index e66fe2c9f11..2b793daacca 100644 --- a/smithy-typescript-codegen/src/main/resources/software/amazon/smithy/typescript/codegen/resolveRuntimeExtensions1.template +++ b/smithy-typescript-codegen/src/main/resources/software/amazon/smithy/typescript/codegen/resolveRuntimeExtensions1.template @@ -14,8 +14,6 @@ export interface RuntimeExtensionsConfig { extensions: RuntimeExtension[] } -const asPartial = >(t: T) => t; - /** * @internal */ @@ -23,8 +21,8 @@ export const resolveRuntimeExtensions = ( runtimeConfig: any, extensions: RuntimeExtension[] ) => { - const extensionConfiguration: ${extensionConfigName} = { + const extensionConfiguration: ${extensionConfigName} = Object.assign( ${getPartialExtensionConfigurations} - }; + ); extensions.forEach(extension => extension.configure(extensionConfiguration)); diff --git a/smithy-typescript-codegen/src/main/resources/software/amazon/smithy/typescript/codegen/resolveRuntimeExtensions2.template b/smithy-typescript-codegen/src/main/resources/software/amazon/smithy/typescript/codegen/resolveRuntimeExtensions2.template index 0c9b01cc391..cd0966b0ccc 100644 --- a/smithy-typescript-codegen/src/main/resources/software/amazon/smithy/typescript/codegen/resolveRuntimeExtensions2.template +++ b/smithy-typescript-codegen/src/main/resources/software/amazon/smithy/typescript/codegen/resolveRuntimeExtensions2.template @@ -1,5 +1,4 @@ - return { - ...runtimeConfig, + return Object.assign(runtimeConfig, ${resolvePartialRuntimeConfigs} - }; + ); } diff --git a/smithy-typescript-codegen/src/main/resources/software/amazon/smithy/typescript/codegen/runtimeExtensions2.template b/smithy-typescript-codegen/src/main/resources/software/amazon/smithy/typescript/codegen/runtimeExtensions2.template index 0c9b01cc391..cd0966b0ccc 100644 --- a/smithy-typescript-codegen/src/main/resources/software/amazon/smithy/typescript/codegen/runtimeExtensions2.template +++ b/smithy-typescript-codegen/src/main/resources/software/amazon/smithy/typescript/codegen/runtimeExtensions2.template @@ -1,5 +1,4 @@ - return { - ...runtimeConfig, + return Object.assign(runtimeConfig, ${resolvePartialRuntimeConfigs} - }; + ); } diff --git a/smithy-typescript-codegen/src/test/java/software/amazon/smithy/typescript/codegen/endpointsV2/EndpointsV2GeneratorTest.java b/smithy-typescript-codegen/src/test/java/software/amazon/smithy/typescript/codegen/endpointsV2/EndpointsV2GeneratorTest.java index ecc03eeceaa..7ce94e99423 100644 --- a/smithy-typescript-codegen/src/test/java/software/amazon/smithy/typescript/codegen/endpointsV2/EndpointsV2GeneratorTest.java +++ b/smithy-typescript-codegen/src/test/java/software/amazon/smithy/typescript/codegen/endpointsV2/EndpointsV2GeneratorTest.java @@ -50,11 +50,10 @@ public void containsExtraContextParameter() { String endpointParameters = manifest.getFileString(CodegenUtils.SOURCE_FOLDER + "/endpoint/EndpointParameters.ts").get(); assertThat(endpointParameters, containsString( - " return {\n" + - " ...options,\n" + + " return Object.assign(options, {\n" + " stage: options.stage ?? \"production\",\n" + " defaultSigningName: \"\",\n" + - " }\n")); + " });\n")); assertThat(endpointParameters, containsString( "export interface ClientInputEndpointParameters {\n" + " region?: string|Provider;\n" + From e4613c621ce6a538a0ed5dae3ccf1752343b98f8 Mon Sep 17 00:00:00 2001 From: George Fu Date: Mon, 24 Mar 2025 16:26:11 -0400 Subject: [PATCH 2/2] separate java changes --- .../src/RpcV2ProtocolClient.ts | 3 +-- .../src/auth/httpAuthSchemeProvider.ts | 4 +++- .../src/runtimeExtensions.ts | 24 ++++++++++--------- .../test/functional/rpcv2cbor.spec.ts | 2 +- .../codegen/RuntimeExtensionsGenerator.java | 4 ++-- .../ServiceBareBonesClientGenerator.java | 6 ++--- .../integration/AddHttpAuthSchemePlugin.java | 17 ++++++++----- .../endpointsV2/EndpointsV2Generator.java | 3 ++- .../resolveRuntimeExtensions1.template | 6 +++-- .../resolveRuntimeExtensions2.template | 5 ++-- .../codegen/runtimeExtensions2.template | 5 ++-- .../endpointsV2/EndpointsV2GeneratorTest.java | 5 ++-- 12 files changed, 48 insertions(+), 36 deletions(-) diff --git a/private/smithy-rpcv2-cbor/src/RpcV2ProtocolClient.ts b/private/smithy-rpcv2-cbor/src/RpcV2ProtocolClient.ts index 05e3283f2ea..d978341a000 100644 --- a/private/smithy-rpcv2-cbor/src/RpcV2ProtocolClient.ts +++ b/private/smithy-rpcv2-cbor/src/RpcV2ProtocolClient.ts @@ -247,12 +247,11 @@ export class RpcV2ProtocolClient extends __Client< constructor(...[configuration]: __CheckOptionalClientConfig) { let _config_0 = __getRuntimeConfig(configuration || {}); - super(_config_0 as any); - this.initConfig = _config_0; let _config_1 = resolveCustomEndpointsConfig(_config_0); let _config_2 = resolveRetryConfig(_config_1); let _config_3 = resolveHttpAuthSchemeConfig(_config_2); let _config_4 = resolveRuntimeExtensions(_config_3, configuration?.extensions || []); + super(_config_4); this.config = _config_4; this.middlewareStack.use(getRetryPlugin(this.config)); this.middlewareStack.use(getContentLengthPlugin(this.config)); diff --git a/private/smithy-rpcv2-cbor/src/auth/httpAuthSchemeProvider.ts b/private/smithy-rpcv2-cbor/src/auth/httpAuthSchemeProvider.ts index 55bbd627a21..f9925d5944b 100644 --- a/private/smithy-rpcv2-cbor/src/auth/httpAuthSchemeProvider.ts +++ b/private/smithy-rpcv2-cbor/src/auth/httpAuthSchemeProvider.ts @@ -104,5 +104,7 @@ export interface HttpAuthSchemeResolvedConfig { export const resolveHttpAuthSchemeConfig = ( config: T & HttpAuthSchemeInputConfig ): T & HttpAuthSchemeResolvedConfig => { - return Object.assign(config, {}) as T & HttpAuthSchemeResolvedConfig; + return { + ...config, + } as T & HttpAuthSchemeResolvedConfig; }; diff --git a/private/smithy-rpcv2-cbor/src/runtimeExtensions.ts b/private/smithy-rpcv2-cbor/src/runtimeExtensions.ts index 22f43123e01..275f5d03689 100644 --- a/private/smithy-rpcv2-cbor/src/runtimeExtensions.ts +++ b/private/smithy-rpcv2-cbor/src/runtimeExtensions.ts @@ -18,22 +18,24 @@ export interface RuntimeExtensionsConfig { extensions: RuntimeExtension[]; } +const asPartial = >(t: T) => t; + /** * @internal */ export const resolveRuntimeExtensions = (runtimeConfig: any, extensions: RuntimeExtension[]) => { - const extensionConfiguration: RpcV2ProtocolExtensionConfiguration = Object.assign( - getDefaultExtensionConfiguration(runtimeConfig), - getHttpHandlerExtensionConfiguration(runtimeConfig), - getHttpAuthExtensionConfiguration(runtimeConfig) - ); + const extensionConfiguration: RpcV2ProtocolExtensionConfiguration = { + ...asPartial(getDefaultExtensionConfiguration(runtimeConfig)), + ...asPartial(getHttpHandlerExtensionConfiguration(runtimeConfig)), + ...asPartial(getHttpAuthExtensionConfiguration(runtimeConfig)), + }; extensions.forEach((extension) => extension.configure(extensionConfiguration)); - return Object.assign( - runtimeConfig, - resolveDefaultRuntimeConfig(extensionConfiguration), - resolveHttpHandlerRuntimeConfig(extensionConfiguration), - resolveHttpAuthRuntimeConfig(extensionConfiguration) - ); + return { + ...runtimeConfig, + ...resolveDefaultRuntimeConfig(extensionConfiguration), + ...resolveHttpHandlerRuntimeConfig(extensionConfiguration), + ...resolveHttpAuthRuntimeConfig(extensionConfiguration), + }; }; diff --git a/private/smithy-rpcv2-cbor/test/functional/rpcv2cbor.spec.ts b/private/smithy-rpcv2-cbor/test/functional/rpcv2cbor.spec.ts index 937bb9928a6..ebe6175a9d5 100644 --- a/private/smithy-rpcv2-cbor/test/functional/rpcv2cbor.spec.ts +++ b/private/smithy-rpcv2-cbor/test/functional/rpcv2cbor.spec.ts @@ -829,7 +829,7 @@ it.skip("RpcV2CborClientPopulatesDefaultValuesInInput:Request", async () => { expect(r.headers["smithy-protocol"]).toBe("rpc-v2-cbor"); expect(r.body).toBeDefined(); - const bodyString = `v2hkZWZhdWx0c79tZGVmYXVsdFN0cmluZ2JoaW5kZWZhdWx0Qm9vbGVhbvVrZGVmYXVsdExpc3Sf/3BkZWZhdWx0VGltZXN0YW1wwQBrZGVmYXVsdEJsb2JDYWJja2RlZmF1bHRCeXRlAWxkZWZhdWx0U2hvcnQBbmRlZmF1bHRJbnRlZ2VyCmtkZWZhdWx0TG9uZxhkbGRlZmF1bHRGbG9hdPo/gAAAbWRlZmF1bHREb3VibGX6P4AAAGpkZWZhdWx0TWFwv/9rZGVmYXVsdEVudW1jRk9PbmRlZmF1bHRJbnRFbnVtAWtlbXB0eVN0cmluZ2BsZmFsc2VCb29sZWFu9GllbXB0eUJsb2JAaHplcm9CeXRlAGl6ZXJvU2hvcnQAa3plcm9JbnRlZ2VyAGh6ZXJvTG9uZwBpemVyb0Zsb2F0+gAAAABqemVyb0RvdWJsZfoAAAAA//8`; + const bodyString = `v2hkZWZhdWx0c79tZGVmYXVsdFN0cmluZ2JoaW5kZWZhdWx0Qm9vbGVhbvVrZGVmYXVsdExpc3Sf/3BkZWZhdWx0VGltZXN0YW1wwQBrZGVmYXVsdEJsb2JjYWJja2RlZmF1bHRCeXRlAWxkZWZhdWx0U2hvcnQBbmRlZmF1bHRJbnRlZ2VyCmtkZWZhdWx0TG9uZxhkbGRlZmF1bHRGbG9hdPo/gAAAbWRlZmF1bHREb3VibGX6P4AAAGpkZWZhdWx0TWFwv/9rZGVmYXVsdEVudW1jRk9PbmRlZmF1bHRJbnRFbnVtAWtlbXB0eVN0cmluZ2BsZmFsc2VCb29sZWFu9GllbXB0eUJsb2JgaHplcm9CeXRlAGl6ZXJvU2hvcnQAa3plcm9JbnRlZ2VyAGh6ZXJvTG9uZwBpemVyb0Zsb2F0+gAAAABqemVyb0RvdWJsZfoAAAAA//8=`; const unequalParts: any = compareEquivalentCborBodies(bodyString, r.body); expect(unequalParts).toBeUndefined(); } diff --git a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/RuntimeExtensionsGenerator.java b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/RuntimeExtensionsGenerator.java index c6bc8351793..03b37c5c348 100644 --- a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/RuntimeExtensionsGenerator.java +++ b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/RuntimeExtensionsGenerator.java @@ -86,7 +86,7 @@ void generate() { writer.indent().onSection("getPartialExtensionConfigurations", original -> { for (TypeScriptIntegration integration : integrations) { integration.getExtensionConfigurationInterfaces(model, settings).forEach(configurationInterface -> { - writer.indent(2).write("$L(runtimeConfig),", + writer.indent(2).write("...asPartial($L(runtimeConfig)),", configurationInterface.getExtensionConfigurationFn().left); writer.dedent(2); }); @@ -97,7 +97,7 @@ void generate() { writer.indent().onSection("resolvePartialRuntimeConfigs", original -> { for (TypeScriptIntegration integration : integrations) { integration.getExtensionConfigurationInterfaces(model, settings).forEach(configurationInterface -> { - writer.indent(2).write("$L(extensionConfiguration),", + writer.indent(2).write("...$L(extensionConfiguration),", configurationInterface.resolveRuntimeConfigFn().left); writer.dedent(2); }); diff --git a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/ServiceBareBonesClientGenerator.java b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/ServiceBareBonesClientGenerator.java index 1659b43bb47..6be0ef279c1 100644 --- a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/ServiceBareBonesClientGenerator.java +++ b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/ServiceBareBonesClientGenerator.java @@ -375,11 +375,8 @@ private void generateConstructor() { .build()); int configVariable = 0; - String initialConfigVar = generateConfigVariable(configVariable); writer.write("let $L = __getRuntimeConfig(configuration || {});", - initialConfigVar); - writer.write("super($L as any);", initialConfigVar); - writer.write("this.initConfig = $L;", initialConfigVar); + generateConfigVariable(configVariable)); if (service.hasTrait(EndpointRuleSetTrait.class)) { configVariable++; @@ -432,6 +429,7 @@ private void generateConstructor() { generateConfigVariable(configVariable), generateConfigVariable(configVariable - 1)); + writer.write("super($L);", generateConfigVariable(configVariable)); writer.write("this.config = $L;", generateConfigVariable(configVariable)); // Add runtime plugins that contain middleware to the middleware stack diff --git a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/auth/http/integration/AddHttpAuthSchemePlugin.java b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/auth/http/integration/AddHttpAuthSchemePlugin.java index e45f7638dec..78f73069aac 100644 --- a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/auth/http/integration/AddHttpAuthSchemePlugin.java +++ b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/auth/http/integration/AddHttpAuthSchemePlugin.java @@ -85,6 +85,9 @@ public List getClientPlugins() { .namespace(AuthUtils.HTTP_AUTH_SCHEME_PROVIDER_MODULE, "/") .name("resolveHttpAuthSchemeConfig") .build()) + .additionalResolveFunctionParamsSupplier((m, s, o) -> Map.of( + "client", Symbol.builder().name("() => this").build() + )) .build() ); } @@ -344,12 +347,13 @@ private void generateHttpAuthSchemeResolvedConfigInterface( const region = config.region ? normalizeProvider(config.region) : undefined; const apiKey = memoizeIdentityProvider(config.apiKey, isIdentityExpired, doesIdentityRequireRefresh); const token = memoizeIdentityProvider(config.token, isIdentityExpired, doesIdentityRequireRefresh); - return Object.assign(config, { + return { + ...config, credentials, region, apiKey, token, - }) as HttpAuthSchemeResolvedConfig; + } as HttpAuthSchemeResolvedConfig; }; */ private void generateResolveHttpAuthSchemeConfigFunction( @@ -364,7 +368,7 @@ private void generateResolveHttpAuthSchemeConfigFunction( .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); w.writeDocs("@internal"); w.writeInline(""" - export const resolveHttpAuthSchemeConfig = (config: T & HttpAuthSchemeInputConfig"""); + export const resolveHttpAuthSchemeConfig = (config: T & HttpAuthSchemeInputConfig"""); if (!previousResolvedFunctions.isEmpty()) { w.writeInline(" & "); Iterator iter = previousResolvedFunctions.values().iterator(); @@ -377,6 +381,7 @@ private void generateResolveHttpAuthSchemeConfigFunction( } } w.write(""" + , { client }: { client: () => { config: R } } ): T & HttpAuthSchemeResolvedConfig => {"""); w.indent(); w.pushState(ResolveHttpAuthSchemeConfigFunctionConfigFieldsCodeSection.builder() @@ -418,16 +423,16 @@ private void generateResolveHttpAuthSchemeConfigFunction( configName = "config_" + i; i++; } - w.write("return Object.assign("); + w.write("return {"); w.indent(); - w.write("$L, {", configName); + w.write("...$L,", configName); for (ConfigField configField : configFields.values()) { if (configField.configFieldWriter().isPresent()) { w.write("$L,", configField.name()); } } w.dedent(); - w.write("}) as T & HttpAuthSchemeResolvedConfig;"); + w.write("} as T & HttpAuthSchemeResolvedConfig;"); w.popState(); w.dedent(); w.write("};"); diff --git a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/endpointsV2/EndpointsV2Generator.java b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/endpointsV2/EndpointsV2Generator.java index d93222c7fbf..81cde408eff 100644 --- a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/endpointsV2/EndpointsV2Generator.java +++ b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/endpointsV2/EndpointsV2Generator.java @@ -146,7 +146,8 @@ private void generateEndpointParameters() { + "): T & ClientResolvedEndpointParameters => {", "}", () -> { - writer.openBlock("return Object.assign(options, {", "});", () -> { + writer.openBlock("return {", "}", () -> { + writer.write("...options,"); ObjectNode ruleSet = endpointRuleSetTrait.getRuleSet().expectObjectNode(); ruleSet.getObjectMember("parameters").ifPresent(parameters -> { parameters.accept(new RuleSetParametersVisitor(writer, true)); diff --git a/smithy-typescript-codegen/src/main/resources/software/amazon/smithy/typescript/codegen/resolveRuntimeExtensions1.template b/smithy-typescript-codegen/src/main/resources/software/amazon/smithy/typescript/codegen/resolveRuntimeExtensions1.template index 2b793daacca..e66fe2c9f11 100644 --- a/smithy-typescript-codegen/src/main/resources/software/amazon/smithy/typescript/codegen/resolveRuntimeExtensions1.template +++ b/smithy-typescript-codegen/src/main/resources/software/amazon/smithy/typescript/codegen/resolveRuntimeExtensions1.template @@ -14,6 +14,8 @@ export interface RuntimeExtensionsConfig { extensions: RuntimeExtension[] } +const asPartial = >(t: T) => t; + /** * @internal */ @@ -21,8 +23,8 @@ export const resolveRuntimeExtensions = ( runtimeConfig: any, extensions: RuntimeExtension[] ) => { - const extensionConfiguration: ${extensionConfigName} = Object.assign( + const extensionConfiguration: ${extensionConfigName} = { ${getPartialExtensionConfigurations} - ); + }; extensions.forEach(extension => extension.configure(extensionConfiguration)); diff --git a/smithy-typescript-codegen/src/main/resources/software/amazon/smithy/typescript/codegen/resolveRuntimeExtensions2.template b/smithy-typescript-codegen/src/main/resources/software/amazon/smithy/typescript/codegen/resolveRuntimeExtensions2.template index cd0966b0ccc..0c9b01cc391 100644 --- a/smithy-typescript-codegen/src/main/resources/software/amazon/smithy/typescript/codegen/resolveRuntimeExtensions2.template +++ b/smithy-typescript-codegen/src/main/resources/software/amazon/smithy/typescript/codegen/resolveRuntimeExtensions2.template @@ -1,4 +1,5 @@ - return Object.assign(runtimeConfig, + return { + ...runtimeConfig, ${resolvePartialRuntimeConfigs} - ); + }; } diff --git a/smithy-typescript-codegen/src/main/resources/software/amazon/smithy/typescript/codegen/runtimeExtensions2.template b/smithy-typescript-codegen/src/main/resources/software/amazon/smithy/typescript/codegen/runtimeExtensions2.template index cd0966b0ccc..0c9b01cc391 100644 --- a/smithy-typescript-codegen/src/main/resources/software/amazon/smithy/typescript/codegen/runtimeExtensions2.template +++ b/smithy-typescript-codegen/src/main/resources/software/amazon/smithy/typescript/codegen/runtimeExtensions2.template @@ -1,4 +1,5 @@ - return Object.assign(runtimeConfig, + return { + ...runtimeConfig, ${resolvePartialRuntimeConfigs} - ); + }; } diff --git a/smithy-typescript-codegen/src/test/java/software/amazon/smithy/typescript/codegen/endpointsV2/EndpointsV2GeneratorTest.java b/smithy-typescript-codegen/src/test/java/software/amazon/smithy/typescript/codegen/endpointsV2/EndpointsV2GeneratorTest.java index 7ce94e99423..ecc03eeceaa 100644 --- a/smithy-typescript-codegen/src/test/java/software/amazon/smithy/typescript/codegen/endpointsV2/EndpointsV2GeneratorTest.java +++ b/smithy-typescript-codegen/src/test/java/software/amazon/smithy/typescript/codegen/endpointsV2/EndpointsV2GeneratorTest.java @@ -50,10 +50,11 @@ public void containsExtraContextParameter() { String endpointParameters = manifest.getFileString(CodegenUtils.SOURCE_FOLDER + "/endpoint/EndpointParameters.ts").get(); assertThat(endpointParameters, containsString( - " return Object.assign(options, {\n" + + " return {\n" + + " ...options,\n" + " stage: options.stage ?? \"production\",\n" + " defaultSigningName: \"\",\n" + - " });\n")); + " }\n")); assertThat(endpointParameters, containsString( "export interface ClientInputEndpointParameters {\n" + " region?: string|Provider;\n" +