From 85c026ba0116fc2e05a5c48c8e2aa92cb2ccf6dd Mon Sep 17 00:00:00 2001 From: sup39 Date: Mon, 17 Aug 2020 13:12:34 +0800 Subject: [PATCH] object-type properties conversion in req func Change Full#advance and implement Full#advanced --- README.md | 148 +++++++++++++++++++++++++++++++-------- bin/api-codegen.js | 4 +- dist/OpenAPI.d.ts | 9 +-- dist/OpenAPI.js | 32 +++++---- dist/codegen.js | 14 ++-- dist/utils/FullDate.d.ts | 3 +- dist/utils/FullDate.js | 4 ++ dist/utils/qStringify.js | 8 ++- lib/OpenAPI.ts | 42 ++++++----- lib/codegen.ts | 16 +++-- lib/utils/FullDate.ts | 6 +- lib/utils/qStringify.ts | 7 +- package.json | 2 +- 13 files changed, 209 insertions(+), 86 deletions(-) diff --git a/README.md b/README.md index 9b51041..32b003a 100644 --- a/README.md +++ b/README.md @@ -57,14 +57,20 @@ module.exports = { ``` ### 3. Run this tool ``` -yarn run api-codegen [-o ] [-s ] +yarn run api-codegen [-o ] [-s ] [-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 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`. NamedCircle.fields // ['name', 'radius', 'color']: Array ``` +### 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 diff --git a/bin/api-codegen.js b/bin/api-codegen.js index b8a39dc..94103c1 100755 --- a/bin/api-codegen.js +++ b/bin/api-codegen.js @@ -8,7 +8,7 @@ const badArgv = (x, code=1) => { console.error([ 'Usage: api-codegen [flags]', 'Flags:', - ' -o --outputDir : output directory(default: api/generated)', + ' -o --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', diff --git a/dist/OpenAPI.d.ts b/dist/OpenAPI.d.ts index f1c7b1a..1f26eac 100644 --- a/dist/OpenAPI.d.ts +++ b/dist/OpenAPI.d.ts @@ -107,16 +107,17 @@ declare type TResTypes = { export declare function resolveRef(obj: T | Reference, dict: Dict | 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; diff --git a/dist/OpenAPI.js b/dist/OpenAPI.js index fab7f50..0d23c0f 100644 --- a/dist/OpenAPI.js +++ b/dist/OpenAPI.js @@ -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 diff --git a/dist/codegen.js b/dist/codegen.js index 5427f71..6b3e4e5 100644 --- a/dist/codegen.js +++ b/dist/codegen.js @@ -166,6 +166,9 @@ function codegenClientAPI(funcs, config, cp) { // type cp.writeln("type TSTP = {[K in keyof T]: (data: any) =>" + "T[K] extends void ? any : T[K]};"); + cp.writeln("export type ExID = {[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 diff --git a/dist/utils/FullDate.d.ts b/dist/utils/FullDate.d.ts index afc4b2c..cfafd9c 100644 --- a/dist/utils/FullDate.d.ts +++ b/dist/utils/FullDate.d.ts @@ -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; } diff --git a/dist/utils/FullDate.js b/dist/utils/FullDate.js index 795089b..457ec27 100644 --- a/dist/utils/FullDate.js +++ b/dist/utils/FullDate.js @@ -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) { diff --git a/dist/utils/qStringify.js b/dist/utils/qStringify.js index bd4a4f8..9c39cc1 100644 --- a/dist/utils/qStringify.js +++ b/dist/utils/qStringify.js @@ -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) { diff --git a/lib/OpenAPI.ts b/lib/OpenAPI.ts index 9e5fff2..26e9508 100644 --- a/lib/OpenAPI.ts +++ b/lib/OpenAPI.ts @@ -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 diff --git a/lib/codegen.ts b/lib/codegen.ts index 5dfbc7e..b9ab274 100644 --- a/lib/codegen.ts +++ b/lib/codegen.ts @@ -166,6 +166,9 @@ function codegenClientAPI(funcs: APIFuncs, config: Config, cp: CodePrinter) { // type cp.writeln(`type TSTP = {[K in keyof T]: (data: any) =>`+ `T[K] extends void ? any : T[K]};`); + cp.writeln(`export type ExID = {[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 diff --git a/lib/utils/FullDate.ts b/lib/utils/FullDate.ts index 9f75a63..1537852 100644 --- a/lib/utils/FullDate.ts +++ b/lib/utils/FullDate.ts @@ -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 { diff --git a/lib/utils/qStringify.ts b/lib/utils/qStringify.ts index aa5fb31..a4bcd89 100644 --- a/lib/utils/qStringify.ts +++ b/lib/utils/qStringify.ts @@ -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; }, }; diff --git a/package.json b/package.json index 3af2012..f618ef9 100644 --- a/package.json +++ b/package.json @@ -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",