@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.
@@ -100,6 +100,7 @@ exports.WarningType = void 0;
100
100
  WarningType["GenerateCardFailed"] = "generate-card-failed";
101
101
  WarningType["OperationOnlyContainsOptionalParam"] = "operation-only-contains-optional-param";
102
102
  WarningType["ConvertSwaggerToOpenAPI"] = "convert-swagger-to-openapi";
103
+ WarningType["FuncDescriptionTooLong"] = "function-description-too-long";
103
104
  WarningType["Unknown"] = "unknown";
104
105
  })(exports.WarningType || (exports.WarningType = {}));
105
106
  /**
@@ -138,6 +139,7 @@ ConstantString.SwaggerNotSupported = "Swagger 2.0 is not supported. Please conve
138
139
  ConstantString.SpecVersionNotSupported = "Unsupported OpenAPI version %s. Please use version 3.0.x.";
139
140
  ConstantString.MultipleAuthNotSupported = "Multiple authentication methods are unsupported. Ensure all selected APIs use identical authentication.";
140
141
  ConstantString.UnsupportedSchema = "Unsupported schema in %s %s: %s";
142
+ 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
143
  ConstantString.WrappedCardVersion = "devPreview";
142
144
  ConstantString.WrappedCardSchema = "https://developer.microsoft.com/json-schemas/teams/vDevPreview/MicrosoftTeams.ResponseRenderingTemplate.schema.json";
143
145
  ConstantString.WrappedCardResponseLayout = "list";
@@ -217,6 +219,7 @@ ConstantString.ConversationStarterMaxLens = 50;
217
219
  ConstantString.CommandTitleMaxLens = 32;
218
220
  ConstantString.ParameterTitleMaxLens = 32;
219
221
  ConstantString.SMERequiredParamsMaxNum = 5;
222
+ ConstantString.FunctionDescriptionMaxLens = 100;
220
223
  ConstantString.DefaultPluginId = "plugin_1";
221
224
  ConstantString.PluginManifestSchema = "https://aka.ms/json-schemas/copilot-extensions/v2.1/plugin.schema.json";
222
225
 
@@ -302,23 +305,28 @@ class Utils {
302
305
  return str.charAt(0).toUpperCase() + str.slice(1);
303
306
  }
304
307
  static getResponseJson(operationObject, allowMultipleMediaType = false) {
305
- var _a, _b;
308
+ var _a;
306
309
  let json = {};
307
310
  let multipleMediaType = false;
308
311
  for (const code of ConstantString.ResponseCodeFor20X) {
309
312
  const responseObject = (_a = operationObject === null || operationObject === void 0 ? void 0 : operationObject.responses) === null || _a === void 0 ? void 0 : _a[code];
310
- if ((_b = responseObject === null || responseObject === void 0 ? void 0 : responseObject.content) === null || _b === void 0 ? void 0 : _b["application/json"]) {
311
- multipleMediaType = false;
312
- json = responseObject.content["application/json"];
313
- if (Utils.containMultipleMediaTypes(responseObject)) {
314
- multipleMediaType = true;
315
- if (!allowMultipleMediaType) {
316
- json = {};
313
+ if (responseObject === null || responseObject === void 0 ? void 0 : responseObject.content) {
314
+ for (const contentType of Object.keys(responseObject.content)) {
315
+ // json media type can also be "application/json; charset=utf-8"
316
+ if (contentType.indexOf("application/json") >= 0) {
317
+ multipleMediaType = false;
318
+ json = responseObject.content[contentType];
319
+ if (Utils.containMultipleMediaTypes(responseObject)) {
320
+ multipleMediaType = true;
321
+ if (!allowMultipleMediaType) {
322
+ json = {};
323
+ }
324
+ }
325
+ else {
326
+ return { json, multipleMediaType };
327
+ }
317
328
  }
318
329
  }
319
- else {
320
- break;
321
- }
322
330
  }
323
331
  }
324
332
  return { json, multipleMediaType };
@@ -592,10 +600,11 @@ class Utils {
592
600
  currentCount += items.length;
593
601
  }
594
602
  else {
595
- if (currentCount < maxCount) {
596
- result.push(element);
597
- currentCount++;
598
- }
603
+ result.push(element);
604
+ currentCount++;
605
+ }
606
+ if (currentCount >= maxCount) {
607
+ break;
599
608
  }
600
609
  }
601
610
  return result;
@@ -1147,6 +1156,161 @@ class ValidatorFactory {
1147
1156
  }
1148
1157
  }
1149
1158
 
1159
+ // Copyright (c) Microsoft Corporation.
1160
+ class SpecOptimizer {
1161
+ static optimize(spec, options) {
1162
+ const mergedOptions = Object.assign(Object.assign({}, SpecOptimizer.defaultOptions), (options !== null && options !== void 0 ? options : {}));
1163
+ const newSpec = JSON.parse(JSON.stringify(spec));
1164
+ if (mergedOptions.removeUserDefinedRootProperty) {
1165
+ SpecOptimizer.removeUserDefinedRootProperty(newSpec);
1166
+ }
1167
+ if (mergedOptions.removeUnusedComponents) {
1168
+ SpecOptimizer.removeUnusedComponents(newSpec);
1169
+ }
1170
+ if (mergedOptions.removeUnusedTags) {
1171
+ SpecOptimizer.removeUnusedTags(newSpec);
1172
+ }
1173
+ if (mergedOptions.removeUnusedSecuritySchemas) {
1174
+ SpecOptimizer.removeUnusedSecuritySchemas(newSpec);
1175
+ }
1176
+ return newSpec;
1177
+ }
1178
+ static removeUnusedSecuritySchemas(spec) {
1179
+ if (!spec.components || !spec.components.securitySchemes) {
1180
+ return;
1181
+ }
1182
+ const usedSecuritySchemas = new Set();
1183
+ for (const pathKey in spec.paths) {
1184
+ for (const methodKey in spec.paths[pathKey]) {
1185
+ const operation = spec.paths[pathKey][methodKey];
1186
+ if (operation.security) {
1187
+ operation.security.forEach((securityReq) => {
1188
+ for (const schemaKey in securityReq) {
1189
+ usedSecuritySchemas.add(schemaKey);
1190
+ }
1191
+ });
1192
+ }
1193
+ }
1194
+ }
1195
+ if (spec.security) {
1196
+ spec.security.forEach((securityReq) => {
1197
+ for (const schemaKey in securityReq) {
1198
+ usedSecuritySchemas.add(schemaKey);
1199
+ }
1200
+ });
1201
+ }
1202
+ for (const schemaKey in spec.components.securitySchemes) {
1203
+ if (!usedSecuritySchemas.has(schemaKey)) {
1204
+ delete spec.components.securitySchemes[schemaKey];
1205
+ }
1206
+ }
1207
+ if (Object.keys(spec.components.securitySchemes).length === 0) {
1208
+ delete spec.components.securitySchemes;
1209
+ }
1210
+ if (Object.keys(spec.components).length === 0) {
1211
+ delete spec.components;
1212
+ }
1213
+ }
1214
+ static removeUnusedTags(spec) {
1215
+ if (!spec.tags) {
1216
+ return;
1217
+ }
1218
+ const usedTags = new Set();
1219
+ for (const pathKey in spec.paths) {
1220
+ for (const methodKey in spec.paths[pathKey]) {
1221
+ const operation = spec.paths[pathKey][methodKey];
1222
+ if (operation.tags) {
1223
+ operation.tags.forEach((tag) => usedTags.add(tag));
1224
+ }
1225
+ }
1226
+ }
1227
+ spec.tags = spec.tags.filter((tagObj) => usedTags.has(tagObj.name));
1228
+ }
1229
+ static removeUserDefinedRootProperty(spec) {
1230
+ for (const key in spec) {
1231
+ if (key.startsWith("x-")) {
1232
+ delete spec[key];
1233
+ }
1234
+ }
1235
+ }
1236
+ static removeUnusedComponents(spec) {
1237
+ const components = spec.components;
1238
+ if (!components) {
1239
+ return;
1240
+ }
1241
+ delete spec.components;
1242
+ const usedComponentsSet = new Set();
1243
+ const specString = JSON.stringify(spec);
1244
+ const componentReferences = SpecOptimizer.getComponentReferences(specString);
1245
+ for (const reference of componentReferences) {
1246
+ this.addComponent(reference, usedComponentsSet, components);
1247
+ }
1248
+ const newComponents = {};
1249
+ for (const componentName of usedComponentsSet) {
1250
+ const parts = componentName.split("/");
1251
+ const component = this.getComponent(componentName, components);
1252
+ if (component) {
1253
+ let current = newComponents;
1254
+ for (let i = 2; i < parts.length; i++) {
1255
+ if (i === parts.length - 1) {
1256
+ current[parts[i]] = component;
1257
+ }
1258
+ else if (!current[parts[i]]) {
1259
+ current[parts[i]] = {};
1260
+ }
1261
+ current = current[parts[i]];
1262
+ }
1263
+ }
1264
+ }
1265
+ // securitySchemes are referenced directly by name, to void issue, just keep them all and use removeUnusedSecuritySchemas to remove unused ones
1266
+ if (components.securitySchemes) {
1267
+ newComponents.securitySchemes = components.securitySchemes;
1268
+ }
1269
+ if (Object.keys(newComponents).length !== 0) {
1270
+ spec.components = newComponents;
1271
+ }
1272
+ }
1273
+ static getComponentReferences(specString) {
1274
+ const matches = Array.from(specString.matchAll(/['"](#\/components\/.+?)['"]/g));
1275
+ const matchResult = matches.map((match) => match[1]);
1276
+ return matchResult;
1277
+ }
1278
+ static getComponent(componentPath, components) {
1279
+ const parts = componentPath.split("/");
1280
+ let current = components;
1281
+ for (const part of parts) {
1282
+ if (part === "#" || part === "components") {
1283
+ continue;
1284
+ }
1285
+ current = current[part];
1286
+ if (!current) {
1287
+ return null;
1288
+ }
1289
+ }
1290
+ return current;
1291
+ }
1292
+ static addComponent(componentName, usedComponentsSet, components) {
1293
+ if (usedComponentsSet.has(componentName)) {
1294
+ return;
1295
+ }
1296
+ usedComponentsSet.add(componentName);
1297
+ const component = this.getComponent(componentName, components);
1298
+ if (component) {
1299
+ const componentString = JSON.stringify(component);
1300
+ const componentReferences = SpecOptimizer.getComponentReferences(componentString);
1301
+ for (const reference of componentReferences) {
1302
+ this.addComponent(reference, usedComponentsSet, components);
1303
+ }
1304
+ }
1305
+ }
1306
+ }
1307
+ SpecOptimizer.defaultOptions = {
1308
+ removeUnusedComponents: true,
1309
+ removeUnusedTags: true,
1310
+ removeUserDefinedRootProperty: true,
1311
+ removeUnusedSecuritySchemas: true,
1312
+ };
1313
+
1150
1314
  // Copyright (c) Microsoft Corporation.
1151
1315
  class SpecFilter {
1152
1316
  static specFilter(filter, unResolveSpec, resolvedSpec, options) {
@@ -1180,7 +1344,7 @@ class SpecFilter {
1180
1344
  }
1181
1345
  }
1182
1346
  newSpec.paths = newPaths;
1183
- return newSpec;
1347
+ return SpecOptimizer.optimize(newSpec);
1184
1348
  }
1185
1349
  catch (err) {
1186
1350
  throw new SpecParserError(err.toString(), exports.ErrorType.FilterSpecFailed);
@@ -1303,7 +1467,7 @@ class AdaptiveCardGenerator {
1303
1467
  {
1304
1468
  type: "Image",
1305
1469
  url: `\${${name}}`,
1306
- $when: `\${${name} != null}`,
1470
+ $when: `\${${name} != null && ${name} != ''}`,
1307
1471
  },
1308
1472
  ];
1309
1473
  }
@@ -1312,7 +1476,7 @@ class AdaptiveCardGenerator {
1312
1476
  {
1313
1477
  type: "Image",
1314
1478
  url: "${$data}",
1315
- $when: "${$data != null}",
1479
+ $when: "${$data != null && $data != ''}",
1316
1480
  },
1317
1481
  ];
1318
1482
  }
@@ -1405,7 +1569,7 @@ function inferPreviewCardTemplate(card) {
1405
1569
  result.image = {
1406
1570
  url: `\${${inferredProperties.imageUrl}}`,
1407
1571
  alt: `\${if(${inferredProperties.imageUrl}, ${inferredProperties.imageUrl}, 'N/A')}`,
1408
- $when: `\${${inferredProperties.imageUrl} != null}`,
1572
+ $when: `\${${inferredProperties.imageUrl} != null && ${inferredProperties.imageUrl} != ''}`,
1409
1573
  };
1410
1574
  }
1411
1575
  return result;
@@ -1577,9 +1741,18 @@ class ManifestUpdater {
1577
1741
  throw new SpecParserError(Utils.format(ConstantString.UnsupportedSchema, method, pathUrl, JSON.stringify(requestBodySchema)), exports.ErrorType.UpdateManifestFailed);
1578
1742
  }
1579
1743
  }
1744
+ let funcDescription = operationItem.description || operationItem.summary || "";
1745
+ if (funcDescription.length > ConstantString.FunctionDescriptionMaxLens) {
1746
+ warnings.push({
1747
+ type: exports.WarningType.FuncDescriptionTooLong,
1748
+ content: Utils.format(ConstantString.FuncDescriptionTooLong, safeFunctionName, funcDescription.length.toString(), ConstantString.FunctionDescriptionMaxLens.toString()),
1749
+ data: safeFunctionName,
1750
+ });
1751
+ funcDescription = funcDescription.slice(0, ConstantString.FunctionDescriptionMaxLens);
1752
+ }
1580
1753
  const funcObj = {
1581
1754
  name: safeFunctionName,
1582
- description: description,
1755
+ description: funcDescription,
1583
1756
  };
1584
1757
  if (options.allowResponseSemantics) {
1585
1758
  try {
@@ -2033,7 +2206,7 @@ class SpecParser {
2033
2206
  * @param outputSpecPath File path of the new OpenAPI specification file to generate. If not specified or empty, no spec file will be generated.
2034
2207
  * @param pluginFilePath File path of the api plugin file to generate.
2035
2208
  */
