Skip to content

Commit

Permalink
feat: list tenant when user clicks switch tenant button (#12580)
Browse files Browse the repository at this point in the history
* feat: add switch tenant command in context item

* feat: add switch tenant context button for Azure account

* refactor: use dynamic option item instead of static to avoid silent long time loading

* test: add ut

* refactor: update error and related msg

* test: add ut to raise code coverage
  • Loading branch information
HuihuiWu-Microsoft authored Oct 24, 2024
1 parent d1fdfb5 commit 57c8910
Show file tree
Hide file tree
Showing 6 changed files with 338 additions and 4 deletions.
36 changes: 32 additions & 4 deletions packages/vscode-extension/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -336,20 +336,30 @@
}
],
"view/item/context": [
{
"command": "fx-extension.m365SwitchTenant",
"when": "fx-extension.isMultiTenantEnabled && view == teamsfx-accounts && viewItem == signedinM365",
"group": "inline@1"
},
{
"command": "fx-extension.azureSwitchTenant",
"when": "fx-extension.isMultiTenantEnabled && view == teamsfx-accounts && viewItem == signedinAzure",
"group": "inline@1"
},
{
"command": "fx-extension.signOut",
"when": "view == teamsfx-accounts && viewItem == signedinM365",
"group": "inline@1"
"group": "inline@2"
},
{
"command": "fx-extension.azureAccountSignOutHelp",
"when": "view == teamsfx-accounts && viewItem == signedinAzure",
"group": "inline@1"
"group": "inline@2"
},
{
"command": "fx-extension.m365AccountSettings",
"when": "view == teamsfx-accounts && viewItem == signedinM365",
"group": "inline@2"
"group": "inline@3"
},
{
"command": "fx-extension.refreshSideloading",
Expand All @@ -374,7 +384,7 @@
{
"command": "fx-extension.azureAccountSettings",
"when": "view == teamsfx-accounts && viewItem == signedinAzure",
"group": "inline@2"
"group": "inline@3"
},
{
"command": "fx-extension.openDocumentLink",
Expand Down Expand Up @@ -570,6 +580,14 @@
"command": "fx-extension.selectAndDebug",
"when": "false"
},
{
"command": "fx-extension.m365SwitchTenant",
"when": "false"
},
{
"command": "fx-extension.azureSwitchTenant",
"when": "false"
},
{
"command": "fx-extension.signOut",
"when": "false"
Expand Down Expand Up @@ -891,6 +909,16 @@
"title": "%teamstoolkit.commands.checkCopilotAccess%",
"icon": "$(info)"
},
{
"command": "fx-extension.m365SwitchTenant",
"title": "%teamstoolkit.commands.switchTenant.m365.title%",
"icon": "$(arrow-swap)"
},
{
"command": "fx-extension.azureSwitchTenant",
"title": "%teamstoolkit.commands.switchTenant.azure.title%",
"icon": "$(arrow-swap)"
},
{
"command": "fx-extension.signOut",
"title": "%teamstoolkit.commands.signOut.title%",
Expand Down
4 changes: 4 additions & 0 deletions packages/vscode-extension/package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@
"teamstoolkit.commands.refresh.title": "Refresh",
"teamstoolkit.commands.reportIssue.title": "Report Issues on GitHub",
"teamstoolkit.commands.selectTutorials.title": "View How-to Guides",
"teamstoolkit.commands.switchTenant.m365.title": "Switch between your available tenants for Microsoft 365 account",
"teamstoolkit.commands.switchTenant.azure.title": "Switch between your available tenants for Azure account",
"teamstoolkit.commands.signOut.title": "Sign Out",
"teamstoolkit.commands.checkCopilotAccess.title": "Check Copilot Access",
"teamstoolkit.commands.updateManifest.title": "Update Teams App",
Expand Down Expand Up @@ -213,6 +215,8 @@
"teamstoolkit.envTree.subscriptionTooltipWithoutName": "'%s' environment is provisioned in Azure subscription '%s'",
"teamstoolkit.handlers.azureSignIn": "Successfully signed in to Azure account.",
"teamstoolkit.handlers.azureSignOut": "Successfully signed out of Azure account.",
"teamstoolkit.handlers.switchtenant.quickpick.title": "Switch Tenant",
"teamstoolkit.handlers.switchtenant.error": "Unable to obtain your Azure credentials. Make sure your Azure account is properly authenticated and try again",
"teamstoolkit.handlers.coreNotReady": "Core module is loading",
"teamstoolkit.handlers.createProjectNotification": "Create a new app or open an existing one to open the README file.",
"teamstoolkit.handlers.createProjectTitle": "Create New App",
Expand Down
19 changes: 19 additions & 0 deletions packages/vscode-extension/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ import { ExtensionSurvey } from "./utils/survey";
import { getSettingsVersion, projectVersionCheck } from "./utils/telemetryUtils";
import { createPluginWithManifest } from "./handlers/createPluginWithManifestHandler";
import { manifestListener } from "./manifestListener";
import { onSwitchAzureTenant, onSwitchM365Tenant } from "./handlers/accounts/switchTenantHandler";

export async function activate(context: vscode.ExtensionContext) {
const value = IsChatParticipantEnabled && semver.gte(vscode.version, "1.90.0");
Expand Down Expand Up @@ -974,9 +975,27 @@ function registerAccountMenuCommands(context: vscode.ExtensionContext) {
}
})
);

const m365SwitchTenant = vscode.commands.registerCommand(
"fx-extension.m365SwitchTenant",
(...args) => Correlator.run(onSwitchM365Tenant, [TelemetryTriggerFrom.SideBar])
);
context.subscriptions.push(m365SwitchTenant);

const azureSwitchTenant = vscode.commands.registerCommand(
"fx-extension.azureSwitchTenant",
(...args) => Correlator.run(onSwitchAzureTenant, [TelemetryTriggerFrom.SideBar])
);
context.subscriptions.push(azureSwitchTenant);
}

