api 5.0.0-beta.2 → 5.0.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.
- package/README.md +7 -8
- package/dist/bin.js +1 -1
- package/dist/cache.d.ts +38 -3
- package/dist/cache.js +7 -26
- package/dist/cli/codegen/index.d.ts +1 -1
- package/dist/cli/codegen/language.d.ts +1 -1
- package/dist/cli/codegen/language.js +13 -0
- package/dist/cli/codegen/languages/typescript/util.d.ts +21 -0
- package/dist/cli/codegen/languages/typescript/util.js +185 -0
- package/dist/cli/codegen/languages/typescript.d.ts +36 -41
- package/dist/cli/codegen/languages/typescript.js +394 -414
- package/dist/cli/commands/install.js +6 -6
- package/dist/cli/storage.d.ts +1 -1
- package/dist/cli/storage.js +2 -2
- package/dist/core/errors/fetchError.d.ts +12 -0
- package/dist/core/errors/fetchError.js +36 -0
- package/dist/core/getJSONSchemaDefaults.d.ts +1 -1
- package/dist/core/index.d.ts +12 -4
- package/dist/core/index.js +36 -11
- package/dist/core/parseResponse.d.ts +6 -1
- package/dist/core/parseResponse.js +9 -3
- package/dist/core/prepareAuth.js +47 -18
- package/dist/core/prepareParams.d.ts +0 -3
- package/dist/core/prepareParams.js +102 -41
- package/dist/fetcher.d.ts +1 -1
- package/dist/fetcher.js +3 -3
- package/dist/index.d.ts +1 -1
- package/dist/index.js +24 -40
- package/dist/packageInfo.d.ts +1 -1
- package/dist/packageInfo.js +1 -1
- package/package.json +31 -17
- package/src/bin.ts +2 -1
- package/src/cache.ts +9 -31
- package/src/cli/codegen/index.ts +1 -1
- package/src/cli/codegen/language.ts +18 -1
- package/src/cli/codegen/languages/typescript/util.ts +183 -0
- package/src/cli/codegen/languages/typescript.ts +348 -340
- package/src/cli/commands/install.ts +6 -8
- package/src/cli/storage.ts +4 -4
- package/src/core/errors/fetchError.ts +31 -0
- package/src/core/getJSONSchemaDefaults.ts +3 -2
- package/src/core/index.ts +53 -18
- package/src/core/parseResponse.ts +8 -2
- package/src/core/prepareAuth.ts +55 -31
- package/src/core/prepareParams.ts +112 -41
- package/src/fetcher.ts +5 -4
- package/src/index.ts +24 -32
- package/src/packageInfo.ts +1 -1
- package/src/typings.d.ts +0 -1
- package/tsconfig.json +1 -1
|
@@ -76,18 +76,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
76
76
|
exports.__esModule = true;
|
|
77
77
|
var fs_1 = __importDefault(require("fs"));
|
|
78
78
|
var path_1 = __importDefault(require("path"));
|
|
79
|
-
var language_1 = __importDefault(require("../language"));
|
|
80
|
-
var logger_1 = __importDefault(require("../../logger"));
|
|
81
|
-
var object_hash_1 = __importDefault(require("object-hash"));
|
|
82
|
-
var ts_morph_1 = require("ts-morph");
|
|
83
|
-
var json_schema_to_typescript_1 = require("json-schema-to-typescript");
|
|
84
|
-
var formatter_1 = require("json-schema-to-typescript/dist/src/formatter");
|
|
85
79
|
var execa_1 = __importDefault(require("execa"));
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
80
|
+
var lodash_setwith_1 = __importDefault(require("lodash.setwith"));
|
|
81
|
+
var ts_morph_1 = require("ts-morph");
|
|
82
|
+
var logger_1 = __importDefault(require("../../logger"));
|
|
83
|
+
var language_1 = __importDefault(require("../language"));
|
|
84
|
+
var util_1 = require("./typescript/util");
|
|
91
85
|
var TSGenerator = /** @class */ (function (_super) {
|
|
92
86
|
__extends(TSGenerator, _super);
|
|
93
87
|
function TSGenerator(spec, specPath, identifier, opts) {
|
|
@@ -99,11 +93,16 @@ var TSGenerator = /** @class */ (function (_super) {
|
|
|
99
93
|
options.compilerTarget = 'esm';
|
|
100
94
|
}
|
|
101
95
|
_this = _super.call(this, spec, specPath, identifier) || this;
|
|
96
|
+
_this.usesHTTPMethodRangeInterface = false;
|
|
102
97
|
_this.requiredPackages = {
|
|
103
|
-
|
|
98
|
+
api: {
|
|
104
99
|
reason: "Required for the `api/dist/core` library that the codegen'd SDK uses for making requests.",
|
|
105
100
|
url: 'https://npm.im/api'
|
|
106
101
|
},
|
|
102
|
+
'json-schema-to-ts': {
|
|
103
|
+
reason: 'Required for TypeScript type handling.',
|
|
104
|
+
url: 'https://npm.im/json-schema-to-ts'
|
|
105
|
+
},
|
|
107
106
|
oas: {
|
|
108
107
|
reason: 'Used within `api/dist/core` and is also loaded for TypeScript types.',
|
|
109
108
|
url: 'https://npm.im/oas'
|
|
@@ -114,24 +113,17 @@ var TSGenerator = /** @class */ (function (_super) {
|
|
|
114
113
|
indentationText: ts_morph_1.IndentationText.TwoSpaces,
|
|
115
114
|
quoteKind: ts_morph_1.QuoteKind.Single
|
|
116
115
|
},
|
|
117
|
-
compilerOptions: __assign({
|
|
116
|
+
compilerOptions: __assign({
|
|
117
|
+
// If we're exporting a TypeScript SDK then we don't need to pollute the codegen directory
|
|
118
|
+
// with unnecessary declaration `.d.ts` files.
|
|
119
|
+
declaration: options.outputJS, outDir: 'dist', resolveJsonModule: true, target: options.compilerTarget === 'cjs' ? ts_morph_1.ScriptTarget.ES5 : ts_morph_1.ScriptTarget.ES2020 }, (options.compilerTarget === 'cjs' ? { esModuleInterop: true } : {}))
|
|
118
120
|
});
|
|
119
121
|
_this.compilerTarget = options.compilerTarget;
|
|
120
122
|
_this.outputJS = options.outputJS;
|
|
121
123
|
_this.types = new Map();
|
|
122
|
-
_this.
|
|
123
|
-
_this.schemas = new Map();
|
|
124
|
+
_this.schemas = {};
|
|
124
125
|
return _this;
|
|
125
126
|
}
|
|
126
|
-
TSGenerator.formatter = function (content) {
|
|
127
|
-
return (0, formatter_1.format)(content, {
|
|
128
|
-
format: true,
|
|
129
|
-
style: {
|
|
130
|
-
printWidth: 120,
|
|
131
|
-
singleQuote: true
|
|
132
|
-
}
|
|
133
|
-
});
|
|
134
|
-
};
|
|
135
127
|
TSGenerator.prototype.installer = function (storage, opts) {
|
|
136
128
|
if (opts === void 0) { opts = {}; }
|
|
137
129
|
return __awaiter(this, void 0, void 0, function () {
|
|
@@ -162,7 +154,7 @@ var TSGenerator = /** @class */ (function (_super) {
|
|
|
162
154
|
// This will install the installed SDK as a dependency within the current working directory,
|
|
163
155
|
// adding `@api/<sdk identifier>` as a dependency there so you can load it with
|
|
164
156
|
// `require('@api/<sdk identifier>)`.
|
|
165
|
-
return [2 /*return*/, (0, execa_1["default"])('npm', __spreadArray(
|
|
157
|
+
return [2 /*return*/, (0, execa_1["default"])('npm', __spreadArray([], npmInstall, true).filter(Boolean), { cwd: storage.getIdentifierStorageDir() }).then(function (res) {
|
|
166
158
|
if (opts.dryRun) {
|
|
167
159
|
(opts.logger ? opts.logger : logger_1["default"])(res.command);
|
|
168
160
|
(opts.logger ? opts.logger : logger_1["default"])(res.stdout);
|
|
@@ -178,232 +170,278 @@ var TSGenerator = /** @class */ (function (_super) {
|
|
|
178
170
|
*/
|
|
179
171
|
TSGenerator.prototype.generator = function () {
|
|
180
172
|
return __awaiter(this, void 0, void 0, function () {
|
|
181
|
-
var
|
|
173
|
+
var sdkSource, types;
|
|
182
174
|
var _this = this;
|
|
183
|
-
return __generator(this, function (
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
writer.writeLine('this.spec = Oas.init(definition);');
|
|
225
|
-
writer.write('this.core = new APICore(this.spec, ').quote(_this.userAgent).write(');');
|
|
226
|
-
return writer;
|
|
175
|
+
return __generator(this, function (_a) {
|
|
176
|
+
sdkSource = this.createSourceFile();
|
|
177
|
+
if (Object.keys(this.schemas).length) {
|
|
178
|
+
this.createSchemasFile();
|
|
179
|
+
this.createTypesFile();
|
|
180
|
+
types = Array.from(this.types.keys());
|
|
181
|
+
types.sort();
|
|
182
|
+
sdkSource.addExportDeclarations([
|
|
183
|
+
{
|
|
184
|
+
isTypeOnly: true,
|
|
185
|
+
namedExports: types,
|
|
186
|
+
moduleSpecifier: './types'
|
|
187
|
+
},
|
|
188
|
+
]);
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
// If we don't have any schemas then we shouldn't import a `types` file that doesn't exist.
|
|
192
|
+
sdkSource
|
|
193
|
+
.getImportDeclarations()
|
|
194
|
+
.find(function (id) { return id.getText() === "import type * as types from './types';"; })
|
|
195
|
+
.remove();
|
|
196
|
+
}
|
|
197
|
+
// If this SDK doesn't use the `HTTPMethodRange` interface for handling `2XX` response status
|
|
198
|
+
// codes then we should remove it from being imported.
|
|
199
|
+
if (!this.usesHTTPMethodRangeInterface) {
|
|
200
|
+
sdkSource
|
|
201
|
+
.getImportDeclarations()
|
|
202
|
+
.find(function (id) { return id.getText().includes('HTTPMethodRange'); })
|
|
203
|
+
.replaceWithText("import type { ConfigOptions, FetchResponse } from 'api/dist/core'");
|
|
204
|
+
}
|
|
205
|
+
if (this.outputJS) {
|
|
206
|
+
return [2 /*return*/, this.project
|
|
207
|
+
.emitToMemory()
|
|
208
|
+
.getFiles()
|
|
209
|
+
.map(function (sourceFile) {
|
|
210
|
+
var _a;
|
|
211
|
+
var file = path_1["default"].basename(sourceFile.filePath);
|
|
212
|
+
if (file === 'schemas.js' || file === 'types.js') {
|
|
213
|
+
// If we're generating a JS SDK then we don't need to generate these two files as the
|
|
214
|
+
// user will have `.d.ts` files for them instead.
|
|
215
|
+
return {};
|
|
227
216
|
}
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
217
|
+
var code = (0, util_1.formatter)(sourceFile.text);
|
|
218
|
+
if (file === 'index.js' && _this.compilerTarget === 'cjs') {
|
|
219
|
+
/**
|
|
220
|
+
* There's an annoying quirk with `ts-morph` where if we're exporting a default export
|
|
221
|
+
* to a CJS environment, it'll export it as `exports.default`. Because we don't want
|
|
222
|
+
* folks in these environments to have to load their SDKs with
|
|
223
|
+
* `require('@api/sdk').default` we're overriding that here to change it to being the
|
|
224
|
+
* module exports.
|
|
225
|
+
*
|
|
226
|
+
* `ts-morph` unfortunately doesn't give us any options for programatically doing this
|
|
227
|
+
* so we need to resort to modifying the emitted JS code.
|
|
228
|
+
*/
|
|
229
|
+
code = code
|
|
230
|
+
.replace(/Object\.defineProperty\(exports, '__esModule', { value: true }\);\n/, '')
|
|
231
|
+
.replace('exports.default = createSDK;', 'module.exports = createSDK;');
|
|
232
|
+
}
|
|
233
|
+
return _a = {},
|
|
234
|
+
_a[file] = code,
|
|
235
|
+
_a;
|
|
236
|
+
})
|
|
237
|
+
.reduce(function (prev, next) { return Object.assign(prev, next); })];
|
|
238
|
+
}
|
|
239
|
+
return [2 /*return*/, __spreadArray(__spreadArray([], this.project.getSourceFiles().map(function (sourceFile) {
|
|
240
|
+
var _a;
|
|
241
|
+
return (_a = {},
|
|
242
|
+
_a[sourceFile.getBaseName()] = (0, util_1.formatter)(sourceFile.getFullText()),
|
|
243
|
+
_a);
|
|
244
|
+
}), true), this.project
|
|
245
|
+
.emitToMemory({ emitOnlyDtsFiles: true })
|
|
246
|
+
.getFiles()
|
|
247
|
+
.map(function (sourceFile) {
|
|
248
|
+
var _a;
|
|
249
|
+
return (_a = {},
|
|
250
|
+
_a[path_1["default"].basename(sourceFile.filePath)] = (0, util_1.formatter)(sourceFile.text),
|
|
251
|
+
_a);
|
|
252
|
+
}), true).reduce(function (prev, next) { return Object.assign(prev, next); })];
|
|
253
|
+
});
|
|
254
|
+
});
|
|
255
|
+
};
|
|
256
|
+
/**
|
|
257
|
+
* Create our main SDK source file.
|
|
258
|
+
*
|
|
259
|
+
*/
|
|
260
|
+
TSGenerator.prototype.createSourceFile = function () {
|
|
261
|
+
var _this = this;
|
|
262
|
+
var operations = this.loadOperationsAndMethods().operations;
|
|
263
|
+
var sourceFile = this.project.createSourceFile('index.ts', '');
|
|
264
|
+
sourceFile.addImportDeclarations([
|
|
265
|
+
// This import will be automatically removed later if the SDK ends up not having any types.
|
|
266
|
+
{ defaultImport: 'type * as types', moduleSpecifier: './types' },
|
|
267
|
+
{
|
|
268
|
+
// `HTTPMethodRange` will be conditionally removed later if it ends up not being used.
|
|
269
|
+
defaultImport: 'type { ConfigOptions, FetchResponse, HTTPMethodRange }',
|
|
270
|
+
moduleSpecifier: 'api/dist/core'
|
|
271
|
+
},
|
|
272
|
+
{ defaultImport: 'Oas', moduleSpecifier: 'oas' },
|
|
273
|
+
{ defaultImport: 'APICore', moduleSpecifier: 'api/dist/core' },
|
|
274
|
+
{ defaultImport: 'definition', moduleSpecifier: this.specPath },
|
|
275
|
+
]);
|
|
276
|
+
// @todo add TOS, License, info.* to a docblock at the top of the SDK.
|
|
277
|
+
this.sdk = sourceFile.addClass({
|
|
278
|
+
name: 'SDK',
|
|
279
|
+
properties: [
|
|
280
|
+
{ name: 'spec', type: 'Oas' },
|
|
281
|
+
{ name: 'core', type: 'APICore' },
|
|
282
|
+
]
|
|
283
|
+
});
|
|
284
|
+
this.sdk.addConstructor({
|
|
285
|
+
statements: function (writer) {
|
|
286
|
+
writer.writeLine('this.spec = Oas.init(definition);');
|
|
287
|
+
writer.write('this.core = new APICore(this.spec, ').quote(_this.userAgent).write(');');
|
|
288
|
+
return writer;
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
// Add our core API methods for controlling auth, servers, and various configurable abilities.
|
|
292
|
+
this.sdk.addMethods([
|
|
293
|
+
{
|
|
294
|
+
name: 'config',
|
|
295
|
+
parameters: [{ name: 'config', type: 'ConfigOptions' }],
|
|
296
|
+
statements: function (writer) { return writer.writeLine('this.core.setConfig(config);'); },
|
|
297
|
+
docs: [
|
|
298
|
+
{
|
|
299
|
+
description: function (writer) {
|
|
300
|
+
return writer.writeLine((0, util_1.wordWrap)('Optionally configure various options that the SDK allows.'));
|
|
301
|
+
},
|
|
302
|
+
tags: [
|
|
303
|
+
{ tagName: 'param', text: 'config Object of supported SDK options and toggles.' },
|
|
262
304
|
{
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
statements: function (writer) {
|
|
266
|
-
writer.writeLine('this.core.setAuth(...values);');
|
|
267
|
-
writer.writeLine('return this;');
|
|
268
|
-
return writer;
|
|
269
|
-
},
|
|
270
|
-
docs: [
|
|
271
|
-
{
|
|
272
|
-
description: function (writer) {
|
|
273
|
-
return writer.writeLine(wordWrap("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.\n\nWith the exception of OpenID and MutualTLS, it supports all forms of authentication supported by the OpenAPI specification.\n\n@example <caption>HTTP Basic auth</caption>\nsdk.auth('username', 'password');\n\n@example <caption>Bearer tokens (HTTP or OAuth 2)</caption>\nsdk.auth('myBearerToken');\n\n@example <caption>API Keys</caption>\nsdk.auth('myApiKey');"));
|
|
274
|
-
},
|
|
275
|
-
tags: [
|
|
276
|
-
{ tagName: 'see', text: '{@link https://spec.openapis.org/oas/v3.0.3#fixed-fields-22}' },
|
|
277
|
-
{ tagName: 'see', text: '{@link https://spec.openapis.org/oas/v3.1.0#fixed-fields-22}' },
|
|
278
|
-
{
|
|
279
|
-
tagName: 'param',
|
|
280
|
-
text: 'values Your auth credentials for the API; can specify up to two strings or numbers.'
|
|
281
|
-
},
|
|
282
|
-
]
|
|
283
|
-
},
|
|
284
|
-
]
|
|
305
|
+
tagName: 'param',
|
|
306
|
+
text: (0, util_1.wordWrap)('config.timeout Override the default `fetch` request timeout of 30 seconds. This number should be represented in milliseconds.')
|
|
285
307
|
},
|
|
308
|
+
]
|
|
309
|
+
},
|
|
310
|
+
]
|
|
311
|
+
},
|
|
312
|
+
{
|
|
313
|
+
name: 'auth',
|
|
314
|
+
parameters: [{ name: '...values', type: 'string[] | number[]' }],
|
|
315
|
+
statements: function (writer) {
|
|
316
|
+
writer.writeLine('this.core.setAuth(...values);');
|
|
317
|
+
writer.writeLine('return this;');
|
|
318
|
+
return writer;
|
|
319
|
+
},
|
|
320
|
+
docs: [
|
|
321
|
+
{
|
|
322
|
+
description: function (writer) {
|
|
323
|
+
return writer.writeLine((0, util_1.wordWrap)("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.\n\nWith the exception of OpenID and MutualTLS, it supports all forms of authentication supported by the OpenAPI specification.\n\n@example <caption>HTTP Basic auth</caption>\nsdk.auth('username', 'password');\n\n@example <caption>Bearer tokens (HTTP or OAuth 2)</caption>\nsdk.auth('myBearerToken');\n\n@example <caption>API Keys</caption>\nsdk.auth('myApiKey');"));
|
|
324
|
+
},
|
|
325
|
+
tags: [
|
|
326
|
+
{ tagName: 'see', text: '{@link https://spec.openapis.org/oas/v3.0.3#fixed-fields-22}' },
|
|
327
|
+
{ tagName: 'see', text: '{@link https://spec.openapis.org/oas/v3.1.0#fixed-fields-22}' },
|
|
286
328
|
{
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
{ name: 'url', type: 'string' },
|
|
290
|
-
{ name: 'variables', initializer: '{}' },
|
|
291
|
-
],
|
|
292
|
-
statements: function (writer) { return writer.writeLine('this.core.setServer(url, variables);'); },
|
|
293
|
-
docs: [
|
|
294
|
-
{
|
|
295
|
-
description: function (writer) {
|
|
296
|
-
return writer.writeLine(wordWrap("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).\n\n@example <caption>Server URL with server variables</caption>\nsdk.server('https://{region}.api.example.com/{basePath}', {\n name: 'eu',\n basePath: 'v14',\n});\n\n@example <caption>Fully qualified server URL</caption>\nsdk.server('https://eu.api.example.com/v14');"));
|
|
297
|
-
},
|
|
298
|
-
tags: [
|
|
299
|
-
{ tagName: 'param', text: 'url Server URL' },
|
|
300
|
-
{ tagName: 'param', text: 'variables An object of variables to replace into the server URL.' },
|
|
301
|
-
]
|
|
302
|
-
},
|
|
303
|
-
]
|
|
329
|
+
tagName: 'param',
|
|
330
|
+
text: 'values Your auth credentials for the API; can specify up to two strings or numbers.'
|
|
304
331
|
},
|
|
305
|
-
]
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
return
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
332
|
+
]
|
|
333
|
+
},
|
|
334
|
+
]
|
|
335
|
+
},
|
|
336
|
+
{
|
|
337
|
+
name: 'server',
|
|
338
|
+
parameters: [
|
|
339
|
+
{ name: 'url', type: 'string' },
|
|
340
|
+
{ name: 'variables', initializer: '{}' },
|
|
341
|
+
],
|
|
342
|
+
statements: function (writer) { return writer.writeLine('this.core.setServer(url, variables);'); },
|
|
343
|
+
docs: [
|
|
344
|
+
{
|
|
345
|
+
description: function (writer) {
|
|
346
|
+
return writer.writeLine((0, util_1.wordWrap)("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).\n\n@example <caption>Server URL with server variables</caption>\nsdk.server('https://{region}.api.example.com/{basePath}', {\n name: 'eu',\n basePath: 'v14',\n});\n\n@example <caption>Fully qualified server URL</caption>\nsdk.server('https://eu.api.example.com/v14');"));
|
|
347
|
+
},
|
|
348
|
+
tags: [
|
|
349
|
+
{ tagName: 'param', text: 'url Server URL' },
|
|
350
|
+
{ tagName: 'param', text: 'variables An object of variables to replace into the server URL.' },
|
|
351
|
+
]
|
|
352
|
+
},
|
|
353
|
+
]
|
|
354
|
+
},
|
|
355
|
+
]);
|
|
356
|
+
// Add all available operation ID accessors into the SDK.
|
|
357
|
+
Object.entries(operations).forEach(function (_a) {
|
|
358
|
+
var operationId = _a[0], data = _a[1];
|
|
359
|
+
_this.createOperationAccessor(data.operation, operationId, data.types.params, data.types.responses);
|
|
360
|
+
});
|
|
361
|
+
// Export our SDK into the source file.
|
|
362
|
+
sourceFile.addVariableStatement({
|
|
363
|
+
declarationKind: ts_morph_1.VariableDeclarationKind.Const,
|
|
364
|
+
declarations: [
|
|
365
|
+
{
|
|
366
|
+
name: 'createSDK',
|
|
367
|
+
initializer: function (writer) {
|
|
368
|
+
// `ts-morph` doesn't have any way to cleanly create an IFEE.
|
|
369
|
+
writer.writeLine('(() => { return new SDK(); })()');
|
|
370
|
+
return writer;
|
|
371
|
+
}
|
|
372
|
+
},
|
|
373
|
+
]
|
|
374
|
+
});
|
|
375
|
+
sourceFile.addExportAssignment({ isExportEquals: false, expression: 'createSDK' });
|
|
376
|
+
return sourceFile;
|
|
377
|
+
};
|
|
378
|
+
/**
|
|
379
|
+
* Create our main schemas file. This is where all of the JSON Schema that our TypeScript typing
|
|
380
|
+
* infrastructure sources its data from. Without this there are no types.
|
|
381
|
+
*
|
|
382
|
+
*/
|
|
383
|
+
TSGenerator.prototype.createSchemasFile = function () {
|
|
384
|
+
var sourceFile = this.project.createSourceFile('schemas.ts', '');
|
|
385
|
+
var sortedSchemas = new Map(Array.from(Object.entries(this.schemas)).sort());
|
|
386
|
+
Array.from(sortedSchemas).forEach(function (_a) {
|
|
387
|
+
var schemaName = _a[0], schema = _a[1];
|
|
388
|
+
sourceFile.addVariableStatement({
|
|
389
|
+
declarationKind: ts_morph_1.VariableDeclarationKind.Const,
|
|
390
|
+
declarations: [
|
|
391
|
+
{
|
|
392
|
+
name: schemaName,
|
|
393
|
+
initializer: function (writer) {
|
|
394
|
+
/**
|
|
395
|
+
* This is the conversion prefix that we add to all `$ref` pointers we find in
|
|
396
|
+
* generated JSON Schema.
|
|
397
|
+
*
|
|
398
|
+
* Because the pointer name is a string we want to have it reference the schema
|
|
399
|
+
* constant we're adding into the codegen'd schema file. As there's no way, not even
|
|
400
|
+
* using `eval()` in this case, to convert a string to a constant we're prefixing
|
|
401
|
+
* them with this so we can later remove it and rewrite the value to a literal.
|
|
402
|
+
* eg. `'Pet'` becomes `Pet`.
|
|
403
|
+
*
|
|
404
|
+
* And because our TypeScript type name generator properly ignores `:`, this is safe
|
|
405
|
+
* to prepend to all generated type names.
|
|
406
|
+
*/
|
|
407
|
+
var str = JSON.stringify(schema);
|
|
408
|
+
str = str.replace(/"::convert::([a-zA-Z_$\\d]*)"/g, '$1');
|
|
409
|
+
writer.writeLine("".concat(str, " as const"));
|
|
410
|
+
return writer;
|
|
329
411
|
}
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
return (_a = {},
|
|
333
|
-
_a[sourceFile.getBaseName()] = TSGenerator.formatter(sourceFile.getFullText()),
|
|
334
|
-
_a);
|
|
335
|
-
}), true), this.project
|
|
336
|
-
.emitToMemory({ emitOnlyDtsFiles: true })
|
|
337
|
-
.getFiles()
|
|
338
|
-
.map(function (sourceFile) {
|
|
339
|
-
var _a;
|
|
340
|
-
return (_a = {},
|
|
341
|
-
_a[path_1["default"].basename(sourceFile.filePath)] = TSGenerator.formatter(sourceFile.text),
|
|
342
|
-
_a);
|
|
343
|
-
}), true).reduce(function (prev, next) { return Object.assign(prev, next); })];
|
|
344
|
-
}
|
|
412
|
+
},
|
|
413
|
+
]
|
|
345
414
|
});
|
|
346
415
|
});
|
|
416
|
+
sourceFile.addStatements("export { ".concat(Array.from(sortedSchemas.keys()).join(', '), " }"));
|
|
417
|
+
return sourceFile;
|
|
347
418
|
};
|
|
348
419
|
/**
|
|
349
|
-
* Create
|
|
420
|
+
* Create our main types file. This sources its data from the JSON Schema `schemas.ts` file and
|
|
421
|
+
* will re-export types to be used in TypeScript implementations and IDE intellisense. This
|
|
422
|
+
* typing work is functional with the `json-schema-to-ts` library.
|
|
350
423
|
*
|
|
351
|
-
* @
|
|
424
|
+
* @see {@link https://npm.im/json-schema-to-ts}
|
|
352
425
|
*/
|
|
353
|
-
TSGenerator.prototype.
|
|
354
|
-
var
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
// Method generic body + metadata parameters are always optional.
|
|
363
|
-
if (method !== 'get') {
|
|
364
|
-
parameters.push({ name: 'body', type: 'unknown', hasQuestionToken: true });
|
|
365
|
-
docblock.tags.push({ tagName: 'param', text: 'body Request body payload data.' });
|
|
366
|
-
}
|
|
367
|
-
parameters.push({ name: 'metadata', type: 'Record<string, unknown>', hasQuestionToken: true });
|
|
368
|
-
docblock.tags.push({
|
|
369
|
-
tagName: 'param',
|
|
370
|
-
text: 'metadata Object containing all path, query, header, and cookie parameters to supply.'
|
|
426
|
+
TSGenerator.prototype.createTypesFile = function () {
|
|
427
|
+
var sourceFile = this.project.createSourceFile('types.ts', '');
|
|
428
|
+
sourceFile.addImportDeclarations([
|
|
429
|
+
{ defaultImport: 'type { FromSchema }', moduleSpecifier: 'json-schema-to-ts' },
|
|
430
|
+
{ defaultImport: '* as schemas', moduleSpecifier: './schemas' },
|
|
431
|
+
]);
|
|
432
|
+
Array.from(new Map(Array.from(this.types.entries()).sort())).forEach(function (_a) {
|
|
433
|
+
var typeName = _a[0], typeExpression = _a[1];
|
|
434
|
+
sourceFile.addTypeAlias({ isExported: true, name: typeName, type: typeExpression });
|
|
371
435
|
});
|
|
372
|
-
|
|
373
|
-
name: method,
|
|
374
|
-
returnType: 'Promise<T>',
|
|
375
|
-
parameters: parameters,
|
|
376
|
-
typeParameters: ['T = unknown'],
|
|
377
|
-
docs: [docblock],
|
|
378
|
-
statements: function (writer) {
|
|
379
|
-
/**
|
|
380
|
-
* @example return this.core.fetch(path, 'get', body, metadata);
|
|
381
|
-
* @example return this.core.fetch(path, 'get', metadata);
|
|
382
|
-
*/
|
|
383
|
-
var fetchStmt = writer.write('return this.core.fetch(path, ').quote(method).write(', ');
|
|
384
|
-
var fetchArgs = parameters.slice(1).map(function (p) { return p.name; });
|
|
385
|
-
fetchArgs.forEach(function (arg, i) {
|
|
386
|
-
fetchStmt.write(arg);
|
|
387
|
-
if (fetchArgs.length > 1 && i !== fetchArgs.length) {
|
|
388
|
-
fetchStmt.write(', ');
|
|
389
|
-
}
|
|
390
|
-
});
|
|
391
|
-
fetchStmt.write(');');
|
|
392
|
-
return fetchStmt;
|
|
393
|
-
}
|
|
394
|
-
}));
|
|
436
|
+
return sourceFile;
|
|
395
437
|
};
|
|
396
438
|
/**
|
|
397
439
|
* Create operation accessors on the SDK.
|
|
398
440
|
*
|
|
399
|
-
* @param operation
|
|
400
|
-
* @param operationId
|
|
401
|
-
* @param paramTypes
|
|
402
|
-
* @param responseTypes
|
|
403
441
|
*/
|
|
404
442
|
TSGenerator.prototype.createOperationAccessor = function (operation, operationId, paramTypes, responseTypes) {
|
|
405
443
|
var _this = this;
|
|
406
|
-
var docblock = {
|
|
444
|
+
var docblock = {};
|
|
407
445
|
var summary = operation.getSummary();
|
|
408
446
|
var description = operation.getDescription();
|
|
409
447
|
if (summary || description) {
|
|
@@ -412,16 +450,16 @@ var TSGenerator = /** @class */ (function (_super) {
|
|
|
412
450
|
// what we surface the main docblock description.
|
|
413
451
|
docblock.description = function (writer) {
|
|
414
452
|
if (description) {
|
|
415
|
-
writer.writeLine(description);
|
|
453
|
+
writer.writeLine((0, util_1.docblockEscape)((0, util_1.wordWrap)(description)));
|
|
416
454
|
}
|
|
417
455
|
else if (summary) {
|
|
418
|
-
writer.writeLine(summary);
|
|
456
|
+
writer.writeLine((0, util_1.docblockEscape)((0, util_1.wordWrap)(summary)));
|
|
419
457
|
}
|
|
420
458
|
writer.newLineIfLastNot();
|
|
421
459
|
return writer;
|
|
422
460
|
};
|
|
423
461
|
if (summary && description) {
|
|
424
|
-
docblock.tags
|
|
462
|
+
docblock.tags = [{ tagName: 'summary', text: (0, util_1.docblockEscape)((0, util_1.wordWrap)(summary)) }];
|
|
425
463
|
}
|
|
426
464
|
}
|
|
427
465
|
var hasOptionalBody = false;
|
|
@@ -434,9 +472,7 @@ var TSGenerator = /** @class */ (function (_super) {
|
|
|
434
472
|
hasOptionalBody = !operation.hasRequiredRequestBody();
|
|
435
473
|
parameters.body = {
|
|
436
474
|
name: 'body',
|
|
437
|
-
type: paramTypes.body
|
|
438
|
-
? this.schemas.get(paramTypes.body).tsType
|
|
439
|
-
: this.schemas.get(paramTypes.formData).tsType,
|
|
475
|
+
type: paramTypes.body ? paramTypes.body : paramTypes.formData,
|
|
440
476
|
hasQuestionToken: hasOptionalBody
|
|
441
477
|
};
|
|
442
478
|
}
|
|
@@ -444,27 +480,37 @@ var TSGenerator = /** @class */ (function (_super) {
|
|
|
444
480
|
hasOptionalMetadata = !operation.hasRequiredParameters();
|
|
445
481
|
parameters.metadata = {
|
|
446
482
|
name: 'metadata',
|
|
447
|
-
type:
|
|
483
|
+
type: paramTypes.metadata,
|
|
448
484
|
hasQuestionToken: hasOptionalMetadata
|
|
449
485
|
};
|
|
450
486
|
}
|
|
451
487
|
}
|
|
452
|
-
var returnType = 'Promise<
|
|
453
|
-
var typeParameters = null;
|
|
488
|
+
var returnType = 'Promise<FetchResponse<number, unknown>>';
|
|
454
489
|
if (responseTypes) {
|
|
455
|
-
returnType = "Promise<".concat(Object.
|
|
456
|
-
.map(function (
|
|
490
|
+
returnType = "Promise<".concat(Object.entries(responseTypes)
|
|
491
|
+
.map(function (_a) {
|
|
492
|
+
var status = _a[0], responseType = _a[1];
|
|
493
|
+
if (status.toLowerCase() === 'default') {
|
|
494
|
+
return "FetchResponse<number, ".concat(responseType, ">");
|
|
495
|
+
}
|
|
496
|
+
else if (status.length === 3 && status.toUpperCase().endsWith('XX')) {
|
|
497
|
+
var statusPrefix = status.slice(0, 1);
|
|
498
|
+
if (!Number.isInteger(Number(statusPrefix))) {
|
|
499
|
+
// If this matches the `_XX` format, but it isn't `{number}XX` then we can't handle
|
|
500
|
+
// it and should instead fall back to treating it as an unknown number.
|
|
501
|
+
return "FetchResponse<number, ".concat(responseType, ">");
|
|
502
|
+
}
|
|
503
|
+
_this.usesHTTPMethodRangeInterface = true;
|
|
504
|
+
return "FetchResponse<HTTPMethodRange<".concat(statusPrefix, "00, ").concat(statusPrefix, "99>, ").concat(responseType, ">");
|
|
505
|
+
}
|
|
506
|
+
return "FetchResponse<".concat(status, ", ").concat(responseType, ">");
|
|
507
|
+
})
|
|
457
508
|
.join(' | '), ">");
|
|
458
509
|
}
|
|
459
|
-
else {
|
|
460
|
-
// We should only add the `<T>` method typing if we don't have any response types present.
|
|
461
|
-
typeParameters = ['T = unknown'];
|
|
462
|
-
}
|
|
463
510
|
var operationIdAccessor = this.sdk.addMethod({
|
|
464
511
|
name: operationId,
|
|
465
|
-
typeParameters: typeParameters,
|
|
466
512
|
returnType: returnType,
|
|
467
|
-
docs: docblock ? [docblock] : null,
|
|
513
|
+
docs: Object.keys(docblock).length ? [docblock] : null,
|
|
468
514
|
statements: function (writer) {
|
|
469
515
|
/**
|
|
470
516
|
* @example return this.core.fetch('/pet/findByStatus', 'get', body, metadata);
|
|
@@ -501,20 +547,18 @@ var TSGenerator = /** @class */ (function (_super) {
|
|
|
501
547
|
if (shouldAddAltTypedOverloads) {
|
|
502
548
|
// Create an overload that has both `body` and `metadata` parameters as required.
|
|
503
549
|
operationIdAccessor.addOverload({
|
|
504
|
-
typeParameters: typeParameters,
|
|
505
550
|
parameters: [
|
|
506
551
|
__assign(__assign({}, parameters.body), { hasQuestionToken: false }),
|
|
507
552
|
__assign(__assign({}, parameters.metadata), { hasQuestionToken: false }),
|
|
508
553
|
],
|
|
509
554
|
returnType: returnType,
|
|
510
|
-
docs: docblock ? [docblock] : null
|
|
555
|
+
docs: Object.keys(docblock).length ? [docblock] : null
|
|
511
556
|
});
|
|
512
557
|
// Create an overload that just has a single `metadata` parameter.
|
|
513
558
|
operationIdAccessor.addOverload({
|
|
514
|
-
typeParameters: typeParameters,
|
|
515
559
|
parameters: [__assign({}, parameters.metadata)],
|
|
516
560
|
returnType: returnType,
|
|
517
|
-
docs: docblock ? [docblock] : null
|
|
561
|
+
docs: Object.keys(docblock).length ? [docblock] : null
|
|
518
562
|
});
|
|
519
563
|
// Create an overload that has both `body` and `metadata` parameters as optional. Even though
|
|
520
564
|
// our `metadata` parameter is actually required for this operation this is the only way we're
|
|
@@ -524,88 +568,18 @@ var TSGenerator = /** @class */ (function (_super) {
|
|
|
524
568
|
// see if what the user is supplying is `metadata` or `body` content when they supply one or
|
|
525
569
|
// both.
|
|
526
570
|
operationIdAccessor.addParameters([
|
|
527
|
-
__assign(__assign({}, parameters.body), {
|
|
571
|
+
__assign(__assign({}, parameters.body), {
|
|
572
|
+
// Overloads have to be the most distilled version of the method so that's why we need to
|
|
573
|
+
// type `body` as either `body` or `metadata`. If we didn't do this, if `body` was a JSON
|
|
574
|
+
// Schema type that didn't allow `additionalProperties` then the implementation overload
|
|
575
|
+
// would throw type errors.
|
|
576
|
+
type: "".concat(parameters.body.type, " | ").concat(parameters.metadata.type), hasQuestionToken: true }),
|
|
528
577
|
__assign(__assign({}, parameters.metadata), { hasQuestionToken: true }),
|
|
529
578
|
]);
|
|
530
579
|
}
|
|
531
580
|
else {
|
|
532
581
|
operationIdAccessor.addParameters(Object.values(parameters));
|
|
533
582
|
}
|
|
534
|
-
// Add a typed generic HTTP method overload for this operation.
|
|
535
|
-
if (this.methodGenerics.has(operation.method)) {
|
|
536
|
-
// If we created alternate overloads for the operation accessor then we need to do the same
|
|
537
|
-
// for its generic HTTP counterpart.
|
|
538
|
-
if (shouldAddAltTypedOverloads) {
|
|
539
|
-
// Create an overload that has both `body` and `metadata` parameters as required.
|
|
540
|
-
this.methodGenerics.get(operation.method).addOverload({
|
|
541
|
-
typeParameters: typeParameters,
|
|
542
|
-
parameters: [
|
|
543
|
-
{ name: 'path', type: "'".concat(operation.path, "'") },
|
|
544
|
-
__assign(__assign({}, parameters.body), { hasQuestionToken: false }),
|
|
545
|
-
__assign(__assign({}, parameters.metadata), { hasQuestionToken: false }),
|
|
546
|
-
],
|
|
547
|
-
returnType: returnType,
|
|
548
|
-
docs: docblock ? [docblock] : null
|
|
549
|
-
});
|
|
550
|
-
// Create an overload that just has a single `metadata` parameter.
|
|
551
|
-
this.methodGenerics.get(operation.method).addOverload({
|
|
552
|
-
typeParameters: typeParameters,
|
|
553
|
-
parameters: [{ name: 'path', type: "'".concat(operation.path, "'") }, parameters.metadata],
|
|
554
|
-
returnType: returnType,
|
|
555
|
-
docs: docblock ? [docblock] : null
|
|
556
|
-
});
|
|
557
|
-
}
|
|
558
|
-
else {
|
|
559
|
-
this.methodGenerics.get(operation.method).addOverload({
|
|
560
|
-
typeParameters: responseTypes ? null : ['T = unknown'],
|
|
561
|
-
parameters: __spreadArray([{ name: 'path', type: "'".concat(operation.path, "'") }], Object.values(parameters), true),
|
|
562
|
-
returnType: returnType,
|
|
563
|
-
docs: docblock ? [docblock] : null
|
|
564
|
-
});
|
|
565
|
-
}
|
|
566
|
-
}
|
|
567
|
-
};
|
|
568
|
-
/**
|
|
569
|
-
* Convert a JSON Schema object into a readily available TypeScript type or interface along with
|
|
570
|
-
* any `$ref` pointers that are in use and turn those into TS types too.
|
|
571
|
-
*
|
|
572
|
-
* Under the hood this uses https://npm.im/json-schema-to-typescript for all composition and
|
|
573
|
-
* conversion.
|
|
574
|
-
*
|
|
575
|
-
* @param schema
|
|
576
|
-
* @param name
|
|
577
|
-
*/
|
|
578
|
-
TSGenerator.prototype.convertJSONSchemaToTypescript = function (schema, name) {
|
|
579
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
580
|
-
var ts, primaryType, tempProject, declarations;
|
|
581
|
-
var _this = this;
|
|
582
|
-
return __generator(this, function (_a) {
|
|
583
|
-
switch (_a.label) {
|
|
584
|
-
case 0: return [4 /*yield*/, (0, json_schema_to_typescript_1.compile)(schema, name, {
|
|
585
|
-
bannerComment: '',
|
|
586
|
-
// Running Prettier here for every JSON Schema object we're generating is way too slow so
|
|
587
|
-
// we're instead running it at the very end after we've constructed the SDK.
|
|
588
|
-
format: false
|
|
589
|
-
})];
|
|
590
|
-
case 1:
|
|
591
|
-
ts = _a.sent();
|
|
592
|
-
tempProject = this.project.createSourceFile("".concat(name, ".types.tmp.ts"), ts);
|
|
593
|
-
declarations = tempProject.getExportedDeclarations();
|
|
594
|
-
Array.from(declarations.keys()).forEach(function (declarationName) {
|
|
595
|
-
if (!primaryType) {
|
|
596
|
-
primaryType = declarationName;
|
|
597
|
-
}
|
|
598
|
-
declarations.get(declarationName).forEach(function (declaration) {
|
|
599
|
-
_this.types.set(declarationName, declaration.getText());
|
|
600
|
-
});
|
|
601
|
-
});
|
|
602
|
-
this.project.removeSourceFile(tempProject);
|
|
603
|
-
return [2 /*return*/, {
|
|
604
|
-
primaryType: primaryType
|
|
605
|
-
}];
|
|
606
|
-
}
|
|
607
|
-
});
|
|
608
|
-
});
|
|
609
583
|
};
|
|
610
584
|
/**
|
|
611
585
|
* Scour through the current OpenAPI definition and compile a store of every operation, along
|
|
@@ -614,78 +588,59 @@ var TSGenerator = /** @class */ (function (_super) {
|
|
|
614
588
|
*
|
|
615
589
|
*/
|
|
616
590
|
TSGenerator.prototype.loadOperationsAndMethods = function () {
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
if (operation.hasOperationId()) {
|
|
640
|
-
operations[operationId] = {
|
|
641
|
-
types: {
|
|
642
|
-
params: params,
|
|
643
|
-
responses: responses
|
|
644
|
-
},
|
|
645
|
-
operation: operation
|
|
646
|
-
};
|
|
647
|
-
}
|
|
648
|
-
});
|
|
649
|
-
});
|
|
650
|
-
// Run through and convert every schema we need to use into TS types.
|
|
651
|
-
return [4 /*yield*/, Promise.all(Array.from(this.schemas.entries()).map(function (_a) {
|
|
652
|
-
var hash = _a[0], _b = _a[1], schema = _b.schema, schemaName = _b.name;
|
|
653
|
-
return __awaiter(_this, void 0, void 0, function () {
|
|
654
|
-
var ts;
|
|
655
|
-
return __generator(this, function (_c) {
|
|
656
|
-
switch (_c.label) {
|
|
657
|
-
case 0: return [4 /*yield*/, this.convertJSONSchemaToTypescript(schema, schemaName)];
|
|
658
|
-
case 1:
|
|
659
|
-
ts = _c.sent();
|
|
660
|
-
this.schemas.set(hash, __assign(__assign({}, this.schemas.get(hash)), { tsType: ts.primaryType }));
|
|
661
|
-
return [2 /*return*/];
|
|
662
|
-
}
|
|
663
|
-
});
|
|
664
|
-
});
|
|
665
|
-
}))];
|
|
666
|
-
case 1:
|
|
667
|
-
// Run through and convert every schema we need to use into TS types.
|
|
668
|
-
_a.sent();
|
|
669
|
-
return [2 /*return*/, {
|
|
670
|
-
operations: operations,
|
|
671
|
-
methods: methods
|
|
672
|
-
}];
|
|
673
|
-
}
|
|
591
|
+
var _this = this;
|
|
592
|
+
var operations = {};
|
|
593
|
+
var methods = new Set();
|
|
594
|
+
// Prepare all of the schemas that we need to process for every operation within this API
|
|
595
|
+
// definition.
|
|
596
|
+
Object.entries(this.spec.getPaths()).forEach(function (_a) {
|
|
597
|
+
var ops = _a[1];
|
|
598
|
+
Object.entries(ops).forEach(function (_a) {
|
|
599
|
+
var method = _a[0], operation = _a[1];
|
|
600
|
+
methods.add(method);
|
|
601
|
+
var operationId = operation.getOperationId({
|
|
602
|
+
// This `camelCase` option will clean up any weird characters that might be present in
|
|
603
|
+
// the `operationId` so as we don't break TS compilation with an invalid method accessor.
|
|
604
|
+
camelCase: true
|
|
605
|
+
});
|
|
606
|
+
operations[operationId] = {
|
|
607
|
+
types: {
|
|
608
|
+
params: _this.prepareParameterTypesForOperation(operation, operationId),
|
|
609
|
+
responses: _this.prepareResponseTypesForOperation(operation, operationId)
|
|
610
|
+
},
|
|
611
|
+
operation: operation
|
|
612
|
+
};
|
|
674
613
|
});
|
|
675
614
|
});
|
|
615
|
+
if (!Object.keys(operations).length) {
|
|
616
|
+
throw new Error('Sorry, this OpenAPI definition does not have any operation paths to generate an SDK for.');
|
|
617
|
+
}
|
|
618
|
+
return {
|
|
619
|
+
operations: operations,
|
|
620
|
+
methods: methods
|
|
621
|
+
};
|
|
676
622
|
};
|
|
677
623
|
/**
|
|
678
624
|
* Compile the parameter (path, query, cookie, and header) schemas for an API operation into
|
|
679
625
|
* usable TypeScript types.
|
|
680
626
|
*
|
|
681
|
-
* @param operation
|
|
682
|
-
* @param operationId
|
|
683
627
|
*/
|
|
684
628
|
TSGenerator.prototype.prepareParameterTypesForOperation = function (operation, operationId) {
|
|
685
629
|
var _this = this;
|
|
686
|
-
var schemas = operation.
|
|
630
|
+
var schemas = operation.getParametersAsJSONSchema({
|
|
631
|
+
includeDiscriminatorMappingRefs: false,
|
|
687
632
|
mergeIntoBodyAndMetadata: true,
|
|
688
|
-
retainDeprecatedProperties: true
|
|
633
|
+
retainDeprecatedProperties: true,
|
|
634
|
+
transformer: function (s) {
|
|
635
|
+
// As our schemas are dereferenced in the `oas` library we don't want to pollute our
|
|
636
|
+
// codegen'd schemas file with duplicate schemas.
|
|
637
|
+
if ('x-readme-ref-name' in s) {
|
|
638
|
+
var typeName = (0, util_1.generateTypeName)(s['x-readme-ref-name']);
|
|
639
|
+
_this.addSchemaToExport(s, typeName, typeName);
|
|
640
|
+
return "::convert::".concat(typeName);
|
|
641
|
+
}
|
|
642
|
+
return s;
|
|
643
|
+
}
|
|
689
644
|
});
|
|
690
645
|
if (!schemas || !schemas.length) {
|
|
691
646
|
return false;
|
|
@@ -700,19 +655,20 @@ var TSGenerator = /** @class */ (function (_super) {
|
|
|
700
655
|
.map(function (_a) {
|
|
701
656
|
var _b;
|
|
702
657
|
var paramType = _a[0], schema = _a[1];
|
|
703
|
-
var
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
});
|
|
658
|
+
var typeName;
|
|
659
|
+
if (typeof schema === 'string' && schema.startsWith('::convert::')) {
|
|
660
|
+
// If this schema is a string and has our conversion prefix then we've already created
|
|
661
|
+
// a type for it.
|
|
662
|
+
typeName = schema.replace('::convert::', '');
|
|
663
|
+
}
|
|
664
|
+
else {
|
|
665
|
+
typeName = (0, util_1.generateTypeName)(operationId, paramType, 'param');
|
|
666
|
+
_this.addSchemaToExport(schema, typeName, "".concat((0, util_1.generateTypeName)(operationId), ".").concat(paramType));
|
|
713
667
|
}
|
|
714
668
|
return _b = {},
|
|
715
|
-
|
|
669
|
+
// Types are prefixed with `types.` because that's how we're importing them from
|
|
670
|
+
// `types.d.ts`.
|
|
671
|
+
_b[paramType] = "types.".concat(typeName),
|
|
716
672
|
_b;
|
|
717
673
|
})
|
|
718
674
|
.reduce(function (prev, next) { return Object.assign(prev, next); }, {});
|
|
@@ -720,9 +676,6 @@ var TSGenerator = /** @class */ (function (_super) {
|
|
|
720
676
|
/**
|
|
721
677
|
* Compile the response schemas for an API operation into usable TypeScript types.
|
|
722
678
|
*
|
|
723
|
-
* @todo what does this do for a spec that has no responses?
|
|
724
|
-
* @param operation
|
|
725
|
-
* @param operationId
|
|
726
679
|
*/
|
|
727
680
|
TSGenerator.prototype.prepareResponseTypesForOperation = function (operation, operationId) {
|
|
728
681
|
var _this = this;
|
|
@@ -733,7 +686,19 @@ var TSGenerator = /** @class */ (function (_super) {
|
|
|
733
686
|
var schemas = responseStatusCodes
|
|
734
687
|
.map(function (status) {
|
|
735
688
|
var _a;
|
|
736
|
-
var schema = operation.
|
|
689
|
+
var schema = operation.getResponseAsJSONSchema(status, {
|
|
690
|
+
includeDiscriminatorMappingRefs: false,
|
|
691
|
+
transformer: function (s) {
|
|
692
|
+
// As our schemas are dereferenced in the `oas` library we don't want to pollute our
|
|
693
|
+
// codegen'd schemas file with duplicate schemas.
|
|
694
|
+
if ('x-readme-ref-name' in s) {
|
|
695
|
+
var typeName = (0, util_1.generateTypeName)(s['x-readme-ref-name']);
|
|
696
|
+
_this.addSchemaToExport(s, typeName, "".concat(typeName));
|
|
697
|
+
return "::convert::".concat(typeName);
|
|
698
|
+
}
|
|
699
|
+
return s;
|
|
700
|
+
}
|
|
701
|
+
});
|
|
737
702
|
if (!schema) {
|
|
738
703
|
return false;
|
|
739
704
|
}
|
|
@@ -746,24 +711,39 @@ var TSGenerator = /** @class */ (function (_super) {
|
|
|
746
711
|
.map(function (_a) {
|
|
747
712
|
var _b;
|
|
748
713
|
var status = _a[0], schema = _a[1].schema;
|
|
749
|
-
var
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
714
|
+
var typeName;
|
|
715
|
+
if (typeof schema === 'string' && schema.startsWith('::convert::')) {
|
|
716
|
+
// If this schema is a string and has our conversion prefix then we've already created
|
|
717
|
+
// a type for it.
|
|
718
|
+
typeName = schema.replace('::convert::', '');
|
|
719
|
+
}
|
|
720
|
+
else {
|
|
721
|
+
typeName = (0, util_1.generateTypeName)(operationId, 'response', status);
|
|
722
|
+
// Because `status` will usually be a number here we need to set the pointer for it
|
|
723
|
+
// within an `[]` as if we do `FromSchema<typeof schemas.operation.response.200>`,
|
|
724
|
+
// TypeScript will throw a compilation error.
|
|
725
|
+
_this.addSchemaToExport(schema, typeName, "".concat((0, util_1.generateTypeName)(operationId), ".response['").concat(status, "']"));
|
|
759
726
|
}
|
|
760
727
|
return _b = {},
|
|
761
|
-
|
|
728
|
+
// Types are prefixed with `types.` because that's how we're importing them from
|
|
729
|
+
// `types.d.ts`.
|
|
730
|
+
_b[status] = "types.".concat(typeName),
|
|
762
731
|
_b;
|
|
763
732
|
})
|
|
764
733
|
.reduce(function (prev, next) { return Object.assign(prev, next); }, {});
|
|
765
734
|
return Object.keys(res).length ? res : undefined;
|
|
766
735
|
};
|
|
736
|
+
/**
|
|
737
|
+
* Add a given schema into our schema dataset that we'll be be exporting as types.
|
|
738
|
+
*
|
|
739
|
+
*/
|
|
740
|
+
TSGenerator.prototype.addSchemaToExport = function (schema, typeName, pointer) {
|
|
741
|
+
if (this.types.has(typeName)) {
|
|
742
|
+
return;
|
|
743
|
+
}
|
|
744
|
+
(0, lodash_setwith_1["default"])(this.schemas, pointer, schema, Object);
|
|
745
|
+
this.types.set(typeName, "FromSchema<typeof schemas.".concat(pointer, ">"));
|
|
746
|
+
};
|
|
767
747
|
return TSGenerator;
|
|
768
748
|
}(language_1["default"]));
|
|
769
749
|
exports["default"] = TSGenerator;
|