@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.
@@ -166,7 +166,8 @@ ConstantString.CommandDescriptionMaxLens = 128;
166
166
  ConstantString.ParameterDescriptionMaxLens = 128;
167
167
  ConstantString.CommandTitleMaxLens = 32;
168
168
  ConstantString.ParameterTitleMaxLens = 32;
169
- ConstantString.SMERequiredParamsMaxNum = 5;
169
+ ConstantString.SMERequiredParamsMaxNum = 5;
170
+ ConstantString.DefaultPluginId = "plugin_1";
170
171
 
171
172
  // Copyright (c) Microsoft Corporation.
172
173
  class SpecParserError extends Error {
@@ -189,249 +190,9 @@ class Utils {
189
190
  }
190
191
  return false;
191
192
  }
192
- static checkParameters(paramObject, isCopilot) {
193
- const paramResult = {
194
- requiredNum: 0,
195
- optionalNum: 0,
196
- isValid: true,
197
- reason: [],
198
- };
199
- if (!paramObject) {
200
- return paramResult;
201
- }
202
- for (let i = 0; i < paramObject.length; i++) {
203
- const param = paramObject[i];
204
- const schema = param.schema;
205
- if (isCopilot && this.hasNestedObjectInSchema(schema)) {
206
- paramResult.isValid = false;
207
- paramResult.reason.push(ErrorType.ParamsContainsNestedObject);
208
- continue;
209
- }
210
- const isRequiredWithoutDefault = param.required && schema.default === undefined;
211
- if (isCopilot) {
212
- if (isRequiredWithoutDefault) {
213
- paramResult.requiredNum = paramResult.requiredNum + 1;
214
- }
215
- else {
216
- paramResult.optionalNum = paramResult.optionalNum + 1;
217
- }
218
- continue;
219
- }
220
- if (param.in === "header" || param.in === "cookie") {
221
- if (isRequiredWithoutDefault) {
222
- paramResult.isValid = false;
223
- paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
224
- }
225
- continue;
226
- }
227
- if (schema.type !== "boolean" &&
228
- schema.type !== "string" &&
229
- schema.type !== "number" &&
230
- schema.type !== "integer") {
231
- if (isRequiredWithoutDefault) {
232
- paramResult.isValid = false;
233
- paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
234
- }
235
- continue;
236
- }
237
- if (param.in === "query" || param.in === "path") {
238
- if (isRequiredWithoutDefault) {
239
- paramResult.requiredNum = paramResult.requiredNum + 1;
240
- }
241
- else {
242
- paramResult.optionalNum = paramResult.optionalNum + 1;
243
- }
244
- }
245
- }
246
- return paramResult;
247
- }
248
- static checkPostBody(schema, isRequired = false, isCopilot = false) {
249
- var _a;
250
- const paramResult = {
251
- requiredNum: 0,
252
- optionalNum: 0,
253
- isValid: true,
254
- reason: [],
255
- };
256
- if (Object.keys(schema).length === 0) {
257
- return paramResult;
258
- }
259
- const isRequiredWithoutDefault = isRequired && schema.default === undefined;
260
- if (isCopilot && this.hasNestedObjectInSchema(schema)) {
261
- paramResult.isValid = false;
262
- paramResult.reason = [ErrorType.RequestBodyContainsNestedObject];
263
- return paramResult;
264
- }
265
- if (schema.type === "string" ||
266
- schema.type === "integer" ||
267
- schema.type === "boolean" ||
268
- schema.type === "number") {
269
- if (isRequiredWithoutDefault) {
270
- paramResult.requiredNum = paramResult.requiredNum + 1;
271
- }
272
- else {
273
- paramResult.optionalNum = paramResult.optionalNum + 1;
274
- }
275
- }
276
- else if (schema.type === "object") {
277
- const { properties } = schema;
278
- for (const property in properties) {
279
- let isRequired = false;
280
- if (schema.required && ((_a = schema.required) === null || _a === void 0 ? void 0 : _a.indexOf(property)) >= 0) {
281
- isRequired = true;
282
- }
283
- const result = Utils.checkPostBody(properties[property], isRequired, isCopilot);
284
- paramResult.requiredNum += result.requiredNum;
285
- paramResult.optionalNum += result.optionalNum;
286
- paramResult.isValid = paramResult.isValid && result.isValid;
287
- paramResult.reason.push(...result.reason);
288
- }
289
- }
290
- else {
291
- if (isRequiredWithoutDefault && !isCopilot) {
292
- paramResult.isValid = false;
293
- paramResult.reason.push(ErrorType.PostBodyContainsRequiredUnsupportedSchema);
294
- }
295
- }
296
- return paramResult;
297
- }
298
193
  static containMultipleMediaTypes(bodyObject) {
299
194
  return Object.keys((bodyObject === null || bodyObject === void 0 ? void 0 : bodyObject.content) || {}).length > 1;
300
195
  }
