@microsoft/m365-spec-parser 0.2.1 → 0.2.2-alpha.0921562c9.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.
@@ -59,10 +59,12 @@ exports.ErrorType = void 0;
59
59
  ErrorType["RelativeServerUrlNotSupported"] = "relative-server-url-not-supported";
60
60
  ErrorType["NoSupportedApi"] = "no-supported-api";
61
61
  ErrorType["NoExtraAPICanBeAdded"] = "no-extra-api-can-be-added";
62
+ ErrorType["AddedAPINotInOriginalSpec"] = "added-api-not-in-original-spec";
62
63
  ErrorType["ResolveServerUrlFailed"] = "resolve-server-url-failed";
63
64
  ErrorType["SwaggerNotSupported"] = "swagger-not-supported";
64
65
  ErrorType["MultipleAuthNotSupported"] = "multiple-auth-not-supported";
65
66
  ErrorType["SpecVersionNotSupported"] = "spec-version-not-supported";
67
+ ErrorType["CircularReferenceNotSupported"] = "circular-reference-not-supported";
66
68
  ErrorType["ListFailed"] = "list-failed";
67
69
  ErrorType["listSupportedAPIInfoFailed"] = "list-supported-api-info-failed";
68
70
  ErrorType["FilterSpecFailed"] = "filter-spec-failed";
