Skip to content

Commit

Permalink
refactor: support api key in scaffold
Browse files Browse the repository at this point in the history
refactor: new template

test: ut

refactor: minor

test: ut
  • Loading branch information
yuqizhou77 committed Nov 1, 2023
1 parent dab103f commit f2aec0b
Show file tree
Hide file tree
Showing 20 changed files with 519 additions and 57 deletions.
7 changes: 4 additions & 3 deletions packages/api/review/teamsfx-api.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,16 @@ import { TokenCredential } from '@azure/core-auth';
// @public (undocumented)
export interface ApiOperation {
// (undocumented)
authName?: string;
data: {
serverUrl: string;
authName?: string;
};
// (undocumented)
groupName: string;
// (undocumented)
id: string;
// (undocumented)
label: string;
// (undocumented)
serverUrl: string;
}

// @public (undocumented)
Expand Down
6 changes: 4 additions & 2 deletions packages/api/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,8 +145,10 @@ export interface ApiOperation {
id: string;
label: string;
groupName: string;
serverUrl: string;
authName?: string;
data: {
serverUrl: string;
authName?: string;
};
}

export interface Warning {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
ResponseTemplatesFolderName,
AppPackageFolderName,
Warning,
ApiOperation,
} from "@microsoft/teamsfx-api";
import { Generator } from "../generator";
import path from "path";
Expand All @@ -41,17 +42,19 @@ import { ProgrammingLanguage } from "../../../question/create";
import * as fs from "fs-extra";
import { assembleError } from "../../../error";
import {
ConstantString,
SpecParserError,
SpecParser,
ValidationStatus,
WarningType,
} from "../../../common/spec-parser";
import * as util from "util";
import { isValidHttpUrl } from "../../../question/util";
import { isApiKeyEnabled } from "../../../common/featureFlags";

