-
Notifications
You must be signed in to change notification settings - Fork 749
feat(lambda): Building Quick Pick structure and Entrypoints #6592
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 4 commits
9e2ebad
11b3098
891cea4
cf4928f
eccc7bd
8381db4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,88 @@ | ||
| /*! | ||
Vandita2020 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| * 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<void> { | ||
| 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, | ||
| }) | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 './serverlesslandMetadataManager' | ||
| import { ToolkitError } from '../../../shared/errors' | ||
|
|
||
| export interface CreateServerlessLandWizardForm { | ||
| name: string | ||
| location: vscode.Uri | ||
| pattern: string | ||
| runtime: string | ||
| iac: string | ||
| region?: string | ||
| schemaName?: string | ||
Vandita2020 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| /** | ||
| * Wizard for creating Serverless Land applications | ||
| * Guides users through the project creation process | ||
| */ | ||
| export class CreateServerlessLandWizard extends Wizard<CreateServerlessLandWizardForm> { | ||
| 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<CreateServerlessLandWizardForm | undefined> { | ||
| try { | ||
| // Load metadata from JSON file | ||
| const projectRoot = path.resolve(__dirname, '../../../../../') | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this the best way to get the root? I'd rather have an absolute path rather than this relative way
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The Previously, when executing the below line of code, an additional |
||
| 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<string>( | ||
| patterns.map((p) => ({ | ||
| label: p.label, | ||
| detail: p.description, | ||
| data: p.label, | ||
| buttons: [ | ||
| { | ||
| iconPath: new vscode.ThemeIcon('github'), | ||
| tooltip: 'GitHub Button', | ||
Vandita2020 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| }, | ||
| { | ||
| iconPath: new vscode.ThemeIcon('open-preview'), | ||
| tooltip: 'Serverless Land Button', | ||
Vandita2020 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| }, | ||
| ], | ||
| })), | ||
| { | ||
| 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<string>( | ||
| 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<string>( | ||
| 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)}`) | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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" | ||
| } | ||
| ] | ||
| } | ||
| } | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.