From fce3698d61e46418d0583907a05ff93fde937a12 Mon Sep 17 00:00:00 2001 From: WANG YEFU Date: Wed, 14 Apr 2021 17:00:03 +0800 Subject: [PATCH] feat(solution): support configurable tabs. (#258) * fix(solution): save programming language in config * fix lint error * fix lint error * feat(solution): support configurable tabs * change sanity check for staticTabs and configurableTabs * feat(solution): VSpublish * remove tab scope question from solution * fix(solution): remove tab scope question * fix(solution): call setApplicationInContext after provision --- .../fx-solution/appstudio/appstudio.ts | 10 +++ .../plugins/solution/fx-solution/constants.ts | 2 + .../plugins/solution/fx-solution/solution.ts | 80 +++++++++++++------ 3 files changed, 68 insertions(+), 24 deletions(-) diff --git a/packages/fx-core/src/plugins/solution/fx-solution/appstudio/appstudio.ts b/packages/fx-core/src/plugins/solution/fx-solution/appstudio/appstudio.ts index 5eaf14a1f9..12072cc570 100644 --- a/packages/fx-core/src/plugins/solution/fx-solution/appstudio/appstudio.ts +++ b/packages/fx-core/src/plugins/solution/fx-solution/appstudio/appstudio.ts @@ -215,6 +215,8 @@ export namespace AppStudio { appId: string, domains: string[], webApplicationInfoResource: string, + staticTabs: string, + configurableTabs: string, tabEndpoint?: string, appName?: string, version?: string, @@ -242,6 +244,14 @@ export namespace AppStudio { updatedManifest.composeExtensions = JSON.parse(composeExtensions) as IComposeExtension[]; } + if (staticTabs) { + updatedManifest.staticTabs = JSON.parse(staticTabs); + } + + if (configurableTabs) { + updatedManifest.configurableTabs = JSON.parse(configurableTabs); + } + for (const domain of domains) { updatedManifest.validDomains?.push(domain); } diff --git a/packages/fx-core/src/plugins/solution/fx-solution/constants.ts b/packages/fx-core/src/plugins/solution/fx-solution/constants.ts index eab435944b..338b4dd76a 100644 --- a/packages/fx-core/src/plugins/solution/fx-solution/constants.ts +++ b/packages/fx-core/src/plugins/solution/fx-solution/constants.ts @@ -31,6 +31,8 @@ export const PERMISSION_REQUEST = "permissionRequest"; * Config key whose value is either javascript, typescript or csharp. */ export const PROGRAMMING_LANGUAGE = "programmingLanguage"; +export const STATIC_TABS = "staticTabs"; +export const CONFIGURABLE_TABS = "configurableTabs"; /** * Config keys that are useful for generating remote teams app manifest diff --git a/packages/fx-core/src/plugins/solution/fx-solution/solution.ts b/packages/fx-core/src/plugins/solution/fx-solution/solution.ts index 11f210a2a8..038c2ec892 100644 --- a/packages/fx-core/src/plugins/solution/fx-solution/solution.ts +++ b/packages/fx-core/src/plugins/solution/fx-solution/solution.ts @@ -49,7 +49,6 @@ import { FRONTEND_DOMAIN, FRONTEND_ENDPOINT, AAD_REMOTE_CLIENT_ID, - TEAMS_APP_MANIFEST_TEMPLATE, REMOTE_TEAMS_APP_ID, Void, SOLUTION_PROVISION_SUCCEEDED, @@ -65,7 +64,9 @@ import { WEB_APPLICATION_INFO_SOURCE, LOCAL_WEB_APPLICATION_INFO_SOURCE, PROVISION_MANIFEST, - PROGRAMMING_LANGUAGE + PROGRAMMING_LANGUAGE, + CONFIGURABLE_TABS, + STATIC_TABS } from "./constants"; import { SpfxPlugin } from "../../resource/spfx"; @@ -781,12 +782,24 @@ export class TeamsAppSolution implements Solution { ctx.logProvider?.debug(`Failed to get webApplicationInfoResource from aad by key ${WEB_APPLICATION_INFO_SOURCE}.`); return err(returnSystemError(new Error("Failed to get webApplicationInfoResource"), "Solution", SolutionError.UpdateManifestError)); } + // STATIC_TABS and CONFIGURABLE_TABS are only available after postProvision. + const staticTabs = ctx.config.get(this.fehostPlugin.name)?.getString(STATIC_TABS); + const configurableTabs = ctx.config.get(this.fehostPlugin.name)?.getString(CONFIGURABLE_TABS); + if (!staticTabs || !configurableTabs || + (staticTabs === "[]" && configurableTabs === "[]")) { + return err(returnSystemError( + new Error(`Invalid frontend config: ${STATIC_TABS}: ${staticTabs} ${CONFIGURABLE_TABS}: ${configurableTabs}`), + "Solution", + SolutionError.UpdateManifestError)); + } const [appDefinition, updatedManifest] = AppStudio.getDevAppDefinition( manifestString, clientId, validDomains, webApplicationInfoResource, + staticTabs, + configurableTabs, endpoint.endsWith("/") ? endpoint.substring(0, endpoint.length - 1) : endpoint, ); const teamsAppId = ctx.config.get(GLOBAL_CONFIG)?.getString(REMOTE_TEAMS_APP_ID); @@ -970,18 +983,18 @@ export class TeamsAppSolution implements Solution { ctx.logProvider?.info("[Teams Toolkit]: provison finished!"); if (selectedPlugins.some((plugin) => plugin.name === this.aadPlugin.name)) { const aadPlugin: AadAppForTeamsPlugin = this.aadPlugin as any; - const result = aadPlugin.setApplicationInContext( + return aadPlugin.setApplicationInContext( getPluginContext(ctx, this.aadPlugin.name, this.manifest), ); - if (result.isErr()) { - return result; - } + } - return this.createAndConfigTeamsManifest(ctx); + return ok(undefined); }, async () => { + + const result = this.createAndConfigTeamsManifest(ctx); ctx.logProvider?.info("[Teams Toolkit]: configuration finished!"); - return ok(undefined); + return result; }, ); } @@ -1164,9 +1177,6 @@ export class TeamsAppSolution implements Solution { async getTabScaffoldQuestions(ctx: SolutionContext):Promise> { const tabNode = new QTreeNode({ type: NodeType.group }); - const tab_scope = new QTreeNode(TabScopQuestion); - tabNode.addChild(tab_scope); - const frontend_host_type = new QTreeNode(FrontendHostTypeQuestion); tabNode.addChild(frontend_host_type); @@ -1432,6 +1442,16 @@ export class TeamsAppSolution implements Solution { return result; } } + const result = this.loadTeamsAppTenantId(ctx.config, await ctx.appStudioToken?.getJsonObject()); + + if (result.isErr()) { + return result; + } + + const postLocalDebugResult = await executeConcurrently(postLocalDebugWithCtx); + if (postLocalDebugResult.isErr()) { + return postLocalDebugResult; + } const maybeConfig = this.getLocalDebugConfig(ctx.config); @@ -1451,12 +1471,24 @@ export class TeamsAppSolution implements Solution { validDomains.push(localBotDomain); } + // STATIC_TABS and CONFIGURABLE_TABS are only available after postProvision. + const staticTabs = ctx.config.get(this.fehostPlugin.name)?.getString(STATIC_TABS); + const configurableTabs = ctx.config.get(this.fehostPlugin.name)?.getString(CONFIGURABLE_TABS); + if (!staticTabs || !configurableTabs || + (staticTabs === "[]" && configurableTabs === "[]")) { + return err(returnSystemError( + new Error(`Invalid frontend config: ${STATIC_TABS}: ${staticTabs} ${CONFIGURABLE_TABS}: ${configurableTabs}`), + "Solution", + SolutionError.UpdateManifestError)); + } const manifestTpl = (await fs.readFile(`${ctx.root}/.${ConfigFolderName}/manifest.remote.json`)).toString(); const [appDefinition, _updatedManifest] = AppStudio.getDevAppDefinition( manifestTpl, localAADId, validDomains, webApplicationInfoResource, + staticTabs, + configurableTabs, localTabEndpoint, this.manifest!.name.short, this.manifest!.version, @@ -1489,14 +1521,7 @@ export class TeamsAppSolution implements Solution { } ctx.config.get(GLOBAL_CONFIG)?.set(LOCAL_DEBUG_TEAMS_APP_ID, maybeTeamsAppId.value); } - - const result = this.loadTeamsAppTenantId(ctx.config, await ctx.appStudioToken?.getJsonObject()); - - if (result.isErr()) { - return result; - } - - return executeConcurrently(postLocalDebugWithCtx); + return ok(Void); } private parseTeamsAppTenantId(appStudioToken?: object): Result { @@ -1908,12 +1933,19 @@ export class TeamsAppSolution implements Solution { if(method === "addCapability"){ return await this.executeAddCapability(func, ctx); } - if (namespace.includes("solution") && method === "registerTeamsAppAndAad") { - const maybeParams = this.extractParamForRegisterTeamsAppAndAad(ctx.answers); - if (maybeParams.isErr()) { - return maybeParams; + if (namespace.includes("solution")) { + if (method === "registerTeamsAppAndAad") { + const maybeParams = this.extractParamForRegisterTeamsAppAndAad(ctx.answers); + if (maybeParams.isErr()) { + return maybeParams; + } + return this.registerTeamsAppAndAad(ctx, maybeParams.value); + } else if (method === "VSpublish") { + // VSpublish means VS calling cli to do publish. It is different than normal cli work flow + // It's teamsfx init followed by teamsfx publish without running provision. + // Using executeUserTask here could bypass the fx project check. + return this.publish(ctx); } - return this.registerTeamsAppAndAad(ctx, maybeParams.value); } else if (array.length == 2) { const pluginName = array[1]; const plugin = this.pluginMap.get(pluginName);