Skip to content

Commit

Permalink
perf(spec-parser): type b oauth/api key auth support (#11391)
Browse files Browse the repository at this point in the history
* perf(spec-parser): type b oauth/api key auth support

* perf: update test case

---------

Co-authored-by: rentu <rentu@microsoft.com>
  • Loading branch information
SLdragon and SLdragon authored Apr 18, 2024
1 parent ddb8cf5 commit 1290774
Show file tree
Hide file tree
Showing 7 changed files with 557 additions and 63 deletions.
35 changes: 30 additions & 5 deletions packages/spec-parser/src/manifestUpdater.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
FunctionObject,
FunctionParameters,
FunctionParameter,
AuthObject,
} from "@microsoft/teams-manifest";
import { AdaptiveCardGenerator } from "./adaptiveCardGenerator";
import { wrapResponseSemantics } from "./adaptiveCardWrapper";
Expand All @@ -34,7 +35,8 @@ export class ManifestUpdater {
outputSpecPath: string,
apiPluginFilePath: string,
spec: OpenAPIV3.Document,
options: ParseOptions
options: ParseOptions,
authInfo?: AuthInfo
): Promise<[TeamsAppManifest, PluginManifestSchema]> {
const manifest: TeamsAppManifest = await fs.readJSON(manifestPath);
const apiPluginRelativePath = ManifestUpdater.getRelativePath(manifestPath, apiPluginFilePath);
Expand All @@ -55,6 +57,7 @@ export class ManifestUpdater {
specRelativePath,
apiPluginFilePath,
appName,
authInfo,
options
);

Expand Down Expand Up @@ -100,6 +103,7 @@ export class ManifestUpdater {
specRelativePath: string,
apiPluginFilePath: string,
appName: string,
authInfo: AuthInfo | undefined,
options: ParseOptions
): Promise<PluginManifestSchema> {
const functions: FunctionObject[] = [];
Expand All @@ -108,6 +112,24 @@ export class ManifestUpdater {

const paths = spec.paths;

const pluginAuthObj: AuthObject = {
type: "None",
};

if (authInfo) {
if (Utils.isOAuthWithAuthCodeFlow(authInfo.authScheme)) {
pluginAuthObj.type = "OAuthPluginVault";
} else if (Utils.isBearerTokenAuth(authInfo.authScheme)) {
pluginAuthObj.type = "ApiKeyPluginVault";
}

if (pluginAuthObj.type !== "None") {
pluginAuthObj.reference_id = `${Utils.getSafeRegistrationIdEnvName(
authInfo.name
)}_REGISTRATION_ID`;
}
}

for (const pathUrl in paths) {
const pathItem = paths[pathUrl];
if (pathItem) {
Expand Down Expand Up @@ -230,13 +252,16 @@ export class ManifestUpdater {
}

apiPlugin.runtimes = apiPlugin.runtimes || [];
const index = apiPlugin.runtimes.findIndex((runtime) => runtime.spec.url === specRelativePath);
const index = apiPlugin.runtimes.findIndex(
(runtime) =>
runtime.spec.url === specRelativePath &&
runtime.type === "OpenApi" &&
(runtime.auth?.type ?? "None") === pluginAuthObj.type
);
if (index === -1) {
apiPlugin.runtimes.push({
type: "OpenApi",
auth: {
type: "None",
},
auth: pluginAuthObj,
spec: {
url: specRelativePath,
},
Expand Down
64 changes: 22 additions & 42 deletions packages/spec-parser/src/specParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import path from "path";
import {
APIInfo,
APIMap,
AuthInfo,
ErrorResult,
ErrorType,
GenerateResult,
Expand Down Expand Up @@ -274,13 +273,9 @@ export class SpecParser {
const newUnResolvedSpec = newSpecs[0];
const newSpec = newSpecs[1];

let resultStr;
if (outputSpecPath.endsWith(".yaml") || outputSpecPath.endsWith(".yml")) {
resultStr = jsyaml.dump(newUnResolvedSpec);
} else {
resultStr = JSON.stringify(newUnResolvedSpec, null, 2);
}
await fs.outputFile(outputSpecPath, resultStr);
const authInfo = Utils.getAuthInfo(newSpec);

await this.saveFilterSpec(outputSpecPath, newUnResolvedSpec);

if (signal?.aborted) {
throw new SpecParserError(ConstantString.CancelledMessage, ErrorType.Cancelled);
Expand All @@ -291,7 +286,8 @@ export class SpecParser {
outputSpecPath,
pluginFilePath,
newSpec,
this.options
this.options,
authInfo
);

await fs.outputJSON(manifestPath, updatedManifest, { spaces: 2 });
Expand Down Expand Up @@ -328,42 +324,13 @@ export class SpecParser {
const newSpecs = await this.getFilteredSpecs(filter, signal);
const newUnResolvedSpec = newSpecs[0];
const newSpec = newSpecs[1];
let authInfo = undefined;

let hasMultipleAuth = false;
let authInfo: AuthInfo | undefined = undefined;

for (const url in newSpec.paths) {
for (const method in newSpec.paths[url]) {
const operation = (newSpec.paths[url] as any)[method] as OpenAPIV3.OperationObject;

const authArray = Utils.getAuthArray(operation.security, newSpec);

if (authArray && authArray.length > 0) {
const currentAuth = authArray[0][0];
if (!authInfo) {
authInfo = authArray[0][0];
} else if (authInfo.name !== currentAuth.name) {
hasMultipleAuth = true;
break;
}
}
}
if (this.options.projectType === ProjectType.SME) {
authInfo = Utils.getAuthInfo(newSpec);
}

if (hasMultipleAuth && this.options.projectType !== ProjectType.TeamsAi) {
throw new SpecParserError(
ConstantString.MultipleAuthNotSupported,
ErrorType.MultipleAuthNotSupported
);
}

let resultStr;
if (outputSpecPath.endsWith(".yaml") || outputSpecPath.endsWith(".yml")) {
resultStr = jsyaml.dump(newUnResolvedSpec);
} else {
resultStr = JSON.stringify(newUnResolvedSpec, null, 2);
}
await fs.outputFile(outputSpecPath, resultStr);
await this.saveFilterSpec(outputSpecPath, newUnResolvedSpec);

if (adaptiveCardFolder) {
for (const url in newSpec.paths) {
Expand Down Expand Up @@ -449,4 +416,17 @@ export class SpecParser {
this.validator = validator;
return validator;
}

private async saveFilterSpec(
outputSpecPath: string,
unResolvedSpec: OpenAPIV3.Document
): Promise<void> {
let resultStr;
if (outputSpecPath.endsWith(".yaml") || outputSpecPath.endsWith(".yml")) {
resultStr = jsyaml.dump(unResolvedSpec);
} else {
resultStr = JSON.stringify(unResolvedSpec, null, 2);
}
await fs.outputFile(outputSpecPath, resultStr);
}
}
41 changes: 28 additions & 13 deletions packages/spec-parser/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,10 @@
"use strict";

import { OpenAPIV3 } from "openapi-types";
import SwaggerParser from "@apidevtools/swagger-parser";
import { ConstantString } from "./constants";
import {
APIMap,
AuthInfo,
ErrorResult,
ErrorType,
ParseOptions,
ProjectType,
ValidateResult,
ValidationStatus,
WarningResult,
WarningType,
} from "./interfaces";
import { AuthInfo, ErrorResult, ErrorType, ParseOptions } from "./interfaces";
import { IMessagingExtensionCommand, IParameter } from "@microsoft/teams-manifest";
import { SpecParserError } from "./specParserError";

export class Utils {
static hasNestedObjectInSchema(schema: OpenAPIV3.SchemaObject): boolean {
Expand Down Expand Up @@ -84,6 +73,32 @@ export class Utils {
return result;
}

static getAuthInfo(spec: OpenAPIV3.Document): AuthInfo | undefined {
let authInfo: AuthInfo | undefined = undefined;

for (const url in spec.paths) {
for (const method in spec.paths[url]) {
const operation = (spec.paths[url] as any)[method] as OpenAPIV3.OperationObject;

const authArray = Utils.getAuthArray(operation.security, spec);

if (authArray && authArray.length > 0) {
const currentAuth = authArray[0][0];
if (!authInfo) {
authInfo = authArray[0][0];
} else if (authInfo.name !== currentAuth.name) {
throw new SpecParserError(
ConstantString.MultipleAuthNotSupported,
ErrorType.MultipleAuthNotSupported
);
}
}
}
}

return authInfo;
}

static updateFirstLetter(str: string): string {
return str.charAt(0).toUpperCase() + str.slice(1);
}
Expand Down
1 change: 0 additions & 1 deletion packages/spec-parser/test/adaptiveCardWrapper.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import {
} from "../src/adaptiveCardWrapper";
import { AdaptiveCard } from "../src/interfaces";
import { ConstantString } from "../src/constants";
import exp from "constants";

describe("adaptiveCardWrapper", () => {
afterEach(() => {
Expand Down
Loading

0 comments on commit 1290774

Please sign in to comment.