@microsoft/m365-spec-parser 0.1.1-alpha.78701ec6a.0 → 0.1.1-alpha.8d8f5a0bb.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.
@@ -53,6 +53,7 @@ var ErrorType;
53
53
  ErrorType["GenerateAdaptiveCardFailed"] = "generate-adaptive-card-failed";
54
54
  ErrorType["GenerateFailed"] = "generate-failed";
55
55
  ErrorType["ValidateFailed"] = "validate-failed";
56
+ ErrorType["GetSpecFailed"] = "get-spec-failed";
56
57
  ErrorType["Cancelled"] = "cancelled";
57
58
  ErrorType["Unknown"] = "unknown";
58
59
  })(ErrorType || (ErrorType = {}));
@@ -103,6 +104,7 @@ ConstantString.OperationOnlyContainsOptionalParam = "Operation %s contains multi
103
104
  ConstantString.ConvertSwaggerToOpenAPI = "The Swagger 2.0 file has been converted to OpenAPI 3.0.";
104
105
  ConstantString.SwaggerNotSupported = "Swagger 2.0 is not supported. Please convert to OpenAPI 3.0 manually before proceeding.";
105
106
  ConstantString.MultipleAPIKeyNotSupported = "Multiple API keys are not supported. Please make sure that all selected APIs use the same API key.";
107
+ ConstantString.UnsupportedSchema = "Unsupported schema in %s %s: %s";
106
108
  ConstantString.WrappedCardVersion = "devPreview";
107
109
  ConstantString.WrappedCardSchema = "https://developer.microsoft.com/json-schemas/teams/vDevPreview/MicrosoftTeams.ResponseRenderingTemplate.schema.json";
108
110
  ConstantString.WrappedCardResponseLayout = "list";
@@ -177,7 +179,18 @@ ConstantString.ParameterTitleMaxLens = 32;
177
179
 
178
180
  // Copyright (c) Microsoft Corporation.
