Skip to content

Commit

Permalink
perf(spec-parser): update manifest for sso auth (#10464)
Browse files Browse the repository at this point in the history
* perf(spec-parser): update manifest for sso auth

* perf: remove unused condition, and add a test case

---------

Co-authored-by: turenlong <rentu@microsoft.com>
  • Loading branch information
SLdragon and SLdragon authored Nov 30, 2023
1 parent 9a338f7 commit 46fbca1
Show file tree
Hide file tree
Showing 3 changed files with 192 additions and 28 deletions.
64 changes: 41 additions & 23 deletions packages/fx-core/src/common/spec-parser/manifestUpdater.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@ import { OpenAPIV3 } from "openapi-types";
import fs from "fs-extra";
import path from "path";
import { ErrorType, WarningResult } from "./interfaces";
import { getSafeRegistrationIdEnvName, parseApiInfo } from "./utils";
import {
getSafeRegistrationIdEnvName,
isAPIKeyAuth,
isBearerTokenAuth,
parseApiInfo,
} from "./utils";
import { SpecParserError } from "./specParserError";
import { ConstantString } from "./constants";
import {
Expand All @@ -21,47 +26,60 @@ export async function updateManifest(
adaptiveCardFolder: string,
spec: OpenAPIV3.Document,
allowMultipleParameters: boolean,
apiKeyAuthName?: string
auth?: OpenAPIV3.SecuritySchemeObject
): Promise<[TeamsAppManifest, WarningResult[]]> {
try {
const originalManifest: TeamsAppManifest = await fs.readJSON(manifestPath);

const updatedPart: any = {};
const [commands, warnings] = await generateCommands(
spec,
adaptiveCardFolder,
manifestPath,
allowMultipleParameters
);
const ComposeExtension: IComposeExtension = {
const composeExtension: IComposeExtension = {
composeExtensionType: "apiBased",
apiSpecificationFile: getRelativePath(manifestPath, outputSpecPath),
commands: commands,
};

if (apiKeyAuthName) {
const safeApiSecretRegistrationId = getSafeRegistrationIdEnvName(
`${apiKeyAuthName}_${ConstantString.RegistrationIdPostfix}`
);
if (auth) {
if (isAPIKeyAuth(auth)) {
auth = auth as OpenAPIV3.ApiKeySecurityScheme;
const safeApiSecretRegistrationId = getSafeRegistrationIdEnvName(
`${auth.name}_${ConstantString.RegistrationIdPostfix}`
);
(composeExtension as any).authorization = {
authType: "apiSecretServiceAuth",
apiSecretServiceAuthConfiguration: {
apiSecretRegistrationId: `\${{${safeApiSecretRegistrationId}}}`,
},
};
} else if (isBearerTokenAuth(auth)) {
(composeExtension as any).authorization = {
authType: "microsoftEntra",
microsoftEntraConfiguration: {
supportsSingleSignOn: true,
},
};

(ComposeExtension as any).authorization = {
authType: "apiSecretServiceAuth",
apiSecretServiceAuthConfiguration: {
apiSecretRegistrationId: `\${{${safeApiSecretRegistrationId}}}`,
},
};
updatedPart.webApplicationInfo = {
id: "${{AAD_APP_CLIENT_ID}}",
resource: "api://${{DOMAIN}}/${{AAD_APP_CLIENT_ID}}",
};
}
}

const updatedPart = {
description: {
short: spec.info.title.slice(0, ConstantString.ShortDescriptionMaxLens),
full: (spec.info.description ?? originalManifest.description.full)?.slice(
0,
ConstantString.FullDescriptionMaxLens
),
},
composeExtensions: [ComposeExtension],
updatedPart.description = {
short: spec.info.title.slice(0, ConstantString.ShortDescriptionMaxLens),
full: (spec.info.description ?? originalManifest.description.full)?.slice(
0,
ConstantString.FullDescriptionMaxLens
),
};

updatedPart.composeExtensions = [composeExtension];

const updatedManifest = { ...originalManifest, ...updatedPart };

return [updatedManifest, warnings];
Expand Down
2 changes: 1 addition & 1 deletion packages/fx-core/src/common/spec-parser/specParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ export class SpecParser {
adaptiveCardFolder,
newSpec,
this.options.allowMultipleParameters,
auth && auth.type === "apiKey" ? auth?.name : ""
auth
);

await fs.outputJSON(manifestPath, updatedManifest, { spaces: 2 });
Expand Down
154 changes: 150 additions & 4 deletions packages/fx-core/tests/common/spec-parser/manifestUpdater.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -461,7 +461,7 @@ describe("manifestUpdater", () => {
expect(warnings).to.deep.equal([]);
});

it("should contain auth property in manifest if pass the api key name", async () => {
it("should contain auth property in manifest if pass the api key auth", async () => {
const manifestPath = "/path/to/your/manifest.json";
const outputSpecPath = "/path/to/your/spec/outputSpec.yaml";
const adaptiveCardFolder = "/path/to/your/adaptiveCards";
Expand Down Expand Up @@ -510,14 +510,156 @@ describe("manifestUpdater", () => {
],
};
const readJSONStub = sinon.stub(fs, "readJSON").resolves(originalManifest);
const apiKeyAuth = {
type: "apiKey" as const,
name: "api_key_name",
in: "header",
};
const [result, warnings] = await updateManifest(
manifestPath,
outputSpecPath,
adaptiveCardFolder,
spec,
false,
apiKeyAuth
);

expect(result).to.deep.equal(expectedManifest);
expect(warnings).to.deep.equal([]);
});

it("should contain auth property in manifest if pass the sso auth", async () => {
const manifestPath = "/path/to/your/manifest.json";
const outputSpecPath = "/path/to/your/spec/outputSpec.yaml";
const adaptiveCardFolder = "/path/to/your/adaptiveCards";
sinon.stub(fs, "pathExists").resolves(true);
const originalManifest = {
name: { short: "Original Name", full: "Original Full Name" },
description: { short: "Original Short Description", full: "Original Full Description" },
composeExtensions: [],
};
const expectedManifest = {
name: { short: "Original Name", full: "Original Full Name" },
description: { short: spec.info.title, full: spec.info.description },
composeExtensions: [
{
composeExtensionType: "apiBased",
apiSpecificationFile: "spec/outputSpec.yaml",
authorization: {
authType: "microsoftEntra",
microsoftEntraConfiguration: {
supportsSingleSignOn: true,
},
},
commands: [
{
context: ["compose"],
type: "query",
title: "Get all pets",
description: "Returns all pets from the system that the user has access to",
id: "getPets",
parameters: [
{ name: "limit", title: "Limit", description: "Maximum number of pets to return" },
],
apiResponseRenderingTemplateFile: "adaptiveCards/getPets.json",
},
{
context: ["compose"],
type: "query",
title: "Create a pet",
description: "Create a new pet in the store",
id: "createPet",
parameters: [{ name: "name", title: "Name", description: "Name of the pet" }],
apiResponseRenderingTemplateFile: "adaptiveCards/createPet.json",
},
],
},
],
webApplicationInfo: {
id: "${{AAD_APP_CLIENT_ID}}",
resource: "api://${{DOMAIN}}/${{AAD_APP_CLIENT_ID}}",
},
};
const readJSONStub = sinon.stub(fs, "readJSON").resolves(originalManifest);
const oauth2 = {
type: "oauth2" as const,
flows: {
implicit: {
authorizationUrl: "https://example.com/api/oauth/dialog",
scopes: {
"write:pets": "modify pets in your account",
"read:pets": "read your pets",
},
},
},
};
const [result, warnings] = await updateManifest(
manifestPath,
outputSpecPath,
adaptiveCardFolder,
spec,
false,
"api_key_name"
oauth2
);

expect(result).to.deep.equal(expectedManifest);
expect(warnings).to.deep.equal([]);
});

it("should not contain auth property in manifest if pass the unknown auth", async () => {
const manifestPath = "/path/to/your/manifest.json";
const outputSpecPath = "/path/to/your/spec/outputSpec.yaml";
const adaptiveCardFolder = "/path/to/your/adaptiveCards";
sinon.stub(fs, "pathExists").resolves(true);
const originalManifest = {
name: { short: "Original Name", full: "Original Full Name" },
description: { short: "Original Short Description", full: "Original Full Description" },
composeExtensions: [],
};
const expectedManifest = {
name: { short: "Original Name", full: "Original Full Name" },
description: { short: spec.info.title, full: spec.info.description },
composeExtensions: [
{
composeExtensionType: "apiBased",
apiSpecificationFile: "spec/outputSpec.yaml",
commands: [
{
context: ["compose"],
type: "query",
title: "Get all pets",
description: "Returns all pets from the system that the user has access to",
id: "getPets",
parameters: [
{ name: "limit", title: "Limit", description: "Maximum number of pets to return" },
],
apiResponseRenderingTemplateFile: "adaptiveCards/getPets.json",
},
{
context: ["compose"],
type: "query",
title: "Create a pet",
description: "Create a new pet in the store",
id: "createPet",
parameters: [{ name: "name", title: "Name", description: "Name of the pet" }],
apiResponseRenderingTemplateFile: "adaptiveCards/createPet.json",
},
],
},
],
};
const readJSONStub = sinon.stub(fs, "readJSON").resolves(originalManifest);
const basicAuth = {
type: "http" as const,
scheme: "basic",
};
const [result, warnings] = await updateManifest(
manifestPath,
outputSpecPath,
adaptiveCardFolder,
spec,
false,
basicAuth
);

expect(result).to.deep.equal(expectedManifest);
Expand Down Expand Up @@ -573,14 +715,18 @@ describe("manifestUpdater", () => {
],
};
const readJSONStub = sinon.stub(fs, "readJSON").resolves(originalManifest);

const apiKeyAuth = {
type: "apiKey" as const,
name: "*api-key_name",
in: "header",
};
const [result, warnings] = await updateManifest(
manifestPath,
outputSpecPath,
adaptiveCardFolder,
spec,
false,
"*api-key_name"
apiKeyAuth
);

expect(result).to.deep.equal(expectedManifest);
Expand Down

0 comments on commit 46fbca1

Please sign in to comment.