Skip to content

Commit

Permalink
perf(spec-parser): keep at most 5 properties in response semantics (#…
Browse files Browse the repository at this point in the history
…11850)

Co-authored-by: rentu <rentu@microsoft.com>
  • Loading branch information
SLdragon and SLdragon authored Jun 18, 2024
1 parent ffc72cd commit 10789b4
Show file tree
Hide file tree
Showing 4 changed files with 245 additions and 4 deletions.
6 changes: 4 additions & 2 deletions packages/spec-parser/src/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,17 +161,19 @@ export interface ImageElement {
$when: string;
}

export type AdaptiveCardBody = Array<TextBlockElement | ImageElement | ArrayElement>;

export interface ArrayElement {
type: string;
$data: string;
items: Array<TextBlockElement | ImageElement | ArrayElement>;
items: AdaptiveCardBody;
}

export interface AdaptiveCard {
type: string;
$schema: string;
version: string;
body: Array<TextBlockElement | ImageElement | ArrayElement>;
body: AdaptiveCardBody;
}

export interface PreviewCardTemplate {
Expand Down
2 changes: 1 addition & 1 deletion packages/spec-parser/src/manifestUpdater.ts
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ export class ManifestUpdater {
const [card, jsonPath] =
AdaptiveCardGenerator.generateAdaptiveCard(operationItem);

card.body = card.body.slice(0, 5);
card.body = Utils.limitACBodyProperties(card.body, 5);
const responseSemantic = wrapResponseSemantics(card, jsonPath);
funcObj.capabilities = {
response_semantics: responseSemantic,
Expand Down
39 changes: 38 additions & 1 deletion packages/spec-parser/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,15 @@

import { OpenAPIV3 } from "openapi-types";
import { ConstantString } from "./constants";
import { AuthInfo, AuthType, ErrorResult, ErrorType, ParseOptions } from "./interfaces";
import {
AdaptiveCardBody,
ArrayElement,
AuthInfo,
AuthType,
ErrorResult,
ErrorType,
ParseOptions,
} from "./interfaces";
import { IMessagingExtensionCommand, IParameter } from "@microsoft/teams-manifest";
import { SpecParserError } from "./specParserError";

Expand Down Expand Up @@ -451,4 +459,33 @@ export class Utils {

return serverUrl;
}

static limitACBodyProperties(body: AdaptiveCardBody, maxCount: number): AdaptiveCardBody {
const result: AdaptiveCardBody = [];
let currentCount = 0;

for (const element of body) {
if (element.type === ConstantString.ContainerType) {
const items = this.limitACBodyProperties(
(element as ArrayElement).items,
maxCount - currentCount
);

result.push({
type: ConstantString.ContainerType,
$data: (element as ArrayElement).$data,
items: items,
});

currentCount += items.length;
} else {
if (currentCount < maxCount) {
result.push(element);
currentCount++;
}
}
}

return result;
}
}
202 changes: 202 additions & 0 deletions packages/spec-parser/test/manifestUpdater.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -590,6 +590,208 @@ describe("updateManifestWithAiPlugin", () => {
expect(apiPlugin).to.deep.equal(expectedPlugins);
expect(warnings).to.deep.equal([]);
});

it("should keep at most 5 properties in response semantics for complex nested properties", async () => {
const spec: any = {
openapi: "3.0.2",
info: {
title: "My API",
description: "My API description",
},
servers: [
{
url: "/v3",
},
],
paths: {
"/pets": {
get: {
operationId: "getPets",
summary: "Get all pets",
description: "Returns all pets from the system that the user has access to",
parameters: [
{
name: "limit",
description: "Maximum number of pets to return",
required: true,
schema: {
type: "integer",
},
},
],
responses: {
200: {
content: {
"application/json": {
schema: {
type: "object",
properties: {
name: {
type: "string",
},
description: {
type: "array",
items: {
type: "object",
properties: {
title: {
type: "array",
items: {
type: "string",
},
},
url: {
type: "string",
},
},
},
},
imageUrl: {
type: "string",
},
id: {
type: "string",
},
age: {
type: "string",
},
status: {
type: "string",
},
},
},
},
},
},
},
},
},
},
};
const manifestPath = "/path/to/your/manifest.json";
const outputSpecPath = "/path/to/your/spec/outputSpec.yaml";
const pluginFilePath = "/path/to/your/ai-plugin.json";

const originalManifest = {
name: { short: "Original Name", full: "Original Full Name" },
description: { short: "Original Short Description", full: "Original Full Description" },
};
const expectedManifest = {
name: { short: "Original Name", full: "Original Full Name" },
description: { short: "My API", full: "My API description" },
copilotExtensions: {
plugins: [
{
file: "ai-plugin.json",
id: "plugin_1",
},
],
},
};

const expectedPlugins: PluginManifestSchema = {
$schema: ConstantString.PluginManifestSchema,
schema_version: "v2.1",
name_for_human: "Original Name",
namespace: "originalname",
description_for_human: "My API description",
functions: [
{
name: "getPets",
description: "Returns all pets from the system that the user has access to",
capabilities: {
response_semantics: {
data_path: "$",
properties: {
subtitle: "$.id",
title: "$.name",
url: "$.imageUrl",
},
static_template: {
$schema: "http://adaptivecards.io/schemas/adaptive-card.json",
body: [
{
text: "name: ${if(name, name, 'N/A')}",
type: "TextBlock",
wrap: true,
},
{
$data: "${description}",
items: [
{
$data: "${title}",
items: [
{
text: "title: ${$data}",
type: "TextBlock",
wrap: true,
},
],
type: "Container",
},
{
text: "description.url: ${if(url, url, 'N/A')}",
type: "TextBlock",
wrap: true,
},
],
type: "Container",
},
{
$when: "${imageUrl != null}",
type: "Image",
url: "${imageUrl}",
},
{
text: "id: ${if(id, id, 'N/A')}",
type: "TextBlock",
wrap: true,
},
],
type: "AdaptiveCard",
version: "1.5",
},
},
},
},
],
runtimes: [
{
type: "OpenApi",
auth: {
type: "None",
},
spec: {
url: "spec/outputSpec.yaml",
},
run_for_functions: ["getPets"],
},
],
};
sinon.stub(fs, "readJSON").resolves(originalManifest);
sinon
.stub(fs, "pathExists")
.withArgs(manifestPath)
.resolves(true)
.withArgs(pluginFilePath)
.resolves(false);

const options: ParseOptions = {
allowMethods: ["get", "post"],
allowResponseSemantics: true,
};
const [manifest, apiPlugin, warnings] = await ManifestUpdater.updateManifestWithAiPlugin(
manifestPath,
outputSpecPath,
pluginFilePath,
spec,
options
);

expect(manifest).to.deep.equal(expectedManifest);
expect(apiPlugin).to.deep.equal(expectedPlugins);
expect(warnings).to.deep.equal([]);
});
});

describe("auth", () => {
Expand Down

0 comments on commit 10789b4

Please sign in to comment.