@microsoft/m365-spec-parser 0.2.2-alpha.4a7c0c761.0 → 0.2.2-alpha.557dab381.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.
@@ -536,6 +536,28 @@ class Utils {
536
536
  const serverUrl = operationServer || methodServer || rootServer;
537
537
  return serverUrl;
538
538
  }
539
+ static limitACBodyProperties(body, maxCount) {
540
+ const result = [];
541
+ let currentCount = 0;
542
+ for (const element of body) {
543
+ if (element.type === ConstantString.ContainerType) {
544
+ const items = this.limitACBodyProperties(element.items, maxCount - currentCount);
545
+ result.push({
546
+ type: ConstantString.ContainerType,
547
+ $data: element.$data,
548
+ items: items,
549
+ });
550
+ currentCount += items.length;
551
+ }
552
+ else {
553
+ if (currentCount < maxCount) {
554
+ result.push(element);
555
+ currentCount++;
556
+ }
557
+ }
558
+ }
559
+ return result;
560
+ }
539
561
  }
540
562
 
541
563
  // Copyright (c) Microsoft Corporation.
@@ -1083,6 +1105,161 @@ class ValidatorFactory {
1083
1105
  }
1084
1106
  }
1085
1107
 
1108
+ // Copyright (c) Microsoft Corporation.
1109
+ class SpecOptimizer {
1110
+ static optimize(spec, options) {
1111
+ const mergedOptions = Object.assign(Object.assign({}, SpecOptimizer.defaultOptions), (options !== null && options !== void 0 ? options : {}));
1112
+ const newSpec = JSON.parse(JSON.stringify(spec));
1113
+ if (mergedOptions.removeUserDefinedRootProperty) {
1114
+ SpecOptimizer.removeUserDefinedRootProperty(newSpec);
1115
+ }
1116
+ if (mergedOptions.removeUnusedComponents) {
1117
+ SpecOptimizer.removeUnusedComponents(newSpec);
1118
+ }
1119
+ if (mergedOptions.removeUnusedTags) {
1120
+ SpecOptimizer.removeUnusedTags(newSpec);
1121
+ }
1122
+ if (mergedOptions.removeUnusedSecuritySchemas) {
1123
+ SpecOptimizer.removeUnusedSecuritySchemas(newSpec);
1124
+ }
1125
+ return newSpec;
1126
+ }
1127
+ static removeUnusedSecuritySchemas(spec) {
1128
+ if (!spec.components || !spec.components.securitySchemes) {
1129
+ return;
1130
+ }
1131
+ const usedSecuritySchemas = new Set();
1132
+ for (const pathKey in spec.paths) {
1133
+ for (const methodKey in spec.paths[pathKey]) {
1134
+ const operation = spec.paths[pathKey][methodKey];
1135
+ if (operation.security) {
1136
+ operation.security.forEach((securityReq) => {
1137
+ for (const schemaKey in securityReq) {
1138
+ usedSecuritySchemas.add(schemaKey);
1139
+ }
1140
+ });
1141
+ }
1142
+ }
1143
+ }
1144
+ if (spec.security) {
1145
+ spec.security.forEach((securityReq) => {
1146
+ for (const schemaKey in securityReq) {
1147
+ usedSecuritySchemas.add(schemaKey);
1148
+ }
1149
+ });
1150
+ }
1151
+ for (const schemaKey in spec.components.securitySchemes) {
1152
+ if (!usedSecuritySchemas.has(schemaKey)) {
1153
+ delete spec.components.securitySchemes[schemaKey];
1154
+ }
1155
+ }
1156
+ if (Object.keys(spec.components.securitySchemes).length === 0) {
1157
+ delete spec.components.securitySchemes;
1158
+ }
1159
+ if (Object.keys(spec.components).length === 0) {
1160
+ delete spec.components;
1161
+ }
1162
+ }
1163
+ static removeUnusedTags(spec) {
1164
+ if (!spec.tags) {
1165
+ return;
1166
+ }
1167
+ const usedTags = new Set();
1168
+ for (const pathKey in spec.paths) {
1169
+ for (const methodKey in spec.paths[pathKey]) {
1170
+ const operation = spec.paths[pathKey][methodKey];
1171
+ if (operation.tags) {
1172
+ operation.tags.forEach((tag) => usedTags.add(tag));
1173
+ }
1174
+ }
1175
+ }
1176
+ spec.tags = spec.tags.filter((tagObj) => usedTags.has(tagObj.name));
1177
+ }
1178
+ static removeUserDefinedRootProperty(spec) {
1179
+ for (const key in spec) {
1180
+ if (key.startsWith("x-")) {
1181
+ delete spec[key];
1182
+ }
1183
+ }
1184
+ }
1185
+ static removeUnusedComponents(spec) {
1186
+ const components = spec.components;
1187
+ if (!components) {
1188
+ return;
1189
+ }
1190
+ delete spec.components;
1191
+ const usedComponentsSet = new Set();
1192
+ const specString = JSON.stringify(spec);
1193
+ const componentReferences = SpecOptimizer.getComponentReferences(specString);
1194
+ for (const reference of componentReferences) {
1195
+ this.addComponent(reference, usedComponentsSet, components);
1196
+ }
1197
+ const newComponents = {};
1198
+ for (const componentName of usedComponentsSet) {
1199
+ const parts = componentName.split("/");
1200
+ const component = this.getComponent(componentName, components);
1201
+ if (component) {
1202
+ let current = newComponents;
1203
+ for (let i = 2; i < parts.length; i++) {
1204
+ if (i === parts.length - 1) {
1205
+ current[parts[i]] = component;
1206
+ }
1207
+ else if (!current[parts[i]]) {
1208
+ current[parts[i]] = {};
1209
+ }
1210
+ current = current[parts[i]];
1211
+ }
1212
+ }
1213
+ }
1214
+ // securitySchemes are referenced directly by name, to void issue, just keep them all and use removeUnusedSecuritySchemas to remove unused ones
1215
+ if (components.securitySchemes) {
1216
+ newComponents.securitySchemes = components.securitySchemes;
1217
+ }
1218
+ if (Object.keys(newComponents).length !== 0) {
1219
+ spec.components = newComponents;
1220
+ }
1221
+ }
1222
+ static getComponentReferences(specString) {
1223
+ const matches = Array.from(specString.matchAll(/['"](#\/components\/.+?)['"]/g));
1224
+ const matchResult = matches.map((match) => match[1]);
1225
+ return matchResult;
1226
+ }
1227
+ static getComponent(componentPath, components) {
1228
+ const parts = componentPath.split("/");
1229
+ let current = components;
1230
+ for (const part of parts) {
1231
+ if (part === "#" || part === "components") {
1232
+ continue;
1233
+ }
1234
+ current = current[part];
1235
+ if (!current) {
1236
+ return null;
1237
+ }
1238
+ }
1239
+ return current;
1240
+ }
1241
+ static addComponent(componentName, usedComponentsSet, components) {
1242
+ if (usedComponentsSet.has(componentName)) {
1243
+ return;
1244
+ }
1245
+ usedComponentsSet.add(componentName);
1246
+ const component = this.getComponent(componentName, components);
1247
+ if (component) {
1248
+ const componentString = JSON.stringify(component);
1249
+ const componentReferences = SpecOptimizer.getComponentReferences(componentString);
1250
+ for (const reference of componentReferences) {
1251
+ this.addComponent(reference, usedComponentsSet, components);
1252
+ }
1253
+ }
1254
+ }
1255
+ }
1256
+ SpecOptimizer.defaultOptions = {
1257
+ removeUnusedComponents: true,
1258
+ removeUnusedTags: true,
1259
+ removeUserDefinedRootProperty: true,
1260
+ removeUnusedSecuritySchemas: true,
1261
+ };
1262
+
1086
1263
  // Copyright (c) Microsoft Corporation.
1087
1264
  class SpecFilter {
1088
1265
  static specFilter(filter, unResolveSpec, resolvedSpec, options) {
@@ -1116,7 +1293,7 @@ class SpecFilter {
1116
1293
  }
1117
1294
  }
1118
1295
  newSpec.paths = newPaths;
1119
- return newSpec;
1296
+ return SpecOptimizer.optimize(newSpec);
1120
1297
  }
1121
1298
  catch (err) {
1122
1299
  throw new SpecParserError(err.toString(), ErrorType.FilterSpecFailed);
@@ -1239,7 +1416,7 @@ class AdaptiveCardGenerator {
1239
1416
  {
1240
1417
  type: "Image",
1241
1418
  url: `\${${name}}`,
1242
- $when: `\${${name} != null}`,
1419
+ $when: `\${${name} != null && ${name} != ''}`,
1243
1420
  },
1244
1421
  ];
1245
1422
  }
@@ -1248,7 +1425,7 @@ class AdaptiveCardGenerator {
1248
1425
  {
1249
1426
  type: "Image",
1250
1427
  url: "${$data}",
1251
- $when: "${$data != null}",
1428
+ $when: "${$data != null && $data != ''}",
1252
1429
  },
1253
1430
  ];
1254
1431
  }
@@ -1341,7 +1518,7 @@ function inferPreviewCardTemplate(card) {
1341
1518
  result.image = {
1342
1519
  url: `\${${inferredProperties.imageUrl}}`,
1343
1520
  alt: `\${if(${inferredProperties.imageUrl}, ${inferredProperties.imageUrl}, 'N/A')}`,
1344
- $when: `\${${inferredProperties.imageUrl} != null}`,
1521
+ $when: `\${${inferredProperties.imageUrl} != null && ${inferredProperties.imageUrl} != ''}`,
1345
1522
  };
1346
1523
  }
1347
1524
  return result;
@@ -1483,6 +1660,7 @@ class ManifestUpdater {
1483
1660
  const confirmationBodies = [];
1484
1661
  if (operationItem) {
1485
1662
  const operationId = operationItem.operationId;
1663
+ const safeFunctionName = operationId.replace(/[^a-zA-Z0-9]/g, "_");
1486
1664
  const description = (_a = operationItem.description) !== null && _a !== void 0 ? _a : "";
1487
1665
  const summary = operationItem.summary;
1488
1666
  const paramObject = operationItem.parameters;
@@ -1510,7 +1688,7 @@ class ManifestUpdater {
1510
1688
  }
1511
1689
  }
1512
1690
  const funcObj = {
1513
- name: operationId,
1691
+ name: safeFunctionName,
1514
1692
  description: description,
1515
1693
  };
1516
1694
  if (options.allowResponseSemantics) {
@@ -1518,7 +1696,7 @@ class ManifestUpdater {
1518
1696
  const { json } = Utils.getResponseJson(operationItem);
1519
1697
  if (json.schema) {
1520
1698
  const [card, jsonPath] = AdaptiveCardGenerator.generateAdaptiveCard(operationItem);
1521
- card.body = card.body.slice(0, 5);
1699
+ card.body = Utils.limitACBodyProperties(card.body, 5);
1522
1700
  const responseSemantic = wrapResponseSemantics(card, jsonPath);
1523
1701
  funcObj.capabilities = {
1524
1702
  response_semantics: responseSemantic,
@@ -1546,7 +1724,7 @@ class ManifestUpdater {
1546
1724
  }
1547
1725
  }
1548
1726
  functions.push(funcObj);
1549
- functionNames.push(operationId);
1727
+ functionNames.push(safeFunctionName);
1550
1728
  const conversationStarterStr = (summary !== null && summary !== void 0 ? summary : description).slice(0, ConstantString.ConversationStarterMaxLens);
1551
1729
  if (conversationStarterStr) {
1552
1730
  conversationStarters.push(conversationStarterStr);
@@ -1874,19 +2052,36 @@ class SpecParser {
1874
2052
  isValid: isValid,
1875
2053
  reason: reason,
1876
2054
  };
1877
- if (isValid) {
2055
+ // Try best to parse server url and auth type
2056
+ try {
1878
2057
  const serverObj = Utils.getServerObject(spec, method.toLocaleLowerCase(), path);
1879
2058
  if (serverObj) {
1880
2059
  apiResult.server = serverObj.url;
1881
2060
  }
2061
+ }
2062
+ catch (err) {
2063
+ // ignore
2064
+ }
2065
+ try {
1882
2066
  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;
2067
+ if (authArray.length !== 0) {
2068
+ for (const auths of authArray) {
2069
+ if (auths.length === 1) {
2070
+ apiResult.auth = auths[0];
2071
+ break;
2072
+ }
2073
+ else {
2074
+ apiResult.auth = {
2075
+ authScheme: { type: "multipleAuth" },
2076
+ name: auths.map((auth) => auth.name).join(", "),
2077
+ };
2078
+ }
1887
2079
  }
1888
2080
  }
1889
2081
  }
2082
+ catch (err) {
2083
+ // ignore
2084
+ }
1890
2085
  result.APIs.push(apiResult);
1891
2086
  }
1892
2087
  result.allAPICount = result.APIs.length;
@@ -1991,10 +2186,11 @@ class SpecParser {
1991
2186
  const operation = newSpec.paths[url][method];
1992
2187
  try {
1993
2188
  const [card, jsonPath] = AdaptiveCardGenerator.generateAdaptiveCard(operation);
1994
- const fileName = path.join(adaptiveCardFolder, `${operation.operationId}.json`);
2189
+ const safeAdaptiveCardName = operation.operationId.replace(/[^a-zA-Z0-9]/g, "_");
2190
+ const fileName = path.join(adaptiveCardFolder, `${safeAdaptiveCardName}.json`);
1995
2191
  const wrappedCard = wrapAdaptiveCard(card, jsonPath);
1996
2192
  await fs.outputJSON(fileName, wrappedCard, { spaces: 2 });
1997
- const dataFileName = path.join(adaptiveCardFolder, `${operation.operationId}.data.json`);
2193
+ const dataFileName = path.join(adaptiveCardFolder, `${safeAdaptiveCardName}.data.json`);
1998
2194
  await fs.outputJSON(dataFileName, {}, { spaces: 2 });
1999
2195
  }
2000
2196
  catch (err) {