diff --git a/.changeset/itchy-zebras-yawn.md b/.changeset/itchy-zebras-yawn.md new file mode 100644 index 00000000000..9e46ad25beb --- /dev/null +++ b/.changeset/itchy-zebras-yawn.md @@ -0,0 +1,5 @@ +--- +"@smithy/core": minor +--- + +cbor (de)serializer for JS diff --git a/.changeset/strong-rings-report.md b/.changeset/strong-rings-report.md new file mode 100644 index 00000000000..6fea5353d93 --- /dev/null +++ b/.changeset/strong-rings-report.md @@ -0,0 +1,5 @@ +--- +"@smithy/smithy-client": minor +--- + +handle timestamp cbor tag diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cc4ddd5c0e9..c8dc392c6a3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,6 +28,37 @@ jobs: - name: clean and build run: ./gradlew clean build -Plog-tests + protocol-tests: + runs-on: ${{ matrix.os }} + name: Protocol Tests + strategy: + matrix: + java: [17] + os: [ubuntu-latest] + + steps: + - uses: actions/checkout@v4 + - uses: gradle/wrapper-validation-action@v2 + - uses: actions/setup-node@v4 + with: + node-version: 16 + cache: "yarn" + + - name: Set up JDK ${{ matrix.java }} + uses: actions/setup-java@v4 + with: + java-version: ${{ matrix.java }} + distribution: 'corretto' + + - name: clean and build + run: ./gradlew clean build -Plog-tests + - name: Install dependencies + run: yarn + - name: Build packages + run: yarn build + - name: Run protocol tests + run: yarn test:protocols + lint-typescript: runs-on: ubuntu-latest name: TypeScript Lint diff --git a/.gitignore b/.gitignore index 2e9f855bd0d..eebad88db79 100644 --- a/.gitignore +++ b/.gitignore @@ -24,7 +24,6 @@ wrapper/ .attach_pid* # local scripting -Makefile workspace # Visual Studio Code diff --git a/Makefile b/Makefile new file mode 100644 index 00000000000..18ef17359e6 --- /dev/null +++ b/Makefile @@ -0,0 +1,19 @@ +.PHONY: build sync + +build: + ./gradlew clean build publishToMavenLocal + +sync: + gh repo sync $$GITHUB_USERNAME/smithy-typescript -b main + git fetch --all + +generate-protocol-tests: + ./gradlew :smithy-typescript-protocol-test-codegen:build + rm -rf ./private/smithy-rpcv2-cbor + cp -r ./smithy-typescript-protocol-test-codegen/build/smithyprojections/smithy-typescript-protocol-test-codegen/smithy-rpcv2-cbor/typescript-codegen ./private/smithy-rpcv2-cbor + cp ./packages/core/jest.config.js ./private/smithy-rpcv2-cbor + npx prettier --write ./private/smithy-rpcv2-cbor + yarn + +test-protocols: + (cd ./private/smithy-rpcv2-cbor && npx jest) \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index d41fb8568f5..57e669af272 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -73,6 +73,7 @@ subprojects { testImplementation("org.junit.jupiter:junit-jupiter-engine:5.10.2") testImplementation("org.junit.jupiter:junit-jupiter-params:5.10.2") testImplementation("org.hamcrest:hamcrest:2.2") + testImplementation("org.mockito:mockito-junit-jupiter:5.12.0") } // Reusable license copySpec diff --git a/package.json b/package.json index 59cb8c8b2c8..23a6fa0e3d2 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "build": "turbo run build", "test": "turbo run test", "test:integration": "yarn build-test-packages && turbo run test:integration", + "test:protocols": "make generate-protocol-tests test-protocols", "lint": "turbo run lint", "lint-fix": "turbo run lint -- --fix", "lint:pkgJson": "yarn lint:dependencies", diff --git a/packages/core/cbor.js b/packages/core/cbor.js new file mode 100644 index 00000000000..710fb798b8f --- /dev/null +++ b/packages/core/cbor.js @@ -0,0 +1,6 @@ + +/** + * Do not edit: + * This is a compatibility redirect for contexts that do not understand package.json exports field. + */ +module.exports = require("./dist-cjs/submodules/cbor/index.js"); diff --git a/packages/core/package.json b/packages/core/package.json index 119c73e7deb..2b253d14dc3 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -11,7 +11,8 @@ "clean": "rimraf ./dist-* && rimraf *.tsbuildinfo || exit 0", "lint": "npx eslint -c ../../.eslintrc.js \"src/**/*.ts\" && node ./scripts/lint", "format": "prettier --config ../../prettier.config.js --ignore-path ../.prettierignore --write \"**/*.{ts,md,json}\"", - "test": "yarn g:jest" + "test": "yarn g:jest --maxWorkers=1", + "test:cbor:perf": "node ./scripts/cbor-perf.mjs" }, "main": "./dist-cjs/index.js", "module": "./dist-es/index.js", @@ -29,6 +30,13 @@ "node": "./package.json", "import": "./package.json", "require": "./package.json" + }, + "./cbor": { + "module": "./dist-es/submodules/cbor/index.js", + "node": "./dist-cjs/submodules/cbor/index.js", + "import": "./dist-es/submodules/cbor/index.js", + "require": "./dist-cjs/submodules/cbor/index.js", + "types": "./dist-types/submodules/cbor/index.d.ts" } }, "author": { @@ -44,7 +52,9 @@ "@smithy/protocol-http": "workspace:^", "@smithy/smithy-client": "workspace:^", "@smithy/types": "workspace:^", + "@smithy/util-body-length-browser": "workspace:^", "@smithy/util-middleware": "workspace:^", + "@smithy/util-utf8": "workspace:^", "tslib": "^2.6.2" }, "engines": { @@ -58,7 +68,8 @@ } }, "files": [ - "dist-*/**" + "dist-*/**", + "./cbor.js" ], "homepage": "https://github.com/awslabs/smithy-typescript/tree/main/packages/core", "repository": { @@ -70,6 +81,7 @@ "@types/node": "^16.18.96", "concurrently": "7.0.0", "downlevel-dts": "0.10.1", + "json-bigint": "^1.0.0", "rimraf": "3.0.2", "typedoc": "0.23.23" }, diff --git a/packages/core/scripts/cbor-perf.mjs b/packages/core/scripts/cbor-perf.mjs new file mode 100644 index 00000000000..d6302ec3a92 --- /dev/null +++ b/packages/core/scripts/cbor-perf.mjs @@ -0,0 +1,262 @@ +import { fromBase64, toBase64 } from "@smithy/util-base64"; + +import * as SmithyCbor from "../dist-cjs/submodules/cbor/index.js"; + +/** + * Control the test data size with this scalar. + */ +const DATA_SCALAR = 5; + +const tests = [ + { + name: "string", + data: (() => { + const buffer = []; + for (let i = 0; i < 3400 * DATA_SCALAR; ++i) { + buffer[i] = Math.random() + ""; + } + return buffer.join("代码"); + })(), + run: true, + }, + { + name: "list", + data: (() => { + const list = []; + for (let i = 0; i < 9000 * DATA_SCALAR; ++i) { + list[i] = "abcdefghijklmnopqrstuvwxyz"[(Math.random() * 26) | 0]; + } + return list; + })(), + run: false, + }, + { + name: "list", + data: (() => { + const list = []; + for (let i = 0; i < 900 * DATA_SCALAR; ++i) { + list[i] = "string".repeat((Math.random() * 35) | 0); + } + return list; + })(), + run: true, + }, + { + name: "list", + data: (() => { + const list = []; + for (let i = 0; i < 6000 * DATA_SCALAR; ++i) { + list[i] = Math.random() * 2 - 1; + } + return list; + })(), + run: false, + }, + { + name: "list", + data: (() => { + const list = []; + for (let i = 0; i < 6000 * DATA_SCALAR; ++i) { + list[i] = Math.random() * 3.4e38; + } + return list; + })(), + run: true, + }, + { + name: "list", + data: (() => { + const list = []; + for (let i = 0; i < 6000 * DATA_SCALAR; ++i) { + list[i] = Math.random() * Number.MAX_VALUE; + } + return list; + })(), + run: true, + }, + { + name: "byte[]", + data: (() => { + const list = new Uint8Array(100000 * DATA_SCALAR); + for (let i = 0; i < list.length; ++i) { + list[i] = ((Math.random() * 20000) | 0) % 255; + } + return list; + })(), + run: true, + }, + { + name: "list", + data: (() => { + const list = []; + for (let i = 0; i < 17000 * DATA_SCALAR; ++i) { + list[i] = ((Math.random() * 20000) | 0) - 10000; + } + return list; + })(), + run: true, + }, + { + name: "list", + data: (() => { + const list = []; + for (let i = 0; i < 10000 * DATA_SCALAR; ++i) { + list[i] = Math.floor(Math.random() * 0x7fffffff * 2 - 0x7fffffff); + } + return list; + })(), + run: true, + }, + { + name: "list", + data: (() => { + const list = []; + for (let i = 0; i < 10000 * DATA_SCALAR; ++i) { + list[i] = Math.floor(-18446744073709551615 + ((Math.random() * 2 * 18446744073709551615) | 0)); + } + return list; + })(), + run: true, + }, + { + name: "map", + data: (() => { + const map = {}; + for (let i = 0; i < 2000 * DATA_SCALAR; ++i) { + map[i] = "abcdefg"[(Math.random() * 5.999) | 0]; + } + return map; + })(), + run: false, + }, + { + name: "map", + data: (() => { + const map = {}; + for (let i = 0; i < 324 * DATA_SCALAR; ++i) { + map["key".repeat((Math.random() * 10) | 0) + i] = "key".repeat((Math.random() * 155) | 0) + i + Math.random(); + } + return map; + })(), + run: true, + }, + { + name: "map", + data: (() => { + const map = {}; + for (let i = 0; i < 324 * DATA_SCALAR; ++i) { + map["key".repeat((Math.random() * 10) | 0) + i] = Math.floor(Math.random() * 0x7fffffff * 2 - 0x7fffffff); + } + return map; + })(), + run: true, + }, + { + name: "list PutMetricData-like", + data: (() => { + const collection = []; + for (let i = 0; i < 600 * DATA_SCALAR; ++i) { + collection[i] = { + MetricData: [ + { + MetricName: "PAGES_VISITED", + Dimensions: [ + { + Name: "UNIQUE_PAGES", + Value: "URLS", + }, + ], + Unit: "None", + Value: 1.0, + }, + ], + Namespace: "SITE/TRAFFIC", + }; + } + return collection; + })(), + run: true, + }, +]; + +const { cbor } = SmithyCbor; +cbor.resizeEncodingBuffer(10_000_000); + +const SCALE = (3 * 100) / DATA_SCALAR; +class Row { + constructor(data) { + Object.assign(this, data); + } +} +const rows = {}; + +for (const { name, data, run, nonScaling } of tests) { + if (!run) { + continue; + } + const scale = nonScaling ? 1 : SCALE; + + const A = performance.now(); + let cborSerialized; + { + for (let i = 0; i < scale; ++i) { + cborSerialized = cbor.serialize(data); + } + } + const B = performance.now(); + let cborDeserialized; + { + for (let i = 0; i < scale; ++i) { + cborDeserialized = cbor.deserialize(cborSerialized); + } + } + const C = performance.now(); + const D = performance.now(); + const E = performance.now(); + const F = performance.now(); + const G = performance.now(); + + let jsonSerialized; + { + for (let i = 0; i < scale; ++i) { + if (name === "byte[]") { + jsonSerialized = JSON.stringify(toBase64(data)); + } else { + jsonSerialized = JSON.stringify(data); + } + } + } + const H = performance.now(); + let jsonDeserialized; + { + for (let i = 0; i < scale; ++i) { + if (name === "byte[]") { + jsonDeserialized = fromBase64(JSON.parse(jsonSerialized)); + } else { + jsonDeserialized = JSON.parse(jsonSerialized); + } + } + } + const I = performance.now(); + const bytes = cborSerialized.byteLength; + const megabytes = (cborSerialized.byteLength * scale) / 1_000_000; + const num_fmt = (ms) => ((megabytes / ms) * 1000).toFixed(0) + "mb/s"; + const jsonBytes = Buffer.from(jsonSerialized).byteLength; + process.stdout.write("."); + + rows[name] = new Row({ + workload: `${(bytes < 1e6 ? bytes / 1e3 : bytes / 1e6).toFixed(0)}${bytes < 1e6 ? "kb" : "mb"} x ${scale}`, + cbor: ((bytes * scale) / 1e6).toFixed(0) + "mb", + json: ((jsonBytes * scale) / 1e6).toFixed(0) + "mb", + cbor_serde: [B - A, C - B].map(num_fmt).join(", "), + json_serde: [H - G, I - H].map(num_fmt).join(", "), + "cbor relative performance": [ + (((H - G) / (B - A)) * 100).toFixed(0) + "% ->", + "<- " + (((I - H) / (C - B)) * 100).toFixed(0) + "%", + ((bytes / jsonBytes) * 100).toFixed(0) + "% payload", + ].join(", "), + }); +} + +console.log(""); +console.table(rows); diff --git a/packages/core/src/submodules/.gitkeep b/packages/core/src/submodules/.gitkeep deleted file mode 100644 index 0f62d2f71d0..00000000000 --- a/packages/core/src/submodules/.gitkeep +++ /dev/null @@ -1 +0,0 @@ -placeholder until submodules exist \ No newline at end of file diff --git a/packages/core/src/submodules/cbor/cbor-decode.ts b/packages/core/src/submodules/cbor/cbor-decode.ts new file mode 100644 index 00000000000..1c0a0629fc8 --- /dev/null +++ b/packages/core/src/submodules/cbor/cbor-decode.ts @@ -0,0 +1,469 @@ +import { toUtf8 } from "@smithy/util-utf8"; + +import { + alloc, + CborArgumentLength, + CborArgumentLengthOffset, + CborListType, + CborMapType, + CborOffset, + CborUnstructuredByteStringType, + CborValueType, + extendedFloat16, + extendedFloat32, + extendedFloat64, + extendedOneByte, + Float32, + majorList, + majorMap, + majorNegativeInt64, + majorTag, + majorUint64, + majorUnstructuredByteString, + majorUtf8String, + minorIndefinite, + specialFalse, + specialNull, + specialTrue, + specialUndefined, + Uint8, + Uint32, + Uint64, +} from "./cbor-types"; + +const USE_TEXT_DECODER = typeof TextDecoder !== "undefined"; +const USE_BUFFER = typeof Buffer !== "undefined"; + +let payload = alloc(0); +let dataView = new DataView(payload.buffer, payload.byteOffset, payload.byteLength); +const textDecoder = USE_TEXT_DECODER ? new TextDecoder() : null; + +/** + * This number stores the last offset of any decoded segment. + */ +let _offset: CborOffset = 0; + +/** + * @internal + * @param bytes - to be set as the decode source. + * + * Sets the decode bytearray source and its data view. + */ +export function setPayload(bytes: Uint8Array) { + payload = bytes; + dataView = new DataView(payload.buffer, payload.byteOffset, payload.byteLength); +} + +/** + * @internal + * Decodes the data between the two indices. + */ +export function decode(at: Uint32, to: Uint32): CborValueType { + if (at >= to) { + throw new Error("unexpected end of (decode) payload."); + } + + const major = (payload[at] & 0b1110_0000) >> 5; + const minor = payload[at] & 0b0001_1111; + + switch (major) { + case majorUint64: + case majorNegativeInt64: + case majorTag: + let unsignedInt: number | Uint64; + let offset: number; + + if (minor < 24) { + unsignedInt = minor; + offset = 1; + } else { + switch (minor) { + case extendedOneByte: + case extendedFloat16: + case extendedFloat32: + case extendedFloat64: + const countLength: CborArgumentLength = minorValueToArgumentLength[minor]; + const countOffset = (countLength + 1) as CborArgumentLengthOffset; + offset = countOffset; + + if (to - at < countOffset) { + throw new Error(`countLength ${countLength} greater than remaining buf len.`); + } + const countIndex = at + 1; + if (countLength === 1) { + unsignedInt = payload[countIndex]; + } else if (countLength === 2) { + unsignedInt = dataView.getUint16(countIndex); + } else if (countLength === 4) { + unsignedInt = dataView.getUint32(countIndex); + } else { + unsignedInt = dataView.getBigUint64(countIndex); + } + break; + default: + throw new Error(`unexpected minor value ${minor}.`); + } + } + + if (major === majorUint64) { + _offset = offset; + return castBigInt(unsignedInt); + } else if (major === majorNegativeInt64) { + let negativeInt: bigint | number; + if (typeof unsignedInt === "bigint") { + negativeInt = BigInt(-1) - unsignedInt; + } else { + negativeInt = -1 - unsignedInt; + } + _offset = offset; + return castBigInt(negativeInt); + } else { + const value = decode(at + offset, to); + const valueOffset = _offset; + + _offset = offset + valueOffset; + return { tag: castBigInt(unsignedInt), value }; + } + case majorUtf8String: + case majorMap: + case majorList: + case majorUnstructuredByteString: + if (minor === minorIndefinite) { + switch (major) { + case majorUtf8String: + return decodeUtf8StringIndefinite(at, to); + case majorMap: + return decodeMapIndefinite(at, to); + case majorList: + return decodeListIndefinite(at, to); + case majorUnstructuredByteString: + return decodeUnstructuredByteStringIndefinite(at, to); + } + } else { + switch (major) { + case majorUtf8String: + return decodeUtf8String(at, to); + case majorMap: + return decodeMap(at, to); + case majorList: + return decodeList(at, to); + case majorUnstructuredByteString: + return decodeUnstructuredByteString(at, to); + } + } + default: + return decodeSpecial(at, to); + } +} + +function bytesToUtf8(bytes: Uint8Array, at: number, to: number): string { + if (USE_BUFFER && bytes.constructor?.name === "Buffer") { + return (bytes as Buffer).toString("utf-8", at, to); + } + if (textDecoder) { + return textDecoder!.decode(bytes.subarray(at, to)); + } + return toUtf8(bytes.subarray(at, to)); +} + +function demote(bigInteger: bigint): number { + // cast is safe for string and array lengths, which do not + // exceed safe integer range. + const num = Number(bigInteger); + if (num < Number.MIN_SAFE_INTEGER || Number.MAX_SAFE_INTEGER < num) { + console.warn(new Error(`@smithy/core/cbor - truncating BigInt(${bigInteger}) to ${num} with loss of precision.`)); + } + return num; +} + +const minorValueToArgumentLength = { + [extendedOneByte]: 1, + [extendedFloat16]: 2, + [extendedFloat32]: 4, + [extendedFloat64]: 8, +} as const; + +/** + * @internal + */ +export function bytesToFloat16(a: Uint8, b: Uint8): Float32 { + const sign = a >> 7; + const exponent = (a & 0b0111_1100) >> 2; + const fraction = ((a & 0b0000_0011) << 8) | b; + + const scalar = sign === 0 ? 1 : -1; + + let exponentComponent: number; + let summation: number; + + if (exponent === 0b00000) { + if (fraction === 0b00000_00000) { + return 0; + } else { + exponentComponent = Math.pow(2, 1 - 15); + summation = 0; + } + } else if (exponent === 0b11111) { + if (fraction === 0b00000_00000) { + return scalar * Infinity; + } else { + return NaN; + } + } else { + exponentComponent = Math.pow(2, exponent - 15); + summation = 1; + } + + summation += fraction / 1024; + return scalar * (exponentComponent * summation); +} + +function decodeCount(at: Uint32, to: Uint32): number { + const minor = payload[at] & 0b0001_1111; + + if (minor < 24) { + _offset = 1; + return minor; + } + + if ( + minor === extendedOneByte || + minor === extendedFloat16 || + minor === extendedFloat32 || + minor === extendedFloat64 + ) { + const countLength: CborArgumentLength = minorValueToArgumentLength[minor]; + _offset = (countLength + 1) as CborArgumentLengthOffset; + if (to - at < _offset) { + throw new Error(`countLength ${countLength} greater than remaining buf len.`); + } + const countIndex = at + 1; + + if (countLength === 1) { + return payload[countIndex]; + } else if (countLength === 2) { + return dataView.getUint16(countIndex); + } else if (countLength === 4) { + return dataView.getUint32(countIndex); + } + return demote(dataView.getBigUint64(countIndex)); + } + + throw new Error(`unexpected minor value ${minor}.`); +} + +function decodeUtf8String(at: Uint32, to: Uint32): string { + const length = decodeCount(at, to); + const offset = _offset; + at += offset; + if (to - at < length) { + throw new Error(`string len ${length} greater than remaining buf len.`); + } + const value = bytesToUtf8(payload, at, at + length); + _offset = offset + length; + return value; +} + +function decodeUtf8StringIndefinite(at: Uint32, to: Uint32): string { + at += 1; + const vector = []; + for (const base = at; at < to; ) { + if (payload[at] === 0b1111_1111) { + const data = alloc(vector.length); + data.set(vector, 0); + _offset = at - base + 2; + return bytesToUtf8(data, 0, data.length); + } + const major = (payload[at] & 0b1110_0000) >> 5; + const minor = payload[at] & 0b0001_1111; + if (major !== majorUtf8String) { + throw new Error(`unexpected major type ${major} in indefinite string.`); + } + if (minor === minorIndefinite) { + throw new Error("nested indefinite string."); + } + const bytes = decodeUnstructuredByteString(at, to); + const length = _offset; + at += length; + for (let i = 0; i < bytes.length; ++i) { + vector.push(bytes[i]); + } + } + throw new Error("expected break marker."); +} + +function decodeUnstructuredByteString(at: Uint32, to: Uint32): CborUnstructuredByteStringType { + const length = decodeCount(at, to); + const offset = _offset; + + at += offset; + if (to - at < length) { + throw new Error(`unstructured byte string len ${length} greater than remaining buf len.`); + } + + const value = payload.subarray(at, at + length); + _offset = offset + length; + return value; +} + +function decodeUnstructuredByteStringIndefinite(at: Uint32, to: Uint32): CborUnstructuredByteStringType { + at += 1; + const vector = []; + + for (const base = at; at < to; ) { + if (payload[at] === 0b1111_1111) { + const data = alloc(vector.length); + data.set(vector, 0); + _offset = at - base + 2; + return data; + } + + const major = (payload[at] & 0b1110_0000) >> 5; + const minor = payload[at] & 0b0001_1111; + if (major !== majorUnstructuredByteString) { + throw new Error(`unexpected major type ${major} in indefinite string.`); + } + if (minor === minorIndefinite) { + throw new Error("nested indefinite string."); + } + + const bytes = decodeUnstructuredByteString(at, to); + const length = _offset; + at += length; + for (let i = 0; i < bytes.length; ++i) { + vector.push(bytes[i]); + } + } + throw new Error("expected break marker."); +} + +function decodeList(at: Uint32, to: Uint32): CborListType { + const listDataLength = decodeCount(at, to); + const offset = _offset; + at += offset; + const base = at; + // perf: pre-allocate array length. + const list = Array(listDataLength); + for (let i = 0; i < listDataLength; ++i) { + const item = decode(at, to); + const itemOffset = _offset; + list[i] = item; + at += itemOffset; + } + _offset = offset + (at - base); + return list; +} + +function decodeListIndefinite(at: Uint32, to: Uint32): CborListType { + at += 1; + const list = [] as CborListType; + for (const base = at; at < to; ) { + if (payload[at] === 0b1111_1111) { + _offset = at - base + 2; + return list; + } + const item = decode(at, to); + const n = _offset; + at += n; + list.push(item); + } + throw new Error("expected break marker."); +} + +function decodeMap(at: Uint32, to: Uint32): CborMapType { + const mapDataLength = decodeCount(at, to); + const offset = _offset; + at += offset; + const base = at; + const map = {} as CborMapType; + for (let i = 0; i < mapDataLength; ++i) { + if (at >= to) { + throw new Error("unexpected end of map payload."); + } + const major = (payload[at] & 0b1110_0000) >> 5; + if (major !== majorUtf8String) { + throw new Error(`unexpected major type ${major} for map key at index ${at}.`); + } + const key = decode(at, to); + at += _offset; + const value = decode(at, to); + at += _offset; + map[key] = value; + } + _offset = offset + (at - base); + return map; +} + +function decodeMapIndefinite(at: Uint32, to: Uint32): CborMapType { + at += 1; + const base = at; + const map = {} as CborMapType; + for (; at < to; ) { + if (at >= to) { + throw new Error("unexpected end of map payload."); + } + if (payload[at] === 0b1111_1111) { + _offset = at - base + 2; + return map; + } + const major = (payload[at] & 0b1110_0000) >> 5; + if (major !== majorUtf8String) { + throw new Error(`unexpected major type ${major} for map key.`); + } + const key = decode(at, to); + at += _offset; + const value = decode(at, to); + at += _offset; + map[key] = value; + } + throw new Error("expected break marker."); +} + +function decodeSpecial(at: Uint32, to: Uint32): CborValueType { + const minor = payload[at] & 0b0001_1111; + switch (minor) { + case specialTrue: + case specialFalse: + _offset = 1; + return minor === specialTrue; + case specialNull: + _offset = 1; + return null; + case specialUndefined: + // Note: the Smithy spec requires that undefined is + // instead deserialized to null. + _offset = 1; + return null; + case extendedFloat16: + if (to - at < 3) { + throw new Error("incomplete float16 at end of buf."); + } + _offset = 3; + return bytesToFloat16(payload[at + 1], payload[at + 2]); + case extendedFloat32: + if (to - at < 5) { + throw new Error("incomplete float32 at end of buf."); + } + _offset = 5; + return dataView.getFloat32(at + 1); + case extendedFloat64: + if (to - at < 9) { + throw new Error("incomplete float64 at end of buf."); + } + _offset = 9; + return dataView.getFloat64(at + 1); + default: + throw new Error(`unexpected minor value ${minor}.`); + } +} + +function castBigInt(bigInt: bigint | number): number | bigint { + if (typeof bigInt === "number") { + return bigInt; + } + const num = Number(bigInt); + if (Number.MIN_SAFE_INTEGER <= num && num <= Number.MAX_SAFE_INTEGER) { + return num; + } + return bigInt; +} diff --git a/packages/core/src/submodules/cbor/cbor-encode.ts b/packages/core/src/submodules/cbor/cbor-encode.ts new file mode 100644 index 00000000000..8aac902846d --- /dev/null +++ b/packages/core/src/submodules/cbor/cbor-encode.ts @@ -0,0 +1,194 @@ +import { fromUtf8 } from "@smithy/util-utf8"; + +import { + CborMajorType, + extendedFloat16, + extendedFloat32, + extendedFloat64, + majorList, + majorMap, + majorNegativeInt64, + majorSpecial, + majorUint64, + majorUnstructuredByteString, + majorUtf8String, + specialFalse, + specialNull, + specialTrue, + Uint64, +} from "./cbor-types"; +import { alloc } from "./cbor-types"; + +const USE_BUFFER = typeof Buffer !== "undefined"; + +const initialSize = 2048; +let data: Uint8Array = alloc(initialSize); +let dataView: DataView = new DataView(data.buffer, data.byteOffset, data.byteLength); +let cursor: number = 0; + +function ensureSpace(bytes: number) { + const remaining = data.byteLength - cursor; + if (remaining < bytes) { + if (cursor < 16_000_000) { + resize(Math.max(data.byteLength * 4, data.byteLength + bytes)); + } else { + resize(data.byteLength + bytes + 16_000_000); + } + } +} + +/** + * @internal + */ +export function toUint8Array(): Uint8Array { + const out = alloc(cursor); + out.set(data.subarray(0, cursor), 0); + cursor = 0; + return out; +} + +export function resize(size: number) { + const old = data; + data = alloc(size); + if (old) { + if ((old as Buffer).copy) { + (old as Buffer).copy(data, 0, 0, old.byteLength); + } else { + data.set(old, 0); + } + } + dataView = new DataView(data.buffer, data.byteOffset, data.byteLength); +} + +function encodeHeader(major: CborMajorType, value: Uint64 | number): void { + if (value < 24) { + data[cursor++] = (major << 5) | (value as number); + } else if (value < 1 << 8) { + data[cursor++] = (major << 5) | 24; + data[cursor++] = value as number; + } else if (value < 1 << 16) { + data[cursor++] = (major << 5) | extendedFloat16; + dataView.setUint16(cursor, value as number); + cursor += 2; + } else if (value < 2 ** 32) { + data[cursor++] = (major << 5) | extendedFloat32; + dataView.setUint32(cursor, value as number); + cursor += 4; + } else { + data[cursor++] = (major << 5) | extendedFloat64; + dataView.setBigUint64(cursor, typeof value === "bigint" ? value : BigInt(value)); + cursor += 8; + } +} + +/** + * @param _input - JS data object. + */ +export function encode(_input: any): void { + const encodeStack = [_input]; + + while (encodeStack.length) { + const input = encodeStack.pop(); + + ensureSpace(typeof input === "string" ? input.length * 4 : 64); + + if (typeof input === "string") { + if (USE_BUFFER) { + encodeHeader(majorUtf8String, Buffer.byteLength(input)); + cursor += (data as Buffer).write(input, cursor); + } else { + const bytes = fromUtf8(input); + encodeHeader(majorUtf8String, bytes.byteLength); + data.set(bytes, cursor); + cursor += bytes.byteLength; + } + continue; + } else if (typeof input === "number") { + if (Number.isInteger(input)) { + const nonNegative = input >= 0; + const major = nonNegative ? majorUint64 : majorNegativeInt64; + const value = nonNegative ? input : -input - 1; + if (value < 24) { + data[cursor++] = (major << 5) | value; + } else if (value < 256 /* 2 ** 8 */) { + data[cursor++] = (major << 5) | 24; + data[cursor++] = value; + } else if (value < 65536 /* 2 ** 16 */) { + data[cursor++] = (major << 5) | extendedFloat16; + data[cursor++] = (value as number) >> 8; + data[cursor++] = value as number & 0b1111_1111; + } else if (value < 4294967296 /* 2 ** 32 */) { + data[cursor++] = (major << 5) | extendedFloat32; + dataView.setUint32(cursor, value); + cursor += 4; + } else { + data[cursor++] = (major << 5) | extendedFloat64; + dataView.setBigUint64(cursor, BigInt(value)); + cursor += 8; + } + continue; + } + data[cursor++] = (majorSpecial << 5) | extendedFloat64; + dataView.setFloat64(cursor, input); + cursor += 8; + continue; + } else if (typeof input === "bigint") { + const nonNegative = input >= 0; + const major = nonNegative ? majorUint64 : majorNegativeInt64; + const value = nonNegative ? input : -input - BigInt(1); + const n = Number(value); + if (n < 24) { + data[cursor++] = (major << 5) | n; + } else if (n < 256 /* 2 ** 8 */) { + data[cursor++] = (major << 5) | 24; + data[cursor++] = n; + } else if (n < 65536 /* 2 ** 16 */) { + data[cursor++] = (major << 5) | extendedFloat16; + data[cursor++] = n >> 8; + data[cursor++] = n & 0b1111_1111; + } else if (n < 4294967296 /* 2 ** 32 */) { + data[cursor++] = (major << 5) | extendedFloat32; + dataView.setUint32(cursor, n); + cursor += 4; + } else { + data[cursor++] = (major << 5) | extendedFloat64; + dataView.setBigUint64(cursor, value); + cursor += 8; + } + continue; + } else if (input === null) { + data[cursor++] = (majorSpecial << 5) | specialNull; + continue; + } else if (typeof input === "boolean") { + data[cursor++] = (majorSpecial << 5) | (input ? specialTrue : specialFalse); + continue; + } else if (typeof input === "undefined") { + // Note: Smithy spec requires that undefined not be serialized + // though the CBOR spec includes it. + throw new Error("@smithy/core/cbor: client may not serialize undefined value."); + } else if (Array.isArray(input)) { + for (let i = input.length - 1; i >= 0; --i) { + encodeStack.push(input[i]); + } + encodeHeader(majorList, input.length); + continue; + } else if (typeof input.byteLength === "number") { + ensureSpace(input.length * 2); + encodeHeader(majorUnstructuredByteString, input.length); + data.set(input, cursor); + cursor += input.byteLength; + continue; + } else if (typeof input === "object") { + const keys = Object.keys(input); + for (let i = keys.length - 1; i >= 0; --i) { + const key = keys[i]; + encodeStack.push(input[key]); + encodeStack.push(key); + } + encodeHeader(majorMap, keys.length); + continue; + } + + throw new Error(`data type ${input?.constructor?.name ?? typeof input} not compatible for encoding.`); + } +} diff --git a/packages/core/src/submodules/cbor/cbor-types.ts b/packages/core/src/submodules/cbor/cbor-types.ts new file mode 100644 index 00000000000..9708fab96cd --- /dev/null +++ b/packages/core/src/submodules/cbor/cbor-types.ts @@ -0,0 +1,68 @@ +export type CborItemType = + | undefined + | boolean + | number + | bigint + | [CborUnstructuredByteStringType, Uint64] + | string + | CborTagType; + +export type CborTagType = { + tag: Uint64 | number; + value: CborValueType; +}; +export type CborUnstructuredByteStringType = Uint8Array; +export type CborListType = Array; +export type CborMapType = Record; +export type CborCollectionType = CborMapType | CborListType; + +export type CborValueType = CborItemType | CborCollectionType | any; + +export type CborArgumentLength = 1 | 2 | 4 | 8; +export type CborArgumentLengthOffset = 1 | 2 | 3 | 5 | 9; +export type CborOffset = number; + +export type Uint8 = number; +export type Uint32 = number; +export type Uint64 = bigint; +export type Float32 = number; + +export type Int64 = bigint; + +export type Float16Binary = number; +export type Float32Binary = number; + +export type CborMajorType = + | typeof majorUint64 + | typeof majorNegativeInt64 + | typeof majorUnstructuredByteString + | typeof majorUtf8String + | typeof majorList + | typeof majorMap + | typeof majorTag + | typeof majorSpecial; + +export const majorUint64 = 0; // 0b000 +export const majorNegativeInt64 = 1; // 0b001 +export const majorUnstructuredByteString = 2; // 0b010 +export const majorUtf8String = 3; // 0b011 +export const majorList = 4; // 0b100 +export const majorMap = 5; // 0b101 +export const majorTag = 6; // 0b110 +export const majorSpecial = 7; // 0b111 + +export const specialFalse = 20; // 0b10100 +export const specialTrue = 21; // 0b10101 +export const specialNull = 22; // 0b10110 +export const specialUndefined = 23; // 0b10111 + +export const extendedOneByte = 24; // 0b11000 +export const extendedFloat16 = 25; // 0b11001 +export const extendedFloat32 = 26; // 0b11010 +export const extendedFloat64 = 27; // 0b11011 + +export const minorIndefinite = 31; // 0b11111 + +export function alloc(size: number) { + return typeof Buffer !== "undefined" ? Buffer.alloc(size) : new Uint8Array(size); +} diff --git a/packages/core/src/submodules/cbor/cbor.spec.ts b/packages/core/src/submodules/cbor/cbor.spec.ts new file mode 100644 index 00000000000..9345b4bff81 --- /dev/null +++ b/packages/core/src/submodules/cbor/cbor.spec.ts @@ -0,0 +1,342 @@ +import * as fs from "fs"; +import JSONbig from "json-bigint"; +import * as path from "path"; + +import { cbor } from "./cbor"; +import { bytesToFloat16 } from "./cbor-decode"; + +// syntax is ESM but the test target is CJS. +const here = __dirname; + +const errorTests = JSONbig({ useNativeBigInt: true, alwaysParseAsBig: false }).parse( + fs.readFileSync(path.join(here, "test-data", "decode-error-tests.json")) +); +const successTests = JSONbig({ useNativeBigInt: true, alwaysParseAsBig: false }).parse( + fs.readFileSync(path.join(here, "test-data", "success-tests.json")) +); + +describe("cbor", () => { + const allocByteArray = (dataOrSize: ArrayBuffer | ArrayLike | number, offset?: number, length?: number) => { + if (typeof offset === "number" && typeof length === "number") { + return typeof Buffer !== "undefined" + ? Buffer.from(dataOrSize as ArrayBuffer, offset, length) + : new Uint8Array(dataOrSize as ArrayBuffer, offset, length); + } + return typeof Buffer !== "undefined" ? Buffer.from(dataOrSize as any) : new Uint8Array(dataOrSize as any); + }; + + const examples = [ + { + name: "false", + data: false, + // special major 7 = 0b111 plus false(20) = 0b10100 + cbor: allocByteArray([0b111_10100]), + }, + { + name: "true", + data: true, + // increment from false + cbor: allocByteArray([0b111_10101]), + }, + { + name: "null", + data: null, + // increment from true + cbor: allocByteArray([0b111_10110]), + }, + { + name: "an unsigned zero integer", + data: 0, + // unsigned int major (0) plus 00's. + cbor: allocByteArray([0b000_00000]), + }, + { + name: "negative 1", + data: -1, + // negative major (1) plus 00's, since -1 is the first negative number. + cbor: allocByteArray([0b001_00000]), + }, + { + name: "a tricky float", + data: [7.624000072479248, 7.624], + cbor: allocByteArray([130, 251, 64, 30, 126, 249, 224, 0, 0, 0, 251, 64, 30, 126, 249, 219, 34, 208, 229]), + }, + { + name: "Number.MIN_SAFE_INTEGER", + data: -9007199254740991, + cbor: allocByteArray([0b001_11011, 0, 31, 255, 255, 255, 255, 255, 254]), + }, + { + name: "Number.MAX_SAFE_INTEGER", + data: 9007199254740991, + cbor: allocByteArray([0b000_11011, 0, 31, 255, 255, 255, 255, 255, 255]), + }, + { + name: "int64 min", + data: BigInt("-18446744073709551616"), + cbor: allocByteArray([0b001_11011, 255, 255, 255, 255, 255, 255, 255, 255]), + }, + { + name: "int64 max", + data: BigInt("18446744073709551615"), + cbor: allocByteArray([0b000_11011, 255, 255, 255, 255, 255, 255, 255, 255]), + }, + { + name: "negative float", + data: -3015135.135135135, + cbor: allocByteArray([0b111_11011, +193, +71, +0, +239, +145, +76, +27, +173]), + }, + { + name: "positive float", + data: 3015135.135135135, + cbor: allocByteArray([0b111_11011, +65, +71, +0, +239, +145, +76, +27, +173]), + }, + { + name: "various numbers", + data: [ + BigInt("18446744073709551615"), + 4294967295, + 65535, + 257, + 256, + 255, + 254, + 129, + 128, + 127, + 65, + 64, + 63, + 33, + 32, + 31, + 17, + 16, + 15, + 9, + 8, + 7, + 5, + 4, + 3, + 2, + 1, + 0, + -1, + -2, + -3, + -4, + -5, + -7, + -8, + -9, + -15, + -16, + -17, + -31, + -32, + -33, + -63, + -64, + -65, + -127, + -128, + -129, + -254, + -255, + -256, + -257, + -65535, + -4294967295, + -BigInt("18446744073709551616"), + ], + cbor: allocByteArray([ + 152, 55, 27, 255, 255, 255, 255, 255, 255, 255, 255, 26, 255, 255, 255, 255, 25, 255, 255, 25, 1, 1, 25, 1, 0, + 24, 255, 24, 254, 24, 129, 24, 128, 24, 127, 24, 65, 24, 64, 24, 63, 24, 33, 24, 32, 24, 31, 17, 16, 15, 9, 8, + 7, 5, 4, 3, 2, 1, 0, 32, 33, 34, 35, 36, 38, 39, 40, 46, 47, 48, 56, 30, 56, 31, 56, 32, 56, 62, 56, 63, 56, 64, + 56, 126, 56, 127, 56, 128, 56, 253, 56, 254, 56, 255, 57, 1, 0, 57, 255, 254, 58, 255, 255, 255, 254, 59, 255, + 255, 255, 255, 255, 255, 255, 255, + ]), + }, + { + name: "an empty string", + data: "", + // string major plus 00's + cbor: allocByteArray([0b011_00000]), + }, + { + name: "a short string", + data: "hello, world", + cbor: allocByteArray([108, 104, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100]), + }, + { + name: "simple object", + data: { + message: "hello, world", + }, + cbor: allocByteArray([ + 161, 103, 109, 101, 115, 115, 97, 103, 101, 108, 104, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100, + ]), + }, + { + name: "complex object", + data: { + number: 135019305913059, + message: "hello, world", + list: [0, false, { a: "b" }], + map: { + a: "a", + b: "b", + items: [0, -1, true, false, null, "", "test", ["nested item A", "nested item B"]], + }, + }, + cbor: allocByteArray([ + 164, 102, 110, 117, 109, 98, 101, 114, 27, 0, 0, 122, 204, 161, 196, 74, 227, 103, 109, 101, 115, 115, 97, 103, + 101, 108, 104, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100, 100, 108, 105, 115, 116, 131, 0, 244, 161, + 97, 97, 97, 98, 99, 109, 97, 112, 163, 97, 97, 97, 97, 97, 98, 97, 98, 101, 105, 116, 101, 109, 115, 136, 0, 32, + 245, 244, 246, 96, 100, 116, 101, 115, 116, 130, 109, 110, 101, 115, 116, 101, 100, 32, 105, 116, 101, 109, 32, + 65, 109, 110, 101, 115, 116, 101, 100, 32, 105, 116, 101, 109, 32, 66, + ]), + }, + ]; + + const toBytes = (hex: string) => { + const bytes = []; + hex.replace(/../g, (substr: string): string => { + bytes.push(parseInt(substr, 16)); + return substr; + }); + return allocByteArray(bytes); + }; + + describe("locally curated scenarios", () => { + for (const { name, data, cbor: cbor_representation } of examples) { + it(`should encode for ${name}`, async () => { + const serialized = cbor.serialize(data); + expect(allocByteArray(serialized.buffer, serialized.byteOffset, serialized.byteLength)).toEqual( + cbor_representation + ); + }); + + it(`should decode for ${name}`, async () => { + const deserialized = cbor.deserialize(cbor_representation); + expect(deserialized).toEqual(data); + }); + } + }); + + describe("externally curated scenarios", () => { + for (const { description, input, error } of errorTests) { + it(description, () => { + expect(error).toBe(true); + const bytes = toBytes(input); + expect(() => { + cbor.deserialize(bytes); + }).toThrow(); + }); + } + + function binaryToFloat32(b: number) { + const dv = new DataView(new ArrayBuffer(4)); + dv.setInt32(0, Number(b)); + return dv.getFloat32(0); + } + + function binaryToFloat64(b: number) { + const binaryArray = b.toString(2).split("").map(Number); + const pad = Array(64).fill(0); + const binary64 = new Uint8Array(pad.concat(binaryArray).slice(-64)); + + const sign = binary64[0]; + const exponent = Number("0b" + Array.from(binary64.subarray(1, 12)).join("")); + const fraction = binary64.subarray(12); + + const scalar = (-1) ** sign; + let sum = 1; + for (let i = 1; i <= 52; ++i) { + const position = i - 1; + const bit = fraction[position]; + sum += 2 ** -i * bit; + } + const exponentScalar = Math.pow(2, exponent - 1023); + return scalar * sum * exponentScalar; + } + + function translateTestData(data: any) { + const [type, value] = Object.entries(data)[0] as [string, any]; + switch (type) { + case "null": + return null; + case "uint": + case "negint": + case "bool": + case "string": + return value; + case "float32": + return binaryToFloat32(value); + case "float64": + return binaryToFloat64(value); + case "bytestring": + return allocByteArray(value.map(Number)); + case "list": + return value.map(translateTestData); + case "map": + const output = {} as Record; + for (const [k, v] of Object.entries(value)) { + output[k] = translateTestData(v); + } + return output; + case "tag": + const { id, value: tagValue } = value; + return { + tag: id, + value: translateTestData(tagValue), + }; + default: + throw new Error(`Unrecognized test scenario type ${type}.`); + } + } + + for (const { description, input, expect: _expect } of successTests) { + const bytes = toBytes(input); + const jsObject = translateTestData(_expect); + + it(`serialization for ${description}`, () => { + const serialized = allocByteArray(cbor.serialize(jsObject)); + const redeserialized = cbor.deserialize(serialized); + + /** + * We cannot assert that serialized == bytes, + * because there are multiple serializations + * that deserialize to the same object. + */ + expect(redeserialized).toEqual(jsObject); + }); + it(`deserialization for ${description}`, () => { + const deserialized = cbor.deserialize(bytes); + expect(deserialized).toEqual(jsObject); + }); + } + }); +}); + +describe("bytesToFloat16", () => { + it("should convert two bytes to float16", () => { + expect(bytesToFloat16(0b0_10100_00, 0b0101_0000)).toEqual(34.5); + + expect(bytesToFloat16(0b0_00000_00, 0b0000_0000)).toEqual(0.0); + expect(bytesToFloat16(0b0_00000_00, 0b0000_0001)).toEqual(5.960464477539063e-8); + expect(bytesToFloat16(0b0_00001_00, 0b0000_0000)).toEqual(0.00006103515625); + + expect(bytesToFloat16(0b0_01101_01, 0b0101_0101)).toEqual(0.333251953125); + expect(bytesToFloat16(0b0_01110_11, 0b1111_1111)).toEqual(0.99951171875); + expect(bytesToFloat16(0b0_01111_00, 0b0000_0000)).toEqual(1.0); + expect(bytesToFloat16(0b0_01111_00, 0b0000_0001)).toEqual(1.0009765625); + expect(bytesToFloat16(0b0_11110_11, 0b1111_1111)).toEqual(65504.0); + + expect(bytesToFloat16(0b0_11111_00, 0b0000_0000)).toEqual(Infinity); + // expect(bytesToFloat16(0b1_00000_00, 0b0000_0000)).toEqual(-0); + expect(bytesToFloat16(0b1_10000_00, 0b0000_0000)).toEqual(-2); + expect(bytesToFloat16(0b1_11111_00, 0b0000_0000)).toEqual(-Infinity); + }); +}); diff --git a/packages/core/src/submodules/cbor/cbor.ts b/packages/core/src/submodules/cbor/cbor.ts new file mode 100644 index 00000000000..a9e8c8a2f40 --- /dev/null +++ b/packages/core/src/submodules/cbor/cbor.ts @@ -0,0 +1,37 @@ +import { decode, setPayload } from "./cbor-decode"; +import { encode, resize, toUint8Array } from "./cbor-encode"; + +/** + * This implementation is synchronous and only implements the parts of CBOR + * specification used by Smithy RPCv2 CBOR protocol. + * + * This cbor serde implementation is derived from AWS SDK for Go's implementation. + * @see https://github.com/aws/smithy-go/tree/main/encoding/cbor + * + * The cbor-x implementation was also instructional: + * @see https://github.com/kriszyp/cbor-x + */ +export const cbor = { + deserialize(payload: Uint8Array) { + setPayload(payload); + return decode(0, payload.length); + }, + serialize(input: any) { + encode(input); + return toUint8Array(); + }, + /** + * @public + * @param size - byte length to allocate. + * + * This may be used to garbage collect the CBOR + * shared encoding buffer space, + * e.g. resizeEncodingBuffer(0); + * + * This may also be used to pre-allocate more space for + * CBOR encoding, e.g. resizeEncodingBuffer(100_000_000); + */ + resizeEncodingBuffer(size: number) { + resize(size); + }, +}; diff --git a/packages/core/src/submodules/cbor/index.ts b/packages/core/src/submodules/cbor/index.ts new file mode 100644 index 00000000000..444b9cbfdbf --- /dev/null +++ b/packages/core/src/submodules/cbor/index.ts @@ -0,0 +1,2 @@ +export { cbor } from "./cbor"; +export * from "./parseCborBody"; diff --git a/packages/core/src/submodules/cbor/parseCborBody.ts b/packages/core/src/submodules/cbor/parseCborBody.ts new file mode 100644 index 00000000000..32bbe0b7ba5 --- /dev/null +++ b/packages/core/src/submodules/cbor/parseCborBody.ts @@ -0,0 +1,114 @@ +import { HttpRequest as __HttpRequest } from "@smithy/protocol-http"; +import { collectBody } from "@smithy/smithy-client"; +import { HeaderBag as __HeaderBag, HttpResponse, SerdeContext as __SerdeContext, SerdeContext } from "@smithy/types"; +import { calculateBodyLength } from "@smithy/util-body-length-browser"; + +import { cbor } from "./cbor"; + +/** + * @internal + */ +export const parseCborBody = (streamBody: any, context: SerdeContext): any => { + return collectBody(streamBody, context).then(async (bytes) => { + if (bytes.length) { + try { + return cbor.deserialize(bytes); + } catch (e: any) { + Object.defineProperty(e, "$responseBodyText", { + value: context.utf8Encoder(bytes), + }); + throw e; + } + } + return {}; + }); +}; + +/** + * @internal + */ +export const dateToTag = (date: Date): { tag: 1; value: number } => { + return { + tag: 1, + value: date.getTime() / 1000, + }; +}; + +/** + * @internal + */ +export const parseCborErrorBody = async (errorBody: any, context: SerdeContext) => { + const value = await parseCborBody(errorBody, context); + value.message = value.message ?? value.Message; + return value; +}; + +/** + * @internal + */ +export const loadSmithyRpcV2CborErrorCode = (output: HttpResponse, data: any): string | undefined => { + const sanitizeErrorCode = (rawValue: string | number): string => { + let cleanValue = rawValue; + if (typeof cleanValue === "number") { + cleanValue = cleanValue.toString(); + } + if (cleanValue.indexOf(",") >= 0) { + cleanValue = cleanValue.split(",")[0]; + } + if (cleanValue.indexOf(":") >= 0) { + cleanValue = cleanValue.split(":")[0]; + } + if (cleanValue.indexOf("#") >= 0) { + cleanValue = cleanValue.split("#")[1]; + } + return cleanValue; + }; + + if (data["__type"] !== undefined) { + return sanitizeErrorCode(data["__type"]); + } + + if (data.code !== undefined) { + return sanitizeErrorCode(data.code); + } +}; + +/** + * @internal + */ +export const checkCborResponse = (response: HttpResponse): void => { + if (String(response.headers["smithy-protocol"]).toLowerCase() !== "rpc-v2-cbor") { + throw new Error("Malformed RPCv2 CBOR response, status: " + response.statusCode); + } +}; + +/** + * @internal + */ +export const buildHttpRpcRequest = async ( + context: __SerdeContext, + headers: __HeaderBag, + path: string, + resolvedHostname: string | undefined, + body: any +): Promise<__HttpRequest> => { + const { hostname, protocol = "https", port, path: basePath } = await context.endpoint(); + const contents: any = { + protocol, + hostname, + port, + method: "POST", + path: basePath.endsWith("/") ? basePath.slice(0, -1) + path : basePath + path, + headers, + }; + if (resolvedHostname !== undefined) { + contents.hostname = resolvedHostname; + } + if (body !== undefined) { + contents.body = body; + try { + contents.headers["content-length"] = String(calculateBodyLength(body)); + } catch (e) {} + } + return new __HttpRequest(contents); +}; diff --git a/packages/core/src/submodules/cbor/test-data/decode-error-tests.json b/packages/core/src/submodules/cbor/test-data/decode-error-tests.json new file mode 100644 index 00000000000..55c203ef176 --- /dev/null +++ b/packages/core/src/submodules/cbor/test-data/decode-error-tests.json @@ -0,0 +1,282 @@ +[ + { + "description": "TestDecode_InvalidArgument - map/2 - arg len 2 greater than remaining buf len", + "input": "b900", + "error": true + }, + { + "description": "TestDecode_InvalidArgument - tag/1 - arg len 1 greater than remaining buf len", + "input": "d8", + "error": true + }, + { + "description": "TestDecode_InvalidArgument - major7/float64 - incomplete float64 at end of buf", + "input": "fb00000000000000", + "error": true + }, + { + "description": "TestDecode_InvalidArgument - negint/4 - arg len 4 greater than remaining buf len", + "input": "3a000000", + "error": true + }, + { + "description": "TestDecode_InvalidArgument - negint/8 - arg len 8 greater than remaining buf len", + "input": "3b00000000000000", + "error": true + }, + { + "description": "TestDecode_InvalidArgument - string/4 - arg len 4 greater than remaining buf len", + "input": "7a000000", + "error": true + }, + { + "description": "TestDecode_InvalidArgument - map/1 - arg len 1 greater than remaining buf len", + "input": "b8", + "error": true + }, + { + "description": "TestDecode_InvalidArgument - map/4 - arg len 4 greater than remaining buf len", + "input": "ba000000", + "error": true + }, + { + "description": "TestDecode_InvalidArgument - tag/2 - arg len 2 greater than remaining buf len", + "input": "d900", + "error": true + }, + { + "description": "TestDecode_InvalidArgument - uint/1 - arg len 1 greater than remaining buf len", + "input": "18", + "error": true + }, + { + "description": "TestDecode_InvalidArgument - string/1 - arg len 1 greater than remaining buf len", + "input": "78", + "error": true + }, + { + "description": "TestDecode_InvalidArgument - string/8 - arg len 8 greater than remaining buf len", + "input": "7b00000000000000", + "error": true + }, + { + "description": "TestDecode_InvalidArgument - string/2 - arg len 2 greater than remaining buf len", + "input": "7900", + "error": true + }, + { + "description": "TestDecode_InvalidArgument - list/2 - arg len 2 greater than remaining buf len", + "input": "9900", + "error": true + }, + { + "description": "TestDecode_InvalidArgument - slice/1 - arg len 1 greater than remaining buf len", + "input": "58", + "error": true + }, + { + "description": "TestDecode_InvalidArgument - slice/4 - arg len 4 greater than remaining buf len", + "input": "5a000000", + "error": true + }, + { + "description": "TestDecode_InvalidArgument - slice/8 - arg len 8 greater than remaining buf len", + "input": "5b00000000000000", + "error": true + }, + { + "description": "TestDecode_InvalidArgument - negint/? - unexpected minor value 31", + "input": "3f", + "error": true + }, + { + "description": "TestDecode_InvalidArgument - tag/8 - arg len 8 greater than remaining buf len", + "input": "db00000000000000", + "error": true + }, + { + "description": "TestDecode_InvalidArgument - uint/2 - arg len 2 greater than remaining buf len", + "input": "1900", + "error": true + }, + { + "description": "TestDecode_InvalidArgument - uint/8 - arg len 8 greater than remaining buf len", + "input": "1b00000000000000", + "error": true + }, + { + "description": "TestDecode_InvalidArgument - negint/2 - arg len 2 greater than remaining buf len", + "input": "3900", + "error": true + }, + { + "description": "TestDecode_InvalidArgument - negint/1 - arg len 1 greater than remaining buf len", + "input": "38", + "error": true + }, + { + "description": "TestDecode_InvalidArgument - list/8 - arg len 8 greater than remaining buf len", + "input": "9b00000000000000", + "error": true + }, + { + "description": "TestDecode_InvalidArgument - tag/4 - arg len 4 greater than remaining buf len", + "input": "da000000", + "error": true + }, + { + "description": "TestDecode_InvalidArgument - major7/float32 - incomplete float32 at end of buf", + "input": "fa000000", + "error": true + }, + { + "description": "TestDecode_InvalidArgument - uint/4 - arg len 4 greater than remaining buf len", + "input": "1a000000", + "error": true + }, + { + "description": "TestDecode_InvalidArgument - slice/2 - arg len 2 greater than remaining buf len", + "input": "5900", + "error": true + }, + { + "description": "TestDecode_InvalidArgument - list/4 - arg len 4 greater than remaining buf len", + "input": "9a000000", + "error": true + }, + { + "description": "TestDecode_InvalidArgument - tag/? - unexpected minor value 31", + "input": "df", + "error": true + }, + { + "description": "TestDecode_InvalidArgument - major7/? - unexpected minor value 31", + "input": "ff", + "error": true + }, + { + "description": "TestDecode_InvalidArgument - uint/? - unexpected minor value 31", + "input": "1f", + "error": true + }, + { + "description": "TestDecode_InvalidArgument - list/1 - arg len 1 greater than remaining buf len", + "input": "98", + "error": true + }, + { + "description": "TestDecode_InvalidArgument - map/8 - arg len 8 greater than remaining buf len", + "input": "bb00000000000000", + "error": true + }, + { + "description": "TestDecode_InvalidList - [] / eof after head - unexpected end of payload", + "input": "81", + "error": true + }, + { + "description": "TestDecode_InvalidList - [] / invalid item - arg len 1 greater than remaining buf len", + "input": "8118", + "error": true + }, + { + "description": "TestDecode_InvalidList - [_ ] / no break - expected break marker", + "input": "9f", + "error": true + }, + { + "description": "TestDecode_InvalidList - [_ ] / invalid item - arg len 1 greater than remaining buf len", + "input": "9f18", + "error": true + }, + { + "description": "TestDecode_InvalidMap - {} / invalid key - slice len 1 greater than remaining buf len", + "input": "a17801", + "error": true + }, + { + "description": "TestDecode_InvalidMap - {} / invalid value - arg len 1 greater than remaining buf len", + "input": "a163666f6f18", + "error": true + }, + { + "description": "TestDecode_InvalidMap - {_ } / no break - expected break marker", + "input": "bf", + "error": true + }, + { + "description": "TestDecode_InvalidMap - {_ } / invalid key - slice len 1 greater than remaining buf len", + "input": "bf7801", + "error": true + }, + { + "description": "TestDecode_InvalidMap - {_ } / invalid value - arg len 1 greater than remaining buf len", + "input": "bf63666f6f18", + "error": true + }, + { + "description": "TestDecode_InvalidMap - {} / eof after head - unexpected end of payload", + "input": "a1", + "error": true + }, + { + "description": "TestDecode_InvalidSlice - slice/1, not enough bytes - slice len 1 greater than remaining buf len", + "input": "5801", + "error": true + }, + { + "description": "TestDecode_InvalidSlice - slice/?, nested indefinite - nested indefinite slice", + "input": "5f5f", + "error": true + }, + { + "description": "TestDecode_InvalidSlice - string/?, no break - expected break marker", + "input": "7f", + "error": true + }, + { + "description": "TestDecode_InvalidSlice - string/?, nested indefinite - nested indefinite slice", + "input": "7f7f", + "error": true + }, + { + "description": "TestDecode_InvalidSlice - string/?, invalid nested definite - decode subslice: slice len 1 greater than remaining buf len", + "input": "7f7801", + "error": true + }, + { + "description": "TestDecode_InvalidSlice - slice/?, no break - expected break marker", + "input": "5f", + "error": true + }, + { + "description": "TestDecode_InvalidSlice - slice/?, invalid nested major - unexpected major type 3 in indefinite slice", + "input": "5f60", + "error": true + }, + { + "description": "TestDecode_InvalidSlice - slice/?, invalid nested definite - decode subslice: slice len 1 greater than remaining buf len", + "input": "5f5801", + "error": true + }, + { + "description": "TestDecode_InvalidSlice - string/1, not enough bytes - slice len 1 greater than remaining buf len", + "input": "7801", + "error": true + }, + { + "description": "TestDecode_InvalidSlice - string/?, invalid nested major - unexpected major type 2 in indefinite slice", + "input": "7f40", + "error": true + }, + { + "description": "TestDecode_InvalidTag - invalid value - arg len 1 greater than remaining buf len", + "input": "c118", + "error": true + }, + { + "description": "TestDecode_InvalidTag - eof - unexpected end of payload", + "input": "c1", + "error": true + } +] diff --git a/packages/core/src/submodules/cbor/test-data/success-tests.json b/packages/core/src/submodules/cbor/test-data/success-tests.json new file mode 100644 index 00000000000..2c64d6aa406 --- /dev/null +++ b/packages/core/src/submodules/cbor/test-data/success-tests.json @@ -0,0 +1,1509 @@ +[ + { + "description": "atomic - uint/0/max", + "input": "17", + "expect": { + "uint": 23 + } + }, + { + "description": "atomic - uint/2/min", + "input": "190000", + "expect": { + "uint": 0 + } + }, + { + "description": "atomic - uint/8/min", + "input": "1b0000000000000000", + "expect": { + "uint": 0 + } + }, + { + "description": "atomic - negint/1/min", + "input": "3800", + "expect": { + "negint": -1 + } + }, + { + "description": "atomic - negint/2/min", + "input": "390000", + "expect": { + "negint": -1 + } + }, + { + "description": "atomic - false", + "input": "f4", + "expect": { + "bool": false + } + }, + { + "description": "atomic - uint/1/min", + "input": "1800", + "expect": { + "uint": 0 + } + }, + { + "description": "atomic - negint/8/min", + "input": "3b0000000000000000", + "expect": { + "negint": -1 + } + }, + { + "description": "atomic - float64/+Inf", + "input": "fb7ff0000000000000", + "expect": { + "float64": 9218868437227405312 + } + }, + { + "description": "atomic - uint/4/min", + "input": "1a00000000", + "expect": { + "uint": 0 + } + }, + { + "description": "atomic - null", + "input": "f6", + "expect": { + "null": {} + } + }, + { + "description": "atomic - negint/2/max", + "input": "39ffff", + "expect": { + "negint": -65536 + } + }, + { + "description": "atomic - negint/8/max", + "input": "3bfffffffffffffffe", + "expect": { + "negint": -18446744073709551615 + } + }, + { + "description": "atomic - float32/1.625", + "input": "fa3fd00000", + "expect": { + "float32": 1070596096 + } + }, + { + "description": "atomic - uint/0/min", + "input": "00", + "expect": { + "uint": 0 + } + }, + { + "description": "atomic - uint/1/max", + "input": "18ff", + "expect": { + "uint": 255 + } + }, + { + "description": "atomic - uint/8/max", + "input": "1bffffffffffffffff", + "expect": { + "uint": 18446744073709551615 + } + }, + { + "description": "atomic - negint/1/max", + "input": "38ff", + "expect": { + "negint": -256 + } + }, + { + "description": "atomic - negint/4/min", + "input": "3a00000000", + "expect": { + "negint": -1 + } + }, + { + "description": "atomic - float64/1.625", + "input": "fb3ffa000000000000", + "expect": { + "float64": 4609997168567123968 + } + }, + { + "description": "atomic - uint/2/max", + "input": "19ffff", + "expect": { + "uint": 65535 + } + }, + { + "description": "atomic - negint/0/max", + "input": "37", + "expect": { + "negint": -24 + } + }, + { + "description": "atomic - negint/4/max", + "input": "3affffffff", + "expect": { + "negint": -4294967296 + } + }, + { + "description": "atomic - uint/4/max", + "input": "1affffffff", + "expect": { + "uint": 4294967295 + } + }, + { + "description": "atomic - negint/0/min", + "input": "20", + "expect": { + "negint": -1 + } + }, + { + "description": "atomic - true", + "input": "f5", + "expect": { + "bool": true + } + }, + { + "description": "atomic - float32/+Inf", + "input": "fa7f800000", + "expect": { + "float32": 2139095040 + } + }, + { + "description": "definite slice - len = 0", + "input": "40", + "expect": { + "bytestring": [] + } + }, + { + "description": "definite slice - len \u003e 0", + "input": "43666f6f", + "expect": { + "bytestring": [102, 111, 111] + } + }, + { + "description": "definite string - len = 0", + "input": "60", + "expect": { + "string": "" + } + }, + { + "description": "definite string - len \u003e 0", + "input": "63666f6f", + "expect": { + "string": "foo" + } + }, + { + "description": "indefinite slice - len = 0", + "input": "5fff", + "expect": { + "bytestring": [] + } + }, + { + "description": "indefinite slice - len = 0, explicit", + "input": "5f40ff", + "expect": { + "bytestring": [] + } + }, + { + "description": "indefinite slice - len = 0, len \u003e 0", + "input": "5f4043666f6fff", + "expect": { + "bytestring": [102, 111, 111] + } + }, + { + "description": "indefinite slice - len \u003e 0, len = 0", + "input": "5f43666f6f40ff", + "expect": { + "bytestring": [102, 111, 111] + } + }, + { + "description": "indefinite slice - len \u003e 0, len \u003e 0", + "input": "5f43666f6f43666f6fff", + "expect": { + "bytestring": [102, 111, 111, 102, 111, 111] + } + }, + { + "description": "indefinite string - len = 0", + "input": "7fff", + "expect": { + "string": "" + } + }, + { + "description": "indefinite string - len = 0, explicit", + "input": "7f60ff", + "expect": { + "string": "" + } + }, + { + "description": "indefinite string - len = 0, len \u003e 0", + "input": "7f6063666f6fff", + "expect": { + "string": "foo" + } + }, + { + "description": "indefinite string - len \u003e 0, len = 0", + "input": "7f63666f6f60ff", + "expect": { + "string": "foo" + } + }, + { + "description": "indefinite string - len \u003e 0, len \u003e 0", + "input": "7f63666f6f63666f6fff", + "expect": { + "string": "foofoo" + } + }, + { + "description": "list - [float64]", + "input": "81fb7ff0000000000000", + "expect": { + "list": [ + { + "float64": 9218868437227405312 + } + ] + } + }, + { + "description": "list - [_ negint/4/min]", + "input": "9f3a00000000ff", + "expect": { + "list": [ + { + "negint": -1 + } + ] + } + }, + { + "description": "list - [uint/1/min]", + "input": "811800", + "expect": { + "list": [ + { + "uint": 0 + } + ] + } + }, + { + "description": "list - [_ uint/4/min]", + "input": "9f1a00000000ff", + "expect": { + "list": [ + { + "uint": 0 + } + ] + } + }, + { + "description": "list - [uint/0/max]", + "input": "8117", + "expect": { + "list": [ + { + "uint": 23 + } + ] + } + }, + { + "description": "list - [uint/1/max]", + "input": "8118ff", + "expect": { + "list": [ + { + "uint": 255 + } + ] + } + }, + { + "description": "list - [negint/2/min]", + "input": "81390000", + "expect": { + "list": [ + { + "negint": -1 + } + ] + } + }, + { + "description": "list - [negint/8/min]", + "input": "813b0000000000000000", + "expect": { + "list": [ + { + "negint": -1 + } + ] + } + }, + { + "description": "list - [_ uint/2/min]", + "input": "9f190000ff", + "expect": { + "list": [ + { + "uint": 0 + } + ] + } + }, + { + "description": "list - [uint/0/min]", + "input": "8100", + "expect": { + "list": [ + { + "uint": 0 + } + ] + } + }, + { + "description": "list - [negint/0/min]", + "input": "8120", + "expect": { + "list": [ + { + "negint": -1 + } + ] + } + }, + { + "description": "list - [negint/0/max]", + "input": "8137", + "expect": { + "list": [ + { + "negint": -24 + } + ] + } + }, + { + "description": "list - [negint/1/min]", + "input": "813800", + "expect": { + "list": [ + { + "negint": -1 + } + ] + } + }, + { + "description": "list - [negint/1/max]", + "input": "8138ff", + "expect": { + "list": [ + { + "negint": -256 + } + ] + } + }, + { + "description": "list - [negint/4/max]", + "input": "813affffffff", + "expect": { + "list": [ + { + "negint": -4294967296 + } + ] + } + }, + { + "description": "list - [_ uint/4/max]", + "input": "9f1affffffffff", + "expect": { + "list": [ + { + "uint": 4294967295 + } + ] + } + }, + { + "description": "list - [_ negint/0/max]", + "input": "9f37ff", + "expect": { + "list": [ + { + "negint": -24 + } + ] + } + }, + { + "description": "list - [uint/2/min]", + "input": "81190000", + "expect": { + "list": [ + { + "uint": 0 + } + ] + } + }, + { + "description": "list - [_ false]", + "input": "9ff4ff", + "expect": { + "list": [ + { + "bool": false + } + ] + } + }, + { + "description": "list - [_ float32]", + "input": "9ffa7f800000ff", + "expect": { + "list": [ + { + "float32": 2139095040 + } + ] + } + }, + { + "description": "list - [_ negint/1/max]", + "input": "9f38ffff", + "expect": { + "list": [ + { + "negint": -256 + } + ] + } + }, + { + "description": "list - [uint/8/max]", + "input": "811bffffffffffffffff", + "expect": { + "list": [ + { + "uint": 18446744073709551615 + } + ] + } + }, + { + "description": "list - [negint/4/min]", + "input": "813a00000000", + "expect": { + "list": [ + { + "negint": -1 + } + ] + } + }, + { + "description": "list - [negint/8/max]", + "input": "813bfffffffffffffffe", + "expect": { + "list": [ + { + "negint": -18446744073709551615 + } + ] + } + }, + { + "description": "list - [_ negint/2/min]", + "input": "9f390000ff", + "expect": { + "list": [ + { + "negint": -1 + } + ] + } + }, + { + "description": "list - [_ negint/4/max]", + "input": "9f3affffffffff", + "expect": { + "list": [ + { + "negint": -4294967296 + } + ] + } + }, + { + "description": "list - [_ true]", + "input": "9ff5ff", + "expect": { + "list": [ + { + "bool": true + } + ] + } + }, + { + "description": "list - [_ null]", + "input": "9ff6ff", + "expect": { + "list": [ + { + "null": {} + } + ] + } + }, + { + "description": "list - [uint/8/min]", + "input": "811b0000000000000000", + "expect": { + "list": [ + { + "uint": 0 + } + ] + } + }, + { + "description": "list - [null]", + "input": "81f6", + "expect": { + "list": [ + { + "null": {} + } + ] + } + }, + { + "description": "list - [_ uint/1/min]", + "input": "9f1800ff", + "expect": { + "list": [ + { + "uint": 0 + } + ] + } + }, + { + "description": "list - [_ uint/1/max]", + "input": "9f18ffff", + "expect": { + "list": [ + { + "uint": 255 + } + ] + } + }, + { + "description": "list - [_ uint/2/max]", + "input": "9f19ffffff", + "expect": { + "list": [ + { + "uint": 65535 + } + ] + } + }, + { + "description": "list - [_ uint/8/min]", + "input": "9f1b0000000000000000ff", + "expect": { + "list": [ + { + "uint": 0 + } + ] + } + }, + { + "description": "list - [_ negint/8/min]", + "input": "9f3b0000000000000000ff", + "expect": { + "list": [ + { + "negint": -1 + } + ] + } + }, + { + "description": "list - [_ float64]", + "input": "9ffb7ff0000000000000ff", + "expect": { + "list": [ + { + "float64": 9218868437227405312 + } + ] + } + }, + { + "description": "list - [uint/4/min]", + "input": "811a00000000", + "expect": { + "list": [ + { + "uint": 0 + } + ] + } + }, + { + "description": "list - [true]", + "input": "81f5", + "expect": { + "list": [ + { + "bool": true + } + ] + } + }, + { + "description": "list - [float32]", + "input": "81fa7f800000", + "expect": { + "list": [ + { + "float32": 2139095040 + } + ] + } + }, + { + "description": "list - [_ uint/0/min]", + "input": "9f00ff", + "expect": { + "list": [ + { + "uint": 0 + } + ] + } + }, + { + "description": "list - [_ uint/0/max]", + "input": "9f17ff", + "expect": { + "list": [ + { + "uint": 23 + } + ] + } + }, + { + "description": "list - [_ uint/8/max]", + "input": "9f1bffffffffffffffffff", + "expect": { + "list": [ + { + "uint": 18446744073709551615 + } + ] + } + }, + { + "description": "list - [_ negint/1/min]", + "input": "9f3800ff", + "expect": { + "list": [ + { + "negint": -1 + } + ] + } + }, + { + "description": "list - [_ negint/2/max]", + "input": "9f39ffffff", + "expect": { + "list": [ + { + "negint": -65536 + } + ] + } + }, + { + "description": "list - [uint/2/max]", + "input": "8119ffff", + "expect": { + "list": [ + { + "uint": 65535 + } + ] + } + }, + { + "description": "list - [negint/2/max]", + "input": "8139ffff", + "expect": { + "list": [ + { + "negint": -65536 + } + ] + } + }, + { + "description": "list - [false]", + "input": "81f4", + "expect": { + "list": [ + { + "bool": false + } + ] + } + }, + { + "description": "list - [_ negint/0/min]", + "input": "9f20ff", + "expect": { + "list": [ + { + "negint": -1 + } + ] + } + }, + { + "description": "list - [_ negint/8/max]", + "input": "9f3bfffffffffffffffeff", + "expect": { + "list": [ + { + "negint": -18446744073709551615 + } + ] + } + }, + { + "description": "list - [uint/4/max]", + "input": "811affffffff", + "expect": { + "list": [ + { + "uint": 4294967295 + } + ] + } + }, + { + "description": "map - {uint/0/min}", + "input": "a163666f6f00", + "expect": { + "map": { + "foo": { + "uint": 0 + } + } + } + }, + { + "description": "map - {uint/4/max}", + "input": "a163666f6f1affffffff", + "expect": { + "map": { + "foo": { + "uint": 4294967295 + } + } + } + }, + { + "description": "map - {negint/0/min}", + "input": "a163666f6f20", + "expect": { + "map": { + "foo": { + "negint": -1 + } + } + } + }, + { + "description": "map - {_ float32}", + "input": "bf63666f6ffa7f800000ff", + "expect": { + "map": { + "foo": { + "float32": 2139095040 + } + } + } + }, + { + "description": "map - {false}", + "input": "a163666f6ff4", + "expect": { + "map": { + "foo": { + "bool": false + } + } + } + }, + { + "description": "map - {float32}", + "input": "a163666f6ffa7f800000", + "expect": { + "map": { + "foo": { + "float32": 2139095040 + } + } + } + }, + { + "description": "map - {_ uint/0/max}", + "input": "bf63666f6f17ff", + "expect": { + "map": { + "foo": { + "uint": 23 + } + } + } + }, + { + "description": "map - {_ negint/2/min}", + "input": "bf63666f6f390000ff", + "expect": { + "map": { + "foo": { + "negint": -1 + } + } + } + }, + { + "description": "map - {_ false}", + "input": "bf63666f6ff4ff", + "expect": { + "map": { + "foo": { + "bool": false + } + } + } + }, + { + "description": "map - {uint/8/min}", + "input": "a163666f6f1b0000000000000000", + "expect": { + "map": { + "foo": { + "uint": 0 + } + } + } + }, + { + "description": "map - {_ negint/0/max}", + "input": "bf63666f6f37ff", + "expect": { + "map": { + "foo": { + "negint": -24 + } + } + } + }, + { + "description": "map - {_ null}", + "input": "bf63666f6ff6ff", + "expect": { + "map": { + "foo": { + "null": {} + } + } + } + }, + { + "description": "map - {uint/1/min}", + "input": "a163666f6f1800", + "expect": { + "map": { + "foo": { + "uint": 0 + } + } + } + }, + { + "description": "map - {_ uint/1/min}", + "input": "bf63666f6f1800ff", + "expect": { + "map": { + "foo": { + "uint": 0 + } + } + } + }, + { + "description": "map - {_ uint/8/max}", + "input": "bf63666f6f1bffffffffffffffffff", + "expect": { + "map": { + "foo": { + "uint": 18446744073709551615 + } + } + } + }, + { + "description": "map - {_ negint/0/min}", + "input": "bf63666f6f20ff", + "expect": { + "map": { + "foo": { + "negint": -1 + } + } + } + }, + { + "description": "map - {_ negint/1/min}", + "input": "bf63666f6f3800ff", + "expect": { + "map": { + "foo": { + "negint": -1 + } + } + } + }, + { + "description": "map - {_ negint/1/max}", + "input": "bf63666f6f38ffff", + "expect": { + "map": { + "foo": { + "negint": -256 + } + } + } + }, + { + "description": "map - {_ negint/2/max}", + "input": "bf63666f6f39ffffff", + "expect": { + "map": { + "foo": { + "negint": -65536 + } + } + } + }, + { + "description": "map - {_ negint/4/min}", + "input": "bf63666f6f3a00000000ff", + "expect": { + "map": { + "foo": { + "negint": -1 + } + } + } + }, + { + "description": "map - {_ true}", + "input": "bf63666f6ff5ff", + "expect": { + "map": { + "foo": { + "bool": true + } + } + } + }, + { + "description": "map - {uint/2/max}", + "input": "a163666f6f19ffff", + "expect": { + "map": { + "foo": { + "uint": 65535 + } + } + } + }, + { + "description": "map - {uint/8/max}", + "input": "a163666f6f1bffffffffffffffff", + "expect": { + "map": { + "foo": { + "uint": 18446744073709551615 + } + } + } + }, + { + "description": "map - {negint/0/max}", + "input": "a163666f6f37", + "expect": { + "map": { + "foo": { + "negint": -24 + } + } + } + }, + { + "description": "map - {negint/1/max}", + "input": "a163666f6f38ff", + "expect": { + "map": { + "foo": { + "negint": -256 + } + } + } + }, + { + "description": "map - {negint/2/max}", + "input": "a163666f6f39ffff", + "expect": { + "map": { + "foo": { + "negint": -65536 + } + } + } + }, + { + "description": "map - {negint/4/min}", + "input": "a163666f6f3a00000000", + "expect": { + "map": { + "foo": { + "negint": -1 + } + } + } + }, + { + "description": "map - {negint/8/max}", + "input": "a163666f6f3bfffffffffffffffe", + "expect": { + "map": { + "foo": { + "negint": -18446744073709551615 + } + } + } + }, + { + "description": "map - {float64}", + "input": "a163666f6ffb7ff0000000000000", + "expect": { + "map": { + "foo": { + "float64": 9218868437227405312 + } + } + } + }, + { + "description": "map - {_ uint/0/min}", + "input": "bf63666f6f00ff", + "expect": { + "map": { + "foo": { + "uint": 0 + } + } + } + }, + { + "description": "map - {_ uint/4/min}", + "input": "bf63666f6f1a00000000ff", + "expect": { + "map": { + "foo": { + "uint": 0 + } + } + } + }, + { + "description": "map - {_ uint/8/min}", + "input": "bf63666f6f1b0000000000000000ff", + "expect": { + "map": { + "foo": { + "uint": 0 + } + } + } + }, + { + "description": "map - {uint/1/max}", + "input": "a163666f6f18ff", + "expect": { + "map": { + "foo": { + "uint": 255 + } + } + } + }, + { + "description": "map - {negint/2/min}", + "input": "a163666f6f390000", + "expect": { + "map": { + "foo": { + "negint": -1 + } + } + } + }, + { + "description": "map - {negint/8/min}", + "input": "a163666f6f3b0000000000000000", + "expect": { + "map": { + "foo": { + "negint": -1 + } + } + } + }, + { + "description": "map - {true}", + "input": "a163666f6ff5", + "expect": { + "map": { + "foo": { + "bool": true + } + } + } + }, + { + "description": "map - {_ uint/2/min}", + "input": "bf63666f6f190000ff", + "expect": { + "map": { + "foo": { + "uint": 0 + } + } + } + }, + { + "description": "map - {_ negint/8/min}", + "input": "bf63666f6f3b0000000000000000ff", + "expect": { + "map": { + "foo": { + "negint": -1 + } + } + } + }, + { + "description": "map - {_ negint/8/max}", + "input": "bf63666f6f3bfffffffffffffffeff", + "expect": { + "map": { + "foo": { + "negint": -18446744073709551615 + } + } + } + }, + { + "description": "map - {uint/0/max}", + "input": "a163666f6f17", + "expect": { + "map": { + "foo": { + "uint": 23 + } + } + } + }, + { + "description": "map - {negint/4/max}", + "input": "a163666f6f3affffffff", + "expect": { + "map": { + "foo": { + "negint": -4294967296 + } + } + } + }, + { + "description": "map - {null}", + "input": "a163666f6ff6", + "expect": { + "map": { + "foo": { + "null": {} + } + } + } + }, + { + "description": "map - {_ uint/4/max}", + "input": "bf63666f6f1affffffffff", + "expect": { + "map": { + "foo": { + "uint": 4294967295 + } + } + } + }, + { + "description": "map - {_ float64}", + "input": "bf63666f6ffb7ff0000000000000ff", + "expect": { + "map": { + "foo": { + "float64": 9218868437227405312 + } + } + } + }, + { + "description": "map - {uint/2/min}", + "input": "a163666f6f190000", + "expect": { + "map": { + "foo": { + "uint": 0 + } + } + } + }, + { + "description": "map - {uint/4/min}", + "input": "a163666f6f1a00000000", + "expect": { + "map": { + "foo": { + "uint": 0 + } + } + } + }, + { + "description": "map - {negint/1/min}", + "input": "a163666f6f3800", + "expect": { + "map": { + "foo": { + "negint": -1 + } + } + } + }, + { + "description": "map - {_ uint/1/max}", + "input": "bf63666f6f18ffff", + "expect": { + "map": { + "foo": { + "uint": 255 + } + } + } + }, + { + "description": "map - {_ uint/2/max}", + "input": "bf63666f6f19ffffff", + "expect": { + "map": { + "foo": { + "uint": 65535 + } + } + } + }, + { + "description": "map - {_ negint/4/max}", + "input": "bf63666f6f3affffffffff", + "expect": { + "map": { + "foo": { + "negint": -4294967296 + } + } + } + }, + { + "description": "tag - 0/min", + "input": "c001", + "expect": { + "tag": { + "id": 0, + "value": { + "uint": 1 + } + } + } + }, + { + "description": "tag - 1/min", + "input": "d80001", + "expect": { + "tag": { + "id": 0, + "value": { + "uint": 1 + } + } + } + }, + { + "description": "tag - 1/max", + "input": "d8ff01", + "expect": { + "tag": { + "id": 255, + "value": { + "uint": 1 + } + } + } + }, + { + "description": "tag - 4/min", + "input": "da0000000001", + "expect": { + "tag": { + "id": 0, + "value": { + "uint": 1 + } + } + } + }, + { + "description": "tag - 8/min", + "input": "db000000000000000001", + "expect": { + "tag": { + "id": 0, + "value": { + "uint": 1 + } + } + } + }, + { + "description": "tag - 0/max", + "input": "d701", + "expect": { + "tag": { + "id": 23, + "value": { + "uint": 1 + } + } + } + }, + { + "description": "tag - 2/min", + "input": "d9000001", + "expect": { + "tag": { + "id": 0, + "value": { + "uint": 1 + } + } + } + }, + { + "description": "tag - 2/max", + "input": "d9ffff01", + "expect": { + "tag": { + "id": 65535, + "value": { + "uint": 1 + } + } + } + }, + { + "description": "tag - 4/max", + "input": "daffffffff01", + "expect": { + "tag": { + "id": 4294967295, + "value": { + "uint": 1 + } + } + } + }, + { + "description": "tag - 8/max", + "input": "dbffffffffffffffff01", + "expect": { + "tag": { + "id": 18446744073709551615, + "value": { + "uint": 1 + } + } + } + } +] diff --git a/packages/core/tsconfig.cjs.json b/packages/core/tsconfig.cjs.json index 046da7474fa..369e51b86bc 100644 --- a/packages/core/tsconfig.cjs.json +++ b/packages/core/tsconfig.cjs.json @@ -3,7 +3,9 @@ "baseUrl": ".", "outDir": "dist-cjs", "rootDir": "src", - "paths": {} + "paths": { + "@smithy/core/cbor": ["./src/submodules/cbor/index.ts"] + } }, "extends": "../../tsconfig.cjs.json", "include": ["src/"] diff --git a/packages/core/tsconfig.es.json b/packages/core/tsconfig.es.json index 387e3fd0ab2..310918c39d3 100644 --- a/packages/core/tsconfig.es.json +++ b/packages/core/tsconfig.es.json @@ -4,7 +4,9 @@ "lib": ["dom"], "outDir": "dist-es", "rootDir": "src", - "paths": {} + "paths": { + "@smithy/core/cbor": ["./src/submodules/cbor/index.ts"] + } }, "extends": "../../tsconfig.es.json", "include": ["src/"] diff --git a/packages/core/tsconfig.types.json b/packages/core/tsconfig.types.json index 07987306c26..a50f3dbebad 100644 --- a/packages/core/tsconfig.types.json +++ b/packages/core/tsconfig.types.json @@ -3,7 +3,9 @@ "baseUrl": ".", "declarationDir": "dist-types", "rootDir": "src", - "paths": {} + "paths": { + "@smithy/core/cbor": ["./src/submodules/cbor/index.ts"] + } }, "extends": "../../tsconfig.types.json", "include": ["src/"] diff --git a/packages/smithy-client/src/date-utils.ts b/packages/smithy-client/src/date-utils.ts index e4949acca65..34e2afea950 100644 --- a/packages/smithy-client/src/date-utils.ts +++ b/packages/smithy-client/src/date-utils.ts @@ -218,6 +218,9 @@ export const parseEpochTimestamp = (value: unknown): Date | undefined => { valueAsDouble = value; } else if (typeof value === "string") { valueAsDouble = strictParseDouble(value)!; + } else if (typeof value === "object" && (value as { tag: number; value: number }).tag === 1) { + // timestamp is a CBOR tag type. + valueAsDouble = (value as { tag: number; value: number }).value; } else { throw new TypeError("Epoch timestamps must be expressed as floating point numbers or their string representation"); } diff --git a/packages/smithy-client/src/serde-json.ts b/packages/smithy-client/src/serde-json.ts index b82ff75ba0e..a979e654d52 100644 --- a/packages/smithy-client/src/serde-json.ts +++ b/packages/smithy-client/src/serde-json.ts @@ -4,6 +4,8 @@ * Maps an object through the default JSON serde behavior. * This means removing nullish fields and un-sparsifying lists. * + * This is also used by Smithy RPCv2 CBOR as the default serde behavior. + * * @param obj - to be checked. * @returns same object with default serde behavior applied. */ diff --git a/private/smithy-rpcv2-cbor/jest.config.js b/private/smithy-rpcv2-cbor/jest.config.js new file mode 100644 index 00000000000..a8d1c2e4991 --- /dev/null +++ b/private/smithy-rpcv2-cbor/jest.config.js @@ -0,0 +1,5 @@ +const base = require("../../jest.config.base.js"); + +module.exports = { + ...base, +}; diff --git a/private/smithy-rpcv2-cbor/package.json b/private/smithy-rpcv2-cbor/package.json new file mode 100644 index 00000000000..59a05bc0015 --- /dev/null +++ b/private/smithy-rpcv2-cbor/package.json @@ -0,0 +1,80 @@ +{ + "name": "@smithy/smithy-rpcv2-cbor", + "description": "@smithy/smithy-rpcv2-cbor client", + "version": "1.0.0-alpha.1", + "scripts": { + "build": "concurrently 'yarn:build:cjs' 'yarn:build:es' 'yarn:build:types'", + "build:cjs": "tsc -p tsconfig.cjs.json", + "build:es": "tsc -p tsconfig.es.json", + "build:types": "tsc -p tsconfig.types.json", + "build:types:downlevel": "downlevel-dts dist-types dist-types/ts3.4", + "clean": "rimraf ./dist-* && rimraf *.tsbuildinfo || exit 0", + "prepack": "yarn run clean && yarn run build" + }, + "main": "./dist-cjs/index.js", + "types": "./dist-types/index.d.ts", + "module": "./dist-es/index.js", + "sideEffects": false, + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/types": "latest", + "@smithy/config-resolver": "^3.0.5", + "@smithy/core": "^2.3.2", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/hash-node": "^3.0.3", + "@smithy/invalid-dependency": "^3.0.3", + "@smithy/middleware-content-length": "^3.0.5", + "@smithy/middleware-retry": "^3.0.14", + "@smithy/middleware-serde": "^3.0.3", + "@smithy/middleware-stack": "^3.0.3", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.1.12", + "@smithy/types": "^3.3.0", + "@smithy/url-parser": "^3.0.3", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.14", + "@smithy/util-defaults-mode-node": "^3.0.14", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-retry": "^3.0.3", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "devDependencies": { + "@tsconfig/node16": "16.1.3", + "@types/node": "^16.18.96", + "concurrently": "7.0.0", + "downlevel-dts": "0.10.1", + "rimraf": "^3.0.0", + "typescript": "~4.9.5" + }, + "engines": { + "node": ">=16.0.0" + }, + "typesVersions": { + "<4.0": { + "dist-types/*": [ + "dist-types/ts3.4/*" + ] + } + }, + "files": [ + "dist-*/**" + ], + "author": { + "name": "Smithy team", + "url": "https://smithy.io/" + }, + "license": "Apache-2.0", + "browser": { + "./dist-es/runtimeConfig": "./dist-es/runtimeConfig.browser" + }, + "react-native": { + "./dist-es/runtimeConfig": "./dist-es/runtimeConfig.native" + }, + "private": true +} diff --git a/private/smithy-rpcv2-cbor/src/RpcV2Protocol.ts b/private/smithy-rpcv2-cbor/src/RpcV2Protocol.ts new file mode 100644 index 00000000000..cde70ffa0c1 --- /dev/null +++ b/private/smithy-rpcv2-cbor/src/RpcV2Protocol.ts @@ -0,0 +1,305 @@ +// smithy-typescript generated code +import { RpcV2ProtocolClient, RpcV2ProtocolClientConfig } from "./RpcV2ProtocolClient"; +import { + EmptyInputOutputCommand, + EmptyInputOutputCommandInput, + EmptyInputOutputCommandOutput, +} from "./commands/EmptyInputOutputCommand"; +import { Float16Command, Float16CommandInput, Float16CommandOutput } from "./commands/Float16Command"; +import { + FractionalSecondsCommand, + FractionalSecondsCommandInput, + FractionalSecondsCommandOutput, +} from "./commands/FractionalSecondsCommand"; +import { + GreetingWithErrorsCommand, + GreetingWithErrorsCommandInput, + GreetingWithErrorsCommandOutput, +} from "./commands/GreetingWithErrorsCommand"; +import { + NoInputOutputCommand, + NoInputOutputCommandInput, + NoInputOutputCommandOutput, +} from "./commands/NoInputOutputCommand"; +import { + OperationWithDefaultsCommand, + OperationWithDefaultsCommandInput, + OperationWithDefaultsCommandOutput, +} from "./commands/OperationWithDefaultsCommand"; +import { + OptionalInputOutputCommand, + OptionalInputOutputCommandInput, + OptionalInputOutputCommandOutput, +} from "./commands/OptionalInputOutputCommand"; +import { + RecursiveShapesCommand, + RecursiveShapesCommandInput, + RecursiveShapesCommandOutput, +} from "./commands/RecursiveShapesCommand"; +import { + RpcV2CborDenseMapsCommand, + RpcV2CborDenseMapsCommandInput, + RpcV2CborDenseMapsCommandOutput, +} from "./commands/RpcV2CborDenseMapsCommand"; +import { + RpcV2CborListsCommand, + RpcV2CborListsCommandInput, + RpcV2CborListsCommandOutput, +} from "./commands/RpcV2CborListsCommand"; +import { + RpcV2CborSparseMapsCommand, + RpcV2CborSparseMapsCommandInput, + RpcV2CborSparseMapsCommandOutput, +} from "./commands/RpcV2CborSparseMapsCommand"; +import { + SimpleScalarPropertiesCommand, + SimpleScalarPropertiesCommandInput, + SimpleScalarPropertiesCommandOutput, +} from "./commands/SimpleScalarPropertiesCommand"; +import { + SparseNullsOperationCommand, + SparseNullsOperationCommandInput, + SparseNullsOperationCommandOutput, +} from "./commands/SparseNullsOperationCommand"; +import { createAggregatedClient } from "@smithy/smithy-client"; +import { HttpHandlerOptions as __HttpHandlerOptions } from "@smithy/types"; + +const commands = { + EmptyInputOutputCommand, + Float16Command, + FractionalSecondsCommand, + GreetingWithErrorsCommand, + NoInputOutputCommand, + OperationWithDefaultsCommand, + OptionalInputOutputCommand, + RecursiveShapesCommand, + RpcV2CborDenseMapsCommand, + RpcV2CborListsCommand, + RpcV2CborSparseMapsCommand, + SimpleScalarPropertiesCommand, + SparseNullsOperationCommand, +}; + +export interface RpcV2Protocol { + /** + * @see {@link EmptyInputOutputCommand} + */ + emptyInputOutput(): Promise; + emptyInputOutput( + args: EmptyInputOutputCommandInput, + options?: __HttpHandlerOptions + ): Promise; + emptyInputOutput( + args: EmptyInputOutputCommandInput, + cb: (err: any, data?: EmptyInputOutputCommandOutput) => void + ): void; + emptyInputOutput( + args: EmptyInputOutputCommandInput, + options: __HttpHandlerOptions, + cb: (err: any, data?: EmptyInputOutputCommandOutput) => void + ): void; + + /** + * @see {@link Float16Command} + */ + float16(): Promise; + float16(args: Float16CommandInput, options?: __HttpHandlerOptions): Promise; + float16(args: Float16CommandInput, cb: (err: any, data?: Float16CommandOutput) => void): void; + float16( + args: Float16CommandInput, + options: __HttpHandlerOptions, + cb: (err: any, data?: Float16CommandOutput) => void + ): void; + + /** + * @see {@link FractionalSecondsCommand} + */ + fractionalSeconds(): Promise; + fractionalSeconds( + args: FractionalSecondsCommandInput, + options?: __HttpHandlerOptions + ): Promise; + fractionalSeconds( + args: FractionalSecondsCommandInput, + cb: (err: any, data?: FractionalSecondsCommandOutput) => void + ): void; + fractionalSeconds( + args: FractionalSecondsCommandInput, + options: __HttpHandlerOptions, + cb: (err: any, data?: FractionalSecondsCommandOutput) => void + ): void; + + /** + * @see {@link GreetingWithErrorsCommand} + */ + greetingWithErrors(): Promise; + greetingWithErrors( + args: GreetingWithErrorsCommandInput, + options?: __HttpHandlerOptions + ): Promise; + greetingWithErrors( + args: GreetingWithErrorsCommandInput, + cb: (err: any, data?: GreetingWithErrorsCommandOutput) => void + ): void; + greetingWithErrors( + args: GreetingWithErrorsCommandInput, + options: __HttpHandlerOptions, + cb: (err: any, data?: GreetingWithErrorsCommandOutput) => void + ): void; + + /** + * @see {@link NoInputOutputCommand} + */ + noInputOutput(): Promise; + noInputOutput(args: NoInputOutputCommandInput, options?: __HttpHandlerOptions): Promise; + noInputOutput(args: NoInputOutputCommandInput, cb: (err: any, data?: NoInputOutputCommandOutput) => void): void; + noInputOutput( + args: NoInputOutputCommandInput, + options: __HttpHandlerOptions, + cb: (err: any, data?: NoInputOutputCommandOutput) => void + ): void; + + /** + * @see {@link OperationWithDefaultsCommand} + */ + operationWithDefaults(): Promise; + operationWithDefaults( + args: OperationWithDefaultsCommandInput, + options?: __HttpHandlerOptions + ): Promise; + operationWithDefaults( + args: OperationWithDefaultsCommandInput, + cb: (err: any, data?: OperationWithDefaultsCommandOutput) => void + ): void; + operationWithDefaults( + args: OperationWithDefaultsCommandInput, + options: __HttpHandlerOptions, + cb: (err: any, data?: OperationWithDefaultsCommandOutput) => void + ): void; + + /** + * @see {@link OptionalInputOutputCommand} + */ + optionalInputOutput(): Promise; + optionalInputOutput( + args: OptionalInputOutputCommandInput, + options?: __HttpHandlerOptions + ): Promise; + optionalInputOutput( + args: OptionalInputOutputCommandInput, + cb: (err: any, data?: OptionalInputOutputCommandOutput) => void + ): void; + optionalInputOutput( + args: OptionalInputOutputCommandInput, + options: __HttpHandlerOptions, + cb: (err: any, data?: OptionalInputOutputCommandOutput) => void + ): void; + + /** + * @see {@link RecursiveShapesCommand} + */ + recursiveShapes(): Promise; + recursiveShapes( + args: RecursiveShapesCommandInput, + options?: __HttpHandlerOptions + ): Promise; + recursiveShapes(args: RecursiveShapesCommandInput, cb: (err: any, data?: RecursiveShapesCommandOutput) => void): void; + recursiveShapes( + args: RecursiveShapesCommandInput, + options: __HttpHandlerOptions, + cb: (err: any, data?: RecursiveShapesCommandOutput) => void + ): void; + + /** + * @see {@link RpcV2CborDenseMapsCommand} + */ + rpcV2CborDenseMaps(): Promise; + rpcV2CborDenseMaps( + args: RpcV2CborDenseMapsCommandInput, + options?: __HttpHandlerOptions + ): Promise; + rpcV2CborDenseMaps( + args: RpcV2CborDenseMapsCommandInput, + cb: (err: any, data?: RpcV2CborDenseMapsCommandOutput) => void + ): void; + rpcV2CborDenseMaps( + args: RpcV2CborDenseMapsCommandInput, + options: __HttpHandlerOptions, + cb: (err: any, data?: RpcV2CborDenseMapsCommandOutput) => void + ): void; + + /** + * @see {@link RpcV2CborListsCommand} + */ + rpcV2CborLists(): Promise; + rpcV2CborLists( + args: RpcV2CborListsCommandInput, + options?: __HttpHandlerOptions + ): Promise; + rpcV2CborLists(args: RpcV2CborListsCommandInput, cb: (err: any, data?: RpcV2CborListsCommandOutput) => void): void; + rpcV2CborLists( + args: RpcV2CborListsCommandInput, + options: __HttpHandlerOptions, + cb: (err: any, data?: RpcV2CborListsCommandOutput) => void + ): void; + + /** + * @see {@link RpcV2CborSparseMapsCommand} + */ + rpcV2CborSparseMaps(): Promise; + rpcV2CborSparseMaps( + args: RpcV2CborSparseMapsCommandInput, + options?: __HttpHandlerOptions + ): Promise; + rpcV2CborSparseMaps( + args: RpcV2CborSparseMapsCommandInput, + cb: (err: any, data?: RpcV2CborSparseMapsCommandOutput) => void + ): void; + rpcV2CborSparseMaps( + args: RpcV2CborSparseMapsCommandInput, + options: __HttpHandlerOptions, + cb: (err: any, data?: RpcV2CborSparseMapsCommandOutput) => void + ): void; + + /** + * @see {@link SimpleScalarPropertiesCommand} + */ + simpleScalarProperties(): Promise; + simpleScalarProperties( + args: SimpleScalarPropertiesCommandInput, + options?: __HttpHandlerOptions + ): Promise; + simpleScalarProperties( + args: SimpleScalarPropertiesCommandInput, + cb: (err: any, data?: SimpleScalarPropertiesCommandOutput) => void + ): void; + simpleScalarProperties( + args: SimpleScalarPropertiesCommandInput, + options: __HttpHandlerOptions, + cb: (err: any, data?: SimpleScalarPropertiesCommandOutput) => void + ): void; + + /** + * @see {@link SparseNullsOperationCommand} + */ + sparseNullsOperation(): Promise; + sparseNullsOperation( + args: SparseNullsOperationCommandInput, + options?: __HttpHandlerOptions + ): Promise; + sparseNullsOperation( + args: SparseNullsOperationCommandInput, + cb: (err: any, data?: SparseNullsOperationCommandOutput) => void + ): void; + sparseNullsOperation( + args: SparseNullsOperationCommandInput, + options: __HttpHandlerOptions, + cb: (err: any, data?: SparseNullsOperationCommandOutput) => void + ): void; +} + +/** + * @public + */ +export class RpcV2Protocol extends RpcV2ProtocolClient implements RpcV2Protocol {} +createAggregatedClient(commands, RpcV2Protocol); diff --git a/private/smithy-rpcv2-cbor/src/RpcV2ProtocolClient.ts b/private/smithy-rpcv2-cbor/src/RpcV2ProtocolClient.ts new file mode 100644 index 00000000000..d978341a000 --- /dev/null +++ b/private/smithy-rpcv2-cbor/src/RpcV2ProtocolClient.ts @@ -0,0 +1,276 @@ +// smithy-typescript generated code +import { + HttpAuthSchemeInputConfig, + HttpAuthSchemeResolvedConfig, + defaultRpcV2ProtocolHttpAuthSchemeParametersProvider, + resolveHttpAuthSchemeConfig, +} from "./auth/httpAuthSchemeProvider"; +import { EmptyInputOutputCommandInput, EmptyInputOutputCommandOutput } from "./commands/EmptyInputOutputCommand"; +import { Float16CommandInput, Float16CommandOutput } from "./commands/Float16Command"; +import { FractionalSecondsCommandInput, FractionalSecondsCommandOutput } from "./commands/FractionalSecondsCommand"; +import { GreetingWithErrorsCommandInput, GreetingWithErrorsCommandOutput } from "./commands/GreetingWithErrorsCommand"; +import { NoInputOutputCommandInput, NoInputOutputCommandOutput } from "./commands/NoInputOutputCommand"; +import { + OperationWithDefaultsCommandInput, + OperationWithDefaultsCommandOutput, +} from "./commands/OperationWithDefaultsCommand"; +import { + OptionalInputOutputCommandInput, + OptionalInputOutputCommandOutput, +} from "./commands/OptionalInputOutputCommand"; +import { RecursiveShapesCommandInput, RecursiveShapesCommandOutput } from "./commands/RecursiveShapesCommand"; +import { RpcV2CborDenseMapsCommandInput, RpcV2CborDenseMapsCommandOutput } from "./commands/RpcV2CborDenseMapsCommand"; +import { RpcV2CborListsCommandInput, RpcV2CborListsCommandOutput } from "./commands/RpcV2CborListsCommand"; +import { + RpcV2CborSparseMapsCommandInput, + RpcV2CborSparseMapsCommandOutput, +} from "./commands/RpcV2CborSparseMapsCommand"; +import { + SimpleScalarPropertiesCommandInput, + SimpleScalarPropertiesCommandOutput, +} from "./commands/SimpleScalarPropertiesCommand"; +import { + SparseNullsOperationCommandInput, + SparseNullsOperationCommandOutput, +} from "./commands/SparseNullsOperationCommand"; +import { getRuntimeConfig as __getRuntimeConfig } from "./runtimeConfig"; +import { RuntimeExtension, RuntimeExtensionsConfig, resolveRuntimeExtensions } from "./runtimeExtensions"; +import { + CustomEndpointsInputConfig, + CustomEndpointsResolvedConfig, + resolveCustomEndpointsConfig, +} from "@smithy/config-resolver"; +import { DefaultIdentityProviderConfig, getHttpAuthSchemePlugin, getHttpSigningPlugin } from "@smithy/core"; +import { getContentLengthPlugin } from "@smithy/middleware-content-length"; +import { RetryInputConfig, RetryResolvedConfig, getRetryPlugin, resolveRetryConfig } from "@smithy/middleware-retry"; +import { HttpHandlerUserInput as __HttpHandlerUserInput } from "@smithy/protocol-http"; +import { + Client as __Client, + DefaultsMode as __DefaultsMode, + SmithyConfiguration as __SmithyConfiguration, + SmithyResolvedConfiguration as __SmithyResolvedConfiguration, +} from "@smithy/smithy-client"; +import { + BodyLengthCalculator as __BodyLengthCalculator, + CheckOptionalClientConfig as __CheckOptionalClientConfig, + ChecksumConstructor as __ChecksumConstructor, + Decoder as __Decoder, + Encoder as __Encoder, + HashConstructor as __HashConstructor, + HttpHandlerOptions as __HttpHandlerOptions, + Logger as __Logger, + Provider as __Provider, + StreamCollector as __StreamCollector, + UrlParser as __UrlParser, +} from "@smithy/types"; + +export { __Client }; + +/** + * @public + */ +export type ServiceInputTypes = + | EmptyInputOutputCommandInput + | Float16CommandInput + | FractionalSecondsCommandInput + | GreetingWithErrorsCommandInput + | NoInputOutputCommandInput + | OperationWithDefaultsCommandInput + | OptionalInputOutputCommandInput + | RecursiveShapesCommandInput + | RpcV2CborDenseMapsCommandInput + | RpcV2CborListsCommandInput + | RpcV2CborSparseMapsCommandInput + | SimpleScalarPropertiesCommandInput + | SparseNullsOperationCommandInput; + +/** + * @public + */ +export type ServiceOutputTypes = + | EmptyInputOutputCommandOutput + | Float16CommandOutput + | FractionalSecondsCommandOutput + | GreetingWithErrorsCommandOutput + | NoInputOutputCommandOutput + | OperationWithDefaultsCommandOutput + | OptionalInputOutputCommandOutput + | RecursiveShapesCommandOutput + | RpcV2CborDenseMapsCommandOutput + | RpcV2CborListsCommandOutput + | RpcV2CborSparseMapsCommandOutput + | SimpleScalarPropertiesCommandOutput + | SparseNullsOperationCommandOutput; + +/** + * @public + */ +export interface ClientDefaults extends Partial<__SmithyConfiguration<__HttpHandlerOptions>> { + /** + * The HTTP handler to use or its constructor options. Fetch in browser and Https in Nodejs. + */ + requestHandler?: __HttpHandlerUserInput; + + /** + * A constructor for a class implementing the {@link @smithy/types#ChecksumConstructor} interface + * that computes the SHA-256 HMAC or checksum of a string or binary buffer. + * @internal + */ + sha256?: __ChecksumConstructor | __HashConstructor; + + /** + * The function that will be used to convert strings into HTTP endpoints. + * @internal + */ + urlParser?: __UrlParser; + + /** + * A function that can calculate the length of a request body. + * @internal + */ + bodyLengthChecker?: __BodyLengthCalculator; + + /** + * A function that converts a stream into an array of bytes. + * @internal + */ + streamCollector?: __StreamCollector; + + /** + * The function that will be used to convert a base64-encoded string to a byte array. + * @internal + */ + base64Decoder?: __Decoder; + + /** + * The function that will be used to convert binary data to a base64-encoded string. + * @internal + */ + base64Encoder?: __Encoder; + + /** + * The function that will be used to convert a UTF8-encoded string to a byte array. + * @internal + */ + utf8Decoder?: __Decoder; + + /** + * The function that will be used to convert binary data to a UTF-8 encoded string. + * @internal + */ + utf8Encoder?: __Encoder; + + /** + * The runtime environment. + * @internal + */ + runtime?: string; + + /** + * Disable dynamically changing the endpoint of the client based on the hostPrefix + * trait of an operation. + */ + disableHostPrefix?: boolean; + + /** + * Value for how many times a request will be made at most in case of retry. + */ + maxAttempts?: number | __Provider; + + /** + * Specifies which retry algorithm to use. + * @see https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/Package/-smithy-util-retry/Enum/RETRY_MODES/ + * + */ + retryMode?: string | __Provider; + + /** + * Optional logger for logging debug/info/warn/error. + */ + logger?: __Logger; + + /** + * Optional extensions + */ + extensions?: RuntimeExtension[]; + + /** + * The {@link @smithy/smithy-client#DefaultsMode} that will be used to determine how certain default configuration options are resolved in the SDK. + */ + defaultsMode?: __DefaultsMode | __Provider<__DefaultsMode>; +} + +/** + * @public + */ +export type RpcV2ProtocolClientConfigType = Partial<__SmithyConfiguration<__HttpHandlerOptions>> & + ClientDefaults & + CustomEndpointsInputConfig & + RetryInputConfig & + HttpAuthSchemeInputConfig; +/** + * @public + * + * The configuration interface of RpcV2ProtocolClient class constructor that set the region, credentials and other options. + */ +export interface RpcV2ProtocolClientConfig extends RpcV2ProtocolClientConfigType {} + +/** + * @public + */ +export type RpcV2ProtocolClientResolvedConfigType = __SmithyResolvedConfiguration<__HttpHandlerOptions> & + Required & + RuntimeExtensionsConfig & + CustomEndpointsResolvedConfig & + RetryResolvedConfig & + HttpAuthSchemeResolvedConfig; +/** + * @public + * + * The resolved configuration interface of RpcV2ProtocolClient class. This is resolved and normalized from the {@link RpcV2ProtocolClientConfig | constructor configuration interface}. + */ +export interface RpcV2ProtocolClientResolvedConfig extends RpcV2ProtocolClientResolvedConfigType {} + +/** + * @public + */ +export class RpcV2ProtocolClient extends __Client< + __HttpHandlerOptions, + ServiceInputTypes, + ServiceOutputTypes, + RpcV2ProtocolClientResolvedConfig +> { + /** + * The resolved configuration of RpcV2ProtocolClient class. This is resolved and normalized from the {@link RpcV2ProtocolClientConfig | constructor configuration interface}. + */ + readonly config: RpcV2ProtocolClientResolvedConfig; + + constructor(...[configuration]: __CheckOptionalClientConfig) { + let _config_0 = __getRuntimeConfig(configuration || {}); + 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)); + this.middlewareStack.use( + getHttpAuthSchemePlugin(this.config, { + httpAuthSchemeParametersProvider: defaultRpcV2ProtocolHttpAuthSchemeParametersProvider, + identityProviderConfigProvider: async (config: RpcV2ProtocolClientResolvedConfig) => + new DefaultIdentityProviderConfig({}), + }) + ); + this.middlewareStack.use(getHttpSigningPlugin(this.config)); + } + + /** + * Destroy underlying resources, like sockets. It's usually not necessary to do this. + * However in Node.js, it's best to explicitly shut down the client's agent when it is no longer needed. + * Otherwise, sockets might stay open for quite a long time before the server terminates them. + */ + destroy(): void { + super.destroy(); + } +} diff --git a/private/smithy-rpcv2-cbor/src/auth/httpAuthExtensionConfiguration.ts b/private/smithy-rpcv2-cbor/src/auth/httpAuthExtensionConfiguration.ts new file mode 100644 index 00000000000..ef06738c086 --- /dev/null +++ b/private/smithy-rpcv2-cbor/src/auth/httpAuthExtensionConfiguration.ts @@ -0,0 +1,60 @@ +// smithy-typescript generated code +import { RpcV2ProtocolHttpAuthSchemeProvider } from "./httpAuthSchemeProvider"; +import { HttpAuthScheme } from "@smithy/types"; + +/** + * @internal + */ +export interface HttpAuthExtensionConfiguration { + setHttpAuthScheme(httpAuthScheme: HttpAuthScheme): void; + httpAuthSchemes(): HttpAuthScheme[]; + setHttpAuthSchemeProvider(httpAuthSchemeProvider: RpcV2ProtocolHttpAuthSchemeProvider): void; + httpAuthSchemeProvider(): RpcV2ProtocolHttpAuthSchemeProvider; +} + +/** + * @internal + */ +export type HttpAuthRuntimeConfig = Partial<{ + httpAuthSchemes: HttpAuthScheme[]; + httpAuthSchemeProvider: RpcV2ProtocolHttpAuthSchemeProvider; +}>; + +/** + * @internal + */ +export const getHttpAuthExtensionConfiguration = ( + runtimeConfig: HttpAuthRuntimeConfig +): HttpAuthExtensionConfiguration => { + let _httpAuthSchemes = runtimeConfig.httpAuthSchemes!; + let _httpAuthSchemeProvider = runtimeConfig.httpAuthSchemeProvider!; + return { + setHttpAuthScheme(httpAuthScheme: HttpAuthScheme): void { + const index = _httpAuthSchemes.findIndex((scheme) => scheme.schemeId === httpAuthScheme.schemeId); + if (index === -1) { + _httpAuthSchemes.push(httpAuthScheme); + } else { + _httpAuthSchemes.splice(index, 1, httpAuthScheme); + } + }, + httpAuthSchemes(): HttpAuthScheme[] { + return _httpAuthSchemes; + }, + setHttpAuthSchemeProvider(httpAuthSchemeProvider: RpcV2ProtocolHttpAuthSchemeProvider): void { + _httpAuthSchemeProvider = httpAuthSchemeProvider; + }, + httpAuthSchemeProvider(): RpcV2ProtocolHttpAuthSchemeProvider { + return _httpAuthSchemeProvider; + }, + }; +}; + +/** + * @internal + */ +export const resolveHttpAuthRuntimeConfig = (config: HttpAuthExtensionConfiguration): HttpAuthRuntimeConfig => { + return { + httpAuthSchemes: config.httpAuthSchemes(), + httpAuthSchemeProvider: config.httpAuthSchemeProvider(), + }; +}; diff --git a/private/smithy-rpcv2-cbor/src/auth/httpAuthSchemeProvider.ts b/private/smithy-rpcv2-cbor/src/auth/httpAuthSchemeProvider.ts new file mode 100644 index 00000000000..f9925d5944b --- /dev/null +++ b/private/smithy-rpcv2-cbor/src/auth/httpAuthSchemeProvider.ts @@ -0,0 +1,110 @@ +// smithy-typescript generated code +import { RpcV2ProtocolClientResolvedConfig } from "../RpcV2ProtocolClient"; +import { + HandlerExecutionContext, + HttpAuthOption, + HttpAuthScheme, + HttpAuthSchemeParameters, + HttpAuthSchemeParametersProvider, + HttpAuthSchemeProvider, +} from "@smithy/types"; +import { getSmithyContext } from "@smithy/util-middleware"; + +/** + * @internal + */ +export interface RpcV2ProtocolHttpAuthSchemeParameters extends HttpAuthSchemeParameters {} + +/** + * @internal + */ +export interface RpcV2ProtocolHttpAuthSchemeParametersProvider + extends HttpAuthSchemeParametersProvider< + RpcV2ProtocolClientResolvedConfig, + HandlerExecutionContext, + RpcV2ProtocolHttpAuthSchemeParameters, + object + > {} + +/** + * @internal + */ +export const defaultRpcV2ProtocolHttpAuthSchemeParametersProvider = async ( + config: RpcV2ProtocolClientResolvedConfig, + context: HandlerExecutionContext, + input: object +): Promise => { + return { + operation: getSmithyContext(context).operation as string, + }; +}; + +function createSmithyApiNoAuthHttpAuthOption(authParameters: RpcV2ProtocolHttpAuthSchemeParameters): HttpAuthOption { + return { + schemeId: "smithy.api#noAuth", + }; +} + +/** + * @internal + */ +export interface RpcV2ProtocolHttpAuthSchemeProvider + extends HttpAuthSchemeProvider {} + +/** + * @internal + */ +export const defaultRpcV2ProtocolHttpAuthSchemeProvider: RpcV2ProtocolHttpAuthSchemeProvider = (authParameters) => { + const options: HttpAuthOption[] = []; + switch (authParameters.operation) { + default: { + options.push(createSmithyApiNoAuthHttpAuthOption(authParameters)); + } + } + return options; +}; + +/** + * @internal + */ +export interface HttpAuthSchemeInputConfig { + /** + * Configuration of HttpAuthSchemes for a client which provides default identity providers and signers per auth scheme. + * @internal + */ + httpAuthSchemes?: HttpAuthScheme[]; + + /** + * Configuration of an HttpAuthSchemeProvider for a client which resolves which HttpAuthScheme to use. + * @internal + */ + httpAuthSchemeProvider?: RpcV2ProtocolHttpAuthSchemeProvider; +} + +/** + * @internal + */ +export interface HttpAuthSchemeResolvedConfig { + /** + * Configuration of HttpAuthSchemes for a client which provides default identity providers and signers per auth scheme. + * @internal + */ + readonly httpAuthSchemes: HttpAuthScheme[]; + + /** + * Configuration of an HttpAuthSchemeProvider for a client which resolves which HttpAuthScheme to use. + * @internal + */ + readonly httpAuthSchemeProvider: RpcV2ProtocolHttpAuthSchemeProvider; +} + +/** + * @internal + */ +export const resolveHttpAuthSchemeConfig = ( + config: T & HttpAuthSchemeInputConfig +): T & HttpAuthSchemeResolvedConfig => { + return { + ...config, + } as T & HttpAuthSchemeResolvedConfig; +}; diff --git a/private/smithy-rpcv2-cbor/src/commands/EmptyInputOutputCommand.ts b/private/smithy-rpcv2-cbor/src/commands/EmptyInputOutputCommand.ts new file mode 100644 index 00000000000..d9622896e66 --- /dev/null +++ b/private/smithy-rpcv2-cbor/src/commands/EmptyInputOutputCommand.ts @@ -0,0 +1,69 @@ +// smithy-typescript generated code +import { RpcV2ProtocolClientResolvedConfig, ServiceInputTypes, ServiceOutputTypes } from "../RpcV2ProtocolClient"; +import { EmptyStructure } from "../models/models_0"; +import { de_EmptyInputOutputCommand, se_EmptyInputOutputCommand } from "../protocols/Rpcv2cbor"; +import { getSerdePlugin } from "@smithy/middleware-serde"; +import { Command as $Command } from "@smithy/smithy-client"; +import { MetadataBearer as __MetadataBearer } from "@smithy/types"; + +/** + * @public + */ +export type { __MetadataBearer }; +export { $Command }; +/** + * @public + * + * The input for {@link EmptyInputOutputCommand}. + */ +export interface EmptyInputOutputCommandInput extends EmptyStructure {} +/** + * @public + * + * The output of {@link EmptyInputOutputCommand}. + */ +export interface EmptyInputOutputCommandOutput extends EmptyStructure, __MetadataBearer {} + +/** + * @public + * + * @example + * Use a bare-bones client and the command you need to make an API call. + * ```javascript + * import { RpcV2ProtocolClient, EmptyInputOutputCommand } from "@smithy/smithy-rpcv2-cbor"; // ES Modules import + * // const { RpcV2ProtocolClient, EmptyInputOutputCommand } = require("@smithy/smithy-rpcv2-cbor"); // CommonJS import + * const client = new RpcV2ProtocolClient(config); + * const input = {}; + * const command = new EmptyInputOutputCommand(input); + * const response = await client.send(command); + * // {}; + * + * ``` + * + * @param EmptyInputOutputCommandInput - {@link EmptyInputOutputCommandInput} + * @returns {@link EmptyInputOutputCommandOutput} + * @see {@link EmptyInputOutputCommandInput} for command's `input` shape. + * @see {@link EmptyInputOutputCommandOutput} for command's `response` shape. + * @see {@link RpcV2ProtocolClientResolvedConfig | config} for RpcV2ProtocolClient's `config` shape. + * + * @throws {@link RpcV2ProtocolServiceException} + *

Base exception class for all service exceptions from RpcV2Protocol service.

+ * + */ +export class EmptyInputOutputCommand extends $Command + .classBuilder< + EmptyInputOutputCommandInput, + EmptyInputOutputCommandOutput, + RpcV2ProtocolClientResolvedConfig, + ServiceInputTypes, + ServiceOutputTypes + >() + .m(function (this: any, Command: any, cs: any, config: RpcV2ProtocolClientResolvedConfig, o: any) { + return [getSerdePlugin(config, this.serialize, this.deserialize)]; + }) + .s("RpcV2Protocol", "EmptyInputOutput", {}) + .n("RpcV2ProtocolClient", "EmptyInputOutputCommand") + .f(void 0, void 0) + .ser(se_EmptyInputOutputCommand) + .de(de_EmptyInputOutputCommand) + .build() {} diff --git a/private/smithy-rpcv2-cbor/src/commands/Float16Command.ts b/private/smithy-rpcv2-cbor/src/commands/Float16Command.ts new file mode 100644 index 00000000000..cac5bc18e2f --- /dev/null +++ b/private/smithy-rpcv2-cbor/src/commands/Float16Command.ts @@ -0,0 +1,71 @@ +// smithy-typescript generated code +import { RpcV2ProtocolClientResolvedConfig, ServiceInputTypes, ServiceOutputTypes } from "../RpcV2ProtocolClient"; +import { Float16Output } from "../models/models_0"; +import { de_Float16Command, se_Float16Command } from "../protocols/Rpcv2cbor"; +import { getSerdePlugin } from "@smithy/middleware-serde"; +import { Command as $Command } from "@smithy/smithy-client"; +import { MetadataBearer as __MetadataBearer } from "@smithy/types"; + +/** + * @public + */ +export type { __MetadataBearer }; +export { $Command }; +/** + * @public + * + * The input for {@link Float16Command}. + */ +export interface Float16CommandInput {} +/** + * @public + * + * The output of {@link Float16Command}. + */ +export interface Float16CommandOutput extends Float16Output, __MetadataBearer {} + +/** + * @public + * + * @example + * Use a bare-bones client and the command you need to make an API call. + * ```javascript + * import { RpcV2ProtocolClient, Float16Command } from "@smithy/smithy-rpcv2-cbor"; // ES Modules import + * // const { RpcV2ProtocolClient, Float16Command } = require("@smithy/smithy-rpcv2-cbor"); // CommonJS import + * const client = new RpcV2ProtocolClient(config); + * const input = {}; + * const command = new Float16Command(input); + * const response = await client.send(command); + * // { // Float16Output + * // value: Number("double"), + * // }; + * + * ``` + * + * @param Float16CommandInput - {@link Float16CommandInput} + * @returns {@link Float16CommandOutput} + * @see {@link Float16CommandInput} for command's `input` shape. + * @see {@link Float16CommandOutput} for command's `response` shape. + * @see {@link RpcV2ProtocolClientResolvedConfig | config} for RpcV2ProtocolClient's `config` shape. + * + * @throws {@link RpcV2ProtocolServiceException} + *

Base exception class for all service exceptions from RpcV2Protocol service.

+ * + */ +export class Float16Command extends $Command + .classBuilder< + Float16CommandInput, + Float16CommandOutput, + RpcV2ProtocolClientResolvedConfig, + ServiceInputTypes, + ServiceOutputTypes + >() + .m(function (this: any, Command: any, cs: any, config: RpcV2ProtocolClientResolvedConfig, o: any) { + return [getSerdePlugin(config, this.serialize, this.deserialize)]; + }) + .s("RpcV2Protocol", "Float16", {}) + .n("RpcV2ProtocolClient", "Float16Command") + .f(void 0, void 0) + .ser(se_Float16Command) + .de(de_Float16Command) + .build() {} diff --git a/private/smithy-rpcv2-cbor/src/commands/FractionalSecondsCommand.ts b/private/smithy-rpcv2-cbor/src/commands/FractionalSecondsCommand.ts new file mode 100644 index 00000000000..d42743248c8 --- /dev/null +++ b/private/smithy-rpcv2-cbor/src/commands/FractionalSecondsCommand.ts @@ -0,0 +1,71 @@ +// smithy-typescript generated code +import { RpcV2ProtocolClientResolvedConfig, ServiceInputTypes, ServiceOutputTypes } from "../RpcV2ProtocolClient"; +import { FractionalSecondsOutput } from "../models/models_0"; +import { de_FractionalSecondsCommand, se_FractionalSecondsCommand } from "../protocols/Rpcv2cbor"; +import { getSerdePlugin } from "@smithy/middleware-serde"; +import { Command as $Command } from "@smithy/smithy-client"; +import { MetadataBearer as __MetadataBearer } from "@smithy/types"; + +/** + * @public + */ +export type { __MetadataBearer }; +export { $Command }; +/** + * @public + * + * The input for {@link FractionalSecondsCommand}. + */ +export interface FractionalSecondsCommandInput {} +/** + * @public + * + * The output of {@link FractionalSecondsCommand}. + */ +export interface FractionalSecondsCommandOutput extends FractionalSecondsOutput, __MetadataBearer {} + +/** + * @public + * + * @example + * Use a bare-bones client and the command you need to make an API call. + * ```javascript + * import { RpcV2ProtocolClient, FractionalSecondsCommand } from "@smithy/smithy-rpcv2-cbor"; // ES Modules import + * // const { RpcV2ProtocolClient, FractionalSecondsCommand } = require("@smithy/smithy-rpcv2-cbor"); // CommonJS import + * const client = new RpcV2ProtocolClient(config); + * const input = {}; + * const command = new FractionalSecondsCommand(input); + * const response = await client.send(command); + * // { // FractionalSecondsOutput + * // datetime: new Date("TIMESTAMP"), + * // }; + * + * ``` + * + * @param FractionalSecondsCommandInput - {@link FractionalSecondsCommandInput} + * @returns {@link FractionalSecondsCommandOutput} + * @see {@link FractionalSecondsCommandInput} for command's `input` shape. + * @see {@link FractionalSecondsCommandOutput} for command's `response` shape. + * @see {@link RpcV2ProtocolClientResolvedConfig | config} for RpcV2ProtocolClient's `config` shape. + * + * @throws {@link RpcV2ProtocolServiceException} + *

Base exception class for all service exceptions from RpcV2Protocol service.

+ * + */ +export class FractionalSecondsCommand extends $Command + .classBuilder< + FractionalSecondsCommandInput, + FractionalSecondsCommandOutput, + RpcV2ProtocolClientResolvedConfig, + ServiceInputTypes, + ServiceOutputTypes + >() + .m(function (this: any, Command: any, cs: any, config: RpcV2ProtocolClientResolvedConfig, o: any) { + return [getSerdePlugin(config, this.serialize, this.deserialize)]; + }) + .s("RpcV2Protocol", "FractionalSeconds", {}) + .n("RpcV2ProtocolClient", "FractionalSecondsCommand") + .f(void 0, void 0) + .ser(se_FractionalSecondsCommand) + .de(de_FractionalSecondsCommand) + .build() {} diff --git a/private/smithy-rpcv2-cbor/src/commands/GreetingWithErrorsCommand.ts b/private/smithy-rpcv2-cbor/src/commands/GreetingWithErrorsCommand.ts new file mode 100644 index 00000000000..197afa08506 --- /dev/null +++ b/private/smithy-rpcv2-cbor/src/commands/GreetingWithErrorsCommand.ts @@ -0,0 +1,84 @@ +// smithy-typescript generated code +import { RpcV2ProtocolClientResolvedConfig, ServiceInputTypes, ServiceOutputTypes } from "../RpcV2ProtocolClient"; +import { GreetingWithErrorsOutput } from "../models/models_0"; +import { de_GreetingWithErrorsCommand, se_GreetingWithErrorsCommand } from "../protocols/Rpcv2cbor"; +import { getSerdePlugin } from "@smithy/middleware-serde"; +import { Command as $Command } from "@smithy/smithy-client"; +import { MetadataBearer as __MetadataBearer } from "@smithy/types"; + +/** + * @public + */ +export type { __MetadataBearer }; +export { $Command }; +/** + * @public + * + * The input for {@link GreetingWithErrorsCommand}. + */ +export interface GreetingWithErrorsCommandInput {} +/** + * @public + * + * The output of {@link GreetingWithErrorsCommand}. + */ +export interface GreetingWithErrorsCommandOutput extends GreetingWithErrorsOutput, __MetadataBearer {} + +/** + * This operation has three possible return values: + * + * 1. A successful response in the form of GreetingWithErrorsOutput + * 2. An InvalidGreeting error. + * 3. A ComplexError error. + * + * Implementations must be able to successfully take a response and + * properly deserialize successful and error responses. + * @example + * Use a bare-bones client and the command you need to make an API call. + * ```javascript + * import { RpcV2ProtocolClient, GreetingWithErrorsCommand } from "@smithy/smithy-rpcv2-cbor"; // ES Modules import + * // const { RpcV2ProtocolClient, GreetingWithErrorsCommand } = require("@smithy/smithy-rpcv2-cbor"); // CommonJS import + * const client = new RpcV2ProtocolClient(config); + * const input = {}; + * const command = new GreetingWithErrorsCommand(input); + * const response = await client.send(command); + * // { // GreetingWithErrorsOutput + * // greeting: "STRING_VALUE", + * // }; + * + * ``` + * + * @param GreetingWithErrorsCommandInput - {@link GreetingWithErrorsCommandInput} + * @returns {@link GreetingWithErrorsCommandOutput} + * @see {@link GreetingWithErrorsCommandInput} for command's `input` shape. + * @see {@link GreetingWithErrorsCommandOutput} for command's `response` shape. + * @see {@link RpcV2ProtocolClientResolvedConfig | config} for RpcV2ProtocolClient's `config` shape. + * + * @throws {@link InvalidGreeting} (client fault) + * This error is thrown when an invalid greeting value is provided. + * + * @throws {@link ComplexError} (client fault) + * This error is thrown when a request is invalid. + * + * @throws {@link RpcV2ProtocolServiceException} + *

Base exception class for all service exceptions from RpcV2Protocol service.

+ * + * @public + */ +export class GreetingWithErrorsCommand extends $Command + .classBuilder< + GreetingWithErrorsCommandInput, + GreetingWithErrorsCommandOutput, + RpcV2ProtocolClientResolvedConfig, + ServiceInputTypes, + ServiceOutputTypes + >() + .m(function (this: any, Command: any, cs: any, config: RpcV2ProtocolClientResolvedConfig, o: any) { + return [getSerdePlugin(config, this.serialize, this.deserialize)]; + }) + .s("RpcV2Protocol", "GreetingWithErrors", {}) + .n("RpcV2ProtocolClient", "GreetingWithErrorsCommand") + .f(void 0, void 0) + .ser(se_GreetingWithErrorsCommand) + .de(de_GreetingWithErrorsCommand) + .build() {} diff --git a/private/smithy-rpcv2-cbor/src/commands/NoInputOutputCommand.ts b/private/smithy-rpcv2-cbor/src/commands/NoInputOutputCommand.ts new file mode 100644 index 00000000000..c53819bb184 --- /dev/null +++ b/private/smithy-rpcv2-cbor/src/commands/NoInputOutputCommand.ts @@ -0,0 +1,68 @@ +// smithy-typescript generated code +import { RpcV2ProtocolClientResolvedConfig, ServiceInputTypes, ServiceOutputTypes } from "../RpcV2ProtocolClient"; +import { de_NoInputOutputCommand, se_NoInputOutputCommand } from "../protocols/Rpcv2cbor"; +import { getSerdePlugin } from "@smithy/middleware-serde"; +import { Command as $Command } from "@smithy/smithy-client"; +import { MetadataBearer as __MetadataBearer } from "@smithy/types"; + +/** + * @public + */ +export type { __MetadataBearer }; +export { $Command }; +/** + * @public + * + * The input for {@link NoInputOutputCommand}. + */ +export interface NoInputOutputCommandInput {} +/** + * @public + * + * The output of {@link NoInputOutputCommand}. + */ +export interface NoInputOutputCommandOutput extends __MetadataBearer {} + +/** + * @public + * + * @example + * Use a bare-bones client and the command you need to make an API call. + * ```javascript + * import { RpcV2ProtocolClient, NoInputOutputCommand } from "@smithy/smithy-rpcv2-cbor"; // ES Modules import + * // const { RpcV2ProtocolClient, NoInputOutputCommand } = require("@smithy/smithy-rpcv2-cbor"); // CommonJS import + * const client = new RpcV2ProtocolClient(config); + * const input = {}; + * const command = new NoInputOutputCommand(input); + * const response = await client.send(command); + * // {}; + * + * ``` + * + * @param NoInputOutputCommandInput - {@link NoInputOutputCommandInput} + * @returns {@link NoInputOutputCommandOutput} + * @see {@link NoInputOutputCommandInput} for command's `input` shape. + * @see {@link NoInputOutputCommandOutput} for command's `response` shape. + * @see {@link RpcV2ProtocolClientResolvedConfig | config} for RpcV2ProtocolClient's `config` shape. + * + * @throws {@link RpcV2ProtocolServiceException} + *

Base exception class for all service exceptions from RpcV2Protocol service.

+ * + */ +export class NoInputOutputCommand extends $Command + .classBuilder< + NoInputOutputCommandInput, + NoInputOutputCommandOutput, + RpcV2ProtocolClientResolvedConfig, + ServiceInputTypes, + ServiceOutputTypes + >() + .m(function (this: any, Command: any, cs: any, config: RpcV2ProtocolClientResolvedConfig, o: any) { + return [getSerdePlugin(config, this.serialize, this.deserialize)]; + }) + .s("RpcV2Protocol", "NoInputOutput", {}) + .n("RpcV2ProtocolClient", "NoInputOutputCommand") + .f(void 0, void 0) + .ser(se_NoInputOutputCommand) + .de(de_NoInputOutputCommand) + .build() {} diff --git a/private/smithy-rpcv2-cbor/src/commands/OperationWithDefaultsCommand.ts b/private/smithy-rpcv2-cbor/src/commands/OperationWithDefaultsCommand.ts new file mode 100644 index 00000000000..67593059323 --- /dev/null +++ b/private/smithy-rpcv2-cbor/src/commands/OperationWithDefaultsCommand.ts @@ -0,0 +1,137 @@ +// smithy-typescript generated code +import { RpcV2ProtocolClientResolvedConfig, ServiceInputTypes, ServiceOutputTypes } from "../RpcV2ProtocolClient"; +import { OperationWithDefaultsInput, OperationWithDefaultsOutput } from "../models/models_0"; +import { de_OperationWithDefaultsCommand, se_OperationWithDefaultsCommand } from "../protocols/Rpcv2cbor"; +import { getSerdePlugin } from "@smithy/middleware-serde"; +import { Command as $Command } from "@smithy/smithy-client"; +import { MetadataBearer as __MetadataBearer } from "@smithy/types"; + +/** + * @public + */ +export type { __MetadataBearer }; +export { $Command }; +/** + * @public + * + * The input for {@link OperationWithDefaultsCommand}. + */ +export interface OperationWithDefaultsCommandInput extends OperationWithDefaultsInput {} +/** + * @public + * + * The output of {@link OperationWithDefaultsCommand}. + */ +export interface OperationWithDefaultsCommandOutput extends OperationWithDefaultsOutput, __MetadataBearer {} + +/** + * @public + * + * @example + * Use a bare-bones client and the command you need to make an API call. + * ```javascript + * import { RpcV2ProtocolClient, OperationWithDefaultsCommand } from "@smithy/smithy-rpcv2-cbor"; // ES Modules import + * // const { RpcV2ProtocolClient, OperationWithDefaultsCommand } = require("@smithy/smithy-rpcv2-cbor"); // CommonJS import + * const client = new RpcV2ProtocolClient(config); + * const input = { // OperationWithDefaultsInput + * defaults: { // Defaults + * defaultString: "STRING_VALUE", + * defaultBoolean: true || false, + * defaultList: [ // TestStringList + * "STRING_VALUE", + * ], + * defaultTimestamp: new Date("TIMESTAMP"), + * defaultBlob: new Uint8Array(), // e.g. Buffer.from("") or new TextEncoder().encode("") + * defaultByte: 0, // BYTE_VALUE + * defaultShort: Number("short"), + * defaultInteger: Number("int"), + * defaultLong: Number("long"), + * defaultFloat: Number("float"), + * defaultDouble: Number("double"), + * defaultMap: { // TestStringMap + * "": "STRING_VALUE", + * }, + * defaultEnum: "FOO" || "BAR" || "BAZ", + * defaultIntEnum: 1 || 2, + * emptyString: "STRING_VALUE", + * falseBoolean: true || false, + * emptyBlob: new Uint8Array(), // e.g. Buffer.from("") or new TextEncoder().encode("") + * zeroByte: 0, // BYTE_VALUE + * zeroShort: Number("short"), + * zeroInteger: Number("int"), + * zeroLong: Number("long"), + * zeroFloat: Number("float"), + * zeroDouble: Number("double"), + * }, + * clientOptionalDefaults: { // ClientOptionalDefaults + * member: Number("int"), + * }, + * topLevelDefault: "STRING_VALUE", + * otherTopLevelDefault: Number("int"), + * }; + * const command = new OperationWithDefaultsCommand(input); + * const response = await client.send(command); + * // { // OperationWithDefaultsOutput + * // defaultString: "STRING_VALUE", + * // defaultBoolean: true || false, + * // defaultList: [ // TestStringList + * // "STRING_VALUE", + * // ], + * // defaultTimestamp: new Date("TIMESTAMP"), + * // defaultBlob: new Uint8Array(), + * // defaultByte: 0, // BYTE_VALUE + * // defaultShort: Number("short"), + * // defaultInteger: Number("int"), + * // defaultLong: Number("long"), + * // defaultFloat: Number("float"), + * // defaultDouble: Number("double"), + * // defaultMap: { // TestStringMap + * // "": "STRING_VALUE", + * // }, + * // defaultEnum: "FOO" || "BAR" || "BAZ", + * // defaultIntEnum: 1 || 2, + * // emptyString: "STRING_VALUE", + * // falseBoolean: true || false, + * // emptyBlob: new Uint8Array(), + * // zeroByte: 0, // BYTE_VALUE + * // zeroShort: Number("short"), + * // zeroInteger: Number("int"), + * // zeroLong: Number("long"), + * // zeroFloat: Number("float"), + * // zeroDouble: Number("double"), + * // }; + * + * ``` + * + * @param OperationWithDefaultsCommandInput - {@link OperationWithDefaultsCommandInput} + * @returns {@link OperationWithDefaultsCommandOutput} + * @see {@link OperationWithDefaultsCommandInput} for command's `input` shape. + * @see {@link OperationWithDefaultsCommandOutput} for command's `response` shape. + * @see {@link RpcV2ProtocolClientResolvedConfig | config} for RpcV2ProtocolClient's `config` shape. + * + * @throws {@link ValidationException} (client fault) + * A standard error for input validation failures. + * This should be thrown by services when a member of the input structure + * falls outside of the modeled or documented constraints. + * + * @throws {@link RpcV2ProtocolServiceException} + *

Base exception class for all service exceptions from RpcV2Protocol service.

+ * + */ +export class OperationWithDefaultsCommand extends $Command + .classBuilder< + OperationWithDefaultsCommandInput, + OperationWithDefaultsCommandOutput, + RpcV2ProtocolClientResolvedConfig, + ServiceInputTypes, + ServiceOutputTypes + >() + .m(function (this: any, Command: any, cs: any, config: RpcV2ProtocolClientResolvedConfig, o: any) { + return [getSerdePlugin(config, this.serialize, this.deserialize)]; + }) + .s("RpcV2Protocol", "OperationWithDefaults", {}) + .n("RpcV2ProtocolClient", "OperationWithDefaultsCommand") + .f(void 0, void 0) + .ser(se_OperationWithDefaultsCommand) + .de(de_OperationWithDefaultsCommand) + .build() {} diff --git a/private/smithy-rpcv2-cbor/src/commands/OptionalInputOutputCommand.ts b/private/smithy-rpcv2-cbor/src/commands/OptionalInputOutputCommand.ts new file mode 100644 index 00000000000..82c4e18697d --- /dev/null +++ b/private/smithy-rpcv2-cbor/src/commands/OptionalInputOutputCommand.ts @@ -0,0 +1,73 @@ +// smithy-typescript generated code +import { RpcV2ProtocolClientResolvedConfig, ServiceInputTypes, ServiceOutputTypes } from "../RpcV2ProtocolClient"; +import { SimpleStructure } from "../models/models_0"; +import { de_OptionalInputOutputCommand, se_OptionalInputOutputCommand } from "../protocols/Rpcv2cbor"; +import { getSerdePlugin } from "@smithy/middleware-serde"; +import { Command as $Command } from "@smithy/smithy-client"; +import { MetadataBearer as __MetadataBearer } from "@smithy/types"; + +/** + * @public + */ +export type { __MetadataBearer }; +export { $Command }; +/** + * @public + * + * The input for {@link OptionalInputOutputCommand}. + */ +export interface OptionalInputOutputCommandInput extends SimpleStructure {} +/** + * @public + * + * The output of {@link OptionalInputOutputCommand}. + */ +export interface OptionalInputOutputCommandOutput extends SimpleStructure, __MetadataBearer {} + +/** + * @public + * + * @example + * Use a bare-bones client and the command you need to make an API call. + * ```javascript + * import { RpcV2ProtocolClient, OptionalInputOutputCommand } from "@smithy/smithy-rpcv2-cbor"; // ES Modules import + * // const { RpcV2ProtocolClient, OptionalInputOutputCommand } = require("@smithy/smithy-rpcv2-cbor"); // CommonJS import + * const client = new RpcV2ProtocolClient(config); + * const input = { // SimpleStructure + * value: "STRING_VALUE", + * }; + * const command = new OptionalInputOutputCommand(input); + * const response = await client.send(command); + * // { // SimpleStructure + * // value: "STRING_VALUE", + * // }; + * + * ``` + * + * @param OptionalInputOutputCommandInput - {@link OptionalInputOutputCommandInput} + * @returns {@link OptionalInputOutputCommandOutput} + * @see {@link OptionalInputOutputCommandInput} for command's `input` shape. + * @see {@link OptionalInputOutputCommandOutput} for command's `response` shape. + * @see {@link RpcV2ProtocolClientResolvedConfig | config} for RpcV2ProtocolClient's `config` shape. + * + * @throws {@link RpcV2ProtocolServiceException} + *

Base exception class for all service exceptions from RpcV2Protocol service.

+ * + */ +export class OptionalInputOutputCommand extends $Command + .classBuilder< + OptionalInputOutputCommandInput, + OptionalInputOutputCommandOutput, + RpcV2ProtocolClientResolvedConfig, + ServiceInputTypes, + ServiceOutputTypes + >() + .m(function (this: any, Command: any, cs: any, config: RpcV2ProtocolClientResolvedConfig, o: any) { + return [getSerdePlugin(config, this.serialize, this.deserialize)]; + }) + .s("RpcV2Protocol", "OptionalInputOutput", {}) + .n("RpcV2ProtocolClient", "OptionalInputOutputCommand") + .f(void 0, void 0) + .ser(se_OptionalInputOutputCommand) + .de(de_OptionalInputOutputCommand) + .build() {} diff --git a/private/smithy-rpcv2-cbor/src/commands/RecursiveShapesCommand.ts b/private/smithy-rpcv2-cbor/src/commands/RecursiveShapesCommand.ts new file mode 100644 index 00000000000..884d3bc4ba0 --- /dev/null +++ b/private/smithy-rpcv2-cbor/src/commands/RecursiveShapesCommand.ts @@ -0,0 +1,97 @@ +// smithy-typescript generated code +import { RpcV2ProtocolClientResolvedConfig, ServiceInputTypes, ServiceOutputTypes } from "../RpcV2ProtocolClient"; +import { RecursiveShapesInputOutput } from "../models/models_0"; +import { de_RecursiveShapesCommand, se_RecursiveShapesCommand } from "../protocols/Rpcv2cbor"; +import { getSerdePlugin } from "@smithy/middleware-serde"; +import { Command as $Command } from "@smithy/smithy-client"; +import { MetadataBearer as __MetadataBearer } from "@smithy/types"; + +/** + * @public + */ +export type { __MetadataBearer }; +export { $Command }; +/** + * @public + * + * The input for {@link RecursiveShapesCommand}. + */ +export interface RecursiveShapesCommandInput extends RecursiveShapesInputOutput {} +/** + * @public + * + * The output of {@link RecursiveShapesCommand}. + */ +export interface RecursiveShapesCommandOutput extends RecursiveShapesInputOutput, __MetadataBearer {} + +/** + * @public + * + * @example + * Use a bare-bones client and the command you need to make an API call. + * ```javascript + * import { RpcV2ProtocolClient, RecursiveShapesCommand } from "@smithy/smithy-rpcv2-cbor"; // ES Modules import + * // const { RpcV2ProtocolClient, RecursiveShapesCommand } = require("@smithy/smithy-rpcv2-cbor"); // CommonJS import + * const client = new RpcV2ProtocolClient(config); + * const input = { // RecursiveShapesInputOutput + * nested: { // RecursiveShapesInputOutputNested1 + * foo: "STRING_VALUE", + * nested: { // RecursiveShapesInputOutputNested2 + * bar: "STRING_VALUE", + * recursiveMember: { + * foo: "STRING_VALUE", + * nested: { + * bar: "STRING_VALUE", + * recursiveMember: "", + * }, + * }, + * }, + * }, + * }; + * const command = new RecursiveShapesCommand(input); + * const response = await client.send(command); + * // { // RecursiveShapesInputOutput + * // nested: { // RecursiveShapesInputOutputNested1 + * // foo: "STRING_VALUE", + * // nested: { // RecursiveShapesInputOutputNested2 + * // bar: "STRING_VALUE", + * // recursiveMember: { + * // foo: "STRING_VALUE", + * // nested: { + * // bar: "STRING_VALUE", + * // recursiveMember: "", + * // }, + * // }, + * // }, + * // }, + * // }; + * + * ``` + * + * @param RecursiveShapesCommandInput - {@link RecursiveShapesCommandInput} + * @returns {@link RecursiveShapesCommandOutput} + * @see {@link RecursiveShapesCommandInput} for command's `input` shape. + * @see {@link RecursiveShapesCommandOutput} for command's `response` shape. + * @see {@link RpcV2ProtocolClientResolvedConfig | config} for RpcV2ProtocolClient's `config` shape. + * + * @throws {@link RpcV2ProtocolServiceException} + *

Base exception class for all service exceptions from RpcV2Protocol service.

+ * + */ +export class RecursiveShapesCommand extends $Command + .classBuilder< + RecursiveShapesCommandInput, + RecursiveShapesCommandOutput, + RpcV2ProtocolClientResolvedConfig, + ServiceInputTypes, + ServiceOutputTypes + >() + .m(function (this: any, Command: any, cs: any, config: RpcV2ProtocolClientResolvedConfig, o: any) { + return [getSerdePlugin(config, this.serialize, this.deserialize)]; + }) + .s("RpcV2Protocol", "RecursiveShapes", {}) + .n("RpcV2ProtocolClient", "RecursiveShapesCommand") + .f(void 0, void 0) + .ser(se_RecursiveShapesCommand) + .de(de_RecursiveShapesCommand) + .build() {} diff --git a/private/smithy-rpcv2-cbor/src/commands/RpcV2CborDenseMapsCommand.ts b/private/smithy-rpcv2-cbor/src/commands/RpcV2CborDenseMapsCommand.ts new file mode 100644 index 00000000000..19cfff77558 --- /dev/null +++ b/private/smithy-rpcv2-cbor/src/commands/RpcV2CborDenseMapsCommand.ts @@ -0,0 +1,114 @@ +// smithy-typescript generated code +import { RpcV2ProtocolClientResolvedConfig, ServiceInputTypes, ServiceOutputTypes } from "../RpcV2ProtocolClient"; +import { RpcV2CborDenseMapsInputOutput } from "../models/models_0"; +import { de_RpcV2CborDenseMapsCommand, se_RpcV2CborDenseMapsCommand } from "../protocols/Rpcv2cbor"; +import { getSerdePlugin } from "@smithy/middleware-serde"; +import { Command as $Command } from "@smithy/smithy-client"; +import { MetadataBearer as __MetadataBearer } from "@smithy/types"; + +/** + * @public + */ +export type { __MetadataBearer }; +export { $Command }; +/** + * @public + * + * The input for {@link RpcV2CborDenseMapsCommand}. + */ +export interface RpcV2CborDenseMapsCommandInput extends RpcV2CborDenseMapsInputOutput {} +/** + * @public + * + * The output of {@link RpcV2CborDenseMapsCommand}. + */ +export interface RpcV2CborDenseMapsCommandOutput extends RpcV2CborDenseMapsInputOutput, __MetadataBearer {} + +/** + * The example tests basic map serialization. + * @example + * Use a bare-bones client and the command you need to make an API call. + * ```javascript + * import { RpcV2ProtocolClient, RpcV2CborDenseMapsCommand } from "@smithy/smithy-rpcv2-cbor"; // ES Modules import + * // const { RpcV2ProtocolClient, RpcV2CborDenseMapsCommand } = require("@smithy/smithy-rpcv2-cbor"); // CommonJS import + * const client = new RpcV2ProtocolClient(config); + * const input = { // RpcV2CborDenseMapsInputOutput + * denseStructMap: { // DenseStructMap + * "": { // GreetingStruct + * hi: "STRING_VALUE", + * }, + * }, + * denseNumberMap: { // DenseNumberMap + * "": Number("int"), + * }, + * denseBooleanMap: { // DenseBooleanMap + * "": true || false, + * }, + * denseStringMap: { // DenseStringMap + * "": "STRING_VALUE", + * }, + * denseSetMap: { // DenseSetMap + * "": [ // StringSet + * "STRING_VALUE", + * ], + * }, + * }; + * const command = new RpcV2CborDenseMapsCommand(input); + * const response = await client.send(command); + * // { // RpcV2CborDenseMapsInputOutput + * // denseStructMap: { // DenseStructMap + * // "": { // GreetingStruct + * // hi: "STRING_VALUE", + * // }, + * // }, + * // denseNumberMap: { // DenseNumberMap + * // "": Number("int"), + * // }, + * // denseBooleanMap: { // DenseBooleanMap + * // "": true || false, + * // }, + * // denseStringMap: { // DenseStringMap + * // "": "STRING_VALUE", + * // }, + * // denseSetMap: { // DenseSetMap + * // "": [ // StringSet + * // "STRING_VALUE", + * // ], + * // }, + * // }; + * + * ``` + * + * @param RpcV2CborDenseMapsCommandInput - {@link RpcV2CborDenseMapsCommandInput} + * @returns {@link RpcV2CborDenseMapsCommandOutput} + * @see {@link RpcV2CborDenseMapsCommandInput} for command's `input` shape. + * @see {@link RpcV2CborDenseMapsCommandOutput} for command's `response` shape. + * @see {@link RpcV2ProtocolClientResolvedConfig | config} for RpcV2ProtocolClient's `config` shape. + * + * @throws {@link ValidationException} (client fault) + * A standard error for input validation failures. + * This should be thrown by services when a member of the input structure + * falls outside of the modeled or documented constraints. + * + * @throws {@link RpcV2ProtocolServiceException} + *

Base exception class for all service exceptions from RpcV2Protocol service.

+ * + * @public + */ +export class RpcV2CborDenseMapsCommand extends $Command + .classBuilder< + RpcV2CborDenseMapsCommandInput, + RpcV2CborDenseMapsCommandOutput, + RpcV2ProtocolClientResolvedConfig, + ServiceInputTypes, + ServiceOutputTypes + >() + .m(function (this: any, Command: any, cs: any, config: RpcV2ProtocolClientResolvedConfig, o: any) { + return [getSerdePlugin(config, this.serialize, this.deserialize)]; + }) + .s("RpcV2Protocol", "RpcV2CborDenseMaps", {}) + .n("RpcV2ProtocolClient", "RpcV2CborDenseMapsCommand") + .f(void 0, void 0) + .ser(se_RpcV2CborDenseMapsCommand) + .de(de_RpcV2CborDenseMapsCommand) + .build() {} diff --git a/private/smithy-rpcv2-cbor/src/commands/RpcV2CborListsCommand.ts b/private/smithy-rpcv2-cbor/src/commands/RpcV2CborListsCommand.ts new file mode 100644 index 00000000000..9d68c23e779 --- /dev/null +++ b/private/smithy-rpcv2-cbor/src/commands/RpcV2CborListsCommand.ts @@ -0,0 +1,152 @@ +// smithy-typescript generated code +import { RpcV2ProtocolClientResolvedConfig, ServiceInputTypes, ServiceOutputTypes } from "../RpcV2ProtocolClient"; +import { RpcV2CborListInputOutput } from "../models/models_0"; +import { de_RpcV2CborListsCommand, se_RpcV2CborListsCommand } from "../protocols/Rpcv2cbor"; +import { getSerdePlugin } from "@smithy/middleware-serde"; +import { Command as $Command } from "@smithy/smithy-client"; +import { MetadataBearer as __MetadataBearer } from "@smithy/types"; + +/** + * @public + */ +export type { __MetadataBearer }; +export { $Command }; +/** + * @public + * + * The input for {@link RpcV2CborListsCommand}. + */ +export interface RpcV2CborListsCommandInput extends RpcV2CborListInputOutput {} +/** + * @public + * + * The output of {@link RpcV2CborListsCommand}. + */ +export interface RpcV2CborListsCommandOutput extends RpcV2CborListInputOutput, __MetadataBearer {} + +/** + * This test case serializes JSON lists for the following cases for both + * input and output: + * + * 1. Normal lists. + * 2. Normal sets. + * 3. Lists of lists. + * 4. Lists of structures. + * @example + * Use a bare-bones client and the command you need to make an API call. + * ```javascript + * import { RpcV2ProtocolClient, RpcV2CborListsCommand } from "@smithy/smithy-rpcv2-cbor"; // ES Modules import + * // const { RpcV2ProtocolClient, RpcV2CborListsCommand } = require("@smithy/smithy-rpcv2-cbor"); // CommonJS import + * const client = new RpcV2ProtocolClient(config); + * const input = { // RpcV2CborListInputOutput + * stringList: [ // StringList + * "STRING_VALUE", + * ], + * stringSet: [ // StringSet + * "STRING_VALUE", + * ], + * integerList: [ // IntegerList + * Number("int"), + * ], + * booleanList: [ // BooleanList + * true || false, + * ], + * timestampList: [ // TimestampList + * new Date("TIMESTAMP"), + * ], + * enumList: [ // FooEnumList + * "Foo" || "Baz" || "Bar" || "1" || "0", + * ], + * intEnumList: [ // IntegerEnumList + * 1 || 2 || 3, + * ], + * nestedStringList: [ // NestedStringList + * [ + * "STRING_VALUE", + * ], + * ], + * structureList: [ // StructureList + * { // StructureListMember + * a: "STRING_VALUE", + * b: "STRING_VALUE", + * }, + * ], + * blobList: [ // BlobList + * new Uint8Array(), // e.g. Buffer.from("") or new TextEncoder().encode("") + * ], + * }; + * const command = new RpcV2CborListsCommand(input); + * const response = await client.send(command); + * // { // RpcV2CborListInputOutput + * // stringList: [ // StringList + * // "STRING_VALUE", + * // ], + * // stringSet: [ // StringSet + * // "STRING_VALUE", + * // ], + * // integerList: [ // IntegerList + * // Number("int"), + * // ], + * // booleanList: [ // BooleanList + * // true || false, + * // ], + * // timestampList: [ // TimestampList + * // new Date("TIMESTAMP"), + * // ], + * // enumList: [ // FooEnumList + * // "Foo" || "Baz" || "Bar" || "1" || "0", + * // ], + * // intEnumList: [ // IntegerEnumList + * // 1 || 2 || 3, + * // ], + * // nestedStringList: [ // NestedStringList + * // [ + * // "STRING_VALUE", + * // ], + * // ], + * // structureList: [ // StructureList + * // { // StructureListMember + * // a: "STRING_VALUE", + * // b: "STRING_VALUE", + * // }, + * // ], + * // blobList: [ // BlobList + * // new Uint8Array(), + * // ], + * // }; + * + * ``` + * + * @param RpcV2CborListsCommandInput - {@link RpcV2CborListsCommandInput} + * @returns {@link RpcV2CborListsCommandOutput} + * @see {@link RpcV2CborListsCommandInput} for command's `input` shape. + * @see {@link RpcV2CborListsCommandOutput} for command's `response` shape. + * @see {@link RpcV2ProtocolClientResolvedConfig | config} for RpcV2ProtocolClient's `config` shape. + * + * @throws {@link ValidationException} (client fault) + * A standard error for input validation failures. + * This should be thrown by services when a member of the input structure + * falls outside of the modeled or documented constraints. + * + * @throws {@link RpcV2ProtocolServiceException} + *

Base exception class for all service exceptions from RpcV2Protocol service.

+ * + * @public + */ +export class RpcV2CborListsCommand extends $Command + .classBuilder< + RpcV2CborListsCommandInput, + RpcV2CborListsCommandOutput, + RpcV2ProtocolClientResolvedConfig, + ServiceInputTypes, + ServiceOutputTypes + >() + .m(function (this: any, Command: any, cs: any, config: RpcV2ProtocolClientResolvedConfig, o: any) { + return [getSerdePlugin(config, this.serialize, this.deserialize)]; + }) + .s("RpcV2Protocol", "RpcV2CborLists", {}) + .n("RpcV2ProtocolClient", "RpcV2CborListsCommand") + .f(void 0, void 0) + .ser(se_RpcV2CborListsCommand) + .de(de_RpcV2CborListsCommand) + .build() {} diff --git a/private/smithy-rpcv2-cbor/src/commands/RpcV2CborSparseMapsCommand.ts b/private/smithy-rpcv2-cbor/src/commands/RpcV2CborSparseMapsCommand.ts new file mode 100644 index 00000000000..01562866fbd --- /dev/null +++ b/private/smithy-rpcv2-cbor/src/commands/RpcV2CborSparseMapsCommand.ts @@ -0,0 +1,114 @@ +// smithy-typescript generated code +import { RpcV2ProtocolClientResolvedConfig, ServiceInputTypes, ServiceOutputTypes } from "../RpcV2ProtocolClient"; +import { RpcV2CborSparseMapsInputOutput } from "../models/models_0"; +import { de_RpcV2CborSparseMapsCommand, se_RpcV2CborSparseMapsCommand } from "../protocols/Rpcv2cbor"; +import { getSerdePlugin } from "@smithy/middleware-serde"; +import { Command as $Command } from "@smithy/smithy-client"; +import { MetadataBearer as __MetadataBearer } from "@smithy/types"; + +/** + * @public + */ +export type { __MetadataBearer }; +export { $Command }; +/** + * @public + * + * The input for {@link RpcV2CborSparseMapsCommand}. + */ +export interface RpcV2CborSparseMapsCommandInput extends RpcV2CborSparseMapsInputOutput {} +/** + * @public + * + * The output of {@link RpcV2CborSparseMapsCommand}. + */ +export interface RpcV2CborSparseMapsCommandOutput extends RpcV2CborSparseMapsInputOutput, __MetadataBearer {} + +/** + * @public + * + * @example + * Use a bare-bones client and the command you need to make an API call. + * ```javascript + * import { RpcV2ProtocolClient, RpcV2CborSparseMapsCommand } from "@smithy/smithy-rpcv2-cbor"; // ES Modules import + * // const { RpcV2ProtocolClient, RpcV2CborSparseMapsCommand } = require("@smithy/smithy-rpcv2-cbor"); // CommonJS import + * const client = new RpcV2ProtocolClient(config); + * const input = { // RpcV2CborSparseMapsInputOutput + * sparseStructMap: { // SparseStructMap + * "": { // GreetingStruct + * hi: "STRING_VALUE", + * }, + * }, + * sparseNumberMap: { // SparseNumberMap + * "": Number("int"), + * }, + * sparseBooleanMap: { // SparseBooleanMap + * "": true || false, + * }, + * sparseStringMap: { // SparseStringMap + * "": "STRING_VALUE", + * }, + * sparseSetMap: { // SparseSetMap + * "": [ // StringSet + * "STRING_VALUE", + * ], + * }, + * }; + * const command = new RpcV2CborSparseMapsCommand(input); + * const response = await client.send(command); + * // { // RpcV2CborSparseMapsInputOutput + * // sparseStructMap: { // SparseStructMap + * // "": { // GreetingStruct + * // hi: "STRING_VALUE", + * // }, + * // }, + * // sparseNumberMap: { // SparseNumberMap + * // "": Number("int"), + * // }, + * // sparseBooleanMap: { // SparseBooleanMap + * // "": true || false, + * // }, + * // sparseStringMap: { // SparseStringMap + * // "": "STRING_VALUE", + * // }, + * // sparseSetMap: { // SparseSetMap + * // "": [ // StringSet + * // "STRING_VALUE", + * // ], + * // }, + * // }; + * + * ``` + * + * @param RpcV2CborSparseMapsCommandInput - {@link RpcV2CborSparseMapsCommandInput} + * @returns {@link RpcV2CborSparseMapsCommandOutput} + * @see {@link RpcV2CborSparseMapsCommandInput} for command's `input` shape. + * @see {@link RpcV2CborSparseMapsCommandOutput} for command's `response` shape. + * @see {@link RpcV2ProtocolClientResolvedConfig | config} for RpcV2ProtocolClient's `config` shape. + * + * @throws {@link ValidationException} (client fault) + * A standard error for input validation failures. + * This should be thrown by services when a member of the input structure + * falls outside of the modeled or documented constraints. + * + * @throws {@link RpcV2ProtocolServiceException} + *

Base exception class for all service exceptions from RpcV2Protocol service.

+ * + */ +export class RpcV2CborSparseMapsCommand extends $Command + .classBuilder< + RpcV2CborSparseMapsCommandInput, + RpcV2CborSparseMapsCommandOutput, + RpcV2ProtocolClientResolvedConfig, + ServiceInputTypes, + ServiceOutputTypes + >() + .m(function (this: any, Command: any, cs: any, config: RpcV2ProtocolClientResolvedConfig, o: any) { + return [getSerdePlugin(config, this.serialize, this.deserialize)]; + }) + .s("RpcV2Protocol", "RpcV2CborSparseMaps", {}) + .n("RpcV2ProtocolClient", "RpcV2CborSparseMapsCommand") + .f(void 0, void 0) + .ser(se_RpcV2CborSparseMapsCommand) + .de(de_RpcV2CborSparseMapsCommand) + .build() {} diff --git a/private/smithy-rpcv2-cbor/src/commands/SimpleScalarPropertiesCommand.ts b/private/smithy-rpcv2-cbor/src/commands/SimpleScalarPropertiesCommand.ts new file mode 100644 index 00000000000..51128e55e15 --- /dev/null +++ b/private/smithy-rpcv2-cbor/src/commands/SimpleScalarPropertiesCommand.ts @@ -0,0 +1,91 @@ +// smithy-typescript generated code +import { RpcV2ProtocolClientResolvedConfig, ServiceInputTypes, ServiceOutputTypes } from "../RpcV2ProtocolClient"; +import { SimpleScalarStructure } from "../models/models_0"; +import { de_SimpleScalarPropertiesCommand, se_SimpleScalarPropertiesCommand } from "../protocols/Rpcv2cbor"; +import { getSerdePlugin } from "@smithy/middleware-serde"; +import { Command as $Command } from "@smithy/smithy-client"; +import { MetadataBearer as __MetadataBearer } from "@smithy/types"; + +/** + * @public + */ +export type { __MetadataBearer }; +export { $Command }; +/** + * @public + * + * The input for {@link SimpleScalarPropertiesCommand}. + */ +export interface SimpleScalarPropertiesCommandInput extends SimpleScalarStructure {} +/** + * @public + * + * The output of {@link SimpleScalarPropertiesCommand}. + */ +export interface SimpleScalarPropertiesCommandOutput extends SimpleScalarStructure, __MetadataBearer {} + +/** + * @public + * + * @example + * Use a bare-bones client and the command you need to make an API call. + * ```javascript + * import { RpcV2ProtocolClient, SimpleScalarPropertiesCommand } from "@smithy/smithy-rpcv2-cbor"; // ES Modules import + * // const { RpcV2ProtocolClient, SimpleScalarPropertiesCommand } = require("@smithy/smithy-rpcv2-cbor"); // CommonJS import + * const client = new RpcV2ProtocolClient(config); + * const input = { // SimpleScalarStructure + * trueBooleanValue: true || false, + * falseBooleanValue: true || false, + * byteValue: 0, // BYTE_VALUE + * doubleValue: Number("double"), + * floatValue: Number("float"), + * integerValue: Number("int"), + * longValue: Number("long"), + * shortValue: Number("short"), + * stringValue: "STRING_VALUE", + * blobValue: new Uint8Array(), // e.g. Buffer.from("") or new TextEncoder().encode("") + * }; + * const command = new SimpleScalarPropertiesCommand(input); + * const response = await client.send(command); + * // { // SimpleScalarStructure + * // trueBooleanValue: true || false, + * // falseBooleanValue: true || false, + * // byteValue: 0, // BYTE_VALUE + * // doubleValue: Number("double"), + * // floatValue: Number("float"), + * // integerValue: Number("int"), + * // longValue: Number("long"), + * // shortValue: Number("short"), + * // stringValue: "STRING_VALUE", + * // blobValue: new Uint8Array(), + * // }; + * + * ``` + * + * @param SimpleScalarPropertiesCommandInput - {@link SimpleScalarPropertiesCommandInput} + * @returns {@link SimpleScalarPropertiesCommandOutput} + * @see {@link SimpleScalarPropertiesCommandInput} for command's `input` shape. + * @see {@link SimpleScalarPropertiesCommandOutput} for command's `response` shape. + * @see {@link RpcV2ProtocolClientResolvedConfig | config} for RpcV2ProtocolClient's `config` shape. + * + * @throws {@link RpcV2ProtocolServiceException} + *

Base exception class for all service exceptions from RpcV2Protocol service.

+ * + */ +export class SimpleScalarPropertiesCommand extends $Command + .classBuilder< + SimpleScalarPropertiesCommandInput, + SimpleScalarPropertiesCommandOutput, + RpcV2ProtocolClientResolvedConfig, + ServiceInputTypes, + ServiceOutputTypes + >() + .m(function (this: any, Command: any, cs: any, config: RpcV2ProtocolClientResolvedConfig, o: any) { + return [getSerdePlugin(config, this.serialize, this.deserialize)]; + }) + .s("RpcV2Protocol", "SimpleScalarProperties", {}) + .n("RpcV2ProtocolClient", "SimpleScalarPropertiesCommand") + .f(void 0, void 0) + .ser(se_SimpleScalarPropertiesCommand) + .de(de_SimpleScalarPropertiesCommand) + .build() {} diff --git a/private/smithy-rpcv2-cbor/src/commands/SparseNullsOperationCommand.ts b/private/smithy-rpcv2-cbor/src/commands/SparseNullsOperationCommand.ts new file mode 100644 index 00000000000..a7f466513a0 --- /dev/null +++ b/private/smithy-rpcv2-cbor/src/commands/SparseNullsOperationCommand.ts @@ -0,0 +1,83 @@ +// smithy-typescript generated code +import { RpcV2ProtocolClientResolvedConfig, ServiceInputTypes, ServiceOutputTypes } from "../RpcV2ProtocolClient"; +import { SparseNullsOperationInputOutput } from "../models/models_0"; +import { de_SparseNullsOperationCommand, se_SparseNullsOperationCommand } from "../protocols/Rpcv2cbor"; +import { getSerdePlugin } from "@smithy/middleware-serde"; +import { Command as $Command } from "@smithy/smithy-client"; +import { MetadataBearer as __MetadataBearer } from "@smithy/types"; + +/** + * @public + */ +export type { __MetadataBearer }; +export { $Command }; +/** + * @public + * + * The input for {@link SparseNullsOperationCommand}. + */ +export interface SparseNullsOperationCommandInput extends SparseNullsOperationInputOutput {} +/** + * @public + * + * The output of {@link SparseNullsOperationCommand}. + */ +export interface SparseNullsOperationCommandOutput extends SparseNullsOperationInputOutput, __MetadataBearer {} + +/** + * @public + * + * @example + * Use a bare-bones client and the command you need to make an API call. + * ```javascript + * import { RpcV2ProtocolClient, SparseNullsOperationCommand } from "@smithy/smithy-rpcv2-cbor"; // ES Modules import + * // const { RpcV2ProtocolClient, SparseNullsOperationCommand } = require("@smithy/smithy-rpcv2-cbor"); // CommonJS import + * const client = new RpcV2ProtocolClient(config); + * const input = { // SparseNullsOperationInputOutput + * sparseStringList: [ // SparseStringList + * "STRING_VALUE", + * ], + * sparseStringMap: { // SparseStringMap + * "": "STRING_VALUE", + * }, + * }; + * const command = new SparseNullsOperationCommand(input); + * const response = await client.send(command); + * // { // SparseNullsOperationInputOutput + * // sparseStringList: [ // SparseStringList + * // "STRING_VALUE", + * // ], + * // sparseStringMap: { // SparseStringMap + * // "": "STRING_VALUE", + * // }, + * // }; + * + * ``` + * + * @param SparseNullsOperationCommandInput - {@link SparseNullsOperationCommandInput} + * @returns {@link SparseNullsOperationCommandOutput} + * @see {@link SparseNullsOperationCommandInput} for command's `input` shape. + * @see {@link SparseNullsOperationCommandOutput} for command's `response` shape. + * @see {@link RpcV2ProtocolClientResolvedConfig | config} for RpcV2ProtocolClient's `config` shape. + * + * @throws {@link RpcV2ProtocolServiceException} + *

Base exception class for all service exceptions from RpcV2Protocol service.

+ * + */ +export class SparseNullsOperationCommand extends $Command + .classBuilder< + SparseNullsOperationCommandInput, + SparseNullsOperationCommandOutput, + RpcV2ProtocolClientResolvedConfig, + ServiceInputTypes, + ServiceOutputTypes + >() + .m(function (this: any, Command: any, cs: any, config: RpcV2ProtocolClientResolvedConfig, o: any) { + return [getSerdePlugin(config, this.serialize, this.deserialize)]; + }) + .s("RpcV2Protocol", "SparseNullsOperation", {}) + .n("RpcV2ProtocolClient", "SparseNullsOperationCommand") + .f(void 0, void 0) + .ser(se_SparseNullsOperationCommand) + .de(de_SparseNullsOperationCommand) + .build() {} diff --git a/private/smithy-rpcv2-cbor/src/commands/index.ts b/private/smithy-rpcv2-cbor/src/commands/index.ts new file mode 100644 index 00000000000..d9a8bbfef70 --- /dev/null +++ b/private/smithy-rpcv2-cbor/src/commands/index.ts @@ -0,0 +1,14 @@ +// smithy-typescript generated code +export * from "./EmptyInputOutputCommand"; +export * from "./Float16Command"; +export * from "./FractionalSecondsCommand"; +export * from "./GreetingWithErrorsCommand"; +export * from "./NoInputOutputCommand"; +export * from "./OperationWithDefaultsCommand"; +export * from "./OptionalInputOutputCommand"; +export * from "./RecursiveShapesCommand"; +export * from "./RpcV2CborDenseMapsCommand"; +export * from "./RpcV2CborListsCommand"; +export * from "./RpcV2CborSparseMapsCommand"; +export * from "./SimpleScalarPropertiesCommand"; +export * from "./SparseNullsOperationCommand"; diff --git a/private/smithy-rpcv2-cbor/src/extensionConfiguration.ts b/private/smithy-rpcv2-cbor/src/extensionConfiguration.ts new file mode 100644 index 00000000000..7fa5c7ff1fc --- /dev/null +++ b/private/smithy-rpcv2-cbor/src/extensionConfiguration.ts @@ -0,0 +1,12 @@ +// smithy-typescript generated code +import { HttpAuthExtensionConfiguration } from "./auth/httpAuthExtensionConfiguration"; +import { HttpHandlerExtensionConfiguration } from "@smithy/protocol-http"; +import { DefaultExtensionConfiguration } from "@smithy/types"; + +/** + * @internal + */ +export interface RpcV2ProtocolExtensionConfiguration + extends HttpHandlerExtensionConfiguration, + DefaultExtensionConfiguration, + HttpAuthExtensionConfiguration {} diff --git a/private/smithy-rpcv2-cbor/src/index.ts b/private/smithy-rpcv2-cbor/src/index.ts new file mode 100644 index 00000000000..9603ea77597 --- /dev/null +++ b/private/smithy-rpcv2-cbor/src/index.ts @@ -0,0 +1,10 @@ +// smithy-typescript generated code +/* eslint-disable */ +export * from "./RpcV2ProtocolClient"; +export * from "./RpcV2Protocol"; +export type { RuntimeExtension } from "./runtimeExtensions"; +export type { RpcV2ProtocolExtensionConfiguration } from "./extensionConfiguration"; +export * from "./commands"; +export * from "./models"; + +export { RpcV2ProtocolServiceException } from "./models/RpcV2ProtocolServiceException"; diff --git a/private/smithy-rpcv2-cbor/src/models/RpcV2ProtocolServiceException.ts b/private/smithy-rpcv2-cbor/src/models/RpcV2ProtocolServiceException.ts new file mode 100644 index 00000000000..792f7ba0caf --- /dev/null +++ b/private/smithy-rpcv2-cbor/src/models/RpcV2ProtocolServiceException.ts @@ -0,0 +1,24 @@ +// smithy-typescript generated code +import { + ServiceException as __ServiceException, + ServiceExceptionOptions as __ServiceExceptionOptions, +} from "@smithy/smithy-client"; + +export type { __ServiceExceptionOptions }; + +export { __ServiceException }; + +/** + * @public + * + * Base exception class for all service exceptions from RpcV2Protocol service. + */ +export class RpcV2ProtocolServiceException extends __ServiceException { + /** + * @internal + */ + constructor(options: __ServiceExceptionOptions) { + super(options); + Object.setPrototypeOf(this, RpcV2ProtocolServiceException.prototype); + } +} diff --git a/private/smithy-rpcv2-cbor/src/models/index.ts b/private/smithy-rpcv2-cbor/src/models/index.ts new file mode 100644 index 00000000000..9eaceb12865 --- /dev/null +++ b/private/smithy-rpcv2-cbor/src/models/index.ts @@ -0,0 +1,2 @@ +// smithy-typescript generated code +export * from "./models_0"; diff --git a/private/smithy-rpcv2-cbor/src/models/models_0.ts b/private/smithy-rpcv2-cbor/src/models/models_0.ts new file mode 100644 index 00000000000..f2ffaf796fd --- /dev/null +++ b/private/smithy-rpcv2-cbor/src/models/models_0.ts @@ -0,0 +1,358 @@ +// smithy-typescript generated code +import { RpcV2ProtocolServiceException as __BaseException } from "./RpcV2ProtocolServiceException"; +import { ExceptionOptionType as __ExceptionOptionType } from "@smithy/smithy-client"; + +/** + * Describes one specific validation failure for an input member. + * @public + */ +export interface ValidationExceptionField { + /** + * A JSONPointer expression to the structure member whose value failed to satisfy the modeled constraints. + * @public + */ + path: string | undefined; + + /** + * A detailed description of the validation failure. + * @public + */ + message: string | undefined; +} + +/** + * A standard error for input validation failures. + * This should be thrown by services when a member of the input structure + * falls outside of the modeled or documented constraints. + * @public + */ +export class ValidationException extends __BaseException { + readonly name: "ValidationException" = "ValidationException"; + readonly $fault: "client" = "client"; + /** + * A list of specific failures encountered while validating the input. + * A member can appear in this list more than once if it failed to satisfy multiple constraints. + * @public + */ + fieldList?: ValidationExceptionField[]; + + /** + * @internal + */ + constructor(opts: __ExceptionOptionType) { + super({ + name: "ValidationException", + $fault: "client", + ...opts, + }); + Object.setPrototypeOf(this, ValidationException.prototype); + this.fieldList = opts.fieldList; + } +} + +/** + * @public + */ +export interface ClientOptionalDefaults { + member?: number; +} + +/** + * @public + */ +export interface ComplexNestedErrorData { + Foo?: string; +} + +/** + * This error is thrown when a request is invalid. + * @public + */ +export class ComplexError extends __BaseException { + readonly name: "ComplexError" = "ComplexError"; + readonly $fault: "client" = "client"; + TopLevel?: string; + Nested?: ComplexNestedErrorData; + /** + * @internal + */ + constructor(opts: __ExceptionOptionType) { + super({ + name: "ComplexError", + $fault: "client", + ...opts, + }); + Object.setPrototypeOf(this, ComplexError.prototype); + this.TopLevel = opts.TopLevel; + this.Nested = opts.Nested; + } +} + +/** + * @public + * @enum + */ +export const TestEnum = { + BAR: "BAR", + BAZ: "BAZ", + FOO: "FOO", +} as const; +/** + * @public + */ +export type TestEnum = (typeof TestEnum)[keyof typeof TestEnum]; + +export enum TestIntEnum { + ONE = 1, + TWO = 2, +} + +/** + * @public + */ +export interface Defaults { + defaultString?: string; + defaultBoolean?: boolean; + defaultList?: string[]; + defaultTimestamp?: Date; + defaultBlob?: Uint8Array; + defaultByte?: number; + defaultShort?: number; + defaultInteger?: number; + defaultLong?: number; + defaultFloat?: number; + defaultDouble?: number; + defaultMap?: Record; + defaultEnum?: TestEnum; + defaultIntEnum?: TestIntEnum; + emptyString?: string; + falseBoolean?: boolean; + emptyBlob?: Uint8Array; + zeroByte?: number; + zeroShort?: number; + zeroInteger?: number; + zeroLong?: number; + zeroFloat?: number; + zeroDouble?: number; +} + +/** + * @public + */ +export interface GreetingStruct { + hi?: string; +} + +/** + * @public + */ +export interface EmptyStructure {} + +/** + * @public + */ +export interface Float16Output { + value?: number; +} + +/** + * @public + */ +export interface FractionalSecondsOutput { + datetime?: Date; +} + +/** + * @public + */ +export interface GreetingWithErrorsOutput { + greeting?: string; +} + +/** + * This error is thrown when an invalid greeting value is provided. + * @public + */ +export class InvalidGreeting extends __BaseException { + readonly name: "InvalidGreeting" = "InvalidGreeting"; + readonly $fault: "client" = "client"; + Message?: string; + /** + * @internal + */ + constructor(opts: __ExceptionOptionType) { + super({ + name: "InvalidGreeting", + $fault: "client", + ...opts, + }); + Object.setPrototypeOf(this, InvalidGreeting.prototype); + this.Message = opts.Message; + } +} + +/** + * @public + */ +export interface OperationWithDefaultsInput { + defaults?: Defaults; + clientOptionalDefaults?: ClientOptionalDefaults; + topLevelDefault?: string; + otherTopLevelDefault?: number; +} + +/** + * @public + */ +export interface OperationWithDefaultsOutput { + defaultString?: string; + defaultBoolean?: boolean; + defaultList?: string[]; + defaultTimestamp?: Date; + defaultBlob?: Uint8Array; + defaultByte?: number; + defaultShort?: number; + defaultInteger?: number; + defaultLong?: number; + defaultFloat?: number; + defaultDouble?: number; + defaultMap?: Record; + defaultEnum?: TestEnum; + defaultIntEnum?: TestIntEnum; + emptyString?: string; + falseBoolean?: boolean; + emptyBlob?: Uint8Array; + zeroByte?: number; + zeroShort?: number; + zeroInteger?: number; + zeroLong?: number; + zeroFloat?: number; + zeroDouble?: number; +} + +/** + * @public + */ +export interface SimpleStructure { + value?: string; +} + +/** + * @public + */ +export interface RpcV2CborDenseMapsInputOutput { + denseStructMap?: Record; + denseNumberMap?: Record; + denseBooleanMap?: Record; + denseStringMap?: Record; + denseSetMap?: Record; +} + +/** + * @public + * @enum + */ +export const FooEnum = { + BAR: "Bar", + BAZ: "Baz", + FOO: "Foo", + ONE: "1", + ZERO: "0", +} as const; +/** + * @public + */ +export type FooEnum = (typeof FooEnum)[keyof typeof FooEnum]; + +export enum IntegerEnum { + A = 1, + B = 2, + C = 3, +} + +/** + * @public + */ +export interface StructureListMember { + a?: string; + b?: string; +} + +/** + * @public + */ +export interface RpcV2CborListInputOutput { + stringList?: string[]; + stringSet?: string[]; + integerList?: number[]; + booleanList?: boolean[]; + timestampList?: Date[]; + enumList?: FooEnum[]; + intEnumList?: IntegerEnum[]; + /** + * A list of lists of strings. + * @public + */ + nestedStringList?: string[][]; + + structureList?: StructureListMember[]; + blobList?: Uint8Array[]; +} + +/** + * @public + */ +export interface RpcV2CborSparseMapsInputOutput { + sparseStructMap?: Record; + sparseNumberMap?: Record; + sparseBooleanMap?: Record; + sparseStringMap?: Record; + sparseSetMap?: Record; +} + +/** + * @public + */ +export interface SimpleScalarStructure { + trueBooleanValue?: boolean; + falseBooleanValue?: boolean; + byteValue?: number; + doubleValue?: number; + floatValue?: number; + integerValue?: number; + longValue?: number; + shortValue?: number; + stringValue?: string; + blobValue?: Uint8Array; +} + +/** + * @public + */ +export interface SparseNullsOperationInputOutput { + sparseStringList?: string[]; + sparseStringMap?: Record; +} + +/** + * @public + */ +export interface RecursiveShapesInputOutputNested1 { + foo?: string; + nested?: RecursiveShapesInputOutputNested2; +} + +/** + * @public + */ +export interface RecursiveShapesInputOutputNested2 { + bar?: string; + recursiveMember?: RecursiveShapesInputOutputNested1; +} + +/** + * @public + */ +export interface RecursiveShapesInputOutput { + nested?: RecursiveShapesInputOutputNested1; +} diff --git a/private/smithy-rpcv2-cbor/src/protocols/Rpcv2cbor.ts b/private/smithy-rpcv2-cbor/src/protocols/Rpcv2cbor.ts new file mode 100644 index 00000000000..c9bc4e334f8 --- /dev/null +++ b/private/smithy-rpcv2-cbor/src/protocols/Rpcv2cbor.ts @@ -0,0 +1,1246 @@ +// smithy-typescript generated code +import { EmptyInputOutputCommandInput, EmptyInputOutputCommandOutput } from "../commands/EmptyInputOutputCommand"; +import { Float16CommandInput, Float16CommandOutput } from "../commands/Float16Command"; +import { FractionalSecondsCommandInput, FractionalSecondsCommandOutput } from "../commands/FractionalSecondsCommand"; +import { GreetingWithErrorsCommandInput, GreetingWithErrorsCommandOutput } from "../commands/GreetingWithErrorsCommand"; +import { NoInputOutputCommandInput, NoInputOutputCommandOutput } from "../commands/NoInputOutputCommand"; +import { + OperationWithDefaultsCommandInput, + OperationWithDefaultsCommandOutput, +} from "../commands/OperationWithDefaultsCommand"; +import { + OptionalInputOutputCommandInput, + OptionalInputOutputCommandOutput, +} from "../commands/OptionalInputOutputCommand"; +import { RecursiveShapesCommandInput, RecursiveShapesCommandOutput } from "../commands/RecursiveShapesCommand"; +import { RpcV2CborDenseMapsCommandInput, RpcV2CborDenseMapsCommandOutput } from "../commands/RpcV2CborDenseMapsCommand"; +import { RpcV2CborListsCommandInput, RpcV2CborListsCommandOutput } from "../commands/RpcV2CborListsCommand"; +import { + RpcV2CborSparseMapsCommandInput, + RpcV2CborSparseMapsCommandOutput, +} from "../commands/RpcV2CborSparseMapsCommand"; +import { + SimpleScalarPropertiesCommandInput, + SimpleScalarPropertiesCommandOutput, +} from "../commands/SimpleScalarPropertiesCommand"; +import { + SparseNullsOperationCommandInput, + SparseNullsOperationCommandOutput, +} from "../commands/SparseNullsOperationCommand"; +import { RpcV2ProtocolServiceException as __BaseException } from "../models/RpcV2ProtocolServiceException"; +import { + ClientOptionalDefaults, + ComplexError, + Defaults, + EmptyStructure, + Float16Output, + FooEnum, + FractionalSecondsOutput, + GreetingStruct, + IntegerEnum, + InvalidGreeting, + OperationWithDefaultsInput, + OperationWithDefaultsOutput, + RecursiveShapesInputOutput, + RecursiveShapesInputOutputNested1, + RecursiveShapesInputOutputNested2, + RpcV2CborDenseMapsInputOutput, + RpcV2CborListInputOutput, + RpcV2CborSparseMapsInputOutput, + SimpleScalarStructure, + SimpleStructure, + SparseNullsOperationInputOutput, + StructureListMember, + ValidationException, +} from "../models/models_0"; +import { + dateToTag as __dateToTag, + buildHttpRpcRequest, + cbor, + checkCborResponse as cr, + loadSmithyRpcV2CborErrorCode, + parseCborBody as parseBody, + parseCborErrorBody as parseErrorBody, +} from "@smithy/core/cbor"; +import { HttpRequest as __HttpRequest, HttpResponse as __HttpResponse } from "@smithy/protocol-http"; +import { + decorateServiceException as __decorateServiceException, + expectBoolean as __expectBoolean, + expectByte as __expectByte, + expectInt32 as __expectInt32, + expectLong as __expectLong, + expectNonNull as __expectNonNull, + expectShort as __expectShort, + expectString as __expectString, + limitedParseDouble as __limitedParseDouble, + limitedParseFloat32 as __limitedParseFloat32, + parseEpochTimestamp as __parseEpochTimestamp, + _json, + collectBody, + take, + withBaseException, +} from "@smithy/smithy-client"; +import { + Endpoint as __Endpoint, + HeaderBag as __HeaderBag, + ResponseMetadata as __ResponseMetadata, + SerdeContext as __SerdeContext, +} from "@smithy/types"; + +/** + * serializeRpcv2cborEmptyInputOutputCommand + */ +export const se_EmptyInputOutputCommand = async ( + input: EmptyInputOutputCommandInput, + context: __SerdeContext +): Promise<__HttpRequest> => { + const headers: __HeaderBag = SHARED_HEADERS; + let body: any; + body = cbor.serialize(_json(input)); + return buildHttpRpcRequest(context, headers, "/service/RpcV2Protocol/operation/EmptyInputOutput", undefined, body); +}; + +/** + * serializeRpcv2cborFloat16Command + */ +export const se_Float16Command = async ( + input: Float16CommandInput, + context: __SerdeContext +): Promise<__HttpRequest> => { + const headers: __HeaderBag = { ...SHARED_HEADERS }; + delete headers["content-type"]; + + return buildHttpRpcRequest(context, headers, "/service/RpcV2Protocol/operation/Float16", undefined, undefined); +}; + +/** + * serializeRpcv2cborFractionalSecondsCommand + */ +export const se_FractionalSecondsCommand = async ( + input: FractionalSecondsCommandInput, + context: __SerdeContext +): Promise<__HttpRequest> => { + const headers: __HeaderBag = { ...SHARED_HEADERS }; + delete headers["content-type"]; + + return buildHttpRpcRequest( + context, + headers, + "/service/RpcV2Protocol/operation/FractionalSeconds", + undefined, + undefined + ); +}; + +/** + * serializeRpcv2cborGreetingWithErrorsCommand + */ +export const se_GreetingWithErrorsCommand = async ( + input: GreetingWithErrorsCommandInput, + context: __SerdeContext +): Promise<__HttpRequest> => { + const headers: __HeaderBag = { ...SHARED_HEADERS }; + delete headers["content-type"]; + + return buildHttpRpcRequest( + context, + headers, + "/service/RpcV2Protocol/operation/GreetingWithErrors", + undefined, + undefined + ); +}; + +/** + * serializeRpcv2cborNoInputOutputCommand + */ +export const se_NoInputOutputCommand = async ( + input: NoInputOutputCommandInput, + context: __SerdeContext +): Promise<__HttpRequest> => { + const headers: __HeaderBag = { ...SHARED_HEADERS }; + delete headers["content-type"]; + + return buildHttpRpcRequest(context, headers, "/service/RpcV2Protocol/operation/NoInputOutput", undefined, undefined); +}; + +/** + * serializeRpcv2cborOperationWithDefaultsCommand + */ +export const se_OperationWithDefaultsCommand = async ( + input: OperationWithDefaultsCommandInput, + context: __SerdeContext +): Promise<__HttpRequest> => { + const headers: __HeaderBag = SHARED_HEADERS; + let body: any; + body = cbor.serialize(se_OperationWithDefaultsInput(input, context)); + return buildHttpRpcRequest( + context, + headers, + "/service/RpcV2Protocol/operation/OperationWithDefaults", + undefined, + body + ); +}; + +/** + * serializeRpcv2cborOptionalInputOutputCommand + */ +export const se_OptionalInputOutputCommand = async ( + input: OptionalInputOutputCommandInput, + context: __SerdeContext +): Promise<__HttpRequest> => { + const headers: __HeaderBag = SHARED_HEADERS; + let body: any; + body = cbor.serialize(_json(input)); + return buildHttpRpcRequest(context, headers, "/service/RpcV2Protocol/operation/OptionalInputOutput", undefined, body); +}; + +/** + * serializeRpcv2cborRecursiveShapesCommand + */ +export const se_RecursiveShapesCommand = async ( + input: RecursiveShapesCommandInput, + context: __SerdeContext +): Promise<__HttpRequest> => { + const headers: __HeaderBag = SHARED_HEADERS; + let body: any; + body = cbor.serialize(se_RecursiveShapesInputOutput(input, context)); + return buildHttpRpcRequest(context, headers, "/service/RpcV2Protocol/operation/RecursiveShapes", undefined, body); +}; + +/** + * serializeRpcv2cborRpcV2CborDenseMapsCommand + */ +export const se_RpcV2CborDenseMapsCommand = async ( + input: RpcV2CborDenseMapsCommandInput, + context: __SerdeContext +): Promise<__HttpRequest> => { + const headers: __HeaderBag = SHARED_HEADERS; + let body: any; + body = cbor.serialize(_json(input)); + return buildHttpRpcRequest(context, headers, "/service/RpcV2Protocol/operation/RpcV2CborDenseMaps", undefined, body); +}; + +/** + * serializeRpcv2cborRpcV2CborListsCommand + */ +export const se_RpcV2CborListsCommand = async ( + input: RpcV2CborListsCommandInput, + context: __SerdeContext +): Promise<__HttpRequest> => { + const headers: __HeaderBag = SHARED_HEADERS; + let body: any; + body = cbor.serialize(se_RpcV2CborListInputOutput(input, context)); + return buildHttpRpcRequest(context, headers, "/service/RpcV2Protocol/operation/RpcV2CborLists", undefined, body); +}; + +/** + * serializeRpcv2cborRpcV2CborSparseMapsCommand + */ +export const se_RpcV2CborSparseMapsCommand = async ( + input: RpcV2CborSparseMapsCommandInput, + context: __SerdeContext +): Promise<__HttpRequest> => { + const headers: __HeaderBag = SHARED_HEADERS; + let body: any; + body = cbor.serialize(se_RpcV2CborSparseMapsInputOutput(input, context)); + return buildHttpRpcRequest(context, headers, "/service/RpcV2Protocol/operation/RpcV2CborSparseMaps", undefined, body); +}; + +/** + * serializeRpcv2cborSimpleScalarPropertiesCommand + */ +export const se_SimpleScalarPropertiesCommand = async ( + input: SimpleScalarPropertiesCommandInput, + context: __SerdeContext +): Promise<__HttpRequest> => { + const headers: __HeaderBag = SHARED_HEADERS; + let body: any; + body = cbor.serialize(se_SimpleScalarStructure(input, context)); + return buildHttpRpcRequest( + context, + headers, + "/service/RpcV2Protocol/operation/SimpleScalarProperties", + undefined, + body + ); +}; + +/** + * serializeRpcv2cborSparseNullsOperationCommand + */ +export const se_SparseNullsOperationCommand = async ( + input: SparseNullsOperationCommandInput, + context: __SerdeContext +): Promise<__HttpRequest> => { + const headers: __HeaderBag = SHARED_HEADERS; + let body: any; + body = cbor.serialize(se_SparseNullsOperationInputOutput(input, context)); + return buildHttpRpcRequest( + context, + headers, + "/service/RpcV2Protocol/operation/SparseNullsOperation", + undefined, + body + ); +}; + +/** + * deserializeRpcv2cborEmptyInputOutputCommand + */ +export const de_EmptyInputOutputCommand = async ( + output: __HttpResponse, + context: __SerdeContext +): Promise => { + cr(output); + if (output.statusCode >= 300) { + return de_CommandError(output, context); + } + + const data: any = await parseBody(output.body, context); + let contents: any = {}; + contents = _json(data); + const response: EmptyInputOutputCommandOutput = { + $metadata: deserializeMetadata(output), + ...contents, + }; + return response; +}; + +/** + * deserializeRpcv2cborFloat16Command + */ +export const de_Float16Command = async ( + output: __HttpResponse, + context: __SerdeContext +): Promise => { + cr(output); + if (output.statusCode >= 300) { + return de_CommandError(output, context); + } + + const data: any = await parseBody(output.body, context); + let contents: any = {}; + contents = de_Float16Output(data, context); + const response: Float16CommandOutput = { + $metadata: deserializeMetadata(output), + ...contents, + }; + return response; +}; + +/** + * deserializeRpcv2cborFractionalSecondsCommand + */ +export const de_FractionalSecondsCommand = async ( + output: __HttpResponse, + context: __SerdeContext +): Promise => { + cr(output); + if (output.statusCode >= 300) { + return de_CommandError(output, context); + } + + const data: any = await parseBody(output.body, context); + let contents: any = {}; + contents = de_FractionalSecondsOutput(data, context); + const response: FractionalSecondsCommandOutput = { + $metadata: deserializeMetadata(output), + ...contents, + }; + return response; +}; + +/** + * deserializeRpcv2cborGreetingWithErrorsCommand + */ +export const de_GreetingWithErrorsCommand = async ( + output: __HttpResponse, + context: __SerdeContext +): Promise => { + cr(output); + if (output.statusCode >= 300) { + return de_CommandError(output, context); + } + + const data: any = await parseBody(output.body, context); + let contents: any = {}; + contents = _json(data); + const response: GreetingWithErrorsCommandOutput = { + $metadata: deserializeMetadata(output), + ...contents, + }; + return response; +}; + +/** + * deserializeRpcv2cborNoInputOutputCommand + */ +export const de_NoInputOutputCommand = async ( + output: __HttpResponse, + context: __SerdeContext +): Promise => { + cr(output); + if (output.statusCode >= 300) { + return de_CommandError(output, context); + } + + await collectBody(output.body, context); + const response: NoInputOutputCommandOutput = { + $metadata: deserializeMetadata(output), + }; + return response; +}; + +/** + * deserializeRpcv2cborOperationWithDefaultsCommand + */ +export const de_OperationWithDefaultsCommand = async ( + output: __HttpResponse, + context: __SerdeContext +): Promise => { + cr(output); + if (output.statusCode >= 300) { + return de_CommandError(output, context); + } + + const data: any = await parseBody(output.body, context); + let contents: any = {}; + contents = de_OperationWithDefaultsOutput(data, context); + const response: OperationWithDefaultsCommandOutput = { + $metadata: deserializeMetadata(output), + ...contents, + }; + return response; +}; + +/** + * deserializeRpcv2cborOptionalInputOutputCommand + */ +export const de_OptionalInputOutputCommand = async ( + output: __HttpResponse, + context: __SerdeContext +): Promise => { + cr(output); + if (output.statusCode >= 300) { + return de_CommandError(output, context); + } + + const data: any = await parseBody(output.body, context); + let contents: any = {}; + contents = _json(data); + const response: OptionalInputOutputCommandOutput = { + $metadata: deserializeMetadata(output), + ...contents, + }; + return response; +}; + +/** + * deserializeRpcv2cborRecursiveShapesCommand + */ +export const de_RecursiveShapesCommand = async ( + output: __HttpResponse, + context: __SerdeContext +): Promise => { + cr(output); + if (output.statusCode >= 300) { + return de_CommandError(output, context); + } + + const data: any = await parseBody(output.body, context); + let contents: any = {}; + contents = de_RecursiveShapesInputOutput(data, context); + const response: RecursiveShapesCommandOutput = { + $metadata: deserializeMetadata(output), + ...contents, + }; + return response; +}; + +/** + * deserializeRpcv2cborRpcV2CborDenseMapsCommand + */ +export const de_RpcV2CborDenseMapsCommand = async ( + output: __HttpResponse, + context: __SerdeContext +): Promise => { + cr(output); + if (output.statusCode >= 300) { + return de_CommandError(output, context); + } + + const data: any = await parseBody(output.body, context); + let contents: any = {}; + contents = _json(data); + const response: RpcV2CborDenseMapsCommandOutput = { + $metadata: deserializeMetadata(output), + ...contents, + }; + return response; +}; + +/** + * deserializeRpcv2cborRpcV2CborListsCommand + */ +export const de_RpcV2CborListsCommand = async ( + output: __HttpResponse, + context: __SerdeContext +): Promise => { + cr(output); + if (output.statusCode >= 300) { + return de_CommandError(output, context); + } + + const data: any = await parseBody(output.body, context); + let contents: any = {}; + contents = de_RpcV2CborListInputOutput(data, context); + const response: RpcV2CborListsCommandOutput = { + $metadata: deserializeMetadata(output), + ...contents, + }; + return response; +}; + +/** + * deserializeRpcv2cborRpcV2CborSparseMapsCommand + */ +export const de_RpcV2CborSparseMapsCommand = async ( + output: __HttpResponse, + context: __SerdeContext +): Promise => { + cr(output); + if (output.statusCode >= 300) { + return de_CommandError(output, context); + } + + const data: any = await parseBody(output.body, context); + let contents: any = {}; + contents = de_RpcV2CborSparseMapsInputOutput(data, context); + const response: RpcV2CborSparseMapsCommandOutput = { + $metadata: deserializeMetadata(output), + ...contents, + }; + return response; +}; + +/** + * deserializeRpcv2cborSimpleScalarPropertiesCommand + */ +export const de_SimpleScalarPropertiesCommand = async ( + output: __HttpResponse, + context: __SerdeContext +): Promise => { + cr(output); + if (output.statusCode >= 300) { + return de_CommandError(output, context); + } + + const data: any = await parseBody(output.body, context); + let contents: any = {}; + contents = de_SimpleScalarStructure(data, context); + const response: SimpleScalarPropertiesCommandOutput = { + $metadata: deserializeMetadata(output), + ...contents, + }; + return response; +}; + +/** + * deserializeRpcv2cborSparseNullsOperationCommand + */ +export const de_SparseNullsOperationCommand = async ( + output: __HttpResponse, + context: __SerdeContext +): Promise => { + cr(output); + if (output.statusCode >= 300) { + return de_CommandError(output, context); + } + + const data: any = await parseBody(output.body, context); + let contents: any = {}; + contents = de_SparseNullsOperationInputOutput(data, context); + const response: SparseNullsOperationCommandOutput = { + $metadata: deserializeMetadata(output), + ...contents, + }; + return response; +}; + +/** + * deserialize_Rpcv2cborCommandError + */ +const de_CommandError = async (output: __HttpResponse, context: __SerdeContext): Promise => { + const parsedOutput: any = { + ...output, + body: await parseErrorBody(output.body, context), + }; + const errorCode = loadSmithyRpcV2CborErrorCode(output, parsedOutput.body); + switch (errorCode) { + case "ComplexError": + case "smithy.protocoltests.rpcv2Cbor#ComplexError": + throw await de_ComplexErrorRes(parsedOutput, context); + case "InvalidGreeting": + case "smithy.protocoltests.rpcv2Cbor#InvalidGreeting": + throw await de_InvalidGreetingRes(parsedOutput, context); + case "ValidationException": + case "smithy.framework#ValidationException": + throw await de_ValidationExceptionRes(parsedOutput, context); + default: + const parsedBody = parsedOutput.body; + return throwDefaultError({ + output, + parsedBody, + errorCode, + }) as never; + } +}; + +/** + * deserializeRpcv2cborValidationExceptionRes + */ +const de_ValidationExceptionRes = async (parsedOutput: any, context: __SerdeContext): Promise => { + const body = parsedOutput.body; + const deserialized: any = _json(body); + const exception = new ValidationException({ + $metadata: deserializeMetadata(parsedOutput), + ...deserialized, + }); + return __decorateServiceException(exception, body); +}; + +/** + * deserializeRpcv2cborComplexErrorRes + */ +const de_ComplexErrorRes = async (parsedOutput: any, context: __SerdeContext): Promise => { + const body = parsedOutput.body; + const deserialized: any = _json(body); + const exception = new ComplexError({ + $metadata: deserializeMetadata(parsedOutput), + ...deserialized, + }); + return __decorateServiceException(exception, body); +}; + +/** + * deserializeRpcv2cborInvalidGreetingRes + */ +const de_InvalidGreetingRes = async (parsedOutput: any, context: __SerdeContext): Promise => { + const body = parsedOutput.body; + const deserialized: any = _json(body); + const exception = new InvalidGreeting({ + $metadata: deserializeMetadata(parsedOutput), + ...deserialized, + }); + return __decorateServiceException(exception, body); +}; + +// se_ClientOptionalDefaults omitted. + +/** + * serializeRpcv2cborDefaults + */ +const se_Defaults = (input: Defaults, context: __SerdeContext): any => { + return take(input, { + defaultBlob: [], + defaultBoolean: [], + defaultByte: [], + defaultDouble: [], + defaultEnum: [], + defaultFloat: [], + defaultIntEnum: [], + defaultInteger: [], + defaultList: _json, + defaultLong: [], + defaultMap: _json, + defaultShort: [], + defaultString: [], + defaultTimestamp: __dateToTag, + emptyBlob: [], + emptyString: [], + falseBoolean: [], + zeroByte: [], + zeroDouble: [], + zeroFloat: [], + zeroInteger: [], + zeroLong: [], + zeroShort: [], + }); +}; + +// se_DenseBooleanMap omitted. + +// se_DenseNumberMap omitted. + +// se_DenseSetMap omitted. + +// se_DenseStringMap omitted. + +// se_DenseStructMap omitted. + +// se_EmptyStructure omitted. + +/** + * serializeRpcv2cborOperationWithDefaultsInput + */ +const se_OperationWithDefaultsInput = (input: OperationWithDefaultsInput, context: __SerdeContext): any => { + return take(input, { + clientOptionalDefaults: _json, + defaults: (_) => se_Defaults(_, context), + otherTopLevelDefault: [], + topLevelDefault: [], + }); +}; + +/** + * serializeRpcv2cborRecursiveShapesInputOutput + */ +const se_RecursiveShapesInputOutput = (input: RecursiveShapesInputOutput, context: __SerdeContext): any => { + return take(input, { + nested: (_) => se_RecursiveShapesInputOutputNested1(_, context), + }); +}; + +/** + * serializeRpcv2cborRecursiveShapesInputOutputNested1 + */ +const se_RecursiveShapesInputOutputNested1 = ( + input: RecursiveShapesInputOutputNested1, + context: __SerdeContext +): any => { + return take(input, { + foo: [], + nested: (_) => se_RecursiveShapesInputOutputNested2(_, context), + }); +}; + +/** + * serializeRpcv2cborRecursiveShapesInputOutputNested2 + */ +const se_RecursiveShapesInputOutputNested2 = ( + input: RecursiveShapesInputOutputNested2, + context: __SerdeContext +): any => { + return take(input, { + bar: [], + recursiveMember: (_) => se_RecursiveShapesInputOutputNested1(_, context), + }); +}; + +// se_RpcV2CborDenseMapsInputOutput omitted. + +/** + * serializeRpcv2cborRpcV2CborListInputOutput + */ +const se_RpcV2CborListInputOutput = (input: RpcV2CborListInputOutput, context: __SerdeContext): any => { + return take(input, { + blobList: (_) => se_BlobList(_, context), + booleanList: _json, + enumList: _json, + intEnumList: _json, + integerList: _json, + nestedStringList: _json, + stringList: _json, + stringSet: _json, + structureList: _json, + timestampList: (_) => se_TimestampList(_, context), + }); +}; + +/** + * serializeRpcv2cborRpcV2CborSparseMapsInputOutput + */ +const se_RpcV2CborSparseMapsInputOutput = (input: RpcV2CborSparseMapsInputOutput, context: __SerdeContext): any => { + return take(input, { + sparseBooleanMap: (_) => se_SparseBooleanMap(_, context), + sparseNumberMap: (_) => se_SparseNumberMap(_, context), + sparseSetMap: (_) => se_SparseSetMap(_, context), + sparseStringMap: (_) => se_SparseStringMap(_, context), + sparseStructMap: (_) => se_SparseStructMap(_, context), + }); +}; + +/** + * serializeRpcv2cborSimpleScalarStructure + */ +const se_SimpleScalarStructure = (input: SimpleScalarStructure, context: __SerdeContext): any => { + return take(input, { + blobValue: [], + byteValue: [], + doubleValue: [], + falseBooleanValue: [], + floatValue: [], + integerValue: [], + longValue: [], + shortValue: [], + stringValue: [], + trueBooleanValue: [], + }); +}; + +// se_SimpleStructure omitted. + +/** + * serializeRpcv2cborSparseBooleanMap + */ +const se_SparseBooleanMap = (input: Record, context: __SerdeContext): any => { + return Object.entries(input).reduce((acc: Record, [key, value]: [string, any]) => { + if (value !== null) { + acc[key] = value; + } else { + acc[key] = null as any; + } + + return acc; + }, {}); +}; + +/** + * serializeRpcv2cborSparseNullsOperationInputOutput + */ +const se_SparseNullsOperationInputOutput = (input: SparseNullsOperationInputOutput, context: __SerdeContext): any => { + return take(input, { + sparseStringList: (_) => se_SparseStringList(_, context), + sparseStringMap: (_) => se_SparseStringMap(_, context), + }); +}; + +/** + * serializeRpcv2cborSparseNumberMap + */ +const se_SparseNumberMap = (input: Record, context: __SerdeContext): any => { + return Object.entries(input).reduce((acc: Record, [key, value]: [string, any]) => { + if (value !== null) { + acc[key] = value; + } else { + acc[key] = null as any; + } + + return acc; + }, {}); +}; + +/** + * serializeRpcv2cborSparseSetMap + */ +const se_SparseSetMap = (input: Record, context: __SerdeContext): any => { + return Object.entries(input).reduce((acc: Record, [key, value]: [string, any]) => { + if (value !== null) { + acc[key] = _json(value); + } else { + acc[key] = null as any; + } + + return acc; + }, {}); +}; + +/** + * serializeRpcv2cborSparseStructMap + */ +const se_SparseStructMap = (input: Record, context: __SerdeContext): any => { + return Object.entries(input).reduce((acc: Record, [key, value]: [string, any]) => { + if (value !== null) { + acc[key] = _json(value); + } else { + acc[key] = null as any; + } + + return acc; + }, {}); +}; + +// se_StructureList omitted. + +// se_StructureListMember omitted. + +// se_TestStringList omitted. + +// se_TestStringMap omitted. + +/** + * serializeRpcv2cborBlobList + */ +const se_BlobList = (input: Uint8Array[], context: __SerdeContext): any => { + return input.filter((e: any) => e != null); +}; + +// se_BooleanList omitted. + +// se_FooEnumList omitted. + +// se_GreetingStruct omitted. + +// se_IntegerEnumList omitted. + +// se_IntegerList omitted. + +// se_NestedStringList omitted. + +/** + * serializeRpcv2cborSparseStringList + */ +const se_SparseStringList = (input: string[], context: __SerdeContext): any => { + return input; +}; + +/** + * serializeRpcv2cborSparseStringMap + */ +const se_SparseStringMap = (input: Record, context: __SerdeContext): any => { + return Object.entries(input).reduce((acc: Record, [key, value]: [string, any]) => { + if (value !== null) { + acc[key] = value; + } else { + acc[key] = null as any; + } + + return acc; + }, {}); +}; + +// se_StringList omitted. + +// se_StringSet omitted. + +/** + * serializeRpcv2cborTimestampList + */ +const se_TimestampList = (input: Date[], context: __SerdeContext): any => { + return input + .filter((e: any) => e != null) + .map((entry) => { + return __dateToTag(entry); + }); +}; + +// de_ValidationException omitted. + +// de_ValidationExceptionField omitted. + +// de_ValidationExceptionFieldList omitted. + +// de_ComplexError omitted. + +// de_ComplexNestedErrorData omitted. + +// de_DenseBooleanMap omitted. + +// de_DenseNumberMap omitted. + +// de_DenseSetMap omitted. + +// de_DenseStringMap omitted. + +// de_DenseStructMap omitted. + +// de_EmptyStructure omitted. + +/** + * deserializeRpcv2cborFloat16Output + */ +const de_Float16Output = (output: any, context: __SerdeContext): Float16Output => { + return take(output, { + value: __limitedParseDouble, + }) as any; +}; + +/** + * deserializeRpcv2cborFractionalSecondsOutput + */ +const de_FractionalSecondsOutput = (output: any, context: __SerdeContext): FractionalSecondsOutput => { + return take(output, { + datetime: (_: any) => __expectNonNull(__parseEpochTimestamp(_)), + }) as any; +}; + +// de_GreetingWithErrorsOutput omitted. + +// de_InvalidGreeting omitted. + +/** + * deserializeRpcv2cborOperationWithDefaultsOutput + */ +const de_OperationWithDefaultsOutput = (output: any, context: __SerdeContext): OperationWithDefaultsOutput => { + return take(output, { + defaultBlob: [], + defaultBoolean: __expectBoolean, + defaultByte: __expectByte, + defaultDouble: __limitedParseDouble, + defaultEnum: __expectString, + defaultFloat: __limitedParseFloat32, + defaultIntEnum: __expectInt32, + defaultInteger: __expectInt32, + defaultList: _json, + defaultLong: __expectLong, + defaultMap: _json, + defaultShort: __expectShort, + defaultString: __expectString, + defaultTimestamp: (_: any) => __expectNonNull(__parseEpochTimestamp(_)), + emptyBlob: [], + emptyString: __expectString, + falseBoolean: __expectBoolean, + zeroByte: __expectByte, + zeroDouble: __limitedParseDouble, + zeroFloat: __limitedParseFloat32, + zeroInteger: __expectInt32, + zeroLong: __expectLong, + zeroShort: __expectShort, + }) as any; +}; + +/** + * deserializeRpcv2cborRecursiveShapesInputOutput + */ +const de_RecursiveShapesInputOutput = (output: any, context: __SerdeContext): RecursiveShapesInputOutput => { + return take(output, { + nested: (_: any) => de_RecursiveShapesInputOutputNested1(_, context), + }) as any; +}; + +/** + * deserializeRpcv2cborRecursiveShapesInputOutputNested1 + */ +const de_RecursiveShapesInputOutputNested1 = ( + output: any, + context: __SerdeContext +): RecursiveShapesInputOutputNested1 => { + return take(output, { + foo: __expectString, + nested: (_: any) => de_RecursiveShapesInputOutputNested2(_, context), + }) as any; +}; + +/** + * deserializeRpcv2cborRecursiveShapesInputOutputNested2 + */ +const de_RecursiveShapesInputOutputNested2 = ( + output: any, + context: __SerdeContext +): RecursiveShapesInputOutputNested2 => { + return take(output, { + bar: __expectString, + recursiveMember: (_: any) => de_RecursiveShapesInputOutputNested1(_, context), + }) as any; +}; + +// de_RpcV2CborDenseMapsInputOutput omitted. + +/** + * deserializeRpcv2cborRpcV2CborListInputOutput + */ +const de_RpcV2CborListInputOutput = (output: any, context: __SerdeContext): RpcV2CborListInputOutput => { + return take(output, { + blobList: (_: any) => de_BlobList(_, context), + booleanList: _json, + enumList: _json, + intEnumList: _json, + integerList: _json, + nestedStringList: _json, + stringList: _json, + stringSet: _json, + structureList: _json, + timestampList: (_: any) => de_TimestampList(_, context), + }) as any; +}; + +/** + * deserializeRpcv2cborRpcV2CborSparseMapsInputOutput + */ +const de_RpcV2CborSparseMapsInputOutput = (output: any, context: __SerdeContext): RpcV2CborSparseMapsInputOutput => { + return take(output, { + sparseBooleanMap: (_: any) => de_SparseBooleanMap(_, context), + sparseNumberMap: (_: any) => de_SparseNumberMap(_, context), + sparseSetMap: (_: any) => de_SparseSetMap(_, context), + sparseStringMap: (_: any) => de_SparseStringMap(_, context), + sparseStructMap: (_: any) => de_SparseStructMap(_, context), + }) as any; +}; + +/** + * deserializeRpcv2cborSimpleScalarStructure + */ +const de_SimpleScalarStructure = (output: any, context: __SerdeContext): SimpleScalarStructure => { + return take(output, { + blobValue: [], + byteValue: __expectByte, + doubleValue: __limitedParseDouble, + falseBooleanValue: __expectBoolean, + floatValue: __limitedParseFloat32, + integerValue: __expectInt32, + longValue: __expectLong, + shortValue: __expectShort, + stringValue: __expectString, + trueBooleanValue: __expectBoolean, + }) as any; +}; + +// de_SimpleStructure omitted. + +/** + * deserializeRpcv2cborSparseBooleanMap + */ +const de_SparseBooleanMap = (output: any, context: __SerdeContext): Record => { + return Object.entries(output).reduce( + (acc: Record, [key, value]: [string, any]) => { + if (value !== null) { + acc[key as string] = __expectBoolean(value) as any; + } else { + acc[key as string] = null as any; + } + return acc; + }, + {} as Record + ); +}; + +/** + * deserializeRpcv2cborSparseNullsOperationInputOutput + */ +const de_SparseNullsOperationInputOutput = (output: any, context: __SerdeContext): SparseNullsOperationInputOutput => { + return take(output, { + sparseStringList: (_: any) => de_SparseStringList(_, context), + sparseStringMap: (_: any) => de_SparseStringMap(_, context), + }) as any; +}; + +/** + * deserializeRpcv2cborSparseNumberMap + */ +const de_SparseNumberMap = (output: any, context: __SerdeContext): Record => { + return Object.entries(output).reduce( + (acc: Record, [key, value]: [string, any]) => { + if (value !== null) { + acc[key as string] = __expectInt32(value) as any; + } else { + acc[key as string] = null as any; + } + return acc; + }, + {} as Record + ); +}; + +/** + * deserializeRpcv2cborSparseSetMap + */ +const de_SparseSetMap = (output: any, context: __SerdeContext): Record => { + return Object.entries(output).reduce( + (acc: Record, [key, value]: [string, any]) => { + if (value !== null) { + acc[key as string] = _json(value); + } else { + acc[key as string] = null as any; + } + return acc; + }, + {} as Record + ); +}; + +/** + * deserializeRpcv2cborSparseStructMap + */ +const de_SparseStructMap = (output: any, context: __SerdeContext): Record => { + return Object.entries(output).reduce( + (acc: Record, [key, value]: [string, any]) => { + if (value !== null) { + acc[key as string] = _json(value); + } else { + acc[key as string] = null as any; + } + return acc; + }, + {} as Record + ); +}; + +// de_StructureList omitted. + +// de_StructureListMember omitted. + +// de_TestStringList omitted. + +// de_TestStringMap omitted. + +/** + * deserializeRpcv2cborBlobList + */ +const de_BlobList = (output: any, context: __SerdeContext): Uint8Array[] => { + const collection = (output || []).filter((e: any) => e != null); + return collection; +}; + +// de_BooleanList omitted. + +// de_FooEnumList omitted. + +// de_GreetingStruct omitted. + +// de_IntegerEnumList omitted. + +// de_IntegerList omitted. + +// de_NestedStringList omitted. + +/** + * deserializeRpcv2cborSparseStringList + */ +const de_SparseStringList = (output: any, context: __SerdeContext): string[] => { + const collection = (output || []).map((entry: any) => { + if (entry === null) { + return null as any; + } + return __expectString(entry) as any; + }); + return collection; +}; + +/** + * deserializeRpcv2cborSparseStringMap + */ +const de_SparseStringMap = (output: any, context: __SerdeContext): Record => { + return Object.entries(output).reduce( + (acc: Record, [key, value]: [string, any]) => { + if (value !== null) { + acc[key as string] = __expectString(value) as any; + } else { + acc[key as string] = null as any; + } + return acc; + }, + {} as Record + ); +}; + +// de_StringList omitted. + +// de_StringSet omitted. + +/** + * deserializeRpcv2cborTimestampList + */ +const de_TimestampList = (output: any, context: __SerdeContext): Date[] => { + const collection = (output || []) + .filter((e: any) => e != null) + .map((entry: any) => { + return __expectNonNull(__parseEpochTimestamp(entry)); + }); + return collection; +}; + +const deserializeMetadata = (output: __HttpResponse): __ResponseMetadata => ({ + httpStatusCode: output.statusCode, + requestId: + output.headers["x-amzn-requestid"] ?? output.headers["x-amzn-request-id"] ?? output.headers["x-amz-request-id"], + extendedRequestId: output.headers["x-amz-id-2"], + cfId: output.headers["x-amz-cf-id"], +}); + +const throwDefaultError = withBaseException(__BaseException); +const SHARED_HEADERS: __HeaderBag = { + "content-type": "application/cbor", + "smithy-protocol": "rpc-v2-cbor", +}; diff --git a/private/smithy-rpcv2-cbor/src/runtimeConfig.browser.ts b/private/smithy-rpcv2-cbor/src/runtimeConfig.browser.ts new file mode 100644 index 00000000000..6c7d4fa35a0 --- /dev/null +++ b/private/smithy-rpcv2-cbor/src/runtimeConfig.browser.ts @@ -0,0 +1,30 @@ +// smithy-typescript generated code +import { Sha256 } from "@aws-crypto/sha256-browser"; +import { FetchHttpHandler as RequestHandler, streamCollector } from "@smithy/fetch-http-handler"; +import { calculateBodyLength } from "@smithy/util-body-length-browser"; +import { DEFAULT_MAX_ATTEMPTS, DEFAULT_RETRY_MODE } from "@smithy/util-retry"; +import { RpcV2ProtocolClientConfig } from "./RpcV2ProtocolClient"; +import { getRuntimeConfig as getSharedRuntimeConfig } from "./runtimeConfig.shared"; +import { loadConfigsForDefaultMode } from "@smithy/smithy-client"; +import { resolveDefaultsModeConfig } from "@smithy/util-defaults-mode-browser"; + +/** + * @internal + */ +export const getRuntimeConfig = (config: RpcV2ProtocolClientConfig) => { + const defaultsMode = resolveDefaultsModeConfig(config); + const defaultConfigProvider = () => defaultsMode().then(loadConfigsForDefaultMode); + const clientSharedValues = getSharedRuntimeConfig(config); + return { + ...clientSharedValues, + ...config, + runtime: "browser", + defaultsMode, + bodyLengthChecker: config?.bodyLengthChecker ?? calculateBodyLength, + maxAttempts: config?.maxAttempts ?? DEFAULT_MAX_ATTEMPTS, + requestHandler: RequestHandler.create(config?.requestHandler ?? defaultConfigProvider), + retryMode: config?.retryMode ?? (async () => (await defaultConfigProvider()).retryMode || DEFAULT_RETRY_MODE), + sha256: config?.sha256 ?? Sha256, + streamCollector: config?.streamCollector ?? streamCollector, + }; +}; diff --git a/private/smithy-rpcv2-cbor/src/runtimeConfig.native.ts b/private/smithy-rpcv2-cbor/src/runtimeConfig.native.ts new file mode 100644 index 00000000000..efbf0075ed3 --- /dev/null +++ b/private/smithy-rpcv2-cbor/src/runtimeConfig.native.ts @@ -0,0 +1,17 @@ +// smithy-typescript generated code +import { Sha256 } from "@aws-crypto/sha256-js"; +import { RpcV2ProtocolClientConfig } from "./RpcV2ProtocolClient"; +import { getRuntimeConfig as getBrowserRuntimeConfig } from "./runtimeConfig.browser"; + +/** + * @internal + */ +export const getRuntimeConfig = (config: RpcV2ProtocolClientConfig) => { + const browserDefaults = getBrowserRuntimeConfig(config); + return { + ...browserDefaults, + ...config, + runtime: "react-native", + sha256: config?.sha256 ?? Sha256, + }; +}; diff --git a/private/smithy-rpcv2-cbor/src/runtimeConfig.shared.ts b/private/smithy-rpcv2-cbor/src/runtimeConfig.shared.ts new file mode 100644 index 00000000000..b9a066d3f36 --- /dev/null +++ b/private/smithy-rpcv2-cbor/src/runtimeConfig.shared.ts @@ -0,0 +1,35 @@ +// smithy-typescript generated code +import { defaultRpcV2ProtocolHttpAuthSchemeProvider } from "./auth/httpAuthSchemeProvider"; +import { NoAuthSigner } from "@smithy/core"; +import { NoOpLogger } from "@smithy/smithy-client"; +import { IdentityProviderConfig } from "@smithy/types"; +import { parseUrl } from "@smithy/url-parser"; +import { fromBase64, toBase64 } from "@smithy/util-base64"; +import { fromUtf8, toUtf8 } from "@smithy/util-utf8"; +import { RpcV2ProtocolClientConfig } from "./RpcV2ProtocolClient"; + +/** + * @internal + */ +export const getRuntimeConfig = (config: RpcV2ProtocolClientConfig) => { + return { + apiVersion: "2020-07-14", + base64Decoder: config?.base64Decoder ?? fromBase64, + base64Encoder: config?.base64Encoder ?? toBase64, + disableHostPrefix: config?.disableHostPrefix ?? false, + extensions: config?.extensions ?? [], + httpAuthSchemeProvider: config?.httpAuthSchemeProvider ?? defaultRpcV2ProtocolHttpAuthSchemeProvider, + httpAuthSchemes: config?.httpAuthSchemes ?? [ + { + schemeId: "smithy.api#noAuth", + identityProvider: (ipc: IdentityProviderConfig) => + ipc.getIdentityProvider("smithy.api#noAuth") || (async () => ({})), + signer: new NoAuthSigner(), + }, + ], + logger: config?.logger ?? new NoOpLogger(), + urlParser: config?.urlParser ?? parseUrl, + utf8Decoder: config?.utf8Decoder ?? fromUtf8, + utf8Encoder: config?.utf8Encoder ?? toUtf8, + }; +}; diff --git a/private/smithy-rpcv2-cbor/src/runtimeConfig.ts b/private/smithy-rpcv2-cbor/src/runtimeConfig.ts new file mode 100644 index 00000000000..737dc04f566 --- /dev/null +++ b/private/smithy-rpcv2-cbor/src/runtimeConfig.ts @@ -0,0 +1,39 @@ +// smithy-typescript generated code +import { Hash } from "@smithy/hash-node"; +import { NODE_MAX_ATTEMPT_CONFIG_OPTIONS, NODE_RETRY_MODE_CONFIG_OPTIONS } from "@smithy/middleware-retry"; +import { loadConfig as loadNodeConfig } from "@smithy/node-config-provider"; +import { NodeHttpHandler as RequestHandler, streamCollector } from "@smithy/node-http-handler"; +import { calculateBodyLength } from "@smithy/util-body-length-node"; +import { DEFAULT_RETRY_MODE } from "@smithy/util-retry"; +import { RpcV2ProtocolClientConfig } from "./RpcV2ProtocolClient"; +import { getRuntimeConfig as getSharedRuntimeConfig } from "./runtimeConfig.shared"; +import { loadConfigsForDefaultMode } from "@smithy/smithy-client"; +import { resolveDefaultsModeConfig } from "@smithy/util-defaults-mode-node"; +import { emitWarningIfUnsupportedVersion } from "@smithy/smithy-client"; + +/** + * @internal + */ +export const getRuntimeConfig = (config: RpcV2ProtocolClientConfig) => { + emitWarningIfUnsupportedVersion(process.version); + const defaultsMode = resolveDefaultsModeConfig(config); + const defaultConfigProvider = () => defaultsMode().then(loadConfigsForDefaultMode); + const clientSharedValues = getSharedRuntimeConfig(config); + return { + ...clientSharedValues, + ...config, + runtime: "node", + defaultsMode, + bodyLengthChecker: config?.bodyLengthChecker ?? calculateBodyLength, + maxAttempts: config?.maxAttempts ?? loadNodeConfig(NODE_MAX_ATTEMPT_CONFIG_OPTIONS), + requestHandler: RequestHandler.create(config?.requestHandler ?? defaultConfigProvider), + retryMode: + config?.retryMode ?? + loadNodeConfig({ + ...NODE_RETRY_MODE_CONFIG_OPTIONS, + default: async () => (await defaultConfigProvider()).retryMode || DEFAULT_RETRY_MODE, + }), + sha256: config?.sha256 ?? Hash.bind(null, "sha256"), + streamCollector: config?.streamCollector ?? streamCollector, + }; +}; diff --git a/private/smithy-rpcv2-cbor/src/runtimeExtensions.ts b/private/smithy-rpcv2-cbor/src/runtimeExtensions.ts new file mode 100644 index 00000000000..275f5d03689 --- /dev/null +++ b/private/smithy-rpcv2-cbor/src/runtimeExtensions.ts @@ -0,0 +1,41 @@ +// smithy-typescript generated code +import { getHttpAuthExtensionConfiguration, resolveHttpAuthRuntimeConfig } from "./auth/httpAuthExtensionConfiguration"; +import { getHttpHandlerExtensionConfiguration, resolveHttpHandlerRuntimeConfig } from "@smithy/protocol-http"; +import { getDefaultExtensionConfiguration, resolveDefaultRuntimeConfig } from "@smithy/smithy-client"; +import { RpcV2ProtocolExtensionConfiguration } from "./extensionConfiguration"; + +/** + * @public + */ +export interface RuntimeExtension { + configure(extensionConfiguration: RpcV2ProtocolExtensionConfiguration): void; +} + +/** + * @public + */ +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)), + }; + + extensions.forEach((extension) => extension.configure(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 new file mode 100644 index 00000000000..940d62070fc --- /dev/null +++ b/private/smithy-rpcv2-cbor/test/functional/rpcv2cbor.spec.ts @@ -0,0 +1,3120 @@ +// smithy-typescript generated code +import { RpcV2ProtocolClient } from "../../src/RpcV2ProtocolClient"; +import { EmptyInputOutputCommand } from "../../src/commands/EmptyInputOutputCommand"; +import { Float16Command } from "../../src/commands/Float16Command"; +import { FractionalSecondsCommand } from "../../src/commands/FractionalSecondsCommand"; +import { GreetingWithErrorsCommand } from "../../src/commands/GreetingWithErrorsCommand"; +import { NoInputOutputCommand } from "../../src/commands/NoInputOutputCommand"; +import { OperationWithDefaultsCommand } from "../../src/commands/OperationWithDefaultsCommand"; +import { OptionalInputOutputCommand } from "../../src/commands/OptionalInputOutputCommand"; +import { RecursiveShapesCommand } from "../../src/commands/RecursiveShapesCommand"; +import { RpcV2CborDenseMapsCommand } from "../../src/commands/RpcV2CborDenseMapsCommand"; +import { RpcV2CborListsCommand } from "../../src/commands/RpcV2CborListsCommand"; +import { RpcV2CborSparseMapsCommand } from "../../src/commands/RpcV2CborSparseMapsCommand"; +import { SimpleScalarPropertiesCommand } from "../../src/commands/SimpleScalarPropertiesCommand"; +import { SparseNullsOperationCommand } from "../../src/commands/SparseNullsOperationCommand"; +import { cbor } from "@smithy/core/cbor"; +import { HttpHandlerOptions, HeaderBag, Endpoint } from "@smithy/types"; +import { HttpHandler, HttpRequest, HttpResponse } from "@smithy/protocol-http"; +import { Readable } from "stream"; + +/** + * Throws an expected exception that contains the serialized request. + */ +class EXPECTED_REQUEST_SERIALIZATION_ERROR extends Error { + constructor(readonly request: HttpRequest) { + super(); + } +} + +/** + * Throws an EXPECTED_REQUEST_SERIALIZATION_ERROR error before sending a + * request. The thrown exception contains the serialized request. + */ +class RequestSerializationTestHandler implements HttpHandler { + handle(request: HttpRequest, options?: HttpHandlerOptions): Promise<{ response: HttpResponse }> { + return Promise.reject(new EXPECTED_REQUEST_SERIALIZATION_ERROR(request)); + } + updateHttpClientConfig(key: never, value: never): void {} + httpHandlerConfigs() { + return {}; + } +} + +/** + * Returns a resolved Promise of the specified response contents. + */ +class ResponseDeserializationTestHandler implements HttpHandler { + isSuccess: boolean; + code: number; + headers: HeaderBag; + body: string | Uint8Array; + isBase64Body: boolean; + + constructor(isSuccess: boolean, code: number, headers?: HeaderBag, body?: string) { + this.isSuccess = isSuccess; + this.code = code; + if (headers === undefined) { + this.headers = {}; + } else { + this.headers = headers; + } + if (body === undefined) { + body = ""; + } + this.body = body; + this.isBase64Body = String(body).length > 0 && Buffer.from(String(body), "base64").toString("base64") === body; + } + + handle(request: HttpRequest, options?: HttpHandlerOptions): Promise<{ response: HttpResponse }> { + return Promise.resolve({ + response: new HttpResponse({ + statusCode: this.code, + headers: this.headers, + body: this.isBase64Body ? toBytes(this.body as string) : Readable.from([this.body]), + }), + }); + } + + updateHttpClientConfig(key: never, value: never): void {} + + httpHandlerConfigs() { + return {}; + } +} + +interface comparableParts { + [key: string]: string; +} + +/** + * Generates a standard map of un-equal values given input parts. + */ +const compareParts = (expectedParts: comparableParts, generatedParts: comparableParts) => { + const unequalParts: any = {}; + Object.keys(expectedParts).forEach((key) => { + if (generatedParts[key] === undefined) { + unequalParts[key] = { exp: expectedParts[key], gen: undefined }; + } else if (!equivalentContents(expectedParts[key], generatedParts[key])) { + unequalParts[key] = { exp: expectedParts[key], gen: generatedParts[key] }; + } + }); + + Object.keys(generatedParts).forEach((key) => { + if (expectedParts[key] === undefined) { + unequalParts[key] = { exp: undefined, gen: generatedParts[key] }; + } + }); + + if (Object.keys(unequalParts).length !== 0) { + return unequalParts; + } + return undefined; +}; + +/** + * Compares all types for equivalent contents, doing nested + * equality checks based on non-`$metadata` + * properties that have defined values. + */ +const equivalentContents = (expected: any, generated: any): boolean => { + if (typeof (global as any).expect === "function") { + expect(normalizeByteArrayType(generated)).toEqual(normalizeByteArrayType(expected)); + return true; + } + + let localExpected = expected; + + // Short circuit on equality. + if (localExpected == generated) { + return true; + } + + if (typeof expected !== "object") { + return expected === generated; + } + + // If a test fails with an issue in the below 6 lines, it's likely + // due to an issue in the nestedness or existence of the property + // being compared. + delete localExpected["$metadata"]; + delete generated["$metadata"]; + Object.keys(localExpected).forEach((key) => localExpected[key] === undefined && delete localExpected[key]); + Object.keys(generated).forEach((key) => generated[key] === undefined && delete generated[key]); + + const expectedProperties = Object.getOwnPropertyNames(localExpected); + const generatedProperties = Object.getOwnPropertyNames(generated); + + // Short circuit on different property counts. + if (expectedProperties.length != generatedProperties.length) { + return false; + } + + // Compare properties directly. + for (var index = 0; index < expectedProperties.length; index++) { + const propertyName = expectedProperties[index]; + if (!equivalentContents(localExpected[propertyName], generated[propertyName])) { + return false; + } + } + + return true; +}; + +const clientParams = { + region: "us-west-2", + credentials: { accessKeyId: "key", secretAccessKey: "secret" }, + endpoint: () => { + const url = new URL("https://localhost/"); + return Promise.resolve({ + ...url, + path: url.pathname, + ...(url.port ? { port: Number(url.port) } : {}), + }) as Promise; + }, +}; + +/** + * A wrapper function that shadows `fail` from jest-jasmine2 + * (jasmine2 was replaced with circus in > v27 as the default test runner) + */ +const fail = (error?: any): never => { + throw new Error(error); +}; + +/** + * Hexadecimal to byteArray. + */ +const toBytes = (hex: string) => { + return Buffer.from(hex, "base64"); +}; + +function normalizeByteArrayType(data: any) { + // normalize float32 errors + if (typeof data === "number") { + const u = new Uint8Array(4); + const dv = new DataView(u.buffer, u.byteOffset, u.byteLength); + dv.setFloat32(0, data); + return dv.getFloat32(0); + } + if (!data || typeof data !== "object") { + return data; + } + if (data instanceof Uint8Array) { + return Uint8Array.from(data); + } + if (data instanceof String || data instanceof Boolean || data instanceof Number) { + return data.valueOf(); + } + const output = {} as any; + for (const key of Object.getOwnPropertyNames(data)) { + output[key] = normalizeByteArrayType(data[key]); + } + return output; +} + +/** + * When Input structure is empty we write CBOR equivalent of {} + */ +it("empty_input:Request", async () => { + const client = new RpcV2ProtocolClient({ + ...clientParams, + requestHandler: new RequestSerializationTestHandler(), + }); + + const command = new EmptyInputOutputCommand({} as any); + try { + await client.send(command); + fail("Expected an EXPECTED_REQUEST_SERIALIZATION_ERROR to be thrown"); + return; + } catch (err) { + if (!(err instanceof EXPECTED_REQUEST_SERIALIZATION_ERROR)) { + fail(err); + return; + } + const r = err.request; + expect(r.method).toBe("POST"); + expect(r.path).toBe("/service/RpcV2Protocol/operation/EmptyInputOutput"); + expect(r.headers["content-length"]).toBeDefined(); + + expect(r.headers["x-amz-target"]).toBeUndefined(); + + expect(r.headers["content-type"]).toBeDefined(); + expect(r.headers["content-type"]).toBe("application/cbor"); + expect(r.headers["smithy-protocol"]).toBeDefined(); + expect(r.headers["smithy-protocol"]).toBe("rpc-v2-cbor"); + + expect(r.body).toBeDefined(); + const bodyString = `v/8=`; + const unequalParts: any = compareEquivalentCborBodies(bodyString, r.body); + expect(unequalParts).toBeUndefined(); + } +}); + +/** + * When output structure is empty we write CBOR equivalent of {} + */ +it("empty_output:Response", async () => { + const client = new RpcV2ProtocolClient({ + ...clientParams, + requestHandler: new ResponseDeserializationTestHandler( + true, + 200, + { + "smithy-protocol": "rpc-v2-cbor", + "content-type": "application/cbor", + }, + `v/8=` + ), + }); + + const params: any = {}; + const command = new EmptyInputOutputCommand(params); + + let r: any; + try { + r = await client.send(command); + } catch (err) { + fail("Expected a valid response to be returned, got " + err); + return; + } + expect(r["$metadata"].httpStatusCode).toBe(200); +}); + +/** + * When output structure is empty the client should accept an empty body + */ +it("empty_output_no_body:Response", async () => { + const client = new RpcV2ProtocolClient({ + ...clientParams, + requestHandler: new ResponseDeserializationTestHandler( + true, + 200, + { + "smithy-protocol": "rpc-v2-cbor", + "content-type": "application/cbor", + }, + `` + ), + }); + + const params: any = {}; + const command = new EmptyInputOutputCommand(params); + + let r: any; + try { + r = await client.send(command); + } catch (err) { + fail("Expected a valid response to be returned, got " + err); + return; + } + expect(r["$metadata"].httpStatusCode).toBe(200); +}); + +/** + * Ensures that clients can correctly parse float16 +Inf. + */ +it("RpcV2CborFloat16Inf:Response", async () => { + const client = new RpcV2ProtocolClient({ + ...clientParams, + requestHandler: new ResponseDeserializationTestHandler( + true, + 200, + { + "smithy-protocol": "rpc-v2-cbor", + "content-type": "application/cbor", + }, + `oWV2YWx1Zfl8AA==` + ), + }); + + const params: any = {}; + const command = new Float16Command(params); + + let r: any; + try { + r = await client.send(command); + } catch (err) { + fail("Expected a valid response to be returned, got " + err); + return; + } + expect(r["$metadata"].httpStatusCode).toBe(200); + const paramsToValidate: any = [ + { + value: Infinity, + }, + ][0]; + Object.keys(paramsToValidate).forEach((param) => { + expect(r[param]).toBeDefined(); + expect(equivalentContents(paramsToValidate[param], r[param])).toBe(true); + }); +}); + +/** + * Ensures that clients can correctly parse float16 -Inf. + */ +it("RpcV2CborFloat16NegInf:Response", async () => { + const client = new RpcV2ProtocolClient({ + ...clientParams, + requestHandler: new ResponseDeserializationTestHandler( + true, + 200, + { + "smithy-protocol": "rpc-v2-cbor", + "content-type": "application/cbor", + }, + `oWV2YWx1Zfn8AA==` + ), + }); + + const params: any = {}; + const command = new Float16Command(params); + + let r: any; + try { + r = await client.send(command); + } catch (err) { + fail("Expected a valid response to be returned, got " + err); + return; + } + expect(r["$metadata"].httpStatusCode).toBe(200); + const paramsToValidate: any = [ + { + value: -Infinity, + }, + ][0]; + Object.keys(paramsToValidate).forEach((param) => { + expect(r[param]).toBeDefined(); + expect(equivalentContents(paramsToValidate[param], r[param])).toBe(true); + }); +}); + +/** + * Ensures that clients can correctly parse float16 NaN with high LSB. + */ +it("RpcV2CborFloat16LSBNaN:Response", async () => { + const client = new RpcV2ProtocolClient({ + ...clientParams, + requestHandler: new ResponseDeserializationTestHandler( + true, + 200, + { + "smithy-protocol": "rpc-v2-cbor", + "content-type": "application/cbor", + }, + `oWV2YWx1Zfl8AQ==` + ), + }); + + const params: any = {}; + const command = new Float16Command(params); + + let r: any; + try { + r = await client.send(command); + } catch (err) { + fail("Expected a valid response to be returned, got " + err); + return; + } + expect(r["$metadata"].httpStatusCode).toBe(200); + const paramsToValidate: any = [ + { + value: NaN, + }, + ][0]; + Object.keys(paramsToValidate).forEach((param) => { + expect(r[param]).toBeDefined(); + expect(equivalentContents(paramsToValidate[param], r[param])).toBe(true); + }); +}); + +/** + * Ensures that clients can correctly parse float16 NaN with high MSB. + */ +it("RpcV2CborFloat16MSBNaN:Response", async () => { + const client = new RpcV2ProtocolClient({ + ...clientParams, + requestHandler: new ResponseDeserializationTestHandler( + true, + 200, + { + "smithy-protocol": "rpc-v2-cbor", + "content-type": "application/cbor", + }, + `oWV2YWx1Zfl+AA==` + ), + }); + + const params: any = {}; + const command = new Float16Command(params); + + let r: any; + try { + r = await client.send(command); + } catch (err) { + fail("Expected a valid response to be returned, got " + err); + return; + } + expect(r["$metadata"].httpStatusCode).toBe(200); + const paramsToValidate: any = [ + { + value: NaN, + }, + ][0]; + Object.keys(paramsToValidate).forEach((param) => { + expect(r[param]).toBeDefined(); + expect(equivalentContents(paramsToValidate[param], r[param])).toBe(true); + }); +}); + +/** + * Ensures that clients can correctly parse a subnormal float16. + */ +it("RpcV2CborFloat16Subnormal:Response", async () => { + const client = new RpcV2ProtocolClient({ + ...clientParams, + requestHandler: new ResponseDeserializationTestHandler( + true, + 200, + { + "smithy-protocol": "rpc-v2-cbor", + "content-type": "application/cbor", + }, + `oWV2YWx1ZfkAUA==` + ), + }); + + const params: any = {}; + const command = new Float16Command(params); + + let r: any; + try { + r = await client.send(command); + } catch (err) { + fail("Expected a valid response to be returned, got " + err); + return; + } + expect(r["$metadata"].httpStatusCode).toBe(200); + const paramsToValidate: any = [ + { + value: 4.76837158203125e-6, + }, + ][0]; + Object.keys(paramsToValidate).forEach((param) => { + expect(r[param]).toBeDefined(); + expect(equivalentContents(paramsToValidate[param], r[param])).toBe(true); + }); +}); + +/** + * Ensures that clients can correctly parse timestamps with fractional seconds + */ +it("RpcV2CborDateTimeWithFractionalSeconds:Response", async () => { + const client = new RpcV2ProtocolClient({ + ...clientParams, + requestHandler: new ResponseDeserializationTestHandler( + true, + 200, + { + "smithy-protocol": "rpc-v2-cbor", + "content-type": "application/cbor", + }, + `v2hkYXRldGltZcH7Qcw32zgPvnf/` + ), + }); + + const params: any = {}; + const command = new FractionalSecondsCommand(params); + + let r: any; + try { + r = await client.send(command); + } catch (err) { + fail("Expected a valid response to be returned, got " + err); + return; + } + expect(r["$metadata"].httpStatusCode).toBe(200); + const paramsToValidate: any = [ + { + datetime: new Date(9.46845296123e8 * 1000), + }, + ][0]; + Object.keys(paramsToValidate).forEach((param) => { + expect(r[param]).toBeDefined(); + expect(equivalentContents(paramsToValidate[param], r[param])).toBe(true); + }); +}); + +/** + * Parses simple RpcV2 Cbor errors + */ +it("RpcV2CborInvalidGreetingError:Error:GreetingWithErrors", async () => { + const client = new RpcV2ProtocolClient({ + ...clientParams, + requestHandler: new ResponseDeserializationTestHandler( + false, + 400, + { + "smithy-protocol": "rpc-v2-cbor", + "content-type": "application/cbor", + }, + `v2ZfX3R5cGV4LnNtaXRoeS5wcm90b2NvbHRlc3RzLnJwY3YyQ2JvciNJbnZhbGlkR3JlZXRpbmdnTWVzc2FnZWJIaf8=` + ), + }); + + const params: any = {}; + const command = new GreetingWithErrorsCommand(params); + + try { + await client.send(command); + } catch (err) { + if (err.name !== "InvalidGreeting") { + console.log(err); + fail(`Expected a InvalidGreeting to be thrown, got ${err.name} instead`); + return; + } + const r: any = err; + expect(r["$metadata"].httpStatusCode).toBe(400); + const paramsToValidate: any = [ + { + message: "Hi", + }, + ][0]; + Object.keys(paramsToValidate).forEach((param) => { + expect(r[param]).toBeDefined(); + expect(equivalentContents(paramsToValidate[param], r[param])).toBe(true); + }); + return; + } + fail("Expected an exception to be thrown from response"); +}); + +/** + * Parses a complex error with no message member + */ +it("RpcV2CborComplexError:Error:GreetingWithErrors", async () => { + const client = new RpcV2ProtocolClient({ + ...clientParams, + requestHandler: new ResponseDeserializationTestHandler( + false, + 400, + { + "smithy-protocol": "rpc-v2-cbor", + "content-type": "application/cbor", + }, + `v2ZfX3R5cGV4K3NtaXRoeS5wcm90b2NvbHRlc3RzLnJwY3YyQ2JvciNDb21wbGV4RXJyb3JoVG9wTGV2ZWxpVG9wIGxldmVsZk5lc3RlZL9jRm9vY2Jhcv//` + ), + }); + + const params: any = {}; + const command = new GreetingWithErrorsCommand(params); + + try { + await client.send(command); + } catch (err) { + if (err.name !== "ComplexError") { + console.log(err); + fail(`Expected a ComplexError to be thrown, got ${err.name} instead`); + return; + } + const r: any = err; + expect(r["$metadata"].httpStatusCode).toBe(400); + const paramsToValidate: any = [ + { + TopLevel: "Top level", + Nested: { + Foo: "bar", + }, + }, + ][0]; + Object.keys(paramsToValidate).forEach((param) => { + expect(r[param]).toBeDefined(); + expect(equivalentContents(paramsToValidate[param], r[param])).toBe(true); + }); + return; + } + fail("Expected an exception to be thrown from response"); +}); + +it("RpcV2CborEmptyComplexError:Error:GreetingWithErrors", async () => { + const client = new RpcV2ProtocolClient({ + ...clientParams, + requestHandler: new ResponseDeserializationTestHandler( + false, + 400, + { + "smithy-protocol": "rpc-v2-cbor", + "content-type": "application/cbor", + }, + `v2ZfX3R5cGV4K3NtaXRoeS5wcm90b2NvbHRlc3RzLnJwY3YyQ2JvciNDb21wbGV4RXJyb3L/` + ), + }); + + const params: any = {}; + const command = new GreetingWithErrorsCommand(params); + + try { + await client.send(command); + } catch (err) { + if (err.name !== "ComplexError") { + console.log(err); + fail(`Expected a ComplexError to be thrown, got ${err.name} instead`); + return; + } + const r: any = err; + expect(r["$metadata"].httpStatusCode).toBe(400); + return; + } + fail("Expected an exception to be thrown from response"); +}); + +/** + * Body is empty and no Content-Type header if no input + */ +it("no_input:Request", async () => { + const client = new RpcV2ProtocolClient({ + ...clientParams, + requestHandler: new RequestSerializationTestHandler(), + }); + + const command = new NoInputOutputCommand({}); + try { + await client.send(command); + fail("Expected an EXPECTED_REQUEST_SERIALIZATION_ERROR to be thrown"); + return; + } catch (err) { + if (!(err instanceof EXPECTED_REQUEST_SERIALIZATION_ERROR)) { + fail(err); + return; + } + const r = err.request; + expect(r.method).toBe("POST"); + expect(r.path).toBe("/service/RpcV2Protocol/operation/NoInputOutput"); + + expect(r.headers["content-type"]).toBeUndefined(); + expect(r.headers["x-amz-target"]).toBeUndefined(); + + expect(r.headers["smithy-protocol"]).toBeDefined(); + expect(r.headers["smithy-protocol"]).toBe("rpc-v2-cbor"); + + expect(r.body).toBeFalsy(); + } +}); + +/** + * A `Content-Type` header should not be set if the response body is empty. + */ +it("no_output:Response", async () => { + const client = new RpcV2ProtocolClient({ + ...clientParams, + requestHandler: new ResponseDeserializationTestHandler( + true, + 200, + { + "smithy-protocol": "rpc-v2-cbor", + }, + `` + ), + }); + + const params: any = {}; + const command = new NoInputOutputCommand(params); + + let r: any; + try { + r = await client.send(command); + } catch (err) { + fail("Expected a valid response to be returned, got " + err); + return; + } + expect(r["$metadata"].httpStatusCode).toBe(200); +}); + +/** + * Clients should accept a CBOR empty struct if there is no output. + */ +it("NoOutputClientAllowsEmptyCbor:Response", async () => { + const client = new RpcV2ProtocolClient({ + ...clientParams, + requestHandler: new ResponseDeserializationTestHandler( + true, + 200, + { + "smithy-protocol": "rpc-v2-cbor", + "content-type": "application/cbor", + }, + `v/8=` + ), + }); + + const params: any = {}; + const command = new NoInputOutputCommand(params); + + let r: any; + try { + r = await client.send(command); + } catch (err) { + fail("Expected a valid response to be returned, got " + err); + return; + } + expect(r["$metadata"].httpStatusCode).toBe(200); +}); + +/** + * Clients should accept an empty body if there is no output and + * should not raise an error if the `Content-Type` header is set. + */ +it("NoOutputClientAllowsEmptyBody:Response", async () => { + const client = new RpcV2ProtocolClient({ + ...clientParams, + requestHandler: new ResponseDeserializationTestHandler( + true, + 200, + { + "smithy-protocol": "rpc-v2-cbor", + "content-type": "application/cbor", + }, + `` + ), + }); + + const params: any = {}; + const command = new NoInputOutputCommand(params); + + let r: any; + try { + r = await client.send(command); + } catch (err) { + fail("Expected a valid response to be returned, got " + err); + return; + } + expect(r["$metadata"].httpStatusCode).toBe(200); +}); + +/** + * Client populates default values in input. + */ +it.skip("RpcV2CborClientPopulatesDefaultValuesInInput:Request", async () => { + const client = new RpcV2ProtocolClient({ + ...clientParams, + requestHandler: new RequestSerializationTestHandler(), + }); + + const command = new OperationWithDefaultsCommand({ + defaults: {} as any, + } as any); + try { + await client.send(command); + fail("Expected an EXPECTED_REQUEST_SERIALIZATION_ERROR to be thrown"); + return; + } catch (err) { + if (!(err instanceof EXPECTED_REQUEST_SERIALIZATION_ERROR)) { + fail(err); + return; + } + const r = err.request; + expect(r.method).toBe("POST"); + expect(r.path).toBe("/service/RpcV2Protocol/operation/OperationWithDefaults"); + expect(r.headers["content-length"]).toBeDefined(); + + expect(r.headers["content-type"]).toBeDefined(); + expect(r.headers["content-type"]).toBe("application/cbor"); + expect(r.headers["smithy-protocol"]).toBeDefined(); + expect(r.headers["smithy-protocol"]).toBe("rpc-v2-cbor"); + + expect(r.body).toBeDefined(); + const bodyString = `v2hkZWZhdWx0c79tZGVmYXVsdFN0cmluZ2JoaW5kZWZhdWx0Qm9vbGVhbvVrZGVmYXVsdExpc3Sf/3BkZWZhdWx0VGltZXN0YW1wwQBrZGVmYXVsdEJsb2JjYWJja2RlZmF1bHRCeXRlAWxkZWZhdWx0U2hvcnQBbmRlZmF1bHRJbnRlZ2VyCmtkZWZhdWx0TG9uZxhkbGRlZmF1bHRGbG9hdPo/gAAAbWRlZmF1bHREb3VibGX6P4AAAGpkZWZhdWx0TWFwv/9rZGVmYXVsdEVudW1jRk9PbmRlZmF1bHRJbnRFbnVtAWtlbXB0eVN0cmluZ2BsZmFsc2VCb29sZWFu9GllbXB0eUJsb2JgaHplcm9CeXRlAGl6ZXJvU2hvcnQAa3plcm9JbnRlZ2VyAGh6ZXJvTG9uZwBpemVyb0Zsb2F0+gAAAABqemVyb0RvdWJsZfoAAAAA//8=`; + const unequalParts: any = compareEquivalentCborBodies(bodyString, r.body); + expect(unequalParts).toBeUndefined(); + } +}); + +/** + * Client skips top level default values in input. + */ +it.skip("RpcV2CborClientSkipsTopLevelDefaultValuesInInput:Request", async () => { + const client = new RpcV2ProtocolClient({ + ...clientParams, + requestHandler: new RequestSerializationTestHandler(), + }); + + const command = new OperationWithDefaultsCommand({} as any); + try { + await client.send(command); + fail("Expected an EXPECTED_REQUEST_SERIALIZATION_ERROR to be thrown"); + return; + } catch (err) { + if (!(err instanceof EXPECTED_REQUEST_SERIALIZATION_ERROR)) { + fail(err); + return; + } + const r = err.request; + expect(r.method).toBe("POST"); + expect(r.path).toBe("/service/RpcV2Protocol/operation/OperationWithDefaults"); + expect(r.headers["content-length"]).toBeDefined(); + + expect(r.headers["content-type"]).toBeDefined(); + expect(r.headers["content-type"]).toBe("application/cbor"); + expect(r.headers["smithy-protocol"]).toBeDefined(); + expect(r.headers["smithy-protocol"]).toBe("rpc-v2-cbor"); + + expect(r.body).toBeDefined(); + const bodyString = `v/8=`; + const unequalParts: any = compareEquivalentCborBodies(bodyString, r.body); + expect(unequalParts).toBeUndefined(); + } +}); + +/** + * Client uses explicitly provided member values over defaults + */ +it.skip("RpcV2CborClientUsesExplicitlyProvidedMemberValuesOverDefaults:Request", async () => { + const client = new RpcV2ProtocolClient({ + ...clientParams, + requestHandler: new RequestSerializationTestHandler(), + }); + + const command = new OperationWithDefaultsCommand({ + defaults: { + defaultString: "bye", + defaultBoolean: true, + defaultList: ["a"], + defaultTimestamp: new Date(1000), + defaultBlob: Uint8Array.from("hi", (c) => c.charCodeAt(0)), + defaultByte: 2, + defaultShort: 2, + defaultInteger: 20, + defaultLong: 200, + defaultFloat: 2.0, + defaultDouble: 2.0, + defaultMap: { + name: "Jack", + } as any, + defaultEnum: "BAR", + defaultIntEnum: 2, + emptyString: "foo", + falseBoolean: true, + emptyBlob: Uint8Array.from("hi", (c) => c.charCodeAt(0)), + zeroByte: 1, + zeroShort: 1, + zeroInteger: 1, + zeroLong: 1, + zeroFloat: 1.0, + zeroDouble: 1.0, + } as any, + } as any); + try { + await client.send(command); + fail("Expected an EXPECTED_REQUEST_SERIALIZATION_ERROR to be thrown"); + return; + } catch (err) { + if (!(err instanceof EXPECTED_REQUEST_SERIALIZATION_ERROR)) { + fail(err); + return; + } + const r = err.request; + expect(r.method).toBe("POST"); + expect(r.path).toBe("/service/RpcV2Protocol/operation/OperationWithDefaults"); + expect(r.headers["content-length"]).toBeDefined(); + + expect(r.headers["content-type"]).toBeDefined(); + expect(r.headers["content-type"]).toBe("application/cbor"); + expect(r.headers["smithy-protocol"]).toBeDefined(); + expect(r.headers["smithy-protocol"]).toBe("rpc-v2-cbor"); + + expect(r.body).toBeDefined(); + const bodyString = `v2hkZWZhdWx0c7dtZGVmYXVsdFN0cmluZ2NieWVuZGVmYXVsdEJvb2xlYW71a2RlZmF1bHRMaXN0gWFhcGRlZmF1bHRUaW1lc3RhbXDB+z/wAAAAAAAAa2RlZmF1bHRCbG9iQmhpa2RlZmF1bHRCeXRlAmxkZWZhdWx0U2hvcnQCbmRlZmF1bHRJbnRlZ2VyFGtkZWZhdWx0TG9uZxjIbGRlZmF1bHRGbG9hdPpAAAAAbWRlZmF1bHREb3VibGX7QAAAAAAAAABqZGVmYXVsdE1hcKFkbmFtZWRKYWNra2RlZmF1bHRFbnVtY0JBUm5kZWZhdWx0SW50RW51bQJrZW1wdHlTdHJpbmdjZm9vbGZhbHNlQm9vbGVhbvVpZW1wdHlCbG9iQmhpaHplcm9CeXRlAWl6ZXJvU2hvcnQBa3plcm9JbnRlZ2VyAWh6ZXJvTG9uZwFpemVyb0Zsb2F0+j+AAABqemVyb0RvdWJsZfs/8AAAAAAAAP8=`; + const unequalParts: any = compareEquivalentCborBodies(bodyString, r.body); + expect(unequalParts).toBeUndefined(); + } +}); + +/** + * Any time a value is provided for a member in the top level of input, it is used, regardless of if its the default. + */ +it.skip("RpcV2CborClientUsesExplicitlyProvidedValuesInTopLevel:Request", async () => { + const client = new RpcV2ProtocolClient({ + ...clientParams, + requestHandler: new RequestSerializationTestHandler(), + }); + + const command = new OperationWithDefaultsCommand({ + topLevelDefault: "hi", + otherTopLevelDefault: 0, + } as any); + try { + await client.send(command); + fail("Expected an EXPECTED_REQUEST_SERIALIZATION_ERROR to be thrown"); + return; + } catch (err) { + if (!(err instanceof EXPECTED_REQUEST_SERIALIZATION_ERROR)) { + fail(err); + return; + } + const r = err.request; + expect(r.method).toBe("POST"); + expect(r.path).toBe("/service/RpcV2Protocol/operation/OperationWithDefaults"); + expect(r.headers["content-length"]).toBeDefined(); + + expect(r.headers["content-type"]).toBeDefined(); + expect(r.headers["content-type"]).toBe("application/cbor"); + expect(r.headers["smithy-protocol"]).toBeDefined(); + expect(r.headers["smithy-protocol"]).toBe("rpc-v2-cbor"); + + expect(r.body).toBeDefined(); + const bodyString = `v290b3BMZXZlbERlZmF1bHRiaGl0b3RoZXJUb3BMZXZlbERlZmF1bHQA/w==`; + const unequalParts: any = compareEquivalentCborBodies(bodyString, r.body); + expect(unequalParts).toBeUndefined(); + } +}); + +/** + * Typically, non top-level members would have defaults filled in, but if they have the clientOptional trait, the defaults should be ignored. + */ +it.skip("RpcV2CborClientIgnoresNonTopLevelDefaultsOnMembersWithClientOptional:Request", async () => { + const client = new RpcV2ProtocolClient({ + ...clientParams, + requestHandler: new RequestSerializationTestHandler(), + }); + + const command = new OperationWithDefaultsCommand({ + clientOptionalDefaults: {} as any, + } as any); + try { + await client.send(command); + fail("Expected an EXPECTED_REQUEST_SERIALIZATION_ERROR to be thrown"); + return; + } catch (err) { + if (!(err instanceof EXPECTED_REQUEST_SERIALIZATION_ERROR)) { + fail(err); + return; + } + const r = err.request; + expect(r.method).toBe("POST"); + expect(r.path).toBe("/service/RpcV2Protocol/operation/OperationWithDefaults"); + expect(r.headers["content-length"]).toBeDefined(); + + expect(r.headers["content-type"]).toBeDefined(); + expect(r.headers["content-type"]).toBe("application/cbor"); + expect(r.headers["smithy-protocol"]).toBeDefined(); + expect(r.headers["smithy-protocol"]).toBe("rpc-v2-cbor"); + + expect(r.body).toBeDefined(); + const bodyString = `v3ZjbGllbnRPcHRpb25hbERlZmF1bHRzoP8=`; + const unequalParts: any = compareEquivalentCborBodies(bodyString, r.body); + expect(unequalParts).toBeUndefined(); + } +}); + +/** + * Client populates default values when missing in response. + */ +it.skip("RpcV2CborClientPopulatesDefaultsValuesWhenMissingInResponse:Response", async () => { + const client = new RpcV2ProtocolClient({ + ...clientParams, + requestHandler: new ResponseDeserializationTestHandler( + true, + 200, + { + "smithy-protocol": "rpc-v2-cbor", + "content-type": "application/cbor", + }, + `v/8=` + ), + }); + + const params: any = {}; + const command = new OperationWithDefaultsCommand(params); + + let r: any; + try { + r = await client.send(command); + } catch (err) { + fail("Expected a valid response to be returned, got " + err); + return; + } + expect(r["$metadata"].httpStatusCode).toBe(200); + const paramsToValidate: any = [ + { + defaultString: "hi", + defaultBoolean: true, + defaultList: [], + defaultTimestamp: new Date(0 * 1000), + defaultBlob: Uint8Array.from("abc", (c) => c.charCodeAt(0)), + defaultByte: 1, + defaultShort: 1, + defaultInteger: 10, + defaultLong: 100, + defaultFloat: 1.0, + defaultDouble: 1.0, + defaultMap: {}, + defaultEnum: "FOO", + defaultIntEnum: 1, + emptyString: "", + falseBoolean: false, + emptyBlob: Uint8Array.from("", (c) => c.charCodeAt(0)), + zeroByte: 0, + zeroShort: 0, + zeroInteger: 0, + zeroLong: 0, + zeroFloat: 0.0, + zeroDouble: 0.0, + }, + ][0]; + Object.keys(paramsToValidate).forEach((param) => { + expect(r[param]).toBeDefined(); + expect(equivalentContents(paramsToValidate[param], r[param])).toBe(true); + }); +}); + +/** + * Client ignores default values if member values are present in the response. + */ +it.skip("RpcV2CborClientIgnoresDefaultValuesIfMemberValuesArePresentInResponse:Response", async () => { + const client = new RpcV2ProtocolClient({ + ...clientParams, + requestHandler: new ResponseDeserializationTestHandler( + true, + 200, + { + "smithy-protocol": "rpc-v2-cbor", + "content-type": "application/cbor", + }, + `v21kZWZhdWx0U3RyaW5nY2J5ZW5kZWZhdWx0Qm9vbGVhbvRrZGVmYXVsdExpc3SBYWFwZGVmYXVsdFRpbWVzdGFtcMH7QAAAAAAAAABrZGVmYXVsdEJsb2JCaGlrZGVmYXVsdEJ5dGUCbGRlZmF1bHRTaG9ydAJuZGVmYXVsdEludGVnZXIUa2RlZmF1bHRMb25nGMhsZGVmYXVsdEZsb2F0+kAAAABtZGVmYXVsdERvdWJsZftAAAAAAAAAAGpkZWZhdWx0TWFwoWRuYW1lZEphY2trZGVmYXVsdEVudW1jQkFSbmRlZmF1bHRJbnRFbnVtAmtlbXB0eVN0cmluZ2Nmb29sZmFsc2VCb29sZWFu9WllbXB0eUJsb2JCaGloemVyb0J5dGUBaXplcm9TaG9ydAFremVyb0ludGVnZXIBaHplcm9Mb25nAWl6ZXJvRmxvYXT6P4AAAGp6ZXJvRG91Ymxl+z/wAAAAAAAA/w==` + ), + }); + + const params: any = {}; + const command = new OperationWithDefaultsCommand(params); + + let r: any; + try { + r = await client.send(command); + } catch (err) { + fail("Expected a valid response to be returned, got " + err); + return; + } + expect(r["$metadata"].httpStatusCode).toBe(200); + const paramsToValidate: any = [ + { + defaultString: "bye", + defaultBoolean: false, + defaultList: ["a"], + defaultTimestamp: new Date(2 * 1000), + defaultBlob: Uint8Array.from("hi", (c) => c.charCodeAt(0)), + defaultByte: 2, + defaultShort: 2, + defaultInteger: 20, + defaultLong: 200, + defaultFloat: 2.0, + defaultDouble: 2.0, + defaultMap: { + name: "Jack", + }, + defaultEnum: "BAR", + defaultIntEnum: 2, + emptyString: "foo", + falseBoolean: true, + emptyBlob: Uint8Array.from("hi", (c) => c.charCodeAt(0)), + zeroByte: 1, + zeroShort: 1, + zeroInteger: 1, + zeroLong: 1, + zeroFloat: 1.0, + zeroDouble: 1.0, + }, + ][0]; + Object.keys(paramsToValidate).forEach((param) => { + expect(r[param]).toBeDefined(); + expect(equivalentContents(paramsToValidate[param], r[param])).toBe(true); + }); +}); + +/** + * When input is empty we write CBOR equivalent of {} + */ +it("optional_input:Request", async () => { + const client = new RpcV2ProtocolClient({ + ...clientParams, + requestHandler: new RequestSerializationTestHandler(), + }); + + const command = new OptionalInputOutputCommand({} as any); + try { + await client.send(command); + fail("Expected an EXPECTED_REQUEST_SERIALIZATION_ERROR to be thrown"); + return; + } catch (err) { + if (!(err instanceof EXPECTED_REQUEST_SERIALIZATION_ERROR)) { + fail(err); + return; + } + const r = err.request; + expect(r.method).toBe("POST"); + expect(r.path).toBe("/service/RpcV2Protocol/operation/OptionalInputOutput"); + + expect(r.headers["x-amz-target"]).toBeUndefined(); + + expect(r.headers["content-type"]).toBeDefined(); + expect(r.headers["content-type"]).toBe("application/cbor"); + expect(r.headers["smithy-protocol"]).toBeDefined(); + expect(r.headers["smithy-protocol"]).toBe("rpc-v2-cbor"); + + expect(r.body).toBeDefined(); + const bodyString = `v/8=`; + const unequalParts: any = compareEquivalentCborBodies(bodyString, r.body); + expect(unequalParts).toBeUndefined(); + } +}); + +/** + * When output is empty we write CBOR equivalent of {} + */ +it("optional_output:Response", async () => { + const client = new RpcV2ProtocolClient({ + ...clientParams, + requestHandler: new ResponseDeserializationTestHandler( + true, + 200, + { + "smithy-protocol": "rpc-v2-cbor", + "content-type": "application/cbor", + }, + `v/8=` + ), + }); + + const params: any = {}; + const command = new OptionalInputOutputCommand(params); + + let r: any; + try { + r = await client.send(command); + } catch (err) { + fail("Expected a valid response to be returned, got " + err); + return; + } + expect(r["$metadata"].httpStatusCode).toBe(200); +}); + +/** + * Serializes recursive structures + */ +it("RpcV2CborRecursiveShapes:Request", async () => { + const client = new RpcV2ProtocolClient({ + ...clientParams, + requestHandler: new RequestSerializationTestHandler(), + }); + + const command = new RecursiveShapesCommand({ + nested: { + foo: "Foo1", + nested: { + bar: "Bar1", + recursiveMember: { + foo: "Foo2", + nested: { + bar: "Bar2", + } as any, + } as any, + } as any, + } as any, + } as any); + try { + await client.send(command); + fail("Expected an EXPECTED_REQUEST_SERIALIZATION_ERROR to be thrown"); + return; + } catch (err) { + if (!(err instanceof EXPECTED_REQUEST_SERIALIZATION_ERROR)) { + fail(err); + return; + } + const r = err.request; + expect(r.method).toBe("POST"); + expect(r.path).toBe("/service/RpcV2Protocol/operation/RecursiveShapes"); + expect(r.headers["content-length"]).toBeDefined(); + + expect(r.headers["content-type"]).toBeDefined(); + expect(r.headers["content-type"]).toBe("application/cbor"); + expect(r.headers["smithy-protocol"]).toBeDefined(); + expect(r.headers["smithy-protocol"]).toBe("rpc-v2-cbor"); + + expect(r.body).toBeDefined(); + const bodyString = `v2ZuZXN0ZWS/Y2Zvb2RGb28xZm5lc3RlZL9jYmFyZEJhcjFvcmVjdXJzaXZlTWVtYmVyv2Nmb29kRm9vMmZuZXN0ZWS/Y2JhcmRCYXIy//////8=`; + const unequalParts: any = compareEquivalentCborBodies(bodyString, r.body); + expect(unequalParts).toBeUndefined(); + } +}); + +/** + * Serializes recursive structures + */ +it("RpcV2CborRecursiveShapes:Response", async () => { + const client = new RpcV2ProtocolClient({ + ...clientParams, + requestHandler: new ResponseDeserializationTestHandler( + true, + 200, + { + "smithy-protocol": "rpc-v2-cbor", + "content-type": "application/cbor", + }, + `v2ZuZXN0ZWS/Y2Zvb2RGb28xZm5lc3RlZL9jYmFyZEJhcjFvcmVjdXJzaXZlTWVtYmVyv2Nmb29kRm9vMmZuZXN0ZWS/Y2JhcmRCYXIy//////8=` + ), + }); + + const params: any = {}; + const command = new RecursiveShapesCommand(params); + + let r: any; + try { + r = await client.send(command); + } catch (err) { + fail("Expected a valid response to be returned, got " + err); + return; + } + expect(r["$metadata"].httpStatusCode).toBe(200); + const paramsToValidate: any = [ + { + nested: { + foo: "Foo1", + nested: { + bar: "Bar1", + recursiveMember: { + foo: "Foo2", + nested: { + bar: "Bar2", + }, + }, + }, + }, + }, + ][0]; + Object.keys(paramsToValidate).forEach((param) => { + expect(r[param]).toBeDefined(); + expect(equivalentContents(paramsToValidate[param], r[param])).toBe(true); + }); +}); + +/** + * Deserializes recursive structures encoded using a map with definite length + */ +it("RpcV2CborRecursiveShapesUsingDefiniteLength:Response", async () => { + const client = new RpcV2ProtocolClient({ + ...clientParams, + requestHandler: new ResponseDeserializationTestHandler( + true, + 200, + { + "smithy-protocol": "rpc-v2-cbor", + "content-type": "application/cbor", + }, + `oWZuZXN0ZWSiY2Zvb2RGb28xZm5lc3RlZKJjYmFyZEJhcjFvcmVjdXJzaXZlTWVtYmVyomNmb29kRm9vMmZuZXN0ZWShY2JhcmRCYXIy` + ), + }); + + const params: any = {}; + const command = new RecursiveShapesCommand(params); + + let r: any; + try { + r = await client.send(command); + } catch (err) { + fail("Expected a valid response to be returned, got " + err); + return; + } + expect(r["$metadata"].httpStatusCode).toBe(200); + const paramsToValidate: any = [ + { + nested: { + foo: "Foo1", + nested: { + bar: "Bar1", + recursiveMember: { + foo: "Foo2", + nested: { + bar: "Bar2", + }, + }, + }, + }, + }, + ][0]; + Object.keys(paramsToValidate).forEach((param) => { + expect(r[param]).toBeDefined(); + expect(equivalentContents(paramsToValidate[param], r[param])).toBe(true); + }); +}); + +/** + * Serializes maps + */ +it("RpcV2CborMaps:Request", async () => { + const client = new RpcV2ProtocolClient({ + ...clientParams, + requestHandler: new RequestSerializationTestHandler(), + }); + + const command = new RpcV2CborDenseMapsCommand({ + denseStructMap: { + foo: { + hi: "there", + } as any, + baz: { + hi: "bye", + } as any, + } as any, + } as any); + try { + await client.send(command); + fail("Expected an EXPECTED_REQUEST_SERIALIZATION_ERROR to be thrown"); + return; + } catch (err) { + if (!(err instanceof EXPECTED_REQUEST_SERIALIZATION_ERROR)) { + fail(err); + return; + } + const r = err.request; + expect(r.method).toBe("POST"); + expect(r.path).toBe("/service/RpcV2Protocol/operation/RpcV2CborDenseMaps"); + expect(r.headers["content-length"]).toBeDefined(); + + expect(r.headers["content-type"]).toBeDefined(); + expect(r.headers["content-type"]).toBe("application/cbor"); + expect(r.headers["smithy-protocol"]).toBeDefined(); + expect(r.headers["smithy-protocol"]).toBe("rpc-v2-cbor"); + + expect(r.body).toBeDefined(); + const bodyString = `oW5kZW5zZVN0cnVjdE1hcKJjZm9voWJoaWV0aGVyZWNiYXqhYmhpY2J5ZQ==`; + const unequalParts: any = compareEquivalentCborBodies(bodyString, r.body); + expect(unequalParts).toBeUndefined(); + } +}); + +/** + * Ensure that 0 and false are sent over the wire in all maps and lists + */ +it("RpcV2CborSerializesZeroValuesInMaps:Request", async () => { + const client = new RpcV2ProtocolClient({ + ...clientParams, + requestHandler: new RequestSerializationTestHandler(), + }); + + const command = new RpcV2CborDenseMapsCommand({ + denseNumberMap: { + x: 0, + } as any, + denseBooleanMap: { + x: false, + } as any, + } as any); + try { + await client.send(command); + fail("Expected an EXPECTED_REQUEST_SERIALIZATION_ERROR to be thrown"); + return; + } catch (err) { + if (!(err instanceof EXPECTED_REQUEST_SERIALIZATION_ERROR)) { + fail(err); + return; + } + const r = err.request; + expect(r.method).toBe("POST"); + expect(r.path).toBe("/service/RpcV2Protocol/operation/RpcV2CborDenseMaps"); + expect(r.headers["content-length"]).toBeDefined(); + + expect(r.headers["content-type"]).toBeDefined(); + expect(r.headers["content-type"]).toBe("application/cbor"); + expect(r.headers["smithy-protocol"]).toBeDefined(); + expect(r.headers["smithy-protocol"]).toBe("rpc-v2-cbor"); + + expect(r.body).toBeDefined(); + const bodyString = `om5kZW5zZU51bWJlck1hcKFheABvZGVuc2VCb29sZWFuTWFwoWF49A==`; + const unequalParts: any = compareEquivalentCborBodies(bodyString, r.body); + expect(unequalParts).toBeUndefined(); + } +}); + +/** + * A request that contains a dense map of sets. + */ +it("RpcV2CborSerializesDenseSetMap:Request", async () => { + const client = new RpcV2ProtocolClient({ + ...clientParams, + requestHandler: new RequestSerializationTestHandler(), + }); + + const command = new RpcV2CborDenseMapsCommand({ + denseSetMap: { + x: [], + y: ["a", "b"], + } as any, + } as any); + try { + await client.send(command); + fail("Expected an EXPECTED_REQUEST_SERIALIZATION_ERROR to be thrown"); + return; + } catch (err) { + if (!(err instanceof EXPECTED_REQUEST_SERIALIZATION_ERROR)) { + fail(err); + return; + } + const r = err.request; + expect(r.method).toBe("POST"); + expect(r.path).toBe("/service/RpcV2Protocol/operation/RpcV2CborDenseMaps"); + expect(r.headers["content-length"]).toBeDefined(); + + expect(r.headers["content-type"]).toBeDefined(); + expect(r.headers["content-type"]).toBe("application/cbor"); + expect(r.headers["smithy-protocol"]).toBeDefined(); + expect(r.headers["smithy-protocol"]).toBe("rpc-v2-cbor"); + + expect(r.body).toBeDefined(); + const bodyString = `oWtkZW5zZVNldE1hcKJheIBheYJhYWFi`; + const unequalParts: any = compareEquivalentCborBodies(bodyString, r.body); + expect(unequalParts).toBeUndefined(); + } +}); + +/** + * Deserializes maps + */ +it("RpcV2CborMaps:Response", async () => { + const client = new RpcV2ProtocolClient({ + ...clientParams, + requestHandler: new ResponseDeserializationTestHandler( + true, + 200, + { + "smithy-protocol": "rpc-v2-cbor", + "content-type": "application/cbor", + }, + `oW5kZW5zZVN0cnVjdE1hcKJjZm9voWJoaWV0aGVyZWNiYXqhYmhpY2J5ZQ==` + ), + }); + + const params: any = {}; + const command = new RpcV2CborDenseMapsCommand(params); + + let r: any; + try { + r = await client.send(command); + } catch (err) { + fail("Expected a valid response to be returned, got " + err); + return; + } + expect(r["$metadata"].httpStatusCode).toBe(200); + const paramsToValidate: any = [ + { + denseStructMap: { + foo: { + hi: "there", + }, + baz: { + hi: "bye", + }, + }, + }, + ][0]; + Object.keys(paramsToValidate).forEach((param) => { + expect(r[param]).toBeDefined(); + expect(equivalentContents(paramsToValidate[param], r[param])).toBe(true); + }); +}); + +/** + * Ensure that 0 and false are sent over the wire in all maps and lists + */ +it("RpcV2CborDeserializesZeroValuesInMaps:Response", async () => { + const client = new RpcV2ProtocolClient({ + ...clientParams, + requestHandler: new ResponseDeserializationTestHandler( + true, + 200, + { + "smithy-protocol": "rpc-v2-cbor", + "content-type": "application/cbor", + }, + `om5kZW5zZU51bWJlck1hcKFheABvZGVuc2VCb29sZWFuTWFwoWF49A==` + ), + }); + + const params: any = {}; + const command = new RpcV2CborDenseMapsCommand(params); + + let r: any; + try { + r = await client.send(command); + } catch (err) { + fail("Expected a valid response to be returned, got " + err); + return; + } + expect(r["$metadata"].httpStatusCode).toBe(200); + const paramsToValidate: any = [ + { + denseNumberMap: { + x: 0, + }, + denseBooleanMap: { + x: false, + }, + }, + ][0]; + Object.keys(paramsToValidate).forEach((param) => { + expect(r[param]).toBeDefined(); + expect(equivalentContents(paramsToValidate[param], r[param])).toBe(true); + }); +}); + +/** + * A response that contains a dense map of sets + */ +it("RpcV2CborDeserializesDenseSetMap:Response", async () => { + const client = new RpcV2ProtocolClient({ + ...clientParams, + requestHandler: new ResponseDeserializationTestHandler( + true, + 200, + { + "smithy-protocol": "rpc-v2-cbor", + "content-type": "application/cbor", + }, + `oWtkZW5zZVNldE1hcKJheIBheYJhYWFi` + ), + }); + + const params: any = {}; + const command = new RpcV2CborDenseMapsCommand(params); + + let r: any; + try { + r = await client.send(command); + } catch (err) { + fail("Expected a valid response to be returned, got " + err); + return; + } + expect(r["$metadata"].httpStatusCode).toBe(200); + const paramsToValidate: any = [ + { + denseSetMap: { + x: [], + y: ["a", "b"], + }, + }, + ][0]; + Object.keys(paramsToValidate).forEach((param) => { + expect(r[param]).toBeDefined(); + expect(equivalentContents(paramsToValidate[param], r[param])).toBe(true); + }); +}); + +/** + * Clients SHOULD tolerate seeing a null value in a dense map, and they SHOULD + * drop the null key-value pair. + */ +it.skip("RpcV2CborDeserializesDenseSetMapAndSkipsNull:Response", async () => { + const client = new RpcV2ProtocolClient({ + ...clientParams, + requestHandler: new ResponseDeserializationTestHandler( + true, + 200, + { + "smithy-protocol": "rpc-v2-cbor", + "content-type": "application/cbor", + }, + `oWtkZW5zZVNldE1hcKNheIBheYJhYWFiYXr2` + ), + }); + + const params: any = {}; + const command = new RpcV2CborDenseMapsCommand(params); + + let r: any; + try { + r = await client.send(command); + } catch (err) { + fail("Expected a valid response to be returned, got " + err); + return; + } + expect(r["$metadata"].httpStatusCode).toBe(200); + const paramsToValidate: any = [ + { + denseSetMap: { + x: [], + y: ["a", "b"], + }, + }, + ][0]; + Object.keys(paramsToValidate).forEach((param) => { + expect(r[param]).toBeDefined(); + expect(equivalentContents(paramsToValidate[param], r[param])).toBe(true); + }); +}); + +/** + * Serializes RpcV2 Cbor lists + */ +it("RpcV2CborLists:Request", async () => { + const client = new RpcV2ProtocolClient({ + ...clientParams, + requestHandler: new RequestSerializationTestHandler(), + }); + + const command = new RpcV2CborListsCommand({ + stringList: ["foo", "bar"], + stringSet: ["foo", "bar"], + integerList: [1, 2], + booleanList: [true, false], + timestampList: [new Date(1398796238000), new Date(1398796238000)], + enumList: ["Foo", "0"], + intEnumList: [1, 2], + nestedStringList: [ + ["foo", "bar"], + ["baz", "qux"], + ], + structureList: [ + { + a: "1", + b: "2", + } as any, + { + a: "3", + b: "4", + } as any, + ], + blobList: [Uint8Array.from("foo", (c) => c.charCodeAt(0)), Uint8Array.from("bar", (c) => c.charCodeAt(0))], + } as any); + try { + await client.send(command); + fail("Expected an EXPECTED_REQUEST_SERIALIZATION_ERROR to be thrown"); + return; + } catch (err) { + if (!(err instanceof EXPECTED_REQUEST_SERIALIZATION_ERROR)) { + fail(err); + return; + } + const r = err.request; + expect(r.method).toBe("POST"); + expect(r.path).toBe("/service/RpcV2Protocol/operation/RpcV2CborLists"); + expect(r.headers["content-length"]).toBeDefined(); + + expect(r.headers["content-type"]).toBeDefined(); + expect(r.headers["content-type"]).toBe("application/cbor"); + expect(r.headers["smithy-protocol"]).toBeDefined(); + expect(r.headers["smithy-protocol"]).toBe("rpc-v2-cbor"); + + expect(r.body).toBeDefined(); + const bodyString = `v2pzdHJpbmdMaXN0gmNmb29jYmFyaXN0cmluZ1NldIJjZm9vY2JhcmtpbnRlZ2VyTGlzdIIBAmtib29sZWFuTGlzdIL19G10aW1lc3RhbXBMaXN0gsH7QdTX+/OAAADB+0HU1/vzgAAAaGVudW1MaXN0gmNGb29hMGtpbnRFbnVtTGlzdIIBAnBuZXN0ZWRTdHJpbmdMaXN0goJjZm9vY2JhcoJjYmF6Y3F1eG1zdHJ1Y3R1cmVMaXN0gqJhYWExYWJhMqJhYWEzYWJhNGhibG9iTGlzdIJDZm9vQ2Jhcv8=`; + const unequalParts: any = compareEquivalentCborBodies(bodyString, r.body); + expect(unequalParts).toBeUndefined(); + } +}); + +/** + * Serializes empty JSON lists + */ +it("RpcV2CborListsEmpty:Request", async () => { + const client = new RpcV2ProtocolClient({ + ...clientParams, + requestHandler: new RequestSerializationTestHandler(), + }); + + const command = new RpcV2CborListsCommand({ + stringList: [], + } as any); + try { + await client.send(command); + fail("Expected an EXPECTED_REQUEST_SERIALIZATION_ERROR to be thrown"); + return; + } catch (err) { + if (!(err instanceof EXPECTED_REQUEST_SERIALIZATION_ERROR)) { + fail(err); + return; + } + const r = err.request; + expect(r.method).toBe("POST"); + expect(r.path).toBe("/service/RpcV2Protocol/operation/RpcV2CborLists"); + expect(r.headers["content-length"]).toBeDefined(); + + expect(r.headers["content-type"]).toBeDefined(); + expect(r.headers["content-type"]).toBe("application/cbor"); + expect(r.headers["smithy-protocol"]).toBeDefined(); + expect(r.headers["smithy-protocol"]).toBe("rpc-v2-cbor"); + + expect(r.body).toBeDefined(); + const bodyString = `v2pzdHJpbmdMaXN0n///`; + const unequalParts: any = compareEquivalentCborBodies(bodyString, r.body); + expect(unequalParts).toBeUndefined(); + } +}); + +/** + * Serializes empty JSON definite length lists + */ +it("RpcV2CborListsEmptyUsingDefiniteLength:Request", async () => { + const client = new RpcV2ProtocolClient({ + ...clientParams, + requestHandler: new RequestSerializationTestHandler(), + }); + + const command = new RpcV2CborListsCommand({ + stringList: [], + } as any); + try { + await client.send(command); + fail("Expected an EXPECTED_REQUEST_SERIALIZATION_ERROR to be thrown"); + return; + } catch (err) { + if (!(err instanceof EXPECTED_REQUEST_SERIALIZATION_ERROR)) { + fail(err); + return; + } + const r = err.request; + expect(r.method).toBe("POST"); + expect(r.path).toBe("/service/RpcV2Protocol/operation/RpcV2CborLists"); + expect(r.headers["content-length"]).toBeDefined(); + + expect(r.headers["content-type"]).toBeDefined(); + expect(r.headers["content-type"]).toBe("application/cbor"); + expect(r.headers["smithy-protocol"]).toBeDefined(); + expect(r.headers["smithy-protocol"]).toBe("rpc-v2-cbor"); + + expect(r.body).toBeDefined(); + const bodyString = `oWpzdHJpbmdMaXN0gA==`; + const unequalParts: any = compareEquivalentCborBodies(bodyString, r.body); + expect(unequalParts).toBeUndefined(); + } +}); + +/** + * Serializes RpcV2 Cbor lists + */ +it("RpcV2CborLists:Response", async () => { + const client = new RpcV2ProtocolClient({ + ...clientParams, + requestHandler: new ResponseDeserializationTestHandler( + true, + 200, + { + "smithy-protocol": "rpc-v2-cbor", + "content-type": "application/cbor", + }, + `v2pzdHJpbmdMaXN0n2Nmb29jYmFy/2lzdHJpbmdTZXSfY2Zvb2NiYXL/a2ludGVnZXJMaXN0nwEC/2tib29sZWFuTGlzdJ/19P9tdGltZXN0YW1wTGlzdJ/B+0HU1/vzgAAAwftB1Nf784AAAP9oZW51bUxpc3SfY0Zvb2Ew/2tpbnRFbnVtTGlzdJ8BAv9wbmVzdGVkU3RyaW5nTGlzdJ+fY2Zvb2NiYXL/n2NiYXpjcXV4//9tc3RydWN0dXJlTGlzdJ+/YWFhMWFiYTL/v2FhYTNhYmE0//9oYmxvYkxpc3SfQ2Zvb0NiYXL//w==` + ), + }); + + const params: any = {}; + const command = new RpcV2CborListsCommand(params); + + let r: any; + try { + r = await client.send(command); + } catch (err) { + fail("Expected a valid response to be returned, got " + err); + return; + } + expect(r["$metadata"].httpStatusCode).toBe(200); + const paramsToValidate: any = [ + { + stringList: ["foo", "bar"], + stringSet: ["foo", "bar"], + integerList: [1, 2], + booleanList: [true, false], + timestampList: [new Date(1398796238 * 1000), new Date(1398796238 * 1000)], + enumList: ["Foo", "0"], + intEnumList: [1, 2], + nestedStringList: [ + ["foo", "bar"], + ["baz", "qux"], + ], + structureList: [ + { + a: "1", + b: "2", + }, + { + a: "3", + b: "4", + }, + ], + blobList: [Uint8Array.from("foo", (c) => c.charCodeAt(0)), Uint8Array.from("bar", (c) => c.charCodeAt(0))], + }, + ][0]; + Object.keys(paramsToValidate).forEach((param) => { + expect(r[param]).toBeDefined(); + expect(equivalentContents(paramsToValidate[param], r[param])).toBe(true); + }); +}); + +/** + * Serializes empty RpcV2 Cbor lists + */ +it("RpcV2CborListsEmpty:Response", async () => { + const client = new RpcV2ProtocolClient({ + ...clientParams, + requestHandler: new ResponseDeserializationTestHandler( + true, + 200, + { + "smithy-protocol": "rpc-v2-cbor", + "content-type": "application/cbor", + }, + `v2pzdHJpbmdMaXN0n///` + ), + }); + + const params: any = {}; + const command = new RpcV2CborListsCommand(params); + + let r: any; + try { + r = await client.send(command); + } catch (err) { + fail("Expected a valid response to be returned, got " + err); + return; + } + expect(r["$metadata"].httpStatusCode).toBe(200); + const paramsToValidate: any = [ + { + stringList: [], + }, + ][0]; + Object.keys(paramsToValidate).forEach((param) => { + expect(r[param]).toBeDefined(); + expect(equivalentContents(paramsToValidate[param], r[param])).toBe(true); + }); +}); + +/** + * Can deserialize indefinite length text strings inside an indefinite length list + */ +it("RpcV2CborIndefiniteStringInsideIndefiniteListCanDeserialize:Response", async () => { + const client = new RpcV2ProtocolClient({ + ...clientParams, + requestHandler: new ResponseDeserializationTestHandler( + true, + 200, + { + "smithy-protocol": "rpc-v2-cbor", + "content-type": "application/cbor", + }, + `v2pzdHJpbmdMaXN0n394HUFuIGV4YW1wbGUgaW5kZWZpbml0ZSBzdHJpbmcsdyB3aGljaCB3aWxsIGJlIGNodW5rZWQsbiBvbiBlYWNoIGNvbW1h/394NUFub3RoZXIgZXhhbXBsZSBpbmRlZmluaXRlIHN0cmluZyB3aXRoIG9ubHkgb25lIGNodW5r/3ZUaGlzIGlzIGEgcGxhaW4gc3RyaW5n//8=` + ), + }); + + const params: any = {}; + const command = new RpcV2CborListsCommand(params); + + let r: any; + try { + r = await client.send(command); + } catch (err) { + fail("Expected a valid response to be returned, got " + err); + return; + } + expect(r["$metadata"].httpStatusCode).toBe(200); + const paramsToValidate: any = [ + { + stringList: [ + "An example indefinite string, which will be chunked, on each comma", + "Another example indefinite string with only one chunk", + "This is a plain string", + ], + }, + ][0]; + Object.keys(paramsToValidate).forEach((param) => { + expect(r[param]).toBeDefined(); + expect(equivalentContents(paramsToValidate[param], r[param])).toBe(true); + }); +}); + +/** + * Can deserialize indefinite length text strings inside a definite length list + */ +it("RpcV2CborIndefiniteStringInsideDefiniteListCanDeserialize:Response", async () => { + const client = new RpcV2ProtocolClient({ + ...clientParams, + requestHandler: new ResponseDeserializationTestHandler( + true, + 200, + { + "smithy-protocol": "rpc-v2-cbor", + "content-type": "application/cbor", + }, + `oWpzdHJpbmdMaXN0g394HUFuIGV4YW1wbGUgaW5kZWZpbml0ZSBzdHJpbmcsdyB3aGljaCB3aWxsIGJlIGNodW5rZWQsbiBvbiBlYWNoIGNvbW1h/394NUFub3RoZXIgZXhhbXBsZSBpbmRlZmluaXRlIHN0cmluZyB3aXRoIG9ubHkgb25lIGNodW5r/3ZUaGlzIGlzIGEgcGxhaW4gc3RyaW5n` + ), + }); + + const params: any = {}; + const command = new RpcV2CborListsCommand(params); + + let r: any; + try { + r = await client.send(command); + } catch (err) { + fail("Expected a valid response to be returned, got " + err); + return; + } + expect(r["$metadata"].httpStatusCode).toBe(200); + const paramsToValidate: any = [ + { + stringList: [ + "An example indefinite string, which will be chunked, on each comma", + "Another example indefinite string with only one chunk", + "This is a plain string", + ], + }, + ][0]; + Object.keys(paramsToValidate).forEach((param) => { + expect(r[param]).toBeDefined(); + expect(equivalentContents(paramsToValidate[param], r[param])).toBe(true); + }); +}); + +/** + * Serializes sparse maps + */ +it("RpcV2CborSparseMaps:Request", async () => { + const client = new RpcV2ProtocolClient({ + ...clientParams, + requestHandler: new RequestSerializationTestHandler(), + }); + + const command = new RpcV2CborSparseMapsCommand({ + sparseStructMap: { + foo: { + hi: "there", + } as any, + baz: { + hi: "bye", + } as any, + } as any, + } as any); + try { + await client.send(command); + fail("Expected an EXPECTED_REQUEST_SERIALIZATION_ERROR to be thrown"); + return; + } catch (err) { + if (!(err instanceof EXPECTED_REQUEST_SERIALIZATION_ERROR)) { + fail(err); + return; + } + const r = err.request; + expect(r.method).toBe("POST"); + expect(r.path).toBe("/service/RpcV2Protocol/operation/RpcV2CborSparseMaps"); + expect(r.headers["content-length"]).toBeDefined(); + + expect(r.headers["content-type"]).toBeDefined(); + expect(r.headers["content-type"]).toBe("application/cbor"); + expect(r.headers["smithy-protocol"]).toBeDefined(); + expect(r.headers["smithy-protocol"]).toBe("rpc-v2-cbor"); + + expect(r.body).toBeDefined(); + const bodyString = `v29zcGFyc2VTdHJ1Y3RNYXC/Y2Zvb79iaGlldGhlcmX/Y2Jher9iaGljYnll////`; + const unequalParts: any = compareEquivalentCborBodies(bodyString, r.body); + expect(unequalParts).toBeUndefined(); + } +}); + +/** + * Serializes null map values in sparse maps + */ +it("RpcV2CborSerializesNullMapValues:Request", async () => { + const client = new RpcV2ProtocolClient({ + ...clientParams, + requestHandler: new RequestSerializationTestHandler(), + }); + + const command = new RpcV2CborSparseMapsCommand({ + sparseBooleanMap: { + x: null, + } as any, + sparseNumberMap: { + x: null, + } as any, + sparseStringMap: { + x: null, + } as any, + sparseStructMap: { + x: null, + } as any, + } as any); + try { + await client.send(command); + fail("Expected an EXPECTED_REQUEST_SERIALIZATION_ERROR to be thrown"); + return; + } catch (err) { + if (!(err instanceof EXPECTED_REQUEST_SERIALIZATION_ERROR)) { + fail(err); + return; + } + const r = err.request; + expect(r.method).toBe("POST"); + expect(r.path).toBe("/service/RpcV2Protocol/operation/RpcV2CborSparseMaps"); + expect(r.headers["content-length"]).toBeDefined(); + + expect(r.headers["content-type"]).toBeDefined(); + expect(r.headers["content-type"]).toBe("application/cbor"); + expect(r.headers["smithy-protocol"]).toBeDefined(); + expect(r.headers["smithy-protocol"]).toBe("rpc-v2-cbor"); + + expect(r.body).toBeDefined(); + const bodyString = `v3BzcGFyc2VCb29sZWFuTWFwv2F49v9vc3BhcnNlTnVtYmVyTWFwv2F49v9vc3BhcnNlU3RyaW5nTWFwv2F49v9vc3BhcnNlU3RydWN0TWFwv2F49v//`; + const unequalParts: any = compareEquivalentCborBodies(bodyString, r.body); + expect(unequalParts).toBeUndefined(); + } +}); + +/** + * A request that contains a sparse map of sets + */ +it("RpcV2CborSerializesSparseSetMap:Request", async () => { + const client = new RpcV2ProtocolClient({ + ...clientParams, + requestHandler: new RequestSerializationTestHandler(), + }); + + const command = new RpcV2CborSparseMapsCommand({ + sparseSetMap: { + x: [], + y: ["a", "b"], + } as any, + } as any); + try { + await client.send(command); + fail("Expected an EXPECTED_REQUEST_SERIALIZATION_ERROR to be thrown"); + return; + } catch (err) { + if (!(err instanceof EXPECTED_REQUEST_SERIALIZATION_ERROR)) { + fail(err); + return; + } + const r = err.request; + expect(r.method).toBe("POST"); + expect(r.path).toBe("/service/RpcV2Protocol/operation/RpcV2CborSparseMaps"); + expect(r.headers["content-length"]).toBeDefined(); + + expect(r.headers["content-type"]).toBeDefined(); + expect(r.headers["content-type"]).toBe("application/cbor"); + expect(r.headers["smithy-protocol"]).toBeDefined(); + expect(r.headers["smithy-protocol"]).toBe("rpc-v2-cbor"); + + expect(r.body).toBeDefined(); + const bodyString = `v2xzcGFyc2VTZXRNYXC/YXif/2F5n2FhYWL///8=`; + const unequalParts: any = compareEquivalentCborBodies(bodyString, r.body); + expect(unequalParts).toBeUndefined(); + } +}); + +/** + * A request that contains a sparse map of sets. + */ +it("RpcV2CborSerializesSparseSetMapAndRetainsNull:Request", async () => { + const client = new RpcV2ProtocolClient({ + ...clientParams, + requestHandler: new RequestSerializationTestHandler(), + }); + + const command = new RpcV2CborSparseMapsCommand({ + sparseSetMap: { + x: [], + y: ["a", "b"], + z: null, + } as any, + } as any); + try { + await client.send(command); + fail("Expected an EXPECTED_REQUEST_SERIALIZATION_ERROR to be thrown"); + return; + } catch (err) { + if (!(err instanceof EXPECTED_REQUEST_SERIALIZATION_ERROR)) { + fail(err); + return; + } + const r = err.request; + expect(r.method).toBe("POST"); + expect(r.path).toBe("/service/RpcV2Protocol/operation/RpcV2CborSparseMaps"); + expect(r.headers["content-length"]).toBeDefined(); + + expect(r.headers["content-type"]).toBeDefined(); + expect(r.headers["content-type"]).toBe("application/cbor"); + expect(r.headers["smithy-protocol"]).toBeDefined(); + expect(r.headers["smithy-protocol"]).toBe("rpc-v2-cbor"); + + expect(r.body).toBeDefined(); + const bodyString = `v2xzcGFyc2VTZXRNYXC/YXif/2F5n2FhYWL/YXr2//8=`; + const unequalParts: any = compareEquivalentCborBodies(bodyString, r.body); + expect(unequalParts).toBeUndefined(); + } +}); + +/** + * Ensure that 0 and false are sent over the wire in all maps and lists + */ +it("RpcV2CborSerializesZeroValuesInSparseMaps:Request", async () => { + const client = new RpcV2ProtocolClient({ + ...clientParams, + requestHandler: new RequestSerializationTestHandler(), + }); + + const command = new RpcV2CborSparseMapsCommand({ + sparseNumberMap: { + x: 0, + } as any, + sparseBooleanMap: { + x: false, + } as any, + } as any); + try { + await client.send(command); + fail("Expected an EXPECTED_REQUEST_SERIALIZATION_ERROR to be thrown"); + return; + } catch (err) { + if (!(err instanceof EXPECTED_REQUEST_SERIALIZATION_ERROR)) { + fail(err); + return; + } + const r = err.request; + expect(r.method).toBe("POST"); + expect(r.path).toBe("/service/RpcV2Protocol/operation/RpcV2CborSparseMaps"); + expect(r.headers["content-length"]).toBeDefined(); + + expect(r.headers["content-type"]).toBeDefined(); + expect(r.headers["content-type"]).toBe("application/cbor"); + expect(r.headers["smithy-protocol"]).toBeDefined(); + expect(r.headers["smithy-protocol"]).toBe("rpc-v2-cbor"); + + expect(r.body).toBeDefined(); + const bodyString = `v29zcGFyc2VOdW1iZXJNYXC/YXgA/3BzcGFyc2VCb29sZWFuTWFwv2F49P//`; + const unequalParts: any = compareEquivalentCborBodies(bodyString, r.body); + expect(unequalParts).toBeUndefined(); + } +}); + +/** + * Deserializes sparse maps + */ +it("RpcV2CborSparseJsonMaps:Response", async () => { + const client = new RpcV2ProtocolClient({ + ...clientParams, + requestHandler: new ResponseDeserializationTestHandler( + true, + 200, + { + "smithy-protocol": "rpc-v2-cbor", + "content-type": "application/cbor", + }, + `v29zcGFyc2VTdHJ1Y3RNYXC/Y2Zvb79iaGlldGhlcmX/Y2Jher9iaGljYnll////` + ), + }); + + const params: any = {}; + const command = new RpcV2CborSparseMapsCommand(params); + + let r: any; + try { + r = await client.send(command); + } catch (err) { + fail("Expected a valid response to be returned, got " + err); + return; + } + expect(r["$metadata"].httpStatusCode).toBe(200); + const paramsToValidate: any = [ + { + sparseStructMap: { + foo: { + hi: "there", + }, + baz: { + hi: "bye", + }, + }, + }, + ][0]; + Object.keys(paramsToValidate).forEach((param) => { + expect(r[param]).toBeDefined(); + expect(equivalentContents(paramsToValidate[param], r[param])).toBe(true); + }); +}); + +/** + * Deserializes null map values + */ +it("RpcV2CborDeserializesNullMapValues:Response", async () => { + const client = new RpcV2ProtocolClient({ + ...clientParams, + requestHandler: new ResponseDeserializationTestHandler( + true, + 200, + { + "smithy-protocol": "rpc-v2-cbor", + "content-type": "application/cbor", + }, + `v3BzcGFyc2VCb29sZWFuTWFwv2F49v9vc3BhcnNlTnVtYmVyTWFwv2F49v9vc3BhcnNlU3RyaW5nTWFwv2F49v9vc3BhcnNlU3RydWN0TWFwv2F49v//` + ), + }); + + const params: any = {}; + const command = new RpcV2CborSparseMapsCommand(params); + + let r: any; + try { + r = await client.send(command); + } catch (err) { + fail("Expected a valid response to be returned, got " + err); + return; + } + expect(r["$metadata"].httpStatusCode).toBe(200); + const paramsToValidate: any = [ + { + sparseBooleanMap: { + x: null, + }, + sparseNumberMap: { + x: null, + }, + sparseStringMap: { + x: null, + }, + sparseStructMap: { + x: null, + }, + }, + ][0]; + Object.keys(paramsToValidate).forEach((param) => { + expect(r[param]).toBeDefined(); + expect(equivalentContents(paramsToValidate[param], r[param])).toBe(true); + }); +}); + +/** + * A response that contains a sparse map of sets + */ +it("RpcV2CborDeserializesSparseSetMap:Response", async () => { + const client = new RpcV2ProtocolClient({ + ...clientParams, + requestHandler: new ResponseDeserializationTestHandler( + true, + 200, + { + "smithy-protocol": "rpc-v2-cbor", + "content-type": "application/cbor", + }, + `v2xzcGFyc2VTZXRNYXC/YXmfYWFhYv9heJ////8=` + ), + }); + + const params: any = {}; + const command = new RpcV2CborSparseMapsCommand(params); + + let r: any; + try { + r = await client.send(command); + } catch (err) { + fail("Expected a valid response to be returned, got " + err); + return; + } + expect(r["$metadata"].httpStatusCode).toBe(200); + const paramsToValidate: any = [ + { + sparseSetMap: { + x: [], + y: ["a", "b"], + }, + }, + ][0]; + Object.keys(paramsToValidate).forEach((param) => { + expect(r[param]).toBeDefined(); + expect(equivalentContents(paramsToValidate[param], r[param])).toBe(true); + }); +}); + +/** + * A response that contains a sparse map of sets with a null + */ +it("RpcV2CborDeserializesSparseSetMapAndRetainsNull:Response", async () => { + const client = new RpcV2ProtocolClient({ + ...clientParams, + requestHandler: new ResponseDeserializationTestHandler( + true, + 200, + { + "smithy-protocol": "rpc-v2-cbor", + "content-type": "application/cbor", + }, + `v2xzcGFyc2VTZXRNYXC/YXif/2F5n2FhYWL/YXr2//8=` + ), + }); + + const params: any = {}; + const command = new RpcV2CborSparseMapsCommand(params); + + let r: any; + try { + r = await client.send(command); + } catch (err) { + fail("Expected a valid response to be returned, got " + err); + return; + } + expect(r["$metadata"].httpStatusCode).toBe(200); + const paramsToValidate: any = [ + { + sparseSetMap: { + x: [], + y: ["a", "b"], + z: null, + }, + }, + ][0]; + Object.keys(paramsToValidate).forEach((param) => { + expect(r[param]).toBeDefined(); + expect(equivalentContents(paramsToValidate[param], r[param])).toBe(true); + }); +}); + +/** + * Ensure that 0 and false are sent over the wire in all maps and lists + */ +it("RpcV2CborDeserializesZeroValuesInSparseMaps:Response", async () => { + const client = new RpcV2ProtocolClient({ + ...clientParams, + requestHandler: new ResponseDeserializationTestHandler( + true, + 200, + { + "smithy-protocol": "rpc-v2-cbor", + "content-type": "application/cbor", + }, + `v29zcGFyc2VOdW1iZXJNYXC/YXgA/3BzcGFyc2VCb29sZWFuTWFwv2F49P//` + ), + }); + + const params: any = {}; + const command = new RpcV2CborSparseMapsCommand(params); + + let r: any; + try { + r = await client.send(command); + } catch (err) { + fail("Expected a valid response to be returned, got " + err); + return; + } + expect(r["$metadata"].httpStatusCode).toBe(200); + const paramsToValidate: any = [ + { + sparseNumberMap: { + x: 0, + }, + sparseBooleanMap: { + x: false, + }, + }, + ][0]; + Object.keys(paramsToValidate).forEach((param) => { + expect(r[param]).toBeDefined(); + expect(equivalentContents(paramsToValidate[param], r[param])).toBe(true); + }); +}); + +/** + * Serializes simple scalar properties + */ +it("RpcV2CborSimpleScalarProperties:Request", async () => { + const client = new RpcV2ProtocolClient({ + ...clientParams, + requestHandler: new RequestSerializationTestHandler(), + }); + + const command = new SimpleScalarPropertiesCommand({ + byteValue: 5, + doubleValue: 1.889, + falseBooleanValue: false, + floatValue: 7.625, + integerValue: 256, + longValue: 9873, + shortValue: 9898, + stringValue: "simple", + trueBooleanValue: true, + blobValue: Uint8Array.from("foo", (c) => c.charCodeAt(0)), + } as any); + try { + await client.send(command); + fail("Expected an EXPECTED_REQUEST_SERIALIZATION_ERROR to be thrown"); + return; + } catch (err) { + if (!(err instanceof EXPECTED_REQUEST_SERIALIZATION_ERROR)) { + fail(err); + return; + } + const r = err.request; + expect(r.method).toBe("POST"); + expect(r.path).toBe("/service/RpcV2Protocol/operation/SimpleScalarProperties"); + expect(r.headers["content-length"]).toBeDefined(); + + expect(r.headers["content-type"]).toBeDefined(); + expect(r.headers["content-type"]).toBe("application/cbor"); + expect(r.headers["smithy-protocol"]).toBeDefined(); + expect(r.headers["smithy-protocol"]).toBe("rpc-v2-cbor"); + + expect(r.body).toBeDefined(); + const bodyString = `v2lieXRlVmFsdWUFa2RvdWJsZVZhbHVl+z/+OVgQYk3TcWZhbHNlQm9vbGVhblZhbHVl9GpmbG9hdFZhbHVl+kD0AABsaW50ZWdlclZhbHVlGQEAaWxvbmdWYWx1ZRkmkWpzaG9ydFZhbHVlGSaqa3N0cmluZ1ZhbHVlZnNpbXBsZXB0cnVlQm9vbGVhblZhbHVl9WlibG9iVmFsdWVDZm9v/w==`; + const unequalParts: any = compareEquivalentCborBodies(bodyString, r.body); + expect(unequalParts).toBeUndefined(); + } +}); + +/** + * RpcV2 Cbor should not serialize null structure values + */ +it("RpcV2CborClientDoesntSerializeNullStructureValues:Request", async () => { + const client = new RpcV2ProtocolClient({ + ...clientParams, + requestHandler: new RequestSerializationTestHandler(), + }); + + const command = new SimpleScalarPropertiesCommand({ + stringValue: null, + } as any); + try { + await client.send(command); + fail("Expected an EXPECTED_REQUEST_SERIALIZATION_ERROR to be thrown"); + return; + } catch (err) { + if (!(err instanceof EXPECTED_REQUEST_SERIALIZATION_ERROR)) { + fail(err); + return; + } + const r = err.request; + expect(r.method).toBe("POST"); + expect(r.path).toBe("/service/RpcV2Protocol/operation/SimpleScalarProperties"); + expect(r.headers["content-length"]).toBeDefined(); + + expect(r.headers["content-type"]).toBeDefined(); + expect(r.headers["content-type"]).toBe("application/cbor"); + expect(r.headers["smithy-protocol"]).toBeDefined(); + expect(r.headers["smithy-protocol"]).toBe("rpc-v2-cbor"); + + expect(r.body).toBeDefined(); + const bodyString = `v/8=`; + const unequalParts: any = compareEquivalentCborBodies(bodyString, r.body); + expect(unequalParts).toBeUndefined(); + } +}); + +/** + * Supports handling NaN float values. + */ +it("RpcV2CborSupportsNaNFloatInputs:Request", async () => { + const client = new RpcV2ProtocolClient({ + ...clientParams, + requestHandler: new RequestSerializationTestHandler(), + }); + + const command = new SimpleScalarPropertiesCommand({ + doubleValue: NaN, + floatValue: NaN, + } as any); + try { + await client.send(command); + fail("Expected an EXPECTED_REQUEST_SERIALIZATION_ERROR to be thrown"); + return; + } catch (err) { + if (!(err instanceof EXPECTED_REQUEST_SERIALIZATION_ERROR)) { + fail(err); + return; + } + const r = err.request; + expect(r.method).toBe("POST"); + expect(r.path).toBe("/service/RpcV2Protocol/operation/SimpleScalarProperties"); + expect(r.headers["content-length"]).toBeDefined(); + + expect(r.headers["content-type"]).toBeDefined(); + expect(r.headers["content-type"]).toBe("application/cbor"); + expect(r.headers["smithy-protocol"]).toBeDefined(); + expect(r.headers["smithy-protocol"]).toBe("rpc-v2-cbor"); + + expect(r.body).toBeDefined(); + const bodyString = `v2tkb3VibGVWYWx1Zft/+AAAAAAAAGpmbG9hdFZhbHVl+n/AAAD/`; + const unequalParts: any = compareEquivalentCborBodies(bodyString, r.body); + expect(unequalParts).toBeUndefined(); + } +}); + +/** + * Supports handling Infinity float values. + */ +it("RpcV2CborSupportsInfinityFloatInputs:Request", async () => { + const client = new RpcV2ProtocolClient({ + ...clientParams, + requestHandler: new RequestSerializationTestHandler(), + }); + + const command = new SimpleScalarPropertiesCommand({ + doubleValue: Infinity, + floatValue: Infinity, + } as any); + try { + await client.send(command); + fail("Expected an EXPECTED_REQUEST_SERIALIZATION_ERROR to be thrown"); + return; + } catch (err) { + if (!(err instanceof EXPECTED_REQUEST_SERIALIZATION_ERROR)) { + fail(err); + return; + } + const r = err.request; + expect(r.method).toBe("POST"); + expect(r.path).toBe("/service/RpcV2Protocol/operation/SimpleScalarProperties"); + expect(r.headers["content-length"]).toBeDefined(); + + expect(r.headers["content-type"]).toBeDefined(); + expect(r.headers["content-type"]).toBe("application/cbor"); + expect(r.headers["smithy-protocol"]).toBeDefined(); + expect(r.headers["smithy-protocol"]).toBe("rpc-v2-cbor"); + + expect(r.body).toBeDefined(); + const bodyString = `v2tkb3VibGVWYWx1Zft/8AAAAAAAAGpmbG9hdFZhbHVl+n+AAAD/`; + const unequalParts: any = compareEquivalentCborBodies(bodyString, r.body); + expect(unequalParts).toBeUndefined(); + } +}); + +/** + * Supports handling Infinity float values. + */ +it("RpcV2CborSupportsNegativeInfinityFloatInputs:Request", async () => { + const client = new RpcV2ProtocolClient({ + ...clientParams, + requestHandler: new RequestSerializationTestHandler(), + }); + + const command = new SimpleScalarPropertiesCommand({ + doubleValue: -Infinity, + floatValue: -Infinity, + } as any); + try { + await client.send(command); + fail("Expected an EXPECTED_REQUEST_SERIALIZATION_ERROR to be thrown"); + return; + } catch (err) { + if (!(err instanceof EXPECTED_REQUEST_SERIALIZATION_ERROR)) { + fail(err); + return; + } + const r = err.request; + expect(r.method).toBe("POST"); + expect(r.path).toBe("/service/RpcV2Protocol/operation/SimpleScalarProperties"); + expect(r.headers["content-length"]).toBeDefined(); + + expect(r.headers["content-type"]).toBeDefined(); + expect(r.headers["content-type"]).toBe("application/cbor"); + expect(r.headers["smithy-protocol"]).toBeDefined(); + expect(r.headers["smithy-protocol"]).toBe("rpc-v2-cbor"); + + expect(r.body).toBeDefined(); + const bodyString = `v2tkb3VibGVWYWx1Zfv/8AAAAAAAAGpmbG9hdFZhbHVl+v+AAAD/`; + const unequalParts: any = compareEquivalentCborBodies(bodyString, r.body); + expect(unequalParts).toBeUndefined(); + } +}); + +/** + * Serializes simple scalar properties + */ +it("RpcV2CborSimpleScalarProperties:Response", async () => { + const client = new RpcV2ProtocolClient({ + ...clientParams, + requestHandler: new ResponseDeserializationTestHandler( + true, + 200, + { + "smithy-protocol": "rpc-v2-cbor", + "content-type": "application/cbor", + }, + `v3B0cnVlQm9vbGVhblZhbHVl9XFmYWxzZUJvb2xlYW5WYWx1ZfRpYnl0ZVZhbHVlBWtkb3VibGVWYWx1Zfs//jlYEGJN02pmbG9hdFZhbHVl+kD0AABsaW50ZWdlclZhbHVlGQEAanNob3J0VmFsdWUZJqprc3RyaW5nVmFsdWVmc2ltcGxlaWJsb2JWYWx1ZUNmb2//` + ), + }); + + const params: any = {}; + const command = new SimpleScalarPropertiesCommand(params); + + let r: any; + try { + r = await client.send(command); + } catch (err) { + fail("Expected a valid response to be returned, got " + err); + return; + } + expect(r["$metadata"].httpStatusCode).toBe(200); + const paramsToValidate: any = [ + { + trueBooleanValue: true, + falseBooleanValue: false, + byteValue: 5, + doubleValue: 1.889, + floatValue: 7.625, + integerValue: 256, + shortValue: 9898, + stringValue: "simple", + blobValue: Uint8Array.from("foo", (c) => c.charCodeAt(0)), + }, + ][0]; + Object.keys(paramsToValidate).forEach((param) => { + expect(r[param]).toBeDefined(); + expect(equivalentContents(paramsToValidate[param], r[param])).toBe(true); + }); +}); + +/** + * Deserializes simple scalar properties encoded using a map with definite length + */ +it("RpcV2CborSimpleScalarPropertiesUsingDefiniteLength:Response", async () => { + const client = new RpcV2ProtocolClient({ + ...clientParams, + requestHandler: new ResponseDeserializationTestHandler( + true, + 200, + { + "smithy-protocol": "rpc-v2-cbor", + "content-type": "application/cbor", + }, + `qXB0cnVlQm9vbGVhblZhbHVl9XFmYWxzZUJvb2xlYW5WYWx1ZfRpYnl0ZVZhbHVlBWtkb3VibGVWYWx1Zfs//jlYEGJN02pmbG9hdFZhbHVl+kD0AABsaW50ZWdlclZhbHVlGQEAanNob3J0VmFsdWUZJqprc3RyaW5nVmFsdWVmc2ltcGxlaWJsb2JWYWx1ZUNmb28=` + ), + }); + + const params: any = {}; + const command = new SimpleScalarPropertiesCommand(params); + + let r: any; + try { + r = await client.send(command); + } catch (err) { + fail("Expected a valid response to be returned, got " + err); + return; + } + expect(r["$metadata"].httpStatusCode).toBe(200); + const paramsToValidate: any = [ + { + trueBooleanValue: true, + falseBooleanValue: false, + byteValue: 5, + doubleValue: 1.889, + floatValue: 7.625, + integerValue: 256, + shortValue: 9898, + stringValue: "simple", + blobValue: Uint8Array.from("foo", (c) => c.charCodeAt(0)), + }, + ][0]; + Object.keys(paramsToValidate).forEach((param) => { + expect(r[param]).toBeDefined(); + expect(equivalentContents(paramsToValidate[param], r[param])).toBe(true); + }); +}); + +/** + * RpcV2 Cbor should not deserialize null structure values + */ +it("RpcV2CborClientDoesntDeserializeNullStructureValues:Response", async () => { + const client = new RpcV2ProtocolClient({ + ...clientParams, + requestHandler: new ResponseDeserializationTestHandler( + true, + 200, + { + "smithy-protocol": "rpc-v2-cbor", + "content-type": "application/cbor", + }, + `v2tzdHJpbmdWYWx1Zfb/` + ), + }); + + const params: any = {}; + const command = new SimpleScalarPropertiesCommand(params); + + let r: any; + try { + r = await client.send(command); + } catch (err) { + fail("Expected a valid response to be returned, got " + err); + return; + } + expect(r["$metadata"].httpStatusCode).toBe(200); +}); + +/** + * Supports handling NaN float values. + */ +it("RpcV2CborSupportsNaNFloatOutputs:Response", async () => { + const client = new RpcV2ProtocolClient({ + ...clientParams, + requestHandler: new ResponseDeserializationTestHandler( + true, + 200, + { + "smithy-protocol": "rpc-v2-cbor", + "content-type": "application/cbor", + }, + `v2tkb3VibGVWYWx1Zft/+AAAAAAAAGpmbG9hdFZhbHVl+n/AAAD/` + ), + }); + + const params: any = {}; + const command = new SimpleScalarPropertiesCommand(params); + + let r: any; + try { + r = await client.send(command); + } catch (err) { + fail("Expected a valid response to be returned, got " + err); + return; + } + expect(r["$metadata"].httpStatusCode).toBe(200); + const paramsToValidate: any = [ + { + doubleValue: NaN, + floatValue: NaN, + }, + ][0]; + Object.keys(paramsToValidate).forEach((param) => { + expect(r[param]).toBeDefined(); + expect(equivalentContents(paramsToValidate[param], r[param])).toBe(true); + }); +}); + +/** + * Supports handling Infinity float values. + */ +it("RpcV2CborSupportsInfinityFloatOutputs:Response", async () => { + const client = new RpcV2ProtocolClient({ + ...clientParams, + requestHandler: new ResponseDeserializationTestHandler( + true, + 200, + { + "smithy-protocol": "rpc-v2-cbor", + "content-type": "application/cbor", + }, + `v2tkb3VibGVWYWx1Zft/8AAAAAAAAGpmbG9hdFZhbHVl+n+AAAD/` + ), + }); + + const params: any = {}; + const command = new SimpleScalarPropertiesCommand(params); + + let r: any; + try { + r = await client.send(command); + } catch (err) { + fail("Expected a valid response to be returned, got " + err); + return; + } + expect(r["$metadata"].httpStatusCode).toBe(200); + const paramsToValidate: any = [ + { + doubleValue: Infinity, + floatValue: Infinity, + }, + ][0]; + Object.keys(paramsToValidate).forEach((param) => { + expect(r[param]).toBeDefined(); + expect(equivalentContents(paramsToValidate[param], r[param])).toBe(true); + }); +}); + +/** + * Supports handling Negative Infinity float values. + */ +it("RpcV2CborSupportsNegativeInfinityFloatOutputs:Response", async () => { + const client = new RpcV2ProtocolClient({ + ...clientParams, + requestHandler: new ResponseDeserializationTestHandler( + true, + 200, + { + "smithy-protocol": "rpc-v2-cbor", + "content-type": "application/cbor", + }, + `v2tkb3VibGVWYWx1Zfv/8AAAAAAAAGpmbG9hdFZhbHVl+v+AAAD/` + ), + }); + + const params: any = {}; + const command = new SimpleScalarPropertiesCommand(params); + + let r: any; + try { + r = await client.send(command); + } catch (err) { + fail("Expected a valid response to be returned, got " + err); + return; + } + expect(r["$metadata"].httpStatusCode).toBe(200); + const paramsToValidate: any = [ + { + doubleValue: -Infinity, + floatValue: -Infinity, + }, + ][0]; + Object.keys(paramsToValidate).forEach((param) => { + expect(r[param]).toBeDefined(); + expect(equivalentContents(paramsToValidate[param], r[param])).toBe(true); + }); +}); + +/** + * Supports upcasting from a smaller byte representation of the same data type. + */ +it("RpcV2CborSupportsUpcastingDataOnDeserialize:Response", async () => { + const client = new RpcV2ProtocolClient({ + ...clientParams, + requestHandler: new ResponseDeserializationTestHandler( + true, + 200, + { + "smithy-protocol": "rpc-v2-cbor", + "content-type": "application/cbor", + }, + `v2tkb3VibGVWYWx1Zfk+AGpmbG9hdFZhbHVl+UegbGludGVnZXJWYWx1ZRg4aWxvbmdWYWx1ZRkBAGpzaG9ydFZhbHVlCv8=` + ), + }); + + const params: any = {}; + const command = new SimpleScalarPropertiesCommand(params); + + let r: any; + try { + r = await client.send(command); + } catch (err) { + fail("Expected a valid response to be returned, got " + err); + return; + } + expect(r["$metadata"].httpStatusCode).toBe(200); + const paramsToValidate: any = [ + { + doubleValue: 1.5, + floatValue: 7.625, + integerValue: 56, + longValue: 256, + shortValue: 10, + }, + ][0]; + Object.keys(paramsToValidate).forEach((param) => { + expect(r[param]).toBeDefined(); + expect(equivalentContents(paramsToValidate[param], r[param])).toBe(true); + }); +}); + +/** + * The client should skip over additional fields that are not part of the structure. This allows a + * client generated against an older Smithy model to be able to communicate with a server that is + * generated against a newer Smithy model. + */ +it("RpcV2CborExtraFieldsInTheBodyShouldBeSkippedByClients:Response", async () => { + const client = new RpcV2ProtocolClient({ + ...clientParams, + requestHandler: new ResponseDeserializationTestHandler( + true, + 200, + { + "smithy-protocol": "rpc-v2-cbor", + "content-type": "application/cbor", + }, + `v2lieXRlVmFsdWUFa2RvdWJsZVZhbHVl+z/+OVgQYk3TcWZhbHNlQm9vbGVhblZhbHVl9GpmbG9hdFZhbHVl+kD0AABrZXh0cmFPYmplY3S/c2luZGVmaW5pdGVMZW5ndGhNYXC/a3dpdGhBbkFycmF5nwECA///cWRlZmluaXRlTGVuZ3RoTWFwo3J3aXRoQURlZmluaXRlQXJyYXmDAQIDeB1hbmRTb21lSW5kZWZpbml0ZUxlbmd0aFN0cmluZ3gfdGhhdCBoYXMsIGJlZW4gY2h1bmtlZCBvbiBjb21tYWxub3JtYWxTdHJpbmdjZm9vanNob3J0VmFsdWUZJw9uc29tZU90aGVyRmllbGR2dGhpcyBzaG91bGQgYmUgc2tpcHBlZP9saW50ZWdlclZhbHVlGQEAaWxvbmdWYWx1ZRkmkWpzaG9ydFZhbHVlGSaqa3N0cmluZ1ZhbHVlZnNpbXBsZXB0cnVlQm9vbGVhblZhbHVl9WlibG9iVmFsdWVDZm9v/w==` + ), + }); + + const params: any = {}; + const command = new SimpleScalarPropertiesCommand(params); + + let r: any; + try { + r = await client.send(command); + } catch (err) { + fail("Expected a valid response to be returned, got " + err); + return; + } + expect(r["$metadata"].httpStatusCode).toBe(200); + const paramsToValidate: any = [ + { + byteValue: 5, + doubleValue: 1.889, + falseBooleanValue: false, + floatValue: 7.625, + integerValue: 256, + longValue: 9873, + shortValue: 9898, + stringValue: "simple", + trueBooleanValue: true, + blobValue: Uint8Array.from("foo", (c) => c.charCodeAt(0)), + }, + ][0]; + Object.keys(paramsToValidate).forEach((param) => { + expect(r[param]).toBeDefined(); + expect(equivalentContents(paramsToValidate[param], r[param])).toBe(true); + }); +}); + +/** + * Serializes null values in maps + */ +it("RpcV2CborSparseMapsSerializeNullValues:Request", async () => { + const client = new RpcV2ProtocolClient({ + ...clientParams, + requestHandler: new RequestSerializationTestHandler(), + }); + + const command = new SparseNullsOperationCommand({ + sparseStringMap: { + foo: null, + } as any, + } as any); + try { + await client.send(command); + fail("Expected an EXPECTED_REQUEST_SERIALIZATION_ERROR to be thrown"); + return; + } catch (err) { + if (!(err instanceof EXPECTED_REQUEST_SERIALIZATION_ERROR)) { + fail(err); + return; + } + const r = err.request; + expect(r.method).toBe("POST"); + expect(r.path).toBe("/service/RpcV2Protocol/operation/SparseNullsOperation"); + expect(r.headers["content-length"]).toBeDefined(); + + expect(r.headers["content-type"]).toBeDefined(); + expect(r.headers["content-type"]).toBe("application/cbor"); + expect(r.headers["smithy-protocol"]).toBeDefined(); + expect(r.headers["smithy-protocol"]).toBe("rpc-v2-cbor"); + + expect(r.body).toBeDefined(); + const bodyString = `v29zcGFyc2VTdHJpbmdNYXC/Y2Zvb/b//w==`; + const unequalParts: any = compareEquivalentCborBodies(bodyString, r.body); + expect(unequalParts).toBeUndefined(); + } +}); + +/** + * Serializes null values in lists + */ +it("RpcV2CborSparseListsSerializeNull:Request", async () => { + const client = new RpcV2ProtocolClient({ + ...clientParams, + requestHandler: new RequestSerializationTestHandler(), + }); + + const command = new SparseNullsOperationCommand({ + sparseStringList: [null], + } as any); + try { + await client.send(command); + fail("Expected an EXPECTED_REQUEST_SERIALIZATION_ERROR to be thrown"); + return; + } catch (err) { + if (!(err instanceof EXPECTED_REQUEST_SERIALIZATION_ERROR)) { + fail(err); + return; + } + const r = err.request; + expect(r.method).toBe("POST"); + expect(r.path).toBe("/service/RpcV2Protocol/operation/SparseNullsOperation"); + expect(r.headers["content-length"]).toBeDefined(); + + expect(r.headers["content-type"]).toBeDefined(); + expect(r.headers["content-type"]).toBe("application/cbor"); + expect(r.headers["smithy-protocol"]).toBeDefined(); + expect(r.headers["smithy-protocol"]).toBe("rpc-v2-cbor"); + + expect(r.body).toBeDefined(); + const bodyString = `v3BzcGFyc2VTdHJpbmdMaXN0n/b//w==`; + const unequalParts: any = compareEquivalentCborBodies(bodyString, r.body); + expect(unequalParts).toBeUndefined(); + } +}); + +/** + * Deserializes null values in maps + */ +it("RpcV2CborSparseMapsDeserializeNullValues:Response", async () => { + const client = new RpcV2ProtocolClient({ + ...clientParams, + requestHandler: new ResponseDeserializationTestHandler( + true, + 200, + { + "smithy-protocol": "rpc-v2-cbor", + "content-type": "application/cbor", + }, + `v29zcGFyc2VTdHJpbmdNYXC/Y2Zvb/b//w==` + ), + }); + + const params: any = {}; + const command = new SparseNullsOperationCommand(params); + + let r: any; + try { + r = await client.send(command); + } catch (err) { + fail("Expected a valid response to be returned, got " + err); + return; + } + expect(r["$metadata"].httpStatusCode).toBe(200); + const paramsToValidate: any = [ + { + sparseStringMap: { + foo: null, + }, + }, + ][0]; + Object.keys(paramsToValidate).forEach((param) => { + expect(r[param]).toBeDefined(); + expect(equivalentContents(paramsToValidate[param], r[param])).toBe(true); + }); +}); + +/** + * Deserializes null values in lists + */ +it("RpcV2CborSparseListsDeserializeNull:Response", async () => { + const client = new RpcV2ProtocolClient({ + ...clientParams, + requestHandler: new ResponseDeserializationTestHandler( + true, + 200, + { + "smithy-protocol": "rpc-v2-cbor", + "content-type": "application/cbor", + }, + `v3BzcGFyc2VTdHJpbmdMaXN0n/b//w==` + ), + }); + + const params: any = {}; + const command = new SparseNullsOperationCommand(params); + + let r: any; + try { + r = await client.send(command); + } catch (err) { + fail("Expected a valid response to be returned, got " + err); + return; + } + expect(r["$metadata"].httpStatusCode).toBe(200); + const paramsToValidate: any = [ + { + sparseStringList: [null], + }, + ][0]; + Object.keys(paramsToValidate).forEach((param) => { + expect(r[param]).toBeDefined(); + expect(equivalentContents(paramsToValidate[param], r[param])).toBe(true); + }); +}); + +const compareEquivalentCborBodies = (expectedBody: string, generatedBody: string | Uint8Array): undefined => { + expect( + normalizeByteArrayType(cbor.deserialize(typeof generatedBody === "string" ? toBytes(generatedBody) : generatedBody)) + ).toEqual(normalizeByteArrayType(cbor.deserialize(toBytes(expectedBody)))); + return undefined; +}; diff --git a/private/smithy-rpcv2-cbor/tsconfig.cjs.json b/private/smithy-rpcv2-cbor/tsconfig.cjs.json new file mode 100644 index 00000000000..3567d85ba84 --- /dev/null +++ b/private/smithy-rpcv2-cbor/tsconfig.cjs.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig", + "compilerOptions": { + "outDir": "dist-cjs" + } +} diff --git a/private/smithy-rpcv2-cbor/tsconfig.es.json b/private/smithy-rpcv2-cbor/tsconfig.es.json new file mode 100644 index 00000000000..809f57bde65 --- /dev/null +++ b/private/smithy-rpcv2-cbor/tsconfig.es.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig", + "compilerOptions": { + "lib": ["dom"], + "module": "esnext", + "outDir": "dist-es" + } +} diff --git a/private/smithy-rpcv2-cbor/tsconfig.json b/private/smithy-rpcv2-cbor/tsconfig.json new file mode 100644 index 00000000000..e7f5ec56b74 --- /dev/null +++ b/private/smithy-rpcv2-cbor/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "@tsconfig/node16/tsconfig.json", + "compilerOptions": { + "downlevelIteration": true, + "importHelpers": true, + "incremental": true, + "removeComments": true, + "resolveJsonModule": true, + "rootDir": "src", + "useUnknownInCatchVariables": false + }, + "exclude": ["test/"] +} diff --git a/private/smithy-rpcv2-cbor/tsconfig.types.json b/private/smithy-rpcv2-cbor/tsconfig.types.json new file mode 100644 index 00000000000..4c3dfa7b3d2 --- /dev/null +++ b/private/smithy-rpcv2-cbor/tsconfig.types.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig", + "compilerOptions": { + "removeComments": false, + "declaration": true, + "declarationDir": "dist-types", + "emitDeclarationOnly": true + }, + "exclude": ["test/**/*", "dist-types/**/*"] +} diff --git a/scripts/compilation/Inliner.js b/scripts/compilation/Inliner.js index d212ea2c696..9355ab1d324 100644 --- a/scripts/compilation/Inliner.js +++ b/scripts/compilation/Inliner.js @@ -191,6 +191,7 @@ module.exports = class Inliner { } await esbuild.build({ ...buildOptions, + keepNames: false, entryPoints: [path.join(root, this.subfolder, this.package, "src", "submodules", submodule, "index.ts")], outfile: path.join(root, this.subfolder, this.package, "dist-cjs", "submodules", submodule, "index.js"), }); diff --git a/settings.gradle.kts b/settings.gradle.kts index 498b798976e..73e3d1531c5 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -19,6 +19,7 @@ import java.nio.charset.StandardCharsets.UTF_8 rootProject.name = "smithy-typescript" include(":smithy-typescript-codegen") include(":smithy-typescript-codegen-test") +include(":smithy-typescript-protocol-test-codegen") include(":smithy-typescript-codegen-test:example-weather-customizations") include(":smithy-typescript-codegen-test:released-version-test") include(":smithy-typescript-ssdk-codegen-test-utils") diff --git a/smithy-typescript-codegen/build.gradle.kts b/smithy-typescript-codegen/build.gradle.kts index 413b3ed111a..c4da63eb2e0 100644 --- a/smithy-typescript-codegen/build.gradle.kts +++ b/smithy-typescript-codegen/build.gradle.kts @@ -39,6 +39,7 @@ dependencies { // Smithy generic dependencies api("software.amazon.smithy:smithy-codegen-core:$smithyVersion") api("software.amazon.smithy:smithy-model:$smithyVersion") + api("software.amazon.smithy:smithy-protocol-traits:$smithyVersion") api("software.amazon.smithy:smithy-protocol-test-traits:$smithyVersion") api("software.amazon.smithy:smithy-rules-engine:$smithyVersion") api("software.amazon.smithy:smithy-waiters:$smithyVersion") diff --git a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/HttpProtocolTestGenerator.java b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/HttpProtocolTestGenerator.java index 7987985556a..084e9a697d1 100644 --- a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/HttpProtocolTestGenerator.java +++ b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/HttpProtocolTestGenerator.java @@ -547,11 +547,13 @@ private void writeHttpBodyAssertions(String body, String mediaType, boolean isCl // because a request case for servers would be comparing parsed objects. We // need to know which is which here to be able to grab the utf8Encoder from // the right place. - if (isClientTest) { - writer.write("const utf8Encoder = client.config.utf8Encoder;"); - } else { - writer.addImport("toUtf8", "__utf8Encoder", TypeScriptDependency.AWS_SDK_UTIL_UTF8); - writer.write("const utf8Encoder = __utf8Encoder;"); + if (!mediaType.equals("application/cbor")) { + if (isClientTest) { + writer.write("const utf8Encoder = client.config.utf8Encoder;"); + } else { + writer.addImport("toUtf8", "__utf8Encoder", TypeScriptDependency.AWS_SDK_UTIL_UTF8); + writer.write("const utf8Encoder = __utf8Encoder;"); + } } // Handle escaping strings with quotes inside them. @@ -596,6 +598,11 @@ private String registerBodyComparatorStub(String mediaType) { case "text/plain": additionalStubs.add("protocol-test-text-stub.ts"); return "compareEquivalentTextBodies(bodyString, r.body)"; + case "application/cbor": + writer.addImportSubmodule("cbor", null, + TypeScriptDependency.SMITHY_CORE, SmithyCoreSubmodules.CBOR); + additionalStubs.add("protocol-test-cbor-stub.ts"); + return "compareEquivalentCborBodies(bodyString, r.body)"; default: LOGGER.warning("Unable to compare bodies with unknown media type `" + mediaType + "`, defaulting to direct comparison."); @@ -810,7 +817,6 @@ private void writeResponseTestSetup(OperationShape operation, HttpResponseTestCa // trick TS in to letting us send this command through. writer.write("const params: any = {};"); writer.write("const command = new $T(params);\n", operationSymbol); - } // Ensure that the serialized response matches the expected response. @@ -912,12 +918,14 @@ private void writeParamAssertions( writer.write("expect(r[param]).toBeDefined();"); if (hasStreamingPayloadBlob) { writer.openBlock("if (param === $S) {", "} else {", payloadBinding.get().getMemberName(), () -> - writer.write("expect(equivalentContents(comparableBlob, " - + "paramsToValidate[param])).toBe(true);")); + writer.write(""" + expect(equivalentContents(paramsToValidate[param], \ + comparableBlob)).toBe(true); + """)); writer.indent(); } - writer.write("expect(equivalentContents(r[param], paramsToValidate[param])).toBe(true);"); + writer.write("expect(equivalentContents(paramsToValidate[param], r[param])).toBe(true);"); if (hasStreamingPayloadBlob) { writer.dedent(); @@ -985,7 +993,7 @@ public Void arrayNode(ArrayNode node) { String closeElement = "]"; // Write the value out directly. - writer.openBlock("$L\n", closeElement + ",\n", openElement, () -> { + writer.openBlock("$L", closeElement + ",", openElement, () -> { Shape wrapperShape = this.workingShape; node.getElements().forEach(element -> { // Swap the working shape to the member of the collection. @@ -993,7 +1001,7 @@ public Void arrayNode(ArrayNode node) { if (wrapperShape instanceof CollectionShape) { this.workingShape = model.expectShape(((CollectionShape) wrapperShape).getMember().getTarget()); } - writer.call(() -> element.accept(this)).write("\n"); + writer.call(() -> element.accept(this)); }); this.workingShape = wrapperShape; }); @@ -1043,7 +1051,7 @@ public Void objectNode(ObjectNode node) { suffix += ";"; appendSemicolon = false; } else { - suffix += ",\n"; + suffix += ","; } writer.openBlock("{", suffix, () -> { @@ -1075,7 +1083,6 @@ public Void objectNode(ObjectNode node) { this.workingShape = model.expectShape(memberShape.getTarget()); writer.call(() -> valueNode.accept(this)); } - writer.write("\n"); }); // Check for setting a potentially unspecified member value for the // idempotency token. @@ -1206,7 +1213,7 @@ public Void arrayNode(ArrayNode node) { String closeElement = "]"; // Write the value out directly. - writer.openBlock("$L\n", closeElement + ",\n", openElement, () -> { + writer.openBlock("$L", closeElement + ",", openElement, () -> { Shape wrapperShape = this.workingShape; node.getElements().forEach(element -> { // Swap the working shape to the member of the collection. @@ -1214,7 +1221,7 @@ public Void arrayNode(ArrayNode node) { if (wrapperShape instanceof CollectionShape) { this.workingShape = model.expectShape(((CollectionShape) wrapperShape).getMember().getTarget()); } - writer.call(() -> element.accept(this)).write("\n"); + writer.call(() -> element.accept(this)); }); this.workingShape = wrapperShape; }); @@ -1257,7 +1264,7 @@ public Void objectNode(ObjectNode node) { // Both objects and maps can use a majority of the same logic. // Use "as any" to have TS complain less about undefined entries. - writer.openBlock("{", "},\n", () -> { + writer.openBlock("{", "},", () -> { Shape wrapperShape = this.workingShape; node.getMembers().forEach((keyNode, valueNode) -> { // Grab the correct member related to the node member we have. @@ -1287,7 +1294,6 @@ public Void objectNode(ObjectNode node) { ? downcaseNodeKeys(valueNode.expectObjectNode()) : valueNode; writer.call(() -> renderNode.accept(this)); - writer.write("\n"); }); this.workingShape = wrapperShape; }); diff --git a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/SmithyCoreSubmodules.java b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/SmithyCoreSubmodules.java new file mode 100644 index 00000000000..796c7b9efd8 --- /dev/null +++ b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/SmithyCoreSubmodules.java @@ -0,0 +1,27 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.typescript.codegen; + +/** + * This is an enum-like submodule list to be used with + * + * TypeScriptWriter::addImportSubmodule( + * Dependency == "cbor", + * null (no alias), + * PackageContainer == "@smithy/core", + * SmithyCoreSubmodules.CBOR == "/cbor" + * ); + * + * The intended result is e.g. + * ```ts + * import { cbor } from "@smithy/core/cbor"; + * ``` + */ +public final class SmithyCoreSubmodules { + public static final String CBOR = "/cbor"; + + private SmithyCoreSubmodules() {} +} diff --git a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/TypeScriptWriter.java b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/TypeScriptWriter.java index 4ff414ba8ae..eb5296bf491 100644 --- a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/TypeScriptWriter.java +++ b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/TypeScriptWriter.java @@ -153,6 +153,17 @@ public TypeScriptWriter addImport(String name, String as, PackageContainer from) return this.addImport(name, as, from.getPackageName()); } + /** + * Same as {@link #addImport(String, String, PackageContainer)} but appends a + * submodule path, for example "@smithy/core/cbor". + */ + public TypeScriptWriter addImportSubmodule(String name, String as, PackageContainer from, String submodule) { + if (from instanceof Dependency dependency) { + addDependency(dependency); + } + return this.addImport(name, as, from.getPackageName() + submodule); + } + /** * Imports a type using an alias from a relative Path. * diff --git a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/integration/HttpProtocolGeneratorUtils.java b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/integration/HttpProtocolGeneratorUtils.java index fbc9f46ed49..55a7fc6905a 100644 --- a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/integration/HttpProtocolGeneratorUtils.java +++ b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/integration/HttpProtocolGeneratorUtils.java @@ -48,12 +48,14 @@ import software.amazon.smithy.typescript.codegen.TypeScriptWriter; import software.amazon.smithy.typescript.codegen.integration.ProtocolGenerator.GenerationContext; import software.amazon.smithy.utils.IoUtils; +import software.amazon.smithy.utils.SmithyInternalApi; import software.amazon.smithy.utils.SmithyUnstableApi; /** * Utility methods for generating HTTP protocols. */ @SmithyUnstableApi +@SmithyInternalApi public final class HttpProtocolGeneratorUtils { private static final Logger LOGGER = Logger.getLogger(HttpBindingProtocolGenerator.class.getName()); @@ -167,7 +169,7 @@ public static String getTimestampOutputParam(TypeScriptWriter writer, * ({@code input.foo}, {@code entry}, etc.) * @return Returns a value or expression of the input string. */ - static String getStringInputParam(GenerationContext context, Shape shape, String dataSource) { + public static String getStringInputParam(GenerationContext context, Shape shape, String dataSource) { // Handle media type generation, defaulting to the dataSource. Optional mediaTypeTrait = shape.getTrait(MediaTypeTrait.class); if (mediaTypeTrait.isPresent()) { @@ -198,7 +200,10 @@ static String getStringInputParam(GenerationContext context, Shape shape, String * only be false if the value is guaranteed to be a string already. * @return Returns a value or expression of the output string. */ - static String getStringOutputParam(GenerationContext context, Shape shape, String dataSource, boolean useExpect) { + public static String getStringOutputParam(GenerationContext context, + Shape shape, + String dataSource, + boolean useExpect) { // Handle media type generation, defaulting to a standard String. Optional mediaTypeTrait = shape.getTrait(MediaTypeTrait.class); if (mediaTypeTrait.isPresent()) { @@ -231,7 +236,7 @@ static String getStringOutputParam(GenerationContext context, Shape shape, Strin * ({@code output.foo}, {@code entry}, etc.) * @return Returns a value or expression of the output string. */ - static String getStringOutputParam(GenerationContext context, Shape shape, String dataSource) { + public static String getStringOutputParam(GenerationContext context, Shape shape, String dataSource) { return getStringOutputParam(context, shape, dataSource, true); } @@ -242,7 +247,7 @@ static String getStringOutputParam(GenerationContext context, Shape shape, Strin * @param context The generation context. * @param responseType The response type for the HTTP protocol. */ - static void generateMetadataDeserializer(GenerationContext context, SymbolReference responseType) { + public static void generateMetadataDeserializer(GenerationContext context, SymbolReference responseType) { TypeScriptWriter writer = context.getWriter(); writer.addImport("ResponseMetadata", "__ResponseMetadata", TypeScriptDependency.SMITHY_TYPES); @@ -264,7 +269,7 @@ static void generateMetadataDeserializer(GenerationContext context, SymbolRefere * * @param context The generation context */ - static void generateCollectBodyString(GenerationContext context) { + public static void generateCollectBodyString(GenerationContext context) { TypeScriptWriter writer = context.getWriter(); writer.addImport("collectBody", null, TypeScriptDependency.AWS_SMITHY_CLIENT); writer.addImport("SerdeContext", "__SerdeContext", TypeScriptDependency.SMITHY_TYPES); @@ -317,7 +322,7 @@ public static void writeRetryableTrait(TypeScriptWriter writer, StructureShape e * @param operationErrorsToShapes A map of error names to their {@link ShapeId}. * @return A set of all error structure shapes for the operation that were dispatched to. */ - static Set generateUnifiedErrorDispatcher( + public static Set generateUnifiedErrorDispatcher( GenerationContext context, List operations, SymbolReference responseType, @@ -416,7 +421,7 @@ static Set generateUnifiedErrorDispatcher( * @param context The generation context. * @param operation The operation to generate for. */ - static void writeHostPrefix(GenerationContext context, OperationShape operation) { + public static void writeHostPrefix(GenerationContext context, OperationShape operation) { TypeScriptWriter writer = context.getWriter(); SymbolProvider symbolProvider = context.getSymbolProvider(); EndpointTrait trait = operation.getTrait(EndpointTrait.class).get(); diff --git a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/integration/HttpRpcProtocolGenerator.java b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/integration/HttpRpcProtocolGenerator.java index ce83cd02eba..5ea9bae231d 100644 --- a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/integration/HttpRpcProtocolGenerator.java +++ b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/integration/HttpRpcProtocolGenerator.java @@ -47,11 +47,12 @@ public abstract class HttpRpcProtocolGenerator implements ProtocolGenerator { private static final ApplicationProtocol APPLICATION_PROTOCOL = ApplicationProtocol.createDefaultHttpApplicationProtocol(); - private final Set serializingDocumentShapes = new TreeSet<>(); - private final Set deserializingDocumentShapes = new TreeSet<>(); - private final Set deserializingErrorShapes = new TreeSet<>(); + protected final Set serializingDocumentShapes = new TreeSet<>(); + protected final Set deserializingDocumentShapes = new TreeSet<>(); + protected final Set deserializingErrorShapes = new TreeSet<>(); + protected final EventStreamGenerator eventStreamGenerator = new EventStreamGenerator(); + private final boolean isErrorCodeInBody; - private final EventStreamGenerator eventStreamGenerator = new EventStreamGenerator(); /** * Creates a Http RPC protocol generator. @@ -238,7 +239,7 @@ public void generateResponseDeserializers(GenerationContext context) { deserializingErrorShapes.addAll(errorShapes); } - private void generateOperationSerializer(GenerationContext context, OperationShape operation) { + protected void generateOperationSerializer(GenerationContext context, OperationShape operation) { SymbolProvider symbolProvider = context.getSymbolProvider(); Symbol symbol = symbolProvider.toSymbol(operation); SymbolReference requestType = getApplicationProtocol().getRequestType(); @@ -348,7 +349,7 @@ protected void writeSharedRequestHeaders(GenerationContext context) { }); } - private boolean writeRequestBody(GenerationContext context, OperationShape operation) { + protected boolean writeRequestBody(GenerationContext context, OperationShape operation) { if (operation.getInput().isPresent()) { // If there's an input present, we know it's a structure. StructureShape inputShape = context.getModel().expectShape(operation.getInput().get()) @@ -436,7 +437,7 @@ protected boolean writeUndefinedInputBody(GenerationContext context, OperationSh return false; } - private void generateOperationDeserializer(GenerationContext context, OperationShape operation) { + protected void generateOperationDeserializer(GenerationContext context, OperationShape operation) { SymbolProvider symbolProvider = context.getSymbolProvider(); Symbol symbol = symbolProvider.toSymbol(operation); SymbolReference responseType = getApplicationProtocol().getResponseType(); @@ -479,7 +480,7 @@ private void generateOperationDeserializer(GenerationContext context, OperationS writer.write(""); } - private void generateErrorDeserializer(GenerationContext context, StructureShape error) { + protected void generateErrorDeserializer(GenerationContext context, StructureShape error) { TypeScriptWriter writer = context.getWriter(); SymbolProvider symbolProvider = context.getSymbolProvider(); Symbol errorSymbol = symbolProvider.toSymbol(error); @@ -530,7 +531,7 @@ private void generateErrorDeserializer(GenerationContext context, StructureShape writer.write(""); } - private void readResponseBody(GenerationContext context, OperationShape operation) { + protected void readResponseBody(GenerationContext context, OperationShape operation) { TypeScriptWriter writer = context.getWriter(); OptionalUtils.ifPresentOrElse( operation.getOutput(), diff --git a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/protocols/AddProtocols.java b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/protocols/AddProtocols.java new file mode 100644 index 00000000000..c1f5287de5c --- /dev/null +++ b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/protocols/AddProtocols.java @@ -0,0 +1,27 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.typescript.codegen.protocols; + +import java.util.List; +import software.amazon.smithy.typescript.codegen.integration.ProtocolGenerator; +import software.amazon.smithy.typescript.codegen.integration.TypeScriptIntegration; +import software.amazon.smithy.typescript.codegen.protocols.cbor.SmithyRpcV2Cbor; +import software.amazon.smithy.utils.ListUtils; +import software.amazon.smithy.utils.SmithyInternalApi; + +/** + * Adds Smithy protocols. + */ +@SmithyInternalApi +public class AddProtocols implements TypeScriptIntegration { + + @Override + public List getProtocolGenerators() { + return ListUtils.of( + new SmithyRpcV2Cbor() + ); + } +} diff --git a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/protocols/SmithyProtocolUtils.java b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/protocols/SmithyProtocolUtils.java new file mode 100644 index 00000000000..8e688bf8013 --- /dev/null +++ b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/protocols/SmithyProtocolUtils.java @@ -0,0 +1,101 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.typescript.codegen.protocols; + +import java.util.Set; +import java.util.TreeSet; +import software.amazon.smithy.model.knowledge.NeighborProviderIndex; +import software.amazon.smithy.model.neighbor.Walker; +import software.amazon.smithy.model.shapes.OperationShape; +import software.amazon.smithy.model.shapes.ServiceShape; +import software.amazon.smithy.model.shapes.Shape; +import software.amazon.smithy.model.shapes.ShapeVisitor; +import software.amazon.smithy.protocoltests.traits.HttpMalformedRequestTestCase; +import software.amazon.smithy.protocoltests.traits.HttpMessageTestCase; +import software.amazon.smithy.typescript.codegen.HttpProtocolTestGenerator; +import software.amazon.smithy.typescript.codegen.TypeScriptSettings; +import software.amazon.smithy.typescript.codegen.integration.ProtocolGenerator; +import software.amazon.smithy.utils.SmithyInternalApi; + + +/** + * Utility methods for generating Smithy protocols. + */ +@SmithyInternalApi +public final class SmithyProtocolUtils { + + private SmithyProtocolUtils() {} + + /** + * Writes a serde function for a set of shapes using the passed visitor. + * This will walk the input set of shapes and invoke the visitor for any + * members of aggregate shapes in the set. + * + * @see software.amazon.smithy.typescript.codegen.integration.DocumentShapeSerVisitor + * @see software.amazon.smithy.typescript.codegen.integration.DocumentShapeDeserVisitor + * + * @param context The generation context. + * @param shapes A list of shapes to generate serde for, including their members. + * @param visitor A ShapeVisitor that generates a serde function for shapes. + */ + public static void generateDocumentBodyShapeSerde( + ProtocolGenerator.GenerationContext context, + Set shapes, + ShapeVisitor visitor + ) { + Walker shapeWalker = new Walker(NeighborProviderIndex.of(context.getModel()).getProvider()); + Set shapesToGenerate = new TreeSet<>(shapes); + shapes.forEach(shape -> shapesToGenerate.addAll(shapeWalker.walkShapes(shape))); + shapesToGenerate.forEach(shape -> shape.accept(visitor)); + } + + public static void generateProtocolTests(ProtocolGenerator generator, ProtocolGenerator.GenerationContext context) { + new HttpProtocolTestGenerator(context, + generator, + SmithyProtocolUtils::filterProtocolTests, + SmithyProtocolUtils::filterMalformedRequestTests).run(); + } + + private static boolean filterProtocolTests( + ServiceShape service, + OperationShape operation, + HttpMessageTestCase testCase, + TypeScriptSettings settings + ) { + if (testCase.getTags().contains("defaults")) { + return true; + } + + // TODO(cbor): enable this test when upgrading to a Smithy version + // TODO(cbor): in which it is fixed. + if (testCase.getId().equals("RpcV2CborDeserializesDenseSetMapAndSkipsNull")) { + return true; + } + + return false; + } + + private static boolean filterMalformedRequestTests( + ServiceShape service, + OperationShape operation, + HttpMalformedRequestTestCase testCase, + TypeScriptSettings settings + ) { + // Handling overflow/underflow of longs in JS is extraordinarily tricky. + // Numbers are actually all 62-bit floats, and so any integral number is + // limited to 53 bits. In typical JS fashion, a value outside of this + // range just kinda silently bumbles on in some third state between valid + // and invalid. Infuriatingly, there doesn't seem to be a consistent way + // to detect this. We could *try* to do bounds checking, but the constants + // we use wouldn't necessarily work, so it could work in some environments + // but not others. + if (operation.getId().getName().equals("MalformedLong") && testCase.hasTag("underflow/overflow")) { + return true; + } + + return false; + } +} diff --git a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/protocols/cbor/CborMemberDeserVisitor.java b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/protocols/cbor/CborMemberDeserVisitor.java new file mode 100644 index 00000000000..c7057a0a55a --- /dev/null +++ b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/protocols/cbor/CborMemberDeserVisitor.java @@ -0,0 +1,63 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.typescript.codegen.protocols.cbor; + +import software.amazon.smithy.model.knowledge.HttpBinding; +import software.amazon.smithy.model.shapes.BlobShape; +import software.amazon.smithy.model.shapes.TimestampShape; +import software.amazon.smithy.model.traits.TimestampFormatTrait; +import software.amazon.smithy.typescript.codegen.TypeScriptDependency; +import software.amazon.smithy.typescript.codegen.integration.DocumentMemberDeserVisitor; +import software.amazon.smithy.typescript.codegen.integration.HttpProtocolGeneratorUtils; +import software.amazon.smithy.typescript.codegen.integration.ProtocolGenerator; + +public class CborMemberDeserVisitor extends DocumentMemberDeserVisitor { + private final String dataSource; + private final ProtocolGenerator.GenerationContext context; + + /** + * Constructor. + * + * @param context The generation context. + * @param dataSource The in-code location of the data to provide an output of + * ({@code output.foo}, {@code entry}, etc.) + */ + public CborMemberDeserVisitor(ProtocolGenerator.GenerationContext context, + String dataSource) { + super(context, dataSource, TimestampFormatTrait.Format.EPOCH_SECONDS); + this.context = context; + context.getWriter().addImport("_json", null, TypeScriptDependency.AWS_SMITHY_CLIENT); + this.serdeElisionEnabled = !context.getSettings().generateServerSdk(); + this.dataSource = dataSource; + } + + /** + * This differs from the base method in that CBOR does not need to wrap + * the blob value in `context.base64Decoder(...)`. The CBOR format deserializer + * already does this whereas e.g. JSON.parse does not. + */ + @Override + public String blobShape(BlobShape shape) { + return dataSource; + } + + /** + * Smithy RPCv2 CBOR only allows the epoch-seconds format, ignoring the model's + * timestamp trait. + */ + @Override + public String timestampShape(TimestampShape shape) { + return HttpProtocolGeneratorUtils.getTimestampOutputParam( + context.getWriter(), + dataSource, + HttpBinding.Location.DOCUMENT, + shape, + TimestampFormatTrait.Format.EPOCH_SECONDS, + requiresNumericEpochSecondsInPayload(), + context.getSettings().generateClient() + ); + } +} diff --git a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/protocols/cbor/CborMemberSerVisitor.java b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/protocols/cbor/CborMemberSerVisitor.java new file mode 100644 index 00000000000..0b370449c94 --- /dev/null +++ b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/protocols/cbor/CborMemberSerVisitor.java @@ -0,0 +1,79 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.typescript.codegen.protocols.cbor; + +import software.amazon.smithy.model.shapes.BlobShape; +import software.amazon.smithy.model.shapes.DoubleShape; +import software.amazon.smithy.model.shapes.FloatShape; +import software.amazon.smithy.model.shapes.TimestampShape; +import software.amazon.smithy.model.traits.TimestampFormatTrait; +import software.amazon.smithy.typescript.codegen.SmithyCoreSubmodules; +import software.amazon.smithy.typescript.codegen.TypeScriptDependency; +import software.amazon.smithy.typescript.codegen.integration.DocumentMemberSerVisitor; +import software.amazon.smithy.typescript.codegen.integration.ProtocolGenerator; + +public class CborMemberSerVisitor extends DocumentMemberSerVisitor { + + private final String dataSource; + private final ProtocolGenerator.GenerationContext context; + + /** + * Constructor. + * + * @param context The generation context. + * @param dataSource The in-code location of the data to provide an input of + * ({@code input.foo}, {@code entry}, etc.) + */ + public CborMemberSerVisitor(ProtocolGenerator.GenerationContext context, + String dataSource) { + super(context, dataSource, TimestampFormatTrait.Format.EPOCH_SECONDS); + this.context = context; + this.serdeElisionEnabled = true; + this.dataSource = dataSource; + } + + /** + * This differs from the base method in that CBOR does not need to wrap + * the blob value in `context.base64Encoder(...)`. The CBOR format serializer + * already does this whereas e.g. JSON.stringify does not. + */ + @Override + public String blobShape(BlobShape shape) { + return dataSource; + } + + /** + * +/- Infinity and NaN have byte representations. No need to + * serialize those values with serializeFloat(). + */ + @Override + public String floatShape(FloatShape shape) { + return dataSource; + } + + /** + * +/- Infinity and NaN have byte representations. No need to + * serialize those values with serializeFloat(). + */ + @Override + public String doubleShape(DoubleShape shape) { + return dataSource; + } + + /** + * CBOR serialization needs a JS object identifiable as a tag. + */ + @Override + public String timestampShape(TimestampShape shape) { + context.getWriter().addImportSubmodule( + "dateToTag", + "__dateToTag", + TypeScriptDependency.SMITHY_CORE, + SmithyCoreSubmodules.CBOR + ); + return "__dateToTag(" + dataSource + ")"; + } +} diff --git a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/protocols/cbor/CborShapeDeserVisitor.java b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/protocols/cbor/CborShapeDeserVisitor.java new file mode 100644 index 00000000000..358b8150a1e --- /dev/null +++ b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/protocols/cbor/CborShapeDeserVisitor.java @@ -0,0 +1,209 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.typescript.codegen.protocols.cbor; + +import java.util.Map; +import java.util.TreeMap; +import software.amazon.smithy.codegen.core.SymbolProvider; +import software.amazon.smithy.model.Model; +import software.amazon.smithy.model.shapes.CollectionShape; +import software.amazon.smithy.model.shapes.DocumentShape; +import software.amazon.smithy.model.shapes.MapShape; +import software.amazon.smithy.model.shapes.MemberShape; +import software.amazon.smithy.model.shapes.NumberShape; +import software.amazon.smithy.model.shapes.Shape; +import software.amazon.smithy.model.shapes.StructureShape; +import software.amazon.smithy.model.shapes.UnionShape; +import software.amazon.smithy.model.traits.SparseTrait; +import software.amazon.smithy.typescript.codegen.TypeScriptDependency; +import software.amazon.smithy.typescript.codegen.TypeScriptWriter; +import software.amazon.smithy.typescript.codegen.integration.DocumentShapeDeserVisitor; +import software.amazon.smithy.typescript.codegen.integration.ProtocolGenerator; +import software.amazon.smithy.typescript.codegen.util.PropertyAccessor; +import software.amazon.smithy.typescript.codegen.validation.UnaryFunctionCall; + + +public class CborShapeDeserVisitor extends DocumentShapeDeserVisitor { + + public CborShapeDeserVisitor(ProtocolGenerator.GenerationContext context) { + super(context); + this.serdeElisionEnabled = true; + } + + @Override + protected void deserializeCollection(ProtocolGenerator.GenerationContext context, CollectionShape shape) { + TypeScriptWriter writer = context.getWriter(); + Shape target = context.getModel().expectShape(shape.getMember().getTarget()); + + String potentialFilter = ""; + if (!shape.hasTrait(SparseTrait.ID)) { + potentialFilter = ".filter((e: any) => e != null)"; + } + final String filterExpression = potentialFilter; + + String returnExpression = target.accept(getMemberVisitor("entry")); + + if (returnExpression.equals("entry")) { + writer.write("const collection = (output || [])$L", filterExpression); + } else { + writer.openBlock( + "const collection = (output || [])$L.map((entry: any) => {", + "});", + filterExpression, () -> { + if (filterExpression.isEmpty()) { + writer.openBlock("if (entry === null) {", "}", () -> { + if (!shape.hasTrait(SparseTrait.ID)) { + writer.write( + "throw new TypeError('All elements of the non-sparse list $S must be non-null.');", + shape.getId() + ); + } else { + writer.write("return null as any;"); + } + }); + } + + writer.write("return $L$L;", + target.accept(getMemberVisitor("entry")), + usesExpect(target) ? " as any" : "" + ); + } + ); + } + + writer.write("return collection;"); + } + + @Override + protected void deserializeDocument(ProtocolGenerator.GenerationContext context, DocumentShape shape) { + context.getWriter().write(""" + return output; // document. + """); + } + + @Override + protected void deserializeMap(ProtocolGenerator.GenerationContext context, MapShape shape) { + TypeScriptWriter writer = context.getWriter(); + Shape target = context.getModel().expectShape(shape.getValue().getTarget()); + SymbolProvider symbolProvider = context.getSymbolProvider(); + + writer.openBlock("return Object.entries(output).reduce((acc: $T, [key, value]: [string, any]) => {", + "", + symbolProvider.toSymbol(shape), + () -> { + writer.openBlock("if (value !== null) {", "}", () -> { + writer.write("acc[key as $T] = $L$L", + symbolProvider.toSymbol(shape.getKey()), + target.accept(getMemberVisitor("value")), + usesExpect(target) ? " as any;" : ";" + ); + }); + + if (shape.hasTrait(SparseTrait.ID)) { + writer.write("else {") + .indent(); + writer.write("acc[key as $T] = null as any;", symbolProvider.toSymbol(shape.getKey())) + .dedent(); + writer.write("}"); + } + + writer.write("return acc;"); + } + ); + writer.writeInline("}, {} as $T);", symbolProvider.toSymbol(shape)); + } + + @Override + protected void deserializeStructure(ProtocolGenerator.GenerationContext context, StructureShape shape) { + TypeScriptWriter writer = context.getWriter(); + + Map members = new TreeMap<>(shape.getAllMembers()); + writer.addImport("take", null, TypeScriptDependency.AWS_SMITHY_CLIENT); + writer.openBlock("return take(output, {", "}) as any;", () -> { + members.forEach((memberName, memberShape) -> { + Shape target = context.getModel().expectShape(memberShape.getTarget()); + + String propertyAccess = PropertyAccessor.getFrom("output", memberName); + String value = target.accept(getMemberVisitor("_")); + + if (usesExpect(target)) { + if (UnaryFunctionCall.check(value)) { + writer.write("'$L': $L,", memberName, UnaryFunctionCall.toRef(value)); + } else { + writer.write("'$L': $L,", memberName, "_ => " + value); + } + } else { + String valueExpression = target.accept(getMemberVisitor(propertyAccess)); + + if (valueExpression.equals(propertyAccess)) { + writer.write("'$1L': [],", memberName); + } else { + String functionExpression = value; + boolean isUnaryCall = UnaryFunctionCall.check(functionExpression); + if (isUnaryCall) { + writer.write("'$1L': $2L,", + memberName, + UnaryFunctionCall.toRef(functionExpression) + ); + } else { + writer.write("'$1L': (_: any) => $2L,", + memberName, + functionExpression + ); + } + } + } + }); + }); + } + + @Override + protected void deserializeUnion(ProtocolGenerator.GenerationContext context, UnionShape shape) { + TypeScriptWriter writer = context.getWriter(); + Model model = context.getModel(); + + Map members = new TreeMap<>(shape.getAllMembers()); + + members.forEach((memberName, memberShape) -> { + Shape target = model.expectShape(memberShape.getTarget()); + + String memberValue = target.accept( + getMemberVisitor(PropertyAccessor.getFrom("output", memberName)) + ); + + if (usesExpect(target)) { + writer.openBlock("if ($L !== undefined) {", "}", memberValue, () -> { + writer.write("return { $L: $L as any }", memberName, memberValue); + }); + } else { + writer.openBlock( + "if ($1L != null) {", "}", + PropertyAccessor.getFrom("output", memberName), + () -> { + writer.write(""" + return { + $L: $L + } + """, + memberName, memberValue + ); + } + ); + } + }); + writer.write("return { $$unknown: Object.entries(output)[0] };"); + } + + private CborMemberDeserVisitor getMemberVisitor(String dataSource) { + return new CborMemberDeserVisitor( + getContext(), dataSource + ); + } + + private boolean usesExpect(Shape shape) { + return shape.isStringShape() || shape.isBooleanShape() || shape instanceof NumberShape; + } +} diff --git a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/protocols/cbor/CborShapeSerVisitor.java b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/protocols/cbor/CborShapeSerVisitor.java new file mode 100644 index 00000000000..2de9043276f --- /dev/null +++ b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/protocols/cbor/CborShapeSerVisitor.java @@ -0,0 +1,166 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.typescript.codegen.protocols.cbor; + +import java.util.Map; +import java.util.TreeMap; +import software.amazon.smithy.codegen.core.Symbol; +import software.amazon.smithy.codegen.core.SymbolProvider; +import software.amazon.smithy.model.Model; +import software.amazon.smithy.model.shapes.CollectionShape; +import software.amazon.smithy.model.shapes.DocumentShape; +import software.amazon.smithy.model.shapes.MapShape; +import software.amazon.smithy.model.shapes.MemberShape; +import software.amazon.smithy.model.shapes.ServiceShape; +import software.amazon.smithy.model.shapes.Shape; +import software.amazon.smithy.model.shapes.StructureShape; +import software.amazon.smithy.model.shapes.UnionShape; +import software.amazon.smithy.model.traits.IdempotencyTokenTrait; +import software.amazon.smithy.model.traits.SparseTrait; +import software.amazon.smithy.model.traits.TimestampFormatTrait; +import software.amazon.smithy.typescript.codegen.TypeScriptDependency; +import software.amazon.smithy.typescript.codegen.TypeScriptWriter; +import software.amazon.smithy.typescript.codegen.integration.DocumentMemberSerVisitor; +import software.amazon.smithy.typescript.codegen.integration.DocumentShapeSerVisitor; +import software.amazon.smithy.typescript.codegen.integration.HttpProtocolGeneratorUtils; +import software.amazon.smithy.typescript.codegen.integration.ProtocolGenerator; +import software.amazon.smithy.typescript.codegen.validation.UnaryFunctionCall; + + +public class CborShapeSerVisitor extends DocumentShapeSerVisitor { + /** + * The service model's timestampFormat is ignored in RPCv2 CBOR protocol. + */ + private static final TimestampFormatTrait.Format TIMESTAMP_FORMAT = TimestampFormatTrait.Format.EPOCH_SECONDS; + + public CborShapeSerVisitor(ProtocolGenerator.GenerationContext context) { + super(context); + this.serdeElisionEnabled = true; + } + + @Override + protected void serializeCollection(ProtocolGenerator.GenerationContext context, CollectionShape shape) { + TypeScriptWriter writer = context.getWriter(); + Shape target = context.getModel().expectShape(shape.getMember().getTarget()); + + String potentialFilter = ""; + boolean hasSparseTrait = shape.hasTrait(SparseTrait.ID); + if (!hasSparseTrait) { + potentialFilter = ".filter((e: any) => e != null)"; + } + + String returnedExpression = target.accept(getMemberVisitor("entry")); + + if (returnedExpression.equals("entry")) { + writer.write("return input$L;", potentialFilter); + } else { + writer.openBlock("return input$L.map(entry => {", "});", potentialFilter, () -> { + if (hasSparseTrait) { + writer.write("if (entry === null) { return null as any; }"); + } + writer.write("return $L;", target.accept(getMemberVisitor("entry"))); + }); + } + } + + @Override + protected void serializeDocument(ProtocolGenerator.GenerationContext context, DocumentShape shape) { + context.getWriter().write(""" + return input; // document. + """); + } + + @Override + protected void serializeMap(ProtocolGenerator.GenerationContext context, MapShape shape) { + TypeScriptWriter writer = context.getWriter(); + Shape target = context.getModel().expectShape(shape.getValue().getTarget()); + SymbolProvider symbolProvider = context.getSymbolProvider(); + + Symbol keySymbol = symbolProvider.toSymbol(shape.getKey()); + String entryKeyType = keySymbol.toString().equals("string") + ? "string" + : symbolProvider.toSymbol(shape.getKey()) + "| string"; + + writer.openBlock("return Object.entries(input).reduce((acc: Record, " + + "[key, value]: [$1L, any]) => {", "}, {});", entryKeyType, + () -> { + writer.write(""" + if (value !== null) { + acc[key] = $L; + } + """, + target.accept(getMemberVisitor("value")) + ); + + if (shape.hasTrait(SparseTrait.ID)) { + writer.write(""" + else { + acc[key] = null as any; + } + """); + } + + writer.write("return acc;"); + } + ); + } + + @Override + protected void serializeStructure(ProtocolGenerator.GenerationContext context, StructureShape shape) { + TypeScriptWriter writer = context.getWriter(); + writer.addImport("take", null, TypeScriptDependency.AWS_SMITHY_CLIENT); + writer.openBlock("return take(input, {", "});", () -> { + Map members = new TreeMap<>(shape.getAllMembers()); + members.forEach((memberName, memberShape) -> { + Shape target = context.getModel().expectShape(memberShape.getTarget()); + + String valueExpression = (memberShape.hasTrait(TimestampFormatTrait.class) + ? HttpProtocolGeneratorUtils.getTimestampInputParam( + context, "_", memberShape, TIMESTAMP_FORMAT + ) + : target.accept(getMemberVisitor("_"))); + + String valueProvider = "_ => " + valueExpression; + boolean isUnaryCall = UnaryFunctionCall.check(valueExpression); + + if (memberShape.hasTrait(IdempotencyTokenTrait.class)) { + writer.addImport("v4", "generateIdempotencyToken", TypeScriptDependency.UUID); + + writer.write("'$L': [true, _ => _ ?? generateIdempotencyToken()],", memberName); + } else { + if (valueProvider.equals("_ => _")) { + writer.write("'$1L': [],", memberName); + } else if (isUnaryCall) { + writer.write("'$1L': $2L,", memberName, UnaryFunctionCall.toRef(valueExpression)); + } else { + writer.write("'$1L': $2L,", memberName, valueProvider); + } + } + }); + }); + } + + @Override + protected void serializeUnion(ProtocolGenerator.GenerationContext context, UnionShape shape) { + TypeScriptWriter writer = context.getWriter(); + Model model = context.getModel(); + ServiceShape serviceShape = context.getService(); + + writer.openBlock("return $L.visit(input, {", "});", shape.getId().getName(serviceShape), () -> { + Map members = new TreeMap<>(shape.getAllMembers()); + members.forEach((memberName, memberShape) -> { + Shape target = model.expectShape(memberShape.getTarget()); + writer.write("$L: value => ({ $S: $L }),", memberName, memberName, + target.accept(getMemberVisitor("value"))); + }); + writer.write("_: (name, value) => ({ name: value } as any)"); + }); + } + + private DocumentMemberSerVisitor getMemberVisitor(String dataSource) { + return new CborMemberSerVisitor(getContext(), dataSource); + } +} diff --git a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/protocols/cbor/SmithyRpcV2Cbor.java b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/protocols/cbor/SmithyRpcV2Cbor.java new file mode 100644 index 00000000000..4b1971864a9 --- /dev/null +++ b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/protocols/cbor/SmithyRpcV2Cbor.java @@ -0,0 +1,311 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.typescript.codegen.protocols.cbor; + +import java.util.Set; +import java.util.TreeSet; +import software.amazon.smithy.codegen.core.Symbol; +import software.amazon.smithy.codegen.core.SymbolProvider; +import software.amazon.smithy.codegen.core.SymbolReference; +import software.amazon.smithy.model.Model; +import software.amazon.smithy.model.shapes.OperationShape; +import software.amazon.smithy.model.shapes.ServiceShape; +import software.amazon.smithy.model.shapes.Shape; +import software.amazon.smithy.model.shapes.ShapeId; +import software.amazon.smithy.model.shapes.StructureShape; +import software.amazon.smithy.protocol.traits.Rpcv2CborTrait; +import software.amazon.smithy.typescript.codegen.CodegenUtils; +import software.amazon.smithy.typescript.codegen.SmithyCoreSubmodules; +import software.amazon.smithy.typescript.codegen.TypeScriptDependency; +import software.amazon.smithy.typescript.codegen.TypeScriptSettings; +import software.amazon.smithy.typescript.codegen.TypeScriptWriter; +import software.amazon.smithy.typescript.codegen.integration.EventStreamGenerator; +import software.amazon.smithy.typescript.codegen.integration.HttpProtocolGeneratorUtils; +import software.amazon.smithy.typescript.codegen.integration.HttpRpcProtocolGenerator; +import software.amazon.smithy.typescript.codegen.integration.ProtocolGenerator; +import software.amazon.smithy.typescript.codegen.knowledge.SerdeElisionIndex; +import software.amazon.smithy.typescript.codegen.protocols.SmithyProtocolUtils; +import software.amazon.smithy.utils.SmithyInternalApi; + +/** + * Generator for Smithy RPCv2 CBOR. + * + * @see CborShapeSerVisitor + * @see CborShapeDeserVisitor + * @see CborMemberSerVisitor + * @see CborMemberDeserVisitor + * @see SmithyProtocolUtils + */ +@SmithyInternalApi +public class SmithyRpcV2Cbor extends HttpRpcProtocolGenerator { + + public SmithyRpcV2Cbor() { + super(true); + } + + @Override + public void generateSharedComponents(GenerationContext context) { + TypeScriptWriter writer = context.getWriter(); + + writer.addImportSubmodule("parseCborBody", "parseBody", + TypeScriptDependency.SMITHY_CORE, SmithyCoreSubmodules.CBOR + ) + .addImportSubmodule("parseCborErrorBody", "parseErrorBody", + TypeScriptDependency.SMITHY_CORE, SmithyCoreSubmodules.CBOR + ) + .addImportSubmodule("loadSmithyRpcV2CborErrorCode", null, + TypeScriptDependency.SMITHY_CORE, SmithyCoreSubmodules.CBOR + ); + + ServiceShape service = context.getService(); + deserializingErrorShapes.forEach(error -> generateErrorDeserializer(context, error)); + eventStreamGenerator.generateEventStreamSerializers( + context, + service, + getDocumentContentType(), + () -> { + writer.write("body = context.utf8Decoder(body);"); + }, + serializingDocumentShapes + ); + Set errorEventShapes = new TreeSet<>(); + SerdeElisionIndex serdeElisionIndex = SerdeElisionIndex.of(context.getModel()); + eventStreamGenerator.generateEventStreamDeserializers( + context, + service, + errorEventShapes, + deserializingDocumentShapes, + true, + enableSerdeElision(), + serdeElisionIndex + ); + errorEventShapes.removeIf(deserializingErrorShapes::contains); + errorEventShapes.forEach(error -> generateErrorDeserializer(context, error)); + generateDocumentBodyShapeSerializers(context, serializingDocumentShapes); + generateDocumentBodyShapeDeserializers(context, deserializingDocumentShapes); + + SymbolReference requestType = getApplicationProtocol().getRequestType(); + SymbolReference responseType = getApplicationProtocol().getResponseType(); + + HttpProtocolGeneratorUtils.generateMetadataDeserializer(context, responseType); + writer.addImport("collectBody", null, TypeScriptDependency.AWS_SMITHY_CLIENT); + + if (context.getSettings().generateClient()) { + writer.addImport("withBaseException", null, TypeScriptDependency.AWS_SMITHY_CLIENT); + SymbolReference exception = HttpProtocolGeneratorUtils.getClientBaseException(context); + writer.write("const throwDefaultError = withBaseException($T);", exception); + } + + writer.addUseImports(requestType); + writer.addImport("SerdeContext", "__SerdeContext", TypeScriptDependency.SMITHY_TYPES); + writer.addImport("HeaderBag", "__HeaderBag", TypeScriptDependency.SMITHY_TYPES); + writer.addImportSubmodule( + "buildHttpRpcRequest", null, + TypeScriptDependency.SMITHY_CORE, SmithyCoreSubmodules.CBOR + ); + writeSharedRequestHeaders(context); + writer.write(""); + + writer.write( + context.getStringStore().flushVariableDeclarationCode() + ); + } + + @Override + public ShapeId getProtocol() { + return Rpcv2CborTrait.ID; + } + + @Override + public void generateProtocolTests(GenerationContext generationContext) { + SmithyProtocolUtils.generateProtocolTests( + this, generationContext + ); + } + + @Override + protected String getDocumentContentType() { + return "application/cbor"; + } + + @Override + protected void generateDocumentBodyShapeSerializers(GenerationContext generationContext, Set shapes) { + SmithyProtocolUtils.generateDocumentBodyShapeSerde( + generationContext, + shapes, + new CborShapeSerVisitor( + generationContext + ) + ); + } + + @Override + protected void generateDocumentBodyShapeDeserializers(GenerationContext generationContext, Set shapes) { + SmithyProtocolUtils.generateDocumentBodyShapeSerde( + generationContext, + shapes, + new CborShapeDeserVisitor( + generationContext + ) + ); + } + + @Override + protected void generateOperationDeserializer(GenerationContext context, OperationShape operation) { + SymbolProvider symbolProvider = context.getSymbolProvider(); + Symbol symbol = symbolProvider.toSymbol(operation); + SymbolReference responseType = getApplicationProtocol().getResponseType(); + TypeScriptWriter writer = context.getWriter(); + + writer.addUseImports(responseType); + String methodName = ProtocolGenerator.getDeserFunctionShortName(symbol); + String methodLongName = ProtocolGenerator.getDeserFunctionName(symbol, getName()); + String errorMethodName = "de_CommandError"; + String serdeContextType = CodegenUtils.getOperationDeserializerContextType(context.getSettings(), writer, + context.getModel(), operation); + Symbol outputType = symbol.expectProperty("outputType", Symbol.class); + + writer.writeDocs(methodLongName); + writer.openBlock(""" + export const $L = async( + output: $T, + context: $L + ): Promise<$T> => {""", "}", + methodName, responseType, serdeContextType, outputType, + () -> { + writer.addImportSubmodule( + "checkCborResponse", "cr", + TypeScriptDependency.SMITHY_CORE, + SmithyCoreSubmodules.CBOR + ); + writer.write("cr(output);"); + + writer.write(""" + if (output.statusCode >= 300) { + return $L(output, context); + } + """, + errorMethodName + ); + + readResponseBody(context, operation); + + writer.write(""" + const response: $T = { + $$metadata: deserializeMetadata(output), $L + }; + return response; + """, + outputType, + operation.getOutput().map((o) -> "...contents,").orElse("") + ); + } + ); + writer.write(""); + } + + @Override + protected String getOperationPath(GenerationContext generationContext, OperationShape operationShape) { + TypeScriptSettings settings = generationContext.getSettings(); + Model model = generationContext.getModel(); + ServiceShape service = settings.getService(model); + + String serviceName = service.getId().getName(); + String operationName = operationShape.getId().getName(); + + return "/service/%s/operation/%s".formatted(serviceName, operationName); + } + + @Override + protected void serializeInputDocument(GenerationContext generationContext, + OperationShape operationShape, + StructureShape inputStructure) { + TypeScriptWriter writer = generationContext.getWriter(); + + writer.addImportSubmodule( + "cbor", null, + TypeScriptDependency.SMITHY_CORE, SmithyCoreSubmodules.CBOR + ); + writer.write("body = cbor.serialize($L);", inputStructure.accept( + new CborMemberSerVisitor(generationContext, "input")) + ); + } + + @Override + protected void writeErrorCodeParser(GenerationContext generationContext) { + TypeScriptWriter writer = generationContext.getWriter(); + + writer.addImportSubmodule( + "loadSmithyRpcV2CborErrorCode", null, + TypeScriptDependency.SMITHY_CORE, SmithyCoreSubmodules.CBOR + ); + writer.write("const errorCode = loadSmithyRpcV2CborErrorCode(output, parsedOutput.body);"); + } + + @Override + protected void deserializeOutputDocument(GenerationContext generationContext, + OperationShape operationShape, + StructureShape outputStructure) { + TypeScriptWriter writer = generationContext.getWriter(); + + writer.write("contents = $L;", outputStructure.accept( + new CborMemberDeserVisitor( + generationContext, + "data" + ) + )); + } + + @Override + protected void writeSharedRequestHeaders(GenerationContext context) { + TypeScriptWriter writer = context.getWriter(); + writer.addImport("HeaderBag", "__HeaderBag", TypeScriptDependency.SMITHY_TYPES); + writer.openBlock("const SHARED_HEADERS: __HeaderBag = {", "};", () -> { + writer.write("'content-type': $S,", getDocumentContentType()); + writer.write(""" + "smithy-protocol": "rpc-v2-cbor" + """); + }); + } + + @Override + protected boolean enableSerdeElision() { + return true; + } + + @Override + protected void writeRequestHeaders(GenerationContext context, OperationShape operation) { + TypeScriptWriter writer = context.getWriter(); + + boolean hasEventStreamOutput = EventStreamGenerator.hasEventStreamOutput(context, operation); + boolean hasEventStreamInput = EventStreamGenerator.hasEventStreamInput(context, operation); + boolean inputIsEmpty = operation.getInput().isEmpty(); + + boolean mutatesDefaultHeader = hasEventStreamOutput | hasEventStreamInput | inputIsEmpty; + + if (mutatesDefaultHeader) { + writer.write("const headers: __HeaderBag = { ...SHARED_HEADERS };"); + } else { + writer.write("const headers: __HeaderBag = SHARED_HEADERS;"); + } + + if (hasEventStreamOutput) { + writer.write(""" + headers.accept = "application/vnd.amazon.eventstream"; + """); + } + if (hasEventStreamInput) { + writer.write(""" + headers["content-type"] = "application/vnd.amazon.eventstream"; + """); + } else if (inputIsEmpty) { + writer.write(""" + delete headers["content-type"]; + """); + } + } + +} diff --git a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/util/PropertyAccessor.java b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/util/PropertyAccessor.java new file mode 100644 index 00000000000..0d33ef979b2 --- /dev/null +++ b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/util/PropertyAccessor.java @@ -0,0 +1,42 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.typescript.codegen.util; + +import java.util.regex.Pattern; + +public final class PropertyAccessor { + /** + * Starts with alpha or underscore, and contains only alphanumeric and underscores. + */ + public static final Pattern VALID_JAVASCRIPT_PROPERTY_NAME = Pattern.compile("^(?![0-9])[a-zA-Z0-9$_]+$"); + + private PropertyAccessor() {} + + /** + * @param propertyName - property being accessed. + * @return brackets wrapping the name if it's not a valid JavaScript property name. + */ + public static String getPropertyAccessor(String propertyName) { + if (VALID_JAVASCRIPT_PROPERTY_NAME.matcher(propertyName).matches()) { + return "." + propertyName; + } + if (propertyName.contains("\"")) { + // This doesn't handle cases of the special characters being pre-escaped in the propertyName, + // but that case does not currently need to be addressed. + return "[`" + propertyName + "`]"; + } + return "[\"" + propertyName + "\"]"; + } + + /** + * @param variable - object host. + * @param propertyName - property being accessed. + * @return e.g. someObject.prop or someObject['property name'] or reluctantly someObject[`bad"property"name`]. + */ + public static String getFrom(String variable, String propertyName) { + return variable + getPropertyAccessor(propertyName); + } +} diff --git a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/validation/UnaryFunctionCall.java b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/validation/UnaryFunctionCall.java new file mode 100644 index 00000000000..f27b6a95f21 --- /dev/null +++ b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/validation/UnaryFunctionCall.java @@ -0,0 +1,65 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.typescript.codegen.validation; + +import java.util.regex.Pattern; + +/** + * For handling expressions that may be unary function calls. + */ +public final class UnaryFunctionCall { + private static final Pattern CHECK_PATTERN = Pattern.compile("^(?!new ).+\\(((?!,).)*\\)$"); + private static final Pattern TO_REF_PATTERN = Pattern.compile("(.*)\\(.*\\)$"); + + private UnaryFunctionCall() { + // Private + } + + /** + * @param expression - to be examined. + * @return whether the expression is a single-depth function call with a single parameter. + */ + public static boolean check(String expression) { + if (expression.equals("_")) { + // not a call per se, but this indicates a pass-through function. + return true; + } + return maxCallDepth(expression) == 1 && CHECK_PATTERN.matcher(expression).matches(); + } + + /** + * @param callExpression the call expression to be converted. Check that + * the expression is a unary call first. + * @return the unary function call converted to a function reference. + */ + public static String toRef(String callExpression) { + return TO_REF_PATTERN.matcher(callExpression).replaceAll("$1"); + } + + /** + * Estimates the call depth of a function call expression. + * + * @param expression A function call expression (e.g., "call() == 1", "call(call()) == 2", etc). + */ + private static int maxCallDepth(String expression) { + int depth = 0; + int maxDepth = 0; + for (int i = 0; i < expression.length(); ++i) { + char c = expression.charAt(i); + if (c == '(') { + depth += 1; + if (depth > maxDepth) { + maxDepth = depth; + } + continue; + } + if (c == ')') { + depth -= 1; + } + } + return maxDepth; + } +} diff --git a/smithy-typescript-codegen/src/main/resources/META-INF/services/software.amazon.smithy.typescript.codegen.integration.TypeScriptIntegration b/smithy-typescript-codegen/src/main/resources/META-INF/services/software.amazon.smithy.typescript.codegen.integration.TypeScriptIntegration index d1d0cf3904f..76c28ba977c 100644 --- a/smithy-typescript-codegen/src/main/resources/META-INF/services/software.amazon.smithy.typescript.codegen.integration.TypeScriptIntegration +++ b/smithy-typescript-codegen/src/main/resources/META-INF/services/software.amazon.smithy.typescript.codegen.integration.TypeScriptIntegration @@ -14,3 +14,4 @@ software.amazon.smithy.typescript.codegen.integration.AddBaseServiceExceptionCla software.amazon.smithy.typescript.codegen.integration.AddSdkStreamMixinDependency software.amazon.smithy.typescript.codegen.integration.DefaultReadmeGenerator software.amazon.smithy.typescript.codegen.integration.AddCompressionDependency +software.amazon.smithy.typescript.codegen.protocols.AddProtocols diff --git a/smithy-typescript-codegen/src/main/resources/software/amazon/smithy/typescript/codegen/protocol-test-cbor-stub.ts b/smithy-typescript-codegen/src/main/resources/software/amazon/smithy/typescript/codegen/protocol-test-cbor-stub.ts new file mode 100644 index 00000000000..8377f771fc8 --- /dev/null +++ b/smithy-typescript-codegen/src/main/resources/software/amazon/smithy/typescript/codegen/protocol-test-cbor-stub.ts @@ -0,0 +1,6 @@ +const compareEquivalentCborBodies = (expectedBody: string, generatedBody: string | Uint8Array): undefined => { + expect( + normalizeByteArrayType(cbor.deserialize(typeof generatedBody === "string" ? toBytes(generatedBody) : generatedBody)) + ).toEqual(normalizeByteArrayType(cbor.deserialize(toBytes(expectedBody)))); + return undefined; +}; diff --git a/smithy-typescript-codegen/src/main/resources/software/amazon/smithy/typescript/codegen/protocol-test-stub.ts b/smithy-typescript-codegen/src/main/resources/software/amazon/smithy/typescript/codegen/protocol-test-stub.ts index dc5283c1379..6dd72041699 100644 --- a/smithy-typescript-codegen/src/main/resources/software/amazon/smithy/typescript/codegen/protocol-test-stub.ts +++ b/smithy-typescript-codegen/src/main/resources/software/amazon/smithy/typescript/codegen/protocol-test-stub.ts @@ -1,4 +1,4 @@ -import { HttpHandlerOptions, HeaderBag } from "@smithy/types"; +import { HttpHandlerOptions, HeaderBag, Endpoint } from "@smithy/types"; import { HttpHandler, HttpRequest, HttpResponse } from "@smithy/protocol-http"; import { Readable } from "stream"; @@ -32,9 +32,10 @@ class ResponseDeserializationTestHandler implements HttpHandler { isSuccess: boolean; code: number; headers: HeaderBag; - body: String; + body: string | Uint8Array; + isBase64Body: boolean; - constructor(isSuccess: boolean, code: number, headers?: HeaderBag, body?: String) { + constructor(isSuccess: boolean, code: number, headers?: HeaderBag, body?: string) { this.isSuccess = isSuccess; this.code = code; if (headers === undefined) { @@ -46,6 +47,7 @@ class ResponseDeserializationTestHandler implements HttpHandler { body = ""; } this.body = body; + this.isBase64Body = String(body).length > 0 && Buffer.from(String(body), "base64").toString("base64") === body; } handle(request: HttpRequest, options?: HttpHandlerOptions): Promise<{ response: HttpResponse }> { @@ -53,11 +55,13 @@ class ResponseDeserializationTestHandler implements HttpHandler { response: new HttpResponse({ statusCode: this.code, headers: this.headers, - body: Readable.from([this.body]), + body: this.isBase64Body ? toBytes(this.body as string) : Readable.from([this.body]), }), }); } + updateHttpClientConfig(key: never, value: never): void {} + httpHandlerConfigs() { return {}; } @@ -98,6 +102,11 @@ const compareParts = (expectedParts: comparableParts, generatedParts: comparable * properties that have defined values. */ const equivalentContents = (expected: any, generated: any): boolean => { + if (typeof (global as any).expect === "function") { + expect(normalizeByteArrayType(generated)).toEqual(normalizeByteArrayType(expected)); + return true; + } + let localExpected = expected; // Short circuit on equality. @@ -105,6 +114,10 @@ const equivalentContents = (expected: any, generated: any): boolean => { return true; } + if (typeof expected !== "object") { + return expected === generated; + } + // If a test fails with an issue in the below 6 lines, it's likely // due to an issue in the nestedness or existence of the property // being compared. @@ -134,8 +147,15 @@ const equivalentContents = (expected: any, generated: any): boolean => { const clientParams = { region: "us-west-2", - endpoint: "https://localhost/", credentials: { accessKeyId: "key", secretAccessKey: "secret" }, + endpoint: () => { + const url = new URL("https://localhost/"); + return Promise.resolve({ + ...url, + path: url.pathname, + ...(url.port ? { port: Number(url.port) } : {}), + }) as Promise; + }, }; /** @@ -145,3 +165,34 @@ const clientParams = { const fail = (error?: any): never => { throw new Error(error); }; + +/** + * Hexadecimal to byteArray. + */ +const toBytes = (hex: string) => { + return Buffer.from(hex, "base64"); +}; + +function normalizeByteArrayType(data: any) { + // normalize float32 errors + if (typeof data === "number") { + const u = new Uint8Array(4); + const dv = new DataView(u.buffer, u.byteOffset, u.byteLength); + dv.setFloat32(0, data); + return dv.getFloat32(0); + } + if (!data || typeof data !== "object") { + return data; + } + if (data instanceof Uint8Array) { + return Uint8Array.from(data); + } + if (data instanceof String || data instanceof Boolean || data instanceof Number) { + return data.valueOf(); + } + const output = {} as any; + for (const key of Object.getOwnPropertyNames(data)) { + output[key] = normalizeByteArrayType(data[key]); + } + return output; +} diff --git a/smithy-typescript-codegen/src/test/java/software/amazon/smithy/typescript/codegen/TypeScriptWriterTest.java b/smithy-typescript-codegen/src/test/java/software/amazon/smithy/typescript/codegen/TypeScriptWriterTest.java index 1facfde1c57..fe5022a415f 100644 --- a/smithy-typescript-codegen/src/test/java/software/amazon/smithy/typescript/codegen/TypeScriptWriterTest.java +++ b/smithy-typescript-codegen/src/test/java/software/amazon/smithy/typescript/codegen/TypeScriptWriterTest.java @@ -65,4 +65,16 @@ public void addsFormatterForSymbols() { public void addsFormatterForSymbolReferences() { // TODO } + + @Test + public void addImportSubmodule() { + TypeScriptWriter writer = new TypeScriptWriter("foo"); + writer.addDependency(TypeScriptDependency.SMITHY_CORE); + writer.addImportSubmodule("symbol", "__symbol", () -> "@smithy/core", "/submodule"); + String result = writer.toString(); + + assertThat(result.trim(), equalTo(""" + %simport { symbol as __symbol } from "@smithy/core/submodule"; + """.formatted(CODEGEN_INDICATOR).trim())); + } } diff --git a/smithy-typescript-codegen/src/test/java/software/amazon/smithy/typescript/codegen/protocols/cbor/CborMemberDeserVisitorTest.java b/smithy-typescript-codegen/src/test/java/software/amazon/smithy/typescript/codegen/protocols/cbor/CborMemberDeserVisitorTest.java new file mode 100644 index 00000000000..9076e5d9a32 --- /dev/null +++ b/smithy-typescript-codegen/src/test/java/software/amazon/smithy/typescript/codegen/protocols/cbor/CborMemberDeserVisitorTest.java @@ -0,0 +1,58 @@ +package software.amazon.smithy.typescript.codegen.protocols.cbor; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import software.amazon.smithy.model.Model; +import software.amazon.smithy.model.shapes.BlobShape; +import software.amazon.smithy.model.shapes.TimestampShape; +import software.amazon.smithy.typescript.codegen.TypeScriptSettings; +import software.amazon.smithy.typescript.codegen.TypeScriptWriter; +import software.amazon.smithy.typescript.codegen.integration.ProtocolGenerator; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class CborMemberDeserVisitorTest { + private CborMemberDeserVisitor subject; + + @BeforeEach + void setup( + @Mock ProtocolGenerator.GenerationContext context, + @Mock Model model, + @Mock TypeScriptWriter typeScriptWriter, + @Mock TypeScriptSettings settings + ) { + when(context.getModel()).thenReturn(model); + when(context.getWriter()).thenReturn(typeScriptWriter); + when(context.getSettings()).thenReturn(settings); + + subject = new CborMemberDeserVisitor( + context, + "data" + ); + } + + @Test + void blobShape(@Mock BlobShape blobShape) { + // no decoder for blob in cbor. + assertEquals( + "data", + subject.blobShape( + blobShape + ) + ); + } + + @Test + void timestampShape(@Mock TimestampShape timestampShape) { + // protocol always uses this timestamp format. + assertEquals( + "__expectNonNull(__parseEpochTimestamp(data))", + subject.timestampShape(timestampShape) + ); + } +} \ No newline at end of file diff --git a/smithy-typescript-codegen/src/test/java/software/amazon/smithy/typescript/codegen/protocols/cbor/CborMemberSerVisitorTest.java b/smithy-typescript-codegen/src/test/java/software/amazon/smithy/typescript/codegen/protocols/cbor/CborMemberSerVisitorTest.java new file mode 100644 index 00000000000..dadb9b0c0b5 --- /dev/null +++ b/smithy-typescript-codegen/src/test/java/software/amazon/smithy/typescript/codegen/protocols/cbor/CborMemberSerVisitorTest.java @@ -0,0 +1,74 @@ +package software.amazon.smithy.typescript.codegen.protocols.cbor; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import software.amazon.smithy.model.Model; +import software.amazon.smithy.model.shapes.BlobShape; +import software.amazon.smithy.model.shapes.DoubleShape; +import software.amazon.smithy.model.shapes.FloatShape; +import software.amazon.smithy.model.shapes.TimestampShape; +import software.amazon.smithy.typescript.codegen.TypeScriptWriter; +import software.amazon.smithy.typescript.codegen.integration.ProtocolGenerator; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.lenient; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class CborMemberSerVisitorTest { + + private CborMemberSerVisitor subject; + + @BeforeEach + void setup( + @Mock ProtocolGenerator.GenerationContext context, + @Mock Model model, + @Mock TypeScriptWriter typeScriptWriter + ) { + when(context.getModel()).thenReturn(model); + lenient().when(context.getWriter()).thenReturn(typeScriptWriter); + + subject = new CborMemberSerVisitor( + context, + "data" + ); + } + + @Test + void blobShape(@Mock BlobShape blob) { + // no encoder for blob in cbor. + assertEquals( + "data", + subject.blobShape(blob) + ); + } + + @Test + void floatShape(@Mock FloatShape floatShape) { + // no serializer function for float in cbor. + assertEquals( + "data", + subject.floatShape(floatShape) + ); + } + + @Test + void doubleShape(@Mock DoubleShape doubleShape) { + // no serializer function for double in cbor. + assertEquals( + "data", + subject.doubleShape(doubleShape) + ); + } + + @Test + void timestampShape(@Mock TimestampShape timestampShape) { + assertEquals( + "__dateToTag(data)", + subject.timestampShape(timestampShape) + ); + } +} \ No newline at end of file diff --git a/smithy-typescript-codegen/src/test/java/software/amazon/smithy/typescript/codegen/protocols/cbor/CborShapeDeserVisitorTest.java b/smithy-typescript-codegen/src/test/java/software/amazon/smithy/typescript/codegen/protocols/cbor/CborShapeDeserVisitorTest.java new file mode 100644 index 00000000000..80775da1c75 --- /dev/null +++ b/smithy-typescript-codegen/src/test/java/software/amazon/smithy/typescript/codegen/protocols/cbor/CborShapeDeserVisitorTest.java @@ -0,0 +1,157 @@ +package software.amazon.smithy.typescript.codegen.protocols.cbor; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import software.amazon.smithy.codegen.core.Symbol; +import software.amazon.smithy.codegen.core.SymbolProvider; +import software.amazon.smithy.model.Model; +import software.amazon.smithy.model.shapes.CollectionShape; +import software.amazon.smithy.model.shapes.DocumentShape; +import software.amazon.smithy.model.shapes.MapShape; +import software.amazon.smithy.model.shapes.MemberShape; +import software.amazon.smithy.model.shapes.Shape; +import software.amazon.smithy.model.shapes.ShapeId; +import software.amazon.smithy.model.shapes.StructureShape; +import software.amazon.smithy.model.shapes.UnionShape; +import software.amazon.smithy.typescript.codegen.TypeScriptDependency; +import software.amazon.smithy.typescript.codegen.TypeScriptSettings; +import software.amazon.smithy.typescript.codegen.TypeScriptWriter; +import software.amazon.smithy.typescript.codegen.integration.ProtocolGenerator; +import software.amazon.smithy.utils.MapUtils; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.lenient; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class CborShapeDeserVisitorTest { + + @Mock + ProtocolGenerator.GenerationContext context; + @Mock + TypeScriptWriter writer; + CborShapeDeserVisitor subject; + + @BeforeEach + void setUp( + @Mock TypeScriptSettings typeScriptSettings + ) { + lenient().when(context.getWriter()).thenReturn(writer); + lenient().when(context.getSettings()).thenReturn(typeScriptSettings); + lenient().when(typeScriptSettings.generateServerSdk()) + .thenReturn(false); + + subject = new CborShapeDeserVisitor( + context + ); + } + + @Test + void deserializeCollection( + @Mock CollectionShape collectionShape, + @Mock MemberShape memberShape, + @Mock ShapeId shapeId, + @Mock Shape target, + @Mock Model model + ) { + when(collectionShape.getMember()).thenReturn(memberShape); + when(memberShape.getTarget()).thenReturn(shapeId); + when(context.getModel()).thenReturn(model); + when(model.expectShape(any(ShapeId.class))) + .thenReturn(target); + when(target.accept(any())).thenReturn("entry"); + + subject.deserializeCollection(context, collectionShape); + + verify(writer).write( + "const collection = (output || [])$L", + ".filter((e: any) => e != null)" + ); + } + + @Test + void deserializeDocument( + @Mock DocumentShape documentShape + ) { + subject.deserializeDocument(context, documentShape); + verify(writer).write( + """ + return output; // document. + """ + ); + } + + @Test + void deserializeMap(@Mock MapShape mapShape, + @Mock MemberShape valueShape, + @Mock ShapeId shapeId, + @Mock SymbolProvider symbolProvider, + @Mock Symbol symbol, + @Mock Shape target, + @Mock Model model) { + when(mapShape.getValue()).thenReturn(valueShape); + when(valueShape.getTarget()).thenReturn(shapeId); + when(context.getSymbolProvider()).thenReturn(symbolProvider); + when(context.getModel()).thenReturn(model); + when(model.expectShape(shapeId)).thenReturn(target); + when(symbolProvider.toSymbol(mapShape)) + .thenReturn(symbol); + + subject.deserializeMap(context, mapShape); + verify(writer).openBlock( + eq("return Object.entries(output).reduce((acc: $T, [key, value]: [string, any]) => {"), + eq(""), + eq(symbol), + any() + ); + } + + @Test + void deserializeStructure(@Mock StructureShape structureShape) { + subject.deserializeStructure(context, structureShape); + + verify(writer).addImport( + "take", null, TypeScriptDependency.AWS_SMITHY_CLIENT + ); + verify(writer).openBlock( + eq("return take(output, {"), + eq("}) as any;"), + any() + ); + } + + @Test + void deserializeUnion(@Mock UnionShape unionShape, + @Mock MemberShape mapMember, + @Mock ShapeId shapeId, + @Mock Shape target, + @Mock Model model) { + when(unionShape.getAllMembers()).thenReturn( + MapUtils.of( + "member", mapMember + ) + ); + + when(mapMember.getTarget()).thenReturn(shapeId); + when(context.getModel()).thenReturn(model); + when(model.expectShape(shapeId)).thenReturn(target); + + subject.deserializeUnion(context, unionShape); + + verify(writer).openBlock( + eq("if ($1L != null) {"), + eq("}"), + eq("output.member"), + any() + ); + + verify(writer).write( + "return { $$unknown: Object.entries(output)[0] };" + ); + } +} \ No newline at end of file diff --git a/smithy-typescript-codegen/src/test/java/software/amazon/smithy/typescript/codegen/protocols/cbor/CborShapeSerVisitorTest.java b/smithy-typescript-codegen/src/test/java/software/amazon/smithy/typescript/codegen/protocols/cbor/CborShapeSerVisitorTest.java new file mode 100644 index 00000000000..6554a1770d3 --- /dev/null +++ b/smithy-typescript-codegen/src/test/java/software/amazon/smithy/typescript/codegen/protocols/cbor/CborShapeSerVisitorTest.java @@ -0,0 +1,148 @@ +package software.amazon.smithy.typescript.codegen.protocols.cbor; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import software.amazon.smithy.codegen.core.Symbol; +import software.amazon.smithy.codegen.core.SymbolProvider; +import software.amazon.smithy.model.Model; +import software.amazon.smithy.model.shapes.CollectionShape; +import software.amazon.smithy.model.shapes.DocumentShape; +import software.amazon.smithy.model.shapes.MapShape; +import software.amazon.smithy.model.shapes.MemberShape; +import software.amazon.smithy.model.shapes.ServiceShape; +import software.amazon.smithy.model.shapes.Shape; +import software.amazon.smithy.model.shapes.ShapeId; +import software.amazon.smithy.model.shapes.StructureShape; +import software.amazon.smithy.model.shapes.UnionShape; +import software.amazon.smithy.typescript.codegen.TypeScriptDependency; +import software.amazon.smithy.typescript.codegen.TypeScriptWriter; +import software.amazon.smithy.typescript.codegen.integration.ProtocolGenerator; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.lenient; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class CborShapeSerVisitorTest { + + CborShapeSerVisitor subject; + + @Mock + ProtocolGenerator.GenerationContext context; + @Mock + TypeScriptWriter writer; + + @BeforeEach + void setUp( + @Mock Model model, + @Mock Shape shape, + @Mock SymbolProvider symbolProvider, + @Mock Symbol symbol + ) { + lenient().when(context.getWriter()).thenReturn(writer); + lenient().when(context.getSymbolProvider()).thenReturn(symbolProvider); + lenient().when(symbolProvider.toSymbol(any())) + .thenReturn(symbol); + lenient().when(symbol.toString()) + .thenReturn("string"); + lenient().when(context.getModel()).thenReturn(model); + lenient().when(model.expectShape(any(ShapeId.class))).thenReturn(shape); + lenient().when(shape.accept(any(CborMemberSerVisitor.class))) + .thenReturn("entry"); + + subject = new CborShapeSerVisitor( + context + ); + } + + @Test + void serializeCollection( + @Mock CollectionShape collectionShape, + @Mock MemberShape memberShape, + @Mock ShapeId shapeId + ) { + when(collectionShape.getMember()).thenReturn(memberShape); + when(memberShape.getTarget()).thenReturn(shapeId); + + subject.serializeCollection( + context, + collectionShape + ); + verify(writer).write( + "return input$L;", + ".filter((e: any) => e != null)" + ); + } + + @Test + void serializeDocument(@Mock DocumentShape documentShape) { + subject.serializeDocument( + context, + documentShape + ); + verify(writer).write( + """ + return input; // document. + """ + ); + } + + @Test + void serializeMap(@Mock MapShape mapShape, + @Mock MemberShape valueShape, + @Mock ShapeId shapeId) { + when(mapShape.getValue()).thenReturn(valueShape); + when(valueShape.getTarget()).thenReturn(shapeId); + + subject.serializeMap( + context, + mapShape + ); + verify(writer).openBlock( + eq("return Object.entries(input).reduce((acc: Record, [key, value]: [$1L, any]) => {"), + eq("}, {});"), + eq("string"), + any() + ); + } + + @Test + void serializeStructure(@Mock StructureShape structureShape) { + subject.serializeStructure( + context, + structureShape + ); + verify(writer).addImport("take", null, TypeScriptDependency.AWS_SMITHY_CLIENT); + verify(writer).openBlock( + eq("return take(input, {"), + eq("});"), + any() + ); + } + + @Test + void serializeUnion(@Mock UnionShape unionShape, + @Mock ServiceShape service, + @Mock ShapeId shapeId) { + when(context.getService()).thenReturn(service); + when(unionShape.getId()).thenReturn(shapeId); + when(shapeId.getName(service)).thenReturn("name"); + + subject.serializeUnion( + context, + unionShape + ); + + verify(writer).openBlock( + eq("return $L.visit(input, {"), + eq("});"), + eq("name"), + any() + ); + } +} \ No newline at end of file diff --git a/smithy-typescript-codegen/src/test/java/software/amazon/smithy/typescript/codegen/util/PropertyAccessorTest.java b/smithy-typescript-codegen/src/test/java/software/amazon/smithy/typescript/codegen/util/PropertyAccessorTest.java new file mode 100644 index 00000000000..468c218d5ac --- /dev/null +++ b/smithy-typescript-codegen/src/test/java/software/amazon/smithy/typescript/codegen/util/PropertyAccessorTest.java @@ -0,0 +1,24 @@ +package software.amazon.smithy.typescript.codegen.util; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class PropertyAccessorTest { + @Test + void getFrom() { + assertEquals("output.fileSystemId", PropertyAccessor.getFrom("output", "fileSystemId")); + assertEquals("output.__fileSystemId", PropertyAccessor.getFrom("output", "__fileSystemId")); + } + + @Test + void getFromQuoted() { + assertEquals("output[\"0fileSystemId\"]", PropertyAccessor.getFrom("output", "0fileSystemId")); + assertEquals("output[\"file-system-id\"]", PropertyAccessor.getFrom("output", "file-system-id")); + } + + @Test + void getFromExtraQuoted() { + assertEquals("output[`file\"system\"id`]", PropertyAccessor.getFrom("output", "file\"system\"id")); + } +} \ No newline at end of file diff --git a/smithy-typescript-codegen/src/test/java/software/amazon/smithy/typescript/codegen/validation/UnaryFunctionCallTest.java b/smithy-typescript-codegen/src/test/java/software/amazon/smithy/typescript/codegen/validation/UnaryFunctionCallTest.java new file mode 100644 index 00000000000..61d510e9ef4 --- /dev/null +++ b/smithy-typescript-codegen/src/test/java/software/amazon/smithy/typescript/codegen/validation/UnaryFunctionCallTest.java @@ -0,0 +1,36 @@ +package software.amazon.smithy.typescript.codegen.validation; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +class UnaryFunctionCallTest { + @Test + void check() { + assertEquals(false, UnaryFunctionCall.check("")); + assertEquals(false, UnaryFunctionCall.check("5")); + assertEquals(false, UnaryFunctionCall.check("(param)")); + assertEquals(false, UnaryFunctionCall.check("x[5]")); + assertEquals(false, UnaryFunctionCall.check("new Date(timestamp)")); + assertEquals(false, UnaryFunctionCall.check("x(y(_))")); + assertEquals(false, UnaryFunctionCall.check("call(param).prop")); + assertEquals(false, UnaryFunctionCall.check("call(param, param2)")); + + assertEquals(true, UnaryFunctionCall.check("_")); + assertEquals(true, UnaryFunctionCall.check("x()")); + assertEquals(true, UnaryFunctionCall.check("x(_)")); + assertEquals(true, UnaryFunctionCall.check("long_function_name(long_parameter_name)")); + assertEquals(true, UnaryFunctionCall.check("container.function(param)")); + assertEquals(true, UnaryFunctionCall.check("factory(param)(param2)")); + } + + @Test + void toRef() { + assertEquals("_", UnaryFunctionCall.toRef("_")); + assertEquals("x", UnaryFunctionCall.toRef("x()")); + assertEquals("x", UnaryFunctionCall.toRef("x(_)")); + assertEquals("long_function_name", UnaryFunctionCall.toRef("long_function_name(long_parameter_name)")); + assertEquals("container.function", UnaryFunctionCall.toRef("container.function(param)")); + assertEquals("factory(param)", UnaryFunctionCall.toRef("factory(param)(param2)")); + } +} \ No newline at end of file diff --git a/smithy-typescript-protocol-test-codegen/build.gradle.kts b/smithy-typescript-protocol-test-codegen/build.gradle.kts new file mode 100644 index 00000000000..31005e01a22 --- /dev/null +++ b/smithy-typescript-protocol-test-codegen/build.gradle.kts @@ -0,0 +1,36 @@ +import software.amazon.smithy.gradle.tasks.SmithyBuild + +val smithyVersion: String by project + +repositories { + mavenLocal() + mavenCentral() +} + +buildscript { + val smithyVersion: String by project + dependencies { + classpath("software.amazon.smithy:smithy-cli:$smithyVersion") + } +} + +plugins { + id("software.amazon.smithy").version("0.6.0") +} + +dependencies { + implementation("software.amazon.smithy:smithy-protocol-tests:$smithyVersion") + implementation(project(":smithy-typescript-codegen")) +} + +// This project doesn't produce a JAR. +tasks["jar"].enabled = false + +tasks["smithyBuildJar"].enabled = false + +tasks.create("buildSdk") { + addRuntimeClasspath = true +} + +// Run the `buildSdk` automatically. +tasks["build"].finalizedBy(tasks["buildSdk"]) diff --git a/smithy-typescript-protocol-test-codegen/smithy-build.json b/smithy-typescript-protocol-test-codegen/smithy-build.json new file mode 100644 index 00000000000..836ec8edb60 --- /dev/null +++ b/smithy-typescript-protocol-test-codegen/smithy-build.json @@ -0,0 +1,30 @@ +{ + "version": "1.0", + "projections": { + "smithy-rpcv2-cbor": { + "transforms": [ + { + "name": "includeServices", + "args": { + "services": ["smithy.protocoltests.rpcv2Cbor#RpcV2Protocol"] + } + } + ], + "plugins": { + "typescript-codegen": { + "package": "@smithy/smithy-rpcv2-cbor", + "packageVersion": "1.0.0-alpha.1", + "packageJson": { + "author": { + "name": "Smithy team", + "url": "https://smithy.io/" + }, + "license": "Apache-2.0" + }, + "private": true, + "experimentalIdentityAndAuth": false + } + } + } + } +} diff --git a/smithy-typescript-ssdk-codegen-test-utils/src/main/java/software/amazon/smithy/typescript/ssdk/codegen/test/utils/AddProtocols.java b/smithy-typescript-ssdk-codegen-test-utils/src/main/java/software/amazon/smithy/typescript/ssdk/codegen/test/utils/AddProtocols.java index d4de750d038..c75a98a970c 100644 --- a/smithy-typescript-ssdk-codegen-test-utils/src/main/java/software/amazon/smithy/typescript/ssdk/codegen/test/utils/AddProtocols.java +++ b/smithy-typescript-ssdk-codegen-test-utils/src/main/java/software/amazon/smithy/typescript/ssdk/codegen/test/utils/AddProtocols.java @@ -14,7 +14,9 @@ public class AddProtocols implements TypeScriptIntegration { @Override public List getProtocolGenerators() { - return ListUtils.of(new TestProtocolGenerator()); + return ListUtils.of( + new TestProtocolGenerator() + ); } } \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 242b1e068e0..dc8674e8157 100644 --- a/yarn.lock +++ b/yarn.lock @@ -26,7 +26,22 @@ __metadata: languageName: node linkType: hard -"@aws-crypto/sha256-js@npm:5.2.0": +"@aws-crypto/sha256-browser@npm:5.2.0": + version: 5.2.0 + resolution: "@aws-crypto/sha256-browser@npm:5.2.0" + dependencies: + "@aws-crypto/sha256-js": ^5.2.0 + "@aws-crypto/supports-web-crypto": ^5.2.0 + "@aws-crypto/util": ^5.2.0 + "@aws-sdk/types": ^3.222.0 + "@aws-sdk/util-locate-window": ^3.0.0 + "@smithy/util-utf8": ^2.0.0 + tslib: ^2.6.2 + checksum: 773f12f2026d82a6bb4a23a8f491894a6d32525bd9b8bfbc12896526cf11882a7607a671c478c45f9cd7d6ba1caaed48a62b67c6f725244bd83a1275108f46c7 + languageName: node + linkType: hard + +"@aws-crypto/sha256-js@npm:5.2.0, @aws-crypto/sha256-js@npm:^5.2.0": version: 5.2.0 resolution: "@aws-crypto/sha256-js@npm:5.2.0" dependencies: @@ -37,6 +52,15 @@ __metadata: languageName: node linkType: hard +"@aws-crypto/supports-web-crypto@npm:^5.2.0": + version: 5.2.0 + resolution: "@aws-crypto/supports-web-crypto@npm:5.2.0" + dependencies: + tslib: ^2.6.2 + checksum: 6ffc21de48b2b2c3e918193101d7e8fe949d47b37688892e1c39eaedaa938be80c0f404fe1c874c30cce16781026777a53bf47d5d90143ca91d0feb7c4a6f830 + languageName: node + linkType: hard + "@aws-crypto/util@npm:^5.2.0": version: 5.2.0 resolution: "@aws-crypto/util@npm:5.2.0" @@ -57,6 +81,25 @@ __metadata: languageName: node linkType: hard +"@aws-sdk/types@npm:latest": + version: 3.598.0 + resolution: "@aws-sdk/types@npm:3.598.0" + dependencies: + "@smithy/types": ^3.1.0 + tslib: ^2.6.2 + checksum: 9b2bd50d6935422dd1046e6eaa48c4c774d06aa1374bf4600a3af2c7a52432b5e25ec111cf49976b07b03d7cb5f4fa6fd44b7ce8a67dd0b3a4cee4abaf9e6fa5 + languageName: node + linkType: hard + +"@aws-sdk/util-locate-window@npm:^3.0.0": + version: 3.568.0 + resolution: "@aws-sdk/util-locate-window@npm:3.568.0" + dependencies: + tslib: ^2.6.2 + checksum: 354db5187beee4203c7ec6583556ab14ecde9644c06aaa51fa2528131836d3fc73035a3b080c904e108c49defce20d5562893113b93d819b70497f47989bb578 + languageName: node + linkType: hard + "@aws-smithy/server-apigateway@workspace:smithy-typescript-ssdk-libs/server-apigateway": version: 0.0.0-use.local resolution: "@aws-smithy/server-apigateway@workspace:smithy-typescript-ssdk-libs/server-apigateway" @@ -2207,7 +2250,7 @@ __metadata: languageName: unknown linkType: soft -"@smithy/config-resolver@workspace:^, @smithy/config-resolver@workspace:packages/config-resolver": +"@smithy/config-resolver@^3.0.5, @smithy/config-resolver@workspace:^, @smithy/config-resolver@workspace:packages/config-resolver": version: 0.0.0-use.local resolution: "@smithy/config-resolver@workspace:packages/config-resolver" dependencies: @@ -2223,7 +2266,7 @@ __metadata: languageName: unknown linkType: soft -"@smithy/core@workspace:packages/core": +"@smithy/core@^2.3.2, @smithy/core@workspace:packages/core": version: 0.0.0-use.local resolution: "@smithy/core@workspace:packages/core" dependencies: @@ -2233,10 +2276,13 @@ __metadata: "@smithy/protocol-http": "workspace:^" "@smithy/smithy-client": "workspace:^" "@smithy/types": "workspace:^" + "@smithy/util-body-length-browser": "workspace:^" "@smithy/util-middleware": "workspace:^" + "@smithy/util-utf8": "workspace:^" "@types/node": ^16.18.96 concurrently: 7.0.0 downlevel-dts: 0.10.1 + json-bigint: ^1.0.0 rimraf: 3.0.2 tslib: ^2.6.2 typedoc: 0.23.23 @@ -2356,7 +2402,7 @@ __metadata: languageName: unknown linkType: soft -"@smithy/fetch-http-handler@workspace:^, @smithy/fetch-http-handler@workspace:packages/fetch-http-handler": +"@smithy/fetch-http-handler@^3.2.4, @smithy/fetch-http-handler@workspace:^, @smithy/fetch-http-handler@workspace:packages/fetch-http-handler": version: 0.0.0-use.local resolution: "@smithy/fetch-http-handler@workspace:packages/fetch-http-handler" dependencies: @@ -2389,7 +2435,7 @@ __metadata: languageName: unknown linkType: soft -"@smithy/hash-node@workspace:packages/hash-node": +"@smithy/hash-node@^3.0.3, @smithy/hash-node@workspace:packages/hash-node": version: 0.0.0-use.local resolution: "@smithy/hash-node@workspace:packages/hash-node" dependencies: @@ -2423,7 +2469,7 @@ __metadata: languageName: unknown linkType: soft -"@smithy/invalid-dependency@workspace:packages/invalid-dependency": +"@smithy/invalid-dependency@^3.0.3, @smithy/invalid-dependency@workspace:packages/invalid-dependency": version: 0.0.0-use.local resolution: "@smithy/invalid-dependency@workspace:packages/invalid-dependency" dependencies: @@ -2511,7 +2557,7 @@ __metadata: languageName: unknown linkType: soft -"@smithy/middleware-content-length@workspace:packages/middleware-content-length": +"@smithy/middleware-content-length@^3.0.5, @smithy/middleware-content-length@workspace:packages/middleware-content-length": version: 0.0.0-use.local resolution: "@smithy/middleware-content-length@workspace:packages/middleware-content-length" dependencies: @@ -2544,7 +2590,7 @@ __metadata: languageName: unknown linkType: soft -"@smithy/middleware-retry@workspace:^, @smithy/middleware-retry@workspace:packages/middleware-retry": +"@smithy/middleware-retry@^3.0.14, @smithy/middleware-retry@workspace:^, @smithy/middleware-retry@workspace:packages/middleware-retry": version: 0.0.0-use.local resolution: "@smithy/middleware-retry@workspace:packages/middleware-retry" dependencies: @@ -2566,7 +2612,7 @@ __metadata: languageName: unknown linkType: soft -"@smithy/middleware-serde@workspace:^, @smithy/middleware-serde@workspace:packages/middleware-serde": +"@smithy/middleware-serde@^3.0.3, @smithy/middleware-serde@workspace:^, @smithy/middleware-serde@workspace:packages/middleware-serde": version: 0.0.0-use.local resolution: "@smithy/middleware-serde@workspace:packages/middleware-serde" dependencies: @@ -2580,7 +2626,7 @@ __metadata: languageName: unknown linkType: soft -"@smithy/middleware-stack@workspace:^, @smithy/middleware-stack@workspace:packages/middleware-stack": +"@smithy/middleware-stack@^3.0.3, @smithy/middleware-stack@workspace:^, @smithy/middleware-stack@workspace:packages/middleware-stack": version: 0.0.0-use.local resolution: "@smithy/middleware-stack@workspace:packages/middleware-stack" dependencies: @@ -2593,7 +2639,7 @@ __metadata: languageName: unknown linkType: soft -"@smithy/node-config-provider@workspace:^, @smithy/node-config-provider@workspace:packages/node-config-provider": +"@smithy/node-config-provider@^3.1.4, @smithy/node-config-provider@workspace:^, @smithy/node-config-provider@workspace:packages/node-config-provider": version: 0.0.0-use.local resolution: "@smithy/node-config-provider@workspace:packages/node-config-provider" dependencies: @@ -2609,7 +2655,7 @@ __metadata: languageName: unknown linkType: soft -"@smithy/node-http-handler@workspace:^, @smithy/node-http-handler@workspace:packages/node-http-handler": +"@smithy/node-http-handler@^3.1.4, @smithy/node-http-handler@workspace:^, @smithy/node-http-handler@workspace:packages/node-http-handler": version: 0.0.0-use.local resolution: "@smithy/node-http-handler@workspace:packages/node-http-handler" dependencies: @@ -2639,7 +2685,7 @@ __metadata: languageName: unknown linkType: soft -"@smithy/protocol-http@workspace:^, @smithy/protocol-http@workspace:packages/protocol-http": +"@smithy/protocol-http@^4.1.0, @smithy/protocol-http@workspace:^, @smithy/protocol-http@workspace:packages/protocol-http": version: 0.0.0-use.local resolution: "@smithy/protocol-http@workspace:packages/protocol-http" dependencies: @@ -2738,7 +2784,7 @@ __metadata: languageName: unknown linkType: soft -"@smithy/smithy-client@workspace:^, @smithy/smithy-client@workspace:packages/smithy-client": +"@smithy/smithy-client@^3.1.12, @smithy/smithy-client@workspace:^, @smithy/smithy-client@workspace:packages/smithy-client": version: 0.0.0-use.local resolution: "@smithy/smithy-client@workspace:packages/smithy-client" dependencies: @@ -2756,7 +2802,47 @@ __metadata: languageName: unknown linkType: soft -"@smithy/types@workspace:^, @smithy/types@workspace:packages/types": +"@smithy/smithy-rpcv2-cbor@workspace:private/smithy-rpcv2-cbor": + version: 0.0.0-use.local + resolution: "@smithy/smithy-rpcv2-cbor@workspace:private/smithy-rpcv2-cbor" + dependencies: + "@aws-crypto/sha256-browser": 5.2.0 + "@aws-crypto/sha256-js": 5.2.0 + "@aws-sdk/types": latest + "@smithy/config-resolver": ^3.0.5 + "@smithy/core": ^2.3.2 + "@smithy/fetch-http-handler": ^3.2.4 + "@smithy/hash-node": ^3.0.3 + "@smithy/invalid-dependency": ^3.0.3 + "@smithy/middleware-content-length": ^3.0.5 + "@smithy/middleware-retry": ^3.0.14 + "@smithy/middleware-serde": ^3.0.3 + "@smithy/middleware-stack": ^3.0.3 + "@smithy/node-config-provider": ^3.1.4 + "@smithy/node-http-handler": ^3.1.4 + "@smithy/protocol-http": ^4.1.0 + "@smithy/smithy-client": ^3.1.12 + "@smithy/types": ^3.3.0 + "@smithy/url-parser": ^3.0.3 + "@smithy/util-base64": ^3.0.0 + "@smithy/util-body-length-browser": ^3.0.0 + "@smithy/util-body-length-node": ^3.0.0 + "@smithy/util-defaults-mode-browser": ^3.0.14 + "@smithy/util-defaults-mode-node": ^3.0.14 + "@smithy/util-middleware": ^3.0.3 + "@smithy/util-retry": ^3.0.3 + "@smithy/util-utf8": ^3.0.0 + "@tsconfig/node16": 16.1.3 + "@types/node": ^16.18.96 + concurrently: 7.0.0 + downlevel-dts: 0.10.1 + rimraf: ^3.0.0 + tslib: ^2.6.2 + typescript: ~4.9.5 + languageName: unknown + linkType: soft + +"@smithy/types@^3.1.0, @smithy/types@^3.3.0, @smithy/types@workspace:^, @smithy/types@workspace:packages/types": version: 0.0.0-use.local resolution: "@smithy/types@workspace:packages/types" dependencies: @@ -2768,7 +2854,7 @@ __metadata: languageName: unknown linkType: soft -"@smithy/url-parser@workspace:^, @smithy/url-parser@workspace:packages/url-parser": +"@smithy/url-parser@^3.0.3, @smithy/url-parser@workspace:^, @smithy/url-parser@workspace:packages/url-parser": version: 0.0.0-use.local resolution: "@smithy/url-parser@workspace:packages/url-parser" dependencies: @@ -2782,7 +2868,7 @@ __metadata: languageName: unknown linkType: soft -"@smithy/util-base64@workspace:^, @smithy/util-base64@workspace:packages/util-base64": +"@smithy/util-base64@^3.0.0, @smithy/util-base64@workspace:^, @smithy/util-base64@workspace:packages/util-base64": version: 0.0.0-use.local resolution: "@smithy/util-base64@workspace:packages/util-base64" dependencies: @@ -2797,7 +2883,7 @@ __metadata: languageName: unknown linkType: soft -"@smithy/util-body-length-browser@workspace:packages/util-body-length-browser": +"@smithy/util-body-length-browser@^3.0.0, @smithy/util-body-length-browser@workspace:^, @smithy/util-body-length-browser@workspace:packages/util-body-length-browser": version: 0.0.0-use.local resolution: "@smithy/util-body-length-browser@workspace:packages/util-body-length-browser" dependencies: @@ -2809,7 +2895,7 @@ __metadata: languageName: unknown linkType: soft -"@smithy/util-body-length-node@workspace:packages/util-body-length-node": +"@smithy/util-body-length-node@^3.0.0, @smithy/util-body-length-node@workspace:packages/util-body-length-node": version: 0.0.0-use.local resolution: "@smithy/util-body-length-node@workspace:packages/util-body-length-node" dependencies: @@ -2859,7 +2945,7 @@ __metadata: languageName: unknown linkType: soft -"@smithy/util-defaults-mode-browser@workspace:packages/util-defaults-mode-browser": +"@smithy/util-defaults-mode-browser@^3.0.14, @smithy/util-defaults-mode-browser@workspace:packages/util-defaults-mode-browser": version: 0.0.0-use.local resolution: "@smithy/util-defaults-mode-browser@workspace:packages/util-defaults-mode-browser" dependencies: @@ -2876,7 +2962,7 @@ __metadata: languageName: unknown linkType: soft -"@smithy/util-defaults-mode-node@workspace:packages/util-defaults-mode-node": +"@smithy/util-defaults-mode-node@^3.0.14, @smithy/util-defaults-mode-node@workspace:packages/util-defaults-mode-node": version: 0.0.0-use.local resolution: "@smithy/util-defaults-mode-node@workspace:packages/util-defaults-mode-node" dependencies: @@ -2922,7 +3008,7 @@ __metadata: languageName: unknown linkType: soft -"@smithy/util-middleware@workspace:^, @smithy/util-middleware@workspace:packages/util-middleware": +"@smithy/util-middleware@^3.0.3, @smithy/util-middleware@workspace:^, @smithy/util-middleware@workspace:packages/util-middleware": version: 0.0.0-use.local resolution: "@smithy/util-middleware@workspace:packages/util-middleware" dependencies: @@ -2936,7 +3022,7 @@ __metadata: languageName: unknown linkType: soft -"@smithy/util-retry@workspace:^, @smithy/util-retry@workspace:packages/util-retry": +"@smithy/util-retry@^3.0.3, @smithy/util-retry@workspace:^, @smithy/util-retry@workspace:packages/util-retry": version: 0.0.0-use.local resolution: "@smithy/util-retry@workspace:packages/util-retry" dependencies: @@ -3032,17 +3118,7 @@ __metadata: languageName: unknown linkType: soft -"@smithy/util-utf8@npm:^2.0.0": - version: 2.3.0 - resolution: "@smithy/util-utf8@npm:2.3.0" - dependencies: - "@smithy/util-buffer-from": ^2.2.0 - tslib: ^2.6.2 - checksum: 00e55d4b4e37d48be0eef3599082402b933c52a1407fed7e8e8ad76d94d81a0b30b8bfaf2047c59d9c3af31e5f20e7a8c959cb7ae270f894255e05a2229964f0 - languageName: node - linkType: hard - -"@smithy/util-utf8@workspace:^, @smithy/util-utf8@workspace:packages/util-utf8": +"@smithy/util-utf8@^3.0.0, @smithy/util-utf8@workspace:^, @smithy/util-utf8@workspace:packages/util-utf8": version: 0.0.0-use.local resolution: "@smithy/util-utf8@workspace:packages/util-utf8" dependencies: @@ -3055,6 +3131,16 @@ __metadata: languageName: unknown linkType: soft +"@smithy/util-utf8@npm:^2.0.0": + version: 2.3.0 + resolution: "@smithy/util-utf8@npm:2.3.0" + dependencies: + "@smithy/util-buffer-from": ^2.2.0 + tslib: ^2.6.2 + checksum: 00e55d4b4e37d48be0eef3599082402b933c52a1407fed7e8e8ad76d94d81a0b30b8bfaf2047c59d9c3af31e5f20e7a8c959cb7ae270f894255e05a2229964f0 + languageName: node + linkType: hard + "@smithy/util-waiter@workspace:packages/util-waiter": version: 0.0.0-use.local resolution: "@smithy/util-waiter@workspace:packages/util-waiter" @@ -3083,6 +3169,13 @@ __metadata: languageName: node linkType: hard +"@tsconfig/node16@npm:16.1.3": + version: 16.1.3 + resolution: "@tsconfig/node16@npm:16.1.3" + checksum: 097f33cb7fe9577cc3c4b7a8d26c7e8b6c45c868d3bda1e2cd120111693b98f88a5138e643e7a6f79d53d94717c3624dc33398804647bf6e0e8782604bbfad53 + languageName: node + linkType: hard + "@tsconfig/recommended@npm:1.0.2": version: 1.0.2 resolution: "@tsconfig/recommended@npm:1.0.2" @@ -4101,6 +4194,13 @@ __metadata: languageName: node linkType: hard +"bignumber.js@npm:^9.0.0": + version: 9.1.2 + resolution: "bignumber.js@npm:9.1.2" + checksum: 582c03af77ec9cb0ebd682a373ee6c66475db94a4325f92299621d544aa4bd45cb45fd60001610e94aef8ae98a0905fa538241d9638d4422d57abbeeac6fadaf + languageName: node + linkType: hard + "binary-extensions@npm:^2.0.0": version: 2.2.0 resolution: "binary-extensions@npm:2.2.0" @@ -7988,6 +8088,15 @@ __metadata: languageName: node linkType: hard +"json-bigint@npm:^1.0.0": + version: 1.0.0 + resolution: "json-bigint@npm:1.0.0" + dependencies: + bignumber.js: ^9.0.0 + checksum: c67bb93ccb3c291e60eb4b62931403e378906aab113ec1c2a8dd0f9a7f065ad6fd9713d627b732abefae2e244ac9ce1721c7a3142b2979532f12b258634ce6f6 + languageName: node + linkType: hard + "json-parse-even-better-errors@npm:^2.3.0, json-parse-even-better-errors@npm:^2.3.1": version: 2.3.1 resolution: "json-parse-even-better-errors@npm:2.3.1"