@ps-aux/api-client-gen 0.0.1
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/dist/downloadSpec.d.ts +1 -0
- package/dist/foo.d.ts +1 -0
- package/dist/gen-model.d.ts +7 -0
- package/dist/generateOpenApiModel.d.ts +1 -0
- package/dist/go +4 -0
- package/dist/go.d.ts +2 -0
- package/dist/go.js +141 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.esm.js +135 -0
- package/dist/index.js +140 -0
- package/jest.config.js +17 -0
- package/package.json +25 -0
- package/rollup.config.mjs +26 -0
- package/src/downloadSpec.ts +24 -0
- package/src/gen-model.ts +59 -0
- package/src/generateOpenApiModel.ts +54 -0
- package/src/go.ts +13 -0
- package/src/index.ts +8 -0
- package/templates/http-client.eta +27 -0
- package/tsconfig.json +9 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const downloadSpec: (path: string, url: string) => Promise<unknown>;
|
package/dist/foo.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const foo = "123";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const generateOpenApiModel: (name: string, input: string, outputDir: string, log: (msg: string) => void, isNode?: boolean) => Promise<void>;
|
package/dist/go
ADDED
package/dist/go.d.ts
ADDED
package/dist/go.js
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
#!/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
var Path = require('path');
|
|
5
|
+
var fs = require('fs');
|
|
6
|
+
var axios = require('axios');
|
|
7
|
+
var swaggerTypescriptApi = require('swagger-typescript-api');
|
|
8
|
+
var fs$1 = require('fs/promises');
|
|
9
|
+
var cosmiconfig = require('cosmiconfig');
|
|
10
|
+
|
|
11
|
+
/******************************************************************************
|
|
12
|
+
Copyright (c) Microsoft Corporation.
|
|
13
|
+
|
|
14
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
15
|
+
purpose with or without fee is hereby granted.
|
|
16
|
+
|
|
17
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
18
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
19
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
20
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
21
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
22
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
23
|
+
PERFORMANCE OF THIS SOFTWARE.
|
|
24
|
+
***************************************************************************** */
|
|
25
|
+
/* global Reflect, Promise, SuppressedError, Symbol */
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
function __awaiter(thisArg, _arguments, P, generator) {
|
|
29
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
30
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
31
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
32
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
33
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
34
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
39
|
+
var e = new Error(message);
|
|
40
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const downloadSpec = (path, url) => __awaiter(void 0, void 0, void 0, function* () {
|
|
44
|
+
const res = yield axios({
|
|
45
|
+
url,
|
|
46
|
+
method: 'GET',
|
|
47
|
+
responseType: 'stream'
|
|
48
|
+
}).catch(err => {
|
|
49
|
+
throw err.toString();
|
|
50
|
+
});
|
|
51
|
+
const writer = fs.createWriteStream(path);
|
|
52
|
+
// TODO Format to readable JSON
|
|
53
|
+
yield res.data.pipe(writer);
|
|
54
|
+
return new Promise((resolve, reject) => {
|
|
55
|
+
writer.on('finish', resolve);
|
|
56
|
+
writer.on('error', err => {
|
|
57
|
+
reject(err);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
const generateOpenApiModel = (name, input, outputDir, log, isNode = false) => __awaiter(void 0, void 0, void 0, function* () {
|
|
63
|
+
log(`Will generate API client name=${name} to ${outputDir}, node=${isNode}`);
|
|
64
|
+
const specialMapping = isNode
|
|
65
|
+
? {
|
|
66
|
+
File: '{file: Buffer | stream.Readable, name: string}',
|
|
67
|
+
}
|
|
68
|
+
: {};
|
|
69
|
+
const dstFile = Path.join(outputDir, name + '.ts');
|
|
70
|
+
yield swaggerTypescriptApi.generateApi({
|
|
71
|
+
name,
|
|
72
|
+
output: outputDir,
|
|
73
|
+
input: input,
|
|
74
|
+
// modular: true,
|
|
75
|
+
httpClientType: 'axios',
|
|
76
|
+
templates: Path.resolve(getThisScriptDirname(), '../templates'),
|
|
77
|
+
defaultResponseAsSuccess: true,
|
|
78
|
+
// generateRouteTypes: true,
|
|
79
|
+
singleHttpClient: true,
|
|
80
|
+
// extractRequestBody: true,
|
|
81
|
+
cleanOutput: true,
|
|
82
|
+
moduleNameFirstTag: true,
|
|
83
|
+
codeGenConstructs: (struct) => ({
|
|
84
|
+
// @ts-ignore
|
|
85
|
+
Keyword: specialMapping,
|
|
86
|
+
}),
|
|
87
|
+
// extractRequestParams: true,
|
|
88
|
+
});
|
|
89
|
+
if (isNode) {
|
|
90
|
+
let content = yield fs$1.readFile(dstFile).then((r) => r.toString());
|
|
91
|
+
// Naive solution
|
|
92
|
+
content = 'import stream from \'node:stream\'\n' + content;
|
|
93
|
+
yield fs$1.writeFile(dstFile, content);
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
const getThisScriptDirname = () => {
|
|
97
|
+
// Might be problem in ESM mode
|
|
98
|
+
return __dirname;
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const generateModel = ({ dstDir, apiName, env, srcSpec, }, log = console.log) => __awaiter(void 0, void 0, void 0, function* () {
|
|
102
|
+
if (!srcSpec)
|
|
103
|
+
throw new Error(`Url or path ('srcSpec' not specified`);
|
|
104
|
+
if (!apiName)
|
|
105
|
+
throw new Error('apiName not specified');
|
|
106
|
+
if (!dstDir)
|
|
107
|
+
throw new Error('dstDir not specified');
|
|
108
|
+
if (!env)
|
|
109
|
+
throw new Error('env not specified');
|
|
110
|
+
const dir = Path.resolve(dstDir, apiName);
|
|
111
|
+
if (!fs.existsSync(dir)) {
|
|
112
|
+
log(`Creating dir ${dir}`);
|
|
113
|
+
fs.mkdirSync(dir, {
|
|
114
|
+
recursive: true,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
const clientDir = Path.resolve(dir, 'client');
|
|
118
|
+
const specPath = yield getSpecPath(srcSpec, dir);
|
|
119
|
+
yield generateOpenApiModel(apiName + '-api', specPath, clientDir, log, env === 'node');
|
|
120
|
+
// TODO does not seem to currently generate all schemas
|
|
121
|
+
// generateSchemas(
|
|
122
|
+
// Path.resolve(modelDir, 'data-contracts.ts'),
|
|
123
|
+
// Path.resolve(dir, 'schemas.ts')
|
|
124
|
+
// )
|
|
125
|
+
});
|
|
126
|
+
const getSpecPath = (urlOrPath, dir) => __awaiter(void 0, void 0, void 0, function* () {
|
|
127
|
+
if (!urlOrPath.startsWith('http')) {
|
|
128
|
+
if (!fs.existsSync(urlOrPath))
|
|
129
|
+
throw new Error(`Spec file ${urlOrPath} does not exists`);
|
|
130
|
+
return urlOrPath;
|
|
131
|
+
}
|
|
132
|
+
const specPath = Path.resolve(dir, `spec.json`);
|
|
133
|
+
yield downloadSpec(specPath, urlOrPath);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
const conf = cosmiconfig.cosmiconfigSync('api-client-gen')
|
|
137
|
+
.search();
|
|
138
|
+
// TODO validate config
|
|
139
|
+
if (!conf)
|
|
140
|
+
throw new Error(`No config provided`);
|
|
141
|
+
generateModel(conf.config);
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import Path from 'path';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import axios from 'axios';
|
|
4
|
+
import { generateApi } from 'swagger-typescript-api';
|
|
5
|
+
import fs$1 from 'fs/promises';
|
|
6
|
+
|
|
7
|
+
/******************************************************************************
|
|
8
|
+
Copyright (c) Microsoft Corporation.
|
|
9
|
+
|
|
10
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
11
|
+
purpose with or without fee is hereby granted.
|
|
12
|
+
|
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
14
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
15
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
16
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
17
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
18
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
19
|
+
PERFORMANCE OF THIS SOFTWARE.
|
|
20
|
+
***************************************************************************** */
|
|
21
|
+
/* global Reflect, Promise, SuppressedError, Symbol */
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
function __awaiter(thisArg, _arguments, P, generator) {
|
|
25
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
26
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
27
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
28
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
29
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
30
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
35
|
+
var e = new Error(message);
|
|
36
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const downloadSpec = (path, url) => __awaiter(void 0, void 0, void 0, function* () {
|
|
40
|
+
const res = yield axios({
|
|
41
|
+
url,
|
|
42
|
+
method: 'GET',
|
|
43
|
+
responseType: 'stream'
|
|
44
|
+
}).catch(err => {
|
|
45
|
+
throw err.toString();
|
|
46
|
+
});
|
|
47
|
+
const writer = fs.createWriteStream(path);
|
|
48
|
+
// TODO Format to readable JSON
|
|
49
|
+
yield res.data.pipe(writer);
|
|
50
|
+
return new Promise((resolve, reject) => {
|
|
51
|
+
writer.on('finish', resolve);
|
|
52
|
+
writer.on('error', err => {
|
|
53
|
+
reject(err);
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const generateOpenApiModel = (name, input, outputDir, log, isNode = false) => __awaiter(void 0, void 0, void 0, function* () {
|
|
59
|
+
log(`Will generate API client name=${name} to ${outputDir}, node=${isNode}`);
|
|
60
|
+
const specialMapping = isNode
|
|
61
|
+
? {
|
|
62
|
+
File: '{file: Buffer | stream.Readable, name: string}',
|
|
63
|
+
}
|
|
64
|
+
: {};
|
|
65
|
+
const dstFile = Path.join(outputDir, name + '.ts');
|
|
66
|
+
yield generateApi({
|
|
67
|
+
name,
|
|
68
|
+
output: outputDir,
|
|
69
|
+
input: input,
|
|
70
|
+
// modular: true,
|
|
71
|
+
httpClientType: 'axios',
|
|
72
|
+
templates: Path.resolve(getThisScriptDirname(), '../templates'),
|
|
73
|
+
defaultResponseAsSuccess: true,
|
|
74
|
+
// generateRouteTypes: true,
|
|
75
|
+
singleHttpClient: true,
|
|
76
|
+
// extractRequestBody: true,
|
|
77
|
+
cleanOutput: true,
|
|
78
|
+
moduleNameFirstTag: true,
|
|
79
|
+
codeGenConstructs: (struct) => ({
|
|
80
|
+
// @ts-ignore
|
|
81
|
+
Keyword: specialMapping,
|
|
82
|
+
}),
|
|
83
|
+
// extractRequestParams: true,
|
|
84
|
+
});
|
|
85
|
+
if (isNode) {
|
|
86
|
+
let content = yield fs$1.readFile(dstFile).then((r) => r.toString());
|
|
87
|
+
// Naive solution
|
|
88
|
+
content = 'import stream from \'node:stream\'\n' + content;
|
|
89
|
+
yield fs$1.writeFile(dstFile, content);
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
const getThisScriptDirname = () => {
|
|
93
|
+
// Might be problem in ESM mode
|
|
94
|
+
return __dirname;
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const generateModel = ({ dstDir, apiName, env, srcSpec, }, log = console.log) => __awaiter(void 0, void 0, void 0, function* () {
|
|
98
|
+
if (!srcSpec)
|
|
99
|
+
throw new Error(`Url or path ('srcSpec' not specified`);
|
|
100
|
+
if (!apiName)
|
|
101
|
+
throw new Error('apiName not specified');
|
|
102
|
+
if (!dstDir)
|
|
103
|
+
throw new Error('dstDir not specified');
|
|
104
|
+
if (!env)
|
|
105
|
+
throw new Error('env not specified');
|
|
106
|
+
const dir = Path.resolve(dstDir, apiName);
|
|
107
|
+
if (!fs.existsSync(dir)) {
|
|
108
|
+
log(`Creating dir ${dir}`);
|
|
109
|
+
fs.mkdirSync(dir, {
|
|
110
|
+
recursive: true,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
const clientDir = Path.resolve(dir, 'client');
|
|
114
|
+
const specPath = yield getSpecPath(srcSpec, dir);
|
|
115
|
+
yield generateOpenApiModel(apiName + '-api', specPath, clientDir, log, env === 'node');
|
|
116
|
+
// TODO does not seem to currently generate all schemas
|
|
117
|
+
// generateSchemas(
|
|
118
|
+
// Path.resolve(modelDir, 'data-contracts.ts'),
|
|
119
|
+
// Path.resolve(dir, 'schemas.ts')
|
|
120
|
+
// )
|
|
121
|
+
});
|
|
122
|
+
const getSpecPath = (urlOrPath, dir) => __awaiter(void 0, void 0, void 0, function* () {
|
|
123
|
+
if (!urlOrPath.startsWith('http')) {
|
|
124
|
+
if (!fs.existsSync(urlOrPath))
|
|
125
|
+
throw new Error(`Spec file ${urlOrPath} does not exists`);
|
|
126
|
+
return urlOrPath;
|
|
127
|
+
}
|
|
128
|
+
const specPath = Path.resolve(dir, `spec.json`);
|
|
129
|
+
yield downloadSpec(specPath, urlOrPath);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
// Bcs of mixing for ESM and CommonJS - todo fix somehow
|
|
133
|
+
var index = { generateModel };
|
|
134
|
+
|
|
135
|
+
export { index as default, generateModel };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
(function (global, factory) {
|
|
2
|
+
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('path'), require('fs'), require('axios'), require('swagger-typescript-api'), require('fs/promises')) :
|
|
3
|
+
typeof define === 'function' && define.amd ? define(['exports', 'path', 'fs', 'axios', 'swagger-typescript-api', 'fs/promises'], factory) :
|
|
4
|
+
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.MyLibrary = {}, global.Path, global.fs, global.axios, global.swaggerTypescriptApi, global.fs$1));
|
|
5
|
+
})(this, (function (exports, Path, fs, axios, swaggerTypescriptApi, fs$1) { 'use strict';
|
|
6
|
+
|
|
7
|
+
/******************************************************************************
|
|
8
|
+
Copyright (c) Microsoft Corporation.
|
|
9
|
+
|
|
10
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
11
|
+
purpose with or without fee is hereby granted.
|
|
12
|
+
|
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
14
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
15
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
16
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
17
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
18
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
19
|
+
PERFORMANCE OF THIS SOFTWARE.
|
|
20
|
+
***************************************************************************** */
|
|
21
|
+
/* global Reflect, Promise, SuppressedError, Symbol */
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
function __awaiter(thisArg, _arguments, P, generator) {
|
|
25
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
26
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
27
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
28
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
29
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
30
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
35
|
+
var e = new Error(message);
|
|
36
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const downloadSpec = (path, url) => __awaiter(void 0, void 0, void 0, function* () {
|
|
40
|
+
const res = yield axios({
|
|
41
|
+
url,
|
|
42
|
+
method: 'GET',
|
|
43
|
+
responseType: 'stream'
|
|
44
|
+
}).catch(err => {
|
|
45
|
+
throw err.toString();
|
|
46
|
+
});
|
|
47
|
+
const writer = fs.createWriteStream(path);
|
|
48
|
+
// TODO Format to readable JSON
|
|
49
|
+
yield res.data.pipe(writer);
|
|
50
|
+
return new Promise((resolve, reject) => {
|
|
51
|
+
writer.on('finish', resolve);
|
|
52
|
+
writer.on('error', err => {
|
|
53
|
+
reject(err);
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const generateOpenApiModel = (name, input, outputDir, log, isNode = false) => __awaiter(void 0, void 0, void 0, function* () {
|
|
59
|
+
log(`Will generate API client name=${name} to ${outputDir}, node=${isNode}`);
|
|
60
|
+
const specialMapping = isNode
|
|
61
|
+
? {
|
|
62
|
+
File: '{file: Buffer | stream.Readable, name: string}',
|
|
63
|
+
}
|
|
64
|
+
: {};
|
|
65
|
+
const dstFile = Path.join(outputDir, name + '.ts');
|
|
66
|
+
yield swaggerTypescriptApi.generateApi({
|
|
67
|
+
name,
|
|
68
|
+
output: outputDir,
|
|
69
|
+
input: input,
|
|
70
|
+
// modular: true,
|
|
71
|
+
httpClientType: 'axios',
|
|
72
|
+
templates: Path.resolve(getThisScriptDirname(), '../templates'),
|
|
73
|
+
defaultResponseAsSuccess: true,
|
|
74
|
+
// generateRouteTypes: true,
|
|
75
|
+
singleHttpClient: true,
|
|
76
|
+
// extractRequestBody: true,
|
|
77
|
+
cleanOutput: true,
|
|
78
|
+
moduleNameFirstTag: true,
|
|
79
|
+
codeGenConstructs: (struct) => ({
|
|
80
|
+
// @ts-ignore
|
|
81
|
+
Keyword: specialMapping,
|
|
82
|
+
}),
|
|
83
|
+
// extractRequestParams: true,
|
|
84
|
+
});
|
|
85
|
+
if (isNode) {
|
|
86
|
+
let content = yield fs$1.readFile(dstFile).then((r) => r.toString());
|
|
87
|
+
// Naive solution
|
|
88
|
+
content = 'import stream from \'node:stream\'\n' + content;
|
|
89
|
+
yield fs$1.writeFile(dstFile, content);
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
const getThisScriptDirname = () => {
|
|
93
|
+
// Might be problem in ESM mode
|
|
94
|
+
return __dirname;
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const generateModel = ({ dstDir, apiName, env, srcSpec, }, log = console.log) => __awaiter(void 0, void 0, void 0, function* () {
|
|
98
|
+
if (!srcSpec)
|
|
99
|
+
throw new Error(`Url or path ('srcSpec' not specified`);
|
|
100
|
+
if (!apiName)
|
|
101
|
+
throw new Error('apiName not specified');
|
|
102
|
+
if (!dstDir)
|
|
103
|
+
throw new Error('dstDir not specified');
|
|
104
|
+
if (!env)
|
|
105
|
+
throw new Error('env not specified');
|
|
106
|
+
const dir = Path.resolve(dstDir, apiName);
|
|
107
|
+
if (!fs.existsSync(dir)) {
|
|
108
|
+
log(`Creating dir ${dir}`);
|
|
109
|
+
fs.mkdirSync(dir, {
|
|
110
|
+
recursive: true,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
const clientDir = Path.resolve(dir, 'client');
|
|
114
|
+
const specPath = yield getSpecPath(srcSpec, dir);
|
|
115
|
+
yield generateOpenApiModel(apiName + '-api', specPath, clientDir, log, env === 'node');
|
|
116
|
+
// TODO does not seem to currently generate all schemas
|
|
117
|
+
// generateSchemas(
|
|
118
|
+
// Path.resolve(modelDir, 'data-contracts.ts'),
|
|
119
|
+
// Path.resolve(dir, 'schemas.ts')
|
|
120
|
+
// )
|
|
121
|
+
});
|
|
122
|
+
const getSpecPath = (urlOrPath, dir) => __awaiter(void 0, void 0, void 0, function* () {
|
|
123
|
+
if (!urlOrPath.startsWith('http')) {
|
|
124
|
+
if (!fs.existsSync(urlOrPath))
|
|
125
|
+
throw new Error(`Spec file ${urlOrPath} does not exists`);
|
|
126
|
+
return urlOrPath;
|
|
127
|
+
}
|
|
128
|
+
const specPath = Path.resolve(dir, `spec.json`);
|
|
129
|
+
yield downloadSpec(specPath, urlOrPath);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
// Bcs of mixing for ESM and CommonJS - todo fix somehow
|
|
133
|
+
var index = { generateModel };
|
|
134
|
+
|
|
135
|
+
exports.default = index;
|
|
136
|
+
exports.generateModel = generateModel;
|
|
137
|
+
|
|
138
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
139
|
+
|
|
140
|
+
}));
|
package/jest.config.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** @type {import('ts-jest').JestConfigWithTsJest} */
|
|
2
|
+
module.exports = {
|
|
3
|
+
preset: 'ts-jest/presets/default-esm', // or other ESM presets
|
|
4
|
+
moduleNameMapper: {
|
|
5
|
+
'^(\\.{1,2}/.*)\\.js$': '$1',
|
|
6
|
+
},
|
|
7
|
+
transform: {
|
|
8
|
+
// '^.+\\.[tj]sx?$' to process js/ts with `ts-jest`
|
|
9
|
+
// '^.+\\.m?[tj]sx?$' to process js/ts/mjs/mts with `ts-jest`
|
|
10
|
+
'^.+\\.tsx?$': [
|
|
11
|
+
'ts-jest',
|
|
12
|
+
{
|
|
13
|
+
useESM: true,
|
|
14
|
+
},
|
|
15
|
+
],
|
|
16
|
+
},
|
|
17
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ps-aux/api-client-gen",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"main": "dist/index.js",
|
|
5
|
+
"types": "dist/index.d.ts",
|
|
6
|
+
"bin": {
|
|
7
|
+
"generate-api-client": "dist/go.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "rollup -c",
|
|
11
|
+
"tc": "tsc",
|
|
12
|
+
"eh": "foo"
|
|
13
|
+
},
|
|
14
|
+
"author": "",
|
|
15
|
+
"license": "ISC",
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"axios": "^1.5.0",
|
|
18
|
+
"cosmiconfig": "^8.3.6",
|
|
19
|
+
"swagger-typescript-api": "^13.0.3"
|
|
20
|
+
},
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"rollup": "^4.1.4",
|
|
23
|
+
"rollup-plugin-typescript2": "^0.36.0"
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import typescript from 'rollup-plugin-typescript2'
|
|
2
|
+
|
|
3
|
+
export default [{
|
|
4
|
+
input: 'src/index.ts',
|
|
5
|
+
output: [
|
|
6
|
+
{
|
|
7
|
+
file: 'dist/index.js',
|
|
8
|
+
format: 'umd',
|
|
9
|
+
name: 'MyLibrary',
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
file: 'dist/index.esm.js',
|
|
13
|
+
format: 'es',
|
|
14
|
+
},
|
|
15
|
+
],
|
|
16
|
+
plugins: [typescript()],
|
|
17
|
+
}, {
|
|
18
|
+
input: 'src/go.ts',
|
|
19
|
+
output: [
|
|
20
|
+
{
|
|
21
|
+
file: 'dist/go.js',
|
|
22
|
+
format: 'cjs',
|
|
23
|
+
}],
|
|
24
|
+
|
|
25
|
+
plugins: [typescript()],
|
|
26
|
+
}]
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import axios from 'axios'
|
|
2
|
+
import fs from 'fs'
|
|
3
|
+
|
|
4
|
+
export const downloadSpec = async (path: string, url: string) => {
|
|
5
|
+
const res = await axios({
|
|
6
|
+
url,
|
|
7
|
+
method: 'GET',
|
|
8
|
+
responseType: 'stream'
|
|
9
|
+
}).catch(err => {
|
|
10
|
+
throw err.toString()
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
const writer = fs.createWriteStream(path)
|
|
14
|
+
|
|
15
|
+
// TODO Format to readable JSON
|
|
16
|
+
await res.data.pipe(writer)
|
|
17
|
+
|
|
18
|
+
return new Promise((resolve, reject) => {
|
|
19
|
+
writer.on('finish', resolve)
|
|
20
|
+
writer.on('error', err => {
|
|
21
|
+
reject(err)
|
|
22
|
+
})
|
|
23
|
+
})
|
|
24
|
+
}
|
package/src/gen-model.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import Path from 'path'
|
|
2
|
+
import fs from 'fs'
|
|
3
|
+
import { downloadSpec } from './downloadSpec'
|
|
4
|
+
import { generateOpenApiModel } from './generateOpenApiModel'
|
|
5
|
+
|
|
6
|
+
export type Params = {
|
|
7
|
+
srcSpec: string, // path or http(s) URL
|
|
8
|
+
dstDir: string
|
|
9
|
+
apiName: string
|
|
10
|
+
env: 'browser' | 'node'
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const generateModel = async ({
|
|
14
|
+
dstDir,
|
|
15
|
+
apiName,
|
|
16
|
+
env,
|
|
17
|
+
srcSpec,
|
|
18
|
+
}: Params, log: (msg: string) => void = console.log): Promise<void> => {
|
|
19
|
+
if (!srcSpec) throw new Error(`Url or path ('srcSpec' not specified`)
|
|
20
|
+
if (!apiName) throw new Error('apiName not specified')
|
|
21
|
+
if (!dstDir) throw new Error('dstDir not specified')
|
|
22
|
+
if (!env) throw new Error('env not specified')
|
|
23
|
+
|
|
24
|
+
const dir = Path.resolve(dstDir, apiName)
|
|
25
|
+
if (!fs.existsSync(dir)) {
|
|
26
|
+
log(`Creating dir ${dir}`)
|
|
27
|
+
fs.mkdirSync(dir, {
|
|
28
|
+
recursive: true,
|
|
29
|
+
})
|
|
30
|
+
}
|
|
31
|
+
const clientDir = Path.resolve(dir, 'client')
|
|
32
|
+
|
|
33
|
+
const specPath = await getSpecPath(srcSpec, dir)
|
|
34
|
+
|
|
35
|
+
await generateOpenApiModel(
|
|
36
|
+
apiName + '-api',
|
|
37
|
+
specPath,
|
|
38
|
+
clientDir,
|
|
39
|
+
log,
|
|
40
|
+
env === 'node',
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
// TODO does not seem to currently generate all schemas
|
|
44
|
+
// generateSchemas(
|
|
45
|
+
// Path.resolve(modelDir, 'data-contracts.ts'),
|
|
46
|
+
// Path.resolve(dir, 'schemas.ts')
|
|
47
|
+
// )
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const getSpecPath = async (urlOrPath: string, dir: string): Promise<string> => {
|
|
51
|
+
if (!urlOrPath.startsWith('http')) {
|
|
52
|
+
if (!fs.existsSync(urlOrPath))
|
|
53
|
+
throw new Error(`Spec file ${urlOrPath} does not exists`)
|
|
54
|
+
return urlOrPath
|
|
55
|
+
}
|
|
56
|
+
const specPath = Path.resolve(dir, `spec.json`)
|
|
57
|
+
|
|
58
|
+
await downloadSpec(specPath, urlOrPath)
|
|
59
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { generateApi } from 'swagger-typescript-api'
|
|
2
|
+
import Path from 'path'
|
|
3
|
+
import fs from 'fs/promises'
|
|
4
|
+
|
|
5
|
+
export const generateOpenApiModel = async (
|
|
6
|
+
name: string,
|
|
7
|
+
input: string,
|
|
8
|
+
outputDir: string,
|
|
9
|
+
log: (msg: string) => void,
|
|
10
|
+
isNode = false,
|
|
11
|
+
): Promise<void> => {
|
|
12
|
+
|
|
13
|
+
log(`Will generate API client name=${name} to ${outputDir}, node=${isNode}`)
|
|
14
|
+
const specialMapping = isNode
|
|
15
|
+
? {
|
|
16
|
+
File: '{file: Buffer | stream.Readable, name: string}',
|
|
17
|
+
}
|
|
18
|
+
: {}
|
|
19
|
+
|
|
20
|
+
const dstFile = Path.join(outputDir, name + '.ts')
|
|
21
|
+
|
|
22
|
+
await generateApi({
|
|
23
|
+
name,
|
|
24
|
+
output: outputDir,
|
|
25
|
+
input: input,
|
|
26
|
+
// modular: true,
|
|
27
|
+
httpClientType: 'axios',
|
|
28
|
+
templates: Path.resolve(getThisScriptDirname(), '../templates'),
|
|
29
|
+
defaultResponseAsSuccess: true,
|
|
30
|
+
// generateRouteTypes: true,
|
|
31
|
+
singleHttpClient: true,
|
|
32
|
+
// extractRequestBody: true,
|
|
33
|
+
cleanOutput: true,
|
|
34
|
+
moduleNameFirstTag: true,
|
|
35
|
+
codeGenConstructs: (struct) => ({
|
|
36
|
+
// @ts-ignore
|
|
37
|
+
Keyword: specialMapping,
|
|
38
|
+
}),
|
|
39
|
+
// extractRequestParams: true,
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
if (isNode) {
|
|
43
|
+
let content = await fs.readFile(dstFile).then((r) => r.toString())
|
|
44
|
+
// Naive solution
|
|
45
|
+
content = 'import stream from \'node:stream\'\n' + content
|
|
46
|
+
|
|
47
|
+
await fs.writeFile(dstFile, content)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const getThisScriptDirname = (): string => {
|
|
52
|
+
// Might be problem in ESM mode
|
|
53
|
+
return __dirname
|
|
54
|
+
}
|
package/src/go.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#!/bin/env node
|
|
2
|
+
import { generateModel } from './gen-model'
|
|
3
|
+
import { cosmiconfigSync } from 'cosmiconfig'
|
|
4
|
+
|
|
5
|
+
const conf = cosmiconfigSync('api-client-gen')
|
|
6
|
+
.search()
|
|
7
|
+
|
|
8
|
+
// TODO validate config
|
|
9
|
+
|
|
10
|
+
if (!conf)
|
|
11
|
+
throw new Error(`No config provided`)
|
|
12
|
+
|
|
13
|
+
generateModel(conf.config)
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// Use this file also as a symlink for http-client.eta file
|
|
2
|
+
|
|
3
|
+
export type ContentType = {}
|
|
4
|
+
|
|
5
|
+
export type RequestParams = {
|
|
6
|
+
fileDownload?: boolean
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const ContentType = {
|
|
10
|
+
Json: 'application/json',
|
|
11
|
+
FormData: 'multipart/form-data',
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export type Request = {
|
|
15
|
+
path: string
|
|
16
|
+
method: 'GET' | 'POST' | 'PUT' | 'DELETE'
|
|
17
|
+
format?: 'json'
|
|
18
|
+
query?: any
|
|
19
|
+
body?: any
|
|
20
|
+
type?: string
|
|
21
|
+
secure?: boolean
|
|
22
|
+
fileDownload?: boolean
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export type HttpClient<Any = any> = {
|
|
26
|
+
request: <Data, A = any>(req: Request) => Promise<Data>
|
|
27
|
+
}
|