Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/funny-cars-dance.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@smithy/smithy-client": minor
"@smithy/types": minor
---

allow adding new checksum algorithms via extension
4 changes: 3 additions & 1 deletion packages/smithy-client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@
"format": "prettier --config ../../prettier.config.js --ignore-path ../../.prettierignore --write \"**/*.{ts,md,json}\"",
"extract:docs": "node ./scripts/fix-api-extractor && api-extractor run --local && node ./scripts/fix-api-extractor --unset",
"test": "yarn g:vitest run",
"test:watch": "yarn g:vitest watch"
"test:watch": "yarn g:vitest watch",
"test:integration": "yarn g:vitest run -c vitest.config.integ.mts",
"test:integration:watch": "yarn g:vitest watch -c vitest.config.integ.mts"
},
"main": "./dist-cjs/index.js",
"module": "./dist-es/index.js",
Expand Down
70 changes: 70 additions & 0 deletions packages/smithy-client/src/extensions/checksum.integ.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { RpcV2Protocol } from "@smithy/smithy-rpcv2-cbor-schema";
import type { Checksum } from "@smithy/types";
import { describe, expect, test as it } from "vitest";

import type { PartialChecksumRuntimeConfigType } from "./checksum";

describe("checksum extension", () => {
it("should allow definition of new checksum algorithms via runtime extension", async () => {
class Sha256Custom implements Checksum {
update() {}
async digest() {
return new Uint8Array(4);
}
reset() {}
}

class R1 {
update() {}
async digest() {
return new Uint8Array(4);
}
reset() {}
}

const client = new RpcV2Protocol({
endpoint: "https://localhost",
extensions: [
{
configure(ext) {
ext.addChecksumAlgorithm({
algorithmId() {
return "r1";
},
checksumConstructor() {
return R1;
},
});
ext.addChecksumAlgorithm({
algorithmId() {
return "sha256";
},
checksumConstructor() {
return Sha256Custom;
},
});
},
},
],
});

const config = client.config as typeof client.config & PartialChecksumRuntimeConfigType;

expect(config.checksumAlgorithms).toEqual({
// the algo id is used as the key if it is not recognized.
r1: R1,

// Rhe uppercase form is used if it is recognized.
// This matches the key in the algorithm selector function.
SHA256: Sha256Custom,
});

// for known algorithms that exist on the config, they are also set by the extension.
expect(config.sha256).toEqual(Sha256Custom);
expect(config.md5).toEqual(undefined);
expect(config.sha1).toEqual(undefined);

// for novel algorithms, they are not set to new fields on the config.
expect((config as any).r1).toEqual(undefined);
});
});
42 changes: 33 additions & 9 deletions packages/smithy-client/src/extensions/checksum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,24 @@ export { AlgorithmId, ChecksumAlgorithm, ChecksumConfiguration };
/**
* @internal
*/
export type PartialChecksumRuntimeConfigType = Partial<{
sha256: ChecksumConstructor | HashConstructor;
md5: ChecksumConstructor | HashConstructor;
crc32: ChecksumConstructor | HashConstructor;
crc32c: ChecksumConstructor | HashConstructor;
sha1: ChecksumConstructor | HashConstructor;
}>;
const knownAlgorithms: string[] = Object.values(AlgorithmId);

/**
* @internal
*/
export type PartialChecksumRuntimeConfigType = {
checksumAlgorithms?: Record<string, ChecksumConstructor | HashConstructor>;
sha256?: ChecksumConstructor | HashConstructor;
md5?: ChecksumConstructor | HashConstructor;
crc32?: ChecksumConstructor | HashConstructor;
crc32c?: ChecksumConstructor | HashConstructor;
sha1?: ChecksumConstructor | HashConstructor;
};

/**
* @param runtimeConfig - config object of the client instance.
* @internal
*/
export const getChecksumConfiguration = (runtimeConfig: PartialChecksumRuntimeConfigType) => {
const checksumAlgorithms: ChecksumAlgorithm[] = [];

Expand All @@ -30,9 +37,22 @@ export const getChecksumConfiguration = (runtimeConfig: PartialChecksumRuntimeCo
checksumConstructor: () => runtimeConfig[algorithmId]!,
});
}

for (const [id, ChecksumCtor] of Object.entries(runtimeConfig.checksumAlgorithms ?? {})) {
checksumAlgorithms.push({
algorithmId: () => id,
checksumConstructor: () => ChecksumCtor,
});
}
return {
addChecksumAlgorithm(algo: ChecksumAlgorithm): void {
runtimeConfig.checksumAlgorithms = runtimeConfig.checksumAlgorithms ?? {};
const id = algo.algorithmId();
const ctor = algo.checksumConstructor();
if (knownAlgorithms.includes(id)) {
runtimeConfig.checksumAlgorithms[id.toUpperCase()] = ctor;
} else {
runtimeConfig.checksumAlgorithms[id] = ctor;
}
checksumAlgorithms.push(algo);
},
checksumAlgorithms(): ChecksumAlgorithm[] {
Expand All @@ -47,7 +67,11 @@ export const getChecksumConfiguration = (runtimeConfig: PartialChecksumRuntimeCo
export const resolveChecksumRuntimeConfig = (clientConfig: ChecksumConfiguration): PartialChecksumRuntimeConfigType => {
const runtimeConfig: PartialChecksumRuntimeConfigType = {};
clientConfig.checksumAlgorithms().forEach((checksumAlgorithm) => {
runtimeConfig[checksumAlgorithm.algorithmId()] = checksumAlgorithm.checksumConstructor();
const id = checksumAlgorithm.algorithmId();
if (knownAlgorithms.includes(id)) {
runtimeConfig[id as AlgorithmId] = checksumAlgorithm.checksumConstructor();
}
// else the algorithm was attached to the checksumAlgorithms object on the client config already.
});

return runtimeConfig;
Expand Down
8 changes: 8 additions & 0 deletions packages/smithy-client/vitest.config.integ.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { defineConfig } from "vitest/config";

export default defineConfig({
test: {
include: ["**/*.integ.spec.ts"],
environment: "node",
},
});
2 changes: 1 addition & 1 deletion packages/types/src/extensions/checksum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export enum AlgorithmId {
* @internal
*/
export interface ChecksumAlgorithm {
algorithmId(): AlgorithmId;
algorithmId(): AlgorithmId | string;
checksumConstructor(): ChecksumConstructor | HashConstructor;
}

Expand Down
Loading