From 29ed27d3f4148946ac8c35c0f4d1b759b6f168b0 Mon Sep 17 00:00:00 2001 From: George Fu Date: Mon, 3 Apr 2023 11:25:55 -0400 Subject: [PATCH] feat(client-rekognitionstreaming): add rekognition-streaming client and refactor websocket implementations (#4548) feat(client-rekognitionstreaming): apply new codegen changes feat(client-rekognitionstreaming): update codegen and typescript version feat(client-rekognitionstreaming): update tsconfigs in middleware-websocket --- .../client-rekognitionstreaming/.gitignore | 9 + clients/client-rekognitionstreaming/LICENSE | 201 +++ clients/client-rekognitionstreaming/README.md | 222 +++ .../api-extractor.json | 4 + .../client-rekognitionstreaming/package.json | 104 ++ .../src/RekognitionStreaming.ts | 83 + .../src/RekognitionStreamingClient.ts | 315 ++++ .../StartFaceLivenessSessionCommand.ts | 133 ++ .../StartStreamingLivenessSessionCommand.ts | 135 ++ .../src/commands/index.ts | 3 + .../src/endpoint/EndpointParameters.ts | 31 + .../src/endpoint/endpointResolver.ts | 16 + .../src/endpoint/ruleset.ts | 29 + .../client-rekognitionstreaming/src/index.ts | 7 + .../RekognitionStreamingServiceException.ts | 20 + .../src/models/index.ts | 2 + .../src/models/models_0.ts | 595 ++++++++ .../src/protocols/Aws_restJson1.ts | 778 ++++++++++ .../src/runtimeConfig.browser.ts | 53 + .../src/runtimeConfig.native.ts | 22 + .../src/runtimeConfig.shared.ts | 24 + .../src/runtimeConfig.ts | 63 + .../tsconfig.cjs.json | 6 + .../tsconfig.es.json | 8 + .../client-rekognitionstreaming/tsconfig.json | 13 + .../tsconfig.types.json | 10 + .../client-rekognitionstreaming/typedoc.json | 6 + .../client-transcribe-streaming/package.json | 21 +- .../src/TranscribeStreamingClient.ts | 10 +- ...CallAnalyticsStreamTranscriptionCommand.ts | 2 + .../StartMedicalStreamTranscriptionCommand.ts | 2 + .../StartStreamTranscriptionCommand.ts | 2 + .../src/runtimeConfig.browser.ts | 9 +- .../src/runtimeConfig.native.ts | 3 +- .../aws-models/rekognitionstreaming.json | 1329 +++++++++++++++++ .../AddEventStreamHandlingDependency.java | 5 +- .../AddTranscribeStreamingDependency.java | 10 +- .../codegen/AddWebsocketPlugin.java | 124 ++ .../aws/typescript/codegen/AwsDependency.java | 1 + ....codegen.integration.TypeScriptIntegration | 1 + .../src/index.ts | 7 +- ...s => middleware-inject-response-values.ts} | 8 +- .../src/middleware-port.spec.ts | 51 + .../src/middleware-port.ts | 33 + .../src/plugin.ts | 20 +- .../src/websocket-handler.spec.ts | 151 -- packages/middleware-websocket/LICENSE | 201 +++ packages/middleware-websocket/README.md | 12 + packages/middleware-websocket/jest.config.js | 7 + packages/middleware-websocket/package.json | 67 + .../src/EventStreamPayloadHandler.spec.ts | 173 +++ .../src/EventStreamPayloadHandler.ts | 80 + .../src/WebsocketSignatureV4.spec.ts} | 15 +- .../src/WebsocketSignatureV4.ts} | 12 +- .../src/configuration.ts | 25 +- .../eventstream-payload-handler-provider.ts | 10 + .../src/get-event-signing-stream.spec.ts | 95 ++ .../src/get-event-signing-stream.ts | 52 + packages/middleware-websocket/src/index.ts | 4 + .../src/middleware-session-id.spec.ts | 10 + .../src/middleware-session-id.ts | 40 + .../middleware-websocket-endpoint.spec.ts} | 42 +- .../src/middleware-websocket-endpoint.ts} | 34 +- packages/middleware-websocket/src/plugin.ts | 18 + packages/middleware-websocket/src/utils.ts | 3 + .../src/websocket-fetch-handler.spec.ts | 170 +++ .../src/websocket-fetch-handler.ts} | 60 +- .../middleware-websocket/tsconfig.cjs.json | 9 + .../middleware-websocket/tsconfig.es.json | 10 + .../middleware-websocket/tsconfig.types.json | 9 + scripts/generate-clients/copy-to-clients.js | 2 +- yarn.lock | 78 +- 72 files changed, 5619 insertions(+), 300 deletions(-) create mode 100644 clients/client-rekognitionstreaming/.gitignore create mode 100644 clients/client-rekognitionstreaming/LICENSE create mode 100644 clients/client-rekognitionstreaming/README.md create mode 100644 clients/client-rekognitionstreaming/api-extractor.json create mode 100644 clients/client-rekognitionstreaming/package.json create mode 100644 clients/client-rekognitionstreaming/src/RekognitionStreaming.ts create mode 100644 clients/client-rekognitionstreaming/src/RekognitionStreamingClient.ts create mode 100644 clients/client-rekognitionstreaming/src/commands/StartFaceLivenessSessionCommand.ts create mode 100644 clients/client-rekognitionstreaming/src/commands/StartStreamingLivenessSessionCommand.ts create mode 100644 clients/client-rekognitionstreaming/src/commands/index.ts create mode 100644 clients/client-rekognitionstreaming/src/endpoint/EndpointParameters.ts create mode 100644 clients/client-rekognitionstreaming/src/endpoint/endpointResolver.ts create mode 100644 clients/client-rekognitionstreaming/src/endpoint/ruleset.ts create mode 100644 clients/client-rekognitionstreaming/src/index.ts create mode 100644 clients/client-rekognitionstreaming/src/models/RekognitionStreamingServiceException.ts create mode 100644 clients/client-rekognitionstreaming/src/models/index.ts create mode 100644 clients/client-rekognitionstreaming/src/models/models_0.ts create mode 100644 clients/client-rekognitionstreaming/src/protocols/Aws_restJson1.ts create mode 100644 clients/client-rekognitionstreaming/src/runtimeConfig.browser.ts create mode 100644 clients/client-rekognitionstreaming/src/runtimeConfig.native.ts create mode 100644 clients/client-rekognitionstreaming/src/runtimeConfig.shared.ts create mode 100644 clients/client-rekognitionstreaming/src/runtimeConfig.ts create mode 100644 clients/client-rekognitionstreaming/tsconfig.cjs.json create mode 100644 clients/client-rekognitionstreaming/tsconfig.es.json create mode 100644 clients/client-rekognitionstreaming/tsconfig.json create mode 100644 clients/client-rekognitionstreaming/tsconfig.types.json create mode 100644 clients/client-rekognitionstreaming/typedoc.json create mode 100644 codegen/sdk-codegen/aws-models/rekognitionstreaming.json create mode 100644 codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AddWebsocketPlugin.java rename packages/middleware-sdk-transcribe-streaming/src/{middleware-session-id.ts => middleware-inject-response-values.ts} (84%) create mode 100644 packages/middleware-sdk-transcribe-streaming/src/middleware-port.spec.ts create mode 100644 packages/middleware-sdk-transcribe-streaming/src/middleware-port.ts delete mode 100644 packages/middleware-sdk-transcribe-streaming/src/websocket-handler.spec.ts create mode 100644 packages/middleware-websocket/LICENSE create mode 100644 packages/middleware-websocket/README.md create mode 100644 packages/middleware-websocket/jest.config.js create mode 100644 packages/middleware-websocket/package.json create mode 100644 packages/middleware-websocket/src/EventStreamPayloadHandler.spec.ts create mode 100644 packages/middleware-websocket/src/EventStreamPayloadHandler.ts rename packages/{middleware-sdk-transcribe-streaming/src/signer.spec.ts => middleware-websocket/src/WebsocketSignatureV4.spec.ts} (89%) rename packages/{middleware-sdk-transcribe-streaming/src/signer.ts => middleware-websocket/src/WebsocketSignatureV4.ts} (80%) rename packages/{middleware-sdk-transcribe-streaming => middleware-websocket}/src/configuration.ts (61%) create mode 100644 packages/middleware-websocket/src/eventstream-payload-handler-provider.ts create mode 100644 packages/middleware-websocket/src/get-event-signing-stream.spec.ts create mode 100644 packages/middleware-websocket/src/get-event-signing-stream.ts create mode 100644 packages/middleware-websocket/src/index.ts create mode 100644 packages/middleware-websocket/src/middleware-session-id.spec.ts create mode 100644 packages/middleware-websocket/src/middleware-session-id.ts rename packages/{middleware-sdk-transcribe-streaming/src/middleware-endpoint.spec.ts => middleware-websocket/src/middleware-websocket-endpoint.spec.ts} (69%) rename packages/{middleware-sdk-transcribe-streaming/src/middleware-endpoint.ts => middleware-websocket/src/middleware-websocket-endpoint.ts} (59%) create mode 100644 packages/middleware-websocket/src/plugin.ts create mode 100644 packages/middleware-websocket/src/utils.ts create mode 100644 packages/middleware-websocket/src/websocket-fetch-handler.spec.ts rename packages/{middleware-sdk-transcribe-streaming/src/websocket-handler.ts => middleware-websocket/src/websocket-fetch-handler.ts} (80%) create mode 100644 packages/middleware-websocket/tsconfig.cjs.json create mode 100644 packages/middleware-websocket/tsconfig.es.json create mode 100644 packages/middleware-websocket/tsconfig.types.json diff --git a/clients/client-rekognitionstreaming/.gitignore b/clients/client-rekognitionstreaming/.gitignore new file mode 100644 index 000000000000..54f14c9aef25 --- /dev/null +++ b/clients/client-rekognitionstreaming/.gitignore @@ -0,0 +1,9 @@ +/node_modules/ +/build/ +/coverage/ +/docs/ +/dist-* +*.tsbuildinfo +*.tgz +*.log +package-lock.json diff --git a/clients/client-rekognitionstreaming/LICENSE b/clients/client-rekognitionstreaming/LICENSE new file mode 100644 index 000000000000..8efcd8d5c5b7 --- /dev/null +++ b/clients/client-rekognitionstreaming/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2018-2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/clients/client-rekognitionstreaming/README.md b/clients/client-rekognitionstreaming/README.md new file mode 100644 index 000000000000..0d55a1fe8eef --- /dev/null +++ b/clients/client-rekognitionstreaming/README.md @@ -0,0 +1,222 @@ + + +# @aws-sdk/client-rekognitionstreaming + +[![NPM version](https://img.shields.io/npm/v/@aws-sdk/client-rekognitionstreaming/latest.svg)](https://www.npmjs.com/package/@aws-sdk/client-rekognitionstreaming) +[![NPM downloads](https://img.shields.io/npm/dm/@aws-sdk/client-rekognitionstreaming.svg)](https://www.npmjs.com/package/@aws-sdk/client-rekognitionstreaming) + +## Description + +AWS SDK for JavaScript RekognitionStreaming Client for Node.js, Browser and React Native. + +## Installing + +To install the this package, simply type add or install @aws-sdk/client-rekognitionstreaming +using your favorite package manager: + +- `npm install @aws-sdk/client-rekognitionstreaming` +- `yarn add @aws-sdk/client-rekognitionstreaming` +- `pnpm add @aws-sdk/client-rekognitionstreaming` + +## Getting Started + +### Import + +The AWS SDK is modulized by clients and commands. +To send a request, you only need to import the `RekognitionStreamingClient` and +the commands you need, for example `StartFaceLivenessSessionCommand`: + +```js +// ES5 example +const { RekognitionStreamingClient, StartFaceLivenessSessionCommand } = require("@aws-sdk/client-rekognitionstreaming"); +``` + +```ts +// ES6+ example +import { RekognitionStreamingClient, StartFaceLivenessSessionCommand } from "@aws-sdk/client-rekognitionstreaming"; +``` + +### Usage + +To send a request, you: + +- Initiate client with configuration (e.g. credentials, region). +- Initiate command with input parameters. +- Call `send` operation on client with command object as input. +- If you are using a custom http handler, you may call `destroy()` to close open connections. + +```js +// a client can be shared by different commands. +const client = new RekognitionStreamingClient({ region: "REGION" }); + +const params = { + /** input parameters */ +}; +const command = new StartFaceLivenessSessionCommand(params); +``` + +#### Async/await + +We recommend using [await](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await) +operator to wait for the promise returned by send operation as follows: + +```js +// async/await. +try { + const data = await client.send(command); + // process data. +} catch (error) { + // error handling. +} finally { + // finally. +} +``` + +Async-await is clean, concise, intuitive, easy to debug and has better error handling +as compared to using Promise chains or callbacks. + +#### Promises + +You can also use [Promise chaining](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises#chaining) +to execute send operation. + +```js +client.send(command).then( + (data) => { + // process data. + }, + (error) => { + // error handling. + } +); +``` + +Promises can also be called using `.catch()` and `.finally()` as follows: + +```js +client + .send(command) + .then((data) => { + // process data. + }) + .catch((error) => { + // error handling. + }) + .finally(() => { + // finally. + }); +``` + +#### Callbacks + +We do not recommend using callbacks because of [callback hell](http://callbackhell.com/), +but they are supported by the send operation. + +```js +// callbacks. +client.send(command, (err, data) => { + // process err and data. +}); +``` + +#### v2 compatible style + +The client can also send requests using v2 compatible style. +However, it results in a bigger bundle size and may be dropped in next major version. More details in the blog post +on [modular packages in AWS SDK for JavaScript](https://aws.amazon.com/blogs/developer/modular-packages-in-aws-sdk-for-javascript/) + +```ts +import * as AWS from "@aws-sdk/client-rekognitionstreaming"; +const client = new AWS.RekognitionStreaming({ region: "REGION" }); + +// async/await. +try { + const data = await client.startFaceLivenessSession(params); + // process data. +} catch (error) { + // error handling. +} + +// Promises. +client + .startFaceLivenessSession(params) + .then((data) => { + // process data. + }) + .catch((error) => { + // error handling. + }); + +// callbacks. +client.startFaceLivenessSession(params, (err, data) => { + // process err and data. +}); +``` + +### Troubleshooting + +When the service returns an exception, the error will include the exception information, +as well as response metadata (e.g. request id). + +```js +try { + const data = await client.send(command); + // process data. +} catch (error) { + const { requestId, cfId, extendedRequestId } = error.$$metadata; + console.log({ requestId, cfId, extendedRequestId }); + /** + * The keys within exceptions are also parsed. + * You can access them by specifying exception names: + * if (error.name === 'SomeServiceException') { + * const value = error.specialKeyInException; + * } + */ +} +``` + +## Getting Help + +Please use these community resources for getting help. +We use the GitHub issues for tracking bugs and feature requests, but have limited bandwidth to address them. + +- Visit [Developer Guide](https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/welcome.html) + or [API Reference](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/index.html). +- Check out the blog posts tagged with [`aws-sdk-js`](https://aws.amazon.com/blogs/developer/tag/aws-sdk-js/) + on AWS Developer Blog. +- Ask a question on [StackOverflow](https://stackoverflow.com/questions/tagged/aws-sdk-js) and tag it with `aws-sdk-js`. +- Join the AWS JavaScript community on [gitter](https://gitter.im/aws/aws-sdk-js-v3). +- If it turns out that you may have found a bug, please [open an issue](https://github.com/aws/aws-sdk-js-v3/issues/new/choose). + +To test your universal JavaScript code in Node.js, browser and react-native environments, +visit our [code samples repo](https://github.com/aws-samples/aws-sdk-js-tests). + +## Contributing + +This client code is generated automatically. Any modifications will be overwritten the next time the `@aws-sdk/client-rekognitionstreaming` package is updated. +To contribute to client you can check our [generate clients scripts](https://github.com/aws/aws-sdk-js-v3/tree/main/scripts/generate-clients). + +## License + +This SDK is distributed under the +[Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0), +see LICENSE for more information. + +## Client Commands (Operations List) + +
+ +StartFaceLivenessSession + + +[Command API Reference](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-rekognitionstreaming/classes/startfacelivenesssessioncommand.html) / [Input](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-rekognitionstreaming/interfaces/startfacelivenesssessioncommandinput.html) / [Output](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-rekognitionstreaming/interfaces/startfacelivenesssessioncommandoutput.html) + +
+
+ +StartStreamingLivenessSession + + +[Command API Reference](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-rekognitionstreaming/classes/startstreaminglivenesssessioncommand.html) / [Input](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-rekognitionstreaming/interfaces/startstreaminglivenesssessioncommandinput.html) / [Output](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-rekognitionstreaming/interfaces/startstreaminglivenesssessioncommandoutput.html) + +
diff --git a/clients/client-rekognitionstreaming/api-extractor.json b/clients/client-rekognitionstreaming/api-extractor.json new file mode 100644 index 000000000000..d5bf5ffeee85 --- /dev/null +++ b/clients/client-rekognitionstreaming/api-extractor.json @@ -0,0 +1,4 @@ +{ + "extends": "../../api-extractor.json", + "mainEntryPointFilePath": "/dist-types/index.d.ts" +} diff --git a/clients/client-rekognitionstreaming/package.json b/clients/client-rekognitionstreaming/package.json new file mode 100644 index 000000000000..4e8fe96f9074 --- /dev/null +++ b/clients/client-rekognitionstreaming/package.json @@ -0,0 +1,104 @@ +{ + "name": "@aws-sdk/client-rekognitionstreaming", + "description": "AWS SDK for JavaScript Rekognitionstreaming Client for Node.js, Browser and React Native", + "version": "3.0.0", + "scripts": { + "build": "concurrently 'yarn:build:cjs' 'yarn:build:es' 'yarn:build:types'", + "build:cjs": "tsc -p tsconfig.cjs.json", + "build:docs": "typedoc", + "build:es": "tsc -p tsconfig.es.json", + "build:include:deps": "lerna run --scope $npm_package_name --include-dependencies build", + "build:types": "tsc -p tsconfig.types.json", + "build:types:downlevel": "downlevel-dts dist-types dist-types/ts3.4", + "clean": "rimraf ./dist-* && rimraf *.tsbuildinfo", + "extract:docs": "api-extractor run --local", + "generate:client": "node ../../scripts/generate-clients/single-service --solo rekognitionstreaming" + }, + "main": "./dist-cjs/index.js", + "types": "./dist-types/index.d.ts", + "module": "./dist-es/index.js", + "sideEffects": false, + "dependencies": { + "@aws-crypto/sha256-browser": "2.0.0", + "@aws-crypto/sha256-js": "2.0.0", + "@aws-sdk/client-sts": "*", + "@aws-sdk/config-resolver": "*", + "@aws-sdk/credential-provider-node": "*", + "@aws-sdk/eventstream-handler-node": "*", + "@aws-sdk/eventstream-serde-browser": "*", + "@aws-sdk/eventstream-serde-config-resolver": "*", + "@aws-sdk/eventstream-serde-node": "*", + "@aws-sdk/fetch-http-handler": "*", + "@aws-sdk/hash-node": "*", + "@aws-sdk/invalid-dependency": "*", + "@aws-sdk/middleware-content-length": "*", + "@aws-sdk/middleware-endpoint": "*", + "@aws-sdk/middleware-eventstream": "*", + "@aws-sdk/middleware-host-header": "*", + "@aws-sdk/middleware-logger": "*", + "@aws-sdk/middleware-recursion-detection": "*", + "@aws-sdk/middleware-retry": "*", + "@aws-sdk/middleware-serde": "*", + "@aws-sdk/middleware-signing": "*", + "@aws-sdk/middleware-stack": "*", + "@aws-sdk/middleware-user-agent": "*", + "@aws-sdk/middleware-websocket": "*", + "@aws-sdk/node-config-provider": "*", + "@aws-sdk/node-http-handler": "*", + "@aws-sdk/protocol-http": "*", + "@aws-sdk/smithy-client": "*", + "@aws-sdk/types": "*", + "@aws-sdk/url-parser": "*", + "@aws-sdk/util-base64": "*", + "@aws-sdk/util-body-length-browser": "*", + "@aws-sdk/util-body-length-node": "*", + "@aws-sdk/util-defaults-mode-browser": "*", + "@aws-sdk/util-defaults-mode-node": "*", + "@aws-sdk/util-endpoints": "*", + "@aws-sdk/util-retry": "*", + "@aws-sdk/util-user-agent-browser": "*", + "@aws-sdk/util-user-agent-node": "*", + "@aws-sdk/util-utf8": "*", + "tslib": "^2.5.0" + }, + "devDependencies": { + "@aws-sdk/service-client-documentation-generator": "*", + "@tsconfig/node14": "1.0.3", + "@types/node": "^14.14.31", + "concurrently": "7.0.0", + "downlevel-dts": "0.10.1", + "rimraf": "3.0.2", + "typedoc": "0.23.23", + "typescript": "~4.9.5" + }, + "engines": { + "node": ">=14.0.0" + }, + "typesVersions": { + "<4.0": { + "dist-types/*": [ + "dist-types/ts3.4/*" + ] + } + }, + "files": [ + "dist-*" + ], + "author": { + "name": "AWS SDK for JavaScript Team", + "url": "https://aws.amazon.com/javascript/" + }, + "license": "Apache-2.0", + "browser": { + "./dist-es/runtimeConfig": "./dist-es/runtimeConfig.browser" + }, + "react-native": { + "./dist-es/runtimeConfig": "./dist-es/runtimeConfig.native" + }, + "homepage": "https://github.com/aws/aws-sdk-js-v3/tree/main/clients/client-rekognitionstreaming", + "repository": { + "type": "git", + "url": "https://github.com/aws/aws-sdk-js-v3.git", + "directory": "clients/client-rekognitionstreaming" + } +} diff --git a/clients/client-rekognitionstreaming/src/RekognitionStreaming.ts b/clients/client-rekognitionstreaming/src/RekognitionStreaming.ts new file mode 100644 index 000000000000..9cc2f395bc75 --- /dev/null +++ b/clients/client-rekognitionstreaming/src/RekognitionStreaming.ts @@ -0,0 +1,83 @@ +// smithy-typescript generated code +import { HttpHandlerOptions as __HttpHandlerOptions } from "@aws-sdk/types"; + +import { + StartFaceLivenessSessionCommand, + StartFaceLivenessSessionCommandInput, + StartFaceLivenessSessionCommandOutput, +} from "./commands/StartFaceLivenessSessionCommand"; +import { + StartStreamingLivenessSessionCommand, + StartStreamingLivenessSessionCommandInput, + StartStreamingLivenessSessionCommandOutput, +} from "./commands/StartStreamingLivenessSessionCommand"; +import { RekognitionStreamingClient } from "./RekognitionStreamingClient"; + +/** + * @public + */ +export class RekognitionStreaming extends RekognitionStreamingClient { + /** + * @public + */ + public startFaceLivenessSession( + args: StartFaceLivenessSessionCommandInput, + options?: __HttpHandlerOptions + ): Promise; + public startFaceLivenessSession( + args: StartFaceLivenessSessionCommandInput, + cb: (err: any, data?: StartFaceLivenessSessionCommandOutput) => void + ): void; + public startFaceLivenessSession( + args: StartFaceLivenessSessionCommandInput, + options: __HttpHandlerOptions, + cb: (err: any, data?: StartFaceLivenessSessionCommandOutput) => void + ): void; + public startFaceLivenessSession( + args: StartFaceLivenessSessionCommandInput, + optionsOrCb?: __HttpHandlerOptions | ((err: any, data?: StartFaceLivenessSessionCommandOutput) => void), + cb?: (err: any, data?: StartFaceLivenessSessionCommandOutput) => void + ): Promise | void { + const command = new StartFaceLivenessSessionCommand(args); + if (typeof optionsOrCb === "function") { + this.send(command, optionsOrCb); + } else if (typeof cb === "function") { + if (typeof optionsOrCb !== "object") throw new Error(`Expect http options but get ${typeof optionsOrCb}`); + this.send(command, optionsOrCb || {}, cb); + } else { + return this.send(command, optionsOrCb); + } + } + + /** + * @public + */ + public startStreamingLivenessSession( + args: StartStreamingLivenessSessionCommandInput, + options?: __HttpHandlerOptions + ): Promise; + public startStreamingLivenessSession( + args: StartStreamingLivenessSessionCommandInput, + cb: (err: any, data?: StartStreamingLivenessSessionCommandOutput) => void + ): void; + public startStreamingLivenessSession( + args: StartStreamingLivenessSessionCommandInput, + options: __HttpHandlerOptions, + cb: (err: any, data?: StartStreamingLivenessSessionCommandOutput) => void + ): void; + public startStreamingLivenessSession( + args: StartStreamingLivenessSessionCommandInput, + optionsOrCb?: __HttpHandlerOptions | ((err: any, data?: StartStreamingLivenessSessionCommandOutput) => void), + cb?: (err: any, data?: StartStreamingLivenessSessionCommandOutput) => void + ): Promise | void { + const command = new StartStreamingLivenessSessionCommand(args); + if (typeof optionsOrCb === "function") { + this.send(command, optionsOrCb); + } else if (typeof cb === "function") { + if (typeof optionsOrCb !== "object") throw new Error(`Expect http options but get ${typeof optionsOrCb}`); + this.send(command, optionsOrCb || {}, cb); + } else { + return this.send(command, optionsOrCb); + } + } +} diff --git a/clients/client-rekognitionstreaming/src/RekognitionStreamingClient.ts b/clients/client-rekognitionstreaming/src/RekognitionStreamingClient.ts new file mode 100644 index 000000000000..efddbf894912 --- /dev/null +++ b/clients/client-rekognitionstreaming/src/RekognitionStreamingClient.ts @@ -0,0 +1,315 @@ +// smithy-typescript generated code +import { RegionInputConfig, RegionResolvedConfig, resolveRegionConfig } from "@aws-sdk/config-resolver"; +import { + EventStreamSerdeInputConfig, + EventStreamSerdeResolvedConfig, + resolveEventStreamSerdeConfig, +} from "@aws-sdk/eventstream-serde-config-resolver"; +import { getContentLengthPlugin } from "@aws-sdk/middleware-content-length"; +import { EndpointInputConfig, EndpointResolvedConfig, resolveEndpointConfig } from "@aws-sdk/middleware-endpoint"; +import { + EventStreamInputConfig, + EventStreamResolvedConfig, + resolveEventStreamConfig, +} from "@aws-sdk/middleware-eventstream"; +import { + getHostHeaderPlugin, + HostHeaderInputConfig, + HostHeaderResolvedConfig, + resolveHostHeaderConfig, +} from "@aws-sdk/middleware-host-header"; +import { getLoggerPlugin } from "@aws-sdk/middleware-logger"; +import { getRecursionDetectionPlugin } from "@aws-sdk/middleware-recursion-detection"; +import { getRetryPlugin, resolveRetryConfig, RetryInputConfig, RetryResolvedConfig } from "@aws-sdk/middleware-retry"; +import { + AwsAuthInputConfig, + AwsAuthResolvedConfig, + getAwsAuthPlugin, + resolveAwsAuthConfig, +} from "@aws-sdk/middleware-signing"; +import { + getUserAgentPlugin, + resolveUserAgentConfig, + UserAgentInputConfig, + UserAgentResolvedConfig, +} from "@aws-sdk/middleware-user-agent"; +import { resolveWebSocketConfig, WebSocketInputConfig, WebSocketResolvedConfig } from "@aws-sdk/middleware-websocket"; +import { HttpHandler as __HttpHandler } from "@aws-sdk/protocol-http"; +import { + Client as __Client, + DefaultsMode as __DefaultsMode, + SmithyConfiguration as __SmithyConfiguration, + SmithyResolvedConfiguration as __SmithyResolvedConfiguration, +} from "@aws-sdk/smithy-client"; +import { + BodyLengthCalculator as __BodyLengthCalculator, + Checksum as __Checksum, + ChecksumConstructor as __ChecksumConstructor, + Credentials as __Credentials, + Decoder as __Decoder, + Encoder as __Encoder, + EndpointV2 as __EndpointV2, + EventStreamPayloadHandlerProvider as __EventStreamPayloadHandlerProvider, + EventStreamSerdeProvider as __EventStreamSerdeProvider, + Hash as __Hash, + HashConstructor as __HashConstructor, + HttpHandlerOptions as __HttpHandlerOptions, + Logger as __Logger, + Provider as __Provider, + Provider, + StreamCollector as __StreamCollector, + UrlParser as __UrlParser, + UserAgent as __UserAgent, +} from "@aws-sdk/types"; + +import { + StartFaceLivenessSessionCommandInput, + StartFaceLivenessSessionCommandOutput, +} from "./commands/StartFaceLivenessSessionCommand"; +import { + StartStreamingLivenessSessionCommandInput, + StartStreamingLivenessSessionCommandOutput, +} from "./commands/StartStreamingLivenessSessionCommand"; +import { + ClientInputEndpointParameters, + ClientResolvedEndpointParameters, + EndpointParameters, + resolveClientEndpointParameters, +} from "./endpoint/EndpointParameters"; +import { getRuntimeConfig as __getRuntimeConfig } from "./runtimeConfig"; + +/** + * @public + */ +export type ServiceInputTypes = StartFaceLivenessSessionCommandInput | StartStreamingLivenessSessionCommandInput; + +/** + * @public + */ +export type ServiceOutputTypes = StartFaceLivenessSessionCommandOutput | StartStreamingLivenessSessionCommandOutput; + +/** + * @public + */ +export interface ClientDefaults extends Partial<__SmithyResolvedConfiguration<__HttpHandlerOptions>> { + /** + * The HTTP handler to use. Fetch in browser and Https in Nodejs. + */ + requestHandler?: __HttpHandler; + + /** + * A constructor for a class implementing the {@link @aws-sdk/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 dyanamically changing the endpoint of the client based on the hostPrefix + * trait of an operation. + */ + disableHostPrefix?: boolean; + + /** + * Unique service identifier. + * @internal + */ + serviceId?: string; + + /** + * Enables IPv6/IPv4 dualstack endpoint. + */ + useDualstackEndpoint?: boolean | __Provider; + + /** + * Enables FIPS compatible endpoints. + */ + useFipsEndpoint?: boolean | __Provider; + + /** + * The AWS region to which this client will send requests + */ + region?: string | __Provider; + + /** + * Default credentials provider; Not available in browser runtime. + * @internal + */ + credentialDefaultProvider?: (input: any) => __Provider<__Credentials>; + + /** + * The function that provides necessary utilities for handling request event stream. + * @internal + */ + eventStreamPayloadHandlerProvider?: __EventStreamPayloadHandlerProvider; + + /** + * The provider populating default tracking information to be sent with `user-agent`, `x-amz-user-agent` header + * @internal + */ + defaultUserAgentProvider?: Provider<__UserAgent>; + + /** + * 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. + */ + retryMode?: string | __Provider; + + /** + * Optional logger for logging debug/info/warn/error. + */ + logger?: __Logger; + + /** + * The function that provides necessary utilities for generating and parsing event stream + */ + eventStreamSerdeProvider?: __EventStreamSerdeProvider; + + /** + * The {@link @aws-sdk/smithy-client#DefaultsMode} that will be used to determine how certain default configuration options are resolved in the SDK. + */ + defaultsMode?: __DefaultsMode | __Provider<__DefaultsMode>; +} + +/** + * @public + */ +type RekognitionStreamingClientConfigType = Partial<__SmithyConfiguration<__HttpHandlerOptions>> & + ClientDefaults & + RegionInputConfig & + EndpointInputConfig & + RetryInputConfig & + HostHeaderInputConfig & + AwsAuthInputConfig & + EventStreamInputConfig & + WebSocketInputConfig & + UserAgentInputConfig & + EventStreamSerdeInputConfig & + ClientInputEndpointParameters; +/** + * @public + * + * The configuration interface of RekognitionStreamingClient class constructor that set the region, credentials and other options. + */ +export interface RekognitionStreamingClientConfig extends RekognitionStreamingClientConfigType {} + +/** + * @public + */ +type RekognitionStreamingClientResolvedConfigType = __SmithyResolvedConfiguration<__HttpHandlerOptions> & + Required & + RegionResolvedConfig & + EndpointResolvedConfig & + RetryResolvedConfig & + HostHeaderResolvedConfig & + AwsAuthResolvedConfig & + EventStreamResolvedConfig & + WebSocketResolvedConfig & + UserAgentResolvedConfig & + EventStreamSerdeResolvedConfig & + ClientResolvedEndpointParameters; +/** + * @public + * + * The resolved configuration interface of RekognitionStreamingClient class. This is resolved and normalized from the {@link RekognitionStreamingClientConfig | constructor configuration interface}. + */ +export interface RekognitionStreamingClientResolvedConfig extends RekognitionStreamingClientResolvedConfigType {} + +/** + * @public + */ +export class RekognitionStreamingClient extends __Client< + __HttpHandlerOptions, + ServiceInputTypes, + ServiceOutputTypes, + RekognitionStreamingClientResolvedConfig +> { + /** + * The resolved configuration of RekognitionStreamingClient class. This is resolved and normalized from the {@link RekognitionStreamingClientConfig | constructor configuration interface}. + */ + readonly config: RekognitionStreamingClientResolvedConfig; + + constructor(configuration: RekognitionStreamingClientConfig) { + const _config_0 = __getRuntimeConfig(configuration); + const _config_1 = resolveClientEndpointParameters(_config_0); + const _config_2 = resolveRegionConfig(_config_1); + const _config_3 = resolveEndpointConfig(_config_2); + const _config_4 = resolveRetryConfig(_config_3); + const _config_5 = resolveHostHeaderConfig(_config_4); + const _config_6 = resolveAwsAuthConfig(_config_5); + const _config_7 = resolveEventStreamConfig(_config_6); + const _config_8 = resolveWebSocketConfig(_config_7); + const _config_9 = resolveUserAgentConfig(_config_8); + const _config_10 = resolveEventStreamSerdeConfig(_config_9); + super(_config_10); + this.config = _config_10; + this.middlewareStack.use(getRetryPlugin(this.config)); + this.middlewareStack.use(getContentLengthPlugin(this.config)); + this.middlewareStack.use(getHostHeaderPlugin(this.config)); + this.middlewareStack.use(getLoggerPlugin(this.config)); + this.middlewareStack.use(getRecursionDetectionPlugin(this.config)); + this.middlewareStack.use(getAwsAuthPlugin(this.config)); + this.middlewareStack.use(getUserAgentPlugin(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/clients/client-rekognitionstreaming/src/commands/StartFaceLivenessSessionCommand.ts b/clients/client-rekognitionstreaming/src/commands/StartFaceLivenessSessionCommand.ts new file mode 100644 index 000000000000..c4c0f51b9573 --- /dev/null +++ b/clients/client-rekognitionstreaming/src/commands/StartFaceLivenessSessionCommand.ts @@ -0,0 +1,133 @@ +// smithy-typescript generated code +import { EndpointParameterInstructions, getEndpointPlugin } from "@aws-sdk/middleware-endpoint"; +import { getEventStreamPlugin } from "@aws-sdk/middleware-eventstream"; +import { getSerdePlugin } from "@aws-sdk/middleware-serde"; +import { getWebSocketPlugin } from "@aws-sdk/middleware-websocket"; +import { HttpRequest as __HttpRequest, HttpResponse as __HttpResponse } from "@aws-sdk/protocol-http"; +import { Command as $Command } from "@aws-sdk/smithy-client"; +import { + EventStreamSerdeContext as __EventStreamSerdeContext, + FinalizeHandlerArguments, + Handler, + HandlerExecutionContext, + HttpHandlerOptions as __HttpHandlerOptions, + MetadataBearer as __MetadataBearer, + MiddlewareStack, + SerdeContext as __SerdeContext, +} from "@aws-sdk/types"; + +import { + StartFaceLivenessSessionRequest, + StartFaceLivenessSessionRequestFilterSensitiveLog, + StartFaceLivenessSessionResponse, + StartFaceLivenessSessionResponseFilterSensitiveLog, +} from "../models/models_0"; +import { + deserializeAws_restJson1StartFaceLivenessSessionCommand, + serializeAws_restJson1StartFaceLivenessSessionCommand, +} from "../protocols/Aws_restJson1"; +import { + RekognitionStreamingClientResolvedConfig, + ServiceInputTypes, + ServiceOutputTypes, +} from "../RekognitionStreamingClient"; + +/** + * @public + * + * The input for {@link StartFaceLivenessSessionCommand}. + */ +export interface StartFaceLivenessSessionCommandInput extends StartFaceLivenessSessionRequest {} +/** + * @public + * + * The output of {@link StartFaceLivenessSessionCommand}. + */ +export interface StartFaceLivenessSessionCommandOutput extends StartFaceLivenessSessionResponse, __MetadataBearer {} + +export class StartFaceLivenessSessionCommand extends $Command< + StartFaceLivenessSessionCommandInput, + StartFaceLivenessSessionCommandOutput, + RekognitionStreamingClientResolvedConfig +> { + // Start section: command_properties + // End section: command_properties + + public static getEndpointParameterInstructions(): EndpointParameterInstructions { + return { + UseFIPS: { type: "builtInParams", name: "useFipsEndpoint" }, + Endpoint: { type: "builtInParams", name: "endpoint" }, + Region: { type: "builtInParams", name: "region" }, + UseDualStack: { type: "builtInParams", name: "useDualstackEndpoint" }, + }; + } + + /** + * @public + */ + constructor(readonly input: StartFaceLivenessSessionCommandInput) { + // Start section: command_constructor + super(); + // End section: command_constructor + } + + /** + * @internal + */ + resolveMiddleware( + clientStack: MiddlewareStack, + configuration: RekognitionStreamingClientResolvedConfig, + options?: __HttpHandlerOptions + ): Handler { + this.middlewareStack.use(getSerdePlugin(configuration, this.serialize, this.deserialize)); + this.middlewareStack.use( + getEndpointPlugin(configuration, StartFaceLivenessSessionCommand.getEndpointParameterInstructions()) + ); + this.middlewareStack.use(getEventStreamPlugin(configuration)); + this.middlewareStack.use( + getWebSocketPlugin(configuration, { headerPrefix: "x-amz-rekognition-streaming-liveness-" }) + ); + + const stack = clientStack.concat(this.middlewareStack); + + const { logger } = configuration; + const clientName = "RekognitionStreamingClient"; + const commandName = "StartFaceLivenessSessionCommand"; + const handlerExecutionContext: HandlerExecutionContext = { + logger, + clientName, + commandName, + inputFilterSensitiveLog: StartFaceLivenessSessionRequestFilterSensitiveLog, + outputFilterSensitiveLog: StartFaceLivenessSessionResponseFilterSensitiveLog, + }; + const { requestHandler } = configuration; + return stack.resolve( + (request: FinalizeHandlerArguments) => + requestHandler.handle(request.request as __HttpRequest, options || {}), + handlerExecutionContext + ); + } + + /** + * @internal + */ + private serialize( + input: StartFaceLivenessSessionCommandInput, + context: __SerdeContext & __EventStreamSerdeContext + ): Promise<__HttpRequest> { + return serializeAws_restJson1StartFaceLivenessSessionCommand(input, context); + } + + /** + * @internal + */ + private deserialize( + output: __HttpResponse, + context: __SerdeContext & __EventStreamSerdeContext + ): Promise { + return deserializeAws_restJson1StartFaceLivenessSessionCommand(output, context); + } + + // Start section: command_body_extra + // End section: command_body_extra +} diff --git a/clients/client-rekognitionstreaming/src/commands/StartStreamingLivenessSessionCommand.ts b/clients/client-rekognitionstreaming/src/commands/StartStreamingLivenessSessionCommand.ts new file mode 100644 index 000000000000..5a18af23917b --- /dev/null +++ b/clients/client-rekognitionstreaming/src/commands/StartStreamingLivenessSessionCommand.ts @@ -0,0 +1,135 @@ +// smithy-typescript generated code +import { EndpointParameterInstructions, getEndpointPlugin } from "@aws-sdk/middleware-endpoint"; +import { getEventStreamPlugin } from "@aws-sdk/middleware-eventstream"; +import { getSerdePlugin } from "@aws-sdk/middleware-serde"; +import { getWebSocketPlugin } from "@aws-sdk/middleware-websocket"; +import { HttpRequest as __HttpRequest, HttpResponse as __HttpResponse } from "@aws-sdk/protocol-http"; +import { Command as $Command } from "@aws-sdk/smithy-client"; +import { + EventStreamSerdeContext as __EventStreamSerdeContext, + FinalizeHandlerArguments, + Handler, + HandlerExecutionContext, + HttpHandlerOptions as __HttpHandlerOptions, + MetadataBearer as __MetadataBearer, + MiddlewareStack, + SerdeContext as __SerdeContext, +} from "@aws-sdk/types"; + +import { + StartStreamingLivenessSessionRequest, + StartStreamingLivenessSessionRequestFilterSensitiveLog, + StartStreamingLivenessSessionResponse, + StartStreamingLivenessSessionResponseFilterSensitiveLog, +} from "../models/models_0"; +import { + deserializeAws_restJson1StartStreamingLivenessSessionCommand, + serializeAws_restJson1StartStreamingLivenessSessionCommand, +} from "../protocols/Aws_restJson1"; +import { + RekognitionStreamingClientResolvedConfig, + ServiceInputTypes, + ServiceOutputTypes, +} from "../RekognitionStreamingClient"; + +/** + * @public + * + * The input for {@link StartStreamingLivenessSessionCommand}. + */ +export interface StartStreamingLivenessSessionCommandInput extends StartStreamingLivenessSessionRequest {} +/** + * @public + * + * The output of {@link StartStreamingLivenessSessionCommand}. + */ +export interface StartStreamingLivenessSessionCommandOutput + extends StartStreamingLivenessSessionResponse, + __MetadataBearer {} + +export class StartStreamingLivenessSessionCommand extends $Command< + StartStreamingLivenessSessionCommandInput, + StartStreamingLivenessSessionCommandOutput, + RekognitionStreamingClientResolvedConfig +> { + // Start section: command_properties + // End section: command_properties + + public static getEndpointParameterInstructions(): EndpointParameterInstructions { + return { + UseFIPS: { type: "builtInParams", name: "useFipsEndpoint" }, + Endpoint: { type: "builtInParams", name: "endpoint" }, + Region: { type: "builtInParams", name: "region" }, + UseDualStack: { type: "builtInParams", name: "useDualstackEndpoint" }, + }; + } + + /** + * @public + */ + constructor(readonly input: StartStreamingLivenessSessionCommandInput) { + // Start section: command_constructor + super(); + // End section: command_constructor + } + + /** + * @internal + */ + resolveMiddleware( + clientStack: MiddlewareStack, + configuration: RekognitionStreamingClientResolvedConfig, + options?: __HttpHandlerOptions + ): Handler { + this.middlewareStack.use(getSerdePlugin(configuration, this.serialize, this.deserialize)); + this.middlewareStack.use( + getEndpointPlugin(configuration, StartStreamingLivenessSessionCommand.getEndpointParameterInstructions()) + ); + this.middlewareStack.use(getEventStreamPlugin(configuration)); + this.middlewareStack.use( + getWebSocketPlugin(configuration, { headerPrefix: "x-amz-rekognition-streaming-liveness-" }) + ); + + const stack = clientStack.concat(this.middlewareStack); + + const { logger } = configuration; + const clientName = "RekognitionStreamingClient"; + const commandName = "StartStreamingLivenessSessionCommand"; + const handlerExecutionContext: HandlerExecutionContext = { + logger, + clientName, + commandName, + inputFilterSensitiveLog: StartStreamingLivenessSessionRequestFilterSensitiveLog, + outputFilterSensitiveLog: StartStreamingLivenessSessionResponseFilterSensitiveLog, + }; + const { requestHandler } = configuration; + return stack.resolve( + (request: FinalizeHandlerArguments) => + requestHandler.handle(request.request as __HttpRequest, options || {}), + handlerExecutionContext + ); + } + + /** + * @internal + */ + private serialize( + input: StartStreamingLivenessSessionCommandInput, + context: __SerdeContext & __EventStreamSerdeContext + ): Promise<__HttpRequest> { + return serializeAws_restJson1StartStreamingLivenessSessionCommand(input, context); + } + + /** + * @internal + */ + private deserialize( + output: __HttpResponse, + context: __SerdeContext & __EventStreamSerdeContext + ): Promise { + return deserializeAws_restJson1StartStreamingLivenessSessionCommand(output, context); + } + + // Start section: command_body_extra + // End section: command_body_extra +} diff --git a/clients/client-rekognitionstreaming/src/commands/index.ts b/clients/client-rekognitionstreaming/src/commands/index.ts new file mode 100644 index 000000000000..4372ecd3959e --- /dev/null +++ b/clients/client-rekognitionstreaming/src/commands/index.ts @@ -0,0 +1,3 @@ +// smithy-typescript generated code +export * from "./StartFaceLivenessSessionCommand"; +export * from "./StartStreamingLivenessSessionCommand"; diff --git a/clients/client-rekognitionstreaming/src/endpoint/EndpointParameters.ts b/clients/client-rekognitionstreaming/src/endpoint/EndpointParameters.ts new file mode 100644 index 000000000000..a20559909ca0 --- /dev/null +++ b/clients/client-rekognitionstreaming/src/endpoint/EndpointParameters.ts @@ -0,0 +1,31 @@ +// smithy-typescript generated code +import { Endpoint, EndpointParameters as __EndpointParameters, EndpointV2, Provider } from "@aws-sdk/types"; + +export interface ClientInputEndpointParameters { + region?: string | Provider; + useDualstackEndpoint?: boolean | Provider; + useFipsEndpoint?: boolean | Provider; + endpoint?: string | Provider | Endpoint | Provider | EndpointV2 | Provider; +} + +export type ClientResolvedEndpointParameters = ClientInputEndpointParameters & { + defaultSigningName: string; +}; + +export const resolveClientEndpointParameters = ( + options: T & ClientInputEndpointParameters +): T & ClientResolvedEndpointParameters => { + return { + ...options, + useDualstackEndpoint: options.useDualstackEndpoint ?? false, + useFipsEndpoint: options.useFipsEndpoint ?? false, + defaultSigningName: "rekognition", + }; +}; + +export interface EndpointParameters extends __EndpointParameters { + Region?: string; + UseDualStack?: boolean; + UseFIPS?: boolean; + Endpoint?: string; +} diff --git a/clients/client-rekognitionstreaming/src/endpoint/endpointResolver.ts b/clients/client-rekognitionstreaming/src/endpoint/endpointResolver.ts new file mode 100644 index 000000000000..4a70bb06b40a --- /dev/null +++ b/clients/client-rekognitionstreaming/src/endpoint/endpointResolver.ts @@ -0,0 +1,16 @@ +// smithy-typescript generated code +import { EndpointV2, Logger } from "@aws-sdk/types"; +import { EndpointParams, resolveEndpoint } from "@aws-sdk/util-endpoints"; + +import { EndpointParameters } from "./EndpointParameters"; +import { ruleSet } from "./ruleset"; + +export const defaultEndpointResolver = ( + endpointParams: EndpointParameters, + context: { logger?: Logger } = {} +): EndpointV2 => { + return resolveEndpoint(ruleSet, { + endpointParams: endpointParams as EndpointParams, + logger: context.logger, + }); +}; diff --git a/clients/client-rekognitionstreaming/src/endpoint/ruleset.ts b/clients/client-rekognitionstreaming/src/endpoint/ruleset.ts new file mode 100644 index 000000000000..71fac527fe44 --- /dev/null +++ b/clients/client-rekognitionstreaming/src/endpoint/ruleset.ts @@ -0,0 +1,29 @@ +// @ts-nocheck +// generated code, do not edit +import { RuleSetObject } from "@aws-sdk/util-endpoints"; + +/* This file is compressed. Log this object + or see "smithy.rules#endpointRuleSet" + in codegen/sdk-codegen/aws-models/rekognitionstreaming.json */ + +const p="required", +q="fn", +r="argv", +s="ref"; +const a="PartitionResult", +b="tree", +c="error", +d="endpoint", +e={[p]:false,"type":"String"}, +f={[p]:true,"default":false,"type":"Boolean"}, +g={[s]:"Endpoint"}, +h={[q]:"booleanEquals",[r]:[{[s]:"UseFIPS"},true]}, +i={[q]:"booleanEquals",[r]:[{[s]:"UseDualStack"},true]}, +j={}, +k={[q]:"booleanEquals",[r]:[true,{[q]:"getAttr",[r]:[{[s]:a},"supportsFIPS"]}]}, +l={[q]:"booleanEquals",[r]:[true,{[q]:"getAttr",[r]:[{[s]:a},"supportsDualStack"]}]}, +m=[g], +n=[h], +o=[i]; +const _data={version:"1.0",parameters:{Region:e,UseDualStack:f,UseFIPS:f,Endpoint:e},rules:[{conditions:[{[q]:"aws.partition",[r]:[{[s]:"Region"}],assign:a}],type:b,rules:[{conditions:[{[q]:"isSet",[r]:m},{[q]:"parseURL",[r]:m,assign:"url"}],type:b,rules:[{conditions:n,error:"Invalid Configuration: FIPS and custom endpoint are not supported",type:c},{type:b,rules:[{conditions:o,error:"Invalid Configuration: Dualstack and custom endpoint are not supported",type:c},{endpoint:{url:g,properties:j,headers:j},type:d}]}]},{conditions:[h,i],type:b,rules:[{conditions:[k,l],type:b,rules:[{endpoint:{url:"https://rekognitionstreaming-fips.{Region}.{PartitionResult#dualStackDnsSuffix}",properties:j,headers:j},type:d}]},{error:"FIPS and DualStack are enabled, but this partition does not support one or both",type:c}]},{conditions:n,type:b,rules:[{conditions:[k],type:b,rules:[{endpoint:{url:"https://rekognitionstreaming-fips.{Region}.{PartitionResult#dnsSuffix}",properties:j,headers:j},type:d}]},{error:"FIPS is enabled but this partition does not support FIPS",type:c}]},{conditions:o,type:b,rules:[{conditions:[l],type:b,rules:[{endpoint:{url:"https://rekognitionstreaming.{Region}.{PartitionResult#dualStackDnsSuffix}",properties:j,headers:j},type:d}]},{error:"DualStack is enabled but this partition does not support DualStack",type:c}]},{endpoint:{url:"https://rekognitionstreaming.{Region}.{PartitionResult#dnsSuffix}",properties:j,headers:j},type:d}]}]}; +export const ruleSet: RuleSetObject = _data; diff --git a/clients/client-rekognitionstreaming/src/index.ts b/clients/client-rekognitionstreaming/src/index.ts new file mode 100644 index 000000000000..d108f18fb6de --- /dev/null +++ b/clients/client-rekognitionstreaming/src/index.ts @@ -0,0 +1,7 @@ +export * from "./RekognitionStreaming"; +// smithy-typescript generated code +export * from "./RekognitionStreamingClient"; +export * from "./commands"; +export * from "./models"; + +export { RekognitionStreamingServiceException } from "./models/RekognitionStreamingServiceException"; diff --git a/clients/client-rekognitionstreaming/src/models/RekognitionStreamingServiceException.ts b/clients/client-rekognitionstreaming/src/models/RekognitionStreamingServiceException.ts new file mode 100644 index 000000000000..c6b94d8b9ad9 --- /dev/null +++ b/clients/client-rekognitionstreaming/src/models/RekognitionStreamingServiceException.ts @@ -0,0 +1,20 @@ +// smithy-typescript generated code +import { + ServiceException as __ServiceException, + ServiceExceptionOptions as __ServiceExceptionOptions, +} from "@aws-sdk/smithy-client"; + +/** + * @public + * + * Base exception class for all service exceptions from RekognitionStreaming service. + */ +export class RekognitionStreamingServiceException extends __ServiceException { + /** + * @internal + */ + constructor(options: __ServiceExceptionOptions) { + super(options); + Object.setPrototypeOf(this, RekognitionStreamingServiceException.prototype); + } +} diff --git a/clients/client-rekognitionstreaming/src/models/index.ts b/clients/client-rekognitionstreaming/src/models/index.ts new file mode 100644 index 000000000000..9eaceb12865f --- /dev/null +++ b/clients/client-rekognitionstreaming/src/models/index.ts @@ -0,0 +1,2 @@ +// smithy-typescript generated code +export * from "./models_0"; diff --git a/clients/client-rekognitionstreaming/src/models/models_0.ts b/clients/client-rekognitionstreaming/src/models/models_0.ts new file mode 100644 index 000000000000..b3143ad799e1 --- /dev/null +++ b/clients/client-rekognitionstreaming/src/models/models_0.ts @@ -0,0 +1,595 @@ +// smithy-typescript generated code +import { ExceptionOptionType as __ExceptionOptionType } from "@aws-sdk/smithy-client"; + +import { RekognitionStreamingServiceException as __BaseException } from "./RekognitionStreamingServiceException"; + +/** + * @public + */ +export class AccessDeniedException extends __BaseException { + readonly name: "AccessDeniedException" = "AccessDeniedException"; + readonly $fault: "client" = "client"; + Message?: string; + Code?: string; + /** + * @internal + */ + constructor(opts: __ExceptionOptionType) { + super({ + name: "AccessDeniedException", + $fault: "client", + ...opts, + }); + Object.setPrototypeOf(this, AccessDeniedException.prototype); + this.Message = opts.Message; + this.Code = opts.Code; + } +} + +/** + * @public + */ +export interface BoundingBox { + Width: number | undefined; + Height: number | undefined; + Left: number | undefined; + Top: number | undefined; +} + +/** + * @public + */ +export interface FreshnessColor { + RGB: number[] | undefined; +} + +/** + * @public + */ +export interface ColorDisplayed { + CurrentColor: FreshnessColor | undefined; + PreviousColor?: FreshnessColor; + SequenceNumber: number | undefined; + CurrentColorStartTimestamp: number | undefined; +} + +/** + * @public + */ +export interface InitialFace { + BoundingBox: BoundingBox | undefined; + InitialFaceDetectedTimestamp: number | undefined; +} + +/** + * @public + */ +export interface TargetFace { + BoundingBox: BoundingBox | undefined; + FaceDetectedInTargetPositionStartTimestamp: number | undefined; + FaceDetectedInTargetPositionEndTimestamp: number | undefined; +} + +/** + * @public + */ +export interface FaceMovementAndLightClientChallenge { + ChallengeId: string | undefined; + VideoStartTimestamp?: number; + InitialFace?: InitialFace; + TargetFace?: TargetFace; + ColorDisplayed?: ColorDisplayed; +} + +/** + * @public + */ +export type ClientChallenge = ClientChallenge.FaceMovementAndLightChallengeMember | ClientChallenge.$UnknownMember; + +/** + * @public + */ +export namespace ClientChallenge { + export interface FaceMovementAndLightChallengeMember { + FaceMovementAndLightChallenge: FaceMovementAndLightClientChallenge; + $unknown?: never; + } + + export interface $UnknownMember { + FaceMovementAndLightChallenge?: never; + $unknown: [string, any]; + } + + export interface Visitor { + FaceMovementAndLightChallenge: (value: FaceMovementAndLightClientChallenge) => T; + _: (name: string, value: any) => T; + } + + export const visit = (value: ClientChallenge, visitor: Visitor): T => { + if (value.FaceMovementAndLightChallenge !== undefined) + return visitor.FaceMovementAndLightChallenge(value.FaceMovementAndLightChallenge); + return visitor._(value.$unknown[0], value.$unknown[1]); + }; +} + +/** + * @public + */ +export interface DeviceInformation { + VideoHeight: number | undefined; + VideoWidth: number | undefined; + ClientSDKVersion: string | undefined; +} + +/** + * @public + */ +export interface ClientSessionInformationEvent { + DeviceInformation: DeviceInformation | undefined; + Challenge: ClientChallenge | undefined; +} + +/** + * @public + */ +export interface ColorSequence { + FreshnessColor: FreshnessColor | undefined; + DownscrollDuration: number | undefined; + FlatDisplayDuration: number | undefined; +} + +/** + * @public + */ +export interface DisconnectionEvent { + TimestampMillis: number | undefined; +} + +/** + * @public + * @enum + */ +export const LightChallengeType = { + SEQUENTIAL: "SEQUENTIAL", + SIMULTANEOUS: "SIMULTANEOUS", +} as const; + +/** + * @public + */ +export type LightChallengeType = (typeof LightChallengeType)[keyof typeof LightChallengeType]; + +/** + * @public + */ +export interface OvalScaleFactors { + Width: number | undefined; + CenterX: number | undefined; + CenterY: number | undefined; +} + +/** + * @public + */ +export interface FaceMovementAndLightServerChallenge { + OvalScaleFactors: OvalScaleFactors | undefined; + LightChallengeType: LightChallengeType | string | undefined; + ColorSequences: ColorSequence[] | undefined; +} + +/** + * @public + */ +export class InternalServerException extends __BaseException { + readonly name: "InternalServerException" = "InternalServerException"; + readonly $fault: "server" = "server"; + Message?: string; + Code?: string; + /** + * @internal + */ + constructor(opts: __ExceptionOptionType) { + super({ + name: "InternalServerException", + $fault: "server", + ...opts, + }); + Object.setPrototypeOf(this, InternalServerException.prototype); + this.Message = opts.Message; + this.Code = opts.Code; + } +} + +/** + * @public + */ +export interface VideoEvent { + VideoChunk?: Uint8Array; + TimestampMillis?: number; +} + +/** + * @public + */ +export type LivenessRequestStream = + | LivenessRequestStream.ClientSessionInformationEventMember + | LivenessRequestStream.VideoEventMember + | LivenessRequestStream.$UnknownMember; + +/** + * @public + */ +export namespace LivenessRequestStream { + export interface VideoEventMember { + VideoEvent: VideoEvent; + ClientSessionInformationEvent?: never; + $unknown?: never; + } + + export interface ClientSessionInformationEventMember { + VideoEvent?: never; + ClientSessionInformationEvent: ClientSessionInformationEvent; + $unknown?: never; + } + + export interface $UnknownMember { + VideoEvent?: never; + ClientSessionInformationEvent?: never; + $unknown: [string, any]; + } + + export interface Visitor { + VideoEvent: (value: VideoEvent) => T; + ClientSessionInformationEvent: (value: ClientSessionInformationEvent) => T; + _: (name: string, value: any) => T; + } + + export const visit = (value: LivenessRequestStream, visitor: Visitor): T => { + if (value.VideoEvent !== undefined) return visitor.VideoEvent(value.VideoEvent); + if (value.ClientSessionInformationEvent !== undefined) + return visitor.ClientSessionInformationEvent(value.ClientSessionInformationEvent); + return visitor._(value.$unknown[0], value.$unknown[1]); + }; +} + +/** + * @public + */ +export type ServerChallenge = ServerChallenge.FaceMovementAndLightChallengeMember | ServerChallenge.$UnknownMember; + +/** + * @public + */ +export namespace ServerChallenge { + export interface FaceMovementAndLightChallengeMember { + FaceMovementAndLightChallenge: FaceMovementAndLightServerChallenge; + $unknown?: never; + } + + export interface $UnknownMember { + FaceMovementAndLightChallenge?: never; + $unknown: [string, any]; + } + + export interface Visitor { + FaceMovementAndLightChallenge: (value: FaceMovementAndLightServerChallenge) => T; + _: (name: string, value: any) => T; + } + + export const visit = (value: ServerChallenge, visitor: Visitor): T => { + if (value.FaceMovementAndLightChallenge !== undefined) + return visitor.FaceMovementAndLightChallenge(value.FaceMovementAndLightChallenge); + return visitor._(value.$unknown[0], value.$unknown[1]); + }; +} + +/** + * @public + */ +export interface SessionInformation { + Challenge: ServerChallenge | undefined; +} + +/** + * @public + */ +export interface ServerSessionInformationEvent { + SessionInformation: SessionInformation | undefined; +} + +/** + * @public + */ +export class ServiceQuotaExceededException extends __BaseException { + readonly name: "ServiceQuotaExceededException" = "ServiceQuotaExceededException"; + readonly $fault: "client" = "client"; + Message?: string; + Code?: string; + /** + * @internal + */ + constructor(opts: __ExceptionOptionType) { + super({ + name: "ServiceQuotaExceededException", + $fault: "client", + ...opts, + }); + Object.setPrototypeOf(this, ServiceQuotaExceededException.prototype); + this.Message = opts.Message; + this.Code = opts.Code; + } +} + +/** + * @public + */ +export class ThrottlingException extends __BaseException { + readonly name: "ThrottlingException" = "ThrottlingException"; + readonly $fault: "client" = "client"; + Message?: string; + Code?: string; + /** + * @internal + */ + constructor(opts: __ExceptionOptionType) { + super({ + name: "ThrottlingException", + $fault: "client", + ...opts, + }); + Object.setPrototypeOf(this, ThrottlingException.prototype); + this.Message = opts.Message; + this.Code = opts.Code; + } +} + +/** + * @public + */ +export class ValidationException extends __BaseException { + readonly name: "ValidationException" = "ValidationException"; + readonly $fault: "client" = "client"; + Message?: string; + Code?: string; + /** + * @internal + */ + constructor(opts: __ExceptionOptionType) { + super({ + name: "ValidationException", + $fault: "client", + ...opts, + }); + Object.setPrototypeOf(this, ValidationException.prototype); + this.Message = opts.Message; + this.Code = opts.Code; + } +} + +/** + * @public + */ +export type LivenessResponseStream = + | LivenessResponseStream.DisconnectionEventMember + | LivenessResponseStream.InternalServerExceptionMember + | LivenessResponseStream.ServerSessionInformationEventMember + | LivenessResponseStream.ServiceQuotaExceededExceptionMember + | LivenessResponseStream.ThrottlingExceptionMember + | LivenessResponseStream.ValidationExceptionMember + | LivenessResponseStream.$UnknownMember; + +/** + * @public + */ +export namespace LivenessResponseStream { + export interface ServerSessionInformationEventMember { + ServerSessionInformationEvent: ServerSessionInformationEvent; + DisconnectionEvent?: never; + ValidationException?: never; + InternalServerException?: never; + ThrottlingException?: never; + ServiceQuotaExceededException?: never; + $unknown?: never; + } + + export interface DisconnectionEventMember { + ServerSessionInformationEvent?: never; + DisconnectionEvent: DisconnectionEvent; + ValidationException?: never; + InternalServerException?: never; + ThrottlingException?: never; + ServiceQuotaExceededException?: never; + $unknown?: never; + } + + export interface ValidationExceptionMember { + ServerSessionInformationEvent?: never; + DisconnectionEvent?: never; + ValidationException: ValidationException; + InternalServerException?: never; + ThrottlingException?: never; + ServiceQuotaExceededException?: never; + $unknown?: never; + } + + export interface InternalServerExceptionMember { + ServerSessionInformationEvent?: never; + DisconnectionEvent?: never; + ValidationException?: never; + InternalServerException: InternalServerException; + ThrottlingException?: never; + ServiceQuotaExceededException?: never; + $unknown?: never; + } + + export interface ThrottlingExceptionMember { + ServerSessionInformationEvent?: never; + DisconnectionEvent?: never; + ValidationException?: never; + InternalServerException?: never; + ThrottlingException: ThrottlingException; + ServiceQuotaExceededException?: never; + $unknown?: never; + } + + export interface ServiceQuotaExceededExceptionMember { + ServerSessionInformationEvent?: never; + DisconnectionEvent?: never; + ValidationException?: never; + InternalServerException?: never; + ThrottlingException?: never; + ServiceQuotaExceededException: ServiceQuotaExceededException; + $unknown?: never; + } + + export interface $UnknownMember { + ServerSessionInformationEvent?: never; + DisconnectionEvent?: never; + ValidationException?: never; + InternalServerException?: never; + ThrottlingException?: never; + ServiceQuotaExceededException?: never; + $unknown: [string, any]; + } + + export interface Visitor { + ServerSessionInformationEvent: (value: ServerSessionInformationEvent) => T; + DisconnectionEvent: (value: DisconnectionEvent) => T; + ValidationException: (value: ValidationException) => T; + InternalServerException: (value: InternalServerException) => T; + ThrottlingException: (value: ThrottlingException) => T; + ServiceQuotaExceededException: (value: ServiceQuotaExceededException) => T; + _: (name: string, value: any) => T; + } + + export const visit = (value: LivenessResponseStream, visitor: Visitor): T => { + if (value.ServerSessionInformationEvent !== undefined) + return visitor.ServerSessionInformationEvent(value.ServerSessionInformationEvent); + if (value.DisconnectionEvent !== undefined) return visitor.DisconnectionEvent(value.DisconnectionEvent); + if (value.ValidationException !== undefined) return visitor.ValidationException(value.ValidationException); + if (value.InternalServerException !== undefined) + return visitor.InternalServerException(value.InternalServerException); + if (value.ThrottlingException !== undefined) return visitor.ThrottlingException(value.ThrottlingException); + if (value.ServiceQuotaExceededException !== undefined) + return visitor.ServiceQuotaExceededException(value.ServiceQuotaExceededException); + return visitor._(value.$unknown[0], value.$unknown[1]); + }; +} + +/** + * @public + */ +export class SessionNotFoundException extends __BaseException { + readonly name: "SessionNotFoundException" = "SessionNotFoundException"; + readonly $fault: "client" = "client"; + Message?: string; + Code?: string; + /** + * @internal + */ + constructor(opts: __ExceptionOptionType) { + super({ + name: "SessionNotFoundException", + $fault: "client", + ...opts, + }); + Object.setPrototypeOf(this, SessionNotFoundException.prototype); + this.Message = opts.Message; + this.Code = opts.Code; + } +} + +/** + * @public + */ +export interface StartFaceLivenessSessionRequest { + SessionId: string | undefined; + ClientSDKVersion: string | undefined; + LivenessRequestStream?: AsyncIterable; +} + +/** + * @public + */ +export interface StartFaceLivenessSessionResponse { + SessionId: string | undefined; + LivenessResponseStream?: AsyncIterable; +} + +/** + * @public + */ +export interface StartStreamingLivenessSessionRequest { + SessionId: string | undefined; + ClientSDKVersion: string | undefined; + LivenessRequestStream?: AsyncIterable; +} + +/** + * @public + */ +export interface StartStreamingLivenessSessionResponse { + SessionId: string | undefined; + LivenessResponseStream?: AsyncIterable; +} + +/** + * @internal + */ +export const LivenessRequestStreamFilterSensitiveLog = (obj: LivenessRequestStream): any => { + if (obj.VideoEvent !== undefined) return { VideoEvent: obj.VideoEvent }; + if (obj.ClientSessionInformationEvent !== undefined) + return { ClientSessionInformationEvent: obj.ClientSessionInformationEvent }; + if (obj.$unknown !== undefined) return { [obj.$unknown[0]]: "UNKNOWN" }; +}; + +/** + * @internal + */ +export const LivenessResponseStreamFilterSensitiveLog = (obj: LivenessResponseStream): any => { + if (obj.ServerSessionInformationEvent !== undefined) + return { ServerSessionInformationEvent: obj.ServerSessionInformationEvent }; + if (obj.DisconnectionEvent !== undefined) return { DisconnectionEvent: obj.DisconnectionEvent }; + if (obj.ValidationException !== undefined) return { ValidationException: obj.ValidationException }; + if (obj.InternalServerException !== undefined) return { InternalServerException: obj.InternalServerException }; + if (obj.ThrottlingException !== undefined) return { ThrottlingException: obj.ThrottlingException }; + if (obj.ServiceQuotaExceededException !== undefined) + return { ServiceQuotaExceededException: obj.ServiceQuotaExceededException }; + if (obj.$unknown !== undefined) return { [obj.$unknown[0]]: "UNKNOWN" }; +}; + +/** + * @internal + */ +export const StartFaceLivenessSessionRequestFilterSensitiveLog = (obj: StartFaceLivenessSessionRequest): any => ({ + ...obj, + ...(obj.LivenessRequestStream && { LivenessRequestStream: "STREAMING_CONTENT" }), +}); + +/** + * @internal + */ +export const StartFaceLivenessSessionResponseFilterSensitiveLog = (obj: StartFaceLivenessSessionResponse): any => ({ + ...obj, + ...(obj.LivenessResponseStream && { LivenessResponseStream: "STREAMING_CONTENT" }), +}); + +/** + * @internal + */ +export const StartStreamingLivenessSessionRequestFilterSensitiveLog = ( + obj: StartStreamingLivenessSessionRequest +): any => ({ + ...obj, + ...(obj.LivenessRequestStream && { LivenessRequestStream: "STREAMING_CONTENT" }), +}); + +/** + * @internal + */ +export const StartStreamingLivenessSessionResponseFilterSensitiveLog = ( + obj: StartStreamingLivenessSessionResponse +): any => ({ + ...obj, + ...(obj.LivenessResponseStream && { LivenessResponseStream: "STREAMING_CONTENT" }), +}); diff --git a/clients/client-rekognitionstreaming/src/protocols/Aws_restJson1.ts b/clients/client-rekognitionstreaming/src/protocols/Aws_restJson1.ts new file mode 100644 index 000000000000..2ad8efc6e9db --- /dev/null +++ b/clients/client-rekognitionstreaming/src/protocols/Aws_restJson1.ts @@ -0,0 +1,778 @@ +// smithy-typescript generated code +import { HttpRequest as __HttpRequest, HttpResponse as __HttpResponse } from "@aws-sdk/protocol-http"; +import { + decorateServiceException as __decorateServiceException, + expectInt32 as __expectInt32, + expectLong as __expectLong, + expectString as __expectString, + expectUnion as __expectUnion, + limitedParseFloat32 as __limitedParseFloat32, + map as __map, + serializeFloat as __serializeFloat, + throwDefaultError, +} from "@aws-sdk/smithy-client"; +import { + Endpoint as __Endpoint, + EventStreamSerdeContext as __EventStreamSerdeContext, + Message as __Message, + MessageHeaders as __MessageHeaders, + ResponseMetadata as __ResponseMetadata, + SerdeContext as __SerdeContext, +} from "@aws-sdk/types"; + +import { + StartFaceLivenessSessionCommandInput, + StartFaceLivenessSessionCommandOutput, +} from "../commands/StartFaceLivenessSessionCommand"; +import { + StartStreamingLivenessSessionCommandInput, + StartStreamingLivenessSessionCommandOutput, +} from "../commands/StartStreamingLivenessSessionCommand"; +import { + AccessDeniedException, + BoundingBox, + ClientChallenge, + ClientSessionInformationEvent, + ColorDisplayed, + ColorSequence, + DeviceInformation, + DisconnectionEvent, + FaceMovementAndLightClientChallenge, + FaceMovementAndLightServerChallenge, + FreshnessColor, + InitialFace, + InternalServerException, + LivenessRequestStream, + LivenessResponseStream, + OvalScaleFactors, + ServerChallenge, + ServerSessionInformationEvent, + ServiceQuotaExceededException, + SessionInformation, + SessionNotFoundException, + TargetFace, + ThrottlingException, + ValidationException, + VideoEvent, +} from "../models/models_0"; +import { RekognitionStreamingServiceException as __BaseException } from "../models/RekognitionStreamingServiceException"; + +export const serializeAws_restJson1StartFaceLivenessSessionCommand = async ( + input: StartFaceLivenessSessionCommandInput, + context: __SerdeContext & __EventStreamSerdeContext +): Promise<__HttpRequest> => { + const { hostname, protocol = "https", port, path: basePath } = await context.endpoint(); + const headers: any = map({}, isSerializableHeaderValue, { + "x-amz-rekognition-streaming-liveness-session-id": input.SessionId!, + "x-amz-rekognition-streaming-liveness-client-sdk-version": input.ClientSDKVersion!, + }); + const resolvedPath = + `${basePath?.endsWith("/") ? basePath.slice(0, -1) : basePath || ""}` + "/start-face-liveness-session"; + let body: any; + if (input.LivenessRequestStream !== undefined) { + body = serializeAws_restJson1LivenessRequestStream(input.LivenessRequestStream, context); + } + return new __HttpRequest({ + protocol, + hostname, + port, + method: "POST", + headers, + path: resolvedPath, + body, + }); +}; + +export const serializeAws_restJson1StartStreamingLivenessSessionCommand = async ( + input: StartStreamingLivenessSessionCommandInput, + context: __SerdeContext & __EventStreamSerdeContext +): Promise<__HttpRequest> => { + const { hostname, protocol = "https", port, path: basePath } = await context.endpoint(); + const headers: any = map({}, isSerializableHeaderValue, { + "x-amz-rekognition-streaming-liveness-session-id": input.SessionId!, + "x-amz-rekognition-streaming-liveness-client-sdk-version": input.ClientSDKVersion!, + }); + const resolvedPath = + `${basePath?.endsWith("/") ? basePath.slice(0, -1) : basePath || ""}` + "/start-streaming-liveness-session"; + let body: any; + if (input.LivenessRequestStream !== undefined) { + body = serializeAws_restJson1LivenessRequestStream(input.LivenessRequestStream, context); + } + return new __HttpRequest({ + protocol, + hostname, + port, + method: "POST", + headers, + path: resolvedPath, + body, + }); +}; + +export const deserializeAws_restJson1StartFaceLivenessSessionCommand = async ( + output: __HttpResponse, + context: __SerdeContext & __EventStreamSerdeContext +): Promise => { + if (output.statusCode !== 200 && output.statusCode >= 300) { + return deserializeAws_restJson1StartFaceLivenessSessionCommandError(output, context); + } + const contents: any = map({ + $metadata: deserializeMetadata(output), + SessionId: [, output.headers["x-amz-rekognition-streaming-liveness-session-id"]], + }); + const data: any = output.body; + contents.LivenessResponseStream = deserializeAws_restJson1LivenessResponseStream(data, context); + return contents; +}; + +const deserializeAws_restJson1StartFaceLivenessSessionCommandError = async ( + output: __HttpResponse, + context: __SerdeContext +): Promise => { + const parsedOutput: any = { + ...output, + body: await parseErrorBody(output.body, context), + }; + const errorCode = loadRestJsonErrorCode(output, parsedOutput.body); + switch (errorCode) { + case "AccessDeniedException": + case "com.amazonaws.rekognitionstreaming#AccessDeniedException": + throw await deserializeAws_restJson1AccessDeniedExceptionResponse(parsedOutput, context); + case "InternalServerException": + case "com.amazonaws.rekognitionstreaming#InternalServerException": + throw await deserializeAws_restJson1InternalServerExceptionResponse(parsedOutput, context); + case "ServiceQuotaExceededException": + case "com.amazonaws.rekognitionstreaming#ServiceQuotaExceededException": + throw await deserializeAws_restJson1ServiceQuotaExceededExceptionResponse(parsedOutput, context); + case "SessionNotFoundException": + case "com.amazonaws.rekognitionstreaming#SessionNotFoundException": + throw await deserializeAws_restJson1SessionNotFoundExceptionResponse(parsedOutput, context); + case "ThrottlingException": + case "com.amazonaws.rekognitionstreaming#ThrottlingException": + throw await deserializeAws_restJson1ThrottlingExceptionResponse(parsedOutput, context); + case "ValidationException": + case "com.amazonaws.rekognitionstreaming#ValidationException": + throw await deserializeAws_restJson1ValidationExceptionResponse(parsedOutput, context); + default: + const parsedBody = parsedOutput.body; + throwDefaultError({ + output, + parsedBody, + exceptionCtor: __BaseException, + errorCode, + }); + } +}; + +export const deserializeAws_restJson1StartStreamingLivenessSessionCommand = async ( + output: __HttpResponse, + context: __SerdeContext & __EventStreamSerdeContext +): Promise => { + if (output.statusCode !== 200 && output.statusCode >= 300) { + return deserializeAws_restJson1StartStreamingLivenessSessionCommandError(output, context); + } + const contents: any = map({ + $metadata: deserializeMetadata(output), + SessionId: [, output.headers["x-amz-rekognition-streaming-liveness-session-id"]], + }); + const data: any = output.body; + contents.LivenessResponseStream = deserializeAws_restJson1LivenessResponseStream(data, context); + return contents; +}; + +const deserializeAws_restJson1StartStreamingLivenessSessionCommandError = async ( + output: __HttpResponse, + context: __SerdeContext +): Promise => { + const parsedOutput: any = { + ...output, + body: await parseErrorBody(output.body, context), + }; + const errorCode = loadRestJsonErrorCode(output, parsedOutput.body); + switch (errorCode) { + case "AccessDeniedException": + case "com.amazonaws.rekognitionstreaming#AccessDeniedException": + throw await deserializeAws_restJson1AccessDeniedExceptionResponse(parsedOutput, context); + case "InternalServerException": + case "com.amazonaws.rekognitionstreaming#InternalServerException": + throw await deserializeAws_restJson1InternalServerExceptionResponse(parsedOutput, context); + case "ServiceQuotaExceededException": + case "com.amazonaws.rekognitionstreaming#ServiceQuotaExceededException": + throw await deserializeAws_restJson1ServiceQuotaExceededExceptionResponse(parsedOutput, context); + case "SessionNotFoundException": + case "com.amazonaws.rekognitionstreaming#SessionNotFoundException": + throw await deserializeAws_restJson1SessionNotFoundExceptionResponse(parsedOutput, context); + case "ThrottlingException": + case "com.amazonaws.rekognitionstreaming#ThrottlingException": + throw await deserializeAws_restJson1ThrottlingExceptionResponse(parsedOutput, context); + case "ValidationException": + case "com.amazonaws.rekognitionstreaming#ValidationException": + throw await deserializeAws_restJson1ValidationExceptionResponse(parsedOutput, context); + default: + const parsedBody = parsedOutput.body; + throwDefaultError({ + output, + parsedBody, + exceptionCtor: __BaseException, + errorCode, + }); + } +}; + +const map = __map; +const deserializeAws_restJson1AccessDeniedExceptionResponse = async ( + parsedOutput: any, + context: __SerdeContext +): Promise => { + const contents: any = map({}); + const data: any = parsedOutput.body; + if (data.Code != null) { + contents.Code = __expectString(data.Code); + } + if (data.Message != null) { + contents.Message = __expectString(data.Message); + } + const exception = new AccessDeniedException({ + $metadata: deserializeMetadata(parsedOutput), + ...contents, + }); + return __decorateServiceException(exception, parsedOutput.body); +}; + +const deserializeAws_restJson1InternalServerExceptionResponse = async ( + parsedOutput: any, + context: __SerdeContext +): Promise => { + const contents: any = map({}); + const data: any = parsedOutput.body; + if (data.Code != null) { + contents.Code = __expectString(data.Code); + } + if (data.Message != null) { + contents.Message = __expectString(data.Message); + } + const exception = new InternalServerException({ + $metadata: deserializeMetadata(parsedOutput), + ...contents, + }); + return __decorateServiceException(exception, parsedOutput.body); +}; + +const deserializeAws_restJson1ServiceQuotaExceededExceptionResponse = async ( + parsedOutput: any, + context: __SerdeContext +): Promise => { + const contents: any = map({}); + const data: any = parsedOutput.body; + if (data.Code != null) { + contents.Code = __expectString(data.Code); + } + if (data.Message != null) { + contents.Message = __expectString(data.Message); + } + const exception = new ServiceQuotaExceededException({ + $metadata: deserializeMetadata(parsedOutput), + ...contents, + }); + return __decorateServiceException(exception, parsedOutput.body); +}; + +const deserializeAws_restJson1SessionNotFoundExceptionResponse = async ( + parsedOutput: any, + context: __SerdeContext +): Promise => { + const contents: any = map({}); + const data: any = parsedOutput.body; + if (data.Code != null) { + contents.Code = __expectString(data.Code); + } + if (data.Message != null) { + contents.Message = __expectString(data.Message); + } + const exception = new SessionNotFoundException({ + $metadata: deserializeMetadata(parsedOutput), + ...contents, + }); + return __decorateServiceException(exception, parsedOutput.body); +}; + +const deserializeAws_restJson1ThrottlingExceptionResponse = async ( + parsedOutput: any, + context: __SerdeContext +): Promise => { + const contents: any = map({}); + const data: any = parsedOutput.body; + if (data.Code != null) { + contents.Code = __expectString(data.Code); + } + if (data.Message != null) { + contents.Message = __expectString(data.Message); + } + const exception = new ThrottlingException({ + $metadata: deserializeMetadata(parsedOutput), + ...contents, + }); + return __decorateServiceException(exception, parsedOutput.body); +}; + +const deserializeAws_restJson1ValidationExceptionResponse = async ( + parsedOutput: any, + context: __SerdeContext +): Promise => { + const contents: any = map({}); + const data: any = parsedOutput.body; + if (data.Code != null) { + contents.Code = __expectString(data.Code); + } + if (data.Message != null) { + contents.Message = __expectString(data.Message); + } + const exception = new ValidationException({ + $metadata: deserializeMetadata(parsedOutput), + ...contents, + }); + return __decorateServiceException(exception, parsedOutput.body); +}; + +const serializeAws_restJson1LivenessRequestStream = ( + input: any, + context: __SerdeContext & __EventStreamSerdeContext +): any => { + const eventMarshallingVisitor = (event: any): __Message => + LivenessRequestStream.visit(event, { + VideoEvent: (value) => serializeAws_restJson1VideoEvent_event(value, context), + ClientSessionInformationEvent: (value) => + serializeAws_restJson1ClientSessionInformationEvent_event(value, context), + _: (value) => value as any, + }); + return context.eventStreamMarshaller.serialize(input, eventMarshallingVisitor); +}; +const serializeAws_restJson1ClientSessionInformationEvent_event = ( + input: ClientSessionInformationEvent, + context: __SerdeContext +): __Message => { + const headers: __MessageHeaders = { + ":event-type": { type: "string", value: "ClientSessionInformationEvent" }, + ":message-type": { type: "string", value: "event" }, + ":content-type": { type: "string", value: "application/json" }, + }; + let body = new Uint8Array(); + body = serializeAws_restJson1ClientSessionInformationEvent(input, context); + body = context.utf8Decoder(JSON.stringify(body)); + return { headers, body }; +}; +const serializeAws_restJson1VideoEvent_event = (input: VideoEvent, context: __SerdeContext): __Message => { + const headers: __MessageHeaders = { + ":event-type": { type: "string", value: "VideoEvent" }, + ":message-type": { type: "string", value: "event" }, + ":content-type": { type: "string", value: "application/json" }, + }; + let body = new Uint8Array(); + body = serializeAws_restJson1VideoEvent(input, context); + body = context.utf8Decoder(JSON.stringify(body)); + return { headers, body }; +}; +const deserializeAws_restJson1LivenessResponseStream = ( + output: any, + context: __SerdeContext & __EventStreamSerdeContext +): AsyncIterable => { + return context.eventStreamMarshaller.deserialize(output, async (event) => { + if (event["ServerSessionInformationEvent"] != null) { + return { + ServerSessionInformationEvent: await deserializeAws_restJson1ServerSessionInformationEvent_event( + event["ServerSessionInformationEvent"], + context + ), + }; + } + if (event["DisconnectionEvent"] != null) { + return { + DisconnectionEvent: await deserializeAws_restJson1DisconnectionEvent_event( + event["DisconnectionEvent"], + context + ), + }; + } + if (event["ValidationException"] != null) { + return { + ValidationException: await deserializeAws_restJson1ValidationException_event( + event["ValidationException"], + context + ), + }; + } + if (event["InternalServerException"] != null) { + return { + InternalServerException: await deserializeAws_restJson1InternalServerException_event( + event["InternalServerException"], + context + ), + }; + } + if (event["ThrottlingException"] != null) { + return { + ThrottlingException: await deserializeAws_restJson1ThrottlingException_event( + event["ThrottlingException"], + context + ), + }; + } + if (event["ServiceQuotaExceededException"] != null) { + return { + ServiceQuotaExceededException: await deserializeAws_restJson1ServiceQuotaExceededException_event( + event["ServiceQuotaExceededException"], + context + ), + }; + } + return { $unknown: output }; + }); +}; +const deserializeAws_restJson1DisconnectionEvent_event = async ( + output: any, + context: __SerdeContext +): Promise => { + const contents: DisconnectionEvent = {} as any; + const data: any = await parseBody(output.body, context); + Object.assign(contents, deserializeAws_restJson1DisconnectionEvent(data, context)); + return contents; +}; +const deserializeAws_restJson1InternalServerException_event = async ( + output: any, + context: __SerdeContext +): Promise => { + const parsedOutput: any = { + ...output, + body: await parseBody(output.body, context), + }; + return deserializeAws_restJson1InternalServerExceptionResponse(parsedOutput, context); +}; +const deserializeAws_restJson1ServerSessionInformationEvent_event = async ( + output: any, + context: __SerdeContext +): Promise => { + const contents: ServerSessionInformationEvent = {} as any; + const data: any = await parseBody(output.body, context); + Object.assign(contents, deserializeAws_restJson1ServerSessionInformationEvent(data, context)); + return contents; +}; +const deserializeAws_restJson1ServiceQuotaExceededException_event = async ( + output: any, + context: __SerdeContext +): Promise => { + const parsedOutput: any = { + ...output, + body: await parseBody(output.body, context), + }; + return deserializeAws_restJson1ServiceQuotaExceededExceptionResponse(parsedOutput, context); +}; +const deserializeAws_restJson1ThrottlingException_event = async ( + output: any, + context: __SerdeContext +): Promise => { + const parsedOutput: any = { + ...output, + body: await parseBody(output.body, context), + }; + return deserializeAws_restJson1ThrottlingExceptionResponse(parsedOutput, context); +}; +const deserializeAws_restJson1ValidationException_event = async ( + output: any, + context: __SerdeContext +): Promise => { + const parsedOutput: any = { + ...output, + body: await parseBody(output.body, context), + }; + return deserializeAws_restJson1ValidationExceptionResponse(parsedOutput, context); +}; +const serializeAws_restJson1BoundingBox = (input: BoundingBox, context: __SerdeContext): any => { + return { + ...(input.Height != null && { Height: __serializeFloat(input.Height) }), + ...(input.Left != null && { Left: __serializeFloat(input.Left) }), + ...(input.Top != null && { Top: __serializeFloat(input.Top) }), + ...(input.Width != null && { Width: __serializeFloat(input.Width) }), + }; +}; + +const serializeAws_restJson1ClientChallenge = (input: ClientChallenge, context: __SerdeContext): any => { + return ClientChallenge.visit(input, { + FaceMovementAndLightChallenge: (value) => ({ + FaceMovementAndLightChallenge: serializeAws_restJson1FaceMovementAndLightClientChallenge(value, context), + }), + _: (name, value) => ({ name: value } as any), + }); +}; + +const serializeAws_restJson1ClientSessionInformationEvent = ( + input: ClientSessionInformationEvent, + context: __SerdeContext +): any => { + return { + ...(input.Challenge != null && { Challenge: serializeAws_restJson1ClientChallenge(input.Challenge, context) }), + ...(input.DeviceInformation != null && { + DeviceInformation: serializeAws_restJson1DeviceInformation(input.DeviceInformation, context), + }), + }; +}; + +const serializeAws_restJson1ColorComponentList = (input: number[], context: __SerdeContext): any => { + return input + .filter((e: any) => e != null) + .map((entry) => { + return entry; + }); +}; + +const serializeAws_restJson1ColorDisplayed = (input: ColorDisplayed, context: __SerdeContext): any => { + return { + ...(input.CurrentColor != null && { + CurrentColor: serializeAws_restJson1FreshnessColor(input.CurrentColor, context), + }), + ...(input.CurrentColorStartTimestamp != null && { CurrentColorStartTimestamp: input.CurrentColorStartTimestamp }), + ...(input.PreviousColor != null && { + PreviousColor: serializeAws_restJson1FreshnessColor(input.PreviousColor, context), + }), + ...(input.SequenceNumber != null && { SequenceNumber: input.SequenceNumber }), + }; +}; + +const serializeAws_restJson1DeviceInformation = (input: DeviceInformation, context: __SerdeContext): any => { + return { + ...(input.ClientSDKVersion != null && { ClientSDKVersion: input.ClientSDKVersion }), + ...(input.VideoHeight != null && { VideoHeight: __serializeFloat(input.VideoHeight) }), + ...(input.VideoWidth != null && { VideoWidth: __serializeFloat(input.VideoWidth) }), + }; +}; + +const serializeAws_restJson1FaceMovementAndLightClientChallenge = ( + input: FaceMovementAndLightClientChallenge, + context: __SerdeContext +): any => { + return { + ...(input.ChallengeId != null && { ChallengeId: input.ChallengeId }), + ...(input.ColorDisplayed != null && { + ColorDisplayed: serializeAws_restJson1ColorDisplayed(input.ColorDisplayed, context), + }), + ...(input.InitialFace != null && { InitialFace: serializeAws_restJson1InitialFace(input.InitialFace, context) }), + ...(input.TargetFace != null && { TargetFace: serializeAws_restJson1TargetFace(input.TargetFace, context) }), + ...(input.VideoStartTimestamp != null && { VideoStartTimestamp: input.VideoStartTimestamp }), + }; +}; + +const serializeAws_restJson1FreshnessColor = (input: FreshnessColor, context: __SerdeContext): any => { + return { + ...(input.RGB != null && { RGB: serializeAws_restJson1ColorComponentList(input.RGB, context) }), + }; +}; + +const serializeAws_restJson1InitialFace = (input: InitialFace, context: __SerdeContext): any => { + return { + ...(input.BoundingBox != null && { BoundingBox: serializeAws_restJson1BoundingBox(input.BoundingBox, context) }), + ...(input.InitialFaceDetectedTimestamp != null && { + InitialFaceDetectedTimestamp: input.InitialFaceDetectedTimestamp, + }), + }; +}; + +const serializeAws_restJson1TargetFace = (input: TargetFace, context: __SerdeContext): any => { + return { + ...(input.BoundingBox != null && { BoundingBox: serializeAws_restJson1BoundingBox(input.BoundingBox, context) }), + ...(input.FaceDetectedInTargetPositionEndTimestamp != null && { + FaceDetectedInTargetPositionEndTimestamp: input.FaceDetectedInTargetPositionEndTimestamp, + }), + ...(input.FaceDetectedInTargetPositionStartTimestamp != null && { + FaceDetectedInTargetPositionStartTimestamp: input.FaceDetectedInTargetPositionStartTimestamp, + }), + }; +}; + +const serializeAws_restJson1VideoEvent = (input: VideoEvent, context: __SerdeContext): any => { + return { + ...(input.TimestampMillis != null && { TimestampMillis: input.TimestampMillis }), + ...(input.VideoChunk != null && { VideoChunk: context.base64Encoder(input.VideoChunk) }), + }; +}; + +const deserializeAws_restJson1ColorComponentList = (output: any, context: __SerdeContext): number[] => { + const retVal = (output || []) + .filter((e: any) => e != null) + .map((entry: any) => { + if (entry === null) { + return null as any; + } + return __expectInt32(entry) as any; + }); + return retVal; +}; + +const deserializeAws_restJson1ColorSequence = (output: any, context: __SerdeContext): ColorSequence => { + return { + DownscrollDuration: __limitedParseFloat32(output.DownscrollDuration), + FlatDisplayDuration: __limitedParseFloat32(output.FlatDisplayDuration), + FreshnessColor: + output.FreshnessColor != null + ? deserializeAws_restJson1FreshnessColor(output.FreshnessColor, context) + : undefined, + } as any; +}; + +const deserializeAws_restJson1ColorSequences = (output: any, context: __SerdeContext): ColorSequence[] => { + const retVal = (output || []) + .filter((e: any) => e != null) + .map((entry: any) => { + if (entry === null) { + return null as any; + } + return deserializeAws_restJson1ColorSequence(entry, context); + }); + return retVal; +}; + +const deserializeAws_restJson1DisconnectionEvent = (output: any, context: __SerdeContext): DisconnectionEvent => { + return { + TimestampMillis: __expectLong(output.TimestampMillis), + } as any; +}; + +const deserializeAws_restJson1FaceMovementAndLightServerChallenge = ( + output: any, + context: __SerdeContext +): FaceMovementAndLightServerChallenge => { + return { + ColorSequences: + output.ColorSequences != null + ? deserializeAws_restJson1ColorSequences(output.ColorSequences, context) + : undefined, + LightChallengeType: __expectString(output.LightChallengeType), + OvalScaleFactors: + output.OvalScaleFactors != null + ? deserializeAws_restJson1OvalScaleFactors(output.OvalScaleFactors, context) + : undefined, + } as any; +}; + +const deserializeAws_restJson1FreshnessColor = (output: any, context: __SerdeContext): FreshnessColor => { + return { + RGB: output.RGB != null ? deserializeAws_restJson1ColorComponentList(output.RGB, context) : undefined, + } as any; +}; + +const deserializeAws_restJson1OvalScaleFactors = (output: any, context: __SerdeContext): OvalScaleFactors => { + return { + CenterX: __limitedParseFloat32(output.CenterX), + CenterY: __limitedParseFloat32(output.CenterY), + Width: __limitedParseFloat32(output.Width), + } as any; +}; + +const deserializeAws_restJson1ServerChallenge = (output: any, context: __SerdeContext): ServerChallenge => { + if (output.FaceMovementAndLightChallenge != null) { + return { + FaceMovementAndLightChallenge: deserializeAws_restJson1FaceMovementAndLightServerChallenge( + output.FaceMovementAndLightChallenge, + context + ), + }; + } + return { $unknown: Object.entries(output)[0] }; +}; + +const deserializeAws_restJson1ServerSessionInformationEvent = ( + output: any, + context: __SerdeContext +): ServerSessionInformationEvent => { + return { + SessionInformation: + output.SessionInformation != null + ? deserializeAws_restJson1SessionInformation(output.SessionInformation, context) + : undefined, + } as any; +}; + +const deserializeAws_restJson1SessionInformation = (output: any, context: __SerdeContext): SessionInformation => { + return { + Challenge: + output.Challenge != null + ? deserializeAws_restJson1ServerChallenge(__expectUnion(output.Challenge), context) + : undefined, + } as any; +}; + +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"], +}); + +// Collect low-level response body stream to Uint8Array. +const collectBody = (streamBody: any = new Uint8Array(), context: __SerdeContext): Promise => { + if (streamBody instanceof Uint8Array) { + return Promise.resolve(streamBody); + } + return context.streamCollector(streamBody) || Promise.resolve(new Uint8Array()); +}; + +// Encode Uint8Array data into string with utf-8. +const collectBodyString = (streamBody: any, context: __SerdeContext): Promise => + collectBody(streamBody, context).then((body) => context.utf8Encoder(body)); + +const isSerializableHeaderValue = (value: any): boolean => + value !== undefined && + value !== null && + value !== "" && + (!Object.getOwnPropertyNames(value).includes("length") || value.length != 0) && + (!Object.getOwnPropertyNames(value).includes("size") || value.size != 0); + +const parseBody = (streamBody: any, context: __SerdeContext): any => + collectBodyString(streamBody, context).then((encoded) => { + if (encoded.length) { + return JSON.parse(encoded); + } + return {}; + }); + +const parseErrorBody = async (errorBody: any, context: __SerdeContext) => { + const value = await parseBody(errorBody, context); + value.message = value.message ?? value.Message; + return value; +}; + +/** + * Load an error code for the aws.rest-json-1.1 protocol. + */ +const loadRestJsonErrorCode = (output: __HttpResponse, data: any): string | undefined => { + const findKey = (object: any, key: string) => Object.keys(object).find((k) => k.toLowerCase() === key.toLowerCase()); + + 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; + }; + + const headerKey = findKey(output.headers, "x-amzn-errortype"); + if (headerKey !== undefined) { + return sanitizeErrorCode(output.headers[headerKey]); + } + + if (data.code !== undefined) { + return sanitizeErrorCode(data.code); + } + + if (data["__type"] !== undefined) { + return sanitizeErrorCode(data["__type"]); + } +}; diff --git a/clients/client-rekognitionstreaming/src/runtimeConfig.browser.ts b/clients/client-rekognitionstreaming/src/runtimeConfig.browser.ts new file mode 100644 index 000000000000..5c59bdbde6cb --- /dev/null +++ b/clients/client-rekognitionstreaming/src/runtimeConfig.browser.ts @@ -0,0 +1,53 @@ +// smithy-typescript generated code +// @ts-ignore: package.json will be imported from dist folders +import packageInfo from "../package.json"; // eslint-disable-line + +import { Sha256 } from "@aws-crypto/sha256-browser"; +import { DEFAULT_USE_DUALSTACK_ENDPOINT, DEFAULT_USE_FIPS_ENDPOINT } from "@aws-sdk/config-resolver"; +import { eventStreamSerdeProvider } from "@aws-sdk/eventstream-serde-browser"; +import { FetchHttpHandler as HttpRequestHandler, streamCollector } from "@aws-sdk/fetch-http-handler"; +import { invalidProvider } from "@aws-sdk/invalid-dependency"; +import { + WebSocketFetchHandler as WebSocketRequestHandler, + eventStreamPayloadHandlerProvider, +} from "@aws-sdk/middleware-websocket"; +import { calculateBodyLength } from "@aws-sdk/util-body-length-browser"; +import { DEFAULT_MAX_ATTEMPTS, DEFAULT_RETRY_MODE } from "@aws-sdk/util-retry"; +import { defaultUserAgent } from "@aws-sdk/util-user-agent-browser"; +import { RekognitionStreamingClientConfig } from "./RekognitionStreamingClient"; +import { getRuntimeConfig as getSharedRuntimeConfig } from "./runtimeConfig.shared"; +import { loadConfigsForDefaultMode } from "@aws-sdk/smithy-client"; +import { resolveDefaultsModeConfig } from "@aws-sdk/util-defaults-mode-browser"; + +/** + * @internal + */ +export const getRuntimeConfig = (config: RekognitionStreamingClientConfig) => { + const defaultsMode = resolveDefaultsModeConfig(config); + const defaultConfigProvider = () => defaultsMode().then(loadConfigsForDefaultMode); + const clientSharedValues = getSharedRuntimeConfig(config); + return { + ...clientSharedValues, + ...config, + runtime: "browser", + defaultsMode, + bodyLengthChecker: config?.bodyLengthChecker ?? calculateBodyLength, + credentialDefaultProvider: + config?.credentialDefaultProvider ?? ((_: unknown) => () => Promise.reject(new Error("Credential is missing"))), + defaultUserAgentProvider: + config?.defaultUserAgentProvider ?? + defaultUserAgent({ serviceId: clientSharedValues.serviceId, clientVersion: packageInfo.version }), + eventStreamPayloadHandlerProvider: config?.eventStreamPayloadHandlerProvider ?? eventStreamPayloadHandlerProvider, + eventStreamSerdeProvider: config?.eventStreamSerdeProvider ?? eventStreamSerdeProvider, + maxAttempts: config?.maxAttempts ?? DEFAULT_MAX_ATTEMPTS, + region: config?.region ?? invalidProvider("Region is missing"), + requestHandler: + config?.requestHandler ?? + new WebSocketRequestHandler(defaultConfigProvider, new HttpRequestHandler(defaultConfigProvider)), + retryMode: config?.retryMode ?? (async () => (await defaultConfigProvider()).retryMode || DEFAULT_RETRY_MODE), + sha256: config?.sha256 ?? Sha256, + streamCollector: config?.streamCollector ?? streamCollector, + useDualstackEndpoint: config?.useDualstackEndpoint ?? (() => Promise.resolve(DEFAULT_USE_DUALSTACK_ENDPOINT)), + useFipsEndpoint: config?.useFipsEndpoint ?? (() => Promise.resolve(DEFAULT_USE_FIPS_ENDPOINT)), + }; +}; diff --git a/clients/client-rekognitionstreaming/src/runtimeConfig.native.ts b/clients/client-rekognitionstreaming/src/runtimeConfig.native.ts new file mode 100644 index 000000000000..3915227e44f9 --- /dev/null +++ b/clients/client-rekognitionstreaming/src/runtimeConfig.native.ts @@ -0,0 +1,22 @@ +// smithy-typescript generated code +import { Sha256 } from "@aws-crypto/sha256-js"; +import { invalidFunction } from "@aws-sdk/invalid-dependency"; + +import { RekognitionStreamingClientConfig } from "./RekognitionStreamingClient"; +import { getRuntimeConfig as getBrowserRuntimeConfig } from "./runtimeConfig.browser"; + +/** + * @internal + */ +export const getRuntimeConfig = (config: RekognitionStreamingClientConfig) => { + const browserDefaults = getBrowserRuntimeConfig(config); + return { + ...browserDefaults, + ...config, + runtime: "react-native", + eventStreamPayloadHandlerProvider: + config?.eventStreamPayloadHandlerProvider ?? + (() => ({ handle: invalidFunction("event stream request is not supported in ReactNative.") })), + sha256: config?.sha256 ?? Sha256, + }; +}; diff --git a/clients/client-rekognitionstreaming/src/runtimeConfig.shared.ts b/clients/client-rekognitionstreaming/src/runtimeConfig.shared.ts new file mode 100644 index 000000000000..cc2da182205d --- /dev/null +++ b/clients/client-rekognitionstreaming/src/runtimeConfig.shared.ts @@ -0,0 +1,24 @@ +// smithy-typescript generated code +import { NoOpLogger } from "@aws-sdk/smithy-client"; +import { parseUrl } from "@aws-sdk/url-parser"; +import { fromBase64, toBase64 } from "@aws-sdk/util-base64"; +import { fromUtf8, toUtf8 } from "@aws-sdk/util-utf8"; + +import { defaultEndpointResolver } from "./endpoint/endpointResolver"; +import { RekognitionStreamingClientConfig } from "./RekognitionStreamingClient"; + +/** + * @internal + */ +export const getRuntimeConfig = (config: RekognitionStreamingClientConfig) => ({ + apiVersion: "2022-05-30", + base64Decoder: config?.base64Decoder ?? fromBase64, + base64Encoder: config?.base64Encoder ?? toBase64, + disableHostPrefix: config?.disableHostPrefix ?? false, + endpointProvider: config?.endpointProvider ?? defaultEndpointResolver, + logger: config?.logger ?? new NoOpLogger(), + serviceId: config?.serviceId ?? "RekognitionStreaming", + urlParser: config?.urlParser ?? parseUrl, + utf8Decoder: config?.utf8Decoder ?? fromUtf8, + utf8Encoder: config?.utf8Encoder ?? toUtf8, +}); diff --git a/clients/client-rekognitionstreaming/src/runtimeConfig.ts b/clients/client-rekognitionstreaming/src/runtimeConfig.ts new file mode 100644 index 000000000000..fed02845e871 --- /dev/null +++ b/clients/client-rekognitionstreaming/src/runtimeConfig.ts @@ -0,0 +1,63 @@ +// smithy-typescript generated code +// @ts-ignore: package.json will be imported from dist folders +import packageInfo from "../package.json"; // eslint-disable-line + +import { decorateDefaultCredentialProvider } from "@aws-sdk/client-sts"; +import { + NODE_REGION_CONFIG_FILE_OPTIONS, + NODE_REGION_CONFIG_OPTIONS, + NODE_USE_DUALSTACK_ENDPOINT_CONFIG_OPTIONS, + NODE_USE_FIPS_ENDPOINT_CONFIG_OPTIONS, +} from "@aws-sdk/config-resolver"; +import { defaultProvider as credentialDefaultProvider } from "@aws-sdk/credential-provider-node"; +import { eventStreamPayloadHandlerProvider } from "@aws-sdk/eventstream-handler-node"; +import { eventStreamSerdeProvider } from "@aws-sdk/eventstream-serde-node"; +import { Hash } from "@aws-sdk/hash-node"; +import { NODE_MAX_ATTEMPT_CONFIG_OPTIONS, NODE_RETRY_MODE_CONFIG_OPTIONS } from "@aws-sdk/middleware-retry"; +import { loadConfig as loadNodeConfig } from "@aws-sdk/node-config-provider"; +import { NodeHttpHandler as RequestHandler, streamCollector } from "@aws-sdk/node-http-handler"; +import { calculateBodyLength } from "@aws-sdk/util-body-length-node"; +import { DEFAULT_RETRY_MODE } from "@aws-sdk/util-retry"; +import { defaultUserAgent } from "@aws-sdk/util-user-agent-node"; +import { RekognitionStreamingClientConfig } from "./RekognitionStreamingClient"; +import { getRuntimeConfig as getSharedRuntimeConfig } from "./runtimeConfig.shared"; +import { loadConfigsForDefaultMode } from "@aws-sdk/smithy-client"; +import { resolveDefaultsModeConfig } from "@aws-sdk/util-defaults-mode-node"; +import { emitWarningIfUnsupportedVersion } from "@aws-sdk/smithy-client"; + +/** + * @internal + */ +export const getRuntimeConfig = (config: RekognitionStreamingClientConfig) => { + 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, + credentialDefaultProvider: + config?.credentialDefaultProvider ?? decorateDefaultCredentialProvider(credentialDefaultProvider), + defaultUserAgentProvider: + config?.defaultUserAgentProvider ?? + defaultUserAgent({ serviceId: clientSharedValues.serviceId, clientVersion: packageInfo.version }), + eventStreamPayloadHandlerProvider: config?.eventStreamPayloadHandlerProvider ?? eventStreamPayloadHandlerProvider, + eventStreamSerdeProvider: config?.eventStreamSerdeProvider ?? eventStreamSerdeProvider, + maxAttempts: config?.maxAttempts ?? loadNodeConfig(NODE_MAX_ATTEMPT_CONFIG_OPTIONS), + region: config?.region ?? loadNodeConfig(NODE_REGION_CONFIG_OPTIONS, NODE_REGION_CONFIG_FILE_OPTIONS), + requestHandler: config?.requestHandler ?? new 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, + useDualstackEndpoint: config?.useDualstackEndpoint ?? loadNodeConfig(NODE_USE_DUALSTACK_ENDPOINT_CONFIG_OPTIONS), + useFipsEndpoint: config?.useFipsEndpoint ?? loadNodeConfig(NODE_USE_FIPS_ENDPOINT_CONFIG_OPTIONS), + }; +}; diff --git a/clients/client-rekognitionstreaming/tsconfig.cjs.json b/clients/client-rekognitionstreaming/tsconfig.cjs.json new file mode 100644 index 000000000000..3567d85ba846 --- /dev/null +++ b/clients/client-rekognitionstreaming/tsconfig.cjs.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig", + "compilerOptions": { + "outDir": "dist-cjs" + } +} diff --git a/clients/client-rekognitionstreaming/tsconfig.es.json b/clients/client-rekognitionstreaming/tsconfig.es.json new file mode 100644 index 000000000000..809f57bde65e --- /dev/null +++ b/clients/client-rekognitionstreaming/tsconfig.es.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig", + "compilerOptions": { + "lib": ["dom"], + "module": "esnext", + "outDir": "dist-es" + } +} diff --git a/clients/client-rekognitionstreaming/tsconfig.json b/clients/client-rekognitionstreaming/tsconfig.json new file mode 100644 index 000000000000..344909de2128 --- /dev/null +++ b/clients/client-rekognitionstreaming/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "@tsconfig/node14/tsconfig.json", + "compilerOptions": { + "downlevelIteration": true, + "importHelpers": true, + "incremental": true, + "removeComments": true, + "resolveJsonModule": true, + "rootDir": "src", + "useUnknownInCatchVariables": false + }, + "exclude": ["test/"] +} diff --git a/clients/client-rekognitionstreaming/tsconfig.types.json b/clients/client-rekognitionstreaming/tsconfig.types.json new file mode 100644 index 000000000000..4c3dfa7b3d25 --- /dev/null +++ b/clients/client-rekognitionstreaming/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/clients/client-rekognitionstreaming/typedoc.json b/clients/client-rekognitionstreaming/typedoc.json new file mode 100644 index 000000000000..8a364aa93b69 --- /dev/null +++ b/clients/client-rekognitionstreaming/typedoc.json @@ -0,0 +1,6 @@ +{ + "extends": ["../../typedoc.client.json"], + "entryPoints": ["src/index.ts"], + "out": "docs", + "readme": "README.md" +} diff --git a/clients/client-transcribe-streaming/package.json b/clients/client-transcribe-streaming/package.json index 028b6e59e577..64a93082c178 100644 --- a/clients/client-transcribe-streaming/package.json +++ b/clients/client-transcribe-streaming/package.json @@ -44,6 +44,7 @@ "@aws-sdk/middleware-signing": "*", "@aws-sdk/middleware-stack": "*", "@aws-sdk/middleware-user-agent": "*", + "@aws-sdk/middleware-websocket": "*", "@aws-sdk/node-config-provider": "*", "@aws-sdk/node-http-handler": "*", "@aws-sdk/protocol-http": "*", @@ -72,27 +73,15 @@ "typedoc": "0.23.23", "typescript": "~4.9.5" }, - "engines": { - "node": ">=14.0.0" - }, - "typesVersions": { - "<4.0": { - "dist-types/*": [ - "dist-types/ts3.4/*" - ] - } - }, - "files": [ - "dist-*" - ], + "engines": { "node": ">=14.0.0" }, + "typesVersions": { "<4.0": { "dist-types/*": ["dist-types/ts3.4/*"] } }, + "files": ["dist-*"], "author": { "name": "AWS SDK for JavaScript Team", "url": "https://aws.amazon.com/javascript/" }, "license": "Apache-2.0", - "browser": { - "./dist-es/runtimeConfig": "./dist-es/runtimeConfig.browser" - }, + "browser": { "./dist-es/runtimeConfig": "./dist-es/runtimeConfig.browser" }, "react-native": { "./dist-es/runtimeConfig": "./dist-es/runtimeConfig.native" }, diff --git a/clients/client-transcribe-streaming/src/TranscribeStreamingClient.ts b/clients/client-transcribe-streaming/src/TranscribeStreamingClient.ts index d15ae9f1772b..3be2e832ff46 100644 --- a/clients/client-transcribe-streaming/src/TranscribeStreamingClient.ts +++ b/clients/client-transcribe-streaming/src/TranscribeStreamingClient.ts @@ -21,12 +21,7 @@ import { import { getLoggerPlugin } from "@aws-sdk/middleware-logger"; import { getRecursionDetectionPlugin } from "@aws-sdk/middleware-recursion-detection"; import { getRetryPlugin, resolveRetryConfig, RetryInputConfig, RetryResolvedConfig } from "@aws-sdk/middleware-retry"; -import { - getWebSocketPlugin, - resolveWebSocketConfig, - WebSocketInputConfig, - WebSocketResolvedConfig, -} from "@aws-sdk/middleware-sdk-transcribe-streaming"; +import { getTranscribeStreamingPlugin } from "@aws-sdk/middleware-sdk-transcribe-streaming"; import { AwsAuthInputConfig, AwsAuthResolvedConfig, @@ -39,6 +34,7 @@ import { UserAgentInputConfig, UserAgentResolvedConfig, } from "@aws-sdk/middleware-user-agent"; +import { resolveWebSocketConfig, WebSocketInputConfig, WebSocketResolvedConfig } from "@aws-sdk/middleware-websocket"; import { HttpHandler as __HttpHandler } from "@aws-sdk/protocol-http"; import { Client as __Client, @@ -339,7 +335,7 @@ export class TranscribeStreamingClient extends __Client< this.middlewareStack.use(getLoggerPlugin(this.config)); this.middlewareStack.use(getRecursionDetectionPlugin(this.config)); this.middlewareStack.use(getAwsAuthPlugin(this.config)); - this.middlewareStack.use(getWebSocketPlugin(this.config)); + this.middlewareStack.use(getTranscribeStreamingPlugin(this.config)); this.middlewareStack.use(getUserAgentPlugin(this.config)); } diff --git a/clients/client-transcribe-streaming/src/commands/StartCallAnalyticsStreamTranscriptionCommand.ts b/clients/client-transcribe-streaming/src/commands/StartCallAnalyticsStreamTranscriptionCommand.ts index 58ac295b265a..8abe42c4135f 100644 --- a/clients/client-transcribe-streaming/src/commands/StartCallAnalyticsStreamTranscriptionCommand.ts +++ b/clients/client-transcribe-streaming/src/commands/StartCallAnalyticsStreamTranscriptionCommand.ts @@ -2,6 +2,7 @@ import { EndpointParameterInstructions, getEndpointPlugin } from "@aws-sdk/middleware-endpoint"; import { getEventStreamPlugin } from "@aws-sdk/middleware-eventstream"; import { getSerdePlugin } from "@aws-sdk/middleware-serde"; +import { getWebSocketPlugin } from "@aws-sdk/middleware-websocket"; import { HttpRequest as __HttpRequest, HttpResponse as __HttpResponse } from "@aws-sdk/protocol-http"; import { Command as $Command } from "@aws-sdk/smithy-client"; import { @@ -182,6 +183,7 @@ export class StartCallAnalyticsStreamTranscriptionCommand extends $Command< getEndpointPlugin(configuration, StartCallAnalyticsStreamTranscriptionCommand.getEndpointParameterInstructions()) ); this.middlewareStack.use(getEventStreamPlugin(configuration)); + this.middlewareStack.use(getWebSocketPlugin(configuration, { headerPrefix: "x-amzn-transcribe-" })); const stack = clientStack.concat(this.middlewareStack); diff --git a/clients/client-transcribe-streaming/src/commands/StartMedicalStreamTranscriptionCommand.ts b/clients/client-transcribe-streaming/src/commands/StartMedicalStreamTranscriptionCommand.ts index 29766f7d6024..a5c98d717416 100644 --- a/clients/client-transcribe-streaming/src/commands/StartMedicalStreamTranscriptionCommand.ts +++ b/clients/client-transcribe-streaming/src/commands/StartMedicalStreamTranscriptionCommand.ts @@ -2,6 +2,7 @@ import { EndpointParameterInstructions, getEndpointPlugin } from "@aws-sdk/middleware-endpoint"; import { getEventStreamPlugin } from "@aws-sdk/middleware-eventstream"; import { getSerdePlugin } from "@aws-sdk/middleware-serde"; +import { getWebSocketPlugin } from "@aws-sdk/middleware-websocket"; import { HttpRequest as __HttpRequest, HttpResponse as __HttpResponse } from "@aws-sdk/protocol-http"; import { Command as $Command } from "@aws-sdk/smithy-client"; import { @@ -181,6 +182,7 @@ export class StartMedicalStreamTranscriptionCommand extends $Command< getEndpointPlugin(configuration, StartMedicalStreamTranscriptionCommand.getEndpointParameterInstructions()) ); this.middlewareStack.use(getEventStreamPlugin(configuration)); + this.middlewareStack.use(getWebSocketPlugin(configuration, { headerPrefix: "x-amzn-transcribe-" })); const stack = clientStack.concat(this.middlewareStack); diff --git a/clients/client-transcribe-streaming/src/commands/StartStreamTranscriptionCommand.ts b/clients/client-transcribe-streaming/src/commands/StartStreamTranscriptionCommand.ts index 0769d43adfff..4b2b56d16954 100644 --- a/clients/client-transcribe-streaming/src/commands/StartStreamTranscriptionCommand.ts +++ b/clients/client-transcribe-streaming/src/commands/StartStreamTranscriptionCommand.ts @@ -2,6 +2,7 @@ import { EndpointParameterInstructions, getEndpointPlugin } from "@aws-sdk/middleware-endpoint"; import { getEventStreamPlugin } from "@aws-sdk/middleware-eventstream"; import { getSerdePlugin } from "@aws-sdk/middleware-serde"; +import { getWebSocketPlugin } from "@aws-sdk/middleware-websocket"; import { HttpRequest as __HttpRequest, HttpResponse as __HttpResponse } from "@aws-sdk/protocol-http"; import { Command as $Command } from "@aws-sdk/smithy-client"; import { @@ -186,6 +187,7 @@ export class StartStreamTranscriptionCommand extends $Command< getEndpointPlugin(configuration, StartStreamTranscriptionCommand.getEndpointParameterInstructions()) ); this.middlewareStack.use(getEventStreamPlugin(configuration)); + this.middlewareStack.use(getWebSocketPlugin(configuration, { headerPrefix: "x-amzn-transcribe-" })); const stack = clientStack.concat(this.middlewareStack); diff --git a/clients/client-transcribe-streaming/src/runtimeConfig.browser.ts b/clients/client-transcribe-streaming/src/runtimeConfig.browser.ts index e7e8ddfed666..b030c4409567 100644 --- a/clients/client-transcribe-streaming/src/runtimeConfig.browser.ts +++ b/clients/client-transcribe-streaming/src/runtimeConfig.browser.ts @@ -5,9 +5,10 @@ import packageInfo from "../package.json"; // eslint-disable-line import { Sha256 } from "@aws-crypto/sha256-browser"; import { DEFAULT_USE_DUALSTACK_ENDPOINT, DEFAULT_USE_FIPS_ENDPOINT } from "@aws-sdk/config-resolver"; import { eventStreamSerdeProvider } from "@aws-sdk/eventstream-serde-browser"; -import { streamCollector } from "@aws-sdk/fetch-http-handler"; +import { FetchHttpHandler as HttpRequestHandler, streamCollector } from "@aws-sdk/fetch-http-handler"; import { invalidProvider } from "@aws-sdk/invalid-dependency"; -import { WebSocketHandler, eventStreamPayloadHandler } from "@aws-sdk/middleware-sdk-transcribe-streaming"; +import { eventStreamPayloadHandler } from "@aws-sdk/middleware-sdk-transcribe-streaming"; +import { WebSocketFetchHandler as WebSocketRequestHandler } from "@aws-sdk/middleware-websocket"; import { calculateBodyLength } from "@aws-sdk/util-body-length-browser"; import { DEFAULT_MAX_ATTEMPTS, DEFAULT_RETRY_MODE } from "@aws-sdk/util-retry"; import { defaultUserAgent } from "@aws-sdk/util-user-agent-browser"; @@ -38,7 +39,9 @@ export const getRuntimeConfig = (config: TranscribeStreamingClientConfig) => { eventStreamSerdeProvider: config?.eventStreamSerdeProvider ?? eventStreamSerdeProvider, maxAttempts: config?.maxAttempts ?? DEFAULT_MAX_ATTEMPTS, region: config?.region ?? invalidProvider("Region is missing"), - requestHandler: config?.requestHandler ?? new WebSocketHandler(), + requestHandler: + config?.requestHandler ?? + new WebSocketRequestHandler(defaultConfigProvider, new HttpRequestHandler(defaultConfigProvider)), retryMode: config?.retryMode ?? (async () => (await defaultConfigProvider()).retryMode || DEFAULT_RETRY_MODE), sha256: config?.sha256 ?? Sha256, streamCollector: config?.streamCollector ?? streamCollector, diff --git a/clients/client-transcribe-streaming/src/runtimeConfig.native.ts b/clients/client-transcribe-streaming/src/runtimeConfig.native.ts index ba8b3b9fb2f1..2e896b099957 100644 --- a/clients/client-transcribe-streaming/src/runtimeConfig.native.ts +++ b/clients/client-transcribe-streaming/src/runtimeConfig.native.ts @@ -1,6 +1,6 @@ // smithy-typescript generated code import { Sha256 } from "@aws-crypto/sha256-js"; -import { eventStreamPayloadHandler, WebSocketHandler } from "@aws-sdk/middleware-sdk-transcribe-streaming"; +import { eventStreamPayloadHandler } from "@aws-sdk/middleware-sdk-transcribe-streaming"; import { getRuntimeConfig as getBrowserRuntimeConfig } from "./runtimeConfig.browser"; import { TranscribeStreamingClientConfig } from "./TranscribeStreamingClient"; @@ -15,7 +15,6 @@ export const getRuntimeConfig = (config: TranscribeStreamingClientConfig) => { ...config, runtime: "react-native", eventStreamPayloadHandlerProvider: config?.eventStreamPayloadHandlerProvider ?? (() => eventStreamPayloadHandler), - requestHandler: config?.requestHandler ?? new WebSocketHandler(), sha256: config?.sha256 ?? Sha256, }; }; diff --git a/codegen/sdk-codegen/aws-models/rekognitionstreaming.json b/codegen/sdk-codegen/aws-models/rekognitionstreaming.json new file mode 100644 index 000000000000..12e083a07149 --- /dev/null +++ b/codegen/sdk-codegen/aws-models/rekognitionstreaming.json @@ -0,0 +1,1329 @@ +{ + "smithy": "2.0", + "metadata": { + "suppressions": [ + { + "id": "HttpMethodSemantics", + "namespace": "*" + }, + { + "id": "HttpResponseCodeSemantics", + "namespace": "*" + }, + { + "id": "PaginatedTrait", + "namespace": "*" + }, + { + "id": "HttpHeaderTrait", + "namespace": "*" + }, + { + "id": "HttpUriConflict", + "namespace": "*" + }, + { + "id": "Service", + "namespace": "*" + } + ] + }, + "shapes": { + "com.amazonaws.rekognitionstreaming#AccessDeniedException": { + "type": "structure", + "members": { + "Message": { + "target": "com.amazonaws.rekognitionstreaming#String" + }, + "Code": { + "target": "com.amazonaws.rekognitionstreaming#String" + } + }, + "traits": { + "smithy.api#error": "client", + "smithy.api#httpError": 403 + } + }, + "com.amazonaws.rekognitionstreaming#BoundingBox": { + "type": "structure", + "members": { + "Width": { + "target": "com.amazonaws.rekognitionstreaming#Float", + "traits": { + "smithy.api#required": {} + } + }, + "Height": { + "target": "com.amazonaws.rekognitionstreaming#Float", + "traits": { + "smithy.api#required": {} + } + }, + "Left": { + "target": "com.amazonaws.rekognitionstreaming#Float", + "traits": { + "smithy.api#required": {} + } + }, + "Top": { + "target": "com.amazonaws.rekognitionstreaming#Float", + "traits": { + "smithy.api#required": {} + } + } + } + }, + "com.amazonaws.rekognitionstreaming#ClientChallenge": { + "type": "union", + "members": { + "FaceMovementAndLightChallenge": { + "target": "com.amazonaws.rekognitionstreaming#FaceMovementAndLightClientChallenge" + } + } + }, + "com.amazonaws.rekognitionstreaming#ClientSessionInformationEvent": { + "type": "structure", + "members": { + "DeviceInformation": { + "target": "com.amazonaws.rekognitionstreaming#DeviceInformation", + "traits": { + "smithy.api#required": {} + } + }, + "Challenge": { + "target": "com.amazonaws.rekognitionstreaming#ClientChallenge", + "traits": { + "smithy.api#required": {} + } + } + } + }, + "com.amazonaws.rekognitionstreaming#ColorComponentInt": { + "type": "integer", + "traits": { + "smithy.api#range": { + "min": 0, + "max": 255 + } + } + }, + "com.amazonaws.rekognitionstreaming#ColorComponentList": { + "type": "list", + "member": { + "target": "com.amazonaws.rekognitionstreaming#ColorComponentInt" + }, + "traits": { + "smithy.api#length": { + "min": 3, + "max": 3 + } + } + }, + "com.amazonaws.rekognitionstreaming#ColorDisplayed": { + "type": "structure", + "members": { + "CurrentColor": { + "target": "com.amazonaws.rekognitionstreaming#FreshnessColor", + "traits": { + "smithy.api#required": {} + } + }, + "PreviousColor": { + "target": "com.amazonaws.rekognitionstreaming#FreshnessColor" + }, + "SequenceNumber": { + "target": "com.amazonaws.rekognitionstreaming#ColorSequenceInt", + "traits": { + "smithy.api#required": {} + } + }, + "CurrentColorStartTimestamp": { + "target": "com.amazonaws.rekognitionstreaming#ULong", + "traits": { + "smithy.api#required": {} + } + } + } + }, + "com.amazonaws.rekognitionstreaming#ColorSequence": { + "type": "structure", + "members": { + "FreshnessColor": { + "target": "com.amazonaws.rekognitionstreaming#FreshnessColor", + "traits": { + "smithy.api#required": {} + } + }, + "DownscrollDuration": { + "target": "com.amazonaws.rekognitionstreaming#Float", + "traits": { + "smithy.api#required": {} + } + }, + "FlatDisplayDuration": { + "target": "com.amazonaws.rekognitionstreaming#Float", + "traits": { + "smithy.api#required": {} + } + } + } + }, + "com.amazonaws.rekognitionstreaming#ColorSequenceInt": { + "type": "integer", + "traits": { + "smithy.api#range": { + "min": 0, + "max": 50 + } + } + }, + "com.amazonaws.rekognitionstreaming#ColorSequences": { + "type": "list", + "member": { + "target": "com.amazonaws.rekognitionstreaming#ColorSequence" + } + }, + "com.amazonaws.rekognitionstreaming#DeviceInformation": { + "type": "structure", + "members": { + "VideoHeight": { + "target": "com.amazonaws.rekognitionstreaming#Float", + "traits": { + "smithy.api#required": {} + } + }, + "VideoWidth": { + "target": "com.amazonaws.rekognitionstreaming#Float", + "traits": { + "smithy.api#required": {} + } + }, + "ClientSDKVersion": { + "target": "com.amazonaws.rekognitionstreaming#String", + "traits": { + "smithy.api#required": {} + } + } + } + }, + "com.amazonaws.rekognitionstreaming#DisconnectionEvent": { + "type": "structure", + "members": { + "TimestampMillis": { + "target": "com.amazonaws.rekognitionstreaming#ULong", + "traits": { + "smithy.api#required": {} + } + } + } + }, + "com.amazonaws.rekognitionstreaming#FaceMovementAndLightClientChallenge": { + "type": "structure", + "members": { + "ChallengeId": { + "target": "com.amazonaws.rekognitionstreaming#UUID", + "traits": { + "smithy.api#required": {} + } + }, + "VideoStartTimestamp": { + "target": "com.amazonaws.rekognitionstreaming#ULong" + }, + "InitialFace": { + "target": "com.amazonaws.rekognitionstreaming#InitialFace" + }, + "TargetFace": { + "target": "com.amazonaws.rekognitionstreaming#TargetFace" + }, + "ColorDisplayed": { + "target": "com.amazonaws.rekognitionstreaming#ColorDisplayed" + } + } + }, + "com.amazonaws.rekognitionstreaming#FaceMovementAndLightServerChallenge": { + "type": "structure", + "members": { + "OvalScaleFactors": { + "target": "com.amazonaws.rekognitionstreaming#OvalScaleFactors", + "traits": { + "smithy.api#required": {} + } + }, + "LightChallengeType": { + "target": "com.amazonaws.rekognitionstreaming#LightChallengeType", + "traits": { + "smithy.api#required": {} + } + }, + "ColorSequences": { + "target": "com.amazonaws.rekognitionstreaming#ColorSequences", + "traits": { + "smithy.api#required": {} + } + } + } + }, + "com.amazonaws.rekognitionstreaming#Float": { + "type": "float" + }, + "com.amazonaws.rekognitionstreaming#FreshnessColor": { + "type": "structure", + "members": { + "RGB": { + "target": "com.amazonaws.rekognitionstreaming#ColorComponentList", + "traits": { + "smithy.api#required": {} + } + } + } + }, + "com.amazonaws.rekognitionstreaming#InitialFace": { + "type": "structure", + "members": { + "BoundingBox": { + "target": "com.amazonaws.rekognitionstreaming#BoundingBox", + "traits": { + "smithy.api#required": {} + } + }, + "InitialFaceDetectedTimestamp": { + "target": "com.amazonaws.rekognitionstreaming#ULong", + "traits": { + "smithy.api#required": {} + } + } + } + }, + "com.amazonaws.rekognitionstreaming#InternalServerException": { + "type": "structure", + "members": { + "Message": { + "target": "com.amazonaws.rekognitionstreaming#String" + }, + "Code": { + "target": "com.amazonaws.rekognitionstreaming#String" + } + }, + "traits": { + "smithy.api#error": "server", + "smithy.api#httpError": 500 + } + }, + "com.amazonaws.rekognitionstreaming#LightChallengeType": { + "type": "enum", + "members": { + "SEQUENTIAL": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "SEQUENTIAL" + } + }, + "SIMULTANEOUS": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "SIMULTANEOUS" + } + } + } + }, + "com.amazonaws.rekognitionstreaming#LivenessRequestStream": { + "type": "union", + "members": { + "VideoEvent": { + "target": "com.amazonaws.rekognitionstreaming#VideoEvent" + }, + "ClientSessionInformationEvent": { + "target": "com.amazonaws.rekognitionstreaming#ClientSessionInformationEvent" + } + }, + "traits": { + "smithy.api#streaming": {} + } + }, + "com.amazonaws.rekognitionstreaming#LivenessResponseStream": { + "type": "union", + "members": { + "ServerSessionInformationEvent": { + "target": "com.amazonaws.rekognitionstreaming#ServerSessionInformationEvent" + }, + "DisconnectionEvent": { + "target": "com.amazonaws.rekognitionstreaming#DisconnectionEvent" + }, + "ValidationException": { + "target": "com.amazonaws.rekognitionstreaming#ValidationException" + }, + "InternalServerException": { + "target": "com.amazonaws.rekognitionstreaming#InternalServerException" + }, + "ThrottlingException": { + "target": "com.amazonaws.rekognitionstreaming#ThrottlingException" + }, + "ServiceQuotaExceededException": { + "target": "com.amazonaws.rekognitionstreaming#ServiceQuotaExceededException" + } + }, + "traits": { + "smithy.api#streaming": {} + } + }, + "com.amazonaws.rekognitionstreaming#OvalScaleFactors": { + "type": "structure", + "members": { + "Width": { + "target": "com.amazonaws.rekognitionstreaming#Float", + "traits": { + "smithy.api#required": {} + } + }, + "CenterX": { + "target": "com.amazonaws.rekognitionstreaming#Float", + "traits": { + "smithy.api#required": {} + } + }, + "CenterY": { + "target": "com.amazonaws.rekognitionstreaming#Float", + "traits": { + "smithy.api#required": {} + } + } + } + }, + "com.amazonaws.rekognitionstreaming#RekognitionStreamingService": { + "type": "service", + "version": "2022-05-30", + "operations": [ + { + "target": "com.amazonaws.rekognitionstreaming#StartFaceLivenessSession" + }, + { + "target": "com.amazonaws.rekognitionstreaming#StartStreamingLivenessSession" + } + ], + "traits": { + "aws.api#service": { + "sdkId": "RekognitionStreaming", + "arnNamespace": "rekognition", + "cloudFormationName": "RekognitionStreaming", + "cloudTrailEventSource": "rekognitionstreaming.amazonaws.com", + "endpointPrefix": "rekognitionstreaming" + }, + "aws.auth#sigv4": { + "name": "rekognition" + }, + "aws.protocols#restJson1": {}, + "smithy.api#title": "AWS Rekognition Streaming", + "smithy.rules#endpointRuleSet": { + "version": "1.0", + "parameters": { + "Region": { + "builtIn": "AWS::Region", + "required": false, + "documentation": "The AWS region used to dispatch the request.", + "type": "String" + }, + "UseDualStack": { + "builtIn": "AWS::UseDualStack", + "required": true, + "default": false, + "documentation": "When true, use the dual-stack endpoint. If the configured endpoint does not support dual-stack, dispatching the request MAY return an error.", + "type": "Boolean" + }, + "UseFIPS": { + "builtIn": "AWS::UseFIPS", + "required": true, + "default": false, + "documentation": "When true, send this request to the FIPS-compliant regional endpoint. If the configured endpoint does not have a FIPS compliant endpoint, dispatching the request will return an error.", + "type": "Boolean" + }, + "Endpoint": { + "builtIn": "SDK::Endpoint", + "required": false, + "documentation": "Override the endpoint used to send this request", + "type": "String" + } + }, + "rules": [ + { + "conditions": [ + { + "fn": "aws.partition", + "argv": [ + { + "ref": "Region" + } + ], + "assign": "PartitionResult" + } + ], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "isSet", + "argv": [ + { + "ref": "Endpoint" + } + ] + }, + { + "fn": "parseURL", + "argv": [ + { + "ref": "Endpoint" + } + ], + "assign": "url" + } + ], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseFIPS" + }, + true + ] + } + ], + "error": "Invalid Configuration: FIPS and custom endpoint are not supported", + "type": "error" + }, + { + "conditions": [], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseDualStack" + }, + true + ] + } + ], + "error": "Invalid Configuration: Dualstack and custom endpoint are not supported", + "type": "error" + }, + { + "conditions": [], + "endpoint": { + "url": { + "ref": "Endpoint" + }, + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + } + ] + }, + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseFIPS" + }, + true + ] + }, + { + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseDualStack" + }, + true + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + true, + { + "fn": "getAttr", + "argv": [ + { + "ref": "PartitionResult" + }, + "supportsFIPS" + ] + } + ] + }, + { + "fn": "booleanEquals", + "argv": [ + true, + { + "fn": "getAttr", + "argv": [ + { + "ref": "PartitionResult" + }, + "supportsDualStack" + ] + } + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [], + "endpoint": { + "url": "https://rekognitionstreaming-fips.{Region}.{PartitionResult#dualStackDnsSuffix}", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + }, + { + "conditions": [], + "error": "FIPS and DualStack are enabled, but this partition does not support one or both", + "type": "error" + } + ] + }, + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseFIPS" + }, + true + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + true, + { + "fn": "getAttr", + "argv": [ + { + "ref": "PartitionResult" + }, + "supportsFIPS" + ] + } + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [], + "endpoint": { + "url": "https://rekognitionstreaming-fips.{Region}.{PartitionResult#dnsSuffix}", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + }, + { + "conditions": [], + "error": "FIPS is enabled but this partition does not support FIPS", + "type": "error" + } + ] + }, + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseDualStack" + }, + true + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + true, + { + "fn": "getAttr", + "argv": [ + { + "ref": "PartitionResult" + }, + "supportsDualStack" + ] + } + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [], + "endpoint": { + "url": "https://rekognitionstreaming.{Region}.{PartitionResult#dualStackDnsSuffix}", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + }, + { + "conditions": [], + "error": "DualStack is enabled but this partition does not support DualStack", + "type": "error" + } + ] + }, + { + "conditions": [], + "endpoint": { + "url": "https://rekognitionstreaming.{Region}.{PartitionResult#dnsSuffix}", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + } + ] + }, + "smithy.rules#endpointTests": { + "testCases": [ + { + "documentation": "For region us-gov-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "endpoint": { + "url": "https://rekognitionstreaming-fips.us-gov-east-1.api.aws" + } + }, + "params": { + "UseDualStack": true, + "UseFIPS": true, + "Region": "us-gov-east-1" + } + }, + { + "documentation": "For region us-gov-east-1 with FIPS enabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://rekognitionstreaming-fips.us-gov-east-1.amazonaws.com" + } + }, + "params": { + "UseDualStack": false, + "UseFIPS": true, + "Region": "us-gov-east-1" + } + }, + { + "documentation": "For region us-gov-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "endpoint": { + "url": "https://rekognitionstreaming.us-gov-east-1.api.aws" + } + }, + "params": { + "UseDualStack": true, + "UseFIPS": false, + "Region": "us-gov-east-1" + } + }, + { + "documentation": "For region us-gov-east-1 with FIPS disabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://rekognitionstreaming.us-gov-east-1.amazonaws.com" + } + }, + "params": { + "UseDualStack": false, + "UseFIPS": false, + "Region": "us-gov-east-1" + } + }, + { + "documentation": "For region cn-north-1 with FIPS enabled and DualStack enabled", + "expect": { + "endpoint": { + "url": "https://rekognitionstreaming-fips.cn-north-1.api.amazonwebservices.com.cn" + } + }, + "params": { + "UseDualStack": true, + "UseFIPS": true, + "Region": "cn-north-1" + } + }, + { + "documentation": "For region cn-north-1 with FIPS enabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://rekognitionstreaming-fips.cn-north-1.amazonaws.com.cn" + } + }, + "params": { + "UseDualStack": false, + "UseFIPS": true, + "Region": "cn-north-1" + } + }, + { + "documentation": "For region cn-north-1 with FIPS disabled and DualStack enabled", + "expect": { + "endpoint": { + "url": "https://rekognitionstreaming.cn-north-1.api.amazonwebservices.com.cn" + } + }, + "params": { + "UseDualStack": true, + "UseFIPS": false, + "Region": "cn-north-1" + } + }, + { + "documentation": "For region cn-north-1 with FIPS disabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://rekognitionstreaming.cn-north-1.amazonaws.com.cn" + } + }, + "params": { + "UseDualStack": false, + "UseFIPS": false, + "Region": "cn-north-1" + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "error": "FIPS and DualStack are enabled, but this partition does not support one or both" + }, + "params": { + "UseDualStack": true, + "UseFIPS": true, + "Region": "us-iso-east-1" + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS enabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://rekognitionstreaming-fips.us-iso-east-1.c2s.ic.gov" + } + }, + "params": { + "UseDualStack": false, + "UseFIPS": true, + "Region": "us-iso-east-1" + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "error": "DualStack is enabled but this partition does not support DualStack" + }, + "params": { + "UseDualStack": true, + "UseFIPS": false, + "Region": "us-iso-east-1" + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS disabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://rekognitionstreaming.us-iso-east-1.c2s.ic.gov" + } + }, + "params": { + "UseDualStack": false, + "UseFIPS": false, + "Region": "us-iso-east-1" + } + }, + { + "documentation": "For region us-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "endpoint": { + "url": "https://rekognitionstreaming-fips.us-east-1.api.aws" + } + }, + "params": { + "UseDualStack": true, + "UseFIPS": true, + "Region": "us-east-1" + } + }, + { + "documentation": "For region us-east-1 with FIPS enabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://rekognitionstreaming-fips.us-east-1.amazonaws.com" + } + }, + "params": { + "UseDualStack": false, + "UseFIPS": true, + "Region": "us-east-1" + } + }, + { + "documentation": "For region us-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "endpoint": { + "url": "https://rekognitionstreaming.us-east-1.api.aws" + } + }, + "params": { + "UseDualStack": true, + "UseFIPS": false, + "Region": "us-east-1" + } + }, + { + "documentation": "For region us-east-1 with FIPS disabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://rekognitionstreaming.us-east-1.amazonaws.com" + } + }, + "params": { + "UseDualStack": false, + "UseFIPS": false, + "Region": "us-east-1" + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "error": "FIPS and DualStack are enabled, but this partition does not support one or both" + }, + "params": { + "UseDualStack": true, + "UseFIPS": true, + "Region": "us-isob-east-1" + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS enabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://rekognitionstreaming-fips.us-isob-east-1.sc2s.sgov.gov" + } + }, + "params": { + "UseDualStack": false, + "UseFIPS": true, + "Region": "us-isob-east-1" + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "error": "DualStack is enabled but this partition does not support DualStack" + }, + "params": { + "UseDualStack": true, + "UseFIPS": false, + "Region": "us-isob-east-1" + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS disabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://rekognitionstreaming.us-isob-east-1.sc2s.sgov.gov" + } + }, + "params": { + "UseDualStack": false, + "UseFIPS": false, + "Region": "us-isob-east-1" + } + }, + { + "documentation": "For custom endpoint with fips disabled and dualstack disabled", + "expect": { + "endpoint": { + "url": "https://example.com" + } + }, + "params": { + "UseDualStack": false, + "UseFIPS": false, + "Region": "us-east-1", + "Endpoint": "https://example.com" + } + }, + { + "documentation": "For custom endpoint with fips enabled and dualstack disabled", + "expect": { + "error": "Invalid Configuration: FIPS and custom endpoint are not supported" + }, + "params": { + "UseDualStack": false, + "UseFIPS": true, + "Region": "us-east-1", + "Endpoint": "https://example.com" + } + }, + { + "documentation": "For custom endpoint with fips disabled and dualstack enabled", + "expect": { + "error": "Invalid Configuration: Dualstack and custom endpoint are not supported" + }, + "params": { + "UseDualStack": true, + "UseFIPS": false, + "Region": "us-east-1", + "Endpoint": "https://example.com" + } + } + ], + "version": "1.0" + } + } + }, + "com.amazonaws.rekognitionstreaming#ServerChallenge": { + "type": "union", + "members": { + "FaceMovementAndLightChallenge": { + "target": "com.amazonaws.rekognitionstreaming#FaceMovementAndLightServerChallenge" + } + } + }, + "com.amazonaws.rekognitionstreaming#ServerSessionInformationEvent": { + "type": "structure", + "members": { + "SessionInformation": { + "target": "com.amazonaws.rekognitionstreaming#SessionInformation", + "traits": { + "smithy.api#required": {} + } + } + } + }, + "com.amazonaws.rekognitionstreaming#ServiceQuotaExceededException": { + "type": "structure", + "members": { + "Message": { + "target": "com.amazonaws.rekognitionstreaming#String" + }, + "Code": { + "target": "com.amazonaws.rekognitionstreaming#String" + } + }, + "traits": { + "smithy.api#error": "client", + "smithy.api#httpError": 429 + } + }, + "com.amazonaws.rekognitionstreaming#SessionInformation": { + "type": "structure", + "members": { + "Challenge": { + "target": "com.amazonaws.rekognitionstreaming#ServerChallenge", + "traits": { + "smithy.api#required": {} + } + } + } + }, + "com.amazonaws.rekognitionstreaming#SessionNotFoundException": { + "type": "structure", + "members": { + "Message": { + "target": "com.amazonaws.rekognitionstreaming#String" + }, + "Code": { + "target": "com.amazonaws.rekognitionstreaming#String" + } + }, + "traits": { + "smithy.api#error": "client", + "smithy.api#httpError": 400 + } + }, + "com.amazonaws.rekognitionstreaming#StartFaceLivenessSession": { + "type": "operation", + "input": { + "target": "com.amazonaws.rekognitionstreaming#StartFaceLivenessSessionRequest" + }, + "output": { + "target": "com.amazonaws.rekognitionstreaming#StartFaceLivenessSessionResponse" + }, + "errors": [ + { + "target": "com.amazonaws.rekognitionstreaming#AccessDeniedException" + }, + { + "target": "com.amazonaws.rekognitionstreaming#InternalServerException" + }, + { + "target": "com.amazonaws.rekognitionstreaming#ServiceQuotaExceededException" + }, + { + "target": "com.amazonaws.rekognitionstreaming#SessionNotFoundException" + }, + { + "target": "com.amazonaws.rekognitionstreaming#ThrottlingException" + }, + { + "target": "com.amazonaws.rekognitionstreaming#ValidationException" + } + ], + "traits": { + "smithy.api#http": { + "method": "POST", + "uri": "/start-face-liveness-session", + "code": 200 + } + } + }, + "com.amazonaws.rekognitionstreaming#StartFaceLivenessSessionRequest": { + "type": "structure", + "members": { + "SessionId": { + "target": "com.amazonaws.rekognitionstreaming#UUID", + "traits": { + "smithy.api#httpHeader": "x-amz-rekognition-streaming-liveness-session-id", + "smithy.api#required": {} + } + }, + "ClientSDKVersion": { + "target": "com.amazonaws.rekognitionstreaming#String", + "traits": { + "smithy.api#httpHeader": "x-amz-rekognition-streaming-liveness-client-sdk-version", + "smithy.api#required": {} + } + }, + "LivenessRequestStream": { + "target": "com.amazonaws.rekognitionstreaming#LivenessRequestStream", + "traits": { + "smithy.api#httpPayload": {} + } + } + } + }, + "com.amazonaws.rekognitionstreaming#StartFaceLivenessSessionResponse": { + "type": "structure", + "members": { + "SessionId": { + "target": "com.amazonaws.rekognitionstreaming#UUID", + "traits": { + "smithy.api#httpHeader": "x-amz-rekognition-streaming-liveness-session-id", + "smithy.api#required": {} + } + }, + "LivenessResponseStream": { + "target": "com.amazonaws.rekognitionstreaming#LivenessResponseStream", + "traits": { + "smithy.api#httpPayload": {} + } + } + } + }, + "com.amazonaws.rekognitionstreaming#StartStreamingLivenessSession": { + "type": "operation", + "input": { + "target": "com.amazonaws.rekognitionstreaming#StartStreamingLivenessSessionRequest" + }, + "output": { + "target": "com.amazonaws.rekognitionstreaming#StartStreamingLivenessSessionResponse" + }, + "errors": [ + { + "target": "com.amazonaws.rekognitionstreaming#AccessDeniedException" + }, + { + "target": "com.amazonaws.rekognitionstreaming#InternalServerException" + }, + { + "target": "com.amazonaws.rekognitionstreaming#ServiceQuotaExceededException" + }, + { + "target": "com.amazonaws.rekognitionstreaming#SessionNotFoundException" + }, + { + "target": "com.amazonaws.rekognitionstreaming#ThrottlingException" + }, + { + "target": "com.amazonaws.rekognitionstreaming#ValidationException" + } + ], + "traits": { + "smithy.api#http": { + "method": "POST", + "uri": "/start-streaming-liveness-session", + "code": 200 + } + } + }, + "com.amazonaws.rekognitionstreaming#StartStreamingLivenessSessionRequest": { + "type": "structure", + "members": { + "SessionId": { + "target": "com.amazonaws.rekognitionstreaming#UUID", + "traits": { + "smithy.api#httpHeader": "x-amz-rekognition-streaming-liveness-session-id", + "smithy.api#required": {} + } + }, + "ClientSDKVersion": { + "target": "com.amazonaws.rekognitionstreaming#String", + "traits": { + "smithy.api#httpHeader": "x-amz-rekognition-streaming-liveness-client-sdk-version", + "smithy.api#required": {} + } + }, + "LivenessRequestStream": { + "target": "com.amazonaws.rekognitionstreaming#LivenessRequestStream", + "traits": { + "smithy.api#httpPayload": {} + } + } + } + }, + "com.amazonaws.rekognitionstreaming#StartStreamingLivenessSessionResponse": { + "type": "structure", + "members": { + "SessionId": { + "target": "com.amazonaws.rekognitionstreaming#UUID", + "traits": { + "smithy.api#httpHeader": "x-amz-rekognition-streaming-liveness-session-id", + "smithy.api#required": {} + } + }, + "LivenessResponseStream": { + "target": "com.amazonaws.rekognitionstreaming#LivenessResponseStream", + "traits": { + "smithy.api#httpPayload": {} + } + } + } + }, + "com.amazonaws.rekognitionstreaming#String": { + "type": "string" + }, + "com.amazonaws.rekognitionstreaming#TargetFace": { + "type": "structure", + "members": { + "BoundingBox": { + "target": "com.amazonaws.rekognitionstreaming#BoundingBox", + "traits": { + "smithy.api#required": {} + } + }, + "FaceDetectedInTargetPositionStartTimestamp": { + "target": "com.amazonaws.rekognitionstreaming#ULong", + "traits": { + "smithy.api#required": {} + } + }, + "FaceDetectedInTargetPositionEndTimestamp": { + "target": "com.amazonaws.rekognitionstreaming#ULong", + "traits": { + "smithy.api#required": {} + } + } + } + }, + "com.amazonaws.rekognitionstreaming#ThrottlingException": { + "type": "structure", + "members": { + "Message": { + "target": "com.amazonaws.rekognitionstreaming#String" + }, + "Code": { + "target": "com.amazonaws.rekognitionstreaming#String" + } + }, + "traits": { + "smithy.api#error": "client", + "smithy.api#httpError": 429 + } + }, + "com.amazonaws.rekognitionstreaming#ULong": { + "type": "long", + "traits": { + "smithy.api#range": { + "min": 0 + } + } + }, + "com.amazonaws.rekognitionstreaming#UUID": { + "type": "string", + "traits": { + "smithy.api#length": { + "min": 36, + "max": 36 + }, + "smithy.api#pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$" + } + }, + "com.amazonaws.rekognitionstreaming#ValidationException": { + "type": "structure", + "members": { + "Message": { + "target": "com.amazonaws.rekognitionstreaming#String" + }, + "Code": { + "target": "com.amazonaws.rekognitionstreaming#String" + } + }, + "traits": { + "smithy.api#error": "client", + "smithy.api#httpError": 400 + } + }, + "com.amazonaws.rekognitionstreaming#VideoChunk": { + "type": "blob" + }, + "com.amazonaws.rekognitionstreaming#VideoEvent": { + "type": "structure", + "members": { + "VideoChunk": { + "target": "com.amazonaws.rekognitionstreaming#VideoChunk" + }, + "TimestampMillis": { + "target": "com.amazonaws.rekognitionstreaming#ULong" + } + } + } + } +} diff --git a/codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AddEventStreamHandlingDependency.java b/codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AddEventStreamHandlingDependency.java index d776f3ed05cc..8c784d22b5a0 100644 --- a/codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AddEventStreamHandlingDependency.java +++ b/codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AddEventStreamHandlingDependency.java @@ -99,9 +99,10 @@ public Map> getRuntimeConfigWriters( }); case BROWSER: /** - * Browser doesn't support streaming requests as of March 2020. + * Browser doesn't support streaming requests as of Aug 2022. * Each service client needs to support eventstream request in browser individually. - * Services like TranscribeStreaming support it via WebSocket. + * Services like TranscribeStreaming and Rekognition supports it via WebSocket. + * See the websocket customization in AddWebsocketPlugin. */ return MapUtils.of("eventStreamPayloadHandlerProvider", writer -> { writer.addDependency(TypeScriptDependency.INVALID_DEPENDENCY); diff --git a/codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AddTranscribeStreamingDependency.java b/codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AddTranscribeStreamingDependency.java index b9dd68984ca9..41f5101c40d0 100644 --- a/codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AddTranscribeStreamingDependency.java +++ b/codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AddTranscribeStreamingDependency.java @@ -43,8 +43,8 @@ public List getClientPlugins() { return ListUtils.of( RuntimeClientPlugin.builder() .withConventions(AwsDependency.TRANSCRIBE_STREAMING_MIDDLEWARE.dependency, - "WebSocket") - .servicePredicate((m, s) -> AddTranscribeStreamingDependency.isTranscribeStreaming(s)) + "TranscribeStreaming", RuntimeClientPlugin.Convention.HAS_MIDDLEWARE) + .servicePredicate((m, s) -> isTranscribeStreaming(s)) .build() ); } @@ -67,12 +67,6 @@ public Map> getRuntimeConfigWriters( writer.addImport("eventStreamPayloadHandler", "eventStreamPayloadHandler", AwsDependency.TRANSCRIBE_STREAMING_MIDDLEWARE.packageName); writer.write("(() => eventStreamPayloadHandler)"); - }, - "requestHandler", writer -> { - writer.addDependency(AwsDependency.TRANSCRIBE_STREAMING_MIDDLEWARE); - writer.addImport("WebSocketHandler", "WebSocketHandler", - AwsDependency.TRANSCRIBE_STREAMING_MIDDLEWARE.packageName); - writer.write("new WebSocketHandler()"); }); switch (target) { diff --git a/codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AddWebsocketPlugin.java b/codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AddWebsocketPlugin.java new file mode 100644 index 000000000000..c68cbe151f89 --- /dev/null +++ b/codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AddWebsocketPlugin.java @@ -0,0 +1,124 @@ +/* + * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package software.amazon.smithy.aws.typescript.codegen; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Consumer; +import software.amazon.smithy.aws.traits.ServiceTrait; +import software.amazon.smithy.codegen.core.CodegenException; +import software.amazon.smithy.codegen.core.SymbolProvider; +import software.amazon.smithy.model.Model; +import software.amazon.smithy.model.knowledge.EventStreamIndex; +import software.amazon.smithy.model.shapes.OperationShape; +import software.amazon.smithy.model.shapes.ServiceShape; +import software.amazon.smithy.typescript.codegen.LanguageTarget; +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.RuntimeClientPlugin; +import software.amazon.smithy.typescript.codegen.integration.TypeScriptIntegration; +import software.amazon.smithy.utils.ListUtils; +import software.amazon.smithy.utils.MapUtils; +import software.amazon.smithy.utils.SetUtils; +import software.amazon.smithy.utils.SmithyInternalApi; + +/** + * Add client plugins and configs to support WebSocket streaming. Services like Transcribe Streaming requires extra + * customization. + **/ +@SmithyInternalApi +public class AddWebsocketPlugin implements TypeScriptIntegration { + @Override + public List getClientPlugins() { + return ListUtils.of( + RuntimeClientPlugin.builder() + .withConventions(AwsDependency.MIDDLEWARE_WEBSOCKET.dependency, + "WebSocket", RuntimeClientPlugin.Convention.HAS_MIDDLEWARE) + .additionalPluginFunctionParamsSupplier((m, s, o) -> getPluginFunctionParams(m, s, o)) + .operationPredicate((m, s, o) -> isWebsocketSupported(s) && hasEventStreamRequest(m, o)) + .build(), + RuntimeClientPlugin.builder() + .withConventions(AwsDependency.MIDDLEWARE_WEBSOCKET.dependency, "WebSocket", + RuntimeClientPlugin.Convention.HAS_CONFIG) + .servicePredicate((m, s) -> isWebsocketSupported(s)) + .build() + ); + } + + @Override + public Map> getRuntimeConfigWriters( + TypeScriptSettings settings, + Model model, + SymbolProvider symbolProvider, + LanguageTarget target + ) { + ServiceShape service = settings.getService(model); + if (!isWebsocketSupported(service)) { + return Collections.emptyMap(); + } + switch (target) { + case BROWSER: + return MapUtils.of( + "eventStreamPayloadHandlerProvider", writer -> { + writer.addDependency(AwsDependency.MIDDLEWARE_WEBSOCKET); + writer.addImport("eventStreamPayloadHandlerProvider", "eventStreamPayloadHandlerProvider", + AwsDependency.MIDDLEWARE_WEBSOCKET.packageName); + writer.write("eventStreamPayloadHandlerProvider"); + }, + "requestHandler", writer -> { + writer.addImport("FetchHttpHandler", "HttpRequestHandler", + TypeScriptDependency.AWS_SDK_FETCH_HTTP_HANDLER.packageName); + writer.addDependency(TypeScriptDependency.AWS_SDK_FETCH_HTTP_HANDLER); + writer.addImport("WebSocketFetchHandler", "WebSocketRequestHandler", + AwsDependency.MIDDLEWARE_WEBSOCKET.packageName); + writer.addDependency(AwsDependency.MIDDLEWARE_WEBSOCKET); + writer.write("new WebSocketRequestHandler(defaultConfigProvider, " + + "new HttpRequestHandler(defaultConfigProvider))"); + }); + default: + return Collections.emptyMap(); + } + } + + private static boolean isWebsocketSupported(ServiceShape service) { + Set websocketServices = SetUtils.of("Transcribe Streaming", "RekognitionStreaming"); + String serviceId = service.getTrait(ServiceTrait.class).map(ServiceTrait::getSdkId).orElse(""); + return websocketServices.contains(serviceId); + } + + private static boolean hasEventStreamRequest(Model model, OperationShape operation) { + EventStreamIndex eventStreamIndex = EventStreamIndex.of(model); + return eventStreamIndex.getInputInfo(operation).isPresent(); + } + + private static Map getPluginFunctionParams( + Model model, + ServiceShape service, + OperationShape operation + ) { + String serviceId = service.getTrait(ServiceTrait.class).map(ServiceTrait::getSdkId).orElse(""); + if (serviceId.equals("Transcribe Streaming")) { + return MapUtils.of("headerPrefix", "x-amzn-transcribe-"); + } else if (serviceId.equals("RekognitionStreaming")) { + return MapUtils.of("headerPrefix", "x-amz-rekognition-streaming-liveness-"); + } else { + throw new CodegenException("Missing endpoint prefix for Websocket plugin of service " + serviceId); + } + } +} diff --git a/codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AwsDependency.java b/codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AwsDependency.java index 4f1172beebcb..d9bae758452f 100644 --- a/codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AwsDependency.java +++ b/codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AwsDependency.java @@ -73,6 +73,7 @@ public enum AwsDependency implements SymbolDependencyContainer { AWS_CRYPTO_SHA1_BROWSER(NORMAL_DEPENDENCY, "@aws-crypto/sha1-browser", "3.0.0"), SIGNATURE_V4_MULTIREGION(NORMAL_DEPENDENCY, "@aws-sdk/signature-v4-multi-region"), RECURSION_DETECTION_MIDDLEWARE(NORMAL_DEPENDENCY, "@aws-sdk/middleware-recursion-detection"), + MIDDLEWARE_WEBSOCKET(NORMAL_DEPENDENCY, "@aws-sdk/middleware-websocket"), // Conditionally added when httpChecksum trait is present MD5_BROWSER(NORMAL_DEPENDENCY, "@aws-sdk/md5-js"), diff --git a/codegen/smithy-aws-typescript-codegen/src/main/resources/META-INF/services/software.amazon.smithy.typescript.codegen.integration.TypeScriptIntegration b/codegen/smithy-aws-typescript-codegen/src/main/resources/META-INF/services/software.amazon.smithy.typescript.codegen.integration.TypeScriptIntegration index cfa00754766f..2e5efdcb3bf0 100644 --- a/codegen/smithy-aws-typescript-codegen/src/main/resources/META-INF/services/software.amazon.smithy.typescript.codegen.integration.TypeScriptIntegration +++ b/codegen/smithy-aws-typescript-codegen/src/main/resources/META-INF/services/software.amazon.smithy.typescript.codegen.integration.TypeScriptIntegration @@ -14,6 +14,7 @@ software.amazon.smithy.aws.typescript.codegen.AddS3Config software.amazon.smithy.aws.typescript.codegen.AddS3ControlDependency software.amazon.smithy.aws.typescript.codegen.AddEventStreamHandlingDependency software.amazon.smithy.aws.typescript.codegen.AddHttp2Dependency +software.amazon.smithy.aws.typescript.codegen.AddWebsocketPlugin software.amazon.smithy.aws.typescript.codegen.AddTranscribeStreamingDependency software.amazon.smithy.aws.typescript.codegen.AddUserAgentDependency software.amazon.smithy.aws.typescript.codegen.AddOmitRetryHeadersDependency diff --git a/packages/middleware-sdk-transcribe-streaming/src/index.ts b/packages/middleware-sdk-transcribe-streaming/src/index.ts index 3fa5142690c6..048bbe852343 100644 --- a/packages/middleware-sdk-transcribe-streaming/src/index.ts +++ b/packages/middleware-sdk-transcribe-streaming/src/index.ts @@ -1,7 +1,4 @@ -export * from "./configuration"; export * from "./eventstream-handler"; -export * from "./middleware-endpoint"; -export * from "./middleware-session-id"; +export * from "./middleware-inject-response-values"; +export * from "./middleware-port"; export * from "./plugin"; -export * from "./signer"; -export * from "./websocket-handler"; diff --git a/packages/middleware-sdk-transcribe-streaming/src/middleware-session-id.ts b/packages/middleware-sdk-transcribe-streaming/src/middleware-inject-response-values.ts similarity index 84% rename from packages/middleware-sdk-transcribe-streaming/src/middleware-session-id.ts rename to packages/middleware-sdk-transcribe-streaming/src/middleware-inject-response-values.ts index 4ac56a2b5607..e0cc03a012b5 100644 --- a/packages/middleware-sdk-transcribe-streaming/src/middleware-session-id.ts +++ b/packages/middleware-sdk-transcribe-streaming/src/middleware-inject-response-values.ts @@ -19,7 +19,7 @@ type WithSession = { * the result stream. So it copies the parameters from input to the same * parameters in the output. */ -export const injectSessionIdMiddleware = +export const injectResponseValuesMiddleware = (config: { requestHandler: RequestHandler }): InitializeMiddleware => (next: InitializeHandler) => async (args: InitializeHandlerArguments) => { @@ -40,11 +40,11 @@ export const injectSessionIdMiddleware = }; const isWebSocket = (config: { requestHandler: RequestHandler }) => - config.requestHandler.metadata?.handlerProtocol === "websocket"; + config.requestHandler.metadata?.handlerProtocol?.includes("websocket"); -export const injectSessionIdMiddlewareOptions: InitializeHandlerOptions = { +export const injectResponseValuesMiddlewareOptions: InitializeHandlerOptions = { step: "initialize", - name: "injectSessionIdMiddleware", + name: "injectResponseValuesMiddleware", tags: ["WEBSOCKET", "EVENT_STREAM"], override: true, }; diff --git a/packages/middleware-sdk-transcribe-streaming/src/middleware-port.spec.ts b/packages/middleware-sdk-transcribe-streaming/src/middleware-port.spec.ts new file mode 100644 index 000000000000..bc4eefca0ff1 --- /dev/null +++ b/packages/middleware-sdk-transcribe-streaming/src/middleware-port.spec.ts @@ -0,0 +1,51 @@ +import { HttpRequest } from "@aws-sdk/protocol-http"; +import { BuildHandlerArguments, RequestHandler } from "@aws-sdk/types"; + +import { websocketPortMiddleware } from "./middleware-port"; + +describe("websocketPortMiddleware", () => { + const mockHandler: RequestHandler = { + metadata: { handlerProtocol: "websocket" }, + handle: () => ({} as any), + }; + it("should skip non-http request", (done) => { + const nonHttpRequest = { + foo: "bar", + }; + const next = (args: BuildHandlerArguments) => { + expect(args.request).toEqual(nonHttpRequest); + done(); + }; + const mw = websocketPortMiddleware({ requestHandler: mockHandler }); + mw(next as any, {} as any)({ request: nonHttpRequest, input: {} }); + }); + + it("should skip non WebSocket requests", (done) => { + const mockHandler: RequestHandler = { + metadata: { handlerProtocol: "some_protocol" }, + handle: () => ({} as any), + }; + const request = new HttpRequest({}); + const next = (args: BuildHandlerArguments) => { + expect(args.request).toEqual(request); + done(); + }; + const mw = websocketPortMiddleware({ requestHandler: mockHandler }); + mw(next as any, {} as any)({ request, input: {} }); + }); + + it("should update endpoint to websocket url", (done) => { + const request = new HttpRequest({ + hostname: "transcribestreaming.us-east-1.amazonaws.com", + }); + const next = (args: BuildHandlerArguments) => { + expect(HttpRequest.isInstance(args.request)).toBeTruthy(); + const processed = args.request as HttpRequest; + expect(processed.hostname).toEqual("transcribestreaming.us-east-1.amazonaws.com:8443"); + expect(processed.headers.host).toEqual("transcribestreaming.us-east-1.amazonaws.com:8443"); + done(); + }; + const mw = websocketPortMiddleware({ requestHandler: mockHandler }); + mw(next as any, {} as any)({ request, input: {} }); + }); +}); diff --git a/packages/middleware-sdk-transcribe-streaming/src/middleware-port.ts b/packages/middleware-sdk-transcribe-streaming/src/middleware-port.ts new file mode 100644 index 000000000000..04e27d94fec7 --- /dev/null +++ b/packages/middleware-sdk-transcribe-streaming/src/middleware-port.ts @@ -0,0 +1,33 @@ +import { HttpRequest } from "@aws-sdk/protocol-http"; +import { + BuildHandler, + BuildHandlerArguments, + BuildMiddleware, + RelativeMiddlewareOptions, + RequestHandler, +} from "@aws-sdk/types"; + +/** + * Middleware that generates WebSocket URL to TranscribeStreaming service + * Reference: https://docs.aws.amazon.com/transcribe/latest/dg/websocket.html + */ +export const websocketPortMiddleware = + (options: { requestHandler: RequestHandler }): BuildMiddleware => + (next: BuildHandler) => + (args: BuildHandlerArguments) => { + const { request } = args; + if (HttpRequest.isInstance(request) && options.requestHandler.metadata?.handlerProtocol?.includes("websocket")) { + // Append port to hostname because it needs to be signed together + request.hostname = `${request.hostname}:8443`; + request.headers.host = request.hostname; + } + return next(args); + }; + +export const websocketPortMiddlewareOptions: RelativeMiddlewareOptions = { + name: "websocketPortMiddleware", + tags: ["WEBSOCKET", "EVENT_STREAM", "PORT"], + relation: "after", + toMiddleware: "eventStreamHeaderMiddleware", + override: true, +}; diff --git a/packages/middleware-sdk-transcribe-streaming/src/plugin.ts b/packages/middleware-sdk-transcribe-streaming/src/plugin.ts index 6b41ac2627e7..51b059e94ce8 100644 --- a/packages/middleware-sdk-transcribe-streaming/src/plugin.ts +++ b/packages/middleware-sdk-transcribe-streaming/src/plugin.ts @@ -1,12 +1,18 @@ -import { Pluggable } from "@aws-sdk/types"; +import { Pluggable, RequestHandler } from "@aws-sdk/types"; -import { WebSocketResolvedConfig } from "./configuration"; -import { websocketURLMiddleware, websocketURLMiddlewareOptions } from "./middleware-endpoint"; -import { injectSessionIdMiddleware, injectSessionIdMiddlewareOptions } from "./middleware-session-id"; +import { + injectResponseValuesMiddleware, + injectResponseValuesMiddlewareOptions, +} from "./middleware-inject-response-values"; +import { websocketPortMiddleware, websocketPortMiddlewareOptions } from "./middleware-port"; -export const getWebSocketPlugin = (config: WebSocketResolvedConfig): Pluggable => ({ +interface PreviouslyResolved { + requestHandler: RequestHandler; +} + +export const getTranscribeStreamingPlugin = (config: PreviouslyResolved): Pluggable => ({ applyToStack: (clientStack) => { - clientStack.addRelativeTo(websocketURLMiddleware(config), websocketURLMiddlewareOptions); - clientStack.add(injectSessionIdMiddleware(config), injectSessionIdMiddlewareOptions); + clientStack.addRelativeTo(websocketPortMiddleware(config), websocketPortMiddlewareOptions); + clientStack.add(injectResponseValuesMiddleware(config), injectResponseValuesMiddlewareOptions); }, }); diff --git a/packages/middleware-sdk-transcribe-streaming/src/websocket-handler.spec.ts b/packages/middleware-sdk-transcribe-streaming/src/websocket-handler.spec.ts deleted file mode 100644 index df6cd3f41436..000000000000 --- a/packages/middleware-sdk-transcribe-streaming/src/websocket-handler.spec.ts +++ /dev/null @@ -1,151 +0,0 @@ -import { HttpRequest } from "@aws-sdk/protocol-http"; -import WS from "jest-websocket-mock"; -import { WebSocket } from "mock-socket"; -import { PassThrough } from "stream"; - -import { WebSocketHandler } from "./websocket-handler"; - -describe("WebSocketHandler", () => { - const mockHostname = "localhost:6789"; - const mockUrl = `ws://${mockHostname}/`; - - beforeEach(() => { - (global as any).WebSocket = WebSocket; - }); - - afterEach(() => { - WS.clean(); - jest.clearAllMocks(); - }); - - it("should contain protocol metadata", () => { - const handler = new WebSocketHandler(); - expect(handler.metadata.handlerProtocol).toEqual("websocket"); - }); - - it("populates socket in socket pool based on handle() requests", async () => { - const handler = new WebSocketHandler(); - const server = new WS(mockUrl); - - // @ts-expect-error Property 'sockets' is private and only accessible within class 'WebSocketHandler'. - expect(handler.sockets[mockUrl]).not.toBeDefined(); - - await handler.handle( - new HttpRequest({ - body: new PassThrough(), - hostname: mockHostname, - protocol: "ws:", - }) - ); - - // @ts-expect-error Property 'sockets' is private and only accessible within class 'WebSocketHandler'. - expect(handler.sockets[mockUrl]).toBeDefined(); - // @ts-expect-error Property 'sockets' is private and only accessible within class 'WebSocketHandler'. - expect(handler.sockets[mockUrl].length).toBe(1); - - await handler.handle( - new HttpRequest({ - body: new PassThrough(), - hostname: mockHostname, - protocol: "ws:", - }) - ); - - // @ts-expect-error Property 'sockets' is private and only accessible within class 'WebSocketHandler'. - expect(handler.sockets[mockUrl].length).toBe(2); - }); - - it("closes socket in socket pool on handler.destroy()", async () => { - const handler = new WebSocketHandler(); - const server = new WS(mockUrl); - - await handler.handle( - new HttpRequest({ - body: new PassThrough(), - hostname: mockHostname, - protocol: "ws:", - }) - ); - - // @ts-expect-error Property 'sockets' is private and only accessible within class 'WebSocketHandler'. - const socket = handler.sockets[mockUrl][0]; - - expect(socket.readyState).toBe(WebSocket.OPEN); - handler.destroy(); - - // Verify that socket.close() is called - expect(socket.readyState).toBe(WebSocket.CLOSING); - }); - - it("should throw in output stream if input stream throws", async () => { - expect.assertions(3); - const handler = new WebSocketHandler(); - //Using Node stream is fine because they are also async iterables. - const payload = new PassThrough(); - - const server = new WS(mockUrl); - - const { - response: { body: responsePayload }, - } = await handler.handle( - new HttpRequest({ - body: payload, - hostname: mockHostname, - protocol: "ws:", - }) - ); - - await server.connected; - payload.emit("error", new Error("FakeError")); - - try { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - for await (const chunk of responsePayload) { - /** pass */ - } - } catch (err) { - expect(err).toBeDefined(); - expect(err.message).toEqual("FakeError"); - // @ts-expect-error Property 'sockets' is private and only accessible within class 'WebSocketHandler'. - expect(handler.sockets[mockUrl].length).toBe(0); - } - }); - - it("should return retryable error if cannot setup ws connection", async () => { - expect.assertions(5); - - const originalFn = setTimeout; - (global as any).setTimeout = jest.fn().mockImplementation(setTimeout); - - const connectionTimeout = 1000; - const handler = new WebSocketHandler({ connectionTimeout }); - - //Using Node stream is fine because they are also async iterables. - const payload = new PassThrough(); - const mockInvalidHostname = "localhost:9876"; - const mockInvalidUrl = `ws://${mockInvalidHostname}/`; - - try { - await handler.handle( - new HttpRequest({ - body: payload, - hostname: mockInvalidHostname, //invalid websocket endpoint - protocol: "ws:", - }) - ); - } catch (err) { - expect(err).toBeDefined(); - expect(err.$metadata).toBeDefined(); - expect(err.$metadata.httpStatusCode >= 500).toBe(true); - expect( - ((global as any).setTimeout as jest.Mock).mock.calls.filter((args) => { - //find the 'setTimeout' call from the websocket handler - return args[0].toString().indexOf("$metadata") >= 0; - })[0][1] - ).toBe(connectionTimeout); - // @ts-expect-error Property 'sockets' is private and only accessible within class 'WebSocketHandler'. - expect(handler.sockets[mockInvalidUrl].length).toBe(0); - } - (global as any).setTimeout = originalFn; - }); -}); diff --git a/packages/middleware-websocket/LICENSE b/packages/middleware-websocket/LICENSE new file mode 100644 index 000000000000..e907b58668da --- /dev/null +++ b/packages/middleware-websocket/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/packages/middleware-websocket/README.md b/packages/middleware-websocket/README.md new file mode 100644 index 000000000000..b5ff7c7f99cd --- /dev/null +++ b/packages/middleware-websocket/README.md @@ -0,0 +1,12 @@ +# @aws-sdk/middleware-websocket + +[![NPM version](https://img.shields.io/npm/v/@aws-sdk/middleware-websocket/latest.svg)](https://www.npmjs.com/package/@aws-sdk/middleware-websocket) +[![NPM downloads](https://img.shields.io/npm/dm/@aws-sdk/middleware-websocket.svg)](https://www.npmjs.com/package/@aws-sdk/middleware-websocket) + +> An internal package + +This package contains necessary dependency to support WebSocket connections for AWS services. These behaviors are subject to change. + +## Usage + +You probably shouldn't, at least directly. diff --git a/packages/middleware-websocket/jest.config.js b/packages/middleware-websocket/jest.config.js new file mode 100644 index 000000000000..64f3d932819c --- /dev/null +++ b/packages/middleware-websocket/jest.config.js @@ -0,0 +1,7 @@ +const base = require("../../jest.config.base.js"); + +module.exports = { + ...base, + //only test cjs dist, avoid testing the package twice + testPathIgnorePatterns: ["/node_modules/", "/es/"], +}; diff --git a/packages/middleware-websocket/package.json b/packages/middleware-websocket/package.json new file mode 100644 index 000000000000..4849395d091b --- /dev/null +++ b/packages/middleware-websocket/package.json @@ -0,0 +1,67 @@ +{ + "name": "@aws-sdk/middleware-websocket", + "version": "3.292.0", + "main": "./dist-cjs/index.js", + "module": "./dist-es/index.js", + "types": "./dist-types/index.d.ts", + "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:include:deps": "lerna run --scope $npm_package_name --include-dependencies build", + "build:types": "tsc -p tsconfig.types.json", + "build:types:downlevel": "downlevel-dts dist-types dist-types/ts3.4", + "clean": "rimraf ./dist-* && rimraf *.tsbuildinfo", + "test": "jest" + }, + "author": { + "name": "AWS SDK for JavaScript Team", + "url": "https://aws.amazon.com/javascript/" + }, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/eventstream-serde-browser": "*", + "@aws-sdk/fetch-http-handler": "*", + "@aws-sdk/middleware-signing": "*", + "@aws-sdk/protocol-http": "*", + "@aws-sdk/signature-v4": "*", + "@aws-sdk/types": "*", + "@aws-sdk/util-format-url": "*", + "@aws-sdk/util-hex-encoding": "*", + "tslib": "^2.5.0" + }, + "devDependencies": { + "@tsconfig/recommended": "1.0.1", + "@types/uuid": "^8.3.0", + "concurrently": "7.0.0", + "downlevel-dts": "0.7.0", + "jest-websocket-mock": "^2.0.2", + "mock-socket": "9.1.5", + "rimraf": "3.0.2", + "typedoc": "0.23.23", + "typescript": "~4.9.5", + "web-streams-polyfill": "3.2.1" + }, + "engines": { + "node": ">= 14.0.0" + }, + "typesVersions": { + "<4.0": { + "dist-types/*": [ + "dist-types/ts3.4/*" + ] + } + }, + "files": [ + "dist-*" + ], + "homepage": "https://github.com/aws/aws-sdk-js-v3/tree/main/packages/middleware-websocket", + "repository": { + "type": "git", + "url": "https://github.com/aws/aws-sdk-js-v3.git", + "directory": "packages/middleware-websocket" + }, + "typedoc": { + "entryPoint": "src/index.ts" + } +} diff --git a/packages/middleware-websocket/src/EventStreamPayloadHandler.spec.ts b/packages/middleware-websocket/src/EventStreamPayloadHandler.spec.ts new file mode 100644 index 000000000000..23083c51ced9 --- /dev/null +++ b/packages/middleware-websocket/src/EventStreamPayloadHandler.spec.ts @@ -0,0 +1,173 @@ +/** + * @jest-environment jsdom + */ +import { EventStreamCodec } from "@aws-sdk/eventstream-codec"; +import { Decoder, Encoder, EventSigner, FinalizeHandler, FinalizeHandlerArguments, HttpRequest } from "@aws-sdk/types"; +import { ReadableStream, TransformStream } from "web-streams-polyfill"; + +import { EventStreamPayloadHandler } from "./EventStreamPayloadHandler"; +import { getEventSigningTransformStream } from "./get-event-signing-stream"; +jest.mock("./get-event-signing-stream"); +jest.mock("@aws-sdk/eventstream-codec"); + +describe(EventStreamPayloadHandler.name, () => { + const mockSigner: EventSigner = { + sign: jest.fn(), + }; + const mockUtf8Decoder: Decoder = jest.fn(); + const mockUtf8encoder: Encoder = jest.fn(); + const mockNextHandler: FinalizeHandler = jest.fn(); + const originalTransformStreamCtor = window.TransformStream; + + beforeEach(() => { + window.TransformStream = TransformStream; + (getEventSigningTransformStream as unknown as jest.Mock).mockImplementation(() => new TransformStream()); + (EventStreamCodec as jest.Mock).mockImplementation(() => {}); + }); + + afterEach(() => { + window.TransformStream = originalTransformStreamCtor; + jest.clearAllMocks(); + }); + + it("should throw if request payload is not a stream", () => { + const handler = new EventStreamPayloadHandler({ + eventSigner: () => Promise.resolve(mockSigner), + utf8Decoder: mockUtf8Decoder, + utf8Encoder: mockUtf8encoder, + }); + expect( + handler.handle(mockNextHandler, { + request: { body: "body" } as HttpRequest, + input: {}, + }) + ).rejects.toThrow("Eventstream payload must be a ReadableStream."); + }); + + it("should close the request payload if downstream middleware throws", async () => { + const mockError = new Error("mockError"); + (mockNextHandler as any).mockImplementationOnce(() => Promise.reject(mockError)); + + const handler = new EventStreamPayloadHandler({ + eventSigner: () => Promise.resolve(mockSigner), + utf8Decoder: mockUtf8Decoder, + utf8Encoder: mockUtf8encoder, + }); + const mockRequest = { body: new ReadableStream() } as HttpRequest; + + try { + await handler.handle(mockNextHandler, { + request: mockRequest, + input: {}, + }); + fail(`Expected ${mockError} to be thrown.`); + } catch (error) { + expect(error).toBe(mockError); + } + + // Stream unlocked means the stream is canceled. + expect((mockRequest.body as ReadableStream).locked).toEqual(false); + }); + + it("should call event signer with request signature from signing middleware", async () => { + const priorSignature = "1234567890"; + const authorization = `AWS4-HMAC-SHA256 Credential=AKID/20200510/us-west-2/foo/aws4_request, SignedHeaders=host, Signature=${priorSignature}`; + + const mockRequest = { + body: new ReadableStream(), + headers: { authorization }, + } as any; + + const handler = new EventStreamPayloadHandler({ + eventSigner: () => Promise.resolve(mockSigner), + utf8Decoder: mockUtf8Decoder, + utf8Encoder: mockUtf8encoder, + }); + + await handler.handle(mockNextHandler, { + request: mockRequest, + input: {}, + }); + + expect(getEventSigningTransformStream).toHaveBeenCalledTimes(1); + expect(getEventSigningTransformStream).toHaveBeenCalledWith(priorSignature, expect.anything(), expect.anything()); + }); + + it("should call event signer with request signature from query string if no signature headers are found", async () => { + const priorSignature = "1234567890"; + const authorization = `AWS4-HMAC-SHA256 Credential=AKID/20200510/us-west-2/foo/aws4_request, SignedHeaders=host, Signature=`; + + const mockRequest = { + body: new ReadableStream(), + headers: { authorization }, + query: { + "X-Amz-Signature": priorSignature, + }, + } as any; + + const handler = new EventStreamPayloadHandler({ + eventSigner: () => Promise.resolve(mockSigner), + utf8Decoder: mockUtf8Decoder, + utf8Encoder: mockUtf8encoder, + }); + + await handler.handle(mockNextHandler, { + request: mockRequest, + input: {}, + }); + + expect(getEventSigningTransformStream).toHaveBeenCalledTimes(1); + expect(getEventSigningTransformStream).toHaveBeenCalledWith(priorSignature, expect.anything(), expect.anything()); + }); + + it("should start piping to request payload through event signer if downstream middleware returns", async () => { + const authorization = + "AWS4-HMAC-SHA256 Credential=AKID/20200510/us-west-2/foo/aws4_request, SignedHeaders=host, Signature=1234567890"; + const originalPayload = new TransformStream(); + const mockRequest = { + body: originalPayload.readable, + headers: { authorization }, + } as any; + const handler = new EventStreamPayloadHandler({ + eventSigner: () => Promise.resolve(mockSigner), + utf8Decoder: mockUtf8Decoder, + utf8Encoder: mockUtf8encoder, + }); + // Middleware that returns the request from payload handler. + (mockNextHandler as any).mockImplementationOnce((args: FinalizeHandlerArguments) => { + const handledRequest = args.request as HttpRequest; + // This middleware returns the output request from eventstream payload handler + return Promise.resolve({ output: { handledRequest } }); + }); + const { + output: { handledRequest }, + } = await handler.handle(mockNextHandler, { + request: mockRequest, + input: {}, + }); + // Expect the output payload stream from handler is not the exact stream supplied to the handler + expect(handledRequest.body).not.toBe(originalPayload.readable); + // Expect the data from the output payload from eventstream payload handler the same as from the + // stream supplied to the handler. + const chunks: any = []; + const reader = handledRequest.body.getReader(); + const push = () => { + reader.read().then(({ done, value }) => { + if (!done) { + chunks.push(value); + push(); + } + }); + }; + push(); + + const writer = originalPayload.writable.getWriter(); + writer.ready.then(() => writer.write(Buffer.from("Some Data"))); + await writer.ready; + await writer.close(); + await writer.closed; + + const collectedData = Buffer.concat(chunks).toString("utf8"); + expect(collectedData).toEqual("Some Data"); + }); +}); diff --git a/packages/middleware-websocket/src/EventStreamPayloadHandler.ts b/packages/middleware-websocket/src/EventStreamPayloadHandler.ts new file mode 100644 index 000000000000..aa0cb6a7ce7a --- /dev/null +++ b/packages/middleware-websocket/src/EventStreamPayloadHandler.ts @@ -0,0 +1,80 @@ +import { EventStreamCodec } from "@aws-sdk/eventstream-codec"; +import { + Decoder, + Encoder, + EventSigner, + EventStreamPayloadHandler as IEventStreamPayloadHandler, + FinalizeHandler, + FinalizeHandlerArguments, + FinalizeHandlerOutput, + HandlerExecutionContext, + HttpRequest, + MetadataBearer, + Provider, +} from "@aws-sdk/types"; + +import { getEventSigningTransformStream } from "./get-event-signing-stream"; + +export interface EventStreamPayloadHandlerOptions { + eventSigner: Provider; + utf8Encoder: Encoder; + utf8Decoder: Decoder; +} + +/** + * A handler that control the eventstream payload flow: + * 1. Pause stream for initial request. + * 2. Close the stream if initial request fails. + * 3. Start piping payload when connection is established. + * 4. Sign the payload after payload stream starting to flow. + */ +export class EventStreamPayloadHandler implements IEventStreamPayloadHandler { + private readonly eventSigner: Provider; + private readonly eventStreamCodec: EventStreamCodec; + + constructor(options: EventStreamPayloadHandlerOptions) { + this.eventSigner = options.eventSigner; + this.eventStreamCodec = new EventStreamCodec(options.utf8Encoder, options.utf8Decoder); + } + + async handle( + next: FinalizeHandler, + args: FinalizeHandlerArguments, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + context: HandlerExecutionContext = {} as any + ): Promise> { + const request = args.request as HttpRequest; + const { body: payload, headers, query } = request; + + if (!(payload instanceof ReadableStream)) { + throw new Error("Eventstream payload must be a ReadableStream."); + } + + const placeHolderStream = new TransformStream(); + request.body = placeHolderStream.readable; + + let result: FinalizeHandlerOutput; + try { + result = await next(args); + } catch (e) { + // Close the payload stream otherwise the retry would hang + // because of the previous connection. + request.body.cancel(); + throw e; + } + + const match = (headers["authorization"] || "").match(/Signature=([\w]+)$/); + // Sign the eventstream based on the signature from initial request. + const priorSignature = (match || [])[1] || (query && (query["X-Amz-Signature"] as string)) || ""; + const signingStream = getEventSigningTransformStream( + priorSignature, + await this.eventSigner(), + this.eventStreamCodec + ); + + const signedPayload = payload.pipeThrough(signingStream); + signedPayload.pipeThrough(placeHolderStream); + + return result; + } +} diff --git a/packages/middleware-sdk-transcribe-streaming/src/signer.spec.ts b/packages/middleware-websocket/src/WebsocketSignatureV4.spec.ts similarity index 89% rename from packages/middleware-sdk-transcribe-streaming/src/signer.spec.ts rename to packages/middleware-websocket/src/WebsocketSignatureV4.spec.ts index b9dec747c5e3..538579dd130a 100644 --- a/packages/middleware-sdk-transcribe-streaming/src/signer.spec.ts +++ b/packages/middleware-websocket/src/WebsocketSignatureV4.spec.ts @@ -1,13 +1,14 @@ +// TODO: remove this file as duplicated to @aws-sdk/middleware-websocket import { HttpRequest } from "@aws-sdk/protocol-http"; import { RequestPresigningArguments, RequestSigningArguments } from "@aws-sdk/types"; -import { SignatureV4 } from "./signer"; +import { WebsocketSignatureV4 } from "./WebsocketSignatureV4"; jest.mock("@aws-sdk/protocol-http"); -describe("signer", () => { +describe("WebsocketSignatureV4", () => { const mockPresignedRequest = { req: "mockPresignedRequest" }; - const mockSignedRequest = { reg: "mockSignedRequest" }; + const mockSignedRequest = { req: "mockSignedRequest" }; const presign = jest.fn().mockResolvedValue(mockPresignedRequest); const sign = jest.fn().mockResolvedValue(mockSignedRequest); @@ -20,8 +21,8 @@ describe("signer", () => { const body = "body"; const method = "method"; - const request = { headers, body, method }; - const sigV4 = new SignatureV4({ signer: { sign, presign } as any }); + const request = { headers, body, method, protocol: "wss:" }; + const sigV4 = new WebsocketSignatureV4({ signer: { sign, presign } as any }); afterEach(() => { jest.clearAllMocks(); @@ -42,7 +43,7 @@ describe("signer", () => { it("with options", async () => { const options = { - expiresIn: 300, + expiresIn: 60, }; const result = await sigV4.presign(request as any, options); expectPresignArgs(result, options); @@ -68,7 +69,7 @@ describe("signer", () => { expect(presign).toHaveBeenCalledWith( { ...request, body: "" }, { - expiresIn: 300, + expiresIn: 60, unsignableHeaders: new Set(Object.keys(request.headers).filter((header) => header !== "host")), } ); diff --git a/packages/middleware-sdk-transcribe-streaming/src/signer.ts b/packages/middleware-websocket/src/WebsocketSignatureV4.ts similarity index 80% rename from packages/middleware-sdk-transcribe-streaming/src/signer.ts rename to packages/middleware-websocket/src/WebsocketSignatureV4.ts index 172c220d2ba6..f81b0d009ebb 100644 --- a/packages/middleware-sdk-transcribe-streaming/src/signer.ts +++ b/packages/middleware-websocket/src/WebsocketSignatureV4.ts @@ -8,7 +8,9 @@ import { RequestSigningArguments, } from "@aws-sdk/types"; -export class SignatureV4 implements RequestSigner, RequestPresigner { +import { isWebSocketRequest } from "./utils"; + +export class WebsocketSignatureV4 implements RequestSigner, RequestPresigner { private readonly signer: BaseSignatureV4; constructor(options: { signer: BaseSignatureV4 }) { this.signer = options.signer; @@ -19,14 +21,14 @@ export class SignatureV4 implements RequestSigner, RequestPresigner { } public async sign(toSign: IHttpRequest, options?: RequestSigningArguments): Promise { - if (HttpRequest.isInstance(toSign)) { + if (HttpRequest.isInstance(toSign) && isWebSocketRequest(toSign)) { // Presign the endpoint url with empty body, otherwise - // the payload hash would be UNSINGED-PAYLOAD + // the payload hash would be UNSIGNED-PAYLOAD const signedRequest = await this.signer.presign( { ...toSign, body: "" }, { - // presigned url must be expired within 5 mins. - expiresIn: 5 * 60, + // presigned url must be expired within 1 min. + expiresIn: 60, // Not to sign headers. Transcribe-streaming WebSocket // request omits headers except for required 'host' header. If we sign // the other headers, the signature could be mismatch. diff --git a/packages/middleware-sdk-transcribe-streaming/src/configuration.ts b/packages/middleware-websocket/src/configuration.ts similarity index 61% rename from packages/middleware-sdk-transcribe-streaming/src/configuration.ts rename to packages/middleware-websocket/src/configuration.ts index 7344a3410eae..f97330e8d1e3 100644 --- a/packages/middleware-sdk-transcribe-streaming/src/configuration.ts +++ b/packages/middleware-websocket/src/configuration.ts @@ -1,7 +1,7 @@ import { SignatureV4 as BaseSignatureV4 } from "@aws-sdk/signature-v4"; import { AuthScheme, Provider, RequestHandler, RequestSigner } from "@aws-sdk/types"; -import { SignatureV4 } from "./signer"; +import { WebsocketSignatureV4 } from "./WebsocketSignatureV4"; export interface WebSocketInputConfig {} @@ -23,18 +23,15 @@ export interface WebSocketResolvedConfig { export const resolveWebSocketConfig = ( input: T & WebSocketInputConfig & PreviouslyResolved -): T & WebSocketResolvedConfig => - input.requestHandler.metadata?.handlerProtocol !== "websocket" - ? input - : { - ...input, - signer: async (authScheme: AuthScheme) => { - const signerObj = await input.signer(authScheme); - if (validateSigner(signerObj)) { - return new SignatureV4({ signer: signerObj }); - } - throw new Error("Expected SignatureV4 signer, please check the client constructor."); - }, - }; +): T & WebSocketResolvedConfig => ({ + ...input, + signer: async (authScheme: AuthScheme) => { + const signerObj = await input.signer(authScheme); + if (validateSigner(signerObj)) { + return new WebsocketSignatureV4({ signer: signerObj }); + } + throw new Error("Expected WebsocketSignatureV4 signer, please check the client constructor."); + }, +}); const validateSigner = (signer: any): signer is BaseSignatureV4 => !!signer; diff --git a/packages/middleware-websocket/src/eventstream-payload-handler-provider.ts b/packages/middleware-websocket/src/eventstream-payload-handler-provider.ts new file mode 100644 index 000000000000..f54783b50914 --- /dev/null +++ b/packages/middleware-websocket/src/eventstream-payload-handler-provider.ts @@ -0,0 +1,10 @@ +import { Decoder, Encoder, EventSigner, EventStreamPayloadHandlerProvider, Provider } from "@aws-sdk/types"; + +import { EventStreamPayloadHandler } from "./EventStreamPayloadHandler"; + +/** NodeJS event stream utils provider */ +export const eventStreamPayloadHandlerProvider: EventStreamPayloadHandlerProvider = (options: { + utf8Encoder: Encoder; + utf8Decoder: Decoder; + eventSigner: Provider; +}) => new EventStreamPayloadHandler(options); diff --git a/packages/middleware-websocket/src/get-event-signing-stream.spec.ts b/packages/middleware-websocket/src/get-event-signing-stream.spec.ts new file mode 100644 index 000000000000..04928c83687d --- /dev/null +++ b/packages/middleware-websocket/src/get-event-signing-stream.spec.ts @@ -0,0 +1,95 @@ +/** + * @jest-environment jsdom + */ +import { EventStreamCodec } from "@aws-sdk/eventstream-codec"; +import { Message, MessageHeaders } from "@aws-sdk/types"; +import { fromUtf8, toUtf8 } from "@aws-sdk/util-utf8"; +import { TransformStream } from "web-streams-polyfill"; + +import { getEventSigningTransformStream } from "./get-event-signing-stream"; + +describe(getEventSigningTransformStream.name, () => { + const originalDate = Date; + const originalTransformStreamCtor = window.TransformStream; + + beforeEach(() => { + window.TransformStream = TransformStream; + }); + afterEach(() => { + Date = originalDate; + window.TransformStream = originalTransformStreamCtor; + }); + + it("should sign a eventstream payload properly", async () => { + const eventStreamCodec = new EventStreamCodec(toUtf8, fromUtf8); + const inputChunks: Array = ( + [ + { + headers: {}, + body: fromUtf8("foo"), + }, + { + headers: {}, + body: fromUtf8("bar"), + }, + ] as Array + ).map((event) => eventStreamCodec.encode(event)); + const expected: Array = [ + { + ":date": { type: "timestamp", value: new Date(1546045446000) }, + ":chunk-signature": { + type: "binary", + value: Uint8Array.from([115, 105, 103, 110, 97, 116, 117, 114, 101, 49]), + }, + }, + { + ":date": { type: "timestamp", value: new Date(1546045447000) }, + ":chunk-signature": { + type: "binary", + value: Uint8Array.from([115, 105, 103, 110, 97, 116, 117, 114, 101, 50]), + }, + }, + ]; + const mockEventSigner = jest + .fn() + .mockReturnValueOnce("7369676e617475726531") //'signature1' + .mockReturnValueOnce("7369676e617475726532"); //'signature2' + // mock 'new Date()' + let mockDateCount = 0; + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const mockDate = jest + .spyOn(window, "Date") + //@ts-ignore: https://stackoverflow.com/questions/60912023/jest-typescript-mock-date-constructor/60918716#60918716 + .mockImplementation((input) => { + if (input) return new originalDate(input); + mockDateCount += 1; + return expected[mockDateCount - 1][":date"].value; + }); + const signingStream = getEventSigningTransformStream("initial", { sign: mockEventSigner }, eventStreamCodec); + const output: Array = []; + + const reader = signingStream.readable.getReader(); + const push = () => { + reader.read().then(({ done, value }) => { + if (!done) { + output.push(eventStreamCodec.decode(value!).headers); + push(); + } + }); + }; + push(); + + const writer = signingStream.writable.getWriter(); + inputChunks.forEach((chunk) => { + writer.ready.then(() => writer.write(chunk)); + }); + await writer.ready; + await writer.close(); + await writer.closed; + expect(output).toEqual(expected); + expect(mockEventSigner.mock.calls[0][1].priorSignature).toBe("initial"); + expect(mockEventSigner.mock.calls[0][1].signingDate.getTime()).toBe((expected[0][":date"].value as Date).getTime()); + expect(mockEventSigner.mock.calls[1][1].priorSignature).toBe("7369676e617475726531"); + expect(mockEventSigner.mock.calls[1][1].signingDate.getTime()).toBe((expected[1][":date"].value as Date).getTime()); + }); +}); diff --git a/packages/middleware-websocket/src/get-event-signing-stream.ts b/packages/middleware-websocket/src/get-event-signing-stream.ts new file mode 100644 index 000000000000..b8e78ae63841 --- /dev/null +++ b/packages/middleware-websocket/src/get-event-signing-stream.ts @@ -0,0 +1,52 @@ +import { EventStreamCodec } from "@aws-sdk/eventstream-codec"; +import { EventSigner, MessageHeaders } from "@aws-sdk/types"; +import { fromHex } from "@aws-sdk/util-hex-encoding"; + +/** + * Get a transform stream that signs the eventstream + * Implementation replicated from @aws-sdk/eventstream-handler-node::EventSigningStream + * but modified to be compatible with WHATWG stream interface + */ +export const getEventSigningTransformStream = ( + initialSignature: string, + eventSigner: EventSigner, + eventStreamCodec: EventStreamCodec +): TransformStream => { + let priorSignature = initialSignature; + const transformer: Transformer = { + start() {}, + async transform(chunk, controller) { + try { + const now = new Date(); + const dateHeader: MessageHeaders = { + ":date": { type: "timestamp", value: now }, + }; + const signature = await eventSigner.sign( + { + payload: chunk, + headers: eventStreamCodec.formatHeaders(dateHeader), + }, + { + priorSignature, + signingDate: now, + } + ); + priorSignature = signature; + const serializedSigned = eventStreamCodec.encode({ + headers: { + ...dateHeader, + ":chunk-signature": { + type: "binary", + value: fromHex(signature), + }, + }, + body: chunk, + }); + controller.enqueue(serializedSigned); + } catch (error) { + controller.error(error); + } + }, + }; + return new TransformStream({ ...transformer }); +}; diff --git a/packages/middleware-websocket/src/index.ts b/packages/middleware-websocket/src/index.ts new file mode 100644 index 000000000000..2acfc78a8f6f --- /dev/null +++ b/packages/middleware-websocket/src/index.ts @@ -0,0 +1,4 @@ +export * from "./configuration"; +export * from "./eventstream-payload-handler-provider"; +export * from "./plugin"; +export * from "./websocket-fetch-handler"; diff --git a/packages/middleware-websocket/src/middleware-session-id.spec.ts b/packages/middleware-websocket/src/middleware-session-id.spec.ts new file mode 100644 index 000000000000..cb9e6d05d6b2 --- /dev/null +++ b/packages/middleware-websocket/src/middleware-session-id.spec.ts @@ -0,0 +1,10 @@ +import { injectSessionIdMiddleware } from "./middleware-session-id"; + +describe(injectSessionIdMiddleware.name, () => { + const mockNextHandler = jest.fn().mockResolvedValue({ output: {} }); + it("should populate the SessionId parameter in response", async () => { + const mw = injectSessionIdMiddleware(); + const { output } = await mw(mockNextHandler as any, {} as any)({ input: { SessionId: "ID" } }); + expect(output).toMatchObject({ SessionId: "ID" }); + }); +}); diff --git a/packages/middleware-websocket/src/middleware-session-id.ts b/packages/middleware-websocket/src/middleware-session-id.ts new file mode 100644 index 000000000000..e63fe0d25ea8 --- /dev/null +++ b/packages/middleware-websocket/src/middleware-session-id.ts @@ -0,0 +1,40 @@ +import { + InitializeHandler, + InitializeHandlerArguments, + InitializeHandlerOptions, + InitializeMiddleware, +} from "@aws-sdk/types"; + +type WithSession = { + SessionId?: string; + [key: string]: any; +}; + +/** + * Most WebSocket operations contains `SessionId` parameter in both input and + * output, with the same value. This middleware populates the `SessionId` + * parameter from request to the response. This is necessary because in + * WebSocket, the SDK cannot access any parameters other than the response + * stream. So we fake response parameter. + */ +export const injectSessionIdMiddleware = + (): InitializeMiddleware => + (next: InitializeHandler) => + async (args: InitializeHandlerArguments) => { + const requestParams = { + ...args.input, + }; + const response = await next(args); + const output = response.output; + if (requestParams.SessionId && output.SessionId == null) { + output.SessionId = requestParams.SessionId; + } + return response; + }; + +export const injectSessionIdMiddlewareOptions: InitializeHandlerOptions = { + step: "initialize", + name: "injectSessionIdMiddleware", + tags: ["WEBSOCKET", "EVENT_STREAM"], + override: true, +}; diff --git a/packages/middleware-sdk-transcribe-streaming/src/middleware-endpoint.spec.ts b/packages/middleware-websocket/src/middleware-websocket-endpoint.spec.ts similarity index 69% rename from packages/middleware-sdk-transcribe-streaming/src/middleware-endpoint.spec.ts rename to packages/middleware-websocket/src/middleware-websocket-endpoint.spec.ts index 1b1337fd539c..015b73aefeff 100644 --- a/packages/middleware-sdk-transcribe-streaming/src/middleware-endpoint.spec.ts +++ b/packages/middleware-websocket/src/middleware-websocket-endpoint.spec.ts @@ -1,13 +1,11 @@ import { HttpRequest } from "@aws-sdk/protocol-http"; import { BuildHandlerArguments, RequestHandler } from "@aws-sdk/types"; -import { websocketURLMiddleware } from "./middleware-endpoint"; +import { websocketEndpointMiddleware } from "./middleware-websocket-endpoint"; -describe("websocketURLMiddleware", () => { - const mockHandler: RequestHandler = { - metadata: { handlerProtocol: "websocket" }, - handle: () => ({} as any), - }; +describe(websocketEndpointMiddleware.name, () => { + const config = { requestHandler: { metadata: { handlerProtocol: "websocket/h1.1" } } as RequestHandler }; + const handlerOption = { headerPrefix: "some-thing" }; it("should skip non-http request", (done) => { const nonHttpRequest = { foo: "bar", @@ -16,41 +14,27 @@ describe("websocketURLMiddleware", () => { expect(args.request).toEqual(nonHttpRequest); done(); }; - const mw = websocketURLMiddleware({ requestHandler: mockHandler }); + const mw = websocketEndpointMiddleware(config, handlerOption); mw(next as any, {} as any)({ request: nonHttpRequest, input: {} }); }); - it("should skip non WebSocket requests", (done) => { - const mockHandler: RequestHandler = { - metadata: { handlerProtocol: "some_protocol" }, - handle: () => ({} as any), - }; - const request = new HttpRequest({}); - const next = (args: BuildHandlerArguments) => { - expect(args.request).toEqual(request); - done(); - }; - const mw = websocketURLMiddleware({ requestHandler: mockHandler }); - mw(next as any, {} as any)({ request, input: {} }); - }); - it("should update endpoint to websocket url", (done) => { const request = new HttpRequest({ protocol: "https:", - hostname: "transcribestreaming.us-east-1.amazonaws.com", - path: "/stream-transcription", + hostname: "foo.us-east-1.amazonaws.com", + path: "/stream-operation", method: "POST", }); const next = (args: BuildHandlerArguments) => { expect(HttpRequest.isInstance(args.request)).toBeTruthy(); const processed = args.request as HttpRequest; expect(processed.protocol).toEqual("wss:"); - expect(processed.hostname).toEqual("transcribestreaming.us-east-1.amazonaws.com:8443"); - expect(processed.path).toEqual("/stream-transcription-websocket"); + expect(processed.hostname).toEqual("foo.us-east-1.amazonaws.com"); + expect(processed.path).toEqual("/stream-operation-websocket"); expect(processed.method).toEqual("GET"); done(); }; - const mw = websocketURLMiddleware({ requestHandler: mockHandler }); + const mw = websocketEndpointMiddleware(config, handlerOption); mw(next as any, {} as any)({ request, input: {} }); }); @@ -72,7 +56,7 @@ describe("websocketURLMiddleware", () => { expect(processed.headers["X-Amz-Content-Sha256"]).toBeUndefined(); done(); }; - const mw = websocketURLMiddleware({ requestHandler: mockHandler }); + const mw = websocketEndpointMiddleware(config, handlerOption); mw(next as any, {} as any)({ request, input: {} }); }); @@ -84,7 +68,7 @@ describe("websocketURLMiddleware", () => { expect(processed.headers["host"]).toBeDefined(); done(); }; - const mw = websocketURLMiddleware({ requestHandler: mockHandler }); + const mw = websocketEndpointMiddleware(config, handlerOption); mw(next as any, {} as any)({ request, input: {} }); }); @@ -110,7 +94,7 @@ describe("websocketURLMiddleware", () => { }); done(); }; - const mw = websocketURLMiddleware({ requestHandler: mockHandler }); + const mw = websocketEndpointMiddleware(config, { headerPrefix: "x-amzn-transcribe-" }); mw(next as any, {} as any)({ request, input: {} }); }); }); diff --git a/packages/middleware-sdk-transcribe-streaming/src/middleware-endpoint.ts b/packages/middleware-websocket/src/middleware-websocket-endpoint.ts similarity index 59% rename from packages/middleware-sdk-transcribe-streaming/src/middleware-endpoint.ts rename to packages/middleware-websocket/src/middleware-websocket-endpoint.ts index 366f859d0505..3fa051caa363 100644 --- a/packages/middleware-sdk-transcribe-streaming/src/middleware-endpoint.ts +++ b/packages/middleware-websocket/src/middleware-websocket-endpoint.ts @@ -8,22 +8,28 @@ import { } from "@aws-sdk/types"; /** - * Middleware that generates WebSocket URL to TranscribeStreaming service - * Reference: https://docs.aws.amazon.com/transcribe/latest/dg/websocket.html + * Middleware that modify the request to from http to WebSocket + * This middleware can only be applied to commands that supports bi-directional event streaming via WebSocket. + * Example of headerPrefix is "x-amz-rekognition-streaming-liveness-*" prefix exists for all rekognition streaming + * websocket API's headers. The common prefix are to be removed when moving them from headers to querystring. */ -export const websocketURLMiddleware = - (options: { requestHandler: RequestHandler }): BuildMiddleware => +export const websocketEndpointMiddleware = + ( + config: { requestHandler: RequestHandler }, + options: { headerPrefix: string } + ): BuildMiddleware => (next: BuildHandler) => (args: BuildHandlerArguments) => { const { request } = args; - if (HttpRequest.isInstance(request) && options.requestHandler.metadata?.handlerProtocol === "websocket") { + if ( + HttpRequest.isInstance(request) && + config.requestHandler.metadata?.handlerProtocol?.toLowerCase().includes("websocket") + ) { // Update http/2 endpoint to WebSocket-specific endpoint. request.protocol = "wss:"; - // Append port to hostname because it needs to be signed together - request.hostname = `${request.hostname}:8443`; - request.path = `${request.path}-websocket`; request.method = "GET"; + request.path = `${request.path}-websocket`; // Move headers to query string. Because the signature is generated with // headers moved to query, the endpoint url needs to tally with the signature. const { headers } = request; @@ -34,11 +40,9 @@ export const websocketURLMiddleware = delete headers["Content-Type"]; delete headers["x-amz-content-sha256"]; - // Serialized header like 'x-amzn-transcribe-sample-rate' should be 'sample-rate' - // in WebSocket URL. for (const name of Object.keys(headers)) { - if (name.indexOf("x-amzn-transcribe-") === 0) { - const chunkedName = name.replace("x-amzn-transcribe-", ""); + if (name.indexOf(options.headerPrefix) === 0) { + const chunkedName = name.replace(options.headerPrefix, ""); request.query[chunkedName] = headers[name]; } } @@ -49,13 +53,13 @@ export const websocketURLMiddleware = request.query["user-agent"] = headers["x-amz-user-agent"]; } // Host header is required for signing - request.headers = { host: request.hostname }; + request.headers = { host: headers.host ?? request.hostname }; } return next(args); }; -export const websocketURLMiddlewareOptions: RelativeMiddlewareOptions = { - name: "websocketURLMiddleware", +export const websocketEndpointMiddlewareOptions: RelativeMiddlewareOptions = { + name: "websocketEndpointMiddleware", tags: ["WEBSOCKET", "EVENT_STREAM"], relation: "after", toMiddleware: "eventStreamHeaderMiddleware", diff --git a/packages/middleware-websocket/src/plugin.ts b/packages/middleware-websocket/src/plugin.ts new file mode 100644 index 000000000000..813d5c6f6c7a --- /dev/null +++ b/packages/middleware-websocket/src/plugin.ts @@ -0,0 +1,18 @@ +import { Pluggable, RequestHandler } from "@aws-sdk/types"; + +import { injectSessionIdMiddleware, injectSessionIdMiddlewareOptions } from "./middleware-session-id"; +import { websocketEndpointMiddleware, websocketEndpointMiddlewareOptions } from "./middleware-websocket-endpoint"; + +interface WebSocketResolvedConfig { + requestHandler: RequestHandler; +} + +export const getWebSocketPlugin = ( + config: WebSocketResolvedConfig, + options: { headerPrefix: string } +): Pluggable => ({ + applyToStack: (clientStack) => { + clientStack.addRelativeTo(websocketEndpointMiddleware(config, options), websocketEndpointMiddlewareOptions); + clientStack.add(injectSessionIdMiddleware(), injectSessionIdMiddlewareOptions); + }, +}); diff --git a/packages/middleware-websocket/src/utils.ts b/packages/middleware-websocket/src/utils.ts new file mode 100644 index 000000000000..ed8c444eebce --- /dev/null +++ b/packages/middleware-websocket/src/utils.ts @@ -0,0 +1,3 @@ +import { HttpRequest } from "@aws-sdk/types"; + +export const isWebSocketRequest = (request: HttpRequest) => request.protocol === "ws:" || request.protocol === "wss:"; diff --git a/packages/middleware-websocket/src/websocket-fetch-handler.spec.ts b/packages/middleware-websocket/src/websocket-fetch-handler.spec.ts new file mode 100644 index 000000000000..8ef512eb1adc --- /dev/null +++ b/packages/middleware-websocket/src/websocket-fetch-handler.spec.ts @@ -0,0 +1,170 @@ +import { FetchHttpHandler } from "@aws-sdk/fetch-http-handler"; +import { HttpRequest } from "@aws-sdk/protocol-http"; +import WS from "jest-websocket-mock"; +import { WebSocket } from "mock-socket"; +import { PassThrough } from "stream"; + +import { WebSocketFetchHandler } from "./websocket-fetch-handler"; + +jest.mock("@aws-sdk/fetch-http-handler"); + +describe(WebSocketFetchHandler.name, () => { + const mockHostname = "localhost:6789"; + const mockUrl = `ws://${mockHostname}/`; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + describe("should handle WebSocket connections", () => { + beforeEach(() => { + (global as any).WebSocket = WebSocket; + }); + + afterEach(() => { + WS.clean(); + }); + + it("should contain protocol metadata", () => { + const handler = new WebSocketFetchHandler(); + expect(handler.metadata.handlerProtocol).toContain("websocket"); + }); + + it("populates socket in socket pool based on handle() requests", async () => { + const handler = new WebSocketFetchHandler(); + const server = new WS(mockUrl); + + // @ts-expect-error Property 'sockets' is private and only accessible within class 'WebSocketHandler'. + expect(handler.sockets[mockUrl]).not.toBeDefined(); + + await handler.handle( + new HttpRequest({ + body: new PassThrough(), + hostname: mockHostname, + protocol: "ws:", + }) + ); + + // @ts-expect-error Property 'sockets' is private and only accessible within class 'WebSocketHandler'. + expect(handler.sockets[mockUrl]).toBeDefined(); + // @ts-expect-error Property 'sockets' is private and only accessible within class 'WebSocketHandler'. + expect(handler.sockets[mockUrl].length).toBe(1); + + await handler.handle( + new HttpRequest({ + body: new PassThrough(), + hostname: mockHostname, + protocol: "ws:", + }) + ); + + // @ts-expect-error Property 'sockets' is private and only accessible within class 'WebSocketHandler'. + expect(handler.sockets[mockUrl].length).toBe(2); + }); + + it("closes socket in socket pool on handler.destroy()", async () => { + const handler = new WebSocketFetchHandler(); + const server = new WS(mockUrl); + + await handler.handle( + new HttpRequest({ + body: new PassThrough(), + hostname: mockHostname, + protocol: "ws:", + }) + ); + + // @ts-expect-error Property 'sockets' is private and only accessible within class 'WebSocketHandler'. + const socket = handler.sockets[mockUrl][0]; + + expect(socket.readyState).toBe(WebSocket.OPEN); + handler.destroy(); + + // Verify that socket.close() is called + expect(socket.readyState).toBe(WebSocket.CLOSING); + }); + + it("should throw in output stream if input stream throws", async () => { + expect.assertions(3); + const handler = new WebSocketFetchHandler(); + //Using Node stream is fine because they are also async iterables. + const payload = new PassThrough(); + const server = new WS(mockUrl); + const { + response: { body: responsePayload }, + } = await handler.handle( + new HttpRequest({ + body: payload, + hostname: mockHostname, + protocol: "ws:", + }) + ); + await server.connected; + payload.emit("error", new Error("FakeError")); + try { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + for await (const chunk of responsePayload) { + /** pass */ + } + } catch (err) { + expect(err).toBeDefined(); + expect(err.message).toEqual("FakeError"); + // @ts-expect-error Property 'sockets' is private and only accessible within class 'WebSocketHandler'. + expect(handler.sockets[mockUrl].length).toBe(0); + } + }); + + it("should return retryable error if cannot setup ws connection", async () => { + expect.assertions(5); + const originalFn = setTimeout; + (global as any).setTimeout = jest.fn().mockImplementation(setTimeout); + const connectionTimeout = 1000; + const handler = new WebSocketFetchHandler(async () => ({ connectionTimeout })); + //Using Node stream is fine because they are also async iterables. + const payload = new PassThrough(); + const mockInvalidHostname = "localhost:9876"; + const mockInvalidUrl = `ws://${mockInvalidHostname}/`; + + try { + await handler.handle( + new HttpRequest({ + body: payload, + hostname: mockInvalidHostname, //invalid websocket endpoint + protocol: "ws:", + }) + ); + } catch (err) { + expect(err).toBeDefined(); + expect(err.$metadata).toBeDefined(); + expect(err.$metadata.httpStatusCode >= 500).toBe(true); + expect( + ((global as any).setTimeout as jest.Mock).mock.calls.filter((args) => { + //find the 'setTimeout' call from the websocket handler + return args[0].toString().indexOf("$metadata") >= 0; + })[0][1] + ).toBe(connectionTimeout); + // @ts-expect-error Property 'sockets' is private and only accessible within class 'WebSocketHandler'. + expect(handler.sockets[mockInvalidUrl].length).toBe(0); + } + (global as any).setTimeout = originalFn; + }); + }); + + describe("should handle http requests", () => { + it("should create fetch http handler at construction", () => { + new WebSocketFetchHandler(); + expect(FetchHttpHandler).toBeCalled(); + }); + + it("should make http request with fetch handler", async () => { + const httpHandler = new FetchHttpHandler(); + const handler = new WebSocketFetchHandler(undefined, httpHandler); + const request = new HttpRequest({}); + try { + await handler.handle(request); + } catch (e) {} + //@ts-ignore + expect(httpHandler.__proto__.handle).toHaveBeenCalledWith(request); + }); + }); +}); diff --git a/packages/middleware-sdk-transcribe-streaming/src/websocket-handler.ts b/packages/middleware-websocket/src/websocket-fetch-handler.ts similarity index 80% rename from packages/middleware-sdk-transcribe-streaming/src/websocket-handler.ts rename to packages/middleware-websocket/src/websocket-fetch-handler.ts index 5258400edf84..df89df13133f 100644 --- a/packages/middleware-sdk-transcribe-streaming/src/websocket-handler.ts +++ b/packages/middleware-websocket/src/websocket-fetch-handler.ts @@ -1,9 +1,14 @@ import { iterableToReadableStream, readableStreamtoIterable } from "@aws-sdk/eventstream-serde-browser"; -import { HttpHandler, HttpRequest, HttpResponse } from "@aws-sdk/protocol-http"; -import { RequestHandlerMetadata } from "@aws-sdk/types"; +import { FetchHttpHandler } from "@aws-sdk/fetch-http-handler"; +import { HttpRequest, HttpResponse } from "@aws-sdk/protocol-http"; +import { Provider, RequestHandler, RequestHandlerMetadata } from "@aws-sdk/types"; import { formatUrl } from "@aws-sdk/util-format-url"; -export interface WebSocketHandlerOptions { +import { isWebSocketRequest } from "./utils"; + +const DEFAULT_WS_CONNECTION_TIMEOUT_MS = 2000; + +export interface WebSocketFetchHandlerOptions { /** * The maximum time in milliseconds that the connection phase of a request * may take before the connection attempt is abandoned. @@ -12,20 +17,29 @@ export interface WebSocketHandlerOptions { } /** - * Base handler for websocket requests. By default, the request input and output + * Base handler for websocket requests and HTTP request. By default, the request input and output * body will be in a ReadableStream, because of interface consistency among middleware. * If ReadableStream is not available, like in React-Native, the response body * will be an async iterable. */ -export class WebSocketHandler implements HttpHandler { +export class WebSocketFetchHandler { public readonly metadata: RequestHandlerMetadata = { - handlerProtocol: "websocket", + handlerProtocol: "websocket/h1.1", }; - private readonly connectionTimeout: number; + private readonly configPromise: Promise; + private readonly httpHandler: RequestHandler; private readonly sockets: Record = {}; - constructor({ connectionTimeout }: WebSocketHandlerOptions = {}) { - this.connectionTimeout = connectionTimeout || 2000; + constructor( + options?: WebSocketFetchHandlerOptions | Provider, + httpHandler: RequestHandler = new FetchHttpHandler() + ) { + this.httpHandler = httpHandler; + if (typeof options === "function") { + this.configPromise = options().then((opts) => opts ?? {}); + } else { + this.configPromise = Promise.resolve(options ?? {}); + } } /** @@ -41,16 +55,10 @@ export class WebSocketHandler implements HttpHandler { } } - /** - * Removes all closing/closed sockets from the socket pool for URL. - */ - private removeNotUsableSockets(url: string): void { - this.sockets[url] = this.sockets[url].filter( - (socket) => ![WebSocket.CLOSING, WebSocket.CLOSED].includes(socket.readyState) - ); - } - async handle(request: HttpRequest): Promise<{ response: HttpResponse }> { + if (!isWebSocketRequest(request)) { + return this.httpHandler.handle(request); + } const url = formatUrl(request); const socket: WebSocket = new WebSocket(url); @@ -61,13 +69,12 @@ export class WebSocketHandler implements HttpHandler { this.sockets[url].push(socket); socket.binaryType = "arraybuffer"; - await this.waitForReady(socket, this.connectionTimeout); - + const { connectionTimeout = DEFAULT_WS_CONNECTION_TIMEOUT_MS } = await this.configPromise; + await this.waitForReady(socket, connectionTimeout); const { body } = request; const bodyStream = getIterator(body); const asyncIterable = this.connect(socket, bodyStream); const outputPayload = toReadableStream(asyncIterable); - return { response: new HttpResponse({ statusCode: 200, // indicates connection success @@ -76,6 +83,15 @@ export class WebSocketHandler implements HttpHandler { }; } + /** + * Removes all closing/closed sockets from the socket pool for URL. + */ + private removeNotUsableSockets(url: string): void { + this.sockets[url] = this.sockets[url].filter( + (socket) => ![WebSocket.CLOSING, WebSocket.CLOSED].includes(socket.readyState) + ); + } + private waitForReady(socket: WebSocket, connectionTimeout: number): Promise { return new Promise((resolve, reject) => { const timeout = setTimeout(() => { @@ -177,7 +193,7 @@ const getIterator = (stream: any): AsyncIterable => { return readableStreamtoIterable(stream); } - // For other types, just wrap them with an async iterable. + //For other types, just wrap them with an async iterable. return { [Symbol.asyncIterator]: async function* () { yield stream; diff --git a/packages/middleware-websocket/tsconfig.cjs.json b/packages/middleware-websocket/tsconfig.cjs.json new file mode 100644 index 000000000000..96198be81644 --- /dev/null +++ b/packages/middleware-websocket/tsconfig.cjs.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "outDir": "dist-cjs", + "rootDir": "src" + }, + "extends": "../../tsconfig.cjs.json", + "include": ["src/"] +} diff --git a/packages/middleware-websocket/tsconfig.es.json b/packages/middleware-websocket/tsconfig.es.json new file mode 100644 index 000000000000..7f162b266e26 --- /dev/null +++ b/packages/middleware-websocket/tsconfig.es.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "lib": [], + "outDir": "dist-es", + "rootDir": "src" + }, + "extends": "../../tsconfig.es.json", + "include": ["src/"] +} diff --git a/packages/middleware-websocket/tsconfig.types.json b/packages/middleware-websocket/tsconfig.types.json new file mode 100644 index 000000000000..6cdf9f52ea06 --- /dev/null +++ b/packages/middleware-websocket/tsconfig.types.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "declarationDir": "dist-types", + "rootDir": "src" + }, + "extends": "../../tsconfig.types.json", + "include": ["src/"] +} diff --git a/scripts/generate-clients/copy-to-clients.js b/scripts/generate-clients/copy-to-clients.js index fb220769d1e0..1537841f7889 100644 --- a/scripts/generate-clients/copy-to-clients.js +++ b/scripts/generate-clients/copy-to-clients.js @@ -49,7 +49,7 @@ const mergeManifest = (fromContent = {}, toContent = {}) => { "downlevel-dts": "0.10.1", rimraf: "3.0.2", typedoc: "0.23.23", - typescript: "~4.6.2", + typescript: "~4.9.5", }; fromContent[name] = Object.keys(fromContent[name]) .filter((dep) => Object.keys(devDepToVersionHash).includes(dep)) diff --git a/yarn.lock b/yarn.lock index 0a802e917ceb..4890ab5d9fd3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -28,6 +28,13 @@ "@aws-sdk/types" "^3.222.0" tslib "^1.11.1" +"@aws-crypto/ie11-detection@^2.0.0": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@aws-crypto/ie11-detection/-/ie11-detection-2.0.2.tgz#9c39f4a5558196636031a933ec1b4792de959d6a" + integrity sha512-5XDMQY98gMAf/WRTic5G++jfmS/VLM0rwpiOpaainKi4L0nqWMSB1SzsrEG5rjFZGYN6ZAefO+/Yta2dFM0kMw== + dependencies: + tslib "^1.11.1" + "@aws-crypto/ie11-detection@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@aws-crypto/ie11-detection/-/ie11-detection-3.0.0.tgz#640ae66b4ec3395cee6a8e94ebcd9f80c24cd688" @@ -48,6 +55,20 @@ "@aws-sdk/util-utf8-browser" "^3.0.0" tslib "^1.11.1" +"@aws-crypto/sha256-browser@2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@aws-crypto/sha256-browser/-/sha256-browser-2.0.0.tgz#741c9024df55ec59b51e5b1f5d806a4852699fb5" + integrity sha512-rYXOQ8BFOaqMEHJrLHul/25ckWH6GTJtdLSajhlqGMx0PmSueAuvboCuZCTqEKlxR8CQOwRarxYMZZSYlhRA1A== + dependencies: + "@aws-crypto/ie11-detection" "^2.0.0" + "@aws-crypto/sha256-js" "^2.0.0" + "@aws-crypto/supports-web-crypto" "^2.0.0" + "@aws-crypto/util" "^2.0.0" + "@aws-sdk/types" "^3.1.0" + "@aws-sdk/util-locate-window" "^3.0.0" + "@aws-sdk/util-utf8-browser" "^3.0.0" + tslib "^1.11.1" + "@aws-crypto/sha256-browser@3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@aws-crypto/sha256-browser/-/sha256-browser-3.0.0.tgz#05f160138ab893f1c6ba5be57cfd108f05827766" @@ -62,6 +83,15 @@ "@aws-sdk/util-utf8-browser" "^3.0.0" tslib "^1.11.1" +"@aws-crypto/sha256-js@2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@aws-crypto/sha256-js/-/sha256-js-2.0.0.tgz#f1f936039bdebd0b9e2dd834d65afdc2aac4efcb" + integrity sha512-VZY+mCY4Nmrs5WGfitmNqXzaE873fcIZDu54cbaDaaamsaTOP1DBImV9F4pICc3EHjQXujyE8jig+PFCaew9ig== + dependencies: + "@aws-crypto/util" "^2.0.0" + "@aws-sdk/types" "^3.1.0" + tslib "^1.11.1" + "@aws-crypto/sha256-js@3.0.0", "@aws-crypto/sha256-js@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@aws-crypto/sha256-js/-/sha256-js-3.0.0.tgz#f06b84d550d25521e60d2a0e2a90139341e007c2" @@ -71,6 +101,22 @@ "@aws-sdk/types" "^3.222.0" tslib "^1.11.1" +"@aws-crypto/sha256-js@^2.0.0": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@aws-crypto/sha256-js/-/sha256-js-2.0.2.tgz#c81e5d378b8a74ff1671b58632779986e50f4c99" + integrity sha512-iXLdKH19qPmIC73fVCrHWCSYjN/sxaAvZ3jNNyw6FclmHyjLKg0f69WlC9KTnyElxCR5MO9SKaG00VwlJwyAkQ== + dependencies: + "@aws-crypto/util" "^2.0.2" + "@aws-sdk/types" "^3.110.0" + tslib "^1.11.1" + +"@aws-crypto/supports-web-crypto@^2.0.0": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@aws-crypto/supports-web-crypto/-/supports-web-crypto-2.0.2.tgz#9f02aafad8789cac9c0ab5faaebb1ab8aa841338" + integrity sha512-6mbSsLHwZ99CTOOswvCRP3C+VCWnzBf+1SnbWxzzJ9lR0mA0JnY2JEAhp8rqmTE0GPFy88rrM27ffgp62oErMQ== + dependencies: + tslib "^1.11.1" + "@aws-crypto/supports-web-crypto@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@aws-crypto/supports-web-crypto/-/supports-web-crypto-3.0.0.tgz#5d1bf825afa8072af2717c3e455f35cda0103ec2" @@ -78,6 +124,15 @@ dependencies: tslib "^1.11.1" +"@aws-crypto/util@^2.0.0", "@aws-crypto/util@^2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@aws-crypto/util/-/util-2.0.2.tgz#adf5ff5dfbc7713082f897f1d01e551ce0edb9c0" + integrity sha512-Lgu5v/0e/BcrZ5m/IWqzPUf3UYFTy/PpeED+uc9SWUR1iZQL8XXbGQg10UfllwwBryO3hFF5dizK+78aoXC1eA== + dependencies: + "@aws-sdk/types" "^3.110.0" + "@aws-sdk/util-utf8-browser" "^3.0.0" + tslib "^1.11.1" + "@aws-crypto/util@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@aws-crypto/util/-/util-3.0.0.tgz#1c7ca90c29293f0883468ad48117937f0fe5bfb0" @@ -4847,6 +4902,15 @@ downlevel-dts@0.10.1: shelljs "^0.8.3" typescript next +downlevel-dts@0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/downlevel-dts/-/downlevel-dts-0.7.0.tgz#155c24310dad8a4820ad077e64b15a8c461aa932" + integrity sha512-tcjGqElN0/oad/LJBlaxmZ3zOYEQOBbGuirKzif8s1jeRiWBdNX9H6OBFlRjOQkosXgV+qSjs4osuGCivyZ4Jw== + dependencies: + semver "^7.3.2" + shelljs "^0.8.3" + typescript "^4.1.0-dev.20201026" + duplexer2@~0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc1" @@ -11607,11 +11671,21 @@ typedoc@0.23.23: minimatch "^5.1.1" shiki "^0.11.1" -"typescript@^3 || ^4", typescript@^4.6.4: +"typescript@^3 || ^4": version "4.9.4" resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.4.tgz#a2a3d2756c079abda241d75f149df9d561091e78" integrity sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg== +typescript@^4.1.0-dev.20201026, typescript@~4.9.5: + version "4.9.5" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" + integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== + +typescript@^4.6.4: + version "4.7.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.4.tgz#1a88596d1cf47d59507a1bcdfb5b9dfe4d488235" + integrity sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ== + typescript@next: version "5.0.0-dev.20230116" resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.0.0-dev.20230116.tgz#7eb84b0b54f7c0a54dba03349ad84421d74a0f76" @@ -12024,7 +12098,7 @@ wcwidth@^1.0.0, wcwidth@^1.0.1: dependencies: defaults "^1.0.3" -web-streams-polyfill@^3.0.0: +web-streams-polyfill@3.2.1, web-streams-polyfill@^3.0.0: version "3.2.1" resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz#71c2718c52b45fd49dbeee88634b3a60ceab42a6" integrity sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==