beer-swagger 1.0.3 → 1.1.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/generate-apis.js +51 -48
- package/package.json +1 -1
package/generate-apis.js
CHANGED
|
@@ -5,7 +5,6 @@ import path from 'path';
|
|
|
5
5
|
import { createInterface } from 'readline';
|
|
6
6
|
|
|
7
7
|
const ENV_PATH = path.join(process.cwd(), '.env');
|
|
8
|
-
const CLIENT_TYPES = new Set(['WEB', 'WeChatMiniProgram']);
|
|
9
8
|
const SKIPPED_SERVICES = new Set([
|
|
10
9
|
'billbear-common-web-gateway',
|
|
11
10
|
'billbear-common-data-panel'
|
|
@@ -116,8 +115,8 @@ function appendParamSuffix(baseName, paramSuffix) {
|
|
|
116
115
|
return `${baseName}${paramSuffix}`;
|
|
117
116
|
}
|
|
118
117
|
|
|
119
|
-
// Add a verb
|
|
120
|
-
function addVerbForSingleSegment(name, method) {
|
|
118
|
+
// Add a verb when the HTTP method should be reflected in the name.
|
|
119
|
+
function addVerbForSingleSegment(name, method, hasPathParams = false) {
|
|
121
120
|
const lower = name.toLowerCase();
|
|
122
121
|
if (KEEP_METHOD_NAMES.has(lower)) {
|
|
123
122
|
return name;
|
|
@@ -126,6 +125,9 @@ function addVerbForSingleSegment(name, method) {
|
|
|
126
125
|
const suffix = name[0].toUpperCase() + name.slice(1);
|
|
127
126
|
return `removeBy${suffix}`;
|
|
128
127
|
}
|
|
128
|
+
if ((method === 'post' || method === 'put') && !hasPathParams) {
|
|
129
|
+
return name;
|
|
130
|
+
}
|
|
129
131
|
const verbMap = {
|
|
130
132
|
get: 'get',
|
|
131
133
|
post: 'save',
|
|
@@ -238,9 +240,9 @@ function mapSchemaToType(schema, schemas, useTypes) {
|
|
|
238
240
|
return `${itemType}[]`;
|
|
239
241
|
}
|
|
240
242
|
if (type === 'object') {
|
|
241
|
-
return '
|
|
243
|
+
return 'object';
|
|
242
244
|
}
|
|
243
|
-
return '
|
|
245
|
+
return 'object';
|
|
244
246
|
}
|
|
245
247
|
|
|
246
248
|
// Map parameter schema to a type for method signatures.
|
|
@@ -281,9 +283,9 @@ function mapParamSchemaToType(schema, schemas, useTypes) {
|
|
|
281
283
|
return '[]';
|
|
282
284
|
}
|
|
283
285
|
if (type === 'object') {
|
|
284
|
-
return '
|
|
286
|
+
return 'object';
|
|
285
287
|
}
|
|
286
|
-
return '
|
|
288
|
+
return 'object';
|
|
287
289
|
}
|
|
288
290
|
|
|
289
291
|
// Resolve default response type from Swagger responses.
|
|
@@ -400,7 +402,7 @@ function toPascal(value) {
|
|
|
400
402
|
}
|
|
401
403
|
|
|
402
404
|
// Generate API file content for a service.
|
|
403
|
-
function generateForSpec(spec, serviceName, fileBase,
|
|
405
|
+
function generateForSpec(spec, serviceName, fileBase, isDefaultFetch) {
|
|
404
406
|
const {
|
|
405
407
|
components = {},
|
|
406
408
|
tags: specTags = [],
|
|
@@ -453,7 +455,8 @@ function generateForSpec(spec, serviceName, fileBase, clientType) {
|
|
|
453
455
|
const pathSegments = p.split('/')
|
|
454
456
|
.filter(Boolean)
|
|
455
457
|
.filter((part) => !part.startsWith('{'));
|
|
456
|
-
|
|
458
|
+
const shouldAddVerb = pathSegments.length === 1 || ((method === 'post' || method === 'put') && pathParams.length > 0);
|
|
459
|
+
name = shouldAddVerb ? addVerbForSingleSegment(inferred, method, pathParams.length > 0) : inferred;
|
|
457
460
|
}
|
|
458
461
|
if (!KEEP_METHOD_NAMES.has(name.toLowerCase())) {
|
|
459
462
|
name = fixVerbCase(name);
|
|
@@ -615,7 +618,7 @@ function generateForSpec(spec, serviceName, fileBase, clientType) {
|
|
|
615
618
|
|
|
616
619
|
if (entryMethod === 'get') {
|
|
617
620
|
methods.push(` public async ${name}<T = ${defaultType}>(${sigParts}): Promise<JsonResponse<T>> {`);
|
|
618
|
-
methods.push(` return this.get<T>(${pathLiteral}, ${paramsObject});`);
|
|
621
|
+
methods.push(` return await this.get<T>(${pathLiteral}, ${paramsObject});`);
|
|
619
622
|
methods.push(' }');
|
|
620
623
|
methods.push('');
|
|
621
624
|
continue;
|
|
@@ -624,12 +627,12 @@ function generateForSpec(spec, serviceName, fileBase, clientType) {
|
|
|
624
627
|
if (entryMethod === 'delete') {
|
|
625
628
|
if (entryHasBody) {
|
|
626
629
|
methods.push(` public async ${name}<T = ${defaultType}>(${sigParts}): Promise<JsonResponse<T>> {`);
|
|
627
|
-
methods.push(` return this.deleteBody<T>(${pathLiteral}, ${paramsObject}, body);`);
|
|
630
|
+
methods.push(` return await this.deleteBody<T>(${pathLiteral}, ${paramsObject}, body);`);
|
|
628
631
|
methods.push(' }');
|
|
629
632
|
methods.push('');
|
|
630
633
|
} else {
|
|
631
634
|
methods.push(` public async ${name}<T = ${defaultType}>(${sigParts}): Promise<JsonResponse<T>> {`);
|
|
632
|
-
methods.push(` return this.delete<T>(${pathLiteral}, ${paramsObject});`);
|
|
635
|
+
methods.push(` return await this.delete<T>(${pathLiteral}, ${paramsObject});`);
|
|
633
636
|
methods.push(' }');
|
|
634
637
|
methods.push('');
|
|
635
638
|
}
|
|
@@ -639,12 +642,12 @@ function generateForSpec(spec, serviceName, fileBase, clientType) {
|
|
|
639
642
|
if (entryMethod === 'post') {
|
|
640
643
|
if (entryHasBody) {
|
|
641
644
|
methods.push(` public async ${name}<T = ${defaultType}>(${sigParts}): Promise<JsonResponse<T>> {`);
|
|
642
|
-
methods.push(` return this.postBody<T>(${pathLiteral}, ${paramsObject}, body);`);
|
|
645
|
+
methods.push(` return await this.postBody<T>(${pathLiteral}, ${paramsObject}, body);`);
|
|
643
646
|
methods.push(' }');
|
|
644
647
|
methods.push('');
|
|
645
648
|
} else {
|
|
646
649
|
methods.push(` public async ${name}<T = ${defaultType}>(${sigParts}): Promise<JsonResponse<T>> {`);
|
|
647
|
-
methods.push(` return this.post<T>(${pathLiteral}, ${paramsObject});`);
|
|
650
|
+
methods.push(` return await this.post<T>(${pathLiteral}, ${paramsObject});`);
|
|
648
651
|
methods.push(' }');
|
|
649
652
|
methods.push('');
|
|
650
653
|
}
|
|
@@ -654,12 +657,12 @@ function generateForSpec(spec, serviceName, fileBase, clientType) {
|
|
|
654
657
|
if (entryMethod === 'put') {
|
|
655
658
|
if (entryHasBody) {
|
|
656
659
|
methods.push(` public async ${name}<T = ${defaultType}>(${sigParts}): Promise<JsonResponse<T>> {`);
|
|
657
|
-
methods.push(` return this.putBody<T>(${pathLiteral}, ${paramsObject}, body);`);
|
|
660
|
+
methods.push(` return await this.putBody<T>(${pathLiteral}, ${paramsObject}, body);`);
|
|
658
661
|
methods.push(' }');
|
|
659
662
|
methods.push('');
|
|
660
663
|
} else {
|
|
661
664
|
methods.push(` public async ${name}<T = ${defaultType}>(${sigParts}): Promise<JsonResponse<T>> {`);
|
|
662
|
-
methods.push(` return this.put<T>(${pathLiteral}, ${paramsObject});`);
|
|
665
|
+
methods.push(` return await this.put<T>(${pathLiteral}, ${paramsObject});`);
|
|
663
666
|
methods.push(' }');
|
|
664
667
|
methods.push('');
|
|
665
668
|
}
|
|
@@ -667,7 +670,7 @@ function generateForSpec(spec, serviceName, fileBase, clientType) {
|
|
|
667
670
|
}
|
|
668
671
|
|
|
669
672
|
methods.push(` public async ${name}<T = ${defaultType}>(${sigParts}): Promise<JsonResponse<T>> {`);
|
|
670
|
-
methods.push(` return this.postBody<T>(${pathLiteral}, ${paramsObject}, body);`);
|
|
673
|
+
methods.push(` return await this.postBody<T>(${pathLiteral}, ${paramsObject}, body);`);
|
|
671
674
|
methods.push(' }');
|
|
672
675
|
methods.push('');
|
|
673
676
|
}
|
|
@@ -698,8 +701,9 @@ function generateForSpec(spec, serviceName, fileBase, clientType) {
|
|
|
698
701
|
lines.push(' * This file is auto-generated. Do not edit manually.');
|
|
699
702
|
lines.push(' * To update, run the generate-apis.js script.');
|
|
700
703
|
lines.push(' */');
|
|
701
|
-
const fetchImport =
|
|
702
|
-
lines.push(`import
|
|
704
|
+
const fetchImport = isDefaultFetch ? 'beer-network/api' : './fetch';
|
|
705
|
+
lines.push(`import type { JsonResponse } from '${fetchImport}';`);
|
|
706
|
+
lines.push(`import Fetch from '${fetchImport}';`);
|
|
703
707
|
if (fileBase) {
|
|
704
708
|
lines.push(`import type * as Types from './types/${fileBase}-type';`);
|
|
705
709
|
}
|
|
@@ -709,7 +713,7 @@ function generateForSpec(spec, serviceName, fileBase, clientType) {
|
|
|
709
713
|
lines.push(' public readonly pathPrefix: string;');
|
|
710
714
|
lines.push(...apiProps);
|
|
711
715
|
lines.push(' constructor(pathPrefix = \'\') {');
|
|
712
|
-
lines.push(` this.pathPrefix =
|
|
716
|
+
lines.push(` this.pathPrefix = \`\${pathPrefix ?? ''}${defaultPathPrefix}\`;`);
|
|
713
717
|
lines.push(...apiInit);
|
|
714
718
|
lines.push(' }');
|
|
715
719
|
lines.push('}');
|
|
@@ -719,7 +723,7 @@ function generateForSpec(spec, serviceName, fileBase, clientType) {
|
|
|
719
723
|
lines.push(` if (!${cacheName}.has(pathPrefix)) {`);
|
|
720
724
|
lines.push(` ${cacheName}.set(pathPrefix, new ${apiClassName}(pathPrefix));`);
|
|
721
725
|
lines.push(' }');
|
|
722
|
-
lines.push(` return ${cacheName}.get(pathPrefix)
|
|
726
|
+
lines.push(` return ${cacheName}.get(pathPrefix) ?? (() => { throw new Error('API instance not found'); })();`);
|
|
723
727
|
lines.push('}');
|
|
724
728
|
lines.push('');
|
|
725
729
|
|
|
@@ -913,22 +917,23 @@ function writeEnvFile(nextEnv) {
|
|
|
913
917
|
fs.writeFileSync(ENV_PATH, `${lines.join('\n')}\n`, 'utf8');
|
|
914
918
|
}
|
|
915
919
|
|
|
916
|
-
function
|
|
917
|
-
if (
|
|
918
|
-
return
|
|
920
|
+
function normalizeBoolean(value) {
|
|
921
|
+
if (value === undefined || value === null || value === '') {
|
|
922
|
+
return undefined;
|
|
919
923
|
}
|
|
920
|
-
if (
|
|
924
|
+
if (typeof value === 'boolean') {
|
|
921
925
|
return value;
|
|
922
926
|
}
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
}
|
|
926
|
-
const normalized = value.replace(/[-_\s]/g, '')
|
|
927
|
+
const normalized = String(value)
|
|
928
|
+
.trim()
|
|
927
929
|
.toLowerCase();
|
|
928
|
-
if (
|
|
929
|
-
return
|
|
930
|
+
if (['true', '1', 'yes', 'y'].includes(normalized)) {
|
|
931
|
+
return true;
|
|
930
932
|
}
|
|
931
|
-
|
|
933
|
+
if (['false', '0', 'no', 'n'].includes(normalized)) {
|
|
934
|
+
return false;
|
|
935
|
+
}
|
|
936
|
+
return undefined;
|
|
932
937
|
}
|
|
933
938
|
|
|
934
939
|
// Ask user for missing values via CLI.
|
|
@@ -948,13 +953,12 @@ async function promptForMissing(env) {
|
|
|
948
953
|
if (!result.SWAGGER_OUT_DIRECTORY) {
|
|
949
954
|
result.SWAGGER_OUT_DIRECTORY = (await ask('DIRECTORY: ')).trim();
|
|
950
955
|
}
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
}
|
|
956
|
-
result.CLIENT_TYPE = selected;
|
|
956
|
+
let isDefaultFetch = normalizeBoolean(result.IS_DEFAULT_FETCH);
|
|
957
|
+
while (isDefaultFetch === undefined) {
|
|
958
|
+
const selected = (await ask('IS_DEFAULT_FETCH (true/false): ')).trim();
|
|
959
|
+
isDefaultFetch = normalizeBoolean(selected);
|
|
957
960
|
}
|
|
961
|
+
result.IS_DEFAULT_FETCH = String(isDefaultFetch);
|
|
958
962
|
rl.close();
|
|
959
963
|
return result;
|
|
960
964
|
}
|
|
@@ -965,23 +969,23 @@ async function main() {
|
|
|
965
969
|
let {
|
|
966
970
|
SWAGGER_BASE_URL: baseUrl,
|
|
967
971
|
SWAGGER_OUT_DIRECTORY: directory,
|
|
968
|
-
|
|
972
|
+
IS_DEFAULT_FETCH: isDefaultFetch
|
|
969
973
|
} = { ...process.env, ...envFile };
|
|
970
|
-
|
|
974
|
+
isDefaultFetch = normalizeBoolean(isDefaultFetch);
|
|
971
975
|
|
|
972
|
-
if (!baseUrl || !directory ||
|
|
976
|
+
if (!baseUrl || !directory || isDefaultFetch === undefined) {
|
|
973
977
|
const filled = await promptForMissing({
|
|
974
978
|
SWAGGER_BASE_URL: baseUrl || '',
|
|
975
979
|
SWAGGER_OUT_DIRECTORY: directory || '',
|
|
976
|
-
|
|
980
|
+
IS_DEFAULT_FETCH: isDefaultFetch === undefined ? '' : String(isDefaultFetch)
|
|
977
981
|
});
|
|
978
982
|
baseUrl = filled.SWAGGER_BASE_URL;
|
|
979
983
|
directory = filled.SWAGGER_OUT_DIRECTORY;
|
|
980
|
-
|
|
984
|
+
isDefaultFetch = normalizeBoolean(filled.IS_DEFAULT_FETCH);
|
|
981
985
|
writeEnvFile({
|
|
982
986
|
SWAGGER_BASE_URL: baseUrl,
|
|
983
987
|
SWAGGER_OUT_DIRECTORY: directory,
|
|
984
|
-
|
|
988
|
+
IS_DEFAULT_FETCH: String(isDefaultFetch)
|
|
985
989
|
});
|
|
986
990
|
}
|
|
987
991
|
|
|
@@ -991,10 +995,9 @@ async function main() {
|
|
|
991
995
|
if (!directory) {
|
|
992
996
|
throw new Error('SWAGGER_OUT_DIRECTORY is required.');
|
|
993
997
|
}
|
|
994
|
-
if (
|
|
995
|
-
throw new Error('
|
|
998
|
+
if (isDefaultFetch === undefined) {
|
|
999
|
+
throw new Error('IS_DEFAULT_FETCH is required.');
|
|
996
1000
|
}
|
|
997
|
-
|
|
998
1001
|
const configUrl = `${baseUrl}/v3/api-docs/swagger-config`;
|
|
999
1002
|
const outputDirectory = path.isAbsolute(directory) ? directory : path.join(process.cwd(), directory);
|
|
1000
1003
|
const typesDirectory = path.join(outputDirectory, 'types');
|
|
@@ -1032,7 +1035,7 @@ async function main() {
|
|
|
1032
1035
|
const specUrl = `${baseUrl}${url}`;
|
|
1033
1036
|
const spec = await fetchJson(specUrl);
|
|
1034
1037
|
const fileBase = toFileBase(serviceName);
|
|
1035
|
-
const content = generateForSpec(spec, serviceName, fileBase,
|
|
1038
|
+
const content = generateForSpec(spec, serviceName, fileBase, isDefaultFetch);
|
|
1036
1039
|
const typesContent = generateTypes(spec);
|
|
1037
1040
|
|
|
1038
1041
|
const outPath = path.join(outputDirectory, `${fileBase}.ts`);
|