@@ -299,7 +301,7 @@ class Utils {
299
301
  static updateFirstLetter(str) {
300
302
  return str.charAt(0).toUpperCase() + str.slice(1);
301
303
  }
302
- static getResponseJson(operationObject) {
304
+ static getResponseJson(operationObject, allowMultipleMediaType = false) {
303
305
  var _a, _b;
304
306
  let json = {};
305
307
  let multipleMediaType = false;
@@ -310,7 +312,9 @@ class Utils {
310
312
  json = responseObject.content["application/json"];
311
313
  if (Utils.containMultipleMediaTypes(responseObject)) {
312
314
  multipleMediaType = true;
313
- json = {};
315
+ if (!allowMultipleMediaType) {
316
+ json = {};
317
+ }
314
318
  }
315
319
  else {
316
320
  break;
@@ -574,10 +578,45 @@ class Utils {
574
578
  const serverUrl = operationServer || methodServer || rootServer;
575
579
  return serverUrl;
576
580
  }
581
+ static limitACBodyProperties(body, maxCount) {
582
+ const result = [];
583
+ let currentCount = 0;
584
+ for (const element of body) {
585
+ if (element.type === ConstantString.ContainerType) {
586
+ const items = this.limitACBodyProperties(element.items, maxCount - currentCount);
587
+ result.push({
588
+ type: ConstantString.ContainerType,
589
+ $data: element.$data,
590
+ items: items,
591
+ });
592
+ currentCount += items.length;
593
+ }
594
+ else {
595
+ if (currentCount < maxCount) {
596
+ result.push(element);
597
+ currentCount++;
598
+ }
599
+ }
600
+ }
601
+ return result;
602
+ }
577
603
  }
578
604
 
579
605
  // Copyright (c) Microsoft Corporation.
580
606
  class Validator {
607
+ constructor() {
608
+ this.hasCircularReference = false;
609
+ }
610
+ checkCircularReference() {
611
+ try {
612
+ JSON.stringify(this.spec);
613
+ }
614
+ catch (e) {
615
+ if (e.message.includes("Converting circular structure to JSON")) {
616
+ this.hasCircularReference = true;
617
+ }
618
+ }
619
+ }
581
620
  listAPIs() {
582
621
  var _a;
583
622
  if (this.apiMap) {
@@ -673,6 +712,22 @@ class Validator {
673
712
  }
674
713
  return result;
675
714
  }
715
+ validateCircularReference(method, path) {
716
+ const result = { isValid: true, reason: [] };
717
+ if (this.hasCircularReference) {
718
+ const operationObject = this.spec.paths[path][method];
719
+ try {
720
+ JSON.stringify(operationObject);
721
+ }
722
+ catch (e) {
723
+ if (e.message.includes("Converting circular structure to JSON")) {
724
+ result.isValid = false;
725
+ result.reason.push(exports.ErrorType.CircularReferenceNotSupported);
726
+ }
727
+ }
728
+ }
729
+ return result;
730
+ }
676
731
  validateResponse(method, path) {
677
732
  const result = { isValid: true, reason: [] };
678
733
  const operationObject = this.spec.paths[path][method];
@@ -861,6 +916,7 @@ class CopilotValidator extends Validator {
861
916
  this.projectType = exports.ProjectType.Copilot;
862
917
  this.options = options;
863
918
  this.spec = spec;
919
+ this.checkCircularReference();
864
920
  }
865
921
  validateSpec() {
866
922
  const result = { errors: [], warnings: [] };
@@ -886,6 +942,10 @@ class CopilotValidator extends Validator {
886
942
  if (!methodAndPathResult.isValid) {
887
943
  return methodAndPathResult;
888
944
  }
945
+ const circularReferenceResult = this.validateCircularReference(method, path);
946
+ if (!circularReferenceResult.isValid) {
947
+ return circularReferenceResult;
948
+ }
889
949
  const operationObject = this.spec.paths[path][method];
890
950
  // validate auth
891
951
  const authCheckResult = this.validateAuth(method, path);
@@ -929,6 +989,7 @@ class SMEValidator extends Validator {
929
989
  this.projectType = exports.ProjectType.SME;
930
990
  this.options = options;
931
991
  this.spec = spec;
992
+ this.checkCircularReference();
932
993
  }
933
994
  validateSpec() {
934
995
  const result = { errors: [], warnings: [] };
@@ -956,6 +1017,10 @@ class SMEValidator extends Validator {
956
1017
  if (!methodAndPathResult.isValid) {
957
1018
  return methodAndPathResult;
958
1019
  }
1020
+ const circularReferenceResult = this.validateCircularReference(method, path);
1021
+ if (!circularReferenceResult.isValid) {
1022
+ return circularReferenceResult;
1023
+ }
959
1024
  const operationObject = this.spec.paths[path][method];
960
1025
  // validate auth
961
1026
  const authCheckResult = this.validateAuth(method, path);
@@ -1026,6 +1091,7 @@ class TeamsAIValidator extends Validator {
1026
1091
  this.projectType = exports.ProjectType.TeamsAi;
1027
1092
  this.options = options;
1028
1093
  this.spec = spec;
1094
+ this.checkCircularReference();
1029
1095
  }
1030
1096
  validateSpec() {
1031
1097
  const result = { errors: [], warnings: [] };
@@ -1045,6 +1111,10 @@ class TeamsAIValidator extends Validator {
1045
1111
  if (!methodAndPathResult.isValid) {
1046
1112
  return methodAndPathResult;
1047
1113
  }
1114
+ const circularReferenceResult = this.validateCircularReference(method, path);
1115
+ if (!circularReferenceResult.isValid) {
1116
+ return circularReferenceResult;
1117
+ }
1048
1118
  const operationObject = this.spec.paths[path][method];
1049
1119
  // validate operationId
1050
1120
  if (!this.options.allowMissingId && !operationObject.operationId) {
@@ -1120,9 +1190,9 @@ class SpecFilter {
1120
1190
 
1121
1191
  // Copyright (c) Microsoft Corporation.
1122
1192
  class AdaptiveCardGenerator {
1123
- static generateAdaptiveCard(operationItem) {
1193
+ static generateAdaptiveCard(operationItem, allowMultipleMediaType = false) {
1124
1194
  try {
1125
- const { json } = Utils.getResponseJson(operationItem);
1195
+ const { json } = Utils.getResponseJson(operationItem, allowMultipleMediaType);
1126
1196
  let cardBody = [];
1127
1197
  let schema = json.schema;
1128
1198
  let jsonPath = "$";
@@ -1480,6 +1550,7 @@ class ManifestUpdater {
1480
1550
  const confirmationBodies = [];
1481
1551
  if (operationItem) {
1482
1552
  const operationId = operationItem.operationId;
1553
+ const safeFunctionName = operationId.replace(/[^a-zA-Z0-9]/g, "_");
1483
1554
  const description = (_a = operationItem.description) !== null && _a !== void 0 ? _a : "";
1484
1555
  const summary = operationItem.summary;
1485
1556
  const paramObject = operationItem.parameters;
@@ -1507,7 +1578,7 @@ class ManifestUpdater {
1507
1578
  }
1508
1579
  }
1509
1580
  const funcObj = {
1510
- name: operationId,
1581
+ name: safeFunctionName,
1511
1582
  description: description,
1512
1583
  };
1513
1584
  if (options.allowResponseSemantics) {
@@ -1515,7 +1586,7 @@ class ManifestUpdater {
1515
1586
  const { json } = Utils.getResponseJson(operationItem);
1516
1587
  if (json.schema) {
1517
1588
  const [card, jsonPath] = AdaptiveCardGenerator.generateAdaptiveCard(operationItem);
1518
- card.body = card.body.slice(0, 5);
1589
+ card.body = Utils.limitACBodyProperties(card.body, 5);
1519
1590
  const responseSemantic = wrapResponseSemantics(card, jsonPath);
1520
1591
  funcObj.capabilities = {
1521
1592
  response_semantics: responseSemantic,
@@ -1543,7 +1614,7 @@ class ManifestUpdater {
1543
1614
  }
1544
1615
  }
1545
1616
  functions.push(funcObj);
1546
- functionNames.push(operationId);
1617
+ functionNames.push(safeFunctionName);
1547
1618
  const conversationStarterStr = (summary !== null && summary !== void 0 ? summary : description).slice(0, ConstantString.ConversationStarterMaxLens);
1548
1619
  if (conversationStarterStr) {
1549
1620
  conversationStarters.push(conversationStarterStr);
@@ -1643,7 +1714,6 @@ class ManifestUpdater {
1643
1714
  const auth = authInfo.authScheme;
1644
1715
  const safeRegistrationIdName = Utils.getSafeRegistrationIdEnvName(`${authInfo.name}_${ConstantString.RegistrationIdPostfix[authInfo.authScheme.type]}`);
1645
1716
  if (Utils.isAPIKeyAuth(auth) || Utils.isBearerTokenAuth(auth)) {
1646
- const safeApiSecretRegistrationId = Utils.getSafeRegistrationIdEnvName(`${authInfo.name}_${ConstantString.RegistrationIdPostfix[authInfo.authScheme.type]}`);
1647
1717
  composeExtension.authorization = {
1648
1718
  authType: "apiSecretServiceAuth",
1649
1719
  apiSecretServiceAuthConfiguration: {
@@ -1652,16 +1722,13 @@ class ManifestUpdater {
1652
1722
  };
1653
1723
  }
1654
1724
  else if (Utils.isOAuthWithAuthCodeFlow(auth)) {
1725
+ // TODO: below schema is coming from design doc, may need to update when shcema is finalized
1655
1726
  composeExtension.authorization = {
1656
1727
  authType: "oAuth2.0",
1657
1728
  oAuthConfiguration: {
1658
1729
  oauthConfigurationId: `\${{${safeRegistrationIdName}}}`,
1659
1730
  },
1660
1731
  };
1661
- updatedPart.webApplicationInfo = {
1662
- id: "${{AAD_APP_CLIENT_ID}}",
1663
- resource: "api://${{DOMAIN}}/${{AAD_APP_CLIENT_ID}}",
1664
- };
1665
1732
  }
1666
1733
  }
1667
1734
  updatedPart.composeExtensions = [composeExtension];
@@ -1699,12 +1766,17 @@ class ManifestUpdater {
1699
1766
  command.parameters = command.parameters.filter((param) => param.isRequired);
1700
1767
  }
1701
1768
  else if (command.parameters && command.parameters.length > 0) {
1702
- command.parameters = [command.parameters[0]];
1703
- warnings.push({
1704
- type: exports.WarningType.OperationOnlyContainsOptionalParam,
1705
- content: Utils.format(ConstantString.OperationOnlyContainsOptionalParam, command.id),
1706
- data: command.id,
1707
- });
1769
+ if (command.parameters.length > 1) {
1770
+ command.parameters = [command.parameters[0]];
1771
+ warnings.push({
1772
+ type: exports.WarningType.OperationOnlyContainsOptionalParam,
1773
+ content: Utils.format(ConstantString.OperationOnlyContainsOptionalParam, command.id),
1774
+ data: {
1775
+ commandId: command.id,
1776
+ parameterName: command.parameters[0].name,
1777
+ },
1778
+ });
1779
+ }
1708
1780
  }
1709
1781
  if (adaptiveCardFolder) {
1710
1782
  const adaptiveCardPath = path__default['default'].join(adaptiveCardFolder, command.id + ".json");
@@ -1782,7 +1854,13 @@ class SpecParser {
1782
1854
  try {
1783
1855
  try {
1784
1856
  yield this.loadSpec();
1785
- yield this.parser.validate(this.spec);
1857
+ if (!this.parser.$refs.circular) {
1858
+ yield this.parser.validate(this.spec);
1859
+ }
1860
+ else {
1861
+ const clonedUnResolveSpec = JSON.parse(JSON.stringify(this.unResolveSpec));
1862
+ yield this.parser.validate(clonedUnResolveSpec);
1863
+ }
1786
1864
  }
1787
1865
  catch (e) {
1788
1866
  return {
@@ -1874,19 +1952,36 @@ class SpecParser {
1874
1952
  isValid: isValid,
1875
1953
  reason: reason,
1876
1954
  };
1877
- if (isValid) {
1955
+ // Try best to parse server url and auth type
1956
+ try {
1878
1957
  const serverObj = Utils.getServerObject(spec, method.toLocaleLowerCase(), path);
1879
1958
  if (serverObj) {
1880
- apiResult.server = Utils.resolveEnv(serverObj.url);
1959
+ apiResult.server = serverObj.url;
1881
1960
  }
1961
+ }
1962
+ catch (err) {
1963
+ // ignore
1964
+ }
1965
+ try {
1882
1966
  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;
1967
+ if (authArray.length !== 0) {
1968
+ for (const auths of authArray) {
1969
+ if (auths.length === 1) {
1970
+ apiResult.auth = auths[0];
1971
+ break;
1972
+ }
1973
+ else {
1974
+ apiResult.auth = {
1975
+ authScheme: { type: "multipleAuth" },
1976
+ name: auths.map((auth) => auth.name).join(", "),
1977
+ };
1978
+ }
1887
1979
  }
1888
1980
  }
1889
1981
  }
1982
+ catch (err) {
1983
+ // ignore
1984
+ }
1890
1985
  result.APIs.push(apiResult);
1891
1986
  }
1892
1987
  result.allAPICount = result.APIs.length;
@@ -1919,7 +2014,8 @@ class SpecParser {
1919
2014
  if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
1920
2015
  throw new SpecParserError(ConstantString.CancelledMessage, exports.ErrorType.Cancelled);
1921
2016
  }
1922
- const newSpec = (yield this.parser.dereference(newUnResolvedSpec));
2017
+ const clonedUnResolveSpec = JSON.parse(JSON.stringify(newUnResolvedSpec));
2018
+ const newSpec = (yield this.parser.dereference(clonedUnResolveSpec));
1923
2019
  return [newUnResolvedSpec, newSpec];
1924
2020
  }
1925
2021
  catch (err) {
@@ -1996,10 +2092,11 @@ class SpecParser {
1996
2092
  const operation = newSpec.paths[url][method];
1997
2093
  try {
1998
2094
  const [card, jsonPath] = AdaptiveCardGenerator.generateAdaptiveCard(operation);
1999
- const fileName = path__default['default'].join(adaptiveCardFolder, `${operation.operationId}.json`);
2095
+ const safeAdaptiveCardName = operation.operationId.replace(/[^a-zA-Z0-9]/g, "_");
2096
+ const fileName = path__default['default'].join(adaptiveCardFolder, `${safeAdaptiveCardName}.json`);
2000
2097
  const wrappedCard = wrapAdaptiveCard(card, jsonPath);
2001
2098
  yield fs__default['default'].outputJSON(fileName, wrappedCard, { spaces: 2 });
2002
- const dataFileName = path__default['default'].join(adaptiveCardFolder, `${operation.operationId}.data.json`);
2099
+ const dataFileName = path__default['default'].join(adaptiveCardFolder, `${safeAdaptiveCardName}.data.json`);
2003
2100
  yield fs__default['default'].outputJSON(dataFileName, {}, { spaces: 2 });
2004
2101
  }
2005
2102
  catch (err) {
@@ -2033,7 +2130,8 @@ class SpecParser {
2033
2130
  loadSpec() {
2034
2131
  return __awaiter(this, void 0, void 0, function* () {
2035
2132
  if (!this.spec) {
2036
- this.unResolveSpec = (yield this.parser.parse(this.pathOrSpec));
2133
+ const spec = (yield this.parser.parse(this.pathOrSpec));
2134
+ this.unResolveSpec = this.resolveEnvForSpec(spec);
2037
2135
  // Convert swagger 2.0 to openapi 3.0
2038
2136
  if (!this.unResolveSpec.openapi && this.unResolveSpec.swagger === "2.0") {
2039
2137
  const specObj = yield converter__default['default'].convert(this.unResolveSpec, {});
@@ -2070,6 +2168,11 @@ class SpecParser {
2070
2168
  yield fs__default['default'].outputFile(outputSpecPath, resultStr);
2071
2169
  });
2072
2170
  }
2171
+ resolveEnvForSpec(spec) {
2172
+ const specString = JSON.stringify(spec);
2173
+ const specResolved = Utils.resolveEnv(specString);
2174
+ return JSON.parse(specResolved);
2175
+ }
2073
2176
  }
2074
2177
 
2075
2178
  exports.AdaptiveCardGenerator = AdaptiveCardGenerator;