const fromApiSpeccomponentName = "copilot-plugin-existing-api";
const fromApiSpecComponentName = "copilot-plugin-existing-api";
const fromApiSpecTemplateName = "copilot-plugin-existing-api";
const fromApiSpecWithApiKeyComponentName = "copilot-plugin-existing-api-api-key";
const fromApiSpecWithApiKeyTemplateName = "copilot-plugin-existing-api-api-key";
const fromOpenAIPlugincomponentName = "copilot-plugin-from-oai-plugin";
const fromOpenAIPluginTemplateName = "copilot-plugin-from-oai-plugin";
const apiSpecFolderName = "apiSpecificationFiles";
Expand All @@ -70,22 +73,25 @@ export class CopilotPluginGenerator {
@hooks([
ActionExecutionMW({
enableTelemetry: true,
telemetryComponentName: fromApiSpeccomponentName,
telemetryComponentName: fromApiSpecComponentName,
telemetryEventName: TelemetryEvents.Generate,
errorSource: fromApiSpeccomponentName,
errorSource: fromApiSpecComponentName,
}),
])
public static async generateFromApiSpec(
context: Context,
inputs: Inputs,
destinationPath: string
): Promise<Result<CopilotPluginGeneratorResult, FxError>> {
const hasAuth = (inputs[QuestionNames.ApiOperation] as ApiOperation[]).find(
(api) => !!api.data.authName
);
return await this.generateForME(
context,
inputs,
destinationPath,
fromApiSpecTemplateName,
fromApiSpeccomponentName
hasAuth ? fromApiSpecWithApiKeyTemplateName : fromApiSpecTemplateName,
hasAuth ? fromApiSpecWithApiKeyComponentName : fromApiSpecComponentName
);
}

Expand Down Expand Up @@ -124,7 +130,8 @@ export class CopilotPluginGenerator {
const safeProjectNameFromVS =
language === "csharp" ? inputs[QuestionNames.SafeProjectName] : undefined;
context.templateVariables = Generator.getDefaultVariables(appName, safeProjectNameFromVS);
const filters = inputs[QuestionNames.ApiOperation] as string[];
const apiOperations = inputs[QuestionNames.ApiOperation] as ApiOperation[];
const filters = apiOperations.map((api) => api.id);
// download template
const templateRes = await Generator.generateTemplate(
context,
Expand All @@ -141,7 +148,8 @@ export class CopilotPluginGenerator {
});

// validate API spec
const specParser = new SpecParser(url);
const allowAPIKeyAuth = isApiKeyEnabled();
const specParser = new SpecParser(url, { allowAPIKeyAuth });
const validationRes = await specParser.validate();
const warnings = validationRes.warnings;
const operationIdWarning = warnings.find((w) => w.type === WarningType.OperationIdMissing);
Expand Down
14 changes: 9 additions & 5 deletions packages/fx-core/src/component/generator/copilotPlugin/helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import { EOL } from "os";
import { SummaryConstant } from "../../configManager/constant";
import { manifestUtils } from "../../driver/teamsApp/utils/ManifestUtils";
import path from "path";
import { isApiKeyEnabled } from "../../../common/featureFlags";

const manifestFilePath = "/.well-known/ai-plugin.json";
const componentName = "OpenAIPluginManifestHelper";
Expand Down Expand Up @@ -152,7 +153,8 @@ export async function listOperations(
}

try {
const specParser = new SpecParser(apiSpecUrl!);
const allowAPIKeyAuth = isApiKeyEnabled();
const specParser = new SpecParser(apiSpecUrl as string, { allowAPIKeyAuth });
const validationRes = await specParser.validate();
validationRes.errors = formatValidationErrors(validationRes.errors);

Expand Down Expand Up @@ -219,11 +221,13 @@ function sortOperations(operations: ListAPIResult[]): ApiOperation[] {
id: operation.api,
label: operation.api,
groupName: arr[0],
serverUrl: operation.server,
data: {
serverUrl: operation.server,
},
};

if (operation.auth) {
result.authName = operation.auth.name;
result.data.authName = operation.auth.name;
}
operationsWithSeparator.push(result);
}
Expand Down Expand Up @@ -576,13 +580,13 @@ function formatValidationErrorContent(error: ApiSpecErrorResult): string {
try {
switch (error.type) {
case ErrorType.SpecNotValid: {
let content = error.content;
let content: string = error.content;
if (error.content.startsWith("ResolverError: Error downloading")) {
content = error.content
.split("\n")
.map((o) => o.trim())
.join(". ");
content = content + ". " + getLocalizedString("core.common.ErrorFetchApiSpec");
content = `${content}. ${getLocalizedString("core.common.ErrorFetchApiSpec")}`;
}
return content;
}
Expand Down
3 changes: 2 additions & 1 deletion packages/fx-core/src/core/FxCore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ import {
} from "./middleware/utils/v3MigrationUtils";
import { CoreTelemetryComponentName, CoreTelemetryEvent, CoreTelemetryProperty } from "./telemetry";
import { CoreHookContext, PreProvisionResForVS, VersionCheckRes } from "./types";
import { isApiKeyEnabled } from "../common/featureFlags";
import "../component/feature/sso";

export type CoreCallbackFunc = (name: string, err?: FxError, data?: any) => void | Promise<void>;
Expand Down Expand Up @@ -1162,7 +1163,7 @@ export class FxCore {
const outputAPISpecPath = path.join(path.dirname(manifestPath), apiSpecificationFile!);

// Merge exisiting operations in manifest.json
const specParser = new SpecParser(url);
const specParser = new SpecParser(url, { allowAPIKeyAuth: isApiKeyEnabled() });
const existingOperationIds = manifestUtils.getOperationIds(manifestRes.value);
const operationMaps = await specParser.listOperationMap();

Expand Down
7 changes: 4 additions & 3 deletions packages/fx-core/src/question/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1551,6 +1551,7 @@ export function apiOperationQuestion(includeExistingAPIs = true): MultiSelectQue
return {
type: "multiSelect",
name: QuestionNames.ApiOperation,
returnObject: true,
title: getLocalizedString("core.createProjectQuestion.apiSpec.operation.title"),
cliDescription: "Select Operation(s) Teams Can Interact with.",
cliShortName: "o",
Expand All @@ -1575,10 +1576,10 @@ export function apiOperationQuestion(includeExistingAPIs = true): MultiSelectQue
for (const inputItem of input) {
const operation = operations.find((op) => op.id === inputItem);
if (operation) {
if (operation.authName) {
authNames.add(operation.authName);
if (operation.data.authName) {
authNames.add(operation.data.authName);
}
serverUrls.add(operation.serverUrl);
serverUrls.add(operation.data.serverUrl);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,16 @@ describe("copilotPluginGenerator", function () {
platform: Platform.VSCode,
projectPath: "path",
[QuestionNames.ApiSpecLocation]: "https://test.com",
[QuestionNames.ApiOperation]: [
{
id: "operation1",
label: "operation1",
groupName: "1",
data: {
serverUrl: "https://server1",
},
},
],
};
const context = createContextV3();
sandbox
Expand Down Expand Up @@ -131,7 +141,16 @@ describe("copilotPluginGenerator", function () {
projectPath: "path",
[QuestionNames.ProgrammingLanguage]: ProgrammingLanguage.CSharp,
[QuestionNames.ApiSpecLocation]: "https://test.com",
[QuestionNames.ApiOperation]: ["operation1"],
[QuestionNames.ApiOperation]: [
{
id: "operation1",
label: "operation1",
groupName: "1",
data: {
serverUrl: "https://server1",
},
},
],
};
const context = createContextV3();
sandbox.stub(SpecParser.prototype, "validate").resolves({
Expand Down Expand Up @@ -183,7 +202,16 @@ describe("copilotPluginGenerator", function () {
projectPath: "path",
[QuestionNames.ProgrammingLanguage]: ProgrammingLanguage.CSharp,
[QuestionNames.ApiSpecLocation]: "https://test.com",
[QuestionNames.ApiOperation]: ["operation1"],
[QuestionNames.ApiOperation]: [
{
id: "operation1",
label: "operation1",
groupName: "1",
data: {
serverUrl: "https://server1",
},
},
],
};
const context = createContextV3();
sandbox.stub(SpecParser.prototype, "validate").resolves({
Expand Down Expand Up @@ -214,6 +242,16 @@ describe("copilotPluginGenerator", function () {
projectPath: "path",
[QuestionNames.ProgrammingLanguage]: ProgrammingLanguage.CSharp,
[QuestionNames.ApiSpecLocation]: "https://test.com",
[QuestionNames.ApiOperation]: [
{
id: "operation1",
label: "operation1",
groupName: "1",
data: {
serverUrl: "https://server1",
},
},
],
};
const context = createContextV3();
sandbox.stub(SpecParser.prototype, "validate").resolves({
Expand All @@ -240,6 +278,16 @@ describe("copilotPluginGenerator", function () {
platform: Platform.VSCode,
projectPath: "path",
openAIPluginManifest: openAIPluginManifest,
[QuestionNames.ApiOperation]: [
{
id: "operation1",
label: "operation1",
groupName: "1",
data: {
serverUrl: "https://server1",
},
},
],
};
const context = createContextV3();
sandbox
Expand Down Expand Up @@ -274,6 +322,16 @@ describe("copilotPluginGenerator", function () {
platform: Platform.VSCode,
projectPath: "path",
openAIPluginManifest: openAIPluginManifest,
[QuestionNames.ApiOperation]: [
{
id: "operation1",
label: "operation1",
groupName: "1",
data: {
serverUrl: "https://server1",
},
},
],
};
const context = createContextV3();
sandbox
Expand Down Expand Up @@ -308,6 +366,16 @@ describe("copilotPluginGenerator", function () {
platform: Platform.VSCode,
projectPath: "path",
[QuestionNames.ApiSpecLocation]: "https://test.com",
[QuestionNames.ApiOperation]: [
{
id: "operation1",
label: "operation1",
groupName: "1",
data: {
serverUrl: "https://server1",
},
},
],
};
const context = createContextV3();
sandbox.stub(SpecParser.prototype, "generate").resolves();
Expand All @@ -325,6 +393,16 @@ describe("copilotPluginGenerator", function () {
platform: Platform.VSCode,
projectPath: "path",
[QuestionNames.ApiSpecLocation]: "https://test.com",
[QuestionNames.ApiOperation]: [
{
id: "operation1",
label: "operation1",
groupName: "1",
data: {
serverUrl: "https://server1",
},
},
],
};
const context = createContextV3();
sandbox.stub(SpecParser.prototype, "validate").resolves({
Expand All @@ -350,6 +428,16 @@ describe("copilotPluginGenerator", function () {
platform: Platform.VSCode,
projectPath: "path",
[QuestionNames.ApiSpecLocation]: "https://test.com",
[QuestionNames.ApiOperation]: [
{
id: "operation1",
label: "operation1",
groupName: "1",
data: {
serverUrl: "https://server1",
},
},
],
};
const context = createContextV3();
sandbox
Expand Down Expand Up @@ -377,6 +465,16 @@ describe("copilotPluginGenerator", function () {
platform: Platform.VSCode,
projectPath: "path",
[QuestionNames.ApiSpecLocation]: "https://test.com",
[QuestionNames.ApiOperation]: [
{
id: "operation1",
label: "operation1",
groupName: "1",
data: {
serverUrl: "https://server1",
},
},
],
};
const context = createContextV3();
sandbox.stub(Generator, "generateTemplate").throws(new Error("test"));
Expand All @@ -391,6 +489,16 @@ describe("copilotPluginGenerator", function () {
platform: Platform.VSCode,
projectPath: "path",
[QuestionNames.ApiSpecLocation]: "https://test.com",
[QuestionNames.ApiOperation]: [
{
id: "operation1",
label: "operation1",
groupName: "1",
data: {
serverUrl: "https://server1",
},
},
],
};
const context = createContextV3();
sandbox
Expand Down
Loading

0 comments on commit f2aec0b

Please sign in to comment.