Skip to content

Commit 90530f0

Browse files
authored
Merge pull request #15389 from HuihuiWu-Microsoft/fix-create-from-tdp
fix: use supportsChannelFeatures value from template as DP is not awa…
2 parents de6ee05 + 42368d5 commit 90530f0

File tree

4 files changed

+316
-0
lines changed

4 files changed

+316
-0
lines changed

packages/fx-core/src/component/developerPortalScaffoldUtils.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,12 @@ async function updateManifest(
205205
manifest.permissions = existingManifestTemplate.permissions;
206206
manifest.validDomains = existingManifestTemplate.validDomains;
207207
manifest.webApplicationInfo = existingManifestTemplate.webApplicationInfo;
208+
if (
209+
existingManifestTemplate.supportsChannelFeatures &&
210+
"supportsChannelFeatures" in appDefinition
211+
) {
212+
manifest.supportsChannelFeatures = existingManifestTemplate.supportsChannelFeatures;
213+
}
208214
}
209215

210216
// manifest: developer

packages/fx-core/src/component/driver/teamsApp/interfaces/appdefinitions/appDefinition.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,4 +92,5 @@ export interface AppDefinition {
9292
publisherDocsUrl?: string;
9393
hasPreviewFeature?: boolean;
9494
localizationInfo?: LocalizationInfo;
95+
supportsChannelFeatures?: string;
9596
}

packages/fx-core/tests/component/developerPortalScaffoldUtils.test.ts

