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

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.
@@ -32,8 +32,11 @@ var ErrorType;
32
32
  ErrorType["PostBodyContainMultipleMediaTypes"] = "post-body-contain-multiple-media-types";
33
33
  ErrorType["ResponseContainMultipleMediaTypes"] = "response-contain-multiple-media-types";
34
34
  ErrorType["ResponseJsonIsEmpty"] = "response-json-is-empty";
35
+ ErrorType["PostBodySchemaIsNotJson"] = "post-body-schema-is-not-json";
35
36
  ErrorType["PostBodyContainsRequiredUnsupportedSchema"] = "post-body-contains-required-unsupported-schema";
36
37
  ErrorType["ParamsContainRequiredUnsupportedSchema"] = "params-contain-required-unsupported-schema";
38
+ ErrorType["ParamsContainsNestedObject"] = "params-contains-nested-object";
39
+ ErrorType["RequestBodyContainsNestedObject"] = "request-body-contains-nested-object";
37
40
  ErrorType["ExceededRequiredParamsLimit"] = "exceeded-required-params-limit";
38
41
  ErrorType["NoParameter"] = "no-parameter";
39
42
  ErrorType["NoAPIInfo"] = "no-api-info";
@@ -109,7 +112,7 @@ ConstantString.WrappedCardResponseLayout = "list";
109
112
  ConstantString.GetMethod = "get";
110
113
  ConstantString.PostMethod = "post";
111
114
  ConstantString.AdaptiveCardVersion = "1.5";
112
- ConstantString.AdaptiveCardSchema = "https://adaptivecards.io/schemas/adaptive-card.json";
115
+ ConstantString.AdaptiveCardSchema = "http://adaptivecards.io/schemas/adaptive-card.json";
113
116
  ConstantString.AdaptiveCardType = "AdaptiveCard";
114
117
  ConstantString.TextBlockType = "TextBlock";
115
118
  ConstantString.ImageType = "Image";
@@ -188,6 +191,17 @@ ConstantString.PluginManifestSchema = "https://developer.microsoft.com/json-sche
188
191
 
189
192
  // Copyright (c) Microsoft Corporation.
