swagger-typescript-api 11.1.1 → 11.1.3
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/CHANGELOG.md +1121 -0
- package/cli/index.js +0 -0
- package/index.d.ts +1 -0
- package/index.js +2 -0
- package/package.json +10 -3
- package/src/index.js +0 -0
- package/src/schema-parser/schema-parser.js +4 -4
- package/src/schema-parser/schema-routes.js +69 -18
- package/src/translators/JavaScript.js +60 -49
- package/templates/base/http-clients/axios-http-client.ejs +5 -0
- package/templates/base/http-clients/fetch-http-client.ejs +2 -0
- package/templates/default/procedure-call.ejs +1 -0
- package/templates/modular/procedure-call.ejs +1 -0
package/cli/index.js
CHANGED
|
File without changes
|
package/index.d.ts
CHANGED
package/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "swagger-typescript-api",
|
|
3
|
-
"version": "11.1.
|
|
3
|
+
"version": "11.1.3",
|
|
4
4
|
"description": "Generate typescript/javascript api from swagger schema",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"cli:json": "node index.js -r -d -p ./swagger-test-cli.json -n swagger-test-cli.ts",
|
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
"test:--enum-names-as-values": "node tests/spec/enumNamesAsValues/test.js",
|
|
39
39
|
"test:--default-response": "node tests/spec/defaultResponse/test.js",
|
|
40
40
|
"test:--js": "node tests/spec/js/test.js",
|
|
41
|
+
"test:jsSingleHttpClientModular": "node tests/spec/jsSingleHttpClientModular/test.js",
|
|
41
42
|
"test:--js--axios": "node tests/spec/jsAxios/test.js",
|
|
42
43
|
"test:--axios": "node tests/spec/axios/test.js",
|
|
43
44
|
"test:--another-array-type": "node tests/spec/another-array-type/test.js",
|
|
@@ -46,11 +47,16 @@
|
|
|
46
47
|
"test:--type-suffix--type-prefix": "node tests/spec/typeSuffixPrefix/test.js",
|
|
47
48
|
"test:--dot-path-params": "node tests/spec/dot-path-params/test.js",
|
|
48
49
|
"test:--primitive-type-constructs": "node tests/spec/primitive-type-constructs/test.js",
|
|
49
|
-
"test:--cli": "node index.js -p tests/spec/cli/schema.json -o tests/spec/cli -n schema.ts --extract-response-body --extract-response-error --api-class-name MySuperApi --type-prefix Prefix",
|
|
50
|
+
"test:--cli": "node index.js -p tests/spec/cli/schema.json -o tests/spec/cli -n schema.ts --extract-response-body --extract-response-error --api-class-name MySuperApi --type-prefix Prefix && node tests/spec/cli/test.js",
|
|
50
51
|
"test:partialBaseTemplate": "node tests/spec/partialBaseTemplate/test.js",
|
|
51
52
|
"test:partialDefaultTemplate": "node tests/spec/partialDefaultTemplate/test.js",
|
|
52
53
|
"test:--patch": "node tests/spec/patch/test.js",
|
|
53
|
-
"test:deprecated": "node tests/spec/deprecated/test.js"
|
|
54
|
+
"test:deprecated": "node tests/spec/deprecated/test.js",
|
|
55
|
+
"test:nullableRefTest3.0": "node tests/spec/nullable-3.0/test.js",
|
|
56
|
+
"test:nullableRefTest2.0": "node tests/spec/nullable-2.0/test.js",
|
|
57
|
+
"test:additionalProperties2.0": "node tests/spec/additional-properties-2.0/test.js",
|
|
58
|
+
"test:enums2.0": "node tests/spec/enums-2.0/test.js",
|
|
59
|
+
"test:another-query-params": "node tests/spec/another-query-params/test.js"
|
|
54
60
|
},
|
|
55
61
|
"author": "acacode",
|
|
56
62
|
"license": "MIT",
|
|
@@ -61,6 +67,7 @@
|
|
|
61
67
|
"@types/node": "^18.11.7",
|
|
62
68
|
"@types/prettier": "^2.7.1",
|
|
63
69
|
"all-contributors-cli": "^6.20.0",
|
|
70
|
+
"axios": "^1.1.3",
|
|
64
71
|
"cross-env": "^7.0.3",
|
|
65
72
|
"git-diff": "^2.0.6",
|
|
66
73
|
"husky": "^4.3.6",
|
package/src/index.js
CHANGED
|
File without changes
|
|
@@ -149,7 +149,7 @@ class SchemaParser {
|
|
|
149
149
|
});
|
|
150
150
|
},
|
|
151
151
|
[SCHEMA_TYPES.OBJECT]: (schema, typeName) => {
|
|
152
|
-
const
|
|
152
|
+
const contentProperties = this.getObjectSchemaContent(schema);
|
|
153
153
|
|
|
154
154
|
return this.attachParsedRef(schema, {
|
|
155
155
|
...(_.isObject(schema) ? schema : {}),
|
|
@@ -159,8 +159,8 @@ class SchemaParser {
|
|
|
159
159
|
typeIdentifier: this.config.Ts.Keyword.Interface,
|
|
160
160
|
name: typeName,
|
|
161
161
|
description: this.schemaFormatters.formatDescription(schema.description),
|
|
162
|
-
allFieldsAreOptional: !_.some(_.values(
|
|
163
|
-
content:
|
|
162
|
+
allFieldsAreOptional: !_.some(_.values(contentProperties), (part) => part.isRequired),
|
|
163
|
+
content: contentProperties,
|
|
164
164
|
});
|
|
165
165
|
},
|
|
166
166
|
[SCHEMA_TYPES.COMPLEX]: (schema, typeName) => {
|
|
@@ -424,7 +424,7 @@ class SchemaParser {
|
|
|
424
424
|
};
|
|
425
425
|
});
|
|
426
426
|
|
|
427
|
-
if (additionalProperties
|
|
427
|
+
if (additionalProperties) {
|
|
428
428
|
propertiesContent.push({
|
|
429
429
|
$$raw: { additionalProperties },
|
|
430
430
|
description: "",
|
|
@@ -16,6 +16,7 @@ const CONTENT_KIND = {
|
|
|
16
16
|
FORM_DATA: "FORM_DATA",
|
|
17
17
|
IMAGE: "IMAGE",
|
|
18
18
|
OTHER: "OTHER",
|
|
19
|
+
TEXT: "TEXT",
|
|
19
20
|
};
|
|
20
21
|
|
|
21
22
|
class SchemaRoutes {
|
|
@@ -103,25 +104,24 @@ class SchemaRoutes {
|
|
|
103
104
|
this.logger.warn("wrong path param name", paramName);
|
|
104
105
|
}
|
|
105
106
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
107
|
+
pathParams.push({
|
|
108
|
+
$match: match,
|
|
109
|
+
name: _.camelCase(paramName),
|
|
110
|
+
required: true,
|
|
111
|
+
type: "string",
|
|
112
|
+
description: "",
|
|
113
|
+
schema: {
|
|
112
114
|
type: "string",
|
|
113
|
-
description: "",
|
|
114
|
-
schema: {
|
|
115
|
-
type: "string",
|
|
116
|
-
},
|
|
117
|
-
in: "path",
|
|
118
115
|
},
|
|
119
|
-
|
|
116
|
+
in: "path",
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
return pathParams;
|
|
120
120
|
},
|
|
121
121
|
[],
|
|
122
122
|
);
|
|
123
123
|
|
|
124
|
-
|
|
124
|
+
let fixedRoute = _.reduce(
|
|
125
125
|
pathParams,
|
|
126
126
|
(fixedRoute, pathParam) => {
|
|
127
127
|
return _.replace(fixedRoute, pathParam.$match, `\${${pathParam.name}}`);
|
|
@@ -129,14 +129,47 @@ class SchemaRoutes {
|
|
|
129
129
|
routeName || "",
|
|
130
130
|
);
|
|
131
131
|
|
|
132
|
+
const queryParamMatches = fixedRoute.match(/(\{\?.*\})/g);
|
|
133
|
+
const queryParams = [];
|
|
134
|
+
|
|
135
|
+
if (queryParamMatches && queryParamMatches.length) {
|
|
136
|
+
queryParamMatches.forEach((match) => {
|
|
137
|
+
fixedRoute = fixedRoute.replace(match, "");
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
_.uniq(
|
|
141
|
+
queryParamMatches
|
|
142
|
+
.join(",")
|
|
143
|
+
.replace(/(\{\?)|(\})|\s/g, "")
|
|
144
|
+
.split(","),
|
|
145
|
+
).forEach((paramName) => {
|
|
146
|
+
if (_.includes(paramName, "-")) {
|
|
147
|
+
this.logger.warn("wrong query param name", paramName);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
queryParams.push({
|
|
151
|
+
$match: paramName,
|
|
152
|
+
name: _.camelCase(paramName),
|
|
153
|
+
required: true,
|
|
154
|
+
type: "string",
|
|
155
|
+
description: "",
|
|
156
|
+
schema: {
|
|
157
|
+
type: "string",
|
|
158
|
+
},
|
|
159
|
+
in: "query",
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
|
|
132
164
|
return {
|
|
133
165
|
originalRoute: routeName || "",
|
|
134
166
|
route: fixedRoute,
|
|
135
167
|
pathParams,
|
|
168
|
+
queryParams,
|
|
136
169
|
};
|
|
137
170
|
};
|
|
138
171
|
|
|
139
|
-
getRouteParams = (routeInfo,
|
|
172
|
+
getRouteParams = (routeInfo, pathParamsFromRouteName, queryParamsFromRouteName) => {
|
|
140
173
|
const { parameters } = routeInfo;
|
|
141
174
|
|
|
142
175
|
const routeParams = {
|
|
@@ -186,13 +219,21 @@ class SchemaRoutes {
|
|
|
186
219
|
});
|
|
187
220
|
|
|
188
221
|
// used in case when path parameters is not declared in requestInfo.parameters ("in": "path")
|
|
189
|
-
_.each(
|
|
222
|
+
_.each(pathParamsFromRouteName, (pathParam) => {
|
|
190
223
|
const alreadyExist = _.some(routeParams.path, (parameter) => parameter.name === pathParam.name);
|
|
191
224
|
|
|
192
225
|
if (!alreadyExist) {
|
|
193
226
|
routeParams.path.push(pathParam);
|
|
194
227
|
}
|
|
195
228
|
});
|
|
229
|
+
// used in case when path parameters is not declared in requestInfo.parameters ("in": "path")
|
|
230
|
+
_.each(queryParamsFromRouteName, (queryParam) => {
|
|
231
|
+
const alreadyExist = _.some(routeParams.query, (parameter) => parameter.name === queryParam.name);
|
|
232
|
+
|
|
233
|
+
if (!alreadyExist) {
|
|
234
|
+
routeParams.query.push(queryParam);
|
|
235
|
+
}
|
|
236
|
+
});
|
|
196
237
|
|
|
197
238
|
return routeParams;
|
|
198
239
|
};
|
|
@@ -207,7 +248,7 @@ class SchemaRoutes {
|
|
|
207
248
|
|
|
208
249
|
getContentKind = (contentTypes) => {
|
|
209
250
|
if (
|
|
210
|
-
_.
|
|
251
|
+
_.some(contentTypes, (contentType) => _.startsWith(contentType, "application/json")) ||
|
|
211
252
|
_.some(contentTypes, (contentType) => _.endsWith(contentType, "+json"))
|
|
212
253
|
) {
|
|
213
254
|
return CONTENT_KIND.JSON;
|
|
@@ -225,6 +266,12 @@ class SchemaRoutes {
|
|
|
225
266
|
return CONTENT_KIND.IMAGE;
|
|
226
267
|
}
|
|
227
268
|
|
|
269
|
+
if (
|
|
270
|
+
_.some(contentTypes, (contentType) => _.startsWith(contentType, "text/"))
|
|
271
|
+
) {
|
|
272
|
+
return CONTENT_KIND.TEXT;
|
|
273
|
+
}
|
|
274
|
+
|
|
228
275
|
return CONTENT_KIND.OTHER;
|
|
229
276
|
};
|
|
230
277
|
|
|
@@ -642,7 +689,11 @@ class SchemaRoutes {
|
|
|
642
689
|
consumes,
|
|
643
690
|
...otherInfo
|
|
644
691
|
} = routeInfo;
|
|
645
|
-
const {
|
|
692
|
+
const {
|
|
693
|
+
route,
|
|
694
|
+
pathParams: pathParamsFromRouteName,
|
|
695
|
+
queryParams: queryParamsFromRouteName,
|
|
696
|
+
} = this.parseRouteName(rawRouteName);
|
|
646
697
|
|
|
647
698
|
const routeId = generateId();
|
|
648
699
|
const firstTag = tags && tags.length > 0 ? tags[0] : null;
|
|
@@ -655,7 +706,7 @@ class SchemaRoutes {
|
|
|
655
706
|
hasSecurity = security.length > 0;
|
|
656
707
|
}
|
|
657
708
|
|
|
658
|
-
const routeParams = this.getRouteParams(routeInfo,
|
|
709
|
+
const routeParams = this.getRouteParams(routeInfo, pathParamsFromRouteName, queryParamsFromRouteName);
|
|
659
710
|
|
|
660
711
|
const pathArgs = routeParams.path.map((pathArgSchema) => ({
|
|
661
712
|
name: pathArgSchema.name,
|
|
@@ -1,49 +1,60 @@
|
|
|
1
|
-
const ts = require("typescript");
|
|
2
|
-
|
|
3
|
-
function translate(fileName, content, options) {
|
|
4
|
-
const output = {};
|
|
5
|
-
const host = ts.createCompilerHost(options, true);
|
|
6
|
-
const fileNames = [fileName];
|
|
7
|
-
const originalSourceFileGet = host.getSourceFile.bind(host);
|
|
8
|
-
host.getSourceFile = (sourceFileName, languageVersion, onError, shouldCreateNewSourceFile) => {
|
|
9
|
-
if (sourceFileName !== fileName)
|
|
10
|
-
return originalSourceFileGet(sourceFileName, languageVersion, onError, shouldCreateNewSourceFile);
|
|
11
|
-
|
|
12
|
-
return ts.createSourceFile(sourceFileName, content, languageVersion, true, ts.ScriptKind.External);
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
host.writeFile = (fileName, contents) => {
|
|
16
|
-
output[fileName] = contents;
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
ts.createProgram(fileNames, options, host).emit();
|
|
20
|
-
|
|
21
|
-
return output;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
module.exports = {
|
|
25
|
-
translate: (fileName, sourceTypeScript) => {
|
|
26
|
-
const translated = translate(fileName, sourceTypeScript, {
|
|
27
|
-
module: "ESNext",
|
|
28
|
-
noImplicitReturns: true,
|
|
29
|
-
alwaysStrict: true,
|
|
30
|
-
target: ts.ScriptTarget.ESNext,
|
|
31
|
-
declaration: true,
|
|
32
|
-
noImplicitAny: false,
|
|
33
|
-
sourceMap: false,
|
|
34
|
-
removeComments: false,
|
|
35
|
-
disableSizeLimit: true,
|
|
36
|
-
esModuleInterop: true,
|
|
37
|
-
emitDecoratorMetadata: true,
|
|
38
|
-
skipLibCheck: true,
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
const sourceFileName = fileName.replace(ts.Extension.Ts, ts.Extension.Js);
|
|
42
|
-
const declarationFileName = fileName.replace(ts.Extension.Ts, ts.Extension.Dts);
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
1
|
+
const ts = require("typescript");
|
|
2
|
+
|
|
3
|
+
function translate(fileName, content, options) {
|
|
4
|
+
const output = {};
|
|
5
|
+
const host = ts.createCompilerHost(options, true);
|
|
6
|
+
const fileNames = [fileName];
|
|
7
|
+
const originalSourceFileGet = host.getSourceFile.bind(host);
|
|
8
|
+
host.getSourceFile = (sourceFileName, languageVersion, onError, shouldCreateNewSourceFile) => {
|
|
9
|
+
if (sourceFileName !== fileName)
|
|
10
|
+
return originalSourceFileGet(sourceFileName, languageVersion, onError, shouldCreateNewSourceFile);
|
|
11
|
+
|
|
12
|
+
return ts.createSourceFile(sourceFileName, content, languageVersion, true, ts.ScriptKind.External);
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
host.writeFile = (fileName, contents) => {
|
|
16
|
+
output[fileName] = contents;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
ts.createProgram(fileNames, options, host).emit();
|
|
20
|
+
|
|
21
|
+
return output;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
module.exports = {
|
|
25
|
+
translate: (fileName, sourceTypeScript) => {
|
|
26
|
+
const translated = translate(fileName, sourceTypeScript, {
|
|
27
|
+
module: "ESNext",
|
|
28
|
+
noImplicitReturns: true,
|
|
29
|
+
alwaysStrict: true,
|
|
30
|
+
target: ts.ScriptTarget.ESNext,
|
|
31
|
+
declaration: true,
|
|
32
|
+
noImplicitAny: false,
|
|
33
|
+
sourceMap: false,
|
|
34
|
+
removeComments: false,
|
|
35
|
+
disableSizeLimit: true,
|
|
36
|
+
esModuleInterop: true,
|
|
37
|
+
emitDecoratorMetadata: true,
|
|
38
|
+
skipLibCheck: true,
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
const sourceFileName = fileName.replace(ts.Extension.Ts, ts.Extension.Js);
|
|
42
|
+
const declarationFileName = fileName.replace(ts.Extension.Ts, ts.Extension.Dts);
|
|
43
|
+
const sourceContent = translated[sourceFileName];
|
|
44
|
+
const tsImportRows = sourceTypeScript.split("\n").filter((line) => line.startsWith("import "));
|
|
45
|
+
const declarationContent = translated[declarationFileName]
|
|
46
|
+
.split("\n")
|
|
47
|
+
.map((line) => {
|
|
48
|
+
if (line.startsWith("import ")) {
|
|
49
|
+
return tsImportRows.shift();
|
|
50
|
+
}
|
|
51
|
+
return line;
|
|
52
|
+
})
|
|
53
|
+
.join("\n");
|
|
54
|
+
|
|
55
|
+
return {
|
|
56
|
+
sourceContent: sourceContent,
|
|
57
|
+
declarationContent: declarationContent,
|
|
58
|
+
};
|
|
59
|
+
},
|
|
60
|
+
};
|
|
@@ -33,6 +33,7 @@ export enum ContentType {
|
|
|
33
33
|
Json = "application/json",
|
|
34
34
|
FormData = "multipart/form-data",
|
|
35
35
|
UrlEncoded = "application/x-www-form-urlencoded",
|
|
36
|
+
Text = "text/plain",
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
export class HttpClient<SecurityDataType = unknown> {
|
|
@@ -114,6 +115,10 @@ export class HttpClient<SecurityDataType = unknown> {
|
|
|
114
115
|
body = this.createFormData(body as Record<string, unknown>);
|
|
115
116
|
}
|
|
116
117
|
|
|
118
|
+
if (type === ContentType.Text && body && body !== null && typeof body !== "string") {
|
|
119
|
+
body = JSON.stringify(body);
|
|
120
|
+
}
|
|
121
|
+
|
|
117
122
|
return this.instance.request({
|
|
118
123
|
...requestParams,
|
|
119
124
|
headers: {
|
|
@@ -45,6 +45,7 @@ export enum ContentType {
|
|
|
45
45
|
Json = "application/json",
|
|
46
46
|
FormData = "multipart/form-data",
|
|
47
47
|
UrlEncoded = "application/x-www-form-urlencoded",
|
|
48
|
+
Text = "text/plain",
|
|
48
49
|
}
|
|
49
50
|
|
|
50
51
|
export class HttpClient<SecurityDataType = unknown> {
|
|
@@ -102,6 +103,7 @@ export class HttpClient<SecurityDataType = unknown> {
|
|
|
102
103
|
|
|
103
104
|
private contentFormatters: Record<ContentType, (input: any) => any> = {
|
|
104
105
|
[ContentType.Json]: (input:any) => input !== null && (typeof input === "object" || typeof input === "string") ? JSON.stringify(input) : input,
|
|
106
|
+
[ContentType.Text]: (input:any) => input !== null && typeof input !== "string" ? JSON.stringify(input) : input,
|
|
105
107
|
[ContentType.FormData]: (input: any) =>
|
|
106
108
|
Object.keys(input || {}).reduce((formData, key) => {
|
|
107
109
|
const property = input[key];
|