From 35c438f86580ac8d8c95aa85d95b173e7a8c3781 Mon Sep 17 00:00:00 2001 From: hkobew Date: Fri, 14 Mar 2025 14:27:19 -0400 Subject: [PATCH 01/10] refactor: rename client --- .../dynamicResources/explorer/nodes/resourcesNode.ts | 4 ++-- .../core/src/lambda/commands/deleteCloudFormation.ts | 4 ++-- .../core/src/lambda/explorer/cloudFormationNodes.ts | 6 +++--- .../core/src/shared/clients/cloudFormationClient.ts | 5 ++--- packages/core/src/shared/sam/deploy.ts | 5 ++--- packages/core/src/shared/sam/sync.ts | 5 ++--- packages/core/src/shared/ui/sam/stackPrompter.ts | 8 ++------ .../test/dynamicResources/awsResourceManager.test.ts | 4 ++-- .../test/lambda/explorer/cloudFormationNodes.test.ts | 4 ++-- packages/core/src/test/shared/sam/deploy.test.ts | 6 +++--- packages/core/src/test/shared/sam/sync.test.ts | 10 +++++----- .../core/src/test/shared/ui/sam/stackPrompter.test.ts | 4 ++-- 12 files changed, 29 insertions(+), 36 deletions(-) diff --git a/packages/core/src/dynamicResources/explorer/nodes/resourcesNode.ts b/packages/core/src/dynamicResources/explorer/nodes/resourcesNode.ts index 313ce4e7d2a..65eefb27746 100644 --- a/packages/core/src/dynamicResources/explorer/nodes/resourcesNode.ts +++ b/packages/core/src/dynamicResources/explorer/nodes/resourcesNode.ts @@ -5,7 +5,7 @@ import * as vscode from 'vscode' import * as nls from 'vscode-nls' -import { CloudFormationClient, DefaultCloudFormationClient } from '../../../shared/clients/cloudFormationClient' +import { CloudFormationClient, CloudFormationClient } from '../../../shared/clients/cloudFormationClient' import { AWSTreeNodeBase } from '../../../shared/treeview/nodes/awsTreeNodeBase' import { PlaceholderNode } from '../../../shared/treeview/nodes/placeholderNode' import { makeChildrenNodes } from '../../../shared/treeview/utils' @@ -23,7 +23,7 @@ export class ResourcesNode extends AWSTreeNodeBase { public constructor( public readonly region: string, - public readonly cloudFormation: CloudFormationClient = new DefaultCloudFormationClient(region), + public readonly cloudFormation: CloudFormationClient = new CloudFormationClient(region), private readonly cloudControl: CloudControlClient = new DefaultCloudControlClient(region), private readonly settings = new ResourcesSettings() ) { diff --git a/packages/core/src/lambda/commands/deleteCloudFormation.ts b/packages/core/src/lambda/commands/deleteCloudFormation.ts index 67c903f35c3..fd22ee53b47 100644 --- a/packages/core/src/lambda/commands/deleteCloudFormation.ts +++ b/packages/core/src/lambda/commands/deleteCloudFormation.ts @@ -7,7 +7,7 @@ import * as nls from 'vscode-nls' const localize = nls.loadMessageBundle() import * as vscode from 'vscode' -import { DefaultCloudFormationClient } from '../../shared/clients/cloudFormationClient' +import { CloudFormationClient } from '../../shared/clients/cloudFormationClient' import * as localizedText from '../../shared/localizedText' import { getLogger, Logger } from '../../shared/logger/logger' @@ -45,7 +45,7 @@ export async function deleteCloudFormation(refresh: () => void, node?: CloudForm }) if (userResponse) { - const client = new DefaultCloudFormationClient(node.regionCode) + const client = new CloudFormationClient(node.regionCode) await client.deleteStack(stackName) diff --git a/packages/core/src/lambda/explorer/cloudFormationNodes.ts b/packages/core/src/lambda/explorer/cloudFormationNodes.ts index 13eb1ca53f1..8aa308fbbcf 100644 --- a/packages/core/src/lambda/explorer/cloudFormationNodes.ts +++ b/packages/core/src/lambda/explorer/cloudFormationNodes.ts @@ -9,7 +9,7 @@ const localize = nls.loadMessageBundle() import { CloudFormation, Lambda } from 'aws-sdk' import * as os from 'os' import * as vscode from 'vscode' -import { DefaultCloudFormationClient } from '../../shared/clients/cloudFormationClient' +import { CloudFormationClient } from '../../shared/clients/cloudFormationClient' import { DefaultLambdaClient } from '../../shared/clients/lambdaClient' import { AWSResourceNode } from '../../shared/treeview/nodes/awsResourceNode' @@ -28,7 +28,7 @@ export class CloudFormationNode extends AWSTreeNodeBase { public constructor( public override readonly regionCode: string, - private readonly client = new DefaultCloudFormationClient(regionCode) + private readonly client = new CloudFormationClient(regionCode) ) { super('CloudFormation', vscode.TreeItemCollapsibleState.Collapsed) this.stackNodes = new Map() @@ -68,7 +68,7 @@ export class CloudFormationStackNode extends AWSTreeNodeBase implements AWSResou public override readonly regionCode: string, private stackSummary: CloudFormation.StackSummary, private readonly lambdaClient = new DefaultLambdaClient(regionCode), - private readonly cloudformationClient = new DefaultCloudFormationClient(regionCode) + private readonly cloudformationClient = new CloudFormationClient(regionCode) ) { super('', vscode.TreeItemCollapsibleState.Collapsed) diff --git a/packages/core/src/shared/clients/cloudFormationClient.ts b/packages/core/src/shared/clients/cloudFormationClient.ts index 19c7ff5aa48..99183efe460 100644 --- a/packages/core/src/shared/clients/cloudFormationClient.ts +++ b/packages/core/src/shared/clients/cloudFormationClient.ts @@ -7,10 +7,9 @@ import { CloudFormation } from 'aws-sdk' import globals from '../extensionGlobals' import { AsyncCollection } from '../utilities/asyncCollection' import { pageableToCollection } from '../utilities/collectionUtils' -import { ClassToInterfaceType, isNonNullable } from '../utilities/tsUtils' +import { isNonNullable } from '../utilities/tsUtils' -export type CloudFormationClient = ClassToInterfaceType -export class DefaultCloudFormationClient { +export class CloudFormationClient { public constructor(public readonly regionCode: string) {} public async deleteStack(name: string): Promise { diff --git a/packages/core/src/shared/sam/deploy.ts b/packages/core/src/shared/sam/deploy.ts index 4746a6a8660..4634a19b16e 100644 --- a/packages/core/src/shared/sam/deploy.ts +++ b/packages/core/src/shared/sam/deploy.ts @@ -8,7 +8,7 @@ import { AWSTreeNodeBase } from '../treeview/nodes/awsTreeNodeBase' import { TreeNode, isTreeNode } from '../treeview/resourceTreeDataProvider' import globals from '../../shared/extensionGlobals' import { ToolkitError } from '../../shared/errors' -import { DefaultCloudFormationClient } from '../clients/cloudFormationClient' +import { CloudFormationClient } from '../clients/cloudFormationClient' import { S3Client } from '../clients/s3' import { samDeployUrl } from '../constants' import { getSpawnEnv } from '../env/resolveEnv' @@ -115,8 +115,7 @@ export class DeployWizard extends CompositeWizard { paramsSource === ParamsSource.Specify || paramsSource === ParamsSource.SpecifyAndSave, }) this.form.stackName.bindPrompter( - ({ region }) => - createStackPrompter(new DefaultCloudFormationClient(region!), deployMementoRootKey, samDeployUrl), + ({ region }) => createStackPrompter(new CloudFormationClient(region!), deployMementoRootKey, samDeployUrl), { showWhen: ({ paramsSource }) => paramsSource === ParamsSource.Specify || paramsSource === ParamsSource.SpecifyAndSave, diff --git a/packages/core/src/shared/sam/sync.ts b/packages/core/src/shared/sam/sync.ts index 99c614ac4d2..ff1e979c210 100644 --- a/packages/core/src/shared/sam/sync.ts +++ b/packages/core/src/shared/sam/sync.ts @@ -10,7 +10,7 @@ import * as path from 'path' import * as localizedText from '../localizedText' import { S3Client } from '../clients/s3' import { DataQuickPickItem, createMultiPick, createQuickPick } from '../ui/pickerPrompter' -import { DefaultCloudFormationClient } from '../clients/cloudFormationClient' +import { CloudFormationClient } from '../clients/cloudFormationClient' import * as CloudFormation from '../cloudformation/cloudformation' import { DefaultEcrClient } from '../clients/ecrClient' import { createRegionPrompter } from '../ui/common/region' @@ -217,8 +217,7 @@ export class SyncWizard extends CompositeWizard { }) this.form.stackName.bindPrompter( - ({ region }) => - createStackPrompter(new DefaultCloudFormationClient(region!), syncMementoRootKey, samSyncUrl), + ({ region }) => createStackPrompter(new CloudFormationClient(region!), syncMementoRootKey, samSyncUrl), { showWhen: ({ paramsSource }) => paramsSource === ParamsSource.Specify || paramsSource === ParamsSource.SpecifyAndSave, diff --git a/packages/core/src/shared/ui/sam/stackPrompter.ts b/packages/core/src/shared/ui/sam/stackPrompter.ts index 12eeec6ca72..58efdcdf08e 100644 --- a/packages/core/src/shared/ui/sam/stackPrompter.ts +++ b/packages/core/src/shared/ui/sam/stackPrompter.ts @@ -4,7 +4,7 @@ */ import { StackSummary } from 'aws-sdk/clients/cloudformation' import { getAwsConsoleUrl } from '../../awsConsole' -import { DefaultCloudFormationClient } from '../../clients/cloudFormationClient' +import { CloudFormationClient } from '../../clients/cloudFormationClient' import * as vscode from 'vscode' import { createCommonButtons } from '../buttons' import { createQuickPick } from '../pickerPrompter' @@ -27,11 +27,7 @@ const canShowStack = (s: StackSummary) => * @returns A quick pick prompter configured for stack selection * */ -export function createStackPrompter( - client: DefaultCloudFormationClient, - mementoRootKey: string, - samCommandUrl: vscode.Uri -) { +export function createStackPrompter(client: CloudFormationClient, mementoRootKey: string, samCommandUrl: vscode.Uri) { const recentStack = getRecentResponse(mementoRootKey, client.regionCode, 'stackName') const consoleUrl = getAwsConsoleUrl('cloudformation', client.regionCode) const items = client.listAllStacks().map((stacks) => diff --git a/packages/core/src/test/dynamicResources/awsResourceManager.test.ts b/packages/core/src/test/dynamicResources/awsResourceManager.test.ts index 03bd07a25dc..5ae80c8eab6 100644 --- a/packages/core/src/test/dynamicResources/awsResourceManager.test.ts +++ b/packages/core/src/test/dynamicResources/awsResourceManager.test.ts @@ -13,7 +13,7 @@ import { ResourceNode } from '../../dynamicResources/explorer/nodes/resourceNode import { ResourceTypeNode } from '../../dynamicResources/explorer/nodes/resourceTypeNode' import { formatResourceModel, AwsResourceManager } from '../../dynamicResources/awsResourceManager' import { CloudControlClient, DefaultCloudControlClient } from '../../shared/clients/cloudControlClient' -import { CloudFormationClient, DefaultCloudFormationClient } from '../../shared/clients/cloudFormationClient' +import { CloudFormationClient, CloudFormationClient } from '../../shared/clients/cloudFormationClient' import { makeTemporaryToolkitFolder, readFileAsString } from '../../shared/filesystemUtilities' import { FakeExtensionContext } from '../fakeExtensionContext' import { existsSync } from 'fs' // eslint-disable-line no-restricted-imports @@ -51,7 +51,7 @@ describe('ResourceManager', function () { cloudControl = stub(DefaultCloudControlClient, { regionCode: '', }) - cloudFormation = stub(DefaultCloudFormationClient, { + cloudFormation = stub(CloudFormationClient, { regionCode: '', }) sandbox = sinon.createSandbox() diff --git a/packages/core/src/test/lambda/explorer/cloudFormationNodes.test.ts b/packages/core/src/test/lambda/explorer/cloudFormationNodes.test.ts index 2eac7d75ecc..1f768caa1bc 100644 --- a/packages/core/src/test/lambda/explorer/cloudFormationNodes.test.ts +++ b/packages/core/src/test/lambda/explorer/cloudFormationNodes.test.ts @@ -12,7 +12,7 @@ import { contextValueCloudformationLambdaFunction, } from '../../../lambda/explorer/cloudFormationNodes' import { LambdaFunctionNode } from '../../../lambda/explorer/lambdaFunctionNode' -import { DefaultCloudFormationClient } from '../../../shared/clients/cloudFormationClient' +import { CloudFormationClient } from '../../../shared/clients/cloudFormationClient' import { DefaultLambdaClient } from '../../../shared/clients/lambdaClient' import globals from '../../../shared/extensionGlobals' import { TestAWSTreeNode } from '../../shared/treeview/nodes/testAWSTreeNode' @@ -34,7 +34,7 @@ function createLambdaClient(...functionNames: string[]) { } function createCloudFormationClient(...stackNames: string[]) { - const client = stub(DefaultCloudFormationClient, { regionCode }) + const client = stub(CloudFormationClient, { regionCode }) client.describeStackResources.resolves({ StackResources: [] }) client.listStacks.returns( asyncGenerator( diff --git a/packages/core/src/test/shared/sam/deploy.test.ts b/packages/core/src/test/shared/sam/deploy.test.ts index a1566e7cda3..77c4a48b008 100644 --- a/packages/core/src/test/shared/sam/deploy.test.ts +++ b/packages/core/src/test/shared/sam/deploy.test.ts @@ -13,7 +13,7 @@ import { samconfigCompleteData, samconfigInvalidData, validTemplateData } from ' import * as SamUtilsModule from '../../../shared/sam/utils' import assert from 'assert' import { getTestWindow } from '../vscode/window' -import { DefaultCloudFormationClient } from '../../../shared/clients/cloudFormationClient' +import { CloudFormationClient } from '../../../shared/clients/cloudFormationClient' import { intoCollection } from '../../../shared/utilities/collectionUtils' import { clickBackButton, createPromptHandler, PrompterTester } from '../wizards/prompterTester' import { RegionNode } from '../../../awsexplorer/regionNode' @@ -42,7 +42,7 @@ describe('SAM DeployWizard', async function () { let workspaceFolder: vscode.WorkspaceFolder let templateFile: vscode.Uri - let mockDefaultCFNClient: sinon.SinonStubbedInstance + let mockDefaultCFNClient: sinon.SinonStubbedInstance let mockDefaultS3Client: sinon.SinonStubbedInstance beforeEach(async () => { @@ -52,7 +52,7 @@ describe('SAM DeployWizard', async function () { sandbox = sinon.createSandbox() // Simulate return of deployed stacks - mockDefaultCFNClient = sandbox.createStubInstance(CloudFormationClientModule.DefaultCloudFormationClient) + mockDefaultCFNClient = sandbox.createStubInstance(CloudFormationClientModule.CloudFormationClient) sandbox.stub(CloudFormationClientModule, 'DefaultCloudFormationClient').returns(mockDefaultCFNClient) mockDefaultCFNClient.listAllStacks.returns(intoCollection(stackSummaries)) diff --git a/packages/core/src/test/shared/sam/sync.test.ts b/packages/core/src/test/shared/sam/sync.test.ts index c53fb883736..66b9ca32822 100644 --- a/packages/core/src/test/shared/sam/sync.test.ts +++ b/packages/core/src/test/shared/sam/sync.test.ts @@ -44,7 +44,7 @@ import { getTestWindow } from '../vscode/window' import { S3Client } from '../../../shared/clients/s3' import { RequiredProps } from '../../../shared/utilities/tsUtils' import S3 from 'aws-sdk/clients/s3' -import { DefaultCloudFormationClient } from '../../../shared/clients/cloudFormationClient' +import { CloudFormationClient } from '../../../shared/clients/cloudFormationClient' import CloudFormation from 'aws-sdk/clients/cloudformation' import { intoCollection } from '../../../shared/utilities/collectionUtils' import { SamConfig, Environment, parseConfig } from '../../../shared/sam/config' @@ -132,7 +132,7 @@ describe('SAM SyncWizard', async () => { let workspaceFolder: vscode.WorkspaceFolder let templateFile: vscode.Uri - let mockDefaultCFNClient: sinon.SinonStubbedInstance + let mockDefaultCFNClient: sinon.SinonStubbedInstance let mockDefaultS3Client: sinon.SinonStubbedInstance let registry: CloudFormationTemplateRegistry @@ -143,7 +143,7 @@ describe('SAM SyncWizard', async () => { sandbox = sinon.createSandbox() // Simulate return of deployed stacks - mockDefaultCFNClient = sandbox.createStubInstance(CloudFormationClientModule.DefaultCloudFormationClient) + mockDefaultCFNClient = sandbox.createStubInstance(CloudFormationClientModule.CloudFormationClient) sandbox.stub(CloudFormationClientModule, 'DefaultCloudFormationClient').returns(mockDefaultCFNClient) mockDefaultCFNClient.listAllStacks.returns(intoCollection(stackSummaries)) @@ -1075,7 +1075,7 @@ describe('SAM runSync', () => { let spyWriteSamconfigGlobal: sinon.SinonSpy let spyRunInterminal: sinon.SinonSpy - let mockDefaultCFNClient: sinon.SinonStubbedInstance + let mockDefaultCFNClient: sinon.SinonStubbedInstance let mockDefaultS3Client: sinon.SinonStubbedInstance let registry: CloudFormationTemplateRegistry @@ -1093,7 +1093,7 @@ describe('SAM runSync', () => { await registry.addItem(templateFile) // Simulate return of deployed stacks - mockDefaultCFNClient = sandbox.createStubInstance(CloudFormationClientModule.DefaultCloudFormationClient) + mockDefaultCFNClient = sandbox.createStubInstance(CloudFormationClientModule.CloudFormationClient) sandbox.stub(CloudFormationClientModule, 'DefaultCloudFormationClient').returns(mockDefaultCFNClient) mockDefaultCFNClient.listAllStacks.returns(intoCollection(stackSummaries)) diff --git a/packages/core/src/test/shared/ui/sam/stackPrompter.test.ts b/packages/core/src/test/shared/ui/sam/stackPrompter.test.ts index 37237030d7e..020d43f7b55 100644 --- a/packages/core/src/test/shared/ui/sam/stackPrompter.test.ts +++ b/packages/core/src/test/shared/ui/sam/stackPrompter.test.ts @@ -10,7 +10,7 @@ import * as vscode from 'vscode' import * as AwsConsoleModule from '../../../../shared/awsConsole' import * as SamUtilsModule from '../../../../shared/sam/utils' import * as ButtonsModule from '../../../../shared/ui/buttons' -import { DefaultCloudFormationClient } from '../../../../shared/clients/cloudFormationClient' +import { CloudFormationClient } from '../../../../shared/clients/cloudFormationClient' import { samSyncUrl } from '../../../../shared/constants' import { createStackPrompter } from '../../../../shared/ui/sam/stackPrompter' import { intoCollection } from '../../../../shared/utilities/collectionUtils' @@ -18,7 +18,7 @@ import { sleep } from '../../../../shared/utilities/timeoutUtils' describe('createStackPrompter', () => { let sandbox: sinon.SinonSandbox - const cfnClient = new DefaultCloudFormationClient('us-east-1') + const cfnClient = new CloudFormationClient('us-east-1') const mementoRootKey = 'samcli.sync.params' beforeEach(() => { From adb4bad11101d749fe2c9815f1040b2fef53f783 Mon Sep 17 00:00:00 2001 From: hkobew Date: Fri, 14 Mar 2025 14:27:49 -0400 Subject: [PATCH 02/10] refactor: rename file for cloud formation client --- packages/core/src/dynamicResources/awsResourceManager.ts | 2 +- .../core/src/dynamicResources/explorer/nodes/resourcesNode.ts | 2 +- packages/core/src/lambda/commands/deleteCloudFormation.ts | 2 +- packages/core/src/lambda/explorer/cloudFormationNodes.ts | 2 +- packages/core/src/lambda/utils.ts | 2 +- .../clients/{cloudFormationClient.ts => cloudFormation.ts} | 0 packages/core/src/shared/sam/deploy.ts | 2 +- packages/core/src/shared/sam/sync.ts | 2 +- packages/core/src/shared/ui/sam/stackPrompter.ts | 2 +- .../core/src/test/dynamicResources/awsResourceManager.test.ts | 2 +- .../test/dynamicResources/explorer/moreResourcesNode.test.ts | 2 +- .../core/src/test/lambda/explorer/cloudFormationNodes.test.ts | 2 +- packages/core/src/test/shared/sam/deploy.test.ts | 4 ++-- packages/core/src/test/shared/sam/sync.test.ts | 4 ++-- packages/core/src/test/shared/ui/sam/stackPrompter.test.ts | 2 +- 15 files changed, 16 insertions(+), 16 deletions(-) rename packages/core/src/shared/clients/{cloudFormationClient.ts => cloudFormation.ts} (100%) diff --git a/packages/core/src/dynamicResources/awsResourceManager.ts b/packages/core/src/dynamicResources/awsResourceManager.ts index 76a45996f28..6dbcc64e1ec 100644 --- a/packages/core/src/dynamicResources/awsResourceManager.ts +++ b/packages/core/src/dynamicResources/awsResourceManager.ts @@ -6,7 +6,7 @@ import { writeFileSync } from 'fs' // eslint-disable-line no-restricted-imports import * as path from 'path' import * as vscode from 'vscode' -import { CloudFormationClient } from '../shared/clients/cloudFormationClient' +import { CloudFormationClient } from '../shared/clients/cloudFormation' import { getNonexistentFilename, makeTemporaryToolkitFolder, diff --git a/packages/core/src/dynamicResources/explorer/nodes/resourcesNode.ts b/packages/core/src/dynamicResources/explorer/nodes/resourcesNode.ts index 65eefb27746..98ac509a50c 100644 --- a/packages/core/src/dynamicResources/explorer/nodes/resourcesNode.ts +++ b/packages/core/src/dynamicResources/explorer/nodes/resourcesNode.ts @@ -5,7 +5,7 @@ import * as vscode from 'vscode' import * as nls from 'vscode-nls' -import { CloudFormationClient, CloudFormationClient } from '../../../shared/clients/cloudFormationClient' +import { CloudFormationClient, CloudFormationClient } from '../../../shared/clients/cloudFormation' import { AWSTreeNodeBase } from '../../../shared/treeview/nodes/awsTreeNodeBase' import { PlaceholderNode } from '../../../shared/treeview/nodes/placeholderNode' import { makeChildrenNodes } from '../../../shared/treeview/utils' diff --git a/packages/core/src/lambda/commands/deleteCloudFormation.ts b/packages/core/src/lambda/commands/deleteCloudFormation.ts index fd22ee53b47..035e7110c86 100644 --- a/packages/core/src/lambda/commands/deleteCloudFormation.ts +++ b/packages/core/src/lambda/commands/deleteCloudFormation.ts @@ -7,7 +7,7 @@ import * as nls from 'vscode-nls' const localize = nls.loadMessageBundle() import * as vscode from 'vscode' -import { CloudFormationClient } from '../../shared/clients/cloudFormationClient' +import { CloudFormationClient } from '../../shared/clients/cloudFormation' import * as localizedText from '../../shared/localizedText' import { getLogger, Logger } from '../../shared/logger/logger' diff --git a/packages/core/src/lambda/explorer/cloudFormationNodes.ts b/packages/core/src/lambda/explorer/cloudFormationNodes.ts index 8aa308fbbcf..17b55ccba37 100644 --- a/packages/core/src/lambda/explorer/cloudFormationNodes.ts +++ b/packages/core/src/lambda/explorer/cloudFormationNodes.ts @@ -9,7 +9,7 @@ const localize = nls.loadMessageBundle() import { CloudFormation, Lambda } from 'aws-sdk' import * as os from 'os' import * as vscode from 'vscode' -import { CloudFormationClient } from '../../shared/clients/cloudFormationClient' +import { CloudFormationClient } from '../../shared/clients/cloudFormation' import { DefaultLambdaClient } from '../../shared/clients/lambdaClient' import { AWSResourceNode } from '../../shared/treeview/nodes/awsResourceNode' diff --git a/packages/core/src/lambda/utils.ts b/packages/core/src/lambda/utils.ts index 5018a221929..8bf3fe4fa3a 100644 --- a/packages/core/src/lambda/utils.ts +++ b/packages/core/src/lambda/utils.ts @@ -9,7 +9,7 @@ const localize = nls.loadMessageBundle() import xml2js = require('xml2js') import { CloudFormation, Lambda } from 'aws-sdk' import * as vscode from 'vscode' -import { CloudFormationClient } from '../shared/clients/cloudFormationClient' +import { CloudFormationClient } from '../shared/clients/cloudFormation' import { LambdaClient } from '../shared/clients/lambdaClient' import { getFamily, getNodeMajorVersion, RuntimeFamily } from './models/samLambdaRuntime' import { getLogger } from '../shared/logger/logger' diff --git a/packages/core/src/shared/clients/cloudFormationClient.ts b/packages/core/src/shared/clients/cloudFormation.ts similarity index 100% rename from packages/core/src/shared/clients/cloudFormationClient.ts rename to packages/core/src/shared/clients/cloudFormation.ts diff --git a/packages/core/src/shared/sam/deploy.ts b/packages/core/src/shared/sam/deploy.ts index 4634a19b16e..ea0c3056fae 100644 --- a/packages/core/src/shared/sam/deploy.ts +++ b/packages/core/src/shared/sam/deploy.ts @@ -8,7 +8,7 @@ import { AWSTreeNodeBase } from '../treeview/nodes/awsTreeNodeBase' import { TreeNode, isTreeNode } from '../treeview/resourceTreeDataProvider' import globals from '../../shared/extensionGlobals' import { ToolkitError } from '../../shared/errors' -import { CloudFormationClient } from '../clients/cloudFormationClient' +import { CloudFormationClient } from '../clients/cloudFormation' import { S3Client } from '../clients/s3' import { samDeployUrl } from '../constants' import { getSpawnEnv } from '../env/resolveEnv' diff --git a/packages/core/src/shared/sam/sync.ts b/packages/core/src/shared/sam/sync.ts index ff1e979c210..60c742e9171 100644 --- a/packages/core/src/shared/sam/sync.ts +++ b/packages/core/src/shared/sam/sync.ts @@ -10,7 +10,7 @@ import * as path from 'path' import * as localizedText from '../localizedText' import { S3Client } from '../clients/s3' import { DataQuickPickItem, createMultiPick, createQuickPick } from '../ui/pickerPrompter' -import { CloudFormationClient } from '../clients/cloudFormationClient' +import { CloudFormationClient } from '../clients/cloudFormation' import * as CloudFormation from '../cloudformation/cloudformation' import { DefaultEcrClient } from '../clients/ecrClient' import { createRegionPrompter } from '../ui/common/region' diff --git a/packages/core/src/shared/ui/sam/stackPrompter.ts b/packages/core/src/shared/ui/sam/stackPrompter.ts index 58efdcdf08e..be1350489c5 100644 --- a/packages/core/src/shared/ui/sam/stackPrompter.ts +++ b/packages/core/src/shared/ui/sam/stackPrompter.ts @@ -4,7 +4,7 @@ */ import { StackSummary } from 'aws-sdk/clients/cloudformation' import { getAwsConsoleUrl } from '../../awsConsole' -import { CloudFormationClient } from '../../clients/cloudFormationClient' +import { CloudFormationClient } from '../../clients/cloudFormation' import * as vscode from 'vscode' import { createCommonButtons } from '../buttons' import { createQuickPick } from '../pickerPrompter' diff --git a/packages/core/src/test/dynamicResources/awsResourceManager.test.ts b/packages/core/src/test/dynamicResources/awsResourceManager.test.ts index 5ae80c8eab6..b5ec86cd2a3 100644 --- a/packages/core/src/test/dynamicResources/awsResourceManager.test.ts +++ b/packages/core/src/test/dynamicResources/awsResourceManager.test.ts @@ -13,7 +13,7 @@ import { ResourceNode } from '../../dynamicResources/explorer/nodes/resourceNode import { ResourceTypeNode } from '../../dynamicResources/explorer/nodes/resourceTypeNode' import { formatResourceModel, AwsResourceManager } from '../../dynamicResources/awsResourceManager' import { CloudControlClient, DefaultCloudControlClient } from '../../shared/clients/cloudControlClient' -import { CloudFormationClient, CloudFormationClient } from '../../shared/clients/cloudFormationClient' +import { CloudFormationClient, CloudFormationClient } from '../../shared/clients/cloudFormation' import { makeTemporaryToolkitFolder, readFileAsString } from '../../shared/filesystemUtilities' import { FakeExtensionContext } from '../fakeExtensionContext' import { existsSync } from 'fs' // eslint-disable-line no-restricted-imports diff --git a/packages/core/src/test/dynamicResources/explorer/moreResourcesNode.test.ts b/packages/core/src/test/dynamicResources/explorer/moreResourcesNode.test.ts index 4719c0e87db..90438e0b7ee 100644 --- a/packages/core/src/test/dynamicResources/explorer/moreResourcesNode.test.ts +++ b/packages/core/src/test/dynamicResources/explorer/moreResourcesNode.test.ts @@ -7,7 +7,7 @@ import assert from 'assert' import * as vscode from 'vscode' import { ResourcesNode } from '../../../dynamicResources/explorer/nodes/resourcesNode' import { ResourceTypeNode } from '../../../dynamicResources/explorer/nodes/resourceTypeNode' -import { CloudFormationClient } from '../../../shared/clients/cloudFormationClient' +import { CloudFormationClient } from '../../../shared/clients/cloudFormation' import { assertNodeListOnlyHasPlaceholderNode } from '../../utilities/explorerNodeAssertions' import { asyncGenerator } from '../../../shared/utilities/collectionUtils' import { CloudFormation } from 'aws-sdk' diff --git a/packages/core/src/test/lambda/explorer/cloudFormationNodes.test.ts b/packages/core/src/test/lambda/explorer/cloudFormationNodes.test.ts index 1f768caa1bc..be2bb7cb17b 100644 --- a/packages/core/src/test/lambda/explorer/cloudFormationNodes.test.ts +++ b/packages/core/src/test/lambda/explorer/cloudFormationNodes.test.ts @@ -12,7 +12,7 @@ import { contextValueCloudformationLambdaFunction, } from '../../../lambda/explorer/cloudFormationNodes' import { LambdaFunctionNode } from '../../../lambda/explorer/lambdaFunctionNode' -import { CloudFormationClient } from '../../../shared/clients/cloudFormationClient' +import { CloudFormationClient } from '../../../shared/clients/cloudFormation' import { DefaultLambdaClient } from '../../../shared/clients/lambdaClient' import globals from '../../../shared/extensionGlobals' import { TestAWSTreeNode } from '../../shared/treeview/nodes/testAWSTreeNode' diff --git a/packages/core/src/test/shared/sam/deploy.test.ts b/packages/core/src/test/shared/sam/deploy.test.ts index 77c4a48b008..cad98b77fd7 100644 --- a/packages/core/src/test/shared/sam/deploy.test.ts +++ b/packages/core/src/test/shared/sam/deploy.test.ts @@ -13,13 +13,13 @@ import { samconfigCompleteData, samconfigInvalidData, validTemplateData } from ' import * as SamUtilsModule from '../../../shared/sam/utils' import assert from 'assert' import { getTestWindow } from '../vscode/window' -import { CloudFormationClient } from '../../../shared/clients/cloudFormationClient' +import { CloudFormationClient } from '../../../shared/clients/cloudFormation' import { intoCollection } from '../../../shared/utilities/collectionUtils' import { clickBackButton, createPromptHandler, PrompterTester } from '../wizards/prompterTester' import { RegionNode } from '../../../awsexplorer/regionNode' import { createTestRegionProvider } from '../regions/testUtil' import { S3Client } from '../../../shared/clients/s3' -import * as CloudFormationClientModule from '../../../shared/clients/cloudFormationClient' +import * as CloudFormationClientModule from '../../../shared/clients/cloudFormation' import * as S3ClientModule from '../../../shared/clients/s3' import * as ProcessUtilsModule from '../../../shared/utilities/processUtils' import * as ProcessTerminalModule from '../../../shared/sam/processTerminal' diff --git a/packages/core/src/test/shared/sam/sync.test.ts b/packages/core/src/test/shared/sam/sync.test.ts index 66b9ca32822..ea75d79f396 100644 --- a/packages/core/src/test/shared/sam/sync.test.ts +++ b/packages/core/src/test/shared/sam/sync.test.ts @@ -11,7 +11,7 @@ import * as SamConfigModule from '../../../shared/sam/config' import * as ResolveEnvModule from '../../../shared/env/resolveEnv' import * as ProcessUtilsModule from '../../../shared/utilities/processUtils' import { CancellationError } from '../../../shared/utilities/timeoutUtils' -import * as CloudFormationClientModule from '../../../shared/clients/cloudFormationClient' +import * as CloudFormationClientModule from '../../../shared/clients/cloudFormation' import { createEnvironmentPrompter, @@ -44,7 +44,7 @@ import { getTestWindow } from '../vscode/window' import { S3Client } from '../../../shared/clients/s3' import { RequiredProps } from '../../../shared/utilities/tsUtils' import S3 from 'aws-sdk/clients/s3' -import { CloudFormationClient } from '../../../shared/clients/cloudFormationClient' +import { CloudFormationClient } from '../../../shared/clients/cloudFormation' import CloudFormation from 'aws-sdk/clients/cloudformation' import { intoCollection } from '../../../shared/utilities/collectionUtils' import { SamConfig, Environment, parseConfig } from '../../../shared/sam/config' diff --git a/packages/core/src/test/shared/ui/sam/stackPrompter.test.ts b/packages/core/src/test/shared/ui/sam/stackPrompter.test.ts index 020d43f7b55..584e4b9a1ce 100644 --- a/packages/core/src/test/shared/ui/sam/stackPrompter.test.ts +++ b/packages/core/src/test/shared/ui/sam/stackPrompter.test.ts @@ -10,7 +10,7 @@ import * as vscode from 'vscode' import * as AwsConsoleModule from '../../../../shared/awsConsole' import * as SamUtilsModule from '../../../../shared/sam/utils' import * as ButtonsModule from '../../../../shared/ui/buttons' -import { CloudFormationClient } from '../../../../shared/clients/cloudFormationClient' +import { CloudFormationClient } from '../../../../shared/clients/cloudFormation' import { samSyncUrl } from '../../../../shared/constants' import { createStackPrompter } from '../../../../shared/ui/sam/stackPrompter' import { intoCollection } from '../../../../shared/utilities/collectionUtils' From d202737a8b38b9e9169c4d7031700a466f4ae5c1 Mon Sep 17 00:00:00 2001 From: hkobew Date: Fri, 14 Mar 2025 14:37:26 -0400 Subject: [PATCH 03/10] refactor: migrate first function --- .../explorer/nodes/resourcesNode.ts | 2 +- .../core/src/shared/clients/cloudFormation.ts | 18 ++++++++---------- .../awsResourceManager.test.ts | 2 +- .../core/src/test/shared/sam/deploy.test.ts | 2 +- packages/core/src/test/shared/sam/sync.test.ts | 4 ++-- 5 files changed, 13 insertions(+), 15 deletions(-) diff --git a/packages/core/src/dynamicResources/explorer/nodes/resourcesNode.ts b/packages/core/src/dynamicResources/explorer/nodes/resourcesNode.ts index 98ac509a50c..dcc191eaf76 100644 --- a/packages/core/src/dynamicResources/explorer/nodes/resourcesNode.ts +++ b/packages/core/src/dynamicResources/explorer/nodes/resourcesNode.ts @@ -5,7 +5,7 @@ import * as vscode from 'vscode' import * as nls from 'vscode-nls' -import { CloudFormationClient, CloudFormationClient } from '../../../shared/clients/cloudFormation' +import { CloudFormationClient } from '../../../shared/clients/cloudFormation' import { AWSTreeNodeBase } from '../../../shared/treeview/nodes/awsTreeNodeBase' import { PlaceholderNode } from '../../../shared/treeview/nodes/placeholderNode' import { makeChildrenNodes } from '../../../shared/treeview/utils' diff --git a/packages/core/src/shared/clients/cloudFormation.ts b/packages/core/src/shared/clients/cloudFormation.ts index 99183efe460..e5251527ca8 100644 --- a/packages/core/src/shared/clients/cloudFormation.ts +++ b/packages/core/src/shared/clients/cloudFormation.ts @@ -4,22 +4,20 @@ */ import { CloudFormation } from 'aws-sdk' +import * as CloudFormationV3 from '@aws-sdk/client-cloudformation' import globals from '../extensionGlobals' import { AsyncCollection } from '../utilities/asyncCollection' import { pageableToCollection } from '../utilities/collectionUtils' import { isNonNullable } from '../utilities/tsUtils' +import { ClientWrapper } from './clientWrapper' -export class CloudFormationClient { - public constructor(public readonly regionCode: string) {} - - public async deleteStack(name: string): Promise { - const client = await this.createSdkClient() +export class CloudFormationClient extends ClientWrapper { + public constructor(regionCode: string) { + super(regionCode, CloudFormationV3.CloudFormationClient) + } - await client - .deleteStack({ - StackName: name, - }) - .promise() + public async deleteStack(name: string): Promise { + return await this.makeRequest(CloudFormationV3.DeleteStackCommand, { StackName: name }) } public async describeType(typeName: string): Promise { diff --git a/packages/core/src/test/dynamicResources/awsResourceManager.test.ts b/packages/core/src/test/dynamicResources/awsResourceManager.test.ts index b5ec86cd2a3..9970334da36 100644 --- a/packages/core/src/test/dynamicResources/awsResourceManager.test.ts +++ b/packages/core/src/test/dynamicResources/awsResourceManager.test.ts @@ -13,7 +13,7 @@ import { ResourceNode } from '../../dynamicResources/explorer/nodes/resourceNode import { ResourceTypeNode } from '../../dynamicResources/explorer/nodes/resourceTypeNode' import { formatResourceModel, AwsResourceManager } from '../../dynamicResources/awsResourceManager' import { CloudControlClient, DefaultCloudControlClient } from '../../shared/clients/cloudControlClient' -import { CloudFormationClient, CloudFormationClient } from '../../shared/clients/cloudFormation' +import { CloudFormationClient } from '../../shared/clients/cloudFormation' import { makeTemporaryToolkitFolder, readFileAsString } from '../../shared/filesystemUtilities' import { FakeExtensionContext } from '../fakeExtensionContext' import { existsSync } from 'fs' // eslint-disable-line no-restricted-imports diff --git a/packages/core/src/test/shared/sam/deploy.test.ts b/packages/core/src/test/shared/sam/deploy.test.ts index cad98b77fd7..230dc5d66cf 100644 --- a/packages/core/src/test/shared/sam/deploy.test.ts +++ b/packages/core/src/test/shared/sam/deploy.test.ts @@ -53,7 +53,7 @@ describe('SAM DeployWizard', async function () { // Simulate return of deployed stacks mockDefaultCFNClient = sandbox.createStubInstance(CloudFormationClientModule.CloudFormationClient) - sandbox.stub(CloudFormationClientModule, 'DefaultCloudFormationClient').returns(mockDefaultCFNClient) + sandbox.stub(CloudFormationClientModule, 'CloudFormationClient').returns(mockDefaultCFNClient) mockDefaultCFNClient.listAllStacks.returns(intoCollection(stackSummaries)) // Simulate return of list bucket diff --git a/packages/core/src/test/shared/sam/sync.test.ts b/packages/core/src/test/shared/sam/sync.test.ts index ea75d79f396..f276ad841a9 100644 --- a/packages/core/src/test/shared/sam/sync.test.ts +++ b/packages/core/src/test/shared/sam/sync.test.ts @@ -144,7 +144,7 @@ describe('SAM SyncWizard', async () => { // Simulate return of deployed stacks mockDefaultCFNClient = sandbox.createStubInstance(CloudFormationClientModule.CloudFormationClient) - sandbox.stub(CloudFormationClientModule, 'DefaultCloudFormationClient').returns(mockDefaultCFNClient) + sandbox.stub(CloudFormationClientModule, 'CloudFormationClient').returns(mockDefaultCFNClient) mockDefaultCFNClient.listAllStacks.returns(intoCollection(stackSummaries)) // Simulate return of list bucket @@ -1094,7 +1094,7 @@ describe('SAM runSync', () => { // Simulate return of deployed stacks mockDefaultCFNClient = sandbox.createStubInstance(CloudFormationClientModule.CloudFormationClient) - sandbox.stub(CloudFormationClientModule, 'DefaultCloudFormationClient').returns(mockDefaultCFNClient) + sandbox.stub(CloudFormationClientModule, 'CloudFormationClient').returns(mockDefaultCFNClient) mockDefaultCFNClient.listAllStacks.returns(intoCollection(stackSummaries)) // Simulate return of list bucket From e93c2aa4c69cdeeb15d8ce2d59ea0ceae557885d Mon Sep 17 00:00:00 2001 From: hkobew Date: Fri, 14 Mar 2025 15:16:32 -0400 Subject: [PATCH 04/10] refactor: update listStacks --- .../lambda/explorer/cloudFormationNodes.ts | 6 ++-- packages/core/src/lambda/utils.ts | 8 ++--- .../core/src/shared/clients/cloudFormation.ts | 30 ++++++++++++++----- .../explorer/cloudFormationNodes.test.ts | 10 +++++-- 4 files changed, 36 insertions(+), 18 deletions(-) diff --git a/packages/core/src/lambda/explorer/cloudFormationNodes.ts b/packages/core/src/lambda/explorer/cloudFormationNodes.ts index 17b55ccba37..6fd8c4d0e96 100644 --- a/packages/core/src/lambda/explorer/cloudFormationNodes.ts +++ b/packages/core/src/lambda/explorer/cloudFormationNodes.ts @@ -9,7 +9,7 @@ const localize = nls.loadMessageBundle() import { CloudFormation, Lambda } from 'aws-sdk' import * as os from 'os' import * as vscode from 'vscode' -import { CloudFormationClient } from '../../shared/clients/cloudFormation' +import { CloudFormationClient, StackSummary } from '../../shared/clients/cloudFormation' import { DefaultLambdaClient } from '../../shared/clients/lambdaClient' import { AWSResourceNode } from '../../shared/treeview/nodes/awsResourceNode' @@ -66,7 +66,7 @@ export class CloudFormationStackNode extends AWSTreeNodeBase implements AWSResou public constructor( public readonly parent: AWSTreeNodeBase, public override readonly regionCode: string, - private stackSummary: CloudFormation.StackSummary, + private stackSummary: StackSummary, private readonly lambdaClient = new DefaultLambdaClient(regionCode), private readonly cloudformationClient = new CloudFormationClient(regionCode) ) { @@ -114,7 +114,7 @@ export class CloudFormationStackNode extends AWSTreeNodeBase implements AWSResou }) } - public update(stackSummary: CloudFormation.StackSummary): void { + public update(stackSummary: StackSummary): void { this.stackSummary = stackSummary this.label = `${this.stackName} [${stackSummary.StackStatus}]` this.tooltip = `${this.stackName}${os.EOL}${this.stackId}` diff --git a/packages/core/src/lambda/utils.ts b/packages/core/src/lambda/utils.ts index 8bf3fe4fa3a..79054e02cac 100644 --- a/packages/core/src/lambda/utils.ts +++ b/packages/core/src/lambda/utils.ts @@ -7,9 +7,9 @@ import * as nls from 'vscode-nls' const localize = nls.loadMessageBundle() import xml2js = require('xml2js') -import { CloudFormation, Lambda } from 'aws-sdk' +import { Lambda } from 'aws-sdk' import * as vscode from 'vscode' -import { CloudFormationClient } from '../shared/clients/cloudFormation' +import { CloudFormationClient, StackSummary } from '../shared/clients/cloudFormation' import { LambdaClient } from '../shared/clients/lambdaClient' import { getFamily, getNodeMajorVersion, RuntimeFamily } from './models/samLambdaRuntime' import { getLogger } from '../shared/logger/logger' @@ -18,9 +18,7 @@ import { FileResourceFetcher } from '../shared/resourcefetcher/fileResourceFetch import { sampleRequestManifestPath } from './constants' import globals from '../shared/extensionGlobals' -export async function* listCloudFormationStacks( - client: CloudFormationClient -): AsyncIterableIterator { +export async function* listCloudFormationStacks(client: CloudFormationClient): AsyncIterableIterator { // TODO: this 'loading' message needs to go under each regional entry // in the explorer, and be removed when that region's query completes const status = vscode.window.setStatusBarMessage( diff --git a/packages/core/src/shared/clients/cloudFormation.ts b/packages/core/src/shared/clients/cloudFormation.ts index e5251527ca8..92d40d5d852 100644 --- a/packages/core/src/shared/clients/cloudFormation.ts +++ b/packages/core/src/shared/clients/cloudFormation.ts @@ -8,9 +8,13 @@ import * as CloudFormationV3 from '@aws-sdk/client-cloudformation' import globals from '../extensionGlobals' import { AsyncCollection } from '../utilities/asyncCollection' import { pageableToCollection } from '../utilities/collectionUtils' -import { isNonNullable } from '../utilities/tsUtils' +import { hasProps, isNonNullable, RequiredProps } from '../utilities/tsUtils' import { ClientWrapper } from './clientWrapper' +export interface StackSummary + extends RequiredProps { + DriftInformation: RequiredProps +} export class CloudFormationClient extends ClientWrapper { public constructor(regionCode: string) { super(regionCode, CloudFormationV3.CloudFormationClient) @@ -33,22 +37,32 @@ export class CloudFormationClient extends ClientWrapper { - const client = await this.createSdkClient() - + ): AsyncIterableIterator { const request: CloudFormation.ListStacksInput = { StackStatusFilter: statusFilter, } do { - const response: CloudFormation.ListStacksOutput = await client.listStacks(request).promise() - - if (response.StackSummaries) { - yield* response.StackSummaries + const response: CloudFormationV3.ListStacksOutput = await this.makeRequest( + CloudFormationV3.ListStacksCommand, + request + ) + + const filteredResponse = response.StackSummaries?.filter(isStackSummary) + if (filteredResponse && filteredResponse.length > 0) { + yield* filteredResponse } request.NextToken = response.NextToken } while (request.NextToken) + + function isStackSummary(s: CloudFormationV3.StackSummary | undefined): s is StackSummary { + return ( + isNonNullable(s) && + hasProps(s, 'StackName', 'CreationTime', 'StackStatus', 'DriftInformation') && + hasProps(s.DriftInformation, 'StackDriftStatus') + ) + } } public listAllStacks(request: CloudFormation.ListStacksInput = {}): AsyncCollection { diff --git a/packages/core/src/test/lambda/explorer/cloudFormationNodes.test.ts b/packages/core/src/test/lambda/explorer/cloudFormationNodes.test.ts index be2bb7cb17b..5c874681bf4 100644 --- a/packages/core/src/test/lambda/explorer/cloudFormationNodes.test.ts +++ b/packages/core/src/test/lambda/explorer/cloudFormationNodes.test.ts @@ -12,7 +12,7 @@ import { contextValueCloudformationLambdaFunction, } from '../../../lambda/explorer/cloudFormationNodes' import { LambdaFunctionNode } from '../../../lambda/explorer/lambdaFunctionNode' -import { CloudFormationClient } from '../../../shared/clients/cloudFormation' +import { CloudFormationClient, StackSummary } from '../../../shared/clients/cloudFormation' import { DefaultLambdaClient } from '../../../shared/clients/lambdaClient' import globals from '../../../shared/extensionGlobals' import { TestAWSTreeNode } from '../../shared/treeview/nodes/testAWSTreeNode' @@ -44,6 +44,9 @@ function createCloudFormationClient(...stackNames: string[]) { StackName: name, CreationTime: new globals.clock.Date(), StackStatus: 'CREATE_COMPLETE', + DriftInformation: { + StackDriftStatus: 'UNKNOWN', + }, } }) ) @@ -53,12 +56,15 @@ function createCloudFormationClient(...stackNames: string[]) { } describe('CloudFormationStackNode', function () { - function createStackSummary() { + function createStackSummary(): StackSummary { return { CreationTime: new globals.clock.Date(), StackId: '1', StackName: 'myStack', StackStatus: 'UPDATE_COMPLETE', + DriftInformation: { + StackDriftStatus: 'UNKNOWN', + }, } } From fcf3283f647789af17e526655c9f3c978a0f5eb6 Mon Sep 17 00:00:00 2001 From: hkobew Date: Fri, 14 Mar 2025 15:33:19 -0400 Subject: [PATCH 05/10] refactor: migrate listAllStacks --- .../core/src/shared/clients/cloudFormation.ts | 29 +++++++++---------- .../core/src/test/shared/sam/deploy.test.ts | 10 +++---- .../core/src/test/shared/sam/sync.test.ts | 9 +++--- .../test/shared/ui/sam/stackPrompter.test.ts | 11 ++++--- 4 files changed, 28 insertions(+), 31 deletions(-) diff --git a/packages/core/src/shared/clients/cloudFormation.ts b/packages/core/src/shared/clients/cloudFormation.ts index 92d40d5d852..15617e17949 100644 --- a/packages/core/src/shared/clients/cloudFormation.ts +++ b/packages/core/src/shared/clients/cloudFormation.ts @@ -7,7 +7,6 @@ import { CloudFormation } from 'aws-sdk' import * as CloudFormationV3 from '@aws-sdk/client-cloudformation' import globals from '../extensionGlobals' import { AsyncCollection } from '../utilities/asyncCollection' -import { pageableToCollection } from '../utilities/collectionUtils' import { hasProps, isNonNullable, RequiredProps } from '../utilities/tsUtils' import { ClientWrapper } from './clientWrapper' @@ -55,22 +54,14 @@ export class CloudFormationClient extends ClientWrapper { - const client = this.createSdkClient() - const requester = async (req: CloudFormation.ListStacksInput) => (await client).listStacks(req).promise() - const collection = pageableToCollection(requester, request, 'NextToken', 'StackSummaries') - - return collection.filter(isNonNullable) + public listAllStacks(request: CloudFormationV3.ListStacksInput = {}): AsyncCollection { + return this.makePaginatedRequest( + CloudFormationV3.paginateListStacks, + request, + (page) => page.StackSummaries + ).map((s) => s.filter(isStackSummary)) } public async *listTypes(): AsyncIterableIterator { @@ -107,3 +98,11 @@ export class CloudFormationClient extends ClientWrapper { it('should create a prompter with existing stacks', async () => { // Arrange - const stackSummaries: CloudFormation.StackSummary[][] = [ + const stackSummaries: StackSummary[][] = [ [ { StackName: 'stack1', StackStatus: 'CREATE_COMPLETE', CreationTime: new Date(), - } as CloudFormation.StackSummary, + } as StackSummary, { StackName: 'stack2', StackStatus: 'CREATE_COMPLETE', CreationTime: new Date(), - } as CloudFormation.StackSummary, + } as StackSummary, { StackName: 'stack3', StackStatus: 'CREATE_COMPLETE', CreationTime: new Date(), - } as CloudFormation.StackSummary, + } as StackSummary, ], ] const expectedItems = [ From bd393803efc56813bad961b08f97eba71c959b7e Mon Sep 17 00:00:00 2001 From: hkobew Date: Fri, 14 Mar 2025 15:40:15 -0400 Subject: [PATCH 06/10] refactor: migrate more pieces of the client --- .../core/src/shared/clients/cloudFormation.ts | 27 ++++++++++--------- .../explorer/cloudFormationNodes.test.ts | 7 +++-- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/packages/core/src/shared/clients/cloudFormation.ts b/packages/core/src/shared/clients/cloudFormation.ts index 15617e17949..9123c039a5a 100644 --- a/packages/core/src/shared/clients/cloudFormation.ts +++ b/packages/core/src/shared/clients/cloudFormation.ts @@ -14,6 +14,12 @@ export interface StackSummary extends RequiredProps { DriftInformation: RequiredProps } + +export type StackResource = RequiredProps + +export interface DescribeStackResourcesOutput extends CloudFormationV3.DescribeStackResourcesOutput { + StackResources: StackResource[] +} export class CloudFormationClient extends ClientWrapper { public constructor(regionCode: string) { super(regionCode, CloudFormationV3.CloudFormationClient) @@ -64,17 +70,18 @@ export class CloudFormationClient extends ClientWrapper s.filter(isStackSummary)) } - public async *listTypes(): AsyncIterableIterator { - const client = await this.createSdkClient() - - const request: CloudFormation.ListTypesInput = { + public async *listTypes(): AsyncIterableIterator { + const request: CloudFormationV3.ListTypesInput = { DeprecatedStatus: 'LIVE', Type: 'RESOURCE', Visibility: 'PUBLIC', } do { - const response: CloudFormation.ListTypesOutput = await client.listTypes(request).promise() + const response: CloudFormationV3.ListTypesOutput = await this.makeRequest( + CloudFormationV3.ListTypesCommand, + request + ) if (response.TypeSummaries) { yield* response.TypeSummaries @@ -84,14 +91,8 @@ export class CloudFormationClient extends ClientWrapper { - const client = await this.createSdkClient() - - return await client - .describeStackResources({ - StackName: name, - }) - .promise() + public async describeStackResources(name: string): Promise { + return await this.makeRequest(CloudFormationV3.DescribeStackResourcesCommand, { StackName: name }) } private async createSdkClient(): Promise { diff --git a/packages/core/src/test/lambda/explorer/cloudFormationNodes.test.ts b/packages/core/src/test/lambda/explorer/cloudFormationNodes.test.ts index 5c874681bf4..8cbeedf25f3 100644 --- a/packages/core/src/test/lambda/explorer/cloudFormationNodes.test.ts +++ b/packages/core/src/test/lambda/explorer/cloudFormationNodes.test.ts @@ -4,7 +4,6 @@ */ import assert from 'assert' -import { CloudFormation } from 'aws-sdk' import * as os from 'os' import { CloudFormationNode, @@ -12,7 +11,7 @@ import { contextValueCloudformationLambdaFunction, } from '../../../lambda/explorer/cloudFormationNodes' import { LambdaFunctionNode } from '../../../lambda/explorer/lambdaFunctionNode' -import { CloudFormationClient, StackSummary } from '../../../shared/clients/cloudFormation' +import { CloudFormationClient, StackResource, StackSummary } from '../../../shared/clients/cloudFormation' import { DefaultLambdaClient } from '../../../shared/clients/lambdaClient' import globals from '../../../shared/extensionGlobals' import { TestAWSTreeNode } from '../../shared/treeview/nodes/testAWSTreeNode' @@ -78,11 +77,11 @@ describe('CloudFormationStackNode', function () { return new CloudFormationStackNode(parentNode, regionCode, summary, lambdaClient, cloudFormationClient) } - function generateStackResources(...functionNames: string[]): CloudFormation.StackResource[] { + function generateStackResources(...functionNames: string[]): StackResource[] { return functionNames.map((name) => ({ PhysicalResourceId: name, LogicalResourceId: name, - ResourceStatus: 'CREATED', + ResourceStatus: 'CREATE_COMPLETE', ResourceType: 'Lambda::Function', Timestamp: new globals.clock.Date(), })) From d52bc7f448cb3dc5527418032230f4b8fb28464a Mon Sep 17 00:00:00 2001 From: hkobew Date: Fri, 14 Mar 2025 15:55:22 -0400 Subject: [PATCH 07/10] refactor: remove final imports --- .../explorer/nodes/resourcesNode.ts | 4 ++-- .../core/src/shared/clients/cloudFormation.ts | 21 ++++--------------- .../awsResourceManager.test.ts | 5 +++-- .../explorer/moreResourcesNode.test.ts | 6 +++--- 4 files changed, 12 insertions(+), 24 deletions(-) diff --git a/packages/core/src/dynamicResources/explorer/nodes/resourcesNode.ts b/packages/core/src/dynamicResources/explorer/nodes/resourcesNode.ts index dcc191eaf76..77fb2ce98b6 100644 --- a/packages/core/src/dynamicResources/explorer/nodes/resourcesNode.ts +++ b/packages/core/src/dynamicResources/explorer/nodes/resourcesNode.ts @@ -11,10 +11,10 @@ import { PlaceholderNode } from '../../../shared/treeview/nodes/placeholderNode' import { makeChildrenNodes } from '../../../shared/treeview/utils' import { toArrayAsync, updateInPlace } from '../../../shared/utilities/collectionUtils' import { ResourceTypeNode } from './resourceTypeNode' -import { CloudFormation } from 'aws-sdk' import { CloudControlClient, DefaultCloudControlClient } from '../../../shared/clients/cloudControlClient' import { memoizedGetResourceTypes, ResourceTypeMetadata } from '../../model/resources' import { ResourcesSettings } from '../../commands/configure' +import { TypeSummary } from '@aws-sdk/client-cloudformation' const localize = nls.loadMessageBundle() @@ -62,7 +62,7 @@ export class ResourcesNode extends AWSTreeNodeBase { const types = await toArrayAsync(this.cloudFormation.listTypes()) types.sort((a, b) => (a.LastUpdated?.getTime() ?? 0) - (b.LastUpdated?.getTime() ?? 0)) - const availableTypes: Map = new Map() + const availableTypes: Map = new Map() for (const type of types) { if (type.TypeName) { availableTypes.set(type.TypeName!, type) diff --git a/packages/core/src/shared/clients/cloudFormation.ts b/packages/core/src/shared/clients/cloudFormation.ts index 9123c039a5a..b46ed8730e9 100644 --- a/packages/core/src/shared/clients/cloudFormation.ts +++ b/packages/core/src/shared/clients/cloudFormation.ts @@ -3,9 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { CloudFormation } from 'aws-sdk' import * as CloudFormationV3 from '@aws-sdk/client-cloudformation' -import globals from '../extensionGlobals' import { AsyncCollection } from '../utilities/asyncCollection' import { hasProps, isNonNullable, RequiredProps } from '../utilities/tsUtils' import { ClientWrapper } from './clientWrapper' @@ -29,21 +27,14 @@ export class CloudFormationClient extends ClientWrapper { - const client = await this.createSdkClient() - - return await client - .describeType({ - Type: 'RESOURCE', - TypeName: typeName, - }) - .promise() + public async describeType(typeName: string): Promise { + return await this.makeRequest(CloudFormationV3.DescribeTypeCommand, { TypeName: typeName }) } public async *listStacks( - statusFilter: string[] = ['CREATE_COMPLETE', 'UPDATE_COMPLETE'] + statusFilter: CloudFormationV3.StackStatus[] = ['CREATE_COMPLETE', 'UPDATE_COMPLETE'] ): AsyncIterableIterator { - const request: CloudFormation.ListStacksInput = { + const request: CloudFormationV3.ListStacksInput = { StackStatusFilter: statusFilter, } @@ -94,10 +85,6 @@ export class CloudFormationClient extends ClientWrapper { return await this.makeRequest(CloudFormationV3.DescribeStackResourcesCommand, { StackName: name }) } - - private async createSdkClient(): Promise { - return await globals.sdkClientBuilder.createAwsService(CloudFormation, undefined, this.regionCode) - } } function isStackSummary(s: CloudFormationV3.StackSummary | undefined): s is StackSummary { diff --git a/packages/core/src/test/dynamicResources/awsResourceManager.test.ts b/packages/core/src/test/dynamicResources/awsResourceManager.test.ts index 9970334da36..84399b10198 100644 --- a/packages/core/src/test/dynamicResources/awsResourceManager.test.ts +++ b/packages/core/src/test/dynamicResources/awsResourceManager.test.ts @@ -20,8 +20,9 @@ import { existsSync } from 'fs' // eslint-disable-line no-restricted-imports import { ResourceTypeMetadata } from '../../dynamicResources/model/resources' import globals from '../../shared/extensionGlobals' import { Stub, stub } from '../utilities/stubber' -import { CloudControl, CloudFormation } from 'aws-sdk' +import { CloudControl } from 'aws-sdk' import { fs } from '../../shared' +import { DescribeTypeOutput } from '@aws-sdk/client-cloudformation' describe('ResourceManager', function () { let sandbox: sinon.SinonSandbox @@ -237,7 +238,7 @@ describe('ResourceManager', function () { }) cloudFormation.describeType.callsFake(async (name: string) => { if (name === fakeTypeName) { - return { Schema: '{}' } as any as CloudFormation.DescribeTypeOutput + return { Schema: '{}' } as any as DescribeTypeOutput } throw new Error() }) diff --git a/packages/core/src/test/dynamicResources/explorer/moreResourcesNode.test.ts b/packages/core/src/test/dynamicResources/explorer/moreResourcesNode.test.ts index 90438e0b7ee..74452c586e5 100644 --- a/packages/core/src/test/dynamicResources/explorer/moreResourcesNode.test.ts +++ b/packages/core/src/test/dynamicResources/explorer/moreResourcesNode.test.ts @@ -10,11 +10,11 @@ import { ResourceTypeNode } from '../../../dynamicResources/explorer/nodes/resou import { CloudFormationClient } from '../../../shared/clients/cloudFormation' import { assertNodeListOnlyHasPlaceholderNode } from '../../utilities/explorerNodeAssertions' import { asyncGenerator } from '../../../shared/utilities/collectionUtils' -import { CloudFormation } from 'aws-sdk' import { CloudControlClient } from '../../../shared/clients/cloudControlClient' import { Settings } from '../../../shared/settings' import { ResourcesSettings } from '../../../dynamicResources/commands/configure' import sinon from 'sinon' +import { TypeSummary } from '@aws-sdk/client-cloudformation' const unsortedText = ['zebra', 'Antelope', 'aardvark', 'elephant'] const sortedText = ['aardvark', 'Antelope', 'elephant', 'zebra'] @@ -101,8 +101,8 @@ describe('ResourcesNode', function () { function prepareMock(resourceTypes: string[]) { const listStub = sinon.stub().returns( - asyncGenerator( - resourceTypes.map((resourceType) => { + asyncGenerator( + resourceTypes.map((resourceType) => { return { TypeName: resourceType, } From a833814cbf857c80c8e7b28c5c17056ffdc0929b Mon Sep 17 00:00:00 2001 From: hkobew Date: Fri, 14 Mar 2025 15:56:17 -0400 Subject: [PATCH 08/10] refactor: remove v3 suffix --- .../core/src/shared/clients/cloudFormation.ts | 52 +++++++++---------- 1 file changed, 25 insertions(+), 27 deletions(-) diff --git a/packages/core/src/shared/clients/cloudFormation.ts b/packages/core/src/shared/clients/cloudFormation.ts index b46ed8730e9..f04d2fdeb08 100644 --- a/packages/core/src/shared/clients/cloudFormation.ts +++ b/packages/core/src/shared/clients/cloudFormation.ts @@ -3,44 +3,44 @@ * SPDX-License-Identifier: Apache-2.0 */ -import * as CloudFormationV3 from '@aws-sdk/client-cloudformation' +import * as CloudFormation from '@aws-sdk/client-cloudformation' import { AsyncCollection } from '../utilities/asyncCollection' import { hasProps, isNonNullable, RequiredProps } from '../utilities/tsUtils' import { ClientWrapper } from './clientWrapper' export interface StackSummary - extends RequiredProps { - DriftInformation: RequiredProps + extends RequiredProps { + DriftInformation: RequiredProps } -export type StackResource = RequiredProps +export type StackResource = RequiredProps -export interface DescribeStackResourcesOutput extends CloudFormationV3.DescribeStackResourcesOutput { +export interface DescribeStackResourcesOutput extends CloudFormation.DescribeStackResourcesOutput { StackResources: StackResource[] } -export class CloudFormationClient extends ClientWrapper { +export class CloudFormationClient extends ClientWrapper { public constructor(regionCode: string) { - super(regionCode, CloudFormationV3.CloudFormationClient) + super(regionCode, CloudFormation.CloudFormationClient) } - public async deleteStack(name: string): Promise { - return await this.makeRequest(CloudFormationV3.DeleteStackCommand, { StackName: name }) + public async deleteStack(name: string): Promise { + return await this.makeRequest(CloudFormation.DeleteStackCommand, { StackName: name }) } - public async describeType(typeName: string): Promise { - return await this.makeRequest(CloudFormationV3.DescribeTypeCommand, { TypeName: typeName }) + public async describeType(typeName: string): Promise { + return await this.makeRequest(CloudFormation.DescribeTypeCommand, { TypeName: typeName }) } public async *listStacks( - statusFilter: CloudFormationV3.StackStatus[] = ['CREATE_COMPLETE', 'UPDATE_COMPLETE'] + statusFilter: CloudFormation.StackStatus[] = ['CREATE_COMPLETE', 'UPDATE_COMPLETE'] ): AsyncIterableIterator { - const request: CloudFormationV3.ListStacksInput = { + const request: CloudFormation.ListStacksInput = { StackStatusFilter: statusFilter, } do { - const response: CloudFormationV3.ListStacksOutput = await this.makeRequest( - CloudFormationV3.ListStacksCommand, + const response: CloudFormation.ListStacksOutput = await this.makeRequest( + CloudFormation.ListStacksCommand, request ) @@ -53,24 +53,22 @@ export class CloudFormationClient extends ClientWrapper { - return this.makePaginatedRequest( - CloudFormationV3.paginateListStacks, - request, - (page) => page.StackSummaries - ).map((s) => s.filter(isStackSummary)) + public listAllStacks(request: CloudFormation.ListStacksInput = {}): AsyncCollection { + return this.makePaginatedRequest(CloudFormation.paginateListStacks, request, (page) => page.StackSummaries).map( + (s) => s.filter(isStackSummary) + ) } - public async *listTypes(): AsyncIterableIterator { - const request: CloudFormationV3.ListTypesInput = { + public async *listTypes(): AsyncIterableIterator { + const request: CloudFormation.ListTypesInput = { DeprecatedStatus: 'LIVE', Type: 'RESOURCE', Visibility: 'PUBLIC', } do { - const response: CloudFormationV3.ListTypesOutput = await this.makeRequest( - CloudFormationV3.ListTypesCommand, + const response: CloudFormation.ListTypesOutput = await this.makeRequest( + CloudFormation.ListTypesCommand, request ) @@ -83,11 +81,11 @@ export class CloudFormationClient extends ClientWrapper { - return await this.makeRequest(CloudFormationV3.DescribeStackResourcesCommand, { StackName: name }) + return await this.makeRequest(CloudFormation.DescribeStackResourcesCommand, { StackName: name }) } } -function isStackSummary(s: CloudFormationV3.StackSummary | undefined): s is StackSummary { +function isStackSummary(s: CloudFormation.StackSummary | undefined): s is StackSummary { return ( isNonNullable(s) && hasProps(s, 'StackName', 'CreationTime', 'StackStatus', 'DriftInformation') && From cccaed6949514e836e321b41d8b84a9cd070da39 Mon Sep 17 00:00:00 2001 From: hkobew Date: Mon, 17 Mar 2025 10:41:35 -0400 Subject: [PATCH 09/10] deps: remove old s3 import --- packages/core/src/test/shared/sam/deploy.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core/src/test/shared/sam/deploy.test.ts b/packages/core/src/test/shared/sam/deploy.test.ts index 6a3ecaf3ef1..73aed7c8d9d 100644 --- a/packages/core/src/test/shared/sam/deploy.test.ts +++ b/packages/core/src/test/shared/sam/deploy.test.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ import * as vscode from 'vscode' -import { S3 } from 'aws-sdk' +import { S3Bucket } from '../../../shared/clients/s3' import { AppNode } from '../../../awsService/appBuilder/explorer/nodes/appNode' import { assertTelemetry, getWorkspaceFolder, TestFolder } from '../../testUtil' import { DeployParams, DeployWizard, getDeployWizard, runDeploy } from '../../../shared/sam/deploy' @@ -1288,7 +1288,7 @@ describe('SAM Deploy', () => { }) const s3BucketListSummary: Array< - RequiredProps & { + RequiredProps & { readonly region: string } > = [ From abbc5b59b7ba9e2ab3f5eb0ea059a15c13b30f04 Mon Sep 17 00:00:00 2001 From: hkobew Date: Mon, 17 Mar 2025 10:49:46 -0400 Subject: [PATCH 10/10] fix: change type on test data --- packages/core/src/test/shared/sam/deploy.test.ts | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/packages/core/src/test/shared/sam/deploy.test.ts b/packages/core/src/test/shared/sam/deploy.test.ts index 73aed7c8d9d..3928f50ceb7 100644 --- a/packages/core/src/test/shared/sam/deploy.test.ts +++ b/packages/core/src/test/shared/sam/deploy.test.ts @@ -25,7 +25,6 @@ import * as ProcessUtilsModule from '../../../shared/utilities/processUtils' import * as ProcessTerminalModule from '../../../shared/sam/processTerminal' import * as ResolveEnvModule from '../../../shared/env/resolveEnv' import * as SamConfiModule from '../../../shared/sam/config' -import { RequiredProps } from '../../../shared/utilities/tsUtils' import { UserAgent as __UserAgent } from '@smithy/types' import { SamAppLocation } from '../../../awsService/appBuilder/explorer/samProject' @@ -1287,14 +1286,10 @@ describe('SAM Deploy', () => { }) }) -const s3BucketListSummary: Array< - RequiredProps & { - readonly region: string - } -> = [ - { Name: 'stack-1-bucket', region: 'us-west-2' }, - { Name: 'stack-2-bucket', region: 'us-west-2' }, - { Name: 'stack-3-bucket', region: 'us-west-2' }, +const s3BucketListSummary: Array & { readonly region: string }> = [ + { Name: 'stack-1-bucket', region: 'us-west-2', Arn: 'arn-1' }, + { Name: 'stack-2-bucket', region: 'us-west-2', Arn: 'arn-2' }, + { Name: 'stack-3-bucket', region: 'us-west-2', Arn: 'arn-3' }, ] const stackSummaries: CloudFormationClientModule.StackSummary[][] = [