Skip to content

Commit 8b6a8e7

Browse files
authored
Add a conditional revision draft deploy pop-up (#478)
1 parent 657473c commit 8b6a8e7

File tree

17 files changed

+150
-40
lines changed

17 files changed

+150
-40
lines changed

package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -522,6 +522,11 @@
522522
"%containerApps.deleteConfirmation.ClickButton%"
523523
],
524524
"default": "EnterName"
525+
},
526+
"containerApps.showDraftCommandDeployPopup": {
527+
"type": "boolean",
528+
"description": "%containerApps.showDraftCommandDeployPopup%",
529+
"default": true
525530
}
526531
}
527532
}

package.nls.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
"containerApps.revealResource": "Reveal Resource",
55
"containerApps.description": "An Azure Container Apps extension for Visual Studio Code.",
66
"containerApps.enableOutputTimestamps": "Prepends each line displayed in the output channel with a timestamp.",
7+
"containerApps.showDraftCommandDeployPopup": "Show an informational deploy pop-up message whenever a draft command is run.",
78
"containerApps.browse": "Browse",
89
"containerApps.createContainerApp": "Create Container App...",
910
"containerApps.editContainerApp": "Edit Container App (Advanced)...",

src/commands/image/updateImage/UpdateImageDraftStep.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export class UpdateImageDraftStep<T extends UpdateImageContext> extends Revision
2222
super(baseItem);
2323
}
2424

25-
public async execute(context: UpdateImageContext, progress: Progress<{ message?: string | undefined; increment?: number | undefined }>): Promise<void> {
25+
public async execute(context: T, progress: Progress<{ message?: string | undefined; increment?: number | undefined }>): Promise<void> {
2626
progress.report({ message: localize('updatingImage', 'Updating image (draft)...') });
2727

2828
this.revisionDraftTemplate.containers = [];
@@ -33,13 +33,13 @@ export class UpdateImageDraftStep<T extends UpdateImageContext> extends Revision
3333
name: getContainerNameForImage(nonNullProp(context, 'image')) + `-${randomUtils.getRandomHexString(5)}`,
3434
});
3535

36-
this.updateRevisionDraftWithTemplate();
36+
await this.updateRevisionDraftWithTemplate(context);
3737

3838
const parentResource: ContainerAppModel | Revision = getParentResourceFromItem(this.baseItem);
3939
ext.outputChannel.appendLog(localize('updatedImage', 'Updated container app "{0}" with image "{1}" (draft).', parentResource.name, context.image));
4040
}
4141

42-
public shouldExecute(context: UpdateImageContext): boolean {
42+
public shouldExecute(context: T): boolean {
4343
return !!context.containerApp && !!context.image;
4444
}
4545
}

src/commands/revisionDraft/RevisionDraftUpdateBaseStep.ts

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,20 @@
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55

6-
import { type Template } from "@azure/arm-appcontainers";
6+
import { KnownActiveRevisionsMode, type Template } from "@azure/arm-appcontainers";
77
import { AzureWizardExecuteStep, nonNullValueAndProp } from "@microsoft/vscode-azext-utils";
8-
import type { Progress } from "vscode";
8+
import { ProgressLocation, window, type Progress } from "vscode";
99
import { ext } from "../../extensionVariables";
1010
import { ContainerAppItem } from "../../tree/ContainerAppItem";
11+
import { RevisionDraftItem } from "../../tree/revisionManagement/RevisionDraftItem";
1112
import type { RevisionsItemModel } from "../../tree/revisionManagement/RevisionItem";
13+
import { localize } from "../../utils/localize";
14+
import { pickContainerAppWithoutPrompt } from "../../utils/pickItem/pickContainerApp";
15+
import { pickRevisionDraft } from "../../utils/pickItem/pickRevision";
1216
import { getParentResourceFromItem } from "../../utils/revisionDraftUtils";
17+
import { settingUtils } from "../../utils/settingUtils";
1318
import type { IContainerAppContext } from "../IContainerAppContext";
19+
import { deployRevisionDraft } from "./deployRevisionDraft/deployRevisionDraft";
1420

