diff --git a/packages/fx-core/src/common/featureFlags.ts b/packages/fx-core/src/common/featureFlags.ts index 1d2e10943a..07fb0dde6f 100644 --- a/packages/fx-core/src/common/featureFlags.ts +++ b/packages/fx-core/src/common/featureFlags.ts @@ -12,42 +12,6 @@ export function isFeatureFlagEnabled(featureFlagName: string, defaultValue = fal } } -export function isCLIDotNetEnabled(): boolean { - return featureFlagManager.getBooleanValue(FeatureFlags.CLIDotNet); -} - -export function enableTestToolByDefault(): boolean { - return featureFlagManager.getBooleanValue(FeatureFlags.TestTool); -} - -export function enableMETestToolByDefault(): boolean { - return featureFlagManager.getBooleanValue(FeatureFlags.METestTool); -} - -export function isNewGeneratorEnabled(): boolean { - return featureFlagManager.getBooleanValue(FeatureFlags.NewGenerator); -} - -export function isOfficeJSONAddinEnabled(): boolean { - return featureFlagManager.getBooleanValue(FeatureFlags.OfficeAddin); -} - -export function isTdpTemplateCliTestEnabled(): boolean { - return featureFlagManager.getBooleanValue(FeatureFlags.TdpTemplateCliTest); -} - -export function isAsyncAppValidationEnabled(): boolean { - return featureFlagManager.getBooleanValue(FeatureFlags.AsyncAppValidation); -} - -export function isNewProjectTypeEnabled(): boolean { - return featureFlagManager.getBooleanValue(FeatureFlags.NewProjectType); -} - -export function isChatParticipantEnabled(): boolean { - return featureFlagManager.getBooleanValue(FeatureFlags.ChatParticipant); -} - /////////////////////////////////////////////////////////////////////////////// // Notes for Office Addin Feature flags: // Case 1: TEAMSFX_OFFICE_ADDIN = false, TEAMSFX_OFFICE_XML_ADDIN = false diff --git a/packages/fx-core/src/component/coordinator/index.ts b/packages/fx-core/src/component/coordinator/index.ts index e1a2771ff4..548b28b009 100644 --- a/packages/fx-core/src/component/coordinator/index.ts +++ b/packages/fx-core/src/component/coordinator/index.ts @@ -24,7 +24,7 @@ import * as path from "path"; import * as uuid from "uuid"; import * as xml2js from "xml2js"; import { AppStudioScopes, getResourceGroupInPortal } from "../../common/constants"; -import { FeatureFlags, featureFlagManager, isNewGeneratorEnabled } from "../../common/featureFlags"; +import { FeatureFlags, featureFlagManager } from "../../common/featureFlags"; import { ErrorContextMW, globalVars } from "../../common/globalVars"; import { getLocalizedString } from "../../common/localizeUtils"; import { convertToAlphanumericOnly } from "../../common/stringUtils"; @@ -174,7 +174,7 @@ class Coordinator { }); } - if (isNewGeneratorEnabled()) { + if (featureFlagManager.getBooleanValue(FeatureFlags.NewGenerator)) { // refactored generator const generator = Generators.find((g) => g.activate(context, inputs)); if (!generator) { diff --git a/packages/fx-core/src/component/generator/generator.ts b/packages/fx-core/src/component/generator/generator.ts index 096ceacb32..7a96ceee0e 100644 --- a/packages/fx-core/src/component/generator/generator.ts +++ b/packages/fx-core/src/component/generator/generator.ts @@ -34,11 +34,7 @@ import { renderTemplateFileData, renderTemplateFileName, } from "./utils"; -import { - enableMETestToolByDefault, - enableTestToolByDefault, - isNewProjectTypeEnabled, -} from "../../common/featureFlags"; +import { featureFlagManager, FeatureFlags } from "../../common/featureFlags"; import { Utils } from "@microsoft/m365-spec-parser"; import { convertToAlphanumericOnly } from "../../common/stringUtils"; @@ -80,15 +76,21 @@ export class Generator { ApiSpecPath: authData?.openapiSpecPath ?? "", ApiKey: authData?.authType === "apiKey" ? "true" : "", OAuth: authData?.authType === "oauth2" ? "true" : "", - enableTestToolByDefault: enableTestToolByDefault() ? "true" : "", - enableMETestToolByDefault: enableMETestToolByDefault() ? "true" : "", + enableTestToolByDefault: featureFlagManager.getBooleanValue(FeatureFlags.TestTool) + ? "true" + : "", + enableMETestToolByDefault: featureFlagManager.getBooleanValue(FeatureFlags.METestTool) + ? "true" + : "", useOpenAI: llmServiceData?.llmService === "llm-service-openai" ? "true" : "", useAzureOpenAI: llmServiceData?.llmService === "llm-service-azure-openai" ? "true" : "", openAIKey: llmServiceData?.openAIKey ?? "", azureOpenAIKey: llmServiceData?.azureOpenAIKey ?? "", azureOpenAIEndpoint: llmServiceData?.azureOpenAIEndpoint ?? "", azureOpenAIDeploymentName: llmServiceData?.azureOpenAIDeploymentName ?? "", - isNewProjectTypeEnabled: isNewProjectTypeEnabled() ? "true" : "", + isNewProjectTypeEnabled: featureFlagManager.getBooleanValue(FeatureFlags.NewProjectType) + ? "true" + : "", NewProjectTypeName: process.env.TEAMSFX_NEW_PROJECT_TYPE_NAME ?? "TeamsApp", NewProjectTypeExt: process.env.TEAMSFX_NEW_PROJECT_TYPE_EXTENSION ?? "ttkproj", }; diff --git a/packages/fx-core/src/component/generator/templates/templateReplaceMap.ts b/packages/fx-core/src/component/generator/templates/templateReplaceMap.ts index 00c21104b5..f483086358 100644 --- a/packages/fx-core/src/component/generator/templates/templateReplaceMap.ts +++ b/packages/fx-core/src/component/generator/templates/templateReplaceMap.ts @@ -1,11 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. import { Inputs } from "@microsoft/teamsfx-api"; -import { - enableMETestToolByDefault, - enableTestToolByDefault, - isNewProjectTypeEnabled, -} from "../../../common/featureFlags"; +import { featureFlagManager, FeatureFlags } from "../../../common/featureFlags"; import { convertToAlphanumericOnly } from "../../../common/stringUtils"; import { QuestionNames } from "../../../question/constants"; @@ -29,15 +25,21 @@ export function getTemplateReplaceMap(inputs: Inputs): { [key: string]: string } PlaceProjectFileInSolutionDir: placeProjectFileInSolutionDir ? "true" : "", SafeProjectName: safeProjectName, SafeProjectNameLowerCase: safeProjectName.toLocaleLowerCase(), - enableTestToolByDefault: enableTestToolByDefault() ? "true" : "", - enableMETestToolByDefault: enableMETestToolByDefault() ? "true" : "", + enableTestToolByDefault: featureFlagManager.getBooleanValue(FeatureFlags.TestTool) + ? "true" + : "", + enableMETestToolByDefault: featureFlagManager.getBooleanValue(FeatureFlags.METestTool) + ? "true" + : "", useOpenAI: llmService === "llm-service-openai" ? "true" : "", useAzureOpenAI: llmService === "llm-service-azure-openai" ? "true" : "", openAIKey: openAIKey ?? "", azureOpenAIKey: azureOpenAIKey ?? "", azureOpenAIEndpoint: azureOpenAIEndpoint ?? "", azureOpenAIDeploymentName: azureOpenAIDeploymentName ?? "", - isNewProjectTypeEnabled: isNewProjectTypeEnabled() ? "true" : "", + isNewProjectTypeEnabled: featureFlagManager.getBooleanValue(FeatureFlags.NewProjectType) + ? "true" + : "", NewProjectTypeName: process.env.TEAMSFX_NEW_PROJECT_TYPE_NAME ?? "TeamsApp", NewProjectTypeExt: process.env.TEAMSFX_NEW_PROJECT_TYPE_EXTENSION ?? "ttkproj", }; diff --git a/packages/fx-core/src/index.ts b/packages/fx-core/src/index.ts index 8bf54cfb71..3eb5f4e21b 100644 --- a/packages/fx-core/src/index.ts +++ b/packages/fx-core/src/index.ts @@ -25,12 +25,7 @@ export { getAllowedAppMaps, } from "./common/constants"; export { Correlator } from "./common/correlator"; -export { - FeatureFlags, - featureFlagManager, - isChatParticipantEnabled, - isFeatureFlagEnabled, -} from "./common/featureFlags"; +export { FeatureFlags, featureFlagManager, isFeatureFlagEnabled } from "./common/featureFlags"; export { globalStateGet, globalStateUpdate } from "./common/globalState"; export { getDefaultString, getLocalizedString } from "./common/localizeUtils"; export * from "./common/permissionInterface"; diff --git a/packages/fx-core/src/question/constants.ts b/packages/fx-core/src/question/constants.ts index 41c1365119..1c229bf475 100644 --- a/packages/fx-core/src/question/constants.ts +++ b/packages/fx-core/src/question/constants.ts @@ -2,13 +2,7 @@ // Licensed under the MIT license. import { Inputs, OptionItem, Platform } from "@microsoft/teamsfx-api"; -import { - FeatureFlags, - featureFlagManager, - isCLIDotNetEnabled, - isChatParticipantEnabled, - isTdpTemplateCliTestEnabled, -} from "../common/featureFlags"; +import { FeatureFlags, featureFlagManager } from "../common/featureFlags"; import { getLocalizedString } from "../common/localizeUtils"; import { OfficeAddinProjectConfig } from "../component/generator/officeXMLAddin/projectConfig"; @@ -137,7 +131,7 @@ export class RuntimeOptions { export function getRuntime(inputs: Inputs): string { let runtime = RuntimeOptions.NodeJS().id; - if (isCLIDotNetEnabled()) { + if (featureFlagManager.getBooleanValue(FeatureFlags.CLIDotNet)) { runtime = inputs[QuestionNames.Runtime] || runtime; } else { if (inputs?.platform === Platform.VS) { @@ -169,7 +163,7 @@ export class ScratchOptions { export class ProjectTypeOptions { static getCreateGroupName(): string | undefined { - return isChatParticipantEnabled() + return featureFlagManager.getBooleanValue(FeatureFlags.ChatParticipant) ? getLocalizedString("core.createProjectQuestion.projectType.createGroup.title") : undefined; } @@ -518,7 +512,7 @@ export class CapabilityOptions { CapabilityOptions.tab(), ...CapabilityOptions.collectMECaps(), ]; - if (isTdpTemplateCliTestEnabled()) { + if (featureFlagManager.getBooleanValue(FeatureFlags.TdpTemplateCliTest)) { capabilities.push(CapabilityOptions.me()); } @@ -669,7 +663,7 @@ export class CapabilityOptions { capabilityOptions.push(...CapabilityOptions.customizeGptOptions()); } capabilityOptions.push(...CapabilityOptions.customCopilots()); - if (isTdpTemplateCliTestEnabled()) { + if (featureFlagManager.getBooleanValue(FeatureFlags.TdpTemplateCliTest)) { // test templates that are used by TDP integration only capabilityOptions.push(...CapabilityOptions.tdpIntegrationCapabilities()); } diff --git a/packages/fx-core/src/question/create.ts b/packages/fx-core/src/question/create.ts index fc5d4ae7a8..bc0343d69b 100644 --- a/packages/fx-core/src/question/create.ts +++ b/packages/fx-core/src/question/create.ts @@ -23,13 +23,7 @@ import * as os from "os"; import * as path from "path"; import { ConstantString } from "../common/constants"; import { Correlator } from "../common/correlator"; -import { - FeatureFlags, - featureFlagManager, - isCLIDotNetEnabled, - isChatParticipantEnabled, - isOfficeJSONAddinEnabled, -} from "../common/featureFlags"; +import { FeatureFlags, featureFlagManager } from "../common/featureFlags"; import { createContext } from "../common/globalVars"; import { getLocalizedString } from "../common/localizeUtils"; import { sampleProvider } from "../common/samples"; @@ -119,7 +113,7 @@ export function projectTypeQuestion(): SingleSelectQuestion { //only for @office agent, officeXMLAddin are supported staticOptions.push(ProjectTypeOptions.officeXMLAddin(inputs.platform)); } else { - if (isOfficeJSONAddinEnabled()) { + if (featureFlagManager.getBooleanValue(FeatureFlags.OfficeAddin)) { staticOptions.push(ProjectTypeOptions.officeAddin(inputs.platform)); } else { staticOptions.push(ProjectTypeOptions.outlookAddin(inputs.platform)); @@ -129,7 +123,7 @@ export function projectTypeQuestion(): SingleSelectQuestion { if ( inputs.platform === Platform.VSCode && - isChatParticipantEnabled() && + featureFlagManager.getBooleanValue(FeatureFlags.ChatParticipant) && !inputs.teamsAppFromTdp ) { staticOptions.push(ProjectTypeOptions.startWithGithubCopilot()); @@ -1524,7 +1518,8 @@ export function createProjectQuestionNode(): IQTreeNode { children: [ { condition: (inputs: Inputs) => - isCLIDotNetEnabled() && CLIPlatforms.includes(inputs.platform), + featureFlagManager.getBooleanValue(FeatureFlags.CLIDotNet) && + CLIPlatforms.includes(inputs.platform), data: runtimeQuestion(), }, { @@ -1595,7 +1590,7 @@ export function createProjectCliHelpNode(): IQTreeNode { QuestionNames.ReplaceBotIds, QuestionNames.Samples, ]; - if (!isCLIDotNetEnabled()) { + if (!featureFlagManager.getBooleanValue(FeatureFlags.CLIDotNet)) { deleteNames.push(QuestionNames.Runtime); } trimQuestionTreeForCliHelp(node, deleteNames); diff --git a/packages/fx-core/tests/component/coordinator/coordinator.create.test.ts b/packages/fx-core/tests/component/coordinator/coordinator.create.test.ts index 1c58c4aced..d5847ae04b 100644 --- a/packages/fx-core/tests/component/coordinator/coordinator.create.test.ts +++ b/packages/fx-core/tests/component/coordinator/coordinator.create.test.ts @@ -4,9 +4,7 @@ import { err, Inputs, ok, Platform, SystemError, UserError } from "@microsoft/te import { assert } from "chai"; import fs from "fs-extra"; import { glob } from "glob"; -import mockedEnv, { RestoreFn } from "mocked-env"; import * as sinon from "sinon"; -import * as FeatureFlags from "../../../src/common/featureFlags"; import { createContext, setTools } from "../../../src/common/globalVars"; import { MetadataV3 } from "../../../src/common/versionMetadata"; import { coordinator } from "../../../src/component/coordinator"; @@ -25,6 +23,7 @@ import { TemplateNames } from "../../../src/component/generator/templates/templa import { settingsUtil } from "../../../src/component/utils/settingsUtil"; import { FxCore } from "../../../src/core/FxCore"; import { InputValidationError, MissingRequiredInputError } from "../../../src/error/common"; +import { CreateSampleProjectInputs } from "../../../src/question"; import { ApiAuthOptions, CapabilityOptions, @@ -36,23 +35,23 @@ import { QuestionNames, ScratchOptions, } from "../../../src/question/constants"; +import { validationUtils } from "../../../src/ui/validationUtils"; import { MockTools, randomAppName } from "../../core/utils"; import { MockedUserInteraction } from "../../plugins/solution/util"; -import { CreateSampleProjectInputs } from "../../../src/question"; -import { validationUtils } from "../../../src/ui/validationUtils"; +import mockedEnv, { RestoreFn } from "mocked-env"; const V3Version = MetadataV3.projectVersion; [false].forEach((newGeneratorFlag) => { - describe(`coordinator create with isNewGeneratorEnabled = ${newGeneratorFlag}`, () => { - const mockedEnvRestore: RestoreFn = () => {}; + describe(`coordinator create with new generator enabled = ${newGeneratorFlag}`, () => { + let mockedEnvRestore: RestoreFn = () => {}; const sandbox = sinon.createSandbox(); const tools = new MockTools(); let generator: sinon.SinonStub; setTools(tools); beforeEach(() => { sandbox.stub(fs, "ensureDir").resolves(); - sandbox.stub(FeatureFlags, "isNewGeneratorEnabled").returns(newGeneratorFlag); + mockedEnvRestore = mockedEnv({ TEAMSFX_NEW_GENERATOR: `${newGeneratorFlag}` }); generator = newGeneratorFlag ? sandbox .stub(DefaultTemplateGenerator.prototype, "scaffolding") @@ -939,15 +938,15 @@ const V3Version = MetadataV3.projectVersion; }); describe("Office Addin", async () => { + let mockedEnvRestore: RestoreFn = () => {}; const sandbox = sinon.createSandbox(); const tools = new MockTools(); - const mockedEnvRestore: RestoreFn = () => {}; tools.ui = new MockedUserInteraction(); setTools(tools); beforeEach(() => { sandbox.stub(fs, "ensureDir").resolves(); - sandbox.stub(FeatureFlags, "isNewGeneratorEnabled").returns(false); + mockedEnvRestore = mockedEnv({ TEAMSFX_NEW_GENERATOR: "false" }); }); afterEach(() => { @@ -1028,15 +1027,15 @@ describe("Office Addin", async () => { }); describe("Office XML Addin", async () => { + let mockedEnvRestore: RestoreFn = () => {}; const sandbox = sinon.createSandbox(); const tools = new MockTools(); - const mockedEnvRestore: RestoreFn = () => {}; tools.ui = new MockedUserInteraction(); setTools(tools); beforeEach(() => { sandbox.stub(fs, "ensureDir").resolves(); - sandbox.stub(FeatureFlags, "isNewGeneratorEnabled").returns(false); + mockedEnvRestore = mockedEnv({ TEAMSFX_NEW_GENERATOR: "false" }); }); afterEach(() => { @@ -1109,15 +1108,15 @@ describe("Office XML Addin", async () => { }); describe("Office Addin", async () => { + let mockedEnvRestore: RestoreFn = () => {}; const sandbox = sinon.createSandbox(); const tools = new MockTools(); - const mockedEnvRestore: RestoreFn = () => {}; tools.ui = new MockedUserInteraction(); setTools(tools); beforeEach(() => { sandbox.stub(fs, "ensureDir").resolves(); - sandbox.stub(FeatureFlags, "isNewGeneratorEnabled").returns(false); + mockedEnvRestore = mockedEnv({ TEAMSFX_NEW_GENERATOR: "false" }); }); afterEach(() => { @@ -1198,6 +1197,7 @@ describe("Office Addin", async () => { }); describe("Copilot plugin", async () => { + let mockedEnvRestore: RestoreFn = () => {}; const sandbox = sinon.createSandbox(); const tools = new MockTools(); tools.ui = new MockedUserInteraction(); @@ -1205,11 +1205,12 @@ describe("Copilot plugin", async () => { beforeEach(() => { sandbox.stub(fs, "ensureDir").resolves(); - sandbox.stub(FeatureFlags, "isNewGeneratorEnabled").returns(false); + mockedEnvRestore = mockedEnv({ TEAMSFX_NEW_GENERATOR: "false" }); }); afterEach(() => { sandbox.restore(); + mockedEnvRestore(); }); it("should scaffold from API spec successfully", async () => { @@ -1253,16 +1254,18 @@ describe("Copilot plugin", async () => { }); }); -describe(`coordinator create with isNewGeneratorEnabled = true`, () => { +describe(`coordinator create with new generator enabled = true`, () => { + let mockedEnvRestore: RestoreFn = () => {}; const sandbox = sinon.createSandbox(); const tools = new MockTools(); setTools(tools); beforeEach(() => { sandbox.stub(fs, "ensureDir").resolves(); - sandbox.stub(FeatureFlags, "isNewGeneratorEnabled").returns(true); + mockedEnvRestore = mockedEnv({ TEAMSFX_NEW_GENERATOR: "true" }); }); afterEach(() => { sandbox.restore(); + mockedEnvRestore(); }); it("should scaffold by OfficeAddinGeneratorNew successfully", async () => { diff --git a/packages/fx-core/tests/component/generator/generator.test.ts b/packages/fx-core/tests/component/generator/generator.test.ts index 443b5a66d4..516cd06262 100644 --- a/packages/fx-core/tests/component/generator/generator.test.ts +++ b/packages/fx-core/tests/component/generator/generator.test.ts @@ -544,7 +544,7 @@ describe("Generator error", async () => { [false, true].forEach((newGeneratorFlag) => { it("template fallback error", async () => { - sandbox.stub(featurefalgs, "isNewGeneratorEnabled").returns(newGeneratorFlag); + sandbox.stub(process, "env").value({ TEAMSFX_NEW_GENERATOR: `${newGeneratorFlag}` }); sandbox.stub(ScaffoldRemoteTemplateAction, "run").resolves(); sandbox.stub(folderUtils, "getTemplatesFolder").resolves("foobar"); const result = newGeneratorFlag @@ -558,7 +558,7 @@ describe("Generator error", async () => { }); it("template not found error", async () => { - sandbox.stub(featurefalgs, "isNewGeneratorEnabled").returns(newGeneratorFlag); + sandbox.stub(process, "env").value({ TEAMSFX_NEW_GENERATOR: `${newGeneratorFlag}` }); sandbox.stub(ScaffoldRemoteTemplateAction, "run").resolves(); sandbox.stub(generatorUtils, "unzip").resolves(); const result = newGeneratorFlag @@ -766,7 +766,7 @@ describe("render template", () => { }); [false, true].forEach((newGeneratorFlag) => { - describe(`Generator happy path with isNewGeneratorEnabled=${newGeneratorFlag}`, async () => { + describe(`Generator happy path with new generator enabled=${newGeneratorFlag}`, async () => { const tools = new MockTools(); setTools(tools); const context = createContext(); @@ -794,7 +794,7 @@ describe("render template", () => { [QuestionNames.ProgrammingLanguage]: ProgrammingLanguage.TS, [QuestionNames.Capabilities]: CapabilityOptions.basicBot().id, } as Inputs; - sandbox.stub(featurefalgs, "isNewGeneratorEnabled").returns(newGeneratorFlag); + sandbox.stub(process, "env").value({ TEAMSFX_NEW_GENERATOR: "true" }); }); afterEach(async () => { diff --git a/packages/fx-core/tests/question/create.test.ts b/packages/fx-core/tests/question/create.test.ts index 93d1a5cfb6..a8253998ab 100644 --- a/packages/fx-core/tests/question/create.test.ts +++ b/packages/fx-core/tests/question/create.test.ts @@ -3754,36 +3754,6 @@ describe("scaffold question", () => { assert.isDefined(officeAddinOption); } }); - it("enable isOfficeJSONAddinEnabled()", async () => { - mockedEnvRestore = mockedEnv({ - [FeatureFlagName.OfficeAddin]: "true", - }); - const question = projectTypeQuestion(); - const inputs: Inputs = { platform: Platform.CLI }; - assert.isDefined(question.dynamicOptions); - if (question.dynamicOptions) { - const options = (await question.dynamicOptions(inputs)) as OptionItem[]; - const officeAddinOption = options.find( - (o) => o.id === ProjectTypeOptions.officeAddin(inputs.platform).id - ); - assert.isDefined(officeAddinOption); - } - }); - it("disable isOfficeJSONAddinEnabled()", async () => { - mockedEnvRestore = mockedEnv({ - [FeatureFlagName.OfficeAddin]: "false", - }); - const question = projectTypeQuestion(); - const inputs: Inputs = { platform: Platform.CLI }; - assert.isDefined(question.dynamicOptions); - if (question.dynamicOptions) { - const options = (await question.dynamicOptions(inputs)) as OptionItem[]; - const officeAddinOption = options.find( - (o) => o.id === ProjectTypeOptions.outlookAddin(inputs.platform).id - ); - assert.isDefined(officeAddinOption); - } - }); it("show customize GPT if CLI and enable declarative GPT() ", async () => { mockedEnvRestore = mockedEnv({ [FeatureFlagName.CustomizeGpt]: "true", diff --git a/packages/vscode-extension/src/controls/webviewPanel.ts b/packages/vscode-extension/src/controls/webviewPanel.ts index f3e013485d..9ab3e46cdd 100644 --- a/packages/vscode-extension/src/controls/webviewPanel.ts +++ b/packages/vscode-extension/src/controls/webviewPanel.ts @@ -6,8 +6,9 @@ import * as vscode from "vscode"; import { Correlator, + FeatureFlags, SampleConfig, - isChatParticipantEnabled, + featureFlagManager, sampleProvider, } from "@microsoft/teamsfx-core"; @@ -331,7 +332,7 @@ export class WebviewPanel { vscode.Uri.joinPath(globalVariables.context.extensionUri, "out", "resource", "mermaid.min.js") ); - const allowChat = isChatParticipantEnabled(); + const allowChat = featureFlagManager.getBooleanValue(FeatureFlags.ChatParticipant); // Use a nonce to to only allow specific scripts to be run const nonce = this.getNonce(); diff --git a/packages/vscode-extension/src/extension.ts b/packages/vscode-extension/src/extension.ts index 18c2814f7c..9fd45cfe3f 100644 --- a/packages/vscode-extension/src/extension.ts +++ b/packages/vscode-extension/src/extension.ts @@ -17,11 +17,10 @@ import { import { AuthSvcScopes, Correlator, + FeatureFlags as CoreFeatureFlags, VersionState, featureFlagManager, - isChatParticipantEnabled, teamsDevPortalClient, - FeatureFlags as FxCoreFeatureFlags, } from "@microsoft/teamsfx-core"; import { @@ -55,14 +54,16 @@ import M365TokenInstance from "./commonlib/m365Login"; import { configMgr } from "./config"; import { CommandKey as CommandKeys } from "./constants"; import { openWelcomePageAfterExtensionInstallation } from "./controls/openWelcomePage"; -import * as copilotChatHandlers from "./handlers/copilotChatHandlers"; +import { TeamsFxTaskType } from "./debug/common/debugConstants"; import { getLocalDebugSessionId, startLocalDebugSession } from "./debug/common/localDebugSession"; +import { registerOfficeTaskAndDebugEvents } from "./debug/officeTaskHandler"; import { disableRunIcon, registerRunIcon } from "./debug/runIconHandler"; import { TeamsfxDebugProvider } from "./debug/teamsfxDebugProvider"; import { registerTeamsfxTaskAndDebugEvents } from "./debug/teamsfxTaskHandler"; import { TeamsfxTaskProvider } from "./debug/teamsfxTaskProvider"; import * as exp from "./exp"; import { TreatmentVariableValue, TreatmentVariables } from "./exp/treatmentVariables"; +import { FeatureFlags } from "./featureFlags"; import { initializeGlobalVariables, isExistingUser, @@ -74,6 +75,15 @@ import { workspaceUri, } from "./globalVariables"; import * as handlers from "./handlers"; +import { checkCopilotAccessHandler } from "./handlers/checkCopilotAccess"; +import { checkCopilotCallback } from "./handlers/checkCopilotCallback"; +import { checkSideloadingCallback } from "./handlers/checkSideloading"; +import * as copilotChatHandlers from "./handlers/copilotChatHandlers"; +import { debugInTestToolHandler } from "./handlers/debugInTestTool"; +import { downloadSampleApp } from "./handlers/downloadSample"; +import * as officeDevHandlers from "./handlers/officeDevHandlers"; +import { showOutputChannelHandler } from "./handlers/showOutputChannel"; +import { createProjectFromWalkthroughHandler } from "./handlers/walkthrough"; import { ManifestTemplateHoverProvider } from "./hoverProvider"; import { CHAT_CREATE_OFFICE_PROJECT_COMMAND_ID, @@ -84,7 +94,6 @@ import { handleOfficeFeedback, officeChatRequestHandler, } from "./officeChat/handlers"; -import * as officeDevHandlers from "./handlers/officeDevHandlers"; import { initVSCodeUI } from "./qm/vsc_ui"; import { ExtTelemetry } from "./telemetry/extTelemetry"; import { TelemetryEvent, TelemetryTriggerFrom } from "./telemetry/extTelemetryEvents"; @@ -93,21 +102,11 @@ import officeDevTreeViewManager from "./treeview/officeDevTreeViewManager"; import TreeViewManagerInstance from "./treeview/treeViewManager"; import { UriHandler, setUriEventHandler } from "./uriHandler"; import { delay, hasAdaptiveCardInWorkspace, isM365Project } from "./utils/commonUtils"; -import { FeatureFlags } from "./featureFlags"; +import { updateAutoOpenGlobalKey } from "./utils/globalStateUtils"; import { loadLocalizedStrings } from "./utils/localizeUtils"; import { checkProjectTypeAndSendTelemetry } from "./utils/projectChecker"; import { ReleaseNote } from "./utils/releaseNote"; import { ExtensionSurvey } from "./utils/survey"; -import { registerOfficeTaskAndDebugEvents } from "./debug/officeTaskHandler"; -import { createProjectFromWalkthroughHandler } from "./handlers/walkthrough"; -import { checkCopilotAccessHandler } from "./handlers/checkCopilotAccess"; -import { showOutputChannelHandler } from "./handlers/showOutputChannel"; -import { debugInTestToolHandler } from "./handlers/debugInTestTool"; -import { checkSideloadingCallback } from "./handlers/checkSideloading"; -import { downloadSampleApp } from "./handlers/downloadSample"; -import { checkCopilotCallback } from "./handlers/checkCopilotCallback"; -import { updateAutoOpenGlobalKey } from "./utils/globalStateUtils"; -import { TeamsFxTaskType } from "./debug/common/debugConstants"; export async function activate(context: vscode.ExtensionContext) { process.env[FeatureFlags.ChatParticipant] = ( @@ -132,7 +131,7 @@ export async function activate(context: vscode.ExtensionContext) { registerInternalCommands(context); - if (isChatParticipantEnabled()) { + if (featureFlagManager.getBooleanValue(CoreFeatureFlags.ChatParticipant)) { registerChatParticipant(context); registerOfficeChatParticipant(context); @@ -159,15 +158,7 @@ export async function activate(context: vscode.ExtensionContext) { await vscode.commands.executeCommand( "setContext", "fx-extension.isChatParticipantEnabled", - isChatParticipantEnabled() - ); - - // Flags for "Build Intelligent Apps" walkthrough. - // DEVEOP_COPILOT_PLUGIN: boolean in vscode settings - await vscode.commands.executeCommand( - "setContext", - "fx-extension.isApiCopilotPluginEnabled", - featureFlagManager.getBooleanValue(FxCoreFeatureFlags.CopilotPlugin) + featureFlagManager.getBooleanValue(CoreFeatureFlags.ChatParticipant) ); // Flags for "Build Intelligent Apps" walkthrough. @@ -175,7 +166,7 @@ export async function activate(context: vscode.ExtensionContext) { await vscode.commands.executeCommand( "setContext", "fx-extension.isApiCopilotPluginEnabled", - featureFlagManager.getBooleanValue(FxCoreFeatureFlags.CopilotPlugin) + featureFlagManager.getBooleanValue(CoreFeatureFlags.CopilotPlugin) ); await vscode.commands.executeCommand( diff --git a/packages/vscode-extension/src/handlers.ts b/packages/vscode-extension/src/handlers.ts index a01a222cba..f3a31171b4 100644 --- a/packages/vscode-extension/src/handlers.ts +++ b/packages/vscode-extension/src/handlers.ts @@ -44,30 +44,31 @@ import { import { AppStudioScopes, AuthSvcScopes, - QuestionNames, + CapabilityOptions, Correlator, DepsManager, DepsType, + FeatureFlags, FxCore, Hub, InvalidProjectError, + JSONSyntaxError, + MetadataV3, + QuestionNames, askSubscription, assembleError, environmentManager, + featureFlagManager, generateScaffoldingSummary, - getProjectMetadata, getHashedEnv, + getProjectMetadata, globalStateGet, globalStateUpdate, isUserCancelError, - isValidProject, isValidOfficeAddInProject, - pathUtils, + isValidProject, manifestUtils, - JSONSyntaxError, - MetadataV3, - CapabilityOptions, - isChatParticipantEnabled, + pathUtils, pluginManifestUtils, teamsDevPortalClient, } from "@microsoft/teamsfx-core"; @@ -86,15 +87,16 @@ import { } from "./constants"; import { PanelType } from "./controls/PanelType"; import { WebviewPanel } from "./controls/webviewPanel"; +import { RecommendedOperations } from "./debug/common/debugConstants"; +import { checkPrerequisitesForGetStarted } from "./debug/depsChecker/getStartedChecker"; import { vscodeLogger } from "./debug/depsChecker/vscodeLogger"; import { vscodeTelemetry } from "./debug/depsChecker/vscodeTelemetry"; import { openHubWebClient } from "./debug/launch"; -import { checkPrerequisitesForGetStarted } from "./debug/depsChecker/getStartedChecker"; import { selectAndDebug } from "./debug/runIconHandler"; +import { isLoginFailureError, showError, wrapError } from "./error/common"; import { ExtensionErrors, ExtensionSource } from "./error/error"; import * as exp from "./exp/index"; import { TreatmentVariableValue } from "./exp/treatmentVariables"; -import { VS_CODE_UI } from "./qm/vsc_ui"; import { context, core, @@ -108,7 +110,9 @@ import { tools, workspaceUri, } from "./globalVariables"; +import { invokeTeamsAgent } from "./handlers/copilotChatHandlers"; import { TeamsAppMigrationHandler } from "./migration/migrationHandler"; +import { VS_CODE_UI } from "./qm/vsc_ui"; import { ExtTelemetry } from "./telemetry/extTelemetry"; import { AccountType, @@ -126,6 +130,7 @@ import { M365AccountNode } from "./treeview/account/m365Node"; import envTreeProviderInstance from "./treeview/environmentTreeViewProvider"; import { TreeViewCommand } from "./treeview/treeViewCommand"; import TreeViewManagerInstance from "./treeview/treeViewManager"; +import { getAppName } from "./utils/appDefinitionUtils"; import { checkCoreNotEmpty, getLocalDebugMessageTemplate, @@ -133,20 +138,16 @@ import { } from "./utils/commonUtils"; import { getResourceGroupNameFromEnv, getSubscriptionInfoFromEnv } from "./utils/envTreeUtils"; import { getDefaultString, localize } from "./utils/localizeUtils"; -import { getAppName } from "./utils/appDefinitionUtils"; +import { triggerV3Migration } from "./utils/migrationUtils"; +import { updateProjectStatus } from "./utils/projectStatusUtils"; import { ExtensionSurvey } from "./utils/survey"; +import { getSystemInputs } from "./utils/systemEnvUtils"; import { getTeamsAppTelemetryInfoByEnv, getTriggerFromProperty, isTriggerFromWalkThrough, } from "./utils/telemetryUtils"; -import { RecommendedOperations } from "./debug/common/debugConstants"; import { openFolder, openOfficeDevFolder } from "./utils/workspaceUtils"; -import { invokeTeamsAgent } from "./handlers/copilotChatHandlers"; -import { updateProjectStatus } from "./utils/projectStatusUtils"; -import { triggerV3Migration } from "./utils/migrationUtils"; -import { getSystemInputs } from "./utils/systemEnvUtils"; -import { isLoginFailureError, showError, wrapError } from "./error/common"; export function activate(): Result { const result: Result = ok(Void); @@ -2876,7 +2877,7 @@ export async function projectVersionCheck() { } function getWalkThroughId(): string { - return isChatParticipantEnabled() + return featureFlagManager.getBooleanValue(FeatureFlags.ChatParticipant) ? "TeamsDevApp.ms-teams-vscode-extension#teamsToolkitGetStartedWithChat" : "TeamsDevApp.ms-teams-vscode-extension#teamsToolkitGetStarted"; } diff --git a/packages/vscode-extension/src/treeview/account/m365Node.ts b/packages/vscode-extension/src/treeview/account/m365Node.ts index a89d1b52b4..45d7197917 100644 --- a/packages/vscode-extension/src/treeview/account/m365Node.ts +++ b/packages/vscode-extension/src/treeview/account/m365Node.ts @@ -1,15 +1,14 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. -import * as vscode from "vscode"; - import { featureFlagManager, FeatureFlags as FxCoreFeatureFlags } from "@microsoft/teamsfx-core"; +import * as vscode from "vscode"; import { TelemetryTriggerFrom } from "../../telemetry/extTelemetryEvents"; import { localize } from "../../utils/localizeUtils"; import { DynamicNode } from "../dynamicNode"; import { AccountItemStatus, loadingIcon, m365Icon } from "./common"; -import { SideloadingNode } from "./sideloadingNode"; import { CopilotNode } from "./copilotNode"; +import { SideloadingNode } from "./sideloadingNode"; export class M365AccountNode extends DynamicNode { public status: AccountItemStatus; diff --git a/packages/vscode-extension/src/treeview/treeViewManager.ts b/packages/vscode-extension/src/treeview/treeViewManager.ts index 96ac44555b..2cfee3f969 100644 --- a/packages/vscode-extension/src/treeview/treeViewManager.ts +++ b/packages/vscode-extension/src/treeview/treeViewManager.ts @@ -3,15 +3,15 @@ import * as vscode from "vscode"; import { TreeCategory } from "@microsoft/teamsfx-api"; -import { isChatParticipantEnabled, manifestUtils } from "@microsoft/teamsfx-core"; +import { featureFlagManager, FeatureFlags, manifestUtils } from "@microsoft/teamsfx-core"; import { isSPFxProject, workspaceUri } from "../globalVariables"; +import { hasAdaptiveCardInWorkspace } from "../utils/commonUtils"; import { localize } from "../utils/localizeUtils"; import accountTreeViewProviderInstance from "./account/accountTreeViewProvider"; import { CommandsTreeViewProvider } from "./commandsTreeViewProvider"; import envTreeProviderInstance from "./environmentTreeViewProvider"; import { CommandStatus, TreeViewCommand } from "./treeViewCommand"; -import { hasAdaptiveCardInWorkspace } from "../utils/commonUtils"; class TreeViewManager { private static instance: TreeViewManager; @@ -231,7 +231,7 @@ class TreeViewManager { undefined, { name: "debug-alt", custom: false } ), - ...(isChatParticipantEnabled() + ...(featureFlagManager.getBooleanValue(FeatureFlags.ChatParticipant) ? [ new TreeViewCommand( localize("teamstoolkit.commandsTreeViewProvider.getCopilotHelpTitle"), diff --git a/packages/vscode-extension/test/extension/handlers.test.ts b/packages/vscode-extension/test/extension/handlers.test.ts index 3e2f682f62..98fc7369f2 100644 --- a/packages/vscode-extension/test/extension/handlers.test.ts +++ b/packages/vscode-extension/test/extension/handlers.test.ts @@ -24,12 +24,12 @@ import { UnhandledError, UserCancelError, environmentManager, + featureFlagManager, manifestUtils, pathUtils, pluginManifestUtils, teamsDevPortalClient, } from "@microsoft/teamsfx-core"; -import * as featureFlags from "@microsoft/teamsfx-core/build/common/featureFlags"; import * as globalState from "@microsoft/teamsfx-core/build/common/globalState"; import * as projectSettingsHelper from "@microsoft/teamsfx-core/build/common/projectSettingsHelper"; import * as chai from "chai"; @@ -48,10 +48,10 @@ import { DeveloperPortalHomeLink, GlobalKey } from "../../src/constants"; import { PanelType } from "../../src/controls/PanelType"; import { WebviewPanel } from "../../src/controls/webviewPanel"; import * as debugConstants from "../../src/debug/common/debugConstants"; -import * as migrationUtils from "../../src/utils/migrationUtils"; +import * as getStartedChecker from "../../src/debug/depsChecker/getStartedChecker"; import * as launch from "../../src/debug/launch"; -import * as localPrerequisites from "../../src/debug/depsChecker/common"; import * as runIconHandlers from "../../src/debug/runIconHandler"; +import * as errorCommon from "../../src/error/common"; import { ExtensionErrors } from "../../src/error/error"; import { TreatmentVariableValue } from "../../src/exp/treatmentVariables"; import * as globalVariables from "../../src/globalVariables"; @@ -66,15 +66,14 @@ import { TelemetryEvent } from "../../src/telemetry/extTelemetryEvents"; import accountTreeViewProviderInstance from "../../src/treeview/account/accountTreeViewProvider"; import envTreeProviderInstance from "../../src/treeview/environmentTreeViewProvider"; import TreeViewManagerInstance from "../../src/treeview/treeViewManager"; -import * as errorCommon from "../../src/error/common"; +import * as appDefinitionUtils from "../../src/utils/appDefinitionUtils"; +import { updateAutoOpenGlobalKey } from "../../src/utils/globalStateUtils"; import * as localizeUtils from "../../src/utils/localizeUtils"; -import * as systemEnvUtils from "../../src/utils/systemEnvUtils"; +import * as migrationUtils from "../../src/utils/migrationUtils"; import { ExtensionSurvey } from "../../src/utils/survey"; -import { MockCore } from "../mocks/mockCore"; +import * as systemEnvUtils from "../../src/utils/systemEnvUtils"; import * as telemetryUtils from "../../src/utils/telemetryUtils"; -import * as appDefinitionUtils from "../../src/utils/appDefinitionUtils"; -import { updateAutoOpenGlobalKey } from "../../src/utils/globalStateUtils"; -import * as getStartedChecker from "../../src/debug/depsChecker/getStartedChecker"; +import { MockCore } from "../mocks/mockCore"; describe("handlers", () => { describe("activate()", function () { @@ -852,7 +851,7 @@ describe("handlers", () => { }); it("openWelcomeHandler", async () => { - sandbox.stub(featureFlags, "isChatParticipantEnabled").returns(false); + sandbox.stub(featureFlagManager, "getBooleanValue").returns(false); const executeCommands = sandbox.stub(vscode.commands, "executeCommand"); const sendTelemetryEvent = sandbox.stub(ExtTelemetry, "sendTelemetryEvent"); @@ -866,7 +865,7 @@ describe("handlers", () => { }); it("openWelcomeHandler with chat", async () => { - sandbox.stub(featureFlags, "isChatParticipantEnabled").returns(true); + sandbox.stub(featureFlagManager, "getBooleanValue").returns(true); const executeCommands = sandbox.stub(vscode.commands, "executeCommand"); const sendTelemetryEvent = sandbox.stub(ExtTelemetry, "sendTelemetryEvent"); diff --git a/packages/vscode-extension/test/extension/treeview/account/m365Node.test.ts b/packages/vscode-extension/test/extension/treeview/account/m365Node.test.ts index fcd8366966..fa952e8e7a 100644 --- a/packages/vscode-extension/test/extension/treeview/account/m365Node.test.ts +++ b/packages/vscode-extension/test/extension/treeview/account/m365Node.test.ts @@ -1,11 +1,10 @@ +import { featureFlagManager } from "@microsoft/teamsfx-core"; import * as chai from "chai"; import * as sinon from "sinon"; import * as vscode from "vscode"; - -import { M365AccountNode } from "../../../../src/treeview/account/m365Node"; import { AccountItemStatus, loadingIcon, m365Icon } from "../../../../src/treeview/account/common"; +import { M365AccountNode } from "../../../../src/treeview/account/m365Node"; import { DynamicNode } from "../../../../src/treeview/dynamicNode"; -import { featureFlagManager } from "@microsoft/teamsfx-core"; describe("m365Node", () => { const sandbox = sinon.createSandbox(); @@ -71,7 +70,6 @@ describe("m365Node", () => { m365Node.updateChecks("test token", true, false); chai.assert.isDefined(m365Node.getChildren()); chai.assert.equal(1, (m365Node.getChildren() as any).length); - sandbox.stub(featureFlagManager, "getBooleanValue").returns(true); const m365NodeWithCopilot = new M365AccountNode(eventEmitter); m365NodeWithCopilot.updateChecks("test token", false, true); diff --git a/packages/vscode-extension/test/extension/treeview/treeViewManager.test.ts b/packages/vscode-extension/test/extension/treeview/treeViewManager.test.ts index 4aa891ef0b..e3e1761786 100644 --- a/packages/vscode-extension/test/extension/treeview/treeViewManager.test.ts +++ b/packages/vscode-extension/test/extension/treeview/treeViewManager.test.ts @@ -1,13 +1,12 @@ +import { TeamsAppManifest, ok } from "@microsoft/teamsfx-api"; +import { featureFlagManager, manifestUtils } from "@microsoft/teamsfx-core"; +import * as featureFlags from "@microsoft/teamsfx-core/build/common/featureFlags"; import * as chai from "chai"; import * as sinon from "sinon"; import * as vscode from "vscode"; - import * as globalVariables from "../../../src/globalVariables"; import { CommandsTreeViewProvider } from "../../../src/treeview/commandsTreeViewProvider"; import treeViewManager from "../../../src/treeview/treeViewManager"; -import * as featureFlags from "@microsoft/teamsfx-core/build/common/featureFlags"; -import { manifestUtils } from "@microsoft/teamsfx-core"; -import { TeamsAppManifest, ok } from "@microsoft/teamsfx-api"; describe("TreeViewManager", () => { const sandbox = sinon.createSandbox(); @@ -31,7 +30,7 @@ describe("TreeViewManager", () => { it("Development Treeview", () => { sandbox.stub(globalVariables, "context").value({ extensionPath: "" }); sandbox.stub(globalVariables, "isSPFxProject").value(false); - sandbox.stub(featureFlags, "isChatParticipantEnabled").returns(false); + sandbox.stub(featureFlagManager, "getBooleanValue").returns(false); treeViewManager.registerTreeViews({ subscriptions: [], } as unknown as vscode.ExtensionContext); @@ -44,7 +43,7 @@ describe("TreeViewManager", () => { it("Development Treeview when ChatParticipant is enabled", () => { sandbox.stub(globalVariables, "context").value({ extensionPath: "" }); sandbox.stub(globalVariables, "isSPFxProject").value(false); - sandbox.stub(featureFlags, "isChatParticipantEnabled").returns(true); + sandbox.stub(featureFlagManager, "getBooleanValue").returns(true); treeViewManager.registerTreeViews({ subscriptions: [], } as unknown as vscode.ExtensionContext); @@ -70,7 +69,7 @@ describe("TreeViewManager", () => { it("updateTreeViewsOnSPFxChanged", () => { sandbox.stub(globalVariables, "isSPFxProject").value(false); - sandbox.stub(featureFlags, "isChatParticipantEnabled").returns(false); + sandbox.stub(featureFlagManager, "getBooleanValue").returns(false); treeViewManager.registerTreeViews({ subscriptions: [], } as unknown as vscode.ExtensionContext); @@ -90,7 +89,7 @@ describe("TreeViewManager", () => { it("updateTreeViewsByContent if remove project related commands", async () => { sandbox.stub(globalVariables, "workspaceUri").value(""); - sandbox.stub(featureFlags, "isChatParticipantEnabled").returns(false); + sandbox.stub(featureFlagManager, "getBooleanValue").returns(false); sandbox.stub(manifestUtils, "readAppManifest").resolves(ok({} as TeamsAppManifest)); sandbox.stub(manifestUtils, "getCapabilities").returns(["tab"]); treeViewManager.registerTreeViews({ @@ -114,7 +113,7 @@ describe("TreeViewManager", () => { it("updateTreeViewsByContent if remove project related commands when ChatParticipant is enabled", async () => { sandbox.stub(globalVariables, "workspaceUri").value(""); - sandbox.stub(featureFlags, "isChatParticipantEnabled").returns(true); + sandbox.stub(featureFlagManager, "getBooleanValue").returns(true); sandbox.stub(manifestUtils, "readAppManifest").resolves(ok({} as TeamsAppManifest)); sandbox.stub(manifestUtils, "getCapabilities").returns(["tab"]); treeViewManager.registerTreeViews({