Skip to content
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

feat(lambda): web-view functionality and fixes for serverless land #6687

Open
wants to merge 9 commits into
base: feature/serverlessland
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion packages/core/package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
8 changes: 4 additions & 4 deletions packages/core/src/awsService/appBuilder/activation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<void> {
export async function activate(context: ExtContext, ctx: vscode.ExtensionContext): Promise<void> {
// recover context variables from global state when activate
const walkthroughSelected = globals.globalState.get<string>(walkthroughContextString)
if (walkthroughSelected !== undefined) {
await setContext(walkthroughContextString, walkthroughSelected)
}

await registerAppBuilderCommands(context)
await registerAppBuilderCommands(context, ctx)

const appBuilderNode: ToolView[] = [
{
Expand Down Expand Up @@ -123,7 +123,7 @@ async function setWalkthrough(walkthroughSelected: string = 'S3'): Promise<void>
*
* @param context VScode Context
*/
async function registerAppBuilderCommands(context: ExtContext): Promise<void> {
async function registerAppBuilderCommands(context: ExtContext, ctx: vscode.ExtensionContext): Promise<void> {
const source = 'AppBuilderWalkthrough'
context.extensionContext.subscriptions.push(
Commands.register('aws.toolkit.installSAMCLI', async () => {
Expand Down Expand Up @@ -202,7 +202,7 @@ async function registerAppBuilderCommands(context: ExtContext): Promise<void> {
}
}),
Commands.register({ id: 'aws.toolkit.lambda.createServerlessLandProject', autoconnect: false }, async () => {
await createNewServerlessLandProject(context)
await createNewServerlessLandProject(context, ctx)
})
)
}
12 changes: 8 additions & 4 deletions packages/core/src/awsService/appBuilder/serverlessLand/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -36,15 +37,15 @@ 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<void> {
export async function createNewServerlessLandProject(extContext: ExtContext, ctx: ExtensionContext): Promise<void> {
let createResult: Result = 'Succeeded'
let reason: string | undefined
let metadataManager: MetadataManager

try {
metadataManager = MetadataManager.getInstance()
// Launch the project creation wizard
const config = await launchProjectCreationWizard(extContext)
const config = await launchProjectCreationWizard(extContext, ctx)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can just use globals.context instead of plumbing it through everywhere

if (!config) {
createResult = 'Cancelled'
reason = 'userCancelled'
Expand Down Expand Up @@ -83,13 +84,15 @@ export async function createNewServerlessLandProject(extContext: ExtContext): Pr
}

async function launchProjectCreationWizard(
extContext: ExtContext
extContext: ExtContext,
ctx: ExtensionContext
): Promise<CreateServerlessLandWizardForm | undefined> {
const awsContext = extContext.awsContext
const credentials = await awsContext.getCredentials()
const defaultRegion = awsContext.getCredentialDefaultRegion()

return new CreateServerlessLandWizard({
ctx,
credentials,
defaultRegion,
}).run()
Expand All @@ -115,9 +118,10 @@ async function openReadmeFile(config: CreateServerlessLandWizardForm): Promise<v
getLogger().warn('README.md file not found in the project directory')
return
}
await new Promise((resolve) => 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')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 { ExtensionContext } from 'vscode'

interface Implementation {
iac: string
Expand All @@ -17,6 +18,10 @@ interface PatternData {
implementation: Implementation[]
}

interface PatternUrls {
githubUrl: string
previewUrl: string
}
export interface ProjectMetadata {
patterns: Record<string, PatternData>
}
Expand All @@ -28,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() {}

Expand All @@ -46,14 +43,19 @@ export class MetadataManager {
return MetadataManager.instance
}

public static initialize(): MetadataManager {
public static initialize(ctx: ExtensionContext): MetadataManager {
const instance = MetadataManager.getInstance()
instance.loadMetadata(MetadataManager.metadataPath).catch((err) => {
const metadataPath = instance.getMetadataPath(ctx)
instance.loadMetadata(metadataPath).catch((err) => {
throw new ToolkitError(`Failed to load metadata: ${err}`)
})
return instance
}

public getMetadataPath(ctx: ExtensionContext): string {
return ctx.asAbsolutePath(path.join('dist', 'src', 'serverlessLand', 'metadata.json'))
}

/**
* Loads metadata from a JSON file
* @param metadataPath Path to the metadata JSON file
Expand Down Expand Up @@ -117,6 +119,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
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*!
* 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 `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; frame-src ${url}; style-src 'unsafe-inline';">
<style>
body, html {
margin: 0;
padding: 0;
width: 100%;
height: 100vh;
overflow: hidden;
}
iframe {
width: 100%;
height: 100%;
border: none;
}
</style>
</head>
<body>
<iframe src="${url}" frameborder="0" allowfullscreen></iframe>
</body>
</html>
`
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -22,6 +23,7 @@ export interface CreateServerlessLandWizardForm {
pattern: string
runtime: string
iac: string
assetName: string
}

function promptPattern(metadataManager: MetadataManager) {
Expand Down Expand Up @@ -134,11 +136,11 @@ function promptName() {
export class CreateServerlessLandWizard extends Wizard<CreateServerlessLandWizardForm> {
private metadataManager: MetadataManager

public constructor(context: { defaultRegion?: string; credentials?: AWS.Credentials }) {
public constructor(context: { ctx: ExtensionContext; defaultRegion?: string; credentials?: AWS.Credentials }) {
super({
exitPrompterProvider: createExitPrompter,
})
this.metadataManager = MetadataManager.initialize()
this.metadataManager = MetadataManager.initialize(context.ctx)
this.form.pattern.bindPrompter(() => promptPattern(this.metadataManager))
this.form.runtime.bindPrompter((state) => promptRuntime(this.metadataManager, state.pattern))
this.form.iac.bindPrompter((state) => promptIac(this.metadataManager, state.pattern))
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/extensionNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ export async function activate(context: vscode.ExtensionContext) {

await activateRedshift(extContext)

await activateAppBuilder(extContext)
await activateAppBuilder(extContext, context)

await activateDocumentDb(extContext)

Expand Down
39 changes: 39 additions & 0 deletions packages/core/src/shared/ui/pickerPrompter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand Down Expand Up @@ -142,6 +144,43 @@ export function createQuickPick<T>(
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()
}
}
}
}
})

const prompter =
mergedOptions.filterBoxInputSettings !== undefined
Expand Down
13 changes: 9 additions & 4 deletions packages/toolkit/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
13 changes: 13 additions & 0 deletions packages/toolkit/scripts/build/copyFiles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'),
Expand Down
Loading