1521
export abstract class RevisionDraftUpdateBaseStep<T extends IContainerAppContext> extends AzureWizardExecuteStep<T> {
1622
/**
@@ -29,8 +35,9 @@ export abstract class RevisionDraftUpdateBaseStep<T extends IContainerAppContext
2935
/**
3036
* Call this method to upload `revisionDraftTemplate` changes to the container app revision draft
3137
*/
32-
protected updateRevisionDraftWithTemplate(): void {
38+
protected async updateRevisionDraftWithTemplate(context: T): Promise<void> {
3339
ext.revisionDraftFileSystem.updateRevisionDraftWithTemplate(this.baseItem, this.revisionDraftTemplate);
40+
await this.showRevisionDraftDeployPopup(context);
3441
}
3542

3643
private initRevisionDraftTemplate(): Template {
@@ -41,4 +48,44 @@ export abstract class RevisionDraftUpdateBaseStep<T extends IContainerAppContext
4148
}
4249
return template;
4350
}
51+
52+
/**
53+
* An informational deploy pop-up to show after executing a revision draft command
54+
*/
55+
private async showRevisionDraftDeployPopup(context: T): Promise<void> {
56+
if (!await settingUtils.getGlobalSetting('showDraftCommandDeployPopup')) {
57+
return;
58+
}
59+
60+
const yes: string = localize('yes', 'Yes');
61+
const no: string = localize('no', 'No');
62+
const dontShowAgain: string = localize('dontShowAgain', 'Don\'t show again');
63+
64+
const message: string = localize('message', 'Would you like to deploy these changes? Click "Yes" to proceed, or "No" to continue making changes.');
65+
const buttonMessages: string[] = [yes, no, dontShowAgain];
66+
67+
void window.showInformationMessage(message, ...buttonMessages).then(async (result: string | undefined) => {
68+
if (result === yes) {
69+
const item: ContainerAppItem | RevisionDraftItem = await window.withProgress({
70+
location: ProgressLocation.Notification,
71+
cancellable: false,
72+
title: localize('preparingForDeployment', 'Preparing for deployment...')
73+
}, async () => {
74+
const containerAppItem: ContainerAppItem = await pickContainerAppWithoutPrompt(context, this.baseItem.containerApp, { showLoadingPrompt: false });
75+
76+
if (this.baseItem.containerApp.revisionsMode === KnownActiveRevisionsMode.Single) {
77+
return containerAppItem;
78+
} else {
79+
return await pickRevisionDraft(context, containerAppItem, { showLoadingPrompt: false });
80+
}
81+
});
82+
83+
await deployRevisionDraft(context, item);
84+
} else if (result === dontShowAgain) {
85+
await settingUtils.updateGlobalSetting('showDraftCommandDeployPopup', false);
86+
} else {
87+
// Do nothing
88+
}
89+
});
90+
}
4491
}

src/commands/scaling/scaleRange/ScaleRangeUpdateStep.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@ export class ScaleRangeUpdateStep<T extends ScaleRangeContext> extends RevisionD
1818
super(baseItem);
1919
}
2020

21-
public async execute(context: ScaleRangeContext): Promise<void> {
21+
public async execute(context: T): Promise<void> {
2222
this.revisionDraftTemplate.scale ||= {};
2323
this.revisionDraftTemplate.scale.minReplicas = context.newMinRange;
2424
this.revisionDraftTemplate.scale.maxReplicas = context.newMaxRange;
2525

26-
this.updateRevisionDraftWithTemplate();
26+
await this.updateRevisionDraftWithTemplate(context);
2727

2828
context.scaleMinRange = nonNullProp(context, 'newMinRange');
2929
context.scaleMaxRange = nonNullProp(context, 'newMaxRange');
@@ -32,7 +32,7 @@ export class ScaleRangeUpdateStep<T extends ScaleRangeContext> extends RevisionD
3232
ext.outputChannel.appendLog(localize('updatedScaleRange', 'Updated replica scaling range to {0}-{1} for "{2}".', context.newMinRange, context.newMaxRange, parentResourceName));
3333
}
3434

35-
public shouldExecute(context: ScaleRangeContext): boolean {
35+
public shouldExecute(context: T): boolean {
3636
return context.newMinRange !== undefined && context.newMaxRange !== undefined;
3737
}
3838
}

src/commands/scaling/scaleRule/addScaleRule/AddScaleRuleStep.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,23 +20,23 @@ export class AddScaleRuleStep<T extends IAddScaleRuleContext> extends RevisionDr
2020
super(baseItem);
2121
}
2222

