@microsoft/m365-spec-parser 0.2.2-alpha.5c16a1140.0 → 0.2.2-alpha.5d0324fcc.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.
@@ -58,6 +58,7 @@ var WarningType;
58
58
  WarningType["GenerateCardFailed"] = "generate-card-failed";
59
59
  WarningType["OperationOnlyContainsOptionalParam"] = "operation-only-contains-optional-param";
60
60
  WarningType["ConvertSwaggerToOpenAPI"] = "convert-swagger-to-openapi";
61
+ WarningType["FuncDescriptionTooLong"] = "function-description-too-long";
61
62
  WarningType["Unknown"] = "unknown";
62
63
  })(WarningType || (WarningType = {}));
63
64
  /**
@@ -96,6 +97,7 @@ ConstantString.SwaggerNotSupported = "Swagger 2.0 is not supported. Please conve
96
97
  ConstantString.SpecVersionNotSupported = "Unsupported OpenAPI version %s. Please use version 3.0.x.";
97
98
  ConstantString.MultipleAuthNotSupported = "Multiple authentication methods are unsupported. Ensure all selected APIs use identical authentication.";
98
99
  ConstantString.UnsupportedSchema = "Unsupported schema in %s %s: %s";
100
+ 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
101
  ConstantString.WrappedCardVersion = "devPreview";
100
102
  ConstantString.WrappedCardSchema = "https://developer.microsoft.com/json-schemas/teams/vDevPreview/MicrosoftTeams.ResponseRenderingTemplate.schema.json";
101
103
  ConstantString.WrappedCardResponseLayout = "list";
@@ -175,6 +177,7 @@ ConstantString.ConversationStarterMaxLens = 50;
175
177
  ConstantString.CommandTitleMaxLens = 32;
176
178
  ConstantString.ParameterTitleMaxLens = 32;
177
179
  ConstantString.SMERequiredParamsMaxNum = 5;
180
+ ConstantString.FunctionDescriptionMaxLens = 100;
178
181
  ConstantString.DefaultPluginId = "plugin_1";
179
182
  ConstantString.PluginManifestSchema = "https://aka.ms/json-schemas/copilot-extensions/v2.1/plugin.schema.json";
180
183
 
@@ -260,23 +263,28 @@ class Utils {
260
263
  return str.charAt(0).toUpperCase() + str.slice(1);
261
264
  }
262
265
  static getResponseJson(operationObject, allowMultipleMediaType = false) {
263
- var _a, _b;
266
+ var _a;
264
267
  let json = {};
265
268
  let multipleMediaType = false;
266
269
  for (const code of ConstantString.ResponseCodeFor20X) {
267
270
  const responseObject = (_a = operationObject === null || operationObject === void 0 ? void 0 : operationObject.responses) === null || _a === void 0 ? void 0 : _a[code];
268
- if ((_b = responseObject === null || responseObject === void 0 ? void 0 : responseObject.content) === null || _b === void 0 ? void 0 : _b["application/json"]) {
269
- multipleMediaType = false;
270
- json = responseObject.content["application/json"];
271
- if (Utils.containMultipleMediaTypes(responseObject)) {
272
- multipleMediaType = true;
273
- if (!allowMultipleMediaType) {
274
- json = {};
271
+ if (responseObject === null || responseObject === void 0 ? void 0 : responseObject.content) {
272
+ for (const contentType of Object.keys(responseObject.content)) {
273
+ // json media type can also be "application/json; charset=utf-8"
274
+ if (contentType.indexOf("application/json") >= 0) {
275
+ multipleMediaType = false;
276
+ json = responseObject.content[contentType];
277
+ if (Utils.containMultipleMediaTypes(responseObject)) {
278
+ multipleMediaType = true;
279
+ if (!allowMultipleMediaType) {
280
+ json = {};
281
+ }
282
+ }
283
+ else {
284
+ return { json, multipleMediaType };
285
+ }
275
286
  }
276
287
  }
277
- else {
278
- break;
279
- }
280
288
  }
281
289
  }
282
290
  return { json, multipleMediaType };
@@ -550,10 +558,11 @@ class Utils {
550
558
  currentCount += items.length;
551
559
  }
552
560
  else {
553
- if (currentCount < maxCount) {
554
- result.push(element);
555
- currentCount++;
556
- }
561
+ result.push(element);
562
+ currentCount++;
563
+ }
564
+ if (currentCount >= maxCount) {
565
+ break;
557
566
  }
558
567
  }
559
568
  return result;
@@ -1105,6 +1114,161 @@ class ValidatorFactory {
1105
1114
  }
1106
1115
  }
1107
1116
 
1117
+ // Copyright (c) Microsoft Corporation.
1118
+ class SpecOptimizer {
1119
+ static optimize(spec, options) {
1120
+ const mergedOptions = Object.assign(Object.assign({}, SpecOptimizer.defaultOptions), (options !== null && options !== void 0 ? options : {}));
1121
+ const newSpec = JSON.parse(JSON.stringify(spec));
1122
+ if (mergedOptions.removeUserDefinedRootProperty) {
1123
+ SpecOptimizer.removeUserDefinedRootProperty(newSpec);
1124
+ }
1125
+ if (mergedOptions.removeUnusedComponents) {
1126
+ SpecOptimizer.removeUnusedComponents(newSpec);
1127
+ }
1128
+ if (mergedOptions.removeUnusedTags) {
1129
+ SpecOptimizer.removeUnusedTags(newSpec);
1130
+ }
1131
+ if (mergedOptions.removeUnusedSecuritySchemas) {
1132
+ SpecOptimizer.removeUnusedSecuritySchemas(newSpec);
1133
+ }
1134
+ return newSpec;
1135
+ }
1136
+ static removeUnusedSecuritySchemas(spec) {
1137
+ if (!spec.components || !spec.components.securitySchemes) {
1138
+ return;
1139
+ }
1140
+ const usedSecuritySchemas = new Set();
1141
+ for (const pathKey in spec.paths) {
1142
+ for (const methodKey in spec.paths[pathKey]) {
1143
+ const operation = spec.paths[pathKey][methodKey];
1144
+ if (operation.security) {
1145
+ operation.security.forEach((securityReq) => {
1146
+ for (const schemaKey in securityReq) {
1147
+ usedSecuritySchemas.add(schemaKey);
1148
+ }
1149
+ });
1150
+ }
1151
+ }
1152
+ }
1153
+ if (spec.security) {
1154
+ spec.security.forEach((securityReq) => {
1155
+ for (const schemaKey in securityReq) {
1156
+ usedSecuritySchemas.add(schemaKey);
1157
+ }
1158
+ });
1159
+ }
1160
+ for (const schemaKey in spec.components.securitySchemes) {
1161
+ if (!usedSecuritySchemas.has(schemaKey)) {
1162
+ delete spec.components.securitySchemes[schemaKey];
1163
+ }
1164
+ }
1165
+ if (Object.keys(spec.components.securitySchemes).length === 0) {
1166
+ delete spec.components.securitySchemes;
1167
+ }
1168
+ if (Object.keys(spec.components).length === 0) {
1169
+ delete spec.components;
1170
+ }
1171
+ }
1172
+ static removeUnusedTags(spec) {
1173
+ if (!spec.tags) {
1174
+ return;
1175
+ }
1176
+ const usedTags = new Set();
1177
+ for (const pathKey in spec.paths) {
1178
+ for (const methodKey in spec.paths[pathKey]) {
1179
+ const operation = spec.paths[pathKey][methodKey];
1180
+ if (operation.tags) {
1181
+ operation.tags.forEach((tag) => usedTags.add(tag));
1182
+ }
1183
+ }
1184
+ }
1185
+ spec.tags = spec.tags.filter((tagObj) => usedTags.has(tagObj.name));
1186
+ }
1187
+ static removeUserDefinedRootProperty(spec) {
1188
+ for (const key in spec) {
1189
+ if (key.startsWith("x-")) {
1190
+ delete spec[key];
1191
+ }
1192
+ }
1193
+ }
1194
+ static removeUnusedComponents(spec) {
1195
+ const components = spec.components;
1196
+ if (!components) {
1197
+ return;
1198
+ }
1199
+ delete spec.components;
1200
+ const usedComponentsSet = new Set();
1201
+ const specString = JSON.stringify(spec);
1202
+ const componentReferences = SpecOptimizer.getComponentReferences(specString);
1203
+ for (const reference of componentReferences) {
1204
+ this.addComponent(reference, usedComponentsSet, components);
1205
+ }
1206
+ const newComponents = {};
1207
+ for (const componentName of usedComponentsSet) {
1208
+ const parts = componentName.split("/");
1209
+ const component = this.getComponent(componentName, components);
1210
+ if (component) {
1211
+ let current = newComponents;
1212
+ for (let i = 2; i < parts.length; i++) {
1213
+ if (i === parts.length - 1) {
1214
+ current[parts[i]] = component;
1215
+ }
1216
+ else if (!current[parts[i]]) {
1217
+ current[parts[i]] = {};
1218
+ }
1219
+ current = current[parts[i]];
1220
+ }
1221
+ }
1222
+ }
1223
+ // securitySchemes are referenced directly by name, to void issue, just keep them all and use removeUnusedSecuritySchemas to remove unused ones
1224
+ if (components.securitySchemes) {
1225
+ newComponents.securitySchemes = components.securitySchemes;
1226
+ }
1227
+ if (Object.keys(newComponents).length !== 0) {
1228
+ spec.components = newComponents;
1229
+ }
1230
+ }
1231
+ static getComponentReferences(specString) {
1232
+ const matches = Array.from(specString.matchAll(/['"](#\/components\/.+?)['"]/g));
1233
+ const matchResult = matches.map((match) => match[1]);
1234
+ return matchResult;
1235
+ }
1236
+ static getComponent(componentPath, components) {
1237
+ const parts = componentPath.split("/");
1238
+ let current = components;
1239
+ for (const part of parts) {
1240
+ if (part === "#" || part === "components") {
1241
+ continue;
1242
+ }
1243
+ current = current[part];
1244
+ if (!current) {
1245
+ return null;
1246
+ }
1247
+ }
1248
+ return current;
1249
+ }
1250
+ static addComponent(componentName, usedComponentsSet, components) {
1251
+ if (usedComponentsSet.has(componentName)) {
1252
+ return;
1253
+ }
1254
+ usedComponentsSet.add(componentName);
1255
+ const component = this.getComponent(componentName, components);
1256
+ if (component) {
1257
+ const componentString = JSON.stringify(component);
1258
+ const componentReferences = SpecOptimizer.getComponentReferences(componentString);
1259
+ for (const reference of componentReferences) {
1260
+ this.addComponent(reference, usedComponentsSet, components);
1261
+ }
1262
+ }
1263
+ }
1264
+ }
1265
+ SpecOptimizer.defaultOptions = {
1266
+ removeUnusedComponents: true,
1267
+ removeUnusedTags: true,
1268
+ removeUserDefinedRootProperty: true,
1269
+ removeUnusedSecuritySchemas: true,
1270
+ };
1271
+
1108
1272
  // Copyright (c) Microsoft Corporation.
1109
1273
  class SpecFilter {
1110
1274
  static specFilter(filter, unResolveSpec, resolvedSpec, options) {
@@ -1138,7 +1302,7 @@ class SpecFilter {
1138
1302
  }
1139
1303
  }
1140
1304
  newSpec.paths = newPaths;
1141
- return newSpec;
1305
+ return SpecOptimizer.optimize(newSpec);
1142
1306
  }
1143
1307
  catch (err) {
1144
1308
  throw new SpecParserError(err.toString(), ErrorType.FilterSpecFailed);
@@ -1261,7 +1425,7 @@ class AdaptiveCardGenerator {
1261
1425
  {
1262
1426
  type: "Image",
1263
1427
  url: `\${${name}}`,
1264
- $when: `\${${name} != null}`,
1428
+ $when: `\${${name} != null && ${name} != ''}`,
1265
1429
  },
1266
1430
  ];
1267
1431
  }
@@ -1270,7 +1434,7 @@ class AdaptiveCardGenerator {
1270
1434
  {
1271
1435
  type: "Image",
1272
1436
  url: "${$data}",
1273
- $when: "${$data != null}",
1437
+ $when: "${$data != null && $data != ''}",
1274
1438
  },
1275
1439
  ];
1276
1440
  }
@@ -1363,7 +1527,7 @@ function inferPreviewCardTemplate(card) {
1363
1527
  result.image = {
1364
1528
  url: `\${${inferredProperties.imageUrl}}`,
1365
1529
  alt: `\${if(${inferredProperties.imageUrl}, ${inferredProperties.imageUrl}, 'N/A')}`,
1366
- $when: `\${${inferredProperties.imageUrl} != null}`,
1530
+ $when: `\${${inferredProperties.imageUrl} != null && ${inferredProperties.imageUrl} != ''}`,
1367
1531
  };
1368
1532
  }
1369
1533
  return result;
@@ -1532,9 +1696,18 @@ class ManifestUpdater {
1532
1696
  throw new SpecParserError(Utils.format(ConstantString.UnsupportedSchema, method, pathUrl, JSON.stringify(requestBodySchema)), ErrorType.UpdateManifestFailed);
1533
1697
  }
1534
1698
  }
1699
+ let funcDescription = operationItem.description || operationItem.summary || "";
1700
+ if (funcDescription.length > ConstantString.FunctionDescriptionMaxLens) {
1701
+ warnings.push({
1702
+ type: WarningType.FuncDescriptionTooLong,
1703
+ content: Utils.format(ConstantString.FuncDescriptionTooLong, safeFunctionName, funcDescription.length.toString(), ConstantString.FunctionDescriptionMaxLens.toString()),
1704
+ data: safeFunctionName,
1705
+ });
1706
+ funcDescription = funcDescription.slice(0, ConstantString.FunctionDescriptionMaxLens);
1707
+ }
1535
1708
  const funcObj = {
1536
1709
  name: safeFunctionName,
1537
- description: description,
1710
+ description: funcDescription,
1538
1711
  };
1539
1712
  if (options.allowResponseSemantics) {
1540
1713
  try {
@@ -1975,7 +2148,7 @@ class SpecParser {
1975
2148
  * @param outputSpecPath File path of the new OpenAPI specification file to generate. If not specified or empty, no spec file will be generated.
1976
2149
  * @param pluginFilePath File path of the api plugin file to generate.
1977
2150
  */
