@microsoft/m365-spec-parser 0.2.4 → 0.2.5-alpha.08a6629d5.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.
@@ -80,11 +80,8 @@ exports.ErrorType = void 0;
80
80
  ErrorType["PostBodyContainMultipleMediaTypes"] = "post-body-contain-multiple-media-types";
81
81
  ErrorType["ResponseContainMultipleMediaTypes"] = "response-contain-multiple-media-types";
82
82
  ErrorType["ResponseJsonIsEmpty"] = "response-json-is-empty";
83
- ErrorType["PostBodySchemaIsNotJson"] = "post-body-schema-is-not-json";
84
83
  ErrorType["PostBodyContainsRequiredUnsupportedSchema"] = "post-body-contains-required-unsupported-schema";
85
84
  ErrorType["ParamsContainRequiredUnsupportedSchema"] = "params-contain-required-unsupported-schema";
86
- ErrorType["ParamsContainsNestedObject"] = "params-contains-nested-object";
87
- ErrorType["RequestBodyContainsNestedObject"] = "request-body-contains-nested-object";
88
85
  ErrorType["ExceededRequiredParamsLimit"] = "exceeded-required-params-limit";
89
86
  ErrorType["NoParameter"] = "no-parameter";
90
87
  ErrorType["NoAPIInfo"] = "no-api-info";
@@ -104,6 +101,7 @@ exports.WarningType = void 0;
104
101
  WarningType["ConvertSwaggerToOpenAPI"] = "convert-swagger-to-openapi";
105
102
  WarningType["FuncDescriptionTooLong"] = "function-description-too-long";
106
103
  WarningType["OperationIdContainsSpecialCharacters"] = "operationid-contains-special-characters";
104
+ WarningType["UnsupportedAuthType"] = "unsupported-auth-type";
107
105
  WarningType["GenerateJsonDataFailed"] = "generate-json-data-failed";
108
106
  WarningType["Unknown"] = "unknown";
109
107
  })(exports.WarningType || (exports.WarningType = {}));
@@ -143,6 +141,7 @@ ConstantString.SwaggerNotSupported = "Swagger 2.0 is not supported. Please conve
143
141
  ConstantString.SpecVersionNotSupported = "Unsupported OpenAPI version %s. Please use version 3.0.x.";
144
142
  ConstantString.MultipleAuthNotSupported = "Multiple authentication methods are unsupported. Ensure all selected APIs use identical authentication.";
145
143
  ConstantString.OperationIdContainsSpecialCharacters = "Operation id '%s' in OpenAPI description document contained special characters and was renamed to '%s'.";
144
+ ConstantString.AuthTypeIsNotSupported = "Unsupported authorization type in API '%s'. No authorization will be used.";
146
145
  ConstantString.UnsupportedSchema = "Unsupported schema in %s %s: %s";
147
146
  ConstantString.FuncDescriptionTooLong = "The description of the function '%s' is too long. The current length is %s characters, while the maximum allowed length is %s characters.";
148
147
  ConstantString.GenerateJsonDataFailed = "Failed to generate JSON data for api: %s due to %s.";
@@ -152,17 +151,12 @@ ConstantString.WrappedCardResponseLayout = "list";
152
151
  ConstantString.GetMethod = "get";
153
152
  ConstantString.PostMethod = "post";
154
153
  ConstantString.AdaptiveCardVersion = "1.5";
155
- ConstantString.AdaptiveCardSchema = "http://adaptivecards.io/schemas/adaptive-card.json";
154
+ ConstantString.AdaptiveCardSchema = "https://adaptivecards.io/schemas/adaptive-card.json";
156
155
  ConstantString.AdaptiveCardType = "AdaptiveCard";
157
156
  ConstantString.TextBlockType = "TextBlock";
158
157
  ConstantString.ImageType = "Image";
159
158
  ConstantString.ContainerType = "Container";
