@microsoft/m365-spec-parser 0.1.1-alpha.4f2290daa.0 → 0.1.1-alpha.5fc8ceacd.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 +536 -339
- package/dist/index.esm2017.js.map +1 -1
- package/dist/index.esm2017.mjs +596 -372
- package/dist/index.esm2017.mjs.map +1 -1
- package/dist/index.esm5.js +536 -339
- package/dist/index.esm5.js.map +1 -1
- package/dist/index.node.cjs.js +644 -418
- package/dist/index.node.cjs.js.map +1 -1
- package/dist/src/constants.d.ts +1 -0
- package/dist/src/index.d.ts +1 -1
- package/dist/src/interfaces.d.ts +14 -0
- package/dist/src/manifestUpdater.d.ts +1 -1
- package/dist/src/specParser.browser.d.ts +3 -2
- package/dist/src/specParser.d.ts +2 -0
- package/dist/src/utils.d.ts +3 -24
- package/package.json +3 -3
package/dist/index.node.cjs.js
CHANGED
|
@@ -208,7 +208,8 @@ ConstantString.CommandDescriptionMaxLens = 128;
|
|
|
208
208
|
ConstantString.ParameterDescriptionMaxLens = 128;
|
|
209
209
|
ConstantString.CommandTitleMaxLens = 32;
|
|
210
210
|
ConstantString.ParameterTitleMaxLens = 32;
|
|
211
|
-
ConstantString.SMERequiredParamsMaxNum = 5;
|
|
211
|
+
ConstantString.SMERequiredParamsMaxNum = 5;
|
|
212
|
+
ConstantString.DefaultPluginId = "plugin_1";
|
|
212
213
|
|
|
213
214
|
// Copyright (c) Microsoft Corporation.
|
|
214
215
|
class SpecParserError extends Error {
|
|
@@ -231,249 +232,9 @@ class Utils {
|
|
|
231
232
|
}
|
|
232
233
|
return false;
|
|
233
234
|
}
|
|
234
|
-
static checkParameters(paramObject, isCopilot) {
|
|
235
|
-
const paramResult = {
|
|
236
|
-
requiredNum: 0,
|
|
237
|
-
optionalNum: 0,
|
|
238
|
-
isValid: true,
|
|
239
|
-
reason: [],
|
|
240
|
-
};
|
|
241
|
-
if (!paramObject) {
|
|
242
|
-
return paramResult;
|
|
243
|
-
}
|
|
244
|
-
for (let i = 0; i < paramObject.length; i++) {
|
|
245
|
-
const param = paramObject[i];
|
|
246
|
-
const schema = param.schema;
|
|
247
|
-
if (isCopilot && this.hasNestedObjectInSchema(schema)) {
|
|
248
|
-
paramResult.isValid = false;
|
|
249
|
-
paramResult.reason.push(exports.ErrorType.ParamsContainsNestedObject);
|
|
250
|
-
continue;
|
|
251
|
-
}
|
|
252
|
-
const isRequiredWithoutDefault = param.required && schema.default === undefined;
|
|
253
|
-
if (isCopilot) {
|
|
254
|
-
if (isRequiredWithoutDefault) {
|
|
255
|
-
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
256
|
-
}
|
|
257
|
-
else {
|
|
258
|
-
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
259
|
-
}
|
|
260
|
-
continue;
|
|
261
|
-
}
|
|
262
|
-
if (param.in === "header" || param.in === "cookie") {
|
|
263
|
-
if (isRequiredWithoutDefault) {
|
|
264
|
-
paramResult.isValid = false;
|
|
265
|
-
paramResult.reason.push(exports.ErrorType.ParamsContainRequiredUnsupportedSchema);
|
|
266
|
-
}
|
|
267
|
-
continue;
|
|
268
|
-
}
|
|
269
|
-
if (schema.type !== "boolean" &&
|
|
270
|
-
schema.type !== "string" &&
|
|
271
|
-
schema.type !== "number" &&
|
|
272
|
-
schema.type !== "integer") {
|
|
273
|
-
if (isRequiredWithoutDefault) {
|
|
274
|
-
paramResult.isValid = false;
|
|
275
|
-
paramResult.reason.push(exports.ErrorType.ParamsContainRequiredUnsupportedSchema);
|
|
276
|
-
}
|
|
277
|
-
continue;
|
|
278
|
-
}
|
|
279
|
-
if (param.in === "query" || param.in === "path") {
|
|
280
|
-
if (isRequiredWithoutDefault) {
|
|
281
|
-
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
282
|
-
}
|
|
283
|
-
else {
|
|
284
|
-
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
return paramResult;
|
|
289
|
-
}
|
|
290
|
-
static checkPostBody(schema, isRequired = false, isCopilot = false) {
|
|
291
|
-
var _a;
|
|
292
|
-
const paramResult = {
|
|
293
|
-
requiredNum: 0,
|
|
294
|
-
optionalNum: 0,
|
|
295
|
-
isValid: true,
|
|
296
|
-
reason: [],
|
|
297
|
-
};
|
|
298
|
-
if (Object.keys(schema).length === 0) {
|
|
299
|
-
return paramResult;
|
|
300
|
-
}
|
|
301
|
-
const isRequiredWithoutDefault = isRequired && schema.default === undefined;
|
|
302
|
-
if (isCopilot && this.hasNestedObjectInSchema(schema)) {
|
|
303
|
-
paramResult.isValid = false;
|
|
304
|
-
paramResult.reason = [exports.ErrorType.RequestBodyContainsNestedObject];
|
|
305
|
-
return paramResult;
|
|
306
|
-
}
|
|
307
|
-
if (schema.type === "string" ||
|
|
308
|
-
schema.type === "integer" ||
|
|
309
|
-
schema.type === "boolean" ||
|
|
310
|
-
schema.type === "number") {
|
|
311
|
-
if (isRequiredWithoutDefault) {
|
|
312
|
-
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
313
|
-
}
|
|
314
|
-
else {
|
|
315
|
-
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
else if (schema.type === "object") {
|
|
319
|
-
const { properties } = schema;
|
|
320
|
-
for (const property in properties) {
|
|
321
|
-
let isRequired = false;
|
|
322
|
-
if (schema.required && ((_a = schema.required) === null || _a === void 0 ? void 0 : _a.indexOf(property)) >= 0) {
|
|
323
|
-
isRequired = true;
|
|
324
|
-
}
|
|
325
|
-
const result = Utils.checkPostBody(properties[property], isRequired, isCopilot);
|
|
326
|
-
paramResult.requiredNum += result.requiredNum;
|
|
327
|
-
paramResult.optionalNum += result.optionalNum;
|
|
328
|
-
paramResult.isValid = paramResult.isValid && result.isValid;
|
|
329
|
-
paramResult.reason.push(...result.reason);
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
else {
|
|
333
|
-
if (isRequiredWithoutDefault && !isCopilot) {
|
|
334
|
-
paramResult.isValid = false;
|
|
335
|
-
paramResult.reason.push(exports.ErrorType.PostBodyContainsRequiredUnsupportedSchema);
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
return paramResult;
|
|
339
|
-
}
|
|
340
235
|
static containMultipleMediaTypes(bodyObject) {
|
|
341
236
|
return Object.keys((bodyObject === null || bodyObject === void 0 ? void 0 : bodyObject.content) || {}).length > 1;
|
|
342
237
|
}
|
|
343
|
-
/**
|
|
344
|
-
* Checks if the given API is supported.
|
|
345
|
-
* @param {string} method - The HTTP method of the API.
|
|
346
|
-
* @param {string} path - The path of the API.
|
|
347
|
-
* @param {OpenAPIV3.Document} spec - The OpenAPI specification document.
|
|
348
|
-
* @returns {boolean} - Returns true if the API is supported, false otherwise.
|
|
349
|
-
* @description The following APIs are supported:
|
|
350
|
-
* 1. only support Get/Post operation without auth property
|
|
351
|
-
* 2. parameter inside query or path only support string, number, boolean and integer
|
|
352
|
-
* 3. parameter inside post body only support string, number, boolean, integer and object
|
|
353
|
-
* 4. request body + required parameters <= 1
|
|
354
|
-
* 5. response body should be “application/json” and not empty, and response code should be 20X
|
|
355
|
-
* 6. only support request body with “application/json” content type
|
|
356
|
-
*/
|
|
357
|
-
static isSupportedApi(method, path, spec, options) {
|
|
358
|
-
var _a;
|
|
359
|
-
const result = { isValid: true, reason: [] };
|
|
360
|
-
method = method.toLocaleLowerCase();
|
|
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);
|
|
423
|
-
}
|
|
424
|
-
requestBodyParamResult = Utils.checkPostBody(requestBodySchema, requestBody.required, isCopilot);
|
|
425
|
-
if (!requestBodyParamResult.isValid && requestBodyParamResult.reason) {
|
|
426
|
-
result.reason.push(...requestBodyParamResult.reason);
|
|
427
|
-
}
|
|
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);
|
|
441
|
-
}
|
|
442
|
-
}
|
|
443
|
-
else if (totalParams === 0) {
|
|
444
|
-
result.reason.push(exports.ErrorType.NoParameter);
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
if (result.reason.length > 0) {
|
|
449
|
-
result.isValid = false;
|
|
450
|
-
}
|
|
451
|
-
return result;
|
|
452
|
-
}
|
|
453
|
-
static isSupportedAuth(authSchemeArray, options) {
|
|
454
|
-
if (authSchemeArray.length === 0) {
|
|
455
|
-
return { isValid: true, reason: [] };
|
|
456
|
-
}
|
|
457
|
-
if (options.allowAPIKeyAuth || options.allowOauth2 || options.allowBearerTokenAuth) {
|
|
458
|
-
// Currently we don't support multiple auth in one operation
|
|
459
|
-
if (authSchemeArray.length > 0 && authSchemeArray.every((auths) => auths.length > 1)) {
|
|
460
|
-
return {
|
|
461
|
-
isValid: false,
|
|
462
|
-
reason: [exports.ErrorType.MultipleAuthNotSupported],
|
|
463
|
-
};
|
|
464
|
-
}
|
|
465
|
-
for (const auths of authSchemeArray) {
|
|
466
|
-
if (auths.length === 1) {
|
|
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: [] };
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
return { isValid: false, reason: [exports.ErrorType.AuthTypeIsNotSupported] };
|
|
476
|
-
}
|
|
477
238
|
static isBearerTokenAuth(authScheme) {
|
|
478
239
|
return authScheme.type === "http" && authScheme.scheme === "bearer";
|
|
479
240
|
}
|
|
@@ -481,10 +242,9 @@ class Utils {
|
|
|
481
242
|
return authScheme.type === "apiKey";
|
|
482
243
|
}
|
|
483
244
|
static isOAuthWithAuthCodeFlow(authScheme) {
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
return false;
|
|
245
|
+
return !!(authScheme.type === "oauth2" &&
|
|
246
|
+
authScheme.flows &&
|
|
247
|
+
authScheme.flows.authorizationCode);
|
|
488
248
|
}
|
|
489
249
|
static getAuthArray(securities, spec) {
|
|
490
250
|
var _a;
|
|
@@ -512,7 +272,7 @@ class Utils {
|
|
|
512
272
|
static updateFirstLetter(str) {
|
|
513
273
|
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
514
274
|
}
|
|
515
|
-
static getResponseJson(operationObject
|
|
275
|
+
static getResponseJson(operationObject) {
|
|
516
276
|
var _a, _b;
|
|
517
277
|
let json = {};
|
|
518
278
|
let multipleMediaType = false;
|
|
@@ -523,9 +283,6 @@ class Utils {
|
|
|
523
283
|
json = responseObject.content["application/json"];
|
|
524
284
|
if (Utils.containMultipleMediaTypes(responseObject)) {
|
|
525
285
|
multipleMediaType = true;
|
|
526
|
-
if (isTeamsAiProject) {
|
|
527
|
-
break;
|
|
528
|
-
}
|
|
529
286
|
json = {};
|
|
530
287
|
}
|
|
531
288
|
else {
|
|
@@ -764,16 +521,49 @@ class Utils {
|
|
|
764
521
|
};
|
|
765
522
|
return command;
|
|
766
523
|
}
|
|
767
|
-
static
|
|
524
|
+
static format(str, ...args) {
|
|
525
|
+
let index = 0;
|
|
526
|
+
return str.replace(/%s/g, () => {
|
|
527
|
+
const arg = args[index++];
|
|
528
|
+
return arg !== undefined ? arg : "";
|
|
529
|
+
});
|
|
530
|
+
}
|
|
531
|
+
static getSafeRegistrationIdEnvName(authName) {
|
|
532
|
+
if (!authName) {
|
|
533
|
+
return "";
|
|
534
|
+
}
|
|
535
|
+
let safeRegistrationIdEnvName = authName.toUpperCase().replace(/[^A-Z0-9_]/g, "_");
|
|
536
|
+
if (!safeRegistrationIdEnvName.match(/^[A-Z]/)) {
|
|
537
|
+
safeRegistrationIdEnvName = "PREFIX_" + safeRegistrationIdEnvName;
|
|
538
|
+
}
|
|
539
|
+
return safeRegistrationIdEnvName;
|
|
540
|
+
}
|
|
541
|
+
static getServerObject(spec, method, path) {
|
|
542
|
+
const pathObj = spec.paths[path];
|
|
543
|
+
const operationObject = pathObj[method];
|
|
544
|
+
const rootServer = spec.servers && spec.servers[0];
|
|
545
|
+
const methodServer = spec.paths[path].servers && spec.paths[path].servers[0];
|
|
546
|
+
const operationServer = operationObject.servers && operationObject.servers[0];
|
|
547
|
+
const serverUrl = operationServer || methodServer || rootServer;
|
|
548
|
+
return serverUrl;
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
// Copyright (c) Microsoft Corporation.
|
|
553
|
+
class Validator {
|
|
554
|
+
listAPIs() {
|
|
768
555
|
var _a;
|
|
769
|
-
|
|
556
|
+
if (this.apiMap) {
|
|
557
|
+
return this.apiMap;
|
|
558
|
+
}
|
|
559
|
+
const paths = this.spec.paths;
|
|
770
560
|
const result = {};
|
|
771
561
|
for (const path in paths) {
|
|
772
562
|
const methods = paths[path];
|
|
773
563
|
for (const method in methods) {
|
|
774
564
|
const operationObject = methods[method];
|
|
775
|
-
if (((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
|
|
776
|
-
const validateResult =
|
|
565
|
+
if (((_a = this.options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
|
|
566
|
+
const validateResult = this.validateAPI(method, path);
|
|
777
567
|
result[`${method.toUpperCase()} ${path}`] = {
|
|
778
568
|
operation: operationObject,
|
|
779
569
|
isValid: validateResult.isValid,
|
|
@@ -782,38 +572,48 @@ class Utils {
|
|
|
782
572
|
}
|
|
783
573
|
}
|
|
784
574
|
}
|
|
575
|
+
this.apiMap = result;
|
|
785
576
|
return result;
|
|
786
577
|
}
|
|
787
|
-
|
|
788
|
-
const
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
content: ConstantString.ConvertSwaggerToOpenAPI,
|
|
795
|
-
});
|
|
796
|
-
}
|
|
797
|
-
const serverErrors = Utils.validateServer(spec, options);
|
|
798
|
-
errors.push(...serverErrors);
|
|
799
|
-
// Remote reference not supported
|
|
800
|
-
const refPaths = parser.$refs.paths();
|
|
801
|
-
// refPaths [0] is the current spec file path
|
|
802
|
-
if (refPaths.length > 1) {
|
|
803
|
-
errors.push({
|
|
804
|
-
type: exports.ErrorType.RemoteRefNotSupported,
|
|
805
|
-
content: Utils.format(ConstantString.RemoteRefNotSupported, refPaths.join(", ")),
|
|
806
|
-
data: refPaths,
|
|
578
|
+
validateSpecVersion() {
|
|
579
|
+
const result = { errors: [], warnings: [] };
|
|
580
|
+
if (this.spec.openapi >= "3.1.0") {
|
|
581
|
+
result.errors.push({
|
|
582
|
+
type: exports.ErrorType.SpecVersionNotSupported,
|
|
583
|
+
content: Utils.format(ConstantString.SpecVersionNotSupported, this.spec.openapi),
|
|
584
|
+
data: this.spec.openapi,
|
|
807
585
|
});
|
|
808
586
|
}
|
|
809
|
-
|
|
587
|
+
return result;
|
|
588
|
+
}
|
|
589
|
+
validateSpecServer() {
|
|
590
|
+
const result = { errors: [], warnings: [] };
|
|
591
|
+
const serverErrors = Utils.validateServer(this.spec, this.options);
|
|
592
|
+
result.errors.push(...serverErrors);
|
|
593
|
+
return result;
|
|
594
|
+
}
|
|
595
|
+
validateSpecNoSupportAPI() {
|
|
596
|
+
const result = { errors: [], warnings: [] };
|
|
597
|
+
const apiMap = this.listAPIs();
|
|
810
598
|
const validAPIs = Object.entries(apiMap).filter(([, value]) => value.isValid);
|
|
811
599
|
if (validAPIs.length === 0) {
|
|
812
|
-
|
|
600
|
+
const data = [];
|
|
601
|
+
for (const key in apiMap) {
|
|
602
|
+
const { reason } = apiMap[key];
|
|
603
|
+
const apiInvalidReason = { api: key, reason: reason };
|
|
604
|
+
data.push(apiInvalidReason);
|
|
605
|
+
}
|
|
606
|
+
result.errors.push({
|
|
813
607
|
type: exports.ErrorType.NoSupportedApi,
|
|
814
608
|
content: ConstantString.NoSupportedApi,
|
|
609
|
+
data,
|
|
815
610
|
});
|
|
816
611
|
}
|
|
612
|
+
return result;
|
|
613
|
+
}
|
|
614
|
+
validateSpecOperationId() {
|
|
615
|
+
const result = { errors: [], warnings: [] };
|
|
616
|
+
const apiMap = this.listAPIs();
|
|
817
617
|
// OperationId missing
|
|
818
618
|
const apisMissingOperationId = [];
|
|
819
619
|
for (const key in apiMap) {
|
|
@@ -823,54 +623,431 @@ class Utils {
|
|
|
823
623
|
}
|
|
824
624
|
}
|
|
825
625
|
if (apisMissingOperationId.length > 0) {
|
|
826
|
-
warnings.push({
|
|
626
|
+
result.warnings.push({
|
|
827
627
|
type: exports.WarningType.OperationIdMissing,
|
|
828
628
|
content: Utils.format(ConstantString.MissingOperationId, apisMissingOperationId.join(", ")),
|
|
829
629
|
data: apisMissingOperationId,
|
|
830
630
|
});
|
|
831
631
|
}
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
632
|
+
return result;
|
|
633
|
+
}
|
|
634
|
+
validateMethodAndPath(method, path) {
|
|
635
|
+
const result = { isValid: true, reason: [] };
|
|
636
|
+
if (this.options.allowMethods && !this.options.allowMethods.includes(method)) {
|
|
637
|
+
result.isValid = false;
|
|
638
|
+
result.reason.push(exports.ErrorType.MethodNotAllowed);
|
|
639
|
+
return result;
|
|
835
640
|
}
|
|
836
|
-
|
|
837
|
-
|
|
641
|
+
const pathObj = this.spec.paths[path];
|
|
642
|
+
if (!pathObj || !pathObj[method]) {
|
|
643
|
+
result.isValid = false;
|
|
644
|
+
result.reason.push(exports.ErrorType.UrlPathNotExist);
|
|
645
|
+
return result;
|
|
838
646
|
}
|
|
839
|
-
return
|
|
840
|
-
status,
|
|
841
|
-
warnings,
|
|
842
|
-
errors,
|
|
843
|
-
};
|
|
647
|
+
return result;
|
|
844
648
|
}
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
649
|
+
validateResponse(method, path) {
|
|
650
|
+
const result = { isValid: true, reason: [] };
|
|
651
|
+
const operationObject = this.spec.paths[path][method];
|
|
652
|
+
const { json, multipleMediaType } = Utils.getResponseJson(operationObject);
|
|
653
|
+
// only support response body only contains “application/json” content type
|
|
654
|
+
if (multipleMediaType) {
|
|
655
|
+
result.reason.push(exports.ErrorType.ResponseContainMultipleMediaTypes);
|
|
656
|
+
}
|
|
657
|
+
else if (Object.keys(json).length === 0) {
|
|
658
|
+
// response body should not be empty
|
|
659
|
+
result.reason.push(exports.ErrorType.ResponseJsonIsEmpty);
|
|
660
|
+
}
|
|
661
|
+
return result;
|
|
851
662
|
}
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
663
|
+
validateServer(method, path) {
|
|
664
|
+
const result = { isValid: true, reason: [] };
|
|
665
|
+
const serverObj = Utils.getServerObject(this.spec, method, path);
|
|
666
|
+
if (!serverObj) {
|
|
667
|
+
// should contain server URL
|
|
668
|
+
result.reason.push(exports.ErrorType.NoServerInformation);
|
|
855
669
|
}
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
670
|
+
else {
|
|
671
|
+
// server url should be absolute url with https protocol
|
|
672
|
+
const serverValidateResult = Utils.checkServerUrl([serverObj]);
|
|
673
|
+
result.reason.push(...serverValidateResult.map((item) => item.type));
|
|
859
674
|
}
|
|
860
|
-
return
|
|
675
|
+
return result;
|
|
861
676
|
}
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
const
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
677
|
+
validateAuth(method, path) {
|
|
678
|
+
const pathObj = this.spec.paths[path];
|
|
679
|
+
const operationObject = pathObj[method];
|
|
680
|
+
const securities = operationObject.security;
|
|
681
|
+
const authSchemeArray = Utils.getAuthArray(securities, this.spec);
|
|
682
|
+
if (authSchemeArray.length === 0) {
|
|
683
|
+
return { isValid: true, reason: [] };
|
|
684
|
+
}
|
|
685
|
+
if (this.options.allowAPIKeyAuth ||
|
|
686
|
+
this.options.allowOauth2 ||
|
|
687
|
+
this.options.allowBearerTokenAuth) {
|
|
688
|
+
// Currently we don't support multiple auth in one operation
|
|
689
|
+
if (authSchemeArray.length > 0 && authSchemeArray.every((auths) => auths.length > 1)) {
|
|
690
|
+
return {
|
|
691
|
+
isValid: false,
|
|
692
|
+
reason: [exports.ErrorType.MultipleAuthNotSupported],
|
|
693
|
+
};
|
|
694
|
+
}
|
|
695
|
+
for (const auths of authSchemeArray) {
|
|
696
|
+
if (auths.length === 1) {
|
|
697
|
+
if ((this.options.allowAPIKeyAuth && Utils.isAPIKeyAuth(auths[0].authScheme)) ||
|
|
698
|
+
(this.options.allowOauth2 && Utils.isOAuthWithAuthCodeFlow(auths[0].authScheme)) ||
|
|
699
|
+
(this.options.allowBearerTokenAuth && Utils.isBearerTokenAuth(auths[0].authScheme))) {
|
|
700
|
+
return { isValid: true, reason: [] };
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
return { isValid: false, reason: [exports.ErrorType.AuthTypeIsNotSupported] };
|
|
706
|
+
}
|
|
707
|
+
checkPostBodySchema(schema, isRequired = false) {
|
|
708
|
+
var _a;
|
|
709
|
+
const paramResult = {
|
|
710
|
+
requiredNum: 0,
|
|
711
|
+
optionalNum: 0,
|
|
712
|
+
isValid: true,
|
|
713
|
+
reason: [],
|
|
714
|
+
};
|
|
715
|
+
if (Object.keys(schema).length === 0) {
|
|
716
|
+
return paramResult;
|
|
717
|
+
}
|
|
718
|
+
const isRequiredWithoutDefault = isRequired && schema.default === undefined;
|
|
719
|
+
const isCopilot = this.projectType === exports.ProjectType.Copilot;
|
|
720
|
+
if (isCopilot && this.hasNestedObjectInSchema(schema)) {
|
|
721
|
+
paramResult.isValid = false;
|
|
722
|
+
paramResult.reason = [exports.ErrorType.RequestBodyContainsNestedObject];
|
|
723
|
+
return paramResult;
|
|
724
|
+
}
|
|
725
|
+
if (schema.type === "string" ||
|
|
726
|
+
schema.type === "integer" ||
|
|
727
|
+
schema.type === "boolean" ||
|
|
728
|
+
schema.type === "number") {
|
|
729
|
+
if (isRequiredWithoutDefault) {
|
|
730
|
+
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
731
|
+
}
|
|
732
|
+
else {
|
|
733
|
+
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
else if (schema.type === "object") {
|
|
737
|
+
const { properties } = schema;
|
|
738
|
+
for (const property in properties) {
|
|
739
|
+
let isRequired = false;
|
|
740
|
+
if (schema.required && ((_a = schema.required) === null || _a === void 0 ? void 0 : _a.indexOf(property)) >= 0) {
|
|
741
|
+
isRequired = true;
|
|
742
|
+
}
|
|
743
|
+
const result = this.checkPostBodySchema(properties[property], isRequired);
|
|
744
|
+
paramResult.requiredNum += result.requiredNum;
|
|
745
|
+
paramResult.optionalNum += result.optionalNum;
|
|
746
|
+
paramResult.isValid = paramResult.isValid && result.isValid;
|
|
747
|
+
paramResult.reason.push(...result.reason);
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
else {
|
|
751
|
+
if (isRequiredWithoutDefault && !isCopilot) {
|
|
752
|
+
paramResult.isValid = false;
|
|
753
|
+
paramResult.reason.push(exports.ErrorType.PostBodyContainsRequiredUnsupportedSchema);
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
return paramResult;
|
|
757
|
+
}
|
|
758
|
+
checkParamSchema(paramObject) {
|
|
759
|
+
const paramResult = {
|
|
760
|
+
requiredNum: 0,
|
|
761
|
+
optionalNum: 0,
|
|
762
|
+
isValid: true,
|
|
763
|
+
reason: [],
|
|
764
|
+
};
|
|
765
|
+
if (!paramObject) {
|
|
766
|
+
return paramResult;
|
|
767
|
+
}
|
|
768
|
+
const isCopilot = this.projectType === exports.ProjectType.Copilot;
|
|
769
|
+
for (let i = 0; i < paramObject.length; i++) {
|
|
770
|
+
const param = paramObject[i];
|
|
771
|
+
const schema = param.schema;
|
|
772
|
+
if (isCopilot && this.hasNestedObjectInSchema(schema)) {
|
|
773
|
+
paramResult.isValid = false;
|
|
774
|
+
paramResult.reason.push(exports.ErrorType.ParamsContainsNestedObject);
|
|
775
|
+
continue;
|
|
776
|
+
}
|
|
777
|
+
const isRequiredWithoutDefault = param.required && schema.default === undefined;
|
|
778
|
+
if (isCopilot) {
|
|
779
|
+
if (isRequiredWithoutDefault) {
|
|
780
|
+
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
781
|
+
}
|
|
782
|
+
else {
|
|
783
|
+
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
784
|
+
}
|
|
785
|
+
continue;
|
|
786
|
+
}
|
|
787
|
+
if (param.in === "header" || param.in === "cookie") {
|
|
788
|
+
if (isRequiredWithoutDefault) {
|
|
789
|
+
paramResult.isValid = false;
|
|
790
|
+
paramResult.reason.push(exports.ErrorType.ParamsContainRequiredUnsupportedSchema);
|
|
791
|
+
}
|
|
792
|
+
continue;
|
|
793
|
+
}
|
|
794
|
+
if (schema.type !== "boolean" &&
|
|
795
|
+
schema.type !== "string" &&
|
|
796
|
+
schema.type !== "number" &&
|
|
797
|
+
schema.type !== "integer") {
|
|
798
|
+
if (isRequiredWithoutDefault) {
|
|
799
|
+
paramResult.isValid = false;
|
|
800
|
+
paramResult.reason.push(exports.ErrorType.ParamsContainRequiredUnsupportedSchema);
|
|
801
|
+
}
|
|
802
|
+
continue;
|
|
803
|
+
}
|
|
804
|
+
if (param.in === "query" || param.in === "path") {
|
|
805
|
+
if (isRequiredWithoutDefault) {
|
|
806
|
+
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
807
|
+
}
|
|
808
|
+
else {
|
|
809
|
+
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
870
810
|
}
|
|
871
811
|
}
|
|
872
812
|
}
|
|
873
|
-
return
|
|
813
|
+
return paramResult;
|
|
814
|
+
}
|
|
815
|
+
hasNestedObjectInSchema(schema) {
|
|
816
|
+
if (schema.type === "object") {
|
|
817
|
+
for (const property in schema.properties) {
|
|
818
|
+
const nestedSchema = schema.properties[property];
|
|
819
|
+
if (nestedSchema.type === "object") {
|
|
820
|
+
return true;
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
return false;
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
// Copyright (c) Microsoft Corporation.
|
|
829
|
+
class CopilotValidator extends Validator {
|
|
830
|
+
constructor(spec, options) {
|
|
831
|
+
super();
|
|
832
|
+
this.projectType = exports.ProjectType.Copilot;
|
|
833
|
+
this.options = options;
|
|
834
|
+
this.spec = spec;
|
|
835
|
+
}
|
|
836
|
+
validateSpec() {
|
|
837
|
+
const result = { errors: [], warnings: [] };
|
|
838
|
+
// validate spec version
|
|
839
|
+
let validationResult = this.validateSpecVersion();
|
|
840
|
+
result.errors.push(...validationResult.errors);
|
|
841
|
+
// validate spec server
|
|
842
|
+
validationResult = this.validateSpecServer();
|
|
843
|
+
result.errors.push(...validationResult.errors);
|
|
844
|
+
// validate no supported API
|
|
845
|
+
validationResult = this.validateSpecNoSupportAPI();
|
|
846
|
+
result.errors.push(...validationResult.errors);
|
|
847
|
+
// validate operationId missing
|
|
848
|
+
validationResult = this.validateSpecOperationId();
|
|
849
|
+
result.warnings.push(...validationResult.warnings);
|
|
850
|
+
return result;
|
|
851
|
+
}
|
|
852
|
+
validateAPI(method, path) {
|
|
853
|
+
const result = { isValid: true, reason: [] };
|
|
854
|
+
method = method.toLocaleLowerCase();
|
|
855
|
+
// validate method and path
|
|
856
|
+
const methodAndPathResult = this.validateMethodAndPath(method, path);
|
|
857
|
+
if (!methodAndPathResult.isValid) {
|
|
858
|
+
return methodAndPathResult;
|
|
859
|
+
}
|
|
860
|
+
const operationObject = this.spec.paths[path][method];
|
|
861
|
+
// validate auth
|
|
862
|
+
const authCheckResult = this.validateAuth(method, path);
|
|
863
|
+
result.reason.push(...authCheckResult.reason);
|
|
864
|
+
// validate operationId
|
|
865
|
+
if (!this.options.allowMissingId && !operationObject.operationId) {
|
|
866
|
+
result.reason.push(exports.ErrorType.MissingOperationId);
|
|
867
|
+
}
|
|
868
|
+
// validate server
|
|
869
|
+
const validateServerResult = this.validateServer(method, path);
|
|
870
|
+
result.reason.push(...validateServerResult.reason);
|
|
871
|
+
// validate response
|
|
872
|
+
const validateResponseResult = this.validateResponse(method, path);
|
|
873
|
+
result.reason.push(...validateResponseResult.reason);
|
|
874
|
+
// validate requestBody
|
|
875
|
+
const requestBody = operationObject.requestBody;
|
|
876
|
+
const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
|
|
877
|
+
if (Utils.containMultipleMediaTypes(requestBody)) {
|
|
878
|
+
result.reason.push(exports.ErrorType.PostBodyContainMultipleMediaTypes);
|
|
879
|
+
}
|
|
880
|
+
if (requestJsonBody) {
|
|
881
|
+
const requestBodySchema = requestJsonBody.schema;
|
|
882
|
+
if (requestBodySchema.type !== "object") {
|
|
883
|
+
result.reason.push(exports.ErrorType.PostBodySchemaIsNotJson);
|
|
884
|
+
}
|
|
885
|
+
const requestBodyParamResult = this.checkPostBodySchema(requestBodySchema, requestBody.required);
|
|
886
|
+
result.reason.push(...requestBodyParamResult.reason);
|
|
887
|
+
}
|
|
888
|
+
// validate parameters
|
|
889
|
+
const paramObject = operationObject.parameters;
|
|
890
|
+
const paramResult = this.checkParamSchema(paramObject);
|
|
891
|
+
result.reason.push(...paramResult.reason);
|
|
892
|
+
if (result.reason.length > 0) {
|
|
893
|
+
result.isValid = false;
|
|
894
|
+
}
|
|
895
|
+
return result;
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
// Copyright (c) Microsoft Corporation.
|
|
900
|
+
class SMEValidator extends Validator {
|
|
901
|
+
constructor(spec, options) {
|
|
902
|
+
super();
|
|
903
|
+
this.projectType = exports.ProjectType.SME;
|
|
904
|
+
this.options = options;
|
|
905
|
+
this.spec = spec;
|
|
906
|
+
}
|
|
907
|
+
validateSpec() {
|
|
908
|
+
const result = { errors: [], warnings: [] };
|
|
909
|
+
// validate spec version
|
|
910
|
+
let validationResult = this.validateSpecVersion();
|
|
911
|
+
result.errors.push(...validationResult.errors);
|
|
912
|
+
// validate spec server
|
|
913
|
+
validationResult = this.validateSpecServer();
|
|
914
|
+
result.errors.push(...validationResult.errors);
|
|
915
|
+
// validate no supported API
|
|
916
|
+
validationResult = this.validateSpecNoSupportAPI();
|
|
917
|
+
result.errors.push(...validationResult.errors);
|
|
918
|
+
// validate operationId missing
|
|
919
|
+
if (this.options.allowMissingId) {
|
|
920
|
+
validationResult = this.validateSpecOperationId();
|
|
921
|
+
result.warnings.push(...validationResult.warnings);
|
|
922
|
+
}
|
|
923
|
+
return result;
|
|
924
|
+
}
|
|
925
|
+
validateAPI(method, path) {
|
|
926
|
+
const result = { isValid: true, reason: [] };
|
|
927
|
+
method = method.toLocaleLowerCase();
|
|
928
|
+
// validate method and path
|
|
929
|
+
const methodAndPathResult = this.validateMethodAndPath(method, path);
|
|
930
|
+
if (!methodAndPathResult.isValid) {
|
|
931
|
+
return methodAndPathResult;
|
|
932
|
+
}
|
|
933
|
+
const operationObject = this.spec.paths[path][method];
|
|
934
|
+
// validate auth
|
|
935
|
+
const authCheckResult = this.validateAuth(method, path);
|
|
936
|
+
result.reason.push(...authCheckResult.reason);
|
|
937
|
+
// validate operationId
|
|
938
|
+
if (!this.options.allowMissingId && !operationObject.operationId) {
|
|
939
|
+
result.reason.push(exports.ErrorType.MissingOperationId);
|
|
940
|
+
}
|
|
941
|
+
// validate server
|
|
942
|
+
const validateServerResult = this.validateServer(method, path);
|
|
943
|
+
result.reason.push(...validateServerResult.reason);
|
|
944
|
+
// validate response
|
|
945
|
+
const validateResponseResult = this.validateResponse(method, path);
|
|
946
|
+
result.reason.push(...validateResponseResult.reason);
|
|
947
|
+
let postBodyResult = {
|
|
948
|
+
requiredNum: 0,
|
|
949
|
+
optionalNum: 0,
|
|
950
|
+
isValid: true,
|
|
951
|
+
reason: [],
|
|
952
|
+
};
|
|
953
|
+
// validate requestBody
|
|
954
|
+
const requestBody = operationObject.requestBody;
|
|
955
|
+
const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
|
|
956
|
+
if (Utils.containMultipleMediaTypes(requestBody)) {
|
|
957
|
+
result.reason.push(exports.ErrorType.PostBodyContainMultipleMediaTypes);
|
|
958
|
+
}
|
|
959
|
+
if (requestJsonBody) {
|
|
960
|
+
const requestBodySchema = requestJsonBody.schema;
|
|
961
|
+
postBodyResult = this.checkPostBodySchema(requestBodySchema, requestBody.required);
|
|
962
|
+
result.reason.push(...postBodyResult.reason);
|
|
963
|
+
}
|
|
964
|
+
// validate parameters
|
|
965
|
+
const paramObject = operationObject.parameters;
|
|
966
|
+
const paramResult = this.checkParamSchema(paramObject);
|
|
967
|
+
result.reason.push(...paramResult.reason);
|
|
968
|
+
// validate total parameters count
|
|
969
|
+
if (paramResult.isValid && postBodyResult.isValid) {
|
|
970
|
+
const paramCountResult = this.validateParamCount(postBodyResult, paramResult);
|
|
971
|
+
result.reason.push(...paramCountResult.reason);
|
|
972
|
+
}
|
|
973
|
+
if (result.reason.length > 0) {
|
|
974
|
+
result.isValid = false;
|
|
975
|
+
}
|
|
976
|
+
return result;
|
|
977
|
+
}
|
|
978
|
+
validateParamCount(postBodyResult, paramResult) {
|
|
979
|
+
const result = { isValid: true, reason: [] };
|
|
980
|
+
const totalRequiredParams = postBodyResult.requiredNum + paramResult.requiredNum;
|
|
981
|
+
const totalParams = totalRequiredParams + postBodyResult.optionalNum + paramResult.optionalNum;
|
|
982
|
+
if (totalRequiredParams > 1) {
|
|
983
|
+
if (!this.options.allowMultipleParameters ||
|
|
984
|
+
totalRequiredParams > SMEValidator.SMERequiredParamsMaxNum) {
|
|
985
|
+
result.reason.push(exports.ErrorType.ExceededRequiredParamsLimit);
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
else if (totalParams === 0) {
|
|
989
|
+
result.reason.push(exports.ErrorType.NoParameter);
|
|
990
|
+
}
|
|
991
|
+
return result;
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
SMEValidator.SMERequiredParamsMaxNum = 5;
|
|
995
|
+
|
|
996
|
+
// Copyright (c) Microsoft Corporation.
|
|
997
|
+
class TeamsAIValidator extends Validator {
|
|
998
|
+
constructor(spec, options) {
|
|
999
|
+
super();
|
|
1000
|
+
this.projectType = exports.ProjectType.TeamsAi;
|
|
1001
|
+
this.options = options;
|
|
1002
|
+
this.spec = spec;
|
|
1003
|
+
}
|
|
1004
|
+
validateSpec() {
|
|
1005
|
+
const result = { errors: [], warnings: [] };
|
|
1006
|
+
// validate spec server
|
|
1007
|
+
let validationResult = this.validateSpecServer();
|
|
1008
|
+
result.errors.push(...validationResult.errors);
|
|
1009
|
+
// validate no supported API
|
|
1010
|
+
validationResult = this.validateSpecNoSupportAPI();
|
|
1011
|
+
result.errors.push(...validationResult.errors);
|
|
1012
|
+
return result;
|
|
1013
|
+
}
|
|
1014
|
+
validateAPI(method, path) {
|
|
1015
|
+
const result = { isValid: true, reason: [] };
|
|
1016
|
+
method = method.toLocaleLowerCase();
|
|
1017
|
+
// validate method and path
|
|
1018
|
+
const methodAndPathResult = this.validateMethodAndPath(method, path);
|
|
1019
|
+
if (!methodAndPathResult.isValid) {
|
|
1020
|
+
return methodAndPathResult;
|
|
1021
|
+
}
|
|
1022
|
+
const operationObject = this.spec.paths[path][method];
|
|
1023
|
+
// validate operationId
|
|
1024
|
+
if (!this.options.allowMissingId && !operationObject.operationId) {
|
|
1025
|
+
result.reason.push(exports.ErrorType.MissingOperationId);
|
|
1026
|
+
}
|
|
1027
|
+
// validate server
|
|
1028
|
+
const validateServerResult = this.validateServer(method, path);
|
|
1029
|
+
result.reason.push(...validateServerResult.reason);
|
|
1030
|
+
if (result.reason.length > 0) {
|
|
1031
|
+
result.isValid = false;
|
|
1032
|
+
}
|
|
1033
|
+
return result;
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
class ValidatorFactory {
|
|
1038
|
+
static create(spec, options) {
|
|
1039
|
+
var _a;
|
|
1040
|
+
const type = (_a = options.projectType) !== null && _a !== void 0 ? _a : exports.ProjectType.SME;
|
|
1041
|
+
switch (type) {
|
|
1042
|
+
case exports.ProjectType.SME:
|
|
1043
|
+
return new SMEValidator(spec, options);
|
|
1044
|
+
case exports.ProjectType.Copilot:
|
|
1045
|
+
return new CopilotValidator(spec, options);
|
|
1046
|
+
case exports.ProjectType.TeamsAi:
|
|
1047
|
+
return new TeamsAIValidator(spec, options);
|
|
1048
|
+
default:
|
|
1049
|
+
throw new Error(`Invalid project type: ${type}`);
|
|
1050
|
+
}
|
|
874
1051
|
}
|
|
875
1052
|
}
|
|
876
1053
|
|
|
@@ -888,7 +1065,8 @@ class SpecFilter {
|
|
|
888
1065
|
if (ConstantString.AllOperationMethods.includes(methodName) &&
|
|
889
1066
|
pathObj &&
|
|
890
1067
|
pathObj[methodName]) {
|
|
891
|
-
const
|
|
1068
|
+
const validator = ValidatorFactory.create(resolvedSpec, options);
|
|
1069
|
+
const validateResult = validator.validateAPI(methodName, path);
|
|
892
1070
|
if (!validateResult.isValid) {
|
|
893
1071
|
continue;
|
|
894
1072
|
}
|
|
@@ -922,13 +1100,14 @@ class ManifestUpdater {
|
|
|
922
1100
|
const apiPluginRelativePath = ManifestUpdater.getRelativePath(manifestPath, apiPluginFilePath);
|
|
923
1101
|
manifest.plugins = [
|
|
924
1102
|
{
|
|
925
|
-
|
|
1103
|
+
file: apiPluginRelativePath,
|
|
1104
|
+
id: ConstantString.DefaultPluginId,
|
|
926
1105
|
},
|
|
927
1106
|
];
|
|
928
1107
|
const appName = this.removeEnvs(manifest.name.short);
|
|
929
1108
|
ManifestUpdater.updateManifestDescription(manifest, spec);
|
|
930
1109
|
const specRelativePath = ManifestUpdater.getRelativePath(manifestPath, outputSpecPath);
|
|
931
|
-
const apiPlugin = ManifestUpdater.generatePluginManifestSchema(spec, specRelativePath, appName, options);
|
|
1110
|
+
const apiPlugin = yield ManifestUpdater.generatePluginManifestSchema(spec, specRelativePath, apiPluginFilePath, appName, options);
|
|
932
1111
|
return [manifest, apiPlugin];
|
|
933
1112
|
});
|
|
934
1113
|
}
|
|
@@ -953,88 +1132,119 @@ class ManifestUpdater {
|
|
|
953
1132
|
}
|
|
954
1133
|
return parameter;
|
|
955
1134
|
}
|
|
956
|
-
static generatePluginManifestSchema(spec, specRelativePath, appName, options) {
|
|
957
|
-
var _a, _b, _c;
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
const
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
parameters.properties[param.name].description
|
|
1135
|
+
static generatePluginManifestSchema(spec, specRelativePath, apiPluginFilePath, appName, options) {
|
|
1136
|
+
var _a, _b, _c, _d;
|
|
1137
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1138
|
+
const functions = [];
|
|
1139
|
+
const functionNames = [];
|
|
1140
|
+
const paths = spec.paths;
|
|
1141
|
+
for (const pathUrl in paths) {
|
|
1142
|
+
const pathItem = paths[pathUrl];
|
|
1143
|
+
if (pathItem) {
|
|
1144
|
+
const operations = pathItem;
|
|
1145
|
+
for (const method in operations) {
|
|
1146
|
+
if (options.allowMethods.includes(method)) {
|
|
1147
|
+
const operationItem = operations[method];
|
|
1148
|
+
if (operationItem) {
|
|
1149
|
+
const operationId = operationItem.operationId;
|
|
1150
|
+
const description = (_a = operationItem.description) !== null && _a !== void 0 ? _a : "";
|
|
1151
|
+
const paramObject = operationItem.parameters;
|
|
1152
|
+
const requestBody = operationItem.requestBody;
|
|
1153
|
+
const parameters = {
|
|
1154
|
+
type: "object",
|
|
1155
|
+
properties: {},
|
|
1156
|
+
required: [],
|
|
1157
|
+
};
|
|
1158
|
+
if (paramObject) {
|
|
1159
|
+
for (let i = 0; i < paramObject.length; i++) {
|
|
1160
|
+
const param = paramObject[i];
|
|
1161
|
+
const schema = param.schema;
|
|
1162
|
+
parameters.properties[param.name] = ManifestUpdater.mapOpenAPISchemaToFuncParam(schema, method, pathUrl);
|
|
1163
|
+
if (param.required) {
|
|
1164
|
+
parameters.required.push(param.name);
|
|
1165
|
+
}
|
|
1166
|
+
if (!parameters.properties[param.name].description) {
|
|
1167
|
+
parameters.properties[param.name].description = (_b = param.description) !== null && _b !== void 0 ? _b : "";
|
|
1168
|
+
}
|
|
988
1169
|
}
|
|
989
1170
|
}
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
1171
|
+
if (requestBody) {
|
|
1172
|
+
const requestJsonBody = requestBody.content["application/json"];
|
|
1173
|
+
const requestBodySchema = requestJsonBody.schema;
|
|
1174
|
+
if (requestBodySchema.type === "object") {
|
|
1175
|
+
if (requestBodySchema.required) {
|
|
1176
|
+
parameters.required.push(...requestBodySchema.required);
|
|
1177
|
+
}
|
|
1178
|
+
for (const property in requestBodySchema.properties) {
|
|
1179
|
+
const schema = requestBodySchema.properties[property];
|
|
1180
|
+
parameters.properties[property] = ManifestUpdater.mapOpenAPISchemaToFuncParam(schema, method, pathUrl);
|
|
1181
|
+
}
|
|
997
1182
|
}
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
parameters.properties[property] = ManifestUpdater.mapOpenAPISchemaToFuncParam(schema, method, pathUrl);
|
|
1183
|
+
else {
|
|
1184
|
+
throw new SpecParserError(Utils.format(ConstantString.UnsupportedSchema, method, pathUrl, JSON.stringify(requestBodySchema)), exports.ErrorType.UpdateManifestFailed);
|
|
1001
1185
|
}
|
|
1002
1186
|
}
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1187
|
+
const funcObj = {
|
|
1188
|
+
name: operationId,
|
|
1189
|
+
description: description,
|
|
1190
|
+
parameters: parameters,
|
|
1191
|
+
};
|
|
1192
|
+
functions.push(funcObj);
|
|
1193
|
+
functionNames.push(operationId);
|
|
1006
1194
|
}
|
|
1007
|
-
const funcObj = {
|
|
1008
|
-
name: operationId,
|
|
1009
|
-
description: description,
|
|
1010
|
-
parameters: parameters,
|
|
1011
|
-
};
|
|
1012
|
-
functions.push(funcObj);
|
|
1013
|
-
functionNames.push(operationId);
|
|
1014
1195
|
}
|
|
1015
1196
|
}
|
|
1016
1197
|
}
|
|
1017
1198
|
}
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1199
|
+
let apiPlugin;
|
|
1200
|
+
if (yield fs__default['default'].pathExists(apiPluginFilePath)) {
|
|
1201
|
+
apiPlugin = yield fs__default['default'].readJSON(apiPluginFilePath);
|
|
1202
|
+
}
|
|
1203
|
+
else {
|
|
1204
|
+
apiPlugin = {
|
|
1205
|
+
schema_version: "v2",
|
|
1206
|
+
name_for_human: "",
|
|
1207
|
+
description_for_human: "",
|
|
1208
|
+
functions: [],
|
|
1209
|
+
runtimes: [],
|
|
1210
|
+
};
|
|
1211
|
+
}
|
|
1212
|
+
apiPlugin.functions = apiPlugin.functions || [];
|
|
1213
|
+
for (const func of functions) {
|
|
1214
|
+
const index = (_c = apiPlugin.functions) === null || _c === void 0 ? void 0 : _c.findIndex((f) => f.name === func.name);
|
|
1215
|
+
if (index === -1) {
|
|
1216
|
+
apiPlugin.functions.push(func);
|
|
1217
|
+
}
|
|
1218
|
+
else {
|
|
1219
|
+
apiPlugin.functions[index] = func;
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
apiPlugin.runtimes = apiPlugin.runtimes || [];
|
|
1223
|
+
const index = apiPlugin.runtimes.findIndex((runtime) => runtime.spec.url === specRelativePath);
|
|
1224
|
+
if (index === -1) {
|
|
1225
|
+
apiPlugin.runtimes.push({
|
|
1026
1226
|
type: "OpenApi",
|
|
1027
1227
|
auth: {
|
|
1028
|
-
type: "none",
|
|
1228
|
+
type: "none",
|
|
1029
1229
|
},
|
|
1030
1230
|
spec: {
|
|
1031
1231
|
url: specRelativePath,
|
|
1032
1232
|
},
|
|
1033
1233
|
run_for_functions: functionNames,
|
|
1034
|
-
}
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1234
|
+
});
|
|
1235
|
+
}
|
|
1236
|
+
else {
|
|
1237
|
+
apiPlugin.runtimes[index].run_for_functions = functionNames;
|
|
1238
|
+
}
|
|
1239
|
+
if (!apiPlugin.name_for_human) {
|
|
1240
|
+
apiPlugin.name_for_human = appName;
|
|
1241
|
+
}
|
|
1242
|
+
if (!apiPlugin.description_for_human) {
|
|
1243
|
+
apiPlugin.description_for_human =
|
|
1244
|
+
(_d = spec.info.description) !== null && _d !== void 0 ? _d : "<Please add description of the plugin>";
|
|
1245
|
+
}
|
|
1246
|
+
return apiPlugin;
|
|
1247
|
+
});
|
|
1038
1248
|
}
|
|
1039
1249
|
static updateManifest(manifestPath, outputSpecPath, spec, options, adaptiveCardFolder, authInfo) {
|
|
1040
1250
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -1443,6 +1653,8 @@ class SpecParser {
|
|
|
1443
1653
|
errors: [{ type: exports.ErrorType.SpecNotValid, content: e.toString() }],
|
|
1444
1654
|
};
|
|
1445
1655
|
}
|
|
1656
|
+
const errors = [];
|
|
1657
|
+
const warnings = [];
|
|
1446
1658
|
if (!this.options.allowSwagger && this.isSwaggerFile) {
|
|
1447
1659
|
return {
|
|
1448
1660
|
status: exports.ValidationStatus.Error,
|
|
@@ -1452,23 +1664,38 @@ class SpecParser {
|
|
|
1452
1664
|
],
|
|
1453
1665
|
};
|
|
1454
1666
|
}
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
content: Utils.format(ConstantString.SpecVersionNotSupported, this.spec.openapi),
|
|
1465
|
-
data: this.spec.openapi,
|
|
1466
|
-
},
|
|
1467
|
-
],
|
|
1468
|
-
};
|
|
1469
|
-
}
|
|
1667
|
+
// Remote reference not supported
|
|
1668
|
+
const refPaths = this.parser.$refs.paths();
|
|
1669
|
+
// refPaths [0] is the current spec file path
|
|
1670
|
+
if (refPaths.length > 1) {
|
|
1671
|
+
errors.push({
|
|
1672
|
+
type: exports.ErrorType.RemoteRefNotSupported,
|
|
1673
|
+
content: Utils.format(ConstantString.RemoteRefNotSupported, refPaths.join(", ")),
|
|
1674
|
+
data: refPaths,
|
|
1675
|
+
});
|
|
1470
1676
|
}
|
|
1471
|
-
|
|
1677
|
+
if (!!this.isSwaggerFile && this.options.allowSwagger) {
|
|
1678
|
+
warnings.push({
|
|
1679
|
+
type: exports.WarningType.ConvertSwaggerToOpenAPI,
|
|
1680
|
+
content: ConstantString.ConvertSwaggerToOpenAPI,
|
|
1681
|
+
});
|
|
1682
|
+
}
|
|
1683
|
+
const validator = this.getValidator(this.spec);
|
|
1684
|
+
const validationResult = validator.validateSpec();
|
|
1685
|
+
warnings.push(...validationResult.warnings);
|
|
1686
|
+
errors.push(...validationResult.errors);
|
|
1687
|
+
let status = exports.ValidationStatus.Valid;
|
|
1688
|
+
if (warnings.length > 0 && errors.length === 0) {
|
|
1689
|
+
status = exports.ValidationStatus.Warning;
|
|
1690
|
+
}
|
|
1691
|
+
else if (errors.length > 0) {
|
|
1692
|
+
status = exports.ValidationStatus.Error;
|
|
1693
|
+
}
|
|
1694
|
+
return {
|
|
1695
|
+
status: status,
|
|
1696
|
+
warnings: warnings,
|
|
1697
|
+
errors: errors,
|
|
1698
|
+
};
|
|
1472
1699
|
}
|
|
1473
1700
|
catch (err) {
|
|
1474
1701
|
throw new SpecParserError(err.toString(), exports.ErrorType.ValidateFailed);
|
|
@@ -1501,34 +1728,27 @@ class SpecParser {
|
|
|
1501
1728
|
for (const apiKey in apiMap) {
|
|
1502
1729
|
const { operation, isValid, reason } = apiMap[apiKey];
|
|
1503
1730
|
const [method, path] = apiKey.split(" ");
|
|
1731
|
+
const operationId = (_a = operation.operationId) !== null && _a !== void 0 ? _a : `${method.toLowerCase()}${Utils.convertPathToCamelCase(path)}`;
|
|
1504
1732
|
const apiResult = {
|
|
1505
|
-
api:
|
|
1733
|
+
api: apiKey,
|
|
1506
1734
|
server: "",
|
|
1507
|
-
operationId:
|
|
1735
|
+
operationId: operationId,
|
|
1508
1736
|
isValid: isValid,
|
|
1509
1737
|
reason: reason,
|
|
1510
1738
|
};
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
}
|
|
1523
|
-
apiResult.operationId = operationId;
|
|
1524
|
-
const authArray = Utils.getAuthArray(operation.security, spec);
|
|
1525
|
-
for (const auths of authArray) {
|
|
1526
|
-
if (auths.length === 1) {
|
|
1527
|
-
apiResult.auth = auths[0];
|
|
1528
|
-
break;
|
|
1739
|
+
if (isValid) {
|
|
1740
|
+
const serverObj = Utils.getServerObject(spec, method.toLocaleLowerCase(), path);
|
|
1741
|
+
if (serverObj) {
|
|
1742
|
+
apiResult.server = Utils.resolveEnv(serverObj.url);
|
|
1743
|
+
}
|
|
1744
|
+
const authArray = Utils.getAuthArray(operation.security, spec);
|
|
1745
|
+
for (const auths of authArray) {
|
|
1746
|
+
if (auths.length === 1) {
|
|
1747
|
+
apiResult.auth = auths[0];
|
|
1748
|
+
break;
|
|
1749
|
+
}
|
|
1529
1750
|
}
|
|
1530
1751
|
}
|
|
1531
|
-
apiResult.api = apiKey;
|
|
1532
1752
|
result.APIs.push(apiResult);
|
|
1533
1753
|
}
|
|
1534
1754
|
result.allAPICount = result.APIs.length;
|
|
@@ -1717,12 +1937,18 @@ class SpecParser {
|
|
|
1717
1937
|
});
|
|
1718
1938
|
}
|
|
1719
1939
|
getAPIs(spec) {
|
|
1720
|
-
|
|
1721
|
-
|
|
1940
|
+
const validator = this.getValidator(spec);
|
|
1941
|
+
const apiMap = validator.listAPIs();
|
|
1942
|
+
this.apiMap = apiMap;
|
|
1943
|
+
return apiMap;
|
|
1944
|
+
}
|
|
1945
|
+
getValidator(spec) {
|
|
1946
|
+
if (this.validator) {
|
|
1947
|
+
return this.validator;
|
|
1722
1948
|
}
|
|
1723
|
-
const
|
|
1724
|
-
this.
|
|
1725
|
-
return
|
|
1949
|
+
const validator = ValidatorFactory.create(spec, this.options);
|
|
1950
|
+
this.validator = validator;
|
|
1951
|
+
return validator;
|
|
1726
1952
|
}
|
|
1727
1953
|
}
|
|
1728
1954
|
|