@microsoft/m365-spec-parser 0.1.1-alpha.e17ffd4d1.0 → 0.1.1-alpha.e1b11d5b2.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 +627 -338
- package/dist/index.esm2017.js.map +1 -1
- package/dist/index.esm2017.mjs +1067 -636
- package/dist/index.esm2017.mjs.map +1 -1
- package/dist/index.esm5.js +627 -338
- package/dist/index.esm5.js.map +1 -1
- package/dist/index.node.cjs.js +1079 -644
- package/dist/index.node.cjs.js.map +1 -1
- package/dist/src/adaptiveCardWrapper.d.ts +2 -0
- package/dist/src/constants.d.ts +8 -3
- package/dist/src/index.d.ts +1 -1
- package/dist/src/interfaces.d.ts +79 -16
- package/dist/src/manifestUpdater.d.ts +7 -4
- package/dist/src/specParser.browser.d.ts +3 -2
- package/dist/src/specParser.d.ts +5 -3
- package/dist/src/utils.d.ts +15 -32
- package/package.json +3 -4
package/dist/index.node.cjs.js
CHANGED
|
@@ -62,6 +62,7 @@ exports.ErrorType = void 0;
|
|
|
62
62
|
ErrorType["ResolveServerUrlFailed"] = "resolve-server-url-failed";
|
|
63
63
|
ErrorType["SwaggerNotSupported"] = "swagger-not-supported";
|
|
64
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,6 +133,7 @@ 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.";
|
|
136
|
+
ConstantString.SpecVersionNotSupported = "Unsupported OpenAPI version %s. Please use version 3.0.x.";
|
|
120
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";
|
|
@@ -128,9 +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 =
|
|
133
|
-
|
|
150
|
+
ConstantString.RegistrationIdPostfix = {
|
|
151
|
+
apiKey: "REGISTRATION_ID",
|
|
152
|
+
oauth2: "CONFIGURATION_ID",
|
|
153
|
+
http: "REGISTRATION_ID",
|
|
154
|
+
openIdConnect: "REGISTRATION_ID",
|
|
155
|
+
};
|
|
134
156
|
ConstantString.ResponseCodeFor20X = [
|
|
135
157
|
"200",
|
|
136
158
|
"201",
|
|
@@ -189,9 +211,11 @@ ConstantString.ShortDescriptionMaxLens = 80;
|
|
|
189
211
|
ConstantString.FullDescriptionMaxLens = 4000;
|
|
190
212
|
ConstantString.CommandDescriptionMaxLens = 128;
|
|
191
213
|
ConstantString.ParameterDescriptionMaxLens = 128;
|
|
214
|
+
ConstantString.ConversationStarterMaxLens = 50;
|
|
192
215
|
ConstantString.CommandTitleMaxLens = 32;
|
|
193
216
|
ConstantString.ParameterTitleMaxLens = 32;
|
|
194
|
-
ConstantString.SMERequiredParamsMaxNum = 5;
|
|
217
|
+
ConstantString.SMERequiredParamsMaxNum = 5;
|
|
218
|
+
ConstantString.DefaultPluginId = "plugin_1";
|
|
195
219
|
|
|
196
220
|
// Copyright (c) Microsoft Corporation.
|
|
197
221
|
class SpecParserError extends Error {
|
|
@@ -214,253 +238,33 @@ class Utils {
|
|
|
214
238
|
}
|
|
215
239
|
return false;
|
|
216
240
|
}
|
|
217
|
-
static checkParameters(paramObject, isCopilot) {
|
|
218
|
-
const paramResult = {
|
|
219
|
-
requiredNum: 0,
|
|
220
|
-
optionalNum: 0,
|
|
221
|
-
isValid: true,
|
|
222
|
-
};
|
|
223
|
-
if (!paramObject) {
|
|
224
|
-
return paramResult;
|
|
225
|
-
}
|
|
226
|
-
for (let i = 0; i < paramObject.length; i++) {
|
|
227
|
-
const param = paramObject[i];
|
|
228
|
-
const schema = param.schema;
|
|
229
|
-
if (isCopilot && this.hasNestedObjectInSchema(schema)) {
|
|
230
|
-
paramResult.isValid = false;
|
|
231
|
-
continue;
|
|
232
|
-
}
|
|
233
|
-
const isRequiredWithoutDefault = param.required && schema.default === undefined;
|
|
234
|
-
if (isCopilot) {
|
|
235
|
-
if (isRequiredWithoutDefault) {
|
|
236
|
-
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
237
|
-
}
|
|
238
|
-
else {
|
|
239
|
-
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
240
|
-
}
|
|
241
|
-
continue;
|
|
242
|
-
}
|
|
243
|
-
if (param.in === "header" || param.in === "cookie") {
|
|
244
|
-
if (isRequiredWithoutDefault) {
|
|
245
|
-
paramResult.isValid = false;
|
|
246
|
-
}
|
|
247
|
-
continue;
|
|
248
|
-
}
|
|
249
|
-
if (schema.type !== "boolean" &&
|
|
250
|
-
schema.type !== "string" &&
|
|
251
|
-
schema.type !== "number" &&
|
|
252
|
-
schema.type !== "integer") {
|
|
253
|
-
if (isRequiredWithoutDefault) {
|
|
254
|
-
paramResult.isValid = false;
|
|
255
|
-
}
|
|
256
|
-
continue;
|
|
257
|
-
}
|
|
258
|
-
if (param.in === "query" || param.in === "path") {
|
|
259
|
-
if (isRequiredWithoutDefault) {
|
|
260
|
-
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
261
|
-
}
|
|
262
|
-
else {
|
|
263
|
-
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
return paramResult;
|
|
268
|
-
}
|
|
269
|
-
static checkPostBody(schema, isRequired = false, isCopilot = false) {
|
|
270
|
-
var _a;
|
|
271
|
-
const paramResult = {
|
|
272
|
-
requiredNum: 0,
|
|
273
|
-
optionalNum: 0,
|
|
274
|
-
isValid: true,
|
|
275
|
-
};
|
|
276
|
-
if (Object.keys(schema).length === 0) {
|
|
277
|
-
return paramResult;
|
|
278
|
-
}
|
|
279
|
-
const isRequiredWithoutDefault = isRequired && schema.default === undefined;
|
|
280
|
-
if (isCopilot && this.hasNestedObjectInSchema(schema)) {
|
|
281
|
-
paramResult.isValid = false;
|
|
282
|
-
return paramResult;
|
|
283
|
-
}
|
|
284
|
-
if (schema.type === "string" ||
|
|
285
|
-
schema.type === "integer" ||
|
|
286
|
-
schema.type === "boolean" ||
|
|
287
|
-
schema.type === "number") {
|
|
288
|
-
if (isRequiredWithoutDefault) {
|
|
289
|
-
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
290
|
-
}
|
|
291
|
-
else {
|
|
292
|
-
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
else if (schema.type === "object") {
|
|
296
|
-
const { properties } = schema;
|
|
297
|
-
for (const property in properties) {
|
|
298
|
-
let isRequired = false;
|
|
299
|
-
if (schema.required && ((_a = schema.required) === null || _a === void 0 ? void 0 : _a.indexOf(property)) >= 0) {
|
|
300
|
-
isRequired = true;
|
|
301
|
-
}
|
|
302
|
-
const result = Utils.checkPostBody(properties[property], isRequired, isCopilot);
|
|
303
|
-
paramResult.requiredNum += result.requiredNum;
|
|
304
|
-
paramResult.optionalNum += result.optionalNum;
|
|
305
|
-
paramResult.isValid = paramResult.isValid && result.isValid;
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
else {
|
|
309
|
-
if (isRequiredWithoutDefault && !isCopilot) {
|
|
310
|
-
paramResult.isValid = false;
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
return paramResult;
|
|
314
|
-
}
|
|
315
241
|
static containMultipleMediaTypes(bodyObject) {
|
|
316
242
|
return Object.keys((bodyObject === null || bodyObject === void 0 ? void 0 : bodyObject.content) || {}).length > 1;
|
|
317
243
|
}
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
* @param {string} method - The HTTP method of the API.
|
|
321
|
-
* @param {string} path - The path of the API.
|
|
322
|
-
* @param {OpenAPIV3.Document} spec - The OpenAPI specification document.
|
|
323
|
-
* @returns {boolean} - Returns true if the API is supported, false otherwise.
|
|
324
|
-
* @description The following APIs are supported:
|
|
325
|
-
* 1. only support Get/Post operation without auth property
|
|
326
|
-
* 2. parameter inside query or path only support string, number, boolean and integer
|
|
327
|
-
* 3. parameter inside post body only support string, number, boolean, integer and object
|
|
328
|
-
* 4. request body + required parameters <= 1
|
|
329
|
-
* 5. response body should be “application/json” and not empty, and response code should be 20X
|
|
330
|
-
* 6. only support request body with “application/json” content type
|
|
331
|
-
*/
|
|
332
|
-
static isSupportedApi(method, path, spec, options) {
|
|
333
|
-
var _a;
|
|
334
|
-
const pathObj = spec.paths[path];
|
|
335
|
-
method = method.toLocaleLowerCase();
|
|
336
|
-
if (pathObj) {
|
|
337
|
-
if (((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && pathObj[method]) {
|
|
338
|
-
const securities = pathObj[method].security;
|
|
339
|
-
const isTeamsAi = options.projectType === exports.ProjectType.TeamsAi;
|
|
340
|
-
const isCopilot = options.projectType === exports.ProjectType.Copilot;
|
|
341
|
-
// Teams AI project doesn't care about auth, it will use authProvider for user to implement
|
|
342
|
-
if (!isTeamsAi) {
|
|
343
|
-
const authArray = Utils.getAuthArray(securities, spec);
|
|
344
|
-
if (!Utils.isSupportedAuth(authArray, options)) {
|
|
345
|
-
return false;
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
const operationObject = pathObj[method];
|
|
349
|
-
if (!options.allowMissingId && !operationObject.operationId) {
|
|
350
|
-
return false;
|
|
351
|
-
}
|
|
352
|
-
const paramObject = operationObject.parameters;
|
|
353
|
-
const requestBody = operationObject.requestBody;
|
|
354
|
-
const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
|
|
355
|
-
if (!isTeamsAi && Utils.containMultipleMediaTypes(requestBody)) {
|
|
356
|
-
return false;
|
|
357
|
-
}
|
|
358
|
-
const responseJson = Utils.getResponseJson(operationObject, isTeamsAi);
|
|
359
|
-
if (Object.keys(responseJson).length === 0) {
|
|
360
|
-
return false;
|
|
361
|
-
}
|
|
362
|
-
// Teams AI project doesn't care about request parameters/body
|
|
363
|
-
if (isTeamsAi) {
|
|
364
|
-
return true;
|
|
365
|
-
}
|
|
366
|
-
let requestBodyParamResult = {
|
|
367
|
-
requiredNum: 0,
|
|
368
|
-
optionalNum: 0,
|
|
369
|
-
isValid: true,
|
|
370
|
-
};
|
|
371
|
-
if (requestJsonBody) {
|
|
372
|
-
const requestBodySchema = requestJsonBody.schema;
|
|
373
|
-
if (isCopilot && requestBodySchema.type !== "object") {
|
|
374
|
-
return false;
|
|
375
|
-
}
|
|
376
|
-
requestBodyParamResult = Utils.checkPostBody(requestBodySchema, requestBody.required, isCopilot);
|
|
377
|
-
}
|
|
378
|
-
if (!requestBodyParamResult.isValid) {
|
|
379
|
-
return false;
|
|
380
|
-
}
|
|
381
|
-
const paramResult = Utils.checkParameters(paramObject, isCopilot);
|
|
382
|
-
if (!paramResult.isValid) {
|
|
383
|
-
return false;
|
|
384
|
-
}
|
|
385
|
-
// Copilot support arbitrary parameters
|
|
386
|
-
if (isCopilot) {
|
|
387
|
-
return true;
|
|
388
|
-
}
|
|
389
|
-
if (requestBodyParamResult.requiredNum + paramResult.requiredNum > 1) {
|
|
390
|
-
if (options.allowMultipleParameters &&
|
|
391
|
-
requestBodyParamResult.requiredNum + paramResult.requiredNum <=
|
|
392
|
-
ConstantString.SMERequiredParamsMaxNum) {
|
|
393
|
-
return true;
|
|
394
|
-
}
|
|
395
|
-
return false;
|
|
396
|
-
}
|
|
397
|
-
else if (requestBodyParamResult.requiredNum +
|
|
398
|
-
requestBodyParamResult.optionalNum +
|
|
399
|
-
paramResult.requiredNum +
|
|
400
|
-
paramResult.optionalNum ===
|
|
401
|
-
0) {
|
|
402
|
-
return false;
|
|
403
|
-
}
|
|
404
|
-
else {
|
|
405
|
-
return true;
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
return false;
|
|
410
|
-
}
|
|
411
|
-
static isSupportedAuth(authSchemaArray, options) {
|
|
412
|
-
if (authSchemaArray.length === 0) {
|
|
413
|
-
return true;
|
|
414
|
-
}
|
|
415
|
-
if (options.allowAPIKeyAuth || options.allowOauth2) {
|
|
416
|
-
// Currently we don't support multiple auth in one operation
|
|
417
|
-
if (authSchemaArray.length > 0 && authSchemaArray.every((auths) => auths.length > 1)) {
|
|
418
|
-
return false;
|
|
419
|
-
}
|
|
420
|
-
for (const auths of authSchemaArray) {
|
|
421
|
-
if (auths.length === 1) {
|
|
422
|
-
if (!options.allowOauth2 &&
|
|
423
|
-
options.allowAPIKeyAuth &&
|
|
424
|
-
Utils.isAPIKeyAuth(auths[0].authSchema)) {
|
|
425
|
-
return true;
|
|
426
|
-
}
|
|
427
|
-
else if (!options.allowAPIKeyAuth &&
|
|
428
|
-
options.allowOauth2 &&
|
|
429
|
-
Utils.isOAuthWithAuthCodeFlow(auths[0].authSchema)) {
|
|
430
|
-
return true;
|
|
431
|
-
}
|
|
432
|
-
else if (options.allowAPIKeyAuth &&
|
|
433
|
-
options.allowOauth2 &&
|
|
434
|
-
(Utils.isAPIKeyAuth(auths[0].authSchema) ||
|
|
435
|
-
Utils.isOAuthWithAuthCodeFlow(auths[0].authSchema))) {
|
|
436
|
-
return true;
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
return false;
|
|
244
|
+
static isBearerTokenAuth(authScheme) {
|
|
245
|
+
return authScheme.type === "http" && authScheme.scheme === "bearer";
|
|
442
246
|
}
|
|
443
|
-
static isAPIKeyAuth(
|
|
444
|
-
return
|
|
247
|
+
static isAPIKeyAuth(authScheme) {
|
|
248
|
+
return authScheme.type === "apiKey";
|
|
445
249
|
}
|
|
446
|
-
static isOAuthWithAuthCodeFlow(
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
return false;
|
|
250
|
+
static isOAuthWithAuthCodeFlow(authScheme) {
|
|
251
|
+
return !!(authScheme.type === "oauth2" &&
|
|
252
|
+
authScheme.flows &&
|
|
253
|
+
authScheme.flows.authorizationCode);
|
|
451
254
|
}
|
|
452
255
|
static getAuthArray(securities, spec) {
|
|
453
256
|
var _a;
|
|
454
257
|
const result = [];
|
|
455
258
|
const securitySchemas = (_a = spec.components) === null || _a === void 0 ? void 0 : _a.securitySchemes;
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
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];
|
|
459
263
|
const authArray = [];
|
|
460
264
|
for (const name in security) {
|
|
461
265
|
const auth = securitySchemas[name];
|
|
462
266
|
authArray.push({
|
|
463
|
-
|
|
267
|
+
authScheme: auth,
|
|
464
268
|
name: name,
|
|
465
269
|
});
|
|
466
270
|
}
|
|
@@ -472,17 +276,39 @@ class Utils {
|
|
|
472
276
|
result.sort((a, b) => a[0].name.localeCompare(b[0].name));
|
|
473
277
|
return result;
|
|
474
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
|
+
}
|
|
475
298
|
static updateFirstLetter(str) {
|
|
476
299
|
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
477
300
|
}
|
|
478
|
-
static getResponseJson(operationObject
|
|
301
|
+
static getResponseJson(operationObject) {
|
|
479
302
|
var _a, _b;
|
|
480
303
|
let json = {};
|
|
304
|
+
let multipleMediaType = false;
|
|
481
305
|
for (const code of ConstantString.ResponseCodeFor20X) {
|
|
482
306
|
const responseObject = (_a = operationObject === null || operationObject === void 0 ? void 0 : operationObject.responses) === null || _a === void 0 ? void 0 : _a[code];
|
|
483
307
|
if ((_b = responseObject === null || responseObject === void 0 ? void 0 : responseObject.content) === null || _b === void 0 ? void 0 : _b["application/json"]) {
|
|
308
|
+
multipleMediaType = false;
|
|
484
309
|
json = responseObject.content["application/json"];
|
|
485
|
-
if (
|
|
310
|
+
if (Utils.containMultipleMediaTypes(responseObject)) {
|
|
311
|
+
multipleMediaType = true;
|
|
486
312
|
json = {};
|
|
487
313
|
}
|
|
488
314
|
else {
|
|
@@ -490,7 +316,7 @@ class Utils {
|
|
|
490
316
|
}
|
|
491
317
|
}
|
|
492
318
|
}
|
|
493
|
-
return json;
|
|
319
|
+
return { json, multipleMediaType };
|
|
494
320
|
}
|
|
495
321
|
static convertPathToCamelCase(path) {
|
|
496
322
|
const pathSegments = path.split(/[./{]/);
|
|
@@ -510,10 +336,10 @@ class Utils {
|
|
|
510
336
|
return undefined;
|
|
511
337
|
}
|
|
512
338
|
}
|
|
513
|
-
static
|
|
339
|
+
static resolveEnv(str) {
|
|
514
340
|
const placeHolderReg = /\${{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*}}/g;
|
|
515
|
-
let matches = placeHolderReg.exec(
|
|
516
|
-
let
|
|
341
|
+
let matches = placeHolderReg.exec(str);
|
|
342
|
+
let newStr = str;
|
|
517
343
|
while (matches != null) {
|
|
518
344
|
const envVar = matches[1];
|
|
519
345
|
const envVal = process.env[envVar];
|
|
@@ -521,17 +347,17 @@ class Utils {
|
|
|
521
347
|
throw new Error(Utils.format(ConstantString.ResolveServerUrlFailed, envVar));
|
|
522
348
|
}
|
|
523
349
|
else {
|
|
524
|
-
|
|
350
|
+
newStr = newStr.replace(matches[0], envVal);
|
|
525
351
|
}
|
|
526
|
-
matches = placeHolderReg.exec(
|
|
352
|
+
matches = placeHolderReg.exec(str);
|
|
527
353
|
}
|
|
528
|
-
return
|
|
354
|
+
return newStr;
|
|
529
355
|
}
|
|
530
356
|
static checkServerUrl(servers) {
|
|
531
357
|
const errors = [];
|
|
532
358
|
let serverUrl;
|
|
533
359
|
try {
|
|
534
|
-
serverUrl = Utils.
|
|
360
|
+
serverUrl = Utils.resolveEnv(servers[0].url);
|
|
535
361
|
}
|
|
536
362
|
catch (err) {
|
|
537
363
|
errors.push({
|
|
@@ -562,6 +388,7 @@ class Utils {
|
|
|
562
388
|
return errors;
|
|
563
389
|
}
|
|
564
390
|
static validateServer(spec, options) {
|
|
391
|
+
var _a;
|
|
565
392
|
const errors = [];
|
|
566
393
|
let hasTopLevelServers = false;
|
|
567
394
|
let hasPathLevelServers = false;
|
|
@@ -582,7 +409,7 @@ class Utils {
|
|
|
582
409
|
}
|
|
583
410
|
for (const method in methods) {
|
|
584
411
|
const operationObject = methods[method];
|
|
585
|
-
if (
|
|
412
|
+
if (((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
|
|
586
413
|
if ((operationObject === null || operationObject === void 0 ? void 0 : operationObject.servers) && operationObject.servers.length >= 1) {
|
|
587
414
|
hasOperationLevelServers = true;
|
|
588
415
|
const serverErrors = Utils.checkServerUrl(operationObject.servers);
|
|
@@ -625,6 +452,7 @@ class Utils {
|
|
|
625
452
|
Utils.updateParameterWithInputType(schema, parameter);
|
|
626
453
|
}
|
|
627
454
|
if (isRequired && schema.default === undefined) {
|
|
455
|
+
parameter.isRequired = true;
|
|
628
456
|
requiredParams.push(parameter);
|
|
629
457
|
}
|
|
630
458
|
else {
|
|
@@ -688,6 +516,7 @@ class Utils {
|
|
|
688
516
|
}
|
|
689
517
|
if (param.in !== "header" && param.in !== "cookie") {
|
|
690
518
|
if (param.required && (schema === null || schema === void 0 ? void 0 : schema.default) === undefined) {
|
|
519
|
+
parameter.isRequired = true;
|
|
691
520
|
requiredParams.push(parameter);
|
|
692
521
|
}
|
|
693
522
|
else {
|
|
@@ -707,13 +536,7 @@ class Utils {
|
|
|
707
536
|
}
|
|
708
537
|
}
|
|
709
538
|
const operationId = operationItem.operationId;
|
|
710
|
-
const parameters = [];
|
|
711
|
-
if (requiredParams.length !== 0) {
|
|
712
|
-
parameters.push(...requiredParams);
|
|
713
|
-
}
|
|
714
|
-
else {
|
|
715
|
-
parameters.push(optionalParams[0]);
|
|
716
|
-
}
|
|
539
|
+
const parameters = [...requiredParams, ...optionalParams];
|
|
717
540
|
const command = {
|
|
718
541
|
context: ["compose"],
|
|
719
542
|
type: "query",
|
|
@@ -722,354 +545,575 @@ class Utils {
|
|
|
722
545
|
parameters: parameters,
|
|
723
546
|
description: ((_b = operationItem.description) !== null && _b !== void 0 ? _b : "").slice(0, ConstantString.CommandDescriptionMaxLens),
|
|
724
547
|
};
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
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 "";
|
|
732
560
|
}
|
|
733
|
-
|
|
561
|
+
let safeRegistrationIdEnvName = authName.toUpperCase().replace(/[^A-Z0-9_]/g, "_");
|
|
562
|
+
if (!safeRegistrationIdEnvName.match(/^[A-Z]/)) {
|
|
563
|
+
safeRegistrationIdEnvName = "PREFIX_" + safeRegistrationIdEnvName;
|
|
564
|
+
}
|
|
565
|
+
return safeRegistrationIdEnvName;
|
|
734
566
|
}
|
|
735
|
-
static
|
|
736
|
-
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;
|
|
737
586
|
const result = {};
|
|
738
587
|
for (const path in paths) {
|
|
739
588
|
const methods = paths[path];
|
|
740
589
|
for (const method in methods) {
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
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
|
+
};
|
|
744
598
|
}
|
|
745
599
|
}
|
|
746
600
|
}
|
|
601
|
+
this.apiMap = result;
|
|
747
602
|
return result;
|
|
748
603
|
}
|
|
749
|
-
|
|
750
|
-
const
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
});
|
|
757
|
-
}
|
|
758
|
-
// Server validation
|
|
759
|
-
const serverErrors = Utils.validateServer(spec, options);
|
|
760
|
-
errors.push(...serverErrors);
|
|
761
|
-
// Remote reference not supported
|
|
762
|
-
const refPaths = parser.$refs.paths();
|
|
763
|
-
// refPaths [0] is the current spec file path
|
|
764
|
-
if (refPaths.length > 1) {
|
|
765
|
-
errors.push({
|
|
766
|
-
type: exports.ErrorType.RemoteRefNotSupported,
|
|
767
|
-
content: Utils.format(ConstantString.RemoteRefNotSupported, refPaths.join(", ")),
|
|
768
|
-
data: refPaths,
|
|
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,
|
|
769
611
|
});
|
|
770
612
|
}
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
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({
|
|
775
633
|
type: exports.ErrorType.NoSupportedApi,
|
|
776
634
|
content: ConstantString.NoSupportedApi,
|
|
635
|
+
data,
|
|
777
636
|
});
|
|
778
637
|
}
|
|
638
|
+
return result;
|
|
639
|
+
}
|
|
640
|
+
validateSpecOperationId() {
|
|
641
|
+
const result = { errors: [], warnings: [] };
|
|
642
|
+
const apiMap = this.listAPIs();
|
|
779
643
|
// OperationId missing
|
|
780
644
|
const apisMissingOperationId = [];
|
|
781
645
|
for (const key in apiMap) {
|
|
782
|
-
const
|
|
783
|
-
if (!
|
|
646
|
+
const { operation } = apiMap[key];
|
|
647
|
+
if (!operation.operationId) {
|
|
784
648
|
apisMissingOperationId.push(key);
|
|
785
649
|
}
|
|
786
650
|
}
|
|
787
651
|
if (apisMissingOperationId.length > 0) {
|
|
788
|
-
warnings.push({
|
|
652
|
+
result.warnings.push({
|
|
789
653
|
type: exports.WarningType.OperationIdMissing,
|
|
790
654
|
content: Utils.format(ConstantString.MissingOperationId, apisMissingOperationId.join(", ")),
|
|
791
655
|
data: apisMissingOperationId,
|
|
792
656
|
});
|
|
793
657
|
}
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
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;
|
|
797
666
|
}
|
|
798
|
-
|
|
799
|
-
|
|
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;
|
|
800
672
|
}
|
|
801
|
-
return
|
|
802
|
-
status,
|
|
803
|
-
warnings,
|
|
804
|
-
errors,
|
|
805
|
-
};
|
|
673
|
+
return result;
|
|
806
674
|
}
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
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;
|
|
813
690
|
}
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
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);
|
|
817
697
|
}
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
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));
|
|
821
702
|
}
|
|
822
|
-
return
|
|
703
|
+
return result;
|
|
823
704
|
}
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
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: [] };
|
|
842
729
|
}
|
|
843
730
|
}
|
|
844
|
-
newPaths[path][methodName] = unResolveSpec.paths[path][methodName];
|
|
845
|
-
// Add the operationId if missing
|
|
846
|
-
if (!newPaths[path][methodName].operationId) {
|
|
847
|
-
newPaths[path][methodName].operationId = `${methodName}${Utils.convertPathToCamelCase(path)}`;
|
|
848
|
-
}
|
|
849
731
|
}
|
|
850
|
-
newSpec.paths = newPaths;
|
|
851
|
-
return newSpec;
|
|
852
732
|
}
|
|
853
|
-
|
|
854
|
-
throw new SpecParserError(err.toString(), exports.ErrorType.FilterSpecFailed);
|
|
855
|
-
}
|
|
856
|
-
}
|
|
857
|
-
}
|
|
858
|
-
|
|
859
|
-
// Copyright (c) Microsoft Corporation.
|
|
860
|
-
class ManifestUpdater {
|
|
861
|
-
static updateManifestWithAiPlugin(manifestPath, outputSpecPath, apiPluginFilePath, spec, options) {
|
|
862
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
863
|
-
const manifest = yield fs__default['default'].readJSON(manifestPath);
|
|
864
|
-
const apiPluginRelativePath = ManifestUpdater.getRelativePath(manifestPath, apiPluginFilePath);
|
|
865
|
-
manifest.plugins = [
|
|
866
|
-
{
|
|
867
|
-
pluginFile: apiPluginRelativePath,
|
|
868
|
-
},
|
|
869
|
-
];
|
|
870
|
-
ManifestUpdater.updateManifestDescription(manifest, spec);
|
|
871
|
-
const specRelativePath = ManifestUpdater.getRelativePath(manifestPath, outputSpecPath);
|
|
872
|
-
const apiPlugin = ManifestUpdater.generatePluginManifestSchema(spec, specRelativePath, options);
|
|
873
|
-
return [manifest, apiPlugin];
|
|
874
|
-
});
|
|
733
|
+
return { isValid: false, reason: [exports.ErrorType.AuthTypeIsNotSupported] };
|
|
875
734
|
}
|
|
876
|
-
|
|
877
|
-
var _a
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
735
|
+
checkPostBodySchema(schema, isRequired = false) {
|
|
736
|
+
var _a;
|
|
737
|
+
const paramResult = {
|
|
738
|
+
requiredNum: 0,
|
|
739
|
+
optionalNum: 0,
|
|
740
|
+
isValid: true,
|
|
741
|
+
reason: [],
|
|
881
742
|
};
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
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
|
+
}
|
|
885
753
|
if (schema.type === "string" ||
|
|
886
|
-
schema.type === "boolean" ||
|
|
887
754
|
schema.type === "integer" ||
|
|
888
|
-
schema.type === "
|
|
889
|
-
schema.type === "
|
|
890
|
-
|
|
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
|
+
}
|
|
891
777
|
}
|
|
892
778
|
else {
|
|
893
|
-
|
|
779
|
+
if (isRequiredWithoutDefault && !isCopilot) {
|
|
780
|
+
paramResult.isValid = false;
|
|
781
|
+
paramResult.reason.push(exports.ErrorType.PostBodyContainsRequiredUnsupportedSchema);
|
|
782
|
+
}
|
|
894
783
|
}
|
|
895
|
-
return
|
|
784
|
+
return paramResult;
|
|
896
785
|
}
|
|
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
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
name: operationId,
|
|
950
|
-
description: description,
|
|
951
|
-
parameters: parameters,
|
|
952
|
-
};
|
|
953
|
-
functions.push(funcObj);
|
|
954
|
-
functionNames.push(operationId);
|
|
955
|
-
}
|
|
956
|
-
}
|
|
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;
|
|
957
838
|
}
|
|
958
839
|
}
|
|
959
840
|
}
|
|
960
|
-
|
|
961
|
-
schema_version: "v2",
|
|
962
|
-
name_for_human: spec.info.title,
|
|
963
|
-
description_for_human: (_c = spec.info.description) !== null && _c !== void 0 ? _c : "<Please add description of the plugin>",
|
|
964
|
-
functions: functions,
|
|
965
|
-
runtimes: [
|
|
966
|
-
{
|
|
967
|
-
type: "OpenApi",
|
|
968
|
-
auth: {
|
|
969
|
-
type: "none", // TODO, support auth in the future
|
|
970
|
-
},
|
|
971
|
-
spec: {
|
|
972
|
-
url: specRelativePath,
|
|
973
|
-
},
|
|
974
|
-
run_for_functions: functionNames,
|
|
975
|
-
},
|
|
976
|
-
],
|
|
977
|
-
};
|
|
978
|
-
return apiPlugin;
|
|
841
|
+
return paramResult;
|
|
979
842
|
}
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
const
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
let warnings = [];
|
|
987
|
-
if (options.projectType === exports.ProjectType.SME) {
|
|
988
|
-
const updateResult = yield ManifestUpdater.generateCommands(spec, manifestPath, options, adaptiveCardFolder);
|
|
989
|
-
const commands = updateResult[0];
|
|
990
|
-
warnings = updateResult[1];
|
|
991
|
-
const composeExtension = {
|
|
992
|
-
composeExtensionType: "apiBased",
|
|
993
|
-
apiSpecificationFile: ManifestUpdater.getRelativePath(manifestPath, outputSpecPath),
|
|
994
|
-
commands: commands,
|
|
995
|
-
};
|
|
996
|
-
if (authInfo) {
|
|
997
|
-
let auth = authInfo.authSchema;
|
|
998
|
-
if (Utils.isAPIKeyAuth(auth)) {
|
|
999
|
-
auth = auth;
|
|
1000
|
-
const safeApiSecretRegistrationId = Utils.getSafeRegistrationIdEnvName(`${authInfo.name}_${ConstantString.RegistrationIdPostfix}`);
|
|
1001
|
-
composeExtension.authorization = {
|
|
1002
|
-
authType: "apiSecretServiceAuth",
|
|
1003
|
-
apiSecretServiceAuthConfiguration: {
|
|
1004
|
-
apiSecretRegistrationId: `\${{${safeApiSecretRegistrationId}}}`,
|
|
1005
|
-
},
|
|
1006
|
-
};
|
|
1007
|
-
}
|
|
1008
|
-
else if (Utils.isOAuthWithAuthCodeFlow(auth)) {
|
|
1009
|
-
const safeOAuth2RegistrationId = Utils.getSafeRegistrationIdEnvName(`${authInfo.name}_${ConstantString.OAuthRegistrationIdPostFix}`);
|
|
1010
|
-
composeExtension.authorization = {
|
|
1011
|
-
authType: "oAuth2.0",
|
|
1012
|
-
oAuthConfiguration: {
|
|
1013
|
-
oauthConfigurationId: `\${{${safeOAuth2RegistrationId}}}`,
|
|
1014
|
-
},
|
|
1015
|
-
};
|
|
1016
|
-
updatedPart.webApplicationInfo = {
|
|
1017
|
-
id: "${{AAD_APP_CLIENT_ID}}",
|
|
1018
|
-
resource: "api://${{DOMAIN}}/${{AAD_APP_CLIENT_ID}}",
|
|
1019
|
-
};
|
|
1020
|
-
}
|
|
1021
|
-
}
|
|
1022
|
-
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;
|
|
1023
849
|
}
|
|
1024
|
-
updatedPart.description = originalManifest.description;
|
|
1025
|
-
ManifestUpdater.updateManifestDescription(updatedPart, spec);
|
|
1026
|
-
const updatedManifest = Object.assign(Object.assign({}, originalManifest), updatedPart);
|
|
1027
|
-
return [updatedManifest, warnings];
|
|
1028
850
|
}
|
|
1029
|
-
|
|
1030
|
-
|
|
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);
|
|
1031
909
|
}
|
|
1032
|
-
|
|
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;
|
|
1033
921
|
}
|
|
1034
|
-
|
|
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) {
|
|
1035
1064
|
var _a;
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
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];
|
|
1063
1102
|
}
|
|
1064
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
|
+
}
|
|
1065
1109
|
}
|
|
1066
1110
|
}
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1111
|
+
newSpec.paths = newPaths;
|
|
1112
|
+
return newSpec;
|
|
1113
|
+
}
|
|
1114
|
+
catch (err) {
|
|
1115
|
+
throw new SpecParserError(err.toString(), exports.ErrorType.FilterSpecFailed);
|
|
1116
|
+
}
|
|
1073
1117
|
}
|
|
1074
1118
|
}
|
|
1075
1119
|
|
|
@@ -1077,7 +1121,7 @@ class ManifestUpdater {
|
|
|
1077
1121
|
class AdaptiveCardGenerator {
|
|
1078
1122
|
static generateAdaptiveCard(operationItem) {
|
|
1079
1123
|
try {
|
|
1080
|
-
const json = Utils.getResponseJson(operationItem);
|
|
1124
|
+
const { json } = Utils.getResponseJson(operationItem);
|
|
1081
1125
|
let cardBody = [];
|
|
1082
1126
|
let schema = json.schema;
|
|
1083
1127
|
let jsonPath = "$";
|
|
@@ -1243,6 +1287,27 @@ function wrapAdaptiveCard(card, jsonPath) {
|
|
|
1243
1287
|
};
|
|
1244
1288
|
return result;
|
|
1245
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
|
+
}
|
|
1246
1311
|
/**
|
|
1247
1312
|
* Infers the preview card template from an Adaptive Card and a JSON path.
|
|
1248
1313
|
* The preview card template includes a title and an optional subtitle and image.
|
|
@@ -1255,11 +1320,29 @@ function wrapAdaptiveCard(card, jsonPath) {
|
|
|
1255
1320
|
* @returns The inferred preview card template.
|
|
1256
1321
|
*/
|
|
1257
1322
|
function inferPreviewCardTemplate(card) {
|
|
1258
|
-
var _a;
|
|
1259
1323
|
const result = {
|
|
1260
|
-
title: "",
|
|
1324
|
+
title: "result",
|
|
1261
1325
|
};
|
|
1262
|
-
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();
|
|
1263
1346
|
let rootObject;
|
|
1264
1347
|
if (((_a = card.body[0]) === null || _a === void 0 ? void 0 : _a.type) === ConstantString.ContainerType) {
|
|
1265
1348
|
rootObject = card.body[0].items;
|
|
@@ -1272,56 +1355,380 @@ function inferPreviewCardTemplate(card) {
|
|
|
1272
1355
|
const textElement = element;
|
|
1273
1356
|
const index = textElement.text.indexOf("${if(");
|
|
1274
1357
|
if (index > 0) {
|
|
1275
|
-
|
|
1276
|
-
|
|
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);
|
|
1277
1372
|
}
|
|
1278
1373
|
}
|
|
1279
1374
|
}
|
|
1280
|
-
for (const
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
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);
|
|
1285
1379
|
}
|
|
1286
1380
|
else if (!result.subtitle &&
|
|
1287
|
-
Utils.isWellKnownName(
|
|
1288
|
-
result.subtitle =
|
|
1289
|
-
|
|
1381
|
+
Utils.isWellKnownName(name, ConstantString.WellknownSubtitleName)) {
|
|
1382
|
+
result.subtitle = name;
|
|
1383
|
+
nameSet.delete(name);
|
|
1290
1384
|
}
|
|
1291
|
-
else if (!result.
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
if (property) {
|
|
1295
|
-
result.image = {
|
|
1296
|
-
url: `\${${property}}`,
|
|
1297
|
-
alt: text,
|
|
1298
|
-
$when: `\${${property} != null}`,
|
|
1299
|
-
};
|
|
1300
|
-
}
|
|
1301
|
-
textBlockElements.delete(element);
|
|
1385
|
+
else if (!result.imageUrl && Utils.isWellKnownName(name, ConstantString.WellknownImageName)) {
|
|
1386
|
+
result.imageUrl = name;
|
|
1387
|
+
nameSet.delete(name);
|
|
1302
1388
|
}
|
|
1303
1389
|
}
|
|
1304
|
-
for (const
|
|
1305
|
-
const text = element.text;
|
|
1390
|
+
for (const name of nameSet) {
|
|
1306
1391
|
if (!result.title) {
|
|
1307
|
-
result.title =
|
|
1308
|
-
|
|
1392
|
+
result.title = name;
|
|
1393
|
+
nameSet.delete(name);
|
|
1309
1394
|
}
|
|
1310
1395
|
else if (!result.subtitle) {
|
|
1311
|
-
result.subtitle =
|
|
1312
|
-
|
|
1396
|
+
result.subtitle = name;
|
|
1397
|
+
nameSet.delete(name);
|
|
1313
1398
|
}
|
|
1314
1399
|
}
|
|
1315
1400
|
if (!result.title && result.subtitle) {
|
|
1316
1401
|
result.title = result.subtitle;
|
|
1317
1402
|
delete result.subtitle;
|
|
1318
1403
|
}
|
|
1319
|
-
if (!result.title) {
|
|
1320
|
-
result.title = "result";
|
|
1321
|
-
}
|
|
1322
1404
|
return result;
|
|
1323
1405
|
}
|
|
1324
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
|
+
|
|
1325
1732
|
// Copyright (c) Microsoft Corporation.
|
|
1326
1733
|
/**
|
|
1327
1734
|
* A class that parses an OpenAPI specification file and provides methods to validate, list, and generate artifacts.
|
|
@@ -1337,10 +1744,15 @@ class SpecParser {
|
|
|
1337
1744
|
allowMissingId: true,
|
|
1338
1745
|
allowSwagger: true,
|
|
1339
1746
|
allowAPIKeyAuth: false,
|
|
1747
|
+
allowBearerTokenAuth: false,
|
|
1340
1748
|
allowMultipleParameters: false,
|
|
1341
1749
|
allowOauth2: false,
|
|
1342
1750
|
allowMethods: ["get", "post"],
|
|
1751
|
+
allowConversationStarters: false,
|
|
1752
|
+
allowResponseSemantics: false,
|
|
1753
|
+
allowConfirmation: false,
|
|
1343
1754
|
projectType: exports.ProjectType.SME,
|
|
1755
|
+
isGptPlugin: false,
|
|
1344
1756
|
};
|
|
1345
1757
|
this.pathOrSpec = pathOrDoc;
|
|
1346
1758
|
this.parser = new SwaggerParser__default['default']();
|
|
@@ -1365,6 +1777,8 @@ class SpecParser {
|
|
|
1365
1777
|
errors: [{ type: exports.ErrorType.SpecNotValid, content: e.toString() }],
|
|
1366
1778
|
};
|
|
1367
1779
|
}
|
|
1780
|
+
const errors = [];
|
|
1781
|
+
const warnings = [];
|
|
1368
1782
|
if (!this.options.allowSwagger && this.isSwaggerFile) {
|
|
1369
1783
|
return {
|
|
1370
1784
|
status: exports.ValidationStatus.Error,
|
|
@@ -1374,7 +1788,38 @@ class SpecParser {
|
|
|
1374
1788
|
],
|
|
1375
1789
|
};
|
|
1376
1790
|
}
|
|
1377
|
-
|
|
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
|
+
};
|
|
1378
1823
|
}
|
|
1379
1824
|
catch (err) {
|
|
1380
1825
|
throw new SpecParserError(err.toString(), exports.ErrorType.ValidateFailed);
|
|
@@ -1398,39 +1843,40 @@ class SpecParser {
|
|
|
1398
1843
|
try {
|
|
1399
1844
|
yield this.loadSpec();
|
|
1400
1845
|
const spec = this.spec;
|
|
1401
|
-
const apiMap = this.
|
|
1402
|
-
const result =
|
|
1846
|
+
const apiMap = this.getAPIs(spec);
|
|
1847
|
+
const result = {
|
|
1848
|
+
APIs: [],
|
|
1849
|
+
allAPICount: 0,
|
|
1850
|
+
validAPICount: 0,
|
|
1851
|
+
};
|
|
1403
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)}`;
|
|
1404
1856
|
const apiResult = {
|
|
1405
|
-
api:
|
|
1857
|
+
api: apiKey,
|
|
1406
1858
|
server: "",
|
|
1407
|
-
operationId:
|
|
1859
|
+
operationId: operationId,
|
|
1860
|
+
isValid: isValid,
|
|
1861
|
+
reason: reason,
|
|
1408
1862
|
};
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
if (!operationId) {
|
|
1421
|
-
operationId = `${method.toLowerCase()}${Utils.convertPathToCamelCase(path)}`;
|
|
1422
|
-
}
|
|
1423
|
-
apiResult.operationId = operationId;
|
|
1424
|
-
const authArray = Utils.getAuthArray(operation.security, spec);
|
|
1425
|
-
for (const auths of authArray) {
|
|
1426
|
-
if (auths.length === 1) {
|
|
1427
|
-
apiResult.auth = auths[0].authSchema;
|
|
1428
|
-
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
|
+
}
|
|
1429
1874
|
}
|
|
1430
1875
|
}
|
|
1431
|
-
apiResult
|
|
1432
|
-
result.push(apiResult);
|
|
1876
|
+
result.APIs.push(apiResult);
|
|
1433
1877
|
}
|
|
1878
|
+
result.allAPICount = result.APIs.length;
|
|
1879
|
+
result.validAPICount = result.APIs.filter((api) => api.isValid).length;
|
|
1434
1880
|
return result;
|
|
1435
1881
|
}
|
|
1436
1882
|
catch (err) {
|
|
@@ -1487,18 +1933,12 @@ class SpecParser {
|
|
|
1487
1933
|
const newSpecs = yield this.getFilteredSpecs(filter, signal);
|
|
1488
1934
|
const newUnResolvedSpec = newSpecs[0];
|
|
1489
1935
|
const newSpec = newSpecs[1];
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
resultStr = jsyaml__default['default'].dump(newUnResolvedSpec);
|
|
1493
|
-
}
|
|
1494
|
-
else {
|
|
1495
|
-
resultStr = JSON.stringify(newUnResolvedSpec, null, 2);
|
|
1496
|
-
}
|
|
1497
|
-
yield fs__default['default'].outputFile(outputSpecPath, resultStr);
|
|
1936
|
+
const authInfo = Utils.getAuthInfo(newSpec);
|
|
1937
|
+
yield this.saveFilterSpec(outputSpecPath, newUnResolvedSpec);
|
|
1498
1938
|
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
|
1499
1939
|
throw new SpecParserError(ConstantString.CancelledMessage, exports.ErrorType.Cancelled);
|
|
1500
1940
|
}
|
|
1501
|
-
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);
|
|
1502
1942
|
yield fs__default['default'].outputJSON(manifestPath, updatedManifest, { spaces: 2 });
|
|
1503
1943
|
yield fs__default['default'].outputJSON(pluginFilePath, apiPlugin, { spaces: 2 });
|
|
1504
1944
|
}
|
|
@@ -1528,32 +1968,11 @@ class SpecParser {
|
|
|
1528
1968
|
const newSpecs = yield this.getFilteredSpecs(filter, signal);
|
|
1529
1969
|
const newUnResolvedSpec = newSpecs[0];
|
|
1530
1970
|
const newSpec = newSpecs[1];
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
for (const method in newSpec.paths[url]) {
|
|
1535
|
-
const operation = newSpec.paths[url][method];
|
|
1536
|
-
const authArray = Utils.getAuthArray(operation.security, newSpec);
|
|
1537
|
-
if (authArray && authArray.length > 0) {
|
|
1538
|
-
authSet.add(authArray[0][0]);
|
|
1539
|
-
if (authSet.size > 1) {
|
|
1540
|
-
hasMultipleAuth = true;
|
|
1541
|
-
break;
|
|
1542
|
-
}
|
|
1543
|
-
}
|
|
1544
|
-
}
|
|
1545
|
-
}
|
|
1546
|
-
if (hasMultipleAuth && this.options.projectType !== exports.ProjectType.TeamsAi) {
|
|
1547
|
-
throw new SpecParserError(ConstantString.MultipleAuthNotSupported, exports.ErrorType.MultipleAuthNotSupported);
|
|
1548
|
-
}
|
|
1549
|
-
let resultStr;
|
|
1550
|
-
if (outputSpecPath.endsWith(".yaml") || outputSpecPath.endsWith(".yml")) {
|
|
1551
|
-
resultStr = jsyaml__default['default'].dump(newUnResolvedSpec);
|
|
1971
|
+
let authInfo = undefined;
|
|
1972
|
+
if (this.options.projectType === exports.ProjectType.SME) {
|
|
1973
|
+
authInfo = Utils.getAuthInfo(newSpec);
|
|
1552
1974
|
}
|
|
1553
|
-
|
|
1554
|
-
resultStr = JSON.stringify(newUnResolvedSpec, null, 2);
|
|
1555
|
-
}
|
|
1556
|
-
yield fs__default['default'].outputFile(outputSpecPath, resultStr);
|
|
1975
|
+
yield this.saveFilterSpec(outputSpecPath, newUnResolvedSpec);
|
|
1557
1976
|
if (adaptiveCardFolder) {
|
|
1558
1977
|
for (const url in newSpec.paths) {
|
|
1559
1978
|
for (const method in newSpec.paths[url]) {
|
|
@@ -1583,7 +2002,6 @@ class SpecParser {
|
|
|
1583
2002
|
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
|
1584
2003
|
throw new SpecParserError(ConstantString.CancelledMessage, exports.ErrorType.Cancelled);
|
|
1585
2004
|
}
|
|
1586
|
-
const authInfo = Array.from(authSet)[0];
|
|
1587
2005
|
const [updatedManifest, warnings] = yield ManifestUpdater.updateManifest(manifestPath, outputSpecPath, newSpec, this.options, adaptiveCardFolder, authInfo);
|
|
1588
2006
|
yield fs__default['default'].outputJSON(manifestPath, updatedManifest, { spaces: 2 });
|
|
1589
2007
|
result.warnings.push(...warnings);
|
|
@@ -1612,13 +2030,30 @@ class SpecParser {
|
|
|
1612
2030
|
}
|
|
1613
2031
|
});
|
|
1614
2032
|
}
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
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;
|
|
1618
2041
|
}
|
|
1619
|
-
const
|
|
1620
|
-
this.
|
|
1621
|
-
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
|
+
});
|
|
1622
2057
|
}
|
|
1623
2058
|
}
|
|
1624
2059
|
|