160
- ConstantString.RegistrationIdPostfix = {
161
- apiKey: "REGISTRATION_ID",
162
- oauth2: "CONFIGURATION_ID",
163
- http: "REGISTRATION_ID",
164
- openIdConnect: "REGISTRATION_ID",
165
- };
159
+ ConstantString.RegistrationIdPostfix = "REGISTRATION_ID";
166
160
  ConstantString.ResponseCodeFor20X = [
167
161
  "200",
168
162
  "201",
@@ -174,6 +168,7 @@ ConstantString.ResponseCodeFor20X = [
174
168
  "207",
175
169
  "208",
176
170
  "226",
171
+ "2XX",
177
172
  "default",
178
173
  ];
179
174
  ConstantString.AllOperationMethods = [
@@ -239,17 +234,6 @@ class SpecParserError extends Error {
239
234
 
240
235
  // Copyright (c) Microsoft Corporation.
241
236
  class Utils {
242
- static hasNestedObjectInSchema(schema) {
243
- if (this.isObjectSchema(schema)) {
244
- for (const property in schema.properties) {
245
- const nestedSchema = schema.properties[property];
246
- if (this.isObjectSchema(nestedSchema)) {
247
- return true;
248
- }
249
- }
250
- }
251
- return false;
252
- }
253
237
  static isObjectSchema(schema) {
254
238
  return schema.type === "object" || (!schema.type && !!schema.properties);
255
239
  }
@@ -262,11 +246,32 @@ class Utils {
262
246
  static isAPIKeyAuth(authScheme) {
263
247
  return authScheme.type === "apiKey";
264
248
  }
249
+ static isAPIKeyAuthButNotInCookie(authScheme) {
250
+ return authScheme.type === "apiKey" && authScheme.in !== "cookie";
251
+ }
265
252
  static isOAuthWithAuthCodeFlow(authScheme) {
266
253
  return !!(authScheme.type === "oauth2" &&
267
254
  authScheme.flows &&
268
255
  authScheme.flows.authorizationCode);
269
256
  }
257
+ static isNotSupportedAuth(authSchemeArray) {
258
+ if (authSchemeArray.length === 0) {
259
+ return false;
260
+ }
261
+ if (authSchemeArray.length > 0 && authSchemeArray.every((auths) => auths.length > 1)) {
262
+ return true;
263
+ }
264
+ for (const auths of authSchemeArray) {
265
+ if (auths.length === 1) {
266
+ if (Utils.isOAuthWithAuthCodeFlow(auths[0].authScheme) ||
267
+ Utils.isBearerTokenAuth(auths[0].authScheme) ||
268
+ Utils.isAPIKeyAuthButNotInCookie(auths[0].authScheme)) {
269
+ return false;
270
+ }
271
+ }
272
+ }
273
+ return true;
274
+ }
270
275
  static getAuthArray(securities, spec) {
271
276
  var _a;
272
277
  const result = [];
@@ -291,6 +296,20 @@ class Utils {
291
296
  result.sort((a, b) => a[0].name.localeCompare(b[0].name));
292
297
  return result;
293
298
  }
299
+ static getAuthMap(spec) {
300
+ const authMap = {};
301
+ for (const url in spec.paths) {
302
+ for (const method in spec.paths[url]) {
303
+ const operation = spec.paths[url][method];
304
+ const authArray = Utils.getAuthArray(operation.security, spec);
305
+ if (authArray && authArray.length > 0) {
306
+ const currentAuth = authArray[0][0];
307
+ authMap[operation.operationId] = currentAuth;
308
+ }
309
+ }
310
+ }
311
+ return authMap;
312
+ }
294
313
  static getAuthInfo(spec) {
295
314
  let authInfo = undefined;
296
315
  for (const url in spec.paths) {
@@ -319,27 +338,33 @@ class Utils {
319
338
  let multipleMediaType = false;
320
339
  for (const code of ConstantString.ResponseCodeFor20X) {
321
340
  const responseObject = (_a = operationObject === null || operationObject === void 0 ? void 0 : operationObject.responses) === null || _a === void 0 ? void 0 : _a[code];
322
- if (responseObject === null || responseObject === void 0 ? void 0 : responseObject.content) {
323
- for (const contentType of Object.keys(responseObject.content)) {
324
- // json media type can also be "application/json; charset=utf-8"
325
- if (contentType.indexOf("application/json") >= 0) {
326
- multipleMediaType = false;
327
- json = responseObject.content[contentType];
328
- if (Utils.containMultipleMediaTypes(responseObject)) {
329
- multipleMediaType = true;
330
- if (!allowMultipleMediaType) {
331
- json = {};
332
- }
333
- }
334
- else {
335
- return { json, multipleMediaType };
336
- }
337
- }
338
- }
341
+ if (!responseObject) {
342
+ continue;
343
+ }
344
+ multipleMediaType = Utils.containMultipleMediaTypes(responseObject);
345
+ if (!allowMultipleMediaType && multipleMediaType) {
346
+ json = {};
347
+ continue;
348
+ }
349
+ const mediaObj = Utils.getJsonContentType(responseObject);
350
+ if (Object.keys(mediaObj).length > 0) {
351
+ json = mediaObj;
352
+ return { json, multipleMediaType };
339
353
  }
340
354
  }
341
355
  return { json, multipleMediaType };
342
356
  }
357
+ static getJsonContentType(responseObject) {
358
+ if (responseObject.content) {
359
+ for (const contentType of Object.keys(responseObject.content)) {
360
+ // json media type can also be "application/json; charset=utf-8"
361
+ if (contentType.indexOf("application/json") >= 0) {
362
+ return responseObject.content[contentType];
363
+ }
364
+ }
365
+ }
366
+ return {};
367
+ }
343
368
  static convertPathToCamelCase(path) {
344
369
  const pathSegments = path.split(/[./{]/);
345
370
  const camelCaseSegments = pathSegments.map((segment) => {
@@ -375,7 +400,7 @@ class Utils {
375
400
  }
376
401
  return newStr;
377
402
  }
378
- static checkServerUrl(servers) {
403
+ static checkServerUrl(servers, allowHttp = false) {
379
404
  const errors = [];
380
405
  let serverUrl;
381
406
  try {
@@ -398,8 +423,7 @@ class Utils {
398
423
  data: servers,
399
424
  });
400
425
  }
401
- else if (protocol !== "https:") {
402
- // Http server url is not supported
426
+ else if (protocol !== "https:" && !(protocol === "http:" && allowHttp)) {
403
427
  const protocolString = protocol.slice(0, -1);
404
428
  errors.push({
405
429
  type: exports.ErrorType.UrlProtocolNotSupported,
@@ -415,10 +439,11 @@ class Utils {
415
439
  let hasTopLevelServers = false;
416
440
  let hasPathLevelServers = false;
417
441
  let hasOperationLevelServers = false;
442
+ const allowHttp = options.projectType === exports.ProjectType.Copilot;
418
443
  if (spec.servers && spec.servers.length >= 1) {
419
444
  hasTopLevelServers = true;
420
445
  // for multiple server, we only use the first url
421
- const serverErrors = Utils.checkServerUrl(spec.servers);
446
+ const serverErrors = Utils.checkServerUrl(spec.servers, allowHttp);
422
447
  errors.push(...serverErrors);
423
448
  }
424
449
  const paths = spec.paths;
@@ -426,7 +451,7 @@ class Utils {
426
451
  const methods = paths[path];
427
452
  if ((methods === null || methods === void 0 ? void 0 : methods.servers) && methods.servers.length >= 1) {
428
453
  hasPathLevelServers = true;
429
- const serverErrors = Utils.checkServerUrl(methods.servers);
454
+ const serverErrors = Utils.checkServerUrl(methods.servers, allowHttp);
430
455
  errors.push(...serverErrors);
431
456
  }
432
457
  for (const method in methods) {
@@ -434,7 +459,7 @@ class Utils {
434
459
  if (((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
435
460
  if ((operationObject === null || operationObject === void 0 ? void 0 : operationObject.servers) && operationObject.servers.length >= 1) {
436
461
  hasOperationLevelServers = true;
437
- const serverErrors = Utils.checkServerUrl(operationObject.servers);
462
+ const serverErrors = Utils.checkServerUrl(operationObject.servers, allowHttp);
438
463
  errors.push(...serverErrors);
439
464
  }
440
465
  }
@@ -723,22 +748,6 @@ class Validator {
723
748
  }
724
749
  return result;
725
750
  }
726
- validateResponse(method, path) {
727
- const result = { isValid: true, reason: [] };
728
- const operationObject = this.spec.paths[path][method];
729
- const { json, multipleMediaType } = Utils.getResponseJson(operationObject);
730
- if (this.options.projectType === exports.ProjectType.SME) {
731
- // only support response body only contains “application/json” content type
732
- if (multipleMediaType) {
733
- result.reason.push(exports.ErrorType.ResponseContainMultipleMediaTypes);
734
- }
735
- else if (Object.keys(json).length === 0) {
736
- // response body should not be empty
737
- result.reason.push(exports.ErrorType.ResponseJsonIsEmpty);
738
- }
739
- }
740
- return result;
741
- }
742
751
  validateServer(method, path) {
743
752
  const result = { isValid: true, reason: [] };
744
753
  const serverObj = Utils.getServerObject(this.spec, method, path);
@@ -747,8 +756,8 @@ class Validator {
747
756
  result.reason.push(exports.ErrorType.NoServerInformation);
748
757
  }
749
758
  else {
750
- // server url should be absolute url with https protocol
751
- const serverValidateResult = Utils.checkServerUrl([serverObj]);
759
+ const allowHttp = this.projectType === exports.ProjectType.Copilot;
760
+ const serverValidateResult = Utils.checkServerUrl([serverObj], allowHttp);
752
761
  result.reason.push(...serverValidateResult.map((item) => item.type));
753
762
  }
754
763
  return result;
@@ -771,6 +780,9 @@ class Validator {
771
780
  reason: [exports.ErrorType.MultipleAuthNotSupported],
772
781
  };
773
782
  }
783
+ if (this.projectType === exports.ProjectType.Copilot) {
784
+ return { isValid: true, reason: [] };
785
+ }
774
786
  for (const auths of authSchemeArray) {
775
787
  if (auths.length === 1) {
776
788
  if ((this.options.allowAPIKeyAuth && Utils.isAPIKeyAuth(auths[0].authScheme)) ||
@@ -783,114 +795,6 @@ class Validator {
783
795
  }
784
796
  return { isValid: false, reason: [exports.ErrorType.AuthTypeIsNotSupported] };
785
797
  }
786
- checkPostBodySchema(schema, isRequired = false) {
787
- var _a;
788
- const paramResult = {
789
- requiredNum: 0,
790
- optionalNum: 0,
791
- isValid: true,
792
- reason: [],
793
- };
794
- if (Object.keys(schema).length === 0) {
795
- return paramResult;
796
- }
797
- const isRequiredWithoutDefault = isRequired && schema.default === undefined;
798
- const isCopilot = this.projectType === exports.ProjectType.Copilot;
799
- if (isCopilot && Utils.hasNestedObjectInSchema(schema)) {
800
- paramResult.isValid = false;
801
- paramResult.reason = [exports.ErrorType.RequestBodyContainsNestedObject];
802
- return paramResult;
803
- }
804
- if (schema.type === "string" ||
805
- schema.type === "integer" ||
806
- schema.type === "boolean" ||
807
- schema.type === "number") {
808
- if (isRequiredWithoutDefault) {
809
- paramResult.requiredNum = paramResult.requiredNum + 1;
810
- }
811
- else {
812
- paramResult.optionalNum = paramResult.optionalNum + 1;
813
- }
814
- }
815
- else if (Utils.isObjectSchema(schema)) {
816
- const { properties } = schema;
817
- for (const property in properties) {
818
- let isRequired = false;
819
- if (schema.required && ((_a = schema.required) === null || _a === void 0 ? void 0 : _a.indexOf(property)) >= 0) {
820
- isRequired = true;
821
- }
822
- const result = this.checkPostBodySchema(properties[property], isRequired);
823
- paramResult.requiredNum += result.requiredNum;
824
- paramResult.optionalNum += result.optionalNum;
825
- paramResult.isValid = paramResult.isValid && result.isValid;
826
- paramResult.reason.push(...result.reason);
827
- }
828
- }
829
- else {
830
- if (isRequiredWithoutDefault && !isCopilot) {
831
- paramResult.isValid = false;
832
- paramResult.reason.push(exports.ErrorType.PostBodyContainsRequiredUnsupportedSchema);
833
- }
834
- }
835
- return paramResult;
836
- }
837
- checkParamSchema(paramObject) {
838
- const paramResult = {
839
- requiredNum: 0,
840
- optionalNum: 0,
841
- isValid: true,
842
- reason: [],
843
- };
844
- if (!paramObject) {
845
- return paramResult;
846
- }
847
- const isCopilot = this.projectType === exports.ProjectType.Copilot;
848
- for (let i = 0; i < paramObject.length; i++) {
849
- const param = paramObject[i];
850
- const schema = param.schema;
851
- if (isCopilot && Utils.hasNestedObjectInSchema(schema)) {
852
- paramResult.isValid = false;
853
- paramResult.reason.push(exports.ErrorType.ParamsContainsNestedObject);
854
- continue;
855
- }
856
- const isRequiredWithoutDefault = param.required && schema.default === undefined;
857
- if (isCopilot) {
858
- if (isRequiredWithoutDefault) {
859
- paramResult.requiredNum = paramResult.requiredNum + 1;
860
- }
861
- else {
862
- paramResult.optionalNum = paramResult.optionalNum + 1;
863
- }
864
- continue;
865
- }
866
- if (param.in === "header" || param.in === "cookie") {
867
- if (isRequiredWithoutDefault) {
868
- paramResult.isValid = false;
869
- paramResult.reason.push(exports.ErrorType.ParamsContainRequiredUnsupportedSchema);
870
- }
871
- continue;
872
- }
873
- if (schema.type !== "boolean" &&
874
- schema.type !== "string" &&
875
- schema.type !== "number" &&
876
- schema.type !== "integer") {
877
- if (isRequiredWithoutDefault) {
878
- paramResult.isValid = false;
879
- paramResult.reason.push(exports.ErrorType.ParamsContainRequiredUnsupportedSchema);
880
- }
881
- continue;
882
- }
883
- if (param.in === "query" || param.in === "path") {
884
- if (isRequiredWithoutDefault) {
885
- paramResult.requiredNum = paramResult.requiredNum + 1;
886
- }
887
- else {
888
- paramResult.optionalNum = paramResult.optionalNum + 1;
889
- }
890
- }
891
- }
892
- return paramResult;
893
- }
894
798
  }
895
799
 
896
800
  // Copyright (c) Microsoft Corporation.
@@ -900,7 +804,6 @@ class CopilotValidator extends Validator {
900
804
  this.projectType = exports.ProjectType.Copilot;
901
805
  this.options = options;
902
806
  this.spec = spec;
903
- this.checkCircularReference();
904
807
  }
905
808
  validateSpec() {
906
809
  const result = { errors: [], warnings: [] };
@@ -926,10 +829,6 @@ class CopilotValidator extends Validator {
926
829
  if (!methodAndPathResult.isValid) {
927
830
  return methodAndPathResult;
928
831
  }
929
- const circularReferenceResult = this.validateCircularReference(method, path);
930
- if (!circularReferenceResult.isValid) {
931
- return circularReferenceResult;
932
- }
933
832
  const operationObject = this.spec.paths[path][method];
934
833
  // validate auth
935
834
  const authCheckResult = this.validateAuth(method, path);
@@ -941,24 +840,6 @@ class CopilotValidator extends Validator {
941
840
  // validate server
942
841
  const validateServerResult = this.validateServer(method, path);
943
842
  result.reason.push(...validateServerResult.reason);
944
- // validate response
945
- const validateResponseResult = this.validateResponse(method, path);
946
- result.reason.push(...validateResponseResult.reason);
947
- // validate requestBody
948
- const requestBody = operationObject.requestBody;
949
- const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
950
- if (requestJsonBody) {
951
- const requestBodySchema = requestJsonBody.schema;
952
- if (!Utils.isObjectSchema(requestBodySchema)) {
953
- result.reason.push(exports.ErrorType.PostBodySchemaIsNotJson);
954
- }
955
- const requestBodyParamResult = this.checkPostBodySchema(requestBodySchema, requestBody.required);
956
- result.reason.push(...requestBodyParamResult.reason);
957
- }
958
- // validate parameters
959
- const paramObject = operationObject.parameters;
960
- const paramResult = this.checkParamSchema(paramObject);
961
- result.reason.push(...paramResult.reason);
962
843
  if (result.reason.length > 0) {
963
844
  result.isValid = false;
964
845
  }
@@ -1050,6 +931,108 @@ class SMEValidator extends Validator {
1050
931
  }
1051
932
  return result;
1052
933
  }
934
+ validateResponse(method, path) {
935
+ const result = { isValid: true, reason: [] };
936
+ const operationObject = this.spec.paths[path][method];
937
+ const { json, multipleMediaType } = Utils.getResponseJson(operationObject);
938
+ // only support response body only contains “application/json” content type
939
+ if (multipleMediaType) {
940
+ result.reason.push(exports.ErrorType.ResponseContainMultipleMediaTypes);
941
+ }
942
+ else if (Object.keys(json).length === 0) {
943
+ // response body should not be empty
944
+ result.reason.push(exports.ErrorType.ResponseJsonIsEmpty);
945
+ }
946
+ return result;
947
+ }
948
+ checkPostBodySchema(schema, isRequired = false) {
949
+ var _a;
950
+ const paramResult = {
951
+ requiredNum: 0,
952
+ optionalNum: 0,
953
+ isValid: true,
954
+ reason: [],
955
+ };
956
+ if (Object.keys(schema).length === 0) {
957
+ return paramResult;
958
+ }
959
+ const isRequiredWithoutDefault = isRequired && schema.default === undefined;
960
+ const isCopilot = this.projectType === exports.ProjectType.Copilot;
961
+ if (schema.type === "string" ||
962
+ schema.type === "integer" ||
963
+ schema.type === "boolean" ||
964
+ schema.type === "number") {
965
+ if (isRequiredWithoutDefault) {
966
+ paramResult.requiredNum = paramResult.requiredNum + 1;
967
+ }
968
+ else {
969
+ paramResult.optionalNum = paramResult.optionalNum + 1;
970
+ }
971
+ }
972
+ else if (Utils.isObjectSchema(schema)) {
973
+ const { properties } = schema;
974
+ for (const property in properties) {
975
+ let isRequired = false;
976
+ if (schema.required && ((_a = schema.required) === null || _a === void 0 ? void 0 : _a.indexOf(property)) >= 0) {
977
+ isRequired = true;
978
+ }
979
+ const result = this.checkPostBodySchema(properties[property], isRequired);
980
+ paramResult.requiredNum += result.requiredNum;
981
+ paramResult.optionalNum += result.optionalNum;
982
+ paramResult.isValid = paramResult.isValid && result.isValid;
983
+ paramResult.reason.push(...result.reason);
984
+ }
985
+ }
986
+ else {
987
+ if (isRequiredWithoutDefault && !isCopilot) {
988
+ paramResult.isValid = false;
989
+ paramResult.reason.push(exports.ErrorType.PostBodyContainsRequiredUnsupportedSchema);
990
+ }
991
+ }
992
+ return paramResult;
993
+ }
994
+ checkParamSchema(paramObject) {
995
+ const paramResult = {
996
+ requiredNum: 0,
997
+ optionalNum: 0,
998
+ isValid: true,
999
+ reason: [],
1000
+ };
1001
+ if (!paramObject) {
1002
+ return paramResult;
1003
+ }
1004
+ for (let i = 0; i < paramObject.length; i++) {
1005
+ const param = paramObject[i];
1006
+ const schema = param.schema;
1007
+ const isRequiredWithoutDefault = param.required && schema.default === undefined;
1008
+ if (param.in === "header" || param.in === "cookie") {
1009
+ if (isRequiredWithoutDefault) {
1010
+ paramResult.isValid = false;
1011
+ paramResult.reason.push(exports.ErrorType.ParamsContainRequiredUnsupportedSchema);
1012
+ }
1013
+ continue;
1014
+ }
1015
+ if (schema.type !== "boolean" &&
1016
+ schema.type !== "string" &&
1017
+ schema.type !== "number" &&
1018
+ schema.type !== "integer") {
1019
+ if (isRequiredWithoutDefault) {
1020
+ paramResult.isValid = false;
1021
+ paramResult.reason.push(exports.ErrorType.ParamsContainRequiredUnsupportedSchema);
1022
+ }
1023
+ continue;
1024
+ }
1025
+ if (param.in === "query" || param.in === "path") {
1026
+ if (isRequiredWithoutDefault) {
1027
+ paramResult.requiredNum = paramResult.requiredNum + 1;
1028
+ }
1029
+ else {
1030
+ paramResult.optionalNum = paramResult.optionalNum + 1;
1031
+ }
1032
+ }
1033
+ }
1034
+ return paramResult;
1035
+ }
1053
1036
  validateParamCount(postBodyResult, paramResult) {
1054
1037
  const result = { isValid: true, reason: [] };
1055
1038
  const totalRequiredParams = postBodyResult.requiredNum + paramResult.requiredNum;
@@ -1751,7 +1734,7 @@ function inferProperties(card) {
1751
1734
 
1752
1735
  // Copyright (c) Microsoft Corporation.
1753
1736
  class ManifestUpdater {
1754
- static updateManifestWithAiPlugin(manifestPath, outputSpecPath, apiPluginFilePath, spec, options, authInfo, existingPluginManifestInfo) {
1737
+ static updateManifestWithAiPlugin(manifestPath, outputSpecPath, apiPluginFilePath, spec, options, authMap, existingPluginManifestInfo) {
1755
1738
  return __awaiter(this, void 0, void 0, function* () {
1756
1739
  const manifest = yield fs__default['default'].readJSON(manifestPath);
1757
1740
  const apiPluginRelativePath = ManifestUpdater.getRelativePath(manifestPath, apiPluginFilePath);
@@ -1782,7 +1765,7 @@ class ManifestUpdater {
1782
1765
  }
1783
1766
  const appName = this.removeEnvs(manifest.name.short);
1784
1767
  const specRelativePath = ManifestUpdater.getRelativePath(manifestPath, outputSpecPath);
1785
- const [apiPlugin, warnings] = yield ManifestUpdater.generatePluginManifestSchema(spec, specRelativePath, apiPluginFilePath, appName, authInfo, options, existingPluginManifestInfo);
1768
+ const [apiPlugin, warnings] = yield ManifestUpdater.generatePluginManifestSchema(spec, specRelativePath, apiPluginFilePath, appName, authMap, options, existingPluginManifestInfo);
1786
1769
  return [manifest, apiPlugin, warnings];
1787
1770
  });
1788
1771
  }
@@ -1805,29 +1788,14 @@ class ManifestUpdater {
1805
1788
  throw new SpecParserError(Utils.format(ConstantString.UnsupportedSchema, method, pathUrl, JSON.stringify(schema)), exports.ErrorType.UpdateManifestFailed);
1806
1789
  }
1807
1790
  }
1808
- static generatePluginManifestSchema(spec, specRelativePath, apiPluginFilePath, appName, authInfo, options, existingPluginManifestInfo) {
1791
+ static generatePluginManifestSchema(spec, specRelativePath, apiPluginFilePath, appName, authMap, options, existingPluginManifestInfo) {
1809
1792
  var _a, _b, _c, _d;
1810
1793
  return __awaiter(this, void 0, void 0, function* () {
1811
1794
  const warnings = [];
1812
1795
  const functions = [];
1813
- const functionNames = [];
1796
+ const functionNamesMap = {};
1814
1797
  const conversationStarters = [];
1815
1798
  const paths = spec.paths;
1816
- const pluginAuthObj = {
1817
- type: "None",
1818
- };
1819
- if (authInfo) {
1820
- if (Utils.isOAuthWithAuthCodeFlow(authInfo.authScheme)) {
1821
- pluginAuthObj.type = "OAuthPluginVault";
1822
- }
1823
- else if (Utils.isBearerTokenAuth(authInfo.authScheme)) {
1824
- pluginAuthObj.type = "ApiKeyPluginVault";
1825
- }
1826
- if (pluginAuthObj.type !== "None") {
1827
- const safeRegistrationIdName = Utils.getSafeRegistrationIdEnvName(`${authInfo.name}_${ConstantString.RegistrationIdPostfix[authInfo.authScheme.type]}`);
1828
- pluginAuthObj.reference_id = `\${{${safeRegistrationIdName}}}`;
1829
- }
1830
- }
1831
1799
  for (const pathUrl in paths) {
1832
1800
  const pathItem = paths[pathUrl];
1833
1801
  if (pathItem) {
@@ -1835,36 +1803,11 @@ class ManifestUpdater {
1835
1803
  for (const method in operations) {
1836
1804
  if (options.allowMethods.includes(method)) {
1837
1805
  const operationItem = operations[method];
1838
- const confirmationBodies = [];
1839
1806
  if (operationItem) {
1840
1807
  const operationId = operationItem.operationId;
1841
1808
  const safeFunctionName = operationId.replace(/[^a-zA-Z0-9]/g, "_");
1842
1809
  const description = (_a = operationItem.description) !== null && _a !== void 0 ? _a : "";
1843
1810
  const summary = operationItem.summary;
1844
- const paramObject = operationItem.parameters;
1845
- const requestBody = operationItem.requestBody;
1846
- if (paramObject) {
1847
- for (let i = 0; i < paramObject.length; i++) {
1848
- const param = paramObject[i];
1849
- const schema = param.schema;
1850
- ManifestUpdater.checkSchema(schema, method, pathUrl);
1851
- confirmationBodies.push(ManifestUpdater.getConfirmationBodyItem(param.name));
1852
- }
1853
- }
1854
- if (requestBody) {
1855
- const requestJsonBody = requestBody.content["application/json"];
1856
- const requestBodySchema = requestJsonBody.schema;
1857
- if (Utils.isObjectSchema(requestBodySchema)) {
1858
- for (const property in requestBodySchema.properties) {
1859
- const schema = requestBodySchema.properties[property];
1860
- ManifestUpdater.checkSchema(schema, method, pathUrl);
1861
- confirmationBodies.push(ManifestUpdater.getConfirmationBodyItem(property));
1862
- }
1863
- }
1864
- else {
1865
- throw new SpecParserError(Utils.format(ConstantString.UnsupportedSchema, method, pathUrl, JSON.stringify(requestBodySchema)), exports.ErrorType.UpdateManifestFailed);
1866
- }
1867
- }
1868
1811
  let funcDescription = operationItem.description || operationItem.summary || "";
1869
1812
  if (funcDescription.length > ConstantString.FunctionDescriptionMaxLens) {
1870
1813
  warnings.push({
@@ -1898,19 +1841,66 @@ class ManifestUpdater {
1898
1841
  }
1899
1842
  }
1900
1843
  if (options.allowConfirmation && method !== ConstantString.GetMethod) {
1901
- if (!funcObj.capabilities) {
1902
- funcObj.capabilities = {};
1844
+ const paramObject = operationItem.parameters;
1845
+ const requestBody = operationItem.requestBody;
1846
+ const confirmationBodies = [];
1847
+ if (paramObject) {
1848
+ for (let i = 0; i < paramObject.length; i++) {
1849
+ const param = paramObject[i];
1850
+ const schema = param.schema;
1851
+ ManifestUpdater.checkSchema(schema, method, pathUrl);
1852
+ confirmationBodies.push(ManifestUpdater.getConfirmationBodyItem(param.name));
1853
+ }
1854
+ }
1855
+ if (requestBody) {
1856
+ const requestJsonBody = Utils.getJsonContentType(requestBody);
1857
+ const requestBodySchema = requestJsonBody.schema;
1858
+ if (Utils.isObjectSchema(requestBodySchema)) {
1859
+ for (const property in requestBodySchema.properties) {
1860
+ const schema = requestBodySchema.properties[property];
1861
+ ManifestUpdater.checkSchema(schema, method, pathUrl);
1862
+ confirmationBodies.push(ManifestUpdater.getConfirmationBodyItem(property));
1863
+ }
1864
+ }
1865
+ else {
1866
+ throw new SpecParserError(Utils.format(ConstantString.UnsupportedSchema, method, pathUrl, JSON.stringify(requestBodySchema)), exports.ErrorType.UpdateManifestFailed);
1867
+ }
1903
1868
  }
1904
- funcObj.capabilities.confirmation = {
1905
- type: "AdaptiveCard",
1906
- title: (_b = operationItem.summary) !== null && _b !== void 0 ? _b : description,
1907
- };
1908
1869
  if (confirmationBodies.length > 0) {
1870
+ if (!funcObj.capabilities) {
1871
+ funcObj.capabilities = {};
1872
+ }
1873
+ funcObj.capabilities.confirmation = {
1874
+ type: "AdaptiveCard",
1875
+ title: (_b = operationItem.summary) !== null && _b !== void 0 ? _b : description,
1876
+ };
1909
1877
  funcObj.capabilities.confirmation.body = confirmationBodies.join("\n");
1910
1878
  }
1911
1879
  }
1912
1880
  functions.push(funcObj);
1913
- functionNames.push(safeFunctionName);
1881
+ const authInfo = authMap[operationId];
1882
+ let key = "None";
1883
+ let authName = "None";
1884
+ if (authInfo) {
1885
+ if (Utils.isOAuthWithAuthCodeFlow(authInfo.authScheme)) {
1886
+ key = "OAuthPluginVault";
1887
+ authName = authInfo.name;
1888
+ }
1889
+ else if (Utils.isBearerTokenAuth(authInfo.authScheme) ||
1890
+ Utils.isAPIKeyAuthButNotInCookie(authInfo.authScheme)) {
1891
+ key = "ApiKeyPluginVault";
1892
+ authName = authInfo.name;
1893
+ }
1894
+ }
1895
+ if (functionNamesMap[key]) {
1896
+ functionNamesMap[key].functionNames.push(safeFunctionName);
1897
+ }
1898
+ else {
1899
+ functionNamesMap[key] = {
1900
+ functionNames: [safeFunctionName],
1901
+ authName: authName,
1902
+ };
1903
+ }
1914
1904
  const conversationStarterStr = (summary !== null && summary !== void 0 ? summary : description).slice(0, ConstantString.ConversationStarterMaxLens);
1915
1905
  if (conversationStarterStr) {
1916
1906
  conversationStarters.push(conversationStarterStr);
@@ -1920,6 +1910,12 @@ class ManifestUpdater {
1920
1910
  }
1921
1911
  }
1922
1912
  }
1913
+ if (Object.keys(functionNamesMap).length === 0) {
1914
+ functionNamesMap["None"] = {
1915
+ functionNames: [],
1916
+ authName: "None",
1917
+ };
1918
+ }
1923
1919
  let apiPlugin;
1924
1920
  if (yield fs__default['default'].pathExists(apiPluginFilePath)) {
1925
1921
  apiPlugin = yield fs__default['default'].readJSON(apiPluginFilePath);
@@ -1955,24 +1951,35 @@ class ManifestUpdater {
1955
1951
  const relativePath = ManifestUpdater.getRelativePath(existingPluginManifestInfo.manifestPath, existingPluginManifestInfo.specPath);
1956
1952
  apiPlugin.runtimes = apiPlugin.runtimes.filter((runtime) => runtime.spec.url !== relativePath);
1957
1953
  }
1958
- const index = apiPlugin.runtimes.findIndex((runtime) => {
1959
- var _a, _b;
1960
- return runtime.spec.url === specRelativePath &&
1961
- runtime.type === "OpenApi" &&
1962
- ((_b = (_a = runtime.auth) === null || _a === void 0 ? void 0 : _a.type) !== null && _b !== void 0 ? _b : "None") === pluginAuthObj.type;
1963
- });
1964
- if (index === -1) {
1965
- apiPlugin.runtimes.push({
1966
- type: "OpenApi",
1967
- auth: pluginAuthObj,
1968
- spec: {
1969
- url: specRelativePath,
1970
- },
1971
- run_for_functions: functionNames,
1954
+ for (const authType in functionNamesMap) {
1955
+ const pluginAuthObj = {
1956
+ type: authType,
1957
+ };
1958
+ const authName = functionNamesMap[authType].authName;
1959
+ if (pluginAuthObj.type !== "None") {
1960
+ const safeRegistrationIdName = Utils.getSafeRegistrationIdEnvName(`${authName}_${ConstantString.RegistrationIdPostfix}`);
1961
+ pluginAuthObj.reference_id = `\${{${safeRegistrationIdName}}}`;
1962
+ }
1963
+ const functionNamesInfo = functionNamesMap[authType];
1964
+ const index = apiPlugin.runtimes.findIndex((runtime) => {
1965
+ var _a, _b;
1966
+ return runtime.spec.url === specRelativePath &&
1967
+ runtime.type === "OpenApi" &&
1968
+ ((_b = (_a = runtime.auth) === null || _a === void 0 ? void 0 : _a.type) !== null && _b !== void 0 ? _b : "None") === authType;
1972
1969
  });
1973
- }
1974
- else {
1975
- apiPlugin.runtimes[index].run_for_functions = functionNames;
1970
+ if (index === -1) {
1971
+ apiPlugin.runtimes.push({
1972
+ type: "OpenApi",
1973
+ auth: pluginAuthObj,
1974
+ spec: {
1975
+ url: specRelativePath,
1976
+ },
1977
+ run_for_functions: functionNamesInfo.functionNames,
1978
+ });
1979
+ }
1980
+ else {
1981
+ apiPlugin.runtimes[index].run_for_functions = functionNamesInfo.functionNames;
1982
+ }
1976
1983
  }
1977
1984
  if (!apiPlugin.name_for_human) {
1978
1985
  apiPlugin.name_for_human = appName;
@@ -2015,7 +2022,7 @@ class ManifestUpdater {
2015
2022
  };
2016
2023
  if (authInfo) {
2017
2024
  const auth = authInfo.authScheme;
2018
- const safeRegistrationIdName = Utils.getSafeRegistrationIdEnvName(`${authInfo.name}_${ConstantString.RegistrationIdPostfix[authInfo.authScheme.type]}`);
2025
+ const safeRegistrationIdName = Utils.getSafeRegistrationIdEnvName(`${authInfo.name}_${ConstantString.RegistrationIdPostfix}`);
2019
2026
  if (Utils.isAPIKeyAuth(auth) || Utils.isBearerTokenAuth(auth)) {
2020
2027
  composeExtension.authorization = {
2021
2028
  authType: "apiSecretServiceAuth",
@@ -2162,8 +2169,11 @@ class SpecParser {
2162
2169
  yield this.parser.validate(this.spec);
2163
2170
  }
2164
2171
  else {
2172
+ // The following code still hangs for Graph API, support will be added when SwaggerParser is updated.
2173
+ /*
2165
2174
  const clonedUnResolveSpec = JSON.parse(JSON.stringify(this.unResolveSpec));
2166
- yield this.parser.validate(clonedUnResolveSpec);
2175
+ await this.parser.validate(clonedUnResolveSpec);
2176
+ */
2167
2177
  }
2168
2178
  }
2169
2179
  catch (e) {
@@ -2326,7 +2336,7 @@ class SpecParser {
2326
2336
  throw new SpecParserError(ConstantString.CancelledMessage, exports.ErrorType.Cancelled);
2327
2337
  }
2328
2338
  const clonedUnResolveSpec = JSON.parse(JSON.stringify(newUnResolvedSpec));
2329
- const newSpec = (yield this.parser.dereference(clonedUnResolveSpec));
2339
+ const newSpec = yield this.deReferenceSpec(clonedUnResolveSpec);
2330
2340
  return [newUnResolvedSpec, newSpec];
2331
2341
  }
2332
2342
  catch (err) {
@@ -2337,6 +2347,12 @@ class SpecParser {
2337
2347
  }
2338
2348
  });
2339
2349
  }
2350
+ deReferenceSpec(spec) {
2351
+ return __awaiter(this, void 0, void 0, function* () {
2352
+ const result = yield this.parser.dereference(spec);
2353
+ return result;
2354
+ });
2355
+ }
2340
2356
  /**
2341
2357
  * Generates and update artifacts from the OpenAPI specification file. Generate Adaptive Cards, update Teams app manifest, and generate a new OpenAPI specification file.
2342
2358
  * @param manifestPath A file path of the Teams app manifest file to update.
@@ -2354,7 +2370,6 @@ class SpecParser {
2354
2370
  const newSpecs = yield this.getFilteredSpecs(filter, signal);
2355
2371
  const newUnResolvedSpec = newSpecs[0];
2356
2372
  const newSpec = newSpecs[1];
2357
- const authInfo = Utils.getAuthInfo(newSpec);
2358
2373
  const paths = newUnResolvedSpec.paths;
2359
2374
  for (const pathUrl in paths) {
2360
2375
  const operations = paths[pathUrl];
@@ -2362,15 +2377,22 @@ class SpecParser {
2362
2377
  const operationItem = operations[method];
2363
2378
  const operationId = operationItem.operationId;
2364
2379
  const containsSpecialCharacters = /[^a-zA-Z0-9_]/.test(operationId);
2365
- if (!containsSpecialCharacters) {
2366
- continue;
2380
+ if (containsSpecialCharacters) {
2381
+ operationItem.operationId = operationId.replace(/[^a-zA-Z0-9]/g, "_");
2382
+ result.warnings.push({
2383
+ type: exports.WarningType.OperationIdContainsSpecialCharacters,
2384
+ content: Utils.format(ConstantString.OperationIdContainsSpecialCharacters, operationId, operationItem.operationId),
2385
+ data: operationId,
2386
+ });
2387
+ }
2388
+ const authArray = Utils.getAuthArray(operationItem.security, newSpec);
2389
+ if (Utils.isNotSupportedAuth(authArray)) {
2390
+ result.warnings.push({
2391
+ type: exports.WarningType.UnsupportedAuthType,
2392
+ content: Utils.format(ConstantString.AuthTypeIsNotSupported, operationId),
2393
+ data: operationId,
2394
+ });
2367
2395
  }
2368
- operationItem.operationId = operationId.replace(/[^a-zA-Z0-9]/g, "_");
2369
- result.warnings.push({
2370
- type: exports.WarningType.OperationIdContainsSpecialCharacters,
2371
- content: Utils.format(ConstantString.OperationIdContainsSpecialCharacters, operationId, operationItem.operationId),
2372
- data: operationId,
2373
- });
2374
2396
  }
2375
2397
  }
2376
2398
  yield this.saveFilterSpec(outputSpecPath, newUnResolvedSpec);
@@ -2383,7 +2405,8 @@ class SpecParser {
2383
2405
  specPath: this.pathOrSpec,
2384
2406
  }
2385
2407
  : undefined;
2386
- const [updatedManifest, apiPlugin, warnings] = yield ManifestUpdater.updateManifestWithAiPlugin(manifestPath, outputSpecPath, pluginFilePath, newSpec, this.options, authInfo, existingPluginManifestInfo);
2408
+ const authMap = Utils.getAuthMap(newSpec);
2409
+ const [updatedManifest, apiPlugin, warnings] = yield ManifestUpdater.updateManifestWithAiPlugin(manifestPath, outputSpecPath, pluginFilePath, newSpec, this.options, authMap, existingPluginManifestInfo);
2387
2410
  result.warnings.push(...warnings);
2388
2411
  yield fs__default['default'].outputJSON(manifestPath, updatedManifest, { spaces: 4 });
2389
2412
  yield fs__default['default'].outputJSON(pluginFilePath, apiPlugin, { spaces: 4 });
@@ -2463,6 +2486,54 @@ class SpecParser {
2463
2486
  return result;
2464
2487
  });
2465
2488
  }
2489
+ /**
2490
+ * Generates and update artifacts from the OpenAPI specification file. Generate Adaptive Cards, update Teams app manifest, and generate a new OpenAPI specification file.
2491
+ * @param pluginFilePath A file path of the plugin manifest file to update.
2492
+ * @param filter An array of strings that represent the filters to apply when generating the artifacts. If filter is empty, it would process nothing.
2493
+ */
2494
+ generateAdaptiveCardInPlugin(pluginFilePath, filter, signal) {
2495
+ return __awaiter(this, void 0, void 0, function* () {
2496
+ if (!this.options.allowResponseSemantics) {
2497
+ return;
2498
+ }
2499
+ const newSpecs = yield this.getFilteredSpecs(filter, signal);
2500
+ const newSpec = newSpecs[1];
2501
+ const apiPlugin = (yield fs__default['default'].readJSON(pluginFilePath));
2502
+ const paths = newSpec.paths;
2503
+ for (const pathUrl in paths) {
2504
+ const pathItem = paths[pathUrl];
2505
+ if (pathItem) {
2506
+ const operations = pathItem;
2507
+ for (const method in operations) {
2508
+ if (this.options.allowMethods.includes(method)) {
2509
+ const operationItem = operations[method];
2510
+ if (operationItem) {
2511
+ try {
2512
+ const operationId = operationItem.operationId;
2513
+ const safeFunctionName = operationId.replace(/[^a-zA-Z0-9]/g, "_");
2514
+ if (apiPlugin.functions.findIndex((func) => func.name === safeFunctionName) === -1) {
2515
+ continue;
2516
+ }
2517
+ const { json } = Utils.getResponseJson(operationItem);
2518
+ if (json.schema) {
2519
+ const [card, jsonPath] = AdaptiveCardGenerator.generateAdaptiveCard(operationItem, false, 5);
2520
+ const responseSemantic = wrapResponseSemantics(card, jsonPath);
2521
+ apiPlugin.functions.find((func) => func.name === safeFunctionName).capabilities = {
2522
+ response_semantics: responseSemantic,
2523
+ };
2524
+ }
2525
+ }
2526
+ catch (err) {
2527
+ throw new SpecParserError(err.toString(), exports.ErrorType.GenerateAdaptiveCardFailed);
2528
+ }
2529
+ }
2530
+ }
2531
+ }
2532
+ }
2533
+ }
2534
+ yield fs__default['default'].outputJSON(pluginFilePath, apiPlugin, { spaces: 4 });
2535
+ });
2536
+ }
2466
2537
  loadSpec() {
2467
2538
  return __awaiter(this, void 0, void 0, function* () {
2468
2539
  if (!this.spec) {
@@ -2475,7 +2546,7 @@ class SpecParser {
2475
2546
  this.isSwaggerFile = true;
2476
2547
  }
2477
2548
  const clonedUnResolveSpec = JSON.parse(JSON.stringify(this.unResolveSpec));
2478
- this.spec = (yield this.parser.dereference(clonedUnResolveSpec));
2549
+ this.spec = yield this.deReferenceSpec(clonedUnResolveSpec);
2479
2550
  }
2480
2551
  });
2481
2552
  }