@microsoft/m365-spec-parser 0.1.1-alpha.4cb5c08a8.0 → 0.1.1-alpha.4e708f092.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.
@@ -145,6 +145,7 @@ ConstantString.AdaptiveCardVersion = "1.5";
145
145
  ConstantString.AdaptiveCardSchema = "http://adaptivecards.io/schemas/adaptive-card.json";
146
146
  ConstantString.AdaptiveCardType = "AdaptiveCard";
147
147
  ConstantString.TextBlockType = "TextBlock";
148
+ ConstantString.ImageType = "Image";
148
149
  ConstantString.ContainerType = "Container";
149
150
  ConstantString.RegistrationIdPostfix = "REGISTRATION_ID";
150
151
  ConstantString.OAuthRegistrationIdPostFix = "OAUTH_REGISTRATION_ID";
@@ -208,7 +209,8 @@ ConstantString.CommandDescriptionMaxLens = 128;
208
209
  ConstantString.ParameterDescriptionMaxLens = 128;
209
210
  ConstantString.CommandTitleMaxLens = 32;
210
211
  ConstantString.ParameterTitleMaxLens = 32;
211
- ConstantString.SMERequiredParamsMaxNum = 5;
212
+ ConstantString.SMERequiredParamsMaxNum = 5;
213
+ ConstantString.DefaultPluginId = "plugin_1";
212
214
 
213
215
  // Copyright (c) Microsoft Corporation.
214
216
  class SpecParserError extends Error {
@@ -231,249 +233,9 @@ class Utils {
231
233
  }
232
234
  return false;
233
235
  }
234
- static checkParameters(paramObject, isCopilot) {
235
- const paramResult = {
236
- requiredNum: 0,
237
- optionalNum: 0,
238
- isValid: true,
239
- reason: [],
240
- };
241
- if (!paramObject) {
242
- return paramResult;
243
- }
244
- for (let i = 0; i < paramObject.length; i++) {
245
- const param = paramObject[i];
246
- const schema = param.schema;
247
- if (isCopilot && this.hasNestedObjectInSchema(schema)) {
248
- paramResult.isValid = false;
249
- paramResult.reason.push(exports.ErrorType.ParamsContainsNestedObject);
250
- continue;
251
- }
252
- const isRequiredWithoutDefault = param.required && schema.default === undefined;
253
- if (isCopilot) {
254
- if (isRequiredWithoutDefault) {
255
- paramResult.requiredNum = paramResult.requiredNum + 1;
256
- }
257
- else {
258
- paramResult.optionalNum = paramResult.optionalNum + 1;
259
- }
260
- continue;
261
- }
262
- if (param.in === "header" || param.in === "cookie") {
263
- if (isRequiredWithoutDefault) {
264
- paramResult.isValid = false;
265
- paramResult.reason.push(exports.ErrorType.ParamsContainRequiredUnsupportedSchema);
266
- }
267
- continue;
268
- }
269
- if (schema.type !== "boolean" &&
270
- schema.type !== "string" &&
271
- schema.type !== "number" &&
272
- schema.type !== "integer") {
273
- if (isRequiredWithoutDefault) {
274
- paramResult.isValid = false;
275
- paramResult.reason.push(exports.ErrorType.ParamsContainRequiredUnsupportedSchema);
276
- }
277
- continue;
278
- }
279
- if (param.in === "query" || param.in === "path") {
280
- if (isRequiredWithoutDefault) {
281
- paramResult.requiredNum = paramResult.requiredNum + 1;
282
- }
283
- else {
284
- paramResult.optionalNum = paramResult.optionalNum + 1;
285
- }
286
- }
287
- }
288
- return paramResult;
289
- }
290
- static checkPostBody(schema, isRequired = false, isCopilot = false) {
291
- var _a;
292
- const paramResult = {
293
- requiredNum: 0,
294
- optionalNum: 0,
295
- isValid: true,
296
- reason: [],
297
- };
298
- if (Object.keys(schema).length === 0) {
299
- return paramResult;
300
- }
301
- const isRequiredWithoutDefault = isRequired && schema.default === undefined;
302
- if (isCopilot && this.hasNestedObjectInSchema(schema)) {
303
- paramResult.isValid = false;
304
- paramResult.reason = [exports.ErrorType.RequestBodyContainsNestedObject];
305
- return paramResult;
306
- }
307
- if (schema.type === "string" ||
308
- schema.type === "integer" ||
309
- schema.type === "boolean" ||
310
- schema.type === "number") {
311
- if (isRequiredWithoutDefault) {
312
- paramResult.requiredNum = paramResult.requiredNum + 1;
313
- }
314
- else {
315
- paramResult.optionalNum = paramResult.optionalNum + 1;
316
- }
317
- }
318
- else if (schema.type === "object") {
319
- const { properties } = schema;
320
- for (const property in properties) {
321
- let isRequired = false;
322
- if (schema.required && ((_a = schema.required) === null || _a === void 0 ? void 0 : _a.indexOf(property)) >= 0) {
323
- isRequired = true;
324
- }
325
- const result = Utils.checkPostBody(properties[property], isRequired, isCopilot);
326
- paramResult.requiredNum += result.requiredNum;
327
- paramResult.optionalNum += result.optionalNum;
328
- paramResult.isValid = paramResult.isValid && result.isValid;
329
- paramResult.reason.push(...result.reason);
330
- }
331
- }
332
- else {
333
- if (isRequiredWithoutDefault && !isCopilot) {
334
- paramResult.isValid = false;
335
- paramResult.reason.push(exports.ErrorType.PostBodyContainsRequiredUnsupportedSchema);
336
- }
337
- }
338
- return paramResult;
339
- }
340
236
  static containMultipleMediaTypes(bodyObject) {
341
237
  return Object.keys((bodyObject === null || bodyObject === void 0 ? void 0 : bodyObject.content) || {}).length > 1;
342
238
  }
343
- /**
344
- * Checks if the given API is supported.
345
- * @param {string} method - The HTTP method of the API.
346
- * @param {string} path - The path of the API.
347
- * @param {OpenAPIV3.Document} spec - The OpenAPI specification document.
348
- * @returns {boolean} - Returns true if the API is supported, false otherwise.
349
- * @description The following APIs are supported:
350
- * 1. only support Get/Post operation without auth property
351
- * 2. parameter inside query or path only support string, number, boolean and integer
352
- * 3. parameter inside post body only support string, number, boolean, integer and object
353
- * 4. request body + required parameters <= 1
354
- * 5. response body should be “application/json” and not empty, and response code should be 20X
355
- * 6. only support request body with “application/json” content type
356
- */
357
- static isSupportedApi(method, path, spec, options) {
358
- var _a;
359
- const result = { isValid: true, reason: [] };
360
- method = method.toLocaleLowerCase();
361
- if (options.allowMethods && !options.allowMethods.includes(method)) {
362
- result.isValid = false;
363
- result.reason.push(exports.ErrorType.MethodNotAllowed);
364
- return result;
365
- }
366
- const pathObj = spec.paths[path];
367
- if (!pathObj || !pathObj[method]) {
368
- result.isValid = false;
369
- result.reason.push(exports.ErrorType.UrlPathNotExist);
370
- return result;
371
- }
372
- const securities = pathObj[method].security;
373
- const isTeamsAi = options.projectType === exports.ProjectType.TeamsAi;
374
- const isCopilot = options.projectType === exports.ProjectType.Copilot;
375
- // Teams AI project doesn't care about auth, it will use authProvider for user to implement
376
- if (!isTeamsAi) {
377
- const authArray = Utils.getAuthArray(securities, spec);
378
- const authCheckResult = Utils.isSupportedAuth(authArray, options);
379
- if (!authCheckResult.isValid) {
380
- result.reason.push(...authCheckResult.reason);
381
- }
382
- }
383
- const operationObject = pathObj[method];
384
- if (!options.allowMissingId && !operationObject.operationId) {
385
- result.reason.push(exports.ErrorType.MissingOperationId);
386
- }
387
- const rootServer = spec.servers && spec.servers[0];
388
- const methodServer = spec.paths[path].servers && ((_a = spec.paths[path]) === null || _a === void 0 ? void 0 : _a.servers[0]);
389
- const operationServer = operationObject.servers && operationObject.servers[0];
390
- const serverUrl = operationServer || methodServer || rootServer;
391
- if (!serverUrl) {
392
- result.reason.push(exports.ErrorType.NoServerInformation);
393
- }
394
- else {
395
- const serverValidateResult = Utils.checkServerUrl([serverUrl]);
396
- result.reason.push(...serverValidateResult.map((item) => item.type));
397
- }
398
- const paramObject = operationObject.parameters;
399
- const requestBody = operationObject.requestBody;
400
- const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
401
- if (!isTeamsAi && Utils.containMultipleMediaTypes(requestBody)) {
402
- result.reason.push(exports.ErrorType.PostBodyContainMultipleMediaTypes);
403
- }
404
- const { json, multipleMediaType } = Utils.getResponseJson(operationObject, isTeamsAi);
405
- if (multipleMediaType && !isTeamsAi) {
406
- result.reason.push(exports.ErrorType.ResponseContainMultipleMediaTypes);
407
- }
408
- else if (Object.keys(json).length === 0) {
409
- result.reason.push(exports.ErrorType.ResponseJsonIsEmpty);
410
- }
411
- // Teams AI project doesn't care about request parameters/body
412
- if (!isTeamsAi) {
413
- let requestBodyParamResult = {
414
- requiredNum: 0,
415
- optionalNum: 0,
416
- isValid: true,
417
- reason: [],
418
- };
419
- if (requestJsonBody) {
420
- const requestBodySchema = requestJsonBody.schema;
421
- if (isCopilot && requestBodySchema.type !== "object") {
422
- result.reason.push(exports.ErrorType.PostBodySchemaIsNotJson);
423
- }
424
- requestBodyParamResult = Utils.checkPostBody(requestBodySchema, requestBody.required, isCopilot);
425
- if (!requestBodyParamResult.isValid && requestBodyParamResult.reason) {
426
- result.reason.push(...requestBodyParamResult.reason);
427
- }
428
- }
429
- const paramResult = Utils.checkParameters(paramObject, isCopilot);
430
- if (!paramResult.isValid && paramResult.reason) {
431
- result.reason.push(...paramResult.reason);
432
- }
433
- // Copilot support arbitrary parameters
434
- if (!isCopilot && paramResult.isValid && requestBodyParamResult.isValid) {
435
- const totalRequiredParams = requestBodyParamResult.requiredNum + paramResult.requiredNum;
436
- const totalParams = totalRequiredParams + requestBodyParamResult.optionalNum + paramResult.optionalNum;
437
- if (totalRequiredParams > 1) {
438
- if (!options.allowMultipleParameters ||
439
- totalRequiredParams > ConstantString.SMERequiredParamsMaxNum) {
440
- result.reason.push(exports.ErrorType.ExceededRequiredParamsLimit);
441
- }
442
- }
443
- else if (totalParams === 0) {
444
- result.reason.push(exports.ErrorType.NoParameter);
445
- }
446
- }
447
- }
448
- if (result.reason.length > 0) {
449
- result.isValid = false;
450
- }
451
- return result;
452
- }
453
- static isSupportedAuth(authSchemeArray, options) {
454
- if (authSchemeArray.length === 0) {
455
- return { isValid: true, reason: [] };
456
- }
457
- if (options.allowAPIKeyAuth || options.allowOauth2 || options.allowBearerTokenAuth) {
458
- // Currently we don't support multiple auth in one operation
459
- if (authSchemeArray.length > 0 && authSchemeArray.every((auths) => auths.length > 1)) {
460
- return {
461
- isValid: false,
462
- reason: [exports.ErrorType.MultipleAuthNotSupported],
463
- };
464
- }
465
- for (const auths of authSchemeArray) {
466
- if (auths.length === 1) {
467
- if ((options.allowAPIKeyAuth && Utils.isAPIKeyAuth(auths[0].authScheme)) ||
468
- (options.allowOauth2 && Utils.isOAuthWithAuthCodeFlow(auths[0].authScheme)) ||
469
- (options.allowBearerTokenAuth && Utils.isBearerTokenAuth(auths[0].authScheme))) {
470
- return { isValid: true, reason: [] };
471
- }
472
- }
473
- }
474
- }
475
- return { isValid: false, reason: [exports.ErrorType.AuthTypeIsNotSupported] };
476
- }
477
239
  static isBearerTokenAuth(authScheme) {
478
240
  return authScheme.type === "http" && authScheme.scheme === "bearer";
479
241
  }
