From e1566689e365ab9991f638d85348c73877b33538 Mon Sep 17 00:00:00 2001 From: Peter Date: Wed, 13 Mar 2019 18:02:37 +0300 Subject: [PATCH 01/58] Add splitter --- .gitignore | 1 + .jshintignore | 1 + .jshintrc | 5 +- README.md | 28 +++ lib/codegen.js | 64 ++++-- lib/splitter.js | 145 ++++++++++++ package-lock.json | 383 ++++++++++++++++++++++++++------ package.json | 5 +- templates/method.mustache | 170 +++++++------- templates/multi-class.mustache | 20 ++ templates/multi-method.mustache | 59 +++++ templates/node-class.mustache | 320 +++++++++++++------------- 12 files changed, 871 insertions(+), 330 deletions(-) create mode 100644 .jshintignore create mode 100644 lib/splitter.js create mode 100644 templates/multi-class.mustache create mode 100644 templates/multi-method.mustache diff --git a/.gitignore b/.gitignore index 311312ea..b0f128e1 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ wks.* *.swp tmp-* +zz/ diff --git a/.jshintignore b/.jshintignore new file mode 100644 index 00000000..c7bd5c3c --- /dev/null +++ b/.jshintignore @@ -0,0 +1 @@ +lib/splitter.js diff --git a/.jshintrc b/.jshintrc index d7950bea..768d6862 100644 --- a/.jshintrc +++ b/.jshintrc @@ -7,7 +7,7 @@ "curly": true, "eqeqeq": true, "immed": true, - "indent": 4, + "indent": 2, "latedef": true, "newcap": false, "noarg": true, @@ -18,6 +18,5 @@ "strict": true, "trailing": true, "smarttabs": true, - "globals": { - } + "globals": {} } diff --git a/README.md b/README.md index 0a725330..1c4b856a 100644 --- a/README.md +++ b/README.md @@ -80,6 +80,34 @@ In addition to the common options listed below, `getCustomCode()` *requires* a ` description: swagger object ``` +If it is required to generate multiple files for Node (i. e. multiple methods based on the initial JSON) provide the following option: + + multiple: true + +When this option is provided, the module will return an array of objects, that have the following structure: + + { content: , + file: , + functions: , + directory: , + isWrapper: } + +This structure describes a file, that contains generated REST APIs + +`content` - file contents, single string + +`file` - file name + +`functions` - array of function names + +`directory` - name of the directory, that should be created for this file + +`isWrapper` - will be **true** for wrapper file + +Resulting array should always contain a single wrapper file + +**Please notice**: when using this option, the module returns a **promise** + ### Template Variables The following data are passed to the [mustache templates](https://github.com/janl/mustache.js): diff --git a/lib/codegen.js b/lib/codegen.js index 54dbe7ef..db0c0b32 100644 --- a/lib/codegen.js +++ b/lib/codegen.js @@ -5,7 +5,9 @@ var Mustache = require('mustache'); var beautify = require('js-beautify').js_beautify; var lint = require('jshint').JSHINT; var _ = require('lodash'); + var ts = require('./typescript'); +var splitter = require('./splitter'); var normalizeName = function(id) { return id.replace(/\.|\-|\{|\}|\s/g, '_'); @@ -47,41 +49,44 @@ var getViewForSwagger2 = function(opts, type){ definitions: [] }; - _.forEach(swagger.paths, function(api, path){ + _.forEach(swagger.paths, function(api, path) { var globalParams = []; /** * @param {Object} op - meta data for the request * @param {string} m - HTTP method name - eg: 'get', 'post', 'put', 'delete' */ - _.forEach(api, function(op, m){ - if(m.toLowerCase() === 'parameters') { + _.forEach(api, function(op, m) { + if (m.toLowerCase() === 'parameters') { globalParams = op; } }); - _.forEach(api, function(op, m){ + _.forEach(api, function(op, m) { var M = m.toUpperCase(); - if(M === '' || authorizedMethods.indexOf(M) === -1) { + // check if this is a supported method + if (M === '' || authorizedMethods.indexOf(M) === -1) { return; } var secureTypes = []; - if(swagger.securityDefinitions !== undefined || op.security !== undefined) { - var mergedSecurity = _.merge([], swagger.security, op.security).map(function(security){ + if (swagger.securityDefinitions !== undefined || op.security !== undefined) { + var mergedSecurity = _.merge([], swagger.security, op.security).map(function(security) { return Object.keys(security); }); - if(swagger.securityDefinitions) { - for(var sk in swagger.securityDefinitions) { - if(mergedSecurity.join(',').indexOf(sk) !== -1){ + if (swagger.securityDefinitions) { + for (var sk in swagger.securityDefinitions) { + if (mergedSecurity.join(',').indexOf(sk) !== -1) { secureTypes.push(swagger.securityDefinitions[sk].type); } } } } + var methodName = (op.operationId ? normalizeName(op.operationId) : getPathToMethodName(opts, m, path)); + // Make sure the method name is unique - if(methods.indexOf(methodName) !== -1) { + if (methods.indexOf(methodName) !== -1) { var i = 1; - while(true) { - if(methods.indexOf(methodName + '_' + i) !== -1) { + while (true) { + if (methods.indexOf(methodName + '_' + i) !== -1) { i++; } else { methodName = methodName + '_' + i; @@ -105,8 +110,14 @@ var getViewForSwagger2 = function(opts, type){ isSecureApiKey: secureTypes.indexOf('apiKey') !== -1, isSecureBasic: secureTypes.indexOf('basic') !== -1, parameters: [], - headers: [] + headers: [], }; + + // add 'destination' field if 'multiple: true' + if (opts.multiple) { + method.destination = op.tags[0].toLowerCase(); + } + if(method.isSecure && method.isSecureToken) { data.isSecureToken = method.isSecureToken; } @@ -117,7 +128,7 @@ var getViewForSwagger2 = function(opts, type){ data.isSecureBasic = method.isSecureBasic; } var produces = op.produces || swagger.produces; - if(produces) { + if (produces) { method.headers.push({ name: 'Accept', value: `'${produces.map(function(value) { return value; }).join(', ')}'`, @@ -253,9 +264,16 @@ var getViewForSwagger1 = function(opts, type){ return data; }; +/** + * Generate code based on the input file + * @param opts - options for the file generation + * @param type - type of code / file to be generated (angular, custom, node, react, typescript) + * @returns {*} + */ var getCode = function(opts, type) { // For Swagger Specification version 2.0 value of field 'swagger' must be a string '2.0' var data = opts.swagger.swagger === '2.0' ? getViewForSwagger2(opts, type) : getViewForSwagger1(opts, type); + if (type === 'custom') { if (!_.isObject(opts.template) || !_.isString(opts.template.class) || !_.isString(opts.template.method)) { throw new Error('Unprovided custom template. Please use the following template: template: { class: "...", method: "...", request: "..." }'); @@ -265,9 +283,16 @@ var getCode = function(opts, type) { opts.template = {}; } var templates = __dirname + '/../templates/'; - opts.template.class = opts.template.class || fs.readFileSync(templates + type + '-class.mustache', 'utf-8'); - opts.template.method = opts.template.method || fs.readFileSync(templates + (type === 'typescript' ? 'typescript-' : '') + 'method.mustache', 'utf-8'); - if(type === 'typescript') { + + // choose templates based on the 'multiple' option TODO: Typescript support? + if (opts.multiple) { + opts.template.class = fs.readFileSync(templates + 'multi-class.mustache', 'utf-8'); + opts.template.method = fs.readFileSync(templates + 'multi-method.mustache', 'utf-8'); + } else { + opts.template.class = opts.template.class || fs.readFileSync(templates + type + '-class.mustache', 'utf-8'); + opts.template.method = opts.template.method || fs.readFileSync(templates + (type === 'typescript' ? 'typescript-' : '') + 'method.mustache', 'utf-8'); + } + if (type === 'typescript') { opts.template.type = opts.template.type || fs.readFileSync(templates + 'type.mustache', 'utf-8'); } } @@ -302,6 +327,9 @@ var getCode = function(opts, type) { } }); } + if (opts.multiple) { + return splitter.split(source, opts.className); + } if (opts.beautify === undefined || opts.beautify === true) { return beautify(source, { indent_size: 4, max_preserve_newlines: 2 }); } else { diff --git a/lib/splitter.js b/lib/splitter.js new file mode 100644 index 00000000..8c751151 --- /dev/null +++ b/lib/splitter.js @@ -0,0 +1,145 @@ +const beautify = require('js-beautify').js; +const fs = require('fs'); +const { transform } = require('lebab'); + +/** + * First letter capitalization, utility function + * @param string + * @returns {string} + */ +function capitalize(string) { + return string[0].toUpperCase() + string.slice(1); +} + +/** + * Split the code to create multiple files and directories + * @param {string} source - source code generated by swagger-js-codegen (using special templates!) + * @param {string} className - specified class name + * @returns {array} - array of objects (files and wrapper) + */ +async function split(source, className) { + try { + // convert ES5 to ES6 + const { code, warnings } = transform(source, ['let', 'arrow', 'arrow-return', 'class']); + + // show conversion errors and warnings + if (warnings && warnings.length && warnings.length > 0) { + console.log('> codegen-splitter @ ES6 conversion warnings:\n', warnings); + } + + // create the source file TODO: better understanding of the file placement + fs.writeFileSync(`${__dirname}/source.js`, code, (err) => { + if (err) { + throw new Error(err); + } + }); + + // load source file and get all of the available methods from the class + console.log('> various', `${__dirname}/source.js`, `${capitalize(className.toLowerCase())}`); + const Instance = require(`${__dirname}/source.js`)[`${capitalize(className.toLowerCase())}`]; + console.log('> ins', Instance); + const methods = Object.getOwnPropertyNames(Instance.prototype).filter(m => m !== 'constructor'); + + // abort everything if there are no methods (i. e. incorrect JSON or something went wrong) + if (methods.length === 0) { + return console.log('> Methods not found'); + } + + // create new instance of the class, use it as a provider of the code + const provider = new Instance(); + + // process all of the methods, store code in array + const destinations = []; + methods.forEach((method) => { + let standalone = provider[method].toString(); + let functionName = standalone.split('(req, res)')[0]; + const destination = functionName.split('_')[0]; + functionName = functionName.split('_')[1]; + standalone = `async function ${functionName}(req, res) ${standalone.split('(req, res)')[1]}`; + destinations.push({ + content: standalone, + file: destination, + function: functionName, + }); + }); + + // create file list + const files = []; + destinations.forEach((f) => { + if (!files.some(entry => entry.file === f.file)) { + files.push({ + content: '', + file: f.file, + functions: [], + }); + } + }); + + // building the controllers + files.forEach((file, i) => { + destinations.forEach((d) => { + if (file.file === d.file) { + files[i].content = `${files[i].content} + + ${d.content}`; + files[i].functions.push(d.function); + } + }); + }); + + // add 'use strict' and exporting + files.forEach((file, i) => { + let fList = ''; + file.functions.forEach((f) => { + fList = `${fList} + ${f},`; + }); + files[i].content = beautify(`'use strict'; + + /* auto-generated: ${file.file}.controller.js */ + ${file.content} + + module.exports = { + ${fList} + };`, { indent_size: 2 }); + }); + + // delete the source file TODO: better understanding of the file placement + await fs.unlink(`${__dirname}/source.js`, (err) => { + if (err) { + throw new Error(err); + } + }); + + // create API wrapper TODO: what should be inside of the wrapper? What to export? + let wrapperContent = ''; + files.forEach((file) => { + const name = file.file; + wrapperContent = `${wrapperContent} + const ${name} = require('./${name}/${name}.controller.js');`; + }); + const wrapper = { + content: beautify(wrapperContent, { indent_size: 2 }), + directory: null, + file: 'index.js', // TODO: wrapper name? + isWrapper: true, + }; + + // finalize and return + files.forEach((file, i) => { + files[i].directory = file.file; + files[i].file = `${file.file}.controller.js`; + files[i].isWrapper = false; + }); + files.push(wrapper); + + return files; + } catch (err) { + console.log('> err', err); + throw new Error(err); + } +} + +module.exports = { + split, +}; diff --git a/package-lock.json b/package-lock.json index 625657b5..2bb2dda6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,16 @@ "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" }, + "acorn": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.1.tgz", + "integrity": "sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA==" + }, + "acorn-jsx": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.0.1.tgz", + "integrity": "sha512-HJ7CfNHrfJLlNTzIEUTj43LNWGkqpRLxm3YjAlcD0ACydk9XynzYsCBHxut+iqt+1aBXkx9UP/w/ZqMr13XIzg==" + }, "ajv": { "version": "5.5.2", "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", @@ -95,6 +105,11 @@ "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", "dev": true }, + "ast-types": { + "version": "0.11.5", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.11.5.tgz", + "integrity": "sha512-oJjo+5e7/vEc2FBK8gUalV0pba4L3VdBIs2EKhOLHLcOd2FgQIVQN9xb0eZ9IjEWyAL7vq6fGJxOvVvdCHNyMw==" + }, "async": { "version": "1.5.2", "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", @@ -134,11 +149,6 @@ "tweetnacl": "^0.14.3" } }, - "bluebird": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", - "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==" - }, "boxen": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/boxen/-/boxen-1.2.2.tgz", @@ -299,9 +309,9 @@ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, "config-chain": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.11.tgz", - "integrity": "sha1-q6CXR9++TD5w52am5BWG4YWfxvI=", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.12.tgz", + "integrity": "sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA==", "requires": { "ini": "^1.3.4", "proto-list": "~1.2.1" @@ -382,6 +392,14 @@ "array-find-index": "^1.0.1" } }, + "d": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", + "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", + "requires": { + "es5-ext": "^0.10.9" + } + }, "dashdash": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", @@ -439,30 +457,25 @@ "dev": true }, "dom-serializer": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz", - "integrity": "sha1-BzxpdUbOB4DOI75KKOKT5AvDDII=", + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz", + "integrity": "sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==", "requires": { - "domelementtype": "~1.1.1", - "entities": "~1.1.1" + "domelementtype": "^1.3.0", + "entities": "^1.1.1" }, "dependencies": { - "domelementtype": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz", - "integrity": "sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs=" - }, "entities": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz", - "integrity": "sha1-blwtClYhtdra7O+AuQ7ftc13cvA=" + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" } } }, "domelementtype": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz", - "integrity": "sha1-sXrtguirWeUt2cGbF1bg/BhyBMI=" + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" }, "domhandler": { "version": "2.3.0", @@ -505,15 +518,26 @@ } }, "editorconfig": { - "version": "0.13.3", - "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-0.13.3.tgz", - "integrity": "sha512-WkjsUNVCu+ITKDj73QDvi0trvpdDWdkDyHybDGSXPfekLCqwmpD7CP7iPbvBgosNuLcI96XTDwNa75JyFl7tEQ==", - "requires": { - "bluebird": "^3.0.5", - "commander": "^2.9.0", - "lru-cache": "^3.2.0", - "semver": "^5.1.0", + "version": "0.15.3", + "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-0.15.3.tgz", + "integrity": "sha512-M9wIMFx96vq0R4F+gRpY3o2exzb8hEj/n9S8unZtHSvYjibBp/iMufSzvmOcV/laG0ZtuTVGtiJggPOSW2r93g==", + "requires": { + "commander": "^2.19.0", + "lru-cache": "^4.1.5", + "semver": "^5.6.0", "sigmund": "^1.0.1" + }, + "dependencies": { + "commander": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz", + "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==" + }, + "semver": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", + "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==" + } } }, "entities": { @@ -530,16 +554,129 @@ "is-arrayish": "^0.2.1" } }, + "es5-ext": { + "version": "0.10.48", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.48.tgz", + "integrity": "sha512-CdRvPlX/24Mj5L4NVxTs4804sxiS2CjVprgCmrgoDkdmjdY4D+ySHa7K3jJf8R40dFg0tIm3z/dk326LrnuSGw==", + "requires": { + "es6-iterator": "~2.0.3", + "es6-symbol": "~3.1.1", + "next-tick": "1" + } + }, + "es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", + "requires": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "es6-map": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/es6-map/-/es6-map-0.1.5.tgz", + "integrity": "sha1-kTbgUD3MBqMBaQ8LsU/042TpSfA=", + "requires": { + "d": "1", + "es5-ext": "~0.10.14", + "es6-iterator": "~2.0.1", + "es6-set": "~0.1.5", + "es6-symbol": "~3.1.1", + "event-emitter": "~0.3.5" + } + }, + "es6-set": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.5.tgz", + "integrity": "sha1-0rPsXU2ADO2BjbU40ol02wpzzLE=", + "requires": { + "d": "1", + "es5-ext": "~0.10.14", + "es6-iterator": "~2.0.1", + "es6-symbol": "3.1.1", + "event-emitter": "~0.3.5" + } + }, + "es6-symbol": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", + "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=", + "requires": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, + "es6-weak-map": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.2.tgz", + "integrity": "sha1-XjqzIlH/0VOKH45f+hNXdy+S2W8=", + "requires": { + "d": "1", + "es5-ext": "^0.10.14", + "es6-iterator": "^2.0.1", + "es6-symbol": "^3.1.1" + } + }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" }, + "escope": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/escope/-/escope-3.6.0.tgz", + "integrity": "sha1-4Bl16BJ4GhY6ba392AOY3GTIicM=", + "requires": { + "es6-map": "^0.1.3", + "es6-weak-map": "^2.0.1", + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "eslint-visitor-keys": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", + "integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==" + }, + "espree": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-4.1.0.tgz", + "integrity": "sha512-I5BycZW6FCVIub93TeVY1s7vjhP9CY6cXCznIRfiig7nRviKZYdRnj/sHEWC6A7WE9RDWOFq9+7OsWSYz8qv2w==", + "requires": { + "acorn": "^6.0.2", + "acorn-jsx": "^5.0.0", + "eslint-visitor-keys": "^1.0.0" + } + }, "esprima": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz", "integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==" }, + "esrecurse": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", + "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "requires": { + "estraverse": "^4.1.0" + } + }, + "estraverse": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", + "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=" + }, + "event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", + "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=", + "requires": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, "eventemitter2": { "version": "0.4.14", "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz", @@ -610,6 +747,14 @@ "integrity": "sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A=", "dev": true }, + "f-matches": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/f-matches/-/f-matches-1.1.0.tgz", + "integrity": "sha1-GOmFRQAL84EAe3qfmaae7Q0wQzY=", + "requires": { + "lodash": "^4.17.5" + } + }, "fast-deep-equal": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", @@ -632,6 +777,7 @@ "version": "2.2.4", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz", "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==", + "dev": true, "requires": { "is-number": "^2.1.0", "isobject": "^2.0.0", @@ -921,6 +1067,22 @@ "supports-color": "^2.0.0" } }, + "jshint": { + "version": "2.9.7", + "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.9.7.tgz", + "integrity": "sha512-Q8XN38hGsVQhdlM+4gd1Xl7OB1VieSuCJf+fEJjpo59JH99bVJhXRXAh26qQ15wfdd1VPMuDWNeSWoNl53T4YA==", + "dev": true, + "requires": { + "cli": "~1.0.0", + "console-browserify": "1.1.x", + "exit": "0.1.x", + "htmlparser2": "3.8.x", + "lodash": "~4.17.10", + "minimatch": "~3.0.2", + "shelljs": "0.3.x", + "strip-json-comments": "1.0.x" + } + }, "strip-ansi": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", @@ -1199,7 +1361,8 @@ "is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true }, "is-builtin-module": { "version": "1.0.0", @@ -1278,6 +1441,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", + "dev": true, "requires": { "kind-of": "^3.0.2" } @@ -1348,6 +1512,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, "requires": { "isarray": "1.0.0" }, @@ -1355,7 +1520,8 @@ "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true } } }, @@ -1366,14 +1532,39 @@ "dev": true }, "js-beautify": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.7.4.tgz", - "integrity": "sha512-6YX1g+lIl0/JDxjFFbgj7fz6i0bWFa2Hdc7PfGqFhynaEiYe1NJ3R1nda0VGaRiGU82OllR+EGDoWFpGr3k5Kg==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.9.0.tgz", + "integrity": "sha512-P0skmY4IDjfLiVrx+GLDeme8w5G0R1IGXgccVU5HP2VM3lRblH7qN2LTea5vZAxrDjpZBD0Jv+ahpjwVcbz/rw==", "requires": { - "config-chain": "~1.1.5", - "editorconfig": "^0.13.2", + "config-chain": "^1.1.12", + "editorconfig": "^0.15.2", + "glob": "^7.1.3", "mkdirp": "~0.5.0", - "nopt": "~3.0.1" + "nopt": "~4.0.1" + }, + "dependencies": { + "glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "nopt": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", + "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + } } }, "js-yaml": { @@ -1393,25 +1584,18 @@ "optional": true }, "jshint": { - "version": "2.9.5", - "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.9.5.tgz", - "integrity": "sha1-HnJSkVzmgbQIJ+4UJIxG006apiw=", + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.10.1.tgz", + "integrity": "sha512-9GpPfKeffYBl7oBDX2lHPG16j0AM7D2bn3aLy9DaWTr6CWa0i/7UGhX8WLZ7V14QQnnr4hXbjauTLYg06F+HYw==", "requires": { "cli": "~1.0.0", "console-browserify": "1.1.x", "exit": "0.1.x", "htmlparser2": "3.8.x", - "lodash": "3.7.x", + "lodash": "~4.17.10", "minimatch": "~3.0.2", "shelljs": "0.3.x", "strip-json-comments": "1.0.x" - }, - "dependencies": { - "lodash": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.7.0.tgz", - "integrity": "sha1-Nni9irmVBXwHreg27S7wh9qBHUU=" - } } }, "json-schema": { @@ -1458,6 +1642,7 @@ "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, "requires": { "is-buffer": "^1.1.5" } @@ -1470,6 +1655,33 @@ "package-json": "^4.0.0" } }, + "lebab": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/lebab/-/lebab-3.0.4.tgz", + "integrity": "sha512-wrh0VBhyPybeS/NaydN09KvPOeK7bAHt0Yyuz96t/kuqzbKNxoawlAxXlJ3vDJZ890jL+njMJitQA69Y6gztJQ==", + "requires": { + "commander": "^2.15.1", + "escope": "^3.6.0", + "espree": "^4.0.0", + "estraverse": "^4.1.1", + "f-matches": "^1.1.0", + "glob": "^7.1.2", + "lodash": "^4.17.11", + "recast": "^0.15.5" + }, + "dependencies": { + "commander": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz", + "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==" + }, + "lodash": { + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", + "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" + } + } + }, "load-json-file": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", @@ -1512,11 +1724,12 @@ "integrity": "sha1-TjNms55/VFfjXxMkvfb4jQv8cwY=" }, "lru-cache": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-3.2.0.tgz", - "integrity": "sha1-cXibO39Tmb7IVl3aOKow0qCX7+4=", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", "requires": { - "pseudomap": "^1.0.1" + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" } }, "make-dir": { @@ -1548,7 +1761,8 @@ "math-random": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.1.tgz", - "integrity": "sha1-izqsWIuKZuSXXjzepn97sylgH6w=" + "integrity": "sha1-izqsWIuKZuSXXjzepn97sylgH6w=", + "dev": true }, "meow": { "version": "3.7.0", @@ -1656,6 +1870,11 @@ "resolved": "https://registry.npmjs.org/mustache/-/mustache-2.2.1.tgz", "integrity": "sha1-LEDKIcJ49TFQaCvPkJDkGjM5uHY=" }, + "next-tick": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", + "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=" + }, "node-fs": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/node-fs/-/node-fs-0.1.7.tgz", @@ -1701,6 +1920,7 @@ "version": "3.0.6", "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", + "dev": true, "requires": { "abbrev": "1" } @@ -1769,11 +1989,24 @@ "wrappy": "1" } }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" + }, "os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "dev": true + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" + }, + "osenv": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } }, "p-finally": { "version": "1.0.0", @@ -1892,6 +2125,11 @@ "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=", "dev": true }, + "private": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", + "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==" + }, "process-nextick-args": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", @@ -1930,6 +2168,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.0.0.tgz", "integrity": "sha512-VdxFOIEY3mNO5PtSRkkle/hPJDHvQhK21oa73K4yAc9qmp6N429gAyF1gZMOTMeS0/AYzaV/2Trcef+NaIonSA==", + "dev": true, "requires": { "is-number": "^4.0.0", "kind-of": "^6.0.0", @@ -1939,12 +2178,14 @@ "is-number": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", - "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==" + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", + "dev": true }, "kind-of": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true } } }, @@ -2003,6 +2244,17 @@ "string_decoder": "~0.10.x" } }, + "recast": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/recast/-/recast-0.15.5.tgz", + "integrity": "sha512-nkAYNqarh73cMWRKFiPQ8I9dOLFvFk6SnG8u/LUlOYfArDOD/EjsVRAs860TlBLrpxqAXHGET/AUAVjdEymL5w==", + "requires": { + "ast-types": "0.11.5", + "esprima": "~4.0.0", + "private": "~0.1.5", + "source-map": "~0.6.1" + } + }, "redent": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", @@ -2048,12 +2300,14 @@ "repeat-element": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz", - "integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=" + "integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=", + "dev": true }, "repeat-string": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true }, "repeating": { "version": "2.0.1", @@ -2159,6 +2413,11 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, "spdx-correct": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.0.tgz", diff --git a/package.json b/package.json index 824b3488..efedbbdd 100644 --- a/package.json +++ b/package.json @@ -30,9 +30,10 @@ "homepage": "https://github.com/wcandillon/swagger-js-codegen", "dependencies": { "commander": "^2.9.0", - "js-beautify": "^1.5.1", + "js-beautify": "^1.9.0", "js-yaml": "^3.10.0", - "jshint": "^2.5.1", + "jshint": "^2.10.1", + "lebab": "^3.0.4", "lodash": "^4.17.10", "mustache": "2.2.1", "update-notifier": "^2.1.0" diff --git a/templates/method.mustache b/templates/method.mustache index 5151e26a..6ad3d9df 100644 --- a/templates/method.mustache +++ b/templates/method.mustache @@ -1,96 +1,96 @@ /** - * {{&summary}} - * @method - * @name {{&className}}#{{&methodName}} - * @param {object} parameters - method options and parameters +* {{&summary}} +* @method +* @name {{&className}}#{{&methodName}} +* @param {object} parameters - method options and parameters {{#parameters}} - {{^isSingleton}} * @param {{=<% %>=}}{<%&type%>}<%={{ }}=%> parameters.{{&camelCaseName}} - {{&description}}{{/isSingleton}} + {{^isSingleton}} * @param {{=<% %>=}}{<%&type%>}<%={{ }}=%> parameters.{{&camelCaseName}} - {{&description}}{{/isSingleton}} {{/parameters}} - */ - {{&className}}.prototype.{{&methodName}} = function(parameters){ - if(parameters === undefined) { - parameters = {}; - } - {{#isES6}}let{{/isES6}}{{^isES6}}var{{/isES6}} deferred = {{#isNode}}Q{{/isNode}}{{^isNode}}$q{{/isNode}}.defer(); - {{#isES6}}let{{/isES6}}{{^isES6}}var{{/isES6}} domain = this.domain, path = '{{&path}}'; - {{#isES6}}let{{/isES6}}{{^isES6}}var{{/isES6}} body = {}, queryParameters = {}, headers = {}, form = {}; +*/ +{{&className}}.prototype.{{&methodName}} = function(parameters){ +if(parameters === undefined) { +parameters = {}; +} +{{#isES6}}let{{/isES6}}{{^isES6}}var{{/isES6}} deferred = {{#isNode}}Q{{/isNode}}{{^isNode}}$q{{/isNode}}.defer(); +{{#isES6}}let{{/isES6}}{{^isES6}}var{{/isES6}} domain = this.domain, path = '{{&path}}'; +{{#isES6}}let{{/isES6}}{{^isES6}}var{{/isES6}} body = {}, queryParameters = {}, headers = {}, form = {}; + +{{#isSecure}} + headers = this.setAuthHeaders(headers); +{{/isSecure}} +{{#headers}} + headers['{{&name}}'] = [{{&value}}]; +{{/headers}} - {{#isSecure}} - headers = this.setAuthHeaders(headers); - {{/isSecure}} - {{#headers}} - headers['{{&name}}'] = [{{&value}}]; - {{/headers}} +{{#parameters}} + {{#isQueryParameter}} + {{#isSingleton}} + queryParameters['{{&name}}'] = '{{&singleton}}'; + {{/isSingleton}} + {{^isSingleton}} + {{#isPatternType}} + Object.keys(parameters).forEach(function(parameterName) { + if(new RegExp('{{&pattern}}').test(parameterName)){ + queryParameters[parameterName] = parameters[parameterName]; + } + }); + {{/isPatternType}} + {{#default}} + /** set default value **/ + queryParameters['{{&name}}'] = {{&default}}; + {{/default}} - {{#parameters}} - {{#isQueryParameter}} - {{#isSingleton}} - queryParameters['{{&name}}'] = '{{&singleton}}'; - {{/isSingleton}} - {{^isSingleton}} - {{#isPatternType}} - Object.keys(parameters).forEach(function(parameterName) { - if(new RegExp('{{&pattern}}').test(parameterName)){ - queryParameters[parameterName] = parameters[parameterName]; - } - }); - {{/isPatternType}} - {{#default}} - /** set default value **/ - queryParameters['{{&name}}'] = {{&default}}; - {{/default}} + {{^isPatternType}} + if(parameters['{{&camelCaseName}}'] !== undefined){ + queryParameters['{{&name}}'] = parameters['{{&camelCaseName}}']; + } + {{/isPatternType}} + {{/isSingleton}} + {{/isQueryParameter}} - {{^isPatternType}} - if(parameters['{{&camelCaseName}}'] !== undefined){ - queryParameters['{{&name}}'] = parameters['{{&camelCaseName}}']; - } - {{/isPatternType}} - {{/isSingleton}} - {{/isQueryParameter}} - - {{#isPathParameter}} - path = path.replace('{{=<% %>=}}{<%&name%>}<%={{ }}=%>', parameters['{{&camelCaseName}}']); - {{/isPathParameter}} - - {{#isHeaderParameter}} - {{#isSingleton}} - headers['{{&name}}'] = '{{&singleton}}'; - {{/isSingleton}} - {{^isSingleton}} - if(parameters['{{&camelCaseName}}'] !== undefined){ - headers['{{&name}}'] = parameters['{{&camelCaseName}}']; - } - {{/isSingleton}} - {{/isHeaderParameter}} - - {{#isBodyParameter}} - if(parameters['{{&camelCaseName}}'] !== undefined){ - body = parameters['{{&camelCaseName}}']; - } - {{/isBodyParameter}} + {{#isPathParameter}} + path = path.replace('{{=<% %>=}}{<%&name%>}<%={{ }}=%>', parameters['{{&camelCaseName}}']); + {{/isPathParameter}} - {{#isFormParameter}} - {{#isSingleton}} - form['{{&name}}'] = '{{&singleton}}'; - {{/isSingleton}} - {{^isSingleton}} - if(parameters['{{&camelCaseName}}'] !== undefined){ - form['{{&name}}'] = parameters['{{&camelCaseName}}']; - } - {{/isSingleton}} - {{/isFormParameter}} + {{#isHeaderParameter}} + {{#isSingleton}} + headers['{{&name}}'] = '{{&singleton}}'; + {{/isSingleton}} + {{^isSingleton}} + if(parameters['{{&camelCaseName}}'] !== undefined){ + headers['{{&name}}'] = parameters['{{&camelCaseName}}']; + } + {{/isSingleton}} + {{/isHeaderParameter}} - {{#required}} - if(parameters['{{&camelCaseName}}'] === undefined){ - deferred.reject(new Error('Missing required {{¶mType}} parameter: {{&camelCaseName}}')); - return deferred.promise; - } - {{/required}} - - {{/parameters}} - queryParameters = mergeQueryParams(parameters, queryParameters); + {{#isBodyParameter}} + if(parameters['{{&camelCaseName}}'] !== undefined){ + body = parameters['{{&camelCaseName}}']; + } + {{/isBodyParameter}} - this.request('{{method}}', domain + path, parameters, body, headers, queryParameters, form, deferred); + {{#isFormParameter}} + {{#isSingleton}} + form['{{&name}}'] = '{{&singleton}}'; + {{/isSingleton}} + {{^isSingleton}} + if(parameters['{{&camelCaseName}}'] !== undefined){ + form['{{&name}}'] = parameters['{{&camelCaseName}}']; + } + {{/isSingleton}} + {{/isFormParameter}} + {{#required}} + if(parameters['{{&camelCaseName}}'] === undefined){ + deferred.reject(new Error('Missing required {{¶mType}} parameter: {{&camelCaseName}}')); return deferred.promise; - }; + } + {{/required}} + +{{/parameters}} +queryParameters = mergeQueryParams(parameters, queryParameters); + +this.request('{{method}}', domain + path, parameters, body, headers, queryParameters, form, deferred); + +return deferred.promise; +}; diff --git a/templates/multi-class.mustache b/templates/multi-class.mustache new file mode 100644 index 00000000..acfa8c3d --- /dev/null +++ b/templates/multi-class.mustache @@ -0,0 +1,20 @@ +'use strict'; + +/** +* {{&description}} +* @class {{&className}} +*/ +{{#isES6}}let{{/isES6}}{{^isES6}}var{{/isES6}} {{&className}} = (function(){ + + function {{&className}}() { + + } + + {{#methods}} + {{> method}} + {{/methods}} + + return {{&className}}; +})(); + +exports.{{&className}} = {{&className}}; diff --git a/templates/multi-method.mustache b/templates/multi-method.mustache new file mode 100644 index 00000000..f41dd704 --- /dev/null +++ b/templates/multi-method.mustache @@ -0,0 +1,59 @@ +/** + * {{&summary}} + * @method {{&methodName}} + * @name {{&className}}#{{&methodName}} + * @param {object} req - request object + * @param {object} res - response object + */ +{{&className}}.prototype.{{&destination}}_{{&methodName}} = function(req, res) { + {{#parameters}} + {{#isQueryParameter}} + // check if query parameter is passed + if (!req.query['{{=<% %>=}}<%&name%><%={{ }}=%>']) { + return res.code(400).send({ status: 400, info: 'MISSING_QUERY_PARAMETER', value: '{{=<% %>=}}<%&name%><%={{ }}=%>', }); + } + {{/isQueryParameter}} + + {{#isPathParameter}} + // check if path parameter is passed + if (!req.params['{{=<% %>=}}<%&name%><%={{ }}=%>']) { + return res.code(400).send({ status: 400, info: 'MISSING_PATH_PARAMETER', value: '{{=<% %>=}}<%&name%><%={{ }}=%>', }); + } + {{/isPathParameter}} + + {{#isBodyParameter}} + // check if body parameter is passed + if (!req.body['{{=<% %>=}}<%&name%><%={{ }}=%>']) { + return res.code(400).send({ status: 400, info: 'MISSING_BODY_PARAMETER', value: '{{=<% %>=}}<%&name%><%={{ }}=%>', }); + } + {{/isBodyParameter}} + + {{#isHeaderParameter}} + {{#isSingleton}} + headers['{{&name}}'] = '{{&singleton}}'; + {{/isSingleton}} + {{^isSingleton}} + if(parameters['{{&camelCaseName}}'] !== undefined){ + headers['{{&name}}'] = parameters['{{&camelCaseName}}']; + } + {{/isSingleton}} + {{/isHeaderParameter}} + + {{#isFormParameter}} + {{#isSingleton}} + form['{{&name}}'] = '{{&singleton}}'; + {{/isSingleton}} + {{^isSingleton}} + if(parameters['{{&camelCaseName}}'] !== undefined){ + form['{{&name}}'] = parameters['{{&camelCaseName}}']; + } + {{/isSingleton}} + {{/isFormParameter}} + {{/parameters}} + + /* + Method logic + */ + + return res.code(200).send({ info: 'OK' }); +}; diff --git a/templates/node-class.mustache b/templates/node-class.mustache index 8001cdd1..9fd6a7d2 100644 --- a/templates/node-class.mustache +++ b/templates/node-class.mustache @@ -1,174 +1,174 @@ /*jshint -W069 */ /** - * {{&description}} - * @class {{&className}} - * @param {(string|object)} [domainOrOptions] - The project domain or options object. If object, see the object's optional properties. - * @param {string} [domainOrOptions.domain] - The project domain - * @param {object} [domainOrOptions.token] - auth token - object with value property and optional headerOrQueryName and isQuery properties - */ +* {{&description}} +* @class {{&className}} +* @param {(string|object)} [domainOrOptions] - The project domain or options object. If object, see the object's optional properties. +* @param {string} [domainOrOptions.domain] - The project domain +* @param {object} [domainOrOptions.token] - auth token - object with value property and optional headerOrQueryName and isQuery properties +*/ {{#isES6}}let{{/isES6}}{{^isES6}}var{{/isES6}} {{&className}} = (function(){ - 'use strict'; +'use strict'; - {{#isES6}}let{{/isES6}}{{^isES6}}var{{/isES6}} request = require('request'); - {{#isES6}}let{{/isES6}}{{^isES6}}var{{/isES6}} Q = require('q'); +{{#isES6}}let{{/isES6}}{{^isES6}}var{{/isES6}} request = require('request'); +{{#isES6}}let{{/isES6}}{{^isES6}}var{{/isES6}} Q = require('q'); - function {{&className}}(options){ - {{#isES6}}let{{/isES6}}{{^isES6}}var{{/isES6}} domain = (typeof options === 'object') ? options.domain : options; - this.domain = domain ? domain : '{{&domain}}'; - if(this.domain.length === 0) { - throw new Error('Domain parameter must be specified as a string.'); - } - {{#isSecure}} - {{#isSecureToken}} - this.token = (typeof options === 'object') ? (options.token ? options.token : {}) : {}; - {{/isSecureToken}} - {{#isSecureApiKey}} - this.apiKey = (typeof options === 'object') ? (options.apiKey ? options.apiKey : {}) : {}; - {{/isSecureApiKey}} - {{#isSecureBasic}} - this.basic = (typeof options === 'object') ? (options.basic ? options.basic : {}) : {}; - {{/isSecureBasic}} - {{/isSecure}} - } +function {{&className}}(options){ +{{#isES6}}let{{/isES6}}{{^isES6}}var{{/isES6}} domain = (typeof options === 'object') ? options.domain : options; +this.domain = domain ? domain : '{{&domain}}'; +if(this.domain.length === 0) { +throw new Error('Domain parameter must be specified as a string.'); +} +{{#isSecure}} + {{#isSecureToken}} + this.token = (typeof options === 'object') ? (options.token ? options.token : {}) : {}; + {{/isSecureToken}} + {{#isSecureApiKey}} + this.apiKey = (typeof options === 'object') ? (options.apiKey ? options.apiKey : {}) : {}; + {{/isSecureApiKey}} + {{#isSecureBasic}} + this.basic = (typeof options === 'object') ? (options.basic ? options.basic : {}) : {}; + {{/isSecureBasic}} +{{/isSecure}} +} - function mergeQueryParams(parameters, queryParameters) { - if (parameters.$queryParameters) { - Object.keys(parameters.$queryParameters) - .forEach(function(parameterName) { - {{#isES6}}let{{/isES6}}{{^isES6}}var{{/isES6}} parameter = parameters.$queryParameters[parameterName]; - queryParameters[parameterName] = parameter; - }); - } - return queryParameters; - } +function mergeQueryParams(parameters, queryParameters) { +if (parameters.$queryParameters) { +Object.keys(parameters.$queryParameters) +.forEach(function(parameterName) { +{{#isES6}}let{{/isES6}}{{^isES6}}var{{/isES6}} parameter = parameters.$queryParameters[parameterName]; +queryParameters[parameterName] = parameter; +}); +} +return queryParameters; +} + +/** +* HTTP Request +* @method +* @name {{&className}}#request +* @param {string} method - http method +* @param {string} url - url to do request +* @param {object} parameters +* @param {object} body - body parameters / object +* @param {object} headers - header parameters +* @param {object} queryParameters - querystring parameters +* @param {object} form - form data object +* @param {object} deferred - promise object +*/ +{{&className}}.prototype.request = function(method, url, parameters, body, headers, queryParameters, form, deferred){ +{{#isES6}}let{{/isES6}}{{^isES6}}var{{/isES6}} req = { +method: method, +uri: url, +qs: queryParameters, +headers: headers, +body: body +}; +if(Object.keys(form).length > 0) { +req.form = form; +} +if(typeof(body) === 'object' && !(body instanceof Buffer)) { +req.json = true; +} +request(req, function(error, response, body){ +if(error) { +deferred.reject(error); +} else { +if(/^application\/(.*\\+)?json/.test(response.headers['content-type'])) { +try { +body = JSON.parse(body); +} catch(e) {} +} +if(response.statusCode === 204) { +deferred.resolve({ response: response }); +} else if(response.statusCode >= 200 && response.statusCode <= 299) { +deferred.resolve({ response: response, body: body }); +} else { +deferred.reject({ response: response, body: body }); +} +} +}); +}; +{{#isSecure}} + {{#isSecureToken}} /** - * HTTP Request - * @method - * @name {{&className}}#request - * @param {string} method - http method - * @param {string} url - url to do request - * @param {object} parameters - * @param {object} body - body parameters / object - * @param {object} headers - header parameters - * @param {object} queryParameters - querystring parameters - * @param {object} form - form data object - * @param {object} deferred - promise object - */ - {{&className}}.prototype.request = function(method, url, parameters, body, headers, queryParameters, form, deferred){ - {{#isES6}}let{{/isES6}}{{^isES6}}var{{/isES6}} req = { - method: method, - uri: url, - qs: queryParameters, - headers: headers, - body: body - }; - if(Object.keys(form).length > 0) { - req.form = form; - } - if(typeof(body) === 'object' && !(body instanceof Buffer)) { - req.json = true; - } - request(req, function(error, response, body){ - if(error) { - deferred.reject(error); - } else { - if(/^application\/(.*\\+)?json/.test(response.headers['content-type'])) { - try { - body = JSON.parse(body); - } catch(e) {} - } - if(response.statusCode === 204) { - deferred.resolve({ response: response }); - } else if(response.statusCode >= 200 && response.statusCode <= 299) { - deferred.resolve({ response: response, body: body }); - } else { - deferred.reject({ response: response, body: body }); - } - } - }); + * Set Token + * @method + * @name {{&className}}#setToken + * @param {string} value - token's value + * @param {string} headerOrQueryName - the header or query name to send the token at + * @param {boolean} isQuery - true if send the token as query param, otherwise, send as header param + */ + {{&className}}.prototype.setToken = function (value, headerOrQueryName, isQuery) { + this.token.value = value; + this.token.headerOrQueryName = headerOrQueryName; + this.token.isQuery = isQuery; }; + {{/isSecureToken}} + {{#isSecureApiKey}} + /** + * Set Api Key + * @method + * @name {{&className}}#setApiKey + * @param {string} value - apiKey's value + * @param {string} headerOrQueryName - the header or query name to send the apiKey at + * @param {boolean} isQuery - true if send the apiKey as query param, otherwise, send as header param + */ + {{&className}}.prototype.setApiKey = function (value, headerOrQueryName, isQuery) { + this.apiKey.value = value; + this.apiKey.headerOrQueryName = headerOrQueryName; + this.apiKey.isQuery = isQuery; + }; + {{/isSecureApiKey}} + {{#isSecureBasic}} + /** + * Set Basic Auth + * @method + * @name {{&className}}#setBasicAuth + * @param {string} username + * @param {string} password + */ + {{&className}}.prototype.setBasicAuth = function (username, password) { + this.basic.username = username; + this.basic.password = password; + }; + {{/isSecureBasic}} + /** + * Set Auth headers + * @method + * @name {{&className}}#setAuthHeaders + * @param {object} headerParams - headers object + */ + {{&className}}.prototype.setAuthHeaders = function (headerParams) { + {{#isES6}}let{{/isES6}}{{^isES6}}var{{/isES6}} headers = headerParams ? headerParams : {}; + {{#isSecureToken}} + if (!this.token.isQuery) { + if (this.token.headerOrQueryName) { + headers[this.token.headerOrQueryName] = this.token.value; + } else if (this.token.value) { + headers['Authorization'] = 'Bearer ' + this.token.value; + } + } + {{/isSecureToken}} + {{#isSecureApiKey}} + if (!this.apiKey.isQuery && this.apiKey.headerOrQueryName) { + headers[this.apiKey.headerOrQueryName] = this.apiKey.value; + } + {{/isSecureApiKey}} + {{#isSecureBasic}} + if (this.basic.username && this.basic.password) { + headers['Authorization'] = 'Basic ' + new Buffer(this.basic.username + ':' + this.basic.password).toString("base64"); + } + {{/isSecureBasic}} + return headers; + }; +{{/isSecure}} - {{#isSecure}} - {{#isSecureToken}} - /** - * Set Token - * @method - * @name {{&className}}#setToken - * @param {string} value - token's value - * @param {string} headerOrQueryName - the header or query name to send the token at - * @param {boolean} isQuery - true if send the token as query param, otherwise, send as header param - */ - {{&className}}.prototype.setToken = function (value, headerOrQueryName, isQuery) { - this.token.value = value; - this.token.headerOrQueryName = headerOrQueryName; - this.token.isQuery = isQuery; - }; - {{/isSecureToken}} - {{#isSecureApiKey}} - /** - * Set Api Key - * @method - * @name {{&className}}#setApiKey - * @param {string} value - apiKey's value - * @param {string} headerOrQueryName - the header or query name to send the apiKey at - * @param {boolean} isQuery - true if send the apiKey as query param, otherwise, send as header param - */ - {{&className}}.prototype.setApiKey = function (value, headerOrQueryName, isQuery) { - this.apiKey.value = value; - this.apiKey.headerOrQueryName = headerOrQueryName; - this.apiKey.isQuery = isQuery; - }; - {{/isSecureApiKey}} - {{#isSecureBasic}} - /** - * Set Basic Auth - * @method - * @name {{&className}}#setBasicAuth - * @param {string} username - * @param {string} password - */ - {{&className}}.prototype.setBasicAuth = function (username, password) { - this.basic.username = username; - this.basic.password = password; - }; - {{/isSecureBasic}} - /** - * Set Auth headers - * @method - * @name {{&className}}#setAuthHeaders - * @param {object} headerParams - headers object - */ - {{&className}}.prototype.setAuthHeaders = function (headerParams) { - {{#isES6}}let{{/isES6}}{{^isES6}}var{{/isES6}} headers = headerParams ? headerParams : {}; - {{#isSecureToken}} - if (!this.token.isQuery) { - if (this.token.headerOrQueryName) { - headers[this.token.headerOrQueryName] = this.token.value; - } else if (this.token.value) { - headers['Authorization'] = 'Bearer ' + this.token.value; - } - } - {{/isSecureToken}} - {{#isSecureApiKey}} - if (!this.apiKey.isQuery && this.apiKey.headerOrQueryName) { - headers[this.apiKey.headerOrQueryName] = this.apiKey.value; - } - {{/isSecureApiKey}} - {{#isSecureBasic}} - if (this.basic.username && this.basic.password) { - headers['Authorization'] = 'Basic ' + new Buffer(this.basic.username + ':' + this.basic.password).toString("base64"); - } - {{/isSecureBasic}} - return headers; - }; - {{/isSecure}} - - {{#methods}} - {{> method}} - {{/methods}} +{{#methods}} + {{> method}} +{{/methods}} - return {{&className}}; +return {{&className}}; })(); exports.{{&className}} = {{&className}}; From 6d129739dd3c4d0dbc50721a3a8ace782bb48d43 Mon Sep 17 00:00:00 2001 From: Peter Date: Thu, 14 Mar 2019 11:22:02 +0300 Subject: [PATCH 02/58] Add file creation --- .jshintignore | 1 + README.md | 40 +++++++++++----------------- lib/codegen.js | 9 ++++++- lib/splitter.js | 70 +++++++++++++++++++++++++++++++------------------ 4 files changed, 68 insertions(+), 52 deletions(-) diff --git a/.jshintignore b/.jshintignore index c7bd5c3c..ae3b5254 100644 --- a/.jshintignore +++ b/.jshintignore @@ -1 +1,2 @@ lib/splitter.js +zz/ diff --git a/README.md b/README.md index 1c4b856a..8d83748e 100644 --- a/README.md +++ b/README.md @@ -80,33 +80,23 @@ In addition to the common options listed below, `getCustomCode()` *requires* a ` description: swagger object ``` -If it is required to generate multiple files for Node (i. e. multiple methods based on the initial JSON) provide the following option: +If it is required to generate multiple files for Node (i. e. multiple methods based on the initial JSON) provide the following options: - multiple: true - -When this option is provided, the module will return an array of objects, that have the following structure: - - { content: , - file: , - functions: , - directory: , - isWrapper: } - -This structure describes a file, that contains generated REST APIs - -`content` - file contents, single string - -`file` - file name - -`functions` - array of function names - -`directory` - name of the directory, that should be created for this file - -`isWrapper` - will be **true** for wrapper file - -Resulting array should always contain a single wrapper file + multiple: + type: boolean + description: this option enables file splitting + path: + type: string + description: this option should contain the path to the project directory (__dirname) + example: '/Users/name/Projects/someProject/' + dir: + type: string + description: this option should contain the name of the directory with APIs + example: 'newAPIs' + +If `multiple` option is provided, `path` and `dir` options **are required** -**Please notice**: when using this option, the module returns a **promise** +The `dir` folder will be created and generated files will be placed inside of it ### Template Variables The following data are passed to the [mustache templates](https://github.com/janl/mustache.js): diff --git a/lib/codegen.js b/lib/codegen.js index db0c0b32..1369e3f6 100644 --- a/lib/codegen.js +++ b/lib/codegen.js @@ -271,6 +271,13 @@ var getViewForSwagger1 = function(opts, type){ * @returns {*} */ var getCode = function(opts, type) { + // check 'multiple' & all of the required parameters + if (opts.multiple) { + if (!(opts.dir && opts.path)) { + throw new Error('Missing some of the required parameters!'); + } + } + // For Swagger Specification version 2.0 value of field 'swagger' must be a string '2.0' var data = opts.swagger.swagger === '2.0' ? getViewForSwagger2(opts, type) : getViewForSwagger1(opts, type); @@ -328,7 +335,7 @@ var getCode = function(opts, type) { }); } if (opts.multiple) { - return splitter.split(source, opts.className); + return splitter.split(source, opts.className, opts.path, opts.dir); } if (opts.beautify === undefined || opts.beautify === true) { return beautify(source, { indent_size: 4, max_preserve_newlines: 2 }); diff --git a/lib/splitter.js b/lib/splitter.js index 8c751151..fedd1cf5 100644 --- a/lib/splitter.js +++ b/lib/splitter.js @@ -15,9 +15,15 @@ function capitalize(string) { * Split the code to create multiple files and directories * @param {string} source - source code generated by swagger-js-codegen (using special templates!) * @param {string} className - specified class name + * @param {string} path - path to the project directory, that calls the code generator (__dirname) + * @param {string} dir - name of the container directory * @returns {array} - array of objects (files and wrapper) */ -async function split(source, className) { +async function split(source, className, path, dir) { + // check if everything we need is there + if (!(source && className && path && dir)) { + throw new Error('Missing some of the required parameters!'); + } try { // convert ES5 to ES6 const { code, warnings } = transform(source, ['let', 'arrow', 'arrow-return', 'class']); @@ -27,17 +33,15 @@ async function split(source, className) { console.log('> codegen-splitter @ ES6 conversion warnings:\n', warnings); } - // create the source file TODO: better understanding of the file placement + // create the source file fs.writeFileSync(`${__dirname}/source.js`, code, (err) => { if (err) { - throw new Error(err); + throw new Error(err.message); } }); // load source file and get all of the available methods from the class - console.log('> various', `${__dirname}/source.js`, `${capitalize(className.toLowerCase())}`); const Instance = require(`${__dirname}/source.js`)[`${capitalize(className.toLowerCase())}`]; - console.log('> ins', Instance); const methods = Object.getOwnPropertyNames(Instance.prototype).filter(m => m !== 'constructor'); // abort everything if there are no methods (i. e. incorrect JSON or something went wrong) @@ -104,39 +108,53 @@ async function split(source, className) { };`, { indent_size: 2 }); }); - // delete the source file TODO: better understanding of the file placement + // delete the source file await fs.unlink(`${__dirname}/source.js`, (err) => { if (err) { - throw new Error(err); + throw new Error(err.message); + } + }); + + // make sure container directory exists + const container = `${path}/${dir}`; + if (!fs.existsSync(container)) { + await fs.mkdirSync(container); + } + + // create API directories and files + files.forEach((file) => { + // make sure API directory exists + if (!fs.existsSync(`${container}/${file.file}`)) { + fs.mkdirSync(`${container}/${file.file}`); } + // create file + fs.writeFileSync(`${container}/${file.file}/${file.file}.controller.js`, + beautify(file.content, { indent_size: 2 }), + (fErr) => { + if (fErr) { + throw new Error(fErr.message); + } + }); }); // create API wrapper TODO: what should be inside of the wrapper? What to export? - let wrapperContent = ''; + let wrapper = ''; files.forEach((file) => { const name = file.file; - wrapperContent = `${wrapperContent} + wrapper = `${wrapper} const ${name} = require('./${name}/${name}.controller.js');`; }); - const wrapper = { - content: beautify(wrapperContent, { indent_size: 2 }), - directory: null, - file: 'index.js', // TODO: wrapper name? - isWrapper: true, - }; - - // finalize and return - files.forEach((file, i) => { - files[i].directory = file.file; - files[i].file = `${file.file}.controller.js`; - files[i].isWrapper = false; - }); - files.push(wrapper); + fs.writeFileSync(`${container}/index.js`, + beautify(wrapper, { indent_size: 2 }), + (wErr) => { + if (wErr) { + throw new Error(wErr.message); + } + }); - return files; + return console.log('> swagger-js-codegen @ Success!'); } catch (err) { - console.log('> err', err); - throw new Error(err); + throw new Error(err.message); } } From 96e74a770c063fe2d68d4a67892ad340a7dc4a0d Mon Sep 17 00:00:00 2001 From: Peter Date: Thu, 14 Mar 2019 15:54:19 +0300 Subject: [PATCH 03/58] Formatter: default API responses --- .jshintignore | 1 + lib/codegen.js | 15 ++++++++++++--- lib/formatter.js | 43 +++++++++++++++++++++++++++++++++++++++++++ lib/splitter.js | 3 +++ 4 files changed, 59 insertions(+), 3 deletions(-) create mode 100644 lib/formatter.js diff --git a/.jshintignore b/.jshintignore index ae3b5254..a2e9bea9 100644 --- a/.jshintignore +++ b/.jshintignore @@ -1,2 +1,3 @@ lib/splitter.js +lib/formatter.js zz/ diff --git a/lib/codegen.js b/lib/codegen.js index 1369e3f6..7920a6e1 100644 --- a/lib/codegen.js +++ b/lib/codegen.js @@ -7,6 +7,7 @@ var lint = require('jshint').JSHINT; var _ = require('lodash'); var ts = require('./typescript'); +var formatter = require('./formatter'); var splitter = require('./splitter'); var normalizeName = function(id) { @@ -62,7 +63,6 @@ var getViewForSwagger2 = function(opts, type){ }); _.forEach(api, function(op, m) { var M = m.toUpperCase(); - // check if this is a supported method if (M === '' || authorizedMethods.indexOf(M) === -1) { return; } @@ -118,6 +118,9 @@ var getViewForSwagger2 = function(opts, type){ method.destination = op.tags[0].toLowerCase(); } + // add 'responses' field, that contains schemas and descriptions + method.responses = op.responses; + if(method.isSecure && method.isSecureToken) { data.isSecureToken = method.isSecureToken; } @@ -188,7 +191,7 @@ var getViewForSwagger2 = function(opts, type){ }); }); - _.forEach(swagger.definitions, function(definition, name){ + _.forEach(swagger.definitions, function(definition, name) { data.definitions.push({ name: name, description: definition.description, @@ -281,6 +284,9 @@ var getCode = function(opts, type) { // For Swagger Specification version 2.0 value of field 'swagger' must be a string '2.0' var data = opts.swagger.swagger === '2.0' ? getViewForSwagger2(opts, type) : getViewForSwagger1(opts, type); + // format the default responses for the APIs TODO: where to place it? + formatter.format(data); + if (type === 'custom') { if (!_.isObject(opts.template) || !_.isString(opts.template.class) || !_.isString(opts.template.method)) { throw new Error('Unprovided custom template. Please use the following template: template: { class: "...", method: "...", request: "..." }'); @@ -335,7 +341,10 @@ var getCode = function(opts, type) { }); } if (opts.multiple) { - return splitter.split(source, opts.className, opts.path, opts.dir); + return splitter.split(beautify(source, { + indent_size: 2, + max_preserve_newlines: 2 + }), opts.className, opts.path, opts.dir); } if (opts.beautify === undefined || opts.beautify === true) { return beautify(source, { indent_size: 4, max_preserve_newlines: 2 }); diff --git a/lib/formatter.js b/lib/formatter.js new file mode 100644 index 00000000..07497506 --- /dev/null +++ b/lib/formatter.js @@ -0,0 +1,43 @@ +const _ = require('lodash'); + +/** + * Format the responses for the APIs + * @param data - initial data, that should have all of the necessary methods and schemas + * @returns {object} + */ +async function format(data) { + const { methods, definitions } = data; + + // check if there are none + if (!(methods && methods.length > 0 && definitions && definitions.length > 0)) { + throw new Error('Methods and definitions should not be empty!'); + } + + const mutable = _.cloneDeep(data); + + // process methods + methods.forEach((method, i) => { + const list = Object.keys(method.responses); + const formatted = {}; + if (list.length > 0) { + list.forEach((response) => { + formatted[response] = method.responses[response]; + const refName = formatted[response].schema['$ref'].split('/').slice(-1)[0]; + definitions.forEach((definition) => { + if (refName === definition.name) { + formatted[response].properties = definition.tsType.properties; + } + }); + }); + } + console.log('> ', formatted); + }); + + // console.log('> definitions', definitions); + + return mutable; +} + +module.exports = { + format, +}; diff --git a/lib/splitter.js b/lib/splitter.js index fedd1cf5..430c8725 100644 --- a/lib/splitter.js +++ b/lib/splitter.js @@ -115,6 +115,9 @@ async function split(source, className, path, dir) { } }); + // TODO: remove this ASAP + // return; + // make sure container directory exists const container = `${path}/${dir}`; if (!fs.existsSync(container)) { From 774e7c5cb1a4f8774d31dcdf7ab72b1d95542c74 Mon Sep 17 00:00:00 2001 From: Peter Date: Fri, 15 Mar 2019 11:30:20 +0300 Subject: [PATCH 04/58] Default API responses --- lib/codegen.js | 6 +++--- lib/formatter.js | 27 ++++++++++++++++++++++----- lib/splitter.js | 3 --- templates/multi-method.mustache | 4 +++- 4 files changed, 28 insertions(+), 12 deletions(-) diff --git a/lib/codegen.js b/lib/codegen.js index 7920a6e1..1d58ee45 100644 --- a/lib/codegen.js +++ b/lib/codegen.js @@ -282,10 +282,10 @@ var getCode = function(opts, type) { } // For Swagger Specification version 2.0 value of field 'swagger' must be a string '2.0' - var data = opts.swagger.swagger === '2.0' ? getViewForSwagger2(opts, type) : getViewForSwagger1(opts, type); + var swaggerView = opts.swagger.swagger === '2.0' ? getViewForSwagger2(opts, type) : getViewForSwagger1(opts, type); - // format the default responses for the APIs TODO: where to place it? - formatter.format(data); + // format the default responses for the APIs + var data = formatter.format(swaggerView); if (type === 'custom') { if (!_.isObject(opts.template) || !_.isString(opts.template.class) || !_.isString(opts.template.method)) { diff --git a/lib/formatter.js b/lib/formatter.js index 07497506..90658b54 100644 --- a/lib/formatter.js +++ b/lib/formatter.js @@ -5,7 +5,7 @@ const _ = require('lodash'); * @param data - initial data, that should have all of the necessary methods and schemas * @returns {object} */ -async function format(data) { +function format(data) { const { methods, definitions } = data; // check if there are none @@ -15,7 +15,7 @@ async function format(data) { const mutable = _.cloneDeep(data); - // process methods + // get definitions based on $ref methods.forEach((method, i) => { const list = Object.keys(method.responses); const formatted = {}; @@ -26,14 +26,31 @@ async function format(data) { definitions.forEach((definition) => { if (refName === definition.name) { formatted[response].properties = definition.tsType.properties; + formatted[response].status = Number(response) || null; } }); }); } - console.log('> ', formatted); - }); - // console.log('> definitions', definitions); + // generate the code + list.forEach((response) => { + let code = `return res.code(${formatted[response].status}).send({`; + formatted[response].properties.forEach((property, i) => { + // remove the whitespaces from the parameter names + const name = property.name.replace(/\s/g, ''); + formatted[response].properties[i].name = name; + // escape the quoutes + const value = property.tsType === 'string' ? '"string"' : 0; + code = `${code} + ${name}: ${value},`; + }); + formatted[response].code = `${code} + });`; + }); + + // add the code to the resulting object + mutable.methods[i].responses = formatted; + }); return mutable; } diff --git a/lib/splitter.js b/lib/splitter.js index 430c8725..fedd1cf5 100644 --- a/lib/splitter.js +++ b/lib/splitter.js @@ -115,9 +115,6 @@ async function split(source, className, path, dir) { } }); - // TODO: remove this ASAP - // return; - // make sure container directory exists const container = `${path}/${dir}`; if (!fs.existsSync(container)) { diff --git a/templates/multi-method.mustache b/templates/multi-method.mustache index f41dd704..6b1e0bfc 100644 --- a/templates/multi-method.mustache +++ b/templates/multi-method.mustache @@ -55,5 +55,7 @@ Method logic */ - return res.code(200).send({ info: 'OK' }); + {{#responses}} + {{#200}}{{{code}}}{{/200}} + {{/responses}} }; From d4cbe15674fe11d42fadce17b092e28f986d1bbd Mon Sep 17 00:00:00 2001 From: Peter Date: Mon, 18 Mar 2019 17:45:13 +0300 Subject: [PATCH 05/58] Classes, x-AuthFieldType --- lib/formatter.js | 10 ++++-- lib/splitter.js | 21 ++++++++---- lib/typescript.js | 4 +++ templates/multi-method.mustache | 61 ++++++++++----------------------- 4 files changed, 46 insertions(+), 50 deletions(-) diff --git a/lib/formatter.js b/lib/formatter.js index 90658b54..cc90a7b6 100644 --- a/lib/formatter.js +++ b/lib/formatter.js @@ -24,6 +24,12 @@ function format(data) { formatted[response] = method.responses[response]; const refName = formatted[response].schema['$ref'].split('/').slice(-1)[0]; definitions.forEach((definition) => { + // TODO: pass the 'x-AuthFieldType' to the method + definition.tsType.properties.forEach((property) => { + + }); + + // copy properties if (refName === definition.name) { formatted[response].properties = definition.tsType.properties; formatted[response].status = Number(response) || null; @@ -39,8 +45,8 @@ function format(data) { // remove the whitespaces from the parameter names const name = property.name.replace(/\s/g, ''); formatted[response].properties[i].name = name; - // escape the quoutes - const value = property.tsType === 'string' ? '"string"' : 0; + // escape the quotes + const value = property.tsType === 'string' ? '\'string\'' : 0; code = `${code} ${name}: ${value},`; }); diff --git a/lib/splitter.js b/lib/splitter.js index fedd1cf5..3a3d68a8 100644 --- a/lib/splitter.js +++ b/lib/splitter.js @@ -24,6 +24,7 @@ async function split(source, className, path, dir) { if (!(source && className && path && dir)) { throw new Error('Missing some of the required parameters!'); } + try { // convert ES5 to ES6 const { code, warnings } = transform(source, ['let', 'arrow', 'arrow-return', 'class']); @@ -59,7 +60,8 @@ async function split(source, className, path, dir) { let functionName = standalone.split('(req, res)')[0]; const destination = functionName.split('_')[0]; functionName = functionName.split('_')[1]; - standalone = `async function ${functionName}(req, res) ${standalone.split('(req, res)')[1]}`; + standalone = `async ${functionName}(req, res) ${standalone.split('(req, res)')[1]}`; + standalone = standalone.split('await_FieldValidator').join('await FieldValidator'); destinations.push({ content: standalone, file: destination, @@ -101,11 +103,18 @@ async function split(source, className, path, dir) { files[i].content = beautify(`'use strict'; /* auto-generated: ${file.file}.controller.js */ - ${file.content} - - module.exports = { - ${fList} - };`, { indent_size: 2 }); + + class ${file.file} { + constructor(req, res, params) { + this.req = req; + this.res = res; + this.params = params; + } + ${file.content} + } + + module.exports = (req, res, params) => ${file.file}(req, res, params); + `, { indent_size: 2 }); }); // delete the source file diff --git a/lib/typescript.js b/lib/typescript.js index b6c5a844..821913cd 100644 --- a/lib/typescript.js +++ b/lib/typescript.js @@ -81,6 +81,10 @@ function convertType(swaggerType, swagger) { typespec.isArray = typespec.tsType === 'array'; typespec.isAtomic = typespec.isAtomic || _.includes(['string', 'number', 'boolean', 'any'], typespec.tsType); + if (swaggerType['x-AuthFieldType']) { + typespec['x-AuthFieldType'] = swaggerType['x-AuthFieldType']; + } + return typespec; } diff --git a/templates/multi-method.mustache b/templates/multi-method.mustache index 6b1e0bfc..a50d74ad 100644 --- a/templates/multi-method.mustache +++ b/templates/multi-method.mustache @@ -7,48 +7,25 @@ */ {{&className}}.prototype.{{&destination}}_{{&methodName}} = function(req, res) { {{#parameters}} - {{#isQueryParameter}} - // check if query parameter is passed - if (!req.query['{{=<% %>=}}<%&name%><%={{ }}=%>']) { - return res.code(400).send({ status: 400, info: 'MISSING_QUERY_PARAMETER', value: '{{=<% %>=}}<%&name%><%={{ }}=%>', }); - } - {{/isQueryParameter}} - - {{#isPathParameter}} - // check if path parameter is passed - if (!req.params['{{=<% %>=}}<%&name%><%={{ }}=%>']) { - return res.code(400).send({ status: 400, info: 'MISSING_PATH_PARAMETER', value: '{{=<% %>=}}<%&name%><%={{ }}=%>', }); - } - {{/isPathParameter}} - - {{#isBodyParameter}} - // check if body parameter is passed - if (!req.body['{{=<% %>=}}<%&name%><%={{ }}=%>']) { - return res.code(400).send({ status: 400, info: 'MISSING_BODY_PARAMETER', value: '{{=<% %>=}}<%&name%><%={{ }}=%>', }); - } - {{/isBodyParameter}} - - {{#isHeaderParameter}} - {{#isSingleton}} - headers['{{&name}}'] = '{{&singleton}}'; - {{/isSingleton}} - {{^isSingleton}} - if(parameters['{{&camelCaseName}}'] !== undefined){ - headers['{{&name}}'] = parameters['{{&camelCaseName}}']; - } - {{/isSingleton}} - {{/isHeaderParameter}} - - {{#isFormParameter}} - {{#isSingleton}} - form['{{&name}}'] = '{{&singleton}}'; - {{/isSingleton}} - {{^isSingleton}} - if(parameters['{{&camelCaseName}}'] !== undefined){ - form['{{&name}}'] = parameters['{{&camelCaseName}}']; - } - {{/isSingleton}} - {{/isFormParameter}} + {{#x-AuthFieldType}} + {{#isQueryParameter}} + await_FieldValidator.validate('{{&x-AuthFieldType}}', req.query['{{=<% %>=}}<%&name%><%={{ }}=%>'], req, res); + {{/isQueryParameter}} + {{#isPathParameter}} + await_FieldValidator.validate('{{&x-AuthFieldType}}', req.params['{{=<% %>=}}<%&name%><%={{ }}=%>'], req, res); + {{/isPathParameter}} + {{#isBodyParameter}} + await_FieldValidator.validate('{{&x-AuthFieldType}}', req.body['{{=<% %>=}}<%&name%><%={{ }}=%>'], req, res); + {{/isBodyParameter}} + {{#isHeaderParameter}} + {{#isSingleton}}{{/isSingleton}} + {{^isSingleton}}{{/isSingleton}} + {{/isHeaderParameter}} + {{#isFormParameter}} + {{#isSingleton}}{{/isSingleton}} + {{^isSingleton}}{{/isSingleton}} + {{/isFormParameter}} + {{/x-AuthFieldType}} {{/parameters}} /* From 70d1c35368a20ea8b634975e75fe8a353008fcd5 Mon Sep 17 00:00:00 2001 From: Peter Date: Tue, 19 Mar 2019 11:08:41 +0300 Subject: [PATCH 06/58] Update x-AuthFieldType check in controllers, update templates --- lib/formatter.js | 28 ++++++++++++++++++---- lib/splitter.js | 15 ------------ templates/multi-method.mustache | 42 ++++++++++++++++++--------------- 3 files changed, 46 insertions(+), 39 deletions(-) diff --git a/lib/formatter.js b/lib/formatter.js index cc90a7b6..9d44d95e 100644 --- a/lib/formatter.js +++ b/lib/formatter.js @@ -15,6 +15,20 @@ function format(data) { const mutable = _.cloneDeep(data); + // locate all of the protected properties + const secure = []; + definitions.forEach((definition) => { + definition.tsType.properties.forEach((property) => { + if (property['x-AuthFieldType']) { + secure.push({ + parameterName: definition.name, + propertyName: property.name, + value: property['x-AuthFieldType'], + }); + } + }); + }); + // get definitions based on $ref methods.forEach((method, i) => { const list = Object.keys(method.responses); @@ -24,11 +38,6 @@ function format(data) { formatted[response] = method.responses[response]; const refName = formatted[response].schema['$ref'].split('/').slice(-1)[0]; definitions.forEach((definition) => { - // TODO: pass the 'x-AuthFieldType' to the method - definition.tsType.properties.forEach((property) => { - - }); - // copy properties if (refName === definition.name) { formatted[response].properties = definition.tsType.properties; @@ -38,6 +47,15 @@ function format(data) { }); } + // pass protected properties to the parameters + secure.forEach((property, j) => { + method.parameters.forEach((parameter, k) => { + if (parameter.name === property.parameterName) { + mutable.methods[i].parameters[k]['x-AuthFieldType'] = secure[j]; + } + }); + }); + // generate the code list.forEach((response) => { let code = `return res.code(${formatted[response].status}).send({`; diff --git a/lib/splitter.js b/lib/splitter.js index 3a3d68a8..ef78f100 100644 --- a/lib/splitter.js +++ b/lib/splitter.js @@ -146,21 +146,6 @@ async function split(source, className, path, dir) { }); }); - // create API wrapper TODO: what should be inside of the wrapper? What to export? - let wrapper = ''; - files.forEach((file) => { - const name = file.file; - wrapper = `${wrapper} - const ${name} = require('./${name}/${name}.controller.js');`; - }); - fs.writeFileSync(`${container}/index.js`, - beautify(wrapper, { indent_size: 2 }), - (wErr) => { - if (wErr) { - throw new Error(wErr.message); - } - }); - return console.log('> swagger-js-codegen @ Success!'); } catch (err) { throw new Error(err.message); diff --git a/templates/multi-method.mustache b/templates/multi-method.mustache index a50d74ad..8c1e944c 100644 --- a/templates/multi-method.mustache +++ b/templates/multi-method.mustache @@ -7,25 +7,29 @@ */ {{&className}}.prototype.{{&destination}}_{{&methodName}} = function(req, res) { {{#parameters}} - {{#x-AuthFieldType}} - {{#isQueryParameter}} - await_FieldValidator.validate('{{&x-AuthFieldType}}', req.query['{{=<% %>=}}<%&name%><%={{ }}=%>'], req, res); - {{/isQueryParameter}} - {{#isPathParameter}} - await_FieldValidator.validate('{{&x-AuthFieldType}}', req.params['{{=<% %>=}}<%&name%><%={{ }}=%>'], req, res); - {{/isPathParameter}} - {{#isBodyParameter}} - await_FieldValidator.validate('{{&x-AuthFieldType}}', req.body['{{=<% %>=}}<%&name%><%={{ }}=%>'], req, res); - {{/isBodyParameter}} - {{#isHeaderParameter}} - {{#isSingleton}}{{/isSingleton}} - {{^isSingleton}}{{/isSingleton}} - {{/isHeaderParameter}} - {{#isFormParameter}} - {{#isSingleton}}{{/isSingleton}} - {{^isSingleton}}{{/isSingleton}} - {{/isFormParameter}} - {{/x-AuthFieldType}} + {{#isQueryParameter}} + {{#x-AuthFieldType}} + await_FieldValidator.validate('{{&x-AuthFieldType.value}}', req.query['{{=<% %>=}}<%&name%><%={{ }}=%>'], req, res); + {{/x-AuthFieldType}} + {{/isQueryParameter}} + {{#isPathParameter}} + {{#x-AuthFieldType}} + await_FieldValidator.validate('{{&x-AuthFieldType.value}}', req.params['{{=<% %>=}}<%&name%><%={{ }}=%>'], req, res); + {{/x-AuthFieldType}} + {{/isPathParameter}} + {{#isBodyParameter}} + {{#x-AuthFieldType}} + await_FieldValidator.validate('{{&x-AuthFieldType.value}}', req.body['{{=<% %>=}}<%&name%><%={{ }}=%>'], req, res); + {{/x-AuthFieldType}} + {{/isBodyParameter}} + {{#isHeaderParameter}} + {{#isSingleton}}{{/isSingleton}} + {{^isSingleton}}{{/isSingleton}} + {{/isHeaderParameter}} + {{#isFormParameter}} + {{#isSingleton}}{{/isSingleton}} + {{^isSingleton}}{{/isSingleton}} + {{/isFormParameter}} {{/parameters}} /* From 01bebda57d437e5a3d4fd17346d2588644401606 Mon Sep 17 00:00:00 2001 From: Peter Date: Tue, 19 Mar 2019 12:38:17 +0300 Subject: [PATCH 07/58] Load definitions to the global scope --- .jshintignore | 3 ++- lib/codegen.js | 6 +++++- lib/expose.js | 8 ++++++++ 3 files changed, 15 insertions(+), 2 deletions(-) create mode 100644 lib/expose.js diff --git a/.jshintignore b/.jshintignore index a2e9bea9..3e5791b3 100644 --- a/.jshintignore +++ b/.jshintignore @@ -1,3 +1,4 @@ -lib/splitter.js +lib/expose.js lib/formatter.js +lib/splitter.js zz/ diff --git a/lib/codegen.js b/lib/codegen.js index 1d58ee45..b180a2dc 100644 --- a/lib/codegen.js +++ b/lib/codegen.js @@ -6,9 +6,10 @@ var beautify = require('js-beautify').js_beautify; var lint = require('jshint').JSHINT; var _ = require('lodash'); -var ts = require('./typescript'); +var expose = require('./expose'); var formatter = require('./formatter'); var splitter = require('./splitter'); +var ts = require('./typescript'); var normalizeName = function(id) { return id.replace(/\.|\-|\{|\}|\s/g, '_'); @@ -284,6 +285,9 @@ var getCode = function(opts, type) { // For Swagger Specification version 2.0 value of field 'swagger' must be a string '2.0' var swaggerView = opts.swagger.swagger === '2.0' ? getViewForSwagger2(opts, type) : getViewForSwagger1(opts, type); + // expose definitions @ global scope + expose(opts.swagger.definitions); + // format the default responses for the APIs var data = formatter.format(swaggerView); diff --git a/lib/expose.js b/lib/expose.js new file mode 100644 index 00000000..d9375e3e --- /dev/null +++ b/lib/expose.js @@ -0,0 +1,8 @@ +function expose(definitions) { + const list = Object.keys(definitions); + list.forEach((definition) => { + global[definition] = definitions[definition]; + }); +} + +module.exports = expose; From bf59213d9093bbfbd59ed50d697e418e698d75ad Mon Sep 17 00:00:00 2001 From: Peter Date: Tue, 19 Mar 2019 17:16:11 +0300 Subject: [PATCH 08/58] Definitions files generation --- lib/codegen.js | 6 +-- lib/expose.js | 122 +++++++++++++++++++++++++++++++++++++++++++++++- lib/splitter.js | 7 +-- 3 files changed, 125 insertions(+), 10 deletions(-) diff --git a/lib/codegen.js b/lib/codegen.js index b180a2dc..542258b5 100644 --- a/lib/codegen.js +++ b/lib/codegen.js @@ -285,12 +285,12 @@ var getCode = function(opts, type) { // For Swagger Specification version 2.0 value of field 'swagger' must be a string '2.0' var swaggerView = opts.swagger.swagger === '2.0' ? getViewForSwagger2(opts, type) : getViewForSwagger1(opts, type); - // expose definitions @ global scope - expose(opts.swagger.definitions); - // format the default responses for the APIs var data = formatter.format(swaggerView); + // expose definitions @ global scope + expose(opts.swagger.definitions, data.methods, opts.path); + if (type === 'custom') { if (!_.isObject(opts.template) || !_.isString(opts.template.class) || !_.isString(opts.template.method)) { throw new Error('Unprovided custom template. Please use the following template: template: { class: "...", method: "...", request: "..." }'); diff --git a/lib/expose.js b/lib/expose.js index d9375e3e..441e1339 100644 --- a/lib/expose.js +++ b/lib/expose.js @@ -1,7 +1,125 @@ -function expose(definitions) { +const beauty = require('js-beautify').js; +const fs = require('fs'); +const { inspect } = require('util'); + +/** + * Expose definitions objects, create files with objects + * @param {object} definitions - object that contain definitions objects + * @param {array} methods - array of the available methods + * @param {string} path - where to generate the files, resulting path will be path/definitions + */ +function expose(definitions, methods, path) { + // get list of the definitions const list = Object.keys(definitions); - list.forEach((definition) => { + + // make sure that /definitions directory exists + const container = `${path}/definitions`; + if (!fs.existsSync(container)) { + fs.mkdirSync(container); + } + + // process definitions + list.forEach(async (definition) => { + // expose definitions objects global[definition] = definitions[definition]; + + // make sure that destination definition directory exists + const destination = `${container}/${definition}`; + if (!fs.existsSync(destination)) { + fs.mkdirSync(destination); + } + + // bind the parameters + let parameters = ''; + methods.forEach((method) => { + method.parameters.forEach((parameter) => { + if (parameter.name === definition) { + let origin = parameter['in']; + if (origin === 'path') origin = 'params'; + parameters = `${parameters} + this.${parameter.name} = req.${origin}['${parameter.name}'];` + } + }) + }); + + // check x-AuthFieldType field + const secure = []; + if (!(definitions[definition].properties instanceof Array)) { + const properties = Object.keys(definitions[definition].properties); + properties.forEach((property) => { + if (definitions[definition].properties[property]['x-AuthFieldType']) { + methods.forEach((method) => { + method.parameters.forEach((parameter) => { + if (parameter.name === definition) { + secure.push({ + type: parameter['in'], + definition, + property, + value: definitions[definition].properties[property]['x-AuthFieldType'], + }); + } + }) + }); + } + }); + } else { + definitions[definition].properties.forEach((property, i) => { + if (property['x-AuthFieldType']) { + methods.forEach((method) => { + method.parameters.forEach((parameter) => { + if (parameter.name === definition) { + secure.push({ + type: parameter['in'], + definition, + property, + value: property['x-AuthFieldType'], + }); + } + }) + }); + } + }); + } + + // create validation method + let validation = ''; + if (secure.length > 0) { + validation = `async validation(req, res, params) {`; + secure.forEach((property) => { + let origin = property.type; + if (origin === 'path') origin = 'params'; + validation = `${validation} + await FieldValidator.validate('${property.value}', req.${origin}['${property.definition}']['${property.property}'], req, res); + `; + }); + validation = `${validation} + }`; + } + + // compile the file + const content = `/* auto-generated: ${definition}.js */ + + module.exports = class ${definition} { + constructor(req, res, params) { + this.req = req; + this.res = res; + this.params = params; + ${parameters} + } + + ${validation} + + ${definition} = ${inspect(definitions[definition], { showHidden: false, depth: null })}; + };`; + + // create file in the destination folder + fs.writeFileSync(`${destination}/${definition}.js`, + beauty(content, { indent_size: 2 }), + (err) => { + if (err) { + throw new Error(err.message); + } + }); }); } diff --git a/lib/splitter.js b/lib/splitter.js index ef78f100..a49f680e 100644 --- a/lib/splitter.js +++ b/lib/splitter.js @@ -104,17 +104,14 @@ async function split(source, className, path, dir) { /* auto-generated: ${file.file}.controller.js */ - class ${file.file} { + module.exports = class ${file.file} { constructor(req, res, params) { this.req = req; this.res = res; this.params = params; } ${file.content} - } - - module.exports = (req, res, params) => ${file.file}(req, res, params); - `, { indent_size: 2 }); + }`, { indent_size: 2 }); }); // delete the source file From 71c0b15365450710555aa0cd9634c415efc99a61 Mon Sep 17 00:00:00 2001 From: Peter Date: Wed, 20 Mar 2019 09:55:11 +0300 Subject: [PATCH 09/58] x-AuthFieldType parameter validation fix --- lib/expose.js | 2 +- lib/splitter.js | 2 +- templates/multi-method.mustache | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/expose.js b/lib/expose.js index 441e1339..64d6ed85 100644 --- a/lib/expose.js +++ b/lib/expose.js @@ -58,7 +58,7 @@ function expose(definitions, methods, path) { value: definitions[definition].properties[property]['x-AuthFieldType'], }); } - }) + }); }); } }); diff --git a/lib/splitter.js b/lib/splitter.js index a49f680e..5fcbc079 100644 --- a/lib/splitter.js +++ b/lib/splitter.js @@ -111,7 +111,7 @@ async function split(source, className, path, dir) { this.params = params; } ${file.content} - }`, { indent_size: 2 }); + };`, { indent_size: 2 }); }); // delete the source file diff --git a/templates/multi-method.mustache b/templates/multi-method.mustache index 8c1e944c..9b3cbb3d 100644 --- a/templates/multi-method.mustache +++ b/templates/multi-method.mustache @@ -9,17 +9,17 @@ {{#parameters}} {{#isQueryParameter}} {{#x-AuthFieldType}} - await_FieldValidator.validate('{{&x-AuthFieldType.value}}', req.query['{{=<% %>=}}<%&name%><%={{ }}=%>'], req, res); + await_FieldValidator.validate('{{&x-AuthFieldType.value}}', req.query['{{&x-AuthFieldType.parameterName}}']['{{&x-AuthFieldType.propertyName}}'], req, res); {{/x-AuthFieldType}} {{/isQueryParameter}} {{#isPathParameter}} {{#x-AuthFieldType}} - await_FieldValidator.validate('{{&x-AuthFieldType.value}}', req.params['{{=<% %>=}}<%&name%><%={{ }}=%>'], req, res); + await_FieldValidator.validate('{{&x-AuthFieldType.value}}', req.params['{{&x-AuthFieldType.parameterName}}']['{{&x-AuthFieldType.propertyName}}'], req, res); {{/x-AuthFieldType}} {{/isPathParameter}} {{#isBodyParameter}} {{#x-AuthFieldType}} - await_FieldValidator.validate('{{&x-AuthFieldType.value}}', req.body['{{=<% %>=}}<%&name%><%={{ }}=%>'], req, res); + await_FieldValidator.validate('{{&x-AuthFieldType.value}}', req.body['{{&x-AuthFieldType.parameterName}}']['{{&x-AuthFieldType.propertyName}}'], req, res); {{/x-AuthFieldType}} {{/isBodyParameter}} {{#isHeaderParameter}} From f10b02b2c12f4bf877509755a820ae08330ff2e7 Mon Sep 17 00:00:00 2001 From: Peter Date: Wed, 20 Mar 2019 17:41:21 +0300 Subject: [PATCH 10/58] Bindings inside of the controllers, global availability --- lib/codegen.js | 2 +- lib/expose.js | 204 +++++++++++++++++++-------------- lib/formatter.js | 127 +++++++++++--------- lib/splitter.js | 32 +++++- package-lock.json | 5 + package.json | 1 + templates/multi-class.mustache | 9 ++ 7 files changed, 237 insertions(+), 143 deletions(-) diff --git a/lib/codegen.js b/lib/codegen.js index 542258b5..7f49d5df 100644 --- a/lib/codegen.js +++ b/lib/codegen.js @@ -285,7 +285,7 @@ var getCode = function(opts, type) { // For Swagger Specification version 2.0 value of field 'swagger' must be a string '2.0' var swaggerView = opts.swagger.swagger === '2.0' ? getViewForSwagger2(opts, type) : getViewForSwagger1(opts, type); - // format the default responses for the APIs + // format the default responses for the APIs, add objects for the load var data = formatter.format(swaggerView); // expose definitions @ global scope diff --git a/lib/expose.js b/lib/expose.js index 64d6ed85..0e77d8e1 100644 --- a/lib/expose.js +++ b/lib/expose.js @@ -1,6 +1,7 @@ const beauty = require('js-beautify').js; const fs = require('fs'); const { inspect } = require('util'); +const RFS = require('require-from-string'); /** * Expose definitions objects, create files with objects @@ -9,95 +10,98 @@ const { inspect } = require('util'); * @param {string} path - where to generate the files, resulting path will be path/definitions */ function expose(definitions, methods, path) { - // get list of the definitions - const list = Object.keys(definitions); + try { + // get list of the definitions + const list = Object.keys(definitions); - // make sure that /definitions directory exists - const container = `${path}/definitions`; - if (!fs.existsSync(container)) { - fs.mkdirSync(container); - } - - // process definitions - list.forEach(async (definition) => { - // expose definitions objects - global[definition] = definitions[definition]; + // do not proceed if there are no definitions + if (list.length === 0) { + return console.log('> swagger-js-codegen @ No objects to expose!'); + } - // make sure that destination definition directory exists - const destination = `${container}/${definition}`; - if (!fs.existsSync(destination)) { - fs.mkdirSync(destination); + // make sure that ~/definitions directory exists + const container = `${path}/definitions`; + if (!fs.existsSync(container)) { + fs.mkdirSync(container); } - // bind the parameters - let parameters = ''; - methods.forEach((method) => { - method.parameters.forEach((parameter) => { - if (parameter.name === definition) { - let origin = parameter['in']; - if (origin === 'path') origin = 'params'; - parameters = `${parameters} + // process definitions + list.forEach(async (definition) => { + // expose definitions objects to the global scope TODO: resolve the issue with global availability + global['objects'] = {}; + // global.objects[definition] = definitions[definition]; + + Object.assign(global.objects, definitions[definition]); + + // bind the parameters + let parameters = ''; + methods.forEach((method) => { + method.parameters.forEach((parameter) => { + if (parameter.name === definition) { + let origin = parameter['in']; + if (origin === 'path') origin = 'params'; + parameters = `${parameters} this.${parameter.name} = req.${origin}['${parameter.name}'];` - } - }) - }); + } + }) + }); - // check x-AuthFieldType field - const secure = []; - if (!(definitions[definition].properties instanceof Array)) { - const properties = Object.keys(definitions[definition].properties); - properties.forEach((property) => { - if (definitions[definition].properties[property]['x-AuthFieldType']) { - methods.forEach((method) => { - method.parameters.forEach((parameter) => { - if (parameter.name === definition) { - secure.push({ - type: parameter['in'], - definition, - property, - value: definitions[definition].properties[property]['x-AuthFieldType'], - }); - } + // check x-AuthFieldType field + const secure = []; + if (!(definitions[definition].properties instanceof Array)) { + const properties = Object.keys(definitions[definition].properties); + properties.forEach((property) => { + if (definitions[definition].properties[property]['x-AuthFieldType']) { + methods.forEach((method) => { + method.parameters.forEach((parameter) => { + if (parameter.name === definition) { + secure.push({ + type: parameter['in'], + definition, + property, + value: definitions[definition].properties[property]['x-AuthFieldType'], + }); + } + }); }); - }); - } - }); - } else { - definitions[definition].properties.forEach((property, i) => { - if (property['x-AuthFieldType']) { - methods.forEach((method) => { - method.parameters.forEach((parameter) => { - if (parameter.name === definition) { - secure.push({ - type: parameter['in'], - definition, - property, - value: property['x-AuthFieldType'], - }); - } - }) - }); - } - }); - } + } + }); + } else { + definitions[definition].properties.forEach((property, i) => { + if (property['x-AuthFieldType']) { + methods.forEach((method) => { + method.parameters.forEach((parameter) => { + if (parameter.name === definition) { + secure.push({ + type: parameter['in'], + definition, + property, + value: property['x-AuthFieldType'], + }); + } + }) + }); + } + }); + } - // create validation method - let validation = ''; - if (secure.length > 0) { - validation = `async validation(req, res, params) {`; - secure.forEach((property) => { - let origin = property.type; - if (origin === 'path') origin = 'params'; - validation = `${validation} + // create validation method + let validation = ''; + if (secure.length > 0) { + validation = `async validation(req, res, params) {`; + secure.forEach((property) => { + let origin = property.type; + if (origin === 'path') origin = 'params'; + validation = `${validation} await FieldValidator.validate('${property.value}', req.${origin}['${property.definition}']['${property.property}'], req, res); `; - }); - validation = `${validation} + }); + validation = `${validation} }`; - } + } - // compile the file - const content = `/* auto-generated: ${definition}.js */ + // compile the file + const content = `/* auto-generated: ${definition}.js */ module.exports = class ${definition} { constructor(req, res, params) { @@ -112,15 +116,45 @@ function expose(definitions, methods, path) { ${definition} = ${inspect(definitions[definition], { showHidden: false, depth: null })}; };`; - // create file in the destination folder - fs.writeFileSync(`${destination}/${definition}.js`, - beauty(content, { indent_size: 2 }), - (err) => { - if (err) { - throw new Error(err.message); - } + // make sure that destination definition directory exists + const destination = `${container}/${definition}`; + if (!fs.existsSync(destination)) { + fs.mkdirSync(destination); + } + + // create file in the destination folder + fs.writeFileSync(`${destination}/${definition}.js`, + beauty(content, { indent_size: 2 }), + (err) => { + if (err) { + throw new Error(err.message || err); + } + }); + }); + + // load classes to the global scope TODO: resolve the issue with global availability + fs.readdir(container, (err, dirs) => { + if (err) { + throw new Error(err.message || err); + } + + dirs.forEach((dir) => { + fs.readFile(`${container}/${dir}/${dir}.js`, (err, data) => { + if (err) { + throw new Error(err.message || err); + } + + // + const Instance = RFS(data); + const Example = new Instance(); + global['classes'] = {}; + Object.assign(global.classes, { [dir]: Example }); + }); }); - }); + }); + } catch (err) { + throw new Error(err.message || err); + } } module.exports = expose; diff --git a/lib/formatter.js b/lib/formatter.js index 9d44d95e..0e625fec 100644 --- a/lib/formatter.js +++ b/lib/formatter.js @@ -6,77 +6,98 @@ const _ = require('lodash'); * @returns {object} */ function format(data) { - const { methods, definitions } = data; + try { + const { methods, definitions } = data; + const objects = []; - // check if there are none - if (!(methods && methods.length > 0 && definitions && definitions.length > 0)) { - throw new Error('Methods and definitions should not be empty!'); - } + // check if there are none + if (!(methods && methods.length > 0 && definitions && definitions.length > 0)) { + return new Error('Methods and definitions should not be empty!'); + } - const mutable = _.cloneDeep(data); + const mutable = _.cloneDeep(data); + + // locate all of the protected properties + const secure = []; + definitions.forEach((definition) => { + definition.tsType.properties.forEach((property) => { + if (property['x-AuthFieldType']) { + secure.push({ + parameterName: definition.name, + propertyName: property.name, + value: property['x-AuthFieldType'], + }); + } + }); + }); - // locate all of the protected properties - const secure = []; - definitions.forEach((definition) => { - definition.tsType.properties.forEach((property) => { - if (property['x-AuthFieldType']) { - secure.push({ - parameterName: definition.name, - propertyName: property.name, - value: property['x-AuthFieldType'], + // get definitions based on $ref + methods.forEach((method, i) => { + const list = Object.keys(method.responses); + const formatted = {}; + if (list.length > 0) { + list.forEach((response) => { + formatted[response] = method.responses[response]; + const refName = formatted[response].schema['$ref'].split('/').slice(-1)[0]; + definitions.forEach((definition) => { + // copy properties + if (refName === definition.name) { + formatted[response].properties = definition.tsType.properties; + formatted[response].status = Number(response) || null; + } + }); }); } - }); - }); - // get definitions based on $ref - methods.forEach((method, i) => { - const list = Object.keys(method.responses); - const formatted = {}; - if (list.length > 0) { - list.forEach((response) => { - formatted[response] = method.responses[response]; - const refName = formatted[response].schema['$ref'].split('/').slice(-1)[0]; - definitions.forEach((definition) => { - // copy properties - if (refName === definition.name) { - formatted[response].properties = definition.tsType.properties; - formatted[response].status = Number(response) || null; + // pass protected properties to the parameters + secure.forEach((property, j) => { + method.parameters.forEach((parameter, k) => { + if (parameter.name === property.parameterName) { + mutable.methods[i].parameters[k]['x-AuthFieldType'] = secure[j]; } }); }); - } - // pass protected properties to the parameters - secure.forEach((property, j) => { - method.parameters.forEach((parameter, k) => { - if (parameter.name === property.parameterName) { - mutable.methods[i].parameters[k]['x-AuthFieldType'] = secure[j]; + // add object references to the method (load objects as local variables inside the controller) + method.parameters.forEach((parameter) => { + if (parameter.schema) { + const reference = parameter.schema['$ref'].split('/').slice(-1)[0]; + objects.push({ + destination: method.destination, + origin: parameter['in'] === 'path' ? 'params' : parameter['in'], + parameterName: parameter.name, + reference, + }) } }); - }); - // generate the code - list.forEach((response) => { - let code = `return res.code(${formatted[response].status}).send({`; - formatted[response].properties.forEach((property, i) => { - // remove the whitespaces from the parameter names - const name = property.name.replace(/\s/g, ''); - formatted[response].properties[i].name = name; - // escape the quotes - const value = property.tsType === 'string' ? '\'string\'' : 0; - code = `${code} + // generate the code + list.forEach((response) => { + let code = `return res.code(${formatted[response].status}).send({`; + formatted[response].properties.forEach((property, i) => { + // remove the whitespaces from the parameter names + const name = property.name.replace(/\s/g, ''); + formatted[response].properties[i].name = name; + // escape the quotes + const value = property.tsType === 'string' ? '\'string\'' : 0; + code = `${code} ${name}: ${value},`; - }); - formatted[response].code = `${code} + }); + formatted[response].code = `${code} });`; + }); + + // add the code to the resulting object + mutable.methods[i].responses = formatted; }); - // add the code to the resulting object - mutable.methods[i].responses = formatted; - }); + // add objects + mutable.objects = objects; - return mutable; + return mutable; + } catch (err) { + throw new Error(err.message || err); + } } module.exports = { diff --git a/lib/splitter.js b/lib/splitter.js index 5fcbc079..d22e0ee6 100644 --- a/lib/splitter.js +++ b/lib/splitter.js @@ -26,6 +26,30 @@ async function split(source, className, path, dir) { } try { + // get the objects before the ES6 conversion + const objectsListStart = '/* objects >'; + const objectsListStop = '< objects */'; + const objectsDivider = '<----->'; + const lineDivider = '<->'; + const objectsString = source.substring(source.indexOf(objectsListStart) + 12, source.indexOf(objectsListStop)); + const compiledObjects = []; + const strings = objectsString.split(objectsDivider); + strings.forEach((string) => { + const contents = string.split(lineDivider); + const object = {}; + contents.forEach((line) => { + const trimmed = line.replace(/\s/g, ''); + if (trimmed !== '') { + object[trimmed.split(':')[0]] = trimmed.split(':')[1]; + } + }); + if (Object.keys(object).length > 0) { + compiledObjects.push(object); + } + }); + + // TODO: add imports to the generated classes based on the compiledObjects array + // convert ES5 to ES6 const { code, warnings } = transform(source, ['let', 'arrow', 'arrow-return', 'class']); @@ -37,7 +61,7 @@ async function split(source, className, path, dir) { // create the source file fs.writeFileSync(`${__dirname}/source.js`, code, (err) => { if (err) { - throw new Error(err.message); + throw new Error(err.message || err); } }); @@ -117,7 +141,7 @@ async function split(source, className, path, dir) { // delete the source file await fs.unlink(`${__dirname}/source.js`, (err) => { if (err) { - throw new Error(err.message); + throw new Error(err.message || err); } }); @@ -138,14 +162,14 @@ async function split(source, className, path, dir) { beautify(file.content, { indent_size: 2 }), (fErr) => { if (fErr) { - throw new Error(fErr.message); + throw new Error(fErr.message || fErr); } }); }); return console.log('> swagger-js-codegen @ Success!'); } catch (err) { - throw new Error(err.message); + throw new Error(err.message || err); } } diff --git a/package-lock.json b/package-lock.json index 2bb2dda6..f8da60c8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2346,6 +2346,11 @@ "uuid": "^3.1.0" } }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==" + }, "resolve": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", diff --git a/package.json b/package.json index efedbbdd..a6b92fc4 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "lebab": "^3.0.4", "lodash": "^4.17.10", "mustache": "2.2.1", + "require-from-string": "^2.0.2", "update-notifier": "^2.1.0" }, "devDependencies": { diff --git a/templates/multi-class.mustache b/templates/multi-class.mustache index acfa8c3d..508a0eca 100644 --- a/templates/multi-class.mustache +++ b/templates/multi-class.mustache @@ -14,6 +14,15 @@ {{> method}} {{/methods}} + /* objects > + {{#objects}} + destination: {{&destination}} + <-> + reference: {{&reference}} + <-----> + {{/objects}} + < objects */ + return {{&className}}; })(); From af8055074548e2f665f8a912d93bd9ecfda5a98f Mon Sep 17 00:00:00 2001 From: Peter Date: Thu, 21 Mar 2019 11:16:48 +0300 Subject: [PATCH 11/58] Load objects and classes to the global scope --- lib/expose.js | 41 +++++++++++++++++++---------------------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/lib/expose.js b/lib/expose.js index 0e77d8e1..62595d10 100644 --- a/lib/expose.js +++ b/lib/expose.js @@ -13,6 +13,7 @@ function expose(definitions, methods, path) { try { // get list of the definitions const list = Object.keys(definitions); + const globals = []; // do not proceed if there are no definitions if (list.length === 0) { @@ -27,11 +28,7 @@ function expose(definitions, methods, path) { // process definitions list.forEach(async (definition) => { - // expose definitions objects to the global scope TODO: resolve the issue with global availability - global['objects'] = {}; - // global.objects[definition] = definitions[definition]; - - Object.assign(global.objects, definitions[definition]); + globals.push({ [definition]: definitions[definition] }); // bind the parameters let parameters = ''; @@ -41,7 +38,7 @@ function expose(definitions, methods, path) { let origin = parameter['in']; if (origin === 'path') origin = 'params'; parameters = `${parameters} - this.${parameter.name} = req.${origin}['${parameter.name}'];` + this.${parameter.name} = req.${origin}['${parameter.name}'];` } }) }); @@ -103,17 +100,15 @@ function expose(definitions, methods, path) { // compile the file const content = `/* auto-generated: ${definition}.js */ - module.exports = class ${definition} { - constructor(req, res, params) { + module.exports = class { + constructor(req = {}, res = {}, params = {}) { this.req = req; this.res = res; this.params = params; - ${parameters} + ${parameters} + this.${definition} = ${inspect(definitions[definition], { showHidden: false, depth: null })}; } - - ${validation} - - ${definition} = ${inspect(definitions[definition], { showHidden: false, depth: null })}; + ${validation} };`; // make sure that destination definition directory exists @@ -132,23 +127,25 @@ function expose(definitions, methods, path) { }); }); - // load classes to the global scope TODO: resolve the issue with global availability + // load objects to the global scope + global['objects'] = {}; + globals.forEach((object) => { + const name = Object.keys(object); + global.objects[name] = object[name]; + }); + + // load classes to the global scope + global['classes'] = {}; fs.readdir(container, (err, dirs) => { if (err) { throw new Error(err.message || err); } - dirs.forEach((dir) => { - fs.readFile(`${container}/${dir}/${dir}.js`, (err, data) => { + fs.readFile(`${container}/${dir}/${dir}.js`, "utf8", (err, data) => { if (err) { throw new Error(err.message || err); } - - // - const Instance = RFS(data); - const Example = new Instance(); - global['classes'] = {}; - Object.assign(global.classes, { [dir]: Example }); + global.classes[dir] = require(`${container}/${dir}/${dir}.js`); }); }); }); From a5ed9112588964ebf3fa446421be68601299f3f7 Mon Sep 17 00:00:00 2001 From: Peter Date: Thu, 21 Mar 2019 12:41:13 +0300 Subject: [PATCH 12/58] Bind definition classes inside of the controllers --- lib/splitter.js | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/lib/splitter.js b/lib/splitter.js index d22e0ee6..5123ff7c 100644 --- a/lib/splitter.js +++ b/lib/splitter.js @@ -1,6 +1,7 @@ const beautify = require('js-beautify').js; const fs = require('fs'); const { transform } = require('lebab'); +const _ = require('lodash'); /** * First letter capitalization, utility function @@ -48,8 +49,6 @@ async function split(source, className, path, dir) { } }); - // TODO: add imports to the generated classes based on the compiledObjects array - // convert ES5 to ES6 const { code, warnings } = transform(source, ['let', 'arrow', 'arrow-return', 'class']); @@ -117,8 +116,16 @@ async function split(source, className, path, dir) { }); }); - // add 'use strict' and exporting + // add 'use strict', exporting and bind definitions files.forEach((file, i) => { + let bindings = ''; + compiledObjects.forEach((entry) => { + if (entry.destination === file.file) { + bindings = `${bindings} + this.${entry.reference} = new classes.${entry.reference}(req, res, params);`; + } + }); + let fList = ''; file.functions.forEach((f) => { fList = `${fList} @@ -133,6 +140,7 @@ async function split(source, className, path, dir) { this.req = req; this.res = res; this.params = params; + ${bindings} } ${file.content} };`, { indent_size: 2 }); From 2f7b5d99439b92f04c0ee1e96deb70c987fab2e4 Mon Sep 17 00:00:00 2001 From: Peter Date: Thu, 21 Mar 2019 15:13:47 +0300 Subject: [PATCH 13/58] Fix duplicating definitions --- lib/splitter.js | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/lib/splitter.js b/lib/splitter.js index 5123ff7c..2a896a78 100644 --- a/lib/splitter.js +++ b/lib/splitter.js @@ -12,6 +12,20 @@ function capitalize(string) { return string[0].toUpperCase() + string.slice(1); } +/** + * Remove duplicates from the array of objects + * @param {array} array - array of objects for the comparison + * @returns {Array} + */ +function removeDuplicates(array) { + const strings = []; + array.forEach((object) => strings.push(JSON.stringify(object))); + const unique = _.uniq(strings); + const objects = []; + unique.forEach(string => objects.push(JSON.parse(string))); + return objects; +} + /** * Split the code to create multiple files and directories * @param {string} source - source code generated by swagger-js-codegen (using special templates!) @@ -119,7 +133,8 @@ async function split(source, className, path, dir) { // add 'use strict', exporting and bind definitions files.forEach((file, i) => { let bindings = ''; - compiledObjects.forEach((entry) => { + const uniqueObjects = removeDuplicates(compiledObjects); + uniqueObjects.forEach((entry) => { if (entry.destination === file.file) { bindings = `${bindings} this.${entry.reference} = new classes.${entry.reference}(req, res, params);`; From 05c5386a23cdb595a20149a534e92afb5ea251ce Mon Sep 17 00:00:00 2001 From: Peter Date: Thu, 21 Mar 2019 15:39:05 +0300 Subject: [PATCH 14/58] Fix x-AuthFieldType inside of the definitions classes --- lib/expose.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/expose.js b/lib/expose.js index 62595d10..bd0d3e32 100644 --- a/lib/expose.js +++ b/lib/expose.js @@ -85,16 +85,14 @@ function expose(definitions, methods, path) { // create validation method let validation = ''; if (secure.length > 0) { - validation = `async validation(req, res, params) {`; + validation = ''; secure.forEach((property) => { let origin = property.type; if (origin === 'path') origin = 'params'; validation = `${validation} - await FieldValidator.validate('${property.value}', req.${origin}['${property.definition}']['${property.property}'], req, res); + FieldValidator.validate('${property.value}', req.${origin}['${property.definition}']['${property.property}'], req, res); `; }); - validation = `${validation} - }`; } // compile the file @@ -106,9 +104,9 @@ function expose(definitions, methods, path) { this.res = res; this.params = params; ${parameters} + ${validation} this.${definition} = ${inspect(definitions[definition], { showHidden: false, depth: null })}; } - ${validation} };`; // make sure that destination definition directory exists From 0b30b93506a313503478ceb8f44ec55790ef925e Mon Sep 17 00:00:00 2001 From: Peter Date: Fri, 22 Mar 2019 13:35:20 +0300 Subject: [PATCH 15/58] Default response values for controllers --- lib/formatter.js | 96 +++++++++++++++++++++++++++++++++++++++++------ lib/typescript.js | 6 +++ 2 files changed, 91 insertions(+), 11 deletions(-) diff --git a/lib/formatter.js b/lib/formatter.js index 0e625fec..9a833142 100644 --- a/lib/formatter.js +++ b/lib/formatter.js @@ -1,5 +1,80 @@ +const { inspect } = require('util'); const _ = require('lodash'); +/** + * Parse nested schemas to build the proper response structure + * @param {object} entry - entry definition object + * @param {array} definitions - all of the definitions + * @returns {object} + */ +function responseBuilder(entry, definitions) { + try { + const response = {}; + + // check tsType + const { tsType } = entry || {}; + if (!tsType) { + return response; + } + + // process properties + const { properties } = tsType || {}; + if (properties && properties.length > 0) { + properties.forEach((property) => { + if (property.tsType === 'string') { + response[property.name] = property.example || 'string'; + } + + if (property.tsType === 'number') { + response[property.name] = property.example || 0; + } + + if (property.tsType === 'array') { + // use provided example if any + if (property.example) { + response[property.name] = property.example; + } else { + // check elementType + if (property.elementType) { + const { tsType: type, target } = ((property || {}).elementType || {}); + if (type && type === 'ref' && target) { + definitions.forEach((definition) => { + if (definition.name === target) { + // recursive call to unwrap all of the refs + response[property.name] = responseBuilder(definition, definitions); + } + }); + } else { + response[property.name] = []; + } + } else { + response[property.name] = []; + } + } + } + + // just in case... + if (property.tsType === 'object') { + response[property.name] = property.example || {}; + } + + if (property.tsType === 'ref') { + definitions.forEach((definition) => { + if (definition.name === property.target) { + // recursive call to unwrap all of the refs + response[property.name] = responseBuilder(definition, definitions); + } + }); + } + }); + } + + return response; + } catch (err) { + throw new Error(err.message || err); + } +} + /** * Format the responses for the APIs * @param data - initial data, that should have all of the necessary methods and schemas @@ -73,18 +148,17 @@ function format(data) { // generate the code list.forEach((response) => { - let code = `return res.code(${formatted[response].status}).send({`; - formatted[response].properties.forEach((property, i) => { - // remove the whitespaces from the parameter names - const name = property.name.replace(/\s/g, ''); - formatted[response].properties[i].name = name; - // escape the quotes - const value = property.tsType === 'string' ? '\'string\'' : 0; - code = `${code} - ${name}: ${value},`; + + // TODO: default responses from JSON: get definition + const ref = formatted[response].schema['$ref'].split('/').slice(-1)[0]; + let responseObject = {}; + definitions.forEach((def) => { + if (def.name === ref) { + responseObject = responseBuilder(def, definitions); + } }); - formatted[response].code = `${code} - });`; + + formatted[response].code = `return res.code(${formatted[response].status}).send(${inspect(responseObject, { showHidden: false, depth: null })});`; }); // add the code to the resulting object diff --git a/lib/typescript.js b/lib/typescript.js index 821913cd..cf3c7288 100644 --- a/lib/typescript.js +++ b/lib/typescript.js @@ -9,6 +9,7 @@ var _ = require('lodash'); * Not all type are currently supported, but they should be straightforward to add. * * @param swaggerType a swagger type definition, i.e., the right hand side of a swagger type definition. + * @param swagger * @returns a recursive structure representing the type, which can be used as a template model. */ function convertType(swaggerType, swagger) { @@ -62,6 +63,11 @@ function convertType(swaggerType, swagger) { var property = convertType(propertyType); property.name = propertyName; + // add example value if possibles + if (propertyType.example) { + property.example = propertyType.example; + } + property.optional = true; if (swaggerType.required && swaggerType.required.indexOf(propertyName) !== -1) { property.optional = false; From 72001af0e4e256a10f72fbff875ffb6c289525dd Mon Sep 17 00:00:00 2001 From: Peter Date: Fri, 22 Mar 2019 15:01:34 +0300 Subject: [PATCH 16/58] FieldValidator fixes --- lib/expose.js | 4 +++- lib/splitter.js | 2 +- templates/multi-method.mustache | 15 ++++++++++++--- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/lib/expose.js b/lib/expose.js index bd0d3e32..3052f724 100644 --- a/lib/expose.js +++ b/lib/expose.js @@ -90,7 +90,9 @@ function expose(definitions, methods, path) { let origin = property.type; if (origin === 'path') origin = 'params'; validation = `${validation} - FieldValidator.validate('${property.value}', req.${origin}['${property.definition}']['${property.property}'], req, res); + global.FieldValidator.validate('${property.value}', req.${origin}['${property.definition}']['${property.property}'], req, res).then(function (res) { + if (!res) return; + }).catch((err) => { return; }); `; }); } diff --git a/lib/splitter.js b/lib/splitter.js index 2a896a78..623ca727 100644 --- a/lib/splitter.js +++ b/lib/splitter.js @@ -98,7 +98,7 @@ async function split(source, className, path, dir) { const destination = functionName.split('_')[0]; functionName = functionName.split('_')[1]; standalone = `async ${functionName}(req, res) ${standalone.split('(req, res)')[1]}`; - standalone = standalone.split('await_FieldValidator').join('await FieldValidator'); + standalone = standalone.split('await_FieldValidator').join('await global.FieldValidator'); destinations.push({ content: standalone, file: destination, diff --git a/templates/multi-method.mustache b/templates/multi-method.mustache index 9b3cbb3d..2a1f6498 100644 --- a/templates/multi-method.mustache +++ b/templates/multi-method.mustache @@ -9,17 +9,26 @@ {{#parameters}} {{#isQueryParameter}} {{#x-AuthFieldType}} - await_FieldValidator.validate('{{&x-AuthFieldType.value}}', req.query['{{&x-AuthFieldType.parameterName}}']['{{&x-AuthFieldType.propertyName}}'], req, res); + var auth = await_FieldValidator.validate('{{&x-AuthFieldType.value}}', req.query['{{&x-AuthFieldType.parameterName}}']['{{&x-AuthFieldType.propertyName}}'], req, res); + if (!auth) { + return; + } {{/x-AuthFieldType}} {{/isQueryParameter}} {{#isPathParameter}} {{#x-AuthFieldType}} - await_FieldValidator.validate('{{&x-AuthFieldType.value}}', req.params['{{&x-AuthFieldType.parameterName}}']['{{&x-AuthFieldType.propertyName}}'], req, res); + var auth = await_FieldValidator.validate('{{&x-AuthFieldType.value}}', req.params['{{&x-AuthFieldType.parameterName}}']['{{&x-AuthFieldType.propertyName}}'], req, res); + if (!auth) { + return; + } {{/x-AuthFieldType}} {{/isPathParameter}} {{#isBodyParameter}} {{#x-AuthFieldType}} - await_FieldValidator.validate('{{&x-AuthFieldType.value}}', req.body['{{&x-AuthFieldType.parameterName}}']['{{&x-AuthFieldType.propertyName}}'], req, res); + var auth = await_FieldValidator.validate('{{&x-AuthFieldType.value}}', req.body['{{&x-AuthFieldType.parameterName}}']['{{&x-AuthFieldType.propertyName}}'], req, res); + if (!auth) { + return; + } {{/x-AuthFieldType}} {{/isBodyParameter}} {{#isHeaderParameter}} From a6775db7eb6de9de8ebae66b7d1fb8271f939017 Mon Sep 17 00:00:00 2001 From: Peter Date: Fri, 22 Mar 2019 16:07:31 +0300 Subject: [PATCH 17/58] Remove 'require-from-string' --- lib/expose.js | 1 - package.json | 1 - 2 files changed, 2 deletions(-) diff --git a/lib/expose.js b/lib/expose.js index 3052f724..aa4502e5 100644 --- a/lib/expose.js +++ b/lib/expose.js @@ -1,7 +1,6 @@ const beauty = require('js-beautify').js; const fs = require('fs'); const { inspect } = require('util'); -const RFS = require('require-from-string'); /** * Expose definitions objects, create files with objects diff --git a/package.json b/package.json index a6b92fc4..efedbbdd 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,6 @@ "lebab": "^3.0.4", "lodash": "^4.17.10", "mustache": "2.2.1", - "require-from-string": "^2.0.2", "update-notifier": "^2.1.0" }, "devDependencies": { From cd2262b77ae4aa54c4be524c849acb994ae0b62e Mon Sep 17 00:00:00 2001 From: Peter Date: Fri, 22 Mar 2019 16:14:39 +0300 Subject: [PATCH 18/58] Fix definitions classes export to global --- lib/expose.js | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/lib/expose.js b/lib/expose.js index aa4502e5..8b0c3284 100644 --- a/lib/expose.js +++ b/lib/expose.js @@ -140,12 +140,7 @@ function expose(definitions, methods, path) { throw new Error(err.message || err); } dirs.forEach((dir) => { - fs.readFile(`${container}/${dir}/${dir}.js`, "utf8", (err, data) => { - if (err) { - throw new Error(err.message || err); - } - global.classes[dir] = require(`${container}/${dir}/${dir}.js`); - }); + global.classes[dir] = require(`${container}/${dir}/${dir}.js`); }); }); } catch (err) { From ff01b38c87a276afc2a8d126cdf224eca4739fc3 Mon Sep 17 00:00:00 2001 From: Peter Date: Fri, 22 Mar 2019 17:01:07 +0300 Subject: [PATCH 19/58] Fix definitions classes call inside of the controllers --- lib/splitter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/splitter.js b/lib/splitter.js index 623ca727..8fd6db81 100644 --- a/lib/splitter.js +++ b/lib/splitter.js @@ -137,7 +137,7 @@ async function split(source, className, path, dir) { uniqueObjects.forEach((entry) => { if (entry.destination === file.file) { bindings = `${bindings} - this.${entry.reference} = new classes.${entry.reference}(req, res, params);`; + this.${entry.reference} = new global.classes.${entry.reference}(req, res, params);`; } }); From 6effc4ccfde9c4347ff7c24595f80de861bca555 Mon Sep 17 00:00:00 2001 From: Peter Date: Mon, 25 Mar 2019 11:36:02 +0300 Subject: [PATCH 20/58] Formatting --- lib/expose.js | 10 ++++++---- lib/splitter.js | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/expose.js b/lib/expose.js index 8b0c3284..64c7a52c 100644 --- a/lib/expose.js +++ b/lib/expose.js @@ -81,7 +81,7 @@ function expose(definitions, methods, path) { }); } - // create validation method + // add validation let validation = ''; if (secure.length > 0) { validation = ''; @@ -89,9 +89,11 @@ function expose(definitions, methods, path) { let origin = property.type; if (origin === 'path') origin = 'params'; validation = `${validation} - global.FieldValidator.validate('${property.value}', req.${origin}['${property.definition}']['${property.property}'], req, res).then(function (res) { - if (!res) return; - }).catch((err) => { return; }); + global.FieldValidator.validate('${property.value}', req.${origin}['${property.definition}']['${property.property}'], req, res) + .then(function (result) { + if (!result) return; + }) + .catch((err) => { return; }); `; }); } diff --git a/lib/splitter.js b/lib/splitter.js index 8fd6db81..bc51a5ca 100644 --- a/lib/splitter.js +++ b/lib/splitter.js @@ -151,7 +151,7 @@ async function split(source, className, path, dir) { /* auto-generated: ${file.file}.controller.js */ module.exports = class ${file.file} { - constructor(req, res, params) { + constructor(req = {}, res = {}, params = {}) { this.req = req; this.res = res; this.params = params; From 9482fc050f2109eb772050fc20a814326c4a8c58 Mon Sep 17 00:00:00 2001 From: Peter Date: Mon, 25 Mar 2019 11:45:00 +0300 Subject: [PATCH 21/58] Remove class names from controllers --- lib/splitter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/splitter.js b/lib/splitter.js index bc51a5ca..1794f3a3 100644 --- a/lib/splitter.js +++ b/lib/splitter.js @@ -150,7 +150,7 @@ async function split(source, className, path, dir) { /* auto-generated: ${file.file}.controller.js */ - module.exports = class ${file.file} { + module.exports = class { constructor(req = {}, res = {}, params = {}) { this.req = req; this.res = res; From 1e455377fbc432637c3460a83a62f29319e9d8c2 Mon Sep 17 00:00:00 2001 From: Peter Date: Mon, 25 Mar 2019 14:19:22 +0300 Subject: [PATCH 22/58] Remove class names from controllers --- templates/multi-method.mustache | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/templates/multi-method.mustache b/templates/multi-method.mustache index 2a1f6498..2f27de5b 100644 --- a/templates/multi-method.mustache +++ b/templates/multi-method.mustache @@ -9,7 +9,7 @@ {{#parameters}} {{#isQueryParameter}} {{#x-AuthFieldType}} - var auth = await_FieldValidator.validate('{{&x-AuthFieldType.value}}', req.query['{{&x-AuthFieldType.parameterName}}']['{{&x-AuthFieldType.propertyName}}'], req, res); + var auth = await_FieldValidator.validate('{{&x-AuthFieldType.value}}', req.query['{{&x-AuthFieldType.propertyName}}'], req, res); if (!auth) { return; } @@ -17,7 +17,7 @@ {{/isQueryParameter}} {{#isPathParameter}} {{#x-AuthFieldType}} - var auth = await_FieldValidator.validate('{{&x-AuthFieldType.value}}', req.params['{{&x-AuthFieldType.parameterName}}']['{{&x-AuthFieldType.propertyName}}'], req, res); + var auth = await_FieldValidator.validate('{{&x-AuthFieldType.value}}', req.params['{{&x-AuthFieldType.propertyName}}'], req, res); if (!auth) { return; } @@ -25,7 +25,7 @@ {{/isPathParameter}} {{#isBodyParameter}} {{#x-AuthFieldType}} - var auth = await_FieldValidator.validate('{{&x-AuthFieldType.value}}', req.body['{{&x-AuthFieldType.parameterName}}']['{{&x-AuthFieldType.propertyName}}'], req, res); + var auth = await_FieldValidator.validate('{{&x-AuthFieldType.value}}', req.body['{{&x-AuthFieldType.propertyName}}'], req, res); if (!auth) { return; } From 140339cbd13a5f2008ad0b07152c6fd9e1136115 Mon Sep 17 00:00:00 2001 From: Peter Date: Mon, 25 Mar 2019 17:46:15 +0300 Subject: [PATCH 23/58] Method template update --- lib/formatter.js | 2 -- templates/multi-method.mustache | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/formatter.js b/lib/formatter.js index 9a833142..9b47c7bb 100644 --- a/lib/formatter.js +++ b/lib/formatter.js @@ -148,8 +148,6 @@ function format(data) { // generate the code list.forEach((response) => { - - // TODO: default responses from JSON: get definition const ref = formatted[response].schema['$ref'].split('/').slice(-1)[0]; let responseObject = {}; definitions.forEach((def) => { diff --git a/templates/multi-method.mustache b/templates/multi-method.mustache index 2f27de5b..77caaf5c 100644 --- a/templates/multi-method.mustache +++ b/templates/multi-method.mustache @@ -14,6 +14,7 @@ return; } {{/x-AuthFieldType}} + var {{name}} = req.query['{{name}}']; {{/isQueryParameter}} {{#isPathParameter}} {{#x-AuthFieldType}} @@ -22,6 +23,7 @@ return; } {{/x-AuthFieldType}} + var {{name}} = req.params['{{name}}']; {{/isPathParameter}} {{#isBodyParameter}} {{#x-AuthFieldType}} From 057b95c02e544f7cd947df5dfae3ba80fc020d7c Mon Sep 17 00:00:00 2001 From: Peter Date: Tue, 26 Mar 2019 11:25:45 +0300 Subject: [PATCH 24/58] Update templates, fix controller parameters binding --- lib/splitter.js | 46 --------------------------------- templates/multi-class.mustache | 9 ------- templates/multi-method.mustache | 1 + 3 files changed, 1 insertion(+), 55 deletions(-) diff --git a/lib/splitter.js b/lib/splitter.js index 1794f3a3..998602e9 100644 --- a/lib/splitter.js +++ b/lib/splitter.js @@ -12,20 +12,6 @@ function capitalize(string) { return string[0].toUpperCase() + string.slice(1); } -/** - * Remove duplicates from the array of objects - * @param {array} array - array of objects for the comparison - * @returns {Array} - */ -function removeDuplicates(array) { - const strings = []; - array.forEach((object) => strings.push(JSON.stringify(object))); - const unique = _.uniq(strings); - const objects = []; - unique.forEach(string => objects.push(JSON.parse(string))); - return objects; -} - /** * Split the code to create multiple files and directories * @param {string} source - source code generated by swagger-js-codegen (using special templates!) @@ -41,28 +27,6 @@ async function split(source, className, path, dir) { } try { - // get the objects before the ES6 conversion - const objectsListStart = '/* objects >'; - const objectsListStop = '< objects */'; - const objectsDivider = '<----->'; - const lineDivider = '<->'; - const objectsString = source.substring(source.indexOf(objectsListStart) + 12, source.indexOf(objectsListStop)); - const compiledObjects = []; - const strings = objectsString.split(objectsDivider); - strings.forEach((string) => { - const contents = string.split(lineDivider); - const object = {}; - contents.forEach((line) => { - const trimmed = line.replace(/\s/g, ''); - if (trimmed !== '') { - object[trimmed.split(':')[0]] = trimmed.split(':')[1]; - } - }); - if (Object.keys(object).length > 0) { - compiledObjects.push(object); - } - }); - // convert ES5 to ES6 const { code, warnings } = transform(source, ['let', 'arrow', 'arrow-return', 'class']); @@ -132,15 +96,6 @@ async function split(source, className, path, dir) { // add 'use strict', exporting and bind definitions files.forEach((file, i) => { - let bindings = ''; - const uniqueObjects = removeDuplicates(compiledObjects); - uniqueObjects.forEach((entry) => { - if (entry.destination === file.file) { - bindings = `${bindings} - this.${entry.reference} = new global.classes.${entry.reference}(req, res, params);`; - } - }); - let fList = ''; file.functions.forEach((f) => { fList = `${fList} @@ -155,7 +110,6 @@ async function split(source, className, path, dir) { this.req = req; this.res = res; this.params = params; - ${bindings} } ${file.content} };`, { indent_size: 2 }); diff --git a/templates/multi-class.mustache b/templates/multi-class.mustache index 508a0eca..acfa8c3d 100644 --- a/templates/multi-class.mustache +++ b/templates/multi-class.mustache @@ -14,15 +14,6 @@ {{> method}} {{/methods}} - /* objects > - {{#objects}} - destination: {{&destination}} - <-> - reference: {{&reference}} - <-----> - {{/objects}} - < objects */ - return {{&className}}; })(); diff --git a/templates/multi-method.mustache b/templates/multi-method.mustache index 77caaf5c..4855562b 100644 --- a/templates/multi-method.mustache +++ b/templates/multi-method.mustache @@ -32,6 +32,7 @@ return; } {{/x-AuthFieldType}} + var {{name}} = global.classes['{{tsType.target}}'](req, res, req.body); {{/isBodyParameter}} {{#isHeaderParameter}} {{#isSingleton}}{{/isSingleton}} From 4b7d7d25892869b224f1f1d64add8f6bf3328a04 Mon Sep 17 00:00:00 2001 From: Peter Date: Tue, 26 Mar 2019 12:23:07 +0300 Subject: [PATCH 25/58] Fix definitions classes instantination --- templates/multi-method.mustache | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/multi-method.mustache b/templates/multi-method.mustache index 4855562b..51aedaab 100644 --- a/templates/multi-method.mustache +++ b/templates/multi-method.mustache @@ -32,7 +32,7 @@ return; } {{/x-AuthFieldType}} - var {{name}} = global.classes['{{tsType.target}}'](req, res, req.body); + var {{name}} = new global.classes['{{tsType.target}}'](req, res, req.body); {{/isBodyParameter}} {{#isHeaderParameter}} {{#isSingleton}}{{/isSingleton}} From d769ab7e62aef7f4aff64880439d00b926726025 Mon Sep 17 00:00:00 2001 From: Peter Date: Tue, 26 Mar 2019 15:27:19 +0300 Subject: [PATCH 26/58] Fix definition binding name --- lib/expose.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/expose.js b/lib/expose.js index 64c7a52c..b952f512 100644 --- a/lib/expose.js +++ b/lib/expose.js @@ -108,7 +108,7 @@ function expose(definitions, methods, path) { this.params = params; ${parameters} ${validation} - this.${definition} = ${inspect(definitions[definition], { showHidden: false, depth: null })}; + this.schema = ${inspect(definitions[definition], { showHidden: false, depth: null })}; } };`; From 0370a29032073ae961438f1c05c1279d1067b5c3 Mon Sep 17 00:00:00 2001 From: Peter Date: Tue, 26 Mar 2019 17:50:57 +0300 Subject: [PATCH 27/58] Fix definitions bindings: nested objects --- lib/expose.js | 84 ++++++++++++++++++++++++++------------------------- 1 file changed, 43 insertions(+), 41 deletions(-) diff --git a/lib/expose.js b/lib/expose.js index b952f512..91d2f842 100644 --- a/lib/expose.js +++ b/lib/expose.js @@ -12,7 +12,6 @@ function expose(definitions, methods, path) { try { // get list of the definitions const list = Object.keys(definitions); - const globals = []; // do not proceed if there are no definitions if (list.length === 0) { @@ -27,20 +26,39 @@ function expose(definitions, methods, path) { // process definitions list.forEach(async (definition) => { - globals.push({ [definition]: definitions[definition] }); - // bind the parameters let parameters = ''; - methods.forEach((method) => { - method.parameters.forEach((parameter) => { - if (parameter.name === definition) { - let origin = parameter['in']; - if (origin === 'path') origin = 'params'; - parameters = `${parameters} - this.${parameter.name} = req.${origin}['${parameter.name}'];` + const props = Object.keys(definitions[definition].properties); + if (props.length && props.length > 0) { + props.forEach((prop) => { + const { type } = definitions[definition].properties[prop]; + if (type) { + if (type === 'array') { + const { items } = definitions[definition].properties[prop]; + if (items && items['$ref']) { + const refName = items['$ref'].split('/').slice(-1)[0]; + parameters = `${parameters} + this.${prop} = []; + if (req.body['${prop}'].length && req.body['${prop}'].length > 0) { + req.body['${prop}'].forEach((object) => { + const ${refName} = new global.classes['${refName}'](req, res, object); + this.${prop}.push(${refName}); + }); + }`; + } + } else { + parameters = `${parameters} + this.${prop} = req.body['${prop}'];`; + } + } else { + if (definitions[definition].properties[prop]['$ref']) { + const refName = definitions[definition].properties[prop]['$ref'].split('/').slice(-1)[0]; + parameters = `${parameters} + this.${prop} = new global.classes['${refName}'](req, res, req.body['${prop}']);`; + } } - }) - }); + }); + } // check x-AuthFieldType field const secure = []; @@ -93,7 +111,9 @@ function expose(definitions, methods, path) { .then(function (result) { if (!result) return; }) - .catch((err) => { return; }); + .catch((err) => { + return; + }); `; }); } @@ -101,16 +121,16 @@ function expose(definitions, methods, path) { // compile the file const content = `/* auto-generated: ${definition}.js */ - module.exports = class { - constructor(req = {}, res = {}, params = {}) { - this.req = req; - this.res = res; - this.params = params; - ${parameters} - ${validation} - this.schema = ${inspect(definitions[definition], { showHidden: false, depth: null })}; - } - };`; + module.exports = class { + constructor(req = {}, res = {}, params = {}) { + this.req = req; + this.res = res; + this.params = params; + ${parameters} + ${validation} + this.schema = ${inspect(definitions[definition], { showHidden: false, depth: null })}; + } + };`; // make sure that destination definition directory exists const destination = `${container}/${definition}`; @@ -127,24 +147,6 @@ function expose(definitions, methods, path) { } }); }); - - // load objects to the global scope - global['objects'] = {}; - globals.forEach((object) => { - const name = Object.keys(object); - global.objects[name] = object[name]; - }); - - // load classes to the global scope - global['classes'] = {}; - fs.readdir(container, (err, dirs) => { - if (err) { - throw new Error(err.message || err); - } - dirs.forEach((dir) => { - global.classes[dir] = require(`${container}/${dir}/${dir}.js`); - }); - }); } catch (err) { throw new Error(err.message || err); } From 5e478dc37eb3f57267341bc9cd54e97df3cb456a Mon Sep 17 00:00:00 2001 From: Peter Date: Tue, 26 Mar 2019 18:01:38 +0300 Subject: [PATCH 28/58] Fix props names for definitions --- lib/expose.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/expose.js b/lib/expose.js index 91d2f842..0689ef46 100644 --- a/lib/expose.js +++ b/lib/expose.js @@ -38,7 +38,7 @@ function expose(definitions, methods, path) { if (items && items['$ref']) { const refName = items['$ref'].split('/').slice(-1)[0]; parameters = `${parameters} - this.${prop} = []; + this['${prop}'] = []; if (req.body['${prop}'].length && req.body['${prop}'].length > 0) { req.body['${prop}'].forEach((object) => { const ${refName} = new global.classes['${refName}'](req, res, object); @@ -48,13 +48,13 @@ function expose(definitions, methods, path) { } } else { parameters = `${parameters} - this.${prop} = req.body['${prop}'];`; + this['${prop}'] = req.body['${prop}'];`; } } else { if (definitions[definition].properties[prop]['$ref']) { const refName = definitions[definition].properties[prop]['$ref'].split('/').slice(-1)[0]; parameters = `${parameters} - this.${prop} = new global.classes['${refName}'](req, res, req.body['${prop}']);`; + this['${prop}'] = new global.classes['${refName}'](req, res, req.body['${prop}']);`; } } }); From 60b67f3ff3b7974108815288c938e1af64724f95 Mon Sep 17 00:00:00 2001 From: Peter Date: Wed, 27 Mar 2019 09:45:03 +0300 Subject: [PATCH 29/58] Fix x-AuthFieldType check for definitions --- lib/expose.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/expose.js b/lib/expose.js index 0689ef46..a4fbd183 100644 --- a/lib/expose.js +++ b/lib/expose.js @@ -107,7 +107,7 @@ function expose(definitions, methods, path) { let origin = property.type; if (origin === 'path') origin = 'params'; validation = `${validation} - global.FieldValidator.validate('${property.value}', req.${origin}['${property.definition}']['${property.property}'], req, res) + global.FieldValidator.validate('${property.value}', req.${origin}['${property.property}'], req, res) .then(function (result) { if (!result) return; }) From f9cdc53f7b3c9e653ae14b646f9d42dbe2b18721 Mon Sep 17 00:00:00 2001 From: Peter Date: Wed, 27 Mar 2019 09:51:58 +0300 Subject: [PATCH 30/58] Fix array binding for definitions --- lib/expose.js | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/lib/expose.js b/lib/expose.js index a4fbd183..d79ddd45 100644 --- a/lib/expose.js +++ b/lib/expose.js @@ -35,16 +35,24 @@ function expose(definitions, methods, path) { if (type) { if (type === 'array') { const { items } = definitions[definition].properties[prop]; - if (items && items['$ref']) { - const refName = items['$ref'].split('/').slice(-1)[0]; + if (items) { + if (items['$ref']) { + const refName = items['$ref'].split('/').slice(-1)[0]; + parameters = `${parameters} + this['${prop}'] = []; + if (req.body['${prop}'].length && req.body['${prop}'].length > 0) { + req.body['${prop}'].forEach((object) => { + const ${refName} = new global.classes['${refName}'](req, res, object); + this.${prop}.push(${refName}); + }); + }`; + } else { + parameters = `${parameters} + this['${prop}'] = req.body['${prop}'];`; + } + } else { parameters = `${parameters} - this['${prop}'] = []; - if (req.body['${prop}'].length && req.body['${prop}'].length > 0) { - req.body['${prop}'].forEach((object) => { - const ${refName} = new global.classes['${refName}'](req, res, object); - this.${prop}.push(${refName}); - }); - }`; + this['${prop}'] = req.body['${prop}'];`; } } else { parameters = `${parameters} From e5e2aace8502eaa76f0b957e5e1c88323decd096 Mon Sep 17 00:00:00 2001 From: Peter Date: Wed, 27 Mar 2019 09:54:04 +0300 Subject: [PATCH 31/58] Definitions formatting --- lib/expose.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/expose.js b/lib/expose.js index d79ddd45..6cb9503d 100644 --- a/lib/expose.js +++ b/lib/expose.js @@ -117,7 +117,9 @@ function expose(definitions, methods, path) { validation = `${validation} global.FieldValidator.validate('${property.value}', req.${origin}['${property.property}'], req, res) .then(function (result) { - if (!result) return; + if (!result) { + return; + } }) .catch((err) => { return; From 8041aa8e23fc46d09765d0d093042027c62451ea Mon Sep 17 00:00:00 2001 From: Peter Date: Wed, 27 Mar 2019 09:58:47 +0300 Subject: [PATCH 32/58] Definitions formatting --- lib/expose.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/expose.js b/lib/expose.js index 6cb9503d..5c624a1f 100644 --- a/lib/expose.js +++ b/lib/expose.js @@ -116,7 +116,7 @@ function expose(definitions, methods, path) { if (origin === 'path') origin = 'params'; validation = `${validation} global.FieldValidator.validate('${property.value}', req.${origin}['${property.property}'], req, res) - .then(function (result) { + .then((result) => { if (!result) { return; } From 5ac8dea1ce8040ac8ba71c3ceab0357b292713be Mon Sep 17 00:00:00 2001 From: Peter Date: Wed, 27 Mar 2019 11:26:38 +0300 Subject: [PATCH 33/58] Fix default API responses (arrays of objects) --- lib/formatter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/formatter.js b/lib/formatter.js index 9b47c7bb..39a7267b 100644 --- a/lib/formatter.js +++ b/lib/formatter.js @@ -41,7 +41,7 @@ function responseBuilder(entry, definitions) { definitions.forEach((definition) => { if (definition.name === target) { // recursive call to unwrap all of the refs - response[property.name] = responseBuilder(definition, definitions); + response[property.name] = [responseBuilder(definition, definitions)]; } }); } else { From 8f5fcd24c9b5197b9307c299f9ec83a3fae661c6 Mon Sep 17 00:00:00 2001 From: Peter Date: Thu, 28 Mar 2019 16:30:04 +0300 Subject: [PATCH 34/58] Fix x-AuthFieldType validation --- lib/formatter.js | 15 +++++++++++++++ lib/splitter.js | 2 +- templates/multi-method.mustache | 12 ++++++------ 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/lib/formatter.js b/lib/formatter.js index 39a7267b..794e4fa2 100644 --- a/lib/formatter.js +++ b/lib/formatter.js @@ -166,6 +166,21 @@ function format(data) { // add objects mutable.objects = objects; + // fix x-AuthFieldType TODO: need to rewrite it properly + mutable.methods.forEach((method, m) => { + method.parameters.forEach((parameter, p) => { + if (parameter['x-AuthFieldType']) { + const { propertyName: name, value } = parameter['x-AuthFieldType']; + if (!(name && value)) { + mutable.methods[m].parameters[p]['x-AuthFieldType'] = { + propertyName: parameter.name, + value: parameter['x-AuthFieldType'], + }; + } + } + }); + }); + return mutable; } catch (err) { throw new Error(err.message || err); diff --git a/lib/splitter.js b/lib/splitter.js index 998602e9..c63923c5 100644 --- a/lib/splitter.js +++ b/lib/splitter.js @@ -32,7 +32,7 @@ async function split(source, className, path, dir) { // show conversion errors and warnings if (warnings && warnings.length && warnings.length > 0) { - console.log('> codegen-splitter @ ES6 conversion warnings:\n', warnings); + console.log('> swagger-js-codegen @ ES6 conversion warnings:\n', warnings); } // create the source file diff --git a/templates/multi-method.mustache b/templates/multi-method.mustache index 51aedaab..f0ad567c 100644 --- a/templates/multi-method.mustache +++ b/templates/multi-method.mustache @@ -9,8 +9,8 @@ {{#parameters}} {{#isQueryParameter}} {{#x-AuthFieldType}} - var auth = await_FieldValidator.validate('{{&x-AuthFieldType.value}}', req.query['{{&x-AuthFieldType.propertyName}}'], req, res); - if (!auth) { + var auth{{&x-AuthFieldType.propertyName}} = await_FieldValidator.validate('{{&x-AuthFieldType.value}}', req.query['{{&x-AuthFieldType.propertyName}}'], req, res); + if (!auth{{&x-AuthFieldType.propertyName}}) { return; } {{/x-AuthFieldType}} @@ -18,8 +18,8 @@ {{/isQueryParameter}} {{#isPathParameter}} {{#x-AuthFieldType}} - var auth = await_FieldValidator.validate('{{&x-AuthFieldType.value}}', req.params['{{&x-AuthFieldType.propertyName}}'], req, res); - if (!auth) { + var auth{{&x-AuthFieldType.propertyName}} = await_FieldValidator.validate('{{&x-AuthFieldType.value}}', req.params['{{&x-AuthFieldType.propertyName}}'], req, res); + if (!auth{{&x-AuthFieldType.propertyName}}) { return; } {{/x-AuthFieldType}} @@ -27,8 +27,8 @@ {{/isPathParameter}} {{#isBodyParameter}} {{#x-AuthFieldType}} - var auth = await_FieldValidator.validate('{{&x-AuthFieldType.value}}', req.body['{{&x-AuthFieldType.propertyName}}'], req, res); - if (!auth) { + var auth{{&x-AuthFieldType.propertyName}} = await_FieldValidator.validate('{{&x-AuthFieldType.value}}', req.body['{{&x-AuthFieldType.propertyName}}'], req, res); + if (!auth{{&x-AuthFieldType.propertyName}}) { return; } {{/x-AuthFieldType}} From 80f25ad6e3c95c4aed3a79b2fccad465c384ef11 Mon Sep 17 00:00:00 2001 From: Peter Date: Fri, 29 Mar 2019 16:51:26 +0300 Subject: [PATCH 35/58] Fix definitions nesting --- lib/expose.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/expose.js b/lib/expose.js index 5c624a1f..417a32e8 100644 --- a/lib/expose.js +++ b/lib/expose.js @@ -40,8 +40,8 @@ function expose(definitions, methods, path) { const refName = items['$ref'].split('/').slice(-1)[0]; parameters = `${parameters} this['${prop}'] = []; - if (req.body['${prop}'].length && req.body['${prop}'].length > 0) { - req.body['${prop}'].forEach((object) => { + if (params['${prop}'].length && params['${prop}'].length > 0) { + params['${prop}'].forEach((object) => { const ${refName} = new global.classes['${refName}'](req, res, object); this.${prop}.push(${refName}); }); From a3c0d5a28883c98f67cc024e976c17896bc5a287 Mon Sep 17 00:00:00 2001 From: Peter Date: Mon, 1 Apr 2019 11:16:39 +0300 Subject: [PATCH 36/58] Add options checks, update package.json, update README --- README.md | 71 +++++++++++++++++++++++++++++++------------------- lib/codegen.js | 25 +++++++++++++----- package.json | 7 +++-- 3 files changed, 67 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index 8d83748e..edd92646 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,55 @@ # Swagger to JS & Typescript Codegen -[![Circle CI](https://circleci.com/gh/wcandillon/swagger-js-codegen.svg?style=svg)](https://circleci.com/gh/wcandillon/swagger-js-codegen) [![NPM version](http://img.shields.io/npm/v/swagger-js-codegen.svg?style=flat)](http://badge.fury.io/js/swagger-js-codegen) -## We are looking for a new maintainer +## Installation +```bash +npm install swagger-js-codegen +``` -This project is no longer actively maintained by its creator. Please let us know if you would like to become a maintainer. -At the time we wrote this package, the swagger didn't have generators for JavaScript nor TypeScript. Now there are [great alternatives of this package available](https://github.com/swagger-api/swagger-codegen). +___ -This package generates a nodejs, reactjs or angularjs class from a [swagger specification file](https://github.com/wordnik/swagger-spec). The code is generated using [mustache templates](https://github.com/wcandillon/swagger-js-codegen/tree/master/templates) and is quality checked by [jshint](https://github.com/jshint/jshint/) and beautified by [js-beautify](https://github.com/beautify-web/js-beautify). +## Multi-class generation (Node) -The typescript generator is based on [superagent](https://github.com/visionmedia/superagent) and can be used for both nodejs and the browser via browserify/webpack. +It is possible now to generate multiple controllers for Node. -## Installation -```bash -npm install swagger-js-codegen +Each controller will have a class that has several methods inside of it. + +Each method represents an API, and has a default response built in. + +Also, generates the `definitions` classes. + +**Options:** + +`className` **[REQUIRED]**: name of the single generated class. You can put any name. + +`swagger` **[REQUIRED]**: loaded Swagger JSON file. + +`multiple` **[REQUIRED]**: this option should be provided and should be set to `true` if you need a multi-class output. + +`path` **[REQUIRED]**: location of the destination directories. `__dirname` is the best option, but you can provide your own destination path. + +`dir` **[OPTIONAL]**: this is the name of the destination directory for **controllers**. I recommend to use `routes` (used as default if this option was not provided). + +Directory with definitions will be **always** named `definitions`. + +**Multi-class generation example:** + +``` +const { CodeGen } = require('swagger-js-codegen'); +const fs = require('fs'); + +const file = 'swagger/swagger.json'; +const spec = JSON.parse(fs.readFileSync(file, 'UTF-8')); + +await CodeGen.getNodeCode({ + className: 'Service', + swagger: spec, + multiple: true, + path: __dirname, + dir: 'routes', +}); ``` +____ ## Example ```javascript var fs = require('fs'); @@ -80,24 +115,6 @@ In addition to the common options listed below, `getCustomCode()` *requires* a ` description: swagger object ``` -If it is required to generate multiple files for Node (i. e. multiple methods based on the initial JSON) provide the following options: - - multiple: - type: boolean - description: this option enables file splitting - path: - type: string - description: this option should contain the path to the project directory (__dirname) - example: '/Users/name/Projects/someProject/' - dir: - type: string - description: this option should contain the name of the directory with APIs - example: 'newAPIs' - -If `multiple` option is provided, `path` and `dir` options **are required** - -The `dir` folder will be created and generated files will be placed inside of it - ### Template Variables The following data are passed to the [mustache templates](https://github.com/janl/mustache.js): diff --git a/lib/codegen.js b/lib/codegen.js index 7f49d5df..e4b731c3 100644 --- a/lib/codegen.js +++ b/lib/codegen.js @@ -270,17 +270,28 @@ var getViewForSwagger1 = function(opts, type){ /** * Generate code based on the input file - * @param opts - options for the file generation + * @param options - options for the file generation * @param type - type of code / file to be generated (angular, custom, node, react, typescript) * @returns {*} */ -var getCode = function(opts, type) { - // check 'multiple' & all of the required parameters - if (opts.multiple) { - if (!(opts.dir && opts.path)) { - throw new Error('Missing some of the required parameters!'); - } +var getCode = function(options, type) { + // check 'multiple' & all of the required parameters + const opts = _.cloneDeep(options); + if (options.multiple) { + if (!options.className) { + throw new Error('Missing the class name!'); + } + if (!options.swagger) { + throw new Error('Missing the Swagger JSON!'); + } + if (!options.path) { + throw new Error('Missing the destination path!'); + } + if (!options.dir) { + opts.dir = 'routes'; + console.log('> swagger-js-codegen @ Directory name not provided, using \'routes\' as a default!'); } + } // For Swagger Specification version 2.0 value of field 'swagger' must be a string '2.0' var swaggerView = opts.swagger.swagger === '2.0' ? getViewForSwagger2(opts, type) : getViewForSwagger1(opts, type); diff --git a/package.json b/package.json index efedbbdd..e5b723f0 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "swagger-js-codegen", "main": "./lib/codegen.js", - "version": "1.12.0", + "version": "2.0.0", "description": "A Swagger codegen for JavaScript", "scripts": { "test": "grunt", @@ -13,9 +13,12 @@ "bugs": { "url": "https://github.com/wcandillon/swagger-js-codegen/issues" }, + "engines" : { + "node" : "^11.13.0" + }, "repository": { "type": "git", - "url": "git://github.com/wcandillon/swagger-js-codegen.git" + "url": "git://github.com/voicenter/swagger-js-codegen" }, "keywords": [ "swagger", From d6706d72202b650a51ea07ce6c15f0ab49fde754 Mon Sep 17 00:00:00 2001 From: Peter Date: Mon, 1 Apr 2019 15:32:10 +0300 Subject: [PATCH 37/58] Fix default directories for the generated files, update README --- README.md | 23 ++++++++++++++++++----- lib/codegen.js | 14 +++++++++----- lib/expose.js | 7 ++++--- 3 files changed, 31 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index edd92646..58a28a57 100644 --- a/README.md +++ b/README.md @@ -13,9 +13,21 @@ It is possible now to generate multiple controllers for Node. Each controller will have a class that has several methods inside of it. -Each method represents an API, and has a default response built in. +Each method represents an API, and has a default built-in response. -Also, generates the `definitions` classes. +Definitions are generated as well. + +**How it works:** + +Definitions are generated before the APIs. File `expose.js` generates all of the necessary definitions and places them in the destination directory. + +APIs are generated after that, based on the Mustache templates. + +Module utilizes the custom Mustache templates (`multi-class` and `multi-method`). + +Mustache generates a single file with a single ES5 class, that contains all of the methods. + +File `splitter.js` splits the single file into several files with classes (based on tags in the original JSON). After the split is completed and methods are combined, they are saved as a controller file in the destination directory. **Options:** @@ -27,9 +39,9 @@ Also, generates the `definitions` classes. `path` **[REQUIRED]**: location of the destination directories. `__dirname` is the best option, but you can provide your own destination path. -`dir` **[OPTIONAL]**: this is the name of the destination directory for **controllers**. I recommend to use `routes` (used as default if this option was not provided). +`controllersDirName` **[OPTIONAL]**: this is the name of the destination directory for **controllers**. `routes_generated` is the recommended name (it is used as default if this option was not provided). -Directory with definitions will be **always** named `definitions`. +`definitionsDirName` **[OPTIONAL]**: this is the name of the destination directory for **definitions**. `definitions_generated` is the recommended name (it is used as default if this option was not provided). **Multi-class generation example:** @@ -45,7 +57,8 @@ await CodeGen.getNodeCode({ swagger: spec, multiple: true, path: __dirname, - dir: 'routes', + controllersDirName: 'routes_generated', + definitionsDirName: 'definitions_generated', }); ``` diff --git a/lib/codegen.js b/lib/codegen.js index e4b731c3..a28037d2 100644 --- a/lib/codegen.js +++ b/lib/codegen.js @@ -287,9 +287,13 @@ var getCode = function(options, type) { if (!options.path) { throw new Error('Missing the destination path!'); } - if (!options.dir) { - opts.dir = 'routes'; - console.log('> swagger-js-codegen @ Directory name not provided, using \'routes\' as a default!'); + if (!options.controllersDirName) { + opts.controllersDirName = 'routes_generated'; + console.log('> swagger-js-codegen @ Controllers directory name not provided, using \'routes_generated\' as a default!'); + } + if (!options.definitionsDirName) { + opts.definitionsDirName = 'definitions_generated'; + console.log('> swagger-js-codegen @ Definitions directory name not provided, using \'definitions_generated\' as a default!'); } } @@ -300,7 +304,7 @@ var getCode = function(options, type) { var data = formatter.format(swaggerView); // expose definitions @ global scope - expose(opts.swagger.definitions, data.methods, opts.path); + expose(opts.swagger.definitions, data.methods, opts.path, opts.definitionsDirName); if (type === 'custom') { if (!_.isObject(opts.template) || !_.isString(opts.template.class) || !_.isString(opts.template.method)) { @@ -359,7 +363,7 @@ var getCode = function(options, type) { return splitter.split(beautify(source, { indent_size: 2, max_preserve_newlines: 2 - }), opts.className, opts.path, opts.dir); + }), opts.className, opts.path, opts.controllersDirName); } if (opts.beautify === undefined || opts.beautify === true) { return beautify(source, { indent_size: 4, max_preserve_newlines: 2 }); diff --git a/lib/expose.js b/lib/expose.js index 417a32e8..e036a3d2 100644 --- a/lib/expose.js +++ b/lib/expose.js @@ -6,9 +6,10 @@ const { inspect } = require('util'); * Expose definitions objects, create files with objects * @param {object} definitions - object that contain definitions objects * @param {array} methods - array of the available methods - * @param {string} path - where to generate the files, resulting path will be path/definitions + * @param {string} path - where to generate the files, resulting path will be path/ + * @param {string} directory - name of the directory */ -function expose(definitions, methods, path) { +function expose(definitions, methods, path, directory) { try { // get list of the definitions const list = Object.keys(definitions); @@ -19,7 +20,7 @@ function expose(definitions, methods, path) { } // make sure that ~/definitions directory exists - const container = `${path}/definitions`; + const container = `${path}/${directory}`; if (!fs.existsSync(container)) { fs.mkdirSync(container); } From d4269a4897c82948103c061dc171370dd52b22db Mon Sep 17 00:00:00 2001 From: Peter Date: Tue, 2 Apr 2019 14:32:48 +0300 Subject: [PATCH 38/58] Simple queries generation --- .jshintignore | 1 + lib/codegen.js | 16 +++++--- lib/querier.js | 69 +++++++++++++++++++++++++++++++++ templates/multi-method.mustache | 13 +++++-- 4 files changed, 90 insertions(+), 9 deletions(-) create mode 100644 lib/querier.js diff --git a/.jshintignore b/.jshintignore index 3e5791b3..eb945d85 100644 --- a/.jshintignore +++ b/.jshintignore @@ -1,4 +1,5 @@ lib/expose.js lib/formatter.js +lib/querier.js lib/splitter.js zz/ diff --git a/lib/codegen.js b/lib/codegen.js index a28037d2..e0f5d5eb 100644 --- a/lib/codegen.js +++ b/lib/codegen.js @@ -8,6 +8,7 @@ var _ = require('lodash'); var expose = require('./expose'); var formatter = require('./formatter'); +var querier = require('./querier'); var splitter = require('./splitter'); var ts = require('./typescript'); @@ -297,14 +298,17 @@ var getCode = function(options, type) { } } - // For Swagger Specification version 2.0 value of field 'swagger' must be a string '2.0' - var swaggerView = opts.swagger.swagger === '2.0' ? getViewForSwagger2(opts, type) : getViewForSwagger1(opts, type); + // For Swagger Specification version 2.0 value of field 'swagger' must be a string '2.0' + var swaggerView = opts.swagger.swagger === '2.0' ? getViewForSwagger2(opts, type) : getViewForSwagger1(opts, type); - // format the default responses for the APIs, add objects for the load - var data = formatter.format(swaggerView); + // format the default responses for the APIs, add objects for the load + var formatted = formatter.format(swaggerView); - // expose definitions @ global scope - expose(opts.swagger.definitions, data.methods, opts.path, opts.definitionsDirName); + // create definitions + expose(opts.swagger.definitions, formatted.methods, opts.path, opts.definitionsDirName); + + // add all of the necessary query options + var data = querier(formatted); if (type === 'custom') { if (!_.isObject(opts.template) || !_.isString(opts.template.class) || !_.isString(opts.template.method)) { diff --git a/lib/querier.js b/lib/querier.js new file mode 100644 index 00000000..76a70d7d --- /dev/null +++ b/lib/querier.js @@ -0,0 +1,69 @@ +const _ = require('lodash'); + +const specialKeys = ['Add', 'Create', 'Delete', 'Disable', 'Update']; + +/** + * Get the required properties for the query + * @param params {array} - array of the method parameters + * @returns {object} - { parameters: {string}, questions: {string} } + */ +function getProperties(params) { + const parameters = []; + const questions = []; + params.forEach((param) => { + if (param.in && param.in === 'path' || param.in === 'query') { + parameters.push(param.name); + } + if (param.in && param.in === 'body') { + const { '$ref': ref } = param.schema || {}; + if (ref) { + parameters.push(ref.split('/').slice(-1)[0]); + } + } + questions.push('?'); + }); + return { + parameters: parameters.join(', '), + questions: questions.join(', '), + } +} + +/** + * Create SQL query for the method + * @param data {object} - object with definitions & methods + * @returns {*} + */ +function querier(data) { + try { + const mutable = _.cloneDeep(data); + const { methods, definitions } = mutable; + + if (!(methods && methods.length > 0 && definitions && definitions.length > 0)) { + return new Error('Methods and definitions should not be empty!'); + } + + methods.forEach((method, m) => { + let special = false; + specialKeys.forEach((key) => { + if (method.methodName.includes(key)) { + special = true; + } + }); + + if (!special) { + const { parameters, questions } = getProperties(method.parameters); + mutable.methods[m].query = { + content: `const results = await dal.query("SELECT FN_${method.methodName}(${questions})", [${parameters}], { redis: true });`, + }; + } else { + + } + }); + + return mutable; + } catch (err) { + throw new Error(err.message || err); + } +} + +module.exports = querier; diff --git a/templates/multi-method.mustache b/templates/multi-method.mustache index f0ad567c..da4ea542 100644 --- a/templates/multi-method.mustache +++ b/templates/multi-method.mustache @@ -44,9 +44,16 @@ {{/isFormParameter}} {{/parameters}} - /* - Method logic - */ + {{#query}} + /* + {{&content}} + */ + {{/query}} + {{^query}} + /* + Method logic + */ + {{/query}} {{#responses}} {{#200}}{{{code}}}{{/200}} From d7b789a26a801aed53385ae9be75136a51a5707b Mon Sep 17 00:00:00 2001 From: Peter Date: Tue, 2 Apr 2019 17:00:45 +0300 Subject: [PATCH 39/58] Generate all queries --- lib/querier.js | 21 +++++---------------- templates/multi-method.mustache | 2 +- 2 files changed, 6 insertions(+), 17 deletions(-) diff --git a/lib/querier.js b/lib/querier.js index 76a70d7d..06003d73 100644 --- a/lib/querier.js +++ b/lib/querier.js @@ -11,15 +11,9 @@ function getProperties(params) { const parameters = []; const questions = []; params.forEach((param) => { - if (param.in && param.in === 'path' || param.in === 'query') { + if (param.name) { parameters.push(param.name); } - if (param.in && param.in === 'body') { - const { '$ref': ref } = param.schema || {}; - if (ref) { - parameters.push(ref.split('/').slice(-1)[0]); - } - } questions.push('?'); }); return { @@ -31,7 +25,7 @@ function getProperties(params) { /** * Create SQL query for the method * @param data {object} - object with definitions & methods - * @returns {*} + * @returns {object} - same as data, but with updated methods */ function querier(data) { try { @@ -50,14 +44,9 @@ function querier(data) { } }); - if (!special) { - const { parameters, questions } = getProperties(method.parameters); - mutable.methods[m].query = { - content: `const results = await dal.query("SELECT FN_${method.methodName}(${questions})", [${parameters}], { redis: true });`, - }; - } else { - - } + const { parameters, questions } = getProperties(method.parameters, special); + mutable.methods[m].query = `const results = await dal.query("${special ? 'CALL SP_' : 'SELECT FN_'}${method.methodName}(${questions})", ` + + `[${parameters}], { redis: ${!special} });`; }); return mutable; diff --git a/templates/multi-method.mustache b/templates/multi-method.mustache index da4ea542..cec0f2ad 100644 --- a/templates/multi-method.mustache +++ b/templates/multi-method.mustache @@ -46,7 +46,7 @@ {{#query}} /* - {{&content}} + {{&query}} */ {{/query}} {{^query}} From 58b6ee0fd2c979744d0900ab7f9f534a1b20f3bb Mon Sep 17 00:00:00 2001 From: Peter Date: Tue, 2 Apr 2019 17:08:32 +0300 Subject: [PATCH 40/58] Multi-method template update --- templates/multi-method.mustache | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/templates/multi-method.mustache b/templates/multi-method.mustache index cec0f2ad..af51ca06 100644 --- a/templates/multi-method.mustache +++ b/templates/multi-method.mustache @@ -10,27 +10,21 @@ {{#isQueryParameter}} {{#x-AuthFieldType}} var auth{{&x-AuthFieldType.propertyName}} = await_FieldValidator.validate('{{&x-AuthFieldType.value}}', req.query['{{&x-AuthFieldType.propertyName}}'], req, res); - if (!auth{{&x-AuthFieldType.propertyName}}) { - return; - } + if (!auth{{&x-AuthFieldType.propertyName}}) { return; } {{/x-AuthFieldType}} var {{name}} = req.query['{{name}}']; {{/isQueryParameter}} {{#isPathParameter}} {{#x-AuthFieldType}} var auth{{&x-AuthFieldType.propertyName}} = await_FieldValidator.validate('{{&x-AuthFieldType.value}}', req.params['{{&x-AuthFieldType.propertyName}}'], req, res); - if (!auth{{&x-AuthFieldType.propertyName}}) { - return; - } + if (!auth{{&x-AuthFieldType.propertyName}}) { return; } {{/x-AuthFieldType}} var {{name}} = req.params['{{name}}']; {{/isPathParameter}} {{#isBodyParameter}} {{#x-AuthFieldType}} var auth{{&x-AuthFieldType.propertyName}} = await_FieldValidator.validate('{{&x-AuthFieldType.value}}', req.body['{{&x-AuthFieldType.propertyName}}'], req, res); - if (!auth{{&x-AuthFieldType.propertyName}}) { - return; - } + if (!auth{{&x-AuthFieldType.propertyName}}) { return; } {{/x-AuthFieldType}} var {{name}} = new global.classes['{{tsType.target}}'](req, res, req.body); {{/isBodyParameter}} From 75f03476940aed5ba1f8f774feb6195de2329346 Mon Sep 17 00:00:00 2001 From: Peter Date: Thu, 4 Apr 2019 10:54:05 +0300 Subject: [PATCH 41/58] Fix typo --- lib/splitter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/splitter.js b/lib/splitter.js index c63923c5..cc5d5433 100644 --- a/lib/splitter.js +++ b/lib/splitter.js @@ -144,7 +144,7 @@ async function split(source, className, path, dir) { }); }); - return console.log('> swagger-js-codegen @ Success!'); + return console.log('> swagger-js-codegen @ SUCCESS!'); } catch (err) { throw new Error(err.message || err); } From 23e5089622df207f59cfd3466e2b318f438302a6 Mon Sep 17 00:00:00 2001 From: Peter Date: Tue, 9 Apr 2019 16:42:32 +0300 Subject: [PATCH 42/58] Add dal import, use try/catch in methods --- lib/splitter.js | 8 +++- templates/multi-method.mustache | 82 ++++++++++++++++++--------------- 2 files changed, 51 insertions(+), 39 deletions(-) diff --git a/lib/splitter.js b/lib/splitter.js index cc5d5433..106b2201 100644 --- a/lib/splitter.js +++ b/lib/splitter.js @@ -103,7 +103,13 @@ async function split(source, className, path, dir) { }); files[i].content = beautify(`'use strict'; - /* auto-generated: ${file.file}.controller.js */ + /* + auto-generated: ${file.file}.controller.js + */ + + /* + const dal = require('../../helpers/dal'); + */ module.exports = class { constructor(req = {}, res = {}, params = {}) { diff --git a/templates/multi-method.mustache b/templates/multi-method.mustache index af51ca06..c5ccb0e8 100644 --- a/templates/multi-method.mustache +++ b/templates/multi-method.mustache @@ -6,50 +6,56 @@ * @param {object} res - response object */ {{&className}}.prototype.{{&destination}}_{{&methodName}} = function(req, res) { - {{#parameters}} - {{#isQueryParameter}} - {{#x-AuthFieldType}} - var auth{{&x-AuthFieldType.propertyName}} = await_FieldValidator.validate('{{&x-AuthFieldType.value}}', req.query['{{&x-AuthFieldType.propertyName}}'], req, res); - if (!auth{{&x-AuthFieldType.propertyName}}) { return; } - {{/x-AuthFieldType}} - var {{name}} = req.query['{{name}}']; - {{/isQueryParameter}} - {{#isPathParameter}} - {{#x-AuthFieldType}} - var auth{{&x-AuthFieldType.propertyName}} = await_FieldValidator.validate('{{&x-AuthFieldType.value}}', req.params['{{&x-AuthFieldType.propertyName}}'], req, res); - if (!auth{{&x-AuthFieldType.propertyName}}) { return; } - {{/x-AuthFieldType}} - var {{name}} = req.params['{{name}}']; - {{/isPathParameter}} - {{#isBodyParameter}} - {{#x-AuthFieldType}} - var auth{{&x-AuthFieldType.propertyName}} = await_FieldValidator.validate('{{&x-AuthFieldType.value}}', req.body['{{&x-AuthFieldType.propertyName}}'], req, res); - if (!auth{{&x-AuthFieldType.propertyName}}) { return; } - {{/x-AuthFieldType}} - var {{name}} = new global.classes['{{tsType.target}}'](req, res, req.body); - {{/isBodyParameter}} - {{#isHeaderParameter}} - {{#isSingleton}}{{/isSingleton}} - {{^isSingleton}}{{/isSingleton}} - {{/isHeaderParameter}} - {{#isFormParameter}} - {{#isSingleton}}{{/isSingleton}} - {{^isSingleton}}{{/isSingleton}} - {{/isFormParameter}} - {{/parameters}} + try { +{{#parameters}} + {{#isQueryParameter}} + {{#x-AuthFieldType}} + var auth{{&x-AuthFieldType.propertyName}} = await_FieldValidator.validate('{{&x-AuthFieldType.value}}', req.query['{{&x-AuthFieldType.propertyName}}'], req, res); + if (!auth{{&x-AuthFieldType.propertyName}}) { return; } + {{/x-AuthFieldType}} + var {{name}} = req.query['{{name}}']; + {{/isQueryParameter}} + {{#isPathParameter}} + {{#x-AuthFieldType}} + var auth{{&x-AuthFieldType.propertyName}} = await_FieldValidator.validate('{{&x-AuthFieldType.value}}', req.params['{{&x-AuthFieldType.propertyName}}'], req, res); + if (!auth{{&x-AuthFieldType.propertyName}}) { return; } + {{/x-AuthFieldType}} + var {{name}} = req.params['{{name}}']; + {{/isPathParameter}} + {{#isBodyParameter}} + {{#x-AuthFieldType}} + var auth{{&x-AuthFieldType.propertyName}} = await_FieldValidator.validate('{{&x-AuthFieldType.value}}', req.body['{{&x-AuthFieldType.propertyName}}'], req, res); + if (!auth{{&x-AuthFieldType.propertyName}}) { return; } + {{/x-AuthFieldType}} + var {{name}} = new global.classes['{{tsType.target}}'](req, res, req.body); + {{/isBodyParameter}} + {{#isHeaderParameter}} + {{#isSingleton}}{{/isSingleton}} + {{^isSingleton}}{{/isSingleton}} + {{/isHeaderParameter}} + {{#isFormParameter}} + {{#isSingleton}}{{/isSingleton}} + {{^isSingleton}}{{/isSingleton}} + {{/isFormParameter}} +{{/parameters}} - {{#query}} + {{#query}} /* {{&query}} */ - {{/query}} - {{^query}} + {{/query}} + {{^query}} /* Method logic */ - {{/query}} + {{/query}} - {{#responses}} - {{#200}}{{{code}}}{{/200}} - {{/responses}} + {{#responses}} + {{#200}}{{{code}}}{{/200}} + {{/responses}} + } catch (err) { + /* + handle errors + */ + } }; From 9fe725b2892cf370c030c0f295c7e84b439aa186 Mon Sep 17 00:00:00 2001 From: Peter Date: Wed, 17 Apr 2019 17:07:04 +0300 Subject: [PATCH 43/58] AMQP: successfull response --- lib/formatter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/formatter.js b/lib/formatter.js index 794e4fa2..dc58cded 100644 --- a/lib/formatter.js +++ b/lib/formatter.js @@ -156,7 +156,7 @@ function format(data) { } }); - formatted[response].code = `return res.code(${formatted[response].status}).send(${inspect(responseObject, { showHidden: false, depth: null })});`; + formatted[response].code = `return global.response(req, res, ${formatted[response].status}, ${inspect(responseObject, { showHidden: false, depth: null })});`; }); // add the code to the resulting object From 14f0c7e5a0601708ac6d060c419b77eefe663419 Mon Sep 17 00:00:00 2001 From: Peter Date: Thu, 18 Apr 2019 11:45:07 +0300 Subject: [PATCH 44/58] Add error responses to the controllers --- lib/codegen.js | 616 ++++++++++++++++---------------- lib/formatter.js | 21 +- templates/multi-method.mustache | 6 +- 3 files changed, 328 insertions(+), 315 deletions(-) diff --git a/lib/codegen.js b/lib/codegen.js index e0f5d5eb..b9424fcf 100644 --- a/lib/codegen.js +++ b/lib/codegen.js @@ -13,260 +13,260 @@ var splitter = require('./splitter'); var ts = require('./typescript'); var normalizeName = function(id) { - return id.replace(/\.|\-|\{|\}|\s/g, '_'); + return id.replace(/\.|\-|\{|\}|\s/g, '_'); }; var getPathToMethodName = function(opts, m, path){ - if(path === '/' || path === '') { - return m; - } + if(path === '/' || path === '') { + return m; + } - // clean url path for requests ending with '/' - var cleanPath = path.replace(/\/$/, ''); + // clean url path for requests ending with '/' + var cleanPath = path.replace(/\/$/, ''); - var segments = cleanPath.split('/').slice(1); - segments = _.transform(segments, function (result, segment) { - if (segment[0] === '{' && segment[segment.length - 1] === '}') { - segment = 'by' + segment[1].toUpperCase() + segment.substring(2, segment.length - 1); - } - result.push(segment); - }); - var result = _.camelCase(segments.join('-')); - return m.toLowerCase() + result[0].toUpperCase() + result.substring(1); + var segments = cleanPath.split('/').slice(1); + segments = _.transform(segments, function (result, segment) { + if (segment[0] === '{' && segment[segment.length - 1] === '}') { + segment = 'by' + segment[1].toUpperCase() + segment.substring(2, segment.length - 1); + } + result.push(segment); + }); + var result = _.camelCase(segments.join('-')); + return m.toLowerCase() + result[0].toUpperCase() + result.substring(1); }; var getViewForSwagger2 = function(opts, type){ - var swagger = opts.swagger; - var methods = []; - var authorizedMethods = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'COPY', 'HEAD', 'OPTIONS', 'LINK', 'UNLIK', 'PURGE', 'LOCK', 'UNLOCK', 'PROPFIND']; - var data = { - isNode: type === 'node' || type === 'react', - isES6: opts.isES6 || type === 'react', - description: swagger.info.description, - isSecure: swagger.securityDefinitions !== undefined, - moduleName: opts.moduleName, - className: opts.className, - imports: opts.imports, - domain: (swagger.schemes && swagger.schemes.length > 0 && swagger.host && swagger.basePath) ? swagger.schemes[0] + '://' + swagger.host + swagger.basePath.replace(/\/+$/g,'') : '', - methods: [], - definitions: [] - }; - - _.forEach(swagger.paths, function(api, path) { - var globalParams = []; - /** - * @param {Object} op - meta data for the request - * @param {string} m - HTTP method name - eg: 'get', 'post', 'put', 'delete' - */ - _.forEach(api, function(op, m) { - if (m.toLowerCase() === 'parameters') { - globalParams = op; - } + var swagger = opts.swagger; + var methods = []; + var authorizedMethods = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'COPY', 'HEAD', 'OPTIONS', 'LINK', 'UNLIK', 'PURGE', 'LOCK', 'UNLOCK', 'PROPFIND']; + var data = { + isNode: type === 'node' || type === 'react', + isES6: opts.isES6 || type === 'react', + description: swagger.info.description, + isSecure: swagger.securityDefinitions !== undefined, + moduleName: opts.moduleName, + className: opts.className, + imports: opts.imports, + domain: (swagger.schemes && swagger.schemes.length > 0 && swagger.host && swagger.basePath) ? swagger.schemes[0] + '://' + swagger.host + swagger.basePath.replace(/\/+$/g,'') : '', + methods: [], + definitions: [] + }; + + _.forEach(swagger.paths, function(api, path) { + var globalParams = []; + /** + * @param {Object} op - meta data for the request + * @param {string} m - HTTP method name - eg: 'get', 'post', 'put', 'delete' + */ + _.forEach(api, function(op, m) { + if (m.toLowerCase() === 'parameters') { + globalParams = op; + } + }); + _.forEach(api, function(op, m) { + var M = m.toUpperCase(); + if (M === '' || authorizedMethods.indexOf(M) === -1) { + return; + } + var secureTypes = []; + if (swagger.securityDefinitions !== undefined || op.security !== undefined) { + var mergedSecurity = _.merge([], swagger.security, op.security).map(function(security) { + return Object.keys(security); }); - _.forEach(api, function(op, m) { - var M = m.toUpperCase(); - if (M === '' || authorizedMethods.indexOf(M) === -1) { - return; - } - var secureTypes = []; - if (swagger.securityDefinitions !== undefined || op.security !== undefined) { - var mergedSecurity = _.merge([], swagger.security, op.security).map(function(security) { - return Object.keys(security); - }); - if (swagger.securityDefinitions) { - for (var sk in swagger.securityDefinitions) { - if (mergedSecurity.join(',').indexOf(sk) !== -1) { - secureTypes.push(swagger.securityDefinitions[sk].type); - } - } - } - } - - var methodName = (op.operationId ? normalizeName(op.operationId) : getPathToMethodName(opts, m, path)); - - // Make sure the method name is unique - if (methods.indexOf(methodName) !== -1) { - var i = 1; - while (true) { - if (methods.indexOf(methodName + '_' + i) !== -1) { - i++; - } else { - methodName = methodName + '_' + i; - break; - } - } - } - methods.push(methodName); - - var method = { - path: path, - className: opts.className, - methodName: methodName, - method: M, - isGET: M === 'GET', - isPOST: M === 'POST', - summary: op.description || op.summary, - externalDocs: op.externalDocs, - isSecure: swagger.security !== undefined || op.security !== undefined, - isSecureToken: secureTypes.indexOf('oauth2') !== -1, - isSecureApiKey: secureTypes.indexOf('apiKey') !== -1, - isSecureBasic: secureTypes.indexOf('basic') !== -1, - parameters: [], - headers: [], - }; - - // add 'destination' field if 'multiple: true' - if (opts.multiple) { - method.destination = op.tags[0].toLowerCase(); - } - - // add 'responses' field, that contains schemas and descriptions - method.responses = op.responses; - - if(method.isSecure && method.isSecureToken) { - data.isSecureToken = method.isSecureToken; - } - if(method.isSecure && method.isSecureApiKey) { - data.isSecureApiKey = method.isSecureApiKey; - } - if(method.isSecure && method.isSecureBasic) { - data.isSecureBasic = method.isSecureBasic; - } - var produces = op.produces || swagger.produces; - if (produces) { - method.headers.push({ - name: 'Accept', - value: `'${produces.map(function(value) { return value; }).join(', ')}'`, - }); - } - - var consumes = op.consumes || swagger.consumes; - if(consumes) { - method.headers.push({name: 'Content-Type', value: '\'' + consumes + '\'' }); + if (swagger.securityDefinitions) { + for (var sk in swagger.securityDefinitions) { + if (mergedSecurity.join(',').indexOf(sk) !== -1) { + secureTypes.push(swagger.securityDefinitions[sk].type); } + } + } + } + + var methodName = (op.operationId ? normalizeName(op.operationId) : getPathToMethodName(opts, m, path)); + + // Make sure the method name is unique + if (methods.indexOf(methodName) !== -1) { + var i = 1; + while (true) { + if (methods.indexOf(methodName + '_' + i) !== -1) { + i++; + } else { + methodName = methodName + '_' + i; + break; + } + } + } + methods.push(methodName); - var params = []; - if(_.isArray(op.parameters)) { - params = op.parameters; - } - params = params.concat(globalParams); - _.forEach(params, function(parameter) { - //Ignore parameters which contain the x-exclude-from-bindings extension - if(parameter['x-exclude-from-bindings'] === true) { - return; - } - - // Ignore headers which are injected by proxies & app servers - // eg: https://cloud.google.com/appengine/docs/go/requests#Go_Request_headers - if (parameter['x-proxy-header'] && !data.isNode) { - return; - } - if (_.isString(parameter.$ref)) { - var segments = parameter.$ref.split('/'); - parameter = swagger.parameters[segments.length === 1 ? segments[0] : segments[2] ]; - } - parameter.camelCaseName = _.camelCase(parameter.name); - if(parameter.enum && parameter.enum.length === 1) { - parameter.isSingleton = true; - parameter.singleton = parameter.enum[0]; - } - if(parameter.in === 'body'){ - parameter.isBodyParameter = true; - } else if(parameter.in === 'path'){ - parameter.isPathParameter = true; - } else if(parameter.in === 'query'){ - if(parameter['x-name-pattern']){ - parameter.isPatternType = true; - parameter.pattern = parameter['x-name-pattern']; - } - parameter.isQueryParameter = true; - } else if(parameter.in === 'header'){ - parameter.isHeaderParameter = true; - } else if(parameter.in === 'formData'){ - parameter.isFormParameter = true; - } - parameter.tsType = ts.convertType(parameter); - parameter.cardinality = parameter.required ? '' : '?'; - method.parameters.push(parameter); - }); - data.methods.push(method); + var method = { + path: path, + className: opts.className, + methodName: methodName, + method: M, + isGET: M === 'GET', + isPOST: M === 'POST', + summary: op.description || op.summary, + externalDocs: op.externalDocs, + isSecure: swagger.security !== undefined || op.security !== undefined, + isSecureToken: secureTypes.indexOf('oauth2') !== -1, + isSecureApiKey: secureTypes.indexOf('apiKey') !== -1, + isSecureBasic: secureTypes.indexOf('basic') !== -1, + parameters: [], + headers: [], + }; + + // add 'destination' field if 'multiple: true' + if (opts.multiple) { + method.destination = op.tags[0].toLowerCase(); + } + + // add 'responses' field, that contains schemas and descriptions + method.responses = op.responses; + + if(method.isSecure && method.isSecureToken) { + data.isSecureToken = method.isSecureToken; + } + if(method.isSecure && method.isSecureApiKey) { + data.isSecureApiKey = method.isSecureApiKey; + } + if(method.isSecure && method.isSecureBasic) { + data.isSecureBasic = method.isSecureBasic; + } + var produces = op.produces || swagger.produces; + if (produces) { + method.headers.push({ + name: 'Accept', + value: `'${produces.map(function(value) { return value; }).join(', ')}'`, }); + } + + var consumes = op.consumes || swagger.consumes; + if(consumes) { + method.headers.push({name: 'Content-Type', value: '\'' + consumes + '\'' }); + } + + var params = []; + if(_.isArray(op.parameters)) { + params = op.parameters; + } + params = params.concat(globalParams); + _.forEach(params, function(parameter) { + //Ignore parameters which contain the x-exclude-from-bindings extension + if(parameter['x-exclude-from-bindings'] === true) { + return; + } + + // Ignore headers which are injected by proxies & app servers + // eg: https://cloud.google.com/appengine/docs/go/requests#Go_Request_headers + if (parameter['x-proxy-header'] && !data.isNode) { + return; + } + if (_.isString(parameter.$ref)) { + var segments = parameter.$ref.split('/'); + parameter = swagger.parameters[segments.length === 1 ? segments[0] : segments[2] ]; + } + parameter.camelCaseName = _.camelCase(parameter.name); + if(parameter.enum && parameter.enum.length === 1) { + parameter.isSingleton = true; + parameter.singleton = parameter.enum[0]; + } + if(parameter.in === 'body'){ + parameter.isBodyParameter = true; + } else if(parameter.in === 'path'){ + parameter.isPathParameter = true; + } else if(parameter.in === 'query'){ + if(parameter['x-name-pattern']){ + parameter.isPatternType = true; + parameter.pattern = parameter['x-name-pattern']; + } + parameter.isQueryParameter = true; + } else if(parameter.in === 'header'){ + parameter.isHeaderParameter = true; + } else if(parameter.in === 'formData'){ + parameter.isFormParameter = true; + } + parameter.tsType = ts.convertType(parameter); + parameter.cardinality = parameter.required ? '' : '?'; + method.parameters.push(parameter); + }); + data.methods.push(method); }); + }); - _.forEach(swagger.definitions, function(definition, name) { - data.definitions.push({ - name: name, - description: definition.description, - tsType: ts.convertType(definition, swagger) - }); + _.forEach(swagger.definitions, function(definition, name) { + data.definitions.push({ + name: name, + description: definition.description, + tsType: ts.convertType(definition, swagger) }); + }); - return data; + return data; }; var getViewForSwagger1 = function(opts, type){ - var swagger = opts.swagger; - var data = { - isNode: type === 'node' || type === 'react', - isES6: opts.isES6 || type === 'react', - description: swagger.description, - moduleName: opts.moduleName, + var swagger = opts.swagger; + var data = { + isNode: type === 'node' || type === 'react', + isES6: opts.isES6 || type === 'react', + description: swagger.description, + moduleName: opts.moduleName, + className: opts.className, + domain: swagger.basePath ? swagger.basePath : '', + methods: [] + }; + swagger.apis.forEach(function(api){ + api.operations.forEach(function(op){ + if (op.method === 'OPTIONS') { + return; + } + var method = { + path: api.path, className: opts.className, - domain: swagger.basePath ? swagger.basePath : '', - methods: [] - }; - swagger.apis.forEach(function(api){ - api.operations.forEach(function(op){ - if (op.method === 'OPTIONS') { - return; - } - var method = { - path: api.path, - className: opts.className, - methodName: op.nickname, - method: op.method, - isGET: op.method === 'GET', - isPOST: op.method.toUpperCase() === 'POST', - summary: op.summary, - parameters: op.parameters, - headers: [] - }; - - if(op.produces) { - var headers = []; - headers.value = []; - headers.name = 'Accept'; - headers.value.push(op.produces.map(function(value) { return '\'' + value + '\''; }).join(', ')); - method.headers.push(headers); - } - - op.parameters = op.parameters ? op.parameters : []; - op.parameters.forEach(function(parameter) { - parameter.camelCaseName = _.camelCase(parameter.name); - if(parameter.enum && parameter.enum.length === 1) { - parameter.isSingleton = true; - parameter.singleton = parameter.enum[0]; - } - if(parameter.paramType === 'body'){ - parameter.isBodyParameter = true; - } else if(parameter.paramType === 'path'){ - parameter.isPathParameter = true; - } else if(parameter.paramType === 'query'){ - if(parameter['x-name-pattern']){ - parameter.isPatternType = true; - parameter.pattern = parameter['x-name-pattern']; - } - parameter.isQueryParameter = true; - } else if(parameter.paramType === 'header'){ - parameter.isHeaderParameter = true; - } else if(parameter.paramType === 'form'){ - parameter.isFormParameter = true; - } - }); - data.methods.push(method); - }); + methodName: op.nickname, + method: op.method, + isGET: op.method === 'GET', + isPOST: op.method.toUpperCase() === 'POST', + summary: op.summary, + parameters: op.parameters, + headers: [] + }; + + if(op.produces) { + var headers = []; + headers.value = []; + headers.name = 'Accept'; + headers.value.push(op.produces.map(function(value) { return '\'' + value + '\''; }).join(', ')); + method.headers.push(headers); + } + + op.parameters = op.parameters ? op.parameters : []; + op.parameters.forEach(function(parameter) { + parameter.camelCaseName = _.camelCase(parameter.name); + if(parameter.enum && parameter.enum.length === 1) { + parameter.isSingleton = true; + parameter.singleton = parameter.enum[0]; + } + if(parameter.paramType === 'body'){ + parameter.isBodyParameter = true; + } else if(parameter.paramType === 'path'){ + parameter.isPathParameter = true; + } else if(parameter.paramType === 'query'){ + if(parameter['x-name-pattern']){ + parameter.isPatternType = true; + parameter.pattern = parameter['x-name-pattern']; + } + parameter.isQueryParameter = true; + } else if(parameter.paramType === 'header'){ + parameter.isHeaderParameter = true; + } else if(parameter.paramType === 'form'){ + parameter.isFormParameter = true; + } + }); + data.methods.push(method); }); - return data; + }); + return data; }; /** @@ -310,89 +310,89 @@ var getCode = function(options, type) { // add all of the necessary query options var data = querier(formatted); - if (type === 'custom') { - if (!_.isObject(opts.template) || !_.isString(opts.template.class) || !_.isString(opts.template.method)) { - throw new Error('Unprovided custom template. Please use the following template: template: { class: "...", method: "...", request: "..." }'); - } - } else { - if (!_.isObject(opts.template)) { - opts.template = {}; - } - var templates = __dirname + '/../templates/'; - - // choose templates based on the 'multiple' option TODO: Typescript support? - if (opts.multiple) { - opts.template.class = fs.readFileSync(templates + 'multi-class.mustache', 'utf-8'); - opts.template.method = fs.readFileSync(templates + 'multi-method.mustache', 'utf-8'); - } else { - opts.template.class = opts.template.class || fs.readFileSync(templates + type + '-class.mustache', 'utf-8'); - opts.template.method = opts.template.method || fs.readFileSync(templates + (type === 'typescript' ? 'typescript-' : '') + 'method.mustache', 'utf-8'); - } - if (type === 'typescript') { - opts.template.type = opts.template.type || fs.readFileSync(templates + 'type.mustache', 'utf-8'); - } - } - - if (opts.mustache) { - _.assign(data, opts.mustache); - } - - var source = Mustache.render(opts.template.class, data, opts.template); - var lintOptions = { - node: type === 'node' || type === 'custom', - browser: type === 'angular' || type === 'custom' || type === 'react', - undef: true, - strict: true, - trailing: true, - smarttabs: true, - maxerr: 999 - }; - if (opts.esnext) { - lintOptions.esnext = true; + if (type === 'custom') { + if (!_.isObject(opts.template) || !_.isString(opts.template.class) || !_.isString(opts.template.method)) { + throw new Error('Unprovided custom template. Please use the following template: template: { class: "...", method: "...", request: "..." }'); } - - if(type === 'typescript') { - opts.lint = false; + } else { + if (!_.isObject(opts.template)) { + opts.template = {}; } + var templates = __dirname + '/../templates/'; - if (opts.lint === undefined || opts.lint === true) { - lint(source, lintOptions); - lint.errors.forEach(function(error) { - if (error.code[0] === 'E') { - throw new Error(error.reason + ' in ' + error.evidence + ' (' + error.code + ')'); - } - }); - } + // choose templates based on the 'multiple' option TODO: Typescript support? if (opts.multiple) { - return splitter.split(beautify(source, { - indent_size: 2, - max_preserve_newlines: 2 - }), opts.className, opts.path, opts.controllersDirName); - } - if (opts.beautify === undefined || opts.beautify === true) { - return beautify(source, { indent_size: 4, max_preserve_newlines: 2 }); + opts.template.class = fs.readFileSync(templates + 'multi-class.mustache', 'utf-8'); + opts.template.method = fs.readFileSync(templates + 'multi-method.mustache', 'utf-8'); } else { - return source; + opts.template.class = opts.template.class || fs.readFileSync(templates + type + '-class.mustache', 'utf-8'); + opts.template.method = opts.template.method || fs.readFileSync(templates + (type === 'typescript' ? 'typescript-' : '') + 'method.mustache', 'utf-8'); } + if (type === 'typescript') { + opts.template.type = opts.template.type || fs.readFileSync(templates + 'type.mustache', 'utf-8'); + } + } + + if (opts.mustache) { + _.assign(data, opts.mustache); + } + + var source = Mustache.render(opts.template.class, data, opts.template); + var lintOptions = { + node: type === 'node' || type === 'custom', + browser: type === 'angular' || type === 'custom' || type === 'react', + undef: true, + strict: true, + trailing: true, + smarttabs: true, + maxerr: 999 + }; + if (opts.esnext) { + lintOptions.esnext = true; + } + + if(type === 'typescript') { + opts.lint = false; + } + + if (opts.lint === undefined || opts.lint === true) { + lint(source, lintOptions); + lint.errors.forEach(function(error) { + if (error.code[0] === 'E') { + throw new Error(error.reason + ' in ' + error.evidence + ' (' + error.code + ')'); + } + }); + } + if (opts.multiple) { + return splitter.split(beautify(source, { + indent_size: 2, + max_preserve_newlines: 2 + }), opts.className, opts.path, opts.controllersDirName); + } + if (opts.beautify === undefined || opts.beautify === true) { + return beautify(source, { indent_size: 4, max_preserve_newlines: 2 }); + } else { + return source; + } }; exports.CodeGen = { - getTypescriptCode: function(opts){ - if (opts.swagger.swagger !== '2.0') { - throw 'Typescript is only supported for Swagger 2.0 specs.'; - } - return getCode(opts, 'typescript'); - }, - getAngularCode: function(opts){ - return getCode(opts, 'angular'); - }, - getNodeCode: function(opts){ - return getCode(opts, 'node'); - }, - getReactCode: function(opts){ - return getCode(opts, 'react'); - }, - getCustomCode: function(opts){ - return getCode(opts, 'custom'); + getTypescriptCode: function(opts){ + if (opts.swagger.swagger !== '2.0') { + throw 'Typescript is only supported for Swagger 2.0 specs.'; } + return getCode(opts, 'typescript'); + }, + getAngularCode: function(opts){ + return getCode(opts, 'angular'); + }, + getNodeCode: function(opts){ + return getCode(opts, 'node'); + }, + getReactCode: function(opts){ + return getCode(opts, 'react'); + }, + getCustomCode: function(opts){ + return getCode(opts, 'custom'); + } }; diff --git a/lib/formatter.js b/lib/formatter.js index dc58cded..fac9a8a3 100644 --- a/lib/formatter.js +++ b/lib/formatter.js @@ -5,9 +5,10 @@ const _ = require('lodash'); * Parse nested schemas to build the proper response structure * @param {object} entry - entry definition object * @param {array} definitions - all of the definitions + * @param {number} status - response status * @returns {object} */ -function responseBuilder(entry, definitions) { +function responseBuilder(entry, definitions, status) { try { const response = {}; @@ -23,6 +24,18 @@ function responseBuilder(entry, definitions) { properties.forEach((property) => { if (property.tsType === 'string') { response[property.name] = property.example || 'string'; + + // this is done for a particular case + // TODO: proper error messages mapping - sligtly change the formatter + if (status >= 400) { + if (property.name === 'description') { + response[property.name] = 'ERROR_DESCRIPTION'; + } + if (property.name === 'reasonPhrase') { + response[property.name] = 'BAD_REQUEST'; + } + } + } if (property.tsType === 'number') { @@ -41,7 +54,7 @@ function responseBuilder(entry, definitions) { definitions.forEach((definition) => { if (definition.name === target) { // recursive call to unwrap all of the refs - response[property.name] = [responseBuilder(definition, definitions)]; + response[property.name] = [responseBuilder(definition, definitions, status)]; } }); } else { @@ -62,7 +75,7 @@ function responseBuilder(entry, definitions) { definitions.forEach((definition) => { if (definition.name === property.target) { // recursive call to unwrap all of the refs - response[property.name] = responseBuilder(definition, definitions); + response[property.name] = responseBuilder(definition, definitions, status); } }); } @@ -152,7 +165,7 @@ function format(data) { let responseObject = {}; definitions.forEach((def) => { if (def.name === ref) { - responseObject = responseBuilder(def, definitions); + responseObject = responseBuilder(def, definitions, formatted[response].status); } }); diff --git a/templates/multi-method.mustache b/templates/multi-method.mustache index c5ccb0e8..791e9003 100644 --- a/templates/multi-method.mustache +++ b/templates/multi-method.mustache @@ -54,8 +54,8 @@ {{#200}}{{{code}}}{{/200}} {{/responses}} } catch (err) { - /* - handle errors - */ + {{#responses}} + {{#400}}{{{code}}}{{/400}} + {{/responses}} } }; From 5d2b5b8ebe8afb1adb2b09e4d3e5d01de5e11340 Mon Sep 17 00:00:00 2001 From: Peter Date: Thu, 18 Apr 2019 11:45:45 +0300 Subject: [PATCH 45/58] Node version ^ --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e5b723f0..cc496c87 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "url": "https://github.com/wcandillon/swagger-js-codegen/issues" }, "engines" : { - "node" : "^11.13.0" + "node" : "^11.14.0" }, "repository": { "type": "git", From 08c0dbfde9c952e52738be1640b2714411323d6e Mon Sep 17 00:00:00 2001 From: Peter Date: Thu, 18 Apr 2019 11:58:19 +0300 Subject: [PATCH 46/58] Pass error to the response function --- lib/formatter.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/formatter.js b/lib/formatter.js index fac9a8a3..d462eef2 100644 --- a/lib/formatter.js +++ b/lib/formatter.js @@ -169,7 +169,9 @@ function format(data) { } }); - formatted[response].code = `return global.response(req, res, ${formatted[response].status}, ${inspect(responseObject, { showHidden: false, depth: null })});`; + const isErr = formatted[response].status >= 400 ? 'err' : 'null'; + formatted[response].code = 'return global.response(req, res, ' + + `${formatted[response].status}, ${inspect(responseObject, { showHidden: false, depth: null })}, ${isErr});`; }); // add the code to the resulting object From d9557d3770a996467b48fc525bff08585dca2320 Mon Sep 17 00:00:00 2001 From: Tzachi Shirazi Date: Wed, 24 Apr 2019 14:41:41 +0300 Subject: [PATCH 47/58] added alias to fn querier. expose data. --- lib/expose.js | 25 +++++------ lib/querier.js | 73 ++++++++++++++++++--------------- templates/multi-method.mustache | 1 + 3 files changed, 55 insertions(+), 44 deletions(-) diff --git a/lib/expose.js b/lib/expose.js index e036a3d2..2d8f06ee 100644 --- a/lib/expose.js +++ b/lib/expose.js @@ -40,30 +40,30 @@ function expose(definitions, methods, path, directory) { if (items['$ref']) { const refName = items['$ref'].split('/').slice(-1)[0]; parameters = `${parameters} - this['${prop}'] = []; + this.data['${prop}'] = []; if (params['${prop}'].length && params['${prop}'].length > 0) { params['${prop}'].forEach((object) => { const ${refName} = new global.classes['${refName}'](req, res, object); - this.${prop}.push(${refName}); + this.data.${prop}.push(${refName}); }); }`; } else { parameters = `${parameters} - this['${prop}'] = req.body['${prop}'];`; + this.data['${prop}'] = req.body['${prop}'];`; } } else { parameters = `${parameters} - this['${prop}'] = req.body['${prop}'];`; + this.data['${prop}'] = req.body['${prop}'];`; } } else { parameters = `${parameters} - this['${prop}'] = req.body['${prop}'];`; + this.data['${prop}'] = req.body['${prop}'];`; } } else { if (definitions[definition].properties[prop]['$ref']) { const refName = definitions[definition].properties[prop]['$ref'].split('/').slice(-1)[0]; parameters = `${parameters} - this['${prop}'] = new global.classes['${refName}'](req, res, req.body['${prop}']);`; + this.data['${prop}'] = new global.classes['${refName}'](req, res, req.body['${prop}']);`; } } }); @@ -137,6 +137,7 @@ function expose(definitions, methods, path, directory) { this.req = req; this.res = res; this.params = params; + this.data = {}; ${parameters} ${validation} this.schema = ${inspect(definitions[definition], { showHidden: false, depth: null })}; @@ -151,12 +152,12 @@ function expose(definitions, methods, path, directory) { // create file in the destination folder fs.writeFileSync(`${destination}/${definition}.js`, - beauty(content, { indent_size: 2 }), - (err) => { - if (err) { - throw new Error(err.message || err); - } - }); + beauty(content, { indent_size: 2 }), + (err) => { + if (err) { + throw new Error(err.message || err); + } + }); }); } catch (err) { throw new Error(err.message || err); diff --git a/lib/querier.js b/lib/querier.js index 06003d73..9904221b 100644 --- a/lib/querier.js +++ b/lib/querier.js @@ -8,18 +8,21 @@ const specialKeys = ['Add', 'Create', 'Delete', 'Disable', 'Update']; * @returns {object} - { parameters: {string}, questions: {string} } */ function getProperties(params) { - const parameters = []; - const questions = []; - params.forEach((param) => { - if (param.name) { - parameters.push(param.name); + const parameters = []; + const questions = []; + params.forEach((param) => { + if (param.name) { + if(param.in === 'body') + parameters.push(param.name + '.data'); + else + parameters.push(param.name); + } + questions.push('?'); + }); + return { + parameters: parameters.join(', '), + questions: questions.join(', '), } - questions.push('?'); - }); - return { - parameters: parameters.join(', '), - questions: questions.join(', '), - } } /** @@ -28,31 +31,37 @@ function getProperties(params) { * @returns {object} - same as data, but with updated methods */ function querier(data) { - try { - const mutable = _.cloneDeep(data); - const { methods, definitions } = mutable; - - if (!(methods && methods.length > 0 && definitions && definitions.length > 0)) { - return new Error('Methods and definitions should not be empty!'); - } + try { + const mutable = _.cloneDeep(data); + const { methods, definitions } = mutable; - methods.forEach((method, m) => { - let special = false; - specialKeys.forEach((key) => { - if (method.methodName.includes(key)) { - special = true; + if (!(methods && methods.length > 0 && definitions && definitions.length > 0)) { + return new Error('Methods and definitions should not be empty!'); } - }); - const { parameters, questions } = getProperties(method.parameters, special); - mutable.methods[m].query = `const results = await dal.query("${special ? 'CALL SP_' : 'SELECT FN_'}${method.methodName}(${questions})", ` - + `[${parameters}], { redis: ${!special} });`; - }); + methods.forEach((method, m) => { + let special = false; + specialKeys.forEach((key) => { + if (method.methodName.includes(key)) { + special = true; + } + }); + + const { parameters, questions } = getProperties(method.parameters, special); + let query =''; + if(special){ + query = `CALL SP_${method.methodName}(${questions})`; + }else{ + query = `SELECT FN_${method.methodName}(${questions}) as Response`; + } + mutable.methods[m].query = `const results = await dal.query("${query}", ` + + `[${parameters}], { redis: ${!special} });`; + }); - return mutable; - } catch (err) { - throw new Error(err.message || err); - } + return mutable; + } catch (err) { + throw new Error(err.message || err); + } } module.exports = querier; diff --git a/templates/multi-method.mustache b/templates/multi-method.mustache index 791e9003..3a2bc64f 100644 --- a/templates/multi-method.mustache +++ b/templates/multi-method.mustache @@ -42,6 +42,7 @@ {{#query}} /* {{&query}} + return res.code(results.Status).send(results); */ {{/query}} {{^query}} From 4362bfcec842285b69660e30f6055b2d4c826d12 Mon Sep 17 00:00:00 2001 From: Tzachi Shirazi Date: Thu, 6 Jun 2019 12:58:07 +0300 Subject: [PATCH 48/58] added specialKeys fix sub Object --- lib/expose.js | 21 ++++++++++++++------- lib/querier.js | 2 +- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/lib/expose.js b/lib/expose.js index 2d8f06ee..44e99e1f 100644 --- a/lib/expose.js +++ b/lib/expose.js @@ -32,38 +32,45 @@ function expose(definitions, methods, path, directory) { const props = Object.keys(definitions[definition].properties); if (props.length && props.length > 0) { props.forEach((prop) => { - const { type } = definitions[definition].properties[prop]; + const { type,format } = definitions[definition].properties[prop]; if (type) { - if (type === 'array') { + if (type === 'string' && format === 'date-time') { + parameters = `${parameters} + this.data['${prop}'] = params['${prop}'].replace(/T/, ' ').replace('Z', '');`; + }else if (type === 'array') { const { items } = definitions[definition].properties[prop]; if (items) { if (items['$ref']) { const refName = items['$ref'].split('/').slice(-1)[0]; + Array.isArray() parameters = `${parameters} this.data['${prop}'] = []; + if(!params['${prop}'])params['${prop}'] = [] if (params['${prop}'].length && params['${prop}'].length > 0) { params['${prop}'].forEach((object) => { const ${refName} = new global.classes['${refName}'](req, res, object); - this.data.${prop}.push(${refName}); + this.data.${prop}.push(${refName}.data); }); }`; } else { parameters = `${parameters} - this.data['${prop}'] = req.body['${prop}'];`; + this.data['${prop}'] = params['${prop}'];`; } } else { parameters = `${parameters} - this.data['${prop}'] = req.body['${prop}'];`; + this.data['${prop}'] = params['${prop}'];`; } } else { parameters = `${parameters} - this.data['${prop}'] = req.body['${prop}'];`; + this.data['${prop}'] = params['${prop}'];`; } } else { if (definitions[definition].properties[prop]['$ref']) { const refName = definitions[definition].properties[prop]['$ref'].split('/').slice(-1)[0]; parameters = `${parameters} - this.data['${prop}'] = new global.classes['${refName}'](req, res, req.body['${prop}']);`; + this['${refName}'] = new global.classes['${refName}'](req, res, params['${prop}']); + this.data['${prop}'] = this['${refName}'].data; + `; } } }); diff --git a/lib/querier.js b/lib/querier.js index 9904221b..0a16425c 100644 --- a/lib/querier.js +++ b/lib/querier.js @@ -1,6 +1,6 @@ const _ = require('lodash'); -const specialKeys = ['Add', 'Create', 'Delete', 'Disable', 'Update']; +const specialKeys = ['Add', 'Create', 'Delete', 'Disable', 'Update', 'Assign', 'Unassign']; /** * Get the required properties for the query From 75cc42a9647465de217216d74d79b88917b694eb Mon Sep 17 00:00:00 2001 From: serhii mamedov Date: Tue, 29 Oct 2019 18:30:20 +0200 Subject: [PATCH 49/58] generate bug fix --- lib/formatter.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/formatter.js b/lib/formatter.js index d462eef2..c42692b7 100644 --- a/lib/formatter.js +++ b/lib/formatter.js @@ -126,7 +126,9 @@ function format(data) { if (list.length > 0) { list.forEach((response) => { formatted[response] = method.responses[response]; - const refName = formatted[response].schema['$ref'].split('/').slice(-1)[0]; + const refName = formatted[response].schema.items ? + formatted[response].schema.items['$ref'].split('/').slice(-1)[0] : + formatted[response].schema['$ref'].split('/').slice(-1)[0]; definitions.forEach((definition) => { // copy properties if (refName === definition.name) { From 0ad9f125e2232765a6bc95da30787ae9e34ad2ad Mon Sep 17 00:00:00 2001 From: serhii mamedov Date: Tue, 29 Oct 2019 18:40:00 +0200 Subject: [PATCH 50/58] generate bug fix 2 --- lib/formatter.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/formatter.js b/lib/formatter.js index c42692b7..79acfff1 100644 --- a/lib/formatter.js +++ b/lib/formatter.js @@ -163,7 +163,9 @@ function format(data) { // generate the code list.forEach((response) => { - const ref = formatted[response].schema['$ref'].split('/').slice(-1)[0]; + const ref = formatted[response].schema.items ? + formatted[response].schema.items['$ref'].split('/').slice(-1)[0] : + formatted[response].schema['$ref'].split('/').slice(-1)[0]; let responseObject = {}; definitions.forEach((def) => { if (def.name === ref) { From 9945584d4e94317b0038994b192aa4e4b098292b Mon Sep 17 00:00:00 2001 From: serhii mamedov Date: Thu, 7 Nov 2019 14:50:17 +0200 Subject: [PATCH 51/58] generate bug fix 3 --- lib/formatter.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/formatter.js b/lib/formatter.js index 79acfff1..c5900afc 100644 --- a/lib/formatter.js +++ b/lib/formatter.js @@ -151,7 +151,9 @@ function format(data) { // add object references to the method (load objects as local variables inside the controller) method.parameters.forEach((parameter) => { if (parameter.schema) { - const reference = parameter.schema['$ref'].split('/').slice(-1)[0]; + const reference = parameter.schema['$ref'] ? + parameter.schema['$ref'].split('/').slice(-1)[0] : + parameter.schema.items['$ref'].split('/').slice(-1)[0]; objects.push({ destination: method.destination, origin: parameter['in'] === 'path' ? 'params' : parameter['in'], From 2e3e131e319ee1f1439b3e2fcd34bb88c9c86508 Mon Sep 17 00:00:00 2001 From: Voicenter Date: Thu, 23 Jul 2020 13:46:34 +0300 Subject: [PATCH 52/58] Add type to validate function --- lib/expose.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/expose.js b/lib/expose.js index 44e99e1f..ee9f0c07 100644 --- a/lib/expose.js +++ b/lib/expose.js @@ -90,6 +90,7 @@ function expose(definitions, methods, path, directory) { definition, property, value: definitions[definition].properties[property]['x-AuthFieldType'], + parameter_type:definitions[definition].properties[property].type }); } }); @@ -107,6 +108,7 @@ function expose(definitions, methods, path, directory) { definition, property, value: property['x-AuthFieldType'], + parameter_type:property.type }); } }) @@ -121,9 +123,9 @@ function expose(definitions, methods, path, directory) { validation = ''; secure.forEach((property) => { let origin = property.type; - if (origin === 'path') origin = 'params'; + if (origin === 'path') {origin = 'req.params' }else {origin = 'this.data'} validation = `${validation} - global.FieldValidator.validate('${property.value}', req.${origin}['${property.property}'], req, res) + global.FieldValidator.validate('${property.value}','${property.parameter_type}',${origin}['${property.property}'], req, res) .then((result) => { if (!result) { return; From a0243a6a8cae4841aa6f69aeca2baaa0b661636b Mon Sep 17 00:00:00 2001 From: Bohdan2000 <39126452+Bohdan2000@users.noreply.github.com> Date: Fri, 31 Jul 2020 12:14:54 +0300 Subject: [PATCH 53/58] Update expose.js added async validation function --- lib/expose.js | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/expose.js b/lib/expose.js index ee9f0c07..01f2b5f4 100644 --- a/lib/expose.js +++ b/lib/expose.js @@ -117,6 +117,7 @@ function expose(definitions, methods, path, directory) { }); } + // add validation let validation = ''; if (secure.length > 0) { @@ -124,16 +125,15 @@ function expose(definitions, methods, path, directory) { secure.forEach((property) => { let origin = property.type; if (origin === 'path') {origin = 'req.params' }else {origin = 'this.data'} - validation = `${validation} - global.FieldValidator.validate('${property.value}','${property.parameter_type}',${origin}['${property.property}'], req, res) - .then((result) => { - if (!result) { - return; - } - }) - .catch((err) => { - return; - }); + validation = ` + async validate() { + try { + ${origin}['${property.property}'] = await global.FieldValidator.validate('${property.value}','${property.parameter_type}',${origin}['${property.property}'], this.req, this.res) + } catch (error) { + console.log('validation error', error); + throw new Error(error.message); + } + } `; }); } @@ -148,9 +148,9 @@ function expose(definitions, methods, path, directory) { this.params = params; this.data = {}; ${parameters} - ${validation} this.schema = ${inspect(definitions[definition], { showHidden: false, depth: null })}; } + ${validation} };`; // make sure that destination definition directory exists From 6c049d5064a9eac6f2527bee736663acf666fad2 Mon Sep 17 00:00:00 2001 From: Dmitry Sam Date: Thu, 8 Apr 2021 16:40:11 +0300 Subject: [PATCH 54/58] use validators list instead of one validation --- lib/expose.js | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/lib/expose.js b/lib/expose.js index 01f2b5f4..8768efe9 100644 --- a/lib/expose.js +++ b/lib/expose.js @@ -117,25 +117,26 @@ function expose(definitions, methods, path, directory) { }); } - // add validation let validation = ''; + let validators = []; if (secure.length > 0) { - validation = ''; secure.forEach((property) => { let origin = property.type; - if (origin === 'path') {origin = 'req.params' }else {origin = 'this.data'} - validation = ` - async validate() { - try { - ${origin}['${property.property}'] = await global.FieldValidator.validate('${property.value}','${property.parameter_type}',${origin}['${property.property}'], this.req, this.res) - } catch (error) { - console.log('validation error', error); - throw new Error(error.message); - } - } - `; + if (origin === 'path') { origin = 'req.params' } else { origin = 'this.data' }; + validators.push(`${origin}['${property.property}'] = await global.FieldValidator.validate('${property.value}','${property.parameter_type}',${origin}['${property.property}'], this.req, this.res);`); }); + + validation = ` + async validate() { + try { + ${validators.join('\n')} + } catch (error) { + console.log('validation error', error); + throw new Error(error.message); + } + } + `; } // compile the file From 7c5dfdfe71c9ffe9947624ff6c73ae49a2c8af27 Mon Sep 17 00:00:00 2001 From: ViktoryaSVA <63469770+ViktoryaSVA@users.noreply.github.com> Date: Wed, 2 Jun 2021 13:58:33 +0300 Subject: [PATCH 55/58] Update multi-method.mustache Create logs for generated_routes --- templates/multi-method.mustache | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/templates/multi-method.mustache b/templates/multi-method.mustache index 3a2bc64f..bae7230b 100644 --- a/templates/multi-method.mustache +++ b/templates/multi-method.mustache @@ -38,19 +38,22 @@ {{^isSingleton}}{{/isSingleton}} {{/isFormParameter}} {{/parameters}} - {{#query}} /* {{&query}} return res.code(results.Status).send(results); */ + {{{ test }}} {{/query}} + {{^query}} /* Method logic */ {{/query}} + + {{#responses}} {{#200}}{{{code}}}{{/200}} {{/responses}} From 93f13cb7b7d28b4dda62a7b31066e492522be07f Mon Sep 17 00:00:00 2001 From: ViktoryaSVA <63469770+ViktoryaSVA@users.noreply.github.com> Date: Wed, 2 Jun 2021 14:48:09 +0300 Subject: [PATCH 56/58] Update multi-method.mustache Create logs for generated_routes --- templates/multi-method.mustache | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/templates/multi-method.mustache b/templates/multi-method.mustache index bae7230b..7662329d 100644 --- a/templates/multi-method.mustache +++ b/templates/multi-method.mustache @@ -43,7 +43,7 @@ {{&query}} return res.code(results.Status).send(results); */ - {{{ test }}} + console.log('===>Executing generated route: {{&methodName}}'); {{/query}} {{^query}} @@ -52,8 +52,6 @@ */ {{/query}} - - {{#responses}} {{#200}}{{{code}}}{{/200}} {{/responses}} From 312b03158a6ee4253c34432ad530400e6ff96cbf Mon Sep 17 00:00:00 2001 From: ViktoryaSVA <63469770+ViktoryaSVA@users.noreply.github.com> Date: Wed, 2 Jun 2021 14:58:50 +0300 Subject: [PATCH 57/58] Update multi-method.mustache Create logs for generated_routes --- templates/multi-method.mustache | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/templates/multi-method.mustache b/templates/multi-method.mustache index 7662329d..61cfdf99 100644 --- a/templates/multi-method.mustache +++ b/templates/multi-method.mustache @@ -43,7 +43,8 @@ {{&query}} return res.code(results.Status).send(results); */ - console.log('===>Executing generated route: {{&methodName}}'); + + console.log('===> Executing generated route: {{&methodName}}'); {{/query}} {{^query}} From 5b2dc6bbcbfdb4a4c40ef581fa7e92239b066269 Mon Sep 17 00:00:00 2001 From: "eran.s" Date: Mon, 9 May 2022 12:53:42 +0300 Subject: [PATCH 58/58] first push mssql --- lib/expose.js | 92 +++++++++++++++++++------ lib/formatter.js | 21 +++--- lib/querier.js | 33 +++++++++ lib/splitter.js | 45 ++++--------- templates/multi-method.mustache | 116 +++++++++++++++++--------------- 5 files changed, 191 insertions(+), 116 deletions(-) diff --git a/lib/expose.js b/lib/expose.js index 8768efe9..c39e96d5 100644 --- a/lib/expose.js +++ b/lib/expose.js @@ -78,10 +78,15 @@ function expose(definitions, methods, path, directory) { // check x-AuthFieldType field const secure = []; + const secureForEntitySystem = []; + if (!(definitions[definition].properties instanceof Array)) { const properties = Object.keys(definitions[definition].properties); + const define = Object.keys(definitions[definition]); + properties.forEach((property) => { - if (definitions[definition].properties[property]['x-AuthFieldType']) { + if ((definitions[definition].properties[property]['x-AuthFieldType'])) { + methods.forEach((method) => { method.parameters.forEach((parameter) => { if (parameter.name === definition) { @@ -90,16 +95,36 @@ function expose(definitions, methods, path, directory) { definition, property, value: definitions[definition].properties[property]['x-AuthFieldType'], - parameter_type:definitions[definition].properties[property].type + parameter_type: definitions[definition].properties[property].type }); } }); }); } + }) + + define.forEach((el) => { + if ((el == 'x-EntitySystem')) { + + methods.forEach((method) => { + method.parameters.forEach((parameter) => { + if (parameter.name === definition) { + secureForEntitySystem.push({ + type: parameter['in'], + definition: 'secure', + property: 'secure2', + value: 'secure3', + parameter_type: 'securesecure' + }); + } + }); + }); + } + }); } else { definitions[definition].properties.forEach((property, i) => { - if (property['x-AuthFieldType']) { + if (property['x-AuthFieldType'] || 'x-EntitySystem') { methods.forEach((method) => { method.parameters.forEach((parameter) => { if (parameter.name === definition) { @@ -120,6 +145,7 @@ function expose(definitions, methods, path, directory) { // add validation let validation = ''; let validators = []; + let validatorsByIdAndType = []; if (secure.length > 0) { secure.forEach((property) => { let origin = property.type; @@ -128,20 +154,44 @@ function expose(definitions, methods, path, directory) { }); validation = ` - async validate() { - try { - ${validators.join('\n')} - } catch (error) { - console.log('validation error', error); - throw new Error(error.message); - } - } - `; + async validate() { + try { + ${validators.join('\n')} + } catch (error) { + console.log('validation error', error); + throw new Error(error.message); + } + } + `; } + if (secureForEntitySystem.length > 0) { + secureForEntitySystem.forEach((property) => { + let origin = property.type; + if (origin === 'path') { origin = 'req.params' } else { origin = 'this.data' }; + validatorsByIdAndType.push(`${origin}['${props[0]}'] = await global.FieldValidator.validateByIdAndType('${props[0]}','${definitions[definition].type}',${origin}['${props[0]}'], this.req, this.res);`); + + }); + console.log(validatorsByIdAndType); + + validation = ` + async validateByIdAndType() { + try { + ${validatorsByIdAndType.join('\n')} + } catch (error) { + console.log('validation error', error); + throw new Error(error.message); + } + } + `; + } + + + + // compile the file const content = `/* auto-generated: ${definition}.js */ - + module.exports = class { constructor(req = {}, res = {}, params = {}) { this.req = req; @@ -149,9 +199,9 @@ function expose(definitions, methods, path, directory) { this.params = params; this.data = {}; ${parameters} - this.schema = ${inspect(definitions[definition], { showHidden: false, depth: null })}; + this.schema = ${inspect(definitions[definition], { showHidden: false, depth: null })}; } - ${validation} + ${validation} };`; // make sure that destination definition directory exists @@ -162,12 +212,12 @@ function expose(definitions, methods, path, directory) { // create file in the destination folder fs.writeFileSync(`${destination}/${definition}.js`, - beauty(content, { indent_size: 2 }), - (err) => { - if (err) { - throw new Error(err.message || err); - } - }); + beauty(content, { indent_size: 2 }), + (err) => { + if (err) { + throw new Error(err.message || err); + } + }); }); } catch (err) { throw new Error(err.message || err); diff --git a/lib/formatter.js b/lib/formatter.js index c5900afc..e4296bd9 100644 --- a/lib/formatter.js +++ b/lib/formatter.js @@ -126,9 +126,9 @@ function format(data) { if (list.length > 0) { list.forEach((response) => { formatted[response] = method.responses[response]; - const refName = formatted[response].schema.items ? - formatted[response].schema.items['$ref'].split('/').slice(-1)[0] : - formatted[response].schema['$ref'].split('/').slice(-1)[0]; + const refName = formatted[response].schema.items ? + formatted[response].schema.items['$ref'].split('/').slice(-1)[0] : + formatted[response].schema['$ref'].split('/').slice(-1)[0]; definitions.forEach((definition) => { // copy properties if (refName === definition.name) { @@ -151,9 +151,9 @@ function format(data) { // add object references to the method (load objects as local variables inside the controller) method.parameters.forEach((parameter) => { if (parameter.schema) { - const reference = parameter.schema['$ref'] ? - parameter.schema['$ref'].split('/').slice(-1)[0] : - parameter.schema.items['$ref'].split('/').slice(-1)[0]; + const reference = parameter.schema['$ref'] ? + parameter.schema['$ref'].split('/').slice(-1)[0] : + parameter.schema.items['$ref'].split('/').slice(-1)[0]; objects.push({ destination: method.destination, origin: parameter['in'] === 'path' ? 'params' : parameter['in'], @@ -166,8 +166,8 @@ function format(data) { // generate the code list.forEach((response) => { const ref = formatted[response].schema.items ? - formatted[response].schema.items['$ref'].split('/').slice(-1)[0] : - formatted[response].schema['$ref'].split('/').slice(-1)[0]; + formatted[response].schema.items['$ref'].split('/').slice(-1)[0] : + formatted[response].schema['$ref'].split('/').slice(-1)[0]; let responseObject = {}; definitions.forEach((def) => { if (def.name === ref) { @@ -175,9 +175,10 @@ function format(data) { } }); + const isErr = formatted[response].status >= 400 ? 'err' : 'null'; - formatted[response].code = 'return global.response(req, res, ' - + `${formatted[response].status}, ${inspect(responseObject, { showHidden: false, depth: null })}, ${isErr});`; + formatted[response].code = `res.code(${formatted[response].status}).send(` + + `${inspect(responseObject, { showHidden: false, depth: null })}, ${isErr});`; }); // add the code to the resulting object diff --git a/lib/querier.js b/lib/querier.js index 0a16425c..313550ba 100644 --- a/lib/querier.js +++ b/lib/querier.js @@ -2,6 +2,26 @@ const _ = require('lodash'); const specialKeys = ['Add', 'Create', 'Delete', 'Disable', 'Update', 'Assign', 'Unassign']; +function capitalizeFirstLetter(string) { + return string.charAt(0).toUpperCase() + string.slice(1); +} + +function getPropertiesForMSSQL(params) { + let inputsJSONParameters = ""; + const variables = []; + params.forEach((param) => { + if (param.name) { + variables.push('@' + param.name); + if(param.in === 'body') inputsJSONParameters += `${param.name}: ${param.name}.data`; + else inputsJSONParameters += `${param.name}`; + } + }); + return { + inputs: `{${inputsJSONParameters}}`, + variables: variables.join(', '), + } +} + /** * Get the required properties for the query * @param params {array} - array of the method parameters @@ -47,6 +67,17 @@ function querier(data) { } }); + const { inputs, variables } = getPropertiesForMSSQL(method.parameters, special); + let query =''; + if(special){ + query = `CALL SP_${method.methodName}(${variables})`; + } else { + query = `SELECT FN_${method.methodName}(${variables}) as Response`; + } + + mutable.methods[m].query = `const results = await dal.query("${query}", { inputs: ${inputs} } , { redis: ${!special} });`; + + /* const { parameters, questions } = getProperties(method.parameters, special); let query =''; if(special){ @@ -56,6 +87,8 @@ function querier(data) { } mutable.methods[m].query = `const results = await dal.query("${query}", ` + `[${parameters}], { redis: ${!special} });`; + + */ }); return mutable; diff --git a/lib/splitter.js b/lib/splitter.js index 106b2201..f939f82e 100644 --- a/lib/splitter.js +++ b/lib/splitter.js @@ -29,19 +29,16 @@ async function split(source, className, path, dir) { try { // convert ES5 to ES6 const { code, warnings } = transform(source, ['let', 'arrow', 'arrow-return', 'class']); - // show conversion errors and warnings if (warnings && warnings.length && warnings.length > 0) { console.log('> swagger-js-codegen @ ES6 conversion warnings:\n', warnings); } - // create the source file fs.writeFileSync(`${__dirname}/source.js`, code, (err) => { if (err) { throw new Error(err.message || err); } }); - // load source file and get all of the available methods from the class const Instance = require(`${__dirname}/source.js`)[`${capitalize(className.toLowerCase())}`]; const methods = Object.getOwnPropertyNames(Instance.prototype).filter(m => m !== 'constructor'); @@ -69,7 +66,6 @@ async function split(source, className, path, dir) { function: functionName, }); }); - // create file list const files = []; destinations.forEach((f) => { @@ -81,44 +77,28 @@ async function split(source, className, path, dir) { }); } }); - // building the controllers files.forEach((file, i) => { destinations.forEach((d) => { if (file.file === d.file) { - files[i].content = `${files[i].content} - - ${d.content}`; + files[i].content = `${files[i].content}${d.content}`; files[i].functions.push(d.function); } }); }); // add 'use strict', exporting and bind definitions + const dal = '// const dal = require(\'../../helpers/dal_mssql\'); \n// const dal = require(\'../../helpers/dal\');'; + files.forEach((file, i) => { let fList = ''; file.functions.forEach((f) => { fList = `${fList} ${f},`; }); - files[i].content = beautify(`'use strict'; - - /* - auto-generated: ${file.file}.controller.js - */ - - /* - const dal = require('../../helpers/dal'); - */ + files[i].content = beautify(`${dal} - module.exports = class { - constructor(req = {}, res = {}, params = {}) { - this.req = req; - this.res = res; - this.params = params; - } - ${file.content} - };`, { indent_size: 2 }); + module.exports = class {${file.content}};`, { indent_size: 2}); }); // delete the source file @@ -142,12 +122,15 @@ async function split(source, className, path, dir) { } // create file fs.writeFileSync(`${container}/${file.file}/${file.file}.controller.js`, - beautify(file.content, { indent_size: 2 }), - (fErr) => { - if (fErr) { - throw new Error(fErr.message || fErr); - } - }); + beautify(file.content, { + indent_size: 2 , + end_with_newline: true, + }), + (fErr) => { + if (fErr) { + throw new Error(fErr.message || fErr); + } + }); }); return console.log('> swagger-js-codegen @ SUCCESS!'); diff --git a/templates/multi-method.mustache b/templates/multi-method.mustache index 61cfdf99..28275fe5 100644 --- a/templates/multi-method.mustache +++ b/templates/multi-method.mustache @@ -1,64 +1,72 @@ /** - * {{&summary}} - * @method {{&methodName}} - * @name {{&className}}#{{&methodName}} - * @param {object} req - request object - * @param {object} res - response object - */ +* {{&summary}} +* @method {{&methodName}} +* @name {{&className}}#{{&methodName}} +* @param {object} req - request object +* @param {object} res - response object +*/ {{&className}}.prototype.{{&destination}}_{{&methodName}} = function(req, res) { - try { +try { +console.log('===> Executing generated route: {{&methodName}}'); + {{#parameters}} - {{#isQueryParameter}} - {{#x-AuthFieldType}} - var auth{{&x-AuthFieldType.propertyName}} = await_FieldValidator.validate('{{&x-AuthFieldType.value}}', req.query['{{&x-AuthFieldType.propertyName}}'], req, res); - if (!auth{{&x-AuthFieldType.propertyName}}) { return; } - {{/x-AuthFieldType}} - var {{name}} = req.query['{{name}}']; - {{/isQueryParameter}} - {{#isPathParameter}} - {{#x-AuthFieldType}} - var auth{{&x-AuthFieldType.propertyName}} = await_FieldValidator.validate('{{&x-AuthFieldType.value}}', req.params['{{&x-AuthFieldType.propertyName}}'], req, res); - if (!auth{{&x-AuthFieldType.propertyName}}) { return; } - {{/x-AuthFieldType}} - var {{name}} = req.params['{{name}}']; - {{/isPathParameter}} - {{#isBodyParameter}} - {{#x-AuthFieldType}} - var auth{{&x-AuthFieldType.propertyName}} = await_FieldValidator.validate('{{&x-AuthFieldType.value}}', req.body['{{&x-AuthFieldType.propertyName}}'], req, res); - if (!auth{{&x-AuthFieldType.propertyName}}) { return; } - {{/x-AuthFieldType}} - var {{name}} = new global.classes['{{tsType.target}}'](req, res, req.body); - {{/isBodyParameter}} - {{#isHeaderParameter}} - {{#isSingleton}}{{/isSingleton}} - {{^isSingleton}}{{/isSingleton}} - {{/isHeaderParameter}} - {{#isFormParameter}} - {{#isSingleton}}{{/isSingleton}} - {{^isSingleton}}{{/isSingleton}} - {{/isFormParameter}} + {{#isQueryParameter}} + {{#x-AuthFieldType}} + var auth{{&x-AuthFieldType.propertyName}} = await_FieldValidator.validate('{{&x-AuthFieldType.value}}', req.query['{{&x-AuthFieldType.propertyName}}'], req, res); + if (!auth{{&x-AuthFieldType.propertyName}}) { return; } + {{/x-AuthFieldType}} + var {{name}} = req.query['{{name}}']; + {{/isQueryParameter}} + {{#isPathParameter}} + {{#x-AuthFieldType}} + var auth{{&x-AuthFieldType.propertyName}} = await_FieldValidator.validate('{{&x-AuthFieldType.value}}', req.params['{{&x-AuthFieldType.propertyName}}'], req, res); + if (!auth{{&x-AuthFieldType.propertyName}}) { return; } + {{/x-AuthFieldType}} + var {{name}} = req.params['{{name}}']; + {{/isPathParameter}} + {{#isBodyParameter}} + {{#x-AuthFieldType}} + var auth{{&x-AuthFieldType.propertyName}} = await_FieldValidator.validate('{{&x-AuthFieldType.value}}', req.body['{{&x-AuthFieldType.propertyName}}'], req, res); + if (!auth{{&x-AuthFieldType.propertyName}}) { return; } + {{/x-AuthFieldType}} + //const {{name}} = new global.classes['{{tsType.target}}'](req, res, req.body); + {{/isBodyParameter}} + {{#isHeaderParameter}} + {{#isSingleton}}{{/isSingleton}} + {{^isSingleton}}{{/isSingleton}} + {{/isHeaderParameter}} + {{#isFormParameter}} + {{#isSingleton}}{{/isSingleton}} + {{^isSingleton}}{{/isSingleton}} + {{/isFormParameter}} {{/parameters}} - {{#query}} - /* - {{&query}} - return res.code(results.Status).send(results); - */ +{{#query}} + {{#parameters}} + {{#isBodyParameter}} + //await {{tsType.target}}.validate(); + + {{/isBodyParameter}} + {{/parameters}} + //{{&query}} - console.log('===> Executing generated route: {{&methodName}}'); - {{/query}} + //res.code(results.Status).send(results); +{{/query}} - {{^query}} +{{^query}} /* - Method logic + Method logic */ - {{/query}} +{{/query}} +{{#responses}} + {{#200}}{{{code}}}{{/200}} +{{/responses}} +} catch (error) { + console.error(`Error in {{&methodName}}: ${error.message}`); - {{#responses}} - {{#200}}{{{code}}}{{/200}} - {{/responses}} - } catch (err) { - {{#responses}} - {{#400}}{{{code}}}{{/400}} - {{/responses}} - } + res.code(500).send({ + Status: 500, + Message: 'Error in {{&methodName}}', + Description: error.message, + }); +} };