@microsoft/m365-spec-parser 0.2.1 → 0.2.2-alpha.089f82c02.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.
@@ -17,10 +17,12 @@ var ErrorType;
17
17
  ErrorType["RelativeServerUrlNotSupported"] = "relative-server-url-not-supported";
18
18
  ErrorType["NoSupportedApi"] = "no-supported-api";
19
19
  ErrorType["NoExtraAPICanBeAdded"] = "no-extra-api-can-be-added";
20
+ ErrorType["AddedAPINotInOriginalSpec"] = "added-api-not-in-original-spec";
20
21
  ErrorType["ResolveServerUrlFailed"] = "resolve-server-url-failed";
21
22
  ErrorType["SwaggerNotSupported"] = "swagger-not-supported";
22
23
  ErrorType["MultipleAuthNotSupported"] = "multiple-auth-not-supported";
23
24
  ErrorType["SpecVersionNotSupported"] = "spec-version-not-supported";
25
+ ErrorType["CircularReferenceNotSupported"] = "circular-reference-not-supported";
24
26
  ErrorType["ListFailed"] = "list-failed";
25
27
  ErrorType["listSupportedAPIInfoFailed"] = "list-supported-api-info-failed";
26
28
  ErrorType["FilterSpecFailed"] = "filter-spec-failed";
