UNPKG

api

Version:

Magical SDK generation from an OpenAPI definition 🪄

260 lines (259 loc) • 13.7 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (g && (g = 0, op[0] && (_ = 0)), _) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { if (ar || !(i in from)) { if (!ar) ar = Array.prototype.slice.call(from, 0, i); ar[i] = from[i]; } } return to.concat(ar || Array.prototype.slice.call(from)); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; var oas_1 = __importDefault(require("oas")); var cache_1 = __importDefault(require("./cache")); var core_1 = __importDefault(require("./core")); var packageInfo_1 = require("./packageInfo"); var Sdk = /** @class */ (function () { function Sdk(uri, opts) { if (opts === void 0) { opts = {}; } this.uri = uri; this.userAgent = "".concat(packageInfo_1.PACKAGE_NAME, " (node)/").concat(packageInfo_1.PACKAGE_VERSION); this.cacheDir = opts.cacheDir ? opts.cacheDir : false; } Sdk.prototype.load = function () { var cache = new cache_1["default"](this.uri, this.cacheDir); var userAgent = this.userAgent; var core = new core_1["default"](); core.setUserAgent(userAgent); var isLoaded = false; var isCached = cache.isCached(); var sdk = {}; /** * Create dynamic accessors for every operation with a defined operation ID. If an operation * does not have an operation ID it can be accessed by its `.method('/path')` accessor instead. * */ function loadOperations(spec) { return Object.entries(spec.getPaths()) .map(function (_a) { var operations = _a[1]; return Object.values(operations); }) .reduce(function (prev, next) { return prev.concat(next); }, []) .reduce(function (prev, next) { var _a; // `getOperationId()` creates dynamic operation IDs when one isn't available but we need // to know here if we actually have one present or not. The `camelCase` option here also // cleans up any `operationId` that we might have into something that can be used as a // valid JS method. var originalOperationId = next.getOperationId(); var operationId = next.getOperationId({ camelCase: true }); var op = (_a = {}, _a[operationId] = (function (operation) { var args = []; for (var _i = 1; _i < arguments.length; _i++) { args[_i - 1] = arguments[_i]; } return core.fetchOperation.apply(core, __spreadArray([operation], args, false)); }).bind(null, next), _a); if (operationId !== originalOperationId) { // If we cleaned up their operation ID into a friendly method accessor (`findPetById` // versus `find pet by id`) we should still let them use the non-friendly version if // they want. // // This work is to maintain backwards compatibility with `api@4` and does not exist // within our code generated SDKs -- those only allow the cleaner camelCase // `operationId` to be used. op[originalOperationId] = (function (operation) { var args = []; for (var _i = 1; _i < arguments.length; _i++) { args[_i - 1] = arguments[_i]; } return core.fetchOperation.apply(core, __spreadArray([operation], args, false)); }).bind(null, next); } return Object.assign(prev, op); }, {}); } function loadFromCache() { return __awaiter(this, void 0, void 0, function () { var cachedSpec, spec; return __generator(this, function (_a) { switch (_a.label) { case 0: if (!isCached) return [3 /*break*/, 2]; return [4 /*yield*/, cache.get()]; case 1: cachedSpec = _a.sent(); return [3 /*break*/, 4]; case 2: return [4 /*yield*/, cache.load()]; case 3: cachedSpec = _a.sent(); isCached = true; _a.label = 4; case 4: spec = new oas_1["default"](cachedSpec); core.setSpec(spec); sdk = Object.assign(sdk, loadOperations(spec)); isLoaded = true; return [2 /*return*/]; } }); }); } var sdkProxy = { // @give this a better type than any get: function (target, method) { // Since auth returns a self-proxy, we **do not** want it to fall through into the async // function below as when that'll happen, instead of returning a self-proxy, it'll end up // returning a Promise. When that happens, chaining `sdk.auth().operationId()` will fail. if (['auth', 'config'].includes(method)) { // @todo split this up so we have better types for `auth` and `config` return function authAndConfigHandler() { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } return target[method].apply(this, args); }; } return function accessorHandler() { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { switch (_a.label) { case 0: if (!!(method in target)) return [3 /*break*/, 2]; // If this method doesn't exist on the proxy, have we loaded the SDK? If we have, then // this method isn't valid. if (isLoaded) { throw new Error("Sorry, `".concat(method, "` does not appear to be a valid operation on this API.")); } return [4 /*yield*/, loadFromCache()]; case 1: _a.sent(); // If after loading the SDK and this method still doesn't exist, then it's not real! if (!(method in sdk)) { throw new Error("Sorry, `".concat(method, "` does not appear to be a valid operation on this API.")); } // @todo give sdk a better type return [2 /*return*/, sdk[method].apply(this, args)]; case 2: return [2 /*return*/, target[method].apply(this, args)]; } }); }); }; } }; sdk = { /** * If the API you're using requires authentication you can supply the required credentials * through this method and the library will magically determine how they should be used * within your API request. * * With the exception of OpenID and MutualTLS, it supports all forms of authentication * supported by the OpenAPI specification. * * @example <caption>HTTP Basic auth</caption> * sdk.auth('username', 'password'); * * @example <caption>Bearer tokens (HTTP or OAuth 2)</caption> * sdk.auth('myBearerToken'); * * @example <caption>API Keys</caption> * sdk.auth('myApiKey'); * * @see {@link https://spec.openapis.org/oas/v3.0.3#fixed-fields-22} * @see {@link https://spec.openapis.org/oas/v3.1.0#fixed-fields-22} * @param values Your auth credentials for the API. Can specify up to two strings or numbers. */ auth: function () { var values = []; for (var _i = 0; _i < arguments.length; _i++) { values[_i] = arguments[_i]; } core.setAuth.apply(core, values); }, /** * Optionally configure various options that the SDK allows. * * @param config Object of supported SDK options and toggles. * @param config.timeout Override the default `fetch` request timeout of 30 seconds (30000ms). */ config: function (config) { core.setConfig(config); }, /** * If the API you're using offers alternate server URLs, and server variables, you can tell * the SDK which one to use with this method. To use it you can supply either one of the * server URLs that are contained within the OpenAPI definition (along with any server * variables), or you can pass it a fully qualified URL to use (that may or may not exist * within the OpenAPI definition). * * @example <caption>Server URL with server variables</caption> * sdk.server('https://{region}.api.example.com/{basePath}', { * name: 'eu', * basePath: 'v14', * }); * * @example <caption>Fully qualified server URL</caption> * sdk.server('https://eu.api.example.com/v14'); * * @param url Server URL * @param variables An object of variables to replace into the server URL. */ server: function (url, variables) { if (variables === void 0) { variables = {}; } core.setServer(url, variables); } }; return new Proxy(sdk, sdkProxy); }; return Sdk; }()); module.exports = function (uri, opts) { if (opts === void 0) { opts = {}; } return new Sdk(uri, opts).load(); };