@microsoft/m365-spec-parser 0.1.1-alpha.a372ccf67.0 → 0.1.1-alpha.b015b287e.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 +204 -137
- package/dist/index.esm2017.js.map +1 -1
- package/dist/index.esm2017.mjs +271 -167
- package/dist/index.esm2017.mjs.map +1 -1
- package/dist/index.esm5.js +204 -137
- package/dist/index.esm5.js.map +1 -1
- package/dist/index.node.cjs.js +271 -167
- package/dist/index.node.cjs.js.map +1 -1
- package/dist/src/constants.d.ts +1 -0
- package/dist/src/interfaces.d.ts +44 -16
- package/dist/src/manifestUpdater.d.ts +2 -1
- package/dist/src/specParser.browser.d.ts +1 -0
- package/dist/src/specParser.d.ts +2 -2
- package/dist/src/utils.d.ts +17 -14
- package/package.json +3 -3
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 = {}));
|
|
@@ -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";
|
|
@@ -219,6 +236,7 @@ class Utils {
|
|
|
219
236
|
requiredNum: 0,
|
|
220
237
|
optionalNum: 0,
|
|
221
238
|
isValid: true,
|
|
239
|
+
reason: [],
|
|
222
240
|
};
|
|
223
241
|
if (!paramObject) {
|
|
224
242
|
return paramResult;
|
|
@@ -228,6 +246,7 @@ class Utils {
|
|
|
228
246
|
const schema = param.schema;
|
|
229
247
|
if (isCopilot && this.hasNestedObjectInSchema(schema)) {
|
|
230
248
|
paramResult.isValid = false;
|
|
249
|
+
paramResult.reason.push(exports.ErrorType.ParamsContainsNestedObject);
|
|
231
250
|
continue;
|
|
232
251
|
}
|
|
233
252
|
const isRequiredWithoutDefault = param.required && schema.default === undefined;
|
|
@@ -243,6 +262,7 @@ class Utils {
|
|
|
243
262
|
if (param.in === "header" || param.in === "cookie") {
|
|
244
263
|
if (isRequiredWithoutDefault) {
|
|
245
264
|
paramResult.isValid = false;
|
|
265
|
+
paramResult.reason.push(exports.ErrorType.ParamsContainRequiredUnsupportedSchema);
|
|
246
266
|
}
|
|
247
267
|
continue;
|
|
248
268
|
}
|
|
@@ -252,6 +272,7 @@ class Utils {
|
|
|
252
272
|
schema.type !== "integer") {
|
|
253
273
|
if (isRequiredWithoutDefault) {
|
|
254
274
|
paramResult.isValid = false;
|
|
275
|
+
paramResult.reason.push(exports.ErrorType.ParamsContainRequiredUnsupportedSchema);
|
|
255
276
|
}
|
|
256
277
|
continue;
|
|
257
278
|
}
|
|
@@ -272,6 +293,7 @@ class Utils {
|
|
|
272
293
|
requiredNum: 0,
|
|
273
294
|
optionalNum: 0,
|
|
274
295
|
isValid: true,
|
|
296
|
+
reason: [],
|
|
275
297
|
};
|
|
276
298
|
if (Object.keys(schema).length === 0) {
|
|
277
299
|
return paramResult;
|
|
@@ -279,6 +301,7 @@ class Utils {
|
|
|
279
301
|
const isRequiredWithoutDefault = isRequired && schema.default === undefined;
|
|
280
302
|
if (isCopilot && this.hasNestedObjectInSchema(schema)) {
|
|
281
303
|
paramResult.isValid = false;
|
|
304
|
+
paramResult.reason = [exports.ErrorType.RequestBodyContainsNestedObject];
|
|
282
305
|
return paramResult;
|
|
283
306
|
}
|
|
284
307
|
if (schema.type === "string" ||
|
|
@@ -303,11 +326,13 @@ class Utils {
|
|
|
303
326
|
paramResult.requiredNum += result.requiredNum;
|
|
304
327
|
paramResult.optionalNum += result.optionalNum;
|
|
305
328
|
paramResult.isValid = paramResult.isValid && result.isValid;
|
|
329
|
+
paramResult.reason.push(...result.reason);
|
|
306
330
|
}
|
|
307
331
|
}
|
|
308
332
|
else {
|
|
309
333
|
if (isRequiredWithoutDefault && !isCopilot) {
|
|
310
334
|
paramResult.isValid = false;
|
|
335
|
+
paramResult.reason.push(exports.ErrorType.PostBodyContainsRequiredUnsupportedSchema);
|
|
311
336
|
}
|
|
312
337
|
}
|
|
313
338
|
return paramResult;
|
|
@@ -331,120 +356,132 @@ class Utils {
|
|
|
331
356
|
*/
|
|
332
357
|
static isSupportedApi(method, path, spec, options) {
|
|
333
358
|
var _a;
|
|
334
|
-
const
|
|
359
|
+
const result = { isValid: true, reason: [] };
|
|
335
360
|
method = method.toLocaleLowerCase();
|
|
336
|
-
if (
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
361
|
+
if (options.allowMethods && !options.allowMethods.includes(method)) {
|
|
362
|
+
result.isValid = false;
|
|
363
|
+
result.reason.push(exports.ErrorType.MethodNotAllowed);
|
|
364
|
+
return result;
|
|
365
|
+
}
|
|
366
|
+
const pathObj = spec.paths[path];
|
|
367
|
+
if (!pathObj || !pathObj[method]) {
|
|
368
|
+
result.isValid = false;
|
|
369
|
+
result.reason.push(exports.ErrorType.UrlPathNotExist);
|
|
370
|
+
return result;
|
|
371
|
+
}
|
|
372
|
+
const securities = pathObj[method].security;
|
|
373
|
+
const isTeamsAi = options.projectType === exports.ProjectType.TeamsAi;
|
|
374
|
+
const isCopilot = options.projectType === exports.ProjectType.Copilot;
|
|
375
|
+
// Teams AI project doesn't care about auth, it will use authProvider for user to implement
|
|
376
|
+
if (!isTeamsAi) {
|
|
377
|
+
const authArray = Utils.getAuthArray(securities, spec);
|
|
378
|
+
const authCheckResult = Utils.isSupportedAuth(authArray, options);
|
|
379
|
+
if (!authCheckResult.isValid) {
|
|
380
|
+
result.reason.push(...authCheckResult.reason);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
const operationObject = pathObj[method];
|
|
384
|
+
if (!options.allowMissingId && !operationObject.operationId) {
|
|
385
|
+
result.reason.push(exports.ErrorType.MissingOperationId);
|
|
386
|
+
}
|
|
387
|
+
const rootServer = spec.servers && spec.servers[0];
|
|
388
|
+
const methodServer = spec.paths[path].servers && ((_a = spec.paths[path]) === null || _a === void 0 ? void 0 : _a.servers[0]);
|
|
389
|
+
const operationServer = operationObject.servers && operationObject.servers[0];
|
|
390
|
+
const serverUrl = operationServer || methodServer || rootServer;
|
|
391
|
+
if (!serverUrl) {
|
|
392
|
+
result.reason.push(exports.ErrorType.NoServerInformation);
|
|
393
|
+
}
|
|
394
|
+
else {
|
|
395
|
+
const serverValidateResult = Utils.checkServerUrl([serverUrl]);
|
|
396
|
+
result.reason.push(...serverValidateResult.map((item) => item.type));
|
|
397
|
+
}
|
|
398
|
+
const paramObject = operationObject.parameters;
|
|
399
|
+
const requestBody = operationObject.requestBody;
|
|
400
|
+
const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
|
|
401
|
+
if (!isTeamsAi && Utils.containMultipleMediaTypes(requestBody)) {
|
|
402
|
+
result.reason.push(exports.ErrorType.PostBodyContainMultipleMediaTypes);
|
|
403
|
+
}
|
|
404
|
+
const { json, multipleMediaType } = Utils.getResponseJson(operationObject, isTeamsAi);
|
|
405
|
+
if (multipleMediaType && !isTeamsAi) {
|
|
406
|
+
result.reason.push(exports.ErrorType.ResponseContainMultipleMediaTypes);
|
|
407
|
+
}
|
|
408
|
+
else if (Object.keys(json).length === 0) {
|
|
409
|
+
result.reason.push(exports.ErrorType.ResponseJsonIsEmpty);
|
|
410
|
+
}
|
|
411
|
+
// Teams AI project doesn't care about request parameters/body
|
|
412
|
+
if (!isTeamsAi) {
|
|
413
|
+
let requestBodyParamResult = {
|
|
414
|
+
requiredNum: 0,
|
|
415
|
+
optionalNum: 0,
|
|
416
|
+
isValid: true,
|
|
417
|
+
reason: [],
|
|
418
|
+
};
|
|
419
|
+
if (requestJsonBody) {
|
|
420
|
+
const requestBodySchema = requestJsonBody.schema;
|
|
421
|
+
if (isCopilot && requestBodySchema.type !== "object") {
|
|
422
|
+
result.reason.push(exports.ErrorType.PostBodySchemaIsNotJson);
|
|
384
423
|
}
|
|
385
|
-
|
|
386
|
-
if (
|
|
387
|
-
|
|
424
|
+
requestBodyParamResult = Utils.checkPostBody(requestBodySchema, requestBody.required, isCopilot);
|
|
425
|
+
if (!requestBodyParamResult.isValid && requestBodyParamResult.reason) {
|
|
426
|
+
result.reason.push(...requestBodyParamResult.reason);
|
|
388
427
|
}
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
428
|
+
}
|
|
429
|
+
const paramResult = Utils.checkParameters(paramObject, isCopilot);
|
|
430
|
+
if (!paramResult.isValid && paramResult.reason) {
|
|
431
|
+
result.reason.push(...paramResult.reason);
|
|
432
|
+
}
|
|
433
|
+
// Copilot support arbitrary parameters
|
|
434
|
+
if (!isCopilot && paramResult.isValid && requestBodyParamResult.isValid) {
|
|
435
|
+
const totalRequiredParams = requestBodyParamResult.requiredNum + paramResult.requiredNum;
|
|
436
|
+
const totalParams = totalRequiredParams + requestBodyParamResult.optionalNum + paramResult.optionalNum;
|
|
437
|
+
if (totalRequiredParams > 1) {
|
|
438
|
+
if (!options.allowMultipleParameters ||
|
|
439
|
+
totalRequiredParams > ConstantString.SMERequiredParamsMaxNum) {
|
|
440
|
+
result.reason.push(exports.ErrorType.ExceededRequiredParamsLimit);
|
|
394
441
|
}
|
|
395
|
-
return false;
|
|
396
|
-
}
|
|
397
|
-
else if (requestBodyParamResult.requiredNum +
|
|
398
|
-
requestBodyParamResult.optionalNum +
|
|
399
|
-
paramResult.requiredNum +
|
|
400
|
-
paramResult.optionalNum ===
|
|
401
|
-
0) {
|
|
402
|
-
return false;
|
|
403
442
|
}
|
|
404
|
-
else {
|
|
405
|
-
|
|
443
|
+
else if (totalParams === 0) {
|
|
444
|
+
result.reason.push(exports.ErrorType.NoParameter);
|
|
406
445
|
}
|
|
407
446
|
}
|
|
408
447
|
}
|
|
409
|
-
|
|
448
|
+
if (result.reason.length > 0) {
|
|
449
|
+
result.isValid = false;
|
|
450
|
+
}
|
|
451
|
+
return result;
|
|
410
452
|
}
|
|
411
|
-
static isSupportedAuth(
|
|
412
|
-
if (
|
|
413
|
-
return true;
|
|
453
|
+
static isSupportedAuth(authSchemeArray, options) {
|
|
454
|
+
if (authSchemeArray.length === 0) {
|
|
455
|
+
return { isValid: true, reason: [] };
|
|
414
456
|
}
|
|
415
|
-
if (options.allowAPIKeyAuth || options.allowOauth2) {
|
|
457
|
+
if (options.allowAPIKeyAuth || options.allowOauth2 || options.allowBearerTokenAuth) {
|
|
416
458
|
// Currently we don't support multiple auth in one operation
|
|
417
|
-
if (
|
|
418
|
-
return
|
|
459
|
+
if (authSchemeArray.length > 0 && authSchemeArray.every((auths) => auths.length > 1)) {
|
|
460
|
+
return {
|
|
461
|
+
isValid: false,
|
|
462
|
+
reason: [exports.ErrorType.MultipleAuthNotSupported],
|
|
463
|
+
};
|
|
419
464
|
}
|
|
420
|
-
for (const auths of
|
|
465
|
+
for (const auths of authSchemeArray) {
|
|
421
466
|
if (auths.length === 1) {
|
|
422
|
-
if (
|
|
423
|
-
options.
|
|
424
|
-
Utils.
|
|
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;
|
|
467
|
+
if ((options.allowAPIKeyAuth && Utils.isAPIKeyAuth(auths[0].authScheme)) ||
|
|
468
|
+
(options.allowOauth2 && Utils.isOAuthWithAuthCodeFlow(auths[0].authScheme)) ||
|
|
469
|
+
(options.allowBearerTokenAuth && Utils.isBearerTokenAuth(auths[0].authScheme))) {
|
|
470
|
+
return { isValid: true, reason: [] };
|
|
437
471
|
}
|
|
438
472
|
}
|
|
439
473
|
}
|
|
440
474
|
}
|
|
441
|
-
return false;
|
|
475
|
+
return { isValid: false, reason: [exports.ErrorType.AuthTypeIsNotSupported] };
|
|
476
|
+
}
|
|
477
|
+
static isBearerTokenAuth(authScheme) {
|
|
478
|
+
return authScheme.type === "http" && authScheme.scheme === "bearer";
|
|
442
479
|
}
|
|
443
|
-
static isAPIKeyAuth(
|
|
444
|
-
return
|
|
480
|
+
static isAPIKeyAuth(authScheme) {
|
|
481
|
+
return authScheme.type === "apiKey";
|
|
445
482
|
}
|
|
446
|
-
static isOAuthWithAuthCodeFlow(
|
|
447
|
-
if (
|
|
483
|
+
static isOAuthWithAuthCodeFlow(authScheme) {
|
|
484
|
+
if (authScheme.type === "oauth2" && authScheme.flows && authScheme.flows.authorizationCode) {
|
|
448
485
|
return true;
|
|
449
486
|
}
|
|
450
487
|
return false;
|
|
@@ -460,7 +497,7 @@ class Utils {
|
|
|
460
497
|
for (const name in security) {
|
|
461
498
|
const auth = securitySchemas[name];
|
|
462
499
|
authArray.push({
|
|
463
|
-
|
|
500
|
+
authScheme: auth,
|
|
464
501
|
name: name,
|
|
465
502
|
});
|
|
466
503
|
}
|
|
@@ -478,11 +515,17 @@ class Utils {
|
|
|
478
515
|
static getResponseJson(operationObject, isTeamsAiProject = false) {
|
|
479
516
|
var _a, _b;
|
|
480
517
|
let json = {};
|
|
518
|
+
let multipleMediaType = false;
|
|
481
519
|
for (const code of ConstantString.ResponseCodeFor20X) {
|
|
482
520
|
const responseObject = (_a = operationObject === null || operationObject === void 0 ? void 0 : operationObject.responses) === null || _a === void 0 ? void 0 : _a[code];
|
|
483
521
|
if ((_b = responseObject === null || responseObject === void 0 ? void 0 : responseObject.content) === null || _b === void 0 ? void 0 : _b["application/json"]) {
|
|
522
|
+
multipleMediaType = false;
|
|
484
523
|
json = responseObject.content["application/json"];
|
|
485
|
-
if (
|
|
524
|
+
if (Utils.containMultipleMediaTypes(responseObject)) {
|
|
525
|
+
multipleMediaType = true;
|
|
526
|
+
if (isTeamsAiProject) {
|
|
527
|
+
break;
|
|
528
|
+
}
|
|
486
529
|
json = {};
|
|
487
530
|
}
|
|
488
531
|
else {
|
|
@@ -490,7 +533,7 @@ class Utils {
|
|
|
490
533
|
}
|
|
491
534
|
}
|
|
492
535
|
}
|
|
493
|
-
return json;
|
|
536
|
+
return { json, multipleMediaType };
|
|
494
537
|
}
|
|
495
538
|
static convertPathToCamelCase(path) {
|
|
496
539
|
const pathSegments = path.split(/[./{]/);
|
|
@@ -510,10 +553,10 @@ class Utils {
|
|
|
510
553
|
return undefined;
|
|
511
554
|
}
|
|
512
555
|
}
|
|
513
|
-
static
|
|
556
|
+
static resolveEnv(str) {
|
|
514
557
|
const placeHolderReg = /\${{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*}}/g;
|
|
515
|
-
let matches = placeHolderReg.exec(
|
|
516
|
-
let
|
|
558
|
+
let matches = placeHolderReg.exec(str);
|
|
559
|
+
let newStr = str;
|
|
517
560
|
while (matches != null) {
|
|
518
561
|
const envVar = matches[1];
|
|
519
562
|
const envVal = process.env[envVar];
|
|
@@ -521,17 +564,17 @@ class Utils {
|
|
|
521
564
|
throw new Error(Utils.format(ConstantString.ResolveServerUrlFailed, envVar));
|
|
522
565
|
}
|
|
523
566
|
else {
|
|
524
|
-
|
|
567
|
+
newStr = newStr.replace(matches[0], envVal);
|
|
525
568
|
}
|
|
526
|
-
matches = placeHolderReg.exec(
|
|
569
|
+
matches = placeHolderReg.exec(str);
|
|
527
570
|
}
|
|
528
|
-
return
|
|
571
|
+
return newStr;
|
|
529
572
|
}
|
|
530
573
|
static checkServerUrl(servers) {
|
|
531
574
|
const errors = [];
|
|
532
575
|
let serverUrl;
|
|
533
576
|
try {
|
|
534
|
-
serverUrl = Utils.
|
|
577
|
+
serverUrl = Utils.resolveEnv(servers[0].url);
|
|
535
578
|
}
|
|
536
579
|
catch (err) {
|
|
537
580
|
errors.push({
|
|
@@ -562,6 +605,7 @@ class Utils {
|
|
|
562
605
|
return errors;
|
|
563
606
|
}
|
|
564
607
|
static validateServer(spec, options) {
|
|
608
|
+
var _a;
|
|
565
609
|
const errors = [];
|
|
566
610
|
let hasTopLevelServers = false;
|
|
567
611
|
let hasPathLevelServers = false;
|
|
@@ -582,7 +626,7 @@ class Utils {
|
|
|
582
626
|
}
|
|
583
627
|
for (const method in methods) {
|
|
584
628
|
const operationObject = methods[method];
|
|
585
|
-
if (
|
|
629
|
+
if (((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
|
|
586
630
|
if ((operationObject === null || operationObject === void 0 ? void 0 : operationObject.servers) && operationObject.servers.length >= 1) {
|
|
587
631
|
hasOperationLevelServers = true;
|
|
588
632
|
const serverErrors = Utils.checkServerUrl(operationObject.servers);
|
|
@@ -625,6 +669,7 @@ class Utils {
|
|
|
625
669
|
Utils.updateParameterWithInputType(schema, parameter);
|
|
626
670
|
}
|
|
627
671
|
if (isRequired && schema.default === undefined) {
|
|
672
|
+
parameter.isRequired = true;
|
|
628
673
|
requiredParams.push(parameter);
|
|
629
674
|
}
|
|
630
675
|
else {
|
|
@@ -688,6 +733,7 @@ class Utils {
|
|
|
688
733
|
}
|
|
689
734
|
if (param.in !== "header" && param.in !== "cookie") {
|
|
690
735
|
if (param.required && (schema === null || schema === void 0 ? void 0 : schema.default) === undefined) {
|
|
736
|
+
parameter.isRequired = true;
|
|
691
737
|
requiredParams.push(parameter);
|
|
692
738
|
}
|
|
693
739
|
else {
|
|
@@ -707,13 +753,7 @@ class Utils {
|
|
|
707
753
|
}
|
|
708
754
|
}
|
|
709
755
|
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
|
-
}
|
|
756
|
+
const parameters = [...requiredParams, ...optionalParams];
|
|
717
757
|
const command = {
|
|
718
758
|
context: ["compose"],
|
|
719
759
|
type: "query",
|
|
@@ -722,25 +762,23 @@ class Utils {
|
|
|
722
762
|
parameters: parameters,
|
|
723
763
|
description: ((_b = operationItem.description) !== null && _b !== void 0 ? _b : "").slice(0, ConstantString.CommandDescriptionMaxLens),
|
|
724
764
|
};
|
|
725
|
-
|
|
726
|
-
if (requiredParams.length === 0 && optionalParams.length > 1) {
|
|
727
|
-
warning = {
|
|
728
|
-
type: exports.WarningType.OperationOnlyContainsOptionalParam,
|
|
729
|
-
content: Utils.format(ConstantString.OperationOnlyContainsOptionalParam, operationId),
|
|
730
|
-
data: operationId,
|
|
731
|
-
};
|
|
732
|
-
}
|
|
733
|
-
return [command, warning];
|
|
765
|
+
return command;
|
|
734
766
|
}
|
|
735
|
-
static
|
|
767
|
+
static listAPIs(spec, options) {
|
|
768
|
+
var _a;
|
|
736
769
|
const paths = spec.paths;
|
|
737
770
|
const result = {};
|
|
738
771
|
for (const path in paths) {
|
|
739
772
|
const methods = paths[path];
|
|
740
773
|
for (const method in methods) {
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
774
|
+
const operationObject = methods[method];
|
|
775
|
+
if (((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
|
|
776
|
+
const validateResult = Utils.isSupportedApi(method, path, spec, options);
|
|
777
|
+
result[`${method.toUpperCase()} ${path}`] = {
|
|
778
|
+
operation: operationObject,
|
|
779
|
+
isValid: validateResult.isValid,
|
|
780
|
+
reason: validateResult.reason,
|
|
781
|
+
};
|
|
744
782
|
}
|
|
745
783
|
}
|
|
746
784
|
}
|
|
@@ -749,13 +787,13 @@ class Utils {
|
|
|
749
787
|
static validateSpec(spec, parser, isSwaggerFile, options) {
|
|
750
788
|
const errors = [];
|
|
751
789
|
const warnings = [];
|
|
790
|
+
const apiMap = Utils.listAPIs(spec, options);
|
|
752
791
|
if (isSwaggerFile) {
|
|
753
792
|
warnings.push({
|
|
754
793
|
type: exports.WarningType.ConvertSwaggerToOpenAPI,
|
|
755
794
|
content: ConstantString.ConvertSwaggerToOpenAPI,
|
|
756
795
|
});
|
|
757
796
|
}
|
|
758
|
-
// Server validation
|
|
759
797
|
const serverErrors = Utils.validateServer(spec, options);
|
|
760
798
|
errors.push(...serverErrors);
|
|
761
799
|
// Remote reference not supported
|
|
@@ -769,8 +807,8 @@ class Utils {
|
|
|
769
807
|
});
|
|
770
808
|
}
|
|
771
809
|
// No supported API
|
|
772
|
-
const
|
|
773
|
-
if (
|
|
810
|
+
const validAPIs = Object.entries(apiMap).filter(([, value]) => value.isValid);
|
|
811
|
+
if (validAPIs.length === 0) {
|
|
774
812
|
errors.push({
|
|
775
813
|
type: exports.ErrorType.NoSupportedApi,
|
|
776
814
|
content: ConstantString.NoSupportedApi,
|
|
@@ -779,8 +817,8 @@ class Utils {
|
|
|
779
817
|
// OperationId missing
|
|
780
818
|
const apisMissingOperationId = [];
|
|
781
819
|
for (const key in apiMap) {
|
|
782
|
-
const
|
|
783
|
-
if (!
|
|
820
|
+
const { operation } = apiMap[key];
|
|
821
|
+
if (!operation.operationId) {
|
|
784
822
|
apisMissingOperationId.push(key);
|
|
785
823
|
}
|
|
786
824
|
}
|
|
@@ -821,30 +859,50 @@ class Utils {
|
|
|
821
859
|
}
|
|
822
860
|
return safeRegistrationIdEnvName;
|
|
823
861
|
}
|
|
862
|
+
static getAllAPICount(spec) {
|
|
863
|
+
let count = 0;
|
|
864
|
+
const paths = spec.paths;
|
|
865
|
+
for (const path in paths) {
|
|
866
|
+
const methods = paths[path];
|
|
867
|
+
for (const method in methods) {
|
|
868
|
+
if (ConstantString.AllOperationMethods.includes(method)) {
|
|
869
|
+
count++;
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
return count;
|
|
874
|
+
}
|
|
824
875
|
}
|
|
825
876
|
|
|
826
877
|
// Copyright (c) Microsoft Corporation.
|
|
827
878
|
class SpecFilter {
|
|
828
879
|
static specFilter(filter, unResolveSpec, resolvedSpec, options) {
|
|
880
|
+
var _a;
|
|
829
881
|
try {
|
|
830
882
|
const newSpec = Object.assign({}, unResolveSpec);
|
|
831
883
|
const newPaths = {};
|
|
832
884
|
for (const filterItem of filter) {
|
|
833
885
|
const [method, path] = filterItem.split(" ");
|
|
834
886
|
const methodName = method.toLowerCase();
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
887
|
+
const pathObj = (_a = resolvedSpec.paths) === null || _a === void 0 ? void 0 : _a[path];
|
|
888
|
+
if (ConstantString.AllOperationMethods.includes(methodName) &&
|
|
889
|
+
pathObj &&
|
|
890
|
+
pathObj[methodName]) {
|
|
891
|
+
const validateResult = Utils.isSupportedApi(methodName, path, resolvedSpec, options);
|
|
892
|
+
if (!validateResult.isValid) {
|
|
893
|
+
continue;
|
|
894
|
+
}
|
|
895
|
+
if (!newPaths[path]) {
|
|
896
|
+
newPaths[path] = Object.assign({}, unResolveSpec.paths[path]);
|
|
897
|
+
for (const m of ConstantString.AllOperationMethods) {
|
|
898
|
+
delete newPaths[path][m];
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
newPaths[path][methodName] = unResolveSpec.paths[path][methodName];
|
|
902
|
+
// Add the operationId if missing
|
|
903
|
+
if (!newPaths[path][methodName].operationId) {
|
|
904
|
+
newPaths[path][methodName].operationId = `${methodName}${Utils.convertPathToCamelCase(path)}`;
|
|
842
905
|
}
|
|
843
|
-
}
|
|
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
906
|
}
|
|
849
907
|
}
|
|
850
908
|
newSpec.paths = newPaths;
|
|
@@ -867,9 +925,10 @@ class ManifestUpdater {
|
|
|
867
925
|
pluginFile: apiPluginRelativePath,
|
|
868
926
|
},
|
|
869
927
|
];
|
|
928
|
+
const appName = this.removeEnvs(manifest.name.short);
|
|
870
929
|
ManifestUpdater.updateManifestDescription(manifest, spec);
|
|
871
930
|
const specRelativePath = ManifestUpdater.getRelativePath(manifestPath, outputSpecPath);
|
|
872
|
-
const apiPlugin = ManifestUpdater.generatePluginManifestSchema(spec, specRelativePath, options);
|
|
931
|
+
const apiPlugin = ManifestUpdater.generatePluginManifestSchema(spec, specRelativePath, appName, options);
|
|
873
932
|
return [manifest, apiPlugin];
|
|
874
933
|
});
|
|
875
934
|
}
|
|
@@ -894,7 +953,7 @@ class ManifestUpdater {
|
|
|
894
953
|
}
|
|
895
954
|
return parameter;
|
|
896
955
|
}
|
|
897
|
-
static generatePluginManifestSchema(spec, specRelativePath, options) {
|
|
956
|
+
static generatePluginManifestSchema(spec, specRelativePath, appName, options) {
|
|
898
957
|
var _a, _b, _c;
|
|
899
958
|
const functions = [];
|
|
900
959
|
const functionNames = [];
|
|
@@ -959,7 +1018,7 @@ class ManifestUpdater {
|
|
|
959
1018
|
}
|
|
960
1019
|
const apiPlugin = {
|
|
961
1020
|
schema_version: "v2",
|
|
962
|
-
name_for_human:
|
|
1021
|
+
name_for_human: appName,
|
|
963
1022
|
description_for_human: (_c = spec.info.description) !== null && _c !== void 0 ? _c : "<Please add description of the plugin>",
|
|
964
1023
|
functions: functions,
|
|
965
1024
|
runtimes: [
|
|
@@ -994,9 +1053,8 @@ class ManifestUpdater {
|
|
|
994
1053
|
commands: commands,
|
|
995
1054
|
};
|
|
996
1055
|
if (authInfo) {
|
|
997
|
-
|
|
998
|
-
if (Utils.isAPIKeyAuth(auth)) {
|
|
999
|
-
auth = auth;
|
|
1056
|
+
const auth = authInfo.authScheme;
|
|
1057
|
+
if (Utils.isAPIKeyAuth(auth) || Utils.isBearerTokenAuth(auth)) {
|
|
1000
1058
|
const safeApiSecretRegistrationId = Utils.getSafeRegistrationIdEnvName(`${authInfo.name}_${ConstantString.RegistrationIdPostfix}`);
|
|
1001
1059
|
composeExtension.authorization = {
|
|
1002
1060
|
authType: "apiSecretServiceAuth",
|
|
@@ -1047,16 +1105,26 @@ class ManifestUpdater {
|
|
|
1047
1105
|
if ((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) {
|
|
1048
1106
|
const operationItem = operations[method];
|
|
1049
1107
|
if (operationItem) {
|
|
1050
|
-
const
|
|
1108
|
+
const command = Utils.parseApiInfo(operationItem, options);
|
|
1109
|
+
if (command.parameters &&
|
|
1110
|
+
command.parameters.length >= 1 &&
|
|
1111
|
+
command.parameters.some((param) => param.isRequired)) {
|
|
1112
|
+
command.parameters = command.parameters.filter((param) => param.isRequired);
|
|
1113
|
+
}
|
|
1114
|
+
else if (command.parameters && command.parameters.length > 0) {
|
|
1115
|
+
command.parameters = [command.parameters[0]];
|
|
1116
|
+
warnings.push({
|
|
1117
|
+
type: exports.WarningType.OperationOnlyContainsOptionalParam,
|
|
1118
|
+
content: Utils.format(ConstantString.OperationOnlyContainsOptionalParam, command.id),
|
|
1119
|
+
data: command.id,
|
|
1120
|
+
});
|
|
1121
|
+
}
|
|
1051
1122
|
if (adaptiveCardFolder) {
|
|
1052
1123
|
const adaptiveCardPath = path__default['default'].join(adaptiveCardFolder, command.id + ".json");
|
|
1053
1124
|
command.apiResponseRenderingTemplateFile = (yield fs__default['default'].pathExists(adaptiveCardPath))
|
|
1054
1125
|
? ManifestUpdater.getRelativePath(manifestPath, adaptiveCardPath)
|
|
1055
1126
|
: "";
|
|
1056
1127
|
}
|
|
1057
|
-
if (warning) {
|
|
1058
|
-
warnings.push(warning);
|
|
1059
|
-
}
|
|
1060
1128
|
commands.push(command);
|
|
1061
1129
|
}
|
|
1062
1130
|
}
|
|
@@ -1071,13 +1139,22 @@ class ManifestUpdater {
|
|
|
1071
1139
|
const relativePath = path__default['default'].relative(path__default['default'].dirname(from), to);
|
|
1072
1140
|
return path__default['default'].normalize(relativePath).replace(/\\/g, "/");
|
|
1073
1141
|
}
|
|
1142
|
+
static removeEnvs(str) {
|
|
1143
|
+
const placeHolderReg = /\${{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*}}/g;
|
|
1144
|
+
const matches = placeHolderReg.exec(str);
|
|
1145
|
+
let newStr = str;
|
|
1146
|
+
if (matches != null) {
|
|
1147
|
+
newStr = newStr.replace(matches[0], "");
|
|
1148
|
+
}
|
|
1149
|
+
return newStr;
|
|
1150
|
+
}
|
|
1074
1151
|
}
|
|
1075
1152
|
|
|
1076
1153
|
// Copyright (c) Microsoft Corporation.
|
|
1077
1154
|
class AdaptiveCardGenerator {
|
|
1078
1155
|
static generateAdaptiveCard(operationItem) {
|
|
1079
1156
|
try {
|
|
1080
|
-
const json = Utils.getResponseJson(operationItem);
|
|
1157
|
+
const { json } = Utils.getResponseJson(operationItem);
|
|
1081
1158
|
let cardBody = [];
|
|
1082
1159
|
let schema = json.schema;
|
|
1083
1160
|
let jsonPath = "$";
|
|
@@ -1337,6 +1414,7 @@ class SpecParser {
|
|
|
1337
1414
|
allowMissingId: true,
|
|
1338
1415
|
allowSwagger: true,
|
|
1339
1416
|
allowAPIKeyAuth: false,
|
|
1417
|
+
allowBearerTokenAuth: false,
|
|
1340
1418
|
allowMultipleParameters: false,
|
|
1341
1419
|
allowOauth2: false,
|
|
1342
1420
|
allowMethods: ["get", "post"],
|
|
@@ -1374,6 +1452,22 @@ class SpecParser {
|
|
|
1374
1452
|
],
|
|
1375
1453
|
};
|
|
1376
1454
|
}
|
|
1455
|
+
if (this.options.projectType === exports.ProjectType.SME ||
|
|
1456
|
+
this.options.projectType === exports.ProjectType.Copilot) {
|
|
1457
|
+
if (this.spec.openapi >= "3.1.0") {
|
|
1458
|
+
return {
|
|
1459
|
+
status: exports.ValidationStatus.Error,
|
|
1460
|
+
warnings: [],
|
|
1461
|
+
errors: [
|
|
1462
|
+
{
|
|
1463
|
+
type: exports.ErrorType.SpecVersionNotSupported,
|
|
1464
|
+
content: Utils.format(ConstantString.SpecVersionNotSupported, this.spec.openapi),
|
|
1465
|
+
data: this.spec.openapi,
|
|
1466
|
+
},
|
|
1467
|
+
],
|
|
1468
|
+
};
|
|
1469
|
+
}
|
|
1470
|
+
}
|
|
1377
1471
|
return Utils.validateSpec(this.spec, this.parser, !!this.isSwaggerFile, this.options);
|
|
1378
1472
|
}
|
|
1379
1473
|
catch (err) {
|
|
@@ -1398,16 +1492,22 @@ class SpecParser {
|
|
|
1398
1492
|
try {
|
|
1399
1493
|
yield this.loadSpec();
|
|
1400
1494
|
const spec = this.spec;
|
|
1401
|
-
const apiMap = this.
|
|
1402
|
-
const result =
|
|
1495
|
+
const apiMap = this.getAPIs(spec);
|
|
1496
|
+
const result = {
|
|
1497
|
+
APIs: [],
|
|
1498
|
+
allAPICount: 0,
|
|
1499
|
+
validAPICount: 0,
|
|
1500
|
+
};
|
|
1403
1501
|
for (const apiKey in apiMap) {
|
|
1502
|
+
const { operation, isValid, reason } = apiMap[apiKey];
|
|
1503
|
+
const [method, path] = apiKey.split(" ");
|
|
1404
1504
|
const apiResult = {
|
|
1405
1505
|
api: "",
|
|
1406
1506
|
server: "",
|
|
1407
1507
|
operationId: "",
|
|
1508
|
+
isValid: isValid,
|
|
1509
|
+
reason: reason,
|
|
1408
1510
|
};
|
|
1409
|
-
const [method, path] = apiKey.split(" ");
|
|
1410
|
-
const operation = apiMap[apiKey];
|
|
1411
1511
|
const rootServer = spec.servers && spec.servers[0];
|
|
1412
1512
|
const methodServer = spec.paths[path].servers && ((_a = spec.paths[path]) === null || _a === void 0 ? void 0 : _a.servers[0]);
|
|
1413
1513
|
const operationServer = operation.servers && operation.servers[0];
|
|
@@ -1415,7 +1515,7 @@ class SpecParser {
|
|
|
1415
1515
|
if (!serverUrl) {
|
|
1416
1516
|
throw new SpecParserError(ConstantString.NoServerInformation, exports.ErrorType.NoServerInformation);
|
|
1417
1517
|
}
|
|
1418
|
-
apiResult.server = Utils.
|
|
1518
|
+
apiResult.server = Utils.resolveEnv(serverUrl.url);
|
|
1419
1519
|
let operationId = operation.operationId;
|
|
1420
1520
|
if (!operationId) {
|
|
1421
1521
|
operationId = `${method.toLowerCase()}${Utils.convertPathToCamelCase(path)}`;
|
|
@@ -1424,13 +1524,15 @@ class SpecParser {
|
|
|
1424
1524
|
const authArray = Utils.getAuthArray(operation.security, spec);
|
|
1425
1525
|
for (const auths of authArray) {
|
|
1426
1526
|
if (auths.length === 1) {
|
|
1427
|
-
apiResult.auth = auths[0]
|
|
1527
|
+
apiResult.auth = auths[0];
|
|
1428
1528
|
break;
|
|
1429
1529
|
}
|
|
1430
1530
|
}
|
|
1431
1531
|
apiResult.api = apiKey;
|
|
1432
|
-
result.push(apiResult);
|
|
1532
|
+
result.APIs.push(apiResult);
|
|
1433
1533
|
}
|
|
1534
|
+
result.allAPICount = result.APIs.length;
|
|
1535
|
+
result.validAPICount = result.APIs.filter((api) => api.isValid).length;
|
|
1434
1536
|
return result;
|
|
1435
1537
|
}
|
|
1436
1538
|
catch (err) {
|
|
@@ -1528,15 +1630,18 @@ class SpecParser {
|
|
|
1528
1630
|
const newSpecs = yield this.getFilteredSpecs(filter, signal);
|
|
1529
1631
|
const newUnResolvedSpec = newSpecs[0];
|
|
1530
1632
|
const newSpec = newSpecs[1];
|
|
1531
|
-
const authSet = new Set();
|
|
1532
1633
|
let hasMultipleAuth = false;
|
|
1634
|
+
let authInfo = undefined;
|
|
1533
1635
|
for (const url in newSpec.paths) {
|
|
1534
1636
|
for (const method in newSpec.paths[url]) {
|
|
1535
1637
|
const operation = newSpec.paths[url][method];
|
|
1536
1638
|
const authArray = Utils.getAuthArray(operation.security, newSpec);
|
|
1537
1639
|
if (authArray && authArray.length > 0) {
|
|
1538
|
-
|
|
1539
|
-
if (
|
|
1640
|
+
const currentAuth = authArray[0][0];
|
|
1641
|
+
if (!authInfo) {
|
|
1642
|
+
authInfo = authArray[0][0];
|
|
1643
|
+
}
|
|
1644
|
+
else if (authInfo.name !== currentAuth.name) {
|
|
1540
1645
|
hasMultipleAuth = true;
|
|
1541
1646
|
break;
|
|
1542
1647
|
}
|
|
@@ -1583,7 +1688,6 @@ class SpecParser {
|
|
|
1583
1688
|
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
|
1584
1689
|
throw new SpecParserError(ConstantString.CancelledMessage, exports.ErrorType.Cancelled);
|
|
1585
1690
|
}
|
|
1586
|
-
const authInfo = Array.from(authSet)[0];
|
|
1587
1691
|
const [updatedManifest, warnings] = yield ManifestUpdater.updateManifest(manifestPath, outputSpecPath, newSpec, this.options, adaptiveCardFolder, authInfo);
|
|
1588
1692
|
yield fs__default['default'].outputJSON(manifestPath, updatedManifest, { spaces: 2 });
|
|
1589
1693
|
result.warnings.push(...warnings);
|
|
@@ -1612,11 +1716,11 @@ class SpecParser {
|
|
|
1612
1716
|
}
|
|
1613
1717
|
});
|
|
1614
1718
|
}
|
|
1615
|
-
|
|
1719
|
+
getAPIs(spec) {
|
|
1616
1720
|
if (this.apiMap !== undefined) {
|
|
1617
1721
|
return this.apiMap;
|
|
1618
1722
|
}
|
|
1619
|
-
const result = Utils.
|
|
1723
|
+
const result = Utils.listAPIs(spec, this.options);
|
|
1620
1724
|
this.apiMap = result;
|
|
1621
1725
|
return result;
|
|
1622
1726
|
}
|