@microsoft/m365-spec-parser 0.1.1-alpha.ebe783822.0 → 0.1.1-alpha.fc0606a28.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.
@@ -23,6 +23,7 @@ var ErrorType;
23
23
  ErrorType["GenerateAdaptiveCardFailed"] = "generate-adaptive-card-failed";
24
24
  ErrorType["GenerateFailed"] = "generate-failed";
25
25
  ErrorType["ValidateFailed"] = "validate-failed";
26
+ ErrorType["GetSpecFailed"] = "get-spec-failed";
26
27
  ErrorType["Cancelled"] = "cancelled";
27
28
  ErrorType["Unknown"] = "unknown";
28
29
  })(ErrorType || (ErrorType = {}));
@@ -73,6 +74,7 @@ ConstantString.OperationOnlyContainsOptionalParam = "Operation %s contains multi
73
74
  ConstantString.ConvertSwaggerToOpenAPI = "The Swagger 2.0 file has been converted to OpenAPI 3.0.";
74
75
  ConstantString.SwaggerNotSupported = "Swagger 2.0 is not supported. Please convert to OpenAPI 3.0 manually before proceeding.";
75
76
  ConstantString.MultipleAPIKeyNotSupported = "Multiple API keys are not supported. Please make sure that all selected APIs use the same API key.";
77
+ ConstantString.UnsupportedSchema = "Unsupported schema in %s %s: %s";
76
78
  ConstantString.WrappedCardVersion = "devPreview";
77
79
  ConstantString.WrappedCardSchema = "https://developer.microsoft.com/json-schemas/teams/vDevPreview/MicrosoftTeams.ResponseRenderingTemplate.schema.json";
78
80
  ConstantString.WrappedCardResponseLayout = "list";
@@ -147,7 +149,18 @@ ConstantString.ParameterTitleMaxLens = 32;
147
149
 
148
150
  // Copyright (c) Microsoft Corporation.
