@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.
- package/dist/index.esm2017.js +41 -13
- package/dist/index.esm2017.js.map +1 -1
- package/dist/index.esm2017.mjs +227 -25
- package/dist/index.esm2017.mjs.map +1 -1
- package/dist/index.esm5.js +41 -13
- package/dist/index.esm5.js.map +1 -1
- package/dist/index.node.cjs.js +227 -25
- package/dist/index.node.cjs.js.map +1 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/interfaces.d.ts +7 -3
- package/dist/src/specOptimizer.d.ts +18 -0
- package/dist/src/utils.d.ts +5 -4
- package/package.json +3 -3
package/dist/index.node.cjs.js
CHANGED
|
@@ -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
|
|
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 (
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
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:
|
|
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
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
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
|
|
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, `${
|
|
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) {
|