@microsoft/m365-spec-parser 0.2.4-alpha.ad0d7aa1a.0 → 0.2.4-alpha.af47bc3a8.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.
@@ -9,6 +9,7 @@ var fs = require('fs-extra');
9
9
  var path = require('path');
10
10
  var teamsManifest = require('@microsoft/teams-manifest');
11
11
  var crypto = require('crypto');
12
+ var jsonSchemaRefParser = require('@apidevtools/json-schema-ref-parser');
12
13
 
13
14
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
14
15
 
@@ -156,12 +157,7 @@ ConstantString.AdaptiveCardType = "AdaptiveCard";
156
157
  ConstantString.TextBlockType = "TextBlock";
157
158
  ConstantString.ImageType = "Image";
158
159
  ConstantString.ContainerType = "Container";
159
- ConstantString.RegistrationIdPostfix = {
160
- apiKey: "REGISTRATION_ID",
161
- oauth2: "CONFIGURATION_ID",
162
- http: "REGISTRATION_ID",
163
- openIdConnect: "REGISTRATION_ID",
164
- };
160
+ ConstantString.RegistrationIdPostfix = "REGISTRATION_ID";
165
161
  ConstantString.ResponseCodeFor20X = [
166
162
  "200",
167
163
  "201",
@@ -173,6 +169,7 @@ ConstantString.ResponseCodeFor20X = [
173
169
  "207",
174
170
  "208",
175
171
  "226",
172
+ "2XX",
176
173
  "default",
177
174
  ];
178
175
  ConstantString.AllOperationMethods = [
@@ -250,6 +247,9 @@ class Utils {
250
247
  static isAPIKeyAuth(authScheme) {
251
248
  return authScheme.type === "apiKey";
252
249
  }
250
+ static isAPIKeyAuthButNotInCookie(authScheme) {
251
+ return authScheme.type === "apiKey" && authScheme.in !== "cookie";
252
+ }
253
253
  static isOAuthWithAuthCodeFlow(authScheme) {
254
254
  return !!(authScheme.type === "oauth2" &&
255
255
  authScheme.flows &&
@@ -265,7 +265,8 @@ class Utils {
265
265
  for (const auths of authSchemeArray) {
266
266
  if (auths.length === 1) {
267
267
  if (Utils.isOAuthWithAuthCodeFlow(auths[0].authScheme) ||
268
- Utils.isBearerTokenAuth(auths[0].authScheme)) {
268
+ Utils.isBearerTokenAuth(auths[0].authScheme) ||
269
+ Utils.isAPIKeyAuthButNotInCookie(auths[0].authScheme)) {
269
270
  return false;
270
271
  }
271
272
  }
@@ -296,6 +297,20 @@ class Utils {
296
297
  result.sort((a, b) => a[0].name.localeCompare(b[0].name));
297
298
  return result;
298
299
  }
300
+ static getAuthMap(spec) {
301
+ const authMap = {};
302
+ for (const url in spec.paths) {
303
+ for (const method in spec.paths[url]) {
304
+ const operation = spec.paths[url][method];
305
+ const authArray = Utils.getAuthArray(operation.security, spec);
306
+ if (authArray && authArray.length > 0) {
307
+ const currentAuth = authArray[0][0];
308
+ authMap[operation.operationId] = currentAuth;
309
+ }
310
+ }
311
+ }
312
+ return authMap;
313
+ }
299
314
  static getAuthInfo(spec) {
300
315
  let authInfo = undefined;
301
316
  for (const url in spec.paths) {
@@ -1720,7 +1735,7 @@ function inferProperties(card) {
1720
1735
 
1721
1736
  // Copyright (c) Microsoft Corporation.
1722
1737
  class ManifestUpdater {
1723
- static updateManifestWithAiPlugin(manifestPath, outputSpecPath, apiPluginFilePath, spec, options, authInfo, existingPluginManifestInfo) {
1738
+ static updateManifestWithAiPlugin(manifestPath, outputSpecPath, apiPluginFilePath, spec, options, authMap, existingPluginManifestInfo) {
1724
1739
  return __awaiter(this, void 0, void 0, function* () {
1725
1740
  const manifest = yield fs__default['default'].readJSON(manifestPath);
1726
1741
  const apiPluginRelativePath = ManifestUpdater.getRelativePath(manifestPath, apiPluginFilePath);
@@ -1751,7 +1766,7 @@ class ManifestUpdater {
1751
1766
  }
1752
1767
  const appName = this.removeEnvs(manifest.name.short);
1753
1768
  const specRelativePath = ManifestUpdater.getRelativePath(manifestPath, outputSpecPath);
1754
- const [apiPlugin, warnings] = yield ManifestUpdater.generatePluginManifestSchema(spec, specRelativePath, apiPluginFilePath, appName, authInfo, options, existingPluginManifestInfo);
1769
+ const [apiPlugin, warnings] = yield ManifestUpdater.generatePluginManifestSchema(spec, specRelativePath, apiPluginFilePath, appName, authMap, options, existingPluginManifestInfo);
1755
1770
  return [manifest, apiPlugin, warnings];
1756
1771
  });
1757
1772
  }
@@ -1774,29 +1789,14 @@ class ManifestUpdater {
1774
1789
  throw new SpecParserError(Utils.format(ConstantString.UnsupportedSchema, method, pathUrl, JSON.stringify(schema)), exports.ErrorType.UpdateManifestFailed);
1775
1790
  }
1776
1791
  }
1777
- static generatePluginManifestSchema(spec, specRelativePath, apiPluginFilePath, appName, authInfo, options, existingPluginManifestInfo) {
1792
+ static generatePluginManifestSchema(spec, specRelativePath, apiPluginFilePath, appName, authMap, options, existingPluginManifestInfo) {
1778
1793
  var _a, _b, _c, _d;
1779
1794
  return __awaiter(this, void 0, void 0, function* () {
1780
1795
  const warnings = [];
1781
1796
  const functions = [];
1782
- const functionNames = [];
1797
+ const functionNamesMap = {};
1783
1798
  const conversationStarters = [];
1784
1799
  const paths = spec.paths;
1785
- const pluginAuthObj = {
1786
- type: "None",
1787
- };
1788
- if (authInfo) {
1789
- if (Utils.isOAuthWithAuthCodeFlow(authInfo.authScheme)) {
1790
- pluginAuthObj.type = "OAuthPluginVault";
1791
- }
1792
- else if (Utils.isBearerTokenAuth(authInfo.authScheme)) {
1793
- pluginAuthObj.type = "ApiKeyPluginVault";
1794
- }
1795
- if (pluginAuthObj.type !== "None") {
1796
- const safeRegistrationIdName = Utils.getSafeRegistrationIdEnvName(`${authInfo.name}_${ConstantString.RegistrationIdPostfix[authInfo.authScheme.type]}`);
1797
- pluginAuthObj.reference_id = `\${{${safeRegistrationIdName}}}`;
1798
- }
1799
- }
1800
1800
  for (const pathUrl in paths) {
1801
1801
  const pathItem = paths[pathUrl];
1802
1802
  if (pathItem) {
@@ -1879,7 +1879,29 @@ class ManifestUpdater {
1879
1879
  }
1880
1880
  }
1881
1881
  functions.push(funcObj);
1882
- functionNames.push(safeFunctionName);
1882
+ const authInfo = authMap[operationId];
1883
+ let key = "None";
1884
+ let authName = "None";
1885
+ if (authInfo) {
1886
+ if (Utils.isOAuthWithAuthCodeFlow(authInfo.authScheme)) {
1887
+ key = "OAuthPluginVault";
1888
+ authName = authInfo.name;
1889
+ }
1890
+ else if (Utils.isBearerTokenAuth(authInfo.authScheme) ||
1891
+ Utils.isAPIKeyAuthButNotInCookie(authInfo.authScheme)) {
1892
+ key = "ApiKeyPluginVault";
1893
+ authName = authInfo.name;
1894
+ }
1895
+ }
1896
+ if (functionNamesMap[key]) {
1897
+ functionNamesMap[key].functionNames.push(safeFunctionName);
1898
+ }
1899
+ else {
1900
+ functionNamesMap[key] = {
1901
+ functionNames: [safeFunctionName],
1902
+ authName: authName,
1903
+ };
1904
+ }
1883
1905
  const conversationStarterStr = (summary !== null && summary !== void 0 ? summary : description).slice(0, ConstantString.ConversationStarterMaxLens);
1884
1906
  if (conversationStarterStr) {
1885
1907
  conversationStarters.push(conversationStarterStr);
@@ -1889,6 +1911,12 @@ class ManifestUpdater {
1889
1911
  }
1890
1912
  }
1891
1913
  }
1914
+ if (Object.keys(functionNamesMap).length === 0) {
1915
+ functionNamesMap["None"] = {
1916
+ functionNames: [],
1917
+ authName: "None",
1918
+ };
1919
+ }
1892
1920
  let apiPlugin;
1893
1921
  if (yield fs__default['default'].pathExists(apiPluginFilePath)) {
1894
1922
  apiPlugin = yield fs__default['default'].readJSON(apiPluginFilePath);
@@ -1924,24 +1952,35 @@ class ManifestUpdater {
1924
1952
  const relativePath = ManifestUpdater.getRelativePath(existingPluginManifestInfo.manifestPath, existingPluginManifestInfo.specPath);
1925
1953
  apiPlugin.runtimes = apiPlugin.runtimes.filter((runtime) => runtime.spec.url !== relativePath);
1926
1954
  }
1927
- const index = apiPlugin.runtimes.findIndex((runtime) => {
1928
- var _a, _b;
1929
- return runtime.spec.url === specRelativePath &&
1930
- runtime.type === "OpenApi" &&
1931
- ((_b = (_a = runtime.auth) === null || _a === void 0 ? void 0 : _a.type) !== null && _b !== void 0 ? _b : "None") === pluginAuthObj.type;
1932
- });
1933
- if (index === -1) {
1934
- apiPlugin.runtimes.push({
1935
- type: "OpenApi",
1936
- auth: pluginAuthObj,
1937
- spec: {
1938
- url: specRelativePath,
1939
- },
1940
- run_for_functions: functionNames,
1955
+ for (const authType in functionNamesMap) {
1956
+ const pluginAuthObj = {
1957
+ type: authType,
1958
+ };
1959
+ const authName = functionNamesMap[authType].authName;
1960
+ if (pluginAuthObj.type !== "None") {
1961
+ const safeRegistrationIdName = Utils.getSafeRegistrationIdEnvName(`${authName}_${ConstantString.RegistrationIdPostfix}`);
1962
+ pluginAuthObj.reference_id = `\${{${safeRegistrationIdName}}}`;
1963
+ }
1964
+ const functionNamesInfo = functionNamesMap[authType];
1965
+ const index = apiPlugin.runtimes.findIndex((runtime) => {
1966
+ var _a, _b;
1967
+ return runtime.spec.url === specRelativePath &&
1968
+ runtime.type === "OpenApi" &&
1969
+ ((_b = (_a = runtime.auth) === null || _a === void 0 ? void 0 : _a.type) !== null && _b !== void 0 ? _b : "None") === authType;
1941
1970
  });
1942
- }
1943
- else {
1944
- apiPlugin.runtimes[index].run_for_functions = functionNames;
1971
+ if (index === -1) {
1972
+ apiPlugin.runtimes.push({
1973
+ type: "OpenApi",
1974
+ auth: pluginAuthObj,
1975
+ spec: {
1976
+ url: specRelativePath,
1977
+ },
1978
+ run_for_functions: functionNamesInfo.functionNames,
1979
+ });
1980
+ }
1981
+ else {
1982
+ apiPlugin.runtimes[index].run_for_functions = functionNamesInfo.functionNames;
1983
+ }
1945
1984
  }
1946
1985
  if (!apiPlugin.name_for_human) {
1947
1986
  apiPlugin.name_for_human = appName;
@@ -1984,7 +2023,7 @@ class ManifestUpdater {
1984
2023
  };
1985
2024
  if (authInfo) {
1986
2025
  const auth = authInfo.authScheme;
1987
- const safeRegistrationIdName = Utils.getSafeRegistrationIdEnvName(`${authInfo.name}_${ConstantString.RegistrationIdPostfix[authInfo.authScheme.type]}`);
2026
+ const safeRegistrationIdName = Utils.getSafeRegistrationIdEnvName(`${authInfo.name}_${ConstantString.RegistrationIdPostfix}`);
1988
2027
  if (Utils.isAPIKeyAuth(auth) || Utils.isBearerTokenAuth(auth)) {
1989
2028
  composeExtension.authorization = {
1990
2029
  authType: "apiSecretServiceAuth",
@@ -2114,6 +2153,7 @@ class SpecParser {
2114
2153
  };
2115
2154
  this.pathOrSpec = pathOrDoc;
2116
2155
  this.parser = new SwaggerParser__default['default']();
2156
+ this.refParser = new jsonSchemaRefParser.$RefParser();
2117
2157
  this.options = Object.assign(Object.assign({}, this.defaultOptions), (options !== null && options !== void 0 ? options : {}));
2118
2158
  }
2119
2159
  /**
@@ -2127,12 +2167,15 @@ class SpecParser {
2127
2167
  let hash = "";
2128
2168
  try {
2129
2169
  yield this.loadSpec();
2130
- if (!this.parser.$refs.circular) {
2170
+ if (!this.refParser.$refs.circular) {
2131
2171
  yield this.parser.validate(this.spec);
2132
2172
  }
2133
2173
  else {
2174
+ // The following code hangs for Graph API, support will be added when SwaggerParser is updated.
2175
+ /*
2134
2176
  const clonedUnResolveSpec = JSON.parse(JSON.stringify(this.unResolveSpec));
2135
- yield this.parser.validate(clonedUnResolveSpec);
2177
+ await this.parser.validate(clonedUnResolveSpec);
2178
+ */
2136
2179
  }
2137
2180
  }
2138
2181
  catch (e) {
@@ -2160,7 +2203,7 @@ class SpecParser {
2160
2203
  };
2161
2204
  }
2162
2205
  // Remote reference not supported
2163
- const refPaths = this.parser.$refs.paths();
2206
+ const refPaths = this.refParser.$refs.paths();
2164
2207
  // refPaths [0] is the current spec file path
2165
2208
  if (refPaths.length > 1) {
2166
2209
  errors.push({
@@ -2295,7 +2338,7 @@ class SpecParser {
2295
2338
  throw new SpecParserError(ConstantString.CancelledMessage, exports.ErrorType.Cancelled);
2296
2339
  }
2297
2340
  const clonedUnResolveSpec = JSON.parse(JSON.stringify(newUnResolvedSpec));
2298
- const newSpec = (yield this.parser.dereference(clonedUnResolveSpec));
2341
+ const newSpec = yield this.deReferenceSpec(clonedUnResolveSpec);
2299
2342
  return [newUnResolvedSpec, newSpec];
2300
2343
  }
2301
2344
  catch (err) {
@@ -2306,6 +2349,12 @@ class SpecParser {
2306
2349
  }
2307
2350
  });
2308
2351
  }
2352
+ deReferenceSpec(spec) {
2353
+ return __awaiter(this, void 0, void 0, function* () {
2354
+ const result = yield this.refParser.dereference(spec);
2355
+ return result;
2356
+ });
2357
+ }
2309
2358
  /**
2310
2359
  * Generates and update artifacts from the OpenAPI specification file. Generate Adaptive Cards, update Teams app manifest, and generate a new OpenAPI specification file.
2311
2360
  * @param manifestPath A file path of the Teams app manifest file to update.
@@ -2323,13 +2372,21 @@ class SpecParser {
2323
2372
  const newSpecs = yield this.getFilteredSpecs(filter, signal);
2324
2373
  const newUnResolvedSpec = newSpecs[0];
2325
2374
  const newSpec = newSpecs[1];
2326
- const authInfo = Utils.getAuthInfo(newSpec);
2327
2375
  const paths = newUnResolvedSpec.paths;
2328
2376
  for (const pathUrl in paths) {
2329
2377
  const operations = paths[pathUrl];
2330
2378
  for (const method in operations) {
2331
2379
  const operationItem = operations[method];
2332
2380
  const operationId = operationItem.operationId;
2381
+ const containsSpecialCharacters = /[^a-zA-Z0-9_]/.test(operationId);
2382
+ if (containsSpecialCharacters) {
2383
+ operationItem.operationId = operationId.replace(/[^a-zA-Z0-9]/g, "_");
2384
+ result.warnings.push({
2385
+ type: exports.WarningType.OperationIdContainsSpecialCharacters,
2386
+ content: Utils.format(ConstantString.OperationIdContainsSpecialCharacters, operationId, operationItem.operationId),
2387
+ data: operationId,
2388
+ });
2389
+ }
2333
2390
  const authArray = Utils.getAuthArray(operationItem.security, newSpec);
2334
2391
  if (Utils.isNotSupportedAuth(authArray)) {
2335
2392
  result.warnings.push({
@@ -2338,16 +2395,6 @@ class SpecParser {
2338
2395
  data: operationId,
2339
2396
  });
2340
2397
  }
2341
- const containsSpecialCharacters = /[^a-zA-Z0-9_]/.test(operationId);
2342
- if (!containsSpecialCharacters) {
2343
- continue;
2344
- }
2345
- operationItem.operationId = operationId.replace(/[^a-zA-Z0-9]/g, "_");
2346
- result.warnings.push({
2347
- type: exports.WarningType.OperationIdContainsSpecialCharacters,
2348
- content: Utils.format(ConstantString.OperationIdContainsSpecialCharacters, operationId, operationItem.operationId),
2349
- data: operationId,
2350
- });
2351
2398
  }
2352
2399
  }
2353
2400
  yield this.saveFilterSpec(outputSpecPath, newUnResolvedSpec);
@@ -2360,7 +2407,8 @@ class SpecParser {
2360
2407
  specPath: this.pathOrSpec,
2361
2408
  }
2362
2409
  : undefined;
2363
- const [updatedManifest, apiPlugin, warnings] = yield ManifestUpdater.updateManifestWithAiPlugin(manifestPath, outputSpecPath, pluginFilePath, newSpec, this.options, authInfo, existingPluginManifestInfo);
2410
+ const authMap = Utils.getAuthMap(newSpec);
2411
+ const [updatedManifest, apiPlugin, warnings] = yield ManifestUpdater.updateManifestWithAiPlugin(manifestPath, outputSpecPath, pluginFilePath, newSpec, this.options, authMap, existingPluginManifestInfo);
2364
2412
  result.warnings.push(...warnings);
2365
2413
  yield fs__default['default'].outputJSON(manifestPath, updatedManifest, { spaces: 4 });
2366
2414
  yield fs__default['default'].outputJSON(pluginFilePath, apiPlugin, { spaces: 4 });
@@ -2452,7 +2500,7 @@ class SpecParser {
2452
2500
  this.isSwaggerFile = true;
2453
2501
  }
2454
2502
  const clonedUnResolveSpec = JSON.parse(JSON.stringify(this.unResolveSpec));
2455
- this.spec = (yield this.parser.dereference(clonedUnResolveSpec));
2503
+ this.spec = yield this.deReferenceSpec(clonedUnResolveSpec);
2456
2504
  }
2457
2505
  });
2458
2506
  }