Skip to content

Commit ecb5463

Browse files
Merge master into feature/emr
2 parents 5dd7e42 + 525181b commit ecb5463

File tree

9 files changed

+987
-395
lines changed

9 files changed

+987
-395
lines changed
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*!
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import * as vscode from 'vscode'
7+
import { Wizard } from '../../../shared/wizards/wizard'
8+
import { createExitPrompter } from '../../../shared/ui/common/exitPrompter'
9+
import * as CloudFormation from '../../../shared/cloudformation/cloudformation'
10+
import { createInputBox } from '../../../shared/ui/inputPrompter'
11+
import { createCommonButtons } from '../../../shared/ui/buttons'
12+
import { getRecentResponse, updateRecentResponse } from '../../../shared/sam/utils'
13+
import { getParameters } from '../../../lambda/config/parameterUtils'
14+
15+
export interface TemplateParametersForm {
16+
[key: string]: any
17+
}
18+
19+
export class TemplateParametersWizard extends Wizard<TemplateParametersForm> {
20+
template: vscode.Uri
21+
preloadedTemplate: CloudFormation.Template | undefined
22+
samTemplateParameters: Map<string, { required: boolean }> | undefined
23+
samCommandUrl: vscode.Uri
24+
commandMementoRootKey: string
25+
26+
public constructor(template: vscode.Uri, samCommandUrl: vscode.Uri, commandMementoRootKey: string) {
27+
super({ exitPrompterProvider: createExitPrompter })
28+
this.template = template
29+
this.samCommandUrl = samCommandUrl
30+
this.commandMementoRootKey = commandMementoRootKey
31+
}
32+
33+
public override async init(): Promise<this> {
34+
this.samTemplateParameters = await getParameters(this.template)
35+
this.preloadedTemplate = await CloudFormation.load(this.template.fsPath)
36+
const samTemplateNames = new Set<string>(this.samTemplateParameters?.keys() ?? [])
37+
38+
samTemplateNames.forEach((name) => {
39+
if (this.preloadedTemplate) {
40+
const defaultValue = this.preloadedTemplate.Parameters
41+
? (this.preloadedTemplate.Parameters[name]?.Default as string)
42+
: undefined
43+
this.form[name].bindPrompter(() =>
44+
this.createParamPromptProvider(name, defaultValue).transform(async (item) => {
45+
await updateRecentResponse(this.commandMementoRootKey, this.template.fsPath, name, item)
46+
return item
47+
})
48+
)
49+
}
50+
})
51+
52+
return this
53+
}
54+
55+
createParamPromptProvider(name: string, defaultValue: string | undefined) {
56+
return createInputBox({
57+
title: `Specify SAM Template parameter value for ${name}`,
58+
buttons: createCommonButtons(this.samCommandUrl),
59+
value: getRecentResponse(this.commandMementoRootKey, this.template.fsPath, name) ?? defaultValue,
60+
})
61+
}
62+
}

packages/core/src/shared/sam/deploy.ts

Lines changed: 95 additions & 209 deletions
Large diffs are not rendered by default.

packages/core/src/shared/sam/sync.ts

