From 853d833bd75b5fe32db7ccc572c74ad9e36f905b Mon Sep 17 00:00:00 2001 From: "Kamat, Trivikram" <16024985+trivikr@users.noreply.github.com> Date: Thu, 12 Sep 2024 15:50:38 +0000 Subject: [PATCH 1/4] chore(middleware-flexible-checksums): add RequestChecksumCalculation and ResponseChecksumValidation --- .../src/constants.ts | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/packages/middleware-flexible-checksums/src/constants.ts b/packages/middleware-flexible-checksums/src/constants.ts index dc68086032c5..3869a198b027 100644 --- a/packages/middleware-flexible-checksums/src/constants.ts +++ b/packages/middleware-flexible-checksums/src/constants.ts @@ -1,3 +1,45 @@ +/** + * Determines when a checksum will be calculated for request payloads. + */ +export enum RequestChecksumCalculation { + /** + * When set, a checksum will be calculated for all request payloads of operations + * modeled with the {@link httpChecksum} trait where `requestChecksumRequired` is `true` + * AND/OR a `requestAlgorithmMember` is modeled. + * {@link https://smithy.io/2.0/aws/aws-core.html#aws-protocols-httpchecksum-trait httpChecksum} + */ + WHEN_SUPPORTED = "WHEN_SUPPORTED", + + /** + * When set, a checksum will only be calculated for request payloads of operations + * modeled with the {@link httpChecksum} trait where `requestChecksumRequired` is `true` + * OR where a `requestAlgorithmMember` is modeled and the user sets it. + * {@link https://smithy.io/2.0/aws/aws-core.html#aws-protocols-httpchecksum-trait httpChecksum} + */ + WHEN_REQUIRED = "WHEN_REQUIRED", +} + +/** + * Determines when checksum validation will be performed on response payloads. + */ +export enum ResponseChecksumValidation { + /** + * When set, checksum validation MUST be performed on all response payloads of operations + * modeled with the {@link httpChecksum} trait where `responseAlgorithms` is modeled, + * except when no modeled checksum algorithms are supported by an SDK. + * {@link https://smithy.io/2.0/aws/aws-core.html#aws-protocols-httpchecksum-trait httpChecksum} + */ + WHEN_SUPPORTED = "WHEN_SUPPORTED", + + /** + * When set, checksum validation MUST NOT be performed on response payloads of operations UNLESS + * the SDK supports the modeled checksum algorithms AND the user has set the `requestValidationModeMember` to `ENABLED`. + * It is currently impossible to model an operation as requiring a response checksum, + * but this setting leaves the door open for future updates. + */ + WHEN_REQUIRED = "WHEN_REQUIRED", +} + /** * Checksum Algorithms supported by the SDK. */ From 5156e60b9b9a16cf394baa749662af96339e59b2 Mon Sep 17 00:00:00 2001 From: "Kamat, Trivikram" <16024985+trivikr@users.noreply.github.com> Date: Thu, 12 Sep 2024 16:02:51 +0000 Subject: [PATCH 2/4] chore: use discriminated unions over string enums Refs: https://rlee.dev/stop-misusing-typescript-string-enums#heading-drawbacks-of-string-enums --- .../src/constants.ts | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/packages/middleware-flexible-checksums/src/constants.ts b/packages/middleware-flexible-checksums/src/constants.ts index 3869a198b027..ac944f5fe23f 100644 --- a/packages/middleware-flexible-checksums/src/constants.ts +++ b/packages/middleware-flexible-checksums/src/constants.ts @@ -1,14 +1,14 @@ /** * Determines when a checksum will be calculated for request payloads. */ -export enum RequestChecksumCalculation { +export const RequestChecksumCalculation = { /** * When set, a checksum will be calculated for all request payloads of operations * modeled with the {@link httpChecksum} trait where `requestChecksumRequired` is `true` * AND/OR a `requestAlgorithmMember` is modeled. * {@link https://smithy.io/2.0/aws/aws-core.html#aws-protocols-httpchecksum-trait httpChecksum} */ - WHEN_SUPPORTED = "WHEN_SUPPORTED", + WHEN_SUPPORTED: "WHEN_SUPPORTED", /** * When set, a checksum will only be calculated for request payloads of operations @@ -16,20 +16,22 @@ export enum RequestChecksumCalculation { * OR where a `requestAlgorithmMember` is modeled and the user sets it. * {@link https://smithy.io/2.0/aws/aws-core.html#aws-protocols-httpchecksum-trait httpChecksum} */ - WHEN_REQUIRED = "WHEN_REQUIRED", -} + WHEN_REQUIRED: "WHEN_REQUIRED", +} as const; + +export type RequestChecksumCalculation = (typeof RequestChecksumCalculation)[keyof typeof RequestChecksumCalculation]; /** * Determines when checksum validation will be performed on response payloads. */ -export enum ResponseChecksumValidation { +export const ResponseChecksumValidation = { /** * When set, checksum validation MUST be performed on all response payloads of operations * modeled with the {@link httpChecksum} trait where `responseAlgorithms` is modeled, * except when no modeled checksum algorithms are supported by an SDK. * {@link https://smithy.io/2.0/aws/aws-core.html#aws-protocols-httpchecksum-trait httpChecksum} */ - WHEN_SUPPORTED = "WHEN_SUPPORTED", + WHEN_SUPPORTED: "WHEN_SUPPORTED", /** * When set, checksum validation MUST NOT be performed on response payloads of operations UNLESS @@ -37,8 +39,10 @@ export enum ResponseChecksumValidation { * It is currently impossible to model an operation as requiring a response checksum, * but this setting leaves the door open for future updates. */ - WHEN_REQUIRED = "WHEN_REQUIRED", -} + WHEN_REQUIRED: "WHEN_REQUIRED", +} as const; + +export type ResponseChecksumValidation = (typeof ResponseChecksumValidation)[keyof typeof ResponseChecksumValidation]; /** * Checksum Algorithms supported by the SDK. From b6b802a6a40fdde366a02b4cab258cce93cf5f8f Mon Sep 17 00:00:00 2001 From: "Kamat, Trivikram" <16024985+trivikr@users.noreply.github.com> Date: Thu, 12 Sep 2024 16:58:02 +0000 Subject: [PATCH 3/4] chore: add utility stringUnionSelector --- .../src/stringUnionSelector.spec.ts | 40 +++++++++++++++++++ .../src/stringUnionSelector.ts | 27 +++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 packages/middleware-flexible-checksums/src/stringUnionSelector.spec.ts create mode 100644 packages/middleware-flexible-checksums/src/stringUnionSelector.ts diff --git a/packages/middleware-flexible-checksums/src/stringUnionSelector.spec.ts b/packages/middleware-flexible-checksums/src/stringUnionSelector.spec.ts new file mode 100644 index 000000000000..6bcfae43b0e8 --- /dev/null +++ b/packages/middleware-flexible-checksums/src/stringUnionSelector.spec.ts @@ -0,0 +1,40 @@ +import { SelectorType, stringUnionSelector } from "./stringUnionSelector"; + +describe(stringUnionSelector.name, () => { + const key = "key"; + const value = "VALUE"; + const obj: { [key]: any } = {} as any; + const union = { [key]: value }; + + describe.each(Object.entries(SelectorType))(`Selector %s`, (selectorKey, selectorValue) => { + beforeEach(() => { + delete obj[key]; + }); + + it(`should return undefined if ${key} is not defined`, () => { + // @ts-expect-error Element implicitly has an 'any' type + expect(stringUnionSelector(obj, key, union, SelectorType[selectorKey])).toBeUndefined(); + }); + + it.each([ + [value, value], + [value, value.toLowerCase()], + [value, [...value].map((c, i) => (i % 2 === 0 ? c.toLowerCase() : c.toUpperCase())).join("")], + ])(`should return number %s if ${key}="%s"`, (output, input) => { + obj[key] = input; + // @ts-expect-error Element implicitly has an 'any' type + expect(stringUnionSelector(obj, key, union, SelectorType[selectorKey])).toBe(output); + }); + + // Thows if the value is something other than different case. + it.each(["value1", "1value", [...value].reverse().join("")])(`should throw if ${key}=%s`, (input) => { + obj[key] = input; + // @ts-expect-error Element implicitly has an 'any' type + expect(() => stringUnionSelector(obj, key, union, SelectorType[selectorKey])).toThrow( + new TypeError( + `Cannot load ${selectorValue} '${key}'. Expected one of ${Object.values(union)}, got '${obj[key]}'.` + ) + ); + }); + }); +}); diff --git a/packages/middleware-flexible-checksums/src/stringUnionSelector.ts b/packages/middleware-flexible-checksums/src/stringUnionSelector.ts new file mode 100644 index 000000000000..2ad49baa50e8 --- /dev/null +++ b/packages/middleware-flexible-checksums/src/stringUnionSelector.ts @@ -0,0 +1,27 @@ +export enum SelectorType { + ENV = "env", + CONFIG = "shared config entry", +} + +/** + * Returns undefined, if obj[key] is not defined. + * Returns string value, if the string is defined in obj[key] and it's uppercase matches union value. + * Throws error for all other cases. + * + * @internal + */ +export const stringUnionSelector = ( + obj: Record, + key: string, + union: Record, + type: SelectorType +) => { + if (!(key in obj)) return undefined; + + const value = obj[key]!.toUpperCase(); + if (!Object.values(union).includes(value)) { + throw new TypeError(`Cannot load ${type} '${key}'. Expected one of ${Object.values(union)}, got '${obj[key]}'.`); + } + + return value; +}; From c218146ef426768c14766a10bc4820ad92108295 Mon Sep 17 00:00:00 2001 From: "Kamat, Trivikram" <16024985+trivikr@users.noreply.github.com> Date: Thu, 12 Sep 2024 17:02:40 +0000 Subject: [PATCH 4/4] chore: add Node Config Options --- .../middleware-flexible-checksums/package.json | 1 + ...EQUEST_CHECKSUM_CALCULATION_CONFIG_OPTIONS.ts | 16 ++++++++++++++++ ...ESPONSE_CHECKSUM_VALIDATION_CONFIG_OPTIONS.ts | 16 ++++++++++++++++ .../middleware-flexible-checksums/src/index.ts | 2 ++ 4 files changed, 35 insertions(+) create mode 100644 packages/middleware-flexible-checksums/src/NODE_REQUEST_CHECKSUM_CALCULATION_CONFIG_OPTIONS.ts create mode 100644 packages/middleware-flexible-checksums/src/NODE_RESPONSE_CHECKSUM_VALIDATION_CONFIG_OPTIONS.ts diff --git a/packages/middleware-flexible-checksums/package.json b/packages/middleware-flexible-checksums/package.json index 3a0dbcc9d20d..8254d604d6a4 100644 --- a/packages/middleware-flexible-checksums/package.json +++ b/packages/middleware-flexible-checksums/package.json @@ -28,6 +28,7 @@ }, "license": "Apache-2.0", "dependencies": { + "@smithy/node-config-provider": "^3.1.5", "@aws-crypto/crc32": "5.2.0", "@aws-crypto/crc32c": "5.2.0", "@aws-sdk/types": "*", diff --git a/packages/middleware-flexible-checksums/src/NODE_REQUEST_CHECKSUM_CALCULATION_CONFIG_OPTIONS.ts b/packages/middleware-flexible-checksums/src/NODE_REQUEST_CHECKSUM_CALCULATION_CONFIG_OPTIONS.ts new file mode 100644 index 000000000000..ac105c94d97a --- /dev/null +++ b/packages/middleware-flexible-checksums/src/NODE_REQUEST_CHECKSUM_CALCULATION_CONFIG_OPTIONS.ts @@ -0,0 +1,16 @@ +import { LoadedConfigSelectors } from "@smithy/node-config-provider"; + +import { RequestChecksumCalculation } from "./constants"; +import { SelectorType, stringUnionSelector } from "./stringUnionSelector"; + +export const ENV_REQUEST_CHECKSUM_CALCULATION = "AWS_REQUEST_CHECKSUM_CALCULATION"; +export const CONFIG_REQUEST_CHECKSUM_CALCULATION = "request_checksum_calculation"; +export const DEFAULT_REQUEST_CHECKSUM_CALCULATION = RequestChecksumCalculation.WHEN_SUPPORTED; + +export const NODE_REQUEST_CHECKSUM_CALCULATION_CONFIG_OPTIONS: LoadedConfigSelectors = { + environmentVariableSelector: (env) => + stringUnionSelector(env, ENV_REQUEST_CHECKSUM_CALCULATION, RequestChecksumCalculation, SelectorType.ENV), + configFileSelector: (profile) => + stringUnionSelector(profile, CONFIG_REQUEST_CHECKSUM_CALCULATION, RequestChecksumCalculation, SelectorType.CONFIG), + default: DEFAULT_REQUEST_CHECKSUM_CALCULATION, +}; diff --git a/packages/middleware-flexible-checksums/src/NODE_RESPONSE_CHECKSUM_VALIDATION_CONFIG_OPTIONS.ts b/packages/middleware-flexible-checksums/src/NODE_RESPONSE_CHECKSUM_VALIDATION_CONFIG_OPTIONS.ts new file mode 100644 index 000000000000..197b4ea6d052 --- /dev/null +++ b/packages/middleware-flexible-checksums/src/NODE_RESPONSE_CHECKSUM_VALIDATION_CONFIG_OPTIONS.ts @@ -0,0 +1,16 @@ +import { LoadedConfigSelectors } from "@smithy/node-config-provider"; + +import { RequestChecksumCalculation } from "./constants"; +import { SelectorType, stringUnionSelector } from "./stringUnionSelector"; + +export const ENV_RESPONSE_CHECKSUM_VALIDATION = "AWS_RESPONSE_CHECKSUM_VALIDATION"; +export const CONFIG_RESPONSE_CHECKSUM_VALIDATION = "response_checksum_validation"; +export const DEFAULT_RESPONSE_CHECKSUM_VALIDATION = RequestChecksumCalculation.WHEN_SUPPORTED; + +export const NODE_RESPONSE_CHECKSUM_VALIDATION_CONFIG_OPTIONS: LoadedConfigSelectors = { + environmentVariableSelector: (env) => + stringUnionSelector(env, ENV_RESPONSE_CHECKSUM_VALIDATION, RequestChecksumCalculation, SelectorType.ENV), + configFileSelector: (profile) => + stringUnionSelector(profile, CONFIG_RESPONSE_CHECKSUM_VALIDATION, RequestChecksumCalculation, SelectorType.CONFIG), + default: DEFAULT_RESPONSE_CHECKSUM_VALIDATION, +}; diff --git a/packages/middleware-flexible-checksums/src/index.ts b/packages/middleware-flexible-checksums/src/index.ts index 26a8b50c93b2..8ff2b77d48e1 100644 --- a/packages/middleware-flexible-checksums/src/index.ts +++ b/packages/middleware-flexible-checksums/src/index.ts @@ -1,3 +1,5 @@ +export * from "./NODE_REQUEST_CHECKSUM_CALCULATION_CONFIG_OPTIONS"; +export * from "./NODE_RESPONSE_CHECKSUM_VALIDATION_CONFIG_OPTIONS"; export * from "./constants"; export * from "./flexibleChecksumsMiddleware"; export * from "./getFlexibleChecksumsPlugin";