179
181
  class Utils {
180
- static checkParameters(paramObject) {
182
+ static hasNestedObjectInSchema(schema) {
183
+ if (schema.type === "object") {
184
+ for (const property in schema.properties) {
185
+ const nestedSchema = schema.properties[property];
186
+ if (nestedSchema.type === "object") {
187
+ return true;
188
+ }
189
+ }
190
+ }
191
+ return false;
192
+ }
193
+ static checkParameters(paramObject, isCopilot) {
181
194
  const paramResult = {
182
195
  requiredNum: 0,
183
196
  optionalNum: 0,
@@ -189,7 +202,20 @@ class Utils {
189
202
  for (let i = 0; i < paramObject.length; i++) {
190
203
  const param = paramObject[i];
191
204
  const schema = param.schema;
205
+ if (isCopilot && this.hasNestedObjectInSchema(schema)) {
206
+ paramResult.isValid = false;
207
+ continue;
208
+ }
192
209
  const isRequiredWithoutDefault = param.required && schema.default === undefined;
210
+ if (isCopilot) {
211
+ if (isRequiredWithoutDefault) {
212
+ paramResult.requiredNum = paramResult.requiredNum + 1;
213
+ }
214
+ else {
215
+ paramResult.optionalNum = paramResult.optionalNum + 1;
216
+ }
217
+ continue;
218
+ }
193
219
  if (param.in === "header" || param.in === "cookie") {
194
220
  if (isRequiredWithoutDefault) {
195
221
  paramResult.isValid = false;
@@ -216,7 +242,7 @@ class Utils {
216
242
  }
217
243
  return paramResult;
218
244
  }
219
- static checkPostBody(schema, isRequired = false) {
245
+ static checkPostBody(schema, isRequired = false, isCopilot = false) {
220
246
  var _a;
221
247
  const paramResult = {
222
248
  requiredNum: 0,
@@ -227,6 +253,10 @@ class Utils {
227
253
  return paramResult;
228
254
  }
229
255
  const isRequiredWithoutDefault = isRequired && schema.default === undefined;
256
+ if (isCopilot && this.hasNestedObjectInSchema(schema)) {
257
+ paramResult.isValid = false;
258
+ return paramResult;
259
+ }
230
260
  if (schema.type === "string" ||
231
261
  schema.type === "integer" ||
232
262
  schema.type === "boolean" ||
@@ -245,14 +275,14 @@ class Utils {
245
275
  if (schema.required && ((_a = schema.required) === null || _a === void 0 ? void 0 : _a.indexOf(property)) >= 0) {
246
276
  isRequired = true;
247
277
  }
248
- const result = Utils.checkPostBody(properties[property], isRequired);
278
+ const result = Utils.checkPostBody(properties[property], isRequired, isCopilot);
249
279
  paramResult.requiredNum += result.requiredNum;
250
280
  paramResult.optionalNum += result.optionalNum;
251
281
  paramResult.isValid = paramResult.isValid && result.isValid;
252
282
  }
253
283
  }
254
284
  else {
255
- if (isRequiredWithoutDefault) {
285
+ if (isRequiredWithoutDefault && !isCopilot) {
256
286
  paramResult.isValid = false;
257
287
  }
258
288
  }
@@ -272,7 +302,7 @@ class Utils {
272
302
  * 5. response body should be “application/json” and not empty, and response code should be 20X
273
303
  * 6. only support request body with “application/json” content type
274
304
  */
275
- static isSupportedApi(method, path, spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2) {
305
+ static isSupportedApi(method, path, spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2, isCopilot) {
276
306
  const pathObj = spec.paths[path];
277
307
  method = method.toLocaleLowerCase();
278
308
  if (pathObj) {
@@ -305,15 +335,22 @@ class Utils {
305
335
  };
306
336
  if (requestJsonBody) {
307
337
  const requestBodySchema = requestJsonBody.schema;
308
- requestBodyParamResult = Utils.checkPostBody(requestBodySchema, requestBody.required);
338
+ if (isCopilot && requestBodySchema.type !== "object") {
339
+ return false;
340
+ }
341
+ requestBodyParamResult = Utils.checkPostBody(requestBodySchema, requestBody.required, isCopilot);
309
342
  }
310
343
  if (!requestBodyParamResult.isValid) {
311
344
  return false;
312
345
  }
313
- const paramResult = Utils.checkParameters(paramObject);
346
+ const paramResult = Utils.checkParameters(paramObject, isCopilot);
314
347
  if (!paramResult.isValid) {
315
348
  return false;
316
349
  }
350
+ // Copilot support arbitrary parameters
351
+ if (isCopilot) {
352
+ return true;
353
+ }
317
354
  if (requestBodyParamResult.requiredNum + paramResult.requiredNum > 1) {
318
355
  if (allowMultipleParameters &&
319
356
  requestBodyParamResult.requiredNum + paramResult.requiredNum <= 5) {
@@ -484,7 +521,7 @@ class Utils {
484
521
  }
485
522
  return errors;
486
523
  }
487
- static validateServer(spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2) {
524
+ static validateServer(spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2, isCopilot) {
488
525
  const errors = [];
489
526
  let hasTopLevelServers = false;
490
527
  let hasPathLevelServers = false;
@@ -505,7 +542,7 @@ class Utils {
505
542
  }
506
543
  for (const method in methods) {
507
544
  const operationObject = methods[method];
508
- if (Utils.isSupportedApi(method, path, spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2)) {
545
+ if (Utils.isSupportedApi(method, path, spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2, isCopilot)) {
509
546
  if ((operationObject === null || operationObject === void 0 ? void 0 : operationObject.servers) && operationObject.servers.length >= 1) {
510
547
  hasOperationLevelServers = true;
511
548
  const serverErrors = Utils.checkServerUrl(operationObject.servers);
@@ -655,14 +692,14 @@ class Utils {
655
692
  }
656
693
  return [command, warning];
657
694
  }
658
- static listSupportedAPIs(spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2) {
695
+ static listSupportedAPIs(spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2, isCopilot) {
659
696
  const paths = spec.paths;
660
697
  const result = {};
661
698
  for (const path in paths) {
662
699
  const methods = paths[path];
663
700
  for (const method in methods) {
664
701
  // For developer preview, only support GET operation with only 1 parameter without auth
665
- if (Utils.isSupportedApi(method, path, spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2)) {
702
+ if (Utils.isSupportedApi(method, path, spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2, isCopilot)) {
666
703
  const operationObject = methods[method];
667
704
  result[`${method.toUpperCase()} ${path}`] = operationObject;
668
705
  }
@@ -670,7 +707,7 @@ class Utils {
670
707
  }
671
708
  return result;
672
709
  }
673
- static validateSpec(spec, parser, isSwaggerFile, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2) {
710
+ static validateSpec(spec, parser, isSwaggerFile, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2, isCopilot) {
674
711
  const errors = [];
675
712
  const warnings = [];
676
713
  if (isSwaggerFile) {
@@ -680,7 +717,7 @@ class Utils {
680
717
  });
681
718
  }
682
719
  // Server validation
683
- const serverErrors = Utils.validateServer(spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2);
720
+ const serverErrors = Utils.validateServer(spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2, isCopilot);
684
721
  errors.push(...serverErrors);
685
722
  // Remote reference not supported
686
723
  const refPaths = parser.$refs.paths();
@@ -693,7 +730,7 @@ class Utils {
693
730
  });
694
731
  }
695
732
  // No supported API
696
- const apiMap = Utils.listSupportedAPIs(spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2);
733
+ const apiMap = Utils.listSupportedAPIs(spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2, isCopilot);
697
734
  if (Object.keys(apiMap).length === 0) {
698
735
  errors.push({
699
736
  type: ErrorType.NoSupportedApi,
@@ -764,6 +801,7 @@ class SpecParser {
764
801
  allowAPIKeyAuth: false,
765
802
  allowMultipleParameters: false,
766
803
  allowOauth2: false,
804
+ isCopilot: false,
767
805
  };
768
806
  this.pathOrSpec = pathOrDoc;
769
807
  this.parser = new SwaggerParser();
@@ -801,7 +839,7 @@ class SpecParser {
801
839
  ],
802
840
  };
803
841
  }
804
- return Utils.validateSpec(this.spec, this.parser, !!this.isSwaggerFile, this.options.allowMissingId, this.options.allowAPIKeyAuth, this.options.allowMultipleParameters, this.options.allowOauth2);
842
+ return Utils.validateSpec(this.spec, this.parser, !!this.isSwaggerFile, this.options.allowMissingId, this.options.allowAPIKeyAuth, this.options.allowMultipleParameters, this.options.allowOauth2, this.options.isCopilot);
805
843
  }
806
844
  catch (err) {
807
845
  throw new SpecParserError(err.toString(), ErrorType.ValidateFailed);
@@ -854,15 +892,39 @@ class SpecParser {
854
892
  throw new Error("Method not implemented.");
855
893
  });
856
894
  }
895
+ /**
896
+ * Generate specs according to the filters.
897
+ * @param filter An array of strings that represent the filters to apply when generating the artifacts. If filter is empty, it would process nothing.
898
+ */
899
+ // eslint-disable-next-line @typescript-eslint/require-await
900
+ getFilteredSpecs(filter, signal) {
901
+ return __awaiter(this, void 0, void 0, function* () {
902
+ throw new Error("Method not implemented.");
903
+ });
904
+ }
905
+ /**
906
+ * Generates and update artifacts from the OpenAPI specification file. Generate Adaptive Cards, update Teams app manifest, and generate a new OpenAPI specification file.
907
+ * @param manifestPath A file path of the Teams app manifest file to update.
908
+ * @param filter An array of strings that represent the filters to apply when generating the artifacts. If filter is empty, it would process nothing.
909
+ * @param outputSpecPath File path of the new OpenAPI specification file to generate. If not specified or empty, no spec file will be generated.
910
+ * @param pluginFilePath File path of the api plugin file to generate.
911
+ */
912
+ // eslint-disable-next-line @typescript-eslint/require-await
913
+ generateForCopilot(manifestPath, filter, outputSpecPath, pluginFilePath, signal) {
914
+ return __awaiter(this, void 0, void 0, function* () {
915
+ throw new Error("Method not implemented.");
916
+ });
917
+ }
857
918
  /**
858
919
  * Generates and update artifacts from the OpenAPI specification file. Generate Adaptive Cards, update Teams app manifest, and generate a new OpenAPI specification file.
859
920
  * @param manifestPath A file path of the Teams app manifest file to update.
860
921
  * @param filter An array of strings that represent the filters to apply when generating the artifacts. If filter is empty, it would process nothing.
861
922
  * @param outputSpecPath File path of the new OpenAPI specification file to generate. If not specified or empty, no spec file will be generated.
862
923
  * @param adaptiveCardFolder Folder path where the Adaptive Card files will be generated. If not specified or empty, Adaptive Card files will not be generated.
924
+ * @param isMe Boolean that indicates whether the project is an Messaging Extension. For Messaging Extension, composeExtensions will be added in Teams app manifest.
863
925
  */
864
926
  // eslint-disable-next-line @typescript-eslint/require-await
865
- generate(manifestPath, filter, outputSpecPath, adaptiveCardFolder, signal) {
927
+ generate(manifestPath, filter, outputSpecPath, adaptiveCardFolder, signal, isMe) {
866
928
  return __awaiter(this, void 0, void 0, function* () {
867
929
  throw new Error("Method not implemented.");
868
930
  });
@@ -883,11 +945,169 @@ class SpecParser {
883
945
  if (this.apiMap !== undefined) {
884
946
  return this.apiMap;
885
947
  }
886
- const result = Utils.listSupportedAPIs(spec, this.options.allowMissingId, this.options.allowAPIKeyAuth, this.options.allowMultipleParameters, this.options.allowOauth2);
948
+ const result = Utils.listSupportedAPIs(spec, this.options.allowMissingId, this.options.allowAPIKeyAuth, this.options.allowMultipleParameters, this.options.allowOauth2, this.options.isCopilot);
887
949
  this.apiMap = result;
888
950
  return result;
889
951
  }
890
952
  }
891
953
 
892
- export { ConstantString, ErrorType, SpecParser, SpecParserError, Utils, ValidationStatus, WarningType };
954
+ // Copyright (c) Microsoft Corporation.
955
+ class AdaptiveCardGenerator {
956
+ static generateAdaptiveCard(operationItem) {
957
+ try {
958
+ const json = Utils.getResponseJson(operationItem);
959
+ let cardBody = [];
960
+ let schema = json.schema;
961
+ let jsonPath = "$";
962
+ if (schema && Object.keys(schema).length > 0) {
963
+ jsonPath = AdaptiveCardGenerator.getResponseJsonPathFromSchema(schema);
964
+ if (jsonPath !== "$") {
965
+ schema = schema.properties[jsonPath];
966
+ }
967
+ cardBody = AdaptiveCardGenerator.generateCardFromResponse(schema, "");
968
+ }
969
+ // if no schema, try to use example value
970
+ if (cardBody.length === 0 && (json.examples || json.example)) {
971
+ cardBody = [
972
+ {
973
+ type: ConstantString.TextBlockType,
974
+ text: "${jsonStringify($root)}",
975
+ wrap: true,
976
+ },
977
+ ];
978
+ }
979
+ // if no example value, use default success response
980
+ if (cardBody.length === 0) {
981
+ cardBody = [
982
+ {
983
+ type: ConstantString.TextBlockType,
984
+ text: "success",
985
+ wrap: true,
986
+ },
987
+ ];
988
+ }
989
+ const fullCard = {
990
+ type: ConstantString.AdaptiveCardType,
991
+ $schema: ConstantString.AdaptiveCardSchema,
992
+ version: ConstantString.AdaptiveCardVersion,
993
+ body: cardBody,
994
+ };
995
+ return [fullCard, jsonPath];
996
+ }
997
+ catch (err) {
998
+ throw new SpecParserError(err.toString(), ErrorType.GenerateAdaptiveCardFailed);
999
+ }
1000
+ }
1001
+ static generateCardFromResponse(schema, name, parentArrayName = "") {
1002
+ if (schema.type === "array") {
1003
+ // schema.items can be arbitrary object: schema { type: array, items: {} }
1004
+ if (Object.keys(schema.items).length === 0) {
1005
+ return [
1006
+ {
1007
+ type: ConstantString.TextBlockType,
1008
+ text: name ? `${name}: \${jsonStringify(${name})}` : "result: ${jsonStringify($root)}",
1009
+ wrap: true,
1010
+ },
1011
+ ];
1012
+ }
1013
+ const obj = AdaptiveCardGenerator.generateCardFromResponse(schema.items, "", name);
1014
+ const template = {
1015
+ type: ConstantString.ContainerType,
1016
+ $data: name ? `\${${name}}` : "${$root}",
1017
+ items: Array(),
1018
+ };
1019
+ template.items.push(...obj);
1020
+ return [template];
1021
+ }
1022
+ // some schema may not contain type but contain properties
1023
+ if (schema.type === "object" || (!schema.type && schema.properties)) {
1024
+ const { properties } = schema;
1025
+ const result = [];
1026
+ for (const property in properties) {
1027
+ const obj = AdaptiveCardGenerator.generateCardFromResponse(properties[property], name ? `${name}.${property}` : property, parentArrayName);
1028
+ result.push(...obj);
1029
+ }
1030
+ if (schema.additionalProperties) {
1031
+ // TODO: better ways to handler warnings.
1032
+ console.warn(ConstantString.AdditionalPropertiesNotSupported);
1033
+ }
1034
+ return result;
1035
+ }
1036
+ if (schema.type === "string" ||
1037
+ schema.type === "integer" ||
1038
+ schema.type === "boolean" ||
1039
+ schema.type === "number") {
1040
+ if (!AdaptiveCardGenerator.isImageUrlProperty(schema, name, parentArrayName)) {
1041
+ // string in root: "ddd"
1042
+ let text = "result: ${$root}";
1043
+ if (name) {
1044
+ // object { id: "1" }
1045
+ text = `${name}: \${if(${name}, ${name}, 'N/A')}`;
1046
+ if (parentArrayName) {
1047
+ // object types inside array: { tags: ["id": 1, "name": "name"] }
1048
+ text = `${parentArrayName}.${text}`;
1049
+ }
1050
+ }
1051
+ else if (parentArrayName) {
1052
+ // string array: photoUrls: ["1", "2"]
1053
+ text = `${parentArrayName}: ` + "${$data}";
1054
+ }
1055
+ return [
1056
+ {
1057
+ type: ConstantString.TextBlockType,
1058
+ text,
1059
+ wrap: true,
1060
+ },
1061
+ ];
1062
+ }
1063
+ else {
1064
+ if (name) {
1065
+ return [
1066
+ {
1067
+ type: "Image",
1068
+ url: `\${${name}}`,
1069
+ $when: `\${${name} != null}`,
1070
+ },
1071
+ ];
1072
+ }
1073
+ else {
1074
+ return [
1075
+ {
1076
+ type: "Image",
1077
+ url: "${$data}",
1078
+ $when: "${$data != null}",
1079
+ },
1080
+ ];
1081
+ }
1082
+ }
1083
+ }
1084
+ if (schema.oneOf || schema.anyOf || schema.not || schema.allOf) {
1085
+ throw new Error(Utils.format(ConstantString.SchemaNotSupported, JSON.stringify(schema)));
1086
+ }
1087
+ throw new Error(Utils.format(ConstantString.UnknownSchema, JSON.stringify(schema)));
1088
+ }
1089
+ // Find the first array property in the response schema object with the well-known name
1090
+ static getResponseJsonPathFromSchema(schema) {
1091
+ if (schema.type === "object" || (!schema.type && schema.properties)) {
1092
+ const { properties } = schema;
1093
+ for (const property in properties) {
1094
+ const schema = properties[property];
1095
+ if (schema.type === "array" &&
1096
+ Utils.isWellKnownName(property, ConstantString.WellknownResultNames)) {
1097
+ return property;
1098
+ }
1099
+ }
1100
+ }
1101
+ return "$";
1102
+ }
1103
+ static isImageUrlProperty(schema, name, parentArrayName) {
1104
+ const propertyName = name ? name : parentArrayName;
1105
+ return (!!propertyName &&
1106
+ schema.type === "string" &&
1107
+ Utils.isWellKnownName(propertyName, ConstantString.WellknownImageName) &&
1108
+ (propertyName.toLocaleLowerCase().indexOf("url") >= 0 || schema.format === "uri"));
1109
+ }
1110
+ }
1111
+
1112
+ export { AdaptiveCardGenerator, ConstantString, ErrorType, SpecParser, SpecParserError, Utils, ValidationStatus, WarningType };
893
1113
  //# sourceMappingURL=index.esm5.js.map