CartoDB-SQL-API/app/controllers/query_controller.js

137 lines
4.8 KiB
JavaScript
Raw Normal View History

'use strict';
2019-07-27 01:38:17 +08:00
const PSQL = require('cartodb-psql');
const bodyParserMiddleware = require('../middlewares/body-parser');
const userMiddleware = require('../middlewares/user');
const errorMiddleware = require('../middlewares/error');
2018-02-19 22:49:17 +08:00
const authorizationMiddleware = require('../middlewares/authorization');
const connectionParamsMiddleware = require('../middlewares/connection-params');
const timeoutLimitsMiddleware = require('../middlewares/timeout-limits');
const { initializeProfilerMiddleware } = require('../middlewares/profiler');
const rateLimitsMiddleware = require('../middlewares/rate-limit');
2018-03-01 20:15:32 +08:00
const { RATE_LIMIT_ENDPOINTS_GROUPS } = rateLimitsMiddleware;
2019-07-26 23:18:18 +08:00
const parameters = require('../middlewares/parameters');
2019-02-27 16:02:31 +08:00
const logMiddleware = require('../middlewares/log');
const cancelOnClientAbort = require('../middlewares/cancel-on-client-abort');
2019-07-26 23:44:28 +08:00
const affectedTables = require('../middlewares/affected-tables');
2019-07-27 00:05:47 +08:00
const accessValidator = require('../middlewares/access-validator');
2019-07-27 00:23:14 +08:00
const queryMayWrite = require('../middlewares/query-may-write');
const cacheControl = require('../middlewares/cache-control');
const cacheChannel = require('../middlewares/cache-channel');
const surrogateKey = require('../middlewares/surrogate-key');
const lastModified = require('../middlewares/last-modified');
2019-07-27 01:22:26 +08:00
const formatter = require('../middlewares/formatter');
2019-07-27 01:31:28 +08:00
const content = require('../middlewares/content');
2019-07-27 01:38:46 +08:00
function QueryController(metadataBackend, userDatabaseService, statsdClient, userLimitsService) {
this.metadataBackend = metadataBackend;
2019-07-27 01:38:46 +08:00
this.statsdClient = statsdClient;
this.userDatabaseService = userDatabaseService;
2018-03-01 18:31:35 +08:00
this.userLimitsService = userLimitsService;
}
2015-12-04 01:43:13 +08:00
QueryController.prototype.route = function (app) {
const { base_url } = global.settings;
const forceToBeMaster = false;
const queryMiddlewares = () => {
return [
bodyParserMiddleware(),
initializeProfilerMiddleware('query'),
userMiddleware(this.metadataBackend),
rateLimitsMiddleware(this.userLimitsService, RATE_LIMIT_ENDPOINTS_GROUPS.QUERY),
authorizationMiddleware(this.metadataBackend, forceToBeMaster),
connectionParamsMiddleware(this.userDatabaseService),
timeoutLimitsMiddleware(this.metadataBackend),
2019-07-26 23:18:18 +08:00
parameters({ strategy: 'query' }),
2019-02-28 18:49:05 +08:00
logMiddleware(logMiddleware.TYPES.QUERY),
cancelOnClientAbort(),
2019-07-26 23:44:28 +08:00
affectedTables(),
2019-07-27 00:05:47 +08:00
accessValidator(),
2019-07-27 00:23:14 +08:00
queryMayWrite(),
cacheControl(),
cacheChannel(),
surrogateKey(),
lastModified(),
2019-07-27 01:22:26 +08:00
formatter(),
2019-07-27 01:31:28 +08:00
content(),
this.handleQuery.bind(this),
errorMiddleware()
];
2018-03-01 21:47:34 +08:00
};
app.all(`${base_url}/sql`, queryMiddlewares());
app.all(`${base_url}/sql.:f`, queryMiddlewares());
};
// jshint maxcomplexity:21
QueryController.prototype.handleQuery = function (req, res, next) {
var self = this;
2019-07-27 01:22:26 +08:00
const { user: username, userDbParams: dbopts, userLimits } = res.locals;
const { orderBy, sortOrder, limit, offset } = res.locals.params;
2019-07-27 01:22:26 +08:00
const { sql, skipfields, decimalPrecision, filename, callback } = res.locals.params;
2019-07-27 01:38:17 +08:00
2019-07-27 01:22:26 +08:00
let { formatter } = req;
try {
if (req.profiler) {
req.profiler.done('init');
}
2019-07-27 01:38:17 +08:00
const opts = {
username: username,
dbopts: dbopts,
sink: res,
gn: 'the_geom', // TODO: read from configuration FILE,
dp: decimalPrecision,
skipfields: skipfields,
sql: new PSQL.QueryWrapper(sql).orderBy(orderBy, sortOrder).window(limit, offset).query(),
filename: filename,
bufferedRows: global.settings.bufferedRows,
callback: callback,
timeout: userLimits.timeout
};
2019-07-27 01:38:17 +08:00
if (req.profiler) {
opts.profiler = req.profiler;
opts.beforeSink = function () {
req.profiler.done('beforeSink');
res.header('X-SQLAPI-Profiler', req.profiler.toJSONString());
};
}
2019-02-27 16:02:31 +08:00
2019-07-27 01:38:17 +08:00
if (dbopts.host) {
res.header('X-Served-By-DB-Host', dbopts.host);
}
2019-07-27 01:38:17 +08:00
formatter.sendResponse(opts, (err) => {
formatter = null;
2019-07-27 01:38:17 +08:00
if (err) {
next(err);
}
if ( req.profiler ) {
req.profiler.sendStats();
}
2019-07-27 01:38:46 +08:00
if (this.statsdClient) {
2019-07-27 01:38:17 +08:00
if ( err ) {
2019-07-27 01:38:46 +08:00
this.statsdClient.increment('sqlapi.query.error');
2019-07-27 01:38:17 +08:00
} else {
2019-07-27 01:38:46 +08:00
this.statsdClient.increment('sqlapi.query.success');
}
}
2019-07-27 01:38:17 +08:00
});
} catch (err) {
next(err);
2019-07-27 01:38:46 +08:00
if (this.statsdClient) {
this.statsdClient.increment('sqlapi.query.error');
}
}
};
module.exports = QueryController;