190
193
  class Utils {
194
+ static hasNestedObjectInSchema(schema) {
195
+ if (this.isObjectSchema(schema)) {
196
+ for (const property in schema.properties) {
197
+ const nestedSchema = schema.properties[property];
198
+ if (this.isObjectSchema(nestedSchema)) {
199
+ return true;
200
+ }
201
+ }
202
+ }
203
+ return false;
204
+ }
191
205
  static isObjectSchema(schema) {
192
206
  return schema.type === "object" || (!schema.type && !!schema.properties);
193
207
  }
@@ -257,32 +271,26 @@ class Utils {
257
271
  let multipleMediaType = false;
258
272
  for (const code of ConstantString.ResponseCodeFor20X) {
259
273
  const responseObject = (_a = operationObject === null || operationObject === void 0 ? void 0 : operationObject.responses) === null || _a === void 0 ? void 0 : _a[code];
260
- if (!responseObject) {
261
- continue;
262
- }
263
- multipleMediaType = Utils.containMultipleMediaTypes(responseObject);
264
- if (!allowMultipleMediaType && multipleMediaType) {
265
- json = {};
266
- continue;
267
- }
268
- const mediaObj = Utils.getJsonContentType(responseObject);
269
- if (Object.keys(mediaObj).length > 0) {
270
- json = mediaObj;
271
- return { json, multipleMediaType };
272
- }
273
- }
274
- return { json, multipleMediaType };
275
- }
276
- static getJsonContentType(responseObject) {
277
- if (responseObject.content) {
278
- for (const contentType of Object.keys(responseObject.content)) {
279
- // json media type can also be "application/json; charset=utf-8"
280
- if (contentType.indexOf("application/json") >= 0) {
281
- return responseObject.content[contentType];
274
+ if (responseObject === null || responseObject === void 0 ? void 0 : responseObject.content) {
275
+ for (const contentType of Object.keys(responseObject.content)) {
276
+ // json media type can also be "application/json; charset=utf-8"
277
+ if (contentType.indexOf("application/json") >= 0) {
278
+ multipleMediaType = false;
279
+ json = responseObject.content[contentType];
280
+ if (Utils.containMultipleMediaTypes(responseObject)) {
281
+ multipleMediaType = true;
282
+ if (!allowMultipleMediaType) {
283
+ json = {};
284
+ }
285
+ }
286
+ else {
287
+ return { json, multipleMediaType };
288
+ }
289
+ }
282
290
  }
283
291
  }
284
292
  }
285
- return {};
293
+ return { json, multipleMediaType };
286
294
  }
287
295
  static convertPathToCamelCase(path) {
288
296
  const pathSegments = path.split(/[./{]/);
@@ -319,7 +327,7 @@ class Utils {
319
327
  }
320
328
  return newStr;
321
329
  }
322
- static checkServerUrl(servers, allowHttp = false) {
330
+ static checkServerUrl(servers) {
323
331
  const errors = [];
324
332
  let serverUrl;
325
333
  try {
@@ -342,7 +350,8 @@ class Utils {
342
350
  data: servers,
343
351
  });
344
352
  }
345
- else if (protocol !== "https:" && !(protocol === "http:" && allowHttp)) {
353
+ else if (protocol !== "https:") {
354
+ // Http server url is not supported
346
355
  const protocolString = protocol.slice(0, -1);
347
356
  errors.push({
348
357
  type: ErrorType.UrlProtocolNotSupported,
@@ -358,11 +367,10 @@ class Utils {
358
367
  let hasTopLevelServers = false;
359
368
  let hasPathLevelServers = false;
360
369
  let hasOperationLevelServers = false;
361
- const allowHttp = options.projectType === ProjectType.Copilot;
362
370
  if (spec.servers && spec.servers.length >= 1) {
363
371
  hasTopLevelServers = true;
364
372
  // for multiple server, we only use the first url
365
- const serverErrors = Utils.checkServerUrl(spec.servers, allowHttp);
373
+ const serverErrors = Utils.checkServerUrl(spec.servers);
366
374
  errors.push(...serverErrors);
367
375
  }
368
376
  const paths = spec.paths;
@@ -370,7 +378,7 @@ class Utils {
370
378
  const methods = paths[path];
371
379
  if ((methods === null || methods === void 0 ? void 0 : methods.servers) && methods.servers.length >= 1) {
372
380
  hasPathLevelServers = true;
373
- const serverErrors = Utils.checkServerUrl(methods.servers, allowHttp);
381
+ const serverErrors = Utils.checkServerUrl(methods.servers);
374
382
  errors.push(...serverErrors);
375
383
  }
376
384
  for (const method in methods) {
@@ -378,7 +386,7 @@ class Utils {
378
386
  if (((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
379
387
  if ((operationObject === null || operationObject === void 0 ? void 0 : operationObject.servers) && operationObject.servers.length >= 1) {
380
388
  hasOperationLevelServers = true;
381
- const serverErrors = Utils.checkServerUrl(operationObject.servers, allowHttp);
389
+ const serverErrors = Utils.checkServerUrl(operationObject.servers);
382
390
  errors.push(...serverErrors);
383
391
  }
384
392
  }
@@ -667,6 +675,22 @@ class Validator {
667
675
  }
668
676
  return result;
669
677
  }
678
+ validateResponse(method, path) {
679
+ const result = { isValid: true, reason: [] };
680
+ const operationObject = this.spec.paths[path][method];
681
+ const { json, multipleMediaType } = Utils.getResponseJson(operationObject);
682
+ if (this.options.projectType === ProjectType.SME) {
683
+ // only support response body only contains “application/json” content type
684
+ if (multipleMediaType) {
685
+ result.reason.push(ErrorType.ResponseContainMultipleMediaTypes);
686
+ }
687
+ else if (Object.keys(json).length === 0) {
688
+ // response body should not be empty
689
+ result.reason.push(ErrorType.ResponseJsonIsEmpty);
690
+ }
691
+ }
692
+ return result;
693
+ }
670
694
  validateServer(method, path) {
671
695
  const result = { isValid: true, reason: [] };
672
696
  const serverObj = Utils.getServerObject(this.spec, method, path);
@@ -675,8 +699,8 @@ class Validator {
675
699
  result.reason.push(ErrorType.NoServerInformation);
676
700
  }
677
701
  else {
678
- const allowHttp = this.projectType === ProjectType.Copilot;
679
- const serverValidateResult = Utils.checkServerUrl([serverObj], allowHttp);
702
+ // server url should be absolute url with https protocol
703
+ const serverValidateResult = Utils.checkServerUrl([serverObj]);
680
704
  result.reason.push(...serverValidateResult.map((item) => item.type));
681
705
  }
682
706
  return result;
@@ -711,6 +735,114 @@ class Validator {
711
735
  }
712
736
  return { isValid: false, reason: [ErrorType.AuthTypeIsNotSupported] };
713
737
  }
738
+ checkPostBodySchema(schema, isRequired = false) {
739
+ var _a;
740
+ const paramResult = {
741
+ requiredNum: 0,
742
+ optionalNum: 0,
743
+ isValid: true,
744
+ reason: [],
745
+ };
746
+ if (Object.keys(schema).length === 0) {
747
+ return paramResult;
748
+ }
749
+ const isRequiredWithoutDefault = isRequired && schema.default === undefined;
750
+ const isCopilot = this.projectType === ProjectType.Copilot;
751
+ if (isCopilot && Utils.hasNestedObjectInSchema(schema)) {
752
+ paramResult.isValid = false;
753
+ paramResult.reason = [ErrorType.RequestBodyContainsNestedObject];
754
+ return paramResult;
755
+ }
756
+ if (schema.type === "string" ||
757
+ schema.type === "integer" ||
758
+ schema.type === "boolean" ||
759
+ schema.type === "number") {
760
+ if (isRequiredWithoutDefault) {
761
+ paramResult.requiredNum = paramResult.requiredNum + 1;
762
+ }
763
+ else {
764
+ paramResult.optionalNum = paramResult.optionalNum + 1;
765
+ }
766
+ }
767
+ else if (Utils.isObjectSchema(schema)) {
768
+ const { properties } = schema;
769
+ for (const property in properties) {
770
+ let isRequired = false;
771
+ if (schema.required && ((_a = schema.required) === null || _a === void 0 ? void 0 : _a.indexOf(property)) >= 0) {
772
+ isRequired = true;
773
+ }
774
+ const result = this.checkPostBodySchema(properties[property], isRequired);
775
+ paramResult.requiredNum += result.requiredNum;
776
+ paramResult.optionalNum += result.optionalNum;
777
+ paramResult.isValid = paramResult.isValid && result.isValid;
778
+ paramResult.reason.push(...result.reason);
779
+ }
780
+ }
781
+ else {
782
+ if (isRequiredWithoutDefault && !isCopilot) {
783
+ paramResult.isValid = false;
784
+ paramResult.reason.push(ErrorType.PostBodyContainsRequiredUnsupportedSchema);
785
+ }
786
+ }
787
+ return paramResult;
788
+ }
789
+ checkParamSchema(paramObject) {
790
+ const paramResult = {
791
+ requiredNum: 0,
792
+ optionalNum: 0,
793
+ isValid: true,
794
+ reason: [],
795
+ };
796
+ if (!paramObject) {
797
+ return paramResult;
798
+ }
799
+ const isCopilot = this.projectType === ProjectType.Copilot;
800
+ for (let i = 0; i < paramObject.length; i++) {
801
+ const param = paramObject[i];
802
+ const schema = param.schema;
803
+ if (isCopilot && Utils.hasNestedObjectInSchema(schema)) {
804
+ paramResult.isValid = false;
805
+ paramResult.reason.push(ErrorType.ParamsContainsNestedObject);
806
+ continue;
807
+ }
808
+ const isRequiredWithoutDefault = param.required && schema.default === undefined;
809
+ if (isCopilot) {
810
+ if (isRequiredWithoutDefault) {
811
+ paramResult.requiredNum = paramResult.requiredNum + 1;
812
+ }
813
+ else {
814
+ paramResult.optionalNum = paramResult.optionalNum + 1;
815
+ }
816
+ continue;
817
+ }
818
+ if (param.in === "header" || param.in === "cookie") {
819
+ if (isRequiredWithoutDefault) {
820
+ paramResult.isValid = false;
821
+ paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
822
+ }
823
+ continue;
824
+ }
825
+ if (schema.type !== "boolean" &&
826
+ schema.type !== "string" &&
827
+ schema.type !== "number" &&
828
+ schema.type !== "integer") {
829
+ if (isRequiredWithoutDefault) {
830
+ paramResult.isValid = false;
831
+ paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
832
+ }
833
+ continue;
834
+ }
835
+ if (param.in === "query" || param.in === "path") {
836
+ if (isRequiredWithoutDefault) {
837
+ paramResult.requiredNum = paramResult.requiredNum + 1;
838
+ }
839
+ else {
840
+ paramResult.optionalNum = paramResult.optionalNum + 1;
841
+ }
842
+ }
843
+ }
844
+ return paramResult;
845
+ }
714
846
  }
715
847
 
716
848
  // Copyright (c) Microsoft Corporation.
@@ -761,6 +893,24 @@ class CopilotValidator extends Validator {
761
893
  // validate server
762
894
  const validateServerResult = this.validateServer(method, path);
763
895
  result.reason.push(...validateServerResult.reason);
896
+ // validate response
897
+ const validateResponseResult = this.validateResponse(method, path);
898
+ result.reason.push(...validateResponseResult.reason);
899
+ // validate requestBody
900
+ const requestBody = operationObject.requestBody;
901
+ const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
902
+ if (requestJsonBody) {
903
+ const requestBodySchema = requestJsonBody.schema;
904
+ if (!Utils.isObjectSchema(requestBodySchema)) {
905
+ result.reason.push(ErrorType.PostBodySchemaIsNotJson);
906
+ }
907
+ const requestBodyParamResult = this.checkPostBodySchema(requestBodySchema, requestBody.required);
908
+ result.reason.push(...requestBodyParamResult.reason);
909
+ }
910
+ // validate parameters
911
+ const paramObject = operationObject.parameters;
912
+ const paramResult = this.checkParamSchema(paramObject);
913
+ result.reason.push(...paramResult.reason);
764
914
  if (result.reason.length > 0) {
765
915
  result.isValid = false;
766
916
  }
@@ -852,108 +1002,6 @@ class SMEValidator extends Validator {
852
1002
  }
853
1003
  return result;
854
1004
  }
855
- validateResponse(method, path) {
856
- const result = { isValid: true, reason: [] };
857
- const operationObject = this.spec.paths[path][method];
858
- const { json, multipleMediaType } = Utils.getResponseJson(operationObject);
859
- // only support response body only contains “application/json” content type
860
- if (multipleMediaType) {
861
- result.reason.push(ErrorType.ResponseContainMultipleMediaTypes);
862
- }
863
- else if (Object.keys(json).length === 0) {
864
- // response body should not be empty
865
- result.reason.push(ErrorType.ResponseJsonIsEmpty);
866
- }
867
- return result;
868
- }
869
- checkPostBodySchema(schema, isRequired = false) {
870
- var _a;
871
- const paramResult = {
872
- requiredNum: 0,
873
- optionalNum: 0,
874
- isValid: true,
875
- reason: [],
876
- };
877
- if (Object.keys(schema).length === 0) {
878
- return paramResult;
879
- }
880
- const isRequiredWithoutDefault = isRequired && schema.default === undefined;
881
- const isCopilot = this.projectType === ProjectType.Copilot;
882
- if (schema.type === "string" ||
883
- schema.type === "integer" ||
884
- schema.type === "boolean" ||
885
- schema.type === "number") {
886
- if (isRequiredWithoutDefault) {
887
- paramResult.requiredNum = paramResult.requiredNum + 1;
888
- }
889
- else {
890
- paramResult.optionalNum = paramResult.optionalNum + 1;
891
- }
892
- }
893
- else if (Utils.isObjectSchema(schema)) {
894
- const { properties } = schema;
895
- for (const property in properties) {
896
- let isRequired = false;
897
- if (schema.required && ((_a = schema.required) === null || _a === void 0 ? void 0 : _a.indexOf(property)) >= 0) {
898
- isRequired = true;
899
- }
900
- const result = this.checkPostBodySchema(properties[property], isRequired);
901
- paramResult.requiredNum += result.requiredNum;
902
- paramResult.optionalNum += result.optionalNum;
903
- paramResult.isValid = paramResult.isValid && result.isValid;
904
- paramResult.reason.push(...result.reason);
905
- }
906
- }
907
- else {
908
- if (isRequiredWithoutDefault && !isCopilot) {
909
- paramResult.isValid = false;
910
- paramResult.reason.push(ErrorType.PostBodyContainsRequiredUnsupportedSchema);
911
- }
912
- }
913
- return paramResult;
914
- }
915
- checkParamSchema(paramObject) {
916
- const paramResult = {
917
- requiredNum: 0,
918
- optionalNum: 0,
919
- isValid: true,
920
- reason: [],
921
- };
922
- if (!paramObject) {
923
- return paramResult;
924
- }
925
- for (let i = 0; i < paramObject.length; i++) {
926
- const param = paramObject[i];
927
- const schema = param.schema;
928
- const isRequiredWithoutDefault = param.required && schema.default === undefined;
929
- if (param.in === "header" || param.in === "cookie") {
930
- if (isRequiredWithoutDefault) {
931
- paramResult.isValid = false;
932
- paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
933
- }
934
- continue;
935
- }
936
- if (schema.type !== "boolean" &&
937
- schema.type !== "string" &&
938
- schema.type !== "number" &&
939
- schema.type !== "integer") {
940
- if (isRequiredWithoutDefault) {
941
- paramResult.isValid = false;
942
- paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
943
- }
944
- continue;
945
- }
946
- if (param.in === "query" || param.in === "path") {
947
- if (isRequiredWithoutDefault) {
948
- paramResult.requiredNum = paramResult.requiredNum + 1;
949
- }
950
- else {
951
- paramResult.optionalNum = paramResult.optionalNum + 1;
952
- }
953
- }
954
- }
955
- return paramResult;
956
- }
957
1005
  validateParamCount(postBodyResult, paramResult) {
958
1006
  const result = { isValid: true, reason: [] };
959
1007
  const totalRequiredParams = postBodyResult.requiredNum + paramResult.requiredNum;