@microsoft/m365-spec-parser 0.2.2-alpha.a0aeb8641.0 → 0.2.2-alpha.ab66695dd.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.
@@ -7,6 +7,7 @@ var converter = require('swagger2openapi');
7
7
  var jsyaml = require('js-yaml');
8
8
  var fs = require('fs-extra');
9
9
  var path = require('path');
10
+ var crypto = require('crypto');
10
11
 
11
12
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
12
13
 
@@ -100,6 +101,7 @@ exports.WarningType = void 0;
100
101
  WarningType["GenerateCardFailed"] = "generate-card-failed";
101
102
  WarningType["OperationOnlyContainsOptionalParam"] = "operation-only-contains-optional-param";
102
103
  WarningType["ConvertSwaggerToOpenAPI"] = "convert-swagger-to-openapi";
104
+ WarningType["FuncDescriptionTooLong"] = "function-description-too-long";
103
105
  WarningType["Unknown"] = "unknown";
104
106
  })(exports.WarningType || (exports.WarningType = {}));
105
107
  /**
@@ -138,6 +140,7 @@ ConstantString.SwaggerNotSupported = "Swagger 2.0 is not supported. Please conve
138
140
  ConstantString.SpecVersionNotSupported = "Unsupported OpenAPI version %s. Please use version 3.0.x.";
139
141
  ConstantString.MultipleAuthNotSupported = "Multiple authentication methods are unsupported. Ensure all selected APIs use identical authentication.";
140
142
  ConstantString.UnsupportedSchema = "Unsupported schema in %s %s: %s";
143
+ 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.";
141
144
  ConstantString.WrappedCardVersion = "devPreview";
142
145
  ConstantString.WrappedCardSchema = "https://developer.microsoft.com/json-schemas/teams/vDevPreview/MicrosoftTeams.ResponseRenderingTemplate.schema.json";
143
146
  ConstantString.WrappedCardResponseLayout = "list";
@@ -217,8 +220,9 @@ ConstantString.ConversationStarterMaxLens = 50;
217
220
  ConstantString.CommandTitleMaxLens = 32;
218
221
  ConstantString.ParameterTitleMaxLens = 32;
219
222
  ConstantString.SMERequiredParamsMaxNum = 5;
223
+ ConstantString.FunctionDescriptionMaxLens = 100;
220
224
  ConstantString.DefaultPluginId = "plugin_1";
221
- ConstantString.PluginManifestSchema = "https://aka.ms/json-schemas/copilot-extensions/v2.1/plugin.schema.json";
225
+ ConstantString.PluginManifestSchema = "https://developer.microsoft.com/json-schemas/copilot/plugin/v2.1/schema.json";
222
226
 
223
227
  // Copyright (c) Microsoft Corporation.
224
228
  class SpecParserError extends Error {
@@ -231,16 +235,19 @@ class SpecParserError extends Error {
231
235
  // Copyright (c) Microsoft Corporation.
232
236
  class Utils {
233
237
  static hasNestedObjectInSchema(schema) {
234
- if (schema.type === "object") {
238
+ if (this.isObjectSchema(schema)) {
235
239
  for (const property in schema.properties) {
236
240
  const nestedSchema = schema.properties[property];
237
- if (nestedSchema.type === "object") {
241
+ if (this.isObjectSchema(nestedSchema)) {
238
242
  return true;
239
243
  }
240
244
  }
241
245
  }
242
246
  return false;
243
247
  }
248
+ static isObjectSchema(schema) {
249
+ return schema.type === "object" || (!schema.type && !!schema.properties);
250
+ }
244
251
  static containMultipleMediaTypes(bodyObject) {
245
252
  return Object.keys((bodyObject === null || bodyObject === void 0 ? void 0 : bodyObject.content) || {}).length > 1;
246
253
  }
@@ -469,7 +476,7 @@ class Utils {
469
476
  optionalParams.push(parameter);
470
477
  }
471
478
  }
472
- else if (schema.type === "object") {
479
+ else if (Utils.isObjectSchema(schema)) {
473
480
  const { properties } = schema;
474
481
  for (const property in properties) {
475
482
  let isRequired = false;
@@ -807,7 +814,7 @@ class Validator {
807
814
  }
808
815
  const isRequiredWithoutDefault = isRequired && schema.default === undefined;
809
816
  const isCopilot = this.projectType === exports.ProjectType.Copilot;
810
- if (isCopilot && this.hasNestedObjectInSchema(schema)) {
817
+ if (isCopilot && Utils.hasNestedObjectInSchema(schema)) {
811
818
  paramResult.isValid = false;
812
819
  paramResult.reason = [exports.ErrorType.RequestBodyContainsNestedObject];
813
820
  return paramResult;
@@ -823,7 +830,7 @@ class Validator {
823
830
  paramResult.optionalNum = paramResult.optionalNum + 1;
824
831
  }
825
832
  }
826
- else if (schema.type === "object") {
833
+ else if (Utils.isObjectSchema(schema)) {
827
834
  const { properties } = schema;
828
835
  for (const property in properties) {
829
836
  let isRequired = false;
@@ -859,7 +866,7 @@ class Validator {
859
866
  for (let i = 0; i < paramObject.length; i++) {
860
867
  const param = paramObject[i];
861
868
  const schema = param.schema;
862
- if (isCopilot && this.hasNestedObjectInSchema(schema)) {
869
+ if (isCopilot && Utils.hasNestedObjectInSchema(schema)) {
863
870
  paramResult.isValid = false;
864
871
  paramResult.reason.push(exports.ErrorType.ParamsContainsNestedObject);
865
872
  continue;
@@ -902,17 +909,6 @@ class Validator {
902
909
  }
903
910
  return paramResult;
904
911
  }
905
- hasNestedObjectInSchema(schema) {
906
- if (schema.type === "object") {
907
- for (const property in schema.properties) {
908
- const nestedSchema = schema.properties[property];
909
- if (nestedSchema.type === "object") {
910
- return true;
911
- }
912
- }
913
- }
914
- return false;
915
- }
916
912
  }
917
913
 
918
914
  // Copyright (c) Microsoft Corporation.
@@ -971,7 +967,7 @@ class CopilotValidator extends Validator {
971
967
  const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
972
968
  if (requestJsonBody) {
973
969
  const requestBodySchema = requestJsonBody.schema;
974
- if (requestBodySchema.type !== "object") {
970
+ if (!Utils.isObjectSchema(requestBodySchema)) {
975
971
  result.reason.push(exports.ErrorType.PostBodySchemaIsNotJson);
976
972
  }
977
973
  const requestBodyParamResult = this.checkPostBodySchema(requestBodySchema, requestBody.required);
@@ -1418,7 +1414,7 @@ class AdaptiveCardGenerator {
1418
1414
  return [template];
1419
1415
  }
1420
1416
  // some schema may not contain type but contain properties
1421
- if (schema.type === "object" || (!schema.type && schema.properties)) {
1417
+ if (Utils.isObjectSchema(schema)) {
1422
1418
  const { properties } = schema;
1423
1419
  const result = [];
1424
1420
  for (const property in properties) {
@@ -1486,7 +1482,7 @@ class AdaptiveCardGenerator {
1486
1482
  }
1487
1483
  // Find the first array property in the response schema object with the well-known name
1488
1484
  static getResponseJsonPathFromSchema(schema) {
1489
- if (schema.type === "object" || (!schema.type && schema.properties)) {
1485
+ if (Utils.isObjectSchema(schema)) {
1490
1486
  const { properties } = schema;
1491
1487
  for (const property in properties) {
1492
1488
  const schema = properties[property];
@@ -1638,7 +1634,7 @@ function inferProperties(card) {
1638
1634
 
1639
1635
  // Copyright (c) Microsoft Corporation.
1640
1636
  class ManifestUpdater {
1641
- static updateManifestWithAiPlugin(manifestPath, outputSpecPath, apiPluginFilePath, spec, options, authInfo) {
1637
+ static updateManifestWithAiPlugin(manifestPath, outputSpecPath, apiPluginFilePath, spec, options, authInfo, existingPluginManifestInfo) {
1642
1638
  return __awaiter(this, void 0, void 0, function* () {
1643
1639
  const manifest = yield fs__default['default'].readJSON(manifestPath);
1644
1640
  const apiPluginRelativePath = ManifestUpdater.getRelativePath(manifestPath, apiPluginFilePath);
@@ -1655,7 +1651,7 @@ class ManifestUpdater {
1655
1651
  }
1656
1652
  const appName = this.removeEnvs(manifest.name.short);
1657
1653
  const specRelativePath = ManifestUpdater.getRelativePath(manifestPath, outputSpecPath);
1658
- const [apiPlugin, warnings] = yield ManifestUpdater.generatePluginManifestSchema(spec, specRelativePath, apiPluginFilePath, appName, authInfo, options);
1654
+ const [apiPlugin, warnings] = yield ManifestUpdater.generatePluginManifestSchema(spec, specRelativePath, apiPluginFilePath, appName, authInfo, options, existingPluginManifestInfo);
1659
1655
  return [manifest, apiPlugin, warnings];
1660
1656
  });
1661
1657
  }
@@ -1678,7 +1674,7 @@ class ManifestUpdater {
1678
1674
  throw new SpecParserError(Utils.format(ConstantString.UnsupportedSchema, method, pathUrl, JSON.stringify(schema)), exports.ErrorType.UpdateManifestFailed);
1679
1675
  }
1680
1676
  }
1681
- static generatePluginManifestSchema(spec, specRelativePath, apiPluginFilePath, appName, authInfo, options) {
1677
+ static generatePluginManifestSchema(spec, specRelativePath, apiPluginFilePath, appName, authInfo, options, existingPluginManifestInfo) {
1682
1678
  var _a, _b, _c, _d;
1683
1679
  return __awaiter(this, void 0, void 0, function* () {
1684
1680
  const warnings = [];
@@ -1727,7 +1723,7 @@ class ManifestUpdater {
1727
1723
  if (requestBody) {
1728
1724
  const requestJsonBody = requestBody.content["application/json"];
1729
1725
  const requestBodySchema = requestJsonBody.schema;
1730
- if (requestBodySchema.type === "object") {
1726
+ if (Utils.isObjectSchema(requestBodySchema)) {
1731
1727
  for (const property in requestBodySchema.properties) {
1732
1728
  const schema = requestBodySchema.properties[property];
1733
1729
  ManifestUpdater.checkSchema(schema, method, pathUrl);
@@ -1738,9 +1734,18 @@ class ManifestUpdater {
1738
1734
  throw new SpecParserError(Utils.format(ConstantString.UnsupportedSchema, method, pathUrl, JSON.stringify(requestBodySchema)), exports.ErrorType.UpdateManifestFailed);
1739
1735
  }
1740
1736
  }
1737
+ let funcDescription = operationItem.description || operationItem.summary || "";
1738
+ if (funcDescription.length > ConstantString.FunctionDescriptionMaxLens) {
1739
+ warnings.push({
1740
+ type: exports.WarningType.FuncDescriptionTooLong,
1741
+ content: Utils.format(ConstantString.FuncDescriptionTooLong, safeFunctionName, funcDescription.length.toString(), ConstantString.FunctionDescriptionMaxLens.toString()),
1742
+ data: safeFunctionName,
1743
+ });
1744
+ funcDescription = funcDescription.slice(0, ConstantString.FunctionDescriptionMaxLens);
1745
+ }
1741
1746
  const funcObj = {
1742
1747
  name: safeFunctionName,
1743
- description: description,
1748
+ description: funcDescription,
1744
1749
  };
1745
1750
  if (options.allowResponseSemantics) {
1746
1751
  try {
@@ -1789,6 +1794,10 @@ class ManifestUpdater {
1789
1794
  if (yield fs__default['default'].pathExists(apiPluginFilePath)) {
1790
1795
  apiPlugin = yield fs__default['default'].readJSON(apiPluginFilePath);
1791
1796
  }
1797
+ else if (existingPluginManifestInfo &&
1798
+ (yield fs__default['default'].pathExists(existingPluginManifestInfo.manifestPath))) {
1799
+ apiPlugin = yield fs__default['default'].readJSON(existingPluginManifestInfo.manifestPath);
1800
+ }
1792
1801
  else {
1793
1802
  apiPlugin = {
1794
1803
  $schema: ConstantString.PluginManifestSchema,
@@ -1811,6 +1820,11 @@ class ManifestUpdater {
1811
1820
  }
1812
1821
  }
1813
1822
  apiPlugin.runtimes = apiPlugin.runtimes || [];
1823
+ // Need to delete previous runtime since spec path has changed
1824
+ if (existingPluginManifestInfo) {
1825
+ const relativePath = ManifestUpdater.getRelativePath(existingPluginManifestInfo.manifestPath, existingPluginManifestInfo.specPath);
1826
+ apiPlugin.runtimes = apiPlugin.runtimes.filter((runtime) => runtime.spec.url !== relativePath);
1827
+ }
1814
1828
  const index = apiPlugin.runtimes.findIndex((runtime) => {
1815
1829
  var _a, _b;
1816
1830
  return runtime.spec.url === specRelativePath &&
@@ -2013,6 +2027,7 @@ class SpecParser {
2013
2027
  validate() {
2014
2028
  return __awaiter(this, void 0, void 0, function* () {
2015
2029
  try {
2030
+ let hash = "";
2016
2031
  try {
2017
2032
  yield this.loadSpec();
2018
2033
  if (!this.parser.$refs.circular) {
@@ -2028,8 +2043,13 @@ class SpecParser {
2028
2043
  status: exports.ValidationStatus.Error,
2029
2044
  warnings: [],
2030
2045
  errors: [{ type: exports.ErrorType.SpecNotValid, content: e.toString() }],
2046
+ specHash: hash,
2031
2047
  };
2032
2048
  }
2049
+ if (this.unResolveSpec.servers) {
2050
+ const serverString = JSON.stringify(this.unResolveSpec.servers);
2051
+ hash = crypto.createHash("sha256").update(serverString).digest("hex");
2052
+ }
2033
2053
  const errors = [];
2034
2054
  const warnings = [];
2035
2055
  if (!this.options.allowSwagger && this.isSwaggerFile) {
@@ -2039,6 +2059,7 @@ class SpecParser {
2039
2059
  errors: [
2040
2060
  { type: exports.ErrorType.SwaggerNotSupported, content: ConstantString.SwaggerNotSupported },
2041
2061
  ],
2062
+ specHash: hash,
2042
2063
  };
2043
2064
  }
2044
2065
  // Remote reference not supported
@@ -2072,6 +2093,7 @@ class SpecParser {
2072
2093
  status: status,
2073
2094
  warnings: warnings,
2074
2095
  errors: errors,
2096
+ specHash: hash,
2075
2097
  };
2076
2098
  }
2077
2099
  catch (err) {
@@ -2194,7 +2216,7 @@ class SpecParser {
2194
2216
  * @param outputSpecPath File path of the new OpenAPI specification file to generate. If not specified or empty, no spec file will be generated.
2195
2217
  * @param pluginFilePath File path of the api plugin file to generate.
2196
2218
  */
