Skip to content

Commit

Permalink
perf(spec-parser): update to support type b ai-plugin (#10972)
Browse files Browse the repository at this point in the history
* perf(spec-parser): update to support ai-plugin

* perf: remove test code

* perf(spec-parser): update test cases to make codecov happy

* perf: refactor code

* perf: fix according to the comments

* perf: update code for browser

* perf: update plugin file name to path, remove state

---------

Co-authored-by: turenlong <rentu@microsoft.com>
  • Loading branch information
SLdragon and SLdragon authored Mar 7, 2024
1 parent cb60542 commit b00041d
Show file tree
Hide file tree
Showing 13 changed files with 1,942 additions and 78 deletions.
2 changes: 1 addition & 1 deletion packages/spec-parser/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"types": "dist/src/index.d.ts",
"scripts": {
"build": "rollup -c",
"test:unit:node": "nyc --no-clean -- mocha \"test/*.test.ts\" -r config/mocha.env.ts --config config/.mocharc.json",
"test:unit:node": "nyc --no-clean -- mocha -r config/mocha.env.ts --config config/.mocharc.json",
"test:unit:browser": "karma start karma.conf.cjs --single-run --unit",
"test:unit": "npm run test:unit:node && npm run test:unit:browser ",
"lint:staged": "lint-staged",
Expand Down
2 changes: 2 additions & 0 deletions packages/spec-parser/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ export class ConstantString {
static readonly MultipleAPIKeyNotSupported =
"Multiple API keys are not supported. Please make sure that all selected APIs use the same API key.";

static readonly UnsupportedSchema = "Unsupported schema in %s %s: %s";

static readonly WrappedCardVersion = "devPreview";
static readonly WrappedCardSchema =
"https://developer.microsoft.com/json-schemas/teams/vDevPreview/MicrosoftTeams.ResponseRenderingTemplate.schema.json";
Expand Down
1 change: 1 addition & 0 deletions packages/spec-parser/src/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ export interface ParseOptions {
allowAPIKeyAuth?: boolean;
allowMultipleParameters?: boolean;
allowOauth2?: boolean;
isCopilot?: boolean;
}

export interface APIInfo {
Expand Down
186 changes: 178 additions & 8 deletions packages/spec-parser/src/manifestUpdater.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,185 @@ import {
IComposeExtension,
IMessagingExtensionCommand,
TeamsAppManifest,
PluginManifestSchema,
FunctionObject,
FunctionParameters,
FunctionParameter,
} from "@microsoft/teams-manifest";

export class ManifestUpdater {
static async updateManifestWithAiPlugin(
manifestPath: string,
outputSpecPath: string,
apiPluginFilePath: string,
spec: OpenAPIV3.Document
): Promise<[TeamsAppManifest, PluginManifestSchema]> {
const manifest: TeamsAppManifest = await fs.readJSON(manifestPath);
const apiPluginRelativePath = ManifestUpdater.getRelativePath(manifestPath, apiPluginFilePath);
manifest.apiPlugins = [
{
pluginFile: apiPluginRelativePath,
},
];

ManifestUpdater.updateManifestDescription(manifest, spec);

const specRelativePath = ManifestUpdater.getRelativePath(manifestPath, outputSpecPath);
const apiPlugin = ManifestUpdater.generatePluginManifestSchema(spec, specRelativePath);

return [manifest, apiPlugin];
}

static updateManifestDescription(manifest: TeamsAppManifest, spec: OpenAPIV3.Document): void {
manifest.description = {
short: spec.info.title.slice(0, ConstantString.ShortDescriptionMaxLens),
full: (spec.info.description ?? manifest.description.full)?.slice(
0,
ConstantString.FullDescriptionMaxLens
),
};
}

static mapOpenAPISchemaToFuncParam(
schema: OpenAPIV3.SchemaObject,
method: string,
pathUrl: string
): FunctionParameter {
let parameter: FunctionParameter;
if (
schema.type === "string" ||
schema.type === "boolean" ||
schema.type === "integer" ||
schema.type === "number" ||
schema.type === "array"
) {
parameter = schema as any;
} else {
throw new SpecParserError(
Utils.format(ConstantString.UnsupportedSchema, method, pathUrl, JSON.stringify(schema)),
ErrorType.UpdateManifestFailed
);
}

return parameter;
}

static generatePluginManifestSchema(
spec: OpenAPIV3.Document,
specRelativePath: string
): PluginManifestSchema {
const functions: FunctionObject[] = [];
const functionNames: string[] = [];

const paths = spec.paths;

for (const pathUrl in paths) {
const pathItem = paths[pathUrl];
if (pathItem) {
const operations = pathItem;
for (const method in operations) {
if (ConstantString.AllOperationMethods.includes(method)) {
const operationItem = (operations as any)[method] as OpenAPIV3.OperationObject;
if (operationItem) {
const operationId = operationItem.operationId!;
const description = operationItem.description ?? "";
const paramObject = operationItem.parameters as OpenAPIV3.ParameterObject[];
const requestBody = operationItem.requestBody as OpenAPIV3.ParameterObject;

const parameters: FunctionParameters = {
type: "object",
properties: {},
required: [],
};

if (paramObject) {
for (let i = 0; i < paramObject.length; i++) {
const param = paramObject[i];

const schema = param.schema as OpenAPIV3.SchemaObject;

parameters.properties![param.name] = ManifestUpdater.mapOpenAPISchemaToFuncParam(
schema,
method,
pathUrl
);

if (param.required) {
parameters.required!.push(param.name);
}

if (!parameters.properties![param.name].description) {
parameters.properties![param.name].description = param.description ?? "";
}
}
}

if (requestBody) {
const requestJsonBody = requestBody.content!["application/json"];
const requestBodySchema = requestJsonBody.schema as OpenAPIV3.SchemaObject;

if (requestBodySchema.type === "object") {
if (requestBodySchema.required) {
parameters.required!.push(...requestBodySchema.required);
}

for (const property in requestBodySchema.properties) {
const schema = requestBodySchema.properties[property] as OpenAPIV3.SchemaObject;
parameters.properties![property] = ManifestUpdater.mapOpenAPISchemaToFuncParam(
schema,
method,
pathUrl
);
}
} else {
throw new SpecParserError(
Utils.format(
ConstantString.UnsupportedSchema,
method,
pathUrl,
JSON.stringify(requestBodySchema)
),
ErrorType.UpdateManifestFailed
);
}
}

const funcObj: FunctionObject = {
name: operationId,
description: description,
parameters: parameters,
};

functions.push(funcObj);
functionNames.push(operationId);
}
}
}
}
}

const apiPlugin: PluginManifestSchema = {
schema_version: "v2",
name_for_human: spec.info.title,
description_for_human: spec.info.description ?? "<Please add description of the plugin>",
functions: functions,
runtimes: [
{
type: "OpenApi",
auth: {
type: "none", // TODO, support auth in the future
},
spec: {
url: specRelativePath,
},
run_for_functions: functionNames,
},
],
};

return apiPlugin;
}

static async updateManifest(
manifestPath: string,
outputSpecPath: string,
Expand Down Expand Up @@ -67,14 +243,8 @@ export class ManifestUpdater {
}
}

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

updatedPart.description = originalManifest.description;
ManifestUpdater.updateManifestDescription(updatedPart, spec);
updatedPart.composeExtensions = isMe === undefined || isMe === true ? [composeExtension] : [];

const updatedManifest = { ...originalManifest, ...updatedPart };
Expand Down
6 changes: 4 additions & 2 deletions packages/spec-parser/src/specFilter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ export class SpecFilter {
allowMissingId: boolean,
allowAPIKeyAuth: boolean,
allowMultipleParameters: boolean,
allowOauth2: boolean
allowOauth2: boolean,
isCopilot: boolean
): OpenAPIV3.Document {
try {
const newSpec = { ...unResolveSpec };
Expand All @@ -33,7 +34,8 @@ export class SpecFilter {
allowMissingId,
allowAPIKeyAuth,
allowMultipleParameters,
allowOauth2
allowOauth2,
isCopilot
)
) {
continue;
Expand Down
24 changes: 22 additions & 2 deletions packages/spec-parser/src/specParser.browser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export class SpecParser {
allowAPIKeyAuth: false,
allowMultipleParameters: false,
allowOauth2: false,
isCopilot: false,
};

/**
Expand Down Expand Up @@ -92,7 +93,8 @@ export class SpecParser {
this.options.allowMissingId,
this.options.allowAPIKeyAuth,
this.options.allowMultipleParameters,
this.options.allowOauth2
this.options.allowOauth2,
this.options.isCopilot
);
} catch (err) {
throw new SpecParserError((err as Error).toString(), ErrorType.ValidateFailed);
Expand Down Expand Up @@ -163,6 +165,23 @@ export class SpecParser {
throw new Error("Method not implemented.");
}

/**
* Generates and update artifacts from the OpenAPI specification file. Generate Adaptive Cards, update Teams app manifest, and generate a new OpenAPI specification file.
* @param manifestPath A file path of the Teams app manifest file to update.
* @param filter An array of strings that represent the filters to apply when generating the artifacts. If filter is empty, it would process nothing.
* @param outputSpecPath File path of the new OpenAPI specification file to generate. If not specified or empty, no spec file will be generated.
* @param pluginFilePath File path of the api plugin file to generate.
*/
// eslint-disable-next-line @typescript-eslint/require-await
async generateForCopilot(
manifestPath: string,
filter: string[],
outputSpecPath: string,
pluginFilePath: string,
signal?: AbortSignal
): Promise<GenerateResult> {
throw new Error("Method not implemented.");
}
/**
* Generates and update artifacts from the OpenAPI specification file. Generate Adaptive Cards, update Teams app manifest, and generate a new OpenAPI specification file.
* @param manifestPath A file path of the Teams app manifest file to update.
Expand Down Expand Up @@ -206,7 +225,8 @@ export class SpecParser {
this.options.allowMissingId,
this.options.allowAPIKeyAuth,
this.options.allowMultipleParameters,
this.options.allowOauth2
this.options.allowOauth2,
this.options.isCopilot
);
this.apiMap = result;
return result;
Expand Down
Loading

0 comments on commit b00041d

Please sign in to comment.