Skip to content
Open

Mssql #258

Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
e156668
Add splitter
peterdee Mar 13, 2019
6d12973
Add file creation
peterdee Mar 14, 2019
96e74a7
Formatter: default API responses
peterdee Mar 14, 2019
774e7c5
Default API responses
peterdee Mar 15, 2019
d4cbe15
Classes, x-AuthFieldType
peterdee Mar 18, 2019
70d1c35
Update x-AuthFieldType check in controllers, update templates
peterdee Mar 19, 2019
01bebda
Load definitions to the global scope
peterdee Mar 19, 2019
bf59213
Definitions files generation
peterdee Mar 19, 2019
71c0b15
x-AuthFieldType parameter validation fix
peterdee Mar 20, 2019
f10b02b
Bindings inside of the controllers, global availability
peterdee Mar 20, 2019
af80550
Load objects and classes to the global scope
peterdee Mar 21, 2019
a5ed911
Bind definition classes inside of the controllers
peterdee Mar 21, 2019
2f7b5d9
Fix duplicating definitions
peterdee Mar 21, 2019
05c5386
Fix x-AuthFieldType inside of the definitions classes
peterdee Mar 21, 2019
0b30b93
Default response values for controllers
peterdee Mar 22, 2019
72001af
FieldValidator fixes
peterdee Mar 22, 2019
a6775db
Remove 'require-from-string'
peterdee Mar 22, 2019
cd2262b
Fix definitions classes export to global
peterdee Mar 22, 2019
ff01b38
Fix definitions classes call inside of the controllers
peterdee Mar 22, 2019
6effc4c
Formatting
peterdee Mar 25, 2019
9482fc0
Remove class names from controllers
peterdee Mar 25, 2019
1e45537
Remove class names from controllers
peterdee Mar 25, 2019
140339c
Method template update
peterdee Mar 25, 2019
057b95c
Update templates, fix controller parameters binding
peterdee Mar 26, 2019
4b7d7d2
Fix definitions classes instantination
peterdee Mar 26, 2019
d769ab7
Fix definition binding name
peterdee Mar 26, 2019
0370a29
Fix definitions bindings: nested objects
peterdee Mar 26, 2019
5e478dc
Fix props names for definitions
peterdee Mar 26, 2019
60b67f3
Fix x-AuthFieldType check for definitions
peterdee Mar 27, 2019
f9cdc53
Fix array binding for definitions
peterdee Mar 27, 2019
e5e2aac
Definitions formatting
peterdee Mar 27, 2019
8041aa8
Definitions formatting
peterdee Mar 27, 2019
5ac8dea
Fix default API responses (arrays of objects)
peterdee Mar 27, 2019
7adbb0c
Merge pull request #1 from voicenter/feature/OFC-3
voicenter Mar 27, 2019
8f5fcd2
Fix x-AuthFieldType validation
peterdee Mar 28, 2019
80f25ad
Fix definitions nesting
peterdee Mar 29, 2019
a3c0d5a
Add options checks, update package.json, update README
peterdee Apr 1, 2019
d6706d7
Fix default directories for the generated files, update README
peterdee Apr 1, 2019
c885b75
Merge pull request #2 from voicenter/feature/OFC-3
peterdee Apr 1, 2019
d4269a4
Simple queries generation
peterdee Apr 2, 2019
92679a2
Merge pull request #3 from voicenter/feature/OFC-3
peterdee Apr 2, 2019
d7b789a
Generate all queries
peterdee Apr 2, 2019
2cfcb38
Merge pull request #4 from voicenter/feature/OFC-3
peterdee Apr 2, 2019
58b6ee0
Multi-method template update
peterdee Apr 2, 2019
7dedbba
Merge pull request #5 from voicenter/feature/OFC-3
peterdee Apr 2, 2019
75f0347
Fix typo
peterdee Apr 4, 2019
69cf41e
Merge pull request #6 from voicenter/feature/OFC-3
peterdee Apr 4, 2019
23e5089
Add dal import, use try/catch in methods
peterdee Apr 9, 2019
1891213
Merge pull request #7 from voicenter/feature/OFC-3
peterdee Apr 9, 2019
9fe725b
AMQP: successfull response
peterdee Apr 17, 2019
14f0c7e
Add error responses to the controllers
peterdee Apr 18, 2019
5d2b5b8
Node version ^
peterdee Apr 18, 2019
08c0dbf
Pass error to the response function
peterdee Apr 18, 2019
26ad4ec
Merge pull request #8 from voicenter/feature/amqp
voicenter Apr 22, 2019
d9557d3
added alias to fn querier.
TzachiSh Apr 24, 2019
4362bfc
added specialKeys
TzachiSh Jun 6, 2019
75cc42a
generate bug fix
Oct 29, 2019
0ad9f12
generate bug fix 2
Oct 29, 2019
9945584
generate bug fix 3
Nov 7, 2019
2e3e131
Add type to validate function
voicenter Jul 23, 2020
a0243a6
Update expose.js
Bohdan2000 Jul 31, 2020
cd821ee
Merge pull request #9 from Bohdan2000/patch-2
voicenter Jul 31, 2020
6c049d5
use validators list instead of one validation
dmitrysam88 Apr 8, 2021
8527cc0
Merge pull request #10 from dmitrysam88/use-validators-list
voicenter Apr 8, 2021
7c5dfdf
Update multi-method.mustache
ViktoryaSVA Jun 2, 2021
93f13cb
Update multi-method.mustache
ViktoryaSVA Jun 2, 2021
312b031
Update multi-method.mustache
ViktoryaSVA Jun 2, 2021
cc89650
Merge pull request #11 from ViktoryaSVA/patch-2
voicenter Jun 2, 2021
5b2dc6b
first push mssql
May 9, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Add splitter
  • Loading branch information
peterdee committed Mar 13, 2019
commit e1566689e365ab9991f638d85348c73877b33538
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ wks.*
*.swp

tmp-*
zz/
1 change: 1 addition & 0 deletions .jshintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
lib/splitter.js
5 changes: 2 additions & 3 deletions .jshintrc
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"curly": true,
"eqeqeq": true,
"immed": true,
"indent": 4,
"indent": 2,
"latedef": true,
"newcap": false,
"noarg": true,
Expand All @@ -18,6 +18,5 @@
"strict": true,
"trailing": true,
"smarttabs": true,
"globals": {
}
"globals": {}
}
28 changes: 28 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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: <STRING>,
file: <STRING>,
functions: <ARRAY>,
directory: <STRING>,
isWrapper: <BOOLEAN> }

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):

Expand Down
64 changes: 46 additions & 18 deletions lib/codegen.js
Original file line number Diff line number Diff line change
Expand Up @@ -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, '_');
Expand Down Expand Up @@ -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;
Expand All @@ -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;
}
Expand All @@ -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(', ')}'`,
Expand Down Expand Up @@ -253,9 +264,16 @@ var getViewForSwagger1 = function(opts, type){
return data;
};

/**
* Generate code based on the input file
* @param opts <OBJECT> - options for the file generation
* @param type <STRING> - 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: "..." }');
Expand All @@ -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');
}
}
Expand Down Expand Up @@ -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 {
Expand Down
145 changes: 145 additions & 0 deletions lib/splitter.js
Original file line number Diff line number Diff line change
@@ -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,
};
Loading