@microsoft/m365-spec-parser 0.2.4 → 0.2.5-alpha.038ce65fb.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.
@@ -5,6 +5,7 @@ import fs from 'fs-extra';
5
5
  import path from 'path';
6
6
  import { ManifestUtil } from '@microsoft/teams-manifest';
7
7
  import { createHash } from 'crypto';
8
+ import { $RefParser } from '@apidevtools/json-schema-ref-parser';
8
9
 
9
10
  // Copyright (c) Microsoft Corporation.
10
11
  /**
@@ -38,11 +39,8 @@ var ErrorType;
38
39
  ErrorType["PostBodyContainMultipleMediaTypes"] = "post-body-contain-multiple-media-types";
39
40
  ErrorType["ResponseContainMultipleMediaTypes"] = "response-contain-multiple-media-types";
40
41
  ErrorType["ResponseJsonIsEmpty"] = "response-json-is-empty";
41
- ErrorType["PostBodySchemaIsNotJson"] = "post-body-schema-is-not-json";
42
42
  ErrorType["PostBodyContainsRequiredUnsupportedSchema"] = "post-body-contains-required-unsupported-schema";
43
43
  ErrorType["ParamsContainRequiredUnsupportedSchema"] = "params-contain-required-unsupported-schema";
44
- ErrorType["ParamsContainsNestedObject"] = "params-contains-nested-object";
45
- ErrorType["RequestBodyContainsNestedObject"] = "request-body-contains-nested-object";
46
44
  ErrorType["ExceededRequiredParamsLimit"] = "exceeded-required-params-limit";
47
45
  ErrorType["NoParameter"] = "no-parameter";
48
46
  ErrorType["NoAPIInfo"] = "no-api-info";
@@ -62,6 +60,7 @@ var WarningType;
62
60
  WarningType["ConvertSwaggerToOpenAPI"] = "convert-swagger-to-openapi";
63
61
  WarningType["FuncDescriptionTooLong"] = "function-description-too-long";
64
62
  WarningType["OperationIdContainsSpecialCharacters"] = "operationid-contains-special-characters";
63
+ WarningType["UnsupportedAuthType"] = "unsupported-auth-type";
65
64
  WarningType["GenerateJsonDataFailed"] = "generate-json-data-failed";
66
65
  WarningType["Unknown"] = "unknown";
67
66
  })(WarningType || (WarningType = {}));
@@ -101,6 +100,7 @@ ConstantString.SwaggerNotSupported = "Swagger 2.0 is not supported. Please conve
101
100
  ConstantString.SpecVersionNotSupported = "Unsupported OpenAPI version %s. Please use version 3.0.x.";
102
101
  ConstantString.MultipleAuthNotSupported = "Multiple authentication methods are unsupported. Ensure all selected APIs use identical authentication.";
103
102
  ConstantString.OperationIdContainsSpecialCharacters = "Operation id '%s' in OpenAPI description document contained special characters and was renamed to '%s'.";
103
+ ConstantString.AuthTypeIsNotSupported = "Unsupported authorization type in API '%s'. No authorization will be used.";
104
104
  ConstantString.UnsupportedSchema = "Unsupported schema in %s %s: %s";
105
105
  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.";
106
106
  ConstantString.GenerateJsonDataFailed = "Failed to generate JSON data for api: %s due to %s.";
@@ -110,17 +110,12 @@ ConstantString.WrappedCardResponseLayout = "list";
110
110
  ConstantString.GetMethod = "get";
111
111
  ConstantString.PostMethod = "post";
112
112
  ConstantString.AdaptiveCardVersion = "1.5";
113
- ConstantString.AdaptiveCardSchema = "http://adaptivecards.io/schemas/adaptive-card.json";
113
+ ConstantString.AdaptiveCardSchema = "https://adaptivecards.io/schemas/adaptive-card.json";
114
114
  ConstantString.AdaptiveCardType = "AdaptiveCard";
115
115
  ConstantString.TextBlockType = "TextBlock";
116
116
  ConstantString.ImageType = "Image";
117
117
  ConstantString.ContainerType = "Container";
118
- ConstantString.RegistrationIdPostfix = {
119
- apiKey: "REGISTRATION_ID",
120
- oauth2: "CONFIGURATION_ID",
121
- http: "REGISTRATION_ID",
122
- openIdConnect: "REGISTRATION_ID",
123
- };
118
+ ConstantString.RegistrationIdPostfix = "REGISTRATION_ID";
124
119
  ConstantString.ResponseCodeFor20X = [
125
120
  "200",
126
121
  "201",
@@ -132,6 +127,7 @@ ConstantString.ResponseCodeFor20X = [
132
127
  "207",
133
128
  "208",
134
129
  "226",
130
+ "2XX",
135
131
  "default",
136
132
  ];
137
133
  ConstantString.AllOperationMethods = [
@@ -197,17 +193,6 @@ class SpecParserError extends Error {
197
193
 
198
194
  // Copyright (c) Microsoft Corporation.
199
195
  class Utils {
200
- static hasNestedObjectInSchema(schema) {
201
- if (this.isObjectSchema(schema)) {
202
- for (const property in schema.properties) {
203
- const nestedSchema = schema.properties[property];
204
- if (this.isObjectSchema(nestedSchema)) {
205
- return true;
206
- }
207
- }
208
- }
209
- return false;
210
- }
211
196
  static isObjectSchema(schema) {
212
197
  return schema.type === "object" || (!schema.type && !!schema.properties);
213
198
  }
@@ -220,11 +205,32 @@ class Utils {
220
205
  static isAPIKeyAuth(authScheme) {
221
206
  return authScheme.type === "apiKey";
222
207
  }
208
+ static isAPIKeyAuthButNotInCookie(authScheme) {
209
+ return authScheme.type === "apiKey" && authScheme.in !== "cookie";
210
+ }
223
211
  static isOAuthWithAuthCodeFlow(authScheme) {
224
212
  return !!(authScheme.type === "oauth2" &&
225
213
  authScheme.flows &&
226
214
  authScheme.flows.authorizationCode);
227
215
  }
216
+ static isNotSupportedAuth(authSchemeArray) {
217
+ if (authSchemeArray.length === 0) {
218
+ return false;
219
+ }
220
+ if (authSchemeArray.length > 0 && authSchemeArray.every((auths) => auths.length > 1)) {
221
+ return true;
222
+ }
223
+ for (const auths of authSchemeArray) {
224
+ if (auths.length === 1) {
225
+ if (Utils.isOAuthWithAuthCodeFlow(auths[0].authScheme) ||
226
+ Utils.isBearerTokenAuth(auths[0].authScheme) ||
227
+ Utils.isAPIKeyAuthButNotInCookie(auths[0].authScheme)) {
228
+ return false;
229
+ }
230
+ }
231
+ }
232
+ return true;
233
+ }
228
234
  static getAuthArray(securities, spec) {
229
235
  var _a;
230
236
  const result = [];
@@ -249,6 +255,20 @@ class Utils {
249
255
  result.sort((a, b) => a[0].name.localeCompare(b[0].name));
250
256
  return result;
251
257
  }
258
+ static getAuthMap(spec) {
259
+ const authMap = {};
260
+ for (const url in spec.paths) {
261
+ for (const method in spec.paths[url]) {
262
+ const operation = spec.paths[url][method];
263
+ const authArray = Utils.getAuthArray(operation.security, spec);
264
+ if (authArray && authArray.length > 0) {
265
+ const currentAuth = authArray[0][0];
266
+ authMap[operation.operationId] = currentAuth;
267
+ }
268
+ }
269
+ }
270
+ return authMap;
271
+ }
252
272
  static getAuthInfo(spec) {
253
273
  let authInfo = undefined;
254
274
  for (const url in spec.paths) {
@@ -277,27 +297,33 @@ class Utils {
277
297
  let multipleMediaType = false;
278
298
  for (const code of ConstantString.ResponseCodeFor20X) {
279
299
  const responseObject = (_a = operationObject === null || operationObject === void 0 ? void 0 : operationObject.responses) === null || _a === void 0 ? void 0 : _a[code];
280
- if (responseObject === null || responseObject === void 0 ? void 0 : responseObject.content) {
281
- for (const contentType of Object.keys(responseObject.content)) {
282
- // json media type can also be "application/json; charset=utf-8"
283
- if (contentType.indexOf("application/json") >= 0) {
284
- multipleMediaType = false;
285
- json = responseObject.content[contentType];
286
- if (Utils.containMultipleMediaTypes(responseObject)) {
287
- multipleMediaType = true;
288
- if (!allowMultipleMediaType) {
289
- json = {};
290
- }
291
- }
292
- else {
293
- return { json, multipleMediaType };
294
- }
295
- }
296
- }
300
+ if (!responseObject) {
301
+ continue;
302
+ }
303
+ multipleMediaType = Utils.containMultipleMediaTypes(responseObject);
304
+ if (!allowMultipleMediaType && multipleMediaType) {
305
+ json = {};
306
+ continue;
307
+ }
308
+ const mediaObj = Utils.getJsonContentType(responseObject);
309
+ if (Object.keys(mediaObj).length > 0) {
310
+ json = mediaObj;
311
+ return { json, multipleMediaType };
297
312
  }
298
313
  }
299
314
  return { json, multipleMediaType };
300
315
  }
316
+ static getJsonContentType(responseObject) {
317
+ if (responseObject.content) {
318
+ for (const contentType of Object.keys(responseObject.content)) {
319
+ // json media type can also be "application/json; charset=utf-8"
320
+ if (contentType.indexOf("application/json") >= 0) {
321
+ return responseObject.content[contentType];
322
+ }
323
+ }
324
+ }
325
+ return {};
326
+ }
301
327
  static convertPathToCamelCase(path) {
302
328
  const pathSegments = path.split(/[./{]/);
303
329
  const camelCaseSegments = pathSegments.map((segment) => {
@@ -333,7 +359,7 @@ class Utils {
333
359
  }
334
360
  return newStr;
335
361
  }
336
- static checkServerUrl(servers) {
362
+ static checkServerUrl(servers, allowHttp = false) {
337
363
  const errors = [];
338
364
  let serverUrl;
339
365
  try {
@@ -356,8 +382,7 @@ class Utils {
356
382
  data: servers,
357
383
  });
358
384
  }
359
- else if (protocol !== "https:") {
360
- // Http server url is not supported
385
+ else if (protocol !== "https:" && !(protocol === "http:" && allowHttp)) {
361
386
  const protocolString = protocol.slice(0, -1);
362
387
  errors.push({
363
388
  type: ErrorType.UrlProtocolNotSupported,
@@ -373,10 +398,11 @@ class Utils {
373
398
  let hasTopLevelServers = false;
374
399
  let hasPathLevelServers = false;
375
400
  let hasOperationLevelServers = false;
401
+ const allowHttp = options.projectType === ProjectType.Copilot;
376
402
  if (spec.servers && spec.servers.length >= 1) {
377
403
  hasTopLevelServers = true;
378
404
  // for multiple server, we only use the first url
379
- const serverErrors = Utils.checkServerUrl(spec.servers);
405
+ const serverErrors = Utils.checkServerUrl(spec.servers, allowHttp);
380
406
  errors.push(...serverErrors);
381
407
  }
382
408
  const paths = spec.paths;
@@ -384,7 +410,7 @@ class Utils {
384
410
  const methods = paths[path];
385
411
  if ((methods === null || methods === void 0 ? void 0 : methods.servers) && methods.servers.length >= 1) {
386
412
  hasPathLevelServers = true;
387
- const serverErrors = Utils.checkServerUrl(methods.servers);
413
+ const serverErrors = Utils.checkServerUrl(methods.servers, allowHttp);
388
414
  errors.push(...serverErrors);
389
415
  }
390
416
  for (const method in methods) {
@@ -392,7 +418,7 @@ class Utils {
392
418
  if (((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
393
419
  if ((operationObject === null || operationObject === void 0 ? void 0 : operationObject.servers) && operationObject.servers.length >= 1) {
394
420
  hasOperationLevelServers = true;
395
- const serverErrors = Utils.checkServerUrl(operationObject.servers);
421
+ const serverErrors = Utils.checkServerUrl(operationObject.servers, allowHttp);
396
422
  errors.push(...serverErrors);
397
423
  }
398
424
  }
@@ -681,22 +707,6 @@ class Validator {
681
707
  }
682
708
  return result;
683
709
  }
684
- validateResponse(method, path) {
685
- const result = { isValid: true, reason: [] };
686
- const operationObject = this.spec.paths[path][method];
687
- const { json, multipleMediaType } = Utils.getResponseJson(operationObject);
688
- if (this.options.projectType === ProjectType.SME) {
689
- // only support response body only contains “application/json” content type
690
- if (multipleMediaType) {
691
- result.reason.push(ErrorType.ResponseContainMultipleMediaTypes);
692
- }
693
- else if (Object.keys(json).length === 0) {
694
- // response body should not be empty
695
- result.reason.push(ErrorType.ResponseJsonIsEmpty);
696
- }
697
- }
698
- return result;
699
- }
700
710
  validateServer(method, path) {
701
711
  const result = { isValid: true, reason: [] };
702
712
  const serverObj = Utils.getServerObject(this.spec, method, path);
@@ -705,8 +715,8 @@ class Validator {
705
715
  result.reason.push(ErrorType.NoServerInformation);
706
716
  }
707
717
  else {
708
- // server url should be absolute url with https protocol
709
- const serverValidateResult = Utils.checkServerUrl([serverObj]);
718
+ const allowHttp = this.projectType === ProjectType.Copilot;
719
+ const serverValidateResult = Utils.checkServerUrl([serverObj], allowHttp);
710
720
  result.reason.push(...serverValidateResult.map((item) => item.type));
711
721
  }
712
722
  return result;
@@ -729,6 +739,9 @@ class Validator {
729
739
  reason: [ErrorType.MultipleAuthNotSupported],
730
740
  };
731
741
  }
742
+ if (this.projectType === ProjectType.Copilot) {
743
+ return { isValid: true, reason: [] };
744
+ }
732
745
  for (const auths of authSchemeArray) {
733
746
  if (auths.length === 1) {
734
747
  if ((this.options.allowAPIKeyAuth && Utils.isAPIKeyAuth(auths[0].authScheme)) ||
@@ -741,114 +754,6 @@ class Validator {
741
754
  }
742
755
  return { isValid: false, reason: [ErrorType.AuthTypeIsNotSupported] };
743
756
  }
744
- checkPostBodySchema(schema, isRequired = false) {
745
- var _a;
746
- const paramResult = {
747
- requiredNum: 0,
748
- optionalNum: 0,
749
- isValid: true,
750
- reason: [],
751
- };
752
- if (Object.keys(schema).length === 0) {
753
- return paramResult;
754
- }
755
- const isRequiredWithoutDefault = isRequired && schema.default === undefined;
756
- const isCopilot = this.projectType === ProjectType.Copilot;
757
- if (isCopilot && Utils.hasNestedObjectInSchema(schema)) {
758
- paramResult.isValid = false;
759
- paramResult.reason = [ErrorType.RequestBodyContainsNestedObject];
760
- return paramResult;
761
- }
762
- if (schema.type === "string" ||
763
- schema.type === "integer" ||
764
- schema.type === "boolean" ||
765
- schema.type === "number") {
766
- if (isRequiredWithoutDefault) {
767
- paramResult.requiredNum = paramResult.requiredNum + 1;
768
- }
769
- else {
770
- paramResult.optionalNum = paramResult.optionalNum + 1;
771
- }
772
- }
773
- else if (Utils.isObjectSchema(schema)) {
774
- const { properties } = schema;
775
- for (const property in properties) {
776
- let isRequired = false;
777
- if (schema.required && ((_a = schema.required) === null || _a === void 0 ? void 0 : _a.indexOf(property)) >= 0) {
778
- isRequired = true;
779
- }
780
- const result = this.checkPostBodySchema(properties[property], isRequired);
781
- paramResult.requiredNum += result.requiredNum;
782
- paramResult.optionalNum += result.optionalNum;
783
- paramResult.isValid = paramResult.isValid && result.isValid;
784
- paramResult.reason.push(...result.reason);
785
- }
786
- }
787
- else {
788
- if (isRequiredWithoutDefault && !isCopilot) {
789
- paramResult.isValid = false;
790
- paramResult.reason.push(ErrorType.PostBodyContainsRequiredUnsupportedSchema);
791
- }
792
- }
793
- return paramResult;
794
- }
795
- checkParamSchema(paramObject) {
796
- const paramResult = {
797
- requiredNum: 0,
798
- optionalNum: 0,
799
- isValid: true,
800
- reason: [],
801
- };
802
- if (!paramObject) {
803
- return paramResult;
804
- }
805
- const isCopilot = this.projectType === ProjectType.Copilot;
806
- for (let i = 0; i < paramObject.length; i++) {
807
- const param = paramObject[i];
808
- const schema = param.schema;
809
- if (isCopilot && Utils.hasNestedObjectInSchema(schema)) {
810
- paramResult.isValid = false;
811
- paramResult.reason.push(ErrorType.ParamsContainsNestedObject);
812
- continue;
813
- }
814
- const isRequiredWithoutDefault = param.required && schema.default === undefined;
815
- if (isCopilot) {
816
- if (isRequiredWithoutDefault) {
817
- paramResult.requiredNum = paramResult.requiredNum + 1;
818
- }
819
- else {
820
- paramResult.optionalNum = paramResult.optionalNum + 1;
821
- }
822
- continue;
823
- }
824
- if (param.in === "header" || param.in === "cookie") {
825
- if (isRequiredWithoutDefault) {
826
- paramResult.isValid = false;
827
- paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
828
- }
829
- continue;
830
- }
831
- if (schema.type !== "boolean" &&
832
- schema.type !== "string" &&
833
- schema.type !== "number" &&
834
- schema.type !== "integer") {
835
- if (isRequiredWithoutDefault) {
836
- paramResult.isValid = false;
837
- paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
838
- }
839
- continue;
840
- }
841
- if (param.in === "query" || param.in === "path") {
842
- if (isRequiredWithoutDefault) {
843
- paramResult.requiredNum = paramResult.requiredNum + 1;
844
- }
845
- else {
846
- paramResult.optionalNum = paramResult.optionalNum + 1;
847
- }
848
- }
849
- }
850
- return paramResult;
851
- }
852
757
  }
853
758
 
854
759
  // Copyright (c) Microsoft Corporation.
@@ -858,7 +763,6 @@ class CopilotValidator extends Validator {
858
763
  this.projectType = ProjectType.Copilot;
859
764
  this.options = options;
860
765
  this.spec = spec;
861
- this.checkCircularReference();
862
766
  }
863
767
  validateSpec() {
864
768
  const result = { errors: [], warnings: [] };
@@ -884,10 +788,6 @@ class CopilotValidator extends Validator {
884
788
  if (!methodAndPathResult.isValid) {
885
789
  return methodAndPathResult;
886
790
  }
887
- const circularReferenceResult = this.validateCircularReference(method, path);
888
- if (!circularReferenceResult.isValid) {
889
- return circularReferenceResult;
890
- }
891
791
  const operationObject = this.spec.paths[path][method];
892
792
  // validate auth
893
793
  const authCheckResult = this.validateAuth(method, path);
@@ -899,24 +799,6 @@ class CopilotValidator extends Validator {
899
799
  // validate server
900
800
  const validateServerResult = this.validateServer(method, path);
901
801
  result.reason.push(...validateServerResult.reason);
902
- // validate response
903
- const validateResponseResult = this.validateResponse(method, path);
904
- result.reason.push(...validateResponseResult.reason);
905
- // validate requestBody
906
- const requestBody = operationObject.requestBody;
907
- const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
908
- if (requestJsonBody) {
909
- const requestBodySchema = requestJsonBody.schema;
910
- if (!Utils.isObjectSchema(requestBodySchema)) {
911
- result.reason.push(ErrorType.PostBodySchemaIsNotJson);
912
- }
913
- const requestBodyParamResult = this.checkPostBodySchema(requestBodySchema, requestBody.required);
914
- result.reason.push(...requestBodyParamResult.reason);
915
- }
916
- // validate parameters
917
- const paramObject = operationObject.parameters;
918
- const paramResult = this.checkParamSchema(paramObject);
919
- result.reason.push(...paramResult.reason);
920
802
  if (result.reason.length > 0) {
921
803
  result.isValid = false;
922
804
  }
@@ -1008,6 +890,108 @@ class SMEValidator extends Validator {
1008
890
  }
1009
891
  return result;
1010
892
  }
893
+ validateResponse(method, path) {
894
+ const result = { isValid: true, reason: [] };
895
+ const operationObject = this.spec.paths[path][method];
896
+ const { json, multipleMediaType } = Utils.getResponseJson(operationObject);
897
+ // only support response body only contains “application/json” content type
898
+ if (multipleMediaType) {
899
+ result.reason.push(ErrorType.ResponseContainMultipleMediaTypes);
900
+ }
901
+ else if (Object.keys(json).length === 0) {
902
+ // response body should not be empty
903
+ result.reason.push(ErrorType.ResponseJsonIsEmpty);
904
+ }
905
+ return result;
906
+ }
907
+ checkPostBodySchema(schema, isRequired = false) {
908
+ var _a;
909
+ const paramResult = {
910
+ requiredNum: 0,
911
+ optionalNum: 0,
912
+ isValid: true,
913
+ reason: [],
914
+ };
915
+ if (Object.keys(schema).length === 0) {
916
+ return paramResult;
917
+ }
918
+ const isRequiredWithoutDefault = isRequired && schema.default === undefined;
919
+ const isCopilot = this.projectType === ProjectType.Copilot;
920
+ if (schema.type === "string" ||
921
+ schema.type === "integer" ||
922
+ schema.type === "boolean" ||
923
+ schema.type === "number") {
924
+ if (isRequiredWithoutDefault) {
925
+ paramResult.requiredNum = paramResult.requiredNum + 1;
926
+ }
927
+ else {
928
+ paramResult.optionalNum = paramResult.optionalNum + 1;
929
+ }
930
+ }
931
+ else if (Utils.isObjectSchema(schema)) {
932
+ const { properties } = schema;
933
+ for (const property in properties) {
934
+ let isRequired = false;
935
+ if (schema.required && ((_a = schema.required) === null || _a === void 0 ? void 0 : _a.indexOf(property)) >= 0) {
936
+ isRequired = true;
937
+ }
938
+ const result = this.checkPostBodySchema(properties[property], isRequired);
939
+ paramResult.requiredNum += result.requiredNum;
940
+ paramResult.optionalNum += result.optionalNum;
941
+ paramResult.isValid = paramResult.isValid && result.isValid;
942
+ paramResult.reason.push(...result.reason);
943
+ }
944
+ }
945
+ else {
946
+ if (isRequiredWithoutDefault && !isCopilot) {
947
+ paramResult.isValid = false;
948
+ paramResult.reason.push(ErrorType.PostBodyContainsRequiredUnsupportedSchema);
949
+ }
950
+ }
951
+ return paramResult;
952
+ }
953
+ checkParamSchema(paramObject) {
954
+ const paramResult = {
955
+ requiredNum: 0,
956
+ optionalNum: 0,
957
+ isValid: true,
958
+ reason: [],
959
+ };
960
+ if (!paramObject) {
961
+ return paramResult;
962
+ }
963
+ for (let i = 0; i < paramObject.length; i++) {
964
+ const param = paramObject[i];
965
+ const schema = param.schema;
966
+ const isRequiredWithoutDefault = param.required && schema.default === undefined;
967
+ if (param.in === "header" || param.in === "cookie") {
968
+ if (isRequiredWithoutDefault) {
969
+ paramResult.isValid = false;
970
+ paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
971
+ }
972
+ continue;
973
+ }
974
+ if (schema.type !== "boolean" &&
975
+ schema.type !== "string" &&
976
+ schema.type !== "number" &&
977
+ schema.type !== "integer") {
978
+ if (isRequiredWithoutDefault) {
979
+ paramResult.isValid = false;
980
+ paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
981
+ }
982
+ continue;
983
+ }
984
+ if (param.in === "query" || param.in === "path") {
985
+ if (isRequiredWithoutDefault) {
986
+ paramResult.requiredNum = paramResult.requiredNum + 1;
987
+ }
988
+ else {
989
+ paramResult.optionalNum = paramResult.optionalNum + 1;
990
+ }
991
+ }
992
+ }
993
+ return paramResult;
994
+ }
1011
995
  validateParamCount(postBodyResult, paramResult) {
1012
996
  const result = { isValid: true, reason: [] };
1013
997
  const totalRequiredParams = postBodyResult.requiredNum + paramResult.requiredNum;
@@ -1709,7 +1693,7 @@ function inferProperties(card) {
1709
1693
 
1710
1694
  // Copyright (c) Microsoft Corporation.
1711
1695
  class ManifestUpdater {
1712
- static async updateManifestWithAiPlugin(manifestPath, outputSpecPath, apiPluginFilePath, spec, options, authInfo, existingPluginManifestInfo) {
1696
+ static async updateManifestWithAiPlugin(manifestPath, outputSpecPath, apiPluginFilePath, spec, options, authMap, existingPluginManifestInfo) {
1713
1697
  const manifest = await fs.readJSON(manifestPath);
1714
1698
  const apiPluginRelativePath = ManifestUpdater.getRelativePath(manifestPath, apiPluginFilePath);
1715
1699
  const useCopilotExtensionsInSchema = await ManifestUtil.useCopilotExtensionsInSchema(manifest);
@@ -1739,7 +1723,7 @@ class ManifestUpdater {
1739
1723
  }
1740
1724
  const appName = this.removeEnvs(manifest.name.short);
1741
1725
  const specRelativePath = ManifestUpdater.getRelativePath(manifestPath, outputSpecPath);
1742
- const [apiPlugin, warnings] = await ManifestUpdater.generatePluginManifestSchema(spec, specRelativePath, apiPluginFilePath, appName, authInfo, options, existingPluginManifestInfo);
1726
+ const [apiPlugin, warnings] = await ManifestUpdater.generatePluginManifestSchema(spec, specRelativePath, apiPluginFilePath, appName, authMap, options, existingPluginManifestInfo);
1743
1727
  return [manifest, apiPlugin, warnings];
1744
1728
  }
1745
1729
  static updateManifestDescription(manifest, spec) {
@@ -1761,28 +1745,13 @@ class ManifestUpdater {
1761
1745
  throw new SpecParserError(Utils.format(ConstantString.UnsupportedSchema, method, pathUrl, JSON.stringify(schema)), ErrorType.UpdateManifestFailed);
1762
1746
  }
1763
1747
  }
1764
- static async generatePluginManifestSchema(spec, specRelativePath, apiPluginFilePath, appName, authInfo, options, existingPluginManifestInfo) {
1748
+ static async generatePluginManifestSchema(spec, specRelativePath, apiPluginFilePath, appName, authMap, options, existingPluginManifestInfo) {
1765
1749
  var _a, _b, _c, _d;
1766
1750
  const warnings = [];
1767
1751
  const functions = [];
1768
- const functionNames = [];
1752
+ const functionNamesMap = {};
1769
1753
  const conversationStarters = [];
1770
1754
  const paths = spec.paths;
1771
- const pluginAuthObj = {
1772
- type: "None",
1773
- };
1774
- if (authInfo) {
1775
- if (Utils.isOAuthWithAuthCodeFlow(authInfo.authScheme)) {
1776
- pluginAuthObj.type = "OAuthPluginVault";
1777
- }
1778
- else if (Utils.isBearerTokenAuth(authInfo.authScheme)) {
1779
- pluginAuthObj.type = "ApiKeyPluginVault";
1780
- }
1781
- if (pluginAuthObj.type !== "None") {
1782
- const safeRegistrationIdName = Utils.getSafeRegistrationIdEnvName(`${authInfo.name}_${ConstantString.RegistrationIdPostfix[authInfo.authScheme.type]}`);
1783
- pluginAuthObj.reference_id = `\${{${safeRegistrationIdName}}}`;
1784
- }
1785
- }
1786
1755
  for (const pathUrl in paths) {
1787
1756
  const pathItem = paths[pathUrl];
1788
1757
  if (pathItem) {
@@ -1790,36 +1759,11 @@ class ManifestUpdater {
1790
1759
  for (const method in operations) {
1791
1760
  if (options.allowMethods.includes(method)) {
1792
1761
  const operationItem = operations[method];
1793
- const confirmationBodies = [];
1794
1762
  if (operationItem) {
1795
1763
  const operationId = operationItem.operationId;
1796
1764
  const safeFunctionName = operationId.replace(/[^a-zA-Z0-9]/g, "_");
1797
1765
  const description = (_a = operationItem.description) !== null && _a !== void 0 ? _a : "";
1798
1766
  const summary = operationItem.summary;
1799
- const paramObject = operationItem.parameters;
1800
- const requestBody = operationItem.requestBody;
1801
- if (paramObject) {
1802
- for (let i = 0; i < paramObject.length; i++) {
1803
- const param = paramObject[i];
1804
- const schema = param.schema;
1805
- ManifestUpdater.checkSchema(schema, method, pathUrl);
1806
- confirmationBodies.push(ManifestUpdater.getConfirmationBodyItem(param.name));
1807
- }
1808
- }
1809
- if (requestBody) {
1810
- const requestJsonBody = requestBody.content["application/json"];
1811
- const requestBodySchema = requestJsonBody.schema;
1812
- if (Utils.isObjectSchema(requestBodySchema)) {
1813
- for (const property in requestBodySchema.properties) {
1814
- const schema = requestBodySchema.properties[property];
1815
- ManifestUpdater.checkSchema(schema, method, pathUrl);
1816
- confirmationBodies.push(ManifestUpdater.getConfirmationBodyItem(property));
1817
- }
1818
- }
1819
- else {
1820
- throw new SpecParserError(Utils.format(ConstantString.UnsupportedSchema, method, pathUrl, JSON.stringify(requestBodySchema)), ErrorType.UpdateManifestFailed);
1821
- }
1822
- }
1823
1767
  let funcDescription = operationItem.description || operationItem.summary || "";
1824
1768
  if (funcDescription.length > ConstantString.FunctionDescriptionMaxLens) {
1825
1769
  warnings.push({
@@ -1853,19 +1797,66 @@ class ManifestUpdater {
1853
1797
  }
1854
1798
  }
1855
1799
  if (options.allowConfirmation && method !== ConstantString.GetMethod) {
1856
- if (!funcObj.capabilities) {
1857
- funcObj.capabilities = {};
1800
+ const paramObject = operationItem.parameters;
1801
+ const requestBody = operationItem.requestBody;
1802
+ const confirmationBodies = [];
1803
+ if (paramObject) {
1804
+ for (let i = 0; i < paramObject.length; i++) {
1805
+ const param = paramObject[i];
1806
+ const schema = param.schema;
1807
+ ManifestUpdater.checkSchema(schema, method, pathUrl);
1808
+ confirmationBodies.push(ManifestUpdater.getConfirmationBodyItem(param.name));
1809
+ }
1810
+ }
1811
+ if (requestBody) {
1812
+ const requestJsonBody = Utils.getJsonContentType(requestBody);
1813
+ const requestBodySchema = requestJsonBody.schema;
1814
+ if (Utils.isObjectSchema(requestBodySchema)) {
1815
+ for (const property in requestBodySchema.properties) {
1816
+ const schema = requestBodySchema.properties[property];
1817
+ ManifestUpdater.checkSchema(schema, method, pathUrl);
1818
+ confirmationBodies.push(ManifestUpdater.getConfirmationBodyItem(property));
1819
+ }
1820
+ }
1821
+ else {
1822
+ throw new SpecParserError(Utils.format(ConstantString.UnsupportedSchema, method, pathUrl, JSON.stringify(requestBodySchema)), ErrorType.UpdateManifestFailed);
1823
+ }
1858
1824
  }
1859
- funcObj.capabilities.confirmation = {
1860
- type: "AdaptiveCard",
1861
- title: (_b = operationItem.summary) !== null && _b !== void 0 ? _b : description,
1862
- };
1863
1825
  if (confirmationBodies.length > 0) {
1826
+ if (!funcObj.capabilities) {
1827
+ funcObj.capabilities = {};
1828
+ }
1829
+ funcObj.capabilities.confirmation = {
1830
+ type: "AdaptiveCard",
1831
+ title: (_b = operationItem.summary) !== null && _b !== void 0 ? _b : description,
1832
+ };
1864
1833
  funcObj.capabilities.confirmation.body = confirmationBodies.join("\n");
1865
1834
  }
1866
1835
  }
1867
1836
  functions.push(funcObj);
1868
- functionNames.push(safeFunctionName);
1837
+ const authInfo = authMap[operationId];
1838
+ let key = "None";
1839
+ let authName = "None";
1840
+ if (authInfo) {
1841
+ if (Utils.isOAuthWithAuthCodeFlow(authInfo.authScheme)) {
1842
+ key = "OAuthPluginVault";
1843
+ authName = authInfo.name;
1844
+ }
1845
+ else if (Utils.isBearerTokenAuth(authInfo.authScheme) ||
1846
+ Utils.isAPIKeyAuthButNotInCookie(authInfo.authScheme)) {
1847
+ key = "ApiKeyPluginVault";
1848
+ authName = authInfo.name;
1849
+ }
1850
+ }
1851
+ if (functionNamesMap[key]) {
1852
+ functionNamesMap[key].functionNames.push(safeFunctionName);
1853
+ }
1854
+ else {
1855
+ functionNamesMap[key] = {
1856
+ functionNames: [safeFunctionName],
1857
+ authName: authName,
1858
+ };
1859
+ }
1869
1860
  const conversationStarterStr = (summary !== null && summary !== void 0 ? summary : description).slice(0, ConstantString.ConversationStarterMaxLens);
1870
1861
  if (conversationStarterStr) {
1871
1862
  conversationStarters.push(conversationStarterStr);
@@ -1875,6 +1866,12 @@ class ManifestUpdater {
1875
1866
  }
1876
1867
  }
1877
1868
  }
1869
+ if (Object.keys(functionNamesMap).length === 0) {
1870
+ functionNamesMap["None"] = {
1871
+ functionNames: [],
1872
+ authName: "None",
1873
+ };
1874
+ }
1878
1875
  let apiPlugin;
1879
1876
  if (await fs.pathExists(apiPluginFilePath)) {
1880
1877
  apiPlugin = await fs.readJSON(apiPluginFilePath);
@@ -1910,24 +1907,35 @@ class ManifestUpdater {
1910
1907
  const relativePath = ManifestUpdater.getRelativePath(existingPluginManifestInfo.manifestPath, existingPluginManifestInfo.specPath);
1911
1908
  apiPlugin.runtimes = apiPlugin.runtimes.filter((runtime) => runtime.spec.url !== relativePath);
1912
1909
  }
1913
- const index = apiPlugin.runtimes.findIndex((runtime) => {
1914
- var _a, _b;
1915
- return runtime.spec.url === specRelativePath &&
1916
- runtime.type === "OpenApi" &&
1917
- ((_b = (_a = runtime.auth) === null || _a === void 0 ? void 0 : _a.type) !== null && _b !== void 0 ? _b : "None") === pluginAuthObj.type;
1918
- });
1919
- if (index === -1) {
1920
- apiPlugin.runtimes.push({
1921
- type: "OpenApi",
1922
- auth: pluginAuthObj,
1923
- spec: {
1924
- url: specRelativePath,
1925
- },
1926
- run_for_functions: functionNames,
1910
+ for (const authType in functionNamesMap) {
1911
+ const pluginAuthObj = {
1912
+ type: authType,
1913
+ };
1914
+ const authName = functionNamesMap[authType].authName;
1915
+ if (pluginAuthObj.type !== "None") {
1916
+ const safeRegistrationIdName = Utils.getSafeRegistrationIdEnvName(`${authName}_${ConstantString.RegistrationIdPostfix}`);
1917
+ pluginAuthObj.reference_id = `\${{${safeRegistrationIdName}}}`;
1918
+ }
1919
+ const functionNamesInfo = functionNamesMap[authType];
1920
+ const index = apiPlugin.runtimes.findIndex((runtime) => {
1921
+ var _a, _b;
1922
+ return runtime.spec.url === specRelativePath &&
1923
+ runtime.type === "OpenApi" &&
1924
+ ((_b = (_a = runtime.auth) === null || _a === void 0 ? void 0 : _a.type) !== null && _b !== void 0 ? _b : "None") === authType;
1927
1925
  });
1928
- }
1929
- else {
1930
- apiPlugin.runtimes[index].run_for_functions = functionNames;
1926
+ if (index === -1) {
1927
+ apiPlugin.runtimes.push({
1928
+ type: "OpenApi",
1929
+ auth: pluginAuthObj,
1930
+ spec: {
1931
+ url: specRelativePath,
1932
+ },
1933
+ run_for_functions: functionNamesInfo.functionNames,
1934
+ });
1935
+ }
1936
+ else {
1937
+ apiPlugin.runtimes[index].run_for_functions = functionNamesInfo.functionNames;
1938
+ }
1931
1939
  }
1932
1940
  if (!apiPlugin.name_for_human) {
1933
1941
  apiPlugin.name_for_human = appName;
@@ -1968,7 +1976,7 @@ class ManifestUpdater {
1968
1976
  };
1969
1977
  if (authInfo) {
1970
1978
  const auth = authInfo.authScheme;
1971
- const safeRegistrationIdName = Utils.getSafeRegistrationIdEnvName(`${authInfo.name}_${ConstantString.RegistrationIdPostfix[authInfo.authScheme.type]}`);
1979
+ const safeRegistrationIdName = Utils.getSafeRegistrationIdEnvName(`${authInfo.name}_${ConstantString.RegistrationIdPostfix}`);
1972
1980
  if (Utils.isAPIKeyAuth(auth) || Utils.isBearerTokenAuth(auth)) {
1973
1981
  composeExtension.authorization = {
1974
1982
  authType: "apiSecretServiceAuth",
@@ -2095,6 +2103,7 @@ class SpecParser {
2095
2103
  };
2096
2104
  this.pathOrSpec = pathOrDoc;
2097
2105
  this.parser = new SwaggerParser();
2106
+ this.refParser = new $RefParser();
2098
2107
  this.options = Object.assign(Object.assign({}, this.defaultOptions), (options !== null && options !== void 0 ? options : {}));
2099
2108
  }
2100
2109
  /**
@@ -2107,12 +2116,15 @@ class SpecParser {
2107
2116
  let hash = "";
2108
2117
  try {
2109
2118
  await this.loadSpec();
2110
- if (!this.parser.$refs.circular) {
2119
+ if (!this.refParser.$refs.circular) {
2111
2120
  await this.parser.validate(this.spec);
2112
2121
  }
2113
2122
  else {
2123
+ // The following code hangs for Graph API, support will be added when SwaggerParser is updated.
2124
+ /*
2114
2125
  const clonedUnResolveSpec = JSON.parse(JSON.stringify(this.unResolveSpec));
2115
2126
  await this.parser.validate(clonedUnResolveSpec);
2127
+ */
2116
2128
  }
