From f437690538a15a49f7bef9711f3f2802ca53f84f Mon Sep 17 00:00:00 2001 From: Vandita Patidar Date: Tue, 18 Feb 2025 14:01:54 -0800 Subject: [PATCH 01/11] Building Quick Pick structure --- .../models/serverlessland-metadata.json | 60 ++++++ .../models/serverlesslandMetadataManager.ts | 122 ++++++++++++ .../awsService/appBuilder/serverlessLand.ts | 88 +++++++++ .../wizards/serverlessLandWizard.ts | 184 ++++++++++++++++++ 4 files changed, 454 insertions(+) create mode 100644 packages/core/src/awsService/appBuilder/models/serverlessland-metadata.json create mode 100644 packages/core/src/awsService/appBuilder/models/serverlesslandMetadataManager.ts create mode 100644 packages/core/src/awsService/appBuilder/serverlessLand.ts create mode 100644 packages/core/src/awsService/appBuilder/wizards/serverlessLandWizard.ts diff --git a/packages/core/src/awsService/appBuilder/models/serverlessland-metadata.json b/packages/core/src/awsService/appBuilder/models/serverlessland-metadata.json new file mode 100644 index 00000000000..3c5876faeb2 --- /dev/null +++ b/packages/core/src/awsService/appBuilder/models/serverlessland-metadata.json @@ -0,0 +1,60 @@ +{ + "patterns": { + "s3-lambda-resizing-sam": { + "name": "Resizing image", + "description": "Lambda, S3 • Python, Javascript, Java, .NET • SAM", + "runtimes": [ + { + "id": "python", + "name": "Python" + }, + { + "id": "javascript", + "name": "Javascript" + }, + { + "id": "dotnet", + "name": "Dotnet" + }, + { + "id": "java", + "name": "Java" + } + ], + "iac": [ + { + "id": "sam", + "name": "SAM" + } + ] + }, + "apigw-rest-api-lambda-sam": { + "name": "Rest API", + "description": "Lambda, API Gateway • Python, Javascript, Java, .NET • SAM", + "runtimes": [ + { + "id": "python", + "name": "Python" + }, + { + "id": "javascript", + "name": "Javascript" + }, + { + "id": "dotnet", + "name": "Dotnet" + }, + { + "id": "java", + "name": "Java" + } + ], + "iac": [ + { + "id": "sam", + "name": "AWS SAM" + } + ] + } + } +} diff --git a/packages/core/src/awsService/appBuilder/models/serverlesslandMetadataManager.ts b/packages/core/src/awsService/appBuilder/models/serverlesslandMetadataManager.ts new file mode 100644 index 00000000000..6971eb1d45f --- /dev/null +++ b/packages/core/src/awsService/appBuilder/models/serverlesslandMetadataManager.ts @@ -0,0 +1,122 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +import * as nodefs from 'fs' // eslint-disable-line no-restricted-imports +import { ToolkitError } from '../../../shared/errors' + +interface IaC { + id: string + name: string +} +interface Runtime { + id: string + name: string + version: string +} +interface PatternData { + name: string + description: string + runtimes: Runtime[] + iac: IaC[] +} + +export interface ProjectMetadata { + patterns: Record +} + +/** + * Manages metadata for serverless application patterns + * Implements Singleton pattern to ensure single instance across the application + */ + +export class MetadataManager { + private static instance: MetadataManager + private metadata: ProjectMetadata | undefined + + private constructor() {} + + public static getInstance(): MetadataManager { + if (!MetadataManager.instance) { + MetadataManager.instance = new MetadataManager() + } + return MetadataManager.instance + } + + /** + * Loads metadata from a JSON file + * @param metadataPath Path to the metadata JSON file + * @returns Promise containing the parsed ProjectMetadata + */ + public async loadMetadata(metadataPath: string): Promise { + try { + if (!this.metadata) { + const metadataContent = nodefs.readFileSync(metadataPath, { encoding: 'utf-8' }) + const parseMetadata = JSON.parse(metadataContent) as ProjectMetadata + this.metadata = parseMetadata + } + return this.metadata + } catch (err) { + throw new ToolkitError(`Failed to load metadata: ${err instanceof Error ? err.message : String(err)}`) + } + } + + /** + * Retrieves available patterns with their descriptions + * @returns Array of pattern objects containing labels and descriptions + */ + public getPatterns(): { label: string; description?: string }[] { + if (!this.metadata) { + return [] + } + return Object.entries(this.metadata.patterns).map(([patternName, patternData]) => { + let description: string | undefined = undefined + if (typeof patternData === 'string') { + description = patternData + } else if (Array.isArray(patternData)) { + // If description is an array, join it into a single string + description = patternData.join(' ') + } + if (!patternData || !patternData.name) { + return { + label: patternName, + description: description || 'No description available', + } + } + return { + label: patternName, + description: patternData.description, + } + }) + } + + /** + * Gets available runtimes for a specific pattern + * @param pattern The pattern name to get runtimes for + * @returns Array of runtime options with labels + */ + public getRuntimes(pattern: string): { label: string }[] { + const patternData = this.metadata?.patterns?.[pattern] + if (!patternData || !patternData.runtimes) { + return [] + } + return patternData.runtimes.map((runtime) => ({ + label: runtime.name, + })) + } + + /** + * Gets available Infrastructure as Code options for a specific pattern + * @param pattern The pattern name to get IaC options for + * @returns Array of IaC options with labels + */ + public getIacOptions(pattern: string): { label: string }[] { + const patternData = this.metadata?.patterns?.[pattern] + if (!patternData || !patternData.iac) { + return [] + } + return patternData.iac.map((iac) => ({ + label: iac.name, + })) + } +} diff --git a/packages/core/src/awsService/appBuilder/serverlessLand.ts b/packages/core/src/awsService/appBuilder/serverlessLand.ts new file mode 100644 index 00000000000..f3663e61ba7 --- /dev/null +++ b/packages/core/src/awsService/appBuilder/serverlessLand.ts @@ -0,0 +1,88 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as nls from 'vscode-nls' +const localize = nls.loadMessageBundle() +import * as path from 'path' +import { getTelemetryReason, getTelemetryResult } from '../../shared/errors' +import { RegionProvider } from '../../shared/regions/regionProvider' +import { getLogger } from '../../shared/logger/logger' +import globals from '../../shared/extensionGlobals' +import { checklogs } from '../../shared/localizedText' +import { Result, telemetry } from '../../shared/telemetry/telemetry' +import { CreateServerlessLandWizard } from '../appBuilder/wizards/serverlessLandWizard' +import { ExtContext } from '../../shared/extensions' +import { addFolderToWorkspace } from '../../shared/utilities/workspaceUtils' + +export const readmeFile: string = 'README.md' + +/** + * Creates a new Serverless Land project using the provided extension context + * @param extContext Extension context containing AWS credentials and region information + * @returns Promise that resolves when the project creation is complete + * + * This function: + * 1. Validates AWS credentials and regions + * 2. Launches the Serverless Land project creation wizard + * 3. Creates the project structure + * 4. Adds the project folder to the workspace + * 5. Opens the README.md file if available + * 6. Handles errors and emits telemetry + */ +export async function createNewServerlessLandProject(extContext: ExtContext): Promise { + const awsContext = extContext.awsContext + const regionProvider: RegionProvider = extContext.regionProvider + let createResult: Result = 'Succeeded' + let reason: string | undefined + + try { + const credentials = await awsContext.getCredentials() + const schemaRegions = regionProvider + .getRegions() + .filter((r) => regionProvider.isServiceInRegion('schemas', r.id)) + const defaultRegion = awsContext.getCredentialDefaultRegion() + + // Launch the project creation wizard + const config = await new CreateServerlessLandWizard({ + credentials, + schemaRegions, + defaultRegion, + }).run() + if (!config) { + createResult = 'Cancelled' + reason = 'userCancelled' + return + } + + // Add the project folder to the workspace + await addFolderToWorkspace( + { + uri: config.location, + name: path.basename(config.location.fsPath), + }, + true + ) + } catch (err) { + createResult = getTelemetryResult(err) + reason = getTelemetryReason(err) + + globals.outputChannel.show(true) + getLogger().error( + localize( + 'AWS.serverlessland.initWizard.general.error', + 'Error creating new SAM Application. {0}', + checklogs() + ) + ) + getLogger().error('Error creating new Serverless Land Application: %O', err as Error) + } finally { + // add telemetry + // TODO: Will add telemetry once the implementation gets completed + telemetry.sam_init.emit({ + result: createResult, + reason: reason, + }) + } +} diff --git a/packages/core/src/awsService/appBuilder/wizards/serverlessLandWizard.ts b/packages/core/src/awsService/appBuilder/wizards/serverlessLandWizard.ts new file mode 100644 index 00000000000..b124bf1c1f7 --- /dev/null +++ b/packages/core/src/awsService/appBuilder/wizards/serverlessLandWizard.ts @@ -0,0 +1,184 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +import * as AWS from '@aws-sdk/types' +import * as vscode from 'vscode' +import { Wizard } from '../../../shared/wizards/wizard' +import * as path from 'path' +import { createInputBox } from '../../../shared/ui/inputPrompter' +import { createCommonButtons } from '../../../shared/ui/buttons' +import { createQuickPick } from '../../../shared/ui/pickerPrompter' +import { createFolderPrompt } from '../../../shared/ui/common/location' +import { Region } from '../../../shared/regions/endpoints' +import { createExitPrompter } from '../../../shared/ui/common/exitPrompter' +import { MetadataManager } from '../models/serverlesslandMetadataManager' +import { ToolkitError } from '../../../shared/errors' + +export interface CreateServerlessLandWizardForm { + name: string + location: vscode.Uri + pattern: string + runtime: string + iac: string + region?: string + schemaName?: string +} + +/** + * Wizard for creating Serverless Land applications + * Guides users through the project creation process + */ +export class CreateServerlessLandWizard extends Wizard { + private metadataManager: MetadataManager + + public constructor(context: { schemaRegions: Region[]; defaultRegion?: string; credentials?: AWS.Credentials }) { + super({ + exitPrompterProvider: createExitPrompter, + }) + this.metadataManager = MetadataManager.getInstance() + } + + public override async run(): Promise { + try { + // Load metadata from JSON file + const projectRoot = path.resolve(__dirname, '../../../../../') + const metadataPath = path.join( + projectRoot, + 'src', + 'awsService', + 'appBuilder', + 'models', + 'serverlessland-metadata.json' + ) + await this.metadataManager.loadMetadata(metadataPath) + + // Initialize and display pattern selection + const patterns = this.metadataManager.getPatterns() + if (patterns.length === 0) { + throw new Error('No patterns found in metadata') + } + + const patternPicker = createQuickPick( + patterns.map((p) => ({ + label: p.label, + detail: p.description, + data: p.label, + buttons: [ + { + iconPath: new vscode.ThemeIcon('github'), + tooltip: 'GitHub Button', + }, + { + iconPath: new vscode.ThemeIcon('open-preview'), + tooltip: 'Serverless Land Button', + }, + ], + })), + { + title: 'Select a Pattern for your application', + placeholder: 'Choose a pattern for your project', + buttons: createCommonButtons(), + matchOnDescription: true, + matchOnDetail: true, + } + ) + + const patternResult = await patternPicker.prompt() + if (!patternResult || typeof patternResult !== 'string') { + return undefined // User cancelled or invalid result + } + const selectedPattern = patternResult + + // Show runtime options based on selected pattern + const runtimes = this.metadataManager.getRuntimes(selectedPattern) + if (runtimes.length === 0) { + throw new Error('No runtimes found for the selected pattern') + } + + const runtimePicker = createQuickPick( + runtimes.map((r) => ({ + label: r.label, + data: r.label, + })), + { + title: 'Select Runtime', + placeholder: 'Choose a runtime for your project', + buttons: createCommonButtons(), + } + ) + const runtimeResult = await runtimePicker.prompt() + if (!runtimeResult || typeof runtimeResult !== 'string') { + return undefined // User cancelled or invalid result + } + const selectedRuntime = runtimeResult + + // Show IAC options based on selected pattern + const iacOptions = this.metadataManager.getIacOptions(selectedPattern) + if (iacOptions.length === 0) { + throw new Error('No IAC options found for the selected pattern') + } + + const iacPicker = createQuickPick( + iacOptions.map((i) => ({ + label: i.label, + data: i.label, + })), + { + title: 'Select IaC', + placeholder: 'Choose an IaC option for your project', + buttons: createCommonButtons(), + } + ) + const iacResult = await iacPicker.prompt() + if (!iacResult || typeof iacResult !== 'string') { + return undefined // User cancelled or invalid result + } + const selectedIac = iacResult + + // Create and show location picker + const locationPicker = createFolderPrompt(vscode.workspace.workspaceFolders ?? [], { + title: 'Select Project Location', + buttons: createCommonButtons(), + browseFolderDetail: 'Select a folder for your project', + }) + + const selectedLocation = await locationPicker.prompt() + if (!selectedLocation || !(selectedLocation instanceof vscode.Uri)) { + return undefined // User cancelled or invalid result + } + + // Create and show project name input + const nameInput = createInputBox({ + title: 'Enter Project Name', + placeholder: 'Enter a name for your new application', + buttons: createCommonButtons(), + validateInput: (value: string): string | undefined => { + if (!value) { + return 'Application name cannot be empty' + } + if (value.includes(path.sep)) { + return `The path separator (${path.sep}) is not allowed in application names` + } + return undefined + }, + }) + + const projectName = await nameInput.prompt() + if (!projectName || typeof projectName !== 'string') { + return undefined // User cancelled + } + + // Return the form with all collected values + return { + name: projectName, + location: selectedLocation, + pattern: selectedPattern, + runtime: selectedRuntime, + iac: selectedIac, + } + } catch (err) { + throw new ToolkitError(`Failed to run wizard: ${err instanceof Error ? err.message : String(err)}`) + } + } +} From 8741fcaf23e47be8e34b2a1e798d5be96a886bdb Mon Sep 17 00:00:00 2001 From: Vandita Patidar Date: Tue, 18 Feb 2025 14:36:08 -0800 Subject: [PATCH 02/11] Arranging file structure --- .../serverlessLandMain.ts} | 18 +++++++++--------- .../serverlessLandWizard.ts | 4 ++-- .../serverlessland-metadata.json | 0 .../serverlesslandMetadataManager.ts | 0 4 files changed, 11 insertions(+), 11 deletions(-) rename packages/core/src/awsService/appBuilder/{serverlessLand.ts => serverlessLand/serverlessLandMain.ts} (81%) rename packages/core/src/awsService/appBuilder/{wizards => serverlessLand}/serverlessLandWizard.ts (98%) rename packages/core/src/awsService/appBuilder/{models => serverlessLand}/serverlessland-metadata.json (100%) rename packages/core/src/awsService/appBuilder/{models => serverlessLand}/serverlesslandMetadataManager.ts (100%) diff --git a/packages/core/src/awsService/appBuilder/serverlessLand.ts b/packages/core/src/awsService/appBuilder/serverlessLand/serverlessLandMain.ts similarity index 81% rename from packages/core/src/awsService/appBuilder/serverlessLand.ts rename to packages/core/src/awsService/appBuilder/serverlessLand/serverlessLandMain.ts index f3663e61ba7..d2b2995dbda 100644 --- a/packages/core/src/awsService/appBuilder/serverlessLand.ts +++ b/packages/core/src/awsService/appBuilder/serverlessLand/serverlessLandMain.ts @@ -6,15 +6,15 @@ import * as nls from 'vscode-nls' const localize = nls.loadMessageBundle() import * as path from 'path' -import { getTelemetryReason, getTelemetryResult } from '../../shared/errors' -import { RegionProvider } from '../../shared/regions/regionProvider' -import { getLogger } from '../../shared/logger/logger' -import globals from '../../shared/extensionGlobals' -import { checklogs } from '../../shared/localizedText' -import { Result, telemetry } from '../../shared/telemetry/telemetry' -import { CreateServerlessLandWizard } from '../appBuilder/wizards/serverlessLandWizard' -import { ExtContext } from '../../shared/extensions' -import { addFolderToWorkspace } from '../../shared/utilities/workspaceUtils' +import { getTelemetryReason, getTelemetryResult } from '../../../shared/errors' +import { RegionProvider } from '../../../shared/regions/regionProvider' +import { getLogger } from '../../../shared/logger/logger' +import globals from '../../../shared/extensionGlobals' +import { checklogs } from '../../../shared/localizedText' +import { Result, telemetry } from '../../../shared/telemetry/telemetry' +import { CreateServerlessLandWizard } from './serverlessLandWizard' +import { ExtContext } from '../../../shared/extensions' +import { addFolderToWorkspace } from '../../../shared/utilities/workspaceUtils' export const readmeFile: string = 'README.md' diff --git a/packages/core/src/awsService/appBuilder/wizards/serverlessLandWizard.ts b/packages/core/src/awsService/appBuilder/serverlessLand/serverlessLandWizard.ts similarity index 98% rename from packages/core/src/awsService/appBuilder/wizards/serverlessLandWizard.ts rename to packages/core/src/awsService/appBuilder/serverlessLand/serverlessLandWizard.ts index b124bf1c1f7..26301acb29e 100644 --- a/packages/core/src/awsService/appBuilder/wizards/serverlessLandWizard.ts +++ b/packages/core/src/awsService/appBuilder/serverlessLand/serverlessLandWizard.ts @@ -12,7 +12,7 @@ import { createQuickPick } from '../../../shared/ui/pickerPrompter' import { createFolderPrompt } from '../../../shared/ui/common/location' import { Region } from '../../../shared/regions/endpoints' import { createExitPrompter } from '../../../shared/ui/common/exitPrompter' -import { MetadataManager } from '../models/serverlesslandMetadataManager' +import { MetadataManager } from './serverlesslandMetadataManager' import { ToolkitError } from '../../../shared/errors' export interface CreateServerlessLandWizardForm { @@ -48,7 +48,7 @@ export class CreateServerlessLandWizard extends Wizard Date: Tue, 18 Feb 2025 16:47:35 -0800 Subject: [PATCH 03/11] Review revision --- .../serverlessLand/serverlessLandMain.ts | 88 --------- .../serverlessLand/serverlessLandWizard.ts | 184 ------------------ .../serverlessland-metadata.json | 60 ------ .../serverlesslandMetadataManager.ts | 122 ------------ 4 files changed, 454 deletions(-) delete mode 100644 packages/core/src/awsService/appBuilder/serverlessLand/serverlessLandMain.ts delete mode 100644 packages/core/src/awsService/appBuilder/serverlessLand/serverlessLandWizard.ts delete mode 100644 packages/core/src/awsService/appBuilder/serverlessLand/serverlessland-metadata.json delete mode 100644 packages/core/src/awsService/appBuilder/serverlessLand/serverlesslandMetadataManager.ts diff --git a/packages/core/src/awsService/appBuilder/serverlessLand/serverlessLandMain.ts b/packages/core/src/awsService/appBuilder/serverlessLand/serverlessLandMain.ts deleted file mode 100644 index d2b2995dbda..00000000000 --- a/packages/core/src/awsService/appBuilder/serverlessLand/serverlessLandMain.ts +++ /dev/null @@ -1,88 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -import * as nls from 'vscode-nls' -const localize = nls.loadMessageBundle() -import * as path from 'path' -import { getTelemetryReason, getTelemetryResult } from '../../../shared/errors' -import { RegionProvider } from '../../../shared/regions/regionProvider' -import { getLogger } from '../../../shared/logger/logger' -import globals from '../../../shared/extensionGlobals' -import { checklogs } from '../../../shared/localizedText' -import { Result, telemetry } from '../../../shared/telemetry/telemetry' -import { CreateServerlessLandWizard } from './serverlessLandWizard' -import { ExtContext } from '../../../shared/extensions' -import { addFolderToWorkspace } from '../../../shared/utilities/workspaceUtils' - -export const readmeFile: string = 'README.md' - -/** - * Creates a new Serverless Land project using the provided extension context - * @param extContext Extension context containing AWS credentials and region information - * @returns Promise that resolves when the project creation is complete - * - * This function: - * 1. Validates AWS credentials and regions - * 2. Launches the Serverless Land project creation wizard - * 3. Creates the project structure - * 4. Adds the project folder to the workspace - * 5. Opens the README.md file if available - * 6. Handles errors and emits telemetry - */ -export async function createNewServerlessLandProject(extContext: ExtContext): Promise { - const awsContext = extContext.awsContext - const regionProvider: RegionProvider = extContext.regionProvider - let createResult: Result = 'Succeeded' - let reason: string | undefined - - try { - const credentials = await awsContext.getCredentials() - const schemaRegions = regionProvider - .getRegions() - .filter((r) => regionProvider.isServiceInRegion('schemas', r.id)) - const defaultRegion = awsContext.getCredentialDefaultRegion() - - // Launch the project creation wizard - const config = await new CreateServerlessLandWizard({ - credentials, - schemaRegions, - defaultRegion, - }).run() - if (!config) { - createResult = 'Cancelled' - reason = 'userCancelled' - return - } - - // Add the project folder to the workspace - await addFolderToWorkspace( - { - uri: config.location, - name: path.basename(config.location.fsPath), - }, - true - ) - } catch (err) { - createResult = getTelemetryResult(err) - reason = getTelemetryReason(err) - - globals.outputChannel.show(true) - getLogger().error( - localize( - 'AWS.serverlessland.initWizard.general.error', - 'Error creating new SAM Application. {0}', - checklogs() - ) - ) - getLogger().error('Error creating new Serverless Land Application: %O', err as Error) - } finally { - // add telemetry - // TODO: Will add telemetry once the implementation gets completed - telemetry.sam_init.emit({ - result: createResult, - reason: reason, - }) - } -} diff --git a/packages/core/src/awsService/appBuilder/serverlessLand/serverlessLandWizard.ts b/packages/core/src/awsService/appBuilder/serverlessLand/serverlessLandWizard.ts deleted file mode 100644 index 26301acb29e..00000000000 --- a/packages/core/src/awsService/appBuilder/serverlessLand/serverlessLandWizard.ts +++ /dev/null @@ -1,184 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ -import * as AWS from '@aws-sdk/types' -import * as vscode from 'vscode' -import { Wizard } from '../../../shared/wizards/wizard' -import * as path from 'path' -import { createInputBox } from '../../../shared/ui/inputPrompter' -import { createCommonButtons } from '../../../shared/ui/buttons' -import { createQuickPick } from '../../../shared/ui/pickerPrompter' -import { createFolderPrompt } from '../../../shared/ui/common/location' -import { Region } from '../../../shared/regions/endpoints' -import { createExitPrompter } from '../../../shared/ui/common/exitPrompter' -import { MetadataManager } from './serverlesslandMetadataManager' -import { ToolkitError } from '../../../shared/errors' - -export interface CreateServerlessLandWizardForm { - name: string - location: vscode.Uri - pattern: string - runtime: string - iac: string - region?: string - schemaName?: string -} - -/** - * Wizard for creating Serverless Land applications - * Guides users through the project creation process - */ -export class CreateServerlessLandWizard extends Wizard { - private metadataManager: MetadataManager - - public constructor(context: { schemaRegions: Region[]; defaultRegion?: string; credentials?: AWS.Credentials }) { - super({ - exitPrompterProvider: createExitPrompter, - }) - this.metadataManager = MetadataManager.getInstance() - } - - public override async run(): Promise { - try { - // Load metadata from JSON file - const projectRoot = path.resolve(__dirname, '../../../../../') - const metadataPath = path.join( - projectRoot, - 'src', - 'awsService', - 'appBuilder', - 'serverlessLand', - 'serverlessland-metadata.json' - ) - await this.metadataManager.loadMetadata(metadataPath) - - // Initialize and display pattern selection - const patterns = this.metadataManager.getPatterns() - if (patterns.length === 0) { - throw new Error('No patterns found in metadata') - } - - const patternPicker = createQuickPick( - patterns.map((p) => ({ - label: p.label, - detail: p.description, - data: p.label, - buttons: [ - { - iconPath: new vscode.ThemeIcon('github'), - tooltip: 'GitHub Button', - }, - { - iconPath: new vscode.ThemeIcon('open-preview'), - tooltip: 'Serverless Land Button', - }, - ], - })), - { - title: 'Select a Pattern for your application', - placeholder: 'Choose a pattern for your project', - buttons: createCommonButtons(), - matchOnDescription: true, - matchOnDetail: true, - } - ) - - const patternResult = await patternPicker.prompt() - if (!patternResult || typeof patternResult !== 'string') { - return undefined // User cancelled or invalid result - } - const selectedPattern = patternResult - - // Show runtime options based on selected pattern - const runtimes = this.metadataManager.getRuntimes(selectedPattern) - if (runtimes.length === 0) { - throw new Error('No runtimes found for the selected pattern') - } - - const runtimePicker = createQuickPick( - runtimes.map((r) => ({ - label: r.label, - data: r.label, - })), - { - title: 'Select Runtime', - placeholder: 'Choose a runtime for your project', - buttons: createCommonButtons(), - } - ) - const runtimeResult = await runtimePicker.prompt() - if (!runtimeResult || typeof runtimeResult !== 'string') { - return undefined // User cancelled or invalid result - } - const selectedRuntime = runtimeResult - - // Show IAC options based on selected pattern - const iacOptions = this.metadataManager.getIacOptions(selectedPattern) - if (iacOptions.length === 0) { - throw new Error('No IAC options found for the selected pattern') - } - - const iacPicker = createQuickPick( - iacOptions.map((i) => ({ - label: i.label, - data: i.label, - })), - { - title: 'Select IaC', - placeholder: 'Choose an IaC option for your project', - buttons: createCommonButtons(), - } - ) - const iacResult = await iacPicker.prompt() - if (!iacResult || typeof iacResult !== 'string') { - return undefined // User cancelled or invalid result - } - const selectedIac = iacResult - - // Create and show location picker - const locationPicker = createFolderPrompt(vscode.workspace.workspaceFolders ?? [], { - title: 'Select Project Location', - buttons: createCommonButtons(), - browseFolderDetail: 'Select a folder for your project', - }) - - const selectedLocation = await locationPicker.prompt() - if (!selectedLocation || !(selectedLocation instanceof vscode.Uri)) { - return undefined // User cancelled or invalid result - } - - // Create and show project name input - const nameInput = createInputBox({ - title: 'Enter Project Name', - placeholder: 'Enter a name for your new application', - buttons: createCommonButtons(), - validateInput: (value: string): string | undefined => { - if (!value) { - return 'Application name cannot be empty' - } - if (value.includes(path.sep)) { - return `The path separator (${path.sep}) is not allowed in application names` - } - return undefined - }, - }) - - const projectName = await nameInput.prompt() - if (!projectName || typeof projectName !== 'string') { - return undefined // User cancelled - } - - // Return the form with all collected values - return { - name: projectName, - location: selectedLocation, - pattern: selectedPattern, - runtime: selectedRuntime, - iac: selectedIac, - } - } catch (err) { - throw new ToolkitError(`Failed to run wizard: ${err instanceof Error ? err.message : String(err)}`) - } - } -} diff --git a/packages/core/src/awsService/appBuilder/serverlessLand/serverlessland-metadata.json b/packages/core/src/awsService/appBuilder/serverlessLand/serverlessland-metadata.json deleted file mode 100644 index 3c5876faeb2..00000000000 --- a/packages/core/src/awsService/appBuilder/serverlessLand/serverlessland-metadata.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "patterns": { - "s3-lambda-resizing-sam": { - "name": "Resizing image", - "description": "Lambda, S3 • Python, Javascript, Java, .NET • SAM", - "runtimes": [ - { - "id": "python", - "name": "Python" - }, - { - "id": "javascript", - "name": "Javascript" - }, - { - "id": "dotnet", - "name": "Dotnet" - }, - { - "id": "java", - "name": "Java" - } - ], - "iac": [ - { - "id": "sam", - "name": "SAM" - } - ] - }, - "apigw-rest-api-lambda-sam": { - "name": "Rest API", - "description": "Lambda, API Gateway • Python, Javascript, Java, .NET • SAM", - "runtimes": [ - { - "id": "python", - "name": "Python" - }, - { - "id": "javascript", - "name": "Javascript" - }, - { - "id": "dotnet", - "name": "Dotnet" - }, - { - "id": "java", - "name": "Java" - } - ], - "iac": [ - { - "id": "sam", - "name": "AWS SAM" - } - ] - } - } -} diff --git a/packages/core/src/awsService/appBuilder/serverlessLand/serverlesslandMetadataManager.ts b/packages/core/src/awsService/appBuilder/serverlessLand/serverlesslandMetadataManager.ts deleted file mode 100644 index 6971eb1d45f..00000000000 --- a/packages/core/src/awsService/appBuilder/serverlessLand/serverlesslandMetadataManager.ts +++ /dev/null @@ -1,122 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ -import * as nodefs from 'fs' // eslint-disable-line no-restricted-imports -import { ToolkitError } from '../../../shared/errors' - -interface IaC { - id: string - name: string -} -interface Runtime { - id: string - name: string - version: string -} -interface PatternData { - name: string - description: string - runtimes: Runtime[] - iac: IaC[] -} - -export interface ProjectMetadata { - patterns: Record -} - -/** - * Manages metadata for serverless application patterns - * Implements Singleton pattern to ensure single instance across the application - */ - -export class MetadataManager { - private static instance: MetadataManager - private metadata: ProjectMetadata | undefined - - private constructor() {} - - public static getInstance(): MetadataManager { - if (!MetadataManager.instance) { - MetadataManager.instance = new MetadataManager() - } - return MetadataManager.instance - } - - /** - * Loads metadata from a JSON file - * @param metadataPath Path to the metadata JSON file - * @returns Promise containing the parsed ProjectMetadata - */ - public async loadMetadata(metadataPath: string): Promise { - try { - if (!this.metadata) { - const metadataContent = nodefs.readFileSync(metadataPath, { encoding: 'utf-8' }) - const parseMetadata = JSON.parse(metadataContent) as ProjectMetadata - this.metadata = parseMetadata - } - return this.metadata - } catch (err) { - throw new ToolkitError(`Failed to load metadata: ${err instanceof Error ? err.message : String(err)}`) - } - } - - /** - * Retrieves available patterns with their descriptions - * @returns Array of pattern objects containing labels and descriptions - */ - public getPatterns(): { label: string; description?: string }[] { - if (!this.metadata) { - return [] - } - return Object.entries(this.metadata.patterns).map(([patternName, patternData]) => { - let description: string | undefined = undefined - if (typeof patternData === 'string') { - description = patternData - } else if (Array.isArray(patternData)) { - // If description is an array, join it into a single string - description = patternData.join(' ') - } - if (!patternData || !patternData.name) { - return { - label: patternName, - description: description || 'No description available', - } - } - return { - label: patternName, - description: patternData.description, - } - }) - } - - /** - * Gets available runtimes for a specific pattern - * @param pattern The pattern name to get runtimes for - * @returns Array of runtime options with labels - */ - public getRuntimes(pattern: string): { label: string }[] { - const patternData = this.metadata?.patterns?.[pattern] - if (!patternData || !patternData.runtimes) { - return [] - } - return patternData.runtimes.map((runtime) => ({ - label: runtime.name, - })) - } - - /** - * Gets available Infrastructure as Code options for a specific pattern - * @param pattern The pattern name to get IaC options for - * @returns Array of IaC options with labels - */ - public getIacOptions(pattern: string): { label: string }[] { - const patternData = this.metadata?.patterns?.[pattern] - if (!patternData || !patternData.iac) { - return [] - } - return patternData.iac.map((iac) => ({ - label: iac.name, - })) - } -} From 14c8ecd1f0c57ec51017628f3257a647d9d60d94 Mon Sep 17 00:00:00 2001 From: Vandita Patidar Date: Thu, 20 Feb 2025 13:06:23 -0800 Subject: [PATCH 04/11] Download code --- .../core/src/awsService/appBuilder/serverlessLand/wizard.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/awsService/appBuilder/serverlessLand/wizard.ts b/packages/core/src/awsService/appBuilder/serverlessLand/wizard.ts index 7bcc17b780c..1964e7e66a2 100644 --- a/packages/core/src/awsService/appBuilder/serverlessLand/wizard.ts +++ b/packages/core/src/awsService/appBuilder/serverlessLand/wizard.ts @@ -11,7 +11,6 @@ import { createInputBox } from '../../../shared/ui/inputPrompter' import { createCommonButtons } from '../../../shared/ui/buttons' import { createQuickPick } from '../../../shared/ui/pickerPrompter' import { createFolderPrompt } from '../../../shared/ui/common/location' -import { createExitPrompter } from '../../../shared/ui/common/exitPrompter' import { MetadataManager } from './metadataManager' import { ToolkitError } from '../../../shared/errors' @@ -22,6 +21,7 @@ export interface CreateServerlessLandWizardForm { pattern: string runtime: string iac: string + assetName: string } function promptPattern(metadataManager: MetadataManager) { From 934e7d39b0cc4ff82fdb84a0970804c14ae3dfdf Mon Sep 17 00:00:00 2001 From: Vandita Patidar Date: Thu, 20 Feb 2025 20:47:12 -0800 Subject: [PATCH 05/11] Updating wizard file --- packages/core/src/awsService/appBuilder/serverlessLand/wizard.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/core/src/awsService/appBuilder/serverlessLand/wizard.ts b/packages/core/src/awsService/appBuilder/serverlessLand/wizard.ts index 1964e7e66a2..5db7729ae10 100644 --- a/packages/core/src/awsService/appBuilder/serverlessLand/wizard.ts +++ b/packages/core/src/awsService/appBuilder/serverlessLand/wizard.ts @@ -11,6 +11,7 @@ import { createInputBox } from '../../../shared/ui/inputPrompter' import { createCommonButtons } from '../../../shared/ui/buttons' import { createQuickPick } from '../../../shared/ui/pickerPrompter' import { createFolderPrompt } from '../../../shared/ui/common/location' +import { createExitPrompter } from '../../../shared/ui/common/exitPrompter' import { MetadataManager } from './metadataManager' import { ToolkitError } from '../../../shared/errors' From f59a1d28d729c695355a8482d12305b6b9991b30 Mon Sep 17 00:00:00 2001 From: Vandita Patidar Date: Thu, 20 Feb 2025 13:06:23 -0800 Subject: [PATCH 06/11] Download code --- .../core/src/awsService/appBuilder/serverlessLand/wizard.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/awsService/appBuilder/serverlessLand/wizard.ts b/packages/core/src/awsService/appBuilder/serverlessLand/wizard.ts index 5db7729ae10..3c2f3f208ad 100644 --- a/packages/core/src/awsService/appBuilder/serverlessLand/wizard.ts +++ b/packages/core/src/awsService/appBuilder/serverlessLand/wizard.ts @@ -5,7 +5,7 @@ import * as nls from 'vscode-nls' import * as AWS from '@aws-sdk/types' import * as vscode from 'vscode' -import { Wizard } from '../../../shared/wizards/wizard' +import { Wizard, WIZARD_BACK, WizardControl } from '../../../shared/wizards/wizard' import * as path from 'path' import { createInputBox } from '../../../shared/ui/inputPrompter' import { createCommonButtons } from '../../../shared/ui/buttons' From c9ab91e3ac198e88a0b0fa6ef176ff5b87db5386 Mon Sep 17 00:00:00 2001 From: Vandita Patidar Date: Sun, 23 Feb 2025 13:26:03 -0800 Subject: [PATCH 07/11] Adding fixes for extension --- .../core/src/awsService/appBuilder/activation.ts | 8 ++++---- .../awsService/appBuilder/serverlessLand/main.ts | 9 ++++++--- .../awsService/appBuilder/serverlessLand/wizard.ts | 5 +++-- packages/core/src/extensionNode.ts | 2 +- packages/toolkit/scripts/build/copyFiles.ts | 13 +++++++++++++ 5 files changed, 27 insertions(+), 10 deletions(-) diff --git a/packages/core/src/awsService/appBuilder/activation.ts b/packages/core/src/awsService/appBuilder/activation.ts index b605ac3528c..0c1d70bdfe2 100644 --- a/packages/core/src/awsService/appBuilder/activation.ts +++ b/packages/core/src/awsService/appBuilder/activation.ts @@ -32,14 +32,14 @@ export const templateToOpenAppComposer = 'aws.toolkit.appComposer.templateToOpen * IMPORTANT: Views that should work in all vscode environments (node or web) * should be setup in {@link activateViewsShared}. */ -export async function activate(context: ExtContext): Promise { +export async function activate(context: ExtContext, ctx: vscode.ExtensionContext): Promise { // recover context variables from global state when activate const walkthroughSelected = globals.globalState.get(walkthroughContextString) if (walkthroughSelected !== undefined) { await setContext(walkthroughContextString, walkthroughSelected) } - await registerAppBuilderCommands(context) + await registerAppBuilderCommands(context, ctx) const appBuilderNode: ToolView[] = [ { @@ -123,7 +123,7 @@ async function setWalkthrough(walkthroughSelected: string = 'S3'): Promise * * @param context VScode Context */ -async function registerAppBuilderCommands(context: ExtContext): Promise { +async function registerAppBuilderCommands(context: ExtContext, ctx: vscode.ExtensionContext): Promise { const source = 'AppBuilderWalkthrough' context.extensionContext.subscriptions.push( Commands.register('aws.toolkit.installSAMCLI', async () => { @@ -202,7 +202,7 @@ async function registerAppBuilderCommands(context: ExtContext): Promise { } }), Commands.register({ id: 'aws.toolkit.lambda.createServerlessLandProject', autoconnect: false }, async () => { - await createNewServerlessLandProject(context) + await createNewServerlessLandProject(context, ctx) }) ) } diff --git a/packages/core/src/awsService/appBuilder/serverlessLand/main.ts b/packages/core/src/awsService/appBuilder/serverlessLand/main.ts index 3b4efbacf04..6734eabc0f6 100644 --- a/packages/core/src/awsService/appBuilder/serverlessLand/main.ts +++ b/packages/core/src/awsService/appBuilder/serverlessLand/main.ts @@ -18,6 +18,7 @@ import { ToolkitError } from '../../../shared/errors' import { fs } from '../../../shared/fs/fs' import { getPattern } from '../../../shared/utilities/downloadPatterns' import { MetadataManager } from './metadataManager' +import type { ExtensionContext } from 'vscode' export const readmeFile: string = 'README.md' const serverlessLandOwner = 'aws-samples' @@ -36,7 +37,7 @@ const serverlessLandRepo = 'serverless-patterns' * 5. Opens the README.md file if available * 6. Handles errors and emits telemetry */ -export async function createNewServerlessLandProject(extContext: ExtContext): Promise { +export async function createNewServerlessLandProject(extContext: ExtContext, ctx: ExtensionContext): Promise { let createResult: Result = 'Succeeded' let reason: string | undefined let metadataManager: MetadataManager @@ -44,7 +45,7 @@ export async function createNewServerlessLandProject(extContext: ExtContext): Pr try { metadataManager = MetadataManager.getInstance() // Launch the project creation wizard - const config = await launchProjectCreationWizard(extContext) + const config = await launchProjectCreationWizard(extContext, ctx) if (!config) { createResult = 'Cancelled' reason = 'userCancelled' @@ -83,13 +84,15 @@ export async function createNewServerlessLandProject(extContext: ExtContext): Pr } async function launchProjectCreationWizard( - extContext: ExtContext + extContext: ExtContext, + ctx: ExtensionContext ): Promise { const awsContext = extContext.awsContext const credentials = await awsContext.getCredentials() const defaultRegion = awsContext.getCredentialDefaultRegion() return new CreateServerlessLandWizard({ + ctx, credentials, defaultRegion, }).run() diff --git a/packages/core/src/awsService/appBuilder/serverlessLand/wizard.ts b/packages/core/src/awsService/appBuilder/serverlessLand/wizard.ts index 3c2f3f208ad..beb8269f0ef 100644 --- a/packages/core/src/awsService/appBuilder/serverlessLand/wizard.ts +++ b/packages/core/src/awsService/appBuilder/serverlessLand/wizard.ts @@ -5,7 +5,7 @@ import * as nls from 'vscode-nls' import * as AWS from '@aws-sdk/types' import * as vscode from 'vscode' -import { Wizard, WIZARD_BACK, WizardControl } from '../../../shared/wizards/wizard' +import { Wizard } from '../../../shared/wizards/wizard' import * as path from 'path' import { createInputBox } from '../../../shared/ui/inputPrompter' import { createCommonButtons } from '../../../shared/ui/buttons' @@ -13,6 +13,7 @@ import { createQuickPick } from '../../../shared/ui/pickerPrompter' import { createFolderPrompt } from '../../../shared/ui/common/location' import { createExitPrompter } from '../../../shared/ui/common/exitPrompter' import { MetadataManager } from './metadataManager' +import type { ExtensionContext } from 'vscode' import { ToolkitError } from '../../../shared/errors' const localize = nls.loadMessageBundle() @@ -135,7 +136,7 @@ function promptName() { export class CreateServerlessLandWizard extends Wizard { private metadataManager: MetadataManager - public constructor(context: { defaultRegion?: string; credentials?: AWS.Credentials }) { + public constructor(context: { ctx: ExtensionContext; defaultRegion?: string; credentials?: AWS.Credentials }) { super({ exitPrompterProvider: createExitPrompter, }) diff --git a/packages/core/src/extensionNode.ts b/packages/core/src/extensionNode.ts index a3e5e03b4ab..6f98f6c69e9 100644 --- a/packages/core/src/extensionNode.ts +++ b/packages/core/src/extensionNode.ts @@ -200,7 +200,7 @@ export async function activate(context: vscode.ExtensionContext) { await activateRedshift(extContext) - await activateAppBuilder(extContext) + await activateAppBuilder(extContext, context) await activateDocumentDb(extContext) diff --git a/packages/toolkit/scripts/build/copyFiles.ts b/packages/toolkit/scripts/build/copyFiles.ts index 6cea899b4ca..7d065040416 100644 --- a/packages/toolkit/scripts/build/copyFiles.ts +++ b/packages/toolkit/scripts/build/copyFiles.ts @@ -69,6 +69,19 @@ const tasks: CopyTask[] = [ destination: path.join('src', 'stepFunctions', 'asl', 'aslServer.js'), }, + // Serverless Land + { + target: path.join( + '../../node_modules/aws-core-vscode', + 'src', + 'awsService', + 'appBuilder', + 'serverlessLand', + 'metadata.json' + ), + destination: path.join('src', 'serverlessLand', 'metadata.json'), + }, + // Vue { target: path.join('../core', 'resources', 'js', 'vscode.js'), From d5698e99ab1138ff3139eb66e3c3d7d1e5161af5 Mon Sep 17 00:00:00 2001 From: Vandita Patidar Date: Mon, 24 Feb 2025 13:41:30 -0800 Subject: [PATCH 08/11] Web view --- .../appBuilder/serverlessLand/metadata.json | 1 + .../serverlessLand/metadataManager.ts | 22 ++++++ .../serverlessLand/webViewManager.ts | 73 +++++++++++++++++++ .../appBuilder/serverlessLand/wizard.ts | 4 +- packages/core/src/shared/ui/pickerPrompter.ts | 41 +++++++++++ 5 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 packages/core/src/awsService/appBuilder/serverlessLand/webViewManager.ts diff --git a/packages/core/src/awsService/appBuilder/serverlessLand/metadata.json b/packages/core/src/awsService/appBuilder/serverlessLand/metadata.json index e172899dd4a..8495fe560e4 100644 --- a/packages/core/src/awsService/appBuilder/serverlessLand/metadata.json +++ b/packages/core/src/awsService/appBuilder/serverlessLand/metadata.json @@ -3,6 +3,7 @@ "s3-lambda-resizing-sam": { "name": "Resizing image", "description": "Lambda, S3 • Python, Javascript, Java, .NET • SAM", + "gitUrl": "https://github.com/aws-samples/serverless-patterns/tree/main/s3-lambda-resizing-python", "implementation": [ { "iac": "sam", diff --git a/packages/core/src/awsService/appBuilder/serverlessLand/metadataManager.ts b/packages/core/src/awsService/appBuilder/serverlessLand/metadataManager.ts index 1cca874de4d..7f628f63c9f 100644 --- a/packages/core/src/awsService/appBuilder/serverlessLand/metadataManager.ts +++ b/packages/core/src/awsService/appBuilder/serverlessLand/metadataManager.ts @@ -17,6 +17,10 @@ interface PatternData { implementation: Implementation[] } +interface PatternUrls { + githubUrl: string + previewUrl: string +} export interface ProjectMetadata { patterns: Record } @@ -117,6 +121,24 @@ export class MetadataManager { })) } + public getUrl(pattern: string): PatternUrls { + const patternData = this.metadata?.patterns?.[pattern] + if (!patternData || !patternData.implementation) { + return { + githubUrl: '', + previewUrl: '', + } + } + const asset = patternData.implementation[0].assetName + + return { + // GitHub URL for the pattern + githubUrl: `https://github.com/aws-samples/serverless-patterns/tree/main/${asset}`, + // Serverless Land preview URL + previewUrl: `https://serverlessland.com/patterns/${asset}`, + } + } + /** * Gets available Infrastructure as Code options for a specific pattern * @param pattern The pattern name to get IaC options for diff --git a/packages/core/src/awsService/appBuilder/serverlessLand/webViewManager.ts b/packages/core/src/awsService/appBuilder/serverlessLand/webViewManager.ts new file mode 100644 index 00000000000..0bb9e70bef6 --- /dev/null +++ b/packages/core/src/awsService/appBuilder/serverlessLand/webViewManager.ts @@ -0,0 +1,73 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +export class WebviewService { + constructor() {} + + public static getWebviewContent(url: string) { + return ` + + + + + + + + + + + + + ` + } + + public static getGitWebviewContent(url: string): string { + const htmlContent = ` + + + + + + +

