@microsoft/m365-spec-parser 0.2.2-alpha.6e69e41c0.0 → 0.2.2-alpha.7f0eac5b0.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.
@@ -302,23 +302,28 @@ class Utils {
302
302
  return str.charAt(0).toUpperCase() + str.slice(1);
303
303
  }
304
304
  static getResponseJson(operationObject, allowMultipleMediaType = false) {
305
- var _a, _b;
305
+ var _a;
306
306
  let json = {};
307
307
  let multipleMediaType = false;
308
308
  for (const code of ConstantString.ResponseCodeFor20X) {
309
309
  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 = {};
310
+ if (responseObject === null || responseObject === void 0 ? void 0 : responseObject.content) {
311
+ for (const contentType of Object.keys(responseObject.content)) {
312
+ // json media type can also be "application/json; charset=utf-8"
313
+ if (contentType.indexOf("application/json") >= 0) {
314
+ multipleMediaType = false;
315
+ json = responseObject.content[contentType];
316
+ if (Utils.containMultipleMediaTypes(responseObject)) {
317
+ multipleMediaType = true;
318
+ if (!allowMultipleMediaType) {
319
+ json = {};
320
+ }
321
+ }
322
+ else {
323
+ return { json, multipleMediaType };
324
+ }
317
325
  }
318
326
  }
319
- else {
320
- break;
321
- }
322
327
  }
323
328
  }
324
329
  return { json, multipleMediaType };
@@ -578,6 +583,29 @@ class Utils {
578
583
  const serverUrl = operationServer || methodServer || rootServer;
579
584
  return serverUrl;
580
585
  }
586
+ static limitACBodyProperties(body, maxCount) {
587
+ const result = [];
588
+ let currentCount = 0;
589
+ for (const element of body) {
590
+ if (element.type === ConstantString.ContainerType) {
591
+ const items = this.limitACBodyProperties(element.items, maxCount - currentCount);
592
+ result.push({
593
+ type: ConstantString.ContainerType,
594
+ $data: element.$data,
595
+ items: items,
596
+ });
597
+ currentCount += items.length;
598
+ }
599
+ else {
600
+ result.push(element);
601
+ currentCount++;
602
+ }
603
+ if (currentCount >= maxCount) {
604
+ break;
605
+ }
606
+ }
607
+ return result;
608
+ }
581
609
  }
582
610
 
583
611
  // Copyright (c) Microsoft Corporation.
@@ -1125,6 +1153,161 @@ class ValidatorFactory {
1125
1153
  }
1126
1154
  }
1127
1155
 
1156
+ // Copyright (c) Microsoft Corporation.
1157
+ class SpecOptimizer {
1158
+ static optimize(spec, options) {
1159
+ const mergedOptions = Object.assign(Object.assign({}, SpecOptimizer.defaultOptions), (options !== null && options !== void 0 ? options : {}));
1160
+ const newSpec = JSON.parse(JSON.stringify(spec));
1161
+ if (mergedOptions.removeUserDefinedRootProperty) {
1162
+ SpecOptimizer.removeUserDefinedRootProperty(newSpec);
1163
+ }
1164
+ if (mergedOptions.removeUnusedComponents) {
1165
+ SpecOptimizer.removeUnusedComponents(newSpec);
1166
+ }
1167
+ if (mergedOptions.removeUnusedTags) {
1168
+ SpecOptimizer.removeUnusedTags(newSpec);
1169
+ }
1170
+ if (mergedOptions.removeUnusedSecuritySchemas) {
1171
+ SpecOptimizer.removeUnusedSecuritySchemas(newSpec);
1172
+ }
1173
+ return newSpec;
1174
+ }
1175
+ static removeUnusedSecuritySchemas(spec) {
1176
+ if (!spec.components || !spec.components.securitySchemes) {
1177
+ return;
1178
+ }
1179
+ const usedSecuritySchemas = new Set();
1180
+ for (const pathKey in spec.paths) {
1181
+ for (const methodKey in spec.paths[pathKey]) {
1182
+ const operation = spec.paths[pathKey][methodKey];
1183
+ if (operation.security) {
1184
+ operation.security.forEach((securityReq) => {
1185
+ for (const schemaKey in securityReq) {
1186
+ usedSecuritySchemas.add(schemaKey);
1187
+ }
1188
+ });
1189
+ }
1190
+ }
1191
+ }
1192
+ if (spec.security) {
1193
+ spec.security.forEach((securityReq) => {
1194
+ for (const schemaKey in securityReq) {
1195
+ usedSecuritySchemas.add(schemaKey);
1196
+ }
1197
+ });
1198
+ }
1199
+ for (const schemaKey in spec.components.securitySchemes) {
1200
+ if (!usedSecuritySchemas.has(schemaKey)) {
1201
+ delete spec.components.securitySchemes[schemaKey];
1202
+ }
1203
+ }
1204
+ if (Object.keys(spec.components.securitySchemes).length === 0) {
1205
+ delete spec.components.securitySchemes;
1206
+ }
1207
+ if (Object.keys(spec.components).length === 0) {
1208
+ delete spec.components;
1209
+ }
1210
+ }
1211
+ static removeUnusedTags(spec) {
1212
+ if (!spec.tags) {
1213
+ return;
1214
+ }
1215
+ const usedTags = new Set();
1216
+ for (const pathKey in spec.paths) {
1217
+ for (const methodKey in spec.paths[pathKey]) {
1218
+ const operation = spec.paths[pathKey][methodKey];
1219
+ if (operation.tags) {
1220
+ operation.tags.forEach((tag) => usedTags.add(tag));
1221
+ }
1222
+ }
1223
+ }
1224
+ spec.tags = spec.tags.filter((tagObj) => usedTags.has(tagObj.name));
1225
+ }
1226
+ static removeUserDefinedRootProperty(spec) {
1227
+ for (const key in spec) {
1228
+ if (key.startsWith("x-")) {
1229
+ delete spec[key];
1230
+ }
1231
+ }
1232
+ }
1233
+ static removeUnusedComponents(spec) {
1234
+ const components = spec.components;
1235
+ if (!components) {
1236
+ return;
1237
+ }
1238
+ delete spec.components;
1239
+ const usedComponentsSet = new Set();
1240
+ const specString = JSON.stringify(spec);
1241
+ const componentReferences = SpecOptimizer.getComponentReferences(specString);
1242
+ for (const reference of componentReferences) {
1243
+ this.addComponent(reference, usedComponentsSet, components);
1244
+ }
1245
+ const newComponents = {};
1246
+ for (const componentName of usedComponentsSet) {
1247
+ const parts = componentName.split("/");
1248
+ const component = this.getComponent(componentName, components);
1249
+ if (component) {
1250
+ let current = newComponents;
1251
+ for (let i = 2; i < parts.length; i++) {
1252
+ if (i === parts.length - 1) {
1253
+ current[parts[i]] = component;
1254
+ }
1255
+ else if (!current[parts[i]]) {
1256
+ current[parts[i]] = {};
1257
+ }
1258
+ current = current[parts[i]];
1259
+ }
1260
+ }
1261
+ }
1262
+ // securitySchemes are referenced directly by name, to void issue, just keep them all and use removeUnusedSecuritySchemas to remove unused ones
1263
+ if (components.securitySchemes) {
1264
+ newComponents.securitySchemes = components.securitySchemes;
1265
+ }
1266
+ if (Object.keys(newComponents).length !== 0) {
1267
+ spec.components = newComponents;
1268
+ }
1269
+ }
1270
+ static getComponentReferences(specString) {
1271
+ const matches = Array.from(specString.matchAll(/['"](#\/components\/.+?)['"]/g));
1272
+ const matchResult = matches.map((match) => match[1]);
1273
+ return matchResult;
1274
+ }
1275
+ static getComponent(componentPath, components) {
1276
+ const parts = componentPath.split("/");
1277
+ let current = components;
1278
+ for (const part of parts) {
1279
+ if (part === "#" || part === "components") {
1280
+ continue;
1281
+ }
1282
+ current = current[part];
1283
+ if (!current) {
1284
+ return null;
1285
+ }
1286
+ }
1287
+ return current;
1288
+ }
1289
+ static addComponent(componentName, usedComponentsSet, components) {
1290
+ if (usedComponentsSet.has(componentName)) {
1291
+ return;
1292
+ }
1293
+ usedComponentsSet.add(componentName);
1294
+ const component = this.getComponent(componentName, components);
1295
+ if (component) {
1296
+ const componentString = JSON.stringify(component);
1297
+ const componentReferences = SpecOptimizer.getComponentReferences(componentString);
1298
+ for (const reference of componentReferences) {
1299
+ this.addComponent(reference, usedComponentsSet, components);
1300
+ }
1301
+ }
1302
+ }
1303
+ }
1304
+ SpecOptimizer.defaultOptions = {
1305
+ removeUnusedComponents: true,
1306
+ removeUnusedTags: true,
1307
+ removeUserDefinedRootProperty: true,
1308
+ removeUnusedSecuritySchemas: true,
1309
+ };
1310
+
1128
1311
  // Copyright (c) Microsoft Corporation.
1129
1312
  class SpecFilter {
1130
1313
  static specFilter(filter, unResolveSpec, resolvedSpec, options) {
@@ -1158,7 +1341,7 @@ class SpecFilter {
1158
1341
  }
1159
1342
  }
1160
1343
  newSpec.paths = newPaths;
1161
- return newSpec;
1344
+ return SpecOptimizer.optimize(newSpec);
1162
1345
  }
1163
1346
  catch (err) {
1164
1347
  throw new SpecParserError(err.toString(), exports.ErrorType.FilterSpecFailed);
@@ -1281,7 +1464,7 @@ class AdaptiveCardGenerator {
1281
1464
  {
1282
1465
  type: "Image",
1283
1466
  url: `\${${name}}`,
1284
- $when: `\${${name} != null}`,
1467
+ $when: `\${${name} != null && ${name} != ''}`,
1285
1468
  },
1286
1469
  ];
1287
1470
  }
@@ -1290,7 +1473,7 @@ class AdaptiveCardGenerator {
1290
1473
  {
1291
1474
  type: "Image",
1292
1475
  url: "${$data}",
1293
- $when: "${$data != null}",
1476
+ $when: "${$data != null && $data != ''}",
1294
1477
  },
1295
1478
  ];
1296
1479
  }
@@ -1383,7 +1566,7 @@ function inferPreviewCardTemplate(card) {
1383
1566
  result.image = {
1384
1567
  url: `\${${inferredProperties.imageUrl}}`,
1385
1568
  alt: `\${if(${inferredProperties.imageUrl}, ${inferredProperties.imageUrl}, 'N/A')}`,
1386
- $when: `\${${inferredProperties.imageUrl} != null}`,
1569
+ $when: `\${${inferredProperties.imageUrl} != null && ${inferredProperties.imageUrl} != ''}`,
1387
1570
  };
1388
1571
  }
1389
1572
  return result;
@@ -1528,6 +1711,7 @@ class ManifestUpdater {
1528
1711
  const confirmationBodies = [];
1529
1712
  if (operationItem) {
1530
1713
  const operationId = operationItem.operationId;
1714
+ const safeFunctionName = operationId.replace(/[^a-zA-Z0-9]/g, "_");
1531
1715
  const description = (_a = operationItem.description) !== null && _a !== void 0 ? _a : "";
1532
1716
  const summary = operationItem.summary;
1533
1717
  const paramObject = operationItem.parameters;
@@ -1555,7 +1739,7 @@ class ManifestUpdater {
1555
1739
  }
1556
1740
  }
1557
1741
  const funcObj = {
1558
- name: operationId,
1742
+ name: safeFunctionName,
1559
1743
  description: description,
1560
1744
  };
1561
1745
  if (options.allowResponseSemantics) {
@@ -1563,7 +1747,7 @@ class ManifestUpdater {
1563
1747
  const { json } = Utils.getResponseJson(operationItem);
1564
1748
  if (json.schema) {
1565
1749
  const [card, jsonPath] = AdaptiveCardGenerator.generateAdaptiveCard(operationItem);
1566
- card.body = card.body.slice(0, 5);
1750
+ card.body = Utils.limitACBodyProperties(card.body, 5);
1567
1751
  const responseSemantic = wrapResponseSemantics(card, jsonPath);
1568
1752
  funcObj.capabilities = {
1569
1753
  response_semantics: responseSemantic,
@@ -1591,7 +1775,7 @@ class ManifestUpdater {
1591
1775
  }
1592
1776
  }
1593
1777
  functions.push(funcObj);
1594
- functionNames.push(operationId);
1778
+ functionNames.push(safeFunctionName);
1595
1779
  const conversationStarterStr = (summary !== null && summary !== void 0 ? summary : description).slice(0, ConstantString.ConversationStarterMaxLens);
1596
1780
  if (conversationStarterStr) {
1597
1781
  conversationStarters.push(conversationStarterStr);
@@ -1929,19 +2113,36 @@ class SpecParser {
1929
2113
  isValid: isValid,
1930
2114
  reason: reason,
1931
2115
  };
1932
- if (isValid) {
2116
+ // Try best to parse server url and auth type
2117
+ try {
1933
2118
  const serverObj = Utils.getServerObject(spec, method.toLocaleLowerCase(), path);
1934
2119
  if (serverObj) {
1935
2120
  apiResult.server = serverObj.url;
1936
2121
  }
2122
+ }
2123
+ catch (err) {
2124
+ // ignore
2125
+ }
2126
+ try {
1937
2127
  const authArray = Utils.getAuthArray(operation.security, spec);
1938
- for (const auths of authArray) {
1939
- if (auths.length === 1) {
1940
- apiResult.auth = auths[0];
1941
- break;
2128
+ if (authArray.length !== 0) {
2129
+ for (const auths of authArray) {
2130
+ if (auths.length === 1) {
2131
+ apiResult.auth = auths[0];
2132
+ break;
2133
+ }
2134
+ else {
2135
+ apiResult.auth = {
2136
+ authScheme: { type: "multipleAuth" },
2137
+ name: auths.map((auth) => auth.name).join(", "),
2138
+ };
2139
+ }
1942
2140
  }
1943
2141
  }
1944
2142
  }
2143
+ catch (err) {
2144
+ // ignore
2145
+ }
1945
2146
  result.APIs.push(apiResult);
1946
2147
  }
1947
2148
  result.allAPICount = result.APIs.length;
@@ -2052,10 +2253,11 @@ class SpecParser {
2052
2253
  const operation = newSpec.paths[url][method];
2053
2254
  try {
2054
2255
  const [card, jsonPath] = AdaptiveCardGenerator.generateAdaptiveCard(operation);
2055
- const fileName = path__default['default'].join(adaptiveCardFolder, `${operation.operationId}.json`);
2256
+ const safeAdaptiveCardName = operation.operationId.replace(/[^a-zA-Z0-9]/g, "_");
2257
+ const fileName = path__default['default'].join(adaptiveCardFolder, `${safeAdaptiveCardName}.json`);
2056
2258
  const wrappedCard = wrapAdaptiveCard(card, jsonPath);
2057
2259
  yield fs__default['default'].outputJSON(fileName, wrappedCard, { spaces: 2 });
2058
- const dataFileName = path__default['default'].join(adaptiveCardFolder, `${operation.operationId}.data.json`);
2260
+ const dataFileName = path__default['default'].join(adaptiveCardFolder, `${safeAdaptiveCardName}.data.json`);
2059
2261
  yield fs__default['default'].outputJSON(dataFileName, {}, { spaces: 2 });
2060
2262
  }
2061
2263
  catch (err) {