Skip to content
Merged
Show file tree
Hide file tree
Changes from 12 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
11 changes: 11 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,12 @@
"command": "containerApps.walkthrough.cleanUpResources",
"title": "%containerApps.walkthrough.cleanUpResources%",
"category": "Azure Container Apps"
},
{
"command": "containerapps.toggleEnvironmentVariableVisibility",
"title": "%containerapps.toggleEnvironmentVariableVisibility%",
"category": "Azure Container Apps",
"icon": "$(eye)"
}
],
"submenus": [
Expand Down Expand Up @@ -517,6 +523,11 @@
"command": "containerApps.disconnectRepo",
"when": "view =~ /(azureResourceGroups|azureFocusView)/ && viewItem =~ /actionsConnected:true(.*)containerAppsActionsItem/i",
"group": "1@2"
},
{
"command": "containerapps.toggleEnvironmentVariableVisibility",
"when": "view =~ /(azureResourceGroups|azureFocusView)/ && viewItem =~ /environmentVariableItem/i",
"group": "inline"
}
],
"commandPalette": [
Expand Down
1 change: 1 addition & 0 deletions package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
"containerApps.deploymentConfiguration.containerApp": "The name of the target container app.",
"containerApps.deploymentConfiguration.containerRegistry": "The name of the container registry to use for storing and building images.",
"containerApps.deploymentConfigurations": "A list of saved deployment configurations used for quickly redeploying a workspace project to a container app.",
"containerApp.toggleEnvironmentVariableVisibility": "Toggle Environment Variable Visibility.",
"containerApps.walkthrough.gettingStarted.title": "Getting Started with Azure Container Apps",
"containerApps.walkthrough.gettingStarted.description": "Learn to deploy a containerized application to Azure Container Apps using a sample GitHub repository.",
"containerApps.walkthrough.addWorkspaceProject.title": "Add a workspace project",
Expand Down
33 changes: 33 additions & 0 deletions resources/containers.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 6 additions & 1 deletion src/commands/registerCommands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { registerCommand, registerCommandWithTreeNodeUnwrapping, registerErrorHandler, registerReportIssueCommand } from '@microsoft/vscode-azext-utils';
import { registerCommand, registerCommandWithTreeNodeUnwrapping, registerErrorHandler, registerReportIssueCommand, type IActionContext } from '@microsoft/vscode-azext-utils';
import { type EnvironmentVariableItem } from '../tree/containers/EnvironmentVariableItem';
import { browseContainerAppNode } from './browseContainerApp';
import { createContainerApp } from './createContainerApp/createContainerApp';
import { createManagedEnvironment } from './createManagedEnvironment/createManagedEnvironment';
Expand Down Expand Up @@ -57,6 +58,10 @@ export function registerCommands(): void {
registerCommandWithTreeNodeUnwrapping('containerApps.editContainerApp', editContainerApp);
registerCommandWithTreeNodeUnwrapping('containerApps.openConsoleInPortal', openConsoleInPortal);
registerCommandWithTreeNodeUnwrapping('containerApps.updateImage', updateImage);
registerCommandWithTreeNodeUnwrapping('containerapps.toggleEnvironmentVariableVisibility',
async (context: IActionContext, item: EnvironmentVariableItem) => {
await item.toggleValueVisibility(context);
});
Comment thread
MicroFish91 marked this conversation as resolved.

// deploy
registerCommandWithTreeNodeUnwrapping('containerApps.deployImageApi', deployImageApi);
Expand Down
51 changes: 51 additions & 0 deletions src/tree/containers/ContainerItem.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { type Container, type Revision } from "@azure/arm-appcontainers";
import { nonNullProp, nonNullValue, type TreeElementBase } from "@microsoft/vscode-azext-utils";
import { type AzureSubscription, type ViewPropertiesModel } from "@microsoft/vscode-azureresources-api";
import { TreeItemCollapsibleState, type TreeItem } from "vscode";
import { getParentResource } from "../../utils/revisionDraftUtils";
import { type ContainerAppModel } from "../ContainerAppItem";
import { type RevisionsItemModel } from "../revisionManagement/RevisionItem";
import { EnvironmentVariablesItem } from "./EnvironmentVariablesItem";
import { ImageItem } from "./ImageItem";

export class ContainerItem implements RevisionsItemModel {
id: string;
label: string;
static readonly contextValue: string = 'containerItem';
static readonly contextValueRegExp: RegExp = new RegExp(ContainerItem.contextValue);

constructor(readonly subscription: AzureSubscription, readonly containerApp: ContainerAppModel, readonly revision: Revision, readonly container: Container) {
this.id = `${this.parentResource.id}/${container.name}`;
this.label = nonNullValue(this.container.name);
}

getTreeItem(): TreeItem {
return {
id: this.id,
label: `${this.container.name}`,
contextValue: ContainerItem.contextValue,
collapsibleState: TreeItemCollapsibleState.Collapsed,
}
}

getChildren(): TreeElementBase[] {
return [
new ImageItem(this.subscription, this.containerApp, this.revision, this.id, this.container),
new EnvironmentVariablesItem(this.subscription, this.containerApp, this.revision, this.id, this.container)
];
}

private get parentResource(): ContainerAppModel | Revision {
return getParentResource(this.containerApp, this.revision);
}

viewProperties: ViewPropertiesModel = {
data: this.container,
label: nonNullProp(this.container, 'name'),
}
}
89 changes: 89 additions & 0 deletions src/tree/containers/ContainersItem.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { KnownActiveRevisionsMode, type Container, type Revision } from "@azure/arm-appcontainers";
import { nonNullValue, nonNullValueAndProp, type TreeElementBase } from "@microsoft/vscode-azext-utils";
import { type AzureSubscription, type ViewPropertiesModel } from "@microsoft/vscode-azureresources-api";
import * as deepEqual from 'deep-eql';
import { TreeItemCollapsibleState, type TreeItem } from "vscode";
import { ext } from "../../extensionVariables";
import { localize } from "../../utils/localize";
import { getParentResource } from "../../utils/revisionDraftUtils";
import { treeUtils } from "../../utils/treeUtils";
import { type ContainerAppModel } from "../ContainerAppItem";
import { RevisionDraftDescendantBase } from "../revisionManagement/RevisionDraftDescendantBase";
import { RevisionDraftItem } from "../revisionManagement/RevisionDraftItem";
import { ContainerItem } from "./ContainerItem";
import { EnvironmentVariablesItem } from "./EnvironmentVariablesItem";
import { ImageItem } from "./ImageItem";

export class ContainersItem extends RevisionDraftDescendantBase {
id: string;
label: string;
private containers: Container[] = [];

constructor(public readonly subscription: AzureSubscription,
public readonly containerApp: ContainerAppModel,
public readonly revision: Revision,) {
super(subscription, containerApp, revision);
this.id = `${this.parentResource.id}/containers`;
this.containers = nonNullValue(revision.template?.containers);
this.label = this.containers.length === 1 ? localize('container', 'Container') : localize('containers', 'Containers');
}

getChildren(): TreeElementBase[] {
if (this.containers.length === 1) {
return [new ImageItem(this.subscription, this.containerApp, this.revision, this.id, this.containers[0]),
new EnvironmentVariablesItem(this.subscription, this.containerApp, this.revision, this.id, this.containers[0])];
}
return nonNullValue(this.containers?.map(container => new ContainerItem(this.subscription, this.containerApp, this.revision, container)));
}

getTreeItem(): TreeItem {
return {
id: this.id,
label: this.label,
iconPath: treeUtils.getIconPath('containers'),
collapsibleState: TreeItemCollapsibleState.Collapsed
}
}

private get parentResource(): ContainerAppModel | Revision {
return getParentResource(this.containerApp, this.revision);
}

protected setProperties(): void {
this.label = this.containers.length === 1 ? localize('container', 'Container') : localize('containers', 'Containers');
this.containers = nonNullValueAndProp(this.parentResource.template, 'containers');
}

protected setDraftProperties(): void {
this.label = this.containers.length === 1 ? localize('container*', 'Container*') : localize('containers*', 'Containers*');
this.containers = nonNullValueAndProp(ext.revisionDraftFileSystem.parseRevisionDraft(this), 'containers');
}

viewProperties: ViewPropertiesModel = {
label: 'Containers',
getData: async () => {
return this.containers.length === 1 ? this.containers[0] : JSON.stringify(this.containers)
}
}

hasUnsavedChanges(): boolean {
// We only care about showing changes to descendants of the revision draft item when in multiple revisions mode
if (this.containerApp.revisionsMode === KnownActiveRevisionsMode.Multiple && !RevisionDraftItem.hasDescendant(this)) {
return false;
}

const draftTemplate = ext.revisionDraftFileSystem.parseRevisionDraft(this)?.containers;
const currentTemplate = this.parentResource.template?.containers;

if (!draftTemplate) {
return false;
}

return !deepEqual(currentTemplate, draftTemplate);
}
}
49 changes: 49 additions & 0 deletions src/tree/containers/EnvironmentVariableItem.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { type Container, type EnvironmentVar, type Revision } from "@azure/arm-appcontainers";
import { type IActionContext } from "@microsoft/vscode-azext-utils";
import { type AzureSubscription } from "@microsoft/vscode-azureresources-api";
import { ThemeIcon, type TreeItem } from "vscode";
import { ext } from "../../extensionVariables";
import { localize } from "../../utils/localize";
import { getParentResource } from "../../utils/revisionDraftUtils";
import { type ContainerAppModel } from "../ContainerAppItem";
import { type RevisionsItemModel } from "../revisionManagement/RevisionItem";

export class EnvironmentVariableItem implements RevisionsItemModel {
_hideValue: boolean;
constructor(public readonly subscription: AzureSubscription,
public readonly containerApp: ContainerAppModel,
public readonly revision: Revision,
readonly containerId: string,
readonly container: Container,
readonly envVariable: EnvironmentVar) {
this._hideValue = true;
}
id: string = `${this.parentResource.id}/${this.container.image}/${this.envVariable.name}`

getTreeItem(): TreeItem {
return {
label: this._hideValue ? `${this.envVariable.name}=Hidden value. Click to view.` : `${this.envVariable.name}=${this.envVariable.value}`,
contextValue: 'environmentVariableItem',
iconPath: new ThemeIcon('symbol-constant'),
command: {
command: 'containerapps.toggleEnvironmentVariableVisibility',
title: localize('commandtitle', 'Toggle Environment Variable Visibility'),
arguments: [this, this._hideValue,]
}
}
}

public async toggleValueVisibility(_: IActionContext): Promise<void> {
this._hideValue = !this._hideValue;
ext.branchDataProvider.refresh(this);
}

private get parentResource(): ContainerAppModel | Revision {
return getParentResource(this.containerApp, this.revision);
}
}
48 changes: 48 additions & 0 deletions src/tree/containers/EnvironmentVariablesItem.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { type Container, type Revision } from "@azure/arm-appcontainers";
import { type TreeElementBase } from "@microsoft/vscode-azext-utils";
import { type AzureSubscription } from "@microsoft/vscode-azureresources-api";
import { ThemeIcon, TreeItemCollapsibleState, type TreeItem } from "vscode";
import { localize } from "../../utils/localize";
import { getParentResource } from "../../utils/revisionDraftUtils";
import { type ContainerAppModel } from "../ContainerAppItem";
import { type RevisionsItemModel } from "../revisionManagement/RevisionItem";
import { EnvironmentVariableItem } from "./EnvironmentVariableItem";

export class EnvironmentVariablesItem implements RevisionsItemModel {
static readonly contextValue: string = 'environmentVariablesItem';
static readonly contextValueRegExp: RegExp = new RegExp(EnvironmentVariablesItem.contextValue);

constructor(public readonly subscription: AzureSubscription,
public readonly containerApp: ContainerAppModel,
public readonly revision: Revision,
readonly containerId: string,
readonly container: Container) {
}
id: string = `${this.parentResource.id}/environmentVariables/${this.container.image}`;

getTreeItem(): TreeItem {
return {
id: this.id,
label: localize('environmentVariables', 'Environment Variables'),
iconPath: new ThemeIcon('settings'),
contextValue: EnvironmentVariablesItem.contextValue,
collapsibleState: TreeItemCollapsibleState.Collapsed
}
}

getChildren(): TreeElementBase[] | undefined {
if (!this.container.env) {
return;
}
return this.container.env?.map(env => new EnvironmentVariableItem(this.subscription, this.containerApp, this.revision, this.id, this.container, env));
}

private get parentResource(): ContainerAppModel | Revision {
return getParentResource(this.containerApp, this.revision);
}
}
Loading