diff --git a/package.json b/package.json index c566f230d17..d7909d239b5 100644 --- a/package.json +++ b/package.json @@ -113,7 +113,7 @@ "@types/glob": "^7.1.1", "@types/jest": "^29.0.0", "@types/js-yaml": "^4.0.0", - "@types/node": "^18.16.1", + "@types/node": "^20.9.0", "@types/yargs": "^17", "@typescript-eslint/eslint-plugin": "^5.34.0", "@typescript-eslint/parser": "^5.34.0", diff --git a/packages/amplify-appsync-simulator/package.json b/packages/amplify-appsync-simulator/package.json index 0546adb96a9..3d9b6d8de8c 100644 --- a/packages/amplify-appsync-simulator/package.json +++ b/packages/amplify-appsync-simulator/package.json @@ -61,7 +61,7 @@ "@aws-amplify/amplify-graphiql-explorer": "2.6.2", "@types/cors": "^2.8.6", "@types/express": "^4.17.3", - "@types/node": "^12.12.6", + "@types/node": "^20.9.0", "@types/ws": "^8.2.2", "jose": "^5.2.0" }, diff --git a/packages/amplify-category-function/package.json b/packages/amplify-category-function/package.json index 46b734586e2..039a0468dac 100644 --- a/packages/amplify-category-function/package.json +++ b/packages/amplify-category-function/package.json @@ -30,6 +30,7 @@ "@aws-amplify/amplify-environment-parameters": "1.9.20", "@aws-amplify/amplify-function-plugin-interface": "1.12.1", "@aws-amplify/amplify-prompts": "2.8.7", + "@aws-sdk/client-ssm": "^3.624.0", "archiver": "^7.0.1", "aws-sdk": "^2.1464.0", "chalk": "^4.1.1", diff --git a/packages/amplify-category-function/src/provider-utils/awscloudformation/secrets/ssmClientWrapper.ts b/packages/amplify-category-function/src/provider-utils/awscloudformation/secrets/ssmClientWrapper.ts index fe26df73887..ccd306a6c57 100644 --- a/packages/amplify-category-function/src/provider-utils/awscloudformation/secrets/ssmClientWrapper.ts +++ b/packages/amplify-category-function/src/provider-utils/awscloudformation/secrets/ssmClientWrapper.ts @@ -1,5 +1,13 @@ import { $TSContext } from '@aws-amplify/amplify-cli-core'; -import type { SSM } from 'aws-sdk'; +import { + DeleteParameterCommand, + DeleteParametersCommand, + GetParametersByPathCommand, + GetParametersByPathResult, + GetParametersCommand, + PutParameterCommand, + SSMClient, +} from '@aws-sdk/client-ssm'; /** * Wrapper around SSM SDK calls @@ -14,7 +22,7 @@ export class SSMClientWrapper { return SSMClientWrapper.instance; }; - private constructor(private readonly ssmClient: SSM) {} + private constructor(private readonly ssmClient: SSMClient) {} /** * Returns a list of secret name value pairs @@ -23,12 +31,12 @@ export class SSMClientWrapper { if (!secretNames || secretNames.length === 0) { return []; } - const result = await this.ssmClient - .getParameters({ + const result = await this.ssmClient.send( + new GetParametersCommand({ Names: secretNames, WithDecryption: true, - }) - .promise(); + }), + ); return result?.Parameters?.map(({ Name, Value }) => ({ secretName: Name, secretValue: Value })); }; @@ -40,8 +48,8 @@ export class SSMClientWrapper { let NextToken; const accumulator: string[] = []; do { - const result: SSM.GetParametersByPathResult = await this.ssmClient - .getParametersByPath({ + const result: GetParametersByPathResult = await this.ssmClient.send( + new GetParametersByPathCommand({ Path: secretPath, MaxResults: 10, ParameterFilters: [ @@ -52,8 +60,8 @@ export class SSMClientWrapper { }, ], NextToken, - }) - .promise(); + }), + ); if (Array.isArray(result?.Parameters)) { accumulator.push(...result.Parameters.filter((param) => param?.Name !== undefined).map((param) => param.Name)); @@ -68,31 +76,32 @@ export class SSMClientWrapper { * Sets the given secretName to the secretValue. If secretName is already present, it is overwritten. */ setSecret = async (secretName: string, secretValue: string): Promise => { - await this.ssmClient - .putParameter({ + await this.ssmClient.send( + new PutParameterCommand({ Name: secretName, Value: secretValue, Type: 'SecureString', Overwrite: true, - }) - .promise(); + }), + ); }; /** * Deletes secretName. If it already doesn't exist, this is treated as success. All other errors will throw. */ deleteSecret = async (secretName: string): Promise => { - await this.ssmClient - .deleteParameter({ - Name: secretName, - }) - .promise() - .catch((err) => { - if (err.code !== 'ParameterNotFound') { - // if the value didn't exist in the first place, consider it deleted - throw err; - } - }); + try { + await this.ssmClient.send( + new DeleteParameterCommand({ + Name: secretName, + }), + ); + } catch (err) { + if (err.code !== 'ParameterNotFound') { + // if the value didn't exist in the first place, consider it deleted + throw err; + } + } }; /** @@ -100,7 +109,7 @@ export class SSMClientWrapper { */ deleteSecrets = async (secretNames: string[]): Promise => { try { - await this.ssmClient.deleteParameters({ Names: secretNames }).promise(); + await this.ssmClient.send(new DeleteParametersCommand({ Names: secretNames })); } catch (err) { // if the value didn't exist in the first place, consider it deleted if (err.code !== 'ParameterNotFound') { @@ -110,8 +119,8 @@ export class SSMClientWrapper { }; } -const getSSMClient = async (context: $TSContext): Promise => { - const { client } = await context.amplify.invokePluginMethod<{ client: SSM }>( +const getSSMClient = async (context: $TSContext): Promise => { + const { client } = await context.amplify.invokePluginMethod<{ client: SSMClient }>( context, 'awscloudformation', undefined, diff --git a/packages/amplify-category-storage/package.json b/packages/amplify-category-storage/package.json index dfdb8dce246..d7860016a46 100644 --- a/packages/amplify-category-storage/package.json +++ b/packages/amplify-category-storage/package.json @@ -32,10 +32,11 @@ "@aws-amplify/amplify-prompts": "2.8.7", "@aws-amplify/amplify-util-import": "2.8.3", "@aws-amplify/cli-extensibility-helper": "3.0.39", + "@aws-sdk/client-dynamodb": "^3.515.0", + "@aws-sdk/client-s3": "^3.515.0", "amplify-headless-interface": "1.17.8", "amplify-util-headless-input": "1.9.19", "aws-cdk-lib": "~2.189.1", - "aws-sdk": "^2.1464.0", "chalk": "^4.1.1", "constructs": "^10.0.5", "enquirer": "^2.3.6", diff --git a/packages/amplify-category-storage/src/provider-utils/awscloudformation/import/import-s3.ts b/packages/amplify-category-storage/src/provider-utils/awscloudformation/import/import-s3.ts index ca15b8d9f62..1ffeacdb806 100644 --- a/packages/amplify-category-storage/src/provider-utils/awscloudformation/import/import-s3.ts +++ b/packages/amplify-category-storage/src/provider-utils/awscloudformation/import/import-s3.ts @@ -10,7 +10,7 @@ import { } from '@aws-amplify/amplify-cli-core'; import { printer } from '@aws-amplify/amplify-prompts'; import { IS3Service } from '@aws-amplify/amplify-util-import'; -import { Bucket } from 'aws-sdk/clients/s3'; +import { Bucket } from '@aws-sdk/client-s3'; import Enquirer from 'enquirer'; import _ from 'lodash'; import { v4 as uuid } from 'uuid'; @@ -115,7 +115,7 @@ const importServiceWalkthrough = async ( const bucketList = await s3.listBuckets(); // Return if no User Pools found in the project's region - if (_.isEmpty(bucketList)) { + if (bucketList.length == 0) { printer.info(importMessages.NoS3BucketsToImport); return undefined; } diff --git a/packages/amplify-category-storage/src/provider-utils/awscloudformation/import/types.ts b/packages/amplify-category-storage/src/provider-utils/awscloudformation/import/types.ts index 958d9a86a7e..98c1abb0120 100644 --- a/packages/amplify-category-storage/src/provider-utils/awscloudformation/import/types.ts +++ b/packages/amplify-category-storage/src/provider-utils/awscloudformation/import/types.ts @@ -1,7 +1,7 @@ import { $TSContext, $TSObject } from '@aws-amplify/amplify-cli-core'; import { IDynamoDBService, IS3Service } from '@aws-amplify/amplify-util-import'; -import { Bucket } from 'aws-sdk/clients/s3'; -import { TableDescription } from 'aws-sdk/clients/dynamodb'; +import { Bucket } from '@aws-sdk/client-s3'; +import { TableDescription } from '@aws-sdk/client-dynamodb'; // parameters.json export type S3ResourceParameters = { diff --git a/packages/amplify-cli-core/package.json b/packages/amplify-cli-core/package.json index f939d25a9c2..52ba5880246 100644 --- a/packages/amplify-cli-core/package.json +++ b/packages/amplify-cli-core/package.json @@ -68,7 +68,7 @@ "@types/hjson": "^2.4.2", "@types/json-schema": "^7.0.5", "@types/lodash": "^4.14.149", - "@types/node": "^12.12.6", + "@types/node": "^20.9.0", "@types/rimraf": "^3.0.0", "@types/uuid": "^8.0.0", "@types/yarnpkg__lockfile": "^1.1.5", diff --git a/packages/amplify-cli-core/src/__tests__/cfnUtilities.test.ts b/packages/amplify-cli-core/src/__tests__/cfnUtilities.test.ts index 814d0327c8c..4b978e6727a 100644 --- a/packages/amplify-cli-core/src/__tests__/cfnUtilities.test.ts +++ b/packages/amplify-cli-core/src/__tests__/cfnUtilities.test.ts @@ -204,7 +204,7 @@ describe('roundtrip CFN Templates to object and back', () => { const writtenYaml = fs_mock.writeFileSync.mock.calls[0][1]; - (fs_mock.readFile as unknown as jest.MockedFunction).mockResolvedValueOnce(writtenYaml); + (fs_mock.readFile as unknown as jest.MockedFunction).mockResolvedValueOnce(writtenYaml as string); const roundtrippedYaml = readCFNTemplate(testPath); diff --git a/packages/amplify-cli/package.json b/packages/amplify-cli/package.json index 0a9036e1e48..256b2720bad 100644 --- a/packages/amplify-cli/package.json +++ b/packages/amplify-cli/package.json @@ -66,6 +66,7 @@ "@aws-amplify/amplify-util-mock": "5.10.16", "@aws-amplify/amplify-util-uibuilder": "1.14.20", "@aws-cdk/cloudformation-diff": "~2.68.0", + "@aws-sdk/client-amplify": "^3.624.0", "amplify-codegen": "^4.10.3", "amplify-dotnet-function-runtime-provider": "2.1.5", "amplify-go-function-runtime-provider": "2.3.52", @@ -112,7 +113,7 @@ "@types/fs-extra": "^8.0.1", "@types/glob": "^7.1.1", "@types/gunzip-maybe": "^1.4.0", - "@types/node": "^12.12.6", + "@types/node": "^20.9.0", "@types/node-fetch": "^2.6.12", "@types/progress": "^2.0.3", "@types/promise-sequential": "^1.1.0", diff --git a/packages/amplify-cli/src/__tests__/extensions/amplify-helpers/delete-project.test.ts b/packages/amplify-cli/src/__tests__/extensions/amplify-helpers/delete-project.test.ts index a84eff15f88..5888b7af643 100644 --- a/packages/amplify-cli/src/__tests__/extensions/amplify-helpers/delete-project.test.ts +++ b/packages/amplify-cli/src/__tests__/extensions/amplify-helpers/delete-project.test.ts @@ -31,27 +31,29 @@ jest.mock('@aws-amplify/amplify-cli-core', () => ({ jest.mock('../../../extensions/amplify-helpers/get-plugin-instance', () => ({ getPluginInstance: jest.fn().mockReturnValue({ getConfiguredAmplifyClient: jest.fn().mockResolvedValue({ - listBackendEnvironments: jest.fn().mockReturnValue({ - promise: jest - .fn() - .mockImplementationOnce(() => ({ + send: jest + .fn() + // first ListBackendEnv Call + .mockImplementationOnce(() => + Promise.resolve({ backendEnvironments: [], - })) - .mockImplementationOnce(() => { - throw new Error('listBackendEnvironments error'); - }) - .mockImplementationOnce(() => { - // eslint-disable-next-line no-throw-literal - throw { - name: 'BucketNotFoundError', - message: 'Bucket not found', - link: 'https://docs.aws.amazon.com/', - }; }), - }), - deleteApp: jest.fn().mockReturnValue({ - promise: jest.fn().mockResolvedValue(true), - }), + ) + // DeleteApp call + .mockImplementationOnce(() => Promise.resolve(true)) + // second ListBackendEnv Call + .mockImplementationOnce(() => { + throw new Error('listBackendEnvironments error'); + }) + // third ListBackendEnv Call + .mockImplementationOnce(() => { + // eslint-disable-next-line no-throw-literal + throw { + name: 'BucketNotFoundError', + message: 'Bucket not found', + link: 'https://docs.aws.amazon.com/', + }; + }), }), }), })); diff --git a/packages/amplify-cli/src/__tests__/utils/encrypt-buffer.test.ts b/packages/amplify-cli/src/__tests__/utils/encrypt-buffer.test.ts index 75e0f030eb1..efb4f7081a4 100644 --- a/packages/amplify-cli/src/__tests__/utils/encrypt-buffer.test.ts +++ b/packages/amplify-cli/src/__tests__/utils/encrypt-buffer.test.ts @@ -24,7 +24,7 @@ describe('encryption helper', () => { decipher.setAuthTag(tag); // encrypt the given text - const decrypted = decipher.update(text, 'binary', 'utf8') + decipher.final('utf8'); + const decrypted = decipher.update(text) + decipher.final('utf8'); expect(decrypted).toEqual(plainText.toString('utf8')); }); diff --git a/packages/amplify-cli/src/extensions/amplify-helpers/delete-project.ts b/packages/amplify-cli/src/extensions/amplify-helpers/delete-project.ts index 558de3b6df4..bddbde84ebc 100644 --- a/packages/amplify-cli/src/extensions/amplify-helpers/delete-project.ts +++ b/packages/amplify-cli/src/extensions/amplify-helpers/delete-project.ts @@ -3,6 +3,7 @@ /* eslint-disable @typescript-eslint/no-var-requires */ import ora from 'ora'; import chalk from 'chalk'; +import { ListBackendEnvironmentsCommand, DeleteAppCommand } from '@aws-sdk/client-amplify'; import { FeatureFlags, $TSContext, AmplifyFault } from '@aws-amplify/amplify-cli-core'; import { printer, prompter } from '@aws-amplify/amplify-prompts'; import { removeEnvFromCloud } from './remove-env-from-cloud'; @@ -37,7 +38,7 @@ export const deleteProject = async (context: $TSContext): Promise => { const amplifyClient = await awsCloudPlugin.getConfiguredAmplifyClient(context, {}); const environments = await amplifyBackendEnvironments(amplifyClient, appId); if (environments.length === 0) { - await amplifyClient.deleteApp({ appId }).promise(); + await amplifyClient.send(new DeleteAppCommand({ appId })); } else { printer.warn('Amplify App cannot be deleted, other environments still linked to Application'); } @@ -77,11 +78,7 @@ const removeLocalAmplifyDir = (context: $TSContext): void => { }; const amplifyBackendEnvironments = async (client, appId): Promise => { - const data = await client - .listBackendEnvironments({ - appId, - }) - .promise(); + const data = await client.send(new ListBackendEnvironmentsCommand({ appId })); return data.backendEnvironments; }; diff --git a/packages/amplify-cli/src/extensions/amplify-helpers/push-resources.ts b/packages/amplify-cli/src/extensions/amplify-helpers/push-resources.ts index 086779639b0..2bd5019f83e 100644 --- a/packages/amplify-cli/src/extensions/amplify-helpers/push-resources.ts +++ b/packages/amplify-cli/src/extensions/amplify-helpers/push-resources.ts @@ -119,6 +119,7 @@ export const pushResources = async ( await onCategoryOutputsChange(context, currentAmplifyMeta); } catch (err) { // TODO PL: this needs to be removed once the api category is using the new amplify error class + console.log(err); const isAuthError = isValidGraphQLAuthError(err.message); if (isAuthError) { retryPush = await handleValidGraphQLAuthError(context, err.message); diff --git a/packages/amplify-console-hosting/package.json b/packages/amplify-console-hosting/package.json index d02a53fc1b7..4198e27a68f 100644 --- a/packages/amplify-console-hosting/package.json +++ b/packages/amplify-console-hosting/package.json @@ -9,6 +9,8 @@ "dependencies": { "@aws-amplify/amplify-cli-core": "4.4.2", "@aws-amplify/amplify-environment-parameters": "1.9.20", + "@aws-sdk/client-amplify": "^3.624.0", + "@aws-sdk/client-s3": "^3.624.0", "archiver": "^7.0.1", "aws-sdk": "^2.1692.0", "chalk": "^4.1.1", diff --git a/packages/amplify-console-hosting/src/hosting/cicd/enable.js b/packages/amplify-console-hosting/src/hosting/cicd/enable.js index cd803b9f708..082a139d631 100644 --- a/packages/amplify-console-hosting/src/hosting/cicd/enable.js +++ b/packages/amplify-console-hosting/src/hosting/cicd/enable.js @@ -8,6 +8,7 @@ const clientFactory = require('../../utils/client-factory'); const pathManager = require('../../utils/path-manager'); const ValidationError = require('../../error/validation-error').default; const statusMod = require('../index'); +const { ListBranchesCommand } = require('@aws-sdk/client-amplify'); /** * Entry point to enable CI/CD hosting @@ -41,11 +42,11 @@ async function enable(context) { async function validateCICDApp(context, appId) { const amplifyClient = await clientFactory.getAmplifyClient(context); - const result = await amplifyClient - .listBranches({ + const result = await amplifyClient.send( + new ListBranchesCommand({ appId, - }) - .promise(); + }), + ); if (result.branches.length === 0) { throw new ValidationError("No hosting URL found. Run 'amplify add hosting' again to set up hosting with Amplify Console."); } diff --git a/packages/amplify-console-hosting/src/hosting/index.js b/packages/amplify-console-hosting/src/hosting/index.js index 6f45e78e2eb..85609b47bb2 100644 --- a/packages/amplify-console-hosting/src/hosting/index.js +++ b/packages/amplify-console-hosting/src/hosting/index.js @@ -10,6 +10,7 @@ const clientFactory = require('../utils/client-factory'); const tableUtils = require('../utils/table-utils'); const { ensureEnvParamManager } = require('@aws-amplify/amplify-environment-parameters'); const { spinner } = require('@aws-amplify/amplify-cli-core'); +const { ListBranchesCommand } = require('@aws-sdk/client-amplify'); const HELP_INFO_PLACE_HOLDER = 'Manual deployment allows you to publish your web app to the Amplify Console without connecting a Git provider. Continuous deployment allows you to publish changes on every code commit by connecting your GitHub, Bitbucket, GitLab, or AWS CodeCommit repositories.'; @@ -166,7 +167,7 @@ function isHostingEnabled(context) { async function isFrontendCreatedOnline(context) { const appId = utils.getAppIdForCurrEnv(context); const amplifyClient = await clientFactory.getAmplifyClient(context); - const result = await amplifyClient.listBranches({ appId }).promise(); + const result = await amplifyClient.send(new ListBranchesCommand({ appId })); if (result.branches.length > 0) { return true; } else { diff --git a/packages/amplify-console-hosting/src/utils/amplify-console-utils.js b/packages/amplify-console-hosting/src/utils/amplify-console-utils.js index 93dd998d3b2..d16566dc2c1 100644 --- a/packages/amplify-console-hosting/src/utils/amplify-console-utils.js +++ b/packages/amplify-console-hosting/src/utils/amplify-console-utils.js @@ -2,6 +2,13 @@ const { spinner } = require('@aws-amplify/amplify-cli-core'); const fs = require('fs-extra'); const fetch = require('node-fetch'); const { ProxyAgent } = require('proxy-agent'); +const { + CreateDeploymentCommand, + GetJobCommand, + ListJobsCommand, + StartDeploymentCommand, + StopJobCommand, +} = require('@aws-sdk/client-amplify'); const DEPLOY_ARTIFACTS_MESSAGE = 'Deploying build artifacts to the Amplify Console..'; const DEPLOY_COMPLETE_MESSAGE = 'Deployment complete!'; @@ -24,9 +31,9 @@ async function publishFileToAmplify(appId, branchName, artifactsPath, amplifyCli branchName, }; await cancelAllPendingJob(appId, branchName, amplifyClient); - const { zipUploadUrl, jobId } = await amplifyClient.createDeployment(params).promise(); + const { zipUploadUrl, jobId } = await amplifyClient.send(new CreateDeploymentCommand(params)); await httpPutFile(artifactsPath, zipUploadUrl); - await amplifyClient.startDeployment({ ...params, jobId }).promise(); + await amplifyClient.send(new StartDeploymentCommand({ ...params, jobId })); await waitJobToSucceed({ ...params, jobId }, amplifyClient); spinner.succeed(DEPLOY_COMPLETE_MESSAGE); } catch (err) { @@ -40,12 +47,12 @@ async function cancelAllPendingJob(appId, branchName, amplifyClient) { appId, branchName, }; - const { jobSummaries } = await amplifyClient.listJobs(params).promise(); + const { jobSummaries } = await amplifyClient.send(new ListJobsCommand(params)); for (const jobSummary of jobSummaries) { const { jobId, status } = jobSummary; if (status === 'PENDING' || status === 'RUNNING') { const job = { ...params, jobId }; - await amplifyClient.stopJob(job).promise(); + await amplifyClient.send(new StopJobCommand(job)); } } } @@ -61,7 +68,7 @@ function waitJobToSucceed(job, amplifyClient) { let processing = true; try { while (processing) { - const getJobResult = await amplifyClient.getJob(job).promise(); + const getJobResult = await amplifyClient.send(new GetJobCommand(job)); const jobSummary = getJobResult.job.summary; if (jobSummary.status === 'FAILED') { console.log(`Job failed.${JSON.stringify(jobSummary)}`); diff --git a/packages/amplify-console-hosting/src/utils/client-factory.js b/packages/amplify-console-hosting/src/utils/client-factory.js index 9439ef0f5ff..f88715f46a5 100644 --- a/packages/amplify-console-hosting/src/utils/client-factory.js +++ b/packages/amplify-console-hosting/src/utils/client-factory.js @@ -1,14 +1,15 @@ const constants = require('../constants/plugin-constants'); -const AWS = require('aws-sdk'); +const { AmplifyClient } = require('@aws-sdk/client-amplify'); +const { S3Client } = require('@aws-sdk/client-s3'); async function getAmplifyClient(context) { const config = await getAWSClientConfig(context); - return new AWS.Amplify(config); + return new AmplifyClient(config); } async function getS3Client(context) { const config = await getAWSClientConfig(context); - return new AWS.S3(config); + return new S3Client(config); } async function getAWSClientConfig(context) { diff --git a/packages/amplify-console-hosting/src/utils/config-utils.js b/packages/amplify-console-hosting/src/utils/config-utils.js index 05269ebb810..38e2ed7145c 100644 --- a/packages/amplify-console-hosting/src/utils/config-utils.js +++ b/packages/amplify-console-hosting/src/utils/config-utils.js @@ -9,6 +9,7 @@ const clientFactory = require('../utils/client-factory'); const consolePathManager = require('../utils/path-manager'); const buildUtils = require('./build-utils'); const { ensureEnvParamManager } = require('@aws-amplify/amplify-environment-parameters'); +const { PutObjectCommand } = require('@aws-sdk/client-s3'); function initCFNTemplate(context, templateFilePath) { const templateContent = context.amplify.readJsonFile(templateFilePath); @@ -176,7 +177,7 @@ async function uploadFile(s3, filePath, key) { }; const projectBucket = stateManager.getMeta().providers[constants.PROVIDER].DeploymentBucketName; s3Params.Bucket = projectBucket; - await s3.putObject(s3Params).promise(); + await s3.send(new PutObjectCommand(s3Params)); return projectBucket; } } diff --git a/packages/amplify-console-hosting/src/utils/table-utils.js b/packages/amplify-console-hosting/src/utils/table-utils.js index 92b01a77639..de3eaa62a45 100644 --- a/packages/amplify-console-hosting/src/utils/table-utils.js +++ b/packages/amplify-console-hosting/src/utils/table-utils.js @@ -1,6 +1,7 @@ const clientFactory = require('./client-factory'); const Table = require('cli-table3'); const { spinner } = require('@aws-amplify/amplify-cli-core'); +const { ListBranchesCommand, ListDomainAssociationsCommand } = require('@aws-sdk/client-amplify'); async function generateTableContentForApp(context, appId) { spinner.start('Fetching AWS Amplify Console domains'); @@ -10,12 +11,12 @@ async function generateTableContentForApp(context, appId) { let nextToken = null; try { do { - const { branches } = await amplifyClient - .listBranches({ + const { branches } = await amplifyClient.send( + new ListBranchesCommand({ appId, nextToken, - }) - .promise(); + }), + ); for (const branch of branches) { const { branchName, displayName } = branch; @@ -27,12 +28,12 @@ async function generateTableContentForApp(context, appId) { nextToken = null; do { - const { domainAssociations } = await amplifyClient - .listDomainAssociations({ + const { domainAssociations } = await amplifyClient.send( + new ListDomainAssociationsCommand({ appId, nextToken, - }) - .promise(); + }), + ); for (const domainAssociation of domainAssociations) { const { domainName, subDomains } = domainAssociation; diff --git a/packages/amplify-dotnet-function-runtime-provider/package.json b/packages/amplify-dotnet-function-runtime-provider/package.json index 04f226bfde1..81605a10538 100644 --- a/packages/amplify-dotnet-function-runtime-provider/package.json +++ b/packages/amplify-dotnet-function-runtime-provider/package.json @@ -31,7 +31,7 @@ "which": "^2.0.2" }, "devDependencies": { - "@types/node": "^12.12.6", + "@types/node": "^20.9.0", "@types/which": "^1.3.2" }, "berry": { diff --git a/packages/amplify-dotnet-function-template-provider/package.json b/packages/amplify-dotnet-function-template-provider/package.json index 97ef55a404d..b037acc690f 100644 --- a/packages/amplify-dotnet-function-template-provider/package.json +++ b/packages/amplify-dotnet-function-template-provider/package.json @@ -32,7 +32,7 @@ "devDependencies": { "@types/inquirer": "^6.5.0", "@types/lodash": "^4.14.149", - "@types/node": "^12.12.6" + "@types/node": "^20.9.0" }, "berry": { "plugins": [ diff --git a/packages/amplify-e2e-tests/package.json b/packages/amplify-e2e-tests/package.json index 4381da9b9c6..96602bb39e9 100644 --- a/packages/amplify-e2e-tests/package.json +++ b/packages/amplify-e2e-tests/package.json @@ -69,7 +69,7 @@ "@types/body-parser": "^1.19.2", "@types/express": "^4.17.3", "@types/lodash": "^4.14.149", - "@types/node": "^18.16.1", + "@types/node": "^20.9.0", "@types/openpgp": "^4.4.18", "@types/ws": "^7.4.4", "jest": "^29.7.0", diff --git a/packages/amplify-e2e-tests/src/__tests__/api_6a.test.ts b/packages/amplify-e2e-tests/src/__tests__/api_6a.test.ts index e9fd28a36b9..e4d92a55a46 100644 --- a/packages/amplify-e2e-tests/src/__tests__/api_6a.test.ts +++ b/packages/amplify-e2e-tests/src/__tests__/api_6a.test.ts @@ -15,6 +15,7 @@ const projName = 'apitest'; let projRoot; beforeEach(async () => { + process.env.AMPLIFY_ENABLE_DEBUG_OUTPUT = 'true'; projRoot = await createNewProjectDir(projName); await initJSProjectWithProfile(projRoot, { name: projName }); await addApiWithoutSchema(projRoot, { transformerVersion: 2 }); diff --git a/packages/amplify-e2e-tests/src/__tests__/delete.test.ts b/packages/amplify-e2e-tests/src/__tests__/delete.test.ts index 24f19f12e37..611ec41b72f 100644 --- a/packages/amplify-e2e-tests/src/__tests__/delete.test.ts +++ b/packages/amplify-e2e-tests/src/__tests__/delete.test.ts @@ -32,6 +32,7 @@ import { getAWSExportsPath } from '../aws-exports/awsExports'; describe('amplify delete', () => { let projRoot: string; beforeEach(async () => { + process.env.AMPLIFY_ENABLE_DEBUG_OUTPUT = 'true'; projRoot = await createNewProjectDir('delete'); }); diff --git a/packages/amplify-e2e-tests/src/__tests__/git-clone-attach.test.ts b/packages/amplify-e2e-tests/src/__tests__/git-clone-attach.test.ts index ad7939eb021..5b4989e084b 100644 --- a/packages/amplify-e2e-tests/src/__tests__/git-clone-attach.test.ts +++ b/packages/amplify-e2e-tests/src/__tests__/git-clone-attach.test.ts @@ -34,6 +34,7 @@ describe('attach amplify to git-cloned project', () => { const s3Client = new S3(); const importBucketName = `git-clone-test-bucket-${getShortId()}`; beforeAll(async () => { + process.env.AMPLIFY_ENABLE_DEBUG_OUTPUT = 'true'; await s3Client.createBucket({ Bucket: importBucketName }).promise(); projRoot = await createNewProjectDir('clone-test'); await initJSProjectWithProfile(projRoot, { envName, disableAmplifyAppCreation: false }); diff --git a/packages/amplify-e2e-tests/src/__tests__/import_s3_1.test.ts b/packages/amplify-e2e-tests/src/__tests__/import_s3_1.test.ts index 7bc7a694d28..6f888b076f6 100644 --- a/packages/amplify-e2e-tests/src/__tests__/import_s3_1.test.ts +++ b/packages/amplify-e2e-tests/src/__tests__/import_s3_1.test.ts @@ -92,6 +92,7 @@ describe('s3 import', () => { }); beforeEach(async () => { + process.env.AMPLIFY_ENABLE_DEBUG_OUTPUT = 'true'; projectRoot = await createNewProjectDir(projectPrefix); ignoreProjectDeleteErrors = false; }); diff --git a/packages/amplify-e2e-tests/src/__tests__/import_s3_2a.test.ts b/packages/amplify-e2e-tests/src/__tests__/import_s3_2a.test.ts index dc641c92f78..4dbae082a71 100644 --- a/packages/amplify-e2e-tests/src/__tests__/import_s3_2a.test.ts +++ b/packages/amplify-e2e-tests/src/__tests__/import_s3_2a.test.ts @@ -50,6 +50,7 @@ describe('s3 import a', () => { let ignoreProjectDeleteErrors = false; beforeAll(async () => { + process.env.AMPLIFY_ENABLE_DEBUG_OUTPUT = 'true'; ogProjectRoot = await createNewProjectDir(ogProjectSettings.name); ogShortId = getShortId(); ogSettings = createStorageSettings(ogProjectSettings.name, ogShortId); diff --git a/packages/amplify-e2e-tests/src/__tests__/import_s3_2b.test.ts b/packages/amplify-e2e-tests/src/__tests__/import_s3_2b.test.ts index ce3e6f33664..ec455950eca 100644 --- a/packages/amplify-e2e-tests/src/__tests__/import_s3_2b.test.ts +++ b/packages/amplify-e2e-tests/src/__tests__/import_s3_2b.test.ts @@ -44,6 +44,7 @@ describe('s3 import b', () => { let ignoreProjectDeleteErrors = false; beforeAll(async () => { + process.env.AMPLIFY_ENABLE_DEBUG_OUTPUT = 'true'; ogProjectRoot = await createNewProjectDir(ogProjectSettings.name); ogShortId = getShortId(); ogSettings = createStorageSettings(ogProjectSettings.name, ogShortId); diff --git a/packages/amplify-e2e-tests/src/__tests__/import_s3_2c.test.ts b/packages/amplify-e2e-tests/src/__tests__/import_s3_2c.test.ts index e6558cd77cd..1244ef9730f 100644 --- a/packages/amplify-e2e-tests/src/__tests__/import_s3_2c.test.ts +++ b/packages/amplify-e2e-tests/src/__tests__/import_s3_2c.test.ts @@ -55,6 +55,7 @@ describe('s3 import c', () => { let ignoreProjectDeleteErrors = false; beforeAll(async () => { + process.env.AMPLIFY_ENABLE_DEBUG_OUTPUT = 'true'; ogProjectRoot = await createNewProjectDir(ogProjectSettings.name); ogShortId = getShortId(); ogSettings = createStorageSettings(ogProjectSettings.name, ogShortId); diff --git a/packages/amplify-e2e-tests/src/__tests__/import_s3_3.test.ts b/packages/amplify-e2e-tests/src/__tests__/import_s3_3.test.ts index 0e8ed5e3883..591772d74c8 100644 --- a/packages/amplify-e2e-tests/src/__tests__/import_s3_3.test.ts +++ b/packages/amplify-e2e-tests/src/__tests__/import_s3_3.test.ts @@ -33,6 +33,7 @@ describe('headless s3 import', () => { let bucketLocation: string; beforeAll(async () => { + process.env.AMPLIFY_ENABLE_DEBUG_OUTPUT = 'true'; const shortId = getShortId(); bucketNameToImport = `${bucketPrefix}${shortId}`; diff --git a/packages/amplify-e2e-tests/src/__tests__/migration/api.key.migration2.test.ts b/packages/amplify-e2e-tests/src/__tests__/migration/api.key.migration2.test.ts index f3b166b726c..b582a031805 100644 --- a/packages/amplify-e2e-tests/src/__tests__/migration/api.key.migration2.test.ts +++ b/packages/amplify-e2e-tests/src/__tests__/migration/api.key.migration2.test.ts @@ -8,11 +8,17 @@ import { getProjectMeta, createNewProjectDir, deleteProjectDir, + tryScheduleCredentialRefresh, } from '@aws-amplify/amplify-e2e-core'; import { addEnvironment } from '../../environment/env'; describe('amplify add api', () => { let projRoot: string; + + beforeAll(() => { + tryScheduleCredentialRefresh(); + }); + beforeEach(async () => { projRoot = await createNewProjectDir('api-key-migration-2'); }); diff --git a/packages/amplify-e2e-tests/src/__tests__/studio-modelgen.test.ts b/packages/amplify-e2e-tests/src/__tests__/studio-modelgen.test.ts index d9f9d34d073..81157c3f989 100644 --- a/packages/amplify-e2e-tests/src/__tests__/studio-modelgen.test.ts +++ b/packages/amplify-e2e-tests/src/__tests__/studio-modelgen.test.ts @@ -12,10 +12,15 @@ import { updateApiSchema, getDeploymentBucketObject, getProjectConfig, + tryScheduleCredentialRefresh, } from '@aws-amplify/amplify-e2e-core'; describe('upload Studio CMS assets on push', () => { let projRoot: string; + beforeAll(() => { + tryScheduleCredentialRefresh(); + }); + beforeEach(async () => { projRoot = await createNewProjectDir('studio-cms-upload'); }); diff --git a/packages/amplify-e2e-tests/src/init-special-cases/index.ts b/packages/amplify-e2e-tests/src/init-special-cases/index.ts index a9534d1e7f5..3e7690b958d 100644 --- a/packages/amplify-e2e-tests/src/init-special-cases/index.ts +++ b/packages/amplify-e2e-tests/src/init-special-cases/index.ts @@ -31,12 +31,17 @@ export async function initWithoutCredentialFileAndNoNewUserSetup(projRoot) { } await initWorkflow(projRoot, settings); } finally { + console.log('resetting credentials and config files'); if (fs.existsSync(configFilePathHide)) { fs.renameSync(configFilePathHide, configFilePath); } if (fs.existsSync(credentialsFilePathHide)) { fs.renameSync(credentialsFilePathHide, credentialsFilePath); } + process.env.AWS_ACCESS_KEY_ID = settings.accessKeyId; + process.env.AWS_SECRET_ACCESS_KEY = settings.secretAccessKey; + process.env.AWS_DEFAULT_REGION = settings.region; + process.env.AWS_REGION = settings.region; } } diff --git a/packages/amplify-go-function-runtime-provider/package.json b/packages/amplify-go-function-runtime-provider/package.json index 9d72516633c..736b079b277 100644 --- a/packages/amplify-go-function-runtime-provider/package.json +++ b/packages/amplify-go-function-runtime-provider/package.json @@ -37,7 +37,7 @@ }, "devDependencies": { "@types/archiver": "^5.1.1", - "@types/node": "^12.12.6", + "@types/node": "^20.9.0", "@types/semver": "^7.1.0", "@types/which": "^1.3.2" }, diff --git a/packages/amplify-go-function-template-provider/package.json b/packages/amplify-go-function-template-provider/package.json index 00858c64405..457381f20cc 100644 --- a/packages/amplify-go-function-template-provider/package.json +++ b/packages/amplify-go-function-template-provider/package.json @@ -30,6 +30,6 @@ "devDependencies": { "@types/fs-extra": "^8.0.1", "@types/lodash": "^4.14.149", - "@types/node": "^12.12.6" + "@types/node": "^20.9.0" } } diff --git a/packages/amplify-graphiql-explorer/package.json b/packages/amplify-graphiql-explorer/package.json index 45463bdd980..c969319a9ec 100644 --- a/packages/amplify-graphiql-explorer/package.json +++ b/packages/amplify-graphiql-explorer/package.json @@ -74,7 +74,7 @@ "devDependencies": { "@semantic-ui-react/css-patch": "^1.0.0", "@types/jest": "^29.5.1", - "@types/node": "^12.12.6", + "@types/node": "^20.9.0", "@types/react": "^17.0.39", "@types/react-dom": "^17.0.11" }, diff --git a/packages/amplify-java-function-runtime-provider/package.json b/packages/amplify-java-function-runtime-provider/package.json index 7b315f38c03..5bb2af980f9 100644 --- a/packages/amplify-java-function-runtime-provider/package.json +++ b/packages/amplify-java-function-runtime-provider/package.json @@ -34,7 +34,7 @@ "which": "^2.0.2" }, "devDependencies": { - "@types/node": "^12.12.6", + "@types/node": "^20.9.0", "@types/semver": "^7.1.0", "@types/which": "^1.3.2" }, diff --git a/packages/amplify-nodejs-function-runtime-provider/package.json b/packages/amplify-nodejs-function-runtime-provider/package.json index 3071665bc88..4dfd49fd11f 100644 --- a/packages/amplify-nodejs-function-runtime-provider/package.json +++ b/packages/amplify-nodejs-function-runtime-provider/package.json @@ -36,7 +36,7 @@ }, "devDependencies": { "@types/exit": "^0.1.31", - "@types/node": "^12.12.6", + "@types/node": "^20.9.0", "@types/semver": "^7", "semver": "^7.5.4" }, diff --git a/packages/amplify-nodejs-function-template-provider/package.json b/packages/amplify-nodejs-function-template-provider/package.json index 3b38a30fbf1..08cda092c27 100644 --- a/packages/amplify-nodejs-function-template-provider/package.json +++ b/packages/amplify-nodejs-function-template-provider/package.json @@ -34,7 +34,7 @@ "devDependencies": { "@types/fs-extra": "^8.0.1", "@types/inquirer": "^6.5.0", - "@types/node": "^12.12.6" + "@types/node": "^20.9.0" }, "berry": { "plugins": [ diff --git a/packages/amplify-opensearch-simulator/package.json b/packages/amplify-opensearch-simulator/package.json index 7a432e15afc..6aa17b41a4f 100644 --- a/packages/amplify-opensearch-simulator/package.json +++ b/packages/amplify-opensearch-simulator/package.json @@ -40,7 +40,7 @@ "wait-port": "^0.2.7" }, "devDependencies": { - "@types/node": "^12.12.6", + "@types/node": "^20.9.0", "@types/openpgp": "^4.4.19", "uuid": "^8.3.2" }, diff --git a/packages/amplify-provider-awscloudformation/API.md b/packages/amplify-provider-awscloudformation/API.md index 85fee4b01ae..1ea70d0d2e3 100644 --- a/packages/amplify-provider-awscloudformation/API.md +++ b/packages/amplify-provider-awscloudformation/API.md @@ -7,18 +7,22 @@ import { $TSAny } from '@aws-amplify/amplify-cli-core'; import { $TSContext } from '@aws-amplify/amplify-cli-core'; import { $TSObject } from '@aws-amplify/amplify-cli-core'; -import * as AWS_2 from 'aws-sdk'; import { CognitoIdentityProviderClient } from '@aws-sdk/client-cognito-identity-provider'; import { IAmplifyResource } from '@aws-amplify/amplify-cli-core'; import { LocationClient } from '@aws-sdk/client-location'; +import { NodeHttpHandler } from '@smithy/node-http-handler'; +import { SSMClient } from '@aws-sdk/client-ssm'; import { Template } from '@aws-amplify/amplify-cli-core'; // @public (undocumented) export interface AwsSdkConfig { // (undocumented) - accessKeyId: string; - // (undocumented) - expiration?: Date; + credentials: { + accessKeyId: string; + secretAccessKey: string; + sessionToken?: string; + expiration?: Date; + }; // (undocumented) httpOptions?: { agent: $TSAny; @@ -26,9 +30,7 @@ export interface AwsSdkConfig { // (undocumented) region: string; // (undocumented) - secretAccessKey: string; - // (undocumented) - sessionToken?: string; + requestHandler?: NodeHttpHandler; } // @public (undocumented) diff --git a/packages/amplify-provider-awscloudformation/package.json b/packages/amplify-provider-awscloudformation/package.json index c7bee4299b2..0ed8db567ba 100644 --- a/packages/amplify-provider-awscloudformation/package.json +++ b/packages/amplify-provider-awscloudformation/package.json @@ -37,12 +37,34 @@ "@aws-amplify/cli-extensibility-helper": "3.0.39", "@aws-amplify/graphql-transformer-core": "^2.11.1", "@aws-amplify/graphql-transformer-interfaces": "^3.12.0", + "@aws-sdk/client-amplify": "^3.624.0", + "@aws-sdk/client-api-gateway": "^3.624.0", + "@aws-sdk/client-appsync": "^3.624.0", + "@aws-sdk/client-cloudformation": "^3.624.0", "@aws-sdk/client-cognito-identity": "^3.624.0", "@aws-sdk/client-cognito-identity-provider": "^3.624.0", + "@aws-sdk/client-dynamodb": "^3.624.0", + "@aws-sdk/client-ec2": "^3.624.0", + "@aws-sdk/client-ecr": "^3.624.0", + "@aws-sdk/client-iam": "^3.624.0", + "@aws-sdk/client-lambda": "^3.624.0", + "@aws-sdk/client-lex-model-building-service": "^3.624.0", "@aws-sdk/client-location": "^3.624.0", + "@aws-sdk/client-polly": "^3.624.0", + "@aws-sdk/client-route-53": "^3.624.0", + "@aws-sdk/client-s3": "^3.624.0", + "@aws-sdk/client-sagemaker": "^3.624.0", + "@aws-sdk/client-secrets-manager": "^3.624.0", + "@aws-sdk/client-sns": "^3.624.0", + "@aws-sdk/client-ssm": "^3.624.0", "@aws-sdk/client-sts": "^3.624.0", + "@aws-sdk/credential-provider-node": "^3.624.0", + "@aws-sdk/credential-providers": "^3.624.0", + "@aws-sdk/lib-dynamodb": "^3.624.0", + "@aws-sdk/lib-storage": "^3.624.0", + "@aws-sdk/s3-request-presigner": "^3.624.0", "@aws-sdk/types": "^3.624.0", - "@smithy/node-http-handler": "^4.0.6", + "@smithy/node-http-handler": "^4.1.0", "amplify-codegen": "^4.10.3", "archiver": "^7.0.1", "aws-cdk-lib": "~2.189.1", @@ -80,8 +102,10 @@ "@types/deep-diff": "^1.0.0", "@types/folder-hash": "^4.0.1", "@types/lodash.throttle": "^4.1.6", - "@types/node": "^18.16.0", + "@types/node": "^20.9.0", "@types/uuid": "^8.0.0", + "aws-sdk-client-mock": "^4.1.0", + "aws-sdk-client-mock-jest": "^4.1.0", "jest": "^29.7.0", "typescript": "^4.9.5" }, diff --git a/packages/amplify-provider-awscloudformation/src/__tests__/amplify-service-manager.test.ts b/packages/amplify-provider-awscloudformation/src/__tests__/amplify-service-manager.test.ts index 9921a8053ae..d8c14199d37 100644 --- a/packages/amplify-provider-awscloudformation/src/__tests__/amplify-service-manager.test.ts +++ b/packages/amplify-provider-awscloudformation/src/__tests__/amplify-service-manager.test.ts @@ -1,21 +1,16 @@ -import { Amplify } from 'aws-sdk'; import { stateManager } from '@aws-amplify/amplify-cli-core'; import { getConfiguredAmplifyClient } from '../aws-utils/aws-amplify'; import { checkAmplifyServiceIAMPermission } from '../amplify-service-permission-check'; import { init } from '../amplify-service-manager'; +import { mockClient } from 'aws-sdk-client-mock'; +import 'aws-sdk-client-mock-jest'; +import { AmplifyClient, CreateAppCommand } from '@aws-sdk/client-amplify'; jest.mock('../aws-utils/aws-amplify'); jest.mock('../amplify-service-permission-check'); -const amplifyClientStub = { - createApp: jest.fn().mockReturnValue({ - promise: jest.fn().mockRejectedValue({ - code: 'LimitExceededException', - }), - }), -} as unknown as Amplify; +const mockAmplifyClient = mockClient(AmplifyClient); const getConfiguredAmplifyClientMock = getConfiguredAmplifyClient as jest.MockedFunction; -getConfiguredAmplifyClientMock.mockResolvedValue(amplifyClientStub); const checkAmplifyServiceIAMPermissionMock = checkAmplifyServiceIAMPermission as jest.MockedFunction< typeof checkAmplifyServiceIAMPermission @@ -25,7 +20,16 @@ checkAmplifyServiceIAMPermissionMock.mockResolvedValue(true); jest.spyOn(stateManager, 'teamProviderInfoExists').mockReturnValue(false); describe('init', () => { + beforeEach(() => { + getConfiguredAmplifyClientMock.mockClear(); + mockAmplifyClient.reset(); + }); it('throws ProjectInitError if Amplify app limit has been reached', async () => { + mockAmplifyClient.on(CreateAppCommand).rejectsOnce({ + name: 'LimitExceededException', + }); + getConfiguredAmplifyClientMock.mockResolvedValue(mockAmplifyClient as unknown as AmplifyClient); + const amplifyServiceParamsStub = { context: {}, awsConfigInfo: {}, @@ -39,16 +43,11 @@ describe('init', () => { }); it('throws Configutation error if config level is general and soorcing wrong credentials', async () => { - const amplifyClientGeneralConfigStub = { - createApp: jest.fn().mockReturnValue({ - promise: jest.fn().mockRejectedValue({ - code: 'ConfigError', - message: 'Missing Region in Config', - }), - }), - } as unknown as Amplify; - getConfiguredAmplifyClientMock.mockClear(); - getConfiguredAmplifyClientMock.mockResolvedValue(amplifyClientGeneralConfigStub); + mockAmplifyClient.on(CreateAppCommand).rejectsOnce({ + name: 'ConfigError', + message: 'Missing Region in Config', + }); + getConfiguredAmplifyClientMock.mockResolvedValue(mockAmplifyClient as unknown as AmplifyClient); const amplifyServiceParamsStub = { context: { diff --git a/packages/amplify-provider-awscloudformation/src/__tests__/aws-utils/S3Service.test.ts b/packages/amplify-provider-awscloudformation/src/__tests__/aws-utils/S3Service.test.ts index a82fdafc6e5..d248c1bfe92 100644 --- a/packages/amplify-provider-awscloudformation/src/__tests__/aws-utils/S3Service.test.ts +++ b/packages/amplify-provider-awscloudformation/src/__tests__/aws-utils/S3Service.test.ts @@ -1,44 +1,56 @@ import { $TSContext } from '@aws-amplify/amplify-cli-core'; import { createS3Service } from '../../aws-utils/S3Service'; +import { HeadBucketCommand } from '@aws-sdk/client-s3'; -jest.mock('aws-sdk', () => { - const mockHeadBucketForUsEast1 = jest.fn().mockImplementation((params: { Bucket: string }) => { - return { - promise: jest.fn().mockImplementation(() => { - if (params.Bucket.includes('us-east-1')) { - return Promise.resolve({}); - } - return Promise.reject({ code: 'BadRequest', region: 'eu-south-1' }); - }), - }; +// Mock the AWS SDK v3 S3Client +jest.mock('@aws-sdk/client-s3', () => { + const mockSendForUsEast1 = jest.fn().mockImplementation(async (command) => { + if (command instanceof HeadBucketCommand) { + const params = command.input; + if (params.Bucket!.includes('us-east-1')) { + return {}; + } + const error = new Error('BadRequest'); + error.name = 'BadRequest'; + throw error; + } + return {}; + }); + + const mockSendForEuSouth1 = jest.fn().mockImplementation(async (command) => { + if (command instanceof HeadBucketCommand) { + const params = command.input; + if (params.Bucket!.includes('eu-south-1')) { + return {}; + } + const error = new Error('BadRequest'); + error.name = 'BadRequest'; + throw error; + } + return {}; }); - const mockHeadBucketForEuSouth1 = jest.fn().mockImplementation((params) => { + const mockS3Client = jest.fn().mockImplementation((options) => { + const region = options.region || 'us-east-1'; + return { - promise: jest.fn().mockImplementation(() => { - if (params.Bucket.includes('eu-south-1')) { - return Promise.resolve({}); - } - return Promise.reject({ code: 'BadRequest', region: 'us-east-1' }); - }), + send: region === 'eu-south-1' ? mockSendForEuSouth1 : mockSendForUsEast1, + config: { + region, + credentials: options.credentials, + }, }; }); return { - S3: jest.fn((options) => { - const region = options.region || 'us-east-1'; - if (region === 'eu-south-1') { - return { - headBucket: mockHeadBucketForEuSouth1, - }; - } + S3Client: mockS3Client, + HeadBucketCommand: jest.fn().mockImplementation((params) => { return { - config: { - region, - }, - headBucket: mockHeadBucketForUsEast1, + input: params, }; }), + ListBucketsCommand: jest.fn(), + GetBucketLocationCommand: jest.fn(), }; }); diff --git a/packages/amplify-provider-awscloudformation/src/__tests__/aws-utils/cloudformation-error-serializer.test.ts b/packages/amplify-provider-awscloudformation/src/__tests__/aws-utils/cloudformation-error-serializer.test.ts index 05767ce621e..91680e14d42 100644 --- a/packages/amplify-provider-awscloudformation/src/__tests__/aws-utils/cloudformation-error-serializer.test.ts +++ b/packages/amplify-provider-awscloudformation/src/__tests__/aws-utils/cloudformation-error-serializer.test.ts @@ -1,3 +1,4 @@ +import { ResourceStatus } from '@aws-sdk/client-cloudformation'; import { deserializeErrorMessages, CFNErrorMessages, @@ -11,7 +12,7 @@ const eventsWithFailure = [ PhysicalResourceId: 'testStackId1', LogicalResourceId: 'testLogicalResourceId1', ResourceType: 'AWS::IAM::Role', - ResourceStatus: 'CREATE_FAILED', + ResourceStatus: ResourceStatus.CREATE_FAILED, ResourceStatusReason: 'Some valid reason 1', // below properties are useless since we don't use them in the code StackId: 'testStackId1', @@ -23,7 +24,7 @@ const eventsWithFailure = [ PhysicalResourceId: 'testStackId2', LogicalResourceId: 'testLogicalResourceId2', ResourceType: 'AWS::CloudFormation::Stack', - ResourceStatus: 'UPDATE_FAILED', + ResourceStatus: ResourceStatus.UPDATE_FAILED, ResourceStatusReason: 'Some valid reason 2', // below properties are useless since we don't use them in the code StackId: 'testStackId2', diff --git a/packages/amplify-provider-awscloudformation/src/__tests__/configuration-manager.test.ts b/packages/amplify-provider-awscloudformation/src/__tests__/configuration-manager.test.ts index 7f4077fd11f..a051697c91d 100644 --- a/packages/amplify-provider-awscloudformation/src/__tests__/configuration-manager.test.ts +++ b/packages/amplify-provider-awscloudformation/src/__tests__/configuration-manager.test.ts @@ -58,12 +58,15 @@ describe('load configuration for env', () => { } as $TSContext; const result = await loadConfigurationForEnv(contextStub, 'test'); expect(result).toMatchInlineSnapshot(` -{ - "accessKeyId": "testAccessKey", - "region": "us-test-1", - "secretAccessKey": "testSecretKey", -} -`); + { + "credentials": { + "accessKeyId": "testAccessKey", + "secretAccessKey": "testSecretKey", + "sessionToken": undefined, + }, + "region": "us-test-1", + } + `); expect(resolveRegionSpy).not.toBeCalled(); }); @@ -82,12 +85,15 @@ describe('load configuration for env', () => { process.env.AWS_REGION = 'us-test-2'; const result = await loadConfigurationForEnv(contextStub, 'test'); expect(result).toMatchInlineSnapshot(` -{ - "accessKeyId": "testAccessKey", - "region": "us-test-2", - "secretAccessKey": "testSecretKey", -} -`); + { + "credentials": { + "accessKeyId": "testAccessKey", + "secretAccessKey": "testSecretKey", + "sessionToken": undefined, + }, + "region": "us-test-2", + } + `); process.env.AWS_REGION = origRegion; }); }); diff --git a/packages/amplify-provider-awscloudformation/src/__tests__/disconnect-dependent-resources/utils.test.ts b/packages/amplify-provider-awscloudformation/src/__tests__/disconnect-dependent-resources/utils.test.ts index 1c6afd4e466..e3f20008e59 100644 --- a/packages/amplify-provider-awscloudformation/src/__tests__/disconnect-dependent-resources/utils.test.ts +++ b/packages/amplify-provider-awscloudformation/src/__tests__/disconnect-dependent-resources/utils.test.ts @@ -8,10 +8,10 @@ import { import { pathManager, stateManager, readCFNTemplate, writeCFNTemplate, CFNTemplateFormat } from '@aws-amplify/amplify-cli-core'; import * as fs from 'fs-extra'; import { S3 } from '../../aws-utils/aws-s3'; -import { CloudFormation } from 'aws-sdk'; import { getPreviousDeploymentRecord } from '../../utils/amplify-resource-state-utils'; import Template from 'cloudform-types/types/template'; import { DeploymentOp, DeploymentStep } from '../../iterative-deployment'; +import { CloudFormationClient } from '@aws-sdk/client-cloudformation'; jest.mock('fs-extra'); jest.mock('@aws-amplify/amplify-cli-core'); @@ -147,14 +147,12 @@ describe('uploadTempFuncDeploymentFiles', () => { describe('generateIterativeFuncDeploymentSteps', () => { it('generates steps with correct pointers', async () => { const cfnClient_stub = { - describeStackResources: () => ({ - promise: async () => ({ - StackResources: [ - { - PhysicalResourceId: 'testStackId', - }, - ], - }), + send: () => ({ + StackResources: [ + { + PhysicalResourceId: 'testStackId', + }, + ], }), }; getPreviousDeploymentRecord_mock @@ -173,7 +171,7 @@ describe('generateIterativeFuncDeploymentSteps', () => { stateManager_mock.getResourceParametersJson.mockReturnValue({}); stateManager_mock.getTeamProviderInfo.mockReturnValue({}); stateManager_mock.getLocalEnvInfo.mockReturnValue({ envName: 'testenv' }); - const result = await generateIterativeFuncDeploymentSteps(cfnClient_stub as unknown as CloudFormation, 'testRootStackId', [ + const result = await generateIterativeFuncDeploymentSteps(cfnClient_stub as unknown as CloudFormationClient, 'testRootStackId', [ 'func1', 'func2', ]); diff --git a/packages/amplify-provider-awscloudformation/src/__tests__/display-helpful-urls.test.ts b/packages/amplify-provider-awscloudformation/src/__tests__/display-helpful-urls.test.ts index 92805736d2a..3ebf3e36c74 100644 --- a/packages/amplify-provider-awscloudformation/src/__tests__/display-helpful-urls.test.ts +++ b/packages/amplify-provider-awscloudformation/src/__tests__/display-helpful-urls.test.ts @@ -1,8 +1,8 @@ import { ApiCategoryFacade, BannerMessage, stateManager } from '@aws-amplify/amplify-cli-core'; -import { AWSError } from 'aws-sdk'; import { printer } from '@aws-amplify/amplify-prompts'; import { SNS } from '../aws-utils/aws-sns'; import { showGraphQLTransformerVersion, showSMSSandboxWarning } from '../display-helpful-urls'; +import { AwsError } from 'aws-sdk-client-mock'; jest.mock('../aws-utils/aws-sns'); jest.mock('@aws-amplify/amplify-cli-core'); @@ -77,8 +77,8 @@ describe('showSMSSandBoxWarning', () => { describe('when IAM user is missing sandbox permission', () => { beforeEach(() => { - const authError = new Error() as AWSError; - authError.code = 'AuthorizationError'; + const authError = new Error() as AwsError; + authError.name = 'AuthorizationError'; mockedSNSClientInstance.isInSandboxMode.mockRejectedValue(authError); }); it('should not show any warning if there is no message associated', async () => { @@ -110,8 +110,8 @@ describe('showSMSSandBoxWarning', () => { describe('it should not show any warning message when the SNS API is not deployed', () => { beforeEach(() => { - const resourceNotFoundError = new Error() as AWSError; - resourceNotFoundError.code = 'ResourceNotFound'; + const resourceNotFoundError = new Error() as AwsError; + resourceNotFoundError.name = 'ResourceNotFound'; mockedSNSClientInstance.isInSandboxMode.mockRejectedValue(resourceNotFoundError); }); it('should not print error', async () => { @@ -127,8 +127,8 @@ describe('showSMSSandBoxWarning', () => { describe('it should not show any warning message when there is a network error', () => { beforeEach(() => { - const networkError = new Error() as AWSError; - networkError.code = 'UnknownEndpoint'; + const networkError = new Error() as AwsError; + networkError.name = 'UnknownEndpoint'; mockedSNSClientInstance.isInSandboxMode.mockRejectedValue(networkError); }); diff --git a/packages/amplify-provider-awscloudformation/src/__tests__/initialize-env.test.ts b/packages/amplify-provider-awscloudformation/src/__tests__/initialize-env.test.ts index ca58f46ac0b..3282d8cea64 100644 --- a/packages/amplify-provider-awscloudformation/src/__tests__/initialize-env.test.ts +++ b/packages/amplify-provider-awscloudformation/src/__tests__/initialize-env.test.ts @@ -18,7 +18,7 @@ describe('initialize environment', () => { }, } as unknown as $TSContext; it('throws AmplifyError if the deployment bucket does not exist', async () => { - downloadZipMock.mockRejectedValueOnce({ code: 'NoSuchBucket' }); + downloadZipMock.mockRejectedValueOnce({ name: 'NoSuchBucket' }); const actual = await expect(run(contextStub, {})).rejects; await actual.toBeInstanceOf(AmplifyException); await actual.toMatchInlineSnapshot( diff --git a/packages/amplify-provider-awscloudformation/src/__tests__/iterative-deployment/stack-event-monitor.test.ts b/packages/amplify-provider-awscloudformation/src/__tests__/iterative-deployment/stack-event-monitor.test.ts index 3e6917d9cfe..5d8d9cbce63 100644 --- a/packages/amplify-provider-awscloudformation/src/__tests__/iterative-deployment/stack-event-monitor.test.ts +++ b/packages/amplify-provider-awscloudformation/src/__tests__/iterative-deployment/stack-event-monitor.test.ts @@ -1,27 +1,26 @@ -import type { CloudFormation } from 'aws-sdk'; import { StackEventMonitor } from '../../iterative-deployment/stack-event-monitor'; +import { mockClient } from 'aws-sdk-client-mock'; +import 'aws-sdk-client-mock-jest'; +import { CloudFormationClient, DescribeStackEventsCommand } from '@aws-sdk/client-cloudformation'; const stackProgressPrinterStub = { printerFn: jest.fn(), addEventActivity: jest.fn(), }; -const cfn = { - describeStackEvents: () => ({ - promise: () => - Promise.resolve({ - NextToken: undefined, - }), - }), -} as unknown as CloudFormation; +const mockCfnClient = mockClient(CloudFormationClient); jest.useFakeTimers(); jest.spyOn(global, 'setTimeout'); jest.spyOn(global, 'clearTimeout'); describe('StackEventMonitor', () => { + mockCfnClient.on(DescribeStackEventsCommand).resolves({ + NextToken: undefined, + }); + const monitor = new StackEventMonitor( - cfn, + mockCfnClient as unknown as CloudFormationClient, 'testStackName', stackProgressPrinterStub.printerFn, stackProgressPrinterStub.addEventActivity, diff --git a/packages/amplify-provider-awscloudformation/src/__tests__/permission-boundary/permissions-boundary.test.ts b/packages/amplify-provider-awscloudformation/src/__tests__/permission-boundary/permissions-boundary.test.ts index abfd084cccc..4099a07638d 100644 --- a/packages/amplify-provider-awscloudformation/src/__tests__/permission-boundary/permissions-boundary.test.ts +++ b/packages/amplify-provider-awscloudformation/src/__tests__/permission-boundary/permissions-boundary.test.ts @@ -1,8 +1,10 @@ import { $TSContext, setPermissionsBoundaryArn, getPermissionsBoundaryArn, stateManager } from '@aws-amplify/amplify-cli-core'; import { prompt } from 'inquirer'; -import { IAM } from 'aws-sdk'; +import { GetPolicyCommand, IAMClient as IAM } from '@aws-sdk/client-iam'; import { configurePermissionsBoundaryForInit } from '../../permissions-boundary/permissions-boundary'; import { IAMClient } from '../../aws-utils/aws-iam'; +import { mockClient } from 'aws-sdk-client-mock'; +import 'aws-sdk-client-mock-jest'; const permissionsBoundaryArn = 'arn:aws:iam::123456789012:policy/some-policy-name'; const argName = 'permissions-boundary'; @@ -25,6 +27,7 @@ const getPermissionsBoundaryArn_mock = getPermissionsBoundaryArn as jest.MockedF const prompt_mock = prompt as jest.MockedFunction; const IAMClient_mock = IAMClient as jest.Mocked; const stateManager_mock = stateManager as jest.Mocked; +const mockIAMClient = mockClient(IAM); stateManager_mock.getLocalEnvInfo.mockReturnValue({ envName: 'testenv' }); @@ -53,6 +56,7 @@ describe('configure permissions boundary on init', () => { }, } as unknown as $TSContext; jest.clearAllMocks(); + mockIAMClient.reset(); }); it('applies policy specifed in cmd arg when present', async () => { context_stub.input.options[argName] = permissionsBoundaryArn; @@ -108,12 +112,9 @@ describe('configure permissions boundary on env add', () => { it('applies existing policy to new env when existing policy is accessible', async () => { getPermissionsBoundaryArn_mock.mockReturnValueOnce(permissionsBoundaryArn); IAMClient_mock.getInstance.mockResolvedValueOnce({ - client: { - getPolicy: jest.fn().mockReturnValueOnce({ - promise: jest.fn(), - }), - } as unknown as IAM, + client: mockIAMClient as unknown as IAM, }); + mockIAMClient.on(GetPolicyCommand).resolvesOnce({}); await configurePermissionsBoundaryForInit(context_stub); expect(setPermissionsBoundaryArn_mock.mock.calls[0][0]).toEqual(permissionsBoundaryArn); expect(prompt_mock).not.toHaveBeenCalled(); @@ -122,12 +123,9 @@ describe('configure permissions boundary on env add', () => { it('prompts for new policy when existing one is not accessible', async () => { getPermissionsBoundaryArn_mock.mockReturnValueOnce(permissionsBoundaryArn); IAMClient_mock.getInstance.mockResolvedValueOnce({ - client: { - getPolicy: jest.fn().mockReturnValueOnce({ - promise: jest.fn().mockRejectedValueOnce({ statusCode: 404, message: 'test error' }), - }), - } as unknown as IAM, + client: mockIAMClient as unknown as IAM, }); + mockIAMClient.on(GetPolicyCommand).rejects({ name: 'NoSuchEntityException', message: 'test error' }); const newPermissionsBoundaryArn = 'thisIsANewArn'; prompt_mock.mockResolvedValueOnce({ permissionsBoundaryArn: newPermissionsBoundaryArn, @@ -140,12 +138,9 @@ describe('configure permissions boundary on env add', () => { context_stub.input.options.yes = true; getPermissionsBoundaryArn_mock.mockReturnValueOnce(permissionsBoundaryArn); IAMClient_mock.getInstance.mockResolvedValueOnce({ - client: { - getPolicy: jest.fn().mockReturnValueOnce({ - promise: jest.fn().mockRejectedValueOnce({ statusCode: 404, message: 'test error' }), - }), - } as unknown as IAM, + client: mockIAMClient as unknown as IAM, }); + mockIAMClient.on(GetPolicyCommand).rejects({ name: 'NoSuchEntityException', message: 'test error' }); await expect(configurePermissionsBoundaryForInit(context_stub)).rejects.toMatchInlineSnapshot( `[InputValidationError: A permissions boundary ARN must be specified using --permissions-boundary]`, ); diff --git a/packages/amplify-provider-awscloudformation/src/__tests__/system-config-manager.test.ts b/packages/amplify-provider-awscloudformation/src/__tests__/system-config-manager.test.ts index a8da05f6557..56fddd0fe8b 100644 --- a/packages/amplify-provider-awscloudformation/src/__tests__/system-config-manager.test.ts +++ b/packages/amplify-provider-awscloudformation/src/__tests__/system-config-manager.test.ts @@ -1,6 +1,6 @@ import { $TSContext } from '@aws-amplify/amplify-cli-core'; import fs from 'fs-extra'; -import { CredentialProviderChain, ProcessCredentials } from 'aws-sdk'; +import { fromProcess } from '@aws-sdk/credential-providers'; import { getProfileCredentials, getProfiledAwsConfig } from '../system-config-manager'; jest.setTimeout(15000); @@ -11,9 +11,8 @@ jest.mock('../utils/aws-logger', () => ({ jest.mock('fs-extra'); const fs_mock = fs as jest.Mocked; -jest.mock('aws-sdk'); -const ProcessCredentialsMock = ProcessCredentials as jest.MockedClass; -const CredentialProviderChainMock = CredentialProviderChain as jest.MockedClass; +jest.mock('@aws-sdk/credential-providers'); +const fromProcessMock = fromProcess as jest.MockedFunction; const context_stub = { print: { @@ -29,14 +28,14 @@ describe('profile tests', () => { fs_mock.existsSync.mockReturnValue(true); describe('credential process loading', () => { - const mockResolvePromise = jest.fn().mockReturnValue( - Promise.resolve({ - accessKeyId: 'chainTestAccessKey', - secretAccessKey: 'chainTestSecret', - sessionToken: 'chainTestSessionToken', - expireTime: new Date(1234), - }), - ); + const mockCredentials = { + accessKeyId: 'chainTestAccessKey', + secretAccessKey: 'chainTestSecret', + sessionToken: 'chainTestSessionToken', + expiration: new Date(1234), + }; + + const mockCredentialProvider = jest.fn().mockResolvedValue(mockCredentials); beforeEach(() => { // setup @@ -47,24 +46,7 @@ describe('profile tests', () => { fs_mock.readFileSync.mockReturnValue(awsConfigContent); - CredentialProviderChainMock.mockImplementation(() => ({ - providers: [], - resolvePromise: mockResolvePromise, - resolve: jest.fn(), - })); - - ProcessCredentialsMock.mockImplementation(() => ({ - accessKeyId: 'testAccessKey', - secretAccessKey: 'testSecret', - sessionToken: 'testSessionToken', - expireTime: new Date(1234), - expired: false, - get: jest.fn(), - getPromise: jest.fn(), - needsRefresh: jest.fn(), - refresh: jest.fn(), - refreshPromise: jest.fn(), - })); + fromProcessMock.mockReturnValue(mockCredentialProvider); }); it('should use credential_process defined in config file', async () => { @@ -73,24 +55,29 @@ describe('profile tests', () => { // expect expect(profile_config).toBeDefined(); - expect(mockResolvePromise).toBeCalled(); - expect(profile_config.accessKeyId).toBe('chainTestAccessKey'); - expect(profile_config.secretAccessKey).toBe('chainTestSecret'); - expect(profile_config.sessionToken).toBe('chainTestSessionToken'); - expect(profile_config.expiration).toEqual(new Date(1234)); + expect(fromProcessMock).toHaveBeenCalledWith({ profile: 'fake' }); + expect(mockCredentialProvider).toHaveBeenCalled(); + expect(profile_config.credentials.accessKeyId).toBe('chainTestAccessKey'); + expect(profile_config.credentials.secretAccessKey).toBe('chainTestSecret'); + expect(profile_config.credentials.sessionToken).toBe('chainTestSessionToken'); + expect(profile_config.credentials.expiration).toEqual(new Date(1234)); }); - it('sets AWS_SDK_LOAD_CONFIG while ProcessCredentials executes', async () => { + it('sets AWS_SDK_LOAD_CONFIG while credential provider executes', async () => { const sdkLoadConfigOriginal = process.env.AWS_SDK_LOAD_CONFIG; - mockResolvePromise.mockImplementationOnce(() => { + mockCredentialProvider.mockImplementationOnce(() => { expect(process.env.AWS_SDK_LOAD_CONFIG).toBeTruthy(); - return Promise.resolve({ accessKeyId: 'chainTestAccessKey', + secretAccessKey: 'chainTestSecret', + sessionToken: 'chainTestSessionToken', + expiration: new Date(1234), }); }); + await getProfiledAwsConfig(context_stub, 'fake'); - expect(mockResolvePromise).toBeCalled(); + expect(fromProcessMock).toHaveBeenCalledWith({ profile: 'fake' }); + expect(mockCredentialProvider).toHaveBeenCalled(); expect(process.env.AWS_SDK_LOAD_CONFIG).toStrictEqual(sdkLoadConfigOriginal); }); }); diff --git a/packages/amplify-provider-awscloudformation/src/__tests__/utils/amplify-resource-state-utils.test.ts b/packages/amplify-provider-awscloudformation/src/__tests__/utils/amplify-resource-state-utils.test.ts index 6e1f293f87a..f9a108b6ff8 100644 --- a/packages/amplify-provider-awscloudformation/src/__tests__/utils/amplify-resource-state-utils.test.ts +++ b/packages/amplify-provider-awscloudformation/src/__tests__/utils/amplify-resource-state-utils.test.ts @@ -1,45 +1,49 @@ +import { CloudFormationClient, DescribeStackResourcesCommand, DescribeStacksCommand } from '@aws-sdk/client-cloudformation'; import { getTableNames, getPreviousDeploymentRecord } from '../../utils/amplify-resource-state-utils'; -import { CloudFormation } from 'aws-sdk'; +import { mockClient } from 'aws-sdk-client-mock'; +import 'aws-sdk-client-mock-jest'; -const cfnClientStub = { - describeStackResources: () => ({ - promise: () => - Promise.resolve({ - StackResources: [ +const mockCfnClient = mockClient(CloudFormationClient); +mockCfnClient + .on(DescribeStackResourcesCommand) + .resolves({ + StackResources: [ + { + LogicalResourceId: 'LogicalResourceIdTest1', + PhysicalResourceId: 'PhysicalResourceIdTest', + ResourceType: undefined, + Timestamp: undefined, + ResourceStatus: undefined, + }, + ], + }) + .on(DescribeStacksCommand) + .resolves({ + Stacks: [ + { + Outputs: [ { - LogicalResourceId: 'LogicalResourceIdTest1', - PhysicalResourceId: 'PhysicalResourceIdTest', + OutputKey: 'GetAttLogicalResourceIdTest1TableName', + OutputValue: 'TestStackOutputValue1', + }, + { + OutputKey: 'InvalidLogicalResourceIdTableName', + OutputValue: 'TestStackOutputValue2', }, ], - }), - }), - describeStacks: () => ({ - promise: () => - Promise.resolve({ - Stacks: [ + Parameters: [ { - Outputs: [ - { - OutputKey: 'GetAttLogicalResourceIdTest1TableName', - OutputValue: 'TestStackOutputValue1', - }, - { - OutputKey: 'InvalidLogicalResourceIdTableName', - OutputValue: 'TestStackOutputValue2', - }, - ], - Parameters: [ - { - ParameterKey: 'TestParameterKey1', - ParameterValue: 'TestParameterValue1', - }, - ], - Capabilities: ['CAPABILITY_IAM'], + ParameterKey: 'TestParameterKey1', + ParameterValue: 'TestParameterValue1', }, ], - }), - }), -} as unknown as CloudFormation; + Capabilities: ['CAPABILITY_IAM'], + StackName: undefined, + CreationTime: undefined, + StackStatus: undefined, + }, + ], + }); describe('amplify-resource-state-utils', () => { const StackID = 'TestSTackID'; @@ -49,7 +53,7 @@ describe('amplify-resource-state-utils', () => { const expectedTableNameMap: Map = new Map(); expectedTableNameMap.set('LogicalResourceIdTest1', 'TestStackOutputValue1'); - const tableNames = await getTableNames(cfnClientStub, tables, StackID); + const tableNames = await getTableNames(mockCfnClient as unknown as CloudFormationClient, tables, StackID); expect(tableNames).toEqual(expectedTableNameMap); }); @@ -58,7 +62,7 @@ describe('amplify-resource-state-utils', () => { capabilities: ['CAPABILITY_IAM'], parameters: { TestParameterKey1: 'TestParameterValue1' }, }; - const prevDeploymentRecord = await getPreviousDeploymentRecord(cfnClientStub, StackID); + const prevDeploymentRecord = await getPreviousDeploymentRecord(mockCfnClient as unknown as CloudFormationClient, StackID); expect(prevDeploymentRecord).toEqual(expectedPrevDeploymentRecord); }); diff --git a/packages/amplify-provider-awscloudformation/src/__tests__/utils/ssm-utils/delete-ssm-parameters.test.ts b/packages/amplify-provider-awscloudformation/src/__tests__/utils/ssm-utils/delete-ssm-parameters.test.ts index f70bd26c3d8..6336560f9f5 100644 --- a/packages/amplify-provider-awscloudformation/src/__tests__/utils/ssm-utils/delete-ssm-parameters.test.ts +++ b/packages/amplify-provider-awscloudformation/src/__tests__/utils/ssm-utils/delete-ssm-parameters.test.ts @@ -1,11 +1,13 @@ import { $TSContext } from '@aws-amplify/amplify-cli-core'; -import type { SSM as SSMType } from 'aws-sdk'; import { SSM } from '../../../aws-utils/aws-ssm'; import { deleteEnvironmentParametersFromService } from '../../../utils/ssm-utils/delete-ssm-parameters'; import { getSsmSdkParametersDeleteParameters, getSsmSdkParametersGetParametersByPath, } from '../../../utils/ssm-utils/get-ssm-sdk-parameters'; +import { DeleteParametersCommand, GetParametersByPathCommand, GetParametersByPathResult, SSMClient } from '@aws-sdk/client-ssm'; +import { mockClient } from 'aws-sdk-client-mock'; +import 'aws-sdk-client-mock-jest'; jest.mock('../../../aws-utils/aws-ssm'); @@ -25,37 +27,34 @@ const contextStub = { }, }, }; +const mockSSMClient = mockClient(SSMClient); describe('parameters-delete-handler', () => { + beforeEach(() => { + mockSSMClient.reset(); + }); it('check if returned function is called once with correct paramater', async () => { - const deleteParametersPromiseMock = jest.fn().mockImplementation(() => Promise.resolve()); - const deleteParametersMock = jest.fn().mockImplementation(() => ({ promise: deleteParametersPromiseMock })); - - const mockGetParamatersReturnedObject: SSMType.GetParametersByPathResult = { + const mockGetParamatersReturnedObject: GetParametersByPathResult = { Parameters: keys.map((key) => { return { Name: key }; }), }; - const getParametersByPathPromiseMock = jest.fn().mockImplementation(() => Promise.resolve(mockGetParamatersReturnedObject)); - const getParametersByPathMock = jest.fn().mockImplementation(() => ({ promise: getParametersByPathPromiseMock })); const mockSSM = SSM as jest.Mocked; mockSSM.getInstance = jest.fn().mockResolvedValue({ - client: { - deleteParameters: deleteParametersMock, - getParametersByPath: getParametersByPathMock, - }, + client: mockSSMClient as unknown as SSMClient, }); + mockSSMClient.on(DeleteParametersCommand).resolvesOnce({}); + mockSSMClient.on(GetParametersByPathCommand).resolvesOnce(mockGetParamatersReturnedObject); + await deleteEnvironmentParametersFromService(contextStub as unknown as $TSContext, envName); - expect(deleteParametersPromiseMock).toBeCalledTimes(1); - expect(deleteParametersMock).toBeCalledTimes(1); + expect(mockSSMClient).toHaveReceivedCommandTimes(DeleteParametersCommand, 1); const expectedDeleteParamater = getSsmSdkParametersDeleteParameters(expectedKeys); - expect(deleteParametersMock).toBeCalledWith(expectedDeleteParamater); + expect(mockSSMClient).toHaveReceivedCommandWith(DeleteParametersCommand, expectedDeleteParamater); - expect(getParametersByPathPromiseMock).toBeCalledTimes(1); - expect(getParametersByPathMock).toBeCalledTimes(1); + expect(mockSSMClient).toHaveReceivedCommandTimes(GetParametersByPathCommand, 1); const expectedGetParamater = getSsmSdkParametersGetParametersByPath(fakeAppId, envName); - expect(getParametersByPathMock).toBeCalledWith(expectedGetParamater); + expect(mockSSMClient).toHaveReceivedCommandWith(GetParametersByPathCommand, expectedGetParamater); }); }); diff --git a/packages/amplify-provider-awscloudformation/src/__tests__/utils/ssm-utils/env-parameter-ssm-helpers.test.ts b/packages/amplify-provider-awscloudformation/src/__tests__/utils/ssm-utils/env-parameter-ssm-helpers.test.ts index 0d8776bd051..74e4d5cd15e 100644 --- a/packages/amplify-provider-awscloudformation/src/__tests__/utils/ssm-utils/env-parameter-ssm-helpers.test.ts +++ b/packages/amplify-provider-awscloudformation/src/__tests__/utils/ssm-utils/env-parameter-ssm-helpers.test.ts @@ -1,7 +1,9 @@ import { $TSContext, stateManager } from '@aws-amplify/amplify-cli-core'; import { SSM } from '../../../aws-utils/aws-ssm'; -import type { SSM as SSMType } from 'aws-sdk'; +import { GetParametersCommand, GetParametersCommandOutput, PutParameterCommand, SSMClient } from '@aws-sdk/client-ssm'; import { getEnvParametersDownloadHandler, getEnvParametersUploadHandler } from '../../../utils/ssm-utils/env-parameter-ssm-helpers'; +import { mockClient } from 'aws-sdk-client-mock'; +import 'aws-sdk-client-mock-jest'; jest.mock('@aws-amplify/amplify-cli-core'); jest.mock('../../../aws-utils/aws-ssm'); @@ -13,20 +15,10 @@ stateManagerMock.metaFileExists = jest.fn().mockReturnValue(true); stateManagerMock.getMeta = jest.fn(); stateManagerMock.getCurrentEnvName = jest.fn().mockReturnValue('mockEnv'); -const putParameterPromiseMock = jest.fn().mockImplementation(() => Promise.resolve()); -const getParametersPromiseMock = jest.fn().mockImplementation(() => Promise.resolve()); - -const getParametersMock = jest.fn().mockImplementation(() => ({ - promise: getParametersPromiseMock, -})); +const mockSsmClient = mockClient(SSMClient); mockSSM.getInstance = jest.fn().mockResolvedValue({ - client: { - putParameter: jest.fn().mockImplementation(() => ({ - promise: putParameterPromiseMock, - })), - getParameters: getParametersMock, - }, + client: mockSsmClient as unknown as SSMClient, }); const contextMock = {} as unknown as $TSContext; @@ -34,12 +26,20 @@ const contextMock = {} as unknown as $TSContext; jest.useFakeTimers(); describe('uploading environment parameters', () => { + beforeEach(() => { + mockSsmClient.reset(); + }); + it('returns an async function which can invoke the SSM client', async () => { stateManagerMock.getMeta.mockReturnValueOnce({ providers: { awscloudformation: { AmplifyAppId: 'mockedAppId' } } }); + mockSsmClient.on(PutParameterCommand).resolves({}); + const returnedFn = await getEnvParametersUploadHandler(contextMock); expect(returnedFn).toBeDefined(); await returnedFn('key', 'value'); - expect(putParameterPromiseMock).toBeCalledTimes(1); + + expect(mockSsmClient.calls().length).toBe(1); + expect(mockSsmClient).toHaveReceivedCommandTimes(PutParameterCommand, 1); }); it('returns no-op when AmplifyAppId is undefined', async () => { @@ -48,12 +48,14 @@ describe('uploading environment parameters', () => { expect(returnedFn).toBeDefined(); await returnedFn('key1', 'value'); await returnedFn('key2', 'value'); - expect(getParametersPromiseMock).not.toBeCalled(); + expect(mockSsmClient.calls().length).toBe(0); + expect(mockSsmClient).toHaveReceivedCommandTimes(PutParameterCommand, 0); }); }); describe('downloading environment parameters', () => { - afterEach(() => { + beforeEach(() => { + mockSsmClient.reset(); jest.clearAllMocks(); jest.clearAllTimers(); }); @@ -64,7 +66,8 @@ describe('downloading environment parameters', () => { expect(returnedFn).toBeDefined(); const mockParams = await returnedFn(['mockMissingParam']); expect(mockParams).toStrictEqual({}); - expect(getParametersMock).not.toBeCalled(); + expect(mockSsmClient.calls().length).toBe(0); + expect(mockSsmClient).toHaveReceivedCommandTimes(GetParametersCommand, 0); }); it('returns {} when no keys are supplied', async () => { @@ -73,20 +76,25 @@ describe('downloading environment parameters', () => { expect(returnedFn).toBeDefined(); const mockParams = await returnedFn([]); expect(mockParams).toStrictEqual({}); - expect(getParametersMock).not.toBeCalled(); + expect(mockSsmClient.calls().length).toBe(0); + expect(mockSsmClient).toHaveReceivedCommandTimes(GetParametersCommand, 0); }); it('returns an async function which can invoke the SSM client', async () => { stateManagerMock.getMeta.mockReturnValueOnce({ providers: { awscloudformation: { AmplifyAppId: 'mockedAppId' } } }); - const singleResultMock: SSMType.GetParametersResult = { Parameters: [{ Name: '/amplify/mockAppId/mockEnv/key', Value: '"value"' }] }; - getParametersPromiseMock.mockResolvedValueOnce(singleResultMock); + + const singleResultMock: GetParametersCommandOutput = { + Parameters: [{ Name: '/amplify/mockAppId/mockEnv/key', Value: '"value"' }], + $metadata: {}, + }; + mockSsmClient.on(GetParametersCommand).resolves(singleResultMock); const returnedFn = await getEnvParametersDownloadHandler(contextMock); expect(returnedFn).toBeDefined(); const mockParams = await returnedFn(['key']); expect(mockParams).toStrictEqual({ key: 'value' }); - expect(getParametersMock).toBeCalledTimes(1); - expect(getParametersPromiseMock).toBeCalledTimes(1); + expect(mockSsmClient.calls().length).toBe(1); + expect(mockSsmClient).toHaveReceivedCommandTimes(GetParametersCommand, 1); }); it('returns function which can handle many parameters in a single request', async () => { @@ -101,22 +109,37 @@ describe('downloading environment parameters', () => { mockCloudParams.push({ Name: `/amplify/mockedAppId/mockEnv/${key}`, Value: '"value"' }); } const expectedKeyPaths = keys.map((key) => `/amplify/mockedAppId/mockEnv/${key}`); - getParametersPromiseMock.mockResolvedValueOnce({ Parameters: mockCloudParams.slice(0, 10) }); - getParametersPromiseMock.mockResolvedValueOnce({ Parameters: mockCloudParams.slice(10) }); + + mockSsmClient + .on(GetParametersCommand) + .resolvesOnce({ + Parameters: mockCloudParams.slice(0, 10), + $metadata: {}, + }) + .resolvesOnce({ + Parameters: mockCloudParams.slice(10), + $metadata: {}, + }); const returnedFn = await getEnvParametersDownloadHandler(contextMock); expect(returnedFn).toBeDefined(); const mockParams = await returnedFn(keys); - expect(getParametersMock).toBeCalledTimes(2); - expect(getParametersMock).toBeCalledWith({ + + expect(mockSsmClient.calls().length).toBe(2); + expect(mockSsmClient).toHaveReceivedCommandTimes(GetParametersCommand, 2); + + // Check first call + expect(mockSsmClient.call(0).args[0].input).toEqual({ Names: expectedKeyPaths.slice(0, 10), WithDecryption: false, }); - expect(getParametersMock).toBeCalledWith({ + + // Check second call + expect(mockSsmClient.call(1).args[0].input).toEqual({ Names: expectedKeyPaths.slice(10), WithDecryption: false, }); + expect(mockParams).toStrictEqual(expectedParams); - expect(getParametersPromiseMock).toBeCalledTimes(2); }); }); diff --git a/packages/amplify-provider-awscloudformation/src/amplify-service-manager.ts b/packages/amplify-provider-awscloudformation/src/amplify-service-manager.ts index 417af98d110..0ae0a57c426 100644 --- a/packages/amplify-provider-awscloudformation/src/amplify-service-manager.ts +++ b/packages/amplify-provider-awscloudformation/src/amplify-service-manager.ts @@ -9,6 +9,15 @@ import { checkAmplifyServiceIAMPermission } from './amplify-service-permission-c import { $TSContext, stateManager, AmplifyFault, AmplifyError } from '@aws-amplify/amplify-cli-core'; import { fileLogger } from './utils/aws-logger'; import { loadConfigurationForEnv } from './configuration-manager'; +import { + CreateAppCommand, + CreateBackendEnvironmentCommand, + DeleteBackendEnvironmentCommand, + GetAppCommand, + GetBackendEnvironmentCommand, + ListAppsCommand, + ListBackendEnvironmentsCommand, +} from '@aws-sdk/client-amplify'; const logger = fileLogger('amplify-service-manager'); @@ -47,11 +56,7 @@ export async function init(amplifyServiceParams) { ])(); try { - const getAppResult = await amplifyClient - .getApp({ - appId: inputAmplifyAppId, - }) - .promise(); + const getAppResult = await amplifyClient.send(new GetAppCommand({ appId: inputAmplifyAppId })); context.print.info(`Amplify AppID found: ${inputAmplifyAppId}. Amplify App name is: ${getAppResult.app.name}`); amplifyAppId = inputAmplifyAppId; } catch (e) { @@ -95,12 +100,12 @@ export async function init(amplifyServiceParams) { maxResults: 25, }, ])(); - listAppsResponse = await amplifyClient - .listApps({ + listAppsResponse = await amplifyClient.send( + new ListAppsCommand({ nextToken: listAppsResponse.nextToken, maxResults: 25, - }) - .promise(); + }), + ); apps = apps.concat(listAppsResponse.apps); } while (listAppsResponse.nextToken); @@ -126,11 +131,11 @@ export async function init(amplifyServiceParams) { logger('init.amplifyClient.createApp', [createAppParams])(); try { if (amplifyAppCreationEnabled()) { - const createAppResponse = await amplifyClient.createApp(createAppParams).promise(); + const createAppResponse = await amplifyClient.send(new CreateAppCommand(createAppParams)); amplifyAppId = createAppResponse.app.appId; } } catch (e) { - if (e.code === 'LimitExceededException') { + if (e.name === 'LimitExceededException') { throw new AmplifyError( 'ProjectInitError', { @@ -141,9 +146,9 @@ export async function init(amplifyServiceParams) { e, ); } - if (context?.exeInfo?.awsConfigInfo?.configLevel === 'general' && e.code === 'ConfigError') { + if (context?.exeInfo?.awsConfigInfo?.configLevel === 'general' && e.name === 'ConfigError') { throw new AmplifyError('ConfigurationError', { - code: e.code, + code: e.name, message: e.message, resolution: 'https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/setting-credentials-node.html', }); @@ -177,12 +182,12 @@ export async function init(amplifyServiceParams) { try { log(); - const { backendEnvironment } = await amplifyClient - .getBackendEnvironment({ + const { backendEnvironment } = await amplifyClient.send( + new GetBackendEnvironmentCommand({ appId: amplifyAppId, environmentName: envName, - }) - .promise(); + }), + ); if (backendEnvironment) { verifiedStackName = backendEnvironment.stackName; @@ -204,7 +209,7 @@ export async function init(amplifyServiceParams) { deploymentArtifacts: deploymentBucketName, }; logger('init.amplifyClient.getBackendEnvironment', [createEnvParams])(); - await amplifyClient.createBackendEnvironment(createEnvParams).promise(); + await amplifyClient.send(new CreateBackendEnvironmentCommand(createEnvParams)); } return { @@ -241,9 +246,9 @@ export async function deleteEnv(context: $TSContext, envName: string, awsConfigI }; logger('deleteEnv.amplifyClient.deleteBackendEnvironment', [deleteEnvParams])(); try { - await amplifyClient.deleteBackendEnvironment(deleteEnvParams).promise(); + await amplifyClient.send(new DeleteBackendEnvironmentCommand(deleteEnvParams)); } catch (ex) { - if (ex.code === 'NotFoundException') { + if (ex.name === 'NotFoundException') { context.print.warning(ex.message); } else { throw new AmplifyFault( @@ -325,14 +330,14 @@ export async function postPushCheck(context) { logger('postPushCheck.amplifyClient.createApp', [createAppParams])(); try { if (amplifyAppCreationEnabled()) { - const createAppResponse = await amplifyClient.createApp(createAppParams).promise(); + const createAppResponse = await amplifyClient.send(new CreateAppCommand(createAppParams)); amplifyAppId = createAppResponse.app.appId; } } catch (e) { - if (e.code === 'LimitExceededException') { + if (e.name === 'LimitExceededException') { // Do nothing } else if ( - e.code === 'BadRequestException' && + e.name === 'BadRequestException' && e.message.includes('Rate exceeded while calling CreateApp, please slow down or try again later.') ) { // Do nothing @@ -359,7 +364,7 @@ export async function postPushCheck(context) { deploymentArtifacts: teamProviderInfo[envName][ProviderName].DeploymentBucketName, }; logger('postPushCheck.amplifyClient.createBackendEnvironment', [createEnvParams])(); - await amplifyClient.createBackendEnvironment(createEnvParams).promise(); + await amplifyClient.send(new CreateBackendEnvironmentCommand(createEnvParams)); } providerMeta[AmplifyAppIdLabel] = amplifyAppId; @@ -439,12 +444,12 @@ async function searchAmplifyService(amplifyClient, stackName): Promise => { }, ])(); try { - const getAppResult = await amplifyClient - .getApp({ - appId: amplifyAppId, - }) - .promise(); + const getAppResult = await amplifyClient.send(new GetAppCommand({ appId: amplifyAppId })); context.print.info(`Amplify AppID found: ${amplifyAppId}. Amplify App name is: ${getAppResult.app.name}`); } catch (e) { throw new AmplifyError( @@ -93,12 +95,12 @@ export const run = async (context): Promise => { nextToken: listEnvResponse.nextToken, }, ])(); - listEnvResponse = await amplifyClient - .listBackendEnvironments({ + listEnvResponse = await amplifyClient.send( + new ListBackendEnvironmentsCommand({ appId: amplifyAppId, nextToken: listEnvResponse.nextToken, - }) - .promise(); + }), + ); backendEnvs = backendEnvs.concat(listEnvResponse.backendEnvironments); } while (listEnvResponse.nextToken); @@ -115,7 +117,7 @@ export const run = async (context): Promise => { const log = logger('run.amplifyClient.createBackendEnvironment', [createEnvParams]); log(); try { - await amplifyClient.createBackendEnvironment(createEnvParams).promise(); + await amplifyClient.send(new CreateBackendEnvironmentCommand(createEnvParams)); } catch (ex) { log(ex); } @@ -126,7 +128,7 @@ export const run = async (context): Promise => { environmentName: envName, }; logger('run.amplifyClient.getBackendEnvironment', [getEnvParams])(); - const { backendEnvironment } = await amplifyClient.getBackendEnvironment(getEnvParams).promise(); + const { backendEnvironment } = await amplifyClient.send(new GetBackendEnvironmentCommand(getEnvParams)); if (StackName !== backendEnvironment.stackName) { throw new AmplifyError('InvalidStackError', { message: `Stack name mismatch for the backend environment ${envName}. Local: ${StackName}, Amplify: ${backendEnvironment.stackName}`, diff --git a/packages/amplify-provider-awscloudformation/src/amplify-service-permission-check.js b/packages/amplify-provider-awscloudformation/src/amplify-service-permission-check.js index ffbddfe6719..074e8647f47 100644 --- a/packages/amplify-provider-awscloudformation/src/amplify-service-permission-check.js +++ b/packages/amplify-provider-awscloudformation/src/amplify-service-permission-check.js @@ -1,5 +1,6 @@ const { printAuthErrorMessage } = require('./aws-utils/aws-amplify'); const { fileLogger } = require('./utils/aws-logger'); +const { ListAppsCommand } = require('@aws-sdk/client-amplify'); const logger = fileLogger('amplify-service-permission-check'); async function checkAmplifyServiceIAMPermission(context, amplifyClient) { @@ -8,10 +9,10 @@ async function checkAmplifyServiceIAMPermission(context, amplifyClient) { try { log(); - await amplifyClient.listApps().promise(); + await amplifyClient.send(new ListAppsCommand({})); } catch (e) { log(e); - if (e.code === 'UnauthorizedException') { + if (e.name === 'UnauthorizedException') { printAuthErrorMessage(context); hasAmplifyServicePermission = false; } diff --git a/packages/amplify-provider-awscloudformation/src/attach-backend.ts b/packages/amplify-provider-awscloudformation/src/attach-backend.ts index 2cd911fcf3a..7cb369d796f 100644 --- a/packages/amplify-provider-awscloudformation/src/attach-backend.ts +++ b/packages/amplify-provider-awscloudformation/src/attach-backend.ts @@ -1,4 +1,3 @@ -import S3 from 'aws-sdk/clients/s3'; import fs from 'fs-extra'; import path from 'path'; import { globSync } from 'glob'; @@ -13,6 +12,9 @@ import { isAmplifyAdminApp } from './utils/admin-helpers'; import { resolveAppId } from './utils/resolve-appId'; import { adminLoginFlow } from './admin-login'; import { fileLogger } from './utils/aws-logger'; +import { GetObjectCommand, PutObjectCommand, S3Client } from '@aws-sdk/client-s3'; +import { GetAppCommand, ListAppsCommand, GetBackendEnvironmentCommand, ListBackendEnvironmentsCommand } from '@aws-sdk/client-amplify'; +import { streamToBuffer } from './zip-util'; const logger = fileLogger('attach-backend'); @@ -118,7 +120,7 @@ async function ensureAmplifyMeta(context, amplifyApp, awsConfigInfo) { async function storeArtifactsForAmplifyService(context, awsConfigInfo, deploymentBucketName) { const projectPath = process.cwd(); - const s3Client = new S3(awsConfigInfo); + const s3Client = new S3Client(awsConfigInfo); const amplifyMetaFilePath = context.amplify.pathManager.getCurrentAmplifyMetaFilePath(projectPath); const backendConfigFilePath = context.amplify.pathManager.getCurrentBackendConfigFilePath(projectPath); await uploadFile(s3Client, deploymentBucketName, amplifyMetaFilePath); @@ -137,7 +139,7 @@ async function uploadFile(s3Client, bucketName, filePath) { Body: body, }; logger('uploadFile.s3.uploadFile', [{ Key: key, Bucket: bucketName }])(); - await s3Client.putObject(s3Params).promise(); + await s3Client.send(new PutObjectCommand(s3Params)); } async function getAmplifyApp(context, amplifyClient) { @@ -151,11 +153,11 @@ async function getAmplifyApp(context, amplifyClient) { }, ])(); try { - const getAppResult = await amplifyClient - .getApp({ + const getAppResult = await amplifyClient.send( + new GetAppCommand({ appId: inputAmplifyAppId, - }) - .promise(); + }), + ); context.print.info(`Amplify AppID found: ${inputAmplifyAppId}. Amplify App name is: ${getAppResult.app.name}`); return getAppResult.app; } catch (e) { @@ -185,12 +187,12 @@ async function getAmplifyApp(context, amplifyClient) { }, ])(); - listAppsResponse = await amplifyClient - .listApps({ + listAppsResponse = await amplifyClient.send( + new ListAppsCommand({ nextToken: listAppsResponse.nextToken, maxResults: 25, - }) - .promise(); + }), + ); apps = apps.concat(listAppsResponse.apps); } while (listAppsResponse.nextToken); @@ -233,12 +235,12 @@ async function getBackendEnv(context, amplifyClient, amplifyApp) { }, ])(); try { - const getBackendEnvironmentResult = await amplifyClient - .getBackendEnvironment({ + const getBackendEnvironmentResult = await amplifyClient.send( + new GetBackendEnvironmentCommand({ appId: amplifyApp.appId, environmentName: inputEnvName, - }) - .promise(); + }), + ); context.print.info(`Backend environment ${inputEnvName} found in Amplify Console app: ${amplifyApp.name}`); return getBackendEnvironmentResult.backendEnvironment; } catch (e) { @@ -263,12 +265,12 @@ async function getBackendEnv(context, amplifyClient, amplifyApp) { nextToken: listEnvResponse.nextToken, }, ])(); - listEnvResponse = await amplifyClient - .listBackendEnvironments({ + listEnvResponse = await amplifyClient.send( + new ListBackendEnvironmentsCommand({ appId: amplifyApp.appId, nextToken: listEnvResponse.nextToken, - }) - .promise(); + }), + ); backendEnvs = backendEnvs.concat(listEnvResponse.backendEnvironments); } while (listEnvResponse.nextToken); @@ -313,7 +315,7 @@ async function downloadBackend(context, backendEnv, awsConfigInfo) { const backendDir = context.amplify.pathManager.getBackendDirPath(projectPath); const zipFileName = constants.S3BackendZipFileName; - const s3Client = new S3(awsConfigInfo); + const s3Client = new S3Client(awsConfigInfo); const deploymentBucketName = backendEnv.deploymentArtifacts; const params = { @@ -325,7 +327,7 @@ async function downloadBackend(context, backendEnv, awsConfigInfo) { let zipObject = null; try { log(); - zipObject = await s3Client.getObject(params).promise(); + zipObject = await s3Client.send(new GetObjectCommand(params)); } catch (err) { log(err); context.print.error(`Error downloading ${zipFileName} from deployment bucket: ${deploymentBucketName}, the error is: ${err.message}`); @@ -334,7 +336,7 @@ async function downloadBackend(context, backendEnv, awsConfigInfo) { return; } - const buff = Buffer.from(zipObject.Body); + const buff = await streamToBuffer(zipObject.Body); fs.ensureDirSync(tempDirPath); diff --git a/packages/amplify-provider-awscloudformation/src/aws-utils/DynamoDBService.ts b/packages/amplify-provider-awscloudformation/src/aws-utils/DynamoDBService.ts index fb3bbec4802..02b14a049d1 100644 --- a/packages/amplify-provider-awscloudformation/src/aws-utils/DynamoDBService.ts +++ b/packages/amplify-provider-awscloudformation/src/aws-utils/DynamoDBService.ts @@ -1,6 +1,13 @@ import { $TSAny, $TSContext } from '@aws-amplify/amplify-cli-core'; import { IDynamoDBService } from '@aws-amplify/amplify-util-import'; -import DynamoDB, { ListTablesInput, ListTablesOutput, TableDescription, TableName } from 'aws-sdk/clients/dynamodb'; +import { + DynamoDBClient, + ListTablesCommand, + ListTablesCommandInput, + ListTablesCommandOutput, + DescribeTableCommand, + TableDescription, +} from '@aws-sdk/client-dynamodb'; import { loadConfiguration } from '../configuration-manager'; import { pagedAWSCall } from './paged-call'; @@ -13,26 +20,28 @@ export const createDynamoDBService = async (context: $TSContext, options: $TSAny // could not load credentials } - const dynamoDB = new DynamoDB({ ...credentials, ...options }); + const dynamoDBClient = new DynamoDBClient({ + ...credentials, + ...options, + }); - return new DynamoDBService(dynamoDB); + return new DynamoDBService(dynamoDBClient); }; export class DynamoDBService implements IDynamoDBService { private cachedTableList: string[] = []; - public constructor(private dynamoDB: DynamoDB) {} + public constructor(private dynamoDBClient: DynamoDBClient) {} - public async listTables(): Promise { + public async listTables(): Promise { if (this.cachedTableList.length === 0) { - const result = await pagedAWSCall( - async (params: ListTablesInput, nextToken: TableName) => { - return await this.dynamoDB - .listTables({ - ...params, - ExclusiveStartTableName: nextToken, - }) - .promise(); + const result = await pagedAWSCall( + async (params: ListTablesCommandInput, nextToken: string) => { + const command = new ListTablesCommand({ + ...params, + ExclusiveStartTableName: nextToken, + }); + return await this.dynamoDBClient.send(command); }, { Limit: 100, @@ -48,11 +57,10 @@ export class DynamoDBService implements IDynamoDBService { } public async getTableDetails(tableName: string): Promise { - const response = await this.dynamoDB - .describeTable({ - TableName: tableName, - }) - .promise(); + const command = new DescribeTableCommand({ + TableName: tableName, + }); + const response = await this.dynamoDBClient.send(command); return response.Table; } diff --git a/packages/amplify-provider-awscloudformation/src/aws-utils/S3Service.ts b/packages/amplify-provider-awscloudformation/src/aws-utils/S3Service.ts index c8a2578ef7a..898786ab682 100644 --- a/packages/amplify-provider-awscloudformation/src/aws-utils/S3Service.ts +++ b/packages/amplify-provider-awscloudformation/src/aws-utils/S3Service.ts @@ -1,24 +1,26 @@ import { $TSContext, AmplifyFault } from '@aws-amplify/amplify-cli-core'; import { IS3Service } from '@aws-amplify/amplify-util-import'; -import { S3 } from 'aws-sdk'; -import { Bucket } from 'aws-sdk/clients/s3'; +import { S3Client, ListBucketsCommand, HeadBucketCommand, GetBucketLocationCommand, Bucket } from '@aws-sdk/client-s3'; import { AwsSecrets, loadConfiguration } from '../configuration-manager'; export const createS3Service = async (context: $TSContext): Promise => { const credentials = await tryGetCredentials(context); - const s3 = new S3({ ...credentials }); + const s3Client = new S3Client({ + ...credentials, + }); - return new S3Service(s3); + return new S3Service(s3Client); }; export class S3Service implements IS3Service { private cachedBucketList: Bucket[] = []; - public constructor(private s3: S3) {} + public constructor(private s3Client: S3Client) {} public async listBuckets(): Promise { if (this.cachedBucketList.length === 0) { - const response = await this.s3.listBuckets().promise(); + const command = new ListBucketsCommand({}); + const response = await this.s3Client.send(command); if (response.Buckets) { this.cachedBucketList.push(...response.Buckets); @@ -28,16 +30,24 @@ export class S3Service implements IS3Service { return this.cachedBucketList; } - private async checkIfBucketExists(bucketName: string, s3?: S3): Promise { - const s3Client = s3 ?? this.s3; + private async checkIfBucketExists(bucketName: string, s3Client?: S3Client): Promise { + const client = s3Client ?? this.s3Client; try { - const response = await s3Client.headBucket({ Bucket: bucketName }).promise(); + const command = new HeadBucketCommand({ Bucket: bucketName }); + const response = await client.send(command); // If the return object has no keys then it means successful empty object was returned. return Object.keys(response).length === 0; } catch (error) { + console.log(error); // workaround for S3 service bug causing headBucket for a opt-in region bucket to respond with BadRequest if s3 client is initialized with a different region - if (error.region !== s3Client.config.region && error.code === 'BadRequest') { - return this.checkIfBucketExists(bucketName, new S3({ ...s3Client.config?.credentials, region: error.region })); + if (error.name === 'BadRequest' || error.$metadata.httpStatusCode === 301) { + // if bucket is in different region, followRegionRedirects should allow us to find it + const newClient = new S3Client({ + ...client.config?.credentials, + region: client.config?.region, + followRegionRedirects: true, + }); + return this.checkIfBucketExists(bucketName, newClient); } return handleS3Error(error); @@ -49,23 +59,27 @@ export class S3Service implements IS3Service { } public async getBucketLocation(bucketName: string): Promise { - const response = await this.s3 - .getBucketLocation({ - Bucket: bucketName, - }) - .promise(); + const command = new GetBucketLocationCommand({ + Bucket: bucketName, + }); + const response = await this.s3Client.send(command); + // For us-east-1 buckets the LocationConstraint is always empty, we have to return a // region in every case. // https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketLocation.html - if (response.LocationConstraint === undefined || response.LocationConstraint === '' || response.LocationConstraint === null) { + if ( + response.LocationConstraint === undefined || + response.LocationConstraint.toString() === '' || + response.LocationConstraint === null + ) { return 'us-east-1'; } return response.LocationConstraint; } } -const handleS3Error = (error: { code: string; message: string }): boolean => { - if (error.code === 'NotFound') { +const handleS3Error = (error: { name: string; message: string }): boolean => { + if (error.name === 'NotFound') { return false; } throw new AmplifyFault( diff --git a/packages/amplify-provider-awscloudformation/src/aws-utils/aws-amplify.js b/packages/amplify-provider-awscloudformation/src/aws-utils/aws-amplify.js index 50f6d5c626c..c4c06233450 100644 --- a/packages/amplify-provider-awscloudformation/src/aws-utils/aws-amplify.js +++ b/packages/amplify-provider-awscloudformation/src/aws-utils/aws-amplify.js @@ -1,4 +1,5 @@ -const aws = require('aws-sdk'); +const { AmplifyClient } = require('@aws-sdk/client-amplify'); +const { NodeHttpHandler } = require('@smithy/node-http-handler'); const configurationManager = require('../configuration-manager'); const { regions: amplifyServiceRegions } = require('../aws-regions'); const { proxyAgent } = require('./aws-globals'); @@ -24,20 +25,21 @@ async function getConfiguredAmplifyClient(context, options = {}) { ...cred, ...defaultOptions, ...options, - httpOptions: { - agent: proxyAgent(), - }, + requestHandler: new NodeHttpHandler({ + httpAgent: proxyAgent(), + httpsAgent: proxyAgent(), + }), }; // this is the "project" config level case, creds and region are explicitly set or retrieved from a profile if (config.region) { if (amplifyServiceRegions.includes(config.region)) { - return new aws.Amplify(config); + return new AmplifyClient(config); } return undefined; } // this is the "general" config level case, aws sdk will resolve creds and region from env variables etc. - return new aws.Amplify(config); + return new AmplifyClient(config); } function printAuthErrorMessage(context) { diff --git a/packages/amplify-provider-awscloudformation/src/aws-utils/aws-apigw.ts b/packages/amplify-provider-awscloudformation/src/aws-utils/aws-apigw.ts index 7d11020cc38..e9e70e03d40 100644 --- a/packages/amplify-provider-awscloudformation/src/aws-utils/aws-apigw.ts +++ b/packages/amplify-provider-awscloudformation/src/aws-utils/aws-apigw.ts @@ -1,13 +1,13 @@ import { $TSContext } from '@aws-amplify/amplify-cli-core'; -import aws from './aws.js'; -import { APIGateway as APIGW } from 'aws-sdk'; +import { APIGatewayClient, APIGatewayClientConfig } from '@aws-sdk/client-api-gateway'; +import { NodeHttpHandler } from '@smithy/node-http-handler'; import { loadConfiguration } from '../configuration-manager'; import { proxyAgent } from './aws-globals'; export class APIGateway { private static instance: APIGateway; private readonly context: $TSContext; - public readonly apigw: APIGW; + public readonly apigw: APIGatewayClient; static async getInstance(context: $TSContext, options = {}): Promise { if (!APIGateway.instance) { @@ -25,12 +25,16 @@ export class APIGateway { constructor(context: $TSContext, creds, options = {}) { this.context = context; - this.apigw = new aws.APIGateway({ + + const clientConfig: APIGatewayClientConfig = { ...creds, ...options, - httpOptions: { - agent: proxyAgent(), - }, - }); + requestHandler: new NodeHttpHandler({ + httpAgent: proxyAgent(), + httpsAgent: proxyAgent(), + }), + }; + + this.apigw = new APIGatewayClient(clientConfig); } } diff --git a/packages/amplify-provider-awscloudformation/src/aws-utils/aws-appsync.js b/packages/amplify-provider-awscloudformation/src/aws-utils/aws-appsync.js index 44be1b9c3bf..66ba526a812 100644 --- a/packages/amplify-provider-awscloudformation/src/aws-utils/aws-appsync.js +++ b/packages/amplify-provider-awscloudformation/src/aws-utils/aws-appsync.js @@ -1,4 +1,5 @@ -const aws = require('./aws.js'); +const { AppSyncClient } = require('@aws-sdk/client-appsync'); +const { NodeHttpHandler } = require('@smithy/node-http-handler'); const configurationManager = require('../configuration-manager'); const { proxyAgent } = require('./aws-globals'); @@ -13,12 +14,13 @@ class AppSync { } this.context = context; - this.appSync = new aws.AppSync({ + this.appSync = new AppSyncClient({ ...cred, ...options, - httpOptions: { - agent: proxyAgent(), - }, + requestHandler: new NodeHttpHandler({ + httpAgent: proxyAgent(), + httpsAgent: proxyAgent(), + }), }); return this; })(); diff --git a/packages/amplify-provider-awscloudformation/src/aws-utils/aws-cfn.js b/packages/amplify-provider-awscloudformation/src/aws-utils/aws-cfn.js index 7dedd13908b..7b1e9bf642b 100644 --- a/packages/amplify-provider-awscloudformation/src/aws-utils/aws-cfn.js +++ b/packages/amplify-provider-awscloudformation/src/aws-utils/aws-cfn.js @@ -7,7 +7,22 @@ const BottleNeck = require('bottleneck'); const chalk = require('chalk'); const columnify = require('columnify'); -const aws = require('./aws'); +const { + CloudFormationClient, + CreateStackCommand, + UpdateStackCommand, + DeleteStackCommand, + DescribeStacksCommand, + ListStacksCommand, + ListStackResourcesCommand, + ListExportsCommand, + DescribeStackEventsCommand, + waitUntilStackCreateComplete, + waitUntilStackUpdateComplete, + waitUntilStackDeleteComplete, +} = require('@aws-sdk/client-cloudformation'); +const { NodeHttpHandler } = require('@smithy/node-http-handler'); + const { S3 } = require('./aws-s3'); const providerName = require('../constants').ProviderName; const { formUserAgentParam } = require('./user-agent'); @@ -56,14 +71,16 @@ class CloudFormation { userAgentOption.customUserAgent = userAgentParam; } - this.cfn = new aws.CloudFormation({ + this.cfn = new CloudFormationClient({ ...cred, ...options, ...userAgentOption, - httpOptions: { - agent: proxyAgent(), - }, + requestHandler: new NodeHttpHandler({ + httpAgent: proxyAgent(), + httpsAgent: proxyAgent(), + }), }); + this.context = context; if (Object.keys(eventMap).length) { this.eventMap = eventMap; @@ -76,44 +93,48 @@ class CloudFormation { createResourceStack(cfnParentStackParams) { const cfnModel = this.cfn; const { context } = this; - const cfnCompleteStatus = 'stackCreateComplete'; const cfnStackCheckParams = { StackName: cfnParentStackParams.StackName, }; const self = this; self.eventStartTime = new Date(); - return new Promise((resolve, reject) => { + return new Promise(async (resolve, reject) => { logger('cfnModel.createStack', [cfnParentStackParams])(); - cfnModel.createStack(cfnParentStackParams, (createErr) => { + try { + await cfnModel.send(new CreateStackCommand(cfnParentStackParams)); this.readStackEvents(cfnParentStackParams.StackName); + } catch (createErr) { logger('cfnModel.createStack', [cfnParentStackParams])(createErr); - if (createErr) { - context.print.error('\nAn error occurred when creating the CloudFormation stack'); - reject(createErr); + context.print.error('\nAn error occurred when creating the CloudFormation stack'); + return reject(createErr); + } + try { + await waitUntilStackCreateComplete({ client: cfnModel }, cfnStackCheckParams); + + if (self.pollForEvents) { + clearTimeout(self.pollForEvents); } - cfnModel.waitFor(cfnCompleteStatus, cfnStackCheckParams, async (completeErr, waitForStackdata) => { - if (self.pollForEvents) { - clearTimeout(self.pollForEvents); - } - this.progressBar?.stop(); + this.progressBar?.stop(); + resolve(cfnModel.send(new DescribeStacksCommand(cfnStackCheckParams))); + } catch (completeErr) { + if (self.pollForEvents) { + clearTimeout(self.pollForEvents); + } - if (completeErr) { - context.print.error('\nAn error occurred when creating the CloudFormation stack'); - const errorDetails = await this.collectStackErrors(cfnParentStackParams.StackName); - logger('cfnModel.createStack', [cfnParentStackParams])(completeErr); - const error = new AmplifyFault( - 'DeploymentFault', - { message: 'Initialization of project failed', details: errorDetails }, - completeErr, - ); - error.stack = null; - reject(error); - } - resolve(waitForStackdata); - }); - }); + this.progressBar?.stop(); + context.print.error('\nAn error occurred when creating the CloudFormation stack'); + const errorDetails = await this.collectStackErrors(cfnParentStackParams.StackName); + logger('cfnModel.createStack', [cfnParentStackParams])(completeErr); + const error = new AmplifyFault( + 'DeploymentFault', + { message: 'Initialization of project failed', details: errorDetails }, + completeErr, + ); + error.stack = null; + reject(error); + } }); } @@ -302,38 +323,31 @@ class CloudFormation { } } - getStackEvents(stackName) { + async getStackEvents(stackName) { const self = this; const describeStackEventsArgs = { StackName: stackName }; const log = logger('getStackEvents.cfnModel.describeStackEvents', [describeStackEventsArgs]); log(); - return this.cfn - .describeStackEvents({ StackName: stackName }) - .promise() - .then((data) => { - let events = data.StackEvents; - events = events.filter((event) => self.eventStartTime < new Date(event.Timestamp)); - return Promise.resolve(events); - }) - .catch((e) => { - log(e); - if (e && e.code === 'Throttling') { - return Promise.resolve([]); - } - return Promise.reject(e); - }); + try { + const data = await this.cfn.send(new DescribeStackEventsCommand({ StackName: stackName })); + let events = data.StackEvents; + events = events.filter((event) => self.eventStartTime < new Date(event.Timestamp)); + return events; + } catch (e) { + log(e); + if (e && e.name === 'Throttling') { + return []; + } + throw e; + } } - getStackParameters(stackName) { - return this.cfn - .describeStack({ StackName: stackName }) - .promise() - .then((data) => { - return data.Parameters; - }); + async getStackParameters(stackName) { + const data = await this.cfn.send(new DescribeStacksCommand({ StackName: stackName })); + return data.Stacks[0].Parameters; } - updateResourceStack(filePath) { + async updateResourceStack(filePath) { try { const backEndDir = pathManager.getBackendDirPath(pathManager.findProjectRoot()); const providerDirectory = path.normalize(path.join(backEndDir, providerName)); @@ -365,91 +379,90 @@ class CloudFormation { }); } - return S3.getInstance(this.context) - .then((s3) => { - const s3Params = { - Body: fs.createReadStream(filePath), - Key: cfnFile, - }; - logger('updateResourceStack.s3.uploadFile', [{ Key: s3Params.cfnFile }])(); - return s3.uploadFile(s3Params, false); - }) - .then((bucketName) => { - const templateURL = `https://s3.amazonaws.com/${bucketName}/${cfnFile}`; - const cfnStackCheckParams = { - StackName: stackName, - }; - const cfnModel = this.cfn; - const { context } = this; - const self = this; - this.eventStartTime = new Date(); - return new Promise((resolve, reject) => { - logger('updateResourceStack.describeStack', [cfnStackCheckParams])(); - this.describeStack(cfnStackCheckParams) - .then(() => { - const cfnParentStackParams = { - StackName: stackName, - TemplateURL: templateURL, - Capabilities: ['CAPABILITY_NAMED_IAM', 'CAPABILITY_AUTO_EXPAND'], - Parameters: [ - { - ParameterKey: 'DeploymentBucketName', - ParameterValue: deploymentBucketName, - }, - { - ParameterKey: 'AuthRoleName', - ParameterValue: authRoleName, - }, - { - ParameterKey: 'UnauthRoleName', - ParameterValue: unauthRoleName, - }, - ], - Tags, - }; - logger('updateResourceStack.updateStack', [cfnStackCheckParams])(); - cfnModel.updateStack(cfnParentStackParams, (updateErr) => { - self.readStackEvents(stackName); - - const cfnCompleteStatus = 'stackUpdateComplete'; - if (updateErr) { - if (self.pollForEvents) { - clearTimeout(self.pollForEvents); - } - return reject(updateErr); - } - cfnModel.waitFor(cfnCompleteStatus, cfnStackCheckParams, async (completeErr) => { - if (self.pollForEvents) { - clearTimeout(self.pollForEvents); - } - this.progressBar?.stop(); - - if (completeErr) { - await this.collectStackErrors(cfnParentStackParams.StackName).then((errorDetails) => { - completeErr.details = errorDetails; - reject(completeErr); - }); - } else { - self.context.usageData.calculatePushNormalizationFactor(this.stackEvents, stackId); - await self.updateamplifyMetaFileWithStackOutputs(stackName); - return resolve(); - } - }); - }); - }) - .catch((err) => { - reject(new Error("Project stack doesn't exist")); - context.print.info(err.stack); - }); - }); - }); + const s3 = await S3.getInstance(this.context); + const s3Params = { + Body: fs.createReadStream(filePath), + Key: cfnFile, + }; + logger('updateResourceStack.s3.uploadFile', [{ Key: s3Params.cfnFile }])(); + const bucketName = await s3.uploadFile(s3Params, false); + + const templateURL = `https://s3.amazonaws.com/${bucketName}/${cfnFile}`; + const cfnStackCheckParams = { + StackName: stackName, + }; + const cfnModel = this.cfn; + const { context } = this; + const self = this; + this.eventStartTime = new Date(); + + try { + logger('updateResourceStack.describeStack', [cfnStackCheckParams])(); + await this.describeStack(cfnStackCheckParams); + + const cfnParentStackParams = { + StackName: stackName, + TemplateURL: templateURL, + Capabilities: ['CAPABILITY_NAMED_IAM', 'CAPABILITY_AUTO_EXPAND'], + Parameters: [ + { + ParameterKey: 'DeploymentBucketName', + ParameterValue: deploymentBucketName, + }, + { + ParameterKey: 'AuthRoleName', + ParameterValue: authRoleName, + }, + { + ParameterKey: 'UnauthRoleName', + ParameterValue: unauthRoleName, + }, + ], + Tags, + }; + + logger('updateResourceStack.updateStack', [cfnStackCheckParams])(); + try { + await cfnModel.send(new UpdateStackCommand(cfnParentStackParams)); + self.readStackEvents(stackName); + + await waitUntilStackUpdateComplete({ client: cfnModel }, cfnStackCheckParams); + + if (self.pollForEvents) { + clearTimeout(self.pollForEvents); + } + this.progressBar?.stop(); + + context.usageData.calculatePushNormalizationFactor(this.stackEvents, stackId); + await self.updateamplifyMetaFileWithStackOutputs(stackName); + } catch (updateErr) { + if (self.pollForEvents) { + clearTimeout(self.pollForEvents); + } + throw updateErr; + } + } catch (err) { + if (err.message.includes('No updates are to be performed')) { + // This is not a real error, the stack is already up to date + return; + } + + this.progressBar?.stop(); + + if (err.name !== 'ValidationError') { + const errorDetails = await this.collectStackErrors(stackName); + err.details = errorDetails; + } + + throw err; + } } catch (error) { this.progressBar?.stop(); throw new AmplifyFault( 'ResourceNotReadyFault', { message: error.message, - code: error.code, + code: error.name, }, error, ); @@ -457,12 +470,13 @@ class CloudFormation { } async listStacks(nextToken = null, stackStatusFilter) { - return await this.cfn - .listStacks({ + const result = await this.cfn.send( + new ListStacksCommand({ NextToken: nextToken, StackStatusFilter: stackStatusFilter, - }) - .promise(); + }), + ); + return result; } async updateamplifyMetaFileWithStackOutputs(parentStackName) { @@ -475,7 +489,7 @@ class CloudFormation { const stackSummaries = await pagedAWSCall( async (params, nextToken) => { - return await this.cfn.listStackResources({ ...params, NextToken: nextToken }).promise(); + return await this.cfn.send(new ListStackResourcesCommand({ ...params, NextToken: nextToken })); }, { StackName: parentStackName, @@ -581,43 +595,37 @@ class CloudFormation { } } - listExports(nextToken = null) { + async listExports(nextToken = null) { const log = logger('listExports.cfn.listExports', [{ NextToken: nextToken }]); - return new Promise((resolve, reject) => { - log(); - this.cfn.listExports(nextToken ? { NextToken: nextToken } : {}, async (err, data) => { - if (err) { - log(err); - reject(err); - } else if (data.NextToken) { - await this.listExports(data.NextToken).then((innerExports) => resolve([...data.Exports, ...innerExports])); - } else { - resolve(data.Exports); - } - }); - }); + log(); + try { + const data = await this.cfn.send(new ListExportsCommand(nextToken ? { NextToken: nextToken } : {})); + if (data.NextToken) { + const innerExports = await this.listExports(data.NextToken); + return [...data.Exports, ...innerExports]; + } + return data.Exports; + } catch (err) { + log(err); + throw err; + } } - describeStack(cfnNestedStackParams, maxTry = 10, timeout = CFN_POLL_TIME) { + async describeStack(cfnNestedStackParams, maxTry = 10, timeout = CFN_POLL_TIME) { const cfnModel = this.cfn; const log = logger('describeStack.cfn.describeStacks', [cfnNestedStackParams]); - return new Promise((resolve, reject) => { + try { log(); - cfnModel - .describeStacks(cfnNestedStackParams) - .promise() - .then((result) => resolve(result)) - .catch((e) => { - log(e); - if (e.code === 'Throttling' && e.retryable) { - setTimeout(() => { - resolve(this.describeStack(cfnNestedStackParams, maxTry - 1, timeout)); - }, timeout); - } else { - reject(e); - } - }); - }); + const result = await cfnModel.send(new DescribeStacksCommand(cfnNestedStackParams)); + return result; + } catch (e) { + log(e); + if (e.name === 'Throttling' && e.retryable && maxTry > 0) { + await new Promise((resolve) => setTimeout(resolve, timeout)); + return this.describeStack(cfnNestedStackParams, maxTry - 1, timeout); + } + throw e; + } } async listStackResources(stackId) { @@ -629,10 +637,11 @@ class CloudFormation { }); } // StackName param can be a StackName, StackId, or a PhysicalResourceId - return this.cfn.listStackResources({ StackName: stackId }).promise(); + const result = await this.cfn.send(new ListStackResourcesCommand({ StackName: stackId })); + return result; } - deleteResourceStack(envName) { + async deleteResourceStack(envName) { const teamProviderInfo = stateManager.getTeamProviderInfo(); const providerInfo = teamProviderInfo?.[envName]?.[providerName]; const stackName = providerInfo?.StackName; @@ -649,48 +658,44 @@ class CloudFormation { const cfnModel = this.cfn; const log = logger('deleteResourceStack.cfn.describeStacks', [cfnStackParams]); - return new Promise((resolve, reject) => { + try { log(); - cfnModel.describeStacks(cfnStackParams, (err, data) => { - const cfnDeleteStatus = 'stackDeleteComplete'; - if ( - (err && err.statusCode === 400 && err.message.includes(`${stackName} does not exist`)) || - data.StackStatus === 'DELETE_COMPLETE' - ) { + try { + const data = await cfnModel.send(new DescribeStacksCommand(cfnStackParams)); + if (data.Stacks[0].StackStatus === 'DELETE_COMPLETE') { this.context.print.warning('Stack has already been deleted or does not exist'); - resolve(); + return; } - if (err === null) { - cfnModel.deleteStack(cfnStackParams, (deleteErr) => { - if (deleteErr) { - console.log(`Error deleting stack ${stackName}`); - return reject(deleteErr); - } - cfnModel.waitFor(cfnDeleteStatus, cfnStackParams, async (completeErr) => { - if (err) { - console.log(`Error deleting stack ${stackName}`); - await this.collectStackErrors(stackName).then((errorDetails) => { - completeErr.details = errorDetails; - reject(completeErr); - }); - } else { - resolve(); - } - }); - }); - } else { - log(err); - reject(err); + } catch (err) { + if (err.statusCode === 400 && err.message.includes(`${stackName} does not exist`)) { + this.context.print.warning('Stack has already been deleted or does not exist'); + return; } - }); - }); + throw err; + } + + try { + await cfnModel.send(new DeleteStackCommand(cfnStackParams)); + await waitUntilStackDeleteComplete({ client: cfnModel }, cfnStackParams); + } catch (completeErr) { + console.log(`Error deleting stack ${stackName}`); + const errorDetails = await this.collectStackErrors(stackName); + completeErr.details = errorDetails; + throw completeErr; + } + } catch (err) { + log(err); + throw err; + } } } function formatOutputs(outputs) { const formattedOutputs = {}; - for (let i = 0; i < outputs.length; i += 1) { - formattedOutputs[outputs[i].OutputKey] = outputs[i].OutputValue; + if (outputs) { + for (let i = 0; i < outputs.length; i += 1) { + formattedOutputs[outputs[i].OutputKey] = outputs[i].OutputValue; + } } return formattedOutputs; diff --git a/packages/amplify-provider-awscloudformation/src/aws-utils/aws-dynamodb.js b/packages/amplify-provider-awscloudformation/src/aws-utils/aws-dynamodb.js index f6c990129f7..3a77bf595a7 100644 --- a/packages/amplify-provider-awscloudformation/src/aws-utils/aws-dynamodb.js +++ b/packages/amplify-provider-awscloudformation/src/aws-utils/aws-dynamodb.js @@ -1,4 +1,5 @@ -const aws = require('./aws.js'); +const { DynamoDBClient } = require('@aws-sdk/client-dynamodb'); +const { NodeHttpHandler } = require('@smithy/node-http-handler'); const configurationManager = require('../configuration-manager'); const { proxyAgent } = require('./aws-globals'); @@ -13,12 +14,13 @@ class DynamoDB { } this.context = context; - this.dynamodb = new aws.DynamoDB({ + this.dynamodb = new DynamoDBClient({ ...cred, ...options, - httpOptions: { - agent: proxyAgent(), - }, + requestHandler: new NodeHttpHandler({ + httpAgent: proxyAgent(), + httpsAgent: proxyAgent(), + }), }); return this; })(); diff --git a/packages/amplify-provider-awscloudformation/src/aws-utils/aws-ecr.ts b/packages/amplify-provider-awscloudformation/src/aws-utils/aws-ecr.ts index 4486e1672da..3073505f16f 100644 --- a/packages/amplify-provider-awscloudformation/src/aws-utils/aws-ecr.ts +++ b/packages/amplify-provider-awscloudformation/src/aws-utils/aws-ecr.ts @@ -1,10 +1,11 @@ -import AWS from 'aws-sdk'; -import aws from './aws'; +import { ECRClient } from '@aws-sdk/client-ecr'; +import { NodeHttpHandler } from '@smithy/node-http-handler'; import { loadConfiguration } from '../configuration-manager'; import { $TSContext } from '@aws-amplify/amplify-cli-core'; import { proxyAgent } from './aws-globals'; + class ECR { - public ecr: AWS.ECR; + public ecr: ECRClient; constructor(private readonly context: $TSContext, options = {}) { const instancePromise = (async () => { @@ -15,12 +16,13 @@ class ECR { // ignore missing config } - this.ecr = new (aws as typeof AWS).ECR({ + this.ecr = new ECRClient({ ...cred, ...options, - httpOptions: { - agent: proxyAgent(), - }, + requestHandler: new NodeHttpHandler({ + httpAgent: proxyAgent(), + httpsAgent: proxyAgent(), + }), }); return this; diff --git a/packages/amplify-provider-awscloudformation/src/aws-utils/aws-iam.ts b/packages/amplify-provider-awscloudformation/src/aws-utils/aws-iam.ts index 1088d916fc9..e76ce3b09c2 100644 --- a/packages/amplify-provider-awscloudformation/src/aws-utils/aws-iam.ts +++ b/packages/amplify-provider-awscloudformation/src/aws-utils/aws-iam.ts @@ -1,6 +1,5 @@ -import aws from './aws.js'; -import awstype from 'aws-sdk'; -import { IAM } from 'aws-sdk'; +import { IAMClient as IAM, IAMClientConfig } from '@aws-sdk/client-iam'; +import { NodeHttpHandler } from '@smithy/node-http-handler'; import { AwsSdkConfig } from '../utils/auth-types.js'; import { getAwsConfig } from '../configuration-manager'; import { $TSContext } from '@aws-amplify/amplify-cli-core'; @@ -10,7 +9,7 @@ export class IAMClient { private static instance: IAMClient; public readonly client: IAM; - static async getInstance(context: $TSContext, options: IAM.ClientConfiguration = {}): Promise { + static async getInstance(context: $TSContext, options: IAMClientConfig = {}): Promise { if (!IAMClient.instance) { let cred: AwsSdkConfig; try { @@ -24,13 +23,14 @@ export class IAMClient { return IAMClient.instance; } - private constructor(creds: AwsSdkConfig, options: IAM.ClientConfiguration = {}) { - this.client = new (aws as typeof awstype).IAM({ + private constructor(creds: AwsSdkConfig, options: IAMClientConfig = {}) { + this.client = new IAM({ ...creds, ...options, - httpOptions: { - agent: proxyAgent(), - }, + requestHandler: new NodeHttpHandler({ + httpAgent: proxyAgent(), + httpsAgent: proxyAgent(), + }), }); } } diff --git a/packages/amplify-provider-awscloudformation/src/aws-utils/aws-lambda.ts b/packages/amplify-provider-awscloudformation/src/aws-utils/aws-lambda.ts index 0a54d8b9b24..2a560db5329 100644 --- a/packages/amplify-provider-awscloudformation/src/aws-utils/aws-lambda.ts +++ b/packages/amplify-provider-awscloudformation/src/aws-utils/aws-lambda.ts @@ -1,17 +1,22 @@ import { $TSAny, $TSContext, AmplifyError } from '@aws-amplify/amplify-cli-core'; -import { Lambda as AwsSdkLambda } from 'aws-sdk'; -import { LayerVersionsListItem, ListLayerVersionsRequest, ListLayerVersionsResponse } from 'aws-sdk/clients/lambda'; +import { + LambdaClient, + ListLayerVersionsCommand, + ListLayerVersionsCommandInput, + ListLayerVersionsCommandOutput, + DeleteLayerVersionCommand, + LayerVersionsListItem, +} from '@aws-sdk/client-lambda'; +import { NodeHttpHandler } from '@smithy/node-http-handler'; import { AwsSecrets, loadConfiguration } from '../configuration-manager'; import { fileLogger } from '../utils/aws-logger'; import { pagedAWSCall } from './paged-call'; import { proxyAgent } from './aws-globals'; -const aws = require('./aws'); - const logger = fileLogger('aws-lambda'); export class Lambda { - private lambda: AwsSdkLambda; + private lambda: LambdaClient; constructor(private readonly context: $TSContext, options = {}) { return (async () => { @@ -21,24 +26,25 @@ export class Lambda { } catch (e) { // ignore missing config } - this.lambda = new aws.Lambda({ + this.lambda = new LambdaClient({ ...cred, ...options, - httpOptions: { - agent: proxyAgent(), - }, + requestHandler: new NodeHttpHandler({ + httpAgent: proxyAgent(), + httpsAgent: proxyAgent(), + }), }); return this; })() as $TSAny; } async listLayerVersions(layerNameOrArn: string) { - const startingParams: ListLayerVersionsRequest = { LayerName: layerNameOrArn, MaxItems: 20 }; - const result = await pagedAWSCall( - async (params: ListLayerVersionsRequest, nextMarker?: string) => { + const startingParams: ListLayerVersionsCommandInput = { LayerName: layerNameOrArn, MaxItems: 20 }; + const result = await pagedAWSCall( + async (params: ListLayerVersionsCommandInput, nextMarker?: string) => { params = nextMarker ? { ...params, Marker: nextMarker } : params; logger('Lambda.listLayerVersions', [params])(); - return await this.lambda.listLayerVersions(params).promise(); + return await this.lambda.send(new ListLayerVersionsCommand(params)); }, startingParams, (response) => response?.LayerVersions, @@ -48,15 +54,18 @@ export class Lambda { } async deleteLayerVersions(layerNameOrArn: string, versions: number[]) { - const params = { LayerName: layerNameOrArn, VersionNumber: undefined }; const deletionPromises = []; for (const version of versions) { - params.VersionNumber = version; + const params = { + LayerName: layerNameOrArn, + VersionNumber: version, + }; + deletionPromises.push(async () => { try { - await this.lambda.deleteLayerVersion(params).promise(); + await this.lambda.send(new DeleteLayerVersionCommand(params)); } catch (err) { - if (err.code !== 'ParameterNotFound') { + if (err.name !== 'ParameterNotFound') { throw new AmplifyError( 'LambdaLayerDeleteError', { diff --git a/packages/amplify-provider-awscloudformation/src/aws-utils/aws-lex.js b/packages/amplify-provider-awscloudformation/src/aws-utils/aws-lex.js index e8bca279458..cbda3b1b82c 100644 --- a/packages/amplify-provider-awscloudformation/src/aws-utils/aws-lex.js +++ b/packages/amplify-provider-awscloudformation/src/aws-utils/aws-lex.js @@ -1,4 +1,5 @@ -const aws = require('./aws.js'); +const { LexModelBuildingServiceClient } = require('@aws-sdk/client-lex-model-building-service'); +const { NodeHttpHandler } = require('@smithy/node-http-handler'); const configurationManager = require('../configuration-manager'); const { proxyAgent } = require('./aws-globals'); @@ -36,13 +37,13 @@ class Lex { // ignore missing config } this.context = context; - this.lex = new aws.LexModelBuildingService({ + this.lex = new LexModelBuildingServiceClient({ ...cred, ...options, - apiVersion: '2017-04-19', - httpOptions: { - agent: proxyAgent(), - }, + requestHandler: new NodeHttpHandler({ + httpAgent: proxyAgent(), + httpsAgent: proxyAgent(), + }), }); return this; })(); diff --git a/packages/amplify-provider-awscloudformation/src/aws-utils/aws-polly.js b/packages/amplify-provider-awscloudformation/src/aws-utils/aws-polly.js index c45cc5b6d3c..a9c50cf6e11 100644 --- a/packages/amplify-provider-awscloudformation/src/aws-utils/aws-polly.js +++ b/packages/amplify-provider-awscloudformation/src/aws-utils/aws-polly.js @@ -1,4 +1,5 @@ -const aws = require('./aws.js'); +const { PollyClient } = require('@aws-sdk/client-polly'); +const { NodeHttpHandler } = require('@smithy/node-http-handler'); const configurationManager = require('../configuration-manager'); const { proxyAgent } = require('./aws-globals'); @@ -12,13 +13,14 @@ class Polly { // ignore missing config } this.context = context; - this.polly = new aws.Polly({ + this.polly = new PollyClient({ ...cred, ...options, apiVersion: '2016-06-10', - httpOptions: { - agent: proxyAgent(), - }, + requestHandler: new NodeHttpHandler({ + httpAgent: proxyAgent(), + httpsAgent: proxyAgent(), + }), }); return this; })(); diff --git a/packages/amplify-provider-awscloudformation/src/aws-utils/aws-route53.js b/packages/amplify-provider-awscloudformation/src/aws-utils/aws-route53.js index 5154344cbc5..c94eefa605c 100644 --- a/packages/amplify-provider-awscloudformation/src/aws-utils/aws-route53.js +++ b/packages/amplify-provider-awscloudformation/src/aws-utils/aws-route53.js @@ -1,5 +1,6 @@ // @ts-check -const aws = require('./aws.js'); +const { Route53Client } = require('@aws-sdk/client-route-53'); +const { NodeHttpHandler } = require('@smithy/node-http-handler'); const configurationManager = require('../configuration-manager'); const { proxyAgent } = require('./aws-globals'); @@ -15,13 +16,14 @@ class Route53 { } this.context = context; - /** @type {AWS.Route53} */ - this.route53 = new aws.Route53({ + /** @type {Route53Client} */ + this.route53 = new Route53Client({ ...cred, ...options, - httpOptions: { - agent: proxyAgent(), - }, + requestHandler: new NodeHttpHandler({ + httpAgent: proxyAgent(), + httpsAgent: proxyAgent(), + }), }); return this; diff --git a/packages/amplify-provider-awscloudformation/src/aws-utils/aws-s3.ts b/packages/amplify-provider-awscloudformation/src/aws-utils/aws-s3.ts index 690184a661f..2388039951f 100644 --- a/packages/amplify-provider-awscloudformation/src/aws-utils/aws-s3.ts +++ b/packages/amplify-provider-awscloudformation/src/aws-utils/aws-s3.ts @@ -12,10 +12,25 @@ import _ from 'lodash'; import fs from 'fs-extra'; import ora from 'ora'; -import { ListObjectVersionsOutput, ListObjectVersionsRequest, ObjectIdentifier } from 'aws-sdk/clients/s3'; +import { + S3Client, + GetObjectCommand, + CreateBucketCommand, + HeadBucketCommand, + ListObjectVersionsCommand, + DeleteObjectsCommand, + HeadObjectCommand, + DeleteObjectCommand, + DeleteBucketCommand, + waitUntilBucketExists, + ListObjectVersionsCommandOutput, + ObjectIdentifier, + ListObjectVersionsCommandInput, +} from '@aws-sdk/client-s3'; +import { Upload } from '@aws-sdk/lib-storage'; +import { NodeHttpHandler } from '@smithy/node-http-handler'; import { pagedAWSCall } from './paged-call'; import { loadConfiguration } from '../configuration-manager'; -import aws from './aws'; import { proxyAgent } from './aws-globals'; const providerName = require('../constants').ProviderName; @@ -34,7 +49,7 @@ type OptionalExceptFor = Partial & Pick { - spinner.text = `Uploading files...${Math.round((max.loaded / max.total) * 100)}%`; + uploadTask.on('httpUploadProgress', (progress) => { + spinner.text = `Uploading files...${Math.round((progress.loaded / progress.total) * 100)}%`; }); } - await uploadTask.promise(); + await uploadTask.done(); return this.uploadState.s3Params.Bucket; } finally { if (showSpinner) { @@ -170,7 +189,7 @@ export class S3 { s3Params = this.attachBucketToParams(s3Params, envName); logger('s3.getFile', [s3Params])(); - const result = await this.s3.getObject(s3Params).promise(); + const result = await this.s3.send(new GetObjectCommand(s3Params)); return result.Body; } @@ -192,9 +211,9 @@ export class S3 { ); this.context.print.warning(`Bucket name: ${bucketName}`); logger('createBucket.s3.createBucket', [params])(); - await this.s3.createBucket(params).promise(); + await this.s3.send(new CreateBucketCommand(params)); logger('createBucket.s3.waitFor', ['bucketExists', params])(); - await this.s3.waitFor('bucketExists', params).promise(); + await waitUntilBucketExists({ client: this.s3, maxWaitTime: 60 }, params); this.context.print.success('S3 bucket successfully created'); } else if (throwIfExists) { throw new AmplifyError('BucketAlreadyExistsError', { @@ -212,13 +231,13 @@ export class S3 { */ async getAllObjectVersions( bucketName: string, - options: OptionalExceptFor = null, + options: OptionalExceptFor = null, ) { - const result = await pagedAWSCall, typeof options, ListObjectVersionsRequest>( + const result = await pagedAWSCall( async (param, nextToken?) => { const parmaWithNextToken = nextToken ? { ...param, ...nextToken } : param; logger('getAllObjectKey.s3.listObjectVersions', [parmaWithNextToken])(); - const objVersionList = await this.s3.listObjectVersions(parmaWithNextToken).promise(); + const objVersionList = await this.s3.send(new ListObjectVersionsCommand(parmaWithNextToken)); return objVersionList; }, { @@ -246,14 +265,14 @@ export class S3 { const chunkedResultLength = chunkedResult.length; for (let idx = 0; idx < chunkedResultLength; idx += 1) { logger(`deleteAllObjects.s3.deleteObjects (${idx} of ${chunkedResultLength})`, [{ Bucket: bucketName }])(); - await this.s3 - .deleteObjects({ + await this.s3.send( + new DeleteObjectsCommand({ Bucket: bucketName, Delete: { Objects: chunkedResult[idx], }, - }) - .promise(); + }), + ); } } @@ -266,12 +285,12 @@ export class S3 { public async checkExistObject(bucketName: string, filePath: string): Promise { logger('checkExistObject.s3', [{ BucketName: bucketName, FilePath: filePath }])(); try { - await this.s3 - .headObject({ + await this.s3.send( + new HeadObjectCommand({ Bucket: bucketName, Key: filePath, - }) - .promise(); + }), + ); return true; } catch (error) { logger('checkExistObject.s3', [{ BucketName: bucketName, FilePath: filePath, Error: error.name }])(); @@ -288,12 +307,12 @@ export class S3 { logger('deleteObject.s3', [{ BucketName: bucketName, FilePath: filePath }])(); const objExists = await this.checkExistObject(bucketName, filePath); if (objExists) { - await this.s3 - .deleteObject({ + await this.s3.send( + new DeleteObjectCommand({ Bucket: bucketName, Key: filePath, - }) - .promise(); + }), + ); } } @@ -308,14 +327,14 @@ export class S3 { const chunkedResultLength = chunkedResult.length; for (let idx = 0; idx < chunkedResultLength; idx += 1) { logger(`deleteAllObjects.s3.deleteObjects (${idx} of ${chunkedResultLength})`, [{ Bucket: bucketName }])(); - await this.s3 - .deleteObjects({ + await this.s3.send( + new DeleteObjectsCommand({ Bucket: bucketName, Delete: { Objects: chunkedResult[idx], }, - }) - .promise(); + }), + ); } } @@ -329,7 +348,7 @@ export class S3 { logger('deleteS3Bucket.s3.deleteAllObjects', [{ BucketName: bucketName }])(); await this.deleteAllObjects(bucketName); logger('deleteS3Bucket.s3.deleteBucket', [{ BucketName: bucketName }])(); - await this.s3.deleteBucket({ Bucket: bucketName }).promise(); + await this.s3.send(new DeleteBucketCommand({ Bucket: bucketName })); } } @@ -351,16 +370,16 @@ export class S3 { public async ifBucketExists(bucketName: string): Promise { try { logger('ifBucketExists.s3.headBucket', [{ BucketName: bucketName }])(); - await this.s3 - .headBucket({ + await this.s3.send( + new HeadBucketCommand({ Bucket: bucketName, - }) - .promise(); + }), + ); return true; } catch (e) { logger('ifBucketExists.s3.headBucket', [{ BucketName: bucketName }])(e); - if (e.code === 'NotFound') { + if (e.name === 'NotFound') { throw new AmplifyError( 'BucketNotFoundError', { @@ -389,16 +408,21 @@ export class S3 { */ public getStringObjectFromBucket = async (bucketName: string, objectKey: string): Promise => { try { - const result = await this.s3 - .getObject({ + const result = await this.s3.send( + new GetObjectCommand({ Bucket: bucketName, Key: objectKey, - }) - .promise(); + }), + ); - return result.Body.toString(); + // Convert the stream to string + if (result.Body) { + const bodyContents = await result.Body.transformToString(); + return bodyContents; + } + return undefined; } catch (e) { - if (e.statusCode === 404) { + if (e.$metadata?.httpStatusCode === 404) { return undefined; } diff --git a/packages/amplify-provider-awscloudformation/src/aws-utils/aws-sagemaker.js b/packages/amplify-provider-awscloudformation/src/aws-utils/aws-sagemaker.js index 9e867e7d838..ca9555b8728 100644 --- a/packages/amplify-provider-awscloudformation/src/aws-utils/aws-sagemaker.js +++ b/packages/amplify-provider-awscloudformation/src/aws-utils/aws-sagemaker.js @@ -1,4 +1,5 @@ -const aws = require('./aws.js'); +const { SageMakerClient } = require('@aws-sdk/client-sagemaker'); +const { NodeHttpHandler } = require('@smithy/node-http-handler'); const configurationManager = require('../configuration-manager'); const { proxyAgent } = require('./aws-globals'); @@ -13,13 +14,13 @@ class SageMaker { } this.context = context; - this.sageMaker = new aws.SageMaker({ + this.sageMaker = new SageMakerClient({ ...cred, ...options, - apiVersion: '2017-07-24', - httpOptions: { - agent: proxyAgent(), - }, + requestHandler: new NodeHttpHandler({ + httpAgent: proxyAgent(), + httpsAgent: proxyAgent(), + }), }); return this; })(); diff --git a/packages/amplify-provider-awscloudformation/src/aws-utils/aws-secretsmanager.js b/packages/amplify-provider-awscloudformation/src/aws-utils/aws-secretsmanager.js index 022964ea4e1..af5620de31f 100644 --- a/packages/amplify-provider-awscloudformation/src/aws-utils/aws-secretsmanager.js +++ b/packages/amplify-provider-awscloudformation/src/aws-utils/aws-secretsmanager.js @@ -1,5 +1,6 @@ // @ts-check -const aws = require('./aws.js'); +const { SecretsManagerClient } = require('@aws-sdk/client-secrets-manager'); +const { NodeHttpHandler } = require('@smithy/node-http-handler'); const configurationManager = require('../configuration-manager'); const { proxyAgent } = require('./aws-globals'); @@ -15,13 +16,14 @@ class SecretsManager { } this.context = context; - /** @type {AWS.SecretsManager} */ - this.secretsManager = new aws.SecretsManager({ + /** @type {SecretsManagerClient} */ + this.secretsManager = new SecretsManagerClient({ ...cred, ...options, - httpOptions: { - agent: proxyAgent(), - }, + requestHandler: new NodeHttpHandler({ + httpAgent: proxyAgent(), + httpsAgent: proxyAgent(), + }), }); return this; diff --git a/packages/amplify-provider-awscloudformation/src/aws-utils/aws-sns.ts b/packages/amplify-provider-awscloudformation/src/aws-utils/aws-sns.ts index 7c82faf9906..2ef7faea18d 100644 --- a/packages/amplify-provider-awscloudformation/src/aws-utils/aws-sns.ts +++ b/packages/amplify-provider-awscloudformation/src/aws-utils/aws-sns.ts @@ -1,6 +1,7 @@ import { $TSAny, $TSContext } from '@aws-amplify/amplify-cli-core'; import { AwsSecrets, loadConfiguration } from '../configuration-manager'; -import aws from './aws.js'; +import { SNSClient, GetSMSSandboxAccountStatusCommand } from '@aws-sdk/client-sns'; +import { NodeHttpHandler } from '@smithy/node-http-handler'; import { proxyAgent } from './aws-globals'; // Currently SNS is used only by Cognito for sending SMS and has the following SNS mapping @@ -15,7 +16,7 @@ const COGNITO_SMS_REGION_MAPPING = { }; export class SNS { private static instance: SNS; - private readonly sns: AWS.SNS; + private readonly sns: SNSClient; static async getInstance(context: $TSContext, options = {}): Promise { if (!SNS.instance) { @@ -37,17 +38,18 @@ export class SNS { } private constructor(context: $TSContext, cred: $TSAny, options = {}) { - this.sns = new aws.SNS({ + this.sns = new SNSClient({ ...cred, ...options, - httpOptions: { - agent: proxyAgent(), - }, + requestHandler: new NodeHttpHandler({ + httpAgent: proxyAgent(), + httpsAgent: proxyAgent(), + }), }); } public async isInSandboxMode(): Promise { - const result = await this.sns.getSMSSandboxAccountStatus().promise(); + const result = await this.sns.send(new GetSMSSandboxAccountStatusCommand({})); return result.IsInSandbox; } } diff --git a/packages/amplify-provider-awscloudformation/src/aws-utils/aws-ssm.ts b/packages/amplify-provider-awscloudformation/src/aws-utils/aws-ssm.ts index 1bf6772319e..529ca0314b1 100644 --- a/packages/amplify-provider-awscloudformation/src/aws-utils/aws-ssm.ts +++ b/packages/amplify-provider-awscloudformation/src/aws-utils/aws-ssm.ts @@ -1,12 +1,12 @@ import { $TSContext, $TSObject } from '@aws-amplify/amplify-cli-core'; import { AwsSecrets, loadConfiguration } from '../configuration-manager'; -import aws from './aws.js'; -import * as AWS from 'aws-sdk'; +import { SSMClient } from '@aws-sdk/client-ssm'; +import { NodeHttpHandler } from '@smithy/node-http-handler'; import { proxyAgent } from './aws-globals'; export class SSM { private static instance: SSM; - readonly client: AWS.SSM; + readonly client: SSMClient; static async getInstance(context: $TSContext, options: $TSObject = {}): Promise { if (!SSM.instance) { @@ -23,12 +23,13 @@ export class SSM { } private constructor(cred: AwsSecrets, options = {}) { - this.client = new aws.SSM({ + this.client = new SSMClient({ ...cred, ...options, - httpOptions: { - agent: proxyAgent(), - }, + requestHandler: new NodeHttpHandler({ + httpAgent: proxyAgent(), + httpsAgent: proxyAgent(), + }), }); } } diff --git a/packages/amplify-provider-awscloudformation/src/aws-utils/aws-sts.ts b/packages/amplify-provider-awscloudformation/src/aws-utils/aws-sts.ts index 69fa9295ec1..c10ed29fead 100644 --- a/packages/amplify-provider-awscloudformation/src/aws-utils/aws-sts.ts +++ b/packages/amplify-provider-awscloudformation/src/aws-utils/aws-sts.ts @@ -1,4 +1,5 @@ -import aws from './aws.js'; +import { STSClient, GetCallerIdentityCommand, GetCallerIdentityCommandOutput } from '@aws-sdk/client-sts'; +import { NodeHttpHandler } from '@smithy/node-http-handler'; import { loadConfiguration } from '../configuration-manager'; import { $TSAny, $TSContext } from '@aws-amplify/amplify-cli-core'; import { proxyAgent } from './aws-globals'; @@ -6,7 +7,7 @@ import { proxyAgent } from './aws-globals'; export class STS { private static instance: STS; private readonly context: $TSContext; - private readonly sts: AWS.STS; + private readonly sts: STSClient; static async getInstance(context: $TSContext, options = {}): Promise { if (!STS.instance) { @@ -24,16 +25,17 @@ export class STS { private constructor(context: $TSContext, cred: $TSAny, options = {}) { this.context = context; - this.sts = new aws.STS({ + this.sts = new STSClient({ ...cred, - options, - httpOptions: { - agent: proxyAgent(), - }, + ...options, + requestHandler: new NodeHttpHandler({ + httpAgent: proxyAgent(), + httpsAgent: proxyAgent(), + }), }); } - async getCallerIdentity(): Promise { - return await this.sts.getCallerIdentity().promise(); + async getCallerIdentity(): Promise { + return await this.sts.send(new GetCallerIdentityCommand({})); } } diff --git a/packages/amplify-provider-awscloudformation/src/aws-utils/cloudformation-error-serializer.ts b/packages/amplify-provider-awscloudformation/src/aws-utils/cloudformation-error-serializer.ts index 04ca0e3371f..a67ed3552a4 100644 --- a/packages/amplify-provider-awscloudformation/src/aws-utils/cloudformation-error-serializer.ts +++ b/packages/amplify-provider-awscloudformation/src/aws-utils/cloudformation-error-serializer.ts @@ -1,4 +1,4 @@ -import { StackEvent } from 'aws-sdk/clients/cloudformation'; +import { StackEvent } from '@aws-sdk/client-cloudformation'; export const getStatusToErrorMsg = (status) => { const MAP = { CREATE_FAILED: 'create', diff --git a/packages/amplify-provider-awscloudformation/src/cloud-formation-error-handler.ts b/packages/amplify-provider-awscloudformation/src/cloud-formation-error-handler.ts index a49459b57ab..d9a814576ba 100644 --- a/packages/amplify-provider-awscloudformation/src/cloud-formation-error-handler.ts +++ b/packages/amplify-provider-awscloudformation/src/cloud-formation-error-handler.ts @@ -33,7 +33,7 @@ export const handleCloudFormationError = (err: Error & { details?: string }): vo { message: 'Deployment is already in progress.', resolution: 'Wait for the other deployment to finish and try again.', - code: (err as $TSAny).code, + code: (err as $TSAny).name, }, err, ); diff --git a/packages/amplify-provider-awscloudformation/src/configuration-manager.ts b/packages/amplify-provider-awscloudformation/src/configuration-manager.ts index c67391c769c..09f8f853873 100644 --- a/packages/amplify-provider-awscloudformation/src/configuration-manager.ts +++ b/packages/amplify-provider-awscloudformation/src/configuration-manager.ts @@ -13,7 +13,6 @@ import chalk from 'chalk'; import { prompt } from 'inquirer'; import _ from 'lodash'; import path from 'path'; -import { STS } from 'aws-sdk'; import awsRegions from './aws-regions'; import constants from './constants'; import * as setupNewUser from './setup-new-user'; @@ -21,7 +20,7 @@ import obfuscateUtil from './utility-obfuscate'; import * as systemConfigManager from './system-config-manager'; import { doAdminTokensExist, getTempCredsWithAdminTokens, isAmplifyAdminApp } from './utils/admin-helpers'; import { resolveAppId } from './utils/resolve-appId'; -import { AuthFlow, AuthFlowConfig, AwsSdkConfig } from './utils/auth-types'; +import { AuthFlow, AuthFlowConfig, AwsSdkConfig, legacyAwsSdkConfig } from './utils/auth-types'; import { accessKeysQuestion, authTypeQuestion, @@ -32,6 +31,7 @@ import { retryAuthConfig, } from './question-flows/configuration-questions'; import { proxyAgent } from './aws-utils/aws-globals'; +import { GetCallerIdentityCommand, STSClient } from '@aws-sdk/client-sts'; interface AwsConfig extends AwsSecrets { useProfile?: boolean; @@ -225,11 +225,11 @@ async function initialize(context: $TSContext, authConfig?: AuthFlowConfig) { if (authConfig?.type === 'accessKeys') { if ( (awsConfigInfo.config?.accessKeyId && awsConfigInfo.config?.secretAccessKey) || - (authConfig?.accessKeyId && authConfig?.secretAccessKey) + (authConfig?.credentials && authConfig?.credentials.accessKeyId && authConfig?.credentials.secretAccessKey) ) { - awsConfigInfo.config.accessKeyId = awsConfigInfo.config.accessKeyId || authConfig.accessKeyId; - awsConfigInfo.config.secretAccessKey = awsConfigInfo.config.secretAccessKey || authConfig.secretAccessKey; - awsConfigInfo.config.sessionToken = awsConfigInfo.config.sessionToken || authConfig.sessionToken; + awsConfigInfo.config.accessKeyId = awsConfigInfo.config.accessKeyId || authConfig.credentials.accessKeyId; + awsConfigInfo.config.secretAccessKey = awsConfigInfo.config.secretAccessKey || authConfig.credentials.secretAccessKey; + awsConfigInfo.config.sessionToken = awsConfigInfo.config.sessionToken || authConfig.credentials.sessionToken; awsConfigInfo.config.region = awsConfigInfo.config.region || authConfig.region; } else { await promptForAuthConfig(context, authConfig); @@ -469,15 +469,16 @@ async function validateConfig(context: $TSContext) { awsConfigInfo.config.secretAccessKey !== constants.DefaultAWSSecretAccessKey && awsConfigInfo.config.region && awsRegions.regions.includes(awsConfigInfo.config.region); - const sts = new STS({ + const sts = new STSClient({ credentials: { accessKeyId: awsConfigInfo.config.accessKeyId, secretAccessKey: awsConfigInfo.config.secretAccessKey, sessionToken: awsConfigInfo.config.sessionToken, }, + region: awsConfigInfo.config.region, }); try { - await sts.getCallerIdentity({}).promise(); + await sts.send(new GetCallerIdentityCommand({})); } catch (err) { awsConfigInfo.configValidated = false; } @@ -512,9 +513,11 @@ function persistLocalEnvConfig(context: $TSContext) { } else { awsInfo.useProfile = false; const awsSecrets = { - accessKeyId: awsConfigInfo.config.accessKeyId, - secretAccessKey: awsConfigInfo.config.secretAccessKey, - sessionToken: awsConfigInfo.config.sessionToken, + credentials: { + accessKeyId: awsConfigInfo.config.accessKeyId, + secretAccessKey: awsConfigInfo.config.secretAccessKey, + sessionToken: awsConfigInfo.config.sessionToken, + }, region: awsConfigInfo.config.region, }; const sharedConfigDirPath = path.join(pathManager.getHomeDotAmplifyDirPath(), constants.ProviderName); @@ -609,7 +612,7 @@ function removeProjectConfig(envName: string) { } export async function loadConfiguration(context: $TSContext): Promise { - const envName = stateManager.getCurrentEnvName() || context?.exeInfo?.inputParams?.amplify?.envName; + const envName = context?.exeInfo?.inputParams?.amplify?.envName || stateManager.getCurrentEnvName(); const config = await loadConfigurationForEnv(context, envName); return config; } @@ -617,8 +620,24 @@ export async function loadConfiguration(context: $TSContext): Promise(profilePath); - if (config.accessKeyId && config.secretAccessKey && config.region) { + if (config.credentials && config.credentials.accessKeyId && config.credentials.secretAccessKey && config.region) { return config; + } else if ( + !config.credentials && + (config as unknown as legacyAwsSdkConfig).accessKeyId && + (config as unknown as legacyAwsSdkConfig).secretAccessKey && + (config as unknown as legacyAwsSdkConfig).region + ) { + // in cases where legacy credentials come in, they are not properly converted to AwsSdkConfig type + const legacy = config as unknown as legacyAwsSdkConfig; + return { + credentials: { + accessKeyId: legacy.accessKeyId, + secretAccessKey: legacy.secretAccessKey, + sessionToken: legacy.sessionToken, + }, + region: legacy.region, + }; } } throw new AmplifyError('ConfigurationError', { @@ -637,8 +656,15 @@ export async function loadConfigurationForEnv(context: $TSContext, env: string, awsConfigInfo.config.region = awsConfigInfo.region; } } - - return awsConfigInfo.config; + const config = { + credentials: { + accessKeyId: awsConfigInfo.config.accessKeyId, + secretAccessKey: awsConfigInfo.config.secretAccessKey, + sessionToken: awsConfigInfo.config.sessionToken, + }, + region: awsConfigInfo.config.region, + }; + return config; } const projectConfigInfo = getConfigForEnv(context, env); @@ -816,9 +842,11 @@ export async function getAwsConfig(context: $TSContext): Promise { } } else { resultAWSConfigInfo = { - accessKeyId: awsConfigInfo.config.accessKeyId, - secretAccessKey: awsConfigInfo.config.secretAccessKey, - sessionToken: awsConfigInfo.config.sessionToken, + credentials: { + accessKeyId: awsConfigInfo.config.accessKeyId, + secretAccessKey: awsConfigInfo.config.secretAccessKey, + sessionToken: awsConfigInfo.config.sessionToken, + }, region: awsConfigInfo.config.region, }; } @@ -887,9 +915,11 @@ async function determineAuthFlow(context: $TSContext, projectConfig?: ProjectCon if (accessKeyId && secretAccessKey && region) { return { type: 'accessKeys', - accessKeyId, + credentials: { + accessKeyId, + secretAccessKey, + }, region, - secretAccessKey, }; } @@ -926,9 +956,11 @@ async function determineAuthFlow(context: $TSContext, projectConfig?: ProjectCon if (accessKeyId && secretAccessKey && region) { return { type: 'accessKeys', - accessKeyId, + credentials: { + accessKeyId, + secretAccessKey, + }, region, - secretAccessKey, }; } } diff --git a/packages/amplify-provider-awscloudformation/src/disconnect-dependent-resources/index.ts b/packages/amplify-provider-awscloudformation/src/disconnect-dependent-resources/index.ts index 5cbef21c793..848d1cd7f10 100644 --- a/packages/amplify-provider-awscloudformation/src/disconnect-dependent-resources/index.ts +++ b/packages/amplify-provider-awscloudformation/src/disconnect-dependent-resources/index.ts @@ -1,5 +1,4 @@ import { $TSAny, $TSContext, pathManager, stateManager } from '@aws-amplify/amplify-cli-core'; -import { CloudFormation } from 'aws-sdk'; import * as fs from 'fs-extra'; import { S3 } from '../aws-utils/aws-s3'; import { loadConfiguration } from '../configuration-manager'; @@ -14,6 +13,7 @@ import { localPrefix, } from './utils'; import { handleCommonSdkError } from '../handle-common-sdk-errors'; +import { CloudFormationClient } from '@aws-sdk/client-cloudformation'; let functionsDependentOnReplacedModelTables: string[] = []; @@ -41,7 +41,7 @@ export const prependDeploymentStepsToDisconnectFunctionsFromReplacedModelTables ); // generate deployment steps that will remove references to the replaced tables in the dependent functions const { deploymentSteps: disconnectFuncsSteps, lastMetaKey } = await generateIterativeFuncDeploymentSteps( - new CloudFormation(await loadConfiguration(context)), + new CloudFormationClient(await loadConfiguration(context)), rootStackId, functionsDependentOnReplacedModelTables, ); diff --git a/packages/amplify-provider-awscloudformation/src/disconnect-dependent-resources/utils.ts b/packages/amplify-provider-awscloudformation/src/disconnect-dependent-resources/utils.ts index 35b4e8bf095..f439abec6c8 100644 --- a/packages/amplify-provider-awscloudformation/src/disconnect-dependent-resources/utils.ts +++ b/packages/amplify-provider-awscloudformation/src/disconnect-dependent-resources/utils.ts @@ -9,13 +9,13 @@ import { } from '@aws-amplify/amplify-cli-core'; import * as path from 'path'; import * as fs from 'fs-extra'; -import { CloudFormation } from 'aws-sdk'; import _ from 'lodash'; import { ensureEnvParamManager } from '@aws-amplify/amplify-environment-parameters'; import { S3 } from '../aws-utils/aws-s3'; import { fileLogger } from '../utils/aws-logger'; import { getPreviousDeploymentRecord } from '../utils/amplify-resource-state-utils'; import { DeploymentOp, DeploymentStep } from '../iterative-deployment'; +import { CloudFormationClient, DescribeStackResourcesCommand } from '@aws-sdk/client-cloudformation'; const logger = fileLogger('disconnect-dependent-resources'); @@ -81,7 +81,7 @@ export const uploadTempFuncDeploymentFiles = async (s3Client: S3, funcNames: str * Generates the iterative deployment steps necessary to remove then re-add function dependency on rebuilt table */ export const generateIterativeFuncDeploymentSteps = async ( - cfnClient: CloudFormation, + cfnClient: CloudFormationClient, rootStackId: string, functionNames: string[], ): Promise<{ deploymentSteps: DeploymentStep[]; lastMetaKey: string }> => { @@ -130,13 +130,13 @@ export const prependDeploymentSteps = ( * Also writes the deployment operation to the temp meta path */ const generateIterativeFuncDeploymentOp = async ( - cfnClient: CloudFormation, + cfnClient: CloudFormationClient, rootStackId: string, functionName: string, ): Promise => { - const funcStack = await cfnClient - .describeStackResources({ StackName: rootStackId, LogicalResourceId: `function${functionName}` }) - .promise(); + const funcStack = await cfnClient.send( + new DescribeStackResourcesCommand({ StackName: rootStackId, LogicalResourceId: `function${functionName}` }), + ); if (!funcStack.StackResources || funcStack.StackResources.length === 0) { throw new AmplifyFault('ResourceNotFoundFault', { diff --git a/packages/amplify-provider-awscloudformation/src/display-helpful-urls.ts b/packages/amplify-provider-awscloudformation/src/display-helpful-urls.ts index fd27422fd7f..a7b87f141b1 100644 --- a/packages/amplify-provider-awscloudformation/src/display-helpful-urls.ts +++ b/packages/amplify-provider-awscloudformation/src/display-helpful-urls.ts @@ -245,15 +245,15 @@ export const showSMSSandboxWarning = async (context): Promise => { printer.warn(productionModeInfo); } } catch (e) { - if (e.code === 'AuthorizationError') { + if (e.name === 'AuthorizationError') { if (smsSandBoxMissingPermissionWarning) { printer.warn(smsSandBoxMissingPermissionWarning); } } else if (e instanceof TypeError) { printer.warn(cliUpdateWarning); - } else if (e.code === 'ResourceNotFound') { + } else if (e.name === 'ResourceNotFound') { // API is not public yet. Ignore it for now. This error should not occur as `COGNITO_SMS_SANDBOX_UPDATE_WARNING` will not be set - } else if (e.code === 'UnknownEndpoint') { + } else if (e.name === 'UnknownEndpoint') { // Network error. Sandbox status is for informational purpose and should not stop deployment log(e); } else { @@ -261,7 +261,7 @@ export const showSMSSandboxWarning = async (context): Promise => { 'SnsSandboxModeCheckFault', { message: e.message, - code: e.code, + code: e.name, }, e, ); diff --git a/packages/amplify-provider-awscloudformation/src/download-api-models.ts b/packages/amplify-provider-awscloudformation/src/download-api-models.ts index aed23fc633e..279ae169e8d 100644 --- a/packages/amplify-provider-awscloudformation/src/download-api-models.ts +++ b/packages/amplify-provider-awscloudformation/src/download-api-models.ts @@ -3,6 +3,7 @@ import { printer } from '@aws-amplify/amplify-prompts'; import * as fs from 'fs-extra'; import sequential from 'promise-sequential'; import { APIGateway } from './aws-utils/aws-apigw'; +import { GetSdkCommand } from '@aws-sdk/client-api-gateway'; /** * Download API models from API Gateway @@ -41,7 +42,7 @@ const extractAPIModel = async (context: $TSContext, resource: $TSObject, framewo const apiName = resource.output.ApiName; - const data = await apigw.apigw.getSdk(apigwParams).promise(); + const data = await apigw.apigw.send(new GetSdkCommand(apigwParams)); const backendDir = pathManager.getBackendDirPath(); diff --git a/packages/amplify-provider-awscloudformation/src/graphql-resource-manager/amplify-graphql-resource-manager.ts b/packages/amplify-provider-awscloudformation/src/graphql-resource-manager/amplify-graphql-resource-manager.ts index 1e11b4be5f0..0ea8eaab6ca 100644 --- a/packages/amplify-provider-awscloudformation/src/graphql-resource-manager/amplify-graphql-resource-manager.ts +++ b/packages/amplify-provider-awscloudformation/src/graphql-resource-manager/amplify-graphql-resource-manager.ts @@ -15,7 +15,7 @@ import { cantHaveMoreThan500ResourcesRule, sanityCheckDiffs, } from 'graphql-transformer-core'; -import { CloudFormation } from 'aws-sdk'; +import { CloudFormationClient, DescribeStackResourcesCommand } from '@aws-sdk/client-cloudformation'; import { Diff } from 'deep-diff'; import _ from 'lodash'; import fs from 'fs-extra'; @@ -40,7 +40,7 @@ const SEARCHABLE_STACK_NAME = 'SearchableStack'; * Type for GQLResourceManagerProps */ export type GQLResourceManagerProps = { - cfnClient: CloudFormation; + cfnClient: CloudFormationClient; resourceMeta?: ResourceMeta; backendDir: string; cloudBackendDir: string; @@ -73,7 +73,7 @@ export type ResourceMeta = { export class GraphQLResourceManager { static serviceName = 'AppSync'; static categoryName = 'api'; - private cfnClient: CloudFormation; + private cfnClient: CloudFormationClient; private resourceMeta: ResourceMeta; private cloudBackendApiProjectRoot: string; private backendApiProjectRoot: string; @@ -89,10 +89,12 @@ export class GraphQLResourceManager { rebuildAllTables = false, ): Promise => { const cred = await loadConfiguration(context); - const cfn = new CloudFormation(cred); - const apiStack = await cfn - .describeStackResources({ StackName: StackId, LogicalResourceId: gqlResource.providerMetadata.logicalId }) - .promise(); + const cfn = new CloudFormationClient(cred); + const describeStackResourcesCommand = new DescribeStackResourcesCommand({ + StackName: StackId, + LogicalResourceId: gqlResource.providerMetadata.logicalId, + }); + const apiStack = await cfn.send(describeStackResourcesCommand); return new GraphQLResourceManager({ cfnClient: cfn, diff --git a/packages/amplify-provider-awscloudformation/src/graphql-resource-manager/dynamodb-gsi-helpers.ts b/packages/amplify-provider-awscloudformation/src/graphql-resource-manager/dynamodb-gsi-helpers.ts index 5a2575503ae..6a1037050d5 100644 --- a/packages/amplify-provider-awscloudformation/src/graphql-resource-manager/dynamodb-gsi-helpers.ts +++ b/packages/amplify-provider-awscloudformation/src/graphql-resource-manager/dynamodb-gsi-helpers.ts @@ -113,6 +113,7 @@ export const removeGSI = (indexName: string, table: DynamoDB.Table): DynamoDB.Ta const removedIndices = _.remove(gsis, { IndexName: indexName }); assertNotIntrinsicFunction(removedIndices); + console.log(gsis); const gsiKeySchemas: Array = gsis.reduce((acc, gsi) => { acc.push(...(gsi.KeySchema as Array)); return acc; diff --git a/packages/amplify-provider-awscloudformation/src/index.ts b/packages/amplify-provider-awscloudformation/src/index.ts index 34e06c8910d..9ca7fd7fe27 100644 --- a/packages/amplify-provider-awscloudformation/src/index.ts +++ b/packages/amplify-provider-awscloudformation/src/index.ts @@ -109,7 +109,7 @@ async function getConfiguredAWSClientConfig(context, category, action) { action = action || ['missing']; const userAgentAction = `${category}:${action[0]}`; const config = { - credentials: credsConfig, + credentials: credsConfig.credentials || credsConfig, customUserAgent: formUserAgentParam(context, userAgentAction), httpOptions: { agent: proxyAgent(), diff --git a/packages/amplify-provider-awscloudformation/src/initialize-env.ts b/packages/amplify-provider-awscloudformation/src/initialize-env.ts index 538f16c4d5f..849150e8c54 100644 --- a/packages/amplify-provider-awscloudformation/src/initialize-env.ts +++ b/packages/amplify-provider-awscloudformation/src/initialize-env.ts @@ -3,6 +3,7 @@ import fs from 'fs-extra'; import { globSync } from 'glob'; import _ from 'lodash'; import path from 'path'; +import { Readable } from 'stream'; import Cloudformation from './aws-utils/aws-cfn'; import { S3 } from './aws-utils/aws-s3'; import { buildOverridesEnabledResources } from './build-override-enabled-resources'; @@ -10,6 +11,7 @@ import { S3BackendZipFileName } from './constants'; import { fileLogger } from './utils/aws-logger'; import { downloadZip, extractZip } from './zip-util'; import { generateDependentResourcesType } from '@aws-amplify/amplify-category-custom'; +import { text } from 'node:stream/consumers'; const logger = fileLogger('initialize-env'); @@ -28,7 +30,7 @@ export async function run(context: $TSContext, providerMetadata: $TSMeta) { try { currentCloudBackendZip = await downloadZip(s3, tempDir, S3BackendZipFileName, undefined); } catch (err) { - if (err?.code === 'NoSuchBucket') { + if (err?.name === 'NoSuchBucket') { throw new AmplifyError('EnvironmentNotInitializedError', { message: `Could not find a deployment bucket for the specified backend environment. This environment may have been deleted.`, resolution: 'Make sure the environment has been initialized with "amplify init" or "amplify env add".', @@ -95,13 +97,12 @@ export async function run(context: $TSContext, providerMetadata: $TSMeta) { // let hasMigratedResources = false; - const s3AmplifyMeta = JSONUtilities.parse( - ( - await s3.getFile({ - Key: PathConstants.AmplifyMetaFileName, - }) - ).toString(), - ); + // convert output of getFile from stream to string for parsing + const s3FileStream = await s3.getFile({ + Key: PathConstants.AmplifyMetaFileName, + }); + const s3FileString = await text(s3FileStream as Readable); + const s3AmplifyMeta = JSONUtilities.parse(s3FileString); Object.keys(s3AmplifyMeta) .filter((k) => k !== 'providers') diff --git a/packages/amplify-provider-awscloudformation/src/iterative-deployment/deployment-manager.ts b/packages/amplify-provider-awscloudformation/src/iterative-deployment/deployment-manager.ts index ad2ac4fdefa..106bb891ddb 100644 --- a/packages/amplify-provider-awscloudformation/src/iterative-deployment/deployment-manager.ts +++ b/packages/amplify-provider-awscloudformation/src/iterative-deployment/deployment-manager.ts @@ -1,8 +1,15 @@ import { $TSContext, AmplifyError, AmplifyFault, IDeploymentStateManager } from '@aws-amplify/amplify-cli-core'; import { AmplifySpinner, printer as promptsPrinter } from '@aws-amplify/amplify-prompts'; import assert from 'assert'; -import * as aws from 'aws-sdk'; -import { ConfigurationOptions } from 'aws-sdk/lib/config-base'; +import { S3Client, HeadObjectCommand } from '@aws-sdk/client-s3'; +import { + CloudFormationClient, + DescribeStacksCommand, + UpdateStackCommand, + waitUntilStackUpdateComplete, +} from '@aws-sdk/client-cloudformation'; +import { DynamoDBClient, DescribeTableCommand } from '@aws-sdk/client-dynamodb'; +import { NodeHttpHandler } from '@smithy/node-http-handler'; import throttle from 'lodash.throttle'; import { interpret } from 'xstate'; import { getBucketKey, getHttpUrl } from './helpers'; @@ -90,7 +97,7 @@ export class DeploymentManager { try { const cred = await loadConfiguration(context); // this is the "general" config level case, aws sdk will resolve creds and region from env variables etc. - const region = cred?.region ?? new aws.S3().config.region; + const region = cred?.region ?? new S3Client().config.region.toString(); return new DeploymentManager(cred, region, deploymentBucket, eventMap, options); } catch (e) { throw new AmplifyError( @@ -105,16 +112,16 @@ export class DeploymentManager { private deployment: DeploymentMachineStep[] = []; private options: Required; - private cfnClient: aws.CloudFormation; - private s3Client: aws.S3; - private ddbClient: aws.DynamoDB; + private cfnClient: CloudFormationClient; + private s3Client: S3Client; + private ddbClient: DynamoDBClient; private deploymentStateManager?: IDeploymentStateManager; private logger: Logger; private printer: IStackProgressPrinter; private spinner: AmplifySpinner; private constructor( - creds: ConfigurationOptions, + creds: any, private region: string, private deploymentBucket: string, private eventMap: EventMap, @@ -127,14 +134,26 @@ export class DeploymentManager { ...options, }; this.eventMap = eventMap; - this.s3Client = new aws.S3(creds); - this.cfnClient = new aws.CloudFormation({ + this.s3Client = new S3Client({ ...creds, region }); + this.cfnClient = new CloudFormationClient({ ...creds, - maxRetries: 10, + region, + maxAttempts: 10, customUserAgent: this.options.userAgent, - httpOptions: { agent: proxyAgent() }, + requestHandler: new NodeHttpHandler({ + httpAgent: proxyAgent(), + httpsAgent: proxyAgent(), + }), + }); + this.ddbClient = new DynamoDBClient({ + ...creds, + region, + maxAttempts: 10, + requestHandler: new NodeHttpHandler({ + httpAgent: proxyAgent(), + httpsAgent: proxyAgent(), + }), }); - this.ddbClient = new aws.DynamoDB({ ...creds, region, maxRetries: 10, httpOptions: { agent: proxyAgent() } }); this.logger = fileLogger('deployment-manager'); this.printer = new StackProgressPrinter(eventMap); this.spinner = new AmplifySpinner(); @@ -364,7 +383,7 @@ export class DeploymentManager { * @param stackName name of the stack */ private ensureStack = async (stackName: string): Promise => { - const result = await this.cfnClient.describeStacks({ StackName: stackName }).promise(); + const result = await this.cfnClient.send(new DescribeStacksCommand({ StackName: stackName })); return result.Stacks[0].StackStatus.endsWith('_COMPLETE'); }; @@ -375,14 +394,14 @@ export class DeploymentManager { private ensureTemplateExists = async (templatePath: string): Promise => { try { const bucketKey = getBucketKey(templatePath, this.deploymentBucket); - await this.s3Client.headObject({ Bucket: this.deploymentBucket, Key: bucketKey }).promise(); + await this.s3Client.send(new HeadObjectCommand({ Bucket: this.deploymentBucket, Key: bucketKey })); return true; } catch (e) { throw new AmplifyError( 'DeploymentError', { message: - e.code === 'NotFound' + e.name === 'NotFound' ? `The cloudformation template ${templatePath} was not found in deployment bucket ${this.deploymentBucket}` : e.message, details: e.message, @@ -396,14 +415,14 @@ export class DeploymentManager { assert(tableName, 'table name should be passed'); try { - const response = await this.ddbClient.describeTable({ TableName: tableName }).promise(); + const response = await this.ddbClient.send(new DescribeTableCommand({ TableName: tableName })); if (response.Table?.TableStatus === 'DELETING') { return false; } const globalSecondaryIndexes = response.Table?.GlobalSecondaryIndexes; return globalSecondaryIndexes ? globalSecondaryIndexes.every((idx) => idx.IndexStatus === 'ACTIVE') : true; } catch (err) { - if (err?.code === 'ResourceNotFoundException') { + if (err?.name === 'ResourceNotFoundException') { return true; // in the case of an iterative update that recreates a table, non-existence means the table has been fully removed } throw new AmplifyFault( @@ -481,26 +500,22 @@ export class DeploymentManager { ParameterValue: val.toString(), })); - await cfn - .updateStack({ + await cfn.send( + new UpdateStackCommand({ StackName: currentStack.stackName, Parameters: parameters, TemplateURL: currentStack.stackTemplateUrl, Capabilities: currentStack.capabilities, ClientRequestToken: currentStack.clientRequestToken, - }) - .promise(); + }), + ); }; private waitForDeployment = async (stackParams: DeploymentMachineOp): Promise => { const { cfnClient } = this; assert(stackParams.stackName, 'stackName should be passed to waitForDeployment'); - await cfnClient - .waitFor('stackUpdateComplete', { - StackName: stackParams.stackName, - }) - .promise(); + await waitUntilStackUpdateComplete({ client: cfnClient, maxWaitTime: 600 }, { StackName: stackParams.stackName }); }; private rollBackStack = async (currentStack: Readonly): Promise => { diff --git a/packages/amplify-provider-awscloudformation/src/iterative-deployment/stack-event-monitor.ts b/packages/amplify-provider-awscloudformation/src/iterative-deployment/stack-event-monitor.ts index f0c4c13354e..5db1a317585 100644 --- a/packages/amplify-provider-awscloudformation/src/iterative-deployment/stack-event-monitor.ts +++ b/packages/amplify-provider-awscloudformation/src/iterative-deployment/stack-event-monitor.ts @@ -1,7 +1,7 @@ -import { StackEvent } from 'aws-sdk/clients/cloudformation'; -import * as aws from 'aws-sdk'; +import { StackEvent } from '@aws-sdk/client-cloudformation'; import { AmplifyFault } from '@aws-amplify/amplify-cli-core'; import { fileLogger, Logger } from '../utils/aws-logger'; +import { CloudFormationClient, DescribeStackEventsCommand } from '@aws-sdk/client-cloudformation'; export interface StackEventMonitorOptions { pollDelay: number; @@ -31,7 +31,7 @@ export class StackEventMonitor { private logger: Logger; constructor( - private cfn: aws.CloudFormation, + private cfn: CloudFormationClient, private stackName: string, private printerFn: () => void, private addEventActivity: (event) => void, @@ -104,12 +104,12 @@ export class StackEventMonitor { let nextToken: string | undefined; let finished = false; while (!finished) { - const response = await this.cfn - .describeStackEvents({ + const response = await this.cfn.send( + new DescribeStackEventsCommand({ StackName: stackName, NextToken: nextToken, - }) - .promise(); + }), + ); const eventPage = response?.StackEvents ?? []; for (const event of eventPage) { @@ -143,10 +143,10 @@ export class StackEventMonitor { } } catch (e) { this.logger('readNewEvents', [])(e); - if (e.code === 'ValidationError' && e.message === `Stack [${this.stackName}] does not exist`) { + if (e.name === 'ValidationError' && e.message === `Stack [${this.stackName}] does not exist`) { return; } - if (e.code !== 'Throttling') { + if (e.name !== 'Throttling') { throw new AmplifyFault( 'NotImplementedFault', { diff --git a/packages/amplify-provider-awscloudformation/src/iterative-deployment/stack-progress-printer.ts b/packages/amplify-provider-awscloudformation/src/iterative-deployment/stack-progress-printer.ts index 8995e744a49..2d8f8a48dc3 100644 --- a/packages/amplify-provider-awscloudformation/src/iterative-deployment/stack-progress-printer.ts +++ b/packages/amplify-provider-awscloudformation/src/iterative-deployment/stack-progress-printer.ts @@ -1,5 +1,5 @@ import { MultiProgressBar } from '@aws-amplify/amplify-prompts'; -import type { StackEvent, StackEvents } from 'aws-sdk/clients/cloudformation'; +import type { StackEvent } from '@aws-sdk/client-cloudformation'; import chalk from 'chalk'; import columnify from 'columnify'; import { @@ -15,7 +15,7 @@ import { IStackProgressPrinter } from './stack-event-monitor'; * Iterative deployment stack printer. */ export class StackProgressPrinter implements IStackProgressPrinter { - private events: StackEvents = []; + private events: StackEvent[] = []; private progressBars: MultiProgressBar; private eventMap: EventMap; private categoriesPrinted: string[] = []; diff --git a/packages/amplify-provider-awscloudformation/src/iterative-deployment/state-machine.ts b/packages/amplify-provider-awscloudformation/src/iterative-deployment/state-machine.ts index b5622ffacf0..9d7835148e0 100644 --- a/packages/amplify-provider-awscloudformation/src/iterative-deployment/state-machine.ts +++ b/packages/amplify-provider-awscloudformation/src/iterative-deployment/state-machine.ts @@ -10,6 +10,7 @@ import { collectError, } from './helpers'; import { send } from 'xstate/lib/actions'; +import { Capability } from '@aws-sdk/client-cloudformation'; export type DeploymentMachineOp = { stackTemplatePath: string; @@ -17,7 +18,7 @@ export type DeploymentMachineOp = { parameters: Record; tableNames: string[]; stackName: string; - capabilities?: string[]; + capabilities?: Capability[]; stackTemplateUrl: string; region: string; clientRequestToken?: string; diff --git a/packages/amplify-provider-awscloudformation/src/network/environment-info.ts b/packages/amplify-provider-awscloudformation/src/network/environment-info.ts index 7396dc4582c..87068dcdd7c 100644 --- a/packages/amplify-provider-awscloudformation/src/network/environment-info.ts +++ b/packages/amplify-provider-awscloudformation/src/network/environment-info.ts @@ -1,5 +1,11 @@ import { AmplifyError, JSONUtilities } from '@aws-amplify/amplify-cli-core'; -import { EC2 } from 'aws-sdk'; +import { + EC2Client, + DescribeAvailabilityZonesCommand, + DescribeVpcsCommand, + DescribeInternetGatewaysCommand, + DescribeSubnetsCommand, +} from '@aws-sdk/client-ec2'; import { Netmask } from 'netmask'; import { loadConfiguration } from '../configuration-manager'; import { RESOURCE_TAG } from './stack'; @@ -28,9 +34,9 @@ export async function getEnvironmentNetworkInfo(context, params: GetEnvironmentN // ignore missing config } - const ec2 = new EC2({ ...cred }); + const ec2Client = new EC2Client(cred); - const { AvailabilityZones } = await ec2.describeAvailabilityZones().promise(); + const { AvailabilityZones } = await ec2Client.send(new DescribeAvailabilityZonesCommand({})); if (subnetsCount > AvailabilityZones.length) { const subnets = subnetsCount; @@ -44,11 +50,11 @@ export async function getEnvironmentNetworkInfo(context, params: GetEnvironmentN }); } - const { Vpcs } = await ec2 - .describeVpcs({ + const { Vpcs } = await ec2Client.send( + new DescribeVpcsCommand({ Filters: [{ Name: 'tag:Name', Values: [vpcName] }], - }) - .promise(); + }), + ); if (Vpcs.length === 0) { // we need a new one @@ -69,8 +75,8 @@ export async function getEnvironmentNetworkInfo(context, params: GetEnvironmentN }); } - const { InternetGateways } = await ec2 - .describeInternetGateways({ + const { InternetGateways } = await ec2Client.send( + new DescribeInternetGatewaysCommand({ Filters: [ { Name: 'attachment.vpc-id', @@ -81,8 +87,8 @@ export async function getEnvironmentNetworkInfo(context, params: GetEnvironmentN Values: ['available'], }, ], - }) - .promise(); + }), + ); if (vpcId && InternetGateways.length === 0) { throw new AmplifyError('ConfigurationError', { @@ -92,7 +98,7 @@ export async function getEnvironmentNetworkInfo(context, params: GetEnvironmentN const [{ InternetGatewayId: internetGatewayId = undefined } = {}] = InternetGateways; - const { Subnets } = await ec2.describeSubnets({ Filters: [{ Name: 'vpc-id', Values: [vpcId] }] }).promise(); + const { Subnets } = await ec2Client.send(new DescribeSubnetsCommand({ Filters: [{ Name: 'vpc-id', Values: [vpcId] }] })); const availabilityZonesIterator = new (class implements IterableIterator { private counter = 0; diff --git a/packages/amplify-provider-awscloudformation/src/permissions-boundary/permissions-boundary.ts b/packages/amplify-provider-awscloudformation/src/permissions-boundary/permissions-boundary.ts index 81654f88b18..122d611d1b4 100644 --- a/packages/amplify-provider-awscloudformation/src/permissions-boundary/permissions-boundary.ts +++ b/packages/amplify-provider-awscloudformation/src/permissions-boundary/permissions-boundary.ts @@ -8,6 +8,7 @@ import { } from '@aws-amplify/amplify-cli-core'; import { prompt } from 'inquirer'; import { IAMClient } from '../aws-utils/aws-iam'; +import { GetPolicyCommand } from '@aws-sdk/client-iam'; export const configurePermissionsBoundaryForExistingEnv = async (context: $TSContext) => { setPermissionsBoundaryArn(await permissionsBoundarySupplier(context)); @@ -136,10 +137,10 @@ const rolloverPermissionsBoundaryToNewEnvironment = async (context: $TSContext) const isPolicyAccessible = async (context: $TSContext, policyArn: string) => { const iamClient = await IAMClient.getInstance(context); try { - await iamClient.client.getPolicy({ PolicyArn: policyArn }).promise(); + await iamClient.client.send(new GetPolicyCommand({ PolicyArn: policyArn })); } catch (err) { // NoSuchEntity error - if (err?.statusCode === 404) { + if (err?.name.includes('NoSuchEntity')) { return false; } // if it's some other error (such as client credentials don't have getPolicy permissions, or network error) diff --git a/packages/amplify-provider-awscloudformation/src/push-resources.ts b/packages/amplify-provider-awscloudformation/src/push-resources.ts index 1f312b9fb76..976a0a47e0d 100644 --- a/packages/amplify-provider-awscloudformation/src/push-resources.ts +++ b/packages/amplify-provider-awscloudformation/src/push-resources.ts @@ -505,7 +505,7 @@ export const run = async (context: $TSContext, resourceDefinition: $TSObject, re 'DeploymentFault', { message: error.message, - code: error.code, + code: error.name, details: error.details, }, error, diff --git a/packages/amplify-provider-awscloudformation/src/system-config-manager.ts b/packages/amplify-provider-awscloudformation/src/system-config-manager.ts index 69e738c01fa..8ecac5d24f4 100644 --- a/packages/amplify-provider-awscloudformation/src/system-config-manager.ts +++ b/packages/amplify-provider-awscloudformation/src/system-config-manager.ts @@ -1,6 +1,8 @@ import { $TSAny, $TSContext, AmplifyError, JSONUtilities, pathManager, SecretFileMode, spinner } from '@aws-amplify/amplify-cli-core'; -import { STS, ProcessCredentials, CredentialProviderChain } from 'aws-sdk'; +import { STSClient, AssumeRoleCommand, AssumeRoleCommandInput } from '@aws-sdk/client-sts'; +import { fromProcess } from '@aws-sdk/credential-providers'; +import { NodeHttpHandler } from '@smithy/node-http-handler'; import * as fs from 'fs-extra'; import * as path from 'path'; import * as ini from 'ini'; @@ -91,23 +93,24 @@ export const getProfiledAwsConfig = async ( ...roleCredentials, }; } else if (profileConfig.credential_process) { - // need to force AWS_SDK_LOAD_CONFIG to a truthy value to force ProcessCredentials to prefer the credential process in ~/.aws/config instead of ~/.aws/credentials + // TO DO: need to revisit this + // need to force AWS_SDK_LOAD_CONFIG to a truthy value to force credential process to prefer the credential process in ~/.aws/config instead of ~/.aws/credentials const sdkLoadConfigOriginal = process.env.AWS_SDK_LOAD_CONFIG; process.env.AWS_SDK_LOAD_CONFIG = '1'; - const chain = new CredentialProviderChain(); - const processProvider = () => new ProcessCredentials({ profile: profileName }); - chain.providers.push(processProvider); + const credentials = await fromProcess({ profile: profileName })(); - const credentials = await chain.resolvePromise(); awsConfigInfo = { - region: profileConfig.region, - accessKeyId: credentials.accessKeyId, - secretAccessKey: credentials.secretAccessKey, - sessionToken: credentials.sessionToken, - expiration: credentials.expireTime, - httpOptions: { - agent: proxyAgent(), + credentials: { + accessKeyId: credentials.accessKeyId, + secretAccessKey: credentials.secretAccessKey, + sessionToken: credentials.sessionToken, + expiration: credentials.expiration, }, + region: profileConfig.region, + requestHandler: new NodeHttpHandler({ + httpAgent: proxyAgent(), + httpsAgent: proxyAgent(), + }), }; process.env.AWS_SDK_LOAD_CONFIG = sdkLoadConfigOriginal; } else { @@ -115,7 +118,9 @@ export const getProfiledAwsConfig = async ( const profileCredentials = getProfileCredentials(profileName); awsConfigInfo = { ...profileConfig, - ...profileCredentials, + credentials: { + ...profileCredentials, + }, }; validateCredentials(awsConfigInfo, profileName); } @@ -149,8 +154,12 @@ const getRoleCredentials = async (context: $TSContext, profileName: string, prof mfaTokenCode = await getMfaTokenCode(); } logger('getRoleCredentials.aws.STS', [sourceProfileAwsConfig])(); - const sts = new STS(sourceProfileAwsConfig); - const assumeRoleRequest = { + + const stsClient = new STSClient({ + ...sourceProfileAwsConfig, + }); + + const assumeRoleRequest: AssumeRoleCommandInput = { RoleArn: profileConfig.role_arn, RoleSessionName: roleSessionName, DurationSeconds: profileConfig.duration_seconds, @@ -161,13 +170,17 @@ const getRoleCredentials = async (context: $TSContext, profileName: string, prof const log = logger('getRoleCredentials.sts.assumeRole', [assumeRoleRequest]); try { log(); - const roleData = await sts.assumeRole(assumeRoleRequest).promise(); - roleCredentials = { - accessKeyId: roleData.Credentials.AccessKeyId, - secretAccessKey: roleData.Credentials.SecretAccessKey, - sessionToken: roleData.Credentials.SessionToken, - expiration: roleData.Credentials.Expiration, - }; + const command = new AssumeRoleCommand(assumeRoleRequest); + const roleData = await stsClient.send(command); + + if (roleData.Credentials) { + roleCredentials = { + accessKeyId: roleData.Credentials.AccessKeyId, + secretAccessKey: roleData.Credentials.SecretAccessKey, + sessionToken: roleData.Credentials.SessionToken, + expiration: roleData.Credentials.Expiration, + }; + } } catch (ex) { log(ex); } @@ -332,10 +345,10 @@ export const getProfileCredentials = (profileName: string): $TSAny => { const validateCredentials = (credentials: $TSAny, profileName: string): void => { const missingKeys = []; - if (!credentials?.accessKeyId && !process.env.AWS_ACCESS_KEY_ID) { + if (!credentials?.credentials.accessKeyId && !process.env.AWS_ACCESS_KEY_ID) { missingKeys.push('aws_access_key_id'); } - if (!credentials?.secretAccessKey && !process.env.AWS_SECRET_ACCESS_KEY) { + if (!credentials?.credentials.secretAccessKey && !process.env.AWS_SECRET_ACCESS_KEY) { missingKeys.push('aws_secret_access_key'); } if (missingKeys.length > 0) { diff --git a/packages/amplify-provider-awscloudformation/src/utility-functions.js b/packages/amplify-provider-awscloudformation/src/utility-functions.js index 8d61590db28..e1cd9ecd366 100644 --- a/packages/amplify-provider-awscloudformation/src/utility-functions.js +++ b/packages/amplify-provider-awscloudformation/src/utility-functions.js @@ -14,6 +14,26 @@ const { run: archiver } = require('./utils/archiver'); const ECR = require('./aws-utils/aws-ecr'); const { pagedAWSCall } = require('./aws-utils/paged-call'); const { fileLogger } = require('./utils/aws-logger'); +const { + GetGraphqlApiCommand, + GetIntrospectionSchemaCommand, + ListApiKeysCommand, + ListGraphqlApisCommand, +} = require('@aws-sdk/client-appsync'); +const { ListHostedZonesCommand } = require('@aws-sdk/client-route-53'); +const { + CreateSecretCommand, + DescribeSecretCommand, + GetSecretValueCommand, + PutSecretValueCommand, + UpdateSecretCommand, +} = require('@aws-sdk/client-secrets-manager'); +const { ListFunctionsCommand } = require('@aws-sdk/client-lambda'); +const { DescribeVoicesCommand } = require('@aws-sdk/client-polly'); +const { DescribeTableCommand, ListTablesCommand } = require('@aws-sdk/client-dynamodb'); +const { GetBuiltinSlotTypesCommand, GetSlotTypesCommand } = require('@aws-sdk/client-lex-model-building-service'); +const { ListEndpointsCommand } = require('@aws-sdk/client-sagemaker'); +const { DescribeRepositoriesCommand } = require('@aws-sdk/client-ecr'); const logger = fileLogger('utility-functions'); const { getAccountId } = require('./amplify-sts'); @@ -35,12 +55,12 @@ module.exports = { let zoneFound; do { - const { NextMarker, IsTruncated, HostedZones } = await client.route53 - .listHostedZones({ + const { NextMarker, IsTruncated, HostedZones } = await client.route53.send( + new ListHostedZonesCommand({ Marker, MaxItems: '100', - }) - .promise(); + }), + ); zoneFound = HostedZones.find((zone) => `${domain}.`.endsWith(zone.Name)); @@ -88,14 +108,14 @@ module.exports = { newSecret: async (context, options) => { const { description, secret, name, version } = options; const client = await new SecretsManager(context); - const response = await client.secretsManager - .createSecret({ + const response = await client.secretsManager.send( + new CreateSecretCommand({ Description: description, Name: name, SecretString: secret, ClientRequestToken: version, - }) - .promise(); + }), + ); return response; }, @@ -105,14 +125,14 @@ module.exports = { updateSecret: async (context, options) => { const { description, secret, name, version } = options; const client = await new SecretsManager(context); - const response = await client.secretsManager - .updateSecret({ + const response = await client.secretsManager.send( + new UpdateSecretCommand({ SecretId: name, Description: description, SecretString: secret, ClientRequestToken: version, - }) - .promise(); + }), + ); return response; }, @@ -127,11 +147,12 @@ module.exports = { let secretArn; try { - ({ ARN: secretArn } = await client.secretsManager.describeSecret({ SecretId: name }).promise()); + ({ ARN: secretArn } = await client.secretsManager.send(new DescribeSecretCommand({ SecretId: name }))); } catch (error) { - const { code } = error; + const { name } = error; - if (code !== 'ResourceNotFoundException') { + if (name !== 'ResourceNotFoundException') { + console.log(error); throw new AmplifyFault( 'ResourceNotFoundFault', { @@ -153,12 +174,12 @@ module.exports = { putSecretValue: async (context, options) => { const { name, secret } = options; const client = await new SecretsManager(context); - const response = await client.secretsManager - .putSecretValue({ + const response = await client.secretsManager.send( + new PutSecretValueCommand({ SecretId: name, SecretString: secret, - }) - .promise(); + }), + ); return response; }, @@ -169,11 +190,11 @@ module.exports = { retrieveSecret: async (context, options) => { const { secretArn: SecretId } = options; const client = await new SecretsManager(context); - const response = await client.secretsManager - .getSecretValue({ + const response = await client.secretsManager.send( + new GetSecretValueCommand({ SecretId, - }) - .promise(); + }), + ); return response; }, @@ -219,12 +240,12 @@ module.exports = { MaxItems: 10000, Marker: nextMarker, })(); - const paginatedFunctions = await lambdaModel.lambda - .listFunctions({ + const paginatedFunctions = await lambdaModel.lambda.send( + new ListFunctionsCommand({ MaxItems: 10000, Marker: nextMarker, - }) - .promise(); + }), + ); if (paginatedFunctions && paginatedFunctions.Functions) { lambdaFunctions.push(...paginatedFunctions.Functions); } @@ -238,7 +259,7 @@ module.exports = { getPollyVoices: async (context) => { const pollyModel = await new Polly(context); logger('getPollyVoices.pollyModel.polly.describeVoices', [])(); - return pollyModel.polly.describeVoices().promise(); + return pollyModel.polly.send(new DescribeVoicesCommand()); }, /** * @@ -256,7 +277,7 @@ module.exports = { ExclusiveStartTableName: nextToken, }, ])(); - const paginatedTables = await dynamodbModel.dynamodb.listTables({ Limit: 100, ExclusiveStartTableName: nextToken }).promise(); + const paginatedTables = await dynamodbModel.dynamodb.send(new ListTablesCommand({ Limit: 100, ExclusiveStartTableName: nextToken })); const dynamodbTables = paginatedTables.TableNames; nextToken = paginatedTables.LastEvaluatedTableName; for (let i = 0; i < dynamodbTables.length; i += 1) { @@ -266,11 +287,11 @@ module.exports = { }, ])(); describeTablePromises.push( - dynamodbModel.dynamodb - .describeTable({ + dynamodbModel.dynamodb.send( + new DescribeTableCommand({ TableName: dynamodbTables[i], - }) - .promise(), + }), + ), ); } } while (nextToken); @@ -306,7 +327,7 @@ module.exports = { return new AppSync(context) .then((result) => { const appSyncModel = result; - return appSyncModel.appSync.listGraphqlApis({ maxResults: 25 }).promise(); + return appSyncModel.appSync.send(new ListGraphqlApisCommand({ maxResults: 25 })); }) .then((result) => result.graphqlApis); }, @@ -328,7 +349,7 @@ module.exports = { format: 'JSON', }, ])(); - return appSyncModel.appSync.getIntrospectionSchema({ apiId: options.apiId, format: 'JSON' }).promise(); + return appSyncModel.appSync.send(GetIntrospectionSchemaCommand({ apiId: options.apiId, format: 'JSON' })); }) .then((result) => result.schema.toString() || null); }, @@ -347,7 +368,7 @@ module.exports = { ])(); return new AppSync(context, awsOptions).then((result) => { const appSyncModel = result; - return appSyncModel.appSync.getGraphqlApi({ apiId: options.apiId }).promise(); + return appSyncModel.appSync.send(new GetGraphqlApiCommand({ apiId: options.apiId })); }); }, /** @@ -364,7 +385,7 @@ module.exports = { logger('getBuiltInSlotTypes.lex.getBuiltinSlotTypes', [params])(); return new Lex(context).then((result) => { logger(); - return result.lex.getBuiltinSlotTypes(params).promise(); + return result.lex.send(new GetBuiltinSlotTypesCommand(params)); }); }, /** @@ -375,7 +396,7 @@ module.exports = { maxResults: 50, }; logger('getSlotTypes.lex.getSlotTypes', [params])(); - return new Lex(context).then((result) => result.lex.getSlotTypes(params).promise()); + return new Lex(context).then((result) => result.lex.send(new GetSlotTypesCommand(params))); }, /** * @deprecated Use getGraphQLApiKeys instead @@ -394,7 +415,7 @@ module.exports = { apiId: options.apiId, }, ])(); - return new AppSync(context, awsOptions).then((result) => result.appSync.listApiKeys({ apiId: options.apiId }).promise()); + return new AppSync(context, awsOptions).then((result) => result.appSync.send(new ListApiKeysCommand({ apiId: options.apiId }))); }, /** * @@ -402,7 +423,7 @@ module.exports = { getEndpoints: async (context) => { const sagemakerModel = await new SageMaker(context); logger('getEndpoints.sageMaker.listEndpoints', [])(); - return sagemakerModel.sageMaker.listEndpoints().promise(); + return sagemakerModel.sageMaker.send(new ListEndpointsCommand()); }, /** * @@ -411,7 +432,7 @@ module.exports = { const ecr = await new ECR(context); const results = await pagedAWSCall( - async (params, nextToken) => ecr.ecr.describeRepositories({ ...params, nextToken }).promise(), + async (params, nextToken) => ecr.ecr.send(new DescribeRepositoriesCommand({ ...params, nextToken })), options, ({ repositories }) => repositories, ({ nextToken }) => nextToken, diff --git a/packages/amplify-provider-awscloudformation/src/utils/admin-helpers.ts b/packages/amplify-provider-awscloudformation/src/utils/admin-helpers.ts index d5341f2d0e1..ec3e972e5bc 100644 --- a/packages/amplify-provider-awscloudformation/src/utils/admin-helpers.ts +++ b/packages/amplify-provider-awscloudformation/src/utils/admin-helpers.ts @@ -1,5 +1,7 @@ import { stateManager, $TSContext, AmplifyError, AmplifyFault } from '@aws-amplify/amplify-cli-core'; -import aws from 'aws-sdk'; +import { STSClient, AssumeRoleCommand } from '@aws-sdk/client-sts'; +import { CognitoIdentityClient, GetCredentialsForIdentityCommand } from '@aws-sdk/client-cognito-identity'; +import { CognitoIdentityProviderClient, InitiateAuthCommand } from '@aws-sdk/client-cognito-identity-provider'; import _ from 'lodash'; import fetch from 'node-fetch'; import { ProxyAgent } from 'proxy-agent'; @@ -75,23 +77,25 @@ export async function getTempCredsWithAdminTokens(context: $TSContext, appId: st // need to use Cognito creds to get STS creds - otherwise // users will not be able to provision Cognito resources - const sts = new aws.STS({ + const sts = new STSClient({ ...awsConfigInfo, - stsRegionalEndpoints: 'regional', }); - const { Credentials } = await sts - .assumeRole({ + + const { Credentials } = await sts.send( + new AssumeRoleCommand({ RoleArn: idToken.payload['cognito:preferred_role'], RoleSessionName: 'amplifyadmin', - }) - .promise(); + }), + ); return { - accessKeyId: Credentials.AccessKeyId, - expiration: Credentials.Expiration, + credentials: { + accessKeyId: Credentials.AccessKeyId, + secretAccessKey: Credentials.SecretAccessKey, + sessionToken: Credentials.SessionToken, + expiration: Credentials.Expiration, + }, region, - secretAccessKey: Credentials.SecretAccessKey, - sessionToken: Credentials.SessionToken, }; } @@ -118,23 +122,28 @@ async function getAdminAppState(appId: string, region: string): Promise { - const cognitoIdentity = new aws.CognitoIdentity({ region }); + const cognitoIdentity = new CognitoIdentityClient({ + region, + }); + const login = idToken.payload.iss.replace('https://', ''); - const { Credentials } = await cognitoIdentity - .getCredentialsForIdentity({ + const { Credentials } = await cognitoIdentity.send( + new GetCredentialsForIdentityCommand({ IdentityId: identityId, Logins: { [login]: idToken.jwtToken, }, - }) - .promise(); + }), + ); return { - accessKeyId: Credentials.AccessKeyId, - expiration: Credentials.Expiration, + credentials: { + accessKeyId: Credentials.AccessKeyId, + secretAccessKey: Credentials.SecretKey, + sessionToken: Credentials.SessionToken, + expiration: Credentials.Expiration, + }, region, - secretAccessKey: Credentials.SecretKey, - sessionToken: Credentials.SessionToken, }; } @@ -143,9 +152,10 @@ async function getRefreshedTokens(context: $TSContext, appId: string) { const authConfig: AdminAuthConfig = stateManager.getAmplifyAdminConfigEntry(appId); if (isJwtExpired(authConfig.idToken)) { - let refreshedTokens: aws.CognitoIdentityServiceProvider.AuthenticationResultType; + let refreshedTokens; try { - refreshedTokens = (await refreshJWTs(authConfig)).AuthenticationResult; + const result = await refreshJWTs(authConfig); + refreshedTokens = result.AuthenticationResult; // Refresh stored tokens authConfig.accessToken.jwtToken = refreshedTokens.AccessToken; authConfig.idToken.jwtToken = refreshedTokens.IdToken; @@ -165,14 +175,18 @@ function isJwtExpired(token: CognitoAccessToken | CognitoIdToken) { } async function refreshJWTs(authConfig: AdminAuthConfig) { - const CognitoISP = new aws.CognitoIdentityServiceProvider({ region: authConfig.region }); - return await CognitoISP.initiateAuth({ - AuthFlow: 'REFRESH_TOKEN', - AuthParameters: { - REFRESH_TOKEN: authConfig.refreshToken.token, - }, - ClientId: authConfig.accessToken.payload.client_id, // App client id from identityPool - }).promise(); + const cognitoISP = new CognitoIdentityProviderClient({ + region: authConfig.region, + }); + return await cognitoISP.send( + new InitiateAuthCommand({ + AuthFlow: 'REFRESH_TOKEN', + AuthParameters: { + REFRESH_TOKEN: authConfig.refreshToken.token, + }, + ClientId: authConfig.accessToken.payload.client_id, // App client id from identityPool + }), + ); } export const adminBackendMap: { diff --git a/packages/amplify-provider-awscloudformation/src/utils/admin-login-server.ts b/packages/amplify-provider-awscloudformation/src/utils/admin-login-server.ts index 6eaf54a4630..82c427b1f9b 100644 --- a/packages/amplify-provider-awscloudformation/src/utils/admin-login-server.ts +++ b/packages/amplify-provider-awscloudformation/src/utils/admin-login-server.ts @@ -1,6 +1,5 @@ import { AmplifyError, stateManager } from '@aws-amplify/amplify-cli-core'; import * as assert from 'assert'; -import { CognitoIdentity } from 'aws-sdk'; import bodyParser from 'body-parser'; // eslint-disable-line import cors from 'cors'; import express from 'express'; // eslint-disable-line @@ -10,6 +9,7 @@ import _ from 'lodash'; import { Printer } from '@aws-amplify/amplify-prompts'; import { AdminAuthPayload, CognitoAccessToken, CognitoIdToken } from './auth-types'; +import { CognitoIdentityClient, GetIdCommand } from '@aws-sdk/client-cognito-identity'; /** * Admin login server class @@ -61,17 +61,17 @@ export class AdminLoginServer { private async getIdentityId(idToken: CognitoIdToken, IdentityPoolId: string, region: string): Promise { // eslint-disable-line - const cognitoIdentity = new CognitoIdentity({ region }); + const cognitoIdentity = new CognitoIdentityClient({ region }); const login = idToken.payload.iss.replace('https://', ''); const logins = { [login]: idToken.jwtToken, }; - const { IdentityId } = await cognitoIdentity - .getId({ + const { IdentityId } = await cognitoIdentity.send( + new GetIdCommand({ IdentityPoolId, Logins: logins, - }) - .promise(); + }), + ); if (!IdentityId) { throw new AmplifyError('AmplifyStudioLoginError', { message: 'IdentityId not defined. Amplify CLI was unable to retrieve credentials.', diff --git a/packages/amplify-provider-awscloudformation/src/utils/amplify-resource-state-utils.ts b/packages/amplify-provider-awscloudformation/src/utils/amplify-resource-state-utils.ts index 7c0de66a677..60a91372f99 100644 --- a/packages/amplify-provider-awscloudformation/src/utils/amplify-resource-state-utils.ts +++ b/packages/amplify-provider-awscloudformation/src/utils/amplify-resource-state-utils.ts @@ -1,7 +1,6 @@ import { Template } from 'cloudform-types'; import { GlobalSecondaryIndex, AttributeDefinition } from 'cloudform-types/types/dynamoDb/table'; -import { CloudFormation } from 'aws-sdk'; -import { Capabilities } from 'aws-sdk/clients/cloudformation'; +import { CloudFormationClient, DescribeStacksCommand, DescribeStackResourcesCommand, Capability } from '@aws-sdk/client-cloudformation'; import _ from 'lodash'; import { JSONUtilities } from '@aws-amplify/amplify-cli-core'; @@ -14,16 +13,15 @@ export interface GSIRecord { */ export interface DeploymentRecord { parameters?: Record; - capabilities?: Capabilities; + capabilities?: Capability[]; } -export const getPreviousDeploymentRecord = async (cfnClient: CloudFormation, stackId: string): Promise => { +export const getPreviousDeploymentRecord = async (cfnClient: CloudFormationClient, stackId: string): Promise => { const depRecord: DeploymentRecord = {}; - const apiStackInfo = await cfnClient - .describeStacks({ - StackName: stackId, - }) - .promise(); + const describeStacksCommand = new DescribeStacksCommand({ + StackName: stackId, + }); + const apiStackInfo = await cfnClient.send(describeStacksCommand); depRecord.parameters = apiStackInfo.Stacks[0].Parameters.reduce((acc, param) => { acc[param.ParameterKey] = param.ParameterValue; return acc; @@ -32,20 +30,18 @@ export const getPreviousDeploymentRecord = async (cfnClient: CloudFormation, sta return depRecord; }; -export const getTableNames = async (cfnClient: CloudFormation, tables: string[], StackId: string): Promise> => { +export const getTableNames = async (cfnClient: CloudFormationClient, tables: string[], StackId: string): Promise> => { const tableNameMap: Map = new Map(); - const apiResources = await cfnClient - .describeStackResources({ - StackName: StackId, - }) - .promise(); + const describeStackResourcesCommand = new DescribeStackResourcesCommand({ + StackName: StackId, + }); + const apiResources = await cfnClient.send(describeStackResourcesCommand); for (const resource of apiResources.StackResources) { if (tables.includes(resource.LogicalResourceId)) { - const tableStack = await cfnClient - .describeStacks({ - StackName: resource.PhysicalResourceId, - }) - .promise(); + const describeStacksCommand = new DescribeStacksCommand({ + StackName: resource.PhysicalResourceId, + }); + const tableStack = await cfnClient.send(describeStacksCommand); const tableName = tableStack.Stacks[0].Outputs.reduce((acc, out) => { if (out.OutputKey === `GetAtt${resource.LogicalResourceId}TableName`) { acc.push(out.OutputValue); diff --git a/packages/amplify-provider-awscloudformation/src/utils/auth-types.ts b/packages/amplify-provider-awscloudformation/src/utils/auth-types.ts index 0fac6ded250..658217c717d 100644 --- a/packages/amplify-provider-awscloudformation/src/utils/auth-types.ts +++ b/packages/amplify-provider-awscloudformation/src/utils/auth-types.ts @@ -1,4 +1,5 @@ import { $TSAny } from '@aws-amplify/amplify-cli-core'; +import { NodeHttpHandler } from '@smithy/node-http-handler'; export type AuthFlow = 'admin' | 'profile' | 'accessKeys' | 'general'; export interface AuthFlowConfig extends Partial { @@ -9,15 +10,30 @@ export interface AuthFlowConfig extends Partial { } export interface AwsSdkConfig { - accessKeyId: string; - expiration?: Date; + credentials: { + accessKeyId: string; + secretAccessKey: string; + sessionToken?: string; + expiration?: Date; + }; region: string; + requestHandler?: NodeHttpHandler; + // TO DO: remove eventually, V2 style of handling proxies + httpOptions?: { + agent: $TSAny; + }; +} +export interface legacyAwsSdkConfig { + accessKeyId: string; secretAccessKey: string; sessionToken?: string; + expiration?: Date; + region: string; httpOptions?: { agent: $TSAny; }; } + export interface AdminAuthPayload { accessToken: CognitoAccessToken; clockDrift: number; diff --git a/packages/amplify-provider-awscloudformation/src/utils/ssm-utils/delete-ssm-parameters.ts b/packages/amplify-provider-awscloudformation/src/utils/ssm-utils/delete-ssm-parameters.ts index 5481adbbc23..f33bf957fd8 100644 --- a/packages/amplify-provider-awscloudformation/src/utils/ssm-utils/delete-ssm-parameters.ts +++ b/packages/amplify-provider-awscloudformation/src/utils/ssm-utils/delete-ssm-parameters.ts @@ -1,10 +1,10 @@ import { $TSContext, AmplifyFault, IAmplifyResource } from '@aws-amplify/amplify-cli-core'; import { printer } from '@aws-amplify/amplify-prompts'; -import type { SSM as SSMType } from 'aws-sdk'; import { SSM } from '../../aws-utils/aws-ssm'; import { resolveAppId } from '../resolve-appId'; import { executeSdkPromisesWithExponentialBackOff } from './exp-backoff-executor'; import { getSsmSdkParametersDeleteParameters, getSsmSdkParametersGetParametersByPath } from './get-ssm-sdk-parameters'; +import { DeleteParametersCommand, DeleteParametersResult, GetParametersByPathCommand, SSMClient } from '@aws-sdk/client-ssm'; /** * Delete all CloudFormation parameters from the service for a given environment @@ -55,7 +55,7 @@ export const deleteEnvironmentParametersForResources = async ( await deleteParametersFromParameterStore(client, removedParameterKeys); }; -const deleteParametersFromParameterStore = async (ssmClient: SSMType, parameterKeys: string[]): Promise => { +const deleteParametersFromParameterStore = async (ssmClient: SSMClient, parameterKeys: string[]): Promise => { if (parameterKeys.length === 0) { return; } @@ -63,10 +63,10 @@ const deleteParametersFromParameterStore = async (ssmClient: SSMType, parameterK const chunkedKeys = chunkForParameterStore(parameterKeys); const deleteKeysFromPSPromises = chunkedKeys.map((keys) => { const ssmArgument = getSsmSdkParametersDeleteParameters(keys); - return () => ssmClient.deleteParameters(ssmArgument).promise(); + return () => ssmClient.send(new DeleteParametersCommand(ssmArgument)); }); - await executeSdkPromisesWithExponentialBackOff(deleteKeysFromPSPromises); + await executeSdkPromisesWithExponentialBackOff(deleteKeysFromPSPromises); } catch (e) { throw new AmplifyFault( 'ParametersDeleteFault', @@ -85,12 +85,12 @@ function isAmplifyParameter(parameter: string) { return lastPartOfPath.startsWith(keyPrefix); } -const getAllEnvParametersFromParameterStore = async (appId: string, envName: string, ssmClient: SSMType): Promise> => { +const getAllEnvParametersFromParameterStore = async (appId: string, envName: string, ssmClient: SSMClient): Promise> => { const parametersUnderPath: Array = []; let receivedNextToken = ''; do { const ssmArgument = getSsmSdkParametersGetParametersByPath(appId, envName, receivedNextToken); - const [data] = await executeSdkPromisesWithExponentialBackOff([() => ssmClient.getParametersByPath(ssmArgument).promise()]); + const [data] = await executeSdkPromisesWithExponentialBackOff([() => ssmClient.send(new GetParametersByPathCommand(ssmArgument))]); parametersUnderPath.push( ...data.Parameters.map((returnedParameter) => returnedParameter.Name).filter((name) => isAmplifyParameter(name)), ); diff --git a/packages/amplify-provider-awscloudformation/src/utils/ssm-utils/env-parameter-ssm-helpers.ts b/packages/amplify-provider-awscloudformation/src/utils/ssm-utils/env-parameter-ssm-helpers.ts index 592c87e0b56..e951c37deb7 100644 --- a/packages/amplify-provider-awscloudformation/src/utils/ssm-utils/env-parameter-ssm-helpers.ts +++ b/packages/amplify-provider-awscloudformation/src/utils/ssm-utils/env-parameter-ssm-helpers.ts @@ -1,9 +1,16 @@ import { $TSContext, AmplifyFault, stateManager } from '@aws-amplify/amplify-cli-core'; import { printer } from '@aws-amplify/amplify-prompts'; -import type { SSM as SSMType } from 'aws-sdk'; import { SSM } from '../../aws-utils/aws-ssm'; import { resolveAppId } from '../resolve-appId'; import { executeSdkPromisesWithExponentialBackOff } from './exp-backoff-executor'; +import { + GetParametersByPathResult, + GetParametersCommand, + GetParametersResult, + PutParameterCommand, + PutParameterCommandInput, + SSMClient, +} from '@aws-sdk/client-ssm'; /** * Higher order function for uploading CloudFormation parameters to the service @@ -30,7 +37,7 @@ export const getEnvParametersUploadHandler = async ( const uploadParameterToParameterStore = ( appId: string, envName: string, - ssmClient: SSMType, + ssmClient: SSMClient, ): ((key: string, value: string | boolean | number) => Promise) => { return async (key: string, value: string | boolean | number): Promise => { try { @@ -41,8 +48,8 @@ const uploadParameterToParameterStore = ( Tier: 'Standard', Type: 'String', Value: stringValue, - }; - await executeSdkPromisesWithExponentialBackOff([() => ssmClient.putParameter(sdkParameters).promise()]); + } as PutParameterCommandInput; + await executeSdkPromisesWithExponentialBackOff([() => ssmClient.send(new PutParameterCommand(sdkParameters))]); } catch (e) { throw new AmplifyFault( 'ParameterUploadFault', @@ -80,7 +87,7 @@ export const getEnvParametersDownloadHandler = async (context: $TSContext): Prom return downloadParametersFromParameterStore(appId, envName, client); }; -const downloadParametersFromParameterStore = (appId: string, envName: string, ssmClient: SSMType): DownloadHandler => { +const downloadParametersFromParameterStore = (appId: string, envName: string, ssmClient: SSMClient): DownloadHandler => { return async (keys: string[]): Promise => { if (keys.length === 0) { return {}; @@ -88,7 +95,7 @@ const downloadParametersFromParameterStore = (appId: string, envName: string, ss const keyPaths = keys.map((key) => `/amplify/${appId}/${envName}/${key}`); try { const sdkPromises = convertKeyPathsToSdkPromises(ssmClient, keyPaths); - const results = await executeSdkPromisesWithExponentialBackOff(sdkPromises); + const results = await executeSdkPromisesWithExponentialBackOff(sdkPromises); return results.reduce((acc, { Parameters }) => { Parameters.forEach((param) => { const [, , , , /* leading slash */ /* amplify */ /* appId */ /* envName */ key] = param.Name.split('/'); @@ -108,7 +115,7 @@ const downloadParametersFromParameterStore = (appId: string, envName: string, ss }; }; -const convertKeyPathsToSdkPromises = (ssmClient: SSMType, keyPaths: string[]): (() => Promise)[] => { +const convertKeyPathsToSdkPromises = (ssmClient: SSMClient, keyPaths: string[]): (() => Promise)[] => { const sdkParameterChunks = []; for (let i = 0; i < keyPaths.length; i += 10) { sdkParameterChunks.push({ @@ -116,5 +123,5 @@ const convertKeyPathsToSdkPromises = (ssmClient: SSMType, keyPaths: string[]): ( WithDecryption: false, }); } - return sdkParameterChunks.map((sdkParameters) => () => ssmClient.getParameters(sdkParameters).promise()); + return sdkParameterChunks.map((sdkParameters) => () => ssmClient.send(new GetParametersCommand(sdkParameters))); }; diff --git a/packages/amplify-provider-awscloudformation/src/utils/ssm-utils/exp-backoff-executor.ts b/packages/amplify-provider-awscloudformation/src/utils/ssm-utils/exp-backoff-executor.ts index d62599c0c64..abf7b7e40f6 100644 --- a/packages/amplify-provider-awscloudformation/src/utils/ssm-utils/exp-backoff-executor.ts +++ b/packages/amplify-provider-awscloudformation/src/utils/ssm-utils/exp-backoff-executor.ts @@ -15,7 +15,7 @@ export const executeSdkPromisesWithExponentialBackOff = async (sdkPromises: ( backOffSleepTimeInMs = 500; consecutiveRetries = 0; } catch (e) { - if (e?.code === 'ThrottlingException' || e?.code === 'Throttling') { + if (e?.name === 'ThrottlingException' || e?.name === 'Throttling') { if (consecutiveRetries < MAX_RETRIES) { ++consecutiveRetries; await new Promise((resolve) => setTimeout(resolve, backOffSleepTimeInMs)); diff --git a/packages/amplify-provider-awscloudformation/src/zip-util.ts b/packages/amplify-provider-awscloudformation/src/zip-util.ts index 83611f580eb..2074a203369 100644 --- a/packages/amplify-provider-awscloudformation/src/zip-util.ts +++ b/packages/amplify-provider-awscloudformation/src/zip-util.ts @@ -1,11 +1,20 @@ import { AmplifyFault, extract } from '@aws-amplify/amplify-cli-core'; import fs from 'fs-extra'; import path from 'path'; +import { Readable } from 'stream'; import { S3 } from './aws-utils/aws-s3'; import { fileLogger } from './utils/aws-logger'; const logger = fileLogger('zip-util'); +export const streamToBuffer = async (stream: Readable): Promise => { + const chunks = []; + for await (const chunk of stream) { + chunks.push(Buffer.from(chunk)); + } + return Buffer.concat(chunks); +}; + /** * Downloads a zip file from S3 */ @@ -23,7 +32,7 @@ export const downloadZip = async (s3: S3, tempDir: string, zipFileName: string, // that doesn't seem to exist in runtime. // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - const buff = Buffer.from(objectResult); + const buff = await streamToBuffer(objectResult as ReadableStream); const tempFile = `${tempDir}/${zipFileName}`; await fs.writeFile(tempFile, buff); diff --git a/packages/amplify-python-function-runtime-provider/package.json b/packages/amplify-python-function-runtime-provider/package.json index d74fa3797a5..f30ca5abb29 100644 --- a/packages/amplify-python-function-runtime-provider/package.json +++ b/packages/amplify-python-function-runtime-provider/package.json @@ -35,7 +35,7 @@ }, "devDependencies": { "@types/fs-extra": "^8.0.1", - "@types/node": "^12.12.6", + "@types/node": "^20.9.0", "@types/semver": "^7.1.0" }, "berry": { diff --git a/packages/amplify-python-function-template-provider/package.json b/packages/amplify-python-function-template-provider/package.json index a46679959d4..53e26156c37 100644 --- a/packages/amplify-python-function-template-provider/package.json +++ b/packages/amplify-python-function-template-provider/package.json @@ -29,7 +29,7 @@ }, "devDependencies": { "@types/fs-extra": "^8.0.1", - "@types/node": "^12.12.6" + "@types/node": "^20.9.0" }, "berry": { "plugins": [ diff --git a/packages/amplify-util-import/API.md b/packages/amplify-util-import/API.md index 9fc18e2a0e1..f8476c591b1 100644 --- a/packages/amplify-util-import/API.md +++ b/packages/amplify-util-import/API.md @@ -4,13 +4,12 @@ ```ts -import { Buckets } from 'aws-sdk/clients/s3'; +import { Bucket } from '@aws-sdk/client-s3'; import { GetUserPoolMfaConfigResponse } from '@aws-sdk/client-cognito-identity-provider'; import { IdentityPool } from '@aws-sdk/client-cognito-identity'; import { IdentityPoolShortDescription } from '@aws-sdk/client-cognito-identity'; import { IdentityProviderType } from '@aws-sdk/client-cognito-identity-provider'; -import { TableDescription } from 'aws-sdk/clients/dynamodb'; -import { TableName } from 'aws-sdk/clients/dynamodb'; +import { TableDescription } from '@aws-sdk/client-dynamodb'; import { UserPoolClientType } from '@aws-sdk/client-cognito-identity-provider'; import { UserPoolDescriptionType } from '@aws-sdk/client-cognito-identity-provider'; import { UserPoolType } from '@aws-sdk/client-cognito-identity-provider'; @@ -34,7 +33,7 @@ export interface IDynamoDBService { // (undocumented) getTableDetails(tableName: string): Promise; // (undocumented) - listTables(): Promise; + listTables(): Promise; // (undocumented) tableExists(tableName: string): Promise; } @@ -61,7 +60,7 @@ export interface IS3Service { // (undocumented) getBucketLocation(bucketName: string): Promise; // (undocumented) - listBuckets(): Promise; + listBuckets(): Promise; } // (No @packageDocumentation comment for this package) diff --git a/packages/amplify-util-import/package.json b/packages/amplify-util-import/package.json index f66dc6c94fe..eaabdeb9700 100644 --- a/packages/amplify-util-import/package.json +++ b/packages/amplify-util-import/package.json @@ -22,10 +22,11 @@ "dependencies": { "@aws-sdk/client-cognito-identity": "^3.624.0", "@aws-sdk/client-cognito-identity-provider": "^3.624.0", - "aws-sdk": "^2.1464.0" + "@aws-sdk/client-dynamodb": "^3.515.0", + "@aws-sdk/client-s3": "^3.515.0" }, "devDependencies": { - "@types/node": "^12.12.6" + "@types/node": "^20.9.0" }, "berry": { "plugins": [ diff --git a/packages/amplify-util-import/src/IDynamoDBService.ts b/packages/amplify-util-import/src/IDynamoDBService.ts index 81864d704e4..597a80a347d 100644 --- a/packages/amplify-util-import/src/IDynamoDBService.ts +++ b/packages/amplify-util-import/src/IDynamoDBService.ts @@ -1,7 +1,7 @@ -import { TableDescription, TableName } from 'aws-sdk/clients/dynamodb'; +import { TableDescription } from '@aws-sdk/client-dynamodb'; export interface IDynamoDBService { - listTables(): Promise; + listTables(): Promise; getTableDetails(tableName: string): Promise; tableExists(tableName: string): Promise; } diff --git a/packages/amplify-util-import/src/IS3Service.ts b/packages/amplify-util-import/src/IS3Service.ts index a949250f3a2..b59e0179245 100644 --- a/packages/amplify-util-import/src/IS3Service.ts +++ b/packages/amplify-util-import/src/IS3Service.ts @@ -1,7 +1,7 @@ -import { Buckets } from 'aws-sdk/clients/s3'; +import { Bucket } from '@aws-sdk/client-s3'; export interface IS3Service { - listBuckets(): Promise; + listBuckets(): Promise; bucketExists(bucketName: string): Promise; getBucketLocation(bucketName: string): Promise; } diff --git a/packages/amplify-util-mock/package.json b/packages/amplify-util-mock/package.json index 2c3dbc67e8d..30781e9a9b9 100644 --- a/packages/amplify-util-mock/package.json +++ b/packages/amplify-util-mock/package.json @@ -73,7 +73,7 @@ "@types/detect-port": "^1.3.0", "@types/jest": "^29.0.0", "@types/lodash": "^4.14.149", - "@types/node": "^12.12.6", + "@types/node": "^20.9.0", "@types/semver": "^7.1.0", "@types/which": "^1.3.2", "amplify-nodejs-function-runtime-provider": "2.5.30", diff --git a/packages/amplify-util-mock/src/__tests__/utils/lambda/populate-lambda-mock-env-vars.test.ts b/packages/amplify-util-mock/src/__tests__/utils/lambda/populate-lambda-mock-env-vars.test.ts index 877734eb261..668c55c691f 100644 --- a/packages/amplify-util-mock/src/__tests__/utils/lambda/populate-lambda-mock-env-vars.test.ts +++ b/packages/amplify-util-mock/src/__tests__/utils/lambda/populate-lambda-mock-env-vars.test.ts @@ -11,9 +11,11 @@ jest.mock('dotenv'); const loadConfigurationForEnv_mock = loadConfigurationForEnv as jest.MockedFunction; loadConfigurationForEnv_mock.mockResolvedValue({ - accessKeyId: 'testaccesskey', - secretAccessKey: 'testsecretaccesskey', - sessionToken: 'testsessiontoken', + credentials: { + accessKeyId: 'testaccesskey', + secretAccessKey: 'testsecretaccesskey', + sessionToken: 'testsessiontoken', + }, region: 'test-region', }); diff --git a/packages/amplify-util-mock/src/utils/lambda/populate-lambda-mock-env-vars.ts b/packages/amplify-util-mock/src/utils/lambda/populate-lambda-mock-env-vars.ts index 64605763522..7232b633009 100644 --- a/packages/amplify-util-mock/src/utils/lambda/populate-lambda-mock-env-vars.ts +++ b/packages/amplify-util-mock/src/utils/lambda/populate-lambda-mock-env-vars.ts @@ -31,9 +31,9 @@ const getAwsCredentials = async (__, context: $TSContext): Promise=1.0.0" + peerDependenciesMeta: + aws-crt: + optional: true + checksum: 853dbd07cf4d6a1a88114417f83a00965bc8b2f66f78a00826f32faeab72e7544fbd36de7184f161d9bc7e9a6f765d56211a1aae17c1d7ba2ba6021af833a092 + languageName: node + linkType: hard + +"@aws-sdk/util-user-agent-node@npm:3.846.0": + version: 3.846.0 + resolution: "@aws-sdk/util-user-agent-node@npm:3.846.0" + dependencies: + "@aws-sdk/middleware-user-agent": 3.846.0 + "@aws-sdk/types": 3.840.0 + "@smithy/node-config-provider": ^4.1.3 + "@smithy/types": ^4.3.1 + tslib: ^2.6.2 + peerDependencies: + aws-crt: ">=1.0.0" + peerDependenciesMeta: + aws-crt: + optional: true + checksum: ee205a6bc978152f33da8a8e373ea16078e96b54403ed3c689f5cb87bfc207abbab6e1e61c52fea52b6f717f79836be61064eeebb4f510a2b4dd02013f03bcad + languageName: node + linkType: hard + +"@aws-sdk/util-user-agent-node@npm:3.848.0": + version: 3.848.0 + resolution: "@aws-sdk/util-user-agent-node@npm:3.848.0" + dependencies: + "@aws-sdk/middleware-user-agent": 3.848.0 + "@aws-sdk/types": 3.840.0 + "@smithy/node-config-provider": ^4.1.3 + "@smithy/types": ^4.3.1 + tslib: ^2.6.2 + peerDependencies: + aws-crt: ">=1.0.0" + peerDependenciesMeta: + aws-crt: + optional: true + checksum: 165308d1323ed0f56f4366e235674a73606c9d32a47c1572541c4befc6ce5ecca2d2334981f0d77791def22dad0a722773b1540f60f2d329710f2ade361801a6 + languageName: node + linkType: hard + +"@aws-sdk/util-user-agent-node@npm:3.864.0": + version: 3.864.0 + resolution: "@aws-sdk/util-user-agent-node@npm:3.864.0" + dependencies: + "@aws-sdk/middleware-user-agent": 3.864.0 + "@aws-sdk/types": 3.862.0 + "@smithy/node-config-provider": ^4.1.4 + "@smithy/types": ^4.3.2 + tslib: ^2.6.2 + peerDependencies: + aws-crt: ">=1.0.0" + peerDependenciesMeta: + aws-crt: + optional: true + checksum: 1eba907bbeb99d1c78912e94589ead12b6ecb6f2fbfffa4fafdff94439dc81d2adfa8145302c3d6bcf355ecee7687081f18d5034269f921affc00c5b8402a9bf + languageName: node + linkType: hard + "@aws-sdk/util-utf8-browser@npm:3.186.0, @aws-sdk/util-utf8-browser@npm:^3.0.0": version: 3.186.0 resolution: "@aws-sdk/util-utf8-browser@npm:3.186.0" @@ -5781,6 +8032,16 @@ __metadata: languageName: node linkType: hard +"@aws-sdk/xml-builder@npm:3.862.0": + version: 3.862.0 + resolution: "@aws-sdk/xml-builder@npm:3.862.0" + dependencies: + "@smithy/types": ^4.3.2 + tslib: ^2.6.2 + checksum: bf388c2cc23cd7d7fbe32d148b59b7476227cadc1d169d92b086befed128926d202c74a58af549888979f57f7bccff2db901b842f36aa135fb3be4b886199053 + languageName: node + linkType: hard + "@babel/cli@npm:^7.27.0": version: 7.27.0 resolution: "@babel/cli@npm:7.27.0" @@ -5808,14 +8069,14 @@ __metadata: languageName: node linkType: hard -"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.10.4, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.16.0, @babel/code-frame@npm:^7.26.2, @babel/code-frame@npm:^7.8.3": - version: 7.26.2 - resolution: "@babel/code-frame@npm:7.26.2" +"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.10.4, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.16.0, @babel/code-frame@npm:^7.26.2, @babel/code-frame@npm:^7.27.1, @babel/code-frame@npm:^7.8.3": + version: 7.27.1 + resolution: "@babel/code-frame@npm:7.27.1" dependencies: - "@babel/helper-validator-identifier": ^7.25.9 + "@babel/helper-validator-identifier": ^7.27.1 js-tokens: ^4.0.0 - picocolors: ^1.0.0 - checksum: 7d79621a6849183c415486af99b1a20b84737e8c11cd55b6544f688c51ce1fd710e6d869c3dd21232023da272a79b91efb3e83b5bc2dc65c1187c5fcd1b72ea8 + picocolors: ^1.1.1 + checksum: 5dd9a18baa5fce4741ba729acc3a3272c49c25cb8736c4b18e113099520e7ef7b545a4096a26d600e4416157e63e87d66db46aa3fbf0a5f2286da2705c12da00 languageName: node linkType: hard @@ -6112,10 +8373,10 @@ __metadata: languageName: node linkType: hard -"@babel/helper-validator-identifier@npm:^7.16.7, @babel/helper-validator-identifier@npm:^7.25.9": - version: 7.25.9 - resolution: "@babel/helper-validator-identifier@npm:7.25.9" - checksum: 4fc6f830177b7b7e887ad3277ddb3b91d81e6c4a24151540d9d1023e8dc6b1c0505f0f0628ae653601eb4388a8db45c1c14b2c07a9173837aef7e4116456259d +"@babel/helper-validator-identifier@npm:^7.16.7, @babel/helper-validator-identifier@npm:^7.25.9, @babel/helper-validator-identifier@npm:^7.27.1": + version: 7.27.1 + resolution: "@babel/helper-validator-identifier@npm:7.27.1" + checksum: c558f11c4871d526498e49d07a84752d1800bf72ac0d3dad100309a2eaba24efbf56ea59af5137ff15e3a00280ebe588560534b0e894a4750f8b1411d8f78b84 languageName: node linkType: hard @@ -8270,6 +10531,13 @@ __metadata: languageName: node linkType: hard +"@jest/diff-sequences@npm:30.0.1": + version: 30.0.1 + resolution: "@jest/diff-sequences@npm:30.0.1" + checksum: 3a840404e6021725ef7f86b11f7b2d13dd02846481264db0e447ee33b7ee992134e402cdc8b8b0ac969d37c6c0183044e382dedee72001cdf50cfb3c8088de74 + languageName: node + linkType: hard + "@jest/environment@npm:^26.6.2": version: 26.6.2 resolution: "@jest/environment@npm:26.6.2" @@ -8306,6 +10574,15 @@ __metadata: languageName: node linkType: hard +"@jest/expect-utils@npm:30.0.4": + version: 30.0.4 + resolution: "@jest/expect-utils@npm:30.0.4" + dependencies: + "@jest/get-type": 30.0.1 + checksum: eda2d34b883e72b4ccccac04082701d37d35cc924bba8bbf044578f34257885b04c343fbfa2949831ee75429f665f3b157066025b1e587737b946a64aa75e973 + languageName: node + linkType: hard + "@jest/expect-utils@npm:^29.7.0": version: 29.7.0 resolution: "@jest/expect-utils@npm:29.7.0" @@ -8367,6 +10644,13 @@ __metadata: languageName: node linkType: hard +"@jest/get-type@npm:30.0.1": + version: 30.0.1 + resolution: "@jest/get-type@npm:30.0.1" + checksum: 92437ae42d0df57e8acc2d067288151439db4752cde4f5e680c73c8a6e34568bbd8c1c81a2f2f9a637a619c2aac8bc87553fb80e31475b59e2ed789a71e5e540 + languageName: node + linkType: hard + "@jest/globals@npm:^27.5.1": version: 27.5.1 resolution: "@jest/globals@npm:27.5.1" @@ -8390,6 +10674,16 @@ __metadata: languageName: node linkType: hard +"@jest/pattern@npm:30.0.1": + version: 30.0.1 + resolution: "@jest/pattern@npm:30.0.1" + dependencies: + "@types/node": "*" + jest-regex-util: 30.0.1 + checksum: 32c5a7bfb6c591f004dac0ed36d645002ed168971e4c89bd915d1577031672870032594767557b855c5bc330aa1e39a2f54bf150d2ee88a7a0886e9cb65318bc + languageName: node + linkType: hard + "@jest/reporters@npm:^29.7.0": version: 29.7.0 resolution: "@jest/reporters@npm:29.7.0" @@ -8427,6 +10721,15 @@ __metadata: languageName: node linkType: hard +"@jest/schemas@npm:30.0.1": + version: 30.0.1 + resolution: "@jest/schemas@npm:30.0.1" + dependencies: + "@sinclair/typebox": ^0.34.0 + checksum: 27977359edc4b33293af7c85c53de5014a87c29b9ab98b0a827fedfc6635abdb522aad8c3ff276080080911f519699b094bd6f4e151b43f0cc5856ccc83c04a7 + languageName: node + linkType: hard + "@jest/schemas@npm:^29.6.3": version: 29.6.3 resolution: "@jest/schemas@npm:29.6.3" @@ -8540,6 +10843,21 @@ __metadata: languageName: node linkType: hard +"@jest/types@npm:30.0.1": + version: 30.0.1 + resolution: "@jest/types@npm:30.0.1" + dependencies: + "@jest/pattern": 30.0.1 + "@jest/schemas": 30.0.1 + "@types/istanbul-lib-coverage": ^2.0.6 + "@types/istanbul-reports": ^3.0.4 + "@types/node": "*" + "@types/yargs": ^17.0.33 + chalk: ^4.1.2 + checksum: 407469331e74f9bb1ffd40202c3a8cece2fd07ba535adeb60557bdcee13713cf2f14cf78869ba7ef50a7e6fe0ed7cc97ec775056dd640fc0a332e8fbfaec1ee8 + languageName: node + linkType: hard + "@jest/types@npm:^26.6.2": version: 26.6.2 resolution: "@jest/types@npm:26.6.2" @@ -9680,6 +11998,13 @@ __metadata: languageName: node linkType: hard +"@sinclair/typebox@npm:^0.34.0": + version: 0.34.38 + resolution: "@sinclair/typebox@npm:0.34.38" + checksum: c1b9a1547c64de01ff5c89351baf289d2d5f19cfef3ae30fe4748a103eb58d0842618318543cd3de964cb0370d5a859e24aba231ade9b43ee2ef4d0bb4db7084 + languageName: node + linkType: hard + "@sindresorhus/is@npm:^4.0.0": version: 4.6.0 resolution: "@sindresorhus/is@npm:4.6.0" @@ -9777,7 +12102,7 @@ __metadata: languageName: node linkType: hard -"@smithy/abort-controller@npm:^3.1.1, @smithy/abort-controller@npm:^3.1.6": +"@smithy/abort-controller@npm:^3.1.6": version: 3.1.9 resolution: "@smithy/abort-controller@npm:3.1.9" dependencies: @@ -9787,13 +12112,13 @@ __metadata: languageName: node linkType: hard -"@smithy/abort-controller@npm:^4.0.4": - version: 4.0.4 - resolution: "@smithy/abort-controller@npm:4.0.4" +"@smithy/abort-controller@npm:^4.0.4, @smithy/abort-controller@npm:^4.0.5": + version: 4.0.5 + resolution: "@smithy/abort-controller@npm:4.0.5" dependencies: - "@smithy/types": ^4.3.1 + "@smithy/types": ^4.3.2 tslib: ^2.6.2 - checksum: eb172b002fb92406c69b83460f949ace73247e6abd85d0d3714de2765c5db7b98070b9abfb630e2c591dd7b2ff770cc24f7737c1c207581f716c402b16bf46f9 + checksum: 0a16d5571f5aa3d6d43465ce1060263a92c6eba011cf448adaeafb940121981ecb26fabb0185745520cace9dfd9aebe6879930ff3b55c8f1b42ac6a337070f20 languageName: node linkType: hard @@ -9848,16 +12173,16 @@ __metadata: languageName: node linkType: hard -"@smithy/config-resolver@npm:^4.1.4": - version: 4.1.4 - resolution: "@smithy/config-resolver@npm:4.1.4" +"@smithy/config-resolver@npm:^4.1.4, @smithy/config-resolver@npm:^4.1.5": + version: 4.1.5 + resolution: "@smithy/config-resolver@npm:4.1.5" dependencies: - "@smithy/node-config-provider": ^4.1.3 - "@smithy/types": ^4.3.1 + "@smithy/node-config-provider": ^4.1.4 + "@smithy/types": ^4.3.2 "@smithy/util-config-provider": ^4.0.0 - "@smithy/util-middleware": ^4.0.4 + "@smithy/util-middleware": ^4.0.5 tslib: ^2.6.2 - checksum: 41832a42f8da7143732c71098b410f4ddcb096066126f7e8f45bae8d9aeb95681bd0d0d54886f46244c945c63829ca5d23373d4de31a038487aa07159722ef4e + checksum: f76f2365403411810a205763a6744eb84d4edfc6bedb87ba0d41b4b310b9c693f3cb17610f963f706b06e90c12864fe54617c9ff1f435fe3b94d825f2def2bfb languageName: node linkType: hard @@ -9877,20 +12202,22 @@ __metadata: languageName: node linkType: hard -"@smithy/core@npm:^3.5.3": - version: 3.5.3 - resolution: "@smithy/core@npm:3.5.3" +"@smithy/core@npm:^3.5.3, @smithy/core@npm:^3.7.0, @smithy/core@npm:^3.8.0": + version: 3.8.0 + resolution: "@smithy/core@npm:3.8.0" dependencies: - "@smithy/middleware-serde": ^4.0.8 - "@smithy/protocol-http": ^5.1.2 - "@smithy/types": ^4.3.1 + "@smithy/middleware-serde": ^4.0.9 + "@smithy/protocol-http": ^5.1.3 + "@smithy/types": ^4.3.2 "@smithy/util-base64": ^4.0.0 "@smithy/util-body-length-browser": ^4.0.0 - "@smithy/util-middleware": ^4.0.4 - "@smithy/util-stream": ^4.2.2 + "@smithy/util-middleware": ^4.0.5 + "@smithy/util-stream": ^4.2.4 "@smithy/util-utf8": ^4.0.0 + "@types/uuid": ^9.0.1 tslib: ^2.6.2 - checksum: ba4bce5c58a93467e52cb9362dbdc8c8aa120dfbc5333e911c8aadcbbcd236054126277eff9f970bfc24a918f44e929a4116e4533644811ad83f44c7abc81766 + uuid: ^9.0.1 + checksum: 0fe1c19b0a2f371ed04b47e51edac896ed24d868a3f78290ea8913e255fef7d023a9c0ba252f5af2b606bfadfdca7fbc545db01dcd0d2162c228d10b2eadc303 languageName: node linkType: hard @@ -9907,16 +12234,16 @@ __metadata: languageName: node linkType: hard -"@smithy/credential-provider-imds@npm:^4.0.6": - version: 4.0.6 - resolution: "@smithy/credential-provider-imds@npm:4.0.6" +"@smithy/credential-provider-imds@npm:^4.0.6, @smithy/credential-provider-imds@npm:^4.0.7": + version: 4.0.7 + resolution: "@smithy/credential-provider-imds@npm:4.0.7" dependencies: - "@smithy/node-config-provider": ^4.1.3 - "@smithy/property-provider": ^4.0.4 - "@smithy/types": ^4.3.1 - "@smithy/url-parser": ^4.0.4 + "@smithy/node-config-provider": ^4.1.4 + "@smithy/property-provider": ^4.0.5 + "@smithy/types": ^4.3.2 + "@smithy/url-parser": ^4.0.5 tslib: ^2.6.2 - checksum: b1f3157d0a7b9f9155ac80aeac70d7db896d23d0322a6b38f0e848f1e53864ba1bca6d3dc5dd9af86446c371ebc5bffe01f0712ad562e7635e7d13e532622aa4 + checksum: 862ac40520e2756918e8ecdf2259ec82f1b1556595b3b8d19d7c68390119c416fdd9c716c78773a2ccec21c32cb81f465e0474073a8a90808e171fbdcdcfbd81 languageName: node linkType: hard @@ -9932,15 +12259,15 @@ __metadata: languageName: node linkType: hard -"@smithy/eventstream-codec@npm:^4.0.4": - version: 4.0.4 - resolution: "@smithy/eventstream-codec@npm:4.0.4" +"@smithy/eventstream-codec@npm:^4.0.5": + version: 4.0.5 + resolution: "@smithy/eventstream-codec@npm:4.0.5" dependencies: "@aws-crypto/crc32": 5.2.0 - "@smithy/types": ^4.3.1 + "@smithy/types": ^4.3.2 "@smithy/util-hex-encoding": ^4.0.0 tslib: ^2.6.2 - checksum: 89b76826d4d3bf97317e3539ece105b9a03552144ad816a687b0b2cbca60e2b3513062c04b6cfacaffb270d616ffc8ac8bf549afc4aa676a6d7465df5a3215ba + checksum: d94928e22468cb6e6d09bdc8a6ee04f05947c141c0b040aa90e95b6edc123ba03a562ff3994b5827c57295981183325ed8e8f6c60448a4eec392227735e86d62 languageName: node linkType: hard @@ -9955,14 +12282,14 @@ __metadata: languageName: node linkType: hard -"@smithy/eventstream-serde-browser@npm:^4.0.4": - version: 4.0.4 - resolution: "@smithy/eventstream-serde-browser@npm:4.0.4" +"@smithy/eventstream-serde-browser@npm:^4.0.4, @smithy/eventstream-serde-browser@npm:^4.0.5": + version: 4.0.5 + resolution: "@smithy/eventstream-serde-browser@npm:4.0.5" dependencies: - "@smithy/eventstream-serde-universal": ^4.0.4 - "@smithy/types": ^4.3.1 + "@smithy/eventstream-serde-universal": ^4.0.5 + "@smithy/types": ^4.3.2 tslib: ^2.6.2 - checksum: b2444538555c54ac96d4049b0be3a65d959914bcd5198a8059edc838c7ffac5a1db225290194f85ea8805c47c1edc95484dfeb415cb2004ec3e572880f4fc8c5 + checksum: 352c6b73482d844f8184d6e6ffd28b1f69b376b59a3246a0ab967c990c15b11507323ff6362b27478062865e131739b27bafa7c6dedfa4c52ac8d795ce0cf8f5 languageName: node linkType: hard @@ -9976,13 +12303,13 @@ __metadata: languageName: node linkType: hard -"@smithy/eventstream-serde-config-resolver@npm:^4.1.2": - version: 4.1.2 - resolution: "@smithy/eventstream-serde-config-resolver@npm:4.1.2" +"@smithy/eventstream-serde-config-resolver@npm:^4.1.2, @smithy/eventstream-serde-config-resolver@npm:^4.1.3": + version: 4.1.3 + resolution: "@smithy/eventstream-serde-config-resolver@npm:4.1.3" dependencies: - "@smithy/types": ^4.3.1 + "@smithy/types": ^4.3.2 tslib: ^2.6.2 - checksum: 54184a29d1e42f1b972292efc3a5cbbe3ca237cd9ab76132bad40e8426fa62d0b7f6fdac01f23e3a9cac69919107ddfd9d2f2873f83ae1f65470d3052c67cefc + checksum: bfe98977649bcbfbe93cdbfb118c363759da6910ca8fa462870427dbc6f1f2f835103831147eee7eef27ace1bc58b65a40969c4630cf76be1eedfcd36996fd28 languageName: node linkType: hard @@ -9997,14 +12324,14 @@ __metadata: languageName: node linkType: hard -"@smithy/eventstream-serde-node@npm:^4.0.4": - version: 4.0.4 - resolution: "@smithy/eventstream-serde-node@npm:4.0.4" +"@smithy/eventstream-serde-node@npm:^4.0.4, @smithy/eventstream-serde-node@npm:^4.0.5": + version: 4.0.5 + resolution: "@smithy/eventstream-serde-node@npm:4.0.5" dependencies: - "@smithy/eventstream-serde-universal": ^4.0.4 - "@smithy/types": ^4.3.1 + "@smithy/eventstream-serde-universal": ^4.0.5 + "@smithy/types": ^4.3.2 tslib: ^2.6.2 - checksum: e6d0765a73332c79b69531ed20c27e49475173da09ce21e4c011a64d8a61d7c5c328c9bc1cab991e145fc969b16071ffd6a33ab11291c0fa2a46e8dae28da23b + checksum: 21c389202d2db2bcff23166b220ff3ba6c178f2b9eff1918317cca24c49cb6e0a53ab7f43fb8039f776f63ffd3a2bf69c909dafa5199d5d4278797cb121d3daa languageName: node linkType: hard @@ -10019,14 +12346,14 @@ __metadata: languageName: node linkType: hard -"@smithy/eventstream-serde-universal@npm:^4.0.4": - version: 4.0.4 - resolution: "@smithy/eventstream-serde-universal@npm:4.0.4" +"@smithy/eventstream-serde-universal@npm:^4.0.5": + version: 4.0.5 + resolution: "@smithy/eventstream-serde-universal@npm:4.0.5" dependencies: - "@smithy/eventstream-codec": ^4.0.4 - "@smithy/types": ^4.3.1 + "@smithy/eventstream-codec": ^4.0.5 + "@smithy/types": ^4.3.2 tslib: ^2.6.2 - checksum: f0c18efa6cafa111ed20c8c53b4a7b6a0f8e25ccb0d2cafdf83282ebc6f96e47f26daf24b5b810ea83a02e03c994c35419d94fad76871f2cc6cb01d2aed277d9 + checksum: 183c6d4895395bbea34d12d3a87e9c69cc598c19e0cf51ba0036277ab520230fdc4b27026d1e438304b3ac4624a4408df15fc037f5c6a96bec0c71c400711170 languageName: node linkType: hard @@ -10056,16 +12383,16 @@ __metadata: languageName: node linkType: hard -"@smithy/fetch-http-handler@npm:^5.0.4": - version: 5.0.4 - resolution: "@smithy/fetch-http-handler@npm:5.0.4" +"@smithy/fetch-http-handler@npm:^5.0.4, @smithy/fetch-http-handler@npm:^5.1.0, @smithy/fetch-http-handler@npm:^5.1.1": + version: 5.1.1 + resolution: "@smithy/fetch-http-handler@npm:5.1.1" dependencies: - "@smithy/protocol-http": ^5.1.2 - "@smithy/querystring-builder": ^4.0.4 - "@smithy/types": ^4.3.1 + "@smithy/protocol-http": ^5.1.3 + "@smithy/querystring-builder": ^4.0.5 + "@smithy/types": ^4.3.2 "@smithy/util-base64": ^4.0.0 tslib: ^2.6.2 - checksum: ce57acfcd40a6ff3965c5f14b432c5ab87f0b0766766960224d4af79af85e37d61da2db6dc5cfa16bf4b8f2d8966a2838d2ee6eef8d5cd5a837aacbc01517851 + checksum: c07f5cad58d5da7cd0de95e2d600e8dee8cda54bba65e7327c5beb25d2aa3eb815d228944bf20860de8927068d3d80baa28f71ecee0a1a3e131307774f53813b languageName: node linkType: hard @@ -10081,15 +12408,15 @@ __metadata: languageName: node linkType: hard -"@smithy/hash-blob-browser@npm:^4.0.4": - version: 4.0.4 - resolution: "@smithy/hash-blob-browser@npm:4.0.4" +"@smithy/hash-blob-browser@npm:^4.0.5": + version: 4.0.5 + resolution: "@smithy/hash-blob-browser@npm:4.0.5" dependencies: "@smithy/chunked-blob-reader": ^5.0.0 "@smithy/chunked-blob-reader-native": ^4.0.0 - "@smithy/types": ^4.3.1 + "@smithy/types": ^4.3.2 tslib: ^2.6.2 - checksum: f970058c2e04e86427e1474355199027fc84dc1d96d9a2278ed37904458d37b020472541390558bd3fb071bbd177b2850b18ceb1beb39d387fead06a2912f974 + checksum: d44a12847dabe3ba33b52306ef8f0f61ed22943d12a1fe2d669a5425d9e7b97fc1fd52b78bd362ba56a24c8f0723630e579f2de5362cddc9c8060854150545d9 languageName: node linkType: hard @@ -10105,15 +12432,15 @@ __metadata: languageName: node linkType: hard -"@smithy/hash-node@npm:^4.0.4": - version: 4.0.4 - resolution: "@smithy/hash-node@npm:4.0.4" +"@smithy/hash-node@npm:^4.0.4, @smithy/hash-node@npm:^4.0.5": + version: 4.0.5 + resolution: "@smithy/hash-node@npm:4.0.5" dependencies: - "@smithy/types": ^4.3.1 + "@smithy/types": ^4.3.2 "@smithy/util-buffer-from": ^4.0.0 "@smithy/util-utf8": ^4.0.0 tslib: ^2.6.2 - checksum: 07beb38643990f6c055457765d65af2aedd5944d819025df90d1f2f59596d1a1394cd8c9035ac6d343bc55e3afeb186b51b0ac91938024da8687120fc0b436dc + checksum: 6c5aeba12b651d74fa05e03b7019d48193b0fac4995ad84fe313961c4e51d16cdbe46f529a3fe435a061fbe7eebee0620def92f9821add28e466152fd3270560 languageName: node linkType: hard @@ -10128,14 +12455,14 @@ __metadata: languageName: node linkType: hard -"@smithy/hash-stream-node@npm:^4.0.4": - version: 4.0.4 - resolution: "@smithy/hash-stream-node@npm:4.0.4" +"@smithy/hash-stream-node@npm:^4.0.5": + version: 4.0.5 + resolution: "@smithy/hash-stream-node@npm:4.0.5" dependencies: - "@smithy/types": ^4.3.1 + "@smithy/types": ^4.3.2 "@smithy/util-utf8": ^4.0.0 tslib: ^2.6.2 - checksum: 4899132433f520e45972bbacb6a999da8d7ccf4c813f2fb28b1af65eaf268ba549b2c37dd54a586cd7bcd82f6e4cec914651a6446b3fb3e1f226ca1864051535 + checksum: 19c7ce086eb86c3a8660a587c70278ab8e201aed10157e6c2f1a7d8071967429bf70b3cd6e81fb29042b3c3ffcf40866bc94ada34b86a1688dc28527884f10de languageName: node linkType: hard @@ -10149,13 +12476,13 @@ __metadata: languageName: node linkType: hard -"@smithy/invalid-dependency@npm:^4.0.4": - version: 4.0.4 - resolution: "@smithy/invalid-dependency@npm:4.0.4" +"@smithy/invalid-dependency@npm:^4.0.4, @smithy/invalid-dependency@npm:^4.0.5": + version: 4.0.5 + resolution: "@smithy/invalid-dependency@npm:4.0.5" dependencies: - "@smithy/types": ^4.3.1 + "@smithy/types": ^4.3.2 tslib: ^2.6.2 - checksum: 5e5a6282c17a7310f8e866c7e34fa07479d42c650cf3c1875bdb0ec38d5280eeac82a269605a3521b8fa455b92673d8fd5e97eb997acf81a80da82d6f501d651 + checksum: 8cc2a14dc47ac5513641747297e6e7e79dceb687e962e1520949db94597a5ce057f9f92657530b6660df100ef1fcff04cd5d9638847c8ada7f7b431a73f34fd2 languageName: node linkType: hard @@ -10197,14 +12524,14 @@ __metadata: languageName: node linkType: hard -"@smithy/md5-js@npm:^4.0.4": - version: 4.0.4 - resolution: "@smithy/md5-js@npm:4.0.4" +"@smithy/md5-js@npm:^4.0.5": + version: 4.0.5 + resolution: "@smithy/md5-js@npm:4.0.5" dependencies: - "@smithy/types": ^4.3.1 + "@smithy/types": ^4.3.2 "@smithy/util-utf8": ^4.0.0 tslib: ^2.6.2 - checksum: 7c66405dca5d7df6694367dbb4a3d9f13fdfe2589abc81f85d5fb7bf876e1382578d9c477d2256d4b5bc59951c3c534e51eb65c53c2fb3251080f16d1d7ea82c + checksum: 30f015e5846963189aef4204029506c145e7c6106983bf0cb0ace88fcfc90c8a97094d65b73a79a86406bfcf70932bab9b7a6074fcac481afd0e078955bc95eb languageName: node linkType: hard @@ -10219,14 +12546,14 @@ __metadata: languageName: node linkType: hard -"@smithy/middleware-content-length@npm:^4.0.4": - version: 4.0.4 - resolution: "@smithy/middleware-content-length@npm:4.0.4" +"@smithy/middleware-content-length@npm:^4.0.4, @smithy/middleware-content-length@npm:^4.0.5": + version: 4.0.5 + resolution: "@smithy/middleware-content-length@npm:4.0.5" dependencies: - "@smithy/protocol-http": ^5.1.2 - "@smithy/types": ^4.3.1 + "@smithy/protocol-http": ^5.1.3 + "@smithy/types": ^4.3.2 tslib: ^2.6.2 - checksum: fde43ff13f0830c4608b83cf6e2bd3ae142aa6eb3df6f6c190c2564dd00c2c98f4f95da9146c69bc09115ad87ffc9dc24935d1a3d6d3b2383a9c8558d9177dd6 + checksum: 2bbe3afc2d29bf4153afb52adb2cadc063e745c2e1f3c630ff10bb97ce4fa8ae7e6872082ec1407b638d0c7cb896ebcc27ca190f9aa78635a8e41a2440fe680a languageName: node linkType: hard @@ -10246,19 +12573,19 @@ __metadata: languageName: node linkType: hard -"@smithy/middleware-endpoint@npm:^4.1.11": - version: 4.1.11 - resolution: "@smithy/middleware-endpoint@npm:4.1.11" +"@smithy/middleware-endpoint@npm:^4.1.11, @smithy/middleware-endpoint@npm:^4.1.14, @smithy/middleware-endpoint@npm:^4.1.15, @smithy/middleware-endpoint@npm:^4.1.18": + version: 4.1.18 + resolution: "@smithy/middleware-endpoint@npm:4.1.18" dependencies: - "@smithy/core": ^3.5.3 - "@smithy/middleware-serde": ^4.0.8 - "@smithy/node-config-provider": ^4.1.3 - "@smithy/shared-ini-file-loader": ^4.0.4 - "@smithy/types": ^4.3.1 - "@smithy/url-parser": ^4.0.4 - "@smithy/util-middleware": ^4.0.4 + "@smithy/core": ^3.8.0 + "@smithy/middleware-serde": ^4.0.9 + "@smithy/node-config-provider": ^4.1.4 + "@smithy/shared-ini-file-loader": ^4.0.5 + "@smithy/types": ^4.3.2 + "@smithy/url-parser": ^4.0.5 + "@smithy/util-middleware": ^4.0.5 tslib: ^2.6.2 - checksum: 28420a3b8b42655e29a005d2de14348082fd472c008bee2d135aa0907772678961bf74a631dc583e136f4819936aa495c80fbcca5079cadfd1800bb6ab391110 + checksum: 22a6e05e427c9899041facefea8bdf8dad393bdb3ccd7ca795fb705e85ee8b9e48c6000e947bb6a8a1cfe48d1f1f1b9f894f0b588e87ce1ea5b187d041bcd6fe languageName: node linkType: hard @@ -10279,20 +12606,21 @@ __metadata: languageName: node linkType: hard -"@smithy/middleware-retry@npm:^4.1.12": - version: 4.1.12 - resolution: "@smithy/middleware-retry@npm:4.1.12" +"@smithy/middleware-retry@npm:^4.1.12, @smithy/middleware-retry@npm:^4.1.15, @smithy/middleware-retry@npm:^4.1.16, @smithy/middleware-retry@npm:^4.1.19": + version: 4.1.19 + resolution: "@smithy/middleware-retry@npm:4.1.19" dependencies: - "@smithy/node-config-provider": ^4.1.3 - "@smithy/protocol-http": ^5.1.2 - "@smithy/service-error-classification": ^4.0.5 - "@smithy/smithy-client": ^4.4.3 - "@smithy/types": ^4.3.1 - "@smithy/util-middleware": ^4.0.4 - "@smithy/util-retry": ^4.0.5 + "@smithy/node-config-provider": ^4.1.4 + "@smithy/protocol-http": ^5.1.3 + "@smithy/service-error-classification": ^4.0.7 + "@smithy/smithy-client": ^4.4.10 + "@smithy/types": ^4.3.2 + "@smithy/util-middleware": ^4.0.5 + "@smithy/util-retry": ^4.0.7 + "@types/uuid": ^9.0.1 tslib: ^2.6.2 uuid: ^9.0.1 - checksum: 7cd2ee73003423d0857a458db64ce0d9d484c8f4b669a8b33c866ee4fdbbc199e85a53f729a76d7f0874e771fb7f9b95ad151af443573588e15e9ecac1f38fbe + checksum: 6595d27404491ee3befc69ffe8ce576f26b409385d6958597c8d889fff7aff26973a54eab605348299c24760912d9606f7efe84e3adf72ab146b114096592bec languageName: node linkType: hard @@ -10306,14 +12634,14 @@ __metadata: languageName: node linkType: hard -"@smithy/middleware-serde@npm:^4.0.8": - version: 4.0.8 - resolution: "@smithy/middleware-serde@npm:4.0.8" +"@smithy/middleware-serde@npm:^4.0.8, @smithy/middleware-serde@npm:^4.0.9": + version: 4.0.9 + resolution: "@smithy/middleware-serde@npm:4.0.9" dependencies: - "@smithy/protocol-http": ^5.1.2 - "@smithy/types": ^4.3.1 + "@smithy/protocol-http": ^5.1.3 + "@smithy/types": ^4.3.2 tslib: ^2.6.2 - checksum: 11414e584780716b2b0487fe748da9927943d4d810b5b0161e73df6ab24a4d17f675773287f95868c57a71013385f7b027eb2afbab1eed3dbaafef754b482b27 + checksum: 71dc9d920d36a3f65cc883718e8c74687a7c8074a148ab1a035e395e43c6566a3514f10b4c15a13b98194ecd1d81816932c9df8dfa5955cd347c6049893defc4 languageName: node linkType: hard @@ -10327,13 +12655,13 @@ __metadata: languageName: node linkType: hard -"@smithy/middleware-stack@npm:^4.0.4": - version: 4.0.4 - resolution: "@smithy/middleware-stack@npm:4.0.4" +"@smithy/middleware-stack@npm:^4.0.4, @smithy/middleware-stack@npm:^4.0.5": + version: 4.0.5 + resolution: "@smithy/middleware-stack@npm:4.0.5" dependencies: - "@smithy/types": ^4.3.1 + "@smithy/types": ^4.3.2 tslib: ^2.6.2 - checksum: b29b6430e31f11683f0ce0e06d21a4bfe6cb791ce1eb5686533559baa81698f617bfbfdac06f569e13f077ce177cb70e55f4db20701906b3e344d9294817f382 + checksum: 2ebe346b8b868d11bf9e5028a225ad1312f7862231ae01c289059291b984127a7c18e17f1fa4d803de09f77441d839bc5e25f8ec9bed10a9a320d0393bc55930 languageName: node linkType: hard @@ -10349,15 +12677,15 @@ __metadata: languageName: node linkType: hard -"@smithy/node-config-provider@npm:^4.1.3": - version: 4.1.3 - resolution: "@smithy/node-config-provider@npm:4.1.3" +"@smithy/node-config-provider@npm:^4.1.3, @smithy/node-config-provider@npm:^4.1.4": + version: 4.1.4 + resolution: "@smithy/node-config-provider@npm:4.1.4" dependencies: - "@smithy/property-provider": ^4.0.4 - "@smithy/shared-ini-file-loader": ^4.0.4 - "@smithy/types": ^4.3.1 + "@smithy/property-provider": ^4.0.5 + "@smithy/shared-ini-file-loader": ^4.0.5 + "@smithy/types": ^4.3.2 tslib: ^2.6.2 - checksum: bea20b3f92290fbefa32d30c4ac7632f94d4e89b5432dfe5a2d0c6261bfd90e882d62dd02e0a4e65f3bc89f815b19e44d7bb103a78b6c77941cc186450ad79f1 + checksum: 950f9e234b8ffb680d2f5b35bc7ff21f73623caf0612d59daba1991da79126ec33e1afd2f6408534b7910474665ab150bd9d341aa46950bf5903665e71c7da6f languageName: node linkType: hard @@ -10374,16 +12702,16 @@ __metadata: languageName: node linkType: hard -"@smithy/node-http-handler@npm:^4.0.6": - version: 4.0.6 - resolution: "@smithy/node-http-handler@npm:4.0.6" +"@smithy/node-http-handler@npm:^4.0.6, @smithy/node-http-handler@npm:^4.1.0, @smithy/node-http-handler@npm:^4.1.1": + version: 4.1.1 + resolution: "@smithy/node-http-handler@npm:4.1.1" dependencies: - "@smithy/abort-controller": ^4.0.4 - "@smithy/protocol-http": ^5.1.2 - "@smithy/querystring-builder": ^4.0.4 - "@smithy/types": ^4.3.1 + "@smithy/abort-controller": ^4.0.5 + "@smithy/protocol-http": ^5.1.3 + "@smithy/querystring-builder": ^4.0.5 + "@smithy/types": ^4.3.2 tslib: ^2.6.2 - checksum: bde23701b6166b76958cbc194d551a139e3dcc1d05a6c7de3d5b14f54934ca5a49a28d13d8ec4b012716aae816cd0c8c4735c959d5ef697a7a1932fbcfc5d7f2 + checksum: a61a841bc6e69c62a983031e8b3faf1ab82abaf0ccd1eb5d3e02e3d99a8be020fa8dff0b2b1f81468db43e0e7be2407785b89e9c6c04035b8b4afde08bed3a98 languageName: node linkType: hard @@ -10397,13 +12725,13 @@ __metadata: languageName: node linkType: hard -"@smithy/property-provider@npm:^4.0.4": - version: 4.0.4 - resolution: "@smithy/property-provider@npm:4.0.4" +"@smithy/property-provider@npm:^4.0.4, @smithy/property-provider@npm:^4.0.5": + version: 4.0.5 + resolution: "@smithy/property-provider@npm:4.0.5" dependencies: - "@smithy/types": ^4.3.1 + "@smithy/types": ^4.3.2 tslib: ^2.6.2 - checksum: c370efbb43ab01fb6050fbf4c231bbe2fb7d660256adeee40c0c4c14b7af1b9b75c36f6924aeacdd2885fad1aaf0655047cafe5f0d22f5e371cbd25ff2f04b27 + checksum: 67b828f4ddfb90a90e8a919328bb3c842612115d84d949087988fd8558cd143ec8f7dc437936ef41f9427a7ea2a6ec6a726c5f92a9c12e8c7bef831c4b4f16f0 languageName: node linkType: hard @@ -10417,13 +12745,13 @@ __metadata: languageName: node linkType: hard -"@smithy/protocol-http@npm:^5.1.2": - version: 5.1.2 - resolution: "@smithy/protocol-http@npm:5.1.2" +"@smithy/protocol-http@npm:^5.1.2, @smithy/protocol-http@npm:^5.1.3": + version: 5.1.3 + resolution: "@smithy/protocol-http@npm:5.1.3" dependencies: - "@smithy/types": ^4.3.1 + "@smithy/types": ^4.3.2 tslib: ^2.6.2 - checksum: 50fb026efa321e65a77f9747312eeb428ff2196095c15ed5937efe807a4734c47746759ccf2dbc84a45719effcbc81221662289be6d4d5ec122afb0e3cd66fd9 + checksum: 5adc1e69b9e2d7c90acfe1a9b731c4f233173e035eb9e8e3dd5fabf63d9a765aff54912a0e94f4f4bff494f4caa9ec40bd53cdc1a94028f561ab5c9649f2790f languageName: node linkType: hard @@ -10438,14 +12766,14 @@ __metadata: languageName: node linkType: hard -"@smithy/querystring-builder@npm:^4.0.4": - version: 4.0.4 - resolution: "@smithy/querystring-builder@npm:4.0.4" +"@smithy/querystring-builder@npm:^4.0.4, @smithy/querystring-builder@npm:^4.0.5": + version: 4.0.5 + resolution: "@smithy/querystring-builder@npm:4.0.5" dependencies: - "@smithy/types": ^4.3.1 + "@smithy/types": ^4.3.2 "@smithy/util-uri-escape": ^4.0.0 tslib: ^2.6.2 - checksum: 30ec0301fbc2212101391841000a3117ab6c3ae2b6b2a1db230cc1dfcf97738f527b23f859f0a5e843f2a983793b58cdcd21a0ce11ef93fcdf5d8a1ee0d70fbc + checksum: 649a046a14f25d5febba341dedd577c9fce80aa86970dc2af0b0289a2b6326731c19ddefcae172a0162a4a73306ad823533528751a0067c910efce3cabe06675 languageName: node linkType: hard @@ -10459,13 +12787,13 @@ __metadata: languageName: node linkType: hard -"@smithy/querystring-parser@npm:^4.0.4": - version: 4.0.4 - resolution: "@smithy/querystring-parser@npm:4.0.4" +"@smithy/querystring-parser@npm:^4.0.5": + version: 4.0.5 + resolution: "@smithy/querystring-parser@npm:4.0.5" dependencies: - "@smithy/types": ^4.3.1 + "@smithy/types": ^4.3.2 tslib: ^2.6.2 - checksum: 36bc93732a1628be5dd53748f6f36237bad26de2da810195213541dd35b20eee0b0264160a0de734b9333ca747e0229253d6729d1a8ddc26d176c0b1cce309e0 + checksum: e12a2e19137bc95487c51652dd20f6cd0199854986019e5461f14f797fda3cda3ec4786ff45af853aa64ab75a2a91d18f3f8320276f7e407016f80e33604564d languageName: node linkType: hard @@ -10478,12 +12806,12 @@ __metadata: languageName: node linkType: hard -"@smithy/service-error-classification@npm:^4.0.5": - version: 4.0.5 - resolution: "@smithy/service-error-classification@npm:4.0.5" +"@smithy/service-error-classification@npm:^4.0.7": + version: 4.0.7 + resolution: "@smithy/service-error-classification@npm:4.0.7" dependencies: - "@smithy/types": ^4.3.1 - checksum: 9ca6a876a403fa15151d955ef43c7b4e8c3a93b334d493ab7086d095bcd8670b848779bb82be66b2a14423edf169f1be514ec381f71d2d78f0612731416911ac + "@smithy/types": ^4.3.2 + checksum: fe44ce36c8759c74a63adc52c47b638ee0a34ea32752d9c5923c370f0497a412ced51d8b83e444303d8d9d544d30d3d16fecb39c9f5cda8622b293704ce999a2 languageName: node linkType: hard @@ -10497,13 +12825,13 @@ __metadata: languageName: node linkType: hard -"@smithy/shared-ini-file-loader@npm:^4.0.4": - version: 4.0.4 - resolution: "@smithy/shared-ini-file-loader@npm:4.0.4" +"@smithy/shared-ini-file-loader@npm:^4.0.4, @smithy/shared-ini-file-loader@npm:^4.0.5": + version: 4.0.5 + resolution: "@smithy/shared-ini-file-loader@npm:4.0.5" dependencies: - "@smithy/types": ^4.3.1 + "@smithy/types": ^4.3.2 tslib: ^2.6.2 - checksum: a3ecabadda13ff6fca99585e7e0086a04c4d2350b8c783b3a23493c2ae0a599f397d3cb80a7e171b7123889340995cada866d320726fa6a03f3063d60d5d0207 + checksum: 9fafb7d4cf10398cf07a2ad7b619b05f136a2a774b1d104eb43b1862f1297d1f88f7e6d72198df43bef35cdf5938b8b5bcf0e896a8bb406474920d0f653a0a4b languageName: node linkType: hard @@ -10523,19 +12851,19 @@ __metadata: languageName: node linkType: hard -"@smithy/signature-v4@npm:^5.1.2": - version: 5.1.2 - resolution: "@smithy/signature-v4@npm:5.1.2" +"@smithy/signature-v4@npm:^5.1.2, @smithy/signature-v4@npm:^5.1.3": + version: 5.1.3 + resolution: "@smithy/signature-v4@npm:5.1.3" dependencies: "@smithy/is-array-buffer": ^4.0.0 - "@smithy/protocol-http": ^5.1.2 - "@smithy/types": ^4.3.1 + "@smithy/protocol-http": ^5.1.3 + "@smithy/types": ^4.3.2 "@smithy/util-hex-encoding": ^4.0.0 - "@smithy/util-middleware": ^4.0.4 + "@smithy/util-middleware": ^4.0.5 "@smithy/util-uri-escape": ^4.0.0 "@smithy/util-utf8": ^4.0.0 tslib: ^2.6.2 - checksum: 83d3870668a6c080c1d0cbecf2e7d1a86c0298cc3a3df9fba21bd942e2a9bcae81eb50960c66bba00c6f9820ef9e5ab3e5ddba67b2d7914a09a82c7887621c0c + checksum: 122a918ee070215e5cea8040605d905143a724b5bb0e5c904085f7a7a4b3d87701e5674b39cc8c9e13639b077994739edcdf33c3884172f363bcf68071c2abc7 languageName: node linkType: hard @@ -10554,18 +12882,18 @@ __metadata: languageName: node linkType: hard -"@smithy/smithy-client@npm:^4.4.3": - version: 4.4.3 - resolution: "@smithy/smithy-client@npm:4.4.3" +"@smithy/smithy-client@npm:^4.4.10, @smithy/smithy-client@npm:^4.4.3, @smithy/smithy-client@npm:^4.4.6, @smithy/smithy-client@npm:^4.4.7": + version: 4.4.10 + resolution: "@smithy/smithy-client@npm:4.4.10" dependencies: - "@smithy/core": ^3.5.3 - "@smithy/middleware-endpoint": ^4.1.11 - "@smithy/middleware-stack": ^4.0.4 - "@smithy/protocol-http": ^5.1.2 - "@smithy/types": ^4.3.1 - "@smithy/util-stream": ^4.2.2 + "@smithy/core": ^3.8.0 + "@smithy/middleware-endpoint": ^4.1.18 + "@smithy/middleware-stack": ^4.0.5 + "@smithy/protocol-http": ^5.1.3 + "@smithy/types": ^4.3.2 + "@smithy/util-stream": ^4.2.4 tslib: ^2.6.2 - checksum: 37f69c4af3883525cebe4ea648b05cd93de01742d7206e7613e65ec15f3bd06874b0d923a6ccfbdc3b7e01a2cb6dc6c961f7590f984eea4e55c68871dfb3b11a + checksum: 994743c7a04e3e1b5136c3be98c3882ab9169d39143530c11553062934887b6b9b7d32de035a15f7ff0f7e6b5db6106ab3e71dc62beb473da9313ff6b8b24a37 languageName: node linkType: hard @@ -10578,12 +12906,12 @@ __metadata: languageName: node linkType: hard -"@smithy/types@npm:^4.3.1": - version: 4.3.1 - resolution: "@smithy/types@npm:4.3.1" +"@smithy/types@npm:^4.3.1, @smithy/types@npm:^4.3.2": + version: 4.3.2 + resolution: "@smithy/types@npm:4.3.2" dependencies: tslib: ^2.6.2 - checksum: 8b350562b9ed4ff97465025b4ae77a34bb07b9d47fb6f9781755aac9401b0355a63c2fef307393e2dae3fa0277149dd7d83f5bc2a63d4ad3519ea32fd56b5cda + checksum: 120c5d38f6362c86e6493cce3b9ca9902cd986dab773b39664ff6a95b787c45481f1b1d230f45a6f5ad0c045fb690dc96b51b9ca7b5e9487714a652ed98231f6 languageName: node linkType: hard @@ -10598,14 +12926,14 @@ __metadata: languageName: node linkType: hard -"@smithy/url-parser@npm:^4.0.4": - version: 4.0.4 - resolution: "@smithy/url-parser@npm:4.0.4" +"@smithy/url-parser@npm:^4.0.4, @smithy/url-parser@npm:^4.0.5": + version: 4.0.5 + resolution: "@smithy/url-parser@npm:4.0.5" dependencies: - "@smithy/querystring-parser": ^4.0.4 - "@smithy/types": ^4.3.1 + "@smithy/querystring-parser": ^4.0.5 + "@smithy/types": ^4.3.2 tslib: ^2.6.2 - checksum: 5f4649d9ff618c683e339fa826b1d722419bf8e20d72726fc5fe3cd479ec8c161d4b09b6e24e49b0143a6fb4f9a950d35410db1834e143c28e377b9c529a3657 + checksum: 19cb3c8a80a7a42936d47011e5991cee6d548f233cde2bf36ccb6c547d075bbc30e3be67e92f60aaf17c4f3875766be319a3da8399af40767a77b04aea3d9ee5 languageName: node linkType: hard @@ -10728,16 +13056,16 @@ __metadata: languageName: node linkType: hard -"@smithy/util-defaults-mode-browser@npm:^4.0.19": - version: 4.0.19 - resolution: "@smithy/util-defaults-mode-browser@npm:4.0.19" +"@smithy/util-defaults-mode-browser@npm:^4.0.19, @smithy/util-defaults-mode-browser@npm:^4.0.22, @smithy/util-defaults-mode-browser@npm:^4.0.23, @smithy/util-defaults-mode-browser@npm:^4.0.26": + version: 4.0.26 + resolution: "@smithy/util-defaults-mode-browser@npm:4.0.26" dependencies: - "@smithy/property-provider": ^4.0.4 - "@smithy/smithy-client": ^4.4.3 - "@smithy/types": ^4.3.1 + "@smithy/property-provider": ^4.0.5 + "@smithy/smithy-client": ^4.4.10 + "@smithy/types": ^4.3.2 bowser: ^2.11.0 tslib: ^2.6.2 - checksum: 05998cf1481f1bc2467f2fba571faa9ebcaeb1cf58d5c411a1096320068a9467b100ee2491eb1d56458d56d723a0b28711a975fb186df60bf3165d2d8aa6a678 + checksum: ba10af21bd302f4705a808673eb3811e36a78c396f7ee93e2dfea5ded7d78470c789d3bc7a23e3d6232b43b7b91f57fbfbd383d11042e6993dc9c49030cbd0ef languageName: node linkType: hard @@ -10756,18 +13084,18 @@ __metadata: languageName: node linkType: hard -"@smithy/util-defaults-mode-node@npm:^4.0.19": - version: 4.0.19 - resolution: "@smithy/util-defaults-mode-node@npm:4.0.19" +"@smithy/util-defaults-mode-node@npm:^4.0.19, @smithy/util-defaults-mode-node@npm:^4.0.22, @smithy/util-defaults-mode-node@npm:^4.0.23, @smithy/util-defaults-mode-node@npm:^4.0.26": + version: 4.0.26 + resolution: "@smithy/util-defaults-mode-node@npm:4.0.26" dependencies: - "@smithy/config-resolver": ^4.1.4 - "@smithy/credential-provider-imds": ^4.0.6 - "@smithy/node-config-provider": ^4.1.3 - "@smithy/property-provider": ^4.0.4 - "@smithy/smithy-client": ^4.4.3 - "@smithy/types": ^4.3.1 + "@smithy/config-resolver": ^4.1.5 + "@smithy/credential-provider-imds": ^4.0.7 + "@smithy/node-config-provider": ^4.1.4 + "@smithy/property-provider": ^4.0.5 + "@smithy/smithy-client": ^4.4.10 + "@smithy/types": ^4.3.2 tslib: ^2.6.2 - checksum: e12adbad9efa9f5604beb356d7b84de62df47cea6535e9835987a764c28602e341ea4909cd08daef6c0627bbcb921725bca524664ac00eb78ac27efbd0e924dd + checksum: 0a682393db1617681fc132c39d9f01accd5c3c250be457ebb514001d83d34252d404fe6315ee0cc5176e0efc7fdeec64e848299bdefe6113d3c70f81717b665b languageName: node linkType: hard @@ -10782,14 +13110,14 @@ __metadata: languageName: node linkType: hard -"@smithy/util-endpoints@npm:^3.0.6": - version: 3.0.6 - resolution: "@smithy/util-endpoints@npm:3.0.6" +"@smithy/util-endpoints@npm:^3.0.6, @smithy/util-endpoints@npm:^3.0.7": + version: 3.0.7 + resolution: "@smithy/util-endpoints@npm:3.0.7" dependencies: - "@smithy/node-config-provider": ^4.1.3 - "@smithy/types": ^4.3.1 + "@smithy/node-config-provider": ^4.1.4 + "@smithy/types": ^4.3.2 tslib: ^2.6.2 - checksum: d7d583c73a0c1ce38188569616cd4d7c95c36c0393516117043962b932f8c743e8cd672d2edd23ea8a9da0e30b84ee0f0ced0709cc8024b70ea8e5f17f505811 + checksum: 7024005a8a4f77ebae52d1dce538d76db3567c6fb22b06ba601dba4d4d8668cb4dbadd229015d02bb6bdb1a5aaa6b2d1c826cfcf412257ceb9dfe52c7ab95fca languageName: node linkType: hard @@ -10821,13 +13149,13 @@ __metadata: languageName: node linkType: hard -"@smithy/util-middleware@npm:^4.0.4": - version: 4.0.4 - resolution: "@smithy/util-middleware@npm:4.0.4" +"@smithy/util-middleware@npm:^4.0.4, @smithy/util-middleware@npm:^4.0.5": + version: 4.0.5 + resolution: "@smithy/util-middleware@npm:4.0.5" dependencies: - "@smithy/types": ^4.3.1 + "@smithy/types": ^4.3.2 tslib: ^2.6.2 - checksum: 39530add63ec13dac555846c30e98128316136f7f57bfd8fe876a8c15a7677cb64d0a33fd1f08b671096d769ab3f025d4d8c785a9d7a7cdf42fd0188236b0f32 + checksum: 74d9bdbcea4c4aa5304197417c370346b230b7a89893ba0dee0d9771b6ead2628a53fb8a64a3822bf1a30a176ebba2c16ece7003c21880a7ff54be0955356606 languageName: node linkType: hard @@ -10842,14 +13170,14 @@ __metadata: languageName: node linkType: hard -"@smithy/util-retry@npm:^4.0.5": - version: 4.0.5 - resolution: "@smithy/util-retry@npm:4.0.5" +"@smithy/util-retry@npm:^4.0.5, @smithy/util-retry@npm:^4.0.6, @smithy/util-retry@npm:^4.0.7": + version: 4.0.7 + resolution: "@smithy/util-retry@npm:4.0.7" dependencies: - "@smithy/service-error-classification": ^4.0.5 - "@smithy/types": ^4.3.1 + "@smithy/service-error-classification": ^4.0.7 + "@smithy/types": ^4.3.2 tslib: ^2.6.2 - checksum: 8e6c136f79c503c02e28b31bc43fce7a37282143c20aee13d2a7421b0502d5c478f2eb3cf3c3455739ed9e441e572e2725bf39339aa08ed53825129123dcfff0 + checksum: 09c633f59ac51203d917548ceb4caf7678e24c87eea024e97e8d62a918be4a76a1c517622b7e9841cf0e9f50778d6787f62efe6c25ae514ed7068e2323303c72 languageName: node linkType: hard @@ -10869,19 +13197,19 @@ __metadata: languageName: node linkType: hard -"@smithy/util-stream@npm:^4.2.2": - version: 4.2.2 - resolution: "@smithy/util-stream@npm:4.2.2" +"@smithy/util-stream@npm:^4.2.2, @smithy/util-stream@npm:^4.2.3, @smithy/util-stream@npm:^4.2.4": + version: 4.2.4 + resolution: "@smithy/util-stream@npm:4.2.4" dependencies: - "@smithy/fetch-http-handler": ^5.0.4 - "@smithy/node-http-handler": ^4.0.6 - "@smithy/types": ^4.3.1 + "@smithy/fetch-http-handler": ^5.1.1 + "@smithy/node-http-handler": ^4.1.1 + "@smithy/types": ^4.3.2 "@smithy/util-base64": ^4.0.0 "@smithy/util-buffer-from": ^4.0.0 "@smithy/util-hex-encoding": ^4.0.0 "@smithy/util-utf8": ^4.0.0 tslib: ^2.6.2 - checksum: 5e4ef783e41185d291a72e8503d02fd5a5f7bd23f3d30198f3d738c0f27dd6d7ea131fe6fbe36a6ac69b8bd4207f7dfc75a15329764e6aa52f62c45bc5442619 + checksum: 45d2945656a68822272eb5e37e447bd161861722d841712d087cc0aaf93ad0da8162eef2164d1a35f55a7124cb8815b357b766c21442b23ea972b1d5345f0526 languageName: node linkType: hard @@ -10944,14 +13272,14 @@ __metadata: languageName: node linkType: hard -"@smithy/util-waiter@npm:^4.0.5": - version: 4.0.5 - resolution: "@smithy/util-waiter@npm:4.0.5" +"@smithy/util-waiter@npm:^4.0.6, @smithy/util-waiter@npm:^4.0.7": + version: 4.0.7 + resolution: "@smithy/util-waiter@npm:4.0.7" dependencies: - "@smithy/abort-controller": ^4.0.4 - "@smithy/types": ^4.3.1 + "@smithy/abort-controller": ^4.0.5 + "@smithy/types": ^4.3.2 tslib: ^2.6.2 - checksum: c53b4ae929d37d8d8b3629b0c91005d48c8f788257eccbfb62b3b7f7a670934d8a44556456289c4a0a5fde957d87162c36318184b5e2df154deeeabe97bfd4b4 + checksum: 14caffd913b9b18ff4f33d6bb1f05eef2e354104a6db2b69654d7db4582c4be46b202d46af87a66177a8a3a99082fa8b0948195de8aeb63998c6ed5b04f2bd3d languageName: node linkType: hard @@ -11383,6 +13711,15 @@ __metadata: languageName: node linkType: hard +"@types/chai@npm:^5.2.2": + version: 5.2.2 + resolution: "@types/chai@npm:5.2.2" + dependencies: + "@types/deep-eql": "*" + checksum: 49282bf0e8246800ebb36f17256f97bd3a8c4fb31f92ad3c0eaa7623518d7e87f1eaad4ad206960fcaf7175854bdff4cb167e4fe96811e0081b4ada83dd533ec + languageName: node + linkType: hard + "@types/columnify@npm:^1.5.0, @types/columnify@npm:^1.5.1": version: 1.5.1 resolution: "@types/columnify@npm:1.5.1" @@ -11446,6 +13783,13 @@ __metadata: languageName: node linkType: hard +"@types/deep-eql@npm:*": + version: 4.0.2 + resolution: "@types/deep-eql@npm:4.0.2" + checksum: bf3f811843117900d7084b9d0c852da9a044d12eb40e6de73b552598a6843c21291a8a381b0532644574beecd5e3491c5ff3a0365ab86b15d59862c025384844 + languageName: node + linkType: hard + "@types/detect-port@npm:^1.3.0": version: 1.3.1 resolution: "@types/detect-port@npm:1.3.1" @@ -11624,10 +13968,10 @@ __metadata: languageName: node linkType: hard -"@types/istanbul-lib-coverage@npm:*, @types/istanbul-lib-coverage@npm:^2.0.0, @types/istanbul-lib-coverage@npm:^2.0.1": - version: 2.0.3 - resolution: "@types/istanbul-lib-coverage@npm:2.0.3" - checksum: 820d093eed629844074ae6b94b7d131eb0aacf33b9c952488d20ccab9dadf1376dbb33a461960ace5bc58208b5fac3ff5991283e9bf07914150998ebdfb0115e +"@types/istanbul-lib-coverage@npm:*, @types/istanbul-lib-coverage@npm:^2.0.0, @types/istanbul-lib-coverage@npm:^2.0.1, @types/istanbul-lib-coverage@npm:^2.0.6": + version: 2.0.6 + resolution: "@types/istanbul-lib-coverage@npm:2.0.6" + checksum: 3948088654f3eeb45363f1db158354fb013b362dba2a5c2c18c559484d5eb9f6fd85b23d66c0a7c2fcfab7308d0a585b14dadaca6cc8bf89ebfdc7f8f5102fb7 languageName: node linkType: hard @@ -11640,12 +13984,12 @@ __metadata: languageName: node linkType: hard -"@types/istanbul-reports@npm:^3.0.0": - version: 3.0.1 - resolution: "@types/istanbul-reports@npm:3.0.1" +"@types/istanbul-reports@npm:^3.0.0, @types/istanbul-reports@npm:^3.0.4": + version: 3.0.4 + resolution: "@types/istanbul-reports@npm:3.0.4" dependencies: "@types/istanbul-lib-report": "*" - checksum: e147f0db9346a0cae9a359220bc76f7c78509fb6979a2597feb24d64b6e8328d2d26f9d152abbd59c6bca721e4ea2530af20116d01df50815efafd1e151fd777 + checksum: 1647fd402aced5b6edac87274af14ebd6b3a85447ef9ad11853a70fd92a98d35f81a5d3ea9fcb5dbb5834e800c6e35b64475e33fcae6bfa9acc70d61497c54ee languageName: node linkType: hard @@ -11771,19 +14115,12 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:*, @types/node@npm:^18.16.0, @types/node@npm:^18.16.1": - version: 18.19.31 - resolution: "@types/node@npm:18.19.31" +"@types/node@npm:*, @types/node@npm:^20.9.0": + version: 20.19.11 + resolution: "@types/node@npm:20.19.11" dependencies: - undici-types: ~5.26.4 - checksum: bfebae8389220c0188492c82eaf328f4ba15e6e9b4abee33d6bf36d3b13f188c2f53eb695d427feb882fff09834f467405e2ed9be6aeb6ad4705509822d2ea08 - languageName: node - linkType: hard - -"@types/node@npm:^12.12.6": - version: 12.20.37 - resolution: "@types/node@npm:12.20.37" - checksum: 6fac1a3b4639314b6f9bda1064de4d63564d3704759a817fbd9d66b2b0ae7bb5622e10ca4acdba3c61a2a0ac94f20e9975993a56a3ff666c29ffac9d65fee196 + undici-types: ~6.21.0 + checksum: 9eecc4be04f1a8afbb8f8059b322fd0bbceeb02f96669bbaa52fb0b264c2e3269432a8833ada4be7b335e18d6b438b2d2c0274f5b3f54cc2081cb7c5374a6561 languageName: node linkType: hard @@ -11972,6 +14309,22 @@ __metadata: languageName: node linkType: hard +"@types/sinon@npm:^17.0.3": + version: 17.0.4 + resolution: "@types/sinon@npm:17.0.4" + dependencies: + "@types/sinonjs__fake-timers": "*" + checksum: 7c67ae1050d98a86d8dd771f0a764e97adb9d54812bf3b001195f8cfaa1e2bdfc725d5b970b91e7b0bb6b7c1ca209f47993f2c6f84f1f868313c37441313ca5b + languageName: node + linkType: hard + +"@types/sinonjs__fake-timers@npm:*": + version: 8.1.5 + resolution: "@types/sinonjs__fake-timers@npm:8.1.5" + checksum: 2b8bdc246365518fc1b08f5720445093cce586183acca19a560be6ef81f824bd9a96c090e462f622af4d206406dadf2033c5daf99a51c1096da6494e5c8dc32e + languageName: node + linkType: hard + "@types/sockjs@npm:^0.3.36": version: 0.3.36 resolution: "@types/sockjs@npm:0.3.36" @@ -11981,10 +14334,10 @@ __metadata: languageName: node linkType: hard -"@types/stack-utils@npm:^2.0.0": - version: 2.0.1 - resolution: "@types/stack-utils@npm:2.0.1" - checksum: 3327ee919a840ffe907bbd5c1d07dfd79137dd9732d2d466cf717ceec5bb21f66296173c53bb56cff95fae4185b9cd6770df3e9745fe4ba528bbc4975f54d13f +"@types/stack-utils@npm:^2.0.0, @types/stack-utils@npm:^2.0.3": + version: 2.0.3 + resolution: "@types/stack-utils@npm:2.0.3" + checksum: 1f4658385ae936330581bcb8aa3a066df03867d90281cdf89cc356d404bd6579be0f11902304e1f775d92df22c6dd761d4451c804b0a4fba973e06211e9bd77c languageName: node linkType: hard @@ -12139,12 +14492,12 @@ __metadata: languageName: node linkType: hard -"@types/yargs@npm:^17, @types/yargs@npm:^17.0.8": - version: 17.0.24 - resolution: "@types/yargs@npm:17.0.24" +"@types/yargs@npm:^17, @types/yargs@npm:^17.0.33, @types/yargs@npm:^17.0.8": + version: 17.0.33 + resolution: "@types/yargs@npm:17.0.33" dependencies: "@types/yargs-parser": "*" - checksum: fbebf57e1d04199e5e7eb0c67a402566fa27177ee21140664e63da826408793d203d262b48f8f41d4a7665126393d2e952a463e960e761226def247d9bbcdbd0 + checksum: d16937d7ac30dff697801c3d6f235be2166df42e4a88bf730fa6dc09201de3727c0a9500c59a672122313341de5f24e45ee0ff579c08ce91928e519090b7906b languageName: node linkType: hard @@ -12374,6 +14727,48 @@ __metadata: languageName: node linkType: hard +"@vitest/expect@npm:>1.6.0": + version: 3.2.4 + resolution: "@vitest/expect@npm:3.2.4" + dependencies: + "@types/chai": ^5.2.2 + "@vitest/spy": 3.2.4 + "@vitest/utils": 3.2.4 + chai: ^5.2.0 + tinyrainbow: ^2.0.0 + checksum: 7586104e3fd31dbe1e6ecaafb9a70131e4197dce2940f727b6a84131eee3decac7b10f9c7c72fa5edbdb68b6f854353bd4c0fa84779e274207fb7379563b10db + languageName: node + linkType: hard + +"@vitest/pretty-format@npm:3.2.4": + version: 3.2.4 + resolution: "@vitest/pretty-format@npm:3.2.4" + dependencies: + tinyrainbow: ^2.0.0 + checksum: 5ad7d4278e067390d7d633e307fee8103958806a419ca380aec0e33fae71b44a64415f7a9b4bc11635d3c13d4a9186111c581d3cef9c65cc317e68f077456887 + languageName: node + linkType: hard + +"@vitest/spy@npm:3.2.4": + version: 3.2.4 + resolution: "@vitest/spy@npm:3.2.4" + dependencies: + tinyspy: ^4.0.3 + checksum: 6ebf0b4697dc238476d6b6a60c76ba9eb1dd8167a307e30f08f64149612fd50227682b876420e4c2e09a76334e73f72e3ebf0e350714dc22474258292e202024 + languageName: node + linkType: hard + +"@vitest/utils@npm:3.2.4": + version: 3.2.4 + resolution: "@vitest/utils@npm:3.2.4" + dependencies: + "@vitest/pretty-format": 3.2.4 + loupe: ^3.1.4 + tinyrainbow: ^2.0.0 + checksum: 024a9b8c8bcc12cf40183c246c244b52ecff861c6deb3477cbf487ac8781ad44c68a9c5fd69f8c1361878e55b97c10d99d511f2597f1f7244b5e5101d028ba64 + languageName: node + linkType: hard + "@webassemblyjs/ast@npm:1.12.1, @webassemblyjs/ast@npm:^1.12.1": version: 1.12.1 resolution: "@webassemblyjs/ast@npm:1.12.1" @@ -12942,7 +15337,7 @@ __metadata: "@types/glob": ^7.1.1 "@types/jest": ^29.0.0 "@types/js-yaml": ^4.0.0 - "@types/node": ^18.16.1 + "@types/node": ^20.9.0 "@types/yargs": ^17 "@typescript-eslint/eslint-plugin": ^5.34.0 "@typescript-eslint/parser": ^5.34.0 @@ -13024,7 +15419,7 @@ __metadata: "@aws-amplify/amplify-cli-core": 4.4.2 "@aws-amplify/amplify-function-plugin-interface": 1.12.1 "@aws-amplify/amplify-prompts": 2.8.7 - "@types/node": ^12.12.6 + "@types/node": ^20.9.0 "@types/which": ^1.3.2 execa: ^5.1.1 fs-extra: ^8.1.0 @@ -13072,7 +15467,7 @@ __metadata: "@types/body-parser": ^1.19.2 "@types/express": ^4.17.3 "@types/lodash": ^4.14.149 - "@types/node": ^18.16.1 + "@types/node": ^20.9.0 "@types/openpgp": ^4.4.18 "@types/ws": ^7.4.4 amplify-dynamodb-simulator: 2.9.24 @@ -13117,7 +15512,7 @@ __metadata: "@aws-amplify/amplify-cli-core": 4.4.2 "@aws-amplify/amplify-function-plugin-interface": 1.12.1 "@types/archiver": ^5.1.1 - "@types/node": ^12.12.6 + "@types/node": ^20.9.0 "@types/semver": ^7.1.0 "@types/which": ^1.3.2 archiver: ^7.0.1 @@ -13146,7 +15541,7 @@ __metadata: dependencies: "@aws-amplify/amplify-cli-core": 4.4.2 "@aws-amplify/amplify-function-plugin-interface": 1.12.1 - "@types/node": ^12.12.6 + "@types/node": ^20.9.0 "@types/semver": ^7.1.0 "@types/which": ^1.3.2 execa: ^5.1.1 @@ -13172,7 +15567,7 @@ __metadata: "@aws-amplify/amplify-cli-core": 4.4.2 "@aws-amplify/amplify-function-plugin-interface": 1.12.1 "@types/exit": ^0.1.31 - "@types/node": ^12.12.6 + "@types/node": ^20.9.0 "@types/semver": ^7 execa: ^5.1.1 exit: ^0.1.2 @@ -13189,7 +15584,7 @@ __metadata: "@aws-amplify/amplify-cli-core": 4.4.2 "@aws-amplify/amplify-function-plugin-interface": 1.12.1 "@types/fs-extra": ^8.0.1 - "@types/node": ^12.12.6 + "@types/node": ^20.9.0 "@types/semver": ^7.1.0 execa: ^5.1.1 glob: ^11.0.1 @@ -13341,7 +15736,7 @@ __metadata: languageName: node linkType: hard -"ansi-styles@npm:^5.0.0": +"ansi-styles@npm:^5.0.0, ansi-styles@npm:^5.2.0": version: 5.2.0 resolution: "ansi-styles@npm:5.2.0" checksum: 9c4ca80eb3c2fb7b33841c210d2f20807f40865d27008d7c3f707b7f95cab7d67462a565e2388ac3285b71cb3d9bb2173de8da37c57692a362885ec34d6e27df @@ -13770,6 +16165,13 @@ __metadata: languageName: node linkType: hard +"assertion-error@npm:^2.0.1": + version: 2.0.1 + resolution: "assertion-error@npm:2.0.1" + checksum: bbbcb117ac6480138f8c93cf7f535614282dea9dc828f540cdece85e3c665e8f78958b96afac52f29ff883c72638e6a87d469ecc9fe5bc902df03ed24a55dba8 + languageName: node + linkType: hard + "ast-types-flow@npm:^0.0.7": version: 0.0.7 resolution: "ast-types-flow@npm:0.0.7" @@ -13970,6 +16372,34 @@ __metadata: languageName: node linkType: hard +"aws-sdk-client-mock-jest@npm:^4.1.0": + version: 4.1.0 + resolution: "aws-sdk-client-mock-jest@npm:4.1.0" + dependencies: + "@vitest/expect": ">1.6.0" + expect: ">28.1.3" + tslib: ^2.1.0 + peerDependencies: + aws-sdk-client-mock: 4.1.0 + vitest: ">1.6.0" + peerDependenciesMeta: + vitest: + optional: true + checksum: bf68498476328b483e2847eb4a0ff65b4d6bf5850ed650d0c5e433373337b9fa3bdd0a64d63f3ff389747b150b1706a150b28e39bb6b5a7c490e9df49a0e62e1 + languageName: node + linkType: hard + +"aws-sdk-client-mock@npm:^4.1.0": + version: 4.1.0 + resolution: "aws-sdk-client-mock@npm:4.1.0" + dependencies: + "@types/sinon": ^17.0.3 + sinon: ^18.0.1 + tslib: ^2.1.0 + checksum: 045caad0cff0ffeb08e69849dcae51aac8999163c58d71220bf47a82c237aabab2abf92bf6bf3bd7666e6e8984513c628e01a89eafa46fb230201d6587bc01e9 + languageName: node + linkType: hard + "aws-sdk-mock@npm:^6.2.0": version: 6.2.0 resolution: "aws-sdk-mock@npm:6.2.0" @@ -15086,6 +17516,19 @@ __metadata: languageName: node linkType: hard +"chai@npm:^5.2.0": + version: 5.2.1 + resolution: "chai@npm:5.2.1" + dependencies: + assertion-error: ^2.0.1 + check-error: ^2.1.1 + deep-eql: ^5.0.1 + loupe: ^3.1.0 + pathval: ^2.0.0 + checksum: 58209c03ae9b2fd97cfa1cb0fbe372b1906e6091311b9ba1b0468cc4923b0766a50a1050a164df3ccefb9464944c9216b632f1477c9e429068013bdbb57220f6 + languageName: node + linkType: hard + "chalk@npm:4.1.0": version: 4.1.0 resolution: "chalk@npm:4.1.0" @@ -15218,6 +17661,13 @@ __metadata: languageName: node linkType: hard +"check-error@npm:^2.1.1": + version: 2.1.1 + resolution: "check-error@npm:2.1.1" + checksum: 979f13eccab306cf1785fa10941a590b4e7ea9916ea2a4f8c87f0316fc3eab07eabefb6e587424ef0f88cbcd3805791f172ea739863ca3d7ce2afc54641c7f0e + languageName: node + linkType: hard + "check-types@npm:^11.1.1": version: 11.1.2 resolution: "check-types@npm:11.1.2" @@ -15279,10 +17729,10 @@ __metadata: languageName: node linkType: hard -"ci-info@npm:^4.0.0": - version: 4.2.0 - resolution: "ci-info@npm:4.2.0" - checksum: 37a2f4b6a213a5cf835890eb0241f0d5b022f6cfefde58a69e9af8e3a0e71e06d6ad7754b0d4efb9cd2613e58a7a33996d71b56b0d04242722e86666f3f3d058 +"ci-info@npm:^4.0.0, ci-info@npm:^4.2.0": + version: 4.3.0 + resolution: "ci-info@npm:4.3.0" + checksum: 60d3dfe95d75c01454ec1cfd5108617dd598a28a2a3e148bd7e1523c1c208b5f5a3007cafcbe293e6fd0a5a310cc32217c5dc54743eeabc0a2bec80072fc055c languageName: node linkType: hard @@ -16907,6 +19357,13 @@ __metadata: languageName: node linkType: hard +"deep-eql@npm:^5.0.1": + version: 5.0.2 + resolution: "deep-eql@npm:5.0.2" + checksum: 7102cf3b7bb719c6b9c0db2e19bf0aa9318d141581befe8c7ce8ccd39af9eaa4346e5e05adef7f9bd7015da0f13a3a25dcfe306ef79dc8668aedbecb658dd247 + languageName: node + linkType: hard + "deep-extend@npm:^0.6.0": version: 0.6.0 resolution: "deep-extend@npm:0.6.0" @@ -18495,6 +20952,20 @@ __metadata: languageName: node linkType: hard +"expect@npm:>28.1.3": + version: 30.0.4 + resolution: "expect@npm:30.0.4" + dependencies: + "@jest/expect-utils": 30.0.4 + "@jest/get-type": 30.0.1 + jest-matcher-utils: 30.0.4 + jest-message-util: 30.0.2 + jest-mock: 30.0.2 + jest-util: 30.0.2 + checksum: de0c7cf4068591feda6b4b1dfcb5711f085266bfa720a8498ac8c0d03fbfa84881f54b67f25c79bee4bf0f6040ee12ed004b209de7d0cff82fd06d7b42baabc2 + languageName: node + linkType: hard + "expect@npm:^27.5.1": version: 27.5.1 resolution: "expect@npm:27.5.1" @@ -21949,6 +24420,18 @@ __metadata: languageName: node linkType: hard +"jest-diff@npm:30.0.4": + version: 30.0.4 + resolution: "jest-diff@npm:30.0.4" + dependencies: + "@jest/diff-sequences": 30.0.1 + "@jest/get-type": 30.0.1 + chalk: ^4.1.2 + pretty-format: 30.0.2 + checksum: aceae3a2e90ec232305ba43082e34ec5d24867459a6f52169e47edfd5f55457788ad534ff781d12e6606a70bc7ddc5090e45748732772679065dfd56f46f8ab1 + languageName: node + linkType: hard + "jest-diff@npm:>=29.4.3 < 30, jest-diff@npm:^29.4.1, jest-diff@npm:^29.7.0": version: 29.7.0 resolution: "jest-diff@npm:29.7.0" @@ -22119,6 +24602,18 @@ __metadata: languageName: node linkType: hard +"jest-matcher-utils@npm:30.0.4": + version: 30.0.4 + resolution: "jest-matcher-utils@npm:30.0.4" + dependencies: + "@jest/get-type": 30.0.1 + chalk: ^4.1.2 + jest-diff: 30.0.4 + pretty-format: 30.0.2 + checksum: 18f9f808e1de56a466d3a858acd5d253ea13e386619de05fe21b37316305b15feb078f12beae9228c878fc6b60b9bbbd1a6240f1878f80a222d241b38e54b53f + languageName: node + linkType: hard + "jest-matcher-utils@npm:^27.5.1": version: 27.5.1 resolution: "jest-matcher-utils@npm:27.5.1" @@ -22143,6 +24638,23 @@ __metadata: languageName: node linkType: hard +"jest-message-util@npm:30.0.2": + version: 30.0.2 + resolution: "jest-message-util@npm:30.0.2" + dependencies: + "@babel/code-frame": ^7.27.1 + "@jest/types": 30.0.1 + "@types/stack-utils": ^2.0.3 + chalk: ^4.1.2 + graceful-fs: ^4.2.11 + micromatch: ^4.0.8 + pretty-format: 30.0.2 + slash: ^3.0.0 + stack-utils: ^2.0.6 + checksum: c010d5b7d86e735e2fb4c4a220f57004349f488f5d4663240a7e9f2694d01b5228136540d55036777fde4227b5e0b56f08885b7f69395b295cab878357b1aeb1 + languageName: node + linkType: hard + "jest-message-util@npm:^26.6.2": version: 26.6.2 resolution: "jest-message-util@npm:26.6.2" @@ -22194,6 +24706,17 @@ __metadata: languageName: node linkType: hard +"jest-mock@npm:30.0.2": + version: 30.0.2 + resolution: "jest-mock@npm:30.0.2" + dependencies: + "@jest/types": 30.0.1 + "@types/node": "*" + jest-util: 30.0.2 + checksum: 7728997c1d654475b88e18b7ba33a2a1b9f89ce33a9082bf2d14dcc3e831f372f80c762e481777886a3a04b4489ea5390ecdeb21c4def57fba5b2c77086a3959 + languageName: node + linkType: hard + "jest-mock@npm:^26.6.2": version: 26.6.2 resolution: "jest-mock@npm:26.6.2" @@ -22237,6 +24760,13 @@ __metadata: languageName: node linkType: hard +"jest-regex-util@npm:30.0.1": + version: 30.0.1 + resolution: "jest-regex-util@npm:30.0.1" + checksum: f30c70524ebde2d1012afe5ffa5691d5d00f7d5ba9e43d588f6460ac6fe96f9e620f2f9b36a02d0d3e7e77bc8efb8b3450ae3b80ac53c8be5099e01bf54f6728 + languageName: node + linkType: hard + "jest-regex-util@npm:^27.0.0, jest-regex-util@npm:^27.5.1": version: 27.5.1 resolution: "jest-regex-util@npm:27.5.1" @@ -22469,6 +24999,20 @@ __metadata: languageName: node linkType: hard +"jest-util@npm:30.0.2": + version: 30.0.2 + resolution: "jest-util@npm:30.0.2" + dependencies: + "@jest/types": 30.0.1 + "@types/node": "*" + chalk: ^4.1.2 + ci-info: ^4.2.0 + graceful-fs: ^4.2.11 + picomatch: ^4.0.2 + checksum: 07de384790b8e5a5925fba5448fa1475790a5b52271fbf99958c18e468da1af940f8b45e330d87766576cf6c5d1f4f41ce51c976483a5079653d9fcdba8aac8e + languageName: node + linkType: hard + "jest-util@npm:^26.6.2": version: 26.6.2 resolution: "jest-util@npm:26.6.2" @@ -23593,6 +26137,13 @@ __metadata: languageName: node linkType: hard +"loupe@npm:^3.1.0, loupe@npm:^3.1.4": + version: 3.1.4 + resolution: "loupe@npm:3.1.4" + checksum: 5c2e6aefaad25f812d361c750b8cf4ff91d68de289f141d7c85c2ce9bb79eeefa06a93c85f7b87cba940531ed8f15e492f32681d47eed23842ad1963eb3a154d + languageName: node + linkType: hard + "lower-case-first@npm:^2.0.1, lower-case-first@npm:^2.0.2": version: 2.0.2 resolution: "lower-case-first@npm:2.0.2" @@ -23983,7 +26534,7 @@ __metadata: languageName: node linkType: hard -"micromatch@npm:^4.0.2, micromatch@npm:^4.0.4, micromatch@npm:~4.0.0": +"micromatch@npm:^4.0.2, micromatch@npm:^4.0.4, micromatch@npm:^4.0.8, micromatch@npm:~4.0.0": version: 4.0.8 resolution: "micromatch@npm:4.0.8" dependencies: @@ -25926,6 +28477,13 @@ __metadata: languageName: node linkType: hard +"pathval@npm:^2.0.0": + version: 2.0.1 + resolution: "pathval@npm:2.0.1" + checksum: 460f4709479fbf2c45903a65655fc8f0a5f6d808f989173aeef5fdea4ff4f303dc13f7870303999add60ec49d4c14733895c0a869392e9866f1091fa64fd7581 + languageName: node + linkType: hard + "pbkdf2@npm:^3.0.3": version: 3.1.3 resolution: "pbkdf2@npm:3.1.3" @@ -27080,6 +29638,17 @@ __metadata: languageName: node linkType: hard +"pretty-format@npm:30.0.2": + version: 30.0.2 + resolution: "pretty-format@npm:30.0.2" + dependencies: + "@jest/schemas": 30.0.1 + ansi-styles: ^5.2.0 + react-is: ^18.3.1 + checksum: cf542dc2d0be95e2b1c6e3a397a4fc13fce1c9f8feed6b56165c0d23c7a83423abb6b032ed8e3e1b7c1c0709f9b117dd30b5185f107e58f8766616be6de84850 + languageName: node + linkType: hard + "pretty-format@npm:^26.6.2": version: 26.6.2 resolution: "pretty-format@npm:26.6.2" @@ -27602,10 +30171,10 @@ __metadata: languageName: node linkType: hard -"react-is@npm:^16.8.6 || ^17.0.0 || ^18.0.0, react-is@npm:^18.0.0": - version: 18.2.0 - resolution: "react-is@npm:18.2.0" - checksum: 6eb5e4b28028c23e2bfcf73371e72cd4162e4ac7ab445ddae2afe24e347a37d6dc22fae6e1748632cd43c6d4f9b8f86dcf26bf9275e1874f436d129952528ae0 +"react-is@npm:^16.8.6 || ^17.0.0 || ^18.0.0, react-is@npm:^18.0.0, react-is@npm:^18.3.1": + version: 18.3.1 + resolution: "react-is@npm:18.3.1" + checksum: f2f1e60010c683479e74c63f96b09fb41603527cd131a9959e2aee1e5a8b0caf270b365e5ca77d4a6b18aae659b60a86150bb3979073528877029b35aecd2072 languageName: node linkType: hard @@ -29395,12 +31964,12 @@ __metadata: languageName: node linkType: hard -"stack-utils@npm:^2.0.2, stack-utils@npm:^2.0.3": - version: 2.0.5 - resolution: "stack-utils@npm:2.0.5" +"stack-utils@npm:^2.0.2, stack-utils@npm:^2.0.3, stack-utils@npm:^2.0.6": + version: 2.0.6 + resolution: "stack-utils@npm:2.0.6" dependencies: escape-string-regexp: ^2.0.0 - checksum: 059f828eed5b03b963e8200529c27bd92b105f2cac9dffc9edcbc739ea8fa108e4ec45d0da257d8e0f7b5ac98db5643a0787e5c25ceab1396f7123e1ee15a086 + checksum: 651c9f87667e077584bbe848acaecc6049bc71979f1e9a46c7b920cad4431c388df0f51b8ad7cfd6eed3db97a2878d0fc8b3122979439ea8bac29c61c95eec8a languageName: node linkType: hard @@ -30227,6 +32796,20 @@ __metadata: languageName: node linkType: hard +"tinyrainbow@npm:^2.0.0": + version: 2.0.0 + resolution: "tinyrainbow@npm:2.0.0" + checksum: c83c52bef4e0ae7fb8ec6a722f70b5b6fa8d8be1c85792e829f56c0e1be94ab70b293c032dc5048d4d37cfe678f1f5babb04bdc65fd123098800148ca989184f + languageName: node + linkType: hard + +"tinyspy@npm:^4.0.3": + version: 4.0.3 + resolution: "tinyspy@npm:4.0.3" + checksum: 0a92a18b5350945cc8a1da3a22c9ad9f4e2945df80aaa0c43e1b3a3cfb64d8501e607ebf0305e048e3c3d3e0e7f8eb10cea27dc17c21effb73e66c4a3be36373 + languageName: node + linkType: hard + "title-case@npm:^3.0.3": version: 3.0.3 resolution: "title-case@npm:3.0.3" @@ -30909,10 +33492,10 @@ __metadata: languageName: node linkType: hard -"undici-types@npm:~5.26.4": - version: 5.26.5 - resolution: "undici-types@npm:5.26.5" - checksum: bb673d7876c2d411b6eb6c560e0c571eef4a01c1c19925175d16e3a30c4c428181fb8d7ae802a261f283e4166a0ac435e2f505743aa9e45d893f9a3df017b501 +"undici-types@npm:~6.21.0": + version: 6.21.0 + resolution: "undici-types@npm:6.21.0" + checksum: c01ed51829b10aa72fc3ce64b747f8e74ae9b60eafa19a7b46ef624403508a54c526ffab06a14a26b3120d055e1104d7abe7c9017e83ced038ea5cf52f8d5e04 languageName: node linkType: hard