@microsoft/m365-spec-parser 0.2.4-rc.0 → 0.2.4

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,7 +5,6 @@ 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';
9
8
 
10
9
  // Copyright (c) Microsoft Corporation.
11
10
  /**
@@ -39,8 +38,11 @@ var ErrorType;
39
38
  ErrorType["PostBodyContainMultipleMediaTypes"] = "post-body-contain-multiple-media-types";
40
39
  ErrorType["ResponseContainMultipleMediaTypes"] = "response-contain-multiple-media-types";
41
40
  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";
44
46
  ErrorType["ExceededRequiredParamsLimit"] = "exceeded-required-params-limit";
45
47
  ErrorType["NoParameter"] = "no-parameter";
46
48
  ErrorType["NoAPIInfo"] = "no-api-info";
@@ -60,7 +62,6 @@ var WarningType;
60
62
  WarningType["ConvertSwaggerToOpenAPI"] = "convert-swagger-to-openapi";
61
63
  WarningType["FuncDescriptionTooLong"] = "function-description-too-long";
62
64
  WarningType["OperationIdContainsSpecialCharacters"] = "operationid-contains-special-characters";
63
- WarningType["UnsupportedAuthType"] = "unsupported-auth-type";
64
65
  WarningType["GenerateJsonDataFailed"] = "generate-json-data-failed";
65
66
  WarningType["Unknown"] = "unknown";
66
67
  })(WarningType || (WarningType = {}));
@@ -100,7 +101,6 @@ ConstantString.SwaggerNotSupported = "Swagger 2.0 is not supported. Please conve
100
101
  ConstantString.SpecVersionNotSupported = "Unsupported OpenAPI version %s. Please use version 3.0.x.";
101
102
  ConstantString.MultipleAuthNotSupported = "Multiple authentication methods are unsupported. Ensure all selected APIs use identical authentication.";
102
103
  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,12 +110,17 @@ ConstantString.WrappedCardResponseLayout = "list";
110
110
  ConstantString.GetMethod = "get";
111
111
  ConstantString.PostMethod = "post";
112
112
  ConstantString.AdaptiveCardVersion = "1.5";
113
- ConstantString.AdaptiveCardSchema = "https://adaptivecards.io/schemas/adaptive-card.json";
113
+ ConstantString.AdaptiveCardSchema = "http://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 = "REGISTRATION_ID";
118
+ ConstantString.RegistrationIdPostfix = {
119
+ apiKey: "REGISTRATION_ID",
120
+ oauth2: "CONFIGURATION_ID",
121
+ http: "REGISTRATION_ID",
122
+ openIdConnect: "REGISTRATION_ID",
123
+ };
119
124
  ConstantString.ResponseCodeFor20X = [
120
125
  "200",
121
126
  "201",
@@ -127,7 +132,6 @@ ConstantString.ResponseCodeFor20X = [
127
132
  "207",
128
133
  "208",
129
134
  "226",
130
- "2XX",
131
135
  "default",
132
136
  ];
133
137
  ConstantString.AllOperationMethods = [
@@ -193,6 +197,17 @@ class SpecParserError extends Error {
193
197
 
194
198
  // Copyright (c) Microsoft Corporation.
195
199
  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
+ }
196
211
  static isObjectSchema(schema) {
197
212
  return schema.type === "object" || (!schema.type && !!schema.properties);
198
213
  }
@@ -205,32 +220,11 @@ class Utils {
205
220
  static isAPIKeyAuth(authScheme) {
206
221
  return authScheme.type === "apiKey";
207
222
  }
208
- static isAPIKeyAuthButNotInCookie(authScheme) {
209
- return authScheme.type === "apiKey" && authScheme.in !== "cookie";
210
- }
211
223
  static isOAuthWithAuthCodeFlow(authScheme) {
212
224
  return !!(authScheme.type === "oauth2" &&
213
225
  authScheme.flows &&
214
226
  authScheme.flows.authorizationCode);
215
227
  }
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
- }
234
228
  static getAuthArray(securities, spec) {
235
229
  var _a;
236
230
  const result = [];
@@ -255,20 +249,6 @@ class Utils {
255
249
  result.sort((a, b) => a[0].name.localeCompare(b[0].name));
256
250
  return result;
257
251
  }
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
- }
272
252
  static getAuthInfo(spec) {
273
253
  let authInfo = undefined;
274
254
  for (const url in spec.paths) {
@@ -297,32 +277,26 @@ class Utils {
297
277
  let multipleMediaType = false;
298
278
  for (const code of ConstantString.ResponseCodeFor20X) {
299
279
  const responseObject = (_a = operationObject === null || operationObject === void 0 ? void 0 : operationObject.responses) === null || _a === void 0 ? void 0 : _a[code];
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 };
312
- }
313
- }
314
- return { json, multipleMediaType };
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];
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
+ }
322
296
  }
323
297
  }
324
298
  }
325
- return {};
299
+ return { json, multipleMediaType };
326
300
  }
327
301
  static convertPathToCamelCase(path) {
328
302
  const pathSegments = path.split(/[./{]/);
@@ -359,7 +333,7 @@ class Utils {
359
333
  }
360
334
  return newStr;
361
335
  }
362
- static checkServerUrl(servers, allowHttp = false) {
336
+ static checkServerUrl(servers) {
363
337
  const errors = [];
364
338
  let serverUrl;
365
339
  try {
@@ -382,7 +356,8 @@ class Utils {
382
356
  data: servers,
383
357
  });
384
358
  }
385
- else if (protocol !== "https:" && !(protocol === "http:" && allowHttp)) {
359
+ else if (protocol !== "https:") {
360
+ // Http server url is not supported
386
361
  const protocolString = protocol.slice(0, -1);
387
362
  errors.push({
388
363
  type: ErrorType.UrlProtocolNotSupported,
@@ -398,11 +373,10 @@ class Utils {
398
373
  let hasTopLevelServers = false;
399
374
  let hasPathLevelServers = false;
400
375
  let hasOperationLevelServers = false;
401
- const allowHttp = options.projectType === ProjectType.Copilot;
402
376
  if (spec.servers && spec.servers.length >= 1) {
403
377
  hasTopLevelServers = true;
404
378
  // for multiple server, we only use the first url
405
- const serverErrors = Utils.checkServerUrl(spec.servers, allowHttp);
379
+ const serverErrors = Utils.checkServerUrl(spec.servers);
406
380
  errors.push(...serverErrors);
407
381
  }
408
382
  const paths = spec.paths;
@@ -410,7 +384,7 @@ class Utils {
410
384
  const methods = paths[path];
411
385
  if ((methods === null || methods === void 0 ? void 0 : methods.servers) && methods.servers.length >= 1) {
412
386
  hasPathLevelServers = true;
413
- const serverErrors = Utils.checkServerUrl(methods.servers, allowHttp);
387
+ const serverErrors = Utils.checkServerUrl(methods.servers);
414
388
  errors.push(...serverErrors);
415
389
  }
416
390
  for (const method in methods) {
@@ -418,7 +392,7 @@ class Utils {
418
392
  if (((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
419
393
  if ((operationObject === null || operationObject === void 0 ? void 0 : operationObject.servers) && operationObject.servers.length >= 1) {
420
394
  hasOperationLevelServers = true;
421
- const serverErrors = Utils.checkServerUrl(operationObject.servers, allowHttp);
395
+ const serverErrors = Utils.checkServerUrl(operationObject.servers);
422
396
  errors.push(...serverErrors);
423
397
  }
424
398
  }
@@ -707,6 +681,22 @@ class Validator {
707
681
  }
708
682
  return result;
709
683
  }
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
+ }
710
700
  validateServer(method, path) {
711
701
  const result = { isValid: true, reason: [] };
712
702
  const serverObj = Utils.getServerObject(this.spec, method, path);
@@ -715,8 +705,8 @@ class Validator {
715
705
  result.reason.push(ErrorType.NoServerInformation);
716
706
  }
717
707
  else {
718
- const allowHttp = this.projectType === ProjectType.Copilot;
719
- const serverValidateResult = Utils.checkServerUrl([serverObj], allowHttp);
708
+ // server url should be absolute url with https protocol
709
+ const serverValidateResult = Utils.checkServerUrl([serverObj]);
720
710
  result.reason.push(...serverValidateResult.map((item) => item.type));
721
711
  }
722
712
  return result;
@@ -739,9 +729,6 @@ class Validator {
739
729
  reason: [ErrorType.MultipleAuthNotSupported],
740
730
  };
741
731
  }
742
- if (this.projectType === ProjectType.Copilot) {
743
- return { isValid: true, reason: [] };
744
- }
745
732
  for (const auths of authSchemeArray) {
746
733
  if (auths.length === 1) {
747
734
  if ((this.options.allowAPIKeyAuth && Utils.isAPIKeyAuth(auths[0].authScheme)) ||
@@ -754,6 +741,114 @@ class Validator {
754
741
  }
755
742
  return { isValid: false, reason: [ErrorType.AuthTypeIsNotSupported] };
756
743
  }
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
+ }
757
852
  }
758
853
 
759
854
  // Copyright (c) Microsoft Corporation.
@@ -763,6 +858,7 @@ class CopilotValidator extends Validator {
763
858
  this.projectType = ProjectType.Copilot;
764
859
  this.options = options;
765
860
  this.spec = spec;
861
+ this.checkCircularReference();
766
862
  }
767
863
  validateSpec() {
768
864
  const result = { errors: [], warnings: [] };
@@ -788,6 +884,10 @@ class CopilotValidator extends Validator {
788
884
  if (!methodAndPathResult.isValid) {
789
885
  return methodAndPathResult;
790
886
  }
887
+ const circularReferenceResult = this.validateCircularReference(method, path);
888
+ if (!circularReferenceResult.isValid) {
889
+ return circularReferenceResult;
890
+ }
791
891
  const operationObject = this.spec.paths[path][method];
792
892
  // validate auth
793
893
  const authCheckResult = this.validateAuth(method, path);
@@ -799,6 +899,24 @@ class CopilotValidator extends Validator {
799
899
  // validate server
800
900
  const validateServerResult = this.validateServer(method, path);
801
901
  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);
802
920
  if (result.reason.length > 0) {
803
921
  result.isValid = false;
804
922
  }
@@ -890,108 +1008,6 @@ class SMEValidator extends Validator {
890
1008
  }
891
1009
  return result;
892
1010
  }
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
- }
995
1011
  validateParamCount(postBodyResult, paramResult) {
996
1012
  const result = { isValid: true, reason: [] };
997
1013
  const totalRequiredParams = postBodyResult.requiredNum + paramResult.requiredNum;
@@ -1693,7 +1709,7 @@ function inferProperties(card) {
1693
1709
 
1694
1710
  // Copyright (c) Microsoft Corporation.
1695
1711
  class ManifestUpdater {
1696
- static async updateManifestWithAiPlugin(manifestPath, outputSpecPath, apiPluginFilePath, spec, options, authMap, existingPluginManifestInfo) {
1712
+ static async updateManifestWithAiPlugin(manifestPath, outputSpecPath, apiPluginFilePath, spec, options, authInfo, existingPluginManifestInfo) {
1697
1713
  const manifest = await fs.readJSON(manifestPath);
1698
1714
  const apiPluginRelativePath = ManifestUpdater.getRelativePath(manifestPath, apiPluginFilePath);
1699
1715
  const useCopilotExtensionsInSchema = await ManifestUtil.useCopilotExtensionsInSchema(manifest);
@@ -1723,7 +1739,7 @@ class ManifestUpdater {
1723
1739
  }
1724
1740
  const appName = this.removeEnvs(manifest.name.short);
1725
1741
  const specRelativePath = ManifestUpdater.getRelativePath(manifestPath, outputSpecPath);
1726
- const [apiPlugin, warnings] = await ManifestUpdater.generatePluginManifestSchema(spec, specRelativePath, apiPluginFilePath, appName, authMap, options, existingPluginManifestInfo);
1742
+ const [apiPlugin, warnings] = await ManifestUpdater.generatePluginManifestSchema(spec, specRelativePath, apiPluginFilePath, appName, authInfo, options, existingPluginManifestInfo);
1727
1743
  return [manifest, apiPlugin, warnings];
1728
1744
  }
1729
1745
  static updateManifestDescription(manifest, spec) {
@@ -1745,13 +1761,28 @@ class ManifestUpdater {
1745
1761
  throw new SpecParserError(Utils.format(ConstantString.UnsupportedSchema, method, pathUrl, JSON.stringify(schema)), ErrorType.UpdateManifestFailed);
1746
1762
  }
1747
1763
  }
1748
- static async generatePluginManifestSchema(spec, specRelativePath, apiPluginFilePath, appName, authMap, options, existingPluginManifestInfo) {
1764
+ static async generatePluginManifestSchema(spec, specRelativePath, apiPluginFilePath, appName, authInfo, options, existingPluginManifestInfo) {
1749
1765
  var _a, _b, _c, _d;
1750
1766
  const warnings = [];
1751
1767
  const functions = [];
1752
- const functionNamesMap = {};
1768
+ const functionNames = [];
1753
1769
  const conversationStarters = [];
1754
1770
  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
+ }
1755
1786
  for (const pathUrl in paths) {
1756
1787
  const pathItem = paths[pathUrl];
1757
1788
  if (pathItem) {
@@ -1759,11 +1790,36 @@ class ManifestUpdater {
1759
1790
  for (const method in operations) {
1760
1791
  if (options.allowMethods.includes(method)) {
1761
1792
  const operationItem = operations[method];
1793
+ const confirmationBodies = [];
1762
1794
  if (operationItem) {
1763
1795
  const operationId = operationItem.operationId;
1764
1796
  const safeFunctionName = operationId.replace(/[^a-zA-Z0-9]/g, "_");
1765
1797
  const description = (_a = operationItem.description) !== null && _a !== void 0 ? _a : "";
1766
1798
  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
+ }
1767
1823
  let funcDescription = operationItem.description || operationItem.summary || "";
1768
1824
  if (funcDescription.length > ConstantString.FunctionDescriptionMaxLens) {
1769
1825
  warnings.push({
@@ -1797,66 +1853,19 @@ class ManifestUpdater {
1797
1853
  }
1798
1854
  }
1799
1855
  if (options.allowConfirmation && method !== ConstantString.GetMethod) {
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
- }
1856
+ if (!funcObj.capabilities) {
1857
+ funcObj.capabilities = {};
1824
1858
  }
1859
+ funcObj.capabilities.confirmation = {
1860
+ type: "AdaptiveCard",
1861
+ title: (_b = operationItem.summary) !== null && _b !== void 0 ? _b : description,
1862
+ };
1825
1863
  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
- };
1833
1864
  funcObj.capabilities.confirmation.body = confirmationBodies.join("\n");
1834
1865
  }
1835
1866
  }
1836
1867
  functions.push(funcObj);
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
- }
1868
+ functionNames.push(safeFunctionName);
1860
1869
  const conversationStarterStr = (summary !== null && summary !== void 0 ? summary : description).slice(0, ConstantString.ConversationStarterMaxLens);
1861
1870
  if (conversationStarterStr) {
1862
1871
  conversationStarters.push(conversationStarterStr);
@@ -1866,12 +1875,6 @@ class ManifestUpdater {
1866
1875
  }
1867
1876
  }
1868
1877
  }
1869
- if (Object.keys(functionNamesMap).length === 0) {
1870
- functionNamesMap["None"] = {
1871
- functionNames: [],
1872
- authName: "None",
1873
- };
1874
- }
1875
1878
  let apiPlugin;
1876
1879
  if (await fs.pathExists(apiPluginFilePath)) {
1877
1880
  apiPlugin = await fs.readJSON(apiPluginFilePath);
@@ -1907,35 +1910,24 @@ class ManifestUpdater {
1907
1910
  const relativePath = ManifestUpdater.getRelativePath(existingPluginManifestInfo.manifestPath, existingPluginManifestInfo.specPath);
1908
1911
  apiPlugin.runtimes = apiPlugin.runtimes.filter((runtime) => runtime.spec.url !== relativePath);
1909
1912
  }
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;
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,
1925
1927
  });
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
- }
1928
+ }
1929
+ else {
1930
+ apiPlugin.runtimes[index].run_for_functions = functionNames;
1939
1931
  }
1940
1932
  if (!apiPlugin.name_for_human) {
1941
1933
  apiPlugin.name_for_human = appName;
@@ -1976,7 +1968,7 @@ class ManifestUpdater {
1976
1968
  };
1977
1969
  if (authInfo) {
1978
1970
  const auth = authInfo.authScheme;
1979
- const safeRegistrationIdName = Utils.getSafeRegistrationIdEnvName(`${authInfo.name}_${ConstantString.RegistrationIdPostfix}`);
1971
+ const safeRegistrationIdName = Utils.getSafeRegistrationIdEnvName(`${authInfo.name}_${ConstantString.RegistrationIdPostfix[authInfo.authScheme.type]}`);
1980
1972
  if (Utils.isAPIKeyAuth(auth) || Utils.isBearerTokenAuth(auth)) {
1981
1973
  composeExtension.authorization = {
1982
1974
  authType: "apiSecretServiceAuth",
@@ -2103,7 +2095,6 @@ class SpecParser {
2103
2095
  };
2104
2096
  this.pathOrSpec = pathOrDoc;
2105
2097
  this.parser = new SwaggerParser();
2106
- this.refParser = new $RefParser();
2107
2098
  this.options = Object.assign(Object.assign({}, this.defaultOptions), (options !== null && options !== void 0 ? options : {}));
2108
2099
  }
2109
2100
  /**
@@ -2116,15 +2107,12 @@ class SpecParser {
2116
2107
  let hash = "";
2117
2108
  try {
2118
2109
  await this.loadSpec();
2119
- if (!this.refParser.$refs.circular) {
2110
+ if (!this.parser.$refs.circular) {
2120
2111
  await this.parser.validate(this.spec);
2121
2112
  }
2122
2113
  else {
2123
- // The following code hangs for Graph API, support will be added when SwaggerParser is updated.
2124
- /*
2125
2114
  const clonedUnResolveSpec = JSON.parse(JSON.stringify(this.unResolveSpec));
2126
2115
  await this.parser.validate(clonedUnResolveSpec);
2127
- */
2128
2116
  }
