Skip to content

Commit 61bb331

Browse files
authored
AWS Explorer indicates when Lambda Functions cannot be found (#814)
* General utility method to make child tree nodes * AWS Explorer now shows a node indicating when Lambda Functions cannot be found in a region
1 parent 9c19c29 commit 61bb331

File tree

6 files changed

+160
-22
lines changed

6 files changed

+160
-22
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"type": "Bug Fix",
3+
"description": "AWS Explorer now shows a node indicating when Lambda Functions cannot be found in a region"
4+
}

package.nls.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,7 @@
4848
"AWS.error.check.logs": "Check the logs for more information by running the \"{0}\" command from the Command Palette.",
4949
"AWS.explorerNode.addRegion": "Add a region to view functions...",
5050
"AWS.explorerNode.addRegion.tooltip": "Click to add a region to view functions...",
51-
"AWS.explorerNode.region.noResources": "[no resources in this region]",
52-
"AWS.explorerNode.lambda.noFunctions": "[no functions in this region]",
51+
"AWS.explorerNode.lambda.noFunctions": "[No Functions found]",
5352
"AWS.explorerNode.cloudFormation.noFunctions": "[no functions in this CloudFormation]",
5453
"AWS.explorerNode.cloudFormation.error": "Error loading CloudFormation resources",
5554
"AWS.explorerNode.container.noItems": "[no items]",

