diff --git a/dist/OpenAPI.d.ts b/dist/OpenAPI.d.ts index 20b0e0b..5475932 100644 --- a/dist/OpenAPI.d.ts +++ b/dist/OpenAPI.d.ts @@ -114,11 +114,11 @@ export declare class SchemaType { get required(): boolean; get maxSize(): string | number | undefined; forProp(prop: string): string; - stp(prop: string): string; + stp(prop: string, label: string, partial?: boolean): string; private schema; constructor(schema: Schema | Reference | string, _required: boolean); static typeNameOf(schema: Schema | Reference): string; - static gcStp(para: string, schema: Schema | Reference): string; + static gcStp(para: string, schema: Schema | Reference, label: string, partial: boolean): string; } export declare type APIFunctions = { [_: string]: APIFunction; diff --git a/dist/OpenAPI.js b/dist/OpenAPI.js index c4362f6..fd21066 100644 --- a/dist/OpenAPI.js +++ b/dist/OpenAPI.js @@ -70,8 +70,9 @@ var SchemaType = /** @class */ (function () { SchemaType.prototype.forProp = function (prop) { return "" + prop + (this.required ? '' : '?') + ": " + this.typeName; }; - SchemaType.prototype.stp = function (prop) { - var stp = SchemaType.gcStp(prop, this.schema); + SchemaType.prototype.stp = function (prop, label, partial) { + if (partial === void 0) { partial = false; } + var stp = SchemaType.gcStp(prop, this.schema, label, partial); return (this.required ? '' : prop + "===undefined ? undefined : ") + stp; }; SchemaType.typeNameOf = function (schema) { @@ -118,27 +119,30 @@ var SchemaType = /** @class */ (function () { sType = "Readonly<" + sType + ">"; return sType; }; - SchemaType.gcStp = function (para, schema) { - var sPara = "'" + para.replace(/'/g, '\\\'') + "'"; + SchemaType.gcStp = function (para, schema, label, partial) { + // partial: Object only, 1 layer only // object if (isReference(schema)) { - return "new " + new SchemaType(schema, true).typeName + "(" + para + ")"; + var typeName = new SchemaType(schema, true).typeName; + return partial ? + typeName + ".Partial(" + para + ")" : + "new " + typeName + "(" + para + ")"; } // any - var code; var type = schema.type, nullable = schema.nullable, format = schema.format; + var sStp; if (type === 'any') return para; if (isArraySchema(schema)) { - code = "STP._Array(" + para + ", " + sPara + ").map(o=>" + SchemaType.gcStp('o', schema.items) + ")"; + sStp = "(v, l)=>STP._Array(v, l, elm=>" + SchemaType.gcStp('elm', schema.items, label + "[]", false) + ")"; } else if (isObjectSchema(schema)) { - code = '{'; + sStp = '()=>({'; for (var _i = 0, _a = Object.entries(schema.properties); _i < _a.length; _i++) { var _b = _a[_i], name_2 = _b[0], sub = _b[1]; - code += name_2 + ": " + SchemaType.gcStp(para + '.' + name_2, sub) + ", "; + sStp += name_2 + ": " + SchemaType.gcStp(para + '.' + name_2, sub, label + '.' + name_2, false) + ", "; } - code += '}'; + sStp += '})'; } else { var t = void 0; @@ -148,30 +152,37 @@ var SchemaType = /** @class */ (function () { else if (format === 'date') t = 'FullDate'; else if (format === 'byte') - t = 'string'; // TODO + t = 'byte'; else if (format === 'binary') - t = 'string'; // TODO + t = 'binary'; else { - if (format) - warn("Unknown format " + format + ", use string instead"); + if (format) { + warn("Unknown string format " + format + ", use string instead"); + } t = 'string'; } } - else if (type === 'integer') - t = 'number'; + else if (type === 'integer') { + if (format === 'int32') + t = 'int32'; + else { + warn("Unsupport integer format " + format + ", use number instead"); + t = 'number'; // TODO int64 + } + } else t = type; if (!StrictTypeParser_1.StrictTypeParser.supportTypes.includes(t)) { - warn("Unknown type " + type + ", use any instead"); + warn("Unsupport type " + type + " " + format + ", use any instead"); return para; } - else - code = "STP._" + t + "(" + para + ", " + sPara + ")"; + sStp = "STP._" + t; } // nullable - if (nullable) - code = para + "===null ? null : " + code; - return code; + var funcName = nullable ? 'nullableParse' : 'parse'; + // result + var sLabel = "'" + label.replace(/'/g, '\\\'') + "'"; // escape + return "STP." + funcName + "(" + sStp + ", " + para + ", " + sLabel + ")"; }; return SchemaType; }()); diff --git a/dist/codegen.js b/dist/codegen.js index 00a2064..f002c3a 100644 --- a/dist/codegen.js +++ b/dist/codegen.js @@ -102,7 +102,7 @@ function codegenIHandler(funcs, config, cp) { // TODO void -> string or any var isValid = validateStatus(status_2); cp.writeln("case " + status_2 + ": return this." + (isValid ? 'onSuccess' : 'onFail') + "(this.handlers[" + status_2 + "],", 1); - cp.writeln(schema.stp('data') + ");"); + cp.writeln(schema.stp('data', 'res.body') + ");"); cp.tab(-1); if (isValid) validTypes.add(schema.typeName); @@ -164,6 +164,7 @@ function codegenRouter(funcs, config, cp) { for (var _i = 0, _a = Object.entries(funcs); _i < _a.length; _i++) { var _b = _a[_i], funcName = _b[0], func = _b[1]; var method = func.method, url = func.url, reqTypes = func.reqTypes, resTypes = func.resTypes; + var isPartial = method === 'patch'; var statuses = Object.keys(resTypes); // TODO escape var sURL = url.replace(/{(.*?)}/g, ':$1'); // {a} -> :a @@ -174,7 +175,7 @@ function codegenRouter(funcs, config, cp) { mid = "bodyParser(" + config_1 + "), "; } cp.writeln("router." + method + "('" + sURL + "', " + mid + "async ctx => {", 1); - // TODO permission check, etc + // req if (Object.keys(reqTypes).length === 0) { cp.writeln('const req = {};'); } @@ -192,16 +193,15 @@ function codegenRouter(funcs, config, cp) { for (var _d = 0, _e = Object.entries(paras); _d < _e.length; _d++) { var _f = _e[_d], name_1 = _f[0], schema = _f[1]; var pn = "ctxGetParas." + _in + "(ctx, '" + name_1 + "')"; - cp.writeln(name_1 + ": " + schema.stp(pn) + ","); + var label = "req." + _in; + cp.writeln(name_1 + ": " + schema.stp(pn, label) + ","); } cp.writeln('},', -1); } // body var body = reqTypes.body; if (body != null) { - var name_2 = 'body'; - var pn = 'reqBody'; - cp.writeln(name_2 + ": " + body.stp(pn)); + cp.writeln("body: " + body.stp('reqBody', 'req.body', isPartial)); } cp.writeln('}} catch(err) {', -1); cp.tab(1); @@ -320,13 +320,13 @@ function codegenSchemas(schemas, config, cp) { cp.writeln(); // schema for (var _i = 0, _a = Object.entries(schemas); _i < _a.length; _i++) { - var _b = _a[_i], name_3 = _b[0], schema = _b[1]; + var _b = _a[_i], typeName = _b[0], schema = _b[1]; if (OpenAPI_1.isObjectSchema(schema)) { - cp.writeln("export class " + name_3 + " {", 1); + cp.writeln("export class " + typeName + " {", 1); var propTypes = []; for (var _c = 0, _d = Object.entries(schema.properties); _c < _d.length; _c++) { var _e = _d[_c], propName = _e[0], prop = _e[1]; - var propType = new OpenAPI_1.SchemaType(prop, true); // TODO required? + var propType = new OpenAPI_1.SchemaType(prop, true); // TODO required propTypes.push([propName, propType]); cp.writeln(propType.forProp(propName) + ';'); } @@ -334,13 +334,24 @@ function codegenSchemas(schemas, config, cp) { cp.writeln('constructor(o: {[_: string]: any}){', 1); for (var _f = 0, propTypes_1 = propTypes; _f < propTypes_1.length; _f++) { var _g = propTypes_1[_f], n = _g[0], t = _g[1]; - cp.writeln("this." + n + " = " + t.stp("o." + n) + ";"); + cp.writeln("this." + n + " = " + t.stp("o." + n, typeName + '.' + n) + ";"); } cp.writeln('}', -1); + // Partial + cp.writeln("static Partial(o: {[_: string]: any}): Partial<" + typeName + "> {", 1); + cp.writeln("const r: Partial<" + typeName + "> = {};"); + var locPartial = "Partial<" + typeName + ">"; + for (var _h = 0, propTypes_2 = propTypes; _h < propTypes_2.length; _h++) { + var _j = propTypes_2[_h], n = _j[0], t = _j[1]; + cp.writeln("if (o." + n + " !== undefined) r." + n + " = " + t.stp("o." + n, locPartial + '.' + n) + ";"); + } + cp.writeln('return r;'); + cp.writeln('}', -1); + // end of class cp.writeln('}', -1); } else { - cp.writeln("export type " + name_3 + " = " + OpenAPI_1.SchemaType.typeNameOf(schema)); + cp.writeln("export type " + typeName + " = " + OpenAPI_1.SchemaType.typeNameOf(schema)); } } // return diff --git a/dist/utils/StrictTypeParser.d.ts b/dist/utils/StrictTypeParser.d.ts index 6ed13c6..d8fd655 100644 --- a/dist/utils/StrictTypeParser.d.ts +++ b/dist/utils/StrictTypeParser.d.ts @@ -1,18 +1,25 @@ import { FullDate } from './FullDate'; export declare module StrictTypeParser { class BadValueError extends Error { - attr: string; + label: string; + constructor(label: string, message: string); + } + class BadTypeError extends BadValueError { + label: string; type: string; value: any; - constructor(attr: string, type: string, value: any); + constructor(label: string, type: string, value: any); } - function _number(x: any, attr: string): number; - function _string(x: any, attr: string): string; - function _boolean(x: any, attr: string): boolean; - function _Date(x: any, attr: string): Date; - function _FullDate(x: any, attr: string): FullDate; - function _byte(x: any, attr: string): string; - function _binary(x: any, attr: string): string; - function _Array(x: any, attr: string): Array; + function _int32(x: any, label: string): number; + function _number(x: any, label: string): number; + function _string(x: any, label: string): string; + function _boolean(x: any, label: string): boolean; + function _Date(x: any, label: string): Date; + function _FullDate(x: any, label: string): FullDate; + function _byte(x: any, label: string): string; + function _binary(x: any, label: string): string; + function _Array(x: any, label: string, mapper: (x: any) => T): Array; + function parse(stp: (val: any, label: string) => T, val: any, label: string): T; + function nullableParse(stp: (val: any, label: string) => T, val: any, label: string): T | null; const supportTypes: string[]; } diff --git a/dist/utils/StrictTypeParser.js b/dist/utils/StrictTypeParser.js index e6f7afb..db6eb39 100644 --- a/dist/utils/StrictTypeParser.js +++ b/dist/utils/StrictTypeParser.js @@ -18,87 +18,129 @@ var StrictTypeParser; (function (StrictTypeParser) { var BadValueError = /** @class */ (function (_super) { __extends(BadValueError, _super); - function BadValueError(attr, type, value) { - var _this = _super.call(this, attr + ": Can not convert `" + (['object', 'array'].includes(typeof value) ? - JSON.stringify(value) : "" + value) + "` to type " + type) || this; - _this.attr = attr; - _this.type = type; - _this.value = value; + function BadValueError(label, message) { + var _this = _super.call(this, message) || this; + _this.label = label; console.error(_this.message); - Object.setPrototypeOf(_this, BadValueError.prototype); + Object.setPrototypeOf(_this, BadTypeError.prototype); return _this; } return BadValueError; }(Error)); StrictTypeParser.BadValueError = BadValueError; - function _number(x, attr) { + var BadTypeError = /** @class */ (function (_super) { + __extends(BadTypeError, _super); + function BadTypeError(label, type, value) { + var _this = _super.call(this, label, label + ": Can not convert `" + (['object', 'array'].includes(typeof value) ? + JSON.stringify(value) : "" + value) + "` to type " + type) || this; + _this.label = label; + _this.type = type; + _this.value = value; + return _this; + } + return BadTypeError; + }(BadValueError)); + StrictTypeParser.BadTypeError = BadTypeError; + function _int32(x, label) { + if (typeof x === 'number' && x === (x | 0)) + return x; + if (typeof x === 'string') { // convert from url + var r = +x | 0; + if (x === r.toString()) + return r; + } + throw new BadTypeError(label, 'int32', x); + } + StrictTypeParser._int32 = _int32; + function _number(x, label) { if (typeof x === 'number') return x; - if (typeof x === 'string') { + if (typeof x === 'string') { // convert from url var r = +x; if (!isNaN(r)) return r; } - throw new BadValueError(attr, 'number', x); + throw new BadTypeError(label, 'number', x); } StrictTypeParser._number = _number; - function _string(x, attr) { + function _string(x, label) { if (typeof x === 'string') return x; if (typeof x === 'object') return x.toString(); - throw new BadValueError(attr, 'string', x); + throw new BadTypeError(label, 'string', x); } StrictTypeParser._string = _string; - function _boolean(x, attr) { + function _boolean(x, label) { if (typeof x === 'boolean') return x; if (x === 'true') return true; if (x === 'false') return false; - throw new BadValueError(attr, 'boolean', x); + throw new BadTypeError(label, 'boolean', x); } StrictTypeParser._boolean = _boolean; - function _Date(x, attr) { + function _Date(x, label) { var r = new Date(x); - if (!isNaN(+r)) + if (x != null && !isNaN(+r)) return r; - throw new BadValueError(attr, 'Date', x); + throw new BadTypeError(label, 'Date', x); } StrictTypeParser._Date = _Date; - function _FullDate(x, attr) { + function _FullDate(x, label) { var r = new FullDate_1.FullDate(x); - if (!isNaN(+r)) + if (x != null && !isNaN(+r)) return r; - throw new BadValueError(attr, 'FullDate', x); + throw new BadTypeError(label, 'FullDate', x); } StrictTypeParser._FullDate = _FullDate; - function _byte(x, attr) { + function _byte(x, label) { if (typeof x === 'string') return x; if (x instanceof Buffer) return x.toString('base64'); - throw new BadValueError(attr, 'byte', x); + throw new BadTypeError(label, 'byte', x); } StrictTypeParser._byte = _byte; - function _binary(x, attr) { + function _binary(x, label) { if (typeof x === 'string') return x; if (x instanceof Buffer) return x.toString('hex'); if ((x === null || x === void 0 ? void 0 : x.buffer) instanceof Buffer) return x.toString('hex'); - throw new BadValueError(attr, 'binary', x); + throw new BadTypeError(label, 'binary', x); } StrictTypeParser._binary = _binary; - function _Array(x, attr) { + function _Array(x, label, mapper) { if (x instanceof Array) - return x; - throw new BadValueError(attr, 'Array', x); + return x.map(mapper); + throw new BadTypeError(label, 'Array', x); } StrictTypeParser._Array = _Array; + function undefinedCheck(val, label) { + if (val === undefined) { + throw new BadValueError(label, label + " is required, but got undefined"); + } + } + function parse(stp, val, label) { + // body + undefinedCheck(val, label); + if (val === null) { + throw new BadValueError(label, label + " is not nullable, but got null"); + } + return stp(val, label); + } + StrictTypeParser.parse = parse; + function nullableParse(stp, val, label) { + // body + undefinedCheck(val, label); + return val === null ? null : stp(val, label); + } + StrictTypeParser.nullableParse = nullableParse; StrictTypeParser.supportTypes = [ - 'number', 'string', 'boolean', 'Date', 'FullDate', 'byte', 'binary' + 'int32', 'number', 'string', 'boolean', + 'Date', 'FullDate', 'byte', 'binary' ]; })(StrictTypeParser = exports.StrictTypeParser || (exports.StrictTypeParser = {})); diff --git a/lib/OpenAPI.ts b/lib/OpenAPI.ts index 2149beb..455a111 100644 --- a/lib/OpenAPI.ts +++ b/lib/OpenAPI.ts @@ -145,8 +145,8 @@ export class SchemaType { forProp(prop: string): string { return `${prop}${this.required ? '' : '?'}: ${this.typeName}`; } - stp(prop: string): string { - const stp = SchemaType.gcStp(prop, this.schema); + stp(prop: string, label: string, partial: boolean=false): string { + const stp = SchemaType.gcStp(prop, this.schema, label, partial); return (this.required ? '' : `${prop}===undefined ? undefined : `)+stp; } @@ -189,46 +189,61 @@ export class SchemaType { if (readOnly) sType = `Readonly<${sType}>`; return sType; } - static gcStp(para: string, schema: Schema | Reference): string { - const sPara = `'${para.replace(/'/g, '\\\'')}'`; + static gcStp(para: string, schema: Schema | Reference, + label: string, partial: boolean): string { + // partial: Object only, 1 layer only // object if (isReference(schema)) { - return `new ${new SchemaType(schema, true).typeName}(${para})`; + const typeName = new SchemaType(schema, true).typeName; + return partial ? + `${typeName}.Partial(${para})` : + `new ${typeName}(${para})`; } // any - let code; const {type, nullable, format} = schema; + let sStp; if (type === 'any') return para; if (isArraySchema(schema)) { - code = `STP._Array(${para}, ${sPara}).map(o=>${ - SchemaType.gcStp('o', schema.items)})`; + sStp = `(v, l)=>STP._Array(v, l, elm=>${ + SchemaType.gcStp('elm', schema.items, `${label}[]`, false)})`; } else if (isObjectSchema(schema)) { - code = '{'; + sStp = '()=>({'; for (const [name, sub] of Object.entries(schema.properties)) { - code += `${name}: ${SchemaType.gcStp(para+'.'+name, sub)}, `; + sStp += `${name}: ${ + SchemaType.gcStp(para+'.'+name, sub, label+'.'+name, false)}, `; } - code += '}'; + sStp += '})'; } else { let t; if (type === 'string') { if (format === 'date-time') t = 'Date'; else if (format === 'date') t = 'FullDate'; - else if (format === 'byte') t = 'string'; // TODO - else if (format === 'binary') t = 'string'; // TODO + else if (format === 'byte') t = 'byte'; + else if (format === 'binary') t = 'binary'; else { - if (format) warn(`Unknown format ${format}, use string instead`); + if (format) { + warn(`Unknown string format ${format}, use string instead`); + } t = 'string'; } - } else if (type === 'integer') t = 'number'; - else t = type; + } else if (type === 'integer') { + if (format === 'int32') t = 'int32'; + else { + warn(`Unsupport integer format ${format}, use number instead`); + t = 'number'; // TODO int64 + } + } else t = type; if (!STP.supportTypes.includes(t)) { - warn(`Unknown type ${type}, use any instead`); + warn(`Unsupport type ${type} ${format}, use any instead`); return para; - } else code = `STP._${t}(${para}, ${sPara})`; + } + sStp = `STP._${t}`; } // nullable - if (nullable) code = `${para}===null ? null : ${code}`; - return code; + const funcName = nullable ? 'nullableParse' : 'parse'; + // result + const sLabel = `'${label.replace(/'/g, '\\\'')}'`; // escape + return `STP.${funcName}(${sStp}, ${para}, ${sLabel})`; } } diff --git a/lib/codegen.ts b/lib/codegen.ts index 7c90cae..55e1d92 100644 --- a/lib/codegen.ts +++ b/lib/codegen.ts @@ -101,7 +101,7 @@ function codegenIHandler(funcs: APIFuncs, config: Config, cp: CodePrinter) { cp.writeln(`case ${status}: return this.${ isValid ? 'onSuccess' : 'onFail' }(this.handlers[${status}],`, 1); - cp.writeln(`${schema.stp('data')});`); + cp.writeln(`${schema.stp('data', 'res.body')});`); cp.tab(-1); if (isValid) validTypes.add(schema.typeName); } @@ -167,6 +167,7 @@ function codegenRouter(funcs: APIFuncs, config: Config, cp: CodePrinter) { const { method, url, reqTypes, resTypes, } = func; + const isPartial = method === 'patch'; const statuses = Object.keys(resTypes); // TODO escape const sURL = url.replace(/{(.*?)}/g, ':$1'); // {a} -> :a @@ -177,7 +178,7 @@ function codegenRouter(funcs: APIFuncs, config: Config, cp: CodePrinter) { mid = `bodyParser(${config}), `; } cp.writeln(`router.${method}('${sURL}', ${mid}async ctx => {`, 1); - // TODO permission check, etc + // req if (Object.keys(reqTypes).length === 0) { cp.writeln('const req = {};'); } else { @@ -191,16 +192,15 @@ function codegenRouter(funcs: APIFuncs, config: Config, cp: CodePrinter) { cp.writeln(`${_in}: {`, 1); for (const [name, schema] of Object.entries(paras)) { const pn = `ctxGetParas.${_in}(ctx, '${name}')`; - cp.writeln(`${name}: ${schema.stp(pn)},`); + const label = `req.${_in}`; + cp.writeln(`${name}: ${schema.stp(pn, label)},`); } cp.writeln('},', -1); } // body const {body} = reqTypes; if (body != null) { - const name = 'body'; - const pn = 'reqBody'; - cp.writeln(`${name}: ${body.stp(pn)}`); + cp.writeln(`body: ${body.stp('reqBody', 'req.body', isPartial)}`); } cp.writeln('}} catch(err) {', -1); cp.tab(1); cp.writeln('if(err instanceof STP.BadValueError)', 1); @@ -316,24 +316,36 @@ function codegenSchemas(schemas: Schemas, config: Config, cp: CodePrinter) { `import {FullDate, StrictTypeParser as STP} from '${utilsTSPath}'`); cp.writeln(); // schema - for (const [name, schema] of Object.entries(schemas)) { + for (const [typeName, schema] of Object.entries(schemas)) { if (isObjectSchema(schema)) { - cp.writeln(`export class ${name} {`, 1); + cp.writeln(`export class ${typeName} {`, 1); const propTypes: [string, SchemaType][] = []; for (const [propName, prop] of Object.entries(schema.properties)) { - const propType = new SchemaType(prop, true); // TODO required? + const propType = new SchemaType(prop, true); // TODO required propTypes.push([propName, propType]); cp.writeln(propType.forProp(propName)+';'); } // method cp.writeln('constructor(o: {[_: string]: any}){', 1); for (const [n, t] of propTypes) { - cp.writeln(`this.${n} = ${t.stp(`o.${n}`)};`); + cp.writeln(`this.${n} = ${t.stp(`o.${n}`, typeName+'.'+n)};`); } cp.writeln('}', -1); + // Partial + cp.writeln( + `static Partial(o: {[_: string]: any}): Partial<${typeName}> {`, 1); + cp.writeln(`const r: Partial<${typeName}> = {};`); + const locPartial = `Partial<${typeName}>`; + for (const [n, t] of propTypes) { + cp.writeln(`if (o.${n} !== undefined) r.${n} = ${ + t.stp(`o.${n}`, locPartial+'.'+n)};`); + } + cp.writeln('return r;'); + cp.writeln('}', -1); + // end of class cp.writeln('}', -1); } else { - cp.writeln(`export type ${name} = ${SchemaType.typeNameOf(schema)}`); + cp.writeln(`export type ${typeName} = ${SchemaType.typeNameOf(schema)}`); } } // return diff --git a/lib/utils/StrictTypeParser.ts b/lib/utils/StrictTypeParser.ts index bc938cf..9aa7bb5 100644 --- a/lib/utils/StrictTypeParser.ts +++ b/lib/utils/StrictTypeParser.ts @@ -2,60 +2,98 @@ import {FullDate} from './FullDate'; export module StrictTypeParser { export class BadValueError extends Error { - constructor(public attr: string, public type: string, public value: any) { - super(`${attr}: Can not convert \`${ + constructor(public label: string, message: string) { + super(message); + console.error(this.message); + Object.setPrototypeOf(this, BadTypeError.prototype); + } + } + export class BadTypeError extends BadValueError { + constructor(public label: string, public type: string, public value: any) { + super(label, `${label}: Can not convert \`${ ['object', 'array'].includes(typeof value) ? JSON.stringify(value) : `${value}` }\` to type ${type}`); - console.error(this.message); - Object.setPrototypeOf(this, BadValueError.prototype); } } - export function _number(x: any, attr: string): number { + export function _int32(x: any, label: string): number { + if (typeof x === 'number' && x === (x|0)) return x; + if (typeof x === 'string') { // convert from url + const r = +x|0; + if (x === r.toString()) return r; + } + throw new BadTypeError(label, 'int32', x); + } + export function _number(x: any, label: string): number { if (typeof x === 'number') return x; - if (typeof x === 'string') { + if (typeof x === 'string') { // convert from url const r = +x; if (!isNaN(r)) return r; } - throw new BadValueError(attr, 'number', x); + throw new BadTypeError(label, 'number', x); } - export function _string(x: any, attr: string): string { + export function _string(x: any, label: string): string { if (typeof x === 'string') return x; if (typeof x === 'object') return x.toString(); - throw new BadValueError(attr, 'string', x); + throw new BadTypeError(label, 'string', x); } - export function _boolean(x: any, attr: string): boolean { + export function _boolean(x: any, label: string): boolean { if (typeof x === 'boolean') return x; if (x==='true') return true; if (x==='false') return false; - throw new BadValueError(attr, 'boolean', x); + throw new BadTypeError(label, 'boolean', x); } - export function _Date(x: any, attr: string): Date { + export function _Date(x: any, label: string): Date { const r = new Date(x); - if (!isNaN(+r)) return r; - throw new BadValueError(attr, 'Date', x); + if (x != null && !isNaN(+r)) return r; + throw new BadTypeError(label, 'Date', x); } - export function _FullDate(x: any, attr: string): FullDate { + export function _FullDate(x: any, label: string): FullDate { const r = new FullDate(x); - if (!isNaN(+r)) return r; - throw new BadValueError(attr, 'FullDate', x); + if (x != null && !isNaN(+r)) return r; + throw new BadTypeError(label, 'FullDate', x); } - export function _byte(x: any, attr: string): string { + export function _byte(x: any, label: string): string { if (typeof x === 'string') return x; if (x instanceof Buffer) return x.toString('base64'); - throw new BadValueError(attr, 'byte', x); + throw new BadTypeError(label, 'byte', x); } - export function _binary(x: any, attr: string): string { + export function _binary(x: any, label: string): string { if (typeof x === 'string') return x; if (x instanceof Buffer) return x.toString('hex'); if (x?.buffer instanceof Buffer) return x.toString('hex'); - throw new BadValueError(attr, 'binary', x); + throw new BadTypeError(label, 'binary', x); } - export function _Array(x: any, attr: string): Array { - if (x instanceof Array) return x; - throw new BadValueError(attr, 'Array', x); + export function _Array(x: any, label: string, + mapper: (x: any)=>T): Array { + if (x instanceof Array) return x.map(mapper); + throw new BadTypeError(label, 'Array', x); + } + + function undefinedCheck(val: any, label: string) { + if (val === undefined) { + throw new BadValueError(label, + `${label} is required, but got undefined`); + } + } + export function parse( + stp: (val: any, label: string)=>T, val: any, label: string): T { + // body + undefinedCheck(val, label); + if (val === null) { + throw new BadValueError(label, + `${label} is not nullable, but got null`); + } + return stp(val, label); + } + export function nullableParse( + stp: (val: any, label: string)=>T, val: any, label: string): T | null { + // body + undefinedCheck(val, label); + return val === null ? null : stp(val, label); } export const supportTypes = [ - 'number', 'string', 'boolean', 'Date', 'FullDate', 'byte', 'binary']; + 'int32', 'number', 'string', 'boolean', + 'Date', 'FullDate', 'byte', 'binary']; } diff --git a/package.json b/package.json index 3cce05b..97884c8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "api-codegen-ts", - "version": "1.0.1", + "version": "1.1.0", "description": "OpenAPI code generator for TypeScript", "main": "dist/index.js", "types": "dist/index.d.ts",