2129
2117
  }
2130
2118
  catch (e) {
@@ -2152,7 +2140,7 @@ class SpecParser {
2152
2140
  };
2153
2141
  }
2154
2142
  // Remote reference not supported
2155
- const refPaths = this.refParser.$refs.paths();
2143
+ const refPaths = this.parser.$refs.paths();
2156
2144
  // refPaths [0] is the current spec file path
2157
2145
  if (refPaths.length > 1) {
2158
2146
  errors.push({
@@ -2281,7 +2269,7 @@ class SpecParser {
2281
2269
  throw new SpecParserError(ConstantString.CancelledMessage, ErrorType.Cancelled);
2282
2270
  }
2283
2271
  const clonedUnResolveSpec = JSON.parse(JSON.stringify(newUnResolvedSpec));
2284
- const newSpec = await this.deReferenceSpec(clonedUnResolveSpec);
2272
+ const newSpec = (await this.parser.dereference(clonedUnResolveSpec));
2285
2273
  return [newUnResolvedSpec, newSpec];
2286
2274
  }
2287
2275
  catch (err) {
@@ -2291,10 +2279,6 @@ class SpecParser {
2291
2279
  throw new SpecParserError(err.toString(), ErrorType.GetSpecFailed);
2292
2280
  }
2293
2281
  }
2294
- async deReferenceSpec(spec) {
2295
- const result = await this.refParser.dereference(spec);
2296
- return result;
2297
- }
2298
2282
  /**
2299
2283
  * Generates and update artifacts from the OpenAPI specification file. Generate Adaptive Cards, update Teams app manifest, and generate a new OpenAPI specification file.
2300
2284
  * @param manifestPath A file path of the Teams app manifest file to update.
@@ -2311,6 +2295,7 @@ class SpecParser {
2311
2295
  const newSpecs = await this.getFilteredSpecs(filter, signal);
2312
2296
  const newUnResolvedSpec = newSpecs[0];
2313
2297
  const newSpec = newSpecs[1];
2298
+ const authInfo = Utils.getAuthInfo(newSpec);
2314
2299
  const paths = newUnResolvedSpec.paths;
2315
2300
  for (const pathUrl in paths) {
2316
2301
  const operations = paths[pathUrl];
@@ -2318,22 +2303,15 @@ class SpecParser {
2318
2303
  const operationItem = operations[method];
2319
2304
  const operationId = operationItem.operationId;
2320
2305
  const containsSpecialCharacters = /[^a-zA-Z0-9_]/.test(operationId);
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
- });
2306
+ if (!containsSpecialCharacters) {
2307
+ continue;
2336
2308
  }
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
+ });
2337
2315
  }
2338
2316
  }
2339
2317
  await this.saveFilterSpec(outputSpecPath, newUnResolvedSpec);
@@ -2346,8 +2324,7 @@ class SpecParser {
2346
2324
  specPath: this.pathOrSpec,
2347
2325
  }
2348
2326
  : undefined;
2349
- const authMap = Utils.getAuthMap(newSpec);
2350
- const [updatedManifest, apiPlugin, warnings] = await ManifestUpdater.updateManifestWithAiPlugin(manifestPath, outputSpecPath, pluginFilePath, newSpec, this.options, authMap, existingPluginManifestInfo);
2327
+ const [updatedManifest, apiPlugin, warnings] = await ManifestUpdater.updateManifestWithAiPlugin(manifestPath, outputSpecPath, pluginFilePath, newSpec, this.options, authInfo, existingPluginManifestInfo);
2351
2328
  result.warnings.push(...warnings);
2352
2329
  await fs.outputJSON(manifestPath, updatedManifest, { spaces: 4 });
2353
2330
  await fs.outputJSON(pluginFilePath, apiPlugin, { spaces: 4 });
@@ -2435,7 +2412,7 @@ class SpecParser {
2435
2412
  this.isSwaggerFile = true;
2436
2413
  }
2437
2414
  const clonedUnResolveSpec = JSON.parse(JSON.stringify(this.unResolveSpec));
2438
- this.spec = await this.deReferenceSpec(clonedUnResolveSpec);
2415
+ this.spec = (await this.parser.dereference(clonedUnResolveSpec));
2439
2416
  }
2440
2417
  }
2441
2418
  getAPIs(spec) {