@@ -257,7 +259,7 @@ class Utils {
257
259
  static updateFirstLetter(str) {
258
260
  return str.charAt(0).toUpperCase() + str.slice(1);
259
261
  }
260
- static getResponseJson(operationObject) {
262
+ static getResponseJson(operationObject, allowMultipleMediaType = false) {
261
263
  var _a, _b;
262
264
  let json = {};
263
265
  let multipleMediaType = false;
@@ -268,7 +270,9 @@ class Utils {
268
270
  json = responseObject.content["application/json"];
269
271
  if (Utils.containMultipleMediaTypes(responseObject)) {
270
272
  multipleMediaType = true;
271
- json = {};
273
+ if (!allowMultipleMediaType) {
274
+ json = {};
275
+ }
272
276
  }
273
277
  else {
274
278
  break;
@@ -536,6 +540,19 @@ class Utils {
536
540
 
537
541
  // Copyright (c) Microsoft Corporation.
538
542
  class Validator {
543
+ constructor() {
544
+ this.hasCircularReference = false;
545
+ }
546
+ checkCircularReference() {
547
+ try {
548
+ JSON.stringify(this.spec);
549
+ }
550
+ catch (e) {
551
+ if (e.message.includes("Converting circular structure to JSON")) {
552
+ this.hasCircularReference = true;
553
+ }
554
+ }
555
+ }
539
556
  listAPIs() {
540
557
  var _a;
541
558
  if (this.apiMap) {
@@ -631,6 +648,22 @@ class Validator {
631
648
  }
632
649
  return result;
633
650
  }
651
+ validateCircularReference(method, path) {
652
+ const result = { isValid: true, reason: [] };
653
+ if (this.hasCircularReference) {
654
+ const operationObject = this.spec.paths[path][method];
655
+ try {
656
+ JSON.stringify(operationObject);
657
+ }
658
+ catch (e) {
659
+ if (e.message.includes("Converting circular structure to JSON")) {
660
+ result.isValid = false;
661
+ result.reason.push(ErrorType.CircularReferenceNotSupported);
662
+ }
663
+ }
664
+ }
665
+ return result;
666
+ }
634
667
  validateResponse(method, path) {
635
668
  const result = { isValid: true, reason: [] };
636
669
  const operationObject = this.spec.paths[path][method];
@@ -819,6 +852,7 @@ class CopilotValidator extends Validator {
819
852
  this.projectType = ProjectType.Copilot;
820
853
  this.options = options;
821
854
  this.spec = spec;
855
+ this.checkCircularReference();
822
856
  }
823
857
  validateSpec() {
824
858
  const result = { errors: [], warnings: [] };
@@ -844,6 +878,10 @@ class CopilotValidator extends Validator {
844
878
  if (!methodAndPathResult.isValid) {
845
879
  return methodAndPathResult;
846
880
  }
881
+ const circularReferenceResult = this.validateCircularReference(method, path);
882
+ if (!circularReferenceResult.isValid) {
883
+ return circularReferenceResult;
884
+ }
847
885
  const operationObject = this.spec.paths[path][method];
848
886
  // validate auth
849
887
  const authCheckResult = this.validateAuth(method, path);
@@ -887,6 +925,7 @@ class SMEValidator extends Validator {
887
925
  this.projectType = ProjectType.SME;
888
926
  this.options = options;
889
927
  this.spec = spec;
928
+ this.checkCircularReference();
890
929
  }
891
930
  validateSpec() {
892
931
  const result = { errors: [], warnings: [] };
@@ -914,6 +953,10 @@ class SMEValidator extends Validator {
914
953
  if (!methodAndPathResult.isValid) {
915
954
  return methodAndPathResult;
916
955
  }
956
+ const circularReferenceResult = this.validateCircularReference(method, path);
957
+ if (!circularReferenceResult.isValid) {
958
+ return circularReferenceResult;
959
+ }
917
960
  const operationObject = this.spec.paths[path][method];
918
961
  // validate auth
919
962
  const authCheckResult = this.validateAuth(method, path);
@@ -984,6 +1027,7 @@ class TeamsAIValidator extends Validator {
984
1027
  this.projectType = ProjectType.TeamsAi;
985
1028
  this.options = options;
986
1029
  this.spec = spec;
1030
+ this.checkCircularReference();
987
1031
  }
988
1032
  validateSpec() {
989
1033
  const result = { errors: [], warnings: [] };
@@ -1003,6 +1047,10 @@ class TeamsAIValidator extends Validator {
1003
1047
  if (!methodAndPathResult.isValid) {
1004
1048
  return methodAndPathResult;
1005
1049
  }
1050
+ const circularReferenceResult = this.validateCircularReference(method, path);
1051
+ if (!circularReferenceResult.isValid) {
1052
+ return circularReferenceResult;
1053
+ }
1006
1054
  const operationObject = this.spec.paths[path][method];
1007
1055
  // validate operationId
1008
1056
  if (!this.options.allowMissingId && !operationObject.operationId) {
@@ -1078,9 +1126,9 @@ class SpecFilter {
1078
1126
 
1079
1127
  // Copyright (c) Microsoft Corporation.
1080
1128
  class AdaptiveCardGenerator {
1081
- static generateAdaptiveCard(operationItem) {
1129
+ static generateAdaptiveCard(operationItem, allowMultipleMediaType = false) {
1082
1130
  try {
1083
- const { json } = Utils.getResponseJson(operationItem);
1131
+ const { json } = Utils.getResponseJson(operationItem, allowMultipleMediaType);
1084
1132
  let cardBody = [];
1085
1133
  let schema = json.schema;
1086
1134
  let jsonPath = "$";
@@ -1596,7 +1644,6 @@ class ManifestUpdater {
1596
1644
  const auth = authInfo.authScheme;
1597
1645
  const safeRegistrationIdName = Utils.getSafeRegistrationIdEnvName(`${authInfo.name}_${ConstantString.RegistrationIdPostfix[authInfo.authScheme.type]}`);
1598
1646
  if (Utils.isAPIKeyAuth(auth) || Utils.isBearerTokenAuth(auth)) {
1599
- const safeApiSecretRegistrationId = Utils.getSafeRegistrationIdEnvName(`${authInfo.name}_${ConstantString.RegistrationIdPostfix[authInfo.authScheme.type]}`);
1600
1647
  composeExtension.authorization = {
1601
1648
  authType: "apiSecretServiceAuth",
1602
1649
  apiSecretServiceAuthConfiguration: {
@@ -1605,16 +1652,13 @@ class ManifestUpdater {
1605
1652
  };
1606
1653
  }
1607
1654
  else if (Utils.isOAuthWithAuthCodeFlow(auth)) {
1655
+ // TODO: below schema is coming from design doc, may need to update when shcema is finalized
1608
1656
  composeExtension.authorization = {
1609
1657
  authType: "oAuth2.0",
1610
1658
  oAuthConfiguration: {
1611
1659
  oauthConfigurationId: `\${{${safeRegistrationIdName}}}`,
1612
1660
  },
1613
1661
  };
1614
- updatedPart.webApplicationInfo = {
1615
- id: "${{AAD_APP_CLIENT_ID}}",
1616
- resource: "api://${{DOMAIN}}/${{AAD_APP_CLIENT_ID}}",
1617
- };
1618
1662
  }
1619
1663
  }
1620
1664
  updatedPart.composeExtensions = [composeExtension];
@@ -1650,12 +1694,17 @@ class ManifestUpdater {
1650
1694
  command.parameters = command.parameters.filter((param) => param.isRequired);
1651
1695
  }
1652
1696
  else if (command.parameters && command.parameters.length > 0) {
1653
- command.parameters = [command.parameters[0]];
1654
- warnings.push({
1655
- type: WarningType.OperationOnlyContainsOptionalParam,
1656
- content: Utils.format(ConstantString.OperationOnlyContainsOptionalParam, command.id),
1657
- data: command.id,
1658
- });
1697
+ if (command.parameters.length > 1) {
1698
+ command.parameters = [command.parameters[0]];
1699
+ warnings.push({
1700
+ type: WarningType.OperationOnlyContainsOptionalParam,
1701
+ content: Utils.format(ConstantString.OperationOnlyContainsOptionalParam, command.id),
1702
+ data: {
1703
+ commandId: command.id,
1704
+ parameterName: command.parameters[0].name,
1705
+ },
1706
+ });
1707
+ }
1659
1708
  }
1660
1709
  if (adaptiveCardFolder) {
1661
1710
  const adaptiveCardPath = path.join(adaptiveCardFolder, command.id + ".json");
@@ -1731,7 +1780,13 @@ class SpecParser {
1731
1780
  try {
1732
1781
  try {
1733
1782
  await this.loadSpec();
1734
- await this.parser.validate(this.spec);
1783
+ if (!this.parser.$refs.circular) {
1784
+ await this.parser.validate(this.spec);
1785
+ }
1786
+ else {
1787
+ const clonedUnResolveSpec = JSON.parse(JSON.stringify(this.unResolveSpec));
1788
+ await this.parser.validate(clonedUnResolveSpec);
1789
+ }
1735
1790
  }
1736
1791
  catch (e) {
1737
1792
  return {
@@ -1819,19 +1874,36 @@ class SpecParser {
1819
1874
  isValid: isValid,
1820
1875
  reason: reason,
1821
1876
  };
1822
- if (isValid) {
1877
+ // Try best to parse server url and auth type
1878
+ try {
1823
1879
  const serverObj = Utils.getServerObject(spec, method.toLocaleLowerCase(), path);
1824
1880
  if (serverObj) {
1825
- apiResult.server = Utils.resolveEnv(serverObj.url);
1881
+ apiResult.server = serverObj.url;
1826
1882
  }
1883
+ }
1884
+ catch (err) {
1885
+ // ignore
1886
+ }
1887
+ try {
1827
1888
  const authArray = Utils.getAuthArray(operation.security, spec);
1828
- for (const auths of authArray) {
1829
- if (auths.length === 1) {
1830
- apiResult.auth = auths[0];
1831
- break;
1889
+ if (authArray.length !== 0) {
1890
+ for (const auths of authArray) {
1891
+ if (auths.length === 1) {
1892
+ apiResult.auth = auths[0];
1893
+ break;
1894
+ }
1895
+ else {
1896
+ apiResult.auth = {
1897
+ authScheme: { type: "multipleAuth" },
1898
+ name: auths.map((auth) => auth.name).join(", "),
1899
+ };
1900
+ }
1832
1901
  }
1833
1902
  }
1834
1903
  }
1904
+ catch (err) {
1905
+ // ignore
1906
+ }
1835
1907
  result.APIs.push(apiResult);
1836
1908
  }
1837
1909
  result.allAPICount = result.APIs.length;
@@ -1862,7 +1934,8 @@ class SpecParser {
1862
1934
  if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
1863
1935
  throw new SpecParserError(ConstantString.CancelledMessage, ErrorType.Cancelled);
1864
1936
  }
1865
- const newSpec = (await this.parser.dereference(newUnResolvedSpec));
1937
+ const clonedUnResolveSpec = JSON.parse(JSON.stringify(newUnResolvedSpec));
1938
+ const newSpec = (await this.parser.dereference(clonedUnResolveSpec));
1866
1939
  return [newUnResolvedSpec, newSpec];
1867
1940
  }
1868
1941
  catch (err) {
@@ -1935,10 +2008,11 @@ class SpecParser {
1935
2008
  const operation = newSpec.paths[url][method];
1936
2009
  try {
1937
2010
  const [card, jsonPath] = AdaptiveCardGenerator.generateAdaptiveCard(operation);
1938
- const fileName = path.join(adaptiveCardFolder, `${operation.operationId}.json`);
2011
+ const safeAdaptiveCardName = operation.operationId.replace(/[^a-zA-Z0-9]/g, "_");
2012
+ const fileName = path.join(adaptiveCardFolder, `${safeAdaptiveCardName}.json`);
1939
2013
  const wrappedCard = wrapAdaptiveCard(card, jsonPath);
1940
2014
  await fs.outputJSON(fileName, wrappedCard, { spaces: 2 });
1941
- const dataFileName = path.join(adaptiveCardFolder, `${operation.operationId}.data.json`);
2015
+ const dataFileName = path.join(adaptiveCardFolder, `${safeAdaptiveCardName}.data.json`);
1942
2016
  await fs.outputJSON(dataFileName, {}, { spaces: 2 });
1943
2017
  }
1944
2018
  catch (err) {
@@ -1970,7 +2044,8 @@ class SpecParser {
1970
2044
  }
1971
2045
  async loadSpec() {
1972
2046
  if (!this.spec) {
1973
- this.unResolveSpec = (await this.parser.parse(this.pathOrSpec));
2047
+ const spec = (await this.parser.parse(this.pathOrSpec));
2048
+ this.unResolveSpec = this.resolveEnvForSpec(spec);
1974
2049
  // Convert swagger 2.0 to openapi 3.0
1975
2050
  if (!this.unResolveSpec.openapi && this.unResolveSpec.swagger === "2.0") {
1976
2051
  const specObj = await converter.convert(this.unResolveSpec, {});
@@ -2004,6 +2079,11 @@ class SpecParser {
2004
2079
  }
2005
2080
  await fs.outputFile(outputSpecPath, resultStr);
2006
2081
  }
2082
+ resolveEnvForSpec(spec) {
2083
+ const specString = JSON.stringify(spec);
2084
+ const specResolved = Utils.resolveEnv(specString);
2085
+ return JSON.parse(specResolved);
2086
+ }
2007
2087
  }
2008
2088
 
2009
2089
  export { AdaptiveCardGenerator, ConstantString, ErrorType, ProjectType, SpecParser, SpecParserError, Utils, ValidationStatus, WarningType };