2117
2129
  }
2118
2130
  catch (e) {
@@ -2140,7 +2152,7 @@ class SpecParser {
2140
2152
  };
2141
2153
  }
2142
2154
  // Remote reference not supported
2143
- const refPaths = this.parser.$refs.paths();
2155
+ const refPaths = this.refParser.$refs.paths();
2144
2156
  // refPaths [0] is the current spec file path
2145
2157
  if (refPaths.length > 1) {
2146
2158
  errors.push({
@@ -2269,7 +2281,7 @@ class SpecParser {
2269
2281
  throw new SpecParserError(ConstantString.CancelledMessage, ErrorType.Cancelled);
2270
2282
  }
2271
2283
  const clonedUnResolveSpec = JSON.parse(JSON.stringify(newUnResolvedSpec));
2272
- const newSpec = (await this.parser.dereference(clonedUnResolveSpec));
2284
+ const newSpec = await this.deReferenceSpec(clonedUnResolveSpec);
2273
2285
  return [newUnResolvedSpec, newSpec];
2274
2286
  }
2275
2287
  catch (err) {
@@ -2279,6 +2291,10 @@ class SpecParser {
2279
2291
  throw new SpecParserError(err.toString(), ErrorType.GetSpecFailed);
2280
2292
  }
2281
2293
  }
2294
+ async deReferenceSpec(spec) {
2295
+ const result = await this.refParser.dereference(spec);
2296
+ return result;
2297
+ }
2282
2298
  /**
2283
2299
  * Generates and update artifacts from the OpenAPI specification file. Generate Adaptive Cards, update Teams app manifest, and generate a new OpenAPI specification file.
2284
2300
  * @param manifestPath A file path of the Teams app manifest file to update.
@@ -2295,7 +2311,6 @@ class SpecParser {
2295
2311
  const newSpecs = await this.getFilteredSpecs(filter, signal);
2296
2312
  const newUnResolvedSpec = newSpecs[0];
2297
2313
  const newSpec = newSpecs[1];
2298
- const authInfo = Utils.getAuthInfo(newSpec);
2299
2314
  const paths = newUnResolvedSpec.paths;
2300
2315
  for (const pathUrl in paths) {
2301
2316
  const operations = paths[pathUrl];
@@ -2303,15 +2318,22 @@ class SpecParser {
2303
2318
  const operationItem = operations[method];
2304
2319
  const operationId = operationItem.operationId;
2305
2320
  const containsSpecialCharacters = /[^a-zA-Z0-9_]/.test(operationId);
2306
- if (!containsSpecialCharacters) {
2307
- continue;
2321
+ if (containsSpecialCharacters) {
2322
+ operationItem.operationId = operationId.replace(/[^a-zA-Z0-9]/g, "_");
2323
+ result.warnings.push({
2324
+ type: WarningType.OperationIdContainsSpecialCharacters,
2325
+ content: Utils.format(ConstantString.OperationIdContainsSpecialCharacters, operationId, operationItem.operationId),
2326
+ data: operationId,
2327
+ });
2328
+ }
2329
+ const authArray = Utils.getAuthArray(operationItem.security, newSpec);
2330
+ if (Utils.isNotSupportedAuth(authArray)) {
2331
+ result.warnings.push({
2332
+ type: WarningType.UnsupportedAuthType,
2333
+ content: Utils.format(ConstantString.AuthTypeIsNotSupported, operationId),
2334
+ data: operationId,
2335
+ });
2308
2336
  }
2309
- operationItem.operationId = operationId.replace(/[^a-zA-Z0-9]/g, "_");
2310
- result.warnings.push({
2311
- type: WarningType.OperationIdContainsSpecialCharacters,
2312
- content: Utils.format(ConstantString.OperationIdContainsSpecialCharacters, operationId, operationItem.operationId),
2313
- data: operationId,
2314
- });
2315
2337
  }
2316
2338
  }
2317
2339
  await this.saveFilterSpec(outputSpecPath, newUnResolvedSpec);
@@ -2324,7 +2346,8 @@ class SpecParser {
2324
2346
  specPath: this.pathOrSpec,
2325
2347
  }
2326
2348
  : undefined;
2327
- const [updatedManifest, apiPlugin, warnings] = await ManifestUpdater.updateManifestWithAiPlugin(manifestPath, outputSpecPath, pluginFilePath, newSpec, this.options, authInfo, existingPluginManifestInfo);
2349
+ const authMap = Utils.getAuthMap(newSpec);
2350
+ const [updatedManifest, apiPlugin, warnings] = await ManifestUpdater.updateManifestWithAiPlugin(manifestPath, outputSpecPath, pluginFilePath, newSpec, this.options, authMap, existingPluginManifestInfo);
2328
2351
  result.warnings.push(...warnings);
2329
2352
  await fs.outputJSON(manifestPath, updatedManifest, { spaces: 4 });
2330
2353
  await fs.outputJSON(pluginFilePath, apiPlugin, { spaces: 4 });
@@ -2412,7 +2435,7 @@ class SpecParser {
2412
2435
  this.isSwaggerFile = true;
2413
2436
  }
2414
2437
  const clonedUnResolveSpec = JSON.parse(JSON.stringify(this.unResolveSpec));
2415
- this.spec = (await this.parser.dereference(clonedUnResolveSpec));
2438
+ this.spec = await this.deReferenceSpec(clonedUnResolveSpec);
2416
2439
  }
2417
2440
  }
2418
2441
  getAPIs(spec) {