@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.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 {
|
|
@@ -748,16 +505,49 @@ class Utils {
|
|
|
748
505
|
};
|
|
749
506
|
return command;
|
|
750
507
|
}
|
|
751
|
-
static
|
|
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;
|
|
522
|
+
}
|
|
523
|
+
return safeRegistrationIdEnvName;
|
|
524
|
+
}
|
|
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() {
|
|
752
539
|
var _a;
|
|
753
|
-
|
|
540
|
+
if (this.apiMap) {
|
|
541
|
+
return this.apiMap;
|
|
542
|
+
}
|
|
543
|
+
const paths = this.spec.paths;
|
|
754
544
|
const result = {};
|
|
755
545
|
for (const path in paths) {
|
|
756
546
|
const methods = paths[path];
|
|
757
547
|
for (const method in methods) {
|
|
758
548
|
const operationObject = methods[method];
|
|
759
|
-
if (((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
|
|
760
|
-
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);
|
|
761
551
|
result[`${method.toUpperCase()} ${path}`] = {
|
|
762
552
|
operation: operationObject,
|
|
763
553
|
isValid: validateResult.isValid,
|
|
@@ -766,38 +556,48 @@ class Utils {
|
|
|
766
556
|
}
|
|
767
557
|
}
|
|
768
558
|
}
|
|
559
|
+
this.apiMap = result;
|
|
769
560
|
return result;
|
|
770
561
|
}
|
|
771
|
-
|
|
772
|
-
const
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
content: ConstantString.ConvertSwaggerToOpenAPI,
|
|
779
|
-
});
|
|
780
|
-
}
|
|
781
|
-
const serverErrors = Utils.validateServer(spec, options);
|
|
782
|
-
errors.push(...serverErrors);
|
|
783
|
-
// Remote reference not supported
|
|
784
|
-
const refPaths = parser.$refs.paths();
|
|
785
|
-
// refPaths [0] is the current spec file path
|
|
786
|
-
if (refPaths.length > 1) {
|
|
787
|
-
errors.push({
|
|
788
|
-
type: ErrorType.RemoteRefNotSupported,
|
|
789
|
-
content: Utils.format(ConstantString.RemoteRefNotSupported, refPaths.join(", ")),
|
|
790
|
-
data: refPaths,
|
|
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,
|
|
791
569
|
});
|
|
792
570
|
}
|
|
793
|
-
|
|
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();
|
|
794
582
|
const validAPIs = Object.entries(apiMap).filter(([, value]) => value.isValid);
|
|
795
583
|
if (validAPIs.length === 0) {
|
|
796
|
-
|
|
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({
|
|
797
591
|
type: ErrorType.NoSupportedApi,
|
|
798
592
|
content: ConstantString.NoSupportedApi,
|
|
593
|
+
data,
|
|
799
594
|
});
|
|
800
595
|
}
|
|
596
|
+
return result;
|
|
597
|
+
}
|
|
598
|
+
validateSpecOperationId() {
|
|
599
|
+
const result = { errors: [], warnings: [] };
|
|
600
|
+
const apiMap = this.listAPIs();
|
|
801
601
|
// OperationId missing
|
|
802
602
|
const apisMissingOperationId = [];
|
|
803
603
|
for (const key in apiMap) {
|
|
@@ -807,54 +607,431 @@ class Utils {
|
|
|
807
607
|
}
|
|
808
608
|
}
|
|
809
609
|
if (apisMissingOperationId.length > 0) {
|
|
810
|
-
warnings.push({
|
|
610
|
+
result.warnings.push({
|
|
811
611
|
type: WarningType.OperationIdMissing,
|
|
812
612
|
content: Utils.format(ConstantString.MissingOperationId, apisMissingOperationId.join(", ")),
|
|
813
613
|
data: apisMissingOperationId,
|
|
814
614
|
});
|
|
815
615
|
}
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
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;
|
|
819
624
|
}
|
|
820
|
-
|
|
821
|
-
|
|
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;
|
|
822
630
|
}
|
|
823
|
-
return
|
|
824
|
-
status,
|
|
825
|
-
warnings,
|
|
826
|
-
errors,
|
|
827
|
-
};
|
|
631
|
+
return result;
|
|
828
632
|
}
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
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;
|
|
835
646
|
}
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
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);
|
|
839
653
|
}
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
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));
|
|
843
658
|
}
|
|
844
|
-
return
|
|
659
|
+
return result;
|
|
845
660
|
}
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
const
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
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
|
+
}
|
|
854
686
|
}
|
|
855
687
|
}
|
|
856
688
|
}
|
|
857
|
-
return
|
|
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;
|
|
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);
|
|
732
|
+
}
|
|
733
|
+
}
|
|
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
|
+
}
|
|
858
1035
|
}
|
|
859
1036
|
}
|
|
860
1037
|
|
|
@@ -893,11 +1070,7 @@ class SpecParser {
|
|
|
893
1070
|
try {
|
|
894
1071
|
try {
|
|
895
1072
|
yield this.loadSpec();
|
|
896
|
-
yield this.parser.validate(this.spec
|
|
897
|
-
validate: {
|
|
898
|
-
schema: false,
|
|
899
|
-
},
|
|
900
|
-
});
|
|
1073
|
+
yield this.parser.validate(this.spec);
|
|
901
1074
|
}
|
|
902
1075
|
catch (e) {
|
|
903
1076
|
return {
|
|
@@ -906,16 +1079,46 @@ class SpecParser {
|
|
|
906
1079
|
errors: [{ type: ErrorType.SpecNotValid, content: e.toString() }],
|
|
907
1080
|
};
|
|
908
1081
|
}
|
|
1082
|
+
const errors = [];
|
|
1083
|
+
const warnings = [];
|
|
909
1084
|
if (!this.options.allowSwagger && this.isSwaggerFile) {
|
|
910
1085
|
return {
|
|
911
1086
|
status: ValidationStatus.Error,
|
|
912
1087
|
warnings: [],
|
|
913
1088
|
errors: [
|
|
914
|
-
{
|
|
1089
|
+
{
|
|
1090
|
+
type: ErrorType.SwaggerNotSupported,
|
|
1091
|
+
content: ConstantString.SwaggerNotSupported,
|
|
1092
|
+
},
|
|
915
1093
|
],
|
|
916
1094
|
};
|
|
917
1095
|
}
|
|
918
|
-
|
|
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
|
+
};
|
|
919
1122
|
}
|
|
920
1123
|
catch (err) {
|
|
921
1124
|
throw new SpecParserError(err.toString(), ErrorType.ValidateFailed);
|
|
@@ -926,17 +1129,20 @@ class SpecParser {
|
|
|
926
1129
|
return __awaiter(this, void 0, void 0, function* () {
|
|
927
1130
|
try {
|
|
928
1131
|
yield this.loadSpec();
|
|
929
|
-
const apiMap = this.
|
|
1132
|
+
const apiMap = this.getAPIs(this.spec);
|
|
930
1133
|
const apiInfos = [];
|
|
931
1134
|
for (const key in apiMap) {
|
|
932
|
-
const
|
|
1135
|
+
const { operation, isValid } = apiMap[key];
|
|
1136
|
+
if (!isValid) {
|
|
1137
|
+
continue;
|
|
1138
|
+
}
|
|
933
1139
|
const [method, path] = key.split(" ");
|
|
934
|
-
const operationId =
|
|
1140
|
+
const operationId = operation.operationId;
|
|
935
1141
|
// In Browser environment, this api is by default not support api without operationId
|
|
936
1142
|
if (!operationId) {
|
|
937
1143
|
continue;
|
|
938
1144
|
}
|
|
939
|
-
const command = Utils.parseApiInfo(
|
|
1145
|
+
const command = Utils.parseApiInfo(operation, this.options);
|
|
940
1146
|
const apiInfo = {
|
|
941
1147
|
method: method,
|
|
942
1148
|
path: path,
|
|
@@ -1014,31 +1220,22 @@ class SpecParser {
|
|
|
1014
1220
|
}
|
|
1015
1221
|
});
|
|
1016
1222
|
}
|
|
1017
|
-
|
|
1223
|
+
getAPIs(spec) {
|
|
1018
1224
|
if (this.apiMap !== undefined) {
|
|
1019
1225
|
return this.apiMap;
|
|
1020
1226
|
}
|
|
1021
|
-
const
|
|
1022
|
-
|
|
1023
|
-
|
|
1227
|
+
const validator = this.getValidator(spec);
|
|
1228
|
+
const apiMap = validator.listAPIs();
|
|
1229
|
+
this.apiMap = apiMap;
|
|
1230
|
+
return apiMap;
|
|
1024
1231
|
}
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
const result = {};
|
|
1029
|
-
for (const path in paths) {
|
|
1030
|
-
const methods = paths[path];
|
|
1031
|
-
for (const method in methods) {
|
|
1032
|
-
const operationObject = methods[method];
|
|
1033
|
-
if (((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
|
|
1034
|
-
const validateResult = Utils.isSupportedApi(method, path, spec, options);
|
|
1035
|
-
if (validateResult.isValid) {
|
|
1036
|
-
result[`${method.toUpperCase()} ${path}`] = operationObject;
|
|
1037
|
-
}
|
|
1038
|
-
}
|
|
1039
|
-
}
|
|
1232
|
+
getValidator(spec) {
|
|
1233
|
+
if (this.validator) {
|
|
1234
|
+
return this.validator;
|
|
1040
1235
|
}
|
|
1041
|
-
|
|
1236
|
+
const validator = ValidatorFactory.create(spec, this.options);
|
|
1237
|
+
this.validator = validator;
|
|
1238
|
+
return validator;
|
|
1042
1239
|
}
|
|
1043
1240
|
}
|
|
1044
1241
|
|