Browse Source

eslint

pull/75/head
v2io 6 years ago
parent
commit
8576c6ab51
  1. 11
      .eslintrc
  2. 27
      .eslintrc.js
  3. 89
      bin/index.js
  4. 25
      examples/aws-lambda/index.js
  5. 94
      index.js
  6. 116
      lib/util/cmd.helper.js
  7. 180
      lib/util/data.helper.js
  8. 274
      lib/util/whereClause.helper.js
  9. 425
      lib/xapi.js
  10. 501
      lib/xctrl.js
  11. 1087
      lib/xsql.js
  12. 460
      package-lock.json
  13. 4
      package.json
  14. 1773
      tests/tests.js

11
.eslintrc

@ -1,11 +0,0 @@
{
"parserOptions": {
"ecmaVersion": 6
},
"rules": {
"eol-last": "error",
"indent": ["error", 2, { "SwitchCase": 1 }],
"no-trailing-spaces": "error",
"no-unused-vars": ["error", { "vars": "all", "args": "none", "ignoreRestSiblings": true }]
}
}

27
.eslintrc.js

@ -0,0 +1,27 @@
module.exports = {
extends: ["eslint-config-airbnb-base", "eslint-config-prettier"],
plugins: ["eslint-plugin-import", "eslint-plugin-prettier"],
parserOptions: {
ecmaFeatures: {
ecmaVersion: 6
}
},
env: {
es6: true,
node: true
},
rules: {
"prettier/prettier": ["error", {}],
"max-len": ["error", { code: 2000, ignoreUrls: true }],
"linebreak-style": 0,
"no-use-before-define": ["error", { functions: false, classes: false }],
"no-plusplus": ["error", { allowForLoopAfterthoughts: true }],
"no-underscore-dangle": 0,
"import/no-amd": 0,
"import/no-dynamic-require": 0,
"no-console": 0,
"no-param-reassign": 0,
"no-unused-vars": ["error", { argsIgnorePattern: "next" }],
"comma-dangle": 0
}
};

89
bin/index.js

