Skip to content

Commit 54f5554

Browse files
committed
feat(lambda): add installation of LocalStack extension to AppBuilder Walkthrough
Update AppBuilder Walkthrough wording and add LocalStack VS Code extension
1 parent 30a4abf commit 54f5554

File tree

5 files changed

+137
-3
lines changed

5 files changed

+137
-3
lines changed

packages/core/package.nls.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -478,7 +478,7 @@
478478
"AWS.toolkit.lambda.walkthrough.title": "Get started building your application",
479479
"AWS.toolkit.lambda.walkthrough.description": "Your quick guide to build an application visually, iterate locally, and deploy to the cloud!",
480480
"AWS.toolkit.lambda.walkthrough.toolInstall.title": "Complete installation",
481-
"AWS.toolkit.lambda.walkthrough.toolInstall.description": "The AWS Command Line Interface (AWS CLI) is an open source tool that enables you to interact with AWS services using commands in your command-line shell. It is required to create and interact with AWS resources. \n\n[Install AWS CLI](command:aws.toolkit.installAWSCLI)\n\n Use the Serverless Application Model (SAM) CLI to locally build, invoke, and deploy your functions. Version 1.98+ is required. \n\n[Install SAM CLI](command:aws.toolkit.installSAMCLI)\n\n Use Docker to locally emulate a Lambda environment. Docker is optional. However, if you want to invoke locally, Docker is required so Lambda can locally emulate the execution environment. \n\n[Install Docker (optional)](command:aws.toolkit.installDocker)",
481+
"AWS.toolkit.lambda.walkthrough.toolInstall.description": "Manage your AWS services and resources with the AWS Command Line Interface (AWS CLI). \n\n[Install AWS CLI](command:aws.toolkit.installAWSCLI)\n\nBuild locally, invoke, and deploy your functions with the Serverless Application Model (SAM) CLI. \n\n[Install SAM CLI](command:aws.toolkit.installSAMCLI)\n\nDocker is an optional, third party tool that assists with local AWS Lambda runtime emulation. Docker is required to invoke Lambda functions on your local machine. \n\n[Install Docker (optional)](command:aws.toolkit.installDocker)\n\nEmulate your AWS cloud services locally with LocalStack to streamline testing in VS Code and CI environments. [Learn more](https://docs.localstack.cloud/aws/). \n\n[Install LocalStack (optional)](command:aws.toolkit.installLocalStack)",
482482
"AWS.toolkit.lambda.walkthrough.chooseTemplate.title": "Choose your application template",
483483
"AWS.toolkit.lambda.walkthrough.chooseTemplate.description": "Select a starter application, visually compose an application from scratch, open an existing application, or browse more application examples. \n\nInfrastructure Composer allows you to visually compose modern applications in the cloud. It will define the necessary permissions between resources when you drag a connection between them. \n\n[Initialize your project](command:aws.toolkit.lambda.initializeWalkthroughProject)",
484484
"AWS.toolkit.lambda.walkthrough.step1.title": "Iterate locally",

packages/core/src/awsService/appBuilder/activation.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,12 @@ import { activateViewsShared, registerToolView } from '../../awsexplorer/activat
1313
import { setContext } from '../../shared/vscode/setContext'
1414
import { fs } from '../../shared/fs/fs'
1515
import { AppBuilderRootNode } from './explorer/nodes/rootNode'
16-
import { initWalkthroughProjectCommand, walkthroughContextString, getOrInstallCliWrapper } from './walkthrough'
16+
import {
17+
initWalkthroughProjectCommand,
18+
walkthroughContextString,
19+
getOrInstallCliWrapper,
20+
installLocalStackExtension,
21+
} from './walkthrough'
1722
import { getLogger } from '../../shared/logger/logger'
1823
import path from 'path'
1924
import { TreeNode } from '../../shared/treeview/resourceTreeDataProvider'
@@ -142,6 +147,9 @@ async function registerAppBuilderCommands(context: ExtContext): Promise<void> {
142147
Commands.register('aws.toolkit.installDocker', async () => {
143148
await getOrInstallCliWrapper('docker', source)
144149
}),
150+
Commands.register('aws.toolkit.installLocalStack', async () => {
151+
await installLocalStackExtension(source)
152+
}),
145153
Commands.register('aws.toolkit.lambda.setWalkthroughToAPI', async () => {
146154
await setWalkthrough('API')
147155
}),

packages/core/src/awsService/appBuilder/walkthrough.ts

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import { ToolkitError } from '../../shared/errors'
1616
import { createSingleFileDialog } from '../../shared/ui/common/openDialog'
1717
import { fs } from '../../shared/fs/fs'
1818
import path from 'path'
19-
import { telemetry } from '../../shared/telemetry/telemetry'
19+
import { telemetry, ToolId } from '../../shared/telemetry/telemetry'
2020

2121
import { minSamCliVersionForAppBuilderSupport } from '../../shared/sam/cli/samCliValidator'
2222
import { SamCliInfoInvocation } from '../../shared/sam/cli/samCliInfo'
@@ -347,3 +347,33 @@ export async function getOrInstallCliWrapper(toolId: AwsClis, source: string) {
347347
}
348348
})
349349
}
350+
351+
export async function installLocalStackExtension(source: string) {
352+
await telemetry.appBuilder_installTool.run(async (span) => {
353+
// TODO: update `toolId` to "localstack" after updating the telemetry definitions: https://github.com/aws/aws-toolkit-common/blob/8c88537fae2ac7e6524fb2b29ae336c606850eeb/telemetry/definitions/commonDefinitions.json#L2215-L2221
354+
const toolId: ToolId = 'sam-cli'
355+
span.record({ source, toolId })
356+
const extensionId = 'localstack.localstack'
357+
const extension = vscode.extensions.getExtension(extensionId)
358+
if (extension) {
359+
void vscode.window.showInformationMessage(
360+
localize(
361+
'AWS.toolkit.lambda.walkthrough.localStackExtension.alreadyInstalled',
362+
'LocalStack extension is already installed'
363+
)
364+
)
365+
} else {
366+
try {
367+
await vscode.commands.executeCommand('workbench.extensions.installExtension', extensionId)
368+
void vscode.window.showInformationMessage(
369+
localize(
370+
'AWS.toolkit.lambda.walkthrough.localStackExtension.installSuccessful',
371+
'LocalStack extension has been installed'
372+
)
373+
)
374+
} catch (err) {
375+
throw ToolkitError.chain(err, 'Failed to install LocalStack extension')
376+
}
377+
}
378+
})
379+
}