1978
- async generateForCopilot(manifestPath, filter, outputSpecPath, pluginFilePath, signal) {
2151
+ async generateForCopilot(manifestPath, filter, outputSpecPath, pluginFilePath, existingPluginFilePath, signal) {
1979
2152
  const result = {
1980
2153
  allSuccess: true,
1981
2154
  warnings: [],
@@ -1992,7 +2165,15 @@ class SpecParser {
1992
2165
  const [updatedManifest, apiPlugin, warnings] = await ManifestUpdater.updateManifestWithAiPlugin(manifestPath, outputSpecPath, pluginFilePath, newSpec, this.options, authInfo);
1993
2166
  result.warnings.push(...warnings);
1994
2167
  await fs.outputJSON(manifestPath, updatedManifest, { spaces: 4 });
1995
- await fs.outputJSON(pluginFilePath, apiPlugin, { spaces: 4 });
2168
+ if (existingPluginFilePath) {
2169
+ const originPluginManifest = (await fs.readJSON(existingPluginFilePath));
2170
+ // TODO (kiota): refactor to avoid generate apiPlugin
2171
+ originPluginManifest.functions = apiPlugin.functions;
2172
+ await fs.outputJSON(pluginFilePath, originPluginManifest, { spaces: 4 });
2173
+ }
2174
+ else {
2175
+ await fs.outputJSON(pluginFilePath, apiPlugin, { spaces: 4 });
2176
+ }
1996
2177
  }
1997
2178
  catch (err) {
1998
2179
  if (err instanceof SpecParserError) {