Browse Source

Feature : API Autochart 🔥 🔥

v npm 0.3.4
pull/13/head
oof1lab 7 years ago
parent
commit
73090ab658
  1. 46
      README.md
  2. 2
      lib/util/cmd.helper.js
  3. 69
      lib/xapi.js
  4. 41
      lib/xsql.js
  5. 2
      package.json
  6. 21
      tests/tests.js

46
README.md

@ -74,6 +74,7 @@ Powered by popular node packages : ([express](https://github.com/expressjs/expre
* Group By, Having (as a separate API) :fire::fire:
* Multiple group by in one API :fire::fire::fire::fire:
* Chart API for numeric column :fire::fire::fire::fire::fire::fire:
* Auto Chart API - (Must see : a gift to lazy while prototyping) :fire::fire::fire::fire::fire::fire:
* Supports views
* Prototyping (features available when using local MySql server only)
* Run dynamic queries :fire::fire::fire:
@ -116,6 +117,7 @@ if you haven't on your system.
| GET :fire:| [/api/tableName/groupby](#group-by-having-as-api) | Group by results of column(s) |
| GET :fire:| [/api/tableName/ugroupby](#union-of-multiple-group-by-statements) | Multiple group by results using one call |
| GET :fire:| [/api/tableName/chart](#chart) | Numeric column distribution based on (min,max,step) or(step array) or (automagic)|
| GET :fire:| [/api/tableName/autochart](#autochart) | Same as Chart but identifies which are numeric column automatically - gift for lazy while prototyping|
| GET :fire:| [/dynamic](#run-dynamic-queries) | execute dynamic mysql statements with params |
| GET :fire:| [/upload](#upload-single-file) | upload single file |
| GET :fire:| [/uploads](#upload-multiple-files) | upload multiple files |
@ -637,6 +639,50 @@ Response
Please Note:
_fields in Chart API can only take numeric column as its argument.
## Autochart
Identifies numeric columns in a table which are not any sort of key and applies chart API as before -
feels like magic when there are multiple numeric columns in table while hacking/prototyping and you invoke this API.
```
http://localhost:3000/api/payments/autochart
[
{
"column": "amount",
"chart": [
{
"amount": "-9860 to 11100",
"_count": 45
},
{
"amount": "11101 to 32060",
"_count": 91
},
{
"amount": "32061 to 53020",
"_count": 109
},
{
"amount": "53021 to 73980",
"_count": 16
},
{
"amount": "73981 to 94940",
"_count": 7
},
{
"amount": "94941 to 115900",
"_count": 3
},
{
"amount": "115901 to 130650",
"_count": 2
}
]
}
]
```
## Run dynamic queries
[:arrow_heading_up:](#api-overview)

2
lib/util/cmd.helper.js

@ -11,7 +11,7 @@ program.on('--help', () => {
})
program
.version('0.3.3')
.version('0.3.4')
.option('-h, --host <n>', 'hostname / localhost by default')
.option('-u, --user <n>', 'username of database / root by default')
.option('-p, --password <n>', 'password of database / empty by default')

69
lib/xapi.js

@ -219,6 +219,11 @@ class Xapi {
.get(this.asyncMiddleware(this.chart.bind(this)));
break;
case 'autoChart':
this.app.route(routes[i]['routeUrl'])
.get(this.asyncMiddleware(this.autoChart.bind(this)));
break;
case 'aggregate':
this.app.route(routes[i]['routeUrl'])
.get(this.asyncMiddleware(this.aggregate.bind(this)));
@ -866,6 +871,70 @@ class Xapi {
}
async autoChart(req, res) {
let query = 'describe ??'
let params = [req.app.locals._tableName]
let obj = {}
let results = []
let isRange = false
if (req.query.range) {
isRange = true
}
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 ??';
params = [];
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;
let minMaxResults = await _this.mysql.exec(query, params);
//console.log(minMaxResults, minMaxResults['max'], req.params);
query = ''
params = []
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
results.push(resultObj);
}
}
res.status(200).json(results);
}
/**************** START : files related ****************/
downloadFile(req, res) {
let file = path.join(process.cwd(), req.query.name);

41
lib/xsql.js

@ -154,6 +154,34 @@ class Xsql {
}
typeOfColumn(Type) {
//TODO: Im sure there are more types to handle here
const strTypes = ['varchar', 'text', 'char', 'tinytext', 'mediumtext', 'longtext', 'blob', 'mediumblob', 'longblob'];
const intTypes = ['int', 'long', 'smallint', 'mediumint', 'bigint', 'tinyint'];
const flatTypes = ['float', 'double', 'decimal'];
const dateTypes = ['date', 'datetime', 'timestamp', 'time', 'year'];
if (getType(Type, strTypes)) {
return "string"
} else if (getType(Type, intTypes)) {
return "int"
} else if (getType(Type, flatTypes)) {
return "float"
} else if (getType(Type, dateTypes)) {
return "date"
} else {
return "unknown"
}
}
isTypeOfColumnNumber(Type) {
//console.log(Type, this.typeOfColumn(Type));
return ('int' === this.typeOfColumn(Type) || 'float' === this.typeOfColumn(Type))
}
getLimitClause(reqParams) {
//defaults
@ -532,6 +560,7 @@ class Xsql {
routes.push(this.prepareRoute(internal, 'get', apiPrefix, tableName + '/chart', 'chart'))
routes.push(this.prepareRoute(internal, 'get', apiPrefix, tableName + '/aggregate', 'aggregate'))
routes.push(this.prepareRoute(internal, 'get', apiPrefix, tableName + '/findOne', 'findOne'))
routes.push(this.prepareRoute(internal, 'get', apiPrefix, tableName + '/autoChart', 'autoChart'))
if (!isView) {
routes.push(this.prepareRoute(internal, 'post', apiPrefix, tableName, 'create'))
@ -712,3 +741,15 @@ function getColumnType(column) {
}
function getType(colType, typesArr) {
for (let i = 0; i < typesArr.length; ++i) {
// if (typesArr[i].indexOf(colType) !== -1) {
// return 1;
// }
if (colType.indexOf(typesArr[i]) !== -1) {
return 1;
}
}
return 0;
}

2
package.json

@ -1,6 +1,6 @@
{
"name": "xmysql",
"version": "0.3.3",
"version": "0.3.4",
"description": "One command to generate REST APIs for any MySql database",
"main": "index.js",
"scripts": {

21
tests/tests.js

@ -858,7 +858,6 @@ describe('xmysql : tests', function () {
res.body.length.should.be.equals(1)
return done();
});
@ -879,7 +878,6 @@ describe('xmysql : tests', function () {
res.body.length.should.be.equals(4)
return done();
});
@ -1223,6 +1221,25 @@ describe('xmysql : tests', function () {
});
})
it('GET /api/payments/autochart should PASS', function (done) {
//post to an url with data
agent.get('/api/payments/autochart') //enter url
.expect(200)//200 for success 4xx for failure
.end(function (err, res) {
// Handle /api/v error
if (err) {
return done(err);
}
res.body[0]['chart'].length.should.be.equals(7)
res.body[0]['chart'][0]['_count'].should.be.equals(45)
res.body[0]['chart'][6]['_count'].should.be.equals(2)
return done();
});
})
it('GET /api/payments?_where=(amount,bw,1000,5000) should PASS', function (done) {

Loading…
Cancel
Save