@ -1,70 +1,71 @@
#! /usr/bin/env node
const morgan = require('morgan');
const bodyParser = require('body-parser');
const express = require('express');
const sqlConfig = require('commander');
const mysql = require('mysql');
const cors = require('cors');
const dataHelp = require('../lib/util/data.helper.js');
const Xapi = require('../lib/xapi.js');
const cmdargs = require('../lib/util/cmd.helper.js');
const cluster = require('cluster');
const numCPUs = require('os').cpus().length;
const morgan = require("morgan");
const bodyParser = require("body-parser");
const express = require("express");
const sqlConfig = require("commander");
const mysql = require("mysql");
const cors = require("cors");
const dataHelp = require("../lib/util/data.helper.js");
const Xapi = require("../lib/xapi.js");
const cmdargs = require("../lib/util/cmd.helper.js");
const cluster = require("cluster");
const numCPUs = require("os").cpus().length;
function startXmysql(sqlConfig) {
/**************** START : setup express ****************/
let app = express();
app.use(morgan('tiny'));
app.use(morgan("tiny"));
app.use(cors());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
app.use(
bodyParser.urlencoded({
extended: true
}));
})
);
/**************** END : setup express ****************/
/**************** START : setup mysql ****************/
let mysqlPool = mysql.createPool(sqlConfig);
/**************** END : setup mysql ****************/
/**************** START : setup Xapi ****************/
console.log('');
console.log('');
console.log('');
console.log(' Generating REST APIs at the speed of your thought.. ');
console.log('');
console.log("");
console.log("");
console.log("");
console.log(" Generating REST APIs at the speed of your thought.. ");
console.log("");
let t = process.hrtime();
let moreApis = new Xapi(sqlConfig, mysqlPool, app);
moreApis.init((err, results) => {
app.listen(sqlConfig.portNumber, sqlConfig.ipAddress);
var t1 = process.hrtime(t);
var t2 = t1[0] + t1[1] / 1000000000;
console.log(" Xmysql took : %d seconds", dataHelp.round(t2, 1));
console.log(" API's base URL : " + "localhost:" + sqlConfig.portNumber);
console.log(' ');
console.log(' - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ');
console.log(
" Xmysql took : %d seconds",
dataHelp.round(t2, 1)
);
console.log(
" API's base URL : " +
"localhost:" +
sqlConfig.portNumber
);
console.log(" ");
console.log(
" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "
);
});
/**************** END : setup Xapi ****************/
}
function start(sqlConfig) {
//handle cmd line arguments
cmdargs.handle(sqlConfig);
if (cluster.isMaster && sqlConfig.useCpuCores > 1) {
console.log(`Master ${process.pid} is running`);
for (let i = 0; i < numCPUs && i < sqlConfig.useCpuCores; i++) {
@ -72,23 +73,21 @@ function start(sqlConfig) {
cluster.fork();
}
cluster.on('exit', function(worker, code, signal) {
console.log('Worker ' + worker.process.pid + ' died with code: '
+ code + ', and signal: ' + signal);
console.log('Starting a new worker');
cluster.on("exit", function(worker, code, signal) {
console.log(
"Worker " +
worker.process.pid +
" died with code: " +
code +
", and signal: " +
signal
);
console.log("Starting a new worker");
cluster.fork();
});
} else {
startXmysql(sqlConfig);
}
}
start(sqlConfig);

25
examples/aws-lambda/index.js

@ -23,27 +23,29 @@
Object.defineProperty(exports, "__esModule", { value: true });
var bodyParser = require("body-parser");
var express = require("express");
var serverless = require('serverless-http');
var serverless = require("serverless-http");
var cors = require("cors");
var mysql = require('mysql');
var Xapi = require('./node_modules/xmysql/lib/xapi.js');
var morgan = require('morgan');
var mysql = require("mysql");
var Xapi = require("./node_modules/xmysql/lib/xapi.js");
var morgan = require("morgan");
var app = express();
var onXapiInitialized = new Promise(function(resolve, reject) {
try {
// /**************** START : setup express ****************/
app.use(morgan('tiny'));
app.use(morgan("tiny"));
app.use(cors());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
app.use(
bodyParser.urlencoded({
extended: true
}));
})
);
// /**************** END : setup express ****************/
app.use(function(req, res, next) {
// You can add authentication here
console.log('Received request for: ' + req.url, req);
console.log("Received request for: " + req.url, req);
next();
});
@ -53,8 +55,8 @@ var onXapiInitialized = new Promise(function (resolve, reject) {
database: config.mysql.database,
user: config.mysql.user,
password: config.mysql.password,
apiPrefix: '/',
ipAddress: 'localhost',
apiPrefix: "/",
ipAddress: "localhost",
portNumber: 3000,
ignoreTables: [],
storageFolder: __dirname
@ -66,8 +68,7 @@ var onXapiInitialized = new Promise(function (resolve, reject) {
app.listen(3000);
resolve();
});
}
catch (err) {
} catch (err) {
reject(err);
}
});

94
index.js

@ -1,71 +1,72 @@
#! /usr/bin/env node
const morgan = require('morgan');
const bodyParser = require('body-parser');
const express = require('express');
const sqlConfig = require('commander');
const mysql = require('mysql');
const cors = require('cors');
const dataHelp = require('./lib/util/data.helper.js');
const Xapi = require('./lib/xapi.js');
const cmdargs = require('./lib/util/cmd.helper.js');
const cluster = require('cluster');
const numCPUs = require('os').cpus().length;
const { version } = require('./package.json');
const morgan = require("morgan");
const bodyParser = require("body-parser");
const express = require("express");
const sqlConfig = require("commander");
const mysql = require("mysql");
const cors = require("cors");
const dataHelp = require("./lib/util/data.helper.js");
const Xapi = require("./lib/xapi.js");
const cmdargs = require("./lib/util/cmd.helper.js");
const cluster = require("cluster");
const numCPUs = require("os").cpus().length;
const { version } = require("./package.json");
function startXmysql(sqlConfig) {
/**************** START : setup express ****************/
let app = express();
app.set('version', version);
app.use(morgan('tiny'));
app.set("version", version);
app.use(morgan("tiny"));
app.use(cors());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
app.use(
bodyParser.urlencoded({
extended: true
}));
})
);
/**************** END : setup express ****************/
/**************** START : setup mysql ****************/
let mysqlPool = mysql.createPool(sqlConfig);
/**************** END : setup mysql ****************/
/**************** START : setup Xapi ****************/
console.log('');
console.log('');
console.log('');
console.log(' Generating REST APIs at the speed of your thought.. ');
console.log('');
console.log("");
console.log("");
console.log("");
console.log(" Generating REST APIs at the speed of your thought.. ");
console.log("");
let t = process.hrtime();
let moreApis = new Xapi(sqlConfig, mysqlPool, app);
moreApis.init((err, results) => {
app.listen(sqlConfig.portNumber, sqlConfig.ipAddress);
var t1 = process.hrtime(t);
var t2 = t1[0] + t1[1] / 1000000000;
console.log(" Xmysql took : %d seconds", dataHelp.round(t2, 1));
console.log(" API's base URL : " + "localhost:" + sqlConfig.portNumber);
console.log(' ');
console.log(' - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ');
console.log(
" Xmysql took : %d seconds",
dataHelp.round(t2, 1)
);
console.log(
" API's base URL : " +
"localhost:" +
sqlConfig.portNumber
);
console.log(" ");
console.log(
" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "
);
});
/**************** END : setup Xapi ****************/
}
function start(sqlConfig) {
cmdargs.handle(sqlConfig);
if (cluster.isMaster && sqlConfig.useCpuCores > 1) {
console.log(`Master ${process.pid} is running`);
for (let i = 0; i < numCPUs && i < sqlConfig.useCpuCores; i++) {
@ -73,24 +74,21 @@ function start(sqlConfig) {
cluster.fork();
}
cluster.on('exit', function(worker, code, signal) {
console.log('Worker ' + worker.process.pid + ' died with code: '
+ code + ', and signal: ' + signal);
console.log('Starting a new worker');
cluster.on("exit", function(worker, code, signal) {
console.log(
"Worker " +
worker.process.pid +
" died with code: " +
code +
", and signal: " +
signal
);
console.log("Starting a new worker");
cluster.fork();
});
} else {
startXmysql(sqlConfig);
}
}
start(sqlConfig);

116
lib/util/cmd.helper.js

@ -1,101 +1,107 @@
'use strict';
const program = require('commander');
const colors = require('colors');
const maxCpus = require('os').cpus().length;
program.on('--help', () => {
console.log('')
console.log(' Examples:'.blue)
console.log('')
console.log(' $ xmysql -u username -p password -d databaseSchema'.blue)
console.log('')
})
"use strict";
const program = require("commander");
const colors = require("colors");
const maxCpus = require("os").cpus().length;
program.on("--help", () => {
console.log("");
console.log(" Examples:".blue);
console.log("");
console.log(" $ xmysql -u username -p password -d databaseSchema".blue);
console.log("");
});
program
.version(module.exports.version)
.option('-h, --host <n>', 'hostname of database / localhost by default')
.option('-u, --user <n>', 'username of database / root by default')
.option('-p, --password <n>', 'password of database / empty by default')
.option('-d, --database <n>', 'database schema name')
.option('-r, --ipAddress <n>', 'IP interface of your server / localhost by default')
.option('-n, --portNumber <n>', 'port number for app / 3000 by default')
.option('-o, --port <n>', 'port number for mysql / 3306 by default')
.option('-S, --socketPath <n>', 'unix socket path / not used by default')
.option('-s, --storageFolder <n>', 'storage folder / current working dir by default / available only with local')
.option('-i, --ignoreTables <n>', 'comma separated table names to ignore')
.option('-a, --apiPrefix <n>', 'api url prefix / "/api/" by default')
.option('-y, --readOnly', 'readonly apis / false by default')
.option('-c, --useCpuCores <n>', 'use number of CPU cores (using cluster) / 1 by default')
.parse(process.argv)
.option("-h, --host <n>", "hostname of database / localhost by default")
.option("-u, --user <n>", "username of database / root by default")
.option("-p, --password <n>", "password of database / empty by default")
.option("-d, --database <n>", "database schema name")
.option(
"-r, --ipAddress <n>",
"IP interface of your server / localhost by default"
)
.option("-n, --portNumber <n>", "port number for app / 3000 by default")
.option("-o, --port <n>", "port number for mysql / 3306 by default")
.option("-S, --socketPath <n>", "unix socket path / not used by default")
.option(
"-s, --storageFolder <n>",
"storage folder / current working dir by default / available only with local"
)
.option("-i, --ignoreTables <n>", "comma separated table names to ignore")
.option("-a, --apiPrefix <n>", 'api url prefix / "/api/" by default')
.option("-y, --readOnly", "readonly apis / false by default")
.option(
"-c, --useCpuCores <n>",
"use number of CPU cores (using cluster) / 1 by default"
)
.parse(process.argv);
function paintHelp(txt) {
return colors.magenta(txt) //display the help text in a color
return colors.magenta(txt); //display the help text in a color
}
function processInvalidArguments(program) {
let err = '';
let err = "";
if (!program.password) {
err += 'Error: password for database is missing\n';
err += "Error: password for database is missing\n";
}
if (!program.database) {
err += 'Error: database name is missing\n';
err += "Error: database name is missing\n";
}
if (err !== '') {
program.outputHelp(paintHelp)
console.log(err.red)
if (err !== "") {
program.outputHelp(paintHelp);
console.log(err.red);
}
}
exports.handle = program => {
/**************** START : default values ****************/
program.ipAddress = program.ipAddress || 'localhost';
program.ipAddress = program.ipAddress || "localhost";
program.portNumber = program.portNumber || 3000;
program.port = program.port || 3306;
program.user = program.user || 'root';
program.password = program.password || '';
program.host = program.host || 'localhost';
program.socketPath = program.socketPath || '';
program.storageFolder = program.storageFolder || process.cwd()
program.apiPrefix = program.apiPrefix || '/api/'
program.user = program.user || "root";
program.password = program.password || "";
program.host = program.host || "localhost";
program.socketPath = program.socketPath || "";
program.storageFolder = program.storageFolder || process.cwd();
program.apiPrefix = program.apiPrefix || "/api/";
program.readOnly = program.readOnly || false;
program.useCpuCores = program.useCpuCores || 1;
if(program.useCpuCores === '0') {
if (program.useCpuCores === "0") {
program.useCpuCores = maxCpus;
}
if (program.ignoreTables) {
let ignoreTables = program.ignoreTables.split(',')
program.ignoreTables = {}
let ignoreTables = program.ignoreTables.split(",");
program.ignoreTables = {};
for (var i = 0; i < ignoreTables.length; ++i) {
program.ignoreTables[ignoreTables[i]] = ignoreTables[i];
}
} else {
program.ignoreTables = {}
program.ignoreTables = {};
}
program.connectionLimit = 10;
if (program.host === 'localhost' || program.host === '127.0.0.1' || program.host === '::1') {
program.dynamic = 1
if (
program.host === "localhost" ||
program.host === "127.0.0.1" ||
program.host === "::1"
) {
program.dynamic = 1;
}
// console.log(program);
/**************** END : default values ****************/
if (program.database && program.host && program.user) {
//console.log('Starting server at:', 'http://' + program.host + ':' + program.portNumber)
} else {
processInvalidArguments(program)
process.exit(1)
processInvalidArguments(program);
process.exit(1);
}
};

180
lib/util/data.helper.js

@ -1,8 +1,6 @@
'use strict';
"use strict";
exports.findOrInsertObjectArrayByKey = (obj, key, array) => {
let found = 0;
let i = 0;
@ -16,15 +14,13 @@ exports.findOrInsertObjectArrayByKey = (obj, key, array) => {
}
if (!found) {
array.push(obj)
array.push(obj);
}
return array[i];
};
exports.findObjectInArrayByKey = (key, value, objArray) => {
for (let i = 0; i < objArray.length; ++i) {
if (objArray[i][key] === value) {
return objArray[i];
@ -34,7 +30,6 @@ exports.findObjectInArrayByKey = (key, value, objArray) => {
return null;
};
exports.round = function(number, precision) {
var factor = Math.pow(10, precision);
var tempNumber = number * factor;
@ -42,16 +37,14 @@ exports.round = function (number, precision) {
return roundedTempNumber / factor;
};
exports.numberRound = (number, precision) => {
var factor = Math.pow(10, precision);
var tempNumber = number * factor;
var roundedTempNumber = Math.round(tempNumber);
return roundedTempNumber / factor;
}
exports.numberGetLength = (number) => {
};
exports.numberGetLength = number => {
var n = number;
if (number < 0) {
@ -59,109 +52,103 @@ exports.numberGetLength = (number) => {
}
return n.toString().length;
}
};
exports.numberGetFixed = (number) => {
exports.numberGetFixed = number => {
//console.log(number, typeof number);
return parseInt(number.toFixed())
}
return parseInt(number.toFixed());
};
exports.getStepArraySimple = function(min, max, step) {
var arr = []
var arr = [];
for (var i = min; i <= max; i = i + step) {
arr.push(i)
arr.push(i);
}
return arr;
};
exports.getStepArray = (min, max, stddev) => {
// console.log(' = = = = = = = ');
//console.log('original numbers', min, max, stddev);
min = this.numberGetFixed(min)
max = this.numberGetFixed(max)
stddev = this.numberGetFixed(stddev)
min = this.numberGetFixed(min);
max = this.numberGetFixed(max);
stddev = this.numberGetFixed(stddev);
// console.log('fixed numbers', min, max, stddev);
let minMinusHalf = min - stddev / 2
let maxMinusHalf = max + stddev / 2
let minMinusHalf = min - stddev / 2;
let maxMinusHalf = max + stddev / 2;
minMinusHalf = this.numberGetFixed(minMinusHalf)
maxMinusHalf = this.numberGetFixed(maxMinusHalf)
minMinusHalf = this.numberGetFixed(minMinusHalf);
maxMinusHalf = this.numberGetFixed(maxMinusHalf);
// console.log('fixed numbers + (min,max)', min, max, stddev, '(', minMinusHalf, ',', maxMinusHalf, ')');
// console.log('numbers length', 'min', numberGetLength(min), 'max', numberGetLength(max), 'stddev', numberGetLength(stddev));
let minLen = this.numberGetLength(minMinusHalf)
let maxLen = this.numberGetLength(maxMinusHalf)
let stddevLen = this.numberGetLength(stddev)
let minLen = this.numberGetLength(minMinusHalf);
let maxLen = this.numberGetLength(maxMinusHalf);
let stddevLen = this.numberGetLength(stddev);
//
// console.log('- - - -');
// console.log('Range', 'min', numberRound(minMinusHalf, -1));
// console.log('Range', 'max', numberRound(maxMinusHalf, -1));
// console.log('Range', 'stddev', numberRound(stddev, -1));
if (minLen > 1)
minMinusHalf = this.numberRound(minMinusHalf, -1)
if (minLen > 1) minMinusHalf = this.numberRound(minMinusHalf, -1);
if (maxLen > 2)
maxMinusHalf = this.numberRound(maxMinusHalf, -1)
if (maxLen > 2) maxMinusHalf = this.numberRound(maxMinusHalf, -1);
if (stddevLen !== 1)
stddev = this.numberRound(stddev, -1)
if (stddevLen !== 1) stddev = this.numberRound(stddev, -1);
var arr = []
var arr = [];
for (var step = minMinusHalf; step < maxMinusHalf; step = step + stddev) {
arr.push(step)
arr.push(step);
}
arr.push(maxMinusHalf)
arr.push(maxMinusHalf);
// console.log(arr);
return arr;
}
};
exports.getSchemaQuery = function() {
return 'select c.table_name, c.column_name, c.ordinal_position,c.column_key,c.is_nullable, c.data_type, c.column_type,c.extra,c.privileges, ' +
'c.column_comment,c.column_default,c.data_type,c.character_maximum_length, ' +
'k.constraint_name, k.referenced_table_name, k.referenced_column_name, ' +
's.index_name,s.seq_in_index, ' +
'v.table_name as isView ' +
'from ' +
'information_schema.columns as c ' +
'left join ' +
'information_schema.key_column_usage as k ' +
'on ' +
'c.column_name=k.column_name and ' +
'c.table_schema = k.referenced_table_schema and ' +
'c.table_name = k.table_name ' +
'left join ' +
'information_schema.statistics as s ' +
'on ' +
'c.column_name = s.column_name and ' +
'c.table_schema = s.index_schema and ' +
'c.table_name = s.table_name ' +
'LEFT JOIN ' +
'information_schema.VIEWS as v ' +
'ON ' +
'c.table_schema = v.table_schema and ' +
'c.table_name = v.table_name ' +
'where ' +
'c.table_schema=? ' +
'order by ' +
'c.table_name, c.ordinal_position';
return (
"select c.table_name, c.column_name, c.ordinal_position,c.column_key,c.is_nullable, c.data_type, c.column_type,c.extra,c.privileges, " +
"c.column_comment,c.column_default,c.data_type,c.character_maximum_length, " +
"k.constraint_name, k.referenced_table_name, k.referenced_column_name, " +
"s.index_name,s.seq_in_index, " +
"v.table_name as isView " +
"from " +
"information_schema.columns as c " +
"left join " +
"information_schema.key_column_usage as k " +
"on " +
"c.column_name=k.column_name and " +
"c.table_schema = k.referenced_table_schema and " +
"c.table_name = k.table_name " +
"left join " +
"information_schema.statistics as s " +
"on " +
"c.column_name = s.column_name and " +
"c.table_schema = s.index_schema and " +
"c.table_name = s.table_name " +
"LEFT JOIN " +
"information_schema.VIEWS as v " +
"ON " +
"c.table_schema = v.table_schema and " +
"c.table_name = v.table_name " +
"where " +
"c.table_schema=? " +
"order by " +
"c.table_name, c.ordinal_position"
);
};
exports.getChartQuery = function() {
return 'select ? as ??, count(*) as _count from ?? where ?? between ? and ? '
}
return "select ? as ??, count(*) as _count from ?? where ?? between ? and ? ";
};
exports.getDataType = function(colType, typesArr) {
// console.log(colType,typesArr);
@ -171,29 +158,40 @@ exports.getDataType = function(colType, typesArr) {
}
}
return 0;
}
};
exports.getColumnType = function(column) {
let strTypes = ['varchar', 'text', 'char', 'tinytext', 'mediumtext', 'longtext', 'blob', 'mediumblob', 'longblob', 'tinyblob', 'binary', 'varbinary'];
let intTypes = ['int', 'long', 'smallint', 'mediumint', 'bigint', 'tinyint'];
let flatTypes = ['float', 'double', 'decimal'];
let dateTypes = ['date', 'datetime', 'timestamp', 'time', 'year'];
let strTypes = [
"varchar",
"text",
"char",
"tinytext",
"mediumtext",
"longtext",
"blob",
"mediumblob",
"longblob",
"tinyblob",
"binary",
"varbinary"
];
let intTypes = ["int", "long", "smallint", "mediumint", "bigint", "tinyint"];
let flatTypes = ["float", "double", "decimal"];
let dateTypes = ["date", "datetime", "timestamp", "time", "year"];
//console.log(column);
if (this.getDataType(column['data_type'], strTypes)) {
return "string"
} else if (this.getDataType(column['data_type'], intTypes)) {
return "int"
} else if (this.getDataType(column['data_type'], flatTypes)) {
return "float"
} else if (this.getDataType(column['data_type'], dateTypes)) {
return "date"
if (this.getDataType(column["data_type"], strTypes)) {
return "string";
} else if (this.getDataType(column["data_type"], intTypes)) {
return "int";
} else if (this.getDataType(column["data_type"], flatTypes)) {
return "float";
} else if (this.getDataType(column["data_type"], dateTypes)) {
return "date";
} else {
return "string"
}
return "string";
}
};
exports.getType = function(colType, typesArr) {
for (let i = 0; i < typesArr.length; ++i) {
@ -206,6 +204,4 @@ exports.getType = function(colType, typesArr) {
}
}
return 0;
}
};

274
lib/util/whereClause.helper.js

@ -1,4 +1,4 @@
'use strict';
"use strict";
/**
*
@ -6,70 +6,62 @@
* @returns obj.query = (?,?,?,?) obj.params = [1,2,3,4]
*/
function prepareInClauseParams(input) {
let inElems = input.split(',')
let obj = {}
obj.whereQuery = ''
obj.whereParams = []
let inElems = input.split(",");
let obj = {};
obj.whereQuery = "";
obj.whereParams = [];
for (var j = 0; j < inElems.length; ++j) {
if (j === 0) {
//enclose with open parenthesis
obj.whereQuery += '('
obj.whereQuery += "(";
}
if (j) {
obj.whereQuery += ','
obj.whereQuery += ",";
}
// add q mark and push the variable
obj.whereQuery += '?'
obj.whereParams.push(inElems[j])
obj.whereQuery += "?";
obj.whereParams.push(inElems[j]);
if (j === inElems.length - 1) {
//enclose with closing parenthesis
obj.whereQuery += ')'
obj.whereQuery += ")";
}
}
//console.log(obj);
return obj
return obj;
}
function prepareLikeValue(value) {
//return value.replace("~", "%");
return value.replace(/~/g, '%');
return value.replace(/~/g, "%");
}
function prepareIsValue(value) {
//return value.replace("~", "%");
if (value === 'null') {
if (value === "null") {
return null;
} else if (value === 'true') {
} else if (value === "true") {
return true;
} else if (value === 'false') {
} else if (value === "false") {
return false;
} else {
return null
return null;
}
}
function prepareBetweenValue(value) {
let values = value.split(',')
let obj = {}
obj.whereQuery = ''
obj.whereParams = []
let values = value.split(",");
let obj = {};
obj.whereQuery = "";
obj.whereParams = [];
if (values.length === 2) {
obj.whereQuery = ' ? and ? '
obj.whereParams.push(values[0])
obj.whereParams.push(values[1])
obj.whereQuery = " ? and ? ";
obj.whereParams.push(values[0]);
obj.whereParams.push(values[1]);
}
//console.log('prepareBetweenValue', obj);
@ -77,75 +69,78 @@ function prepareBetweenValue(value) {
return obj;
}
function replaceWhereParamsToQMarks(openParentheses, str, comparisonOperator, condType) {
let converted = ''
function replaceWhereParamsToQMarks(
openParentheses,
str,
comparisonOperator,
condType
) {
let converted = "";
if (openParentheses) {
for (var i = 0; i < str.length; ++i) {
if (str[i] === '(') {
converted += '('
if (str[i] === "(") {
converted += "(";
} else {
converted += '??'
converted += "??";
break;
}
}
} else {
if (comparisonOperator !== ' in ' && comparisonOperator !== ' between ' && condType !== ' on ') {
converted = '?'
} else if (condType === ' on ') {
converted = '??'
if (
comparisonOperator !== " in " &&
comparisonOperator !== " between " &&
condType !== " on "
) {
converted = "?";
} else if (condType === " on ") {
converted = "??";
}
for (var i = str.length - 1; i >= 0; --i) {
if (str[i] === ')') {
converted += ')'
if (str[i] === ")") {
converted += ")";
} else {
break;
}
}
}
return converted;
}
function getComparisonOperator(operator) {
switch (operator) {
case 'eq':
return '='
case "eq":
return "=";
break;
case 'ne':
return '!='
case "ne":
return "!=";
break;
case 'lt':
return '<'
case "lt":
return "<";
break;
case 'lte':
return '<='
case "lte":
return "<=";
break;
case 'gt':
return '>'
case "gt":
return ">";
break;
case 'gte':
return '>='
case "gte":
return ">=";
break;
case 'is':
return ' is '
case "is":
return " is ";
break;
case 'isnot':
return ' is not '
case "isnot":
return " is not ";
break;
// case 'isnull':
@ -156,89 +151,90 @@ function getComparisonOperator(operator) {
// return ' is not NULL '
// break;
case 'like':
return ' like '
case "like":
return " like ";
break;
case 'nlike':
return ' not like '
case "nlike":
return " not like ";
break;
case 'in':
return ' in '
case "in":
return " in ";
break;
case 'bw':
return ' between '
case "bw":
return " between ";
break;
default:
return null
return null;
break;
}
}
function getLogicalOperator(operator) {
switch (operator) {
case '~or':
return 'or'
case "~or":
return "or";
break;
case '~and':
return 'and'
case "~and":
return "and";
break;
// case '~not':
// return 'not'
// break;
case '~xor':
return 'xor'
case "~xor":
return "xor";
break;
default:
return null
return null;
break;
}
}
exports.getConditionClause = function (whereInQueryParams, condType = 'where') {
let whereQuery = '';
exports.getConditionClause = function(whereInQueryParams, condType = "where") {
let whereQuery = "";
let whereParams = [];
let grammarErr = 0;
let numOfConditions = whereInQueryParams.split(/~or|~and|~not|~xor/);
let logicalOperatorsInClause = whereInQueryParams.match(/(~or|~and|~not|~xor)/g)
if (numOfConditions && logicalOperatorsInClause && numOfConditions.length !== logicalOperatorsInClause.length + 1) {
console.log('conditions and logical operators mismatch', numOfConditions.length, logicalOperatorsInClause.length);
let logicalOperatorsInClause = whereInQueryParams.match(
/(~or|~and|~not|~xor)/g
);
if (
numOfConditions &&
logicalOperatorsInClause &&
numOfConditions.length !== logicalOperatorsInClause.length + 1
) {
console.log(
"conditions and logical operators mismatch",
numOfConditions.length,
logicalOperatorsInClause.length
);
} else {
//console.log('numOfConditions',numOfConditions,whereInQueryParams);
//console.log('logicalOperatorsInClause',logicalOperatorsInClause);
for (var i = 0; i < numOfConditions.length; ++i) {
let variable = ''
let comparisonOperator = ''
let logicalOperator = ''
let variableValue = ''
let temp = ''
let variable = "";
let comparisonOperator = "";
let logicalOperator = "";
let variableValue = "";
let temp = "";
// split on commas
var arr = numOfConditions[i].split(',');
var arr = numOfConditions[i].split(",");
// consider first two splits only
var result = arr.splice(0, 2);
// join to make only 3 array elements
result.push(arr.join(','));
result.push(arr.join(","));
// variable, operator, variablevalue
if (result.length !== 3) {
@ -258,81 +254,86 @@ exports.getConditionClause = function (whereInQueryParams, condType = 'where') {
}
// get the variableName and push
whereParams.push(variable[1])
whereParams.push(variable[1]);
// then replace the variable name with ??
temp = replaceWhereParamsToQMarks(true, result[0], ' ignore ', condType)
temp = replaceWhereParamsToQMarks(true, result[0], " ignore ", condType);
if (!temp) {
grammarErr = 1;
break;
}
whereQuery += temp
whereQuery += temp;
/**************** END : variable ****************/
/**************** START : operator and value ****************/
comparisonOperator = getComparisonOperator(result[1])
comparisonOperator = getComparisonOperator(result[1]);
if (!comparisonOperator) {
grammarErr = 1;
break;
}
whereQuery += comparisonOperator
whereQuery += comparisonOperator;
// get the variableValue and push to params
variableValue = result[2].match(/(.*?)\)/)
variableValue = result[2].match(/(.*?)\)/);
if (!variableValue || variableValue.length !== 2) {
grammarErr = 1;
break;
}
if (comparisonOperator === ' in ') {
let obj = prepareInClauseParams(variableValue[1])
whereQuery += obj.whereQuery
whereParams = whereParams.concat(obj.whereParams)
} else if (comparisonOperator === ' like ' || comparisonOperator === ' not like ') {
whereParams.push(prepareLikeValue(variableValue[1]))
} else if (comparisonOperator === ' is ') {
whereParams.push(prepareIsValue(variableValue[1]))
} else if (comparisonOperator === ' between ') {
let obj = prepareBetweenValue(variableValue[1])
whereQuery += obj.whereQuery
whereParams = whereParams.concat(obj.whereParams)
if (comparisonOperator === " in ") {
let obj = prepareInClauseParams(variableValue[1]);
whereQuery += obj.whereQuery;
whereParams = whereParams.concat(obj.whereParams);
} else if (
comparisonOperator === " like " ||
comparisonOperator === " not like "
) {
whereParams.push(prepareLikeValue(variableValue[1]));
} else if (comparisonOperator === " is ") {
whereParams.push(prepareIsValue(variableValue[1]));
} else if (comparisonOperator === " between ") {
let obj = prepareBetweenValue(variableValue[1]);
whereQuery += obj.whereQuery;
whereParams = whereParams.concat(obj.whereParams);
//console.log(whereQuery, whereParams);
} else {
whereParams.push(variableValue[1])
whereParams.push(variableValue[1]);
}
// then replace the variableValue with ?
temp = replaceWhereParamsToQMarks(false, result[2], comparisonOperator, condType)
temp = replaceWhereParamsToQMarks(
false,
result[2],
comparisonOperator,
condType
);
if (!temp) {
grammarErr = 1;
break;
}
whereQuery += temp
whereQuery += temp;
if (numOfConditions.length !== -1 && i !== numOfConditions.length - 1) {
//console.log('finding logical operator for',logicalOperatorsInClause[i]);
logicalOperator = getLogicalOperator(logicalOperatorsInClause[i])
logicalOperator = getLogicalOperator(logicalOperatorsInClause[i]);
if (!logicalOperator) {
grammarErr = 1;
break;
}
whereQuery += getLogicalOperator(logicalOperatorsInClause[i])
whereQuery += getLogicalOperator(logicalOperatorsInClause[i]);
}
/**************** END : operator ****************/
}
}
let obj = {}
let obj = {};
obj['query'] = ''
obj['params'] = []
obj['err'] = grammarErr
obj["query"] = "";
obj["params"] = [];
obj["err"] = grammarErr;
// console.log(whereInQueryParams);
// console.log(whereQuery);
@ -341,10 +342,9 @@ exports.getConditionClause = function (whereInQueryParams, condType = 'where') {
// console.log('= = = = = = = = =');
if (!grammarErr) {
obj['query'] = whereQuery
obj['params'] = whereParams
obj["query"] = whereQuery;
obj["params"] = whereParams;
}
return obj
}
return obj;
};

425
lib/xapi.js

@ -1,380 +1,424 @@
'use strict';
"use strict";
var Xsql = require('./xsql.js');
var Xctrl = require('./xctrl.js');
var multer = require('multer');
const path = require('path');
var Xsql = require("./xsql.js");
var Xctrl = require("./xctrl.js");
var multer = require("multer");
const path = require("path");
const v8 = require('v8'),
os = require('os');
const v8 = require("v8"),
os = require("os");
//define class
class Xapi {
constructor(args, mysqlPool, app) {
this.config = args;
this.mysql = new Xsql(args, mysqlPool)
this.mysql = new Xsql(args, mysqlPool);
this.app = app;
this.ctrls = [];
/**************** START : multer ****************/
this.storage = multer.diskStorage({
destination: function(req, file, cb) {
cb(null, process.cwd())
cb(null, process.cwd());
},
filename: function(req, file, cb) {
console.log(file);
cb(null, Date.now() + '-' + file.originalname)
cb(null, Date.now() + "-" + file.originalname);
}
})
});
this.upload = multer({storage: this.storage})
this.upload = multer({ storage: this.storage });
/**************** END : multer ****************/
}
init(cbk) {
this.mysql.init((err, results) => {
this.app.use(this.urlMiddleware.bind(this))
let stat = this.setupRoutes()
this.app.use(this.errorMiddleware.bind(this))
cbk(err, stat)
})
this.app.use(this.urlMiddleware.bind(this));
let stat = this.setupRoutes();
this.app.use(this.errorMiddleware.bind(this));
cbk(err, stat);
});
}
urlMiddleware(req, res, next) {
// get only request url from originalUrl
let justUrl = req.originalUrl.split('?')[0]
let pathSplit = []
let justUrl = req.originalUrl.split("?")[0];
let pathSplit = [];
// split by apiPrefix
let apiSuffix = justUrl.split(this.config.apiPrefix)
let apiSuffix = justUrl.split(this.config.apiPrefix);
if (apiSuffix.length === 2) {
// split by /
pathSplit = apiSuffix[1].split('/')
pathSplit = apiSuffix[1].split("/");
if (pathSplit.length) {
if (pathSplit.length >= 3) {
// handle for relational routes
req.app.locals._parentTable = pathSplit[0]
req.app.locals._childTable = pathSplit[2]
req.app.locals._parentTable = pathSplit[0];
req.app.locals._childTable = pathSplit[2];
} else {
// handles rest of routes
req.app.locals._tableName = pathSplit[0]
req.app.locals._tableName = pathSplit[0];
}
}
}
next();
}
errorMiddleware(err, req, res, next) {
if (err && err.code)
res.status(400).json({error: err});
if (err && err.code) res.status(400).json({ error: err });
else if (err && err.message)
res.status(500).json({error: 'Internal server error : ' + err.message});
else
res.status(500).json({error: 'Internal server error : ' + err});
res.status(500).json({ error: "Internal server error : " + err.message });
else res.status(500).json({ error: "Internal server error : " + err });
next(err);
}
asyncMiddleware(fn) {
return (req, res, next) => {
Promise.resolve(fn(req, res, next))
.catch((err) => {
Promise.resolve(fn(req, res, next)).catch(err => {
next(err);
});
}
};
}
root(req, res) {
let routes = [];
routes = this.mysql.getSchemaRoutes(false, req.protocol + '://' + req.get('host') + this.config.apiPrefix);
routes = routes.concat(this.mysql.globalRoutesPrint(req.protocol + '://' + req.get('host') + this.config.apiPrefix))
res.json(routes)
routes = this.mysql.getSchemaRoutes(
false,
req.protocol + "://" + req.get("host") + this.config.apiPrefix
);
routes = routes.concat(
this.mysql.globalRoutesPrint(
req.protocol + "://" + req.get("host") + this.config.apiPrefix
)
);
res.json(routes);
}
setupRoutes() {
let stat = {}
stat.tables = 0
stat.apis = 0
let stat = {};
stat.tables = 0;
stat.apis = 0;
// console.log('this.config while setting up routes', this.config);
// show routes for database schema
this.app.get('/', this.asyncMiddleware(this.root.bind(this)))
this.app.get("/", this.asyncMiddleware(this.root.bind(this)));
// show all resouces
this.app.route(this.config.apiPrefix + 'tables')
this.app
.route(this.config.apiPrefix + "tables")
.get(this.asyncMiddleware(this.tables.bind(this)));
this.app.route(this.config.apiPrefix + 'xjoin')
this.app
.route(this.config.apiPrefix + "xjoin")
.get(this.asyncMiddleware(this.xjoin.bind(this)));
stat.apis += 3;
/**************** START : setup routes for each table ****************/
let resources = [];
resources = this.mysql.getSchemaRoutes(true, this.config.apiPrefix);
stat.tables += resources.length
stat.tables += resources.length;
// iterate over each resource
for (var j = 0; j < resources.length; ++j) {
let resourceCtrl = new Xctrl(this.app, this.mysql)
let resourceCtrl = new Xctrl(this.app, this.mysql);
this.ctrls.push(resourceCtrl);
let routes = resources[j]['routes'];
let routes = resources[j]["routes"];
stat.apis += resources[j]['routes'].length
stat.apis += resources[j]["routes"].length;
// iterate over each routes in resource and map function
for (var i = 0; i < routes.length; ++i) {
switch (routes[i]['routeType']) {
case 'list':
this.app.route(routes[i]['routeUrl'])
switch (routes[i]["routeType"]) {
case "list":
this.app
.route(routes[i]["routeUrl"])
.get(this.asyncMiddleware(resourceCtrl.list.bind(resourceCtrl)));
break;
case 'findOne':
this.app.route(routes[i]['routeUrl'])
.get(this.asyncMiddleware(resourceCtrl.findOne.bind(resourceCtrl)));
case "findOne":
this.app
.route(routes[i]["routeUrl"])
.get(
this.asyncMiddleware(resourceCtrl.findOne.bind(resourceCtrl))
);
break;
case 'create':
case "create":
if (!this.config.readOnly)
this.app.route(routes[i]['routeUrl'])
.post(this.asyncMiddleware(resourceCtrl.create.bind(resourceCtrl)));
this.app
.route(routes[i]["routeUrl"])
.post(
this.asyncMiddleware(resourceCtrl.create.bind(resourceCtrl))
);
break;
case 'read':
this.app.route(routes[i]['routeUrl'])
case "read":
this.app
.route(routes[i]["routeUrl"])
.get(this.asyncMiddleware(resourceCtrl.read.bind(resourceCtrl)));
break;
case 'bulkInsert':
case "bulkInsert":
if (!this.config.readOnly) {
this.app.route(routes[i]['routeUrl'])
.post(this.asyncMiddleware(resourceCtrl.bulkInsert.bind(resourceCtrl)));
this.app
.route(routes[i]["routeUrl"])
.post(
this.asyncMiddleware(
resourceCtrl.bulkInsert.bind(resourceCtrl)
)
);
}
break;
case 'bulkRead':
case "bulkRead":
if (!this.config.readOnly) {
this.app.route(routes[i]['routeUrl'])
.get(this.asyncMiddleware(resourceCtrl.bulkRead.bind(resourceCtrl)));
this.app
.route(routes[i]["routeUrl"])
.get(
this.asyncMiddleware(resourceCtrl.bulkRead.bind(resourceCtrl))
);
} else {
stat.apis--;
}
break;
case 'bulkDelete':
case "bulkDelete":
if (!this.config.readOnly) {
this.app.route(routes[i]['routeUrl'])
.delete(this.asyncMiddleware(resourceCtrl.bulkDelete.bind(resourceCtrl)));
this.app
.route(routes[i]["routeUrl"])
.delete(
this.asyncMiddleware(
resourceCtrl.bulkDelete.bind(resourceCtrl)
)
);
} else {
stat.apis--;
}
break;
case 'patch':
case "patch":
if (!this.config.readOnly) {
this.app.route(routes[i]['routeUrl'])
.patch(this.asyncMiddleware(resourceCtrl.patch.bind(resourceCtrl)));
this.app
.route(routes[i]["routeUrl"])
.patch(
this.asyncMiddleware(resourceCtrl.patch.bind(resourceCtrl))
);
} else {
stat.apis--;
}
break;
case 'update':
case "update":
if (!this.config.readOnly) {
this.app.route(routes[i]['routeUrl'])
.put(this.asyncMiddleware(resourceCtrl.update.bind(resourceCtrl)));
this.app
.route(routes[i]["routeUrl"])
.put(
this.asyncMiddleware(resourceCtrl.update.bind(resourceCtrl))
);
} else {
stat.apis--;
}
break;
case 'delete':
case "delete":
if (!this.config.readOnly) {
this.app.route(routes[i]['routeUrl'])
.delete(this.asyncMiddleware(resourceCtrl.delete.bind(resourceCtrl)));
this.app
.route(routes[i]["routeUrl"])
.delete(
this.asyncMiddleware(resourceCtrl.delete.bind(resourceCtrl))
);
} else {
stat.apis--;
}
break;
case 'exists':
this.app.route(routes[i]['routeUrl'])
.get(this.asyncMiddleware(resourceCtrl.exists.bind(resourceCtrl)));
case "exists":
this.app
.route(routes[i]["routeUrl"])
.get(
this.asyncMiddleware(resourceCtrl.exists.bind(resourceCtrl))
);
break;
case 'count':
this.app.route(routes[i]['routeUrl'])
case "count":
this.app
.route(routes[i]["routeUrl"])
.get(this.asyncMiddleware(resourceCtrl.count.bind(resourceCtrl)));
break;
case 'distinct':
this.app.route(routes[i]['routeUrl'])
.get(this.asyncMiddleware(resourceCtrl.distinct.bind(resourceCtrl)));
case "distinct":
this.app
.route(routes[i]["routeUrl"])
.get(
this.asyncMiddleware(resourceCtrl.distinct.bind(resourceCtrl))
);
break;
case 'describe':
this.app.route(routes[i]['routeUrl'])
case "describe":
this.app
.route(routes[i]["routeUrl"])
.get(this.asyncMiddleware(this.tableDescribe.bind(this)));
break;
case 'relational':
this.app.route(routes[i]['routeUrl'])
.get(this.asyncMiddleware(resourceCtrl.nestedList.bind(resourceCtrl)));
case "relational":
this.app
.route(routes[i]["routeUrl"])
.get(
this.asyncMiddleware(resourceCtrl.nestedList.bind(resourceCtrl))
);
break;
case 'groupby':
this.app.route(routes[i]['routeUrl'])
.get(this.asyncMiddleware(resourceCtrl.groupBy.bind(resourceCtrl)));
case "groupby":
this.app
.route(routes[i]["routeUrl"])
.get(
this.asyncMiddleware(resourceCtrl.groupBy.bind(resourceCtrl))
);
break;
case 'ugroupby':
this.app.route(routes[i]['routeUrl'])
.get(this.asyncMiddleware(resourceCtrl.ugroupby.bind(resourceCtrl)));
case "ugroupby":
this.app
.route(routes[i]["routeUrl"])
.get(
this.asyncMiddleware(resourceCtrl.ugroupby.bind(resourceCtrl))
);
break;
case 'chart':
this.app.route(routes[i]['routeUrl'])
case "chart":
this.app
.route(routes[i]["routeUrl"])
.get(this.asyncMiddleware(resourceCtrl.chart.bind(resourceCtrl)));
break;
case 'autoChart':
this.app.route(routes[i]['routeUrl'])
.get(this.asyncMiddleware(resourceCtrl.autoChart.bind(resourceCtrl)));
case "autoChart":
this.app
.route(routes[i]["routeUrl"])
.get(
this.asyncMiddleware(resourceCtrl.autoChart.bind(resourceCtrl))
);
break;
case 'aggregate':
this.app.route(routes[i]['routeUrl'])
.get(this.asyncMiddleware(resourceCtrl.aggregate.bind(resourceCtrl)));
case "aggregate":
this.app
.route(routes[i]["routeUrl"])
.get(
this.asyncMiddleware(resourceCtrl.aggregate.bind(resourceCtrl))
);
break;
}
}
}
/**************** END : setup routes for each table ****************/
if (this.config.dynamic === 1 && !this.config.readOnly) {
this.app.route('/dynamic*')
this.app
.route("/dynamic*")
.post(this.asyncMiddleware(this.runQuery.bind(this)));
/**************** START : multer routes ****************/
this.app.post('/upload', this.upload.single('file'), this.uploadFile.bind(this));
this.app.post('/uploads', this.upload.array('files', 10), this.uploadFiles.bind(this));
this.app.get('/download', this.downloadFile.bind(this));
this.app.post(
"/upload",
this.upload.single("file"),
this.uploadFile.bind(this)
);
this.app.post(
"/uploads",
this.upload.array("files", 10),
this.uploadFiles.bind(this)
);
this.app.get("/download", this.downloadFile.bind(this));
/**************** END : multer routes ****************/
stat.apis += 4;
}
/**************** START : health and version ****************/
this.app.get('/_health', this.asyncMiddleware(this.health.bind(this)));
this.app.get('/_version', this.asyncMiddleware(this.version.bind(this)));
this.app.get("/_health", this.asyncMiddleware(this.health.bind(this)));
this.app.get("/_version", this.asyncMiddleware(this.version.bind(this)));
stat.apis += 2;
/**************** END : health and version ****************/
let statStr = ' Generated: ' + stat.apis + ' REST APIs for ' + stat.tables + ' tables '
console.log(' - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ');
console.log(' ');
console.log(' Database : %s', this.config.database);
console.log(' Number of Tables : %s', stat.tables);
console.log(' ');
console.log(' REST APIs Generated : %s'.green.bold, stat.apis);
console.log(' ');
return stat
let statStr =
" Generated: " +
stat.apis +
" REST APIs for " +
stat.tables +
" tables ";
console.log(
" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "
);
console.log(" ");
console.log(
" Database : %s",
this.config.database
);
console.log(" Number of Tables : %s", stat.tables);
console.log(" ");
console.log(
" REST APIs Generated : %s".green.bold,
stat.apis
);
console.log(" ");
return stat;
}
async xjoin(req, res) {
let obj = {};
let obj = {}
obj.query = '';
obj.query = "";
obj.params = [];
this.mysql.prepareJoinQuery(req, res, obj)
this.mysql.prepareJoinQuery(req, res, obj);
//console.log(obj);
if (obj.query.length) {
let results = await this.mysql.exec(obj.query, obj.params)
res.status(200).json(results)
let results = await this.mysql.exec(obj.query, obj.params);
res.status(200).json(results);
} else {
res.status(400).json({err: 'Invalid Xjoin request'});
res.status(400).json({ err: "Invalid Xjoin request" });
}
}
async tableDescribe(req, res) {
let query = 'describe ??';
let query = "describe ??";
let params = [req.app.locals._tableName];
let results = await this.mysql.exec(query, params);
res.status(200).json(results);
}
async tables(req, res) {
let query = 'SELECT table_name AS resource FROM information_schema.tables WHERE table_schema = ? ';
let query =
"SELECT table_name AS resource FROM information_schema.tables WHERE table_schema = ? ";
let params = [this.config.database];
if (Object.keys(this.config.ignoreTables).length > 0) {
query += 'and table_name not in (?)'
params.push(Object.keys(this.config.ignoreTables))
query += "and table_name not in (?)";
params.push(Object.keys(this.config.ignoreTables));
}
let results = await this.mysql.exec(query, params)
let results = await this.mysql.exec(query, params);
res.status(200).json(results)
res.status(200).json(results);
}
async runQuery(req, res) {
let query = req.body.query;
let params = req.body.params;
let results = await this.mysql.exec(query, params);
res.status(200).json(results);
}
/**************** START : files related ****************/
downloadFile(req, res) {
let file = path.join(process.cwd(), req.query.name);
@ -382,19 +426,17 @@ class Xapi {
}
uploadFile(req, res) {
if (req.file) {
console.log(req.file.path);
res.end(req.file.path);
} else {
res.end('upload failed');
res.end("upload failed");
}
}
uploadFiles(req, res) {
if (!req.files || req.files.length === 0) {
res.end('upload failed')
res.end("upload failed");
} else {
let files = [];
for (let i = 0; i < req.files.length; ++i) {
@ -403,58 +445,49 @@ class Xapi {
res.end(files.toString());
}
}
/**************** END : files related ****************/
/**************** START : health and version ****************/
async getMysqlUptime() {
let v = await this.mysql.exec('SHOW GLOBAL STATUS LIKE \'Uptime\';', []);
return v[0]['Value'];
let v = await this.mysql.exec("SHOW GLOBAL STATUS LIKE 'Uptime';", []);
return v[0]["Value"];
}
async getMysqlHealth() {
let v = await this.mysql.exec('select version() as version', []);
return v[0]['version'];
let v = await this.mysql.exec("select version() as version", []);
return v[0]["version"];
}
async health(req, res) {
let status = {};
status['process_uptime'] = process.uptime();
status['mysql_uptime'] = await this.getMysqlUptime();
status["process_uptime"] = process.uptime();
status["mysql_uptime"] = await this.getMysqlUptime();
if (Object.keys(req.query).length) {
status['process_memory_usage'] = process.memoryUsage();
status['os_total_memory'] = os.totalmem();
status['os_free_memory'] = os.freemem();
status['os_load_average'] = os.loadavg();
status['v8_heap_statistics'] = v8.getHeapStatistics();
status["process_memory_usage"] = process.memoryUsage();
status["os_total_memory"] = os.totalmem();
status["os_free_memory"] = os.freemem();
status["os_load_average"] = os.loadavg();
status["v8_heap_statistics"] = v8.getHeapStatistics();
}
res.json(status);
}
async version(req, res) {
let version = {};
version['Xmysql'] = this.app.get('version');
version['mysql'] = await this.getMysqlHealth();
version['node'] = process.versions.node;
version["Xmysql"] = this.app.get("version");
version["mysql"] = await this.getMysqlHealth();
version["node"] = process.versions.node;
res.json(version);
}
/**************** END : health and version ****************/
}
//expose class
module.exports = Xapi;

501
lib/xctrl.js

@ -1,16 +1,12 @@
//define class
class xctrl {
constructor(app, mysql) {
this.app = app;
this.mysql = mysql;
}
async create(req, res) {
let query = 'INSERT INTO ?? SET ?';
let query = "INSERT INTO ?? SET ?";
let params = [];
params.push(req.app.locals._tableName);
@ -18,105 +14,102 @@ class xctrl {
var results = await this.mysql.exec(query, params);
res.status(200).json(results);
}
async list(req, res) {
let queryParamsObj = {}
queryParamsObj.query = ''
queryParamsObj.params = []
let queryParamsObj = {};
queryParamsObj.query = "";
queryParamsObj.params = [];
this.mysql.prepareListQuery(req, res, queryParamsObj, 0);
let results = await this.mysql.exec(queryParamsObj.query, queryParamsObj.params);
let results = await this.mysql.exec(
queryParamsObj.query,
queryParamsObj.params
);
res.status(200).json(results);
}
async nestedList(req, res) {
let queryParamsObj = {}
queryParamsObj.query = '';
let queryParamsObj = {};
queryParamsObj.query = "";
queryParamsObj.params = [];
this.mysql.prepareListQuery(req, res, queryParamsObj, 1)
this.mysql.prepareListQuery(req, res, queryParamsObj, 1);
let results = await this.mysql.exec(queryParamsObj.query, queryParamsObj.params);
let results = await this.mysql.exec(
queryParamsObj.query,
queryParamsObj.params
);
res.status(200).json(results);
}
async findOne(req, res) {
let queryParamsObj = {}
queryParamsObj.query = ''
queryParamsObj.params = []
let queryParamsObj = {};
queryParamsObj.query = "";
queryParamsObj.params = [];
this.mysql.prepareListQuery(req, res, queryParamsObj, 2);
let results = await this.mysql.exec(queryParamsObj.query, queryParamsObj.params);
let results = await this.mysql.exec(
queryParamsObj.query,
queryParamsObj.params
);
res.status(200).json(results);
}
async read(req, res) {
let query = 'select * from ?? where ';
let query = "select * from ?? where ";
let params = [];
params.push(req.app.locals._tableName);
let clause = this.mysql.getPrimaryKeyWhereClause(req.app.locals._tableName,
req.params.id.split('___'));
let clause = this.mysql.getPrimaryKeyWhereClause(
req.app.locals._tableName,
req.params.id.split("___")
);
if (!clause) {
return res.status(400).send({
error: "Table is made of composite primary keys - all keys were not in input"
error:
"Table is made of composite primary keys - all keys were not in input"
});
}
query += clause;
query += ' LIMIT 1'
query += " LIMIT 1";
let results = await this.mysql.exec(query, params);
res.status(200).json(results);
}
async exists(req, res) {
let query = 'select * from ?? where ';
let query = "select * from ?? where ";
let params = [];
params.push(req.app.locals._tableName);
let clause = this.mysql.getPrimaryKeyWhereClause(req.app.locals._tableName,
req.params.id.split('___'));
let clause = this.mysql.getPrimaryKeyWhereClause(
req.app.locals._tableName,
req.params.id.split("___")
);
if (!clause) {
return res.status(400).send({
error: "Table is made of composite primary keys - all keys were not in input"
})
error:
"Table is made of composite primary keys - all keys were not in input"
});
}
query += clause;
query += ' LIMIT 1'
query += " LIMIT 1";
let results = await this.mysql.exec(query, params);
res.status(200).json(results);
}
async update(req, res) {
let query = 'REPLACE INTO ?? SET ?';
let query = "REPLACE INTO ?? SET ?";
let params = [];
params.push(req.app.locals._tableName);
@ -124,31 +117,31 @@ class xctrl {
var results = await this.mysql.exec(query, params);
res.status(200).json(results);
}
async patch(req, res) {
let query = 'UPDATE ?? SET ';
let query = "UPDATE ?? SET ";
let keys = Object.keys(req.body);
// SET clause
let updateKeys = '';
let updateKeys = "";
for (let i = 0; i < keys.length; ++i) {
updateKeys += keys[i] + ' = ? '
if (i !== keys.length - 1)
updateKeys += ', '
updateKeys += keys[i] + " = ? ";
if (i !== keys.length - 1) updateKeys += ", ";
}
// where clause
query += updateKeys + ' where '
let clause = this.mysql.getPrimaryKeyWhereClause(req.app.locals._tableName,
req.params.id.split('___'));
query += updateKeys + " where ";
let clause = this.mysql.getPrimaryKeyWhereClause(
req.app.locals._tableName,
req.params.id.split("___")
);
if (!clause) {
return res.status(400).send({
error: "Table is made of composite primary keys - all keys were not in input"
})
error:
"Table is made of composite primary keys - all keys were not in input"
});
}
query += clause;
@ -160,23 +153,23 @@ class xctrl {
let results = await this.mysql.exec(query, params);
res.status(200).json(results);
}
async delete(req, res) {
let query = 'DELETE FROM ?? WHERE ';
let query = "DELETE FROM ?? WHERE ";
let params = [];
params.push(req.app.locals._tableName);
let clause = this.mysql.getPrimaryKeyWhereClause(req.app.locals._tableName,
req.params.id.split('___'));
let clause = this.mysql.getPrimaryKeyWhereClause(
req.app.locals._tableName,
req.params.id.split("___")
);
if (!clause) {
return res.status(400).send({
error: "Table is made of composite primary keys - all keys were not in input"
error:
"Table is made of composite primary keys - all keys were not in input"
});
}
@ -184,278 +177,306 @@ class xctrl {
let results = await this.mysql.exec(query, params);
res.status(200).json(results);
}
async bulkInsert(req, res) {
let queryParamsObj = {}
queryParamsObj.query = ''
queryParamsObj.params = []
let results = []
let queryParamsObj = {};
queryParamsObj.query = "";
queryParamsObj.params = [];
let results = [];
//console.log(req.app.locals._tableName, req.body);
this.mysql.prepareBulkInsert(req.app.locals._tableName, req.body, queryParamsObj)
this.mysql.prepareBulkInsert(
req.app.locals._tableName,
req.body,
queryParamsObj
);
results = await this.mysql.exec(queryParamsObj.query, queryParamsObj.params);
results = await this.mysql.exec(
queryParamsObj.query,
queryParamsObj.params
);
res.status(200).json(results);
}
async bulkDelete(req, res) {
let query = 'delete from ?? where ?? in ';
let query = "delete from ?? where ?? in ";
let params = [];
params.push(req.app.locals._tableName);
params.push(this.mysql.getPrimaryKeyName(req.app.locals._tableName));
query += '('
query += "(";
if (req.query && req.query._ids) {
let ids = req.query._ids.split(',')
let ids = req.query._ids.split(",");
for (var i = 0; i < ids.length; ++i) {
if (i) {
query += ','
query += ",";
}
query += '?'
params.push(ids[i])
query += "?";
params.push(ids[i]);
}
}
query += ')'
query += ")";
//console.log(query, params);
var results = await this.mysql.exec(query, params);
res.status(200).json(results);
}
async bulkRead(req, res) {
let queryParamsObj = {}
queryParamsObj.query = ''
queryParamsObj.params = []
let queryParamsObj = {};
queryParamsObj.query = "";
queryParamsObj.params = [];
this.mysql.prepareListQuery(req, res, queryParamsObj, 3);
//console.log(queryParamsObj.query, queryParamsObj.params);
let results = await this.mysql.exec(queryParamsObj.query, queryParamsObj.params);
let results = await this.mysql.exec(
queryParamsObj.query,
queryParamsObj.params
);
res.status(200).json(results);
}
async count(req, res) {
let queryParams = {};
let queryParams = {}
queryParams.query = 'select count(1) as no_of_rows from ?? ';
queryParams.query = "select count(1) as no_of_rows from ?? ";
queryParams.params = [];
queryParams.params.push(req.app.locals._tableName);
this.mysql.getWhereClause(req.query._where, req.app.locals._tableName, queryParams, ' where ')
this.mysql.getWhereClause(
req.query._where,
req.app.locals._tableName,
queryParams,
" where "
);
let results = await this.mysql.exec(queryParams.query, queryParams.params);
res.status(200).json(results);
}
async distinct(req, res) {
let queryParamsObj = {}
queryParamsObj.query = ''
queryParamsObj.params = []
let queryParamsObj = {};
queryParamsObj.query = "";
queryParamsObj.params = [];
this.mysql.prepareListQuery(req, res, queryParamsObj, 4);
let results = await this.mysql.exec(queryParamsObj.query, queryParamsObj.params);
let results = await this.mysql.exec(
queryParamsObj.query,
queryParamsObj.params
);
res.status(200).json(results);
}
async groupBy(req, res) {
if (req.query && req.query._fields) {
let queryParamsObj = {}
queryParamsObj.query = 'select ';
let queryParamsObj = {};
queryParamsObj.query = "select ";
queryParamsObj.params = [];
/**************** add columns and group by columns ****************/
this.mysql.getColumnsForSelectStmt(req.app.locals._tableName, req.query, queryParamsObj)
this.mysql.getColumnsForSelectStmt(
req.app.locals._tableName,
req.query,
queryParamsObj
);
queryParamsObj.query += ',count(*) as _count from ?? group by ';
queryParamsObj.query += ",count(*) as _count from ?? group by ";
let tableName = req.app.locals._tableName;
queryParamsObj.params.push(tableName);
this.mysql.getColumnsForSelectStmt(req.app.locals._tableName, req.query, queryParamsObj)
this.mysql.getColumnsForSelectStmt(
req.app.locals._tableName,
req.query,
queryParamsObj
);
if (!req.query._sort) {
req.query._sort = {}
req.query._sort = '-_count'
req.query._sort = {};
req.query._sort = "-_count";
}
/**************** add having clause ****************/
this.mysql.getHavingClause(req.query._having, req.app.locals._tableName, queryParamsObj, ' having ');
this.mysql.getHavingClause(
req.query._having,
req.app.locals._tableName,
queryParamsObj,
" having "
);
/**************** add orderby clause ****************/
this.mysql.getOrderByClause(req.query, tableName, queryParamsObj);
//console.log(queryParamsObj.query, queryParamsObj.params);
var results = await this.mysql.exec(queryParamsObj.query, queryParamsObj.params);
var results = await this.mysql.exec(
queryParamsObj.query,
queryParamsObj.params
);
res.status(200).json(results);
} else {
res.status(400).json({message: 'Missing _fields query params eg: /api/tableName/groupby?_fields=column1'})
res
.status(400)
.json({
message:
"Missing _fields query params eg: /api/tableName/groupby?_fields=column1"
});
}
}
async ugroupby(req, res) {
if (req.query && req.query._fields) {
let queryParamsObj = {}
queryParamsObj.query = '';
let queryParamsObj = {};
queryParamsObj.query = "";
queryParamsObj.params = [];
let uGrpByResults = {}
let uGrpByResults = {};
/**************** add fields with count(*) *****************/
let fields = req.query._fields.split(',')
let fields = req.query._fields.split(",");
for (var i = 0; i < fields.length; ++i) {
uGrpByResults[fields[i]] = []
uGrpByResults[fields[i]] = [];
if (i) {
queryParamsObj.query += ' UNION '
queryParamsObj.query += " UNION ";
}
queryParamsObj.query += ' SELECT IFNULL(CONCAT(?,?,??),?) as ugroupby, count(*) as _count from ?? GROUP BY ?? '
queryParamsObj.params.push(fields[i])
queryParamsObj.params.push('~')
queryParamsObj.params.push(fields[i])
queryParamsObj.params.push(fields[i] + '~')
queryParamsObj.params.push(req.app.locals._tableName)
queryParamsObj.params.push(fields[i])
queryParamsObj.query +=
" SELECT IFNULL(CONCAT(?,?,??),?) as ugroupby, count(*) as _count from ?? GROUP BY ?? ";
queryParamsObj.params.push(fields[i]);
queryParamsObj.params.push("~");
queryParamsObj.params.push(fields[i]);
queryParamsObj.params.push(fields[i] + "~");
queryParamsObj.params.push(req.app.locals._tableName);
queryParamsObj.params.push(fields[i]);
}
//console.log(queryParamsObj.query, queryParamsObj.params);
var results = await this.mysql.exec(queryParamsObj.query, queryParamsObj.params);
var results = await this.mysql.exec(
queryParamsObj.query,
queryParamsObj.params
);
for (var i = 0; i < results.length; ++i) {
let grpByColName = results[i]["ugroupby"].split("~")[0];
let grpByColValue = results[i]["ugroupby"].split("~")[1];
let grpByColName = results[i]['ugroupby'].split('~')[0]
let grpByColValue = results[i]['ugroupby'].split('~')[1]
let obj = {}
obj[grpByColValue] = results[i]['_count'];
uGrpByResults[grpByColName].push(obj)
let obj = {};
obj[grpByColValue] = results[i]["_count"];
uGrpByResults[grpByColName].push(obj);
}
res.status(200).json(uGrpByResults);
} else {
res.status(400).json({message: 'Missing _fields query params eg: /api/tableName/ugroupby?_fields=column1,column2'})
res
.status(400)
.json({
message:
"Missing _fields query params eg: /api/tableName/ugroupby?_fields=column1,column2"
});
}
}
async aggregate(req, res) {
if (req.query && req.query._fields) {
let tableName = req.app.locals._tableName;
let query = 'select '
let params = []
let fields = req.query._fields.split(',');
let query = "select ";
let params = [];
let fields = req.query._fields.split(",");
for (var i = 0; i < fields.length; ++i) {
if (i) {
query = query + ','
query = query + ",";
}
query = query + ' min(??) as ?,max(??) as ?,avg(??) as ?,sum(??) as ?,stddev(??) as ?,variance(??) as ? '
query =
query +
" min(??) as ?,max(??) as ?,avg(??) as ?,sum(??) as ?,stddev(??) as ?,variance(??) as ? ";
params.push(fields[i]);
params.push('min_of_' + fields[i]);
params.push("min_of_" + fields[i]);
params.push(fields[i]);
params.push('max_of_' + fields[i]);
params.push("max_of_" + fields[i]);
params.push(fields[i]);
params.push('avg_of_' + fields[i]);
params.push("avg_of_" + fields[i]);
params.push(fields[i]);
params.push('sum_of_' + fields[i]);
params.push("sum_of_" + fields[i]);
params.push(fields[i]);
params.push('stddev_of_' + fields[i]);
params.push("stddev_of_" + fields[i]);
params.push(fields[i]);
params.push('variance_of_' + fields[i]);
params.push("variance_of_" + fields[i]);
}
query = query + ' from ??'
params.push(tableName)
query = query + " from ??";
params.push(tableName);
var results = await this.mysql.exec(query, params);
res.status(200).json(results);
} else {
res.status(400).json({message: 'Missing _fields in query params eg: /api/tableName/aggregate?_fields=numericColumn1'});
res
.status(400)
.json({
message:
"Missing _fields in query params eg: /api/tableName/aggregate?_fields=numericColumn1"
});
}
}
async chart(req, res) {
let query = ''
let params = []
let obj = {}
let query = "";
let params = [];
let obj = {};
if (req.query) {
let isRange = false
let isRange = false;
if (req.query.range) {
isRange = true
isRange = true;
}
if (req.query && req.query.min && req.query.max && req.query.step) {
//console.log(req.params.min, req.params.max, req.params.step);
obj = this.mysql.getChartQueryAndParamsFromMinMaxStep(req.app.locals._tableName,
obj = this.mysql.getChartQueryAndParamsFromMinMaxStep(
req.app.locals._tableName,
req.query._fields,
parseInt(req.query.min),
parseInt(req.query.max),
parseInt(req.query.step),
isRange)
} else if (req.query && req.query.steparray && req.query.steparray.length > 1) {
obj = this.mysql.getChartQueryAndParamsFromStepArray(req.app.locals._tableName,
isRange
);
} else if (
req.query &&
req.query.steparray &&
req.query.steparray.length > 1
) {
obj = this.mysql.getChartQueryAndParamsFromStepArray(
req.app.locals._tableName,
req.query._fields,
(req.query.steparray.split(',')).map(Number),
isRange)
} else if (req.query && req.query.steppair && req.query.steppair.length > 1) {
obj = this.mysql.getChartQueryAndParamsFromStepPair(req.app.locals._tableName,
req.query.steparray.split(",").map(Number),
isRange
);
} else if (
req.query &&
req.query.steppair &&
req.query.steppair.length > 1
) {
obj = this.mysql.getChartQueryAndParamsFromStepPair(
req.app.locals._tableName,
req.query._fields,
(req.query.steppair.split(',')).map(Number),
false)
req.query.steppair.split(",").map(Number),
false
);
} else {
query = 'select min(??) as min,max(??) as max,stddev(??) as stddev,avg(??) as avg from ??';
query =
"select min(??) as min,max(??) as max,stddev(??) as stddev,avg(??) as avg from ??";
params = [];
params.push(req.query._fields);
@ -466,66 +487,70 @@ class xctrl {
let _this = this;
let results = await
_this.mysql.exec(query, params);
let results = await _this.mysql.exec(query, params);
//console.log(results, results['max'], req.params);
obj = _this.mysql.getChartQueryAndParamsFromMinMaxStddev(req.app.locals._tableName,
obj = _this.mysql.getChartQueryAndParamsFromMinMaxStddev(
req.app.locals._tableName,
req.query._fields,
results[0]['min'],
results[0]['max'],
results[0]['stddev'],
results[0]["min"],
results[0]["max"],
results[0]["stddev"],
isRange
)
);
}
this.mysql.getWhereClause(req.query._where, req.app.locals._tableName, obj, ' where ')
this.mysql.getWhereClause(
req.query._where,
req.app.locals._tableName,
obj,
" where "
);
let results = await
this.mysql.exec(obj.query, obj.params);
let results = await this.mysql.exec(obj.query, obj.params);
res.status(200).json(results);
} else {
res.status(400).json({message: 'Missing _fields in query params eg: /api/tableName/chart?_fields=numericColumn1'});
res
.status(400)
.json({
message:
"Missing _fields in query params eg: /api/tableName/chart?_fields=numericColumn1"
});
}
}
async autoChart(req, res) {
let query = "describe ??";
let params = [req.app.locals._tableName];
let obj = {};
let results = [];
let query = 'describe ??'
let params = [req.app.locals._tableName]
let obj = {}
let results = []
let isRange = false
let isRange = false;
if (req.query.range) {
isRange = true
isRange = true;
}
let describeResults = await this.mysql.exec(query, params)
let describeResults = await this.mysql.exec(query, params);
//console.log(describeResults);
for (var i = 0; i < describeResults.length; ++i) {
//console.log('is this numeric column', describeResults[i]['Type']);
if (describeResults[i]['Key'] !== 'PRI' && this.mysql.isTypeOfColumnNumber(describeResults[i]['Type'])) {
query = 'select min(??) as min,max(??) as max,stddev(??) as stddev,avg(??) as avg from ??';
if (
describeResults[i]["Key"] !== "PRI" &&
this.mysql.isTypeOfColumnNumber(describeResults[i]["Type"])
) {
query =
"select min(??) as min,max(??) as max,stddev(??) as stddev,avg(??) as avg from ??";
params = [];
params.push(describeResults[i]['Field']);
params.push(describeResults[i]['Field']);
params.push(describeResults[i]['Field']);
params.push(describeResults[i]['Field']);
params.push(describeResults[i]["Field"]);
params.push(describeResults[i]["Field"]);
params.push(describeResults[i]["Field"]);
params.push(describeResults[i]["Field"]);
params.push(req.app.locals._tableName);
let _this = this;
@ -534,32 +559,30 @@ class xctrl {
//console.log(minMaxResults, minMaxResults['max'], req.params);
query = ''
params = []
query = "";
params = [];
obj = _this.mysql.getChartQueryAndParamsFromMinMaxStddev(req.app.locals._tableName,
describeResults[i]['Field'],
minMaxResults[0]['min'],
minMaxResults[0]['max'],
minMaxResults[0]['stddev'],
obj = _this.mysql.getChartQueryAndParamsFromMinMaxStddev(
req.app.locals._tableName,
describeResults[i]["Field"],
minMaxResults[0]["min"],
minMaxResults[0]["max"],
minMaxResults[0]["stddev"],
isRange
)
);
let r = await this.mysql.exec(obj.query, obj.params);
let resultObj = {}
resultObj['column'] = describeResults[i]['Field']
resultObj['chart'] = r
let resultObj = {};
resultObj["column"] = describeResults[i]["Field"];
resultObj["chart"] = r;
results.push(resultObj);
}
}
res.status(200).json(results);
}
}
//expose class

1087
lib/xsql.js

File diff suppressed because it is too large Load Diff

460
package-lock.json generated

@ -176,6 +176,12 @@
"typedarray": "^0.0.6"
}
},
"contains-path": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz",
"integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=",
"dev": true
},
"content-disposition": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz",
@ -231,6 +237,15 @@
"ms": "2.0.0"
}
},
"define-properties": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
"integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
"dev": true,
"requires": {
"object-keys": "^1.0.12"
}
},
"delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
@ -285,6 +300,16 @@
"integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==",
"dev": true
},
"doctrine": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz",
"integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=",
"dev": true,
"requires": {
"esutils": "^2.0.2",
"isarray": "^1.0.0"
}
},
"ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
@ -295,6 +320,40 @@
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.1.tgz",
"integrity": "sha1-eePVhlU0aQn+bw9Fpd5oEDspTSA="
},
"error-ex": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
"integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
"dev": true,
"requires": {
"is-arrayish": "^0.2.1"
}
},
"es-abstract": {
"version": "1.13.0",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz",
"integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==",
"dev": true,
"requires": {
"es-to-primitive": "^1.2.0",
"function-bind": "^1.1.1",
"has": "^1.0.3",
"is-callable": "^1.1.4",
"is-regex": "^1.0.4",
"object-keys": "^1.0.12"
}
},
"es-to-primitive": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz",
"integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==",
"dev": true,
"requires": {
"is-callable": "^1.1.4",
"is-date-object": "^1.0.1",
"is-symbol": "^1.0.2"
}
},
"escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
@ -306,6 +365,85 @@
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
"dev": true
},
"eslint-config-airbnb-base": {
"version": "13.1.0",
"resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-13.1.0.tgz",
"integrity": "sha512-XWwQtf3U3zIoKO1BbHh6aUhJZQweOwSt4c2JrPDg9FP3Ltv3+YfEv7jIDB8275tVnO/qOHbfuYg3kzw6Je7uWw==",
"dev": true,
"requires": {
"eslint-restricted-globals": "^0.1.1",
"object.assign": "^4.1.0",
"object.entries": "^1.0.4"
}
},
"eslint-config-prettier": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-4.1.0.tgz",
"integrity": "sha512-zILwX9/Ocz4SV2vX7ox85AsrAgXV3f2o2gpIicdMIOra48WYqgUnWNH/cR/iHtmD2Vb3dLSC3LiEJnS05Gkw7w==",
"dev": true,
"requires": {
"get-stdin": "^6.0.0"
}
},
"eslint-import-resolver-node": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.2.tgz",
"integrity": "sha512-sfmTqJfPSizWu4aymbPr4Iidp5yKm8yDkHp+Ir3YiTHiiDfxh69mOUsmiqW6RZ9zRXFaF64GtYmN7e+8GHBv6Q==",
"dev": true,
"requires": {
"debug": "^2.6.9",
"resolve": "^1.5.0"
}
},
"eslint-module-utils": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.3.0.tgz",
"integrity": "sha512-lmDJgeOOjk8hObTysjqH7wyMi+nsHwwvfBykwfhjR1LNdd7C2uFJBvx4OpWYpXOw4df1yE1cDEVd1yLHitk34w==",
"dev": true,
"requires": {
"debug": "^2.6.8",
"pkg-dir": "^2.0.0"
}
},
"eslint-plugin-import": {
"version": "2.16.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.16.0.tgz",
"integrity": "sha512-z6oqWlf1x5GkHIFgrSvtmudnqM6Q60KM4KvpWi5ubonMjycLjndvd5+8VAZIsTlHC03djdgJuyKG6XO577px6A==",
"dev": true,
"requires": {
"contains-path": "^0.1.0",
"debug": "^2.6.9",
"doctrine": "1.5.0",
"eslint-import-resolver-node": "^0.3.2",
"eslint-module-utils": "^2.3.0",
"has": "^1.0.3",
"lodash": "^4.17.11",
"minimatch": "^3.0.4",
"read-pkg-up": "^2.0.0",
"resolve": "^1.9.0"
}
},
"eslint-plugin-prettier": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.0.1.tgz",
"integrity": "sha512-/PMttrarPAY78PLvV3xfWibMOdMDl57hmlQ2XqFeA37wd+CJ7WSxV7txqjVPHi/AAFKd2lX0ZqfsOc/i5yFCSQ==",
"dev": true,
"requires": {
"prettier-linter-helpers": "^1.0.0"
}
},
"eslint-restricted-globals": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/eslint-restricted-globals/-/eslint-restricted-globals-0.1.1.tgz",
"integrity": "sha1-NfDVy8ZMLj7WLpO0saevBbp+1Nc=",
"dev": true
},
"esutils": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz",
"integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=",
"dev": true
},
"etag": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
@ -354,6 +492,12 @@
"integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=",
"dev": true
},
"fast-diff": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz",
"integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==",
"dev": true
},
"finalhandler": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.0.tgz",
@ -368,6 +512,15 @@
"unpipe": "~1.0.0"
}
},
"find-up": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
"integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=",
"dev": true,
"requires": {
"locate-path": "^2.0.0"
}
},
"form-data": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.1.tgz",
@ -401,6 +554,18 @@
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
"dev": true
},
"function-bind": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
"dev": true
},
"get-stdin": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz",
"integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==",
"dev": true
},
"glob": {
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
@ -415,24 +580,51 @@
"path-is-absolute": "^1.0.0"
}
},
"graceful-fs": {
"version": "4.1.15",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz",
"integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==",
"dev": true
},
"growl": {
"version": "1.10.5",
"resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz",
"integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==",
"dev": true
},
"has": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
"dev": true,
"requires": {
"function-bind": "^1.1.1"
}
},
"has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
"dev": true
},
"has-symbols": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz",
"integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=",
"dev": true
},
"he": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz",
"integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=",
"dev": true
},
"hosted-git-info": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz",
"integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==",
"dev": true
},
"http-errors": {
"version": "1.6.2",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz",
@ -476,11 +668,75 @@
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.5.2.tgz",
"integrity": "sha1-1LUFvemUaYfM8PxY2QEP+WB+P6A="
},
"is-arrayish": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
"integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
"dev": true
},
"is-callable": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz",
"integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==",
"dev": true
},
"is-date-object": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz",
"integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=",
"dev": true
},
"is-regex": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz",
"integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=",
"dev": true,
"requires": {
"has": "^1.0.1"
}
},
"is-symbol": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz",
"integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==",
"dev": true,
"requires": {
"has-symbols": "^1.0.0"
}
},
"isarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
},
"load-json-file": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz",
"integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=",
"dev": true,
"requires": {
"graceful-fs": "^4.1.2",
"parse-json": "^2.2.0",
"pify": "^2.0.0",
"strip-bom": "^3.0.0"
}
},
"locate-path": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz",
"integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=",
"dev": true,
"requires": {
"p-locate": "^2.0.0",
"path-exists": "^3.0.0"
}
},
"lodash": {
"version": "4.17.11",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
"integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==",
"dev": true
},
"log": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/log/-/log-1.4.0.tgz",
@ -631,11 +887,53 @@
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz",
"integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk="
},
"normalize-package-data": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
"integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
"dev": true,
"requires": {
"hosted-git-info": "^2.1.4",
"resolve": "^1.10.0",
"semver": "2 || 3 || 4 || 5",
"validate-npm-package-license": "^3.0.1"
}
},
"object-assign": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz",
"integrity": "sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I="
},
"object-keys": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.0.tgz",
"integrity": "sha512-6OO5X1+2tYkNyNEx6TsCxEqFfRWaqx6EtMiSbGrw8Ob8v9Ne+Hl8rBAgLBZn5wjEz3s/s6U1WXFUFOcxxAwUpg==",
"dev": true
},
"object.assign": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz",
"integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==",
"dev": true,
"requires": {
"define-properties": "^1.1.2",
"function-bind": "^1.1.1",
"has-symbols": "^1.0.0",
"object-keys": "^1.0.11"
}
},
"object.entries": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.0.tgz",
"integrity": "sha512-l+H6EQ8qzGRxbkHOd5I/aHRhHDKoQXQ8g0BYt4uSweQU1/J6dZUOyWh9a2Vky35YCKjzmgxOzta2hH6kf9HuXA==",
"dev": true,
"requires": {
"define-properties": "^1.1.3",
"es-abstract": "^1.12.0",
"function-bind": "^1.1.1",
"has": "^1.0.3"
}
},
"on-finished": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
@ -658,22 +956,100 @@
"wrappy": "1"
}
},
"p-limit": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz",
"integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==",
"dev": true,
"requires": {
"p-try": "^1.0.0"
}
},
"p-locate": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz",
"integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=",
"dev": true,
"requires": {
"p-limit": "^1.1.0"
}
},
"p-try": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz",
"integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=",
"dev": true
},
"parse-json": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
"integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=",
"dev": true,
"requires": {
"error-ex": "^1.2.0"
}
},
"parseurl": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz",
"integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M="
},
"path-exists": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
"integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
"dev": true
},
"path-is-absolute": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
"dev": true
},
"path-parse": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
"integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
"dev": true
},
"path-to-regexp": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
"integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
},
"path-type": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz",
"integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=",
"dev": true,
"requires": {
"pify": "^2.0.0"
}
},
"pify": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
"integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
"dev": true
},
"pkg-dir": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz",
"integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=",
"dev": true,
"requires": {
"find-up": "^2.1.0"
}
},
"prettier-linter-helpers": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz",
"integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==",
"dev": true,
"requires": {
"fast-diff": "^1.1.2"
}
},
"process-nextick-args": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz",
@ -709,6 +1085,27 @@
"unpipe": "1.0.0"
}
},
"read-pkg": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz",
"integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=",
"dev": true,
"requires": {
"load-json-file": "^2.0.0",
"normalize-package-data": "^2.3.2",
"path-type": "^2.0.0"
}
},
"read-pkg-up": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz",
"integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=",
"dev": true,
"requires": {
"find-up": "^2.0.0",
"read-pkg": "^2.0.0"
}
},
"readable-stream": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz",
@ -723,11 +1120,26 @@
"util-deprecate": "~1.0.1"
}
},
"resolve": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.0.tgz",
"integrity": "sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==",
"dev": true,
"requires": {
"path-parse": "^1.0.6"
}
},
"safe-buffer": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
"integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg=="
},
"semver": {
"version": "5.7.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz",
"integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==",
"dev": true
},
"send": {
"version": "0.16.1",
"resolved": "https://registry.npmjs.org/send/-/send-0.16.1.tgz",
@ -827,6 +1239,38 @@
"nan": ">=2.5.1"
}
},
"spdx-correct": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz",
"integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==",
"dev": true,
"requires": {
"spdx-expression-parse": "^3.0.0",
"spdx-license-ids": "^3.0.0"
}
},
"spdx-exceptions": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz",
"integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==",
"dev": true
},
"spdx-expression-parse": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz",
"integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==",
"dev": true,
"requires": {
"spdx-exceptions": "^2.1.0",
"spdx-license-ids": "^3.0.0"
}
},
"spdx-license-ids": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.3.tgz",
"integrity": "sha512-uBIcIl3Ih6Phe3XHK1NqboJLdGfwr1UN3k6wSD1dZpmPsIkb8AGNbZYJ1fOBk834+Gxy8rpfDxrS6XLEMZMY2g==",
"dev": true
},
"sqlstring": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.0.tgz",
@ -850,6 +1294,12 @@
"safe-buffer": "~5.1.0"
}
},
"strip-bom": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
"integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=",
"dev": true
},
"superagent": {
"version": "3.6.3",
"resolved": "https://registry.npmjs.org/superagent/-/superagent-3.6.3.tgz",
@ -942,6 +1392,16 @@
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
"integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
},
"validate-npm-package-license": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
"integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
"dev": true,
"requires": {
"spdx-correct": "^3.0.0",
"spdx-expression-parse": "^3.0.0"
}
},
"vary": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",

4
package.json

@ -36,6 +36,10 @@
"mysql": "^2.15.0"
},
"devDependencies": {
"eslint-config-airbnb-base": "^13.1.0",
"eslint-config-prettier": "^4.1.0",
"eslint-plugin-import": "^2.16.0",
"eslint-plugin-prettier": "^3.0.1",
"mocha": "^5.2.0",
"should": "^13.1.2",
"sleep": "^5.1.1",

1773
tests/tests.js

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save