2036
- generateForCopilot(manifestPath, filter, outputSpecPath, pluginFilePath, signal) {
2209
+ generateForCopilot(manifestPath, filter, outputSpecPath, pluginFilePath, existingPluginFilePath, signal) {
2037
2210
  return __awaiter(this, void 0, void 0, function* () {
2038
2211
  const result = {
2039
2212
  allSuccess: true,
@@ -2051,7 +2224,15 @@ class SpecParser {
2051
2224
  const [updatedManifest, apiPlugin, warnings] = yield ManifestUpdater.updateManifestWithAiPlugin(manifestPath, outputSpecPath, pluginFilePath, newSpec, this.options, authInfo);
2052
2225
  result.warnings.push(...warnings);
2053
2226
  yield fs__default['default'].outputJSON(manifestPath, updatedManifest, { spaces: 4 });
2054
- yield fs__default['default'].outputJSON(pluginFilePath, apiPlugin, { spaces: 4 });
2227
+ if (existingPluginFilePath) {
2228
+ const originPluginManifest = (yield fs__default['default'].readJSON(existingPluginFilePath));
2229
+ // TODO (kiota): refactor to avoid generate apiPlugin
2230
+ originPluginManifest.functions = apiPlugin.functions;
2231
+ yield fs__default['default'].outputJSON(pluginFilePath, originPluginManifest, { spaces: 4 });
2232
+ }
2233
+ else {
2234
+ yield fs__default['default'].outputJSON(pluginFilePath, apiPlugin, { spaces: 4 });
2235
+ }
2055
2236
  }
2056
2237
  catch (err) {
2057
2238
  if (err instanceof SpecParserError) {