object-type properties conversion in req func
Change Full#advance and implement Full#advanced
This commit is contained in:
parent
771c34b8c8
commit
85c026ba01
13 changed files with 209 additions and 86 deletions
148
README.md
148
README.md
|
@ -57,14 +57,20 @@ module.exports = {
|
|||
```
|
||||
### 3. Run this tool
|
||||
```
|
||||
yarn run api-codegen <your-openapi-document> [-o <output-dir>] [-s <ctx.state-interface-path>]
|
||||
yarn run api-codegen <your-openapi-document> [-o <output-dir>] [-s <ctx.state-interface-path>] [-c]
|
||||
```
|
||||
|
||||
The default output directory is `api/generated`.
|
||||
For example, if you put your api document at `api.yml`, and want the generated code put in `generated/` directory, you can execute
|
||||
```
|
||||
# example
|
||||
yarn run api-codegen api.yml -o generated
|
||||
```
|
||||
|
||||
#### Flags(Optional)
|
||||
- `-o` `--output-dir <output-dir>`: output directory(default: api/generated)
|
||||
- `-c` `--client-only`: client code only(default: client & server)
|
||||
|
||||
### 4. Implement server api
|
||||
```
|
||||
import {IServerAPI} from '#api/IServerAPI';
|
||||
|
@ -154,7 +160,7 @@ where the `path`, `query`, `header`, `cookie` is a object whose key is the `name
|
|||
|
||||
```
|
||||
import api from '#api/ClientAPI';
|
||||
// import {FullDate} from '@sup39/api-ts-gen/utils';
|
||||
// import {FullDate} from '@sup39/api-ts-gen/dist/utils';
|
||||
// import {SchemaA} from '#api/schemas';
|
||||
|
||||
// ...
|
||||
|
@ -178,9 +184,9 @@ api.$baseURL = '/same/as/the/prefix/in/server';
|
|||
If the format is `string` `date`, you should use `FullDate` instead of `Date`.
|
||||
`FullDate` is a wrapper of `Date`, which implements `.toString()`, `.toJSON()` and `.valueOf()` to make it more convenience to convert it to String or JSON.
|
||||
|
||||
Import `FullDate` class from `@sup39/api-ts-gen/utils`.
|
||||
Import `FullDate` class from `@sup39/api-ts-gen/dist/utils`.
|
||||
```
|
||||
import {FullDate} from '@sup39/api-ts-gen/utils';
|
||||
import {FullDate} from '@sup39/api-ts-gen/dist/utils';
|
||||
|
||||
// initialization
|
||||
new FullDate(new Date()); // from a Date instance
|
||||
|
@ -204,13 +210,23 @@ d.month = 3; // date.setUTCMonth(3-1)
|
|||
d.day = 5; // date.setUTCDate(5)
|
||||
|
||||
// method
|
||||
/*
|
||||
.advance(period: number): FullDate
|
||||
return a new FullDate that advanced by $peroid days
|
||||
/* CAUTION: this method has been updated since version 2.0.6
|
||||
.advance(period: number): this
|
||||
return this advanced by $peroid days
|
||||
*/
|
||||
const d0 = new FullDate('2015-05-04');
|
||||
d0.advance(217) // 2015-12-07
|
||||
d0.advance(-1129) // 2012-03-31
|
||||
d0 === d0.advance(-1129) // true
|
||||
|
||||
/*
|
||||
.advanced(period: number): FullDate
|
||||
return a new FullDate advanced by $peroid days
|
||||
*/
|
||||
const d0 = new FullDate('2015-05-04');
|
||||
d0.advance(217) // 2015-12-07
|
||||
d0.advance(-1129) // 2012-03-31
|
||||
d0 === d0.advance(-1129) // false
|
||||
|
||||
/*
|
||||
.distanceFrom(d0: FullDate): number
|
||||
|
@ -511,44 +527,120 @@ Its type is `Array<keyof Schema>`.
|
|||
NamedCircle.fields // ['name', 'radius', 'color']: Array<keyof NamedCircle>
|
||||
```
|
||||
|
||||
### Nested Object
|
||||
Any object type property which contains `id` property
|
||||
will be treat specially.
|
||||
When sending request, properties other than `id` will be removed,
|
||||
for the convenience to work well with [Vapor](https://vapor.codes).
|
||||
|
||||
For example,
|
||||
```
|
||||
Group:
|
||||
type: "object"
|
||||
properties:
|
||||
id:
|
||||
type: "integer"
|
||||
format: "int32"
|
||||
name:
|
||||
type: "string"
|
||||
note:
|
||||
type: "string"
|
||||
Person:
|
||||
type: "object"
|
||||
properties:
|
||||
id:
|
||||
type: "integer"
|
||||
format: "int32"
|
||||
group:
|
||||
$ref: "#/components/schemas/Group"
|
||||
fullName:
|
||||
type: "object"
|
||||
properties:
|
||||
firstName:
|
||||
type: "string"
|
||||
lastName:
|
||||
type: "string"
|
||||
age:
|
||||
type: "integer"
|
||||
format: "int32"
|
||||
```
|
||||
will become
|
||||
```
|
||||
interface Group {
|
||||
id: number;
|
||||
name: string;
|
||||
note: string;
|
||||
}
|
||||
interface Person {
|
||||
id: number;
|
||||
group: Group;
|
||||
name: {
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
};
|
||||
value: number;
|
||||
}
|
||||
```
|
||||
|
||||
However, in http-request function in ClientAPI,
|
||||
the type of the parameter will become
|
||||
```
|
||||
interface Group {
|
||||
id: number;
|
||||
name: string;
|
||||
note: string;
|
||||
} // no change
|
||||
interface Task {
|
||||
id: number;
|
||||
group: {
|
||||
id: number;
|
||||
}; // properties other than `id` is removed
|
||||
name: {
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
}; // no change because there is no `id` property
|
||||
value: number;
|
||||
}
|
||||
```
|
||||
|
||||
## Limitations
|
||||
### application/json only
|
||||
This tool only supports `application/json` type for request and response body. Any other type like `multipart/form` or `image/*` are **not supported** and will be ignored.
|
||||
|
||||
### schema $ref only
|
||||
Other $ref like requestBody, responseBody are not supported currently.
|
||||
|
||||
## Versions
|
||||
#### 2.0.6
|
||||
- Change Full#advance and implement Full#advanced
|
||||
- Nest [object-type properties with id property] conversion in request function
|
||||
#### 2.0.5
|
||||
- implement \$ref support for responses, parameters, requestBody
|
||||
- Implement \$ref support for responses, parameters, requestBody
|
||||
#### 2.0.4
|
||||
- fix FullDate stringify in Axios params
|
||||
- use local timezone instead of UTC in FullDate
|
||||
- Fix FullDate stringify in Axios params
|
||||
- Use local timezone instead of UTC in FullDate
|
||||
#### 2.0.3
|
||||
- implement `required` property of schema
|
||||
- Implement `required` property of schema
|
||||
- client-only codegen
|
||||
#### 2.0.2
|
||||
- make number convertible to boolean
|
||||
- Make number convertible to boolean
|
||||
#### 2.0.1
|
||||
- use IState as a generic type and remove it from api-codegen.
|
||||
- Use IState as a generic type and remove it from api-codegen.
|
||||
#### 2.0.0
|
||||
- simplify generated code
|
||||
- merge all APIPromise class
|
||||
- remove IServerAPI and IClientAPI
|
||||
- remove res Object, return [status, body] in ServerAPI instead
|
||||
- remove schema classes, use interface instead
|
||||
- Simplify generated code
|
||||
- Merge all APIPromise class
|
||||
- Remove IServerAPI and IClientAPI
|
||||
- Remove res Object, return [status, body] in ServerAPI instead
|
||||
- Remove schema classes, use interface instead
|
||||
- `-s` flag for `ctx.state` interface path
|
||||
#### 1.1.3
|
||||
- expose fields of schemas to XXX.fields(static variable)
|
||||
- Expose fields of schemas to XXX.fields(static variable)
|
||||
#### 1.1.2
|
||||
- publish to npmjs and change the package name in generated code
|
||||
- specify constructor argument type of FullDate
|
||||
- Publish to npmjs and change the package name in generated code
|
||||
- Specify constructor argument type of FullDate
|
||||
#### 1.1.1
|
||||
- implement FullDate#distanceFrom(d0)
|
||||
- fix FullDate timezone bug, use UTC instead
|
||||
- Implement FullDate#distanceFrom(d0)
|
||||
- Fix FullDate timezone bug, use UTC instead
|
||||
#### 1.1.0
|
||||
- fix Partial constructor, enhance error msg
|
||||
- add int32 STP
|
||||
- Fix Partial constructor, enhance error msg
|
||||
- Add int32 STP
|
||||
#### 1.0.1
|
||||
- implement FullDate getter, setter, function(advance)
|
||||
#### 1.0.0
|
||||
|
|
|
@ -8,7 +8,7 @@ const badArgv = (x, code=1) => {
|
|||
console.error([
|
||||
'Usage: api-codegen <apiDocPath> [flags]',
|
||||
'Flags:',
|
||||
' -o --outputDir <output-dir>: output directory(default: api/generated)',
|
||||
' -o --output-dir <output-dir>: output directory(default: api/generated)',
|
||||
' -c --client-only: client code only(default: client & server)',
|
||||
].join('\n'));
|
||||
process.exit(code);
|
||||
|
@ -22,7 +22,7 @@ const errExit = (x, err, code=1) => {
|
|||
const argAttrs = ['apiDocPath'];
|
||||
const flag2attr = {
|
||||
'o': 'outputDir',
|
||||
'outputDir': 'outputDir',
|
||||
'output-dir': 'outputDir',
|
||||
};
|
||||
const flag2attr0 = { // nullary
|
||||
'c': 'clientOnly',
|
||||
|
|
9
dist/OpenAPI.d.ts
vendored
9
dist/OpenAPI.d.ts
vendored
|
@ -107,16 +107,17 @@ declare type TResTypes = {
|
|||
export declare function resolveRef<T>(obj: T | Reference, dict: Dict<T | Reference> | undefined, prefix: string): T | undefined;
|
||||
export declare class SchemaType {
|
||||
private _required;
|
||||
private _sameFile;
|
||||
private _typeName?;
|
||||
get typeName(): string;
|
||||
get required(): boolean;
|
||||
get maxSize(): string | number | undefined;
|
||||
forProp(prop: string): string;
|
||||
stp(prop: string, label: string, partial?: boolean): string;
|
||||
stp(prop: string, label: string, partial?: boolean, sameFile?: boolean): string;
|
||||
private schema;
|
||||
constructor(schema: Schema | Reference | string, _required: boolean);
|
||||
static typeNameOf(schema: Schema | Reference): string;
|
||||
static gcStp(para: string, schema: Schema | Reference, label: string, partial: boolean): string;
|
||||
constructor(schema: Schema | Reference | string, _required: boolean, _sameFile: boolean);
|
||||
static typeNameOf(schema: Schema | Reference, sameFile: boolean): string;
|
||||
static gcStp(para: string, schema: Schema | Reference, label: string, partial: boolean, sameFile: boolean): string;
|
||||
}
|
||||
export declare type APIFunctions = {
|
||||
[_: string]: APIFunction;
|
||||
|
|
32
dist/OpenAPI.js
vendored
32
dist/OpenAPI.js
vendored
|
@ -56,21 +56,22 @@ function mediaTypes2type(content, required) {
|
|||
if (Object.keys(content !== null && content !== void 0 ? content : {}).length > 0) {
|
||||
warn('only support application/json now');
|
||||
}
|
||||
return new SchemaType('any', false);
|
||||
return new SchemaType('any', false, false);
|
||||
}
|
||||
// schema
|
||||
var schema = media.schema;
|
||||
return new SchemaType(schema !== null && schema !== void 0 ? schema : 'any', required !== null && required !== void 0 ? required : false);
|
||||
return new SchemaType(schema !== null && schema !== void 0 ? schema : 'any', required !== null && required !== void 0 ? required : false, false);
|
||||
}
|
||||
var SchemaType = /** @class */ (function () {
|
||||
function SchemaType(schema, _required) {
|
||||
function SchemaType(schema, _required, _sameFile) {
|
||||
this._required = _required;
|
||||
this._sameFile = _sameFile;
|
||||
this.schema = typeof schema === 'string' ? { type: schema } : schema;
|
||||
}
|
||||
Object.defineProperty(SchemaType.prototype, "typeName", {
|
||||
get: function () {
|
||||
var _a;
|
||||
return (_a = this._typeName) !== null && _a !== void 0 ? _a : (this._typeName = SchemaType.typeNameOf(this.schema));
|
||||
return (_a = this._typeName) !== null && _a !== void 0 ? _a : (this._typeName = SchemaType.typeNameOf(this.schema, this._sameFile));
|
||||
},
|
||||
enumerable: true,
|
||||
configurable: true
|
||||
|
@ -92,12 +93,13 @@ var SchemaType = /** @class */ (function () {
|
|||
SchemaType.prototype.forProp = function (prop) {
|
||||
return "" + prop + (this.required ? '' : '?') + ": " + this.typeName;
|
||||
};
|
||||
SchemaType.prototype.stp = function (prop, label, partial) {
|
||||
SchemaType.prototype.stp = function (prop, label, partial, sameFile) {
|
||||
if (partial === void 0) { partial = false; }
|
||||
var stp = SchemaType.gcStp(prop, this.schema, label, partial);
|
||||
if (sameFile === void 0) { sameFile = false; }
|
||||
var stp = SchemaType.gcStp(prop, this.schema, label, partial, sameFile);
|
||||
return (this.required ? '' : prop + "===void 0 ? void 0 : ") + stp;
|
||||
};
|
||||
SchemaType.typeNameOf = function (schema) {
|
||||
SchemaType.typeNameOf = function (schema, sameFile) {
|
||||
var _a;
|
||||
if (isReference(schema)) {
|
||||
var $ref = schema.$ref;
|
||||
|
@ -106,18 +108,18 @@ var SchemaType = /** @class */ (function () {
|
|||
warn("Invalid $ref, use any instead: " + $ref);
|
||||
return 'any';
|
||||
}
|
||||
return "Schemas." + typeName;
|
||||
return sameFile ? typeName : "Schemas." + typeName;
|
||||
}
|
||||
var type = schema.type, format = schema.format, nullable = schema.nullable, readOnly = schema.readOnly;
|
||||
var sType = type;
|
||||
if (isArraySchema(schema)) {
|
||||
sType = "Array<" + SchemaType.typeNameOf(schema.items) + ">";
|
||||
sType = "Array<" + SchemaType.typeNameOf(schema.items, sameFile) + ">";
|
||||
}
|
||||
else if (isObjectSchema(schema)) {
|
||||
sType = '{';
|
||||
for (var _i = 0, _b = Object.entries(schema.properties); _i < _b.length; _i++) {
|
||||
var _c = _b[_i], name_2 = _c[0], sub = _c[1];
|
||||
sType += name_2 + ": " + SchemaType.typeNameOf(sub) + ", ";
|
||||
sType += name_2 + ": " + SchemaType.typeNameOf(sub, sameFile) + ", ";
|
||||
}
|
||||
sType += '}';
|
||||
}
|
||||
|
@ -141,11 +143,11 @@ var SchemaType = /** @class */ (function () {
|
|||
sType = "Readonly<" + sType + ">";
|
||||
return sType;
|
||||
};
|
||||
SchemaType.gcStp = function (para, schema, label, partial) {
|
||||
SchemaType.gcStp = function (para, schema, label, partial, sameFile) {
|
||||
// partial: Object only, 1 layer only
|
||||
// object
|
||||
if (isReference(schema)) {
|
||||
var typeName = new SchemaType(schema, true).typeName;
|
||||
var typeName = new SchemaType(schema, true, sameFile).typeName;
|
||||
return typeName + "." + (partial ? 'Partial' : 'from') + "(" + para + ")";
|
||||
}
|
||||
// any
|
||||
|
@ -154,13 +156,13 @@ var SchemaType = /** @class */ (function () {
|
|||
if (type === 'any')
|
||||
return para;
|
||||
if (isArraySchema(schema)) {
|
||||
sStp = "(v, l)=>STP._Array(v, l, elm=>" + SchemaType.gcStp('elm', schema.items, label + "[]", false) + ")";
|
||||
sStp = "(v, l)=>STP._Array(v, l, elm=>" + SchemaType.gcStp('elm', schema.items, label + "[]", false, sameFile) + ")";
|
||||
}
|
||||
else if (isObjectSchema(schema)) {
|
||||
sStp = '()=>({';
|
||||
for (var _i = 0, _a = Object.entries(schema.properties); _i < _a.length; _i++) {
|
||||
var _b = _a[_i], name_3 = _b[0], sub = _b[1];
|
||||
sStp += name_3 + ": " + SchemaType.gcStp(para + '.' + name_3, sub, label + '.' + name_3, false) + ", ";
|
||||
sStp += name_3 + ": " + SchemaType.gcStp(para + '.' + name_3, sub, label + '.' + name_3, false, sameFile) + ", ";
|
||||
}
|
||||
sStp += '})';
|
||||
}
|
||||
|
@ -241,7 +243,7 @@ function apiFunctionsOf(openAPI) {
|
|||
// add
|
||||
if (reqTypes[_in] == null)
|
||||
reqTypes[_in] = {};
|
||||
reqTypes[_in][name_5] = new SchemaType(schema !== null && schema !== void 0 ? schema : 'any', required !== null && required !== void 0 ? required : false);
|
||||
reqTypes[_in][name_5] = new SchemaType(schema !== null && schema !== void 0 ? schema : 'any', required !== null && required !== void 0 ? required : false, false);
|
||||
}
|
||||
}
|
||||
// requestBody
|
||||
|
|
14
dist/codegen.js
vendored
14
dist/codegen.js
vendored
|
@ -166,6 +166,9 @@ function codegenClientAPI(funcs, config, cp) {
|
|||
// type
|
||||
cp.writeln("type TSTP<T> = {[K in keyof T]: (data: any) =>" +
|
||||
"T[K] extends void ? any : T[K]};");
|
||||
cp.writeln("export type ExID<T> = {[K in keyof T]: " +
|
||||
"'id' extends keyof T[K] ? {id: T[K]['id']} : T[K]};");
|
||||
cp.writeln();
|
||||
// axios
|
||||
cp.writeln('const $http = axios.create({', 1);
|
||||
cp.writeln('validateStatus: () => true,');
|
||||
|
@ -212,7 +215,7 @@ function codegenClientAPI(funcs, config, cp) {
|
|||
}
|
||||
// body
|
||||
if (body != null) {
|
||||
cp.writeln("body" + (body.required ? '' : '?') + ": " + gcReq('body') + ",");
|
||||
cp.writeln("body" + (body.required ? '' : '?') + ": ExID<" + gcReq('body') + ">,");
|
||||
}
|
||||
// return value
|
||||
cp.tab(-1);
|
||||
|
@ -267,7 +270,7 @@ function codegenSchemas(schemas, config, cp) {
|
|||
var requireds = new Set((_a = schema.required) !== null && _a !== void 0 ? _a : []);
|
||||
for (var _d = 0, _e = Object.entries(schema.properties); _d < _e.length; _d++) {
|
||||
var _f = _e[_d], propName = _f[0], prop = _f[1];
|
||||
var propType = new OpenAPI_1.SchemaType(prop, requireds.has(propName));
|
||||
var propType = new OpenAPI_1.SchemaType(prop, requireds.has(propName), true);
|
||||
propTypes.push([propName, propType]);
|
||||
cp.writeln(propType.forProp(propName) + ';');
|
||||
}
|
||||
|
@ -278,7 +281,7 @@ function codegenSchemas(schemas, config, cp) {
|
|||
cp.writeln("from: (o: {[_: string]: any}): " + typeName + " => ({", 1);
|
||||
for (var _g = 0, propTypes_1 = propTypes; _g < propTypes_1.length; _g++) {
|
||||
var _h = propTypes_1[_g], n = _h[0], t = _h[1];
|
||||
cp.writeln(n + ": " + t.stp("o." + n, typeName + '.' + n) + ",");
|
||||
cp.writeln(n + ": " + t.stp("o." + n, typeName + '.' + n, false, true) + ",");
|
||||
}
|
||||
cp.writeln('}),', -1);
|
||||
// Partial
|
||||
|
@ -287,7 +290,7 @@ function codegenSchemas(schemas, config, cp) {
|
|||
var locPartial = "Partial<" + typeName + ">";
|
||||
for (var _j = 0, propTypes_2 = propTypes; _j < propTypes_2.length; _j++) {
|
||||
var _k = propTypes_2[_j], n = _k[0], t = _k[1];
|
||||
cp.writeln("if (o." + n + " !== void 0) r." + n + " = " + t.stp("o." + n, locPartial + '.' + n) + ";");
|
||||
cp.writeln("if (o." + n + " !== void 0) r." + n + " = " + t.stp("o." + n, locPartial + '.' + n, false, true) + ";");
|
||||
}
|
||||
cp.writeln('return r;');
|
||||
cp.writeln('},', -1);
|
||||
|
@ -299,7 +302,8 @@ function codegenSchemas(schemas, config, cp) {
|
|||
cp.writeln('}', -1);
|
||||
}
|
||||
else {
|
||||
cp.writeln("export type " + typeName + " = " + OpenAPI_1.SchemaType.typeNameOf(schema));
|
||||
cp.writeln("export type " + typeName + " = " +
|
||||
OpenAPI_1.SchemaType.typeNameOf(schema, true));
|
||||
}
|
||||
}
|
||||
// return
|
||||
|
|
3
dist/utils/FullDate.d.ts
vendored
3
dist/utils/FullDate.d.ts
vendored
|
@ -17,6 +17,7 @@ export declare class FullDate {
|
|||
set year(val: number);
|
||||
set month(val: number);
|
||||
set day(val: number);
|
||||
advance(period: number): FullDate;
|
||||
advance(period: number): this;
|
||||
advanced(period: number): FullDate;
|
||||
distanceFrom(d0: FullDate): number;
|
||||
}
|
||||
|
|
4
dist/utils/FullDate.js
vendored
4
dist/utils/FullDate.js
vendored
|
@ -88,6 +88,10 @@ var FullDate = /** @class */ (function () {
|
|||
});
|
||||
// func
|
||||
FullDate.prototype.advance = function (period) {
|
||||
this._date = new Date(this._date.valueOf() + period * 86400e3);
|
||||
return this;
|
||||
};
|
||||
FullDate.prototype.advanced = function (period) {
|
||||
return new FullDate(this._date.valueOf() + period * 86400e3);
|
||||
};
|
||||
FullDate.prototype.distanceFrom = function (d0) {
|
||||
|
|
8
dist/utils/qStringify.js
vendored
8
dist/utils/qStringify.js
vendored
|
@ -5,8 +5,12 @@ var options0 = {
|
|||
filter: function (prefix, value) {
|
||||
var _a;
|
||||
var con = value === null || value === void 0 ? void 0 : value.constructor;
|
||||
// use Class.toString() if defined explicitly (exception: Object, Date)
|
||||
return (con && con !== Object && con !== Date && ((_a = con.prototype) === null || _a === void 0 ? void 0 : _a.hasOwnProperty('toString'))) ? value.toString() : value;
|
||||
if (con === null || con === Array || con === Object)
|
||||
return value;
|
||||
if (con === Date)
|
||||
return value.toJSON();
|
||||
// use Class.toString() if defined explicitly
|
||||
return ((_a = con.prototype) === null || _a === void 0 ? void 0 : _a.hasOwnProperty('toString')) ? value.toString() : value;
|
||||
},
|
||||
};
|
||||
function qStringify(obj, options) {
|
||||
|
|
|
@ -148,17 +148,17 @@ function mediaTypes2type(
|
|||
if (Object.keys(content ?? {}).length > 0) {
|
||||
warn('only support application/json now');
|
||||
}
|
||||
return new SchemaType('any', false);
|
||||
return new SchemaType('any', false, false);
|
||||
}
|
||||
// schema
|
||||
const {schema} = media;
|
||||
return new SchemaType(schema ?? 'any', required ?? false);
|
||||
return new SchemaType(schema ?? 'any', required ?? false, false);
|
||||
}
|
||||
export class SchemaType {
|
||||
private _typeName?: string;
|
||||
get typeName(): string {
|
||||
return this._typeName ??
|
||||
(this._typeName = SchemaType.typeNameOf(this.schema));
|
||||
(this._typeName = SchemaType.typeNameOf(this.schema, this._sameFile));
|
||||
}
|
||||
get required(): boolean {
|
||||
return this._required;
|
||||
|
@ -169,18 +169,24 @@ export class SchemaType {
|
|||
forProp(prop: string): string {
|
||||
return `${prop}${this.required ? '' : '?'}: ${this.typeName}`;
|
||||
}
|
||||
stp(prop: string, label: string, partial: boolean=false): string {
|
||||
const stp = SchemaType.gcStp(prop, this.schema, label, partial);
|
||||
stp(
|
||||
prop: string, label: string,
|
||||
partial: boolean=false, sameFile: boolean=false,
|
||||
): string {
|
||||
const stp = SchemaType.gcStp(prop, this.schema, label, partial, sameFile);
|
||||
return (this.required ? '' : `${prop}===void 0 ? void 0 : `)+stp;
|
||||
}
|
||||
|
||||
private schema: Schema | Reference;
|
||||
constructor(schema: Schema | Reference | string,
|
||||
private _required: boolean) {
|
||||
constructor(
|
||||
schema: Schema | Reference | string,
|
||||
private _required: boolean,
|
||||
private _sameFile: boolean,
|
||||
) {
|
||||
this.schema = typeof schema === 'string' ? {type: schema} : schema;
|
||||
}
|
||||
|
||||
static typeNameOf(schema: Schema | Reference): string {
|
||||
static typeNameOf(schema: Schema | Reference, sameFile: boolean): string {
|
||||
if (isReference(schema)) {
|
||||
const {$ref} = schema;
|
||||
const typeName = /^#\/components\/schemas\/(\w+)$/g.exec($ref)?.[1];
|
||||
|
@ -188,18 +194,18 @@ export class SchemaType {
|
|||
warn(`Invalid $ref, use any instead: ${$ref}`);
|
||||
return 'any';
|
||||
}
|
||||
return `Schemas.${typeName}`;
|
||||
return sameFile ? typeName : `Schemas.${typeName}`;
|
||||
}
|
||||
const {
|
||||
type, format, nullable, readOnly,
|
||||
} = schema;
|
||||
let sType = type;
|
||||
if (isArraySchema(schema)) {
|
||||
sType = `Array<${SchemaType.typeNameOf(schema.items)}>`;
|
||||
sType = `Array<${SchemaType.typeNameOf(schema.items, sameFile)}>`;
|
||||
} else if (isObjectSchema(schema)) {
|
||||
sType = '{';
|
||||
for (const [name, sub] of Object.entries(schema.properties)) {
|
||||
sType += `${name}: ${SchemaType.typeNameOf(sub)}, `;
|
||||
sType += `${name}: ${SchemaType.typeNameOf(sub, sameFile)}, `;
|
||||
}
|
||||
sType += '}';
|
||||
} else if (type === 'string') {
|
||||
|
@ -214,11 +220,11 @@ export class SchemaType {
|
|||
return sType;
|
||||
}
|
||||
static gcStp(para: string, schema: Schema | Reference,
|
||||
label: string, partial: boolean): string {
|
||||
label: string, partial: boolean, sameFile: boolean): string {
|
||||
// partial: Object only, 1 layer only
|
||||
// object
|
||||
if (isReference(schema)) {
|
||||
const typeName = new SchemaType(schema, true).typeName;
|
||||
const typeName = new SchemaType(schema, true, sameFile).typeName;
|
||||
return `${typeName}.${partial ? 'Partial': 'from'}(${para})`;
|
||||
}
|
||||
// any
|
||||
|
@ -226,13 +232,13 @@ export class SchemaType {
|
|||
let sStp;
|
||||
if (type === 'any') return para;
|
||||
if (isArraySchema(schema)) {
|
||||
sStp = `(v, l)=>STP._Array(v, l, elm=>${
|
||||
SchemaType.gcStp('elm', schema.items, `${label}[]`, false)})`;
|
||||
sStp = `(v, l)=>STP._Array(v, l, elm=>${SchemaType.gcStp(
|
||||
'elm', schema.items, `${label}[]`, false, sameFile)})`;
|
||||
} else if (isObjectSchema(schema)) {
|
||||
sStp = '()=>({';
|
||||
for (const [name, sub] of Object.entries(schema.properties)) {
|
||||
sStp += `${name}: ${
|
||||
SchemaType.gcStp(para+'.'+name, sub, label+'.'+name, false)}, `;
|
||||
sStp += `${name}: ${SchemaType.gcStp(
|
||||
para+'.'+name, sub, label+'.'+name, false, sameFile)}, `;
|
||||
}
|
||||
sStp += '})';
|
||||
} else {
|
||||
|
@ -304,7 +310,7 @@ export function apiFunctionsOf(openAPI: OpenAPI): APIFunctions {
|
|||
// add
|
||||
if (reqTypes[_in] == null) reqTypes[_in] = {};
|
||||
reqTypes[_in]![name] = new SchemaType(
|
||||
schema ?? 'any', required ?? false);
|
||||
schema ?? 'any', required ?? false, false);
|
||||
}
|
||||
}
|
||||
// requestBody
|
||||
|
|
|
@ -166,6 +166,9 @@ function codegenClientAPI(funcs: APIFuncs, config: Config, cp: CodePrinter) {
|
|||
// type
|
||||
cp.writeln(`type TSTP<T> = {[K in keyof T]: (data: any) =>`+
|
||||
`T[K] extends void ? any : T[K]};`);
|
||||
cp.writeln(`export type ExID<T> = {[K in keyof T]: ` +
|
||||
`'id' extends keyof T[K] ? {id: T[K]['id']} : T[K]};`);
|
||||
cp.writeln();
|
||||
// axios
|
||||
cp.writeln('const $http = axios.create({', 1);
|
||||
cp.writeln('validateStatus: () => true,');
|
||||
|
@ -190,7 +193,7 @@ function codegenClientAPI(funcs: APIFuncs, config: Config, cp: CodePrinter) {
|
|||
cp.writeln('},', -1);
|
||||
// functions
|
||||
for (const [funcName, func] of Object.entries(funcs)) {
|
||||
const gcReq = (_in: string) => `TAPI['${funcName}']['req']['${_in}']`;
|
||||
const gcReq = (_in: string) =>`TAPI['${funcName}']['req']['${_in}']`;
|
||||
const {method, url, reqTypes, resTypes} = func;
|
||||
const {
|
||||
query, header, path, body,
|
||||
|
@ -211,7 +214,7 @@ function codegenClientAPI(funcs: APIFuncs, config: Config, cp: CodePrinter) {
|
|||
}
|
||||
// body
|
||||
if (body != null) {
|
||||
cp.writeln(`body${body.required ? '' : '?'}: ${gcReq('body')},`);
|
||||
cp.writeln(`body${body.required ? '' : '?'}: ExID<${gcReq('body')}>,`);
|
||||
}
|
||||
// return value
|
||||
cp.tab(-1);
|
||||
|
@ -258,7 +261,7 @@ function codegenSchemas(
|
|||
const propTypes: [string, SchemaType][] = [];
|
||||
const requireds = new Set(schema.required ?? []);
|
||||
for (const [propName, prop] of Object.entries(schema.properties)) {
|
||||
const propType = new SchemaType(prop, requireds.has(propName));
|
||||
const propType = new SchemaType(prop, requireds.has(propName), true);
|
||||
propTypes.push([propName, propType]);
|
||||
cp.writeln(propType.forProp(propName)+';');
|
||||
}
|
||||
|
@ -268,7 +271,7 @@ function codegenSchemas(
|
|||
// .from
|
||||
cp.writeln(`from: (o: {[_: string]: any}): ${typeName} => ({`, 1);
|
||||
for (const [n, t] of propTypes) {
|
||||
cp.writeln(`${n}: ${t.stp(`o.${n}`, typeName+'.'+n)},`);
|
||||
cp.writeln(`${n}: ${t.stp(`o.${n}`, typeName+'.'+n, false, true)},`);
|
||||
}
|
||||
cp.writeln('}),', -1);
|
||||
// Partial
|
||||
|
@ -278,7 +281,7 @@ function codegenSchemas(
|
|||
const locPartial = `Partial<${typeName}>`;
|
||||
for (const [n, t] of propTypes) {
|
||||
cp.writeln(`if (o.${n} !== void 0) r.${n} = ${
|
||||
t.stp(`o.${n}`, locPartial+'.'+n)};`);
|
||||
t.stp(`o.${n}`, locPartial+'.'+n, false, true)};`);
|
||||
}
|
||||
cp.writeln('return r;');
|
||||
cp.writeln('},', -1);
|
||||
|
@ -289,7 +292,8 @@ function codegenSchemas(
|
|||
// end of const
|
||||
cp.writeln('}', -1);
|
||||
} else {
|
||||
cp.writeln(`export type ${typeName} = ${SchemaType.typeNameOf(schema)}`);
|
||||
cp.writeln(`export type ${typeName} = ` +
|
||||
SchemaType.typeNameOf(schema, true));
|
||||
}
|
||||
}
|
||||
// return
|
||||
|
|
|
@ -65,7 +65,11 @@ export class FullDate {
|
|||
this._date.setDate(val);
|
||||
}
|
||||
// func
|
||||
advance(period: number): FullDate {
|
||||
advance(period: number): this {
|
||||
this._date = new Date(this._date.valueOf()+period*86400e3);
|
||||
return this;
|
||||
}
|
||||
advanced(period: number): FullDate {
|
||||
return new FullDate(this._date.valueOf()+period*86400e3);
|
||||
}
|
||||
distanceFrom(d0: FullDate): number {
|
||||
|
|
|
@ -3,9 +3,10 @@ import * as Qs0 from 'qs';
|
|||
const options0 = {
|
||||
filter(prefix: string, value: any) {
|
||||
const con = value?.constructor;
|
||||
// use Class.toString() if defined explicitly (exception: Object, Date)
|
||||
return (con && con !== Object && con !== Date &&
|
||||
con.prototype?.hasOwnProperty('toString')) ? value.toString() : value;
|
||||
if (con === null || con === Array || con === Object) return value;
|
||||
if (con === Date) return value.toJSON();
|
||||
// use Class.toString() if defined explicitly
|
||||
return con.prototype?.hasOwnProperty('toString') ? value.toString() : value;
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@sup39/api-ts-gen",
|
||||
"version": "2.0.5",
|
||||
"version": "2.0.6",
|
||||
"description": "OpenAPI code generator for TypeScript",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
|
|
Reference in a new issue