Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
10 changes: 10 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,11 @@
"title": "%containerApps.openConsoleInPortal%",
"category": "Azure Container Apps"
},
{
"command": "containerApps.openExpressPortal",
"title": "%containerApps.openExpressPortal%",
"category": "Azure Container Apps"
},
{
"command": "containerApps.editContainer",
"title": "%containerApps.editContainer%",
Expand Down Expand Up @@ -451,6 +456,11 @@
"when": "view =~ /(azureResourceGroups|azureFocusView)/ && viewItem =~ /containerAppItem/i",
"group": "6@2"
},
{
"command": "containerApps.openExpressPortal",
"when": "view =~ /(azureResourceGroups|azureFocusView)/ && viewItem =~ /containerAppItem(.*)express/i",
"group": "9@2"
},
{
"command": "containerApps.openConsoleInPortal",
"when": "view =~ /(azureResourceGroups|azureFocusView)/ && viewItem =~ /containerAppItem/i",
Expand Down
1 change: 1 addition & 0 deletions package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
"containerApps.deleteConfirmation.ClickButton": "Prompts with a warning dialog where you click a button to delete.",
"containerApps.openInPortal": "Open in Portal",
"containerApps.openConsoleInPortal": "Open Console in Portal",
"containerApps.openExpressPortal": "Open in Express Portal",
"containerApps.editScaleRange": "Edit Scaling Range...",
"containerApps.addScaleRule": "Add Scale Rule...",
"containerApps.deleteScaleRule": "Delete Scale Rule...",
Expand Down
16 changes: 16 additions & 0 deletions src/commands/openExpressPortal.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { type IActionContext } from "@microsoft/vscode-azext-utils";
import { env, Uri } from "vscode";
import { expressPortalBaseUrl } from "../constants";
import { type ContainerAppItem } from "../tree/ContainerAppItem";
import { pickContainerApp } from "../utils/pickItem/pickContainerApp";

export async function openExpressPortal(context: IActionContext, node?: ContainerAppItem): Promise<void> {
node ??= await pickContainerApp(context);
const url = `${expressPortalBaseUrl}/${node.subscription.subscriptionId}/${node.containerApp.resourceGroup}/${node.containerApp.name}/overview`;
await env.openExternal(Uri.parse(url));
}
2 changes: 2 additions & 0 deletions src/commands/registerCommands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import { toggleIngressVisibility } from './ingress/toggleIngressVisibility/toggl
import { startStreamingLogs } from './logStream/startStreamingLogs';
import { stopStreamingLogs } from './logStream/stopStreamingLogs';
import { openConsoleInPortal } from './openConsoleInPortal';
import { openExpressPortal } from './openExpressPortal';
import { revealInEnvironment } from './revealInEnvironment';
import { activateRevision } from './revision/activateRevision';
import { chooseRevisionMode } from './revision/chooseRevisionMode/chooseRevisionMode';
Expand Down Expand Up @@ -67,6 +68,7 @@ export function registerCommands(): void {
registerCommandWithTreeNodeUnwrapping('containerApps.deleteContainerApp', deleteContainerApp);
registerCommandWithTreeNodeUnwrapping('containerApps.editContainerApp', editContainerApp);
registerCommandWithTreeNodeUnwrapping('containerApps.openConsoleInPortal', openConsoleInPortal);
registerCommandWithTreeNodeUnwrapping('containerApps.openExpressPortal', openExpressPortal);
registerCommandWithTreeNodeUnwrapping('containerApps.revealInEnvironment', revealInEnvironment);
registerCommandWithTreeNodeUnwrapping('containerApps.toggleEnvironmentVariableVisibility',
async (context: IActionContext, item: EnvironmentVariableItem) => {
Expand Down
3 changes: 3 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,6 @@ export const unsavedChangesTrueContextValue: string = 'unsavedChanges:true';
export const unsavedChangesFalseContextValue: string = 'unsavedChanges:false';

export const activityInfoContext: string = 'activity:info';

export const expressContextValue: string = 'express';
export const expressPortalBaseUrl: string = 'https://containerapps.azure.com/apps';
12 changes: 10 additions & 2 deletions src/tree/ContainerAppItem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import deepEqual from "deep-eql";
import { TreeItemCollapsibleState, type TreeItem, type Uri } from "vscode";
import { DeleteAllContainerAppsStep } from "../commands/deleteContainerApp/DeleteAllContainerAppsStep";
import { type IDeleteContainerAppWizardContext } from "../commands/deleteContainerApp/IDeleteContainerAppWizardContext";
import { revisionModeMultipleContextValue, revisionModeSingleContextValue, unsavedChangesFalseContextValue, unsavedChangesTrueContextValue } from "../constants";
import { expressContextValue, revisionModeMultipleContextValue, revisionModeSingleContextValue, unsavedChangesFalseContextValue, unsavedChangesTrueContextValue } from "../constants";
import { ext } from "../extensionVariables";
import { createActivityContext } from "../utils/activityUtils";
import { createContainerAppsAPIClient, createContainerAppsClient } from "../utils/azureClients";
Expand Down Expand Up @@ -47,7 +47,7 @@ export class ContainerAppItem implements ContainerAppsItem, RevisionsDraftModel
return this._containerApp;
}

constructor(public readonly subscription: AzureSubscription, private _containerApp: ContainerAppModel) {
constructor(public readonly subscription: AzureSubscription, private _containerApp: ContainerAppModel, public readonly isExpress: boolean = false) {
this.id = this.containerApp.id;
this.resourceGroup = this.containerApp.resourceGroup;
this.name = this.containerApp.name;
Expand All @@ -67,6 +67,10 @@ export class ContainerAppItem implements ContainerAppsItem, RevisionsDraftModel
values.push(this.containerApp.revisionsMode === KnownActiveRevisionsMode.Single ? revisionModeSingleContextValue : revisionModeMultipleContextValue);
values.push(this.hasUnsavedChanges() ? unsavedChangesTrueContextValue : unsavedChangesFalseContextValue);

if (this.isExpress) {
values.push(expressContextValue);
}

return createContextValue(values);
}

Expand All @@ -79,6 +83,10 @@ export class ContainerAppItem implements ContainerAppsItem, RevisionsDraftModel
return this.containerApp.provisioningState;
}

if (this.isExpress) {
return localize('express', '(Express)');
}

return undefined;
}

Expand Down
29 changes: 24 additions & 5 deletions src/tree/ManagedEnvironmentItem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,26 @@ import { getResourceGroupFromId, uiUtils } from "@microsoft/vscode-azext-azureut
import { callWithTelemetryAndErrorHandling, createContextValue, createSubscriptionContext, nonNullProp, nonNullValueAndProp, type IActionContext } from "@microsoft/vscode-azext-utils";
import { type AzureResource, type AzureSubscription, type ViewPropertiesModel } from "@microsoft/vscode-azureresources-api";
import { TreeItemCollapsibleState, type TreeItem } from "vscode";
import { createContainerAppsAPIClient } from "../utils/azureClients";
import { createContainerAppsAPIClient, createContainerAppsPreviewAPIClient } from "../utils/azureClients";
import { localize } from "../utils/localize";
import { treeUtils } from "../utils/treeUtils";
import { ContainerAppItem } from "./ContainerAppItem";
import { type ContainerAppsItem, type TreeElementBase } from "./ContainerAppsBranchDataProvider";

type ManagedEnvironmentModel = ManagedEnvironment & ResourceModel;
type ManagedEnvironmentModel = ManagedEnvironment & ResourceModel & {
environmentMode?: string;
};

export class ManagedEnvironmentItem implements TreeElementBase {
static readonly contextValue: string = 'managedEnvironmentItem';
static readonly contextValueRegExp: RegExp = new RegExp(ManagedEnvironmentItem.contextValue);
viewProperties: ViewPropertiesModel;
id: string;
readonly isExpress: boolean;


constructor(public readonly subscription: AzureSubscription, public readonly resource: AzureResource, public readonly managedEnvironment: ManagedEnvironmentModel) {
this.isExpress = managedEnvironment.environmentMode === 'Express';
this.id = managedEnvironment.id;
this.viewProperties = {
data: managedEnvironment,
Expand Down Expand Up @@ -53,7 +58,7 @@ export class ManagedEnvironmentItem implements TreeElementBase {
context.valuesToMask.push(...containerApps.map(ca => ca.name), ...containerApps.map(ca => ca.id));

return containerApps
.map(ca => new ContainerAppItem(this.subscription, ca))
.map(ca => new ContainerAppItem(this.subscription, ca, this.isExpress))
.sort((a, b) => treeUtils.sortById(a, b));
});

Expand All @@ -63,6 +68,7 @@ export class ManagedEnvironmentItem implements TreeElementBase {
getTreeItem(): TreeItem {
return {
label: this.managedEnvironment.name,
description: this.isExpress ? localize('express', '(Express)') : undefined,
id: this.id,
iconPath: treeUtils.getIconPath('managed-environment'),
contextValue: this.contextValue,
Expand All @@ -84,8 +90,21 @@ export class ManagedEnvironmentItem implements TreeElementBase {

static async Get(context: IActionContext, subscription: AzureSubscription, resourceGroup: string, name: string): Promise<ManagedEnvironmentModel> {
const subContext = createSubscriptionContext(subscription);
const client: ContainerAppsAPIClient = await createContainerAppsAPIClient([context, subContext]);
return ManagedEnvironmentItem.CreateManagedEnvironmentModel(await client.managedEnvironments.get(resourceGroup, name));
const client: ContainerAppsAPIClient = await createContainerAppsPreviewAPIClient([context, subContext]);

let environmentMode: string | undefined;
const result = await client.managedEnvironments.get(resourceGroup, name, {
onResponse: (response) => {
const rawBody = response.bodyAsText;
if (rawBody) {
environmentMode = (JSON.parse(rawBody) as { properties?: { environmentMode?: string } })?.properties?.environmentMode;
}
},
});

const model = ManagedEnvironmentItem.CreateManagedEnvironmentModel(result);
model.environmentMode = environmentMode;
return model;
}

private static CreateManagedEnvironmentModel(managedEnvironment: ManagedEnvironment): ManagedEnvironmentModel {
Expand Down
6 changes: 3 additions & 3 deletions src/tree/scaling/ScaleItem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/

import { KnownActiveRevisionsMode, type Revision, type Scale } from "@azure/arm-appcontainers";
import { createGenericElement, nonNullValueAndProp } from "@microsoft/vscode-azext-utils";
import { createGenericElement } from "@microsoft/vscode-azext-utils";
import { type AzureSubscription, type ViewPropertiesModel } from "@microsoft/vscode-azureresources-api";
import deepEqual from 'deep-eql';
import { ThemeIcon, TreeItemCollapsibleState, type TreeItem } from "vscode";
Expand Down Expand Up @@ -50,12 +50,12 @@ export class ScaleItem extends RevisionDraftDescendantBase {

protected setProperties(): void {
this.label = scaling;
this.scale = nonNullValueAndProp(this.parentResource.template, 'scale');
this.scale = this.parentResource.template?.scale ?? {};
}

protected setDraftProperties(): void {
this.label = `${scaling}*`;
this.scale = nonNullValueAndProp(ext.revisionDraftFileSystem.parseRevisionDraft(this), 'scale');
this.scale = ext.revisionDraftFileSystem.parseRevisionDraft(this)?.scale ?? {};
}

getTreeItem(): TreeItem {
Expand Down
19 changes: 19 additions & 0 deletions src/utils/azureClients.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,25 @@ export async function createContainerAppsAPIClient(context: AzExtClientContext):
return createAzureClient(context, (await import('@azure/arm-appcontainers')).ContainerAppsAPIClient as unknown as AzExtClientType<ContainerAppsAPIClient>);
}

const previewApiVersion = '2026-03-02-preview';

export async function createContainerAppsPreviewAPIClient(context: AzExtClientContext): Promise<ContainerAppsAPIClient> {
const client = await createContainerAppsAPIClient(context);
client.apiVersion = previewApiVersion;
client.pipeline.addPolicy({
name: 'PreviewApiVersionPolicy',
sendRequest(request, next) {
const [base, query] = request.url.split('?');
if (query) {
const newQuery = query.split('&').map(p => p.startsWith('api-version=') ? `api-version=${previewApiVersion}` : p).join('&');
request.url = `${base}?${newQuery}`;
}
return next(request);
},
});
return client;
}

export async function createContainerRegistryManagementClient(context: AzExtClientContext): Promise<ContainerRegistryManagementClient> {
return createAzureClient(context, (await import('@azure/arm-containerregistry')).ContainerRegistryManagementClient);
}
Expand Down
Loading