Skip to content

Commit 0e5defb

Browse files
author
Vandita Patidar
committed
Download functionality
1 parent bb4eeef commit 0e5defb

File tree

3 files changed

+147
-121
lines changed

3 files changed

+147
-121
lines changed

packages/core/src/awsService/appBuilder/models/metadata.json

Lines changed: 26 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -3,56 +3,52 @@
33
"s3-lambda-resizing-sam": {
44
"name": "Resizing image",
55
"description": "Lambda, S3 • Python, Javascript, Java, .NET • SAM",
6-
"runtimes": [
6+
"implementation": [
77
{
8-
"id": "python",
9-
"name": "Python"
8+
"iac": "sam",
9+
"runtime": "python",
10+
"assetName": "s3-lambda-resizing-python"
1011
},
1112
{
12-
"id": "javascript",
13-
"name": "Javascript"
13+
"iac": "sam",
14+
"runtime": "javascript",
15+
"assetName": "s3-lambda"
1416
},
1517
{
16-
"id": "dotnet",
17-
"name": "Dotnet"
18+
"iac": "sam",
19+
"runtime": "java",
20+
"assetName": "s3-lambda-resizing-java"
1821
},
1922
{
20-
"id": "java",
21-
"name": "Java"
22-
}
23-
],
24-
"iac": [
25-
{
26-
"id": "sam",
27-
"name": "SAM"
23+
"iac": "sam",
24+
"runtime": "dotnet",
25+
"assetName": "s3-lambda-dotnet"
2826
}
2927
]
3028
},
3129
"apigw-rest-api-lambda-sam": {
3230
"name": "Rest API",
3331
"description": "Lambda, API Gateway • Python, Javascript, Java, .NET • SAM",
34-
"runtimes": [
32+
"implementation": [
3533
{
36-
"id": "python",
37-
"name": "Python"
34+
"iac": "sam",
35+
"runtime": "python",
36+
"assetName": "apigw-rest-api-lambda-python"
3837
},
3938
{
40-
"id": "javascript",
41-
"name": "Javascript"
39+
"iac": "sam",
40+
"runtime": "javascript",
41+
"assetName": "apigw-rest-api-lambda-node"
4242
},
4343
{
44-
"id": "dotnet",
45-
"name": "Dotnet"
44+
"iac": "sam",
45+
"runtime": "java",
46+
"assetName": "apigw-rest-api-lambda-java"
4647
},
4748
{
48-
"id": "java",
49-
"name": "Java"
50-
}
51-
],
52-
"iac": [
53-
{
54-
"id": "sam",
55-
"name": "AWS SAM"
49+
"iac": "sam",
50+
"runtime": "dotnet",
51+
"assetName": "apigw-rest-api-lambda-dotnet"
5652
}
5753
]
5854
}

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

Lines changed: 92 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -6,45 +6,20 @@
66
import * as nls from 'vscode-nls'
77
const localize = nls.loadMessageBundle()
88
import * as path from 'path'
9-
// import * as vscode from 'vscode'
10-
import { getTelemetryResult, RegionProvider } from '../../shared'
9+
import * as vscode from 'vscode'
10+
import { getTelemetryResult, RegionProvider, ToolkitError } from '../../shared'
1111
import { getLogger } from '../../shared/logger'
12-
import globals from '../../shared/extensionGlobals'
13-
import { checklogs } from '../../shared/localizedText'
14-
// import { fileExists } from '../../shared/filesystemUtilities'
15-
// import { CreateServerlessLandWizardForm } from '../appBuilder/wizards/serverlessLandWizard'
12+
import { fileExists } from '../../shared/filesystemUtilities'
13+
import { CreateServerlessLandWizardForm } from '../appBuilder/wizards/serverlessLandWizard'
1614
import { Result, telemetry } from '../../shared/telemetry/telemetry'
1715
import { CreateServerlessLandWizard } from '../appBuilder/wizards/serverlessLandWizard'
1816
import { ExtContext } from '../../shared/extensions'
1917
import { addFolderToWorkspace } from '../../shared/utilities/workspaceUtils'
18+
import { getPattern } from '../../shared/utilities/downloadPatterns'
2019

2120
export const readmeFile: string = 'README.md'
22-
23-
// export async function getProjectUri(
24-
// config: Pick<CreateServerlessLandWizardForm, 'location' | 'name'>,
25-
// files: string
26-
// ): Promise<vscode.Uri | undefined> {
27-
// if (files.length === 0) {
28-
// throw Error('expected "files" parameter to have at least one item')
29-
// }
30-
// let file: string
31-
// let cfnTemplatePath: string
32-
// for (const f of files) {
33-
// file = f
34-
// cfnTemplatePath = path.resolve(config.location.fsPath, config.name, file)
35-
// if (await fileExists(cfnTemplatePath)) {
36-
// return vscode.Uri.file(cfnTemplatePath)
37-
// }
38-
// }
39-
// void vscode.window.showWarningMessage(
40-
// localize(
41-
// 'AWS.samcli.initWizard.source.error.notFound',
42-
// 'Project created successfully, but {0} file not found: {1}',
43-
// file!,
44-
// cfnTemplatePath!
45-
// )
46-
// )
47-
// }
21+
const serverlessLandOwner = 'aws-samples'
22+
const serverlessLandRepo = 'serverless-patterns'
4823

4924
/**
5025
* Creates a new Serverless Land project using the provided extension context
@@ -60,67 +35,30 @@ export const readmeFile: string = 'README.md'
6035
* 6. Handles errors and emits telemetry
6136
*/
6237
export async function createNewServerlessLandProject(extContext: ExtContext): Promise<void> {
63-
const awsContext = extContext.awsContext
64-
const regionProvider: RegionProvider = extContext.regionProvider
6538
let createResult: Result = 'Succeeded'
6639
let reason: string | undefined
6740

6841
try {
69-
const credentials = await awsContext.getCredentials()
70-
const schemaRegions = regionProvider
71-
.getRegions()
72-
.filter((r) => regionProvider.isServiceInRegion('schemas', r.id))
73-
const defaultRegion = awsContext.getCredentialDefaultRegion()
74-
7542
// Launch the project creation wizard
76-
const config = await new CreateServerlessLandWizard({
77-
credentials,
78-
schemaRegions,
79-
defaultRegion,
80-
}).run()
43+
const config = await launchProjectCreationWizard(extContext)
8144
if (!config) {
8245
createResult = 'Cancelled'
8346
reason = 'userCancelled'
8447
return
8548
}
86-
87-
// Add the project folder to the workspace
49+
await downloadPatternCode(config)
50+
await openReadmeFile(config)
8851
await addFolderToWorkspace(
8952
{
90-
uri: config.location,
91-
name: path.basename(config.location.fsPath),
53+
uri: vscode.Uri.joinPath(config.location, config.name),
54+
name: path.basename(config.name),
9255
},
9356
true
9457
)
95-
96-
// Verify project creation and locate project files
97-
// const projectUri = await getProjectUri(config, readmeFile)
98-
// if (!projectUri) {
99-
// reason = 'fileNotFound'
100-
101-
// return
102-
// }
103-
104-
// Open README.md file
105-
// const readmeUri = vscode.Uri.file(path.join(path.dirname(projectUri.fsPath), readmeFile))
106-
// if (await fileExists(readmeUri.fsPath)) {
107-
// const document = await vscode.workspace.openTextDocument(readmeUri)
108-
// await vscode.window.showTextDocument(document)
109-
// } else {
110-
// getLogger().warn(
111-
// localize('AWS.serverlessLand.readme.notFound', 'README.md file not found in the project directory')
112-
// )
113-
// }
11458
} catch (err) {
11559
createResult = getTelemetryResult(err)
11660
reason = getTelemetryResult(err)
117-
118-
globals.outputChannel.show(true)
119-
getLogger().error(
120-
localize('AWS.samcli.initWizard.general.error', 'Error creating new SAM Application. {0}', checklogs())
121-
)
122-
123-
getLogger().error('Error creating new SAM Application: %O', err as Error)
61+
throw new ToolkitError('Error creating new ServerlessLand Application')
12462
} finally {
12563
// add telemetry
12664
telemetry.sam_init.emit({
@@ -129,3 +67,82 @@ export async function createNewServerlessLandProject(extContext: ExtContext): Pr
12967
})
13068
}
13169
}
70+
71+
async function launchProjectCreationWizard(
72+
extContext: ExtContext
73+
): Promise<CreateServerlessLandWizardForm | undefined> {
74+
const awsContext = extContext.awsContext
75+
const regionProvider: RegionProvider = extContext.regionProvider
76+
const credentials = await awsContext.getCredentials()
77+
const schemaRegions = regionProvider.getRegions().filter((r) => regionProvider.isServiceInRegion('schemas', r.id))
78+
const defaultRegion = awsContext.getCredentialDefaultRegion()
79+
80+
return new CreateServerlessLandWizard({
81+
credentials,
82+
schemaRegions,
83+
defaultRegion,
84+
}).run()
85+
}
86+
87+
async function downloadPatternCode(config: CreateServerlessLandWizardForm): Promise<void> {
88+
const assetName = config.assetName + '.zip'
89+
const location = vscode.Uri.joinPath(config.location, config.name)
90+
try {
91+
await getPattern(serverlessLandOwner, serverlessLandRepo, assetName, location, true)
92+
} catch (error) {
93+
if (error instanceof ToolkitError) {
94+
throw error
95+
}
96+
throw new ToolkitError(`Failed to download pattern: ${error}`)
97+
}
98+
}
99+
100+
async function openReadmeFile(config: CreateServerlessLandWizardForm): Promise<void> {
101+
try {
102+
const projectUri = await getProjectUri(config, readmeFile)
103+
if (!projectUri) {
104+
getLogger().warn('Project URI not found when trying to open README')
105+
return
106+
}
107+
108+
const readmeUri = vscode.Uri.file(path.join(path.dirname(projectUri.fsPath), readmeFile))
109+
if (!(await fileExists(readmeUri.fsPath))) {
110+
getLogger().warn(
111+
localize('AWS.serverlessLand.readme.notFound', 'README.md file not found in the project directory')
112+
)
113+
return
114+
}
115+
116+
try {
117+
const document = await vscode.workspace.openTextDocument(readmeUri)
118+
await vscode.window.showTextDocument(document, { preview: true })
119+
} catch (err) {
120+
getLogger().error(`Failed to open README file: ${err}`)
121+
throw new ToolkitError('Failed to open README file')
122+
}
123+
} catch (err) {
124+
getLogger().error(`Error in openReadmeFile: ${err}`)
125+
throw new ToolkitError('Error processing README file')
126+
}
127+
}
128+
129+
async function getProjectUri(
130+
config: Pick<CreateServerlessLandWizardForm, 'location' | 'name'>,
131+
file: string
132+
): Promise<vscode.Uri | undefined> {
133+
if (!file) {
134+
throw Error('expected "file" parameter to have at least one item')
135+
}
136+
const cfnTemplatePath = path.resolve(config.location.fsPath, config.name, file)
137+
if (await fileExists(cfnTemplatePath)) {
138+
return vscode.Uri.file(cfnTemplatePath)
139+
}
140+
void vscode.window.showWarningMessage(
141+
localize(
142+
'AWS.serverlessLand.initWizard.source.error.notFound',
143+
'Project created successfully, but {0} file not found: {1}',
144+
file!,
145+
cfnTemplatePath!
146+
)
147+
)
148+
}

packages/core/src/awsService/appBuilder/wizards/serverlessLandWizard.ts

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -20,23 +20,19 @@ export interface CreateServerlessLandWizardForm {
2020
pattern: string
2121
runtime: string
2222
iac: string
23+
assetName: string
2324
region?: string
2425
schemaName?: string
2526
}
26-
interface IaC {
27-
id: string
28-
name: string
29-
}
30-
interface Runtime {
31-
id: string
32-
name: string
33-
version: string
27+
interface Implementation {
28+
iac: string
29+
runtime: string
30+
assetName: string
3431
}
3532
interface PatternData {
3633
name: string
3734
description: string
38-
runtimes: Runtime[]
39-
iac: IaC[]
35+
implementation: Implementation[]
4036
}
4137

4238
export interface ProjectMetadata {
@@ -114,11 +110,12 @@ export class MetadataManager {
114110
*/
115111
public getRuntimes(pattern: string): { label: string }[] {
116112
const patternData = this.metadata?.patterns?.[pattern]
117-
if (!patternData || !patternData.runtimes) {
113+
if (!patternData || !patternData.implementation) {
118114
return []
119115
}
120-
return patternData.runtimes.map((runtime) => ({
121-
label: runtime.name,
116+
const uniqueRuntimes = new Set(patternData.implementation.map((item) => item.runtime))
117+
return Array.from(uniqueRuntimes).map((runtime) => ({
118+
label: runtime,
122119
}))
123120
}
124121

@@ -129,13 +126,25 @@ export class MetadataManager {
129126
*/
130127
public getIacOptions(pattern: string): { label: string }[] {
131128
const patternData = this.metadata?.patterns?.[pattern]
132-
if (!patternData || !patternData.iac) {
129+
if (!patternData || !patternData.implementation) {
133130
return []
134131
}
135-
return patternData.iac.map((iac) => ({
136-
label: iac.name,
132+
const uniqueIaCs = new Set(patternData.implementation.map((item) => item.iac))
133+
return Array.from(uniqueIaCs).map((iac) => ({
134+
label: iac,
137135
}))
138136
}
137+
138+
public getAssetName(selectedPattern: string, selectedRuntime: string, selectedIaC: string): string {
139+
const patternData = this.metadata?.patterns?.[selectedPattern]
140+
if (!patternData || !patternData.implementation) {
141+
return ''
142+
}
143+
const matchingImplementation = patternData.implementation.find(
144+
(impl) => impl.runtime === selectedRuntime && impl.iac === selectedIaC
145+
)
146+
return matchingImplementation?.assetName ?? ''
147+
}
139148
}
140149

141150
/**
@@ -242,6 +251,9 @@ export class CreateServerlessLandWizard extends Wizard<CreateServerlessLandWizar
242251
}
243252
const selectedIac = iacResult
244253

254+
// Get Asset Name based on selected Pattern, Runtime and IaC
255+
const assetName = this.metadataManager.getAssetName(selectedPattern, selectedRuntime, selectedIac)
256+
245257
// Create and show location picker
246258
const locationPicker = createFolderPrompt(vscode.workspace.workspaceFolders ?? [], {
247259
title: 'Select Project Location',
@@ -282,6 +294,7 @@ export class CreateServerlessLandWizard extends Wizard<CreateServerlessLandWizar
282294
pattern: selectedPattern,
283295
runtime: selectedRuntime,
284296
iac: selectedIac,
297+
assetName: assetName,
285298
}
286299
} catch (err) {
287300
throw new Error(`Failed to run wizard: ${err instanceof Error ? err.message : String(err)}`)

0 commit comments

Comments
 (0)