Skip to content

Commit ba923e6

Browse files
authored
chore(middleware-flexible-checksums): add RequestChecksumCalculation and ResponseChecksumValidation (#6465)
1 parent 059223d commit ba923e6

7 files changed

+148
-0
lines changed

packages/middleware-flexible-checksums/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
},
2929
"license": "Apache-2.0",
3030
"dependencies": {
31+
"@smithy/node-config-provider": "^3.1.5",
3132
"@aws-crypto/crc32": "5.2.0",
3233
"@aws-crypto/crc32c": "5.2.0",
3334
"@aws-sdk/types": "*",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { LoadedConfigSelectors } from "@smithy/node-config-provider";
2+
3+
import { RequestChecksumCalculation } from "./constants";
4+
import { SelectorType, stringUnionSelector } from "./stringUnionSelector";
5+
6+
export const ENV_REQUEST_CHECKSUM_CALCULATION = "AWS_REQUEST_CHECKSUM_CALCULATION";
7+
export const CONFIG_REQUEST_CHECKSUM_CALCULATION = "request_checksum_calculation";
8+
export const DEFAULT_REQUEST_CHECKSUM_CALCULATION = RequestChecksumCalculation.WHEN_SUPPORTED;
9+
10+
export const NODE_REQUEST_CHECKSUM_CALCULATION_CONFIG_OPTIONS: LoadedConfigSelectors<string> = {
11+
environmentVariableSelector: (env) =>
12+
stringUnionSelector(env, ENV_REQUEST_CHECKSUM_CALCULATION, RequestChecksumCalculation, SelectorType.ENV),
13+
configFileSelector: (profile) =>
14+
stringUnionSelector(profile, CONFIG_REQUEST_CHECKSUM_CALCULATION, RequestChecksumCalculation, SelectorType.CONFIG),
15+
default: DEFAULT_REQUEST_CHECKSUM_CALCULATION,
16+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { LoadedConfigSelectors } from "@smithy/node-config-provider";
2+
3+
import { RequestChecksumCalculation } from "./constants";
4+
import { SelectorType, stringUnionSelector } from "./stringUnionSelector";
5+
6+
export const ENV_RESPONSE_CHECKSUM_VALIDATION = "AWS_RESPONSE_CHECKSUM_VALIDATION";
7+
export const CONFIG_RESPONSE_CHECKSUM_VALIDATION = "response_checksum_validation";
8+
export const DEFAULT_RESPONSE_CHECKSUM_VALIDATION = RequestChecksumCalculation.WHEN_SUPPORTED;
9+
10+
export const NODE_RESPONSE_CHECKSUM_VALIDATION_CONFIG_OPTIONS: LoadedConfigSelectors<string> = {
11+
environmentVariableSelector: (env) =>
12+
stringUnionSelector(env, ENV_RESPONSE_CHECKSUM_VALIDATION, RequestChecksumCalculation, SelectorType.ENV),
13+
configFileSelector: (profile) =>
14+
stringUnionSelector(profile, CONFIG_RESPONSE_CHECKSUM_VALIDATION, RequestChecksumCalculation, SelectorType.CONFIG),
15+
default: DEFAULT_RESPONSE_CHECKSUM_VALIDATION,
16+
};

packages/middleware-flexible-checksums/src/constants.ts

+46
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,49 @@
1+
/**
2+
* Determines when a checksum will be calculated for request payloads.
3+
*/
4+
export const RequestChecksumCalculation = {
5+
/**
6+
* When set, a checksum will be calculated for all request payloads of operations
7+
* modeled with the {@link httpChecksum} trait where `requestChecksumRequired` is `true`
8+
* AND/OR a `requestAlgorithmMember` is modeled.
9+
* {@link https://smithy.io/2.0/aws/aws-core.html#aws-protocols-httpchecksum-trait httpChecksum}
10+
*/
11+
WHEN_SUPPORTED: "WHEN_SUPPORTED",
12+
13+
/**
14+
* When set, a checksum will only be calculated for request payloads of operations
15+
* modeled with the {@link httpChecksum} trait where `requestChecksumRequired` is `true`
16+
* OR where a `requestAlgorithmMember` is modeled and the user sets it.
17+
* {@link https://smithy.io/2.0/aws/aws-core.html#aws-protocols-httpchecksum-trait httpChecksum}
18+
*/
19+
WHEN_REQUIRED: "WHEN_REQUIRED",
20+
} as const;
21+
22+
export type RequestChecksumCalculation = (typeof RequestChecksumCalculation)[keyof typeof RequestChecksumCalculation];
23+
24+
/**
25+
* Determines when checksum validation will be performed on response payloads.
26+
*/
27+
export const ResponseChecksumValidation = {
28+
/**
29+
* When set, checksum validation MUST be performed on all response payloads of operations
30+
* modeled with the {@link httpChecksum} trait where `responseAlgorithms` is modeled,
31+
* except when no modeled checksum algorithms are supported by an SDK.
32+
* {@link https://smithy.io/2.0/aws/aws-core.html#aws-protocols-httpchecksum-trait httpChecksum}
33+
*/
34+
WHEN_SUPPORTED: "WHEN_SUPPORTED",
35+
36+
/**
37+
* When set, checksum validation MUST NOT be performed on response payloads of operations UNLESS
38+
* the SDK supports the modeled checksum algorithms AND the user has set the `requestValidationModeMember` to `ENABLED`.
39+
* It is currently impossible to model an operation as requiring a response checksum,
40+
* but this setting leaves the door open for future updates.
41+
*/
42+
WHEN_REQUIRED: "WHEN_REQUIRED",
43+
} as const;
44+
45+
export type ResponseChecksumValidation = (typeof ResponseChecksumValidation)[keyof typeof ResponseChecksumValidation];
46+
147
/**
248
* Checksum Algorithms supported by the SDK.
349
*/
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
export * from "./NODE_REQUEST_CHECKSUM_CALCULATION_CONFIG_OPTIONS";
2+
export * from "./NODE_RESPONSE_CHECKSUM_VALIDATION_CONFIG_OPTIONS";
13
export * from "./constants";
24
export * from "./flexibleChecksumsMiddleware";
35
export * from "./getFlexibleChecksumsPlugin";
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { SelectorType, stringUnionSelector } from "./stringUnionSelector";
2+
3+
describe(stringUnionSelector.name, () => {
4+
const key = "key";
5+
const value = "VALUE";
6+
const obj: { [key]: any } = {} as any;
7+
const union = { [key]: value };
8+
9+
describe.each(Object.entries(SelectorType))(`Selector %s`, (selectorKey, selectorValue) => {
10+
beforeEach(() => {
11+
delete obj[key];
12+
});
13+
14+
it(`should return undefined if ${key} is not defined`, () => {
15+
// @ts-expect-error Element implicitly has an 'any' type
16+
expect(stringUnionSelector(obj, key, union, SelectorType[selectorKey])).toBeUndefined();
17+
});
18+
19+
it.each([
20+
[value, value],
21+
[value, value.toLowerCase()],
22+
[value, [...value].map((c, i) => (i % 2 === 0 ? c.toLowerCase() : c.toUpperCase())).join("")],
23+
])(`should return number %s if ${key}="%s"`, (output, input) => {
24+
obj[key] = input;
25+
// @ts-expect-error Element implicitly has an 'any' type
26+
expect(stringUnionSelector(obj, key, union, SelectorType[selectorKey])).toBe(output);
27+
});
28+
29+
// Thows if the value is something other than different case.
30+
it.each(["value1", "1value", [...value].reverse().join("")])(`should throw if ${key}=%s`, (input) => {
31+
obj[key] = input;
32+
// @ts-expect-error Element implicitly has an 'any' type
33+
expect(() => stringUnionSelector(obj, key, union, SelectorType[selectorKey])).toThrow(
34+
new TypeError(
35+
`Cannot load ${selectorValue} '${key}'. Expected one of ${Object.values(union)}, got '${obj[key]}'.`
36+
)
37+
);
38+
});
39+
});
40+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
export enum SelectorType {
2+
ENV = "env",
3+
CONFIG = "shared config entry",
4+
}
5+
6+
/**
7+
* Returns undefined, if obj[key] is not defined.
8+
* Returns string value, if the string is defined in obj[key] and it's uppercase matches union value.
9+
* Throws error for all other cases.
10+
*
11+
* @internal
12+
*/
13+
export const stringUnionSelector = (
14+
obj: Record<string, string | undefined>,
15+
key: string,
16+
union: Record<string, string>,
17+
type: SelectorType
18+
) => {
19+
if (!(key in obj)) return undefined;
20+
21+
const value = obj[key]!.toUpperCase();
22+
if (!Object.values(union).includes(value)) {
23+
throw new TypeError(`Cannot load ${type} '${key}'. Expected one of ${Object.values(union)}, got '${obj[key]}'.`);
24+
}
25+
26+
return value;
27+
};

0 commit comments

Comments
 (0)