const libs = `${process.cwd()}/libs`; const log = require(`${libs}/log`)(module); const _ = require('lodash'); class ReqQueryFields { constructor(fields = {}, fieldValues = {}) { // Parâmetros no campo query da requisição. // Exemplo de field: // { // name: 'dims', // field: true, // where: false // } this.fields = fields; this.fieldValues = fieldValues; } addField(field) { // Parâmetro no campo query da requisição. // Exemplo de field: // { // name: 'dims', // field: true, // where: false, // fieldValues: {} // } if(typeof this.fields[field.name] === 'undefined') { log.debug("added field "+field.name); this.fields[field.name] = field; } return this; } addValue(fieldValue) { // Array de valores aceitos pelo campo // Exemplo de valor: // { // name: 'location', // table: 'localizacao', // tableField: 'descricao' // resultField: 'location_name', // where: { // relation: '=', // type: 'integer', // field: 'id_localizacao', // table: 'turma' // }, // join: { // primary: 'pk_localizacao_id', // foreign: 'id_localizacao', // foreignTable: 'turma' // } // } if(typeof this.fieldValues[fieldValue.name] === 'undefined') { this.fieldValues[fieldValue.name] = fieldValue; log.debug("added value "+fieldValue.name); } return this; } addValueToField(fieldValue, field) { if(typeof this.fields[field] === 'undefined') { throw 'No field with name ' +field+ ' defined'; } if(typeof this.fields[field].values === 'undefined') { this.fields[field].values = {}; } if(typeof this.fields[field].values[fieldValue.name] === 'undefined') { this.fields[field].values[fieldValue.name] = fieldValue; log.debug("added value "+fieldValue.name+ ' to field ' + field); } return this; } parse() { return (req, res, next) => { Object.keys(this.fields).map((key, index) => { let params = []; let f = this.fields[key]; log.debug('f'); log.debug(f); // Unimos os valores aceitos globalmente com os aceitos apenas pelo parâmetro let values = _.merge(this.fieldValues, f.values); Object.keys(values).map((k, i) => { let value = values[k]; log.debug('value'); log.debug(value); params.push(value.name); }); let queryField = f.name; let arrayOfParams = params; req[queryField] = {}; if (req.query[queryField]) { const params = req.query[queryField].split(','); // Temporary object to hold the params and it's values const obj = {}; for (const param of params) { // Get the key and the value - state:41 is key 'state' whith value 41. // kv is then an array [key, value] or [key] if there is no value const kv = param.split(':'); // Check if there is a value. If there isn't, assign true obj[kv[0]] = (typeof kv[1] === 'undefined') ? true : kv[1]; // obj is now an object {kv[0]: kv[1]} ou {kv[0]: true} } // If the array exists and is not empty we intersect if (typeof arrayOfParams !== 'undefined' && arrayOfParams.length > 0) { // Intersect the keys of the obj with the array arrayOfParams // The intersection array is assigned with the keys const intersection = _.intersection(arrayOfParams, Object.keys(obj)); // This is a bit tricky... // For each key in the intersection array we get it's value in the obj // and assign it to the custom attribute in the req obj. // For example: instersection => ["state"] so // obj[intersection[i]] (with i=0) is obj["state"], that is 41 // and req[queryField]["state"] = 41 for (let i = 0; i < intersection.length; ++i) { req[queryField][intersection[i]] = obj[intersection[i]]; } req[queryField].size = intersection.length; } else { req[queryField] = obj; req[queryField].size = Object.keys(obj).length; } } }); next(); }; } build() { return (req, res, next) => { Object.keys(this.fields).map((key, index) => { let field = this.fields[key]; log.debug(field); let param = req[field.name]; log.debug('param'); log.debug(param); Object.keys(param).map((k, i) => { let values = _.merge(this.fieldValues, field.values); log.debug('ValueS'); log.debug(values); log.debug('k'); log.debug(k); if(typeof values[k] !== 'undefined') { // Clonamos para não alterar let value = _.clone(values[k]); log.debug(value); // Checa se não fizemos o join para este valor e se é necessário fazer if(!value.hasJoined && typeof value.join !== 'undefined') { let foreignTable = ''; if(value.join.foreignTable) foreignTable = value.join.foreignTable+'.'; // Fazemos o join req.sql.join(value.table, null, foreignTable+value.join.foreign+'='+value.table+'.'+value.join.primary); // Marcamos o join como feito para não ter problemas value.hasJoined = true; } // Se o valor é um campo a ser incluído no SELECT if(typeof field.field !== 'undefined' && field.field) { log.debug('SELECT'); req.sql.field(value.table+'.'+value.tableField, value.resultField || value.tableField) .group(value.table+'.'+value.tableField) .order(value.table+'.'+value.tableField); } // Se o valor é um campo para ser usado no WHERE if(typeof field.where !== 'undefined' && field.where) { log.debug('WHERE'); // Valor do where let whereValue = param[k]; // Valor sempre vem como string, necessário fazer parse para o banco if(value.where.type === 'integer') whereValue = parseInt(whereValue, 10); if(value.where.type === 'double') whereValue = parseFloat(whereValue); if(value.where.type === 'string') whereValue = '%'+whereValue+'%'; let tbl = value.where.table || value.table; let whereField = (value.where.type === 'string')? 'LOWER('+tbl+'.'+value.where.field+')' : tbl+'.'+value.where.field; let lower = (value.where.type === 'string') ? ' LOWER(?) ' : ' ? '; req.sql.where(whereField + ' ' + value.where.relation + lower, whereValue); } } }); }); next(); }; } } module.exports = ReqQueryFields;