src/lambda/explorer/lambdaNodes.ts

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,10 @@ import { Lambda } from 'aws-sdk'
1010
import * as vscode from 'vscode'
1111
import { LambdaClient } from '../../shared/clients/lambdaClient'
1212
import { ext } from '../../shared/extensionGlobals'
13-
import { AWSTreeErrorHandlerNode } from '../../shared/treeview/nodes/awsTreeErrorHandlerNode'
1413
import { AWSTreeNodeBase } from '../../shared/treeview/nodes/awsTreeNodeBase'
1514
import { ErrorNode } from '../../shared/treeview/nodes/errorNode'
15+
import { PlaceholderNode } from '../../shared/treeview/nodes/placeholderNode'
16+
import { makeChildrenNodes } from '../../shared/treeview/treeNodeUtilities'
1617
import { toArrayAsync, toMap, updateInPlace } from '../../shared/utilities/collectionUtils'
1718
import { listLambdaFunctions } from '../utils'
1819
import { LambdaFunctionNode } from './lambdaFunctionNode'
@@ -23,25 +24,28 @@ export const CONTEXT_VALUE_LAMBDA_FUNCTION = 'awsRegionFunctionNode'
2324
* An AWS Explorer node representing the Lambda Service.
2425
* Contains Lambda Functions for a specific region as child nodes.
2526
*/
26-
export class LambdaNode extends AWSTreeErrorHandlerNode {
27+
export class LambdaNode extends AWSTreeNodeBase {
2728
private readonly functionNodes: Map<string, LambdaFunctionNode>
2829

2930
public constructor(private readonly regionCode: string) {
3031
super('Lambda', vscode.TreeItemCollapsibleState.Collapsed)
3132
this.functionNodes = new Map<string, LambdaFunctionNode>()
3233
}
3334

34-
public async getChildren(): Promise<(LambdaFunctionNode | ErrorNode)[]> {
35-
await this.handleErrorProneOperation(
36-
async () => this.updateChildren(),
37-
localize('AWS.explorerNode.lambda.error', 'Error loading Lambda resources')
38-
)
35+
public async getChildren(): Promise<AWSTreeNodeBase[]> {
36+
return await makeChildrenNodes({
37+
getChildNodes: async () => {
38+
await this.updateChildren()
3939

40-
return !!this.errorNode
41-
? [this.errorNode]
42-
: [...this.functionNodes.values()].sort((nodeA, nodeB) =>
43-
nodeA.functionName.localeCompare(nodeB.functionName)
44-
)
40+
return [...this.functionNodes.values()]
41+
},
42+
getErrorNode: async (error: Error) =>
43+
new ErrorNode(this, error, localize('AWS.explorerNode.lambda.error', 'Error loading Lambda resources')),
44+
getNoChildrenPlaceholderNode: async () =>
45+
new PlaceholderNode(this, localize('AWS.explorerNode.lambda.noFunctions', '[No Functions found]')),
46+
sort: (nodeA: LambdaFunctionNode, nodeB: LambdaFunctionNode) =>
47+
nodeA.functionName.localeCompare(nodeB.functionName)
48+
})
4549
}
4650

4751
public async updateChildren(): Promise<void> {
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*!
2+
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import { getLogger } from '../logger/logger'
7+
import { AWSTreeNodeBase } from './nodes/awsTreeNodeBase'
8+
9+
/**
10+
* Produces a list of child nodes using handlers to consistently populate the
11+
* list when errors occur or if the list would otherwise be empty.
12+
*/
13+
export async function makeChildrenNodes(parameters: {
14+
getChildNodes(): Promise<AWSTreeNodeBase[]>
15+
getNoChildrenPlaceholderNode?(): Promise<AWSTreeNodeBase>
16+
getErrorNode(error: Error): Promise<AWSTreeNodeBase>
17+
sort?(a: AWSTreeNodeBase, b: AWSTreeNodeBase): number
18+
}): Promise<AWSTreeNodeBase[]> {
19+
let childNodes: AWSTreeNodeBase[] = []
20+
try {
21+
childNodes.push(...(await parameters.getChildNodes()))
22+
23+
if (childNodes.length === 0 && parameters.getNoChildrenPlaceholderNode) {
24+
childNodes.push(await parameters.getNoChildrenPlaceholderNode())
25+
}
26+
27+
if (parameters.sort) {
28+
childNodes = childNodes.sort((a, b) => parameters.sort!(a, b))
29+
}
30+
} catch (err) {
31+
const error = err as Error
32+
getLogger().error(`Error whlie getting Child nodes: ${error.message}`)
33+
34+
childNodes.push(await parameters.getErrorNode(error))
35+
}
36+
37+
return childNodes
38+
}

src/test/lambda/explorer/lambdaNodes.test.ts

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,10 @@ import { LambdaFunctionNode } from '../../../lambda/explorer/lambdaFunctionNode'
1010
import { CONTEXT_VALUE_LAMBDA_FUNCTION, LambdaNode } from '../../../lambda/explorer/lambdaNodes'
1111
import { ToolkitClientBuilder } from '../../../shared/clients/toolkitClientBuilder'
1212
import { ext } from '../../../shared/extensionGlobals'
13-
import { assertNodeListOnlyContainsErrorNode } from './explorerNodeAssertions'
13+
import {
14+
assertNodeListOnlyContainsErrorNode,
15+
assertNodeListOnlyContainsPlaceholderNode
16+
} from './explorerNodeAssertions'
1417

1518
// TODO : Consolidate all asyncGenerator calls into a shared utility method
1619
async function* asyncGenerator<T>(items: T[]): AsyncIterableIterator<T> {
@@ -42,15 +45,13 @@ describe('LambdaNode', () => {
4245
sandbox.restore()
4346
})
4447

45-
// TODO : Lambda Nodes do not show a placeholder when no Functions could be found
46-
// TODO : https://github.com/aws/aws-toolkit-vscode/issues/812
47-
// it('returns placeholder node if no children are present', async () => {
48-
// lambdaFunctionNames = []
48+
it('returns placeholder node if no children are present', async () => {
49+
lambdaFunctionNames = []
4950

50-
// const childNodes = await testNode.getChildren()
51+
const childNodes = await testNode.getChildren()
5152

52-
// assertNodeListOnlyContainsPlaceholderNode(childNodes)
53-
// })
53+
assertNodeListOnlyContainsPlaceholderNode(childNodes)
54+
})
5455

5556
it('has LambdaFunctionNode child nodes', async () => {
5657
const childNodes = await testNode.getChildren()
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/*!
2+
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import * as assert from 'assert'
7+
import { ErrorNode } from '../../../shared/treeview/nodes/errorNode'
8+
import { PlaceholderNode } from '../../../shared/treeview/nodes/placeholderNode'
9+
import { makeChildrenNodes } from '../../../shared/treeview/treeNodeUtilities'
10+
import { TestAWSTreeNode } from './nodes/testAWSTreeNode'
11+
12+
describe('makeChildrenNodes', async () => {
13+
const parentNode = new TestAWSTreeNode('parent node')
14+
const nodeA = new PlaceholderNode(parentNode, 'node A')
15+
const nodeB = new PlaceholderNode(parentNode, 'node B')
16+
17+
it('returns child nodes', async () => {
18+
const childNodes = await makeChildrenNodes({
19+
getChildNodes: async () => [nodeA, nodeB],
20+
getErrorNode: async (error: Error) => makeErrorNode(error)
21+
})
22+
23+
assert.strictEqual(childNodes.length, 2, 'Unexpected child node count')
24+
assert.deepStrictEqual(childNodes[0], nodeA, 'Unexpected first child node')
25+
assert.deepStrictEqual(childNodes[1], nodeB, 'Unexpected second child node')
26+
})
27+
28+
it('returns an error node if an error is encountered', async () => {
29+
const expectedError = new Error('loading error')
30+
const expectedErrorNode = makeErrorNode(expectedError)
31+
32+
const childNodes = await makeChildrenNodes({
33+
getChildNodes: async () => {
34+
throw expectedError
35+
},
36+
getErrorNode: async (error: Error) => expectedErrorNode
37+
})
38+
39+
assert.strictEqual(childNodes.length, 1, 'Unexpected child node count')
40+
const actualChildNode = childNodes[0]
41+
assert.deepStrictEqual(actualChildNode, expectedErrorNode, 'Child node was not the error node')
42+
})
43+
44+
it('returns a placeholder node if there are no child nodes', async () => {
45+
const expectedPlaceholderNode = new PlaceholderNode(parentNode, 'No child nodes found')
46+
const childNodes = await makeChildrenNodes({
47+
getChildNodes: async () => [],
48+
getErrorNode: async (error: Error) => makeErrorNode(error),
49+
getNoChildrenPlaceholderNode: async () => expectedPlaceholderNode
50+
})
51+
52+
assert.strictEqual(childNodes.length, 1, 'Unexpected child node count')
53+
const actualChildNode = childNodes[0]
54+
assert.deepStrictEqual(actualChildNode, expectedPlaceholderNode, 'Child node was not the placeholder node')
55+
})
56+
57+
it('returns an empty list if there are no child nodes and no placeholder', async () => {
58+
const childNodes = await makeChildrenNodes({
59+
getChildNodes: async () => [],
60+
getErrorNode: async (error: Error) => makeErrorNode(error)
61+
})
62+
63+
assert.strictEqual(childNodes.length, 0, 'Unexpected child node count')
64+
})
65+
66+
it('sorts the child nodes', async () => {
67+
const childNodes = await makeChildrenNodes({
68+
getChildNodes: async () => [nodeB, nodeA],
69+
getErrorNode: async (error: Error) => makeErrorNode(error),
70+
sort: (a, b) => a.label!.localeCompare(b.label!)
71+
})
72+
73+
assert.strictEqual(childNodes.length, 2, 'Unexpected child node count')
74+
assert.deepStrictEqual(childNodes[0], nodeA, 'Unexpected first child node')
75+
assert.deepStrictEqual(childNodes[1], nodeB, 'Unexpected second child node')
76+
})
77+
78+
it('does not sort the child nodes if a sort method is not provided', async () => {
79+
const childNodes = await makeChildrenNodes({
80+
getChildNodes: async () => [nodeB, nodeA],
81+
getErrorNode: async (error: Error) => makeErrorNode(error)
82+
})
83+
84+
assert.strictEqual(childNodes.length, 2, 'Unexpected child node count')
85+
assert.deepStrictEqual(childNodes[0], nodeB, 'Unexpected first child node')
86+
assert.deepStrictEqual(childNodes[1], nodeA, 'Unexpected second child node')
87+
})
88+
89+
function makeErrorNode(error: Error): ErrorNode {
90+
return new ErrorNode(parentNode, error, error.message)
91+
}
92+
})

0 commit comments

Comments
 (0)