301
- /**
302
- * Checks if the given API is supported.
303
- * @param {string} method - The HTTP method of the API.
304
- * @param {string} path - The path of the API.
305
- * @param {OpenAPIV3.Document} spec - The OpenAPI specification document.
306
- * @returns {boolean} - Returns true if the API is supported, false otherwise.
307
- * @description The following APIs are supported:
308
- * 1. only support Get/Post operation without auth property
309
- * 2. parameter inside query or path only support string, number, boolean and integer
310
- * 3. parameter inside post body only support string, number, boolean, integer and object
311
- * 4. request body + required parameters <= 1
312
- * 5. response body should be “application/json” and not empty, and response code should be 20X
313
- * 6. only support request body with “application/json” content type
314
- */
315
- static isSupportedApi(method, path, spec, options) {
316
- var _a;
317
- const result = { isValid: true, reason: [] };
318
- method = method.toLocaleLowerCase();
319
- if (options.allowMethods && !options.allowMethods.includes(method)) {
320
- result.isValid = false;
321
- result.reason.push(ErrorType.MethodNotAllowed);
322
- return result;
323
- }
324
- const pathObj = spec.paths[path];
325
- if (!pathObj || !pathObj[method]) {
326
- result.isValid = false;
327
- result.reason.push(ErrorType.UrlPathNotExist);
328
- return result;
329
- }
330
- const securities = pathObj[method].security;
331
- const isTeamsAi = options.projectType === ProjectType.TeamsAi;
332
- const isCopilot = options.projectType === ProjectType.Copilot;
333
- // Teams AI project doesn't care about auth, it will use authProvider for user to implement
334
- if (!isTeamsAi) {
335
- const authArray = Utils.getAuthArray(securities, spec);
336
- const authCheckResult = Utils.isSupportedAuth(authArray, options);
337
- if (!authCheckResult.isValid) {
338
- result.reason.push(...authCheckResult.reason);
339
- }
340
- }
341
- const operationObject = pathObj[method];
342
- if (!options.allowMissingId && !operationObject.operationId) {
343
- result.reason.push(ErrorType.MissingOperationId);
344
- }
345
- const rootServer = spec.servers && spec.servers[0];
346
- const methodServer = spec.paths[path].servers && ((_a = spec.paths[path]) === null || _a === void 0 ? void 0 : _a.servers[0]);
347
- const operationServer = operationObject.servers && operationObject.servers[0];
348
- const serverUrl = operationServer || methodServer || rootServer;
349
- if (!serverUrl) {
350
- result.reason.push(ErrorType.NoServerInformation);
351
- }
352
- else {
353
- const serverValidateResult = Utils.checkServerUrl([serverUrl]);
354
- result.reason.push(...serverValidateResult.map((item) => item.type));
355
- }
356
- const paramObject = operationObject.parameters;
357
- const requestBody = operationObject.requestBody;
358
- const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
359
- if (!isTeamsAi && Utils.containMultipleMediaTypes(requestBody)) {
360
- result.reason.push(ErrorType.PostBodyContainMultipleMediaTypes);
361
- }
362
- const { json, multipleMediaType } = Utils.getResponseJson(operationObject, isTeamsAi);
363
- if (multipleMediaType && !isTeamsAi) {
364
- result.reason.push(ErrorType.ResponseContainMultipleMediaTypes);
365
- }
366
- else if (Object.keys(json).length === 0) {
367
- result.reason.push(ErrorType.ResponseJsonIsEmpty);
368
- }
369
- // Teams AI project doesn't care about request parameters/body
370
- if (!isTeamsAi) {
371
- let requestBodyParamResult = {
372
- requiredNum: 0,
373
- optionalNum: 0,
374
- isValid: true,
375
- reason: [],
376
- };
377
- if (requestJsonBody) {
378
- const requestBodySchema = requestJsonBody.schema;
379
- if (isCopilot && requestBodySchema.type !== "object") {
380
- result.reason.push(ErrorType.PostBodySchemaIsNotJson);
381
- }
382
- requestBodyParamResult = Utils.checkPostBody(requestBodySchema, requestBody.required, isCopilot);
383
- if (!requestBodyParamResult.isValid && requestBodyParamResult.reason) {
384
- result.reason.push(...requestBodyParamResult.reason);
385
- }
386
- }
387
- const paramResult = Utils.checkParameters(paramObject, isCopilot);
388
- if (!paramResult.isValid && paramResult.reason) {
389
- result.reason.push(...paramResult.reason);
390
- }
391
- // Copilot support arbitrary parameters
392
- if (!isCopilot && paramResult.isValid && requestBodyParamResult.isValid) {
393
- const totalRequiredParams = requestBodyParamResult.requiredNum + paramResult.requiredNum;
394
- const totalParams = totalRequiredParams + requestBodyParamResult.optionalNum + paramResult.optionalNum;
395
- if (totalRequiredParams > 1) {
396
- if (!options.allowMultipleParameters ||
397
- totalRequiredParams > ConstantString.SMERequiredParamsMaxNum) {
398
- result.reason.push(ErrorType.ExceededRequiredParamsLimit);
399
- }
400
- }
401
- else if (totalParams === 0) {
402
- result.reason.push(ErrorType.NoParameter);
403
- }
404
- }
405
- }
406
- if (result.reason.length > 0) {
407
- result.isValid = false;
408
- }
409
- return result;
410
- }
411
- static isSupportedAuth(authSchemeArray, options) {
412
- if (authSchemeArray.length === 0) {
413
- return { isValid: true, reason: [] };
414
- }
415
- if (options.allowAPIKeyAuth || options.allowOauth2 || options.allowBearerTokenAuth) {
416
- // Currently we don't support multiple auth in one operation
417
- if (authSchemeArray.length > 0 && authSchemeArray.every((auths) => auths.length > 1)) {
418
- return {
419
- isValid: false,
420
- reason: [ErrorType.MultipleAuthNotSupported],
421
- };
422
- }
423
- for (const auths of authSchemeArray) {
424
- if (auths.length === 1) {
425
- if ((options.allowAPIKeyAuth && Utils.isAPIKeyAuth(auths[0].authScheme)) ||
426
- (options.allowOauth2 && Utils.isOAuthWithAuthCodeFlow(auths[0].authScheme)) ||
427
- (options.allowBearerTokenAuth && Utils.isBearerTokenAuth(auths[0].authScheme))) {
428
- return { isValid: true, reason: [] };
429
- }
430
- }
431
- }
432
- }
433
- return { isValid: false, reason: [ErrorType.AuthTypeIsNotSupported] };
434
- }
435
196
  static isBearerTokenAuth(authScheme) {
436
197
  return authScheme.type === "http" && authScheme.scheme === "bearer";
437
198
  }
@@ -439,10 +200,9 @@ class Utils {
439
200
  return authScheme.type === "apiKey";
440
201
  }
441
202
  static isOAuthWithAuthCodeFlow(authScheme) {
442
- if (authScheme.type === "oauth2" && authScheme.flows && authScheme.flows.authorizationCode) {
443
- return true;
444
- }
445
- return false;
203
+ return !!(authScheme.type === "oauth2" &&
204
+ authScheme.flows &&
205
+ authScheme.flows.authorizationCode);
446
206
  }