Lines changed: 110 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import * as vscode from 'vscode'
99
import * as path from 'path'
1010
import * as localizedText from '../localizedText'
1111
import { DefaultS3Client } from '../clients/s3Client'
12-
import { Wizard } from '../wizards/wizard'
1312
import { DataQuickPickItem, createMultiPick, createQuickPick } from '../ui/pickerPrompter'
1413
import { DefaultCloudFormationClient } from '../clients/cloudFormationClient'
1514
import * as CloudFormation from '../cloudformation/cloudformation'
@@ -28,14 +27,14 @@ import { createExitPrompter } from '../ui/common/exitPrompter'
2827
import { getConfigFileUri, SamConfig, validateSamSyncConfig, writeSamconfigGlobal } from './config'
2928
import { cast, Optional } from '../utilities/typeConstructors'
3029
import { pushIf, toRecord } from '../utilities/collectionUtils'
31-
import { getOverriddenParameters } from '../../lambda/config/parameterUtils'
30+
import { getParameters } from '../../lambda/config/parameterUtils'
3231
import { addTelemetryEnvVar } from './cli/samCliInvokerUtils'
3332
import { samSyncParamUrl, samSyncUrl, samUpgradeUrl } from '../constants'
3433
import { openUrl } from '../utilities/vsCodeUtils'
3534
import { showOnce } from '../utilities/messages'
3635
import { IamConnection } from '../../auth/connection'
3736
import { CloudFormationTemplateRegistry } from '../fs/templateRegistry'
38-
import { TreeNode } from '../treeview/resourceTreeDataProvider'
37+
import { isTreeNode, TreeNode } from '../treeview/resourceTreeDataProvider'
3938
import { getSpawnEnv } from '../env/resolveEnv'
4039
import {
4140
getProjectRoot,
@@ -52,13 +51,19 @@ import { ParamsSource, createSyncParamsSourcePrompter } from '../ui/sam/paramsSo
5251
import { createEcrPrompter } from '../ui/sam/ecrPrompter'
5352
import { BucketSource, createBucketNamePrompter, createBucketSourcePrompter } from '../ui/sam/bucketPrompter'
5453
import { runInTerminal } from './processTerminal'
54+
import {
55+
TemplateParametersForm,
56+
TemplateParametersWizard,
57+
} from '../../awsService/appBuilder/wizards/templateParametersWizard'
58+
import { CompositeWizard } from '../wizards/compositeWizard'
5559

5660
export interface SyncParams {
5761
readonly paramsSource: ParamsSource
5862
readonly region: string
5963
readonly deployType: 'infra' | 'code'
6064
readonly projectRoot: vscode.Uri
6165
readonly template: TemplateItem
66+
readonly templateParameters: any
6267
readonly stackName: string
6368
readonly bucketSource: BucketSource
6469
readonly bucketName: string
@@ -147,7 +152,30 @@ export const syncFlagItems: DataQuickPickItem<string>[] = [
147152
},
148153
]
149154

150-
export class SyncWizard extends Wizard<SyncParams> {
155+
export enum SamSyncEntryPoints {
156+
SamTemplateFile,
157+
SamConfigFile,
158+
RegionNodeContextMenu,
159+
AppBuilderNodeButton,
160+
CommandPalette,
161+
}
162+
163+
function getSyncEntryPoint(arg: vscode.Uri | AWSTreeNodeBase | TreeNode | undefined) {
164+
if (arg instanceof vscode.Uri) {
165+
if (arg.path.endsWith('samconfig.toml')) {
166+
return SamSyncEntryPoints.SamConfigFile
167+
}
168+
return SamSyncEntryPoints.SamTemplateFile
169+
} else if (arg instanceof AWSTreeNodeBase) {
170+
return SamSyncEntryPoints.RegionNodeContextMenu
171+
} else if (isTreeNode(arg)) {
172+
return SamSyncEntryPoints.AppBuilderNodeButton
173+
} else {
174+
return SamSyncEntryPoints.CommandPalette
175+
}
176+
}
177+
178+
export class SyncWizard extends CompositeWizard<SyncParams> {
151179
registry: CloudFormationTemplateRegistry
152180
public constructor(
153181
state: Pick<SyncParams, 'deployType'> & Partial<SyncParams>,
@@ -156,17 +184,38 @@ export class SyncWizard extends Wizard<SyncParams> {
156184
) {
157185
super({ initState: state, exitPrompterProvider: shouldPromptExit ? createExitPrompter : undefined })
158186
this.registry = registry
187+
}
188+
189+
public override async init(): Promise<this> {
159190
this.form.template.bindPrompter(() => createTemplatePrompter(this.registry, syncMementoRootKey, samSyncUrl))
191+
this.form.templateParameters.bindPrompter(
192+
async ({ template }) =>
193+
this.createWizardPrompter<TemplateParametersWizard, TemplateParametersForm>(
194+
TemplateParametersWizard,
195+
template!.uri,
196+
samSyncUrl,
197+
syncMementoRootKey
198+
),
199+
{
200+
showWhen: async ({ template }) => {
201+
const samTemplateParameters = await getParameters(template!.uri)
202+
return !!samTemplateParameters && samTemplateParameters.size > 0
203+
},
204+
}
205+
)
206+
160207
this.form.projectRoot.setDefault(({ template }) => getProjectRoot(template))
161208

162209
this.form.paramsSource.bindPrompter(async ({ projectRoot }) => {
163210
const existValidSamConfig: boolean | undefined = await validateSamSyncConfig(projectRoot)
164211
return createSyncParamsSourcePrompter(existValidSamConfig)
165212
})
213+
166214
this.form.region.bindPrompter(() => createRegionPrompter().transform((r) => r.id), {
167215
showWhen: ({ paramsSource }) =>
168216
paramsSource === ParamsSource.Specify || paramsSource === ParamsSource.SpecifyAndSave,
169217
})
218+
170219
this.form.stackName.bindPrompter(
171220
({ region }) =>
172221
createStackPrompter(new DefaultCloudFormationClient(region!), syncMementoRootKey, samSyncUrl),
@@ -210,6 +259,7 @@ export class SyncWizard extends Wizard<SyncParams> {
210259
paramsSource === ParamsSource.Specify || paramsSource === ParamsSource.SpecifyAndSave,
211260
}
212261
)
262+
return this
213263
}
214264
}
215265

@@ -296,30 +346,22 @@ export async function saveAndBindArgs(args: SyncParams): Promise<{ readonly boun
296346
return { boundArgs }
297347
}
298348

299-
async function loadLegacyParameterOverrides(template: TemplateItem) {
300-
try {
301-
const params = await getOverriddenParameters(template.uri)
302-
if (!params) {
303-
return
304-
}
305-
306-
return [...params.entries()].map(([k, v]) => `${k}=${v}`)
307-
} catch (err) {
308-
getLogger().warn(`sam: unable to load legacy parameter overrides: %s`, err)
309-
}
310-
}
311-
312349
export async function runSamSync(args: SyncParams) {
313350
telemetry.record({ lambdaPackageType: args.ecrRepoUri !== undefined ? 'Image' : 'Zip' })
314351

315352
const { path: samCliPath, parsedVersion } = await getSamCliPathAndVersion()
316353
const { boundArgs } = await saveAndBindArgs(args)
317-
const overrides = await loadLegacyParameterOverrides(args.template)
318-
if (overrides !== undefined) {
319-
// Leaving this out of the definitions file as this is _very_ niche and specific to the
320-
// implementation. Plus we would have to redefine `sam_sync` to add it.
321-
telemetry.record({ isUsingTemplatesJson: true } as any)
322-
boundArgs.push('--parameter-overrides', ...overrides)
354+
355+
if (!!args.templateParameters && Object.entries(args.templateParameters).length > 0) {
356+
const templateParameters = new Map<string, string>(Object.entries(args.templateParameters))
357+
const paramsToSet: string[] = []
358+
for (const [key, value] of templateParameters.entries()) {
359+
if (value) {
360+
await updateRecentResponse(syncMementoRootKey, args.template.uri.fsPath, key, value)
361+
paramsToSet.push(`ParameterKey=${key},ParameterValue=${value}`)
362+
}
363+
}
364+
paramsToSet.length > 0 && boundArgs.push('--parameter-overrides', paramsToSet.join(' '))
323365
}
324366

325367
// '--no-watch' was not added until https://github.com/aws/aws-sam-cli/releases/tag/v1.77.0
@@ -431,21 +473,30 @@ export async function prepareSyncParams(
431473
): Promise<Partial<SyncParams>> {
432474
// Skip creating dependency layers by default for backwards compat
433475
const baseParams: Partial<SyncParams> = { skipDependencyLayer: true }
476+
const entryPoint = getSyncEntryPoint(arg)
434477

435-
if (arg instanceof AWSTreeNodeBase) {
436-
// "Deploy" command was invoked on a regionNode.
437-
return { ...baseParams, region: arg.regionCode }
438-
} else if (arg instanceof vscode.Uri) {
439-
if (arg.path.endsWith('samconfig.toml')) {
440-
// "Deploy" command was invoked on a samconfig.toml file.
441-
// TODO: add step to verify samconfig content to skip param source prompter
442-
const config = await SamConfig.fromConfigFileUri(arg)
478+
switch (entryPoint) {
479+
case SamSyncEntryPoints.SamTemplateFile: {
480+
const entryPointArg = arg as vscode.Uri
481+
const template = {
482+
uri: entryPointArg,
483+
data: await CloudFormation.load(entryPointArg.fsPath, validate),
484+
}
485+
486+
return {
487+
...baseParams,
488+
template: template,
489+
projectRoot: getProjectRootUri(template.uri),
490+
}
491+
}
492+
case SamSyncEntryPoints.SamConfigFile: {
493+
const config = await SamConfig.fromConfigFileUri(arg as vscode.Uri)
443494
const params = getSyncParamsFromConfig(config)
444495
const projectRoot = vscode.Uri.joinPath(config.location, '..')
445496
const templateUri = params.templatePath
446497
? vscode.Uri.file(path.resolve(projectRoot.fsPath, params.templatePath))
447498
: undefined
448-
const template = templateUri
499+
const samConfigFileTemplate = templateUri
449500
? {
450501
uri: templateUri,
451502
data: await CloudFormation.load(templateUri.fsPath),
@@ -454,29 +505,38 @@ export async function prepareSyncParams(
454505
// Always use the dependency layer if the user specified to do so
455506
const skipDependencyLayer = !config.getCommandParam('sync', 'dependency_layer')
456507

457-
return { ...baseParams, ...params, template, projectRoot, skipDependencyLayer } as SyncParams
508+
return {
509+
...baseParams,
510+
...params,
511+
template: samConfigFileTemplate,
512+
projectRoot,
513+
skipDependencyLayer,
514+
} as SyncParams
458515
}
459-
460-
// "Deploy" command was invoked on a template.yaml file.
461-
const template = {
462-
uri: arg,
463-
data: await CloudFormation.load(arg.fsPath, validate),
516+
case SamSyncEntryPoints.RegionNodeContextMenu: {
517+
const entryPointArg = arg as AWSTreeNodeBase
518+
return { ...baseParams, region: entryPointArg.regionCode }
464519
}
465-
466-
return { ...baseParams, template, projectRoot: getProjectRootUri(template.uri) }
467-
} else if (arg && arg.getTreeItem()) {
468-
// "Deploy" command was invoked on a TreeNode on the AppBuilder.
469-
const templateUri = (arg.getTreeItem() as vscode.TreeItem).resourceUri
470-
if (templateUri) {
471-
const template = {
472-
uri: templateUri,
473-
data: await CloudFormation.load(templateUri.fsPath, validate),
520+
case SamSyncEntryPoints.AppBuilderNodeButton: {
521+
const entryPointArg = arg as TreeNode
522+
const templateUri = (entryPointArg.getTreeItem() as vscode.TreeItem).resourceUri
523+
if (templateUri) {
524+
const template = {
525+
uri: templateUri,
526+
data: await CloudFormation.load(templateUri.fsPath, validate),
527+
}
528+
return {
529+
...baseParams,
530+
template,
531+
projectRoot: getProjectRootUri(templateUri),
532+
}
474533
}
475-
return { ...baseParams, template, projectRoot: getProjectRootUri(template.uri) }
534+
return baseParams
476535
}
536+
case SamSyncEntryPoints.CommandPalette:
537+
default:
538+
return baseParams
477539
}
478-
479-
return baseParams
480540
}
481541

482542
export type SamSyncResult = {

packages/core/src/shared/ui/wizardPrompter.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ import { Prompter, PromptResult } from './prompter'
1818
* - {@link SingleNestedWizard}
1919
* - {@link DoubleNestedWizard}
2020
*/
21+
22+
// eslint-disable-next-line @typescript-eslint/naming-convention
23+
export const WIZARD_PROMPTER = 'WIZARD_PROMPTER'
2124
export class WizardPrompter<T> extends Prompter<T> {
2225
public get recentItem(): any {
2326
return undefined
@@ -56,6 +59,7 @@ export class WizardPrompter<T> extends Prompter<T> {
5659
}
5760
}
5861

62+
// eslint-disable-next-line @typescript-eslint/naming-convention
5963
protected async promptUser(): Promise<PromptResult<T>> {
6064
this.response = await this.wizard.run()
6165

packages/core/src/test/awsService/appBuilder/wizards/deployTypeWizard.test.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,10 @@ describe('DeployTypeWizard', function () {
6363
assert.strictEqual(picker.items.length, 2)
6464
picker.acceptItem(picker.items[1])
6565
})
66-
.handleInputBox('Specify SAM parameter value for SourceBucketName', (inputBox) => {
66+
.handleInputBox('Specify SAM Template parameter value for SourceBucketName', (inputBox) => {
6767
inputBox.acceptValue('my-source-bucket-name')
6868
})
69-
.handleInputBox('Specify SAM parameter value for DestinationBucketName', (inputBox) => {
69+
.handleInputBox('Specify SAM Template parameter value for DestinationBucketName', (inputBox) => {
7070
inputBox.acceptValue('my-destination-bucket-name')
7171
})
7272
.handleQuickPick('Specify parameter source for deploy', async (quickPick) => {
@@ -98,6 +98,12 @@ describe('DeployTypeWizard', function () {
9898
assert.strictEqual(picker.items.length, 2)
9999
picker.acceptItem(picker.items[0])
100100
})
101+
.handleInputBox('Specify SAM Template parameter value for SourceBucketName', (inputBox) => {
102+
inputBox.acceptValue('my-source-bucket-name')
103+
})
104+
.handleInputBox('Specify SAM Template parameter value for DestinationBucketName', (inputBox) => {
105+
inputBox.acceptValue('my-destination-bucket-name')
106+
})
101107
.handleQuickPick('Specify parameter source for sync', async (quickPick) => {
102108
// Need time to check samconfig.toml file and generate options
103109
await quickPick.untilReady()

0 commit comments

Comments
 (0)