149
151
  class Utils {
150
- static checkParameters(paramObject) {
152
+ static hasNestedObjectInSchema(schema) {
153
+ if (schema.type === "object") {
154
+ for (const property in schema.properties) {
155
+ const nestedSchema = schema.properties[property];
156
+ if (nestedSchema.type === "object") {
157
+ return true;
158
+ }
159
+ }
160
+ }
161
+ return false;
162
+ }
163
+ static checkParameters(paramObject, isCopilot) {
151
164
  const paramResult = {
152
165
  requiredNum: 0,
153
166
  optionalNum: 0,
@@ -159,7 +172,20 @@ class Utils {
159
172
  for (let i = 0; i < paramObject.length; i++) {
160
173
  const param = paramObject[i];
161
174
  const schema = param.schema;
175
+ if (isCopilot && this.hasNestedObjectInSchema(schema)) {
176
+ paramResult.isValid = false;
177
+ continue;
178
+ }
162
179
  const isRequiredWithoutDefault = param.required && schema.default === undefined;
180
+ if (isCopilot) {
181
+ if (isRequiredWithoutDefault) {
182
+ paramResult.requiredNum = paramResult.requiredNum + 1;
183
+ }
184
+ else {
185
+ paramResult.optionalNum = paramResult.optionalNum + 1;
186
+ }
187
+ continue;
188
+ }
163
189
  if (param.in === "header" || param.in === "cookie") {
164
190
  if (isRequiredWithoutDefault) {
165
191
  paramResult.isValid = false;
@@ -186,7 +212,7 @@ class Utils {
186
212
  }
187
213
  return paramResult;
188
214
  }
189
- static checkPostBody(schema, isRequired = false) {
215
+ static checkPostBody(schema, isRequired = false, isCopilot = false) {
190
216
  var _a;
191
217
  const paramResult = {
192
218
  requiredNum: 0,
@@ -197,6 +223,10 @@ class Utils {
197
223
  return paramResult;
198
224
  }
199
225
  const isRequiredWithoutDefault = isRequired && schema.default === undefined;
226
+ if (isCopilot && this.hasNestedObjectInSchema(schema)) {
227
+ paramResult.isValid = false;
228
+ return paramResult;
229
+ }
200
230
  if (schema.type === "string" ||
201
231
  schema.type === "integer" ||
202
232
  schema.type === "boolean" ||
@@ -215,14 +245,14 @@ class Utils {
215
245
  if (schema.required && ((_a = schema.required) === null || _a === void 0 ? void 0 : _a.indexOf(property)) >= 0) {
216
246
  isRequired = true;
217
247
  }
218
- const result = Utils.checkPostBody(properties[property], isRequired);
248
+ const result = Utils.checkPostBody(properties[property], isRequired, isCopilot);
219
249
  paramResult.requiredNum += result.requiredNum;
220
250
  paramResult.optionalNum += result.optionalNum;
221
251
  paramResult.isValid = paramResult.isValid && result.isValid;
222
252
  }
223
253
  }
224
254
  else {
225
- if (isRequiredWithoutDefault) {
255
+ if (isRequiredWithoutDefault && !isCopilot) {
226
256
  paramResult.isValid = false;
227
257
  }
228
258
  }
@@ -242,7 +272,7 @@ class Utils {
242
272
  * 5. response body should be “application/json” and not empty, and response code should be 20X
243
273
  * 6. only support request body with “application/json” content type
244
274
  */
245
- static isSupportedApi(method, path, spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2) {
275
+ static isSupportedApi(method, path, spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2, isCopilot) {
246
276
  const pathObj = spec.paths[path];
247
277
  method = method.toLocaleLowerCase();
248
278
  if (pathObj) {
@@ -275,15 +305,22 @@ class Utils {
275
305
  };
276
306
  if (requestJsonBody) {
277
307
  const requestBodySchema = requestJsonBody.schema;
278
- requestBodyParamResult = Utils.checkPostBody(requestBodySchema, requestBody.required);
308
+ if (isCopilot && requestBodySchema.type !== "object") {
309
+ return false;
310
+ }
311
+ requestBodyParamResult = Utils.checkPostBody(requestBodySchema, requestBody.required, isCopilot);
279
312
  }
280
313
  if (!requestBodyParamResult.isValid) {
281
314
  return false;
282
315
  }
283
- const paramResult = Utils.checkParameters(paramObject);
316
+ const paramResult = Utils.checkParameters(paramObject, isCopilot);
284
317
  if (!paramResult.isValid) {
285
318
  return false;
286
319
  }
320
+ // Copilot support arbitrary parameters
321
+ if (isCopilot) {
322
+ return true;
323
+ }
287
324
  if (requestBodyParamResult.requiredNum + paramResult.requiredNum > 1) {
288
325
  if (allowMultipleParameters &&
289
326
  requestBodyParamResult.requiredNum + paramResult.requiredNum <= 5) {
@@ -454,7 +491,7 @@ class Utils {
454
491
  }
455
492
  return errors;
456
493
  }
457
- static validateServer(spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2) {
494
+ static validateServer(spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2, isCopilot) {
458
495
  const errors = [];
459
496
  let hasTopLevelServers = false;
460
497
  let hasPathLevelServers = false;
@@ -475,7 +512,7 @@ class Utils {
475
512
  }
476
513
  for (const method in methods) {
477
514
  const operationObject = methods[method];
478
- if (Utils.isSupportedApi(method, path, spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2)) {
515
+ if (Utils.isSupportedApi(method, path, spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2, isCopilot)) {
479
516
  if ((operationObject === null || operationObject === void 0 ? void 0 : operationObject.servers) && operationObject.servers.length >= 1) {
480
517
  hasOperationLevelServers = true;
481
518
  const serverErrors = Utils.checkServerUrl(operationObject.servers);
@@ -625,14 +662,14 @@ class Utils {
625
662
  }
626
663
  return [command, warning];
627
664
  }
628
- static listSupportedAPIs(spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2) {
665
+ static listSupportedAPIs(spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2, isCopilot) {
629
666
  const paths = spec.paths;
630
667
  const result = {};
631
668
  for (const path in paths) {
632
669
  const methods = paths[path];
633
670
  for (const method in methods) {
634
671
  // For developer preview, only support GET operation with only 1 parameter without auth
635
- if (Utils.isSupportedApi(method, path, spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2)) {
672
+ if (Utils.isSupportedApi(method, path, spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2, isCopilot)) {
636
673
  const operationObject = methods[method];
637
674
  result[`${method.toUpperCase()} ${path}`] = operationObject;
638
675
  }
@@ -640,7 +677,7 @@ class Utils {
640
677
  }
641
678
  return result;
642
679
  }
643
- static validateSpec(spec, parser, isSwaggerFile, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2) {
680
+ static validateSpec(spec, parser, isSwaggerFile, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2, isCopilot) {
644
681
  const errors = [];
645
682
  const warnings = [];
646
683
  if (isSwaggerFile) {
@@ -650,7 +687,7 @@ class Utils {
650
687
  });
651
688
  }
652
689
  // Server validation
653
- const serverErrors = Utils.validateServer(spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2);
690
+ const serverErrors = Utils.validateServer(spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2, isCopilot);
654
691
  errors.push(...serverErrors);
655
692
  // Remote reference not supported
656
693
  const refPaths = parser.$refs.paths();
@@ -663,7 +700,7 @@ class Utils {
663
700
  });
664
701
  }
665
702
  // No supported API
666
- const apiMap = Utils.listSupportedAPIs(spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2);
703
+ const apiMap = Utils.listSupportedAPIs(spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2, isCopilot);
667
704
  if (Object.keys(apiMap).length === 0) {
668
705
  errors.push({
669
706
  type: ErrorType.NoSupportedApi,
@@ -734,6 +771,7 @@ class SpecParser {
734
771
  allowAPIKeyAuth: false,
735
772
  allowMultipleParameters: false,
736
773
  allowOauth2: false,
774
+ isCopilot: false,
737
775
  };
738
776
  this.pathOrSpec = pathOrDoc;
739
777
  this.parser = new SwaggerParser();
@@ -770,7 +808,7 @@ class SpecParser {
770
808
  ],
771
809
  };
772
810
  }
773
- return Utils.validateSpec(this.spec, this.parser, !!this.isSwaggerFile, this.options.allowMissingId, this.options.allowAPIKeyAuth, this.options.allowMultipleParameters, this.options.allowOauth2);
811
+ return Utils.validateSpec(this.spec, this.parser, !!this.isSwaggerFile, this.options.allowMissingId, this.options.allowAPIKeyAuth, this.options.allowMultipleParameters, this.options.allowOauth2, this.options.isCopilot);
774
812
  }
775
813
  catch (err) {
776
814
  throw new SpecParserError(err.toString(), ErrorType.ValidateFailed);
@@ -818,15 +856,35 @@ class SpecParser {
818
856
  async list() {
819
857
  throw new Error("Method not implemented.");
820
858
  }
859
+ /**
860
+ * Generate specs according to the filters.
861
+ * @param filter An array of strings that represent the filters to apply when generating the artifacts. If filter is empty, it would process nothing.
862
+ */
863
+ // eslint-disable-next-line @typescript-eslint/require-await
864
+ async getFilteredSpecs(filter, signal) {
865
+ throw new Error("Method not implemented.");
866
+ }
867
+ /**
868
+ * Generates and update artifacts from the OpenAPI specification file. Generate Adaptive Cards, update Teams app manifest, and generate a new OpenAPI specification file.
869
+ * @param manifestPath A file path of the Teams app manifest file to update.
870
+ * @param filter An array of strings that represent the filters to apply when generating the artifacts. If filter is empty, it would process nothing.
871
+ * @param outputSpecPath File path of the new OpenAPI specification file to generate. If not specified or empty, no spec file will be generated.
872
+ * @param pluginFilePath File path of the api plugin file to generate.
873
+ */
874
+ // eslint-disable-next-line @typescript-eslint/require-await
875
+ async generateForCopilot(manifestPath, filter, outputSpecPath, pluginFilePath, signal) {
876
+ throw new Error("Method not implemented.");
877
+ }
821
878
  /**
822
879
  * Generates and update artifacts from the OpenAPI specification file. Generate Adaptive Cards, update Teams app manifest, and generate a new OpenAPI specification file.
823
880
  * @param manifestPath A file path of the Teams app manifest file to update.
824
881
  * @param filter An array of strings that represent the filters to apply when generating the artifacts. If filter is empty, it would process nothing.
825
882
  * @param outputSpecPath File path of the new OpenAPI specification file to generate. If not specified or empty, no spec file will be generated.
826
883
  * @param adaptiveCardFolder Folder path where the Adaptive Card files will be generated. If not specified or empty, Adaptive Card files will not be generated.
884
+ * @param isMe Boolean that indicates whether the project is an Messaging Extension. For Messaging Extension, composeExtensions will be added in Teams app manifest.
827
885
  */
828
886
  // eslint-disable-next-line @typescript-eslint/require-await
829
- async generate(manifestPath, filter, outputSpecPath, adaptiveCardFolder, signal) {
887
+ async generate(manifestPath, filter, outputSpecPath, adaptiveCardFolder, signal, isMe) {
830
888
  throw new Error("Method not implemented.");
831
889
  }
832
890
  async loadSpec() {
@@ -843,11 +901,169 @@ class SpecParser {
843
901
  if (this.apiMap !== undefined) {
844
902
  return this.apiMap;
845
903
  }
846
- const result = Utils.listSupportedAPIs(spec, this.options.allowMissingId, this.options.allowAPIKeyAuth, this.options.allowMultipleParameters, this.options.allowOauth2);
904
+ const result = Utils.listSupportedAPIs(spec, this.options.allowMissingId, this.options.allowAPIKeyAuth, this.options.allowMultipleParameters, this.options.allowOauth2, this.options.isCopilot);
847
905
  this.apiMap = result;
848
906
  return result;
849
907
  }
850
908
  }
851
909
 
852
- export { ConstantString, ErrorType, SpecParser, SpecParserError, Utils, ValidationStatus, WarningType };
910
+ // Copyright (c) Microsoft Corporation.
911
+ class AdaptiveCardGenerator {
912
+ static generateAdaptiveCard(operationItem) {
913
+ try {
914
+ const json = Utils.getResponseJson(operationItem);
915
+ let cardBody = [];
916
+ let schema = json.schema;
917
+ let jsonPath = "$";
918
+ if (schema && Object.keys(schema).length > 0) {
919
+ jsonPath = AdaptiveCardGenerator.getResponseJsonPathFromSchema(schema);
920
+ if (jsonPath !== "$") {
921
+ schema = schema.properties[jsonPath];
922
+ }
923
+ cardBody = AdaptiveCardGenerator.generateCardFromResponse(schema, "");
924
+ }
925
+ // if no schema, try to use example value
926
+ if (cardBody.length === 0 && (json.examples || json.example)) {
927
+ cardBody = [
928
+ {
929
+ type: ConstantString.TextBlockType,
930
+ text: "${jsonStringify($root)}",
931
+ wrap: true,
932
+ },
933
+ ];
934
+ }
935
+ // if no example value, use default success response
936
+ if (cardBody.length === 0) {
937
+ cardBody = [
938
+ {
939
+ type: ConstantString.TextBlockType,
940
+ text: "success",
941
+ wrap: true,
942
+ },
943
+ ];
944
+ }
945
+ const fullCard = {
946
+ type: ConstantString.AdaptiveCardType,
947
+ $schema: ConstantString.AdaptiveCardSchema,
948
+ version: ConstantString.AdaptiveCardVersion,
949
+ body: cardBody,
950
+ };
951
+ return [fullCard, jsonPath];
952
+ }
953
+ catch (err) {
954
+ throw new SpecParserError(err.toString(), ErrorType.GenerateAdaptiveCardFailed);
955
+ }
956
+ }
957
+ static generateCardFromResponse(schema, name, parentArrayName = "") {
958
+ if (schema.type === "array") {
959
+ // schema.items can be arbitrary object: schema { type: array, items: {} }
960
+ if (Object.keys(schema.items).length === 0) {
961
+ return [
962
+ {
963
+ type: ConstantString.TextBlockType,
964
+ text: name ? `${name}: \${jsonStringify(${name})}` : "result: ${jsonStringify($root)}",
965
+ wrap: true,
966
+ },
967
+ ];
968
+ }
969
+ const obj = AdaptiveCardGenerator.generateCardFromResponse(schema.items, "", name);
970
+ const template = {
971
+ type: ConstantString.ContainerType,
972
+ $data: name ? `\${${name}}` : "${$root}",
973
+ items: Array(),
974
+ };
975
+ template.items.push(...obj);
976
+ return [template];
977
+ }
978
+ // some schema may not contain type but contain properties
979
+ if (schema.type === "object" || (!schema.type && schema.properties)) {
980
+ const { properties } = schema;
981
+ const result = [];
982
+ for (const property in properties) {
983
+ const obj = AdaptiveCardGenerator.generateCardFromResponse(properties[property], name ? `${name}.${property}` : property, parentArrayName);
984
+ result.push(...obj);
985
+ }
986
+ if (schema.additionalProperties) {
987
+ // TODO: better ways to handler warnings.
988
+ console.warn(ConstantString.AdditionalPropertiesNotSupported);
989
+ }
990
+ return result;
991
+ }
992
+ if (schema.type === "string" ||
993
+ schema.type === "integer" ||
994
+ schema.type === "boolean" ||
995
+ schema.type === "number") {
996
+ if (!AdaptiveCardGenerator.isImageUrlProperty(schema, name, parentArrayName)) {
997
+ // string in root: "ddd"
998
+ let text = "result: ${$root}";
999
+ if (name) {
1000
+ // object { id: "1" }
1001
+ text = `${name}: \${if(${name}, ${name}, 'N/A')}`;
1002
+ if (parentArrayName) {
1003
+ // object types inside array: { tags: ["id": 1, "name": "name"] }
1004
+ text = `${parentArrayName}.${text}`;
1005
+ }
1006
+ }
1007
+ else if (parentArrayName) {
1008
+ // string array: photoUrls: ["1", "2"]
1009
+ text = `${parentArrayName}: ` + "${$data}";
1010
+ }
1011
+ return [
1012
+ {
1013
+ type: ConstantString.TextBlockType,
1014
+ text,
1015
+ wrap: true,
1016
+ },
1017
+ ];
1018
+ }
1019
+ else {
1020
+ if (name) {
1021
+ return [
1022
+ {
1023
+ type: "Image",
1024
+ url: `\${${name}}`,
1025
+ $when: `\${${name} != null}`,
1026
+ },
1027
+ ];
1028
+ }
1029
+ else {
1030
+ return [
1031
+ {
1032
+ type: "Image",
1033
+ url: "${$data}",
1034
+ $when: "${$data != null}",
1035
+ },
1036
+ ];
1037
+ }
1038
+ }
1039
+ }
1040
+ if (schema.oneOf || schema.anyOf || schema.not || schema.allOf) {
1041
+ throw new Error(Utils.format(ConstantString.SchemaNotSupported, JSON.stringify(schema)));
1042
+ }
1043
+ throw new Error(Utils.format(ConstantString.UnknownSchema, JSON.stringify(schema)));
1044
+ }
1045
+ // Find the first array property in the response schema object with the well-known name
1046
+ static getResponseJsonPathFromSchema(schema) {
1047
+ if (schema.type === "object" || (!schema.type && schema.properties)) {
1048
+ const { properties } = schema;
1049
+ for (const property in properties) {
1050
+ const schema = properties[property];
1051
+ if (schema.type === "array" &&
1052
+ Utils.isWellKnownName(property, ConstantString.WellknownResultNames)) {
1053
+ return property;
1054
+ }
1055
+ }
1056
+ }
1057
+ return "$";
1058
+ }
1059
+ static isImageUrlProperty(schema, name, parentArrayName) {
1060
+ const propertyName = name ? name : parentArrayName;
1061
+ return (!!propertyName &&
1062
+ schema.type === "string" &&
1063
+ Utils.isWellKnownName(propertyName, ConstantString.WellknownImageName) &&
1064
+ (propertyName.toLocaleLowerCase().indexOf("url") >= 0 || schema.format === "uri"));
1065
+ }
1066
+ }
1067
+
1068
+ export { AdaptiveCardGenerator, ConstantString, ErrorType, SpecParser, SpecParserError, Utils, ValidationStatus, WarningType };
853
1069
  //# sourceMappingURL=index.esm2017.js.map