diff --git a/README.md b/README.md index caceb157e7..b1699dfc2c 100644 --- a/README.md +++ b/README.md @@ -79,8 +79,9 @@ Root URL (localhost:3000/) returns all REST API urls for each table in schema. * POST      /api/tableName * GET       /api/tableName/:id * PUT       /api/tableName/:id +* GET       /api/tableName/findOne * GET       /api/tableName/count -* GET       /api/tableName/exists +* GET       /api/tableName/:id/exists * GET       /api/tableName/groupby * GET       /api/tableName/aggregate * GET       /api/parentTable/:id/childTable @@ -208,6 +209,26 @@ eg: filter of rows using _where is available for relational route URLs too. /api/offices/1/employees?_where=(jobTitle,eq,Sales%20Rep) ``` +## FindOne +``` +/api/tableName/findOne?_where=(id,eq,1) +``` +Works similar to list but only returns top/one result. Used in conjunction with _where + +## Count +``` +/api/tableName/count +``` + +Returns number of rows in table + +## Exists +``` +/api/tableName/1/exists +``` + +Returns true or false depending on whether record exists + ## Group By, Having (as query params) diff --git a/index.js b/index.js index f0453bf898..0b27c862f4 100644 --- a/index.js +++ b/index.js @@ -31,7 +31,9 @@ let mysqlPool = mysql.createPool(sqlConfig); /**************** START : setup Xapi ****************/ console.log(''); -console.log(' REST APIs at the speed of thought.. '); +console.log(''); +console.log(''); +console.log(' Generating REST APIs at the speed of your thought.. '); console.log(''); let t = process.hrtime(); @@ -43,7 +45,7 @@ moreApis.init((err, results) => { var t1 = process.hrtime(t); var t2 = t1[0]+t1[1]/1000000000 - console.log(' '); + console.log(" Xmysql took : %d seconds",dataHelp.round(t2,1)); console.log(' '); console.log(' - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - '); diff --git a/lib/util/cmd.helper.js b/lib/util/cmd.helper.js index 9dcd379c34..1a4da9c847 100644 --- a/lib/util/cmd.helper.js +++ b/lib/util/cmd.helper.js @@ -11,7 +11,7 @@ program.on('--help', () => { }) program - .version('0.1.9') + .version('0.2.0') .option('-h, --host ', 'hostname') .option('-d, --database ', 'database schema name') .option('-u, --user ', 'username of database / root by default') diff --git a/lib/xapi.js b/lib/xapi.js index 6cb3912828..ab23b8a385 100644 --- a/lib/xapi.js +++ b/lib/xapi.js @@ -135,6 +135,11 @@ class Xapi { .get(this.asyncMiddleware(this.list.bind(this))); break; + case 'findOne': + this.app.route(routes[i]['routeUrl']) + .get(this.asyncMiddleware(this.findOne.bind(this))); + break; + case 'create': this.app.route(routes[i]['routeUrl']) .post(this.asyncMiddleware(this.create.bind(this))); @@ -211,12 +216,11 @@ class Xapi { console.log(' - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - '); console.log(' '); - console.log(' Database : %s',this.config.database); - console.log(' Number of resources : %s',stat.tables); + console.log(' Database : %s', this.config.database); + console.log(' Number of resources : %s', stat.tables); console.log(' '); - console.log(' REST APIs Generated : %s'.green.bold,stat.apis ); + console.log(' REST APIs Generated : %s'.green.bold, stat.apis); console.log(' '); - return stat } @@ -233,7 +237,16 @@ class Xapi { } - prepareListQuery(req, res, queryParamsObj, nested = false) { + /** + * + * @param req + * @param res + * @param queryParamsObj : {query, params} + * @param listType : 0:list, 1:nested, 2:findOne + * + * Updates query, params for query of type listType + */ + prepareListQuery(req, res, queryParamsObj, listType = 0) { queryParamsObj.query = 'select '; queryParamsObj.params = []; @@ -248,7 +261,7 @@ class Xapi { /**************** add tableName ****************/ queryParamsObj.query += ' from ?? '; - if (nested) { + if (listType === 1) { req.app.locals._tableName = req.app.locals._childTable; @@ -289,8 +302,12 @@ class Xapi { this.mysql.getOrderByClause(req.query, req.app.locals._tableName, queryParamsObj); /**************** add limit clause ****************/ - queryParamsObj.query += ' limit ?,? ' - queryParamsObj.params = queryParamsObj.params.concat(this.mysql.getLimitClause(req.query)); + if (listType === 2) { + queryParamsObj.query += ' limit 1 ' + } else { + queryParamsObj.query += ' limit ?,? ' + queryParamsObj.params = queryParamsObj.params.concat(this.mysql.getLimitClause(req.query)); + } //console.log(queryParamsObj.query, queryParamsObj.params); @@ -302,7 +319,7 @@ class Xapi { queryParamsObj.query = '' queryParamsObj.params = [] - this.prepareListQuery(req, res, queryParamsObj, false); + this.prepareListQuery(req, res, queryParamsObj, 0); let results = await this.mysql.exec(queryParamsObj.query, queryParamsObj.params); res.status(200).json(results); @@ -315,7 +332,20 @@ class Xapi { queryParamsObj.query = ''; queryParamsObj.params = []; - this.prepareListQuery(req, res, queryParamsObj, true) + this.prepareListQuery(req, res, queryParamsObj, 1) + + 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 = [] + + this.prepareListQuery(req, res, queryParamsObj, 2); let results = await this.mysql.exec(queryParamsObj.query, queryParamsObj.params); res.status(200).json(results); @@ -488,13 +518,13 @@ class Xapi { 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 '; 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 = {} diff --git a/lib/xsql.js b/lib/xsql.js index 9e64dd65d3..249c9e21b2 100644 --- a/lib/xsql.js +++ b/lib/xsql.js @@ -457,6 +457,7 @@ class Xsql { routes.push(this.prepareRoute(internal, 'get', apiPrefix, tableName + '/count', 'count')) routes.push(this.prepareRoute(internal, 'get', apiPrefix, tableName + '/groupby', 'groupby')) 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, 'post', apiPrefix, tableName, 'create')) routes.push(this.prepareRoute(internal, 'get', apiPrefix, tableName, 'list')) routes.push(this.prepareRoute(internal, 'get', apiPrefix, tableName + '/:id', 'read')) diff --git a/package.json b/package.json index 42353e68b0..d29eb8155a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "xmysql", - "version": "0.1.9", + "version": "0.2.0", "description": "One command to generate REST APIs for any MySql database", "main": "index.js", "scripts": { diff --git a/tests/tests.js b/tests/tests.js index 5a79baf659..da76b12f7f 100644 --- a/tests/tests.js +++ b/tests/tests.js @@ -332,6 +332,30 @@ describe('xmysql : tests', function () { }); + + + it('GET /api/customers/findOne?_where=(customerNumber,eq,119) should PASS', function (done) { + + //http get an url + agent.get('/api/customers/findOne?_where=(customerNumber,eq,119)') // api url + .expect(200) // 2xx for success and 4xx for failure + .end(function (err, res) { + // Handle /api/offices/1/employees error + if (err) { + return done(err); + } + + // tate is an invalid column but still it should query right number of columns + res.body.length.should.be.equal(1) + res.body[0]['customerNumber'].should.be.equal(119) + + return done(); + + }); + + }); + + it('GET /api/offices?_fields=-territory,-addressLine2,-state,-tate should PASS', function (done) { //http get an url @@ -1299,6 +1323,7 @@ describe('xmysql : tests', function () { + // // it('GET http://localhost:3000/api/customers/groupby?_fields=city,country&_having=(customerNumber,lt,110) should PASS', function (done) { //