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

refactor(lambda): decompose methods for SAM sync/deploy/build #6014

Merged
merged 21 commits into from
Nov 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
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
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
*/

import { CloudFormationTemplateRegistry } from '../../../shared/fs/templateRegistry'
import { createTemplatePrompter, TemplateItem } from '../../../shared/sam/sync'
import { syncMementoRootKey } from '../../../shared/sam/sync'

import { createExitPrompter } from '../../../shared/ui/common/exitPrompter'
import { createTemplatePrompter, TemplateItem } from '../../../shared/ui/sam/templatePrompter'
import { Wizard } from '../../../shared/wizards/wizard'

export interface OpenTemplateParams {
Expand All @@ -15,6 +17,6 @@ export interface OpenTemplateParams {
export class OpenTemplateWizard extends Wizard<OpenTemplateParams> {
public constructor(state: Partial<OpenTemplateParams>, registry: CloudFormationTemplateRegistry) {
super({ initState: state, exitPrompterProvider: createExitPrompter })
this.form.template.bindPrompter(() => createTemplatePrompter(registry))
this.form.template.bindPrompter(() => createTemplatePrompter(registry, syncMementoRootKey))
}
}
11 changes: 7 additions & 4 deletions packages/core/src/shared/sam/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
*/

import * as vscode from 'vscode'
import { TemplateItem, createTemplatePrompter } from './sync'
import { TemplateItem, createTemplatePrompter } from '../ui/sam/templatePrompter'
import { ChildProcess } from '../utilities/processUtils'
import { addTelemetryEnvVar } from './cli/samCliInvokerUtils'
import { Wizard } from '../wizards/wizard'
Expand All @@ -19,10 +19,11 @@ import globals from '../extensionGlobals'
import { TreeNode } from '../treeview/resourceTreeDataProvider'
import { telemetry } from '../telemetry/telemetry'
import { getSpawnEnv } from '../env/resolveEnv'
import { getErrorCode, getProjectRoot, getSamCliPathAndVersion, isDotnetRuntime } from './utils'
import { getErrorCode, getProjectRoot, getSamCliPathAndVersion, isDotnetRuntime, updateRecentResponse } from './utils'
import { getConfigFileUri, validateSamBuildConfig } from './config'
import { runInTerminal } from './processTerminal'

const buildMementoRootKey = 'samcli.build.params'
export interface BuildParams {
readonly template: TemplateItem
readonly projectRoot: vscode.Uri
Expand Down Expand Up @@ -58,7 +59,7 @@ export function createParamsSourcePrompter(existValidSamconfig: boolean) {
)

return createQuickPick(items, {
title: 'Specify parameters for build',
title: 'Specify parameter source for build',
placeholder: 'Select configuration options for sam build',
buttons: createCommonButtons(samBuildUrl),
})
Expand Down Expand Up @@ -128,7 +129,7 @@ export class BuildWizard extends Wizard<BuildParams> {
this.arg = arg
if (this.arg === undefined) {
// "Build" command was invoked on the command palette.
this.form.template.bindPrompter(() => createTemplatePrompter(this.registry))
this.form.template.bindPrompter(() => createTemplatePrompter(this.registry, buildMementoRootKey))
this.form.projectRoot.setDefault(({ template }) => getProjectRoot(template))
this.form.paramsSource.bindPrompter(async ({ projectRoot }) => {
const existValidSamConfig: boolean | undefined = await validateSamBuildConfig(projectRoot)
Expand Down Expand Up @@ -216,6 +217,8 @@ export async function runBuild(arg?: TreeNode): Promise<SamBuildResult> {
const templatePath = params.template.uri.fsPath
buildFlags.push('--template', `${templatePath}`)

await updateRecentResponse(buildMementoRootKey, 'global', 'templatePath', templatePath)

try {
const { path: samCliPath } = await getSamCliPathAndVersion()

Expand Down
162 changes: 62 additions & 100 deletions packages/core/src/shared/sam/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
*/

import * as vscode from 'vscode'
import { ToolkitError, getLogger, globals } from '../../shared'
import { ToolkitError, globals } from '../../shared'
import * as CloudFormation from '../../shared/cloudformation/cloudformation'
import { getParameters } from '../../lambda/config/parameterUtils'
import { DefaultCloudFormationClient } from '../clients/cloudFormationClient'
Expand All @@ -17,14 +17,23 @@ import { createCommonButtons } from '../ui/buttons'
import { createExitPrompter } from '../ui/common/exitPrompter'
import { createRegionPrompter } from '../ui/common/region'
import { createInputBox } from '../ui/inputPrompter'
import { DataQuickPickItem, createQuickPick } from '../ui/pickerPrompter'
import { ChildProcess } from '../utilities/processUtils'
import { CancellationError } from '../utilities/timeoutUtils'
import { Wizard } from '../wizards/wizard'
import { addTelemetryEnvVar } from './cli/samCliInvokerUtils'
import { validateSamDeployConfig, SamConfig, writeSamconfigGlobal } from './config'
import { TemplateItem, createStackPrompter, createBucketPrompter, createTemplatePrompter } from './sync'
import { getErrorCode, getProjectRoot, getSamCliPathAndVersion, getSource } from './utils'
import { BucketSource, createBucketSourcePrompter, createBucketNamePrompter } from '../ui/sam/bucketPrompter'
import { createStackPrompter } from '../ui/sam/stackPrompter'
import { TemplateItem, createTemplatePrompter } from '../ui/sam/templatePrompter'
import { createDeployParamsSourcePrompter, ParamsSource } from '../ui/sam/paramsSourcePrompter'
import {
getErrorCode,
getProjectRoot,
getSamCliPathAndVersion,
getSource,
getRecentResponse,
updateRecentResponse,
} from './utils'
import { runInTerminal } from './processTerminal'

export interface DeployParams {
Expand All @@ -39,89 +48,24 @@ export interface DeployParams {
[key: string]: any
}

const mementoRootKey = 'samcli.deploy.params'
export function getRecentParams(identifier: string, key: string): string | undefined {
const root = globals.context.workspaceState.get(mementoRootKey, {} as Record<string, Record<string, string>>)
const deployMementoRootKey = 'samcli.deploy.params'

return root[identifier]?.[key]
}

export async function updateRecentParams(identifier: string, key: string, value: string | undefined) {
try {
const root = globals.context.workspaceState.get(mementoRootKey, {} as Record<string, Record<string, string>>)
await globals.context.workspaceState.update(mementoRootKey, {
...root,
[identifier]: { ...root[identifier], [key]: value },
})
} catch (err) {
getLogger().warn(`sam: unable to save response at key "${key}": %s`, err)
}
function getRecentDeployParams(identifier: string, key: string): string | undefined {
return getRecentResponse(deployMementoRootKey, identifier, key)
}

function createParamPromptProvider(name: string, defaultValue: string | undefined, templateFsPath: string = 'default') {
return createInputBox({
title: `Specify SAM parameter value for ${name}`,
buttons: createCommonButtons(samDeployUrl),
value: getRecentParams(templateFsPath, name) ?? defaultValue,
value: getRecentDeployParams(templateFsPath, name) ?? defaultValue,
})
}
function bucketSourcePrompter() {
const items: DataQuickPickItem<BucketSource>[] = [
{
label: 'Create a SAM CLI managed S3 bucket',
data: BucketSource.SamCliManaged,
},
{
label: 'Specify an S3 bucket',
data: BucketSource.UserProvided,
},
]

return createQuickPick(items, {
title: 'Specify S3 bucket for deployment artifacts',
placeholder: 'Press enter to proceed with highlighted option',
buttons: createCommonButtons(samDeployUrl),
})
}
function paramsSourcePrompter(existValidSamconfig: boolean | undefined) {
const items: DataQuickPickItem<ParamsSource>[] = [
{
label: 'Specify required parameters and save as defaults',
data: ParamsSource.SpecifyAndSave,
},
{
label: 'Specify required parameters',
data: ParamsSource.Specify,
},
]

if (existValidSamconfig) {
items.push({
label: 'Use default values from samconfig',
data: ParamsSource.SamConfig,
})
}

return createQuickPick(items, {
title: 'Specify parameters for deploy',
placeholder: 'Press enter to proceed with highlighted option',
buttons: createCommonButtons(samDeployUrl),
})
}
type DeployResult = {
isSuccess: boolean
}

export enum BucketSource {
SamCliManaged,
UserProvided,
}
export enum ParamsSource {
SpecifyAndSave,
Specify,
SamConfig,
}

export class DeployWizard extends Wizard<DeployParams> {
registry: CloudFormationTemplateRegistry
state: Partial<DeployParams>
Expand Down Expand Up @@ -153,50 +97,58 @@ export class DeployWizard extends Wizard<DeployParams> {
this.form.template.setDefault(templateItem)
this.form.projectRoot.setDefault(() => projectRootFolder)
this.form.paramsSource.bindPrompter(async () =>
paramsSourcePrompter(await validateSamDeployConfig(projectRootFolder))
createDeployParamsSourcePrompter(await validateSamDeployConfig(projectRootFolder))
)

this.form.region.bindPrompter(() => createRegionPrompter().transform((r) => r.id), {
showWhen: ({ paramsSource }) =>
paramsSource === ParamsSource.Specify || paramsSource === ParamsSource.SpecifyAndSave,
})
this.form.stackName.bindPrompter(
({ region }) => createStackPrompter(new DefaultCloudFormationClient(region!)),
({ region }) =>
createStackPrompter(new DefaultCloudFormationClient(region!), deployMementoRootKey, samDeployUrl),
{
showWhen: ({ paramsSource }) =>
paramsSource === ParamsSource.Specify || paramsSource === ParamsSource.SpecifyAndSave,
}
)
this.form.bucketSource.bindPrompter(() => bucketSourcePrompter(), {
this.form.bucketSource.bindPrompter(() => createBucketSourcePrompter(), {
showWhen: ({ paramsSource }) =>
paramsSource === ParamsSource.Specify || paramsSource === ParamsSource.SpecifyAndSave,
})
this.form.bucketName.bindPrompter(({ region }) => createBucketPrompter(new DefaultS3Client(region!)), {
showWhen: ({ bucketSource }) => bucketSource === BucketSource.UserProvided,
})
this.form.bucketName.bindPrompter(
({ region }) => createBucketNamePrompter(new DefaultS3Client(region!), deployMementoRootKey),
{
showWhen: ({ bucketSource }) => bucketSource === BucketSource.UserProvided,
}
)
} else if (this.arg && this.arg.regionCode) {
// "Deploy" command was invoked on a regionNode.
this.form.template.bindPrompter(() => createTemplatePrompter(this.registry))
this.form.template.bindPrompter(() => createTemplatePrompter(this.registry, deployMementoRootKey))
this.form.projectRoot.setDefault(({ template }) => getProjectRoot(template))
this.form.paramsSource.bindPrompter(async ({ projectRoot }) => {
const existValidSamConfig: boolean | undefined = await validateSamDeployConfig(projectRoot)
return paramsSourcePrompter(existValidSamConfig)
return createDeployParamsSourcePrompter(existValidSamConfig)
})
this.form.region.setDefault(() => this.arg.regionCode)
this.form.stackName.bindPrompter(
({ region }) => createStackPrompter(new DefaultCloudFormationClient(region!)),
({ region }) =>
createStackPrompter(new DefaultCloudFormationClient(region!), deployMementoRootKey, samDeployUrl),
{
showWhen: ({ paramsSource }) =>
paramsSource === ParamsSource.Specify || paramsSource === ParamsSource.SpecifyAndSave,
}
)
this.form.bucketSource.bindPrompter(() => bucketSourcePrompter(), {
this.form.bucketSource.bindPrompter(() => createBucketSourcePrompter(), {
showWhen: ({ paramsSource }) =>
paramsSource === ParamsSource.Specify || paramsSource === ParamsSource.SpecifyAndSave,
})
this.form.bucketName.bindPrompter(({ region }) => createBucketPrompter(new DefaultS3Client(region!)), {
showWhen: ({ bucketSource }) => bucketSource === BucketSource.UserProvided,
})
this.form.bucketName.bindPrompter(
({ region }) => createBucketNamePrompter(new DefaultS3Client(region!), deployMementoRootKey),
{
showWhen: ({ bucketSource }) => bucketSource === BucketSource.UserProvided,
}
)
} else if (this.arg && this.arg.getTreeItem().resourceUri) {
// "Deploy" command was invoked on a TreeNode on the AppBuilder.
const templateUri = this.arg.getTreeItem().resourceUri as vscode.Uri
Expand All @@ -206,54 +158,62 @@ export class DeployWizard extends Wizard<DeployParams> {
this.addParameterPromptersIfApplicable(templateUri)
this.form.template.setDefault(templateItem)
this.form.paramsSource.bindPrompter(async () =>
paramsSourcePrompter(await validateSamDeployConfig(projectRootFolder))
createDeployParamsSourcePrompter(await validateSamDeployConfig(projectRootFolder))
)

this.form.region.bindPrompter(() => createRegionPrompter().transform((r) => r.id), {
showWhen: ({ paramsSource }) =>
paramsSource === ParamsSource.Specify || paramsSource === ParamsSource.SpecifyAndSave,
})
this.form.stackName.bindPrompter(
({ region }) => createStackPrompter(new DefaultCloudFormationClient(region!)),
({ region }) =>
createStackPrompter(new DefaultCloudFormationClient(region!), deployMementoRootKey, samDeployUrl),
{
showWhen: ({ paramsSource }) =>
paramsSource === ParamsSource.Specify || paramsSource === ParamsSource.SpecifyAndSave,
}
)
this.form.bucketSource.bindPrompter(() => bucketSourcePrompter(), {
this.form.bucketSource.bindPrompter(() => createBucketSourcePrompter(), {
showWhen: ({ paramsSource }) =>
paramsSource === ParamsSource.Specify || paramsSource === ParamsSource.SpecifyAndSave,
})
this.form.bucketName.bindPrompter(({ region }) => createBucketPrompter(new DefaultS3Client(region!)), {
showWhen: ({ bucketSource }) => bucketSource === BucketSource.UserProvided,
})
this.form.bucketName.bindPrompter(
({ region }) => createBucketNamePrompter(new DefaultS3Client(region!), deployMementoRootKey),
{
showWhen: ({ bucketSource }) => bucketSource === BucketSource.UserProvided,
}
)
this.form.projectRoot.setDefault(() => getProjectRoot(templateItem))
} else {
// "Deploy" command was invoked on the command palette.
this.form.template.bindPrompter(() => createTemplatePrompter(this.registry))
this.form.template.bindPrompter(() => createTemplatePrompter(this.registry, deployMementoRootKey))
this.form.projectRoot.setDefault(({ template }) => getProjectRoot(template))
this.form.paramsSource.bindPrompter(async ({ projectRoot }) => {
const existValidSamConfig: boolean | undefined = await validateSamDeployConfig(projectRoot)
return paramsSourcePrompter(existValidSamConfig)
return createDeployParamsSourcePrompter(existValidSamConfig)
})
this.form.region.bindPrompter(() => createRegionPrompter().transform((r) => r.id), {
showWhen: ({ paramsSource }) =>
paramsSource === ParamsSource.Specify || paramsSource === ParamsSource.SpecifyAndSave,
})
this.form.stackName.bindPrompter(
({ region }) => createStackPrompter(new DefaultCloudFormationClient(region!)),
({ region }) =>
createStackPrompter(new DefaultCloudFormationClient(region!), deployMementoRootKey, samDeployUrl),
{
showWhen: ({ paramsSource }) =>
paramsSource === ParamsSource.Specify || paramsSource === ParamsSource.SpecifyAndSave,
}
)
this.form.bucketSource.bindPrompter(() => bucketSourcePrompter(), {
this.form.bucketSource.bindPrompter(() => createBucketSourcePrompter(), {
showWhen: ({ paramsSource }) =>
paramsSource === ParamsSource.Specify || paramsSource === ParamsSource.SpecifyAndSave,
})
this.form.bucketName.bindPrompter(({ region }) => createBucketPrompter(new DefaultS3Client(region!)), {
showWhen: ({ bucketSource }) => bucketSource === BucketSource.UserProvided,
})
this.form.bucketName.bindPrompter(
({ region }) => createBucketNamePrompter(new DefaultS3Client(region!), deployMementoRootKey),
{
showWhen: ({ bucketSource }) => bucketSource === BucketSource.UserProvided,
}
)
}

return this
Expand Down Expand Up @@ -346,13 +306,15 @@ export async function runDeploy(arg: any, wizardParams?: DeployParams): Promise<
const paramsToSet: string[] = []
for (const name of parameterNames) {
if (params[name]) {
await updateRecentParams(params.template.uri.fsPath, name, params[name])
await updateRecentResponse(deployMementoRootKey, params.template.uri.fsPath, name, params[name])
paramsToSet.push(`ParameterKey=${name},ParameterValue=${params[name]}`)
}
}
paramsToSet.length > 0 && deployFlags.push('--parameter-overrides', paramsToSet.join(' '))
}

await updateRecentResponse(deployMementoRootKey, 'global', 'templatePath', params.template.uri.fsPath)

try {
const { path: samCliPath } = await getSamCliPathAndVersion()

Expand Down
Loading
Loading