From 5889622627669f1f8264a8eb6c79e4c9efe26c54 Mon Sep 17 00:00:00 2001 From: Tim Nguyen Date: Wed, 10 Aug 2022 13:44:12 -0400 Subject: [PATCH 001/621] fix: runLocally.sh script can handle config file with comments (#446) Co-authored-by: Tim Nguyen --- solutions/swb-reference/scripts/runLocally.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/solutions/swb-reference/scripts/runLocally.sh b/solutions/swb-reference/scripts/runLocally.sh index 7adfd643cd..3cc521598c 100755 --- a/solutions/swb-reference/scripts/runLocally.sh +++ b/solutions/swb-reference/scripts/runLocally.sh @@ -1,6 +1,6 @@ #!/bin/bash -shortName=$(cat src/config/${STAGE}.yaml | grep awsRegionShortName: | awk '{print $NF}') -region=$(cat src/config/${STAGE}.yaml | grep awsRegion: | awk '{print $NF}') +shortName=$(cat src/config/${STAGE}.yaml | grep awsRegionShortName: | cut -f1 -d "#" | awk '{print $NF}') +region=$(cat src/config/${STAGE}.yaml | grep awsRegion: | cut -f1 -d "#" | awk '{print $NF}') stackName="swb-${STAGE}-${shortName}" # SWBStack.ts read this value to set up API to be run locally From 1bd09a7ac7913d5c0e8e4c5900d3ae35916153ae Mon Sep 17 00:00:00 2001 From: Tim Nguyen Date: Wed, 10 Aug 2022 17:21:03 -0400 Subject: [PATCH 002/621] fix: download cfn template file to create md5 sum because encrypted S3 files Etag doesn't match md5 (#440) --- .../serviceCatalogSetup.test.ts | 24 +++++++++++---- .../src/postDeployment/serviceCatalogSetup.ts | 30 ++++++++++++++----- 2 files changed, 42 insertions(+), 12 deletions(-) diff --git a/workbench-core/environments/src/postDeployment/serviceCatalogSetup.test.ts b/workbench-core/environments/src/postDeployment/serviceCatalogSetup.test.ts index 676104e2a0..4faa2ad005 100644 --- a/workbench-core/environments/src/postDeployment/serviceCatalogSetup.test.ts +++ b/workbench-core/environments/src/postDeployment/serviceCatalogSetup.test.ts @@ -5,8 +5,9 @@ jest.mock('md5-file'); +import { Readable } from 'stream'; import { CloudFormationClient, DescribeStacksCommand } from '@aws-sdk/client-cloudformation'; -import { ListObjectsCommand, S3Client } from '@aws-sdk/client-s3'; +import { GetObjectCommand, ListObjectsCommand, S3Client } from '@aws-sdk/client-s3'; import { CreateConstraintCommand, CreatePortfolioCommand, @@ -246,17 +247,30 @@ describe('ServiceCatalogSetup', () => { ] }); - // Mock Get S3 files + // Mock Get List of S3 files s3Mock.on(ListObjectsCommand).resolves({ Contents: [ { - Key: `${constants.S3_ARTIFACT_BUCKET_SC_PREFIX}sagemaker.cfn.yaml`, - ETag: 'abc123' + Key: `${constants.S3_ARTIFACT_BUCKET_SC_PREFIX}sagemaker.cfn.yaml` } ] }); + + // Mock Reading S3 File + const readableStream = new Readable({ + read() {} + }); + + readableStream.push('ABC'); + readableStream.push(null); + + s3Mock.on(GetObjectCommand).resolves({ + Body: readableStream + }); + + // MD5 Sum of S3 file md5File.sync = jest.fn(() => { - return 'abc123'; + return '902fbdd2b1df0c4f70b4a5d23525e932'; }); // Mock Create Launch Constraint diff --git a/workbench-core/environments/src/postDeployment/serviceCatalogSetup.ts b/workbench-core/environments/src/postDeployment/serviceCatalogSetup.ts index 03d780d99e..0a0ff4be1b 100644 --- a/workbench-core/environments/src/postDeployment/serviceCatalogSetup.ts +++ b/workbench-core/environments/src/postDeployment/serviceCatalogSetup.ts @@ -5,9 +5,12 @@ /* eslint-disable security/detect-non-literal-fs-filename */ +import crypto from 'crypto'; import fs from 'fs'; import { join } from 'path'; +import { Readable } from 'stream'; +import { _Object } from '@aws-sdk/client-s3'; import { InvalidParametersException, ProductViewDetail } from '@aws-sdk/client-service-catalog'; import { AwsService } from '@aws/workbench-core-base'; @@ -181,18 +184,31 @@ export default class ServiceCatalogSetup { }; const listObjectOutput = await this._aws.clients.s3.listObjects(listS3ObjectsParam); - - const S3FileNameToEtag: { [key: string]: string } = {}; + const S3FileNameToMd5Sum: { [key: string]: string } = {}; if (listObjectOutput.Contents) { - listObjectOutput.Contents.forEach((content) => { - if (content.Key && content.ETag) { + for (let i = 0; i < listObjectOutput.Contents.length; i++) { + // eslint-disable-next-line security/detect-object-injection + const content: _Object = listObjectOutput.Contents[i]; + if (content.Key) { + const getObjResponse = await this._aws.clients.s3.getObject({ Bucket: s3Bucket, Key: content.Key }); + const streamToString = (stream: Readable): Promise => + new Promise((resolve, reject) => { + const chunks: Uint8Array[] = []; + stream.on('data', (chunk) => chunks.push(chunk)); + stream.on('error', reject); + stream.on('end', () => resolve(Buffer.concat(chunks).toString('utf8'))); + }); + const s3FileMd5Sum: string = crypto + .createHash('md5') + .update(await streamToString(getObjResponse.Body! as Readable)) + .digest('hex'); const S3FileName = content.Key.split('/').pop(); if (S3FileName) { // eslint-disable-next-line security/detect-object-injection - S3FileNameToEtag[S3FileName] = content.ETag.replace(/"/g, ''); + S3FileNameToMd5Sum[S3FileName] = s3FileMd5Sum; } } - }); + } } const envsToFilePath: { [key: string]: string } = {}; cfnFilePaths.forEach((filePath: string) => { @@ -200,7 +216,7 @@ export default class ServiceCatalogSetup { if (fileName) { const localCfnTemplateMd5Sum = md5File.sync(cfnFilePaths[0]); // eslint-disable-next-line security/detect-object-injection - if (localCfnTemplateMd5Sum !== S3FileNameToEtag[fileName]) { + if (localCfnTemplateMd5Sum !== S3FileNameToMd5Sum[fileName]) { const envType = fileName.replace('.cfn.yaml', ''); // eslint-disable-next-line security/detect-object-injection envsToFilePath[envType] = filePath; From fa365d9cdd76fcc6ddb5ffe52a0274293aad3e3b Mon Sep 17 00:00:00 2001 From: nguyen102 Date: Wed, 10 Aug 2022 21:29:54 +0000 Subject: [PATCH 003/621] docs: Auto README.md updates [skip ci] --- README.md | 2 +- workbench-core/environments/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d3cb85404f..fda88a9efa 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ # Code Coverage | Statements | Branches | Functions | Lines | | --------------------------- | ----------------------- | ------------------------- | ----------------- | -| ![Statements](https://img.shields.io/badge/statements-87.08%25-yellow.svg?style=flat) | ![Branches](https://img.shields.io/badge/branches-84.88%25-yellow.svg?style=flat) | ![Functions](https://img.shields.io/badge/functions-88.44%25-yellow.svg?style=flat) | ![Lines](https://img.shields.io/badge/lines-87.59%25-yellow.svg?style=flat) | +| ![Statements](https://img.shields.io/badge/statements-87.14%25-yellow.svg?style=flat) | ![Branches](https://img.shields.io/badge/branches-84.85%25-yellow.svg?style=flat) | ![Functions](https://img.shields.io/badge/functions-88.5%25-yellow.svg?style=flat) | ![Lines](https://img.shields.io/badge/lines-87.64%25-yellow.svg?style=flat) | # Contributing Guidelines diff --git a/workbench-core/environments/README.md b/workbench-core/environments/README.md index 10c607ad18..0cbe3d2ba5 100644 --- a/workbench-core/environments/README.md +++ b/workbench-core/environments/README.md @@ -5,7 +5,7 @@ # Code Coverage | Statements | Branches | Functions | Lines | | --------------------------- | ----------------------- | ------------------------- | ----------------- | -| ![Statements](https://img.shields.io/badge/statements-78.83%25-red.svg?style=flat) | ![Branches](https://img.shields.io/badge/branches-71.8%25-red.svg?style=flat) | ![Functions](https://img.shields.io/badge/functions-81.33%25-yellow.svg?style=flat) | ![Lines](https://img.shields.io/badge/lines-78.8%25-red.svg?style=flat) | +| ![Statements](https://img.shields.io/badge/statements-79.12%25-red.svg?style=flat) | ![Branches](https://img.shields.io/badge/branches-71.59%25-red.svg?style=flat) | ![Functions](https://img.shields.io/badge/functions-81.69%25-yellow.svg?style=flat) | ![Lines](https://img.shields.io/badge/lines-79.03%25-red.svg?style=flat) | This project provides the library and utilities function for setting up and managing environments. For an example of how this project can be used, please refer to [swb-reference](../../solutions/swb-reference). From 933366520ee3d82eb1f3b4ed518289f8516e8b11 Mon Sep 17 00:00:00 2001 From: Tim Nguyen Date: Thu, 11 Aug 2022 15:53:54 -0400 Subject: [PATCH 004/621] docs: update documentation with information about CWL (#448) * docs: add CWL information in README * chore: pr comments Co-authored-by: Tim Nguyen --- solutions/swb-reference/README.md | 6 ++++++ solutions/swb-reference/SETUP_v2p1.md | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/solutions/swb-reference/README.md b/solutions/swb-reference/README.md index f87b671a56..f099a01f6e 100644 --- a/solutions/swb-reference/README.md +++ b/solutions/swb-reference/README.md @@ -118,6 +118,12 @@ clientId: '' 4. Run `STAGE= node generateCognitoToken.js ''` with the correct value for `` and ``. It should be a user that has been created for your SWB deployment. Note, the quotes around `` is necessary for the script to correctly parse passwords that have symbols in it. 5. In the console output, use the `accessToken` that is provided to make authenticated API requests. +## Appendix +### Cloudwatch Logs +* `swb---apiLambda`: Logs for api lambda. This lambda gets executed when user makes a request to SWB APIs. +* `swb---accountHandlerLambda`: Logs for account handler lambda. This lamba runs every 5 minutes and is responsible for keeping the hosting account resources in sync with the main account. +* `swb---statusHandlerLambda`: Logs for status handler lambda. This lambda is triggered by EventBridge events that originated in hosting accounts. It updates DDB with environment statuses from the hosting accounts. +* ## FAQ 1. **Why is there `jest.config.js` and `config/jest.config.json`?** diff --git a/solutions/swb-reference/SETUP_v2p1.md b/solutions/swb-reference/SETUP_v2p1.md index 551278f68e..11d97f358d 100644 --- a/solutions/swb-reference/SETUP_v2p1.md +++ b/solutions/swb-reference/SETUP_v2p1.md @@ -107,7 +107,7 @@ VPC VpcSubnet ``` -## Setup UI and Get access token +## Get access token To get the `access_token` for making authenticated API requests please refer [here](README.md#obtain-access-token-for-making-authenticated-api-requests). @@ -272,7 +272,7 @@ POST `{{API_URL}}/aws-accounts` Wait for account handler to run. It runs once every 5 minutes. You'll know that it's completed when the account status is listed as `CURRENT` in DDB. You can find cloudwatch logs for the account handler in the `Main account`. It's at `aws/lambda/swb---accountHandlerLambda` -## Setup UI and Get access token +## Setup UI Follow the instructions [here](../swb-ui/README.md#deploy-ui-to-aws) to deploy the SWB UI to AWS. From 90a4c93ae98c85e5dd882d60116c9b5529f24c67 Mon Sep 17 00:00:00 2001 From: FernandoAranda Date: Thu, 11 Aug 2022 14:39:54 -0700 Subject: [PATCH 005/621] fix(swb deployment): fix files outputs to be json (#455) * fix(swb deployment): fix files outputs to be json * fix(remove extra ignore): remove extra ignore command Co-authored-by: Fernando Aranda --- .gitignore | 4 ++-- solutions/swb-reference/package.json | 2 +- solutions/swb-reference/src/constants.ts | 2 +- solutions/swb-ui/infrastructure/package.json | 2 +- solutions/swb-ui/infrastructure/src/constants.ts | 2 +- solutions/swb-ui/scripts/deploy.sh | 2 +- solutions/swb-ui/scripts/dev.sh | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index a257ee6020..47cb4e252f 100644 --- a/.gitignore +++ b/.gitignore @@ -96,7 +96,7 @@ solutions/swb-reference/integration-tests/config/*.yaml !solutions/swb-reference/integration-tests/config/example.yaml !solutions/swb-reference/src/config/example.yaml !solutions/swb-reference/src/config/testEnv.yaml -solutions/swb-reference/src/config/*.js +solutions/swb-reference/src/config/*.json solutions/swb-reference/.aws-sam/ # local environment variables @@ -110,6 +110,6 @@ solutions/swb-ui/out #swb-ui cdk-infrastructure solutions/swb-ui/infrastructure/src/config/*.yaml -solutions/swb-ui/infrastructure/src/config/*.js +solutions/swb-ui/infrastructure/src/config/*.json !solutions/swb-ui/infrastructure/src/config/example.yaml diff --git a/solutions/swb-reference/package.json b/solutions/swb-reference/package.json index d9f90e2f5a..03bc33fff5 100644 --- a/solutions/swb-reference/package.json +++ b/solutions/swb-reference/package.json @@ -16,7 +16,7 @@ "build": "heft build --clean && rushx pkg-json-lint", "build:test": "heft test --clean && rushx pkg-json-lint && rushx make-badges", "cdk": "cdk", - "cdk-deploy": "rush build && rushx compile && cdk deploy --all --require-approval never --outputs-file ./src/config/${STAGE}.js", + "cdk-deploy": "rush build && rushx compile && cdk deploy --all --require-approval never --outputs-file ./src/config/${STAGE}.json", "cdk-deploy:noBuild": "rushx compile && cdk deploy --all", "compile": "scripts/buildLambda.js", "depcheck": "depcheck", diff --git a/solutions/swb-reference/src/constants.ts b/solutions/swb-reference/src/constants.ts index f89e7357a9..ec3f504a5b 100644 --- a/solutions/swb-reference/src/constants.ts +++ b/solutions/swb-reference/src/constants.ts @@ -111,7 +111,7 @@ function getUiClientUrl(): string { // correct file // eslint-disable-next-line security/detect-non-literal-fs-filename fs.readFileSync( - join(__dirname, `../../../swb-ui/infrastructure/src/config/${process.env.STAGE}.js`), + join(__dirname, `../../../swb-ui/infrastructure/src/config/${process.env.STAGE}.json`), 'utf8' ) // nosemgrep ); diff --git a/solutions/swb-ui/infrastructure/package.json b/solutions/swb-ui/infrastructure/package.json index de27909fe7..23b9fd9460 100644 --- a/solutions/swb-ui/infrastructure/package.json +++ b/solutions/swb-ui/infrastructure/package.json @@ -19,7 +19,7 @@ "build": "heft build --clean", "build:test": "rushx build && rushx test", "cdk": "cdk", - "cdk-deploy": "rushx build && cdk deploy --all --require-approval never --outputs-file ./src/config/${STAGE}.js", + "cdk-deploy": "rushx build && cdk deploy --all --require-approval never --outputs-file ./src/config/${STAGE}.json", "sort-package-json": "sort-package-json package.json", "test": "rushx test:only", "test:only": "heft test --clean --no-build", diff --git a/solutions/swb-ui/infrastructure/src/constants.ts b/solutions/swb-ui/infrastructure/src/constants.ts index 1ae70ea262..a33cc4f7f3 100644 --- a/solutions/swb-ui/infrastructure/src/constants.ts +++ b/solutions/swb-ui/infrastructure/src/constants.ts @@ -73,7 +73,7 @@ function getAPIOutputs(): { awsRegionShortName: string; apiUrlOutput: string; aw // __dirname is a variable that reference the current directory. We use it so we can dynamically navigate to the // correct file // eslint-disable-next-line security/detect-non-literal-fs-filename - fs.readFileSync(join(__dirname, `../../../swb-reference/src/config/${process.env.STAGE}.js`), 'utf8') // nosemgrep + fs.readFileSync(join(__dirname, `../../../swb-reference/src/config/${process.env.STAGE}.json`), 'utf8') // nosemgrep ); const apiStackName = Object.entries(apiStackOutputs).map(([key, value]) => key)[0]; //output has a format { stackname: {...props} } // eslint-disable-next-line security/detect-object-injection diff --git a/solutions/swb-ui/scripts/deploy.sh b/solutions/swb-ui/scripts/deploy.sh index caf80bbdaf..f13c040578 100755 --- a/solutions/swb-ui/scripts/deploy.sh +++ b/solutions/swb-ui/scripts/deploy.sh @@ -1,5 +1,5 @@ ##This script can only be run by using the command `STAGE= rushx deploy-ui-and-api` from `/swb-ui` directory, running this script directly from this directory will not work correctly as it uses relative paths. -apiURL=$(cat ../swb-reference/src/config/${STAGE}.js | grep apiUrlOutput | awk '{print $NF}' | sed 's/\"//g' | sed 's/,//g' ) ##Get value from swb-reference/src/config/{STAGE}.js and replace all '"' and ',' with empty. +apiURL=$(cat ../swb-reference/src/config/${STAGE}.json | grep apiUrlOutput | awk '{print $NF}' | sed 's/\"//g' | sed 's/,//g' ) ##Get value from swb-reference/src/config/{STAGE}.json and replace all '"' and ',' with empty. if [[ -z $apiURL ]]; ## Validate not empty apiURL then echo Configuration with STAGE='"'${STAGE}'"' not found, make sure to deploy API with STAGE=${STAGE} and try again. diff --git a/solutions/swb-ui/scripts/dev.sh b/solutions/swb-ui/scripts/dev.sh index 66e5637d1f..f1f0dea201 100755 --- a/solutions/swb-ui/scripts/dev.sh +++ b/solutions/swb-ui/scripts/dev.sh @@ -1,5 +1,5 @@ ##This script can only be run by using the command `STAGE= rushx dev` from `/swb-ui` directory, running this script directly from this directory will not work correctly as it uses relative paths. -apiURL=$(cat ../swb-reference/src/config/${STAGE}.js | grep apiUrlOutput | awk '{print $NF}' | sed 's/\"//g' | sed 's/,//g' ) ##Get value from swb-reference/src/config/{STAGE}.js and replace all '"' and ',' with empty. +apiURL=$(cat ../swb-reference/src/config/${STAGE}.json| grep apiUrlOutput | awk '{print $NF}' | sed 's/\"//g' | sed 's/,//g' ) ##Get value from swb-reference/src/config/{STAGE}.json and replace all '"' and ',' with empty. if [[ -z $apiURL ]]; ## Validate not empty apiURL then echo Configuration with STAGE='"'${STAGE}'"' not found, make sure to deploy API with STAGE=${STAGE} and try again. From f614bf2b9c48630e80378fd1d4e78f997d6cc85e Mon Sep 17 00:00:00 2001 From: Sanket Dharwadkar Date: Fri, 12 Aug 2022 07:22:47 -0700 Subject: [PATCH 006/621] test: integration test SWB env with DS (#453) * test: add complex test for DS SWB integration --- common/config/rush/pnpm-lock.yaml | 2 + solutions/swb-reference/README.md | 15 +-- .../integration-tests/config/example.yaml | 9 +- .../support/clientSession.ts | 2 +- .../support/complex/datasetHelper.ts | 52 ++++++++ .../integration-tests/support/resources.ts | 5 +- .../resources/base/collectionResource.ts | 1 + .../support/resources/datasets/dataset.ts | 30 +++++ .../support/resources/datasets/datasets.ts | 42 ++++++ .../resources/environments/environment.ts | 22 ++-- .../integration-tests/support/setup.ts | 17 ++- .../support/utils/settings.ts | 33 ++++- .../tests/isolated/datasets/create.test.ts | 120 ++++++++++++++++++ .../isolated/environments/terminate.test.ts | 3 +- .../tests/multiStep/dataset.test.ts | 78 ++++++++++++ solutions/swb-reference/jest.config.js | 17 +++ solutions/swb-reference/package.json | 1 + .../scripts/generateCognitoTokens.js | 17 ++- workbench-core/base/src/index.ts | 2 +- 19 files changed, 429 insertions(+), 39 deletions(-) create mode 100644 solutions/swb-reference/integration-tests/support/complex/datasetHelper.ts create mode 100644 solutions/swb-reference/integration-tests/support/resources/datasets/dataset.ts create mode 100644 solutions/swb-reference/integration-tests/support/resources/datasets/datasets.ts create mode 100644 solutions/swb-reference/integration-tests/tests/isolated/datasets/create.test.ts create mode 100644 solutions/swb-reference/integration-tests/tests/multiStep/dataset.test.ts diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index 317d548f1f..8c92e2f3f2 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -176,6 +176,7 @@ importers: '@aws-sdk/client-cloudformation': ^3.118.1 '@aws-sdk/client-dynamodb': ^3.118.1 '@aws-sdk/client-s3': ^3.118.1 + '@aws-sdk/client-s3-control': ^3.118.1 '@aws-sdk/client-sagemaker': ^3.118.1 '@aws-sdk/client-service-catalog': ^3.118.1 '@aws-sdk/client-ssm': ^3.118.1 @@ -230,6 +231,7 @@ importers: '@aws-sdk/client-cloudformation': 3.130.0 '@aws-sdk/client-dynamodb': 3.130.0 '@aws-sdk/client-s3': 3.130.0 + '@aws-sdk/client-s3-control': 3.130.0 '@aws-sdk/client-service-catalog': 3.130.0 '@aws-sdk/client-ssm': 3.130.0 '@aws-sdk/client-sts': 3.130.0 diff --git a/solutions/swb-reference/README.md b/solutions/swb-reference/README.md index f099a01f6e..e2486ce4b0 100644 --- a/solutions/swb-reference/README.md +++ b/solutions/swb-reference/README.md @@ -106,17 +106,10 @@ const { data: response } = await adminSession.resources.environments.get({status Go to `solutions/swb-app` to update `staticRouteConfig.ts` and `staticPermissionsConfig.ts` with any necessary changes to routes/permissions. ## Obtain Access Token for making authenticated API requests -1. Create a staging file in `integration-tests/config` with these three fields filled out -```yaml -#Cognito Integ Test Client -awsRegion: '' -userPoolId: '' -clientId: '' -``` -2. Go to `swb-reference/scripts` folder -3. Pull down all required dependencies by running `rushx build` -4. Run `STAGE= node generateCognitoToken.js ''` with the correct value for `` and ``. It should be a user that has been created for your SWB deployment. Note, the quotes around `` is necessary for the script to correctly parse passwords that have symbols in it. -5. In the console output, use the `accessToken` that is provided to make authenticated API requests. +1. Go to `swb-reference/scripts` folder +2. Pull down all required dependencies by running `rushx build` +3. Run `STAGE= node generateCognitoToken.js ''` with the correct value for `` and ``. It should be a user that has been created for your SWB deployment. Note, the quotes around `` is necessary for the script to correctly parse passwords that have symbols in it. +4. In the console output, use the `accessToken` that is provided to make authenticated API requests. ## Appendix ### Cloudwatch Logs diff --git a/solutions/swb-reference/integration-tests/config/example.yaml b/solutions/swb-reference/integration-tests/config/example.yaml index 6afefe0a7a..5a2dedd528 100644 --- a/solutions/swb-reference/integration-tests/config/example.yaml +++ b/solutions/swb-reference/integration-tests/config/example.yaml @@ -1,4 +1,6 @@ -#apiBaseUrl: '' +## Note: +## Please deploy the swb-reference package before running the integration test suite. +## Settings declared here are merged with the main accounts CFN outputs to create integration test config items # Default Env Config for launching a new environment #envTypeId: '' @@ -7,11 +9,8 @@ #envType: '' # Negative Testing -#alreadyTerminateEnvId: '' # id of an environment that has already been terminated +#terminatedEnvId: '' # id of an environment that has already been terminated #Cognito Integ Test Client -#awsRegion: '' -#userPoolId: '' -#clientId: '' #rootUsername: '' #rootPasswordParamStorePath: '' \ No newline at end of file diff --git a/solutions/swb-reference/integration-tests/support/clientSession.ts b/solutions/swb-reference/integration-tests/support/clientSession.ts index e4f1f94b43..8b9dace7bb 100644 --- a/solutions/swb-reference/integration-tests/support/clientSession.ts +++ b/solutions/swb-reference/integration-tests/support/clientSession.ts @@ -34,7 +34,7 @@ export default class ClientSession { } this._axiosInstance = axios.create({ - baseURL: this._settings.get('apiBaseUrl'), + baseURL: this._settings.get('apiUrlOutput'), timeout: 30000, // 30 seconds to mimic API gateway timeout headers }); diff --git a/solutions/swb-reference/integration-tests/support/complex/datasetHelper.ts b/solutions/swb-reference/integration-tests/support/complex/datasetHelper.ts new file mode 100644 index 0000000000..d2583f9a12 --- /dev/null +++ b/solutions/swb-reference/integration-tests/support/complex/datasetHelper.ts @@ -0,0 +1,52 @@ +import { AccessPoint } from '@aws-sdk/client-s3-control'; +import { AwsService } from "@aws/workbench-core-base"; +import _ from "lodash"; +import ClientSession from "../clientSession"; + +export class DatasetHelper { + private _awsSdk: AwsService; + private _adminSession: ClientSession; + public constructor(awsSdkClient: AwsService, adminSession: ClientSession) { + this._awsSdk = awsSdkClient; + this._adminSession = adminSession; + } + + public async listAccessPoints(bucket: string, accountId: string): Promise> { + const response = await this._awsSdk.clients.s3Control.listAccessPoints({AccountId: accountId, Bucket: bucket})!; + return response.AccessPointList!; + } + + public async deleteS3Resources(bucket: string, dir: string): Promise { + const listedObjects = await this._awsSdk.clients.s3.listObjectsV2({Bucket: bucket,Prefix: dir})!; + if (listedObjects.Contents!.length === 0) return; + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const deleteParams: {Bucket: string, Delete: {Objects: any}} = {Bucket: bucket,Delete: { Objects: [] }}; + listedObjects.Contents!.forEach((key) => { + deleteParams.Delete.Objects.push({ Key: key.Key }); + }); + + await this._awsSdk.clients.s3.deleteObjects(deleteParams); + if (listedObjects.IsTruncated) await this.deleteS3Resources(bucket, dir); + } + + public async deleteDdbRecords(dataSetId: string): Promise { + await this._awsSdk.helpers.ddb.delete({pk: `DATASET#${dataSetId}`, sk: `DATASET#${dataSetId}`}).execute(); + const data = await this._awsSdk.helpers.ddb + .query({ + key: { + name: 'pk', + value: `DATASET#${dataSetId}` + } + }).execute(); + if (data.Count === 0) return; + + const endpoints = data.Items!; + // Tests are not expected to create more than a couple of endpoints per DS max, so no support needed for pagintated query results + await Promise.all( + _.map(endpoints, async endpoint => { + await this._awsSdk.helpers.ddb.delete({pk: endpoint.pk, sk: endpoint.sk}).execute(); + }) + ); + } +} \ No newline at end of file diff --git a/solutions/swb-reference/integration-tests/support/resources.ts b/solutions/swb-reference/integration-tests/support/resources.ts index 7106f6975d..0ae56e4d63 100644 --- a/solutions/swb-reference/integration-tests/support/resources.ts +++ b/solutions/swb-reference/integration-tests/support/resources.ts @@ -3,16 +3,19 @@ * SPDX-License-Identifier: Apache-2.0 */ import ClientSession from './clientSession'; +import Datasets from './resources/datasets/datasets'; import Environments from './resources/environments/environments'; function getResources(clientSession: ClientSession): Resources { return { - environments: new Environments(clientSession) + environments: new Environments(clientSession), + datasets: new Datasets(clientSession) }; } interface Resources { environments: Environments; + datasets: Datasets; } export { getResources, Resources }; diff --git a/solutions/swb-reference/integration-tests/support/resources/base/collectionResource.ts b/solutions/swb-reference/integration-tests/support/resources/base/collectionResource.ts index d92d3ced4f..cddf286cc6 100644 --- a/solutions/swb-reference/integration-tests/support/resources/base/collectionResource.ts +++ b/solutions/swb-reference/integration-tests/support/resources/base/collectionResource.ts @@ -55,6 +55,7 @@ export default class CollectionResource { return response; } + // List call public async get(queryParams: { [key: string]: string }): Promise { return this._axiosInstance.get(this._api, { params: queryParams }); } diff --git a/solutions/swb-reference/integration-tests/support/resources/datasets/dataset.ts b/solutions/swb-reference/integration-tests/support/resources/datasets/dataset.ts new file mode 100644 index 0000000000..60b11ed947 --- /dev/null +++ b/solutions/swb-reference/integration-tests/support/resources/datasets/dataset.ts @@ -0,0 +1,30 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { AxiosResponse } from 'axios'; +import ClientSession from '../../clientSession'; +import { DatasetHelper } from '../../complex/datasetHelper'; +import Resource from '../base/resource'; + +export default class Dataset extends Resource { + public constructor(id: string, clientSession: ClientSession, parentApi: string) { + super(clientSession, 'dataset', id, parentApi); + } + + public async share(requestBody: {[id: string]: string}): Promise { + return this._axiosInstance.post(`${this._api}/share`, requestBody); + } + + protected async cleanup(): Promise { + const defAdminSession = await this._setup.getDefaultAdminSession(); + const { data: resource } = await defAdminSession.resources.datasets.dataset(this._id).get(); + const { storageName, path } = resource; + + // Delete DDB entries, and path folder from bucket (to prevent test resources polluting a prod env) + const datasetHelper = new DatasetHelper(this._setup.getMainAwsClient(), defAdminSession); + await datasetHelper.deleteS3Resources(storageName, path); + await datasetHelper.deleteDdbRecords(this._id); + } +} diff --git a/solutions/swb-reference/integration-tests/support/resources/datasets/datasets.ts b/solutions/swb-reference/integration-tests/support/resources/datasets/datasets.ts new file mode 100644 index 0000000000..1338af569f --- /dev/null +++ b/solutions/swb-reference/integration-tests/support/resources/datasets/datasets.ts @@ -0,0 +1,42 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +import { AxiosResponse } from 'axios'; +import ClientSession from '../../clientSession'; +import RandomTextGenerator from '../../utils/randomTextGenerator'; +import CollectionResource from '../base/collectionResource'; +import Dataset from './dataset'; + +export default class Datasets extends CollectionResource { + public constructor(clientSession: ClientSession) { + super(clientSession, 'datasets', 'dataset'); + this._api = 'datasets'; + } + + public dataset(id: string): Dataset { + return new Dataset(id, this._clientSession, this._api); + } + + public async import(requestBody: {[id: string]: string}): Promise { + return this._axiosInstance.post(`${this._api}/import`, requestBody); + } + + protected _buildDefaults(resource: DataSetCreateRequest): DataSetCreateRequest { + const randomTextGenerator = new RandomTextGenerator(this._settings.get('runId')); + const dataSetName = randomTextGenerator.getFakeText('test-DS'); + return { + datasetName: resource.datasetName ?? dataSetName, + path: resource.path ?? dataSetName, + storageName: resource.storageName, + awsAccountId: resource.awsAccountId + }; + } +} + +interface DataSetCreateRequest { + datasetName: string, + storageName: string, + path: string, + awsAccountId: string +} diff --git a/solutions/swb-reference/integration-tests/support/resources/environments/environment.ts b/solutions/swb-reference/integration-tests/support/resources/environments/environment.ts index b8c23f80fe..67c47e4f81 100644 --- a/solutions/swb-reference/integration-tests/support/resources/environments/environment.ts +++ b/solutions/swb-reference/integration-tests/support/resources/environments/environment.ts @@ -37,16 +37,22 @@ export default class Environment extends Resource { // Exit early because environment has already been terminated return; } - while (envStatus === 'PENDING' && totalTimeWaitedInSeconds < maxWaitTimeInSeconds) { - await sleep(15000); - const { data: resource } = await defAdminSession.resources.environments.environment(this._id).get(); - envStatus = resource.status; - totalTimeWaitedInSeconds = (Date.now() - startTimeInMs) / 1000; + try{ + console.log(`Attempting to delete environment ${this._id}. This will take a few minutes.`) + while (envStatus === 'PENDING' && totalTimeWaitedInSeconds < maxWaitTimeInSeconds) { + await sleep(15000); + const { data: resource } = await defAdminSession.resources.environments.environment(this._id).get(); + envStatus = resource.status; + totalTimeWaitedInSeconds = (Date.now() - startTimeInMs) / 1000; + } + await defAdminSession.resources.environments.environment(this._id).terminate(); + console.log(`Deleted environment ${this._id}`); + } catch (e){ console.log( - `Cleanup for environments. Trying to delete env ${this._id}. Current env status is "${envStatus}". Waiting till environment is in valid state for deleting. Total waited time so far is ${totalTimeWaitedInSeconds} seconds` + `Could not delete environment. Last known status for env ${this._id} was "${envStatus}". + Waited ${totalTimeWaitedInSeconds} seconds for environment to reach valid state so it could be deleted; encountered error: ${e}` ); } - await defAdminSession.resources.environments.environment(this._id).delete(); - console.log(`Deleting environment ${this._id}`); + } } diff --git a/solutions/swb-reference/integration-tests/support/setup.ts b/solutions/swb-reference/integration-tests/support/setup.ts index 3938df6b39..7f66311048 100644 --- a/solutions/swb-reference/integration-tests/support/setup.ts +++ b/solutions/swb-reference/integration-tests/support/setup.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { CognitoTokenService } from '@aws/workbench-core-base'; +import { AwsService, CognitoTokenService } from '@aws/workbench-core-base'; import _ from 'lodash'; import ClientSession from './clientSession'; import Settings from './utils/settings'; @@ -35,8 +35,8 @@ export default class Setup { public async getDefaultAdminSession(): Promise { // TODO: Handle token expiration and getting defaultAdminSession instead of creating a new Admin Session if (this._defaultAdminSession === undefined) { - const userPoolId = this._settings.get('userPoolId'); - const clientId = this._settings.get('clientId'); + const userPoolId = this._settings.get('cognitoUserPoolId'); + const clientId = this._settings.get('cognitoUserPoolClientId'); const rootUsername = this._settings.get('rootUsername'); const rootPasswordParamStorePath = this._settings.get('rootPasswordParamStorePath'); const awsRegion = this._settings.get('awsRegion'); @@ -56,6 +56,17 @@ export default class Setup { return this._defaultAdminSession; } + public getStackName(): string { + return `swb-${process.env.STAGE}-${this._settings.get('awsRegionShortName')}`; + } + + public getMainAwsClient(): AwsService { + return new AwsService({ + region: this._settings.get('awsRegion'), + ddbTableName: this.getStackName() // table name is same as stack name + }); + } + public async cleanup(): Promise { // We need to reverse the order of the queue before we cleanup the sessions const sessions = _.reverse(_.slice(this._sessions)); diff --git a/solutions/swb-reference/integration-tests/support/utils/settings.ts b/solutions/swb-reference/integration-tests/support/utils/settings.ts index d012d839d5..9025393ffd 100644 --- a/solutions/swb-reference/integration-tests/support/utils/settings.ts +++ b/solutions/swb-reference/integration-tests/support/utils/settings.ts @@ -7,18 +7,40 @@ import _ from 'lodash'; interface Setting { - apiBaseUrl: string; envTypeId: string; envTypeConfigId: string; projectId: string; envType: string; runId: string; - alreadyTerminateEnvId: string; - awsRegion: string; - userPoolId: string; - clientId: string; + terminatedEnvId: string; rootUsername: string; rootPasswordParamStorePath: string; + + // Main CFN template outputs + cognitoUserPoolClientId: string; + MainAccountEncryptionKeyOutput: string; + SagemakerNotebookTerminateSSMDocOutput: string; + awsRegion: string; + DataSetsBucketName: string; + apiUrlOutput: string; + S3BucketArtifactsArnOutput: string; + uiClientURL: string; + LaunchConstraintIamRoleNameOutput: string; + S3BucketDatasetsArnOutput: string; + StatusHandlerLambdaArnOutput: string; + ApiLambdaRoleOutput: string; + AccountHandlerLambdaRoleOutput: string; + cognitoUserPoolId: string; + S3BucketAccessLogsNameOutput: string; + SagemakerNotebookLaunchSSMDocOutput: string; + awsRegionShortName: string; + cognitoDomainName: string; + APIGatewayAPIEndpoint67A1C4AD: string; + dynamoDBTableOutput: string; + StatusHandlerLambdaRoleOutput: string; + + // Derived + mainAccountId: string; } type SettingKey = keyof Setting; @@ -38,6 +60,7 @@ export default class Settings { } public set(key: SettingKey, value: string): void { + // TODO: Prevent updating main CFN output values this._content[key] = value; } diff --git a/solutions/swb-reference/integration-tests/tests/isolated/datasets/create.test.ts b/solutions/swb-reference/integration-tests/tests/isolated/datasets/create.test.ts new file mode 100644 index 0000000000..0eb6c8f89e --- /dev/null +++ b/solutions/swb-reference/integration-tests/tests/isolated/datasets/create.test.ts @@ -0,0 +1,120 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +import ClientSession from '../../../support/clientSession'; +import Setup from '../../../support/setup'; +import HttpError from '../../../support/utils/HttpError'; +import RandomTextGenerator from '../../../support/utils/randomTextGenerator'; +import { checkHttpError } from '../../../support/utils/utilities'; + +describe('datasets create negative tests', () => { + const setup: Setup = new Setup(); + let adminSession: ClientSession; + const randomTextGenerator = new RandomTextGenerator(setup.getSettings().get('runId')); + + beforeEach(() => { + expect.hasAssertions(); + }); + + beforeAll(async () => { + adminSession = await setup.getDefaultAdminSession(); + }); + + afterAll(async () => { + await setup.cleanup(); + }); + + const validLaunchParameters = { + datasetName: randomTextGenerator.getFakeText('fakeName'), + storageName: randomTextGenerator.getFakeText('fakeBucket'), + path: randomTextGenerator.getFakeText('fakePath'), + awsAccountId: randomTextGenerator.getFakeText('fakeAccount'), + }; + + describe('missing parameters', () => { + test('datasetName', async () => { + try { + const invalidParam: {[id: string]: string} = { ...validLaunchParameters }; + delete invalidParam.datasetName; + await adminSession.resources.datasets.create(invalidParam, false); + } catch (e) { + checkHttpError( + e, + new HttpError(400, { + statusCode: 400, + error: 'Bad Request', + message: "requires property 'datasetName'" + }) + ); + } + }); + + test('path', async () => { + try { + const invalidParam: {[id: string]: string} = { ...validLaunchParameters }; + delete invalidParam.path; + await adminSession.resources.datasets.create(invalidParam, false); + } catch (e) { + checkHttpError( + e, + new HttpError(400, { + statusCode: 400, + error: 'Bad Request', + message: "requires property 'path'" + }) + ); + } + }); + + test('storageName', async () => { + try { + const invalidParam: {[id: string]: string} = { ...validLaunchParameters }; + delete invalidParam.storageName; + await adminSession.resources.datasets.create(invalidParam, false); + } catch (e) { + checkHttpError( + e, + new HttpError(400, { + statusCode: 400, + error: 'Bad Request', + message: "requires property 'storageName'" + }) + ); + } + }); + + test('awsAccountId', async () => { + try { + const invalidParam: {[id: string]: string} = { ...validLaunchParameters }; + delete invalidParam.awsAccountId; + await adminSession.resources.datasets.create(invalidParam, false); + } catch (e) { + checkHttpError( + e, + new HttpError(400, { + statusCode: 400, + error: 'Bad Request', + message: "requires property 'awsAccountId'" + }) + ); + } + }); + + test('all parameters', async () => { + try { + await adminSession.resources.datasets.create({}, false); + } catch (e) { + checkHttpError( + e, + new HttpError(400, { + statusCode: 400, + error: 'Bad Request', + message: + "requires property 'datasetName'. requires property 'storageName'. requires property 'path'. requires property 'awsAccountId'" + }) + ); + } + }); + }); +}); diff --git a/solutions/swb-reference/integration-tests/tests/isolated/environments/terminate.test.ts b/solutions/swb-reference/integration-tests/tests/isolated/environments/terminate.test.ts index 3a913d9185..0529a250fb 100644 --- a/solutions/swb-reference/integration-tests/tests/isolated/environments/terminate.test.ts +++ b/solutions/swb-reference/integration-tests/tests/isolated/environments/terminate.test.ts @@ -28,7 +28,6 @@ describe('environment terminate negative tests', () => { try { await adminSession.resources.environments.environment(fakeEnvId).terminate(); } catch (e) { - console.log('error is', e); checkHttpError( e, new HttpError(404, { @@ -41,7 +40,7 @@ describe('environment terminate negative tests', () => { }); test('terminate an environment that is already terminated should return a 204 and not change the environment status', async () => { - const envId = setup.getSettings().get('alreadyTerminateEnvId'); + const envId = setup.getSettings().get('terminatedEnvId'); const terminateResponse = await adminSession.resources.environments.environment(envId).terminate(); expect(terminateResponse.status).toEqual(204); diff --git a/solutions/swb-reference/integration-tests/tests/multiStep/dataset.test.ts b/solutions/swb-reference/integration-tests/tests/multiStep/dataset.test.ts new file mode 100644 index 0000000000..e4f42774b2 --- /dev/null +++ b/solutions/swb-reference/integration-tests/tests/multiStep/dataset.test.ts @@ -0,0 +1,78 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +import { v4 as uuidv4 } from 'uuid'; +import ClientSession from '../../support/clientSession'; +import Setup from '../../support/setup'; +import RandomTextGenerator from '../../support/utils/randomTextGenerator'; +import { uuidRegExp } from '../../support/utils/regExpressions'; +import Settings from '../../support/utils/settings'; + +describe('multiStep dataset integration test', () => { + const setup: Setup = new Setup(); + const settings: Settings = setup.getSettings(); + let adminSession: ClientSession; + + beforeAll(async () => { + adminSession = await setup.getDefaultAdminSession(); + }); + + afterAll(async () => { + await setup.cleanup(); + }); + + test('Environment provisioning with dataset', async () => { + const randomTextGenerator = new RandomTextGenerator(settings.get('runId')); + const datasetName = randomTextGenerator.getFakeText('env-DS-test'); + + // Create dataset + const dataSetBody = { + storageName: settings.get('DataSetsBucketName'), + awsAccountId: settings.get('mainAccountId'), + path: datasetName, // using same name to help potential troubleshooting + datasetName + }; + + const { data: dataSet } = await adminSession.resources.datasets.create(dataSetBody); + expect(dataSet).toMatchObject({ + id: expect.stringMatching(uuidRegExp) + }); + + // Provision environment with dataset + const envBody = { + envTypeId: settings.get('envTypeId'), + envTypeConfigId: settings.get('envTypeConfigId'), + envType: settings.get('envType'), + datasetIds: [dataSet.id], + name: uuidv4(), + projectId: settings.get('projectId'), + description: 'Temporary DataSet for integration test' + }; + const { data: env } = await adminSession.resources.environments.create(envBody); + + // Verify environment has access point for dataset + const { data: envDetails } = await adminSession.resources.environments.environment(env.id).get(); + const awsRegion = settings.get('awsRegion'); + const mainAccountId = settings.get('mainAccountId'); + const accessPointName = `${dataSet.id.slice(0, 13)}-mounted-on-${env.id.slice(0, 13)}`; + expect(envDetails).toMatchObject({ + datasetIds: [dataSet.id], + ENDPOINTS: expect.arrayContaining([expect.objectContaining({ + endPointUrl: `s3://arn:aws:s3:${awsRegion}:${mainAccountId}:accesspoint/${accessPointName}`, + storageArn: `arn:aws:s3:::${settings.get('DataSetsBucketName')}`, + dataSetId: dataSet.id, + path: datasetName + })]), + DATASETS: expect.arrayContaining([expect.objectContaining({ + id: dataSet.id, + name: datasetName + })])} + ); + + // Verify dataset has env access point listed in its external endpoints + const { data: dataSetDetails } = await adminSession.resources.datasets.dataset(dataSet.id).get(); + // Dataset was created just for this test case, so we expect only one endpoint + expect(dataSetDetails.externalEndpoints).toMatchObject([envDetails.ENDPOINTS[0].sk.split('ENDPOINT#')[1]]); + }); +}); diff --git a/solutions/swb-reference/jest.config.js b/solutions/swb-reference/jest.config.js index 613651b0af..7482128e62 100644 --- a/solutions/swb-reference/jest.config.js +++ b/solutions/swb-reference/jest.config.js @@ -24,8 +24,25 @@ async function init() { fs.readFileSync(join(__dirname, `integration-tests/config/${stage}.yaml`), 'utf8') ); + let outputs; + try { + const apiStackOutputs = JSON.parse( + fs.readFileSync(join(__dirname, `../src/config/${process.env.STAGE}.json`), 'utf8') // nosemgrep + ); + const apiStackName = Object.entries(apiStackOutputs).map(([key, value]) => key)[0]; //output has a format { stackname: {...props} } + outputs = apiStackOutputs[apiStackName]; + } catch (e) { + throw new Error( + 'There was a problem reading the main stage file. Please run cdk-deploy prior to running the integration test suite' + ); + } + + const mainAccountId = outputs.dynamoDBTableOutput.split(':')[4]; + settings = { ...config, + ...outputs, + mainAccountId, runId: `${Date.now()}` }; } diff --git a/solutions/swb-reference/package.json b/solutions/swb-reference/package.json index 03bc33fff5..87f2ab7f58 100644 --- a/solutions/swb-reference/package.json +++ b/solutions/swb-reference/package.json @@ -34,6 +34,7 @@ "@aws-sdk/client-cloudformation": "^3.118.1", "@aws-sdk/client-dynamodb": "^3.118.1", "@aws-sdk/client-s3": "^3.118.1", + "@aws-sdk/client-s3-control": "^3.118.1", "@aws-sdk/client-service-catalog": "^3.118.1", "@aws-sdk/client-ssm": "^3.118.1", "@aws-sdk/client-sts": "^3.118.1", diff --git a/solutions/swb-reference/scripts/generateCognitoTokens.js b/solutions/swb-reference/scripts/generateCognitoTokens.js index b4815989cc..c5876a473b 100755 --- a/solutions/swb-reference/scripts/generateCognitoTokens.js +++ b/solutions/swb-reference/scripts/generateCognitoTokens.js @@ -20,8 +20,21 @@ const config = yaml.load( fs.readFileSync(join(__dirname, `../integration-tests/config/${process.env.STAGE}.yaml`), 'utf8') // nosemgrep ); -const clientId = config.clientId; -const userPoolId = config.userPoolId; +let outputs; +try { + const apiStackOutputs = JSON.parse( + fs.readFileSync(join(__dirname, `../src/config/${process.env.STAGE}.json`), 'utf8') // nosemgrep + ); + const apiStackName = Object.entries(apiStackOutputs).map(([key, value]) => key)[0]; //output has a format { stackname: {...props} } + outputs = apiStackOutputs[apiStackName]; +} catch (e) { + throw new Error( + 'There was a problem reading the main stage file. Please run cdk-deploy prior to running this script' + ); +} + +const clientId = outputs.cognitoUserPoolClientId; +const userPoolId = outputs.cognitoUserPoolId; const region = config.awsRegion; const username = process.argv[2]; const password = process.argv[3]; diff --git a/workbench-core/base/src/index.ts b/workbench-core/base/src/index.ts index 4971b2fb72..42f1c55df4 100644 --- a/workbench-core/base/src/index.ts +++ b/workbench-core/base/src/index.ts @@ -9,4 +9,4 @@ import { buildDynamoDbKey, buildDynamoDBPkSk } from './aws/helpers/dynamoDB/ddbU import { QueryParams } from './aws/helpers/dynamoDB/dynamoDBService'; import CognitoTokenService from './cognitoTokenService'; -export { AuditLogger, AwsService, QueryParams, buildDynamoDbKey, buildDynamoDBPkSk, CognitoTokenService }; +export { AuditLogger, AwsService, CognitoTokenService, QueryParams, buildDynamoDbKey, buildDynamoDBPkSk }; From 7a114290a215af911cb5642f05e5b83da8f869fb Mon Sep 17 00:00:00 2001 From: Sanket Dharwadkar Date: Fri, 12 Aug 2022 08:54:16 -0700 Subject: [PATCH 007/621] fix: config path for integ test (#457) --- solutions/swb-reference/jest.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/solutions/swb-reference/jest.config.js b/solutions/swb-reference/jest.config.js index 7482128e62..c9e3a478ef 100644 --- a/solutions/swb-reference/jest.config.js +++ b/solutions/swb-reference/jest.config.js @@ -27,7 +27,7 @@ async function init() { let outputs; try { const apiStackOutputs = JSON.parse( - fs.readFileSync(join(__dirname, `../src/config/${process.env.STAGE}.json`), 'utf8') // nosemgrep + fs.readFileSync(join(__dirname, `src/config/${process.env.STAGE}.json`), 'utf8') // nosemgrep ); const apiStackName = Object.entries(apiStackOutputs).map(([key, value]) => key)[0]; //output has a format { stackname: {...props} } outputs = apiStackOutputs[apiStackName]; From 7644bde21312c5bd36a900b2aa61ac57864fbd81 Mon Sep 17 00:00:00 2001 From: HaiTao Zhang Date: Fri, 12 Aug 2022 15:13:08 -0700 Subject: [PATCH 008/621] ci: update PR process (#451) * ci: add change verify to PR checks * ci: add license-checker to all packages and include rush version in create release branch * ci: add license-checker workflow for PRs * ci: configure license checker workflow to be specific to a package.json change * fix: requires merge to be true * docs: changefiles for package changes when adding license-checker * ci: remove push on create release branch Co-authored-by: Robert Smayda --- .github/workflows/create-release-branch.yml | 5 +- .github/workflows/license-checker.yml | 22 +++ .../publish-and-merge-to-develop.yml | 3 +- .github/workflows/verify-changefiles.yml | 19 +++ ...feat-updatePRprocess_2022-08-12-02-01.json | 10 ++ ...feat-updatePRprocess_2022-08-12-02-01.json | 10 ++ ...feat-updatePRprocess_2022-08-12-02-01.json | 10 ++ ...feat-updatePRprocess_2022-08-12-02-01.json | 10 ++ ...feat-updatePRprocess_2022-08-12-02-01.json | 10 ++ ...feat-updatePRprocess_2022-08-12-02-01.json | 10 ++ ...feat-updatePRprocess_2022-08-12-02-01.json | 10 ++ ...feat-updatePRprocess_2022-08-12-02-01.json | 10 ++ ...feat-updatePRprocess_2022-08-12-02-01.json | 10 ++ .../rush/browser-approved-packages.json | 4 + common/config/rush/command-line.json | 7 + common/config/rush/pnpm-lock.yaml | 125 ++++++++++++++++-- solutions/example-ui-app/package.json | 2 + solutions/swb-app/package.json | 2 + solutions/swb-reference/package.json | 2 + solutions/swb-ui/infrastructure/package.json | 2 + solutions/swb-ui/package.json | 2 + workbench-core/audit/package.json | 2 + workbench-core/authentication/package.json | 2 + workbench-core/authorization/package.json | 2 + workbench-core/base/package.json | 2 + workbench-core/datasets/package.json | 2 + workbench-core/environments/package.json | 2 + workbench-core/eslint-custom/package.json | 2 + workbench-core/example/express/package.json | 2 + .../example/infrastructure/package.json | 2 + workbench-core/infrastructure/package.json | 2 + workbench-core/logging/package.json | 2 + .../repo-scripts/repo-toolbox/package.json | 2 + 33 files changed, 298 insertions(+), 11 deletions(-) create mode 100644 .github/workflows/license-checker.yml create mode 100644 .github/workflows/verify-changefiles.yml create mode 100644 common/changes/@aws/swb-app/feat-updatePRprocess_2022-08-12-02-01.json create mode 100644 common/changes/@aws/workbench-core-audit/feat-updatePRprocess_2022-08-12-02-01.json create mode 100644 common/changes/@aws/workbench-core-authentication/feat-updatePRprocess_2022-08-12-02-01.json create mode 100644 common/changes/@aws/workbench-core-authorization/feat-updatePRprocess_2022-08-12-02-01.json create mode 100644 common/changes/@aws/workbench-core-base/feat-updatePRprocess_2022-08-12-02-01.json create mode 100644 common/changes/@aws/workbench-core-datasets/feat-updatePRprocess_2022-08-12-02-01.json create mode 100644 common/changes/@aws/workbench-core-environments/feat-updatePRprocess_2022-08-12-02-01.json create mode 100644 common/changes/@aws/workbench-core-infrastructure/feat-updatePRprocess_2022-08-12-02-01.json create mode 100644 common/changes/@aws/workbench-core-logging/feat-updatePRprocess_2022-08-12-02-01.json diff --git a/.github/workflows/create-release-branch.yml b/.github/workflows/create-release-branch.yml index 81f2dac41b..35ecf13448 100644 --- a/.github/workflows/create-release-branch.yml +++ b/.github/workflows/create-release-branch.yml @@ -15,4 +15,7 @@ jobs: uuid=`uuidgen` echo 'Creating release branch' git checkout -b release/$uuid - git push origin release/$uuid + + echo 'Rush version to bump updates' + node common/scripts/install-run-rush.js version --bump -b release/$uuid --ignore-git-hooks + diff --git a/.github/workflows/license-checker.yml b/.github/workflows/license-checker.yml new file mode 100644 index 0000000000..7cb727cd63 --- /dev/null +++ b/.github/workflows/license-checker.yml @@ -0,0 +1,22 @@ +name: license checker +on: + pull_request: + types: [opened, synchronize, edited, reopened] + paths: + - '**/package.json' + +jobs: + license-checker: + runs-on: ubuntu-20.04 + steps: + - name: Checkout repository + uses: actions/checkout@v3 + with: + ref: ${{ github.head_ref }} + token: ${{ secrets.MERGE_TOKEN }} + + - name: Base Action + uses: ./.github/actions/baseAction + + - name: Rush license-checker + run: node common/scripts/install-run-rush.js license-checker diff --git a/.github/workflows/publish-and-merge-to-develop.yml b/.github/workflows/publish-and-merge-to-develop.yml index 3bb6ad048c..0870b9a77f 100644 --- a/.github/workflows/publish-and-merge-to-develop.yml +++ b/.github/workflows/publish-and-merge-to-develop.yml @@ -7,7 +7,8 @@ on: - main jobs: - rush-publish: + rush-publish: + if: github.event.pull_request.merged == true runs-on: ubuntu-20.04 permissions: id-token: write diff --git a/.github/workflows/verify-changefiles.yml b/.github/workflows/verify-changefiles.yml new file mode 100644 index 0000000000..2939fd841d --- /dev/null +++ b/.github/workflows/verify-changefiles.yml @@ -0,0 +1,19 @@ +name: Verify Changefiles +on: + pull_request: + types: [opened, synchronize, edited, reopened] + branches: + - develop + +jobs: + run-rush-change-verify: + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v3 + with: + token: ${{ secrets.MERGE_TOKEN }} + fetch-depth: 0 + - name: rush-change-verify + run: | + echo "checking if all changefiles were created" + node common/scripts/install-run-rush.js change --verify -b origin/develop \ No newline at end of file diff --git a/common/changes/@aws/swb-app/feat-updatePRprocess_2022-08-12-02-01.json b/common/changes/@aws/swb-app/feat-updatePRprocess_2022-08-12-02-01.json new file mode 100644 index 0000000000..c810bb2793 --- /dev/null +++ b/common/changes/@aws/swb-app/feat-updatePRprocess_2022-08-12-02-01.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@aws/swb-app", + "comment": "add license-checker", + "type": "none" + } + ], + "packageName": "@aws/swb-app" +} \ No newline at end of file diff --git a/common/changes/@aws/workbench-core-audit/feat-updatePRprocess_2022-08-12-02-01.json b/common/changes/@aws/workbench-core-audit/feat-updatePRprocess_2022-08-12-02-01.json new file mode 100644 index 0000000000..2553e89ec7 --- /dev/null +++ b/common/changes/@aws/workbench-core-audit/feat-updatePRprocess_2022-08-12-02-01.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@aws/workbench-core-audit", + "comment": "add license-checker", + "type": "none" + } + ], + "packageName": "@aws/workbench-core-audit" +} \ No newline at end of file diff --git a/common/changes/@aws/workbench-core-authentication/feat-updatePRprocess_2022-08-12-02-01.json b/common/changes/@aws/workbench-core-authentication/feat-updatePRprocess_2022-08-12-02-01.json new file mode 100644 index 0000000000..1f4dedfaeb --- /dev/null +++ b/common/changes/@aws/workbench-core-authentication/feat-updatePRprocess_2022-08-12-02-01.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@aws/workbench-core-authentication", + "comment": "add license-checker", + "type": "none" + } + ], + "packageName": "@aws/workbench-core-authentication" +} \ No newline at end of file diff --git a/common/changes/@aws/workbench-core-authorization/feat-updatePRprocess_2022-08-12-02-01.json b/common/changes/@aws/workbench-core-authorization/feat-updatePRprocess_2022-08-12-02-01.json new file mode 100644 index 0000000000..228e86d9c7 --- /dev/null +++ b/common/changes/@aws/workbench-core-authorization/feat-updatePRprocess_2022-08-12-02-01.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@aws/workbench-core-authorization", + "comment": "add license-checker", + "type": "none" + } + ], + "packageName": "@aws/workbench-core-authorization" +} \ No newline at end of file diff --git a/common/changes/@aws/workbench-core-base/feat-updatePRprocess_2022-08-12-02-01.json b/common/changes/@aws/workbench-core-base/feat-updatePRprocess_2022-08-12-02-01.json new file mode 100644 index 0000000000..efddca0bbe --- /dev/null +++ b/common/changes/@aws/workbench-core-base/feat-updatePRprocess_2022-08-12-02-01.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@aws/workbench-core-base", + "comment": "add license-checker", + "type": "none" + } + ], + "packageName": "@aws/workbench-core-base" +} \ No newline at end of file diff --git a/common/changes/@aws/workbench-core-datasets/feat-updatePRprocess_2022-08-12-02-01.json b/common/changes/@aws/workbench-core-datasets/feat-updatePRprocess_2022-08-12-02-01.json new file mode 100644 index 0000000000..1cf53f0c28 --- /dev/null +++ b/common/changes/@aws/workbench-core-datasets/feat-updatePRprocess_2022-08-12-02-01.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@aws/workbench-core-datasets", + "comment": "add license-checker", + "type": "none" + } + ], + "packageName": "@aws/workbench-core-datasets" +} \ No newline at end of file diff --git a/common/changes/@aws/workbench-core-environments/feat-updatePRprocess_2022-08-12-02-01.json b/common/changes/@aws/workbench-core-environments/feat-updatePRprocess_2022-08-12-02-01.json new file mode 100644 index 0000000000..e181104f95 --- /dev/null +++ b/common/changes/@aws/workbench-core-environments/feat-updatePRprocess_2022-08-12-02-01.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@aws/workbench-core-environments", + "comment": "add license-checker", + "type": "none" + } + ], + "packageName": "@aws/workbench-core-environments" +} \ No newline at end of file diff --git a/common/changes/@aws/workbench-core-infrastructure/feat-updatePRprocess_2022-08-12-02-01.json b/common/changes/@aws/workbench-core-infrastructure/feat-updatePRprocess_2022-08-12-02-01.json new file mode 100644 index 0000000000..6fb2e80a22 --- /dev/null +++ b/common/changes/@aws/workbench-core-infrastructure/feat-updatePRprocess_2022-08-12-02-01.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@aws/workbench-core-infrastructure", + "comment": "add license-checker", + "type": "none" + } + ], + "packageName": "@aws/workbench-core-infrastructure" +} \ No newline at end of file diff --git a/common/changes/@aws/workbench-core-logging/feat-updatePRprocess_2022-08-12-02-01.json b/common/changes/@aws/workbench-core-logging/feat-updatePRprocess_2022-08-12-02-01.json new file mode 100644 index 0000000000..89ac4a8eab --- /dev/null +++ b/common/changes/@aws/workbench-core-logging/feat-updatePRprocess_2022-08-12-02-01.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@aws/workbench-core-logging", + "comment": "add license-checker", + "type": "none" + } + ], + "packageName": "@aws/workbench-core-logging" +} \ No newline at end of file diff --git a/common/config/rush/browser-approved-packages.json b/common/config/rush/browser-approved-packages.json index d3ba00d590..3de1e63c27 100644 --- a/common/config/rush/browser-approved-packages.json +++ b/common/config/rush/browser-approved-packages.json @@ -354,6 +354,10 @@ "name": "jwt-decode", "allowedCategories": [ "production" ] }, + { + "name": "license-checker", + "allowedCategories": [ "production", "prototypes" ] + }, { "name": "lodash", "allowedCategories": [ "production", "prototypes" ] diff --git a/common/config/rush/command-line.json b/common/config/rush/command-line.json index ae048f1cd8..7ab79816a5 100644 --- a/common/config/rush/command-line.json +++ b/common/config/rush/command-line.json @@ -222,6 +222,13 @@ "safeForSimultaneousRushProcesses": true, "enableParallelism": true }, + { + "name": "license-checker", + "commandKind": "bulk", + "summary": "Run license checker on dependencies", + "safeForSimultaneousRushProcesses": true, + "enableParallelism": true + }, { "name": "common-coverage-report", "commandKind": "global", diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index 8c92e2f3f2..b6bc4ccc8f 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -35,6 +35,7 @@ importers: eslint-plugin-testing-library: 5.0.1 istanbul-badges-readme: 1.8.1 jest: ^27.5.1 + license-checker: ~25.0.1 next: ^12.1.5 next-global-css: ^1.2.0 next-i18next: ^11.0.0 @@ -84,6 +85,7 @@ importers: eslint-plugin-testing-library: 5.0.1_eslint@8.16.0+typescript@4.7.2 istanbul-badges-readme: 1.8.1 jest: 27.5.1 + license-checker: 25.0.1 npm-package-json-lint: 6.3.0 npm-package-json-lint-config-default: 5.0.0_npm-package-json-lint@6.3.0 sass: 1.52.2 @@ -123,6 +125,7 @@ importers: istanbul-badges-readme: 1.8.1 jest: ^27.5.1 jsonschema: ^1.4.1 + license-checker: ~25.0.1 lodash: ^4.17.21 nodemon: ^2.0.19 npm-package-json-lint: ^6.3.0 @@ -164,6 +167,7 @@ importers: eslint-plugin-import: 2.26.0_eslint@8.16.0 istanbul-badges-readme: 1.8.1 jest: 27.5.1 + license-checker: 25.0.1 nodemon: 2.0.19 npm-package-json-lint: 6.3.0 npm-package-json-lint-config-default: 5.0.0_npm-package-json-lint@6.3.0 @@ -219,6 +223,7 @@ importers: jest: ^27.5.1 jest-circus: ^26.6.3 js-yaml: ^4.1.0 + license-checker: ~25.0.1 lodash: ^4.17.21 npm-package-json-lint: ^6.3.0 npm-package-json-lint-config-default: ^5.0.0 @@ -277,6 +282,7 @@ importers: istanbul-badges-readme: 1.8.1 jest: 27.5.1_ts-node@10.8.0 jest-circus: 26.6.3_ts-node@10.8.0 + license-checker: 25.0.1 npm-package-json-lint: 6.3.0 npm-package-json-lint-config-default: 5.0.0_npm-package-json-lint@6.3.0 sort-package-json: 1.57.0 @@ -324,6 +330,7 @@ importers: istanbul-badges-readme: 1.8.1 jest: ^27.5.1 jwt-decode: ^3.1.2 + license-checker: ~25.0.1 next: ^12.1.5 next-global-css: ^1.2.0 next-transpile-modules: ^9.0.0 @@ -388,6 +395,7 @@ importers: eslint-plugin-testing-library: 5.0.1_eslint@8.16.0+typescript@4.7.2 istanbul-badges-readme: 1.8.1 jest: 27.5.1 + license-checker: 25.0.1 nodemon: 2.0.19 npm-package-json-lint: 6.3.0 npm-package-json-lint-config-default: 5.0.0_npm-package-json-lint@6.3.0 @@ -411,6 +419,7 @@ importers: constructs: ^10.0.0 jest: ^27.5.1 js-yaml: ^4.1.0 + license-checker: ~25.0.1 sort-package-json: ^1.57.0 source-map-support: ^0.5.16 ts-jest: ^27.1.3 @@ -430,6 +439,7 @@ importers: aws-cdk-lib: 2.29.1_constructs@10.1.25 constructs: 10.1.25 jest: 27.5.1_ts-node@10.8.0 + license-checker: 25.0.1 sort-package-json: 1.57.0 source-map-support: 0.5.21 ts-jest: 27.1.5_4e623839ecfc1b0242d7f262e6e0b1df @@ -452,6 +462,7 @@ importers: eslint: ^8.7.0 eslint-plugin-import: ^2.26.0 istanbul-badges-readme: 1.8.1 + license-checker: ~25.0.1 lodash: ^4.17.21 npm-package-json-lint: ^6.3.0 npm-package-json-lint-config-default: ^5.0.0 @@ -474,6 +485,7 @@ importers: eslint: 8.16.0 eslint-plugin-import: 2.26.0_eslint@8.16.0 istanbul-badges-readme: 1.8.1 + license-checker: 25.0.1 npm-package-json-lint: 6.3.0 npm-package-json-lint-config-default: 5.0.0_npm-package-json-lint@6.3.0 sort-package-json: 1.57.0 @@ -503,6 +515,7 @@ importers: eslint-plugin-import: ^2.26.0 istanbul-badges-readme: 1.8.1 jsonschema: ^1.4.1 + license-checker: ~25.0.1 lodash: ^4.17.21 npm-package-json-lint: ^6.3.0 npm-package-json-lint-config-default: ^5.0.0 @@ -533,6 +546,7 @@ importers: eslint: 8.16.0 eslint-plugin-import: 2.26.0_eslint@8.16.0 istanbul-badges-readme: 1.8.1 + license-checker: 25.0.1 npm-package-json-lint: 6.3.0 npm-package-json-lint-config-default: 5.0.0_npm-package-json-lint@6.3.0 sort-package-json: 1.57.0 @@ -558,6 +572,7 @@ importers: istanbul-badges-readme: 1.8.1 jest: ^27.5.1 jest-fast-check: ^1.0.2 + license-checker: ~25.0.1 lodash: ^4.17.21 npm-package-json-lint: ^6.3.0 npm-package-json-lint-config-default: ^5.0.0 @@ -584,6 +599,7 @@ importers: istanbul-badges-readme: 1.8.1 jest: 27.5.1 jest-fast-check: 1.0.2_fast-check@2.25.0+jest@27.5.1 + license-checker: 25.0.1 npm-package-json-lint: 6.3.0 npm-package-json-lint-config-default: 5.0.0_npm-package-json-lint@6.3.0 sort-package-json: 1.57.0 @@ -625,6 +641,7 @@ importers: eslint-plugin-import: ^2.26.0 istanbul-badges-readme: 1.8.1 jest: ^27.5.1 + license-checker: ~25.0.1 lodash: ^4.17.21 npm-package-json-lint: ^6.3.0 npm-package-json-lint-config-default: ^5.0.0 @@ -668,6 +685,7 @@ importers: eslint-plugin-import: 2.26.0_eslint@8.16.0 istanbul-badges-readme: 1.8.1 jest: 27.5.1 + license-checker: 25.0.1 npm-package-json-lint: 6.3.0 npm-package-json-lint-config-default: 5.0.0_npm-package-json-lint@6.3.0 sort-package-json: 1.57.0 @@ -705,6 +723,7 @@ importers: jest: ^27.5.1 jest-fast-check: ^1.0.2 jsonschema: ^1.4.1 + license-checker: ~25.0.1 lodash: ^4.17.21 npm-package-json-lint: ^6.3.0 npm-package-json-lint-config-default: ^5.0.0 @@ -745,6 +764,7 @@ importers: istanbul-badges-readme: 1.8.1 jest: 27.5.1 jest-fast-check: 1.0.2_fast-check@2.25.0+jest@27.5.1 + license-checker: 25.0.1 npm-package-json-lint: 6.3.0 npm-package-json-lint-config-default: 5.0.0_npm-package-json-lint@6.3.0 sort-package-json: 1.57.0 @@ -791,6 +811,7 @@ importers: istanbul-badges-readme: 1.8.1 jest: ^27.5.1 jsonschema: ^1.4.1 + license-checker: ~25.0.1 lodash: ^4.17.21 md5-file: ^5.0.0 npm-package-json-lint: ^6.3.0 @@ -842,6 +863,7 @@ importers: eslint-plugin-import: 2.26.0_eslint@8.16.0 istanbul-badges-readme: 1.8.1 jest: 27.5.1 + license-checker: 25.0.1 npm-package-json-lint: 6.3.0 npm-package-json-lint-config-default: 5.0.0_npm-package-json-lint@6.3.0 sort-package-json: 1.57.0 @@ -860,6 +882,7 @@ importers: eslint: ^8.7.0 eslint-plugin-import: ^2.26.0 eslint-plugin-security: ^1.4.0 + license-checker: ~25.0.1 npm-package-json-lint: ^6.3.0 npm-package-json-lint-config-default: ^5.0.0 sort-package-json: ^1.57.0 @@ -876,6 +899,7 @@ importers: '@types/heft-jest': 1.0.2 '@types/node': 14.18.20 depcheck: 1.4.3 + license-checker: 25.0.1 npm-package-json-lint: 6.3.0 npm-package-json-lint-config-default: 5.0.0_npm-package-json-lint@6.3.0 sort-package-json: 1.57.0 @@ -898,6 +922,7 @@ importers: eslint-plugin-import: ^2.26.0 eslint-plugin-security: ^1.4.0 express: ^4.17.3 + license-checker: ~25.0.1 npm-package-json-lint: ^6.3.0 npm-package-json-lint-config-default: ^5.0.0 sort-package-json: ^1.57.0 @@ -919,6 +944,7 @@ importers: eslint-plugin-import: 2.26.0_eslint@8.16.0 eslint-plugin-security: 1.5.0 express: 4.18.1 + license-checker: 25.0.1 npm-package-json-lint: 6.3.0 npm-package-json-lint-config-default: 5.0.0_npm-package-json-lint@6.3.0 sort-package-json: 1.57.0 @@ -954,6 +980,7 @@ importers: express: ^4.17.3 istanbul-badges-readme: 1.8.1 jest: ^27.5.1 + license-checker: ~25.0.1 lodash: ^4.17.21 npm-package-json-lint: ^6.3.0 npm-package-json-lint-config-default: ^5.0.0 @@ -992,6 +1019,7 @@ importers: express: 4.18.1 istanbul-badges-readme: 1.8.1 jest: 27.5.1 + license-checker: 25.0.1 npm-package-json-lint: 6.3.0 npm-package-json-lint-config-default: 5.0.0_npm-package-json-lint@6.3.0 sort-package-json: 1.57.0 @@ -1014,6 +1042,7 @@ importers: eslint-plugin-import: ^2.26.0 eslint-plugin-security: ^1.4.0 istanbul-badges-readme: 1.8.1 + license-checker: ~25.0.1 lodash: ^4.17.21 npm-package-json-lint: ^6.3.0 npm-package-json-lint-config-default: ^5.0.0 @@ -1037,6 +1066,7 @@ importers: eslint-plugin-import: 2.26.0_eslint@8.16.0 eslint-plugin-security: 1.5.0 istanbul-badges-readme: 1.8.1 + license-checker: 25.0.1 npm-package-json-lint: 6.3.0 npm-package-json-lint-config-default: 5.0.0_npm-package-json-lint@6.3.0 sort-package-json: 1.57.0 @@ -1058,6 +1088,7 @@ importers: istanbul-badges-readme: 1.8.1 jest: ^27.5.1 jest-fast-check: ^1.0.2 + license-checker: ~25.0.1 npm-package-json-lint: ^6.3.0 npm-package-json-lint-config-default: ^5.0.0 sort-package-json: ^1.57.0 @@ -1084,6 +1115,7 @@ importers: istanbul-badges-readme: 1.8.1 jest: 27.5.1 jest-fast-check: 1.0.2_fast-check@2.25.0+jest@27.5.1 + license-checker: 25.0.1 npm-package-json-lint: 6.3.0 npm-package-json-lint-config-default: 5.0.0_npm-package-json-lint@6.3.0 sort-package-json: 1.57.0 @@ -1107,6 +1139,7 @@ importers: eslint: ^8.7.0 eslint-plugin-import: ^2.26.0 eslint-plugin-security: ^1.4.0 + license-checker: ~25.0.1 npm-package-json-lint: ^6.3.0 npm-package-json-lint-config-default: ^5.0.0 sort-package-json: ^1.57.0 @@ -1129,6 +1162,7 @@ importers: eslint: 8.16.0 eslint-plugin-import: 2.26.0_eslint@8.16.0 eslint-plugin-security: 1.5.0 + license-checker: 25.0.1 npm-package-json-lint: 6.3.0 npm-package-json-lint-config-default: 5.0.0_npm-package-json-lint@6.3.0 sort-package-json: 1.57.0 @@ -7198,6 +7232,11 @@ packages: engines: {node: '>=8'} dev: true + /array-find-index/1.0.2: + resolution: {integrity: sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw==} + engines: {node: '>=0.10.0'} + dev: true + /array-flatten/1.1.1: resolution: {integrity: sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=} @@ -7250,7 +7289,6 @@ packages: /asap/2.0.6: resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} - dev: false /assign-symbols/1.0.0: resolution: {integrity: sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==} @@ -8283,7 +8321,6 @@ packages: /debuglog/1.0.1: resolution: {integrity: sha512-syBZ+rnAK3EgMsH2aYEOLUW7mZSY9Gb+0wUMCFsZvcmiz+HigA0LOcq/HoQqVuGG+EKykunc7QG2bzrponfaSw==} - dev: false /decamelize-keys/1.1.0: resolution: {integrity: sha512-ocLWuYzRPoS9bfiSdDd3cxvrzovVMZnRDVEzAs+hWIVXGDbHxWMECij2OBuyB/An0FFW/nLuq6Kv1i/YC5Qfzg==} @@ -8435,7 +8472,6 @@ packages: dependencies: asap: 2.0.6 wrappy: 1.0.2 - dev: false /diff-sequences/26.6.2: resolution: {integrity: sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==} @@ -11709,6 +11745,22 @@ packages: prelude-ls: 1.2.1 type-check: 0.4.0 + /license-checker/25.0.1: + resolution: {integrity: sha512-mET5AIwl7MR2IAKYYoVBBpV0OnkKQ1xGj2IMMeEFIs42QAkEVjRtFZGWmQ28WeU7MP779iAgOaOy93Mn44mn6g==} + hasBin: true + dependencies: + chalk: 2.4.2 + debug: 3.2.7 + mkdirp: 0.5.6 + nopt: 4.0.3 + read-installed: 4.0.3 + semver: 5.7.1 + spdx-correct: 3.1.1 + spdx-expression-parse: 3.0.1 + spdx-satisfies: 4.0.1 + treeify: 1.1.0 + dev: true + /lie/3.3.0: resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==} dependencies: @@ -11996,6 +12048,13 @@ packages: resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} dev: true + /mkdirp/0.5.6: + resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} + hasBin: true + dependencies: + minimist: 1.2.6 + dev: true + /mkdirp/1.0.4: resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} engines: {node: '>=10'} @@ -12231,6 +12290,14 @@ packages: abbrev: 1.1.1 dev: true + /nopt/4.0.3: + resolution: {integrity: sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==} + hasBin: true + dependencies: + abbrev: 1.1.1 + osenv: 0.1.5 + dev: true + /normalize-package-data/2.5.0: resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} dependencies: @@ -12267,7 +12334,6 @@ packages: /npm-normalize-package-bin/1.0.1: resolution: {integrity: sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==} - dev: false /npm-package-arg/6.1.1: resolution: {integrity: sha512-qBpssaL3IOZWi5vEKUKW0cO7kzLeT+EQO9W8RsLOZf76KF9E/K9+wH0C7t06HXPpaH8WH5xF1MExLuCwbTqRUg==} @@ -12482,19 +12548,16 @@ packages: /os-homedir/1.0.2: resolution: {integrity: sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==} engines: {node: '>=0.10.0'} - dev: false /os-tmpdir/1.0.2: resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} engines: {node: '>=0.10.0'} - dev: false /osenv/0.1.5: resolution: {integrity: sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==} dependencies: os-homedir: 1.0.2 os-tmpdir: 1.0.2 - dev: false /p-finally/1.0.0: resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==} @@ -12968,6 +13031,19 @@ packages: object-assign: 4.1.1 dev: false + /read-installed/4.0.3: + resolution: {integrity: sha512-O03wg/IYuV/VtnK2h/KXEt9VIbMUFbk3ERG0Iu4FhLZw0EP0T9znqrYDGn6ncbEsXUFaUjiVAWXHzxwt3lhRPQ==} + dependencies: + debuglog: 1.0.1 + read-package-json: 2.1.2 + readdir-scoped-modules: 1.1.0 + semver: 5.7.1 + slide: 1.1.6 + util-extend: 1.0.3 + optionalDependencies: + graceful-fs: 4.2.10 + dev: true + /read-package-json/2.1.2: resolution: {integrity: sha512-D1KmuLQr6ZSJS0tW8hf3WGpRlwszJOXZ3E8Yd/DNRaM5d+1wVRZdHlpGBLAuovjr28LbWvjpWkBHMxpRGGjzNA==} dependencies: @@ -12975,7 +13051,6 @@ packages: json-parse-even-better-errors: 2.3.1 normalize-package-data: 2.5.0 npm-normalize-package-bin: 1.0.1 - dev: false /read-package-tree/5.1.6: resolution: {integrity: sha512-FCX1aT3GWyY658wzDICef4p+n0dB+ENRct8E/Qyvppj6xVpOYerBHfUu7OP5Rt1/393Tdglguf5ju5DEX4wZNg==} @@ -13041,7 +13116,6 @@ packages: dezalgo: 1.0.4 graceful-fs: 4.2.10 once: 1.4.0 - dev: false /readdirp/3.5.0: resolution: {integrity: sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==} @@ -13515,6 +13589,10 @@ packages: is-fullwidth-code-point: 3.0.0 dev: true + /slide/1.1.6: + resolution: {integrity: sha512-NwrtjCg+lZoqhFU8fOwl4ay2ei8PaqCBOUV3/ektPY9trO1yQ1oXEfmHAhKArUVUr/hOHvy5f6AdP17dCM0zMw==} + dev: true + /snapdragon-node/2.1.1: resolution: {integrity: sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==} engines: {node: '>=0.10.0'} @@ -13624,6 +13702,14 @@ packages: resolution: {integrity: sha1-YvXpRmmBwbeW3Fkpk34RycaSG9A=} dev: true + /spdx-compare/1.0.0: + resolution: {integrity: sha512-C1mDZOX0hnu0ep9dfmuoi03+eOdDoz2yvK79RxbcrVEG1NO1Ph35yW102DHWKN4pk80nwCgeMmSY5L25VE4D9A==} + dependencies: + array-find-index: 1.0.2 + spdx-expression-parse: 3.0.1 + spdx-ranges: 2.1.1 + dev: true + /spdx-correct/3.1.1: resolution: {integrity: sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==} dependencies: @@ -13642,6 +13728,18 @@ packages: /spdx-license-ids/3.0.11: resolution: {integrity: sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==} + /spdx-ranges/2.1.1: + resolution: {integrity: sha512-mcdpQFV7UDAgLpXEE/jOMqvK4LBoO0uTQg0uvXUewmEFhpiZx5yJSZITHB8w1ZahKdhfZqP5GPEOKLyEq5p8XA==} + dev: true + + /spdx-satisfies/4.0.1: + resolution: {integrity: sha512-WVzZ/cXAzoNmjCWiEluEA3BjHp5tiUmmhn9MK+X0tBbR9sOqtC6UQwmgCNrAIZvNlMuBUYAaHYfb2oqlF9SwKA==} + dependencies: + spdx-compare: 1.0.0 + spdx-expression-parse: 3.0.1 + spdx-ranges: 2.1.1 + dev: true + /split-string/3.1.0: resolution: {integrity: sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==} engines: {node: '>=0.10.0'} @@ -14046,6 +14144,11 @@ packages: hasBin: true dev: true + /treeify/1.1.0: + resolution: {integrity: sha512-1m4RA7xVAJrSGrrXGs0L3YTwyvBs2S8PbRHaLZAkFw7JR8oIFwYtysxlBZhYIa7xSyiYJKZ3iGrrk55cGA3i9A==} + engines: {node: '>=0.6'} + dev: true + /trim-newlines/3.0.1: resolution: {integrity: sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==} engines: {node: '>=8'} @@ -14450,6 +14553,10 @@ packages: /util-deprecate/1.0.2: resolution: {integrity: sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=} + /util-extend/1.0.3: + resolution: {integrity: sha512-mLs5zAK+ctllYBj+iAQvlDCwoxU/WDOUaJkcFudeiAX6OajC6BKXJUa9a+tbtkC11dz2Ufb7h0lyvIOVn4LADA==} + dev: true + /utils-merge/1.0.1: resolution: {integrity: sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=} engines: {node: '>= 0.4.0'} diff --git a/solutions/example-ui-app/package.json b/solutions/example-ui-app/package.json index 9a7ed575ed..0c04c43e70 100644 --- a/solutions/example-ui-app/package.json +++ b/solutions/example-ui-app/package.json @@ -17,6 +17,7 @@ "build:test": "rushx build && rushx test", "depcheck": "depcheck", "dev": "next dev", + "license-checker": "license-checker --onlyAllow 'MIT; Apache-2.0; ISC; BSD'", "lint": "next lint --max-warnings 0 && rushx pkg-json-lint", "lint:fix": "next lint --fix", "make-badges": "istanbul-badges-readme --coverageDir=./temp/coverage --exitCode=1", @@ -64,6 +65,7 @@ "eslint-plugin-testing-library": "5.0.1", "istanbul-badges-readme": "1.8.1", "jest": "^27.5.1", + "license-checker": "~25.0.1", "npm-package-json-lint": "^6.3.0", "npm-package-json-lint-config-default": "^5.0.0", "sass": "^1.49.8", diff --git a/solutions/swb-app/package.json b/solutions/swb-app/package.json index 4c21bffc76..01f0569283 100644 --- a/solutions/swb-app/package.json +++ b/solutions/swb-app/package.json @@ -18,6 +18,7 @@ "build": "heft build --clean && rushx pkg-json-lint", "build:test": "heft test --clean && rushx pkg-json-lint && rushx make-badges", "depcheck": "depcheck", + "license-checker": "license-checker --onlyAllow 'MIT; Apache-2.0; ISC; BSD'", "lint:fix": "eslint . --fix", "make-badges": "istanbul-badges-readme --coverageDir=./temp/coverage --exitCode=1", "pkg-json-lint": "npmPkgJsonLint -c ../../.npmpackagejsonlintrc.json .", @@ -61,6 +62,7 @@ "eslint-plugin-import": "^2.26.0", "istanbul-badges-readme": "1.8.1", "jest": "^27.5.1", + "license-checker": "~25.0.1", "nodemon": "^2.0.19", "npm-package-json-lint": "^6.3.0", "npm-package-json-lint-config-default": "^5.0.0", diff --git a/solutions/swb-reference/package.json b/solutions/swb-reference/package.json index 87f2ab7f58..37e425fd88 100644 --- a/solutions/swb-reference/package.json +++ b/solutions/swb-reference/package.json @@ -22,6 +22,7 @@ "depcheck": "depcheck", "integration-tests": "rushx build && jest lib/integration-tests", "jest": "jest", + "license-checker": "license-checker --onlyAllow 'MIT; Apache-2.0; ISC; BSD'", "lint:fix": "eslint . --fix", "make-badges": "istanbul-badges-readme --coverageDir=./temp/coverage --exitCode=1", "pkg-json-lint": "npmPkgJsonLint -c ../../.npmpackagejsonlintrc.json .", @@ -88,6 +89,7 @@ "istanbul-badges-readme": "1.8.1", "jest": "^27.5.1", "jest-circus": "^26.6.3", + "license-checker": "~25.0.1", "npm-package-json-lint": "^6.3.0", "npm-package-json-lint-config-default": "^5.0.0", "sort-package-json": "^1.57.0", diff --git a/solutions/swb-ui/infrastructure/package.json b/solutions/swb-ui/infrastructure/package.json index 23b9fd9460..9636107eff 100644 --- a/solutions/swb-ui/infrastructure/package.json +++ b/solutions/swb-ui/infrastructure/package.json @@ -20,6 +20,7 @@ "build:test": "rushx build && rushx test", "cdk": "cdk", "cdk-deploy": "rushx build && cdk deploy --all --require-approval never --outputs-file ./src/config/${STAGE}.json", + "license-checker": "license-checker --onlyAllow 'MIT; Apache-2.0; ISC; BSD'", "sort-package-json": "sort-package-json package.json", "test": "rushx test:only", "test:only": "heft test --clean --no-build", @@ -40,6 +41,7 @@ "aws-cdk-lib": "^2.29.1", "constructs": "^10.0.0", "jest": "^27.5.1", + "license-checker": "~25.0.1", "sort-package-json": "^1.57.0", "source-map-support": "^0.5.16", "ts-jest": "^27.1.3", diff --git a/solutions/swb-ui/package.json b/solutions/swb-ui/package.json index 896bf3e843..96fab5c68a 100644 --- a/solutions/swb-ui/package.json +++ b/solutions/swb-ui/package.json @@ -21,6 +21,7 @@ "deploy-ui-and-api": "./scripts/deploy.sh", "dev": "./scripts/dev.sh", "export": "next build && next export", + "license-checker": "license-checker --onlyAllow 'MIT; Apache-2.0; ISC; BSD'", "lint": "next lint --max-warnings 0 && rushx pkg-json-lint", "lint:fix": "next lint --fix", "make-badges": "istanbul-badges-readme --coverageDir=./temp/coverage", @@ -79,6 +80,7 @@ "eslint-plugin-testing-library": "5.0.1", "istanbul-badges-readme": "1.8.1", "jest": "^27.5.1", + "license-checker": "~25.0.1", "nodemon": "^2.0.19", "npm-package-json-lint": "^6.3.0", "npm-package-json-lint-config-default": "^5.0.0", diff --git a/workbench-core/audit/package.json b/workbench-core/audit/package.json index 31eab54082..bc0cfdbd7f 100644 --- a/workbench-core/audit/package.json +++ b/workbench-core/audit/package.json @@ -18,6 +18,7 @@ "build": "heft build --clean && rushx pkg-json-lint", "build:test": "heft test --clean && rushx pkg-json-lint && rushx make-badges", "depcheck": "depcheck", + "license-checker": "license-checker --onlyAllow 'MIT; Apache-2.0; ISC; BSD'", "lint:fix": "eslint . --fix", "make-badges": "istanbul-badges-readme --coverageDir=./temp/coverage --exitCode=1", "pkg-json-lint": "npmPkgJsonLint -c ../../.npmpackagejsonlintrc.json .", @@ -43,6 +44,7 @@ "eslint": "^8.7.0", "eslint-plugin-import": "^2.26.0", "istanbul-badges-readme": "1.8.1", + "license-checker": "~25.0.1", "npm-package-json-lint": "^6.3.0", "npm-package-json-lint-config-default": "^5.0.0", "sort-package-json": "^1.57.0", diff --git a/workbench-core/authentication/package.json b/workbench-core/authentication/package.json index 0f98157636..0cf97e2df9 100644 --- a/workbench-core/authentication/package.json +++ b/workbench-core/authentication/package.json @@ -18,6 +18,7 @@ "build": "heft build --clean && rushx pkg-json-lint", "build:test": "heft test --clean && rushx pkg-json-lint && rushx make-badges", "depcheck": "depcheck", + "license-checker": "license-checker --onlyAllow 'MIT; Apache-2.0; ISC; BSD'", "lint:fix": "eslint . --fix", "make-badges": "istanbul-badges-readme --coverageDir=./temp/coverage --exitCode=1", "pkg-json-lint": "npmPkgJsonLint -c ../../.npmpackagejsonlintrc.json .", @@ -51,6 +52,7 @@ "eslint": "^8.7.0", "eslint-plugin-import": "^2.26.0", "istanbul-badges-readme": "1.8.1", + "license-checker": "~25.0.1", "npm-package-json-lint": "^6.3.0", "npm-package-json-lint-config-default": "^5.0.0", "sort-package-json": "^1.57.0", diff --git a/workbench-core/authorization/package.json b/workbench-core/authorization/package.json index b961545971..f288fec26e 100644 --- a/workbench-core/authorization/package.json +++ b/workbench-core/authorization/package.json @@ -18,6 +18,7 @@ "build": "heft build --clean && rushx pkg-json-lint", "build:test": "heft test --clean && rushx pkg-json-lint && rushx make-badges", "depcheck": "depcheck", + "license-checker": "license-checker --onlyAllow 'MIT; Apache-2.0; ISC; BSD'", "lint:fix": "eslint . --fix", "make-badges": "istanbul-badges-readme --coverageDir=./temp/coverage --exitCode=1", "pkg-json-lint": "npmPkgJsonLint -c ../../.npmpackagejsonlintrc.json .", @@ -47,6 +48,7 @@ "istanbul-badges-readme": "1.8.1", "jest": "^27.5.1", "jest-fast-check": "^1.0.2", + "license-checker": "~25.0.1", "npm-package-json-lint": "^6.3.0", "npm-package-json-lint-config-default": "^5.0.0", "sort-package-json": "^1.57.0", diff --git a/workbench-core/base/package.json b/workbench-core/base/package.json index bbc1a3184e..39ebb5d379 100644 --- a/workbench-core/base/package.json +++ b/workbench-core/base/package.json @@ -19,6 +19,7 @@ "build:test": "heft test --clean && rushx pkg-json-lint && rushx make-badges", "depcheck": "depcheck", "jest": "jest", + "license-checker": "license-checker --onlyAllow 'MIT; Apache-2.0; ISC; BSD'", "lint:fix": "eslint . --fix", "make-badges": "istanbul-badges-readme --coverageDir=./temp/coverage --exitCode=1", "pkg-json-lint": "npmPkgJsonLint -c ../../.npmpackagejsonlintrc.json .", @@ -74,6 +75,7 @@ "eslint-plugin-import": "^2.26.0", "istanbul-badges-readme": "1.8.1", "jest": "^27.5.1", + "license-checker": "~25.0.1", "npm-package-json-lint": "^6.3.0", "npm-package-json-lint-config-default": "^5.0.0", "sort-package-json": "^1.57.0", diff --git a/workbench-core/datasets/package.json b/workbench-core/datasets/package.json index 5e21b320df..1e4ec19607 100644 --- a/workbench-core/datasets/package.json +++ b/workbench-core/datasets/package.json @@ -18,6 +18,7 @@ "build": "heft build --clean && rushx pkg-json-lint", "build:test": "heft test --clean && rushx pkg-json-lint && rushx make-badges", "depcheck": "depcheck", + "license-checker": "license-checker --onlyAllow 'MIT; Apache-2.0; ISC; BSD'", "lint:fix": "eslint . --fix", "make-badges": "istanbul-badges-readme --coverageDir=./temp/coverage --exitCode=1", "pkg-json-lint": "npmPkgJsonLint -c ../../.npmpackagejsonlintrc.json .", @@ -69,6 +70,7 @@ "istanbul-badges-readme": "1.8.1", "jest": "^27.5.1", "jest-fast-check": "^1.0.2", + "license-checker": "~25.0.1", "npm-package-json-lint": "^6.3.0", "npm-package-json-lint-config-default": "^5.0.0", "sort-package-json": "^1.57.0", diff --git a/workbench-core/environments/package.json b/workbench-core/environments/package.json index c5ce7e741a..336b991cbc 100644 --- a/workbench-core/environments/package.json +++ b/workbench-core/environments/package.json @@ -19,6 +19,7 @@ "build:test": "heft test --clean && rushx pkg-json-lint && rushx make-badges", "depcheck": "depcheck", "jest": "jest", + "license-checker": "license-checker --onlyAllow 'MIT; Apache-2.0; ISC; BSD'", "lint:fix": "eslint . --fix", "make-badges": "istanbul-badges-readme --coverageDir=./temp/coverage --exitCode=1", "pkg-json-lint": "npmPkgJsonLint -c ../../.npmpackagejsonlintrc.json .", @@ -80,6 +81,7 @@ "eslint-plugin-import": "^2.26.0", "istanbul-badges-readme": "1.8.1", "jest": "^27.5.1", + "license-checker": "~25.0.1", "npm-package-json-lint": "^6.3.0", "npm-package-json-lint-config-default": "^5.0.0", "sort-package-json": "^1.57.0", diff --git a/workbench-core/eslint-custom/package.json b/workbench-core/eslint-custom/package.json index dede43fe71..cbebb64991 100644 --- a/workbench-core/eslint-custom/package.json +++ b/workbench-core/eslint-custom/package.json @@ -18,6 +18,7 @@ "build": "heft build --clean && rushx pkg-json-lint", "build:test": "heft test --clean && rushx pkg-json-lint", "depcheck": "depcheck", + "license-checker": "license-checker --onlyAllow 'MIT; Apache-2.0; ISC; BSD'", "lint:fix": "eslint . --fix", "make-badges": "echo 'NO TEST COVERAGE, SO NO BADGE !'", "pkg-json-lint": "npmPkgJsonLint -c ../../.npmpackagejsonlintrc.json .", @@ -38,6 +39,7 @@ "@types/heft-jest": "1.0.2", "@types/node": "^14", "depcheck": "^1.4.3", + "license-checker": "~25.0.1", "npm-package-json-lint": "^6.3.0", "npm-package-json-lint-config-default": "^5.0.0", "sort-package-json": "^1.57.0", diff --git a/workbench-core/example/express/package.json b/workbench-core/example/express/package.json index e6ad00bde4..90f5cc5fdd 100644 --- a/workbench-core/example/express/package.json +++ b/workbench-core/example/express/package.json @@ -18,6 +18,7 @@ "build": "heft build --clean && rushx pkg-json-lint", "build:test": "heft test --clean && rushx pkg-json-lint", "depcheck": "depcheck", + "license-checker": "license-checker --onlyAllow 'MIT; Apache-2.0; ISC; BSD'", "lint:fix": "eslint . --fix", "make-badges": "echo 'NO TEST COVERAGE, SO NO BADGE !'", "pkg-json-lint": "npmPkgJsonLint -c ../../../.npmpackagejsonlintrc.json .", @@ -44,6 +45,7 @@ "eslint-plugin-import": "^2.26.0", "eslint-plugin-security": "^1.4.0", "express": "^4.17.3", + "license-checker": "~25.0.1", "npm-package-json-lint": "^6.3.0", "npm-package-json-lint-config-default": "^5.0.0", "sort-package-json": "^1.57.0", diff --git a/workbench-core/example/infrastructure/package.json b/workbench-core/example/infrastructure/package.json index 106791d861..8204501c5f 100644 --- a/workbench-core/example/infrastructure/package.json +++ b/workbench-core/example/infrastructure/package.json @@ -24,6 +24,7 @@ "cdk:synth": "cdk synth", "compile": "node scripts/buildLambda.js", "depcheck": "depcheck", + "license-checker": "license-checker --onlyAllow 'MIT; Apache-2.0; ISC; BSD'", "lint:fix": "eslint . --fix", "make-badges": "istanbul-badges-readme --coverageDir=./temp/coverage --exitCode=1", "pkg-json-lint": "npmPkgJsonLint -c ../../../.npmpackagejsonlintrc.json .", @@ -65,6 +66,7 @@ "express": "^4.17.3", "istanbul-badges-readme": "1.8.1", "jest": "^27.5.1", + "license-checker": "~25.0.1", "npm-package-json-lint": "^6.3.0", "npm-package-json-lint-config-default": "^5.0.0", "sort-package-json": "^1.57.0", diff --git a/workbench-core/infrastructure/package.json b/workbench-core/infrastructure/package.json index a710f20323..ae4ec4424e 100644 --- a/workbench-core/infrastructure/package.json +++ b/workbench-core/infrastructure/package.json @@ -18,6 +18,7 @@ "build": "heft build --clean && rushx pkg-json-lint", "build:test": "heft test --clean && rushx pkg-json-lint && rushx make-badges", "depcheck": "depcheck", + "license-checker": "license-checker --onlyAllow 'MIT; Apache-2.0; ISC; BSD'", "lint:fix": "eslint . --fix", "make-badges": "istanbul-badges-readme --coverageDir=./temp/coverage --exitCode=1", "pkg-json-lint": "npmPkgJsonLint -c ../../.npmpackagejsonlintrc.json .", @@ -44,6 +45,7 @@ "eslint-plugin-import": "^2.26.0", "eslint-plugin-security": "^1.4.0", "istanbul-badges-readme": "1.8.1", + "license-checker": "~25.0.1", "npm-package-json-lint": "^6.3.0", "npm-package-json-lint-config-default": "^5.0.0", "sort-package-json": "^1.57.0", diff --git a/workbench-core/logging/package.json b/workbench-core/logging/package.json index 7863c0452e..2634f9c3cf 100644 --- a/workbench-core/logging/package.json +++ b/workbench-core/logging/package.json @@ -18,6 +18,7 @@ "build": "heft build --clean && rushx pkg-json-lint", "build:test": "heft test --clean --silent && rushx pkg-json-lint && rushx make-badges", "depcheck": "depcheck", + "license-checker": "license-checker --onlyAllow 'MIT; Apache-2.0; ISC; BSD'", "lint:fix": "eslint . --fix", "make-badges": "istanbul-badges-readme --coverageDir=./temp/coverage --exitCode=1", "pkg-json-lint": "npmPkgJsonLint -c ../../.npmpackagejsonlintrc.json .", @@ -46,6 +47,7 @@ "istanbul-badges-readme": "1.8.1", "jest": "^27.5.1", "jest-fast-check": "^1.0.2", + "license-checker": "~25.0.1", "npm-package-json-lint": "^6.3.0", "npm-package-json-lint-config-default": "^5.0.0", "sort-package-json": "^1.57.0", diff --git a/workbench-core/repo-scripts/repo-toolbox/package.json b/workbench-core/repo-scripts/repo-toolbox/package.json index 8642c1c790..465f8c2642 100644 --- a/workbench-core/repo-scripts/repo-toolbox/package.json +++ b/workbench-core/repo-scripts/repo-toolbox/package.json @@ -18,6 +18,7 @@ "build": "heft build --clean && rushx pkg-json-lint", "build:test": "heft test --clean && rushx pkg-json-lint", "depcheck": "depcheck", + "license-checker": "license-checker --onlyAllow 'MIT; Apache-2.0; ISC; BSD'", "lint:fix": "eslint . --fix", "make-badges": "echo 'NO TEST COVERAGE, SO NO BADGE !'", "pkg-json-lint": "npmPkgJsonLint -c ../../../.npmpackagejsonlintrc.json .", @@ -45,6 +46,7 @@ "eslint": "^8.7.0", "eslint-plugin-import": "^2.26.0", "eslint-plugin-security": "^1.4.0", + "license-checker": "~25.0.1", "npm-package-json-lint": "^6.3.0", "npm-package-json-lint-config-default": "^5.0.0", "sort-package-json": "^1.57.0", From 40108fdfce976cd21067057126dca5d52f10bd7c Mon Sep 17 00:00:00 2001 From: Brendon Hudnell Date: Mon, 15 Aug 2022 12:15:10 -0600 Subject: [PATCH 009/621] fix: cookies not being stored correctly (#445) * fix(solutions/swb): changed logout to a POST route, added explicit programs for scripts * fix: changed aipHelper withCredentialss defaults to true, removed unneeded cors allowedHeaders * fix: changed sameSite to none, using maxAge instead of expires * refactor: replaced utils dependency on aws sdk with own enum * fix: now using defaults when sdk call is undefined * test: added missing property to workbenchCognito tests * refactor(workbench-core/authentication): refactored package to use ms as default unit of time * feat: made sameSite cookie setting configurable with a default of strict * fix: lowered id and access token lifespan to 15 minutes, token verifier only verify access tokens * test: fixed broken test * revert: remove accidentally committed files * fix: pR fix * Update workbench-core/authentication/src/authenticationMiddleware.ts Co-authored-by: Robert Smayda * Update workbench-core/authentication/src/authenticationMiddleware.ts Co-authored-by: Robert Smayda * Update workbench-core/authentication/src/authenticationMiddleware.ts Co-authored-by: Robert Smayda * Update workbench-core/authentication/src/authenticationMiddleware.ts Co-authored-by: Robert Smayda * docs: added rush change files, added missing tsdoc * Update common/changes/@aws/swb-app/fix-re-enable-cookie-based-authentication_2022-08-15-17-33.json Co-authored-by: Robert Smayda * Update common/changes/@aws/workbench-core-infrastructure/fix-re-enable-cookie-based-authentication_2022-08-15-17-33.json Co-authored-by: Robert Smayda * Update common/changes/@aws/workbench-core-environments/fix-re-enable-cookie-based-authentication_2022-08-15-17-33.json Co-authored-by: Robert Smayda * Update common/changes/@aws/workbench-core-authentication/fix-re-enable-cookie-based-authentication_2022-08-15-17-33.json Co-authored-by: Robert Smayda * fix: missed rename in test file Co-authored-by: Robert Smayda --- ...based-authentication_2022-08-15-17-33.json | 10 + ...based-authentication_2022-08-15-17-33.json | 10 + ...based-authentication_2022-08-15-17-33.json | 10 + ...based-authentication_2022-08-15-17-33.json | 10 + solutions/swb-app/src/authRoutes.ts | 11 +- solutions/swb-app/src/generateRouter.ts | 1 - solutions/swb-app/src/staticRouteConfig.ts | 2 +- .../integration-tests/config/example.yaml | 2 +- solutions/swb-reference/package.json | 2 +- solutions/swb-ui/package.json | 4 +- solutions/swb-ui/src/api/apiHelper.ts | 12 +- solutions/swb-ui/src/api/auth.ts | 6 +- .../authentication/src/__mocks__/utils.ts | 9 +- .../src/authenticationMiddleware.test.ts | 186 +++++++++++------- .../src/authenticationMiddleware.ts | 81 ++++---- workbench-core/authentication/src/index.ts | 2 +- .../cognitoAuthenticationPlugin.test.ts | 124 +++++------- .../plugins/cognitoAuthenticationPlugin.ts | 72 +++---- workbench-core/authentication/src/tokens.ts | 6 +- .../authentication/src/utils.test.ts | 45 +++-- workbench-core/authentication/src/utils.ts | 30 ++- .../src/services/environmentService.ts | 2 +- .../src/workbenchCognito.test.ts | 16 +- .../infrastructure/src/workbenchCognito.ts | 7 +- 24 files changed, 367 insertions(+), 293 deletions(-) create mode 100644 common/changes/@aws/swb-app/fix-re-enable-cookie-based-authentication_2022-08-15-17-33.json create mode 100644 common/changes/@aws/workbench-core-authentication/fix-re-enable-cookie-based-authentication_2022-08-15-17-33.json create mode 100644 common/changes/@aws/workbench-core-environments/fix-re-enable-cookie-based-authentication_2022-08-15-17-33.json create mode 100644 common/changes/@aws/workbench-core-infrastructure/fix-re-enable-cookie-based-authentication_2022-08-15-17-33.json diff --git a/common/changes/@aws/swb-app/fix-re-enable-cookie-based-authentication_2022-08-15-17-33.json b/common/changes/@aws/swb-app/fix-re-enable-cookie-based-authentication_2022-08-15-17-33.json new file mode 100644 index 0000000000..f436204eb8 --- /dev/null +++ b/common/changes/@aws/swb-app/fix-re-enable-cookie-based-authentication_2022-08-15-17-33.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@aws/swb-app", + "comment": "Changed logout route to POST, removed unnecessary CORS allowed headers, added samesSite: 'none' cookie setting to authN routes", + "type": "minor" + } + ], + "packageName": "@aws/swb-app" +} \ No newline at end of file diff --git a/common/changes/@aws/workbench-core-authentication/fix-re-enable-cookie-based-authentication_2022-08-15-17-33.json b/common/changes/@aws/workbench-core-authentication/fix-re-enable-cookie-based-authentication_2022-08-15-17-33.json new file mode 100644 index 0000000000..a6348531cb --- /dev/null +++ b/common/changes/@aws/workbench-core-authentication/fix-re-enable-cookie-based-authentication_2022-08-15-17-33.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@aws/workbench-core-authentication", + "comment": "Added option to middleware to set the sameSite cookie option if 'strict' is too restrictive, removed Authorization header based authentication, now using maxAge instead of expires for auth cookies, changed getTimeInSeconds to getTimeInMS, cognitoAuthenticationPlugin now uses ms internally, validateToken now only validates access tokens", + "type": "minor" + } + ], + "packageName": "@aws/workbench-core-authentication" +} \ No newline at end of file diff --git a/common/changes/@aws/workbench-core-environments/fix-re-enable-cookie-based-authentication_2022-08-15-17-33.json b/common/changes/@aws/workbench-core-environments/fix-re-enable-cookie-based-authentication_2022-08-15-17-33.json new file mode 100644 index 0000000000..93aa905fdf --- /dev/null +++ b/common/changes/@aws/workbench-core-environments/fix-re-enable-cookie-based-authentication_2022-08-15-17-33.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@aws/workbench-core-environments", + "comment": "Replaced require with import", + "type": "patch" + } + ], + "packageName": "@aws/workbench-core-environments" +} \ No newline at end of file diff --git a/common/changes/@aws/workbench-core-infrastructure/fix-re-enable-cookie-based-authentication_2022-08-15-17-33.json b/common/changes/@aws/workbench-core-infrastructure/fix-re-enable-cookie-based-authentication_2022-08-15-17-33.json new file mode 100644 index 0000000000..dc062709ab --- /dev/null +++ b/common/changes/@aws/workbench-core-infrastructure/fix-re-enable-cookie-based-authentication_2022-08-15-17-33.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@aws/workbench-core-infrastructure", + "comment": "Added default token validity length (15 minutes for id and access, 30 days for refresh)", + "type": "patch" + } + ], + "packageName": "@aws/workbench-core-infrastructure" +} \ No newline at end of file diff --git a/solutions/swb-app/src/authRoutes.ts b/solutions/swb-app/src/authRoutes.ts index bcee9f5fb2..9d35ba2b6e 100644 --- a/solutions/swb-app/src/authRoutes.ts +++ b/solutions/swb-app/src/authRoutes.ts @@ -21,11 +21,14 @@ export function setUpAuthRoutes(router: Router, auth: AuthenticationService, log router.get('/login', wrapAsync(getAuthorizationCodeUrl(auth))); // User would have manually logged in at this point, and received an auth code. Exchange auth code for token - router.post('/token', wrapAsync(getTokensFromAuthorizationCode(auth, { loggingService: logger }))); + router.post( + '/token', + wrapAsync(getTokensFromAuthorizationCode(auth, { loggingService: logger, sameSite: 'none' })) + ); - router.get('/logout', wrapAsync(logoutUser(auth, { loggingService: logger }))); + router.post('/logout', wrapAsync(logoutUser(auth, { loggingService: logger, sameSite: 'none' }))); - router.get('/refresh', wrapAsync(refreshAccessToken(auth, { loggingService: logger }))); + router.get('/refresh', wrapAsync(refreshAccessToken(auth, { loggingService: logger, sameSite: 'none' }))); - router.get('/loggedIn', wrapAsync(isUserLoggedIn(auth, { loggingService: logger }))); + router.get('/loggedIn', wrapAsync(isUserLoggedIn(auth, { loggingService: logger, sameSite: 'none' }))); } diff --git a/solutions/swb-app/src/generateRouter.ts b/solutions/swb-app/src/generateRouter.ts index e1088be440..1ff75521a5 100644 --- a/solutions/swb-app/src/generateRouter.ts +++ b/solutions/swb-app/src/generateRouter.ts @@ -46,7 +46,6 @@ export function generateRouter(apiRouteConfig: ApiRouteConfig): Express { app.use( cors({ origin: apiRouteConfig.allowedOrigins, - allowedHeaders: ['Set-Cookie', 'Content-Type'], credentials: true }) ); diff --git a/solutions/swb-app/src/staticRouteConfig.ts b/solutions/swb-app/src/staticRouteConfig.ts index b5c3c686e7..4b2462cb73 100644 --- a/solutions/swb-app/src/staticRouteConfig.ts +++ b/solutions/swb-app/src/staticRouteConfig.ts @@ -211,7 +211,7 @@ export const routesIgnored: RoutesIgnored = { POST: true }, '/logout': { - GET: true + POST: true }, '/refresh': { GET: true diff --git a/solutions/swb-reference/integration-tests/config/example.yaml b/solutions/swb-reference/integration-tests/config/example.yaml index 5a2dedd528..f4cacd4762 100644 --- a/solutions/swb-reference/integration-tests/config/example.yaml +++ b/solutions/swb-reference/integration-tests/config/example.yaml @@ -13,4 +13,4 @@ #Cognito Integ Test Client #rootUsername: '' -#rootPasswordParamStorePath: '' \ No newline at end of file +#rootPasswordParamStorePath: '' diff --git a/solutions/swb-reference/package.json b/solutions/swb-reference/package.json index 37e425fd88..860c4045bb 100644 --- a/solutions/swb-reference/package.json +++ b/solutions/swb-reference/package.json @@ -18,7 +18,7 @@ "cdk": "cdk", "cdk-deploy": "rush build && rushx compile && cdk deploy --all --require-approval never --outputs-file ./src/config/${STAGE}.json", "cdk-deploy:noBuild": "rushx compile && cdk deploy --all", - "compile": "scripts/buildLambda.js", + "compile": "node scripts/buildLambda.js", "depcheck": "depcheck", "integration-tests": "rushx build && jest lib/integration-tests", "jest": "jest", diff --git a/solutions/swb-ui/package.json b/solutions/swb-ui/package.json index 96fab5c68a..65944aeaba 100644 --- a/solutions/swb-ui/package.json +++ b/solutions/swb-ui/package.json @@ -18,8 +18,8 @@ "build": "next build && rushx lint", "build:test": "rushx build && rushx test", "depcheck": "depcheck", - "deploy-ui-and-api": "./scripts/deploy.sh", - "dev": "./scripts/dev.sh", + "deploy-ui-and-api": "bash ./scripts/deploy.sh", + "dev": "bash ./scripts/dev.sh", "export": "next build && next export", "license-checker": "license-checker --onlyAllow 'MIT; Apache-2.0; ISC; BSD'", "lint": "next lint --max-warnings 0 && rushx pkg-json-lint", diff --git a/solutions/swb-ui/src/api/apiHelper.ts b/solutions/swb-ui/src/api/apiHelper.ts index 7a7fcd302c..bf504d58e0 100644 --- a/solutions/swb-ui/src/api/apiHelper.ts +++ b/solutions/swb-ui/src/api/apiHelper.ts @@ -7,7 +7,7 @@ import axios from 'axios'; const urlBase: string | undefined = process.env.NEXT_PUBLIC_API_BASE_URL; -const httpApiGet = async (urlPath: string, params: any, withCredentials: boolean = false): Promise => { +const httpApiGet = async (urlPath: string, params: any, withCredentials: boolean = true): Promise => { const options = { method: 'GET', url: `${urlBase}${urlPath}`, @@ -16,7 +16,7 @@ const httpApiGet = async (urlPath: string, params: any, withCredentials: boolean }; return await fetchData(options); }; -const httpApiPost = async (urlPath: string, params: any, withCredentials: boolean = false): Promise => { +const httpApiPost = async (urlPath: string, params: any, withCredentials: boolean = true): Promise => { const options = { method: 'POST', url: `${urlBase}${urlPath}`, @@ -25,7 +25,7 @@ const httpApiPost = async (urlPath: string, params: any, withCredentials: boolea }; return await fetchData(options); }; -const httpApiPut = async (urlPath: string, params: any, withCredentials: boolean = false): Promise => { +const httpApiPut = async (urlPath: string, params: any, withCredentials: boolean = true): Promise => { const options = { method: 'PUT', url: `${urlBase}${urlPath}`, @@ -34,11 +34,7 @@ const httpApiPut = async (urlPath: string, params: any, withCredentials: boolean }; return await fetchData(options); }; -const httpApiDelete = async ( - urlPath: string, - params: any, - withCredentials: boolean = false -): Promise => { +const httpApiDelete = async (urlPath: string, params: any, withCredentials: boolean = true): Promise => { const options = { method: 'DELETE', url: `${urlBase}${urlPath}`, diff --git a/solutions/swb-ui/src/api/auth.ts b/solutions/swb-ui/src/api/auth.ts index ee5117d5cd..4801e99109 100644 --- a/solutions/swb-ui/src/api/auth.ts +++ b/solutions/swb-ui/src/api/auth.ts @@ -6,19 +6,19 @@ import { httpApiGet, httpApiPost } from './apiHelper'; const token = async (body: { code: string; codeVerifier: string | null }): Promise => { - return await httpApiPost('token', body, true); + return await httpApiPost('token', body); }; const login = async (): Promise => { return await httpApiGet( 'login/?stateVerifier=TEMP_STATE_VERIFIER&codeChallenge=TEMP_CODE_CHALLENGE', {}, - true + false ); }; const logout = async (): Promise => { - return await httpApiGet('logout', {}); + return await httpApiPost('logout', {}); }; const checkIfloggedIn = async (): Promise => { diff --git a/workbench-core/authentication/src/__mocks__/utils.ts b/workbench-core/authentication/src/__mocks__/utils.ts index 09b2f124f7..da19cfc0db 100644 --- a/workbench-core/authentication/src/__mocks__/utils.ts +++ b/workbench-core/authentication/src/__mocks__/utils.ts @@ -3,8 +3,13 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { TimeUnitsType } from '@aws-sdk/client-cognito-identity-provider'; +export enum TimeUnits { + DAYS = 'days', + HOURS = 'hours', + MINUTES = 'minutes', + SECONDS = 'seconds' +} -export function getTimeInSeconds(length: number, units: TimeUnitsType): number { +export function getTimeInMS(length: number, units: TimeUnits): number { return 1; } diff --git a/workbench-core/authentication/src/authenticationMiddleware.test.ts b/workbench-core/authentication/src/authenticationMiddleware.test.ts index fdef430f2c..259c013cb5 100644 --- a/workbench-core/authentication/src/authenticationMiddleware.test.ts +++ b/workbench-core/authentication/src/authenticationMiddleware.test.ts @@ -30,7 +30,7 @@ const cognitoPluginOptions: CognitoAuthenticationPluginOptions = { websiteUrl: 'fake-website-url' } as const; -const cookieOpts = { +const defaultCookieOpts = { httpOnly: true, secure: true, sameSite: 'strict' @@ -51,6 +51,7 @@ describe('authenticationMiddleware integration tests', () => { beforeEach(() => { res = { cookie: jest.fn((name, val, opts) => res), + clearCookie: jest.fn((name, opts) => res), status: jest.fn((code) => res), sendStatus: jest.fn((code) => res), json: jest.fn((body) => res), @@ -66,9 +67,6 @@ describe('authenticationMiddleware integration tests', () => { }); it('should return 200, the id token in the response body, and set the access and refresh tokens as cookies when the code and codeVerifier params are valid', async () => { - const accessExpires = new Date(tokens.accessToken.expiresIn * 1000); - const refreshExpires = new Date(tokens.refreshToken.expiresIn * 1000); - const req: Request = { body: { code: 'validCode', @@ -79,19 +77,15 @@ describe('authenticationMiddleware integration tests', () => { await getTokensFromAuthorizationCodeRouteHandler(req, res); expect(res.cookie).toHaveBeenNthCalledWith(1, 'access_token', tokens.accessToken.token, { - ...cookieOpts, - expires: accessExpires + ...defaultCookieOpts, + maxAge: tokens.accessToken.expiresIn }); expect(res.cookie).toHaveBeenNthCalledWith(2, 'refresh_token', tokens.refreshToken.token, { - ...cookieOpts, - expires: refreshExpires + ...defaultCookieOpts, + maxAge: tokens.refreshToken.expiresIn }); expect(res.status).toHaveBeenCalledWith(200); - //TODO: Remove accessToken once cookies are properly set - expect(res.json).toHaveBeenCalledWith({ - idToken: tokens.idToken.token, - accessToken: tokens.accessToken.token - }); + expect(res.json).toHaveBeenCalledWith({ idToken: tokens.idToken.token }); }); it('should set the access and refresh tokens as session cookies when the AuthenticationService IDP sets them as such', async () => { @@ -117,16 +111,13 @@ describe('authenticationMiddleware integration tests', () => { await getTokensFromAuthorizationCodeRouteHandler(req, res); expect(res.cookie).toHaveBeenNthCalledWith(1, 'access_token', tokens.accessToken.token, { - ...cookieOpts + ...defaultCookieOpts }); expect(res.cookie).toHaveBeenNthCalledWith(2, 'refresh_token', tokens.refreshToken.token, { - ...cookieOpts + ...defaultCookieOpts }); expect(res.status).toHaveBeenCalledWith(200); - expect(res.json).toHaveBeenCalledWith({ - idToken: tokens.idToken.token, - accessToken: tokens.accessToken.token - }); + expect(res.json).toHaveBeenCalledWith({ idToken: tokens.idToken.token }); }); it('should return 400 when code param is missing', async () => { @@ -238,6 +229,33 @@ describe('authenticationMiddleware integration tests', () => { expect(res.sendStatus).toHaveBeenCalledWith(401); expect(loggingSpy).toHaveBeenCalledTimes(1); }); + + it('should return 200, the id token in the response body, and set the access and refresh tokens as cookies when the sameSite option is set to "none"', async () => { + getTokensFromAuthorizationCodeRouteHandler = getTokensFromAuthorizationCode(authenticationService, { + sameSite: 'none' + }); + const req: Request = { + body: { + code: 'validCode', + codeVerifier: 'validCodeVerifier' + } + } as Request; + + await getTokensFromAuthorizationCodeRouteHandler(req, res); + + expect(res.cookie).toHaveBeenNthCalledWith(1, 'access_token', tokens.accessToken.token, { + ...defaultCookieOpts, + sameSite: 'none', + maxAge: tokens.accessToken.expiresIn + }); + expect(res.cookie).toHaveBeenNthCalledWith(2, 'refresh_token', tokens.refreshToken.token, { + ...defaultCookieOpts, + sameSite: 'none', + maxAge: tokens.refreshToken.expiresIn + }); + expect(res.status).toHaveBeenCalledWith(200); + expect(res.json).toHaveBeenCalledWith({ idToken: tokens.idToken.token }); + }); }); describe('getAuthorizationCodeUrl tests', () => { @@ -483,14 +501,8 @@ describe('authenticationMiddleware integration tests', () => { await logoutUserRouteHandler(req, res); - expect(res.cookie).toHaveBeenNthCalledWith(1, 'access_token', 'cleared', { - sameSite: 'lax', - expires: new Date(0) - }); - expect(res.cookie).toHaveBeenNthCalledWith(2, 'refresh_token', 'cleared', { - sameSite: 'lax', - expires: new Date(0) - }); + expect(res.clearCookie).toHaveBeenNthCalledWith(1, 'access_token', defaultCookieOpts); + expect(res.clearCookie).toHaveBeenNthCalledWith(2, 'refresh_token', defaultCookieOpts); expect(res.status).toHaveBeenCalledWith(200); expect(res.json).toHaveBeenCalledWith({ logoutUrl: 'https://www.fakeurl.com/logout?client_id=fake-id&logout_uri=https://www.fakewebsite.com' @@ -504,14 +516,8 @@ describe('authenticationMiddleware integration tests', () => { await logoutUserRouteHandler(req, res); - expect(res.cookie).toHaveBeenNthCalledWith(1, 'access_token', 'cleared', { - sameSite: 'lax', - expires: new Date(0) - }); - expect(res.cookie).toHaveBeenNthCalledWith(2, 'refresh_token', 'cleared', { - sameSite: 'lax', - expires: new Date(0) - }); + expect(res.clearCookie).toHaveBeenNthCalledWith(1, 'access_token', defaultCookieOpts); + expect(res.clearCookie).toHaveBeenNthCalledWith(2, 'refresh_token', defaultCookieOpts); expect(res.status).toHaveBeenCalledWith(200); expect(res.json).toHaveBeenCalledWith({ logoutUrl: 'https://www.fakeurl.com/logout?client_id=fake-id&logout_uri=https://www.fakewebsite.com' @@ -527,14 +533,8 @@ describe('authenticationMiddleware integration tests', () => { await logoutUserRouteHandler(req, res); - expect(res.cookie).toHaveBeenNthCalledWith(1, 'access_token', 'cleared', { - sameSite: 'lax', - expires: new Date(0) - }); - expect(res.cookie).toHaveBeenNthCalledWith(2, 'refresh_token', 'cleared', { - sameSite: 'lax', - expires: new Date(0) - }); + expect(res.clearCookie).toHaveBeenNthCalledWith(1, 'access_token', defaultCookieOpts); + expect(res.clearCookie).toHaveBeenNthCalledWith(2, 'refresh_token', defaultCookieOpts); expect(res.status).toHaveBeenCalledWith(200); expect(res.json).toHaveBeenCalledWith({ logoutUrl: 'https://www.fakeurl.com/logout?client_id=fake-id&logout_uri=https://www.fakewebsite.com' @@ -550,14 +550,8 @@ describe('authenticationMiddleware integration tests', () => { await logoutUserRouteHandler(req, res); - expect(res.cookie).toHaveBeenNthCalledWith(1, 'access_token', 'cleared', { - sameSite: 'lax', - expires: new Date(0) - }); - expect(res.cookie).toHaveBeenNthCalledWith(2, 'refresh_token', 'cleared', { - sameSite: 'lax', - expires: new Date(0) - }); + expect(res.clearCookie).toHaveBeenNthCalledWith(1, 'access_token', defaultCookieOpts); + expect(res.clearCookie).toHaveBeenNthCalledWith(2, 'refresh_token', defaultCookieOpts); expect(res.status).toHaveBeenCalledWith(200); expect(res.json).toHaveBeenCalledWith({ logoutUrl: 'https://www.fakeurl.com/logout?client_id=fake-id&logout_uri=https://www.fakewebsite.com' @@ -589,19 +583,37 @@ describe('authenticationMiddleware integration tests', () => { await logoutUserRouteHandler(req, res); - expect(res.cookie).toHaveBeenNthCalledWith(1, 'access_token', 'cleared', { - sameSite: 'lax', - expires: new Date(0) + expect(res.clearCookie).toHaveBeenNthCalledWith(1, 'access_token', defaultCookieOpts); + expect(res.clearCookie).toHaveBeenNthCalledWith(2, 'refresh_token', defaultCookieOpts); + expect(res.status).toHaveBeenCalledWith(200); + expect(res.json).toHaveBeenCalledWith({ + logoutUrl: 'https://www.fakeurl.com/logout?client_id=fake-id&logout_uri=https://www.fakewebsite.com' + }); + expect(loggingSpy).toHaveBeenCalledTimes(1); + }); + + it('should return 200, clear cookies, and revoke refresh token when the sameSite option is set to "none"', async () => { + logoutUserRouteHandler = logoutUser(authenticationService, { sameSite: 'none' }); + const req: Request = { + cookies: { + refresh_token: 'validToken' + } + } as Request; + + await logoutUserRouteHandler(req, res); + + expect(res.clearCookie).toHaveBeenNthCalledWith(1, 'access_token', { + ...defaultCookieOpts, + sameSite: 'none' }); - expect(res.cookie).toHaveBeenNthCalledWith(2, 'refresh_token', 'cleared', { - sameSite: 'lax', - expires: new Date(0) + expect(res.clearCookie).toHaveBeenNthCalledWith(2, 'refresh_token', { + ...defaultCookieOpts, + sameSite: 'none' }); expect(res.status).toHaveBeenCalledWith(200); expect(res.json).toHaveBeenCalledWith({ logoutUrl: 'https://www.fakeurl.com/logout?client_id=fake-id&logout_uri=https://www.fakewebsite.com' }); - expect(loggingSpy).toHaveBeenCalledTimes(1); }); }); @@ -613,8 +625,6 @@ describe('authenticationMiddleware integration tests', () => { }); it('should return 200, the id token in the response body, and set the access token as a cookie when the refresh_token cookie is present and valid', async () => { - const accessExpires = new Date(tokens.accessToken.expiresIn * 1000); - const req: Request = { cookies: { refresh_token: 'validToken' @@ -624,8 +634,8 @@ describe('authenticationMiddleware integration tests', () => { await refreshAccessTokenRouteHandler(req, res); expect(res.cookie).toHaveBeenCalledWith('access_token', tokens.accessToken.token, { - ...cookieOpts, - expires: accessExpires + ...defaultCookieOpts, + maxAge: tokens.accessToken.expiresIn }); expect(res.status).toHaveBeenCalledWith(200); expect(res.json).toHaveBeenCalledWith({ idToken: tokens.idToken.token }); @@ -649,7 +659,9 @@ describe('authenticationMiddleware integration tests', () => { await refreshAccessTokenRouteHandler(req, res); - expect(res.cookie).toHaveBeenCalledWith('access_token', tokens.accessToken.token, { ...cookieOpts }); + expect(res.cookie).toHaveBeenCalledWith('access_token', tokens.accessToken.token, { + ...defaultCookieOpts + }); expect(res.status).toHaveBeenCalledWith(200); expect(res.json).toHaveBeenCalledWith({ idToken: tokens.idToken.token }); }); @@ -718,6 +730,25 @@ describe('authenticationMiddleware integration tests', () => { expect(res.sendStatus).toHaveBeenCalledWith(401); expect(loggingSpy).toHaveBeenCalledTimes(1); }); + + it('should return 200, the id token in the response body, and set the access token as a cookie when the sameSite option is set to "none"', async () => { + refreshAccessTokenRouteHandler = refreshAccessToken(authenticationService, { sameSite: 'none' }); + const req: Request = { + cookies: { + refresh_token: 'validToken' + } + } as Request; + + await refreshAccessTokenRouteHandler(req, res); + + expect(res.cookie).toHaveBeenCalledWith('access_token', tokens.accessToken.token, { + ...defaultCookieOpts, + sameSite: 'none', + maxAge: tokens.accessToken.expiresIn + }); + expect(res.status).toHaveBeenCalledWith(200); + expect(res.json).toHaveBeenCalledWith({ idToken: tokens.idToken.token }); + }); }); describe('isUserLoggedIn tests', () => { @@ -760,8 +791,6 @@ describe('authenticationMiddleware integration tests', () => { }); it('should return 200, set the access token as a cookie, set the id token in the response body, and set loggedIn to true in the response body when the access_token cookie is missing and the refresh_token cookie is present and valid', async () => { - const accessExpires = new Date(tokens.accessToken.expiresIn * 1000); - const req: Request = { cookies: { refresh_token: 'validToken' @@ -773,8 +802,8 @@ describe('authenticationMiddleware integration tests', () => { expect(res.status).toHaveBeenCalledWith(200); expect(res.json).toHaveBeenCalledWith({ idToken: tokens.idToken.token, loggedIn: true }); expect(res.cookie).toHaveBeenCalledWith('access_token', tokens.accessToken.token, { - ...cookieOpts, - expires: accessExpires + ...defaultCookieOpts, + maxAge: tokens.accessToken.expiresIn }); }); @@ -798,7 +827,9 @@ describe('authenticationMiddleware integration tests', () => { expect(res.status).toHaveBeenCalledWith(200); expect(res.json).toHaveBeenCalledWith({ idToken: tokens.idToken.token, loggedIn: true }); - expect(res.cookie).toHaveBeenCalledWith('access_token', tokens.accessToken.token, { ...cookieOpts }); + expect(res.cookie).toHaveBeenCalledWith('access_token', tokens.accessToken.token, { + ...defaultCookieOpts + }); }); it('should return 200 and set loggedIn to false in the response body when the access_token and refresh_token cookies are missing', async () => { @@ -872,5 +903,24 @@ describe('authenticationMiddleware integration tests', () => { expect(res.json).toHaveBeenCalledWith({ loggedIn: false }); expect(loggingSpy).toHaveBeenCalledTimes(1); }); + + it('should return 200, set the access token as a cookie, set the id token in the response body, and set loggedIn to true in the response body when the sameSite option is set to "none"', async () => { + isUserLoggedInRouteHandler = isUserLoggedIn(authenticationService, { sameSite: 'none' }); + const req: Request = { + cookies: { + refresh_token: 'validToken' + } + } as Request; + + await isUserLoggedInRouteHandler(req, res); + + expect(res.status).toHaveBeenCalledWith(200); + expect(res.json).toHaveBeenCalledWith({ idToken: tokens.idToken.token, loggedIn: true }); + expect(res.cookie).toHaveBeenCalledWith('access_token', tokens.accessToken.token, { + ...defaultCookieOpts, + sameSite: 'none', + maxAge: tokens.accessToken.expiresIn + }); + }); }); }); diff --git a/workbench-core/authentication/src/authenticationMiddleware.ts b/workbench-core/authentication/src/authenticationMiddleware.ts index 4d3eb7ac4d..b2917b5e7b 100644 --- a/workbench-core/authentication/src/authenticationMiddleware.ts +++ b/workbench-core/authentication/src/authenticationMiddleware.ts @@ -5,12 +5,18 @@ import { AuthenticatedUser, RoutesIgnored } from '@aws/workbench-core-authorization'; import { LoggingService } from '@aws/workbench-core-logging'; -import { Request, Response, NextFunction } from 'express'; +import { Request, Response, NextFunction, CookieOptions } from 'express'; import get from 'lodash/get'; import has from 'lodash/has'; import { AuthenticationService } from './authenticationService'; import { isIdpUnavailableError } from './errors/idpUnavailableError'; +const defaultCookieOptions: CookieOptions = { + httpOnly: true, + secure: true, + sameSite: 'strict' +}; + /** * An Express route handler function used to exchange the authorization code received from the authentication server for authentication tokens. * This route places the access token and refresh token, if it exists, into http only, secure, same site strict cookies and returns the id token @@ -21,7 +27,7 @@ import { isIdpUnavailableError } from './errors/idpUnavailableError'; * - a request body parameter named `codeVerifier` that holds a pkce code verifier value * * @param authenticationService - a configured {@link AuthenticationService} instance - * @param options - an options object containing an optional logging service parameter + * @param options - object containing optional sameSite cookie and logging service parameters * @returns the route handler function * * @example @@ -31,10 +37,10 @@ import { isIdpUnavailableError } from './errors/idpUnavailableError'; */ export function getTokensFromAuthorizationCode( authenticationService: AuthenticationService, - options?: { loggingService?: LoggingService } + options?: { loggingService?: LoggingService; sameSite?: 'none' | 'lax' | 'strict' } ): (req: Request, res: Response) => Promise { return async function (req: Request, res: Response) { - const { loggingService } = options || {}; + const { loggingService, sameSite } = options || {}; const code = req.body.code; const codeVerifier = req.body.codeVerifier; @@ -45,30 +51,21 @@ export function getTokensFromAuthorizationCode( codeVerifier ); - const now = Date.now(); - - // set cookies. - // TODO: Delete code below adding access token to response and rely solely on cookies - const data = { - idToken: idToken.token, - accessToken: accessToken.token - }; + // set cookies res.cookie('access_token', accessToken.token, { - httpOnly: true, - secure: true, - sameSite: 'strict', - expires: accessToken.expiresIn ? new Date(now + accessToken.expiresIn * 1000) : undefined + ...defaultCookieOptions, + sameSite: sameSite ?? defaultCookieOptions.sameSite, + maxAge: accessToken.expiresIn }); if (refreshToken) { res.cookie('refresh_token', refreshToken.token, { - httpOnly: true, - secure: true, - sameSite: 'strict', - expires: refreshToken.expiresIn ? new Date(now + refreshToken.expiresIn * 1000) : undefined + ...defaultCookieOptions, + sameSite: sameSite ?? defaultCookieOptions.sameSite, + maxAge: refreshToken.expiresIn }); } - res.status(200).json(data); + res.status(200).json({ idToken: idToken.token }); } catch (error) { if (loggingService) { loggingService.error(error); @@ -176,7 +173,7 @@ export function verifyToken( * - if there is a refresh token, it is stored in a cookie named `refresh_token` * * @param authenticationService - a configured {@link AuthenticationService} instance - * @param options - an options object containing an optional logging service parameter + * @param options - object containing optional sameSite cookie and logging service parameters * @returns the route handler function * * @example @@ -186,10 +183,10 @@ export function verifyToken( */ export function logoutUser( authenticationService: AuthenticationService, - options?: { loggingService?: LoggingService } + options?: { loggingService?: LoggingService; sameSite?: 'none' | 'lax' | 'strict' } ): (req: Request, res: Response) => Promise { return async function (req: Request, res: Response) { - const { loggingService } = options || {}; + const { loggingService, sameSite } = options || {}; const refreshToken = req.cookies.refresh_token; if (typeof refreshToken === 'string') { @@ -206,8 +203,14 @@ export function logoutUser( } } - res.cookie('access_token', 'cleared', { sameSite: 'lax', expires: new Date(0) }); - res.cookie('refresh_token', 'cleared', { sameSite: 'lax', expires: new Date(0) }); + res.clearCookie('access_token', { + ...defaultCookieOptions, + sameSite: sameSite ?? defaultCookieOptions.sameSite + }); + res.clearCookie('refresh_token', { + ...defaultCookieOptions, + sameSite: sameSite ?? defaultCookieOptions.sameSite + }); res.status(200).json({ logoutUrl: authenticationService.getLogoutUrl() }); }; @@ -220,7 +223,7 @@ export function logoutUser( * - the refresh token is stored in a cookie named `refresh_token` * * @param authenticationService - a configured {@link AuthenticationService} instance - * @param options - an options object containing an optional logging service parameter + * @param options - object containing optional sameSite cookie and logging service parameters * @returns the route handler function * * @example @@ -230,10 +233,10 @@ export function logoutUser( */ export function refreshAccessToken( authenticationService: AuthenticationService, - options?: { loggingService?: LoggingService } + options?: { loggingService?: LoggingService; sameSite?: 'none' | 'lax' | 'strict' } ): (req: Request, res: Response) => Promise { return async function (req: Request, res: Response) { - const { loggingService } = options || {}; + const { loggingService, sameSite } = options || {}; const refreshToken = req.cookies.refresh_token; if (typeof refreshToken === 'string') { @@ -242,10 +245,9 @@ export function refreshAccessToken( // set access cookie res.cookie('access_token', accessToken.token, { - httpOnly: true, - secure: true, - sameSite: 'strict', - expires: accessToken.expiresIn ? new Date(Date.now() + accessToken.expiresIn * 1000) : undefined + ...defaultCookieOptions, + sameSite: sameSite ?? defaultCookieOptions.sameSite, + maxAge: accessToken.expiresIn }); res.status(200).json({ idToken: idToken.token }); @@ -277,7 +279,7 @@ export function refreshAccessToken( * - if there is a refresh token, it is stored in a cookie named `refresh_token` * * @param authenticationService - a configured {@link AuthenticationService} instance - * @param options - an options object containing an optional logging service parameter + * @param options - object containing optional sameSite cookie and logging service parameters * @returns the route handler function * * @example @@ -287,10 +289,10 @@ export function refreshAccessToken( */ export function isUserLoggedIn( authenticationService: AuthenticationService, - options?: { loggingService?: LoggingService } + options?: { loggingService?: LoggingService; sameSite?: 'none' | 'lax' | 'strict' } ): (req: Request, res: Response) => Promise { return async function (req: Request, res: Response) { - const { loggingService } = options || {}; + const { loggingService, sameSite } = options || {}; const accessToken = req.cookies.access_token; const refreshToken = req.cookies.refresh_token; @@ -300,10 +302,9 @@ export function isUserLoggedIn( // set access cookie res.cookie('access_token', accessToken.token, { - httpOnly: true, - secure: true, - sameSite: 'strict', - expires: accessToken.expiresIn ? new Date(Date.now() + accessToken.expiresIn * 1000) : undefined + ...defaultCookieOptions, + sameSite: sameSite ?? defaultCookieOptions.sameSite, + maxAge: accessToken.expiresIn }); res.status(200).json({ idToken: idToken.token, loggedIn: true }); diff --git a/workbench-core/authentication/src/index.ts b/workbench-core/authentication/src/index.ts index ac5de16e6b..5e370f19be 100644 --- a/workbench-core/authentication/src/index.ts +++ b/workbench-core/authentication/src/index.ts @@ -32,7 +32,7 @@ export { UserAlreadyExistsError, isUserAlreadyExistsError } from './errors/userA export { UserNotFoundError, isUserNotFoundError } from './errors/userNotFoundError'; export { DecodedJWT } from './decodedJWT'; export { Tokens } from './tokens'; -export { getTimeInSeconds } from './utils'; +export { getTimeInMS, TimeUnits } from './utils'; export { getAuthorizationCodeUrl, getTokensFromAuthorizationCode, diff --git a/workbench-core/authentication/src/plugins/cognitoAuthenticationPlugin.test.ts b/workbench-core/authentication/src/plugins/cognitoAuthenticationPlugin.test.ts index 20ebce1419..ad8d92b9b5 100644 --- a/workbench-core/authentication/src/plugins/cognitoAuthenticationPlugin.test.ts +++ b/workbench-core/authentication/src/plugins/cognitoAuthenticationPlugin.test.ts @@ -7,13 +7,15 @@ jest.mock('../utils'); import { CognitoIdentityProviderClient, + DescribeUserPoolClientCommand, DescribeUserPoolClientCommandOutput, NotAuthorizedException, ResourceNotFoundException, TimeUnitsType } from '@aws-sdk/client-cognito-identity-provider'; import { CognitoJwtVerifier } from 'aws-jwt-verify'; -import { CognitoJwtPayload } from 'aws-jwt-verify/jwt-model'; +import { CognitoAccessTokenPayload } from 'aws-jwt-verify/jwt-model'; +import { mockClient } from 'aws-sdk-client-mock'; import axios from 'axios'; import { CognitoAuthenticationPlugin, @@ -41,6 +43,23 @@ const encodedClientId = Buffer.from( `${cognitoPluginOptions.clientId}:${cognitoPluginOptions.clientSecret}` ).toString('base64'); +const baseDecodedAccessToken: CognitoAccessTokenPayload = { + token_use: 'access', + client_id: 'client_id', + version: 1, + username: 'username', + scope: 'scope', + sub: 'sub', + iss: 'iss', + exp: 3600, + iat: 123, + auth_time: 456, + jti: 'jti', + origin_jti: 'origin_jti' +}; + +const cognitoMock = mockClient(CognitoIdentityProviderClient); + const userPoolClientInfo: Partial = { UserPoolClient: { TokenValidityUnits: { @@ -58,6 +77,7 @@ describe('CognitoAuthenticationPlugin tests', () => { let plugin: CognitoAuthenticationPlugin; beforeEach(() => { + cognitoMock.reset(); plugin = new CognitoAuthenticationPlugin(cognitoPluginOptions); }); @@ -121,21 +141,11 @@ describe('CognitoAuthenticationPlugin tests', () => { describe('validateToken tests', () => { it('should return the decoded token when a valid token is passed in', async () => { - const decodedToken: CognitoJwtPayload = { - token_use: 'access', - sub: 'sub', - iss: 'iss', - exp: 3600, - iat: 123, - auth_time: 456, - jti: 'jti', - origin_jti: 'origin_jti' - }; - jest.spyOn(CognitoJwtVerifier.prototype, 'verify').mockResolvedValueOnce(decodedToken); + jest.spyOn(CognitoJwtVerifier.prototype, 'verify').mockResolvedValueOnce(baseDecodedAccessToken); const decoded = await plugin.validateToken('validToken'); - expect(decoded).toMatchObject(decodedToken); + expect(decoded).toMatchObject(baseDecodedAccessToken); }); it('should throw InvalidJWTError when an invalid token is passed in', async () => { @@ -274,54 +284,20 @@ describe('CognitoAuthenticationPlugin tests', () => { describe('getUserIdFromToken tests', () => { it('should return the sub claim from the decoded token', () => { - const decodedToken: CognitoJwtPayload = { - token_use: 'access', - sub: 'sub', - iss: 'iss', - exp: 3600, - iat: 123, - auth_time: 456, - jti: 'jti', - origin_jti: 'origin_jti' - }; - - const userId = plugin.getUserIdFromToken(decodedToken); + const userId = plugin.getUserIdFromToken(baseDecodedAccessToken); expect(userId).toBe('sub'); }); it('should return the cognito:groups claim from the decoded token', () => { - const decodedToken: CognitoJwtPayload = { - token_use: 'access', - sub: 'sub', - iss: 'iss', - exp: 3600, - iat: 123, - auth_time: 456, - jti: 'jti', - origin_jti: 'origin_jti', - 'cognito:groups': ['Admin'] - }; - - const userId = plugin.getUserRolesFromToken(decodedToken); + const userId = plugin.getUserRolesFromToken({ ...baseDecodedAccessToken, 'cognito:groups': ['Admin'] }); expect(userId).toMatchObject(['Admin']); }); it('should throw InvalidJWTError when the decoded token doesnt have the cognito:groups claim', () => { - const decodedToken: CognitoJwtPayload = { - token_use: 'access', - sub: 'sub', - iss: 'iss', - exp: 3600, - iat: 123, - auth_time: 456, - jti: 'jti', - origin_jti: 'origin_jti' - }; - expect(() => { - plugin.getUserRolesFromToken(decodedToken); + plugin.getUserRolesFromToken(baseDecodedAccessToken); }).toThrow(new InvalidJWTError('no cognito:roles claim')); }); }); @@ -338,9 +314,7 @@ describe('CognitoAuthenticationPlugin tests', () => { } }; const axiosSpy = jest.spyOn(axios, 'post').mockResolvedValueOnce(fakeTokens); - jest - .spyOn(CognitoIdentityProviderClient.prototype, 'send') - .mockImplementationOnce(() => Promise.resolve(userPoolClientInfo)); + cognitoMock.on(DescribeUserPoolClientCommand).resolves(userPoolClientInfo); const tokens = await plugin.handleAuthorizationCode(validCode, codeVerifier); @@ -566,9 +540,7 @@ describe('CognitoAuthenticationPlugin tests', () => { } }; const axiosSpy = jest.spyOn(axios, 'post').mockResolvedValueOnce(fakeTokens); - jest - .spyOn(CognitoIdentityProviderClient.prototype, 'send') - .mockImplementationOnce(() => Promise.resolve(userPoolClientInfo)); + cognitoMock.on(DescribeUserPoolClientCommand).resolves(userPoolClientInfo); const tokens = await plugin.refreshAccessToken(refreshToken); @@ -720,33 +692,33 @@ describe('CognitoAuthenticationPlugin tests', () => { expect(encodedId).toBe(encodedClientId); }); + }); + describe('_getTokensExpiration tests', () => { it('should return a TokensExpiration object when user pool has token expiration defined', async () => { - jest - .spyOn(CognitoIdentityProviderClient.prototype, 'send') - .mockImplementationOnce(() => Promise.resolve(userPoolClientInfo)); + cognitoMock.on(DescribeUserPoolClientCommand).resolves(userPoolClientInfo); - const tokens = await plugin['_getTokensExpiration'](); + const tokens = await plugin['_getTokensExpirationinMS'](); expect(tokens).toMatchObject({ idToken: 1, accessToken: 1, refreshToken: 1 }); }); - it('should return an empty TokensExpiration object when user pool doesnt have token expiration defined', async () => { - jest - .spyOn(CognitoIdentityProviderClient.prototype, 'send') - .mockImplementationOnce(() => Promise.resolve({})); + it('should return a TokensExpiration object when user pool token expiration is undefined', async () => { + cognitoMock.on(DescribeUserPoolClientCommand).resolves({ + UserPoolClient: { + TokenValidityUnits: {} + } + }); - const tokens = await plugin['_getTokensExpiration'](); + const tokens = await plugin['_getTokensExpirationinMS'](); - expect(tokens).toMatchObject({}); + expect(tokens).toMatchObject({ idToken: 1, accessToken: 1, refreshToken: 1 }); }); it('should throw PluginConfigurationError when the service doesnt have correct permissions', async () => { - jest - .spyOn(CognitoIdentityProviderClient.prototype, 'send') - .mockImplementationOnce(() => Promise.reject(new NotAuthorizedException({ $metadata: {} }))); + cognitoMock.on(DescribeUserPoolClientCommand).rejects(new NotAuthorizedException({ $metadata: {} })); - await expect(plugin['_getTokensExpiration']()).rejects.toThrow( + await expect(plugin['_getTokensExpirationinMS']()).rejects.toThrow( new PluginConfigurationError( 'service is not authorized to perform this action. Check IAM permissions' ) @@ -754,21 +726,17 @@ describe('CognitoAuthenticationPlugin tests', () => { }); it('should throw PluginConfigurationError when the service doesnt have correct permissions', async () => { - jest - .spyOn(CognitoIdentityProviderClient.prototype, 'send') - .mockImplementationOnce(() => Promise.reject(new ResourceNotFoundException({ $metadata: {} }))); + cognitoMock.on(DescribeUserPoolClientCommand).rejects(new ResourceNotFoundException({ $metadata: {} })); - await expect(plugin['_getTokensExpiration']()).rejects.toThrow( + await expect(plugin['_getTokensExpirationinMS']()).rejects.toThrow( new PluginConfigurationError('invalid user pool id or client id') ); }); it('should rethrow an error when the error is unexpected', async () => { - jest - .spyOn(CognitoIdentityProviderClient.prototype, 'send') - .mockImplementationOnce(() => Promise.reject(new Error())); + cognitoMock.on(DescribeUserPoolClientCommand).rejects(new Error()); - await expect(plugin['_getTokensExpiration']()).rejects.toThrow(Error); + await expect(plugin['_getTokensExpirationinMS']()).rejects.toThrow(Error); }); }); }); diff --git a/workbench-core/authentication/src/plugins/cognitoAuthenticationPlugin.ts b/workbench-core/authentication/src/plugins/cognitoAuthenticationPlugin.ts index 15f776a486..c8d39dda12 100644 --- a/workbench-core/authentication/src/plugins/cognitoAuthenticationPlugin.ts +++ b/workbench-core/authentication/src/plugins/cognitoAuthenticationPlugin.ts @@ -6,12 +6,11 @@ import { CognitoIdentityProviderClient, DescribeUserPoolClientCommand, - DescribeUserPoolClientCommandInput, - TimeUnitsType + DescribeUserPoolClientCommandInput } from '@aws-sdk/client-cognito-identity-provider'; import { CognitoJwtVerifier } from 'aws-jwt-verify'; import { CognitoJwtVerifierSingleUserPool } from 'aws-jwt-verify/cognito-verifier'; -import { CognitoJwtPayload } from 'aws-jwt-verify/jwt-model'; +import { CognitoAccessTokenPayload } from 'aws-jwt-verify/jwt-model'; import axios, { AxiosError } from 'axios'; import { AuthenticationPlugin } from '../authenticationPlugin'; import { IdpUnavailableError } from '../errors/idpUnavailableError'; @@ -22,12 +21,12 @@ import { InvalidTokenError } from '../errors/invalidTokenError'; import { InvalidTokenTypeError } from '../errors/invalidTokenTypeError'; import { PluginConfigurationError } from '../errors/pluginConfigurationError'; import { Tokens } from '../tokens'; -import { getTimeInSeconds } from '../utils'; +import { getTimeInMS, TimeUnits } from '../utils'; interface TokensExpiration { - idToken?: number; // in seconds - accessToken?: number; // in seconds - refreshToken?: number; // in seconds + idToken: number; // ms + accessToken: number; // ms + refreshToken: number; // ms } export interface CognitoAuthenticationPluginOptions { @@ -74,7 +73,7 @@ export class CognitoAuthenticationPlugin implements AuthenticationPlugin { private _baseUrl: string; private _verifier: CognitoJwtVerifierSingleUserPool<{ userPoolId: string; - tokenUse: null; + tokenUse: 'access'; clientId: string; }>; @@ -107,7 +106,7 @@ export class CognitoAuthenticationPlugin implements AuthenticationPlugin { try { this._verifier = CognitoJwtVerifier.create({ userPoolId, - tokenUse: null, // can check both access and ID tokens + tokenUse: 'access', clientId }); } catch (error) { @@ -141,14 +140,14 @@ export class CognitoAuthenticationPlugin implements AuthenticationPlugin { } /** - * Validates an id or access token and returns the values on the token. + * Validates an access token and returns the values on the token. * - * @param token - an Id or Access token to be validated + * @param token - an access token to be validated * @returns the decoded jwt * * @throws {@link InvalidJWTError} if the token is invalid */ - public async validateToken(token: string): Promise { + public async validateToken(token: string): Promise { try { return await this._verifier.verify(token); } catch (error) { @@ -205,7 +204,7 @@ export class CognitoAuthenticationPlugin implements AuthenticationPlugin { * @param decodedToken - a decoded Id or access token from which to extract the user Id. * @returns the user Id found within the token. */ - public getUserIdFromToken(decodedToken: CognitoJwtPayload): string { + public getUserIdFromToken(decodedToken: CognitoAccessTokenPayload): string { return decodedToken.sub; } @@ -217,7 +216,7 @@ export class CognitoAuthenticationPlugin implements AuthenticationPlugin { * * @throws {@link InvalidJWTError} if the token doesnt contain the user's roles */ - public getUserRolesFromToken(decodedToken: CognitoJwtPayload): string[] { + public getUserRolesFromToken(decodedToken: CognitoAccessTokenPayload): string[] { const roles = decodedToken['cognito:groups']; if (!roles) { // jwt does not have a cognito:roles claim @@ -259,7 +258,7 @@ export class CognitoAuthenticationPlugin implements AuthenticationPlugin { } ); - const expiresIn = await this._getTokensExpiration(); + const expiresIn = await this._getTokensExpirationinMS(); return { idToken: { @@ -338,7 +337,7 @@ export class CognitoAuthenticationPlugin implements AuthenticationPlugin { } ); - const expiresIn = await this._getTokensExpiration(); + const expiresIn = await this._getTokensExpirationinMS(); return { idToken: { @@ -390,7 +389,7 @@ export class CognitoAuthenticationPlugin implements AuthenticationPlugin { * * @returns a {@link TokensExpiration} object */ - private async _getTokensExpiration(): Promise { + private async _getTokensExpirationinMS(): Promise { const client = new CognitoIdentityProviderClient({ region: this._region }); const describeInput: DescribeUserPoolClientCommandInput = { @@ -400,35 +399,20 @@ export class CognitoAuthenticationPlugin implements AuthenticationPlugin { const describeCommand = new DescribeUserPoolClientCommand(describeInput); try { - const clientInfo = await client.send(describeCommand); - - const { - IdToken: idTokenUnits, - AccessToken: accessTokenUnits, - RefreshToken: refreshTokenUnits - } = clientInfo.UserPoolClient?.TokenValidityUnits || {}; - - const refreshTokenTime = clientInfo.UserPoolClient?.RefreshTokenValidity; - const idTokenTime = clientInfo.UserPoolClient?.IdTokenValidity; - const accessTokenTime = clientInfo.UserPoolClient?.AccessTokenValidity; - - const idTokenExpiresIn = - idTokenTime && idTokenUnits - ? getTimeInSeconds(idTokenTime, idTokenUnits as TimeUnitsType) - : undefined; - const accessTokenExpiresIn = - accessTokenTime && accessTokenUnits - ? getTimeInSeconds(accessTokenTime, accessTokenUnits as TimeUnitsType) - : undefined; - const refreshTokenExpiresIn = - refreshTokenTime && refreshTokenUnits - ? getTimeInSeconds(refreshTokenTime, refreshTokenUnits as TimeUnitsType) - : undefined; + const { UserPoolClient } = await client.send(describeCommand); + + const idTokenTime = UserPoolClient!.IdTokenValidity ?? 60; + const accessTokenTime = UserPoolClient!.AccessTokenValidity ?? 60; + const refreshTokenTime = UserPoolClient!.RefreshTokenValidity ?? 30; + + const idTokenUnits = UserPoolClient!.TokenValidityUnits!.IdToken ?? TimeUnits.MINUTES; + const accessTokenUnits = UserPoolClient!.TokenValidityUnits!.AccessToken ?? TimeUnits.MINUTES; + const refreshTokenUnits = UserPoolClient!.TokenValidityUnits!.RefreshToken ?? TimeUnits.DAYS; return { - idToken: idTokenExpiresIn, - accessToken: accessTokenExpiresIn, - refreshToken: refreshTokenExpiresIn + idToken: getTimeInMS(idTokenTime, idTokenUnits as TimeUnits), + accessToken: getTimeInMS(accessTokenTime, accessTokenUnits as TimeUnits), + refreshToken: getTimeInMS(refreshTokenTime, refreshTokenUnits as TimeUnits) }; } catch (error) { if (error.name === 'NotAuthorizedException') { diff --git a/workbench-core/authentication/src/tokens.ts b/workbench-core/authentication/src/tokens.ts index b93ef0e102..c31d3cd907 100644 --- a/workbench-core/authentication/src/tokens.ts +++ b/workbench-core/authentication/src/tokens.ts @@ -6,14 +6,14 @@ export interface Tokens { idToken: { token: string; - expiresIn?: number; // seconds + expiresIn?: number; // ms }; accessToken: { token: string; - expiresIn?: number; // seconds + expiresIn?: number; // ms }; refreshToken?: { token: string; - expiresIn?: number; // seconds + expiresIn?: number; // ms }; } diff --git a/workbench-core/authentication/src/utils.test.ts b/workbench-core/authentication/src/utils.test.ts index f43b708409..a913830ef7 100644 --- a/workbench-core/authentication/src/utils.test.ts +++ b/workbench-core/authentication/src/utils.test.ts @@ -3,55 +3,54 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { TimeUnitsType } from '@aws-sdk/client-cognito-identity-provider'; -import { getTimeInSeconds } from '.'; +import { getTimeInMS, TimeUnits } from '.'; describe('util tests', () => { - describe('getTimeInSeconds tests', () => { - it('should return length converted to seconds if units is TimeUnitsType.DAYS', () => { - const time = getTimeInSeconds(1, TimeUnitsType.DAYS); + describe('getTimeInMS tests', () => { + it('should return length converted to ms if units is TimeUnits.DAYS', () => { + const time = getTimeInMS(1, TimeUnits.DAYS); - expect(time).toBe(86400); + expect(time).toBe(86400000); }); - it('should return 0 when length is 0 and units is TimeUnitsType.DAYS', () => { - const time = getTimeInSeconds(0, TimeUnitsType.DAYS); + it('should return 0 when length is 0 and units is TimeUnits.DAYS', () => { + const time = getTimeInMS(0, TimeUnits.DAYS); expect(time).toBe(0); }); - it('should return length converted to seconds if units is TimeUnitsType.HOURS', () => { - const time = getTimeInSeconds(1, TimeUnitsType.HOURS); + it('should return length converted to ms if units is TimeUnits.HOURS', () => { + const time = getTimeInMS(1, TimeUnits.HOURS); - expect(time).toBe(3600); + expect(time).toBe(3600000); }); - it('should return 0 when length is 0 and units is TimeUnitsType.HOURS', () => { - const time = getTimeInSeconds(0, TimeUnitsType.HOURS); + it('should return 0 when length is 0 and units is TimeUnits.HOURS', () => { + const time = getTimeInMS(0, TimeUnits.HOURS); expect(time).toBe(0); }); - it('should return length converted to seconds if units is TimeUnitsType.MINUTES', () => { - const time = getTimeInSeconds(1, TimeUnitsType.MINUTES); + it('should return length converted to ms if units is TimeUnits.MINUTES', () => { + const time = getTimeInMS(1, TimeUnits.MINUTES); - expect(time).toBe(60); + expect(time).toBe(60000); }); - it('should return 0 when length is 0 and units is TimeUnitsType.MINUTES', () => { - const time = getTimeInSeconds(0, TimeUnitsType.MINUTES); + it('should return 0 when length is 0 and units is TimeUnits.MINUTES', () => { + const time = getTimeInMS(0, TimeUnits.MINUTES); expect(time).toBe(0); }); - it('should return length converted to seconds if units is TimeUnitsType.SECONDS', () => { - const time = getTimeInSeconds(1, TimeUnitsType.SECONDS); + it('should return length converted to ms if units is TimeUnits.SECONDS', () => { + const time = getTimeInMS(1, TimeUnits.SECONDS); - expect(time).toBe(1); + expect(time).toBe(1000); }); - it('should return 0 when length is 0 and units is TimeUnitsType.SECONDS', () => { - const time = getTimeInSeconds(0, TimeUnitsType.SECONDS); + it('should return 0 when length is 0 and units is TimeUnits.SECONDS', () => { + const time = getTimeInMS(0, TimeUnits.SECONDS); expect(time).toBe(0); }); diff --git a/workbench-core/authentication/src/utils.ts b/workbench-core/authentication/src/utils.ts index 05c6f2f231..12f41ec2aa 100644 --- a/workbench-core/authentication/src/utils.ts +++ b/workbench-core/authentication/src/utils.ts @@ -3,17 +3,29 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { TimeUnitsType } from '@aws-sdk/client-cognito-identity-provider'; +export enum TimeUnits { + DAYS = 'days', + HOURS = 'hours', + MINUTES = 'minutes', + SECONDS = 'seconds' +} -export function getTimeInSeconds(length: number, units: TimeUnitsType): number { - if (units === TimeUnitsType.DAYS) { - return length * 86400; +/** + * Converts a amount of time into milliseconds + * + * @param length - the length of time + * @param units - the unit of time + * @returns the time in milliseconds + */ +export function getTimeInMS(length: number, units: TimeUnits): number { + if (units === TimeUnits.DAYS) { + return length * 24 * 60 * 60 * 1000; } - if (units === TimeUnitsType.HOURS) { - return length * 3600; + if (units === TimeUnits.HOURS) { + return length * 60 * 60 * 1000; } - if (units === TimeUnitsType.MINUTES) { - return length * 60; + if (units === TimeUnits.MINUTES) { + return length * 60 * 1000; } - return length; + return length * 1000; } diff --git a/workbench-core/environments/src/services/environmentService.ts b/workbench-core/environments/src/services/environmentService.ts index 4d61dc041b..e4f463d392 100644 --- a/workbench-core/environments/src/services/environmentService.ts +++ b/workbench-core/environments/src/services/environmentService.ts @@ -9,7 +9,7 @@ import { BatchGetItemCommandOutput, GetItemCommandOutput } from '@aws-sdk/client import { AuthenticatedUser } from '@aws/workbench-core-authorization'; import { AwsService, QueryParams } from '@aws/workbench-core-base'; import Boom from '@hapi/boom'; -import _ = require('lodash'); +import _ from 'lodash'; import { v4 as uuidv4 } from 'uuid'; import envResourceTypeToKey from '../constants/environmentResourceTypeToKey'; import { EnvironmentStatus } from '../constants/environmentStatus'; diff --git a/workbench-core/infrastructure/src/workbenchCognito.test.ts b/workbench-core/infrastructure/src/workbenchCognito.test.ts index 4e342ef986..d1cab5a6c4 100644 --- a/workbench-core/infrastructure/src/workbenchCognito.test.ts +++ b/workbench-core/infrastructure/src/workbenchCognito.test.ts @@ -97,7 +97,21 @@ describe('WorkbenchCognito tests', () => { EnableTokenRevocation: true, GenerateSecret: true, LogoutURLs: [workbenchCognitoProps.websiteUrl], - PreventUserExistenceErrors: 'ENABLED' + PreventUserExistenceErrors: 'ENABLED', + IdTokenValidity: 15, + AccessTokenValidity: 15, + RefreshTokenValidity: 43200, + TokenValidityUnits: { + IdToken: 'minutes', + AccessToken: 'minutes', + RefreshToken: 'minutes' + }, + ExplicitAuthFlows: [ + 'ALLOW_ADMIN_USER_PASSWORD_AUTH', + 'ALLOW_CUSTOM_AUTH', + 'ALLOW_USER_SRP_AUTH', + 'ALLOW_REFRESH_TOKEN_AUTH' + ] }); }); diff --git a/workbench-core/infrastructure/src/workbenchCognito.ts b/workbench-core/infrastructure/src/workbenchCognito.ts index bf0c0ac889..1e18841c9f 100644 --- a/workbench-core/infrastructure/src/workbenchCognito.ts +++ b/workbench-core/infrastructure/src/workbenchCognito.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { SecretValue, Stack } from 'aws-cdk-lib'; +import { Duration, SecretValue, Stack } from 'aws-cdk-lib'; import { AccountRecovery, Mfa, @@ -58,7 +58,10 @@ const userPoolClientDefaults: UserPoolClientOptions = { custom: true }, preventUserExistenceErrors: true, - enableTokenRevocation: true + enableTokenRevocation: true, + idTokenValidity: Duration.minutes(15), + accessTokenValidity: Duration.minutes(15), + refreshTokenValidity: Duration.days(30) }; export interface WorkbenchCognitoProps { From 2f6e3f26760906326ad0a3836dd12327ccfe1aa9 Mon Sep 17 00:00:00 2001 From: bhudnell Date: Mon, 15 Aug 2022 18:21:49 +0000 Subject: [PATCH 010/621] docs: Auto README.md updates [skip ci] --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fda88a9efa..9afa9ba6fb 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ # Code Coverage | Statements | Branches | Functions | Lines | | --------------------------- | ----------------------- | ------------------------- | ----------------- | -| ![Statements](https://img.shields.io/badge/statements-87.14%25-yellow.svg?style=flat) | ![Branches](https://img.shields.io/badge/branches-84.85%25-yellow.svg?style=flat) | ![Functions](https://img.shields.io/badge/functions-88.5%25-yellow.svg?style=flat) | ![Lines](https://img.shields.io/badge/lines-87.64%25-yellow.svg?style=flat) | +| ![Statements](https://img.shields.io/badge/statements-87.15%25-yellow.svg?style=flat) | ![Branches](https://img.shields.io/badge/branches-85.01%25-yellow.svg?style=flat) | ![Functions](https://img.shields.io/badge/functions-88.52%25-yellow.svg?style=flat) | ![Lines](https://img.shields.io/badge/lines-87.64%25-yellow.svg?style=flat) | # Contributing Guidelines From 89c7f346895d081a6fc8a7fbe0c3ef0e6e9c5d97 Mon Sep 17 00:00:00 2001 From: Sanket Dharwadkar Date: Tue, 16 Aug 2022 12:52:04 -0700 Subject: [PATCH 011/621] test: add list env integ tests (#461) --- .../tests/isolated/environments/list.test.ts | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 solutions/swb-reference/integration-tests/tests/isolated/environments/list.test.ts diff --git a/solutions/swb-reference/integration-tests/tests/isolated/environments/list.test.ts b/solutions/swb-reference/integration-tests/tests/isolated/environments/list.test.ts new file mode 100644 index 0000000000..ec19799166 --- /dev/null +++ b/solutions/swb-reference/integration-tests/tests/isolated/environments/list.test.ts @@ -0,0 +1,66 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +import ClientSession from '../../../support/clientSession'; +import Setup from '../../../support/setup'; +import HttpError from '../../../support/utils/HttpError'; +import { checkHttpError } from '../../../support/utils/utilities'; + +describe('list environments', () => { + const setup: Setup = new Setup(); + let adminSession: ClientSession; + + beforeEach(() => { + expect.hasAssertions(); + }); + + beforeAll(async () => { + adminSession = await setup.getDefaultAdminSession(); + }); + + afterAll(async () => { + await setup.cleanup(); + }); + test('list environments when status query is invalid', async () => { + try { + const queryParams = { + status: 'someInvalidStatus' + } + await adminSession.resources.environments.get(queryParams); + } catch (e) { + checkHttpError( + e, + new HttpError(400, { + statusCode: 400, + error: 'Bad Request', + message: 'Invalid environment status. Please try again with valid inputs.' + }) + ); + } + }); + + const validEnvStatuses = [ + 'PENDING', + 'COMPLETED', + 'STARTING', + 'STOPPING', + 'STOPPED', + 'TERMINATING', + 'TERMINATED', + 'FAILED', + 'TERMINATING_FAILED', + 'STARTING_FAILED', + 'STOPPING_FAILED' + ]; + + test.each(validEnvStatuses)('list environments when status query is %s', async (status) => { + const queryParams = { + status + } + + const { data: response } = await adminSession.resources.environments.get(queryParams); + + expect(Array.isArray(response.data)).toBe(true); + }); +}); From 59128f56b772d094c6d7f610511067f8e2a69a22 Mon Sep 17 00:00:00 2001 From: Brendon Hudnell Date: Tue, 16 Aug 2022 14:08:39 -0600 Subject: [PATCH 012/621] fix: authN verify token fix (#463) * fix: fixed missing replacing auth header for cookie for token verification * docs: added rush change file --- .../fix-authn-verify-token-fix_2022-08-16-19-54.json | 10 ++++++++++ .../authentication/src/authenticationMiddleware.ts | 4 +++- 2 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 common/changes/@aws/workbench-core-authentication/fix-authn-verify-token-fix_2022-08-16-19-54.json diff --git a/common/changes/@aws/workbench-core-authentication/fix-authn-verify-token-fix_2022-08-16-19-54.json b/common/changes/@aws/workbench-core-authentication/fix-authn-verify-token-fix_2022-08-16-19-54.json new file mode 100644 index 0000000000..0f372c1982 --- /dev/null +++ b/common/changes/@aws/workbench-core-authentication/fix-authn-verify-token-fix_2022-08-16-19-54.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@aws/workbench-core-authentication", + "comment": "Fixed bug in authenticationMiddleware where verifyToken used Authorization header instead of access_token cookie", + "type": "patch" + } + ], + "packageName": "@aws/workbench-core-authentication" +} \ No newline at end of file diff --git a/workbench-core/authentication/src/authenticationMiddleware.ts b/workbench-core/authentication/src/authenticationMiddleware.ts index b2917b5e7b..c2d5f394e8 100644 --- a/workbench-core/authentication/src/authenticationMiddleware.ts +++ b/workbench-core/authentication/src/authenticationMiddleware.ts @@ -138,10 +138,12 @@ export function verifyToken( ): (req: Request, res: Response, next: NextFunction) => Promise { return async function (req: Request, res: Response, next: NextFunction) { const { ignoredRoutes, loggingService } = options || {}; + if (has(ignoredRoutes, req.path) && get(get(ignoredRoutes, req.path), req.method)) { next(); } else { - const accessToken = req.headers ? req.headers.authorization : undefined; + const accessToken = req.cookies.access_token; + if (typeof accessToken === 'string') { try { const decodedAccessToken = await authenticationService.validateToken(accessToken); From 8da4f7984a8d470ab987b5f11e83cb811b23cf75 Mon Sep 17 00:00:00 2001 From: bhudnell Date: Tue, 16 Aug 2022 20:19:56 +0000 Subject: [PATCH 013/621] docs: Auto README.md updates [skip ci] --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9afa9ba6fb..5b6aa728ec 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ # Code Coverage | Statements | Branches | Functions | Lines | | --------------------------- | ----------------------- | ------------------------- | ----------------- | -| ![Statements](https://img.shields.io/badge/statements-87.15%25-yellow.svg?style=flat) | ![Branches](https://img.shields.io/badge/branches-85.01%25-yellow.svg?style=flat) | ![Functions](https://img.shields.io/badge/functions-88.52%25-yellow.svg?style=flat) | ![Lines](https://img.shields.io/badge/lines-87.64%25-yellow.svg?style=flat) | +| ![Statements](https://img.shields.io/badge/statements-87.15%25-yellow.svg?style=flat) | ![Branches](https://img.shields.io/badge/branches-84.99%25-yellow.svg?style=flat) | ![Functions](https://img.shields.io/badge/functions-88.52%25-yellow.svg?style=flat) | ![Lines](https://img.shields.io/badge/lines-87.64%25-yellow.svg?style=flat) | # Contributing Guidelines From 7d44ac0e5033342578c2ff90ac95b07635644a76 Mon Sep 17 00:00:00 2001 From: HaiTao Zhang Date: Tue, 16 Aug 2022 14:18:44 -0700 Subject: [PATCH 014/621] fix: add ignore license checker when package.json changes are not detected (#464) * fix: add ignore license checker when package.json changes are not detected * chore: change echo message * fix: change name of job --- .github/workflows/ignore-license-checker.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 .github/workflows/ignore-license-checker.yml diff --git a/.github/workflows/ignore-license-checker.yml b/.github/workflows/ignore-license-checker.yml new file mode 100644 index 0000000000..d6321c804e --- /dev/null +++ b/.github/workflows/ignore-license-checker.yml @@ -0,0 +1,11 @@ +name: license checker +on: + pull_request: + types: [opened, synchronize, edited, reopened] + paths-ignore: + - '**/package.json' +jobs: + license-checker: + runs-on: ubuntu-20.04 + steps: + - run: 'echo "License checker not required" ' From 0fdfa5c168a561c4dd0f96a0b55537fc65df7a8e Mon Sep 17 00:00:00 2001 From: Brendon Hudnell Date: Wed, 17 Aug 2022 09:51:00 -0600 Subject: [PATCH 015/621] feat: changed authentication service to take a website param for redirection after login/logout (#449) * feat: changed authentication service to take a webasite param for redirection after login/logout * refactor: replaced require with import * fix: removed auth header code, fixed tests broken by merge * docs: updated documentation to reflect changes * docs: updated more readmes * fix: removed unnecessary consts * docs: removed websiteUrl param from config yaml files * docs: added rush change files * Update common/changes/@aws/workbench-core-infrastructure/feat-authn-dynamic-website-url_2022-08-16-20-18.json Co-authored-by: Robert Smayda Co-authored-by: Robert Smayda Co-authored-by: HaiTao Zhang --- ...-dynamic-website-url_2022-08-16-20-18.json | 10 ++ ...-dynamic-website-url_2022-08-16-20-18.json | 10 ++ ...-dynamic-website-url_2022-08-16-20-18.json | 10 ++ solutions/swb-app/src/generateRouter.ts | 6 +- solutions/swb-reference/SETUP_v2p1.md | 1 - .../integration-tests/config/example.yaml | 2 +- .../support/complex/datasetHelper.ts | 97 ++++++------ .../support/resources/datasets/dataset.ts | 2 +- .../support/resources/datasets/datasets.ts | 10 +- .../resources/environments/environment.ts | 7 +- .../support/utils/settings.ts | 2 +- .../tests/isolated/datasets/create.test.ts | 12 +- .../tests/multiStep/dataset.test.ts | 30 ++-- solutions/swb-reference/src/SWBStack.ts | 10 +- solutions/swb-reference/src/config/demo.yaml | 1 - .../swb-reference/src/config/example.yaml | 1 - .../swb-reference/src/config/testEnv.yaml | 1 - solutions/swb-reference/src/constants.ts | 6 +- .../docs/authenticationMiddleware.md | 21 ++- .../src/__mocks__/authenticationService.ts | 14 +- .../src/authenticationMiddleware.test.ts | 139 ++++++++++++++---- .../src/authenticationMiddleware.ts | 32 +++- .../src/authenticationPlugin.ts | 9 +- .../src/authenticationService.test.ts | 21 +-- .../src/authenticationService.ts | 19 ++- .../__mocks__/cognitoAuthenticationPlugin.ts | 14 +- .../cognitoAuthenticationPlugin.test.ts | 109 ++++++-------- .../plugins/cognitoAuthenticationPlugin.ts | 30 ++-- workbench-core/infrastructure/README.md | 2 +- .../src/workbenchCognito.test.ts | 12 +- .../infrastructure/src/workbenchCognito.ts | 8 +- 31 files changed, 398 insertions(+), 250 deletions(-) create mode 100644 common/changes/@aws/swb-app/feat-authn-dynamic-website-url_2022-08-16-20-18.json create mode 100644 common/changes/@aws/workbench-core-authentication/feat-authn-dynamic-website-url_2022-08-16-20-18.json create mode 100644 common/changes/@aws/workbench-core-infrastructure/feat-authn-dynamic-website-url_2022-08-16-20-18.json diff --git a/common/changes/@aws/swb-app/feat-authn-dynamic-website-url_2022-08-16-20-18.json b/common/changes/@aws/swb-app/feat-authn-dynamic-website-url_2022-08-16-20-18.json new file mode 100644 index 0000000000..3bc3bcd64f --- /dev/null +++ b/common/changes/@aws/swb-app/feat-authn-dynamic-website-url_2022-08-16-20-18.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@aws/swb-app", + "comment": "Removed `websiteUrl` parameter from `cognitoPluginOptions` object", + "type": "minor" + } + ], + "packageName": "@aws/swb-app" +} \ No newline at end of file diff --git a/common/changes/@aws/workbench-core-authentication/feat-authn-dynamic-website-url_2022-08-16-20-18.json b/common/changes/@aws/workbench-core-authentication/feat-authn-dynamic-website-url_2022-08-16-20-18.json new file mode 100644 index 0000000000..ce08053c73 --- /dev/null +++ b/common/changes/@aws/workbench-core-authentication/feat-authn-dynamic-website-url_2022-08-16-20-18.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@aws/workbench-core-authentication", + "comment": "Added `websiteUrl` param to AuthenticationService to allow for dynamic redirection after login/logout", + "type": "minor" + } + ], + "packageName": "@aws/workbench-core-authentication" +} \ No newline at end of file diff --git a/common/changes/@aws/workbench-core-infrastructure/feat-authn-dynamic-website-url_2022-08-16-20-18.json b/common/changes/@aws/workbench-core-infrastructure/feat-authn-dynamic-website-url_2022-08-16-20-18.json new file mode 100644 index 0000000000..b4e53ab4cf --- /dev/null +++ b/common/changes/@aws/workbench-core-infrastructure/feat-authn-dynamic-website-url_2022-08-16-20-18.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@aws/workbench-core-infrastructure", + "comment": "Renamed `websiteUrl` to `websiteUrls` and changed type to string array to allow one backend API to serve multiple UI clients", + "type": "minor" + } + ], + "packageName": "@aws/workbench-core-infrastructure" +} \ No newline at end of file diff --git a/solutions/swb-app/src/generateRouter.ts b/solutions/swb-app/src/generateRouter.ts index 1ff75521a5..32ac258873 100644 --- a/solutions/swb-app/src/generateRouter.ts +++ b/solutions/swb-app/src/generateRouter.ts @@ -23,8 +23,7 @@ import { import { LoggingService } from '@aws/workbench-core-logging'; import cookieParser from 'cookie-parser'; import cors from 'cors'; -import express = require('express'); -import { Router, Express, Request, Response } from 'express'; +import express, { Router, Express, Request, Response } from 'express'; import { setUpAccountRoutes } from './accountRoutes'; import { ApiRoute, ApiRouteConfig } from './apiRouteConfig'; import { setUpAuthRoutes } from './authRoutes'; @@ -57,8 +56,7 @@ export function generateRouter(apiRouteConfig: ApiRouteConfig): Express { cognitoDomain: process.env.COGNITO_DOMAIN!, userPoolId: process.env.USER_POOL_ID!, clientId: process.env.CLIENT_ID!, - clientSecret: process.env.CLIENT_SECRET!, - websiteUrl: process.env.WEBSITE_URL! + clientSecret: process.env.CLIENT_SECRET! }; const authenticationService = new AuthenticationService( diff --git a/solutions/swb-reference/SETUP_v2p1.md b/solutions/swb-reference/SETUP_v2p1.md index 11d97f358d..10baa71f3f 100644 --- a/solutions/swb-reference/SETUP_v2p1.md +++ b/solutions/swb-reference/SETUP_v2p1.md @@ -28,7 +28,6 @@ These manual steps will not be required in the final implementation of SWBv2. 1. Open your new `.yaml` file and uncomment `awsRegion` and `awsRegionShortName`. `aws-region` value can be one of the values on this [table](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Concepts.RegionsAndAvailabilityZones.html#Concepts.RegionsAndAvailabilityZones.Regions), under the `Region` column. `awsRegionName` can be a two or three letter abbreviation for that region, of your own choosing. The `awsRegion` value will determine which region SWBv2 is deployed in. 1. Uncomment `rootUserEmail` and provide the main account user's email address 1. Uncomment `cognitoDomain` and provide a unique string that will be used for the cognito domain. This should be an alphanumeric string (hyphens allowed) that does not conflict with any other existing cognito domains. -1. Uncomment `websiteUrl` and set it to the website url that will be used for the SWB UI. 1. If running your Lambda locally, `userPoolId`, `clientId`, and `clientSecret` will need to be set after the first execution of `cdk-deploy` as seen below under "Deploy the code". You will then need to re-run `STAGE= rushx cdk-deploy`. 1. Run `chmod 777 .yaml` to allow local script to read the file diff --git a/solutions/swb-reference/integration-tests/config/example.yaml b/solutions/swb-reference/integration-tests/config/example.yaml index f4cacd4762..54de44104a 100644 --- a/solutions/swb-reference/integration-tests/config/example.yaml +++ b/solutions/swb-reference/integration-tests/config/example.yaml @@ -1,4 +1,4 @@ -## Note: +## Note: ## Please deploy the swb-reference package before running the integration test suite. ## Settings declared here are merged with the main accounts CFN outputs to create integration test config items diff --git a/solutions/swb-reference/integration-tests/support/complex/datasetHelper.ts b/solutions/swb-reference/integration-tests/support/complex/datasetHelper.ts index d2583f9a12..8add37e534 100644 --- a/solutions/swb-reference/integration-tests/support/complex/datasetHelper.ts +++ b/solutions/swb-reference/integration-tests/support/complex/datasetHelper.ts @@ -1,52 +1,61 @@ import { AccessPoint } from '@aws-sdk/client-s3-control'; -import { AwsService } from "@aws/workbench-core-base"; -import _ from "lodash"; -import ClientSession from "../clientSession"; +import { AwsService } from '@aws/workbench-core-base'; +import _ from 'lodash'; +import ClientSession from '../clientSession'; export class DatasetHelper { - private _awsSdk: AwsService; - private _adminSession: ClientSession; - public constructor(awsSdkClient: AwsService, adminSession: ClientSession) { - this._awsSdk = awsSdkClient; - this._adminSession = adminSession; - } + private _awsSdk: AwsService; + private _adminSession: ClientSession; + public constructor(awsSdkClient: AwsService, adminSession: ClientSession) { + this._awsSdk = awsSdkClient; + this._adminSession = adminSession; + } - public async listAccessPoints(bucket: string, accountId: string): Promise> { - const response = await this._awsSdk.clients.s3Control.listAccessPoints({AccountId: accountId, Bucket: bucket})!; - return response.AccessPointList!; - } + public async listAccessPoints(bucket: string, accountId: string): Promise> { + const response = await this._awsSdk.clients.s3Control.listAccessPoints({ + AccountId: accountId, + Bucket: bucket + })!; + return response.AccessPointList!; + } - public async deleteS3Resources(bucket: string, dir: string): Promise { - const listedObjects = await this._awsSdk.clients.s3.listObjectsV2({Bucket: bucket,Prefix: dir})!; - if (listedObjects.Contents!.length === 0) return; + public async deleteS3Resources(bucket: string, dir: string): Promise { + const listedObjects = await this._awsSdk.clients.s3.listObjectsV2({ Bucket: bucket, Prefix: dir })!; + if (listedObjects.Contents!.length === 0) return; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const deleteParams: {Bucket: string, Delete: {Objects: any}} = {Bucket: bucket,Delete: { Objects: [] }}; - listedObjects.Contents!.forEach((key) => { - deleteParams.Delete.Objects.push({ Key: key.Key }); - }); - - await this._awsSdk.clients.s3.deleteObjects(deleteParams); - if (listedObjects.IsTruncated) await this.deleteS3Resources(bucket, dir); - } + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const deleteParams: { Bucket: string; Delete: { Objects: any } } = { + Bucket: bucket, + Delete: { Objects: [] } + }; + listedObjects.Contents!.forEach((key) => { + deleteParams.Delete.Objects.push({ Key: key.Key }); + }); - public async deleteDdbRecords(dataSetId: string): Promise { - await this._awsSdk.helpers.ddb.delete({pk: `DATASET#${dataSetId}`, sk: `DATASET#${dataSetId}`}).execute(); - const data = await this._awsSdk.helpers.ddb - .query({ - key: { - name: 'pk', - value: `DATASET#${dataSetId}` - } - }).execute(); - if (data.Count === 0) return; + await this._awsSdk.clients.s3.deleteObjects(deleteParams); + if (listedObjects.IsTruncated) await this.deleteS3Resources(bucket, dir); + } - const endpoints = data.Items!; - // Tests are not expected to create more than a couple of endpoints per DS max, so no support needed for pagintated query results - await Promise.all( - _.map(endpoints, async endpoint => { - await this._awsSdk.helpers.ddb.delete({pk: endpoint.pk, sk: endpoint.sk}).execute(); - }) - ); - } -} \ No newline at end of file + public async deleteDdbRecords(dataSetId: string): Promise { + await this._awsSdk.helpers.ddb + .delete({ pk: `DATASET#${dataSetId}`, sk: `DATASET#${dataSetId}` }) + .execute(); + const data = await this._awsSdk.helpers.ddb + .query({ + key: { + name: 'pk', + value: `DATASET#${dataSetId}` + } + }) + .execute(); + if (data.Count === 0) return; + + const endpoints = data.Items!; + // Tests are not expected to create more than a couple of endpoints per DS max, so no support needed for pagintated query results + await Promise.all( + _.map(endpoints, async (endpoint) => { + await this._awsSdk.helpers.ddb.delete({ pk: endpoint.pk, sk: endpoint.sk }).execute(); + }) + ); + } +} diff --git a/solutions/swb-reference/integration-tests/support/resources/datasets/dataset.ts b/solutions/swb-reference/integration-tests/support/resources/datasets/dataset.ts index 60b11ed947..b7a349d0ed 100644 --- a/solutions/swb-reference/integration-tests/support/resources/datasets/dataset.ts +++ b/solutions/swb-reference/integration-tests/support/resources/datasets/dataset.ts @@ -13,7 +13,7 @@ export default class Dataset extends Resource { super(clientSession, 'dataset', id, parentApi); } - public async share(requestBody: {[id: string]: string}): Promise { + public async share(requestBody: { [id: string]: string }): Promise { return this._axiosInstance.post(`${this._api}/share`, requestBody); } diff --git a/solutions/swb-reference/integration-tests/support/resources/datasets/datasets.ts b/solutions/swb-reference/integration-tests/support/resources/datasets/datasets.ts index 1338af569f..3a0ea6e047 100644 --- a/solutions/swb-reference/integration-tests/support/resources/datasets/datasets.ts +++ b/solutions/swb-reference/integration-tests/support/resources/datasets/datasets.ts @@ -18,7 +18,7 @@ export default class Datasets extends CollectionResource { return new Dataset(id, this._clientSession, this._api); } - public async import(requestBody: {[id: string]: string}): Promise { + public async import(requestBody: { [id: string]: string }): Promise { return this._axiosInstance.post(`${this._api}/import`, requestBody); } @@ -35,8 +35,8 @@ export default class Datasets extends CollectionResource { } interface DataSetCreateRequest { - datasetName: string, - storageName: string, - path: string, - awsAccountId: string + datasetName: string; + storageName: string; + path: string; + awsAccountId: string; } diff --git a/solutions/swb-reference/integration-tests/support/resources/environments/environment.ts b/solutions/swb-reference/integration-tests/support/resources/environments/environment.ts index 67c47e4f81..7472af02e3 100644 --- a/solutions/swb-reference/integration-tests/support/resources/environments/environment.ts +++ b/solutions/swb-reference/integration-tests/support/resources/environments/environment.ts @@ -37,8 +37,8 @@ export default class Environment extends Resource { // Exit early because environment has already been terminated return; } - try{ - console.log(`Attempting to delete environment ${this._id}. This will take a few minutes.`) + try { + console.log(`Attempting to delete environment ${this._id}. This will take a few minutes.`); while (envStatus === 'PENDING' && totalTimeWaitedInSeconds < maxWaitTimeInSeconds) { await sleep(15000); const { data: resource } = await defAdminSession.resources.environments.environment(this._id).get(); @@ -47,12 +47,11 @@ export default class Environment extends Resource { } await defAdminSession.resources.environments.environment(this._id).terminate(); console.log(`Deleted environment ${this._id}`); - } catch (e){ + } catch (e) { console.log( `Could not delete environment. Last known status for env ${this._id} was "${envStatus}". Waited ${totalTimeWaitedInSeconds} seconds for environment to reach valid state so it could be deleted; encountered error: ${e}` ); } - } } diff --git a/solutions/swb-reference/integration-tests/support/utils/settings.ts b/solutions/swb-reference/integration-tests/support/utils/settings.ts index 9025393ffd..26e1a13106 100644 --- a/solutions/swb-reference/integration-tests/support/utils/settings.ts +++ b/solutions/swb-reference/integration-tests/support/utils/settings.ts @@ -15,7 +15,7 @@ interface Setting { terminatedEnvId: string; rootUsername: string; rootPasswordParamStorePath: string; - + // Main CFN template outputs cognitoUserPoolClientId: string; MainAccountEncryptionKeyOutput: string; diff --git a/solutions/swb-reference/integration-tests/tests/isolated/datasets/create.test.ts b/solutions/swb-reference/integration-tests/tests/isolated/datasets/create.test.ts index 0eb6c8f89e..ac63b72c60 100644 --- a/solutions/swb-reference/integration-tests/tests/isolated/datasets/create.test.ts +++ b/solutions/swb-reference/integration-tests/tests/isolated/datasets/create.test.ts @@ -29,13 +29,13 @@ describe('datasets create negative tests', () => { datasetName: randomTextGenerator.getFakeText('fakeName'), storageName: randomTextGenerator.getFakeText('fakeBucket'), path: randomTextGenerator.getFakeText('fakePath'), - awsAccountId: randomTextGenerator.getFakeText('fakeAccount'), + awsAccountId: randomTextGenerator.getFakeText('fakeAccount') }; describe('missing parameters', () => { test('datasetName', async () => { try { - const invalidParam: {[id: string]: string} = { ...validLaunchParameters }; + const invalidParam: { [id: string]: string } = { ...validLaunchParameters }; delete invalidParam.datasetName; await adminSession.resources.datasets.create(invalidParam, false); } catch (e) { @@ -49,10 +49,10 @@ describe('datasets create negative tests', () => { ); } }); - + test('path', async () => { try { - const invalidParam: {[id: string]: string} = { ...validLaunchParameters }; + const invalidParam: { [id: string]: string } = { ...validLaunchParameters }; delete invalidParam.path; await adminSession.resources.datasets.create(invalidParam, false); } catch (e) { @@ -69,7 +69,7 @@ describe('datasets create negative tests', () => { test('storageName', async () => { try { - const invalidParam: {[id: string]: string} = { ...validLaunchParameters }; + const invalidParam: { [id: string]: string } = { ...validLaunchParameters }; delete invalidParam.storageName; await adminSession.resources.datasets.create(invalidParam, false); } catch (e) { @@ -86,7 +86,7 @@ describe('datasets create negative tests', () => { test('awsAccountId', async () => { try { - const invalidParam: {[id: string]: string} = { ...validLaunchParameters }; + const invalidParam: { [id: string]: string } = { ...validLaunchParameters }; delete invalidParam.awsAccountId; await adminSession.resources.datasets.create(invalidParam, false); } catch (e) { diff --git a/solutions/swb-reference/integration-tests/tests/multiStep/dataset.test.ts b/solutions/swb-reference/integration-tests/tests/multiStep/dataset.test.ts index e4f42774b2..2b2fb0e962 100644 --- a/solutions/swb-reference/integration-tests/tests/multiStep/dataset.test.ts +++ b/solutions/swb-reference/integration-tests/tests/multiStep/dataset.test.ts @@ -58,21 +58,27 @@ describe('multiStep dataset integration test', () => { const accessPointName = `${dataSet.id.slice(0, 13)}-mounted-on-${env.id.slice(0, 13)}`; expect(envDetails).toMatchObject({ datasetIds: [dataSet.id], - ENDPOINTS: expect.arrayContaining([expect.objectContaining({ - endPointUrl: `s3://arn:aws:s3:${awsRegion}:${mainAccountId}:accesspoint/${accessPointName}`, - storageArn: `arn:aws:s3:::${settings.get('DataSetsBucketName')}`, - dataSetId: dataSet.id, - path: datasetName - })]), - DATASETS: expect.arrayContaining([expect.objectContaining({ - id: dataSet.id, - name: datasetName - })])} - ); + ENDPOINTS: expect.arrayContaining([ + expect.objectContaining({ + endPointUrl: `s3://arn:aws:s3:${awsRegion}:${mainAccountId}:accesspoint/${accessPointName}`, + storageArn: `arn:aws:s3:::${settings.get('DataSetsBucketName')}`, + dataSetId: dataSet.id, + path: datasetName + }) + ]), + DATASETS: expect.arrayContaining([ + expect.objectContaining({ + id: dataSet.id, + name: datasetName + }) + ]) + }); // Verify dataset has env access point listed in its external endpoints const { data: dataSetDetails } = await adminSession.resources.datasets.dataset(dataSet.id).get(); // Dataset was created just for this test case, so we expect only one endpoint - expect(dataSetDetails.externalEndpoints).toMatchObject([envDetails.ENDPOINTS[0].sk.split('ENDPOINT#')[1]]); + expect(dataSetDetails.externalEndpoints).toMatchObject([ + envDetails.ENDPOINTS[0].sk.split('ENDPOINT#')[1] + ]); }); }); diff --git a/solutions/swb-reference/src/SWBStack.ts b/solutions/swb-reference/src/SWBStack.ts index 664fd8e695..125abfa12a 100644 --- a/solutions/swb-reference/src/SWBStack.ts +++ b/solutions/swb-reference/src/SWBStack.ts @@ -54,7 +54,6 @@ export class SWBStack extends Stack { CLIENT_ID: string; CLIENT_SECRET: string; USER_POOL_ID: string; - WEBSITE_URL: string; MAIN_ACCT_ENCRYPTION_KEY_ARN_OUTPUT_KEY: string; }; @@ -81,7 +80,7 @@ export class SWBStack extends Stack { COGNITO_DOMAIN, USER_POOL_CLIENT_NAME, USER_POOL_NAME, - WEBSITE_URL, + WEBSITE_URLS, USER_POOL_ID, CLIENT_ID, CLIENT_SECRET, @@ -96,7 +95,7 @@ export class SWBStack extends Stack { const workbenchCognito = this._createCognitoResources( COGNITO_DOMAIN, - WEBSITE_URL, + WEBSITE_URLS, USER_POOL_NAME, USER_POOL_CLIENT_NAME ); @@ -134,7 +133,6 @@ export class SWBStack extends Stack { CLIENT_ID: clientId, CLIENT_SECRET: clientSecret, USER_POOL_ID: userPoolId, - WEBSITE_URL, MAIN_ACCT_ENCRYPTION_KEY_ARN_OUTPUT_KEY }; @@ -838,13 +836,13 @@ export class SWBStack extends Stack { private _createCognitoResources( domainPrefix: string, - websiteUrl: string, + websiteUrls: string[], userPoolName: string, userPoolClientName: string ): WorkbenchCognito { const props: WorkbenchCognitoProps = { domainPrefix: domainPrefix, - websiteUrl: websiteUrl, + websiteUrls: websiteUrls, userPoolName: userPoolName, userPoolClientName: userPoolClientName, oidcIdentityProviders: [] diff --git a/solutions/swb-reference/src/config/demo.yaml b/solutions/swb-reference/src/config/demo.yaml index c4cc5d5ae6..3cb297eb76 100644 --- a/solutions/swb-reference/src/config/demo.yaml +++ b/solutions/swb-reference/src/config/demo.yaml @@ -5,4 +5,3 @@ awsRegionShortName: oh rootUserEmail: aws-chamonix-dev+demoRoot@amazon.com allowedOrigins: ['http://localhost:3000'] cognitoDomain: 'chamonix-dev-demo-domain' -websiteUrl: 'http://localhost:3000' diff --git a/solutions/swb-reference/src/config/example.yaml b/solutions/swb-reference/src/config/example.yaml index af3fb2821b..95799fd254 100644 --- a/solutions/swb-reference/src/config/example.yaml +++ b/solutions/swb-reference/src/config/example.yaml @@ -4,7 +4,6 @@ #rootUserEmail: user@example.com # SWB will automatically create this Cognito user for you. #allowedOrigins: ["http://localhost:3000"] # SWB API will allow this domains to make requests. #cognitoDomain: 'user-test' # Must be globally unique -#websiteUrl: 'http://localhost:3000' # Used for local Lambda development; grab them from Cognito console after cdk-deploy completes. # Once values are entered, re-run cdk-deploy. diff --git a/solutions/swb-reference/src/config/testEnv.yaml b/solutions/swb-reference/src/config/testEnv.yaml index 1f49ab56c7..065aa17028 100644 --- a/solutions/swb-reference/src/config/testEnv.yaml +++ b/solutions/swb-reference/src/config/testEnv.yaml @@ -5,4 +5,3 @@ awsRegionShortName: va rootUserEmail: aws-chamonix-dev+testEnvRoot@amazon.com allowedOrigins: ['http://localhost:3000', 'http://localhost:3002'] cognitoDomain: 'chamonix-dev-test-domain' -websiteUrl: 'http://localhost:3000' diff --git a/solutions/swb-reference/src/constants.ts b/solutions/swb-reference/src/constants.ts index ec3f504a5b..8fec5c91e4 100644 --- a/solutions/swb-reference/src/constants.ts +++ b/solutions/swb-reference/src/constants.ts @@ -29,7 +29,7 @@ function getConstants(): { AWS_REGION_SHORT_NAME: string; UI_CLIENT_URL: string; COGNITO_DOMAIN: string; - WEBSITE_URL: string; + WEBSITE_URLS: string[]; USER_POOL_ID: string; CLIENT_ID: string; CLIENT_SECRET: string; @@ -57,7 +57,7 @@ function getConstants(): { const USER_POOL_CLIENT_NAME = `swb-client-${config.stage}-${config.awsRegionShortName}`; const USER_POOL_NAME = `swb-userpool-${config.stage}-${config.awsRegionShortName}`; const COGNITO_DOMAIN = config.cognitoDomain; - const WEBSITE_URL = uiClientURL || config.websiteUrl; + const WEBSITE_URLS = allowedOrigins; const USER_POOL_ID = config.userPoolId; const CLIENT_ID = config.clientId; const CLIENT_SECRET = config.clientSecret; @@ -95,7 +95,7 @@ function getConstants(): { UI_CLIENT_URL: uiClientURL, STATUS_HANDLER_ARN_OUTPUT_KEY, COGNITO_DOMAIN, - WEBSITE_URL, + WEBSITE_URLS, USER_POOL_ID, CLIENT_ID, CLIENT_SECRET, diff --git a/workbench-core/authentication/docs/authenticationMiddleware.md b/workbench-core/authentication/docs/authenticationMiddleware.md index b115c5a370..89b073097b 100644 --- a/workbench-core/authentication/docs/authenticationMiddleware.md +++ b/workbench-core/authentication/docs/authenticationMiddleware.md @@ -42,7 +42,7 @@ const ignoredRoutes: RoutesIgnored = { POST: true }, '/logout': { - GET: true + POST: true }, '/refresh': { GET: true @@ -65,7 +65,7 @@ app.use(verifyToken(authenticationService, { ignoredRoutes, loggingService })); // These routes are public (as defined in the ignoredRoutes object above) app.get('/login', getAuthorizationCodeUrl(authenticationService)); app.post('/token', getTokensFromAuthorizationCode(authenticationService, { loggingService })); -app.get('/logout', logoutUser(authenticationService, { loggingService })); +app.post('/logout', logoutUser(authenticationService, { loggingService })); app.get('/refresh', refreshAccessToken(authenticationService, { loggingService })); app.get('/loggedIn', isUserLoggedIn(authenticationService, { loggingService })); @@ -91,6 +91,7 @@ The `stateVerifier` and `codeChallenge` request query parameters are temporary v #### Assumptions - a url request query parameter named `stateVerifier` that holds a temporary state value - a url request query parameter named `codeChallenge` that holds a temporary pkce code challenge value +- the request origin header exists #### Parameters - authenticationService: a configured AuthenticationService instance @@ -107,16 +108,18 @@ This route places the access token and refresh token, if it exists, into http on #### Assumptions - a url request body parameter named `code` that holds the authorization code - a url request body parameter named `codeVerifier` that holds a pkce code verifier value +- the request origin header exists #### Parameters - authenticationService: a configured AuthenticationService instance - options: - loggingService: an optional LoggingService instance. If included errors from the AuthenticationService will be logged here + - sameSite: an optional sameSite cookie paramater for the access and refresh tokens. Options are: `'strict'`, `'lax'`, and `'none'`. Defaults to `'strict'` #### Example ```ts const loggingService = new LoggingService(); -app.get('tokens', getTokensFromAuthorizationCode(authenticationService, { loggingService })); +app.get('tokens', getTokensFromAuthorizationCode(authenticationService, { loggingService, sameSite: 'strict' })); ``` ### verifyToken @@ -143,7 +146,7 @@ const ignoredRoutes: RoutesIgnored = { POST: true }, '/logout': { - GET: true + POST: true }, '/refresh': { GET: true @@ -167,11 +170,12 @@ This route handler used to refresh an expired access code. - authenticationService: a configured AuthenticationService instance - options: - loggingService: an optional LoggingService instance. If included errors from the AuthenticationService will be logged here + - sameSite: an optional sameSite cookie paramater for the access and refresh tokens. Options are: `'strict'`, `'lax'`, and `'none'`. Defaults to `'strict'` #### Example ```ts const loggingService = new LoggingService(); -app.get('refresh', refreshAccessToken(authenticationService, { loggingService })); +app.get('refresh', refreshAccessToken(authenticationService, { loggingService, sameSite: 'strict' })); ``` ### logoutUser @@ -180,16 +184,18 @@ This route handler is used to logout a user. #### Assumptions - the access token is stored in a cookie named `access_token` - if there is a refresh token, it is stored in a cookie named `refresh_token` +- the request origin header exists #### Parameters - authenticationService: a configured AuthenticationService instance - options: - loggingService: an optional LoggingService instance. If included errors from the AuthenticationService will be logged here + - sameSite: an optional sameSite cookie paramater for the access and refresh tokens. Options are: `'strict'`, `'lax'`, and `'none'`. Defaults to `'strict'` #### Example ```ts const loggingService = new LoggingService(); -app.get('logout', logoutUser(authenticationService, { loggingService })); +app.get('logout', logoutUser(authenticationService, { loggingService, sameSite: 'strict' })); ``` ### isUserLoggedIn @@ -203,9 +209,10 @@ This route handler is used to check if the user making the request is logged in. - authenticationService: a configured AuthenticationService instance - options: - loggingService: an optional LoggingService instance. If included errors from the AuthenticationService will be logged here + - sameSite: an optional sameSite cookie paramater for the access and refresh tokens. Options are: `'strict'`, `'lax'`, and `'none'`. Defaults to `'strict'` #### Example ```ts const loggingService = new LoggingService(); -app.get('loggedIn', isUserLoggedIn(authenticationService, { loggingService })); +app.get('loggedIn', isUserLoggedIn(authenticationService, { loggingService, sameSite: 'strict' })); ``` diff --git a/workbench-core/authentication/src/__mocks__/authenticationService.ts b/workbench-core/authentication/src/__mocks__/authenticationService.ts index 6e3cd994d2..6ac7faa650 100644 --- a/workbench-core/authentication/src/__mocks__/authenticationService.ts +++ b/workbench-core/authentication/src/__mocks__/authenticationService.ts @@ -56,15 +56,19 @@ export class AuthenticationService { return ['role']; } - public async handleAuthorizationCode(code: string, codeVerifier: string): Promise { + public async handleAuthorizationCode( + code: string, + codeVerifier: string, + websiteUrl: string + ): Promise { if (code === 'validCode' && codeVerifier === 'validCodeVerifier') { return tokens; } throw new Error(); } - public getAuthorizationCodeUrl(state: string, codeChallenge: string): string { - return `https://www.fakeurl.com/authorize?client_id=fake-id&response_type=code&scope=openid&redirect_uri=https://www.fakewebsite.com&state=${state}&code_challenge_method=S256&code_challenge=${codeChallenge}`; + public getAuthorizationCodeUrl(state: string, codeChallenge: string, websiteUrl: string): string { + return `https://www.fakeurl.com/authorize?client_id=fake-id&response_type=code&scope=openid&redirect_uri=${websiteUrl}&state=${state}&code_challenge_method=S256&code_challenge=${codeChallenge}`; } public async refreshAccessToken(refreshToken: string): Promise { @@ -74,7 +78,7 @@ export class AuthenticationService { throw new Error(); } - public getLogoutUrl(state: string, codeChallenge: string): string { - return 'https://www.fakeurl.com/logout?client_id=fake-id&logout_uri=https://www.fakewebsite.com'; + public getLogoutUrl(websiteUrl: string): string { + return `https://www.fakeurl.com/logout?client_id=fake-id&logout_uri=${websiteUrl}`; } } diff --git a/workbench-core/authentication/src/authenticationMiddleware.test.ts b/workbench-core/authentication/src/authenticationMiddleware.test.ts index 259c013cb5..9709f98be0 100644 --- a/workbench-core/authentication/src/authenticationMiddleware.test.ts +++ b/workbench-core/authentication/src/authenticationMiddleware.test.ts @@ -26,8 +26,7 @@ const cognitoPluginOptions: CognitoAuthenticationPluginOptions = { cognitoDomain: 'fake-domain', userPoolId: 'us-west-2_fakeId', clientId: 'fake-client-id', - clientSecret: 'fake-client-secret', - websiteUrl: 'fake-website-url' + clientSecret: 'fake-client-secret' } as const; const defaultCookieOpts = { @@ -71,6 +70,9 @@ describe('authenticationMiddleware integration tests', () => { body: { code: 'validCode', codeVerifier: 'validCodeVerifier' + }, + headers: { + origin: 'https://www.fakewebsite.com' } } as Request; @@ -93,6 +95,9 @@ describe('authenticationMiddleware integration tests', () => { body: { code: 'validCode', codeVerifier: 'validCodeVerifier' + }, + headers: { + origin: 'https://www.fakewebsite.com' } } as Request; @@ -124,6 +129,9 @@ describe('authenticationMiddleware integration tests', () => { const req: Request = { body: { codeVerifier: 'validCodeVerifier' + }, + headers: { + origin: 'https://www.fakewebsite.com' } } as Request; @@ -137,6 +145,9 @@ describe('authenticationMiddleware integration tests', () => { body: { code: 123, codeVerifier: 'validCodeVerifier' + }, + headers: { + origin: 'https://www.fakewebsite.com' } } as Request; @@ -149,6 +160,9 @@ describe('authenticationMiddleware integration tests', () => { const req: Request = { body: { code: 'validCode' + }, + headers: { + origin: 'https://www.fakewebsite.com' } } as Request; @@ -162,6 +176,9 @@ describe('authenticationMiddleware integration tests', () => { body: { code: 'validCode', codeVerifier: 123 + }, + headers: { + origin: 'https://www.fakewebsite.com' } } as Request; @@ -170,11 +187,28 @@ describe('authenticationMiddleware integration tests', () => { expect(res.sendStatus).toHaveBeenCalledWith(400); }); + it('should return 400 when origin header is missing', async () => { + const req: Request = { + body: { + code: 'validCode', + codeVerifier: 'validCodeVerifier' + }, + headers: {} + } as Request; + + await getTokensFromAuthorizationCodeRouteHandler(req, res); + + expect(res.sendStatus).toHaveBeenCalledWith(400); + }); + it('should return 401 when code param is invalid', async () => { const req: Request = { body: { code: 'invalidCode', codeVerifier: 'validCodeVerifier' + }, + headers: { + origin: 'https://www.fakewebsite.com' } } as Request; @@ -188,6 +222,9 @@ describe('authenticationMiddleware integration tests', () => { body: { code: 'validCode', codeVerifier: 'invalidCodeVerifier' + }, + headers: { + origin: 'https://www.fakewebsite.com' } } as Request; @@ -201,6 +238,9 @@ describe('authenticationMiddleware integration tests', () => { body: { code: 'validCode', codeVerifier: 'validCodeVerifier' + }, + headers: { + origin: 'https://www.fakewebsite.com' } } as Request; jest @@ -220,6 +260,9 @@ describe('authenticationMiddleware integration tests', () => { body: { code: 'validCode', codeVerifier: 'invalidCodeVerifier' + }, + headers: { + origin: 'https://www.fakewebsite.com' } } as Request; const loggingSpy = jest.spyOn(loggingService, 'error').mockImplementationOnce(() => {}); @@ -238,6 +281,9 @@ describe('authenticationMiddleware integration tests', () => { body: { code: 'validCode', codeVerifier: 'validCodeVerifier' + }, + headers: { + origin: 'https://www.fakewebsite.com' } } as Request; @@ -273,6 +319,9 @@ describe('authenticationMiddleware integration tests', () => { query: { stateVerifier, codeChallenge + }, + headers: { + origin: 'https://www.fakewebsite.com' } } as unknown as Request; @@ -288,6 +337,9 @@ describe('authenticationMiddleware integration tests', () => { const req: Request = { query: { codeChallenge: 'validCodeChallenge' + }, + headers: { + origin: 'https://www.fakewebsite.com' } } as unknown as Request; @@ -301,6 +353,9 @@ describe('authenticationMiddleware integration tests', () => { query: { stateVerifier: 123, codeChallenge: 'validCodeChallenge' + }, + headers: { + origin: 'https://www.fakewebsite.com' } } as unknown as Request; @@ -313,6 +368,9 @@ describe('authenticationMiddleware integration tests', () => { const req: Request = { query: { stateVerifier: 'validState' + }, + headers: { + origin: 'https://www.fakewebsite.com' } } as unknown as Request; @@ -326,6 +384,9 @@ describe('authenticationMiddleware integration tests', () => { query: { stateVerifier: 'validState', codeChallenge: 123 + }, + headers: { + origin: 'https://www.fakewebsite.com' } } as unknown as Request; @@ -333,6 +394,20 @@ describe('authenticationMiddleware integration tests', () => { expect(res.sendStatus).toHaveBeenCalledWith(400); }); + + it('should return 400 when origin header is missing', async () => { + const req: Request = { + query: { + stateVerifier: 'validState', + codeChallenge: 'validCodeChallenge' + }, + headers: {} + } as unknown as Request; + + await getAuthorizationCodeUrlRouteHandler(req, res); + + expect(res.sendStatus).toHaveBeenCalledWith(400); + }); }); describe('verifyToken tests', () => { @@ -346,9 +421,6 @@ describe('authenticationMiddleware integration tests', () => { const req: Request = { cookies: { access_token: 'validToken' - }, - headers: { - authorization: 'validToken' } } as Request; @@ -373,9 +445,6 @@ describe('authenticationMiddleware integration tests', () => { method: 'GET', cookies: { access_token: 'validToken' - }, - headers: { - authorization: 'validToken' } } as Request; @@ -401,9 +470,6 @@ describe('authenticationMiddleware integration tests', () => { method: 'POST', cookies: { access_token: 'validToken' - }, - headers: { - authorization: 'validToken' } } as Request; @@ -448,9 +514,6 @@ describe('authenticationMiddleware integration tests', () => { const req: Request = { cookies: { access_token: 'invalidToken' - }, - headers: { - authorization: 'invalidToken' } } as Request; @@ -467,9 +530,6 @@ describe('authenticationMiddleware integration tests', () => { const req: Request = { cookies: { access_token: 'invalidToken' - }, - headers: { - authorization: 'invalidToken' } } as Request; @@ -496,6 +556,9 @@ describe('authenticationMiddleware integration tests', () => { const req: Request = { cookies: { refresh_token: 'validToken' + }, + headers: { + origin: 'https://www.fakewebsite.com' } } as Request; @@ -511,7 +574,10 @@ describe('authenticationMiddleware integration tests', () => { it('should return 200 and clear cookies when refresh_token cookie is missing', async () => { const req: Request = { - cookies: {} + cookies: {}, + headers: { + origin: 'https://www.fakewebsite.com' + } } as Request; await logoutUserRouteHandler(req, res); @@ -528,6 +594,9 @@ describe('authenticationMiddleware integration tests', () => { const req: Request = { cookies: { refresh_token: 123 + }, + headers: { + origin: 'https://www.fakewebsite.com' } } as Request; @@ -545,6 +614,9 @@ describe('authenticationMiddleware integration tests', () => { const req: Request = { cookies: { refresh_token: 'invalidToken' + }, + headers: { + origin: 'https://www.fakewebsite.com' } } as Request; @@ -558,10 +630,26 @@ describe('authenticationMiddleware integration tests', () => { }); }); + it('should return 400 when origin header is missing', async () => { + const req: Request = { + cookies: { + refresh_token: 'validToken' + }, + headers: {} + } as Request; + + await logoutUserRouteHandler(req, res); + + expect(res.sendStatus).toHaveBeenCalledWith(400); + }); + it('should return 503 when authN service IDP is unavailable', async () => { const req: Request = { cookies: { refresh_token: 'validToken' + }, + headers: { + origin: 'https://www.fakewebsite.com' } } as Request; jest.spyOn(authenticationService, 'revokeToken').mockRejectedValueOnce(new IdpUnavailableError()); @@ -576,6 +664,9 @@ describe('authenticationMiddleware integration tests', () => { const req: Request = { cookies: { refresh_token: 'invalidToken' + }, + headers: { + origin: 'https://www.fakewebsite.com' } } as Request; @@ -597,6 +688,9 @@ describe('authenticationMiddleware integration tests', () => { const req: Request = { cookies: { refresh_token: 'validToken' + }, + headers: { + origin: 'https://www.fakewebsite.com' } } as Request; @@ -762,9 +856,6 @@ describe('authenticationMiddleware integration tests', () => { const req: Request = { cookies: { access_token: 'validToken' - }, - headers: { - authorization: 'validToken' } } as Request; @@ -778,9 +869,6 @@ describe('authenticationMiddleware integration tests', () => { const req: Request = { cookies: { access_token: 'invalidToken' - }, - headers: { - authorization: 'invalidToken' } } as Request; @@ -860,9 +948,6 @@ describe('authenticationMiddleware integration tests', () => { const req: Request = { cookies: { access_token: 'validToken' - }, - headers: { - authorization: 'validToken' } } as Request; jest.spyOn(authenticationService, 'isUserLoggedIn').mockRejectedValueOnce(new IdpUnavailableError()); diff --git a/workbench-core/authentication/src/authenticationMiddleware.ts b/workbench-core/authentication/src/authenticationMiddleware.ts index c2d5f394e8..9c9e64f398 100644 --- a/workbench-core/authentication/src/authenticationMiddleware.ts +++ b/workbench-core/authentication/src/authenticationMiddleware.ts @@ -25,6 +25,7 @@ const defaultCookieOptions: CookieOptions = { * This function assumes: * - a request body parameter named `code` that holds the authorization code * - a request body parameter named `codeVerifier` that holds a pkce code verifier value + * - the request origin header exists * * @param authenticationService - a configured {@link AuthenticationService} instance * @param options - object containing optional sameSite cookie and logging service parameters @@ -43,12 +44,14 @@ export function getTokensFromAuthorizationCode( const { loggingService, sameSite } = options || {}; const code = req.body.code; const codeVerifier = req.body.codeVerifier; + const websiteUrl = req.headers.origin; - if (typeof code === 'string' && typeof codeVerifier === 'string') { + if (typeof code === 'string' && typeof codeVerifier === 'string' && typeof websiteUrl === 'string') { try { const { idToken, accessToken, refreshToken } = await authenticationService.handleAuthorizationCode( code, - codeVerifier + codeVerifier, + websiteUrl ); // set cookies @@ -90,6 +93,7 @@ export function getTokensFromAuthorizationCode( * This function assumes: * - a request query parameter named `stateVerifier` that holds a temporary state value * - a request query parameter named `codeChallenge` that holds a temporary pkce code challenge value + * - the request origin header exists * * @param authenticationService - a configured {@link AuthenticationService} instance * @returns the route handler function @@ -105,10 +109,16 @@ export function getAuthorizationCodeUrl( return async function (req: Request, res: Response) { const stateVerifier = req.query.stateVerifier; const codeChallenge = req.query.codeChallenge; - if (typeof stateVerifier === 'string' && typeof codeChallenge === 'string') { - res - .status(200) - .json({ redirectUrl: authenticationService.getAuthorizationCodeUrl(stateVerifier, codeChallenge) }); + const websiteUrl = req.headers.origin; + + if ( + typeof stateVerifier === 'string' && + typeof codeChallenge === 'string' && + typeof websiteUrl === 'string' + ) { + res.status(200).json({ + redirectUrl: authenticationService.getAuthorizationCodeUrl(stateVerifier, codeChallenge, websiteUrl) + }); } else { res.sendStatus(400); } @@ -173,6 +183,7 @@ export function verifyToken( * This function assumes: * - the access token is stored in a cookie named `access_token` * - if there is a refresh token, it is stored in a cookie named `refresh_token` + * - the request origin header exists * * @param authenticationService - a configured {@link AuthenticationService} instance * @param options - object containing optional sameSite cookie and logging service parameters @@ -190,6 +201,12 @@ export function logoutUser( return async function (req: Request, res: Response) { const { loggingService, sameSite } = options || {}; const refreshToken = req.cookies.refresh_token; + const websiteUrl = req.headers.origin; + + if (!websiteUrl) { + res.sendStatus(400); + return; + } if (typeof refreshToken === 'string') { try { @@ -201,6 +218,7 @@ export function logoutUser( } if (isIdpUnavailableError(error)) { res.sendStatus(503); + return; } } } @@ -214,7 +232,7 @@ export function logoutUser( sameSite: sameSite ?? defaultCookieOptions.sameSite }); - res.status(200).json({ logoutUrl: authenticationService.getLogoutUrl() }); + res.status(200).json({ logoutUrl: authenticationService.getLogoutUrl(websiteUrl) }); }; } diff --git a/workbench-core/authentication/src/authenticationPlugin.ts b/workbench-core/authentication/src/authenticationPlugin.ts index 4c86b5aa68..ab3ffc3184 100644 --- a/workbench-core/authentication/src/authenticationPlugin.ts +++ b/workbench-core/authentication/src/authenticationPlugin.ts @@ -70,6 +70,7 @@ export interface AuthenticationPlugin { * * @param code - an authorization code * @param codeVerifier - the PKCE code verifier + * @param websiteUrl - the url to redirect to after login is completed. Must be the same url used in the {@link getAuthorizationCodeUrl} function * @returns a {@link Tokens} object containing the id, access, and refresh tokens and their expiration (in seconds) * * @throws {@link InvalidAuthorizationCodeError} if the authorization code is invalid @@ -77,7 +78,7 @@ export interface AuthenticationPlugin { * @throws {@link InvalidCodeVerifierError} if the PCKE verifier is invalid * @throws {@link IdpUnavailableError} if the plugin's IDP is unavailable */ - handleAuthorizationCode(code: string, codeVerifier: string): Promise; + handleAuthorizationCode(code: string, codeVerifier: string, websiteUrl: string): Promise; /** * Takes temporary state and codeChallenge values and returns the URL of the endpoint used to retrieve the authorization code. @@ -87,9 +88,10 @@ export interface AuthenticationPlugin { * * @param state - a temporary value to represent the state parameter * @param codeChallenge - a temporary value to represent the code challenge parameter + * @param websiteUrl - the url to redirect to after login is completed * @returns the endpoint URL string */ - getAuthorizationCodeUrl(state: string, codeChallenge: string): string; + getAuthorizationCodeUrl(state: string, codeChallenge: string, websiteUrl: string): string; /** * Uses the refresh token to generate new jwt tokens. @@ -106,7 +108,8 @@ export interface AuthenticationPlugin { /** * Gets the URL of the endpoint used to logout the user. * + * @param websiteUrl - the url to redirect to after logout is completed * @returns the endpoint URL string */ - getLogoutUrl(): string; + getLogoutUrl(websiteUrl: string): string; } diff --git a/workbench-core/authentication/src/authenticationService.test.ts b/workbench-core/authentication/src/authenticationService.test.ts index f2f2369a95..5d4cd3726a 100644 --- a/workbench-core/authentication/src/authenticationService.test.ts +++ b/workbench-core/authentication/src/authenticationService.test.ts @@ -11,8 +11,7 @@ const cognitoPluginOptions: CognitoAuthenticationPluginOptions = { cognitoDomain: 'fake-domain', userPoolId: 'fake-user-pool', clientId: 'fake-client-id', - clientSecret: 'fake-client-secret', - websiteUrl: 'fake-website-url' + clientSecret: 'fake-client-secret' } as const; describe('AuthenticationService tests', () => { @@ -70,7 +69,11 @@ describe('AuthenticationService tests', () => { }); it('handleAuthorizationCode should return a Promise that contains the id, access, and refresh tokens and their expiration (in seconds)', async () => { - const result = await service.handleAuthorizationCode('access code', 'code verifier'); + const result = await service.handleAuthorizationCode( + 'access code', + 'code verifier', + 'https://www.fakewebsite.com' + ); expect(result).toMatchObject({ idToken: { @@ -91,10 +94,11 @@ describe('AuthenticationService tests', () => { it('getAuthorizationCodeUrl should return the full URL of the authentication servers authorization code endpoint', () => { const state = 'state'; const codeChallenge = 'code challenge'; - const url = service.getAuthorizationCodeUrl(state, codeChallenge); + const websiteUrl = 'https://www.fakewebsite.com'; + const url = service.getAuthorizationCodeUrl(state, codeChallenge, websiteUrl); expect(url).toBe( - `https://www.fakeurl.com/authorize?client_id=fake-id&response_type=code&scope=openid&redirect_uri=https://www.fakewebsite.com&state=${state}&code_challenge_method=S256&code_challenge=${codeChallenge}` + `https://www.fakeurl.com/authorize?client_id=fake-id&response_type=code&scope=openid&redirect_uri=${websiteUrl}&state=${state}&code_challenge_method=S256&code_challenge=${codeChallenge}` ); }); @@ -114,10 +118,9 @@ describe('AuthenticationService tests', () => { }); it('getLogoutUrl should return the full URL of the authentication servers logout endpoint', () => { - const url = service.getLogoutUrl(); + const websiteUrl = 'https://www.fakewebsite.com'; + const url = service.getLogoutUrl(websiteUrl); - expect(url).toBe( - 'https://www.fakeurl.com/logout?client_id=fake-id&logout_uri=https://www.fakewebsite.com' - ); + expect(url).toBe(`https://www.fakeurl.com/logout?client_id=fake-id&logout_uri=${websiteUrl}`); }); }); diff --git a/workbench-core/authentication/src/authenticationService.ts b/workbench-core/authentication/src/authenticationService.ts index f69288aee2..48a5499d4f 100644 --- a/workbench-core/authentication/src/authenticationService.ts +++ b/workbench-core/authentication/src/authenticationService.ts @@ -88,6 +88,7 @@ export class AuthenticationService { * * @param code - an authorization code * @param codeVerifier - the PKCE code verifier + * @param websiteUrl - the url to redirect to after login is completed. Must be the same url used in the {@link getAuthorizationCodeUrl} function * @returns a {@link Tokens} object containing the id, access, and refresh tokens and their expiration (in seconds) * * @throws {@link InvalidAuthorizationCodeError} if the authorization code is invalid @@ -95,8 +96,12 @@ export class AuthenticationService { * @throws {@link InvalidCodeVerifierError} if the PCKE verifier is invalid * @throws {@link IdpUnavailableError} if the plugin's IDP is unavailable */ - public async handleAuthorizationCode(code: string, codeVerifier: string): Promise { - return this._authenticationPlugin.handleAuthorizationCode(code, codeVerifier); + public async handleAuthorizationCode( + code: string, + codeVerifier: string, + websiteUrl: string + ): Promise { + return this._authenticationPlugin.handleAuthorizationCode(code, codeVerifier, websiteUrl); } /** @@ -107,10 +112,11 @@ export class AuthenticationService { * * @param state - a temporary value to represent the state parameter * @param codeChallenge - a temporary value to represent the code challenge parameter + * @param websiteUrl - the url to redirect to after login is completed * @returns the endpoint URL string */ - public getAuthorizationCodeUrl(state: string, codeChallenge: string): string { - return this._authenticationPlugin.getAuthorizationCodeUrl(state, codeChallenge); + public getAuthorizationCodeUrl(state: string, codeChallenge: string, websiteUrl: string): string { + return this._authenticationPlugin.getAuthorizationCodeUrl(state, codeChallenge, websiteUrl); } /** @@ -130,9 +136,10 @@ export class AuthenticationService { /** * Gets the URL of the endpoint used to logout the user. * + * @param websiteUrl - the url to redirect to after logout is completed * @returns the endpoint URL string */ - public getLogoutUrl(): string { - return this._authenticationPlugin.getLogoutUrl(); + public getLogoutUrl(websiteUrl: string): string { + return this._authenticationPlugin.getLogoutUrl(websiteUrl); } } diff --git a/workbench-core/authentication/src/plugins/__mocks__/cognitoAuthenticationPlugin.ts b/workbench-core/authentication/src/plugins/__mocks__/cognitoAuthenticationPlugin.ts index 9d9f73423f..7fb8ca6e41 100644 --- a/workbench-core/authentication/src/plugins/__mocks__/cognitoAuthenticationPlugin.ts +++ b/workbench-core/authentication/src/plugins/__mocks__/cognitoAuthenticationPlugin.ts @@ -35,7 +35,11 @@ export class CognitoAuthenticationPlugin implements AuthenticationPlugin { public getUserRolesFromToken(decodedToken: CognitoJwtPayload): string[] { return ['role']; } - public async handleAuthorizationCode(code: string, codeVerifier: string): Promise { + public async handleAuthorizationCode( + code: string, + codeVerifier: string, + websiteUrl: string + ): Promise { return Promise.resolve({ idToken: { token: 'id token', @@ -51,8 +55,8 @@ export class CognitoAuthenticationPlugin implements AuthenticationPlugin { } }); } - public getAuthorizationCodeUrl(state: string, codeChallenge: string): string { - return `https://www.fakeurl.com/authorize?client_id=fake-id&response_type=code&scope=openid&redirect_uri=https://www.fakewebsite.com&state=${state}&code_challenge_method=S256&code_challenge=${codeChallenge}`; + public getAuthorizationCodeUrl(state: string, codeChallenge: string, websiteUrl: string): string { + return `https://www.fakeurl.com/authorize?client_id=fake-id&response_type=code&scope=openid&redirect_uri=${websiteUrl}&state=${state}&code_challenge_method=S256&code_challenge=${codeChallenge}`; } public async refreshAccessToken(refreshToken: string): Promise { return Promise.resolve({ @@ -66,7 +70,7 @@ export class CognitoAuthenticationPlugin implements AuthenticationPlugin { } }); } - public getLogoutUrl(): string { - return 'https://www.fakeurl.com/logout?client_id=fake-id&logout_uri=https://www.fakewebsite.com'; + public getLogoutUrl(websiteUrl: string): string { + return `https://www.fakeurl.com/logout?client_id=fake-id&logout_uri=${websiteUrl}`; } } diff --git a/workbench-core/authentication/src/plugins/cognitoAuthenticationPlugin.test.ts b/workbench-core/authentication/src/plugins/cognitoAuthenticationPlugin.test.ts index ad8d92b9b5..5d1bfa165e 100644 --- a/workbench-core/authentication/src/plugins/cognitoAuthenticationPlugin.test.ts +++ b/workbench-core/authentication/src/plugins/cognitoAuthenticationPlugin.test.ts @@ -33,12 +33,15 @@ const cognitoPluginOptions: CognitoAuthenticationPluginOptions = { cognitoDomain: 'fake-domain', userPoolId: 'us-west-2_fakeId', clientId: 'fake-client-id', - clientSecret: 'fake-client-secret', - websiteUrl: 'fake-website-url' + clientSecret: 'fake-client-secret' } as const; const baseUrl = cognitoPluginOptions.cognitoDomain; +const websiteUrl = 'https://www.fakewebsite.com'; +const validToken = 'validToken'; +const invalidToken = 'invalidToken'; + const encodedClientId = Buffer.from( `${cognitoPluginOptions.clientId}:${cognitoPluginOptions.clientSecret}` ).toString('base64'); @@ -103,7 +106,6 @@ describe('CognitoAuthenticationPlugin tests', () => { describe('isUserLoggedIn tests', () => { it('should be true when a valid token is passed in', async () => { - const validToken = 'validToken'; const axiosSpy = jest.spyOn(axios, 'get').mockResolvedValueOnce('valid'); const loggedIn = await plugin.isUserLoggedIn(validToken); @@ -115,7 +117,6 @@ describe('CognitoAuthenticationPlugin tests', () => { }); it('should be false when an invalid token is passed in', async () => { - const invalidToken = 'invalidToken'; const axiosSpy = jest.spyOn(axios, 'get').mockRejectedValueOnce('invalid'); const loggedIn = await plugin.isUserLoggedIn(invalidToken); @@ -127,7 +128,6 @@ describe('CognitoAuthenticationPlugin tests', () => { }); it('should throw IdpUnavailableError when Cognito is unavailable', async () => { - const validToken = 'validToken'; const axiosSpy = jest.spyOn(axios, 'get').mockRejectedValueOnce({ response: { status: 500 } }); await expect(plugin.isUserLoggedIn(validToken)).rejects.toThrow( @@ -149,7 +149,6 @@ describe('CognitoAuthenticationPlugin tests', () => { }); it('should throw InvalidJWTError when an invalid token is passed in', async () => { - const invalidToken = 'invalidToken'; const verifierSpy = jest .spyOn(CognitoJwtVerifier.prototype, 'verify') .mockRejectedValueOnce(new Error()); @@ -163,7 +162,6 @@ describe('CognitoAuthenticationPlugin tests', () => { describe('revokeToken tests', () => { it('should revoke the refresh token when passed in', async () => { - const validToken = 'validToken'; const axiosSpy = jest.spyOn(axios, 'post').mockResolvedValueOnce('revoked'); await plugin.revokeToken(validToken); @@ -181,7 +179,6 @@ describe('CognitoAuthenticationPlugin tests', () => { }); it('should throw InvalidTokenTypeError when a non-refresh token is passed in', async () => { - const invalidToken = 'invalidToken'; const axiosSpy = jest .spyOn(axios, 'post') .mockRejectedValueOnce({ response: { data: { error: 'unsupported_token_type' } } }); @@ -202,7 +199,6 @@ describe('CognitoAuthenticationPlugin tests', () => { }); it('should throw PluginConfigurationError when token revocation is disabled for the app client', async () => { - const invalidToken = 'invalidToken'; const axiosSpy = jest .spyOn(axios, 'post') .mockRejectedValueOnce({ response: { data: { error: 'invalid_request' } } }); @@ -223,7 +219,6 @@ describe('CognitoAuthenticationPlugin tests', () => { }); it('should throw PluginConfigurationError when the client id or secret is invalid', async () => { - const invalidToken = 'invalidToken'; const axiosSpy = jest .spyOn(axios, 'post') .mockRejectedValueOnce({ response: { data: { error: 'invalid_client' } } }); @@ -244,7 +239,6 @@ describe('CognitoAuthenticationPlugin tests', () => { }); it('should throw IdpUnavailableError when Cognito is unavailable', async () => { - const validToken = 'validToken'; const axiosSpy = jest .spyOn(axios, 'post') .mockRejectedValueOnce({ response: { status: 500, data: {} } }); @@ -265,7 +259,6 @@ describe('CognitoAuthenticationPlugin tests', () => { }); it('should rethrow an error when the error is unexpected', async () => { - const invalidToken = 'invalidToken'; const axiosSpy = jest.spyOn(axios, 'post').mockRejectedValueOnce(new Error()); await expect(plugin.revokeToken(invalidToken)).rejects.toThrow(Error); @@ -303,9 +296,12 @@ describe('CognitoAuthenticationPlugin tests', () => { }); describe('handleAuthorizationCode tests', () => { + const validCode = 'validCode'; + const invalidCode = 'invalidCode'; + const validCodeVerifier = 'validCodeVerifier'; + const invalidCodeVerifier = 'invalidCodeVerifier'; + it('should exchange the authorization code for tokens when the code is valid', async () => { - const validCode = 'validCode'; - const codeVerifier = 'codeVerifier'; const fakeTokens = { data: { id_token: 'id token', @@ -316,15 +312,15 @@ describe('CognitoAuthenticationPlugin tests', () => { const axiosSpy = jest.spyOn(axios, 'post').mockResolvedValueOnce(fakeTokens); cognitoMock.on(DescribeUserPoolClientCommand).resolves(userPoolClientInfo); - const tokens = await plugin.handleAuthorizationCode(validCode, codeVerifier); + const tokens = await plugin.handleAuthorizationCode(validCode, validCodeVerifier, websiteUrl); expect(axiosSpy).toHaveBeenCalledWith( `${baseUrl}/oauth2/token`, new URLSearchParams({ grant_type: 'authorization_code', code: validCode, - redirect_uri: cognitoPluginOptions.websiteUrl, - code_verifier: codeVerifier + redirect_uri: websiteUrl, + code_verifier: validCodeVerifier }), { headers: { @@ -350,13 +346,13 @@ describe('CognitoAuthenticationPlugin tests', () => { }); it('should throw InvalidAuthorizationCodeError when an invalid authorization code is passed in', async () => { - const invalidCode = 'invalidCode'; - const codeVerifier = 'codeVerifier'; const axiosSpy = jest .spyOn(axios, 'post') .mockRejectedValueOnce({ response: { data: { error: 'invalid_grant' } } }); - await expect(plugin.handleAuthorizationCode(invalidCode, codeVerifier)).rejects.toThrow( + await expect( + plugin.handleAuthorizationCode(invalidCode, validCodeVerifier, websiteUrl) + ).rejects.toThrow( new InvalidAuthorizationCodeError('authorization code has been used already or is invalid') ); expect(axiosSpy).toHaveBeenCalledWith( @@ -364,8 +360,8 @@ describe('CognitoAuthenticationPlugin tests', () => { new URLSearchParams({ grant_type: 'authorization_code', code: invalidCode, - redirect_uri: cognitoPluginOptions.websiteUrl, - code_verifier: codeVerifier + redirect_uri: websiteUrl, + code_verifier: validCodeVerifier }), { headers: { @@ -377,22 +373,20 @@ describe('CognitoAuthenticationPlugin tests', () => { }); it('should throw PluginConfigurationError when the client id or secret is invalid', async () => { - const invalidCode = 'invalidCode'; - const codeVerifier = 'codeVerifier'; const axiosSpy = jest .spyOn(axios, 'post') .mockRejectedValueOnce({ response: { data: { error: 'invalid_client' } } }); - await expect(plugin.handleAuthorizationCode(invalidCode, codeVerifier)).rejects.toThrow( + await expect(plugin.handleAuthorizationCode(validCode, validCodeVerifier, websiteUrl)).rejects.toThrow( new PluginConfigurationError('invalid client id or client secret') ); expect(axiosSpy).toHaveBeenCalledWith( `${baseUrl}/oauth2/token`, new URLSearchParams({ grant_type: 'authorization_code', - code: invalidCode, - redirect_uri: cognitoPluginOptions.websiteUrl, - code_verifier: codeVerifier + code: validCode, + redirect_uri: websiteUrl, + code_verifier: validCodeVerifier }), { headers: { @@ -404,22 +398,20 @@ describe('CognitoAuthenticationPlugin tests', () => { }); it('should throw PluginConfigurationError when the authorization code grant is disabled for the app client', async () => { - const invalidCode = 'invalidCode'; - const codeVerifier = 'codeVerifier'; const axiosSpy = jest .spyOn(axios, 'post') .mockRejectedValueOnce({ response: { data: { error: 'unauthorized_client' } } }); - await expect(plugin.handleAuthorizationCode(invalidCode, codeVerifier)).rejects.toThrow( + await expect(plugin.handleAuthorizationCode(validCode, validCodeVerifier, websiteUrl)).rejects.toThrow( new PluginConfigurationError('authorization code grant is disabled for this app client') ); expect(axiosSpy).toHaveBeenCalledWith( `${baseUrl}/oauth2/token`, new URLSearchParams({ grant_type: 'authorization_code', - code: invalidCode, - redirect_uri: cognitoPluginOptions.websiteUrl, - code_verifier: codeVerifier + code: validCode, + redirect_uri: websiteUrl, + code_verifier: validCodeVerifier }), { headers: { @@ -431,21 +423,19 @@ describe('CognitoAuthenticationPlugin tests', () => { }); it('should throw InvalidCodeVerifierError when the code verifier is invalid', async () => { - const validCode = 'validCode'; - const invalidCodeVerifier = 'invalidCodeVerifier'; const axiosSpy = jest .spyOn(axios, 'post') .mockRejectedValueOnce({ response: { data: { error: 'invalid_request' } } }); - await expect(plugin.handleAuthorizationCode(validCode, invalidCodeVerifier)).rejects.toThrow( - new InvalidCodeVerifierError('pkce code verifier is invalid') - ); + await expect( + plugin.handleAuthorizationCode(validCode, invalidCodeVerifier, websiteUrl) + ).rejects.toThrow(new InvalidCodeVerifierError('pkce code verifier is invalid')); expect(axiosSpy).toHaveBeenCalledWith( `${baseUrl}/oauth2/token`, new URLSearchParams({ grant_type: 'authorization_code', code: validCode, - redirect_uri: cognitoPluginOptions.websiteUrl, + redirect_uri: websiteUrl, code_verifier: invalidCodeVerifier }), { @@ -458,13 +448,11 @@ describe('CognitoAuthenticationPlugin tests', () => { }); it('should throw IdpUnavailableError when Cognito is unavailable', async () => { - const validCode = 'validCode'; - const validCodeVerifier = 'validCodeVerifier'; const axiosSpy = jest .spyOn(axios, 'post') .mockRejectedValueOnce({ response: { status: 500, data: {} } }); - await expect(plugin.handleAuthorizationCode(validCode, validCodeVerifier)).rejects.toThrow( + await expect(plugin.handleAuthorizationCode(validCode, validCodeVerifier, websiteUrl)).rejects.toThrow( new IdpUnavailableError('Cognito is unavailable') ); expect(axiosSpy).toHaveBeenCalledWith( @@ -472,7 +460,7 @@ describe('CognitoAuthenticationPlugin tests', () => { new URLSearchParams({ grant_type: 'authorization_code', code: validCode, - redirect_uri: cognitoPluginOptions.websiteUrl, + redirect_uri: websiteUrl, code_verifier: validCodeVerifier }), { @@ -485,18 +473,18 @@ describe('CognitoAuthenticationPlugin tests', () => { }); it('should rethrow an error when the error is unexpected', async () => { - const invalidCode = 'invalidCode'; - const codeVerifier = 'codeVerifier'; const axiosSpy = jest.spyOn(axios, 'post').mockRejectedValueOnce(new Error()); - await expect(plugin.handleAuthorizationCode(invalidCode, codeVerifier)).rejects.toThrow(Error); + await expect(plugin.handleAuthorizationCode(validCode, validCodeVerifier, websiteUrl)).rejects.toThrow( + Error + ); expect(axiosSpy).toHaveBeenCalledWith( `${baseUrl}/oauth2/token`, new URLSearchParams({ grant_type: 'authorization_code', - code: invalidCode, - redirect_uri: cognitoPluginOptions.websiteUrl, - code_verifier: codeVerifier + code: validCode, + redirect_uri: websiteUrl, + code_verifier: validCodeVerifier }), { headers: { @@ -512,27 +500,29 @@ describe('CognitoAuthenticationPlugin tests', () => { it('should return the full URL of the authentication servers authorization code endpoint', () => { const state = 'TEMP_STATE'; const codeChallenge = 'TEMP_CODE_CHALLENGE'; - const url = plugin.getAuthorizationCodeUrl(state, codeChallenge); + const url = plugin.getAuthorizationCodeUrl(state, codeChallenge, websiteUrl); expect(url).toBe( - `${baseUrl}/oauth2/authorize?client_id=${cognitoPluginOptions.clientId}&response_type=code&scope=openid&redirect_uri=${cognitoPluginOptions.websiteUrl}&state=${state}&code_challenge_method=S256&code_challenge=${codeChallenge}` + `${baseUrl}/oauth2/authorize?client_id=${cognitoPluginOptions.clientId}&response_type=code&scope=openid&redirect_uri=${websiteUrl}&state=${state}&code_challenge_method=S256&code_challenge=${codeChallenge}` ); }); }); describe('getLogoutUrl tests', () => { it('should return the full URL of the authentication servers logout endpoint', () => { - const url = plugin.getLogoutUrl(); + const url = plugin.getLogoutUrl(websiteUrl); expect(url).toBe( - `${baseUrl}/logout?client_id=${cognitoPluginOptions.clientId}&logout_uri=${cognitoPluginOptions.websiteUrl}` + `${baseUrl}/logout?client_id=${cognitoPluginOptions.clientId}&logout_uri=${websiteUrl}` ); }); }); describe('refreshAccessToken tests', () => { + const validRefreshToken = 'validRefreshToken'; + const invalidRefreshToken = 'invalidRefreshToken'; + it('should retrieve new id and access tokens from Cognito when the refresh token is valid', async () => { - const refreshToken = 'refreshToken'; const fakeTokens = { data: { id_token: 'id token', @@ -542,13 +532,13 @@ describe('CognitoAuthenticationPlugin tests', () => { const axiosSpy = jest.spyOn(axios, 'post').mockResolvedValueOnce(fakeTokens); cognitoMock.on(DescribeUserPoolClientCommand).resolves(userPoolClientInfo); - const tokens = await plugin.refreshAccessToken(refreshToken); + const tokens = await plugin.refreshAccessToken(validRefreshToken); expect(axiosSpy).toHaveBeenCalledWith( `${baseUrl}/oauth2/token`, new URLSearchParams({ grant_type: 'refresh_token', - refresh_token: refreshToken + refresh_token: validRefreshToken }), { headers: { @@ -570,7 +560,6 @@ describe('CognitoAuthenticationPlugin tests', () => { }); it('should throw InvalidTokenError when an invalid refresh token is passed in', async () => { - const invalidRefreshToken = 'invalidRefreshToken'; const axiosSpy = jest .spyOn(axios, 'post') .mockRejectedValueOnce({ response: { data: { error: 'invalid_grant' } } }); @@ -594,7 +583,6 @@ describe('CognitoAuthenticationPlugin tests', () => { }); it('should throw PluginConfigurationError when the client id or secret is invalid', async () => { - const invalidRefreshToken = 'invalidRefreshToken'; const axiosSpy = jest .spyOn(axios, 'post') .mockRejectedValueOnce({ response: { data: { error: 'invalid_client' } } }); @@ -618,7 +606,6 @@ describe('CognitoAuthenticationPlugin tests', () => { }); it('should throw PluginConfigurationError when refreshing access tokens is disabled for the app client', async () => { - const invalidRefreshToken = 'invalidRefreshToken'; const axiosSpy = jest .spyOn(axios, 'post') .mockRejectedValueOnce({ response: { data: { error: 'unauthorized_client' } } }); @@ -642,7 +629,6 @@ describe('CognitoAuthenticationPlugin tests', () => { }); it('should throw IdpUnavailableError when Cognito is unavailable', async () => { - const validRefreshToken = 'validRefreshToken'; const axiosSpy = jest .spyOn(axios, 'post') .mockRejectedValueOnce({ response: { status: 500, data: {} } }); @@ -666,7 +652,6 @@ describe('CognitoAuthenticationPlugin tests', () => { }); it('should rethrow an error when the error is unexpected', async () => { - const invalidRefreshToken = 'invalidRefreshToken'; const axiosSpy = jest.spyOn(axios, 'post').mockRejectedValueOnce(new Error()); await expect(plugin.refreshAccessToken(invalidRefreshToken)).rejects.toThrow(Error); diff --git a/workbench-core/authentication/src/plugins/cognitoAuthenticationPlugin.ts b/workbench-core/authentication/src/plugins/cognitoAuthenticationPlugin.ts index c8d39dda12..38007d247e 100644 --- a/workbench-core/authentication/src/plugins/cognitoAuthenticationPlugin.ts +++ b/workbench-core/authentication/src/plugins/cognitoAuthenticationPlugin.ts @@ -49,14 +49,6 @@ export interface CognitoAuthenticationPluginOptions { * The Cognito app client secret. */ clientSecret: string; - - /** - * The website URL to redirect back to once login in is completed on the hosted UI. - * The URL must exist in the Cognito app client allowed callback URLs list. - * - * @example "https://www.exampleURL.com" - */ - websiteUrl: string; } /** @@ -65,7 +57,6 @@ export interface CognitoAuthenticationPluginOptions { */ export class CognitoAuthenticationPlugin implements AuthenticationPlugin { private _region: string; - private _websiteUrl: string; private _userPoolId: string; private _clientId: string; private _clientSecret: string; @@ -85,12 +76,10 @@ export class CognitoAuthenticationPlugin implements AuthenticationPlugin { */ public constructor({ cognitoDomain, - websiteUrl, userPoolId, clientId, clientSecret }: CognitoAuthenticationPluginOptions) { - this._websiteUrl = websiteUrl; this._userPoolId = userPoolId; this._clientId = clientId; this._clientSecret = clientSecret; @@ -230,6 +219,7 @@ export class CognitoAuthenticationPlugin implements AuthenticationPlugin { * * @param code - the authorization code * @param codeVerifier - the PKCE code verifier + * @param websiteUrl - the url to redirect to after login is completed. Must be the same url used in the {@link getAuthorizationCodeUrl} function * @returns a {@link Tokens} object containing the id, access, and refresh tokens and their expiration (in seconds) * * @throws {@link InvalidAuthorizationCodeError} if the authorization code is invalid @@ -237,7 +227,11 @@ export class CognitoAuthenticationPlugin implements AuthenticationPlugin { * @throws {@link InvalidCodeVerifierError} if the PCKE verifier is invalid * @throws {@link IdpUnavailableError} if Cognito is unavailable */ - public async handleAuthorizationCode(code: string, codeVerifier: string): Promise { + public async handleAuthorizationCode( + code: string, + codeVerifier: string, + websiteUrl: string + ): Promise { try { const encodedClientId = this._getEncodedClientId(); @@ -247,7 +241,7 @@ export class CognitoAuthenticationPlugin implements AuthenticationPlugin { new URLSearchParams({ grant_type: 'authorization_code', code: code, - redirect_uri: this._websiteUrl, + redirect_uri: websiteUrl, code_verifier: codeVerifier }), { @@ -302,10 +296,11 @@ export class CognitoAuthenticationPlugin implements AuthenticationPlugin { * * @param state - a temporary value to represent the state parameter * @param codeChallenge - a temporary value to represent the code challenge parameter + * @param websiteUrl - the url to redirect to after login is completed * @returns the endpoint URL string */ - public getAuthorizationCodeUrl(state: string, codeChallenge: string): string { - return `${this._baseUrl}/oauth2/authorize?client_id=${this._clientId}&response_type=code&scope=openid&redirect_uri=${this._websiteUrl}&state=${state}&code_challenge_method=S256&code_challenge=${codeChallenge}`; + public getAuthorizationCodeUrl(state: string, codeChallenge: string, websiteUrl: string): string { + return `${this._baseUrl}/oauth2/authorize?client_id=${this._clientId}&response_type=code&scope=openid&redirect_uri=${websiteUrl}&state=${state}&code_challenge_method=S256&code_challenge=${codeChallenge}`; } /** @@ -369,10 +364,11 @@ export class CognitoAuthenticationPlugin implements AuthenticationPlugin { /** * Gets the URL of the endpoint used to logout the user. * + * @param websiteUrl - the url to redirect to after logout is completed * @returns the endpoint URL string */ - public getLogoutUrl(): string { - return `${this._baseUrl}/logout?client_id=${this._clientId}&logout_uri=${this._websiteUrl}`; + public getLogoutUrl(websiteUrl: string): string { + return `${this._baseUrl}/logout?client_id=${this._clientId}&logout_uri=${websiteUrl}`; } /** diff --git a/workbench-core/infrastructure/README.md b/workbench-core/infrastructure/README.md index 97d0a4f8f0..910273a717 100644 --- a/workbench-core/infrastructure/README.md +++ b/workbench-core/infrastructure/README.md @@ -37,7 +37,7 @@ Next, set the properties and initialize the construct. // If a user pool name or user pool client name are not provided, CDK will generate them for you. const cognitoProps: WorkbenchCognitoProps = { domainPrefix = 'myDomainPrefix', - websiteUrl = 'https://mysite.mydomain.com', + websiteUrls = ['https://mysite.mydomain.com'], // oidcIdentityProviders = [ myWorkbenchUserPoolOidcIdentityProviderConfig ] }; diff --git a/workbench-core/infrastructure/src/workbenchCognito.test.ts b/workbench-core/infrastructure/src/workbenchCognito.test.ts index d1cab5a6c4..187199fb1a 100644 --- a/workbench-core/infrastructure/src/workbenchCognito.test.ts +++ b/workbench-core/infrastructure/src/workbenchCognito.test.ts @@ -16,7 +16,7 @@ describe('WorkbenchCognito tests', () => { it('has the correct user pool properties', () => { const workbenchCognitoProps: WorkbenchCognitoProps = { domainPrefix: 'test-domain', - websiteUrl: 'https://www.example.com', + websiteUrls: ['https://www.example.com'], userPoolName: 'test-user-pool' }; const stack = new Stack(); @@ -66,7 +66,7 @@ describe('WorkbenchCognito tests', () => { it('has the correct user pool domain properties', () => { const workbenchCognitoProps: WorkbenchCognitoProps = { domainPrefix: 'test-domain', - websiteUrl: 'https://www.example.com' + websiteUrls: ['https://www.example.com'] }; const stack = new Stack(); new WorkbenchCognito(stack, 'TestWorkbenchCognito', workbenchCognitoProps); @@ -81,7 +81,7 @@ describe('WorkbenchCognito tests', () => { it('has the correct user pool client properties', () => { const workbenchCognitoProps: WorkbenchCognitoProps = { domainPrefix: 'test-domain', - websiteUrl: 'https://www.example.com', + websiteUrls: ['https://www.example.com'], userPoolClientName: 'test-user-pool-client' }; const stack = new Stack(); @@ -93,10 +93,10 @@ describe('WorkbenchCognito tests', () => { AllowedOAuthFlows: ['code'], AllowedOAuthFlowsUserPoolClient: true, AllowedOAuthScopes: ['openid'], - CallbackURLs: [workbenchCognitoProps.websiteUrl], + CallbackURLs: workbenchCognitoProps.websiteUrls, EnableTokenRevocation: true, GenerateSecret: true, - LogoutURLs: [workbenchCognitoProps.websiteUrl], + LogoutURLs: workbenchCognitoProps.websiteUrls, PreventUserExistenceErrors: 'ENABLED', IdTokenValidity: 15, AccessTokenValidity: 15, @@ -140,7 +140,7 @@ describe('WorkbenchCognito tests', () => { }; const workbenchCognitoProps: WorkbenchCognitoProps = { domainPrefix: 'test-domain', - websiteUrl: 'https://www.example.com', + websiteUrls: ['https://www.example.com'], oidcIdentityProviders: [oidcProvider1, oidcProvider2] }; const stack = new Stack(); diff --git a/workbench-core/infrastructure/src/workbenchCognito.ts b/workbench-core/infrastructure/src/workbenchCognito.ts index 1e18841c9f..1716ece052 100644 --- a/workbench-core/infrastructure/src/workbenchCognito.ts +++ b/workbench-core/infrastructure/src/workbenchCognito.ts @@ -66,7 +66,7 @@ const userPoolClientDefaults: UserPoolClientOptions = { export interface WorkbenchCognitoProps { domainPrefix: string; - websiteUrl: string; + websiteUrls: string[]; userPoolName?: string; userPoolClientName?: string; oidcIdentityProviders?: WorkbenchUserPoolOidcIdentityProvider[]; @@ -88,7 +88,7 @@ export class WorkbenchCognito extends Construct { public constructor(scope: Construct, id: string, props: WorkbenchCognitoProps) { const { domainPrefix, - websiteUrl, + websiteUrls, userPoolName, userPoolClientName, oidcIdentityProviders: oidcIdentityProviderProps @@ -117,8 +117,8 @@ export class WorkbenchCognito extends Construct { const tempProps: UserPoolClientOptions = { oAuth: { - callbackUrls: [websiteUrl], - logoutUrls: [websiteUrl] + callbackUrls: websiteUrls, + logoutUrls: websiteUrls } }; const userPoolClientProps = merge(userPoolClientDefaults, tempProps); From 0be4ed811b9f71fb5c5d2ed68c40f1ca4b8f710d Mon Sep 17 00:00:00 2001 From: bhudnell Date: Wed, 17 Aug 2022 16:00:09 +0000 Subject: [PATCH 016/621] docs: Auto README.md updates [skip ci] --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5b6aa728ec..35b00ba217 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ # Code Coverage | Statements | Branches | Functions | Lines | | --------------------------- | ----------------------- | ------------------------- | ----------------- | -| ![Statements](https://img.shields.io/badge/statements-87.15%25-yellow.svg?style=flat) | ![Branches](https://img.shields.io/badge/branches-84.99%25-yellow.svg?style=flat) | ![Functions](https://img.shields.io/badge/functions-88.52%25-yellow.svg?style=flat) | ![Lines](https://img.shields.io/badge/lines-87.64%25-yellow.svg?style=flat) | +| ![Statements](https://img.shields.io/badge/statements-87.17%25-yellow.svg?style=flat) | ![Branches](https://img.shields.io/badge/branches-85.03%25-yellow.svg?style=flat) | ![Functions](https://img.shields.io/badge/functions-88.52%25-yellow.svg?style=flat) | ![Lines](https://img.shields.io/badge/lines-87.67%25-yellow.svg?style=flat) | # Contributing Guidelines From 5bd628013b2976680094aca7ee2c767c0165c911 Mon Sep 17 00:00:00 2001 From: Tim Nguyen Date: Wed, 17 Aug 2022 12:24:39 -0400 Subject: [PATCH 017/621] chore: extend access token expiration time (#466) --- .../chore-extend-access-token_2022-08-17-14-09.json | 10 ++++++++++ solutions/swb-reference/src/SWBStack.ts | 3 ++- workbench-core/infrastructure/src/workbenchCognito.ts | 6 ++++-- 3 files changed, 16 insertions(+), 3 deletions(-) create mode 100644 common/changes/@aws/workbench-core-infrastructure/chore-extend-access-token_2022-08-17-14-09.json diff --git a/common/changes/@aws/workbench-core-infrastructure/chore-extend-access-token_2022-08-17-14-09.json b/common/changes/@aws/workbench-core-infrastructure/chore-extend-access-token_2022-08-17-14-09.json new file mode 100644 index 0000000000..285318b5fb --- /dev/null +++ b/common/changes/@aws/workbench-core-infrastructure/chore-extend-access-token_2022-08-17-14-09.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@aws/workbench-core-infrastructure", + "comment": "Added optional parameter to override default access token expiration time", + "type": "none" + } + ], + "packageName": "@aws/workbench-core-infrastructure" +} \ No newline at end of file diff --git a/solutions/swb-reference/src/SWBStack.ts b/solutions/swb-reference/src/SWBStack.ts index 125abfa12a..17019687dc 100644 --- a/solutions/swb-reference/src/SWBStack.ts +++ b/solutions/swb-reference/src/SWBStack.ts @@ -845,7 +845,8 @@ export class SWBStack extends Stack { websiteUrls: websiteUrls, userPoolName: userPoolName, userPoolClientName: userPoolClientName, - oidcIdentityProviders: [] + oidcIdentityProviders: [], + accessTokenValidity: Duration.minutes(60) // Extend access token expiration to 60 minutes to allow integration tests to run successfully. Once MAFoundation-310 has been implemented to allow multiple clientIds, we'll create a separate client for integration tests and the "main" client access token expiration time can be return to 15 minutes }; const workbenchCognito = new WorkbenchCognito(this, 'ServiceWorkbenchCognito', props); diff --git a/workbench-core/infrastructure/src/workbenchCognito.ts b/workbench-core/infrastructure/src/workbenchCognito.ts index 1716ece052..7e9ad16c37 100644 --- a/workbench-core/infrastructure/src/workbenchCognito.ts +++ b/workbench-core/infrastructure/src/workbenchCognito.ts @@ -70,6 +70,7 @@ export interface WorkbenchCognitoProps { userPoolName?: string; userPoolClientName?: string; oidcIdentityProviders?: WorkbenchUserPoolOidcIdentityProvider[]; + accessTokenValidity?: Duration; } export interface WorkbenchUserPoolOidcIdentityProvider @@ -119,7 +120,8 @@ export class WorkbenchCognito extends Construct { oAuth: { callbackUrls: websiteUrls, logoutUrls: websiteUrls - } + }, + accessTokenValidity: props.accessTokenValidity }; const userPoolClientProps = merge(userPoolClientDefaults, tempProps); this.userPoolClient = new UserPoolClient(this, 'WorkbenchUserPoolClient', { @@ -155,4 +157,4 @@ export class WorkbenchCognito extends Construct { this.userPoolClientId = this.userPoolClient.userPoolClientId; this.userPoolClientSecret = SecretValue.unsafePlainText(userPoolClientSecret); } -} +} \ No newline at end of file From 03e4a4051ecd53c90b8f688b955c0b778b202408 Mon Sep 17 00:00:00 2001 From: FernandoAranda Date: Wed, 17 Aug 2022 10:53:47 -0700 Subject: [PATCH 018/621] feat: integration test pipeline (#467) * feat(integration test pipeline): run integration tests on pipeline * feat(integration tests pipeline): add integration test running on pipeline Co-authored-by: Fernando Aranda --- .../deploy-integration-swb-reference.yml | 14 +++- .gitignore | 1 + .../SWBv2.postman_collection.json | 84 +++++++++---------- .../integration-tests/config/testEnv.yaml | 16 ++++ .../support/clientSession.ts | 4 +- 5 files changed, 74 insertions(+), 45 deletions(-) create mode 100644 solutions/swb-reference/integration-tests/config/testEnv.yaml diff --git a/.github/workflows/deploy-integration-swb-reference.yml b/.github/workflows/deploy-integration-swb-reference.yml index d6d8a8a38e..1611d54313 100644 --- a/.github/workflows/deploy-integration-swb-reference.yml +++ b/.github/workflows/deploy-integration-swb-reference.yml @@ -45,4 +45,16 @@ jobs: node ../../common/scripts/install-run-rushx.js compile STAGE='testEnv' node ../../common/scripts/install-run-rushx.js cdk bootstrap ${{ secrets.aws-dev-account-region }} -v STAGE='testEnv' node ../../common/scripts/install-run-rushx.js cdk-deploy - STAGE='testEnv' node ../../common/scripts/install-run-rushx.js run-postDeployment \ No newline at end of file + STAGE='testEnv' node ../../common/scripts/install-run-rushx.js run-postDeployment + + - name: Deploy SWB-UI + # if: steps.changed-swb-files.outputs.any_changed == 'true' + run: | + cd solutions/swb-ui + STAGE='testEnv' node ../../common/scripts/install-run-rushx.js deploy-ui-and-api + + - name: Run Integration tests + # if: steps.changed-swb-files.outputs.any_changed == 'true' + run: | + cd solutions/swb-reference + STAGE='testEnv' node ../../common/scripts/install-run-rushx.js integration-tests \ No newline at end of file diff --git a/.gitignore b/.gitignore index 47cb4e252f..8830977ff0 100644 --- a/.gitignore +++ b/.gitignore @@ -94,6 +94,7 @@ solutions/swb-reference/temp solutions/swb-reference/src/config/*.yaml solutions/swb-reference/integration-tests/config/*.yaml !solutions/swb-reference/integration-tests/config/example.yaml +!solutions/swb-reference/integration-tests/config/testEnv.yaml !solutions/swb-reference/src/config/example.yaml !solutions/swb-reference/src/config/testEnv.yaml solutions/swb-reference/src/config/*.json diff --git a/solutions/swb-reference/SWBv2.postman_collection.json b/solutions/swb-reference/SWBv2.postman_collection.json index eac11bbc2e..c587756736 100644 --- a/solutions/swb-reference/SWBv2.postman_collection.json +++ b/solutions/swb-reference/SWBv2.postman_collection.json @@ -15,8 +15,8 @@ "method": "POST", "header": [ { - "key": "Authorization", - "value": "{{ACCESS_TOKEN}}", + "key": "Cookie", + "value": "access_token={{ACCESS_TOKEN}}", "type": "text" } ], @@ -43,8 +43,8 @@ "method": "POST", "header": [ { - "key": "Authorization", - "value": "{{ACCESS_TOKEN}}", + "key": "Cookie", + "value": "access_token={{ACCESS_TOKEN}}", "type": "text" } ], @@ -74,8 +74,8 @@ "method": "GET", "header": [ { - "key": "Authorization", - "value": "{{ACCESS_TOKEN}}", + "key": "Cookie", + "value": "access_token={{ACCESS_TOKEN}}", "type": "text" } ], @@ -102,8 +102,8 @@ "method": "GET", "header": [ { - "key": "Authorization", - "value": "{{ACCESS_TOKEN}}", + "key": "Cookie", + "value": "access_token={{ACCESS_TOKEN}}", "type": "text" } ], @@ -135,8 +135,8 @@ "method": "PUT", "header": [ { - "key": "Authorization", - "value": "{{ACCESS_TOKEN}}", + "key": "Cookie", + "value": "access_token={{ACCESS_TOKEN}}", "type": "text" } ], @@ -161,8 +161,8 @@ "method": "POST", "header": [ { - "key": "Authorization", - "value": "{{ACCESS_TOKEN}}", + "key": "Cookie", + "value": "access_token={{ACCESS_TOKEN}}", "type": "text" } ], @@ -189,8 +189,8 @@ "method": "PUT", "header": [ { - "key": "Authorization", - "value": "{{ACCESS_TOKEN}}", + "key": "Cookie", + "value": "access_token={{ACCESS_TOKEN}}", "type": "text" } ], @@ -215,8 +215,8 @@ "method": "PUT", "header": [ { - "key": "Authorization", - "value": "{{ACCESS_TOKEN}}", + "key": "Cookie", + "value": "access_token={{ACCESS_TOKEN}}", "type": "text" } ], @@ -241,8 +241,8 @@ "method": "GET", "header": [ { - "key": "Authorization", - "value": "{{ACCESS_TOKEN}}", + "key": "Cookie", + "value": "access_token={{ACCESS_TOKEN}}", "type": "text" } ], @@ -267,8 +267,8 @@ "method": "GET", "header": [ { - "key": "Authorization", - "value": "{{ACCESS_TOKEN}}", + "key": "Cookie", + "value": "access_token={{ACCESS_TOKEN}}", "type": "text" } ], @@ -343,8 +343,8 @@ "method": "GET", "header": [ { - "key": "Authorization", - "value": "{{ACCESS_TOKEN}}", + "key": "Cookie", + "value": "access_token={{ACCESS_TOKEN}}", "type": "text" } ], @@ -374,8 +374,8 @@ "method": "POST", "header": [ { - "key": "Authorization", - "value": "{{ACCESS_TOKEN}}", + "key": "Cookie", + "value": "access_token={{ACCESS_TOKEN}}", "type": "text" } ], @@ -402,8 +402,8 @@ "method": "PUT", "header": [ { - "key": "Authorization", - "value": "{{ACCESS_TOKEN}}", + "key": "Cookie", + "value": "access_token={{ACCESS_TOKEN}}", "type": "text" } ], @@ -436,8 +436,8 @@ "method": "GET", "header": [ { - "key": "Authorization", - "value": "{{ACCESS_TOKEN}}", + "key": "Cookie", + "value": "access_token={{ACCESS_TOKEN}}", "type": "text" } ], @@ -455,8 +455,8 @@ "method": "GET", "header": [ { - "key": "Authorization", - "value": "{{ACCESS_TOKEN}}", + "key": "Cookie", + "value": "access_token={{ACCESS_TOKEN}}", "type": "text" } ], @@ -486,8 +486,8 @@ "method": "POST", "header": [ { - "key": "Authorization", - "value": "{{ACCESS_TOKEN}}", + "key": "Cookie", + "value": "access_token={{ACCESS_TOKEN}}", "type": "text" } ], @@ -521,8 +521,8 @@ "method": "PUT", "header": [ { - "key": "Authorization", - "value": "{{ACCESS_TOKEN}}", + "key": "Cookie", + "value": "access_token={{ACCESS_TOKEN}}", "type": "text" } ], @@ -561,8 +561,8 @@ "method": "GET", "header": [ { - "key": "Authorization", - "value": "{{ACCESS_TOKEN}}", + "key": "Cookie", + "value": "access_token={{ACCESS_TOKEN}}", "type": "text" } ], @@ -592,8 +592,8 @@ "method": "GET", "header": [ { - "key": "Authorization", - "value": "{{ACCESS_TOKEN}}", + "key": "Cookie", + "value": "access_token={{ACCESS_TOKEN}}", "type": "text" } ], @@ -635,8 +635,8 @@ "method": "GET", "header": [ { - "key": "Authorization", - "value": "{{ACCESS_TOKEN}}", + "key": "Cookie", + "value": "access_token={{ACCESS_TOKEN}}", "type": "text" } ], @@ -656,8 +656,8 @@ "method": "POST", "header": [ { - "key": "Authorization", - "value": "{{ACCESS_TOKEN}}", + "key": "Cookie", + "value": "access_token={{ACCESS_TOKEN}}", "type": "text" } ], diff --git a/solutions/swb-reference/integration-tests/config/testEnv.yaml b/solutions/swb-reference/integration-tests/config/testEnv.yaml new file mode 100644 index 0000000000..9bc5caa2c9 --- /dev/null +++ b/solutions/swb-reference/integration-tests/config/testEnv.yaml @@ -0,0 +1,16 @@ +## Note: +## Please deploy the swb-reference package before running the integration test suite. +## Settings declared here are merged with the main accounts CFN outputs to create integration test config items + +# Default Env Config for launching a new environment +envTypeId: 'b8f160e0-8235-48ca-b928-c057d7ccb309' +envTypeConfigId: '7f7bd246-1d51-47e9-a5bd-a4530dd8e040' +projectId: 'cf3019e3-88d5-4a64-9025-26a177e36f59' +envType: 'sagemakerNotebook' + +# Negative Testing +terminatedEnvId: 'b44262fb-ed34-47ae-a4a4-d2d9d9c7a5d5' # id of an environment that has already been terminated + +#Cognito Integ Test Client +rootUsername: 'aws-chamonix-dev+testenvroot@amazon.com' +rootPasswordParamStorePath: '/swb/testEnv/rootUser/password' diff --git a/solutions/swb-reference/integration-tests/support/clientSession.ts b/solutions/swb-reference/integration-tests/support/clientSession.ts index 8b9dace7bb..7ead20697c 100644 --- a/solutions/swb-reference/integration-tests/support/clientSession.ts +++ b/solutions/swb-reference/integration-tests/support/clientSession.ts @@ -25,12 +25,12 @@ export default class ClientSession { const headers: { 'Content-Type': string; - Authorization?: string; + Cookie?: string; } = { 'Content-Type': 'application/json' }; // For anonymous sessions, authorization header is not required if (!this._isAnonymousSession) { - headers.Authorization = accessToken; + headers.Cookie = `access_token=${accessToken}`; } this._axiosInstance = axios.create({ From e0ebf5e0e2cd8cea15b7208a27db329509753e2f Mon Sep 17 00:00:00 2001 From: FernandoAranda Date: Thu, 18 Aug 2022 12:08:34 -0700 Subject: [PATCH 019/621] feat(environments multi step test): add environments multi step test (#458) * feat(environments multi step test): add environments multi step test * fix(environments integration tests): limitate termination max waiting * feat(environments multistep test): move polling to utilities * fix(enviroments multistep test): remove unused code * test(integration test): add environment multi step test * test(integration test): remove await from nested promise * test(integration test): remove unecessary await N * test(integration test): double timeout of pollings and use existing environments type type Co-authored-by: Fernando Aranda --- ...ments-MultiStep-Test_2022-08-18-15-37.json | 10 + solutions/swb-reference/README.md | 2 +- .../support/models/environments.ts | 12 + .../resources/environments/environment.ts | 29 ++- .../support/utils/constants.ts | 16 ++ .../support/utils/utilities.ts | 34 ++- .../tests/isolated/environments/list.test.ts | 6 +- .../tests/multiStep/environment.test.ts | 216 +++++++++++++++++- .../infrastructure/src/workbenchCognito.ts | 2 +- 9 files changed, 306 insertions(+), 21 deletions(-) create mode 100644 common/changes/@aws/workbench-core-infrastructure/feat-Environments-MultiStep-Test_2022-08-18-15-37.json create mode 100644 solutions/swb-reference/integration-tests/support/models/environments.ts create mode 100644 solutions/swb-reference/integration-tests/support/utils/constants.ts diff --git a/common/changes/@aws/workbench-core-infrastructure/feat-Environments-MultiStep-Test_2022-08-18-15-37.json b/common/changes/@aws/workbench-core-infrastructure/feat-Environments-MultiStep-Test_2022-08-18-15-37.json new file mode 100644 index 0000000000..ab119520b5 --- /dev/null +++ b/common/changes/@aws/workbench-core-infrastructure/feat-Environments-MultiStep-Test_2022-08-18-15-37.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@aws/workbench-core-infrastructure", + "comment": "Add Multi Step Environments Integration Test", + "type": "none" + } + ], + "packageName": "@aws/workbench-core-infrastructure" +} \ No newline at end of file diff --git a/solutions/swb-reference/README.md b/solutions/swb-reference/README.md index e2486ce4b0..33bde48b5a 100644 --- a/solutions/swb-reference/README.md +++ b/solutions/swb-reference/README.md @@ -108,7 +108,7 @@ Go to `solutions/swb-app` to update `staticRouteConfig.ts` and `staticPermission ## Obtain Access Token for making authenticated API requests 1. Go to `swb-reference/scripts` folder 2. Pull down all required dependencies by running `rushx build` -3. Run `STAGE= node generateCognitoToken.js ''` with the correct value for `` and ``. It should be a user that has been created for your SWB deployment. Note, the quotes around `` is necessary for the script to correctly parse passwords that have symbols in it. +3. Run `STAGE= node generateCognitoTokens.js ''` with the correct value for `` and ``. It should be a user that has been created for your SWB deployment. Note, the quotes around `` is necessary for the script to correctly parse passwords that have symbols in it. 4. In the console output, use the `accessToken` that is provided to make authenticated API requests. ## Appendix diff --git a/solutions/swb-reference/integration-tests/support/models/environments.ts b/solutions/swb-reference/integration-tests/support/models/environments.ts new file mode 100644 index 0000000000..ef7df692b7 --- /dev/null +++ b/solutions/swb-reference/integration-tests/support/models/environments.ts @@ -0,0 +1,12 @@ +import { EnvironmentStatus } from '@aws/workbench-core-environments'; + +export interface EnvironmentItemResponse { + id: string; + status: EnvironmentStatus; +} + +export interface ListEnvironmentResponse { + data: { + data: EnvironmentItemResponse[]; + }; +} diff --git a/solutions/swb-reference/integration-tests/support/resources/environments/environment.ts b/solutions/swb-reference/integration-tests/support/resources/environments/environment.ts index 7472af02e3..96b92cbbf9 100644 --- a/solutions/swb-reference/integration-tests/support/resources/environments/environment.ts +++ b/solutions/swb-reference/integration-tests/support/resources/environments/environment.ts @@ -5,7 +5,8 @@ import { EnvironmentStatus } from '@aws/workbench-core-environments'; import { AxiosResponse } from 'axios'; import ClientSession from '../../clientSession'; -import { sleep } from '../../utils/utilities'; +import { ENVIRONMENT_START_MAX_WAITING_SECONDS } from '../../utils/constants'; +import { poll } from '../../utils/utilities'; import Resource from '../base/resource'; export default class Environment extends Resource { @@ -13,6 +14,10 @@ export default class Environment extends Resource { super(clientSession, 'environment', id, parentApi); } + public async connect(): Promise { + return this._axiosInstance.get(`${this._api}/connections`); + } + public async stop(): Promise { return this._axiosInstance.put(`${this._api}/stop`); } @@ -27,10 +32,6 @@ export default class Environment extends Resource { protected async cleanup(): Promise { const defAdminSession = await this._setup.getDefaultAdminSession(); - const maxWaitTimeInSeconds = 600; - const startTimeInMs = Date.now(); - let totalTimeWaitedInSeconds = 0; - const { data: resource } = await defAdminSession.resources.environments.environment(this._id).get(); let envStatus: EnvironmentStatus = resource.status; if (['TERMINATED', 'TERMINATING'].includes(envStatus)) { @@ -39,18 +40,22 @@ export default class Environment extends Resource { } try { console.log(`Attempting to delete environment ${this._id}. This will take a few minutes.`); - while (envStatus === 'PENDING' && totalTimeWaitedInSeconds < maxWaitTimeInSeconds) { - await sleep(15000); - const { data: resource } = await defAdminSession.resources.environments.environment(this._id).get(); - envStatus = resource.status; - totalTimeWaitedInSeconds = (Date.now() - startTimeInMs) / 1000; - } + await poll( + async () => defAdminSession.resources.environments.environment(this._id).get(), + (env) => env?.data?.status !== 'PENDING' && env?.data?.status !== 'STARTING', + ENVIRONMENT_START_MAX_WAITING_SECONDS + ); + const { data: completedResource } = await defAdminSession.resources.environments + .environment(this._id) + .get(); + envStatus = completedResource.status; + console.log(`Terminating environment ${this._id}.`); await defAdminSession.resources.environments.environment(this._id).terminate(); console.log(`Deleted environment ${this._id}`); } catch (e) { console.log( `Could not delete environment. Last known status for env ${this._id} was "${envStatus}". - Waited ${totalTimeWaitedInSeconds} seconds for environment to reach valid state so it could be deleted; encountered error: ${e}` + Encountered error: ${e}` ); } } diff --git a/solutions/swb-reference/integration-tests/support/utils/constants.ts b/solutions/swb-reference/integration-tests/support/utils/constants.ts new file mode 100644 index 0000000000..ad02f98f8c --- /dev/null +++ b/solutions/swb-reference/integration-tests/support/utils/constants.ts @@ -0,0 +1,16 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +//Average start waiting time is 5 minutes, setting start max waiting to 10 minutes +export const ENVIRONMENT_START_MAX_WAITING_SECONDS: number = 600; + +//Average start waiting time is 2 minutes, setting start max waiting to 4 minutes +export const ENVIRONMENT_STOP_MAX_WAITING_SECONDS: number = 240; + +//Average start waiting time is 1:30 minutes, setting start max waiting to 3 minutes +export const ENVIRONMENT_TERMINATE_MAX_WAITING_SECONDS: number = 180; + +export const DEFAULT_POLLING_INTERVAL_SECONDS: number = 15; + +export const DEFAULT_POLLING_MAX_WAITING_SECONDS: number = 600; diff --git a/solutions/swb-reference/integration-tests/support/utils/utilities.ts b/solutions/swb-reference/integration-tests/support/utils/utilities.ts index 2569cf057a..3829a53873 100644 --- a/solutions/swb-reference/integration-tests/support/utils/utilities.ts +++ b/solutions/swb-reference/integration-tests/support/utils/utilities.ts @@ -2,6 +2,7 @@ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0 */ +import { DEFAULT_POLLING_INTERVAL_SECONDS, DEFAULT_POLLING_MAX_WAITING_SECONDS } from './constants'; import HttpError from './HttpError'; /** @@ -21,4 +22,35 @@ function checkHttpError(actualError: Error, expectedError: HttpError): void { expect(actualError instanceof HttpError).toBeTruthy(); expect(expectedError.isEqual(actualError)).toBeTruthy(); } -export { sleep, checkHttpError }; + +async function poll( + conditionData: () => Promise, + stopCondition: (resource: T) => boolean, + maxWaitTimeInSeconds?: number, + pollingIntervalInSeconds?: number +): Promise { + const startTimeInMs = Date.now(); + const timeLimitInSeconds = maxWaitTimeInSeconds ?? DEFAULT_POLLING_MAX_WAITING_SECONDS; + const intervalSeconds = pollingIntervalInSeconds ?? DEFAULT_POLLING_INTERVAL_SECONDS; + let totalTimeWaitedInSeconds = 0; + + let data = await conditionData(); + try { + console.log(`Polling in progress. This will take a few minutes.`); + while (!stopCondition(data) && totalTimeWaitedInSeconds < timeLimitInSeconds) { + await sleep(intervalSeconds * 1000); + data = await conditionData(); + totalTimeWaitedInSeconds = (Date.now() - startTimeInMs) / 1000; + } + if (totalTimeWaitedInSeconds >= timeLimitInSeconds) + console.log(`Polling exceeded the time limit (${timeLimitInSeconds} seconds).`); + else console.log(`Polling finished successfully.`); + } catch (e) { + console.log( + `Polling failed.". + Waited ${totalTimeWaitedInSeconds} seconds for resource to reach valid state so it could finish polling; encountered error: ${e}` + ); + } +} + +export { sleep, checkHttpError, poll }; diff --git a/solutions/swb-reference/integration-tests/tests/isolated/environments/list.test.ts b/solutions/swb-reference/integration-tests/tests/isolated/environments/list.test.ts index ec19799166..2b1d686d29 100644 --- a/solutions/swb-reference/integration-tests/tests/isolated/environments/list.test.ts +++ b/solutions/swb-reference/integration-tests/tests/isolated/environments/list.test.ts @@ -26,7 +26,7 @@ describe('list environments', () => { try { const queryParams = { status: 'someInvalidStatus' - } + }; await adminSession.resources.environments.get(queryParams); } catch (e) { checkHttpError( @@ -57,10 +57,10 @@ describe('list environments', () => { test.each(validEnvStatuses)('list environments when status query is %s', async (status) => { const queryParams = { status - } + }; const { data: response } = await adminSession.resources.environments.get(queryParams); - + expect(Array.isArray(response.data)).toBe(true); }); }); diff --git a/solutions/swb-reference/integration-tests/tests/multiStep/environment.test.ts b/solutions/swb-reference/integration-tests/tests/multiStep/environment.test.ts index 78e3761a6e..e476ebd56b 100644 --- a/solutions/swb-reference/integration-tests/tests/multiStep/environment.test.ts +++ b/solutions/swb-reference/integration-tests/tests/multiStep/environment.test.ts @@ -3,8 +3,15 @@ * SPDX-License-Identifier: Apache-2.0 */ import ClientSession from '../../support/clientSession'; +import { ListEnvironmentResponse } from '../../support/models/environments'; import Setup from '../../support/setup'; +import { + ENVIRONMENT_START_MAX_WAITING_SECONDS, + ENVIRONMENT_STOP_MAX_WAITING_SECONDS, + ENVIRONMENT_TERMINATE_MAX_WAITING_SECONDS +} from '../../support/utils/constants'; import { uuidRegExp } from '../../support/utils/regExpressions'; +import { poll } from '../../support/utils/utilities'; describe('multiStep environment test', () => { const setup: Setup = new Setup(); @@ -19,8 +26,10 @@ describe('multiStep environment test', () => { }); test('launch, connect, stop, get, terminate', async () => { - const { data: response } = await adminSession.resources.environments.create(); - expect(response).toMatchObject({ + //Create Environment A + console.log('Creating Environment A'); + const { data: environmentA } = await adminSession.resources.environments.create(); + expect(environmentA).toMatchObject({ id: expect.stringMatching(uuidRegExp), instanceId: '', // empty string because instanceId value has not been propagated by statusHandler yet provisionedProductId: '', // empty string because provisionedProductId has not been propagated by statusHandler yet @@ -29,6 +38,207 @@ describe('multiStep environment test', () => { PROJ: expect.anything() // PROJ should be defined }); - // TODO: Add connect, stop, get, terminate test + //Create Environment B + console.log('Creating Environment B'); + const { data: environmentB } = await adminSession.resources.environments.create(); + expect(environmentB).toMatchObject({ + id: expect.stringMatching(uuidRegExp), + instanceId: '', // empty string because instanceId value has not been propagated by statusHandler yet + provisionedProductId: '', // empty string because provisionedProductId has not been propagated by statusHandler yet + status: 'PENDING', + ETC: expect.anything(), //ETC should be defined + PROJ: expect.anything() // PROJ should be defined + }); + + //Verify Environment A was started and is available + console.log('Verify Environment A was started and is available'); + await poll( + async () => adminSession.resources.environments.environment(environmentA.id).get(), + (env) => env?.data?.status !== 'PENDING', + ENVIRONMENT_START_MAX_WAITING_SECONDS + ); //wait for environmentA to complete + const { data: environmentACompleted } = await adminSession.resources.environments + .environment(environmentA.id) + .get(); + expect(environmentACompleted).toMatchObject({ + id: expect.stringMatching(uuidRegExp), + status: 'COMPLETED', + ETC: expect.anything(), //ETC should be defined + PROJ: expect.anything() // PROJ should be defined + }); + console.log('Environment A Completed'); + + //Verify Environment B was started and is available + console.log('Verify Environment B was started and is available'); + await poll( + async () => adminSession.resources.environments.environment(environmentB.id).get(), + (env) => env?.data?.status !== 'PENDING', + ENVIRONMENT_START_MAX_WAITING_SECONDS + ); //wait for environmentB to complete + const { data: environmentBCompleted } = await adminSession.resources.environments + .environment(environmentB.id) + .get(); + expect(environmentBCompleted).toMatchObject({ + id: expect.stringMatching(uuidRegExp), + status: 'COMPLETED', + ETC: expect.anything(), //ETC should be defined + PROJ: expect.anything() // PROJ should be defined + }); + console.log('Environment B Completed'); + + //Verify Connect Environment A + console.log('Verify Connect Environment A'); + const { data: environmentAConnectInfo } = await adminSession.resources.environments + .environment(environmentA.id) + .connect(); + expect(environmentAConnectInfo).toMatchObject({ + authCredResponse: { + url: expect.anything() + }, + instructionResponse: expect.anything() + }); + + //Stop Environment A + console.log('Stopping Environment A'); + await adminSession.resources.environments.environment(environmentA.id).stop(); + await poll( + async () => adminSession.resources.environments.environment(environmentA.id).get(), + (env) => env?.data?.status !== 'STOPPING', + ENVIRONMENT_STOP_MAX_WAITING_SECONDS + ); //wait for environmentA to stop + const { data: environmentAStopped } = await adminSession.resources.environments + .environment(environmentA.id) + .get(); + expect(environmentAStopped).toMatchObject({ + id: expect.stringMatching(uuidRegExp), + status: 'STOPPED', + ETC: expect.anything(), //ETC should be defined + PROJ: expect.anything() // PROJ should be defined + }); + console.log('Environment A Stopped'); + + //Search Environment A filtering by name + console.log('Searching for Environment A: filtering by "name"'); + const { data: environmentsNameFilter }: ListEnvironmentResponse = + await adminSession.resources.environments.get({ + name: environmentAStopped.name + }); + expect( + environmentsNameFilter.data.filter((env) => env.id === environmentAStopped.id).length + ).toBeTruthy(); + + //Search Environment A filtering by status + console.log('Searching for Environment A: filtering by "status"'); + const { data: environmentsStatusFilter }: ListEnvironmentResponse = + await adminSession.resources.environments.get({ + status: environmentAStopped.status + }); + expect( + environmentsStatusFilter.data.filter((env) => env.id === environmentAStopped.id).length + ).toBeTruthy(); + + //Search Environment A filtering by created at + console.log('Searching for Environment A: filtering by "createdAt"'); + const { data: environmentsCreatedAtFilter }: ListEnvironmentResponse = + await adminSession.resources.environments.get({ + createdAtFrom: environmentAStopped.createdAt, + createdAtTo: environmentAStopped.createdAt + }); + expect( + environmentsCreatedAtFilter.data.filter((env) => env.id === environmentAStopped.id).length + ).toBeTruthy(); + + //Search Environment A filtering by owner + console.log('Searching for Environment A: filtering by "owner"'); + const { data: environmentsOwnerFilter }: ListEnvironmentResponse = + await adminSession.resources.environments.get({ + owner: environmentAStopped.owner + }); + expect( + environmentsOwnerFilter.data.filter((env) => env.id === environmentAStopped.id).length + ).toBeTruthy(); + + //Start Environment A after being stopped + console.log('Starting Environment A after being stopped'); + await adminSession.resources.environments.environment(environmentA.id).start(); + await poll( + async () => adminSession.resources.environments.environment(environmentA.id).get(), + (env) => env?.data?.status !== 'PENDING' && env?.data?.status !== 'STARTING', + ENVIRONMENT_START_MAX_WAITING_SECONDS + ); //wait for environmentA to complete + const { data: environmentAStarted } = await adminSession.resources.environments + .environment(environmentA.id) + .get(); + expect(environmentAStarted).toMatchObject({ + id: expect.stringMatching(uuidRegExp), + status: 'COMPLETED', + ETC: expect.anything(), //ETC should be defined + PROJ: expect.anything() // PROJ should be defined + }); + console.log('Environment A Completed'); + + //Stop Environments A and B + console.log('Stopping Environments A and B'); + await adminSession.resources.environments.environment(environmentA.id).stop(); + await adminSession.resources.environments.environment(environmentB.id).stop(); + + //Wait for Environment A to stop + await poll( + async () => adminSession.resources.environments.environment(environmentA.id).get(), + (env) => env?.data?.status !== 'STOPPING', + ENVIRONMENT_STOP_MAX_WAITING_SECONDS + ); //wait for environmentA to stop + const { data: environmentAStopped2 } = await adminSession.resources.environments + .environment(environmentA.id) + .get(); + expect(environmentAStopped2).toMatchObject({ + id: expect.stringMatching(uuidRegExp), + status: 'STOPPED', + ETC: expect.anything(), //ETC should be defined + PROJ: expect.anything() // PROJ should be defined + }); + console.log('Environment A Stopped'); + + //Wait for Environment B to stop + await poll( + async () => adminSession.resources.environments.environment(environmentB.id).get(), + (env) => env?.data?.status !== 'STOPPING', + ENVIRONMENT_STOP_MAX_WAITING_SECONDS + ); //wait for environmentB to stop + const { data: environmentBStopped } = await adminSession.resources.environments + .environment(environmentB.id) + .get(); + expect(environmentBStopped).toMatchObject({ + id: expect.stringMatching(uuidRegExp), + status: 'STOPPED', + ETC: expect.anything(), //ETC should be defined + PROJ: expect.anything() // PROJ should be defined + }); + console.log('Environment B Stopped'); + + //Terminate Environments A and B + console.log('Terminating Environments A and B'); + await adminSession.resources.environments.environment(environmentA.id).terminate(); + await adminSession.resources.environments.environment(environmentB.id).terminate(); + + //Wait for Environments A and B to terminate + await poll( + async () => adminSession.resources.environments.environment(environmentA.id).get(), + (env) => env?.data?.status !== 'TERMINATING', + ENVIRONMENT_TERMINATE_MAX_WAITING_SECONDS + ); //wait for environmentA to Terminate + await poll( + async () => adminSession.resources.environments.environment(environmentB.id).get(), + (env) => env?.data?.status !== 'TERMINATING', + ENVIRONMENT_TERMINATE_MAX_WAITING_SECONDS + ); //wait for environmentB to Terminate + //Validate Environments A and B are not retrieved on get all environments call + console.log('Check that terminated environments are not shown when listing all environments'); + const { data: allEnvironments }: ListEnvironmentResponse = await adminSession.resources.environments.get( + {} + ); + expect( + allEnvironments.data.filter((env) => env.id === environmentA.id || env.id === environmentB.id).length + ).toEqual(0); }); }); diff --git a/workbench-core/infrastructure/src/workbenchCognito.ts b/workbench-core/infrastructure/src/workbenchCognito.ts index 7e9ad16c37..0e630ccbf4 100644 --- a/workbench-core/infrastructure/src/workbenchCognito.ts +++ b/workbench-core/infrastructure/src/workbenchCognito.ts @@ -157,4 +157,4 @@ export class WorkbenchCognito extends Construct { this.userPoolClientId = this.userPoolClient.userPoolClientId; this.userPoolClientSecret = SecretValue.unsafePlainText(userPoolClientSecret); } -} \ No newline at end of file +} From f8dafa6851c4699c16c69492cdaf8523c01593a5 Mon Sep 17 00:00:00 2001 From: Samir Sharan <11716061+insignias@users.noreply.github.com> Date: Thu, 18 Aug 2022 16:15:35 -0400 Subject: [PATCH 020/621] docs: rush add instructions for developers (#468) Co-authored-by: Samir Sharan --- DEVELOPMENT.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index e86bcab2d8..cc2b4b4337 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -73,5 +73,8 @@ Please refer to [Contributing via Pull Requests](./CONTRIBUTING.md#contributing- 3. `rush install --bypass-policy` (This will install the `cinstall` and `cupdate` commands) 4. Start using `rush cinstall` or `rush cupdate` instead of `rush install` or `rush update`. +### Add packages using rush: +1. `rush add -p --caret -s; rush cupdate` + ## Getting support for rush [Getting Support](https://rushjs.io/pages/help/support/) From 86e29b9c924c5bbfbaae9f18d0e6dcd200bd0192 Mon Sep 17 00:00:00 2001 From: kpark277 <103979972+kpark277@users.noreply.github.com> Date: Mon, 22 Aug 2022 08:01:27 -0700 Subject: [PATCH 021/621] test(swb-reference): adding GetEnvironment and GetEnvironmentConnection integ tests (#471) --- .../isolated/environments/connections.test.ts | 37 +++++++++++++++- .../tests/isolated/environments/get.test.ts | 42 +++++++++++++++++++ 2 files changed, 77 insertions(+), 2 deletions(-) create mode 100644 solutions/swb-reference/integration-tests/tests/isolated/environments/get.test.ts diff --git a/solutions/swb-reference/integration-tests/tests/isolated/environments/connections.test.ts b/solutions/swb-reference/integration-tests/tests/isolated/environments/connections.test.ts index bd1eb5dd37..7e03f5dc69 100644 --- a/solutions/swb-reference/integration-tests/tests/isolated/environments/connections.test.ts +++ b/solutions/swb-reference/integration-tests/tests/isolated/environments/connections.test.ts @@ -2,8 +2,41 @@ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0 */ + +import ClientSession from '../../../support/clientSession'; +import Setup from '../../../support/setup'; +import HttpError from '../../../support/utils/HttpError'; +import { checkHttpError } from '../../../support/utils/utilities'; + describe('environments connection negative tests', () => { - test('environment does not exist', () => { - expect(true).toEqual(true); + const setup: Setup = new Setup(); + let adminSession: ClientSession; + + beforeEach(() => { + expect.hasAssertions(); + }); + + beforeAll(async () => { + adminSession = await setup.getDefaultAdminSession(); + }); + + afterAll(async () => { + await setup.cleanup(); + }); + + test('environment does not exist', async () => { + const fakeEnvId = '927ff6bd-9d0e-44d0-b754-47ee50e68edb'; + try { + await adminSession.resources.environments.environment(fakeEnvId).connect(); + } catch (e) { + checkHttpError( + e, + new HttpError(404, { + statusCode: 404, + error: 'Not Found', + message: `Could not find environment ${fakeEnvId}` + }) + ); + } }); }); diff --git a/solutions/swb-reference/integration-tests/tests/isolated/environments/get.test.ts b/solutions/swb-reference/integration-tests/tests/isolated/environments/get.test.ts new file mode 100644 index 0000000000..fd4bf7aac4 --- /dev/null +++ b/solutions/swb-reference/integration-tests/tests/isolated/environments/get.test.ts @@ -0,0 +1,42 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import ClientSession from '../../../support/clientSession'; +import Setup from '../../../support/setup'; +import HttpError from '../../../support/utils/HttpError'; +import { checkHttpError } from '../../../support/utils/utilities'; + +describe('get environment negative tests', () => { + const setup: Setup = new Setup(); + let adminSession: ClientSession; + + beforeEach(() => { + expect.hasAssertions(); + }); + + beforeAll(async () => { + adminSession = await setup.getDefaultAdminSession(); + }); + + afterAll(async () => { + await setup.cleanup(); + }); + + test('environment does not exist', async () => { + const fakeEnvId = '927ff6bd-9d0e-44d0-b754-47ee50e68edb'; + try { + await adminSession.resources.environments.environment(fakeEnvId).get(); + } catch (e) { + checkHttpError( + e, + new HttpError(404, { + statusCode: 404, + error: 'Not Found', + message: `Could not find environment ${fakeEnvId}` + }) + ); + } + }); +}); From 42a228d452c22e591c2ebe1566b716cbbaf32e54 Mon Sep 17 00:00:00 2001 From: Samir Sharan <11716061+insignias@users.noreply.github.com> Date: Mon, 22 Aug 2022 14:30:50 -0400 Subject: [PATCH 022/621] chore: rush change updates (#472) * chore: rush change updates * docs: doc update Co-authored-by: Samir Sharan --- DEVELOPMENT.md | 6 ++++-- rush.json | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index cc2b4b4337..410f903b93 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -42,8 +42,9 @@ NOTE: to install new packages or dependencies: **DO NOT USE** `npm install`. Ref - generate code coverage summary in the root README file 6. We have commit-msg git-hook configured: - A check has been added for commit messages to ensure they are in [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/) format. -7. `git push` -8. Further checks are triggered on a Pull Request. Please refer the [Pull Requests Process](#pull-requests-process) section +7. [`rush change`](https://rushjs.io/pages/commands/rush_change/) compares changes between <`your branch`> and the remote `develop` branch and generates `changefiles` for each package that was changed. The `publish` command will consume these files and perform the proper version bumps. Note these changes will eventually be published in a `CHANGELOG.md` file in each package. +8. Add, commit the `changefiles` generated by `rush change` command and then push the changes using `git push` +9. Further checks are triggered on a Pull Request. Please refer the [Pull Requests Process](#pull-requests-process) section ## Pull Requests Process @@ -65,6 +66,7 @@ Please refer to [Contributing via Pull Requests](./CONTRIBUTING.md#contributing- - run build - `rushx build` - run test - `rushx test` - run lint:fix - `rushx lint:fix` +6. [`rush change`](https://rushjs.io/pages/commands/rush_change/) - By default `rush change` command compares the changes to <`your branch`> against the default branch(`develop`) defined in the `rush.json` file. However, if you want to override the `rush change` to compare <`your branch`> against <`some other parent branch`> instead of default branch (`develop`) then you can use: `rush change -b ` ## Commands applicable for developers with existing GitHooks ### `rush cinstall/cupdate` usage instructions: diff --git a/rush.json b/rush.json index 19aaa62fc6..201817862f 100644 --- a/rush.json +++ b/rush.json @@ -19,7 +19,7 @@ }, "repository": { "url": "https://github.com/awslabs/solution-spark-on-aws.git", - "defaultBranch": "main" + "defaultBranch": "develop" }, "eventHooks": { "preRushInstall": [], From 647a403e0b0abcedd57a8e272b8e4706d8ae8d65 Mon Sep 17 00:00:00 2001 From: Marianna Ghirardelli <43092418+maghirardelli@users.noreply.github.com> Date: Mon, 22 Aug 2022 17:05:42 -0400 Subject: [PATCH 023/621] refactor: UI design system refactor (#470) * refactor(ui): refactor to Cloudscape Design System which is the evolution of AWS Design System --- .../rush/browser-approved-packages.json | 20 +++++ common/config/rush/pnpm-lock.yaml | 77 ++++++++++++++++--- solutions/swb-ui/README.md | 2 +- solutions/swb-ui/next.config.js | 2 +- solutions/swb-ui/package.json | 9 +-- solutions/swb-ui/src/common/alerts.tsx | 2 +- .../swb-ui/src/common/dateRelativeOptions.tsx | 2 +- .../src/common/dateRelativeProperties.tsx | 2 +- solutions/swb-ui/src/common/labels.tsx | 2 +- solutions/swb-ui/src/common/splitPanel.tsx | 2 +- .../swb-ui/src/common/tableEmptyState.tsx | 2 +- .../swb-ui/src/common/tableNoMatchState.tsx | 2 +- .../swb-ui/src/components/BaseLayout.tsx | 8 +- .../swb-ui/src/components/EnvTypeCards.tsx | 4 +- .../src/components/EnvTypeConfigCards.tsx | 4 +- .../components/EnvironmentConnectModal.tsx | 6 +- solutions/swb-ui/src/components/Header.tsx | 2 +- solutions/swb-ui/src/components/Hero.tsx | 4 +- solutions/swb-ui/src/components/Login.tsx | 2 +- .../swb-ui/src/components/Navigation.tsx | 2 +- .../src/context/NotificationContext.tsx | 2 +- .../workspacesColumnDefinitions.tsx | 2 +- .../workspacesFilteringOptions.tsx | 2 +- .../workspacesFilteringProperties.tsx | 4 +- solutions/swb-ui/src/models/User.ts | 2 +- solutions/swb-ui/src/pages/_app.tsx | 2 +- .../swb-ui/src/pages/environments/index.tsx | 6 +- .../swb-ui/src/pages/environments/new.tsx | 2 +- solutions/swb-ui/src/pages/index.tsx | 4 +- solutions/swb-ui/src/pages/users/index.tsx | 2 +- solutions/swb-ui/src/pages/users/new.tsx | 2 +- .../swb-ui/src/styles/Header.module.scss | 4 +- solutions/swb-ui/src/styles/globals.scss | 4 +- .../usersColumnDefinitions.tsx | 2 +- 34 files changed, 136 insertions(+), 60 deletions(-) diff --git a/common/config/rush/browser-approved-packages.json b/common/config/rush/browser-approved-packages.json index 3de1e63c27..6a2cbcb3e2 100644 --- a/common/config/rush/browser-approved-packages.json +++ b/common/config/rush/browser-approved-packages.json @@ -174,6 +174,22 @@ "name": "@casl/ability", "allowedCategories": [ "production" ] }, + { + "name": "@cloudscape-design/collection-hooks", + "allowedCategories": [ "production" ] + }, + { + "name": "@cloudscape-design/components", + "allowedCategories": [ "production" ] + }, + { + "name": "@cloudscape-design/design-tokens", + "allowedCategories": [ "production" ] + }, + { + "name": "@cloudscape-design/global-styles", + "allowedCategories": [ "production" ] + }, { "name": "@hapi/boom", "allowedCategories": [ "production" ] @@ -370,6 +386,10 @@ "name": "next", "allowedCategories": [ "production", "prototypes" ] }, + { + "name": "next-compose-plugins", + "allowedCategories": [ "production" ] + }, { "name": "next-global-css", "allowedCategories": [ "production", "prototypes" ] diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index b6bc4ccc8f..fcb35fe246 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -294,15 +294,14 @@ importers: specifiers: '@aws/eslint-config-workbench-core-eslint-custom': workspace:* '@aws/workbench-core-environments': workspace:* - '@awsui/collection-hooks': ^1.0.0 - '@awsui/components-react': ^3.0.0 - '@awsui/design-tokens': ^3.0.0 - '@awsui/global-styles': ^1.0.0 - '@awsui/test-utils-core': ^1.0.0 '@babel/core': ^7.17.9 '@babel/preset-env': ^7.16.11 '@babel/preset-react': ^7.16.7 '@babel/preset-typescript': ^7.16.7 + '@cloudscape-design/collection-hooks': ^1.0.3 + '@cloudscape-design/components': ^3.0.27 + '@cloudscape-design/design-tokens': ^3.0.1 + '@cloudscape-design/global-styles': ^1.0.1 '@rushstack/eslint-config': ^2.5.1 '@rushstack/heft': ^0.45.0 '@rushstack/heft-jest-plugin': ^0.2.3 @@ -363,15 +362,14 @@ importers: uuid: 8.3.2 devDependencies: '@aws/eslint-config-workbench-core-eslint-custom': link:../../workbench-core/eslint-custom - '@awsui/collection-hooks': 1.0.25_react@17.0.2 - '@awsui/components-react': 3.0.512_b3482aaf5744fc7c2aeb7941b0e0a78f - '@awsui/design-tokens': 3.0.21 - '@awsui/global-styles': 1.0.14 - '@awsui/test-utils-core': 1.0.19 '@babel/core': 7.18.2 '@babel/preset-env': 7.18.2_@babel+core@7.18.2 '@babel/preset-react': 7.17.12_@babel+core@7.18.2 '@babel/preset-typescript': 7.17.12_@babel+core@7.18.2 + '@cloudscape-design/collection-hooks': 1.0.3_react@17.0.2 + '@cloudscape-design/components': 3.0.27_b3482aaf5744fc7c2aeb7941b0e0a78f + '@cloudscape-design/design-tokens': 3.0.1 + '@cloudscape-design/global-styles': 1.0.1 '@rushstack/eslint-config': 2.6.0_eslint@8.16.0+typescript@4.7.2 '@rushstack/heft': 0.45.5 '@rushstack/heft-jest-plugin': 0.2.15_@rushstack+heft@0.45.5 @@ -5005,6 +5003,65 @@ packages: '@ucast/mongo2js': 1.3.3 dev: false + /@cloudscape-design/collection-hooks/1.0.3_react@17.0.2: + resolution: {integrity: sha512-aiUSxtZ+LNsWBncqxDq7cnZlbsZ3Dt3jweqbyMZIKly3BS9LABN5dw03/OyVRV8dMsOslvXg7mRSB0daifaGZA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + react: 17.0.2 + dev: true + + /@cloudscape-design/components/3.0.27_b3482aaf5744fc7c2aeb7941b0e0a78f: + resolution: {integrity: sha512-eJeaDA5OSQ3KaKfIySDo08zSirGm+0+Z4cnBU9bj2K15Bvxtfx75N+P7wQWgmEEwBBvtTIqgtOt6jN3cXI+BTw==} + peerDependencies: + react: ^16.8 || ^17 || ^18 + react-dom: ^16.8 || ^17 || ^18 + dependencies: + '@cloudscape-design/collection-hooks': 1.0.3_react@17.0.2 + '@cloudscape-design/test-utils-core': 1.0.1 + '@cloudscape-design/theming-runtime': 1.0.1 + '@juggle/resize-observer': 3.3.1 + ace-builds: 1.5.3 + balanced-match: 1.0.2 + clsx: 1.1.1 + d3-scale: 2.2.2 + d3-shape: 1.3.7 + date-fns: 2.28.0 + mnth: 2.0.0 + react: 17.0.2 + react-dom: 17.0.2_react@17.0.2 + react-focus-lock: 2.8.1_b08e3c15324cbe90a6ff8fcd416c932c + react-keyed-flatten-children: 1.3.0_react@17.0.2 + react-resizable: 1.11.1_react-dom@17.0.2+react@17.0.2 + react-transition-group: 4.4.2_react-dom@17.0.2+react@17.0.2 + react-virtual: 2.10.4_react@17.0.2 + tslib: 2.4.0 + weekstart: 1.1.0 + transitivePeerDependencies: + - '@types/react' + dev: true + + /@cloudscape-design/design-tokens/3.0.1: + resolution: {integrity: sha512-RkHFxmv0DE/YXD+nnySXA20tETdLW2TZx++bGMVssygGUVLxYQoH+KhGYoLC/LjT2R1KL5Pzf+mOr1iFCYouaw==} + dev: true + + /@cloudscape-design/global-styles/1.0.1: + resolution: {integrity: sha512-Ofb1bRiZllftEiaIknpO9QSVYPFkdDUMFKownvjAOshDsxZ9GhNatjFTW94PL25YhgQOrotkN8g4vdL02b/umQ==} + dev: true + + /@cloudscape-design/test-utils-core/1.0.1: + resolution: {integrity: sha512-w3rDQ2S786YqAmv779AiOJ6l7CaAxSlxHxD9O1en32q0y3VOFsTNHCfu6RYdMy0gI5N8uBsQl4Cp8hbtgH2SXw==} + dependencies: + css-selector-tokenizer: 0.8.0 + css.escape: 1.5.1 + dev: true + + /@cloudscape-design/theming-runtime/1.0.1: + resolution: {integrity: sha512-/Hj3EiCvZRkbwcEWv/F0ut8iF0ZjF/H/Cmcm1XHA5b/kV6HzmeFLSFs+QMFEq4sdrvHXtdQORB99lBEOVXh9Cg==} + dependencies: + tslib: 2.4.0 + dev: true + /@cnakazawa/watch/1.0.4: resolution: {integrity: sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ==} engines: {node: '>=0.1.95'} diff --git a/solutions/swb-ui/README.md b/solutions/swb-ui/README.md index 0926c92713..1ffb804fd4 100644 --- a/solutions/swb-ui/README.md +++ b/solutions/swb-ui/README.md @@ -78,4 +78,4 @@ Open [http://localhost:3000](http://localhost:3000) in your browser to access th ## Design system -For the design system we are using @awsui project. More information can be found on the [AWS-UI GitHub](https://github.com/aws/awsui-documentation). \ No newline at end of file +For the design system we are using @cloudscape-design project. More information can be found on the [Cloudscape Design site](https://cloudscape.design/). \ No newline at end of file diff --git a/solutions/swb-ui/next.config.js b/solutions/swb-ui/next.config.js index f9b05aa543..de6fb267ff 100644 --- a/solutions/swb-ui/next.config.js +++ b/solutions/swb-ui/next.config.js @@ -1,4 +1,4 @@ -const withTM = require('next-transpile-modules')(['@awsui/components-react']); +const withTM = require('next-transpile-modules')(['@cloudscape-design/components']); /** @type {import('next').NextConfig} */ const nextConfig = { diff --git a/solutions/swb-ui/package.json b/solutions/swb-ui/package.json index 65944aeaba..abb7718f6b 100644 --- a/solutions/swb-ui/package.json +++ b/solutions/swb-ui/package.json @@ -48,15 +48,14 @@ }, "devDependencies": { "@aws/eslint-config-workbench-core-eslint-custom": "workspace:*", - "@awsui/collection-hooks": "^1.0.0", - "@awsui/components-react": "^3.0.0", - "@awsui/design-tokens": "^3.0.0", - "@awsui/global-styles": "^1.0.0", - "@awsui/test-utils-core": "^1.0.0", "@babel/core": "^7.17.9", "@babel/preset-env": "^7.16.11", "@babel/preset-react": "^7.16.7", "@babel/preset-typescript": "^7.16.7", + "@cloudscape-design/collection-hooks": "^1.0.3", + "@cloudscape-design/components": "^3.0.27", + "@cloudscape-design/design-tokens": "^3.0.1", + "@cloudscape-design/global-styles": "^1.0.1", "@rushstack/eslint-config": "^2.5.1", "@rushstack/heft": "^0.45.0", "@rushstack/heft-jest-plugin": "^0.2.3", diff --git a/solutions/swb-ui/src/common/alerts.tsx b/solutions/swb-ui/src/common/alerts.tsx index 15c347ac2a..5103957c6f 100644 --- a/solutions/swb-ui/src/common/alerts.tsx +++ b/solutions/swb-ui/src/common/alerts.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { Alert, AlertProps, Button } from '@awsui/components-react'; +import { Alert, AlertProps, Button } from '@cloudscape-design/components'; import React from 'react'; export function TerminateWarning( diff --git a/solutions/swb-ui/src/common/dateRelativeOptions.tsx b/solutions/swb-ui/src/common/dateRelativeOptions.tsx index afca719a19..37ff231f33 100644 --- a/solutions/swb-ui/src/common/dateRelativeOptions.tsx +++ b/solutions/swb-ui/src/common/dateRelativeOptions.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { DateRangePickerProps } from '@awsui/components-react'; +import { DateRangePickerProps } from '@cloudscape-design/components'; export const relativeOptions: readonly DateRangePickerProps.RelativeOption[] = [ { diff --git a/solutions/swb-ui/src/common/dateRelativeProperties.tsx b/solutions/swb-ui/src/common/dateRelativeProperties.tsx index 90c4ba935d..190f4d73dd 100644 --- a/solutions/swb-ui/src/common/dateRelativeProperties.tsx +++ b/solutions/swb-ui/src/common/dateRelativeProperties.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { DateRangePickerProps } from '@awsui/components-react'; +import { DateRangePickerProps } from '@cloudscape-design/components'; import { addDays, addHours, addMinutes, addMonths, addSeconds, addWeeks, addYears } from 'date-fns'; const differenceInDays = (dateOne: Date, dateTwo: Date): number => { diff --git a/solutions/swb-ui/src/common/labels.tsx b/solutions/swb-ui/src/common/labels.tsx index b5e9b7a278..bb3ebce72a 100644 --- a/solutions/swb-ui/src/common/labels.tsx +++ b/solutions/swb-ui/src/common/labels.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { AppLayoutProps, PaginationProps, PropertyFilterProps } from '@awsui/components-react'; +import { AppLayoutProps, PaginationProps, PropertyFilterProps } from '@cloudscape-design/components'; // i18nStrings labels for export const i18nStrings: PropertyFilterProps.I18nStrings = { diff --git a/solutions/swb-ui/src/common/splitPanel.tsx b/solutions/swb-ui/src/common/splitPanel.tsx index c08b3b3141..428cc7df81 100644 --- a/solutions/swb-ui/src/common/splitPanel.tsx +++ b/solutions/swb-ui/src/common/splitPanel.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { AppLayoutProps, BarChart, Box, Button, SplitPanelProps } from '@awsui/components-react'; +import { AppLayoutProps, BarChart, Box, Button, SplitPanelProps } from '@cloudscape-design/components'; import { useEffect, useState } from 'react'; export const splitPaneli18nstrings: SplitPanelProps.I18nStrings = { diff --git a/solutions/swb-ui/src/common/tableEmptyState.tsx b/solutions/swb-ui/src/common/tableEmptyState.tsx index d8a94336aa..dda49dd319 100644 --- a/solutions/swb-ui/src/common/tableEmptyState.tsx +++ b/solutions/swb-ui/src/common/tableEmptyState.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { Box, Button } from '@awsui/components-react'; +import { Box, Button } from '@cloudscape-design/components'; /** * Displays the empty state of any table diff --git a/solutions/swb-ui/src/common/tableNoMatchState.tsx b/solutions/swb-ui/src/common/tableNoMatchState.tsx index 8d0f7d8b07..1597cd03ca 100644 --- a/solutions/swb-ui/src/common/tableNoMatchState.tsx +++ b/solutions/swb-ui/src/common/tableNoMatchState.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { Box, Button } from '@awsui/components-react'; +import { Box } from '@cloudscape-design/components'; /** * Displays the no match state of any table diff --git a/solutions/swb-ui/src/components/BaseLayout.tsx b/solutions/swb-ui/src/components/BaseLayout.tsx index 716f8c75e2..278318221a 100644 --- a/solutions/swb-ui/src/components/BaseLayout.tsx +++ b/solutions/swb-ui/src/components/BaseLayout.tsx @@ -3,10 +3,10 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { BreadcrumbGroupProps } from '@awsui/components-react'; -import AppLayout, { AppLayoutProps } from '@awsui/components-react/app-layout'; -import BreadcrumbGroup from '@awsui/components-react/breadcrumb-group'; -import Flashbar from '@awsui/components-react/flashbar'; +import { BreadcrumbGroupProps } from '@cloudscape-design/components'; +import AppLayout, { AppLayoutProps } from '@cloudscape-design/components/app-layout'; +import BreadcrumbGroup from '@cloudscape-design/components/breadcrumb-group'; +import Flashbar from '@cloudscape-design/components/flashbar'; import Head from 'next/head'; import React, { useState } from 'react'; import { layoutLabels } from '../common/labels'; diff --git a/solutions/swb-ui/src/components/EnvTypeCards.tsx b/solutions/swb-ui/src/components/EnvTypeCards.tsx index aa3c6fd984..6233db33ad 100644 --- a/solutions/swb-ui/src/components/EnvTypeCards.tsx +++ b/solutions/swb-ui/src/components/EnvTypeCards.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { useCollection } from '@awsui/collection-hooks'; +import { useCollection } from '@cloudscape-design/collection-hooks'; import { CollectionPreferences, Cards, @@ -12,7 +12,7 @@ import { TextFilter, CardsProps, TextContent -} from '@awsui/components-react'; +} from '@cloudscape-design/components'; import { useState } from 'react'; import { TableEmptyDisplay } from '../common/tableEmptyState'; import { TableNoMatchDisplay } from '../common/tableNoMatchState'; diff --git a/solutions/swb-ui/src/components/EnvTypeConfigCards.tsx b/solutions/swb-ui/src/components/EnvTypeConfigCards.tsx index 41d3e5f371..fb9b3eeeb6 100644 --- a/solutions/swb-ui/src/components/EnvTypeConfigCards.tsx +++ b/solutions/swb-ui/src/components/EnvTypeConfigCards.tsx @@ -3,8 +3,8 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { useCollection } from '@awsui/collection-hooks'; -import { Cards, Box, CardsProps } from '@awsui/components-react'; +import { useCollection } from '@cloudscape-design/collection-hooks'; +import { Cards, Box, CardsProps } from '@cloudscape-design/components'; import React, { useEffect, useState } from 'react'; import { EnvTypeConfigItem } from '../models/EnvironmentTypeConfig'; diff --git a/solutions/swb-ui/src/components/EnvironmentConnectModal.tsx b/solutions/swb-ui/src/components/EnvironmentConnectModal.tsx index 3f1c914d2f..29ee74f92d 100644 --- a/solutions/swb-ui/src/components/EnvironmentConnectModal.tsx +++ b/solutions/swb-ui/src/components/EnvironmentConnectModal.tsx @@ -4,9 +4,9 @@ */ import { EnvironmentConnectionLinkPlaceholder } from '@aws/workbench-core-environments'; -import { Modal, SpaceBetween, Link } from '@awsui/components-react'; -import Box from '@awsui/components-react/box'; -import Button from '@awsui/components-react/button'; +import { Modal, SpaceBetween, Link } from '@cloudscape-design/components'; +import Box from '@cloudscape-design/components/box'; +import Button from '@cloudscape-design/components/button'; import { useNotifications } from '../context/NotificationContext'; interface EnvironmentConnectModalProps { diff --git a/solutions/swb-ui/src/components/Header.tsx b/solutions/swb-ui/src/components/Header.tsx index 3239dbb0a7..d48d314b4e 100644 --- a/solutions/swb-ui/src/components/Header.tsx +++ b/solutions/swb-ui/src/components/Header.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import TopNavigation from '@awsui/components-react/top-navigation'; +import TopNavigation from '@cloudscape-design/components/top-navigation'; import { headerLabels } from '../common/labels'; import { useAuthentication } from '../context/AuthenticationContext'; import { useSettings } from '../context/SettingsContext'; diff --git a/solutions/swb-ui/src/components/Hero.tsx b/solutions/swb-ui/src/components/Hero.tsx index fe70c30ab4..e9cb138f4a 100644 --- a/solutions/swb-ui/src/components/Hero.tsx +++ b/solutions/swb-ui/src/components/Hero.tsx @@ -3,8 +3,8 @@ * SPDX-License-Identifier: Apache-2.0 */ -import Box from '@awsui/components-react/box'; -import Grid from '@awsui/components-react/grid'; +import Box from '@cloudscape-design/components/box'; +import Grid from '@cloudscape-design/components/grid'; import { useSettings } from '../context/SettingsContext'; import Login from './Login'; diff --git a/solutions/swb-ui/src/components/Login.tsx b/solutions/swb-ui/src/components/Login.tsx index d4384c499a..b53eb50751 100644 --- a/solutions/swb-ui/src/components/Login.tsx +++ b/solutions/swb-ui/src/components/Login.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import Button from '@awsui/components-react/button'; +import Button from '@cloudscape-design/components/button'; import { useAuthentication } from '../context/AuthenticationContext'; import styles from '../styles/Hero.module.scss'; diff --git a/solutions/swb-ui/src/components/Navigation.tsx b/solutions/swb-ui/src/components/Navigation.tsx index 2a9a09720c..a4989e77d4 100644 --- a/solutions/swb-ui/src/components/Navigation.tsx +++ b/solutions/swb-ui/src/components/Navigation.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import SideNavigation, { SideNavigationProps } from '@awsui/components-react/side-navigation'; +import SideNavigation, { SideNavigationProps } from '@cloudscape-design/components/side-navigation'; import React from 'react'; import { useAuthentication } from '../context/AuthenticationContext'; import RouteGuard from './RouteGuard'; diff --git a/solutions/swb-ui/src/context/NotificationContext.tsx b/solutions/swb-ui/src/context/NotificationContext.tsx index 6fcdeadf39..53a68d1937 100644 --- a/solutions/swb-ui/src/context/NotificationContext.tsx +++ b/solutions/swb-ui/src/context/NotificationContext.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { FlashbarProps } from '@awsui/components-react/flashbar'; +import { FlashbarProps } from '@cloudscape-design/components/flashbar'; import { createContext, useContext, Context, useState } from 'react'; export interface Notifications { diff --git a/solutions/swb-ui/src/environments-table-config/workspacesColumnDefinitions.tsx b/solutions/swb-ui/src/environments-table-config/workspacesColumnDefinitions.tsx index 952d93a829..e279a7ec5e 100644 --- a/solutions/swb-ui/src/environments-table-config/workspacesColumnDefinitions.tsx +++ b/solutions/swb-ui/src/environments-table-config/workspacesColumnDefinitions.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { TableProps } from '@awsui/components-react'; +import { TableProps } from '@cloudscape-design/components'; export const columnDefinitions: readonly TableProps.ColumnDefinition[] = [ { diff --git a/solutions/swb-ui/src/environments-table-config/workspacesFilteringOptions.tsx b/solutions/swb-ui/src/environments-table-config/workspacesFilteringOptions.tsx index 15bbb98c77..390ec20615 100644 --- a/solutions/swb-ui/src/environments-table-config/workspacesFilteringOptions.tsx +++ b/solutions/swb-ui/src/environments-table-config/workspacesFilteringOptions.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { PropertyFilterProps } from '@awsui/components-react/property-filter'; +import { PropertyFilterProps } from '@cloudscape-design/components/property-filter'; export const filteringOptions: readonly PropertyFilterProps.FilteringOption[] = [ { propertyKey: 'owner', value: '' }, diff --git a/solutions/swb-ui/src/environments-table-config/workspacesFilteringProperties.tsx b/solutions/swb-ui/src/environments-table-config/workspacesFilteringProperties.tsx index 6e88c11f94..4180774f51 100644 --- a/solutions/swb-ui/src/environments-table-config/workspacesFilteringProperties.tsx +++ b/solutions/swb-ui/src/environments-table-config/workspacesFilteringProperties.tsx @@ -3,9 +3,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { FilteringProperty } from '@awsui/collection-hooks/dist/cjs/interfaces'; +import { PropertyFilterProperty } from '@cloudscape-design/collection-hooks/dist/cjs/interfaces'; -export const filteringProperties: readonly FilteringProperty[] = [ +export const filteringProperties: readonly PropertyFilterProperty[] = [ { key: 'workspaceName', operators: ['=', '!=', ':', '!:'], diff --git a/solutions/swb-ui/src/models/User.ts b/solutions/swb-ui/src/models/User.ts index ad4c9dc377..f2e06c1bd7 100644 --- a/solutions/swb-ui/src/models/User.ts +++ b/solutions/swb-ui/src/models/User.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { IconProps } from '@awsui/components-react/icon'; +import { IconProps } from '@cloudscape-design/components/icon'; export interface UserItem { id: string; diff --git a/solutions/swb-ui/src/pages/_app.tsx b/solutions/swb-ui/src/pages/_app.tsx index f15805e928..6006bd4562 100644 --- a/solutions/swb-ui/src/pages/_app.tsx +++ b/solutions/swb-ui/src/pages/_app.tsx @@ -4,7 +4,7 @@ */ // No BaseLayout for the global _app.tsx because login page should have no nav -import '@awsui/global-styles/index.css'; +import '@cloudscape-design/global-styles/index.css'; import '../styles/globals.scss'; import type { AppProps } from 'next/app'; import Header from '../components/Header'; diff --git a/solutions/swb-ui/src/pages/environments/index.tsx b/solutions/swb-ui/src/pages/environments/index.tsx index b2bab11398..e4c4dc6164 100644 --- a/solutions/swb-ui/src/pages/environments/index.tsx +++ b/solutions/swb-ui/src/pages/environments/index.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { useCollection } from '@awsui/collection-hooks'; +import { useCollection } from '@cloudscape-design/collection-hooks'; import { Box, BreadcrumbGroupProps, @@ -18,8 +18,8 @@ import { SpaceBetween, Table, StatusIndicator -} from '@awsui/components-react'; -import { FlashbarProps } from '@awsui/components-react/flashbar'; +} from '@cloudscape-design/components'; +import { FlashbarProps } from '@cloudscape-design/components/flashbar'; import type { NextPage } from 'next'; import { useRouter } from 'next/router'; diff --git a/solutions/swb-ui/src/pages/environments/new.tsx b/solutions/swb-ui/src/pages/environments/new.tsx index c0442281fb..bd2f6f4bf0 100644 --- a/solutions/swb-ui/src/pages/environments/new.tsx +++ b/solutions/swb-ui/src/pages/environments/new.tsx @@ -18,7 +18,7 @@ import { Textarea, Select, Multiselect -} from '@awsui/components-react'; +} from '@cloudscape-design/components'; import type { NextPage } from 'next'; import { useRouter } from 'next/router'; import React, { useEffect, useState } from 'react'; diff --git a/solutions/swb-ui/src/pages/index.tsx b/solutions/swb-ui/src/pages/index.tsx index 01108523d4..338b77dbf4 100644 --- a/solutions/swb-ui/src/pages/index.tsx +++ b/solutions/swb-ui/src/pages/index.tsx @@ -3,8 +3,8 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { BreadcrumbGroupProps } from '@awsui/components-react'; -import Box from '@awsui/components-react/box'; +import { BreadcrumbGroupProps } from '@cloudscape-design/components'; +import Box from '@cloudscape-design/components/box'; import type { NextPage } from 'next'; import BaseLayout from '../components/BaseLayout'; import Hero from '../components/Hero'; diff --git a/solutions/swb-ui/src/pages/users/index.tsx b/solutions/swb-ui/src/pages/users/index.tsx index 63eb9dfba0..7b1eed638b 100644 --- a/solutions/swb-ui/src/pages/users/index.tsx +++ b/solutions/swb-ui/src/pages/users/index.tsx @@ -12,7 +12,7 @@ import { SpaceBetween, Table, StatusIndicator -} from '@awsui/components-react'; +} from '@cloudscape-design/components'; import type { NextPage } from 'next'; import Head from 'next/head'; import React, { useEffect, useState } from 'react'; diff --git a/solutions/swb-ui/src/pages/users/new.tsx b/solutions/swb-ui/src/pages/users/new.tsx index 40d3162583..0ddbf29ca2 100644 --- a/solutions/swb-ui/src/pages/users/new.tsx +++ b/solutions/swb-ui/src/pages/users/new.tsx @@ -13,7 +13,7 @@ import { FormField, Button, Container -} from '@awsui/components-react'; +} from '@cloudscape-design/components'; import type { NextPage } from 'next'; import { useRouter } from 'next/router'; import React, { useEffect, useState } from 'react'; diff --git a/solutions/swb-ui/src/styles/Header.module.scss b/solutions/swb-ui/src/styles/Header.module.scss index d0686d19df..1d85e21569 100644 --- a/solutions/swb-ui/src/styles/Header.module.scss +++ b/solutions/swb-ui/src/styles/Header.module.scss @@ -1,10 +1,10 @@ -@use '~@awsui/design-tokens/index.scss' as awsui; +@use '~@cloudscape-design/design-tokens/index.scss' as cloudscape-design; .header { display: block; position: sticky; top: 0; z-index: 1000; - background: awsui.$color-background-home-header; + background: cloudscape-design.$color-background-home-header; font-family: 'Amazon Ember', 'Helvetica Neue', Roboto, Arial, sans-serif; } diff --git a/solutions/swb-ui/src/styles/globals.scss b/solutions/swb-ui/src/styles/globals.scss index b7987c7ad6..b9b395cdfd 100644 --- a/solutions/swb-ui/src/styles/globals.scss +++ b/solutions/swb-ui/src/styles/globals.scss @@ -1,5 +1,5 @@ -@use '~@awsui/design-tokens/index.scss' as awsui; +@use '~@cloudscape-design/design-tokens/index.scss' as cloudscape-design; body { - background: awsui.$color-background-layout-main; + background: cloudscape-design.$color-background-layout-main; } diff --git a/solutions/swb-ui/src/users-table-config/usersColumnDefinitions.tsx b/solutions/swb-ui/src/users-table-config/usersColumnDefinitions.tsx index bd868ffaf3..e1884fd1ac 100644 --- a/solutions/swb-ui/src/users-table-config/usersColumnDefinitions.tsx +++ b/solutions/swb-ui/src/users-table-config/usersColumnDefinitions.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { TableProps } from '@awsui/components-react'; +import { TableProps } from '@cloudscape-design/components'; export const columnDefinitions: readonly TableProps.ColumnDefinition[] = [ { From e1630e9d798f4d65a56245eeba82a835352227a9 Mon Sep 17 00:00:00 2001 From: Brendon Hudnell Date: Tue, 23 Aug 2022 12:38:17 -0600 Subject: [PATCH 024/621] docs: updated workbench cognito documentation (#469) * feat: doc/workbench-cognito-cdk-construct * docs: added doc for workbench cognito construct * docs: added rush change files * docs: more doc update * docs: added access token validity example * fix: spelling * Update workbench-core/infrastructure/docs/workbenchCognito.md Co-authored-by: HaiTao Zhang Co-authored-by: Robert Smayda Co-authored-by: HaiTao Zhang --- ...ognito-cdk-construct_2022-08-19-18-47.json | 10 ++++ workbench-core/infrastructure/README.md | 39 +----------- .../infrastructure/docs/workbenchCognito.md | 60 +++++++++++++++++++ 3 files changed, 71 insertions(+), 38 deletions(-) create mode 100644 common/changes/@aws/workbench-core-infrastructure/doc-workbench-cognito-cdk-construct_2022-08-19-18-47.json create mode 100644 workbench-core/infrastructure/docs/workbenchCognito.md diff --git a/common/changes/@aws/workbench-core-infrastructure/doc-workbench-cognito-cdk-construct_2022-08-19-18-47.json b/common/changes/@aws/workbench-core-infrastructure/doc-workbench-cognito-cdk-construct_2022-08-19-18-47.json new file mode 100644 index 0000000000..60fe232ab9 --- /dev/null +++ b/common/changes/@aws/workbench-core-infrastructure/doc-workbench-cognito-cdk-construct_2022-08-19-18-47.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@aws/workbench-core-infrastructure", + "comment": "Updated doc for WorkbenchCognito construct", + "type": "none" + } + ], + "packageName": "@aws/workbench-core-infrastructure" +} \ No newline at end of file diff --git a/workbench-core/infrastructure/README.md b/workbench-core/infrastructure/README.md index 910273a717..e8b3c38669 100644 --- a/workbench-core/infrastructure/README.md +++ b/workbench-core/infrastructure/README.md @@ -11,41 +11,4 @@ Workbench core components are designed to work with existing infrastructure when available. When infrastructure is not available this package is here to help. This package serves two purposes. First, to help fill in the gaps where organizations are diverging from reference infrastructure, but still need to deploy a few elements. Second, to assist solution developers by providing easy to configure CDK style constructs which can be used as infrastructure building blocks in their solutions. ## Components -### WorkbenchCognito -The WorkbenchCognito component is a CDK construct which deploys an AWS Cognito User Pool. The User Pool will be initialized with a client application for the website URL provided in the properties. One may also initialized one or more OIDC provider configurations to allow for alternate single sign on providers to be utilized using the `workbench-core-authentication` package's Cognito interface. - -## Usage -First, install the package in your solution using your prefered package manager. -```bash -npm -i @aws/workbench-core-infrastructure -``` - -Import the -```typescript -import { WorkbenchCognito, WorkbenchCognitoProps } from '@aws/workbench-core-infrastructure' -// import the OidcProvider properties interface if including a SSO component. -// import { WorkbenchUserPoolOidcIdentityProvider } from '@aws/workbench-core-infrastructure' -``` - -Next, set the properties and initialize the construct. - -```typescript -// ... -// within a CDK app or construct - -// set the cognito properites. -// If a user pool name or user pool client name are not provided, CDK will generate them for you. -const cognitoProps: WorkbenchCognitoProps = { - domainPrefix = 'myDomainPrefix', - websiteUrls = ['https://mysite.mydomain.com'], - // oidcIdentityProviders = [ myWorkbenchUserPoolOidcIdentityProviderConfig ] -}; - -this.myWorkbenchCognito = new WorkbenchCognito(this, 'my-workbench-cognito', cognitoProps); - -// ... -// the rest of your app/construct code. -``` - -Now build and deploy. - +- [WorkbenchCognito](./docs/workbenchCognito.md) diff --git a/workbench-core/infrastructure/docs/workbenchCognito.md b/workbench-core/infrastructure/docs/workbenchCognito.md new file mode 100644 index 0000000000..942aed8011 --- /dev/null +++ b/workbench-core/infrastructure/docs/workbenchCognito.md @@ -0,0 +1,60 @@ +# WorkbenchCognito CDK Construct + +## Description +A CDK Construct to create a Cognito user pool for use with the [authentication package](../../authentication/). The construct creates a user pool, a domain, an app client, and (optionally) one or more OpenID Connect(OIDC)-compatible Identity Providers (IdPs) + +## Usage + +### Installing the package + +Using NPM: +```bash +npm install @aws/workbench-core-infrastructure +``` + +Using Yarn: +```bash +yarn add @aws/workbench-core-infrastructure +``` + +### Example +```ts +// Import the construct and properties interface +import { WorkbenchCognito, WorkbenchCognitoProps } from '@aws/workbench-core-infrastructure'; +// Use the imports below if including a SSO component +import { WorkbenchUserPoolOidcIdentityProvider } from '@aws/workbench-core-infrastructure'; +import { ProviderAttribute } from 'aws-cdk-lib/aws-cognito'; + +// ... +// Within a CDK app or construct + +// Create an OidcProvider if including a SSO component +const myWorkbenchUserPoolOidcIdentityProvider: WorkbenchUserPoolOidcIdentityProvider = { + name: 'publicNameForProvider', + clientId: 'providerClientId', + clientSecret: 'providerClientSecret', + issuerUrl: 'providerIssuerUrl', + attributeMapping: { + // These attributes are used by workbench-core-authentication package + email: ProviderAttribute.other('providerEmailAttributeValue'), + givenName: ProviderAttribute.other('providerGivenNameAttributeValue'), + familyName: ProviderAttribute.other('providerFamilyNameAttributeValue'), + } +} + +// Set the cognito properties +// If a user pool name or user pool client name are not provided, CDK will generate them for you +const cognitoProps: WorkbenchCognitoProps = { + domainPrefix: 'myDomainPrefix', + websiteUrls: ['https://mysite.mydomain.com'], + // Include your created OidcProvider if including a SSO component + oidcIdentityProviders = [ myWorkbenchUserPoolOidcIdentityProvider ] + // If an access token validity of 15 minutes is insufficient, you may change it here + accessTokenValidity: Duration.minutes(60) // 1 hour +}; + +this.myWorkbenchCognito = new WorkbenchCognito(this, 'my-workbench-cognito', cognitoProps); + +// ... +// The rest of your app/construct code +``` \ No newline at end of file From c8a98ca62014b38270267c5147ff40bc2d847199 Mon Sep 17 00:00:00 2001 From: Samir Sharan <11716061+insignias@users.noreply.github.com> Date: Tue, 23 Aug 2022 18:30:18 -0400 Subject: [PATCH 025/621] chore: add slack notification for Merge-develop-to-stage workflow failure (#476) * chore: test slack notification * chore: test 1 * chore: test 2 * chore: test 3 * chore: test 4 * chore: test 5 * chore: test 7 * chore: test 7 * chore: test 8 * chore: test 8 * chore: test 9 * chore: test 10 * chore: test 11 * chore: test 12 * chore: test 12 * chore: fix action * chore: test again * chore: test 12 * chore: final config * chore: cleanup * chore: test new * chore: test new 1 * chore: revert new test Co-authored-by: Samir Sharan --- .github/actions/notification/action.yml | 19 +++++++++++++++++++ .../deploy-integration-swb-reference.yml | 8 +++++++- .../workflows/deploy-swb-reference-demo.yml | 8 +++++++- .github/workflows/merge-develop-to-stage.yml | 8 +++++++- .github/workflows/readme-commit.yml | 8 +++++++- 5 files changed, 47 insertions(+), 4 deletions(-) create mode 100644 .github/actions/notification/action.yml diff --git a/.github/actions/notification/action.yml b/.github/actions/notification/action.yml new file mode 100644 index 0000000000..b56e9ebf24 --- /dev/null +++ b/.github/actions/notification/action.yml @@ -0,0 +1,19 @@ +# +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +name: Slack Notification +description: Action to send slack notification on failure +inputs: + slack_webhook_url: + description: "Slack WebHook URL" + required: true + +runs: + using: "composite" + steps: + - name: Slack Notification + shell: bash + run: | + curl -X POST -H 'Content-type: application/json' --data '{"text":" -> [ STATUS: ${{ job.status }} WORKFLOW_NAME: ${{ github.workflow }} JOB: ${{ github.job }} JOB_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} ] "}' ${{ inputs.slack_webhook_url }} \ No newline at end of file diff --git a/.github/workflows/deploy-integration-swb-reference.yml b/.github/workflows/deploy-integration-swb-reference.yml index 1611d54313..01e0790d86 100644 --- a/.github/workflows/deploy-integration-swb-reference.yml +++ b/.github/workflows/deploy-integration-swb-reference.yml @@ -57,4 +57,10 @@ jobs: # if: steps.changed-swb-files.outputs.any_changed == 'true' run: | cd solutions/swb-reference - STAGE='testEnv' node ../../common/scripts/install-run-rushx.js integration-tests \ No newline at end of file + STAGE='testEnv' node ../../common/scripts/install-run-rushx.js integration-tests + + - name: Slack Failure Notification + if: failure() + uses: ./.github/actions/notification + with: + slack_webhook_url: ${{ secrets.SLACK_WEBHOOK }} \ No newline at end of file diff --git a/.github/workflows/deploy-swb-reference-demo.yml b/.github/workflows/deploy-swb-reference-demo.yml index 764bfb40a0..c0558256c4 100644 --- a/.github/workflows/deploy-swb-reference-demo.yml +++ b/.github/workflows/deploy-swb-reference-demo.yml @@ -39,4 +39,10 @@ jobs: node ../../common/scripts/install-run-rushx.js compile STAGE='demo' node ../../common/scripts/install-run-rushx.js cdk bootstrap STAGE='demo' node ../../common/scripts/install-run-rushx.js cdk-deploy - STAGE='demo' node ../../common/scripts/install-run-rushx.js run-postDeployment \ No newline at end of file + STAGE='demo' node ../../common/scripts/install-run-rushx.js run-postDeployment + + - name: Slack Failure Notification + if: failure() + uses: ./.github/actions/notification + with: + slack_webhook_url: ${{ secrets.SLACK_WEBHOOK }} \ No newline at end of file diff --git a/.github/workflows/merge-develop-to-stage.yml b/.github/workflows/merge-develop-to-stage.yml index f330598bc3..b0b82df52d 100644 --- a/.github/workflows/merge-develop-to-stage.yml +++ b/.github/workflows/merge-develop-to-stage.yml @@ -109,4 +109,10 @@ jobs: repo: solution-spark-on-aws branch: stage env: - GITHUB_TOKEN: ${{ secrets.MERGE_TOKEN }} \ No newline at end of file + GITHUB_TOKEN: ${{ secrets.MERGE_TOKEN }} + + - name: Slack Failure Notification + if: failure() + uses: ./.github/actions/notification + with: + slack_webhook_url: ${{ secrets.SLACK_WEBHOOK }} \ No newline at end of file diff --git a/.github/workflows/readme-commit.yml b/.github/workflows/readme-commit.yml index a502569f5f..1141641f36 100644 --- a/.github/workflows/readme-commit.yml +++ b/.github/workflows/readme-commit.yml @@ -98,4 +98,10 @@ jobs: repo: solution-spark-on-aws branch: develop env: - GITHUB_TOKEN: ${{ secrets.merge-token }} \ No newline at end of file + GITHUB_TOKEN: ${{ secrets.merge-token }} + + - name: Slack Failure Notification + if: failure() + uses: ./.github/actions/notification + with: + slack_webhook_url: ${{ secrets.SLACK_WEBHOOK }} \ No newline at end of file From 01129ac2a0c606bbbf22a50eae6e59a3cbdfcc30 Mon Sep 17 00:00:00 2001 From: HaiTao Zhang Date: Tue, 23 Aug 2022 16:16:20 -0700 Subject: [PATCH 026/621] feat: add license-check-and-add to all packages. and automate adding license headers (#474) * feat: add license-check-and-add to all packages, automate adding license headers * chore: add changefiles * fix: change ~ to ^ --- .../check-and-add-license-header.yml | 84 ++++++++ .licenseignorefile | 4 + apache-license-header.txt | 2 + ...feat-addApacheHeader_2022-08-23-06-35.json | 10 + ...feat-addApacheHeader_2022-08-23-06-35.json | 10 + ...feat-addApacheHeader_2022-08-23-06-35.json | 10 + ...feat-addApacheHeader_2022-08-23-06-35.json | 10 + ...feat-addApacheHeader_2022-08-23-06-35.json | 10 + ...feat-addApacheHeader_2022-08-23-06-35.json | 10 + ...feat-addApacheHeader_2022-08-23-06-35.json | 10 + ...feat-addApacheHeader_2022-08-23-06-35.json | 10 + ...feat-addApacheHeader_2022-08-23-06-35.json | 10 + .../rush/browser-approved-packages.json | 4 + common/config/rush/command-line.json | 14 ++ common/config/rush/pnpm-lock.yaml | 200 ++++++++++++++++-- .../example-ui-app/license-add-config.json | 32 +++ solutions/example-ui-app/package.json | 5 +- solutions/swb-app/license-add-config.json | 32 +++ solutions/swb-app/package.json | 5 +- .../swb-reference/license-add-config.json | 32 +++ solutions/swb-reference/package.json | 5 +- .../infrastructure/license-add-config.json | 32 +++ solutions/swb-ui/infrastructure/package.json | 5 +- solutions/swb-ui/license-add-config.json | 32 +++ solutions/swb-ui/package.json | 5 +- workbench-core/audit/license-add-config.json | 32 +++ workbench-core/audit/package.json | 5 +- .../authentication/license-add-config.json | 32 +++ workbench-core/authentication/package.json | 5 +- .../authorization/license-add-config.json | 32 +++ workbench-core/authorization/package.json | 5 +- workbench-core/base/license-add-config.json | 32 +++ workbench-core/base/package.json | 5 +- .../datasets/license-add-config.json | 32 +++ workbench-core/datasets/package.json | 5 +- .../environments/license-add-config.json | 32 +++ workbench-core/environments/package.json | 5 +- .../eslint-custom/license-add-config.json | 32 +++ workbench-core/eslint-custom/package.json | 5 +- .../example/express/license-add-config.json | 32 +++ workbench-core/example/express/package.json | 5 +- .../infrastructure/license-add-config.json | 32 +++ .../example/infrastructure/package.json | 5 +- .../infrastructure/license-add-config.json | 32 +++ workbench-core/infrastructure/package.json | 5 +- .../logging/license-add-config.json | 32 +++ workbench-core/logging/package.json | 5 +- .../repo-toolbox/license-add-config.json | 32 +++ .../repo-scripts/repo-toolbox/package.json | 5 +- 49 files changed, 992 insertions(+), 35 deletions(-) create mode 100644 .github/workflows/check-and-add-license-header.yml create mode 100644 .licenseignorefile create mode 100644 apache-license-header.txt create mode 100644 common/changes/@aws/swb-app/feat-addApacheHeader_2022-08-23-06-35.json create mode 100644 common/changes/@aws/workbench-core-audit/feat-addApacheHeader_2022-08-23-06-35.json create mode 100644 common/changes/@aws/workbench-core-authentication/feat-addApacheHeader_2022-08-23-06-35.json create mode 100644 common/changes/@aws/workbench-core-authorization/feat-addApacheHeader_2022-08-23-06-35.json create mode 100644 common/changes/@aws/workbench-core-base/feat-addApacheHeader_2022-08-23-06-35.json create mode 100644 common/changes/@aws/workbench-core-datasets/feat-addApacheHeader_2022-08-23-06-35.json create mode 100644 common/changes/@aws/workbench-core-environments/feat-addApacheHeader_2022-08-23-06-35.json create mode 100644 common/changes/@aws/workbench-core-infrastructure/feat-addApacheHeader_2022-08-23-06-35.json create mode 100644 common/changes/@aws/workbench-core-logging/feat-addApacheHeader_2022-08-23-06-35.json create mode 100644 solutions/example-ui-app/license-add-config.json create mode 100644 solutions/swb-app/license-add-config.json create mode 100644 solutions/swb-reference/license-add-config.json create mode 100644 solutions/swb-ui/infrastructure/license-add-config.json create mode 100644 solutions/swb-ui/license-add-config.json create mode 100644 workbench-core/audit/license-add-config.json create mode 100644 workbench-core/authentication/license-add-config.json create mode 100644 workbench-core/authorization/license-add-config.json create mode 100644 workbench-core/base/license-add-config.json create mode 100644 workbench-core/datasets/license-add-config.json create mode 100644 workbench-core/environments/license-add-config.json create mode 100644 workbench-core/eslint-custom/license-add-config.json create mode 100644 workbench-core/example/express/license-add-config.json create mode 100644 workbench-core/example/infrastructure/license-add-config.json create mode 100644 workbench-core/infrastructure/license-add-config.json create mode 100644 workbench-core/logging/license-add-config.json create mode 100644 workbench-core/repo-scripts/repo-toolbox/license-add-config.json diff --git a/.github/workflows/check-and-add-license-header.yml b/.github/workflows/check-and-add-license-header.yml new file mode 100644 index 0000000000..0590f2df93 --- /dev/null +++ b/.github/workflows/check-and-add-license-header.yml @@ -0,0 +1,84 @@ +name: check-and-add-license-header +on: + pull_request: + types: [opened, synchronize, edited, reopened] + branches: + - develop + +jobs: + check-license-header: + runs-on: ubuntu-20.04 + outputs: + files-have-license-header: "${{ steps.check-license-header.outcome }}" + steps: + - uses: actions/checkout@v3 + with: + token: ${{secrets.MERGE_TOKEN}} + fetch-depth: 0 + - name: Base Action + uses: ./.github/actions/baseAction + - name: Check license header + id: check-license-header + run: node common/scripts/install-run-rush.js check-license-header + continue-on-error: true + add-license-header: + runs-on: ubuntu-20.04 + if: ${{ needs.check-license-header.outputs.files-have-license-header == 'failure'}} + needs: [check-license-header] + steps: + - uses: actions/checkout@v3 + with: + token: ${{secrets.MERGE_TOKEN}} + fetch-depth: 0 + + - name: Base Action + uses: ./.github/actions/baseAction + + - name: Add license header + run: node common/scripts/install-run-rush.js add-license-header + + - name: Import GPG key + uses: crazy-max/ghaction-import-gpg@v4 + with: + gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }} + passphrase: ${{ secrets.PASSPHRASE }} + git_user_signingkey: true + git_commit_gpgsign: true + + - name: Get branch name + id: branch-name + uses: tj-actions/branch-names@v5.1 + + # There's no way for github actions to push to a protected branch. This is a workaround + # See https://github.community/t/how-to-push-to-protected-branches-in-a-github-action/16101/30 + # Conditional on if branch is protected or not + - name: Temporaily disable branch protection on PR branch + if: ${{ github.ref_protected == true }} + uses: octokit/request-action@v2.x + with: + route: DELETE /repos/{owner}/{repo}/branches/{branch}/protection/enforce_admins + owner: awslabs + repo: solution-spark-on-aws + branch: ${{ steps.branch-name.outputs.head_ref_branch }} + env: + GITHUB_TOKEN: ${{ secrets.MERGE_TOKEN }} + + - name: Auto commit updated files with license headers + uses: stefanzweifel/git-auto-commit-action@v4 + with: + commit_message: "docs: Auto add license headers" + commit_options: '--no-verify --gpg-sign' + commit_user_name: ${{ secrets.BOT_USER }} + commit_user_email: ${{ secrets.BOT_USER_EMAIL }} + branch: ${{ steps.branch-name.outputs.head_ref_branch }} + + - name: Enable branch protection on PR branch + uses: octokit/request-action@v2.x + if: ${{ always() && github.ref_protected == true }} # Make sure to enable branch protection even if other steps fail + with: + route: POST /repos/{owner}/{repo}/branches/{branch}/protection/enforce_admins + owner: awslabs + repo: solution-spark-on-aws + branch: ${{ steps.branch-name.outputs.head_ref_branch }} + env: + GITHUB_TOKEN: ${{ secrets.MERGE_TOKEN }} diff --git a/.licenseignorefile b/.licenseignorefile new file mode 100644 index 0000000000..8af71eac6f --- /dev/null +++ b/.licenseignorefile @@ -0,0 +1,4 @@ +* + +!/src/**/*.ts +!/src/**/*.tsx \ No newline at end of file diff --git a/apache-license-header.txt b/apache-license-header.txt new file mode 100644 index 0000000000..478e610532 --- /dev/null +++ b/apache-license-header.txt @@ -0,0 +1,2 @@ +Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +SPDX-License-Identifier: Apache-2.0 \ No newline at end of file diff --git a/common/changes/@aws/swb-app/feat-addApacheHeader_2022-08-23-06-35.json b/common/changes/@aws/swb-app/feat-addApacheHeader_2022-08-23-06-35.json new file mode 100644 index 0000000000..839cd1ef3e --- /dev/null +++ b/common/changes/@aws/swb-app/feat-addApacheHeader_2022-08-23-06-35.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@aws/swb-app", + "comment": "add license-check-and-add to package.json", + "type": "none" + } + ], + "packageName": "@aws/swb-app" +} \ No newline at end of file diff --git a/common/changes/@aws/workbench-core-audit/feat-addApacheHeader_2022-08-23-06-35.json b/common/changes/@aws/workbench-core-audit/feat-addApacheHeader_2022-08-23-06-35.json new file mode 100644 index 0000000000..55e68435ff --- /dev/null +++ b/common/changes/@aws/workbench-core-audit/feat-addApacheHeader_2022-08-23-06-35.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@aws/workbench-core-audit", + "comment": "add license-check-and-add to package.json", + "type": "none" + } + ], + "packageName": "@aws/workbench-core-audit" +} \ No newline at end of file diff --git a/common/changes/@aws/workbench-core-authentication/feat-addApacheHeader_2022-08-23-06-35.json b/common/changes/@aws/workbench-core-authentication/feat-addApacheHeader_2022-08-23-06-35.json new file mode 100644 index 0000000000..45bdeb9ed2 --- /dev/null +++ b/common/changes/@aws/workbench-core-authentication/feat-addApacheHeader_2022-08-23-06-35.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@aws/workbench-core-authentication", + "comment": "add license-check-and-add to package.json", + "type": "none" + } + ], + "packageName": "@aws/workbench-core-authentication" +} \ No newline at end of file diff --git a/common/changes/@aws/workbench-core-authorization/feat-addApacheHeader_2022-08-23-06-35.json b/common/changes/@aws/workbench-core-authorization/feat-addApacheHeader_2022-08-23-06-35.json new file mode 100644 index 0000000000..cc1eaacd12 --- /dev/null +++ b/common/changes/@aws/workbench-core-authorization/feat-addApacheHeader_2022-08-23-06-35.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@aws/workbench-core-authorization", + "comment": "add license-check-and-add to package.json", + "type": "none" + } + ], + "packageName": "@aws/workbench-core-authorization" +} \ No newline at end of file diff --git a/common/changes/@aws/workbench-core-base/feat-addApacheHeader_2022-08-23-06-35.json b/common/changes/@aws/workbench-core-base/feat-addApacheHeader_2022-08-23-06-35.json new file mode 100644 index 0000000000..69d14c17cc --- /dev/null +++ b/common/changes/@aws/workbench-core-base/feat-addApacheHeader_2022-08-23-06-35.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@aws/workbench-core-base", + "comment": "add license-check-and-add to package.json", + "type": "none" + } + ], + "packageName": "@aws/workbench-core-base" +} \ No newline at end of file diff --git a/common/changes/@aws/workbench-core-datasets/feat-addApacheHeader_2022-08-23-06-35.json b/common/changes/@aws/workbench-core-datasets/feat-addApacheHeader_2022-08-23-06-35.json new file mode 100644 index 0000000000..e1c0da7c21 --- /dev/null +++ b/common/changes/@aws/workbench-core-datasets/feat-addApacheHeader_2022-08-23-06-35.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@aws/workbench-core-datasets", + "comment": "add license-check-and-add to package.json", + "type": "none" + } + ], + "packageName": "@aws/workbench-core-datasets" +} \ No newline at end of file diff --git a/common/changes/@aws/workbench-core-environments/feat-addApacheHeader_2022-08-23-06-35.json b/common/changes/@aws/workbench-core-environments/feat-addApacheHeader_2022-08-23-06-35.json new file mode 100644 index 0000000000..1492d9429a --- /dev/null +++ b/common/changes/@aws/workbench-core-environments/feat-addApacheHeader_2022-08-23-06-35.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@aws/workbench-core-environments", + "comment": "add license-check-and-add to package.json", + "type": "none" + } + ], + "packageName": "@aws/workbench-core-environments" +} \ No newline at end of file diff --git a/common/changes/@aws/workbench-core-infrastructure/feat-addApacheHeader_2022-08-23-06-35.json b/common/changes/@aws/workbench-core-infrastructure/feat-addApacheHeader_2022-08-23-06-35.json new file mode 100644 index 0000000000..10288246d6 --- /dev/null +++ b/common/changes/@aws/workbench-core-infrastructure/feat-addApacheHeader_2022-08-23-06-35.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@aws/workbench-core-infrastructure", + "comment": "add license-check-and-add to package.json", + "type": "none" + } + ], + "packageName": "@aws/workbench-core-infrastructure" +} \ No newline at end of file diff --git a/common/changes/@aws/workbench-core-logging/feat-addApacheHeader_2022-08-23-06-35.json b/common/changes/@aws/workbench-core-logging/feat-addApacheHeader_2022-08-23-06-35.json new file mode 100644 index 0000000000..846326e3a2 --- /dev/null +++ b/common/changes/@aws/workbench-core-logging/feat-addApacheHeader_2022-08-23-06-35.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@aws/workbench-core-logging", + "comment": "add license-check-and-add to package.json", + "type": "none" + } + ], + "packageName": "@aws/workbench-core-logging" +} \ No newline at end of file diff --git a/common/config/rush/browser-approved-packages.json b/common/config/rush/browser-approved-packages.json index 6a2cbcb3e2..cd1a4ed839 100644 --- a/common/config/rush/browser-approved-packages.json +++ b/common/config/rush/browser-approved-packages.json @@ -370,6 +370,10 @@ "name": "jwt-decode", "allowedCategories": [ "production" ] }, + { + "name": "license-check-and-add", + "allowedCategories": [ "production", "prototypes" ] + }, { "name": "license-checker", "allowedCategories": [ "production", "prototypes" ] diff --git a/common/config/rush/command-line.json b/common/config/rush/command-line.json index 7ab79816a5..db2c0d1e25 100644 --- a/common/config/rush/command-line.json +++ b/common/config/rush/command-line.json @@ -229,6 +229,20 @@ "safeForSimultaneousRushProcesses": true, "enableParallelism": true }, + { + "name": "add-license-header", + "commandKind": "bulk", + "summary": "Add license header", + "safeForSimultaneousRushProcesses": true, + "enableParallelism": true + }, + { + "name": "check-license-header", + "commandKind": "bulk", + "summary": "Check license header", + "safeForSimultaneousRushProcesses": true, + "enableParallelism": true + }, { "name": "common-coverage-report", "commandKind": "global", diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index fcb35fe246..4473b43775 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -35,7 +35,8 @@ importers: eslint-plugin-testing-library: 5.0.1 istanbul-badges-readme: 1.8.1 jest: ^27.5.1 - license-checker: ~25.0.1 + license-check-and-add: ^4.0.5 + license-checker: ^25.0.1 next: ^12.1.5 next-global-css: ^1.2.0 next-i18next: ^11.0.0 @@ -85,6 +86,7 @@ importers: eslint-plugin-testing-library: 5.0.1_eslint@8.16.0+typescript@4.7.2 istanbul-badges-readme: 1.8.1 jest: 27.5.1 + license-check-and-add: 4.0.5 license-checker: 25.0.1 npm-package-json-lint: 6.3.0 npm-package-json-lint-config-default: 5.0.0_npm-package-json-lint@6.3.0 @@ -125,7 +127,8 @@ importers: istanbul-badges-readme: 1.8.1 jest: ^27.5.1 jsonschema: ^1.4.1 - license-checker: ~25.0.1 + license-check-and-add: ^4.0.5 + license-checker: ^25.0.1 lodash: ^4.17.21 nodemon: ^2.0.19 npm-package-json-lint: ^6.3.0 @@ -167,6 +170,7 @@ importers: eslint-plugin-import: 2.26.0_eslint@8.16.0 istanbul-badges-readme: 1.8.1 jest: 27.5.1 + license-check-and-add: 4.0.5 license-checker: 25.0.1 nodemon: 2.0.19 npm-package-json-lint: 6.3.0 @@ -223,7 +227,8 @@ importers: jest: ^27.5.1 jest-circus: ^26.6.3 js-yaml: ^4.1.0 - license-checker: ~25.0.1 + license-check-and-add: ^4.0.5 + license-checker: ^25.0.1 lodash: ^4.17.21 npm-package-json-lint: ^6.3.0 npm-package-json-lint-config-default: ^5.0.0 @@ -282,6 +287,7 @@ importers: istanbul-badges-readme: 1.8.1 jest: 27.5.1_ts-node@10.8.0 jest-circus: 26.6.3_ts-node@10.8.0 + license-check-and-add: 4.0.5 license-checker: 25.0.1 npm-package-json-lint: 6.3.0 npm-package-json-lint-config-default: 5.0.0_npm-package-json-lint@6.3.0 @@ -329,7 +335,8 @@ importers: istanbul-badges-readme: 1.8.1 jest: ^27.5.1 jwt-decode: ^3.1.2 - license-checker: ~25.0.1 + license-check-and-add: ^4.0.5 + license-checker: ^25.0.1 next: ^12.1.5 next-global-css: ^1.2.0 next-transpile-modules: ^9.0.0 @@ -393,6 +400,7 @@ importers: eslint-plugin-testing-library: 5.0.1_eslint@8.16.0+typescript@4.7.2 istanbul-badges-readme: 1.8.1 jest: 27.5.1 + license-check-and-add: 4.0.5 license-checker: 25.0.1 nodemon: 2.0.19 npm-package-json-lint: 6.3.0 @@ -417,7 +425,8 @@ importers: constructs: ^10.0.0 jest: ^27.5.1 js-yaml: ^4.1.0 - license-checker: ~25.0.1 + license-check-and-add: ^4.0.5 + license-checker: ^25.0.1 sort-package-json: ^1.57.0 source-map-support: ^0.5.16 ts-jest: ^27.1.3 @@ -437,6 +446,7 @@ importers: aws-cdk-lib: 2.29.1_constructs@10.1.25 constructs: 10.1.25 jest: 27.5.1_ts-node@10.8.0 + license-check-and-add: 4.0.5 license-checker: 25.0.1 sort-package-json: 1.57.0 source-map-support: 0.5.21 @@ -460,7 +470,8 @@ importers: eslint: ^8.7.0 eslint-plugin-import: ^2.26.0 istanbul-badges-readme: 1.8.1 - license-checker: ~25.0.1 + license-check-and-add: ^4.0.5 + license-checker: ^25.0.1 lodash: ^4.17.21 npm-package-json-lint: ^6.3.0 npm-package-json-lint-config-default: ^5.0.0 @@ -483,6 +494,7 @@ importers: eslint: 8.16.0 eslint-plugin-import: 2.26.0_eslint@8.16.0 istanbul-badges-readme: 1.8.1 + license-check-and-add: 4.0.5 license-checker: 25.0.1 npm-package-json-lint: 6.3.0 npm-package-json-lint-config-default: 5.0.0_npm-package-json-lint@6.3.0 @@ -513,7 +525,8 @@ importers: eslint-plugin-import: ^2.26.0 istanbul-badges-readme: 1.8.1 jsonschema: ^1.4.1 - license-checker: ~25.0.1 + license-check-and-add: ^4.0.5 + license-checker: ^25.0.1 lodash: ^4.17.21 npm-package-json-lint: ^6.3.0 npm-package-json-lint-config-default: ^5.0.0 @@ -544,6 +557,7 @@ importers: eslint: 8.16.0 eslint-plugin-import: 2.26.0_eslint@8.16.0 istanbul-badges-readme: 1.8.1 + license-check-and-add: 4.0.5 license-checker: 25.0.1 npm-package-json-lint: 6.3.0 npm-package-json-lint-config-default: 5.0.0_npm-package-json-lint@6.3.0 @@ -570,7 +584,8 @@ importers: istanbul-badges-readme: 1.8.1 jest: ^27.5.1 jest-fast-check: ^1.0.2 - license-checker: ~25.0.1 + license-check-and-add: ^4.0.5 + license-checker: ^25.0.1 lodash: ^4.17.21 npm-package-json-lint: ^6.3.0 npm-package-json-lint-config-default: ^5.0.0 @@ -597,6 +612,7 @@ importers: istanbul-badges-readme: 1.8.1 jest: 27.5.1 jest-fast-check: 1.0.2_fast-check@2.25.0+jest@27.5.1 + license-check-and-add: 4.0.5 license-checker: 25.0.1 npm-package-json-lint: 6.3.0 npm-package-json-lint-config-default: 5.0.0_npm-package-json-lint@6.3.0 @@ -639,7 +655,8 @@ importers: eslint-plugin-import: ^2.26.0 istanbul-badges-readme: 1.8.1 jest: ^27.5.1 - license-checker: ~25.0.1 + license-check-and-add: ^4.0.5 + license-checker: ^25.0.1 lodash: ^4.17.21 npm-package-json-lint: ^6.3.0 npm-package-json-lint-config-default: ^5.0.0 @@ -683,6 +700,7 @@ importers: eslint-plugin-import: 2.26.0_eslint@8.16.0 istanbul-badges-readme: 1.8.1 jest: 27.5.1 + license-check-and-add: 4.0.5 license-checker: 25.0.1 npm-package-json-lint: 6.3.0 npm-package-json-lint-config-default: 5.0.0_npm-package-json-lint@6.3.0 @@ -721,7 +739,8 @@ importers: jest: ^27.5.1 jest-fast-check: ^1.0.2 jsonschema: ^1.4.1 - license-checker: ~25.0.1 + license-check-and-add: ^4.0.5 + license-checker: ^25.0.1 lodash: ^4.17.21 npm-package-json-lint: ^6.3.0 npm-package-json-lint-config-default: ^5.0.0 @@ -762,6 +781,7 @@ importers: istanbul-badges-readme: 1.8.1 jest: 27.5.1 jest-fast-check: 1.0.2_fast-check@2.25.0+jest@27.5.1 + license-check-and-add: 4.0.5 license-checker: 25.0.1 npm-package-json-lint: 6.3.0 npm-package-json-lint-config-default: 5.0.0_npm-package-json-lint@6.3.0 @@ -809,7 +829,8 @@ importers: istanbul-badges-readme: 1.8.1 jest: ^27.5.1 jsonschema: ^1.4.1 - license-checker: ~25.0.1 + license-check-and-add: ^4.0.5 + license-checker: ^25.0.1 lodash: ^4.17.21 md5-file: ^5.0.0 npm-package-json-lint: ^6.3.0 @@ -861,6 +882,7 @@ importers: eslint-plugin-import: 2.26.0_eslint@8.16.0 istanbul-badges-readme: 1.8.1 jest: 27.5.1 + license-check-and-add: 4.0.5 license-checker: 25.0.1 npm-package-json-lint: 6.3.0 npm-package-json-lint-config-default: 5.0.0_npm-package-json-lint@6.3.0 @@ -880,7 +902,8 @@ importers: eslint: ^8.7.0 eslint-plugin-import: ^2.26.0 eslint-plugin-security: ^1.4.0 - license-checker: ~25.0.1 + license-check-and-add: ^4.0.5 + license-checker: ^25.0.1 npm-package-json-lint: ^6.3.0 npm-package-json-lint-config-default: ^5.0.0 sort-package-json: ^1.57.0 @@ -897,6 +920,7 @@ importers: '@types/heft-jest': 1.0.2 '@types/node': 14.18.20 depcheck: 1.4.3 + license-check-and-add: 4.0.5 license-checker: 25.0.1 npm-package-json-lint: 6.3.0 npm-package-json-lint-config-default: 5.0.0_npm-package-json-lint@6.3.0 @@ -920,7 +944,8 @@ importers: eslint-plugin-import: ^2.26.0 eslint-plugin-security: ^1.4.0 express: ^4.17.3 - license-checker: ~25.0.1 + license-check-and-add: ^4.0.5 + license-checker: ^25.0.1 npm-package-json-lint: ^6.3.0 npm-package-json-lint-config-default: ^5.0.0 sort-package-json: ^1.57.0 @@ -942,6 +967,7 @@ importers: eslint-plugin-import: 2.26.0_eslint@8.16.0 eslint-plugin-security: 1.5.0 express: 4.18.1 + license-check-and-add: 4.0.5 license-checker: 25.0.1 npm-package-json-lint: 6.3.0 npm-package-json-lint-config-default: 5.0.0_npm-package-json-lint@6.3.0 @@ -978,7 +1004,8 @@ importers: express: ^4.17.3 istanbul-badges-readme: 1.8.1 jest: ^27.5.1 - license-checker: ~25.0.1 + license-check-and-add: ^4.0.5 + license-checker: ^25.0.1 lodash: ^4.17.21 npm-package-json-lint: ^6.3.0 npm-package-json-lint-config-default: ^5.0.0 @@ -1017,6 +1044,7 @@ importers: express: 4.18.1 istanbul-badges-readme: 1.8.1 jest: 27.5.1 + license-check-and-add: 4.0.5 license-checker: 25.0.1 npm-package-json-lint: 6.3.0 npm-package-json-lint-config-default: 5.0.0_npm-package-json-lint@6.3.0 @@ -1040,7 +1068,8 @@ importers: eslint-plugin-import: ^2.26.0 eslint-plugin-security: ^1.4.0 istanbul-badges-readme: 1.8.1 - license-checker: ~25.0.1 + license-check-and-add: ^4.0.5 + license-checker: ^25.0.1 lodash: ^4.17.21 npm-package-json-lint: ^6.3.0 npm-package-json-lint-config-default: ^5.0.0 @@ -1064,6 +1093,7 @@ importers: eslint-plugin-import: 2.26.0_eslint@8.16.0 eslint-plugin-security: 1.5.0 istanbul-badges-readme: 1.8.1 + license-check-and-add: 4.0.5 license-checker: 25.0.1 npm-package-json-lint: 6.3.0 npm-package-json-lint-config-default: 5.0.0_npm-package-json-lint@6.3.0 @@ -1086,7 +1116,8 @@ importers: istanbul-badges-readme: 1.8.1 jest: ^27.5.1 jest-fast-check: ^1.0.2 - license-checker: ~25.0.1 + license-check-and-add: ^4.0.5 + license-checker: ^25.0.1 npm-package-json-lint: ^6.3.0 npm-package-json-lint-config-default: ^5.0.0 sort-package-json: ^1.57.0 @@ -1113,6 +1144,7 @@ importers: istanbul-badges-readme: 1.8.1 jest: 27.5.1 jest-fast-check: 1.0.2_fast-check@2.25.0+jest@27.5.1 + license-check-and-add: 4.0.5 license-checker: 25.0.1 npm-package-json-lint: 6.3.0 npm-package-json-lint-config-default: 5.0.0_npm-package-json-lint@6.3.0 @@ -1137,7 +1169,8 @@ importers: eslint: ^8.7.0 eslint-plugin-import: ^2.26.0 eslint-plugin-security: ^1.4.0 - license-checker: ~25.0.1 + license-check-and-add: ^4.0.5 + license-checker: ^25.0.1 npm-package-json-lint: ^6.3.0 npm-package-json-lint-config-default: ^5.0.0 sort-package-json: ^1.57.0 @@ -1160,6 +1193,7 @@ importers: eslint: 8.16.0 eslint-plugin-import: 2.26.0_eslint@8.16.0 eslint-plugin-security: 1.5.0 + license-check-and-add: 4.0.5 license-checker: 25.0.1 npm-package-json-lint: 6.3.0 npm-package-json-lint-config-default: 5.0.0_npm-package-json-lint@6.3.0 @@ -7193,6 +7227,11 @@ packages: engines: {node: '>=0.10.0'} dev: true + /ansi-regex/4.1.1: + resolution: {integrity: sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==} + engines: {node: '>=6'} + dev: true + /ansi-regex/5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} @@ -7978,6 +8017,14 @@ packages: engines: {node: '>= 10'} dev: false + /cliui/5.0.0: + resolution: {integrity: sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==} + dependencies: + string-width: 3.1.0 + strip-ansi: 5.2.0 + wrap-ansi: 5.1.0 + dev: true + /cliui/6.0.0: resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==} dependencies: @@ -8608,6 +8655,10 @@ packages: engines: {node: '>=10'} dev: true + /emoji-regex/7.0.3: + resolution: {integrity: sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==} + dev: true + /emoji-regex/8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -9571,6 +9622,13 @@ packages: dependencies: locate-path: 2.0.0 + /find-up/3.0.0: + resolution: {integrity: sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==} + engines: {node: '>=6'} + dependencies: + locate-path: 3.0.0 + dev: true + /find-up/4.1.0: resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} engines: {node: '>=8'} @@ -9657,6 +9715,15 @@ packages: jsonfile: 4.0.0 universalify: 0.1.2 + /fs-extra/8.1.0: + resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} + engines: {node: '>=6 <7 || >=8'} + dependencies: + graceful-fs: 4.2.10 + jsonfile: 4.0.0 + universalify: 0.1.2 + dev: true + /fs-extra/9.1.0: resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==} engines: {node: '>=10'} @@ -9787,6 +9854,11 @@ packages: resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==} dev: true + /gitignore-to-glob/0.3.0: + resolution: {integrity: sha512-mk74BdnK7lIwDHnotHddx1wsjMOFIThpLY3cPNniJ/2fA/tlLzHnFxIdR+4sLOu5KGgQJdij4kjJ2RoUNnCNMA==} + engines: {node: '>=4.4 <5 || >=6.9'} + dev: true + /glob-escape/0.0.2: resolution: {integrity: sha512-L/cXYz8x7qer1HAyUQ+mbjcUsJVdpRxpAf7CwqHoNBs9vTpABlGfNN4tzkDxt+u3Z7ZncVyKlCNPtzb0R/7WbA==} engines: {node: '>= 0.10'} @@ -9863,6 +9935,20 @@ packages: slash: 3.0.0 dev: true + /globby/10.0.2: + resolution: {integrity: sha512-7dUi7RvCoT/xast/o/dLN53oqND4yk0nsHkhRgn9w65C4PofCLOoJ39iSOg+qVDdWQPIEj+eszMHQ+aLVwwQSg==} + engines: {node: '>=8'} + dependencies: + '@types/glob': 7.2.0 + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.2.11 + glob: 7.2.3 + ignore: 5.2.0 + merge2: 1.4.1 + slash: 3.0.0 + dev: true + /globby/11.1.0: resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} engines: {node: '>=10'} @@ -10274,6 +10360,11 @@ packages: number-is-nan: 1.0.1 dev: true + /is-fullwidth-code-point/2.0.0: + resolution: {integrity: sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==} + engines: {node: '>=4'} + dev: true + /is-fullwidth-code-point/3.0.0: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} @@ -11802,6 +11893,17 @@ packages: prelude-ls: 1.2.1 type-check: 0.4.0 + /license-check-and-add/4.0.5: + resolution: {integrity: sha512-FySnMi3Kf/vO5jka8tcbVF1FhDFb8PWsQ8pg5Y7U/zkQgta+fIrJGcGHO58WFjfKlgvhneG1uQ00Fpxzhau3QA==} + hasBin: true + dependencies: + fs-extra: 8.1.0 + gitignore-to-glob: 0.3.0 + globby: 10.0.2 + ignore: 5.2.0 + yargs: 13.3.2 + dev: true + /license-checker/25.0.1: resolution: {integrity: sha512-mET5AIwl7MR2IAKYYoVBBpV0OnkKQ1xGj2IMMeEFIs42QAkEVjRtFZGWmQ28WeU7MP779iAgOaOy93Mn44mn6g==} hasBin: true @@ -11844,6 +11946,14 @@ packages: p-locate: 2.0.0 path-exists: 3.0.0 + /locate-path/3.0.0: + resolution: {integrity: sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==} + engines: {node: '>=6'} + dependencies: + p-locate: 3.0.0 + path-exists: 3.0.0 + dev: true + /locate-path/5.0.0: resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} engines: {node: '>=8'} @@ -12639,6 +12749,13 @@ packages: dependencies: p-limit: 1.3.0 + /p-locate/3.0.0: + resolution: {integrity: sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==} + engines: {node: '>=6'} + dependencies: + p-limit: 2.3.0 + dev: true + /p-locate/4.1.0: resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} engines: {node: '>=8'} @@ -13523,7 +13640,7 @@ packages: send: 0.18.0 /set-blocking/2.0.0: - resolution: {integrity: sha1-BF+XgtARrppoA93TgrJDkrPYkPc=} + resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} dev: true /set-immediate-shim/1.0.1: @@ -13863,6 +13980,15 @@ packages: strip-ansi: 3.0.1 dev: true + /string-width/3.1.0: + resolution: {integrity: sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==} + engines: {node: '>=6'} + dependencies: + emoji-regex: 7.0.3 + is-fullwidth-code-point: 2.0.0 + strip-ansi: 5.2.0 + dev: true + /string-width/4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} @@ -13914,6 +14040,13 @@ packages: ansi-regex: 2.1.1 dev: true + /strip-ansi/5.2.0: + resolution: {integrity: sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==} + engines: {node: '>=6'} + dependencies: + ansi-regex: 4.1.1 + dev: true + /strip-ansi/6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} @@ -14810,6 +14943,15 @@ packages: resolution: {integrity: sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=} dev: false + /wrap-ansi/5.1.0: + resolution: {integrity: sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==} + engines: {node: '>=6'} + dependencies: + ansi-styles: 3.2.1 + string-width: 3.1.0 + strip-ansi: 5.2.0 + dev: true + /wrap-ansi/6.2.0: resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} engines: {node: '>=8'} @@ -14909,6 +15051,13 @@ packages: resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} engines: {node: '>= 6'} + /yargs-parser/13.1.2: + resolution: {integrity: sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==} + dependencies: + camelcase: 5.3.1 + decamelize: 1.2.0 + dev: true + /yargs-parser/18.1.3: resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==} engines: {node: '>=6'} @@ -14927,6 +15076,21 @@ packages: engines: {node: '>=12'} dev: true + /yargs/13.3.2: + resolution: {integrity: sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==} + dependencies: + cliui: 5.0.0 + find-up: 3.0.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + require-main-filename: 2.0.0 + set-blocking: 2.0.0 + string-width: 3.1.0 + which-module: 2.0.0 + y18n: 4.0.3 + yargs-parser: 13.1.2 + dev: true + /yargs/15.4.1: resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==} engines: {node: '>=8'} diff --git a/solutions/example-ui-app/license-add-config.json b/solutions/example-ui-app/license-add-config.json new file mode 100644 index 0000000000..ed85c268a8 --- /dev/null +++ b/solutions/example-ui-app/license-add-config.json @@ -0,0 +1,32 @@ +{ + "license": "../../apache-license-header.txt", + "ignoreFile": "../../.licenseignorefile", + "defaultFormat": { + "gitignore|npmignore|eslintignore|dockerignore|sh|py": { + "eachLine": { + "prepend": "# " + } + }, + "html|xml|svg": { + "prepend": "" + }, + "js|ts|tsx|css|scss|less|php|as|c|java|cpp|go|cto|acl": { + "prepend": "/*", + "append": " */\n", + "eachLine": { + "prepend": " * " + } + }, + "txt": {} + }, + "licenseFormats": { + "ts|tsx": { + "prepend": "/*", + "append": " */\n", + "eachLine": { + "prepend": " * " + } + } + } +} diff --git a/solutions/example-ui-app/package.json b/solutions/example-ui-app/package.json index 0c04c43e70..ceac5fd42d 100644 --- a/solutions/example-ui-app/package.json +++ b/solutions/example-ui-app/package.json @@ -13,8 +13,10 @@ "url": "http://aws.amazon.com" }, "scripts": { + "add-license-header": "license-check-and-add add -f license-add-config.json", "build": "next build && rushx lint", "build:test": "rushx build && rushx test", + "check-license-header": "license-check-and-add check -f license-add-config.json", "depcheck": "depcheck", "dev": "next dev", "license-checker": "license-checker --onlyAllow 'MIT; Apache-2.0; ISC; BSD'", @@ -65,7 +67,8 @@ "eslint-plugin-testing-library": "5.0.1", "istanbul-badges-readme": "1.8.1", "jest": "^27.5.1", - "license-checker": "~25.0.1", + "license-check-and-add": "^4.0.5", + "license-checker": "^25.0.1", "npm-package-json-lint": "^6.3.0", "npm-package-json-lint-config-default": "^5.0.0", "sass": "^1.49.8", diff --git a/solutions/swb-app/license-add-config.json b/solutions/swb-app/license-add-config.json new file mode 100644 index 0000000000..ed85c268a8 --- /dev/null +++ b/solutions/swb-app/license-add-config.json @@ -0,0 +1,32 @@ +{ + "license": "../../apache-license-header.txt", + "ignoreFile": "../../.licenseignorefile", + "defaultFormat": { + "gitignore|npmignore|eslintignore|dockerignore|sh|py": { + "eachLine": { + "prepend": "# " + } + }, + "html|xml|svg": { + "prepend": "" + }, + "js|ts|tsx|css|scss|less|php|as|c|java|cpp|go|cto|acl": { + "prepend": "/*", + "append": " */\n", + "eachLine": { + "prepend": " * " + } + }, + "txt": {} + }, + "licenseFormats": { + "ts|tsx": { + "prepend": "/*", + "append": " */\n", + "eachLine": { + "prepend": " * " + } + } + } +} diff --git a/solutions/swb-app/package.json b/solutions/swb-app/package.json index 01f0569283..6f8e79e132 100644 --- a/solutions/swb-app/package.json +++ b/solutions/swb-app/package.json @@ -15,8 +15,10 @@ "main": "lib/index.js", "typings": "lib/index.d.ts", "scripts": { + "add-license-header": "license-check-and-add add -f license-add-config.json", "build": "heft build --clean && rushx pkg-json-lint", "build:test": "heft test --clean && rushx pkg-json-lint && rushx make-badges", + "check-license-header": "license-check-and-add check -f license-add-config.json", "depcheck": "depcheck", "license-checker": "license-checker --onlyAllow 'MIT; Apache-2.0; ISC; BSD'", "lint:fix": "eslint . --fix", @@ -62,7 +64,8 @@ "eslint-plugin-import": "^2.26.0", "istanbul-badges-readme": "1.8.1", "jest": "^27.5.1", - "license-checker": "~25.0.1", + "license-check-and-add": "^4.0.5", + "license-checker": "^25.0.1", "nodemon": "^2.0.19", "npm-package-json-lint": "^6.3.0", "npm-package-json-lint-config-default": "^5.0.0", diff --git a/solutions/swb-reference/license-add-config.json b/solutions/swb-reference/license-add-config.json new file mode 100644 index 0000000000..ed85c268a8 --- /dev/null +++ b/solutions/swb-reference/license-add-config.json @@ -0,0 +1,32 @@ +{ + "license": "../../apache-license-header.txt", + "ignoreFile": "../../.licenseignorefile", + "defaultFormat": { + "gitignore|npmignore|eslintignore|dockerignore|sh|py": { + "eachLine": { + "prepend": "# " + } + }, + "html|xml|svg": { + "prepend": "" + }, + "js|ts|tsx|css|scss|less|php|as|c|java|cpp|go|cto|acl": { + "prepend": "/*", + "append": " */\n", + "eachLine": { + "prepend": " * " + } + }, + "txt": {} + }, + "licenseFormats": { + "ts|tsx": { + "prepend": "/*", + "append": " */\n", + "eachLine": { + "prepend": " * " + } + } + } +} diff --git a/solutions/swb-reference/package.json b/solutions/swb-reference/package.json index 860c4045bb..a20e437a44 100644 --- a/solutions/swb-reference/package.json +++ b/solutions/swb-reference/package.json @@ -13,11 +13,13 @@ "url": "http://aws.amazon.com" }, "scripts": { + "add-license-header": "license-check-and-add add -f license-add-config.json", "build": "heft build --clean && rushx pkg-json-lint", "build:test": "heft test --clean && rushx pkg-json-lint && rushx make-badges", "cdk": "cdk", "cdk-deploy": "rush build && rushx compile && cdk deploy --all --require-approval never --outputs-file ./src/config/${STAGE}.json", "cdk-deploy:noBuild": "rushx compile && cdk deploy --all", + "check-license-header": "license-check-and-add check -f license-add-config.json", "compile": "node scripts/buildLambda.js", "depcheck": "depcheck", "integration-tests": "rushx build && jest lib/integration-tests", @@ -89,7 +91,8 @@ "istanbul-badges-readme": "1.8.1", "jest": "^27.5.1", "jest-circus": "^26.6.3", - "license-checker": "~25.0.1", + "license-check-and-add": "^4.0.5", + "license-checker": "^25.0.1", "npm-package-json-lint": "^6.3.0", "npm-package-json-lint-config-default": "^5.0.0", "sort-package-json": "^1.57.0", diff --git a/solutions/swb-ui/infrastructure/license-add-config.json b/solutions/swb-ui/infrastructure/license-add-config.json new file mode 100644 index 0000000000..973a3df987 --- /dev/null +++ b/solutions/swb-ui/infrastructure/license-add-config.json @@ -0,0 +1,32 @@ +{ + "license": "../../../apache-license-header.txt", + "ignoreFile": "../../../.licenseignorefile", + "defaultFormat": { + "gitignore|npmignore|eslintignore|dockerignore|sh|py": { + "eachLine": { + "prepend": "# " + } + }, + "html|xml|svg": { + "prepend": "" + }, + "js|ts|tsx|css|scss|less|php|as|c|java|cpp|go|cto|acl": { + "prepend": "/*", + "append": " */\n", + "eachLine": { + "prepend": " * " + } + }, + "txt": {} + }, + "licenseFormats": { + "ts|tsx": { + "prepend": "/*", + "append": " */\n", + "eachLine": { + "prepend": " * " + } + } + } +} diff --git a/solutions/swb-ui/infrastructure/package.json b/solutions/swb-ui/infrastructure/package.json index 9636107eff..ef3c336242 100644 --- a/solutions/swb-ui/infrastructure/package.json +++ b/solutions/swb-ui/infrastructure/package.json @@ -16,10 +16,12 @@ "cdk-infrastructure": "bin/cdk-infrastructure.js" }, "scripts": { + "add-license-header": "license-check-and-add add -f license-add-config.json", "build": "heft build --clean", "build:test": "rushx build && rushx test", "cdk": "cdk", "cdk-deploy": "rushx build && cdk deploy --all --require-approval never --outputs-file ./src/config/${STAGE}.json", + "check-license-header": "license-check-and-add check -f license-add-config.json", "license-checker": "license-checker --onlyAllow 'MIT; Apache-2.0; ISC; BSD'", "sort-package-json": "sort-package-json package.json", "test": "rushx test:only", @@ -41,7 +43,8 @@ "aws-cdk-lib": "^2.29.1", "constructs": "^10.0.0", "jest": "^27.5.1", - "license-checker": "~25.0.1", + "license-check-and-add": "^4.0.5", + "license-checker": "^25.0.1", "sort-package-json": "^1.57.0", "source-map-support": "^0.5.16", "ts-jest": "^27.1.3", diff --git a/solutions/swb-ui/license-add-config.json b/solutions/swb-ui/license-add-config.json new file mode 100644 index 0000000000..ed85c268a8 --- /dev/null +++ b/solutions/swb-ui/license-add-config.json @@ -0,0 +1,32 @@ +{ + "license": "../../apache-license-header.txt", + "ignoreFile": "../../.licenseignorefile", + "defaultFormat": { + "gitignore|npmignore|eslintignore|dockerignore|sh|py": { + "eachLine": { + "prepend": "# " + } + }, + "html|xml|svg": { + "prepend": "" + }, + "js|ts|tsx|css|scss|less|php|as|c|java|cpp|go|cto|acl": { + "prepend": "/*", + "append": " */\n", + "eachLine": { + "prepend": " * " + } + }, + "txt": {} + }, + "licenseFormats": { + "ts|tsx": { + "prepend": "/*", + "append": " */\n", + "eachLine": { + "prepend": " * " + } + } + } +} diff --git a/solutions/swb-ui/package.json b/solutions/swb-ui/package.json index abb7718f6b..38eb30cf66 100644 --- a/solutions/swb-ui/package.json +++ b/solutions/swb-ui/package.json @@ -15,8 +15,10 @@ "main": "lib/index.js", "typings": "lib/index.d.ts", "scripts": { + "add-license-header": "license-check-and-add add -f license-add-config.json", "build": "next build && rushx lint", "build:test": "rushx build && rushx test", + "check-license-header": "license-check-and-add check -f license-add-config.json", "depcheck": "depcheck", "deploy-ui-and-api": "bash ./scripts/deploy.sh", "dev": "bash ./scripts/dev.sh", @@ -79,7 +81,8 @@ "eslint-plugin-testing-library": "5.0.1", "istanbul-badges-readme": "1.8.1", "jest": "^27.5.1", - "license-checker": "~25.0.1", + "license-check-and-add": "^4.0.5", + "license-checker": "^25.0.1", "nodemon": "^2.0.19", "npm-package-json-lint": "^6.3.0", "npm-package-json-lint-config-default": "^5.0.0", diff --git a/workbench-core/audit/license-add-config.json b/workbench-core/audit/license-add-config.json new file mode 100644 index 0000000000..ed85c268a8 --- /dev/null +++ b/workbench-core/audit/license-add-config.json @@ -0,0 +1,32 @@ +{ + "license": "../../apache-license-header.txt", + "ignoreFile": "../../.licenseignorefile", + "defaultFormat": { + "gitignore|npmignore|eslintignore|dockerignore|sh|py": { + "eachLine": { + "prepend": "# " + } + }, + "html|xml|svg": { + "prepend": "" + }, + "js|ts|tsx|css|scss|less|php|as|c|java|cpp|go|cto|acl": { + "prepend": "/*", + "append": " */\n", + "eachLine": { + "prepend": " * " + } + }, + "txt": {} + }, + "licenseFormats": { + "ts|tsx": { + "prepend": "/*", + "append": " */\n", + "eachLine": { + "prepend": " * " + } + } + } +} diff --git a/workbench-core/audit/package.json b/workbench-core/audit/package.json index bc0cfdbd7f..87a535e5e0 100644 --- a/workbench-core/audit/package.json +++ b/workbench-core/audit/package.json @@ -15,8 +15,10 @@ "main": "lib/index.js", "typings": "lib/index.d.ts", "scripts": { + "add-license-header": "license-check-and-add add -f license-add-config.json", "build": "heft build --clean && rushx pkg-json-lint", "build:test": "heft test --clean && rushx pkg-json-lint && rushx make-badges", + "check-license-header": "license-check-and-add check -f license-add-config.json", "depcheck": "depcheck", "license-checker": "license-checker --onlyAllow 'MIT; Apache-2.0; ISC; BSD'", "lint:fix": "eslint . --fix", @@ -44,7 +46,8 @@ "eslint": "^8.7.0", "eslint-plugin-import": "^2.26.0", "istanbul-badges-readme": "1.8.1", - "license-checker": "~25.0.1", + "license-check-and-add": "^4.0.5", + "license-checker": "^25.0.1", "npm-package-json-lint": "^6.3.0", "npm-package-json-lint-config-default": "^5.0.0", "sort-package-json": "^1.57.0", diff --git a/workbench-core/authentication/license-add-config.json b/workbench-core/authentication/license-add-config.json new file mode 100644 index 0000000000..ed85c268a8 --- /dev/null +++ b/workbench-core/authentication/license-add-config.json @@ -0,0 +1,32 @@ +{ + "license": "../../apache-license-header.txt", + "ignoreFile": "../../.licenseignorefile", + "defaultFormat": { + "gitignore|npmignore|eslintignore|dockerignore|sh|py": { + "eachLine": { + "prepend": "# " + } + }, + "html|xml|svg": { + "prepend": "" + }, + "js|ts|tsx|css|scss|less|php|as|c|java|cpp|go|cto|acl": { + "prepend": "/*", + "append": " */\n", + "eachLine": { + "prepend": " * " + } + }, + "txt": {} + }, + "licenseFormats": { + "ts|tsx": { + "prepend": "/*", + "append": " */\n", + "eachLine": { + "prepend": " * " + } + } + } +} diff --git a/workbench-core/authentication/package.json b/workbench-core/authentication/package.json index 0cf97e2df9..48860476cb 100644 --- a/workbench-core/authentication/package.json +++ b/workbench-core/authentication/package.json @@ -15,8 +15,10 @@ "main": "lib/index.js", "typings": "lib/index.d.ts", "scripts": { + "add-license-header": "license-check-and-add add -f license-add-config.json", "build": "heft build --clean && rushx pkg-json-lint", "build:test": "heft test --clean && rushx pkg-json-lint && rushx make-badges", + "check-license-header": "license-check-and-add check -f license-add-config.json", "depcheck": "depcheck", "license-checker": "license-checker --onlyAllow 'MIT; Apache-2.0; ISC; BSD'", "lint:fix": "eslint . --fix", @@ -52,7 +54,8 @@ "eslint": "^8.7.0", "eslint-plugin-import": "^2.26.0", "istanbul-badges-readme": "1.8.1", - "license-checker": "~25.0.1", + "license-check-and-add": "^4.0.5", + "license-checker": "^25.0.1", "npm-package-json-lint": "^6.3.0", "npm-package-json-lint-config-default": "^5.0.0", "sort-package-json": "^1.57.0", diff --git a/workbench-core/authorization/license-add-config.json b/workbench-core/authorization/license-add-config.json new file mode 100644 index 0000000000..ed85c268a8 --- /dev/null +++ b/workbench-core/authorization/license-add-config.json @@ -0,0 +1,32 @@ +{ + "license": "../../apache-license-header.txt", + "ignoreFile": "../../.licenseignorefile", + "defaultFormat": { + "gitignore|npmignore|eslintignore|dockerignore|sh|py": { + "eachLine": { + "prepend": "# " + } + }, + "html|xml|svg": { + "prepend": "" + }, + "js|ts|tsx|css|scss|less|php|as|c|java|cpp|go|cto|acl": { + "prepend": "/*", + "append": " */\n", + "eachLine": { + "prepend": " * " + } + }, + "txt": {} + }, + "licenseFormats": { + "ts|tsx": { + "prepend": "/*", + "append": " */\n", + "eachLine": { + "prepend": " * " + } + } + } +} diff --git a/workbench-core/authorization/package.json b/workbench-core/authorization/package.json index f288fec26e..21dfb1c9aa 100644 --- a/workbench-core/authorization/package.json +++ b/workbench-core/authorization/package.json @@ -15,8 +15,10 @@ "main": "lib/index.js", "typings": "lib/index.d.ts", "scripts": { + "add-license-header": "license-check-and-add add -f license-add-config.json", "build": "heft build --clean && rushx pkg-json-lint", "build:test": "heft test --clean && rushx pkg-json-lint && rushx make-badges", + "check-license-header": "license-check-and-add check -f license-add-config.json", "depcheck": "depcheck", "license-checker": "license-checker --onlyAllow 'MIT; Apache-2.0; ISC; BSD'", "lint:fix": "eslint . --fix", @@ -48,7 +50,8 @@ "istanbul-badges-readme": "1.8.1", "jest": "^27.5.1", "jest-fast-check": "^1.0.2", - "license-checker": "~25.0.1", + "license-check-and-add": "^4.0.5", + "license-checker": "^25.0.1", "npm-package-json-lint": "^6.3.0", "npm-package-json-lint-config-default": "^5.0.0", "sort-package-json": "^1.57.0", diff --git a/workbench-core/base/license-add-config.json b/workbench-core/base/license-add-config.json new file mode 100644 index 0000000000..ed85c268a8 --- /dev/null +++ b/workbench-core/base/license-add-config.json @@ -0,0 +1,32 @@ +{ + "license": "../../apache-license-header.txt", + "ignoreFile": "../../.licenseignorefile", + "defaultFormat": { + "gitignore|npmignore|eslintignore|dockerignore|sh|py": { + "eachLine": { + "prepend": "# " + } + }, + "html|xml|svg": { + "prepend": "" + }, + "js|ts|tsx|css|scss|less|php|as|c|java|cpp|go|cto|acl": { + "prepend": "/*", + "append": " */\n", + "eachLine": { + "prepend": " * " + } + }, + "txt": {} + }, + "licenseFormats": { + "ts|tsx": { + "prepend": "/*", + "append": " */\n", + "eachLine": { + "prepend": " * " + } + } + } +} diff --git a/workbench-core/base/package.json b/workbench-core/base/package.json index 39ebb5d379..801317d9f4 100644 --- a/workbench-core/base/package.json +++ b/workbench-core/base/package.json @@ -15,8 +15,10 @@ "main": "lib/index.js", "typings": "lib/index.d.ts", "scripts": { + "add-license-header": "license-check-and-add add -f license-add-config.json", "build": "heft build --clean && rushx pkg-json-lint", "build:test": "heft test --clean && rushx pkg-json-lint && rushx make-badges", + "check-license-header": "license-check-and-add check -f license-add-config.json", "depcheck": "depcheck", "jest": "jest", "license-checker": "license-checker --onlyAllow 'MIT; Apache-2.0; ISC; BSD'", @@ -75,7 +77,8 @@ "eslint-plugin-import": "^2.26.0", "istanbul-badges-readme": "1.8.1", "jest": "^27.5.1", - "license-checker": "~25.0.1", + "license-check-and-add": "^4.0.5", + "license-checker": "^25.0.1", "npm-package-json-lint": "^6.3.0", "npm-package-json-lint-config-default": "^5.0.0", "sort-package-json": "^1.57.0", diff --git a/workbench-core/datasets/license-add-config.json b/workbench-core/datasets/license-add-config.json new file mode 100644 index 0000000000..ed85c268a8 --- /dev/null +++ b/workbench-core/datasets/license-add-config.json @@ -0,0 +1,32 @@ +{ + "license": "../../apache-license-header.txt", + "ignoreFile": "../../.licenseignorefile", + "defaultFormat": { + "gitignore|npmignore|eslintignore|dockerignore|sh|py": { + "eachLine": { + "prepend": "# " + } + }, + "html|xml|svg": { + "prepend": "" + }, + "js|ts|tsx|css|scss|less|php|as|c|java|cpp|go|cto|acl": { + "prepend": "/*", + "append": " */\n", + "eachLine": { + "prepend": " * " + } + }, + "txt": {} + }, + "licenseFormats": { + "ts|tsx": { + "prepend": "/*", + "append": " */\n", + "eachLine": { + "prepend": " * " + } + } + } +} diff --git a/workbench-core/datasets/package.json b/workbench-core/datasets/package.json index 1e4ec19607..07fb414443 100644 --- a/workbench-core/datasets/package.json +++ b/workbench-core/datasets/package.json @@ -15,8 +15,10 @@ "main": "lib/index.js", "typings": "lib/index.d.ts", "scripts": { + "add-license-header": "license-check-and-add add -f license-add-config.json", "build": "heft build --clean && rushx pkg-json-lint", "build:test": "heft test --clean && rushx pkg-json-lint && rushx make-badges", + "check-license-header": "license-check-and-add check -f license-add-config.json", "depcheck": "depcheck", "license-checker": "license-checker --onlyAllow 'MIT; Apache-2.0; ISC; BSD'", "lint:fix": "eslint . --fix", @@ -70,7 +72,8 @@ "istanbul-badges-readme": "1.8.1", "jest": "^27.5.1", "jest-fast-check": "^1.0.2", - "license-checker": "~25.0.1", + "license-check-and-add": "^4.0.5", + "license-checker": "^25.0.1", "npm-package-json-lint": "^6.3.0", "npm-package-json-lint-config-default": "^5.0.0", "sort-package-json": "^1.57.0", diff --git a/workbench-core/environments/license-add-config.json b/workbench-core/environments/license-add-config.json new file mode 100644 index 0000000000..ed85c268a8 --- /dev/null +++ b/workbench-core/environments/license-add-config.json @@ -0,0 +1,32 @@ +{ + "license": "../../apache-license-header.txt", + "ignoreFile": "../../.licenseignorefile", + "defaultFormat": { + "gitignore|npmignore|eslintignore|dockerignore|sh|py": { + "eachLine": { + "prepend": "# " + } + }, + "html|xml|svg": { + "prepend": "" + }, + "js|ts|tsx|css|scss|less|php|as|c|java|cpp|go|cto|acl": { + "prepend": "/*", + "append": " */\n", + "eachLine": { + "prepend": " * " + } + }, + "txt": {} + }, + "licenseFormats": { + "ts|tsx": { + "prepend": "/*", + "append": " */\n", + "eachLine": { + "prepend": " * " + } + } + } +} diff --git a/workbench-core/environments/package.json b/workbench-core/environments/package.json index 336b991cbc..0e506671b5 100644 --- a/workbench-core/environments/package.json +++ b/workbench-core/environments/package.json @@ -15,8 +15,10 @@ "main": "lib/index.js", "typings": "lib/index.d.ts", "scripts": { + "add-license-header": "license-check-and-add add -f license-add-config.json", "build": "heft build --clean && rushx pkg-json-lint", "build:test": "heft test --clean && rushx pkg-json-lint && rushx make-badges", + "check-license-header": "license-check-and-add check -f license-add-config.json", "depcheck": "depcheck", "jest": "jest", "license-checker": "license-checker --onlyAllow 'MIT; Apache-2.0; ISC; BSD'", @@ -81,7 +83,8 @@ "eslint-plugin-import": "^2.26.0", "istanbul-badges-readme": "1.8.1", "jest": "^27.5.1", - "license-checker": "~25.0.1", + "license-check-and-add": "^4.0.5", + "license-checker": "^25.0.1", "npm-package-json-lint": "^6.3.0", "npm-package-json-lint-config-default": "^5.0.0", "sort-package-json": "^1.57.0", diff --git a/workbench-core/eslint-custom/license-add-config.json b/workbench-core/eslint-custom/license-add-config.json new file mode 100644 index 0000000000..ed85c268a8 --- /dev/null +++ b/workbench-core/eslint-custom/license-add-config.json @@ -0,0 +1,32 @@ +{ + "license": "../../apache-license-header.txt", + "ignoreFile": "../../.licenseignorefile", + "defaultFormat": { + "gitignore|npmignore|eslintignore|dockerignore|sh|py": { + "eachLine": { + "prepend": "# " + } + }, + "html|xml|svg": { + "prepend": "" + }, + "js|ts|tsx|css|scss|less|php|as|c|java|cpp|go|cto|acl": { + "prepend": "/*", + "append": " */\n", + "eachLine": { + "prepend": " * " + } + }, + "txt": {} + }, + "licenseFormats": { + "ts|tsx": { + "prepend": "/*", + "append": " */\n", + "eachLine": { + "prepend": " * " + } + } + } +} diff --git a/workbench-core/eslint-custom/package.json b/workbench-core/eslint-custom/package.json index cbebb64991..84c0cdfa3d 100644 --- a/workbench-core/eslint-custom/package.json +++ b/workbench-core/eslint-custom/package.json @@ -15,8 +15,10 @@ "main": "lib/index.js", "typings": "lib/index.d.ts", "scripts": { + "add-license-header": "license-check-and-add add -f license-add-config.json", "build": "heft build --clean && rushx pkg-json-lint", "build:test": "heft test --clean && rushx pkg-json-lint", + "check-license-header": "license-check-and-add check -f license-add-config.json", "depcheck": "depcheck", "license-checker": "license-checker --onlyAllow 'MIT; Apache-2.0; ISC; BSD'", "lint:fix": "eslint . --fix", @@ -39,7 +41,8 @@ "@types/heft-jest": "1.0.2", "@types/node": "^14", "depcheck": "^1.4.3", - "license-checker": "~25.0.1", + "license-check-and-add": "^4.0.5", + "license-checker": "^25.0.1", "npm-package-json-lint": "^6.3.0", "npm-package-json-lint-config-default": "^5.0.0", "sort-package-json": "^1.57.0", diff --git a/workbench-core/example/express/license-add-config.json b/workbench-core/example/express/license-add-config.json new file mode 100644 index 0000000000..973a3df987 --- /dev/null +++ b/workbench-core/example/express/license-add-config.json @@ -0,0 +1,32 @@ +{ + "license": "../../../apache-license-header.txt", + "ignoreFile": "../../../.licenseignorefile", + "defaultFormat": { + "gitignore|npmignore|eslintignore|dockerignore|sh|py": { + "eachLine": { + "prepend": "# " + } + }, + "html|xml|svg": { + "prepend": "" + }, + "js|ts|tsx|css|scss|less|php|as|c|java|cpp|go|cto|acl": { + "prepend": "/*", + "append": " */\n", + "eachLine": { + "prepend": " * " + } + }, + "txt": {} + }, + "licenseFormats": { + "ts|tsx": { + "prepend": "/*", + "append": " */\n", + "eachLine": { + "prepend": " * " + } + } + } +} diff --git a/workbench-core/example/express/package.json b/workbench-core/example/express/package.json index 90f5cc5fdd..96745c268f 100644 --- a/workbench-core/example/express/package.json +++ b/workbench-core/example/express/package.json @@ -15,8 +15,10 @@ "main": "lib/index.js", "typings": "lib/index.d.ts", "scripts": { + "add-license-header": "license-check-and-add add -f license-add-config.json", "build": "heft build --clean && rushx pkg-json-lint", "build:test": "heft test --clean && rushx pkg-json-lint", + "check-license-header": "license-check-and-add check -f license-add-config.json", "depcheck": "depcheck", "license-checker": "license-checker --onlyAllow 'MIT; Apache-2.0; ISC; BSD'", "lint:fix": "eslint . --fix", @@ -45,7 +47,8 @@ "eslint-plugin-import": "^2.26.0", "eslint-plugin-security": "^1.4.0", "express": "^4.17.3", - "license-checker": "~25.0.1", + "license-check-and-add": "^4.0.5", + "license-checker": "^25.0.1", "npm-package-json-lint": "^6.3.0", "npm-package-json-lint-config-default": "^5.0.0", "sort-package-json": "^1.57.0", diff --git a/workbench-core/example/infrastructure/license-add-config.json b/workbench-core/example/infrastructure/license-add-config.json new file mode 100644 index 0000000000..973a3df987 --- /dev/null +++ b/workbench-core/example/infrastructure/license-add-config.json @@ -0,0 +1,32 @@ +{ + "license": "../../../apache-license-header.txt", + "ignoreFile": "../../../.licenseignorefile", + "defaultFormat": { + "gitignore|npmignore|eslintignore|dockerignore|sh|py": { + "eachLine": { + "prepend": "# " + } + }, + "html|xml|svg": { + "prepend": "" + }, + "js|ts|tsx|css|scss|less|php|as|c|java|cpp|go|cto|acl": { + "prepend": "/*", + "append": " */\n", + "eachLine": { + "prepend": " * " + } + }, + "txt": {} + }, + "licenseFormats": { + "ts|tsx": { + "prepend": "/*", + "append": " */\n", + "eachLine": { + "prepend": " * " + } + } + } +} diff --git a/workbench-core/example/infrastructure/package.json b/workbench-core/example/infrastructure/package.json index 8204501c5f..7d888b81f6 100644 --- a/workbench-core/example/infrastructure/package.json +++ b/workbench-core/example/infrastructure/package.json @@ -16,12 +16,14 @@ "infrastructure": "lib/index.js" }, "scripts": { + "add-license-header": "license-check-and-add add -f license-add-config.json", "build": "rushx compile && heft build --clean && rushx pkg-json-lint", "build:test": "rushx compile && heft test -u --clean --test-path-pattern infrastructure-stack && rushx pkg-json-lint && rushx make-badges", "cdk": "cdk", "cdk:bootstrap": "cdk bootstrap", "cdk:deploy": "rushx compile && cdk deploy --all --require-approval never --outputs-file cdk-outputs.json", "cdk:synth": "cdk synth", + "check-license-header": "license-check-and-add check -f license-add-config.json", "compile": "node scripts/buildLambda.js", "depcheck": "depcheck", "license-checker": "license-checker --onlyAllow 'MIT; Apache-2.0; ISC; BSD'", @@ -66,7 +68,8 @@ "express": "^4.17.3", "istanbul-badges-readme": "1.8.1", "jest": "^27.5.1", - "license-checker": "~25.0.1", + "license-check-and-add": "^4.0.5", + "license-checker": "^25.0.1", "npm-package-json-lint": "^6.3.0", "npm-package-json-lint-config-default": "^5.0.0", "sort-package-json": "^1.57.0", diff --git a/workbench-core/infrastructure/license-add-config.json b/workbench-core/infrastructure/license-add-config.json new file mode 100644 index 0000000000..ed85c268a8 --- /dev/null +++ b/workbench-core/infrastructure/license-add-config.json @@ -0,0 +1,32 @@ +{ + "license": "../../apache-license-header.txt", + "ignoreFile": "../../.licenseignorefile", + "defaultFormat": { + "gitignore|npmignore|eslintignore|dockerignore|sh|py": { + "eachLine": { + "prepend": "# " + } + }, + "html|xml|svg": { + "prepend": "" + }, + "js|ts|tsx|css|scss|less|php|as|c|java|cpp|go|cto|acl": { + "prepend": "/*", + "append": " */\n", + "eachLine": { + "prepend": " * " + } + }, + "txt": {} + }, + "licenseFormats": { + "ts|tsx": { + "prepend": "/*", + "append": " */\n", + "eachLine": { + "prepend": " * " + } + } + } +} diff --git a/workbench-core/infrastructure/package.json b/workbench-core/infrastructure/package.json index ae4ec4424e..a32cebdc9d 100644 --- a/workbench-core/infrastructure/package.json +++ b/workbench-core/infrastructure/package.json @@ -15,8 +15,10 @@ "main": "lib/index.js", "typings": "lib/index.d.ts", "scripts": { + "add-license-header": "license-check-and-add add -f license-add-config.json", "build": "heft build --clean && rushx pkg-json-lint", "build:test": "heft test --clean && rushx pkg-json-lint && rushx make-badges", + "check-license-header": "license-check-and-add check -f license-add-config.json", "depcheck": "depcheck", "license-checker": "license-checker --onlyAllow 'MIT; Apache-2.0; ISC; BSD'", "lint:fix": "eslint . --fix", @@ -45,7 +47,8 @@ "eslint-plugin-import": "^2.26.0", "eslint-plugin-security": "^1.4.0", "istanbul-badges-readme": "1.8.1", - "license-checker": "~25.0.1", + "license-check-and-add": "^4.0.5", + "license-checker": "^25.0.1", "npm-package-json-lint": "^6.3.0", "npm-package-json-lint-config-default": "^5.0.0", "sort-package-json": "^1.57.0", diff --git a/workbench-core/logging/license-add-config.json b/workbench-core/logging/license-add-config.json new file mode 100644 index 0000000000..ed85c268a8 --- /dev/null +++ b/workbench-core/logging/license-add-config.json @@ -0,0 +1,32 @@ +{ + "license": "../../apache-license-header.txt", + "ignoreFile": "../../.licenseignorefile", + "defaultFormat": { + "gitignore|npmignore|eslintignore|dockerignore|sh|py": { + "eachLine": { + "prepend": "# " + } + }, + "html|xml|svg": { + "prepend": "" + }, + "js|ts|tsx|css|scss|less|php|as|c|java|cpp|go|cto|acl": { + "prepend": "/*", + "append": " */\n", + "eachLine": { + "prepend": " * " + } + }, + "txt": {} + }, + "licenseFormats": { + "ts|tsx": { + "prepend": "/*", + "append": " */\n", + "eachLine": { + "prepend": " * " + } + } + } +} diff --git a/workbench-core/logging/package.json b/workbench-core/logging/package.json index 2634f9c3cf..a23c0fd1e0 100644 --- a/workbench-core/logging/package.json +++ b/workbench-core/logging/package.json @@ -15,8 +15,10 @@ "main": "lib/index.js", "typings": "lib/index.d.ts", "scripts": { + "add-license-header": "license-check-and-add add -f license-add-config.json", "build": "heft build --clean && rushx pkg-json-lint", "build:test": "heft test --clean --silent && rushx pkg-json-lint && rushx make-badges", + "check-license-header": "license-check-and-add check -f license-add-config.json", "depcheck": "depcheck", "license-checker": "license-checker --onlyAllow 'MIT; Apache-2.0; ISC; BSD'", "lint:fix": "eslint . --fix", @@ -47,7 +49,8 @@ "istanbul-badges-readme": "1.8.1", "jest": "^27.5.1", "jest-fast-check": "^1.0.2", - "license-checker": "~25.0.1", + "license-check-and-add": "^4.0.5", + "license-checker": "^25.0.1", "npm-package-json-lint": "^6.3.0", "npm-package-json-lint-config-default": "^5.0.0", "sort-package-json": "^1.57.0", diff --git a/workbench-core/repo-scripts/repo-toolbox/license-add-config.json b/workbench-core/repo-scripts/repo-toolbox/license-add-config.json new file mode 100644 index 0000000000..973a3df987 --- /dev/null +++ b/workbench-core/repo-scripts/repo-toolbox/license-add-config.json @@ -0,0 +1,32 @@ +{ + "license": "../../../apache-license-header.txt", + "ignoreFile": "../../../.licenseignorefile", + "defaultFormat": { + "gitignore|npmignore|eslintignore|dockerignore|sh|py": { + "eachLine": { + "prepend": "# " + } + }, + "html|xml|svg": { + "prepend": "" + }, + "js|ts|tsx|css|scss|less|php|as|c|java|cpp|go|cto|acl": { + "prepend": "/*", + "append": " */\n", + "eachLine": { + "prepend": " * " + } + }, + "txt": {} + }, + "licenseFormats": { + "ts|tsx": { + "prepend": "/*", + "append": " */\n", + "eachLine": { + "prepend": " * " + } + } + } +} diff --git a/workbench-core/repo-scripts/repo-toolbox/package.json b/workbench-core/repo-scripts/repo-toolbox/package.json index 465f8c2642..e20fd68140 100644 --- a/workbench-core/repo-scripts/repo-toolbox/package.json +++ b/workbench-core/repo-scripts/repo-toolbox/package.json @@ -15,8 +15,10 @@ "main": "lib/index.js", "typings": "lib/index.d.ts", "scripts": { + "add-license-header": "license-check-and-add add -f license-add-config.json", "build": "heft build --clean && rushx pkg-json-lint", "build:test": "heft test --clean && rushx pkg-json-lint", + "check-license-header": "license-check-and-add check -f license-add-config.json", "depcheck": "depcheck", "license-checker": "license-checker --onlyAllow 'MIT; Apache-2.0; ISC; BSD'", "lint:fix": "eslint . --fix", @@ -46,7 +48,8 @@ "eslint": "^8.7.0", "eslint-plugin-import": "^2.26.0", "eslint-plugin-security": "^1.4.0", - "license-checker": "~25.0.1", + "license-check-and-add": "^4.0.5", + "license-checker": "^25.0.1", "npm-package-json-lint": "^6.3.0", "npm-package-json-lint-config-default": "^5.0.0", "sort-package-json": "^1.57.0", From d68af46734012fa98ac1884b2162293f57bad0fd Mon Sep 17 00:00:00 2001 From: FernandoAranda Date: Wed, 24 Aug 2022 08:51:50 -0700 Subject: [PATCH 027/621] fix(environment multistep integration test): fix environments throttling issue (#477) * fix(environment multistep integration test): fix throttling issue with environment actions * chore(merge develop): merge develop Co-authored-by: Fernando Aranda --- .../support/resources/environments/environment.ts | 8 ++++++-- .../integration-tests/support/utils/constants.ts | 2 ++ .../integration-tests/tests/multiStep/environment.test.ts | 3 ++- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/solutions/swb-reference/integration-tests/support/resources/environments/environment.ts b/solutions/swb-reference/integration-tests/support/resources/environments/environment.ts index 96b92cbbf9..2dc357d084 100644 --- a/solutions/swb-reference/integration-tests/support/resources/environments/environment.ts +++ b/solutions/swb-reference/integration-tests/support/resources/environments/environment.ts @@ -5,8 +5,8 @@ import { EnvironmentStatus } from '@aws/workbench-core-environments'; import { AxiosResponse } from 'axios'; import ClientSession from '../../clientSession'; -import { ENVIRONMENT_START_MAX_WAITING_SECONDS } from '../../utils/constants'; -import { poll } from '../../utils/utilities'; +import { ENVIRONMENT_START_MAX_WAITING_SECONDS, DEFLAKE_DELAY_IN_MILLISECONDS } from '../../utils/constants'; +import { poll, sleep } from '../../utils/utilities'; import Resource from '../base/resource'; export default class Environment extends Resource { @@ -15,18 +15,22 @@ export default class Environment extends Resource { } public async connect(): Promise { + await sleep(DEFLAKE_DELAY_IN_MILLISECONDS); //Avoid throttling when connecting to multiple environments return this._axiosInstance.get(`${this._api}/connections`); } public async stop(): Promise { + await sleep(DEFLAKE_DELAY_IN_MILLISECONDS); //Avoid throttling when stopping multiple environments return this._axiosInstance.put(`${this._api}/stop`); } public async start(): Promise { + await sleep(DEFLAKE_DELAY_IN_MILLISECONDS); //Avoid throttling when starting multiple environments return this._axiosInstance.put(`${this._api}/start`); } public async terminate(): Promise { + await sleep(DEFLAKE_DELAY_IN_MILLISECONDS); //Avoid throttling when terminating multiple environments return this._axiosInstance.put(`${this._api}/terminate`); } diff --git a/solutions/swb-reference/integration-tests/support/utils/constants.ts b/solutions/swb-reference/integration-tests/support/utils/constants.ts index ad02f98f8c..05307d35e1 100644 --- a/solutions/swb-reference/integration-tests/support/utils/constants.ts +++ b/solutions/swb-reference/integration-tests/support/utils/constants.ts @@ -14,3 +14,5 @@ export const ENVIRONMENT_TERMINATE_MAX_WAITING_SECONDS: number = 180; export const DEFAULT_POLLING_INTERVAL_SECONDS: number = 15; export const DEFAULT_POLLING_MAX_WAITING_SECONDS: number = 600; + +export const DEFLAKE_DELAY_IN_MILLISECONDS: number = 2000; diff --git a/solutions/swb-reference/integration-tests/tests/multiStep/environment.test.ts b/solutions/swb-reference/integration-tests/tests/multiStep/environment.test.ts index e476ebd56b..d1ae233148 100644 --- a/solutions/swb-reference/integration-tests/tests/multiStep/environment.test.ts +++ b/solutions/swb-reference/integration-tests/tests/multiStep/environment.test.ts @@ -178,8 +178,9 @@ describe('multiStep environment test', () => { console.log('Environment A Completed'); //Stop Environments A and B - console.log('Stopping Environments A and B'); + console.log(`Stopping Environments A: ${environmentA.id}`); await adminSession.resources.environments.environment(environmentA.id).stop(); + console.log(`Stopping Environments B: ${environmentB.id}`); await adminSession.resources.environments.environment(environmentB.id).stop(); //Wait for Environment A to stop From 7abaed046ca3709f53ea298faac078887a97c89c Mon Sep 17 00:00:00 2001 From: Samir Sharan <11716061+insignias@users.noreply.github.com> Date: Wed, 24 Aug 2022 12:22:59 -0400 Subject: [PATCH 028/621] chore: fix slack notification (#478) * chore: test slack notification * Update test.yml fix test.yml * chore: test again * chore: test again * chore: test without newline * chore: test final * chore: test final 1 * chore: cleanup * chore: formatting fix Co-authored-by: Samir Sharan Co-authored-by: Brendon Hudnell --- .github/actions/notification/action.yml | 2 +- .github/workflows/deploy-integration-swb-reference.yml | 4 +++- .github/workflows/deploy-swb-reference-demo.yml | 4 +++- .github/workflows/merge-develop-to-stage.yml | 3 +++ .github/workflows/readme-commit.yml | 5 ++++- 5 files changed, 14 insertions(+), 4 deletions(-) diff --git a/.github/actions/notification/action.yml b/.github/actions/notification/action.yml index b56e9ebf24..6fa8c01080 100644 --- a/.github/actions/notification/action.yml +++ b/.github/actions/notification/action.yml @@ -16,4 +16,4 @@ runs: - name: Slack Notification shell: bash run: | - curl -X POST -H 'Content-type: application/json' --data '{"text":" -> [ STATUS: ${{ job.status }} WORKFLOW_NAME: ${{ github.workflow }} JOB: ${{ github.job }} JOB_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} ] "}' ${{ inputs.slack_webhook_url }} \ No newline at end of file + curl -X POST -H 'Content-type: application/json' --data '{"text":"\nSTATUS: \"${{ job.status }}\" \nWORKFLOW_NAME: \"${{ github.workflow }}\" \nJOB: \"${{ github.job }}\" \nJOB_URL: \"${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}\" "}' ${{ inputs.slack_webhook_url }} \ No newline at end of file diff --git a/.github/workflows/deploy-integration-swb-reference.yml b/.github/workflows/deploy-integration-swb-reference.yml index 01e0790d86..d7ff10eb0c 100644 --- a/.github/workflows/deploy-integration-swb-reference.yml +++ b/.github/workflows/deploy-integration-swb-reference.yml @@ -13,6 +13,8 @@ on: required: true aws-dev-account-region: required: true + slack_webhook_url: + required: true jobs: # deploy only when files specific to swb package changed @@ -63,4 +65,4 @@ jobs: if: failure() uses: ./.github/actions/notification with: - slack_webhook_url: ${{ secrets.SLACK_WEBHOOK }} \ No newline at end of file + slack_webhook_url: ${{ secrets.slack_webhook_url }} \ No newline at end of file diff --git a/.github/workflows/deploy-swb-reference-demo.yml b/.github/workflows/deploy-swb-reference-demo.yml index c0558256c4..46b8845c4d 100644 --- a/.github/workflows/deploy-swb-reference-demo.yml +++ b/.github/workflows/deploy-swb-reference-demo.yml @@ -9,6 +9,8 @@ on: secrets: role-to-assume: required: true + slack_webhook_url: + required: true jobs: # deploy only when files specific to swb package changed @@ -45,4 +47,4 @@ jobs: if: failure() uses: ./.github/actions/notification with: - slack_webhook_url: ${{ secrets.SLACK_WEBHOOK }} \ No newline at end of file + slack_webhook_url: ${{ secrets.slack_webhook_url }} \ No newline at end of file diff --git a/.github/workflows/merge-develop-to-stage.yml b/.github/workflows/merge-develop-to-stage.yml index b0b82df52d..1138b5967b 100644 --- a/.github/workflows/merge-develop-to-stage.yml +++ b/.github/workflows/merge-develop-to-stage.yml @@ -18,6 +18,7 @@ jobs: merge-token: ${{ secrets.MERGE_TOKEN }} bot-user: ${{ secrets.BOT_USER }} bot-user-email: ${{ secrets.BOT_USER_EMAIL }} + slack_webhook_url: ${{ secrets.SLACK_WEBHOOK }} pre-deployment-check: name: Pre deployment check @@ -38,12 +39,14 @@ jobs: aws-dev-region: ${{ secrets.AWS_DEV_REGION }} role-to-assume: ${{ secrets.ASSUME_ROLE }} aws-dev-account-region: ${{ secrets.AWS_DEV_ACCOUNT_REGION }} + slack_webhook_url: ${{ secrets.SLACK_WEBHOOK }} deploy-swb-demo: needs: [ deploy-swb-reference] uses: awslabs/solution-spark-on-aws/.github/workflows/deploy-swb-reference-demo.yml@develop secrets: role-to-assume: ${{ secrets.ASSUME_ROLE }} + slack_webhook_url: ${{ secrets.SLACK_WEBHOOK }} # deploy-example: # needs: [pre-deployment-check] diff --git a/.github/workflows/readme-commit.yml b/.github/workflows/readme-commit.yml index 1141641f36..1f5d7c273e 100644 --- a/.github/workflows/readme-commit.yml +++ b/.github/workflows/readme-commit.yml @@ -17,6 +17,9 @@ on: required: true bot-user-email: required: true + slack_webhook_url: + required: true + outputs: commit_hash: description: 'README update commit hash' @@ -104,4 +107,4 @@ jobs: if: failure() uses: ./.github/actions/notification with: - slack_webhook_url: ${{ secrets.SLACK_WEBHOOK }} \ No newline at end of file + slack_webhook_url: ${{ secrets.slack_webhook_url }} \ No newline at end of file From b9a1894bd1c6a34efa8282c39ade1c29f32854ab Mon Sep 17 00:00:00 2001 From: Brendon Hudnell Date: Wed, 24 Aug 2022 12:41:04 -0600 Subject: [PATCH 029/621] feat: add csrf protection (#465) * fix(solutions/swb): changed logout to a POST route, added explicit programs for scripts * fix: changed aipHelper withCredentialss defaults to true, removed unneeded cors allowedHeaders * fix: changed sameSite to none, using maxAge instead of expires * refactor: replaced utils dependency on aws sdk with own enum * fix: now using defaults when sdk call is undefined * test: added missing property to workbenchCognito tests * refactor(workbench-core/authentication): refactored package to use ms as default unit of time * feat: made sameSite cookie setting configurable with a default of strict * fix: lowered id and access token lifespan to 15 minutes, token verifier only verify access tokens * test: fixed broken test * revert: remove accidentally committed files * fix: pR fix * feat: added csrf middleware to generateRouter * revert: revert accidental merge overwrite * feat: moved csurf into authN package as its own middleware * feat: implemented csrf protection on swb-ui * feat: added csrf protection to swb ui and api * feat: added csrf protection middleware to authentication service * revert: revert accidental package json commit * docs: fixed incorrect doc * docs: updated docs to reflect changes * docs: added rush change files * removed integration misnomer from test name --- ...-add-csrf-protection_2022-08-19-16-14.json | 10 ++ ...-add-csrf-protection_2022-08-19-16-14.json | 10 ++ .../rush/browser-approved-packages.json | 4 + common/config/rush/pnpm-lock.yaml | 85 ++++++++++ solutions/swb-app/src/generateRouter.ts | 2 + solutions/swb-reference/src/SWBStack.ts | 2 +- solutions/swb-ui/src/api/apiHelper.ts | 11 +- solutions/swb-ui/src/api/auth.ts | 10 +- .../src/context/AuthenticationContext.tsx | 5 +- .../docs/authenticationMiddleware.md | 30 +++- workbench-core/authentication/package.json | 2 + .../src/authenticationMiddleware.test.ts | 160 ++++++++++++++++-- .../src/authenticationMiddleware.ts | 80 +++++++-- workbench-core/authentication/src/index.ts | 1 + 14 files changed, 366 insertions(+), 46 deletions(-) create mode 100644 common/changes/@aws/swb-app/feat-add-csrf-protection_2022-08-19-16-14.json create mode 100644 common/changes/@aws/workbench-core-authentication/feat-add-csrf-protection_2022-08-19-16-14.json diff --git a/common/changes/@aws/swb-app/feat-add-csrf-protection_2022-08-19-16-14.json b/common/changes/@aws/swb-app/feat-add-csrf-protection_2022-08-19-16-14.json new file mode 100644 index 0000000000..1df0375ba3 --- /dev/null +++ b/common/changes/@aws/swb-app/feat-add-csrf-protection_2022-08-19-16-14.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@aws/swb-app", + "comment": "Added CSRF protection", + "type": "patch" + } + ], + "packageName": "@aws/swb-app" +} \ No newline at end of file diff --git a/common/changes/@aws/workbench-core-authentication/feat-add-csrf-protection_2022-08-19-16-14.json b/common/changes/@aws/workbench-core-authentication/feat-add-csrf-protection_2022-08-19-16-14.json new file mode 100644 index 0000000000..da61d96f39 --- /dev/null +++ b/common/changes/@aws/workbench-core-authentication/feat-add-csrf-protection_2022-08-19-16-14.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@aws/workbench-core-authentication", + "comment": "Added CSRF middleware, updated other middlware/route handlers to accept a csrf parameter", + "type": "minor" + } + ], + "packageName": "@aws/workbench-core-authentication" +} \ No newline at end of file diff --git a/common/config/rush/browser-approved-packages.json b/common/config/rush/browser-approved-packages.json index cd1a4ed839..e053b9a25e 100644 --- a/common/config/rush/browser-approved-packages.json +++ b/common/config/rush/browser-approved-packages.json @@ -294,6 +294,10 @@ "name": "cors", "allowedCategories": [ "production" ] }, + { + "name": "csurf", + "allowedCategories": [ "production" ] + }, { "name": "date-fns", "allowedCategories": [ "production" ] diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index 4473b43775..3ef63802e8 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -513,6 +513,7 @@ importers: '@rushstack/heft': ^0.45.0 '@rushstack/heft-jest-plugin': ^0.2.3 '@rushstack/heft-node-rig': ^1.7.1 + '@types/csurf': ^1.11.2 '@types/express': ^4.17.13 '@types/heft-jest': 1.0.2 '@types/lodash': ^4.14.181 @@ -520,6 +521,7 @@ importers: aws-jwt-verify: ^3.0.0 aws-sdk-client-mock: ^0.6.2 axios: ^0.27.1 + csurf: ^1.11.0 depcheck: ^1.4.3 eslint: ^8.7.0 eslint-plugin-import: ^2.26.0 @@ -538,6 +540,7 @@ importers: '@aws/workbench-core-logging': link:../logging aws-jwt-verify: 3.1.0 axios: 0.27.2 + csurf: 1.11.0 jsonschema: 1.4.1 lodash: 4.17.21 devDependencies: @@ -548,6 +551,7 @@ importers: '@rushstack/heft': 0.45.5 '@rushstack/heft-jest-plugin': 0.2.15_@rushstack+heft@0.45.5 '@rushstack/heft-node-rig': 1.9.6_@rushstack+heft@0.45.5 + '@types/csurf': 1.11.2 '@types/express': 4.17.13 '@types/heft-jest': 1.0.2 '@types/lodash': 4.14.182 @@ -6565,6 +6569,12 @@ packages: resolution: {integrity: sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==} dev: true + /@types/csurf/1.11.2: + resolution: {integrity: sha512-9bc98EnwmC1S0aSJiA8rWwXtgXtXHHOQOsGHptImxFgqm6CeH+mIOunHRg6+/eg2tlmDMX3tY7XrWxo2M/nUNQ==} + dependencies: + '@types/express-serve-static-core': 4.17.28 + dev: true + /@types/diff/5.0.1: resolution: {integrity: sha512-XIpxU6Qdvp1ZE6Kr3yrkv1qgUab0fyf4mHYvW8N3Bx3PCsbN6or1q9/q72cv5jIFWolaGH08U9XyYoLLIykyKQ==} dev: true @@ -8205,6 +8215,11 @@ packages: /cookie-signature/1.0.6: resolution: {integrity: sha1-4wOogrNCzD7oylE6eZmXNNqzriw=} + /cookie/0.4.0: + resolution: {integrity: sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==} + engines: {node: '>= 0.6'} + dev: false + /cookie/0.4.1: resolution: {integrity: sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==} engines: {node: '>= 0.6'} @@ -8289,6 +8304,15 @@ packages: resolution: {integrity: sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw==} dev: false + /csrf/3.1.0: + resolution: {integrity: sha512-uTqEnCvWRk042asU6JtapDTcJeeailFy4ydOQS28bj1hcLnYRiqi8SsD2jS412AY1I/4qdOwWZun774iqywf9w==} + engines: {node: '>= 0.8'} + dependencies: + rndm: 1.2.0 + tsscmp: 1.0.6 + uid-safe: 2.1.5 + dev: false + /css-selector-tokenizer/0.8.0: resolution: {integrity: sha512-Jd6Ig3/pe62/qe5SBPTN8h8LeUg/pT4lLgtavPf7updwwHpvFzxvOQBHYj2LZDMjUnBzgvIUSjRcf6oT5HzHFg==} dependencies: @@ -8332,6 +8356,16 @@ packages: /csstype/3.1.0: resolution: {integrity: sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA==} + /csurf/1.11.0: + resolution: {integrity: sha512-UCtehyEExKTxgiu8UHdGvHj4tnpE/Qctue03Giq5gPgMQ9cg/ciod5blZQ5a4uCEenNQjxyGuzygLdKUmee/bQ==} + engines: {node: '>= 0.8.0'} + dependencies: + cookie: 0.4.0 + cookie-signature: 1.0.6 + csrf: 3.1.0 + http-errors: 1.7.3 + dev: false + /d3-array/1.2.4: resolution: {integrity: sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw==} dev: true @@ -8541,6 +8575,11 @@ packages: - supports-color dev: true + /depd/1.1.2: + resolution: {integrity: sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==} + engines: {node: '>= 0.6'} + dev: false + /depd/2.0.0: resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} engines: {node: '>= 0.8'} @@ -10070,6 +10109,17 @@ packages: void-elements: 3.1.0 dev: false + /http-errors/1.7.3: + resolution: {integrity: sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==} + engines: {node: '>= 0.6'} + dependencies: + depd: 1.1.2 + inherits: 2.0.4 + setprototypeof: 1.1.1 + statuses: 1.5.0 + toidentifier: 1.0.0 + dev: false + /http-errors/2.0.0: resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} engines: {node: '>= 0.8'} @@ -13054,6 +13104,11 @@ packages: resolution: {integrity: sha512-SbiLPU40JuJniHexQSAgad32hfwd+DRUdwF2PlVuI5RZD0/vahUco7R8vD86J/tcEKKF9vZrUVwgtmGCqlCKyA==} dev: false + /random-bytes/1.0.0: + resolution: {integrity: sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ==} + engines: {node: '>= 0.8'} + dev: false + /range-parser/1.2.1: resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} engines: {node: '>= 0.6'} @@ -13486,6 +13541,10 @@ packages: dependencies: glob: 7.2.3 + /rndm/1.2.0: + resolution: {integrity: sha512-fJhQQI5tLrQvYIYFpOnFinzv9dwmR7hRnUz1XqP3OJ1jIweTNOd6aTO4jwQSgcBSFUB+/KHJxuGneime+FdzOw==} + dev: false + /rsvp/4.8.5: resolution: {integrity: sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==} engines: {node: 6.* || >= 7.*} @@ -13658,6 +13717,10 @@ packages: split-string: 3.1.0 dev: true + /setprototypeof/1.1.1: + resolution: {integrity: sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==} + dev: false + /setprototypeof/1.2.0: resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} @@ -13950,6 +14013,11 @@ packages: object-copy: 0.1.0 dev: true + /statuses/1.5.0: + resolution: {integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==} + engines: {node: '>= 0.6'} + dev: false + /statuses/2.0.1: resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} engines: {node: '>= 0.8'} @@ -14299,6 +14367,11 @@ packages: safe-regex: 1.1.0 dev: true + /toidentifier/1.0.0: + resolution: {integrity: sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==} + engines: {node: '>=0.6'} + dev: false + /toidentifier/1.0.1: resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} engines: {node: '>=0.6'} @@ -14534,6 +14607,11 @@ packages: /tslib/2.4.0: resolution: {integrity: sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==} + /tsscmp/1.0.6: + resolution: {integrity: sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==} + engines: {node: '>=0.6.x'} + dev: false + /tsutils/3.21.0_typescript@4.7.2: resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} engines: {node: '>= 6'} @@ -14622,6 +14700,13 @@ packages: hasBin: true dev: true + /uid-safe/2.1.5: + resolution: {integrity: sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==} + engines: {node: '>= 0.8'} + dependencies: + random-bytes: 1.0.0 + dev: false + /unbox-primitive/1.0.2: resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} dependencies: diff --git a/solutions/swb-app/src/generateRouter.ts b/solutions/swb-app/src/generateRouter.ts index 32ac258873..1f06923bbc 100644 --- a/solutions/swb-app/src/generateRouter.ts +++ b/solutions/swb-app/src/generateRouter.ts @@ -4,6 +4,7 @@ */ import { + csurf, verifyToken, AuthenticationService, CognitoAuthenticationPluginOptions, @@ -51,6 +52,7 @@ export function generateRouter(apiRouteConfig: ApiRouteConfig): Express { // parse application/json app.use(express.json()); app.use(cookieParser()); + app.use(csurf('none')); const cognitoPluginOptions: CognitoAuthenticationPluginOptions = { cognitoDomain: process.env.COGNITO_DOMAIN!, diff --git a/solutions/swb-reference/src/SWBStack.ts b/solutions/swb-reference/src/SWBStack.ts index 17019687dc..2bbc88b397 100644 --- a/solutions/swb-reference/src/SWBStack.ts +++ b/solutions/swb-reference/src/SWBStack.ts @@ -747,7 +747,7 @@ export class SWBStack extends Stack { ) }, defaultCorsPreflightOptions: { - allowHeaders: ['Content-Type', 'X-Amz-Date', 'Authorization', 'X-Api-Key'], + allowHeaders: ['Content-Type', 'X-Amz-Date', 'Authorization', 'X-Api-Key', 'CSRF-Token'], allowMethods: ['OPTIONS', 'GET', 'POST', 'PUT', 'PATCH', 'DELETE'], allowCredentials: true, allowOrigins: JSON.parse(this.lambdaEnvVars.ALLOWED_ORIGINS || '[]') diff --git a/solutions/swb-ui/src/api/apiHelper.ts b/solutions/swb-ui/src/api/apiHelper.ts index bf504d58e0..fed5560f35 100644 --- a/solutions/swb-ui/src/api/apiHelper.ts +++ b/solutions/swb-ui/src/api/apiHelper.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import axios from 'axios'; +import axios, { AxiosRequestConfig } from 'axios'; const urlBase: string | undefined = process.env.NEXT_PUBLIC_API_BASE_URL; @@ -44,9 +44,12 @@ const httpApiDelete = async (urlPath: string, params: any, withCredentials: bool return await fetchData(options); }; -const fetchData = async (options: any): Promise => { - // TODO: remove these headers once accessToken is properly set by cookies - options.headers = { Authorization: `${localStorage.getItem('accessToken')}` }; +const fetchData = async (options: AxiosRequestConfig): Promise => { + // add the CSRF header + const csrfToken = localStorage.getItem('csrfToken'); + if (csrfToken) { + options.headers = { 'csrf-token': csrfToken }; + } //TODO add auth token and error handling const { data } = await axios(options).catch(function (error) { console.log(error); diff --git a/solutions/swb-ui/src/api/auth.ts b/solutions/swb-ui/src/api/auth.ts index 4801e99109..a2b795fca5 100644 --- a/solutions/swb-ui/src/api/auth.ts +++ b/solutions/swb-ui/src/api/auth.ts @@ -10,19 +10,15 @@ const token = async (body: { code: string; codeVerifier: string | null }): Promi }; const login = async (): Promise => { - return await httpApiGet( - 'login/?stateVerifier=TEMP_STATE_VERIFIER&codeChallenge=TEMP_CODE_CHALLENGE', - {}, - false - ); + return await httpApiGet('login/?stateVerifier=TEMP_STATE_VERIFIER&codeChallenge=TEMP_CODE_CHALLENGE', {}); }; const logout = async (): Promise => { return await httpApiPost('logout', {}); }; -const checkIfloggedIn = async (): Promise => { +const checkIfLoggedIn = async (): Promise => { return await httpApiGet('loggedIn', {}); }; -export { login, logout, token, checkIfloggedIn }; +export { login, logout, token, checkIfLoggedIn }; diff --git a/solutions/swb-ui/src/context/AuthenticationContext.tsx b/solutions/swb-ui/src/context/AuthenticationContext.tsx index 1a295ca1e1..aed52d1fef 100644 --- a/solutions/swb-ui/src/context/AuthenticationContext.tsx +++ b/solutions/swb-ui/src/context/AuthenticationContext.tsx @@ -54,7 +54,6 @@ export function AuthenticationProvider({ children }: { children: React.ReactNode }); localStorage.setItem('idToken', response.idToken); - localStorage.setItem('accessToken', response.accessToken); decodeTokenAndSetUser(response.idToken); localStorage.removeItem('stateVerifier'); @@ -89,7 +88,8 @@ export function AuthenticationProvider({ children }: { children: React.ReactNode async function signIn(): Promise { try { const response = await login(); - let signInUrl: string = response.redirectUrl; + let signInUrl: string = response.signInUrl; + localStorage.setItem('csrfToken', response.csrfToken); const challenge = pkceChallenge(128); localStorage.setItem('pkceVerifier', challenge.code_verifier); @@ -109,7 +109,6 @@ export function AuthenticationProvider({ children }: { children: React.ReactNode const response = await logout(); const logoutUrl = response.logoutUrl; - window.localStorage.removeItem('accessToken'); window.localStorage.removeItem('idToken'); window.location.assign(logoutUrl); } catch (e) { diff --git a/workbench-core/authentication/docs/authenticationMiddleware.md b/workbench-core/authentication/docs/authenticationMiddleware.md index 89b073097b..05ec1ffdcf 100644 --- a/workbench-core/authentication/docs/authenticationMiddleware.md +++ b/workbench-core/authentication/docs/authenticationMiddleware.md @@ -13,6 +13,7 @@ The provided middleware and route handlers assume that the Express application i - [built in express](https://expressjs.com/en/4x/api.html) via `express.json()` for express versions >= 4.16.0 - [body-parser](https://www.npmjs.com/package/body-parser) for express versions < 4.16.0 - the `verifyToken` middleware is mounted at the app level with no path, as it should execute every time a request is received +- the `csurf` middleware is mounted at the app level with no path, as it should execute every time a request is received - the `RoutesIgnored` object is imported from the [authorization package](../../authorization/) #### Example @@ -22,8 +23,7 @@ const cognitoAuthenticationPluginOptions: CognitoAuthenticationPluginOptions = { cognitoDomain: '', userPoolId: '', clientId: '', - clientSecret: '', - websiteUrl: '' + clientSecret: '' }; // Create an AuthenticationService instance @@ -62,6 +62,9 @@ app.use(express.json()); // Add the verifyToken middleware to the app app.use(verifyToken(authenticationService, { ignoredRoutes, loggingService })); +// Add the csurf middleware to the app +app.use(csurf()); + // These routes are public (as defined in the ignoredRoutes object above) app.get('/login', getAuthorizationCodeUrl(authenticationService)); app.post('/token', getTokensFromAuthorizationCode(authenticationService, { loggingService })); @@ -78,12 +81,35 @@ app.listen(3001); ``` ## Functions +- [csurf](#csurf) - [getAuthorizationCodeUrl](#getauthorizationcodeurl) - [getTokensFromAuthorizationCode](#gettokensfromauthorizationcode) - [verifyToken](#verifytoken) - [refreshAccessToken](#refreshaccesstoken) - [logoutUser](#logoutuser) +### csurf +This middleware is used to add csrf protection to an Express app. +It uses Express's [csurf](http://expressjs.com/en/resources/middleware/csurf.html) library with the cookie implementation. + +#### Assumptions +- the middleware is mounted using `app.use()` +- the csrf token returned by the `getAuthorizationCodeUrl` or `isUserLoggedIn` route handler is included in all requests in one of the following colations: + - req.body._csrf + - req.query._csrf + - req.headers['csrf-token'] + - req.headers['xsrf-token'] + - req.headers['x-csrf-token'] + - req.headers['x-xsrf-token'] + +#### Parameters +- sameSite: (optional) the csrf cookie's `sameSite` value. Defaults to `'strict'` if not included + +#### Example +```ts +app.use(csurf('strict')); +``` + ### getAuthorizationCodeUrl This route handler is used to get the url to the authentication hosted UI. The `stateVerifier` and `codeChallenge` request query parameters are temporary values passed in by the client. The client will replace these values later in order to keep them a client secret. diff --git a/workbench-core/authentication/package.json b/workbench-core/authentication/package.json index 48860476cb..f06492d4f4 100644 --- a/workbench-core/authentication/package.json +++ b/workbench-core/authentication/package.json @@ -34,6 +34,7 @@ "@aws/workbench-core-logging": "workspace:*", "aws-jwt-verify": "^3.0.0", "axios": "^0.27.1", + "csurf": "^1.11.0", "jsonschema": "^1.4.1", "lodash": "^4.17.21" }, @@ -45,6 +46,7 @@ "@rushstack/heft": "^0.45.0", "@rushstack/heft-jest-plugin": "^0.2.3", "@rushstack/heft-node-rig": "^1.7.1", + "@types/csurf": "^1.11.2", "@types/express": "^4.17.13", "@types/heft-jest": "1.0.2", "@types/lodash": "^4.14.181", diff --git a/workbench-core/authentication/src/authenticationMiddleware.test.ts b/workbench-core/authentication/src/authenticationMiddleware.test.ts index 9709f98be0..43a114394e 100644 --- a/workbench-core/authentication/src/authenticationMiddleware.test.ts +++ b/workbench-core/authentication/src/authenticationMiddleware.test.ts @@ -5,14 +5,17 @@ jest.mock('./authenticationService'); jest.mock('./plugins/cognitoAuthenticationPlugin'); +jest.mock('csurf'); import { LoggingService } from '@aws/workbench-core-logging'; +import csrf from 'csurf'; import { NextFunction, Request, Response } from 'express'; import { tokens } from './__mocks__/authenticationService'; import { AuthenticationService, CognitoAuthenticationPlugin, CognitoAuthenticationPluginOptions, + csurf, getAuthorizationCodeUrl, getTokensFromAuthorizationCode, IdpUnavailableError, @@ -35,7 +38,9 @@ const defaultCookieOpts = { sameSite: 'strict' } as const; -describe('authenticationMiddleware integration tests', () => { +const csrfToken = 'csrfToken'; + +describe('authenticationMiddleware tests', () => { let authenticationService: AuthenticationService; let loggingService: LoggingService; let res: Response; @@ -58,6 +63,38 @@ describe('authenticationMiddleware integration tests', () => { } as unknown as Response; }); + describe('csurf tests', () => { + it('should return an instance of the csrf middleware configured to use default cookie options when sameSite parameter is undefined', () => { + csurf(); + + expect(csrf).toHaveBeenCalledWith({ cookie: defaultCookieOpts }); + }); + + it('should return an instance of the csrf middleware configured to use sameSite: "strict" when sameSite parameter is "strict"', () => { + const sameSite = 'strict'; + + csurf(sameSite); + + expect(csrf).toHaveBeenCalledWith({ cookie: { ...defaultCookieOpts, sameSite } }); + }); + + it('should return an instance of the csrf middleware configured to use sameSite: "lax" when sameSite parameter is "lax"', () => { + const sameSite = 'lax'; + + csurf(sameSite); + + expect(csrf).toHaveBeenCalledWith({ cookie: { ...defaultCookieOpts, sameSite } }); + }); + + it('should return an instance of the csrf middleware configured to use sameSite: "none" when sameSite parameter is "none"', () => { + const sameSite = 'none'; + + csurf(sameSite); + + expect(csrf).toHaveBeenCalledWith({ cookie: { ...defaultCookieOpts, sameSite } }); + }); + }); + describe('getTokensFromAuthorizationCode tests', () => { let getTokensFromAuthorizationCodeRouteHandler: (req: Request, res: Response) => Promise; @@ -311,7 +348,7 @@ describe('authenticationMiddleware integration tests', () => { getAuthorizationCodeUrlRouteHandler = getAuthorizationCodeUrl(authenticationService); }); - it('should return 200 and the authorization code url when the stateVerifier and codeChallenge params are valid', async () => { + it('should return 200 and the authorization code url and csrf token in the response body when the stateVerifier and codeChallenge params are valid', async () => { const stateVerifier = 'stateVerifier'; const codeChallenge = 'codeChallenge'; @@ -322,14 +359,16 @@ describe('authenticationMiddleware integration tests', () => { }, headers: { origin: 'https://www.fakewebsite.com' - } + }, + csrfToken: jest.fn(() => csrfToken) } as unknown as Request; await getAuthorizationCodeUrlRouteHandler(req, res); expect(res.status).toHaveBeenCalledWith(200); expect(res.json).toHaveBeenCalledWith({ - redirectUrl: `https://www.fakeurl.com/authorize?client_id=fake-id&response_type=code&scope=openid&redirect_uri=https://www.fakewebsite.com&state=${stateVerifier}&code_challenge_method=S256&code_challenge=${codeChallenge}` + signInUrl: `https://www.fakeurl.com/authorize?client_id=fake-id&response_type=code&scope=openid&redirect_uri=https://www.fakewebsite.com&state=${stateVerifier}&code_challenge_method=S256&code_challenge=${codeChallenge}`, + csrfToken }); }); @@ -408,6 +447,54 @@ describe('authenticationMiddleware integration tests', () => { expect(res.sendStatus).toHaveBeenCalledWith(400); }); + + it('should return 200 and the authorization code url and csrfToken in the response body when the csrf parameter is true', async () => { + getAuthorizationCodeUrlRouteHandler = getAuthorizationCodeUrl(authenticationService, { csrf: true }); + const stateVerifier = 'stateVerifier'; + const codeChallenge = 'codeChallenge'; + + const req: Request = { + query: { + stateVerifier, + codeChallenge + }, + headers: { + origin: 'https://www.fakewebsite.com' + }, + csrfToken: jest.fn(() => csrfToken) + } as unknown as Request; + + await getAuthorizationCodeUrlRouteHandler(req, res); + + expect(res.status).toHaveBeenCalledWith(200); + expect(res.json).toHaveBeenCalledWith({ + signInUrl: `https://www.fakeurl.com/authorize?client_id=fake-id&response_type=code&scope=openid&redirect_uri=https://www.fakewebsite.com&state=${stateVerifier}&code_challenge_method=S256&code_challenge=${codeChallenge}`, + csrfToken + }); + }); + + it('should return 200 and the authorization code url when the csrf parameter is false', async () => { + getAuthorizationCodeUrlRouteHandler = getAuthorizationCodeUrl(authenticationService, { csrf: false }); + const stateVerifier = 'stateVerifier'; + const codeChallenge = 'codeChallenge'; + + const req: Request = { + query: { + stateVerifier, + codeChallenge + }, + headers: { + origin: 'https://www.fakewebsite.com' + } + } as unknown as Request; + + await getAuthorizationCodeUrlRouteHandler(req, res); + + expect(res.status).toHaveBeenCalledWith(200); + expect(res.json).toHaveBeenCalledWith({ + signInUrl: `https://www.fakeurl.com/authorize?client_id=fake-id&response_type=code&scope=openid&redirect_uri=https://www.fakewebsite.com&state=${stateVerifier}&code_challenge_method=S256&code_challenge=${codeChallenge}` + }); + }); }); describe('verifyToken tests', () => { @@ -852,17 +939,18 @@ describe('authenticationMiddleware integration tests', () => { isUserLoggedInRouteHandler = isUserLoggedIn(authenticationService); }); - it('should return 200 and set loggedIn to true in the response body when the access_token cookie is present and valid', async () => { + it('should return 200 and set the csrfToken and loggedIn to true in the response body when the access_token cookie is present and valid', async () => { const req: Request = { cookies: { access_token: 'validToken' - } - } as Request; + }, + csrfToken: jest.fn(() => csrfToken) + } as unknown as Request; await isUserLoggedInRouteHandler(req, res); expect(res.status).toHaveBeenCalledWith(200); - expect(res.json).toHaveBeenCalledWith({ loggedIn: true }); + expect(res.json).toHaveBeenCalledWith({ loggedIn: true, csrfToken }); }); it('should return 200 and set loggedIn to false in the response body when the access_token cookie is present and invalid', async () => { @@ -878,17 +966,18 @@ describe('authenticationMiddleware integration tests', () => { expect(res.json).toHaveBeenCalledWith({ loggedIn: false }); }); - it('should return 200, set the access token as a cookie, set the id token in the response body, and set loggedIn to true in the response body when the access_token cookie is missing and the refresh_token cookie is present and valid', async () => { + it('should return 200, set the access token as a cookie, and set the id token, csrf token, and set loggedIn to true in the response body when the access_token cookie is missing and the refresh_token cookie is present and valid', async () => { const req: Request = { cookies: { refresh_token: 'validToken' - } - } as Request; + }, + csrfToken: jest.fn(() => csrfToken) + } as unknown as Request; await isUserLoggedInRouteHandler(req, res); expect(res.status).toHaveBeenCalledWith(200); - expect(res.json).toHaveBeenCalledWith({ idToken: tokens.idToken.token, loggedIn: true }); + expect(res.json).toHaveBeenCalledWith({ idToken: tokens.idToken.token, loggedIn: true, csrfToken }); expect(res.cookie).toHaveBeenCalledWith('access_token', tokens.accessToken.token, { ...defaultCookieOpts, maxAge: tokens.accessToken.expiresIn @@ -899,8 +988,9 @@ describe('authenticationMiddleware integration tests', () => { const req: Request = { cookies: { refresh_token: 'validToken' - } - } as Request; + }, + csrfToken: jest.fn(() => csrfToken) + } as unknown as Request; jest.spyOn(authenticationService, 'refreshAccessToken').mockResolvedValueOnce({ idToken: { @@ -914,7 +1004,7 @@ describe('authenticationMiddleware integration tests', () => { await isUserLoggedInRouteHandler(req, res); expect(res.status).toHaveBeenCalledWith(200); - expect(res.json).toHaveBeenCalledWith({ idToken: tokens.idToken.token, loggedIn: true }); + expect(res.json).toHaveBeenCalledWith({ idToken: tokens.idToken.token, loggedIn: true, csrfToken }); expect(res.cookie).toHaveBeenCalledWith('access_token', tokens.accessToken.token, { ...defaultCookieOpts }); @@ -991,6 +1081,45 @@ describe('authenticationMiddleware integration tests', () => { it('should return 200, set the access token as a cookie, set the id token in the response body, and set loggedIn to true in the response body when the sameSite option is set to "none"', async () => { isUserLoggedInRouteHandler = isUserLoggedIn(authenticationService, { sameSite: 'none' }); + const req: Request = { + cookies: { + refresh_token: 'validToken' + }, + csrfToken: jest.fn(() => csrfToken) + } as unknown as Request; + + await isUserLoggedInRouteHandler(req, res); + + expect(res.status).toHaveBeenCalledWith(200); + expect(res.json).toHaveBeenCalledWith({ idToken: tokens.idToken.token, loggedIn: true, csrfToken }); + expect(res.cookie).toHaveBeenCalledWith('access_token', tokens.accessToken.token, { + ...defaultCookieOpts, + sameSite: 'none', + maxAge: tokens.accessToken.expiresIn + }); + }); + + it('should return 200, set the access token as a cookie, and set the id token, csrf token, and set loggedIn to true in the response body when the csrf parameter is true', async () => { + isUserLoggedInRouteHandler = isUserLoggedIn(authenticationService, { csrf: true }); + const req: Request = { + cookies: { + refresh_token: 'validToken' + }, + csrfToken: jest.fn(() => csrfToken) + } as unknown as Request; + + await isUserLoggedInRouteHandler(req, res); + + expect(res.status).toHaveBeenCalledWith(200); + expect(res.json).toHaveBeenCalledWith({ idToken: tokens.idToken.token, loggedIn: true, csrfToken }); + expect(res.cookie).toHaveBeenCalledWith('access_token', tokens.accessToken.token, { + ...defaultCookieOpts, + maxAge: tokens.accessToken.expiresIn + }); + }); + + it('should return 200, set the access token as a cookie, and set the id token and set loggedIn to true in the response body when the csrf parameter is false', async () => { + isUserLoggedInRouteHandler = isUserLoggedIn(authenticationService, { csrf: false }); const req: Request = { cookies: { refresh_token: 'validToken' @@ -1003,7 +1132,6 @@ describe('authenticationMiddleware integration tests', () => { expect(res.json).toHaveBeenCalledWith({ idToken: tokens.idToken.token, loggedIn: true }); expect(res.cookie).toHaveBeenCalledWith('access_token', tokens.accessToken.token, { ...defaultCookieOpts, - sameSite: 'none', maxAge: tokens.accessToken.expiresIn }); }); diff --git a/workbench-core/authentication/src/authenticationMiddleware.ts b/workbench-core/authentication/src/authenticationMiddleware.ts index 9c9e64f398..f2921f789c 100644 --- a/workbench-core/authentication/src/authenticationMiddleware.ts +++ b/workbench-core/authentication/src/authenticationMiddleware.ts @@ -5,6 +5,7 @@ import { AuthenticatedUser, RoutesIgnored } from '@aws/workbench-core-authorization'; import { LoggingService } from '@aws/workbench-core-logging'; +import csrf from 'csurf'; import { Request, Response, NextFunction, CookieOptions } from 'express'; import get from 'lodash/get'; import has from 'lodash/has'; @@ -17,6 +18,40 @@ const defaultCookieOptions: CookieOptions = { sameSite: 'strict' }; +/** + * An Express middleware function used to add csrf protection to an Express app. + * Uses Express's [csurf](http://expressjs.com/en/resources/middleware/csurf.html) library with the cookie implementation. + * + * This function assumes: + * - the middleware is mounted using `app.use()` + * - the csrf token returned by the `getAuthorizationCodeUrl` or `isUserLoggedIn` route handler is included in all requests in one of the following colations: + * - req.body._csrf + * - req.query._csrf + * - req.headers['csrf-token'] + * - req.headers['xsrf-token'] + * - req.headers['x-csrf-token'] + * - req.headers['x-xsrf-token'] + + * + * @param sameSite - (optional) the csrf cookie's `sameSite` value. Defaults to `'strict'` if not included + * @returns the middleware function + * + * @example + * ``` + * app.use(csurf()); + * ``` + */ +export function csurf( + sameSite?: 'none' | 'lax' | 'strict' +): (req: Request, res: Response, next: NextFunction) => void { + return csrf({ + cookie: { + ...defaultCookieOptions, + sameSite: sameSite ?? defaultCookieOptions.sameSite + } + }); +} + /** * An Express route handler function used to exchange the authorization code received from the authentication server for authentication tokens. * This route places the access token and refresh token, if it exists, into http only, secure, same site strict cookies and returns the id token @@ -33,7 +68,7 @@ const defaultCookieOptions: CookieOptions = { * * @example * ``` - * app.get('tokens', getTokensFromAuthorizationCode(authenticationService)); + * app.post('token', getTokensFromAuthorizationCode(authenticationService)); * ``` */ export function getTokensFromAuthorizationCode( @@ -96,29 +131,36 @@ export function getTokensFromAuthorizationCode( * - the request origin header exists * * @param authenticationService - a configured {@link AuthenticationService} instance + * @param options - object containing optional csrf parameter * @returns the route handler function * * @example * ``` - * app.get('codeUrl', getAuthorizationCodeUrl(authenticationService)); + * app.get('login', getAuthorizationCodeUrl(authenticationService)); * ``` */ export function getAuthorizationCodeUrl( - authenticationService: AuthenticationService + authenticationService: AuthenticationService, + options?: { csrf?: boolean } ): (req: Request, res: Response) => Promise { return async function (req: Request, res: Response) { + const { csrf } = options || {}; const stateVerifier = req.query.stateVerifier; const codeChallenge = req.query.codeChallenge; const websiteUrl = req.headers.origin; + const includeCsrfToken = csrf ?? true; if ( typeof stateVerifier === 'string' && typeof codeChallenge === 'string' && typeof websiteUrl === 'string' ) { - res.status(200).json({ - redirectUrl: authenticationService.getAuthorizationCodeUrl(stateVerifier, codeChallenge, websiteUrl) - }); + const data = { + signInUrl: authenticationService.getAuthorizationCodeUrl(stateVerifier, codeChallenge, websiteUrl), + csrfToken: includeCsrfToken ? req.csrfToken() : undefined + }; + + res.status(200).json(data); } else { res.sendStatus(400); } @@ -134,7 +176,7 @@ export function getAuthorizationCodeUrl( * - the access token is stored in a cookie named `access_token` * * @param authenticationService - a configured {@link AuthenticationService} instance - * @param options - an options object containing optional routes to ignore and logging service parameters + * @param options - object containing optional routes to ignore and logging service parameters * @returns the middleware function * * @example @@ -191,7 +233,7 @@ export function verifyToken( * * @example * ``` - * app.get('logout', logoutUser(authenticationService)); + * app.post('logout', logoutUser(authenticationService)); * ``` */ export function logoutUser( @@ -299,7 +341,7 @@ export function refreshAccessToken( * - if there is a refresh token, it is stored in a cookie named `refresh_token` * * @param authenticationService - a configured {@link AuthenticationService} instance - * @param options - object containing optional sameSite cookie and logging service parameters + * @param options - object containing optional sameSite cookie, csrf, and logging service parameters * @returns the route handler function * * @example @@ -309,12 +351,13 @@ export function refreshAccessToken( */ export function isUserLoggedIn( authenticationService: AuthenticationService, - options?: { loggingService?: LoggingService; sameSite?: 'none' | 'lax' | 'strict' } + options?: { loggingService?: LoggingService; sameSite?: 'none' | 'lax' | 'strict'; csrf?: boolean } ): (req: Request, res: Response) => Promise { return async function (req: Request, res: Response) { - const { loggingService, sameSite } = options || {}; + const { loggingService, sameSite, csrf } = options || {}; const accessToken = req.cookies.access_token; const refreshToken = req.cookies.refresh_token; + const includeCsrfToken = csrf ?? true; try { if (typeof refreshToken === 'string') { @@ -327,11 +370,22 @@ export function isUserLoggedIn( maxAge: accessToken.expiresIn }); - res.status(200).json({ idToken: idToken.token, loggedIn: true }); + const data = { + idToken: idToken.token, + loggedIn: true, + csrfToken: includeCsrfToken ? req.csrfToken() : undefined + }; + + res.status(200).json(data); } else if (typeof accessToken === 'string') { const loggedIn = await authenticationService.isUserLoggedIn(accessToken); - res.status(200).json({ loggedIn }); + const data = { + loggedIn, + csrfToken: includeCsrfToken && loggedIn ? req.csrfToken() : undefined + }; + + res.status(200).json(data); } else { res.status(200).json({ loggedIn: false }); } diff --git a/workbench-core/authentication/src/index.ts b/workbench-core/authentication/src/index.ts index 5e370f19be..2b400a58bb 100644 --- a/workbench-core/authentication/src/index.ts +++ b/workbench-core/authentication/src/index.ts @@ -34,6 +34,7 @@ export { DecodedJWT } from './decodedJWT'; export { Tokens } from './tokens'; export { getTimeInMS, TimeUnits } from './utils'; export { + csurf, getAuthorizationCodeUrl, getTokensFromAuthorizationCode, isUserLoggedIn, From e210e1d6d91d4632f303a0c6cc5a2dea16e20fd9 Mon Sep 17 00:00:00 2001 From: bhudnell Date: Wed, 24 Aug 2022 18:51:22 +0000 Subject: [PATCH 030/621] docs: Auto README.md updates [skip ci] --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 35b00ba217..fcc5818e51 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ # Code Coverage | Statements | Branches | Functions | Lines | | --------------------------- | ----------------------- | ------------------------- | ----------------- | -| ![Statements](https://img.shields.io/badge/statements-87.17%25-yellow.svg?style=flat) | ![Branches](https://img.shields.io/badge/branches-85.03%25-yellow.svg?style=flat) | ![Functions](https://img.shields.io/badge/functions-88.52%25-yellow.svg?style=flat) | ![Lines](https://img.shields.io/badge/lines-87.67%25-yellow.svg?style=flat) | +| ![Statements](https://img.shields.io/badge/statements-87.21%25-yellow.svg?style=flat) | ![Branches](https://img.shields.io/badge/branches-85.31%25-yellow.svg?style=flat) | ![Functions](https://img.shields.io/badge/functions-88.55%25-yellow.svg?style=flat) | ![Lines](https://img.shields.io/badge/lines-87.71%25-yellow.svg?style=flat) | # Contributing Guidelines From c59d646027743b21e0e0e9c0047cd2eb0480b94b Mon Sep 17 00:00:00 2001 From: Travis Berkley <78979861+travberk@users.noreply.github.com> Date: Thu, 25 Aug 2022 16:45:58 -0500 Subject: [PATCH 031/621] docs: Adding /doc folder and workspace troubleshooting files. (#475) Co-authored-by: Travis Berkley Co-authored-by: Tim Nguyen Co-authored-by: Brendon Hudnell --- solutions/swb-reference/README.md | 4 +++ .../WorkspaceLifecycleTroubleshootingGuide.md | 31 ++++++++++++++++++ .../docs/launch-terminate-workflow.jpg | Bin 0 -> 88179 bytes .../docs/start-stop-workflow.jpg | Bin 0 -> 75339 bytes 4 files changed, 35 insertions(+) create mode 100644 solutions/swb-reference/docs/WorkspaceLifecycleTroubleshootingGuide.md create mode 100644 solutions/swb-reference/docs/launch-terminate-workflow.jpg create mode 100644 solutions/swb-reference/docs/start-stop-workflow.jpg diff --git a/solutions/swb-reference/README.md b/solutions/swb-reference/README.md index 33bde48b5a..12d691d0cb 100644 --- a/solutions/swb-reference/README.md +++ b/solutions/swb-reference/README.md @@ -111,6 +111,10 @@ Go to `solutions/swb-app` to update `staticRouteConfig.ts` and `staticPermission 3. Run `STAGE= node generateCognitoTokens.js ''` with the correct value for `` and ``. It should be a user that has been created for your SWB deployment. Note, the quotes around `` is necessary for the script to correctly parse passwords that have symbols in it. 4. In the console output, use the `accessToken` that is provided to make authenticated API requests. +## Troubleshooting Guides + +[Workspace Lifecycle Troubleshooting Guide](docs/WorkspaceLifecycleTroubleshootingGuide.md). + ## Appendix ### Cloudwatch Logs * `swb---apiLambda`: Logs for api lambda. This lambda gets executed when user makes a request to SWB APIs. diff --git a/solutions/swb-reference/docs/WorkspaceLifecycleTroubleshootingGuide.md b/solutions/swb-reference/docs/WorkspaceLifecycleTroubleshootingGuide.md new file mode 100644 index 0000000000..ac0008f672 --- /dev/null +++ b/solutions/swb-reference/docs/WorkspaceLifecycleTroubleshootingGuide.md @@ -0,0 +1,31 @@ +# Workspace Lifecycle Troubleshooting Guide + + +## Launching/Terminating a Workspace + +![Diagram 1 - worfklow for Launching/Terminating a workspace](./launch-terminate-workflow.jpg) + +A user will initiate the request through the UI, which in turn makes an API request. The API Gateway calls the "apiHandler" Lambda function. Each execution will execute a Systems Manager Automation within the hosting account. It will also make a new entry in the DynamoDB table named `swb--` For example, the table might be named `swb-dev-va`. (See item 1 in the diagram.) + +In the hosting account, there will be an Execution ID entry in Systems Manager under Automation. (See item 2 in diagram.) The document will end in "Launch" or "Terminate" depending on the action requested. The contents of this log detail which steps were executed successfuly and which, if any, failed. + +One of the steps in the Systems Manager Execution creates or deletes a CloudFormation stack in the hosting account, depending on the request. The stack will be named `SC--pp-`. (See item 3 in the diagram.) If successful, the Outputs tab will show values such as the resource ARN, instance names, etc. If the stack fails to deploy or delete, look for details under the "Events" tab. + +Some workspace types will create log files within CloudWatch under the hosting account. (See item 4 in diagram.) For example, if your workspace type is SageMaker, you can find a log group named `/aws/sagemaker/NotebookInstances` that contain detailed information about the workspace. The information contained in these logs will vary by workspace. + +As the workspace transitions, it sends events to EventBridge. The Hosting account EventBridge sends these events to the Main account, where an EventBridge rule calls the "statusHandler" Lambda function in the Main account. This Lambda creates a CloudWatch log in the group name that begins with `/aws/lambda/swb---statusHandlerLambda`. For example, the log group may be named `/aws/lambda/swb-dev-va-statusHandlerLambda`. (See item 5 in the diagram.) The log entries will depend on the type of workspace being created. For example, there will be two types of log entries for a SageMaker workspace. There will be INFO records showing a status of `CREATE_IN_PROGRESS` and finally `InService` once provisioning is complete. Any errors will also be shown in this log. + +The "statusHandler" Lambda function is also responsible for updating the DynamoDB table entries for the workspace. (See item 1 in the diagram.) The DynamoDB values are reflected in the UI as the workspace's status for the user to see. + + +## Starting/Stopping a Workspace ## + +![Diagram 2 - worfklow for Starting/Stopping a workspace](./start-stop-workflow.jpg) + +A user will initiate the request through the UI, which will make an API request. In turn, the Lambda function "apiLambda" will be called. Each execution will generate a CloudWatch log in the group name that begins with `/aws/lambda/swb---apiLambda`. For example, the log group may be named `/aws/lambda/swb-dev-va-apiHandler`. (See item 1 in the diagram.) If there are any errors when the Lambda function assumes the role in the Hosting account, they will be logged here. + +The "apiLambda" function will then start or stop the workspace directly. Some workspace types have log files within CloudWatch under the hosting account. (See item 2 in diagram.) For example, if your workspace type is SageMaker, you can find a log group named `/aws/sagemaker/NotebookInstances` that contain detailed information about the workspace. The information contained in these logs will vary by workspace. + +As the workspace transitions, it will send events to EventBridge. The Hosting account EventBridge sends these events to the Main account, where an EventBridge rule calls the "statusHandler" Lambda function. This Lambda creates a CloudWatch log in the group name that begins with `/aws/lambda/swb---statusHandlerLambda`. For example, the log group may be named `/aws/lambda/swb-dev-va-statusHandlerLambda`. (See item 3 in the diagram.) The log entries will depend on the type of workspace being modified. For example, there will be two log entries for a SageMaker workspace. The INFO for the first will show a status of `Pending` for starts and "Stopping" for stops. The second INFO entry will show a status of "InService" or `Stopped`. + +The "statusHandler" Lambda function is also responsible for updating the DynamoDB table entries for the workspace. It will update the items in the DynamoDB table named `swb--` For example, the table might be named `swb-dev-va`. (See item 4 in the diagram.) The DynamoDB values are reflected in the UI as the workspace's status for the user to see. diff --git a/solutions/swb-reference/docs/launch-terminate-workflow.jpg b/solutions/swb-reference/docs/launch-terminate-workflow.jpg new file mode 100644 index 0000000000000000000000000000000000000000..3fbaf079f2aaf21ded8359ef46f3508da83b4bde GIT binary patch literal 88179 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�!>c3l`nMXAzzQJN5nbfl_C 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 literal 0 HcmV?d00001 diff --git a/solutions/swb-reference/docs/start-stop-workflow.jpg b/solutions/swb-reference/docs/start-stop-workflow.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a4643c4f3ac26b0b65b31b65d7df533f7e2ae838 GIT binary patch literal 75339 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 LR80fy|8D{SBg5L6 literal 0 HcmV?d00001 From f1ecbfd1d3cc00de6886573fccd32071bb4db0c1 Mon Sep 17 00:00:00 2001 From: Brendon Hudnell Date: Thu, 25 Aug 2022 16:18:30 -0600 Subject: [PATCH 032/621] fix: added csrf to integration tests and postman collection (#480) --- .../rush/browser-approved-packages.json | 4 + common/config/rush/pnpm-lock.yaml | 2 + solutions/swb-reference/README.md | 2 +- solutions/swb-reference/SETUP_v2p1.md | 4 +- .../SWBv2.postman_collection.json | 147 +++++++++++++++--- .../support/clientSession.ts | 10 +- solutions/swb-reference/package.json | 1 + .../scripts/generateCognitoTokens.js | 11 +- 8 files changed, 154 insertions(+), 27 deletions(-) diff --git a/common/config/rush/browser-approved-packages.json b/common/config/rush/browser-approved-packages.json index e053b9a25e..fb44aa5fdc 100644 --- a/common/config/rush/browser-approved-packages.json +++ b/common/config/rush/browser-approved-packages.json @@ -294,6 +294,10 @@ "name": "cors", "allowedCategories": [ "production" ] }, + { + "name": "csrf", + "allowedCategories": [ "production" ] + }, { "name": "csurf", "allowedCategories": [ "production" ] diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index 3ef63802e8..f999bad4e6 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -218,6 +218,7 @@ importers: axios: ^0.27.1 cdk-ssm-document: ^3.1.1 constructs: ^10.0.0 + csrf: ^3.1.0 depcheck: ^1.4.3 esbuild: ^0.14.21 eslint: ^8.7.0 @@ -255,6 +256,7 @@ importers: '@aws/workbench-core-logging': link:../../workbench-core/logging '@vendia/serverless-express': 4.8.0 aws-sdk-client-mock: 0.6.2_e3d47e7de0bdd7e8ce04c01f4d10cebe + csrf: 3.1.0 express: 4.18.1 js-yaml: 4.1.0 lodash: 4.17.21 diff --git a/solutions/swb-reference/README.md b/solutions/swb-reference/README.md index 12d691d0cb..c79e37541e 100644 --- a/solutions/swb-reference/README.md +++ b/solutions/swb-reference/README.md @@ -109,7 +109,7 @@ Go to `solutions/swb-app` to update `staticRouteConfig.ts` and `staticPermission 1. Go to `swb-reference/scripts` folder 2. Pull down all required dependencies by running `rushx build` 3. Run `STAGE= node generateCognitoTokens.js ''` with the correct value for `` and ``. It should be a user that has been created for your SWB deployment. Note, the quotes around `` is necessary for the script to correctly parse passwords that have symbols in it. -4. In the console output, use the `accessToken` that is provided to make authenticated API requests. +4. In the console output, use the `accessToken`, `csrfCookie`, and `csrfToken` that are provided to make authenticated API requests. ## Troubleshooting Guides diff --git a/solutions/swb-reference/SETUP_v2p1.md b/solutions/swb-reference/SETUP_v2p1.md index 10baa71f3f..cbc2682fda 100644 --- a/solutions/swb-reference/SETUP_v2p1.md +++ b/solutions/swb-reference/SETUP_v2p1.md @@ -108,11 +108,11 @@ VpcSubnet ## Get access token -To get the `access_token` for making authenticated API requests please refer [here](README.md#obtain-access-token-for-making-authenticated-api-requests). +To get the `accessToken`, `csrfCookie`, and `csrfToken` for making authenticated API requests please refer [here](README.md#obtain-access-token-for-making-authenticated-api-requests). ## POSTMAN Setup In POSTMAN create an environment using the instructions [here](https://learning.postman.com/docs/sending-requests/managing-environments/#creating-environments). -Your environment should have two variables. Name the first one `API_URL` and the value should be the `APIGatewayAPIEndpoint` value that you got when deploying the `Main Account`. Name the second one `ACCESS_TOKEN` and the value should be the `accessToken` you got from [Setup UI and Get Access Token](#setup-ui-and-get-access-token) +Your environment should have four variables. Name the first one `API_URL` and the value should be the `APIGatewayAPIEndpoint` value that you got when deploying the `Main Account`. Name the second, third and fourth ones `ACCESS_TOKEN`, `CSRF_COOKIE`, and `CSRF_TOKEN` and their values should be the `accessToken`, `csrfCookie`, and `csrfToken` you got from [Setup UI and Get Access Token](#setup-ui-and-get-access-token) Import [SWBv2 Postman Collection](./SWBv2.postman_collection.json). Instructions for how to import a collection is [here](https://learning.postman.com/docs/getting-started/importing-and-exporting-data/#importing-data-into-postman) diff --git a/solutions/swb-reference/SWBv2.postman_collection.json b/solutions/swb-reference/SWBv2.postman_collection.json index c587756736..3c22e4b52c 100644 --- a/solutions/swb-reference/SWBv2.postman_collection.json +++ b/solutions/swb-reference/SWBv2.postman_collection.json @@ -16,7 +16,12 @@ "header": [ { "key": "Cookie", - "value": "access_token={{ACCESS_TOKEN}}", + "value": "access_token={{ACCESS_TOKEN}};_csrf={{CSRF_COOKIE}}", + "type": "text" + }, + { + "key": "csrf-token", + "value": "{{CSRF_TOKEN}}", "type": "text" } ], @@ -44,7 +49,12 @@ "header": [ { "key": "Cookie", - "value": "access_token={{ACCESS_TOKEN}}", + "value": "access_token={{ACCESS_TOKEN}};_csrf={{CSRF_COOKIE}}", + "type": "text" + }, + { + "key": "csrf-token", + "value": "{{CSRF_TOKEN}}", "type": "text" } ], @@ -75,7 +85,12 @@ "header": [ { "key": "Cookie", - "value": "access_token={{ACCESS_TOKEN}}", + "value": "access_token={{ACCESS_TOKEN}};_csrf={{CSRF_COOKIE}}", + "type": "text" + }, + { + "key": "csrf-token", + "value": "{{CSRF_TOKEN}}", "type": "text" } ], @@ -103,7 +118,12 @@ "header": [ { "key": "Cookie", - "value": "access_token={{ACCESS_TOKEN}}", + "value": "access_token={{ACCESS_TOKEN}};_csrf={{CSRF_COOKIE}}", + "type": "text" + }, + { + "key": "csrf-token", + "value": "{{CSRF_TOKEN}}", "type": "text" } ], @@ -136,7 +156,12 @@ "header": [ { "key": "Cookie", - "value": "access_token={{ACCESS_TOKEN}}", + "value": "access_token={{ACCESS_TOKEN}};_csrf={{CSRF_COOKIE}}", + "type": "text" + }, + { + "key": "csrf-token", + "value": "{{CSRF_TOKEN}}", "type": "text" } ], @@ -162,7 +187,12 @@ "header": [ { "key": "Cookie", - "value": "access_token={{ACCESS_TOKEN}}", + "value": "access_token={{ACCESS_TOKEN}};_csrf={{CSRF_COOKIE}}", + "type": "text" + }, + { + "key": "csrf-token", + "value": "{{CSRF_TOKEN}}", "type": "text" } ], @@ -190,7 +220,12 @@ "header": [ { "key": "Cookie", - "value": "access_token={{ACCESS_TOKEN}}", + "value": "access_token={{ACCESS_TOKEN}};_csrf={{CSRF_COOKIE}}", + "type": "text" + }, + { + "key": "csrf-token", + "value": "{{CSRF_TOKEN}}", "type": "text" } ], @@ -216,7 +251,12 @@ "header": [ { "key": "Cookie", - "value": "access_token={{ACCESS_TOKEN}}", + "value": "access_token={{ACCESS_TOKEN}};_csrf={{CSRF_COOKIE}}", + "type": "text" + }, + { + "key": "csrf-token", + "value": "{{CSRF_TOKEN}}", "type": "text" } ], @@ -242,7 +282,12 @@ "header": [ { "key": "Cookie", - "value": "access_token={{ACCESS_TOKEN}}", + "value": "access_token={{ACCESS_TOKEN}};_csrf={{CSRF_COOKIE}}", + "type": "text" + }, + { + "key": "csrf-token", + "value": "{{CSRF_TOKEN}}", "type": "text" } ], @@ -268,7 +313,12 @@ "header": [ { "key": "Cookie", - "value": "access_token={{ACCESS_TOKEN}}", + "value": "access_token={{ACCESS_TOKEN}};_csrf={{CSRF_COOKIE}}", + "type": "text" + }, + { + "key": "csrf-token", + "value": "{{CSRF_TOKEN}}", "type": "text" } ], @@ -344,7 +394,12 @@ "header": [ { "key": "Cookie", - "value": "access_token={{ACCESS_TOKEN}}", + "value": "access_token={{ACCESS_TOKEN}};_csrf={{CSRF_COOKIE}}", + "type": "text" + }, + { + "key": "csrf-token", + "value": "{{CSRF_TOKEN}}", "type": "text" } ], @@ -375,7 +430,12 @@ "header": [ { "key": "Cookie", - "value": "access_token={{ACCESS_TOKEN}}", + "value": "access_token={{ACCESS_TOKEN}};_csrf={{CSRF_COOKIE}}", + "type": "text" + }, + { + "key": "csrf-token", + "value": "{{CSRF_TOKEN}}", "type": "text" } ], @@ -403,7 +463,12 @@ "header": [ { "key": "Cookie", - "value": "access_token={{ACCESS_TOKEN}}", + "value": "access_token={{ACCESS_TOKEN}};_csrf={{CSRF_COOKIE}}", + "type": "text" + }, + { + "key": "csrf-token", + "value": "{{CSRF_TOKEN}}", "type": "text" } ], @@ -437,7 +502,12 @@ "header": [ { "key": "Cookie", - "value": "access_token={{ACCESS_TOKEN}}", + "value": "access_token={{ACCESS_TOKEN}};_csrf={{CSRF_COOKIE}}", + "type": "text" + }, + { + "key": "csrf-token", + "value": "{{CSRF_TOKEN}}", "type": "text" } ], @@ -456,7 +526,12 @@ "header": [ { "key": "Cookie", - "value": "access_token={{ACCESS_TOKEN}}", + "value": "access_token={{ACCESS_TOKEN}};_csrf={{CSRF_COOKIE}}", + "type": "text" + }, + { + "key": "csrf-token", + "value": "{{CSRF_TOKEN}}", "type": "text" } ], @@ -487,7 +562,12 @@ "header": [ { "key": "Cookie", - "value": "access_token={{ACCESS_TOKEN}}", + "value": "access_token={{ACCESS_TOKEN}};_csrf={{CSRF_COOKIE}}", + "type": "text" + }, + { + "key": "csrf-token", + "value": "{{CSRF_TOKEN}}", "type": "text" } ], @@ -522,7 +602,12 @@ "header": [ { "key": "Cookie", - "value": "access_token={{ACCESS_TOKEN}}", + "value": "access_token={{ACCESS_TOKEN}};_csrf={{CSRF_COOKIE}}", + "type": "text" + }, + { + "key": "csrf-token", + "value": "{{CSRF_TOKEN}}", "type": "text" } ], @@ -562,7 +647,12 @@ "header": [ { "key": "Cookie", - "value": "access_token={{ACCESS_TOKEN}}", + "value": "access_token={{ACCESS_TOKEN}};_csrf={{CSRF_COOKIE}}", + "type": "text" + }, + { + "key": "csrf-token", + "value": "{{CSRF_TOKEN}}", "type": "text" } ], @@ -593,7 +683,12 @@ "header": [ { "key": "Cookie", - "value": "access_token={{ACCESS_TOKEN}}", + "value": "access_token={{ACCESS_TOKEN}};_csrf={{CSRF_COOKIE}}", + "type": "text" + }, + { + "key": "csrf-token", + "value": "{{CSRF_TOKEN}}", "type": "text" } ], @@ -636,7 +731,12 @@ "header": [ { "key": "Cookie", - "value": "access_token={{ACCESS_TOKEN}}", + "value": "access_token={{ACCESS_TOKEN}};_csrf={{CSRF_COOKIE}}", + "type": "text" + }, + { + "key": "csrf-token", + "value": "{{CSRF_TOKEN}}", "type": "text" } ], @@ -657,7 +757,12 @@ "header": [ { "key": "Cookie", - "value": "access_token={{ACCESS_TOKEN}}", + "value": "access_token={{ACCESS_TOKEN}};_csrf={{CSRF_COOKIE}}", + "type": "text" + }, + { + "key": "csrf-token", + "value": "{{CSRF_TOKEN}}", "type": "text" } ], diff --git a/solutions/swb-reference/integration-tests/support/clientSession.ts b/solutions/swb-reference/integration-tests/support/clientSession.ts index 7ead20697c..ecf2f1e1cf 100644 --- a/solutions/swb-reference/integration-tests/support/clientSession.ts +++ b/solutions/swb-reference/integration-tests/support/clientSession.ts @@ -3,6 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ import axios, { AxiosError, AxiosInstance, AxiosResponse } from 'axios'; +import Csrf from 'csrf'; import _ from 'lodash'; import { getResources, Resources } from './resources'; import Setup from './setup'; @@ -26,11 +27,16 @@ export default class ClientSession { const headers: { 'Content-Type': string; Cookie?: string; + 'csrf-token'?: string; } = { 'Content-Type': 'application/json' }; - // For anonymous sessions, authorization header is not required + // For anonymous sessions, access token cookie is not required if (!this._isAnonymousSession) { - headers.Cookie = `access_token=${accessToken}`; + const csrf = new Csrf(); + const secret = csrf.secretSync(); + const token = csrf.create(secret); + headers.Cookie = `access_token=${accessToken};_csrf=${secret};`; + headers['csrf-token'] = token; } this._axiosInstance = axios.create({ diff --git a/solutions/swb-reference/package.json b/solutions/swb-reference/package.json index a20e437a44..a441c52eb4 100644 --- a/solutions/swb-reference/package.json +++ b/solutions/swb-reference/package.json @@ -51,6 +51,7 @@ "@aws/workbench-core-logging": "workspace:*", "@vendia/serverless-express": "^4.5.4", "aws-sdk-client-mock": "^0.6.2", + "csrf": "^3.1.0", "express": "^4.17.3", "js-yaml": "^4.1.0", "lodash": "^4.17.21", diff --git a/solutions/swb-reference/scripts/generateCognitoTokens.js b/solutions/swb-reference/scripts/generateCognitoTokens.js index c5876a473b..b2b5a3e6ba 100755 --- a/solutions/swb-reference/scripts/generateCognitoTokens.js +++ b/solutions/swb-reference/scripts/generateCognitoTokens.js @@ -12,6 +12,7 @@ const { join } = require('path'); const yaml = require('js-yaml'); const fs = require('fs'); const { CognitoTokenService } = require('@aws/workbench-core-base'); +const Csrf = require('csrf'); const config = yaml.load( // __dirname is a variable that reference the current directory. We use it so we can dynamically navigate to the @@ -39,6 +40,10 @@ const region = config.awsRegion; const username = process.argv[2]; const password = process.argv[3]; +const csrf = new Csrf(); +const secret = csrf.secretSync(); +const token = csrf.create(secret); + async function run() { try { const cognitoTokenService = new CognitoTokenService(region); @@ -49,7 +54,11 @@ async function run() { undefined, password ); - console.log(tokens); + console.log({ + ...tokens, + csrfCookie: secret, + csrfToken: token + }); } catch (e) { console.error('Unable to get cognito tokens', e); } From 110c9cd066eba2d19f184214254ca8a826c33bd3 Mon Sep 17 00:00:00 2001 From: FernandoAranda Date: Tue, 30 Aug 2022 09:43:55 -0700 Subject: [PATCH 033/621] feat: cypress integration (#481) * feat(e2e test): add Cypress to swb-reference * chore(e2e tests): add rush changes * feat(e2e test): update documentation and fix cypress known issue * chore(e2e test): fix pipeline issue * chore(e2e tests): fix typos and remove wait methods in cypress * docs(e2e tests): fix typo * chore(e2e tests): add attribute search for profile dropdown menu * chore(e2e tests): remove unecessary validations Co-authored-by: Fernando Aranda --- .gitignore | 6 + .../rush/browser-approved-packages.json | 4 + common/config/rush/pnpm-lock.yaml | 580 +++++++++++++++++- solutions/swb-reference/README.md | 76 +++ solutions/swb-reference/cypress.config.ts | 59 ++ .../end-to-end-tests/config/example.yaml | 2 + .../cypress/common/login.cy.ts | 5 + .../cypress/fixtures/example.json | 5 + .../cypress/support/commands.ts | 81 +++ .../cypress/support/constants.ts | 7 + .../end-to-end-tests/cypress/support/e2e.ts | 20 + solutions/swb-reference/package.json | 3 + solutions/swb-ui/src/components/Login.tsx | 7 +- .../swb-ui/src/pages/environments/index.tsx | 1 + 14 files changed, 845 insertions(+), 11 deletions(-) create mode 100644 solutions/swb-reference/cypress.config.ts create mode 100644 solutions/swb-reference/end-to-end-tests/config/example.yaml create mode 100644 solutions/swb-reference/end-to-end-tests/cypress/common/login.cy.ts create mode 100644 solutions/swb-reference/end-to-end-tests/cypress/fixtures/example.json create mode 100644 solutions/swb-reference/end-to-end-tests/cypress/support/commands.ts create mode 100644 solutions/swb-reference/end-to-end-tests/cypress/support/constants.ts create mode 100644 solutions/swb-reference/end-to-end-tests/cypress/support/e2e.ts diff --git a/.gitignore b/.gitignore index 8830977ff0..6343655ee6 100644 --- a/.gitignore +++ b/.gitignore @@ -99,6 +99,12 @@ solutions/swb-reference/integration-tests/config/*.yaml !solutions/swb-reference/src/config/testEnv.yaml solutions/swb-reference/src/config/*.json solutions/swb-reference/.aws-sam/ +solutions/swb-reference/end-to-end-tests/config/*.yaml +!solutions/swb-reference/end-to-end-tests/config/example.yaml +solutions/swb-reference/end-to-end-tests/cypress/screenshots +solutions/swb-reference/end-to-end-tests/cypress/videos +solutions/swb-reference/end-to-end-tests/cypress/downloads + # local environment variables .env*.local diff --git a/common/config/rush/browser-approved-packages.json b/common/config/rush/browser-approved-packages.json index fb44aa5fdc..1075198f23 100644 --- a/common/config/rush/browser-approved-packages.json +++ b/common/config/rush/browser-approved-packages.json @@ -294,6 +294,10 @@ "name": "cors", "allowedCategories": [ "production" ] }, + { + "name": "cypress", + "allowedCategories": [ "production" ] + }, { "name": "csrf", "allowedCategories": [ "production" ] diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index f999bad4e6..617dc1a5e0 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -218,6 +218,7 @@ importers: axios: ^0.27.1 cdk-ssm-document: ^3.1.1 constructs: ^10.0.0 + cypress: ^10.6.0 csrf: ^3.1.0 depcheck: ^1.4.3 esbuild: ^0.14.21 @@ -282,6 +283,7 @@ importers: axios: 0.27.2 cdk-ssm-document: 3.1.1_8d994d7bf27edab84c843ce1928354a1 constructs: 10.1.25 + cypress: 10.6.0 depcheck: 1.4.3 esbuild: 0.14.42 eslint: 8.16.0 @@ -5114,7 +5116,6 @@ packages: /@colors/colors/1.5.0: resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} engines: {node: '>=0.1.90'} - dev: false /@cspotcode/source-map-support/0.8.1: resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} @@ -5123,6 +5124,37 @@ packages: '@jridgewell/trace-mapping': 0.3.9 dev: true + /@cypress/request/2.88.10: + resolution: {integrity: sha512-Zp7F+R93N0yZyG34GutyTNr+okam7s/Fzc1+i3kcqOP8vk6OuajuE9qZJ6Rs+10/1JFtXFYMdyarnU1rZuJesg==} + engines: {node: '>= 6'} + dependencies: + aws-sign2: 0.7.0 + aws4: 1.11.0 + caseless: 0.12.0 + combined-stream: 1.0.8 + extend: 3.0.2 + forever-agent: 0.6.1 + form-data: 2.3.3 + http-signature: 1.3.6 + is-typedarray: 1.0.0 + isstream: 0.1.2 + json-stringify-safe: 5.0.1 + mime-types: 2.1.35 + performance-now: 2.1.0 + qs: 6.5.3 + safe-buffer: 5.2.1 + tough-cookie: 2.5.0 + tunnel-agent: 0.6.0 + uuid: 8.3.2 + dev: true + + /@cypress/xvfb/1.2.4: + resolution: {integrity: sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q==} + dependencies: + debug: 3.2.7 + lodash.once: 4.1.1 + dev: true + /@dabh/diagnostics/2.0.3: resolution: {integrity: sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==} dependencies: @@ -6741,6 +6773,14 @@ packages: dependencies: '@sinonjs/fake-timers': 7.1.2 + /@types/sinonjs__fake-timers/8.1.1: + resolution: {integrity: sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g==} + dev: true + + /@types/sizzle/2.3.3: + resolution: {integrity: sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ==} + dev: true + /@types/stack-utils/2.0.1: resolution: {integrity: sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==} dev: true @@ -6784,6 +6824,14 @@ packages: '@types/yargs-parser': 21.0.0 dev: true + /@types/yauzl/2.10.0: + resolution: {integrity: sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==} + requiresBuild: true + dependencies: + '@types/node': 14.18.20 + dev: true + optional: true + /@typescript-eslint/eslint-plugin/5.20.0_dce2f58b47902bf309a7c2e5b23210cc: resolution: {integrity: sha512-fapGzoxilCn3sBtC6NtXZX6+P/Hef7VDbyfGqTTpzYydwhlkevB+0vE0EnmHPVTVSy68GUncyJ/2PcrFBeCo5Q==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -7203,6 +7251,14 @@ packages: transitivePeerDependencies: - supports-color + /aggregate-error/3.1.0: + resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} + engines: {node: '>=8'} + dependencies: + clean-stack: 2.2.0 + indent-string: 4.0.0 + dev: true + /ajv-errors/1.0.1_ajv@6.12.6: resolution: {integrity: sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==} peerDependencies: @@ -7228,6 +7284,11 @@ packages: uri-js: 4.4.1 dev: true + /ansi-colors/4.1.3: + resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} + engines: {node: '>=6'} + dev: true + /ansi-escapes/4.3.2: resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} engines: {node: '>=8'} @@ -7288,6 +7349,10 @@ packages: resolution: {integrity: sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==} dev: true + /arch/2.2.0: + resolution: {integrity: sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==} + dev: true + /are-we-there-yet/1.1.7: resolution: {integrity: sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g==} dependencies: @@ -7398,6 +7463,17 @@ packages: /asap/2.0.6: resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} + /asn1/0.2.6: + resolution: {integrity: sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==} + dependencies: + safer-buffer: 2.1.2 + dev: true + + /assert-plus/1.0.0: + resolution: {integrity: sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==} + engines: {node: '>=0.8'} + dev: true + /assign-symbols/1.0.0: resolution: {integrity: sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==} engines: {node: '>=0.10.0'} @@ -7414,7 +7490,6 @@ packages: /async/3.2.3: resolution: {integrity: sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g==} - dev: false /asynckit/0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} @@ -7506,6 +7581,14 @@ packages: xml2js: 0.4.19 dev: true + /aws-sign2/0.7.0: + resolution: {integrity: sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==} + dev: true + + /aws4/1.11.0: + resolution: {integrity: sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==} + dev: true + /axe-core/4.4.2: resolution: {integrity: sha512-LVAaGp/wkkgYJcjmHsoKx4juT1aQvJyPcW09MLCjVTh3V2cc6PnyempiLMNH5iMdfIX/zdbjUx2KDjMLCTdPeA==} engines: {node: '>=12'} @@ -7698,6 +7781,12 @@ packages: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} dev: true + /bcrypt-pbkdf/1.0.2: + resolution: {integrity: sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==} + dependencies: + tweetnacl: 0.14.5 + dev: true + /better-path-resolve/1.0.0: resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} engines: {node: '>=4'} @@ -7718,6 +7807,14 @@ packages: readable-stream: 3.6.0 dev: true + /blob-util/2.0.2: + resolution: {integrity: sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ==} + dev: true + + /bluebird/3.7.2: + resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==} + dev: true + /body-parser/1.20.0: resolution: {integrity: sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} @@ -7795,6 +7892,10 @@ packages: node-int64: 0.4.0 dev: true + /buffer-crc32/0.2.13: + resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} + dev: true + /buffer-equal-constant-time/1.0.1: resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} dev: false @@ -7806,7 +7907,7 @@ packages: resolution: {integrity: sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==} dependencies: base64-js: 1.5.1 - ieee754: 1.1.13 + ieee754: 1.2.1 isarray: 1.0.0 dev: true @@ -7844,6 +7945,11 @@ packages: unset-value: 1.0.0 dev: true + /cachedir/2.3.0: + resolution: {integrity: sha512-A+Fezp4zxnit6FanDmv9EqXNAi3vt9DWp51/71UEhXukb7QUuvtv9344h91dyAxuTLoSYJFU299qzR3tzwPAhw==} + engines: {node: '>=6'} + dev: true + /call-bind/1.0.2: resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==} dependencies: @@ -7887,6 +7993,10 @@ packages: resolution: {integrity: sha512-mzDSXIPaFwVDvZAHqZ9VlbyF4yyXRuX6IvB06WvPYkqJVO24kX1PPhv9bfpKNFZyxYFmmgo03HUiD8iklmJYRQ==} engines: {node: '>= 0.8.0'} + /caseless/0.12.0: + resolution: {integrity: sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==} + dev: true + /cdk-nag/2.14.26_8d994d7bf27edab84c843ce1928354a1: resolution: {integrity: sha512-emOwacq+6kHgVcCqQWRGdznBYt+GlfYU0Bqh4q9XvvxCSXxRL0K5UKrTFDqXpcquvncryegh4Pa2UoFBVdOWgA==} peerDependencies: @@ -7945,6 +8055,11 @@ packages: resolution: {integrity: sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==} dev: true + /check-more-types/2.24.0: + resolution: {integrity: sha512-Pj779qHxV2tuapviy1bSZNEL1maXr13bPYpsvSDB68HlYcYuhlDrmGd63i0JHMCLKzc7rUSNIrpdJlhVlNwrxA==} + engines: {node: '>= 0.8.0'} + dev: true + /chokidar/3.4.3: resolution: {integrity: sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ==} engines: {node: '>= 8.10.0'} @@ -8010,12 +8125,16 @@ packages: static-extend: 0.1.2 dev: true + /clean-stack/2.2.0: + resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} + engines: {node: '>=6'} + dev: true + /cli-cursor/3.1.0: resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} engines: {node: '>=8'} dependencies: restore-cursor: 3.1.0 - dev: false /cli-table/0.3.11: resolution: {integrity: sha512-IqLQi4lO0nIB4tcdTpN4LCB9FI3uqrJZK7RC515EnhZ6qBaglkIgICb1wjeAqpdoOabm1+SuQtkXIPdYC93jhQ==} @@ -8024,6 +8143,23 @@ packages: colors: 1.0.3 dev: false + /cli-table3/0.6.2: + resolution: {integrity: sha512-QyavHCaIC80cMivimWu4aWHilIpiDpfm3hGmqAmXVL1UsnbLuBSMd21hTX6VY4ZSDSM73ESLeF8TOYId3rBTbw==} + engines: {node: 10.* || >= 12.*} + dependencies: + string-width: 4.2.3 + optionalDependencies: + '@colors/colors': 1.5.0 + dev: true + + /cli-truncate/2.1.0: + resolution: {integrity: sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==} + engines: {node: '>=8'} + dependencies: + slice-ansi: 3.0.0 + string-width: 4.2.3 + dev: true + /cli-width/3.0.0: resolution: {integrity: sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==} engines: {node: '>= 10'} @@ -8123,6 +8259,10 @@ packages: color-string: 1.9.1 dev: true + /colorette/2.0.19: + resolution: {integrity: sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==} + dev: true + /colors/1.0.3: resolution: {integrity: sha512-pFGrxThWcWQ2MsAz6RtgeWe4NK2kUE1WfsrvvlctdII745EW9I0yflqhe7++M5LEc7bV2c/9/5zc8sFcpL0Drw==} engines: {node: '>=0.1.90'} @@ -8154,12 +8294,22 @@ packages: resolution: {integrity: sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow==} dev: true + /commander/5.1.0: + resolution: {integrity: sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==} + engines: {node: '>= 6'} + dev: true + + /common-tags/1.8.2: + resolution: {integrity: sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==} + engines: {node: '>=4.0.0'} + dev: true + /component-emitter/1.3.0: resolution: {integrity: sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==} dev: true /concat-map/0.0.1: - resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=} + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} /concurrently/7.2.1: resolution: {integrity: sha512-7cab/QyqipqghrVr9qZmoWbidu0nHsmxrpNqQ7r/67vfl1DWJElexehQnTH1p+87tDkihaAjM79xTZyBQh7HLw==} @@ -8253,6 +8403,10 @@ packages: requiresBuild: true dev: false + /core-util-is/1.0.2: + resolution: {integrity: sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==} + dev: true + /core-util-is/1.0.3: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} @@ -8358,6 +8512,56 @@ packages: /csstype/3.1.0: resolution: {integrity: sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA==} + /cypress/10.6.0: + resolution: {integrity: sha512-6sOpHjostp8gcLO34p6r/Ci342lBs8S5z9/eb3ZCQ22w2cIhMWGUoGKkosabPBfKcvRS9BE4UxybBtlIs8gTQA==} + engines: {node: '>=12.0.0'} + hasBin: true + requiresBuild: true + dependencies: + '@cypress/request': 2.88.10 + '@cypress/xvfb': 1.2.4 + '@types/node': 14.18.20 + '@types/sinonjs__fake-timers': 8.1.1 + '@types/sizzle': 2.3.3 + arch: 2.2.0 + blob-util: 2.0.2 + bluebird: 3.7.2 + buffer: 5.7.1 + cachedir: 2.3.0 + chalk: 4.1.2 + check-more-types: 2.24.0 + cli-cursor: 3.1.0 + cli-table3: 0.6.2 + commander: 5.1.0 + common-tags: 1.8.2 + dayjs: 1.11.5 + debug: 4.3.4_supports-color@8.1.1 + enquirer: 2.3.6 + eventemitter2: 6.4.7 + execa: 4.1.0 + executable: 4.1.1 + extract-zip: 2.0.1_supports-color@8.1.1 + figures: 3.2.0 + fs-extra: 9.1.0 + getos: 3.2.1 + is-ci: 3.0.1 + is-installed-globally: 0.4.0 + lazy-ass: 1.6.0 + listr2: 3.14.0_enquirer@2.3.6 + lodash: 4.17.21 + log-symbols: 4.1.0 + minimist: 1.2.6 + ospath: 1.2.2 + pretty-bytes: 5.6.0 + proxy-from-env: 1.0.0 + request-progress: 3.0.0 + semver: 7.3.7 + supports-color: 8.1.1 + tmp: 0.2.1 + untildify: 4.0.0 + yauzl: 2.10.0 + dev: true + /csurf/1.11.0: resolution: {integrity: sha512-UCtehyEExKTxgiu8UHdGvHj4tnpE/Qctue03Giq5gPgMQ9cg/ciod5blZQ5a4uCEenNQjxyGuzygLdKUmee/bQ==} engines: {node: '>= 0.8.0'} @@ -8425,6 +8629,13 @@ packages: resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==} dev: true + /dashdash/1.14.1: + resolution: {integrity: sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==} + engines: {node: '>=0.10'} + dependencies: + assert-plus: 1.0.0 + dev: true + /data-urls/2.0.0: resolution: {integrity: sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==} engines: {node: '>=10'} @@ -8438,6 +8649,10 @@ packages: resolution: {integrity: sha512-8d35hViGYx/QH0icHYCeLmsLmMUheMmTyV9Fcm6gvNwdw31yXXH+O85sOBJ+OLnLQMKZowvpKb6FgMIQjcpvQw==} engines: {node: '>=0.11'} + /dayjs/1.11.5: + resolution: {integrity: sha512-CAdX5Q3YW3Gclyo5Vpqkgpj8fSdLQcRuzfX6mC6Phy0nfJ0eGYOeS7m4mt2plDWLAtA4TqTakvbboHvUxfe4iA==} + dev: true + /debug/2.6.9: resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} dependencies: @@ -8459,6 +8674,19 @@ packages: dependencies: ms: 2.1.2 + /debug/4.3.4_supports-color@8.1.1: + resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.2 + supports-color: 8.1.1 + dev: true + /debuglog/1.0.1: resolution: {integrity: sha512-syBZ+rnAK3EgMsH2aYEOLUW7mZSY9Gb+0wUMCFsZvcmiz+HigA0LOcq/HoQqVuGG+EKykunc7QG2bzrponfaSw==} @@ -8673,6 +8901,13 @@ packages: webidl-conversions: 5.0.0 dev: true + /ecc-jsbn/0.1.2: + resolution: {integrity: sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==} + dependencies: + jsbn: 0.1.1 + safer-buffer: 2.1.2 + dev: true + /ecdsa-sig-formatter/1.0.11: resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} dependencies: @@ -8729,6 +8964,13 @@ packages: tapable: 2.2.1 dev: false + /enquirer/2.3.6: + resolution: {integrity: sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==} + engines: {node: '>=8.6'} + dependencies: + ansi-colors: 4.1.3 + dev: true + /entities/2.2.0: resolution: {integrity: sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==} @@ -9406,6 +9648,10 @@ packages: resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} engines: {node: '>= 0.6'} + /eventemitter2/6.4.7: + resolution: {integrity: sha512-tYUSVOGeQPKt/eC1ABfhHy5Xd96N3oIijJvN3O9+TsC28T5V9yX9oEfEK5faP0EFSNVOG97qtAS68GBrQB2hDg==} + dev: true + /events/1.1.1: resolution: {integrity: sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw==} engines: {node: '>=0.4.x'} @@ -9433,6 +9679,21 @@ packages: strip-eof: 1.0.0 dev: true + /execa/4.1.0: + resolution: {integrity: sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==} + engines: {node: '>=10'} + dependencies: + cross-spawn: 7.0.3 + get-stream: 5.2.0 + human-signals: 1.1.1 + is-stream: 2.0.1 + merge-stream: 2.0.0 + npm-run-path: 4.0.1 + onetime: 5.1.2 + signal-exit: 3.0.7 + strip-final-newline: 2.0.0 + dev: true + /execa/5.1.1: resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} engines: {node: '>=10'} @@ -9448,6 +9709,13 @@ packages: strip-final-newline: 2.0.0 dev: true + /executable/4.1.1: + resolution: {integrity: sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==} + engines: {node: '>=4'} + dependencies: + pify: 2.3.0 + dev: true + /exit/0.1.2: resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==} engines: {node: '>= 0.8.0'} @@ -9544,6 +9812,10 @@ packages: is-extendable: 1.0.1 dev: true + /extend/3.0.2: + resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} + dev: true + /external-editor/3.1.0: resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==} engines: {node: '>=4'} @@ -9567,6 +9839,25 @@ packages: to-regex: 3.0.2 dev: true + /extract-zip/2.0.1_supports-color@8.1.1: + resolution: {integrity: sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==} + engines: {node: '>= 10.17.0'} + hasBin: true + dependencies: + debug: 4.3.4_supports-color@8.1.1 + get-stream: 5.2.0 + yauzl: 2.10.0 + optionalDependencies: + '@types/yauzl': 2.10.0 + transitivePeerDependencies: + - supports-color + dev: true + + /extsprintf/1.3.0: + resolution: {integrity: sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==} + engines: {'0': node >=0.6.0} + dev: true + /fast-check/2.25.0: resolution: {integrity: sha512-wRUT2KD2lAmT75WNIJIHECawoUUMHM0I5jrlLXGtGeqmPL8jl/EldUDjY1VCp6fDY8yflyfUeIOsOBrIbIiArg==} engines: {node: '>=8.0.0'} @@ -9612,6 +9903,12 @@ packages: bser: 2.1.1 dev: true + /fd-slicer/1.1.0: + resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} + dependencies: + pend: 1.2.0 + dev: true + /fecha/4.2.3: resolution: {integrity: sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==} dev: false @@ -9621,7 +9918,6 @@ packages: engines: {node: '>=8'} dependencies: escape-string-regexp: 1.0.5 - dev: false /file-entry-cache/6.0.1: resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} @@ -9713,6 +10009,19 @@ packages: engines: {node: '>=0.10.0'} dev: true + /forever-agent/0.6.1: + resolution: {integrity: sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==} + dev: true + + /form-data/2.3.3: + resolution: {integrity: sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==} + engines: {node: '>= 0.12'} + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + mime-types: 2.1.35 + dev: true + /form-data/3.0.1: resolution: {integrity: sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==} engines: {node: '>= 6'} @@ -9865,6 +10174,13 @@ packages: pump: 3.0.0 dev: true + /get-stream/5.2.0: + resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} + engines: {node: '>=8'} + dependencies: + pump: 3.0.0 + dev: true + /get-stream/6.0.1: resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} engines: {node: '>=10'} @@ -9882,6 +10198,18 @@ packages: engines: {node: '>=0.10.0'} dev: true + /getos/3.2.1: + resolution: {integrity: sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q==} + dependencies: + async: 3.2.3 + dev: true + + /getpass/0.1.7: + resolution: {integrity: sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==} + dependencies: + assert-plus: 1.0.0 + dev: true + /git-hooks-list/1.0.3: resolution: {integrity: sha512-Y7wLWcrLUXwk2noSka166byGCvhMtDRpgHdzCno1UQv/n/Hegp++a2xBWJL1lJarnKD3SWaljD+0z1ztqxuKyQ==} dev: true @@ -9951,6 +10279,13 @@ packages: once: 1.4.0 path-is-absolute: 1.0.1 + /global-dirs/3.0.0: + resolution: {integrity: sha512-v8ho2DS5RiCjftj1nD9NmnfaOzTdud7RRnVd9kFNOjqZbISlx5DQ+OrTkywgd0dIt7oFCvKetZSHoHcP3sDdiA==} + engines: {node: '>=10'} + dependencies: + ini: 2.0.0 + dev: true + /globals/11.12.0: resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} engines: {node: '>=4'} @@ -10143,6 +10478,15 @@ packages: - supports-color dev: true + /http-signature/1.3.6: + resolution: {integrity: sha512-3adrsD6zqo4GsTqtO7FyrejHNv+NgiIfAfv68+jVlFmSr9OGy7zrxONceFRLKvnnZA5jbxQBX1u9PpB6Wi32Gw==} + engines: {node: '>=0.10'} + dependencies: + assert-plus: 1.0.0 + jsprim: 2.0.2 + sshpk: 1.17.0 + dev: true + /https-proxy-agent/5.0.1: resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} engines: {node: '>= 6'} @@ -10152,6 +10496,11 @@ packages: transitivePeerDependencies: - supports-color + /human-signals/1.1.1: + resolution: {integrity: sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==} + engines: {node: '>=8.12.0'} + dev: true + /human-signals/2.1.0: resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} engines: {node: '>=10.17.0'} @@ -10250,6 +10599,11 @@ packages: resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} dev: true + /ini/2.0.0: + resolution: {integrity: sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==} + engines: {node: '>=10'} + dev: true + /inquirer/7.3.3: resolution: {integrity: sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==} engines: {node: '>=8.0.0'} @@ -10346,6 +10700,13 @@ packages: ci-info: 2.0.0 dev: true + /is-ci/3.0.1: + resolution: {integrity: sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==} + hasBin: true + dependencies: + ci-info: 3.3.1 + dev: true + /is-core-module/2.9.0: resolution: {integrity: sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==} dependencies: @@ -10432,6 +10793,14 @@ packages: dependencies: is-extglob: 2.1.1 + /is-installed-globally/0.4.0: + resolution: {integrity: sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==} + engines: {node: '>=10'} + dependencies: + global-dirs: 3.0.0 + is-path-inside: 3.0.3 + dev: true + /is-negative-zero/2.0.2: resolution: {integrity: sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==} engines: {node: '>= 0.4'} @@ -10453,6 +10822,11 @@ packages: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} + /is-path-inside/3.0.3: + resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} + engines: {node: '>=8'} + dev: true + /is-plain-obj/1.1.0: resolution: {integrity: sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==} engines: {node: '>=0.10.0'} @@ -10556,6 +10930,10 @@ packages: engines: {node: '>=0.10.0'} dev: true + /isstream/0.1.2: + resolution: {integrity: sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==} + dev: true + /istanbul-badges-readme/1.8.1: resolution: {integrity: sha512-I7cEFAFlWc5VB17a71U0TgQbez5+U78F0f4kky1w33L4t3BC4/8gbN1tbmX4LVaki52Op4LrkttpvcdZ6kd/jg==} hasBin: true @@ -11744,6 +12122,10 @@ packages: dependencies: argparse: 2.0.1 + /jsbn/0.1.1: + resolution: {integrity: sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==} + dev: true + /jsdom/16.7.0: resolution: {integrity: sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==} engines: {node: '>=10'} @@ -11807,9 +12189,17 @@ packages: resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} dev: true + /json-schema/0.4.0: + resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} + dev: true + /json-stable-stringify-without-jsonify/1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + /json-stringify-safe/5.0.1: + resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} + dev: true + /json5/1.0.1: resolution: {integrity: sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==} hasBin: true @@ -11844,6 +12234,16 @@ packages: /jsonschema/1.4.1: resolution: {integrity: sha512-S6cATIPVv1z0IlxdN+zUk5EPjkGCdnhN4wVSBlvoUO1tOLJootbo9CquNJmbIh4yikWHiUedhRYrNPn1arpEmQ==} + /jsprim/2.0.2: + resolution: {integrity: sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==} + engines: {'0': node >=0.6.0} + dependencies: + assert-plus: 1.0.0 + extsprintf: 1.3.0 + json-schema: 0.4.0 + verror: 1.10.0 + dev: true + /jsx-ast-utils/3.3.0: resolution: {integrity: sha512-XzO9luP6L0xkxwhIJMTJQpZo/eeN60K08jHdexfD569AGxeNug6UketeHXEhROoM8aR7EcUoOQmIhcJQjcuq8Q==} engines: {node: '>=4.0'} @@ -11925,6 +12325,11 @@ packages: language-subtag-registry: 0.3.21 dev: true + /lazy-ass/1.6.0: + resolution: {integrity: sha512-cc8oEVoctTvsFZ/Oje/kGnHbpWHYBe8IAJe4C0QNc3t8uM/0Y8+erSz/7Y1ALuXTEZTMvxXwO6YbX1ey3ujiZw==} + engines: {node: '> 0.8'} + dev: true + /leven/3.1.0: resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} engines: {node: '>=6'} @@ -11981,6 +12386,26 @@ packages: /lines-and-columns/1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + /listr2/3.14.0_enquirer@2.3.6: + resolution: {integrity: sha512-TyWI8G99GX9GjE54cJ+RrNMcIFBfwMPxc3XTFiAYGN4s10hWROGtOg7+O6u6LE3mNkyld7RSLE6nrKBvTfcs3g==} + engines: {node: '>=10.0.0'} + peerDependencies: + enquirer: '>= 2.3.0 < 3' + peerDependenciesMeta: + enquirer: + optional: true + dependencies: + cli-truncate: 2.1.0 + colorette: 2.0.19 + enquirer: 2.3.6 + log-update: 4.0.0 + p-map: 4.0.0 + rfdc: 1.3.0 + rxjs: 7.5.6 + through: 2.3.8 + wrap-ansi: 7.0.0 + dev: true + /load-json-file/6.2.0: resolution: {integrity: sha512-gUD/epcRms75Cw8RT1pUdHugZYM5ce64ucs2GEISABwkRsOQr0q2wm/MV2TKThycIe5e0ytRweW2RZxclogCdQ==} engines: {node: '>=8'} @@ -12030,6 +12455,10 @@ packages: /lodash.merge/4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + /lodash.once/4.1.1: + resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==} + dev: true + /lodash.truncate/4.4.2: resolution: {integrity: sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==} dev: true @@ -12045,6 +12474,16 @@ packages: is-unicode-supported: 0.1.0 dev: true + /log-update/4.0.0: + resolution: {integrity: sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==} + engines: {node: '>=10'} + dependencies: + ansi-escapes: 4.3.2 + cli-cursor: 3.1.0 + slice-ansi: 4.0.0 + wrap-ansi: 6.2.0 + dev: true + /logform/2.4.0: resolution: {integrity: sha512-CPSJw4ftjf517EhXZGGvTHHkYobo7ZCc0kvwUoOYcjfR2UVrI66RHj8MCrfAdEitdmFqbu2BYdYs8FHHZSb6iw==} dependencies: @@ -12778,6 +13217,10 @@ packages: os-homedir: 1.0.2 os-tmpdir: 1.0.2 + /ospath/1.2.2: + resolution: {integrity: sha512-o6E5qJV5zkAbIDNhGSIlyOhScKXgQrSRMilfph0clDfM0nEnBOlKlH4sWDmG95BW/CvwNz0vmm7dJVtU2KlMiA==} + dev: true + /p-finally/1.0.0: resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==} engines: {node: '>=4'} @@ -12815,6 +13258,13 @@ packages: p-limit: 2.3.0 dev: true + /p-map/4.0.0: + resolution: {integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==} + engines: {node: '>=10'} + dependencies: + aggregate-error: 3.1.0 + dev: true + /p-reflect/2.1.0: resolution: {integrity: sha512-paHV8NUz8zDHu5lhr/ngGWQiW067DK/+IbJ+RfZ4k+s8y4EKyYCz8pGYWjxCg35eHztpJAt+NUgvN4L+GCbPlg==} engines: {node: '>=8'} @@ -12905,6 +13355,14 @@ packages: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} + /pend/1.2.0: + resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} + dev: true + + /performance-now/2.1.0: + resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==} + dev: true + /picocolors/1.0.0: resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} @@ -12912,6 +13370,11 @@ packages: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} + /pify/2.3.0: + resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} + engines: {node: '>=0.10.0'} + dev: true + /pirates/4.0.5: resolution: {integrity: sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==} engines: {node: '>= 6'} @@ -13001,6 +13464,11 @@ packages: hasBin: true dev: true + /pretty-bytes/5.6.0: + resolution: {integrity: sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==} + engines: {node: '>=6'} + dev: true + /pretty-format/26.6.2: resolution: {integrity: sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==} engines: {node: '>= 10'} @@ -13050,6 +13518,10 @@ packages: forwarded: 0.2.0 ipaddr.js: 1.9.1 + /proxy-from-env/1.0.0: + resolution: {integrity: sha512-F2JHgJQ1iqwnHDcQjVBsq3n/uoaFL+iPW/eAeL7kVxy/2RrWaN4WroKjjvbsoRtv0ftelNyC01bjRhn/bhcf4A==} + dev: true + /psl/1.8.0: resolution: {integrity: sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==} @@ -13082,6 +13554,11 @@ packages: dependencies: side-channel: 1.0.4 + /qs/6.5.3: + resolution: {integrity: sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==} + engines: {node: '>=0.6'} + dev: true + /query-ast/1.0.4: resolution: {integrity: sha512-KFJFSvODCBjIH5HbHvITj9EEZKYUU6VX0T5CuB1ayvjUoUaZkKMi6eeby5Tf8DMukyZHlJQOE1+f3vevKUe6eg==} dependencies: @@ -13451,6 +13928,12 @@ packages: engines: {node: '>=0.10'} dev: true + /request-progress/3.0.0: + resolution: {integrity: sha512-MnWzEHHaxHO2iWiQuHrUPBi/1WeBf5PkxQqNyNvLl9VAYSdXkP8tQ3pBSeCPD+yw0v0Aq1zosWLz0BdeXpWwZg==} + dependencies: + throttleit: 1.0.0 + dev: true + /require-directory/2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} @@ -13526,7 +14009,6 @@ packages: dependencies: onetime: 5.1.2 signal-exit: 3.0.7 - dev: false /ret/0.1.15: resolution: {integrity: sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==} @@ -13537,6 +14019,10 @@ packages: resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + /rfdc/1.3.0: + resolution: {integrity: sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==} + dev: true + /rimraf/3.0.2: resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} hasBin: true @@ -13568,6 +14054,12 @@ packages: dependencies: tslib: 1.14.1 + /rxjs/7.5.6: + resolution: {integrity: sha512-dnyv2/YsXhnm461G+R/Pe5bWP41Nm6LBXEYWI6eiFP4fiwx6WRI/CD0zbdVAudd9xwLEF2IDcKXLHit0FYjUzw==} + dependencies: + tslib: 2.4.0 + dev: true + /safe-buffer/5.1.2: resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} @@ -13819,6 +14311,15 @@ packages: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} + /slice-ansi/3.0.0: + resolution: {integrity: sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==} + engines: {node: '>=8'} + dependencies: + ansi-styles: 4.3.0 + astral-regex: 2.0.0 + is-fullwidth-code-point: 3.0.0 + dev: true + /slice-ansi/4.0.0: resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==} engines: {node: '>=10'} @@ -13989,6 +14490,22 @@ packages: /sprintf-js/1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + /sshpk/1.17.0: + resolution: {integrity: sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==} + engines: {node: '>=0.10.0'} + hasBin: true + dependencies: + asn1: 0.2.6 + assert-plus: 1.0.0 + bcrypt-pbkdf: 1.0.2 + dashdash: 1.14.1 + ecc-jsbn: 0.1.2 + getpass: 0.1.7 + jsbn: 0.1.1 + safer-buffer: 2.1.2 + tweetnacl: 0.14.5 + dev: true + /ssri/8.0.1: resolution: {integrity: sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==} engines: {node: '>= 8'} @@ -14315,9 +14832,12 @@ packages: resolution: {integrity: sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w==} dev: true + /throttleit/1.0.0: + resolution: {integrity: sha512-rkTVqu6IjfQ/6+uNuuc3sZek4CEYxTJom3IktzgdSxcZqdARuebbA/f4QmAxMQIxqq9ZLEUkSYqvuk1I6VKq4g==} + dev: true + /through/2.3.8: - resolution: {integrity: sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=} - dev: false + resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} /timsort/0.3.0: resolution: {integrity: sha512-qsdtZH+vMoCARQtyod4imc2nIJwg9Cc7lPRrw9CzF8ZKR0khdr8+2nX80PBhET3tcyTtJDxAffGh2rXH4tyU8A==} @@ -14329,6 +14849,13 @@ packages: os-tmpdir: 1.0.2 dev: false + /tmp/0.2.1: + resolution: {integrity: sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==} + engines: {node: '>=8.17.0'} + dependencies: + rimraf: 3.0.2 + dev: true + /tmpl/1.0.5: resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} dev: true @@ -14385,6 +14912,14 @@ packages: nopt: 1.0.10 dev: true + /tough-cookie/2.5.0: + resolution: {integrity: sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==} + engines: {node: '>=0.8'} + dependencies: + psl: 1.8.0 + punycode: 2.1.1 + dev: true + /tough-cookie/4.0.0: resolution: {integrity: sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==} engines: {node: '>=6'} @@ -14624,7 +15159,7 @@ packages: typescript: 4.7.2 /tunnel-agent/0.6.0: - resolution: {integrity: sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=} + resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} dependencies: safe-buffer: 5.2.1 dev: true @@ -14634,6 +15169,10 @@ packages: engines: {node: '>=0.6.11 <=0.7.0 || >=0.7.3'} dev: false + /tweetnacl/0.14.5: + resolution: {integrity: sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==} + dev: true + /type-check/0.3.2: resolution: {integrity: sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==} engines: {node: '>= 0.8.0'} @@ -14774,6 +15313,11 @@ packages: isobject: 3.0.1 dev: true + /untildify/4.0.0: + resolution: {integrity: sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==} + engines: {node: '>=8'} + dev: true + /uri-js/4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} dependencies: @@ -14888,6 +15432,15 @@ packages: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} + /verror/1.10.0: + resolution: {integrity: sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==} + engines: {'0': node >=0.6.0} + dependencies: + assert-plus: 1.0.0 + core-util-is: 1.0.2 + extsprintf: 1.3.0 + dev: true + /void-elements/3.1.0: resolution: {integrity: sha1-YU9/v42AHwu18GYfWy9XhXUOTwk=} engines: {node: '>=0.10.0'} @@ -15221,6 +15774,13 @@ packages: yargs-parser: 21.0.1 dev: true + /yauzl/2.10.0: + resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==} + dependencies: + buffer-crc32: 0.2.13 + fd-slicer: 1.1.0 + dev: true + /yn/3.1.1: resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} engines: {node: '>=6'} diff --git a/solutions/swb-reference/README.md b/solutions/swb-reference/README.md index c79e37541e..d703f117e3 100644 --- a/solutions/swb-reference/README.md +++ b/solutions/swb-reference/README.md @@ -111,6 +111,82 @@ Go to `solutions/swb-app` to update `staticRouteConfig.ts` and `staticPermission 3. Run `STAGE= node generateCognitoTokens.js ''` with the correct value for `` and ``. It should be a user that has been created for your SWB deployment. Note, the quotes around `` is necessary for the script to correctly parse passwords that have symbols in it. 4. In the console output, use the `accessToken`, `csrfCookie`, and `csrfToken` that are provided to make authenticated API requests. + +## End To End Tests + +End to end testing is implemented in Service Workbench by using Cypress framework inside `swb-reference/end-to-end-tests directory` + + +### Cypress Configuration + +In order for end to end tests to run, Cypress needs environment variables such as website URL or user and password to login. +All environment variables for end to end testing are retrieved from `swb-reference/end-to-end-tests/config/.yaml` and `swb-reference/src/config/.json`(generated after deploying API) files and set inside `swb-reference/cypress.config.ts`. + + +### Running End To End Tests + +#### Prerequisite: +Follow the instructions [here](../swb-ui/README.md#deploy-ui-to-aws) to deploy the SWB UI to AWS. + +1. In `swb-reference/end-to-end-tests/config` make a copy of `example.yaml` and name it `.yaml`. Uncomment the attributes and provide the appropriate config value. +2. Go to `swb-reference` directory +3. Run command `STAGE= rushx e2e-test` + + +### Debugging End to End Tests + +#### Prerequisite: + +Follow the instructions [here](../swb-ui/README.md#deploy-ui-to-aws) to deploy the SWB UI to AWS. + + +1. Go to `swb-reference` directory +2. Run command `STAGE= rushx e2e-test:open`, a new Cypress window will be displayed +4. Select E2E Testing in Cypress Window +5. Select preferred browser to test +6. Click Start E2E Testing Button, this will open a page with all test cases. +7. Click the link with the test case name to run inside the grid, this will trigger the test case and display a list with all the steps the test case followed + +Cypress will normally give a description for errors that happen during the test, for more details open the browser console. + + +### Writing End To End Tests + +In order for Cypress to read a test file, it must have the extension `.cy.ts` and be located in a folder inside `swb-reference/end-to-end-tests/cypress`. +All Cypress configuration and env variables logic is contained in `swb-reference/cypress.config.ts` file. + +To create a new test case that needs authentication, the command `cy.login('')` must be used, login state will not be saved in between test cases but we can invoke function `beforeEach(()=> {})` at the begining of the test to make a function run before every test in that file. + +Example of code with login + +```ts +describe('Page routing', () => { + it('Should navigate to /environments after login as IT Admin', async () => { + cy.login('ITAdmin'); + cy.location('pathname').should('eq', '/environments'); + }); +}); +``` + +Example of code with `beforeEach(()=> {})` + +```ts +describe('Redirects IT Admin', () => { + beforeEach(()=> { + cy.login('ITAdmin'); + }); + it('Should navigate to /environments after login', async () => { + cy.location('pathname').should('eq', '/environments'); + }); + + it('Should navigate to /environments/new on create click', async () => { + cy.get('[data-testid="createEnvironment"]').click(); + cy.location('pathname').should('eq', '/environments/new'); + }); +}); +``` +For more information about Cypress and End to End tests good practices visit [Cypress Documentation](https://docs.cypress.io/guides/references/best-practices). + ## Troubleshooting Guides [Workspace Lifecycle Troubleshooting Guide](docs/WorkspaceLifecycleTroubleshootingGuide.md). diff --git a/solutions/swb-reference/cypress.config.ts b/solutions/swb-reference/cypress.config.ts new file mode 100644 index 0000000000..41028a8bb3 --- /dev/null +++ b/solutions/swb-reference/cypress.config.ts @@ -0,0 +1,59 @@ +import { defineConfig } from 'cypress'; + +export default defineConfig({ + e2e: { + fixturesFolder: 'end-to-end-tests/cypress/fixtures', + fileServerFolder: 'end-to-end-tests', + screenshotsFolder: 'end-to-end-tests/cypress/screenshots', + videosFolder: 'end-to-end-tests/cypress/videos', + downloadsFolder: 'end-to-end-tests/cypress/downloads', + supportFile: 'end-to-end-tests/cypress/support/e2e.ts', + specPattern: 'end-to-end-tests/cypress/**/*.cy.{js,jsx,ts,tsx}', + experimentalSessionAndOrigin: true, + setupNodeEvents(on, config) { + //this function is called in a different scope making cypress and cy commands unable to work + const environment = getEnvironmentVariables(config.env.STAGE); + environment.ADMIN_PASSWORD = config.env.AdminPassword ?? environment.ADMIN_PASSWORD; //read password from yaml only when password is not set in env variables already + config.env = { ...config.env, ...environment }; + config.baseUrl = environment.BASE_URL; + // implement node event listeners here + return config; + } + } +}); + +const getEnvironmentVariables = ( + stage: string +): { + ADMIN_USER: string; + ADMIN_PASSWORD: string; + BASE_URL: string; + COGNITO_DOMAIN_NAME: string; +} => { + const fs = require('fs'); + const yaml = require('js-yaml'); + const _ = require('lodash'); + const apiStackOutputs: any = JSON.parse( + // eslint-disable-next-line security/detect-non-literal-fs-filename + fs.readFileSync(`${__dirname}/src/config/${stage}.json`, 'utf8') // nosemgrep + ); + const yamlConfig: any = yaml.load( + // eslint-disable-next-line security/detect-non-literal-fs-filename + fs.readFileSync(`${__dirname}/end-to-end-tests/config/${stage}.yaml`, 'utf8') // nosemgrep + ); + const apiStackName = Object.entries(apiStackOutputs).map(([key, value]) => key)[0]; //output has a format { stackname: {...props} } + // eslint-disable-next-line security/detect-object-injection + const outputs = apiStackOutputs[apiStackName]; + const ADMIN_USER = yamlConfig.AdminUser; + const ADMIN_PASSWORD = yamlConfig.AdminPassword; + const BASE_URL = outputs.uiClientURL; + const COGNITO_DOMAIN_NAME = outputs.cognitoDomainName; + debugger; + + return { + ADMIN_USER, + ADMIN_PASSWORD, + BASE_URL, + COGNITO_DOMAIN_NAME + }; +}; diff --git a/solutions/swb-reference/end-to-end-tests/config/example.yaml b/solutions/swb-reference/end-to-end-tests/config/example.yaml new file mode 100644 index 0000000000..a2938ad993 --- /dev/null +++ b/solutions/swb-reference/end-to-end-tests/config/example.yaml @@ -0,0 +1,2 @@ +# AdminUser: 'user@example.com' ##IT Admin User +# AdminPassword: '' ##IT Admin Password diff --git a/solutions/swb-reference/end-to-end-tests/cypress/common/login.cy.ts b/solutions/swb-reference/end-to-end-tests/cypress/common/login.cy.ts new file mode 100644 index 0000000000..1f3045a2df --- /dev/null +++ b/solutions/swb-reference/end-to-end-tests/cypress/common/login.cy.ts @@ -0,0 +1,5 @@ +describe('IT Admin Login', () => { + it('Should login as IT Admin', async () => { + cy.login('ITAdmin'); + }); +}); diff --git a/solutions/swb-reference/end-to-end-tests/cypress/fixtures/example.json b/solutions/swb-reference/end-to-end-tests/cypress/fixtures/example.json new file mode 100644 index 0000000000..02e4254378 --- /dev/null +++ b/solutions/swb-reference/end-to-end-tests/cypress/fixtures/example.json @@ -0,0 +1,5 @@ +{ + "name": "Using fixtures to represent data", + "email": "hello@cypress.io", + "body": "Fixtures are a great way to mock data for responses to routes" +} diff --git a/solutions/swb-reference/end-to-end-tests/cypress/support/commands.ts b/solutions/swb-reference/end-to-end-tests/cypress/support/commands.ts new file mode 100644 index 0000000000..f6ab0f52ae --- /dev/null +++ b/solutions/swb-reference/end-to-end-tests/cypress/support/commands.ts @@ -0,0 +1,81 @@ +/// + +// *********************************************** +// This example commands.ts shows you how to +// create various custom commands and overwrite +// existing commands. +// +// For more comprehensive examples of custom +// commands please read more here: +// https://on.cypress.io/custom-commands +// *********************************************** +// +// +// -- This is a parent command -- +// Cypress.Commands.add('login', (email, password) => { ... }) +// +// +// -- This is a child command -- +// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... }) +// +// +// -- This is a dual command -- +// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... }) +// +// +// -- This will overwrite an existing command -- +// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... }) +// +// declare global { +// namespace Cypress { +// interface Chainable { +// login(email: string, password: string): Chainable +// drag(subject: string, options?: Partial): Chainable +// dismiss(subject: string, options?: Partial): Chainable +// visit(originalFn: CommandOriginalFn, url: string, options: Partial): Chainable +// } +// } +// } +import { ADMIN_PASSWORD_PROPERTY, ADMIN_USER_PROPERTY, COGNITO_DOMAIN_NAME_PROPERTY } from './constants'; + +Cypress.on( + 'uncaught:exception', + (err) => !err.message.includes('ResizeObserver loop completed with undelivered notifications') //known issue with cypress https://github.com/quasarframework/quasar/issues/2233 +); + +Cypress.Commands.add('login', (role: string) => { + const login = getLoginInfo(role); + cy.visit('/'); + cy.get('[data-testid="login"]').should('be.visible'); + cy.get('[data-testid="login"]').click(); + cy.origin( + Cypress.env(COGNITO_DOMAIN_NAME_PROPERTY), + { args: [login.user, login.password] }, + ([user, password]) => { + cy.get('input[name=username]:visible', { timeout: 10000 }).type(user); //wait up to 10 seconds to have the username field displayed + cy.get('input[name=password]:visible').type(password); + cy.get('[name=signInSubmitButton]:visible').click(); + } + ); + + cy.get('[data-testid="environmentListHeader"]', { timeout: 10000 }) + .contains('Workspaces') + .should('be.visible'); //redirection for environments may take time to load + cy.location('pathname').should('eq', '/environments'); +}); + +Cypress.Commands.add('logout', (role: string) => { + cy.get('#header [data-utility-index=0]').click(); + cy.get('[data-testid=signout]').click(); + cy.location('pathname').should('eq', '/'); + cy.get('[data-testid="login"]').should('be.visible'); +}); + +const getLoginInfo = (role: string): { user: string; password: string } => { + switch (role) { + case 'ITAdmin': + return { user: Cypress.env(ADMIN_USER_PROPERTY), password: Cypress.env(ADMIN_PASSWORD_PROPERTY) }; + default: + return { user: '', password: '' }; + } +}; diff --git a/solutions/swb-reference/end-to-end-tests/cypress/support/constants.ts b/solutions/swb-reference/end-to-end-tests/cypress/support/constants.ts new file mode 100644 index 0000000000..e43e438dc5 --- /dev/null +++ b/solutions/swb-reference/end-to-end-tests/cypress/support/constants.ts @@ -0,0 +1,7 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +export const ADMIN_PASSWORD_PROPERTY: string = 'ADMIN_PASSWORD'; +export const ADMIN_USER_PROPERTY: string = 'ADMIN_USER'; +export const COGNITO_DOMAIN_NAME_PROPERTY: string = 'COGNITO_DOMAIN_NAME'; diff --git a/solutions/swb-reference/end-to-end-tests/cypress/support/e2e.ts b/solutions/swb-reference/end-to-end-tests/cypress/support/e2e.ts new file mode 100644 index 0000000000..598ab5f0d7 --- /dev/null +++ b/solutions/swb-reference/end-to-end-tests/cypress/support/e2e.ts @@ -0,0 +1,20 @@ +// *********************************************************** +// This example support/e2e.ts is processed and +// loaded automatically before your test files. +// +// This is a great place to put global configuration and +// behavior that modifies Cypress. +// +// You can change the location of this file or turn off +// automatically serving support files with the +// 'supportFile' configuration option. +// +// You can read more here: +// https://on.cypress.io/configuration +// *********************************************************** + +// Import commands.js using ES2015 syntax: +import './commands'; + +// Alternatively you can use CommonJS syntax: +// require('./commands') diff --git a/solutions/swb-reference/package.json b/solutions/swb-reference/package.json index a441c52eb4..0b07b873e5 100644 --- a/solutions/swb-reference/package.json +++ b/solutions/swb-reference/package.json @@ -22,6 +22,8 @@ "check-license-header": "license-check-and-add check -f license-add-config.json", "compile": "node scripts/buildLambda.js", "depcheck": "depcheck", + "e2e-test": "cypress run --env STAGE=${STAGE}", + "e2e-test:open": "cypress open --env STAGE=${STAGE}", "integration-tests": "rushx build && jest lib/integration-tests", "jest": "jest", "license-checker": "license-checker --onlyAllow 'MIT; Apache-2.0; ISC; BSD'", @@ -85,6 +87,7 @@ "axios": "^0.27.1", "cdk-ssm-document": "^3.1.1", "constructs": "^10.0.0", + "cypress": "^10.6.0", "depcheck": "^1.4.3", "esbuild": "^0.14.21", "eslint": "^8.7.0", diff --git a/solutions/swb-ui/src/components/Login.tsx b/solutions/swb-ui/src/components/Login.tsx index b53eb50751..9c13aafa4e 100644 --- a/solutions/swb-ui/src/components/Login.tsx +++ b/solutions/swb-ui/src/components/Login.tsx @@ -11,7 +11,12 @@ function Login(): JSX.Element { const { signIn } = useAuthentication(); return ( - ); diff --git a/solutions/swb-ui/src/pages/environments/index.tsx b/solutions/swb-ui/src/pages/environments/index.tsx index e4c4dc6164..25e34f8a2f 100644 --- a/solutions/swb-ui/src/pages/environments/index.tsx +++ b/solutions/swb-ui/src/pages/environments/index.tsx @@ -345,6 +345,7 @@ const Environment: NextPage = () => { header={ <>
From 85a640bfc242aad09a7b4d3fd1149b3fd5389fb2 Mon Sep 17 00:00:00 2001 From: Samir Sharan <11716061+insignias@users.noreply.github.com> Date: Wed, 31 Aug 2022 18:30:40 -0400 Subject: [PATCH 034/621] chore: Fix chore separate UI common packages (#484) * feat: adding new package for common UI components --- ...e-UI-common-packages_2022-08-31-15-00.json | 10 + ...e-UI-common-packages_2022-08-31-15-00.json | 10 + ...e-UI-common-packages_2022-08-31-15-00.json | 10 + ...e-UI-common-packages_2022-08-31-15-00.json | 10 + ...e-UI-common-packages_2022-08-31-15-00.json | 10 + ...e-UI-common-packages_2022-08-31-15-00.json | 10 + ...e-UI-common-packages_2022-08-31-15-00.json | 10 + ...e-UI-common-packages_2022-08-31-15-00.json | 10 + ...e-UI-common-packages_2022-08-31-15-00.json | 10 + ...e-UI-common-packages_2022-08-31-15-00.json | 10 + .../rush/browser-approved-packages.json | 62 +- common/config/rush/command-line.json | 4 +- common/config/rush/pnpm-lock.yaml | 1420 +++--- rush.json | 6 + solutions/example-ui-app/package.json | 6 +- solutions/swb-app/package.json | 8 +- solutions/swb-reference/package.json | 8 +- solutions/swb-ui/.eslintignore | 6 +- solutions/swb-ui/infrastructure/package.json | 6 +- .../swb-ui/infrastructure/src/SWBUIStack.ts | 5 + .../swb-ui/infrastructure/src/constants.ts | 4 +- solutions/swb-ui/infrastructure/src/index.ts | 2 + solutions/swb-ui/package.json | 25 +- solutions/swb-ui/pnpm-lock.yaml | 71 - solutions/swb-ui/src/api/datasets.ts | 6 +- .../swb-ui/src/api/environmentTypeConfigs.ts | 6 +- solutions/swb-ui/src/api/environmentTypes.ts | 6 +- solutions/swb-ui/src/api/environments.ts | 12 +- solutions/swb-ui/src/api/projects.ts | 6 +- solutions/swb-ui/src/common/splitPanel.tsx | 147 - .../swb-ui/src/components/EnvTypeCards.tsx | 3 +- .../components/EnvironmentConnectModal.tsx | 2 +- solutions/swb-ui/src/models/Project.ts | 4 +- solutions/swb-ui/src/pages/_app.tsx | 10 +- .../swb-ui/src/pages/environments/index.tsx | 22 +- .../swb-ui/src/pages/environments/new.tsx | 3 +- solutions/swb-ui/src/pages/index.tsx | 3 +- solutions/swb-ui/src/pages/users/index.tsx | 7 +- solutions/swb-ui/src/pages/users/new.tsx | 5 +- workbench-core/audit/package.json | 8 +- workbench-core/authentication/package.json | 8 +- workbench-core/authorization/package.json | 8 +- workbench-core/base/package.json | 8 +- workbench-core/datasets/package.json | 8 +- workbench-core/environments/package.json | 8 +- workbench-core/eslint-custom/package.json | 8 +- workbench-core/example/express/package.json | 8 +- .../example/infrastructure/package.json | 8 +- workbench-core/infrastructure/package.json | 8 +- workbench-core/logging/package.json | 6 +- .../repo-scripts/repo-toolbox/package.json | 8 +- workbench-core/swb-common-ui/.depcheckrc | 20 + workbench-core/swb-common-ui/.eslintignore | 4 + workbench-core/swb-common-ui/.eslintrc.js | 17 + workbench-core/swb-common-ui/.npmignore | 33 + workbench-core/swb-common-ui/LICENSE | 201 + workbench-core/swb-common-ui/NOTICE | 4264 +++++++++++++++++ workbench-core/swb-common-ui/README.md | 9 + workbench-core/swb-common-ui/config/heft.json | 51 + .../swb-common-ui/config/jest.config.json | 14 + workbench-core/swb-common-ui/config/rig.json | 18 + .../swb-common-ui/license-add-config.json | 32 + workbench-core/swb-common-ui/package.json | 67 + .../swb-common-ui}/src/api/apiHelper.ts | 40 +- .../swb-common-ui}/src/api/auth.ts | 8 +- .../swb-common-ui}/src/api/users.ts | 6 +- .../swb-common-ui}/src/common/alerts.tsx | 2 +- .../src/common/dateRelativeOptions.tsx | 0 .../src/common/dateRelativeProperties.tsx | 4 +- .../swb-common-ui}/src/common/labels.tsx | 4 +- .../src/common/tableCounterStrings.tsx | 2 +- .../src/common/tableEmptyState.tsx | 1 + .../src/common/tableNoMatchState.tsx | 1 + .../swb-common-ui}/src/common/utils.ts | 11 +- .../src/components/BaseLayout.tsx | 8 +- .../swb-common-ui}/src/components/Header.tsx | 15 +- .../swb-common-ui}/src/components/Hero.tsx | 5 +- .../swb-common-ui}/src/components/Login.tsx | 10 +- .../src/components/Navigation.tsx | 2 +- .../src/components/RouteGuard.tsx | 4 +- .../src/context/AuthenticationContext.tsx | 4 +- .../src/context/NotificationContext.tsx | 8 +- .../src/context/SettingsContext.tsx | 8 +- workbench-core/swb-common-ui/src/index.tsx | 49 + .../swb-common-ui}/src/models/AppSettings.ts | 0 .../swb-common-ui}/src/models/User.ts | 1 + workbench-core/swb-common-ui/tsconfig.json | 21 + 87 files changed, 5990 insertions(+), 1023 deletions(-) create mode 100644 common/changes/@aws/swb-app/fix-chore-separate-UI-common-packages_2022-08-31-15-00.json create mode 100644 common/changes/@aws/workbench-core-audit/fix-chore-separate-UI-common-packages_2022-08-31-15-00.json create mode 100644 common/changes/@aws/workbench-core-authentication/fix-chore-separate-UI-common-packages_2022-08-31-15-00.json create mode 100644 common/changes/@aws/workbench-core-authorization/fix-chore-separate-UI-common-packages_2022-08-31-15-00.json create mode 100644 common/changes/@aws/workbench-core-base/fix-chore-separate-UI-common-packages_2022-08-31-15-00.json create mode 100644 common/changes/@aws/workbench-core-datasets/fix-chore-separate-UI-common-packages_2022-08-31-15-00.json create mode 100644 common/changes/@aws/workbench-core-environments/fix-chore-separate-UI-common-packages_2022-08-31-15-00.json create mode 100644 common/changes/@aws/workbench-core-infrastructure/fix-chore-separate-UI-common-packages_2022-08-31-15-00.json create mode 100644 common/changes/@aws/workbench-core-logging/fix-chore-separate-UI-common-packages_2022-08-31-15-00.json create mode 100644 common/changes/@aws/workbench-core-swb-common-ui/fix-chore-separate-UI-common-packages_2022-08-31-15-00.json delete mode 100644 solutions/swb-ui/pnpm-lock.yaml delete mode 100644 solutions/swb-ui/src/common/splitPanel.tsx create mode 100644 workbench-core/swb-common-ui/.depcheckrc create mode 100644 workbench-core/swb-common-ui/.eslintignore create mode 100644 workbench-core/swb-common-ui/.eslintrc.js create mode 100644 workbench-core/swb-common-ui/.npmignore create mode 100644 workbench-core/swb-common-ui/LICENSE create mode 100644 workbench-core/swb-common-ui/NOTICE create mode 100644 workbench-core/swb-common-ui/README.md create mode 100644 workbench-core/swb-common-ui/config/heft.json create mode 100644 workbench-core/swb-common-ui/config/jest.config.json create mode 100644 workbench-core/swb-common-ui/config/rig.json create mode 100644 workbench-core/swb-common-ui/license-add-config.json create mode 100644 workbench-core/swb-common-ui/package.json rename {solutions/swb-ui => workbench-core/swb-common-ui}/src/api/apiHelper.ts (76%) rename {solutions/swb-ui => workbench-core/swb-common-ui}/src/api/auth.ts (67%) rename {solutions/swb-ui => workbench-core/swb-common-ui}/src/api/users.ts (78%) rename {solutions/swb-ui => workbench-core/swb-common-ui}/src/common/alerts.tsx (95%) rename {solutions/swb-ui => workbench-core/swb-common-ui}/src/common/dateRelativeOptions.tsx (100%) rename {solutions/swb-ui => workbench-core/swb-common-ui}/src/common/dateRelativeProperties.tsx (92%) rename {solutions/swb-ui => workbench-core/swb-common-ui}/src/common/labels.tsx (94%) rename {solutions/swb-ui => workbench-core/swb-common-ui}/src/common/tableCounterStrings.tsx (69%) rename {solutions/swb-ui => workbench-core/swb-common-ui}/src/common/tableEmptyState.tsx (96%) rename {solutions/swb-ui => workbench-core/swb-common-ui}/src/common/tableNoMatchState.tsx (95%) rename {solutions/swb-ui => workbench-core/swb-common-ui}/src/common/utils.ts (56%) rename {solutions/swb-ui => workbench-core/swb-common-ui}/src/components/BaseLayout.tsx (91%) rename {solutions/swb-ui => workbench-core/swb-common-ui}/src/components/Header.tsx (75%) rename {solutions/swb-ui => workbench-core/swb-common-ui}/src/components/Hero.tsx (95%) rename {solutions/swb-ui => workbench-core/swb-common-ui}/src/components/Login.tsx (75%) rename {solutions/swb-ui => workbench-core/swb-common-ui}/src/components/Navigation.tsx (98%) rename {solutions/swb-ui => workbench-core/swb-common-ui}/src/components/RouteGuard.tsx (88%) rename {solutions/swb-ui => workbench-core/swb-common-ui}/src/context/AuthenticationContext.tsx (98%) rename {solutions/swb-ui => workbench-core/swb-common-ui}/src/context/NotificationContext.tsx (86%) rename {solutions/swb-ui => workbench-core/swb-common-ui}/src/context/SettingsContext.tsx (70%) create mode 100644 workbench-core/swb-common-ui/src/index.tsx rename {solutions/swb-ui => workbench-core/swb-common-ui}/src/models/AppSettings.ts (100%) rename {solutions/swb-ui => workbench-core/swb-common-ui}/src/models/User.ts (99%) create mode 100644 workbench-core/swb-common-ui/tsconfig.json diff --git a/common/changes/@aws/swb-app/fix-chore-separate-UI-common-packages_2022-08-31-15-00.json b/common/changes/@aws/swb-app/fix-chore-separate-UI-common-packages_2022-08-31-15-00.json new file mode 100644 index 0000000000..d63da96ac8 --- /dev/null +++ b/common/changes/@aws/swb-app/fix-chore-separate-UI-common-packages_2022-08-31-15-00.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@aws/swb-app", + "comment": "Updates for separating UI packages", + "type": "minor" + } + ], + "packageName": "@aws/swb-app" +} \ No newline at end of file diff --git a/common/changes/@aws/workbench-core-audit/fix-chore-separate-UI-common-packages_2022-08-31-15-00.json b/common/changes/@aws/workbench-core-audit/fix-chore-separate-UI-common-packages_2022-08-31-15-00.json new file mode 100644 index 0000000000..9f955620da --- /dev/null +++ b/common/changes/@aws/workbench-core-audit/fix-chore-separate-UI-common-packages_2022-08-31-15-00.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@aws/workbench-core-audit", + "comment": "Updates for separating UI packages", + "type": "minor" + } + ], + "packageName": "@aws/workbench-core-audit" +} \ No newline at end of file diff --git a/common/changes/@aws/workbench-core-authentication/fix-chore-separate-UI-common-packages_2022-08-31-15-00.json b/common/changes/@aws/workbench-core-authentication/fix-chore-separate-UI-common-packages_2022-08-31-15-00.json new file mode 100644 index 0000000000..7c4db541c9 --- /dev/null +++ b/common/changes/@aws/workbench-core-authentication/fix-chore-separate-UI-common-packages_2022-08-31-15-00.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@aws/workbench-core-authentication", + "comment": "Updates for separating UI packages", + "type": "minor" + } + ], + "packageName": "@aws/workbench-core-authentication" +} \ No newline at end of file diff --git a/common/changes/@aws/workbench-core-authorization/fix-chore-separate-UI-common-packages_2022-08-31-15-00.json b/common/changes/@aws/workbench-core-authorization/fix-chore-separate-UI-common-packages_2022-08-31-15-00.json new file mode 100644 index 0000000000..87a958c5a3 --- /dev/null +++ b/common/changes/@aws/workbench-core-authorization/fix-chore-separate-UI-common-packages_2022-08-31-15-00.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@aws/workbench-core-authorization", + "comment": "Updates for separating UI packages", + "type": "minor" + } + ], + "packageName": "@aws/workbench-core-authorization" +} \ No newline at end of file diff --git a/common/changes/@aws/workbench-core-base/fix-chore-separate-UI-common-packages_2022-08-31-15-00.json b/common/changes/@aws/workbench-core-base/fix-chore-separate-UI-common-packages_2022-08-31-15-00.json new file mode 100644 index 0000000000..b4deb7fb3c --- /dev/null +++ b/common/changes/@aws/workbench-core-base/fix-chore-separate-UI-common-packages_2022-08-31-15-00.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@aws/workbench-core-base", + "comment": "Updates for separating UI packages", + "type": "minor" + } + ], + "packageName": "@aws/workbench-core-base" +} \ No newline at end of file diff --git a/common/changes/@aws/workbench-core-datasets/fix-chore-separate-UI-common-packages_2022-08-31-15-00.json b/common/changes/@aws/workbench-core-datasets/fix-chore-separate-UI-common-packages_2022-08-31-15-00.json new file mode 100644 index 0000000000..6dd3fae822 --- /dev/null +++ b/common/changes/@aws/workbench-core-datasets/fix-chore-separate-UI-common-packages_2022-08-31-15-00.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@aws/workbench-core-datasets", + "comment": "Updates for separating UI packages", + "type": "minor" + } + ], + "packageName": "@aws/workbench-core-datasets" +} \ No newline at end of file diff --git a/common/changes/@aws/workbench-core-environments/fix-chore-separate-UI-common-packages_2022-08-31-15-00.json b/common/changes/@aws/workbench-core-environments/fix-chore-separate-UI-common-packages_2022-08-31-15-00.json new file mode 100644 index 0000000000..c1cee6ed55 --- /dev/null +++ b/common/changes/@aws/workbench-core-environments/fix-chore-separate-UI-common-packages_2022-08-31-15-00.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@aws/workbench-core-environments", + "comment": "Updates for separating UI packages", + "type": "minor" + } + ], + "packageName": "@aws/workbench-core-environments" +} \ No newline at end of file diff --git a/common/changes/@aws/workbench-core-infrastructure/fix-chore-separate-UI-common-packages_2022-08-31-15-00.json b/common/changes/@aws/workbench-core-infrastructure/fix-chore-separate-UI-common-packages_2022-08-31-15-00.json new file mode 100644 index 0000000000..857496c878 --- /dev/null +++ b/common/changes/@aws/workbench-core-infrastructure/fix-chore-separate-UI-common-packages_2022-08-31-15-00.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@aws/workbench-core-infrastructure", + "comment": "Updates for separating UI packages", + "type": "minor" + } + ], + "packageName": "@aws/workbench-core-infrastructure" +} \ No newline at end of file diff --git a/common/changes/@aws/workbench-core-logging/fix-chore-separate-UI-common-packages_2022-08-31-15-00.json b/common/changes/@aws/workbench-core-logging/fix-chore-separate-UI-common-packages_2022-08-31-15-00.json new file mode 100644 index 0000000000..9eb88c1114 --- /dev/null +++ b/common/changes/@aws/workbench-core-logging/fix-chore-separate-UI-common-packages_2022-08-31-15-00.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@aws/workbench-core-logging", + "comment": "Updates for separating UI packages", + "type": "minor" + } + ], + "packageName": "@aws/workbench-core-logging" +} \ No newline at end of file diff --git a/common/changes/@aws/workbench-core-swb-common-ui/fix-chore-separate-UI-common-packages_2022-08-31-15-00.json b/common/changes/@aws/workbench-core-swb-common-ui/fix-chore-separate-UI-common-packages_2022-08-31-15-00.json new file mode 100644 index 0000000000..8a14864a48 --- /dev/null +++ b/common/changes/@aws/workbench-core-swb-common-ui/fix-chore-separate-UI-common-packages_2022-08-31-15-00.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@aws/workbench-core-swb-common-ui", + "comment": "Updates for separating UI packages", + "type": "minor" + } + ], + "packageName": "@aws/workbench-core-swb-common-ui" +} \ No newline at end of file diff --git a/common/config/rush/browser-approved-packages.json b/common/config/rush/browser-approved-packages.json index 1075198f23..4b5a78bea4 100644 --- a/common/config/rush/browser-approved-packages.json +++ b/common/config/rush/browser-approved-packages.json @@ -134,6 +134,10 @@ "name": "@aws/workbench-core-logging", "allowedCategories": [ "production", "prototypes" ] }, + { + "name": "@aws/workbench-core-swb-common-ui", + "allowedCategories": [ "production" ] + }, { "name": "@awsui/collection-hooks", "allowedCategories": [ "production", "prototypes" ] @@ -158,10 +162,26 @@ "name": "@babel/core", "allowedCategories": [ "production", "prototypes" ] }, + { + "name": "@babel/plugin-syntax-dynamic-import", + "allowedCategories": [ "production" ] + }, + { + "name": "@babel/plugin-syntax-flow", + "allowedCategories": [ "production" ] + }, + { + "name": "@babel/plugin-transform-react-jsx", + "allowedCategories": [ "production" ] + }, { "name": "@babel/preset-env", "allowedCategories": [ "production", "prototypes" ] }, + { + "name": "@babel/preset-next", + "allowedCategories": [ "production" ] + }, { "name": "@babel/preset-react", "allowedCategories": [ "production", "prototypes" ] @@ -170,6 +190,10 @@ "name": "@babel/preset-typescript", "allowedCategories": [ "production", "prototypes" ] }, + { + "name": "@babel/runtime", + "allowedCategories": [ "production" ] + }, { "name": "@casl/ability", "allowedCategories": [ "production" ] @@ -214,6 +238,14 @@ "name": "@rushstack/heft-node-rig", "allowedCategories": [ "production", "prototypes" ] }, + { + "name": "@rushstack/heft-web-rig", + "allowedCategories": [ "production" ] + }, + { + "name": "@rushstack/heft-webpack5-plugin", + "allowedCategories": [ "production" ] + }, { "name": "@rushstack/node-core-library", "allowedCategories": [ "production" ] @@ -270,6 +302,14 @@ "name": "babel-jest", "allowedCategories": [ "production", "prototypes" ] }, + { + "name": "babel-loader", + "allowedCategories": [ "production" ] + }, + { + "name": "babel-preset-next", + "allowedCategories": [ "production" ] + }, { "name": "cdk-nag", "allowedCategories": [ "prototypes" ] @@ -295,15 +335,15 @@ "allowedCategories": [ "production" ] }, { - "name": "cypress", + "name": "csrf", "allowedCategories": [ "production" ] }, { - "name": "csrf", + "name": "csurf", "allowedCategories": [ "production" ] }, { - "name": "csurf", + "name": "cypress", "allowedCategories": [ "production" ] }, { @@ -442,6 +482,10 @@ "name": "react-dom", "allowedCategories": [ "production", "prototypes" ] }, + { + "name": "react-scripts", + "allowedCategories": [ "production" ] + }, { "name": "sass", "allowedCategories": [ "production", "prototypes" ] @@ -474,6 +518,10 @@ "name": "ts-node", "allowedCategories": [ "production" ] }, + { + "name": "tslib", + "allowedCategories": [ "production" ] + }, { "name": "typescript", "allowedCategories": [ "production", "prototypes" ] @@ -482,6 +530,14 @@ "name": "uuid", "allowedCategories": [ "production" ] }, + { + "name": "web-vitals", + "allowedCategories": [ "production" ] + }, + { + "name": "webpack", + "allowedCategories": [ "production" ] + }, { "name": "winston", "allowedCategories": [ "production" ] diff --git a/common/config/rush/command-line.json b/common/config/rush/command-line.json index db2c0d1e25..926894ea95 100644 --- a/common/config/rush/command-line.json +++ b/common/config/rush/command-line.json @@ -304,14 +304,14 @@ "commandKind": "global", "summary": "Run rush install with bypass-policy and perform custom git-hook install", "safeForSimultaneousRushProcesses": true, - "shellCommand": "node common/scripts/install-run-rush.js install --bypass-policy && node common/scripts/install-hooks" + "shellCommand": "node common/scripts/install-run-rush.js install --bypass-policy --max-install-attempts 1 && node common/scripts/install-hooks" }, { "name": "cupdate", "commandKind": "global", "summary": "Run rush update with bypass-policy and perform custom git-hook install", "safeForSimultaneousRushProcesses": true, - "shellCommand": "node common/scripts/install-run-rush.js update --bypass-policy && node common/scripts/install-hooks" + "shellCommand": "node common/scripts/install-run-rush.js update --bypass-policy --max-install-attempts 1 && node common/scripts/install-hooks" } ], diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index 617dc1a5e0..921fb9eb2e 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -17,9 +17,9 @@ importers: '@babel/preset-env': ^7.16.11 '@babel/preset-react': ^7.16.7 '@babel/preset-typescript': ^7.16.7 - '@rushstack/eslint-config': ^2.5.1 - '@rushstack/heft': ^0.45.0 - '@rushstack/heft-jest-plugin': ^0.2.3 + '@rushstack/eslint-config': ^3.0.0 + '@rushstack/heft': ^0.47.5 + '@rushstack/heft-jest-plugin': ^0.3.28 '@testing-library/dom': ^8.13.0 '@testing-library/jest-dom': 5.16.1 '@testing-library/react': 12.1.2 @@ -68,9 +68,9 @@ importers: '@babel/preset-env': 7.18.2_@babel+core@7.18.2 '@babel/preset-react': 7.17.12_@babel+core@7.18.2 '@babel/preset-typescript': 7.17.12_@babel+core@7.18.2 - '@rushstack/eslint-config': 2.6.0_eslint@8.16.0+typescript@4.7.2 - '@rushstack/heft': 0.45.5 - '@rushstack/heft-jest-plugin': 0.2.15_@rushstack+heft@0.45.5 + '@rushstack/eslint-config': 3.0.0_eslint@8.16.0+typescript@4.7.2 + '@rushstack/heft': 0.47.5 + '@rushstack/heft-jest-plugin': 0.3.28_@rushstack+heft@0.47.5 '@testing-library/dom': 8.13.0 '@testing-library/jest-dom': 5.16.1 '@testing-library/react': 12.1.2_react-dom@17.0.2+react@17.0.2 @@ -105,10 +105,10 @@ importers: '@aws/workbench-core-environments': workspace:* '@aws/workbench-core-logging': workspace:* '@hapi/boom': ^10.0.0 - '@rushstack/eslint-config': ^2.5.1 - '@rushstack/heft': ^0.45.0 - '@rushstack/heft-jest-plugin': ^0.2.3 - '@rushstack/heft-node-rig': ^1.7.1 + '@rushstack/eslint-config': ^3.0.0 + '@rushstack/heft': ^0.47.5 + '@rushstack/heft-jest-plugin': ^0.3.28 + '@rushstack/heft-node-rig': ^1.10.5 '@types/cookie-parser': ^1.4.3 '@types/cors': ^2.8.12 '@types/express': ^4.17.13 @@ -152,10 +152,10 @@ importers: uuid: 8.3.2 devDependencies: '@aws/eslint-config-workbench-core-eslint-custom': link:../../workbench-core/eslint-custom - '@rushstack/eslint-config': 2.6.0_eslint@8.16.0+typescript@4.7.2 - '@rushstack/heft': 0.45.5 - '@rushstack/heft-jest-plugin': 0.2.15_@rushstack+heft@0.45.5 - '@rushstack/heft-node-rig': 1.9.6_@rushstack+heft@0.45.5 + '@rushstack/eslint-config': 3.0.0_eslint@8.16.0+typescript@4.7.2 + '@rushstack/heft': 0.47.5 + '@rushstack/heft-jest-plugin': 0.3.28_@rushstack+heft@0.47.5 + '@rushstack/heft-node-rig': 1.10.5_@rushstack+heft@0.47.5 '@types/cookie-parser': 1.4.3 '@types/cors': 2.8.12 '@types/express': 4.17.13 @@ -198,10 +198,10 @@ importers: '@aws/workbench-core-environments': workspace:* '@aws/workbench-core-infrastructure': workspace:* '@aws/workbench-core-logging': workspace:* - '@rushstack/eslint-config': ^2.5.1 - '@rushstack/heft': ^0.45.0 - '@rushstack/heft-jest-plugin': ^0.2.3 - '@rushstack/heft-node-rig': ^1.7.1 + '@rushstack/eslint-config': ^3.0.0 + '@rushstack/heft': ^0.47.5 + '@rushstack/heft-jest-plugin': ^0.3.28 + '@rushstack/heft-node-rig': ^1.10.5 '@types/aws-lambda': ^8.10.92 '@types/express': ^4.17.13 '@types/heft-jest': 1.0.2 @@ -218,8 +218,8 @@ importers: axios: ^0.27.1 cdk-ssm-document: ^3.1.1 constructs: ^10.0.0 - cypress: ^10.6.0 csrf: ^3.1.0 + cypress: ^10.6.0 depcheck: ^1.4.3 esbuild: ^0.14.21 eslint: ^8.7.0 @@ -265,10 +265,10 @@ importers: devDependencies: '@aws-sdk/client-sagemaker': 3.130.0 '@aws/eslint-config-workbench-core-eslint-custom': link:../../workbench-core/eslint-custom - '@rushstack/eslint-config': 2.6.0_eslint@8.16.0+typescript@4.7.2 - '@rushstack/heft': 0.45.5 - '@rushstack/heft-jest-plugin': 0.2.15_04f3c65f6de4a5e5529a6fededa44e1d - '@rushstack/heft-node-rig': 1.9.6_04f3c65f6de4a5e5529a6fededa44e1d + '@rushstack/eslint-config': 3.0.0_eslint@8.16.0+typescript@4.7.2 + '@rushstack/heft': 0.47.5 + '@rushstack/heft-jest-plugin': 0.3.28_3d8d10679dc80a68f97d705c60d2bfb3 + '@rushstack/heft-node-rig': 1.10.5_3d8d10679dc80a68f97d705c60d2bfb3 '@types/aws-lambda': 8.10.97 '@types/express': 4.17.13 '@types/heft-jest': 1.0.2 @@ -304,6 +304,7 @@ importers: specifiers: '@aws/eslint-config-workbench-core-eslint-custom': workspace:* '@aws/workbench-core-environments': workspace:* + '@aws/workbench-core-swb-common-ui': workspace:* '@babel/core': ^7.17.9 '@babel/preset-env': ^7.16.11 '@babel/preset-react': ^7.16.7 @@ -312,42 +313,33 @@ importers: '@cloudscape-design/components': ^3.0.27 '@cloudscape-design/design-tokens': ^3.0.1 '@cloudscape-design/global-styles': ^1.0.1 - '@rushstack/eslint-config': ^2.5.1 - '@rushstack/heft': ^0.45.0 - '@rushstack/heft-jest-plugin': ^0.2.3 - '@rushstack/heft-node-rig': ^1.7.1 + '@rushstack/eslint-config': ^3.0.0 + '@rushstack/heft': ^0.47.5 + '@rushstack/heft-jest-plugin': ^0.3.28 + '@rushstack/heft-node-rig': ^1.10.5 '@testing-library/dom': ^8.13.0 '@testing-library/jest-dom': 5.16.1 '@testing-library/react': 12.1.2 '@testing-library/user-event': 13.5.0 - '@types/express': ^4.17.13 '@types/heft-jest': 1.0.2 '@types/jest': ^27.4.1 '@types/node': ^14 '@types/react': 17.0.38 - '@types/uuid': ^8.3.4 - axios: ^0.27.1 babel-jest: 27.5.1 - concurrently: ^7.0.0 - date-fns: ^2.28.0 depcheck: ^1.4.3 eslint: ^8.7.0 eslint-config-next: 12.1.5 eslint-plugin-import: ^2.26.0 eslint-plugin-testing-library: 5.0.1 - express: ^4.17.3 istanbul-badges-readme: 1.8.1 jest: ^27.5.1 - jwt-decode: ^3.1.2 license-check-and-add: ^4.0.5 license-checker: ^25.0.1 next: ^12.1.5 next-global-css: ^1.2.0 next-transpile-modules: ^9.0.0 - nodemon: ^2.0.19 npm-package-json-lint: ^6.3.0 npm-package-json-lint-config-default: ^5.0.0 - pkce-challenge: ^3.0.0 react: 17.0.2 react-dom: 17.0.2 sass: ^1.49.8 @@ -356,21 +348,15 @@ importers: swr: ^1.3.0 ts-jest: ^27.1.3 typescript: ^4.5.2 - uuid: ^8.3.2 dependencies: '@aws/workbench-core-environments': link:../../workbench-core/environments - axios: 0.27.2 - date-fns: 2.28.0 - express: 4.18.1 - jwt-decode: 3.1.2 + '@aws/workbench-core-swb-common-ui': link:../../workbench-core/swb-common-ui next: 12.1.6_fe3859d3c8c94a7c7e52ff5ea4f038fc next-global-css: 1.3.1 next-transpile-modules: 9.0.0 - pkce-challenge: 3.0.0 react: 17.0.2 react-dom: 17.0.2_react@17.0.2 swr: 1.3.0_react@17.0.2 - uuid: 8.3.2 devDependencies: '@aws/eslint-config-workbench-core-eslint-custom': link:../../workbench-core/eslint-custom '@babel/core': 7.18.2 @@ -381,22 +367,19 @@ importers: '@cloudscape-design/components': 3.0.27_b3482aaf5744fc7c2aeb7941b0e0a78f '@cloudscape-design/design-tokens': 3.0.1 '@cloudscape-design/global-styles': 1.0.1 - '@rushstack/eslint-config': 2.6.0_eslint@8.16.0+typescript@4.7.2 - '@rushstack/heft': 0.45.5 - '@rushstack/heft-jest-plugin': 0.2.15_@rushstack+heft@0.45.5 - '@rushstack/heft-node-rig': 1.9.6_@rushstack+heft@0.45.5 + '@rushstack/eslint-config': 3.0.0_eslint@8.16.0+typescript@4.7.2 + '@rushstack/heft': 0.47.5 + '@rushstack/heft-jest-plugin': 0.3.28_@rushstack+heft@0.47.5 + '@rushstack/heft-node-rig': 1.10.5_@rushstack+heft@0.47.5 '@testing-library/dom': 8.13.0 '@testing-library/jest-dom': 5.16.1 '@testing-library/react': 12.1.2_react-dom@17.0.2+react@17.0.2 '@testing-library/user-event': 13.5.0_@testing-library+dom@8.13.0 - '@types/express': 4.17.13 '@types/heft-jest': 1.0.2 '@types/jest': 27.5.2 '@types/node': 14.18.20 '@types/react': 17.0.38 - '@types/uuid': 8.3.4 babel-jest: 27.5.1_@babel+core@7.18.2 - concurrently: 7.2.1 depcheck: 1.4.3 eslint: 8.16.0 eslint-config-next: 12.1.5_5794735a2262de3d29e0baaa885b5942 @@ -406,7 +389,6 @@ importers: jest: 27.5.1 license-check-and-add: 4.0.5 license-checker: 25.0.1 - nodemon: 2.0.19 npm-package-json-lint: 6.3.0 npm-package-json-lint-config-default: 5.0.0_npm-package-json-lint@6.3.0 sass: 1.52.2 @@ -417,9 +399,9 @@ importers: ../../solutions/swb-ui/infrastructure: specifiers: - '@rushstack/heft': ^0.45.0 - '@rushstack/heft-jest-plugin': ^0.2.3 - '@rushstack/heft-node-rig': ^1.7.1 + '@rushstack/heft': ^0.47.5 + '@rushstack/heft-jest-plugin': ^0.3.28 + '@rushstack/heft-node-rig': ^1.10.5 '@types/jest': ^27.4.1 '@types/js-yaml': ^4.0.5 '@types/node': ^14 @@ -439,9 +421,9 @@ importers: dependencies: js-yaml: 4.1.0 devDependencies: - '@rushstack/heft': 0.45.5 - '@rushstack/heft-jest-plugin': 0.2.15_04f3c65f6de4a5e5529a6fededa44e1d - '@rushstack/heft-node-rig': 1.9.6_04f3c65f6de4a5e5529a6fededa44e1d + '@rushstack/heft': 0.47.5 + '@rushstack/heft-jest-plugin': 0.3.28_3d8d10679dc80a68f97d705c60d2bfb3 + '@rushstack/heft-node-rig': 1.10.5_3d8d10679dc80a68f97d705c60d2bfb3 '@types/jest': 27.5.2 '@types/js-yaml': 4.0.5 '@types/node': 14.18.20 @@ -462,10 +444,10 @@ importers: specifiers: '@aws/eslint-config-workbench-core-eslint-custom': workspace:* '@aws/workbench-core-authorization': workspace:* - '@rushstack/eslint-config': ^2.5.1 - '@rushstack/heft': ^0.45.0 - '@rushstack/heft-jest-plugin': ^0.2.3 - '@rushstack/heft-node-rig': ^1.7.1 + '@rushstack/eslint-config': ^3.0.0 + '@rushstack/heft': ^0.47.5 + '@rushstack/heft-jest-plugin': ^0.3.28 + '@rushstack/heft-node-rig': ^1.10.5 '@types/express': ^4.17.13 '@types/heft-jest': 1.0.2 '@types/lodash': ^4.14.181 @@ -486,10 +468,10 @@ importers: lodash: 4.17.21 devDependencies: '@aws/eslint-config-workbench-core-eslint-custom': link:../eslint-custom - '@rushstack/eslint-config': 2.6.0_eslint@8.16.0+typescript@4.7.2 - '@rushstack/heft': 0.45.5 - '@rushstack/heft-jest-plugin': 0.2.15_@rushstack+heft@0.45.5 - '@rushstack/heft-node-rig': 1.9.6_@rushstack+heft@0.45.5 + '@rushstack/eslint-config': 3.0.0_eslint@8.16.0+typescript@4.7.2 + '@rushstack/heft': 0.47.5 + '@rushstack/heft-jest-plugin': 0.3.28_@rushstack+heft@0.47.5 + '@rushstack/heft-node-rig': 1.10.5_@rushstack+heft@0.47.5 '@types/express': 4.17.13 '@types/heft-jest': 1.0.2 '@types/lodash': 4.14.182 @@ -513,10 +495,10 @@ importers: '@aws/eslint-config-workbench-core-eslint-custom': workspace:* '@aws/workbench-core-authorization': workspace:* '@aws/workbench-core-logging': workspace:* - '@rushstack/eslint-config': ^2.5.1 - '@rushstack/heft': ^0.45.0 - '@rushstack/heft-jest-plugin': ^0.2.3 - '@rushstack/heft-node-rig': ^1.7.1 + '@rushstack/eslint-config': ^3.0.0 + '@rushstack/heft': ^0.47.5 + '@rushstack/heft-jest-plugin': ^0.3.28 + '@rushstack/heft-node-rig': ^1.10.5 '@types/csurf': ^1.11.2 '@types/express': ^4.17.13 '@types/heft-jest': 1.0.2 @@ -551,10 +533,10 @@ importers: '@aws-sdk/client-s3': 3.130.0 '@aws-sdk/types': 3.127.0 '@aws/eslint-config-workbench-core-eslint-custom': link:../eslint-custom - '@rushstack/eslint-config': 2.6.0_eslint@8.16.0+typescript@4.7.2 - '@rushstack/heft': 0.45.5 - '@rushstack/heft-jest-plugin': 0.2.15_@rushstack+heft@0.45.5 - '@rushstack/heft-node-rig': 1.9.6_@rushstack+heft@0.45.5 + '@rushstack/eslint-config': 3.0.0_eslint@8.16.0+typescript@4.7.2 + '@rushstack/heft': 0.47.5 + '@rushstack/heft-jest-plugin': 0.3.28_@rushstack+heft@0.47.5 + '@rushstack/heft-node-rig': 1.10.5_@rushstack+heft@0.47.5 '@types/csurf': 1.11.2 '@types/express': 4.17.13 '@types/heft-jest': 1.0.2 @@ -577,10 +559,10 @@ importers: '@aws/eslint-config-workbench-core-eslint-custom': workspace:* '@aws/workbench-core-logging': workspace:* '@casl/ability': ^5.4.3 - '@rushstack/eslint-config': ^2.5.1 - '@rushstack/heft': ^0.45.0 - '@rushstack/heft-jest-plugin': ^0.2.3 - '@rushstack/heft-node-rig': ^1.7.1 + '@rushstack/eslint-config': ^3.0.0 + '@rushstack/heft': ^0.47.5 + '@rushstack/heft-jest-plugin': ^0.3.28 + '@rushstack/heft-node-rig': ^1.10.5 '@types/express': ^4.17.13 '@types/heft-jest': 1.0.2 '@types/lodash': ^4.14.181 @@ -605,10 +587,10 @@ importers: lodash: 4.17.21 devDependencies: '@aws/eslint-config-workbench-core-eslint-custom': link:../eslint-custom - '@rushstack/eslint-config': 2.6.0_eslint@8.16.0+typescript@4.7.2 - '@rushstack/heft': 0.45.5 - '@rushstack/heft-jest-plugin': 0.2.15_@rushstack+heft@0.45.5 - '@rushstack/heft-node-rig': 1.9.6_@rushstack+heft@0.45.5 + '@rushstack/eslint-config': 3.0.0_eslint@8.16.0+typescript@4.7.2 + '@rushstack/heft': 0.47.5 + '@rushstack/heft-jest-plugin': 0.3.28_@rushstack+heft@0.47.5 + '@rushstack/heft-node-rig': 1.10.5_@rushstack+heft@0.47.5 '@types/express': 4.17.13 '@types/heft-jest': 1.0.2 '@types/lodash': 4.14.182 @@ -649,10 +631,10 @@ importers: '@aws/eslint-config-workbench-core-eslint-custom': workspace:* '@aws/workbench-core-audit': workspace:* '@aws/workbench-core-logging': workspace:* - '@rushstack/eslint-config': ^2.5.1 - '@rushstack/heft': ^0.45.0 - '@rushstack/heft-jest-plugin': ^0.2.3 - '@rushstack/heft-node-rig': ^1.7.1 + '@rushstack/eslint-config': ^3.0.0 + '@rushstack/heft': ^0.47.5 + '@rushstack/heft-jest-plugin': ^0.3.28 + '@rushstack/heft-node-rig': ^1.10.5 '@types/heft-jest': 1.0.2 '@types/jest': ^27.4.1 '@types/js-yaml': ^4.0.5 @@ -694,10 +676,10 @@ importers: lodash: 4.17.21 devDependencies: '@aws/eslint-config-workbench-core-eslint-custom': link:../eslint-custom - '@rushstack/eslint-config': 2.6.0_eslint@8.16.0+typescript@4.7.2 - '@rushstack/heft': 0.45.5 - '@rushstack/heft-jest-plugin': 0.2.15_@rushstack+heft@0.45.5 - '@rushstack/heft-node-rig': 1.9.6_@rushstack+heft@0.45.5 + '@rushstack/eslint-config': 3.0.0_eslint@8.16.0+typescript@4.7.2 + '@rushstack/heft': 0.47.5 + '@rushstack/heft-jest-plugin': 0.3.28_@rushstack+heft@0.47.5 + '@rushstack/heft-node-rig': 1.10.5_@rushstack+heft@0.47.5 '@types/heft-jest': 1.0.2 '@types/jest': 27.5.2 '@types/js-yaml': 4.0.5 @@ -729,10 +711,10 @@ importers: '@aws/workbench-core-base': workspace:* '@aws/workbench-core-logging': workspace:* '@hapi/boom': ^10.0.0 - '@rushstack/eslint-config': ^2.5.1 - '@rushstack/heft': ^0.45.0 - '@rushstack/heft-jest-plugin': ^0.2.3 - '@rushstack/heft-node-rig': ^1.7.1 + '@rushstack/eslint-config': ^3.0.0 + '@rushstack/heft': ^0.47.5 + '@rushstack/heft-jest-plugin': ^0.3.28 + '@rushstack/heft-node-rig': ^1.10.5 '@types/heft-jest': 1.0.2 '@types/lodash': ^4.14.181 '@types/node': ^14 @@ -772,10 +754,10 @@ importers: uuid: 8.3.2 devDependencies: '@aws/eslint-config-workbench-core-eslint-custom': link:../eslint-custom - '@rushstack/eslint-config': 2.6.0_eslint@8.16.0+typescript@4.7.2 - '@rushstack/heft': 0.45.5 - '@rushstack/heft-jest-plugin': 0.2.15_@rushstack+heft@0.45.5 - '@rushstack/heft-node-rig': 1.9.6_@rushstack+heft@0.45.5 + '@rushstack/eslint-config': 3.0.0_eslint@8.16.0+typescript@4.7.2 + '@rushstack/heft': 0.47.5 + '@rushstack/heft-jest-plugin': 0.3.28_@rushstack+heft@0.47.5 + '@rushstack/heft-node-rig': 1.10.5_@rushstack+heft@0.47.5 '@types/heft-jest': 1.0.2 '@types/lodash': 4.14.182 '@types/node': 14.18.20 @@ -820,10 +802,10 @@ importers: '@aws/workbench-core-datasets': workspace:* '@aws/workbench-core-logging': workspace:* '@hapi/boom': ^10.0.0 - '@rushstack/eslint-config': ^2.5.1 - '@rushstack/heft': ^0.45.0 - '@rushstack/heft-jest-plugin': ^0.2.3 - '@rushstack/heft-node-rig': ^1.7.1 + '@rushstack/eslint-config': ^3.0.0 + '@rushstack/heft': ^0.47.5 + '@rushstack/heft-jest-plugin': ^0.3.28 + '@rushstack/heft-node-rig': ^1.10.5 '@types/heft-jest': 1.0.2 '@types/jest': ^27.4.1 '@types/lodash': ^4.14.181 @@ -875,10 +857,10 @@ importers: devDependencies: '@aws-sdk/types': 3.127.0 '@aws/eslint-config-workbench-core-eslint-custom': link:../eslint-custom - '@rushstack/eslint-config': 2.6.0_eslint@8.16.0+typescript@4.7.2 - '@rushstack/heft': 0.45.5 - '@rushstack/heft-jest-plugin': 0.2.15_@rushstack+heft@0.45.5 - '@rushstack/heft-node-rig': 1.9.6_@rushstack+heft@0.45.5 + '@rushstack/eslint-config': 3.0.0_eslint@8.16.0+typescript@4.7.2 + '@rushstack/heft': 0.47.5 + '@rushstack/heft-jest-plugin': 0.3.28_@rushstack+heft@0.47.5 + '@rushstack/heft-node-rig': 1.10.5_@rushstack+heft@0.47.5 '@types/heft-jest': 1.0.2 '@types/jest': 27.5.2 '@types/lodash': 4.14.182 @@ -900,10 +882,10 @@ importers: ../../workbench-core/eslint-custom: specifiers: - '@rushstack/eslint-config': ^2.5.1 - '@rushstack/heft': ^0.45.0 - '@rushstack/heft-jest-plugin': ^0.2.3 - '@rushstack/heft-node-rig': ^1.7.1 + '@rushstack/eslint-config': ^3.0.0 + '@rushstack/heft': ^0.47.5 + '@rushstack/heft-jest-plugin': ^0.3.28 + '@rushstack/heft-node-rig': ^1.10.5 '@types/heft-jest': 1.0.2 '@types/node': ^14 depcheck: ^1.4.3 @@ -917,14 +899,14 @@ importers: sort-package-json: ^1.57.0 typescript: ^4.5.2 dependencies: - '@rushstack/eslint-config': 2.6.0_eslint@8.16.0+typescript@4.7.2 + '@rushstack/eslint-config': 3.0.0_eslint@8.16.0+typescript@4.7.2 eslint: 8.16.0 eslint-plugin-import: 2.26.0_eslint@8.16.0 eslint-plugin-security: 1.5.0 devDependencies: - '@rushstack/heft': 0.45.5 - '@rushstack/heft-jest-plugin': 0.2.15_@rushstack+heft@0.45.5 - '@rushstack/heft-node-rig': 1.9.6_@rushstack+heft@0.45.5 + '@rushstack/heft': 0.47.5 + '@rushstack/heft-jest-plugin': 0.3.28_@rushstack+heft@0.47.5 + '@rushstack/heft-node-rig': 1.10.5_@rushstack+heft@0.47.5 '@types/heft-jest': 1.0.2 '@types/node': 14.18.20 depcheck: 1.4.3 @@ -940,10 +922,10 @@ importers: '@aws/eslint-config-workbench-core-eslint-custom': workspace:* '@aws/workbench-core-audit': workspace:* '@aws/workbench-core-logging': workspace:* - '@rushstack/eslint-config': ^2.5.1 - '@rushstack/heft': ^0.45.0 - '@rushstack/heft-jest-plugin': ^0.2.3 - '@rushstack/heft-node-rig': ^1.7.1 + '@rushstack/eslint-config': ^3.0.0 + '@rushstack/heft': ^0.47.5 + '@rushstack/heft-jest-plugin': ^0.3.28 + '@rushstack/heft-node-rig': ^1.10.5 '@types/express': ^4.17.13 '@types/heft-jest': 1.0.2 '@types/node': ^14 @@ -963,10 +945,10 @@ importers: '@aws/workbench-core-logging': link:../../logging devDependencies: '@aws/eslint-config-workbench-core-eslint-custom': link:../../eslint-custom - '@rushstack/eslint-config': 2.6.0_eslint@8.16.0+typescript@4.7.2 - '@rushstack/heft': 0.45.5 - '@rushstack/heft-jest-plugin': 0.2.15_@rushstack+heft@0.45.5 - '@rushstack/heft-node-rig': 1.9.6_@rushstack+heft@0.45.5 + '@rushstack/eslint-config': 3.0.0_eslint@8.16.0+typescript@4.7.2 + '@rushstack/heft': 0.47.5 + '@rushstack/heft-jest-plugin': 0.3.28_@rushstack+heft@0.47.5 + '@rushstack/heft-node-rig': 1.10.5_@rushstack+heft@0.47.5 '@types/express': 4.17.13 '@types/heft-jest': 1.0.2 '@types/node': 14.18.20 @@ -990,10 +972,10 @@ importers: '@aws-cdk/aws-apigatewayv2-integrations-alpha': ^2.22.0-alpha.0 '@aws/eslint-config-workbench-core-eslint-custom': workspace:* '@aws/workbench-core-example-express': workspace:* - '@rushstack/eslint-config': ^2.5.1 - '@rushstack/heft': ^0.45.0 - '@rushstack/heft-jest-plugin': ^0.2.3 - '@rushstack/heft-node-rig': ^1.7.1 + '@rushstack/eslint-config': ^3.0.0 + '@rushstack/heft': ^0.47.5 + '@rushstack/heft-jest-plugin': ^0.3.28 + '@rushstack/heft-node-rig': ^1.10.5 '@types/express': ^4.17.13 '@types/heft-jest': 1.0.2 '@types/lodash': ^4.14.181 @@ -1032,10 +1014,10 @@ importers: '@aws-cdk/aws-apigatewayv2-authorizers-alpha': 2.22.0-alpha.0_321f8d5612d8741dd602b5547195cfdc '@aws-cdk/aws-apigatewayv2-integrations-alpha': 2.22.0-alpha.0_321f8d5612d8741dd602b5547195cfdc '@aws/eslint-config-workbench-core-eslint-custom': link:../../eslint-custom - '@rushstack/eslint-config': 2.6.0_eslint@8.16.0+typescript@4.7.2 - '@rushstack/heft': 0.45.5 - '@rushstack/heft-jest-plugin': 0.2.15_@rushstack+heft@0.45.5 - '@rushstack/heft-node-rig': 1.9.6_@rushstack+heft@0.45.5 + '@rushstack/eslint-config': 3.0.0_eslint@8.16.0+typescript@4.7.2 + '@rushstack/heft': 0.47.5 + '@rushstack/heft-jest-plugin': 0.3.28_@rushstack+heft@0.47.5 + '@rushstack/heft-node-rig': 1.10.5_@rushstack+heft@0.47.5 '@types/express': 4.17.13 '@types/heft-jest': 1.0.2 '@types/lodash': 4.14.182 @@ -1062,10 +1044,10 @@ importers: ../../workbench-core/infrastructure: specifiers: '@aws/eslint-config-workbench-core-eslint-custom': workspace:* - '@rushstack/eslint-config': ^2.5.1 - '@rushstack/heft': ^0.45.0 - '@rushstack/heft-jest-plugin': ^0.2.3 - '@rushstack/heft-node-rig': ^1.7.1 + '@rushstack/eslint-config': ^3.0.0 + '@rushstack/heft': ^0.47.5 + '@rushstack/heft-jest-plugin': ^0.3.28 + '@rushstack/heft-node-rig': ^1.10.5 '@types/heft-jest': 1.0.2 '@types/lodash': ^4.14.181 '@types/node': ^14 @@ -1089,10 +1071,10 @@ importers: lodash: 4.17.21 devDependencies: '@aws/eslint-config-workbench-core-eslint-custom': link:../eslint-custom - '@rushstack/eslint-config': 2.6.0_eslint@8.16.0+typescript@4.7.2 - '@rushstack/heft': 0.45.5 - '@rushstack/heft-jest-plugin': 0.2.15_@rushstack+heft@0.45.5 - '@rushstack/heft-node-rig': 1.9.6_@rushstack+heft@0.45.5 + '@rushstack/eslint-config': 3.0.0_eslint@8.16.0+typescript@4.7.2 + '@rushstack/heft': 0.47.5 + '@rushstack/heft-jest-plugin': 0.3.28_@rushstack+heft@0.47.5 + '@rushstack/heft-node-rig': 1.10.5_@rushstack+heft@0.47.5 '@types/heft-jest': 1.0.2 '@types/lodash': 4.14.182 '@types/node': 14.18.20 @@ -1111,9 +1093,9 @@ importers: ../../workbench-core/logging: specifiers: '@aws/eslint-config-workbench-core-eslint-custom': workspace:* - '@rushstack/heft': ^0.45.0 - '@rushstack/heft-jest-plugin': ^0.2.3 - '@rushstack/heft-node-rig': ^1.7.1 + '@rushstack/heft': ^0.47.5 + '@rushstack/heft-jest-plugin': ^0.3.28 + '@rushstack/heft-node-rig': ^1.10.5 '@types/heft-jest': 1.0.2 '@types/node': ^14 '@types/triple-beam': ^1.3.2 @@ -1140,9 +1122,9 @@ importers: winston-transport: 4.5.0 devDependencies: '@aws/eslint-config-workbench-core-eslint-custom': link:../eslint-custom - '@rushstack/heft': 0.45.5 - '@rushstack/heft-jest-plugin': 0.2.15_@rushstack+heft@0.45.5 - '@rushstack/heft-node-rig': 1.9.6_@rushstack+heft@0.45.5 + '@rushstack/heft': 0.47.5 + '@rushstack/heft-jest-plugin': 0.3.28_@rushstack+heft@0.47.5 + '@rushstack/heft-node-rig': 1.10.5_@rushstack+heft@0.47.5 '@types/heft-jest': 1.0.2 '@types/node': 14.18.20 depcheck: 1.4.3 @@ -1163,10 +1145,10 @@ importers: specifiers: '@aws/eslint-config-workbench-core-eslint-custom': workspace:* '@microsoft/rush-lib': ^5.69.0 - '@rushstack/eslint-config': ^2.5.1 - '@rushstack/heft': ^0.45.0 - '@rushstack/heft-jest-plugin': ^0.2.3 - '@rushstack/heft-node-rig': ^1.7.1 + '@rushstack/eslint-config': ^3.0.0 + '@rushstack/heft': ^0.47.5 + '@rushstack/heft-jest-plugin': ^0.3.28 + '@rushstack/heft-node-rig': ^1.10.5 '@rushstack/node-core-library': ^3.45.5 '@rushstack/ts-command-line': ^4.11.0 '@types/diff': 5.0.1 @@ -1190,10 +1172,10 @@ importers: diff: 5.1.0 devDependencies: '@aws/eslint-config-workbench-core-eslint-custom': link:../../eslint-custom - '@rushstack/eslint-config': 2.6.0_eslint@8.16.0+typescript@4.7.2 - '@rushstack/heft': 0.45.5 - '@rushstack/heft-jest-plugin': 0.2.15_@rushstack+heft@0.45.5 - '@rushstack/heft-node-rig': 1.9.6_@rushstack+heft@0.45.5 + '@rushstack/eslint-config': 3.0.0_eslint@8.16.0+typescript@4.7.2 + '@rushstack/heft': 0.47.5 + '@rushstack/heft-jest-plugin': 0.3.28_@rushstack+heft@0.47.5 + '@rushstack/heft-node-rig': 1.10.5_@rushstack+heft@0.47.5 '@types/diff': 5.0.1 '@types/heft-jest': 1.0.2 '@types/node': 14.18.20 @@ -1208,6 +1190,71 @@ importers: sort-package-json: 1.57.0 typescript: 4.7.2 + ../../workbench-core/swb-common-ui: + specifiers: + '@aws/eslint-config-workbench-core-eslint-custom': workspace:* + '@cloudscape-design/components': ^3.0.27 + '@rushstack/eslint-config': ^3.0.0 + '@rushstack/heft': ^0.47.5 + '@rushstack/heft-jest-plugin': ^0.3.28 + '@rushstack/heft-node-rig': ^1.10.5 + '@types/heft-jest': 1.0.2 + '@types/node': ^14 + '@types/react': 17.0.38 + '@types/uuid': ^8.3.4 + axios: ^0.27.1 + date-fns: ^2.28.0 + depcheck: ^1.4.3 + eslint: ^8.7.0 + eslint-plugin-import: ^2.26.0 + istanbul-badges-readme: 1.8.1 + jwt-decode: ^3.1.2 + license-check-and-add: ^4.0.5 + license-checker: ^25.0.1 + next: ^12.1.5 + npm-package-json-lint: ^6.3.0 + npm-package-json-lint-config-default: ^5.0.0 + pkce-challenge: ^3.0.0 + react: 17.0.2 + react-dom: 17.0.2 + sass: ^1.49.8 + sort-package-json: ^1.57.0 + swr: ^1.3.0 + typescript: ^4.5.2 + uuid: ^8.3.2 + dependencies: + axios: 0.27.2 + date-fns: 2.28.0 + jwt-decode: 3.1.2 + next: 12.1.6_757e1cdb914f1581201e1269321c5a94 + pkce-challenge: 3.0.0 + react: 17.0.2 + react-dom: 17.0.2_react@17.0.2 + swr: 1.3.0_react@17.0.2 + typescript: 4.7.2 + uuid: 8.3.2 + devDependencies: + '@aws/eslint-config-workbench-core-eslint-custom': link:../eslint-custom + '@cloudscape-design/components': 3.0.27_b3482aaf5744fc7c2aeb7941b0e0a78f + '@rushstack/eslint-config': 3.0.0_eslint@8.16.0+typescript@4.7.2 + '@rushstack/heft': 0.47.5 + '@rushstack/heft-jest-plugin': 0.3.28_@rushstack+heft@0.47.5 + '@rushstack/heft-node-rig': 1.10.5_@rushstack+heft@0.47.5 + '@types/heft-jest': 1.0.2 + '@types/node': 14.18.20 + '@types/react': 17.0.38 + '@types/uuid': 8.3.4 + depcheck: 1.4.3 + eslint: 8.16.0 + eslint-plugin-import: 2.26.0_eslint@8.16.0 + istanbul-badges-readme: 1.8.1 + license-check-and-add: 4.0.5 + license-checker: 25.0.1 + npm-package-json-lint: 6.3.0 + npm-package-json-lint-config-default: 5.0.0_npm-package-json-lint@6.3.0 + sass: 1.52.2 + sort-package-json: 1.57.0 + packages: /@ampproject/remapping/2.2.0: @@ -1215,7 +1262,7 @@ packages: engines: {node: '>=6.0.0'} dependencies: '@jridgewell/gen-mapping': 0.1.1 - '@jridgewell/trace-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.15 dev: true /@aws-cdk/assert/2.26.0_c9e2f77c41c390210db2e247727e987d: @@ -3773,6 +3820,13 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/highlight': 7.17.12 + dev: true + + /@babel/code-frame/7.18.6: + resolution: {integrity: sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/highlight': 7.18.6 /@babel/compat-data/7.17.10: resolution: {integrity: sha512-GZt/TCsG70Ms19gfZO1tM4CVnXsPgEPBCpJu+Qz3L0LUDsY5nZqFZglIoPC1kIYOtNBZlrnFT+klg12vFGZXrw==} @@ -3784,15 +3838,15 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@ampproject/remapping': 2.2.0 - '@babel/code-frame': 7.16.7 - '@babel/generator': 7.18.2 + '@babel/code-frame': 7.18.6 + '@babel/generator': 7.18.13 '@babel/helper-compilation-targets': 7.18.2_@babel+core@7.18.2 '@babel/helper-module-transforms': 7.18.0 '@babel/helpers': 7.18.2 - '@babel/parser': 7.18.4 - '@babel/template': 7.16.7 - '@babel/traverse': 7.18.2 - '@babel/types': 7.18.4 + '@babel/parser': 7.18.13 + '@babel/template': 7.18.10 + '@babel/traverse': 7.18.13 + '@babel/types': 7.18.13 convert-source-map: 1.8.0 debug: 4.3.4 gensync: 1.0.0-beta.2 @@ -3802,6 +3856,15 @@ packages: - supports-color dev: true + /@babel/generator/7.18.13: + resolution: {integrity: sha512-CkPg8ySSPuHTYPJYo7IRALdqyjM9HCbt/3uOBEFbzyGVP6Mn8bwFPB0jX6982JVNBlYzM1nnPkfjuXSOPtQeEQ==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.18.13 + '@jridgewell/gen-mapping': 0.3.2 + jsesc: 2.5.2 + dev: true + /@babel/generator/7.18.2: resolution: {integrity: sha512-W1lG5vUwFvfMd8HVXqdfbuG7RuaSrTCCD8cl8fP8wOivdbtbIg2Db3IWUcgvfxKbbn6ZBGYRW/Zk1MIwK49mgw==} engines: {node: '>=6.9.0'} @@ -3811,11 +3874,11 @@ packages: jsesc: 2.5.2 dev: true - /@babel/helper-annotate-as-pure/7.16.7: - resolution: {integrity: sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==} + /@babel/helper-annotate-as-pure/7.18.6: + resolution: {integrity: sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.18.4 + '@babel/types': 7.18.13 dev: true /@babel/helper-builder-binary-assignment-operator-visitor/7.16.7: @@ -3823,7 +3886,7 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/helper-explode-assignable-expression': 7.16.7 - '@babel/types': 7.18.4 + '@babel/types': 7.18.13 dev: true /@babel/helper-compilation-targets/7.18.2_@babel+core@7.18.2: @@ -3835,24 +3898,24 @@ packages: '@babel/compat-data': 7.17.10 '@babel/core': 7.18.2 '@babel/helper-validator-option': 7.16.7 - browserslist: 4.20.3 + browserslist: 4.21.3 semver: 6.3.0 dev: true - /@babel/helper-create-class-features-plugin/7.18.0_@babel+core@7.18.2: - resolution: {integrity: sha512-Kh8zTGR9de3J63e5nS0rQUdRs/kbtwoeQQ0sriS0lItjC96u8XXZN6lKpuyWd2coKSU13py/y+LTmThLuVX0Pg==} + /@babel/helper-create-class-features-plugin/7.18.13_@babel+core@7.18.2: + resolution: {integrity: sha512-hDvXp+QYxSRL+23mpAlSGxHMDyIGChm0/AwTfTAAK5Ufe40nCsyNdaYCGuK91phn/fVu9kqayImRDkvNAgdrsA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: '@babel/core': 7.18.2 - '@babel/helper-annotate-as-pure': 7.16.7 - '@babel/helper-environment-visitor': 7.18.2 - '@babel/helper-function-name': 7.17.9 - '@babel/helper-member-expression-to-functions': 7.17.7 - '@babel/helper-optimise-call-expression': 7.16.7 - '@babel/helper-replace-supers': 7.18.2 - '@babel/helper-split-export-declaration': 7.16.7 + '@babel/helper-annotate-as-pure': 7.18.6 + '@babel/helper-environment-visitor': 7.18.9 + '@babel/helper-function-name': 7.18.9 + '@babel/helper-member-expression-to-functions': 7.18.9 + '@babel/helper-optimise-call-expression': 7.18.6 + '@babel/helper-replace-supers': 7.18.9 + '@babel/helper-split-export-declaration': 7.18.6 transitivePeerDependencies: - supports-color dev: true @@ -3864,23 +3927,21 @@ packages: '@babel/core': ^7.0.0 dependencies: '@babel/core': 7.18.2 - '@babel/helper-annotate-as-pure': 7.16.7 + '@babel/helper-annotate-as-pure': 7.18.6 regexpu-core: 5.0.1 dev: true - /@babel/helper-define-polyfill-provider/0.3.1_@babel+core@7.18.2: - resolution: {integrity: sha512-J9hGMpJQmtWmj46B3kBHmL38UhJGhYX7eqkcq+2gsstyYt341HmPeWspihX43yVRA0mS+8GGk2Gckc7bY/HCmA==} + /@babel/helper-define-polyfill-provider/0.3.2_@babel+core@7.18.2: + resolution: {integrity: sha512-r9QJJ+uDWrd+94BSPcP6/de67ygLtvVy6cK4luE6MOuDsZIdoaPBnfSpbO/+LTifjPckbKXRuI9BB/Z2/y3iTg==} peerDependencies: '@babel/core': ^7.4.0-0 dependencies: '@babel/core': 7.18.2 '@babel/helper-compilation-targets': 7.18.2_@babel+core@7.18.2 - '@babel/helper-module-imports': 7.16.7 - '@babel/helper-plugin-utils': 7.17.12 - '@babel/traverse': 7.18.2 + '@babel/helper-plugin-utils': 7.18.9 debug: 4.3.4 lodash.debounce: 4.0.8 - resolve: 1.22.0 + resolve: 1.22.1 semver: 6.3.0 transitivePeerDependencies: - supports-color @@ -3891,11 +3952,16 @@ packages: engines: {node: '>=6.9.0'} dev: true + /@babel/helper-environment-visitor/7.18.9: + resolution: {integrity: sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==} + engines: {node: '>=6.9.0'} + dev: true + /@babel/helper-explode-assignable-expression/7.16.7: resolution: {integrity: sha512-KyUenhWMC8VrxzkGP0Jizjo4/Zx+1nNZhgocs+gLzyZyB8SHidhoq9KK/8Ato4anhwsivfkBLftky7gvzbZMtQ==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.18.4 + '@babel/types': 7.18.13 dev: true /@babel/helper-function-name/7.17.9: @@ -3906,6 +3972,14 @@ packages: '@babel/types': 7.18.4 dev: true + /@babel/helper-function-name/7.18.9: + resolution: {integrity: sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/template': 7.18.10 + '@babel/types': 7.18.13 + dev: true + /@babel/helper-hoist-variables/7.16.7: resolution: {integrity: sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==} engines: {node: '>=6.9.0'} @@ -3913,45 +3987,52 @@ packages: '@babel/types': 7.18.4 dev: true - /@babel/helper-member-expression-to-functions/7.17.7: - resolution: {integrity: sha512-thxXgnQ8qQ11W2wVUObIqDL4p148VMxkt5T/qpN5k2fboRyzFGFmKsTGViquyM5QHKUy48OZoca8kw4ajaDPyw==} + /@babel/helper-hoist-variables/7.18.6: + resolution: {integrity: sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.18.4 + '@babel/types': 7.18.13 dev: true - /@babel/helper-module-imports/7.16.7: - resolution: {integrity: sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==} + /@babel/helper-member-expression-to-functions/7.18.9: + resolution: {integrity: sha512-RxifAh2ZoVU67PyKIO4AMi1wTenGfMR/O/ae0CCRqwgBAt5v7xjdtRw7UoSbsreKrQn5t7r89eruK/9JjYHuDg==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.18.4 + '@babel/types': 7.18.13 + dev: true + + /@babel/helper-module-imports/7.18.6: + resolution: {integrity: sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.18.13 dev: true /@babel/helper-module-transforms/7.18.0: resolution: {integrity: sha512-kclUYSUBIjlvnzN2++K9f2qzYKFgjmnmjwL4zlmU5f8ZtzgWe8s0rUPSTGy2HmK4P8T52MQsS+HTQAgZd3dMEA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/helper-environment-visitor': 7.18.2 - '@babel/helper-module-imports': 7.16.7 + '@babel/helper-environment-visitor': 7.18.9 + '@babel/helper-module-imports': 7.18.6 '@babel/helper-simple-access': 7.18.2 - '@babel/helper-split-export-declaration': 7.16.7 - '@babel/helper-validator-identifier': 7.16.7 - '@babel/template': 7.16.7 - '@babel/traverse': 7.18.2 - '@babel/types': 7.18.4 + '@babel/helper-split-export-declaration': 7.18.6 + '@babel/helper-validator-identifier': 7.18.6 + '@babel/template': 7.18.10 + '@babel/traverse': 7.18.13 + '@babel/types': 7.18.13 transitivePeerDependencies: - supports-color dev: true - /@babel/helper-optimise-call-expression/7.16.7: - resolution: {integrity: sha512-EtgBhg7rd/JcnpZFXpBy0ze1YRfdm7BnBX4uKMBd3ixa3RGAE002JZB66FJyNH7g0F38U05pXmA5P8cBh7z+1w==} + /@babel/helper-optimise-call-expression/7.18.6: + resolution: {integrity: sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.18.4 + '@babel/types': 7.18.13 dev: true - /@babel/helper-plugin-utils/7.17.12: - resolution: {integrity: sha512-JDkf04mqtN3y4iAbO1hv9U2ARpPyPL1zqyWs/2WG1pgSq9llHFjStX5jdxb84himgJm+8Ng+x0oiWF/nw/XQKA==} + /@babel/helper-plugin-utils/7.18.9: + resolution: {integrity: sha512-aBXPT3bmtLryXaoJLyYPXPlSD4p1ld9aYeR+sJNOZjJJGiOpb+fKfh3NkcCu7J54nUJwCERPBExCCpyCOHnu/w==} engines: {node: '>=6.9.0'} dev: true @@ -3959,22 +4040,22 @@ packages: resolution: {integrity: sha512-fm0gH7Flb8H51LqJHy3HJ3wnE1+qtYR2A99K06ahwrawLdOFsCEWjZOrYricXJHoPSudNKxrMBUPEIPxiIIvBw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/helper-annotate-as-pure': 7.16.7 + '@babel/helper-annotate-as-pure': 7.18.6 '@babel/helper-wrap-function': 7.16.8 - '@babel/types': 7.18.4 + '@babel/types': 7.18.13 transitivePeerDependencies: - supports-color dev: true - /@babel/helper-replace-supers/7.18.2: - resolution: {integrity: sha512-XzAIyxx+vFnrOxiQrToSUOzUOn0e1J2Li40ntddek1Y69AXUTXoDJ40/D5RdjFu7s7qHiaeoTiempZcbuVXh2Q==} + /@babel/helper-replace-supers/7.18.9: + resolution: {integrity: sha512-dNsWibVI4lNT6HiuOIBr1oyxo40HvIVmbwPUm3XZ7wMh4k2WxrxTqZwSqw/eEmXDS9np0ey5M2bz9tBmO9c+YQ==} engines: {node: '>=6.9.0'} dependencies: - '@babel/helper-environment-visitor': 7.18.2 - '@babel/helper-member-expression-to-functions': 7.17.7 - '@babel/helper-optimise-call-expression': 7.16.7 - '@babel/traverse': 7.18.2 - '@babel/types': 7.18.4 + '@babel/helper-environment-visitor': 7.18.9 + '@babel/helper-member-expression-to-functions': 7.18.9 + '@babel/helper-optimise-call-expression': 7.18.6 + '@babel/traverse': 7.18.13 + '@babel/types': 7.18.13 transitivePeerDependencies: - supports-color dev: true @@ -3983,14 +4064,14 @@ packages: resolution: {integrity: sha512-7LIrjYzndorDY88MycupkpQLKS1AFfsVRm2k/9PtKScSy5tZq0McZTj+DiMRynboZfIqOKvo03pmhTaUgiD6fQ==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.18.4 + '@babel/types': 7.18.13 dev: true /@babel/helper-skip-transparent-expression-wrappers/7.16.0: resolution: {integrity: sha512-+il1gTy0oHwUsBQZyJvukbB4vPMdcYBrFHa0Uc4AizLxbq6BOYC51Rv4tWocX9BLBDLZ4kc6qUFpQ6HRgL+3zw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.18.4 + '@babel/types': 7.18.13 dev: true /@babel/helper-split-export-declaration/7.16.7: @@ -4000,9 +4081,26 @@ packages: '@babel/types': 7.18.4 dev: true + /@babel/helper-split-export-declaration/7.18.6: + resolution: {integrity: sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.18.13 + dev: true + + /@babel/helper-string-parser/7.18.10: + resolution: {integrity: sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw==} + engines: {node: '>=6.9.0'} + dev: true + /@babel/helper-validator-identifier/7.16.7: resolution: {integrity: sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==} engines: {node: '>=6.9.0'} + dev: true + + /@babel/helper-validator-identifier/7.18.6: + resolution: {integrity: sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==} + engines: {node: '>=6.9.0'} /@babel/helper-validator-option/7.16.7: resolution: {integrity: sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ==} @@ -4013,10 +4111,10 @@ packages: resolution: {integrity: sha512-8RpyRVIAW1RcDDGTA+GpPAwV22wXCfKOoM9bet6TLkGIFTkRQSkH1nMQ5Yet4MpoXe1ZwHPVtNasc2w0uZMqnw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/helper-function-name': 7.17.9 - '@babel/template': 7.16.7 - '@babel/traverse': 7.18.2 - '@babel/types': 7.18.4 + '@babel/helper-function-name': 7.18.9 + '@babel/template': 7.18.10 + '@babel/traverse': 7.18.13 + '@babel/types': 7.18.13 transitivePeerDependencies: - supports-color dev: true @@ -4025,9 +4123,9 @@ packages: resolution: {integrity: sha512-j+d+u5xT5utcQSzrh9p+PaJX94h++KN+ng9b9WEJq7pkUPAd61FGqhjuUEdfknb3E/uDBb7ruwEeKkIxNJPIrg==} engines: {node: '>=6.9.0'} dependencies: - '@babel/template': 7.16.7 - '@babel/traverse': 7.18.2 - '@babel/types': 7.18.4 + '@babel/template': 7.18.10 + '@babel/traverse': 7.18.13 + '@babel/types': 7.18.13 transitivePeerDependencies: - supports-color dev: true @@ -4039,6 +4137,15 @@ packages: '@babel/helper-validator-identifier': 7.16.7 chalk: 2.4.2 js-tokens: 4.0.0 + dev: true + + /@babel/highlight/7.18.6: + resolution: {integrity: sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-validator-identifier': 7.18.6 + chalk: 2.4.2 + js-tokens: 4.0.0 /@babel/parser/7.16.4: resolution: {integrity: sha512-6V0qdPUaiVHH3RtZeLIsc+6pDhbYzHR8ogA8w+f+Wc77DuXto19g2QUwveINoS34Uw+W8/hQDGJCx+i4n7xcng==} @@ -4046,6 +4153,12 @@ packages: hasBin: true dev: true + /@babel/parser/7.18.13: + resolution: {integrity: sha512-dgXcIfMuQ0kgzLB2b9tRZs7TTFFaGM2AbtA4fJgUUYukzGH4jwsS7hzQHEGs67jdehpm22vkgKwvbU+aEflgwg==} + engines: {node: '>=6.0.0'} + hasBin: true + dev: true + /@babel/parser/7.18.4: resolution: {integrity: sha512-FDge0dFazETFcxGw/EXzOkN8uJp0PC7Qbm+Pe9T+av2zlBpOgunFHkQPPn+eRuClU73JF+98D531UgayY89tow==} engines: {node: '>=6.0.0'} @@ -4059,7 +4172,7 @@ packages: '@babel/core': ^7.0.0 dependencies: '@babel/core': 7.18.2 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-plugin-utils': 7.18.9 dev: true /@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/7.17.12_@babel+core@7.18.2: @@ -4069,7 +4182,7 @@ packages: '@babel/core': ^7.13.0 dependencies: '@babel/core': 7.18.2 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-plugin-utils': 7.18.9 '@babel/helper-skip-transparent-expression-wrappers': 7.16.0 '@babel/plugin-proposal-optional-chaining': 7.17.12_@babel+core@7.18.2 dev: true @@ -4081,7 +4194,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.2 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-plugin-utils': 7.18.9 '@babel/helper-remap-async-to-generator': 7.16.8 '@babel/plugin-syntax-async-generators': 7.8.4_@babel+core@7.18.2 transitivePeerDependencies: @@ -4095,8 +4208,8 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.2 - '@babel/helper-create-class-features-plugin': 7.18.0_@babel+core@7.18.2 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-create-class-features-plugin': 7.18.13_@babel+core@7.18.2 + '@babel/helper-plugin-utils': 7.18.9 transitivePeerDependencies: - supports-color dev: true @@ -4108,8 +4221,8 @@ packages: '@babel/core': ^7.12.0 dependencies: '@babel/core': 7.18.2 - '@babel/helper-create-class-features-plugin': 7.18.0_@babel+core@7.18.2 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-create-class-features-plugin': 7.18.13_@babel+core@7.18.2 + '@babel/helper-plugin-utils': 7.18.9 '@babel/plugin-syntax-class-static-block': 7.14.5_@babel+core@7.18.2 transitivePeerDependencies: - supports-color @@ -4122,7 +4235,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.2 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-plugin-utils': 7.18.9 '@babel/plugin-syntax-dynamic-import': 7.8.3_@babel+core@7.18.2 dev: true @@ -4133,7 +4246,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.2 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-plugin-utils': 7.18.9 '@babel/plugin-syntax-export-namespace-from': 7.8.3_@babel+core@7.18.2 dev: true @@ -4144,7 +4257,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.2 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-plugin-utils': 7.18.9 '@babel/plugin-syntax-json-strings': 7.8.3_@babel+core@7.18.2 dev: true @@ -4155,7 +4268,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.2 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-plugin-utils': 7.18.9 '@babel/plugin-syntax-logical-assignment-operators': 7.10.4_@babel+core@7.18.2 dev: true @@ -4166,7 +4279,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.2 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-plugin-utils': 7.18.9 '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3_@babel+core@7.18.2 dev: true @@ -4177,7 +4290,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.2 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-plugin-utils': 7.18.9 '@babel/plugin-syntax-numeric-separator': 7.10.4_@babel+core@7.18.2 dev: true @@ -4190,7 +4303,7 @@ packages: '@babel/compat-data': 7.17.10 '@babel/core': 7.18.2 '@babel/helper-compilation-targets': 7.18.2_@babel+core@7.18.2 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-plugin-utils': 7.18.9 '@babel/plugin-syntax-object-rest-spread': 7.8.3_@babel+core@7.18.2 '@babel/plugin-transform-parameters': 7.17.12_@babel+core@7.18.2 dev: true @@ -4202,7 +4315,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.2 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-plugin-utils': 7.18.9 '@babel/plugin-syntax-optional-catch-binding': 7.8.3_@babel+core@7.18.2 dev: true @@ -4213,7 +4326,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.2 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-plugin-utils': 7.18.9 '@babel/helper-skip-transparent-expression-wrappers': 7.16.0 '@babel/plugin-syntax-optional-chaining': 7.8.3_@babel+core@7.18.2 dev: true @@ -4225,8 +4338,8 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.2 - '@babel/helper-create-class-features-plugin': 7.18.0_@babel+core@7.18.2 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-create-class-features-plugin': 7.18.13_@babel+core@7.18.2 + '@babel/helper-plugin-utils': 7.18.9 transitivePeerDependencies: - supports-color dev: true @@ -4238,9 +4351,9 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.2 - '@babel/helper-annotate-as-pure': 7.16.7 - '@babel/helper-create-class-features-plugin': 7.18.0_@babel+core@7.18.2 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-annotate-as-pure': 7.18.6 + '@babel/helper-create-class-features-plugin': 7.18.13_@babel+core@7.18.2 + '@babel/helper-plugin-utils': 7.18.9 '@babel/plugin-syntax-private-property-in-object': 7.14.5_@babel+core@7.18.2 transitivePeerDependencies: - supports-color @@ -4254,7 +4367,7 @@ packages: dependencies: '@babel/core': 7.18.2 '@babel/helper-create-regexp-features-plugin': 7.17.12_@babel+core@7.18.2 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-plugin-utils': 7.18.9 dev: true /@babel/plugin-syntax-async-generators/7.8.4_@babel+core@7.18.2: @@ -4263,7 +4376,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.2 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-plugin-utils': 7.18.9 dev: true /@babel/plugin-syntax-bigint/7.8.3_@babel+core@7.18.2: @@ -4272,7 +4385,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.2 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-plugin-utils': 7.18.9 dev: true /@babel/plugin-syntax-class-properties/7.12.13_@babel+core@7.18.2: @@ -4281,7 +4394,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.2 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-plugin-utils': 7.18.9 dev: true /@babel/plugin-syntax-class-static-block/7.14.5_@babel+core@7.18.2: @@ -4291,7 +4404,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.2 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-plugin-utils': 7.18.9 dev: true /@babel/plugin-syntax-dynamic-import/7.8.3_@babel+core@7.18.2: @@ -4300,7 +4413,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.2 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-plugin-utils': 7.18.9 dev: true /@babel/plugin-syntax-export-namespace-from/7.8.3_@babel+core@7.18.2: @@ -4309,7 +4422,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.2 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-plugin-utils': 7.18.9 dev: true /@babel/plugin-syntax-import-assertions/7.17.12_@babel+core@7.18.2: @@ -4319,7 +4432,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.2 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-plugin-utils': 7.18.9 dev: true /@babel/plugin-syntax-import-meta/7.10.4_@babel+core@7.18.2: @@ -4328,7 +4441,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.2 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-plugin-utils': 7.18.9 dev: true /@babel/plugin-syntax-json-strings/7.8.3_@babel+core@7.18.2: @@ -4337,7 +4450,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.2 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-plugin-utils': 7.18.9 dev: true /@babel/plugin-syntax-jsx/7.17.12_@babel+core@7.18.2: @@ -4347,7 +4460,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.2 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-plugin-utils': 7.18.9 dev: true /@babel/plugin-syntax-logical-assignment-operators/7.10.4_@babel+core@7.18.2: @@ -4356,7 +4469,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.2 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-plugin-utils': 7.18.9 dev: true /@babel/plugin-syntax-nullish-coalescing-operator/7.8.3_@babel+core@7.18.2: @@ -4365,7 +4478,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.2 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-plugin-utils': 7.18.9 dev: true /@babel/plugin-syntax-numeric-separator/7.10.4_@babel+core@7.18.2: @@ -4374,7 +4487,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.2 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-plugin-utils': 7.18.9 dev: true /@babel/plugin-syntax-object-rest-spread/7.8.3_@babel+core@7.18.2: @@ -4383,7 +4496,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.2 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-plugin-utils': 7.18.9 dev: true /@babel/plugin-syntax-optional-catch-binding/7.8.3_@babel+core@7.18.2: @@ -4392,7 +4505,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.2 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-plugin-utils': 7.18.9 dev: true /@babel/plugin-syntax-optional-chaining/7.8.3_@babel+core@7.18.2: @@ -4401,7 +4514,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.2 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-plugin-utils': 7.18.9 dev: true /@babel/plugin-syntax-private-property-in-object/7.14.5_@babel+core@7.18.2: @@ -4411,7 +4524,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.2 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-plugin-utils': 7.18.9 dev: true /@babel/plugin-syntax-top-level-await/7.14.5_@babel+core@7.18.2: @@ -4421,7 +4534,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.2 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-plugin-utils': 7.18.9 dev: true /@babel/plugin-syntax-typescript/7.17.12_@babel+core@7.18.2: @@ -4431,7 +4544,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.2 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-plugin-utils': 7.18.9 dev: true /@babel/plugin-transform-arrow-functions/7.17.12_@babel+core@7.18.2: @@ -4441,7 +4554,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.2 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-plugin-utils': 7.18.9 dev: true /@babel/plugin-transform-async-to-generator/7.17.12_@babel+core@7.18.2: @@ -4451,8 +4564,8 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.2 - '@babel/helper-module-imports': 7.16.7 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-module-imports': 7.18.6 + '@babel/helper-plugin-utils': 7.18.9 '@babel/helper-remap-async-to-generator': 7.16.8 transitivePeerDependencies: - supports-color @@ -4465,7 +4578,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.2 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-plugin-utils': 7.18.9 dev: true /@babel/plugin-transform-block-scoping/7.18.4_@babel+core@7.18.2: @@ -4475,7 +4588,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.2 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-plugin-utils': 7.18.9 dev: true /@babel/plugin-transform-classes/7.18.4_@babel+core@7.18.2: @@ -4485,13 +4598,13 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.2 - '@babel/helper-annotate-as-pure': 7.16.7 - '@babel/helper-environment-visitor': 7.18.2 - '@babel/helper-function-name': 7.17.9 - '@babel/helper-optimise-call-expression': 7.16.7 - '@babel/helper-plugin-utils': 7.17.12 - '@babel/helper-replace-supers': 7.18.2 - '@babel/helper-split-export-declaration': 7.16.7 + '@babel/helper-annotate-as-pure': 7.18.6 + '@babel/helper-environment-visitor': 7.18.9 + '@babel/helper-function-name': 7.18.9 + '@babel/helper-optimise-call-expression': 7.18.6 + '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-replace-supers': 7.18.9 + '@babel/helper-split-export-declaration': 7.18.6 globals: 11.12.0 transitivePeerDependencies: - supports-color @@ -4504,7 +4617,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.2 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-plugin-utils': 7.18.9 dev: true /@babel/plugin-transform-destructuring/7.18.0_@babel+core@7.18.2: @@ -4514,7 +4627,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.2 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-plugin-utils': 7.18.9 dev: true /@babel/plugin-transform-dotall-regex/7.16.7_@babel+core@7.18.2: @@ -4525,7 +4638,7 @@ packages: dependencies: '@babel/core': 7.18.2 '@babel/helper-create-regexp-features-plugin': 7.17.12_@babel+core@7.18.2 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-plugin-utils': 7.18.9 dev: true /@babel/plugin-transform-duplicate-keys/7.17.12_@babel+core@7.18.2: @@ -4535,7 +4648,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.2 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-plugin-utils': 7.18.9 dev: true /@babel/plugin-transform-exponentiation-operator/7.16.7_@babel+core@7.18.2: @@ -4546,7 +4659,7 @@ packages: dependencies: '@babel/core': 7.18.2 '@babel/helper-builder-binary-assignment-operator-visitor': 7.16.7 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-plugin-utils': 7.18.9 dev: true /@babel/plugin-transform-for-of/7.18.1_@babel+core@7.18.2: @@ -4556,7 +4669,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.2 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-plugin-utils': 7.18.9 dev: true /@babel/plugin-transform-function-name/7.16.7_@babel+core@7.18.2: @@ -4567,8 +4680,8 @@ packages: dependencies: '@babel/core': 7.18.2 '@babel/helper-compilation-targets': 7.18.2_@babel+core@7.18.2 - '@babel/helper-function-name': 7.17.9 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-function-name': 7.18.9 + '@babel/helper-plugin-utils': 7.18.9 dev: true /@babel/plugin-transform-literals/7.17.12_@babel+core@7.18.2: @@ -4578,7 +4691,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.2 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-plugin-utils': 7.18.9 dev: true /@babel/plugin-transform-member-expression-literals/7.16.7_@babel+core@7.18.2: @@ -4588,7 +4701,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.2 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-plugin-utils': 7.18.9 dev: true /@babel/plugin-transform-modules-amd/7.18.0_@babel+core@7.18.2: @@ -4599,7 +4712,7 @@ packages: dependencies: '@babel/core': 7.18.2 '@babel/helper-module-transforms': 7.18.0 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-plugin-utils': 7.18.9 babel-plugin-dynamic-import-node: 2.3.3 transitivePeerDependencies: - supports-color @@ -4613,7 +4726,7 @@ packages: dependencies: '@babel/core': 7.18.2 '@babel/helper-module-transforms': 7.18.0 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-plugin-utils': 7.18.9 '@babel/helper-simple-access': 7.18.2 babel-plugin-dynamic-import-node: 2.3.3 transitivePeerDependencies: @@ -4627,10 +4740,10 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.2 - '@babel/helper-hoist-variables': 7.16.7 + '@babel/helper-hoist-variables': 7.18.6 '@babel/helper-module-transforms': 7.18.0 - '@babel/helper-plugin-utils': 7.17.12 - '@babel/helper-validator-identifier': 7.16.7 + '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-validator-identifier': 7.18.6 babel-plugin-dynamic-import-node: 2.3.3 transitivePeerDependencies: - supports-color @@ -4644,7 +4757,7 @@ packages: dependencies: '@babel/core': 7.18.2 '@babel/helper-module-transforms': 7.18.0 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-plugin-utils': 7.18.9 transitivePeerDependencies: - supports-color dev: true @@ -4657,7 +4770,7 @@ packages: dependencies: '@babel/core': 7.18.2 '@babel/helper-create-regexp-features-plugin': 7.17.12_@babel+core@7.18.2 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-plugin-utils': 7.18.9 dev: true /@babel/plugin-transform-new-target/7.17.12_@babel+core@7.18.2: @@ -4667,7 +4780,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.2 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-plugin-utils': 7.18.9 dev: true /@babel/plugin-transform-object-super/7.16.7_@babel+core@7.18.2: @@ -4677,8 +4790,8 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.2 - '@babel/helper-plugin-utils': 7.17.12 - '@babel/helper-replace-supers': 7.18.2 + '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-replace-supers': 7.18.9 transitivePeerDependencies: - supports-color dev: true @@ -4690,7 +4803,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.2 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-plugin-utils': 7.18.9 dev: true /@babel/plugin-transform-property-literals/7.16.7_@babel+core@7.18.2: @@ -4700,7 +4813,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.2 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-plugin-utils': 7.18.9 dev: true /@babel/plugin-transform-react-display-name/7.16.7_@babel+core@7.18.2: @@ -4710,7 +4823,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.2 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-plugin-utils': 7.18.9 dev: true /@babel/plugin-transform-react-jsx-development/7.16.7_@babel+core@7.18.2: @@ -4730,11 +4843,11 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.2 - '@babel/helper-annotate-as-pure': 7.16.7 - '@babel/helper-module-imports': 7.16.7 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-annotate-as-pure': 7.18.6 + '@babel/helper-module-imports': 7.18.6 + '@babel/helper-plugin-utils': 7.18.9 '@babel/plugin-syntax-jsx': 7.17.12_@babel+core@7.18.2 - '@babel/types': 7.18.4 + '@babel/types': 7.18.13 dev: true /@babel/plugin-transform-react-pure-annotations/7.18.0_@babel+core@7.18.2: @@ -4744,8 +4857,8 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.2 - '@babel/helper-annotate-as-pure': 7.16.7 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-annotate-as-pure': 7.18.6 + '@babel/helper-plugin-utils': 7.18.9 dev: true /@babel/plugin-transform-regenerator/7.18.0_@babel+core@7.18.2: @@ -4755,7 +4868,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.2 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-plugin-utils': 7.18.9 regenerator-transform: 0.15.0 dev: true @@ -4766,7 +4879,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.2 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-plugin-utils': 7.18.9 dev: true /@babel/plugin-transform-shorthand-properties/7.16.7_@babel+core@7.18.2: @@ -4776,7 +4889,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.2 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-plugin-utils': 7.18.9 dev: true /@babel/plugin-transform-spread/7.17.12_@babel+core@7.18.2: @@ -4786,7 +4899,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.2 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-plugin-utils': 7.18.9 '@babel/helper-skip-transparent-expression-wrappers': 7.16.0 dev: true @@ -4797,7 +4910,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.2 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-plugin-utils': 7.18.9 dev: true /@babel/plugin-transform-template-literals/7.18.2_@babel+core@7.18.2: @@ -4807,7 +4920,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.2 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-plugin-utils': 7.18.9 dev: true /@babel/plugin-transform-typeof-symbol/7.17.12_@babel+core@7.18.2: @@ -4817,7 +4930,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.2 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-plugin-utils': 7.18.9 dev: true /@babel/plugin-transform-typescript/7.18.4_@babel+core@7.18.2: @@ -4827,8 +4940,8 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.2 - '@babel/helper-create-class-features-plugin': 7.18.0_@babel+core@7.18.2 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-create-class-features-plugin': 7.18.13_@babel+core@7.18.2 + '@babel/helper-plugin-utils': 7.18.9 '@babel/plugin-syntax-typescript': 7.17.12_@babel+core@7.18.2 transitivePeerDependencies: - supports-color @@ -4841,7 +4954,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.2 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-plugin-utils': 7.18.9 dev: true /@babel/plugin-transform-unicode-regex/7.16.7_@babel+core@7.18.2: @@ -4852,7 +4965,7 @@ packages: dependencies: '@babel/core': 7.18.2 '@babel/helper-create-regexp-features-plugin': 7.17.12_@babel+core@7.18.2 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-plugin-utils': 7.18.9 dev: true /@babel/preset-env/7.18.2_@babel+core@7.18.2: @@ -4864,7 +4977,7 @@ packages: '@babel/compat-data': 7.17.10 '@babel/core': 7.18.2 '@babel/helper-compilation-targets': 7.18.2_@babel+core@7.18.2 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-plugin-utils': 7.18.9 '@babel/helper-validator-option': 7.16.7 '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.17.12_@babel+core@7.18.2 '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.17.12_@babel+core@7.18.2 @@ -4931,9 +5044,9 @@ packages: '@babel/plugin-transform-unicode-escapes': 7.16.7_@babel+core@7.18.2 '@babel/plugin-transform-unicode-regex': 7.16.7_@babel+core@7.18.2 '@babel/preset-modules': 0.1.5_@babel+core@7.18.2 - '@babel/types': 7.18.4 - babel-plugin-polyfill-corejs2: 0.3.1_@babel+core@7.18.2 - babel-plugin-polyfill-corejs3: 0.5.2_@babel+core@7.18.2 + '@babel/types': 7.18.13 + babel-plugin-polyfill-corejs2: 0.3.2_@babel+core@7.18.2 + babel-plugin-polyfill-corejs3: 0.5.3_@babel+core@7.18.2 babel-plugin-polyfill-regenerator: 0.3.1_@babel+core@7.18.2 core-js-compat: 3.22.8 semver: 6.3.0 @@ -4947,10 +5060,10 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.2 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-plugin-utils': 7.18.9 '@babel/plugin-proposal-unicode-property-regex': 7.17.12_@babel+core@7.18.2 '@babel/plugin-transform-dotall-regex': 7.16.7_@babel+core@7.18.2 - '@babel/types': 7.18.4 + '@babel/types': 7.18.13 esutils: 2.0.3 dev: true @@ -4961,7 +5074,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.2 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-plugin-utils': 7.18.9 '@babel/helper-validator-option': 7.16.7 '@babel/plugin-transform-react-display-name': 7.16.7_@babel+core@7.18.2 '@babel/plugin-transform-react-jsx': 7.17.12_@babel+core@7.18.2 @@ -4976,7 +5089,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.2 - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-plugin-utils': 7.18.9 '@babel/helper-validator-option': 7.16.7 '@babel/plugin-transform-typescript': 7.18.4_@babel+core@7.18.2 transitivePeerDependencies: @@ -4991,8 +5104,8 @@ packages: regenerator-runtime: 0.13.9 dev: true - /@babel/runtime/7.18.3: - resolution: {integrity: sha512-38Y8f7YUhce/K7RMwTp7m0uCumpv9hZkitCbBClqQIow1qSbCvGkcegKOXpEWCQLfWmevgRiWokZ1GkpfhbZug==} + /@babel/runtime/7.18.9: + resolution: {integrity: sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw==} engines: {node: '>=6.9.0'} dependencies: regenerator-runtime: 0.13.9 @@ -5006,6 +5119,33 @@ packages: '@babel/types': 7.18.4 dev: true + /@babel/template/7.18.10: + resolution: {integrity: sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.18.6 + '@babel/parser': 7.18.13 + '@babel/types': 7.18.13 + dev: true + + /@babel/traverse/7.18.13: + resolution: {integrity: sha512-N6kt9X1jRMLPxxxPYWi7tgvJRH/rtoU+dbKAPDM44RFHiMH8igdsaSBgFeskhSl/kLWLDUvIh1RXCrTmg0/zvA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.18.6 + '@babel/generator': 7.18.13 + '@babel/helper-environment-visitor': 7.18.9 + '@babel/helper-function-name': 7.18.9 + '@babel/helper-hoist-variables': 7.18.6 + '@babel/helper-split-export-declaration': 7.18.6 + '@babel/parser': 7.18.13 + '@babel/types': 7.18.13 + debug: 4.3.4 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + dev: true + /@babel/traverse/7.18.2: resolution: {integrity: sha512-9eNwoeovJ6KH9zcCNnENY7DMFwTU9JdGCFtqNLfUAqtUHRCOsTOqWoffosP8vKmNYeSBUv3yVJXjfd8ucwOjUA==} engines: {node: '>=6.9.0'} @@ -5024,6 +5164,15 @@ packages: - supports-color dev: true + /@babel/types/7.18.13: + resolution: {integrity: sha512-ePqfTihzW0W6XAU+aMw2ykilisStJfDnsejDCXRchCcMJ4O0+8DhPXf2YUbZ6wjBlsEmZwLK/sPweWtu8hcJYQ==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-string-parser': 7.18.10 + '@babel/helper-validator-identifier': 7.18.6 + to-fast-properties: 2.0.0 + dev: true + /@babel/types/7.18.4: resolution: {integrity: sha512-ThN1mBcMq5pG/Vm2IcBmPPfyPXbd8S02rS+OBIDENdufvqC7Z/jHPCv9IcP01277aKtDI8g/2XysBN4hA8niiw==} engines: {node: '>=6.9.0'} @@ -5732,7 +5881,16 @@ packages: dependencies: '@jridgewell/set-array': 1.1.1 '@jridgewell/sourcemap-codec': 1.4.13 - '@jridgewell/trace-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.15 + dev: true + + /@jridgewell/gen-mapping/0.3.2: + resolution: {integrity: sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==} + engines: {node: '>=6.0.0'} + dependencies: + '@jridgewell/set-array': 1.1.1 + '@jridgewell/sourcemap-codec': 1.4.13 + '@jridgewell/trace-mapping': 0.3.15 dev: true /@jridgewell/resolve-uri/3.0.7: @@ -5749,8 +5907,8 @@ packages: resolution: {integrity: sha512-GryiOJmNcWbovBxTfZSF71V/mXbgcV3MewDe3kIMCLyIh5e7SKAeUZs+rMnJ8jkMolZ/4/VsdBmMrw3l+VdZ3w==} dev: true - /@jridgewell/trace-mapping/0.3.13: - resolution: {integrity: sha512-o1xbKhp9qnIAoHJSWd6KlCZfqslL4valSF81H8ImioOAxluWYWOpWkpyktY2vnt4tbrX9XYaxovq6cgowaJp2w==} + /@jridgewell/trace-mapping/0.3.15: + resolution: {integrity: sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g==} dependencies: '@jridgewell/resolve-uri': 3.0.7 '@jridgewell/sourcemap-codec': 1.4.13 @@ -5767,30 +5925,30 @@ packages: resolution: {integrity: sha512-zMM9Ds+SawiUkakS7y94Ymqx+S0ORzpG3frZirN3l+UlXUmSUR7hF4wxCVqW+ei94JzV5kt0uXBcoOEAuiydrw==} dev: true - /@microsoft/api-extractor-model/7.17.3: - resolution: {integrity: sha512-ETslFxVEZTEK6mrOARxM34Ll2W/5H2aTk9Pe9dxsMCnthE8O/CaStV4WZAGsvvZKyjelSWgPVYGowxGVnwOMlQ==} + /@microsoft/api-extractor-model/7.23.3: + resolution: {integrity: sha512-HpsWzG6jrWHrTlIg53kmp/IVQPBHUZc+8dunnr9VXrmDjVBehaXxp9A6jhTQ/bd7W1m5TYfAvwCmseC1+9FCuA==} dependencies: '@microsoft/tsdoc': 0.14.1 '@microsoft/tsdoc-config': 0.16.1 - '@rushstack/node-core-library': 3.45.5 + '@rushstack/node-core-library': 3.51.1 dev: true - /@microsoft/api-extractor/7.24.2: - resolution: {integrity: sha512-QWZh9aQZvBAdRVK+Go8NiW8YNMN//OGiNqgA3iZ2sEy8imUqkRBCybXgmw2HkEYyPnn55CFoMKvnAHvV9+4B/A==} + /@microsoft/api-extractor/7.29.5: + resolution: {integrity: sha512-+vqO/TAGw9xXANpvTjA4y5ADcaRuYuBoJ9IfoAHubrGuxKG6GoW3P2tfdgwteLz95CnlftBxYp+3NG/mf05P9Q==} hasBin: true dependencies: - '@microsoft/api-extractor-model': 7.17.3 + '@microsoft/api-extractor-model': 7.23.3 '@microsoft/tsdoc': 0.14.1 '@microsoft/tsdoc-config': 0.16.1 - '@rushstack/node-core-library': 3.45.5 - '@rushstack/rig-package': 0.3.11 - '@rushstack/ts-command-line': 4.11.0 + '@rushstack/node-core-library': 3.51.1 + '@rushstack/rig-package': 0.3.14 + '@rushstack/ts-command-line': 4.12.2 colors: 1.2.5 lodash: 4.17.21 resolve: 1.17.0 semver: 7.3.7 source-map: 0.6.1 - typescript: 4.6.4 + typescript: 4.7.4 dev: true /@microsoft/rush-lib/5.71.0: @@ -6100,20 +6258,20 @@ packages: resolution: {integrity: sha512-Ba7HmkFgfQxZqqaeIWWkNK0rEhpxVQHIoVyW1YDSkGsGIXzcaW4deC8B0pZrNSSyLTdIk7y+5olKt5+g0GmFIQ==} dev: true - /@rushstack/eslint-config/2.6.0_eslint@8.16.0+typescript@4.7.2: - resolution: {integrity: sha512-HXK6HGeHgnDMlWyeeH6IAwUZyOAMargVw3JMXpyhGJEPq4L7zO02/WvUGsFnwoOTzMNktNlo3YNrOmZnasuEGg==} + /@rushstack/eslint-config/3.0.0_eslint@8.16.0+typescript@4.7.2: + resolution: {integrity: sha512-9vkSsu7QnoCyujiJMB1uegfGF9LXe5Q+s08836H0PzHQ3lptsw4iqy1EQuuEPasjws6WR8b5oTT8SsrYVp9dcQ==} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - typescript: '>=3.0.0' - dependencies: - '@rushstack/eslint-patch': 1.1.3 - '@rushstack/eslint-plugin': 0.9.0_eslint@8.16.0+typescript@4.7.2 - '@rushstack/eslint-plugin-packlets': 0.4.0_eslint@8.16.0+typescript@4.7.2 - '@rushstack/eslint-plugin-security': 0.3.0_eslint@8.16.0+typescript@4.7.2 - '@typescript-eslint/eslint-plugin': 5.20.0_dce2f58b47902bf309a7c2e5b23210cc - '@typescript-eslint/experimental-utils': 5.20.0_eslint@8.16.0+typescript@4.7.2 - '@typescript-eslint/parser': 5.20.0_eslint@8.16.0+typescript@4.7.2 - '@typescript-eslint/typescript-estree': 5.20.0_typescript@4.7.2 + typescript: '>=4.7.0' + dependencies: + '@rushstack/eslint-patch': 1.1.4 + '@rushstack/eslint-plugin': 0.10.0_eslint@8.16.0+typescript@4.7.2 + '@rushstack/eslint-plugin-packlets': 0.5.0_eslint@8.16.0+typescript@4.7.2 + '@rushstack/eslint-plugin-security': 0.4.0_eslint@8.16.0+typescript@4.7.2 + '@typescript-eslint/eslint-plugin': 5.30.7_6e54d71f4d634e52cdff80d4e418c837 + '@typescript-eslint/experimental-utils': 5.30.7_eslint@8.16.0+typescript@4.7.2 + '@typescript-eslint/parser': 5.30.7_eslint@8.16.0+typescript@4.7.2 + '@typescript-eslint/typescript-estree': 5.30.7_typescript@4.7.2 eslint: 8.16.0 eslint-plugin-promise: 6.0.0_eslint@8.16.0 eslint-plugin-react: 7.27.1_eslint@8.16.0 @@ -6126,54 +6284,45 @@ packages: resolution: {integrity: sha512-ZK5v4bJwgXldAUA8r3q9YKfCwOqoHTK/ZqRjSeRXQrBXWouoPnS4MQtgC4AXGiiBuUu5wxrRgTlv0ktmM4P1Aw==} dev: true - /@rushstack/eslint-patch/1.1.3: - resolution: {integrity: sha512-WiBSI6JBIhC6LRIsB2Kwh8DsGTlbBU+mLRxJmAe3LjHTdkDpwIbEOZgoXBbZilk/vlfjK8i6nKRAvIRn1XaIMw==} + /@rushstack/eslint-patch/1.1.4: + resolution: {integrity: sha512-LwzQKA4vzIct1zNZzBmRKI9QuNpLgTQMEjsQLf3BXuGYb3QPTP4Yjf6mkdX+X1mYttZ808QpOwAzZjv28kq7DA==} - /@rushstack/eslint-plugin-packlets/0.4.0_eslint@8.16.0+typescript@4.7.2: - resolution: {integrity: sha512-oEUb5XPYNljpQXd+4ikaJ78agURaVAt1JrrGYdn4atMOdLUoyx5t9Om26IQ88gMkHSwHAyRyzzGcL282BwS2Nw==} + /@rushstack/eslint-plugin-packlets/0.5.0_eslint@8.16.0+typescript@4.7.2: + resolution: {integrity: sha512-I160nHeAGzA0q4g3cR7kiHNgiU1HqrYto52+lEmxLAdbBllqc6IOyiWQfCDb5ug0f+Y8bTwMQHiUrI7XclZB/Q==} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 dependencies: - '@rushstack/tree-pattern': 0.2.3 - '@typescript-eslint/experimental-utils': 5.20.0_eslint@8.16.0+typescript@4.7.2 + '@rushstack/tree-pattern': 0.2.4 + '@typescript-eslint/experimental-utils': 5.30.7_eslint@8.16.0+typescript@4.7.2 eslint: 8.16.0 transitivePeerDependencies: - supports-color - typescript - /@rushstack/eslint-plugin-security/0.3.0_eslint@8.16.0+typescript@4.7.2: - resolution: {integrity: sha512-l5BgfHsrZtU6IjC5wAQhNP/7NkCaTohiO3cwDztqgXnLDJUa/hBRD180tInRdOwRfZrd4pvRZ29MCohOh5wf3g==} + /@rushstack/eslint-plugin-security/0.4.0_eslint@8.16.0+typescript@4.7.2: + resolution: {integrity: sha512-jRFtrOnZZcuJ2MRA9RtoeyKiFQ60iKu7SDF1wkc7M9nHL5C1HkFApX6nTlAjY7C5B7UlV+9BP9fDmOJJmB4FSw==} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 dependencies: - '@rushstack/tree-pattern': 0.2.3 - '@typescript-eslint/experimental-utils': 5.20.0_eslint@8.16.0+typescript@4.7.2 + '@rushstack/tree-pattern': 0.2.4 + '@typescript-eslint/experimental-utils': 5.30.7_eslint@8.16.0+typescript@4.7.2 eslint: 8.16.0 transitivePeerDependencies: - supports-color - typescript - /@rushstack/eslint-plugin/0.9.0_eslint@8.16.0+typescript@4.7.2: - resolution: {integrity: sha512-epWOeNYjBRIYEOMcZ61BZx6ma1eHbp1HiwZD62204pwv5IYzh5OxfzgfGbp2RMlWaZBOh0+pRVJNaU4tGE1p+A==} + /@rushstack/eslint-plugin/0.10.0_eslint@8.16.0+typescript@4.7.2: + resolution: {integrity: sha512-39DCBD6s7Y5XQxvcMmitXfupkReGcg0lmtil9mrGHkDoyiUln90sOWtpkSl6LqUrSL3lx7N2wRvJiJlwGIPYFQ==} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 dependencies: - '@rushstack/tree-pattern': 0.2.3 - '@typescript-eslint/experimental-utils': 5.20.0_eslint@8.16.0+typescript@4.7.2 + '@rushstack/tree-pattern': 0.2.4 + '@typescript-eslint/experimental-utils': 5.30.7_eslint@8.16.0+typescript@4.7.2 eslint: 8.16.0 transitivePeerDependencies: - supports-color - typescript - /@rushstack/heft-config-file/0.8.3: - resolution: {integrity: sha512-RXAcsA+oboUE/IZk5ONHKhn7XibRXljWql5B7l1ddFzbwDUnbddPz8IEMDCKG1v/Uma/7WSqpBR210RoC5uszg==} - engines: {node: '>=10.13.0'} - dependencies: - '@rushstack/node-core-library': 3.45.4 - '@rushstack/rig-package': 0.3.11 - jsonpath-plus: 4.0.0 - dev: true - /@rushstack/heft-config-file/0.8.4: resolution: {integrity: sha512-OFvezlWYFQlKSXXIIjuGlBwSIKIl7WXYQ48diK/J5WJWpdVaq/SLVzB3coAxNZPA/a7u+dbs1DcLORBa2e133Q==} engines: {node: '>=10.13.0'} @@ -6181,66 +6330,28 @@ packages: '@rushstack/node-core-library': 3.45.5 '@rushstack/rig-package': 0.3.11 jsonpath-plus: 4.0.0 + dev: false - /@rushstack/heft-jest-plugin/0.2.15_04f3c65f6de4a5e5529a6fededa44e1d: - resolution: {integrity: sha512-AHWGGIzByXb0wonywmUTJ8IZrMQOxM45rZ0v9hbXogD8PbtA7WhMZ7RyoiAHL1vrRbElAcnLC6xT1Erzh/09Pg==} - peerDependencies: - '@rushstack/heft': ^0.45.0 - dependencies: - '@jest/core': 27.4.7_ts-node@10.8.0 - '@jest/reporters': 27.4.6 - '@jest/transform': 27.4.6 - '@rushstack/heft': 0.45.5 - '@rushstack/heft-config-file': 0.8.3 - '@rushstack/node-core-library': 3.45.4 - jest-config: 27.4.7_ts-node@10.8.0 - jest-resolve: 27.4.6 - jest-snapshot: 27.4.6 - lodash: 4.17.21 - transitivePeerDependencies: - - bufferutil - - canvas - - node-notifier - - supports-color - - ts-node - - utf-8-validate - dev: true - - /@rushstack/heft-jest-plugin/0.2.15_@rushstack+heft@0.45.5: - resolution: {integrity: sha512-AHWGGIzByXb0wonywmUTJ8IZrMQOxM45rZ0v9hbXogD8PbtA7WhMZ7RyoiAHL1vrRbElAcnLC6xT1Erzh/09Pg==} - peerDependencies: - '@rushstack/heft': ^0.45.0 + /@rushstack/heft-config-file/0.9.5: + resolution: {integrity: sha512-vD24lHBK2t2dsgYtV/XtYEzpKP0kB9+Vzh/psHIcPxTEnKvl7cxVWvcBfQ1lgzg5D0teYjicyemN/yLTAAEHqw==} + engines: {node: '>=10.13.0'} dependencies: - '@jest/core': 27.4.7 - '@jest/reporters': 27.4.6 - '@jest/transform': 27.4.6 - '@rushstack/heft': 0.45.5 - '@rushstack/heft-config-file': 0.8.3 - '@rushstack/node-core-library': 3.45.4 - jest-config: 27.4.7 - jest-resolve: 27.4.6 - jest-snapshot: 27.4.6 - lodash: 4.17.21 - transitivePeerDependencies: - - bufferutil - - canvas - - node-notifier - - supports-color - - ts-node - - utf-8-validate + '@rushstack/node-core-library': 3.51.1 + '@rushstack/rig-package': 0.3.14 + jsonpath-plus: 4.0.0 dev: true - /@rushstack/heft-jest-plugin/0.3.5_04f3c65f6de4a5e5529a6fededa44e1d: - resolution: {integrity: sha512-n64rwKqZ3TeZEV/VV7GNI6vZ37DXCaE0uICJIo2jRAJ4kAi1RixbCftVou/lm7Ndr6pX08Zvtvcp2nseRM7BQg==} + /@rushstack/heft-jest-plugin/0.3.28_3d8d10679dc80a68f97d705c60d2bfb3: + resolution: {integrity: sha512-FWb76XtvAvKZHd9lTGRzzboKPfRNE78ds7/+iaw5YcfMD6BNVsk3HOEnT7+B2ynIACE4MlolSzJvEqVY7YwIoA==} peerDependencies: - '@rushstack/heft': ^0.45.5 + '@rushstack/heft': ^0.47.5 dependencies: '@jest/core': 27.4.7_ts-node@10.8.0 '@jest/reporters': 27.4.6 '@jest/transform': 27.4.6 - '@rushstack/heft': 0.45.5 - '@rushstack/heft-config-file': 0.8.4 - '@rushstack/node-core-library': 3.45.5 + '@rushstack/heft': 0.47.5 + '@rushstack/heft-config-file': 0.9.5 + '@rushstack/node-core-library': 3.51.1 jest-config: 27.4.7_ts-node@10.8.0 jest-resolve: 27.4.6 jest-snapshot: 27.4.6 @@ -6254,17 +6365,17 @@ packages: - utf-8-validate dev: true - /@rushstack/heft-jest-plugin/0.3.5_@rushstack+heft@0.45.5: - resolution: {integrity: sha512-n64rwKqZ3TeZEV/VV7GNI6vZ37DXCaE0uICJIo2jRAJ4kAi1RixbCftVou/lm7Ndr6pX08Zvtvcp2nseRM7BQg==} + /@rushstack/heft-jest-plugin/0.3.28_@rushstack+heft@0.47.5: + resolution: {integrity: sha512-FWb76XtvAvKZHd9lTGRzzboKPfRNE78ds7/+iaw5YcfMD6BNVsk3HOEnT7+B2ynIACE4MlolSzJvEqVY7YwIoA==} peerDependencies: - '@rushstack/heft': ^0.45.5 + '@rushstack/heft': ^0.47.5 dependencies: '@jest/core': 27.4.7 '@jest/reporters': 27.4.6 '@jest/transform': 27.4.6 - '@rushstack/heft': 0.45.5 - '@rushstack/heft-config-file': 0.8.4 - '@rushstack/node-core-library': 3.45.5 + '@rushstack/heft': 0.47.5 + '@rushstack/heft-config-file': 0.9.5 + '@rushstack/node-core-library': 3.51.1 jest-config: 27.4.7 jest-resolve: 27.4.6 jest-snapshot: 27.4.6 @@ -6278,17 +6389,17 @@ packages: - utf-8-validate dev: true - /@rushstack/heft-node-rig/1.9.6_04f3c65f6de4a5e5529a6fededa44e1d: - resolution: {integrity: sha512-lR66pnjaXbuhXUxJQ5uo18XLJjYyzKodD0jSIuGWW2AYLc1ZRgd0uypiFu746N1VH6xMS3friK6I2FjZqp2ymg==} + /@rushstack/heft-node-rig/1.10.5_3d8d10679dc80a68f97d705c60d2bfb3: + resolution: {integrity: sha512-09mG9AVaSYK8y2qyfjkbVoPEm10mR3JG/RIkMV7Hxx6iqHwbLY0fPFMjVIycEiCaxwtBnOn4C4sLjsweAk845w==} peerDependencies: - '@rushstack/heft': ^0.45.5 + '@rushstack/heft': ^0.47.5 dependencies: - '@microsoft/api-extractor': 7.24.2 - '@rushstack/heft': 0.45.5 - '@rushstack/heft-jest-plugin': 0.3.5_04f3c65f6de4a5e5529a6fededa44e1d + '@microsoft/api-extractor': 7.29.5 + '@rushstack/heft': 0.47.5 + '@rushstack/heft-jest-plugin': 0.3.28_3d8d10679dc80a68f97d705c60d2bfb3 eslint: 8.7.0 jest-environment-node: 27.4.6 - typescript: 4.6.4 + typescript: 4.7.4 transitivePeerDependencies: - bufferutil - canvas @@ -6298,17 +6409,17 @@ packages: - utf-8-validate dev: true - /@rushstack/heft-node-rig/1.9.6_@rushstack+heft@0.45.5: - resolution: {integrity: sha512-lR66pnjaXbuhXUxJQ5uo18XLJjYyzKodD0jSIuGWW2AYLc1ZRgd0uypiFu746N1VH6xMS3friK6I2FjZqp2ymg==} + /@rushstack/heft-node-rig/1.10.5_@rushstack+heft@0.47.5: + resolution: {integrity: sha512-09mG9AVaSYK8y2qyfjkbVoPEm10mR3JG/RIkMV7Hxx6iqHwbLY0fPFMjVIycEiCaxwtBnOn4C4sLjsweAk845w==} peerDependencies: - '@rushstack/heft': ^0.45.5 + '@rushstack/heft': ^0.47.5 dependencies: - '@microsoft/api-extractor': 7.24.2 - '@rushstack/heft': 0.45.5 - '@rushstack/heft-jest-plugin': 0.3.5_@rushstack+heft@0.45.5 + '@microsoft/api-extractor': 7.29.5 + '@rushstack/heft': 0.47.5 + '@rushstack/heft-jest-plugin': 0.3.28_@rushstack+heft@0.47.5 eslint: 8.7.0 jest-environment-node: 27.4.6 - typescript: 4.6.4 + typescript: 4.7.4 transitivePeerDependencies: - bufferutil - canvas @@ -6318,15 +6429,15 @@ packages: - utf-8-validate dev: true - /@rushstack/heft/0.45.5: - resolution: {integrity: sha512-tFj2q6coj3V3NOBa1VhoER/7n0pSercJ2SLeSysrA4dTUCgZJCRoL0uex7kvAdA4jo9RBaEEDF/WvtTHzx1Q3w==} + /@rushstack/heft/0.47.5: + resolution: {integrity: sha512-bQeHlP8tctN4RO0In9ULMEXsBho1vS8FckCtooYOCDiAk9eFp8jTRFLOF1YcderZfQa2AI1gsnv9NXTHOIxavg==} engines: {node: '>=10.13.0'} hasBin: true dependencies: - '@rushstack/heft-config-file': 0.8.4 - '@rushstack/node-core-library': 3.45.5 - '@rushstack/rig-package': 0.3.11 - '@rushstack/ts-command-line': 4.11.0 + '@rushstack/heft-config-file': 0.9.5 + '@rushstack/node-core-library': 3.51.1 + '@rushstack/rig-package': 0.3.14 + '@rushstack/ts-command-line': 4.12.2 '@types/tapable': 1.0.6 argparse: 1.0.10 chokidar: 3.4.3 @@ -6339,8 +6450,8 @@ packages: true-case-path: 2.2.1 dev: true - /@rushstack/node-core-library/3.45.4: - resolution: {integrity: sha512-FMoEQWjK7nWAO2uFgV1eVpVhY9ZDGOdIIomi9zTej64cKJ+8/Nvu+ny0xKaUDEjw/ALftN2D2ml7L0RDpW/Z9g==} + /@rushstack/node-core-library/3.45.5: + resolution: {integrity: sha512-KbN7Hp9vH3bD3YJfv6RnVtzzTAwGYIBl7y2HQLY4WEQqRbvE3LgI78W9l9X+cTAXCX//p0EeoiUYNTFdqJrMZg==} dependencies: '@types/node': 12.20.24 colors: 1.2.5 @@ -6351,10 +6462,10 @@ packages: semver: 7.3.7 timsort: 0.3.0 z-schema: 5.0.3 - dev: true + dev: false - /@rushstack/node-core-library/3.45.5: - resolution: {integrity: sha512-KbN7Hp9vH3bD3YJfv6RnVtzzTAwGYIBl7y2HQLY4WEQqRbvE3LgI78W9l9X+cTAXCX//p0EeoiUYNTFdqJrMZg==} + /@rushstack/node-core-library/3.51.1: + resolution: {integrity: sha512-xLoUztvGpaT5CphDexDPt2WbBx8D68VS5tYOkwfr98p90y0f/wepgXlTA/q5MUeZGGucASiXKp5ysdD+GPYf9A==} dependencies: '@types/node': 12.20.24 colors: 1.2.5 @@ -6363,8 +6474,8 @@ packages: jju: 1.4.0 resolve: 1.17.0 semver: 7.3.7 - timsort: 0.3.0 z-schema: 5.0.3 + dev: true /@rushstack/package-deps-hash/3.2.20: resolution: {integrity: sha512-pZzMeIDXm7L8L1P3pvKSJO8XELgAZxjPyiZpRS0LF1zAA7WhAz5CKnXzlr/kShudMtsavtpliib7gtMzWRqsTg==} @@ -6377,6 +6488,14 @@ packages: dependencies: resolve: 1.17.0 strip-json-comments: 3.1.1 + dev: false + + /@rushstack/rig-package/0.3.14: + resolution: {integrity: sha512-Ic9EN3kWJCK6iOxEDtwED9nrM146zCDrQaUxbeGOF+q/VLZ/HNHPw+aLqrqmTl0ZT66Sf75Qk6OG+rySjTorvQ==} + dependencies: + resolve: 1.17.0 + strip-json-comments: 3.1.1 + dev: true /@rushstack/rush-amazon-s3-build-cache-plugin/5.71.0: resolution: {integrity: sha512-O87pP33WqPPykzLt+Bjj3H0v+LJawojkUJRU5zBVXrA8cUpQZ7Iv4Ntzwq5Xq51sPASxG+6kOYLmcH1ffCNmJw==} @@ -6425,8 +6544,8 @@ packages: wordwrap: 1.0.0 dev: false - /@rushstack/tree-pattern/0.2.3: - resolution: {integrity: sha512-8KWZxzn6XKuy3iKRSAd2CHXSXneRlGCmH9h/qM7jYQDekp+U18oUzub5xqOqHS2PLUC+torOMYZxgAIO/fF86A==} + /@rushstack/tree-pattern/0.2.4: + resolution: {integrity: sha512-H8i0OinWsdKM1TKEKPeRRTw85e+/7AIFpxm7q1blceZJhuxRBjCGAUZvQXZK4CMLx75xPqh/h1t5WHwFmElAPA==} /@rushstack/ts-command-line/4.11.0: resolution: {integrity: sha512-ptG9L0mjvJ5QtK11GsAFY+jGfsnqHDS6CY6Yw1xT7a9bhjfNYnf6UPwjV+pF6UgiucfNcMDNW9lkDLxvZKKxMg==} @@ -6435,6 +6554,16 @@ packages: argparse: 1.0.10 colors: 1.2.5 string-argv: 0.3.1 + dev: false + + /@rushstack/ts-command-line/4.12.2: + resolution: {integrity: sha512-poBtnumLuWmwmhCEkVAgynWgtnF9Kygekxyp4qtQUSbBrkuyPQTL85c8Cva1YfoUpOdOXxezMAkUt0n5SNKGqw==} + dependencies: + '@types/argparse': 1.0.38 + argparse: 1.0.10 + colors: 1.2.5 + string-argv: 0.3.1 + dev: true /@sinonjs/commons/1.8.3: resolution: {integrity: sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==} @@ -6472,7 +6601,7 @@ packages: engines: {node: '>=12'} dependencies: '@babel/code-frame': 7.16.7 - '@babel/runtime': 7.18.3 + '@babel/runtime': 7.18.9 '@types/aria-query': 4.2.2 aria-query: 5.0.0 chalk: 4.1.2 @@ -6485,7 +6614,7 @@ packages: resolution: {integrity: sha512-ajUJdfDIuTCadB79ukO+0l8O+QwN0LiSxDaYUTI4LndbbUsGi6rWU1SCexXzBA2NSjlVB9/vbkasQIL3tmPBjw==} engines: {node: '>=8', npm: '>=6', yarn: '>=1'} dependencies: - '@babel/runtime': 7.18.3 + '@babel/runtime': 7.18.9 '@types/testing-library__jest-dom': 5.14.3 aria-query: 5.0.0 chalk: 3.0.0 @@ -6503,7 +6632,7 @@ packages: react: '*' react-dom: '*' dependencies: - '@babel/runtime': 7.18.3 + '@babel/runtime': 7.18.9 '@testing-library/dom': 8.13.0 react: 17.0.2 react-dom: 17.0.2_react@17.0.2 @@ -6515,7 +6644,7 @@ packages: peerDependencies: '@testing-library/dom': '>=7.21.4' dependencies: - '@babel/runtime': 7.18.3 + '@babel/runtime': 7.18.9 '@testing-library/dom': 8.13.0 dev: true @@ -6554,8 +6683,8 @@ packages: /@types/babel__core/7.1.19: resolution: {integrity: sha512-WEOTgRsbYkvA/KCsDwVEGkd7WAr1e3g31VHQ8zy5gul/V1qKullU/BU5I68X5v7V3GnB9eotmom4v5a5gjxorw==} dependencies: - '@babel/parser': 7.18.4 - '@babel/types': 7.18.4 + '@babel/parser': 7.18.13 + '@babel/types': 7.18.13 '@types/babel__generator': 7.6.4 '@types/babel__template': 7.4.1 '@types/babel__traverse': 7.17.1 @@ -6564,20 +6693,20 @@ packages: /@types/babel__generator/7.6.4: resolution: {integrity: sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==} dependencies: - '@babel/types': 7.18.4 + '@babel/types': 7.18.13 dev: true /@types/babel__template/7.4.1: resolution: {integrity: sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==} dependencies: - '@babel/parser': 7.18.4 - '@babel/types': 7.18.4 + '@babel/parser': 7.18.13 + '@babel/types': 7.18.13 dev: true /@types/babel__traverse/7.17.1: resolution: {integrity: sha512-kVzjari1s2YVi77D3w1yuvohV2idweYXMCDzqBiVNN63TcDWrIlTVOYpqVrvbbyOE/IyzBoTKF0fdnLPEORFxA==} dependencies: - '@babel/types': 7.18.4 + '@babel/types': 7.18.13 dev: true /@types/body-parser/1.19.2: @@ -6832,8 +6961,8 @@ packages: dev: true optional: true - /@typescript-eslint/eslint-plugin/5.20.0_dce2f58b47902bf309a7c2e5b23210cc: - resolution: {integrity: sha512-fapGzoxilCn3sBtC6NtXZX6+P/Hef7VDbyfGqTTpzYydwhlkevB+0vE0EnmHPVTVSy68GUncyJ/2PcrFBeCo5Q==} + /@typescript-eslint/eslint-plugin/5.30.7_6e54d71f4d634e52cdff80d4e418c837: + resolution: {integrity: sha512-l4L6Do+tfeM2OK0GJsU7TUcM/1oN/N25xHm3Jb4z3OiDU4Lj8dIuxX9LpVMS9riSXQs42D1ieX7b85/r16H9Fw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: '@typescript-eslint/parser': ^5.0.0 @@ -6843,10 +6972,10 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/parser': 5.20.0_eslint@8.16.0+typescript@4.7.2 - '@typescript-eslint/scope-manager': 5.20.0 - '@typescript-eslint/type-utils': 5.20.0_eslint@8.16.0+typescript@4.7.2 - '@typescript-eslint/utils': 5.20.0_eslint@8.16.0+typescript@4.7.2 + '@typescript-eslint/parser': 5.30.7_eslint@8.16.0+typescript@4.7.2 + '@typescript-eslint/scope-manager': 5.30.7 + '@typescript-eslint/type-utils': 5.30.7_eslint@8.16.0+typescript@4.7.2 + '@typescript-eslint/utils': 5.30.7_eslint@8.16.0+typescript@4.7.2 debug: 4.3.4 eslint: 8.16.0 functional-red-black-tree: 1.0.1 @@ -6858,30 +6987,30 @@ packages: transitivePeerDependencies: - supports-color - /@typescript-eslint/experimental-utils/5.20.0_eslint@8.16.0+typescript@4.7.2: - resolution: {integrity: sha512-w5qtx2Wr9x13Dp/3ic9iGOGmVXK5gMwyc8rwVgZU46K9WTjPZSyPvdER9Ycy+B5lNHvoz+z2muWhUvlTpQeu+g==} + /@typescript-eslint/experimental-utils/5.27.0_eslint@8.16.0+typescript@4.7.2: + resolution: {integrity: sha512-ZOn342bYh19IYvkiorrqnzNoRAr91h3GiFSSfa4tlHV+R9GgR8SxCwAi8PKMyT8+pfwMxfQdNbwKsMurbF9hzg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 dependencies: - '@typescript-eslint/utils': 5.20.0_eslint@8.16.0+typescript@4.7.2 + '@typescript-eslint/utils': 5.27.0_eslint@8.16.0+typescript@4.7.2 eslint: 8.16.0 transitivePeerDependencies: - supports-color - typescript + dev: true - /@typescript-eslint/experimental-utils/5.27.0_eslint@8.16.0+typescript@4.7.2: - resolution: {integrity: sha512-ZOn342bYh19IYvkiorrqnzNoRAr91h3GiFSSfa4tlHV+R9GgR8SxCwAi8PKMyT8+pfwMxfQdNbwKsMurbF9hzg==} + /@typescript-eslint/experimental-utils/5.30.7_eslint@8.16.0+typescript@4.7.2: + resolution: {integrity: sha512-r218ZVL0zFBYzEq8/9K2ZhRgsmKUhm8xd3sWChgvTbmP98kHGuY83IUl64SS9fx9OSBM9vMLdzBfox4eDdm/ZQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 dependencies: - '@typescript-eslint/utils': 5.27.0_eslint@8.16.0+typescript@4.7.2 + '@typescript-eslint/utils': 5.30.7_eslint@8.16.0+typescript@4.7.2 eslint: 8.16.0 transitivePeerDependencies: - supports-color - typescript - dev: true /@typescript-eslint/parser/5.10.1_eslint@8.16.0+typescript@4.7.2: resolution: {integrity: sha512-GReo3tjNBwR5RnRO0K2wDIDN31cM3MmDtgyQ85oAxAmC5K3j/g85IjP+cDfcqDsDDBf1HNKQAD0WqOYL8jXqUA==} @@ -6903,8 +7032,8 @@ packages: - supports-color dev: true - /@typescript-eslint/parser/5.20.0_eslint@8.16.0+typescript@4.7.2: - resolution: {integrity: sha512-UWKibrCZQCYvobmu3/N8TWbEeo/EPQbS41Ux1F9XqPzGuV7pfg6n50ZrFo6hryynD8qOTTfLHtHjjdQtxJ0h/w==} + /@typescript-eslint/parser/5.30.7_eslint@8.16.0+typescript@4.7.2: + resolution: {integrity: sha512-Rg5xwznHWWSy7v2o0cdho6n+xLhK2gntImp0rJroVVFkcYFYQ8C8UJTSuTw/3CnExBmPjycjmUJkxVmjXsld6A==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -6913,9 +7042,9 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/scope-manager': 5.20.0 - '@typescript-eslint/types': 5.20.0 - '@typescript-eslint/typescript-estree': 5.20.0_typescript@4.7.2 + '@typescript-eslint/scope-manager': 5.30.7 + '@typescript-eslint/types': 5.30.7 + '@typescript-eslint/typescript-estree': 5.30.7_typescript@4.7.2 debug: 4.3.4 eslint: 8.16.0 typescript: 4.7.2 @@ -6930,13 +7059,6 @@ packages: '@typescript-eslint/visitor-keys': 5.10.1 dev: true - /@typescript-eslint/scope-manager/5.20.0: - resolution: {integrity: sha512-h9KtuPZ4D/JuX7rpp1iKg3zOH0WNEa+ZIXwpW/KWmEFDxlA/HSfCMhiyF1HS/drTICjIbpA6OqkAhrP/zkCStg==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dependencies: - '@typescript-eslint/types': 5.20.0 - '@typescript-eslint/visitor-keys': 5.20.0 - /@typescript-eslint/scope-manager/5.27.0: resolution: {integrity: sha512-VnykheBQ/sHd1Vt0LJ1JLrMH1GzHO+SzX6VTXuStISIsvRiurue/eRkTqSrG0CexHQgKG8shyJfR4o5VYioB9g==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -6945,8 +7067,15 @@ packages: '@typescript-eslint/visitor-keys': 5.27.0 dev: true - /@typescript-eslint/type-utils/5.20.0_eslint@8.16.0+typescript@4.7.2: - resolution: {integrity: sha512-WxNrCwYB3N/m8ceyoGCgbLmuZwupvzN0rE8NBuwnl7APgjv24ZJIjkNzoFBXPRCGzLNkoU/WfanW0exvp/+3Iw==} + /@typescript-eslint/scope-manager/5.30.7: + resolution: {integrity: sha512-7BM1bwvdF1UUvt+b9smhqdc/eniOnCKxQT/kj3oXtj3LqnTWCAM0qHRHfyzCzhEfWX0zrW7KqXXeE4DlchZBKw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + '@typescript-eslint/types': 5.30.7 + '@typescript-eslint/visitor-keys': 5.30.7 + + /@typescript-eslint/type-utils/5.30.7_eslint@8.16.0+typescript@4.7.2: + resolution: {integrity: sha512-nD5qAE2aJX/YLyKMvOU5jvJyku4QN5XBVsoTynFrjQZaDgDV6i7QHFiYCx10wvn7hFvfuqIRNBtsgaLe0DbWhw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: '*' @@ -6955,7 +7084,7 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/utils': 5.20.0_eslint@8.16.0+typescript@4.7.2 + '@typescript-eslint/utils': 5.30.7_eslint@8.16.0+typescript@4.7.2 debug: 4.3.4 eslint: 8.16.0 tsutils: 3.21.0_typescript@4.7.2 @@ -6968,15 +7097,15 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - /@typescript-eslint/types/5.20.0: - resolution: {integrity: sha512-+d8wprF9GyvPwtoB4CxBAR/s0rpP25XKgnOvMf/gMXYDvlUC3rPFHupdTQ/ow9vn7UDe5rX02ovGYQbv/IUCbg==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - /@typescript-eslint/types/5.27.0: resolution: {integrity: sha512-lY6C7oGm9a/GWhmUDOs3xAVRz4ty/XKlQ2fOLr8GAIryGn0+UBOoJDWyHer3UgrHkenorwvBnphhP+zPmzmw0A==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true + /@typescript-eslint/types/5.30.7: + resolution: {integrity: sha512-ocVkETUs82+U+HowkovV6uxf1AnVRKCmDRNUBUUo46/5SQv1owC/EBFkiu4MOHeZqhKz2ktZ3kvJJ1uFqQ8QPg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + /@typescript-eslint/typescript-estree/5.10.1_typescript@4.7.2: resolution: {integrity: sha512-PwIGnH7jIueXv4opcwEbVGDATjGPO1dx9RkUl5LlHDSe+FXxPwFL5W/qYd5/NHr7f6lo/vvTrAzd0KlQtRusJQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -6998,8 +7127,8 @@ packages: - supports-color dev: true - /@typescript-eslint/typescript-estree/5.20.0_typescript@4.7.2: - resolution: {integrity: sha512-36xLjP/+bXusLMrT9fMMYy1KJAGgHhlER2TqpUVDYUQg4w0q/NW/sg4UGAgVwAqb8V4zYg43KMUpM8vV2lve6w==} + /@typescript-eslint/typescript-estree/5.27.0_typescript@4.7.2: + resolution: {integrity: sha512-QywPMFvgZ+MHSLRofLI7BDL+UczFFHyj0vF5ibeChDAJgdTV8k4xgEwF0geFhVlPc1p8r70eYewzpo6ps+9LJQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: typescript: '*' @@ -7007,8 +7136,8 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/types': 5.20.0 - '@typescript-eslint/visitor-keys': 5.20.0 + '@typescript-eslint/types': 5.27.0 + '@typescript-eslint/visitor-keys': 5.27.0 debug: 4.3.4 globby: 11.1.0 is-glob: 4.0.3 @@ -7017,9 +7146,10 @@ packages: typescript: 4.7.2 transitivePeerDependencies: - supports-color + dev: true - /@typescript-eslint/typescript-estree/5.27.0_typescript@4.7.2: - resolution: {integrity: sha512-QywPMFvgZ+MHSLRofLI7BDL+UczFFHyj0vF5ibeChDAJgdTV8k4xgEwF0geFhVlPc1p8r70eYewzpo6ps+9LJQ==} + /@typescript-eslint/typescript-estree/5.30.7_typescript@4.7.2: + resolution: {integrity: sha512-tNslqXI1ZdmXXrHER83TJ8OTYl4epUzJC0aj2i4DMDT4iU+UqLT3EJeGQvJ17BMbm31x5scSwo3hPM0nqQ1AEA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: typescript: '*' @@ -7027,8 +7157,8 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/types': 5.27.0 - '@typescript-eslint/visitor-keys': 5.27.0 + '@typescript-eslint/types': 5.30.7 + '@typescript-eslint/visitor-keys': 5.30.7 debug: 4.3.4 globby: 11.1.0 is-glob: 4.0.3 @@ -7037,42 +7167,41 @@ packages: typescript: 4.7.2 transitivePeerDependencies: - supports-color - dev: true - /@typescript-eslint/utils/5.20.0_eslint@8.16.0+typescript@4.7.2: - resolution: {integrity: sha512-lHONGJL1LIO12Ujyx8L8xKbwWSkoUKFSO+0wDAqGXiudWB2EO7WEUT+YZLtVbmOmSllAjLb9tpoIPwpRe5Tn6w==} + /@typescript-eslint/utils/5.27.0_eslint@8.16.0+typescript@4.7.2: + resolution: {integrity: sha512-nZvCrkIJppym7cIbP3pOwIkAefXOmfGPnCM0LQfzNaKxJHI6VjI8NC662uoiPlaf5f6ymkTy9C3NQXev2mdXmA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 dependencies: '@types/json-schema': 7.0.11 - '@typescript-eslint/scope-manager': 5.20.0 - '@typescript-eslint/types': 5.20.0 - '@typescript-eslint/typescript-estree': 5.20.0_typescript@4.7.2 + '@typescript-eslint/scope-manager': 5.27.0 + '@typescript-eslint/types': 5.27.0 + '@typescript-eslint/typescript-estree': 5.27.0_typescript@4.7.2 eslint: 8.16.0 eslint-scope: 5.1.1 eslint-utils: 3.0.0_eslint@8.16.0 transitivePeerDependencies: - supports-color - typescript + dev: true - /@typescript-eslint/utils/5.27.0_eslint@8.16.0+typescript@4.7.2: - resolution: {integrity: sha512-nZvCrkIJppym7cIbP3pOwIkAefXOmfGPnCM0LQfzNaKxJHI6VjI8NC662uoiPlaf5f6ymkTy9C3NQXev2mdXmA==} + /@typescript-eslint/utils/5.30.7_eslint@8.16.0+typescript@4.7.2: + resolution: {integrity: sha512-Z3pHdbFw+ftZiGUnm1GZhkJgVqsDL5CYW2yj+TB2mfXDFOMqtbzQi2dNJIyPqPbx9mv2kUxS1gU+r2gKlKi1rQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 dependencies: '@types/json-schema': 7.0.11 - '@typescript-eslint/scope-manager': 5.27.0 - '@typescript-eslint/types': 5.27.0 - '@typescript-eslint/typescript-estree': 5.27.0_typescript@4.7.2 + '@typescript-eslint/scope-manager': 5.30.7 + '@typescript-eslint/types': 5.30.7 + '@typescript-eslint/typescript-estree': 5.30.7_typescript@4.7.2 eslint: 8.16.0 eslint-scope: 5.1.1 eslint-utils: 3.0.0_eslint@8.16.0 transitivePeerDependencies: - supports-color - typescript - dev: true /@typescript-eslint/visitor-keys/5.10.1: resolution: {integrity: sha512-NjQ0Xinhy9IL979tpoTRuLKxMc0zJC7QVSdeerXs2/QvOy2yRkzX5dRb10X5woNUdJgU8G3nYRDlI33sq1K4YQ==} @@ -7082,13 +7211,6 @@ packages: eslint-visitor-keys: 3.3.0 dev: true - /@typescript-eslint/visitor-keys/5.20.0: - resolution: {integrity: sha512-1flRpNF+0CAQkMNlTJ6L/Z5jiODG/e5+7mk6XwtPOUS3UrTz3UOiAg9jG2VtKsWI6rZQfy4C6a232QNRZTRGlg==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dependencies: - '@typescript-eslint/types': 5.20.0 - eslint-visitor-keys: 3.3.0 - /@typescript-eslint/visitor-keys/5.27.0: resolution: {integrity: sha512-46cYrteA2MrIAjv9ai44OQDUoCZyHeGIc4lsjCUX2WT6r4C+kidz1bNiR4017wHOPUythYeH+Sc7/cFP97KEAA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -7097,6 +7219,13 @@ packages: eslint-visitor-keys: 3.3.0 dev: true + /@typescript-eslint/visitor-keys/5.30.7: + resolution: {integrity: sha512-KrRXf8nnjvcpxDFOKej4xkD7657+PClJs5cJVSG7NNoCNnjEdc46juNAQt7AyuWctuCgs6mVRc1xGctEqrjxWw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + '@typescript-eslint/types': 5.30.7 + eslint-visitor-keys: 3.3.0 + /@ucast/core/1.10.1: resolution: {integrity: sha512-sXKbvQiagjFh2JCpaHUa64P4UdJbOxYeC5xiZFn8y6iYdb0WkismduE+RmiJrIjw/eLDYmIEXiQeIYYowmkcAw==} dev: false @@ -7128,7 +7257,7 @@ packages: /@vue/compiler-core/3.2.36: resolution: {integrity: sha512-bbyZM5hvBicv0PW3KUfVi+x3ylHnfKG7DOn5wM+f2OztTzTjLEyBb/5yrarIYpmnGitVGbjZqDbODyW4iK8hqw==} dependencies: - '@babel/parser': 7.18.4 + '@babel/parser': 7.18.13 '@vue/shared': 3.2.36 estree-walker: 2.0.2 source-map: 0.6.1 @@ -7144,7 +7273,7 @@ packages: /@vue/compiler-sfc/3.2.36: resolution: {integrity: sha512-AvGb4bTj4W8uQ4BqaSxo7UwTEqX5utdRSMyHy58OragWlt8nEACQ9mIeQh3K4di4/SX+41+pJrLIY01lHAOFOA==} dependencies: - '@babel/parser': 7.18.4 + '@babel/parser': 7.18.13 '@vue/compiler-core': 3.2.36 '@vue/compiler-dom': 3.2.36 '@vue/compiler-ssr': 3.2.36 @@ -7166,7 +7295,7 @@ packages: /@vue/reactivity-transform/3.2.36: resolution: {integrity: sha512-Jk5o2BhpODC9XTA7o4EL8hSJ4JyrFWErLtClG3NH8wDS7ri9jBDWxI7/549T7JY9uilKsaNM+4pJASLj5dtRwA==} dependencies: - '@babel/parser': 7.18.4 + '@babel/parser': 7.18.13 '@vue/compiler-core': 3.2.36 '@vue/shared': 3.2.36 estree-walker: 2.0.2 @@ -7376,7 +7505,7 @@ packages: resolution: {integrity: sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==} engines: {node: '>=6.0'} dependencies: - '@babel/runtime': 7.18.3 + '@babel/runtime': 7.18.9 '@babel/runtime-corejs3': 7.18.3 dev: true @@ -7411,7 +7540,7 @@ packages: dev: true /array-flatten/1.1.1: - resolution: {integrity: sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=} + resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} /array-includes/3.1.5: resolution: {integrity: sha512-iSDYZMMyTPkiFasVqfuAQnWAYcvO/SeBSCGKePoEthjp4LEMTe4uLc7b025o4jAZpHhihh8xPo99TNWUWWkGDQ==} @@ -7654,7 +7783,7 @@ packages: resolution: {integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==} engines: {node: '>=8'} dependencies: - '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-plugin-utils': 7.18.9 '@istanbuljs/load-nyc-config': 1.1.0 '@istanbuljs/schema': 0.1.3 istanbul-lib-instrument: 5.2.0 @@ -7677,32 +7806,32 @@ packages: resolution: {integrity: sha512-50wCwD5EMNW4aRpOwtqzyZHIewTYNxLA4nhB+09d8BIssfNfzBRhkBIHiaPv1Si226TQSvp8gxAJm2iY2qs2hQ==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dependencies: - '@babel/template': 7.16.7 - '@babel/types': 7.18.4 + '@babel/template': 7.18.10 + '@babel/types': 7.18.13 '@types/babel__core': 7.1.19 '@types/babel__traverse': 7.17.1 dev: true - /babel-plugin-polyfill-corejs2/0.3.1_@babel+core@7.18.2: - resolution: {integrity: sha512-v7/T6EQcNfVLfcN2X8Lulb7DjprieyLWJK/zOWH5DUYcAgex9sP3h25Q+DLsX9TloXe3y1O8l2q2Jv9q8UVB9w==} + /babel-plugin-polyfill-corejs2/0.3.2_@babel+core@7.18.2: + resolution: {integrity: sha512-LPnodUl3lS0/4wN3Rb+m+UK8s7lj2jcLRrjho4gLw+OJs+I4bvGXshINesY5xx/apM+biTnQ9reDI8yj+0M5+Q==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/compat-data': 7.17.10 '@babel/core': 7.18.2 - '@babel/helper-define-polyfill-provider': 0.3.1_@babel+core@7.18.2 + '@babel/helper-define-polyfill-provider': 0.3.2_@babel+core@7.18.2 semver: 6.3.0 transitivePeerDependencies: - supports-color dev: true - /babel-plugin-polyfill-corejs3/0.5.2_@babel+core@7.18.2: - resolution: {integrity: sha512-G3uJih0XWiID451fpeFaYGVuxHEjzKTHtc9uGFEjR6hHrvNzeS/PX+LLLcetJcytsB5m4j+K3o/EpXJNb/5IEQ==} + /babel-plugin-polyfill-corejs3/0.5.3_@babel+core@7.18.2: + resolution: {integrity: sha512-zKsXDh0XjnrUEW0mxIHLfjBfnXSMr5Q/goMe/fxpQnLm07mcOZiIZHBNWCMx60HmdvjxfXcalac0tfFg0wqxyw==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.2 - '@babel/helper-define-polyfill-provider': 0.3.1_@babel+core@7.18.2 + '@babel/helper-define-polyfill-provider': 0.3.2_@babel+core@7.18.2 core-js-compat: 3.22.8 transitivePeerDependencies: - supports-color @@ -7714,7 +7843,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.2 - '@babel/helper-define-polyfill-provider': 0.3.1_@babel+core@7.18.2 + '@babel/helper-define-polyfill-provider': 0.3.2_@babel+core@7.18.2 transitivePeerDependencies: - supports-color dev: true @@ -7867,16 +7996,15 @@ packages: resolution: {integrity: sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==} dev: true - /browserslist/4.20.3: - resolution: {integrity: sha512-NBhymBQl1zM0Y5dQT/O+xiLP9/rzOIQdKM/eMJBAq7yBgaB6krIYLGejrwVYnSHZdqjscB1SPuAjHwxjvN6Wdg==} + /browserslist/4.21.3: + resolution: {integrity: sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true dependencies: - caniuse-lite: 1.0.30001346 - electron-to-chromium: 1.4.144 - escalade: 3.1.1 - node-releases: 2.0.5 - picocolors: 1.0.0 + caniuse-lite: 1.0.30001382 + electron-to-chromium: 1.4.227 + node-releases: 2.0.6 + update-browserslist-db: 1.0.5_browserslist@4.21.3 dev: true /bs-logger/0.2.6: @@ -7979,8 +8107,8 @@ packages: engines: {node: '>=10'} dev: true - /caniuse-lite/1.0.30001346: - resolution: {integrity: sha512-q6ibZUO2t88QCIPayP/euuDREq+aMAxFE5S70PkrLh0iTDj/zEhgvJRKC2+CvXY6EWc6oQwUR48lL5vCW6jiXQ==} + /caniuse-lite/1.0.30001382: + resolution: {integrity: sha512-2rtJwDmSZ716Pxm1wCtbPvHtbDWAreTPxXbkc5RkKglow3Ig/4GNGazDI9/BVnXbG/wnv6r3B5FEbkfg9OcTGg==} /capture-exit/2.0.0: resolution: {integrity: sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==} @@ -8309,7 +8437,7 @@ packages: dev: true /concat-map/0.0.1: - resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=} /concurrently/7.2.1: resolution: {integrity: sha512-7cab/QyqipqghrVr9qZmoWbidu0nHsmxrpNqQ7r/67vfl1DWJElexehQnTH1p+87tDkihaAjM79xTZyBQh7HLw==} @@ -8389,17 +8517,19 @@ packages: /core-js-compat/3.22.8: resolution: {integrity: sha512-pQnwg4xtuvc2Bs/5zYQPaEYYSuTxsF7LBWF0SvnVhthZo/Qe+rJpcEekrdNK5DWwDJ0gv0oI9NNX5Mppdy0ctg==} dependencies: - browserslist: 4.20.3 + browserslist: 4.21.3 semver: 7.0.0 dev: true /core-js-pure/3.22.8: resolution: {integrity: sha512-bOxbZIy9S5n4OVH63XaLVXZ49QKicjowDx/UELyJ68vxfCRpYsbyh/WNZNfEfAk+ekA8vSjt+gCDpvh672bc3w==} + deprecated: core-js-pure@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js-pure. requiresBuild: true dev: true /core-js/3.22.8: resolution: {integrity: sha512-UoGQ/cfzGYIuiq6Z7vWL1HfkE9U9IZ4Ub+0XSiJTCzvbZzgPA69oDF2f+lgJ6dFFLEdjW5O6svvoKzXX23xFkA==} + deprecated: core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js. requiresBuild: true dev: false @@ -8512,6 +8642,16 @@ packages: /csstype/3.1.0: resolution: {integrity: sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA==} + /csurf/1.11.0: + resolution: {integrity: sha512-UCtehyEExKTxgiu8UHdGvHj4tnpE/Qctue03Giq5gPgMQ9cg/ciod5blZQ5a4uCEenNQjxyGuzygLdKUmee/bQ==} + engines: {node: '>= 0.8.0'} + dependencies: + cookie: 0.4.0 + cookie-signature: 1.0.6 + csrf: 3.1.0 + http-errors: 1.7.3 + dev: false + /cypress/10.6.0: resolution: {integrity: sha512-6sOpHjostp8gcLO34p6r/Ci342lBs8S5z9/eb3ZCQ22w2cIhMWGUoGKkosabPBfKcvRS9BE4UxybBtlIs8gTQA==} engines: {node: '>=12.0.0'} @@ -8562,16 +8702,6 @@ packages: yauzl: 2.10.0 dev: true - /csurf/1.11.0: - resolution: {integrity: sha512-UCtehyEExKTxgiu8UHdGvHj4tnpE/Qctue03Giq5gPgMQ9cg/ciod5blZQ5a4uCEenNQjxyGuzygLdKUmee/bQ==} - engines: {node: '>= 0.8.0'} - dependencies: - cookie: 0.4.0 - cookie-signature: 1.0.6 - csrf: 3.1.0 - http-errors: 1.7.3 - dev: false - /d3-array/1.2.4: resolution: {integrity: sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw==} dev: true @@ -8779,7 +8909,7 @@ packages: hasBin: true dependencies: '@babel/parser': 7.16.4 - '@babel/traverse': 7.18.2 + '@babel/traverse': 7.18.13 '@vue/compiler-sfc': 3.2.36 camelcase: 6.3.0 cosmiconfig: 7.0.1 @@ -8796,7 +8926,7 @@ packages: query-ast: 1.0.4 readdirp: 3.6.0 require-package-name: 2.0.1 - resolve: 1.22.0 + resolve: 1.22.1 sass: 1.52.2 scss-parser: 1.0.5 semver: 7.3.7 @@ -8890,7 +9020,7 @@ packages: /dom-helpers/5.2.1: resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==} dependencies: - '@babel/runtime': 7.18.3 + '@babel/runtime': 7.18.9 csstype: 3.1.0 dev: true @@ -8917,8 +9047,8 @@ packages: /ee-first/1.1.1: resolution: {integrity: sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=} - /electron-to-chromium/1.4.144: - resolution: {integrity: sha512-R3RV3rU1xWwFJlSClVWDvARaOk6VUO/FubHLodIASDB3Mc2dzuWvNdfOgH9bwHUTqT79u92qw60NWfwUdzAqdg==} + /electron-to-chromium/1.4.227: + resolution: {integrity: sha512-I9VVajA3oswIJOUFg2PSBqrHLF5Y+ahIfjOV9+v6uYyBqFZutmPxA6fxocDUUmgwYevRWFu1VjLyVG3w45qa/g==} dev: true /emittery/0.7.2: @@ -8956,8 +9086,8 @@ packages: once: 1.4.0 dev: true - /enhanced-resolve/5.9.3: - resolution: {integrity: sha512-Bq9VSor+kjvW3f9/MiiR4eE3XYgOl7/rS8lnSxbRbF3kS0B2r+Y9w5krBWxZgDxASVZbdYrn5wT4j/Wb0J9qow==} + /enhanced-resolve/5.10.0: + resolution: {integrity: sha512-T0yTFjdpldGY8PmuXXR0PyQ1ufZpEGiHVrp7zHKB7jdR4qlmZHhONVM5AQOAWXuF/w3dnHbEQVrNptJgt7F+cQ==} engines: {node: '>=10.13.0'} dependencies: graceful-fs: 4.2.10 @@ -9291,14 +9421,14 @@ packages: resolution: {integrity: sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA==} dependencies: debug: 2.6.9 - resolve: 1.22.0 + resolve: 1.22.1 dev: true /eslint-import-resolver-node/0.3.6: resolution: {integrity: sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==} dependencies: debug: 3.2.7 - resolve: 1.22.0 + resolve: 1.22.1 /eslint-import-resolver-typescript/2.4.0_ec640624cf8bc291366aa0299377ad0c: resolution: {integrity: sha512-useJKURidCcldRLCNKWemr1fFQL1SzB3G4a0li6lFGvlc5xGe1hY343bvG07cbpCzPuM/lK19FIJB3XGFSkplA==} @@ -9312,7 +9442,7 @@ packages: eslint-plugin-import: 2.25.2_eslint@8.16.0 glob: 7.2.3 is-glob: 4.0.3 - resolve: 1.22.0 + resolve: 1.22.1 tsconfig-paths: 3.14.1 transitivePeerDependencies: - supports-color @@ -9343,7 +9473,7 @@ packages: is-glob: 4.0.3 minimatch: 3.1.2 object.values: 1.1.5 - resolve: 1.22.0 + resolve: 1.22.1 tsconfig-paths: 3.14.1 dev: true @@ -9365,7 +9495,7 @@ packages: is-glob: 4.0.3 minimatch: 3.1.2 object.values: 1.1.5 - resolve: 1.22.0 + resolve: 1.22.1 tsconfig-paths: 3.14.1 /eslint-plugin-jsx-a11y/6.5.1_eslint@8.16.0: @@ -9374,7 +9504,7 @@ packages: peerDependencies: eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 dependencies: - '@babel/runtime': 7.18.3 + '@babel/runtime': 7.18.9 aria-query: 4.2.2 array-includes: 3.1.5 ast-types-flow: 0.0.7 @@ -10513,7 +10643,7 @@ packages: /i18next/21.8.7: resolution: {integrity: sha512-fYbEmaCIMSDrAzXGnisZyBd0h3bcO43jVJa5fulk4ambTIOgs9tqgNsiyr1sy6Xi4iZpYMHp2ZBjU3IcCLGxPA==} dependencies: - '@babel/runtime': 7.18.3 + '@babel/runtime': 7.18.9 dev: false /iconv-lite/0.4.24: @@ -10949,7 +11079,7 @@ packages: engines: {node: '>=8'} dependencies: '@babel/core': 7.18.2 - '@babel/parser': 7.18.4 + '@babel/parser': 7.18.13 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.0 semver: 6.3.0 @@ -11437,6 +11567,7 @@ packages: /jest-fast-check/1.0.2_fast-check@2.25.0+jest@27.5.1: resolution: {integrity: sha512-XuPENBGUN2gaTdPxgPJwFsYp0oj+MieRLpBha0flMxfI01zPHgGkZjGSLThimmsU5XFTGhJYZGVnift7UmsSuw==} + deprecated: Replaced by @fast-check/jest peerDependencies: fast-check: ^2.0.0 jest: '>=25.1.0' @@ -11606,7 +11737,7 @@ packages: resolution: {integrity: sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dependencies: - '@babel/code-frame': 7.16.7 + '@babel/code-frame': 7.18.6 '@jest/types': 27.5.1 '@types/stack-utils': 2.0.1 chalk: 4.1.2 @@ -11715,7 +11846,7 @@ packages: jest-pnp-resolver: 1.2.2_jest-resolve@27.4.6 jest-util: 27.5.1 jest-validate: 27.5.1 - resolve: 1.22.0 + resolve: 1.22.1 resolve.exports: 1.1.0 slash: 3.0.0 dev: true @@ -11731,7 +11862,7 @@ packages: jest-pnp-resolver: 1.2.2_jest-resolve@27.5.1 jest-util: 27.5.1 jest-validate: 27.5.1 - resolve: 1.22.0 + resolve: 1.22.1 resolve.exports: 1.1.0 slash: 3.0.0 dev: true @@ -11913,10 +12044,10 @@ packages: engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dependencies: '@babel/core': 7.18.2 - '@babel/generator': 7.18.2 + '@babel/generator': 7.18.13 '@babel/plugin-syntax-typescript': 7.17.12_@babel+core@7.18.2 - '@babel/traverse': 7.18.2 - '@babel/types': 7.18.4 + '@babel/traverse': 7.18.13 + '@babel/types': 7.18.13 '@jest/transform': 27.5.1 '@jest/types': 27.5.1 '@types/babel__traverse': 7.17.1 @@ -11943,10 +12074,10 @@ packages: engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dependencies: '@babel/core': 7.18.2 - '@babel/generator': 7.18.2 + '@babel/generator': 7.18.13 '@babel/plugin-syntax-typescript': 7.17.12_@babel+core@7.18.2 - '@babel/traverse': 7.18.2 - '@babel/types': 7.18.4 + '@babel/traverse': 7.18.13 + '@babel/types': 7.18.13 '@jest/transform': 27.5.1 '@jest/types': 27.5.1 '@types/babel__traverse': 7.17.1 @@ -12729,7 +12860,7 @@ packages: resolution: {integrity: sha512-3ZH4UWBGpAwCKdfjynLQpUDVZWMe6vRHwarIpMdGLUp89CVR9hjzgyWERtMyqx+fPEqQ/PsAxFwvwPxLFxW40A==} engines: {node: '>=12.13.0'} dependencies: - '@babel/runtime': 7.18.3 + '@babel/runtime': 7.18.9 dev: true /ms/2.0.0: @@ -12815,7 +12946,7 @@ packages: next: '>= 10.0.0' react: '>= 16.8.0' dependencies: - '@babel/runtime': 7.18.3 + '@babel/runtime': 7.18.9 '@types/hoist-non-react-statics': 3.3.1 core-js: 3.22.8 hoist-non-react-statics: 3.3.2 @@ -12832,10 +12963,53 @@ packages: /next-transpile-modules/9.0.0: resolution: {integrity: sha512-VCNFOazIAnXn1hvgYYSTYMnoWgKgwlYh4lm1pKbSfiB3kj5ZYLcKVhfh3jkPOg1cnd9DP+pte9yCUocdPEUBTQ==} dependencies: - enhanced-resolve: 5.9.3 + enhanced-resolve: 5.10.0 escalade: 3.1.1 dev: false + /next/12.1.6_757e1cdb914f1581201e1269321c5a94: + resolution: {integrity: sha512-cebwKxL3/DhNKfg9tPZDQmbRKjueqykHHbgaoG4VBRH3AHQJ2HO0dbKFiS1hPhe1/qgc2d/hFeadsbPicmLD+A==} + engines: {node: '>=12.22.0'} + hasBin: true + peerDependencies: + fibers: '>= 3.1.0' + node-sass: ^6.0.0 || ^7.0.0 + react: ^17.0.2 || ^18.0.0-0 + react-dom: ^17.0.2 || ^18.0.0-0 + sass: ^1.3.0 + peerDependenciesMeta: + fibers: + optional: true + node-sass: + optional: true + sass: + optional: true + dependencies: + '@next/env': 12.1.6 + caniuse-lite: 1.0.30001382 + postcss: 8.4.5 + react: 17.0.2 + react-dom: 17.0.2_react@17.0.2 + sass: 1.52.2 + styled-jsx: 5.0.2_react@17.0.2 + optionalDependencies: + '@next/swc-android-arm-eabi': 12.1.6 + '@next/swc-android-arm64': 12.1.6 + '@next/swc-darwin-arm64': 12.1.6 + '@next/swc-darwin-x64': 12.1.6 + '@next/swc-linux-arm-gnueabihf': 12.1.6 + '@next/swc-linux-arm64-gnu': 12.1.6 + '@next/swc-linux-arm64-musl': 12.1.6 + '@next/swc-linux-x64-gnu': 12.1.6 + '@next/swc-linux-x64-musl': 12.1.6 + '@next/swc-win32-arm64-msvc': 12.1.6 + '@next/swc-win32-ia32-msvc': 12.1.6 + '@next/swc-win32-x64-msvc': 12.1.6 + transitivePeerDependencies: + - '@babel/core' + - babel-plugin-macros + dev: false + /next/12.1.6_fe3859d3c8c94a7c7e52ff5ea4f038fc: resolution: {integrity: sha512-cebwKxL3/DhNKfg9tPZDQmbRKjueqykHHbgaoG4VBRH3AHQJ2HO0dbKFiS1hPhe1/qgc2d/hFeadsbPicmLD+A==} engines: {node: '>=12.22.0'} @@ -12855,7 +13029,7 @@ packages: optional: true dependencies: '@next/env': 12.1.6 - caniuse-lite: 1.0.30001346 + caniuse-lite: 1.0.30001382 postcss: 8.4.5 react: 17.0.2 react-dom: 17.0.2_react@17.0.2 @@ -12919,8 +13093,8 @@ packages: resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} dev: true - /node-releases/2.0.5: - resolution: {integrity: sha512-U9h1NLROZTq9uE1SNffn6WuPDg8icmi3ns4rEl/oTfIle4iLjTliCzgTsbaIFMq/Xn078/lfY/BL0GWZ+psK4Q==} + /node-releases/2.0.6: + resolution: {integrity: sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==} dev: true /nodemon/2.0.19: @@ -12960,7 +13134,7 @@ packages: resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} dependencies: hosted-git-info: 2.8.9 - resolve: 1.22.0 + resolve: 1.22.1 semver: 5.7.1 validate-npm-package-license: 3.0.4 @@ -13300,7 +13474,7 @@ packages: resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} engines: {node: '>=8'} dependencies: - '@babel/code-frame': 7.16.7 + '@babel/code-frame': 7.18.6 error-ex: 1.3.2 json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 @@ -13616,7 +13790,7 @@ packages: peerDependencies: react: ^15.3.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 dependencies: - '@babel/runtime': 7.18.3 + '@babel/runtime': 7.18.9 react: 17.0.2 dev: true @@ -13648,7 +13822,7 @@ packages: peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 dependencies: - '@babel/runtime': 7.18.3 + '@babel/runtime': 7.18.9 focus-lock: 0.10.2 prop-types: 15.8.1 react: 17.0.2 @@ -13672,7 +13846,7 @@ packages: react-native: optional: true dependencies: - '@babel/runtime': 7.18.3 + '@babel/runtime': 7.18.9 html-escaper: 2.0.2 html-parse-stringify: 3.0.1 i18next: 21.8.7 @@ -13714,7 +13888,7 @@ packages: react: '>=16.6.0' react-dom: '>=16.6.0' dependencies: - '@babel/runtime': 7.18.3 + '@babel/runtime': 7.18.9 dom-helpers: 5.2.1 loose-envify: 1.4.0 prop-types: 15.8.1 @@ -13864,7 +14038,7 @@ packages: /regenerator-transform/0.15.0: resolution: {integrity: sha512-LsrGtPmbYg19bcPHwdtmXwbW+TqNvtY4riE3P83foeHRroMbH6/2ddFBfab3t7kbzc7v7p4wbkIecHImqt0QNg==} dependencies: - '@babel/runtime': 7.18.3 + '@babel/runtime': 7.18.9 dev: true /regex-not/1.0.2: @@ -13996,6 +14170,15 @@ packages: is-core-module: 2.9.0 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 + dev: true + + /resolve/1.22.1: + resolution: {integrity: sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==} + hasBin: true + dependencies: + is-core-module: 2.9.0 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 /resolve/2.0.0-next.3: resolution: {integrity: sha512-W8LucSynKUIDu9ylraa7ueVZ7hc0uAgJBxVsQSKOXOyle8a93qXhcz+XAXZ8bIq2d6i4Ehddn6Evt+0/UwKk6Q==} @@ -14559,7 +14742,7 @@ packages: dev: true /string-width/1.0.2: - resolution: {integrity: sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=} + resolution: {integrity: sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==} engines: {node: '>=0.10.0'} dependencies: code-point-at: 1.1.0 @@ -14621,7 +14804,7 @@ packages: safe-buffer: 5.2.1 /strip-ansi/3.0.1: - resolution: {integrity: sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=} + resolution: {integrity: sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==} engines: {node: '>=0.10.0'} dependencies: ansi-regex: 2.1.1 @@ -14691,6 +14874,22 @@ packages: react: 17.0.2 dev: false + /styled-jsx/5.0.2_react@17.0.2: + resolution: {integrity: sha512-LqPQrbBh3egD57NBcHET4qcgshPks+yblyhPlH2GY8oaDgKs8SK4C3dBh3oSJjgzJ3G5t1SYEZGHkP+QEpX9EQ==} + engines: {node: '>= 12.0.0'} + peerDependencies: + '@babel/core': '*' + babel-plugin-macros: '*' + react: '>= 16.8.0 || 17.x.x || ^18.0.0-0' + peerDependenciesMeta: + '@babel/core': + optional: true + babel-plugin-macros: + optional: true + dependencies: + react: 17.0.2 + dev: false + /supports-color/5.5.0: resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} engines: {node: '>=4'} @@ -14841,6 +15040,7 @@ packages: /timsort/0.3.0: resolution: {integrity: sha512-qsdtZH+vMoCARQtyod4imc2nIJwg9Cc7lPRrw9CzF8ZKR0khdr8+2nX80PBhET3tcyTtJDxAffGh2rXH4tyU8A==} + dev: false /tmp/0.0.33: resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} @@ -15229,14 +15429,13 @@ packages: dependencies: is-typedarray: 1.0.0 - /typescript/4.6.4: - resolution: {integrity: sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg==} + /typescript/4.7.2: + resolution: {integrity: sha512-Mamb1iX2FDUpcTRzltPxgWMKy3fhg0TN378ylbktPGPK/99KbDtMQ4W1hwgsbPAsG3a0xKa1vmw4VKZQbkvz5A==} engines: {node: '>=4.2.0'} hasBin: true - dev: true - /typescript/4.7.2: - resolution: {integrity: sha512-Mamb1iX2FDUpcTRzltPxgWMKy3fhg0TN378ylbktPGPK/99KbDtMQ4W1hwgsbPAsG3a0xKa1vmw4VKZQbkvz5A==} + /typescript/4.7.4: + resolution: {integrity: sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==} engines: {node: '>=4.2.0'} hasBin: true dev: true @@ -15302,7 +15501,7 @@ packages: engines: {node: '>= 10.0.0'} /unpipe/1.0.0: - resolution: {integrity: sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=} + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} engines: {node: '>= 0.8'} /unset-value/1.0.0: @@ -15318,6 +15517,17 @@ packages: engines: {node: '>=8'} dev: true + /update-browserslist-db/1.0.5_browserslist@4.21.3: + resolution: {integrity: sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + dependencies: + browserslist: 4.21.3 + escalade: 3.1.1 + picocolors: 1.0.0 + dev: true + /uri-js/4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} dependencies: @@ -15372,7 +15582,7 @@ packages: dev: true /util-deprecate/1.0.2: - resolution: {integrity: sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=} + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} /util-extend/1.0.3: resolution: {integrity: sha512-mLs5zAK+ctllYBj+iAQvlDCwoxU/WDOUaJkcFudeiAX6OajC6BKXJUa9a+tbtkC11dz2Ufb7h0lyvIOVn4LADA==} @@ -15433,7 +15643,7 @@ packages: engines: {node: '>= 0.8'} /verror/1.10.0: - resolution: {integrity: sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==} + resolution: {integrity: sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=} engines: {'0': node >=0.6.0} dependencies: assert-plus: 1.0.0 @@ -15547,7 +15757,7 @@ packages: /wide-align/1.1.5: resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==} dependencies: - string-width: 1.0.2 + string-width: 4.2.3 dev: true /winston-transport/4.5.0: diff --git a/rush.json b/rush.json index 201817862f..327b49c37b 100644 --- a/rush.json +++ b/rush.json @@ -47,6 +47,12 @@ "reviewCategory": "production", "shouldPublish": true }, + { + "packageName": "@aws/workbench-core-swb-common-ui", + "projectFolder": "workbench-core/swb-common-ui", + "reviewCategory": "production", + "shouldPublish": true + }, { "packageName": "@aws/workbench-core-logging", "projectFolder": "workbench-core/logging", diff --git a/solutions/example-ui-app/package.json b/solutions/example-ui-app/package.json index ceac5fd42d..c0d73889c1 100644 --- a/solutions/example-ui-app/package.json +++ b/solutions/example-ui-app/package.json @@ -49,9 +49,9 @@ "@babel/preset-env": "^7.16.11", "@babel/preset-react": "^7.16.7", "@babel/preset-typescript": "^7.16.7", - "@rushstack/eslint-config": "^2.5.1", - "@rushstack/heft": "^0.45.0", - "@rushstack/heft-jest-plugin": "^0.2.3", + "@rushstack/eslint-config": "^3.0.0", + "@rushstack/heft": "^0.47.5", + "@rushstack/heft-jest-plugin": "^0.3.28", "@testing-library/dom": "^8.13.0", "@testing-library/jest-dom": "5.16.1", "@testing-library/react": "12.1.2", diff --git a/solutions/swb-app/package.json b/solutions/swb-app/package.json index 6f8e79e132..02f7f05f3b 100644 --- a/solutions/swb-app/package.json +++ b/solutions/swb-app/package.json @@ -46,10 +46,10 @@ }, "devDependencies": { "@aws/eslint-config-workbench-core-eslint-custom": "workspace:*", - "@rushstack/eslint-config": "^2.5.1", - "@rushstack/heft": "^0.45.0", - "@rushstack/heft-jest-plugin": "^0.2.3", - "@rushstack/heft-node-rig": "^1.7.1", + "@rushstack/eslint-config": "^3.0.0", + "@rushstack/heft": "^0.47.5", + "@rushstack/heft-jest-plugin": "^0.3.28", + "@rushstack/heft-node-rig": "^1.10.5", "@types/cookie-parser": "^1.4.3", "@types/cors": "^2.8.12", "@types/express": "^4.17.13", diff --git a/solutions/swb-reference/package.json b/solutions/swb-reference/package.json index 0b07b873e5..c9d4cef108 100644 --- a/solutions/swb-reference/package.json +++ b/solutions/swb-reference/package.json @@ -68,10 +68,10 @@ "@aws-sdk/client-sts": "^3.118.1", "@aws-sdk/types": "^3.110.0", "@aws/eslint-config-workbench-core-eslint-custom": "workspace:*", - "@rushstack/eslint-config": "^2.5.1", - "@rushstack/heft": "^0.45.0", - "@rushstack/heft-jest-plugin": "^0.2.3", - "@rushstack/heft-node-rig": "^1.7.1", + "@rushstack/eslint-config": "^3.0.0", + "@rushstack/heft": "^0.47.5", + "@rushstack/heft-jest-plugin": "^0.3.28", + "@rushstack/heft-node-rig": "^1.10.5", "@types/aws-lambda": "^8.10.92", "@types/express": "^4.17.13", "@types/heft-jest": "1.0.2", diff --git a/solutions/swb-ui/.eslintignore b/solutions/swb-ui/.eslintignore index 195a261c9c..90314a910f 100644 --- a/solutions/swb-ui/.eslintignore +++ b/solutions/swb-ui/.eslintignore @@ -7,4 +7,8 @@ next.config.js node_modules next-env.d.ts next-i18next.config.js -temp \ No newline at end of file +temp +**/lib +infrastructure/src/redirectFunction.js +out +infrastructure/cdk.out \ No newline at end of file diff --git a/solutions/swb-ui/infrastructure/package.json b/solutions/swb-ui/infrastructure/package.json index ef3c336242..bf1ba4675f 100644 --- a/solutions/swb-ui/infrastructure/package.json +++ b/solutions/swb-ui/infrastructure/package.json @@ -32,9 +32,9 @@ "js-yaml": "^4.1.0" }, "devDependencies": { - "@rushstack/heft": "^0.45.0", - "@rushstack/heft-jest-plugin": "^0.2.3", - "@rushstack/heft-node-rig": "^1.7.1", + "@rushstack/heft": "^0.47.5", + "@rushstack/heft-jest-plugin": "^0.3.28", + "@rushstack/heft-node-rig": "^1.10.5", "@types/jest": "^27.4.1", "@types/js-yaml": "^4.0.5", "@types/node": "^14", diff --git a/solutions/swb-ui/infrastructure/src/SWBUIStack.ts b/solutions/swb-ui/infrastructure/src/SWBUIStack.ts index 6cbbf8737c..e402ac6c7b 100644 --- a/solutions/swb-ui/infrastructure/src/SWBUIStack.ts +++ b/solutions/swb-ui/infrastructure/src/SWBUIStack.ts @@ -40,6 +40,7 @@ export class SWBUIStack extends Stack { RESPONSE_HEADERS_ARTIFACT_NAME: string; RESPONSE_HEADERS_NAME: string; }; + // eslint-disable-next-line @typescript-eslint/explicit-member-accessibility constructor(scope: Construct, id: string, props?: StackProps) { const { STAGE, @@ -133,6 +134,7 @@ export class SWBUIStack extends Stack { this._addS3TLSSigV4BucketPolicy(s3Bucket); + // eslint-disable-next-line no-new new CfnOutput(this, outputKey, { value: s3Bucket.bucketArn }); @@ -140,6 +142,7 @@ export class SWBUIStack extends Stack { } private _deployS3BucketAndInvalidateDistribution(bucket: Bucket, distribution: Distribution): void { + // eslint-disable-next-line no-new new BucketDeployment(this, this.distributionEnvVars.S3_ARTIFACT_BUCKET_DEPLOYMENT_NAME, { destinationBucket: bucket, sources: [Source.asset(path.resolve(__dirname, '../../out'))], @@ -177,6 +180,7 @@ export class SWBUIStack extends Stack { }, additionalBehaviors: {} }); + // eslint-disable-next-line no-new new CfnOutput(this, this.distributionEnvVars.DISTRIBUTION_ARTIFACT_DOMAIN, { value: `https://${distribution.distributionDomainName}` }); @@ -187,6 +191,7 @@ export class SWBUIStack extends Stack { This function recieves the web routing format URL and transforms it into file path. e.g. /environments => /environments/index.html */ + // eslint-disable-next-line @typescript-eslint/ban-types private _createRedirectFunction(): Function { return new Function(this, this.distributionEnvVars.DISTRIBUTION_FUNCTION_ARTIFACT_NAME, { code: FunctionCode.fromFile({ diff --git a/solutions/swb-ui/infrastructure/src/constants.ts b/solutions/swb-ui/infrastructure/src/constants.ts index a33cc4f7f3..15def0431b 100644 --- a/solutions/swb-ui/infrastructure/src/constants.ts +++ b/solutions/swb-ui/infrastructure/src/constants.ts @@ -80,7 +80,7 @@ function getAPIOutputs(): { awsRegionShortName: string; apiUrlOutput: string; aw const outputs = apiStackOutputs[apiStackName]; if (!outputs.awsRegionShortName || !outputs.apiUrlOutput || !outputs.awsRegion) { - throw `Configuration file for ${process.env.STAGE} was found with incorrect format. Please deploy application swb-reference and try again.`; //validate when API unsuccessfully finished and UI is deployed + throw new Error(`Configuration file for ${process.env.STAGE} was found with incorrect format. Please deploy application swb-reference and try again.`); //validate when API unsuccessfully finished and UI is deployed } return { awsRegionShortName: outputs.awsRegionShortName, @@ -91,7 +91,7 @@ function getAPIOutputs(): { awsRegionShortName: string; apiUrlOutput: string; aw console.error( `No API Stack deployed found for ${process.env.STAGE}.Please deploy application swb-reference and try again.` ); - throw `No API Stack deployed found for ${process.env.STAGE}.`; + throw new Error(`No API Stack deployed found for ${process.env.STAGE}.`); } } diff --git a/solutions/swb-ui/infrastructure/src/index.ts b/solutions/swb-ui/infrastructure/src/index.ts index 27a1586f7d..c98873d076 100644 --- a/solutions/swb-ui/infrastructure/src/index.ts +++ b/solutions/swb-ui/infrastructure/src/index.ts @@ -9,7 +9,9 @@ import 'source-map-support/register'; import * as cdk from 'aws-cdk-lib'; import { SWBUIStack } from './SWBUIStack'; +// eslint-disable-next-line @rushstack/typedef-var const app = new cdk.App(); +// eslint-disable-next-line no-new new SWBUIStack(app, 'SWBUIStack', { /* If you don't specify 'env', this stack will be environment-agnostic. * Account/Region-dependent features and context lookups will not work, diff --git a/solutions/swb-ui/package.json b/solutions/swb-ui/package.json index 38eb30cf66..e968010a26 100644 --- a/solutions/swb-ui/package.json +++ b/solutions/swb-ui/package.json @@ -24,8 +24,8 @@ "dev": "bash ./scripts/dev.sh", "export": "next build && next export", "license-checker": "license-checker --onlyAllow 'MIT; Apache-2.0; ISC; BSD'", - "lint": "next lint --max-warnings 0 && rushx pkg-json-lint", - "lint:fix": "next lint --fix", + "lint": "eslint . && rushx pkg-json-lint", + "lint:fix": "eslint . --fix", "make-badges": "istanbul-badges-readme --coverageDir=./temp/coverage", "pkg-json-lint": "npmPkgJsonLint -c ../../.npmpackagejsonlintrc.json .", "serve": "node .next/server/webpack-runtime.js", @@ -35,18 +35,13 @@ }, "dependencies": { "@aws/workbench-core-environments": "workspace:*", - "axios": "^0.27.1", - "date-fns": "^2.28.0", - "express": "^4.17.3", - "jwt-decode": "^3.1.2", + "@aws/workbench-core-swb-common-ui": "workspace:*", "next": "^12.1.5", "next-global-css": "^1.2.0", "next-transpile-modules": "^9.0.0", - "pkce-challenge": "^3.0.0", "react": "17.0.2", "react-dom": "17.0.2", - "swr": "^1.3.0", - "uuid": "^8.3.2" + "swr": "^1.3.0" }, "devDependencies": { "@aws/eslint-config-workbench-core-eslint-custom": "workspace:*", @@ -58,22 +53,19 @@ "@cloudscape-design/components": "^3.0.27", "@cloudscape-design/design-tokens": "^3.0.1", "@cloudscape-design/global-styles": "^1.0.1", - "@rushstack/eslint-config": "^2.5.1", - "@rushstack/heft": "^0.45.0", - "@rushstack/heft-jest-plugin": "^0.2.3", - "@rushstack/heft-node-rig": "^1.7.1", + "@rushstack/eslint-config": "^3.0.0", + "@rushstack/heft": "^0.47.5", + "@rushstack/heft-jest-plugin": "^0.3.28", + "@rushstack/heft-node-rig": "^1.10.5", "@testing-library/dom": "^8.13.0", "@testing-library/jest-dom": "5.16.1", "@testing-library/react": "12.1.2", "@testing-library/user-event": "13.5.0", - "@types/express": "^4.17.13", "@types/heft-jest": "1.0.2", "@types/jest": "^27.4.1", "@types/node": "^14", "@types/react": "17.0.38", - "@types/uuid": "^8.3.4", "babel-jest": "27.5.1", - "concurrently": "^7.0.0", "depcheck": "^1.4.3", "eslint": "^8.7.0", "eslint-config-next": "12.1.5", @@ -83,7 +75,6 @@ "jest": "^27.5.1", "license-check-and-add": "^4.0.5", "license-checker": "^25.0.1", - "nodemon": "^2.0.19", "npm-package-json-lint": "^6.3.0", "npm-package-json-lint-config-default": "^5.0.0", "sass": "^1.49.8", diff --git a/solutions/swb-ui/pnpm-lock.yaml b/solutions/swb-ui/pnpm-lock.yaml deleted file mode 100644 index c7509bcbf4..0000000000 --- a/solutions/swb-ui/pnpm-lock.yaml +++ /dev/null @@ -1,71 +0,0 @@ -lockfileVersion: 5.3 - -packages: - - /@babel/runtime/7.17.8: - resolution: {integrity: sha512-dQpEpK0O9o6lj6oPu0gRDbbnk+4LeHlNcBpspf6Olzt3GIX4P1lWF1gS+pHLDFlaJvbR6q7jCfQ08zA4QJBnmA==} - engines: {node: '>=6.9.0'} - dependencies: - regenerator-runtime: 0.13.9 - dev: false - - /@types/hoist-non-react-statics/3.3.1: - resolution: {integrity: sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==} - dependencies: - '@types/react': 17.0.39 - hoist-non-react-statics: 3.3.2 - dev: false - - /@types/prop-types/15.7.4: - resolution: {integrity: sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ==} - dev: false - - /@types/react/17.0.39: - resolution: {integrity: sha512-UVavlfAxDd/AgAacMa60Azl7ygyQNRwC/DsHZmKgNvPmRR5p70AJ5Q9EAmL2NWOJmeV+vVUI4IAP7GZrN8h8Ug==} - dependencies: - '@types/prop-types': 15.7.4 - '@types/scheduler': 0.16.2 - csstype: 3.0.11 - dev: false - - /@types/scheduler/0.16.2: - resolution: {integrity: sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==} - dev: false - - /core-js/3.21.1: - resolution: {integrity: sha512-FRq5b/VMrWlrmCzwRrpDYNxyHP9BcAZC+xHJaqTgIE5091ZV1NTmyh0sGOg5XqpnHvR0svdy0sv1gWA1zmhxig==} - requiresBuild: true - dev: false - - /csstype/3.0.11: - resolution: {integrity: sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw==} - dev: false - - /hoist-non-react-statics/3.3.2: - resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} - dependencies: - react-is: 16.13.1 - dev: false - - /html-escaper/2.0.2: - resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} - dev: false - - /html-parse-stringify/3.0.1: - resolution: {integrity: sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==} - dependencies: - void-elements: 3.1.0 - dev: false - - /react-is/16.13.1: - resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} - dev: false - - /regenerator-runtime/0.13.9: - resolution: {integrity: sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==} - dev: false - - /void-elements/3.1.0: - resolution: {integrity: sha1-YU9/v42AHwu18GYfWy9XhXUOTwk=} - engines: {node: '>=0.10.0'} - dev: false diff --git a/solutions/swb-ui/src/api/datasets.ts b/solutions/swb-ui/src/api/datasets.ts index 361af37b54..68f701840c 100644 --- a/solutions/swb-ui/src/api/datasets.ts +++ b/solutions/swb-ui/src/api/datasets.ts @@ -3,13 +3,13 @@ * SPDX-License-Identifier: Apache-2.0 */ +import { httpApiGet } from '@aws/workbench-core-swb-common-ui'; import useSWR from 'swr'; import { DatasetItem } from '../models/Dataset'; -import { httpApiGet } from './apiHelper'; -const useDatasets = () => { +const useDatasets = (): { datasets: DatasetItem[], areDatasetsLoading: boolean } => { const { data, isValidating } = useSWR(() => 'datasets', httpApiGet); - const datasets: DatasetItem[] = data || []; + const datasets: DatasetItem[] = data?.data ?? []; return { datasets, areDatasetsLoading: isValidating }; }; diff --git a/solutions/swb-ui/src/api/environmentTypeConfigs.ts b/solutions/swb-ui/src/api/environmentTypeConfigs.ts index e237a1dd57..c531ae600d 100644 --- a/solutions/swb-ui/src/api/environmentTypeConfigs.ts +++ b/solutions/swb-ui/src/api/environmentTypeConfigs.ts @@ -3,16 +3,16 @@ * SPDX-License-Identifier: Apache-2.0 */ +import { httpApiGet } from '@aws/workbench-core-swb-common-ui'; import useSWR from 'swr'; -import { httpApiGet } from './apiHelper'; import { EnvTypeConfigItem } from '../models/EnvironmentTypeConfig'; -const useEnvTypeConfigs = (id: string) => { +const useEnvTypeConfigs = (id: string): {envTypeConfigs: EnvTypeConfigItem[], areEnvTypeConfigsLoading: boolean} => { const { data, isValidating } = useSWR( () => (id ? `environmentTypes/${id}/configurations` : null), httpApiGet ); - const envTypeConfigs: EnvTypeConfigItem[] = (data && data.data) || []; + const envTypeConfigs: EnvTypeConfigItem[] = data?.data ?? []; return { envTypeConfigs, areEnvTypeConfigsLoading: isValidating }; }; diff --git a/solutions/swb-ui/src/api/environmentTypes.ts b/solutions/swb-ui/src/api/environmentTypes.ts index ab9e514b55..e99678b82c 100644 --- a/solutions/swb-ui/src/api/environmentTypes.ts +++ b/solutions/swb-ui/src/api/environmentTypes.ts @@ -3,13 +3,13 @@ * SPDX-License-Identifier: Apache-2.0 */ +import { httpApiGet } from '@aws/workbench-core-swb-common-ui'; import useSWR from 'swr'; -import { httpApiGet } from './apiHelper'; import { EnvTypeItem } from '../models/EnvironmentType'; -const useEnvironmentType = () => { +const useEnvironmentType = (): { envTypes: EnvTypeItem[], areEnvTypesLoading: boolean } => { const { data, isValidating } = useSWR(`environmentTypes`, httpApiGet); - const envTypes: EnvTypeItem[] = ((data && data.data) || []).filter( + const envTypes: EnvTypeItem[] = (data?.data ?? []).filter( (t: EnvTypeItem) => t.status === 'APPROVED' ); return { envTypes, areEnvTypesLoading: isValidating }; diff --git a/solutions/swb-ui/src/api/environments.ts b/solutions/swb-ui/src/api/environments.ts index 1d5e88a7ca..79d76bc557 100644 --- a/solutions/swb-ui/src/api/environments.ts +++ b/solutions/swb-ui/src/api/environments.ts @@ -3,17 +3,19 @@ * SPDX-License-Identifier: Apache-2.0 */ -import useSWR from 'swr'; +import { convertToRecord, httpApiGet, httpApiPut, httpApiPost } from '@aws/workbench-core-swb-common-ui'; +import useSWR, { KeyedMutator } from 'swr'; import { EnvironmentItem, EnvironmentConnectResponse, CreateEnvironmentForm, EnvironmentsQueryParams } from '../models/Environment'; -import { httpApiGet, httpApiPut, httpApiPost } from './apiHelper'; -import { convertToRecord } from '../common/utils'; -const useEnvironments = (params?: EnvironmentsQueryParams) => { +const useEnvironments = (params?: EnvironmentsQueryParams): +// eslint-disable-next-line @typescript-eslint/no-explicit-any +{ environments: {workspaceName: string, workspaceStatus: string, project: string}[], mutate: KeyedMutator, +paginationToken: string, areEnvironmentsLoading: boolean } => { let queryString = new URLSearchParams(convertToRecord(params)).toString(); queryString = queryString ? `?${queryString}` : ''; const { data, mutate, isValidating } = useSWR(`environments${queryString}`, httpApiGet); @@ -21,7 +23,7 @@ const useEnvironments = (params?: EnvironmentsQueryParams) => { // `/environments` API returns a JSON in this format // { data: [], paginationToken: ''} // The paginationToken attribute is only provided if there are more than one page of result - const environments = (data && data.data) || []; + const environments = data?.data ?? []; environments.forEach((item: EnvironmentItem) => { item.workspaceName = item.name; item.workspaceStatus = item.status; diff --git a/solutions/swb-ui/src/api/projects.ts b/solutions/swb-ui/src/api/projects.ts index a1e59ba4b4..f8ac0e77a4 100644 --- a/solutions/swb-ui/src/api/projects.ts +++ b/solutions/swb-ui/src/api/projects.ts @@ -3,13 +3,13 @@ * SPDX-License-Identifier: Apache-2.0 */ +import { httpApiGet } from '@aws/workbench-core-swb-common-ui'; import useSWR from 'swr'; -import { httpApiGet } from './apiHelper'; import { ProjectItem } from '../models/Project'; -const useProjects = () => { +const useProjects = (): { projects: ProjectItem[], areProjectsLoading: boolean } => { const { data, isValidating } = useSWR(() => 'projects', httpApiGet); - const projects: ProjectItem[] = (data && data.data) || []; + const projects: ProjectItem[] = data?.data ?? []; return { projects, areProjectsLoading: isValidating }; }; diff --git a/solutions/swb-ui/src/common/splitPanel.tsx b/solutions/swb-ui/src/common/splitPanel.tsx deleted file mode 100644 index 428cc7df81..0000000000 --- a/solutions/swb-ui/src/common/splitPanel.tsx +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -import { AppLayoutProps, BarChart, Box, Button, SplitPanelProps } from '@cloudscape-design/components'; -import { useEffect, useState } from 'react'; - -export const splitPaneli18nstrings: SplitPanelProps.I18nStrings = { - preferencesTitle: 'Split panel preferences', - preferencesPositionLabel: 'Split panel position', - preferencesPositionDescription: 'Choose the default split panel position for the service.', - preferencesPositionSide: 'Side', - preferencesPositionBottom: 'Bottom', - preferencesConfirm: 'Confirm', - preferencesCancel: 'Cancel', - closeButtonAriaLabel: 'Close panel', - openButtonAriaLabel: 'Open panel', - resizeHandleAriaLabel: 'Resize split panel' -}; - -export const getPanelContent = (items: any, itemType: string): any => { - if (!items.length) { - return { - header: `0 ${itemType}s selected`, - body: `Select a ${itemType} to see its details` - }; - } - - const item = items[0]; - - // All data returned is dummy data for POC - return { - header: 'Cost', - body: ( - '$' + e.toLocaleString('en-US') - }, - { - title: 'Average cost', - type: 'threshold', - y: 87, - valueFormatter: (e) => '$' + e.toLocaleString('en-US') - } - ]} - xDomain={[ - new Date(1601096400000), - new Date(1601103600000), - new Date(1601110800000), - new Date(1601118000000), - new Date(1601125200000), - new Date(1601125200000), - new Date(1601125200000) - ]} - yDomain={[0, 400]} - i18nStrings={{ - filterLabel: 'Filter displayed data', - filterPlaceholder: 'Filter data', - filterSelectedAriaLabel: 'selected', - legendAriaLabel: 'Legend', - chartAriaRoleDescription: 'line chart', - xTickFormatter: (e) => - e - .toLocaleDateString('en-US', { - month: 'short', - day: 'numeric', - hour: 'numeric', - minute: 'numeric', - hour12: !1 - }) - .split(',') - .join('\n'), - yTickFormatter: undefined - }} - ariaLabel="Single data series line chart" - errorText="Error loading data." - height={300} - loadingText="Loading chart" - recoveryText="Retry" - xScaleType="categorical" - xTitle="Time (UTC)" - yTitle="Revenue (USD)" - empty={ - - No data available - - There is no data available - - - } - noMatch={ - - No matching data - - There is no matching data to display - - - - } - /> - ) - }; -}; - -export const useSplitPanel = (selectedItems: any): AppLayoutProps => { - const [splitPanelSize, setSplitPanelSize] = useState(650); - const [splitPanelOpen, setSplitPanelOpen] = useState(false); - const [hasManuallyClosedOnce, setHasManuallyClosedOnce] = useState(false); - - const onSplitPanelResize = ({ detail: { size } }: any): void => { - setSplitPanelSize(size); - }; - - const onSplitPanelToggle = ({ detail: { open } }: any): void => { - setSplitPanelOpen(open); - - if (!open) { - setHasManuallyClosedOnce(true); - } - }; - - useEffect(() => { - if (selectedItems.length && !hasManuallyClosedOnce) { - setSplitPanelOpen(true); - } - }, [selectedItems.length, hasManuallyClosedOnce]); - - return { - splitPanelOpen, - onSplitPanelToggle, - splitPanelSize, - onSplitPanelResize - }; -}; diff --git a/solutions/swb-ui/src/components/EnvTypeCards.tsx b/solutions/swb-ui/src/components/EnvTypeCards.tsx index 6233db33ad..10bdad959e 100644 --- a/solutions/swb-ui/src/components/EnvTypeCards.tsx +++ b/solutions/swb-ui/src/components/EnvTypeCards.tsx @@ -3,6 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ +import { TableEmptyDisplay, TableNoMatchDisplay } from '@aws/workbench-core-swb-common-ui'; import { useCollection } from '@cloudscape-design/collection-hooks'; import { CollectionPreferences, @@ -14,8 +15,6 @@ import { TextContent } from '@cloudscape-design/components'; import { useState } from 'react'; -import { TableEmptyDisplay } from '../common/tableEmptyState'; -import { TableNoMatchDisplay } from '../common/tableNoMatchState'; import { EnvTypeItem } from '../models/EnvironmentType'; interface OnSelectEnvTypeFunction { diff --git a/solutions/swb-ui/src/components/EnvironmentConnectModal.tsx b/solutions/swb-ui/src/components/EnvironmentConnectModal.tsx index 29ee74f92d..c9dac17da6 100644 --- a/solutions/swb-ui/src/components/EnvironmentConnectModal.tsx +++ b/solutions/swb-ui/src/components/EnvironmentConnectModal.tsx @@ -4,10 +4,10 @@ */ import { EnvironmentConnectionLinkPlaceholder } from '@aws/workbench-core-environments'; +import { useNotifications } from '@aws/workbench-core-swb-common-ui'; import { Modal, SpaceBetween, Link } from '@cloudscape-design/components'; import Box from '@cloudscape-design/components/box'; import Button from '@cloudscape-design/components/button'; -import { useNotifications } from '../context/NotificationContext'; interface EnvironmentConnectModalProps { closeModal: () => void; diff --git a/solutions/swb-ui/src/models/Project.ts b/solutions/swb-ui/src/models/Project.ts index 6f91ab8d58..d79f6f2fe5 100644 --- a/solutions/swb-ui/src/models/Project.ts +++ b/solutions/swb-ui/src/models/Project.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -export type ProjectItem = { +export interface ProjectItem { id: string; name: string; -}; +} diff --git a/solutions/swb-ui/src/pages/_app.tsx b/solutions/swb-ui/src/pages/_app.tsx index 6006bd4562..00950508c1 100644 --- a/solutions/swb-ui/src/pages/_app.tsx +++ b/solutions/swb-ui/src/pages/_app.tsx @@ -6,20 +6,18 @@ // No BaseLayout for the global _app.tsx because login page should have no nav import '@cloudscape-design/global-styles/index.css'; import '../styles/globals.scss'; +import '../styles/Header.module.scss'; +import '../styles/Hero.module.scss'; +import { AuthenticationProvider, NotificationsProvider, SettingsProvider, Header } from '@aws/workbench-core-swb-common-ui'; import type { AppProps } from 'next/app'; -import Header from '../components/Header'; -import { AuthenticationProvider } from '../context/AuthenticationContext'; -import { NotificationsProvider } from '../context/NotificationContext'; -import { SettingsProvider } from '../context/SettingsContext'; -// eslint-disable-next-line @typescript-eslint/naming-convention function App({ Component, pageProps }: AppProps): JSX.Element { return (
- +
diff --git a/solutions/swb-ui/src/pages/environments/index.tsx b/solutions/swb-ui/src/pages/environments/index.tsx index 25e34f8a2f..e445afb1dc 100644 --- a/solutions/swb-ui/src/pages/environments/index.tsx +++ b/solutions/swb-ui/src/pages/environments/index.tsx @@ -3,6 +3,19 @@ * SPDX-License-Identifier: Apache-2.0 */ +import { + datei18nStrings, + relativeOptions, + convertToAbsoluteRange, + isValidRangeFunction, + getFilterCounterText, + TableEmptyDisplay, + TableNoMatchDisplay, + i18nStrings, + paginationLables, + useNotifications, + BaseLayout +} from '@aws/workbench-core-swb-common-ui'; import { useCollection } from '@cloudscape-design/collection-hooks'; import { Box, @@ -25,16 +38,7 @@ import type { NextPage } from 'next'; import { useRouter } from 'next/router'; import React, { SetStateAction, useEffect, useState } from 'react'; import { useEnvironments, terminate, start, stop, connect } from '../../api/environments'; -import { datei18nStrings, relativeOptions } from '../../common/dateRelativeOptions'; -import { convertToAbsoluteRange, isValidRangeFunction } from '../../common/dateRelativeProperties'; -import { i18nStrings, paginationLables } from '../../common/labels'; - -import { getFilterCounterText } from '../../common/tableCounterStrings'; -import { TableEmptyDisplay } from '../../common/tableEmptyState'; -import { TableNoMatchDisplay } from '../../common/tableNoMatchState'; -import BaseLayout from '../../components/BaseLayout'; import EnvironmentConnectModal from '../../components/EnvironmentConnectModal'; -import { useNotifications } from '../../context/NotificationContext'; import { columnDefinitions, searchableColumns diff --git a/solutions/swb-ui/src/pages/environments/new.tsx b/solutions/swb-ui/src/pages/environments/new.tsx index bd2f6f4bf0..4872dc7e2f 100644 --- a/solutions/swb-ui/src/pages/environments/new.tsx +++ b/solutions/swb-ui/src/pages/environments/new.tsx @@ -3,6 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ +import { nameRegex, BaseLayout } from '@aws/workbench-core-swb-common-ui'; import { Box, BreadcrumbGroupProps, @@ -27,8 +28,6 @@ import { createEnvironment } from '../../api/environments'; import { useEnvTypeConfigs } from '../../api/environmentTypeConfigs'; import { useEnvironmentType } from '../../api/environmentTypes'; import { useProjects } from '../../api/projects'; -import { nameRegex } from '../../common/utils'; -import BaseLayout from '../../components/BaseLayout'; import EnvTypeCards from '../../components/EnvTypeCards'; import EnvTypeConfigCards from '../../components/EnvTypeConfigCards'; import { CreateEnvironmentForm, CreateEnvironmentFormValidation } from '../../models/Environment'; diff --git a/solutions/swb-ui/src/pages/index.tsx b/solutions/swb-ui/src/pages/index.tsx index 338b77dbf4..a295dbdaa4 100644 --- a/solutions/swb-ui/src/pages/index.tsx +++ b/solutions/swb-ui/src/pages/index.tsx @@ -3,11 +3,10 @@ * SPDX-License-Identifier: Apache-2.0 */ +import { BaseLayout, Hero } from '@aws/workbench-core-swb-common-ui'; import { BreadcrumbGroupProps } from '@cloudscape-design/components'; import Box from '@cloudscape-design/components/box'; import type { NextPage } from 'next'; -import BaseLayout from '../components/BaseLayout'; -import Hero from '../components/Hero'; // Login Page const Home: NextPage = () => { diff --git a/solutions/swb-ui/src/pages/users/index.tsx b/solutions/swb-ui/src/pages/users/index.tsx index 7b1eed638b..22c6d10b87 100644 --- a/solutions/swb-ui/src/pages/users/index.tsx +++ b/solutions/swb-ui/src/pages/users/index.tsx @@ -4,6 +4,7 @@ */ /* eslint-disable @typescript-eslint/no-unused-vars */ +import { useUsers, BaseLayout, useSettings } from '@aws/workbench-core-swb-common-ui'; import { Box, BreadcrumbGroupProps, @@ -14,11 +15,7 @@ import { StatusIndicator } from '@cloudscape-design/components'; import type { NextPage } from 'next'; -import Head from 'next/head'; -import React, { useEffect, useState } from 'react'; -import { useUsers } from '../../api/users'; -import BaseLayout from '../../components/BaseLayout'; -import { useSettings } from '../../context/SettingsContext'; +import React, { useState } from 'react'; import { columnDefinitions } from '../../users-table-config/usersColumnDefinitions'; export interface UserProps { diff --git a/solutions/swb-ui/src/pages/users/new.tsx b/solutions/swb-ui/src/pages/users/new.tsx index 0ddbf29ca2..a1fe870c91 100644 --- a/solutions/swb-ui/src/pages/users/new.tsx +++ b/solutions/swb-ui/src/pages/users/new.tsx @@ -3,6 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ +import { emailRegex, nameRegex, addUserToRole, createUser, CreateUserForm, CreateUserFormValidation, BaseLayout } from '@aws/workbench-core-swb-common-ui'; import { Box, BreadcrumbGroupProps, @@ -17,10 +18,6 @@ import { import type { NextPage } from 'next'; import { useRouter } from 'next/router'; import React, { useEffect, useState } from 'react'; -import { addUserToRole, createUser } from '../../api/users'; -import { emailRegex, nameRegex } from '../../common/utils'; -import BaseLayout from '../../components/BaseLayout'; -import { CreateUserForm, CreateUserFormValidation } from '../../models/User'; export interface UserProps { locale: string; diff --git a/workbench-core/audit/package.json b/workbench-core/audit/package.json index 87a535e5e0..2329922c7c 100644 --- a/workbench-core/audit/package.json +++ b/workbench-core/audit/package.json @@ -34,10 +34,10 @@ }, "devDependencies": { "@aws/eslint-config-workbench-core-eslint-custom": "workspace:*", - "@rushstack/eslint-config": "^2.5.1", - "@rushstack/heft": "^0.45.0", - "@rushstack/heft-jest-plugin": "^0.2.3", - "@rushstack/heft-node-rig": "^1.7.1", + "@rushstack/eslint-config": "^3.0.0", + "@rushstack/heft": "^0.47.5", + "@rushstack/heft-jest-plugin": "^0.3.28", + "@rushstack/heft-node-rig": "^1.10.5", "@types/express": "^4.17.13", "@types/heft-jest": "1.0.2", "@types/lodash": "^4.14.181", diff --git a/workbench-core/authentication/package.json b/workbench-core/authentication/package.json index f06492d4f4..6dfeb589f1 100644 --- a/workbench-core/authentication/package.json +++ b/workbench-core/authentication/package.json @@ -42,10 +42,10 @@ "@aws-sdk/client-s3": "^3.118.1", "@aws-sdk/types": "^3.110.0", "@aws/eslint-config-workbench-core-eslint-custom": "workspace:*", - "@rushstack/eslint-config": "^2.5.1", - "@rushstack/heft": "^0.45.0", - "@rushstack/heft-jest-plugin": "^0.2.3", - "@rushstack/heft-node-rig": "^1.7.1", + "@rushstack/eslint-config": "^3.0.0", + "@rushstack/heft": "^0.47.5", + "@rushstack/heft-jest-plugin": "^0.3.28", + "@rushstack/heft-node-rig": "^1.10.5", "@types/csurf": "^1.11.2", "@types/express": "^4.17.13", "@types/heft-jest": "1.0.2", diff --git a/workbench-core/authorization/package.json b/workbench-core/authorization/package.json index 21dfb1c9aa..fcdef4b2ff 100644 --- a/workbench-core/authorization/package.json +++ b/workbench-core/authorization/package.json @@ -35,10 +35,10 @@ }, "devDependencies": { "@aws/eslint-config-workbench-core-eslint-custom": "workspace:*", - "@rushstack/eslint-config": "^2.5.1", - "@rushstack/heft": "^0.45.0", - "@rushstack/heft-jest-plugin": "^0.2.3", - "@rushstack/heft-node-rig": "^1.7.1", + "@rushstack/eslint-config": "^3.0.0", + "@rushstack/heft": "^0.47.5", + "@rushstack/heft-jest-plugin": "^0.3.28", + "@rushstack/heft-node-rig": "^1.10.5", "@types/express": "^4.17.13", "@types/heft-jest": "1.0.2", "@types/lodash": "^4.14.181", diff --git a/workbench-core/base/package.json b/workbench-core/base/package.json index 801317d9f4..2725ccc297 100644 --- a/workbench-core/base/package.json +++ b/workbench-core/base/package.json @@ -63,10 +63,10 @@ }, "devDependencies": { "@aws/eslint-config-workbench-core-eslint-custom": "workspace:*", - "@rushstack/eslint-config": "^2.5.1", - "@rushstack/heft": "^0.45.0", - "@rushstack/heft-jest-plugin": "^0.2.3", - "@rushstack/heft-node-rig": "^1.7.1", + "@rushstack/eslint-config": "^3.0.0", + "@rushstack/heft": "^0.47.5", + "@rushstack/heft-jest-plugin": "^0.3.28", + "@rushstack/heft-node-rig": "^1.10.5", "@types/heft-jest": "1.0.2", "@types/jest": "^27.4.1", "@types/js-yaml": "^4.0.5", diff --git a/workbench-core/datasets/package.json b/workbench-core/datasets/package.json index 07fb414443..1d20bb0427 100644 --- a/workbench-core/datasets/package.json +++ b/workbench-core/datasets/package.json @@ -55,10 +55,10 @@ }, "devDependencies": { "@aws/eslint-config-workbench-core-eslint-custom": "workspace:*", - "@rushstack/eslint-config": "^2.5.1", - "@rushstack/heft": "^0.45.0", - "@rushstack/heft-jest-plugin": "^0.2.3", - "@rushstack/heft-node-rig": "^1.7.1", + "@rushstack/eslint-config": "^3.0.0", + "@rushstack/heft": "^0.47.5", + "@rushstack/heft-jest-plugin": "^0.3.28", + "@rushstack/heft-node-rig": "^1.10.5", "@types/heft-jest": "1.0.2", "@types/lodash": "^4.14.181", "@types/node": "^14", diff --git a/workbench-core/environments/package.json b/workbench-core/environments/package.json index 0e506671b5..183a04457a 100644 --- a/workbench-core/environments/package.json +++ b/workbench-core/environments/package.json @@ -68,10 +68,10 @@ "devDependencies": { "@aws-sdk/types": "^3.110.0", "@aws/eslint-config-workbench-core-eslint-custom": "workspace:*", - "@rushstack/eslint-config": "^2.5.1", - "@rushstack/heft": "^0.45.0", - "@rushstack/heft-jest-plugin": "^0.2.3", - "@rushstack/heft-node-rig": "^1.7.1", + "@rushstack/eslint-config": "^3.0.0", + "@rushstack/heft": "^0.47.5", + "@rushstack/heft-jest-plugin": "^0.3.28", + "@rushstack/heft-node-rig": "^1.10.5", "@types/heft-jest": "1.0.2", "@types/jest": "^27.4.1", "@types/lodash": "^4.14.181", diff --git a/workbench-core/eslint-custom/package.json b/workbench-core/eslint-custom/package.json index 84c0cdfa3d..4c9540c08a 100644 --- a/workbench-core/eslint-custom/package.json +++ b/workbench-core/eslint-custom/package.json @@ -29,15 +29,15 @@ "test:only": "heft test --clean --no-build" }, "dependencies": { - "@rushstack/eslint-config": "^2.5.1", + "@rushstack/eslint-config": "^3.0.0", "eslint": "^8.7.0", "eslint-plugin-import": "^2.26.0", "eslint-plugin-security": "^1.4.0" }, "devDependencies": { - "@rushstack/heft": "^0.45.0", - "@rushstack/heft-jest-plugin": "^0.2.3", - "@rushstack/heft-node-rig": "^1.7.1", + "@rushstack/heft": "^0.47.5", + "@rushstack/heft-jest-plugin": "^0.3.28", + "@rushstack/heft-node-rig": "^1.10.5", "@types/heft-jest": "1.0.2", "@types/node": "^14", "depcheck": "^1.4.3", diff --git a/workbench-core/example/express/package.json b/workbench-core/example/express/package.json index 96745c268f..81fc0bffa9 100644 --- a/workbench-core/example/express/package.json +++ b/workbench-core/example/express/package.json @@ -35,10 +35,10 @@ }, "devDependencies": { "@aws/eslint-config-workbench-core-eslint-custom": "workspace:*", - "@rushstack/eslint-config": "^2.5.1", - "@rushstack/heft": "^0.45.0", - "@rushstack/heft-jest-plugin": "^0.2.3", - "@rushstack/heft-node-rig": "^1.7.1", + "@rushstack/eslint-config": "^3.0.0", + "@rushstack/heft": "^0.47.5", + "@rushstack/heft-jest-plugin": "^0.3.28", + "@rushstack/heft-node-rig": "^1.10.5", "@types/express": "^4.17.13", "@types/heft-jest": "1.0.2", "@types/node": "^14", diff --git a/workbench-core/example/infrastructure/package.json b/workbench-core/example/infrastructure/package.json index 7d888b81f6..eec739e25b 100644 --- a/workbench-core/example/infrastructure/package.json +++ b/workbench-core/example/infrastructure/package.json @@ -48,10 +48,10 @@ "@aws-cdk/aws-apigatewayv2-authorizers-alpha": "^2.22.0-alpha.0", "@aws-cdk/aws-apigatewayv2-integrations-alpha": "^2.22.0-alpha.0", "@aws/eslint-config-workbench-core-eslint-custom": "workspace:*", - "@rushstack/eslint-config": "^2.5.1", - "@rushstack/heft": "^0.45.0", - "@rushstack/heft-jest-plugin": "^0.2.3", - "@rushstack/heft-node-rig": "^1.7.1", + "@rushstack/eslint-config": "^3.0.0", + "@rushstack/heft": "^0.47.5", + "@rushstack/heft-jest-plugin": "^0.3.28", + "@rushstack/heft-node-rig": "^1.10.5", "@types/express": "^4.17.13", "@types/heft-jest": "1.0.2", "@types/lodash": "^4.14.181", diff --git a/workbench-core/infrastructure/package.json b/workbench-core/infrastructure/package.json index a32cebdc9d..23a185eb18 100644 --- a/workbench-core/infrastructure/package.json +++ b/workbench-core/infrastructure/package.json @@ -35,10 +35,10 @@ }, "devDependencies": { "@aws/eslint-config-workbench-core-eslint-custom": "workspace:*", - "@rushstack/eslint-config": "^2.5.1", - "@rushstack/heft": "^0.45.0", - "@rushstack/heft-jest-plugin": "^0.2.3", - "@rushstack/heft-node-rig": "^1.7.1", + "@rushstack/eslint-config": "^3.0.0", + "@rushstack/heft": "^0.47.5", + "@rushstack/heft-jest-plugin": "^0.3.28", + "@rushstack/heft-node-rig": "^1.10.5", "@types/heft-jest": "1.0.2", "@types/lodash": "^4.14.181", "@types/node": "^14", diff --git a/workbench-core/logging/package.json b/workbench-core/logging/package.json index a23c0fd1e0..116397d0eb 100644 --- a/workbench-core/logging/package.json +++ b/workbench-core/logging/package.json @@ -37,9 +37,9 @@ }, "devDependencies": { "@aws/eslint-config-workbench-core-eslint-custom": "workspace:*", - "@rushstack/heft": "^0.45.0", - "@rushstack/heft-jest-plugin": "^0.2.3", - "@rushstack/heft-node-rig": "^1.7.1", + "@rushstack/heft": "^0.47.5", + "@rushstack/heft-jest-plugin": "^0.3.28", + "@rushstack/heft-node-rig": "^1.10.5", "@types/heft-jest": "1.0.2", "@types/node": "^14", "depcheck": "^1.4.3", diff --git a/workbench-core/repo-scripts/repo-toolbox/package.json b/workbench-core/repo-scripts/repo-toolbox/package.json index e20fd68140..81ba7461bb 100644 --- a/workbench-core/repo-scripts/repo-toolbox/package.json +++ b/workbench-core/repo-scripts/repo-toolbox/package.json @@ -37,10 +37,10 @@ }, "devDependencies": { "@aws/eslint-config-workbench-core-eslint-custom": "workspace:*", - "@rushstack/eslint-config": "^2.5.1", - "@rushstack/heft": "^0.45.0", - "@rushstack/heft-jest-plugin": "^0.2.3", - "@rushstack/heft-node-rig": "^1.7.1", + "@rushstack/eslint-config": "^3.0.0", + "@rushstack/heft": "^0.47.5", + "@rushstack/heft-jest-plugin": "^0.3.28", + "@rushstack/heft-node-rig": "^1.10.5", "@types/diff": "5.0.1", "@types/heft-jest": "1.0.2", "@types/node": "^14", diff --git a/workbench-core/swb-common-ui/.depcheckrc b/workbench-core/swb-common-ui/.depcheckrc new file mode 100644 index 0000000000..c15dbb0037 --- /dev/null +++ b/workbench-core/swb-common-ui/.depcheckrc @@ -0,0 +1,20 @@ +ignores: [ + '@aws/eslint-config-workbench-core-eslint-custom', + '@rushstack/heft-jest-plugin', + '@rushstack/heft-node-rig', + '@types/heft-jest', + 'eslint-plugin-import', + 'eslint-plugin-security', + 'npm-package-json-lint-config-default', + '@types/node' +] +ignore-patterns: [ + // files matching these patterns will be ignored + '.heft', + '.rush', + 'config', + 'lib', + 'node_modules', + 'temp', + ] +skip-missing: true \ No newline at end of file diff --git a/workbench-core/swb-common-ui/.eslintignore b/workbench-core/swb-common-ui/.eslintignore new file mode 100644 index 0000000000..2669727c91 --- /dev/null +++ b/workbench-core/swb-common-ui/.eslintignore @@ -0,0 +1,4 @@ +.eslintrc.js +node_modules +lib +temp \ No newline at end of file diff --git a/workbench-core/swb-common-ui/.eslintrc.js b/workbench-core/swb-common-ui/.eslintrc.js new file mode 100644 index 0000000000..9eef2104b2 --- /dev/null +++ b/workbench-core/swb-common-ui/.eslintrc.js @@ -0,0 +1,17 @@ +// This is a workaround for https://github.com/eslint/eslint/issues/3458 +require('@rushstack/eslint-config/patch/modern-module-resolution'); + +module.exports = { + extends: ['@aws/eslint-config-workbench-core-eslint-custom', 'next/core-web-vitals'], + parserOptions: { tsconfigRootDir: __dirname }, + rules: { + // these rules are because the @cloudscape-design/components package doesnt export *Props interfaces in index.js files. + // This causes the eslint errors below + 'import/default': 0, + 'import/namespace': 0, + 'import/no-named-as-default': 0, + 'import/no-named-as-default-member': 0, + // disabling this rule because next expects a pages directory which we dont have here + '@next/next/no-html-link-for-pages': 0 + } +}; diff --git a/workbench-core/swb-common-ui/.npmignore b/workbench-core/swb-common-ui/.npmignore new file mode 100644 index 0000000000..87a8491edc --- /dev/null +++ b/workbench-core/swb-common-ui/.npmignore @@ -0,0 +1,33 @@ +# THIS IS A STANDARD TEMPLATE FOR .npmignore FILES IN THIS REPO. + +# Ignore all files by default, to avoid accidentally publishing unintended files. +* + +# Use negative patterns to bring back the specific things we want to publish. +!/bin/** +!/lib/** +!/lib-*/** +!/dist/** +!ThirdPartyNotice.txt +!NOTICE + +# Ignore certain patterns that should not get published. +/dist/*.stats.* +/lib/**/test/ +/lib-*/**/test/ +/lib/**/__mocks__/ +*.test.js +*.test.d.ts +*.test.d.ts.map +*.test.js.map + +# NOTE: These don't need to be specified, because NPM includes them automatically. +# +# package.json +# README (and its variants) +# CHANGELOG (and its variants) +# LICENSE / LICENCE + +#-------------------------------------------- +# DO NOT MODIFY THE TEMPLATE ABOVE THIS LINE +#-------------------------------------------- diff --git a/workbench-core/swb-common-ui/LICENSE b/workbench-core/swb-common-ui/LICENSE new file mode 100644 index 0000000000..2ea34327d4 --- /dev/null +++ b/workbench-core/swb-common-ui/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 2022 MA Foundation + + 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/workbench-core/swb-common-ui/NOTICE b/workbench-core/swb-common-ui/NOTICE new file mode 100644 index 0000000000..7226fb0719 --- /dev/null +++ b/workbench-core/swb-common-ui/NOTICE @@ -0,0 +1,4264 @@ +@aws/eslint-config-workbench-core-eslint-custom +0.0.1 +license: Apache-2.0 +authors: Amazon Web Services + +****************************** + +@aws/workbench-core-audit +0.1.1 +license: Apache-2.0 +authors: Amazon Web Services + +****************************** + +@aws/workbench-core-authorization +0.1.1 +license: Apache-2.0 +authors: Amazon Web Services + +****************************** + +@aws/workbench-core-base +0.1.2 +license: Apache-2.0 +authors: Amazon Web Services + +****************************** + +@aws/workbench-core-logging +0.1.1 +license: Apache-2.0 +authors: Amazon Web Services + +****************************** + +@aws-cdk/aws-dynamodb +1.158.0 + 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. + + +****************************** + +@aws-cdk/aws-iam +1.159.0 + 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. + + +****************************** + +@aws-sdk/client-cloudformation +3.130.0 + 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-2020 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. + + +****************************** + +@aws-sdk/client-cognito-identity-provider +3.130.0 + 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-2020 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. + + +****************************** + +@aws-sdk/client-dynamodb +3.130.0 + 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-2020 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. + + +****************************** + +@aws-sdk/client-ec2 +3.130.0 + 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-2020 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. + + +****************************** + +@aws-sdk/client-eventbridge +3.130.0 + 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-2020 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. + + +****************************** + +@aws-sdk/client-iam +3.130.0 + 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-2020 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. + + +****************************** + +@aws-sdk/client-kms +3.130.0 + 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-2020 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. + + +****************************** + +@aws-sdk/client-lambda +3.105.0 + 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-2020 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. + + +****************************** + +@aws-sdk/client-s3 +3.130.0 + 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-2020 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. + + +****************************** + +@aws-sdk/client-s3-control +3.130.0 + 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-2020 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. + + +****************************** + +@aws-sdk/client-sagemaker +3.130.0 + 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-2020 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. + + +****************************** + +@aws-sdk/client-service-catalog +3.130.0 + 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-2020 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. + + +****************************** + +@aws-sdk/client-ssm +3.130.0 + 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-2020 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. + + +****************************** + +@aws-sdk/client-sts +3.130.0 + 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-2020 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. + + +****************************** + +@aws-sdk/types +3.127.0 +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-2020 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. + +****************************** + +@aws-sdk/util-dynamodb +3.105.0 +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-2020 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. + +****************************** + +@casl/ability +5.4.4 +MIT License + +Copyright (c) 2017-2018 Sergii Stotskyi + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +****************************** + +@hapi/boom +10.0.0 +Copyright (c) 2012-2022, Project contributors +Copyright (c) 2012-2020, Sideway Inc +Copyright (c) 2012-2014, Walmart. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: +* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +* The names of any contributors may not be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS OFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +****************************** + +@rushstack/eslint-config +2.6.0 +@rushstack/eslint-config + +Copyright (c) Microsoft Corporation. All rights reserved. + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +****************************** + +eslint +8.16.0 +Copyright OpenJS Foundation and other contributors, + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +****************************** + +eslint-plugin-import +2.26.0 +The MIT License (MIT) + +Copyright (c) 2015 Ben Mosher + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + + +****************************** + +eslint-plugin-security +1.5.0 + + 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 2015 &yet, LLC + + 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. + + +****************************** + +jsonschema +1.4.1 +jsonschema is licensed under MIT license. + +Copyright (C) 2012-2015 Tom de Grunt + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +****************************** + +lodash +4.17.21 +Copyright OpenJS Foundation and other contributors + +Based on Underscore.js, copyright Jeremy Ashkenas, +DocumentCloud and Investigative Reporters & Editors + +This software consists of voluntary contributions made by many +individuals. For exact contribution history, see the revision history +available at https://github.com/lodash/lodash + +The following license applies to all parts of this software except as +documented below: + +==== + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +==== + +Copyright and related rights for sample code are waived via CC0. Sample +code is defined as all source code displayed within the prose of the +documentation. + +CC0: http://creativecommons.org/publicdomain/zero/1.0/ + +==== + +Files located in the node_modules and vendor directories are externally +maintained libraries used by this software which have their own +licenses; we recommend you read them, as their terms may differ from the +terms above. + + +****************************** + +triple-beam +1.3.0 +MIT License + +Copyright (c) 2017 winstonjs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +****************************** + +uuid +8.3.2 +The MIT License (MIT) + +Copyright (c) 2010-2020 Robert Kieffer and other contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +****************************** + +winston +3.7.2 +Copyright (c) 2010 Charlie Robbins + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +****************************** + +winston-transport +4.5.0 +The MIT License (MIT) + +Copyright (c) 2015 Charlie Robbins & the contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/workbench-core/swb-common-ui/README.md b/workbench-core/swb-common-ui/README.md new file mode 100644 index 0000000000..508d63f3f0 --- /dev/null +++ b/workbench-core/swb-common-ui/README.md @@ -0,0 +1,9 @@ +## Code Coverage +| Statements | Branches | Functions | Lines | +| --------------------------- | ----------------------- | ------------------------- | ----------------- | +| ![Statements](https://img.shields.io/badge/statements-Unknown%25-brightgreen.svg?style=flat) | ![Branches](https://img.shields.io/badge/branches-Unknown%25-brightgreen.svg?style=flat) | ![Functions](https://img.shields.io/badge/functions-Unknown%25-brightgreen.svg?style=flat) | ![Lines](https://img.shields.io/badge/lines-Unknown%25-brightgreen.svg?style=flat) | + + +- swb-common-ui (BaseLayout) -> swb-ui +- swb-common-ui (BaseLayout) -> datasets-ui (needs BaseLayout) -> swb-ui +- swb-common-ui (BaseLayout) -> newFeature-ui -> swb-ui \ No newline at end of file diff --git a/workbench-core/swb-common-ui/config/heft.json b/workbench-core/swb-common-ui/config/heft.json new file mode 100644 index 0000000000..75892c1242 --- /dev/null +++ b/workbench-core/swb-common-ui/config/heft.json @@ -0,0 +1,51 @@ +/** + * Defines configuration used by core Heft. + */ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/heft.schema.json", + + "eventActions": [ + { + /** + * The kind of built-in operation that should be performed. + * The "deleteGlobs" action deletes files or folders that match the + * specified glob patterns. + */ + "actionKind": "deleteGlobs", + + /** + * The stage of the Heft run during which this action should occur. Note that actions specified in heft.json + * occur at the end of the stage of the Heft run. + */ + "heftEvent": "clean", + + /** + * A user-defined tag whose purpose is to allow configs to replace/delete handlers that were added by other + * configs. + */ + "actionId": "defaultClean", + + /** + * Glob patterns to be deleted. The paths are resolved relative to the project folder. + */ + "globsToDelete": ["dist", "lib", "temp"] + } + ], + + /** + * The list of Heft plugins to be loaded. + */ + "heftPlugins": [ + { + // /** + // * The path to the plugin package. + // */ + "plugin": "@rushstack/heft-jest-plugin" + // + // /** + // * An optional object that provides additional settings that may be defined by the plugin. + // */ + // // "options": { } + } + ] +} diff --git a/workbench-core/swb-common-ui/config/jest.config.json b/workbench-core/swb-common-ui/config/jest.config.json new file mode 100644 index 0000000000..cf5480d82f --- /dev/null +++ b/workbench-core/swb-common-ui/config/jest.config.json @@ -0,0 +1,14 @@ +{ + "extends": "@rushstack/heft-node-rig/profiles/default/config/jest.config.json", + "collectCoverage": true, + "coveragePathIgnorePatterns": ["/src/index.ts"], + "coverageThreshold": { + "global": { + "branches": 90, + "functions": 90, + "lines": 90, + "statements": 90 + } + }, + "coverageReporters": ["json-summary", "json", "lcov", "text"] +} diff --git a/workbench-core/swb-common-ui/config/rig.json b/workbench-core/swb-common-ui/config/rig.json new file mode 100644 index 0000000000..f6c7b5537d --- /dev/null +++ b/workbench-core/swb-common-ui/config/rig.json @@ -0,0 +1,18 @@ +// The "rig.json" file directs tools to look for their config files in an external package. +// Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package +{ + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + + /** + * (Required) The name of the rig package to inherit from. + * It should be an NPM package name with the "-rig" suffix. + */ + "rigPackageName": "@rushstack/heft-node-rig" + + /** + * (Optional) Selects a config profile from the rig package. The name must consist of + * lowercase alphanumeric words separated by hyphens, for example "sample-profile". + * If omitted, then the "default" profile will be used." + */ + // "rigProfile": "your-profile-name" +} diff --git a/workbench-core/swb-common-ui/license-add-config.json b/workbench-core/swb-common-ui/license-add-config.json new file mode 100644 index 0000000000..ed85c268a8 --- /dev/null +++ b/workbench-core/swb-common-ui/license-add-config.json @@ -0,0 +1,32 @@ +{ + "license": "../../apache-license-header.txt", + "ignoreFile": "../../.licenseignorefile", + "defaultFormat": { + "gitignore|npmignore|eslintignore|dockerignore|sh|py": { + "eachLine": { + "prepend": "# " + } + }, + "html|xml|svg": { + "prepend": "" + }, + "js|ts|tsx|css|scss|less|php|as|c|java|cpp|go|cto|acl": { + "prepend": "/*", + "append": " */\n", + "eachLine": { + "prepend": " * " + } + }, + "txt": {} + }, + "licenseFormats": { + "ts|tsx": { + "prepend": "/*", + "append": " */\n", + "eachLine": { + "prepend": " * " + } + } + } +} diff --git a/workbench-core/swb-common-ui/package.json b/workbench-core/swb-common-ui/package.json new file mode 100644 index 0000000000..8f564c0a6d --- /dev/null +++ b/workbench-core/swb-common-ui/package.json @@ -0,0 +1,67 @@ +{ + "name": "@aws/workbench-core-swb-common-ui", + "version": "0.0.1", + "description": "Access and management of data files for the purpose of connecting to various AWS environments.", + "homepage": "https://github.com/awslabs/solution-spark-on-aws", + "repository": { + "type": "git", + "url": "https://github.com/awslabs/solution-spark-on-aws.git" + }, + "license": "Apache-2.0", + "author": { + "name": "Amazon Web Services", + "url": "http://aws.amazon.com" + }, + "main": "lib/esm/index.js", + "module": "./lib/esm/index.js", + "typings": "lib/esm/index.d.ts", + "scripts": { + "add-license-header": "license-check-and-add add -f license-add-config.json", + "build": "heft build --clean && rushx pkg-json-lint", + "build:test": "heft test --clean && rushx pkg-json-lint && rushx make-badges", + "check-license-header": "license-check-and-add check -f license-add-config.json", + "depcheck": "depcheck", + "license-checker": "license-checker --onlyAllow 'MIT; Apache-2.0; ISC; BSD'", + "lint": "eslint . && rushx pkg-json-lint", + "lint:fix": "eslint . --fix", + "make-badges": "istanbul-badges-readme --coverageDir=./temp/coverage --exitCode=1", + "pkg-json-lint": "npmPkgJsonLint -c ../../.npmpackagejsonlintrc.json .", + "sort-package-json": "sort-package-json package.json", + "test": "rushx test:only && rushx make-badges", + "test:only": "heft test --clean --no-build" + }, + "dependencies": { + "axios": "^0.27.1", + "date-fns": "^2.28.0", + "jwt-decode": "^3.1.2", + "next": "^12.1.5", + "pkce-challenge": "^3.0.0", + "react": "17.0.2", + "react-dom": "17.0.2", + "swr": "^1.3.0", + "typescript": "^4.5.2", + "uuid": "^8.3.2" + }, + "devDependencies": { + "@aws/eslint-config-workbench-core-eslint-custom": "workspace:*", + "@cloudscape-design/components": "^3.0.27", + "@rushstack/eslint-config": "^3.0.0", + "@rushstack/heft": "^0.47.5", + "@rushstack/heft-jest-plugin": "^0.3.28", + "@rushstack/heft-node-rig": "^1.10.5", + "@types/heft-jest": "1.0.2", + "@types/node": "^14", + "@types/react": "17.0.38", + "@types/uuid": "^8.3.4", + "depcheck": "^1.4.3", + "eslint": "^8.7.0", + "eslint-plugin-import": "^2.26.0", + "istanbul-badges-readme": "1.8.1", + "license-check-and-add": "^4.0.5", + "license-checker": "^25.0.1", + "npm-package-json-lint": "^6.3.0", + "npm-package-json-lint-config-default": "^5.0.0", + "sass": "^1.49.8", + "sort-package-json": "^1.57.0" + } +} diff --git a/solutions/swb-ui/src/api/apiHelper.ts b/workbench-core/swb-common-ui/src/api/apiHelper.ts similarity index 76% rename from solutions/swb-ui/src/api/apiHelper.ts rename to workbench-core/swb-common-ui/src/api/apiHelper.ts index fed5560f35..a9a2735dcd 100644 --- a/solutions/swb-ui/src/api/apiHelper.ts +++ b/workbench-core/swb-common-ui/src/api/apiHelper.ts @@ -7,6 +7,25 @@ import axios, { AxiosRequestConfig } from 'axios'; const urlBase: string | undefined = process.env.NEXT_PUBLIC_API_BASE_URL; +// TODO: Use generics instead of using any for methods here + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const fetchData = async (options: AxiosRequestConfig): Promise => { + // add the CSRF header + const csrfToken = localStorage.getItem('csrfToken'); + if (csrfToken) { + options.headers = { 'csrf-token': csrfToken }; + } + //TODO add auth token and error handling + const { data } = await axios(options).catch(function (error: Error) { + console.log(error); + //TODO: call logger to capture exception + throw new Error('there was an error while trying to retrieve data'); + }); + return data; +}; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any const httpApiGet = async (urlPath: string, params: any, withCredentials: boolean = true): Promise => { const options = { method: 'GET', @@ -16,6 +35,8 @@ const httpApiGet = async (urlPath: string, params: any, withCredentials: boolean }; return await fetchData(options); }; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any const httpApiPost = async (urlPath: string, params: any, withCredentials: boolean = true): Promise => { const options = { method: 'POST', @@ -25,6 +46,8 @@ const httpApiPost = async (urlPath: string, params: any, withCredentials: boolea }; return await fetchData(options); }; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any const httpApiPut = async (urlPath: string, params: any, withCredentials: boolean = true): Promise => { const options = { method: 'PUT', @@ -34,6 +57,8 @@ const httpApiPut = async (urlPath: string, params: any, withCredentials: boolean }; return await fetchData(options); }; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any const httpApiDelete = async (urlPath: string, params: any, withCredentials: boolean = true): Promise => { const options = { method: 'DELETE', @@ -44,19 +69,4 @@ const httpApiDelete = async (urlPath: string, params: any, withCredentials: bool return await fetchData(options); }; -const fetchData = async (options: AxiosRequestConfig): Promise => { - // add the CSRF header - const csrfToken = localStorage.getItem('csrfToken'); - if (csrfToken) { - options.headers = { 'csrf-token': csrfToken }; - } - //TODO add auth token and error handling - const { data } = await axios(options).catch(function (error) { - console.log(error); - //TODO: call logger to capture exception - throw 'there was an error while trying to retrieve data'; - }); - return data; -}; - export { httpApiGet, httpApiPost, httpApiPut, httpApiDelete }; diff --git a/solutions/swb-ui/src/api/auth.ts b/workbench-core/swb-common-ui/src/api/auth.ts similarity index 67% rename from solutions/swb-ui/src/api/auth.ts rename to workbench-core/swb-common-ui/src/api/auth.ts index a2b795fca5..9c3347cc4e 100644 --- a/solutions/swb-ui/src/api/auth.ts +++ b/workbench-core/swb-common-ui/src/api/auth.ts @@ -5,18 +5,24 @@ import { httpApiGet, httpApiPost } from './apiHelper'; -const token = async (body: { code: string; codeVerifier: string | null }): Promise => { +// eslint-disable-next-line @rushstack/no-new-null +const token = async (body: { code: string; codeVerifier: string | null }): +// eslint-disable-next-line @typescript-eslint/no-explicit-any +Promise => { return await httpApiPost('token', body); }; +// eslint-disable-next-line @typescript-eslint/no-explicit-any const login = async (): Promise => { return await httpApiGet('login/?stateVerifier=TEMP_STATE_VERIFIER&codeChallenge=TEMP_CODE_CHALLENGE', {}); }; +// eslint-disable-next-line @typescript-eslint/no-explicit-any const logout = async (): Promise => { return await httpApiPost('logout', {}); }; +// eslint-disable-next-line @typescript-eslint/no-explicit-any const checkIfLoggedIn = async (): Promise => { return await httpApiGet('loggedIn', {}); }; diff --git a/solutions/swb-ui/src/api/users.ts b/workbench-core/swb-common-ui/src/api/users.ts similarity index 78% rename from solutions/swb-ui/src/api/users.ts rename to workbench-core/swb-common-ui/src/api/users.ts index f13d5bc69c..c704d93f97 100644 --- a/solutions/swb-ui/src/api/users.ts +++ b/workbench-core/swb-common-ui/src/api/users.ts @@ -16,10 +16,12 @@ const createUser = async (createUserForm: CreateUserForm): Promise => { await httpApiPost(`users`, { ...createUserForm }); }; -const useUsers = () => { +// TODO should users type/interface be defined in this package? +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const useUsers = (): any => { const { data, mutate } = useSWR('users', httpApiGet, {}); - const users = (data && data.users) || []; + const users = data?.users ?? []; return { users, mutate }; }; diff --git a/solutions/swb-ui/src/common/alerts.tsx b/workbench-core/swb-common-ui/src/common/alerts.tsx similarity index 95% rename from solutions/swb-ui/src/common/alerts.tsx rename to workbench-core/swb-common-ui/src/common/alerts.tsx index 5103957c6f..1f5538b387 100644 --- a/solutions/swb-ui/src/common/alerts.tsx +++ b/workbench-core/swb-common-ui/src/common/alerts.tsx @@ -4,7 +4,7 @@ */ import { Alert, AlertProps, Button } from '@cloudscape-design/components'; -import React from 'react'; +import * as React from 'react'; export function TerminateWarning( item: string, diff --git a/solutions/swb-ui/src/common/dateRelativeOptions.tsx b/workbench-core/swb-common-ui/src/common/dateRelativeOptions.tsx similarity index 100% rename from solutions/swb-ui/src/common/dateRelativeOptions.tsx rename to workbench-core/swb-common-ui/src/common/dateRelativeOptions.tsx diff --git a/solutions/swb-ui/src/common/dateRelativeProperties.tsx b/workbench-core/swb-common-ui/src/common/dateRelativeProperties.tsx similarity index 92% rename from solutions/swb-ui/src/common/dateRelativeProperties.tsx rename to workbench-core/swb-common-ui/src/common/dateRelativeProperties.tsx index 190f4d73dd..9a38d212bb 100644 --- a/solutions/swb-ui/src/common/dateRelativeProperties.tsx +++ b/workbench-core/swb-common-ui/src/common/dateRelativeProperties.tsx @@ -33,6 +33,8 @@ const lengthInDays = (unit: DateRangePickerProps.TimeUnit, amount: number): numb } }; +// TODO: Remove any and replace with a type +// eslint-disable-next-line @typescript-eslint/no-explicit-any export const isValidRangeFunction = (range: any): DateRangePickerProps.ValidationResult => { if (range.type === 'absolute') { const [startDateWithoutTime] = range.startDate.split('T'); @@ -83,7 +85,7 @@ export const isValidRangeFunction = (range: any): DateRangePickerProps.Validatio return { valid: true }; }; -export function convertToAbsoluteRange(range: any): { start: Date | undefined; end: Date } { +export function convertToAbsoluteRange(range: DateRangePickerProps.RelativeValue | DateRangePickerProps.AbsoluteValue): { start: Date | undefined; end: Date } { if (range.type === 'absolute') { return { start: new Date(range.startDate), diff --git a/solutions/swb-ui/src/common/labels.tsx b/workbench-core/swb-common-ui/src/common/labels.tsx similarity index 94% rename from solutions/swb-ui/src/common/labels.tsx rename to workbench-core/swb-common-ui/src/common/labels.tsx index bb3ebce72a..9c561aa59e 100644 --- a/solutions/swb-ui/src/common/labels.tsx +++ b/workbench-core/swb-common-ui/src/common/labels.tsx @@ -53,6 +53,7 @@ export const paginationLables: PaginationProps.Labels = { pageLabel: (pageNumber: number) => `Page ${pageNumber} of all pages` }; +// eslint-disable-next-line @rushstack/typedef-var export const headerLabels = { searchIconAriaLabel: 'Search', searchDismissIconAriaLabel: 'Close search', @@ -63,7 +64,8 @@ export const headerLabels = { signout: 'Sign out' }; -export const Navigation = { +// eslint-disable-next-line @rushstack/typedef-var +export const NavigationLabels = { Administration: 'Administration', CreateDashboard: 'Create new dashboard', Dashboards: 'Dashboards', diff --git a/solutions/swb-ui/src/common/tableCounterStrings.tsx b/workbench-core/swb-common-ui/src/common/tableCounterStrings.tsx similarity index 69% rename from solutions/swb-ui/src/common/tableCounterStrings.tsx rename to workbench-core/swb-common-ui/src/common/tableCounterStrings.tsx index 0bd9053207..1811fd6791 100644 --- a/solutions/swb-ui/src/common/tableCounterStrings.tsx +++ b/workbench-core/swb-common-ui/src/common/tableCounterStrings.tsx @@ -3,5 +3,5 @@ * SPDX-License-Identifier: Apache-2.0 */ -export const getFilterCounterText = (count: number | undefined) => +export const getFilterCounterText = (count: number | undefined): string => `${count} ${count === 1 ? 'match' : 'matches'}`; diff --git a/solutions/swb-ui/src/common/tableEmptyState.tsx b/workbench-core/swb-common-ui/src/common/tableEmptyState.tsx similarity index 96% rename from solutions/swb-ui/src/common/tableEmptyState.tsx rename to workbench-core/swb-common-ui/src/common/tableEmptyState.tsx index dda49dd319..0e3fd3aa2c 100644 --- a/solutions/swb-ui/src/common/tableEmptyState.tsx +++ b/workbench-core/swb-common-ui/src/common/tableEmptyState.tsx @@ -4,6 +4,7 @@ */ import { Box, Button } from '@cloudscape-design/components'; +import * as React from 'react'; /** * Displays the empty state of any table diff --git a/solutions/swb-ui/src/common/tableNoMatchState.tsx b/workbench-core/swb-common-ui/src/common/tableNoMatchState.tsx similarity index 95% rename from solutions/swb-ui/src/common/tableNoMatchState.tsx rename to workbench-core/swb-common-ui/src/common/tableNoMatchState.tsx index 1597cd03ca..2f8cb03edb 100644 --- a/solutions/swb-ui/src/common/tableNoMatchState.tsx +++ b/workbench-core/swb-common-ui/src/common/tableNoMatchState.tsx @@ -4,6 +4,7 @@ */ import { Box } from '@cloudscape-design/components'; +import * as React from 'react'; /** * Displays the no match state of any table diff --git a/solutions/swb-ui/src/common/utils.ts b/workbench-core/swb-common-ui/src/common/utils.ts similarity index 56% rename from solutions/swb-ui/src/common/utils.ts rename to workbench-core/swb-common-ui/src/common/utils.ts index 2171a28c8f..a6fc822558 100644 --- a/solutions/swb-ui/src/common/utils.ts +++ b/workbench-core/swb-common-ui/src/common/utils.ts @@ -5,19 +5,20 @@ export const nameRegex: RegExp = new RegExp('^[A-Za-z]{1}[A-Za-z0-9-\\s]*$'); -/* eslint-disable security/detect-unsafe-regex */ +/* eslint-disable-next-line security/detect-unsafe-regex */ export const cidrRegex: RegExp = new RegExp( '^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])[.]){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(/(3[0-2]|[1-2][0-9]|[0-9]))$' ); -export const emailRegex: RegExp = new RegExp( - /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,4}))$/ -); +// eslint-disable-next-line security/detect-unsafe-regex +export const emailRegex: RegExp = new RegExp(/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,4}))$/); +// eslint-disable-next-line @typescript-eslint/no-explicit-any export const convertToRecord = (queryObject: any): Record => { - let result: Record = {}; + const result: Record = {}; if (!queryObject) return result; Object.entries(queryObject).forEach(([key, value]) => { + // eslint-disable-next-line security/detect-object-injection if (value) result[key] = value as string; }); diff --git a/solutions/swb-ui/src/components/BaseLayout.tsx b/workbench-core/swb-common-ui/src/components/BaseLayout.tsx similarity index 91% rename from solutions/swb-ui/src/components/BaseLayout.tsx rename to workbench-core/swb-common-ui/src/components/BaseLayout.tsx index 278318221a..b966f706fb 100644 --- a/solutions/swb-ui/src/components/BaseLayout.tsx +++ b/workbench-core/swb-common-ui/src/components/BaseLayout.tsx @@ -8,7 +8,7 @@ import AppLayout, { AppLayoutProps } from '@cloudscape-design/components/app-lay import BreadcrumbGroup from '@cloudscape-design/components/breadcrumb-group'; import Flashbar from '@cloudscape-design/components/flashbar'; import Head from 'next/head'; -import React, { useState } from 'react'; +import * as React from 'react'; import { layoutLabels } from '../common/labels'; import Navigation from '../components/Navigation'; import { useNotifications } from '../context/NotificationContext'; @@ -21,14 +21,13 @@ export interface LayoutProps { activeHref?: string; } -export default function Layout({ +export default function BaseLayout({ navigationHide, children, breadcrumbs, activeHref = '#/' }: LayoutProps): JSX.Element { - // eslint-disable-next-line prefer-const - let [navigationOpen, setNavigationOpen] = useState(false); + const [navigationOpen, setNavigationOpen] = React.useState(false); const { notifications, displayNotification } = useNotifications(); const id = 'BetaCodeWarning'; displayNotification(id, { @@ -65,7 +64,6 @@ export default function Layout({ onNavigationChange={({ detail }) => { // eslint-disable-next-line security/detect-non-literal-fs-filename setNavigationOpen(detail.open); - navigationOpen = true; }} /> diff --git a/solutions/swb-ui/src/components/Header.tsx b/workbench-core/swb-common-ui/src/components/Header.tsx similarity index 75% rename from solutions/swb-ui/src/components/Header.tsx rename to workbench-core/swb-common-ui/src/components/Header.tsx index d48d314b4e..c48293a8f5 100644 --- a/solutions/swb-ui/src/components/Header.tsx +++ b/workbench-core/swb-common-ui/src/components/Header.tsx @@ -4,26 +4,23 @@ */ import TopNavigation from '@cloudscape-design/components/top-navigation'; +import * as React from 'react'; import { headerLabels } from '../common/labels'; import { useAuthentication } from '../context/AuthenticationContext'; import { useSettings } from '../context/SettingsContext'; import { researcherUser } from '../models/User'; -import styles from '../styles/Header.module.scss'; export default function Header(): JSX.Element { const { settings } = useSettings(); - // eslint-disable-next-line prefer-const - let { user, signOut } = useAuthentication(); + const { user, signOut } = useAuthentication(); - if (user === undefined) { - user = researcherUser; - } + const displayUser = user ?? researcherUser; const profileActions = [{ id: 'signout', text: headerLabels.signout }]; return ( await signOut() } diff --git a/solutions/swb-ui/src/components/Hero.tsx b/workbench-core/swb-common-ui/src/components/Hero.tsx similarity index 95% rename from solutions/swb-ui/src/components/Hero.tsx rename to workbench-core/swb-common-ui/src/components/Hero.tsx index e9cb138f4a..1629862240 100644 --- a/solutions/swb-ui/src/components/Hero.tsx +++ b/workbench-core/swb-common-ui/src/components/Hero.tsx @@ -5,10 +5,11 @@ import Box from '@cloudscape-design/components/box'; import Grid from '@cloudscape-design/components/grid'; +import * as React from 'react'; import { useSettings } from '../context/SettingsContext'; import Login from './Login'; -function Hero(): JSX.Element { +export default function Hero(): JSX.Element { const { settings } = useSettings(); return ( @@ -42,5 +43,3 @@ function Hero(): JSX.Element { ); } - -export default Hero; diff --git a/solutions/swb-ui/src/components/Login.tsx b/workbench-core/swb-common-ui/src/components/Login.tsx similarity index 75% rename from solutions/swb-ui/src/components/Login.tsx rename to workbench-core/swb-common-ui/src/components/Login.tsx index 9c13aafa4e..3d5b40db69 100644 --- a/solutions/swb-ui/src/components/Login.tsx +++ b/workbench-core/swb-common-ui/src/components/Login.tsx @@ -4,22 +4,20 @@ */ import Button from '@cloudscape-design/components/button'; +import * as React from 'react'; import { useAuthentication } from '../context/AuthenticationContext'; -import styles from '../styles/Hero.module.scss'; -function Login(): JSX.Element { +export default function Login(): JSX.Element { const { signIn } = useAuthentication(); return ( ); -} - -export default Login; +} \ No newline at end of file diff --git a/solutions/swb-ui/src/components/Navigation.tsx b/workbench-core/swb-common-ui/src/components/Navigation.tsx similarity index 98% rename from solutions/swb-ui/src/components/Navigation.tsx rename to workbench-core/swb-common-ui/src/components/Navigation.tsx index a4989e77d4..06fa8a5ee2 100644 --- a/solutions/swb-ui/src/components/Navigation.tsx +++ b/workbench-core/swb-common-ui/src/components/Navigation.tsx @@ -4,7 +4,7 @@ */ import SideNavigation, { SideNavigationProps } from '@cloudscape-design/components/side-navigation'; -import React from 'react'; +import * as React from 'react'; import { useAuthentication } from '../context/AuthenticationContext'; import RouteGuard from './RouteGuard'; diff --git a/solutions/swb-ui/src/components/RouteGuard.tsx b/workbench-core/swb-common-ui/src/components/RouteGuard.tsx similarity index 88% rename from solutions/swb-ui/src/components/RouteGuard.tsx rename to workbench-core/swb-common-ui/src/components/RouteGuard.tsx index 5763ddb0b3..e1c3cfac9d 100644 --- a/solutions/swb-ui/src/components/RouteGuard.tsx +++ b/workbench-core/swb-common-ui/src/components/RouteGuard.tsx @@ -12,7 +12,7 @@ interface Props { * @param children - Elements to be displayed if user is authenticated. * @returns children Elements */ -function RouteGuard({ children }: Props): JSX.Element { +export default function RouteGuard({ children }: Props): JSX.Element { // TODO: Once accessToken cookie is properly set, change to check isLoggedIn to drive redirect if (typeof window !== 'undefined') { const token = localStorage.getItem('idToken'); @@ -23,5 +23,3 @@ function RouteGuard({ children }: Props): JSX.Element { return children; } - -export default RouteGuard; diff --git a/solutions/swb-ui/src/context/AuthenticationContext.tsx b/workbench-core/swb-common-ui/src/context/AuthenticationContext.tsx similarity index 98% rename from solutions/swb-ui/src/context/AuthenticationContext.tsx rename to workbench-core/swb-common-ui/src/context/AuthenticationContext.tsx index aed52d1fef..5a454e7030 100644 --- a/solutions/swb-ui/src/context/AuthenticationContext.tsx +++ b/workbench-core/swb-common-ui/src/context/AuthenticationContext.tsx @@ -12,8 +12,8 @@ import { UserItem } from '../models/User'; export interface AuthenticationProps { user?: UserItem; - signIn: () => void; - signOut: () => void; + signIn: () => Promise; + signOut: () => Promise; } const AuthenticationContext: Context = createContext( diff --git a/solutions/swb-ui/src/context/NotificationContext.tsx b/workbench-core/swb-common-ui/src/context/NotificationContext.tsx similarity index 86% rename from solutions/swb-ui/src/context/NotificationContext.tsx rename to workbench-core/swb-common-ui/src/context/NotificationContext.tsx index 53a68d1937..2a00cc8925 100644 --- a/solutions/swb-ui/src/context/NotificationContext.tsx +++ b/workbench-core/swb-common-ui/src/context/NotificationContext.tsx @@ -4,7 +4,7 @@ */ import { FlashbarProps } from '@cloudscape-design/components/flashbar'; -import { createContext, useContext, Context, useState } from 'react'; +import * as React from 'react'; export interface Notifications { [key: string]: FlashbarProps.MessageDefinition; @@ -16,14 +16,14 @@ export interface NotificationProps { closeNotification: (id: string) => void; } -const NotificationsContext: Context = createContext({ +const NotificationsContext: React.Context = React.createContext({ notifications: {} as Notifications, displayNotification: (id: string, notification: FlashbarProps.MessageDefinition) => {}, closeNotification: (id: string) => {} }); export function NotificationsProvider({ children }: { children: React.ReactNode }): JSX.Element { - const [allNotifications, setAllNotifications] = useState({}); + const [allNotifications, setAllNotifications] = React.useState({}); const displayNotification = (id: string, notification: FlashbarProps.MessageDefinition): void => { if (id in allNotifications) { return; @@ -49,5 +49,5 @@ export function NotificationsProvider({ children }: { children: React.ReactNode } export function useNotifications(): NotificationProps { - return useContext(NotificationsContext); + return React.useContext(NotificationsContext); } diff --git a/solutions/swb-ui/src/context/SettingsContext.tsx b/workbench-core/swb-common-ui/src/context/SettingsContext.tsx similarity index 70% rename from solutions/swb-ui/src/context/SettingsContext.tsx rename to workbench-core/swb-common-ui/src/context/SettingsContext.tsx index 421986b884..53143efa43 100644 --- a/solutions/swb-ui/src/context/SettingsContext.tsx +++ b/workbench-core/swb-common-ui/src/context/SettingsContext.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { Context, createContext, useContext, useState } from 'react'; +import * as React from 'react'; import { AppSettings, defaultAppSettings } from '../models/AppSettings'; export interface SettingsProps { @@ -11,18 +11,18 @@ export interface SettingsProps { reload: () => void; } -const SettingsContext: Context = createContext({ +const SettingsContext: React.Context = React.createContext({ settings: defaultAppSettings, reload: () => {} }); export function SettingsProvider({ children }: { children: React.ReactNode }): JSX.Element { - const [settings] = useState(defaultAppSettings); + const [settings] = React.useState(defaultAppSettings); return ( {} }}>{children} ); } export function useSettings(): SettingsProps { - return useContext(SettingsContext); + return React.useContext(SettingsContext); } diff --git a/workbench-core/swb-common-ui/src/index.tsx b/workbench-core/swb-common-ui/src/index.tsx new file mode 100644 index 0000000000..47e9ce357a --- /dev/null +++ b/workbench-core/swb-common-ui/src/index.tsx @@ -0,0 +1,49 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { httpApiGet, httpApiPost, httpApiPut, httpApiDelete } from './api/apiHelper'; +import { login, logout, token, checkIfLoggedIn } from './api/auth'; +import { addUserToRole, createUser, useUsers } from './api/users'; +import { TerminateWarning } from './common/alerts'; +import { relativeOptions, datei18nStrings } from './common/dateRelativeOptions'; +import { isValidRangeFunction, convertToAbsoluteRange } from './common/dateRelativeProperties'; +import { i18nStrings, layoutLabels, paginationLables, headerLabels, NavigationLabels } from './common/labels'; +import { getFilterCounterText } from './common/tableCounterStrings'; +import { TableEmptyDisplay } from './common/tableEmptyState'; +import { TableNoMatchDisplay } from './common/tableNoMatchState'; +import { nameRegex, cidrRegex, emailRegex, convertToRecord } from './common/utils'; +import BaseLayout from './components/BaseLayout'; +import Header from './components/Header'; +import Hero from './components/Hero'; +import Login from './components/Login'; +import Navigation from './components/Navigation'; +import RouteGuard from './components/RouteGuard'; +import { AuthenticationProvider, useAuthentication } from './context/AuthenticationContext'; +import { NotificationsProvider, useNotifications } from './context/NotificationContext'; +import { SettingsProvider, useSettings } from './context/SettingsContext'; +import { defaultAppSettings } from './models/AppSettings'; +import { researcherUser, adminUser, type CreateUserForm, type CreateUserFormValidation, type UserItem } from './models/User'; + +export { + // From '/api' folder + httpApiGet, httpApiPost, httpApiPut, httpApiDelete, + login, logout, token, checkIfLoggedIn, + addUserToRole, createUser, useUsers, + + // From '/common' folder: + TerminateWarning, isValidRangeFunction, convertToAbsoluteRange, + getFilterCounterText, TableEmptyDisplay, TableNoMatchDisplay, convertToRecord, + relativeOptions, datei18nStrings, i18nStrings, layoutLabels, paginationLables, + headerLabels, NavigationLabels, nameRegex, cidrRegex, emailRegex, + + // From '/components' folder + BaseLayout, Header, Hero, Navigation, Login, RouteGuard, + + // From '/context' folder + AuthenticationProvider, useAuthentication, NotificationsProvider, useNotifications, SettingsProvider, useSettings, + + // From '/models' folder + defaultAppSettings, researcherUser, adminUser, CreateUserForm, CreateUserFormValidation, UserItem +}; diff --git a/solutions/swb-ui/src/models/AppSettings.ts b/workbench-core/swb-common-ui/src/models/AppSettings.ts similarity index 100% rename from solutions/swb-ui/src/models/AppSettings.ts rename to workbench-core/swb-common-ui/src/models/AppSettings.ts diff --git a/solutions/swb-ui/src/models/User.ts b/workbench-core/swb-common-ui/src/models/User.ts similarity index 99% rename from solutions/swb-ui/src/models/User.ts rename to workbench-core/swb-common-ui/src/models/User.ts index f2e06c1bd7..80f3658b8b 100644 --- a/solutions/swb-ui/src/models/User.ts +++ b/workbench-core/swb-common-ui/src/models/User.ts @@ -46,3 +46,4 @@ export interface CreateUserFormValidation { familyNameError?: string; givenNameError?: string; } + diff --git a/workbench-core/swb-common-ui/tsconfig.json b/workbench-core/swb-common-ui/tsconfig.json new file mode 100644 index 0000000000..43d3190449 --- /dev/null +++ b/workbench-core/swb-common-ui/tsconfig.json @@ -0,0 +1,21 @@ +{ + "$schema": "http://json.schemastore.org/tsconfig", + "extends": "./node_modules/@rushstack/heft-node-rig/profiles/default/tsconfig-base.json", + "compilerOptions": { + "types": ["heft-jest", "node"], + "outDir": "lib/esm", + "module": "esnext", + "target": "es5", + "lib": ["es6", "dom", "es2016", "es2017"], + "jsx": "react", + "declaration": true, + "moduleResolution": "node", + "noUnusedLocals": true, + "noImplicitAny": true, + "strictNullChecks": true, + "suppressImplicitAnyIndexErrors": true, + "allowSyntheticDefaultImports": true + }, + "include": ["src"], + "exclude": ["node_modules", "lib"] +} From 2dbd4efbe25025a5e42c8907faffb73d0ebfee76 Mon Sep 17 00:00:00 2001 From: SanketD92 Date: Wed, 31 Aug 2022 22:41:10 +0000 Subject: [PATCH 035/621] docs: Auto README.md updates [skip ci] --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index fcc5818e51..df211366fd 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,7 @@ In order to start developement please refer to [Development Instructions](./DEVE | [workbench-core/environments](./workbench-core/environments/) | [@aws/workbench-core-environments] | [README](./workbench-core/environments/README.md) | [workbench-core/infrastructure](./workbench-core/infrastructure/) | [@aws/workbench-core-infrastructure] | [README](./workbench-core/infrastructure/README.md) | [workbench-core/logging](./workbench-core/logging/) | [@aws/workbench-core-logging] | [README](./workbench-core/logging/README.md) +| [workbench-core/swb-common-ui](./workbench-core/swb-common-ui/) | [@aws/workbench-core-swb-common-ui] | [README](./workbench-core/swb-common-ui/README.md) ## Finding contributions to work on From 306ea5c2102e8ee2c238f724dc496d9b582bb3de Mon Sep 17 00:00:00 2001 From: Sanket Dharwadkar Date: Fri, 2 Sep 2022 09:15:57 -0700 Subject: [PATCH 036/621] feat: datasets list UI (#462) * feat: add dataset column definitions --- ...eat-datasets-list-ui_2022-09-01-06-04.json | 10 + ...eat-datasets-list-ui_2022-09-01-06-04.json | 10 + .../rush/browser-approved-packages.json | 4 + common/config/rush/pnpm-lock.yaml | 258 +- rush.json | 6 + solutions/swb-ui/package.json | 1 + solutions/swb-ui/src/pages/_app.tsx | 1 + solutions/swb-ui/src/pages/datasets/index.tsx | 8 + .../swb-ui/src/pages/environments/new.tsx | 2 +- workbench-core/datasets-ui/.depcheckrc | 20 + workbench-core/datasets-ui/.eslintignore | 4 + workbench-core/datasets-ui/.eslintrc.js | 11 + workbench-core/datasets-ui/.npmignore | 33 + workbench-core/datasets-ui/LICENSE | 201 + workbench-core/datasets-ui/NOTICE | 4264 +++++++++++++++++ workbench-core/datasets-ui/README.md | 4 + workbench-core/datasets-ui/config/heft.json | 51 + .../datasets-ui/config/jest.config.json | 14 + workbench-core/datasets-ui/config/rig.json | 18 + .../datasets-ui/license-add-config.json | 32 + workbench-core/datasets-ui/package.json | 64 + .../datasets-ui}/src/api/datasets.ts | 4 +- .../datasetsColumnDefinitions.tsx | 33 + .../datasetsFilteringOptions.tsx | 13 + .../datasetsFilteringProperties.tsx | 27 + workbench-core/datasets-ui/src/index.tsx | 26 + .../datasets-ui}/src/models/Dataset.ts | 0 .../datasets-ui/src/pages/index.tsx | 147 + workbench-core/datasets-ui/tsconfig.json | 21 + workbench-core/swb-common-ui/package.json | 2 +- .../src/components/Navigation.tsx | 6 +- 31 files changed, 5289 insertions(+), 6 deletions(-) create mode 100644 common/changes/@aws/workbench-core-datasets-ui/feat-datasets-list-ui_2022-09-01-06-04.json create mode 100644 common/changes/@aws/workbench-core-swb-common-ui/feat-datasets-list-ui_2022-09-01-06-04.json create mode 100644 solutions/swb-ui/src/pages/datasets/index.tsx create mode 100644 workbench-core/datasets-ui/.depcheckrc create mode 100644 workbench-core/datasets-ui/.eslintignore create mode 100644 workbench-core/datasets-ui/.eslintrc.js create mode 100644 workbench-core/datasets-ui/.npmignore create mode 100644 workbench-core/datasets-ui/LICENSE create mode 100644 workbench-core/datasets-ui/NOTICE create mode 100644 workbench-core/datasets-ui/README.md create mode 100644 workbench-core/datasets-ui/config/heft.json create mode 100644 workbench-core/datasets-ui/config/jest.config.json create mode 100644 workbench-core/datasets-ui/config/rig.json create mode 100644 workbench-core/datasets-ui/license-add-config.json create mode 100644 workbench-core/datasets-ui/package.json rename {solutions/swb-ui => workbench-core/datasets-ui}/src/api/datasets.ts (69%) create mode 100644 workbench-core/datasets-ui/src/datasets-table-config/datasetsColumnDefinitions.tsx create mode 100644 workbench-core/datasets-ui/src/datasets-table-config/datasetsFilteringOptions.tsx create mode 100644 workbench-core/datasets-ui/src/datasets-table-config/datasetsFilteringProperties.tsx create mode 100644 workbench-core/datasets-ui/src/index.tsx rename {solutions/swb-ui => workbench-core/datasets-ui}/src/models/Dataset.ts (100%) create mode 100644 workbench-core/datasets-ui/src/pages/index.tsx create mode 100644 workbench-core/datasets-ui/tsconfig.json diff --git a/common/changes/@aws/workbench-core-datasets-ui/feat-datasets-list-ui_2022-09-01-06-04.json b/common/changes/@aws/workbench-core-datasets-ui/feat-datasets-list-ui_2022-09-01-06-04.json new file mode 100644 index 0000000000..1948ae840d --- /dev/null +++ b/common/changes/@aws/workbench-core-datasets-ui/feat-datasets-list-ui_2022-09-01-06-04.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@aws/workbench-core-datasets-ui", + "comment": "Additions to Dataset UI package", + "type": "minor" + } + ], + "packageName": "@aws/workbench-core-datasets-ui" +} \ No newline at end of file diff --git a/common/changes/@aws/workbench-core-swb-common-ui/feat-datasets-list-ui_2022-09-01-06-04.json b/common/changes/@aws/workbench-core-swb-common-ui/feat-datasets-list-ui_2022-09-01-06-04.json new file mode 100644 index 0000000000..a06900b6de --- /dev/null +++ b/common/changes/@aws/workbench-core-swb-common-ui/feat-datasets-list-ui_2022-09-01-06-04.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@aws/workbench-core-swb-common-ui", + "comment": "Additions to Dataset UI package", + "type": "patch" + } + ], + "packageName": "@aws/workbench-core-swb-common-ui" +} \ No newline at end of file diff --git a/common/config/rush/browser-approved-packages.json b/common/config/rush/browser-approved-packages.json index 4b5a78bea4..45c8e895ab 100644 --- a/common/config/rush/browser-approved-packages.json +++ b/common/config/rush/browser-approved-packages.json @@ -118,6 +118,10 @@ "name": "@aws/workbench-core-datasets", "allowedCategories": [ "production" ] }, + { + "name": "@aws/workbench-core-datasets-ui", + "allowedCategories": [ "production" ] + }, { "name": "@aws/workbench-core-environments", "allowedCategories": [ "production" ] diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index 921fb9eb2e..417f99c174 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -303,6 +303,7 @@ importers: ../../solutions/swb-ui: specifiers: '@aws/eslint-config-workbench-core-eslint-custom': workspace:* + '@aws/workbench-core-datasets-ui': workspace:* '@aws/workbench-core-environments': workspace:* '@aws/workbench-core-swb-common-ui': workspace:* '@babel/core': ^7.17.9 @@ -349,6 +350,7 @@ importers: ts-jest: ^27.1.3 typescript: ^4.5.2 dependencies: + '@aws/workbench-core-datasets-ui': link:../../workbench-core/datasets-ui '@aws/workbench-core-environments': link:../../workbench-core/environments '@aws/workbench-core-swb-common-ui': link:../../workbench-core/swb-common-ui next: 12.1.6_fe3859d3c8c94a7c7e52ff5ea4f038fc @@ -779,6 +781,65 @@ importers: ts-jest: 27.1.5_jest@27.5.1+typescript@4.7.2 typescript: 4.7.2 + ../../workbench-core/datasets-ui: + specifiers: + '@aws/eslint-config-workbench-core-eslint-custom': workspace:* + '@aws/workbench-core-swb-common-ui': workspace:* + '@cloudscape-design/collection-hooks': ^1.0.3 + '@cloudscape-design/components': ^3.0.27 + '@rushstack/eslint-config': ^3.0.0 + '@rushstack/heft': ^0.47.5 + '@rushstack/heft-jest-plugin': ^0.3.28 + '@rushstack/heft-node-rig': ^1.10.5 + '@types/heft-jest': 1.0.2 + '@types/node': ^14 + '@types/react': 17.0.38 + '@types/react-dom': ^18.0.6 + depcheck: ^1.4.3 + eslint: ^8.7.0 + eslint-plugin-import: ^2.26.0 + istanbul-badges-readme: 1.8.1 + license-check-and-add: ^4.0.5 + license-checker: ^25.0.1 + next: ^12.1.5 + npm-package-json-lint: ^6.3.0 + npm-package-json-lint-config-default: ^5.0.0 + react: 17.0.2 + react-dom: 17.0.2 + sass: ^1.49.8 + sort-package-json: ^1.57.0 + swr: ^1.3.0 + typescript: ^4.5.2 + dependencies: + '@aws/workbench-core-swb-common-ui': link:../swb-common-ui + next: 12.1.6_757e1cdb914f1581201e1269321c5a94 + react: 17.0.2 + react-dom: 17.0.2_react@17.0.2 + swr: 1.3.0_react@17.0.2 + typescript: 4.7.4 + devDependencies: + '@aws/eslint-config-workbench-core-eslint-custom': link:../eslint-custom + '@cloudscape-design/collection-hooks': 1.0.3_react@17.0.2 + '@cloudscape-design/components': 3.0.27_b3482aaf5744fc7c2aeb7941b0e0a78f + '@rushstack/eslint-config': 3.0.0_eslint@8.16.0+typescript@4.7.4 + '@rushstack/heft': 0.47.5 + '@rushstack/heft-jest-plugin': 0.3.28_@rushstack+heft@0.47.5 + '@rushstack/heft-node-rig': 1.10.5_@rushstack+heft@0.47.5 + '@types/heft-jest': 1.0.2 + '@types/node': 14.18.20 + '@types/react': 17.0.38 + '@types/react-dom': 18.0.6 + depcheck: 1.4.3 + eslint: 8.16.0 + eslint-plugin-import: 2.26.0_eslint@8.16.0 + istanbul-badges-readme: 1.8.1 + license-check-and-add: 4.0.5 + license-checker: 25.0.1 + npm-package-json-lint: 6.3.0 + npm-package-json-lint-config-default: 5.0.0_npm-package-json-lint@6.3.0 + sass: 1.52.2 + sort-package-json: 1.57.0 + ../../workbench-core/environments: specifiers: '@aws-cdk/aws-iam': ^1.159.0 @@ -6280,6 +6341,29 @@ packages: transitivePeerDependencies: - supports-color + /@rushstack/eslint-config/3.0.0_eslint@8.16.0+typescript@4.7.4: + resolution: {integrity: sha512-9vkSsu7QnoCyujiJMB1uegfGF9LXe5Q+s08836H0PzHQ3lptsw4iqy1EQuuEPasjws6WR8b5oTT8SsrYVp9dcQ==} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + typescript: '>=4.7.0' + dependencies: + '@rushstack/eslint-patch': 1.1.4 + '@rushstack/eslint-plugin': 0.10.0_eslint@8.16.0+typescript@4.7.4 + '@rushstack/eslint-plugin-packlets': 0.5.0_eslint@8.16.0+typescript@4.7.4 + '@rushstack/eslint-plugin-security': 0.4.0_eslint@8.16.0+typescript@4.7.4 + '@typescript-eslint/eslint-plugin': 5.30.7_f850c3a044082aaf153a1cedc5abf281 + '@typescript-eslint/experimental-utils': 5.30.7_eslint@8.16.0+typescript@4.7.4 + '@typescript-eslint/parser': 5.30.7_eslint@8.16.0+typescript@4.7.4 + '@typescript-eslint/typescript-estree': 5.30.7_typescript@4.7.4 + eslint: 8.16.0 + eslint-plugin-promise: 6.0.0_eslint@8.16.0 + eslint-plugin-react: 7.27.1_eslint@8.16.0 + eslint-plugin-tsdoc: 0.2.16 + typescript: 4.7.4 + transitivePeerDependencies: + - supports-color + dev: true + /@rushstack/eslint-patch/1.0.8: resolution: {integrity: sha512-ZK5v4bJwgXldAUA8r3q9YKfCwOqoHTK/ZqRjSeRXQrBXWouoPnS4MQtgC4AXGiiBuUu5wxrRgTlv0ktmM4P1Aw==} dev: true @@ -6299,6 +6383,19 @@ packages: - supports-color - typescript + /@rushstack/eslint-plugin-packlets/0.5.0_eslint@8.16.0+typescript@4.7.4: + resolution: {integrity: sha512-I160nHeAGzA0q4g3cR7kiHNgiU1HqrYto52+lEmxLAdbBllqc6IOyiWQfCDb5ug0f+Y8bTwMQHiUrI7XclZB/Q==} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + '@rushstack/tree-pattern': 0.2.4 + '@typescript-eslint/experimental-utils': 5.30.7_eslint@8.16.0+typescript@4.7.4 + eslint: 8.16.0 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + /@rushstack/eslint-plugin-security/0.4.0_eslint@8.16.0+typescript@4.7.2: resolution: {integrity: sha512-jRFtrOnZZcuJ2MRA9RtoeyKiFQ60iKu7SDF1wkc7M9nHL5C1HkFApX6nTlAjY7C5B7UlV+9BP9fDmOJJmB4FSw==} peerDependencies: @@ -6311,6 +6408,19 @@ packages: - supports-color - typescript + /@rushstack/eslint-plugin-security/0.4.0_eslint@8.16.0+typescript@4.7.4: + resolution: {integrity: sha512-jRFtrOnZZcuJ2MRA9RtoeyKiFQ60iKu7SDF1wkc7M9nHL5C1HkFApX6nTlAjY7C5B7UlV+9BP9fDmOJJmB4FSw==} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + '@rushstack/tree-pattern': 0.2.4 + '@typescript-eslint/experimental-utils': 5.30.7_eslint@8.16.0+typescript@4.7.4 + eslint: 8.16.0 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + /@rushstack/eslint-plugin/0.10.0_eslint@8.16.0+typescript@4.7.2: resolution: {integrity: sha512-39DCBD6s7Y5XQxvcMmitXfupkReGcg0lmtil9mrGHkDoyiUln90sOWtpkSl6LqUrSL3lx7N2wRvJiJlwGIPYFQ==} peerDependencies: @@ -6323,6 +6433,19 @@ packages: - supports-color - typescript + /@rushstack/eslint-plugin/0.10.0_eslint@8.16.0+typescript@4.7.4: + resolution: {integrity: sha512-39DCBD6s7Y5XQxvcMmitXfupkReGcg0lmtil9mrGHkDoyiUln90sOWtpkSl6LqUrSL3lx7N2wRvJiJlwGIPYFQ==} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + '@rushstack/tree-pattern': 0.2.4 + '@typescript-eslint/experimental-utils': 5.30.7_eslint@8.16.0+typescript@4.7.4 + eslint: 8.16.0 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + /@rushstack/heft-config-file/0.8.4: resolution: {integrity: sha512-OFvezlWYFQlKSXXIIjuGlBwSIKIl7WXYQ48diK/J5WJWpdVaq/SLVzB3coAxNZPA/a7u+dbs1DcLORBa2e133Q==} engines: {node: '>=10.13.0'} @@ -6880,6 +7003,12 @@ packages: resolution: {integrity: sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==} dev: true + /@types/react-dom/18.0.6: + resolution: {integrity: sha512-/5OFZgfIPSwy+YuIBP/FgJnQnsxhZhjjrnxudMddeblOouIodEQ75X14Rr4wGSG/bknL+Omy9iWlLo1u/9GzAA==} + dependencies: + '@types/react': 17.0.38 + dev: true + /@types/react/17.0.38: resolution: {integrity: sha512-SI92X1IA+FMnP3qM5m4QReluXzhcmovhZnLNm3pyeQlooi02qI7sLiepEYqT678uNiyc25XfCqxREFpy3W7YhQ==} dependencies: @@ -6987,6 +7116,33 @@ packages: transitivePeerDependencies: - supports-color + /@typescript-eslint/eslint-plugin/5.30.7_f850c3a044082aaf153a1cedc5abf281: + resolution: {integrity: sha512-l4L6Do+tfeM2OK0GJsU7TUcM/1oN/N25xHm3Jb4z3OiDU4Lj8dIuxX9LpVMS9riSXQs42D1ieX7b85/r16H9Fw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + '@typescript-eslint/parser': ^5.0.0 + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/parser': 5.30.7_eslint@8.16.0+typescript@4.7.4 + '@typescript-eslint/scope-manager': 5.30.7 + '@typescript-eslint/type-utils': 5.30.7_eslint@8.16.0+typescript@4.7.4 + '@typescript-eslint/utils': 5.30.7_eslint@8.16.0+typescript@4.7.4 + debug: 4.3.4 + eslint: 8.16.0 + functional-red-black-tree: 1.0.1 + ignore: 5.2.0 + regexpp: 3.2.0 + semver: 7.3.7 + tsutils: 3.21.0_typescript@4.7.4 + typescript: 4.7.4 + transitivePeerDependencies: + - supports-color + dev: true + /@typescript-eslint/experimental-utils/5.27.0_eslint@8.16.0+typescript@4.7.2: resolution: {integrity: sha512-ZOn342bYh19IYvkiorrqnzNoRAr91h3GiFSSfa4tlHV+R9GgR8SxCwAi8PKMyT8+pfwMxfQdNbwKsMurbF9hzg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -7012,6 +7168,19 @@ packages: - supports-color - typescript + /@typescript-eslint/experimental-utils/5.30.7_eslint@8.16.0+typescript@4.7.4: + resolution: {integrity: sha512-r218ZVL0zFBYzEq8/9K2ZhRgsmKUhm8xd3sWChgvTbmP98kHGuY83IUl64SS9fx9OSBM9vMLdzBfox4eDdm/ZQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + '@typescript-eslint/utils': 5.30.7_eslint@8.16.0+typescript@4.7.4 + eslint: 8.16.0 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + /@typescript-eslint/parser/5.10.1_eslint@8.16.0+typescript@4.7.2: resolution: {integrity: sha512-GReo3tjNBwR5RnRO0K2wDIDN31cM3MmDtgyQ85oAxAmC5K3j/g85IjP+cDfcqDsDDBf1HNKQAD0WqOYL8jXqUA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -7051,6 +7220,26 @@ packages: transitivePeerDependencies: - supports-color + /@typescript-eslint/parser/5.30.7_eslint@8.16.0+typescript@4.7.4: + resolution: {integrity: sha512-Rg5xwznHWWSy7v2o0cdho6n+xLhK2gntImp0rJroVVFkcYFYQ8C8UJTSuTw/3CnExBmPjycjmUJkxVmjXsld6A==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/scope-manager': 5.30.7 + '@typescript-eslint/types': 5.30.7 + '@typescript-eslint/typescript-estree': 5.30.7_typescript@4.7.4 + debug: 4.3.4 + eslint: 8.16.0 + typescript: 4.7.4 + transitivePeerDependencies: + - supports-color + dev: true + /@typescript-eslint/scope-manager/5.10.1: resolution: {integrity: sha512-Lyvi559Gvpn94k7+ElXNMEnXu/iundV5uFmCUNnftbFrUbAJ1WBoaGgkbOBm07jVZa682oaBU37ao/NGGX4ZDg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -7092,6 +7281,25 @@ packages: transitivePeerDependencies: - supports-color + /@typescript-eslint/type-utils/5.30.7_eslint@8.16.0+typescript@4.7.4: + resolution: {integrity: sha512-nD5qAE2aJX/YLyKMvOU5jvJyku4QN5XBVsoTynFrjQZaDgDV6i7QHFiYCx10wvn7hFvfuqIRNBtsgaLe0DbWhw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: '*' + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/utils': 5.30.7_eslint@8.16.0+typescript@4.7.4 + debug: 4.3.4 + eslint: 8.16.0 + tsutils: 3.21.0_typescript@4.7.4 + typescript: 4.7.4 + transitivePeerDependencies: + - supports-color + dev: true + /@typescript-eslint/types/5.10.1: resolution: {integrity: sha512-ZvxQ2QMy49bIIBpTqFiOenucqUyjTQ0WNLhBM6X1fh1NNlYAC6Kxsx8bRTY3jdYsYg44a0Z/uEgQkohbR0H87Q==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -7168,6 +7376,27 @@ packages: transitivePeerDependencies: - supports-color + /@typescript-eslint/typescript-estree/5.30.7_typescript@4.7.4: + resolution: {integrity: sha512-tNslqXI1ZdmXXrHER83TJ8OTYl4epUzJC0aj2i4DMDT4iU+UqLT3EJeGQvJ17BMbm31x5scSwo3hPM0nqQ1AEA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/types': 5.30.7 + '@typescript-eslint/visitor-keys': 5.30.7 + debug: 4.3.4 + globby: 11.1.0 + is-glob: 4.0.3 + semver: 7.3.7 + tsutils: 3.21.0_typescript@4.7.4 + typescript: 4.7.4 + transitivePeerDependencies: + - supports-color + dev: true + /@typescript-eslint/utils/5.27.0_eslint@8.16.0+typescript@4.7.2: resolution: {integrity: sha512-nZvCrkIJppym7cIbP3pOwIkAefXOmfGPnCM0LQfzNaKxJHI6VjI8NC662uoiPlaf5f6ymkTy9C3NQXev2mdXmA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -7203,6 +7432,24 @@ packages: - supports-color - typescript + /@typescript-eslint/utils/5.30.7_eslint@8.16.0+typescript@4.7.4: + resolution: {integrity: sha512-Z3pHdbFw+ftZiGUnm1GZhkJgVqsDL5CYW2yj+TB2mfXDFOMqtbzQi2dNJIyPqPbx9mv2kUxS1gU+r2gKlKi1rQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + '@types/json-schema': 7.0.11 + '@typescript-eslint/scope-manager': 5.30.7 + '@typescript-eslint/types': 5.30.7 + '@typescript-eslint/typescript-estree': 5.30.7_typescript@4.7.4 + eslint: 8.16.0 + eslint-scope: 5.1.1 + eslint-utils: 3.0.0_eslint@8.16.0 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + /@typescript-eslint/visitor-keys/5.10.1: resolution: {integrity: sha512-NjQ0Xinhy9IL979tpoTRuLKxMc0zJC7QVSdeerXs2/QvOy2yRkzX5dRb10X5woNUdJgU8G3nYRDlI33sq1K4YQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -15358,6 +15605,16 @@ packages: tslib: 1.14.1 typescript: 4.7.2 + /tsutils/3.21.0_typescript@4.7.4: + resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} + engines: {node: '>= 6'} + peerDependencies: + typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta' + dependencies: + tslib: 1.14.1 + typescript: 4.7.4 + dev: true + /tunnel-agent/0.6.0: resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} dependencies: @@ -15438,7 +15695,6 @@ packages: resolution: {integrity: sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==} engines: {node: '>=4.2.0'} hasBin: true - dev: true /uid-safe/2.1.5: resolution: {integrity: sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==} diff --git a/rush.json b/rush.json index 327b49c37b..72632c9acb 100644 --- a/rush.json +++ b/rush.json @@ -101,6 +101,12 @@ "reviewCategory": "production", "shouldPublish": true }, + { + "packageName": "@aws/workbench-core-datasets-ui", + "projectFolder": "workbench-core/datasets-ui", + "reviewCategory": "production", + "shouldPublish": true + }, { "packageName": "@aws/workbench-core-example-express", "projectFolder": "workbench-core/example/express", diff --git a/solutions/swb-ui/package.json b/solutions/swb-ui/package.json index e968010a26..60c35c269e 100644 --- a/solutions/swb-ui/package.json +++ b/solutions/swb-ui/package.json @@ -35,6 +35,7 @@ }, "dependencies": { "@aws/workbench-core-environments": "workspace:*", + "@aws/workbench-core-datasets-ui": "workspace:*", "@aws/workbench-core-swb-common-ui": "workspace:*", "next": "^12.1.5", "next-global-css": "^1.2.0", diff --git a/solutions/swb-ui/src/pages/_app.tsx b/solutions/swb-ui/src/pages/_app.tsx index 00950508c1..710838ce80 100644 --- a/solutions/swb-ui/src/pages/_app.tsx +++ b/solutions/swb-ui/src/pages/_app.tsx @@ -11,6 +11,7 @@ import '../styles/Hero.module.scss'; import { AuthenticationProvider, NotificationsProvider, SettingsProvider, Header } from '@aws/workbench-core-swb-common-ui'; import type { AppProps } from 'next/app'; +// eslint-disable-next-line @typescript-eslint/naming-convention function App({ Component, pageProps }: AppProps): JSX.Element { return ( diff --git a/solutions/swb-ui/src/pages/datasets/index.tsx b/solutions/swb-ui/src/pages/datasets/index.tsx new file mode 100644 index 0000000000..79395b229f --- /dev/null +++ b/solutions/swb-ui/src/pages/datasets/index.tsx @@ -0,0 +1,8 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { DatasetsPage } from '@aws/workbench-core-datasets-ui'; + +export default DatasetsPage; diff --git a/solutions/swb-ui/src/pages/environments/new.tsx b/solutions/swb-ui/src/pages/environments/new.tsx index 4872dc7e2f..dc2036357e 100644 --- a/solutions/swb-ui/src/pages/environments/new.tsx +++ b/solutions/swb-ui/src/pages/environments/new.tsx @@ -3,6 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ +import { useDatasets } from '@aws/workbench-core-datasets-ui'; import { nameRegex, BaseLayout } from '@aws/workbench-core-swb-common-ui'; import { Box, @@ -23,7 +24,6 @@ import { import type { NextPage } from 'next'; import { useRouter } from 'next/router'; import React, { useEffect, useState } from 'react'; -import { useDatasets } from '../../api/datasets'; import { createEnvironment } from '../../api/environments'; import { useEnvTypeConfigs } from '../../api/environmentTypeConfigs'; import { useEnvironmentType } from '../../api/environmentTypes'; diff --git a/workbench-core/datasets-ui/.depcheckrc b/workbench-core/datasets-ui/.depcheckrc new file mode 100644 index 0000000000..c15dbb0037 --- /dev/null +++ b/workbench-core/datasets-ui/.depcheckrc @@ -0,0 +1,20 @@ +ignores: [ + '@aws/eslint-config-workbench-core-eslint-custom', + '@rushstack/heft-jest-plugin', + '@rushstack/heft-node-rig', + '@types/heft-jest', + 'eslint-plugin-import', + 'eslint-plugin-security', + 'npm-package-json-lint-config-default', + '@types/node' +] +ignore-patterns: [ + // files matching these patterns will be ignored + '.heft', + '.rush', + 'config', + 'lib', + 'node_modules', + 'temp', + ] +skip-missing: true \ No newline at end of file diff --git a/workbench-core/datasets-ui/.eslintignore b/workbench-core/datasets-ui/.eslintignore new file mode 100644 index 0000000000..2669727c91 --- /dev/null +++ b/workbench-core/datasets-ui/.eslintignore @@ -0,0 +1,4 @@ +.eslintrc.js +node_modules +lib +temp \ No newline at end of file diff --git a/workbench-core/datasets-ui/.eslintrc.js b/workbench-core/datasets-ui/.eslintrc.js new file mode 100644 index 0000000000..a59fa4da0a --- /dev/null +++ b/workbench-core/datasets-ui/.eslintrc.js @@ -0,0 +1,11 @@ +// This is a workaround for https://github.com/eslint/eslint/issues/3458 +require('@rushstack/eslint-config/patch/modern-module-resolution'); + +module.exports = { + extends: ['@aws/eslint-config-workbench-core-eslint-custom', 'next/core-web-vitals'], + parserOptions: { tsconfigRootDir: __dirname }, + rules: { + // These rules are turned off to be compatible with @cloudscape-design/components exported components + 'import/namespace': 0 + } +}; diff --git a/workbench-core/datasets-ui/.npmignore b/workbench-core/datasets-ui/.npmignore new file mode 100644 index 0000000000..87a8491edc --- /dev/null +++ b/workbench-core/datasets-ui/.npmignore @@ -0,0 +1,33 @@ +# THIS IS A STANDARD TEMPLATE FOR .npmignore FILES IN THIS REPO. + +# Ignore all files by default, to avoid accidentally publishing unintended files. +* + +# Use negative patterns to bring back the specific things we want to publish. +!/bin/** +!/lib/** +!/lib-*/** +!/dist/** +!ThirdPartyNotice.txt +!NOTICE + +# Ignore certain patterns that should not get published. +/dist/*.stats.* +/lib/**/test/ +/lib-*/**/test/ +/lib/**/__mocks__/ +*.test.js +*.test.d.ts +*.test.d.ts.map +*.test.js.map + +# NOTE: These don't need to be specified, because NPM includes them automatically. +# +# package.json +# README (and its variants) +# CHANGELOG (and its variants) +# LICENSE / LICENCE + +#-------------------------------------------- +# DO NOT MODIFY THE TEMPLATE ABOVE THIS LINE +#-------------------------------------------- diff --git a/workbench-core/datasets-ui/LICENSE b/workbench-core/datasets-ui/LICENSE new file mode 100644 index 0000000000..2ea34327d4 --- /dev/null +++ b/workbench-core/datasets-ui/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 2022 MA Foundation + + 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/workbench-core/datasets-ui/NOTICE b/workbench-core/datasets-ui/NOTICE new file mode 100644 index 0000000000..7226fb0719 --- /dev/null +++ b/workbench-core/datasets-ui/NOTICE @@ -0,0 +1,4264 @@ +@aws/eslint-config-workbench-core-eslint-custom +0.0.1 +license: Apache-2.0 +authors: Amazon Web Services + +****************************** + +@aws/workbench-core-audit +0.1.1 +license: Apache-2.0 +authors: Amazon Web Services + +****************************** + +@aws/workbench-core-authorization +0.1.1 +license: Apache-2.0 +authors: Amazon Web Services + +****************************** + +@aws/workbench-core-base +0.1.2 +license: Apache-2.0 +authors: Amazon Web Services + +****************************** + +@aws/workbench-core-logging +0.1.1 +license: Apache-2.0 +authors: Amazon Web Services + +****************************** + +@aws-cdk/aws-dynamodb +1.158.0 + 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. + + +****************************** + +@aws-cdk/aws-iam +1.159.0 + 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. + + +****************************** + +@aws-sdk/client-cloudformation +3.130.0 + 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-2020 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. + + +****************************** + +@aws-sdk/client-cognito-identity-provider +3.130.0 + 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-2020 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. + + +****************************** + +@aws-sdk/client-dynamodb +3.130.0 + 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-2020 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. + + +****************************** + +@aws-sdk/client-ec2 +3.130.0 + 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-2020 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. + + +****************************** + +@aws-sdk/client-eventbridge +3.130.0 + 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-2020 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. + + +****************************** + +@aws-sdk/client-iam +3.130.0 + 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-2020 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. + + +****************************** + +@aws-sdk/client-kms +3.130.0 + 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-2020 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. + + +****************************** + +@aws-sdk/client-lambda +3.105.0 + 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-2020 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. + + +****************************** + +@aws-sdk/client-s3 +3.130.0 + 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-2020 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. + + +****************************** + +@aws-sdk/client-s3-control +3.130.0 + 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-2020 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. + + +****************************** + +@aws-sdk/client-sagemaker +3.130.0 + 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-2020 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. + + +****************************** + +@aws-sdk/client-service-catalog +3.130.0 + 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-2020 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. + + +****************************** + +@aws-sdk/client-ssm +3.130.0 + 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-2020 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. + + +****************************** + +@aws-sdk/client-sts +3.130.0 + 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-2020 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. + + +****************************** + +@aws-sdk/types +3.127.0 +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-2020 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. + +****************************** + +@aws-sdk/util-dynamodb +3.105.0 +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-2020 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. + +****************************** + +@casl/ability +5.4.4 +MIT License + +Copyright (c) 2017-2018 Sergii Stotskyi + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +****************************** + +@hapi/boom +10.0.0 +Copyright (c) 2012-2022, Project contributors +Copyright (c) 2012-2020, Sideway Inc +Copyright (c) 2012-2014, Walmart. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: +* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +* The names of any contributors may not be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS OFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +****************************** + +@rushstack/eslint-config +2.6.0 +@rushstack/eslint-config + +Copyright (c) Microsoft Corporation. All rights reserved. + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +****************************** + +eslint +8.16.0 +Copyright OpenJS Foundation and other contributors, + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +****************************** + +eslint-plugin-import +2.26.0 +The MIT License (MIT) + +Copyright (c) 2015 Ben Mosher + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + + +****************************** + +eslint-plugin-security +1.5.0 + + 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 2015 &yet, LLC + + 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. + + +****************************** + +jsonschema +1.4.1 +jsonschema is licensed under MIT license. + +Copyright (C) 2012-2015 Tom de Grunt + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +****************************** + +lodash +4.17.21 +Copyright OpenJS Foundation and other contributors + +Based on Underscore.js, copyright Jeremy Ashkenas, +DocumentCloud and Investigative Reporters & Editors + +This software consists of voluntary contributions made by many +individuals. For exact contribution history, see the revision history +available at https://github.com/lodash/lodash + +The following license applies to all parts of this software except as +documented below: + +==== + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +==== + +Copyright and related rights for sample code are waived via CC0. Sample +code is defined as all source code displayed within the prose of the +documentation. + +CC0: http://creativecommons.org/publicdomain/zero/1.0/ + +==== + +Files located in the node_modules and vendor directories are externally +maintained libraries used by this software which have their own +licenses; we recommend you read them, as their terms may differ from the +terms above. + + +****************************** + +triple-beam +1.3.0 +MIT License + +Copyright (c) 2017 winstonjs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +****************************** + +uuid +8.3.2 +The MIT License (MIT) + +Copyright (c) 2010-2020 Robert Kieffer and other contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +****************************** + +winston +3.7.2 +Copyright (c) 2010 Charlie Robbins + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +****************************** + +winston-transport +4.5.0 +The MIT License (MIT) + +Copyright (c) 2015 Charlie Robbins & the contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/workbench-core/datasets-ui/README.md b/workbench-core/datasets-ui/README.md new file mode 100644 index 0000000000..883d0c6231 --- /dev/null +++ b/workbench-core/datasets-ui/README.md @@ -0,0 +1,4 @@ +## Code Coverage +| Statements | Branches | Functions | Lines | +| --------------------------- | ----------------------- | ------------------------- | ----------------- | +| ![Statements](https://img.shields.io/badge/statements-Unknown%25-brightgreen.svg?style=flat) | ![Branches](https://img.shields.io/badge/branches-Unknown%25-brightgreen.svg?style=flat) | ![Functions](https://img.shields.io/badge/functions-Unknown%25-brightgreen.svg?style=flat) | ![Lines](https://img.shields.io/badge/lines-Unknown%25-brightgreen.svg?style=flat) | \ No newline at end of file diff --git a/workbench-core/datasets-ui/config/heft.json b/workbench-core/datasets-ui/config/heft.json new file mode 100644 index 0000000000..75892c1242 --- /dev/null +++ b/workbench-core/datasets-ui/config/heft.json @@ -0,0 +1,51 @@ +/** + * Defines configuration used by core Heft. + */ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/heft.schema.json", + + "eventActions": [ + { + /** + * The kind of built-in operation that should be performed. + * The "deleteGlobs" action deletes files or folders that match the + * specified glob patterns. + */ + "actionKind": "deleteGlobs", + + /** + * The stage of the Heft run during which this action should occur. Note that actions specified in heft.json + * occur at the end of the stage of the Heft run. + */ + "heftEvent": "clean", + + /** + * A user-defined tag whose purpose is to allow configs to replace/delete handlers that were added by other + * configs. + */ + "actionId": "defaultClean", + + /** + * Glob patterns to be deleted. The paths are resolved relative to the project folder. + */ + "globsToDelete": ["dist", "lib", "temp"] + } + ], + + /** + * The list of Heft plugins to be loaded. + */ + "heftPlugins": [ + { + // /** + // * The path to the plugin package. + // */ + "plugin": "@rushstack/heft-jest-plugin" + // + // /** + // * An optional object that provides additional settings that may be defined by the plugin. + // */ + // // "options": { } + } + ] +} diff --git a/workbench-core/datasets-ui/config/jest.config.json b/workbench-core/datasets-ui/config/jest.config.json new file mode 100644 index 0000000000..cf5480d82f --- /dev/null +++ b/workbench-core/datasets-ui/config/jest.config.json @@ -0,0 +1,14 @@ +{ + "extends": "@rushstack/heft-node-rig/profiles/default/config/jest.config.json", + "collectCoverage": true, + "coveragePathIgnorePatterns": ["/src/index.ts"], + "coverageThreshold": { + "global": { + "branches": 90, + "functions": 90, + "lines": 90, + "statements": 90 + } + }, + "coverageReporters": ["json-summary", "json", "lcov", "text"] +} diff --git a/workbench-core/datasets-ui/config/rig.json b/workbench-core/datasets-ui/config/rig.json new file mode 100644 index 0000000000..f6c7b5537d --- /dev/null +++ b/workbench-core/datasets-ui/config/rig.json @@ -0,0 +1,18 @@ +// The "rig.json" file directs tools to look for their config files in an external package. +// Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package +{ + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + + /** + * (Required) The name of the rig package to inherit from. + * It should be an NPM package name with the "-rig" suffix. + */ + "rigPackageName": "@rushstack/heft-node-rig" + + /** + * (Optional) Selects a config profile from the rig package. The name must consist of + * lowercase alphanumeric words separated by hyphens, for example "sample-profile". + * If omitted, then the "default" profile will be used." + */ + // "rigProfile": "your-profile-name" +} diff --git a/workbench-core/datasets-ui/license-add-config.json b/workbench-core/datasets-ui/license-add-config.json new file mode 100644 index 0000000000..ed85c268a8 --- /dev/null +++ b/workbench-core/datasets-ui/license-add-config.json @@ -0,0 +1,32 @@ +{ + "license": "../../apache-license-header.txt", + "ignoreFile": "../../.licenseignorefile", + "defaultFormat": { + "gitignore|npmignore|eslintignore|dockerignore|sh|py": { + "eachLine": { + "prepend": "# " + } + }, + "html|xml|svg": { + "prepend": "" + }, + "js|ts|tsx|css|scss|less|php|as|c|java|cpp|go|cto|acl": { + "prepend": "/*", + "append": " */\n", + "eachLine": { + "prepend": " * " + } + }, + "txt": {} + }, + "licenseFormats": { + "ts|tsx": { + "prepend": "/*", + "append": " */\n", + "eachLine": { + "prepend": " * " + } + } + } +} diff --git a/workbench-core/datasets-ui/package.json b/workbench-core/datasets-ui/package.json new file mode 100644 index 0000000000..7d53d30980 --- /dev/null +++ b/workbench-core/datasets-ui/package.json @@ -0,0 +1,64 @@ +{ + "name": "@aws/workbench-core-datasets-ui", + "version": "0.0.1", + "description": "Package that contains UI components for Datasets", + "homepage": "https://github.com/awslabs/solution-spark-on-aws", + "repository": { + "type": "git", + "url": "https://github.com/awslabs/solution-spark-on-aws.git" + }, + "license": "Apache-2.0", + "author": { + "name": "Amazon Web Services", + "url": "http://aws.amazon.com" + }, + "main": "lib/esm/index.js", + "module": "./lib/esm/index.js", + "typings": "lib/esm/index.d.ts", + "scripts": { + "add-license-header": "license-check-and-add add -f license-add-config.json", + "build": "heft build --clean && rushx pkg-json-lint", + "build:test": "heft test --clean && rushx pkg-json-lint && rushx make-badges", + "check-license-header": "license-check-and-add check -f license-add-config.json", + "depcheck": "depcheck", + "license-checker": "license-checker --onlyAllow 'MIT; Apache-2.0; ISC; BSD'", + "lint": "eslint . && rushx pkg-json-lint", + "lint:fix": "eslint . --fix", + "make-badges": "istanbul-badges-readme --coverageDir=./temp/coverage --exitCode=1", + "pkg-json-lint": "npmPkgJsonLint -c ../../.npmpackagejsonlintrc.json .", + "sort-package-json": "sort-package-json package.json", + "test": "rushx test:only && rushx make-badges", + "test:only": "heft test --clean --no-build" + }, + "dependencies": { + "@aws/workbench-core-swb-common-ui": "workspace:*", + "next": "^12.1.5", + "react": "17.0.2", + "react-dom": "17.0.2", + "swr": "^1.3.0", + "typescript": "^4.5.2" + }, + "devDependencies": { + "@aws/eslint-config-workbench-core-eslint-custom": "workspace:*", + "@cloudscape-design/components": "^3.0.27", + "@cloudscape-design/collection-hooks": "^1.0.3", + "@rushstack/eslint-config": "^3.0.0", + "@rushstack/heft": "^0.47.5", + "@rushstack/heft-jest-plugin": "^0.3.28", + "@rushstack/heft-node-rig": "^1.10.5", + "@types/heft-jest": "1.0.2", + "@types/node": "^14", + "@types/react": "17.0.38", + "@types/react-dom": "^18.0.6", + "depcheck": "^1.4.3", + "eslint": "^8.7.0", + "eslint-plugin-import": "^2.26.0", + "istanbul-badges-readme": "1.8.1", + "license-check-and-add": "^4.0.5", + "license-checker": "^25.0.1", + "npm-package-json-lint": "^6.3.0", + "npm-package-json-lint-config-default": "^5.0.0", + "sass": "^1.49.8", + "sort-package-json": "^1.57.0" + } +} diff --git a/solutions/swb-ui/src/api/datasets.ts b/workbench-core/datasets-ui/src/api/datasets.ts similarity index 69% rename from solutions/swb-ui/src/api/datasets.ts rename to workbench-core/datasets-ui/src/api/datasets.ts index 68f701840c..31460ed137 100644 --- a/solutions/swb-ui/src/api/datasets.ts +++ b/workbench-core/datasets-ui/src/api/datasets.ts @@ -9,7 +9,9 @@ import { DatasetItem } from '../models/Dataset'; const useDatasets = (): { datasets: DatasetItem[], areDatasetsLoading: boolean } => { const { data, isValidating } = useSWR(() => 'datasets', httpApiGet); - const datasets: DatasetItem[] = data?.data ?? []; + // TODO: Once datasetService methods return Promise instead of the item directly replace the line below with: + // const datasets: DatasetItem[] = data?.data ?? []; + const datasets: DatasetItem[] = data ?? []; return { datasets, areDatasetsLoading: isValidating }; }; diff --git a/workbench-core/datasets-ui/src/datasets-table-config/datasetsColumnDefinitions.tsx b/workbench-core/datasets-ui/src/datasets-table-config/datasetsColumnDefinitions.tsx new file mode 100644 index 0000000000..0a93e5889e --- /dev/null +++ b/workbench-core/datasets-ui/src/datasets-table-config/datasetsColumnDefinitions.tsx @@ -0,0 +1,33 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { TableProps } from '@cloudscape-design/components'; + +export const columnDefinitions: readonly TableProps.ColumnDefinition[] = [ + { + id: 'datasetName', + header: 'Dataset name', + cell: (e: { name: string }) => e.name, + sortingField: 'name' + }, + { + id: 'category', + header: 'Category', + cell: () => 'Internal', // TODO: Replace with e.category once implemented + sortingField: 'category' + }, + { + id: 'description', + header: 'Description', + cell: () => 'Sample description of this dataset', // TODO: Replace with e.description once implemented + sortingField: 'description' + }, +]; + +export const searchableColumns: readonly string[] = [ + 'datasetName', + 'category', + 'description' +]; \ No newline at end of file diff --git a/workbench-core/datasets-ui/src/datasets-table-config/datasetsFilteringOptions.tsx b/workbench-core/datasets-ui/src/datasets-table-config/datasetsFilteringOptions.tsx new file mode 100644 index 0000000000..ec0bfb279e --- /dev/null +++ b/workbench-core/datasets-ui/src/datasets-table-config/datasetsFilteringOptions.tsx @@ -0,0 +1,13 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { PropertyFilterProps } from '@cloudscape-design/components/property-filter'; + +export const filteringOptions: readonly PropertyFilterProps.FilteringOption[] = [ + { propertyKey: 'datasetName', value: '' }, + { propertyKey: 'category', value: 'Internal' }, + { propertyKey: 'category', value: 'External' }, + { propertyKey: 'description', value: '' } +]; \ No newline at end of file diff --git a/workbench-core/datasets-ui/src/datasets-table-config/datasetsFilteringProperties.tsx b/workbench-core/datasets-ui/src/datasets-table-config/datasetsFilteringProperties.tsx new file mode 100644 index 0000000000..391736ef05 --- /dev/null +++ b/workbench-core/datasets-ui/src/datasets-table-config/datasetsFilteringProperties.tsx @@ -0,0 +1,27 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { PropertyFilterProperty } from '@cloudscape-design/collection-hooks/dist/cjs/interfaces'; + +export const filteringProperties: readonly PropertyFilterProperty[] = [ + { + key: 'datasetName', + operators: ['=', '!=', ':', '!:'], + propertyLabel: 'Dataset Name', + groupValuesLabel: 'Dataset Name Values' + }, + { + key: 'category', + operators: ['=', '!='], + propertyLabel: 'Dataset Category', + groupValuesLabel: 'Dataset Category Values' + }, + { + key: 'description', + operators: ['=', '!='], + propertyLabel: 'Dataset Description', + groupValuesLabel: 'Dataset Description Values' + } +]; \ No newline at end of file diff --git a/workbench-core/datasets-ui/src/index.tsx b/workbench-core/datasets-ui/src/index.tsx new file mode 100644 index 0000000000..ef727675ac --- /dev/null +++ b/workbench-core/datasets-ui/src/index.tsx @@ -0,0 +1,26 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { useDatasets } from './api/datasets'; +import { columnDefinitions, searchableColumns } from './datasets-table-config/datasetsColumnDefinitions'; +import { filteringOptions } from './datasets-table-config/datasetsFilteringOptions'; +import { filteringProperties } from './datasets-table-config/datasetsFilteringProperties'; +import { type DatasetItem } from './models/Dataset'; +import { DatasetsPage } from './pages/index'; + + +export { + // From '/api' folder + useDatasets, + + // From '/datasets-table-config' folder + columnDefinitions, searchableColumns, filteringOptions, filteringProperties, + + // From '/models' folder + DatasetItem, + + // From 'pages' folder + DatasetsPage +}; diff --git a/solutions/swb-ui/src/models/Dataset.ts b/workbench-core/datasets-ui/src/models/Dataset.ts similarity index 100% rename from solutions/swb-ui/src/models/Dataset.ts rename to workbench-core/datasets-ui/src/models/Dataset.ts diff --git a/workbench-core/datasets-ui/src/pages/index.tsx b/workbench-core/datasets-ui/src/pages/index.tsx new file mode 100644 index 0000000000..4f0c0298d8 --- /dev/null +++ b/workbench-core/datasets-ui/src/pages/index.tsx @@ -0,0 +1,147 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { i18nStrings, getFilterCounterText, TableEmptyDisplay, TableNoMatchDisplay, useNotifications, BaseLayout } from '@aws/workbench-core-swb-common-ui'; +import { useCollection } from '@cloudscape-design/collection-hooks'; +import { + Box, + BreadcrumbGroupProps, + Header, + PropertyFilter, + SpaceBetween, + Table +} from '@cloudscape-design/components'; +import { FlashbarProps } from '@cloudscape-design/components/flashbar'; +import type { NextPage } from 'next'; +import { useRouter } from 'next/router'; +import React, { useState } from 'react'; +import { useDatasets } from '../api/datasets'; +import { columnDefinitions, searchableColumns } from '../datasets-table-config/datasetsColumnDefinitions'; +import { filteringOptions } from '../datasets-table-config/datasetsFilteringOptions'; +import { filteringProperties } from '../datasets-table-config/datasetsFilteringProperties'; + +export const DatasetsPage: NextPage = () => { + // For functions to return content specific to the table + const itemType: string = 'dataset'; + // App settings constant + + const { datasets, areDatasetsLoading } = useDatasets(); + const router = useRouter(); + const { message, notificationType } = router.query; + const { displayNotification, closeNotification } = useNotifications(); + + const [hasInitialNotificationBeenShown, setHasInitialNotificationBeenShown] = useState(false); + if (!!message && !!notificationType && !hasInitialNotificationBeenShown) { + const datasetMessageId = 'DatasetMessage'; + const notification = { + type: notificationType as FlashbarProps.Type, + dismissible: true, + dismissLabel: 'Dismiss message', + onDismiss: () => { + closeNotification(datasetMessageId); + }, + content: message, + id: datasetMessageId + }; + displayNotification(datasetMessageId, notification); + setHasInitialNotificationBeenShown(true); + } + + // App layout constants + const breadcrumbs: BreadcrumbGroupProps.Item[] = [ + { + text: 'Service Workbench', + href: '/' + }, + { + text: 'Datasets', + href: '/datasets' + } + ]; + + // Property and date filter collections + const { items, filteredItemsCount, collectionProps, propertyFilterProps } = useCollection(datasets, { + filtering: { + empty: TableEmptyDisplay(itemType), + noMatch: TableNoMatchDisplay(itemType), + // eslint-disable-next-line @typescript-eslint/no-explicit-any + filteringFunction: (item: any, filteringText): boolean => { + const filteringTextLowerCase = filteringText.toLowerCase(); + + return ( + searchableColumns + // eslint-disable-next-line security/detect-object-injection + .map((key) => item[key]) + .some( + (value) => typeof value === 'string' && value.toLowerCase().indexOf(filteringTextLowerCase) > -1 + ) + ); + } + }, + propertyFiltering: { + filteringProperties: filteringProperties, + empty: TableEmptyDisplay(itemType), + noMatch: TableNoMatchDisplay(itemType) + }, + + sorting: {}, + selection: {} + }); + + const getContent = (): JSX.Element => { + return ( + + + `${selectedItems.length} ${selectedItems.length === 1 ? 'item' : 'items'} selected`, + itemSelectionLabel: ({ selectedItems }, item) => { + const isItemSelected = selectedItems.filter((i) => i.dataset === item.dataset).length; + return `${item.dataset} is ${isItemSelected ? '' : 'not'} selected`; + } + }} + header={ + <> +
+ + + + } + > + Datasets +
+ + } + columnDefinitions={columnDefinitions} + loadingText="Loading datasets" + filter={ + + + + } + items={items} + /> + + ); + }; + return ( + + {getContent()} + + ); +}; diff --git a/workbench-core/datasets-ui/tsconfig.json b/workbench-core/datasets-ui/tsconfig.json new file mode 100644 index 0000000000..43d3190449 --- /dev/null +++ b/workbench-core/datasets-ui/tsconfig.json @@ -0,0 +1,21 @@ +{ + "$schema": "http://json.schemastore.org/tsconfig", + "extends": "./node_modules/@rushstack/heft-node-rig/profiles/default/tsconfig-base.json", + "compilerOptions": { + "types": ["heft-jest", "node"], + "outDir": "lib/esm", + "module": "esnext", + "target": "es5", + "lib": ["es6", "dom", "es2016", "es2017"], + "jsx": "react", + "declaration": true, + "moduleResolution": "node", + "noUnusedLocals": true, + "noImplicitAny": true, + "strictNullChecks": true, + "suppressImplicitAnyIndexErrors": true, + "allowSyntheticDefaultImports": true + }, + "include": ["src"], + "exclude": ["node_modules", "lib"] +} diff --git a/workbench-core/swb-common-ui/package.json b/workbench-core/swb-common-ui/package.json index 8f564c0a6d..2e88977a8a 100644 --- a/workbench-core/swb-common-ui/package.json +++ b/workbench-core/swb-common-ui/package.json @@ -1,7 +1,7 @@ { "name": "@aws/workbench-core-swb-common-ui", "version": "0.0.1", - "description": "Access and management of data files for the purpose of connecting to various AWS environments.", + "description": "Package that contains common SWB UI components.", "homepage": "https://github.com/awslabs/solution-spark-on-aws", "repository": { "type": "git", diff --git a/workbench-core/swb-common-ui/src/components/Navigation.tsx b/workbench-core/swb-common-ui/src/components/Navigation.tsx index 06fa8a5ee2..3b1af977d0 100644 --- a/workbench-core/swb-common-ui/src/components/Navigation.tsx +++ b/workbench-core/swb-common-ui/src/components/Navigation.tsx @@ -27,10 +27,12 @@ export default function Navigation({ }; const adminNavItems: ReadonlyArray = [ { type: 'link', text: 'Users', href: '/users' }, - { type: 'link', text: 'Workspaces', href: '/environments' } + { type: 'link', text: 'Workspaces', href: '/environments' }, + { type: 'link', text: 'Datasets', href: '/datasets' } ]; const userNavItems: ReadonlyArray = [ - { type: 'link', text: 'Workspaces', href: '/environments' } + { type: 'link', text: 'Workspaces', href: '/environments' }, + { type: 'link', text: 'Datasets', href: '/datasets' } ]; // Role-based navigation display From be74ae9b1b92bd3738690806fe2e0ae36e638f7b Mon Sep 17 00:00:00 2001 From: SanketD92 Date: Fri, 2 Sep 2022 16:24:18 +0000 Subject: [PATCH 037/621] docs: Auto README.md updates [skip ci] --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index df211366fd..86dfb38ea1 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,7 @@ In order to start developement please refer to [Development Instructions](./DEVE | [workbench-core/authorization](./workbench-core/authorization/) | [@aws/workbench-core-authorization] | [README](./workbench-core/authorization/README.md) | [workbench-core/base](./workbench-core/base/) | [@aws/workbench-core-base] | [README](./workbench-core/base/README.md) | [workbench-core/datasets](./workbench-core/datasets/) | [@aws/workbench-core-datasets] | [README](./workbench-core/datasets/README.md) +| [workbench-core/datasets-ui](./workbench-core/datasets-ui/) | [@aws/workbench-core-datasets-ui] | [README](./workbench-core/datasets-ui/README.md) | [workbench-core/environments](./workbench-core/environments/) | [@aws/workbench-core-environments] | [README](./workbench-core/environments/README.md) | [workbench-core/infrastructure](./workbench-core/infrastructure/) | [@aws/workbench-core-infrastructure] | [README](./workbench-core/infrastructure/README.md) | [workbench-core/logging](./workbench-core/logging/) | [@aws/workbench-core-logging] | [README](./workbench-core/logging/README.md) From 290301e60230eb5b146cd73a63b819ac7f95aca2 Mon Sep 17 00:00:00 2001 From: Sanket Dharwadkar Date: Fri, 2 Sep 2022 11:04:12 -0700 Subject: [PATCH 038/621] fix: datasets base UI page (#486) * fix: strip down DS list page to bare min func --- .../fix-datasets-ui_2022-09-02-17-29.json | 10 ++++ .../datasets-ui/src/api/datasets.ts | 4 +- .../datasets-ui/src/pages/index.tsx | 60 ++----------------- 3 files changed, 17 insertions(+), 57 deletions(-) create mode 100644 common/changes/@aws/workbench-core-datasets-ui/fix-datasets-ui_2022-09-02-17-29.json diff --git a/common/changes/@aws/workbench-core-datasets-ui/fix-datasets-ui_2022-09-02-17-29.json b/common/changes/@aws/workbench-core-datasets-ui/fix-datasets-ui_2022-09-02-17-29.json new file mode 100644 index 0000000000..dd5c5b7410 --- /dev/null +++ b/common/changes/@aws/workbench-core-datasets-ui/fix-datasets-ui_2022-09-02-17-29.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@aws/workbench-core-datasets-ui", + "comment": "remove unnecessary code from base DS list page", + "type": "none" + } + ], + "packageName": "@aws/workbench-core-datasets-ui" +} \ No newline at end of file diff --git a/workbench-core/datasets-ui/src/api/datasets.ts b/workbench-core/datasets-ui/src/api/datasets.ts index 31460ed137..95ae8c0835 100644 --- a/workbench-core/datasets-ui/src/api/datasets.ts +++ b/workbench-core/datasets-ui/src/api/datasets.ts @@ -9,9 +9,9 @@ import { DatasetItem } from '../models/Dataset'; const useDatasets = (): { datasets: DatasetItem[], areDatasetsLoading: boolean } => { const { data, isValidating } = useSWR(() => 'datasets', httpApiGet); - // TODO: Once datasetService methods return Promise instead of the item directly replace the line below with: + // TODO: Once datasetService methods return response wrapped in a {data: } body, replace the line below with: // const datasets: DatasetItem[] = data?.data ?? []; - const datasets: DatasetItem[] = data ?? []; + const datasets: DatasetItem[] = data || []; return { datasets, areDatasetsLoading: isValidating }; }; diff --git a/workbench-core/datasets-ui/src/pages/index.tsx b/workbench-core/datasets-ui/src/pages/index.tsx index 4f0c0298d8..2d216ee151 100644 --- a/workbench-core/datasets-ui/src/pages/index.tsx +++ b/workbench-core/datasets-ui/src/pages/index.tsx @@ -3,13 +3,12 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { i18nStrings, getFilterCounterText, TableEmptyDisplay, TableNoMatchDisplay, useNotifications, BaseLayout } from '@aws/workbench-core-swb-common-ui'; +import { useNotifications, BaseLayout } from '@aws/workbench-core-swb-common-ui'; import { useCollection } from '@cloudscape-design/collection-hooks'; import { Box, BreadcrumbGroupProps, Header, - PropertyFilter, SpaceBetween, Table } from '@cloudscape-design/components'; @@ -18,13 +17,10 @@ import type { NextPage } from 'next'; import { useRouter } from 'next/router'; import React, { useState } from 'react'; import { useDatasets } from '../api/datasets'; -import { columnDefinitions, searchableColumns } from '../datasets-table-config/datasetsColumnDefinitions'; -import { filteringOptions } from '../datasets-table-config/datasetsFilteringOptions'; -import { filteringProperties } from '../datasets-table-config/datasetsFilteringProperties'; +import { columnDefinitions } from '../datasets-table-config/datasetsColumnDefinitions'; export const DatasetsPage: NextPage = () => { // For functions to return content specific to the table - const itemType: string = 'dataset'; // App settings constant const { datasets, areDatasetsLoading } = useDatasets(); @@ -62,51 +58,15 @@ export const DatasetsPage: NextPage = () => { ]; // Property and date filter collections - const { items, filteredItemsCount, collectionProps, propertyFilterProps } = useCollection(datasets, { - filtering: { - empty: TableEmptyDisplay(itemType), - noMatch: TableNoMatchDisplay(itemType), - // eslint-disable-next-line @typescript-eslint/no-explicit-any - filteringFunction: (item: any, filteringText): boolean => { - const filteringTextLowerCase = filteringText.toLowerCase(); - - return ( - searchableColumns - // eslint-disable-next-line security/detect-object-injection - .map((key) => item[key]) - .some( - (value) => typeof value === 'string' && value.toLowerCase().indexOf(filteringTextLowerCase) > -1 - ) - ); - } - }, - propertyFiltering: { - filteringProperties: filteringProperties, - empty: TableEmptyDisplay(itemType), - noMatch: TableNoMatchDisplay(itemType) - }, - - sorting: {}, - selection: {} - }); + const { items } = useCollection(datasets, {}); const getContent = (): JSX.Element => { return (
- `${selectedItems.length} ${selectedItems.length === 1 ? 'item' : 'items'} selected`, - itemSelectionLabel: ({ selectedItems }, item) => { - const isItemSelected = selectedItems.filter((i) => i.dataset === item.dataset).length; - return `${item.dataset} is ${isItemSelected ? '' : 'not'} selected`; - } - }} + header={ <>
{ } columnDefinitions={columnDefinitions} loadingText="Loading datasets" - filter={ - - - - } + items={items} /> From 1f69cb0dc6db5965358b67fb042bb719af55d9f0 Mon Sep 17 00:00:00 2001 From: kpark277 <103979972+kpark277@users.noreply.github.com> Date: Fri, 2 Sep 2022 13:59:13 -0700 Subject: [PATCH 039/621] feat(swb-common-ui): refresh access token (#485) * feat(swb-common-ui): uI will use refresh token to refresh access token if expired * docs(swb-common-ui): adding change file --- ...-refresh-access-token_2022-09-02-15-27.json | 10 ++++++++++ .../swb-common-ui/src/api/apiHelper.ts | 18 ++++++++++++++++++ .../src/context/AuthenticationContext.tsx | 3 --- 3 files changed, 28 insertions(+), 3 deletions(-) create mode 100644 common/changes/@aws/workbench-core-swb-common-ui/feat-refresh-access-token_2022-09-02-15-27.json diff --git a/common/changes/@aws/workbench-core-swb-common-ui/feat-refresh-access-token_2022-09-02-15-27.json b/common/changes/@aws/workbench-core-swb-common-ui/feat-refresh-access-token_2022-09-02-15-27.json new file mode 100644 index 0000000000..23de8027d2 --- /dev/null +++ b/common/changes/@aws/workbench-core-swb-common-ui/feat-refresh-access-token_2022-09-02-15-27.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@aws/workbench-core-swb-common-ui", + "comment": "use refresh token to refresh expired access token", + "type": "patch" + } + ], + "packageName": "@aws/workbench-core-swb-common-ui" +} \ No newline at end of file diff --git a/workbench-core/swb-common-ui/src/api/apiHelper.ts b/workbench-core/swb-common-ui/src/api/apiHelper.ts index a9a2735dcd..ec9da4e256 100644 --- a/workbench-core/swb-common-ui/src/api/apiHelper.ts +++ b/workbench-core/swb-common-ui/src/api/apiHelper.ts @@ -69,4 +69,22 @@ const httpApiDelete = async (urlPath: string, params: any, withCredentials: bool return await fetchData(options); }; +// Response interceptor for API calls +axios.interceptors.response.use( + (response) => { + return response; + }, + // eslint-disable-next-line @typescript-eslint/typedef + async function (error) { + const originalRequest = error.config; + if (error.response.status === 401 && !originalRequest._retry) { + originalRequest._retry = true; + const response = await httpApiGet('refresh', {}); + localStorage.setItem('idToken', response.idToken); + return axios(originalRequest); + } + return Promise.reject(error); + } +); + export { httpApiGet, httpApiPost, httpApiPut, httpApiDelete }; diff --git a/workbench-core/swb-common-ui/src/context/AuthenticationContext.tsx b/workbench-core/swb-common-ui/src/context/AuthenticationContext.tsx index 5a454e7030..88156df93b 100644 --- a/workbench-core/swb-common-ui/src/context/AuthenticationContext.tsx +++ b/workbench-core/swb-common-ui/src/context/AuthenticationContext.tsx @@ -23,9 +23,6 @@ const AuthenticationContext: Context = createContext(); - // TODO: Fix API Lambda to store access_token and refresh_token in cookies correctly - // Once they're stored by the auth middleware, checkIfLoggedIn will return the correct value - useEffect(() => { if (typeof window !== 'undefined') { const idToken = localStorage.getItem('idToken'); From f0f32277722922545bdf5698a842adfddc58e0a5 Mon Sep 17 00:00:00 2001 From: FernandoAranda Date: Wed, 14 Sep 2022 10:17:50 -0700 Subject: [PATCH 040/621] docs(integration testing): update readme for integration testing (#487) * docs(integration testing): update readme for integration testing * docs(integration tests): fix spacing in readme * docs(fix yaml file references): fix yaml file references N * docs(integration tests docs): fix bot validation * docs(integration tests): update documentation of integration tests * docs(update integration testing readme): update integration testing readme * docs(integration test docs): update integration tests readme Co-authored-by: Fernando Aranda --- ...ation-testing-readme_2022-09-07-16-15.json | 10 + solutions/swb-reference/README.md | 243 +++++++++++++++++- solutions/swb-ui/package.json | 2 +- workbench-core/datasets-ui/package.json | 2 +- 4 files changed, 253 insertions(+), 4 deletions(-) create mode 100644 common/changes/@aws/workbench-core-datasets-ui/docs-integration-testing-readme_2022-09-07-16-15.json diff --git a/common/changes/@aws/workbench-core-datasets-ui/docs-integration-testing-readme_2022-09-07-16-15.json b/common/changes/@aws/workbench-core-datasets-ui/docs-integration-testing-readme_2022-09-07-16-15.json new file mode 100644 index 0000000000..29911c887e --- /dev/null +++ b/common/changes/@aws/workbench-core-datasets-ui/docs-integration-testing-readme_2022-09-07-16-15.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@aws/workbench-core-datasets-ui", + "comment": "documentation update", + "type": "none" + } + ], + "packageName": "@aws/workbench-core-datasets-ui" +} \ No newline at end of file diff --git a/solutions/swb-reference/README.md b/solutions/swb-reference/README.md index d703f117e3..4ba8958813 100644 --- a/solutions/swb-reference/README.md +++ b/solutions/swb-reference/README.md @@ -68,11 +68,250 @@ This step is necessary to setup Service Catalog portfolio and products ## Integration Tests +`swb-reference` package contains integration tests that run API tests against SWB APIs, they can be configured to run automatically as part of a GitHub workflow or CI/CD pipeline. + +Note: Integration tests will create resources in the environment they are executed against. + + +### Prerequisite + +Follow instructions [here](./SETUP_v2p1.md##installation) to setup installation of API and Postman collection. + +#### Resources to create through SWB UI: + +**Test Administrator:** Create an internal admin-role user for running integration tests. (**Note the rootUsername and rootPassword**) + + + +#### Resources for Advanced Tests + +- **Environment Type:** Follow instructions [here](../swb-reference/SETUP_v2p1.md###setup-project-configurations,-environmentType,-and-environmenttypeconfig) to create an environmentType. + +- **Environment Configuration:** Follow instructions [here](../swb-reference/SETUP_v2p1.md###setup-project-configurations,-environmentType,-and-environmenttypeconfig) to create a configuration for every corresponding environment type that was created. + + + To run integration tests 1. In `./integration-tests/config` make a copy of `example.yaml` and name it `.yaml`. Uncomment the attributes and provide the appropriate config value. -2. For `rootPasswordParamsStorePath`, go to the AWS console for your Main account, and [create a parameter](https://docs.aws.amazon.com/systems-manager/latest/userguide/parameter-create-console.html) to store the root user password. The name of the parameter should be `/swb//rootUser/password` -3. In this root directory run `STAGE= rushx integration-tests` + + +2. For `envTypeId` and `envType` open Postman Collection and select `List envTypes` inside `envType` folder (If Postman collection is not setup follow instructions [here](./SETUP_v2p1.md##postman-setup)) + + 1. Excecute `List envTypes` request, you should get a json response with the next information + ``` + { + "data": [ + { + "status": "APPROVED", + "createdAt": "2022-08-11T15:27:53.895Z", + "updatedBy": "########-####-####-####-############", + "createdBy": "########-####-####-####-############", + "name": "Sagemaker Jupyter Notebook", + "allowedRoleIds": [], + "resourceType": "envType", + "provisioningArtifactId": "", + "params": [ + { + "DefaultValue": "ml.t3.xlarge", + "IsNoEcho": false, + "ParameterConstraints": { + "AllowedValues": [] + }, + "ParameterType": "String", + "Description": "EC2 instance type to launch", + "ParameterKey": "InstanceType" + }, + { + "IsNoEcho": false, + "ParameterConstraints": { + "AllowedValues": [] + }, + "ParameterType": "Number", + "Description": "Number of idle minutes for auto stop to shutdown the instance (0 to disable auto-stop)", + "ParameterKey": "AutoStopIdleTimeInMinutes" + }, + { + "IsNoEcho": false, + "ParameterConstraints": { + "AllowedValues": [] + }, + "ParameterType": "String", + "Description": "The IAM policy to be associated with the launched workstation", + "ParameterKey": "IamPolicyDocument" + }, + { + "DefaultValue": "1.1.1.1/1", + "IsNoEcho": false, + "ParameterConstraints": { + "AllowedValues": [] + }, + "ParameterType": "String", + "Description": "CIDR to restrict IPs that can access the environment", + "ParameterKey": "CIDR" + } + ], + "updatedAt": "2022-08-11T15:27:53.895Z", + "sk": "ET#", + "owner": "########-####-####-####-############", + "description": "An Amazon SageMaker Jupyter Notebook", + "id": "########-####-####-####-############", + "pk": "ET#", + "productId": "", + "type": "sagemakerNotebook" + } + ] + } + ``` + If there are no environment types displayed please follow instructions [here](../swb-reference/SETUP_v2p1.md###setup-project-configurations,-environmentType,-and-environmenttypeconfig) to create a new environment type + + 2. Choose the Environment Type that integration test will use as default when creating any Environment and copy the values from properties `id` and `type` from request. + + 3. In `./integration-tests/config` directory assign `id` value to `envTypeId` property and `type` value to `envType` property in `.yaml` file + + +3. For `envTypeConfigId` open Postman Collection and select `List envTypeConfigs` inside `envTypeConfig` folder. + + 1. Replace `:envTypeId` in the URL request `{{API_URL}}/environmentTypes/:envTypeId/configurations` with value of the environment type id from the previous step. + + 2. Excecute `List envTypeConfigs` request, you should get a json response with the next information + ``` + { + "data": [ + { + "createdAt": "2022-08-11T15:29:15.935Z", + "updatedBy": "########-####-####-####-############", + "createdBy": "########-####-####-####-############", + "name": "Config 1", + "allowedRoleIds": [], + "resourceType": "envTypeConfig", + "provisioningArtifactId": "", + "params": [ + { + "value": "${iamPolicyDocument}", + "key": "IamPolicyDocument" + }, + { + "value": "ml.t3.medium", + "key": "InstanceType" + }, + { + "value": "0", + "key": "AutoStopIdleTimeInMinutes" + }, + { + "value": "0.0.0.0/0", + "key": "CIDR" + } + ], + "updatedAt": "2022-08-11T15:29:15.935Z", + "sk": "ET#}", + "owner": "########-####-####-####-############", + "description": "Description for config 1", + "id": "########-####-####-####-############", + "pk": "ETC", + "productId": "", + "type": "sagemakerNotebook" + } + ] + } + ``` + If there are no environment type configs displayed please follow instructions [here](../swb-reference/SETUP_v2p1.md###setup-project-configurations,-environmentType,-and-environmenttypeconfig) to create a new environment type config. + + 3. Choose the Environment Type Config that integration test will use as default when creating any Environment and copy the `id` value from the request. + + 4. In `./integration-tests/config` directory assign value copied to `envTypeConfigId` property in `.yaml` file + + +4. For `projectId` open Postman Collection and select `List projects` inside `projects` folder. + + 1. Excecute `List projects` request, you should get a json response with the next information + ``` + { + "data": [ + { + "subnetId": "subnet-", + "accountId": "", + "awsAccountId": "", + "environmentInstanceFiles": "s3://swb-dev-va-/environment-files", + "createdAt": "2022-01-28T22:42:20.296Z", + "updatedBy": "abc", + "vpcId": "vpc", + "envMgmtRoleArn": "arn:aws:iam:::role/swb-dev-va-env-mgmt", + "createdBy": "abc", + "name": "Project 1", + "encryptionKeyArn": "arn:aws:kms:us-east-1::key/########-####-####-####-############", + "resourceType": "project", + "externalId": "workbench", + "updatedAt": "2022-02-02T21:07:30.237Z", + "projectAdmins": [], + "indexId": "index-123", + "sk": "PROJ#", + "owner": "abc", + "description": "Example project 1", + "id": "########-####-####-####-############", + "pk": "########-####-####-####-############", + "hostingAccountHandlerRoleArn": "arn:aws:iam:::role/swb-dev-va-hosting-account-role" + } + ] + } + ``` + + 2. Choose the Project that integration test will use as default when creating any Environment and copy the `id` value from the request. + + 3. In `./integration-tests/config` directory assign value copied to `projectId` property in `.yaml` file + + +5. For `terminatedEnvId` we need the id of an environment that has been terminated, Postman collection request `List Environments` does not show terminated environments ,so we need to save the id of a stopped environment before we terminate it. + + 1. Create an environment by opening Postman Collection and select `Launch Environment` inside `environments` folder + + 2. Click on the Body section of the request and fill the next values + ``` + { + "description": "test 123", + "name": "testEnv1", + "envTypeId": "", + "envTypeConfigId": "", + "projectId": "", + "datasetIds": [], + "envType": "" + } + ``` + We can use the values from previous steps to fill the create request body + + 3. Execute `Launch Environment` request and copy the id property from the response + ``` + { + "id": "########-####-####-####-############", + "instanceId": "", + "cidr": "", + "description": "description", + "name": "environment Name", + "outputs": [], + ...... + ``` + + 4. In `./integration-tests/config` directory assign value copied to `terminatedEnvId` property in `.yaml` file. + + 5. Wait for environment to have status `COMPLETED`, in Postman collection select `List Environments` inside `environments` folder and excecute request, look for environment created and check if the property `status` has value `COMPLETED`, if it has `PENDING` status wait 5 minutes and excecute `List Environments` request again. + + 6. Once environment is completed select `Stop Environment` inside `environments` folder and replace the `:id` on the request URL `{{API_URL}}/environments/:id/stop` with the id of the environment created in previous step. + + 7. Excecute `Stop Environment` and wait until environment has status `STOPPED`, use `List Environments` request to monitor status. + + 8. Once environemnt is stopped select `Terminate Environment` inside `environments` folder and replace the `:id` on the request URL `{{API_URL}}/environments/:id` with the id of the environment created in previous step. + + 9. Excecute `Terminate Environment` and wait until environment is terminated, once the environment is terminated it will not be displayed in `List Environments` request anymore. + + +5. For `rootUsername`, type the email of the root user that is going to login into the application to run the integration tests, this is configured in `.yaml` file in `./src/config` directory for the installation step + +6. For `rootPasswordParamsStorePath`, go to the AWS console for your Main account, and [create a parameter](https://docs.aws.amazon.com/systems-manager/latest/userguide/parameter-create-console.html) to store the root user password. +The name of the parameter should be `/swb//rootUser/password` +Temporary root password is created and sent to root user after running the [post deployment step](./SETUP_v2p1.md###deploy-the-code) of installation and is updated the first time the root user login. + +7. In this root directory run `STAGE= rushx integration-tests` To use the framework for calling the SWBv2 API, create a `ClientSession` and then use the `resources` attribute to call the `CRUD` commands diff --git a/solutions/swb-ui/package.json b/solutions/swb-ui/package.json index 60c35c269e..1d347f5b54 100644 --- a/solutions/swb-ui/package.json +++ b/solutions/swb-ui/package.json @@ -34,8 +34,8 @@ "test:only": "jest --ci --coverage --passWithNoTests 2>&1" }, "dependencies": { - "@aws/workbench-core-environments": "workspace:*", "@aws/workbench-core-datasets-ui": "workspace:*", + "@aws/workbench-core-environments": "workspace:*", "@aws/workbench-core-swb-common-ui": "workspace:*", "next": "^12.1.5", "next-global-css": "^1.2.0", diff --git a/workbench-core/datasets-ui/package.json b/workbench-core/datasets-ui/package.json index 7d53d30980..4a6b2b4d87 100644 --- a/workbench-core/datasets-ui/package.json +++ b/workbench-core/datasets-ui/package.json @@ -40,8 +40,8 @@ }, "devDependencies": { "@aws/eslint-config-workbench-core-eslint-custom": "workspace:*", - "@cloudscape-design/components": "^3.0.27", "@cloudscape-design/collection-hooks": "^1.0.3", + "@cloudscape-design/components": "^3.0.27", "@rushstack/eslint-config": "^3.0.0", "@rushstack/heft": "^0.47.5", "@rushstack/heft-jest-plugin": "^0.3.28", From 4c6f0af005dd1189bf599549f2bb6012b385c9d3 Mon Sep 17 00:00:00 2001 From: Sanket Dharwadkar Date: Thu, 15 Sep 2022 11:58:30 -0700 Subject: [PATCH 041/621] test: aws accounts integ test (#488) * test: adding integ tests for aws account --- ...-accounts-integ-test_2022-09-14-16-54.json | 10 ++ ...-accounts-integ-test_2022-09-14-16-54.json | 10 ++ common/config/rush/pnpm-lock.yaml | 8 +- .../integration-tests/config/example.yaml | 13 +- .../support/complex/accountHelper.ts | 153 ++++++++++++++++++ .../complex/checkEventBridgePermission.ts | 1 - .../support/complex/datasetHelper.ts | 9 +- .../support/complex/deleteAccount.ts | 1 - .../integration-tests/support/resources.ts | 5 +- .../support/resources/accounts/account.ts | 28 ++++ .../support/resources/accounts/accounts.ts | 37 +++++ .../support/resources/datasets/dataset.ts | 2 +- .../support/utils/settings.ts | 10 +- .../isolated/aws-accounts/create.test.ts | 65 ++++++++ .../tests/multiStep/aws-account.test.ts | 58 +++++++ solutions/swb-reference/package.json | 2 + solutions/swb-reference/src/SWBStack.ts | 2 +- workbench-core/datasets/README.md | 2 +- workbench-core/datasets/src/iamHelper.test.ts | 131 +++++++++++++++ workbench-core/datasets/src/iamHelper.ts | 21 +++ .../src/services/accountService.ts | 8 +- 21 files changed, 549 insertions(+), 27 deletions(-) create mode 100644 common/changes/@aws/workbench-core-datasets/test-aws-accounts-integ-test_2022-09-14-16-54.json create mode 100644 common/changes/@aws/workbench-core-environments/test-aws-accounts-integ-test_2022-09-14-16-54.json create mode 100644 solutions/swb-reference/integration-tests/support/complex/accountHelper.ts delete mode 100644 solutions/swb-reference/integration-tests/support/complex/checkEventBridgePermission.ts delete mode 100644 solutions/swb-reference/integration-tests/support/complex/deleteAccount.ts create mode 100644 solutions/swb-reference/integration-tests/support/resources/accounts/account.ts create mode 100644 solutions/swb-reference/integration-tests/support/resources/accounts/accounts.ts create mode 100644 solutions/swb-reference/integration-tests/tests/isolated/aws-accounts/create.test.ts create mode 100644 solutions/swb-reference/integration-tests/tests/multiStep/aws-account.test.ts diff --git a/common/changes/@aws/workbench-core-datasets/test-aws-accounts-integ-test_2022-09-14-16-54.json b/common/changes/@aws/workbench-core-datasets/test-aws-accounts-integ-test_2022-09-14-16-54.json new file mode 100644 index 0000000000..e9d3718243 --- /dev/null +++ b/common/changes/@aws/workbench-core-datasets/test-aws-accounts-integ-test_2022-09-14-16-54.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@aws/workbench-core-datasets", + "comment": "test: add accounts integ test", + "type": "none" + } + ], + "packageName": "@aws/workbench-core-datasets" +} \ No newline at end of file diff --git a/common/changes/@aws/workbench-core-environments/test-aws-accounts-integ-test_2022-09-14-16-54.json b/common/changes/@aws/workbench-core-environments/test-aws-accounts-integ-test_2022-09-14-16-54.json new file mode 100644 index 0000000000..4d7a1c187a --- /dev/null +++ b/common/changes/@aws/workbench-core-environments/test-aws-accounts-integ-test_2022-09-14-16-54.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@aws/workbench-core-environments", + "comment": "test: add accounts integ test", + "type": "none" + } + ], + "packageName": "@aws/workbench-core-environments" +} \ No newline at end of file diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index 417f99c174..f5855e16e8 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -181,6 +181,7 @@ importers: ../../solutions/swb-reference: specifiers: + '@aws-cdk/aws-iam': ^1.159.0 '@aws-sdk/client-cloudformation': ^3.118.1 '@aws-sdk/client-dynamodb': ^3.118.1 '@aws-sdk/client-s3': ^3.118.1 @@ -263,6 +264,7 @@ importers: lodash: 4.17.21 uuid: 8.3.2 devDependencies: + '@aws-cdk/aws-iam': 1.159.0 '@aws-sdk/client-sagemaker': 3.130.0 '@aws/eslint-config-workbench-core-eslint-custom': link:../../workbench-core/eslint-custom '@rushstack/eslint-config': 3.0.0_eslint@8.16.0+typescript@4.7.2 @@ -1542,7 +1544,6 @@ packages: '@aws-cdk/cx-api': 1.159.0 '@aws-cdk/region-info': 1.159.0 constructs: 3.4.25 - dev: false /@aws-cdk/aws-kinesis/1.158.0: resolution: {integrity: sha512-Wkbtq3lV97MvXw4fhIjh7EVtLtIvgIg+5v8AJoD1GTOw/iHyZi0hXfNBqO+sTkqq51RJnHMDP6naQgcE6M9VxQ==} @@ -1699,7 +1700,6 @@ packages: dependencies: jsonschema: 1.4.1 semver: 7.3.7 - dev: false bundledDependencies: - jsonschema - semver @@ -1748,7 +1748,6 @@ packages: fs-extra: 9.1.0 ignore: 5.2.0 minimatch: 3.1.2 - dev: false bundledDependencies: - fs-extra - minimatch @@ -1785,7 +1784,6 @@ packages: dependencies: '@aws-cdk/cloud-assembly-schema': 1.159.0 semver: 7.3.7 - dev: false bundledDependencies: - semver @@ -1797,7 +1795,6 @@ packages: /@aws-cdk/region-info/1.159.0: resolution: {integrity: sha512-RrCXLeMcGPSuCR0LxAMTLrVlk1dG7d+BFfEnTV8BZDz6sxaGm4gs10xUZ2QQW4ebQdN5zn0xUnEiElO1PFR/Qg==} engines: {node: '>= 10.13.0 <13 || >=13.7.0'} - dev: false /@aws-crypto/crc32/2.0.0: resolution: {integrity: sha512-TvE1r2CUueyXOuHdEigYjIZVesInd9KN+K/TFFNfkkxRThiNxO6i4ZqqAVMoEjAamZZ1AA8WXJkjCz7YShHPQA==} @@ -8713,7 +8710,6 @@ packages: /constructs/3.4.25: resolution: {integrity: sha512-Ilp8VFOVHCS9+TQxp0o+kD43s4Q53rqfu26vrI5ubVAFtxTEKsUyCnugeqwjm+rtMHM1iNFGujk0tGVFNhSnxQ==} engines: {node: '>= 14.17.0'} - dev: false /content-disposition/0.5.4: resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} diff --git a/solutions/swb-reference/integration-tests/config/example.yaml b/solutions/swb-reference/integration-tests/config/example.yaml index 54de44104a..8bc41e3e00 100644 --- a/solutions/swb-reference/integration-tests/config/example.yaml +++ b/solutions/swb-reference/integration-tests/config/example.yaml @@ -2,15 +2,22 @@ ## Please deploy the swb-reference package before running the integration test suite. ## Settings declared here are merged with the main accounts CFN outputs to create integration test config items -# Default Env Config for launching a new environment +## Default Env Config for launching a new environment #envTypeId: '' #envTypeConfigId: '' #projectId: '' #envType: '' -# Negative Testing +## Negative Testing #terminatedEnvId: '' # id of an environment that has already been terminated -#Cognito Integ Test Client +## Cognito Integ Test Client #rootUsername: '' #rootPasswordParamStorePath: '' + +## Pre-requisite for running aws-accounts integration test +## Create the onboard_account stack in an external AWS account, and populate the following values below: +#awsAccountId: '' +#envMgmtRoleArn: '' +#hostingAccountHandlerRoleArn: '' +#encryptionKeyArn: '' \ No newline at end of file diff --git a/solutions/swb-reference/integration-tests/support/complex/accountHelper.ts b/solutions/swb-reference/integration-tests/support/complex/accountHelper.ts new file mode 100644 index 0000000000..5e5b0e47f5 --- /dev/null +++ b/solutions/swb-reference/integration-tests/support/complex/accountHelper.ts @@ -0,0 +1,153 @@ +import { PolicyDocument } from '@aws-cdk/aws-iam'; +import { GetBucketPolicyCommandOutput, PutBucketPolicyCommandInput } from '@aws-sdk/client-s3-control'; +import { AwsService } from '@aws/workbench-core-base'; +import { IamHelper } from '@aws/workbench-core-datasets'; +import _ from 'lodash'; +import Setup from '../setup'; +import Settings from '../utils/settings'; + +export class AccountHelper { + private _awsSdk: AwsService; + private _settings: Settings; + public constructor() { + const setup: Setup = new Setup(); + this._settings = setup.getSettings(); + this._awsSdk = setup.getMainAwsClient(); + } + + // TODO: Replace with Accounts list API call when it is available + public async listOnboardedAccounts(): Promise<{[id: string]: string}[]> { + const queryParams = { + index: 'getResourceByCreatedAt', + key: { name: 'resourceType', value: 'account' } + }; + const response = await this._awsSdk.helpers.ddb.query(queryParams).execute(); + let accounts: {[id: string]: string}[] = []; + if (response && response.Items) { + accounts = response.Items.map((item) => { + return item as unknown as {[id: string]: string}; + }); + } + return accounts; + } + + public async deleteDdbRecords(accountId: string, awsAccountId: string): Promise { + await this._awsSdk.helpers.ddb + .delete({ pk: `ACC#${accountId}`, sk: `ACC#${accountId}` }).execute(); + await this._awsSdk.helpers.ddb + .delete({ pk: `AWSACC#${awsAccountId}`, sk: `ACC#${accountId}` }).execute(); + } + + public async verifyBusAllowsAccount(awsAccountId: string): Promise { + const busName = 'default'; + const busRuleName = 'RouteHostEvents'; + const describeRuleParams = { Name: busRuleName, EventBusName: busName }; + + // Rule will always exist since other integ tests require an active hosting account onboarded + const busRule = await this._awsSdk.clients.eventBridge.describeRule(describeRuleParams); + + return _.includes(JSON.parse(busRule.EventPattern!).account, awsAccountId); + } + + public async removeBusPermissions(awsAccountId: string): Promise { + const busName = 'default'; + + const params = { + Action: 'events:PutEvents', + EventBusName: busName, + Principal: awsAccountId, + StatementId: `Allow-main-account-to-get-${awsAccountId}-events` + }; + + // Remove permission for main account to receive hosting account events + await this._awsSdk.clients.eventBridge.removePermission(params); + + const busRuleName = 'RouteHostEvents'; + const describeRuleParams = { Name: busRuleName, EventBusName: busName }; + + // Rule will always exist since other integ tests require an active hosting account onboarded + const busRule = await this._awsSdk.clients.eventBridge.describeRule(describeRuleParams); + + const putRuleParams = { + Name: busRuleName, + EventPattern: JSON.stringify({ + account: _.difference(JSON.parse(busRule.EventPattern!).account, [awsAccountId]), + source: [{ 'anything-but': ['aws.config', 'aws.cloudtrail', 'aws.ssm', 'aws.tag'] }], + 'detail-type': [{ 'anything-but': 'AWS API Call via CloudTrail' }] + }), + EventBusName: busName + }; + + // Update rule for main account event bus + await this._awsSdk.clients.eventBridge.putRule(putRuleParams); + } + + public async removeAccountFromKeyPolicy( + awsAccountId: string + ): Promise { + const mainAcctEncryptionArn = this._settings.get('MainAccountEncryptionKeyOutput'); + const keyId = mainAcctEncryptionArn.split('/').pop()!; + const keyPolicyResponse = await this._awsSdk.clients.kms.getKeyPolicy({ + KeyId: keyId, + PolicyName: 'default' + }); + let keyPolicy = PolicyDocument.fromJson(JSON.parse(keyPolicyResponse.Policy!)); + + keyPolicy = IamHelper.removePrincipalFromStatement( + keyPolicy, + 'main-key-share-statement', + `arn:aws:iam::${awsAccountId}:root` + ); + + const putPolicyParams = { + KeyId: keyId, + PolicyName: 'default', + Policy: JSON.stringify(keyPolicy.toJSON()) + }; + + // Update key policy + await this._awsSdk.clients.kms.putKeyPolicy(putPolicyParams); + } + + public async removeAccountFromBucketPolicy(awsAccountId: string): Promise { + const bucketName = this._settings.get('S3BucketArtifactsArnOutput').split(':').pop(); + const bucketPolicyResponse: GetBucketPolicyCommandOutput = await this._awsSdk.clients.s3.getBucketPolicy({ + Bucket: bucketName + }); + let bucketPolicy; + bucketPolicy = PolicyDocument.fromJson(JSON.parse(bucketPolicyResponse.Policy!)); + + bucketPolicy = IamHelper.removePrincipalFromStatement( + bucketPolicy, + 'List:environment-files', + `arn:aws:iam::${awsAccountId}:root` + ); + + bucketPolicy = IamHelper.removePrincipalFromStatement( + bucketPolicy, + 'Get:environment-files', + `arn:aws:iam::${awsAccountId}:root` + ); + + const putPolicyParams: PutBucketPolicyCommandInput = { + Bucket: bucketName, + Policy: JSON.stringify(bucketPolicy.toJSON()) + }; + + // Update bucket policy + await this._awsSdk.clients.s3.putBucketPolicy(putPolicyParams); + } + + public async deOnboardAccount(awsAccountId: string): Promise { + // Undo all operations that happen in: hostingAccountLifecycleService.initializeAccount() + + // Update main account default event bus to remove hosting account state change events + await this.removeBusPermissions(awsAccountId); + + // Remove account to artifactBucket's bucket policy + await this.removeAccountFromBucketPolicy(awsAccountId); + + // Update main account encryption key policy + await this.removeAccountFromKeyPolicy(awsAccountId); + } +} diff --git a/solutions/swb-reference/integration-tests/support/complex/checkEventBridgePermission.ts b/solutions/swb-reference/integration-tests/support/complex/checkEventBridgePermission.ts deleted file mode 100644 index 0e4f065803..0000000000 --- a/solutions/swb-reference/integration-tests/support/complex/checkEventBridgePermission.ts +++ /dev/null @@ -1 +0,0 @@ -//TODO: Implement this class diff --git a/solutions/swb-reference/integration-tests/support/complex/datasetHelper.ts b/solutions/swb-reference/integration-tests/support/complex/datasetHelper.ts index 8add37e534..441dc9d98a 100644 --- a/solutions/swb-reference/integration-tests/support/complex/datasetHelper.ts +++ b/solutions/swb-reference/integration-tests/support/complex/datasetHelper.ts @@ -1,14 +1,13 @@ import { AccessPoint } from '@aws-sdk/client-s3-control'; import { AwsService } from '@aws/workbench-core-base'; import _ from 'lodash'; -import ClientSession from '../clientSession'; +import Setup from '../setup'; export class DatasetHelper { private _awsSdk: AwsService; - private _adminSession: ClientSession; - public constructor(awsSdkClient: AwsService, adminSession: ClientSession) { - this._awsSdk = awsSdkClient; - this._adminSession = adminSession; + public constructor() { + const setup = new Setup(); + this._awsSdk = setup.getMainAwsClient(); } public async listAccessPoints(bucket: string, accountId: string): Promise> { diff --git a/solutions/swb-reference/integration-tests/support/complex/deleteAccount.ts b/solutions/swb-reference/integration-tests/support/complex/deleteAccount.ts deleted file mode 100644 index 12f8cc04b1..0000000000 --- a/solutions/swb-reference/integration-tests/support/complex/deleteAccount.ts +++ /dev/null @@ -1 +0,0 @@ -// TODO: Implement this class diff --git a/solutions/swb-reference/integration-tests/support/resources.ts b/solutions/swb-reference/integration-tests/support/resources.ts index 0ae56e4d63..d628ab0675 100644 --- a/solutions/swb-reference/integration-tests/support/resources.ts +++ b/solutions/swb-reference/integration-tests/support/resources.ts @@ -3,17 +3,20 @@ * SPDX-License-Identifier: Apache-2.0 */ import ClientSession from './clientSession'; +import Accounts from './resources/accounts/accounts'; import Datasets from './resources/datasets/datasets'; import Environments from './resources/environments/environments'; function getResources(clientSession: ClientSession): Resources { return { environments: new Environments(clientSession), - datasets: new Datasets(clientSession) + datasets: new Datasets(clientSession), + accounts: new Accounts(clientSession) }; } interface Resources { + accounts: Accounts; environments: Environments; datasets: Datasets; } diff --git a/solutions/swb-reference/integration-tests/support/resources/accounts/account.ts b/solutions/swb-reference/integration-tests/support/resources/accounts/account.ts new file mode 100644 index 0000000000..5b36a569b6 --- /dev/null +++ b/solutions/swb-reference/integration-tests/support/resources/accounts/account.ts @@ -0,0 +1,28 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import _ from 'lodash'; +import ClientSession from '../../clientSession'; +import { AccountHelper } from '../../complex/accountHelper'; +import Resource from '../base/resource'; + +export default class Account extends Resource { + public constructor(id: string, clientSession: ClientSession, parentApi: string) { + super(clientSession, 'aws-accounts', id, parentApi); + } + + protected async cleanup(): Promise { + const accountHelper = new AccountHelper(); + const settings = this._setup.getSettings(); + const existingAccounts = await accountHelper.listOnboardedAccounts(); + const resource = _.find(existingAccounts, { awsAccountId: settings.get('hostAwsAccountId') }); + + if (resource) { + const { id, awsAccountId } = resource; + await accountHelper.deOnboardAccount(awsAccountId); + await accountHelper.deleteDdbRecords(id, awsAccountId); + } + } +} diff --git a/solutions/swb-reference/integration-tests/support/resources/accounts/accounts.ts b/solutions/swb-reference/integration-tests/support/resources/accounts/accounts.ts new file mode 100644 index 0000000000..4fe7fb69eb --- /dev/null +++ b/solutions/swb-reference/integration-tests/support/resources/accounts/accounts.ts @@ -0,0 +1,37 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import ClientSession from '../../clientSession'; +import CollectionResource from '../base/collectionResource'; +import Account from './account'; + +export default class Accounts extends CollectionResource { + public constructor(clientSession: ClientSession) { + super(clientSession, 'aws-accounts', 'account'); + this._api = 'aws-accounts'; + } + + public account(id: string): Account { + return new Account(id, this._clientSession, this._api); + } + + protected _buildDefaults(resource: AccountCreateRequest): AccountCreateRequest { + return { + awsAccountId: 'sampleAccountId', + envMgmtRoleArn: resource.envMgmtRoleArn, + hostingAccountHandlerRoleArn: resource.hostingAccountHandlerRoleArn, + environmentInstanceFiles: resource.environmentInstanceFiles, + encryptionKeyArn: resource.encryptionKeyArn + }; + } +} + +interface AccountCreateRequest { + awsAccountId: string; + envMgmtRoleArn: string; + hostingAccountHandlerRoleArn: string; + environmentInstanceFiles: string; + encryptionKeyArn: string; +} diff --git a/solutions/swb-reference/integration-tests/support/resources/datasets/dataset.ts b/solutions/swb-reference/integration-tests/support/resources/datasets/dataset.ts index b7a349d0ed..9ae8411367 100644 --- a/solutions/swb-reference/integration-tests/support/resources/datasets/dataset.ts +++ b/solutions/swb-reference/integration-tests/support/resources/datasets/dataset.ts @@ -23,7 +23,7 @@ export default class Dataset extends Resource { const { storageName, path } = resource; // Delete DDB entries, and path folder from bucket (to prevent test resources polluting a prod env) - const datasetHelper = new DatasetHelper(this._setup.getMainAwsClient(), defAdminSession); + const datasetHelper = new DatasetHelper(); await datasetHelper.deleteS3Resources(storageName, path); await datasetHelper.deleteDdbRecords(this._id); } diff --git a/solutions/swb-reference/integration-tests/support/utils/settings.ts b/solutions/swb-reference/integration-tests/support/utils/settings.ts index 26e1a13106..9e9c35a331 100644 --- a/solutions/swb-reference/integration-tests/support/utils/settings.ts +++ b/solutions/swb-reference/integration-tests/support/utils/settings.ts @@ -39,11 +39,17 @@ interface Setting { dynamoDBTableOutput: string; StatusHandlerLambdaRoleOutput: string; + // Configs for AWS Account onboard test + hostAwsAccountId: string; + envMgmtRoleArn: string; + hostingAccountHandlerRoleArn: string; + encryptionKeyArn: string; + // Derived mainAccountId: string; } -type SettingKey = keyof Setting; +export type SettingKey = keyof Setting; /** * All settings used during the tests are stored here. The main advantage of having to use get/set methods * when accessing settings values is so that we can print an informative message when keys are missing. @@ -72,7 +78,7 @@ export default class Settings { return value; } - public optional(key: SettingKey, defaultValue: string): string { + public optional(key: SettingKey, defaultValue?: string): string | undefined { const value = this._content[key]; if (_.isNil(value) || (_.isString(value) && _.isEmpty(value))) return defaultValue; diff --git a/solutions/swb-reference/integration-tests/tests/isolated/aws-accounts/create.test.ts b/solutions/swb-reference/integration-tests/tests/isolated/aws-accounts/create.test.ts new file mode 100644 index 0000000000..e28f7117d8 --- /dev/null +++ b/solutions/swb-reference/integration-tests/tests/isolated/aws-accounts/create.test.ts @@ -0,0 +1,65 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +import _ from 'lodash'; +import ClientSession from '../../../support/clientSession'; +import { AccountHelper } from '../../../support/complex/accountHelper'; +import Setup from '../../../support/setup'; +import HttpError from '../../../support/utils/HttpError'; +import RandomTextGenerator from '../../../support/utils/randomTextGenerator'; +import { checkHttpError } from '../../../support/utils/utilities'; + +describe('aws-accounts create negative tests', () => { + const setup: Setup = new Setup(); + let adminSession: ClientSession; + const randomTextGenerator = new RandomTextGenerator(setup.getSettings().get('runId')); + + beforeEach(() => { + expect.hasAssertions(); + }); + + beforeAll(async () => { + adminSession = await setup.getDefaultAdminSession(); + }); + + afterAll(async () => { + await setup.cleanup(); + }); + + const validLaunchParameters = { + awsAccountId: randomTextGenerator.getFakeText('fakeAccount'), + envMgmtRoleArn: randomTextGenerator.getFakeText('fakeEnvMgmtRoleArn'), + hostingAccountHandlerRoleArn: randomTextGenerator.getFakeText('fakeHostingAccountHandlerRoleArn'), + environmentInstanceFiles: randomTextGenerator.getFakeText('fakeEnvironmentInstanceFiles'), + encryptionKeyArn: randomTextGenerator.getFakeText('fakeEncryptionKeyArn') + }; + + describe('Account already onboarded', () => { + test('awsAccountId', async () => { + const accountHelper = new AccountHelper(); + const invalidParam: { [id: string]: string } = { ...validLaunchParameters }; + const existingAccounts = await accountHelper.listOnboardedAccounts(); + + if (existingAccounts.length === 0) { + console.log('No hosting accounts have been onboarded. Skipping this test.'); + return; + } + + const existingAwsAccountId = _.first(existingAccounts)!.awsAccountId; + invalidParam.awsAccountId = existingAwsAccountId; + try { + await adminSession.resources.accounts.create(invalidParam, false); + } catch (e) { + checkHttpError( + e, + new HttpError(400, { + statusCode: 400, + error: 'Bad Request', + message: 'This AWS Account was found in DDB. Please provide the correct id value in request body' + }) + ); + } + }); + }); +}); diff --git a/solutions/swb-reference/integration-tests/tests/multiStep/aws-account.test.ts b/solutions/swb-reference/integration-tests/tests/multiStep/aws-account.test.ts new file mode 100644 index 0000000000..a25af3a3f5 --- /dev/null +++ b/solutions/swb-reference/integration-tests/tests/multiStep/aws-account.test.ts @@ -0,0 +1,58 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import _ from 'lodash'; +import ClientSession from '../../support/clientSession'; +import { AccountHelper } from '../../support/complex/accountHelper'; +import Setup from '../../support/setup'; +import Settings, { SettingKey } from '../../support/utils/settings'; + +describe('multiStep aws-account integration test', () => { + const setup: Setup = new Setup(); + const settings: Settings = setup.getSettings(); + let adminSession: ClientSession; + + beforeAll(async () => { + adminSession = await setup.getDefaultAdminSession(); + }); + + afterAll(async () => { + await setup.cleanup(); + }); + + function canTestAccountsApi(): boolean { + const settings = setup.getSettings(); + const requiredSettings = ['hostAwsAccountId', 'envMgmtRoleArn', 'hostingAccountHandlerRoleArn', + 'encryptionKeyArn', 'S3BucketArtifactsArnOutput']; + + // Check if all configs exist in given config + return _.every(requiredSettings, setting => { + return settings.optional(setting as SettingKey); + }); + } + + test('Onboarding new hosting account', async () => { + if (!canTestAccountsApi()) { + console.log('Config settings needed to test AWS Account onboarding are missing. Skipping this test') + return; + } + + const accountHelper = new AccountHelper(); + const artifactsBucketName = settings.get('S3BucketArtifactsArnOutput').split(':').pop(); + + // Create account + const accountCreateBody = { + awsAccountId: settings.get('hostAwsAccountId'), + envMgmtRoleArn: settings.get('envMgmtRoleArn'), + hostingAccountHandlerRoleArn: settings.get('hostingAccountHandlerRoleArn'), + environmentInstanceFiles: `s3://${artifactsBucketName}/environment-files`, + encryptionKeyArn: settings.get('encryptionKeyArn') + }; + + const {data: account} = await adminSession.resources.accounts.create(accountCreateBody, false); + expect(account).toMatchObject(accountCreateBody); + expect(await accountHelper.verifyBusAllowsAccount(account.awsAccountId)).toBe(true); + }); +}); diff --git a/solutions/swb-reference/package.json b/solutions/swb-reference/package.json index c9d4cef108..4b7e4836e3 100644 --- a/solutions/swb-reference/package.json +++ b/solutions/swb-reference/package.json @@ -60,6 +60,7 @@ "uuid": "^8.3.2" }, "devDependencies": { + "@aws-cdk/aws-iam": "^1.159.0", "@aws-sdk/client-cloudformation": "^3.118.1", "@aws-sdk/client-s3": "^3.118.1", "@aws-sdk/client-sagemaker": "^3.118.1", @@ -68,6 +69,7 @@ "@aws-sdk/client-sts": "^3.118.1", "@aws-sdk/types": "^3.110.0", "@aws/eslint-config-workbench-core-eslint-custom": "workspace:*", + "@aws/workbench-core-datasets": "workspace:*", "@rushstack/eslint-config": "^3.0.0", "@rushstack/heft": "^0.47.5", "@rushstack/heft-jest-plugin": "^0.3.28", diff --git a/solutions/swb-reference/src/SWBStack.ts b/solutions/swb-reference/src/SWBStack.ts index 2bbc88b397..4ceb815c4b 100644 --- a/solutions/swb-reference/src/SWBStack.ts +++ b/solutions/swb-reference/src/SWBStack.ts @@ -654,7 +654,7 @@ export class SWBStack extends Stack { sid: 'KMSAccess' }), new PolicyStatement({ - actions: ['events:DescribeRule', 'events:Put*'], + actions: ['events:DescribeRule', 'events:Put*', 'events:RemovePermission'], resources: ['*'], sid: 'EventbridgeAccess' }), diff --git a/workbench-core/datasets/README.md b/workbench-core/datasets/README.md index ac734f0d57..d31801fe79 100644 --- a/workbench-core/datasets/README.md +++ b/workbench-core/datasets/README.md @@ -7,7 +7,7 @@ | Statements | Branches | Functions | Lines | | --------------------------- | ----------------------- | ------------------------- | ----------------- | -| ![Statements](https://img.shields.io/badge/statements-95.19%25-brightgreen.svg?style=flat) | ![Branches](https://img.shields.io/badge/branches-92.66%25-brightgreen.svg?style=flat) | ![Functions](https://img.shields.io/badge/functions-95.89%25-brightgreen.svg?style=flat) | ![Lines](https://img.shields.io/badge/lines-96.46%25-brightgreen.svg?style=flat) | +| ![Statements](https://img.shields.io/badge/statements-95.37%25-brightgreen.svg?style=flat) | ![Branches](https://img.shields.io/badge/branches-92.98%25-brightgreen.svg?style=flat) | ![Functions](https://img.shields.io/badge/functions-96%25-brightgreen.svg?style=flat) | ![Lines](https://img.shields.io/badge/lines-96.59%25-brightgreen.svg?style=flat) | ## Description diff --git a/workbench-core/datasets/src/iamHelper.test.ts b/workbench-core/datasets/src/iamHelper.test.ts index 51ef31aad5..ada00ce6ee 100644 --- a/workbench-core/datasets/src/iamHelper.test.ts +++ b/workbench-core/datasets/src/iamHelper.test.ts @@ -383,6 +383,137 @@ describe('IamHelper', () => { }); }); + describe('removePrincipalFromStatement', () => { + it('returns empty doc if source doc is empty', () => { + const source = new PolicyDocument(); + const targetSid = 'StatementToCheck'; + const newPrincipal = `arn:aws:iam::newAccountId:root`; + try { + IamHelper.removePrincipalFromStatement(source, targetSid, newPrincipal); + } catch (err) { + expect(err.message).toBe('Cannot remove principal. Policy document is invalid'); + } + }); + + it('removes principal from new array before returning policy doc', () => { + const source = new PolicyDocument(); + const existingStatement = PolicyStatement.fromJson({ + Sid: 'StatementToCheck', + Principal: { + AWS: ['arn:aws:iam::oldAccountId:root', 'arn:aws:iam::newAccountId:root'] + }, + Action: '*', + Effect: 'Allow', + Resource: ['arn:aws:s3:::someBucket', 'arn:aws:s3:::someBucket/*'] + }); + + const expected = new PolicyDocument(); + const updatedStatement = PolicyStatement.fromJson({ + Sid: 'StatementToCheck', + Principal: { + AWS: ['arn:aws:iam::oldAccountId:root'] + }, + Action: '*', + Effect: 'Allow', + Resource: ['arn:aws:s3:::someBucket', 'arn:aws:s3:::someBucket/*'] + }); + source.addStatements(existingStatement); + expected.addStatements(updatedStatement); + const targetSid = 'StatementToCheck'; + const principalToRemove = 'arn:aws:iam::newAccountId:root'; + const response = IamHelper.removePrincipalFromStatement(source, targetSid, principalToRemove); + + expect(response.toJSON()).toStrictEqual(expected.toJSON()); + }); + + it('removes nothing if principal does not exist on policy doc', () => { + const source = new PolicyDocument(); + const existingStatement = PolicyStatement.fromJson({ + Sid: 'StatementToCheck', + Principal: { + AWS: ['arn:aws:iam::oldAccountId:root', 'arn:aws:iam::otherAccountId:root'] + }, + Action: '*', + Effect: 'Allow', + Resource: ['arn:aws:s3:::someBucket', 'arn:aws:s3:::someBucket/*'] + }); + + const expected = new PolicyDocument(); + const updatedStatement = PolicyStatement.fromJson({ + Sid: 'StatementToCheck', + Principal: { + AWS: ['arn:aws:iam::oldAccountId:root', 'arn:aws:iam::otherAccountId:root'] + }, + Action: '*', + Effect: 'Allow', + Resource: ['arn:aws:s3:::someBucket', 'arn:aws:s3:::someBucket/*'] + }); + source.addStatements(existingStatement); + expected.addStatements(updatedStatement); + const targetSid = 'StatementToCheck'; + const principalToRemove = 'arn:aws:iam::newAccountId:root'; + const response = IamHelper.removePrincipalFromStatement(source, targetSid, principalToRemove); + + expect(response.toJSON()).toStrictEqual(expected.toJSON()); + }); + + it('removes principal from existing array before returning policy doc', () => { + const source = new PolicyDocument(); + const existingStatement = PolicyStatement.fromJson({ + Sid: 'StatementToCheck', + Principal: { + AWS: ['arn:aws:iam::oldAccountId:root', 'arn:aws:iam::oldAccountId2:root', 'arn:aws:iam::newAccountId:root'] + }, + Action: '*', + Effect: 'Allow', + Resource: ['arn:aws:s3:::someBucket', 'arn:aws:s3:::someBucket/*'] + }); + + const expected = new PolicyDocument(); + const updatedStatement = PolicyStatement.fromJson({ + Sid: 'StatementToCheck', + Principal: { + AWS: [ + 'arn:aws:iam::oldAccountId:root', + 'arn:aws:iam::oldAccountId2:root' + ] + }, + Action: '*', + Effect: 'Allow', + Resource: ['arn:aws:s3:::someBucket', 'arn:aws:s3:::someBucket/*'] + }); + source.addStatements(existingStatement); + expected.addStatements(updatedStatement); + const targetSid = 'StatementToCheck'; + const principalToRemove = 'arn:aws:iam::newAccountId:root'; + const response = IamHelper.removePrincipalFromStatement(source, targetSid, principalToRemove); + + expect(response.toJSON()).toStrictEqual(expected.toJSON()); + }); + + it('removes principal from existing array before returning policy doc', () => { + const source = new PolicyDocument(); + const existingStatement = PolicyStatement.fromJson({ + Sid: 'StatementToCheck', + Principal: { + AWS: 'arn:aws:iam::newAccountId:root' + }, + Action: '*', + Effect: 'Allow', + Resource: ['arn:aws:s3:::someBucket', 'arn:aws:s3:::someBucket/*'] + }); + + source.addStatements(existingStatement); + const targetSid = 'StatementToCheck'; + const principalToRemove = 'arn:aws:iam::newAccountId:root'; + try{ + IamHelper.removePrincipalFromStatement(source, targetSid, principalToRemove); + } catch(err){ + expect(err.message).toStrictEqual('Cannot remove principal since only one principal is assigned'); + } + }); + }); + describe('compareStatementResource', () => { it('returns false when the source and target resources do not match.', () => { const source = PolicyStatement.fromJson( diff --git a/workbench-core/datasets/src/iamHelper.ts b/workbench-core/datasets/src/iamHelper.ts index 412e2dea0c..a4513975fd 100644 --- a/workbench-core/datasets/src/iamHelper.ts +++ b/workbench-core/datasets/src/iamHelper.ts @@ -52,6 +52,27 @@ export class IamHelper { ); } + public static removePrincipalFromStatement( + source: PolicyDocument, + targetSid: string, + awsPrincipal: string + ): PolicyDocument { + const policyObj = source.toJSON(); + if (!policyObj || !policyObj.Statement) { + throw new Error('Cannot remove principal. Policy document is invalid'); + } + const returnDoc = new PolicyDocument(); + _.forEach(policyObj.Statement, (s) => { + if (s.Sid === targetSid) { + if (!_.isArray(s.Principal.AWS)) throw new Error('Cannot remove principal since only one principal is assigned'); + s.Principal.AWS = _.difference(s.Principal.AWS, [awsPrincipal]); + } + const statement: PolicyStatement = PolicyStatement.fromJson(s); + returnDoc.addStatements(statement); + }); + return returnDoc; + } + public static addPrincipalToStatement( source: PolicyDocument, targetSid: string, diff --git a/workbench-core/environments/src/services/accountService.ts b/workbench-core/environments/src/services/accountService.ts index 30050b67d3..b37811a9ca 100644 --- a/workbench-core/environments/src/services/accountService.ts +++ b/workbench-core/environments/src/services/accountService.ts @@ -120,16 +120,14 @@ export default class AccountService { public async _validateCreate(accountMetadata: { [key: string]: string }): Promise { // Verify awsAccountId is specified if (_.isUndefined(accountMetadata.awsAccountId)) - throw new Error('Missing AWS Account ID in request body'); + throw Boom.badRequest('Missing AWS Account ID in request body'); // Check if AWS account ID already exists in DDB const key = { key: { name: 'pk', value: `AWSACC#${accountMetadata.awsAccountId}` } }; const ddbEntries = await this._aws.helpers.ddb.query(key).execute(); // When trying to onboard a new account, its AWS accound ID shouldn't be present in DDB if (ddbEntries && ddbEntries!.Count && ddbEntries.Count > 0) { - throw new Error( - 'This AWS Account was found in DDB. Please provide the correct id value in request body' - ); + throw Boom.badRequest('This AWS Account was found in DDB. Please provide the correct id value in request body'); } } @@ -138,7 +136,7 @@ export default class AccountService { if (!_.isUndefined(accountMetadata.awsAccountId)) { const ddbEntry = await this.getAccount(accountMetadata.id); if (ddbEntry.awsAccountId !== accountMetadata.awsAccountId) { - throw new Error('The AWS Account mapped to this accountId is different than the one provided'); + throw Boom.badRequest('The AWS Account mapped to this accountId is different than the one provided'); } } } From 57e205ad2e351e2f9f43672551021fb64ee13cde Mon Sep 17 00:00:00 2001 From: SanketD92 Date: Thu, 15 Sep 2022 19:07:49 +0000 Subject: [PATCH 042/621] docs: Auto README.md updates [skip ci] --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 86dfb38ea1..daea9e7c54 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ # Code Coverage | Statements | Branches | Functions | Lines | | --------------------------- | ----------------------- | ------------------------- | ----------------- | -| ![Statements](https://img.shields.io/badge/statements-87.21%25-yellow.svg?style=flat) | ![Branches](https://img.shields.io/badge/branches-85.31%25-yellow.svg?style=flat) | ![Functions](https://img.shields.io/badge/functions-88.55%25-yellow.svg?style=flat) | ![Lines](https://img.shields.io/badge/lines-87.71%25-yellow.svg?style=flat) | +| ![Statements](https://img.shields.io/badge/statements-87.27%25-yellow.svg?style=flat) | ![Branches](https://img.shields.io/badge/branches-85.37%25-yellow.svg?style=flat) | ![Functions](https://img.shields.io/badge/functions-88.59%25-yellow.svg?style=flat) | ![Lines](https://img.shields.io/badge/lines-87.76%25-yellow.svg?style=flat) | # Contributing Guidelines From 8ae475e58750f83e23e9da9d682e5be48441f6bf Mon Sep 17 00:00:00 2001 From: Sanket Dharwadkar Date: Fri, 16 Sep 2022 11:51:18 -0700 Subject: [PATCH 043/621] chore: separate env UI (#489) * feat: refactor UI components to separate packages --- .github/CODEOWNERS | 11 +- ...hore-separate-env-UI_2022-09-14-23-16.json | 10 + ...hore-separate-env-UI_2022-09-14-23-16.json | 10 + .../rush/browser-approved-packages.json | 12 + common/config/rush/pnpm-lock.yaml | 126 + rush.json | 12 + solutions/swb-ui/package.json | 1 + .../swb-ui/src/pages/environments/index.tsx | 469 +- .../swb-ui/src/pages/environments/new.tsx | 333 +- workbench-core/accounts-ui/.depcheckrc | 20 + workbench-core/accounts-ui/.eslintignore | 4 + workbench-core/accounts-ui/.eslintrc.js | 11 + workbench-core/accounts-ui/.npmignore | 33 + workbench-core/accounts-ui/LICENSE | 201 + workbench-core/accounts-ui/NOTICE | 4264 +++++++++++++++++ workbench-core/accounts-ui/README.md | 4 + .../accounts-ui/license-add-config.json | 32 + workbench-core/accounts-ui/package.json | 64 + .../accounts-ui}/src/api/projects.ts | 0 workbench-core/accounts-ui/src/index.tsx | 11 + .../accounts-ui}/src/models/Project.ts | 0 .../accounts-ui/src/pages/index.tsx | 6 + workbench-core/accounts-ui/tsconfig.json | 21 + workbench-core/environments-ui/.depcheckrc | 20 + workbench-core/environments-ui/.eslintignore | 4 + workbench-core/environments-ui/.eslintrc.js | 11 + workbench-core/environments-ui/.npmignore | 33 + workbench-core/environments-ui/LICENSE | 201 + workbench-core/environments-ui/NOTICE | 4264 +++++++++++++++++ workbench-core/environments-ui/README.md | 4 + .../environments-ui/license-add-config.json | 32 + workbench-core/environments-ui/package.json | 67 + .../src/api/environmentTypeConfigs.ts | 0 .../src/api/environmentTypes.ts | 0 .../environments-ui}/src/api/environments.ts | 0 .../src/components/EnvTypeCards.tsx | 2 +- .../src/components/EnvTypeConfigCards.tsx | 0 .../components/EnvironmentConnectModal.tsx | 5 +- .../workspacesColumnDefinitions.tsx | 0 .../workspacesFilteringOptions.tsx | 0 .../workspacesFilteringProperties.tsx | 0 workbench-core/environments-ui/src/index.tsx | 12 + .../src/models/Environment.ts | 0 .../src/models/EnvironmentType.ts | 0 .../src/models/EnvironmentTypeConfig.ts | 0 .../src/pages/api/environments.ts | 0 .../src/pages/environments/index.tsx | 473 ++ .../src/pages/environments/new.tsx | 337 ++ workbench-core/environments-ui/tsconfig.json | 21 + 49 files changed, 10338 insertions(+), 803 deletions(-) create mode 100644 common/changes/@aws/workbench-core-accounts-ui/chore-separate-env-UI_2022-09-14-23-16.json create mode 100644 common/changes/@aws/workbench-core-environments-ui/chore-separate-env-UI_2022-09-14-23-16.json create mode 100644 workbench-core/accounts-ui/.depcheckrc create mode 100644 workbench-core/accounts-ui/.eslintignore create mode 100644 workbench-core/accounts-ui/.eslintrc.js create mode 100644 workbench-core/accounts-ui/.npmignore create mode 100644 workbench-core/accounts-ui/LICENSE create mode 100644 workbench-core/accounts-ui/NOTICE create mode 100644 workbench-core/accounts-ui/README.md create mode 100644 workbench-core/accounts-ui/license-add-config.json create mode 100644 workbench-core/accounts-ui/package.json rename {solutions/swb-ui => workbench-core/accounts-ui}/src/api/projects.ts (100%) create mode 100644 workbench-core/accounts-ui/src/index.tsx rename {solutions/swb-ui => workbench-core/accounts-ui}/src/models/Project.ts (100%) create mode 100644 workbench-core/accounts-ui/src/pages/index.tsx create mode 100644 workbench-core/accounts-ui/tsconfig.json create mode 100644 workbench-core/environments-ui/.depcheckrc create mode 100644 workbench-core/environments-ui/.eslintignore create mode 100644 workbench-core/environments-ui/.eslintrc.js create mode 100644 workbench-core/environments-ui/.npmignore create mode 100644 workbench-core/environments-ui/LICENSE create mode 100644 workbench-core/environments-ui/NOTICE create mode 100644 workbench-core/environments-ui/README.md create mode 100644 workbench-core/environments-ui/license-add-config.json create mode 100644 workbench-core/environments-ui/package.json rename {solutions/swb-ui => workbench-core/environments-ui}/src/api/environmentTypeConfigs.ts (100%) rename {solutions/swb-ui => workbench-core/environments-ui}/src/api/environmentTypes.ts (100%) rename {solutions/swb-ui => workbench-core/environments-ui}/src/api/environments.ts (100%) rename {solutions/swb-ui => workbench-core/environments-ui}/src/components/EnvTypeCards.tsx (98%) rename {solutions/swb-ui => workbench-core/environments-ui}/src/components/EnvTypeConfigCards.tsx (100%) rename {solutions/swb-ui => workbench-core/environments-ui}/src/components/EnvironmentConnectModal.tsx (94%) rename {solutions/swb-ui => workbench-core/environments-ui}/src/environments-table-config/workspacesColumnDefinitions.tsx (100%) rename {solutions/swb-ui => workbench-core/environments-ui}/src/environments-table-config/workspacesFilteringOptions.tsx (100%) rename {solutions/swb-ui => workbench-core/environments-ui}/src/environments-table-config/workspacesFilteringProperties.tsx (100%) create mode 100644 workbench-core/environments-ui/src/index.tsx rename {solutions/swb-ui => workbench-core/environments-ui}/src/models/Environment.ts (100%) rename {solutions/swb-ui => workbench-core/environments-ui}/src/models/EnvironmentType.ts (100%) rename {solutions/swb-ui => workbench-core/environments-ui}/src/models/EnvironmentTypeConfig.ts (100%) rename {solutions/swb-ui => workbench-core/environments-ui}/src/pages/api/environments.ts (100%) create mode 100644 workbench-core/environments-ui/src/pages/environments/index.tsx create mode 100644 workbench-core/environments-ui/src/pages/environments/new.tsx create mode 100644 workbench-core/environments-ui/tsconfig.json diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 4a62c406bd..e63eb878c9 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,4 +1,5 @@ * @awslabs/aws-ma-foundation +rush.json @awslabs/aws-ma-foundation @awslabs/aws-ma-swb common/config/rush/browser-approved-packages.json common/config/rush/nonbrowser-approved-packages.json common/config/rush/pnpm-lock.yaml @@ -15,4 +16,12 @@ common/changes/@aws/swb-app @awslabs/aws-ma-swb workbench-core/base @awslabs/aws-ma-swb common/changes/@aws/workbench-core-base @awslabs/aws-ma-swb workbench-core/environments @awslabs/aws-ma-swb -common/changes/@aws/workbench-core-environments @awslabs/aws-ma-swb \ No newline at end of file +common/changes/@aws/workbench-core-environments @awslabs/aws-ma-swb +workbench-core/environments-ui @awslabs/aws-ma-swb +common/changes/@aws/workbench-core-environments-ui @awslabs/aws-ma-swb +workbench-core/accounts-ui @awslabs/aws-ma-swb +common/changes/@aws/workbench-core-accounts-ui @awslabs/aws-ma-swb +workbench-core/datasets @awslabs/aws-ma-foundation @awslabs/aws-ma-swb +common/changes/@aws/workbench-core-datasets @awslabs/aws-ma-foundation @awslabs/aws-ma-swb +workbench-core/datasets-ui @awslabs/aws-ma-foundation @awslabs/aws-ma-swb +common/changes/@aws/workbench-core-datasets-ui @awslabs/aws-ma-foundation @awslabs/aws-ma-swb \ No newline at end of file diff --git a/common/changes/@aws/workbench-core-accounts-ui/chore-separate-env-UI_2022-09-14-23-16.json b/common/changes/@aws/workbench-core-accounts-ui/chore-separate-env-UI_2022-09-14-23-16.json new file mode 100644 index 0000000000..d37c158dcd --- /dev/null +++ b/common/changes/@aws/workbench-core-accounts-ui/chore-separate-env-UI_2022-09-14-23-16.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@aws/workbench-core-accounts-ui", + "comment": "feat: separate out UI packages", + "type": "minor" + } + ], + "packageName": "@aws/workbench-core-accounts-ui" +} \ No newline at end of file diff --git a/common/changes/@aws/workbench-core-environments-ui/chore-separate-env-UI_2022-09-14-23-16.json b/common/changes/@aws/workbench-core-environments-ui/chore-separate-env-UI_2022-09-14-23-16.json new file mode 100644 index 0000000000..f6f07b1d29 --- /dev/null +++ b/common/changes/@aws/workbench-core-environments-ui/chore-separate-env-UI_2022-09-14-23-16.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@aws/workbench-core-environments-ui", + "comment": "feat: separate out UI packages", + "type": "minor" + } + ], + "packageName": "@aws/workbench-core-environments-ui" +} \ No newline at end of file diff --git a/common/config/rush/browser-approved-packages.json b/common/config/rush/browser-approved-packages.json index 45c8e895ab..978f280d5b 100644 --- a/common/config/rush/browser-approved-packages.json +++ b/common/config/rush/browser-approved-packages.json @@ -126,6 +126,10 @@ "name": "@aws/workbench-core-environments", "allowedCategories": [ "production" ] }, + { + "name": "@aws/workbench-core-environments-ui", + "allowedCategories": [ "production" ] + }, { "name": "@aws/workbench-core-example-express", "allowedCategories": [ "prototypes" ] @@ -138,10 +142,18 @@ "name": "@aws/workbench-core-logging", "allowedCategories": [ "production", "prototypes" ] }, + { + "name": "@aws/workbench-core-accounts-ui", + "allowedCategories": [ "production" ] + }, { "name": "@aws/workbench-core-swb-common-ui", "allowedCategories": [ "production" ] }, + { + "name": "@aws/workbench-core-swb-datasets-ui", + "allowedCategories": [ "production" ] + }, { "name": "@awsui/collection-hooks", "allowedCategories": [ "production", "prototypes" ] diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index f5855e16e8..7daac499a5 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -307,6 +307,7 @@ importers: '@aws/eslint-config-workbench-core-eslint-custom': workspace:* '@aws/workbench-core-datasets-ui': workspace:* '@aws/workbench-core-environments': workspace:* + '@aws/workbench-core-environments-ui': workspace:* '@aws/workbench-core-swb-common-ui': workspace:* '@babel/core': ^7.17.9 '@babel/preset-env': ^7.16.11 @@ -354,6 +355,7 @@ importers: dependencies: '@aws/workbench-core-datasets-ui': link:../../workbench-core/datasets-ui '@aws/workbench-core-environments': link:../../workbench-core/environments + '@aws/workbench-core-environments-ui': link:../../workbench-core/environments-ui '@aws/workbench-core-swb-common-ui': link:../../workbench-core/swb-common-ui next: 12.1.6_fe3859d3c8c94a7c7e52ff5ea4f038fc next-global-css: 1.3.1 @@ -444,6 +446,65 @@ importers: ts-node: 10.8.0_0c1b05e96b429c05fff7f0589ebca14f typescript: 4.7.2 + ../../workbench-core/accounts-ui: + specifiers: + '@aws/eslint-config-workbench-core-eslint-custom': workspace:* + '@aws/workbench-core-swb-common-ui': workspace:* + '@cloudscape-design/collection-hooks': ^1.0.3 + '@cloudscape-design/components': ^3.0.27 + '@rushstack/eslint-config': ^3.0.0 + '@rushstack/heft': ^0.47.5 + '@rushstack/heft-jest-plugin': ^0.3.28 + '@rushstack/heft-node-rig': ^1.10.5 + '@types/heft-jest': 1.0.2 + '@types/node': ^14 + '@types/react': 17.0.38 + '@types/react-dom': ^18.0.6 + depcheck: ^1.4.3 + eslint: ^8.7.0 + eslint-plugin-import: ^2.26.0 + istanbul-badges-readme: 1.8.1 + license-check-and-add: ^4.0.5 + license-checker: ^25.0.1 + next: ^12.1.5 + npm-package-json-lint: ^6.3.0 + npm-package-json-lint-config-default: ^5.0.0 + react: 17.0.2 + react-dom: 17.0.2 + sass: ^1.49.8 + sort-package-json: ^1.57.0 + swr: ^1.3.0 + typescript: ^4.5.2 + dependencies: + '@aws/workbench-core-swb-common-ui': link:../swb-common-ui + next: 12.1.6_757e1cdb914f1581201e1269321c5a94 + react: 17.0.2 + react-dom: 17.0.2_react@17.0.2 + swr: 1.3.0_react@17.0.2 + typescript: 4.7.4 + devDependencies: + '@aws/eslint-config-workbench-core-eslint-custom': link:../eslint-custom + '@cloudscape-design/collection-hooks': 1.0.3_react@17.0.2 + '@cloudscape-design/components': 3.0.27_b3482aaf5744fc7c2aeb7941b0e0a78f + '@rushstack/eslint-config': 3.0.0_eslint@8.16.0+typescript@4.7.4 + '@rushstack/heft': 0.47.5 + '@rushstack/heft-jest-plugin': 0.3.28_@rushstack+heft@0.47.5 + '@rushstack/heft-node-rig': 1.10.5_@rushstack+heft@0.47.5 + '@types/heft-jest': 1.0.2 + '@types/node': 14.18.20 + '@types/react': 17.0.38 + '@types/react-dom': 18.0.6 + depcheck: 1.4.3 + eslint: 8.16.0 + eslint-plugin-import: 2.26.0_eslint@8.16.0 + istanbul-badges-readme: 1.8.1 + license-check-and-add: 4.0.5 + license-checker: 25.0.1 + npm-package-json-lint: 6.3.0 + npm-package-json-lint-config-default: 5.0.0_npm-package-json-lint@6.3.0 + sass: 1.52.2 + sort-package-json: 1.57.0 + ../../workbench-core/audit: specifiers: '@aws/eslint-config-workbench-core-eslint-custom': workspace:* @@ -943,6 +1004,71 @@ importers: ts-jest: 27.1.5_4e623839ecfc1b0242d7f262e6e0b1df typescript: 4.7.2 + ../../workbench-core/environments-ui: + specifiers: + '@aws/eslint-config-workbench-core-eslint-custom': workspace:* + '@aws/workbench-core-accounts-ui': workspace:* + '@aws/workbench-core-datasets-ui': workspace:* + '@aws/workbench-core-environments': workspace:* + '@aws/workbench-core-swb-common-ui': workspace:* + '@cloudscape-design/collection-hooks': ^1.0.3 + '@cloudscape-design/components': ^3.0.27 + '@rushstack/eslint-config': ^3.0.0 + '@rushstack/heft': ^0.47.5 + '@rushstack/heft-jest-plugin': ^0.3.28 + '@rushstack/heft-node-rig': ^1.10.5 + '@types/heft-jest': 1.0.2 + '@types/node': ^14 + '@types/react': 17.0.38 + '@types/react-dom': ^18.0.6 + depcheck: ^1.4.3 + eslint: ^8.7.0 + eslint-plugin-import: ^2.26.0 + istanbul-badges-readme: 1.8.1 + license-check-and-add: ^4.0.5 + license-checker: ^25.0.1 + next: ^12.1.5 + npm-package-json-lint: ^6.3.0 + npm-package-json-lint-config-default: ^5.0.0 + react: 17.0.2 + react-dom: 17.0.2 + sass: ^1.49.8 + sort-package-json: ^1.57.0 + swr: ^1.3.0 + typescript: ^4.5.2 + dependencies: + '@aws/workbench-core-accounts-ui': link:../accounts-ui + '@aws/workbench-core-datasets-ui': link:../datasets-ui + '@aws/workbench-core-environments': link:../environments + '@aws/workbench-core-swb-common-ui': link:../swb-common-ui + next: 12.1.6_757e1cdb914f1581201e1269321c5a94 + react: 17.0.2 + react-dom: 17.0.2_react@17.0.2 + swr: 1.3.0_react@17.0.2 + typescript: 4.7.4 + devDependencies: + '@aws/eslint-config-workbench-core-eslint-custom': link:../eslint-custom + '@cloudscape-design/collection-hooks': 1.0.3_react@17.0.2 + '@cloudscape-design/components': 3.0.27_b3482aaf5744fc7c2aeb7941b0e0a78f + '@rushstack/eslint-config': 3.0.0_eslint@8.16.0+typescript@4.7.4 + '@rushstack/heft': 0.47.5 + '@rushstack/heft-jest-plugin': 0.3.28_@rushstack+heft@0.47.5 + '@rushstack/heft-node-rig': 1.10.5_@rushstack+heft@0.47.5 + '@types/heft-jest': 1.0.2 + '@types/node': 14.18.20 + '@types/react': 17.0.38 + '@types/react-dom': 18.0.6 + depcheck: 1.4.3 + eslint: 8.16.0 + eslint-plugin-import: 2.26.0_eslint@8.16.0 + istanbul-badges-readme: 1.8.1 + license-check-and-add: 4.0.5 + license-checker: 25.0.1 + npm-package-json-lint: 6.3.0 + npm-package-json-lint-config-default: 5.0.0_npm-package-json-lint@6.3.0 + sass: 1.52.2 + sort-package-json: 1.57.0 + ../../workbench-core/eslint-custom: specifiers: '@rushstack/eslint-config': ^3.0.0 diff --git a/rush.json b/rush.json index 72632c9acb..d3efe94acc 100644 --- a/rush.json +++ b/rush.json @@ -107,6 +107,18 @@ "reviewCategory": "production", "shouldPublish": true }, + { + "packageName": "@aws/workbench-core-environments-ui", + "projectFolder": "workbench-core/environments-ui", + "reviewCategory": "production", + "shouldPublish": true + }, + { + "packageName": "@aws/workbench-core-accounts-ui", + "projectFolder": "workbench-core/accounts-ui", + "reviewCategory": "production", + "shouldPublish": true + }, { "packageName": "@aws/workbench-core-example-express", "projectFolder": "workbench-core/example/express", diff --git a/solutions/swb-ui/package.json b/solutions/swb-ui/package.json index 1d347f5b54..5d9b5ff666 100644 --- a/solutions/swb-ui/package.json +++ b/solutions/swb-ui/package.json @@ -35,6 +35,7 @@ }, "dependencies": { "@aws/workbench-core-datasets-ui": "workspace:*", + "@aws/workbench-core-environments-ui": "workspace:*", "@aws/workbench-core-environments": "workspace:*", "@aws/workbench-core-swb-common-ui": "workspace:*", "next": "^12.1.5", diff --git a/solutions/swb-ui/src/pages/environments/index.tsx b/solutions/swb-ui/src/pages/environments/index.tsx index e445afb1dc..6ce4e2e2dd 100644 --- a/solutions/swb-ui/src/pages/environments/index.tsx +++ b/solutions/swb-ui/src/pages/environments/index.tsx @@ -3,471 +3,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { - datei18nStrings, - relativeOptions, - convertToAbsoluteRange, - isValidRangeFunction, - getFilterCounterText, - TableEmptyDisplay, - TableNoMatchDisplay, - i18nStrings, - paginationLables, - useNotifications, - BaseLayout -} from '@aws/workbench-core-swb-common-ui'; -import { useCollection } from '@cloudscape-design/collection-hooks'; -import { - Box, - BreadcrumbGroupProps, - Button, - CollectionPreferences, - DateRangePicker, - DateRangePickerProps, - Header, - Pagination, - PaginationProps, - PropertyFilter, - SpaceBetween, - Table, - StatusIndicator -} from '@cloudscape-design/components'; -import { FlashbarProps } from '@cloudscape-design/components/flashbar'; +import { EnvironmentsPage } from '@aws/workbench-core-environments-ui'; -import type { NextPage } from 'next'; -import { useRouter } from 'next/router'; -import React, { SetStateAction, useEffect, useState } from 'react'; -import { useEnvironments, terminate, start, stop, connect } from '../../api/environments'; -import EnvironmentConnectModal from '../../components/EnvironmentConnectModal'; -import { - columnDefinitions, - searchableColumns -} from '../../environments-table-config/workspacesColumnDefinitions'; -import { filteringOptions } from '../../environments-table-config/workspacesFilteringOptions'; -import { filteringProperties } from '../../environments-table-config/workspacesFilteringProperties'; -import { EnvironmentConnectResponse, EnvironmentsTableFilter } from '../../models/Environment'; - -const Environment: NextPage = () => { - // For functions to return content specific to the table - const itemType: string = 'workspace'; - // App settings constant - - const pageSizeOptions = [ - { label: '20', value: 20 }, - { label: '30', value: 30 }, - { label: '50', value: 50 } - ]; - const [filterParams, setFilterParams] = useState({ - paginationToken: '', - pageSize: pageSizeOptions[0]?.value, - descending: 'createdAt', - currentPageIndex: 1, - paginationTokens: new Map().set(1, ''), - hasOpenEndPagination: true, - pageCount: 1 - }); - const { environments, mutate, paginationToken, areEnvironmentsLoading } = useEnvironments({ - ascending: filterParams.ascending, - createdAtFrom: filterParams.createdAtFrom, - createdAtTo: filterParams.createdAtTo, - descending: filterParams.descending, - pageSize: filterParams.pageSize, - paginationToken: filterParams.paginationToken - }); - const [error, setError] = useState(''); - const router = useRouter(); - const { message, notificationType } = router.query; - const { displayNotification, closeNotification } = useNotifications(); - - const [hasInitialNotificationBeenShown, setHasInitialNotificationBeenShown] = useState(false); - if (!!message && !!notificationType && !hasInitialNotificationBeenShown) { - const envMessageId = 'EnvironmentMessage'; - const notification = { - type: notificationType as FlashbarProps.Type, - dismissible: true, - dismissLabel: 'Dismiss message', - onDismiss: () => { - closeNotification(envMessageId); - }, - content: message, - id: envMessageId - }; - displayNotification(envMessageId, notification); - setHasInitialNotificationBeenShown(true); - } - - // App layout constants - const breadcrumbs: BreadcrumbGroupProps.Item[] = [ - { - text: 'Service Workbench', - href: '/' - }, - { - text: 'Workspaces', - href: '/environments' - } - ]; - - // Date filter constants - const [dateFilter, setDateFilter] = React.useState(null); - - // Property and date filter collections - const { items, filteredItemsCount, collectionProps, propertyFilterProps } = useCollection(environments, { - filtering: { - empty: TableEmptyDisplay(itemType), - noMatch: TableNoMatchDisplay(itemType), - // eslint-disable-next-line @typescript-eslint/no-explicit-any - filteringFunction: (item: any, filteringText): any => { - const filteringTextLowerCase = filteringText.toLowerCase(); - - return ( - searchableColumns - // eslint-disable-next-line security/detect-object-injection - .map((key) => item[key]) - .some( - (value) => typeof value === 'string' && value.toLowerCase().indexOf(filteringTextLowerCase) > -1 - ) - ); - } - }, - propertyFiltering: { - filteringProperties: filteringProperties, - empty: TableEmptyDisplay(itemType), - noMatch: TableNoMatchDisplay(itemType) - }, - - sorting: {}, - selection: {} - }); - - // Action button constants - // Constant buttons should be enabled based on statuses in the array - const connectButtonEnableStatuses: string[] = ['AVAILABLE', 'STARTED', 'COMPLETED']; - const startButtonEnableStatuses: string[] = ['STOPPED']; - const stopButtonEnableStatuses: string[] = ['AVAILABLE', 'STARTED', 'COMPLETED']; - const terminateButtonEnableStatuses: string[] = ['FAILED', 'STOPPED']; - // Constant buttons should show loading based on statuses in the array - const stopButtonLoadingStatuses: string[] = ['STOPPING']; - const terminateButtonLoadingStatuses: string[] = ['TERMINATING']; - const startButtonLoadingStatuses: string[] = ['STARTING']; - const [terminatingIds, setTerminatingIds] = useState(new Set()); - const [stoppingIds, setStoppingIds] = useState(new Set()); - - const [startingIds, setstartingIds] = useState(new Set()); - const [showConnectEnvironmentModal, setShowConnectEnvironmentModal] = useState(false); - const [isLoadingEnvConnection, setIsLoadingEnvConnection] = useState(false); - const [envConnectResponse, setEnvConnectResponse] = useState({ - instructionResponse: '', - authCredResponse: {} - }); - - const isOneItemSelected = (): boolean | undefined => { - return collectionProps.selectedItems && collectionProps.selectedItems.length === 1; - }; - const getEnvironmentStatus = (): string => { - const selectedItems = collectionProps.selectedItems; - if (selectedItems !== undefined && isOneItemSelected()) { - return collectionProps.selectedItems?.at(0).workspaceStatus; - } - return ''; - }; - - const getSelectedId = (): string => { - if (isOneItemSelected()) { - return collectionProps.selectedItems?.at(0).id; - } - return ''; - }; - - const executeAction = async (action: string): Promise => { - let actionLabel = 'Retrieve Workspaces Data'; - if (isOneItemSelected()) { - const id = collectionProps.selectedItems?.at(0).id; - try { - setError(''); - switch (action) { - case 'TERMINATE': - setTerminatingIds((prev) => new Set(prev.add(id))); - actionLabel = 'Terminate Workspace'; - await terminate(id); - break; - case 'STOP': - setStoppingIds((prev) => new Set(prev.add(id))); - actionLabel = 'Stop Workspace'; - await stop(id); - break; - case 'START': - setstartingIds((prev) => new Set(prev.add(id))); - actionLabel = 'Start Workspace'; - await start(id); - break; - case 'CONNECT': - const connectingEnvId = collectionProps.selectedItems ? collectionProps.selectedItems[0].id : ''; - setIsLoadingEnvConnection(true); - const response = await connect(connectingEnvId); - setEnvConnectResponse(response); - setIsLoadingEnvConnection(false); - actionLabel = 'Connect to Workspace'; - setShowConnectEnvironmentModal(true); - break; - } - await mutate(); - } catch { - setError(`There was a problem trying to ${actionLabel}.`); - } finally { - setTerminatingIds((prev) => { - prev.delete(id); - return new Set(prev); - }); - setStoppingIds((prev) => { - prev.delete(id); - return new Set(prev); - }); - - setstartingIds((prev) => { - prev.delete(id); - return new Set(prev); - }); - } - } - }; - - const onPaginationChange = (detail: PaginationProps.ChangeDetail): void => { - //when clicking next page, this will check if there are more pages to load, if not, disable next button and go back to previous page - if (!filterParams.paginationTokens.get(detail.currentPageIndex) && detail.currentPageIndex > 1) { - setFilterParams((prevState) => ({ - ...prevState, - currentPageIndex: detail.currentPageIndex - 1, - hasOpenEndPagination: false - })); - return; - } - //get previously saved token of page clicked - const paginationToken = filterParams.paginationTokens.get(detail.currentPageIndex) || ''; - - //update pages shown in pagination if we discover a new page and set current page - setFilterParams((prevState) => ({ - ...prevState, - pageCount: - detail.currentPageIndex > prevState.pageCount ? detail.currentPageIndex : prevState.pageCount, - currentPageIndex: detail.currentPageIndex, - paginationToken: paginationToken - })); - }; - const onSortingChange = (isDescending: boolean | undefined, sortingField: string | undefined): void => { - setDateFilter(null); - setFilterParams((prevState) => ({ - ...prevState, - ascending: isDescending ? undefined : sortingField, - descending: isDescending ? sortingField : undefined, - paginationToken: undefined, - createdAtFrom: undefined, - createdAtTo: undefined, - currentPageIndex: 1, - paginationTokens: new Map().set(1, ''), - hasOpenEndPagination: true, - pageCount: 1 - })); - }; - - const onDateFilterChange = (dateFilterValue: DateRangePickerProps.RelativeValue): void => { - let start: Date | undefined = undefined; - let end: Date | undefined = undefined; - setDateFilter(dateFilterValue); - if (dateFilterValue) { - const range = convertToAbsoluteRange(dateFilterValue); - start = range.start; - end = range.end; - } - setFilterParams((prevState) => ({ - ...prevState, - ascending: undefined, - descending: undefined, - paginationToken: undefined, - createdAtFrom: start?.toISOString(), - createdAtTo: end?.toISOString(), - currentPageIndex: 1, - paginationTokens: new Map().set(1, ''), - hasOpenEndPagination: true, - pageCount: 1 - })); - }; - - const onConfirmPageSize = (pageSize: number | undefined): void => { - setFilterParams((prevState) => ({ - ...prevState, - pageSize: pageSize || pageSizeOptions[0]?.value, - paginationToken: undefined, - currentPageIndex: 1, - paginationTokens: new Map().set(1, ''), - hasOpenEndPagination: true, - pageCount: 1 - })); - }; - - useEffect(() => { - //save next page token into dictionary so we can access previous or next page by directly clicking page number - setFilterParams((prevState) => ({ - ...prevState, - paginationTokens: prevState.paginationTokens.set(prevState.currentPageIndex + 1, paginationToken) - })); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [paginationToken]); - - const getContent = (): JSX.Element => { - return ( - - {showConnectEnvironmentModal && ( - { - setShowConnectEnvironmentModal(false); - }} - instructions={envConnectResponse.instructionResponse} - authCredResponse={envConnectResponse.authCredResponse} - /> - )} - {!!error && {error}} -
- onSortingChange(event.detail.isDescending, event.detail.sortingColumn.sortingField) - } - loading={areEnvironmentsLoading} - selectionType="multi" - selectedItems={collectionProps.selectedItems} - ariaLabels={{ - selectionGroupLabel: 'Items selection', - allItemsSelectionLabel: ({ selectedItems }) => - `${selectedItems.length} ${selectedItems.length === 1 ? 'item' : 'items'} selected`, - itemSelectionLabel: ({ selectedItems }, item) => { - const isItemSelected = selectedItems.filter((i) => i.workspace === item.workspace).length; - return `${item.workspace} is ${isItemSelected ? '' : 'not'} selected`; - } - }} - header={ - <> -
- - - - - - - - - } - > - Workspaces -
- - } - columnDefinitions={columnDefinitions} - loadingText="Loading workspaces" - filter={ - - - ) => onDateFilterChange(detail.value)} - value={dateFilter} - relativeOptions={relativeOptions} - i18nStrings={datei18nStrings} - placeholder="Filter by a date and time range" - isValidRange={isValidRangeFunction} - /> - - } - pagination={ - onPaginationChange(detail)} - openEnd={filterParams.hasOpenEndPagination} - ariaLabels={paginationLables} - /> - } - preferences={ - onConfirmPageSize(pageSize)} - pageSizePreference={{ - title: 'Page size', - options: pageSizeOptions - }} - /> - } - items={items} - /> - - ); - }; - return ( - - {getContent()} - - ); -}; - -export default Environment; +export default EnvironmentsPage; diff --git a/solutions/swb-ui/src/pages/environments/new.tsx b/solutions/swb-ui/src/pages/environments/new.tsx index dc2036357e..343ce7df5a 100644 --- a/solutions/swb-ui/src/pages/environments/new.tsx +++ b/solutions/swb-ui/src/pages/environments/new.tsx @@ -3,335 +3,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { useDatasets } from '@aws/workbench-core-datasets-ui'; -import { nameRegex, BaseLayout } from '@aws/workbench-core-swb-common-ui'; -import { - Box, - BreadcrumbGroupProps, - SpaceBetween, - Form, - Header, - ExpandableSection, - Input, - FormField, - Button, - Container, - Link, - Textarea, - Select, - Multiselect -} from '@cloudscape-design/components'; -import type { NextPage } from 'next'; -import { useRouter } from 'next/router'; -import React, { useEffect, useState } from 'react'; -import { createEnvironment } from '../../api/environments'; -import { useEnvTypeConfigs } from '../../api/environmentTypeConfigs'; -import { useEnvironmentType } from '../../api/environmentTypes'; -import { useProjects } from '../../api/projects'; -import EnvTypeCards from '../../components/EnvTypeCards'; -import EnvTypeConfigCards from '../../components/EnvTypeConfigCards'; -import { CreateEnvironmentForm, CreateEnvironmentFormValidation } from '../../models/Environment'; -import { EnvTypeItem } from '../../models/EnvironmentType'; -import { EnvTypeConfigItem } from '../../models/EnvironmentTypeConfig'; +import { NewEnvironmentPage } from '@aws/workbench-core-environments-ui'; -const Environment: NextPage = () => { - // App settings constant - - const router = useRouter(); - const [isSubmitLoading, setIsSubmitLoading] = useState(false); - const [disableSubmit, setDisableSubmit] = useState(true); - const [selectedEnvType, setSelectedEnvType] = useState(); - const [error, setError] = useState(''); - const [formData, setFormData] = useState({}); - const [formErrors, setFormErrors] = useState({}); - const { envTypes, areEnvTypesLoading } = useEnvironmentType(); - const { envTypeConfigs, areEnvTypeConfigsLoading } = useEnvTypeConfigs(formData?.envTypeId || ''); - const { projects, areProjectsLoading } = useProjects(); - const { datasets, areDatasetsLoading } = useDatasets(); - - const breadcrumbs: BreadcrumbGroupProps.Item[] = [ - { - text: 'Service Workbench', - href: '/' - }, - { - text: 'Workspaces', - href: '/environments' - }, - { - text: 'Create Workspace', - href: '/environments/new' - } - ]; - const validationRules = [ - { - field: 'name', - // eslint-disable-next-line @typescript-eslint/no-explicit-any - condition: (a: any) => !!a, - message: 'Workspace Name is Required' - }, - { - field: 'name', - // eslint-disable-next-line @typescript-eslint/no-explicit-any - condition: (a: any) => !!a && nameRegex.test(a), - message: - 'Workspace Name must start with an alphabetic character and can only contain alphanumeric characters (case sensitive) and hyphens.' - }, - { - field: 'name', - // eslint-disable-next-line @typescript-eslint/no-explicit-any - condition: (a: any) => !!a && a.length <= 128, - message: 'Workspace Name cannot be longer than 128 characters' - }, - { - field: 'projectId', - // eslint-disable-next-line @typescript-eslint/no-explicit-any - condition: (a: any) => !!a, - message: 'Project ID is Required' - }, - { - field: 'envTypeId', - // eslint-disable-next-line @typescript-eslint/no-explicit-any - condition: (a: any) => !!a, - message: 'Compute Platform is Required' - }, - { - field: 'envTypeConfigId', - // eslint-disable-next-line @typescript-eslint/no-explicit-any - condition: (a: any) => !!a, - message: 'Configuration is Required' - }, - { - field: 'description', - // eslint-disable-next-line @typescript-eslint/no-explicit-any - condition: (a: any) => !a || a.length <= 500, - message: 'Description cannot be longer than 500 characters' - }, - { - field: 'description', - // eslint-disable-next-line @typescript-eslint/no-explicit-any - condition: (a: any) => !!a, - message: 'Description is Required' - } - ]; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const validateField = (field: keyof CreateEnvironmentForm, value: any): boolean => { - for (const rule of validationRules.filter((f) => f.field === field)) { - // eslint-disable-next-line security/detect-object-injection - if (!rule.condition(value)) { - setFormErrors((prevState: CreateEnvironmentFormValidation) => ({ - ...prevState, - [`${field}Error`]: rule.message - })); - return false; - } - } - setFormErrors((prevState: CreateEnvironmentFormValidation) => ({ ...prevState, [`${field}Error`]: '' })); - return true; - }; - const onSelectEnvType = async (selection: EnvTypeItem[]): Promise => { - const selected = (selection && selection.at(0)) || undefined; - setSelectedEnvType(selected); - setFormData({ - ...formData, - envTypeId: selected?.id, - envTypeConfigId: undefined, - envType: selected?.type, - datasetIds: [] - }); - validateField('envType', selected?.id); - validateField('envTypeConfigId', undefined); - }; - const onSelectEnvTypeConfig = (selection: EnvTypeConfigItem[]): void => { - const selected = (selection && selection.at(0)) || undefined; - setFormData({ ...formData, envTypeConfigId: selected?.id }); - validateField('envTypeConfigId', selected?.id); - }; - - const submitForm = async (): Promise => { - setIsSubmitLoading(true); - try { - await createEnvironment(formData); - await router.push({ - pathname: '/environments', - query: { - message: 'Workspace Created Successfully', - notificationType: 'success' - } - }); - } catch { - setError('There was a problem creating a workspace.'); - } finally { - setIsSubmitLoading(false); - } - }; - - useEffect(() => { - setDisableSubmit( - !validationRules.every((rule) => rule.condition(formData[rule.field as keyof CreateEnvironmentForm])) - ); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [formData]); - - const getContent = (): JSX.Element => { - return ( - - -
e.preventDefault()}> - - - - - } - header={ -
- Create Research Workspace -
- } - > - - - Select Compute Platform ({envTypes.length}) - Selected: {selectedEnvType?.name || 'None'} - - } - defaultExpanded - > - - await onSelectEnvType(selected.selectedItems)} - /> - - - Select Configurations} - > - - -
  • Name can only contain alphanumeric characters (case sensitive) and hyphens.
  • -
  • It must start with an alphabetic character.
  • -
  • Cannot be longer than 128 characters.
  • - - } - errorText={formErrors?.nameError} - > - { - setFormData({ ...formData, name: value }); - validateField('name', value); - }} - /> -
    - -