@microsoft/m365-spec-parser 0.1.1-alpha.f04ac4ba4.0 → 0.1.1-alpha.f2bd7316b.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.esm2017.js +635 -334
- package/dist/index.esm2017.js.map +1 -1
- package/dist/index.esm2017.mjs +1076 -631
- package/dist/index.esm2017.mjs.map +1 -1
- package/dist/index.esm5.js +635 -334
- package/dist/index.esm5.js.map +1 -1
- package/dist/index.node.cjs.js +1089 -640
- package/dist/index.node.cjs.js.map +1 -1
- package/dist/src/adaptiveCardWrapper.d.ts +2 -0
- package/dist/src/constants.d.ts +9 -3
- package/dist/src/index.d.ts +1 -1
- package/dist/src/interfaces.d.ts +83 -20
- package/dist/src/manifestUpdater.d.ts +9 -6
- package/dist/src/specParser.browser.d.ts +3 -2
- package/dist/src/specParser.d.ts +5 -3
- package/dist/src/utils.d.ts +17 -33
- package/package.json +3 -3
package/dist/index.node.cjs.js
CHANGED
|
@@ -61,7 +61,8 @@ exports.ErrorType = void 0;
|
|
|
61
61
|
ErrorType["NoExtraAPICanBeAdded"] = "no-extra-api-can-be-added";
|
|
62
62
|
ErrorType["ResolveServerUrlFailed"] = "resolve-server-url-failed";
|
|
63
63
|
ErrorType["SwaggerNotSupported"] = "swagger-not-supported";
|
|
64
|
-
ErrorType["
|
|
64
|
+
ErrorType["MultipleAuthNotSupported"] = "multiple-auth-not-supported";
|
|
65
|
+
ErrorType["SpecVersionNotSupported"] = "spec-version-not-supported";
|
|
65
66
|
ErrorType["ListFailed"] = "list-failed";
|
|
66
67
|
ErrorType["listSupportedAPIInfoFailed"] = "list-supported-api-info-failed";
|
|
67
68
|
ErrorType["FilterSpecFailed"] = "filter-spec-failed";
|
|
@@ -70,6 +71,21 @@ exports.ErrorType = void 0;
|
|
|
70
71
|
ErrorType["GenerateFailed"] = "generate-failed";
|
|
71
72
|
ErrorType["ValidateFailed"] = "validate-failed";
|
|
72
73
|
ErrorType["GetSpecFailed"] = "get-spec-failed";
|
|
74
|
+
ErrorType["AuthTypeIsNotSupported"] = "auth-type-is-not-supported";
|
|
75
|
+
ErrorType["MissingOperationId"] = "missing-operation-id";
|
|
76
|
+
ErrorType["PostBodyContainMultipleMediaTypes"] = "post-body-contain-multiple-media-types";
|
|
77
|
+
ErrorType["ResponseContainMultipleMediaTypes"] = "response-contain-multiple-media-types";
|
|
78
|
+
ErrorType["ResponseJsonIsEmpty"] = "response-json-is-empty";
|
|
79
|
+
ErrorType["PostBodySchemaIsNotJson"] = "post-body-schema-is-not-json";
|
|
80
|
+
ErrorType["PostBodyContainsRequiredUnsupportedSchema"] = "post-body-contains-required-unsupported-schema";
|
|
81
|
+
ErrorType["ParamsContainRequiredUnsupportedSchema"] = "params-contain-required-unsupported-schema";
|
|
82
|
+
ErrorType["ParamsContainsNestedObject"] = "params-contains-nested-object";
|
|
83
|
+
ErrorType["RequestBodyContainsNestedObject"] = "request-body-contains-nested-object";
|
|
84
|
+
ErrorType["ExceededRequiredParamsLimit"] = "exceeded-required-params-limit";
|
|
85
|
+
ErrorType["NoParameter"] = "no-parameter";
|
|
86
|
+
ErrorType["NoAPIInfo"] = "no-api-info";
|
|
87
|
+
ErrorType["MethodNotAllowed"] = "method-not-allowed";
|
|
88
|
+
ErrorType["UrlPathNotExist"] = "url-path-not-exist";
|
|
73
89
|
ErrorType["Cancelled"] = "cancelled";
|
|
74
90
|
ErrorType["Unknown"] = "unknown";
|
|
75
91
|
})(exports.ErrorType || (exports.ErrorType = {}));
|
|
@@ -109,7 +125,7 @@ ConstantString.RemoteRefNotSupported = "Remote reference is not supported: %s.";
|
|
|
109
125
|
ConstantString.MissingOperationId = "Missing operationIds: %s.";
|
|
110
126
|
ConstantString.NoSupportedApi = "No supported API is found in the OpenAPI description document: only GET and POST methods are supported, additionally, there can be at most one required parameter, and no auth is allowed.";
|
|
111
127
|
ConstantString.AdditionalPropertiesNotSupported = "'additionalProperties' is not supported, and will be ignored.";
|
|
112
|
-
ConstantString.SchemaNotSupported = "'oneOf', 'anyOf', and 'not' schema are not supported: %s.";
|
|
128
|
+
ConstantString.SchemaNotSupported = "'oneOf', 'allOf', 'anyOf', and 'not' schema are not supported: %s.";
|
|
113
129
|
ConstantString.UnknownSchema = "Unknown schema: %s.";
|
|
114
130
|
ConstantString.UrlProtocolNotSupported = "Server url is not correct: protocol %s is not supported, you should use https protocol instead.";
|
|
115
131
|
ConstantString.RelativeServerUrlNotSupported = "Server url is not correct: relative server url is not supported.";
|
|
@@ -117,7 +133,8 @@ ConstantString.ResolveServerUrlFailed = "Unable to resolve the server URL: pleas
|
|
|
117
133
|
ConstantString.OperationOnlyContainsOptionalParam = "Operation %s contains multiple optional parameters. The first optional parameter is used for this command.";
|
|
118
134
|
ConstantString.ConvertSwaggerToOpenAPI = "The Swagger 2.0 file has been converted to OpenAPI 3.0.";
|
|
119
135
|
ConstantString.SwaggerNotSupported = "Swagger 2.0 is not supported. Please convert to OpenAPI 3.0 manually before proceeding.";
|
|
120
|
-
ConstantString.
|
|
136
|
+
ConstantString.SpecVersionNotSupported = "Unsupported OpenAPI version %s. Please use version 3.0.x.";
|
|
137
|
+
ConstantString.MultipleAuthNotSupported = "Multiple authentication methods are unsupported. Ensure all selected APIs use identical authentication.";
|
|
121
138
|
ConstantString.UnsupportedSchema = "Unsupported schema in %s %s: %s";
|
|
122
139
|
ConstantString.WrappedCardVersion = "devPreview";
|
|
123
140
|
ConstantString.WrappedCardSchema = "https://developer.microsoft.com/json-schemas/teams/vDevPreview/MicrosoftTeams.ResponseRenderingTemplate.schema.json";
|
|
@@ -128,8 +145,14 @@ ConstantString.AdaptiveCardVersion = "1.5";
|
|
|
128
145
|
ConstantString.AdaptiveCardSchema = "http://adaptivecards.io/schemas/adaptive-card.json";
|
|
129
146
|
ConstantString.AdaptiveCardType = "AdaptiveCard";
|
|
130
147
|
ConstantString.TextBlockType = "TextBlock";
|
|
148
|
+
ConstantString.ImageType = "Image";
|
|
131
149
|
ConstantString.ContainerType = "Container";
|
|
132
|
-
ConstantString.RegistrationIdPostfix =
|
|
150
|
+
ConstantString.RegistrationIdPostfix = {
|
|
151
|
+
apiKey: "REGISTRATION_ID",
|
|
152
|
+
oauth2: "CONFIGURATION_ID",
|
|
153
|
+
http: "REGISTRATION_ID",
|
|
154
|
+
openIdConnect: "REGISTRATION_ID",
|
|
155
|
+
};
|
|
133
156
|
ConstantString.ResponseCodeFor20X = [
|
|
134
157
|
"200",
|
|
135
158
|
"201",
|
|
@@ -188,9 +211,11 @@ ConstantString.ShortDescriptionMaxLens = 80;
|
|
|
188
211
|
ConstantString.FullDescriptionMaxLens = 4000;
|
|
189
212
|
ConstantString.CommandDescriptionMaxLens = 128;
|
|
190
213
|
ConstantString.ParameterDescriptionMaxLens = 128;
|
|
214
|
+
ConstantString.ConversationStarterMaxLens = 50;
|
|
191
215
|
ConstantString.CommandTitleMaxLens = 32;
|
|
192
216
|
ConstantString.ParameterTitleMaxLens = 32;
|
|
193
|
-
ConstantString.SMERequiredParamsMaxNum = 5;
|
|
217
|
+
ConstantString.SMERequiredParamsMaxNum = 5;
|
|
218
|
+
ConstantString.DefaultPluginId = "plugin_1";
|
|
194
219
|
|
|
195
220
|
// Copyright (c) Microsoft Corporation.
|
|
196
221
|
class SpecParserError extends Error {
|
|
@@ -213,242 +238,33 @@ class Utils {
|
|
|
213
238
|
}
|
|
214
239
|
return false;
|
|
215
240
|
}
|
|
216
|
-
static
|
|
217
|
-
|
|
218
|
-
requiredNum: 0,
|
|
219
|
-
optionalNum: 0,
|
|
220
|
-
isValid: true,
|
|
221
|
-
};
|
|
222
|
-
if (!paramObject) {
|
|
223
|
-
return paramResult;
|
|
224
|
-
}
|
|
225
|
-
for (let i = 0; i < paramObject.length; i++) {
|
|
226
|
-
const param = paramObject[i];
|
|
227
|
-
const schema = param.schema;
|
|
228
|
-
if (isCopilot && this.hasNestedObjectInSchema(schema)) {
|
|
229
|
-
paramResult.isValid = false;
|
|
230
|
-
continue;
|
|
231
|
-
}
|
|
232
|
-
const isRequiredWithoutDefault = param.required && schema.default === undefined;
|
|
233
|
-
if (isCopilot) {
|
|
234
|
-
if (isRequiredWithoutDefault) {
|
|
235
|
-
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
236
|
-
}
|
|
237
|
-
else {
|
|
238
|
-
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
239
|
-
}
|
|
240
|
-
continue;
|
|
241
|
-
}
|
|
242
|
-
if (param.in === "header" || param.in === "cookie") {
|
|
243
|
-
if (isRequiredWithoutDefault) {
|
|
244
|
-
paramResult.isValid = false;
|
|
245
|
-
}
|
|
246
|
-
continue;
|
|
247
|
-
}
|
|
248
|
-
if (schema.type !== "boolean" &&
|
|
249
|
-
schema.type !== "string" &&
|
|
250
|
-
schema.type !== "number" &&
|
|
251
|
-
schema.type !== "integer") {
|
|
252
|
-
if (isRequiredWithoutDefault) {
|
|
253
|
-
paramResult.isValid = false;
|
|
254
|
-
}
|
|
255
|
-
continue;
|
|
256
|
-
}
|
|
257
|
-
if (param.in === "query" || param.in === "path") {
|
|
258
|
-
if (isRequiredWithoutDefault) {
|
|
259
|
-
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
260
|
-
}
|
|
261
|
-
else {
|
|
262
|
-
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
return paramResult;
|
|
267
|
-
}
|
|
268
|
-
static checkPostBody(schema, isRequired = false, isCopilot = false) {
|
|
269
|
-
var _a;
|
|
270
|
-
const paramResult = {
|
|
271
|
-
requiredNum: 0,
|
|
272
|
-
optionalNum: 0,
|
|
273
|
-
isValid: true,
|
|
274
|
-
};
|
|
275
|
-
if (Object.keys(schema).length === 0) {
|
|
276
|
-
return paramResult;
|
|
277
|
-
}
|
|
278
|
-
const isRequiredWithoutDefault = isRequired && schema.default === undefined;
|
|
279
|
-
if (isCopilot && this.hasNestedObjectInSchema(schema)) {
|
|
280
|
-
paramResult.isValid = false;
|
|
281
|
-
return paramResult;
|
|
282
|
-
}
|
|
283
|
-
if (schema.type === "string" ||
|
|
284
|
-
schema.type === "integer" ||
|
|
285
|
-
schema.type === "boolean" ||
|
|
286
|
-
schema.type === "number") {
|
|
287
|
-
if (isRequiredWithoutDefault) {
|
|
288
|
-
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
289
|
-
}
|
|
290
|
-
else {
|
|
291
|
-
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
else if (schema.type === "object") {
|
|
295
|
-
const { properties } = schema;
|
|
296
|
-
for (const property in properties) {
|
|
297
|
-
let isRequired = false;
|
|
298
|
-
if (schema.required && ((_a = schema.required) === null || _a === void 0 ? void 0 : _a.indexOf(property)) >= 0) {
|
|
299
|
-
isRequired = true;
|
|
300
|
-
}
|
|
301
|
-
const result = Utils.checkPostBody(properties[property], isRequired, isCopilot);
|
|
302
|
-
paramResult.requiredNum += result.requiredNum;
|
|
303
|
-
paramResult.optionalNum += result.optionalNum;
|
|
304
|
-
paramResult.isValid = paramResult.isValid && result.isValid;
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
else {
|
|
308
|
-
if (isRequiredWithoutDefault && !isCopilot) {
|
|
309
|
-
paramResult.isValid = false;
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
return paramResult;
|
|
313
|
-
}
|
|
314
|
-
/**
|
|
315
|
-
* Checks if the given API is supported.
|
|
316
|
-
* @param {string} method - The HTTP method of the API.
|
|
317
|
-
* @param {string} path - The path of the API.
|
|
318
|
-
* @param {OpenAPIV3.Document} spec - The OpenAPI specification document.
|
|
319
|
-
* @returns {boolean} - Returns true if the API is supported, false otherwise.
|
|
320
|
-
* @description The following APIs are supported:
|
|
321
|
-
* 1. only support Get/Post operation without auth property
|
|
322
|
-
* 2. parameter inside query or path only support string, number, boolean and integer
|
|
323
|
-
* 3. parameter inside post body only support string, number, boolean, integer and object
|
|
324
|
-
* 4. request body + required parameters <= 1
|
|
325
|
-
* 5. response body should be “application/json” and not empty, and response code should be 20X
|
|
326
|
-
* 6. only support request body with “application/json” content type
|
|
327
|
-
*/
|
|
328
|
-
static isSupportedApi(method, path, spec, options) {
|
|
329
|
-
var _a;
|
|
330
|
-
const pathObj = spec.paths[path];
|
|
331
|
-
method = method.toLocaleLowerCase();
|
|
332
|
-
if (pathObj) {
|
|
333
|
-
if (((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && pathObj[method]) {
|
|
334
|
-
const securities = pathObj[method].security;
|
|
335
|
-
const authArray = Utils.getAuthArray(securities, spec);
|
|
336
|
-
if (!Utils.isSupportedAuth(authArray, options)) {
|
|
337
|
-
return false;
|
|
338
|
-
}
|
|
339
|
-
const operationObject = pathObj[method];
|
|
340
|
-
if (!options.allowMissingId && !operationObject.operationId) {
|
|
341
|
-
return false;
|
|
342
|
-
}
|
|
343
|
-
const paramObject = operationObject.parameters;
|
|
344
|
-
const requestBody = operationObject.requestBody;
|
|
345
|
-
const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
|
|
346
|
-
const mediaTypesCount = Object.keys((requestBody === null || requestBody === void 0 ? void 0 : requestBody.content) || {}).length;
|
|
347
|
-
if (mediaTypesCount > 1) {
|
|
348
|
-
return false;
|
|
349
|
-
}
|
|
350
|
-
const responseJson = Utils.getResponseJson(operationObject);
|
|
351
|
-
if (Object.keys(responseJson).length === 0) {
|
|
352
|
-
return false;
|
|
353
|
-
}
|
|
354
|
-
let requestBodyParamResult = {
|
|
355
|
-
requiredNum: 0,
|
|
356
|
-
optionalNum: 0,
|
|
357
|
-
isValid: true,
|
|
358
|
-
};
|
|
359
|
-
const isCopilot = options.projectType === exports.ProjectType.Copilot;
|
|
360
|
-
if (requestJsonBody) {
|
|
361
|
-
const requestBodySchema = requestJsonBody.schema;
|
|
362
|
-
if (isCopilot && requestBodySchema.type !== "object") {
|
|
363
|
-
return false;
|
|
364
|
-
}
|
|
365
|
-
requestBodyParamResult = Utils.checkPostBody(requestBodySchema, requestBody.required, isCopilot);
|
|
366
|
-
}
|
|
367
|
-
if (!requestBodyParamResult.isValid) {
|
|
368
|
-
return false;
|
|
369
|
-
}
|
|
370
|
-
const paramResult = Utils.checkParameters(paramObject, isCopilot);
|
|
371
|
-
if (!paramResult.isValid) {
|
|
372
|
-
return false;
|
|
373
|
-
}
|
|
374
|
-
// Copilot support arbitrary parameters
|
|
375
|
-
if (isCopilot) {
|
|
376
|
-
return true;
|
|
377
|
-
}
|
|
378
|
-
if (requestBodyParamResult.requiredNum + paramResult.requiredNum > 1) {
|
|
379
|
-
if (options.allowMultipleParameters &&
|
|
380
|
-
requestBodyParamResult.requiredNum + paramResult.requiredNum <=
|
|
381
|
-
ConstantString.SMERequiredParamsMaxNum) {
|
|
382
|
-
return true;
|
|
383
|
-
}
|
|
384
|
-
return false;
|
|
385
|
-
}
|
|
386
|
-
else if (requestBodyParamResult.requiredNum +
|
|
387
|
-
requestBodyParamResult.optionalNum +
|
|
388
|
-
paramResult.requiredNum +
|
|
389
|
-
paramResult.optionalNum ===
|
|
390
|
-
0) {
|
|
391
|
-
return false;
|
|
392
|
-
}
|
|
393
|
-
else {
|
|
394
|
-
return true;
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
}
|
|
398
|
-
return false;
|
|
241
|
+
static containMultipleMediaTypes(bodyObject) {
|
|
242
|
+
return Object.keys((bodyObject === null || bodyObject === void 0 ? void 0 : bodyObject.content) || {}).length > 1;
|
|
399
243
|
}
|
|
400
|
-
static
|
|
401
|
-
|
|
402
|
-
return true;
|
|
403
|
-
}
|
|
404
|
-
if (options.allowAPIKeyAuth || options.allowOauth2) {
|
|
405
|
-
// Currently we don't support multiple auth in one operation
|
|
406
|
-
if (authSchemaArray.length > 0 && authSchemaArray.every((auths) => auths.length > 1)) {
|
|
407
|
-
return false;
|
|
408
|
-
}
|
|
409
|
-
for (const auths of authSchemaArray) {
|
|
410
|
-
if (auths.length === 1) {
|
|
411
|
-
if (!options.allowOauth2 &&
|
|
412
|
-
options.allowAPIKeyAuth &&
|
|
413
|
-
Utils.isAPIKeyAuth(auths[0].authSchema)) {
|
|
414
|
-
return true;
|
|
415
|
-
}
|
|
416
|
-
else if (!options.allowAPIKeyAuth &&
|
|
417
|
-
options.allowOauth2 &&
|
|
418
|
-
Utils.isBearerTokenAuth(auths[0].authSchema)) {
|
|
419
|
-
return true;
|
|
420
|
-
}
|
|
421
|
-
else if (options.allowAPIKeyAuth &&
|
|
422
|
-
options.allowOauth2 &&
|
|
423
|
-
(Utils.isAPIKeyAuth(auths[0].authSchema) ||
|
|
424
|
-
Utils.isBearerTokenAuth(auths[0].authSchema))) {
|
|
425
|
-
return true;
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
return false;
|
|
244
|
+
static isBearerTokenAuth(authScheme) {
|
|
245
|
+
return authScheme.type === "http" && authScheme.scheme === "bearer";
|
|
431
246
|
}
|
|
432
|
-
static isAPIKeyAuth(
|
|
433
|
-
return
|
|
247
|
+
static isAPIKeyAuth(authScheme) {
|
|
248
|
+
return authScheme.type === "apiKey";
|
|
434
249
|
}
|
|
435
|
-
static
|
|
436
|
-
return (
|
|
437
|
-
|
|
438
|
-
|
|
250
|
+
static isOAuthWithAuthCodeFlow(authScheme) {
|
|
251
|
+
return !!(authScheme.type === "oauth2" &&
|
|
252
|
+
authScheme.flows &&
|
|
253
|
+
authScheme.flows.authorizationCode);
|
|
439
254
|
}
|
|
440
255
|
static getAuthArray(securities, spec) {
|
|
441
256
|
var _a;
|
|
442
257
|
const result = [];
|
|
443
258
|
const securitySchemas = (_a = spec.components) === null || _a === void 0 ? void 0 : _a.securitySchemes;
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
259
|
+
const securitiesArr = securities !== null && securities !== void 0 ? securities : spec.security;
|
|
260
|
+
if (securitiesArr && securitySchemas) {
|
|
261
|
+
for (let i = 0; i < securitiesArr.length; i++) {
|
|
262
|
+
const security = securitiesArr[i];
|
|
447
263
|
const authArray = [];
|
|
448
264
|
for (const name in security) {
|
|
449
265
|
const auth = securitySchemas[name];
|
|
450
266
|
authArray.push({
|
|
451
|
-
|
|
267
|
+
authScheme: auth,
|
|
452
268
|
name: name,
|
|
453
269
|
});
|
|
454
270
|
}
|
|
@@ -460,24 +276,47 @@ class Utils {
|
|
|
460
276
|
result.sort((a, b) => a[0].name.localeCompare(b[0].name));
|
|
461
277
|
return result;
|
|
462
278
|
}
|
|
279
|
+
static getAuthInfo(spec) {
|
|
280
|
+
let authInfo = undefined;
|
|
281
|
+
for (const url in spec.paths) {
|
|
282
|
+
for (const method in spec.paths[url]) {
|
|
283
|
+
const operation = spec.paths[url][method];
|
|
284
|
+
const authArray = Utils.getAuthArray(operation.security, spec);
|
|
285
|
+
if (authArray && authArray.length > 0) {
|
|
286
|
+
const currentAuth = authArray[0][0];
|
|
287
|
+
if (!authInfo) {
|
|
288
|
+
authInfo = authArray[0][0];
|
|
289
|
+
}
|
|
290
|
+
else if (authInfo.name !== currentAuth.name) {
|
|
291
|
+
throw new SpecParserError(ConstantString.MultipleAuthNotSupported, exports.ErrorType.MultipleAuthNotSupported);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
return authInfo;
|
|
297
|
+
}
|
|
463
298
|
static updateFirstLetter(str) {
|
|
464
299
|
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
465
300
|
}
|
|
466
301
|
static getResponseJson(operationObject) {
|
|
467
302
|
var _a, _b;
|
|
468
303
|
let json = {};
|
|
304
|
+
let multipleMediaType = false;
|
|
469
305
|
for (const code of ConstantString.ResponseCodeFor20X) {
|
|
470
306
|
const responseObject = (_a = operationObject === null || operationObject === void 0 ? void 0 : operationObject.responses) === null || _a === void 0 ? void 0 : _a[code];
|
|
471
|
-
const mediaTypesCount = Object.keys((responseObject === null || responseObject === void 0 ? void 0 : responseObject.content) || {}).length;
|
|
472
|
-
if (mediaTypesCount > 1) {
|
|
473
|
-
return {};
|
|
474
|
-
}
|
|
475
307
|
if ((_b = responseObject === null || responseObject === void 0 ? void 0 : responseObject.content) === null || _b === void 0 ? void 0 : _b["application/json"]) {
|
|
308
|
+
multipleMediaType = false;
|
|
476
309
|
json = responseObject.content["application/json"];
|
|
477
|
-
|
|
310
|
+
if (Utils.containMultipleMediaTypes(responseObject)) {
|
|
311
|
+
multipleMediaType = true;
|
|
312
|
+
json = {};
|
|
313
|
+
}
|
|
314
|
+
else {
|
|
315
|
+
break;
|
|
316
|
+
}
|
|
478
317
|
}
|
|
479
318
|
}
|
|
480
|
-
return json;
|
|
319
|
+
return { json, multipleMediaType };
|
|
481
320
|
}
|
|
482
321
|
static convertPathToCamelCase(path) {
|
|
483
322
|
const pathSegments = path.split(/[./{]/);
|
|
@@ -497,10 +336,10 @@ class Utils {
|
|
|
497
336
|
return undefined;
|
|
498
337
|
}
|
|
499
338
|
}
|
|
500
|
-
static
|
|
339
|
+
static resolveEnv(str) {
|
|
501
340
|
const placeHolderReg = /\${{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*}}/g;
|
|
502
|
-
let matches = placeHolderReg.exec(
|
|
503
|
-
let
|
|
341
|
+
let matches = placeHolderReg.exec(str);
|
|
342
|
+
let newStr = str;
|
|
504
343
|
while (matches != null) {
|
|
505
344
|
const envVar = matches[1];
|
|
506
345
|
const envVal = process.env[envVar];
|
|
@@ -508,17 +347,17 @@ class Utils {
|
|
|
508
347
|
throw new Error(Utils.format(ConstantString.ResolveServerUrlFailed, envVar));
|
|
509
348
|
}
|
|
510
349
|
else {
|
|
511
|
-
|
|
350
|
+
newStr = newStr.replace(matches[0], envVal);
|
|
512
351
|
}
|
|
513
|
-
matches = placeHolderReg.exec(
|
|
352
|
+
matches = placeHolderReg.exec(str);
|
|
514
353
|
}
|
|
515
|
-
return
|
|
354
|
+
return newStr;
|
|
516
355
|
}
|
|
517
356
|
static checkServerUrl(servers) {
|
|
518
357
|
const errors = [];
|
|
519
358
|
let serverUrl;
|
|
520
359
|
try {
|
|
521
|
-
serverUrl = Utils.
|
|
360
|
+
serverUrl = Utils.resolveEnv(servers[0].url);
|
|
522
361
|
}
|
|
523
362
|
catch (err) {
|
|
524
363
|
errors.push({
|
|
@@ -549,6 +388,7 @@ class Utils {
|
|
|
549
388
|
return errors;
|
|
550
389
|
}
|
|
551
390
|
static validateServer(spec, options) {
|
|
391
|
+
var _a;
|
|
552
392
|
const errors = [];
|
|
553
393
|
let hasTopLevelServers = false;
|
|
554
394
|
let hasPathLevelServers = false;
|
|
@@ -569,7 +409,7 @@ class Utils {
|
|
|
569
409
|
}
|
|
570
410
|
for (const method in methods) {
|
|
571
411
|
const operationObject = methods[method];
|
|
572
|
-
if (
|
|
412
|
+
if (((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
|
|
573
413
|
if ((operationObject === null || operationObject === void 0 ? void 0 : operationObject.servers) && operationObject.servers.length >= 1) {
|
|
574
414
|
hasOperationLevelServers = true;
|
|
575
415
|
const serverErrors = Utils.checkServerUrl(operationObject.servers);
|
|
@@ -612,6 +452,7 @@ class Utils {
|
|
|
612
452
|
Utils.updateParameterWithInputType(schema, parameter);
|
|
613
453
|
}
|
|
614
454
|
if (isRequired && schema.default === undefined) {
|
|
455
|
+
parameter.isRequired = true;
|
|
615
456
|
requiredParams.push(parameter);
|
|
616
457
|
}
|
|
617
458
|
else {
|
|
@@ -675,6 +516,7 @@ class Utils {
|
|
|
675
516
|
}
|
|
676
517
|
if (param.in !== "header" && param.in !== "cookie") {
|
|
677
518
|
if (param.required && (schema === null || schema === void 0 ? void 0 : schema.default) === undefined) {
|
|
519
|
+
parameter.isRequired = true;
|
|
678
520
|
requiredParams.push(parameter);
|
|
679
521
|
}
|
|
680
522
|
else {
|
|
@@ -694,13 +536,7 @@ class Utils {
|
|
|
694
536
|
}
|
|
695
537
|
}
|
|
696
538
|
const operationId = operationItem.operationId;
|
|
697
|
-
const parameters = [];
|
|
698
|
-
if (requiredParams.length !== 0) {
|
|
699
|
-
parameters.push(...requiredParams);
|
|
700
|
-
}
|
|
701
|
-
else {
|
|
702
|
-
parameters.push(optionalParams[0]);
|
|
703
|
-
}
|
|
539
|
+
const parameters = [...requiredParams, ...optionalParams];
|
|
704
540
|
const command = {
|
|
705
541
|
context: ["compose"],
|
|
706
542
|
type: "query",
|
|
@@ -709,353 +545,575 @@ class Utils {
|
|
|
709
545
|
parameters: parameters,
|
|
710
546
|
description: ((_b = operationItem.description) !== null && _b !== void 0 ? _b : "").slice(0, ConstantString.CommandDescriptionMaxLens),
|
|
711
547
|
};
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
548
|
+
return command;
|
|
549
|
+
}
|
|
550
|
+
static format(str, ...args) {
|
|
551
|
+
let index = 0;
|
|
552
|
+
return str.replace(/%s/g, () => {
|
|
553
|
+
const arg = args[index++];
|
|
554
|
+
return arg !== undefined ? arg : "";
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
static getSafeRegistrationIdEnvName(authName) {
|
|
558
|
+
if (!authName) {
|
|
559
|
+
return "";
|
|
719
560
|
}
|
|
720
|
-
|
|
561
|
+
let safeRegistrationIdEnvName = authName.toUpperCase().replace(/[^A-Z0-9_]/g, "_");
|
|
562
|
+
if (!safeRegistrationIdEnvName.match(/^[A-Z]/)) {
|
|
563
|
+
safeRegistrationIdEnvName = "PREFIX_" + safeRegistrationIdEnvName;
|
|
564
|
+
}
|
|
565
|
+
return safeRegistrationIdEnvName;
|
|
721
566
|
}
|
|
722
|
-
static
|
|
723
|
-
const
|
|
567
|
+
static getServerObject(spec, method, path) {
|
|
568
|
+
const pathObj = spec.paths[path];
|
|
569
|
+
const operationObject = pathObj[method];
|
|
570
|
+
const rootServer = spec.servers && spec.servers[0];
|
|
571
|
+
const methodServer = spec.paths[path].servers && spec.paths[path].servers[0];
|
|
572
|
+
const operationServer = operationObject.servers && operationObject.servers[0];
|
|
573
|
+
const serverUrl = operationServer || methodServer || rootServer;
|
|
574
|
+
return serverUrl;
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
// Copyright (c) Microsoft Corporation.
|
|
579
|
+
class Validator {
|
|
580
|
+
listAPIs() {
|
|
581
|
+
var _a;
|
|
582
|
+
if (this.apiMap) {
|
|
583
|
+
return this.apiMap;
|
|
584
|
+
}
|
|
585
|
+
const paths = this.spec.paths;
|
|
724
586
|
const result = {};
|
|
725
587
|
for (const path in paths) {
|
|
726
588
|
const methods = paths[path];
|
|
727
589
|
for (const method in methods) {
|
|
728
|
-
|
|
729
|
-
if (
|
|
730
|
-
const
|
|
731
|
-
result[`${method.toUpperCase()} ${path}`] =
|
|
590
|
+
const operationObject = methods[method];
|
|
591
|
+
if (((_a = this.options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
|
|
592
|
+
const validateResult = this.validateAPI(method, path);
|
|
593
|
+
result[`${method.toUpperCase()} ${path}`] = {
|
|
594
|
+
operation: operationObject,
|
|
595
|
+
isValid: validateResult.isValid,
|
|
596
|
+
reason: validateResult.reason,
|
|
597
|
+
};
|
|
732
598
|
}
|
|
733
599
|
}
|
|
734
600
|
}
|
|
601
|
+
this.apiMap = result;
|
|
735
602
|
return result;
|
|
736
603
|
}
|
|
737
|
-
|
|
738
|
-
const
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
604
|
+
validateSpecVersion() {
|
|
605
|
+
const result = { errors: [], warnings: [] };
|
|
606
|
+
if (this.spec.openapi >= "3.1.0") {
|
|
607
|
+
result.errors.push({
|
|
608
|
+
type: exports.ErrorType.SpecVersionNotSupported,
|
|
609
|
+
content: Utils.format(ConstantString.SpecVersionNotSupported, this.spec.openapi),
|
|
610
|
+
data: this.spec.openapi,
|
|
744
611
|
});
|
|
745
612
|
}
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
const
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
613
|
+
return result;
|
|
614
|
+
}
|
|
615
|
+
validateSpecServer() {
|
|
616
|
+
const result = { errors: [], warnings: [] };
|
|
617
|
+
const serverErrors = Utils.validateServer(this.spec, this.options);
|
|
618
|
+
result.errors.push(...serverErrors);
|
|
619
|
+
return result;
|
|
620
|
+
}
|
|
621
|
+
validateSpecNoSupportAPI() {
|
|
622
|
+
const result = { errors: [], warnings: [] };
|
|
623
|
+
const apiMap = this.listAPIs();
|
|
624
|
+
const validAPIs = Object.entries(apiMap).filter(([, value]) => value.isValid);
|
|
625
|
+
if (validAPIs.length === 0) {
|
|
626
|
+
const data = [];
|
|
627
|
+
for (const key in apiMap) {
|
|
628
|
+
const { reason } = apiMap[key];
|
|
629
|
+
const apiInvalidReason = { api: key, reason: reason };
|
|
630
|
+
data.push(apiInvalidReason);
|
|
631
|
+
}
|
|
632
|
+
result.errors.push({
|
|
763
633
|
type: exports.ErrorType.NoSupportedApi,
|
|
764
634
|
content: ConstantString.NoSupportedApi,
|
|
635
|
+
data,
|
|
765
636
|
});
|
|
766
637
|
}
|
|
638
|
+
return result;
|
|
639
|
+
}
|
|
640
|
+
validateSpecOperationId() {
|
|
641
|
+
const result = { errors: [], warnings: [] };
|
|
642
|
+
const apiMap = this.listAPIs();
|
|
767
643
|
// OperationId missing
|
|
768
644
|
const apisMissingOperationId = [];
|
|
769
645
|
for (const key in apiMap) {
|
|
770
|
-
const
|
|
771
|
-
if (!
|
|
646
|
+
const { operation } = apiMap[key];
|
|
647
|
+
if (!operation.operationId) {
|
|
772
648
|
apisMissingOperationId.push(key);
|
|
773
649
|
}
|
|
774
650
|
}
|
|
775
651
|
if (apisMissingOperationId.length > 0) {
|
|
776
|
-
warnings.push({
|
|
652
|
+
result.warnings.push({
|
|
777
653
|
type: exports.WarningType.OperationIdMissing,
|
|
778
654
|
content: Utils.format(ConstantString.MissingOperationId, apisMissingOperationId.join(", ")),
|
|
779
655
|
data: apisMissingOperationId,
|
|
780
656
|
});
|
|
781
657
|
}
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
658
|
+
return result;
|
|
659
|
+
}
|
|
660
|
+
validateMethodAndPath(method, path) {
|
|
661
|
+
const result = { isValid: true, reason: [] };
|
|
662
|
+
if (this.options.allowMethods && !this.options.allowMethods.includes(method)) {
|
|
663
|
+
result.isValid = false;
|
|
664
|
+
result.reason.push(exports.ErrorType.MethodNotAllowed);
|
|
665
|
+
return result;
|
|
785
666
|
}
|
|
786
|
-
|
|
787
|
-
|
|
667
|
+
const pathObj = this.spec.paths[path];
|
|
668
|
+
if (!pathObj || !pathObj[method]) {
|
|
669
|
+
result.isValid = false;
|
|
670
|
+
result.reason.push(exports.ErrorType.UrlPathNotExist);
|
|
671
|
+
return result;
|
|
788
672
|
}
|
|
789
|
-
return
|
|
790
|
-
status,
|
|
791
|
-
warnings,
|
|
792
|
-
errors,
|
|
793
|
-
};
|
|
673
|
+
return result;
|
|
794
674
|
}
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
675
|
+
validateResponse(method, path) {
|
|
676
|
+
const result = { isValid: true, reason: [] };
|
|
677
|
+
const operationObject = this.spec.paths[path][method];
|
|
678
|
+
const { json, multipleMediaType } = Utils.getResponseJson(operationObject);
|
|
679
|
+
if (this.options.projectType === exports.ProjectType.SME) {
|
|
680
|
+
// only support response body only contains “application/json” content type
|
|
681
|
+
if (multipleMediaType) {
|
|
682
|
+
result.reason.push(exports.ErrorType.ResponseContainMultipleMediaTypes);
|
|
683
|
+
}
|
|
684
|
+
else if (Object.keys(json).length === 0) {
|
|
685
|
+
// response body should not be empty
|
|
686
|
+
result.reason.push(exports.ErrorType.ResponseJsonIsEmpty);
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
return result;
|
|
801
690
|
}
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
691
|
+
validateServer(method, path) {
|
|
692
|
+
const result = { isValid: true, reason: [] };
|
|
693
|
+
const serverObj = Utils.getServerObject(this.spec, method, path);
|
|
694
|
+
if (!serverObj) {
|
|
695
|
+
// should contain server URL
|
|
696
|
+
result.reason.push(exports.ErrorType.NoServerInformation);
|
|
805
697
|
}
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
698
|
+
else {
|
|
699
|
+
// server url should be absolute url with https protocol
|
|
700
|
+
const serverValidateResult = Utils.checkServerUrl([serverObj]);
|
|
701
|
+
result.reason.push(...serverValidateResult.map((item) => item.type));
|
|
809
702
|
}
|
|
810
|
-
return
|
|
703
|
+
return result;
|
|
811
704
|
}
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
705
|
+
validateAuth(method, path) {
|
|
706
|
+
const pathObj = this.spec.paths[path];
|
|
707
|
+
const operationObject = pathObj[method];
|
|
708
|
+
const securities = operationObject.security;
|
|
709
|
+
const authSchemeArray = Utils.getAuthArray(securities, this.spec);
|
|
710
|
+
if (authSchemeArray.length === 0) {
|
|
711
|
+
return { isValid: true, reason: [] };
|
|
712
|
+
}
|
|
713
|
+
if (this.options.allowAPIKeyAuth ||
|
|
714
|
+
this.options.allowOauth2 ||
|
|
715
|
+
this.options.allowBearerTokenAuth) {
|
|
716
|
+
// Currently we don't support multiple auth in one operation
|
|
717
|
+
if (authSchemeArray.length > 0 && authSchemeArray.every((auths) => auths.length > 1)) {
|
|
718
|
+
return {
|
|
719
|
+
isValid: false,
|
|
720
|
+
reason: [exports.ErrorType.MultipleAuthNotSupported],
|
|
721
|
+
};
|
|
722
|
+
}
|
|
723
|
+
for (const auths of authSchemeArray) {
|
|
724
|
+
if (auths.length === 1) {
|
|
725
|
+
if ((this.options.allowAPIKeyAuth && Utils.isAPIKeyAuth(auths[0].authScheme)) ||
|
|
726
|
+
(this.options.allowOauth2 && Utils.isOAuthWithAuthCodeFlow(auths[0].authScheme)) ||
|
|
727
|
+
(this.options.allowBearerTokenAuth && Utils.isBearerTokenAuth(auths[0].authScheme))) {
|
|
728
|
+
return { isValid: true, reason: [] };
|
|
830
729
|
}
|
|
831
730
|
}
|
|
832
|
-
newPaths[path][methodName] = unResolveSpec.paths[path][methodName];
|
|
833
|
-
// Add the operationId if missing
|
|
834
|
-
if (!newPaths[path][methodName].operationId) {
|
|
835
|
-
newPaths[path][methodName].operationId = `${methodName}${Utils.convertPathToCamelCase(path)}`;
|
|
836
|
-
}
|
|
837
731
|
}
|
|
838
|
-
newSpec.paths = newPaths;
|
|
839
|
-
return newSpec;
|
|
840
732
|
}
|
|
841
|
-
|
|
842
|
-
throw new SpecParserError(err.toString(), exports.ErrorType.FilterSpecFailed);
|
|
843
|
-
}
|
|
844
|
-
}
|
|
845
|
-
}
|
|
846
|
-
|
|
847
|
-
// Copyright (c) Microsoft Corporation.
|
|
848
|
-
class ManifestUpdater {
|
|
849
|
-
static updateManifestWithAiPlugin(manifestPath, outputSpecPath, apiPluginFilePath, spec, options) {
|
|
850
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
851
|
-
const manifest = yield fs__default['default'].readJSON(manifestPath);
|
|
852
|
-
const apiPluginRelativePath = ManifestUpdater.getRelativePath(manifestPath, apiPluginFilePath);
|
|
853
|
-
manifest.plugins = [
|
|
854
|
-
{
|
|
855
|
-
pluginFile: apiPluginRelativePath,
|
|
856
|
-
},
|
|
857
|
-
];
|
|
858
|
-
ManifestUpdater.updateManifestDescription(manifest, spec);
|
|
859
|
-
const specRelativePath = ManifestUpdater.getRelativePath(manifestPath, outputSpecPath);
|
|
860
|
-
const apiPlugin = ManifestUpdater.generatePluginManifestSchema(spec, specRelativePath, options);
|
|
861
|
-
return [manifest, apiPlugin];
|
|
862
|
-
});
|
|
733
|
+
return { isValid: false, reason: [exports.ErrorType.AuthTypeIsNotSupported] };
|
|
863
734
|
}
|
|
864
|
-
|
|
865
|
-
var _a
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
735
|
+
checkPostBodySchema(schema, isRequired = false) {
|
|
736
|
+
var _a;
|
|
737
|
+
const paramResult = {
|
|
738
|
+
requiredNum: 0,
|
|
739
|
+
optionalNum: 0,
|
|
740
|
+
isValid: true,
|
|
741
|
+
reason: [],
|
|
869
742
|
};
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
743
|
+
if (Object.keys(schema).length === 0) {
|
|
744
|
+
return paramResult;
|
|
745
|
+
}
|
|
746
|
+
const isRequiredWithoutDefault = isRequired && schema.default === undefined;
|
|
747
|
+
const isCopilot = this.projectType === exports.ProjectType.Copilot;
|
|
748
|
+
if (isCopilot && this.hasNestedObjectInSchema(schema)) {
|
|
749
|
+
paramResult.isValid = false;
|
|
750
|
+
paramResult.reason = [exports.ErrorType.RequestBodyContainsNestedObject];
|
|
751
|
+
return paramResult;
|
|
752
|
+
}
|
|
873
753
|
if (schema.type === "string" ||
|
|
874
|
-
schema.type === "boolean" ||
|
|
875
754
|
schema.type === "integer" ||
|
|
876
|
-
schema.type === "
|
|
877
|
-
schema.type === "
|
|
878
|
-
|
|
755
|
+
schema.type === "boolean" ||
|
|
756
|
+
schema.type === "number") {
|
|
757
|
+
if (isRequiredWithoutDefault) {
|
|
758
|
+
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
759
|
+
}
|
|
760
|
+
else {
|
|
761
|
+
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
else if (schema.type === "object") {
|
|
765
|
+
const { properties } = schema;
|
|
766
|
+
for (const property in properties) {
|
|
767
|
+
let isRequired = false;
|
|
768
|
+
if (schema.required && ((_a = schema.required) === null || _a === void 0 ? void 0 : _a.indexOf(property)) >= 0) {
|
|
769
|
+
isRequired = true;
|
|
770
|
+
}
|
|
771
|
+
const result = this.checkPostBodySchema(properties[property], isRequired);
|
|
772
|
+
paramResult.requiredNum += result.requiredNum;
|
|
773
|
+
paramResult.optionalNum += result.optionalNum;
|
|
774
|
+
paramResult.isValid = paramResult.isValid && result.isValid;
|
|
775
|
+
paramResult.reason.push(...result.reason);
|
|
776
|
+
}
|
|
879
777
|
}
|
|
880
778
|
else {
|
|
881
|
-
|
|
779
|
+
if (isRequiredWithoutDefault && !isCopilot) {
|
|
780
|
+
paramResult.isValid = false;
|
|
781
|
+
paramResult.reason.push(exports.ErrorType.PostBodyContainsRequiredUnsupportedSchema);
|
|
782
|
+
}
|
|
882
783
|
}
|
|
883
|
-
return
|
|
784
|
+
return paramResult;
|
|
884
785
|
}
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
name: operationId,
|
|
938
|
-
description: description,
|
|
939
|
-
parameters: parameters,
|
|
940
|
-
};
|
|
941
|
-
functions.push(funcObj);
|
|
942
|
-
functionNames.push(operationId);
|
|
943
|
-
}
|
|
944
|
-
}
|
|
786
|
+
checkParamSchema(paramObject) {
|
|
787
|
+
const paramResult = {
|
|
788
|
+
requiredNum: 0,
|
|
789
|
+
optionalNum: 0,
|
|
790
|
+
isValid: true,
|
|
791
|
+
reason: [],
|
|
792
|
+
};
|
|
793
|
+
if (!paramObject) {
|
|
794
|
+
return paramResult;
|
|
795
|
+
}
|
|
796
|
+
const isCopilot = this.projectType === exports.ProjectType.Copilot;
|
|
797
|
+
for (let i = 0; i < paramObject.length; i++) {
|
|
798
|
+
const param = paramObject[i];
|
|
799
|
+
const schema = param.schema;
|
|
800
|
+
if (isCopilot && this.hasNestedObjectInSchema(schema)) {
|
|
801
|
+
paramResult.isValid = false;
|
|
802
|
+
paramResult.reason.push(exports.ErrorType.ParamsContainsNestedObject);
|
|
803
|
+
continue;
|
|
804
|
+
}
|
|
805
|
+
const isRequiredWithoutDefault = param.required && schema.default === undefined;
|
|
806
|
+
if (isCopilot) {
|
|
807
|
+
if (isRequiredWithoutDefault) {
|
|
808
|
+
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
809
|
+
}
|
|
810
|
+
else {
|
|
811
|
+
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
812
|
+
}
|
|
813
|
+
continue;
|
|
814
|
+
}
|
|
815
|
+
if (param.in === "header" || param.in === "cookie") {
|
|
816
|
+
if (isRequiredWithoutDefault) {
|
|
817
|
+
paramResult.isValid = false;
|
|
818
|
+
paramResult.reason.push(exports.ErrorType.ParamsContainRequiredUnsupportedSchema);
|
|
819
|
+
}
|
|
820
|
+
continue;
|
|
821
|
+
}
|
|
822
|
+
if (schema.type !== "boolean" &&
|
|
823
|
+
schema.type !== "string" &&
|
|
824
|
+
schema.type !== "number" &&
|
|
825
|
+
schema.type !== "integer") {
|
|
826
|
+
if (isRequiredWithoutDefault) {
|
|
827
|
+
paramResult.isValid = false;
|
|
828
|
+
paramResult.reason.push(exports.ErrorType.ParamsContainRequiredUnsupportedSchema);
|
|
829
|
+
}
|
|
830
|
+
continue;
|
|
831
|
+
}
|
|
832
|
+
if (param.in === "query" || param.in === "path") {
|
|
833
|
+
if (isRequiredWithoutDefault) {
|
|
834
|
+
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
835
|
+
}
|
|
836
|
+
else {
|
|
837
|
+
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
945
838
|
}
|
|
946
839
|
}
|
|
947
840
|
}
|
|
948
|
-
|
|
949
|
-
schema_version: "v2",
|
|
950
|
-
name_for_human: spec.info.title,
|
|
951
|
-
description_for_human: (_c = spec.info.description) !== null && _c !== void 0 ? _c : "<Please add description of the plugin>",
|
|
952
|
-
functions: functions,
|
|
953
|
-
runtimes: [
|
|
954
|
-
{
|
|
955
|
-
type: "OpenApi",
|
|
956
|
-
auth: {
|
|
957
|
-
type: "none", // TODO, support auth in the future
|
|
958
|
-
},
|
|
959
|
-
spec: {
|
|
960
|
-
url: specRelativePath,
|
|
961
|
-
},
|
|
962
|
-
run_for_functions: functionNames,
|
|
963
|
-
},
|
|
964
|
-
],
|
|
965
|
-
};
|
|
966
|
-
return apiPlugin;
|
|
841
|
+
return paramResult;
|
|
967
842
|
}
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
const
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
let warnings = [];
|
|
975
|
-
if (options.projectType === exports.ProjectType.SME) {
|
|
976
|
-
const updateResult = yield ManifestUpdater.generateCommands(spec, manifestPath, options, adaptiveCardFolder);
|
|
977
|
-
const commands = updateResult[0];
|
|
978
|
-
warnings = updateResult[1];
|
|
979
|
-
const composeExtension = {
|
|
980
|
-
composeExtensionType: "apiBased",
|
|
981
|
-
apiSpecificationFile: ManifestUpdater.getRelativePath(manifestPath, outputSpecPath),
|
|
982
|
-
commands: commands,
|
|
983
|
-
};
|
|
984
|
-
if (auth) {
|
|
985
|
-
if (Utils.isAPIKeyAuth(auth)) {
|
|
986
|
-
auth = auth;
|
|
987
|
-
const safeApiSecretRegistrationId = Utils.getSafeRegistrationIdEnvName(`${auth.name}_${ConstantString.RegistrationIdPostfix}`);
|
|
988
|
-
composeExtension.authorization = {
|
|
989
|
-
authType: "apiSecretServiceAuth",
|
|
990
|
-
apiSecretServiceAuthConfiguration: {
|
|
991
|
-
apiSecretRegistrationId: `\${{${safeApiSecretRegistrationId}}}`,
|
|
992
|
-
},
|
|
993
|
-
};
|
|
994
|
-
}
|
|
995
|
-
else if (Utils.isBearerTokenAuth(auth)) {
|
|
996
|
-
composeExtension.authorization = {
|
|
997
|
-
authType: "microsoftEntra",
|
|
998
|
-
microsoftEntraConfiguration: {
|
|
999
|
-
supportsSingleSignOn: true,
|
|
1000
|
-
},
|
|
1001
|
-
};
|
|
1002
|
-
updatedPart.webApplicationInfo = {
|
|
1003
|
-
id: "${{AAD_APP_CLIENT_ID}}",
|
|
1004
|
-
resource: "api://${{DOMAIN}}/${{AAD_APP_CLIENT_ID}}",
|
|
1005
|
-
};
|
|
1006
|
-
}
|
|
1007
|
-
}
|
|
1008
|
-
updatedPart.composeExtensions = [composeExtension];
|
|
843
|
+
hasNestedObjectInSchema(schema) {
|
|
844
|
+
if (schema.type === "object") {
|
|
845
|
+
for (const property in schema.properties) {
|
|
846
|
+
const nestedSchema = schema.properties[property];
|
|
847
|
+
if (nestedSchema.type === "object") {
|
|
848
|
+
return true;
|
|
1009
849
|
}
|
|
1010
|
-
updatedPart.description = originalManifest.description;
|
|
1011
|
-
ManifestUpdater.updateManifestDescription(updatedPart, spec);
|
|
1012
|
-
const updatedManifest = Object.assign(Object.assign({}, originalManifest), updatedPart);
|
|
1013
|
-
return [updatedManifest, warnings];
|
|
1014
850
|
}
|
|
1015
|
-
|
|
1016
|
-
|
|
851
|
+
}
|
|
852
|
+
return false;
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
// Copyright (c) Microsoft Corporation.
|
|
857
|
+
class CopilotValidator extends Validator {
|
|
858
|
+
constructor(spec, options) {
|
|
859
|
+
super();
|
|
860
|
+
this.projectType = exports.ProjectType.Copilot;
|
|
861
|
+
this.options = options;
|
|
862
|
+
this.spec = spec;
|
|
863
|
+
}
|
|
864
|
+
validateSpec() {
|
|
865
|
+
const result = { errors: [], warnings: [] };
|
|
866
|
+
// validate spec version
|
|
867
|
+
let validationResult = this.validateSpecVersion();
|
|
868
|
+
result.errors.push(...validationResult.errors);
|
|
869
|
+
// validate spec server
|
|
870
|
+
validationResult = this.validateSpecServer();
|
|
871
|
+
result.errors.push(...validationResult.errors);
|
|
872
|
+
// validate no supported API
|
|
873
|
+
validationResult = this.validateSpecNoSupportAPI();
|
|
874
|
+
result.errors.push(...validationResult.errors);
|
|
875
|
+
// validate operationId missing
|
|
876
|
+
validationResult = this.validateSpecOperationId();
|
|
877
|
+
result.warnings.push(...validationResult.warnings);
|
|
878
|
+
return result;
|
|
879
|
+
}
|
|
880
|
+
validateAPI(method, path) {
|
|
881
|
+
const result = { isValid: true, reason: [] };
|
|
882
|
+
method = method.toLocaleLowerCase();
|
|
883
|
+
// validate method and path
|
|
884
|
+
const methodAndPathResult = this.validateMethodAndPath(method, path);
|
|
885
|
+
if (!methodAndPathResult.isValid) {
|
|
886
|
+
return methodAndPathResult;
|
|
887
|
+
}
|
|
888
|
+
const operationObject = this.spec.paths[path][method];
|
|
889
|
+
// validate auth
|
|
890
|
+
const authCheckResult = this.validateAuth(method, path);
|
|
891
|
+
result.reason.push(...authCheckResult.reason);
|
|
892
|
+
// validate operationId
|
|
893
|
+
if (!this.options.allowMissingId && !operationObject.operationId) {
|
|
894
|
+
result.reason.push(exports.ErrorType.MissingOperationId);
|
|
895
|
+
}
|
|
896
|
+
// validate server
|
|
897
|
+
const validateServerResult = this.validateServer(method, path);
|
|
898
|
+
result.reason.push(...validateServerResult.reason);
|
|
899
|
+
// validate response
|
|
900
|
+
const validateResponseResult = this.validateResponse(method, path);
|
|
901
|
+
result.reason.push(...validateResponseResult.reason);
|
|
902
|
+
// validate requestBody
|
|
903
|
+
const requestBody = operationObject.requestBody;
|
|
904
|
+
const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
|
|
905
|
+
if (requestJsonBody) {
|
|
906
|
+
const requestBodySchema = requestJsonBody.schema;
|
|
907
|
+
if (requestBodySchema.type !== "object") {
|
|
908
|
+
result.reason.push(exports.ErrorType.PostBodySchemaIsNotJson);
|
|
1017
909
|
}
|
|
1018
|
-
|
|
910
|
+
const requestBodyParamResult = this.checkPostBodySchema(requestBodySchema, requestBody.required);
|
|
911
|
+
result.reason.push(...requestBodyParamResult.reason);
|
|
912
|
+
}
|
|
913
|
+
// validate parameters
|
|
914
|
+
const paramObject = operationObject.parameters;
|
|
915
|
+
const paramResult = this.checkParamSchema(paramObject);
|
|
916
|
+
result.reason.push(...paramResult.reason);
|
|
917
|
+
if (result.reason.length > 0) {
|
|
918
|
+
result.isValid = false;
|
|
919
|
+
}
|
|
920
|
+
return result;
|
|
1019
921
|
}
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
// Copyright (c) Microsoft Corporation.
|
|
925
|
+
class SMEValidator extends Validator {
|
|
926
|
+
constructor(spec, options) {
|
|
927
|
+
super();
|
|
928
|
+
this.projectType = exports.ProjectType.SME;
|
|
929
|
+
this.options = options;
|
|
930
|
+
this.spec = spec;
|
|
931
|
+
}
|
|
932
|
+
validateSpec() {
|
|
933
|
+
const result = { errors: [], warnings: [] };
|
|
934
|
+
// validate spec version
|
|
935
|
+
let validationResult = this.validateSpecVersion();
|
|
936
|
+
result.errors.push(...validationResult.errors);
|
|
937
|
+
// validate spec server
|
|
938
|
+
validationResult = this.validateSpecServer();
|
|
939
|
+
result.errors.push(...validationResult.errors);
|
|
940
|
+
// validate no supported API
|
|
941
|
+
validationResult = this.validateSpecNoSupportAPI();
|
|
942
|
+
result.errors.push(...validationResult.errors);
|
|
943
|
+
// validate operationId missing
|
|
944
|
+
if (this.options.allowMissingId) {
|
|
945
|
+
validationResult = this.validateSpecOperationId();
|
|
946
|
+
result.warnings.push(...validationResult.warnings);
|
|
947
|
+
}
|
|
948
|
+
return result;
|
|
949
|
+
}
|
|
950
|
+
validateAPI(method, path) {
|
|
951
|
+
const result = { isValid: true, reason: [] };
|
|
952
|
+
method = method.toLocaleLowerCase();
|
|
953
|
+
// validate method and path
|
|
954
|
+
const methodAndPathResult = this.validateMethodAndPath(method, path);
|
|
955
|
+
if (!methodAndPathResult.isValid) {
|
|
956
|
+
return methodAndPathResult;
|
|
957
|
+
}
|
|
958
|
+
const operationObject = this.spec.paths[path][method];
|
|
959
|
+
// validate auth
|
|
960
|
+
const authCheckResult = this.validateAuth(method, path);
|
|
961
|
+
result.reason.push(...authCheckResult.reason);
|
|
962
|
+
// validate operationId
|
|
963
|
+
if (!this.options.allowMissingId && !operationObject.operationId) {
|
|
964
|
+
result.reason.push(exports.ErrorType.MissingOperationId);
|
|
965
|
+
}
|
|
966
|
+
// validate server
|
|
967
|
+
const validateServerResult = this.validateServer(method, path);
|
|
968
|
+
result.reason.push(...validateServerResult.reason);
|
|
969
|
+
// validate response
|
|
970
|
+
const validateResponseResult = this.validateResponse(method, path);
|
|
971
|
+
result.reason.push(...validateResponseResult.reason);
|
|
972
|
+
let postBodyResult = {
|
|
973
|
+
requiredNum: 0,
|
|
974
|
+
optionalNum: 0,
|
|
975
|
+
isValid: true,
|
|
976
|
+
reason: [],
|
|
977
|
+
};
|
|
978
|
+
// validate requestBody
|
|
979
|
+
const requestBody = operationObject.requestBody;
|
|
980
|
+
const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
|
|
981
|
+
if (Utils.containMultipleMediaTypes(requestBody)) {
|
|
982
|
+
result.reason.push(exports.ErrorType.PostBodyContainMultipleMediaTypes);
|
|
983
|
+
}
|
|
984
|
+
if (requestJsonBody) {
|
|
985
|
+
const requestBodySchema = requestJsonBody.schema;
|
|
986
|
+
postBodyResult = this.checkPostBodySchema(requestBodySchema, requestBody.required);
|
|
987
|
+
result.reason.push(...postBodyResult.reason);
|
|
988
|
+
}
|
|
989
|
+
// validate parameters
|
|
990
|
+
const paramObject = operationObject.parameters;
|
|
991
|
+
const paramResult = this.checkParamSchema(paramObject);
|
|
992
|
+
result.reason.push(...paramResult.reason);
|
|
993
|
+
// validate total parameters count
|
|
994
|
+
if (paramResult.isValid && postBodyResult.isValid) {
|
|
995
|
+
const paramCountResult = this.validateParamCount(postBodyResult, paramResult);
|
|
996
|
+
result.reason.push(...paramCountResult.reason);
|
|
997
|
+
}
|
|
998
|
+
if (result.reason.length > 0) {
|
|
999
|
+
result.isValid = false;
|
|
1000
|
+
}
|
|
1001
|
+
return result;
|
|
1002
|
+
}
|
|
1003
|
+
validateParamCount(postBodyResult, paramResult) {
|
|
1004
|
+
const result = { isValid: true, reason: [] };
|
|
1005
|
+
const totalRequiredParams = postBodyResult.requiredNum + paramResult.requiredNum;
|
|
1006
|
+
const totalParams = totalRequiredParams + postBodyResult.optionalNum + paramResult.optionalNum;
|
|
1007
|
+
if (totalRequiredParams > 1) {
|
|
1008
|
+
if (!this.options.allowMultipleParameters ||
|
|
1009
|
+
totalRequiredParams > SMEValidator.SMERequiredParamsMaxNum) {
|
|
1010
|
+
result.reason.push(exports.ErrorType.ExceededRequiredParamsLimit);
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
else if (totalParams === 0) {
|
|
1014
|
+
result.reason.push(exports.ErrorType.NoParameter);
|
|
1015
|
+
}
|
|
1016
|
+
return result;
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
SMEValidator.SMERequiredParamsMaxNum = 5;
|
|
1020
|
+
|
|
1021
|
+
// Copyright (c) Microsoft Corporation.
|
|
1022
|
+
class TeamsAIValidator extends Validator {
|
|
1023
|
+
constructor(spec, options) {
|
|
1024
|
+
super();
|
|
1025
|
+
this.projectType = exports.ProjectType.TeamsAi;
|
|
1026
|
+
this.options = options;
|
|
1027
|
+
this.spec = spec;
|
|
1028
|
+
}
|
|
1029
|
+
validateSpec() {
|
|
1030
|
+
const result = { errors: [], warnings: [] };
|
|
1031
|
+
// validate spec server
|
|
1032
|
+
let validationResult = this.validateSpecServer();
|
|
1033
|
+
result.errors.push(...validationResult.errors);
|
|
1034
|
+
// validate no supported API
|
|
1035
|
+
validationResult = this.validateSpecNoSupportAPI();
|
|
1036
|
+
result.errors.push(...validationResult.errors);
|
|
1037
|
+
return result;
|
|
1038
|
+
}
|
|
1039
|
+
validateAPI(method, path) {
|
|
1040
|
+
const result = { isValid: true, reason: [] };
|
|
1041
|
+
method = method.toLocaleLowerCase();
|
|
1042
|
+
// validate method and path
|
|
1043
|
+
const methodAndPathResult = this.validateMethodAndPath(method, path);
|
|
1044
|
+
if (!methodAndPathResult.isValid) {
|
|
1045
|
+
return methodAndPathResult;
|
|
1046
|
+
}
|
|
1047
|
+
const operationObject = this.spec.paths[path][method];
|
|
1048
|
+
// validate operationId
|
|
1049
|
+
if (!this.options.allowMissingId && !operationObject.operationId) {
|
|
1050
|
+
result.reason.push(exports.ErrorType.MissingOperationId);
|
|
1051
|
+
}
|
|
1052
|
+
// validate server
|
|
1053
|
+
const validateServerResult = this.validateServer(method, path);
|
|
1054
|
+
result.reason.push(...validateServerResult.reason);
|
|
1055
|
+
if (result.reason.length > 0) {
|
|
1056
|
+
result.isValid = false;
|
|
1057
|
+
}
|
|
1058
|
+
return result;
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
class ValidatorFactory {
|
|
1063
|
+
static create(spec, options) {
|
|
1064
|
+
var _a;
|
|
1065
|
+
const type = (_a = options.projectType) !== null && _a !== void 0 ? _a : exports.ProjectType.SME;
|
|
1066
|
+
switch (type) {
|
|
1067
|
+
case exports.ProjectType.SME:
|
|
1068
|
+
return new SMEValidator(spec, options);
|
|
1069
|
+
case exports.ProjectType.Copilot:
|
|
1070
|
+
return new CopilotValidator(spec, options);
|
|
1071
|
+
case exports.ProjectType.TeamsAi:
|
|
1072
|
+
return new TeamsAIValidator(spec, options);
|
|
1073
|
+
default:
|
|
1074
|
+
throw new Error(`Invalid project type: ${type}`);
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
// Copyright (c) Microsoft Corporation.
|
|
1080
|
+
class SpecFilter {
|
|
1081
|
+
static specFilter(filter, unResolveSpec, resolvedSpec, options) {
|
|
1082
|
+
var _a;
|
|
1083
|
+
try {
|
|
1084
|
+
const newSpec = Object.assign({}, unResolveSpec);
|
|
1085
|
+
const newPaths = {};
|
|
1086
|
+
for (const filterItem of filter) {
|
|
1087
|
+
const [method, path] = filterItem.split(" ");
|
|
1088
|
+
const methodName = method.toLowerCase();
|
|
1089
|
+
const pathObj = (_a = resolvedSpec.paths) === null || _a === void 0 ? void 0 : _a[path];
|
|
1090
|
+
if (ConstantString.AllOperationMethods.includes(methodName) &&
|
|
1091
|
+
pathObj &&
|
|
1092
|
+
pathObj[methodName]) {
|
|
1093
|
+
const validator = ValidatorFactory.create(resolvedSpec, options);
|
|
1094
|
+
const validateResult = validator.validateAPI(methodName, path);
|
|
1095
|
+
if (!validateResult.isValid) {
|
|
1096
|
+
continue;
|
|
1097
|
+
}
|
|
1098
|
+
if (!newPaths[path]) {
|
|
1099
|
+
newPaths[path] = Object.assign({}, unResolveSpec.paths[path]);
|
|
1100
|
+
for (const m of ConstantString.AllOperationMethods) {
|
|
1101
|
+
delete newPaths[path][m];
|
|
1049
1102
|
}
|
|
1050
1103
|
}
|
|
1104
|
+
newPaths[path][methodName] = unResolveSpec.paths[path][methodName];
|
|
1105
|
+
// Add the operationId if missing
|
|
1106
|
+
if (!newPaths[path][methodName].operationId) {
|
|
1107
|
+
newPaths[path][methodName].operationId = `${methodName}${Utils.convertPathToCamelCase(path)}`;
|
|
1108
|
+
}
|
|
1051
1109
|
}
|
|
1052
1110
|
}
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1111
|
+
newSpec.paths = newPaths;
|
|
1112
|
+
return newSpec;
|
|
1113
|
+
}
|
|
1114
|
+
catch (err) {
|
|
1115
|
+
throw new SpecParserError(err.toString(), exports.ErrorType.FilterSpecFailed);
|
|
1116
|
+
}
|
|
1059
1117
|
}
|
|
1060
1118
|
}
|
|
1061
1119
|
|
|
@@ -1063,7 +1121,7 @@ class ManifestUpdater {
|
|
|
1063
1121
|
class AdaptiveCardGenerator {
|
|
1064
1122
|
static generateAdaptiveCard(operationItem) {
|
|
1065
1123
|
try {
|
|
1066
|
-
const json = Utils.getResponseJson(operationItem);
|
|
1124
|
+
const { json } = Utils.getResponseJson(operationItem);
|
|
1067
1125
|
let cardBody = [];
|
|
1068
1126
|
let schema = json.schema;
|
|
1069
1127
|
let jsonPath = "$";
|
|
@@ -1229,6 +1287,27 @@ function wrapAdaptiveCard(card, jsonPath) {
|
|
|
1229
1287
|
};
|
|
1230
1288
|
return result;
|
|
1231
1289
|
}
|
|
1290
|
+
function wrapResponseSemantics(card, jsonPath) {
|
|
1291
|
+
const props = inferProperties(card);
|
|
1292
|
+
const dataPath = jsonPath === "$" ? "$" : "$." + jsonPath;
|
|
1293
|
+
const result = {
|
|
1294
|
+
data_path: dataPath,
|
|
1295
|
+
};
|
|
1296
|
+
if (props.title || props.subtitle || props.imageUrl) {
|
|
1297
|
+
result.properties = {};
|
|
1298
|
+
if (props.title) {
|
|
1299
|
+
result.properties.title = "$." + props.title;
|
|
1300
|
+
}
|
|
1301
|
+
if (props.subtitle) {
|
|
1302
|
+
result.properties.subtitle = "$." + props.subtitle;
|
|
1303
|
+
}
|
|
1304
|
+
if (props.imageUrl) {
|
|
1305
|
+
result.properties.url = "$." + props.imageUrl;
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1308
|
+
result.static_template = card;
|
|
1309
|
+
return result;
|
|
1310
|
+
}
|
|
1232
1311
|
/**
|
|
1233
1312
|
* Infers the preview card template from an Adaptive Card and a JSON path.
|
|
1234
1313
|
* The preview card template includes a title and an optional subtitle and image.
|
|
@@ -1241,11 +1320,29 @@ function wrapAdaptiveCard(card, jsonPath) {
|
|
|
1241
1320
|
* @returns The inferred preview card template.
|
|
1242
1321
|
*/
|
|
1243
1322
|
function inferPreviewCardTemplate(card) {
|
|
1244
|
-
var _a;
|
|
1245
1323
|
const result = {
|
|
1246
|
-
title: "",
|
|
1324
|
+
title: "result",
|
|
1247
1325
|
};
|
|
1248
|
-
const
|
|
1326
|
+
const inferredProperties = inferProperties(card);
|
|
1327
|
+
if (inferredProperties.title) {
|
|
1328
|
+
result.title = `\${if(${inferredProperties.title}, ${inferredProperties.title}, 'N/A')}`;
|
|
1329
|
+
}
|
|
1330
|
+
if (inferredProperties.subtitle) {
|
|
1331
|
+
result.subtitle = `\${if(${inferredProperties.subtitle}, ${inferredProperties.subtitle}, 'N/A')}`;
|
|
1332
|
+
}
|
|
1333
|
+
if (inferredProperties.imageUrl) {
|
|
1334
|
+
result.image = {
|
|
1335
|
+
url: `\${${inferredProperties.imageUrl}}`,
|
|
1336
|
+
alt: `\${if(${inferredProperties.imageUrl}, ${inferredProperties.imageUrl}, 'N/A')}`,
|
|
1337
|
+
$when: `\${${inferredProperties.imageUrl} != null}`,
|
|
1338
|
+
};
|
|
1339
|
+
}
|
|
1340
|
+
return result;
|
|
1341
|
+
}
|
|
1342
|
+
function inferProperties(card) {
|
|
1343
|
+
var _a;
|
|
1344
|
+
const result = {};
|
|
1345
|
+
const nameSet = new Set();
|
|
1249
1346
|
let rootObject;
|
|
1250
1347
|
if (((_a = card.body[0]) === null || _a === void 0 ? void 0 : _a.type) === ConstantString.ContainerType) {
|
|
1251
1348
|
rootObject = card.body[0].items;
|
|
@@ -1258,56 +1355,380 @@ function inferPreviewCardTemplate(card) {
|
|
|
1258
1355
|
const textElement = element;
|
|
1259
1356
|
const index = textElement.text.indexOf("${if(");
|
|
1260
1357
|
if (index > 0) {
|
|
1261
|
-
|
|
1262
|
-
|
|
1358
|
+
const text = textElement.text.substring(index);
|
|
1359
|
+
const match = text.match(/\${if\(([^,]+),/);
|
|
1360
|
+
const property = match ? match[1] : "";
|
|
1361
|
+
if (property) {
|
|
1362
|
+
nameSet.add(property);
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
else if (element.type === ConstantString.ImageType) {
|
|
1367
|
+
const imageElement = element;
|
|
1368
|
+
const match = imageElement.url.match(/\${([^,]+)}/);
|
|
1369
|
+
const property = match ? match[1] : "";
|
|
1370
|
+
if (property) {
|
|
1371
|
+
nameSet.add(property);
|
|
1263
1372
|
}
|
|
1264
1373
|
}
|
|
1265
1374
|
}
|
|
1266
|
-
for (const
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
textBlockElements.delete(element);
|
|
1375
|
+
for (const name of nameSet) {
|
|
1376
|
+
if (!result.title && Utils.isWellKnownName(name, ConstantString.WellknownTitleName)) {
|
|
1377
|
+
result.title = name;
|
|
1378
|
+
nameSet.delete(name);
|
|
1271
1379
|
}
|
|
1272
1380
|
else if (!result.subtitle &&
|
|
1273
|
-
Utils.isWellKnownName(
|
|
1274
|
-
result.subtitle =
|
|
1275
|
-
|
|
1381
|
+
Utils.isWellKnownName(name, ConstantString.WellknownSubtitleName)) {
|
|
1382
|
+
result.subtitle = name;
|
|
1383
|
+
nameSet.delete(name);
|
|
1276
1384
|
}
|
|
1277
|
-
else if (!result.
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
if (property) {
|
|
1281
|
-
result.image = {
|
|
1282
|
-
url: `\${${property}}`,
|
|
1283
|
-
alt: text,
|
|
1284
|
-
$when: `\${${property} != null}`,
|
|
1285
|
-
};
|
|
1286
|
-
}
|
|
1287
|
-
textBlockElements.delete(element);
|
|
1385
|
+
else if (!result.imageUrl && Utils.isWellKnownName(name, ConstantString.WellknownImageName)) {
|
|
1386
|
+
result.imageUrl = name;
|
|
1387
|
+
nameSet.delete(name);
|
|
1288
1388
|
}
|
|
1289
1389
|
}
|
|
1290
|
-
for (const
|
|
1291
|
-
const text = element.text;
|
|
1390
|
+
for (const name of nameSet) {
|
|
1292
1391
|
if (!result.title) {
|
|
1293
|
-
result.title =
|
|
1294
|
-
|
|
1392
|
+
result.title = name;
|
|
1393
|
+
nameSet.delete(name);
|
|
1295
1394
|
}
|
|
1296
1395
|
else if (!result.subtitle) {
|
|
1297
|
-
result.subtitle =
|
|
1298
|
-
|
|
1396
|
+
result.subtitle = name;
|
|
1397
|
+
nameSet.delete(name);
|
|
1299
1398
|
}
|
|
1300
1399
|
}
|
|
1301
1400
|
if (!result.title && result.subtitle) {
|
|
1302
1401
|
result.title = result.subtitle;
|
|
1303
1402
|
delete result.subtitle;
|
|
1304
1403
|
}
|
|
1305
|
-
if (!result.title) {
|
|
1306
|
-
result.title = "result";
|
|
1307
|
-
}
|
|
1308
1404
|
return result;
|
|
1309
1405
|
}
|
|
1310
1406
|
|
|
1407
|
+
// Copyright (c) Microsoft Corporation.
|
|
1408
|
+
class ManifestUpdater {
|
|
1409
|
+
static updateManifestWithAiPlugin(manifestPath, outputSpecPath, apiPluginFilePath, spec, options, authInfo) {
|
|
1410
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1411
|
+
const manifest = yield fs__default['default'].readJSON(manifestPath);
|
|
1412
|
+
const apiPluginRelativePath = ManifestUpdater.getRelativePath(manifestPath, apiPluginFilePath);
|
|
1413
|
+
// Insert plugins in manifest.json if it is plugin for Copilot.
|
|
1414
|
+
if (!options.isGptPlugin) {
|
|
1415
|
+
manifest.plugins = [
|
|
1416
|
+
{
|
|
1417
|
+
file: apiPluginRelativePath,
|
|
1418
|
+
id: ConstantString.DefaultPluginId,
|
|
1419
|
+
},
|
|
1420
|
+
];
|
|
1421
|
+
ManifestUpdater.updateManifestDescription(manifest, spec);
|
|
1422
|
+
}
|
|
1423
|
+
const appName = this.removeEnvs(manifest.name.short);
|
|
1424
|
+
const specRelativePath = ManifestUpdater.getRelativePath(manifestPath, outputSpecPath);
|
|
1425
|
+
const apiPlugin = yield ManifestUpdater.generatePluginManifestSchema(spec, specRelativePath, apiPluginFilePath, appName, authInfo, options);
|
|
1426
|
+
return [manifest, apiPlugin];
|
|
1427
|
+
});
|
|
1428
|
+
}
|
|
1429
|
+
static updateManifestDescription(manifest, spec) {
|
|
1430
|
+
var _a, _b;
|
|
1431
|
+
manifest.description = {
|
|
1432
|
+
short: spec.info.title.slice(0, ConstantString.ShortDescriptionMaxLens),
|
|
1433
|
+
full: (_b = ((_a = spec.info.description) !== null && _a !== void 0 ? _a : manifest.description.full)) === null || _b === void 0 ? void 0 : _b.slice(0, ConstantString.FullDescriptionMaxLens),
|
|
1434
|
+
};
|
|
1435
|
+
}
|
|
1436
|
+
static checkSchema(schema, method, pathUrl) {
|
|
1437
|
+
if (schema.type === "array") {
|
|
1438
|
+
const items = schema.items;
|
|
1439
|
+
ManifestUpdater.checkSchema(items, method, pathUrl);
|
|
1440
|
+
}
|
|
1441
|
+
else if (schema.type !== "string" &&
|
|
1442
|
+
schema.type !== "boolean" &&
|
|
1443
|
+
schema.type !== "integer" &&
|
|
1444
|
+
schema.type !== "number") {
|
|
1445
|
+
throw new SpecParserError(Utils.format(ConstantString.UnsupportedSchema, method, pathUrl, JSON.stringify(schema)), exports.ErrorType.UpdateManifestFailed);
|
|
1446
|
+
}
|
|
1447
|
+
}
|
|
1448
|
+
static generatePluginManifestSchema(spec, specRelativePath, apiPluginFilePath, appName, authInfo, options) {
|
|
1449
|
+
var _a, _b, _c, _d;
|
|
1450
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1451
|
+
const functions = [];
|
|
1452
|
+
const functionNames = [];
|
|
1453
|
+
const conversationStarters = [];
|
|
1454
|
+
const paths = spec.paths;
|
|
1455
|
+
const pluginAuthObj = {
|
|
1456
|
+
type: "None",
|
|
1457
|
+
};
|
|
1458
|
+
if (authInfo) {
|
|
1459
|
+
if (Utils.isOAuthWithAuthCodeFlow(authInfo.authScheme)) {
|
|
1460
|
+
pluginAuthObj.type = "OAuthPluginVault";
|
|
1461
|
+
}
|
|
1462
|
+
else if (Utils.isBearerTokenAuth(authInfo.authScheme)) {
|
|
1463
|
+
pluginAuthObj.type = "ApiKeyPluginVault";
|
|
1464
|
+
}
|
|
1465
|
+
if (pluginAuthObj.type !== "None") {
|
|
1466
|
+
const safeRegistrationIdName = Utils.getSafeRegistrationIdEnvName(`${authInfo.name}_${ConstantString.RegistrationIdPostfix[authInfo.authScheme.type]}`);
|
|
1467
|
+
pluginAuthObj.reference_id = `\${{${safeRegistrationIdName}}}`;
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1470
|
+
for (const pathUrl in paths) {
|
|
1471
|
+
const pathItem = paths[pathUrl];
|
|
1472
|
+
if (pathItem) {
|
|
1473
|
+
const operations = pathItem;
|
|
1474
|
+
for (const method in operations) {
|
|
1475
|
+
if (options.allowMethods.includes(method)) {
|
|
1476
|
+
const operationItem = operations[method];
|
|
1477
|
+
const confirmationBodies = [];
|
|
1478
|
+
if (operationItem) {
|
|
1479
|
+
const operationId = operationItem.operationId;
|
|
1480
|
+
const description = (_a = operationItem.description) !== null && _a !== void 0 ? _a : "";
|
|
1481
|
+
const summary = operationItem.summary;
|
|
1482
|
+
const paramObject = operationItem.parameters;
|
|
1483
|
+
const requestBody = operationItem.requestBody;
|
|
1484
|
+
if (paramObject) {
|
|
1485
|
+
for (let i = 0; i < paramObject.length; i++) {
|
|
1486
|
+
const param = paramObject[i];
|
|
1487
|
+
const schema = param.schema;
|
|
1488
|
+
ManifestUpdater.checkSchema(schema, method, pathUrl);
|
|
1489
|
+
confirmationBodies.push(ManifestUpdater.getConfirmationBodyItem(param.name));
|
|
1490
|
+
}
|
|
1491
|
+
}
|
|
1492
|
+
if (requestBody) {
|
|
1493
|
+
const requestJsonBody = requestBody.content["application/json"];
|
|
1494
|
+
const requestBodySchema = requestJsonBody.schema;
|
|
1495
|
+
if (requestBodySchema.type === "object") {
|
|
1496
|
+
for (const property in requestBodySchema.properties) {
|
|
1497
|
+
const schema = requestBodySchema.properties[property];
|
|
1498
|
+
ManifestUpdater.checkSchema(schema, method, pathUrl);
|
|
1499
|
+
confirmationBodies.push(ManifestUpdater.getConfirmationBodyItem(property));
|
|
1500
|
+
}
|
|
1501
|
+
}
|
|
1502
|
+
else {
|
|
1503
|
+
throw new SpecParserError(Utils.format(ConstantString.UnsupportedSchema, method, pathUrl, JSON.stringify(requestBodySchema)), exports.ErrorType.UpdateManifestFailed);
|
|
1504
|
+
}
|
|
1505
|
+
}
|
|
1506
|
+
const funcObj = {
|
|
1507
|
+
name: operationId,
|
|
1508
|
+
description: description,
|
|
1509
|
+
};
|
|
1510
|
+
if (options.allowResponseSemantics) {
|
|
1511
|
+
const { json } = Utils.getResponseJson(operationItem);
|
|
1512
|
+
if (json.schema) {
|
|
1513
|
+
const [card, jsonPath] = AdaptiveCardGenerator.generateAdaptiveCard(operationItem);
|
|
1514
|
+
const responseSemantic = wrapResponseSemantics(card, jsonPath);
|
|
1515
|
+
funcObj.capabilities = {
|
|
1516
|
+
response_semantics: responseSemantic,
|
|
1517
|
+
};
|
|
1518
|
+
}
|
|
1519
|
+
}
|
|
1520
|
+
if (options.allowConfirmation && method !== ConstantString.GetMethod) {
|
|
1521
|
+
if (!funcObj.capabilities) {
|
|
1522
|
+
funcObj.capabilities = {};
|
|
1523
|
+
}
|
|
1524
|
+
funcObj.capabilities.confirmation = {
|
|
1525
|
+
type: "AdaptiveCard",
|
|
1526
|
+
title: (_b = operationItem.summary) !== null && _b !== void 0 ? _b : description,
|
|
1527
|
+
};
|
|
1528
|
+
if (confirmationBodies.length > 0) {
|
|
1529
|
+
funcObj.capabilities.confirmation.body = confirmationBodies.join("\n");
|
|
1530
|
+
}
|
|
1531
|
+
}
|
|
1532
|
+
functions.push(funcObj);
|
|
1533
|
+
functionNames.push(operationId);
|
|
1534
|
+
const conversationStarterStr = (summary !== null && summary !== void 0 ? summary : description).slice(0, ConstantString.ConversationStarterMaxLens);
|
|
1535
|
+
if (conversationStarterStr) {
|
|
1536
|
+
conversationStarters.push(conversationStarterStr);
|
|
1537
|
+
}
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1540
|
+
}
|
|
1541
|
+
}
|
|
1542
|
+
}
|
|
1543
|
+
let apiPlugin;
|
|
1544
|
+
if (yield fs__default['default'].pathExists(apiPluginFilePath)) {
|
|
1545
|
+
apiPlugin = yield fs__default['default'].readJSON(apiPluginFilePath);
|
|
1546
|
+
}
|
|
1547
|
+
else {
|
|
1548
|
+
apiPlugin = {
|
|
1549
|
+
schema_version: "v2.1",
|
|
1550
|
+
name_for_human: "",
|
|
1551
|
+
description_for_human: "",
|
|
1552
|
+
namespace: "",
|
|
1553
|
+
functions: [],
|
|
1554
|
+
runtimes: [],
|
|
1555
|
+
};
|
|
1556
|
+
}
|
|
1557
|
+
apiPlugin.functions = apiPlugin.functions || [];
|
|
1558
|
+
for (const func of functions) {
|
|
1559
|
+
const index = (_c = apiPlugin.functions) === null || _c === void 0 ? void 0 : _c.findIndex((f) => f.name === func.name);
|
|
1560
|
+
if (index === -1) {
|
|
1561
|
+
apiPlugin.functions.push(func);
|
|
1562
|
+
}
|
|
1563
|
+
else {
|
|
1564
|
+
apiPlugin.functions[index] = func;
|
|
1565
|
+
}
|
|
1566
|
+
}
|
|
1567
|
+
apiPlugin.runtimes = apiPlugin.runtimes || [];
|
|
1568
|
+
const index = apiPlugin.runtimes.findIndex((runtime) => {
|
|
1569
|
+
var _a, _b;
|
|
1570
|
+
return runtime.spec.url === specRelativePath &&
|
|
1571
|
+
runtime.type === "OpenApi" &&
|
|
1572
|
+
((_b = (_a = runtime.auth) === null || _a === void 0 ? void 0 : _a.type) !== null && _b !== void 0 ? _b : "None") === pluginAuthObj.type;
|
|
1573
|
+
});
|
|
1574
|
+
if (index === -1) {
|
|
1575
|
+
apiPlugin.runtimes.push({
|
|
1576
|
+
type: "OpenApi",
|
|
1577
|
+
auth: pluginAuthObj,
|
|
1578
|
+
spec: {
|
|
1579
|
+
url: specRelativePath,
|
|
1580
|
+
},
|
|
1581
|
+
run_for_functions: functionNames,
|
|
1582
|
+
});
|
|
1583
|
+
}
|
|
1584
|
+
else {
|
|
1585
|
+
apiPlugin.runtimes[index].run_for_functions = functionNames;
|
|
1586
|
+
}
|
|
1587
|
+
if (!apiPlugin.name_for_human) {
|
|
1588
|
+
apiPlugin.name_for_human = appName;
|
|
1589
|
+
}
|
|
1590
|
+
if (!apiPlugin.namespace) {
|
|
1591
|
+
apiPlugin.namespace = ManifestUpdater.removeAllSpecialCharacters(appName);
|
|
1592
|
+
}
|
|
1593
|
+
if (!apiPlugin.description_for_human) {
|
|
1594
|
+
apiPlugin.description_for_human =
|
|
1595
|
+
(_d = spec.info.description) !== null && _d !== void 0 ? _d : "<Please add description of the plugin>";
|
|
1596
|
+
}
|
|
1597
|
+
if (options.allowConversationStarters && conversationStarters.length > 0) {
|
|
1598
|
+
if (!apiPlugin.capabilities) {
|
|
1599
|
+
apiPlugin.capabilities = {
|
|
1600
|
+
localization: {},
|
|
1601
|
+
};
|
|
1602
|
+
}
|
|
1603
|
+
if (!apiPlugin.capabilities.conversation_starters) {
|
|
1604
|
+
apiPlugin.capabilities.conversation_starters = conversationStarters
|
|
1605
|
+
.slice(0, 5)
|
|
1606
|
+
.map((text) => ({ text }));
|
|
1607
|
+
}
|
|
1608
|
+
}
|
|
1609
|
+
return apiPlugin;
|
|
1610
|
+
});
|
|
1611
|
+
}
|
|
1612
|
+
static updateManifest(manifestPath, outputSpecPath, spec, options, adaptiveCardFolder, authInfo) {
|
|
1613
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1614
|
+
try {
|
|
1615
|
+
const originalManifest = yield fs__default['default'].readJSON(manifestPath);
|
|
1616
|
+
const updatedPart = {};
|
|
1617
|
+
updatedPart.composeExtensions = [];
|
|
1618
|
+
let warnings = [];
|
|
1619
|
+
if (options.projectType === exports.ProjectType.SME) {
|
|
1620
|
+
const updateResult = yield ManifestUpdater.generateCommands(spec, manifestPath, options, adaptiveCardFolder);
|
|
1621
|
+
const commands = updateResult[0];
|
|
1622
|
+
warnings = updateResult[1];
|
|
1623
|
+
const composeExtension = {
|
|
1624
|
+
composeExtensionType: "apiBased",
|
|
1625
|
+
apiSpecificationFile: ManifestUpdater.getRelativePath(manifestPath, outputSpecPath),
|
|
1626
|
+
commands: commands,
|
|
1627
|
+
};
|
|
1628
|
+
if (authInfo) {
|
|
1629
|
+
const auth = authInfo.authScheme;
|
|
1630
|
+
const safeRegistrationIdName = Utils.getSafeRegistrationIdEnvName(`${authInfo.name}_${ConstantString.RegistrationIdPostfix[authInfo.authScheme.type]}`);
|
|
1631
|
+
if (Utils.isAPIKeyAuth(auth) || Utils.isBearerTokenAuth(auth)) {
|
|
1632
|
+
const safeApiSecretRegistrationId = Utils.getSafeRegistrationIdEnvName(`${authInfo.name}_${ConstantString.RegistrationIdPostfix[authInfo.authScheme.type]}`);
|
|
1633
|
+
composeExtension.authorization = {
|
|
1634
|
+
authType: "apiSecretServiceAuth",
|
|
1635
|
+
apiSecretServiceAuthConfiguration: {
|
|
1636
|
+
apiSecretRegistrationId: `\${{${safeRegistrationIdName}}}`,
|
|
1637
|
+
},
|
|
1638
|
+
};
|
|
1639
|
+
}
|
|
1640
|
+
else if (Utils.isOAuthWithAuthCodeFlow(auth)) {
|
|
1641
|
+
composeExtension.authorization = {
|
|
1642
|
+
authType: "oAuth2.0",
|
|
1643
|
+
oAuthConfiguration: {
|
|
1644
|
+
oauthConfigurationId: `\${{${safeRegistrationIdName}}}`,
|
|
1645
|
+
},
|
|
1646
|
+
};
|
|
1647
|
+
updatedPart.webApplicationInfo = {
|
|
1648
|
+
id: "${{AAD_APP_CLIENT_ID}}",
|
|
1649
|
+
resource: "api://${{DOMAIN}}/${{AAD_APP_CLIENT_ID}}",
|
|
1650
|
+
};
|
|
1651
|
+
}
|
|
1652
|
+
}
|
|
1653
|
+
updatedPart.composeExtensions = [composeExtension];
|
|
1654
|
+
}
|
|
1655
|
+
updatedPart.description = originalManifest.description;
|
|
1656
|
+
ManifestUpdater.updateManifestDescription(updatedPart, spec);
|
|
1657
|
+
const updatedManifest = Object.assign(Object.assign({}, originalManifest), updatedPart);
|
|
1658
|
+
return [updatedManifest, warnings];
|
|
1659
|
+
}
|
|
1660
|
+
catch (err) {
|
|
1661
|
+
throw new SpecParserError(err.toString(), exports.ErrorType.UpdateManifestFailed);
|
|
1662
|
+
}
|
|
1663
|
+
});
|
|
1664
|
+
}
|
|
1665
|
+
static generateCommands(spec, manifestPath, options, adaptiveCardFolder) {
|
|
1666
|
+
var _a;
|
|
1667
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1668
|
+
const paths = spec.paths;
|
|
1669
|
+
const commands = [];
|
|
1670
|
+
const warnings = [];
|
|
1671
|
+
if (paths) {
|
|
1672
|
+
for (const pathUrl in paths) {
|
|
1673
|
+
const pathItem = paths[pathUrl];
|
|
1674
|
+
if (pathItem) {
|
|
1675
|
+
const operations = pathItem;
|
|
1676
|
+
// Currently only support GET and POST method
|
|
1677
|
+
for (const method in operations) {
|
|
1678
|
+
if ((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) {
|
|
1679
|
+
const operationItem = operations[method];
|
|
1680
|
+
if (operationItem) {
|
|
1681
|
+
const command = Utils.parseApiInfo(operationItem, options);
|
|
1682
|
+
if (command.parameters &&
|
|
1683
|
+
command.parameters.length >= 1 &&
|
|
1684
|
+
command.parameters.some((param) => param.isRequired)) {
|
|
1685
|
+
command.parameters = command.parameters.filter((param) => param.isRequired);
|
|
1686
|
+
}
|
|
1687
|
+
else if (command.parameters && command.parameters.length > 0) {
|
|
1688
|
+
command.parameters = [command.parameters[0]];
|
|
1689
|
+
warnings.push({
|
|
1690
|
+
type: exports.WarningType.OperationOnlyContainsOptionalParam,
|
|
1691
|
+
content: Utils.format(ConstantString.OperationOnlyContainsOptionalParam, command.id),
|
|
1692
|
+
data: command.id,
|
|
1693
|
+
});
|
|
1694
|
+
}
|
|
1695
|
+
if (adaptiveCardFolder) {
|
|
1696
|
+
const adaptiveCardPath = path__default['default'].join(adaptiveCardFolder, command.id + ".json");
|
|
1697
|
+
command.apiResponseRenderingTemplateFile = (yield fs__default['default'].pathExists(adaptiveCardPath))
|
|
1698
|
+
? ManifestUpdater.getRelativePath(manifestPath, adaptiveCardPath)
|
|
1699
|
+
: "";
|
|
1700
|
+
}
|
|
1701
|
+
commands.push(command);
|
|
1702
|
+
}
|
|
1703
|
+
}
|
|
1704
|
+
}
|
|
1705
|
+
}
|
|
1706
|
+
}
|
|
1707
|
+
}
|
|
1708
|
+
return [commands, warnings];
|
|
1709
|
+
});
|
|
1710
|
+
}
|
|
1711
|
+
static getRelativePath(from, to) {
|
|
1712
|
+
const relativePath = path__default['default'].relative(path__default['default'].dirname(from), to);
|
|
1713
|
+
return path__default['default'].normalize(relativePath).replace(/\\/g, "/");
|
|
1714
|
+
}
|
|
1715
|
+
static removeEnvs(str) {
|
|
1716
|
+
const placeHolderReg = /\${{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*}}/g;
|
|
1717
|
+
const matches = placeHolderReg.exec(str);
|
|
1718
|
+
let newStr = str;
|
|
1719
|
+
if (matches != null) {
|
|
1720
|
+
newStr = newStr.replace(matches[0], "");
|
|
1721
|
+
}
|
|
1722
|
+
return newStr;
|
|
1723
|
+
}
|
|
1724
|
+
static removeAllSpecialCharacters(str) {
|
|
1725
|
+
return str.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
1726
|
+
}
|
|
1727
|
+
static getConfirmationBodyItem(paramName) {
|
|
1728
|
+
return `* **${Utils.updateFirstLetter(paramName)}**: {{function.parameters.${paramName}}}`;
|
|
1729
|
+
}
|
|
1730
|
+
}
|
|
1731
|
+
|
|
1311
1732
|
// Copyright (c) Microsoft Corporation.
|
|
1312
1733
|
/**
|
|
1313
1734
|
* A class that parses an OpenAPI specification file and provides methods to validate, list, and generate artifacts.
|
|
@@ -1323,10 +1744,15 @@ class SpecParser {
|
|
|
1323
1744
|
allowMissingId: true,
|
|
1324
1745
|
allowSwagger: true,
|
|
1325
1746
|
allowAPIKeyAuth: false,
|
|
1747
|
+
allowBearerTokenAuth: false,
|
|
1326
1748
|
allowMultipleParameters: false,
|
|
1327
1749
|
allowOauth2: false,
|
|
1328
1750
|
allowMethods: ["get", "post"],
|
|
1751
|
+
allowConversationStarters: false,
|
|
1752
|
+
allowResponseSemantics: false,
|
|
1753
|
+
allowConfirmation: false,
|
|
1329
1754
|
projectType: exports.ProjectType.SME,
|
|
1755
|
+
isGptPlugin: false,
|
|
1330
1756
|
};
|
|
1331
1757
|
this.pathOrSpec = pathOrDoc;
|
|
1332
1758
|
this.parser = new SwaggerParser__default['default']();
|
|
@@ -1351,6 +1777,8 @@ class SpecParser {
|
|
|
1351
1777
|
errors: [{ type: exports.ErrorType.SpecNotValid, content: e.toString() }],
|
|
1352
1778
|
};
|
|
1353
1779
|
}
|
|
1780
|
+
const errors = [];
|
|
1781
|
+
const warnings = [];
|
|
1354
1782
|
if (!this.options.allowSwagger && this.isSwaggerFile) {
|
|
1355
1783
|
return {
|
|
1356
1784
|
status: exports.ValidationStatus.Error,
|
|
@@ -1360,7 +1788,38 @@ class SpecParser {
|
|
|
1360
1788
|
],
|
|
1361
1789
|
};
|
|
1362
1790
|
}
|
|
1363
|
-
|
|
1791
|
+
// Remote reference not supported
|
|
1792
|
+
const refPaths = this.parser.$refs.paths();
|
|
1793
|
+
// refPaths [0] is the current spec file path
|
|
1794
|
+
if (refPaths.length > 1) {
|
|
1795
|
+
errors.push({
|
|
1796
|
+
type: exports.ErrorType.RemoteRefNotSupported,
|
|
1797
|
+
content: Utils.format(ConstantString.RemoteRefNotSupported, refPaths.join(", ")),
|
|
1798
|
+
data: refPaths,
|
|
1799
|
+
});
|
|
1800
|
+
}
|
|
1801
|
+
if (!!this.isSwaggerFile && this.options.allowSwagger) {
|
|
1802
|
+
warnings.push({
|
|
1803
|
+
type: exports.WarningType.ConvertSwaggerToOpenAPI,
|
|
1804
|
+
content: ConstantString.ConvertSwaggerToOpenAPI,
|
|
1805
|
+
});
|
|
1806
|
+
}
|
|
1807
|
+
const validator = this.getValidator(this.spec);
|
|
1808
|
+
const validationResult = validator.validateSpec();
|
|
1809
|
+
warnings.push(...validationResult.warnings);
|
|
1810
|
+
errors.push(...validationResult.errors);
|
|
1811
|
+
let status = exports.ValidationStatus.Valid;
|
|
1812
|
+
if (warnings.length > 0 && errors.length === 0) {
|
|
1813
|
+
status = exports.ValidationStatus.Warning;
|
|
1814
|
+
}
|
|
1815
|
+
else if (errors.length > 0) {
|
|
1816
|
+
status = exports.ValidationStatus.Error;
|
|
1817
|
+
}
|
|
1818
|
+
return {
|
|
1819
|
+
status: status,
|
|
1820
|
+
warnings: warnings,
|
|
1821
|
+
errors: errors,
|
|
1822
|
+
};
|
|
1364
1823
|
}
|
|
1365
1824
|
catch (err) {
|
|
1366
1825
|
throw new SpecParserError(err.toString(), exports.ErrorType.ValidateFailed);
|
|
@@ -1384,39 +1843,40 @@ class SpecParser {
|
|
|
1384
1843
|
try {
|
|
1385
1844
|
yield this.loadSpec();
|
|
1386
1845
|
const spec = this.spec;
|
|
1387
|
-
const apiMap = this.
|
|
1388
|
-
const result =
|
|
1846
|
+
const apiMap = this.getAPIs(spec);
|
|
1847
|
+
const result = {
|
|
1848
|
+
APIs: [],
|
|
1849
|
+
allAPICount: 0,
|
|
1850
|
+
validAPICount: 0,
|
|
1851
|
+
};
|
|
1389
1852
|
for (const apiKey in apiMap) {
|
|
1853
|
+
const { operation, isValid, reason } = apiMap[apiKey];
|
|
1854
|
+
const [method, path] = apiKey.split(" ");
|
|
1855
|
+
const operationId = (_a = operation.operationId) !== null && _a !== void 0 ? _a : `${method.toLowerCase()}${Utils.convertPathToCamelCase(path)}`;
|
|
1390
1856
|
const apiResult = {
|
|
1391
|
-
api:
|
|
1857
|
+
api: apiKey,
|
|
1392
1858
|
server: "",
|
|
1393
|
-
operationId:
|
|
1859
|
+
operationId: operationId,
|
|
1860
|
+
isValid: isValid,
|
|
1861
|
+
reason: reason,
|
|
1394
1862
|
};
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
if (!operationId) {
|
|
1407
|
-
operationId = `${method.toLowerCase()}${Utils.convertPathToCamelCase(path)}`;
|
|
1408
|
-
}
|
|
1409
|
-
apiResult.operationId = operationId;
|
|
1410
|
-
const authArray = Utils.getAuthArray(operation.security, spec);
|
|
1411
|
-
for (const auths of authArray) {
|
|
1412
|
-
if (auths.length === 1) {
|
|
1413
|
-
apiResult.auth = auths[0].authSchema;
|
|
1414
|
-
break;
|
|
1863
|
+
if (isValid) {
|
|
1864
|
+
const serverObj = Utils.getServerObject(spec, method.toLocaleLowerCase(), path);
|
|
1865
|
+
if (serverObj) {
|
|
1866
|
+
apiResult.server = Utils.resolveEnv(serverObj.url);
|
|
1867
|
+
}
|
|
1868
|
+
const authArray = Utils.getAuthArray(operation.security, spec);
|
|
1869
|
+
for (const auths of authArray) {
|
|
1870
|
+
if (auths.length === 1) {
|
|
1871
|
+
apiResult.auth = auths[0];
|
|
1872
|
+
break;
|
|
1873
|
+
}
|
|
1415
1874
|
}
|
|
1416
1875
|
}
|
|
1417
|
-
apiResult
|
|
1418
|
-
result.push(apiResult);
|
|
1876
|
+
result.APIs.push(apiResult);
|
|
1419
1877
|
}
|
|
1878
|
+
result.allAPICount = result.APIs.length;
|
|
1879
|
+
result.validAPICount = result.APIs.filter((api) => api.isValid).length;
|
|
1420
1880
|
return result;
|
|
1421
1881
|
}
|
|
1422
1882
|
catch (err) {
|
|
@@ -1473,18 +1933,12 @@ class SpecParser {
|
|
|
1473
1933
|
const newSpecs = yield this.getFilteredSpecs(filter, signal);
|
|
1474
1934
|
const newUnResolvedSpec = newSpecs[0];
|
|
1475
1935
|
const newSpec = newSpecs[1];
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
resultStr = jsyaml__default['default'].dump(newUnResolvedSpec);
|
|
1479
|
-
}
|
|
1480
|
-
else {
|
|
1481
|
-
resultStr = JSON.stringify(newUnResolvedSpec, null, 2);
|
|
1482
|
-
}
|
|
1483
|
-
yield fs__default['default'].outputFile(outputSpecPath, resultStr);
|
|
1936
|
+
const authInfo = Utils.getAuthInfo(newSpec);
|
|
1937
|
+
yield this.saveFilterSpec(outputSpecPath, newUnResolvedSpec);
|
|
1484
1938
|
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
|
1485
1939
|
throw new SpecParserError(ConstantString.CancelledMessage, exports.ErrorType.Cancelled);
|
|
1486
1940
|
}
|
|
1487
|
-
const [updatedManifest, apiPlugin] = yield ManifestUpdater.updateManifestWithAiPlugin(manifestPath, outputSpecPath, pluginFilePath, newSpec, this.options);
|
|
1941
|
+
const [updatedManifest, apiPlugin] = yield ManifestUpdater.updateManifestWithAiPlugin(manifestPath, outputSpecPath, pluginFilePath, newSpec, this.options, authInfo);
|
|
1488
1942
|
yield fs__default['default'].outputJSON(manifestPath, updatedManifest, { spaces: 2 });
|
|
1489
1943
|
yield fs__default['default'].outputJSON(pluginFilePath, apiPlugin, { spaces: 2 });
|
|
1490
1944
|
}
|
|
@@ -1514,32 +1968,11 @@ class SpecParser {
|
|
|
1514
1968
|
const newSpecs = yield this.getFilteredSpecs(filter, signal);
|
|
1515
1969
|
const newUnResolvedSpec = newSpecs[0];
|
|
1516
1970
|
const newSpec = newSpecs[1];
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
for (const method in newSpec.paths[url]) {
|
|
1521
|
-
const operation = newSpec.paths[url][method];
|
|
1522
|
-
const authArray = Utils.getAuthArray(operation.security, newSpec);
|
|
1523
|
-
if (authArray && authArray.length > 0) {
|
|
1524
|
-
AuthSet.add(authArray[0][0].authSchema);
|
|
1525
|
-
if (AuthSet.size > 1) {
|
|
1526
|
-
hasMultipleAPIKeyAuth = true;
|
|
1527
|
-
break;
|
|
1528
|
-
}
|
|
1529
|
-
}
|
|
1530
|
-
}
|
|
1531
|
-
}
|
|
1532
|
-
if (hasMultipleAPIKeyAuth) {
|
|
1533
|
-
throw new SpecParserError(ConstantString.MultipleAPIKeyNotSupported, exports.ErrorType.MultipleAPIKeyNotSupported);
|
|
1534
|
-
}
|
|
1535
|
-
let resultStr;
|
|
1536
|
-
if (outputSpecPath.endsWith(".yaml") || outputSpecPath.endsWith(".yml")) {
|
|
1537
|
-
resultStr = jsyaml__default['default'].dump(newUnResolvedSpec);
|
|
1971
|
+
let authInfo = undefined;
|
|
1972
|
+
if (this.options.projectType === exports.ProjectType.SME) {
|
|
1973
|
+
authInfo = Utils.getAuthInfo(newSpec);
|
|
1538
1974
|
}
|
|
1539
|
-
|
|
1540
|
-
resultStr = JSON.stringify(newUnResolvedSpec, null, 2);
|
|
1541
|
-
}
|
|
1542
|
-
yield fs__default['default'].outputFile(outputSpecPath, resultStr);
|
|
1975
|
+
yield this.saveFilterSpec(outputSpecPath, newUnResolvedSpec);
|
|
1543
1976
|
if (adaptiveCardFolder) {
|
|
1544
1977
|
for (const url in newSpec.paths) {
|
|
1545
1978
|
for (const method in newSpec.paths[url]) {
|
|
@@ -1569,8 +2002,7 @@ class SpecParser {
|
|
|
1569
2002
|
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
|
1570
2003
|
throw new SpecParserError(ConstantString.CancelledMessage, exports.ErrorType.Cancelled);
|
|
1571
2004
|
}
|
|
1572
|
-
const
|
|
1573
|
-
const [updatedManifest, warnings] = yield ManifestUpdater.updateManifest(manifestPath, outputSpecPath, newSpec, this.options, adaptiveCardFolder, auth);
|
|
2005
|
+
const [updatedManifest, warnings] = yield ManifestUpdater.updateManifest(manifestPath, outputSpecPath, newSpec, this.options, adaptiveCardFolder, authInfo);
|
|
1574
2006
|
yield fs__default['default'].outputJSON(manifestPath, updatedManifest, { spaces: 2 });
|
|
1575
2007
|
result.warnings.push(...warnings);
|
|
1576
2008
|
}
|
|
@@ -1598,13 +2030,30 @@ class SpecParser {
|
|
|
1598
2030
|
}
|
|
1599
2031
|
});
|
|
1600
2032
|
}
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
2033
|
+
getAPIs(spec) {
|
|
2034
|
+
const validator = this.getValidator(spec);
|
|
2035
|
+
const apiMap = validator.listAPIs();
|
|
2036
|
+
return apiMap;
|
|
2037
|
+
}
|
|
2038
|
+
getValidator(spec) {
|
|
2039
|
+
if (this.validator) {
|
|
2040
|
+
return this.validator;
|
|
1604
2041
|
}
|
|
1605
|
-
const
|
|
1606
|
-
this.
|
|
1607
|
-
return
|
|
2042
|
+
const validator = ValidatorFactory.create(spec, this.options);
|
|
2043
|
+
this.validator = validator;
|
|
2044
|
+
return validator;
|
|
2045
|
+
}
|
|
2046
|
+
saveFilterSpec(outputSpecPath, unResolvedSpec) {
|
|
2047
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2048
|
+
let resultStr;
|
|
2049
|
+
if (outputSpecPath.endsWith(".yaml") || outputSpecPath.endsWith(".yml")) {
|
|
2050
|
+
resultStr = jsyaml__default['default'].dump(unResolvedSpec);
|
|
2051
|
+
}
|
|
2052
|
+
else {
|
|
2053
|
+
resultStr = JSON.stringify(unResolvedSpec, null, 2);
|
|
2054
|
+
}
|
|
2055
|
+
yield fs__default['default'].outputFile(outputSpecPath, resultStr);
|
|
2056
|
+
});
|
|
1608
2057
|
}
|
|
1609
2058
|
}
|
|
1610
2059
|
|