@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.
@@ -62,8 +62,11 @@ var ErrorType;
62
62
  ErrorType["PostBodyContainMultipleMediaTypes"] = "post-body-contain-multiple-media-types";
63
63
  ErrorType["ResponseContainMultipleMediaTypes"] = "response-contain-multiple-media-types";
64
64
  ErrorType["ResponseJsonIsEmpty"] = "response-json-is-empty";
65
+ ErrorType["PostBodySchemaIsNotJson"] = "post-body-schema-is-not-json";
65
66
  ErrorType["PostBodyContainsRequiredUnsupportedSchema"] = "post-body-contains-required-unsupported-schema";
66
67
  ErrorType["ParamsContainRequiredUnsupportedSchema"] = "params-contain-required-unsupported-schema";
68
+ ErrorType["ParamsContainsNestedObject"] = "params-contains-nested-object";
69
+ ErrorType["RequestBodyContainsNestedObject"] = "request-body-contains-nested-object";
67
70
  ErrorType["ExceededRequiredParamsLimit"] = "exceeded-required-params-limit";
68
71
  ErrorType["NoParameter"] = "no-parameter";
69
72
  ErrorType["NoAPIInfo"] = "no-api-info";
@@ -83,7 +86,6 @@ var WarningType;
83
86
  WarningType["ConvertSwaggerToOpenAPI"] = "convert-swagger-to-openapi";
84
87
  WarningType["FuncDescriptionTooLong"] = "function-description-too-long";
85
88
  WarningType["OperationIdContainsSpecialCharacters"] = "operationid-contains-special-characters";
86
- WarningType["UnsupportedAuthType"] = "unsupported-auth-type";
87
89
  WarningType["GenerateJsonDataFailed"] = "generate-json-data-failed";
88
90
  WarningType["Unknown"] = "unknown";
89
91
  })(WarningType || (WarningType = {}));
@@ -131,7 +133,6 @@ ConstantString.SwaggerNotSupported = "Swagger 2.0 is not supported. Please conve
131
133
  ConstantString.SpecVersionNotSupported = "Unsupported OpenAPI version %s. Please use version 3.0.x.";
132
134
  ConstantString.MultipleAuthNotSupported = "Multiple authentication methods are unsupported. Ensure all selected APIs use identical authentication.";
133
135
  ConstantString.OperationIdContainsSpecialCharacters = "Operation id '%s' in OpenAPI description document contained special characters and was renamed to '%s'.";
134
- ConstantString.AuthTypeIsNotSupported = "Unsupported authorization type in API '%s'. No authorization will be used.";
135
136
  ConstantString.UnsupportedSchema = "Unsupported schema in %s %s: %s";
136
137
  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.";
137
138
  ConstantString.GenerateJsonDataFailed = "Failed to generate JSON data for api: %s due to %s.";
@@ -141,12 +142,17 @@ ConstantString.WrappedCardResponseLayout = "list";
141
142
  ConstantString.GetMethod = "get";
142
143
  ConstantString.PostMethod = "post";
143
144
  ConstantString.AdaptiveCardVersion = "1.5";
144
- ConstantString.AdaptiveCardSchema = "https://adaptivecards.io/schemas/adaptive-card.json";
145
+ ConstantString.AdaptiveCardSchema = "http://adaptivecards.io/schemas/adaptive-card.json";
145
146
  ConstantString.AdaptiveCardType = "AdaptiveCard";
146
147
  ConstantString.TextBlockType = "TextBlock";
147
148
  ConstantString.ImageType = "Image";
148
149
  ConstantString.ContainerType = "Container";
