Skip to content

Commit

Permalink
refactor: office xml addin telemetry (#11055)
Browse files Browse the repository at this point in the history
  • Loading branch information
ZYUN-MSFT authored Mar 21, 2024
1 parent e3e9eda commit e5e01ea
Show file tree
Hide file tree
Showing 10 changed files with 159 additions and 29 deletions.
18 changes: 10 additions & 8 deletions packages/fx-core/src/common/featureFlags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ export function isOfficeXMLAddinEnabled(): boolean {
return isFeatureFlagEnabled(FeatureFlagName.OfficeXMLAddin, false);
}

export function isOfficeJSONAddinEnabled(): boolean {
return isFeatureFlagEnabled(FeatureFlagName.OfficeAddin, false);
}

export function isTeamsFxRebrandingEnabled(): boolean {
return isFeatureFlagEnabled(FeatureFlagName.TeamsFxRebranding, false);
}
Expand All @@ -76,16 +80,12 @@ export function isNewProjectTypeEnabled(): boolean {
return isFeatureFlagEnabled(FeatureFlagName.NewProjectType, true);
}

export function isOfficeJSONAddinEnabled(): boolean {
return isFeatureFlagEnabled(FeatureFlagName.OfficeAddin, false);
}

///////////////////////////////////////////////////////////////////////////////
// Notes for Office Addin Feature flags:
// Case 1: TEAMSFX_OFFICE_ADDIN = false, TEAMSFX_OFFICE_XML_ADDIN = false
// 1.1 project-type option: `outlook-addin-type`
// 1.2 addin-host: not show but use `outlook` internally
// 1.3 capabilities options: [`jason-taskpane`, `outlook-addin-import`]
// 1.3 capabilities options: [`json-taskpane`, `outlook-addin-import`]
// 1.4 programming-language options: [`typescript`] (skip in UI)
// 1.5 office-addin-framework-type: not show question but use `default_old` internally
// 1.6 generator class: OfficeAddinGenerator
Expand All @@ -101,15 +101,17 @@ export function isOfficeJSONAddinEnabled(): boolean {
// 2.4 programming-language options:
// if (addin-host == `outlook`) then [`typescript`] (skip in UI)
// else two options: [`typescript`, `javascript`]
// 2.5 office-addin-framework-type options: not show but use `default` internally
// 2.5 office-addin-framework-type options:
// if (word excel and powerpoint) use `default` internally
// else if (outlook) use `default_old` internally
// 2.6 generator class:
// if (addin-host == `outlook`) then OfficeAddinGenerator
// else OfficeXMLAddinGenerator
// 2.7 template link:
// if (addin-host == `outlook`) config.json.json-taskpane.default.[programming-language]
// else config[addin-host].[capabilities].default.[programming-language]
// Case 3: TEAMSFX_OFFICE_ADDIN = true AND TEAMSFX_OFFICE_XML_ADDIN = true
// 3.1 project-type option: `office-addin-type`
// 3.1 project-type option: `office-addin-type`
// 3.2 addin-host: not show but will use `wxpo` internally
// 3.3 capabilities options: [`json-taskpane`, `office-addin-import`]
// 3.4 programming-language options: [`typescript`, `javascript`]
Expand All @@ -119,7 +121,7 @@ export function isOfficeJSONAddinEnabled(): boolean {
// case 4: TEAMSFX_OFFICE_ADDIN = true AND TEAMSFX_OFFICE_XML_ADDIN = fasle
// 4.1 project-type option: `office-addin-type`
// 4.2 addin-host: not show but will use `wxpo` internally
// 4.3 capabilities options: [`jason-taskpane`, `office-addin-import`]
// 4.3 capabilities options: [`json-taskpane`, `office-addin-import`]
// 4.4 programming-language options: [`typescript`, `javascript`]
// 4.5 office-addin-framework-type options: [`default`, `react`]
// 4.6 generator class: OfficeAddinGenerator
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@
import { hooks } from "@feathersjs/hooks/lib";
import { Context, FxError, Inputs, Result, err, ok } from "@microsoft/teamsfx-api";
import * as childProcess from "child_process";
import _ from "lodash";
import _, { merge } from "lodash";
import { OfficeAddinManifest } from "office-addin-manifest";
import { join } from "path";
import { promisify } from "util";
import { getLocalizedString } from "../../../common/localizeUtils";
import { assembleError } from "../../../error";
import { QuestionNames } from "../../../question/questionNames";
import { ActionExecutionMW } from "../../middleware/actionExecutionMW";
import { ActionExecutionMW, ActionContext } from "../../middleware/actionExecutionMW";
import { Generator } from "../generator";
import { HelperMethods } from "../officeAddin/helperMethods";
import { getOfficeAddinTemplateConfig } from "./projectConfig";
Expand All @@ -24,6 +24,14 @@ import { convertToLangKey } from "../utils";
const COMPONENT_NAME = "office-xml-addin";
const TELEMETRY_EVENT = "generate";
const TEMPLATE_BASE = "office-xml-addin";
const TEMPLATE_COMMON_NAME = "office-xml-addin-common";
const TEMPLATE_COMMON_LANG = "common";

const enum OfficeXMLAddinTelemetryProperties {
host = "office-xml-addin-host",
project = "office-xml-addin-project",
lang = "office-xml-addin-lang",
}

/**
* project-type=office-xml-addin-type addin-host!==outlook
Expand All @@ -40,7 +48,8 @@ export class OfficeXMLAddinGenerator {
static async generate(
context: Context,
inputs: Inputs,
destinationPath: string
destinationPath: string,
actionContext?: ActionContext
): Promise<Result<undefined, FxError>> {
const host = inputs[QuestionNames.OfficeAddinHost] as string;
const capability = inputs[QuestionNames.Capabilities];
Expand All @@ -50,15 +59,21 @@ export class OfficeXMLAddinGenerator {
const langKey = convertToLangKey(lang);
const appName = inputs[QuestionNames.AppName] as string;
const projectType = inputs[QuestionNames.ProjectType];
const templteConfig = getOfficeAddinTemplateConfig(projectType, host);
const templateName = templteConfig[capability].localTemplate;
const projectLink = templteConfig[capability].framework["default"][lang];
const templateConfig = getOfficeAddinTemplateConfig(projectType, host);
const templateName = templateConfig[capability].localTemplate;
const projectLink = templateConfig[capability].framework["default"][lang];
const workingDir = process.cwd();
const progressBar = context.userInteraction.createProgressBar(
getLocalizedString("core.createProjectQuestion.officeXMLAddin.bar.title"),
1
);

merge(actionContext?.telemetryProps, {
[OfficeXMLAddinTelemetryProperties.host]: host,
[OfficeXMLAddinTelemetryProperties.project]: capability,
[OfficeXMLAddinTelemetryProperties.lang]: lang,
});

try {
process.chdir(destinationPath);
await progressBar.start();
Expand Down Expand Up @@ -87,7 +102,7 @@ export class OfficeXMLAddinGenerator {
langKey
);
if (getManifestOnlyProjectTemplateRes.isErr())
return err(getManifestOnlyProjectTemplateRes.error);
throw err(getManifestOnlyProjectTemplateRes.error);
}

// -> Common Step: Copy the README (or with manifest for manifest-only proj)
Expand All @@ -97,7 +112,7 @@ export class OfficeXMLAddinGenerator {
`${TEMPLATE_BASE}-${templateName}`,
langKey
);
if (getReadmeTemplateRes.isErr()) return err(getReadmeTemplateRes.error);
if (getReadmeTemplateRes.isErr()) throw err(getReadmeTemplateRes.error);

// -> Common Step: Modify the Manifest
await OfficeAddinManifest.modifyManifestFile(
Expand All @@ -106,6 +121,15 @@ export class OfficeXMLAddinGenerator {
`${appName}`
);

// -> Common Step: Generate OfficeXMLAddin specific `teamsapp.yml`
const generateOfficeYMLRes = await Generator.generateTemplate(
context,
destinationPath,
TEMPLATE_COMMON_NAME,
TEMPLATE_COMMON_LANG
);
if (generateOfficeYMLRes.isErr()) throw err(generateOfficeYMLRes.error);

process.chdir(workingDir);
await progressBar.end(true, true);
return ok(undefined);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ export const OfficeAddinProjectConfig: IOfficeAddinProjectConfig = {
localTemplate: "excel-react",
...CommonProjectConfig.react,
},
"excel-cfshared": {
"excel-custom-functions-shared": {
title: "core.createProjectQuestion.officeXMLAddin.excel.cf.shared.title",
detail: "core.createProjectQuestion.officeXMLAddin.excel.cf.shared.detail",
localTemplate: "excel-cf",
Expand All @@ -134,7 +134,7 @@ export const OfficeAddinProjectConfig: IOfficeAddinProjectConfig = {
},
},
},
"excel-cfjs": {
"excel-custom-functions-js": {
title: "core.createProjectQuestion.officeXMLAddin.excel.cf.js.title",
detail: "core.createProjectQuestion.officeXMLAddin.excel.cf.js.detail",
localTemplate: "excel-cf",
Expand Down
17 changes: 12 additions & 5 deletions packages/fx-core/src/question/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,10 @@ import {
OpenAIPluginManifestHelper,
listOperations,
} from "../component/generator/copilotPlugin/helper";
import { OfficeAddinProjectConfig } from "../component/generator/officeXMLAddin/projectConfig";
import {
OfficeAddinProjectConfig,
getOfficeAddinTemplateConfig,
} from "../component/generator/officeXMLAddin/projectConfig";
import { DevEnvironmentSetupError } from "../component/generator/spfx/error";
import { SPFxGenerator } from "../component/generator/spfx/spfxGenerator";
import { Constants } from "../component/generator/spfx/utils/constants";
Expand Down Expand Up @@ -1410,10 +1413,14 @@ export function getLanguageOptions(inputs: Inputs): OptionItem[] {
) {
return [{ id: ProgrammingLanguage.TS, label: "TypeScript" }];
}
return [
{ id: ProgrammingLanguage.TS, label: "TypeScript" },
{ id: ProgrammingLanguage.JS, label: "JavaScript" },
];
const officeXMLAddinLangConfig = getOfficeAddinTemplateConfig(projectType, host)[capabilities]
.framework["default"];
const officeXMLAddinLangOptions = [];
if (!!officeXMLAddinLangConfig.typescript)
officeXMLAddinLangOptions.push({ id: ProgrammingLanguage.TS, label: "TypeScript" });
if (!!officeXMLAddinLangConfig.javascript)
officeXMLAddinLangOptions.push({ id: ProgrammingLanguage.JS, label: "JavaScript" });
return officeXMLAddinLangOptions;
}

if (capabilities === CapabilityOptions.SPFxTab().id) {
Expand Down
4 changes: 2 additions & 2 deletions packages/fx-core/src/question/inputs/CreateProjectInputs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ export interface CreateProjectInputs extends Inputs {
| "excel-taskpane"
| "excel-sso"
| "excel-react"
| "excel-cfshared"
| "excel-cfjs"
| "excel-custom-functions-shared"
| "excel-custom-functions-js"
| "excel-manifest"
| "powerpoint-taskpane"
| "powerpoint-sso"
Expand Down
4 changes: 2 additions & 2 deletions packages/fx-core/src/question/options/CreateProjectOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ export const CreateProjectOptions: CLICommandOption[] = [
"excel-taskpane",
"excel-sso",
"excel-react",
"excel-cfshared",
"excel-cfjs",
"excel-custom-functions-shared",
"excel-custom-functions-js",
"excel-manifest",
"powerpoint-taskpane",
"powerpoint-sso",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -885,15 +885,20 @@ describe("coordinator create", () => {
describe("Office Addin", async () => {
const sandbox = sinon.createSandbox();
const tools = new MockTools();
let mockedEnvRestore: RestoreFn = () => {};
tools.ui = new MockedUserInteraction();
setTools(tools);

beforeEach(() => {
sandbox.stub(fs, "ensureDir").resolves();
mockedEnvRestore = mockedEnv({
[FeatureFlagName.OfficeXMLAddin]: "false",
});
});

afterEach(() => {
sandbox.restore();
mockedEnvRestore();
});

it("should scaffold taskpane successfully", async () => {
Expand Down Expand Up @@ -1054,15 +1059,20 @@ describe("Office XML Addin", async () => {
describe("Office Addin", async () => {
const sandbox = sinon.createSandbox();
const tools = new MockTools();
let mockedEnvRestore: RestoreFn = () => {};
tools.ui = new MockedUserInteraction();
setTools(tools);

beforeEach(() => {
sandbox.stub(fs, "ensureDir").resolves();
mockedEnvRestore = mockedEnv({
[FeatureFlagName.OfficeXMLAddin]: "false",
});
});

afterEach(() => {
sandbox.restore();
mockedEnvRestore();
});

it("should scaffold taskpane successfully", async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* @author [email protected]
*/

import { Context, Inputs, ok, Platform } from "@microsoft/teamsfx-api";
import { Context, Inputs, ok, Platform, err, SystemError } from "@microsoft/teamsfx-api";
import * as chai from "chai";
import * as childProcess from "child_process";
import fs from "fs";
Expand All @@ -31,6 +31,7 @@ describe("OfficeXMLAddinGenerator", function () {
const testFolder = path.resolve("./tmp");
let context: Context;
let mockedEnvRestore: RestoreFn;
const mockedError = new SystemError("mockedSource", "mockedError", "mockedMessage");

beforeEach(async () => {
mockedEnvRestore = mockedEnv(
Expand Down Expand Up @@ -106,7 +107,7 @@ describe("OfficeXMLAddinGenerator", function () {
[QuestionNames.Capabilities]: "word-manifest",
[QuestionNames.AppName]: "office-addin-test",
[QuestionNames.OfficeAddinFolder]: undefined,
[QuestionNames.ProgrammingLanguage]: "typescript",
[QuestionNames.ProgrammingLanguage]: "javascript",
};

sinon.stub(Generator, "generateTemplate").resolves(ok(undefined));
Expand Down Expand Up @@ -134,6 +135,66 @@ describe("OfficeXMLAddinGenerator", function () {

chai.assert.isTrue(result.isErr());
});

it("should failed when get manifest-only failed", async () => {
const inputs: Inputs = {
platform: Platform.CLI,
projectPath: testFolder,
[QuestionNames.ProjectType]: ProjectTypeOptions.officeXMLAddin().id,
[QuestionNames.OfficeAddinHost]: OfficeAddinHostOptions.word().id,
[QuestionNames.Capabilities]: ["word-manifest"],
[QuestionNames.AppName]: "office-addin-test",
[QuestionNames.OfficeAddinFolder]: undefined,
[QuestionNames.ProgrammingLanguage]: "javascript",
};

sinon.stub(Generator, "generateTemplate").onCall(0).resolves(err(mockedError));
const result = await OfficeXMLAddinGenerator.generate(context, inputs, testFolder);

chai.assert.isTrue(result.isErr());
});

it("should failed when get readme failed", async () => {
const inputs: Inputs = {
platform: Platform.CLI,
projectPath: testFolder,
[QuestionNames.ProjectType]: ProjectTypeOptions.officeXMLAddin().id,
[QuestionNames.OfficeAddinHost]: OfficeAddinHostOptions.word().id,
[QuestionNames.Capabilities]: ["word-manifest"],
[QuestionNames.AppName]: "office-addin-test",
[QuestionNames.OfficeAddinFolder]: undefined,
[QuestionNames.ProgrammingLanguage]: "javascript",
};

const generatorStub = sinon.stub(Generator, "generateTemplate");
generatorStub.onCall(0).resolves(ok(undefined));
generatorStub.onCall(1).resolves(err(mockedError));
const result = await OfficeXMLAddinGenerator.generate(context, inputs, testFolder);

chai.assert.isTrue(result.isErr());
});

it("should failed when gen yml failed", async () => {
const inputs: Inputs = {
platform: Platform.CLI,
projectPath: testFolder,
[QuestionNames.ProjectType]: ProjectTypeOptions.officeXMLAddin().id,
[QuestionNames.OfficeAddinHost]: OfficeAddinHostOptions.word().id,
[QuestionNames.Capabilities]: ["word-manifest"],
[QuestionNames.AppName]: "office-addin-test",
[QuestionNames.OfficeAddinFolder]: undefined,
[QuestionNames.ProgrammingLanguage]: "javascript",
};

const generatorStub = sinon.stub(Generator, "generateTemplate");
generatorStub.onCall(0).resolves(ok(undefined));
generatorStub.onCall(1).resolves(ok(undefined));
generatorStub.onCall(2).resolves(err(mockedError));
sinon.stub(OfficeAddinManifest, "modifyManifestFile").resolves({});
const result = await OfficeXMLAddinGenerator.generate(context, inputs, testFolder);

chai.assert.isTrue(result.isErr());
});
});

describe("getOfficeAddinTemplateConfig", () => {
Expand Down
Loading

0 comments on commit e5e01ea

Please sign in to comment.