From d048221a890e9c886995a44c0bbafbc4e143c783 Mon Sep 17 00:00:00 2001 From: oof1lab Date: Fri, 17 Nov 2017 20:07:00 +0530 Subject: [PATCH] Feature : xjoin API :fire: :fire: npm v0.4.0 --- README.md | 71 ++++++++++- lib/util/cmd.helper.js | 2 +- lib/util/whereClause.helper.js | 179 +++++++++++++------------- lib/xapi.js | 227 ++++++++++++++++++++++++++++++--- lib/xsql.js | 49 ++++++- package.json | 2 +- tests/tests.js | 103 ++++++++++++++- 7 files changed, 519 insertions(+), 114 deletions(-) diff --git a/README.md b/README.md index 98132c4ba9..2de2bf2a2f 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,8 @@ 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: +* Auto Chart API - (a gift for lazy while prototyping) :fire::fire::fire::fire::fire::fire: +* #### [XJOIN - (Supports any number of JOINS)](#xjoin) :fire::fire::fire::fire::fire::fire::fire::fire::fire::fire::fire::fire: * Supports views * Prototyping (features available when using local MySql server only) * Run dynamic queries :fire::fire::fire: @@ -118,6 +119,7 @@ if you haven't on your system. | 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:| [/api/xjoin](#xjoin) | handles join | | 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 | @@ -684,6 +686,71 @@ http://localhost:3000/api/payments/autochart ] ``` +## XJOIN + +### Xjoin Syntax: + +``` +_join : List of tableNames alternated by type of join to be made (_j, _ij,_ lj, _rj, _fj) +alias.tableName : TableName as alias +_j : Join [ _j => join, _ij => ij, _lj => left join , _rj => right join , _fj => full join) +_onNumber : Number 'n' indicates condition to be applied for 'n'th join between (n-1) and 'n'th table in list +``` + +#### Simple example of two table join: + +Sql join query: + +```sql + +SELECT * +FROM productlines as pl + JOIN products as pr + ON pl.productline = pr.productline + +``` + +Equivalent xjoin query API: +``` +/api/xjoin?_join=pl.productlines,j,pr.products&_on1=(pl.productline,eq,pr.productline) +``` + +#### Multiple tables join + +Sql join query: +```sql +SELECT * +FROM productlines as pl + JOIN products as pr + ON pl.productline = pr.productline + JOIN orderdetails as ord + ON pr.productcode = ord.productcode +``` + +Equivalent xjoin query API: + +``` +/api/xjoin?_join=pl.productlines,j,pr.products,j,ord.orderDetails&_on1=(pl.productline,eq,pr.productline)&_on2=(pr.productcode,eq,ord.productcode) + +``` + +**Explanation:** +> pl.productlines => productlines as pl + +> _j => join + +> pr.products => products as pl + +> _on1 => join condition between productlines and products => (pl.productline,eq,pr.productline) + +> _on2 => join condition between products and orderdetails => (pr.productcode,eq,ord.productcode) + +Example to use : _fields, _where, _p, _size in query params + +``` +/api/xjoin?_join=pl.productlines,_j,pr.products&_on1=(pl.productline,eq,pr.productline)&_fields=pl.productline,pr.productName&_size=2&_where=(productName,like,1972~) +``` + ## Run dynamic queries [:arrow_heading_up:](#api-overview) @@ -758,7 +825,7 @@ http://localhost:3000/download?name=fileName ## When to use ? [:arrow_heading_up:](#api-overview) -* You need just REST APIs without much hassle for (ANY) MySql database. +* You need just REST APIs for (ANY) MySql database at blink of an eye (literally). * You are learning new frontend frameworks and need REST APIs for your MySql database. * You are working on a demo, hacks etc diff --git a/lib/util/cmd.helper.js b/lib/util/cmd.helper.js index 2bb80aece5..763439645b 100644 --- a/lib/util/cmd.helper.js +++ b/lib/util/cmd.helper.js @@ -11,7 +11,7 @@ program.on('--help', () => { }) program - .version('0.3.4') + .version('0.4.0') .option('-h, --host ', 'hostname / localhost by default') .option('-u, --user ', 'username of database / root by default') .option('-p, --password ', 'password of database / empty by default') diff --git a/lib/util/whereClause.helper.js b/lib/util/whereClause.helper.js index 7893a775a2..292dc544fe 100644 --- a/lib/util/whereClause.helper.js +++ b/lib/util/whereClause.helper.js @@ -78,7 +78,7 @@ function prepareBetweenValue(value) { } -function replaceWhereParamsToQMarks(openParentheses, str, comparisonOperator) { +function replaceWhereParamsToQMarks(openParentheses, str, comparisonOperator, condType) { let converted = '' @@ -93,8 +93,11 @@ function replaceWhereParamsToQMarks(openParentheses, str, comparisonOperator) { } } else { - if (comparisonOperator !== ' in ' && comparisonOperator !== ' between ') + 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] === ')') { @@ -105,6 +108,7 @@ function replaceWhereParamsToQMarks(openParentheses, str, comparisonOperator) { } } + return converted; } @@ -215,116 +219,113 @@ exports.getConditionClause = function (whereInQueryParams, condType = 'where') { if (numOfConditions && logicalOperatorsInClause && numOfConditions.length !== logicalOperatorsInClause.length + 1) { console.log('conditions and logical operators mismatch', numOfConditions.length, logicalOperatorsInClause.length); - return - } - - // 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 = '' + } else { + //console.log('numOfConditions',numOfConditions,whereInQueryParams); + //console.log('logicalOperatorsInClause',logicalOperatorsInClause); - // split on commas - var arr = numOfConditions[i].split(','); + for (var i = 0; i < numOfConditions.length; ++i) { - // consider first two splits only - var result = arr.splice(0, 2); + let variable = '' + let comparisonOperator = '' + let logicalOperator = '' + let variableValue = '' + let temp = '' - // join to make only 3 array elements - result.push(arr.join(',')); + // split on commas + var arr = numOfConditions[i].split(','); - // variable, operator, variablevalue - if (result.length !== 3) { - grammarErr = 1; - break; - } + // consider first two splits only + var result = arr.splice(0, 2); - /**************** START : variable ****************/ - //console.log(result); - variable = result[0].match(/\(+(.*)/); + // join to make only 3 array elements + result.push(arr.join(',')); - // console.log('variable',variable); - - if (!variable || variable.length !== 2) { - grammarErr = 1; - break; - } + // variable, operator, variablevalue + if (result.length !== 3) { + grammarErr = 1; + break; + } - // get the variableName and push - whereParams.push(variable[1]) + /**************** START : variable ****************/ + //console.log('variable, operator, variablevalue',result); + variable = result[0].match(/\(+(.*)/); - // then replace the variable name with ?? - temp = replaceWhereParamsToQMarks(true, result[0]) - if (!temp) { - grammarErr = 1; - break; - } - whereQuery += temp + //console.log('variable',variable); - /**************** END : variable ****************/ + if (!variable || variable.length !== 2) { + grammarErr = 1; + break; + } + // get the variableName and push + whereParams.push(variable[1]) - /**************** START : operator and value ****************/ - comparisonOperator = getComparisonOperator(result[1]) - if (!comparisonOperator) { - grammarErr = 1; - break; - } - whereQuery += comparisonOperator + // then replace the variable name with ?? + temp = replaceWhereParamsToQMarks(true, result[0], ' ignore ', condType) + if (!temp) { + grammarErr = 1; + break; + } + whereQuery += temp - // get the variableValue and push to params - variableValue = result[2].match(/(.*?)\)/) - if (!variableValue || variableValue.length !== 2) { - grammarErr = 1; - break; - } + /**************** END : variable ****************/ - 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]) - } - // then replace the variableValue with ? - temp = replaceWhereParamsToQMarks(false, result[2], comparisonOperator) - if (!temp) { - grammarErr = 1; - break; - } - whereQuery += temp + /**************** START : operator and value ****************/ + comparisonOperator = getComparisonOperator(result[1]) + if (!comparisonOperator) { + grammarErr = 1; + break; + } + whereQuery += comparisonOperator + // get the variableValue and push to params + variableValue = result[2].match(/(.*?)\)/) + if (!variableValue || variableValue.length !== 2) { + grammarErr = 1; + break; + } - // only - if (numOfConditions.length !== -1 && i !== numOfConditions.length - 1) { - //console.log('finding logical operator for',logicalOperatorsInClause[i]); - logicalOperator = getLogicalOperator(logicalOperatorsInClause[i]) + 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]) + } - if (!logicalOperator) { + // then replace the variableValue with ? + temp = replaceWhereParamsToQMarks(false, result[2], comparisonOperator, condType) + if (!temp) { grammarErr = 1; break; } + whereQuery += temp - whereQuery += getLogicalOperator(logicalOperatorsInClause[i]) - } - /**************** END : operator ****************/ + if (numOfConditions.length !== -1 && i !== numOfConditions.length - 1) { + //console.log('finding logical operator for',logicalOperatorsInClause[i]); + logicalOperator = getLogicalOperator(logicalOperatorsInClause[i]) + + if (!logicalOperator) { + grammarErr = 1; + break; + } + whereQuery += getLogicalOperator(logicalOperatorsInClause[i]) + } + /**************** END : operator ****************/ + + } } let obj = {} diff --git a/lib/xapi.js b/lib/xapi.js index 1cb49d6800..994e78f255 100644 --- a/lib/xapi.js +++ b/lib/xapi.js @@ -1,10 +1,12 @@ 'use strict'; var Xsql = require('./xsql.js'); +var whrHelp = require('./util/whereClause.helper.js'); var multer = require('multer'); var path = require('path'); const colors = require('colors'); + //define class class Xapi { @@ -110,6 +112,11 @@ class Xapi { this.app.route('/api/tables') .get(this.asyncMiddleware(this.tables.bind(this))); + this.app.route('/api/xjoin') + .get(this.asyncMiddleware(this.xjoin.bind(this))); + + stat.api += 3; + /**************** START : setup routes for each table ****************/ let resources = []; @@ -276,12 +283,34 @@ class Xapi { } + _getGrpByHavingOrderBy(req, tableName, queryParamsObj, listType) { + + /**************** add group by ****************/ + this.mysql.getGroupByClause(req.query._groupby, req.app.locals._tableName, queryParamsObj); + + /**************** add having ****************/ + this.mysql.getHavingClause(req.query._having, req.app.locals._tableName, queryParamsObj); + + /**************** add order clause ****************/ + this.mysql.getOrderByClause(req.query, req.app.locals._tableName, queryParamsObj); + + /**************** add limit clause ****************/ + if (listType === 2) { //nested + queryParamsObj.query += ' limit 1 ' + } else { + queryParamsObj.query += ' limit ?,? ' + queryParamsObj.params = queryParamsObj.params.concat(this.mysql.getLimitClause(req.query)); + } + + } + + /** * * @param req * @param res * @param queryParamsObj : {query, params} - * @param listType : 0:list, 1:nested, 2:findOne, 3:bulkRead, 4:distinct + * @param listType : 0:list, 1:nested, 2:findOne, 3:bulkRead, 4:distinct, 5:xjoin * * Updates query, params for query of type listType */ @@ -304,7 +333,7 @@ class Xapi { /**************** add tableName ****************/ queryParamsObj.query += ' from ?? '; - if (listType === 1) { + if (listType === 1) { //nested list req.app.locals._tableName = req.app.locals._childTable; @@ -357,27 +386,165 @@ class Xapi { } - /**************** add group by ****************/ - this.mysql.getGroupByClause(req.query._groupby, req.app.locals._tableName, queryParamsObj); + this._getGrpByHavingOrderBy(req, req.app.locals._tableName, queryParamsObj) - /**************** add having ****************/ - this.mysql.getHavingClause(req.query._having, req.app.locals._tableName, queryParamsObj); - /**************** add order clause ****************/ - this.mysql.getOrderByClause(req.query, req.app.locals._tableName, queryParamsObj); + //console.log(queryParamsObj.query, queryParamsObj.params); + } + + + _joinTableNames(isSecondJoin, joinTables, index, queryParamsObj) { + + if (isSecondJoin) { + + /** + * in second join - there will be ONE table and an ON condition + * this if clause deals with this + * + */ + + // add : join / left join / right join / full join / inner join + queryParamsObj.query += this.mysql.getJoinType(joinTables[index]) + queryParamsObj.query += ' ?? as ?? ' + + // eg: tbl.tableName + let tableNameAndAs = joinTables[index + 1].split('.') + + if (tableNameAndAs.length === 2) { + queryParamsObj.params.push(tableNameAndAs[1]) + queryParamsObj.params.push(tableNameAndAs[0]) + } else { + queryParamsObj.grammarErr = 1 + console.log('there was no dot for tableName ', joinTables[index + 1]); + } - /**************** add limit clause ****************/ - if (listType === 2) { - queryParamsObj.query += ' limit 1 ' } else { - queryParamsObj.query += ' limit ?,? ' - queryParamsObj.params = queryParamsObj.params.concat(this.mysql.getLimitClause(req.query)); + + /** + * in first join - there will be TWO tables and an ON condition + * this else clause deals with this + */ + + + // first table + queryParamsObj.query += ' ?? as ?? ' + // add : join / left join / right join / full join / inner join + queryParamsObj.query += this.mysql.getJoinType(joinTables[index + 1]) + // second table + queryParamsObj.query += ' ?? as ?? ' + + let tableNameAndAs = joinTables[index].split('.') + if (tableNameAndAs.length === 2) { + queryParamsObj.params.push(tableNameAndAs[1]) + queryParamsObj.params.push(tableNameAndAs[0]) + } else { + queryParamsObj.grammarErr = 1 + console.log('there was no dot for tableName ', joinTables[index]); + } + + tableNameAndAs = [] + tableNameAndAs = joinTables[index + 2].split('.') + if (tableNameAndAs.length === 2) { + queryParamsObj.params.push(tableNameAndAs[1]) + queryParamsObj.params.push(tableNameAndAs[0]) + } else { + queryParamsObj.grammarErr = 1 + console.log('there was no dot for tableName ', joinTables[index]); + } } - //console.log(queryParamsObj.query, queryParamsObj.params); + } + + prepareJoinQuery(req, res, queryParamsObj) { + + queryParamsObj.query = 'SELECT ' + queryParamsObj.grammarErr = 0; + /**************** START : get fields ****************/ + if (req.query._fields) { + + let fields = req.query._fields.split(',') + + // from _fields to - ??, ??, ?? [col1,col2,col3] + for (var i = 0; i < fields.length; ++i) { + if (i) { + queryParamsObj.query += ',' + } + queryParamsObj.query += ' ?? ' + queryParamsObj.params.push(fields[i]) + } + + } else { + + queryParamsObj.query += ' * ' + + } + + queryParamsObj.query += ' from ' + /**************** END : get fields ****************/ + + + /**************** START : get join + on ****************/ + let joinTables = req.query._join.split(',') + if (joinTables.length < 3) { + //console.log('grammar error ', joinTables.length); + queryParamsObj.grammarErr = 1; + } + + //console.log('jointables.length', joinTables); + + let onCondnCount = 0; + + for (let i = 0; i < joinTables.length - 1 && queryParamsObj.grammarErr === 0; i = i + 2) { + + onCondnCount++; + + this._joinTableNames(i, joinTables, i, queryParamsObj) + + if (queryParamsObj.grammarErr) { + console.log('failed at _joinTableNames', queryParamsObj); + break; + } + + //console.log('after join tables', queryParamsObj); + + let onCondn = '_on' + (onCondnCount) + let onCondnObj = {} + if (onCondn in req.query) { + //console.log(onCondn, req.query[onCondn]); + onCondnObj = whrHelp.getConditionClause(req.query[onCondn], ' on ') + //console.log('onCondnObj', onCondnObj); + queryParamsObj.query += ' on ' + onCondnObj.query + queryParamsObj.params = queryParamsObj.params.concat(onCondnObj.params) + } else { + queryParamsObj.grammarErr = 1; + //console.log('No on condition: ', onCondn); + break; + } + + //console.log('- - - - - - -'); + if (i === 0) { + i = i + 1 + } + //console.log('index after loop', i); + } + /**************** END : get join + on ****************/ + + if (queryParamsObj.grammarErr) { + queryParamsObj.query = '' + queryParamsObj.params = [] + } + + this.mysql.getWhereClause(req.query._where, ' ignore ', queryParamsObj, ' where '); + + //console.log('after where',queryParamsObj); + + this._getGrpByHavingOrderBy(req, 'ignore', queryParamsObj, 5) + + return queryParamsObj; } + async list(req, res) { let queryParamsObj = {} @@ -391,6 +558,25 @@ class Xapi { } + + async xjoin(req, res) { + + let obj = {} + + obj.query = ''; + obj.params = []; + + this.prepareJoinQuery(req, res, obj) + + //console.log(obj); + + let results = await this.mysql.exec(obj.query, obj.params) + res.status(200).json(results) + + //http://localhost:3000/api/xjoin?_join=pl.productlines,j,pr.products,j,ord.orderdetails&on1=(pl.productline,eq,pr.products)&on2=(pr.productcode,eq,ord.productcode) + + } + async distinct(req, res) { let queryParamsObj = {} @@ -628,19 +814,24 @@ class Xapi { let results = await this.mysql.exec(query, params); res.status(200).json(results); - } async tables(req, res) { - let query = 'show tables'; - let params = []; + 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)) + } 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; diff --git a/lib/xsql.js b/lib/xsql.js index be2b1fa9a9..0dc8d81919 100644 --- a/lib/xsql.js +++ b/lib/xsql.js @@ -245,7 +245,7 @@ class Xsql { /**************** START : prepare value object in prepared statement ****************/ - // iterate over sent object array + // iterate over sent object array let arrOfArr = [] for (var i = 0; i < objectArray.length; ++i) { let arrValues = [] @@ -297,7 +297,6 @@ class Xsql { queryParamsObj.params = queryParamsObj.params.concat(whereClauseObj.params) } - //console.log('> > > after where clause filling up:', queryParamsObj.query, queryParamsObj.params); } } @@ -604,11 +603,57 @@ class Xsql { } + getJoinType(joinInQueryParams) { + + //console.log('joinInQueryParams',joinInQueryParams); + + switch (joinInQueryParams) { + + case '_lj': + return ' left join ' + break; + + case '_rj': + return ' right join ' + break; + + case '_fj': + return ' full join ' + break; + + case '_ij': + return ' inner join ' + break; + + case '_j': + return ' join ' + break; + + } + + return ' join ' + + } + + getJoinTables(req) { + + } + + getJoinOnConditions() { + + } + + getJoinQuery() { + + } + + globalRoutesPrint(apiPrefix) { let r = [] r.push(apiPrefix + "tables") + r.push(apiPrefix + "xjoin") if (this.sqlConfig.dynamic) { r.push(apiPrefix + "dynamic") diff --git a/package.json b/package.json index 76dbbc7787..396a7cf78b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "xmysql", - "version": "0.3.4", + "version": "0.4.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 b6d86e5276..7be4377f89 100644 --- a/tests/tests.js +++ b/tests/tests.js @@ -1593,6 +1593,108 @@ describe('xmysql : tests', function () { }); }); + it('GET /api/xjoin?_join=pl.productlines,_j,pr.products&_on1=(pl.productline,eq,pr.productline) should PASS', function (done) { + + //post to an url with data + agent.get('/api/xjoin?_join=pl.productlines,_j,pr.products&_on1=(pl.productline,eq,pr.productline)') //enter url + .expect(200)//200 for success 4xx for failure + .end(function (err, res) { + + // Handle /api/v error + if (err) { + return done(err); + } + + //validate response + Object.keys(res.body[0]).length.should.be.equals(12) + return done(); + + }); + }); + + + it('GET /api/xjoin?_join=pl.productlines,_j,pr.products&_on1=(pl.productline,eq,pr.productline)&_fields=pl.productline,pr.productName should PASS', function (done) { + + //post to an url with data + agent.get('/api/xjoin?_join=pl.productlines,_j,pr.products&_on1=(pl.productline,eq,pr.productline)&_fields=pl.productline,pr.productName') //enter url + .expect(200)//200 for success 4xx for failure + .end(function (err, res) { + + // Handle /api/v error + if (err) { + return done(err); + } + + //validate response + Object.keys(res.body[0]).length.should.be.equals(2) + res.body.length.should.be.equals(20) + + return done(); + + }); + }); + + it('GET /api/xjoin?_join=pl.productlines,_j,pr.products&_on1=(pl.productline,eq,pr.productline)&_fields=pl.productline,pr.productName&_size=2 should PASS', function (done) { + + //post to an url with data + agent.get('/api/xjoin?_join=pl.productlines,_j,pr.products&_on1=(pl.productline,eq,pr.productline)&_fields=pl.productline,pr.productName&_size=2') //enter url + .expect(200)//200 for success 4xx for failure + .end(function (err, res) { + + // Handle /api/v error + if (err) { + return done(err); + } + + //validate response + Object.keys(res.body[0]).length.should.be.equals(2) + res.body.length.should.be.equals(2) + + return done(); + + }); + }); + + it('GET /api/xjoin?_join=pl.productlines,_j,pr.products,_j,ord.orderDetails&_on1=(pl.productline,eq,pr.productline)&_on2=(pr.productcode,eq,ord.productcode) should PASS', function (done) { + + //post to an url with data + agent.get('/api/xjoin?_join=pl.productlines,_j,pr.products,_j,ord.orderDetails&_on1=(pl.productline,eq,pr.productline)&_on2=(pr.productcode,eq,ord.productcode)') //enter url + .expect(200)//200 for success 4xx for failure + .end(function (err, res) { + + // Handle /api/v error + if (err) { + return done(err); + } + + //validate response + Object.keys(res.body[0]).length.should.be.equals(16) + res.body.length.should.be.equals(20) + + return done(); + + }); + }); + + it('GET /api/xjoin?_join=pl.productlines,_j,pr.products&_on1=(pl.productline,eq,pr.productline)&_fields=pl.productline,pr.productName&_size=2&_where=(productName,like,1972~) should PASS', function (done) { + + //post to an url with data + agent.get('/api/xjoin?_join=pl.productlines,_j,pr.products&_on1=(pl.productline,eq,pr.productline)&_fields=pl.productline,pr.productName&_size=2&_where=(productName,like,1972~)') //enter url + .expect(200)//200 for success 4xx for failure + .end(function (err, res) { + + // Handle /api/v error + if (err) { + return done(err); + } + + //validate response + res.body.length.should.be.equals(1) + + return done(); + + }); + }); it('where clause unit ?_where=(abc,eq,1234) should PASS', function (done) { @@ -1609,7 +1711,6 @@ describe('xmysql : tests', function () { }); - it('where clause unit ?_where=(abc,ne,1234) should PASS', function (done) { var err = whereClause.getConditionClause('(abc,ne,1234)')