Skip to content

Commit 65d6a4b

Browse files
Merge master into feature/editable_diffview
2 parents 4f1c0be + 220d65c commit 65d6a4b

File tree

7 files changed

+325
-35
lines changed

7 files changed

+325
-35
lines changed

packages/core/src/awsService/cloudformation/auth/credentials.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@ export class AwsCredentialsService implements Disposable {
1919
private client: LanguageClient | undefined
2020

2121
constructor(
22-
private stacksManager: StacksManager,
23-
private resourcesManager: ResourcesManager,
24-
private regionManager: CloudFormationRegionManager
22+
private readonly stacksManager: StacksManager,
23+
private readonly resourcesManager: ResourcesManager,
24+
private readonly regionManager: CloudFormationRegionManager
2525
) {
2626
this.authChangeListener = globals.awsContext.onDidChangeContext(() => {
2727
void this.updateCredentialsFromActiveConnection()
@@ -53,7 +53,7 @@ export class AwsCredentialsService implements Disposable {
5353
await this.client.sendRequest('aws/credentials/iam/update', encryptedRequest)
5454
}
5555

56-
void this.stacksManager.reload()
56+
this.stacksManager.clear()
5757
void this.resourcesManager.reload()
5858
}
5959

packages/core/src/awsService/cloudformation/explorer/nodes/stacksNode.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,17 +33,23 @@ export class StacksNode extends AWSTreeNodeBase {
3333
}
3434

3535
public override async getChildren(): Promise<AWSTreeNodeBase[]> {
36+
await this.stacksManager.ensureLoaded()
3637
this.updateNode()
3738
const stacks = this.stacksManager.get()
3839
const nodes = stacks.map((stack: StackSummary) => new StackNode(stack, this.changeSetsManager))
3940
return this.stacksManager.hasMore() ? [...nodes, new LoadMoreStacksNode(this)] : nodes
4041
}
4142

4243
private updateNode(): void {
43-
const count = this.stacksManager.get().length
44-
const hasMore = this.stacksManager.hasMore()
45-
this.description = hasMore ? `(${count}+)` : `(${count})`
46-
this.contextValue = hasMore ? 'stackSectionWithMore' : 'stackSection'
44+
if (this.stacksManager.isLoaded()) {
45+
const count = this.stacksManager.get().length
46+
const hasMore = this.stacksManager.hasMore()
47+
this.description = hasMore ? `(${count}+)` : `(${count})`
48+
this.contextValue = hasMore ? 'stackSectionWithMore' : 'stackSection'
49+
} else {
50+
this.description = undefined
51+
this.contextValue = 'stackSection'
52+
}
4753
}
4854

4955
public async loadMoreStacks(): Promise<void> {

packages/core/src/awsService/cloudformation/stacks/stacksManager.ts

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,14 @@ type ListStacksResult = {
2323
}
2424

2525
const ListStacksRequest = new RequestType<ListStacksParams, ListStacksResult, void>('aws/cfn/stacks')
26-
const PollIntervalMs = 1000
2726

2827
type StacksChangeListener = (stacks: StackSummary[]) => void
2928

3029
export class StacksManager implements Disposable {
3130
private stacks: StackSummary[] = []
3231
private nextToken?: string
3332
private readonly listeners: StacksChangeListener[] = []
34-
private poller?: NodeJS.Timeout
33+
private loaded = false
3534

3635
constructor(private readonly client: LanguageClient) {}
3736

@@ -51,6 +50,23 @@ export class StacksManager implements Disposable {
5150
void this.loadStacks()
5251
}
5352

53+
isLoaded() {
54+
return this.loaded
55+
}
56+
57+
async ensureLoaded() {
58+
if (!this.loaded) {
59+
await this.loadStacks()
60+
}
61+
}
62+
63+
clear() {
64+
this.stacks = []
65+
this.nextToken = undefined
66+
this.loaded = false
67+
this.notifyListeners()
68+
}
69+
5470
updateStackStatus(stackName: string, stackStatus: string) {
5571
const stack = this.stacks.find((s) => s.StackName === stackName)
5672
if (stack) {
@@ -80,21 +96,8 @@ export class StacksManager implements Disposable {
8096
}
8197
}
8298

83-
startPolling() {
84-
this.poller ??= setInterval(() => {
85-
this.reload()
86-
}, PollIntervalMs)
87-
}
88-
89-
stopPolling() {
90-
if (this.poller) {
91-
clearInterval(this.poller)
92-
this.poller = undefined
93-
}
94-
}
95-
9699
dispose() {
97-
this.stopPolling()
100+
// do nothing
98101
}
99102

100103
private async loadStacks() {
@@ -106,16 +109,14 @@ export class StacksManager implements Disposable {
106109
})
107110
this.stacks = response.stacks
108111
this.nextToken = response.nextToken
112+
this.loaded = true
109113
} catch (error) {
110114
await handleLspError(error, 'Error loading stacks')
111115
this.stacks = []
112116
this.nextToken = undefined
113117
} finally {
114118
await setContext('aws.cloudformation.refreshingStacks', false)
115119
this.notifyListeners()
116-
if (this.stacks.length === 0) {
117-
this.stopPolling()
118-
}
119120
}
120121
}
121122

packages/core/src/test/awsService/cloudformation/auth/credentials.test.ts

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ describe('AwsCredentialsService', function () {
1818

1919
beforeEach(function () {
2020
sandbox = sinon.createSandbox()
21-
mockStacksManager = { reload: sandbox.stub(), hasMore: sandbox.stub().returns(false) }
21+
mockStacksManager = { reload: sandbox.stub(), hasMore: sandbox.stub().returns(false), clear: sandbox.stub() }
2222
mockResourcesManager = { reload: sandbox.stub() }
2323
mockClient = { sendRequest: sandbox.stub() }
2424

@@ -31,13 +31,6 @@ describe('AwsCredentialsService', function () {
3131
sandbox.restore()
3232
})
3333

34-
describe('constructor', function () {
35-
it('should initialize credentials service', function () {
36-
credentialsService = new AwsCredentialsService(mockStacksManager, mockResourcesManager, mockRegionManager)
37-
assert(credentialsService !== undefined)
38-
})
39-
})
40-
4134
describe('createEncryptedCredentialsRequest', function () {
4235
beforeEach(function () {
4336
credentialsService = new AwsCredentialsService(mockStacksManager, mockResourcesManager, mockRegionManager)
@@ -89,5 +82,11 @@ describe('AwsCredentialsService', function () {
8982
// Test passes if no error thrown
9083
assert(true)
9184
})
85+
86+
it('should clear stacks', async function () {
87+
credentialsService = new AwsCredentialsService(mockStacksManager, mockResourcesManager, mockRegionManager)
88+
await credentialsService.initialize(mockClient)
89+
assert(mockStacksManager.clear.calledOnce)
90+
})
9291
})
9392
})
Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
/*!
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import assert from 'assert'
7+
import * as sinon from 'sinon'
8+
import { TreeItemCollapsibleState } from 'vscode'
9+
import { StacksNode } from '../../../../../awsService/cloudformation/explorer/nodes/stacksNode'
10+
import { StacksManager } from '../../../../../awsService/cloudformation/stacks/stacksManager'
11+
import { ChangeSetsManager } from '../../../../../awsService/cloudformation/stacks/changeSetsManager'
12+
import { StackSummary } from '@aws-sdk/client-cloudformation'
13+
14+
describe('StacksNode', function () {
15+
let stacksNode: StacksNode
16+
let mockStacksManager: sinon.SinonStubbedInstance<StacksManager>
17+
let mockChangeSetsManager: ChangeSetsManager
18+
let sandbox: sinon.SinonSandbox
19+
20+
beforeEach(function () {
21+
sandbox = sinon.createSandbox()
22+
mockStacksManager = {
23+
get: sandbox.stub(),
24+
hasMore: sandbox.stub(),
25+
isLoaded: sandbox.stub(),
26+
ensureLoaded: sandbox.stub(),
27+
loadMoreStacks: sandbox.stub(),
28+
} as any
29+
mockChangeSetsManager = {} as ChangeSetsManager
30+
})
31+
32+
afterEach(function () {
33+
sandbox.restore()
34+
})
35+
36+
describe('constructor', function () {
37+
it('should set correct properties when not loaded', function () {
38+
mockStacksManager.get.returns([])
39+
mockStacksManager.hasMore.returns(false)
40+
mockStacksManager.isLoaded.returns(false)
41+
42+
stacksNode = new StacksNode(mockStacksManager as any, mockChangeSetsManager)
43+
44+
assert.strictEqual(stacksNode.label, 'Stacks')
45+
assert.strictEqual(stacksNode.collapsibleState, TreeItemCollapsibleState.Collapsed)
46+
assert.strictEqual(stacksNode.description, undefined)
47+
assert.strictEqual(stacksNode.contextValue, 'stackSection')
48+
})
49+
50+
it('should set description when loaded', function () {
51+
const mockStacks: StackSummary[] = [
52+
{ StackName: 'stack-1', StackStatus: 'CREATE_COMPLETE' } as StackSummary,
53+
{ StackName: 'stack-2', StackStatus: 'UPDATE_COMPLETE' } as StackSummary,
54+
]
55+
mockStacksManager.get.returns(mockStacks)
56+
mockStacksManager.hasMore.returns(false)
57+
mockStacksManager.isLoaded.returns(true)
58+
59+
stacksNode = new StacksNode(mockStacksManager as any, mockChangeSetsManager)
60+
61+
assert.strictEqual(stacksNode.description, '(2)')
62+
assert.strictEqual(stacksNode.contextValue, 'stackSection')
63+
})
64+
65+
it('should set contextValue to stackSectionWithMore when hasMore', function () {
66+
const mockStacks: StackSummary[] = [
67+
{ StackName: 'stack-1', StackStatus: 'CREATE_COMPLETE' } as StackSummary,
68+
]
69+
mockStacksManager.get.returns(mockStacks)
70+
mockStacksManager.hasMore.returns(true)
71+
mockStacksManager.isLoaded.returns(true)
72+
73+
stacksNode = new StacksNode(mockStacksManager as any, mockChangeSetsManager)
74+
75+
assert.strictEqual(stacksNode.description, '(1+)')
76+
assert.strictEqual(stacksNode.contextValue, 'stackSectionWithMore')
77+
})
78+
})
79+
80+
describe('getChildren', function () {
81+
beforeEach(function () {
82+
mockStacksManager.get.returns([])
83+
mockStacksManager.hasMore.returns(false)
84+
mockStacksManager.isLoaded.returns(false)
85+
stacksNode = new StacksNode(mockStacksManager as any, mockChangeSetsManager)
86+
})
87+
88+
it('should call ensureLoaded', async function () {
89+
mockStacksManager.ensureLoaded.resolves()
90+
91+
await stacksNode.getChildren()
92+
93+
assert.strictEqual(mockStacksManager.ensureLoaded.calledOnce, true)
94+
})
95+
96+
it('should return StackNode for each stack', async function () {
97+
const mockStacks: StackSummary[] = [
98+
{ StackName: 'stack-1', StackStatus: 'CREATE_COMPLETE' } as StackSummary,
99+
{ StackName: 'stack-2', StackStatus: 'UPDATE_COMPLETE' } as StackSummary,
100+
]
101+
mockStacksManager.ensureLoaded.resolves()
102+
mockStacksManager.get.returns(mockStacks)
103+
mockStacksManager.hasMore.returns(false)
104+
mockStacksManager.isLoaded.returns(true)
105+
106+
const children = await stacksNode.getChildren()
107+
108+
assert.strictEqual(children.length, 2)
109+
assert.strictEqual(children[0].label, 'stack-1')
110+
assert.strictEqual(children[1].label, 'stack-2')
111+
})
112+
113+
it('should include LoadMoreStacksNode when hasMore', async function () {
114+
const mockStacks: StackSummary[] = [
115+
{ StackName: 'stack-1', StackStatus: 'CREATE_COMPLETE' } as StackSummary,
116+
]
117+
mockStacksManager.ensureLoaded.resolves()
118+
mockStacksManager.get.returns(mockStacks)
119+
mockStacksManager.hasMore.returns(true)
120+
mockStacksManager.isLoaded.returns(true)
121+
122+
const children = await stacksNode.getChildren()
123+
124+
assert.strictEqual(children.length, 2)
125+
assert.strictEqual(children[0].label, 'stack-1')
126+
assert.strictEqual(children[1].label, '[Load More...]')
127+
assert.strictEqual(children[1].contextValue, 'loadMoreStacks')
128+
})
129+
130+
it('should update node description after load', async function () {
131+
const mockStacks: StackSummary[] = [
132+
{ StackName: 'stack-1', StackStatus: 'CREATE_COMPLETE' } as StackSummary,
133+
]
134+
mockStacksManager.ensureLoaded.resolves()
135+
mockStacksManager.get.returns(mockStacks)
136+
mockStacksManager.hasMore.returns(false)
137+
mockStacksManager.isLoaded.returns(true)
138+
139+
await stacksNode.getChildren()
140+
141+
assert.strictEqual(stacksNode.description, '(1)')
142+
assert.strictEqual(stacksNode.contextValue, 'stackSection')
143+
})
144+
145+
it('should return empty array when no stacks', async function () {
146+
mockStacksManager.ensureLoaded.resolves()
147+
mockStacksManager.get.returns([])
148+
mockStacksManager.hasMore.returns(false)
149+
mockStacksManager.isLoaded.returns(true)
150+
151+
const children = await stacksNode.getChildren()
152+
153+
assert.strictEqual(children.length, 0)
154+
})
155+
})
156+
157+
describe('loadMoreStacks', function () {
158+
beforeEach(function () {
159+
mockStacksManager.get.returns([])
160+
mockStacksManager.hasMore.returns(false)
161+
mockStacksManager.isLoaded.returns(false)
162+
stacksNode = new StacksNode(mockStacksManager as any, mockChangeSetsManager)
163+
})
164+
165+
it('should call stacksManager.loadMoreStacks', async function () {
166+
mockStacksManager.loadMoreStacks.resolves()
167+
168+
await stacksNode.loadMoreStacks()
169+
170+
assert.strictEqual(mockStacksManager.loadMoreStacks.calledOnce, true)
171+
})
172+
173+
it('should update node description after loading more', async function () {
174+
const mockStacks: StackSummary[] = [
175+
{ StackName: 'stack-1', StackStatus: 'CREATE_COMPLETE' } as StackSummary,
176+
{ StackName: 'stack-2', StackStatus: 'UPDATE_COMPLETE' } as StackSummary,
177+
]
178+
mockStacksManager.loadMoreStacks.resolves()
179+
mockStacksManager.get.returns(mockStacks)
180+
mockStacksManager.hasMore.returns(true)
181+
mockStacksManager.isLoaded.returns(true)
182+
183+
await stacksNode.loadMoreStacks()
184+
185+
assert.strictEqual(stacksNode.description, '(2+)')
186+
assert.strictEqual(stacksNode.contextValue, 'stackSectionWithMore')
187+
})
188+
189+
it('should update contextValue when no more stacks', async function () {
190+
const mockStacks: StackSummary[] = [
191+
{ StackName: 'stack-1', StackStatus: 'CREATE_COMPLETE' } as StackSummary,
192+
]
193+
mockStacksManager.loadMoreStacks.resolves()
194+
mockStacksManager.get.returns(mockStacks)
195+
mockStacksManager.hasMore.returns(false)
196+
mockStacksManager.isLoaded.returns(true)
197+
198+
await stacksNode.loadMoreStacks()
199+
200+
assert.strictEqual(stacksNode.description, '(1)')
201+
assert.strictEqual(stacksNode.contextValue, 'stackSection')
202+
})
203+
})
204+
})

0 commit comments

Comments
 (0)