@microsoft/m365-spec-parser 0.2.4-alpha.e84e1682b.0 → 0.2.4-alpha.f33089c4c.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
 
@@ -101,6 +102,7 @@ exports.WarningType = void 0;
101
102
  WarningType["ConvertSwaggerToOpenAPI"] = "convert-swagger-to-openapi";
102
103
  WarningType["FuncDescriptionTooLong"] = "function-description-too-long";
103
104
  WarningType["OperationIdContainsSpecialCharacters"] = "operationid-contains-special-characters";
105
+ WarningType["UnsupportedAuthType"] = "unsupported-auth-type";
104
106
  WarningType["GenerateJsonDataFailed"] = "generate-json-data-failed";
105
107
  WarningType["Unknown"] = "unknown";
106
108
  })(exports.WarningType || (exports.WarningType = {}));
@@ -140,6 +142,7 @@ ConstantString.SwaggerNotSupported = "Swagger 2.0 is not supported. Please conve
140
142
  ConstantString.SpecVersionNotSupported = "Unsupported OpenAPI version %s. Please use version 3.0.x.";
141
143
  ConstantString.MultipleAuthNotSupported = "Multiple authentication methods are unsupported. Ensure all selected APIs use identical authentication.";
142
144
  ConstantString.OperationIdContainsSpecialCharacters = "Operation id '%s' in OpenAPI description document contained special characters and was renamed to '%s'.";
145
+ ConstantString.AuthTypeIsNotSupported = "Unsupported authorization type in API '%s'. No authorization will be used.";
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.";
145
148
  ConstantString.GenerateJsonDataFailed = "Failed to generate JSON data for api: %s due to %s.";
@@ -154,12 +157,7 @@ ConstantString.AdaptiveCardType = "AdaptiveCard";
154
157
  ConstantString.TextBlockType = "TextBlock";
155
158
  ConstantString.ImageType = "Image";
156
159
  ConstantString.ContainerType = "Container";
