@microsoft/m365-spec-parser 0.1.1-alpha.7fe3da414.0 → 0.1.1-alpha.87f45d762.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 +537 -357
- package/dist/index.esm2017.js.map +1 -1
- package/dist/index.esm2017.mjs +611 -391
- package/dist/index.esm2017.mjs.map +1 -1
- package/dist/index.esm5.js +537 -357
- package/dist/index.esm5.js.map +1 -1
- package/dist/index.node.cjs.js +767 -545
- 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 +4 -25
- package/package.json +3 -3
package/dist/index.esm5.js
CHANGED
|
@@ -200,7 +200,8 @@ ConstantString.CommandDescriptionMaxLens = 128;
|
|
|
200
200
|
ConstantString.ParameterDescriptionMaxLens = 128;
|
|
201
201
|
ConstantString.CommandTitleMaxLens = 32;
|
|
202
202
|
ConstantString.ParameterTitleMaxLens = 32;
|
|
203
|
-
ConstantString.SMERequiredParamsMaxNum = 5;
|
|
203
|
+
ConstantString.SMERequiredParamsMaxNum = 5;
|
|
204
|
+
ConstantString.DefaultPluginId = "plugin_1";
|
|
204
205
|
|
|
205
206
|
// Copyright (c) Microsoft Corporation.
|
|
206
207
|
class Utils {
|
|
@@ -215,249 +216,9 @@ class Utils {
|
|
|
215
216
|
}
|
|
216
217
|
return false;
|
|
217
218
|
}
|
|
218
|
-
static checkParameters(paramObject, isCopilot) {
|
|
219
|
-
const paramResult = {
|
|
220
|
-
requiredNum: 0,
|
|
221
|
-
optionalNum: 0,
|
|
222
|
-
isValid: true,
|
|
223
|
-
reason: [],
|
|
224
|
-
};
|
|
225
|
-
if (!paramObject) {
|
|
226
|
-
return paramResult;
|
|
227
|
-
}
|
|
228
|
-
for (let i = 0; i < paramObject.length; i++) {
|
|
229
|
-
const param = paramObject[i];
|
|
230
|
-
const schema = param.schema;
|
|
231
|
-
if (isCopilot && this.hasNestedObjectInSchema(schema)) {
|
|
232
|
-
paramResult.isValid = false;
|
|
233
|
-
paramResult.reason.push(ErrorType.ParamsContainsNestedObject);
|
|
234
|
-
continue;
|
|
235
|
-
}
|
|
236
|
-
const isRequiredWithoutDefault = param.required && schema.default === undefined;
|
|
237
|
-
if (isCopilot) {
|
|
238
|
-
if (isRequiredWithoutDefault) {
|
|
239
|
-
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
240
|
-
}
|
|
241
|
-
else {
|
|
242
|
-
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
243
|
-
}
|
|
244
|
-
continue;
|
|
245
|
-
}
|
|
246
|
-
if (param.in === "header" || param.in === "cookie") {
|
|
247
|
-
if (isRequiredWithoutDefault) {
|
|
248
|
-
paramResult.isValid = false;
|
|
249
|
-
paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
|
|
250
|
-
}
|
|
251
|
-
continue;
|
|
252
|
-
}
|
|
253
|
-
if (schema.type !== "boolean" &&
|
|
254
|
-
schema.type !== "string" &&
|
|
255
|
-
schema.type !== "number" &&
|
|
256
|
-
schema.type !== "integer") {
|
|
257
|
-
if (isRequiredWithoutDefault) {
|
|
258
|
-
paramResult.isValid = false;
|
|
259
|
-
paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
|
|
260
|
-
}
|
|
261
|
-
continue;
|
|
262
|
-
}
|
|
263
|
-
if (param.in === "query" || param.in === "path") {
|
|
264
|
-
if (isRequiredWithoutDefault) {
|
|
265
|
-
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
266
|
-
}
|
|
267
|
-
else {
|
|
268
|
-
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
return paramResult;
|
|
273
|
-
}
|
|
274
|
-
static checkPostBody(schema, isRequired = false, isCopilot = false) {
|
|
275
|
-
var _a;
|
|
276
|
-
const paramResult = {
|
|
277
|
-
requiredNum: 0,
|
|
278
|
-
optionalNum: 0,
|
|
279
|
-
isValid: true,
|
|
280
|
-
reason: [],
|
|
281
|
-
};
|
|
282
|
-
if (Object.keys(schema).length === 0) {
|
|
283
|
-
return paramResult;
|
|
284
|
-
}
|
|
285
|
-
const isRequiredWithoutDefault = isRequired && schema.default === undefined;
|
|
286
|
-
if (isCopilot && this.hasNestedObjectInSchema(schema)) {
|
|
287
|
-
paramResult.isValid = false;
|
|
288
|
-
paramResult.reason = [ErrorType.RequestBodyContainsNestedObject];
|
|
289
|
-
return paramResult;
|
|
290
|
-
}
|
|
291
|
-
if (schema.type === "string" ||
|
|
292
|
-
schema.type === "integer" ||
|
|
293
|
-
schema.type === "boolean" ||
|
|
294
|
-
schema.type === "number") {
|
|
295
|
-
if (isRequiredWithoutDefault) {
|
|
296
|
-
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
297
|
-
}
|
|
298
|
-
else {
|
|
299
|
-
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
else if (schema.type === "object") {
|
|
303
|
-
const { properties } = schema;
|
|
304
|
-
for (const property in properties) {
|
|
305
|
-
let isRequired = false;
|
|
306
|
-
if (schema.required && ((_a = schema.required) === null || _a === void 0 ? void 0 : _a.indexOf(property)) >= 0) {
|
|
307
|
-
isRequired = true;
|
|
308
|
-
}
|
|
309
|
-
const result = Utils.checkPostBody(properties[property], isRequired, isCopilot);
|
|
310
|
-
paramResult.requiredNum += result.requiredNum;
|
|
311
|
-
paramResult.optionalNum += result.optionalNum;
|
|
312
|
-
paramResult.isValid = paramResult.isValid && result.isValid;
|
|
313
|
-
paramResult.reason.push(...result.reason);
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
else {
|
|
317
|
-
if (isRequiredWithoutDefault && !isCopilot) {
|
|
318
|
-
paramResult.isValid = false;
|
|
319
|
-
paramResult.reason.push(ErrorType.PostBodyContainsRequiredUnsupportedSchema);
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
return paramResult;
|
|
323
|
-
}
|
|
324
219
|
static containMultipleMediaTypes(bodyObject) {
|
|
325
220
|
return Object.keys((bodyObject === null || bodyObject === void 0 ? void 0 : bodyObject.content) || {}).length > 1;
|
|
326
221
|
}
|
|
327
|
-
/**
|
|
328
|
-
* Checks if the given API is supported.
|
|
329
|
-
* @param {string} method - The HTTP method of the API.
|
|
330
|
-
* @param {string} path - The path of the API.
|
|
331
|
-
* @param {OpenAPIV3.Document} spec - The OpenAPI specification document.
|
|
332
|
-
* @returns {boolean} - Returns true if the API is supported, false otherwise.
|
|
333
|
-
* @description The following APIs are supported:
|
|
334
|
-
* 1. only support Get/Post operation without auth property
|
|
335
|
-
* 2. parameter inside query or path only support string, number, boolean and integer
|
|
336
|
-
* 3. parameter inside post body only support string, number, boolean, integer and object
|
|
337
|
-
* 4. request body + required parameters <= 1
|
|
338
|
-
* 5. response body should be “application/json” and not empty, and response code should be 20X
|
|
339
|
-
* 6. only support request body with “application/json” content type
|
|
340
|
-
*/
|
|
341
|
-
static isSupportedApi(method, path, spec, options) {
|
|
342
|
-
var _a;
|
|
343
|
-
const result = { isValid: true, reason: [] };
|
|
344
|
-
method = method.toLocaleLowerCase();
|
|
345
|
-
if (options.allowMethods && !options.allowMethods.includes(method)) {
|
|
346
|
-
result.isValid = false;
|
|
347
|
-
result.reason.push(ErrorType.MethodNotAllowed);
|
|
348
|
-
return result;
|
|
349
|
-
}
|
|
350
|
-
const pathObj = spec.paths[path];
|
|
351
|
-
if (!pathObj || !pathObj[method]) {
|
|
352
|
-
result.isValid = false;
|
|
353
|
-
result.reason.push(ErrorType.UrlPathNotExist);
|
|
354
|
-
return result;
|
|
355
|
-
}
|
|
356
|
-
const securities = pathObj[method].security;
|
|
357
|
-
const isTeamsAi = options.projectType === ProjectType.TeamsAi;
|
|
358
|
-
const isCopilot = options.projectType === ProjectType.Copilot;
|
|
359
|
-
// Teams AI project doesn't care about auth, it will use authProvider for user to implement
|
|
360
|
-
if (!isTeamsAi) {
|
|
361
|
-
const authArray = Utils.getAuthArray(securities, spec);
|
|
362
|
-
const authCheckResult = Utils.isSupportedAuth(authArray, options);
|
|
363
|
-
if (!authCheckResult.isValid) {
|
|
364
|
-
result.reason.push(...authCheckResult.reason);
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
const operationObject = pathObj[method];
|
|
368
|
-
if (!options.allowMissingId && !operationObject.operationId) {
|
|
369
|
-
result.reason.push(ErrorType.MissingOperationId);
|
|
370
|
-
}
|
|
371
|
-
const rootServer = spec.servers && spec.servers[0];
|
|
372
|
-
const methodServer = spec.paths[path].servers && ((_a = spec.paths[path]) === null || _a === void 0 ? void 0 : _a.servers[0]);
|
|
373
|
-
const operationServer = operationObject.servers && operationObject.servers[0];
|
|
374
|
-
const serverUrl = operationServer || methodServer || rootServer;
|
|
375
|
-
if (!serverUrl) {
|
|
376
|
-
result.reason.push(ErrorType.NoServerInformation);
|
|
377
|
-
}
|
|
378
|
-
else {
|
|
379
|
-
const serverValidateResult = Utils.checkServerUrl([serverUrl]);
|
|
380
|
-
result.reason.push(...serverValidateResult.map((item) => item.type));
|
|
381
|
-
}
|
|
382
|
-
const paramObject = operationObject.parameters;
|
|
383
|
-
const requestBody = operationObject.requestBody;
|
|
384
|
-
const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
|
|
385
|
-
if (!isTeamsAi && Utils.containMultipleMediaTypes(requestBody)) {
|
|
386
|
-
result.reason.push(ErrorType.PostBodyContainMultipleMediaTypes);
|
|
387
|
-
}
|
|
388
|
-
const { json, multipleMediaType } = Utils.getResponseJson(operationObject, isTeamsAi);
|
|
389
|
-
if (multipleMediaType && !isTeamsAi) {
|
|
390
|
-
result.reason.push(ErrorType.ResponseContainMultipleMediaTypes);
|
|
391
|
-
}
|
|
392
|
-
else if (Object.keys(json).length === 0) {
|
|
393
|
-
result.reason.push(ErrorType.ResponseJsonIsEmpty);
|
|
394
|
-
}
|
|
395
|
-
// Teams AI project doesn't care about request parameters/body
|
|
396
|
-
if (!isTeamsAi) {
|
|
397
|
-
let requestBodyParamResult = {
|
|
398
|
-
requiredNum: 0,
|
|
399
|
-
optionalNum: 0,
|
|
400
|
-
isValid: true,
|
|
401
|
-
reason: [],
|
|
402
|
-
};
|
|
403
|
-
if (requestJsonBody) {
|
|
404
|
-
const requestBodySchema = requestJsonBody.schema;
|
|
405
|
-
if (isCopilot && requestBodySchema.type !== "object") {
|
|
406
|
-
result.reason.push(ErrorType.PostBodySchemaIsNotJson);
|
|
407
|
-
}
|
|
408
|
-
requestBodyParamResult = Utils.checkPostBody(requestBodySchema, requestBody.required, isCopilot);
|
|
409
|
-
if (!requestBodyParamResult.isValid && requestBodyParamResult.reason) {
|
|
410
|
-
result.reason.push(...requestBodyParamResult.reason);
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
const paramResult = Utils.checkParameters(paramObject, isCopilot);
|
|
414
|
-
if (!paramResult.isValid && paramResult.reason) {
|
|
415
|
-
result.reason.push(...paramResult.reason);
|
|
416
|
-
}
|
|
417
|
-
// Copilot support arbitrary parameters
|
|
418
|
-
if (!isCopilot && paramResult.isValid && requestBodyParamResult.isValid) {
|
|
419
|
-
const totalRequiredParams = requestBodyParamResult.requiredNum + paramResult.requiredNum;
|
|
420
|
-
const totalParams = totalRequiredParams + requestBodyParamResult.optionalNum + paramResult.optionalNum;
|
|
421
|
-
if (totalRequiredParams > 1) {
|
|
422
|
-
if (!options.allowMultipleParameters ||
|
|
423
|
-
totalRequiredParams > ConstantString.SMERequiredParamsMaxNum) {
|
|
424
|
-
result.reason.push(ErrorType.ExceededRequiredParamsLimit);
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
else if (totalParams === 0) {
|
|
428
|
-
result.reason.push(ErrorType.NoParameter);
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
if (result.reason.length > 0) {
|
|
433
|
-
result.isValid = false;
|
|
434
|
-
}
|
|
435
|
-
return result;
|
|
436
|
-
}
|
|
437
|
-
static isSupportedAuth(authSchemeArray, options) {
|
|
438
|
-
if (authSchemeArray.length === 0) {
|
|
439
|
-
return { isValid: true, reason: [] };
|
|
440
|
-
}
|
|
441
|
-
if (options.allowAPIKeyAuth || options.allowOauth2 || options.allowBearerTokenAuth) {
|
|
442
|
-
// Currently we don't support multiple auth in one operation
|
|
443
|
-
if (authSchemeArray.length > 0 && authSchemeArray.every((auths) => auths.length > 1)) {
|
|
444
|
-
return {
|
|
445
|
-
isValid: false,
|
|
446
|
-
reason: [ErrorType.MultipleAuthNotSupported],
|
|
447
|
-
};
|
|
448
|
-
}
|
|
449
|
-
for (const auths of authSchemeArray) {
|
|
450
|
-
if (auths.length === 1) {
|
|
451
|
-
if ((options.allowAPIKeyAuth && Utils.isAPIKeyAuth(auths[0].authScheme)) ||
|
|
452
|
-
(options.allowOauth2 && Utils.isOAuthWithAuthCodeFlow(auths[0].authScheme)) ||
|
|
453
|
-
(options.allowBearerTokenAuth && Utils.isBearerTokenAuth(auths[0].authScheme))) {
|
|
454
|
-
return { isValid: true, reason: [] };
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
|
-
return { isValid: false, reason: [ErrorType.AuthTypeIsNotSupported] };
|
|
460
|
-
}
|
|
461
222
|
static isBearerTokenAuth(authScheme) {
|
|
462
223
|
return authScheme.type === "http" && authScheme.scheme === "bearer";
|
|
463
224
|
}
|
|
@@ -465,10 +226,9 @@ class Utils {
|
|
|
465
226
|
return authScheme.type === "apiKey";
|
|
466
227
|
}
|
|
467
228
|
static isOAuthWithAuthCodeFlow(authScheme) {
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
return false;
|
|
229
|
+
return !!(authScheme.type === "oauth2" &&
|
|
230
|
+
authScheme.flows &&
|
|
231
|
+
authScheme.flows.authorizationCode);
|
|
472
232
|
}
|
|
473
233
|
static getAuthArray(securities, spec) {
|
|
474
234
|
var _a;
|
|
@@ -496,7 +256,7 @@ class Utils {
|
|
|
496
256
|
static updateFirstLetter(str) {
|
|
497
257
|
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
498
258
|
}
|
|
499
|
-
static getResponseJson(operationObject
|
|
259
|
+
static getResponseJson(operationObject) {
|
|
500
260
|
var _a, _b;
|
|
501
261
|
let json = {};
|
|
502
262
|
let multipleMediaType = false;
|
|
@@ -507,9 +267,6 @@ class Utils {
|
|
|
507
267
|
json = responseObject.content["application/json"];
|
|
508
268
|
if (Utils.containMultipleMediaTypes(responseObject)) {
|
|
509
269
|
multipleMediaType = true;
|
|
510
|
-
if (isTeamsAiProject) {
|
|
511
|
-
break;
|
|
512
|
-
}
|
|
513
270
|
json = {};
|
|
514
271
|
}
|
|
515
272
|
else {
|
|
@@ -737,13 +494,7 @@ class Utils {
|
|
|
737
494
|
}
|
|
738
495
|
}
|
|
739
496
|
const operationId = operationItem.operationId;
|
|
740
|
-
const parameters = [];
|
|
741
|
-
if (requiredParams.length !== 0) {
|
|
742
|
-
parameters.push(...requiredParams);
|
|
743
|
-
}
|
|
744
|
-
else {
|
|
745
|
-
parameters.push(optionalParams[0]);
|
|
746
|
-
}
|
|
497
|
+
const parameters = [...requiredParams, ...optionalParams];
|
|
747
498
|
const command = {
|
|
748
499
|
context: ["compose"],
|
|
749
500
|
type: "query",
|
|
@@ -752,26 +503,51 @@ class Utils {
|
|
|
752
503
|
parameters: parameters,
|
|
753
504
|
description: ((_b = operationItem.description) !== null && _b !== void 0 ? _b : "").slice(0, ConstantString.CommandDescriptionMaxLens),
|
|
754
505
|
};
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
506
|
+
return command;
|
|
507
|
+
}
|
|
508
|
+
static format(str, ...args) {
|
|
509
|
+
let index = 0;
|
|
510
|
+
return str.replace(/%s/g, () => {
|
|
511
|
+
const arg = args[index++];
|
|
512
|
+
return arg !== undefined ? arg : "";
|
|
513
|
+
});
|
|
514
|
+
}
|
|
515
|
+
static getSafeRegistrationIdEnvName(authName) {
|
|
516
|
+
if (!authName) {
|
|
517
|
+
return "";
|
|
518
|
+
}
|
|
519
|
+
let safeRegistrationIdEnvName = authName.toUpperCase().replace(/[^A-Z0-9_]/g, "_");
|
|
520
|
+
if (!safeRegistrationIdEnvName.match(/^[A-Z]/)) {
|
|
521
|
+
safeRegistrationIdEnvName = "PREFIX_" + safeRegistrationIdEnvName;
|
|
762
522
|
}
|
|
763
|
-
return
|
|
523
|
+
return safeRegistrationIdEnvName;
|
|
764
524
|
}
|
|
765
|
-
static
|
|
525
|
+
static getServerObject(spec, method, path) {
|
|
526
|
+
const pathObj = spec.paths[path];
|
|
527
|
+
const operationObject = pathObj[method];
|
|
528
|
+
const rootServer = spec.servers && spec.servers[0];
|
|
529
|
+
const methodServer = spec.paths[path].servers && spec.paths[path].servers[0];
|
|
530
|
+
const operationServer = operationObject.servers && operationObject.servers[0];
|
|
531
|
+
const serverUrl = operationServer || methodServer || rootServer;
|
|
532
|
+
return serverUrl;
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
// Copyright (c) Microsoft Corporation.
|
|
537
|
+
class Validator {
|
|
538
|
+
listAPIs() {
|
|
766
539
|
var _a;
|
|
767
|
-
|
|
540
|
+
if (this.apiMap) {
|
|
541
|
+
return this.apiMap;
|
|
542
|
+
}
|
|
543
|
+
const paths = this.spec.paths;
|
|
768
544
|
const result = {};
|
|
769
545
|
for (const path in paths) {
|
|
770
546
|
const methods = paths[path];
|
|
771
547
|
for (const method in methods) {
|
|
772
548
|
const operationObject = methods[method];
|
|
773
|
-
if (((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
|
|
774
|
-
const validateResult =
|
|
549
|
+
if (((_a = this.options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
|
|
550
|
+
const validateResult = this.validateAPI(method, path);
|
|
775
551
|
result[`${method.toUpperCase()} ${path}`] = {
|
|
776
552
|
operation: operationObject,
|
|
777
553
|
isValid: validateResult.isValid,
|
|
@@ -780,38 +556,48 @@ class Utils {
|
|
|
780
556
|
}
|
|
781
557
|
}
|
|
782
558
|
}
|
|
559
|
+
this.apiMap = result;
|
|
783
560
|
return result;
|
|
784
561
|
}
|
|
785
|
-
|
|
786
|
-
const
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
content: ConstantString.ConvertSwaggerToOpenAPI,
|
|
562
|
+
validateSpecVersion() {
|
|
563
|
+
const result = { errors: [], warnings: [] };
|
|
564
|
+
if (this.spec.openapi >= "3.1.0") {
|
|
565
|
+
result.errors.push({
|
|
566
|
+
type: ErrorType.SpecVersionNotSupported,
|
|
567
|
+
content: Utils.format(ConstantString.SpecVersionNotSupported, this.spec.openapi),
|
|
568
|
+
data: this.spec.openapi,
|
|
793
569
|
});
|
|
794
570
|
}
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
const
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
}
|
|
807
|
-
// No supported API
|
|
571
|
+
return result;
|
|
572
|
+
}
|
|
573
|
+
validateSpecServer() {
|
|
574
|
+
const result = { errors: [], warnings: [] };
|
|
575
|
+
const serverErrors = Utils.validateServer(this.spec, this.options);
|
|
576
|
+
result.errors.push(...serverErrors);
|
|
577
|
+
return result;
|
|
578
|
+
}
|
|
579
|
+
validateSpecNoSupportAPI() {
|
|
580
|
+
const result = { errors: [], warnings: [] };
|
|
581
|
+
const apiMap = this.listAPIs();
|
|
808
582
|
const validAPIs = Object.entries(apiMap).filter(([, value]) => value.isValid);
|
|
809
583
|
if (validAPIs.length === 0) {
|
|
810
|
-
|
|
584
|
+
const data = [];
|
|
585
|
+
for (const key in apiMap) {
|
|
586
|
+
const { reason } = apiMap[key];
|
|
587
|
+
const apiInvalidReason = { api: key, reason: reason };
|
|
588
|
+
data.push(apiInvalidReason);
|
|
589
|
+
}
|
|
590
|
+
result.errors.push({
|
|
811
591
|
type: ErrorType.NoSupportedApi,
|
|
812
592
|
content: ConstantString.NoSupportedApi,
|
|
593
|
+
data,
|
|
813
594
|
});
|
|
814
595
|
}
|
|
596
|
+
return result;
|
|
597
|
+
}
|
|
598
|
+
validateSpecOperationId() {
|
|
599
|
+
const result = { errors: [], warnings: [] };
|
|
600
|
+
const apiMap = this.listAPIs();
|
|
815
601
|
// OperationId missing
|
|
816
602
|
const apisMissingOperationId = [];
|
|
817
603
|
for (const key in apiMap) {
|
|
@@ -821,54 +607,431 @@ class Utils {
|
|
|
821
607
|
}
|
|
822
608
|
}
|
|
823
609
|
if (apisMissingOperationId.length > 0) {
|
|
824
|
-
warnings.push({
|
|
610
|
+
result.warnings.push({
|
|
825
611
|
type: WarningType.OperationIdMissing,
|
|
826
612
|
content: Utils.format(ConstantString.MissingOperationId, apisMissingOperationId.join(", ")),
|
|
827
613
|
data: apisMissingOperationId,
|
|
828
614
|
});
|
|
829
615
|
}
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
616
|
+
return result;
|
|
617
|
+
}
|
|
618
|
+
validateMethodAndPath(method, path) {
|
|
619
|
+
const result = { isValid: true, reason: [] };
|
|
620
|
+
if (this.options.allowMethods && !this.options.allowMethods.includes(method)) {
|
|
621
|
+
result.isValid = false;
|
|
622
|
+
result.reason.push(ErrorType.MethodNotAllowed);
|
|
623
|
+
return result;
|
|
833
624
|
}
|
|
834
|
-
|
|
835
|
-
|
|
625
|
+
const pathObj = this.spec.paths[path];
|
|
626
|
+
if (!pathObj || !pathObj[method]) {
|
|
627
|
+
result.isValid = false;
|
|
628
|
+
result.reason.push(ErrorType.UrlPathNotExist);
|
|
629
|
+
return result;
|
|
836
630
|
}
|
|
837
|
-
return
|
|
838
|
-
status,
|
|
839
|
-
warnings,
|
|
840
|
-
errors,
|
|
841
|
-
};
|
|
631
|
+
return result;
|
|
842
632
|
}
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
633
|
+
validateResponse(method, path) {
|
|
634
|
+
const result = { isValid: true, reason: [] };
|
|
635
|
+
const operationObject = this.spec.paths[path][method];
|
|
636
|
+
const { json, multipleMediaType } = Utils.getResponseJson(operationObject);
|
|
637
|
+
// only support response body only contains “application/json” content type
|
|
638
|
+
if (multipleMediaType) {
|
|
639
|
+
result.reason.push(ErrorType.ResponseContainMultipleMediaTypes);
|
|
640
|
+
}
|
|
641
|
+
else if (Object.keys(json).length === 0) {
|
|
642
|
+
// response body should not be empty
|
|
643
|
+
result.reason.push(ErrorType.ResponseJsonIsEmpty);
|
|
644
|
+
}
|
|
645
|
+
return result;
|
|
849
646
|
}
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
647
|
+
validateServer(method, path) {
|
|
648
|
+
const result = { isValid: true, reason: [] };
|
|
649
|
+
const serverObj = Utils.getServerObject(this.spec, method, path);
|
|
650
|
+
if (!serverObj) {
|
|
651
|
+
// should contain server URL
|
|
652
|
+
result.reason.push(ErrorType.NoServerInformation);
|
|
853
653
|
}
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
654
|
+
else {
|
|
655
|
+
// server url should be absolute url with https protocol
|
|
656
|
+
const serverValidateResult = Utils.checkServerUrl([serverObj]);
|
|
657
|
+
result.reason.push(...serverValidateResult.map((item) => item.type));
|
|
857
658
|
}
|
|
858
|
-
return
|
|
659
|
+
return result;
|
|
859
660
|
}
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
const
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
661
|
+
validateAuth(method, path) {
|
|
662
|
+
const pathObj = this.spec.paths[path];
|
|
663
|
+
const operationObject = pathObj[method];
|
|
664
|
+
const securities = operationObject.security;
|
|
665
|
+
const authSchemeArray = Utils.getAuthArray(securities, this.spec);
|
|
666
|
+
if (authSchemeArray.length === 0) {
|
|
667
|
+
return { isValid: true, reason: [] };
|
|
668
|
+
}
|
|
669
|
+
if (this.options.allowAPIKeyAuth ||
|
|
670
|
+
this.options.allowOauth2 ||
|
|
671
|
+
this.options.allowBearerTokenAuth) {
|
|
672
|
+
// Currently we don't support multiple auth in one operation
|
|
673
|
+
if (authSchemeArray.length > 0 && authSchemeArray.every((auths) => auths.length > 1)) {
|
|
674
|
+
return {
|
|
675
|
+
isValid: false,
|
|
676
|
+
reason: [ErrorType.MultipleAuthNotSupported],
|
|
677
|
+
};
|
|
678
|
+
}
|
|
679
|
+
for (const auths of authSchemeArray) {
|
|
680
|
+
if (auths.length === 1) {
|
|
681
|
+
if ((this.options.allowAPIKeyAuth && Utils.isAPIKeyAuth(auths[0].authScheme)) ||
|
|
682
|
+
(this.options.allowOauth2 && Utils.isOAuthWithAuthCodeFlow(auths[0].authScheme)) ||
|
|
683
|
+
(this.options.allowBearerTokenAuth && Utils.isBearerTokenAuth(auths[0].authScheme))) {
|
|
684
|
+
return { isValid: true, reason: [] };
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
return { isValid: false, reason: [ErrorType.AuthTypeIsNotSupported] };
|
|
690
|
+
}
|
|
691
|
+
checkPostBodySchema(schema, isRequired = false) {
|
|
692
|
+
var _a;
|
|
693
|
+
const paramResult = {
|
|
694
|
+
requiredNum: 0,
|
|
695
|
+
optionalNum: 0,
|
|
696
|
+
isValid: true,
|
|
697
|
+
reason: [],
|
|
698
|
+
};
|
|
699
|
+
if (Object.keys(schema).length === 0) {
|
|
700
|
+
return paramResult;
|
|
701
|
+
}
|
|
702
|
+
const isRequiredWithoutDefault = isRequired && schema.default === undefined;
|
|
703
|
+
const isCopilot = this.projectType === ProjectType.Copilot;
|
|
704
|
+
if (isCopilot && this.hasNestedObjectInSchema(schema)) {
|
|
705
|
+
paramResult.isValid = false;
|
|
706
|
+
paramResult.reason = [ErrorType.RequestBodyContainsNestedObject];
|
|
707
|
+
return paramResult;
|
|
708
|
+
}
|
|
709
|
+
if (schema.type === "string" ||
|
|
710
|
+
schema.type === "integer" ||
|
|
711
|
+
schema.type === "boolean" ||
|
|
712
|
+
schema.type === "number") {
|
|
713
|
+
if (isRequiredWithoutDefault) {
|
|
714
|
+
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
715
|
+
}
|
|
716
|
+
else {
|
|
717
|
+
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
else if (schema.type === "object") {
|
|
721
|
+
const { properties } = schema;
|
|
722
|
+
for (const property in properties) {
|
|
723
|
+
let isRequired = false;
|
|
724
|
+
if (schema.required && ((_a = schema.required) === null || _a === void 0 ? void 0 : _a.indexOf(property)) >= 0) {
|
|
725
|
+
isRequired = true;
|
|
868
726
|
}
|
|
727
|
+
const result = this.checkPostBodySchema(properties[property], isRequired);
|
|
728
|
+
paramResult.requiredNum += result.requiredNum;
|
|
729
|
+
paramResult.optionalNum += result.optionalNum;
|
|
730
|
+
paramResult.isValid = paramResult.isValid && result.isValid;
|
|
731
|
+
paramResult.reason.push(...result.reason);
|
|
869
732
|
}
|
|
870
733
|
}
|
|
871
|
-
|
|
734
|
+
else {
|
|
735
|
+
if (isRequiredWithoutDefault && !isCopilot) {
|
|
736
|
+
paramResult.isValid = false;
|
|
737
|
+
paramResult.reason.push(ErrorType.PostBodyContainsRequiredUnsupportedSchema);
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
return paramResult;
|
|
741
|
+
}
|
|
742
|
+
checkParamSchema(paramObject) {
|
|
743
|
+
const paramResult = {
|
|
744
|
+
requiredNum: 0,
|
|
745
|
+
optionalNum: 0,
|
|
746
|
+
isValid: true,
|
|
747
|
+
reason: [],
|
|
748
|
+
};
|
|
749
|
+
if (!paramObject) {
|
|
750
|
+
return paramResult;
|
|
751
|
+
}
|
|
752
|
+
const isCopilot = this.projectType === ProjectType.Copilot;
|
|
753
|
+
for (let i = 0; i < paramObject.length; i++) {
|
|
754
|
+
const param = paramObject[i];
|
|
755
|
+
const schema = param.schema;
|
|
756
|
+
if (isCopilot && this.hasNestedObjectInSchema(schema)) {
|
|
757
|
+
paramResult.isValid = false;
|
|
758
|
+
paramResult.reason.push(ErrorType.ParamsContainsNestedObject);
|
|
759
|
+
continue;
|
|
760
|
+
}
|
|
761
|
+
const isRequiredWithoutDefault = param.required && schema.default === undefined;
|
|
762
|
+
if (isCopilot) {
|
|
763
|
+
if (isRequiredWithoutDefault) {
|
|
764
|
+
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
765
|
+
}
|
|
766
|
+
else {
|
|
767
|
+
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
768
|
+
}
|
|
769
|
+
continue;
|
|
770
|
+
}
|
|
771
|
+
if (param.in === "header" || param.in === "cookie") {
|
|
772
|
+
if (isRequiredWithoutDefault) {
|
|
773
|
+
paramResult.isValid = false;
|
|
774
|
+
paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
|
|
775
|
+
}
|
|
776
|
+
continue;
|
|
777
|
+
}
|
|
778
|
+
if (schema.type !== "boolean" &&
|
|
779
|
+
schema.type !== "string" &&
|
|
780
|
+
schema.type !== "number" &&
|
|
781
|
+
schema.type !== "integer") {
|
|
782
|
+
if (isRequiredWithoutDefault) {
|
|
783
|
+
paramResult.isValid = false;
|
|
784
|
+
paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
|
|
785
|
+
}
|
|
786
|
+
continue;
|
|
787
|
+
}
|
|
788
|
+
if (param.in === "query" || param.in === "path") {
|
|
789
|
+
if (isRequiredWithoutDefault) {
|
|
790
|
+
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
791
|
+
}
|
|
792
|
+
else {
|
|
793
|
+
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
return paramResult;
|
|
798
|
+
}
|
|
799
|
+
hasNestedObjectInSchema(schema) {
|
|
800
|
+
if (schema.type === "object") {
|
|
801
|
+
for (const property in schema.properties) {
|
|
802
|
+
const nestedSchema = schema.properties[property];
|
|
803
|
+
if (nestedSchema.type === "object") {
|
|
804
|
+
return true;
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
return false;
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
// Copyright (c) Microsoft Corporation.
|
|
813
|
+
class CopilotValidator extends Validator {
|
|
814
|
+
constructor(spec, options) {
|
|
815
|
+
super();
|
|
816
|
+
this.projectType = ProjectType.Copilot;
|
|
817
|
+
this.options = options;
|
|
818
|
+
this.spec = spec;
|
|
819
|
+
}
|
|
820
|
+
validateSpec() {
|
|
821
|
+
const result = { errors: [], warnings: [] };
|
|
822
|
+
// validate spec version
|
|
823
|
+
let validationResult = this.validateSpecVersion();
|
|
824
|
+
result.errors.push(...validationResult.errors);
|
|
825
|
+
// validate spec server
|
|
826
|
+
validationResult = this.validateSpecServer();
|
|
827
|
+
result.errors.push(...validationResult.errors);
|
|
828
|
+
// validate no supported API
|
|
829
|
+
validationResult = this.validateSpecNoSupportAPI();
|
|
830
|
+
result.errors.push(...validationResult.errors);
|
|
831
|
+
// validate operationId missing
|
|
832
|
+
validationResult = this.validateSpecOperationId();
|
|
833
|
+
result.warnings.push(...validationResult.warnings);
|
|
834
|
+
return result;
|
|
835
|
+
}
|
|
836
|
+
validateAPI(method, path) {
|
|
837
|
+
const result = { isValid: true, reason: [] };
|
|
838
|
+
method = method.toLocaleLowerCase();
|
|
839
|
+
// validate method and path
|
|
840
|
+
const methodAndPathResult = this.validateMethodAndPath(method, path);
|
|
841
|
+
if (!methodAndPathResult.isValid) {
|
|
842
|
+
return methodAndPathResult;
|
|
843
|
+
}
|
|
844
|
+
const operationObject = this.spec.paths[path][method];
|
|
845
|
+
// validate auth
|
|
846
|
+
const authCheckResult = this.validateAuth(method, path);
|
|
847
|
+
result.reason.push(...authCheckResult.reason);
|
|
848
|
+
// validate operationId
|
|
849
|
+
if (!this.options.allowMissingId && !operationObject.operationId) {
|
|
850
|
+
result.reason.push(ErrorType.MissingOperationId);
|
|
851
|
+
}
|
|
852
|
+
// validate server
|
|
853
|
+
const validateServerResult = this.validateServer(method, path);
|
|
854
|
+
result.reason.push(...validateServerResult.reason);
|
|
855
|
+
// validate response
|
|
856
|
+
const validateResponseResult = this.validateResponse(method, path);
|
|
857
|
+
result.reason.push(...validateResponseResult.reason);
|
|
858
|
+
// validate requestBody
|
|
859
|
+
const requestBody = operationObject.requestBody;
|
|
860
|
+
const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
|
|
861
|
+
if (Utils.containMultipleMediaTypes(requestBody)) {
|
|
862
|
+
result.reason.push(ErrorType.PostBodyContainMultipleMediaTypes);
|
|
863
|
+
}
|
|
864
|
+
if (requestJsonBody) {
|
|
865
|
+
const requestBodySchema = requestJsonBody.schema;
|
|
866
|
+
if (requestBodySchema.type !== "object") {
|
|
867
|
+
result.reason.push(ErrorType.PostBodySchemaIsNotJson);
|
|
868
|
+
}
|
|
869
|
+
const requestBodyParamResult = this.checkPostBodySchema(requestBodySchema, requestBody.required);
|
|
870
|
+
result.reason.push(...requestBodyParamResult.reason);
|
|
871
|
+
}
|
|
872
|
+
// validate parameters
|
|
873
|
+
const paramObject = operationObject.parameters;
|
|
874
|
+
const paramResult = this.checkParamSchema(paramObject);
|
|
875
|
+
result.reason.push(...paramResult.reason);
|
|
876
|
+
if (result.reason.length > 0) {
|
|
877
|
+
result.isValid = false;
|
|
878
|
+
}
|
|
879
|
+
return result;
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
// Copyright (c) Microsoft Corporation.
|
|
884
|
+
class SMEValidator extends Validator {
|
|
885
|
+
constructor(spec, options) {
|
|
886
|
+
super();
|
|
887
|
+
this.projectType = ProjectType.SME;
|
|
888
|
+
this.options = options;
|
|
889
|
+
this.spec = spec;
|
|
890
|
+
}
|
|
891
|
+
validateSpec() {
|
|
892
|
+
const result = { errors: [], warnings: [] };
|
|
893
|
+
// validate spec version
|
|
894
|
+
let validationResult = this.validateSpecVersion();
|
|
895
|
+
result.errors.push(...validationResult.errors);
|
|
896
|
+
// validate spec server
|
|
897
|
+
validationResult = this.validateSpecServer();
|
|
898
|
+
result.errors.push(...validationResult.errors);
|
|
899
|
+
// validate no supported API
|
|
900
|
+
validationResult = this.validateSpecNoSupportAPI();
|
|
901
|
+
result.errors.push(...validationResult.errors);
|
|
902
|
+
// validate operationId missing
|
|
903
|
+
if (this.options.allowMissingId) {
|
|
904
|
+
validationResult = this.validateSpecOperationId();
|
|
905
|
+
result.warnings.push(...validationResult.warnings);
|
|
906
|
+
}
|
|
907
|
+
return result;
|
|
908
|
+
}
|
|
909
|
+
validateAPI(method, path) {
|
|
910
|
+
const result = { isValid: true, reason: [] };
|
|
911
|
+
method = method.toLocaleLowerCase();
|
|
912
|
+
// validate method and path
|
|
913
|
+
const methodAndPathResult = this.validateMethodAndPath(method, path);
|
|
914
|
+
if (!methodAndPathResult.isValid) {
|
|
915
|
+
return methodAndPathResult;
|
|
916
|
+
}
|
|
917
|
+
const operationObject = this.spec.paths[path][method];
|
|
918
|
+
// validate auth
|
|
919
|
+
const authCheckResult = this.validateAuth(method, path);
|
|
920
|
+
result.reason.push(...authCheckResult.reason);
|
|
921
|
+
// validate operationId
|
|
922
|
+
if (!this.options.allowMissingId && !operationObject.operationId) {
|
|
923
|
+
result.reason.push(ErrorType.MissingOperationId);
|
|
924
|
+
}
|
|
925
|
+
// validate server
|
|
926
|
+
const validateServerResult = this.validateServer(method, path);
|
|
927
|
+
result.reason.push(...validateServerResult.reason);
|
|
928
|
+
// validate response
|
|
929
|
+
const validateResponseResult = this.validateResponse(method, path);
|
|
930
|
+
result.reason.push(...validateResponseResult.reason);
|
|
931
|
+
let postBodyResult = {
|
|
932
|
+
requiredNum: 0,
|
|
933
|
+
optionalNum: 0,
|
|
934
|
+
isValid: true,
|
|
935
|
+
reason: [],
|
|
936
|
+
};
|
|
937
|
+
// validate requestBody
|
|
938
|
+
const requestBody = operationObject.requestBody;
|
|
939
|
+
const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
|
|
940
|
+
if (Utils.containMultipleMediaTypes(requestBody)) {
|
|
941
|
+
result.reason.push(ErrorType.PostBodyContainMultipleMediaTypes);
|
|
942
|
+
}
|
|
943
|
+
if (requestJsonBody) {
|
|
944
|
+
const requestBodySchema = requestJsonBody.schema;
|
|
945
|
+
postBodyResult = this.checkPostBodySchema(requestBodySchema, requestBody.required);
|
|
946
|
+
result.reason.push(...postBodyResult.reason);
|
|
947
|
+
}
|
|
948
|
+
// validate parameters
|
|
949
|
+
const paramObject = operationObject.parameters;
|
|
950
|
+
const paramResult = this.checkParamSchema(paramObject);
|
|
951
|
+
result.reason.push(...paramResult.reason);
|
|
952
|
+
// validate total parameters count
|
|
953
|
+
if (paramResult.isValid && postBodyResult.isValid) {
|
|
954
|
+
const paramCountResult = this.validateParamCount(postBodyResult, paramResult);
|
|
955
|
+
result.reason.push(...paramCountResult.reason);
|
|
956
|
+
}
|
|
957
|
+
if (result.reason.length > 0) {
|
|
958
|
+
result.isValid = false;
|
|
959
|
+
}
|
|
960
|
+
return result;
|
|
961
|
+
}
|
|
962
|
+
validateParamCount(postBodyResult, paramResult) {
|
|
963
|
+
const result = { isValid: true, reason: [] };
|
|
964
|
+
const totalRequiredParams = postBodyResult.requiredNum + paramResult.requiredNum;
|
|
965
|
+
const totalParams = totalRequiredParams + postBodyResult.optionalNum + paramResult.optionalNum;
|
|
966
|
+
if (totalRequiredParams > 1) {
|
|
967
|
+
if (!this.options.allowMultipleParameters ||
|
|
968
|
+
totalRequiredParams > SMEValidator.SMERequiredParamsMaxNum) {
|
|
969
|
+
result.reason.push(ErrorType.ExceededRequiredParamsLimit);
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
else if (totalParams === 0) {
|
|
973
|
+
result.reason.push(ErrorType.NoParameter);
|
|
974
|
+
}
|
|
975
|
+
return result;
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
SMEValidator.SMERequiredParamsMaxNum = 5;
|
|
979
|
+
|
|
980
|
+
// Copyright (c) Microsoft Corporation.
|
|
981
|
+
class TeamsAIValidator extends Validator {
|
|
982
|
+
constructor(spec, options) {
|
|
983
|
+
super();
|
|
984
|
+
this.projectType = ProjectType.TeamsAi;
|
|
985
|
+
this.options = options;
|
|
986
|
+
this.spec = spec;
|
|
987
|
+
}
|
|
988
|
+
validateSpec() {
|
|
989
|
+
const result = { errors: [], warnings: [] };
|
|
990
|
+
// validate spec server
|
|
991
|
+
let validationResult = this.validateSpecServer();
|
|
992
|
+
result.errors.push(...validationResult.errors);
|
|
993
|
+
// validate no supported API
|
|
994
|
+
validationResult = this.validateSpecNoSupportAPI();
|
|
995
|
+
result.errors.push(...validationResult.errors);
|
|
996
|
+
return result;
|
|
997
|
+
}
|
|
998
|
+
validateAPI(method, path) {
|
|
999
|
+
const result = { isValid: true, reason: [] };
|
|
1000
|
+
method = method.toLocaleLowerCase();
|
|
1001
|
+
// validate method and path
|
|
1002
|
+
const methodAndPathResult = this.validateMethodAndPath(method, path);
|
|
1003
|
+
if (!methodAndPathResult.isValid) {
|
|
1004
|
+
return methodAndPathResult;
|
|
1005
|
+
}
|
|
1006
|
+
const operationObject = this.spec.paths[path][method];
|
|
1007
|
+
// validate operationId
|
|
1008
|
+
if (!this.options.allowMissingId && !operationObject.operationId) {
|
|
1009
|
+
result.reason.push(ErrorType.MissingOperationId);
|
|
1010
|
+
}
|
|
1011
|
+
// validate server
|
|
1012
|
+
const validateServerResult = this.validateServer(method, path);
|
|
1013
|
+
result.reason.push(...validateServerResult.reason);
|
|
1014
|
+
if (result.reason.length > 0) {
|
|
1015
|
+
result.isValid = false;
|
|
1016
|
+
}
|
|
1017
|
+
return result;
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
class ValidatorFactory {
|
|
1022
|
+
static create(spec, options) {
|
|
1023
|
+
var _a;
|
|
1024
|
+
const type = (_a = options.projectType) !== null && _a !== void 0 ? _a : ProjectType.SME;
|
|
1025
|
+
switch (type) {
|
|
1026
|
+
case ProjectType.SME:
|
|
1027
|
+
return new SMEValidator(spec, options);
|
|
1028
|
+
case ProjectType.Copilot:
|
|
1029
|
+
return new CopilotValidator(spec, options);
|
|
1030
|
+
case ProjectType.TeamsAi:
|
|
1031
|
+
return new TeamsAIValidator(spec, options);
|
|
1032
|
+
default:
|
|
1033
|
+
throw new Error(`Invalid project type: ${type}`);
|
|
1034
|
+
}
|
|
872
1035
|
}
|
|
873
1036
|
}
|
|
874
1037
|
|
|
@@ -907,11 +1070,7 @@ class SpecParser {
|
|
|
907
1070
|
try {
|
|
908
1071
|
try {
|
|
909
1072
|
yield this.loadSpec();
|
|
910
|
-
yield this.parser.validate(this.spec
|
|
911
|
-
validate: {
|
|
912
|
-
schema: false,
|
|
913
|
-
},
|
|
914
|
-
});
|
|
1073
|
+
yield this.parser.validate(this.spec);
|
|
915
1074
|
}
|
|
916
1075
|
catch (e) {
|
|
917
1076
|
return {
|
|
@@ -920,16 +1079,46 @@ class SpecParser {
|
|
|
920
1079
|
errors: [{ type: ErrorType.SpecNotValid, content: e.toString() }],
|
|
921
1080
|
};
|
|
922
1081
|
}
|
|
1082
|
+
const errors = [];
|
|
1083
|
+
const warnings = [];
|
|
923
1084
|
if (!this.options.allowSwagger && this.isSwaggerFile) {
|
|
924
1085
|
return {
|
|
925
1086
|
status: ValidationStatus.Error,
|
|
926
1087
|
warnings: [],
|
|
927
1088
|
errors: [
|
|
928
|
-
{
|
|
1089
|
+
{
|
|
1090
|
+
type: ErrorType.SwaggerNotSupported,
|
|
1091
|
+
content: ConstantString.SwaggerNotSupported,
|
|
1092
|
+
},
|
|
929
1093
|
],
|
|
930
1094
|
};
|
|
931
1095
|
}
|
|
932
|
-
|
|
1096
|
+
// Remote reference not supported
|
|
1097
|
+
const refPaths = this.parser.$refs.paths();
|
|
1098
|
+
// refPaths [0] is the current spec file path
|
|
1099
|
+
if (refPaths.length > 1) {
|
|
1100
|
+
errors.push({
|
|
1101
|
+
type: ErrorType.RemoteRefNotSupported,
|
|
1102
|
+
content: Utils.format(ConstantString.RemoteRefNotSupported, refPaths.join(", ")),
|
|
1103
|
+
data: refPaths,
|
|
1104
|
+
});
|
|
1105
|
+
}
|
|
1106
|
+
const validator = this.getValidator(this.spec);
|
|
1107
|
+
const validationResult = validator.validateSpec();
|
|
1108
|
+
warnings.push(...validationResult.warnings);
|
|
1109
|
+
errors.push(...validationResult.errors);
|
|
1110
|
+
let status = ValidationStatus.Valid;
|
|
1111
|
+
if (warnings.length > 0 && errors.length === 0) {
|
|
1112
|
+
status = ValidationStatus.Warning;
|
|
1113
|
+
}
|
|
1114
|
+
else if (errors.length > 0) {
|
|
1115
|
+
status = ValidationStatus.Error;
|
|
1116
|
+
}
|
|
1117
|
+
return {
|
|
1118
|
+
status: status,
|
|
1119
|
+
warnings: warnings,
|
|
1120
|
+
errors: errors,
|
|
1121
|
+
};
|
|
933
1122
|
}
|
|
934
1123
|
catch (err) {
|
|
935
1124
|
throw new SpecParserError(err.toString(), ErrorType.ValidateFailed);
|
|
@@ -940,17 +1129,20 @@ class SpecParser {
|
|
|
940
1129
|
return __awaiter(this, void 0, void 0, function* () {
|
|
941
1130
|
try {
|
|
942
1131
|
yield this.loadSpec();
|
|
943
|
-
const apiMap = this.
|
|
1132
|
+
const apiMap = this.getAPIs(this.spec);
|
|
944
1133
|
const apiInfos = [];
|
|
945
1134
|
for (const key in apiMap) {
|
|
946
|
-
const
|
|
1135
|
+
const { operation, isValid } = apiMap[key];
|
|
1136
|
+
if (!isValid) {
|
|
1137
|
+
continue;
|
|
1138
|
+
}
|
|
947
1139
|
const [method, path] = key.split(" ");
|
|
948
|
-
const operationId =
|
|
1140
|
+
const operationId = operation.operationId;
|
|
949
1141
|
// In Browser environment, this api is by default not support api without operationId
|
|
950
1142
|
if (!operationId) {
|
|
951
1143
|
continue;
|
|
952
1144
|
}
|
|
953
|
-
const
|
|
1145
|
+
const command = Utils.parseApiInfo(operation, this.options);
|
|
954
1146
|
const apiInfo = {
|
|
955
1147
|
method: method,
|
|
956
1148
|
path: path,
|
|
@@ -959,9 +1151,6 @@ class SpecParser {
|
|
|
959
1151
|
parameters: command.parameters,
|
|
960
1152
|
description: command.description,
|
|
961
1153
|
};
|
|
962
|
-
if (warning) {
|
|
963
|
-
apiInfo.warning = warning;
|
|
964
|
-
}
|
|
965
1154
|
apiInfos.push(apiInfo);
|
|
966
1155
|
}
|
|
967
1156
|
return apiInfos;
|
|
@@ -1031,31 +1220,22 @@ class SpecParser {
|
|
|
1031
1220
|
}
|
|
1032
1221
|
});
|
|
1033
1222
|
}
|
|
1034
|
-
|
|
1223
|
+
getAPIs(spec) {
|
|
1035
1224
|
if (this.apiMap !== undefined) {
|
|
1036
1225
|
return this.apiMap;
|
|
1037
1226
|
}
|
|
1038
|
-
const
|
|
1039
|
-
|
|
1040
|
-
|
|
1227
|
+
const validator = this.getValidator(spec);
|
|
1228
|
+
const apiMap = validator.listAPIs();
|
|
1229
|
+
this.apiMap = apiMap;
|
|
1230
|
+
return apiMap;
|
|
1041
1231
|
}
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
const result = {};
|
|
1046
|
-
for (const path in paths) {
|
|
1047
|
-
const methods = paths[path];
|
|
1048
|
-
for (const method in methods) {
|
|
1049
|
-
const operationObject = methods[method];
|
|
1050
|
-
if (((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
|
|
1051
|
-
const validateResult = Utils.isSupportedApi(method, path, spec, options);
|
|
1052
|
-
if (validateResult.isValid) {
|
|
1053
|
-
result[`${method.toUpperCase()} ${path}`] = operationObject;
|
|
1054
|
-
}
|
|
1055
|
-
}
|
|
1056
|
-
}
|
|
1232
|
+
getValidator(spec) {
|
|
1233
|
+
if (this.validator) {
|
|
1234
|
+
return this.validator;
|
|
1057
1235
|
}
|
|
1058
|
-
|
|
1236
|
+
const validator = ValidatorFactory.create(spec, this.options);
|
|
1237
|
+
this.validator = validator;
|
|
1238
|
+
return validator;
|
|
1059
1239
|
}
|
|
1060
1240
|
}
|
|
1061
1241
|
|