149
- ConstantString.RegistrationIdPostfix = "REGISTRATION_ID";
150
+ ConstantString.RegistrationIdPostfix = {
151
+ apiKey: "REGISTRATION_ID",
152
+ oauth2: "CONFIGURATION_ID",
153
+ http: "REGISTRATION_ID",
154
+ openIdConnect: "REGISTRATION_ID",
155
+ };
150
156
  ConstantString.ResponseCodeFor20X = [
151
157
  "200",
152
158
  "201",
@@ -158,7 +164,6 @@ ConstantString.ResponseCodeFor20X = [
158
164
  "207",
159
165
  "208",
160
166
  "226",
161
- "2XX",
162
167
  "default",
163
168
  ];
164
169
  ConstantString.AllOperationMethods = [
@@ -216,6 +221,17 @@ ConstantString.PluginManifestSchema = "https://developer.microsoft.com/json-sche
216
221
 
217
222
  // Copyright (c) Microsoft Corporation.
218
223
  class Utils {
224
+ static hasNestedObjectInSchema(schema) {
225
+ if (this.isObjectSchema(schema)) {
226
+ for (const property in schema.properties) {
227
+ const nestedSchema = schema.properties[property];
228
+ if (this.isObjectSchema(nestedSchema)) {
229
+ return true;
230
+ }
231
+ }
232
+ }
233
+ return false;
234
+ }
219
235
  static isObjectSchema(schema) {
220
236
  return schema.type === "object" || (!schema.type && !!schema.properties);
221
237
  }
@@ -228,32 +244,11 @@ class Utils {
228
244
  static isAPIKeyAuth(authScheme) {
229
245
  return authScheme.type === "apiKey";
230
246
  }
231
- static isAPIKeyAuthButNotInCookie(authScheme) {
232
- return authScheme.type === "apiKey" && authScheme.in !== "cookie";
233
- }
234
247
  static isOAuthWithAuthCodeFlow(authScheme) {
235
248
  return !!(authScheme.type === "oauth2" &&
236
249
  authScheme.flows &&
237
250
  authScheme.flows.authorizationCode);
238
251
  }
239
- static isNotSupportedAuth(authSchemeArray) {
240
- if (authSchemeArray.length === 0) {
241
- return false;
242
- }
243
- if (authSchemeArray.length > 0 && authSchemeArray.every((auths) => auths.length > 1)) {
244
- return true;
245
- }
246
- for (const auths of authSchemeArray) {
247
- if (auths.length === 1) {
248
- if (Utils.isOAuthWithAuthCodeFlow(auths[0].authScheme) ||
249
- Utils.isBearerTokenAuth(auths[0].authScheme) ||
250
- Utils.isAPIKeyAuthButNotInCookie(auths[0].authScheme)) {
251
- return false;
252
- }
253
- }
254
- }
255
- return true;
256
- }
257
252
  static getAuthArray(securities, spec) {
258
253
  var _a;
259
254
  const result = [];
@@ -278,20 +273,6 @@ class Utils {
278
273
  result.sort((a, b) => a[0].name.localeCompare(b[0].name));
279
274
  return result;
280
275
  }
281
- static getAuthMap(spec) {
282
- const authMap = {};
283
- for (const url in spec.paths) {
284
- for (const method in spec.paths[url]) {
285
- const operation = spec.paths[url][method];
286
- const authArray = Utils.getAuthArray(operation.security, spec);
287
- if (authArray && authArray.length > 0) {
288
- const currentAuth = authArray[0][0];
289
- authMap[operation.operationId] = currentAuth;
290
- }
291
- }
292
- }
293
- return authMap;
294
- }
295
276
  static getAuthInfo(spec) {
296
277
  let authInfo = undefined;
297
278
  for (const url in spec.paths) {
@@ -320,32 +301,26 @@ class Utils {
320
301
  let multipleMediaType = false;
321
302
  for (const code of ConstantString.ResponseCodeFor20X) {
322
303
  const responseObject = (_a = operationObject === null || operationObject === void 0 ? void 0 : operationObject.responses) === null || _a === void 0 ? void 0 : _a[code];
323
- if (!responseObject) {
324
- continue;
325
- }
326
- multipleMediaType = Utils.containMultipleMediaTypes(responseObject);
327
- if (!allowMultipleMediaType && multipleMediaType) {
328
- json = {};
329
- continue;
330
- }
331
- const mediaObj = Utils.getJsonContentType(responseObject);
332
- if (Object.keys(mediaObj).length > 0) {
333
- json = mediaObj;
334
- return { json, multipleMediaType };
335
- }
336
- }
337
- return { json, multipleMediaType };
338
- }
339
- static getJsonContentType(responseObject) {
340
- if (responseObject.content) {
341
- for (const contentType of Object.keys(responseObject.content)) {
342
- // json media type can also be "application/json; charset=utf-8"
343
- if (contentType.indexOf("application/json") >= 0) {
344
- return responseObject.content[contentType];
304
+ if (responseObject === null || responseObject === void 0 ? void 0 : responseObject.content) {
305
+ for (const contentType of Object.keys(responseObject.content)) {
306
+ // json media type can also be "application/json; charset=utf-8"
307
+ if (contentType.indexOf("application/json") >= 0) {
308
+ multipleMediaType = false;
309
+ json = responseObject.content[contentType];
310
+ if (Utils.containMultipleMediaTypes(responseObject)) {
311
+ multipleMediaType = true;
312
+ if (!allowMultipleMediaType) {
313
+ json = {};
314
+ }
315
+ }
316
+ else {
317
+ return { json, multipleMediaType };
318
+ }
319
+ }
345
320
  }
346
321
  }
347
322
  }
348
- return {};
323
+ return { json, multipleMediaType };
349
324
  }
350
325
  static convertPathToCamelCase(path) {
351
326
  const pathSegments = path.split(/[./{]/);
@@ -382,7 +357,7 @@ class Utils {
382
357
  }
383
358
  return newStr;
384
359
  }
385
- static checkServerUrl(servers, allowHttp = false) {
360
+ static checkServerUrl(servers) {
386
361
  const errors = [];
387
362
  let serverUrl;
388
363
  try {
@@ -405,7 +380,8 @@ class Utils {
405
380
  data: servers,
406
381
  });
407
382
  }
408
- else if (protocol !== "https:" && !(protocol === "http:" && allowHttp)) {
383
+ else if (protocol !== "https:") {
384
+ // Http server url is not supported
409
385
  const protocolString = protocol.slice(0, -1);
410
386
  errors.push({
411
387
  type: ErrorType.UrlProtocolNotSupported,
@@ -421,11 +397,10 @@ class Utils {
421
397
  let hasTopLevelServers = false;
422
398
  let hasPathLevelServers = false;
423
399
  let hasOperationLevelServers = false;
424
- const allowHttp = options.projectType === ProjectType.Copilot;
425
400
  if (spec.servers && spec.servers.length >= 1) {
426
401
  hasTopLevelServers = true;
427
402
  // for multiple server, we only use the first url
428
- const serverErrors = Utils.checkServerUrl(spec.servers, allowHttp);
403
+ const serverErrors = Utils.checkServerUrl(spec.servers);
429
404
  errors.push(...serverErrors);
430
405
  }
431
406
  const paths = spec.paths;
@@ -433,7 +408,7 @@ class Utils {
433
408
  const methods = paths[path];
434
409
  if ((methods === null || methods === void 0 ? void 0 : methods.servers) && methods.servers.length >= 1) {
435
410
  hasPathLevelServers = true;
436
- const serverErrors = Utils.checkServerUrl(methods.servers, allowHttp);
411
+ const serverErrors = Utils.checkServerUrl(methods.servers);
437
412
  errors.push(...serverErrors);
438
413
  }
439
414
  for (const method in methods) {
@@ -441,7 +416,7 @@ class Utils {
441
416
  if (((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
442
417
  if ((operationObject === null || operationObject === void 0 ? void 0 : operationObject.servers) && operationObject.servers.length >= 1) {
443
418
  hasOperationLevelServers = true;
444
- const serverErrors = Utils.checkServerUrl(operationObject.servers, allowHttp);
419
+ const serverErrors = Utils.checkServerUrl(operationObject.servers);
445
420
  errors.push(...serverErrors);
446
421
  }
447
422
  }
@@ -730,6 +705,22 @@ class Validator {
730
705
  }
731
706
  return result;
732
707
  }
708
+ validateResponse(method, path) {
709
+ const result = { isValid: true, reason: [] };
710
+ const operationObject = this.spec.paths[path][method];
711
+ const { json, multipleMediaType } = Utils.getResponseJson(operationObject);
712
+ if (this.options.projectType === ProjectType.SME) {
713
+ // only support response body only contains “application/json” content type
714
+ if (multipleMediaType) {
715
+ result.reason.push(ErrorType.ResponseContainMultipleMediaTypes);
716
+ }
717
+ else if (Object.keys(json).length === 0) {
718
+ // response body should not be empty
719
+ result.reason.push(ErrorType.ResponseJsonIsEmpty);
720
+ }
721
+ }
722
+ return result;
723
+ }
733
724
  validateServer(method, path) {
734
725
  const result = { isValid: true, reason: [] };
735
726
  const serverObj = Utils.getServerObject(this.spec, method, path);
@@ -738,8 +729,8 @@ class Validator {
738
729
  result.reason.push(ErrorType.NoServerInformation);
739
730
  }
740
731
  else {
741
- const allowHttp = this.projectType === ProjectType.Copilot;
742
- const serverValidateResult = Utils.checkServerUrl([serverObj], allowHttp);
732
+ // server url should be absolute url with https protocol
733
+ const serverValidateResult = Utils.checkServerUrl([serverObj]);
743
734
  result.reason.push(...serverValidateResult.map((item) => item.type));
744
735
  }
745
736
  return result;
@@ -762,9 +753,6 @@ class Validator {
762
753
  reason: [ErrorType.MultipleAuthNotSupported],
763
754
  };
764
755
  }
765
- if (this.projectType === ProjectType.Copilot) {
766
- return { isValid: true, reason: [] };
767
- }
768
756
  for (const auths of authSchemeArray) {
769
757
  if (auths.length === 1) {
770
758
  if ((this.options.allowAPIKeyAuth && Utils.isAPIKeyAuth(auths[0].authScheme)) ||
@@ -777,6 +765,114 @@ class Validator {
777
765
  }
778
766
  return { isValid: false, reason: [ErrorType.AuthTypeIsNotSupported] };
779
767
  }
768
+ checkPostBodySchema(schema, isRequired = false) {
769
+ var _a;
770
+ const paramResult = {
771
+ requiredNum: 0,
772
+ optionalNum: 0,
773
+ isValid: true,
774
+ reason: [],
775
+ };
776
+ if (Object.keys(schema).length === 0) {
777
+ return paramResult;
778
+ }
779
+ const isRequiredWithoutDefault = isRequired && schema.default === undefined;
780
+ const isCopilot = this.projectType === ProjectType.Copilot;
781
+ if (isCopilot && Utils.hasNestedObjectInSchema(schema)) {
782
+ paramResult.isValid = false;
783
+ paramResult.reason = [ErrorType.RequestBodyContainsNestedObject];
784
+ return paramResult;
785
+ }
786
+ if (schema.type === "string" ||
787
+ schema.type === "integer" ||
788
+ schema.type === "boolean" ||
789
+ schema.type === "number") {
790
+ if (isRequiredWithoutDefault) {
791
+ paramResult.requiredNum = paramResult.requiredNum + 1;
792
+ }
793
+ else {
794
+ paramResult.optionalNum = paramResult.optionalNum + 1;
795
+ }
796
+ }
797
+ else if (Utils.isObjectSchema(schema)) {
798
+ const { properties } = schema;
799
+ for (const property in properties) {
800
+ let isRequired = false;
801
+ if (schema.required && ((_a = schema.required) === null || _a === void 0 ? void 0 : _a.indexOf(property)) >= 0) {
802
+ isRequired = true;
803
+ }
804
+ const result = this.checkPostBodySchema(properties[property], isRequired);
805
+ paramResult.requiredNum += result.requiredNum;
806
+ paramResult.optionalNum += result.optionalNum;
807
+ paramResult.isValid = paramResult.isValid && result.isValid;
808
+ paramResult.reason.push(...result.reason);
809
+ }
810
+ }
811
+ else {
812
+ if (isRequiredWithoutDefault && !isCopilot) {
813
+ paramResult.isValid = false;
814
+ paramResult.reason.push(ErrorType.PostBodyContainsRequiredUnsupportedSchema);
815
+ }
816
+ }
817
+ return paramResult;
818
+ }
819
+ checkParamSchema(paramObject) {
820
+ const paramResult = {
821
+ requiredNum: 0,
822
+ optionalNum: 0,
823
+ isValid: true,
824
+ reason: [],
825
+ };
826
+ if (!paramObject) {
827
+ return paramResult;
828
+ }
829
+ const isCopilot = this.projectType === ProjectType.Copilot;
830
+ for (let i = 0; i < paramObject.length; i++) {
831
+ const param = paramObject[i];
832
+ const schema = param.schema;
833
+ if (isCopilot && Utils.hasNestedObjectInSchema(schema)) {
834
+ paramResult.isValid = false;
835
+ paramResult.reason.push(ErrorType.ParamsContainsNestedObject);
836
+ continue;
837
+ }
838
+ const isRequiredWithoutDefault = param.required && schema.default === undefined;
839
+ if (isCopilot) {
840
+ if (isRequiredWithoutDefault) {
841
+ paramResult.requiredNum = paramResult.requiredNum + 1;
842
+ }
843
+ else {
844
+ paramResult.optionalNum = paramResult.optionalNum + 1;
845
+ }
846
+ continue;
847
+ }
848
+ if (param.in === "header" || param.in === "cookie") {
849
+ if (isRequiredWithoutDefault) {
850
+ paramResult.isValid = false;
851
+ paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
852
+ }
853
+ continue;
854
+ }
855
+ if (schema.type !== "boolean" &&
856
+ schema.type !== "string" &&
857
+ schema.type !== "number" &&
858
+ schema.type !== "integer") {
859
+ if (isRequiredWithoutDefault) {
860
+ paramResult.isValid = false;
861
+ paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
862
+ }
863
+ continue;
864
+ }
865
+ if (param.in === "query" || param.in === "path") {
866
+ if (isRequiredWithoutDefault) {
867
+ paramResult.requiredNum = paramResult.requiredNum + 1;
868
+ }
869
+ else {
870
+ paramResult.optionalNum = paramResult.optionalNum + 1;
871
+ }
872
+ }
873
+ }
874
+ return paramResult;
875
+ }
780
876
  }
781
877
 
782
878
  // Copyright (c) Microsoft Corporation.
@@ -786,6 +882,7 @@ class CopilotValidator extends Validator {
786
882
  this.projectType = ProjectType.Copilot;
787
883
  this.options = options;
788
884
  this.spec = spec;
885
+ this.checkCircularReference();
789
886
  }
790
887
  validateSpec() {
791
888
  const result = { errors: [], warnings: [] };
@@ -811,6 +908,10 @@ class CopilotValidator extends Validator {
811
908
  if (!methodAndPathResult.isValid) {
812
909
  return methodAndPathResult;
813
910
  }
911
+ const circularReferenceResult = this.validateCircularReference(method, path);
912
+ if (!circularReferenceResult.isValid) {
913
+ return circularReferenceResult;
914
+ }
814
915
  const operationObject = this.spec.paths[path][method];
815
916
  // validate auth
816
917
  const authCheckResult = this.validateAuth(method, path);
@@ -822,6 +923,24 @@ class CopilotValidator extends Validator {
822
923
  // validate server
823
924
  const validateServerResult = this.validateServer(method, path);
824
925
  result.reason.push(...validateServerResult.reason);
926
+ // validate response
927
+ const validateResponseResult = this.validateResponse(method, path);
928
+ result.reason.push(...validateResponseResult.reason);
929
+ // validate requestBody
930
+ const requestBody = operationObject.requestBody;
931
+ const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
932
+ if (requestJsonBody) {
933
+ const requestBodySchema = requestJsonBody.schema;
934
+ if (!Utils.isObjectSchema(requestBodySchema)) {
935
+ result.reason.push(ErrorType.PostBodySchemaIsNotJson);
936
+ }
937
+ const requestBodyParamResult = this.checkPostBodySchema(requestBodySchema, requestBody.required);
938
+ result.reason.push(...requestBodyParamResult.reason);
939
+ }
940
+ // validate parameters
941
+ const paramObject = operationObject.parameters;
942
+ const paramResult = this.checkParamSchema(paramObject);
943
+ result.reason.push(...paramResult.reason);
825
944
  if (result.reason.length > 0) {
826
945
  result.isValid = false;
827
946
  }
@@ -913,108 +1032,6 @@ class SMEValidator extends Validator {
913
1032
  }
914
1033
  return result;
915
1034
  }
916
- validateResponse(method, path) {
917
- const result = { isValid: true, reason: [] };
918
- const operationObject = this.spec.paths[path][method];
919
- const { json, multipleMediaType } = Utils.getResponseJson(operationObject);
920
- // only support response body only contains “application/json” content type
921
- if (multipleMediaType) {
922
- result.reason.push(ErrorType.ResponseContainMultipleMediaTypes);
923
- }
924
- else if (Object.keys(json).length === 0) {
925
- // response body should not be empty
926
- result.reason.push(ErrorType.ResponseJsonIsEmpty);
927
- }
928
- return result;
929
- }
930
- checkPostBodySchema(schema, isRequired = false) {
931
- var _a;
932
- const paramResult = {
933
- requiredNum: 0,
934
- optionalNum: 0,
935
- isValid: true,
936
- reason: [],
937
- };
938
- if (Object.keys(schema).length === 0) {
939
- return paramResult;
940
- }
941
- const isRequiredWithoutDefault = isRequired && schema.default === undefined;
942
- const isCopilot = this.projectType === ProjectType.Copilot;
943
- if (schema.type === "string" ||
944
- schema.type === "integer" ||
945
- schema.type === "boolean" ||
946
- schema.type === "number") {
947
- if (isRequiredWithoutDefault) {
948
- paramResult.requiredNum = paramResult.requiredNum + 1;
949
- }
950
- else {
951
- paramResult.optionalNum = paramResult.optionalNum + 1;
952
- }
953
- }
954
- else if (Utils.isObjectSchema(schema)) {
955
- const { properties } = schema;
956
- for (const property in properties) {
957
- let isRequired = false;
958
- if (schema.required && ((_a = schema.required) === null || _a === void 0 ? void 0 : _a.indexOf(property)) >= 0) {
959
- isRequired = true;
960
- }
961
- const result = this.checkPostBodySchema(properties[property], isRequired);
962
- paramResult.requiredNum += result.requiredNum;
963
- paramResult.optionalNum += result.optionalNum;
964
- paramResult.isValid = paramResult.isValid && result.isValid;
965
- paramResult.reason.push(...result.reason);
966
- }
967
- }
968
- else {
969
- if (isRequiredWithoutDefault && !isCopilot) {
970
- paramResult.isValid = false;
971
- paramResult.reason.push(ErrorType.PostBodyContainsRequiredUnsupportedSchema);
972
- }
973
- }
974
- return paramResult;
975
- }
976
- checkParamSchema(paramObject) {
977
- const paramResult = {
978
- requiredNum: 0,
979
- optionalNum: 0,
980
- isValid: true,
981
- reason: [],
982
- };
983
- if (!paramObject) {
984
- return paramResult;
985
- }
986
- for (let i = 0; i < paramObject.length; i++) {
987
- const param = paramObject[i];
988
- const schema = param.schema;
989
- const isRequiredWithoutDefault = param.required && schema.default === undefined;
990
- if (param.in === "header" || param.in === "cookie") {
991
- if (isRequiredWithoutDefault) {
992
- paramResult.isValid = false;
993
- paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
994
- }
995
- continue;
996
- }
997
- if (schema.type !== "boolean" &&
998
- schema.type !== "string" &&
999
- schema.type !== "number" &&
1000
- schema.type !== "integer") {
1001
- if (isRequiredWithoutDefault) {
1002
- paramResult.isValid = false;
1003
- paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
1004
- }
1005
- continue;
1006
- }
1007
- if (param.in === "query" || param.in === "path") {
1008
- if (isRequiredWithoutDefault) {
1009
- paramResult.requiredNum = paramResult.requiredNum + 1;
1010
- }
1011
- else {
1012
- paramResult.optionalNum = paramResult.optionalNum + 1;
1013
- }
1014
- }
1015
- }
1016
- return paramResult;
1017
- }
1018
1035
  validateParamCount(postBodyResult, paramResult) {
1019
1036
  const result = { isValid: true, reason: [] };
1020
1037
  const totalRequiredParams = postBodyResult.requiredNum + paramResult.requiredNum;