@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.
@@ -103,6 +103,8 @@ exports.WarningType = void 0;
103
103
  WarningType["OperationOnlyContainsOptionalParam"] = "operation-only-contains-optional-param";
104
104
  WarningType["ConvertSwaggerToOpenAPI"] = "convert-swagger-to-openapi";
105
105
  WarningType["FuncDescriptionTooLong"] = "function-description-too-long";
106
+ WarningType["OperationIdContainsSpecialCharacters"] = "operationid-contains-special-characters";
107
+ WarningType["GenerateJsonDataFailed"] = "generate-json-data-failed";
106
108
  WarningType["Unknown"] = "unknown";
107
109
  })(exports.WarningType || (exports.WarningType = {}));
108
110
  /**
@@ -140,8 +142,10 @@ ConstantString.ConvertSwaggerToOpenAPI = "The Swagger 2.0 file has been converte
140
142
  ConstantString.SwaggerNotSupported = "Swagger 2.0 is not supported. Please convert to OpenAPI 3.0 manually before proceeding.";
141
143
  ConstantString.SpecVersionNotSupported = "Unsupported OpenAPI version %s. Please use version 3.0.x.";
142
144
  ConstantString.MultipleAuthNotSupported = "Multiple authentication methods are unsupported. Ensure all selected APIs use identical authentication.";
145
+ ConstantString.OperationIdContainsSpecialCharacters = "Operation id '%s' in OpenAPI description document contained special characters and was renamed to '%s'.";
143
146
  ConstantString.UnsupportedSchema = "Unsupported schema in %s %s: %s";
144
147
  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.";
148
+ ConstantString.GenerateJsonDataFailed = "Failed to generate JSON data for api: %s due to %s.";
145
149
  ConstantString.WrappedCardVersion = "devPreview";
146
150
  ConstantString.WrappedCardSchema = "https://developer.microsoft.com/json-schemas/teams/vDevPreview/MicrosoftTeams.ResponseRenderingTemplate.schema.json";
147
151
  ConstantString.WrappedCardResponseLayout = "list";
@@ -236,16 +240,19 @@ class SpecParserError extends Error {
236
240
  // Copyright (c) Microsoft Corporation.
237
241
  class Utils {
238
242
  static hasNestedObjectInSchema(schema) {
239
- if (schema.type === "object") {
243
+ if (this.isObjectSchema(schema)) {
240
244
  for (const property in schema.properties) {
241
245
  const nestedSchema = schema.properties[property];
242
- if (nestedSchema.type === "object") {
246
+ if (this.isObjectSchema(nestedSchema)) {
243
247
  return true;
244
248
  }
245
249
  }
246
250
  }
247
251
  return false;
248
252
  }
253
+ static isObjectSchema(schema) {
254
+ return schema.type === "object" || (!schema.type && !!schema.properties);
255
+ }
249
256
  static containMultipleMediaTypes(bodyObject) {
250
257
  return Object.keys((bodyObject === null || bodyObject === void 0 ? void 0 : bodyObject.content) || {}).length > 1;
251
258
  }
@@ -474,7 +481,7 @@ class Utils {
474
481
  optionalParams.push(parameter);
475
482
  }
476
483
  }
477
- else if (schema.type === "object") {
484
+ else if (Utils.isObjectSchema(schema)) {
478
485
  const { properties } = schema;
479
486
  for (const property in properties) {
480
487
  let isRequired = false;
@@ -588,29 +595,6 @@ class Utils {
588
595
  const serverUrl = operationServer || methodServer || rootServer;
589
596
  return serverUrl;
590
597
  }
591
- static limitACBodyProperties(body, maxCount) {
592
- const result = [];
593
- let currentCount = 0;
594
- for (const element of body) {
595
- if (element.type === ConstantString.ContainerType) {
596
- const items = this.limitACBodyProperties(element.items, maxCount - currentCount);
597
- result.push({
598
- type: ConstantString.ContainerType,
599
- $data: element.$data,
600
- items: items,
601
- });
602
- currentCount += items.length;
603
- }
604
- else {
605
- result.push(element);
606
- currentCount++;
607
- }
608
- if (currentCount >= maxCount) {
609
- break;
610
- }
611
- }
612
- return result;
613
- }
614
598
  }
615
599
 
616
600
  // Copyright (c) Microsoft Corporation.
@@ -812,7 +796,7 @@ class Validator {
812
796
  }
813
797
  const isRequiredWithoutDefault = isRequired && schema.default === undefined;
814
798
  const isCopilot = this.projectType === exports.ProjectType.Copilot;
815
- if (isCopilot && this.hasNestedObjectInSchema(schema)) {
799
+ if (isCopilot && Utils.hasNestedObjectInSchema(schema)) {
816
800
  paramResult.isValid = false;
817
801
  paramResult.reason = [exports.ErrorType.RequestBodyContainsNestedObject];
818
802
  return paramResult;
@@ -828,7 +812,7 @@ class Validator {
828
812
  paramResult.optionalNum = paramResult.optionalNum + 1;
829
813
  }
830
814
  }
831
- else if (schema.type === "object") {
815
+ else if (Utils.isObjectSchema(schema)) {
832
816
  const { properties } = schema;
833
817
  for (const property in properties) {
834
818
  let isRequired = false;
@@ -864,7 +848,7 @@ class Validator {
864
848
  for (let i = 0; i < paramObject.length; i++) {
865
849
  const param = paramObject[i];
866
850
  const schema = param.schema;
867
- if (isCopilot && this.hasNestedObjectInSchema(schema)) {
851
+ if (isCopilot && Utils.hasNestedObjectInSchema(schema)) {
868
852
  paramResult.isValid = false;
869
853
  paramResult.reason.push(exports.ErrorType.ParamsContainsNestedObject);
870
854
  continue;
@@ -907,17 +891,6 @@ class Validator {
907
891
  }
908
892
  return paramResult;
909
893
  }
910
- hasNestedObjectInSchema(schema) {
911
- if (schema.type === "object") {
912
- for (const property in schema.properties) {
913
- const nestedSchema = schema.properties[property];
914
- if (nestedSchema.type === "object") {
915
- return true;
916
- }
917
- }
918
- }
919
- return false;
920
- }
921
894
  }
922
895
 
923
896
  // Copyright (c) Microsoft Corporation.
@@ -976,7 +949,7 @@ class CopilotValidator extends Validator {
976
949
  const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
977
950
  if (requestJsonBody) {
978
951
  const requestBodySchema = requestJsonBody.schema;
979
- if (requestBodySchema.type !== "object") {
952
+ if (!Utils.isObjectSchema(requestBodySchema)) {
980
953
  result.reason.push(exports.ErrorType.PostBodySchemaIsNotJson);
981
954
  }
982
955
  const requestBodyParamResult = this.checkPostBodySchema(requestBodySchema, requestBody.required);
@@ -1354,20 +1327,157 @@ class SpecFilter {
1354
1327
  }
1355
1328
  }
1356
1329
 
1330
+ // Copyright (c) Microsoft Corporation.
1331
+ class JsonDataGenerator {
1332
+ static generate(schema) {
1333
+ return this.generateMockData(schema);
1334
+ }
1335
+ static generateMockData(schema) {
1336
+ if (this.visitedSchemas.has(schema)) {
1337
+ return null; // Prevent circular reference
1338
+ }
1339
+ this.visitedSchemas.add(schema);
1340
+ let result;
1341
+ if (schema.anyOf) {
1342
+ // Select the first schema in anyOf
1343
+ const selectedSchema = schema.anyOf[0];
1344
+ result = this.generateMockData(selectedSchema);
1345
+ }
1346
+ else if (schema.oneOf) {
1347
+ // Select the first schema in oneOf
1348
+ const selectedSchema = schema.oneOf[0];
1349
+ result = this.generateMockData(selectedSchema);
1350
+ }
1351
+ else if (schema.allOf) {
1352
+ // merge all schemas in allOf
1353
+ result = {};
1354
+ for (const subschema of schema.allOf) {
1355
+ const data = this.generateMockData(subschema);
1356
+ result = Object.assign(Object.assign({}, result), data);
1357
+ }
1358
+ }
1359
+ else {
1360
+ switch (schema.type) {
1361
+ case "string":
1362
+ if (schema.example !== undefined) {
1363
+ result = schema.example;
1364
+ }
1365
+ else if (schema.format) {
1366
+ switch (schema.format) {
1367
+ case "date-time":
1368
+ result = "2024-11-01T05:25:43.593Z";
1369
+ break;
1370
+ case "email":
1371
+ result = "example@example.com";
1372
+ break;
1373
+ case "uuid":
1374
+ result = "123e4567-e89b-12d3-a456-426614174000";
1375
+ break;
1376
+ case "ipv4":
1377
+ result = "192.168.0.1";
1378
+ break;
1379
+ case "ipv6":
1380
+ result = "2001:0db8:85a3:0000:0000:8a2e:0370:7334";
1381
+ break;
1382
+ default:
1383
+ result = "example string";
1384
+ }
1385
+ }
1386
+ else {
1387
+ result = "example string";
1388
+ }
1389
+ break;
1390
+ case "number":
1391
+ if (schema.example !== undefined) {
1392
+ result = schema.example;
1393
+ }
1394
+ else if (schema.format) {
1395
+ switch (schema.format) {
1396
+ case "float":
1397
+ result = 3.14;
1398
+ break;
1399
+ case "double":
1400
+ result = 3.14159;
1401
+ break;
1402
+ default:
1403
+ result = 123;
1404
+ }
1405
+ }
1406
+ else {
1407
+ result = 123;
1408
+ }
1409
+ break;
1410
+ case "integer":
1411
+ if (schema.example !== undefined) {
1412
+ result = schema.example;
1413
+ }
1414
+ else if (schema.format) {
1415
+ switch (schema.format) {
1416
+ case "int32":
1417
+ result = 123456;
1418
+ break;
1419
+ case "int64":
1420
+ result = 123456789;
1421
+ break;
1422
+ default:
1423
+ result = 123;
1424
+ }
1425
+ }
1426
+ else {
1427
+ result = 123;
1428
+ }
1429
+ break;
1430
+ case "boolean":
1431
+ result = schema.example !== undefined ? schema.example : true;
1432
+ break;
1433
+ case "array":
1434
+ result = [this.generateMockData(schema.items)];
1435
+ break;
1436
+ case "object":
1437
+ result = {};
1438
+ if (schema.properties) {
1439
+ for (const key in schema.properties) {
1440
+ result[key] = this.generateMockData(schema.properties[key]);
1441
+ }
1442
+ }
1443
+ break;
1444
+ default:
1445
+ result = schema.example || null;
1446
+ }
1447
+ }
1448
+ this.visitedSchemas.delete(schema);
1449
+ return result;
1450
+ }
1451
+ }
1452
+ JsonDataGenerator.visitedSchemas = new Set();
1453
+
1357
1454
  // Copyright (c) Microsoft Corporation.
1358
1455
  class AdaptiveCardGenerator {
1359
- static generateAdaptiveCard(operationItem, allowMultipleMediaType = false) {
1456
+ static generateAdaptiveCard(operationItem, allowMultipleMediaType = false, maxElementCount = Number.MAX_SAFE_INTEGER) {
1360
1457
  try {
1361
1458
  const { json } = Utils.getResponseJson(operationItem, allowMultipleMediaType);
1362
1459
  let cardBody = [];
1460
+ let jsonData = {};
1461
+ const warnings = [];
1462
+ const operationId = operationItem.operationId;
1363
1463
  let schema = json.schema;
1364
1464
  let jsonPath = "$";
1365
1465
  if (schema && Object.keys(schema).length > 0) {
1466
+ try {
1467
+ jsonData = JsonDataGenerator.generate(schema);
1468
+ }
1469
+ catch (err) {
1470
+ warnings.push({
1471
+ type: exports.WarningType.GenerateJsonDataFailed,
1472
+ content: Utils.format(ConstantString.GenerateJsonDataFailed, operationId, err.toString()),
1473
+ data: operationId,
1474
+ });
1475
+ }
1366
1476
  jsonPath = AdaptiveCardGenerator.getResponseJsonPathFromSchema(schema);
1367
1477
  if (jsonPath !== "$") {
1368
1478
  schema = schema.properties[jsonPath];
1369
1479
  }
1370
- cardBody = AdaptiveCardGenerator.generateCardFromResponse(schema, "");
1480
+ cardBody = AdaptiveCardGenerator.generateCardFromResponse(schema, "", "", maxElementCount);
1371
1481
  }
1372
1482
  // if no schema, try to use example value
1373
1483
  if (cardBody.length === 0 && (json.examples || json.example)) {
@@ -1395,16 +1505,20 @@ class AdaptiveCardGenerator {
1395
1505
  version: ConstantString.AdaptiveCardVersion,
1396
1506
  body: cardBody,
1397
1507
  };
1398
- return [fullCard, jsonPath];
1508
+ return [fullCard, jsonPath, jsonData, warnings];
1399
1509
  }
1400
1510
  catch (err) {
1401
1511
  throw new SpecParserError(err.toString(), exports.ErrorType.GenerateAdaptiveCardFailed);
1402
1512
  }
1403
1513
  }
1404
- static generateCardFromResponse(schema, name, parentArrayName = "") {
1514
+ static generateCardFromResponse(schema, name, parentArrayName = "", maxElementCount = Number.MAX_SAFE_INTEGER, counter = { count: 0 }) {
1515
+ if (counter.count >= maxElementCount) {
1516
+ return [];
1517
+ }
1405
1518
  if (schema.type === "array") {
1406
1519
  // schema.items can be arbitrary object: schema { type: array, items: {} }
1407
1520
  if (Object.keys(schema.items).length === 0) {
1521
+ counter.count++;
1408
1522
  return [
1409
1523
  {
1410
1524
  type: ConstantString.TextBlockType,
@@ -1413,7 +1527,7 @@ class AdaptiveCardGenerator {
1413
1527
  },
1414
1528
  ];
1415
1529
  }
1416
- const obj = AdaptiveCardGenerator.generateCardFromResponse(schema.items, "", name);
1530
+ const obj = AdaptiveCardGenerator.generateCardFromResponse(schema.items, "", name, maxElementCount, counter);
1417
1531
  const template = {
1418
1532
  type: ConstantString.ContainerType,
1419
1533
  $data: name ? `\${${name}}` : "${$root}",
@@ -1423,11 +1537,11 @@ class AdaptiveCardGenerator {
1423
1537
  return [template];
1424
1538
  }
1425
1539
  // some schema may not contain type but contain properties
1426
- if (schema.type === "object" || (!schema.type && schema.properties)) {
1540
+ if (Utils.isObjectSchema(schema)) {
1427
1541
  const { properties } = schema;
1428
1542
  const result = [];
1429
1543
  for (const property in properties) {
1430
- const obj = AdaptiveCardGenerator.generateCardFromResponse(properties[property], name ? `${name}.${property}` : property, parentArrayName);
1544
+ const obj = AdaptiveCardGenerator.generateCardFromResponse(properties[property], name ? `${name}.${property}` : property, parentArrayName, maxElementCount, counter);
1431
1545
  result.push(...obj);
1432
1546
  }
1433
1547
  if (schema.additionalProperties) {
@@ -1440,6 +1554,7 @@ class AdaptiveCardGenerator {
1440
1554
  schema.type === "integer" ||
1441
1555
  schema.type === "boolean" ||
1442
1556
  schema.type === "number") {
1557
+ counter.count++;
1443
1558
  if (!AdaptiveCardGenerator.isImageUrlProperty(schema, name, parentArrayName)) {
1444
1559
  // string in root: "ddd"
1445
1560
  let text = "result: ${$root}";
@@ -1464,24 +1579,17 @@ class AdaptiveCardGenerator {
1464
1579
  ];
1465
1580
  }
1466
1581
  else {
1467
- if (name) {
1468
- return [
1469
- {
1470
- type: "Image",
1471
- url: `\${${name}}`,
1472
- $when: `\${${name} != null && ${name} != ''}`,
1473
- },
1474
- ];
1475
- }
1476
- else {
1477
- return [
1478
- {
1479
- type: "Image",
1480
- url: "${$data}",
1481
- $when: "${$data != null && $data != ''}",
1482
- },
1483
- ];
1484
- }
1582
+ const url = name ? `\${${name}}` : "${$data}";
1583
+ const condition = name
1584
+ ? `\${${name} != null && ${name} != ''}`
1585
+ : "${$data != null && $data != ''}";
1586
+ return [
1587
+ {
1588
+ type: "Image",
1589
+ url,
1590
+ $when: condition,
1591
+ },
1592
+ ];
1485
1593
  }
1486
1594
  }
1487
1595
  if (schema.oneOf || schema.anyOf || schema.not || schema.allOf) {
@@ -1491,7 +1599,7 @@ class AdaptiveCardGenerator {
1491
1599
  }
1492
1600
  // Find the first array property in the response schema object with the well-known name
1493
1601
  static getResponseJsonPathFromSchema(schema) {
1494
- if (schema.type === "object" || (!schema.type && schema.properties)) {
1602
+ if (Utils.isObjectSchema(schema)) {
1495
1603
  const { properties } = schema;
1496
1604
  for (const property in properties) {
1497
1605
  const schema = properties[property];
@@ -1746,7 +1854,7 @@ class ManifestUpdater {
1746
1854
  if (requestBody) {
1747
1855
  const requestJsonBody = requestBody.content["application/json"];
1748
1856
  const requestBodySchema = requestJsonBody.schema;
1749
- if (requestBodySchema.type === "object") {
1857
+ if (Utils.isObjectSchema(requestBodySchema)) {
1750
1858
  for (const property in requestBodySchema.properties) {
1751
1859
  const schema = requestBodySchema.properties[property];
1752
1860
  ManifestUpdater.checkSchema(schema, method, pathUrl);
@@ -1774,8 +1882,7 @@ class ManifestUpdater {
1774
1882
  try {
1775
1883
  const { json } = Utils.getResponseJson(operationItem);
1776
1884
  if (json.schema) {
1777
- const [card, jsonPath] = AdaptiveCardGenerator.generateAdaptiveCard(operationItem);
1778
- card.body = Utils.limitACBodyProperties(card.body, 5);
1885
+ const [card, jsonPath] = AdaptiveCardGenerator.generateAdaptiveCard(operationItem, false, 5);
1779
1886
  const responseSemantic = wrapResponseSemantics(card, jsonPath);
1780
1887
  funcObj.capabilities = {
1781
1888
  response_semantics: responseSemantic,
@@ -2250,6 +2357,24 @@ class SpecParser {
2250
2357
  const newUnResolvedSpec = newSpecs[0];
2251
2358
  const newSpec = newSpecs[1];
2252
2359
  const authInfo = Utils.getAuthInfo(newSpec);
2360
+ const paths = newUnResolvedSpec.paths;
2361
+ for (const pathUrl in paths) {
2362
+ const operations = paths[pathUrl];
2363
+ for (const method in operations) {
2364
+ const operationItem = operations[method];
2365
+ const operationId = operationItem.operationId;
2366
+ const containsSpecialCharacters = /[^a-zA-Z0-9_]/.test(operationId);
2367
+ if (!containsSpecialCharacters) {
2368
+ continue;
2369
+ }
2370
+ operationItem.operationId = operationId.replace(/[^a-zA-Z0-9]/g, "_");
2371
+ result.warnings.push({
2372
+ type: exports.WarningType.OperationIdContainsSpecialCharacters,
2373
+ content: Utils.format(ConstantString.OperationIdContainsSpecialCharacters, operationId, operationItem.operationId),
2374
+ data: operationId,
2375
+ });
2376
+ }
2377
+ }
2253
2378
  yield this.saveFilterSpec(outputSpecPath, newUnResolvedSpec);
2254
2379
  if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
2255
2380
  throw new SpecParserError(ConstantString.CancelledMessage, exports.ErrorType.Cancelled);
@@ -2303,13 +2428,14 @@ class SpecParser {
2303
2428
  if (this.options.allowMethods.includes(method)) {
2304
2429
  const operation = newSpec.paths[url][method];
2305
2430
  try {
2306
- const [card, jsonPath] = AdaptiveCardGenerator.generateAdaptiveCard(operation);
2431
+ const [card, jsonPath, jsonData, warnings] = AdaptiveCardGenerator.generateAdaptiveCard(operation);
2432
+ result.warnings.push(...warnings);
2307
2433
  const safeAdaptiveCardName = operation.operationId.replace(/[^a-zA-Z0-9]/g, "_");
2308
2434
  const fileName = path__default['default'].join(adaptiveCardFolder, `${safeAdaptiveCardName}.json`);
2309
2435
  const wrappedCard = wrapAdaptiveCard(card, jsonPath);
2310
2436
  yield fs__default['default'].outputJSON(fileName, wrappedCard, { spaces: 2 });
2311
2437
  const dataFileName = path__default['default'].join(adaptiveCardFolder, `${safeAdaptiveCardName}.data.json`);
2312
- yield fs__default['default'].outputJSON(dataFileName, {}, { spaces: 2 });
2438
+ yield fs__default['default'].outputJSON(dataFileName, jsonData, { spaces: 2 });
2313
2439
  }
2314
2440
  catch (err) {
2315
2441
  result.allSuccess = false;