157
- ConstantString.RegistrationIdPostfix = {
158
- apiKey: "REGISTRATION_ID",
159
- oauth2: "CONFIGURATION_ID",
160
- http: "REGISTRATION_ID",
161
- openIdConnect: "REGISTRATION_ID",
162
- };
160
+ ConstantString.RegistrationIdPostfix = "REGISTRATION_ID";
163
161
  ConstantString.ResponseCodeFor20X = [
164
162
  "200",
165
163
  "201",
@@ -171,6 +169,7 @@ ConstantString.ResponseCodeFor20X = [
171
169
  "207",
172
170
  "208",
173
171
  "226",
172
+ "2XX",
174
173
  "default",
175
174
  ];
176
175
  ConstantString.AllOperationMethods = [
@@ -248,11 +247,32 @@ class Utils {
248
247
  static isAPIKeyAuth(authScheme) {
249
248
  return authScheme.type === "apiKey";
250
249
  }
250
+ static isAPIKeyAuthButNotInCookie(authScheme) {
251
+ return authScheme.type === "apiKey" && authScheme.in !== "cookie";
252
+ }
251
253
  static isOAuthWithAuthCodeFlow(authScheme) {
252
254
  return !!(authScheme.type === "oauth2" &&
253
255
  authScheme.flows &&
254
256
  authScheme.flows.authorizationCode);
255
257
  }
258
+ static isNotSupportedAuth(authSchemeArray) {
259
+ if (authSchemeArray.length === 0) {
260
+ return false;
261
+ }
262
+ if (authSchemeArray.length > 0 && authSchemeArray.every((auths) => auths.length > 1)) {
263
+ return true;
264
+ }
265
+ for (const auths of authSchemeArray) {
266
+ if (auths.length === 1) {
267
+ if (Utils.isOAuthWithAuthCodeFlow(auths[0].authScheme) ||
268
+ Utils.isBearerTokenAuth(auths[0].authScheme) ||
269
+ Utils.isAPIKeyAuthButNotInCookie(auths[0].authScheme)) {
270
+ return false;
271
+ }
272
+ }
273
+ }
274
+ return true;
275
+ }
256
276
  static getAuthArray(securities, spec) {
257
277
  var _a;
258
278
  const result = [];
@@ -277,6 +297,20 @@ class Utils {
277
297
  result.sort((a, b) => a[0].name.localeCompare(b[0].name));
278
298
  return result;
279
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
+ }
280
314
  static getAuthInfo(spec) {
281
315
  let authInfo = undefined;
282
316
  for (const url in spec.paths) {
@@ -747,6 +781,9 @@ class Validator {
747
781
  reason: [exports.ErrorType.MultipleAuthNotSupported],
748
782
  };
749
783
  }
784
+ if (this.projectType === exports.ProjectType.Copilot) {
785
+ return { isValid: true, reason: [] };
786
+ }
750
787
  for (const auths of authSchemeArray) {
751
788
  if (auths.length === 1) {
752
789
  if ((this.options.allowAPIKeyAuth && Utils.isAPIKeyAuth(auths[0].authScheme)) ||
@@ -1698,7 +1735,7 @@ function inferProperties(card) {
1698
1735
 
1699
1736
  // Copyright (c) Microsoft Corporation.
1700
1737
  class ManifestUpdater {
1701
- static updateManifestWithAiPlugin(manifestPath, outputSpecPath, apiPluginFilePath, spec, options, authInfo, existingPluginManifestInfo) {
1738
+ static updateManifestWithAiPlugin(manifestPath, outputSpecPath, apiPluginFilePath, spec, options, authMap, existingPluginManifestInfo) {
1702
1739
  return __awaiter(this, void 0, void 0, function* () {
1703
1740
  const manifest = yield fs__default['default'].readJSON(manifestPath);
1704
1741
  const apiPluginRelativePath = ManifestUpdater.getRelativePath(manifestPath, apiPluginFilePath);
@@ -1729,7 +1766,7 @@ class ManifestUpdater {
1729
1766
  }
1730
1767
  const appName = this.removeEnvs(manifest.name.short);
1731
1768
  const specRelativePath = ManifestUpdater.getRelativePath(manifestPath, outputSpecPath);
1732
- 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);
1733
1770
  return [manifest, apiPlugin, warnings];
1734
1771
  });
1735
1772
  }
@@ -1752,29 +1789,14 @@ class ManifestUpdater {
1752
1789
  throw new SpecParserError(Utils.format(ConstantString.UnsupportedSchema, method, pathUrl, JSON.stringify(schema)), exports.ErrorType.UpdateManifestFailed);
1753
1790
  }
1754
1791
  }
1755
- static generatePluginManifestSchema(spec, specRelativePath, apiPluginFilePath, appName, authInfo, options, existingPluginManifestInfo) {
1792
+ static generatePluginManifestSchema(spec, specRelativePath, apiPluginFilePath, appName, authMap, options, existingPluginManifestInfo) {
1756
1793
  var _a, _b, _c, _d;
1757
1794
  return __awaiter(this, void 0, void 0, function* () {
1758
1795
  const warnings = [];
1759
1796
  const functions = [];
1760
- const functionNames = [];
1797
+ const functionNamesMap = {};
1761
1798
  const conversationStarters = [];
1762
1799
  const paths = spec.paths;
1763
- const pluginAuthObj = {
1764
- type: "None",
1765
- };
1766
- if (authInfo) {
1767
- if (Utils.isOAuthWithAuthCodeFlow(authInfo.authScheme)) {
1768
- pluginAuthObj.type = "OAuthPluginVault";
1769
- }
1770
- else if (Utils.isBearerTokenAuth(authInfo.authScheme)) {
1771
- pluginAuthObj.type = "ApiKeyPluginVault";
1772
- }
1773
- if (pluginAuthObj.type !== "None") {
1774
- const safeRegistrationIdName = Utils.getSafeRegistrationIdEnvName(`${authInfo.name}_${ConstantString.RegistrationIdPostfix[authInfo.authScheme.type]}`);
1775
- pluginAuthObj.reference_id = `\${{${safeRegistrationIdName}}}`;
1776
- }
1777
- }
1778
1800
  for (const pathUrl in paths) {
1779
1801
  const pathItem = paths[pathUrl];
1780
1802
  if (pathItem) {
@@ -1857,7 +1879,29 @@ class ManifestUpdater {
1857
1879
  }
1858
1880
  }
1859
1881
  functions.push(funcObj);
1860
- 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
+ }
1861
1905
  const conversationStarterStr = (summary !== null && summary !== void 0 ? summary : description).slice(0, ConstantString.ConversationStarterMaxLens);
1862
1906
  if (conversationStarterStr) {
1863
1907
  conversationStarters.push(conversationStarterStr);
@@ -1867,6 +1911,12 @@ class ManifestUpdater {
1867
1911
  }
1868
1912
  }
1869
1913
  }
1914
+ if (Object.keys(functionNamesMap).length === 0) {
1915
+ functionNamesMap["None"] = {
1916
+ functionNames: [],
1917
+ authName: "None",
1918
+ };
1919
+ }
1870
1920
  let apiPlugin;
1871
1921
  if (yield fs__default['default'].pathExists(apiPluginFilePath)) {
1872
1922
  apiPlugin = yield fs__default['default'].readJSON(apiPluginFilePath);
@@ -1902,24 +1952,35 @@ class ManifestUpdater {
1902
1952
  const relativePath = ManifestUpdater.getRelativePath(existingPluginManifestInfo.manifestPath, existingPluginManifestInfo.specPath);
1903
1953
  apiPlugin.runtimes = apiPlugin.runtimes.filter((runtime) => runtime.spec.url !== relativePath);
1904
1954
  }
1905
- const index = apiPlugin.runtimes.findIndex((runtime) => {
1906
- var _a, _b;
1907
- return runtime.spec.url === specRelativePath &&
1908
- runtime.type === "OpenApi" &&
1909
- ((_b = (_a = runtime.auth) === null || _a === void 0 ? void 0 : _a.type) !== null && _b !== void 0 ? _b : "None") === pluginAuthObj.type;
1910
- });
1911
- if (index === -1) {
1912
- apiPlugin.runtimes.push({
1913
- type: "OpenApi",
1914
- auth: pluginAuthObj,
1915
- spec: {
1916
- url: specRelativePath,
1917
- },
1918
- 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;
1919
1970
  });
1920
- }
1921
- else {
1922
- 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
+ }
1923
1984
  }
1924
1985
  if (!apiPlugin.name_for_human) {
1925
1986
  apiPlugin.name_for_human = appName;
@@ -1962,7 +2023,7 @@ class ManifestUpdater {
1962
2023
  };
1963
2024
  if (authInfo) {
1964
2025
  const auth = authInfo.authScheme;
1965
- const safeRegistrationIdName = Utils.getSafeRegistrationIdEnvName(`${authInfo.name}_${ConstantString.RegistrationIdPostfix[authInfo.authScheme.type]}`);
2026
+ const safeRegistrationIdName = Utils.getSafeRegistrationIdEnvName(`${authInfo.name}_${ConstantString.RegistrationIdPostfix}`);
1966
2027
  if (Utils.isAPIKeyAuth(auth) || Utils.isBearerTokenAuth(auth)) {
1967
2028
  composeExtension.authorization = {
1968
2029
  authType: "apiSecretServiceAuth",
@@ -2092,6 +2153,7 @@ class SpecParser {
2092
2153
  };
2093
2154
  this.pathOrSpec = pathOrDoc;
2094
2155
  this.parser = new SwaggerParser__default['default']();
2156
+ this.refParser = new jsonSchemaRefParser.$RefParser();
2095
2157
  this.options = Object.assign(Object.assign({}, this.defaultOptions), (options !== null && options !== void 0 ? options : {}));
2096
2158
  }
2097
2159
  /**
@@ -2105,12 +2167,15 @@ class SpecParser {
2105
2167
  let hash = "";
2106
2168
  try {
2107
2169
  yield this.loadSpec();
2108
- if (!this.parser.$refs.circular) {
2170
+ if (!this.refParser.$refs.circular) {
2109
2171
  yield this.parser.validate(this.spec);
2110
2172
  }
2111
2173
  else {
2174
+ // The following code hangs for Graph API, support will be added when SwaggerParser is updated.
2175
+ /*
2112
2176
  const clonedUnResolveSpec = JSON.parse(JSON.stringify(this.unResolveSpec));
2113
- yield this.parser.validate(clonedUnResolveSpec);
2177
+ await this.parser.validate(clonedUnResolveSpec);
2178
+ */
2114
2179
  }
2115
2180
  }
2116
2181
  catch (e) {
@@ -2138,7 +2203,7 @@ class SpecParser {
2138
2203
  };
2139
2204
  }
2140
2205
  // Remote reference not supported
2141
- const refPaths = this.parser.$refs.paths();
2206
+ const refPaths = this.refParser.$refs.paths();
2142
2207
  // refPaths [0] is the current spec file path
2143
2208
  if (refPaths.length > 1) {
2144
2209
  errors.push({
@@ -2273,7 +2338,7 @@ class SpecParser {
2273
2338
  throw new SpecParserError(ConstantString.CancelledMessage, exports.ErrorType.Cancelled);
2274
2339
  }
2275
2340
  const clonedUnResolveSpec = JSON.parse(JSON.stringify(newUnResolvedSpec));
2276
- const newSpec = (yield this.parser.dereference(clonedUnResolveSpec));
2341
+ const newSpec = yield this.deReferenceSpec(clonedUnResolveSpec);
2277
2342
  return [newUnResolvedSpec, newSpec];
2278
2343
  }
2279
2344
  catch (err) {
@@ -2284,6 +2349,12 @@ class SpecParser {
2284
2349
  }
2285
2350
  });
2286
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
+ }
2287
2358
  /**
2288
2359
  * Generates and update artifacts from the OpenAPI specification file. Generate Adaptive Cards, update Teams app manifest, and generate a new OpenAPI specification file.
2289
2360
  * @param manifestPath A file path of the Teams app manifest file to update.
@@ -2301,7 +2372,6 @@ class SpecParser {
2301
2372
  const newSpecs = yield this.getFilteredSpecs(filter, signal);
2302
2373
  const newUnResolvedSpec = newSpecs[0];
2303
2374
  const newSpec = newSpecs[1];
2304
- const authInfo = Utils.getAuthInfo(newSpec);
2305
2375
  const paths = newUnResolvedSpec.paths;
2306
2376
  for (const pathUrl in paths) {
2307
2377
  const operations = paths[pathUrl];
@@ -2309,15 +2379,22 @@ class SpecParser {
2309
2379
  const operationItem = operations[method];
2310
2380
  const operationId = operationItem.operationId;
2311
2381
  const containsSpecialCharacters = /[^a-zA-Z0-9_]/.test(operationId);
2312
- if (!containsSpecialCharacters) {
2313
- continue;
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
+ }
2390
+ const authArray = Utils.getAuthArray(operationItem.security, newSpec);
2391
+ if (Utils.isNotSupportedAuth(authArray)) {
2392
+ result.warnings.push({
2393
+ type: exports.WarningType.UnsupportedAuthType,
2394
+ content: Utils.format(ConstantString.AuthTypeIsNotSupported, operationId),
2395
+ data: operationId,
2396
+ });
2314
2397
  }
2315
- operationItem.operationId = operationId.replace(/[^a-zA-Z0-9]/g, "_");
2316
- result.warnings.push({
2317
- type: exports.WarningType.OperationIdContainsSpecialCharacters,
2318
- content: Utils.format(ConstantString.OperationIdContainsSpecialCharacters, operationId, operationItem.operationId),
2319
- data: operationId,
2320
- });
2321
2398
  }
2322
2399
  }
2323
2400
  yield this.saveFilterSpec(outputSpecPath, newUnResolvedSpec);
@@ -2330,7 +2407,8 @@ class SpecParser {
2330
2407
  specPath: this.pathOrSpec,
2331
2408
  }
2332
2409
  : undefined;
2333
- 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);
2334
2412
  result.warnings.push(...warnings);
2335
2413
  yield fs__default['default'].outputJSON(manifestPath, updatedManifest, { spaces: 4 });
2336
2414
  yield fs__default['default'].outputJSON(pluginFilePath, apiPlugin, { spaces: 4 });
@@ -2422,7 +2500,7 @@ class SpecParser {
2422
2500
  this.isSwaggerFile = true;
2423
2501
  }
2424
2502
  const clonedUnResolveSpec = JSON.parse(JSON.stringify(this.unResolveSpec));
2425
- this.spec = (yield this.parser.dereference(clonedUnResolveSpec));
2503
+ this.spec = yield this.deReferenceSpec(clonedUnResolveSpec);
2426
2504
  }
2427
2505
  });
2428
2506
  }