447
207
  static getAuthArray(securities, spec) {
448
208
  var _a;
@@ -470,7 +230,7 @@ class Utils {
470
230
  static updateFirstLetter(str) {
471
231
  return str.charAt(0).toUpperCase() + str.slice(1);
472
232
  }
473
- static getResponseJson(operationObject, isTeamsAiProject = false) {
233
+ static getResponseJson(operationObject) {
474
234
  var _a, _b;
475
235
  let json = {};
476
236
  let multipleMediaType = false;
@@ -481,9 +241,6 @@ class Utils {
481
241
  json = responseObject.content["application/json"];
482
242
  if (Utils.containMultipleMediaTypes(responseObject)) {
483
243
  multipleMediaType = true;
484
- if (isTeamsAiProject) {
485
- break;
486
- }
487
244
  json = {};
488
245
  }
489
246
  else {
@@ -711,13 +468,7 @@ class Utils {
711
468
  }
712
469
  }
713
470
  const operationId = operationItem.operationId;
714
- const parameters = [];
715
- if (requiredParams.length !== 0) {
716
- parameters.push(...requiredParams);
717
- }
718
- else {
719
- parameters.push(optionalParams[0]);
720
- }
471
+ const parameters = [...requiredParams, ...optionalParams];
721
472
  const command = {
722
473
  context: ["compose"],
723
474
  type: "query",
@@ -726,26 +477,51 @@ class Utils {
726
477
  parameters: parameters,
727
478
  description: ((_b = operationItem.description) !== null && _b !== void 0 ? _b : "").slice(0, ConstantString.CommandDescriptionMaxLens),
728
479
  };
729
- let warning = undefined;
730
- if (requiredParams.length === 0 && optionalParams.length > 1) {
731
- warning = {
732
- type: WarningType.OperationOnlyContainsOptionalParam,
733
- content: Utils.format(ConstantString.OperationOnlyContainsOptionalParam, operationId),
734
- data: operationId,
735
- };
480
+ return command;
481
+ }
482
+ static format(str, ...args) {
483
+ let index = 0;
484
+ return str.replace(/%s/g, () => {
485
+ const arg = args[index++];
486
+ return arg !== undefined ? arg : "";
487
+ });
488
+ }
489
+ static getSafeRegistrationIdEnvName(authName) {
490
+ if (!authName) {
491
+ return "";
492
+ }
493
+ let safeRegistrationIdEnvName = authName.toUpperCase().replace(/[^A-Z0-9_]/g, "_");
494
+ if (!safeRegistrationIdEnvName.match(/^[A-Z]/)) {
495
+ safeRegistrationIdEnvName = "PREFIX_" + safeRegistrationIdEnvName;
736
496
  }
737
- return [command, warning];
497
+ return safeRegistrationIdEnvName;
498
+ }
499
+ static getServerObject(spec, method, path) {
500
+ const pathObj = spec.paths[path];
501
+ const operationObject = pathObj[method];
502
+ const rootServer = spec.servers && spec.servers[0];
503
+ const methodServer = spec.paths[path].servers && spec.paths[path].servers[0];
504
+ const operationServer = operationObject.servers && operationObject.servers[0];
505
+ const serverUrl = operationServer || methodServer || rootServer;
506
+ return serverUrl;
738
507
  }
739
- static listAPIs(spec, options) {
508
+ }
509
+
510
+ // Copyright (c) Microsoft Corporation.
511
+ class Validator {
512
+ listAPIs() {
740
513
  var _a;
741
- const paths = spec.paths;
514
+ if (this.apiMap) {
515
+ return this.apiMap;
516
+ }
517
+ const paths = this.spec.paths;
742
518
  const result = {};
743
519
  for (const path in paths) {
744
520
  const methods = paths[path];
745
521
  for (const method in methods) {
746
522
  const operationObject = methods[method];
747
- if (((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
748
- const validateResult = Utils.isSupportedApi(method, path, spec, options);
523
+ if (((_a = this.options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
524
+ const validateResult = this.validateAPI(method, path);
749
525
  result[`${method.toUpperCase()} ${path}`] = {
750
526
  operation: operationObject,
751
527
  isValid: validateResult.isValid,
@@ -754,38 +530,48 @@ class Utils {
754
530
  }
755
531
  }
756
532
  }
533
+ this.apiMap = result;
757
534
  return result;
758
535
  }
759
- static validateSpec(spec, parser, isSwaggerFile, options) {
760
- const errors = [];
761
- const warnings = [];
762
- const apiMap = Utils.listAPIs(spec, options);
763
- if (isSwaggerFile) {
764
- warnings.push({
765
- type: WarningType.ConvertSwaggerToOpenAPI,
766
- content: ConstantString.ConvertSwaggerToOpenAPI,
767
- });
768
- }
769
- const serverErrors = Utils.validateServer(spec, options);
770
- errors.push(...serverErrors);
771
- // Remote reference not supported
772
- const refPaths = parser.$refs.paths();
773
- // refPaths [0] is the current spec file path
774
- if (refPaths.length > 1) {
775
- errors.push({
776
- type: ErrorType.RemoteRefNotSupported,
777
- content: Utils.format(ConstantString.RemoteRefNotSupported, refPaths.join(", ")),
778
- data: refPaths,
536
+ validateSpecVersion() {
537
+ const result = { errors: [], warnings: [] };
538
+ if (this.spec.openapi >= "3.1.0") {
539
+ result.errors.push({
540
+ type: ErrorType.SpecVersionNotSupported,
541
+ content: Utils.format(ConstantString.SpecVersionNotSupported, this.spec.openapi),
542
+ data: this.spec.openapi,
779
543
  });
780
544
  }
781
- // No supported API
545
+ return result;
546
+ }
547
+ validateSpecServer() {
548
+ const result = { errors: [], warnings: [] };
549
+ const serverErrors = Utils.validateServer(this.spec, this.options);
550
+ result.errors.push(...serverErrors);
551
+ return result;
552
+ }
553
+ validateSpecNoSupportAPI() {
554
+ const result = { errors: [], warnings: [] };
555
+ const apiMap = this.listAPIs();
782
556
  const validAPIs = Object.entries(apiMap).filter(([, value]) => value.isValid);
783
557
  if (validAPIs.length === 0) {
784
- errors.push({
558
+ const data = [];
559
+ for (const key in apiMap) {
560
+ const { reason } = apiMap[key];
561
+ const apiInvalidReason = { api: key, reason: reason };
562
+ data.push(apiInvalidReason);
563
+ }
564
+ result.errors.push({
785
565
  type: ErrorType.NoSupportedApi,
786
566
  content: ConstantString.NoSupportedApi,
567
+ data,
787
568
  });
788
569
  }
570
+ return result;
571
+ }
572
+ validateSpecOperationId() {
573
+ const result = { errors: [], warnings: [] };
574
+ const apiMap = this.listAPIs();
789
575
  // OperationId missing
790
576
  const apisMissingOperationId = [];
791
577
  for (const key in apiMap) {
@@ -795,54 +581,431 @@ class Utils {
795
581
  }
796
582
  }
797
583
  if (apisMissingOperationId.length > 0) {
798
- warnings.push({
584
+ result.warnings.push({
799
585
  type: WarningType.OperationIdMissing,
800
586
  content: Utils.format(ConstantString.MissingOperationId, apisMissingOperationId.join(", ")),
801
587
  data: apisMissingOperationId,
802
588
  });
803
589
  }
804
- let status = ValidationStatus.Valid;
805
- if (warnings.length > 0 && errors.length === 0) {
806
- status = ValidationStatus.Warning;
590
+ return result;
591
+ }
592
+ validateMethodAndPath(method, path) {
593
+ const result = { isValid: true, reason: [] };
594
+ if (this.options.allowMethods && !this.options.allowMethods.includes(method)) {
595
+ result.isValid = false;
596
+ result.reason.push(ErrorType.MethodNotAllowed);
597
+ return result;
807
598
  }
808
- else if (errors.length > 0) {
809
- status = ValidationStatus.Error;
599
+ const pathObj = this.spec.paths[path];
600
+ if (!pathObj || !pathObj[method]) {
601
+ result.isValid = false;
602
+ result.reason.push(ErrorType.UrlPathNotExist);
603
+ return result;
810
604
  }
811
- return {
812
- status,
813
- warnings,
814
- errors,
815
- };
605
+ return result;
816
606
  }
817
- static format(str, ...args) {
818
- let index = 0;
819
- return str.replace(/%s/g, () => {
820
- const arg = args[index++];
821
- return arg !== undefined ? arg : "";
822
- });
607
+ validateResponse(method, path) {
608
+ const result = { isValid: true, reason: [] };
609
+ const operationObject = this.spec.paths[path][method];
610
+ const { json, multipleMediaType } = Utils.getResponseJson(operationObject);
611
+ // only support response body only contains “application/json” content type
612
+ if (multipleMediaType) {
613
+ result.reason.push(ErrorType.ResponseContainMultipleMediaTypes);
614
+ }
615
+ else if (Object.keys(json).length === 0) {
616
+ // response body should not be empty
617
+ result.reason.push(ErrorType.ResponseJsonIsEmpty);
618
+ }
619
+ return result;
823
620
  }
824
- static getSafeRegistrationIdEnvName(authName) {
825
- if (!authName) {
826
- return "";
621
+ validateServer(method, path) {
622
+ const result = { isValid: true, reason: [] };
623
+ const serverObj = Utils.getServerObject(this.spec, method, path);
624
+ if (!serverObj) {
625
+ // should contain server URL
626
+ result.reason.push(ErrorType.NoServerInformation);
827
627
  }
828
- let safeRegistrationIdEnvName = authName.toUpperCase().replace(/[^A-Z0-9_]/g, "_");
829
- if (!safeRegistrationIdEnvName.match(/^[A-Z]/)) {
830
- safeRegistrationIdEnvName = "PREFIX_" + safeRegistrationIdEnvName;
628
+ else {
629
+ // server url should be absolute url with https protocol
630
+ const serverValidateResult = Utils.checkServerUrl([serverObj]);
631
+ result.reason.push(...serverValidateResult.map((item) => item.type));
831
632
  }
832
- return safeRegistrationIdEnvName;
633
+ return result;
833
634
  }
834
- static getAllAPICount(spec) {
835
- let count = 0;
836
- const paths = spec.paths;
837
- for (const path in paths) {
838
- const methods = paths[path];
839
- for (const method in methods) {
840
- if (ConstantString.AllOperationMethods.includes(method)) {
841
- count++;
635
+ validateAuth(method, path) {
636
+ const pathObj = this.spec.paths[path];
637
+ const operationObject = pathObj[method];
638
+ const securities = operationObject.security;
639
+ const authSchemeArray = Utils.getAuthArray(securities, this.spec);
640
+ if (authSchemeArray.length === 0) {
641
+ return { isValid: true, reason: [] };
642
+ }
643
+ if (this.options.allowAPIKeyAuth ||
644
+ this.options.allowOauth2 ||
645
+ this.options.allowBearerTokenAuth) {
646
+ // Currently we don't support multiple auth in one operation
647
+ if (authSchemeArray.length > 0 && authSchemeArray.every((auths) => auths.length > 1)) {
648
+ return {
649
+ isValid: false,
650
+ reason: [ErrorType.MultipleAuthNotSupported],
651
+ };
652
+ }
653
+ for (const auths of authSchemeArray) {
654
+ if (auths.length === 1) {
655
+ if ((this.options.allowAPIKeyAuth && Utils.isAPIKeyAuth(auths[0].authScheme)) ||
656
+ (this.options.allowOauth2 && Utils.isOAuthWithAuthCodeFlow(auths[0].authScheme)) ||
657
+ (this.options.allowBearerTokenAuth && Utils.isBearerTokenAuth(auths[0].authScheme))) {
658
+ return { isValid: true, reason: [] };
659
+ }
842
660
  }
843
661
  }
844
662
  }
845
- return count;
663
+ return { isValid: false, reason: [ErrorType.AuthTypeIsNotSupported] };
664
+ }
665
+ checkPostBodySchema(schema, isRequired = false) {
666
+ var _a;
667
+ const paramResult = {
668
+ requiredNum: 0,
669
+ optionalNum: 0,
670
+ isValid: true,
671
+ reason: [],
672
+ };
673
+ if (Object.keys(schema).length === 0) {
674
+ return paramResult;
675
+ }
676
+ const isRequiredWithoutDefault = isRequired && schema.default === undefined;
677
+ const isCopilot = this.projectType === ProjectType.Copilot;
678
+ if (isCopilot && this.hasNestedObjectInSchema(schema)) {
679
+ paramResult.isValid = false;
680
+ paramResult.reason = [ErrorType.RequestBodyContainsNestedObject];
681
+ return paramResult;
682
+ }
683
+ if (schema.type === "string" ||
684
+ schema.type === "integer" ||
685
+ schema.type === "boolean" ||
686
+ schema.type === "number") {
687
+ if (isRequiredWithoutDefault) {
688
+ paramResult.requiredNum = paramResult.requiredNum + 1;
689
+ }
690
+ else {
691
+ paramResult.optionalNum = paramResult.optionalNum + 1;
692
+ }
693
+ }
694
+ else if (schema.type === "object") {
695
+ const { properties } = schema;
696
+ for (const property in properties) {
697
+ let isRequired = false;
698
+ if (schema.required && ((_a = schema.required) === null || _a === void 0 ? void 0 : _a.indexOf(property)) >= 0) {
699
+ isRequired = true;
700
+ }
701
+ const result = this.checkPostBodySchema(properties[property], isRequired);
702
+ paramResult.requiredNum += result.requiredNum;
703
+ paramResult.optionalNum += result.optionalNum;
704
+ paramResult.isValid = paramResult.isValid && result.isValid;
705
+ paramResult.reason.push(...result.reason);
706
+ }
707
+ }
708
+ else {
709
+ if (isRequiredWithoutDefault && !isCopilot) {
710
+ paramResult.isValid = false;
711
+ paramResult.reason.push(ErrorType.PostBodyContainsRequiredUnsupportedSchema);
712
+ }
713
+ }
714
+ return paramResult;
715
+ }
716
+ checkParamSchema(paramObject) {
717
+ const paramResult = {
718
+ requiredNum: 0,
719
+ optionalNum: 0,
720
+ isValid: true,
721
+ reason: [],
722
+ };
723
+ if (!paramObject) {
724
+ return paramResult;
725
+ }
726
+ const isCopilot = this.projectType === ProjectType.Copilot;
727
+ for (let i = 0; i < paramObject.length; i++) {
728
+ const param = paramObject[i];
729
+ const schema = param.schema;
730
+ if (isCopilot && this.hasNestedObjectInSchema(schema)) {
731
+ paramResult.isValid = false;
732
+ paramResult.reason.push(ErrorType.ParamsContainsNestedObject);
733
+ continue;
734
+ }
735
+ const isRequiredWithoutDefault = param.required && schema.default === undefined;
736
+ if (isCopilot) {
737
+ if (isRequiredWithoutDefault) {
738
+ paramResult.requiredNum = paramResult.requiredNum + 1;
739
+ }
740
+ else {
741
+ paramResult.optionalNum = paramResult.optionalNum + 1;
742
+ }
743
+ continue;
744
+ }
745
+ if (param.in === "header" || param.in === "cookie") {
746
+ if (isRequiredWithoutDefault) {
747
+ paramResult.isValid = false;
748
+ paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
749
+ }
750
+ continue;
751
+ }
752
+ if (schema.type !== "boolean" &&
753
+ schema.type !== "string" &&
754
+ schema.type !== "number" &&
755
+ schema.type !== "integer") {
756
+ if (isRequiredWithoutDefault) {
757
+ paramResult.isValid = false;
758
+ paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
759
+ }
760
+ continue;
761
+ }
762
+ if (param.in === "query" || param.in === "path") {
763
+ if (isRequiredWithoutDefault) {
764
+ paramResult.requiredNum = paramResult.requiredNum + 1;
765
+ }
766
+ else {
767
+ paramResult.optionalNum = paramResult.optionalNum + 1;
768
+ }
769
+ }
770
+ }
771
+ return paramResult;
772
+ }
773
+ hasNestedObjectInSchema(schema) {
774
+ if (schema.type === "object") {
775
+ for (const property in schema.properties) {
776
+ const nestedSchema = schema.properties[property];
777
+ if (nestedSchema.type === "object") {
778
+ return true;
779
+ }
780
+ }
781
+ }
782
+ return false;
783
+ }
784
+ }
785
+
786
+ // Copyright (c) Microsoft Corporation.
787
+ class CopilotValidator extends Validator {
788
+ constructor(spec, options) {
789
+ super();
790
+ this.projectType = ProjectType.Copilot;
791
+ this.options = options;
792
+ this.spec = spec;
793
+ }
794
+ validateSpec() {
795
+ const result = { errors: [], warnings: [] };
796
+ // validate spec version
797
+ let validationResult = this.validateSpecVersion();
798
+ result.errors.push(...validationResult.errors);
799
+ // validate spec server
800
+ validationResult = this.validateSpecServer();
801
+ result.errors.push(...validationResult.errors);
802
+ // validate no supported API
803
+ validationResult = this.validateSpecNoSupportAPI();
804
+ result.errors.push(...validationResult.errors);
805
+ // validate operationId missing
806
+ validationResult = this.validateSpecOperationId();
807
+ result.warnings.push(...validationResult.warnings);
808
+ return result;
809
+ }
810
+ validateAPI(method, path) {
811
+ const result = { isValid: true, reason: [] };
812
+ method = method.toLocaleLowerCase();
813
+ // validate method and path
814
+ const methodAndPathResult = this.validateMethodAndPath(method, path);
815
+ if (!methodAndPathResult.isValid) {
816
+ return methodAndPathResult;
817
+ }
818
+ const operationObject = this.spec.paths[path][method];
819
+ // validate auth
820
+ const authCheckResult = this.validateAuth(method, path);
821
+ result.reason.push(...authCheckResult.reason);
822
+ // validate operationId
823
+ if (!this.options.allowMissingId && !operationObject.operationId) {
824
+ result.reason.push(ErrorType.MissingOperationId);
825
+ }
826
+ // validate server
827
+ const validateServerResult = this.validateServer(method, path);
828
+ result.reason.push(...validateServerResult.reason);
829
+ // validate response
830
+ const validateResponseResult = this.validateResponse(method, path);
831
+ result.reason.push(...validateResponseResult.reason);
832
+ // validate requestBody
833
+ const requestBody = operationObject.requestBody;
834
+ const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
835
+ if (Utils.containMultipleMediaTypes(requestBody)) {
836
+ result.reason.push(ErrorType.PostBodyContainMultipleMediaTypes);
837
+ }
838
+ if (requestJsonBody) {
839
+ const requestBodySchema = requestJsonBody.schema;
840
+ if (requestBodySchema.type !== "object") {
841
+ result.reason.push(ErrorType.PostBodySchemaIsNotJson);
842
+ }
843
+ const requestBodyParamResult = this.checkPostBodySchema(requestBodySchema, requestBody.required);
844
+ result.reason.push(...requestBodyParamResult.reason);
845
+ }
846
+ // validate parameters
847
+ const paramObject = operationObject.parameters;
848
+ const paramResult = this.checkParamSchema(paramObject);
849
+ result.reason.push(...paramResult.reason);
850
+ if (result.reason.length > 0) {
851
+ result.isValid = false;
852
+ }
853
+ return result;
854
+ }
855
+ }
856
+
857
+ // Copyright (c) Microsoft Corporation.
858
+ class SMEValidator extends Validator {
859
+ constructor(spec, options) {
860
+ super();
861
+ this.projectType = ProjectType.SME;
862
+ this.options = options;
863
+ this.spec = spec;
864
+ }
865
+ validateSpec() {
866
+ const result = { errors: [], warnings: [] };
867
+ // validate spec version
868
+ let validationResult = this.validateSpecVersion();
869
+ result.errors.push(...validationResult.errors);
870
+ // validate spec server
871
+ validationResult = this.validateSpecServer();
872
+ result.errors.push(...validationResult.errors);
873
+ // validate no supported API
874
+ validationResult = this.validateSpecNoSupportAPI();
875
+ result.errors.push(...validationResult.errors);
876
+ // validate operationId missing
877
+ if (this.options.allowMissingId) {
878
+ validationResult = this.validateSpecOperationId();
879
+ result.warnings.push(...validationResult.warnings);
880
+ }
881
+ return result;
882
+ }
883
+ validateAPI(method, path) {
884
+ const result = { isValid: true, reason: [] };
885
+ method = method.toLocaleLowerCase();
886
+ // validate method and path
887
+ const methodAndPathResult = this.validateMethodAndPath(method, path);
888
+ if (!methodAndPathResult.isValid) {
889
+ return methodAndPathResult;
890
+ }
891
+ const operationObject = this.spec.paths[path][method];
892
+ // validate auth
893
+ const authCheckResult = this.validateAuth(method, path);
894
+ result.reason.push(...authCheckResult.reason);
895
+ // validate operationId
896
+ if (!this.options.allowMissingId && !operationObject.operationId) {
897
+ result.reason.push(ErrorType.MissingOperationId);
898
+ }
899
+ // validate server
900
+ const validateServerResult = this.validateServer(method, path);
901
+ result.reason.push(...validateServerResult.reason);
902
+ // validate response
903
+ const validateResponseResult = this.validateResponse(method, path);
904
+ result.reason.push(...validateResponseResult.reason);
905
+ let postBodyResult = {
906
+ requiredNum: 0,
907
+ optionalNum: 0,
908
+ isValid: true,
909
+ reason: [],
910
+ };
911
+ // validate requestBody
912
+ const requestBody = operationObject.requestBody;
913
+ const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
914
+ if (Utils.containMultipleMediaTypes(requestBody)) {
915
+ result.reason.push(ErrorType.PostBodyContainMultipleMediaTypes);
916
+ }
917
+ if (requestJsonBody) {
918
+ const requestBodySchema = requestJsonBody.schema;
919
+ postBodyResult = this.checkPostBodySchema(requestBodySchema, requestBody.required);
920
+ result.reason.push(...postBodyResult.reason);
921
+ }
922
+ // validate parameters
923
+ const paramObject = operationObject.parameters;
924
+ const paramResult = this.checkParamSchema(paramObject);
925
+ result.reason.push(...paramResult.reason);
926
+ // validate total parameters count
927
+ if (paramResult.isValid && postBodyResult.isValid) {
928
+ const paramCountResult = this.validateParamCount(postBodyResult, paramResult);
929
+ result.reason.push(...paramCountResult.reason);
930
+ }
931
+ if (result.reason.length > 0) {
932
+ result.isValid = false;
933
+ }
934
+ return result;
935
+ }
936
+ validateParamCount(postBodyResult, paramResult) {
937
+ const result = { isValid: true, reason: [] };
938
+ const totalRequiredParams = postBodyResult.requiredNum + paramResult.requiredNum;
939
+ const totalParams = totalRequiredParams + postBodyResult.optionalNum + paramResult.optionalNum;
940
+ if (totalRequiredParams > 1) {
941
+ if (!this.options.allowMultipleParameters ||
942
+ totalRequiredParams > SMEValidator.SMERequiredParamsMaxNum) {
943
+ result.reason.push(ErrorType.ExceededRequiredParamsLimit);
944
+ }
945
+ }
946
+ else if (totalParams === 0) {
947
+ result.reason.push(ErrorType.NoParameter);
948
+ }
949
+ return result;
950
+ }
951
+ }
952
+ SMEValidator.SMERequiredParamsMaxNum = 5;
953
+
954
+ // Copyright (c) Microsoft Corporation.
955
+ class TeamsAIValidator extends Validator {
956
+ constructor(spec, options) {
957
+ super();
958
+ this.projectType = ProjectType.TeamsAi;
959
+ this.options = options;
960
+ this.spec = spec;
961
+ }
962
+ validateSpec() {
963
+ const result = { errors: [], warnings: [] };
964
+ // validate spec server
965
+ let validationResult = this.validateSpecServer();
966
+ result.errors.push(...validationResult.errors);
967
+ // validate no supported API
968
+ validationResult = this.validateSpecNoSupportAPI();
969
+ result.errors.push(...validationResult.errors);
970
+ return result;
971
+ }
972
+ validateAPI(method, path) {
973
+ const result = { isValid: true, reason: [] };
974
+ method = method.toLocaleLowerCase();
975
+ // validate method and path
976
+ const methodAndPathResult = this.validateMethodAndPath(method, path);
977
+ if (!methodAndPathResult.isValid) {
978
+ return methodAndPathResult;
979
+ }
980
+ const operationObject = this.spec.paths[path][method];
981
+ // validate operationId
982
+ if (!this.options.allowMissingId && !operationObject.operationId) {
983
+ result.reason.push(ErrorType.MissingOperationId);
984
+ }
985
+ // validate server
986
+ const validateServerResult = this.validateServer(method, path);
987
+ result.reason.push(...validateServerResult.reason);
988
+ if (result.reason.length > 0) {
989
+ result.isValid = false;
990
+ }
991
+ return result;
992
+ }
993
+ }
994
+
995
+ class ValidatorFactory {
996
+ static create(spec, options) {
997
+ var _a;
998
+ const type = (_a = options.projectType) !== null && _a !== void 0 ? _a : ProjectType.SME;
999
+ switch (type) {
1000
+ case ProjectType.SME:
1001
+ return new SMEValidator(spec, options);
1002
+ case ProjectType.Copilot:
1003
+ return new CopilotValidator(spec, options);
1004
+ case ProjectType.TeamsAi:
1005
+ return new TeamsAIValidator(spec, options);
1006
+ default:
1007
+ throw new Error(`Invalid project type: ${type}`);
1008
+ }
846
1009
  }
847
1010
  }
848
1011
 
@@ -860,7 +1023,8 @@ class SpecFilter {
860
1023
  if (ConstantString.AllOperationMethods.includes(methodName) &&
861
1024
  pathObj &&
862
1025
  pathObj[methodName]) {
863
- const validateResult = Utils.isSupportedApi(methodName, path, resolvedSpec, options);
1026
+ const validator = ValidatorFactory.create(resolvedSpec, options);
1027
+ const validateResult = validator.validateAPI(methodName, path);
864
1028
  if (!validateResult.isValid) {
865
1029
  continue;
866
1030
  }
@@ -893,13 +1057,14 @@ class ManifestUpdater {
893
1057
  const apiPluginRelativePath = ManifestUpdater.getRelativePath(manifestPath, apiPluginFilePath);
894
1058
  manifest.plugins = [
895
1059
  {
896
- pluginFile: apiPluginRelativePath,
1060
+ file: apiPluginRelativePath,
1061
+ id: ConstantString.DefaultPluginId,
897
1062
  },
898
1063
  ];
899
1064
  const appName = this.removeEnvs(manifest.name.short);
900
1065
  ManifestUpdater.updateManifestDescription(manifest, spec);
901
1066
  const specRelativePath = ManifestUpdater.getRelativePath(manifestPath, outputSpecPath);
902
- const apiPlugin = ManifestUpdater.generatePluginManifestSchema(spec, specRelativePath, appName, options);
1067
+ const apiPlugin = await ManifestUpdater.generatePluginManifestSchema(spec, specRelativePath, apiPluginFilePath, appName, options);
903
1068
  return [manifest, apiPlugin];
904
1069
  }
905
1070
  static updateManifestDescription(manifest, spec) {
@@ -923,8 +1088,8 @@ class ManifestUpdater {
923
1088
  }
924
1089
  return parameter;
925
1090
  }
926
- static generatePluginManifestSchema(spec, specRelativePath, appName, options) {
927
- var _a, _b, _c;
1091
+ static async generatePluginManifestSchema(spec, specRelativePath, apiPluginFilePath, appName, options) {
1092
+ var _a, _b, _c, _d;
928
1093
  const functions = [];
929
1094
  const functionNames = [];
930
1095
  const paths = spec.paths;
@@ -986,24 +1151,53 @@ class ManifestUpdater {
986
1151
  }
987
1152
  }
988
1153
  }
989
- const apiPlugin = {
990
- schema_version: "v2",
991
- name_for_human: appName,
992
- description_for_human: (_c = spec.info.description) !== null && _c !== void 0 ? _c : "<Please add description of the plugin>",
993
- functions: functions,
994
- runtimes: [
995
- {
996
- type: "OpenApi",
997
- auth: {
998
- type: "none", // TODO, support auth in the future
999
- },
1000
- spec: {
1001
- url: specRelativePath,
1002
- },
1003
- run_for_functions: functionNames,
1154
+ let apiPlugin;
1155
+ if (await fs.pathExists(apiPluginFilePath)) {
1156
+ apiPlugin = await fs.readJSON(apiPluginFilePath);
1157
+ }
1158
+ else {
1159
+ apiPlugin = {
1160
+ schema_version: "v2",
1161
+ name_for_human: "",
1162
+ description_for_human: "",
1163
+ functions: [],
1164
+ runtimes: [],
1165
+ };
1166
+ }
1167
+ apiPlugin.functions = apiPlugin.functions || [];
1168
+ for (const func of functions) {
1169
+ const index = (_c = apiPlugin.functions) === null || _c === void 0 ? void 0 : _c.findIndex((f) => f.name === func.name);
1170
+ if (index === -1) {
1171
+ apiPlugin.functions.push(func);
1172
+ }
1173
+ else {
1174
+ apiPlugin.functions[index] = func;
1175
+ }
1176
+ }
1177
+ apiPlugin.runtimes = apiPlugin.runtimes || [];
1178
+ const index = apiPlugin.runtimes.findIndex((runtime) => runtime.spec.url === specRelativePath);
1179
+ if (index === -1) {
1180
+ apiPlugin.runtimes.push({
1181
+ type: "OpenApi",
1182
+ auth: {
1183
+ type: "none",
1004
1184
  },
1005
- ],
1006
- };
1185
+ spec: {
1186
+ url: specRelativePath,
1187
+ },
1188
+ run_for_functions: functionNames,
1189
+ });
1190
+ }
1191
+ else {
1192
+ apiPlugin.runtimes[index].run_for_functions = functionNames;
1193
+ }
1194
+ if (!apiPlugin.name_for_human) {
1195
+ apiPlugin.name_for_human = appName;
1196
+ }
1197
+ if (!apiPlugin.description_for_human) {
1198
+ apiPlugin.description_for_human =
1199
+ (_d = spec.info.description) !== null && _d !== void 0 ? _d : "<Please add description of the plugin>";
1200
+ }
1007
1201
  return apiPlugin;
1008
1202
  }
1009
1203
  static async updateManifest(manifestPath, outputSpecPath, spec, options, adaptiveCardFolder, authInfo) {
@@ -1072,16 +1266,26 @@ class ManifestUpdater {
1072
1266
  if ((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) {
1073
1267
  const operationItem = operations[method];
1074
1268
  if (operationItem) {
1075
- const [command, warning] = Utils.parseApiInfo(operationItem, options);
1269
+ const command = Utils.parseApiInfo(operationItem, options);
1270
+ if (command.parameters &&
1271
+ command.parameters.length >= 1 &&
1272
+ command.parameters.some((param) => param.isRequired)) {
1273
+ command.parameters = command.parameters.filter((param) => param.isRequired);
1274
+ }
1275
+ else if (command.parameters && command.parameters.length > 0) {
1276
+ command.parameters = [command.parameters[0]];
1277
+ warnings.push({
1278
+ type: WarningType.OperationOnlyContainsOptionalParam,
1279
+ content: Utils.format(ConstantString.OperationOnlyContainsOptionalParam, command.id),
1280
+ data: command.id,
1281
+ });
1282
+ }
1076
1283
  if (adaptiveCardFolder) {
1077
1284
  const adaptiveCardPath = path.join(adaptiveCardFolder, command.id + ".json");
1078
1285
  command.apiResponseRenderingTemplateFile = (await fs.pathExists(adaptiveCardPath))
1079
1286
  ? ManifestUpdater.getRelativePath(manifestPath, adaptiveCardPath)
1080
1287
  : "";
1081
1288
  }
1082
- if (warning) {
1083
- warnings.push(warning);
1084
- }
1085
1289
  commands.push(command);
1086
1290
  }
1087
1291
  }
@@ -1398,6 +1602,8 @@ class SpecParser {
1398
1602
  errors: [{ type: ErrorType.SpecNotValid, content: e.toString() }],
1399
1603
  };
1400
1604
  }
1605
+ const errors = [];
1606
+ const warnings = [];
1401
1607
  if (!this.options.allowSwagger && this.isSwaggerFile) {
1402
1608
  return {
1403
1609
  status: ValidationStatus.Error,
@@ -1407,23 +1613,38 @@ class SpecParser {
1407
1613
  ],
1408
1614
  };
1409
1615
  }
1410
- if (this.options.projectType === ProjectType.SME ||
1411
- this.options.projectType === ProjectType.Copilot) {
1412
- if (this.spec.openapi >= "3.1.0") {
1413
- return {
1414
- status: ValidationStatus.Error,
1415
- warnings: [],
1416
- errors: [
1417
- {
1418
- type: ErrorType.SpecVersionNotSupported,
1419
- content: Utils.format(ConstantString.SpecVersionNotSupported, this.spec.openapi),
1420
- data: this.spec.openapi,
1421
- },
1422
- ],
1423
- };
1424
- }
1616
+ // Remote reference not supported
1617
+ const refPaths = this.parser.$refs.paths();
1618
+ // refPaths [0] is the current spec file path
1619
+ if (refPaths.length > 1) {
1620
+ errors.push({
1621
+ type: ErrorType.RemoteRefNotSupported,
1622
+ content: Utils.format(ConstantString.RemoteRefNotSupported, refPaths.join(", ")),
1623
+ data: refPaths,
1624
+ });
1625
+ }
1626
+ if (!!this.isSwaggerFile && this.options.allowSwagger) {
1627
+ warnings.push({
1628
+ type: WarningType.ConvertSwaggerToOpenAPI,
1629
+ content: ConstantString.ConvertSwaggerToOpenAPI,
1630
+ });
1425
1631
  }
1426
- return Utils.validateSpec(this.spec, this.parser, !!this.isSwaggerFile, this.options);
1632
+ const validator = this.getValidator(this.spec);
1633
+ const validationResult = validator.validateSpec();
1634
+ warnings.push(...validationResult.warnings);
1635
+ errors.push(...validationResult.errors);
1636
+ let status = ValidationStatus.Valid;
1637
+ if (warnings.length > 0 && errors.length === 0) {
1638
+ status = ValidationStatus.Warning;
1639
+ }
1640
+ else if (errors.length > 0) {
1641
+ status = ValidationStatus.Error;
1642
+ }
1643
+ return {
1644
+ status: status,
1645
+ warnings: warnings,
1646
+ errors: errors,
1647
+ };
1427
1648
  }
1428
1649
  catch (err) {
1429
1650
  throw new SpecParserError(err.toString(), ErrorType.ValidateFailed);
@@ -1452,34 +1673,27 @@ class SpecParser {
1452
1673
  for (const apiKey in apiMap) {
1453
1674
  const { operation, isValid, reason } = apiMap[apiKey];
1454
1675
  const [method, path] = apiKey.split(" ");
1676
+ const operationId = (_a = operation.operationId) !== null && _a !== void 0 ? _a : `${method.toLowerCase()}${Utils.convertPathToCamelCase(path)}`;
1455
1677
  const apiResult = {
1456
- api: "",
1678
+ api: apiKey,
1457
1679
  server: "",
1458
- operationId: "",
1680
+ operationId: operationId,
1459
1681
  isValid: isValid,
1460
1682
  reason: reason,
1461
1683
  };
1462
- const rootServer = spec.servers && spec.servers[0];
1463
- const methodServer = spec.paths[path].servers && ((_a = spec.paths[path]) === null || _a === void 0 ? void 0 : _a.servers[0]);
1464
- const operationServer = operation.servers && operation.servers[0];
1465
- const serverUrl = operationServer || methodServer || rootServer;
1466
- if (!serverUrl) {
1467
- throw new SpecParserError(ConstantString.NoServerInformation, ErrorType.NoServerInformation);
1468
- }
1469
- apiResult.server = Utils.resolveEnv(serverUrl.url);
1470
- let operationId = operation.operationId;
1471
- if (!operationId) {
1472
- operationId = `${method.toLowerCase()}${Utils.convertPathToCamelCase(path)}`;
1473
- }
1474
- apiResult.operationId = operationId;
1475
- const authArray = Utils.getAuthArray(operation.security, spec);
1476
- for (const auths of authArray) {
1477
- if (auths.length === 1) {
1478
- apiResult.auth = auths[0];
1479
- break;
1684
+ if (isValid) {
1685
+ const serverObj = Utils.getServerObject(spec, method.toLocaleLowerCase(), path);
1686
+ if (serverObj) {
1687
+ apiResult.server = Utils.resolveEnv(serverObj.url);
1688
+ }
1689
+ const authArray = Utils.getAuthArray(operation.security, spec);
1690
+ for (const auths of authArray) {
1691
+ if (auths.length === 1) {
1692
+ apiResult.auth = auths[0];
1693
+ break;
1694
+ }
1480
1695
  }
1481
1696
  }
1482
- apiResult.api = apiKey;
1483
1697
  result.APIs.push(apiResult);
1484
1698
  }
1485
1699
  result.allAPICount = result.APIs.length;
@@ -1659,12 +1873,18 @@ class SpecParser {
1659
1873
  }
1660
1874
  }
1661
1875
  getAPIs(spec) {
1662
- if (this.apiMap !== undefined) {
1663
- return this.apiMap;
1876
+ const validator = this.getValidator(spec);
1877
+ const apiMap = validator.listAPIs();
1878
+ this.apiMap = apiMap;
1879
+ return apiMap;
1880
+ }
1881
+ getValidator(spec) {
1882
+ if (this.validator) {
1883
+ return this.validator;
1664
1884
  }
1665
- const result = Utils.listAPIs(spec, this.options);
1666
- this.apiMap = result;
1667
- return result;
1885
+ const validator = ValidatorFactory.create(spec, this.options);
1886
+ this.validator = validator;
1887
+ return validator;
1668
1888
  }
1669
1889
  }
1670
1890