Skip to content
Snippets Groups Projects
Unverified Commit 1baf5471 authored by João Victor Risso's avatar João Victor Risso
Browse files

Refactor enrollments route to include query building

Changes:
- Add error handling in the database query function. When a database error
occurs, it is logged in the server and a plain text response is returned to the
user.
- Implement the building of queries inside the API, which removes the
  need to have stored functions/procedures on the database, which is not
  flexible or efficient to maintain/extend.
- Use route chaining to determine which function will build the query
  that needs to be executed. The last function is the one that actually
  sends the query to the database and send the response.

Future Work:
- Instead of returning a plain text response, return a HTTP 501 status,
  which is currently not possible. When one attempts to send such status
  the framework throws an error that the headers were already sent for
  the current request.
- Chaining based on the route might not be the best solution here.
parent 7e4819e9
No related branches found
No related tags found
1 merge request!7Refactor enrollments route to include query building
Pipeline #
......@@ -60,144 +60,218 @@ router.get('/data', function(req, res) {
router.get('/enrollments', function(req, res, next) {
const params = req.query;
req.paramCnt = 0;
if (params.id) {
if (typeof params.id !== 'undefined') {
req.id = parseInt(params.id, 10);
req.paramCnt += 1;
}
if (params.location_id) {
if (typeof params.location_id !== 'undefined') {
req.location_id = parseInt(params.location_id, 10);
req.paramCnt += 1;
}
if (params.adm_dependency_id) {
if (typeof params.adm_dependency_id !== 'undefined') {
req.adm_dependency_id = parseInt(params.adm_dependency_id, 10);
req.paramCnt += 1;
}
if (params.census_year) {
if (typeof params.census_year !== 'undefined') {
req.census_year = parseInt(params.census_year, 10);
req.paramCnt += 1;
}
if (params.aggregate) {
log.debug('aggregate parameter detected');
next('route');
} else {
log.debug('No aggregate parameter detected');
next();
}
next();
});
/*
switch(params.aggregate)
{
case "city":
if (id) {
enrollmentSql = "SELECT nome AS name, total FROM mat_municipio(" + id + "," + census_year + "," + adm_dependency_id + "," + location_id + ")";
} else {
enrollmentSql = "SELECT nome AS name, total FROM mat_municipios(" + census_year + "," + adm_dependency_id + "," + location_id + ")";
}
break;
case "state":
if (id) {
enrollmentSql = "SELECT nome AS name, total FROM mat_estado(" + id + "," + census_year + "," + adm_dependency_id + "," + location_id + ")";
} else {
enrollmentSql = "SELECT nome AS name, total FROM mat_estados(" + census_year + "," + adm_dependency_id + "," + location_id + ")";
}
break;
case "region":
if (id) {
enrollmentSql = "SELECT nome AS name, total FROM mat_regiao(" + id + "," + census_year + "," + adm_dependency_id + "," + location_id + ")";
} else {
enrollmentSql = "SELECT nome AS name, total FROM mat_regioes(" + census_year + "," + adm_dependency_id + "," + location_id + ")";
}
break;
default:
enrollmentSql = "SELECT nome AS name, total FROM mat_brasil(" + census_year + "," + adm_dependency_id + "," + location_id + ")";
}
router.get('/enrollments', function(req, res, next) {
const params = req.query;
if (typeof params.aggregate !== 'undefined' && params.aggregate === 'region') {
log.debug('Using enrollments query for regions');
req.sqlQuery = 'SELECT r.nome, COALESCE(SUM(t.num_matriculas), 0) AS total '
+ 'FROM regioes AS r '
+ 'INNER JOIN estados AS e ON r.pk_regiao_id = e.fk_regiao_id '
+ 'INNER JOIN municipios AS m ON e.pk_estado_id = m.fk_estado_id '
+ 'LEFT OUTER JOIN turmas AS t ON ( '
+ 'm.pk_municipio_id = t.fk_municipio_id ';
req.sqlQueryParams = [];
log.debug(params);
log.debug("Executing query: " + enrollmentSql);
if (typeof req.census_year !== 'undefined') {
req.sqlQuery += ' AND ';
req.sqlQuery += 't.ano_censo = ?';
req.sqlQueryParams.push(req.census_year);
}
conn.query(enrollmentSql, true).then(function(result) {
log.debug(result);
if (req.query.format === 'csv') {
res.csv(result.data);
} else if (req.query.format === 'xml') {
res.send(xml("result", JSON.stringify({enrollments: result.data})))
if (typeof req.adm_dependency_id !== 'undefined') {
req.sqlQuery += ' AND ';
req.sqlQuery += 't.fk_dependencia_adm_id = ?';
req.sqlQueryParams.push(req.adm_dependency_id);
}
else {
res.json({
result: result.data
});
if (typeof req.location_id !== 'undefined') {
req.sqlQuery += ' AND ';
req.sqlQuery += 't.id_localizacao = ?';
req.sqlQueryParams.push(req.location_id);
}
log.debug("All resources were released");
}, function(error) {
});
*/
}, function(req, res, next) {
/** When no +aggregate+ parameter value is specified on the request, then
* assign the query to compute the result for the whole country.
*/
log.debug('Using SQL query for the whole country');
req.sql_query = 'SELECT * FROM turmas LIMIT 1';
next('route');
req.sqlQuery += ')';
if (typeof req.id !== 'undefined') {
req.sqlQuery += ' WHERE ';
req.sqlQuery += 'r.pk_regiao_id = ?';
req.sqlQueryParams.push(req.id);
}
req.sqlQuery += ' GROUP BY r.nome';
}
next();
});
router.get('/enrollments', function(req, res, next) {
const params = req.query;
if (!params.aggregate) {
next('route');
} else if (params.aggregate == 'region') {
log.debug('Using enrollments query for regions');
req.sql_query = 'SELECT * FROM turmas LIMIT 1';
if (typeof params.aggregate !== 'undefined' && params.aggregate === 'state') {
log.debug('Using enrollments query for states');
req.sqlQuery = 'SELECT e.nome, COALESCE(SUM(t.num_matriculas), 0) as total '
+ 'FROM estados AS e '
+ 'INNER JOIN municipios AS m ON m.fk_estado_id = e.pk_estado_id '
+ 'LEFT OUTER JOIN turmas AS t ON ('
+ 'm.pk_municipio_id = t.fk_municipio_id ';
req.sqlQueryParams = [];
if (typeof req.census_year !== 'undefined') {
req.sqlQuery += ' AND ';
req.sqlQuery += 't.ano_censo = ?';
req.sqlQueryParams.push(req.census_year);
}
if (typeof req.adm_dependency_id !== 'undefined') {
req.sqlQuery += ' AND ';
req.sqlQuery += 't.fk_dependencia_adm_id = ?';
req.sqlQueryParams.push(req.adm_dependency_id);
}
if (typeof req.location_id !== 'undefined') {
req.sqlQuery += ' AND ';
req.sqlQuery += 't.id_localizacao = ?';
req.sqlQueryParams.push(req.location_id);
}
req.sqlQuery += ')';
if (typeof req.id !== 'undefined') {
req.sqlQuery += " WHERE ";
req.sqlQuery += "e.pk_estado_id = ?";
req.sqlQueryParams.push(req.id);
}
req.sqlQuery += ' GROUP BY e.nome';
}
next('route');
next();
});
router.get('/enrollments', function(req, res, next) {
const params = req.query;
if (!params.aggregate) {
next('route');
} else if (params.aggregate == 'state') {
log.debug('Using enrollments query for states');
req.sql_query = 'SELECT * FROM turmas LIMIT 1';
if (typeof params.aggregate !== 'undefined' && params.aggregate === 'city') {
log.debug('Using enrollments query for cities');
req.sqlQuery = 'SELECT m.nome, COALESCE(SUM(t.num_matriculas), 0) as total '
+ 'FROM municipios AS m '
+ 'LEFT OUTER JOIN turmas AS t ON ( '
+ 'm.pk_municipio_id = t.fk_municipio_id';
req.sqlQueryParams = [];
if (typeof req.census_year !== 'undefined') {
req.sqlQuery += ' AND ';
req.sqlQuery += 't.ano_censo = ?';
req.sqlQueryParams.push(req.census_year);
}
if (typeof req.adm_dependency_id !== 'undefined') {
req.sqlQuery += ' AND ';
req.sqlQuery += 't.fk_dependencia_adm_id = ?';
req.sqlQueryParams.push(req.adm_dependency_id);
}
if (typeof req.location_id !== 'undefined') {
req.sqlQuery += ' AND ';
req.sqlQuery += 't.id_localizacao = ?';
req.sqlQueryParams.push(req.location_id);
}
req.sqlQuery += ')';
if (typeof req.id !== 'undefined') {
req.sqlQuery += " WHERE ";
req.sqlQuery += "m.pk_municipio_id = ?";
req.sqlQueryParams.push(req.id);
}
req.sqlQuery += 'GROUP BY m.nome';
}
next('route');
next();
});
router.get('/enrollments', function(req, res, next) {
const params = req.query;
if (!params.aggregate) {
next('route');
} else if (params.aggregate == 'city') {
log.debug('Using enrollments query for cities');
req.sql_query = 'SELECT * FROM turmas LIMIT 1';
if (typeof params.aggregate === 'undefined') {
log.debug('Using enrollments query for the whole country');
req.sqlQuery = 'SELECT \'Brasil\', COALESCE(SUM(t.num_matriculas),0) AS total '
+ 'FROM turmas AS t';
req.sqlQueryParams = [];
if (req.paramCnt > 0) {
req.sqlQuery += ' WHERE ';
}
if (typeof req.census_year !== 'undefined') {
req.sqlQuery += 't.ano_censo = ?';
req.sqlQueryParams.push(req.census_year);
}
if (typeof req.adm_dependency_id !== 'undefined') {
if (req.sqlQueryParams.length > 0) {
req.sqlQuery += ' AND ';
}
req.sqlQuery += 't.fk_dependencia_adm = ?';
req.sqlQueryParams.push(req.adm_dependency_id);
}
if (typeof req.location_id !== 'undefined') {
if (req.sqlQueryParams.length > 0) {
req.sqlQuery += ' AND ';
}
req.sqlQuery += 't.id_localizacao = ?';
req.sqlQueryParams.push(req.location_id);
}
}
next('route');
next();
});
router.get('/enrollments', function(req, res, next) {
log.debug('Request parameters: ${req}?');
if (!req.sql_query) {
log.debug('Request parameters: ${ req }?');
if (!req.sqlQuery) {
/* Should only happen if there is a bug in the chaining of the
* '/enrollments' route, since when no +aggregate+ parameter is given,
* it defaults to use the query for the whole country.
*/
log.error('BUG -- No SQL query was found to be executed!');
res.status(501).end();
res.send('Request could not be satisfied due to an internal error');
} else {
log.debug('SQL query: ${req.sql_query}?');
conn.query(req.sql_query, true).then(function(result) {
log.debug(result);
if (req.query.format === 'csv') {
res.csv(result.data);
} else if (req.query.format === 'xml') {
res.send(xml('result', JSON.stringify({enrollments: result.data})));
} else {
res.json({ result: result.data });
}
log.debug('SQL query: ${ req.sqlQuery }?');
log.debug(req.sqlQuery);
conn.prepare(req.sqlQuery, true).then(function(dbQuery) {
dbQuery.exec(req.sqlQueryParams).then(function(dbResult) {
log.debug(dbResult);
if (req.query.format === 'csv') {
res.csv(dbResult.data);
} else if (req.query.format === 'xml') {
res.send(xml('result', JSON.stringify({enrollments: dbResult.data})));
} else {
res.json({ result: dbResult.data });
}
});
}, function(error) {
log.error('SQL query error: ${error}?');
res.status(501).end();
log.error('SQL query error: ${ error.message }?');
log.debug(error);
res.send('Request could not be satisfied due to an internal error');
});
}
});
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment