@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.
@@ -260,23 +260,28 @@ class Utils {
260
260
  return str.charAt(0).toUpperCase() + str.slice(1);
261
261
  }
262
262
  static getResponseJson(operationObject, allowMultipleMediaType = false) {
263
- var _a, _b;
263
+ var _a;
264
264
  let json = {};
265
265
  let multipleMediaType = false;
266
266
  for (const code of ConstantString.ResponseCodeFor20X) {
267
267
  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 = {};
268
+ if (responseObject === null || responseObject === void 0 ? void 0 : responseObject.content) {
269
+ for (const contentType of Object.keys(responseObject.content)) {
270
+ // json media type can also be "application/json; charset=utf-8"
271
+ if (contentType.indexOf("application/json") >= 0) {
272
+ multipleMediaType = false;
273
+ json = responseObject.content[contentType];
274
+ if (Utils.containMultipleMediaTypes(responseObject)) {
275
+ multipleMediaType = true;
276
+ if (!allowMultipleMediaType) {
277
+ json = {};
278
+ }
279
+ }
280
+ else {
281
+ return { json, multipleMediaType };
282
+ }
275
283
  }
276
284
  }
277
- else {
278
- break;
279
- }
280
285
  }
281
286
  }
282
287
  return { json, multipleMediaType };
@@ -536,6 +541,29 @@ class Utils {
536
541
  const serverUrl = operationServer || methodServer || rootServer;
537
542
  return serverUrl;
538
543
  }
544
+ static limitACBodyProperties(body, maxCount) {
545
+ const result = [];
546
+ let currentCount = 0;
547
+ for (const element of body) {
548
+ if (element.type === ConstantString.ContainerType) {
549
+ const items = this.limitACBodyProperties(element.items, maxCount - currentCount);
550
+ result.push({
551
+ type: ConstantString.ContainerType,
552
+ $data: element.$data,
553
+ items: items,
554
+ });
555
+ currentCount += items.length;
556
+ }
557
+ else {
558
+ result.push(element);
559
+ currentCount++;
560
+ }
561
+ if (currentCount >= maxCount) {
562
+ break;
563
+ }
564
+ }
565
+ return result;
566
+ }
539
567
  }
540
568
 
541
569
  // Copyright (c) Microsoft Corporation.
@@ -1083,6 +1111,161 @@ class ValidatorFactory {
1083
1111
  }
1084
1112
  }
1085
1113
 
