Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for domain name label scopes #2703

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
164 changes: 52 additions & 112 deletions package-lock.json

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -773,15 +773,15 @@
"webpack-cli": "^4.6.0"
},
"dependencies": {
"@azure/arm-appservice": "^13.0.2",
"@azure/arm-appservice": "^15.0.0",
"@azure/arm-resources": "^5.0.0",
"@azure/core-client": "^1.7.3",
"@azure/core-rest-pipeline": "^1.11.0",
"@microsoft/vscode-azext-azureappservice": "^3.1.0",
"@microsoft/vscode-azext-azureappservice": "file:../vscode-azuretools/appservice/microsoft-vscode-azext-azureappservice-3.3.1.tgz",
Copy link
Contributor Author

@MicroFish91 MicroFish91 Feb 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll clean up the package.json and package-lock.json after these packages are ready to be imported through npm registry.

"@microsoft/vscode-azext-azureappsettings": "^0.2.0",
"@microsoft/vscode-azext-azureutils": "^3.1.1",
"@microsoft/vscode-azext-utils": "^2.5.7",
"@microsoft/vscode-azureresources-api": "^2.1.0",
"@microsoft/vscode-azext-utils": "file:../vscode-azuretools/utils/microsoft-vscode-azext-utils-2.5.13.tgz",
"@microsoft/vscode-azureresources-api": "^2.3.2",
"dotenv": "^6.2.0",
"fast-xml-parser": "^4.4.1",
"fs-extra": "^8.0.0",
Expand Down
17 changes: 15 additions & 2 deletions src/WebAppResolver.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { type Site } from "@azure/arm-appservice";
import { uiUtils } from "@microsoft/vscode-azext-azureutils";
import { callWithTelemetryAndErrorHandling, nonNullProp, nonNullValue, type IActionContext, type ISubscriptionContext } from "@microsoft/vscode-azext-utils";
import { callWithTelemetryAndErrorHandling, nonNullProp, nonNullValue, nonNullValueAndProp, type IActionContext, type ISubscriptionContext } from "@microsoft/vscode-azext-utils";
import { type AppResource, type AppResourceResolver } from "@microsoft/vscode-azext-utils/hostapi";
import { ResolvedWebAppResource } from "./tree/ResolvedWebAppResource";
import { createWebSiteClient } from "./utils/azureClients";
Expand All @@ -9,6 +9,7 @@ export class WebAppResolver implements AppResourceResolver {

private siteCacheLastUpdated = 0;
private siteCache: Map<string, Site> = new Map<string, Site>();
private siteNameCounter: Map<string, number> = new Map<string, number>();
private listWebAppsTask: Promise<void> | undefined;

public async resolveResource(subContext: ISubscriptionContext, resource: AppResource): Promise<ResolvedWebAppResource | undefined> {
Expand All @@ -17,10 +18,17 @@ export class WebAppResolver implements AppResourceResolver {

if (this.siteCacheLastUpdated < Date.now() - 1000 * 3) {
this.siteCacheLastUpdated = Date.now();

this.listWebAppsTask = new Promise((resolve, reject) => {
this.siteCache.clear();
this.siteNameCounter.clear();

uiUtils.listAllIterator(client.webApps.list()).then((sites) => {
for (const site of sites) {
const siteName: string = nonNullProp(site, 'name');
Copy link
Contributor Author

@MicroFish91 MicroFish91 Feb 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adds a lookahead that checks for duplicate site names and adds a location description in order to help differentiate them, example:

image

const count: number = (this.siteNameCounter.get(siteName) ?? 0) + 1;

this.siteNameCounter.set(siteName, count);
this.siteCache.set(nonNullProp(site, 'id').toLowerCase(), site);
}
resolve();
Expand All @@ -33,7 +41,12 @@ export class WebAppResolver implements AppResourceResolver {

await this.listWebAppsTask;
const site = this.siteCache.get(nonNullProp(resource, 'id').toLowerCase());
return new ResolvedWebAppResource(subContext, nonNullValue(site));

return new ResolvedWebAppResource(subContext, nonNullValue(site), {
// Multiple sites with the same name could be displayed as long as they are in different locations
// To help distinguish these apps for our users, lookahead and determine if the location should be provided for duplicated site names
showLocationAsTreeItemDescription: (this.siteNameCounter.get(nonNullValueAndProp(site, 'name')) ?? 1) > 1,
});
});
}

Expand Down
4 changes: 2 additions & 2 deletions src/commands/createWebApp/IWebAppWizardContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/

import { type IAppServiceWizardContext } from '@microsoft/vscode-azext-azureappservice';
import { type ExecuteActivityContext } from '@microsoft/vscode-azext-utils';
import { type ExecuteActivityContext, type ISubscriptionActionContext } from '@microsoft/vscode-azext-utils';
import { type AppStackMajorVersion, type AppStackMinorVersion } from './stacks/models/AppStackModel';
import { type JavaContainers, type WebAppRuntimes, type WebAppStack, type WebAppStackValue } from './stacks/models/WebAppStackModel';

Expand All @@ -20,7 +20,7 @@ export type FullJavaStack = {
minorVersion: AppStackMinorVersion<JavaContainers>;
};

export interface IWebAppWizardContext extends IAppServiceWizardContext, ExecuteActivityContext {
export interface IWebAppWizardContext extends ISubscriptionActionContext, IAppServiceWizardContext, ExecuteActivityContext {
newSiteRuntime?: string;

usingBackupStacks?: boolean;
Expand Down
22 changes: 16 additions & 6 deletions src/commands/createWebApp/createWebApp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { AppInsightsCreateStep, AppInsightsListStep, AppKind, AppServicePlanCreateStep, AppServicePlanListStep, AppServicePlanSkuStep, CustomLocationListStep, LogAnalyticsCreateStep, ParsedSite, setLocationsTask, SiteNameStep } from "@microsoft/vscode-azext-azureappservice";
import { AppInsightsCreateStep, AppInsightsListStep, AppKind, AppServicePlanCreateStep, AppServicePlanListStep, AppServicePlanSkuStep, CustomLocationListStep, DomainNameLabelScope, LogAnalyticsCreateStep, ParsedSite, setLocationsTask, SiteDomainNameLabelScopeStep, SiteNameStep } from "@microsoft/vscode-azext-azureappservice";
import { LocationListStep, ResourceGroupCreateStep, ResourceGroupListStep, SubscriptionTreeItemBase, VerifyProvidersStep } from "@microsoft/vscode-azext-azureutils";
import { AzureWizard, maskUserInfo, nonNullProp, parseError, type AzExtParentTreeItem, type AzureWizardExecuteStep, type AzureWizardPromptStep, type IActionContext, type ICreateChildImplContext } from "@microsoft/vscode-azext-utils";
import { webProvider } from "../../constants";
import { ext } from "../../extensionVariables";
import { localize } from "../../localize";
import { SiteTreeItem } from "../../tree/SiteTreeItem";
import { createActivityContext } from "../../utils/activityUtils";
import { WebAppWithDomainLabelScopeCreateStep } from "./domainLabelScope/WebAppWithDomainLabelScopeCreateStep";
import { type IWebAppWizardContext } from "./IWebAppWizardContext";
import { SetPostPromptDefaultsStep } from "./SetPostPromptDefaultsStep";
import { setPrePromptDefaults } from "./setPrePromptDefaults";
Expand Down Expand Up @@ -43,19 +44,25 @@ export async function createWebApp(context: IActionContext & Partial<ICreateChil

const promptSteps: AzureWizardPromptStep<IWebAppWizardContext>[] = [];
const executeSteps: AzureWizardExecuteStep<IWebAppWizardContext>[] = [];

// Add these steps to the front because we need this information for checking site name availability
LocationListStep.addStep(wizardContext, promptSteps);
promptSteps.push(new SiteDomainNameLabelScopeStep());
if (context.advancedCreation) {
promptSteps.push(new ResourceGroupListStep());
}

const siteStep: SiteNameStep = new SiteNameStep();
promptSteps.push(siteStep);

if (context.advancedCreation) {
promptSteps.push(new ResourceGroupListStep());
promptSteps.push(new WebAppStackStep());
CustomLocationListStep.addStep(wizardContext, promptSteps);
promptSteps.push(new AppServicePlanListStep());
promptSteps.push(new AppInsightsListStep());
} else {
promptSteps.push(new WebAppStackStep());
promptSteps.push(new AppServicePlanSkuStep());
LocationListStep.addStep(wizardContext, promptSteps);
executeSteps.push(new ResourceGroupCreateStep());
executeSteps.push(new AppServicePlanCreateStep());
executeSteps.push(new AppInsightsCreateStep());
Expand All @@ -64,7 +71,6 @@ export async function createWebApp(context: IActionContext & Partial<ICreateChil

executeSteps.push(new VerifyProvidersStep([webProvider, 'Microsoft.Insights']));
executeSteps.push(new LogAnalyticsCreateStep());
executeSteps.push(new WebAppCreateStep());

if (wizardContext.newSiteOS !== undefined) {
await setLocationsTask(wizardContext);
Expand All @@ -76,14 +82,18 @@ export async function createWebApp(context: IActionContext & Partial<ICreateChil
await wizard.prompt();

const newSiteName = nonNullProp(wizardContext, 'newSiteName');

wizardContext.activityTitle = localize('createWebApp', 'Create Web App "{0}"', newSiteName);

if (wizardContext.newSiteDomainNameLabelScope === DomainNameLabelScope.Global) {
executeSteps.push(new WebAppCreateStep());
} else {
executeSteps.push(new WebAppWithDomainLabelScopeCreateStep());
}

await wizard.execute();
await ext.rgApi.appResourceTree.refresh(context);

const rawSite = nonNullProp(wizardContext, 'site');
// site is set as a result of SiteCreateStep.execute()
const site = new ParsedSite(rawSite, wizardContext);
ext.outputChannel.appendLog(getCreatedWebAppMessage(site));

Expand Down
4 changes: 4 additions & 0 deletions src/commands/createWebApp/domainLabelScope/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
## Domain Label Scope

The files in this folder are intended to be temporary because the App Service SDK does not currently support the required API version we need. Consequently, we needed to implement a custom call with custom types. In the future, we expect to be able to remove this folder and consolidate everything into a single web app creation file.

Loading
Loading