@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.
@@ -3,6 +3,7 @@ import converter from 'swagger2openapi';
3
3
  import jsyaml from 'js-yaml';
4
4
  import fs from 'fs-extra';
5
5
  import path from 'path';
6
+ import { createHash } from 'crypto';
6
7
 
7
8
  // Copyright (c) Microsoft Corporation.
8
9
  /**
@@ -58,6 +59,7 @@ var WarningType;
58
59
  WarningType["GenerateCardFailed"] = "generate-card-failed";
59
60
  WarningType["OperationOnlyContainsOptionalParam"] = "operation-only-contains-optional-param";
60
61
  WarningType["ConvertSwaggerToOpenAPI"] = "convert-swagger-to-openapi";
62
+ WarningType["FuncDescriptionTooLong"] = "function-description-too-long";
61
63
  WarningType["Unknown"] = "unknown";
62
64
  })(WarningType || (WarningType = {}));
63
65
  /**
@@ -96,6 +98,7 @@ ConstantString.SwaggerNotSupported = "Swagger 2.0 is not supported. Please conve
96
98
  ConstantString.SpecVersionNotSupported = "Unsupported OpenAPI version %s. Please use version 3.0.x.";
97
99
  ConstantString.MultipleAuthNotSupported = "Multiple authentication methods are unsupported. Ensure all selected APIs use identical authentication.";
98
100
  ConstantString.UnsupportedSchema = "Unsupported schema in %s %s: %s";
101
+ 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.";
99
102
  ConstantString.WrappedCardVersion = "devPreview";
100
103
  ConstantString.WrappedCardSchema = "https://developer.microsoft.com/json-schemas/teams/vDevPreview/MicrosoftTeams.ResponseRenderingTemplate.schema.json";
101
104
  ConstantString.WrappedCardResponseLayout = "list";
@@ -175,8 +178,9 @@ ConstantString.ConversationStarterMaxLens = 50;
175
178
  ConstantString.CommandTitleMaxLens = 32;
176
179
  ConstantString.ParameterTitleMaxLens = 32;
177
180
  ConstantString.SMERequiredParamsMaxNum = 5;
181
+ ConstantString.FunctionDescriptionMaxLens = 100;
178
182
  ConstantString.DefaultPluginId = "plugin_1";
179
- ConstantString.PluginManifestSchema = "https://aka.ms/json-schemas/copilot-extensions/v2.1/plugin.schema.json";
183
+ ConstantString.PluginManifestSchema = "https://developer.microsoft.com/json-schemas/copilot/plugin/v2.1/schema.json";
180
184
 
181
185
  // Copyright (c) Microsoft Corporation.
182
186
  class SpecParserError extends Error {
@@ -189,16 +193,19 @@ class SpecParserError extends Error {
189
193
  // Copyright (c) Microsoft Corporation.
190
194
  class Utils {
191
195
  static hasNestedObjectInSchema(schema) {
192
- if (schema.type === "object") {
196
+ if (this.isObjectSchema(schema)) {
193
197
  for (const property in schema.properties) {
194
198
  const nestedSchema = schema.properties[property];
195
- if (nestedSchema.type === "object") {
199
+ if (this.isObjectSchema(nestedSchema)) {
196
200
  return true;
197
201
  }
198
202
  }
199
203
  }
200
204
  return false;
201
205
  }
206
+ static isObjectSchema(schema) {
207
+ return schema.type === "object" || (!schema.type && !!schema.properties);
208
+ }
202
209
  static containMultipleMediaTypes(bodyObject) {
203
210
  return Object.keys((bodyObject === null || bodyObject === void 0 ? void 0 : bodyObject.content) || {}).length > 1;
204
211
  }
@@ -427,7 +434,7 @@ class Utils {
427
434
  optionalParams.push(parameter);
428
435
  }
429
436
  }
430
- else if (schema.type === "object") {
437
+ else if (Utils.isObjectSchema(schema)) {
431
438
  const { properties } = schema;
432
439
  for (const property in properties) {
433
440
  let isRequired = false;
@@ -765,7 +772,7 @@ class Validator {
765
772
  }
766
773
  const isRequiredWithoutDefault = isRequired && schema.default === undefined;
767
774
  const isCopilot = this.projectType === ProjectType.Copilot;
768
- if (isCopilot && this.hasNestedObjectInSchema(schema)) {
775
+ if (isCopilot && Utils.hasNestedObjectInSchema(schema)) {
769
776
  paramResult.isValid = false;
770
777
  paramResult.reason = [ErrorType.RequestBodyContainsNestedObject];
771
778
  return paramResult;
@@ -781,7 +788,7 @@ class Validator {
781
788
  paramResult.optionalNum = paramResult.optionalNum + 1;
782
789
  }
783
790
  }
784
- else if (schema.type === "object") {
791
+ else if (Utils.isObjectSchema(schema)) {
785
792
  const { properties } = schema;
786
793
  for (const property in properties) {
787
794
  let isRequired = false;
@@ -817,7 +824,7 @@ class Validator {
817
824
  for (let i = 0; i < paramObject.length; i++) {
818
825
  const param = paramObject[i];
819
826
  const schema = param.schema;
820
- if (isCopilot && this.hasNestedObjectInSchema(schema)) {
827
+ if (isCopilot && Utils.hasNestedObjectInSchema(schema)) {
821
828
  paramResult.isValid = false;
822
829
  paramResult.reason.push(ErrorType.ParamsContainsNestedObject);
823
830
  continue;
@@ -860,17 +867,6 @@ class Validator {
860
867
  }
861
868
  return paramResult;
862
869
  }
863
- hasNestedObjectInSchema(schema) {
864
- if (schema.type === "object") {
865
- for (const property in schema.properties) {
866
- const nestedSchema = schema.properties[property];
867
- if (nestedSchema.type === "object") {
868
- return true;
869
- }
870
- }
871
- }
872
- return false;
873
- }
874
870
  }
875
871
 
876
872
  // Copyright (c) Microsoft Corporation.
@@ -929,7 +925,7 @@ class CopilotValidator extends Validator {
929
925
  const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
930
926
  if (requestJsonBody) {
931
927
  const requestBodySchema = requestJsonBody.schema;
932
- if (requestBodySchema.type !== "object") {
928
+ if (!Utils.isObjectSchema(requestBodySchema)) {
933
929
  result.reason.push(ErrorType.PostBodySchemaIsNotJson);
934
930
  }
935
931
  const requestBodyParamResult = this.checkPostBodySchema(requestBodySchema, requestBody.required);
@@ -1376,7 +1372,7 @@ class AdaptiveCardGenerator {
1376
1372
  return [template];
1377
1373
  }
1378
1374
  // some schema may not contain type but contain properties
1379
- if (schema.type === "object" || (!schema.type && schema.properties)) {
1375
+ if (Utils.isObjectSchema(schema)) {
1380
1376
  const { properties } = schema;
1381
1377
  const result = [];
1382
1378
  for (const property in properties) {
@@ -1444,7 +1440,7 @@ class AdaptiveCardGenerator {
1444
1440
  }
1445
1441
  // Find the first array property in the response schema object with the well-known name
1446
1442
  static getResponseJsonPathFromSchema(schema) {
1447
- if (schema.type === "object" || (!schema.type && schema.properties)) {
1443
+ if (Utils.isObjectSchema(schema)) {
1448
1444
  const { properties } = schema;
1449
1445
  for (const property in properties) {
1450
1446
  const schema = properties[property];
@@ -1596,7 +1592,7 @@ function inferProperties(card) {
1596
1592
 
1597
1593
  // Copyright (c) Microsoft Corporation.
1598
1594
  class ManifestUpdater {
1599
- static async updateManifestWithAiPlugin(manifestPath, outputSpecPath, apiPluginFilePath, spec, options, authInfo) {
1595
+ static async updateManifestWithAiPlugin(manifestPath, outputSpecPath, apiPluginFilePath, spec, options, authInfo, existingPluginManifestInfo) {
1600
1596
  const manifest = await fs.readJSON(manifestPath);
1601
1597
  const apiPluginRelativePath = ManifestUpdater.getRelativePath(manifestPath, apiPluginFilePath);
1602
1598
  manifest.copilotExtensions = manifest.copilotExtensions || {};
@@ -1612,7 +1608,7 @@ class ManifestUpdater {
1612
1608
  }
1613
1609
  const appName = this.removeEnvs(manifest.name.short);
1614
1610
  const specRelativePath = ManifestUpdater.getRelativePath(manifestPath, outputSpecPath);
1615
- const [apiPlugin, warnings] = await ManifestUpdater.generatePluginManifestSchema(spec, specRelativePath, apiPluginFilePath, appName, authInfo, options);
1611
+ const [apiPlugin, warnings] = await ManifestUpdater.generatePluginManifestSchema(spec, specRelativePath, apiPluginFilePath, appName, authInfo, options, existingPluginManifestInfo);
1616
1612
  return [manifest, apiPlugin, warnings];
1617
1613
  }
1618
1614
  static updateManifestDescription(manifest, spec) {
@@ -1634,7 +1630,7 @@ class ManifestUpdater {
1634
1630
  throw new SpecParserError(Utils.format(ConstantString.UnsupportedSchema, method, pathUrl, JSON.stringify(schema)), ErrorType.UpdateManifestFailed);
1635
1631
  }
1636
1632
  }
1637
- static async generatePluginManifestSchema(spec, specRelativePath, apiPluginFilePath, appName, authInfo, options) {
1633
+ static async generatePluginManifestSchema(spec, specRelativePath, apiPluginFilePath, appName, authInfo, options, existingPluginManifestInfo) {
1638
1634
  var _a, _b, _c, _d;
1639
1635
  const warnings = [];
1640
1636
  const functions = [];
@@ -1682,7 +1678,7 @@ class ManifestUpdater {
1682
1678
  if (requestBody) {
1683
1679
  const requestJsonBody = requestBody.content["application/json"];
1684
1680
  const requestBodySchema = requestJsonBody.schema;
1685
- if (requestBodySchema.type === "object") {
1681
+ if (Utils.isObjectSchema(requestBodySchema)) {
1686
1682
  for (const property in requestBodySchema.properties) {
1687
1683
  const schema = requestBodySchema.properties[property];
1688
1684
  ManifestUpdater.checkSchema(schema, method, pathUrl);
@@ -1693,9 +1689,18 @@ class ManifestUpdater {
1693
1689
  throw new SpecParserError(Utils.format(ConstantString.UnsupportedSchema, method, pathUrl, JSON.stringify(requestBodySchema)), ErrorType.UpdateManifestFailed);
1694
1690
  }
1695
1691
  }
1692
+ let funcDescription = operationItem.description || operationItem.summary || "";
1693
+ if (funcDescription.length > ConstantString.FunctionDescriptionMaxLens) {
1694
+ warnings.push({
1695
+ type: WarningType.FuncDescriptionTooLong,
1696
+ content: Utils.format(ConstantString.FuncDescriptionTooLong, safeFunctionName, funcDescription.length.toString(), ConstantString.FunctionDescriptionMaxLens.toString()),
1697
+ data: safeFunctionName,
1698
+ });
1699
+ funcDescription = funcDescription.slice(0, ConstantString.FunctionDescriptionMaxLens);
1700
+ }
1696
1701
  const funcObj = {
1697
1702
  name: safeFunctionName,
1698
- description: description,
1703
+ description: funcDescription,
1699
1704
  };
1700
1705
  if (options.allowResponseSemantics) {
1701
1706
  try {
@@ -1744,6 +1749,10 @@ class ManifestUpdater {
1744
1749
  if (await fs.pathExists(apiPluginFilePath)) {
1745
1750
  apiPlugin = await fs.readJSON(apiPluginFilePath);
1746
1751
  }
1752
+ else if (existingPluginManifestInfo &&
1753
+ (await fs.pathExists(existingPluginManifestInfo.manifestPath))) {
1754
+ apiPlugin = await fs.readJSON(existingPluginManifestInfo.manifestPath);
1755
+ }
1747
1756
  else {
1748
1757
  apiPlugin = {
1749
1758
  $schema: ConstantString.PluginManifestSchema,
@@ -1766,6 +1775,11 @@ class ManifestUpdater {
1766
1775
  }
1767
1776
  }
1768
1777
  apiPlugin.runtimes = apiPlugin.runtimes || [];
1778
+ // Need to delete previous runtime since spec path has changed
1779
+ if (existingPluginManifestInfo) {
1780
+ const relativePath = ManifestUpdater.getRelativePath(existingPluginManifestInfo.manifestPath, existingPluginManifestInfo.specPath);
1781
+ apiPlugin.runtimes = apiPlugin.runtimes.filter((runtime) => runtime.spec.url !== relativePath);
1782
+ }
1769
1783
  const index = apiPlugin.runtimes.findIndex((runtime) => {
1770
1784
  var _a, _b;
1771
1785
  return runtime.spec.url === specRelativePath &&
@@ -1962,6 +1976,7 @@ class SpecParser {
1962
1976
  */
