Skip to content

Commit 85a46ce

Browse files
refactor(lambda): decompose methods for SAM sync/deploy/build (aws#6014)
## Problem The `sync.ts` are huge consisting of many helper functions that being reused for deploy and build. ## Solution - split shared methods into separate files for easier testing and maintenance - split sync.test.ts file into smaller file - consolidate `paramsSourcePrompter` for both sync and deploy to avoid duplicate code - update PrompterTester helper class to clean up VS Code UI element for early exit from consumer callback - rename paramsSource prompter title --- License: I confirm that my contribution is made under the terms of the Apache 2.0 license. --------- Co-authored-by: Roger Zhang <[email protected]>
1 parent 6fb2ecb commit 85a46ce

25 files changed

+2215
-1381
lines changed

packages/core/src/awsService/appBuilder/explorer/openTemplate.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
44
*/
55

66
import { CloudFormationTemplateRegistry } from '../../../shared/fs/templateRegistry'
7-
import { createTemplatePrompter, TemplateItem } from '../../../shared/sam/sync'
7+
import { syncMementoRootKey } from '../../../shared/sam/sync'
8+
89
import { createExitPrompter } from '../../../shared/ui/common/exitPrompter'
10+
import { createTemplatePrompter, TemplateItem } from '../../../shared/ui/sam/templatePrompter'
911
import { Wizard } from '../../../shared/wizards/wizard'
1012

1113
export interface OpenTemplateParams {
@@ -15,6 +17,6 @@ export interface OpenTemplateParams {
1517
export class OpenTemplateWizard extends Wizard<OpenTemplateParams> {
1618
public constructor(state: Partial<OpenTemplateParams>, registry: CloudFormationTemplateRegistry) {
1719
super({ initState: state, exitPrompterProvider: createExitPrompter })
18-
this.form.template.bindPrompter(() => createTemplatePrompter(registry))
20+
this.form.template.bindPrompter(() => createTemplatePrompter(registry, syncMementoRootKey))
1921
}
2022
}

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

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
*/
55

66
import * as vscode from 'vscode'
7-
import { TemplateItem, createTemplatePrompter } from './sync'
7+
import { TemplateItem, createTemplatePrompter } from '../ui/sam/templatePrompter'
88
import { ChildProcess } from '../utilities/processUtils'
99
import { addTelemetryEnvVar } from './cli/samCliInvokerUtils'
1010
import { Wizard } from '../wizards/wizard'
@@ -19,10 +19,11 @@ import globals from '../extensionGlobals'
1919
import { TreeNode } from '../treeview/resourceTreeDataProvider'
2020
import { telemetry } from '../telemetry/telemetry'
2121
import { getSpawnEnv } from '../env/resolveEnv'
22-
import { getErrorCode, getProjectRoot, getSamCliPathAndVersion, isDotnetRuntime } from './utils'
22+
import { getErrorCode, getProjectRoot, getSamCliPathAndVersion, isDotnetRuntime, updateRecentResponse } from './utils'
2323
import { getConfigFileUri, validateSamBuildConfig } from './config'
2424
import { runInTerminal } from './processTerminal'
2525

26+
const buildMementoRootKey = 'samcli.build.params'
2627
export interface BuildParams {
2728
readonly template: TemplateItem
2829
readonly projectRoot: vscode.Uri
@@ -58,7 +59,7 @@ export function createParamsSourcePrompter(existValidSamconfig: boolean) {
5859
)
5960

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

220+
await updateRecentResponse(buildMementoRootKey, 'global', 'templatePath', templatePath)
221+
219222
try {
220223
const { path: samCliPath } = await getSamCliPathAndVersion()
221224

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

Lines changed: 62 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
*/
55

66
import * as vscode from 'vscode'
7-
import { ToolkitError, getLogger, globals } from '../../shared'
7+
import { ToolkitError, globals } from '../../shared'
88
import * as CloudFormation from '../../shared/cloudformation/cloudformation'
99
import { getParameters } from '../../lambda/config/parameterUtils'
1010
import { DefaultCloudFormationClient } from '../clients/cloudFormationClient'
@@ -17,14 +17,23 @@ import { createCommonButtons } from '../ui/buttons'
1717
import { createExitPrompter } from '../ui/common/exitPrompter'
1818
import { createRegionPrompter } from '../ui/common/region'
1919
import { createInputBox } from '../ui/inputPrompter'
20-
import { DataQuickPickItem, createQuickPick } from '../ui/pickerPrompter'
2120
import { ChildProcess } from '../utilities/processUtils'
2221
import { CancellationError } from '../utilities/timeoutUtils'
2322
import { Wizard } from '../wizards/wizard'
2423
import { addTelemetryEnvVar } from './cli/samCliInvokerUtils'
2524
import { validateSamDeployConfig, SamConfig, writeSamconfigGlobal } from './config'
26-
import { TemplateItem, createStackPrompter, createBucketPrompter, createTemplatePrompter } from './sync'
27-
import { getErrorCode, getProjectRoot, getSamCliPathAndVersion, getSource } from './utils'
25+
import { BucketSource, createBucketSourcePrompter, createBucketNamePrompter } from '../ui/sam/bucketPrompter'
26+
import { createStackPrompter } from '../ui/sam/stackPrompter'
27+
import { TemplateItem, createTemplatePrompter } from '../ui/sam/templatePrompter'
28+
import { createDeployParamsSourcePrompter, ParamsSource } from '../ui/sam/paramsSourcePrompter'
29+
import {
30+
getErrorCode,
31+
getProjectRoot,
32+
getSamCliPathAndVersion,
33+
getSource,
34+
getRecentResponse,
35+
updateRecentResponse,
36+
} from './utils'
2837
import { runInTerminal } from './processTerminal'
2938

3039
export interface DeployParams {
@@ -39,89 +48,24 @@ export interface DeployParams {
3948
[key: string]: any
4049
}
4150

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

46-
return root[identifier]?.[key]
47-
}
48-
49-
export async function updateRecentParams(identifier: string, key: string, value: string | undefined) {
50-
try {
51-
const root = globals.context.workspaceState.get(mementoRootKey, {} as Record<string, Record<string, string>>)
52-
await globals.context.workspaceState.update(mementoRootKey, {
53-
...root,
54-
[identifier]: { ...root[identifier], [key]: value },
55-
})
56-
} catch (err) {
57-
getLogger().warn(`sam: unable to save response at key "${key}": %s`, err)
58-
}
53+
function getRecentDeployParams(identifier: string, key: string): string | undefined {
54+
return getRecentResponse(deployMementoRootKey, identifier, key)
5955
}
6056

6157
function createParamPromptProvider(name: string, defaultValue: string | undefined, templateFsPath: string = 'default') {
6258
return createInputBox({
6359
title: `Specify SAM parameter value for ${name}`,
6460
buttons: createCommonButtons(samDeployUrl),
65-
value: getRecentParams(templateFsPath, name) ?? defaultValue,
61+
value: getRecentDeployParams(templateFsPath, name) ?? defaultValue,
6662
})
6763
}
68-
function bucketSourcePrompter() {
69-
const items: DataQuickPickItem<BucketSource>[] = [
70-
{
71-
label: 'Create a SAM CLI managed S3 bucket',
72-
data: BucketSource.SamCliManaged,
73-
},
74-
{
75-
label: 'Specify an S3 bucket',
76-
data: BucketSource.UserProvided,
77-
},
78-
]
7964

80-
return createQuickPick(items, {
81-
title: 'Specify S3 bucket for deployment artifacts',
82-
placeholder: 'Press enter to proceed with highlighted option',
83-
buttons: createCommonButtons(samDeployUrl),
84-
})
85-
}
86-
function paramsSourcePrompter(existValidSamconfig: boolean | undefined) {
87-
const items: DataQuickPickItem<ParamsSource>[] = [
88-
{
89-
label: 'Specify required parameters and save as defaults',
90-
data: ParamsSource.SpecifyAndSave,
91-
},
92-
{
93-
label: 'Specify required parameters',
94-
data: ParamsSource.Specify,
95-
},
96-
]
97-
98-
if (existValidSamconfig) {
99-
items.push({
100-
label: 'Use default values from samconfig',
101-
data: ParamsSource.SamConfig,
102-
})
103-
}
104-
105-
return createQuickPick(items, {
106-
title: 'Specify parameters for deploy',
107-
placeholder: 'Press enter to proceed with highlighted option',
108-
buttons: createCommonButtons(samDeployUrl),
109-
})
110-
}
11165
type DeployResult = {
11266
isSuccess: boolean
11367
}
11468

115-
export enum BucketSource {
116-
SamCliManaged,
117-
UserProvided,
118-
}
119-
export enum ParamsSource {
120-
SpecifyAndSave,
121-
Specify,
122-
SamConfig,
123-
}
124-
12569
export class DeployWizard extends Wizard<DeployParams> {
12670
registry: CloudFormationTemplateRegistry
12771
state: Partial<DeployParams>
@@ -153,50 +97,58 @@ export class DeployWizard extends Wizard<DeployParams> {
15397
this.form.template.setDefault(templateItem)
15498
this.form.projectRoot.setDefault(() => projectRootFolder)
15599
this.form.paramsSource.bindPrompter(async () =>
156-
paramsSourcePrompter(await validateSamDeployConfig(projectRootFolder))
100+
createDeployParamsSourcePrompter(await validateSamDeployConfig(projectRootFolder))
157101
)
158102

159103
this.form.region.bindPrompter(() => createRegionPrompter().transform((r) => r.id), {
160104
showWhen: ({ paramsSource }) =>
161105
paramsSource === ParamsSource.Specify || paramsSource === ParamsSource.SpecifyAndSave,
162106
})
163107
this.form.stackName.bindPrompter(
164-
({ region }) => createStackPrompter(new DefaultCloudFormationClient(region!)),
108+
({ region }) =>
109+
createStackPrompter(new DefaultCloudFormationClient(region!), deployMementoRootKey, samDeployUrl),
165110
{
166111
showWhen: ({ paramsSource }) =>
167112
paramsSource === ParamsSource.Specify || paramsSource === ParamsSource.SpecifyAndSave,
168113
}
169114
)
170-
this.form.bucketSource.bindPrompter(() => bucketSourcePrompter(), {
115+
this.form.bucketSource.bindPrompter(() => createBucketSourcePrompter(), {
171116
showWhen: ({ paramsSource }) =>
172117
paramsSource === ParamsSource.Specify || paramsSource === ParamsSource.SpecifyAndSave,
173118
})
174-
this.form.bucketName.bindPrompter(({ region }) => createBucketPrompter(new DefaultS3Client(region!)), {
175-
showWhen: ({ bucketSource }) => bucketSource === BucketSource.UserProvided,
176-
})
119+
this.form.bucketName.bindPrompter(
120+
({ region }) => createBucketNamePrompter(new DefaultS3Client(region!), deployMementoRootKey),
121+
{
122+
showWhen: ({ bucketSource }) => bucketSource === BucketSource.UserProvided,
123+
}
124+
)
177125
} else if (this.arg && this.arg.regionCode) {
178126
// "Deploy" command was invoked on a regionNode.
179-
this.form.template.bindPrompter(() => createTemplatePrompter(this.registry))
127+
this.form.template.bindPrompter(() => createTemplatePrompter(this.registry, deployMementoRootKey))
180128
this.form.projectRoot.setDefault(({ template }) => getProjectRoot(template))
181129
this.form.paramsSource.bindPrompter(async ({ projectRoot }) => {
182130
const existValidSamConfig: boolean | undefined = await validateSamDeployConfig(projectRoot)
183-
return paramsSourcePrompter(existValidSamConfig)
131+
return createDeployParamsSourcePrompter(existValidSamConfig)
184132
})
185133
this.form.region.setDefault(() => this.arg.regionCode)
186134
this.form.stackName.bindPrompter(
187-
({ region }) => createStackPrompter(new DefaultCloudFormationClient(region!)),
135+
({ region }) =>
136+
createStackPrompter(new DefaultCloudFormationClient(region!), deployMementoRootKey, samDeployUrl),
188137
{
189138
showWhen: ({ paramsSource }) =>
190139
paramsSource === ParamsSource.Specify || paramsSource === ParamsSource.SpecifyAndSave,
191140
}
192141
)
193-
this.form.bucketSource.bindPrompter(() => bucketSourcePrompter(), {
142+
this.form.bucketSource.bindPrompter(() => createBucketSourcePrompter(), {
194143
showWhen: ({ paramsSource }) =>
195144
paramsSource === ParamsSource.Specify || paramsSource === ParamsSource.SpecifyAndSave,
196145
})
197-
this.form.bucketName.bindPrompter(({ region }) => createBucketPrompter(new DefaultS3Client(region!)), {
198-
showWhen: ({ bucketSource }) => bucketSource === BucketSource.UserProvided,
199-
})
146+
this.form.bucketName.bindPrompter(
147+
({ region }) => createBucketNamePrompter(new DefaultS3Client(region!), deployMementoRootKey),
148+
{
149+
showWhen: ({ bucketSource }) => bucketSource === BucketSource.UserProvided,
150+
}
151+
)
200152
} else if (this.arg && this.arg.getTreeItem().resourceUri) {
201153
// "Deploy" command was invoked on a TreeNode on the AppBuilder.
202154
const templateUri = this.arg.getTreeItem().resourceUri as vscode.Uri
@@ -206,54 +158,62 @@ export class DeployWizard extends Wizard<DeployParams> {
206158
this.addParameterPromptersIfApplicable(templateUri)
207159
this.form.template.setDefault(templateItem)
208160
this.form.paramsSource.bindPrompter(async () =>
209-
paramsSourcePrompter(await validateSamDeployConfig(projectRootFolder))
161+
createDeployParamsSourcePrompter(await validateSamDeployConfig(projectRootFolder))
210162
)
211163

212164
this.form.region.bindPrompter(() => createRegionPrompter().transform((r) => r.id), {
213165
showWhen: ({ paramsSource }) =>
214166
paramsSource === ParamsSource.Specify || paramsSource === ParamsSource.SpecifyAndSave,
215167
})
216168
this.form.stackName.bindPrompter(
217-
({ region }) => createStackPrompter(new DefaultCloudFormationClient(region!)),
169+
({ region }) =>
170+
createStackPrompter(new DefaultCloudFormationClient(region!), deployMementoRootKey, samDeployUrl),
218171
{
219172
showWhen: ({ paramsSource }) =>
220173
paramsSource === ParamsSource.Specify || paramsSource === ParamsSource.SpecifyAndSave,
221174
}
222175
)
223-
this.form.bucketSource.bindPrompter(() => bucketSourcePrompter(), {
176+
this.form.bucketSource.bindPrompter(() => createBucketSourcePrompter(), {
224177
showWhen: ({ paramsSource }) =>
225178
paramsSource === ParamsSource.Specify || paramsSource === ParamsSource.SpecifyAndSave,
226179
})
227-
this.form.bucketName.bindPrompter(({ region }) => createBucketPrompter(new DefaultS3Client(region!)), {
228-
showWhen: ({ bucketSource }) => bucketSource === BucketSource.UserProvided,
229-
})
180+
this.form.bucketName.bindPrompter(
181+
({ region }) => createBucketNamePrompter(new DefaultS3Client(region!), deployMementoRootKey),
182+
{
183+
showWhen: ({ bucketSource }) => bucketSource === BucketSource.UserProvided,
184+
}
185+
)
230186
this.form.projectRoot.setDefault(() => getProjectRoot(templateItem))
231187
} else {
232188
// "Deploy" command was invoked on the command palette.
233-
this.form.template.bindPrompter(() => createTemplatePrompter(this.registry))
189+
this.form.template.bindPrompter(() => createTemplatePrompter(this.registry, deployMementoRootKey))
234190
this.form.projectRoot.setDefault(({ template }) => getProjectRoot(template))
235191
this.form.paramsSource.bindPrompter(async ({ projectRoot }) => {
236192
const existValidSamConfig: boolean | undefined = await validateSamDeployConfig(projectRoot)
237-
return paramsSourcePrompter(existValidSamConfig)
193+
return createDeployParamsSourcePrompter(existValidSamConfig)
238194
})
239195
this.form.region.bindPrompter(() => createRegionPrompter().transform((r) => r.id), {
240196
showWhen: ({ paramsSource }) =>
241197
paramsSource === ParamsSource.Specify || paramsSource === ParamsSource.SpecifyAndSave,
242198
})
243199
this.form.stackName.bindPrompter(
244-
({ region }) => createStackPrompter(new DefaultCloudFormationClient(region!)),
200+
({ region }) =>
201+
createStackPrompter(new DefaultCloudFormationClient(region!), deployMementoRootKey, samDeployUrl),
245202
{
246203
showWhen: ({ paramsSource }) =>
247204
paramsSource === ParamsSource.Specify || paramsSource === ParamsSource.SpecifyAndSave,
248205
}
249206
)
250-
this.form.bucketSource.bindPrompter(() => bucketSourcePrompter(), {
207+
this.form.bucketSource.bindPrompter(() => createBucketSourcePrompter(), {
251208
showWhen: ({ paramsSource }) =>
252209
paramsSource === ParamsSource.Specify || paramsSource === ParamsSource.SpecifyAndSave,
253210
})
254-
this.form.bucketName.bindPrompter(({ region }) => createBucketPrompter(new DefaultS3Client(region!)), {
255-
showWhen: ({ bucketSource }) => bucketSource === BucketSource.UserProvided,
256-
})
211+
this.form.bucketName.bindPrompter(
212+
({ region }) => createBucketNamePrompter(new DefaultS3Client(region!), deployMementoRootKey),
213+
{
214+
showWhen: ({ bucketSource }) => bucketSource === BucketSource.UserProvided,
215+
}
216+
)
257217
}
258218

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

316+
await updateRecentResponse(deployMementoRootKey, 'global', 'templatePath', params.template.uri.fsPath)
317+
356318
try {
357319
const { path: samCliPath } = await getSamCliPathAndVersion()
358320

0 commit comments

Comments
 (0)