1114
+ // Copyright (c) Microsoft Corporation.
1115
+ class SpecOptimizer {
1116
+ static optimize(spec, options) {
1117
+ const mergedOptions = Object.assign(Object.assign({}, SpecOptimizer.defaultOptions), (options !== null && options !== void 0 ? options : {}));
1118
+ const newSpec = JSON.parse(JSON.stringify(spec));
1119
+ if (mergedOptions.removeUserDefinedRootProperty) {
1120
+ SpecOptimizer.removeUserDefinedRootProperty(newSpec);
1121
+ }
1122
+ if (mergedOptions.removeUnusedComponents) {
1123
+ SpecOptimizer.removeUnusedComponents(newSpec);
1124
+ }
1125
+ if (mergedOptions.removeUnusedTags) {
1126
+ SpecOptimizer.removeUnusedTags(newSpec);
1127
+ }
1128
+ if (mergedOptions.removeUnusedSecuritySchemas) {
1129
+ SpecOptimizer.removeUnusedSecuritySchemas(newSpec);
1130
+ }
1131
+ return newSpec;
1132
+ }
1133
+ static removeUnusedSecuritySchemas(spec) {
1134
+ if (!spec.components || !spec.components.securitySchemes) {
1135
+ return;
1136
+ }
1137
+ const usedSecuritySchemas = new Set();
1138
+ for (const pathKey in spec.paths) {
1139
+ for (const methodKey in spec.paths[pathKey]) {
1140
+ const operation = spec.paths[pathKey][methodKey];
1141
+ if (operation.security) {
1142
+ operation.security.forEach((securityReq) => {
1143
+ for (const schemaKey in securityReq) {
1144
+ usedSecuritySchemas.add(schemaKey);
1145
+ }
1146
+ });
1147
+ }
1148
+ }
1149
+ }
1150
+ if (spec.security) {
1151
+ spec.security.forEach((securityReq) => {
1152
+ for (const schemaKey in securityReq) {
1153
+ usedSecuritySchemas.add(schemaKey);
1154
+ }
1155
+ });
1156
+ }
1157
+ for (const schemaKey in spec.components.securitySchemes) {
1158
+ if (!usedSecuritySchemas.has(schemaKey)) {
1159
+ delete spec.components.securitySchemes[schemaKey];
1160
+ }
1161
+ }
1162
+ if (Object.keys(spec.components.securitySchemes).length === 0) {
1163
+ delete spec.components.securitySchemes;
1164
+ }
1165
+ if (Object.keys(spec.components).length === 0) {
1166
+ delete spec.components;
1167
+ }
1168
+ }
1169
+ static removeUnusedTags(spec) {
1170
+ if (!spec.tags) {
1171
+ return;
1172
+ }
1173
+ const usedTags = new Set();
1174
+ for (const pathKey in spec.paths) {
1175
+ for (const methodKey in spec.paths[pathKey]) {
1176
+ const operation = spec.paths[pathKey][methodKey];
1177
+ if (operation.tags) {
1178
+ operation.tags.forEach((tag) => usedTags.add(tag));
1179
+ }
1180
+ }
1181
+ }
1182
+ spec.tags = spec.tags.filter((tagObj) => usedTags.has(tagObj.name));
1183
+ }
1184
+ static removeUserDefinedRootProperty(spec) {
1185
+ for (const key in spec) {
1186
+ if (key.startsWith("x-")) {
1187
+ delete spec[key];
1188
+ }
1189
+ }
1190
+ }
1191
+ static removeUnusedComponents(spec) {
1192
+ const components = spec.components;
1193
+ if (!components) {
1194
+ return;
1195
+ }
1196
+ delete spec.components;
1197
+ const usedComponentsSet = new Set();
1198
+ const specString = JSON.stringify(spec);
1199
+ const componentReferences = SpecOptimizer.getComponentReferences(specString);
1200
+ for (const reference of componentReferences) {
1201
+ this.addComponent(reference, usedComponentsSet, components);
1202
+ }
1203
+ const newComponents = {};
1204
+ for (const componentName of usedComponentsSet) {
1205
+ const parts = componentName.split("/");
1206
+ const component = this.getComponent(componentName, components);
1207
+ if (component) {
1208
+ let current = newComponents;
1209
+ for (let i = 2; i < parts.length; i++) {
1210
+ if (i === parts.length - 1) {
1211
+ current[parts[i]] = component;
1212
+ }
1213
+ else if (!current[parts[i]]) {
1214
+ current[parts[i]] = {};
1215
+ }
1216
+ current = current[parts[i]];
1217
+ }
1218
+ }
1219
+ }
1220
+ // securitySchemes are referenced directly by name, to void issue, just keep them all and use removeUnusedSecuritySchemas to remove unused ones
1221
+ if (components.securitySchemes) {
1222
+ newComponents.securitySchemes = components.securitySchemes;
1223
+ }
1224
+ if (Object.keys(newComponents).length !== 0) {
1225
+ spec.components = newComponents;
1226
+ }
1227
+ }
1228
+ static getComponentReferences(specString) {
1229
+ const matches = Array.from(specString.matchAll(/['"](#\/components\/.+?)['"]/g));
1230
+ const matchResult = matches.map((match) => match[1]);
1231
+ return matchResult;
1232
+ }
1233
+ static getComponent(componentPath, components) {
1234
+ const parts = componentPath.split("/");
1235
+ let current = components;
1236
+ for (const part of parts) {
1237
+ if (part === "#" || part === "components") {
1238
+ continue;
1239
+ }
1240
+ current = current[part];
1241
+ if (!current) {
1242
+ return null;
1243
+ }
1244
+ }
1245
+ return current;
1246
+ }
1247
+ static addComponent(componentName, usedComponentsSet, components) {
1248
+ if (usedComponentsSet.has(componentName)) {
1249
+ return;
1250
+ }
1251
+ usedComponentsSet.add(componentName);
1252
+ const component = this.getComponent(componentName, components);
1253
+ if (component) {
1254
+ const componentString = JSON.stringify(component);
1255
+ const componentReferences = SpecOptimizer.getComponentReferences(componentString);
1256
+ for (const reference of componentReferences) {
1257
+ this.addComponent(reference, usedComponentsSet, components);
1258
+ }
1259
+ }
1260
+ }
1261
+ }
1262
+ SpecOptimizer.defaultOptions = {
1263
+ removeUnusedComponents: true,
1264
+ removeUnusedTags: true,
1265
+ removeUserDefinedRootProperty: true,
1266
+ removeUnusedSecuritySchemas: true,
1267
+ };
1268
+
1086
1269
  // Copyright (c) Microsoft Corporation.
1087
1270
  class SpecFilter {
1088
1271
  static specFilter(filter, unResolveSpec, resolvedSpec, options) {
@@ -1116,7 +1299,7 @@ class SpecFilter {
1116
1299
  }
1117
1300
  }
1118
1301
  newSpec.paths = newPaths;
1119
- return newSpec;
1302
+ return SpecOptimizer.optimize(newSpec);
1120
1303
  }
1121
1304
  catch (err) {
1122
1305
  throw new SpecParserError(err.toString(), ErrorType.FilterSpecFailed);
@@ -1239,7 +1422,7 @@ class AdaptiveCardGenerator {
1239
1422
  {
1240
1423
  type: "Image",
1241
1424
  url: `\${${name}}`,
1242
- $when: `\${${name} != null}`,
1425
+ $when: `\${${name} != null && ${name} != ''}`,
1243
1426
  },
1244
1427
  ];
1245
1428
  }
@@ -1248,7 +1431,7 @@ class AdaptiveCardGenerator {
1248
1431
  {
1249
1432
  type: "Image",
1250
1433
  url: "${$data}",
1251
- $when: "${$data != null}",
1434
+ $when: "${$data != null && $data != ''}",
1252
1435
  },
1253
1436
  ];
1254
1437
  }
@@ -1341,7 +1524,7 @@ function inferPreviewCardTemplate(card) {
1341
1524
  result.image = {
1342
1525
  url: `\${${inferredProperties.imageUrl}}`,
1343
1526
  alt: `\${if(${inferredProperties.imageUrl}, ${inferredProperties.imageUrl}, 'N/A')}`,
1344
- $when: `\${${inferredProperties.imageUrl} != null}`,
1527
+ $when: `\${${inferredProperties.imageUrl} != null && ${inferredProperties.imageUrl} != ''}`,
1345
1528
  };
1346
1529
  }
1347
1530
  return result;
@@ -1483,6 +1666,7 @@ class ManifestUpdater {
1483
1666
  const confirmationBodies = [];
1484
1667
  if (operationItem) {
1485
1668
  const operationId = operationItem.operationId;
1669
+ const safeFunctionName = operationId.replace(/[^a-zA-Z0-9]/g, "_");
1486
1670
  const description = (_a = operationItem.description) !== null && _a !== void 0 ? _a : "";
1487
1671
  const summary = operationItem.summary;
1488
1672
  const paramObject = operationItem.parameters;
@@ -1510,7 +1694,7 @@ class ManifestUpdater {
1510
1694
  }
1511
1695
  }
1512
1696
  const funcObj = {
1513
- name: operationId,
1697
+ name: safeFunctionName,
1514
1698
  description: description,
1515
1699
  };
1516
1700
  if (options.allowResponseSemantics) {
@@ -1518,7 +1702,7 @@ class ManifestUpdater {
1518
1702
  const { json } = Utils.getResponseJson(operationItem);
1519
1703
  if (json.schema) {
1520
1704
  const [card, jsonPath] = AdaptiveCardGenerator.generateAdaptiveCard(operationItem);
1521
- card.body = card.body.slice(0, 5);
1705
+ card.body = Utils.limitACBodyProperties(card.body, 5);
1522
1706
  const responseSemantic = wrapResponseSemantics(card, jsonPath);
1523
1707
  funcObj.capabilities = {
1524
1708
  response_semantics: responseSemantic,
@@ -1546,7 +1730,7 @@ class ManifestUpdater {
1546
1730
  }
1547
1731
  }
1548
1732
  functions.push(funcObj);
1549
- functionNames.push(operationId);
1733
+ functionNames.push(safeFunctionName);
1550
1734
  const conversationStarterStr = (summary !== null && summary !== void 0 ? summary : description).slice(0, ConstantString.ConversationStarterMaxLens);
1551
1735
  if (conversationStarterStr) {
1552
1736
  conversationStarters.push(conversationStarterStr);
@@ -1874,19 +2058,36 @@ class SpecParser {
1874
2058
  isValid: isValid,
1875
2059
  reason: reason,
1876
2060
  };
1877
- if (isValid) {
2061
+ // Try best to parse server url and auth type
2062
+ try {
1878
2063
  const serverObj = Utils.getServerObject(spec, method.toLocaleLowerCase(), path);
1879
2064
  if (serverObj) {
1880
2065
  apiResult.server = serverObj.url;
1881
2066
  }
2067
+ }
2068
+ catch (err) {
2069
+ // ignore
2070
+ }
2071
+ try {
1882
2072
  const authArray = Utils.getAuthArray(operation.security, spec);
1883
- for (const auths of authArray) {
1884
- if (auths.length === 1) {
1885
- apiResult.auth = auths[0];
1886
- break;
2073
+ if (authArray.length !== 0) {
2074
+ for (const auths of authArray) {
2075
+ if (auths.length === 1) {
2076
+ apiResult.auth = auths[0];
2077
+ break;
2078
+ }
2079
+ else {
2080
+ apiResult.auth = {
2081
+ authScheme: { type: "multipleAuth" },
2082
+ name: auths.map((auth) => auth.name).join(", "),
2083
+ };
2084
+ }
1887
2085
  }
1888
2086
  }
1889
2087
  }
2088
+ catch (err) {
2089
+ // ignore
2090
+ }
1890
2091
  result.APIs.push(apiResult);
1891
2092
  }
1892
2093
  result.allAPICount = result.APIs.length;
@@ -1991,10 +2192,11 @@ class SpecParser {
1991
2192
  const operation = newSpec.paths[url][method];
1992
2193
  try {
1993
2194
  const [card, jsonPath] = AdaptiveCardGenerator.generateAdaptiveCard(operation);
1994
- const fileName = path.join(adaptiveCardFolder, `${operation.operationId}.json`);
2195
+ const safeAdaptiveCardName = operation.operationId.replace(/[^a-zA-Z0-9]/g, "_");
2196
+ const fileName = path.join(adaptiveCardFolder, `${safeAdaptiveCardName}.json`);
1995
2197
  const wrappedCard = wrapAdaptiveCard(card, jsonPath);
1996
2198
  await fs.outputJSON(fileName, wrappedCard, { spaces: 2 });
1997
- const dataFileName = path.join(adaptiveCardFolder, `${operation.operationId}.data.json`);
2199
+ const dataFileName = path.join(adaptiveCardFolder, `${safeAdaptiveCardName}.data.json`);
1998
2200
  await fs.outputJSON(dataFileName, {}, { spaces: 2 });
1999
2201
  }
2000
2202
  catch (err) {