@@ -481,10 +243,9 @@ class Utils {
481
243
  return authScheme.type === "apiKey";
482
244
  }
483
245
  static isOAuthWithAuthCodeFlow(authScheme) {
484
- if (authScheme.type === "oauth2" && authScheme.flows && authScheme.flows.authorizationCode) {
485
- return true;
486
- }
487
- return false;
246
+ return !!(authScheme.type === "oauth2" &&
247
+ authScheme.flows &&
248
+ authScheme.flows.authorizationCode);
488
249
  }
489
250
  static getAuthArray(securities, spec) {
490
251
  var _a;
@@ -512,7 +273,7 @@ class Utils {
512
273
  static updateFirstLetter(str) {
513
274
  return str.charAt(0).toUpperCase() + str.slice(1);
514
275
  }
515
- static getResponseJson(operationObject, isTeamsAiProject = false) {
276
+ static getResponseJson(operationObject) {
516
277
  var _a, _b;
517
278
  let json = {};
518
279
  let multipleMediaType = false;
@@ -523,9 +284,6 @@ class Utils {
523
284
  json = responseObject.content["application/json"];
524
285
  if (Utils.containMultipleMediaTypes(responseObject)) {
525
286
  multipleMediaType = true;
526
- if (isTeamsAiProject) {
527
- break;
528
- }
529
287
  json = {};
530
288
  }
531
289
  else {
@@ -753,13 +511,7 @@ class Utils {
753
511
  }
754
512
  }
755
513
  const operationId = operationItem.operationId;
756
- const parameters = [];
757
- if (requiredParams.length !== 0) {
758
- parameters.push(...requiredParams);
759
- }
760
- else {
761
- parameters.push(optionalParams[0]);
762
- }
514
+ const parameters = [...requiredParams, ...optionalParams];
763
515
  const command = {
764
516
  context: ["compose"],
765
517
  type: "query",
@@ -768,26 +520,51 @@ class Utils {
768
520
  parameters: parameters,
769
521
  description: ((_b = operationItem.description) !== null && _b !== void 0 ? _b : "").slice(0, ConstantString.CommandDescriptionMaxLens),
770
522
  };
771
- let warning = undefined;
772
- if (requiredParams.length === 0 && optionalParams.length > 1) {
773
- warning = {
774
- type: exports.WarningType.OperationOnlyContainsOptionalParam,
775
- content: Utils.format(ConstantString.OperationOnlyContainsOptionalParam, operationId),
776
- data: operationId,
777
- };
523
+ return command;
524
+ }
525
+ static format(str, ...args) {
526
+ let index = 0;
527
+ return str.replace(/%s/g, () => {
528
+ const arg = args[index++];
529
+ return arg !== undefined ? arg : "";
530
+ });
531
+ }
532
+ static getSafeRegistrationIdEnvName(authName) {
533
+ if (!authName) {
534
+ return "";
778
535
  }
779
- return [command, warning];
536
+ let safeRegistrationIdEnvName = authName.toUpperCase().replace(/[^A-Z0-9_]/g, "_");
537
+ if (!safeRegistrationIdEnvName.match(/^[A-Z]/)) {
538
+ safeRegistrationIdEnvName = "PREFIX_" + safeRegistrationIdEnvName;
539
+ }
540
+ return safeRegistrationIdEnvName;
541
+ }
542
+ static getServerObject(spec, method, path) {
543
+ const pathObj = spec.paths[path];
544
+ const operationObject = pathObj[method];
545
+ const rootServer = spec.servers && spec.servers[0];
546
+ const methodServer = spec.paths[path].servers && spec.paths[path].servers[0];
547
+ const operationServer = operationObject.servers && operationObject.servers[0];
548
+ const serverUrl = operationServer || methodServer || rootServer;
549
+ return serverUrl;
780
550
  }
781
- static listAPIs(spec, options) {
551
+ }
552
+
553
+ // Copyright (c) Microsoft Corporation.
554
+ class Validator {
555
+ listAPIs() {
782
556
  var _a;
783
- const paths = spec.paths;
557
+ if (this.apiMap) {
558
+ return this.apiMap;
559
+ }
560
+ const paths = this.spec.paths;
784
561
  const result = {};
785
562
  for (const path in paths) {
786
563
  const methods = paths[path];
787
564
  for (const method in methods) {
788
565
  const operationObject = methods[method];
789
- if (((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
790
- const validateResult = Utils.isSupportedApi(method, path, spec, options);
566
+ if (((_a = this.options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
567
+ const validateResult = this.validateAPI(method, path);
791
568
  result[`${method.toUpperCase()} ${path}`] = {
792
569
  operation: operationObject,
793
570
  isValid: validateResult.isValid,
@@ -796,38 +573,48 @@ class Utils {
796
573
  }
797
574
  }
798
575
  }
576
+ this.apiMap = result;
799
577
  return result;
800
578
  }
801
- static validateSpec(spec, parser, isSwaggerFile, options) {
802
- const errors = [];
803
- const warnings = [];
804
- const apiMap = Utils.listAPIs(spec, options);
805
- if (isSwaggerFile) {
806
- warnings.push({
807
- type: exports.WarningType.ConvertSwaggerToOpenAPI,
808
- content: ConstantString.ConvertSwaggerToOpenAPI,
579
+ validateSpecVersion() {
580
+ const result = { errors: [], warnings: [] };
581
+ if (this.spec.openapi >= "3.1.0") {
582
+ result.errors.push({
583
+ type: exports.ErrorType.SpecVersionNotSupported,
584
+ content: Utils.format(ConstantString.SpecVersionNotSupported, this.spec.openapi),
585
+ data: this.spec.openapi,
809
586
  });
810
587
  }
811
- const serverErrors = Utils.validateServer(spec, options);
812
- errors.push(...serverErrors);
813
- // Remote reference not supported
814
- const refPaths = parser.$refs.paths();
815
- // refPaths [0] is the current spec file path
816
- if (refPaths.length > 1) {
817
- errors.push({
818
- type: exports.ErrorType.RemoteRefNotSupported,
819
- content: Utils.format(ConstantString.RemoteRefNotSupported, refPaths.join(", ")),
820
- data: refPaths,
821
- });
822
- }
823
- // No supported API
588
+ return result;
589
+ }
590
+ validateSpecServer() {
591
+ const result = { errors: [], warnings: [] };
592
+ const serverErrors = Utils.validateServer(this.spec, this.options);
593
+ result.errors.push(...serverErrors);
594
+ return result;
595
+ }
596
+ validateSpecNoSupportAPI() {
597
+ const result = { errors: [], warnings: [] };
598
+ const apiMap = this.listAPIs();
824
599
  const validAPIs = Object.entries(apiMap).filter(([, value]) => value.isValid);
825
600
  if (validAPIs.length === 0) {
826
- errors.push({
601
+ const data = [];
602
+ for (const key in apiMap) {
603
+ const { reason } = apiMap[key];
604
+ const apiInvalidReason = { api: key, reason: reason };
605
+ data.push(apiInvalidReason);
606
+ }
607
+ result.errors.push({
827
608
  type: exports.ErrorType.NoSupportedApi,
828
609
  content: ConstantString.NoSupportedApi,
610
+ data,
829
611
  });
830
612
  }
613
+ return result;
614
+ }
615
+ validateSpecOperationId() {
616
+ const result = { errors: [], warnings: [] };
617
+ const apiMap = this.listAPIs();
831
618
  // OperationId missing
832
619
  const apisMissingOperationId = [];
833
620
  for (const key in apiMap) {
@@ -837,320 +624,472 @@ class Utils {
837
624
  }
838
625
  }
839
626
  if (apisMissingOperationId.length > 0) {
840
- warnings.push({
627
+ result.warnings.push({
841
628
  type: exports.WarningType.OperationIdMissing,
842
629
  content: Utils.format(ConstantString.MissingOperationId, apisMissingOperationId.join(", ")),
843
630
  data: apisMissingOperationId,
844
631
  });
845
632
  }
846
- let status = exports.ValidationStatus.Valid;
847
- if (warnings.length > 0 && errors.length === 0) {
848
- status = exports.ValidationStatus.Warning;
633
+ return result;
634
+ }
635
+ validateMethodAndPath(method, path) {
636
+ const result = { isValid: true, reason: [] };
637
+ if (this.options.allowMethods && !this.options.allowMethods.includes(method)) {
638
+ result.isValid = false;
639
+ result.reason.push(exports.ErrorType.MethodNotAllowed);
640
+ return result;
849
641
  }
850
- else if (errors.length > 0) {
851
- status = exports.ValidationStatus.Error;
642
+ const pathObj = this.spec.paths[path];
643
+ if (!pathObj || !pathObj[method]) {
644
+ result.isValid = false;
645
+ result.reason.push(exports.ErrorType.UrlPathNotExist);
646
+ return result;
852
647
  }
853
- return {
854
- status,
855
- warnings,
856
- errors,
857
- };
858
- }
859
- static format(str, ...args) {
860
- let index = 0;
861
- return str.replace(/%s/g, () => {
862
- const arg = args[index++];
863
- return arg !== undefined ? arg : "";
864
- });
648
+ return result;
865
649
  }
866
- static getSafeRegistrationIdEnvName(authName) {
867
- if (!authName) {
868
- return "";
650
+ validateResponse(method, path) {
651
+ const result = { isValid: true, reason: [] };
652
+ const operationObject = this.spec.paths[path][method];
653
+ const { json, multipleMediaType } = Utils.getResponseJson(operationObject);
654
+ // only support response body only contains “application/json” content type
655
+ if (multipleMediaType) {
656
+ result.reason.push(exports.ErrorType.ResponseContainMultipleMediaTypes);
869
657
  }
870
- let safeRegistrationIdEnvName = authName.toUpperCase().replace(/[^A-Z0-9_]/g, "_");
871
- if (!safeRegistrationIdEnvName.match(/^[A-Z]/)) {
872
- safeRegistrationIdEnvName = "PREFIX_" + safeRegistrationIdEnvName;
658
+ else if (Object.keys(json).length === 0) {
659
+ // response body should not be empty
660
+ result.reason.push(exports.ErrorType.ResponseJsonIsEmpty);
873
661
  }
874
- return safeRegistrationIdEnvName;
662
+ return result;
875
663
  }
876
- static getAllAPICount(spec) {
877
- let count = 0;
878
- const paths = spec.paths;
879
- for (const path in paths) {
880
- const methods = paths[path];
881
- for (const method in methods) {
882
- if (ConstantString.AllOperationMethods.includes(method)) {
883
- count++;
664
+ validateServer(method, path) {
665
+ const result = { isValid: true, reason: [] };
666
+ const serverObj = Utils.getServerObject(this.spec, method, path);
667
+ if (!serverObj) {
668
+ // should contain server URL
669
+ result.reason.push(exports.ErrorType.NoServerInformation);
670
+ }
671
+ else {
672
+ // server url should be absolute url with https protocol
673
+ const serverValidateResult = Utils.checkServerUrl([serverObj]);
674
+ result.reason.push(...serverValidateResult.map((item) => item.type));
675
+ }
676
+ return result;
677
+ }
678
+ validateAuth(method, path) {
679
+ const pathObj = this.spec.paths[path];
680
+ const operationObject = pathObj[method];
681
+ const securities = operationObject.security;
682
+ const authSchemeArray = Utils.getAuthArray(securities, this.spec);
683
+ if (authSchemeArray.length === 0) {
684
+ return { isValid: true, reason: [] };
685
+ }
686
+ if (this.options.allowAPIKeyAuth ||
687
+ this.options.allowOauth2 ||
688
+ this.options.allowBearerTokenAuth) {
689
+ // Currently we don't support multiple auth in one operation
690
+ if (authSchemeArray.length > 0 && authSchemeArray.every((auths) => auths.length > 1)) {
691
+ return {
692
+ isValid: false,
693
+ reason: [exports.ErrorType.MultipleAuthNotSupported],
694
+ };
695
+ }
696
+ for (const auths of authSchemeArray) {
697
+ if (auths.length === 1) {
698
+ if ((this.options.allowAPIKeyAuth && Utils.isAPIKeyAuth(auths[0].authScheme)) ||
699
+ (this.options.allowOauth2 && Utils.isOAuthWithAuthCodeFlow(auths[0].authScheme)) ||
700
+ (this.options.allowBearerTokenAuth && Utils.isBearerTokenAuth(auths[0].authScheme))) {
701
+ return { isValid: true, reason: [] };
702
+ }
884
703
  }
885
704
  }
886
705
  }
887
- return count;
706
+ return { isValid: false, reason: [exports.ErrorType.AuthTypeIsNotSupported] };
888
707
  }
889
- }
890
-
891
- // Copyright (c) Microsoft Corporation.
892
- class SpecFilter {
893
- static specFilter(filter, unResolveSpec, resolvedSpec, options) {
708
+ checkPostBodySchema(schema, isRequired = false) {
894
709
  var _a;
895
- try {
896
- const newSpec = Object.assign({}, unResolveSpec);
897
- const newPaths = {};
898
- for (const filterItem of filter) {
899
- const [method, path] = filterItem.split(" ");
900
- const methodName = method.toLowerCase();
901
- const pathObj = (_a = resolvedSpec.paths) === null || _a === void 0 ? void 0 : _a[path];
902
- if (ConstantString.AllOperationMethods.includes(methodName) &&
903
- pathObj &&
904
- pathObj[methodName]) {
905
- const validateResult = Utils.isSupportedApi(methodName, path, resolvedSpec, options);
906
- if (!validateResult.isValid) {
907
- continue;
908
- }
909
- if (!newPaths[path]) {
910
- newPaths[path] = Object.assign({}, unResolveSpec.paths[path]);
911
- for (const m of ConstantString.AllOperationMethods) {
912
- delete newPaths[path][m];
913
- }
914
- }
915
- newPaths[path][methodName] = unResolveSpec.paths[path][methodName];
916
- // Add the operationId if missing
917
- if (!newPaths[path][methodName].operationId) {
918
- newPaths[path][methodName].operationId = `${methodName}${Utils.convertPathToCamelCase(path)}`;
919
- }
710
+ const paramResult = {
711
+ requiredNum: 0,
712
+ optionalNum: 0,
713
+ isValid: true,
714
+ reason: [],
715
+ };
716
+ if (Object.keys(schema).length === 0) {
717
+ return paramResult;
718
+ }
719
+ const isRequiredWithoutDefault = isRequired && schema.default === undefined;
720
+ const isCopilot = this.projectType === exports.ProjectType.Copilot;
721
+ if (isCopilot && this.hasNestedObjectInSchema(schema)) {
722
+ paramResult.isValid = false;
723
+ paramResult.reason = [exports.ErrorType.RequestBodyContainsNestedObject];
724
+ return paramResult;
725
+ }
726
+ if (schema.type === "string" ||
727
+ schema.type === "integer" ||
728
+ schema.type === "boolean" ||
729
+ schema.type === "number") {
730
+ if (isRequiredWithoutDefault) {
731
+ paramResult.requiredNum = paramResult.requiredNum + 1;
732
+ }
733
+ else {
734
+ paramResult.optionalNum = paramResult.optionalNum + 1;
735
+ }
736
+ }
737
+ else if (schema.type === "object") {
738
+ const { properties } = schema;
739
+ for (const property in properties) {
740
+ let isRequired = false;
741
+ if (schema.required && ((_a = schema.required) === null || _a === void 0 ? void 0 : _a.indexOf(property)) >= 0) {
742
+ isRequired = true;
920
743
  }
744
+ const result = this.checkPostBodySchema(properties[property], isRequired);
745
+ paramResult.requiredNum += result.requiredNum;
746
+ paramResult.optionalNum += result.optionalNum;
747
+ paramResult.isValid = paramResult.isValid && result.isValid;
748
+ paramResult.reason.push(...result.reason);
921
749
  }
922
- newSpec.paths = newPaths;
923
- return newSpec;
924
750
  }
925
- catch (err) {
926
- throw new SpecParserError(err.toString(), exports.ErrorType.FilterSpecFailed);
751
+ else {
752
+ if (isRequiredWithoutDefault && !isCopilot) {
753
+ paramResult.isValid = false;
754
+ paramResult.reason.push(exports.ErrorType.PostBodyContainsRequiredUnsupportedSchema);
755
+ }
756
+ }
757
+ return paramResult;
758
+ }
759
+ checkParamSchema(paramObject) {
760
+ const paramResult = {
761
+ requiredNum: 0,
762
+ optionalNum: 0,
763
+ isValid: true,
764
+ reason: [],
765
+ };
766
+ if (!paramObject) {
767
+ return paramResult;
768
+ }
769
+ const isCopilot = this.projectType === exports.ProjectType.Copilot;
770
+ for (let i = 0; i < paramObject.length; i++) {
771
+ const param = paramObject[i];
772
+ const schema = param.schema;
773
+ if (isCopilot && this.hasNestedObjectInSchema(schema)) {
774
+ paramResult.isValid = false;
775
+ paramResult.reason.push(exports.ErrorType.ParamsContainsNestedObject);
776
+ continue;
777
+ }
778
+ const isRequiredWithoutDefault = param.required && schema.default === undefined;
779
+ if (isCopilot) {
780
+ if (isRequiredWithoutDefault) {
781
+ paramResult.requiredNum = paramResult.requiredNum + 1;
782
+ }
783
+ else {
784
+ paramResult.optionalNum = paramResult.optionalNum + 1;
785
+ }
786
+ continue;
787
+ }
788
+ if (param.in === "header" || param.in === "cookie") {
789
+ if (isRequiredWithoutDefault) {
790
+ paramResult.isValid = false;
791
+ paramResult.reason.push(exports.ErrorType.ParamsContainRequiredUnsupportedSchema);
792
+ }
793
+ continue;
794
+ }
795
+ if (schema.type !== "boolean" &&
796
+ schema.type !== "string" &&
797
+ schema.type !== "number" &&
798
+ schema.type !== "integer") {
799
+ if (isRequiredWithoutDefault) {
800
+ paramResult.isValid = false;
801
+ paramResult.reason.push(exports.ErrorType.ParamsContainRequiredUnsupportedSchema);
802
+ }
803
+ continue;
804
+ }
805
+ if (param.in === "query" || param.in === "path") {
806
+ if (isRequiredWithoutDefault) {
807
+ paramResult.requiredNum = paramResult.requiredNum + 1;
808
+ }
809
+ else {
810
+ paramResult.optionalNum = paramResult.optionalNum + 1;
811
+ }
812
+ }
927
813
  }
814
+ return paramResult;
815
+ }
816
+ hasNestedObjectInSchema(schema) {
817
+ if (schema.type === "object") {
818
+ for (const property in schema.properties) {
819
+ const nestedSchema = schema.properties[property];
820
+ if (nestedSchema.type === "object") {
821
+ return true;
822
+ }
823
+ }
824
+ }
825
+ return false;
928
826
  }
929
827
  }
930
828
 
931
829
  // Copyright (c) Microsoft Corporation.
932
- class ManifestUpdater {
933
- static updateManifestWithAiPlugin(manifestPath, outputSpecPath, apiPluginFilePath, spec, options) {
934
- return __awaiter(this, void 0, void 0, function* () {
935
- const manifest = yield fs__default['default'].readJSON(manifestPath);
936
- const apiPluginRelativePath = ManifestUpdater.getRelativePath(manifestPath, apiPluginFilePath);
937
- manifest.plugins = [
938
- {
939
- pluginFile: apiPluginRelativePath,
940
- },
941
- ];
942
- const appName = this.removeEnvs(manifest.name.short);
943
- ManifestUpdater.updateManifestDescription(manifest, spec);
944
- const specRelativePath = ManifestUpdater.getRelativePath(manifestPath, outputSpecPath);
945
- const apiPlugin = ManifestUpdater.generatePluginManifestSchema(spec, specRelativePath, appName, options);
946
- return [manifest, apiPlugin];
947
- });
830
+ class CopilotValidator extends Validator {
831
+ constructor(spec, options) {
832
+ super();
833
+ this.projectType = exports.ProjectType.Copilot;
834
+ this.options = options;
835
+ this.spec = spec;
948
836
  }
949
- static updateManifestDescription(manifest, spec) {
950
- var _a, _b;
951
- manifest.description = {
952
- short: spec.info.title.slice(0, ConstantString.ShortDescriptionMaxLens),
953
- full: (_b = ((_a = spec.info.description) !== null && _a !== void 0 ? _a : manifest.description.full)) === null || _b === void 0 ? void 0 : _b.slice(0, ConstantString.FullDescriptionMaxLens),
954
- };
837
+ validateSpec() {
838
+ const result = { errors: [], warnings: [] };
839
+ // validate spec version
840
+ let validationResult = this.validateSpecVersion();
841
+ result.errors.push(...validationResult.errors);
842
+ // validate spec server
843
+ validationResult = this.validateSpecServer();
844
+ result.errors.push(...validationResult.errors);
845
+ // validate no supported API
846
+ validationResult = this.validateSpecNoSupportAPI();
847
+ result.errors.push(...validationResult.errors);
848
+ // validate operationId missing
849
+ validationResult = this.validateSpecOperationId();
850
+ result.warnings.push(...validationResult.warnings);
851
+ return result;
955
852
  }
956
- static mapOpenAPISchemaToFuncParam(schema, method, pathUrl) {
957
- let parameter;
958
- if (schema.type === "string" ||
959
- schema.type === "boolean" ||
960
- schema.type === "integer" ||
961
- schema.type === "number" ||
962
- schema.type === "array") {
963
- parameter = schema;
853
+ validateAPI(method, path) {
854
+ const result = { isValid: true, reason: [] };
855
+ method = method.toLocaleLowerCase();
856
+ // validate method and path
857
+ const methodAndPathResult = this.validateMethodAndPath(method, path);
858
+ if (!methodAndPathResult.isValid) {
859
+ return methodAndPathResult;
860
+ }
861
+ const operationObject = this.spec.paths[path][method];
862
+ // validate auth
863
+ const authCheckResult = this.validateAuth(method, path);
864
+ result.reason.push(...authCheckResult.reason);
865
+ // validate operationId
866
+ if (!this.options.allowMissingId && !operationObject.operationId) {
867
+ result.reason.push(exports.ErrorType.MissingOperationId);
964
868
  }
965
- else {
966
- throw new SpecParserError(Utils.format(ConstantString.UnsupportedSchema, method, pathUrl, JSON.stringify(schema)), exports.ErrorType.UpdateManifestFailed);
869
+ // validate server
870
+ const validateServerResult = this.validateServer(method, path);
871
+ result.reason.push(...validateServerResult.reason);
872
+ // validate response
873
+ const validateResponseResult = this.validateResponse(method, path);
874
+ result.reason.push(...validateResponseResult.reason);
875
+ // validate requestBody
876
+ const requestBody = operationObject.requestBody;
877
+ const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
878
+ if (Utils.containMultipleMediaTypes(requestBody)) {
879
+ result.reason.push(exports.ErrorType.PostBodyContainMultipleMediaTypes);
967
880
  }
968
- return parameter;
969
- }
970
- static generatePluginManifestSchema(spec, specRelativePath, appName, options) {
971
- var _a, _b, _c;
972
- const functions = [];
973
- const functionNames = [];
974
- const paths = spec.paths;
975
- for (const pathUrl in paths) {
976
- const pathItem = paths[pathUrl];
977
- if (pathItem) {
978
- const operations = pathItem;
979
- for (const method in operations) {
980
- if (options.allowMethods.includes(method)) {
981
- const operationItem = operations[method];
982
- if (operationItem) {
983
- const operationId = operationItem.operationId;
984
- const description = (_a = operationItem.description) !== null && _a !== void 0 ? _a : "";
985
- const paramObject = operationItem.parameters;
986
- const requestBody = operationItem.requestBody;
987
- const parameters = {
988
- type: "object",
989
- properties: {},
990
- required: [],
991
- };
992
- if (paramObject) {
993
- for (let i = 0; i < paramObject.length; i++) {
994
- const param = paramObject[i];
995
- const schema = param.schema;
996
- parameters.properties[param.name] = ManifestUpdater.mapOpenAPISchemaToFuncParam(schema, method, pathUrl);
997
- if (param.required) {
998
- parameters.required.push(param.name);
999
- }
1000
- if (!parameters.properties[param.name].description) {
1001
- parameters.properties[param.name].description = (_b = param.description) !== null && _b !== void 0 ? _b : "";
1002
- }
1003
- }
1004
- }
1005
- if (requestBody) {
1006
- const requestJsonBody = requestBody.content["application/json"];
1007
- const requestBodySchema = requestJsonBody.schema;
1008
- if (requestBodySchema.type === "object") {
1009
- if (requestBodySchema.required) {
1010
- parameters.required.push(...requestBodySchema.required);
1011
- }
1012
- for (const property in requestBodySchema.properties) {
1013
- const schema = requestBodySchema.properties[property];
1014
- parameters.properties[property] = ManifestUpdater.mapOpenAPISchemaToFuncParam(schema, method, pathUrl);
1015
- }
1016
- }
1017
- else {
1018
- throw new SpecParserError(Utils.format(ConstantString.UnsupportedSchema, method, pathUrl, JSON.stringify(requestBodySchema)), exports.ErrorType.UpdateManifestFailed);
1019
- }
1020
- }
1021
- const funcObj = {
1022
- name: operationId,
1023
- description: description,
1024
- parameters: parameters,
1025
- };
1026
- functions.push(funcObj);
1027
- functionNames.push(operationId);
1028
- }
1029
- }
1030
- }
881
+ if (requestJsonBody) {
882
+ const requestBodySchema = requestJsonBody.schema;
883
+ if (requestBodySchema.type !== "object") {
884
+ result.reason.push(exports.ErrorType.PostBodySchemaIsNotJson);
1031
885
  }
886
+ const requestBodyParamResult = this.checkPostBodySchema(requestBodySchema, requestBody.required);
887
+ result.reason.push(...requestBodyParamResult.reason);
1032
888
  }
1033
- const apiPlugin = {
1034
- schema_version: "v2",
1035
- name_for_human: appName,
1036
- description_for_human: (_c = spec.info.description) !== null && _c !== void 0 ? _c : "<Please add description of the plugin>",
1037
- functions: functions,
1038
- runtimes: [
1039
- {
1040
- type: "OpenApi",
1041
- auth: {
1042
- type: "none", // TODO, support auth in the future
1043
- },
1044
- spec: {
1045
- url: specRelativePath,
1046
- },
1047
- run_for_functions: functionNames,
1048
- },
1049
- ],
889
+ // validate parameters
890
+ const paramObject = operationObject.parameters;
891
+ const paramResult = this.checkParamSchema(paramObject);
892
+ result.reason.push(...paramResult.reason);
893
+ if (result.reason.length > 0) {
894
+ result.isValid = false;
895
+ }
896
+ return result;
897
+ }
898
+ }
899
+
900
+ // Copyright (c) Microsoft Corporation.
901
+ class SMEValidator extends Validator {
902
+ constructor(spec, options) {
903
+ super();
904
+ this.projectType = exports.ProjectType.SME;
905
+ this.options = options;
906
+ this.spec = spec;
907
+ }
908
+ validateSpec() {
909
+ const result = { errors: [], warnings: [] };
910
+ // validate spec version
911
+ let validationResult = this.validateSpecVersion();
912
+ result.errors.push(...validationResult.errors);
913
+ // validate spec server
914
+ validationResult = this.validateSpecServer();
915
+ result.errors.push(...validationResult.errors);
916
+ // validate no supported API
917
+ validationResult = this.validateSpecNoSupportAPI();
918
+ result.errors.push(...validationResult.errors);
919
+ // validate operationId missing
920
+ if (this.options.allowMissingId) {
921
+ validationResult = this.validateSpecOperationId();
922
+ result.warnings.push(...validationResult.warnings);
923
+ }
924
+ return result;
925
+ }
926
+ validateAPI(method, path) {
927
+ const result = { isValid: true, reason: [] };
928
+ method = method.toLocaleLowerCase();
929
+ // validate method and path
930
+ const methodAndPathResult = this.validateMethodAndPath(method, path);
931
+ if (!methodAndPathResult.isValid) {
932
+ return methodAndPathResult;
933
+ }
934
+ const operationObject = this.spec.paths[path][method];
935
+ // validate auth
936
+ const authCheckResult = this.validateAuth(method, path);
937
+ result.reason.push(...authCheckResult.reason);
938
+ // validate operationId
939
+ if (!this.options.allowMissingId && !operationObject.operationId) {
940
+ result.reason.push(exports.ErrorType.MissingOperationId);
941
+ }
942
+ // validate server
943
+ const validateServerResult = this.validateServer(method, path);
944
+ result.reason.push(...validateServerResult.reason);
945
+ // validate response
946
+ const validateResponseResult = this.validateResponse(method, path);
947
+ result.reason.push(...validateResponseResult.reason);
948
+ let postBodyResult = {
949
+ requiredNum: 0,
950
+ optionalNum: 0,
951
+ isValid: true,
952
+ reason: [],
1050
953
  };
1051
- return apiPlugin;
954
+ // validate requestBody
955
+ const requestBody = operationObject.requestBody;
956
+ const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
957
+ if (Utils.containMultipleMediaTypes(requestBody)) {
958
+ result.reason.push(exports.ErrorType.PostBodyContainMultipleMediaTypes);
959
+ }
960
+ if (requestJsonBody) {
961
+ const requestBodySchema = requestJsonBody.schema;
962
+ postBodyResult = this.checkPostBodySchema(requestBodySchema, requestBody.required);
963
+ result.reason.push(...postBodyResult.reason);
964
+ }
965
+ // validate parameters
966
+ const paramObject = operationObject.parameters;
967
+ const paramResult = this.checkParamSchema(paramObject);
968
+ result.reason.push(...paramResult.reason);
969
+ // validate total parameters count
970
+ if (paramResult.isValid && postBodyResult.isValid) {
971
+ const paramCountResult = this.validateParamCount(postBodyResult, paramResult);
972
+ result.reason.push(...paramCountResult.reason);
973
+ }
974
+ if (result.reason.length > 0) {
975
+ result.isValid = false;
976
+ }
977
+ return result;
1052
978
  }
1053
- static updateManifest(manifestPath, outputSpecPath, spec, options, adaptiveCardFolder, authInfo) {
1054
- return __awaiter(this, void 0, void 0, function* () {
1055
- try {
1056
- const originalManifest = yield fs__default['default'].readJSON(manifestPath);
1057
- const updatedPart = {};
1058
- updatedPart.composeExtensions = [];
1059
- let warnings = [];
1060
- if (options.projectType === exports.ProjectType.SME) {
1061
- const updateResult = yield ManifestUpdater.generateCommands(spec, manifestPath, options, adaptiveCardFolder);
1062
- const commands = updateResult[0];
1063
- warnings = updateResult[1];
1064
- const composeExtension = {
1065
- composeExtensionType: "apiBased",
1066
- apiSpecificationFile: ManifestUpdater.getRelativePath(manifestPath, outputSpecPath),
1067
- commands: commands,
1068
- };
1069
- if (authInfo) {
1070
- const auth = authInfo.authScheme;
1071
- if (Utils.isAPIKeyAuth(auth) || Utils.isBearerTokenAuth(auth)) {
1072
- const safeApiSecretRegistrationId = Utils.getSafeRegistrationIdEnvName(`${authInfo.name}_${ConstantString.RegistrationIdPostfix}`);
1073
- composeExtension.authorization = {
1074
- authType: "apiSecretServiceAuth",
1075
- apiSecretServiceAuthConfiguration: {
1076
- apiSecretRegistrationId: `\${{${safeApiSecretRegistrationId}}}`,
1077
- },
1078
- };
1079
- }
1080
- else if (Utils.isOAuthWithAuthCodeFlow(auth)) {
1081
- const safeOAuth2RegistrationId = Utils.getSafeRegistrationIdEnvName(`${authInfo.name}_${ConstantString.OAuthRegistrationIdPostFix}`);
1082
- composeExtension.authorization = {
1083
- authType: "oAuth2.0",
1084
- oAuthConfiguration: {
1085
- oauthConfigurationId: `\${{${safeOAuth2RegistrationId}}}`,
1086
- },
1087
- };
1088
- updatedPart.webApplicationInfo = {
1089
- id: "${{AAD_APP_CLIENT_ID}}",
1090
- resource: "api://${{DOMAIN}}/${{AAD_APP_CLIENT_ID}}",
1091
- };
1092
- }
1093
- }
1094
- updatedPart.composeExtensions = [composeExtension];
1095
- }
1096
- updatedPart.description = originalManifest.description;
1097
- ManifestUpdater.updateManifestDescription(updatedPart, spec);
1098
- const updatedManifest = Object.assign(Object.assign({}, originalManifest), updatedPart);
1099
- return [updatedManifest, warnings];
1100
- }
1101
- catch (err) {
1102
- throw new SpecParserError(err.toString(), exports.ErrorType.UpdateManifestFailed);
979
+ validateParamCount(postBodyResult, paramResult) {
980
+ const result = { isValid: true, reason: [] };
981
+ const totalRequiredParams = postBodyResult.requiredNum + paramResult.requiredNum;
982
+ const totalParams = totalRequiredParams + postBodyResult.optionalNum + paramResult.optionalNum;
983
+ if (totalRequiredParams > 1) {
984
+ if (!this.options.allowMultipleParameters ||
985
+ totalRequiredParams > SMEValidator.SMERequiredParamsMaxNum) {
986
+ result.reason.push(exports.ErrorType.ExceededRequiredParamsLimit);
1103
987
  }
1104
- });
988
+ }
989
+ else if (totalParams === 0) {
990
+ result.reason.push(exports.ErrorType.NoParameter);
991
+ }
992
+ return result;
1105
993
  }
1106
- static generateCommands(spec, manifestPath, options, adaptiveCardFolder) {
994
+ }
995
+ SMEValidator.SMERequiredParamsMaxNum = 5;
996
+
997
+ // Copyright (c) Microsoft Corporation.
998
+ class TeamsAIValidator extends Validator {
999
+ constructor(spec, options) {
1000
+ super();
1001
+ this.projectType = exports.ProjectType.TeamsAi;
1002
+ this.options = options;
1003
+ this.spec = spec;
1004
+ }
1005
+ validateSpec() {
1006
+ const result = { errors: [], warnings: [] };
1007
+ // validate spec server
1008
+ let validationResult = this.validateSpecServer();
1009
+ result.errors.push(...validationResult.errors);
1010
+ // validate no supported API
1011
+ validationResult = this.validateSpecNoSupportAPI();
1012
+ result.errors.push(...validationResult.errors);
1013
+ return result;
1014
+ }
1015
+ validateAPI(method, path) {
1016
+ const result = { isValid: true, reason: [] };
1017
+ method = method.toLocaleLowerCase();
1018
+ // validate method and path
1019
+ const methodAndPathResult = this.validateMethodAndPath(method, path);
1020
+ if (!methodAndPathResult.isValid) {
1021
+ return methodAndPathResult;
1022
+ }
1023
+ const operationObject = this.spec.paths[path][method];
1024
+ // validate operationId
1025
+ if (!this.options.allowMissingId && !operationObject.operationId) {
1026
+ result.reason.push(exports.ErrorType.MissingOperationId);
1027
+ }
1028
+ // validate server
1029
+ const validateServerResult = this.validateServer(method, path);
1030
+ result.reason.push(...validateServerResult.reason);
1031
+ if (result.reason.length > 0) {
1032
+ result.isValid = false;
1033
+ }
1034
+ return result;
1035
+ }
1036
+ }
1037
+
1038
+ class ValidatorFactory {
1039
+ static create(spec, options) {
1107
1040
  var _a;
1108
- return __awaiter(this, void 0, void 0, function* () {
1109
- const paths = spec.paths;
1110
- const commands = [];
1111
- const warnings = [];
1112
- if (paths) {
1113
- for (const pathUrl in paths) {
1114
- const pathItem = paths[pathUrl];
1115
- if (pathItem) {
1116
- const operations = pathItem;
1117
- // Currently only support GET and POST method
1118
- for (const method in operations) {
1119
- if ((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) {
1120
- const operationItem = operations[method];
1121
- if (operationItem) {
1122
- const [command, warning] = Utils.parseApiInfo(operationItem, options);
1123
- if (adaptiveCardFolder) {
1124
- const adaptiveCardPath = path__default['default'].join(adaptiveCardFolder, command.id + ".json");
1125
- command.apiResponseRenderingTemplateFile = (yield fs__default['default'].pathExists(adaptiveCardPath))
1126
- ? ManifestUpdater.getRelativePath(manifestPath, adaptiveCardPath)
1127
- : "";
1128
- }
1129
- if (warning) {
1130
- warnings.push(warning);
1131
- }
1132
- commands.push(command);
1133
- }
1134
- }
1041
+ const type = (_a = options.projectType) !== null && _a !== void 0 ? _a : exports.ProjectType.SME;
1042
+ switch (type) {
1043
+ case exports.ProjectType.SME:
1044
+ return new SMEValidator(spec, options);
1045
+ case exports.ProjectType.Copilot:
1046
+ return new CopilotValidator(spec, options);
1047
+ case exports.ProjectType.TeamsAi:
1048
+ return new TeamsAIValidator(spec, options);
1049
+ default:
1050
+ throw new Error(`Invalid project type: ${type}`);
1051
+ }
1052
+ }
1053
+ }
1054
+
1055
+ // Copyright (c) Microsoft Corporation.
1056
+ class SpecFilter {
1057
+ static specFilter(filter, unResolveSpec, resolvedSpec, options) {
1058
+ var _a;
1059
+ try {
1060
+ const newSpec = Object.assign({}, unResolveSpec);
1061
+ const newPaths = {};
1062
+ for (const filterItem of filter) {
1063
+ const [method, path] = filterItem.split(" ");
1064
+ const methodName = method.toLowerCase();
1065
+ const pathObj = (_a = resolvedSpec.paths) === null || _a === void 0 ? void 0 : _a[path];
1066
+ if (ConstantString.AllOperationMethods.includes(methodName) &&
1067
+ pathObj &&
1068
+ pathObj[methodName]) {
1069
+ const validator = ValidatorFactory.create(resolvedSpec, options);
1070
+ const validateResult = validator.validateAPI(methodName, path);
1071
+ if (!validateResult.isValid) {
1072
+ continue;
1073
+ }
1074
+ if (!newPaths[path]) {
1075
+ newPaths[path] = Object.assign({}, unResolveSpec.paths[path]);
1076
+ for (const m of ConstantString.AllOperationMethods) {
1077
+ delete newPaths[path][m];
1135
1078
  }
1136
1079
  }
1080
+ newPaths[path][methodName] = unResolveSpec.paths[path][methodName];
1081
+ // Add the operationId if missing
1082
+ if (!newPaths[path][methodName].operationId) {
1083
+ newPaths[path][methodName].operationId = `${methodName}${Utils.convertPathToCamelCase(path)}`;
1084
+ }
1137
1085
  }
1138
1086
  }
1139
- return [commands, warnings];
1140
- });
1141
- }
1142
- static getRelativePath(from, to) {
1143
- const relativePath = path__default['default'].relative(path__default['default'].dirname(from), to);
1144
- return path__default['default'].normalize(relativePath).replace(/\\/g, "/");
1145
- }
1146
- static removeEnvs(str) {
1147
- const placeHolderReg = /\${{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*}}/g;
1148
- const matches = placeHolderReg.exec(str);
1149
- let newStr = str;
1150
- if (matches != null) {
1151
- newStr = newStr.replace(matches[0], "");
1087
+ newSpec.paths = newPaths;
1088
+ return newSpec;
1089
+ }
1090
+ catch (err) {
1091
+ throw new SpecParserError(err.toString(), exports.ErrorType.FilterSpecFailed);
1152
1092
  }
1153
- return newStr;
1154
1093
  }
1155
1094
  }
1156
1095
 
@@ -1324,6 +1263,27 @@ function wrapAdaptiveCard(card, jsonPath) {
1324
1263
  };
1325
1264
  return result;
1326
1265
  }
1266
+ function wrapResponseSemantics(card, jsonPath) {
1267
+ const props = inferProperties(card);
1268
+ const dataPath = jsonPath === "$" ? "$" : "$." + jsonPath;
1269
+ const result = {
1270
+ data_path: dataPath,
1271
+ };
1272
+ if (props.title || props.subtitle || props.imageUrl) {
1273
+ result.properties = {};
1274
+ if (props.title) {
1275
+ result.properties.title = "$." + props.title;
1276
+ }
1277
+ if (props.subtitle) {
1278
+ result.properties.subtitle = "$." + props.subtitle;
1279
+ }
1280
+ if (props.imageUrl) {
1281
+ result.properties.url = "$." + props.imageUrl;
1282
+ }
1283
+ }
1284
+ result.static_template = card;
1285
+ return result;
1286
+ }
1327
1287
  /**
1328
1288
  * Infers the preview card template from an Adaptive Card and a JSON path.
1329
1289
  * The preview card template includes a title and an optional subtitle and image.
@@ -1336,11 +1296,29 @@ function wrapAdaptiveCard(card, jsonPath) {
1336
1296
  * @returns The inferred preview card template.
1337
1297
  */
1338
1298
  function inferPreviewCardTemplate(card) {
1339
- var _a;
1340
1299
  const result = {
1341
- title: "",
1300
+ title: "result",
1342
1301
  };
1343
- const textBlockElements = new Set();
1302
+ const inferredProperties = inferProperties(card);
1303
+ if (inferredProperties.title) {
1304
+ result.title = `\${if(${inferredProperties.title}, ${inferredProperties.title}, 'N/A')}`;
1305
+ }
1306
+ if (inferredProperties.subtitle) {
1307
+ result.subtitle = `\${if(${inferredProperties.subtitle}, ${inferredProperties.subtitle}, 'N/A')}`;
1308
+ }
1309
+ if (inferredProperties.imageUrl) {
1310
+ result.image = {
1311
+ url: `\${${inferredProperties.imageUrl}}`,
1312
+ alt: `\${if(${inferredProperties.imageUrl}, ${inferredProperties.imageUrl}, 'N/A')}`,
1313
+ $when: `\${${inferredProperties.imageUrl} != null}`,
1314
+ };
1315
+ }
1316
+ return result;
1317
+ }
1318
+ function inferProperties(card) {
1319
+ var _a;
1320
+ const result = {};
1321
+ const nameSet = new Set();
1344
1322
  let rootObject;
1345
1323
  if (((_a = card.body[0]) === null || _a === void 0 ? void 0 : _a.type) === ConstantString.ContainerType) {
1346
1324
  rootObject = card.body[0].items;
@@ -1353,56 +1331,346 @@ function inferPreviewCardTemplate(card) {
1353
1331
  const textElement = element;
1354
1332
  const index = textElement.text.indexOf("${if(");
1355
1333
  if (index > 0) {
1356
- textElement.text = textElement.text.substring(index);
1357
- textBlockElements.add(textElement);
1334
+ const text = textElement.text.substring(index);
1335
+ const match = text.match(/\${if\(([^,]+),/);
1336
+ const property = match ? match[1] : "";
1337
+ if (property) {
1338
+ nameSet.add(property);
1339
+ }
1340
+ }
1341
+ }
1342
+ else if (element.type === ConstantString.ImageType) {
1343
+ const imageElement = element;
1344
+ const match = imageElement.url.match(/\${([^,]+)}/);
1345
+ const property = match ? match[1] : "";
1346
+ if (property) {
1347
+ nameSet.add(property);
1358
1348
  }
1359
1349
  }
1360
1350
  }
1361
- for (const element of textBlockElements) {
1362
- const text = element.text;
1363
- if (!result.title && Utils.isWellKnownName(text, ConstantString.WellknownTitleName)) {
1364
- result.title = text;
1365
- textBlockElements.delete(element);
1351
+ for (const name of nameSet) {
1352
+ if (!result.title && Utils.isWellKnownName(name, ConstantString.WellknownTitleName)) {
1353
+ result.title = name;
1354
+ nameSet.delete(name);
1366
1355
  }
1367
1356
  else if (!result.subtitle &&
1368
- Utils.isWellKnownName(text, ConstantString.WellknownSubtitleName)) {
1369
- result.subtitle = text;
1370
- textBlockElements.delete(element);
1357
+ Utils.isWellKnownName(name, ConstantString.WellknownSubtitleName)) {
1358
+ result.subtitle = name;
1359
+ nameSet.delete(name);
1371
1360
  }
1372
- else if (!result.image && Utils.isWellKnownName(text, ConstantString.WellknownImageName)) {
1373
- const match = text.match(/\${if\(([^,]+),/);
1374
- const property = match ? match[1] : "";
1375
- if (property) {
1376
- result.image = {
1377
- url: `\${${property}}`,
1378
- alt: text,
1379
- $when: `\${${property} != null}`,
1380
- };
1381
- }
1382
- textBlockElements.delete(element);
1361
+ else if (!result.imageUrl && Utils.isWellKnownName(name, ConstantString.WellknownImageName)) {
1362
+ result.imageUrl = name;
1363
+ nameSet.delete(name);
1383
1364
  }
1384
1365
  }
1385
- for (const element of textBlockElements) {
1386
- const text = element.text;
1366
+ for (const name of nameSet) {
1387
1367
  if (!result.title) {
1388
- result.title = text;
1389
- textBlockElements.delete(element);
1368
+ result.title = name;
1369
+ nameSet.delete(name);
1390
1370
  }
1391
1371
  else if (!result.subtitle) {
1392
- result.subtitle = text;
1393
- textBlockElements.delete(element);
1372
+ result.subtitle = name;
1373
+ nameSet.delete(name);
1394
1374
  }
1395
1375
  }
1396
1376
  if (!result.title && result.subtitle) {
1397
1377
  result.title = result.subtitle;
1398
1378
  delete result.subtitle;
1399
1379
  }
1400
- if (!result.title) {
1401
- result.title = "result";
1402
- }
1403
1380
  return result;
1404
1381
  }
1405
1382
 
1383
+ // Copyright (c) Microsoft Corporation.
1384
+ class ManifestUpdater {
1385
+ static updateManifestWithAiPlugin(manifestPath, outputSpecPath, apiPluginFilePath, spec, options) {
1386
+ return __awaiter(this, void 0, void 0, function* () {
1387
+ const manifest = yield fs__default['default'].readJSON(manifestPath);
1388
+ const apiPluginRelativePath = ManifestUpdater.getRelativePath(manifestPath, apiPluginFilePath);
1389
+ manifest.plugins = [
1390
+ {
1391
+ file: apiPluginRelativePath,
1392
+ id: ConstantString.DefaultPluginId,
1393
+ },
1394
+ ];
1395
+ const appName = this.removeEnvs(manifest.name.short);
1396
+ ManifestUpdater.updateManifestDescription(manifest, spec);
1397
+ const specRelativePath = ManifestUpdater.getRelativePath(manifestPath, outputSpecPath);
1398
+ const apiPlugin = yield ManifestUpdater.generatePluginManifestSchema(spec, specRelativePath, apiPluginFilePath, appName, options);
1399
+ return [manifest, apiPlugin];
1400
+ });
1401
+ }
1402
+ static updateManifestDescription(manifest, spec) {
1403
+ var _a, _b;
1404
+ manifest.description = {
1405
+ short: spec.info.title.slice(0, ConstantString.ShortDescriptionMaxLens),
1406
+ full: (_b = ((_a = spec.info.description) !== null && _a !== void 0 ? _a : manifest.description.full)) === null || _b === void 0 ? void 0 : _b.slice(0, ConstantString.FullDescriptionMaxLens),
1407
+ };
1408
+ }
1409
+ static mapOpenAPISchemaToFuncParam(schema, method, pathUrl) {
1410
+ let parameter;
1411
+ if (schema.type === "string" ||
1412
+ schema.type === "boolean" ||
1413
+ schema.type === "integer" ||
1414
+ schema.type === "number" ||
1415
+ schema.type === "array") {
1416
+ parameter = schema;
1417
+ }
1418
+ else {
1419
+ throw new SpecParserError(Utils.format(ConstantString.UnsupportedSchema, method, pathUrl, JSON.stringify(schema)), exports.ErrorType.UpdateManifestFailed);
1420
+ }
1421
+ return parameter;
1422
+ }
1423
+ static generatePluginManifestSchema(spec, specRelativePath, apiPluginFilePath, appName, options) {
1424
+ var _a, _b, _c, _d;
1425
+ return __awaiter(this, void 0, void 0, function* () {
1426
+ const functions = [];
1427
+ const functionNames = [];
1428
+ const conversationStarters = [];
1429
+ const paths = spec.paths;
1430
+ for (const pathUrl in paths) {
1431
+ const pathItem = paths[pathUrl];
1432
+ if (pathItem) {
1433
+ const operations = pathItem;
1434
+ for (const method in operations) {
1435
+ if (options.allowMethods.includes(method)) {
1436
+ const operationItem = operations[method];
1437
+ if (operationItem) {
1438
+ const operationId = operationItem.operationId;
1439
+ const description = (_a = operationItem.description) !== null && _a !== void 0 ? _a : "";
1440
+ const paramObject = operationItem.parameters;
1441
+ const requestBody = operationItem.requestBody;
1442
+ const parameters = {
1443
+ type: "object",
1444
+ properties: {},
1445
+ required: [],
1446
+ };
1447
+ if (paramObject) {
1448
+ for (let i = 0; i < paramObject.length; i++) {
1449
+ const param = paramObject[i];
1450
+ const schema = param.schema;
1451
+ parameters.properties[param.name] = ManifestUpdater.mapOpenAPISchemaToFuncParam(schema, method, pathUrl);
1452
+ if (param.required) {
1453
+ parameters.required.push(param.name);
1454
+ }
1455
+ if (!parameters.properties[param.name].description) {
1456
+ parameters.properties[param.name].description = (_b = param.description) !== null && _b !== void 0 ? _b : "";
1457
+ }
1458
+ }
1459
+ }
1460
+ if (requestBody) {
1461
+ const requestJsonBody = requestBody.content["application/json"];
1462
+ const requestBodySchema = requestJsonBody.schema;
1463
+ if (requestBodySchema.type === "object") {
1464
+ if (requestBodySchema.required) {
1465
+ parameters.required.push(...requestBodySchema.required);
1466
+ }
1467
+ for (const property in requestBodySchema.properties) {
1468
+ const schema = requestBodySchema.properties[property];
1469
+ parameters.properties[property] = ManifestUpdater.mapOpenAPISchemaToFuncParam(schema, method, pathUrl);
1470
+ }
1471
+ }
1472
+ else {
1473
+ throw new SpecParserError(Utils.format(ConstantString.UnsupportedSchema, method, pathUrl, JSON.stringify(requestBodySchema)), exports.ErrorType.UpdateManifestFailed);
1474
+ }
1475
+ }
1476
+ const funcObj = {
1477
+ name: operationId,
1478
+ description: description,
1479
+ parameters: parameters,
1480
+ };
1481
+ if (options.allowResponseSemantics) {
1482
+ const [card, jsonPath] = AdaptiveCardGenerator.generateAdaptiveCard(operationItem);
1483
+ const responseSemantic = wrapResponseSemantics(card, jsonPath);
1484
+ funcObj.capabilities = {
1485
+ response_semantics: responseSemantic,
1486
+ };
1487
+ }
1488
+ functions.push(funcObj);
1489
+ functionNames.push(operationId);
1490
+ if (description) {
1491
+ conversationStarters.push(description);
1492
+ }
1493
+ }
1494
+ }
1495
+ }
1496
+ }
1497
+ }
1498
+ let apiPlugin;
1499
+ if (yield fs__default['default'].pathExists(apiPluginFilePath)) {
1500
+ apiPlugin = yield fs__default['default'].readJSON(apiPluginFilePath);
1501
+ }
1502
+ else {
1503
+ apiPlugin = {
1504
+ schema_version: "v2",
1505
+ name_for_human: "",
1506
+ description_for_human: "",
1507
+ functions: [],
1508
+ runtimes: [],
1509
+ };
1510
+ }
1511
+ apiPlugin.functions = apiPlugin.functions || [];
1512
+ for (const func of functions) {
1513
+ const index = (_c = apiPlugin.functions) === null || _c === void 0 ? void 0 : _c.findIndex((f) => f.name === func.name);
1514
+ if (index === -1) {
1515
+ apiPlugin.functions.push(func);
1516
+ }
1517
+ else {
1518
+ apiPlugin.functions[index] = func;
1519
+ }
1520
+ }
1521
+ apiPlugin.runtimes = apiPlugin.runtimes || [];
1522
+ const index = apiPlugin.runtimes.findIndex((runtime) => runtime.spec.url === specRelativePath);
1523
+ if (index === -1) {
1524
+ apiPlugin.runtimes.push({
1525
+ type: "OpenApi",
1526
+ auth: {
1527
+ type: "none",
1528
+ },
1529
+ spec: {
1530
+ url: specRelativePath,
1531
+ },
1532
+ run_for_functions: functionNames,
1533
+ });
1534
+ }
1535
+ else {
1536
+ apiPlugin.runtimes[index].run_for_functions = functionNames;
1537
+ }
1538
+ if (!apiPlugin.name_for_human) {
1539
+ apiPlugin.name_for_human = appName;
1540
+ }
1541
+ if (!apiPlugin.description_for_human) {
1542
+ apiPlugin.description_for_human =
1543
+ (_d = spec.info.description) !== null && _d !== void 0 ? _d : "<Please add description of the plugin>";
1544
+ }
1545
+ if (options.allowConversationStarters && conversationStarters.length > 0) {
1546
+ if (!apiPlugin.capabilities) {
1547
+ apiPlugin.capabilities = {
1548
+ localization: {},
1549
+ };
1550
+ }
1551
+ if (!apiPlugin.capabilities.conversation_starters) {
1552
+ apiPlugin.capabilities.conversation_starters = conversationStarters
1553
+ .slice(0, 5)
1554
+ .map((text) => ({ text }));
1555
+ }
1556
+ }
1557
+ return apiPlugin;
1558
+ });
1559
+ }
1560
+ static updateManifest(manifestPath, outputSpecPath, spec, options, adaptiveCardFolder, authInfo) {
1561
+ return __awaiter(this, void 0, void 0, function* () {
1562
+ try {
1563
+ const originalManifest = yield fs__default['default'].readJSON(manifestPath);
1564
+ const updatedPart = {};
1565
+ updatedPart.composeExtensions = [];
1566
+ let warnings = [];
1567
+ if (options.projectType === exports.ProjectType.SME) {
1568
+ const updateResult = yield ManifestUpdater.generateCommands(spec, manifestPath, options, adaptiveCardFolder);
1569
+ const commands = updateResult[0];
1570
+ warnings = updateResult[1];
1571
+ const composeExtension = {
1572
+ composeExtensionType: "apiBased",
1573
+ apiSpecificationFile: ManifestUpdater.getRelativePath(manifestPath, outputSpecPath),
1574
+ commands: commands,
1575
+ };
1576
+ if (authInfo) {
1577
+ const auth = authInfo.authScheme;
1578
+ if (Utils.isAPIKeyAuth(auth) || Utils.isBearerTokenAuth(auth)) {
1579
+ const safeApiSecretRegistrationId = Utils.getSafeRegistrationIdEnvName(`${authInfo.name}_${ConstantString.RegistrationIdPostfix}`);
1580
+ composeExtension.authorization = {
1581
+ authType: "apiSecretServiceAuth",
1582
+ apiSecretServiceAuthConfiguration: {
1583
+ apiSecretRegistrationId: `\${{${safeApiSecretRegistrationId}}}`,
1584
+ },
1585
+ };
1586
+ }
1587
+ else if (Utils.isOAuthWithAuthCodeFlow(auth)) {
1588
+ const safeOAuth2RegistrationId = Utils.getSafeRegistrationIdEnvName(`${authInfo.name}_${ConstantString.OAuthRegistrationIdPostFix}`);
1589
+ composeExtension.authorization = {
1590
+ authType: "oAuth2.0",
1591
+ oAuthConfiguration: {
1592
+ oauthConfigurationId: `\${{${safeOAuth2RegistrationId}}}`,
1593
+ },
1594
+ };
1595
+ updatedPart.webApplicationInfo = {
1596
+ id: "${{AAD_APP_CLIENT_ID}}",
1597
+ resource: "api://${{DOMAIN}}/${{AAD_APP_CLIENT_ID}}",
1598
+ };
1599
+ }
1600
+ }
1601
+ updatedPart.composeExtensions = [composeExtension];
1602
+ }
1603
+ updatedPart.description = originalManifest.description;
1604
+ ManifestUpdater.updateManifestDescription(updatedPart, spec);
1605
+ const updatedManifest = Object.assign(Object.assign({}, originalManifest), updatedPart);
1606
+ return [updatedManifest, warnings];
1607
+ }
1608
+ catch (err) {
1609
+ throw new SpecParserError(err.toString(), exports.ErrorType.UpdateManifestFailed);
1610
+ }
1611
+ });
1612
+ }
1613
+ static generateCommands(spec, manifestPath, options, adaptiveCardFolder) {
1614
+ var _a;
1615
+ return __awaiter(this, void 0, void 0, function* () {
1616
+ const paths = spec.paths;
1617
+ const commands = [];
1618
+ const warnings = [];
1619
+ if (paths) {
1620
+ for (const pathUrl in paths) {
1621
+ const pathItem = paths[pathUrl];
1622
+ if (pathItem) {
1623
+ const operations = pathItem;
1624
+ // Currently only support GET and POST method
1625
+ for (const method in operations) {
1626
+ if ((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) {
1627
+ const operationItem = operations[method];
1628
+ if (operationItem) {
1629
+ const command = Utils.parseApiInfo(operationItem, options);
1630
+ if (command.parameters &&
1631
+ command.parameters.length >= 1 &&
1632
+ command.parameters.some((param) => param.isRequired)) {
1633
+ command.parameters = command.parameters.filter((param) => param.isRequired);
1634
+ }
1635
+ else if (command.parameters && command.parameters.length > 0) {
1636
+ command.parameters = [command.parameters[0]];
1637
+ warnings.push({
1638
+ type: exports.WarningType.OperationOnlyContainsOptionalParam,
1639
+ content: Utils.format(ConstantString.OperationOnlyContainsOptionalParam, command.id),
1640
+ data: command.id,
1641
+ });
1642
+ }
1643
+ if (adaptiveCardFolder) {
1644
+ const adaptiveCardPath = path__default['default'].join(adaptiveCardFolder, command.id + ".json");
1645
+ command.apiResponseRenderingTemplateFile = (yield fs__default['default'].pathExists(adaptiveCardPath))
1646
+ ? ManifestUpdater.getRelativePath(manifestPath, adaptiveCardPath)
1647
+ : "";
1648
+ }
1649
+ commands.push(command);
1650
+ }
1651
+ }
1652
+ }
1653
+ }
1654
+ }
1655
+ }
1656
+ return [commands, warnings];
1657
+ });
1658
+ }
1659
+ static getRelativePath(from, to) {
1660
+ const relativePath = path__default['default'].relative(path__default['default'].dirname(from), to);
1661
+ return path__default['default'].normalize(relativePath).replace(/\\/g, "/");
1662
+ }
1663
+ static removeEnvs(str) {
1664
+ const placeHolderReg = /\${{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*}}/g;
1665
+ const matches = placeHolderReg.exec(str);
1666
+ let newStr = str;
1667
+ if (matches != null) {
1668
+ newStr = newStr.replace(matches[0], "");
1669
+ }
1670
+ return newStr;
1671
+ }
1672
+ }
1673
+
1406
1674
  // Copyright (c) Microsoft Corporation.
1407
1675
  /**
1408
1676
  * A class that parses an OpenAPI specification file and provides methods to validate, list, and generate artifacts.
@@ -1422,6 +1690,8 @@ class SpecParser {
1422
1690
  allowMultipleParameters: false,
1423
1691
  allowOauth2: false,
1424
1692
  allowMethods: ["get", "post"],
1693
+ allowConversationStarters: false,
1694
+ allowResponseSemantics: false,
1425
1695
  projectType: exports.ProjectType.SME,
1426
1696
  };
1427
1697
  this.pathOrSpec = pathOrDoc;
@@ -1447,6 +1717,8 @@ class SpecParser {
1447
1717
  errors: [{ type: exports.ErrorType.SpecNotValid, content: e.toString() }],
1448
1718
  };
1449
1719
  }
1720
+ const errors = [];
1721
+ const warnings = [];
1450
1722
  if (!this.options.allowSwagger && this.isSwaggerFile) {
1451
1723
  return {
1452
1724
  status: exports.ValidationStatus.Error,
@@ -1456,23 +1728,38 @@ class SpecParser {
1456
1728
  ],
1457
1729
  };
1458
1730
  }
1459
- if (this.options.projectType === exports.ProjectType.SME ||
1460
- this.options.projectType === exports.ProjectType.Copilot) {
1461
- if (this.spec.openapi >= "3.1.0") {
1462
- return {
1463
- status: exports.ValidationStatus.Error,
1464
- warnings: [],
1465
- errors: [
1466
- {
1467
- type: exports.ErrorType.SpecVersionNotSupported,
1468
- content: Utils.format(ConstantString.SpecVersionNotSupported, this.spec.openapi),
1469
- data: this.spec.openapi,
1470
- },
1471
- ],
1472
- };
1473
- }
1731
+ // Remote reference not supported
1732
+ const refPaths = this.parser.$refs.paths();
1733
+ // refPaths [0] is the current spec file path
1734
+ if (refPaths.length > 1) {
1735
+ errors.push({
1736
+ type: exports.ErrorType.RemoteRefNotSupported,
1737
+ content: Utils.format(ConstantString.RemoteRefNotSupported, refPaths.join(", ")),
1738
+ data: refPaths,
1739
+ });
1740
+ }
1741
+ if (!!this.isSwaggerFile && this.options.allowSwagger) {
1742
+ warnings.push({
1743
+ type: exports.WarningType.ConvertSwaggerToOpenAPI,
1744
+ content: ConstantString.ConvertSwaggerToOpenAPI,
1745
+ });
1746
+ }
1747
+ const validator = this.getValidator(this.spec);
1748
+ const validationResult = validator.validateSpec();
1749
+ warnings.push(...validationResult.warnings);
1750
+ errors.push(...validationResult.errors);
1751
+ let status = exports.ValidationStatus.Valid;
1752
+ if (warnings.length > 0 && errors.length === 0) {
1753
+ status = exports.ValidationStatus.Warning;
1474
1754
  }
1475
- return Utils.validateSpec(this.spec, this.parser, !!this.isSwaggerFile, this.options);
1755
+ else if (errors.length > 0) {
1756
+ status = exports.ValidationStatus.Error;
1757
+ }
1758
+ return {
1759
+ status: status,
1760
+ warnings: warnings,
1761
+ errors: errors,
1762
+ };
1476
1763
  }
1477
1764
  catch (err) {
1478
1765
  throw new SpecParserError(err.toString(), exports.ErrorType.ValidateFailed);
@@ -1505,34 +1792,27 @@ class SpecParser {
1505
1792
  for (const apiKey in apiMap) {
1506
1793
  const { operation, isValid, reason } = apiMap[apiKey];
1507
1794
  const [method, path] = apiKey.split(" ");
1795
+ const operationId = (_a = operation.operationId) !== null && _a !== void 0 ? _a : `${method.toLowerCase()}${Utils.convertPathToCamelCase(path)}`;
1508
1796
  const apiResult = {
1509
- api: "",
1797
+ api: apiKey,
1510
1798
  server: "",
1511
- operationId: "",
1799
+ operationId: operationId,
1512
1800
  isValid: isValid,
1513
1801
  reason: reason,
1514
1802
  };
1515
- const rootServer = spec.servers && spec.servers[0];
1516
- const methodServer = spec.paths[path].servers && ((_a = spec.paths[path]) === null || _a === void 0 ? void 0 : _a.servers[0]);
1517
- const operationServer = operation.servers && operation.servers[0];
1518
- const serverUrl = operationServer || methodServer || rootServer;
1519
- if (!serverUrl) {
1520
- throw new SpecParserError(ConstantString.NoServerInformation, exports.ErrorType.NoServerInformation);
1521
- }
1522
- apiResult.server = Utils.resolveEnv(serverUrl.url);
1523
- let operationId = operation.operationId;
1524
- if (!operationId) {
1525
- operationId = `${method.toLowerCase()}${Utils.convertPathToCamelCase(path)}`;
1526
- }
1527
- apiResult.operationId = operationId;
1528
- const authArray = Utils.getAuthArray(operation.security, spec);
1529
- for (const auths of authArray) {
1530
- if (auths.length === 1) {
1531
- apiResult.auth = auths[0];
1532
- break;
1803
+ if (isValid) {
1804
+ const serverObj = Utils.getServerObject(spec, method.toLocaleLowerCase(), path);
1805
+ if (serverObj) {
1806
+ apiResult.server = Utils.resolveEnv(serverObj.url);
1807
+ }
1808
+ const authArray = Utils.getAuthArray(operation.security, spec);
1809
+ for (const auths of authArray) {
1810
+ if (auths.length === 1) {
1811
+ apiResult.auth = auths[0];
1812
+ break;
1813
+ }
1533
1814
  }
1534
1815
  }
1535
- apiResult.api = apiKey;
1536
1816
  result.APIs.push(apiResult);
1537
1817
  }
1538
1818
  result.allAPICount = result.APIs.length;
@@ -1721,12 +2001,17 @@ class SpecParser {
1721
2001
  });
1722
2002
  }
1723
2003
  getAPIs(spec) {
1724
- if (this.apiMap !== undefined) {
1725
- return this.apiMap;
2004
+ const validator = this.getValidator(spec);
2005
+ const apiMap = validator.listAPIs();
2006
+ return apiMap;
2007
+ }
2008
+ getValidator(spec) {
2009
+ if (this.validator) {
2010
+ return this.validator;
1726
2011
  }
1727
- const result = Utils.listAPIs(spec, this.options);
1728
- this.apiMap = result;
1729
- return result;
2012
+ const validator = ValidatorFactory.create(spec, this.options);
2013
+ this.validator = validator;
2014
+ return validator;
1730
2015
  }
1731
2016
  }
1732
2017