Skip to content

Commit

Permalink
chore(middleware-flexible-checksums): add RequestChecksumCalculation …
Browse files Browse the repository at this point in the history
…and ResponseChecksumValidation (#6465)
  • Loading branch information
trivikr authored Sep 12, 2024
1 parent 059223d commit ba923e6
Show file tree
Hide file tree
Showing 7 changed files with 148 additions and 0 deletions.
1 change: 1 addition & 0 deletions packages/middleware-flexible-checksums/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": "*",
Expand Down
Original file line number Diff line number Diff line change
@@ -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<string> = {
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,
};
Original file line number Diff line number Diff line change
@@ -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<string> = {
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,
};
46 changes: 46 additions & 0 deletions packages/middleware-flexible-checksums/src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,49 @@
/**
* Determines when a checksum will be calculated for request payloads.
*/
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 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",
} as const;

export type RequestChecksumCalculation = (typeof RequestChecksumCalculation)[keyof typeof RequestChecksumCalculation];

/**
* Determines when checksum validation will be performed on response payloads.
*/
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 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",
} as const;

export type ResponseChecksumValidation = (typeof ResponseChecksumValidation)[keyof typeof ResponseChecksumValidation];

/**
* Checksum Algorithms supported by the SDK.
*/
Expand Down
2 changes: 2 additions & 0 deletions packages/middleware-flexible-checksums/src/index.ts
Original file line number Diff line number Diff line change
@@ -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";
Original file line number Diff line number Diff line change
@@ -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]}'.`
)
);
});
});
});
27 changes: 27 additions & 0 deletions packages/middleware-flexible-checksums/src/stringUnionSelector.ts
Original file line number Diff line number Diff line change
@@ -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<string, string | undefined>,
key: string,
union: Record<string, string>,
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;
};

0 comments on commit ba923e6

Please sign in to comment.