1963
1977
  async validate() {
1964
1978
  try {
1979
+ let hash = "";
1965
1980
  try {
1966
1981
  await this.loadSpec();
1967
1982
  if (!this.parser.$refs.circular) {
@@ -1977,8 +1992,13 @@ class SpecParser {
1977
1992
  status: ValidationStatus.Error,
1978
1993
  warnings: [],
1979
1994
  errors: [{ type: ErrorType.SpecNotValid, content: e.toString() }],
1995
+ specHash: hash,
1980
1996
  };
1981
1997
  }
1998
+ if (this.unResolveSpec.servers) {
1999
+ const serverString = JSON.stringify(this.unResolveSpec.servers);
2000
+ hash = createHash("sha256").update(serverString).digest("hex");
2001
+ }
1982
2002
  const errors = [];
1983
2003
  const warnings = [];
1984
2004
  if (!this.options.allowSwagger && this.isSwaggerFile) {
@@ -1988,6 +2008,7 @@ class SpecParser {
1988
2008
  errors: [
1989
2009
  { type: ErrorType.SwaggerNotSupported, content: ConstantString.SwaggerNotSupported },
1990
2010
  ],
2011
+ specHash: hash,
1991
2012
  };
1992
2013
  }
1993
2014
  // Remote reference not supported
@@ -2021,6 +2042,7 @@ class SpecParser {
2021
2042
  status: status,
2022
2043
  warnings: warnings,
2023
2044
  errors: errors,
2045
+ specHash: hash,
2024
2046
  };
2025
2047
  }
2026
2048
  catch (err) {
@@ -2136,7 +2158,7 @@ class SpecParser {
2136
2158
  * @param outputSpecPath File path of the new OpenAPI specification file to generate. If not specified or empty, no spec file will be generated.
2137
2159
  * @param pluginFilePath File path of the api plugin file to generate.
2138
2160
  */
2139
- async generateForCopilot(manifestPath, filter, outputSpecPath, pluginFilePath, signal) {
2161
+ async generateForCopilot(manifestPath, filter, outputSpecPath, pluginFilePath, existingPluginFilePath, signal) {
2140
2162
  const result = {
2141
2163
  allSuccess: true,
2142
2164
  warnings: [],
@@ -2150,7 +2172,13 @@ class SpecParser {
2150
2172
  if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
2151
2173
  throw new SpecParserError(ConstantString.CancelledMessage, ErrorType.Cancelled);
2152
2174
  }
2153
- const [updatedManifest, apiPlugin, warnings] = await ManifestUpdater.updateManifestWithAiPlugin(manifestPath, outputSpecPath, pluginFilePath, newSpec, this.options, authInfo);
2175
+ const existingPluginManifestInfo = existingPluginFilePath
2176
+ ? {
2177
+ manifestPath: existingPluginFilePath,
2178
+ specPath: this.pathOrSpec,
2179
+ }
2180
+ : undefined;
2181
+ const [updatedManifest, apiPlugin, warnings] = await ManifestUpdater.updateManifestWithAiPlugin(manifestPath, outputSpecPath, pluginFilePath, newSpec, this.options, authInfo, existingPluginManifestInfo);
2154
2182
  result.warnings.push(...warnings);
2155
2183
  await fs.outputJSON(manifestPath, updatedManifest, { spaces: 4 });
2156
2184
  await fs.outputJSON(pluginFilePath, apiPlugin, { spaces: 4 });