Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion packages/core/package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -414,5 +414,6 @@
"AWS.toolkit.lambda.walkthrough.step1.title": "Iterate locally",
"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.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"
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,6 @@
</checkbox>
</div>
</checklist>
<checkbox class="theme-picker-link" when-checked="command:aws.lambda.createNewSamApp" checked-on="false">
<checkbox class="theme-picker-link" when-checked="command:aws.toolkit.lambda.serverlessLand" checked-on="false">
See more application example...
</checkbox>
5 changes: 4 additions & 1 deletion packages/core/src/awsService/appBuilder/activation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import { ResourceNode } from './explorer/nodes/resourceNode'
import { getSyncWizard, runSync } from '../../shared/sam/sync'
import { getDeployWizard, runDeploy } from '../../shared/sam/deploy'
import { DeployTypeWizard } from './wizards/deployTypeWizard'

import { createNewServerlessLandProject } from './serverlessLand/main'
export const templateToOpenAppComposer = 'aws.toolkit.appComposer.templateToOpenOnStart'

/**
Expand Down Expand Up @@ -200,6 +200,9 @@ async function registerAppBuilderCommands(context: ExtContext): Promise<void> {
await runSync('infra', arg, undefined, choices.syncParam)
}
}
}),
Commands.register({ id: 'aws.toolkit.lambda.serverlessLand', autoconnect: false }, async () => {
await createNewServerlessLandProject(context)
})
)
}
82 changes: 82 additions & 0 deletions packages/core/src/awsService/appBuilder/serverlessLand/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*!
* 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 { 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 './wizard'
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
let createResult: Result = 'Succeeded'
let reason: string | undefined

try {
const credentials = await awsContext.getCredentials()
const defaultRegion = awsContext.getCredentialDefaultRegion()

// Launch the project creation wizard
const config = await new CreateServerlessLandWizard({
credentials,
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 Serverless Land 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,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"
}
]
}
}
}
Original file line number Diff line number Diff line change
@@ -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<string, PatternData>
}

/**
* 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<ProjectMetadata> {
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,
}))
}
}
Loading
Loading