@microsoft/m365-spec-parser 0.2.2 → 0.2.3-alpha.07f6df81c.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.
@@ -61,6 +61,8 @@ var WarningType;
61
61
  WarningType["OperationOnlyContainsOptionalParam"] = "operation-only-contains-optional-param";
62
62
  WarningType["ConvertSwaggerToOpenAPI"] = "convert-swagger-to-openapi";
63
63
  WarningType["FuncDescriptionTooLong"] = "function-description-too-long";
64
+ WarningType["OperationIdContainsSpecialCharacters"] = "operationid-contains-special-characters";
65
+ WarningType["GenerateJsonDataFailed"] = "generate-json-data-failed";
64
66
  WarningType["Unknown"] = "unknown";
65
67
  })(WarningType || (WarningType = {}));
66
68
  /**
@@ -98,8 +100,10 @@ ConstantString.ConvertSwaggerToOpenAPI = "The Swagger 2.0 file has been converte
98
100
  ConstantString.SwaggerNotSupported = "Swagger 2.0 is not supported. Please convert to OpenAPI 3.0 manually before proceeding.";
99
101
  ConstantString.SpecVersionNotSupported = "Unsupported OpenAPI version %s. Please use version 3.0.x.";
100
102
  ConstantString.MultipleAuthNotSupported = "Multiple authentication methods are unsupported. Ensure all selected APIs use identical authentication.";
103
+ ConstantString.OperationIdContainsSpecialCharacters = "Operation id '%s' in OpenAPI description document contained special characters and was renamed to '%s'.";
101
104
  ConstantString.UnsupportedSchema = "Unsupported schema in %s %s: %s";
102
105
  ConstantString.FuncDescriptionTooLong = "The description of the function '%s' is too long. The current length is %s characters, while the maximum allowed length is %s characters.";
106
+ ConstantString.GenerateJsonDataFailed = "Failed to generate JSON data for api: %s due to %s.";
103
107
  ConstantString.WrappedCardVersion = "devPreview";
104
108
  ConstantString.WrappedCardSchema = "https://developer.microsoft.com/json-schemas/teams/vDevPreview/MicrosoftTeams.ResponseRenderingTemplate.schema.json";
105
109
  ConstantString.WrappedCardResponseLayout = "list";
@@ -194,16 +198,19 @@ class SpecParserError extends Error {
194
198
  // Copyright (c) Microsoft Corporation.
195
199
  class Utils {
196
200
  static hasNestedObjectInSchema(schema) {
197
- if (schema.type === "object") {
201
+ if (this.isObjectSchema(schema)) {
198
202
  for (const property in schema.properties) {
199
203
  const nestedSchema = schema.properties[property];
200
- if (nestedSchema.type === "object") {
204
+ if (this.isObjectSchema(nestedSchema)) {
201
205
  return true;
202
206
  }
203
207
  }
204
208
  }
205
209
  return false;
206
210
  }
211
+ static isObjectSchema(schema) {
212
+ return schema.type === "object" || (!schema.type && !!schema.properties);
213
+ }
207
214
  static containMultipleMediaTypes(bodyObject) {
208
215
  return Object.keys((bodyObject === null || bodyObject === void 0 ? void 0 : bodyObject.content) || {}).length > 1;
209
216
  }
@@ -432,7 +439,7 @@ class Utils {
432
439
  optionalParams.push(parameter);
433
440
  }
434
441
  }
435
- else if (schema.type === "object") {
442
+ else if (Utils.isObjectSchema(schema)) {
436
443
  const { properties } = schema;
437
444
  for (const property in properties) {
438
445
  let isRequired = false;
@@ -546,29 +553,6 @@ class Utils {
546
553
  const serverUrl = operationServer || methodServer || rootServer;
547
554
  return serverUrl;
548
555
  }
549
- static limitACBodyProperties(body, maxCount) {
550
- const result = [];
551
- let currentCount = 0;
552
- for (const element of body) {
553
- if (element.type === ConstantString.ContainerType) {
554
- const items = this.limitACBodyProperties(element.items, maxCount - currentCount);
555
- result.push({
556
- type: ConstantString.ContainerType,
557
- $data: element.$data,
558
- items: items,
559
- });
560
- currentCount += items.length;
561
- }
562
- else {
563
- result.push(element);
564
- currentCount++;
565
- }
566
- if (currentCount >= maxCount) {
567
- break;
568
- }
569
- }
570
- return result;
571
- }
572
556
  }
573
557
 
574
558
  // Copyright (c) Microsoft Corporation.
@@ -770,7 +754,7 @@ class Validator {
770
754
  }
771
755
  const isRequiredWithoutDefault = isRequired && schema.default === undefined;
772
756
  const isCopilot = this.projectType === ProjectType.Copilot;
773
- if (isCopilot && this.hasNestedObjectInSchema(schema)) {
757
+ if (isCopilot && Utils.hasNestedObjectInSchema(schema)) {
774
758
  paramResult.isValid = false;
775
759
  paramResult.reason = [ErrorType.RequestBodyContainsNestedObject];
776
760
  return paramResult;
@@ -786,7 +770,7 @@ class Validator {
786
770
  paramResult.optionalNum = paramResult.optionalNum + 1;
787
771
  }
788
772
  }
789
- else if (schema.type === "object") {
773
+ else if (Utils.isObjectSchema(schema)) {
790
774
  const { properties } = schema;
791
775
  for (const property in properties) {
792
776
  let isRequired = false;
@@ -822,7 +806,7 @@ class Validator {
822
806
  for (let i = 0; i < paramObject.length; i++) {
823
807
  const param = paramObject[i];
824
808
  const schema = param.schema;
825
- if (isCopilot && this.hasNestedObjectInSchema(schema)) {
809
+ if (isCopilot && Utils.hasNestedObjectInSchema(schema)) {
826
810
  paramResult.isValid = false;
827
811
  paramResult.reason.push(ErrorType.ParamsContainsNestedObject);
828
812
  continue;
@@ -865,17 +849,6 @@ class Validator {
865
849
  }
866
850
  return paramResult;
867
851
  }
868
- hasNestedObjectInSchema(schema) {
869
- if (schema.type === "object") {
870
- for (const property in schema.properties) {
871
- const nestedSchema = schema.properties[property];
872
- if (nestedSchema.type === "object") {
873
- return true;
874
- }
875
- }
876
- }
877
- return false;
878
- }
879
852
  }
880
853
 
881
854
  // Copyright (c) Microsoft Corporation.
@@ -934,7 +907,7 @@ class CopilotValidator extends Validator {
934
907
  const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
935
908
  if (requestJsonBody) {
936
909
  const requestBodySchema = requestJsonBody.schema;
937
- if (requestBodySchema.type !== "object") {
910
+ if (!Utils.isObjectSchema(requestBodySchema)) {
938
911
  result.reason.push(ErrorType.PostBodySchemaIsNotJson);
939
912
  }
940
913
  const requestBodyParamResult = this.checkPostBodySchema(requestBodySchema, requestBody.required);
@@ -1312,20 +1285,157 @@ class SpecFilter {
1312
1285
  }
1313
1286
  }
1314
1287
 
1288
+ // Copyright (c) Microsoft Corporation.
1289
+ class JsonDataGenerator {
1290
+ static generate(schema) {
1291
+ return this.generateMockData(schema);
1292
+ }
1293
+ static generateMockData(schema) {
1294
+ if (this.visitedSchemas.has(schema)) {
1295
+ return null; // Prevent circular reference
1296
+ }
1297
+ this.visitedSchemas.add(schema);
1298
+ let result;
1299
+ if (schema.anyOf) {
1300
+ // Select the first schema in anyOf
1301
+ const selectedSchema = schema.anyOf[0];
1302
+ result = this.generateMockData(selectedSchema);
1303
+ }
1304
+ else if (schema.oneOf) {
1305
+ // Select the first schema in oneOf
1306
+ const selectedSchema = schema.oneOf[0];
1307
+ result = this.generateMockData(selectedSchema);
1308
+ }
1309
+ else if (schema.allOf) {
1310
+ // merge all schemas in allOf
1311
+ result = {};
1312
+ for (const subschema of schema.allOf) {
1313
+ const data = this.generateMockData(subschema);
1314
+ result = Object.assign(Object.assign({}, result), data);
1315
+ }
1316
+ }
1317
+ else {
1318
+ switch (schema.type) {
1319
+ case "string":
1320
+ if (schema.example !== undefined) {
1321
+ result = schema.example;
1322
+ }
1323
+ else if (schema.format) {
1324
+ switch (schema.format) {
1325
+ case "date-time":
1326
+ result = "2024-11-01T05:25:43.593Z";
1327
+ break;
1328
+ case "email":
1329
+ result = "example@example.com";
1330
+ break;
1331
+ case "uuid":
1332
+ result = "123e4567-e89b-12d3-a456-426614174000";
1333
+ break;
1334
+ case "ipv4":
1335
+ result = "192.168.0.1";
1336
+ break;
1337
+ case "ipv6":
1338
+ result = "2001:0db8:85a3:0000:0000:8a2e:0370:7334";
1339
+ break;
1340
+ default:
1341
+ result = "example string";
1342
+ }
1343
+ }
1344
+ else {
1345
+ result = "example string";
1346
+ }
1347
+ break;
1348
+ case "number":
1349
+ if (schema.example !== undefined) {
1350
+ result = schema.example;
1351
+ }
1352
+ else if (schema.format) {
1353
+ switch (schema.format) {
1354
+ case "float":
1355
+ result = 3.14;
1356
+ break;
1357
+ case "double":
1358
+ result = 3.14159;
1359
+ break;
1360
+ default:
1361
+ result = 123;
1362
+ }
1363
+ }
1364
+ else {
1365
+ result = 123;
1366
+ }
1367
+ break;
1368
+ case "integer":
1369
+ if (schema.example !== undefined) {
1370
+ result = schema.example;
1371
+ }
1372
+ else if (schema.format) {
1373
+ switch (schema.format) {
1374
+ case "int32":
1375
+ result = 123456;
1376
+ break;
1377
+ case "int64":
1378
+ result = 123456789;
1379
+ break;
1380
+ default:
1381
+ result = 123;
1382
+ }
1383
+ }
1384
+ else {
1385
+ result = 123;
1386
+ }
1387
+ break;
1388
+ case "boolean":
1389
+ result = schema.example !== undefined ? schema.example : true;
1390
+ break;
1391
+ case "array":
1392
+ result = [this.generateMockData(schema.items)];
1393
+ break;
1394
+ case "object":
1395
+ result = {};
1396
+ if (schema.properties) {
1397
+ for (const key in schema.properties) {
1398
+ result[key] = this.generateMockData(schema.properties[key]);
1399
+ }
1400
+ }
1401
+ break;
1402
+ default:
1403
+ result = schema.example || null;
1404
+ }
1405
+ }
1406
+ this.visitedSchemas.delete(schema);
1407
+ return result;
1408
+ }
1409
+ }
1410
+ JsonDataGenerator.visitedSchemas = new Set();
1411
+
1315
1412
  // Copyright (c) Microsoft Corporation.
1316
1413
  class AdaptiveCardGenerator {
1317
- static generateAdaptiveCard(operationItem, allowMultipleMediaType = false) {
1414
+ static generateAdaptiveCard(operationItem, allowMultipleMediaType = false, maxElementCount = Number.MAX_SAFE_INTEGER) {
1318
1415
  try {
1319
1416
  const { json } = Utils.getResponseJson(operationItem, allowMultipleMediaType);
1320
1417
  let cardBody = [];
1418
+ let jsonData = {};
1419
+ const warnings = [];
1420
+ const operationId = operationItem.operationId;
1321
1421
  let schema = json.schema;
1322
1422
  let jsonPath = "$";
1323
1423
  if (schema && Object.keys(schema).length > 0) {
1424
+ try {
1425
+ jsonData = JsonDataGenerator.generate(schema);
1426
+ }
1427
+ catch (err) {
1428
+ warnings.push({
1429
+ type: WarningType.GenerateJsonDataFailed,
1430
+ content: Utils.format(ConstantString.GenerateJsonDataFailed, operationId, err.toString()),
1431
+ data: operationId,
1432
+ });
1433
+ }
1324
1434
  jsonPath = AdaptiveCardGenerator.getResponseJsonPathFromSchema(schema);
1325
1435
  if (jsonPath !== "$") {
1326
1436
  schema = schema.properties[jsonPath];
1327
1437
  }
1328
- cardBody = AdaptiveCardGenerator.generateCardFromResponse(schema, "");
1438
+ cardBody = AdaptiveCardGenerator.generateCardFromResponse(schema, "", "", maxElementCount);
1329
1439
  }
1330
1440
  // if no schema, try to use example value
1331
1441
  if (cardBody.length === 0 && (json.examples || json.example)) {
@@ -1353,16 +1463,20 @@ class AdaptiveCardGenerator {
1353
1463
  version: ConstantString.AdaptiveCardVersion,
1354
1464
  body: cardBody,
1355
1465
  };
1356
- return [fullCard, jsonPath];
1466
+ return [fullCard, jsonPath, jsonData, warnings];
1357
1467
  }
1358
1468
  catch (err) {
1359
1469
  throw new SpecParserError(err.toString(), ErrorType.GenerateAdaptiveCardFailed);
1360
1470
  }
1361
1471
  }
1362
- static generateCardFromResponse(schema, name, parentArrayName = "") {
1472
+ static generateCardFromResponse(schema, name, parentArrayName = "", maxElementCount = Number.MAX_SAFE_INTEGER, counter = { count: 0 }) {
1473
+ if (counter.count >= maxElementCount) {
1474
+ return [];
1475
+ }
1363
1476
  if (schema.type === "array") {
1364
1477
  // schema.items can be arbitrary object: schema { type: array, items: {} }
1365
1478
  if (Object.keys(schema.items).length === 0) {
1479
+ counter.count++;
1366
1480
  return [
1367
1481
  {
1368
1482
  type: ConstantString.TextBlockType,
@@ -1371,7 +1485,7 @@ class AdaptiveCardGenerator {
1371
1485
  },
1372
1486
  ];
1373
1487
  }
1374
- const obj = AdaptiveCardGenerator.generateCardFromResponse(schema.items, "", name);
1488
+ const obj = AdaptiveCardGenerator.generateCardFromResponse(schema.items, "", name, maxElementCount, counter);
1375
1489
  const template = {
1376
1490
  type: ConstantString.ContainerType,
1377
1491
  $data: name ? `\${${name}}` : "${$root}",
@@ -1381,11 +1495,11 @@ class AdaptiveCardGenerator {
1381
1495
  return [template];
1382
1496
  }
1383
1497
  // some schema may not contain type but contain properties
1384
- if (schema.type === "object" || (!schema.type && schema.properties)) {
1498
+ if (Utils.isObjectSchema(schema)) {
1385
1499
  const { properties } = schema;
1386
1500
  const result = [];
1387
1501
  for (const property in properties) {
1388
- const obj = AdaptiveCardGenerator.generateCardFromResponse(properties[property], name ? `${name}.${property}` : property, parentArrayName);
1502
+ const obj = AdaptiveCardGenerator.generateCardFromResponse(properties[property], name ? `${name}.${property}` : property, parentArrayName, maxElementCount, counter);
1389
1503
  result.push(...obj);
1390
1504
  }
1391
1505
  if (schema.additionalProperties) {
@@ -1398,6 +1512,7 @@ class AdaptiveCardGenerator {
1398
1512
  schema.type === "integer" ||
1399
1513
  schema.type === "boolean" ||
1400
1514
  schema.type === "number") {
1515
+ counter.count++;
1401
1516
  if (!AdaptiveCardGenerator.isImageUrlProperty(schema, name, parentArrayName)) {
1402
1517
  // string in root: "ddd"
1403
1518
  let text = "result: ${$root}";
@@ -1422,24 +1537,17 @@ class AdaptiveCardGenerator {
1422
1537
  ];
1423
1538
  }
1424
1539
  else {
1425
- if (name) {
1426
- return [
1427
- {
1428
- type: "Image",
1429
- url: `\${${name}}`,
1430
- $when: `\${${name} != null && ${name} != ''}`,
1431
- },
1432
- ];
1433
- }
1434
- else {
1435
- return [
1436
- {
1437
- type: "Image",
1438
- url: "${$data}",
1439
- $when: "${$data != null && $data != ''}",
1440
- },
1441
- ];
1442
- }
1540
+ const url = name ? `\${${name}}` : "${$data}";
1541
+ const condition = name
1542
+ ? `\${${name} != null && ${name} != ''}`
1543
+ : "${$data != null && $data != ''}";
1544
+ return [
1545
+ {
1546
+ type: "Image",
1547
+ url,
1548
+ $when: condition,
1549
+ },
1550
+ ];
1443
1551
  }
1444
1552
  }
1445
1553
  if (schema.oneOf || schema.anyOf || schema.not || schema.allOf) {
@@ -1449,7 +1557,7 @@ class AdaptiveCardGenerator {
1449
1557
  }
1450
1558
  // Find the first array property in the response schema object with the well-known name
1451
1559
  static getResponseJsonPathFromSchema(schema) {
1452
- if (schema.type === "object" || (!schema.type && schema.properties)) {
1560
+ if (Utils.isObjectSchema(schema)) {
1453
1561
  const { properties } = schema;
1454
1562
  for (const property in properties) {
1455
1563
  const schema = properties[property];
@@ -1701,7 +1809,7 @@ class ManifestUpdater {
1701
1809
  if (requestBody) {
1702
1810
  const requestJsonBody = requestBody.content["application/json"];
1703
1811
  const requestBodySchema = requestJsonBody.schema;
1704
- if (requestBodySchema.type === "object") {
1812
+ if (Utils.isObjectSchema(requestBodySchema)) {
1705
1813
  for (const property in requestBodySchema.properties) {
1706
1814
  const schema = requestBodySchema.properties[property];
1707
1815
  ManifestUpdater.checkSchema(schema, method, pathUrl);
@@ -1729,8 +1837,7 @@ class ManifestUpdater {
1729
1837
  try {
1730
1838
  const { json } = Utils.getResponseJson(operationItem);
1731
1839
  if (json.schema) {
1732
- const [card, jsonPath] = AdaptiveCardGenerator.generateAdaptiveCard(operationItem);
1733
- card.body = Utils.limitACBodyProperties(card.body, 5);
1840
+ const [card, jsonPath] = AdaptiveCardGenerator.generateAdaptiveCard(operationItem, false, 5);
1734
1841
  const responseSemantic = wrapResponseSemantics(card, jsonPath);
1735
1842
  funcObj.capabilities = {
1736
1843
  response_semantics: responseSemantic,
@@ -2191,6 +2298,24 @@ class SpecParser {
2191
2298
  const newUnResolvedSpec = newSpecs[0];
2192
2299
  const newSpec = newSpecs[1];
2193
2300
  const authInfo = Utils.getAuthInfo(newSpec);
2301
+ const paths = newUnResolvedSpec.paths;
2302
+ for (const pathUrl in paths) {
2303
+ const operations = paths[pathUrl];
2304
+ for (const method in operations) {
2305
+ const operationItem = operations[method];
2306
+ const operationId = operationItem.operationId;
2307
+ const containsSpecialCharacters = /[^a-zA-Z0-9_]/.test(operationId);
2308
+ if (!containsSpecialCharacters) {
2309
+ continue;
2310
+ }
2311
+ operationItem.operationId = operationId.replace(/[^a-zA-Z0-9]/g, "_");
2312
+ result.warnings.push({
2313
+ type: WarningType.OperationIdContainsSpecialCharacters,
2314
+ content: Utils.format(ConstantString.OperationIdContainsSpecialCharacters, operationId, operationItem.operationId),
2315
+ data: operationId,
2316
+ });
2317
+ }
2318
+ }
2194
2319
  await this.saveFilterSpec(outputSpecPath, newUnResolvedSpec);
2195
2320
  if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
2196
2321
  throw new SpecParserError(ConstantString.CancelledMessage, ErrorType.Cancelled);
@@ -2242,13 +2367,14 @@ class SpecParser {
2242
2367
  if (this.options.allowMethods.includes(method)) {
2243
2368
  const operation = newSpec.paths[url][method];
2244
2369
  try {
2245
- const [card, jsonPath] = AdaptiveCardGenerator.generateAdaptiveCard(operation);
2370
+ const [card, jsonPath, jsonData, warnings] = AdaptiveCardGenerator.generateAdaptiveCard(operation);
2371
+ result.warnings.push(...warnings);
2246
2372
  const safeAdaptiveCardName = operation.operationId.replace(/[^a-zA-Z0-9]/g, "_");
2247
2373
  const fileName = path.join(adaptiveCardFolder, `${safeAdaptiveCardName}.json`);
2248
2374
  const wrappedCard = wrapAdaptiveCard(card, jsonPath);
2249
2375
  await fs.outputJSON(fileName, wrappedCard, { spaces: 2 });
2250
2376
  const dataFileName = path.join(adaptiveCardFolder, `${safeAdaptiveCardName}.data.json`);
2251
- await fs.outputJSON(dataFileName, {}, { spaces: 2 });
2377
+ await fs.outputJSON(dataFileName, jsonData, { spaces: 2 });
2252
2378
  }
2253
2379
  catch (err) {
2254
2380
  result.allSuccess = false;