@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.esm2017.mjs
CHANGED
|
@@ -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
|
|
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 (
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
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:
|
|
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
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
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
|
|
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, `${
|
|
2199
|
+
const dataFileName = path.join(adaptiveCardFolder, `${safeAdaptiveCardName}.data.json`);
|
|
1998
2200
|
await fs.outputJSON(dataFileName, {}, { spaces: 2 });
|
|
1999
2201
|
}
|
|
2000
2202
|
catch (err) {
|