swagger-codegen-typescript-koa2
Version:
Generate TypeScript Koa2 server skeleton codes from swagger spec.
262 lines • 9.79 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const create_debug = require("debug");
const fs = require("fs");
const upperFirst = require("lodash.upperfirst");
const mustache = require("mustache");
const sw2 = require("swagger2");
const debug = create_debug('codegen');
class TypeGenerator {
constructor(mode) {
this._mode = mode;
this._codes = [];
this._schema_type = undefined;
this._internal_type = undefined;
this._external_type = undefined;
this._decoder = undefined;
}
get schema_type() {
return (this._schema_type === undefined) ? 'undefined' : this._schema_type;
}
;
get internal_type() {
return (this._internal_type === undefined) ? 'undefined' : this._internal_type;
}
;
get external_type() {
return (this._external_type === undefined) ? this.internal_type : this._external_type;
}
;
get decoder() {
return (this._decoder === undefined) ? `${this.schema_type}_${this.internal_type}` : this._decoder;
}
;
get type() {
return (this._mode.internal) ? this.internal_type : this.external_type;
}
get code() {
if (this._codes.length == 0) {
return this.type;
}
else {
return this._codes.join('\n');
}
}
static walk(def, mode) {
const g = new TypeGenerator(mode);
g.on_definition(def);
return g;
}
on_definition(def) {
this._schema_type = undefined;
this._internal_type = 'undefined';
this._decoder = undefined;
if (def === undefined) {
return;
}
if (def.$ref !== undefined) {
const a = /^#\/(definitions|parameters)\/([a-zA-Z0-9_]+)$/.exec(def.$ref);
if (a !== null && a.length === 3) {
this._schema_type = this._mode.swagger_dts_ns + '.' + a[2];
this._internal_type = this._mode.swagger_dts_ns + '.' + a[2];
return;
}
}
if (def.type === undefined) {
this._schema_type = undefined;
this._internal_type = 'undefined';
}
else if (def.type == 'string') {
this._schema_type = def.type;
this._external_type = 'string';
if (def.format === 'date' || def.format === 'date-time') {
this._internal_type = 'Date';
}
else {
this._internal_type = 'string';
}
}
else if (def.type == 'integer') {
this._schema_type = def.type;
this._external_type = 'number';
if ((def.format === undefined || def.format === 'int64') &&
(def.maximum === undefined || Number.MAX_SAFE_INTEGER < def.maximum ||
def.minimum === undefined || def.minimum < Number.MIN_SAFE_INTEGER)) {
this._internal_type = 'BigNumber';
}
else {
this._internal_type = 'number';
}
}
else if (def.type == 'number') {
this._schema_type = def.type;
this._internal_type = 'number';
}
else if (def.type == 'boolean') {
this._schema_type = def.type;
this._internal_type = 'boolean';
}
else if (def.type === 'array') {
const subtype = TypeGenerator.walk(def.items, this._mode);
this._schema_type = `array_${subtype.schema_type}`;
this._internal_type = `${subtype.code}[]`;
this._decoder = `array_${subtype.schema_type}_${subtype.code}`;
}
else if (def.type === 'object') {
if (def.properties === undefined) {
this._schema_type = def.type;
this._internal_type = 'object';
this._codes.push("{ }");
}
else {
this._schema_type = def.type;
this._internal_type = 'object';
const props = def.properties;
const subcodes = Object.keys(props).map((k) => {
const p = props[k];
const required = ((def.required && (0 <= def.required.indexOf(k))) || (p.required === true)) ? '' : '?';
const t = TypeGenerator.walk(def.properties[k], this._mode);
return (`${k}${required}: ${t.code}`);
});
this._codes.push("{\n" + subcodes.join('\n') + "\n}");
}
}
}
}
class Generator {
constructor(doc, out) {
this.doc = doc;
this.compiled = sw2.compileDocument(doc); // which internally calls json-schema-deref-sync
this.out = out;
this.results = {};
}
static generate(doc, out) {
const g = new Generator(doc, out);
g.on_root();
}
render(filename, data) {
const tmpl = fs.readFileSync(`${__dirname}/mustache/${filename}.mustache`, 'utf-8');
const result = mustache.render(tmpl, data);
this.out.write(result);
}
on_root() {
debug('on_root');
this.results = {};
this.render('header', {
swagger_dts_ns: Generator.SWAGGER_DTS_NS,
});
this.render('paths-header', {});
this.results.operations = [];
Object.keys(this.doc.paths).forEach(path => {
debug('path=' + path);
const path_item = this.doc.paths[path];
const fullpath = this.doc.basePath ? this.doc.basePath + path : path;
const cpath = this.compiled(fullpath);
if (cpath == null) {
throw new Error('cannot found compiled path: ' + fullpath);
}
this.on_path(path, path_item, cpath);
});
this.render('paths-footer', {});
this.render('footer', { operations: this.results.operations });
}
on_path(path, item, cpath) {
debug('on_path: ' + path);
Generator.METHODS.forEach(method => {
const op = item[method];
if (op != null) {
const cop = cpath.path[method];
if (cop == null) {
throw new Error(`cannot found compiled operator: path=${path}, method=${method}`);
}
this.on_operation(path, method, op, cop);
}
});
}
parse_operation_parameter(p, position) {
let ret = {
name: p.name,
required: p.required,
};
if (p.name === undefined) {
throw new Error(`the 'name' field of Parameter is not defined: ${position}`);
}
const schema = p.schema || p;
const g = TypeGenerator.walk(schema, { internal: false, swagger_dts_ns: Generator.SWAGGER_DTS_NS });
if (p.in === undefined) {
throw new Error(`the 'in' field of Parameter is not defined: ${p.name}: ${position}`);
}
else if (p.in === 'query') {
ret.decoder = `decode_string_${g.decoder.toLowerCase()}_external`;
ret.container = `query.${p.name}`;
}
else if (p.in === 'header' || p.in === 'path') {
ret.decoder = `decode_string_${g.decoder.toLowerCase()}_external`;
ret.container = `params.${p.name}`;
}
else if (p.in === 'body') {
ret.decoder = `decode_json_${g.decoder.toLowerCase()}_external`;
ret.container = 'request.body';
}
else {
throw new Error(`the 'in' field of Parameter is unknown: ${p.in}`);
}
ret.type_code = g.code;
ret.schema_code = JSON.stringify(schema);
return ret;
}
parse_operation_response(status, res, position) {
const schema = res.schema;
const g = TypeGenerator.walk(schema, { internal: false, swagger_dts_ns: Generator.SWAGGER_DTS_NS });
return {
type_code: g.code,
schema_code: JSON.stringify(schema),
status: {
raw: status,
pascalcase: upperFirst(status),
}
};
}
on_operation(path, method, op, cop) {
//debug('resolved=' + JSON.stringify(cop.resolvedParameters));
let render_opts = {
method: {
lower: method.toLowerCase(),
upper: method.toUpperCase(),
},
path: {
raw: path,
basePath: this.doc.basePath,
koaPath: path.replace(/\{([a-zA-Z0-9_]+)\}/g, ':$1'),
},
operationId: cop.operationId,
};
const position = `path=${path}, method=${method}`;
render_opts.parameters = cop.resolvedParameters.map((p) => {
return this.parse_operation_parameter(p, position);
});
render_opts.responses = Object.keys(op.responses).map((status) => {
const res = op.responses[status];
return this.parse_operation_response(status, res, position);
});
render_opts.response_types_join = render_opts.responses.map((d) => {
return `Response${d.status.pascalcase}`;
}).join(' | ');
this.render('operation', render_opts);
this.results.operations.push({
operationId: cop.operationId
});
}
}
Generator.METHODS = ['get', 'put', 'post', 'delete', 'options', 'head', 'patch'];
Generator.SWAGGER_DTS_NS = 'd';
function main(inpath, outpath) {
const out = fs.createWriteStream(outpath, { flags: 'wx' });
const doc = sw2.loadDocumentSync(inpath);
if (!sw2.validateDocument(doc)) {
throw Error(`validation failed: ${inpath}`);
}
Generator.generate(doc, out);
}
exports.main = main;
//# sourceMappingURL=index.js.map