async function initializeContextKey(context: vscode.ExtensionContext, isTeamsFxProject: boolean) {
await vscode.commands.executeCommand(
"setContext",
"fx-extension.isMultiTenantEnabled",
featureFlagManager.getBooleanValue(CoreFeatureFlags.MultiTenant)
);

await vscode.commands.executeCommand("setContext", "fx-extension.isSPFx", isSPFxProject);

await vscode.commands.executeCommand(
Expand Down
113 changes: 113 additions & 0 deletions packages/vscode-extension/src/handlers/accounts/switchTenantHandler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

import { listAllTenants } from "@microsoft/teamsfx-core/build/common/tools";
import { ExtTelemetry } from "../../telemetry/extTelemetry";
import { AccountType, TelemetryEvent, TelemetryProperty } from "../../telemetry/extTelemetryEvents";
import { getTriggerFromProperty } from "../../utils/telemetryUtils";
import M365TokenInstance from "../../commonlib/m365Login";
import azureAccountManager from "../../commonlib/azureLogin";
import { AzureScopes, isUserCancelError } from "@microsoft/teamsfx-core";
import { FxError, SingleSelectConfig, SystemError } from "@microsoft/teamsfx-api";
import { localize } from "../../utils/localizeUtils";
import { VS_CODE_UI } from "../../qm/vsc_ui";
import { ExtensionSource } from "../../error/error";
import { showError } from "../../error/common";

export async function onSwitchM365Tenant(...args: unknown[]): Promise<void> {
ExtTelemetry.sendTelemetryEvent(TelemetryEvent.SwitchTenantStart, {
[TelemetryProperty.AccountType]: AccountType.M365,
...getTriggerFromProperty(args),
});

let error: FxError | undefined = undefined;
const tokenRes = await M365TokenInstance.getAccessToken({
scopes: AzureScopes,
});
if (tokenRes.isOk()) {
const config: SingleSelectConfig = {
name: "SwitchTenant",
title: localize("teamstoolkit.handlers.switchtenant.quickpick.title"),
options: async () => {
const tenants = await listAllTenants(tokenRes.value);
return tenants.map((tenant: any) => {
return {
id: tenant.tenantId,
label: tenant.displayName,
description: tenant.defaultDomain,
};
});
},
};
const result = await VS_CODE_UI.selectOption(config);
if (result.isOk()) {
// TODO: set tenant
ExtTelemetry.sendTelemetryEvent(TelemetryEvent.SwitchTenant, {
[TelemetryProperty.AccountType]: AccountType.M365,
...getTriggerFromProperty(args),
});
return;
} else {
error = result.error;
}
} else {
error = tokenRes.error;
}

if (!isUserCancelError(error)) {
void showError(error);
}
ExtTelemetry.sendTelemetryErrorEvent(TelemetryEvent.SwitchTenant, error, {
[TelemetryProperty.AccountType]: AccountType.M365,
...getTriggerFromProperty(args),
});
}

export async function onSwitchAzureTenant(...args: unknown[]): Promise<void> {
ExtTelemetry.sendTelemetryEvent(TelemetryEvent.SwitchTenantStart, {
[TelemetryProperty.AccountType]: AccountType.Azure,
...getTriggerFromProperty(args),
});

const config: SingleSelectConfig = {
name: "SwitchTenant",
title: localize("teamstoolkit.handlers.switchtenant.quickpick.title"),
options: async () => {
const tokenCredential = await azureAccountManager.getIdentityCredentialAsync(false);
const token = tokenCredential ? await tokenCredential.getToken(AzureScopes) : undefined;
if (token && token.token) {
const tenants = await listAllTenants(token.token);
return tenants.map((tenant: any) => {
return {
id: tenant.tenantId,
label: tenant.displayName,
description: tenant.defaultDomain,
};
});
} else {
throw new SystemError(
ExtensionSource,
"SwitchTenantFailed",
localize("teamstoolkit.handlers.switchtenant.error")
);
}
},
};
const result = await VS_CODE_UI.selectOption(config);
if (result.isOk()) {
// TODO: set tenant
ExtTelemetry.sendTelemetryEvent(TelemetryEvent.SwitchTenant, {
[TelemetryProperty.AccountType]: AccountType.Azure,
...getTriggerFromProperty(args),
});
return;
} else {
if (!isUserCancelError(result.error)) {
void showError(result.error);
}
ExtTelemetry.sendTelemetryErrorEvent(TelemetryEvent.SwitchTenant, result.error, {
[TelemetryProperty.AccountType]: AccountType.Azure,
...getTriggerFromProperty(args),
});
}
}
3 changes: 3 additions & 0 deletions packages/vscode-extension/src/telemetry/extTelemetryEvents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ export enum TelemetryEvent {
SignOutStart = "sign-out-start",
SignOut = "sign-out",

SwitchTenantStart = "switch-tenant-start",
SwitchTenant = "switch-tenant",

SelectSubscription = "select-subscription",

CreateProjectStart = "create-project-start",
Expand Down
Loading

0 comments on commit 57c8910

Please sign in to comment.