To preview GitHub page, click here.

+ + + ` + return htmlContent + } +} diff --git a/packages/core/src/awsService/appBuilder/serverlessLand/wizard.ts b/packages/core/src/awsService/appBuilder/serverlessLand/wizard.ts index beb8269f0ef..1106436d7b1 100644 --- a/packages/core/src/awsService/appBuilder/serverlessLand/wizard.ts +++ b/packages/core/src/awsService/appBuilder/serverlessLand/wizard.ts @@ -32,7 +32,7 @@ function promptPattern(metadataManager: MetadataManager) { throw new ToolkitError('No patterns found in metadata') } - return createQuickPick( + const quickPick = createQuickPick( patterns.map((p) => ({ label: p.label, detail: p.description, @@ -56,6 +56,8 @@ function promptPattern(metadataManager: MetadataManager) { matchOnDetail: true, } ) + + return quickPick } function promptRuntime(metadataManager: MetadataManager, pattern: string | undefined) { diff --git a/packages/core/src/shared/ui/pickerPrompter.ts b/packages/core/src/shared/ui/pickerPrompter.ts index daa65246fed..599f5302caf 100644 --- a/packages/core/src/shared/ui/pickerPrompter.ts +++ b/packages/core/src/shared/ui/pickerPrompter.ts @@ -12,6 +12,8 @@ import { Prompter, PromptResult, Transform } from './prompter' import { assign, isAsyncIterable } from '../utilities/collectionUtils' import { recentlyUsed } from '../localizedText' import { getLogger } from '../logger/logger' +import { MetadataManager } from '../../awsService/appBuilder/serverlessLand/metadataManager' +import { WebviewService } from '../../awsService/appBuilder/serverlessLand/webViewManager' const localize = nls.loadMessageBundle() @@ -143,6 +145,45 @@ export function createQuickPick( assign(mergedOptions, picker) picker.buttons = mergedOptions.buttons ?? [] + picker.onDidTriggerItemButton(async (event) => { + const metadataManager = MetadataManager.getInstance() + if (event.button.tooltip === 'Open in GitHub' || event.button.tooltip === 'Open in Serverless Land') { + const selectedPattern = event.item + if (selectedPattern) { + const patternUrl = metadataManager.getUrl(selectedPattern.label) + if (patternUrl) { + if (event.button.tooltip === 'Open in GitHub') { + const panel = vscode.window.createWebviewPanel( + 'githubPreview', + `GitHub Repository ${selectedPattern.label}`, + vscode.ViewColumn.One, + { + enableScripts: true, + retainContextWhenHidden: true, + enableCommandUris: true, + enableFindWidget: true, + } + ) + panel.webview.html = WebviewService.getGitWebviewContent(patternUrl.githubUrl) + } else if (event.button.tooltip === 'Open in Serverless Land') { + const panel = vscode.window.createWebviewPanel( + 'serverlessLandPreview', + 'Serverless Land Preview', + vscode.ViewColumn.One, + { + enableScripts: true, + retainContextWhenHidden: true, + enableCommandUris: true, + enableFindWidget: true, + } + ) + panel.webview.html = WebviewService.getWebviewContent(patternUrl.previewUrl) + } + } + } + } + }) + const prompter = mergedOptions.filterBoxInputSettings !== undefined ? new FilterBoxQuickPickPrompter(picker, mergedOptions) From 56bc627c0d8b4d516919c45f61f4755a6e509c4e Mon Sep 17 00:00:00 2001 From: Vandita Patidar Date: Tue, 25 Feb 2025 22:03:41 -0800 Subject: [PATCH 09/11] Fixing bug bash changes --- packages/core/package.nls.json | 2 +- .../src/awsService/appBuilder/activation.ts | 8 ++-- .../appBuilder/serverlessLand/main.ts | 12 ++--- .../appBuilder/serverlessLand/metadata.json | 1 - .../serverlessLand/metadataManager.ts | 16 +++---- .../serverlessLand/webViewManager.ts | 36 --------------- .../appBuilder/serverlessLand/wizard.ts | 7 +-- packages/core/src/extensionNode.ts | 2 +- packages/core/src/shared/ui/pickerPrompter.ts | 46 +++++++++---------- packages/toolkit/package.json | 13 ++++-- 10 files changed, 51 insertions(+), 92 deletions(-) diff --git a/packages/core/package.nls.json b/packages/core/package.nls.json index 99461cd1069..92b7600fa50 100644 --- a/packages/core/package.nls.json +++ b/packages/core/package.nls.json @@ -437,5 +437,5 @@ "AWS.toolkit.lambda.walkthrough.step1.description": "Locally test and debug your code.", "AWS.toolkit.lambda.walkthrough.step2.title": "Deploy to the cloud", "AWS.toolkit.lambda.walkthrough.step2.description": "Test your application in the cloud from within VS Code. \n\nNote: The AWS CLI and the SAM CLI require AWS Credentials to interact with the cloud. For information on setting up your credentials, see [Authentication and access credentials](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-configure.html). \n\n[Configure credentials](command:aws.toolkit.lambda.walkthrough.credential)", - "AWS.toolkit.lambda.serverlessLand.quickpickTitle": "Create Lambda Application from template" + "AWS.toolkit.lambda.serverlessLand.quickpickTitle": "Create application with Serverless template" } diff --git a/packages/core/src/awsService/appBuilder/activation.ts b/packages/core/src/awsService/appBuilder/activation.ts index 0c1d70bdfe2..b605ac3528c 100644 --- a/packages/core/src/awsService/appBuilder/activation.ts +++ b/packages/core/src/awsService/appBuilder/activation.ts @@ -32,14 +32,14 @@ export const templateToOpenAppComposer = 'aws.toolkit.appComposer.templateToOpen * IMPORTANT: Views that should work in all vscode environments (node or web) * should be setup in {@link activateViewsShared}. */ -export async function activate(context: ExtContext, ctx: vscode.ExtensionContext): Promise { +export async function activate(context: ExtContext): Promise { // recover context variables from global state when activate const walkthroughSelected = globals.globalState.get(walkthroughContextString) if (walkthroughSelected !== undefined) { await setContext(walkthroughContextString, walkthroughSelected) } - await registerAppBuilderCommands(context, ctx) + await registerAppBuilderCommands(context) const appBuilderNode: ToolView[] = [ { @@ -123,7 +123,7 @@ async function setWalkthrough(walkthroughSelected: string = 'S3'): Promise * * @param context VScode Context */ -async function registerAppBuilderCommands(context: ExtContext, ctx: vscode.ExtensionContext): Promise { +async function registerAppBuilderCommands(context: ExtContext): Promise { const source = 'AppBuilderWalkthrough' context.extensionContext.subscriptions.push( Commands.register('aws.toolkit.installSAMCLI', async () => { @@ -202,7 +202,7 @@ async function registerAppBuilderCommands(context: ExtContext, ctx: vscode.Exten } }), Commands.register({ id: 'aws.toolkit.lambda.createServerlessLandProject', autoconnect: false }, async () => { - await createNewServerlessLandProject(context, ctx) + await createNewServerlessLandProject(context) }) ) } diff --git a/packages/core/src/awsService/appBuilder/serverlessLand/main.ts b/packages/core/src/awsService/appBuilder/serverlessLand/main.ts index 6734eabc0f6..a07c7e0814b 100644 --- a/packages/core/src/awsService/appBuilder/serverlessLand/main.ts +++ b/packages/core/src/awsService/appBuilder/serverlessLand/main.ts @@ -18,7 +18,6 @@ import { ToolkitError } from '../../../shared/errors' import { fs } from '../../../shared/fs/fs' import { getPattern } from '../../../shared/utilities/downloadPatterns' import { MetadataManager } from './metadataManager' -import type { ExtensionContext } from 'vscode' export const readmeFile: string = 'README.md' const serverlessLandOwner = 'aws-samples' @@ -37,7 +36,7 @@ const serverlessLandRepo = 'serverless-patterns' * 5. Opens the README.md file if available * 6. Handles errors and emits telemetry */ -export async function createNewServerlessLandProject(extContext: ExtContext, ctx: ExtensionContext): Promise { +export async function createNewServerlessLandProject(extContext: ExtContext): Promise { let createResult: Result = 'Succeeded' let reason: string | undefined let metadataManager: MetadataManager @@ -45,7 +44,7 @@ export async function createNewServerlessLandProject(extContext: ExtContext, ctx try { metadataManager = MetadataManager.getInstance() // Launch the project creation wizard - const config = await launchProjectCreationWizard(extContext, ctx) + const config = await launchProjectCreationWizard(extContext) if (!config) { createResult = 'Cancelled' reason = 'userCancelled' @@ -84,15 +83,13 @@ export async function createNewServerlessLandProject(extContext: ExtContext, ctx } async function launchProjectCreationWizard( - extContext: ExtContext, - ctx: ExtensionContext + extContext: ExtContext ): Promise { const awsContext = extContext.awsContext const credentials = await awsContext.getCredentials() const defaultRegion = awsContext.getCredentialDefaultRegion() return new CreateServerlessLandWizard({ - ctx, credentials, defaultRegion, }).run() @@ -118,9 +115,10 @@ async function openReadmeFile(config: CreateServerlessLandWizardForm): Promise setTimeout(resolve, 1000)) await vscode.commands.executeCommand('workbench.action.focusFirstEditorGroup') - await vscode.window.showTextDocument(readmeUri) + await vscode.commands.executeCommand('markdown.showPreview', readmeUri) } catch (err) { getLogger().error(`Error in openReadmeFile: ${err}`) throw new ToolkitError('Error processing README file') diff --git a/packages/core/src/awsService/appBuilder/serverlessLand/metadata.json b/packages/core/src/awsService/appBuilder/serverlessLand/metadata.json index 8495fe560e4..e172899dd4a 100644 --- a/packages/core/src/awsService/appBuilder/serverlessLand/metadata.json +++ b/packages/core/src/awsService/appBuilder/serverlessLand/metadata.json @@ -3,7 +3,6 @@ "s3-lambda-resizing-sam": { "name": "Resizing image", "description": "Lambda, S3 • Python, Javascript, Java, .NET • SAM", - "gitUrl": "https://github.com/aws-samples/serverless-patterns/tree/main/s3-lambda-resizing-python", "implementation": [ { "iac": "sam", diff --git a/packages/core/src/awsService/appBuilder/serverlessLand/metadataManager.ts b/packages/core/src/awsService/appBuilder/serverlessLand/metadataManager.ts index 7f628f63c9f..94570bdd319 100644 --- a/packages/core/src/awsService/appBuilder/serverlessLand/metadataManager.ts +++ b/packages/core/src/awsService/appBuilder/serverlessLand/metadataManager.ts @@ -5,6 +5,7 @@ import * as nodefs from 'fs' // eslint-disable-line no-restricted-imports import { ToolkitError } from '../../../shared/errors' import path from 'path' +import globals from '../../../shared/extensionGlobals' interface Implementation { iac: string @@ -32,14 +33,6 @@ export interface ProjectMetadata { export class MetadataManager { private static instance: MetadataManager private metadata: ProjectMetadata | undefined - private static readonly metadataPath = path.join( - path.resolve(__dirname, '../../../../../'), - 'src', - 'awsService', - 'appBuilder', - 'serverlessLand', - 'metadata.json' - ) private constructor() {} @@ -52,12 +45,17 @@ export class MetadataManager { public static initialize(): MetadataManager { const instance = MetadataManager.getInstance() - instance.loadMetadata(MetadataManager.metadataPath).catch((err) => { + const metadataPath = instance.getMetadataPath() + instance.loadMetadata(metadataPath).catch((err) => { throw new ToolkitError(`Failed to load metadata: ${err}`) }) return instance } + public getMetadataPath(): string { + return globals.context.asAbsolutePath(path.join('dist', 'src', 'serverlessLand', 'metadata.json')) + } + /** * Loads metadata from a JSON file * @param metadataPath Path to the metadata JSON file diff --git a/packages/core/src/awsService/appBuilder/serverlessLand/webViewManager.ts b/packages/core/src/awsService/appBuilder/serverlessLand/webViewManager.ts index 0bb9e70bef6..250ee883241 100644 --- a/packages/core/src/awsService/appBuilder/serverlessLand/webViewManager.ts +++ b/packages/core/src/awsService/appBuilder/serverlessLand/webViewManager.ts @@ -34,40 +34,4 @@ export class WebviewService { ` } - - public static getGitWebviewContent(url: string): string { - const htmlContent = ` - - - - - - -

To preview GitHub page, click here.

- - - ` - return htmlContent - } } diff --git a/packages/core/src/awsService/appBuilder/serverlessLand/wizard.ts b/packages/core/src/awsService/appBuilder/serverlessLand/wizard.ts index 1106436d7b1..5db7729ae10 100644 --- a/packages/core/src/awsService/appBuilder/serverlessLand/wizard.ts +++ b/packages/core/src/awsService/appBuilder/serverlessLand/wizard.ts @@ -13,7 +13,6 @@ import { createQuickPick } from '../../../shared/ui/pickerPrompter' import { createFolderPrompt } from '../../../shared/ui/common/location' import { createExitPrompter } from '../../../shared/ui/common/exitPrompter' import { MetadataManager } from './metadataManager' -import type { ExtensionContext } from 'vscode' import { ToolkitError } from '../../../shared/errors' const localize = nls.loadMessageBundle() @@ -32,7 +31,7 @@ function promptPattern(metadataManager: MetadataManager) { throw new ToolkitError('No patterns found in metadata') } - const quickPick = createQuickPick( + return createQuickPick( patterns.map((p) => ({ label: p.label, detail: p.description, @@ -56,8 +55,6 @@ function promptPattern(metadataManager: MetadataManager) { matchOnDetail: true, } ) - - return quickPick } function promptRuntime(metadataManager: MetadataManager, pattern: string | undefined) { @@ -138,7 +135,7 @@ function promptName() { export class CreateServerlessLandWizard extends Wizard { private metadataManager: MetadataManager - public constructor(context: { ctx: ExtensionContext; defaultRegion?: string; credentials?: AWS.Credentials }) { + public constructor(context: { defaultRegion?: string; credentials?: AWS.Credentials }) { super({ exitPrompterProvider: createExitPrompter, }) diff --git a/packages/core/src/extensionNode.ts b/packages/core/src/extensionNode.ts index 6f98f6c69e9..a3e5e03b4ab 100644 --- a/packages/core/src/extensionNode.ts +++ b/packages/core/src/extensionNode.ts @@ -200,7 +200,7 @@ export async function activate(context: vscode.ExtensionContext) { await activateRedshift(extContext) - await activateAppBuilder(extContext, context) + await activateAppBuilder(extContext) await activateDocumentDb(extContext) diff --git a/packages/core/src/shared/ui/pickerPrompter.ts b/packages/core/src/shared/ui/pickerPrompter.ts index 599f5302caf..8b8b5ad542d 100644 --- a/packages/core/src/shared/ui/pickerPrompter.ts +++ b/packages/core/src/shared/ui/pickerPrompter.ts @@ -144,6 +144,7 @@ export function createQuickPick( const mergedOptions = { ...defaultQuickpickOptions, ...options } assign(mergedOptions, picker) picker.buttons = mergedOptions.buttons ?? [] + let serverlessPanel: vscode.WebviewPanel | undefined picker.onDidTriggerItemButton(async (event) => { const metadataManager = MetadataManager.getInstance() @@ -153,31 +154,28 @@ export function createQuickPick( const patternUrl = metadataManager.getUrl(selectedPattern.label) if (patternUrl) { if (event.button.tooltip === 'Open in GitHub') { - const panel = vscode.window.createWebviewPanel( - 'githubPreview', - `GitHub Repository ${selectedPattern.label}`, - vscode.ViewColumn.One, - { - enableScripts: true, - retainContextWhenHidden: true, - enableCommandUris: true, - enableFindWidget: true, - } - ) - panel.webview.html = WebviewService.getGitWebviewContent(patternUrl.githubUrl) + await vscode.env.openExternal(vscode.Uri.parse(patternUrl.githubUrl)) } else if (event.button.tooltip === 'Open in Serverless Land') { - const panel = vscode.window.createWebviewPanel( - 'serverlessLandPreview', - 'Serverless Land Preview', - vscode.ViewColumn.One, - { - enableScripts: true, - retainContextWhenHidden: true, - enableCommandUris: true, - enableFindWidget: true, - } - ) - panel.webview.html = WebviewService.getWebviewContent(patternUrl.previewUrl) + if (!serverlessPanel) { + serverlessPanel = vscode.window.createWebviewPanel( + 'serverlessLandPreview', + `${selectedPattern.label}`, + vscode.ViewColumn.One, + { + enableScripts: true, + retainContextWhenHidden: true, + enableCommandUris: false, + enableFindWidget: true, + } + ) + serverlessPanel.onDidDispose(() => { + serverlessPanel = undefined + }) + } else { + serverlessPanel.title = `${selectedPattern.label}` + } + serverlessPanel.webview.html = WebviewService.getWebviewContent(patternUrl.previewUrl) + serverlessPanel.reveal() } } } diff --git a/packages/toolkit/package.json b/packages/toolkit/package.json index c662436830d..749b05f16ec 100644 --- a/packages/toolkit/package.json +++ b/packages/toolkit/package.json @@ -1327,24 +1327,29 @@ "group": "1_account@3" }, { - "command": "aws.lambda.createNewSamApp", + "command": "aws.toolkit.lambda.createServerlessLandProject", "when": "view == aws.explorer", "group": "3_lambda@1" }, { - "command": "aws.launchConfigForm", + "command": "aws.lambda.createNewSamApp", "when": "view == aws.explorer", "group": "3_lambda@2" }, + { + "command": "aws.launchConfigForm", + "when": "view == aws.explorer", + "group": "3_lambda@3" + }, { "command": "aws.deploySamApplication", "when": "config.aws.samcli.legacyDeploy && view == aws.explorer", - "group": "3_lambda@3" + "group": "3_lambda@4" }, { "command": "aws.samcli.sync", "when": "!config.aws.samcli.legacyDeploy && view == aws.explorer", - "group": "3_lambda@3" + "group": "3_lambda@4" }, { "submenu": "aws.toolkit.submenu.feedback", From d226d316ae9bf0f886a8aba9674496b0df1d1e12 Mon Sep 17 00:00:00 2001 From: Vandita Patidar Date: Thu, 27 Feb 2025 23:38:04 -0800 Subject: [PATCH 10/11] removing wwebview --- .../appBuilder/serverlessLand/main.ts | 2 +- .../serverlessLand/metadataManager.ts | 18 ++------ .../serverlessLand/webViewManager.ts | 37 ---------------- .../appBuilder/serverlessLand/wizard.ts | 6 +-- packages/core/src/shared/ui/pickerPrompter.ts | 44 +++++-------------- 5 files changed, 16 insertions(+), 91 deletions(-) delete mode 100644 packages/core/src/awsService/appBuilder/serverlessLand/webViewManager.ts diff --git a/packages/core/src/awsService/appBuilder/serverlessLand/main.ts b/packages/core/src/awsService/appBuilder/serverlessLand/main.ts index a07c7e0814b..9b64a955fbf 100644 --- a/packages/core/src/awsService/appBuilder/serverlessLand/main.ts +++ b/packages/core/src/awsService/appBuilder/serverlessLand/main.ts @@ -115,7 +115,7 @@ async function openReadmeFile(config: CreateServerlessLandWizardForm): Promise setTimeout(resolve, 1000)) + await new Promise((resolve) => setTimeout(resolve, 2000)) await vscode.commands.executeCommand('workbench.action.focusFirstEditorGroup') await vscode.commands.executeCommand('markdown.showPreview', readmeUri) diff --git a/packages/core/src/awsService/appBuilder/serverlessLand/metadataManager.ts b/packages/core/src/awsService/appBuilder/serverlessLand/metadataManager.ts index 94570bdd319..5299b1302ca 100644 --- a/packages/core/src/awsService/appBuilder/serverlessLand/metadataManager.ts +++ b/packages/core/src/awsService/appBuilder/serverlessLand/metadataManager.ts @@ -18,10 +18,6 @@ interface PatternData { implementation: Implementation[] } -interface PatternUrls { - githubUrl: string - previewUrl: string -} export interface ProjectMetadata { patterns: Record } @@ -119,22 +115,14 @@ export class MetadataManager { })) } - public getUrl(pattern: string): PatternUrls { + public getUrl(pattern: string): string { const patternData = this.metadata?.patterns?.[pattern] if (!patternData || !patternData.implementation) { - return { - githubUrl: '', - previewUrl: '', - } + return '' } const asset = patternData.implementation[0].assetName - return { - // GitHub URL for the pattern - githubUrl: `https://github.com/aws-samples/serverless-patterns/tree/main/${asset}`, - // Serverless Land preview URL - previewUrl: `https://serverlessland.com/patterns/${asset}`, - } + return `https://serverlessland.com/patterns/${asset}` } /** diff --git a/packages/core/src/awsService/appBuilder/serverlessLand/webViewManager.ts b/packages/core/src/awsService/appBuilder/serverlessLand/webViewManager.ts deleted file mode 100644 index 250ee883241..00000000000 --- a/packages/core/src/awsService/appBuilder/serverlessLand/webViewManager.ts +++ /dev/null @@ -1,37 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ -export class WebviewService { - constructor() {} - - public static getWebviewContent(url: string) { - return ` - - - - - - - - - - - - - ` - } -} diff --git a/packages/core/src/awsService/appBuilder/serverlessLand/wizard.ts b/packages/core/src/awsService/appBuilder/serverlessLand/wizard.ts index 5db7729ae10..9f2b66ee261 100644 --- a/packages/core/src/awsService/appBuilder/serverlessLand/wizard.ts +++ b/packages/core/src/awsService/appBuilder/serverlessLand/wizard.ts @@ -38,11 +38,7 @@ function promptPattern(metadataManager: MetadataManager) { data: p.label, buttons: [ { - iconPath: new vscode.ThemeIcon('github'), - tooltip: 'Open in GitHub', - }, - { - iconPath: new vscode.ThemeIcon('open-preview'), + iconPath: new vscode.ThemeIcon('link-external'), tooltip: 'Open in Serverless Land', }, ], diff --git a/packages/core/src/shared/ui/pickerPrompter.ts b/packages/core/src/shared/ui/pickerPrompter.ts index 8b8b5ad542d..33099a8170f 100644 --- a/packages/core/src/shared/ui/pickerPrompter.ts +++ b/packages/core/src/shared/ui/pickerPrompter.ts @@ -13,7 +13,6 @@ import { assign, isAsyncIterable } from '../utilities/collectionUtils' import { recentlyUsed } from '../localizedText' import { getLogger } from '../logger/logger' import { MetadataManager } from '../../awsService/appBuilder/serverlessLand/metadataManager' -import { WebviewService } from '../../awsService/appBuilder/serverlessLand/webViewManager' const localize = nls.loadMessageBundle() @@ -144,42 +143,21 @@ export function createQuickPick( const mergedOptions = { ...defaultQuickpickOptions, ...options } assign(mergedOptions, picker) picker.buttons = mergedOptions.buttons ?? [] - let serverlessPanel: vscode.WebviewPanel | undefined picker.onDidTriggerItemButton(async (event) => { const metadataManager = MetadataManager.getInstance() - if (event.button.tooltip === 'Open in GitHub' || event.button.tooltip === 'Open in Serverless Land') { - const selectedPattern = event.item - if (selectedPattern) { - const patternUrl = metadataManager.getUrl(selectedPattern.label) - if (patternUrl) { - if (event.button.tooltip === 'Open in GitHub') { - await vscode.env.openExternal(vscode.Uri.parse(patternUrl.githubUrl)) - } else if (event.button.tooltip === 'Open in Serverless Land') { - if (!serverlessPanel) { - serverlessPanel = vscode.window.createWebviewPanel( - 'serverlessLandPreview', - `${selectedPattern.label}`, - vscode.ViewColumn.One, - { - enableScripts: true, - retainContextWhenHidden: true, - enableCommandUris: false, - enableFindWidget: true, - } - ) - serverlessPanel.onDidDispose(() => { - serverlessPanel = undefined - }) - } else { - serverlessPanel.title = `${selectedPattern.label}` - } - serverlessPanel.webview.html = WebviewService.getWebviewContent(patternUrl.previewUrl) - serverlessPanel.reveal() - } - } - } + if (event.button.tooltip !== 'Open in Serverless Land') { + return + } + const selectedPattern = event.item + if (!selectedPattern) { + return + } + const patternUrl = metadataManager.getUrl(selectedPattern.label) + if (!patternUrl) { + return } + await vscode.env.openExternal(vscode.Uri.parse(patternUrl)) }) const prompter = From f63ee4f9a8f1dedd0540410c66eb32d71e478d40 Mon Sep 17 00:00:00 2001 From: Vandita Patidar Date: Sun, 2 Mar 2025 16:01:15 -0800 Subject: [PATCH 11/11] quickPick changes --- .../appBuilder/serverlessLand/main.ts | 2 -- .../serverlessLand/metadataManager.ts | 3 +-- .../appBuilder/serverlessLand/wizard.ts | 17 +++++++++-------- packages/core/src/shared/ui/pickerPrompter.ts | 3 ++- 4 files changed, 12 insertions(+), 13 deletions(-) diff --git a/packages/core/src/awsService/appBuilder/serverlessLand/main.ts b/packages/core/src/awsService/appBuilder/serverlessLand/main.ts index 9b64a955fbf..4b76901cf4e 100644 --- a/packages/core/src/awsService/appBuilder/serverlessLand/main.ts +++ b/packages/core/src/awsService/appBuilder/serverlessLand/main.ts @@ -115,8 +115,6 @@ async function openReadmeFile(config: CreateServerlessLandWizardForm): Promise setTimeout(resolve, 2000)) - await vscode.commands.executeCommand('workbench.action.focusFirstEditorGroup') await vscode.commands.executeCommand('markdown.showPreview', readmeUri) } catch (err) { diff --git a/packages/core/src/awsService/appBuilder/serverlessLand/metadataManager.ts b/packages/core/src/awsService/appBuilder/serverlessLand/metadataManager.ts index 5299b1302ca..f649185bff2 100644 --- a/packages/core/src/awsService/appBuilder/serverlessLand/metadataManager.ts +++ b/packages/core/src/awsService/appBuilder/serverlessLand/metadataManager.ts @@ -4,7 +4,6 @@ */ import * as nodefs from 'fs' // eslint-disable-line no-restricted-imports import { ToolkitError } from '../../../shared/errors' -import path from 'path' import globals from '../../../shared/extensionGlobals' interface Implementation { @@ -49,7 +48,7 @@ export class MetadataManager { } public getMetadataPath(): string { - return globals.context.asAbsolutePath(path.join('dist', 'src', 'serverlessLand', 'metadata.json')) + return globals.context.asAbsolutePath('dist/src/serverlessLand/metadata.json') } /** diff --git a/packages/core/src/awsService/appBuilder/serverlessLand/wizard.ts b/packages/core/src/awsService/appBuilder/serverlessLand/wizard.ts index 9f2b66ee261..29a7efa3637 100644 --- a/packages/core/src/awsService/appBuilder/serverlessLand/wizard.ts +++ b/packages/core/src/awsService/appBuilder/serverlessLand/wizard.ts @@ -71,7 +71,7 @@ function promptRuntime(metadataManager: MetadataManager, pattern: string | undef { title: localize('AWS.serverlessLand.initWizard.runtime.prompt', 'Select Runtime'), placeholder: 'Choose a runtime for your project', - buttons: [vscode.QuickInputButtons.Back], + buttons: createCommonButtons(), } ) } @@ -94,7 +94,7 @@ function promptIac(metadataManager: MetadataManager, pattern: string | undefined { title: localize('AWS.serverlessLand.initWizard.iac.prompt', 'Select IaC'), placeholder: 'Choose an IaC option for your project', - buttons: [vscode.QuickInputButtons.Back], + buttons: createCommonButtons(), } ) } @@ -102,16 +102,17 @@ function promptIac(metadataManager: MetadataManager, pattern: string | undefined function promptLocation() { return createFolderPrompt(vscode.workspace.workspaceFolders ?? [], { title: localize('AWS.serverlessLand.initWizard.location.prompt', 'Select Project Location'), - buttons: [vscode.QuickInputButtons.Back], - browseFolderDetail: 'Select a folder for your project', + buttons: createCommonButtons(), + browseFolderDetail: 'Select a parent folder for your project', }) } -function promptName() { +function promptName(location: vscode.Uri | undefined) { + const folderName = location ? path.basename(location.fsPath) : '' return createInputBox({ title: localize('AWS.serverlessLand.initWizard.name.prompt', 'Enter Project Name'), - placeholder: 'Enter a name for your new application', - buttons: [vscode.QuickInputButtons.Back], + placeholder: ` ${folderName}/: Enter a name for your new application`, + buttons: createCommonButtons(), validateInput: (value: string): string | undefined => { if (!value) { return 'Application name cannot be empty' @@ -140,6 +141,6 @@ export class CreateServerlessLandWizard extends Wizard promptRuntime(this.metadataManager, state.pattern)) this.form.iac.bindPrompter((state) => promptIac(this.metadataManager, state.pattern)) this.form.location.bindPrompter(() => promptLocation()) - this.form.name.bindPrompter(() => promptName()) + this.form.name.bindPrompter((state) => promptName(state.location)) } } diff --git a/packages/core/src/shared/ui/pickerPrompter.ts b/packages/core/src/shared/ui/pickerPrompter.ts index 33099a8170f..eaf4a4e6b4c 100644 --- a/packages/core/src/shared/ui/pickerPrompter.ts +++ b/packages/core/src/shared/ui/pickerPrompter.ts @@ -12,6 +12,7 @@ import { Prompter, PromptResult, Transform } from './prompter' import { assign, isAsyncIterable } from '../utilities/collectionUtils' import { recentlyUsed } from '../localizedText' import { getLogger } from '../logger/logger' +import { openUrl } from '../utilities/vsCodeUtils' import { MetadataManager } from '../../awsService/appBuilder/serverlessLand/metadataManager' const localize = nls.loadMessageBundle() @@ -157,7 +158,7 @@ export function createQuickPick( if (!patternUrl) { return } - await vscode.env.openExternal(vscode.Uri.parse(patternUrl)) + await openUrl(vscode.Uri.parse(patternUrl)) }) const prompter =