Lines changed: 304 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1346,6 +1346,310 @@ describe("developPortalScaffoldUtils", () => {
13461346

13471347
chai.assert.isTrue(res.isErr());
13481348
});
1349+
1350+
it("should copy supportsChannelFeatures when both conditions are met", async () => {
1351+
const ctx = createContext();
1352+
ctx.tokenProvider = {
1353+
m365TokenProvider: new MockedM365Provider(),
1354+
azureAccountProvider: new MockedAzureAccountProvider(),
1355+
};
1356+
ctx.projectPath = "project-path";
1357+
const appDefinition: AppDefinition = {
1358+
appId: "mock-app-id",
1359+
teamsAppId: "mock-app-id",
1360+
supportsChannelFeatures: "tier1", // Property exists in appDefinition
1361+
};
1362+
const inputs: Inputs = {
1363+
platform: Platform.VSCode,
1364+
projectPath: "project-path",
1365+
};
1366+
const manifest: TeamsAppManifest = {
1367+
manifestVersion: "version",
1368+
id: "mock-app-id",
1369+
name: { short: "short-name" },
1370+
description: { short: "", full: "" },
1371+
version: "version",
1372+
icons: { outline: "outline.png", color: "color.png" },
1373+
accentColor: "#ffffff",
1374+
developer: {
1375+
privacyUrl: "",
1376+
websiteUrl: "",
1377+
termsOfUseUrl: "",
1378+
name: "",
1379+
},
1380+
};
1381+
1382+
const manifestTemplate: TeamsAppManifest = {
1383+
manifestVersion: "version",
1384+
id: "mock-app-id",
1385+
name: { short: "short-name" },
1386+
description: { short: "", full: "" },
1387+
version: "version",
1388+
icons: { outline: "outline.png", color: "color.png" },
1389+
accentColor: "#ffffff",
1390+
developer: {
1391+
privacyUrl: "",
1392+
websiteUrl: "",
1393+
termsOfUseUrl: "",
1394+
name: "",
1395+
},
1396+
supportsChannelFeatures: "tier1" as any, // Exists in existing manifest template
1397+
};
1398+
1399+
let updatedManifestData = "";
1400+
1401+
sandbox.stub(pathUtils, "getEnvFilePath").resolves(ok("fake env path"));
1402+
sandbox.stub(fs, "pathExists").resolves(true);
1403+
sandbox.stub(appStudio, "getAppPackage").resolves(
1404+
ok({
1405+
manifest: Buffer.from(JSON.stringify(manifest)),
1406+
icons: { color: Buffer.from(""), outline: Buffer.from("") },
1407+
languages: {},
1408+
})
1409+
);
1410+
sandbox.stub(fs, "writeFile").callsFake((file: number | fs.PathLike, data: any) => {
1411+
if (file === path.join(ctx.projectPath!, "appPackage", "manifest.json")) {
1412+
updatedManifestData = data;
1413+
}
1414+
});
1415+
1416+
sandbox.stub(envUtil, "writeEnv").resolves(ok(undefined));
1417+
sandbox.stub(manifestUtils, "_readAppManifest").resolves(ok(manifestTemplate));
1418+
1419+
const res = await developerPortalScaffoldUtils.updateFilesForTdp(ctx, appDefinition, inputs);
1420+
1421+
chai.assert.isTrue(res.isOk());
1422+
const updatedManifest = JSON.parse(updatedManifestData) as TeamsAppManifest;
1423+
chai.assert.equal(updatedManifest.supportsChannelFeatures, "tier1");
1424+
});
1425+
1426+
it("should not copy supportsChannelFeatures when property missing from appDefinition", async () => {
1427+
const ctx = createContext();
1428+
ctx.tokenProvider = {
1429+
m365TokenProvider: new MockedM365Provider(),
1430+
azureAccountProvider: new MockedAzureAccountProvider(),
1431+
};
1432+
ctx.projectPath = "project-path";
1433+
const appDefinition: AppDefinition = {
1434+
appId: "mock-app-id",
1435+
teamsAppId: "mock-app-id",
1436+
// No supportsChannelFeatures property in appDefinition
1437+
};
1438+
const inputs: Inputs = {
1439+
platform: Platform.VSCode,
1440+
projectPath: "project-path",
1441+
};
1442+
const manifest: TeamsAppManifest = {
1443+
manifestVersion: "version",
1444+
id: "mock-app-id",
1445+
name: { short: "short-name" },
1446+
description: { short: "", full: "" },
1447+
version: "version",
1448+
icons: { outline: "outline.png", color: "color.png" },
1449+
accentColor: "#ffffff",
1450+
developer: {
1451+
privacyUrl: "",
1452+
websiteUrl: "",
1453+
termsOfUseUrl: "",
1454+
name: "",
1455+
},
1456+
};
1457+
1458+
const manifestTemplate: TeamsAppManifest = {
1459+
manifestVersion: "version",
1460+
id: "mock-app-id",
1461+
name: { short: "short-name" },
1462+
description: { short: "", full: "" },
1463+
version: "version",
1464+
icons: { outline: "outline.png", color: "color.png" },
1465+
accentColor: "#ffffff",
1466+
developer: {
1467+
privacyUrl: "",
1468+
websiteUrl: "",
1469+
termsOfUseUrl: "",
1470+
name: "",
1471+
},
1472+
supportsChannelFeatures: "tier1" as any, // Exists in existing manifest template
1473+
};
1474+
1475+
let updatedManifestData = "";
1476+
1477+
sandbox.stub(pathUtils, "getEnvFilePath").resolves(ok("fake env path"));
1478+
sandbox.stub(fs, "pathExists").resolves(true);
1479+
sandbox.stub(appStudio, "getAppPackage").resolves(
1480+
ok({
1481+
manifest: Buffer.from(JSON.stringify(manifest)),
1482+
icons: { color: Buffer.from(""), outline: Buffer.from("") },
1483+
languages: {},
1484+
})
1485+
);
1486+
sandbox.stub(fs, "writeFile").callsFake((file: number | fs.PathLike, data: any) => {
1487+
if (file === path.join(ctx.projectPath!, "appPackage", "manifest.json")) {
1488+
updatedManifestData = data;
1489+
}
1490+
});
1491+
1492+
sandbox.stub(envUtil, "writeEnv").resolves(ok(undefined));
1493+
sandbox.stub(manifestUtils, "_readAppManifest").resolves(ok(manifestTemplate));
1494+
1495+
const res = await developerPortalScaffoldUtils.updateFilesForTdp(ctx, appDefinition, inputs);
1496+
1497+
chai.assert.isTrue(res.isOk());
1498+
const updatedManifest = JSON.parse(updatedManifestData) as TeamsAppManifest;
1499+
chai.assert.isUndefined(updatedManifest.supportsChannelFeatures);
1500+
});
1501+
1502+
it("should not copy supportsChannelFeatures when property missing from manifestTemplate", async () => {
1503+
const ctx = createContext();
1504+
ctx.tokenProvider = {
1505+
m365TokenProvider: new MockedM365Provider(),
1506+
azureAccountProvider: new MockedAzureAccountProvider(),
1507+
};
1508+
ctx.projectPath = "project-path";
1509+
const appDefinition: AppDefinition = {
1510+
appId: "mock-app-id",
1511+
teamsAppId: "mock-app-id",
1512+
supportsChannelFeatures: "tier1", // Property exists in appDefinition
1513+
};
1514+
const inputs: Inputs = {
1515+
platform: Platform.VSCode,
1516+
projectPath: "project-path",
1517+
};
1518+
const manifest: TeamsAppManifest = {
1519+
manifestVersion: "version",
1520+
id: "mock-app-id",
1521+
name: { short: "short-name" },
1522+
description: { short: "", full: "" },
1523+
version: "version",
1524+
icons: { outline: "outline.png", color: "color.png" },
1525+
accentColor: "#ffffff",
1526+
developer: {
1527+
privacyUrl: "",
1528+
websiteUrl: "",
1529+
termsOfUseUrl: "",
1530+
name: "",
1531+
},
1532+
};
1533+
1534+
const manifestTemplate: TeamsAppManifest = {
1535+
manifestVersion: "version",
1536+
id: "mock-app-id",
1537+
name: { short: "short-name" },
1538+
description: { short: "", full: "" },
1539+
version: "version",
1540+
icons: { outline: "outline.png", color: "color.png" },
1541+
accentColor: "#ffffff",
1542+
developer: {
1543+
privacyUrl: "",
1544+
websiteUrl: "",
1545+
termsOfUseUrl: "",
1546+
name: "",
1547+
},
1548+
// No supportsChannelFeatures in manifestTemplate
1549+
};
1550+
1551+
let updatedManifestData = "";
1552+
1553+
sandbox.stub(pathUtils, "getEnvFilePath").resolves(ok("fake env path"));
1554+
sandbox.stub(fs, "pathExists").resolves(true);
1555+
sandbox.stub(appStudio, "getAppPackage").resolves(
1556+
ok({
1557+
manifest: Buffer.from(JSON.stringify(manifest)),
1558+
icons: { color: Buffer.from(""), outline: Buffer.from("") },
1559+
languages: {},
1560+
})
1561+
);
1562+
sandbox.stub(fs, "writeFile").callsFake((file: number | fs.PathLike, data: any) => {
1563+
if (file === path.join(ctx.projectPath!, "appPackage", "manifest.json")) {
1564+
updatedManifestData = data;
1565+
}
1566+
});
1567+
1568+
sandbox.stub(envUtil, "writeEnv").resolves(ok(undefined));
1569+
sandbox.stub(manifestUtils, "_readAppManifest").resolves(ok(manifestTemplate));
1570+
1571+
const res = await developerPortalScaffoldUtils.updateFilesForTdp(ctx, appDefinition, inputs);
1572+
1573+
chai.assert.isTrue(res.isOk());
1574+
const updatedManifest = JSON.parse(updatedManifestData) as TeamsAppManifest;
1575+
chai.assert.isUndefined(updatedManifest.supportsChannelFeatures);
1576+
});
1577+
1578+
it("should not copy supportsChannelFeatures when neither condition is met", async () => {
1579+
const ctx = createContext();
1580+
ctx.tokenProvider = {
1581+
m365TokenProvider: new MockedM365Provider(),
1582+
azureAccountProvider: new MockedAzureAccountProvider(),
1583+
};
1584+
ctx.projectPath = "project-path";
1585+
const appDefinition: AppDefinition = {
1586+
appId: "mock-app-id",
1587+
teamsAppId: "mock-app-id",
1588+
// No supportsChannelFeatures property in appDefinition
1589+
};
1590+
const inputs: Inputs = {
1591+
platform: Platform.VSCode,
1592+
projectPath: "project-path",
1593+
};
1594+
const manifest: TeamsAppManifest = {
1595+
manifestVersion: "version",
1596+
id: "mock-app-id",
1597+
name: { short: "short-name" },
1598+
description: { short: "", full: "" },
1599+
version: "version",
1600+
icons: { outline: "outline.png", color: "color.png" },
1601+
accentColor: "#ffffff",
1602+
developer: {
1603+
privacyUrl: "",
1604+
websiteUrl: "",
1605+
termsOfUseUrl: "",
1606+
name: "",
1607+
},
1608+
};
1609+
1610+
const manifestTemplate: TeamsAppManifest = {
1611+
manifestVersion: "version",
1612+
id: "mock-app-id",
1613+
name: { short: "short-name" },
1614+
description: { short: "", full: "" },
1615+
version: "version",
1616+
icons: { outline: "outline.png", color: "color.png" },
1617+
accentColor: "#ffffff",
1618+
developer: {
1619+
privacyUrl: "",
1620+
websiteUrl: "",
1621+
termsOfUseUrl: "",
1622+
name: "",
1623+
},
1624+
// No supportsChannelFeatures in manifestTemplate either
1625+
};
1626+
1627+
let updatedManifestData = "";
1628+
1629+
sandbox.stub(pathUtils, "getEnvFilePath").resolves(ok("fake env path"));
1630+
sandbox.stub(fs, "pathExists").resolves(true);
1631+
sandbox.stub(appStudio, "getAppPackage").resolves(
1632+
ok({
1633+
manifest: Buffer.from(JSON.stringify(manifest)),
1634+
icons: { color: Buffer.from(""), outline: Buffer.from("") },
1635+
languages: {},
1636+
})
1637+
);
1638+
sandbox.stub(fs, "writeFile").callsFake((file: number | fs.PathLike, data: any) => {
1639+
if (file === path.join(ctx.projectPath!, "appPackage", "manifest.json")) {
1640+
updatedManifestData = data;
1641+
}
1642+
});
1643+
1644+
sandbox.stub(envUtil, "writeEnv").resolves(ok(undefined));
1645+
sandbox.stub(manifestUtils, "_readAppManifest").resolves(ok(manifestTemplate));
1646+
1647+
const res = await developerPortalScaffoldUtils.updateFilesForTdp(ctx, appDefinition, inputs);
1648+
1649+
chai.assert.isTrue(res.isOk());
1650+
const updatedManifest = JSON.parse(updatedManifestData) as TeamsAppManifest;
1651+
chai.assert.isUndefined(updatedManifest.supportsChannelFeatures);
1652+
});
13491653
});
13501654

13511655
describe("getProjectTypeAndCapability", () => {

packages/manifest/src/manifest.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -581,6 +581,11 @@ export class TeamsAppManifest {
581581
* A value indicating whether or not show loading indicator when app/tab is loading
582582
*/
583583
showLoadingIndicator?: boolean;
584+
/**
585+
* A property in the app manifest that declares support for all channel features,
586+
* categorized by tiers.
587+
*/
588+
supportsChannelFeatures?: "tier1";
584589
/**
585590
* A value indicating whether a personal app is rendered without a tab header-bar
586591
*/

0 commit comments

Comments
 (0)