packages/core/src/test/awsService/appBuilder/walkthrough.test.ts

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
RuntimeLocationWizard,
1616
genWalkthroughProject,
1717
openProjectInWorkspace,
18+
installLocalStackExtension,
1819
} from '../../../awsService/appBuilder/walkthrough'
1920
import { createWizardTester } from '../../shared/wizards/wizardTestUtils'
2021
import { fs } from '../../../shared'
@@ -460,5 +461,96 @@ describe('AppBuilder Walkthrough', function () {
460461
toolId: 'sam-cli',
461462
})
462463
})
464+
465+
describe('Install LocalStack Extension', function () {
466+
const expectedLocalStackToolId = 'sam-cli' // temporarily until toolkit-common changes
467+
468+
it('should show already installed message when extension exists', async function () {
469+
const mockExtension = { id: 'localstack.localstack' }
470+
sandbox
471+
.stub(vscode.extensions, 'getExtension')
472+
.withArgs('localstack.localstack')
473+
.returns(mockExtension as any)
474+
const spyExecuteCommand = sandbox.spy(vscode.commands, 'executeCommand')
475+
476+
await installLocalStackExtension('test-source')
477+
478+
const message = await getTestWindow().waitForMessage(/LocalStack extension is already installed/)
479+
message.close()
480+
481+
// Verify installation command was not called
482+
sandbox.assert.neverCalledWith(spyExecuteCommand, 'workbench.extensions.installExtension')
483+
484+
// Verify telemetry
485+
assertTelemetry({
486+
result: 'Succeeded',
487+
source: 'test-source',
488+
toolId: expectedLocalStackToolId, // Note: currently uses sam-cli as toolId per TODO comment
489+
})
490+
})
491+
492+
it('should successfully install extension when not present', async function () {
493+
sandbox.stub(vscode.extensions, 'getExtension').withArgs('localstack.localstack').returns(undefined)
494+
const spyExecuteCommand = sandbox.stub(vscode.commands, 'executeCommand').resolves()
495+
496+
await installLocalStackExtension('test-source')
497+
498+
const message = await getTestWindow().waitForMessage(/LocalStack extension has been installed/)
499+
message.close()
500+
501+
// Verify installation command was called with correct extension ID
502+
sandbox.assert.calledWith(
503+
spyExecuteCommand,
504+
'workbench.extensions.installExtension',
505+
'localstack.localstack'
506+
)
507+
508+
// Verify telemetry
509+
assertTelemetry({
510+
result: 'Succeeded',
511+
source: 'test-source',
512+
toolId: expectedLocalStackToolId,
513+
})
514+
})
515+
516+
it('should handle installation failure and throw ToolkitError', async function () {
517+
sandbox.stub(vscode.extensions, 'getExtension').withArgs('localstack.localstack').returns(undefined)
518+
const installError = new Error('Installation failed')
519+
sandbox.stub(vscode.commands, 'executeCommand').rejects(installError)
520+
521+
await assert.rejects(installLocalStackExtension('test-source'), (error: any) => {
522+
assert.strictEqual(error.message, 'Failed to install LocalStack extension')
523+
assert.strictEqual(error.cause, installError)
524+
return true
525+
})
526+
527+
// Verify telemetry is still recorded even on failure
528+
assertTelemetry({
529+
result: 'Failed',
530+
source: 'test-source',
531+
toolId: expectedLocalStackToolId,
532+
})
533+
})
534+
535+
it('should record telemetry with correct source parameter', async function () {
536+
const mockExtension = { id: 'localstack.localstack' }
537+
sandbox
538+
.stub(vscode.extensions, 'getExtension')
539+
.withArgs('localstack.localstack')
540+
.returns(mockExtension as any)
541+
542+
await installLocalStackExtension('walkthrough-button')
543+
544+
const message = await getTestWindow().waitForMessage(/LocalStack extension is already installed/)
545+
message.close()
546+
547+
// Verify telemetry includes the correct source
548+
assertTelemetry({
549+
result: 'Succeeded',
550+
source: 'walkthrough-button',
551+
toolId: expectedLocalStackToolId,
552+
})
553+
})
554+
})
463555
})
464556
})
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"type": "Feature",
3+
"description": "Lambda AppBuilder: Now you can install LocalStack VS Code extension from the AppBuilder walkthrough"
4+
}

0 commit comments

Comments
 (0)