23-
public async execute(context: IAddScaleRuleContext): Promise<void> {
23+
public async execute(context: T): Promise<void> {
2424
this.revisionDraftTemplate.scale ||= {};
2525
this.revisionDraftTemplate.scale.rules ||= [];
2626

2727
context.scaleRule = this.buildRule(context);
2828
this.integrateRule(context, this.revisionDraftTemplate.scale.rules, context.scaleRule);
29-
this.updateRevisionDraftWithTemplate();
29+
await this.updateRevisionDraftWithTemplate(context);
3030

3131
const resourceName = getParentResourceFromItem(this.baseItem).name;
3232
ext.outputChannel.appendLog(localize('addedScaleRule', 'Added {0} rule "{1}" to "{2}" (draft)', context.newRuleType, context.newRuleName, resourceName));
3333
}
3434

35-
public shouldExecute(context: IAddScaleRuleContext): boolean {
35+
public shouldExecute(context: T): boolean {
3636
return !!context.newRuleName && !!context.newRuleType;
3737
}
3838

39-
private buildRule(context: IAddScaleRuleContext): ScaleRule {
39+
private buildRule(context: T): ScaleRule {
4040
const scaleRule: ScaleRule = { name: context.newRuleName };
4141
switch (context.newRuleType) {
4242
case ScaleRuleTypes.HTTP:
@@ -58,7 +58,7 @@ export class AddScaleRuleStep<T extends IAddScaleRuleContext> extends RevisionDr
5858
return scaleRule;
5959
}
6060

61-
private integrateRule(context: IAddScaleRuleContext, scaleRules: ScaleRule[], scaleRule: ScaleRule): void {
61+
private integrateRule(context: T, scaleRules: ScaleRule[], scaleRule: ScaleRule): void {
6262
switch (context.newRuleType) {
6363
case ScaleRuleTypes.HTTP:
6464
// Portal only allows one HTTP rule per revision

src/commands/scaling/scaleRule/addScaleRule/addScaleRule.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,6 @@ export async function addScaleRule(context: IActionContext, node?: ScaleRuleGrou
3939
});
4040

4141
await wizard.prompt();
42-
4342
wizardContext.activityTitle = localize('addScaleRuleTitle', 'Add {0} rule "{1}" to "{2}" (draft)', wizardContext.newRuleType, wizardContext.newRuleName, parentResource.name);
44-
4543
await wizard.execute();
4644
}

src/commands/scaling/scaleRule/deleteScaleRule/DeleteScaleRuleStep.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,20 +17,20 @@ export class DeleteScaleRuleStep<T extends ScaleRuleContext> extends RevisionDra
1717
super(baseItem);
1818
}
1919

20-
public async execute(context: ScaleRuleContext): Promise<void> {
20+
public async execute(context: T): Promise<void> {
2121
this.revisionDraftTemplate.scale ||= {};
2222
this.revisionDraftTemplate.scale.rules ||= [];
2323

2424
const index = this.revisionDraftTemplate.scale.rules.findIndex(r => r.name === nonNullValueAndProp(context.scaleRule, 'name'));
2525
this.revisionDraftTemplate.scale.rules.splice(index, 1);
2626

27-
this.updateRevisionDraftWithTemplate();
27+
await this.updateRevisionDraftWithTemplate(context);
2828

2929
const resourceName = getParentResourceFromItem(this.baseItem).name;
3030
ext.outputChannel.appendLog(localize('deletedScaleRule', 'Deleted rule "{0}" to "{1}" (draft)', context.scaleRule?.name, resourceName));
3131
}
3232

33-
public shouldExecute(context: ScaleRuleContext): boolean {
33+
public shouldExecute(context: T): boolean {
3434
return !!context.scaleRule;
3535
}
3636
}

src/tree/ContainerAppItem.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
import { ContainerApp, ContainerAppsAPIClient, KnownActiveRevisionsMode, Revision, Template } from "@azure/arm-appcontainers";
77
import { getResourceGroupFromId, uiUtils } from "@microsoft/vscode-azext-azureutils";
8-
import { AzureWizard, DeleteConfirmationStep, IActionContext, callWithTelemetryAndErrorHandling, createContextValue, createSubscriptionContext, nonNullProp, nonNullValue } from "@microsoft/vscode-azext-utils";
8+
import { AzureWizard, DeleteConfirmationStep, IActionContext, callWithTelemetryAndErrorHandling, createContextValue, createSubscriptionContext, nonNullProp, nonNullValue, nonNullValueAndProp } from "@microsoft/vscode-azext-utils";
99
import { AzureSubscription, ViewPropertiesModel } from "@microsoft/vscode-azureresources-api";
1010
import * as deepEqual from "deep-eql";
1111
import { TreeItem, TreeItemCollapsibleState, Uri } from "vscode";
@@ -61,8 +61,13 @@ export class ContainerAppItem implements ContainerAppsItem, RevisionsDraftModel
6161

6262
private get contextValue(): string {
6363
const values: string[] = [ContainerAppItem.contextValue];
64+
65+
// Enable more granular tree item filtering by container app name
66+
values.push(nonNullValueAndProp(this.containerApp, 'name'));
67+
6468
values.push(this.containerApp.revisionsMode === KnownActiveRevisionsMode.Single ? revisionModeSingleContextValue : revisionModeMultipleContextValue);
6569
values.push(this.hasUnsavedChanges() ? unsavedChangesTrueContextValue : unsavedChangesFalseContextValue);
70+
6671
return createContextValue(values);
6772
}
6873

src/tree/ManagedEnvironmentItem.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
import { ContainerAppsAPIClient, ManagedEnvironment, Resource } from "@azure/arm-appcontainers";
77
import { getResourceGroupFromId, uiUtils } from "@microsoft/vscode-azext-azureutils";
8-
import { IActionContext, callWithTelemetryAndErrorHandling, createSubscriptionContext, nonNullProp } from "@microsoft/vscode-azext-utils";
8+
import { IActionContext, callWithTelemetryAndErrorHandling, createContextValue, createSubscriptionContext, nonNullProp, nonNullValueAndProp } from "@microsoft/vscode-azext-utils";
99
import { AzureResource, AzureSubscription } from "@microsoft/vscode-azureresources-api";
1010
import { TreeItem, TreeItemCollapsibleState } from "vscode";
1111
import { createContainerAppsAPIClient } from "../utils/azureClients";
@@ -25,6 +25,16 @@ export class ManagedEnvironmentItem implements TreeElementBase {
2525
this.id = managedEnvironment.id;
2626
}
2727

28+
private get contextValue(): string {
29+
const values: string[] = [];
30+
31+
// Enable more granular tree item filtering by environment name
32+
values.push(nonNullValueAndProp(this.managedEnvironment, 'name'));
33+
34+
values.push(ManagedEnvironmentItem.contextValue);
35+
return createContextValue(values);
36+
}
37+
2838
async getChildren(): Promise<ContainerAppsItem[]> {
2939
const result = await callWithTelemetryAndErrorHandling('getChildren', async (context) => {
3040
const containerApps = await ContainerAppItem.List(context, this.subscription, this.id);
@@ -39,7 +49,7 @@ export class ManagedEnvironmentItem implements TreeElementBase {
3949
label: this.managedEnvironment.name,
4050
id: this.id,
4151
iconPath: treeUtils.getIconPath('managed-environment'),
42-
contextValue: ManagedEnvironmentItem.contextValue,
52+
contextValue: this.contextValue,
4353
collapsibleState: TreeItemCollapsibleState.Collapsed,
4454
}
4555
}

0 commit comments

Comments
 (0)