2197
- generateForCopilot(manifestPath, filter, outputSpecPath, pluginFilePath, signal) {
2219
+ generateForCopilot(manifestPath, filter, outputSpecPath, pluginFilePath, existingPluginFilePath, signal) {
2198
2220
  return __awaiter(this, void 0, void 0, function* () {
2199
2221
  const result = {
2200
2222
  allSuccess: true,
@@ -2209,7 +2231,13 @@ class SpecParser {
2209
2231
  if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
2210
2232
  throw new SpecParserError(ConstantString.CancelledMessage, exports.ErrorType.Cancelled);
2211
2233
  }
2212
- const [updatedManifest, apiPlugin, warnings] = yield ManifestUpdater.updateManifestWithAiPlugin(manifestPath, outputSpecPath, pluginFilePath, newSpec, this.options, authInfo);
2234
+ const existingPluginManifestInfo = existingPluginFilePath
2235
+ ? {
2236
+ manifestPath: existingPluginFilePath,
2237
+ specPath: this.pathOrSpec,
2238
+ }
2239
+ : undefined;
2240
+ const [updatedManifest, apiPlugin, warnings] = yield ManifestUpdater.updateManifestWithAiPlugin(manifestPath, outputSpecPath, pluginFilePath, newSpec, this.options, authInfo, existingPluginManifestInfo);
2213
2241
  result.warnings.push(...warnings);
2214
2242
  yield fs__default['default'].outputJSON(manifestPath, updatedManifest, { spaces: 4 });
2215
2243
  yield fs__default['default'].outputJSON(pluginFilePath, apiPlugin, { spaces: 4 });