Merge remote-tracking branch 'origin/master' into mvt-path-conf
This commit is contained in:
commit
d30f710534
6
NEWS.md
6
NEWS.md
@ -3,6 +3,12 @@
|
|||||||
## 4.0.1
|
## 4.0.1
|
||||||
Released 2017-mm-dd
|
Released 2017-mm-dd
|
||||||
|
|
||||||
|
- Split and move `req2params` method to multiple middlewares.
|
||||||
|
- Use express error handler middleware to respond in case of something went wrong.
|
||||||
|
- Use `res.locals` object to share info between middlewares and leave `req.params` as an object containing properties mapped to the named route params.
|
||||||
|
- Move `LZMA` decompression to its own middleware.
|
||||||
|
|
||||||
|
|
||||||
## 4.0.0
|
## 4.0.0
|
||||||
Released 2017-10-04
|
Released 2017-10-04
|
||||||
|
|
||||||
|
@ -19,22 +19,22 @@ function AuthApi(pgConnection, metadataBackend, mapStore, templateMaps) {
|
|||||||
|
|
||||||
module.exports = AuthApi;
|
module.exports = AuthApi;
|
||||||
|
|
||||||
// Check if a request is authorized by a signer
|
// Check if the user is authorized by a signer
|
||||||
//
|
//
|
||||||
// @param req express request object
|
// @param res express response object
|
||||||
// @param callback function(err, signed_by) signed_by will be
|
// @param callback function(err, signed_by) signed_by will be
|
||||||
// null if the request is not signed by anyone
|
// null if the request is not signed by anyone
|
||||||
// or will be a string cartodb username otherwise.
|
// or will be a string cartodb username otherwise.
|
||||||
//
|
//
|
||||||
AuthApi.prototype.authorizedBySigner = function(req, callback) {
|
AuthApi.prototype.authorizedBySigner = function(res, callback) {
|
||||||
if ( ! req.params.token || ! req.params.signer ) {
|
if ( ! res.locals.token || ! res.locals.signer ) {
|
||||||
return callback(null, false); // no signer requested
|
return callback(null, false); // no signer requested
|
||||||
}
|
}
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
var layergroup_id = req.params.token;
|
var layergroup_id = res.locals.token;
|
||||||
var auth_token = req.params.auth_token;
|
var auth_token = res.locals.auth_token;
|
||||||
|
|
||||||
this.mapStore.load(layergroup_id, function(err, mapConfig) {
|
this.mapStore.load(layergroup_id, function(err, mapConfig) {
|
||||||
if (err) {
|
if (err) {
|
||||||
@ -84,11 +84,12 @@ AuthApi.prototype.authorizedByAPIKey = function(user, req, callback) {
|
|||||||
* Check access authorization
|
* Check access authorization
|
||||||
*
|
*
|
||||||
* @param req - standard req object. Importantly contains table and host information
|
* @param req - standard req object. Importantly contains table and host information
|
||||||
|
* @param res - standard res object. Contains the auth parameters in locals
|
||||||
* @param callback function(err, allowed) is access allowed not?
|
* @param callback function(err, allowed) is access allowed not?
|
||||||
*/
|
*/
|
||||||
AuthApi.prototype.authorize = function(req, callback) {
|
AuthApi.prototype.authorize = function(req, res, callback) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var user = req.context.user;
|
var user = res.locals.user;
|
||||||
|
|
||||||
step(
|
step(
|
||||||
function () {
|
function () {
|
||||||
@ -101,11 +102,11 @@ AuthApi.prototype.authorize = function(req, callback) {
|
|||||||
// if not authorized by api_key, continue
|
// if not authorized by api_key, continue
|
||||||
if (!authorized) {
|
if (!authorized) {
|
||||||
// not authorized by api_key, check if authorized by signer
|
// not authorized by api_key, check if authorized by signer
|
||||||
return self.authorizedBySigner(req, this);
|
return self.authorizedBySigner(res, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
// authorized by api key, login as the given username and stop
|
// authorized by api key, login as the given username and stop
|
||||||
self.pgConnection.setDBAuth(user, req.params, function(err) {
|
self.pgConnection.setDBAuth(user, res.locals, function(err) {
|
||||||
callback(err, true); // authorized (or error)
|
callback(err, true); // authorized (or error)
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -120,7 +121,7 @@ AuthApi.prototype.authorize = function(req, callback) {
|
|||||||
// if no signer name was given, let dbparams and
|
// if no signer name was given, let dbparams and
|
||||||
// PostgreSQL do the rest.
|
// PostgreSQL do the rest.
|
||||||
//
|
//
|
||||||
if ( ! req.params.signer ) {
|
if ( ! res.locals.signer ) {
|
||||||
return callback(null, true); // authorized so far
|
return callback(null, true); // authorized so far
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,7 +129,7 @@ AuthApi.prototype.authorize = function(req, callback) {
|
|||||||
return callback(null, false);
|
return callback(null, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.pgConnection.setDBAuth(user, req.params, function(err) {
|
self.pgConnection.setDBAuth(user, res.locals, function(err) {
|
||||||
req.profiler.done('setDBAuth');
|
req.profiler.done('setDBAuth');
|
||||||
callback(err, true); // authorized (or error)
|
callback(err, true); // authorized (or error)
|
||||||
});
|
});
|
||||||
|
@ -113,9 +113,7 @@ function getOverrideParams(params, ownFilter) {
|
|||||||
return overrideParams;
|
return overrideParams;
|
||||||
}
|
}
|
||||||
|
|
||||||
DataviewBackend.prototype.search = function (mapConfigProvider, user, params, callback) {
|
DataviewBackend.prototype.search = function (mapConfigProvider, user, dataviewName, params, callback) {
|
||||||
var dataviewName = params.dataviewName;
|
|
||||||
|
|
||||||
step(
|
step(
|
||||||
function getMapConfig() {
|
function getMapConfig() {
|
||||||
mapConfigProvider.getMapConfig(this);
|
mapConfigProvider.getMapConfig(this);
|
||||||
|
@ -10,8 +10,10 @@ var BaseController = require('./base');
|
|||||||
var cors = require('../middleware/cors');
|
var cors = require('../middleware/cors');
|
||||||
var userMiddleware = require('../middleware/user');
|
var userMiddleware = require('../middleware/user');
|
||||||
|
|
||||||
function AnalysesController(authApi, pgConnection) {
|
|
||||||
BaseController.call(this, authApi, pgConnection);
|
function AnalysesController(prepareContext) {
|
||||||
|
BaseController.call(this);
|
||||||
|
this.prepareContext = prepareContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
util.inherits(AnalysesController, BaseController);
|
util.inherits(AnalysesController, BaseController);
|
||||||
@ -19,7 +21,13 @@ util.inherits(AnalysesController, BaseController);
|
|||||||
module.exports = AnalysesController;
|
module.exports = AnalysesController;
|
||||||
|
|
||||||
AnalysesController.prototype.register = function(app) {
|
AnalysesController.prototype.register = function(app) {
|
||||||
app.get(app.base_url_mapconfig + '/analyses/catalog', cors(), userMiddleware, this.catalog.bind(this));
|
app.get(
|
||||||
|
app.base_url_mapconfig + '/analyses/catalog',
|
||||||
|
cors(),
|
||||||
|
userMiddleware,
|
||||||
|
this.prepareContext,
|
||||||
|
this.catalog.bind(this)
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
AnalysesController.prototype.sendResponse = function(req, res, resource) {
|
AnalysesController.prototype.sendResponse = function(req, res, resource) {
|
||||||
@ -27,17 +35,13 @@ AnalysesController.prototype.sendResponse = function(req, res, resource) {
|
|||||||
this.send(req, res, resource, 200);
|
this.send(req, res, resource, 200);
|
||||||
};
|
};
|
||||||
|
|
||||||
AnalysesController.prototype.catalog = function(req, res) {
|
AnalysesController.prototype.catalog = function (req, res, next) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var username = req.context.user;
|
var username = res.locals.user;
|
||||||
|
|
||||||
step(
|
step(
|
||||||
function reqParams() {
|
function catalogQuery() {
|
||||||
self.req2params(req, this);
|
var pg = new PSQL(dbParamsFromReqParams(res.locals));
|
||||||
},
|
|
||||||
function catalogQuery(err) {
|
|
||||||
assert.ifError(err);
|
|
||||||
var pg = new PSQL(dbParamsFromReqParams(req.params));
|
|
||||||
getMetadata(username, pg, this);
|
getMetadata(username, pg, this);
|
||||||
},
|
},
|
||||||
function prepareResponse(err, results) {
|
function prepareResponse(err, results) {
|
||||||
@ -80,7 +84,8 @@ AnalysesController.prototype.catalog = function(req, res) {
|
|||||||
err = new Error('Unauthorized');
|
err = new Error('Unauthorized');
|
||||||
err.http_status = 401;
|
err.http_status = 401;
|
||||||
}
|
}
|
||||||
self.sendError(req, res, err);
|
|
||||||
|
next(req, res, err);
|
||||||
} else {
|
} else {
|
||||||
self.sendResponse(req, res, { catalog: catalogWithTables });
|
self.sendResponse(req, res, { catalog: catalogWithTables });
|
||||||
}
|
}
|
||||||
|
@ -1,130 +1,12 @@
|
|||||||
var assert = require('assert');
|
|
||||||
|
|
||||||
var _ = require('underscore');
|
|
||||||
var step = require('step');
|
|
||||||
var debug = require('debug')('windshaft:cartodb');
|
var debug = require('debug')('windshaft:cartodb');
|
||||||
|
|
||||||
// Whitelist query parameters and attach format
|
function BaseController() {
|
||||||
var REQUEST_QUERY_PARAMS_WHITELIST = [
|
|
||||||
'config',
|
|
||||||
'map_key',
|
|
||||||
'api_key',
|
|
||||||
'auth_token',
|
|
||||||
'callback',
|
|
||||||
'zoom',
|
|
||||||
'lon',
|
|
||||||
'lat',
|
|
||||||
// analysis
|
|
||||||
'filters' // json
|
|
||||||
];
|
|
||||||
|
|
||||||
function BaseController(authApi, pgConnection) {
|
|
||||||
this.authApi = authApi;
|
|
||||||
this.pgConnection = pgConnection;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = BaseController;
|
module.exports = BaseController;
|
||||||
|
|
||||||
// jshint maxcomplexity:8
|
|
||||||
/**
|
|
||||||
* Whitelist input and get database name & default geometry type from
|
|
||||||
* subdomain/user metadata held in CartoDB Redis
|
|
||||||
* @param req - standard express request obj. Should have host & table
|
|
||||||
* @param callback
|
|
||||||
*/
|
|
||||||
BaseController.prototype.req2params = function(req, callback){
|
|
||||||
var self = this;
|
|
||||||
|
|
||||||
var allowedQueryParams = REQUEST_QUERY_PARAMS_WHITELIST;
|
|
||||||
if (Array.isArray(req.context.allowedQueryParams)) {
|
|
||||||
allowedQueryParams = allowedQueryParams.concat(req.context.allowedQueryParams);
|
|
||||||
}
|
|
||||||
req.query = _.pick(req.query, allowedQueryParams);
|
|
||||||
req.params = _.extend({}, req.params); // shuffle things as request is a strange array/object
|
|
||||||
|
|
||||||
var user = req.context.user;
|
|
||||||
|
|
||||||
if ( req.params.token ) {
|
|
||||||
// Token might match the following patterns:
|
|
||||||
// - {user}@{tpl_id}@{token}:{cache_buster}
|
|
||||||
var tksplit = req.params.token.split(':');
|
|
||||||
req.params.token = tksplit[0];
|
|
||||||
if ( tksplit.length > 1 ) {
|
|
||||||
req.params.cache_buster= tksplit[1];
|
|
||||||
}
|
|
||||||
tksplit = req.params.token.split('@');
|
|
||||||
if ( tksplit.length > 1 ) {
|
|
||||||
req.params.signer = tksplit.shift();
|
|
||||||
if ( ! req.params.signer ) {
|
|
||||||
req.params.signer = user;
|
|
||||||
}
|
|
||||||
else if ( req.params.signer !== user ) {
|
|
||||||
var err = new Error(
|
|
||||||
'Cannot use map signature of user "' + req.params.signer + '" on db of user "' + user + '"'
|
|
||||||
);
|
|
||||||
err.http_status = 403;
|
|
||||||
req.profiler.done('req2params');
|
|
||||||
callback(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if ( tksplit.length > 1 ) {
|
|
||||||
/*var template_hash = */tksplit.shift(); // unused
|
|
||||||
}
|
|
||||||
req.params.token = tksplit.shift();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// bring all query values onto req.params object
|
|
||||||
_.extend(req.params, req.query);
|
|
||||||
|
|
||||||
req.profiler.done('req2params.setup');
|
|
||||||
|
|
||||||
step(
|
|
||||||
function getPrivacy(){
|
|
||||||
self.authApi.authorize(req, this);
|
|
||||||
},
|
|
||||||
function validateAuthorization(err, authorized) {
|
|
||||||
req.profiler.done('authorize');
|
|
||||||
assert.ifError(err);
|
|
||||||
if(!authorized) {
|
|
||||||
err = new Error("Sorry, you are unauthorized (permission denied)");
|
|
||||||
err.http_status = 403;
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
function getDatabase(err){
|
|
||||||
assert.ifError(err);
|
|
||||||
self.pgConnection.setDBConn(user, req.params, this);
|
|
||||||
},
|
|
||||||
function finishSetup(err) {
|
|
||||||
if ( err ) {
|
|
||||||
req.profiler.done('req2params');
|
|
||||||
return callback(err, req);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add default database connection parameters
|
|
||||||
// if none given
|
|
||||||
_.defaults(req.params, {
|
|
||||||
dbuser: global.environment.postgres.user,
|
|
||||||
dbpassword: global.environment.postgres.password,
|
|
||||||
dbhost: global.environment.postgres.host,
|
|
||||||
dbport: global.environment.postgres.port
|
|
||||||
});
|
|
||||||
|
|
||||||
req.profiler.done('req2params');
|
|
||||||
callback(null, req);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
|
||||||
// jshint maxcomplexity:6
|
|
||||||
|
|
||||||
// jshint maxcomplexity:9
|
// jshint maxcomplexity:9
|
||||||
BaseController.prototype.send = function(req, res, body, status, headers) {
|
BaseController.prototype.send = function(req, res, body, status, headers) {
|
||||||
if (req.params.dbhost) {
|
|
||||||
res.set('X-Served-By-DB-Host', req.params.dbhost);
|
|
||||||
}
|
|
||||||
|
|
||||||
res.set('X-Tiler-Profiler', req.profiler.toJSONString());
|
res.set('X-Tiler-Profiler', req.profiler.toJSONString());
|
||||||
|
|
||||||
if (headers) {
|
if (headers) {
|
||||||
@ -152,150 +34,3 @@ BaseController.prototype.send = function(req, res, body, status, headers) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
// jshint maxcomplexity:6
|
// jshint maxcomplexity:6
|
||||||
|
|
||||||
BaseController.prototype.sendError = function(req, res, err, label) {
|
|
||||||
var allErrors = Array.isArray(err) ? err : [err];
|
|
||||||
|
|
||||||
allErrors = populateTimeoutErrors(allErrors);
|
|
||||||
|
|
||||||
label = label || 'UNKNOWN';
|
|
||||||
err = allErrors[0] || new Error(label);
|
|
||||||
allErrors[0] = err;
|
|
||||||
|
|
||||||
var statusCode = findStatusCode(err);
|
|
||||||
|
|
||||||
if (err.message === 'Tile does not exist' && req.params.format === 'mvt') {
|
|
||||||
statusCode = 204;
|
|
||||||
}
|
|
||||||
|
|
||||||
debug('[%s ERROR] -- %d: %s, %s', label, statusCode, err, err.stack);
|
|
||||||
|
|
||||||
// If a callback was requested, force status to 200
|
|
||||||
if (req.query && req.query.callback) {
|
|
||||||
statusCode = 200;
|
|
||||||
}
|
|
||||||
|
|
||||||
var errorResponseBody = {
|
|
||||||
errors: allErrors.map(errorMessage),
|
|
||||||
errors_with_context: allErrors.map(errorMessageWithContext)
|
|
||||||
};
|
|
||||||
|
|
||||||
this.send(req, res, errorResponseBody, statusCode);
|
|
||||||
};
|
|
||||||
|
|
||||||
function stripConnectionInfo(message) {
|
|
||||||
// Strip connection info, if any
|
|
||||||
return message
|
|
||||||
// See https://github.com/CartoDB/Windshaft/issues/173
|
|
||||||
.replace(/Connection string: '[^']*'\n\s/im, '')
|
|
||||||
// See https://travis-ci.org/CartoDB/Windshaft/jobs/20703062#L1644
|
|
||||||
.replace(/is the server.*encountered/im, 'encountered');
|
|
||||||
}
|
|
||||||
|
|
||||||
var ERROR_INFO_TO_EXPOSE = {
|
|
||||||
message: true,
|
|
||||||
layer: true,
|
|
||||||
type: true,
|
|
||||||
analysis: true,
|
|
||||||
subtype: true
|
|
||||||
};
|
|
||||||
|
|
||||||
function shouldBeExposed (prop) {
|
|
||||||
return !!ERROR_INFO_TO_EXPOSE[prop];
|
|
||||||
}
|
|
||||||
|
|
||||||
function errorMessage(err) {
|
|
||||||
// See https://github.com/Vizzuality/Windshaft-cartodb/issues/68
|
|
||||||
var message = (_.isString(err) ? err : err.message) || 'Unknown error';
|
|
||||||
|
|
||||||
return stripConnectionInfo(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
function errorMessageWithContext(err) {
|
|
||||||
// See https://github.com/Vizzuality/Windshaft-cartodb/issues/68
|
|
||||||
var message = (_.isString(err) ? err : err.message) || 'Unknown error';
|
|
||||||
|
|
||||||
var error = {
|
|
||||||
type: err.type || 'unknown',
|
|
||||||
message: stripConnectionInfo(message),
|
|
||||||
};
|
|
||||||
|
|
||||||
for (var prop in err) {
|
|
||||||
// type & message are properties from Error's prototype and will be skipped
|
|
||||||
if (err.hasOwnProperty(prop) && shouldBeExposed(prop)) {
|
|
||||||
error[prop] = err[prop];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
module.exports.errorMessage = errorMessage;
|
|
||||||
|
|
||||||
function findStatusCode(err) {
|
|
||||||
var statusCode;
|
|
||||||
if ( err.http_status ) {
|
|
||||||
statusCode = err.http_status;
|
|
||||||
} else {
|
|
||||||
statusCode = statusFromErrorMessage('' + err);
|
|
||||||
}
|
|
||||||
return statusCode;
|
|
||||||
}
|
|
||||||
module.exports.findStatusCode = findStatusCode;
|
|
||||||
|
|
||||||
function statusFromErrorMessage(errMsg) {
|
|
||||||
// Find an appropriate statusCode based on message
|
|
||||||
// jshint maxcomplexity:7
|
|
||||||
var statusCode = 400;
|
|
||||||
if ( -1 !== errMsg.indexOf('permission denied') ) {
|
|
||||||
statusCode = 403;
|
|
||||||
}
|
|
||||||
else if ( -1 !== errMsg.indexOf('authentication failed') ) {
|
|
||||||
statusCode = 403;
|
|
||||||
}
|
|
||||||
else if (errMsg.match(/Postgis Plugin.*[\s|\n].*column.*does not exist/)) {
|
|
||||||
statusCode = 400;
|
|
||||||
}
|
|
||||||
else if ( -1 !== errMsg.indexOf('does not exist') ) {
|
|
||||||
if ( -1 !== errMsg.indexOf(' role ') ) {
|
|
||||||
statusCode = 403; // role 'xxx' does not exist
|
|
||||||
} else if ( errMsg.match(/function .* does not exist/) ) {
|
|
||||||
statusCode = 400; // invalid SQL (SQL function does not exist)
|
|
||||||
} else {
|
|
||||||
statusCode = 404;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return statusCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isRenderTimeoutError (err) {
|
|
||||||
return err.message === 'Render timed out';
|
|
||||||
}
|
|
||||||
|
|
||||||
function isDatasourceTimeoutError (err) {
|
|
||||||
return err.message && err.message.match(/canceling statement due to statement timeout/i);
|
|
||||||
}
|
|
||||||
|
|
||||||
function isTimeoutError (err) {
|
|
||||||
return isRenderTimeoutError(err) || isDatasourceTimeoutError(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
function populateTimeoutErrors (errors) {
|
|
||||||
return errors.map(function (error) {
|
|
||||||
if (isRenderTimeoutError(error)) {
|
|
||||||
error.subtype = 'render';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isDatasourceTimeoutError(error)) {
|
|
||||||
error.subtype = 'datasource';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isTimeoutError(error)) {
|
|
||||||
error.message = 'You are over platform\'s limits. Please contact us to know more details';
|
|
||||||
error.type = 'limit';
|
|
||||||
error.http_status = 429;
|
|
||||||
}
|
|
||||||
|
|
||||||
return error;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
@ -28,9 +28,9 @@ var QueryTables = require('cartodb-query-tables');
|
|||||||
* @param {AnalysisBackend} analysisBackend
|
* @param {AnalysisBackend} analysisBackend
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
function LayergroupController(authApi, pgConnection, mapStore, tileBackend, previewBackend, attributesBackend,
|
function LayergroupController(prepareContext, pgConnection, mapStore, tileBackend, previewBackend, attributesBackend,
|
||||||
surrogateKeysCache, userLimitsApi, layergroupAffectedTables, analysisBackend) {
|
surrogateKeysCache, userLimitsApi, layergroupAffectedTables, analysisBackend) {
|
||||||
BaseController.call(this, authApi, pgConnection);
|
BaseController.call(this);
|
||||||
|
|
||||||
this.pgConnection = pgConnection;
|
this.pgConnection = pgConnection;
|
||||||
this.mapStore = mapStore;
|
this.mapStore = mapStore;
|
||||||
@ -43,39 +43,65 @@ function LayergroupController(authApi, pgConnection, mapStore, tileBackend, prev
|
|||||||
|
|
||||||
this.dataviewBackend = new DataviewBackend(analysisBackend);
|
this.dataviewBackend = new DataviewBackend(analysisBackend);
|
||||||
this.analysisStatusBackend = new AnalysisStatusBackend();
|
this.analysisStatusBackend = new AnalysisStatusBackend();
|
||||||
|
|
||||||
|
this.prepareContext = prepareContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
util.inherits(LayergroupController, BaseController);
|
util.inherits(LayergroupController, BaseController);
|
||||||
|
|
||||||
module.exports = LayergroupController;
|
module.exports = LayergroupController;
|
||||||
|
|
||||||
|
|
||||||
LayergroupController.prototype.register = function(app) {
|
LayergroupController.prototype.register = function(app) {
|
||||||
app.get(app.base_url_mapconfig +
|
app.get(
|
||||||
'/:token/:z/:x/:y@:scale_factor?x.:format', cors(), userMiddleware,
|
app.base_url_mapconfig + '/:token/:z/:x/:y@:scale_factor?x.:format',
|
||||||
this.tile.bind(this));
|
cors(),
|
||||||
|
userMiddleware,
|
||||||
|
this.prepareContext,
|
||||||
|
this.tile.bind(this)
|
||||||
|
);
|
||||||
|
|
||||||
app.get(app.base_url_mapconfig +
|
app.get(
|
||||||
'/:token/:z/:x/:y.:format', cors(), userMiddleware,
|
app.base_url_mapconfig + '/:token/:z/:x/:y.:format',
|
||||||
this.tile.bind(this));
|
cors(),
|
||||||
|
userMiddleware,
|
||||||
|
this.prepareContext,
|
||||||
|
this.tile.bind(this)
|
||||||
|
);
|
||||||
|
|
||||||
app.get(app.base_url_mapconfig +
|
app.get(
|
||||||
'/:token/:layer/:z/:x/:y.(:format)', cors(), userMiddleware,
|
app.base_url_mapconfig + '/:token/:layer/:z/:x/:y.(:format)',
|
||||||
this.layer.bind(this));
|
cors(),
|
||||||
|
userMiddleware,
|
||||||
|
validateLayerRouteMiddleware,
|
||||||
|
this.prepareContext,
|
||||||
|
this.layer.bind(this)
|
||||||
|
);
|
||||||
|
|
||||||
app.get(app.base_url_mapconfig +
|
app.get(
|
||||||
'/:token/:layer/attributes/:fid', cors(), userMiddleware,
|
app.base_url_mapconfig + '/:token/:layer/attributes/:fid',
|
||||||
this.attributes.bind(this));
|
cors(),
|
||||||
|
userMiddleware,
|
||||||
|
this.prepareContext,
|
||||||
|
this.attributes.bind(this)
|
||||||
|
);
|
||||||
|
|
||||||
app.get(app.base_url_mapconfig +
|
app.get(
|
||||||
'/static/center/:token/:z/:lat/:lng/:width/:height.:format',
|
app.base_url_mapconfig + '/static/center/:token/:z/:lat/:lng/:width/:height.:format',
|
||||||
cors(), userMiddleware, allowQueryParams(['layer']),
|
cors(),
|
||||||
this.center.bind(this));
|
userMiddleware,
|
||||||
|
allowQueryParams(['layer']),
|
||||||
|
this.prepareContext,
|
||||||
|
this.center.bind(this)
|
||||||
|
);
|
||||||
|
|
||||||
app.get(app.base_url_mapconfig +
|
app.get(
|
||||||
'/static/bbox/:token/:west,:south,:east,:north/:width/:height.:format',
|
app.base_url_mapconfig + '/static/bbox/:token/:west,:south,:east,:north/:width/:height.:format',
|
||||||
cors(), userMiddleware, allowQueryParams(['layer']),
|
cors(),
|
||||||
this.bbox.bind(this));
|
userMiddleware,
|
||||||
|
allowQueryParams(['layer']),
|
||||||
|
this.prepareContext,
|
||||||
|
this.bbox.bind(this)
|
||||||
|
);
|
||||||
|
|
||||||
// Undocumented/non-supported API endpoint methods.
|
// Undocumented/non-supported API endpoint methods.
|
||||||
// Use at your own peril.
|
// Use at your own peril.
|
||||||
@ -98,6 +124,7 @@ LayergroupController.prototype.register = function(app) {
|
|||||||
cors(),
|
cors(),
|
||||||
userMiddleware,
|
userMiddleware,
|
||||||
allowQueryParams(allowedDataviewQueryParams),
|
allowQueryParams(allowedDataviewQueryParams),
|
||||||
|
this.prepareContext,
|
||||||
this.dataview.bind(this)
|
this.dataview.bind(this)
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -106,6 +133,7 @@ LayergroupController.prototype.register = function(app) {
|
|||||||
cors(),
|
cors(),
|
||||||
userMiddleware,
|
userMiddleware,
|
||||||
allowQueryParams(allowedDataviewQueryParams),
|
allowQueryParams(allowedDataviewQueryParams),
|
||||||
|
this.prepareContext,
|
||||||
this.dataview.bind(this)
|
this.dataview.bind(this)
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -114,6 +142,7 @@ LayergroupController.prototype.register = function(app) {
|
|||||||
cors(),
|
cors(),
|
||||||
userMiddleware,
|
userMiddleware,
|
||||||
allowQueryParams(allowedDataviewQueryParams),
|
allowQueryParams(allowedDataviewQueryParams),
|
||||||
|
this.prepareContext,
|
||||||
this.dataviewSearch.bind(this)
|
this.dataviewSearch.bind(this)
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -122,30 +151,32 @@ LayergroupController.prototype.register = function(app) {
|
|||||||
cors(),
|
cors(),
|
||||||
userMiddleware,
|
userMiddleware,
|
||||||
allowQueryParams(allowedDataviewQueryParams),
|
allowQueryParams(allowedDataviewQueryParams),
|
||||||
|
this.prepareContext,
|
||||||
this.dataviewSearch.bind(this)
|
this.dataviewSearch.bind(this)
|
||||||
);
|
);
|
||||||
|
|
||||||
app.get(app.base_url_mapconfig +
|
app.get(
|
||||||
'/:token/analysis/node/:nodeId', cors(), userMiddleware,
|
app.base_url_mapconfig + '/:token/analysis/node/:nodeId',
|
||||||
this.analysisNodeStatus.bind(this));
|
cors(),
|
||||||
|
userMiddleware,
|
||||||
|
this.prepareContext,
|
||||||
|
this.analysisNodeStatus.bind(this)
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
LayergroupController.prototype.analysisNodeStatus = function(req, res) {
|
LayergroupController.prototype.analysisNodeStatus = function(req, res, next) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
step(
|
step(
|
||||||
function setupParams() {
|
function retrieveNodeStatus() {
|
||||||
self.req2params(req, this);
|
self.analysisStatusBackend.getNodeStatus(res.locals, this);
|
||||||
},
|
|
||||||
function retrieveNodeStatus(err) {
|
|
||||||
assert.ifError(err);
|
|
||||||
self.analysisStatusBackend.getNodeStatus(req.params, this);
|
|
||||||
},
|
},
|
||||||
function finish(err, nodeStatus, stats) {
|
function finish(err, nodeStatus, stats) {
|
||||||
req.profiler.add(stats || {});
|
req.profiler.add(stats || {});
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
self.sendError(req, res, err, 'GET NODE STATUS');
|
err.label = 'GET NODE STATUS';
|
||||||
|
next(err);
|
||||||
} else {
|
} else {
|
||||||
self.sendResponse(req, res, nodeStatus, 200, {
|
self.sendResponse(req, res, nodeStatus, 200, {
|
||||||
'Cache-Control': 'public,max-age=5',
|
'Cache-Control': 'public,max-age=5',
|
||||||
@ -156,54 +187,50 @@ LayergroupController.prototype.analysisNodeStatus = function(req, res) {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
LayergroupController.prototype.dataview = function(req, res) {
|
LayergroupController.prototype.dataview = function(req, res, next) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
step(
|
step(
|
||||||
function setupParams() {
|
function retrieveDataview() {
|
||||||
self.req2params(req, this);
|
|
||||||
},
|
|
||||||
function retrieveDataview(err) {
|
|
||||||
assert.ifError(err);
|
|
||||||
|
|
||||||
var mapConfigProvider = new MapStoreMapConfigProvider(
|
var mapConfigProvider = new MapStoreMapConfigProvider(
|
||||||
self.mapStore, req.context.user, self.userLimitsApi, req.params
|
self.mapStore, res.locals.user, self.userLimitsApi, res.locals
|
||||||
|
);
|
||||||
|
self.dataviewBackend.getDataview(
|
||||||
|
mapConfigProvider,
|
||||||
|
res.locals.user,
|
||||||
|
res.locals,
|
||||||
|
this
|
||||||
);
|
);
|
||||||
self.dataviewBackend.getDataview(mapConfigProvider, req.context.user, req.params, this);
|
|
||||||
},
|
},
|
||||||
function finish(err, dataview, stats) {
|
function finish(err, dataview, stats) {
|
||||||
req.profiler.add(stats || {});
|
req.profiler.add(stats || {});
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
self.sendError(req, res, err, 'GET DATAVIEW');
|
err.label = 'GET DATAVIEW';
|
||||||
|
next(err);
|
||||||
} else {
|
} else {
|
||||||
self.sendResponse(req, res, dataview, 200);
|
self.sendResponse(req, res, dataview, 200);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
LayergroupController.prototype.dataviewSearch = function(req, res) {
|
LayergroupController.prototype.dataviewSearch = function(req, res, next) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
step(
|
step(
|
||||||
function setupParams() {
|
function searchDataview() {
|
||||||
self.req2params(req, this);
|
|
||||||
},
|
|
||||||
function searchDataview(err) {
|
|
||||||
assert.ifError(err);
|
|
||||||
|
|
||||||
var mapConfigProvider = new MapStoreMapConfigProvider(
|
var mapConfigProvider = new MapStoreMapConfigProvider(
|
||||||
self.mapStore, req.context.user, self.userLimitsApi, req.params
|
self.mapStore, res.locals.user, self.userLimitsApi, res.locals
|
||||||
);
|
);
|
||||||
self.dataviewBackend.search(mapConfigProvider, req.context.user, req.params, this);
|
self.dataviewBackend.search(mapConfigProvider, res.locals.user, req.params.dataviewName, res.locals, this);
|
||||||
},
|
},
|
||||||
function finish(err, searchResult, stats) {
|
function finish(err, searchResult, stats) {
|
||||||
req.profiler.add(stats || {});
|
req.profiler.add(stats || {});
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
self.sendError(req, res, err, 'GET DATAVIEW SEARCH');
|
err.label = 'GET DATAVIEW SEARCH';
|
||||||
|
next(err);
|
||||||
} else {
|
} else {
|
||||||
self.sendResponse(req, res, searchResult, 200);
|
self.sendResponse(req, res, searchResult, 200);
|
||||||
}
|
}
|
||||||
@ -212,28 +239,24 @@ LayergroupController.prototype.dataviewSearch = function(req, res) {
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
LayergroupController.prototype.attributes = function(req, res) {
|
LayergroupController.prototype.attributes = function(req, res, next) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
req.profiler.start('windshaft.maplayer_attribute');
|
req.profiler.start('windshaft.maplayer_attribute');
|
||||||
|
|
||||||
step(
|
step(
|
||||||
function setupParams() {
|
function retrieveFeatureAttributes() {
|
||||||
self.req2params(req, this);
|
|
||||||
},
|
|
||||||
function retrieveFeatureAttributes(err) {
|
|
||||||
assert.ifError(err);
|
|
||||||
|
|
||||||
var mapConfigProvider = new MapStoreMapConfigProvider(
|
var mapConfigProvider = new MapStoreMapConfigProvider(
|
||||||
self.mapStore, req.context.user, self.userLimitsApi, req.params
|
self.mapStore, res.locals.user, self.userLimitsApi, res.locals
|
||||||
);
|
);
|
||||||
self.attributesBackend.getFeatureAttributes(mapConfigProvider, req.params, false, this);
|
self.attributesBackend.getFeatureAttributes(mapConfigProvider, res.locals, false, this);
|
||||||
},
|
},
|
||||||
function finish(err, tile, stats) {
|
function finish(err, tile, stats) {
|
||||||
req.profiler.add(stats || {});
|
req.profiler.add(stats || {});
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
self.sendError(req, res, err, 'GET ATTRIBUTES');
|
err.label = 'GET ATTRIBUTES';
|
||||||
|
next(err);
|
||||||
} else {
|
} else {
|
||||||
self.sendResponse(req, res, tile, 200);
|
self.sendResponse(req, res, tile, 200);
|
||||||
}
|
}
|
||||||
@ -243,37 +266,30 @@ LayergroupController.prototype.attributes = function(req, res) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Gets a tile for a given token and set of tile ZXY coords. (OSM style)
|
// Gets a tile for a given token and set of tile ZXY coords. (OSM style)
|
||||||
LayergroupController.prototype.tile = function(req, res) {
|
LayergroupController.prototype.tile = function(req, res, next) {
|
||||||
req.profiler.start('windshaft.map_tile');
|
req.profiler.start('windshaft.map_tile');
|
||||||
this.tileOrLayer(req, res);
|
this.tileOrLayer(req, res, next);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Gets a tile for a given token, layer set of tile ZXY coords. (OSM style)
|
// Gets a tile for a given token, layer set of tile ZXY coords. (OSM style)
|
||||||
LayergroupController.prototype.layer = function(req, res, next) {
|
LayergroupController.prototype.layer = function(req, res, next) {
|
||||||
if (req.params.token === 'static') {
|
|
||||||
return next();
|
|
||||||
}
|
|
||||||
req.profiler.start('windshaft.maplayer_tile');
|
req.profiler.start('windshaft.maplayer_tile');
|
||||||
this.tileOrLayer(req, res);
|
this.tileOrLayer(req, res, next);
|
||||||
};
|
};
|
||||||
|
|
||||||
LayergroupController.prototype.tileOrLayer = function (req, res) {
|
LayergroupController.prototype.tileOrLayer = function (req, res, next) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
step(
|
step(
|
||||||
function mapController$prepareParams() {
|
function mapController$getTileOrGrid() {
|
||||||
self.req2params(req, this);
|
|
||||||
},
|
|
||||||
function mapController$getTileOrGrid(err) {
|
|
||||||
assert.ifError(err);
|
|
||||||
self.tileBackend.getTile(
|
self.tileBackend.getTile(
|
||||||
new MapStoreMapConfigProvider(self.mapStore, req.context.user, self.userLimitsApi, req.params),
|
new MapStoreMapConfigProvider(self.mapStore, res.locals.user, self.userLimitsApi, res.locals),
|
||||||
req.params, this
|
req.params, this
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
function mapController$finalize(err, tile, headers, stats) {
|
function mapController$finalize(err, tile, headers, stats) {
|
||||||
req.profiler.add(stats);
|
req.profiler.add(stats);
|
||||||
self.finalizeGetTileOrGrid(err, req, res, tile, headers);
|
self.finalizeGetTileOrGrid(err, req, res, tile, headers, next);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -284,7 +300,7 @@ function getStatusCode(tile, format){
|
|||||||
|
|
||||||
// This function is meant for being called as the very last
|
// This function is meant for being called as the very last
|
||||||
// step by all endpoints serving tiles or grids
|
// step by all endpoints serving tiles or grids
|
||||||
LayergroupController.prototype.finalizeGetTileOrGrid = function(err, req, res, tile, headers) {
|
LayergroupController.prototype.finalizeGetTileOrGrid = function(err, req, res, tile, headers, next) {
|
||||||
var supportedFormats = {
|
var supportedFormats = {
|
||||||
grid_json: true,
|
grid_json: true,
|
||||||
json_torque: true,
|
json_torque: true,
|
||||||
@ -313,7 +329,9 @@ LayergroupController.prototype.finalizeGetTileOrGrid = function(err, req, res, t
|
|||||||
}
|
}
|
||||||
err.message = errMsg;
|
err.message = errMsg;
|
||||||
|
|
||||||
this.sendError(req, res, err, 'TILE RENDER');
|
err.label = 'TILE RENDER';
|
||||||
|
next(err);
|
||||||
|
|
||||||
global.statsClient.increment('windshaft.tiles.error');
|
global.statsClient.increment('windshaft.tiles.error');
|
||||||
global.statsClient.increment('windshaft.tiles.' + formatStat + '.error');
|
global.statsClient.increment('windshaft.tiles.' + formatStat + '.error');
|
||||||
} else {
|
} else {
|
||||||
@ -323,42 +341,38 @@ LayergroupController.prototype.finalizeGetTileOrGrid = function(err, req, res, t
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
LayergroupController.prototype.bbox = function(req, res) {
|
LayergroupController.prototype.bbox = function(req, res, next) {
|
||||||
this.staticMap(req, res, +req.params.width, +req.params.height, {
|
this.staticMap(req, res, +req.params.width, +req.params.height, {
|
||||||
west: +req.params.west,
|
west: +req.params.west,
|
||||||
north: +req.params.north,
|
north: +req.params.north,
|
||||||
east: +req.params.east,
|
east: +req.params.east,
|
||||||
south: +req.params.south
|
south: +req.params.south
|
||||||
});
|
}, null, next);
|
||||||
};
|
};
|
||||||
|
|
||||||
LayergroupController.prototype.center = function(req, res) {
|
LayergroupController.prototype.center = function(req, res, next) {
|
||||||
this.staticMap(req, res, +req.params.width, +req.params.height, +req.params.z, {
|
this.staticMap(req, res, +req.params.width, +req.params.height, +req.params.z, {
|
||||||
lng: +req.params.lng,
|
lng: +req.params.lng,
|
||||||
lat: +req.params.lat
|
lat: +req.params.lat
|
||||||
});
|
}, next);
|
||||||
};
|
};
|
||||||
|
|
||||||
LayergroupController.prototype.staticMap = function(req, res, width, height, zoom /* bounds */, center) {
|
LayergroupController.prototype.staticMap = function(req, res, width, height, zoom /* bounds */, center, next) {
|
||||||
var format = req.params.format === 'jpg' ? 'jpeg' : 'png';
|
var format = req.params.format === 'jpg' ? 'jpeg' : 'png';
|
||||||
req.params.layer = 'all';
|
req.params.format = req.params.format || 'png';
|
||||||
req.params.format = 'png';
|
res.locals.layer = res.locals.layer || 'all';
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
step(
|
step(
|
||||||
function reqParams() {
|
function getImage() {
|
||||||
self.req2params(req, this);
|
|
||||||
},
|
|
||||||
function getImage(err) {
|
|
||||||
assert.ifError(err);
|
|
||||||
if (center) {
|
if (center) {
|
||||||
self.previewBackend.getImage(
|
self.previewBackend.getImage(
|
||||||
new MapStoreMapConfigProvider(self.mapStore, req.context.user, self.userLimitsApi, req.params),
|
new MapStoreMapConfigProvider(self.mapStore, res.locals.user, self.userLimitsApi, res.locals),
|
||||||
format, width, height, zoom, center, this);
|
format, width, height, zoom, center, this);
|
||||||
} else {
|
} else {
|
||||||
self.previewBackend.getImage(
|
self.previewBackend.getImage(
|
||||||
new MapStoreMapConfigProvider(self.mapStore, req.context.user, self.userLimitsApi, req.params),
|
new MapStoreMapConfigProvider(self.mapStore, res.locals.user, self.userLimitsApi, res.locals),
|
||||||
format, width, height, zoom /* bounds */, this);
|
format, width, height, zoom /* bounds */, this);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -367,7 +381,8 @@ LayergroupController.prototype.staticMap = function(req, res, width, height, zoo
|
|||||||
req.profiler.add(stats || {});
|
req.profiler.add(stats || {});
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
self.sendError(req, res, err, 'STATIC_MAP');
|
err.label = 'STATIC_MAP';
|
||||||
|
next(err);
|
||||||
} else {
|
} else {
|
||||||
res.set('Content-Type', headers['Content-Type'] || 'image/' + format);
|
res.set('Content-Type', headers['Content-Type'] || 'image/' + format);
|
||||||
self.sendResponse(req, res, image, 200);
|
self.sendResponse(req, res, image, 200);
|
||||||
@ -385,18 +400,18 @@ LayergroupController.prototype.sendResponse = function(req, res, body, status, h
|
|||||||
|
|
||||||
// Set Last-Modified header
|
// Set Last-Modified header
|
||||||
var lastUpdated;
|
var lastUpdated;
|
||||||
if (req.params.cache_buster) {
|
if (res.locals.cache_buster) {
|
||||||
// Assuming cache_buster is a timestamp
|
// Assuming cache_buster is a timestamp
|
||||||
lastUpdated = new Date(parseInt(req.params.cache_buster));
|
lastUpdated = new Date(parseInt(res.locals.cache_buster));
|
||||||
} else {
|
} else {
|
||||||
lastUpdated = new Date();
|
lastUpdated = new Date();
|
||||||
}
|
}
|
||||||
res.set('Last-Modified', lastUpdated.toUTCString());
|
res.set('Last-Modified', lastUpdated.toUTCString());
|
||||||
|
|
||||||
var dbName = req.params.dbname;
|
var dbName = res.locals.dbname;
|
||||||
step(
|
step(
|
||||||
function getAffectedTables() {
|
function getAffectedTables() {
|
||||||
self.getAffectedTables(req.context.user, dbName, req.params.token, this);
|
self.getAffectedTables(res.locals.user, dbName, res.locals.token, this);
|
||||||
},
|
},
|
||||||
function sendResponse(err, affectedTables) {
|
function sendResponse(err, affectedTables) {
|
||||||
req.profiler.done('affectedTables');
|
req.profiler.done('affectedTables');
|
||||||
@ -472,3 +487,12 @@ LayergroupController.prototype.getAffectedTables = function(user, dbName, layerg
|
|||||||
callback
|
callback
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
function validateLayerRouteMiddleware(req, res, next) {
|
||||||
|
if (req.params.token === 'static') {
|
||||||
|
return next('route');
|
||||||
|
}
|
||||||
|
|
||||||
|
next();
|
||||||
|
}
|
@ -20,7 +20,6 @@ var NamedMapsCacheEntry = require('../cache/model/named_maps_entry');
|
|||||||
var NamedMapMapConfigProvider = require('../models/mapconfig/provider/named-map-provider');
|
var NamedMapMapConfigProvider = require('../models/mapconfig/provider/named-map-provider');
|
||||||
var CreateLayergroupMapConfigProvider = require('../models/mapconfig/provider/create-layergroup-provider');
|
var CreateLayergroupMapConfigProvider = require('../models/mapconfig/provider/create-layergroup-provider');
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {AuthApi} authApi
|
* @param {AuthApi} authApi
|
||||||
* @param {PgConnection} pgConnection
|
* @param {PgConnection} pgConnection
|
||||||
@ -34,11 +33,11 @@ var CreateLayergroupMapConfigProvider = require('../models/mapconfig/provider/cr
|
|||||||
* @param {StatsBackend} statsBackend
|
* @param {StatsBackend} statsBackend
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
function MapController(authApi, pgConnection, templateMaps, mapBackend, metadataBackend,
|
function MapController(prepareContext, pgConnection, templateMaps, mapBackend, metadataBackend,
|
||||||
surrogateKeysCache, userLimitsApi, layergroupAffectedTables, mapConfigAdapter,
|
surrogateKeysCache, userLimitsApi, layergroupAffectedTables, mapConfigAdapter,
|
||||||
statsBackend) {
|
statsBackend) {
|
||||||
|
|
||||||
BaseController.call(this, authApi, pgConnection);
|
BaseController.call(this);
|
||||||
|
|
||||||
this.pgConnection = pgConnection;
|
this.pgConnection = pgConnection;
|
||||||
this.templateMaps = templateMaps;
|
this.templateMaps = templateMaps;
|
||||||
@ -52,6 +51,7 @@ function MapController(authApi, pgConnection, templateMaps, mapBackend, metadata
|
|||||||
this.resourceLocator = new ResourceLocator(global.environment);
|
this.resourceLocator = new ResourceLocator(global.environment);
|
||||||
|
|
||||||
this.statsBackend = statsBackend;
|
this.statsBackend = statsBackend;
|
||||||
|
this.prepareContext = prepareContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
util.inherits(MapController, BaseController);
|
util.inherits(MapController, BaseController);
|
||||||
@ -60,38 +60,60 @@ module.exports = MapController;
|
|||||||
|
|
||||||
|
|
||||||
MapController.prototype.register = function(app) {
|
MapController.prototype.register = function(app) {
|
||||||
app.get(app.base_url_mapconfig, cors(), userMiddleware, this.createGet.bind(this));
|
app.get(
|
||||||
app.post(app.base_url_mapconfig, cors(), userMiddleware, this.createPost.bind(this));
|
app.base_url_mapconfig,
|
||||||
app.get(app.base_url_templated + '/:template_id/jsonp', cors(), userMiddleware, this.jsonp.bind(this));
|
cors(),
|
||||||
app.post(app.base_url_templated + '/:template_id', cors(), userMiddleware, this.instantiate.bind(this));
|
userMiddleware,
|
||||||
|
this.prepareContext,
|
||||||
|
this.createGet.bind(this)
|
||||||
|
);
|
||||||
|
app.post(
|
||||||
|
app.base_url_mapconfig,
|
||||||
|
cors(),
|
||||||
|
userMiddleware,
|
||||||
|
this.prepareContext,
|
||||||
|
this.createPost.bind(this)
|
||||||
|
);
|
||||||
|
app.get(
|
||||||
|
app.base_url_templated + '/:template_id/jsonp',
|
||||||
|
cors(),
|
||||||
|
userMiddleware,
|
||||||
|
this.prepareContext,
|
||||||
|
this.jsonp.bind(this)
|
||||||
|
);
|
||||||
|
app.post(
|
||||||
|
app.base_url_templated + '/:template_id',
|
||||||
|
cors(),
|
||||||
|
userMiddleware,
|
||||||
|
this.prepareContext,
|
||||||
|
this.instantiate.bind(this)
|
||||||
|
);
|
||||||
app.options(app.base_url_mapconfig, cors('Content-Type'));
|
app.options(app.base_url_mapconfig, cors('Content-Type'));
|
||||||
};
|
};
|
||||||
|
|
||||||
MapController.prototype.createGet = function(req, res){
|
MapController.prototype.createGet = function(req, res, next){
|
||||||
req.profiler.start('windshaft.createmap_get');
|
req.profiler.start('windshaft.createmap_get');
|
||||||
|
|
||||||
this.create(req, res, function createGet$prepareConfig(err, req) {
|
this.create(req, res, function createGet$prepareConfig(req, config) {
|
||||||
assert.ifError(err);
|
if ( ! config ) {
|
||||||
if ( ! req.params.config ) {
|
|
||||||
throw new Error('layergroup GET needs a "config" parameter');
|
throw new Error('layergroup GET needs a "config" parameter');
|
||||||
}
|
}
|
||||||
return JSON.parse(req.params.config);
|
return JSON.parse(config);
|
||||||
});
|
}, next);
|
||||||
};
|
};
|
||||||
|
|
||||||
MapController.prototype.createPost = function(req, res) {
|
MapController.prototype.createPost = function(req, res, next) {
|
||||||
req.profiler.start('windshaft.createmap_post');
|
req.profiler.start('windshaft.createmap_post');
|
||||||
|
|
||||||
this.create(req, res, function createPost$prepareConfig(err, req) {
|
this.create(req, res, function createPost$prepareConfig(req) {
|
||||||
assert.ifError(err);
|
|
||||||
if (!req.is('application/json')) {
|
if (!req.is('application/json')) {
|
||||||
throw new Error('layergroup POST data must be of type application/json');
|
throw new Error('layergroup POST data must be of type application/json');
|
||||||
}
|
}
|
||||||
return req.body;
|
return req.body;
|
||||||
});
|
}, next);
|
||||||
};
|
};
|
||||||
|
|
||||||
MapController.prototype.instantiate = function(req, res) {
|
MapController.prototype.instantiate = function(req, res, next) {
|
||||||
req.profiler.start('windshaft-cartodb.instance_template_post');
|
req.profiler.start('windshaft-cartodb.instance_template_post');
|
||||||
|
|
||||||
this.instantiateTemplate(req, res, function prepareTemplateParams(callback) {
|
this.instantiateTemplate(req, res, function prepareTemplateParams(callback) {
|
||||||
@ -99,10 +121,10 @@ MapController.prototype.instantiate = function(req, res) {
|
|||||||
return callback(new Error('Template POST data must be of type application/json'));
|
return callback(new Error('Template POST data must be of type application/json'));
|
||||||
}
|
}
|
||||||
return callback(null, req.body);
|
return callback(null, req.body);
|
||||||
});
|
}, next);
|
||||||
};
|
};
|
||||||
|
|
||||||
MapController.prototype.jsonp = function(req, res) {
|
MapController.prototype.jsonp = function(req, res, next) {
|
||||||
req.profiler.start('windshaft-cartodb.instance_template_get');
|
req.profiler.start('windshaft-cartodb.instance_template_get');
|
||||||
|
|
||||||
this.instantiateTemplate(req, res, function prepareJsonTemplateParams(callback) {
|
this.instantiateTemplate(req, res, function prepareJsonTemplateParams(callback) {
|
||||||
@ -121,10 +143,10 @@ MapController.prototype.jsonp = function(req, res) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return callback(err, templateParams);
|
return callback(err, templateParams);
|
||||||
});
|
}, next);
|
||||||
};
|
};
|
||||||
|
|
||||||
MapController.prototype.create = function(req, res, prepareConfigFn) {
|
MapController.prototype.create = function(req, res, prepareConfigFn, next) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
var mapConfig;
|
var mapConfig;
|
||||||
@ -132,35 +154,36 @@ MapController.prototype.create = function(req, res, prepareConfigFn) {
|
|||||||
var context = {};
|
var context = {};
|
||||||
|
|
||||||
step(
|
step(
|
||||||
function setupParams(){
|
function prepareConfig () {
|
||||||
self.req2params(req, this);
|
const requestMapConfig = prepareConfigFn(req, res.locals.config);
|
||||||
|
return requestMapConfig;
|
||||||
},
|
},
|
||||||
prepareConfigFn,
|
|
||||||
function prepareAdapterMapConfig(err, requestMapConfig) {
|
function prepareAdapterMapConfig(err, requestMapConfig) {
|
||||||
assert.ifError(err);
|
assert.ifError(err);
|
||||||
context.analysisConfiguration = {
|
context.analysisConfiguration = {
|
||||||
user: req.context.user,
|
user: res.locals.user,
|
||||||
db: {
|
db: {
|
||||||
host: req.params.dbhost,
|
host: res.locals.dbhost,
|
||||||
port: req.params.dbport,
|
port: res.locals.dbport,
|
||||||
dbname: req.params.dbname,
|
dbname: res.locals.dbname,
|
||||||
user: req.params.dbuser,
|
user: res.locals.dbuser,
|
||||||
pass: req.params.dbpassword
|
pass: res.locals.dbpassword
|
||||||
},
|
},
|
||||||
batch: {
|
batch: {
|
||||||
username: req.context.user,
|
username: res.locals.user,
|
||||||
apiKey: req.params.api_key
|
apiKey: res.locals.api_key
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
self.mapConfigAdapter.getMapConfig(req.context.user, requestMapConfig, req.params, context, this);
|
self.mapConfigAdapter.getMapConfig(res.locals.user, requestMapConfig, res.locals, context, this);
|
||||||
},
|
},
|
||||||
function createLayergroup(err, requestMapConfig) {
|
function createLayergroup(err, requestMapConfig) {
|
||||||
assert.ifError(err);
|
assert.ifError(err);
|
||||||
var datasource = context.datasource || Datasource.EmptyDatasource();
|
var datasource = context.datasource || Datasource.EmptyDatasource();
|
||||||
mapConfig = new MapConfig(requestMapConfig, datasource);
|
mapConfig = new MapConfig(requestMapConfig, datasource);
|
||||||
self.mapBackend.createLayergroup(
|
self.mapBackend.createLayergroup(
|
||||||
mapConfig, req.params,
|
mapConfig,
|
||||||
new CreateLayergroupMapConfigProvider(mapConfig, req.context.user, self.userLimitsApi, req.params),
|
res.locals,
|
||||||
|
new CreateLayergroupMapConfigProvider(mapConfig, res.locals.user, self.userLimitsApi, res.locals),
|
||||||
this
|
this
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -188,11 +211,12 @@ MapController.prototype.create = function(req, res, prepareConfigFn) {
|
|||||||
|
|
||||||
err = error;
|
err = error;
|
||||||
}
|
}
|
||||||
self.sendError(req, res, err, 'ANONYMOUS LAYERGROUP');
|
err.label = 'ANONYMOUS LAYERGROUP';
|
||||||
|
next(err);
|
||||||
} else {
|
} else {
|
||||||
var analysesResults = context.analysesResults || [];
|
var analysesResults = context.analysesResults || [];
|
||||||
self.addDataviewsAndWidgetsUrls(req.context.user, layergroup, mapConfig.obj());
|
self.addDataviewsAndWidgetsUrls(res.locals.user, layergroup, mapConfig.obj());
|
||||||
self.addAnalysesMetadata(req.context.user, layergroup, analysesResults, true);
|
self.addAnalysesMetadata(res.locals.user, layergroup, analysesResults, true);
|
||||||
addContextMetadata(layergroup, mapConfig.obj(), context);
|
addContextMetadata(layergroup, mapConfig.obj(), context);
|
||||||
res.set('X-Layergroup-Id', layergroup.layergroupid);
|
res.set('X-Layergroup-Id', layergroup.layergroupid);
|
||||||
self.send(req, res, layergroup, 200);
|
self.send(req, res, layergroup, 200);
|
||||||
@ -212,17 +236,14 @@ function addContextMetadata(layergroup, mapConfig, context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MapController.prototype.instantiateTemplate = function(req, res, prepareParamsFn) {
|
MapController.prototype.instantiateTemplate = function(req, res, prepareParamsFn, next) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
var cdbuser = req.context.user;
|
var cdbuser = res.locals.user;
|
||||||
|
|
||||||
var mapConfigProvider;
|
var mapConfigProvider;
|
||||||
var mapConfig;
|
var mapConfig;
|
||||||
step(
|
step(
|
||||||
function setupParams(){
|
|
||||||
self.req2params(req, this);
|
|
||||||
},
|
|
||||||
function getTemplateParams() {
|
function getTemplateParams() {
|
||||||
prepareParamsFn(this);
|
prepareParamsFn(this);
|
||||||
},
|
},
|
||||||
@ -237,8 +258,8 @@ MapController.prototype.instantiateTemplate = function(req, res, prepareParamsFn
|
|||||||
cdbuser,
|
cdbuser,
|
||||||
req.params.template_id,
|
req.params.template_id,
|
||||||
templateParams,
|
templateParams,
|
||||||
req.query.auth_token,
|
res.locals.auth_token,
|
||||||
req.params
|
res.locals
|
||||||
);
|
);
|
||||||
mapConfigProvider.getMapConfig(this);
|
mapConfigProvider.getMapConfig(this);
|
||||||
},
|
},
|
||||||
@ -259,7 +280,8 @@ MapController.prototype.instantiateTemplate = function(req, res, prepareParamsFn
|
|||||||
},
|
},
|
||||||
function finishTemplateInstantiation(err, layergroup) {
|
function finishTemplateInstantiation(err, layergroup) {
|
||||||
if (err) {
|
if (err) {
|
||||||
self.sendError(req, res, err, 'NAMED MAP LAYERGROUP');
|
err.label = 'NAMED MAP LAYERGROUP';
|
||||||
|
next(err);
|
||||||
} else {
|
} else {
|
||||||
var templateHash = self.templateMaps.fingerPrint(mapConfigProvider.template).substring(0, 8);
|
var templateHash = self.templateMaps.fingerPrint(mapConfigProvider.template).substring(0, 8);
|
||||||
layergroup.layergroupid = cdbuser + '@' + templateHash + '@' + layergroup.layergroupid;
|
layergroup.layergroupid = cdbuser + '@' + templateHash + '@' + layergroup.layergroupid;
|
||||||
@ -282,7 +304,7 @@ MapController.prototype.afterLayergroupCreate =
|
|||||||
function(req, res, mapconfig, layergroup, analysesResults, callback) {
|
function(req, res, mapconfig, layergroup, analysesResults, callback) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
var username = req.context.user;
|
var username = res.locals.user;
|
||||||
|
|
||||||
var tasksleft = 2; // redis key and affectedTables
|
var tasksleft = 2; // redis key and affectedTables
|
||||||
var errors = [];
|
var errors = [];
|
||||||
@ -323,7 +345,7 @@ function(req, res, mapconfig, layergroup, analysesResults, callback) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
var dbName = req.params.dbname;
|
var dbName = res.locals.dbname;
|
||||||
var layergroupId = layergroup.layergroupid;
|
var layergroupId = layergroup.layergroupid;
|
||||||
var dbConnection;
|
var dbConnection;
|
||||||
|
|
||||||
|
@ -10,9 +10,9 @@ var cors = require('../middleware/cors');
|
|||||||
var userMiddleware = require('../middleware/user');
|
var userMiddleware = require('../middleware/user');
|
||||||
var allowQueryParams = require('../middleware/allow-query-params');
|
var allowQueryParams = require('../middleware/allow-query-params');
|
||||||
|
|
||||||
function NamedMapsController(authApi, pgConnection, namedMapProviderCache, tileBackend, previewBackend,
|
function NamedMapsController(prepareContext, namedMapProviderCache, tileBackend, previewBackend,
|
||||||
surrogateKeysCache, tablesExtentApi, metadataBackend) {
|
surrogateKeysCache, tablesExtentApi, metadataBackend) {
|
||||||
BaseController.call(this, authApi, pgConnection);
|
BaseController.call(this);
|
||||||
|
|
||||||
this.namedMapProviderCache = namedMapProviderCache;
|
this.namedMapProviderCache = namedMapProviderCache;
|
||||||
this.tileBackend = tileBackend;
|
this.tileBackend = tileBackend;
|
||||||
@ -20,6 +20,7 @@ function NamedMapsController(authApi, pgConnection, namedMapProviderCache, tileB
|
|||||||
this.surrogateKeysCache = surrogateKeysCache;
|
this.surrogateKeysCache = surrogateKeysCache;
|
||||||
this.tablesExtentApi = tablesExtentApi;
|
this.tablesExtentApi = tablesExtentApi;
|
||||||
this.metadataBackend = metadataBackend;
|
this.metadataBackend = metadataBackend;
|
||||||
|
this.prepareContext = prepareContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
util.inherits(NamedMapsController, BaseController);
|
util.inherits(NamedMapsController, BaseController);
|
||||||
@ -27,18 +28,26 @@ util.inherits(NamedMapsController, BaseController);
|
|||||||
module.exports = NamedMapsController;
|
module.exports = NamedMapsController;
|
||||||
|
|
||||||
NamedMapsController.prototype.register = function(app) {
|
NamedMapsController.prototype.register = function(app) {
|
||||||
app.get(app.base_url_templated +
|
app.get(
|
||||||
'/:template_id/:layer/:z/:x/:y.(:format)', cors(), userMiddleware,
|
app.base_url_templated + '/:template_id/:layer/:z/:x/:y.(:format)',
|
||||||
this.tile.bind(this));
|
cors(),
|
||||||
|
userMiddleware,
|
||||||
|
this.prepareContext,
|
||||||
|
this.tile.bind(this)
|
||||||
|
);
|
||||||
|
|
||||||
app.get(app.base_url_mapconfig +
|
app.get(
|
||||||
'/static/named/:template_id/:width/:height.:format', cors(), userMiddleware,
|
app.base_url_mapconfig + '/static/named/:template_id/:width/:height.:format',
|
||||||
|
cors(),
|
||||||
|
userMiddleware,
|
||||||
allowQueryParams(['layer', 'zoom', 'lon', 'lat', 'bbox']),
|
allowQueryParams(['layer', 'zoom', 'lon', 'lat', 'bbox']),
|
||||||
this.staticMap.bind(this));
|
this.prepareContext,
|
||||||
|
this.staticMap.bind(this)
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
NamedMapsController.prototype.sendResponse = function(req, res, resource, headers, namedMapProvider) {
|
NamedMapsController.prototype.sendResponse = function(req, res, resource, headers, namedMapProvider) {
|
||||||
this.surrogateKeysCache.tag(res, new NamedMapsCacheEntry(req.context.user, namedMapProvider.getTemplateName()));
|
this.surrogateKeysCache.tag(res, new NamedMapsCacheEntry(res.locals.user, namedMapProvider.getTemplateName()));
|
||||||
res.set('Content-Type', headers['content-type'] || headers['Content-Type'] || 'image/png');
|
res.set('Content-Type', headers['content-type'] || headers['Content-Type'] || 'image/png');
|
||||||
res.set('Cache-Control', 'public,max-age=7200,must-revalidate');
|
res.set('Cache-Control', 'public,max-age=7200,must-revalidate');
|
||||||
|
|
||||||
@ -75,24 +84,20 @@ NamedMapsController.prototype.sendResponse = function(req, res, resource, header
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
NamedMapsController.prototype.tile = function(req, res) {
|
NamedMapsController.prototype.tile = function(req, res, next) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
var cdbUser = req.context.user;
|
var cdbUser = res.locals.user;
|
||||||
|
|
||||||
var namedMapProvider;
|
var namedMapProvider;
|
||||||
step(
|
step(
|
||||||
function reqParams() {
|
function getNamedMapProvider() {
|
||||||
self.req2params(req, this);
|
|
||||||
},
|
|
||||||
function getNamedMapProvider(err) {
|
|
||||||
assert.ifError(err);
|
|
||||||
self.namedMapProviderCache.get(
|
self.namedMapProviderCache.get(
|
||||||
cdbUser,
|
cdbUser,
|
||||||
req.params.template_id,
|
req.params.template_id,
|
||||||
req.query.config,
|
req.query.config,
|
||||||
req.query.auth_token,
|
req.query.auth_token,
|
||||||
req.params,
|
res.locals,
|
||||||
this
|
this
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -104,7 +109,8 @@ NamedMapsController.prototype.tile = function(req, res) {
|
|||||||
function handleImage(err, tile, headers, stats) {
|
function handleImage(err, tile, headers, stats) {
|
||||||
req.profiler.add(stats);
|
req.profiler.add(stats);
|
||||||
if (err) {
|
if (err) {
|
||||||
self.sendError(req, res, err, 'NAMED_MAP_TILE');
|
err.label = 'NAMED_MAP_TILE';
|
||||||
|
next(err);
|
||||||
} else {
|
} else {
|
||||||
self.sendResponse(req, res, tile, headers, namedMapProvider);
|
self.sendResponse(req, res, tile, headers, namedMapProvider);
|
||||||
}
|
}
|
||||||
@ -112,28 +118,24 @@ NamedMapsController.prototype.tile = function(req, res) {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
NamedMapsController.prototype.staticMap = function(req, res) {
|
NamedMapsController.prototype.staticMap = function(req, res, next) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
var cdbUser = req.context.user;
|
var cdbUser = res.locals.user;
|
||||||
|
|
||||||
var format = req.params.format === 'jpg' ? 'jpeg' : 'png';
|
var format = req.params.format === 'jpg' ? 'jpeg' : 'png';
|
||||||
req.params.format = 'png';
|
res.locals.format = req.params.format || 'png';
|
||||||
req.params.layer = 'all';
|
res.locals.layer = res.locals.layer || 'all';
|
||||||
|
|
||||||
var namedMapProvider;
|
var namedMapProvider;
|
||||||
step(
|
step(
|
||||||
function reqParams() {
|
function getNamedMapProvider() {
|
||||||
self.req2params(req, this);
|
|
||||||
},
|
|
||||||
function getNamedMapProvider(err) {
|
|
||||||
assert.ifError(err);
|
|
||||||
self.namedMapProviderCache.get(
|
self.namedMapProviderCache.get(
|
||||||
cdbUser,
|
cdbUser,
|
||||||
req.params.template_id,
|
req.params.template_id,
|
||||||
req.query.config,
|
req.query.config,
|
||||||
req.query.auth_token,
|
req.query.auth_token,
|
||||||
req.params,
|
res.locals,
|
||||||
this
|
this
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -142,11 +144,11 @@ NamedMapsController.prototype.staticMap = function(req, res) {
|
|||||||
|
|
||||||
namedMapProvider = _namedMapProvider;
|
namedMapProvider = _namedMapProvider;
|
||||||
|
|
||||||
self.prepareLayerFilterFromPreviewLayers(cdbUser, req, namedMapProvider, this);
|
self.prepareLayerFilterFromPreviewLayers(cdbUser, req, res.locals, namedMapProvider, this);
|
||||||
},
|
},
|
||||||
function prepareImageOptions(err) {
|
function prepareImageOptions(err) {
|
||||||
assert.ifError(err);
|
assert.ifError(err);
|
||||||
self.getStaticImageOptions(cdbUser, req.params, namedMapProvider, this);
|
self.getStaticImageOptions(cdbUser, res.locals, namedMapProvider, this);
|
||||||
},
|
},
|
||||||
function getImage(err, imageOpts) {
|
function getImage(err, imageOpts) {
|
||||||
assert.ifError(err);
|
assert.ifError(err);
|
||||||
@ -180,7 +182,8 @@ NamedMapsController.prototype.staticMap = function(req, res) {
|
|||||||
req.profiler.add(stats || {});
|
req.profiler.add(stats || {});
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
self.sendError(req, res, err, 'STATIC_VIZ_MAP');
|
err.label = 'STATIC_VIZ_MAP';
|
||||||
|
next(err);
|
||||||
} else {
|
} else {
|
||||||
self.sendResponse(req, res, image, headers, namedMapProvider);
|
self.sendResponse(req, res, image, headers, namedMapProvider);
|
||||||
}
|
}
|
||||||
@ -188,7 +191,13 @@ NamedMapsController.prototype.staticMap = function(req, res) {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
NamedMapsController.prototype.prepareLayerFilterFromPreviewLayers = function (user, req, namedMapProvider, callback) {
|
NamedMapsController.prototype.prepareLayerFilterFromPreviewLayers = function (
|
||||||
|
user,
|
||||||
|
req,
|
||||||
|
params,
|
||||||
|
namedMapProvider,
|
||||||
|
callback
|
||||||
|
) {
|
||||||
var self = this;
|
var self = this;
|
||||||
namedMapProvider.getTemplate(function (err, template) {
|
namedMapProvider.getTemplate(function (err, template) {
|
||||||
if (err) {
|
if (err) {
|
||||||
@ -213,7 +222,7 @@ NamedMapsController.prototype.prepareLayerFilterFromPreviewLayers = function (us
|
|||||||
}
|
}
|
||||||
|
|
||||||
// overwrites 'all' default filter
|
// overwrites 'all' default filter
|
||||||
req.params.layer = layerVisibilityFilter.join(',');
|
params.layer = layerVisibilityFilter.join(',');
|
||||||
|
|
||||||
// recreates the provider
|
// recreates the provider
|
||||||
self.namedMapProviderCache.get(
|
self.namedMapProviderCache.get(
|
||||||
@ -221,7 +230,7 @@ NamedMapsController.prototype.prepareLayerFilterFromPreviewLayers = function (us
|
|||||||
req.params.template_id,
|
req.params.template_id,
|
||||||
req.query.config,
|
req.query.config,
|
||||||
req.query.auth_token,
|
req.query.auth_token,
|
||||||
req.params,
|
params,
|
||||||
callback
|
callback
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -15,8 +15,8 @@ var userMiddleware = require('../middleware/user');
|
|||||||
* @param {TemplateMaps} templateMaps
|
* @param {TemplateMaps} templateMaps
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
function NamedMapsAdminController(authApi, pgConnection, templateMaps) {
|
function NamedMapsAdminController(authApi, templateMaps) {
|
||||||
BaseController.call(this, authApi, pgConnection);
|
BaseController.call(this);
|
||||||
|
|
||||||
this.authApi = authApi;
|
this.authApi = authApi;
|
||||||
this.templateMaps = templateMaps;
|
this.templateMaps = templateMaps;
|
||||||
@ -26,19 +26,52 @@ util.inherits(NamedMapsAdminController, BaseController);
|
|||||||
|
|
||||||
module.exports = NamedMapsAdminController;
|
module.exports = NamedMapsAdminController;
|
||||||
|
|
||||||
NamedMapsAdminController.prototype.register = function(app) {
|
NamedMapsAdminController.prototype.register = function (app) {
|
||||||
app.post(app.base_url_templated, cors(), userMiddleware, this.create.bind(this));
|
app.post(
|
||||||
app.put(app.base_url_templated + '/:template_id', cors(), userMiddleware, this.update.bind(this));
|
app.base_url_templated + '/',
|
||||||
app.get(app.base_url_templated + '/:template_id', cors(), userMiddleware, this.retrieve.bind(this));
|
cors(),
|
||||||
app.delete(app.base_url_templated + '/:template_id', cors(), userMiddleware, this.destroy.bind(this));
|
userMiddleware,
|
||||||
app.get(app.base_url_templated, cors(), userMiddleware, this.list.bind(this));
|
this.create.bind(this)
|
||||||
app.options(app.base_url_templated + '/:template_id', cors('Content-Type'));
|
);
|
||||||
|
|
||||||
|
app.put(
|
||||||
|
app.base_url_templated + '/:template_id',
|
||||||
|
cors(),
|
||||||
|
userMiddleware,
|
||||||
|
this.update.bind(this)
|
||||||
|
);
|
||||||
|
|
||||||
|
app.get(
|
||||||
|
app.base_url_templated + '/:template_id',
|
||||||
|
cors(),
|
||||||
|
userMiddleware,
|
||||||
|
this.retrieve.bind(this)
|
||||||
|
);
|
||||||
|
|
||||||
|
app.delete(
|
||||||
|
app.base_url_templated + '/:template_id',
|
||||||
|
cors(),
|
||||||
|
userMiddleware,
|
||||||
|
this.destroy.bind(this)
|
||||||
|
);
|
||||||
|
|
||||||
|
app.get(
|
||||||
|
app.base_url_templated + '/',
|
||||||
|
cors(),
|
||||||
|
userMiddleware,
|
||||||
|
this.list.bind(this)
|
||||||
|
);
|
||||||
|
|
||||||
|
app.options(
|
||||||
|
app.base_url_templated + '/:template_id',
|
||||||
|
cors('Content-Type')
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
NamedMapsAdminController.prototype.create = function(req, res) {
|
NamedMapsAdminController.prototype.create = function(req, res, next) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
var cdbuser = req.context.user;
|
var cdbuser = res.locals.user;
|
||||||
|
|
||||||
step(
|
step(
|
||||||
function checkPerms(){
|
function checkPerms(){
|
||||||
@ -55,14 +88,14 @@ NamedMapsAdminController.prototype.create = function(req, res) {
|
|||||||
assert.ifError(err);
|
assert.ifError(err);
|
||||||
return { template_id: tpl_id };
|
return { template_id: tpl_id };
|
||||||
},
|
},
|
||||||
finishFn(self, req, res, 'POST TEMPLATE')
|
finishFn(self, req, res, 'POST TEMPLATE', null, next)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
NamedMapsAdminController.prototype.update = function(req, res) {
|
NamedMapsAdminController.prototype.update = function(req, res, next) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
var cdbuser = req.context.user;
|
var cdbuser = res.locals.user;
|
||||||
var template;
|
var template;
|
||||||
var tpl_id;
|
var tpl_id;
|
||||||
|
|
||||||
@ -84,16 +117,16 @@ NamedMapsAdminController.prototype.update = function(req, res) {
|
|||||||
|
|
||||||
return { template_id: tpl_id };
|
return { template_id: tpl_id };
|
||||||
},
|
},
|
||||||
finishFn(self, req, res, 'PUT TEMPLATE')
|
finishFn(self, req, res, 'PUT TEMPLATE', null, next)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
NamedMapsAdminController.prototype.retrieve = function(req, res) {
|
NamedMapsAdminController.prototype.retrieve = function(req, res, next) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
req.profiler.start('windshaft-cartodb.get_template');
|
req.profiler.start('windshaft-cartodb.get_template');
|
||||||
|
|
||||||
var cdbuser = req.context.user;
|
var cdbuser = res.locals.user;
|
||||||
var tpl_id;
|
var tpl_id;
|
||||||
step(
|
step(
|
||||||
function checkPerms(){
|
function checkPerms(){
|
||||||
@ -118,16 +151,16 @@ NamedMapsAdminController.prototype.retrieve = function(req, res) {
|
|||||||
delete tpl_val.auth_id;
|
delete tpl_val.auth_id;
|
||||||
return { template: tpl_val };
|
return { template: tpl_val };
|
||||||
},
|
},
|
||||||
finishFn(self, req, res, 'GET TEMPLATE')
|
finishFn(self, req, res, 'GET TEMPLATE', null, next)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
NamedMapsAdminController.prototype.destroy = function(req, res) {
|
NamedMapsAdminController.prototype.destroy = function(req, res, next) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
req.profiler.start('windshaft-cartodb.delete_template');
|
req.profiler.start('windshaft-cartodb.delete_template');
|
||||||
|
|
||||||
var cdbuser = req.context.user;
|
var cdbuser = res.locals.user;
|
||||||
var tpl_id;
|
var tpl_id;
|
||||||
step(
|
step(
|
||||||
function checkPerms(){
|
function checkPerms(){
|
||||||
@ -144,15 +177,15 @@ NamedMapsAdminController.prototype.destroy = function(req, res) {
|
|||||||
assert.ifError(err);
|
assert.ifError(err);
|
||||||
return '';
|
return '';
|
||||||
},
|
},
|
||||||
finishFn(self, req, res, 'DELETE TEMPLATE', 204)
|
finishFn(self, req, res, 'DELETE TEMPLATE', 204, next)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
NamedMapsAdminController.prototype.list = function(req, res) {
|
NamedMapsAdminController.prototype.list = function(req, res, next) {
|
||||||
var self = this;
|
var self = this;
|
||||||
req.profiler.start('windshaft-cartodb.get_template_list');
|
req.profiler.start('windshaft-cartodb.get_template_list');
|
||||||
|
|
||||||
var cdbuser = req.context.user;
|
var cdbuser = res.locals.user;
|
||||||
|
|
||||||
step(
|
step(
|
||||||
function checkPerms(){
|
function checkPerms(){
|
||||||
@ -168,14 +201,15 @@ NamedMapsAdminController.prototype.list = function(req, res) {
|
|||||||
assert.ifError(err);
|
assert.ifError(err);
|
||||||
return { template_ids: tpl_ids };
|
return { template_ids: tpl_ids };
|
||||||
},
|
},
|
||||||
finishFn(self, req, res, 'GET TEMPLATE LIST')
|
finishFn(self, req, res, 'GET TEMPLATE LIST', null, next)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
function finishFn(controller, req, res, description, status) {
|
function finishFn(controller, req, res, description, status, next) {
|
||||||
return function finish(err, response){
|
return function finish(err, response){
|
||||||
if (err) {
|
if (err) {
|
||||||
controller.sendError(req, res, err, description);
|
err.label = description;
|
||||||
|
next(err);
|
||||||
} else {
|
} else {
|
||||||
controller.send(req, res, response, status || 200);
|
controller.send(req, res, response, status || 200);
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ module.exports = function allowQueryParams(params) {
|
|||||||
throw new Error('allowQueryParams must receive an Array of params');
|
throw new Error('allowQueryParams must receive an Array of params');
|
||||||
}
|
}
|
||||||
return function allowQueryParamsMiddleware(req, res, next) {
|
return function allowQueryParamsMiddleware(req, res, next) {
|
||||||
req.context.allowedQueryParams = params;
|
res.locals.allowedQueryParams = params;
|
||||||
next();
|
next();
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
20
lib/cartodb/middleware/context/authorize.js
Normal file
20
lib/cartodb/middleware/context/authorize.js
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
module.exports = function authorizeMiddleware (authApi) {
|
||||||
|
return function (req, res, next) {
|
||||||
|
req.profiler.done('req2params.setup');
|
||||||
|
|
||||||
|
authApi.authorize(req, res, (err, authorized) => {
|
||||||
|
req.profiler.done('authorize');
|
||||||
|
if (err) {
|
||||||
|
return next(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!authorized) {
|
||||||
|
err = new Error("Sorry, you are unauthorized (permission denied)");
|
||||||
|
err.http_status = 403;
|
||||||
|
return next(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
return next();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
};
|
32
lib/cartodb/middleware/context/clean-up-query-params.js
Normal file
32
lib/cartodb/middleware/context/clean-up-query-params.js
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
const _ = require('underscore');
|
||||||
|
|
||||||
|
// Whitelist query parameters and attach format
|
||||||
|
const REQUEST_QUERY_PARAMS_WHITELIST = [
|
||||||
|
'config',
|
||||||
|
'map_key',
|
||||||
|
'api_key',
|
||||||
|
'auth_token',
|
||||||
|
'callback',
|
||||||
|
'zoom',
|
||||||
|
'lon',
|
||||||
|
'lat',
|
||||||
|
// analysis
|
||||||
|
'filters' // json
|
||||||
|
];
|
||||||
|
|
||||||
|
module.exports = function cleanUpQueryParamsMiddleware () {
|
||||||
|
return function cleanUpQueryParams (req, res, next) {
|
||||||
|
var allowedQueryParams = REQUEST_QUERY_PARAMS_WHITELIST;
|
||||||
|
|
||||||
|
if (Array.isArray(res.locals.allowedQueryParams)) {
|
||||||
|
allowedQueryParams = allowedQueryParams.concat(res.locals.allowedQueryParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
req.query = _.pick(req.query, allowedQueryParams);
|
||||||
|
|
||||||
|
// bring all query values onto res.locals object
|
||||||
|
_.extend(res.locals, req.query);
|
||||||
|
|
||||||
|
next();
|
||||||
|
};
|
||||||
|
};
|
31
lib/cartodb/middleware/context/db-conn-setup.js
Normal file
31
lib/cartodb/middleware/context/db-conn-setup.js
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
const _ = require('underscore');
|
||||||
|
|
||||||
|
module.exports = function dbConnSetupMiddleware(pgConnection) {
|
||||||
|
return function dbConnSetup(req, res, next) {
|
||||||
|
const user = res.locals.user;
|
||||||
|
pgConnection.setDBConn(user, res.locals, (err) => {
|
||||||
|
if (err) {
|
||||||
|
if (err.message && -1 !== err.message.indexOf('name not found')) {
|
||||||
|
err.http_status = 404;
|
||||||
|
}
|
||||||
|
req.profiler.done('req2params');
|
||||||
|
return next(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add default database connection parameters
|
||||||
|
// if none given
|
||||||
|
_.defaults(res.locals, {
|
||||||
|
dbuser: global.environment.postgres.user,
|
||||||
|
dbpassword: global.environment.postgres.password,
|
||||||
|
dbhost: global.environment.postgres.host,
|
||||||
|
dbport: global.environment.postgres.port
|
||||||
|
});
|
||||||
|
|
||||||
|
res.set('X-Served-By-DB-Host', res.locals.dbhost);
|
||||||
|
|
||||||
|
req.profiler.done('req2params');
|
||||||
|
|
||||||
|
next(null);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
};
|
15
lib/cartodb/middleware/context/index.js
Normal file
15
lib/cartodb/middleware/context/index.js
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
const locals = require('./locals');
|
||||||
|
const cleanUpQueryParams = require('./clean-up-query-params');
|
||||||
|
const layergroupToken = require('./layergroup-token');
|
||||||
|
const authorize = require('./authorize');
|
||||||
|
const dbConnSetup = require('./db-conn-setup');
|
||||||
|
|
||||||
|
module.exports = function prepareContextMiddleware(authApi, pgConnection) {
|
||||||
|
return [
|
||||||
|
locals,
|
||||||
|
cleanUpQueryParams(),
|
||||||
|
layergroupToken,
|
||||||
|
authorize(authApi),
|
||||||
|
dbConnSetup(pgConnection)
|
||||||
|
];
|
||||||
|
};
|
32
lib/cartodb/middleware/context/layergroup-token.js
Normal file
32
lib/cartodb/middleware/context/layergroup-token.js
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
var LayergroupToken = require('../../models/layergroup-token');
|
||||||
|
|
||||||
|
module.exports = function layergroupTokenMiddleware(req, res, next) {
|
||||||
|
if (!res.locals.token) {
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
var user = res.locals.user;
|
||||||
|
|
||||||
|
var layergroupToken = LayergroupToken.parse(res.locals.token);
|
||||||
|
res.locals.token = layergroupToken.token;
|
||||||
|
res.locals.cache_buster = layergroupToken.cacheBuster;
|
||||||
|
|
||||||
|
if (layergroupToken.signer) {
|
||||||
|
res.locals.signer = layergroupToken.signer;
|
||||||
|
if (!res.locals.signer) {
|
||||||
|
res.locals.signer = user;
|
||||||
|
} else if (res.locals.signer !== user) {
|
||||||
|
var err = new Error(`Cannot use map signature of user "${res.locals.signer}" on db of user "${user}"`);
|
||||||
|
err.type = 'auth';
|
||||||
|
err.http_status = 403;
|
||||||
|
if (req.query && req.query.callback) {
|
||||||
|
err.http_status = 200;
|
||||||
|
}
|
||||||
|
|
||||||
|
req.profiler.done('req2params');
|
||||||
|
return next(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return next();
|
||||||
|
};
|
8
lib/cartodb/middleware/context/locals.js
Normal file
8
lib/cartodb/middleware/context/locals.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
const _ = require('underscore');
|
||||||
|
|
||||||
|
module.exports = function localsMiddleware(req, res, next) {
|
||||||
|
_.extend(res.locals, req.params);
|
||||||
|
|
||||||
|
next();
|
||||||
|
};
|
||||||
|
|
172
lib/cartodb/middleware/error-middleware.js
Normal file
172
lib/cartodb/middleware/error-middleware.js
Normal file
@ -0,0 +1,172 @@
|
|||||||
|
const _ = require('underscore');
|
||||||
|
const debug = require('debug')('windshaft:cartodb:error-middleware');
|
||||||
|
|
||||||
|
module.exports = function errorMiddleware (/* options */) {
|
||||||
|
return function error (err, req, res, next) {
|
||||||
|
// jshint unused:false
|
||||||
|
// jshint maxcomplexity:9
|
||||||
|
var allErrors = Array.isArray(err) ? err : [err];
|
||||||
|
|
||||||
|
allErrors = populateTimeoutErrors(allErrors);
|
||||||
|
|
||||||
|
const label = err.label || 'UNKNOWN';
|
||||||
|
err = allErrors[0] || new Error(label);
|
||||||
|
allErrors[0] = err;
|
||||||
|
|
||||||
|
var statusCode = findStatusCode(err);
|
||||||
|
|
||||||
|
if (err.message === 'Tile does not exist' && res.locals.format === 'mvt') {
|
||||||
|
statusCode = 204;
|
||||||
|
}
|
||||||
|
|
||||||
|
debug('[%s ERROR] -- %d: %s, %s', label, statusCode, err, err.stack);
|
||||||
|
|
||||||
|
// If a callback was requested, force status to 200
|
||||||
|
if (req.query && req.query.callback) {
|
||||||
|
statusCode = 200;
|
||||||
|
}
|
||||||
|
|
||||||
|
var errorResponseBody = {
|
||||||
|
errors: allErrors.map(errorMessage),
|
||||||
|
errors_with_context: allErrors.map(errorMessageWithContext)
|
||||||
|
};
|
||||||
|
|
||||||
|
res.set('X-Tiler-Profiler', req.profiler.toJSONString());
|
||||||
|
|
||||||
|
res.status(statusCode);
|
||||||
|
|
||||||
|
if (req.query && req.query.callback) {
|
||||||
|
res.jsonp(errorResponseBody);
|
||||||
|
} else {
|
||||||
|
res.json(errorResponseBody);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// May throw due to dns, see
|
||||||
|
// See http://github.com/CartoDB/Windshaft/issues/166
|
||||||
|
req.profiler.sendStats();
|
||||||
|
} catch (err) {
|
||||||
|
debug("error sending profiling stats: " + err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
function isRenderTimeoutError (err) {
|
||||||
|
return err.message === 'Render timed out';
|
||||||
|
}
|
||||||
|
|
||||||
|
function isDatasourceTimeoutError (err) {
|
||||||
|
return err.message && err.message.match(/canceling statement due to statement timeout/i);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isTimeoutError (err) {
|
||||||
|
return isRenderTimeoutError(err) || isDatasourceTimeoutError(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
function populateTimeoutErrors (errors) {
|
||||||
|
return errors.map(function (error) {
|
||||||
|
if (isRenderTimeoutError(error)) {
|
||||||
|
error.subtype = 'render';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isDatasourceTimeoutError(error)) {
|
||||||
|
error.subtype = 'datasource';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isTimeoutError(error)) {
|
||||||
|
error.message = 'You are over platform\'s limits. Please contact us to know more details';
|
||||||
|
error.type = 'limit';
|
||||||
|
error.http_status = 429;
|
||||||
|
}
|
||||||
|
|
||||||
|
return error;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function findStatusCode(err) {
|
||||||
|
var statusCode;
|
||||||
|
if ( err.http_status ) {
|
||||||
|
statusCode = err.http_status;
|
||||||
|
} else {
|
||||||
|
statusCode = statusFromErrorMessage('' + err);
|
||||||
|
}
|
||||||
|
return statusCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports.findStatusCode = findStatusCode;
|
||||||
|
|
||||||
|
function statusFromErrorMessage(errMsg) {
|
||||||
|
// Find an appropriate statusCode based on message
|
||||||
|
// jshint maxcomplexity:7
|
||||||
|
var statusCode = 400;
|
||||||
|
if ( -1 !== errMsg.indexOf('permission denied') ) {
|
||||||
|
statusCode = 403;
|
||||||
|
}
|
||||||
|
else if ( -1 !== errMsg.indexOf('authentication failed') ) {
|
||||||
|
statusCode = 403;
|
||||||
|
}
|
||||||
|
else if (errMsg.match(/Postgis Plugin.*[\s|\n].*column.*does not exist/)) {
|
||||||
|
statusCode = 400;
|
||||||
|
}
|
||||||
|
else if ( -1 !== errMsg.indexOf('does not exist') ) {
|
||||||
|
if ( -1 !== errMsg.indexOf(' role ') ) {
|
||||||
|
statusCode = 403; // role 'xxx' does not exist
|
||||||
|
} else if ( errMsg.match(/function .* does not exist/) ) {
|
||||||
|
statusCode = 400; // invalid SQL (SQL function does not exist)
|
||||||
|
} else {
|
||||||
|
statusCode = 404;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return statusCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
function errorMessage(err) {
|
||||||
|
// See https://github.com/Vizzuality/Windshaft-cartodb/issues/68
|
||||||
|
var message = (_.isString(err) ? err : err.message) || 'Unknown error';
|
||||||
|
|
||||||
|
return stripConnectionInfo(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports.errorMessage = errorMessage;
|
||||||
|
|
||||||
|
function stripConnectionInfo(message) {
|
||||||
|
// Strip connection info, if any
|
||||||
|
return message
|
||||||
|
// See https://github.com/CartoDB/Windshaft/issues/173
|
||||||
|
.replace(/Connection string: '[^']*'\n\s/im, '')
|
||||||
|
// See https://travis-ci.org/CartoDB/Windshaft/jobs/20703062#L1644
|
||||||
|
.replace(/is the server.*encountered/im, 'encountered');
|
||||||
|
}
|
||||||
|
|
||||||
|
var ERROR_INFO_TO_EXPOSE = {
|
||||||
|
message: true,
|
||||||
|
layer: true,
|
||||||
|
type: true,
|
||||||
|
analysis: true,
|
||||||
|
subtype: true
|
||||||
|
};
|
||||||
|
|
||||||
|
function shouldBeExposed (prop) {
|
||||||
|
return !!ERROR_INFO_TO_EXPOSE[prop];
|
||||||
|
}
|
||||||
|
|
||||||
|
function errorMessageWithContext(err) {
|
||||||
|
// See https://github.com/Vizzuality/Windshaft-cartodb/issues/68
|
||||||
|
var message = (_.isString(err) ? err : err.message) || 'Unknown error';
|
||||||
|
|
||||||
|
var error = {
|
||||||
|
type: err.type || 'unknown',
|
||||||
|
message: stripConnectionInfo(message),
|
||||||
|
};
|
||||||
|
|
||||||
|
for (var prop in err) {
|
||||||
|
// type & message are properties from Error's prototype and will be skipped
|
||||||
|
if (err.hasOwnProperty(prop) && shouldBeExposed(prop)) {
|
||||||
|
error[prop] = err[prop];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
@ -1,8 +1,8 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var LZMA = require('lzma').LZMA;
|
const LZMA = require('lzma').LZMA;
|
||||||
|
|
||||||
var lzmaWorker = new LZMA();
|
const lzmaWorker = new LZMA();
|
||||||
|
|
||||||
module.exports = function lzmaMiddleware(req, res, next) {
|
module.exports = function lzmaMiddleware(req, res, next) {
|
||||||
if (!req.query.hasOwnProperty('lzma')) {
|
if (!req.query.hasOwnProperty('lzma')) {
|
||||||
|
@ -2,6 +2,13 @@ var CdbRequest = require('../models/cdb_request');
|
|||||||
var cdbRequest = new CdbRequest();
|
var cdbRequest = new CdbRequest();
|
||||||
|
|
||||||
module.exports = function userMiddleware(req, res, next) {
|
module.exports = function userMiddleware(req, res, next) {
|
||||||
req.context.user = cdbRequest.userByReq(req);
|
res.locals.user = cdbRequest.userByReq(req);
|
||||||
|
|
||||||
|
// avoid a req.params.user equals to undefined
|
||||||
|
// overwrites res.locals.user
|
||||||
|
if(!req.params.user) {
|
||||||
|
delete req.params.user;
|
||||||
|
}
|
||||||
|
|
||||||
next();
|
next();
|
||||||
};
|
};
|
||||||
|
@ -4,8 +4,6 @@ var RedisPool = require('redis-mpool');
|
|||||||
var cartodbRedis = require('cartodb-redis');
|
var cartodbRedis = require('cartodb-redis');
|
||||||
var _ = require('underscore');
|
var _ = require('underscore');
|
||||||
|
|
||||||
var lzmaMiddleware = require('./middleware/lzma');
|
|
||||||
|
|
||||||
var controller = require('./controllers');
|
var controller = require('./controllers');
|
||||||
|
|
||||||
var SurrogateKeysCache = require('./cache/surrogate_keys_cache');
|
var SurrogateKeysCache = require('./cache/surrogate_keys_cache');
|
||||||
@ -46,6 +44,11 @@ var MapConfigAdapter = require('./models/mapconfig/adapter');
|
|||||||
|
|
||||||
var StatsBackend = require('./backends/stats');
|
var StatsBackend = require('./backends/stats');
|
||||||
|
|
||||||
|
const lzmaMiddleware = require('./middleware/lzma');
|
||||||
|
const errorMiddleware = require('./middleware/error-middleware');
|
||||||
|
|
||||||
|
const prepareContextMiddleware = require('./middleware/context');
|
||||||
|
|
||||||
module.exports = function(serverOptions) {
|
module.exports = function(serverOptions) {
|
||||||
// Make stats client globally accessible
|
// Make stats client globally accessible
|
||||||
global.statsClient = StatsClient.getInstance(serverOptions.statsd);
|
global.statsClient = StatsClient.getInstance(serverOptions.statsd);
|
||||||
@ -209,12 +212,16 @@ module.exports = function(serverOptions) {
|
|||||||
|
|
||||||
var versions = getAndValidateVersions(serverOptions);
|
var versions = getAndValidateVersions(serverOptions);
|
||||||
|
|
||||||
|
const prepareContext = typeof serverOptions.req2params === 'function' ?
|
||||||
|
serverOptions.req2params :
|
||||||
|
prepareContextMiddleware(authApi, pgConnection);
|
||||||
|
|
||||||
/*******************************************************************************************************************
|
/*******************************************************************************************************************
|
||||||
* Routing
|
* Routing
|
||||||
******************************************************************************************************************/
|
******************************************************************************************************************/
|
||||||
|
|
||||||
new controller.Layergroup(
|
new controller.Layergroup(
|
||||||
authApi,
|
prepareContext,
|
||||||
pgConnection,
|
pgConnection,
|
||||||
mapStore,
|
mapStore,
|
||||||
tileBackend,
|
tileBackend,
|
||||||
@ -227,7 +234,7 @@ module.exports = function(serverOptions) {
|
|||||||
).register(app);
|
).register(app);
|
||||||
|
|
||||||
new controller.Map(
|
new controller.Map(
|
||||||
authApi,
|
prepareContext,
|
||||||
pgConnection,
|
pgConnection,
|
||||||
templateMaps,
|
templateMaps,
|
||||||
mapBackend,
|
mapBackend,
|
||||||
@ -240,8 +247,7 @@ module.exports = function(serverOptions) {
|
|||||||
).register(app);
|
).register(app);
|
||||||
|
|
||||||
new controller.NamedMaps(
|
new controller.NamedMaps(
|
||||||
authApi,
|
prepareContext,
|
||||||
pgConnection,
|
|
||||||
namedMapProviderCache,
|
namedMapProviderCache,
|
||||||
tileBackend,
|
tileBackend,
|
||||||
previewBackend,
|
previewBackend,
|
||||||
@ -250,9 +256,9 @@ module.exports = function(serverOptions) {
|
|||||||
metadataBackend
|
metadataBackend
|
||||||
).register(app);
|
).register(app);
|
||||||
|
|
||||||
new controller.NamedMapsAdmin(authApi, pgConnection, templateMaps).register(app);
|
new controller.NamedMapsAdmin(authApi, templateMaps).register(app);
|
||||||
|
|
||||||
new controller.Analyses(authApi, pgConnection).register(app);
|
new controller.Analyses(prepareContext).register(app);
|
||||||
|
|
||||||
new controller.ServerInfo(versions).register(app);
|
new controller.ServerInfo(versions).register(app);
|
||||||
|
|
||||||
@ -260,6 +266,8 @@ module.exports = function(serverOptions) {
|
|||||||
* END Routing
|
* END Routing
|
||||||
******************************************************************************************************************/
|
******************************************************************************************************************/
|
||||||
|
|
||||||
|
app.use(errorMiddleware());
|
||||||
|
|
||||||
return app;
|
return app;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -354,7 +362,6 @@ function bootstrap(opts) {
|
|||||||
app.use(bodyParser.json());
|
app.use(bodyParser.json());
|
||||||
|
|
||||||
app.use(function bootstrap$prepareRequestResponse(req, res, next) {
|
app.use(function bootstrap$prepareRequestResponse(req, res, next) {
|
||||||
req.context = req.context || {};
|
|
||||||
req.profiler = new Profiler({
|
req.profiler = new Profiler({
|
||||||
statsd_client: global.statsClient,
|
statsd_client: global.statsClient,
|
||||||
profile: opts.useProfiler
|
profile: opts.useProfiler
|
||||||
|
@ -7,7 +7,7 @@ var serverOptions = require('../../../lib/cartodb/server_options');
|
|||||||
var server = new CartodbWindshaft(serverOptions);
|
var server = new CartodbWindshaft(serverOptions);
|
||||||
var TestClient = require('../../support/test-client');
|
var TestClient = require('../../support/test-client');
|
||||||
|
|
||||||
var LayergroupToken = require('../../support/layergroup-token');
|
var LayergroupToken = require('../../../lib/cartodb/models/layergroup-token');
|
||||||
|
|
||||||
describe('named-maps analysis', function() {
|
describe('named-maps analysis', function() {
|
||||||
|
|
||||||
|
2
test/acceptance/cache/cache_headers.js
vendored
2
test/acceptance/cache/cache_headers.js
vendored
@ -8,7 +8,7 @@ var serverOptions = require('../../../lib/cartodb/server_options');
|
|||||||
var server = new CartodbWindshaft(serverOptions);
|
var server = new CartodbWindshaft(serverOptions);
|
||||||
server.setMaxListeners(0);
|
server.setMaxListeners(0);
|
||||||
|
|
||||||
var LayergroupToken = require('../../support/layergroup-token');
|
var LayergroupToken = require('../../../lib/cartodb/models/layergroup-token');
|
||||||
|
|
||||||
describe('get requests with cache headers', function() {
|
describe('get requests with cache headers', function() {
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
var assert = require('../support/assert');
|
var assert = require('../support/assert');
|
||||||
var step = require('step');
|
var step = require('step');
|
||||||
var LayergroupToken = require('../support/layergroup-token');
|
var LayergroupToken = require('../../lib/cartodb/models/layergroup-token');
|
||||||
var testHelper = require(__dirname + '/../support/test_helper');
|
var testHelper = require(__dirname + '/../support/test_helper');
|
||||||
var CartodbWindshaft = require(__dirname + '/../../lib/cartodb/server');
|
var CartodbWindshaft = require(__dirname + '/../../lib/cartodb/server');
|
||||||
var serverOptions = require(__dirname + '/../../lib/cartodb/server_options');
|
var serverOptions = require(__dirname + '/../../lib/cartodb/server_options');
|
||||||
|
@ -9,7 +9,7 @@ var mapnik = require('windshaft').mapnik;
|
|||||||
var semver = require('semver');
|
var semver = require('semver');
|
||||||
|
|
||||||
var helper = require(__dirname + '/../support/test_helper');
|
var helper = require(__dirname + '/../support/test_helper');
|
||||||
var LayergroupToken = require('../support/layergroup-token');
|
var LayergroupToken = require('../../lib/cartodb/models/layergroup-token');
|
||||||
|
|
||||||
var windshaft_fixtures = __dirname + '/../../node_modules/windshaft/test/fixtures';
|
var windshaft_fixtures = __dirname + '/../../node_modules/windshaft/test/fixtures';
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ var assert = require('../support/assert');
|
|||||||
|
|
||||||
var _ = require('underscore');
|
var _ = require('underscore');
|
||||||
|
|
||||||
var LayergroupToken = require('../support/layergroup-token');
|
var LayergroupToken = require('../../lib/cartodb/models/layergroup-token');
|
||||||
|
|
||||||
var PgQueryRunner = require('../../lib/cartodb/backends/pg_query_runner');
|
var PgQueryRunner = require('../../lib/cartodb/backends/pg_query_runner');
|
||||||
var QueryTables = require('cartodb-query-tables');
|
var QueryTables = require('cartodb-query-tables');
|
||||||
|
@ -5,7 +5,7 @@ var CartodbWindshaft = require(__dirname + '/../../lib/cartodb/server');
|
|||||||
var serverOptions = require(__dirname + '/../../lib/cartodb/server_options');
|
var serverOptions = require(__dirname + '/../../lib/cartodb/server_options');
|
||||||
var server = new CartodbWindshaft(serverOptions);
|
var server = new CartodbWindshaft(serverOptions);
|
||||||
|
|
||||||
var LayergroupToken = require('../support/layergroup-token');
|
var LayergroupToken = require('../../lib/cartodb/models/layergroup-token');
|
||||||
|
|
||||||
var RedisPool = require('redis-mpool');
|
var RedisPool = require('redis-mpool');
|
||||||
var TemplateMaps = require('../../lib/cartodb/backends/template_maps.js');
|
var TemplateMaps = require('../../lib/cartodb/backends/template_maps.js');
|
||||||
|
@ -5,7 +5,7 @@ var CartodbWindshaft = require(__dirname + '/../../lib/cartodb/server');
|
|||||||
var serverOptions = require(__dirname + '/../../lib/cartodb/server_options');
|
var serverOptions = require(__dirname + '/../../lib/cartodb/server_options');
|
||||||
var server = new CartodbWindshaft(serverOptions);
|
var server = new CartodbWindshaft(serverOptions);
|
||||||
|
|
||||||
var LayergroupToken = require('../support/layergroup-token');
|
var LayergroupToken = require('../../lib/cartodb/models/layergroup-token');
|
||||||
|
|
||||||
var RedisPool = require('redis-mpool');
|
var RedisPool = require('redis-mpool');
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ var CartodbWindshaft = require(__dirname + '/../../lib/cartodb/server');
|
|||||||
var serverOptions = require(__dirname + '/../../lib/cartodb/server_options');
|
var serverOptions = require(__dirname + '/../../lib/cartodb/server_options');
|
||||||
var server = new CartodbWindshaft(serverOptions);
|
var server = new CartodbWindshaft(serverOptions);
|
||||||
|
|
||||||
var LayergroupToken = require('../support/layergroup-token');
|
var LayergroupToken = require('../../lib/cartodb/models/layergroup-token');
|
||||||
|
|
||||||
var RedisPool = require('redis-mpool');
|
var RedisPool = require('redis-mpool');
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ var cartodbServer = require('../../../lib/cartodb/server');
|
|||||||
var PortedServerOptions = require('./support/ported_server_options');
|
var PortedServerOptions = require('./support/ported_server_options');
|
||||||
var BaseController = require('../../../lib/cartodb/controllers/base');
|
var BaseController = require('../../../lib/cartodb/controllers/base');
|
||||||
|
|
||||||
var LayergroupToken = require('../../support/layergroup-token');
|
var LayergroupToken = require('../../../lib/cartodb/models/layergroup-token');
|
||||||
|
|
||||||
|
|
||||||
describe('attributes', function() {
|
describe('attributes', function() {
|
||||||
|
@ -7,7 +7,7 @@ var step = require('step');
|
|||||||
var mapnik = require('windshaft').mapnik;
|
var mapnik = require('windshaft').mapnik;
|
||||||
var cartodbServer = require('../../../lib/cartodb/server');
|
var cartodbServer = require('../../../lib/cartodb/server');
|
||||||
var ServerOptions = require('./support/ported_server_options');
|
var ServerOptions = require('./support/ported_server_options');
|
||||||
var LayergroupToken = require('../../support/layergroup-token');
|
var LayergroupToken = require('../../../lib/cartodb/models/layergroup-token');
|
||||||
var BaseController = require('../../../lib/cartodb/controllers/base');
|
var BaseController = require('../../../lib/cartodb/controllers/base');
|
||||||
|
|
||||||
describe('multilayer', function() {
|
describe('multilayer', function() {
|
||||||
|
@ -5,7 +5,7 @@ var _ = require('underscore');
|
|||||||
var cartodbServer = require('../../../lib/cartodb/server');
|
var cartodbServer = require('../../../lib/cartodb/server');
|
||||||
var getLayerTypeFn = require('windshaft').model.MapConfig.prototype.getType;
|
var getLayerTypeFn = require('windshaft').model.MapConfig.prototype.getType;
|
||||||
var PortedServerOptions = require('./support/ported_server_options');
|
var PortedServerOptions = require('./support/ported_server_options');
|
||||||
var LayergroupToken = require('../../support/layergroup-token');
|
var LayergroupToken = require('../../../lib/cartodb/models/layergroup-token');
|
||||||
|
|
||||||
var BaseController = require('../../../lib/cartodb/controllers/base');
|
var BaseController = require('../../../lib/cartodb/controllers/base');
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ var cartodbServer = require('../../../lib/cartodb/server');
|
|||||||
var ServerOptions = require('./support/ported_server_options');
|
var ServerOptions = require('./support/ported_server_options');
|
||||||
|
|
||||||
var BaseController = require('../../../lib/cartodb/controllers/base');
|
var BaseController = require('../../../lib/cartodb/controllers/base');
|
||||||
var LayergroupToken = require('../../support/layergroup-token');
|
var LayergroupToken = require('../../../lib/cartodb/models/layergroup-token');
|
||||||
|
|
||||||
describe('raster', function() {
|
describe('raster', function() {
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ var cartodbServer = require('../../../lib/cartodb/server');
|
|||||||
var ServerOptions = require('./support/ported_server_options');
|
var ServerOptions = require('./support/ported_server_options');
|
||||||
|
|
||||||
var BaseController = require('../../../lib/cartodb/controllers/base');
|
var BaseController = require('../../../lib/cartodb/controllers/base');
|
||||||
var LayergroupToken = require('../../support/layergroup-token');
|
var LayergroupToken = require('../../../lib/cartodb/models/layergroup-token');
|
||||||
|
|
||||||
describe('retina support', function() {
|
describe('retina support', function() {
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ var cartodbServer = require('../../../lib/cartodb/server');
|
|||||||
var ServerOptions = require('./support/ported_server_options');
|
var ServerOptions = require('./support/ported_server_options');
|
||||||
|
|
||||||
var BaseController = require('../../../lib/cartodb/controllers/base');
|
var BaseController = require('../../../lib/cartodb/controllers/base');
|
||||||
var LayergroupToken = require('../../support/layergroup-token');
|
var LayergroupToken = require('../../../lib/cartodb/models/layergroup-token');
|
||||||
|
|
||||||
var IMAGE_EQUALS_TOLERANCE_PER_MIL = 85;
|
var IMAGE_EQUALS_TOLERANCE_PER_MIL = 85;
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
var _ = require('underscore');
|
var _ = require('underscore');
|
||||||
var serverOptions = require('../../../../lib/cartodb/server_options');
|
var serverOptions = require('../../../../lib/cartodb/server_options');
|
||||||
var LayergroupToken = require('../../../support/layergroup-token');
|
|
||||||
var mapnik = require('windshaft').mapnik;
|
var mapnik = require('windshaft').mapnik;
|
||||||
|
var LayergroupToken = require('../../../../lib/cartodb/models/layergroup-token');
|
||||||
var OverviewsQueryRewriter = require('../../../../lib/cartodb/utils/overviews_query_rewriter');
|
var OverviewsQueryRewriter = require('../../../../lib/cartodb/utils/overviews_query_rewriter');
|
||||||
var overviewsQueryRewriter = new OverviewsQueryRewriter({
|
var overviewsQueryRewriter = new OverviewsQueryRewriter({
|
||||||
zoom_level: 'CDB_ZoomFromScale(!scale_denominator!)'
|
zoom_level: 'CDB_ZoomFromScale(!scale_denominator!)'
|
||||||
@ -48,7 +48,7 @@ module.exports = _.extend({}, serverOptions, {
|
|||||||
log_format: null, // do not log anything
|
log_format: null, // do not log anything
|
||||||
afterLayergroupCreateCalls: 0,
|
afterLayergroupCreateCalls: 0,
|
||||||
useProfiler: true,
|
useProfiler: true,
|
||||||
req2params: function(req, callback){
|
req2params: function(req, res, callback){
|
||||||
|
|
||||||
if ( req.query.testUnexpectedError ) {
|
if ( req.query.testUnexpectedError ) {
|
||||||
return callback('test unexpected error');
|
return callback('test unexpected error');
|
||||||
@ -56,13 +56,14 @@ module.exports = _.extend({}, serverOptions, {
|
|||||||
|
|
||||||
// this is in case you want to test sql parameters eg ...png?sql=select * from my_table limit 10
|
// this is in case you want to test sql parameters eg ...png?sql=select * from my_table limit 10
|
||||||
req.params = _.extend({}, req.params);
|
req.params = _.extend({}, req.params);
|
||||||
|
|
||||||
if (req.params.token) {
|
if (req.params.token) {
|
||||||
req.params.token = LayergroupToken.parse(req.params.token).token;
|
req.params.token = LayergroupToken.parse(req.params.token).token;
|
||||||
}
|
}
|
||||||
|
|
||||||
_.extend(req.params, req.query);
|
_.extend(req.params, req.query);
|
||||||
req.params.user = 'localhost';
|
req.params.user = 'localhost';
|
||||||
req.context = {user: 'localhost'};
|
res.locals.user = 'localhost';
|
||||||
|
|
||||||
req.params.dbhost = global.environment.postgres.host;
|
req.params.dbhost = global.environment.postgres.host;
|
||||||
req.params.dbport = req.params.dbport || global.environment.postgres.port;
|
req.params.dbport = req.params.dbport || global.environment.postgres.port;
|
||||||
@ -73,6 +74,9 @@ module.exports = _.extend({}, serverOptions, {
|
|||||||
}
|
}
|
||||||
req.params.dbname = 'test_windshaft_cartodb_user_1_db';
|
req.params.dbname = 'test_windshaft_cartodb_user_1_db';
|
||||||
|
|
||||||
|
// add all params to res.locals
|
||||||
|
res.locals = _.extend({}, req.params);
|
||||||
|
|
||||||
|
|
||||||
// increment number of calls counter
|
// increment number of calls counter
|
||||||
global.req2params_calls = global.req2params_calls ? global.req2params_calls + 1 : 1;
|
global.req2params_calls = global.req2params_calls ? global.req2params_calls + 1 : 1;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
var testHelper = require('../../../support/test_helper');
|
var testHelper = require('../../../support/test_helper');
|
||||||
var LayergroupToken = require('../../../support/layergroup-token');
|
var LayergroupToken = require('../../../../lib/cartodb/models/layergroup-token');
|
||||||
|
|
||||||
var step = require('step');
|
var step = require('step');
|
||||||
var assert = require('../../../support/assert');
|
var assert = require('../../../support/assert');
|
||||||
|
@ -7,7 +7,7 @@ var cartodbServer = require('../../../lib/cartodb/server');
|
|||||||
var ServerOptions = require('./support/ported_server_options');
|
var ServerOptions = require('./support/ported_server_options');
|
||||||
|
|
||||||
var BaseController = require('../../../lib/cartodb/controllers/base');
|
var BaseController = require('../../../lib/cartodb/controllers/base');
|
||||||
var LayergroupToken = require('../../support/layergroup-token');
|
var LayergroupToken = require('../../../lib/cartodb/models/layergroup-token');
|
||||||
|
|
||||||
describe('torque', function() {
|
describe('torque', function() {
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ var cartodbServer = require('../../../lib/cartodb/server');
|
|||||||
var ServerOptions = require('./support/ported_server_options');
|
var ServerOptions = require('./support/ported_server_options');
|
||||||
|
|
||||||
var BaseController = require('../../../lib/cartodb/controllers/base');
|
var BaseController = require('../../../lib/cartodb/controllers/base');
|
||||||
var LayergroupToken = require('../../support/layergroup-token');
|
var LayergroupToken = require('../../../lib/cartodb/models/layergroup-token');
|
||||||
|
|
||||||
describe('torque boundary points', function() {
|
describe('torque boundary points', function() {
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ var serverOptions = require(__dirname + '/../../lib/cartodb/server_options');
|
|||||||
var server = new CartodbWindshaft(serverOptions);
|
var server = new CartodbWindshaft(serverOptions);
|
||||||
server.setMaxListeners(0);
|
server.setMaxListeners(0);
|
||||||
|
|
||||||
var LayergroupToken = require('../support/layergroup-token');
|
var LayergroupToken = require('../../lib/cartodb/models/layergroup-token');
|
||||||
|
|
||||||
describe('template_api', function() {
|
describe('template_api', function() {
|
||||||
server.layergroupAffectedTablesCache.cache.reset();
|
server.layergroupAffectedTablesCache.cache.reset();
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
var assert = require('../../support/assert');
|
var assert = require('../../support/assert');
|
||||||
var step = require('step');
|
var step = require('step');
|
||||||
var LayergroupToken = require('../../support/layergroup-token');
|
var LayergroupToken = require('../../../lib/cartodb/models/layergroup-token');
|
||||||
var testHelper = require('../../support/test_helper');
|
var testHelper = require('../../support/test_helper');
|
||||||
var CartodbWindshaft = require('../../../lib/cartodb/server');
|
var CartodbWindshaft = require('../../../lib/cartodb/server');
|
||||||
var serverOptions = require('../../../lib/cartodb/server_options');
|
var serverOptions = require('../../../lib/cartodb/server_options');
|
||||||
|
@ -10,7 +10,7 @@ var CartodbWindshaft = require('../../../lib/cartodb/server');
|
|||||||
var serverOptions = require('../../../lib/cartodb/server_options');
|
var serverOptions = require('../../../lib/cartodb/server_options');
|
||||||
var server = new CartodbWindshaft(serverOptions);
|
var server = new CartodbWindshaft(serverOptions);
|
||||||
|
|
||||||
var LayergroupToken = require('../../support/layergroup-token');
|
var LayergroupToken = require('../../../lib/cartodb/models/layergroup-token');
|
||||||
|
|
||||||
describe('named-maps widgets', function() {
|
describe('named-maps widgets', function() {
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ var PSQL = require('cartodb-psql');
|
|||||||
var _ = require('underscore');
|
var _ = require('underscore');
|
||||||
var mapnik = require('windshaft').mapnik;
|
var mapnik = require('windshaft').mapnik;
|
||||||
|
|
||||||
var LayergroupToken = require('./layergroup-token');
|
var LayergroupToken = require('../../lib/cartodb/models/layergroup-token');
|
||||||
|
|
||||||
var assert = require('./assert');
|
var assert = require('./assert');
|
||||||
var helper = require('./test_helper');
|
var helper = require('./test_helper');
|
||||||
@ -116,6 +116,15 @@ module.exports.SQL = {
|
|||||||
ONE_POINT: 'select 1 as cartodb_id, \'SRID=3857;POINT(0 0)\'::geometry the_geom_webmercator'
|
ONE_POINT: 'select 1 as cartodb_id, \'SRID=3857;POINT(0 0)\'::geometry the_geom_webmercator'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function resErr2errRes(callback) {
|
||||||
|
return (res, err) => {
|
||||||
|
if (err) {
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
return callback(err, res);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
TestClient.prototype.getWidget = function(widgetName, params, callback) {
|
TestClient.prototype.getWidget = function(widgetName, params, callback) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
@ -716,9 +725,9 @@ TestClient.prototype.getTile = function(z, x, y, params, callback) {
|
|||||||
expectedResponse.headers['Content-Type'] = 'application/json; charset=utf-8';
|
expectedResponse.headers['Content-Type'] = 'application/json; charset=utf-8';
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.response(self.server, request, expectedResponse, this);
|
assert.response(self.server, request, expectedResponse, resErr2errRes(this));
|
||||||
},
|
},
|
||||||
function finish(res, err) {
|
function finish(err, res) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
@ -869,9 +878,9 @@ TestClient.prototype.getStaticCenter = function (params, callback) {
|
|||||||
}
|
}
|
||||||
}, params.response);
|
}, params.response);
|
||||||
|
|
||||||
assert.response(self.server, request, expectedResponse, this);
|
assert.response(self.server, request, expectedResponse, resErr2errRes(this));
|
||||||
},
|
},
|
||||||
function(res, err) {
|
function(err, res) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
@ -968,9 +977,9 @@ TestClient.prototype.getNodeStatus = function(nodeName, callback) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
assert.response(self.server, request, expectedResponse, this);
|
assert.response(self.server, request, expectedResponse, resErr2errRes(this));
|
||||||
},
|
},
|
||||||
function finish(res, err) {
|
function finish(err, res) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
@ -1063,9 +1072,9 @@ TestClient.prototype.getAttributes = function(params, callback) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
assert.response(self.server, request, expectedResponse, this);
|
assert.response(self.server, request, expectedResponse, resErr2errRes(this));
|
||||||
},
|
},
|
||||||
function finish(res, err) {
|
function finish(err, res) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
|
@ -1,21 +1,21 @@
|
|||||||
require('../../support/test_helper.js');
|
require('../../support/test_helper.js');
|
||||||
|
|
||||||
var assert = require('assert');
|
var assert = require('assert');
|
||||||
var BaseController = require('../../../lib/cartodb/controllers/base');
|
var errorMiddleware = require('../../../lib/cartodb/middleware/error-middleware');
|
||||||
|
|
||||||
describe('BaseController', function() {
|
describe('error-middleware', function() {
|
||||||
|
|
||||||
it('different formats for postgis plugin error returns 400 as status code', function() {
|
it('different formats for postgis plugin error returns 400 as status code', function() {
|
||||||
|
|
||||||
var expectedStatusCode = 400;
|
var expectedStatusCode = 400;
|
||||||
assert.equal(
|
assert.equal(
|
||||||
BaseController.findStatusCode("Postgis Plugin: ERROR: column \"missing\" does not exist\n"),
|
errorMiddleware.findStatusCode("Postgis Plugin: ERROR: column \"missing\" does not exist\n"),
|
||||||
expectedStatusCode,
|
expectedStatusCode,
|
||||||
"Error status code for single line does not match"
|
"Error status code for single line does not match"
|
||||||
);
|
);
|
||||||
|
|
||||||
assert.equal(
|
assert.equal(
|
||||||
BaseController.findStatusCode("Postgis Plugin: PSQL error:\nERROR: column \"missing\" does not exist\n"),
|
errorMiddleware.findStatusCode("Postgis Plugin: PSQL error:\nERROR: column \"missing\" does not exist\n"),
|
||||||
expectedStatusCode,
|
expectedStatusCode,
|
||||||
"Error status code for multiline/PSQL does not match"
|
"Error status code for multiline/PSQL does not match"
|
||||||
);
|
);
|
||||||
|
@ -2,7 +2,7 @@ require('../../support/test_helper');
|
|||||||
|
|
||||||
var assert = require('assert');
|
var assert = require('assert');
|
||||||
|
|
||||||
var BaseController = require('../../../lib/cartodb/controllers/base');
|
var errorMiddleware = require('../../../lib/cartodb/middleware/error-middleware');
|
||||||
|
|
||||||
describe('error messages clean up', function() {
|
describe('error messages clean up', function() {
|
||||||
|
|
||||||
@ -15,7 +15,7 @@ describe('error messages clean up', function() {
|
|||||||
" encountered during parsing of layer 'layer0' in Layer"
|
" encountered during parsing of layer 'layer0' in Layer"
|
||||||
].join('\n');
|
].join('\n');
|
||||||
|
|
||||||
var outMessage = BaseController.errorMessage(inMessage);
|
var outMessage = errorMiddleware.errorMessage(inMessage);
|
||||||
|
|
||||||
assert.ok(outMessage.match('connect'), outMessage);
|
assert.ok(outMessage.match('connect'), outMessage);
|
||||||
assert.ok(!outMessage.match(/666/), outMessage);
|
assert.ok(!outMessage.match(/666/), outMessage);
|
||||||
|
@ -41,7 +41,9 @@ describe('tile stats', function() {
|
|||||||
jsonp: function() {},
|
jsonp: function() {},
|
||||||
send: function() {}
|
send: function() {}
|
||||||
};
|
};
|
||||||
layergroupController.finalizeGetTileOrGrid('Unsupported format png2', reqMock, resMock, null, null);
|
|
||||||
|
var next = function () {};
|
||||||
|
layergroupController.finalizeGetTileOrGrid('Unsupported format png2', reqMock, resMock, null, null, next);
|
||||||
|
|
||||||
assert.ok(formatMatched, 'Format was never matched in increment method');
|
assert.ok(formatMatched, 'Format was never matched in increment method');
|
||||||
assert.equal(expectedCalls, 0, 'Unexpected number of calls to increment method');
|
assert.equal(expectedCalls, 0, 'Unexpected number of calls to increment method');
|
||||||
@ -74,7 +76,8 @@ describe('tile stats', function() {
|
|||||||
|
|
||||||
var layergroupController = new LayergroupController();
|
var layergroupController = new LayergroupController();
|
||||||
|
|
||||||
layergroupController.finalizeGetTileOrGrid('Another error happened', reqMock, resMock, null, null);
|
var next = function () {};
|
||||||
|
layergroupController.finalizeGetTileOrGrid('Another error happened', reqMock, resMock, null, null, next);
|
||||||
|
|
||||||
assert.ok(formatMatched, 'Format was never matched in increment method');
|
assert.ok(formatMatched, 'Format was never matched in increment method');
|
||||||
assert.equal(expectedCalls, 0, 'Unexpected number of calls to increment method');
|
assert.equal(expectedCalls, 0, 'Unexpected number of calls to increment method');
|
||||||
|
171
test/unit/cartodb/prepare-context.test.js
Normal file
171
test/unit/cartodb/prepare-context.test.js
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
var assert = require('assert');
|
||||||
|
var _ = require('underscore');
|
||||||
|
|
||||||
|
var RedisPool = require('redis-mpool');
|
||||||
|
var cartodbRedis = require('cartodb-redis');
|
||||||
|
var PgConnection = require('../../../lib/cartodb/backends/pg_connection');
|
||||||
|
var AuthApi = require('../../../lib/cartodb/api/auth_api');
|
||||||
|
var TemplateMaps = require('../../../lib/cartodb/backends/template_maps');
|
||||||
|
|
||||||
|
const cleanUpQueryParamsMiddleware = require('../../../lib/cartodb/middleware/context/clean-up-query-params');
|
||||||
|
const authorizeMiddleware = require('../../../lib/cartodb/middleware/context/authorize');
|
||||||
|
const dbConnSetupMiddleware = require('../../../lib/cartodb/middleware/context/db-conn-setup');
|
||||||
|
const localsMiddleware = require('../../../lib/cartodb/middleware/context/locals');
|
||||||
|
|
||||||
|
var windshaft = require('windshaft');
|
||||||
|
|
||||||
|
describe('prepare-context', function() {
|
||||||
|
|
||||||
|
var test_user = _.template(global.environment.postgres_auth_user, {user_id:1});
|
||||||
|
var test_pubuser = global.environment.postgres.user;
|
||||||
|
var test_database = test_user + '_db';
|
||||||
|
|
||||||
|
let cleanUpQueryParams;
|
||||||
|
let dbConnSetup;
|
||||||
|
let authorize;
|
||||||
|
|
||||||
|
before(function() {
|
||||||
|
var redisPool = new RedisPool(global.environment.redis);
|
||||||
|
var mapStore = new windshaft.storage.MapStore();
|
||||||
|
var metadataBackend = cartodbRedis({pool: redisPool});
|
||||||
|
var pgConnection = new PgConnection(metadataBackend);
|
||||||
|
var templateMaps = new TemplateMaps(redisPool);
|
||||||
|
var authApi = new AuthApi(pgConnection, metadataBackend, mapStore, templateMaps);
|
||||||
|
|
||||||
|
cleanUpQueryParams = cleanUpQueryParamsMiddleware();
|
||||||
|
authorize = authorizeMiddleware(authApi);
|
||||||
|
dbConnSetup = dbConnSetupMiddleware(pgConnection);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('can be found in server_options', function(){
|
||||||
|
assert.ok(_.isFunction(authorize));
|
||||||
|
assert.ok(_.isFunction(dbConnSetup));
|
||||||
|
assert.ok(_.isFunction(cleanUpQueryParams));
|
||||||
|
});
|
||||||
|
|
||||||
|
function prepareRequest(req) {
|
||||||
|
req.profiler = {
|
||||||
|
done: function() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
return req;
|
||||||
|
}
|
||||||
|
|
||||||
|
function prepareResponse(res) {
|
||||||
|
if(!res.locals) {
|
||||||
|
res.locals = {};
|
||||||
|
}
|
||||||
|
res.locals.user = 'localhost';
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
it('res.locals are created', function(done) {
|
||||||
|
let req = {};
|
||||||
|
let res = {};
|
||||||
|
|
||||||
|
localsMiddleware(prepareRequest(req), prepareResponse(res), function(err) {
|
||||||
|
if ( err ) { done(err); return; }
|
||||||
|
assert.ok(res.hasOwnProperty('locals'), 'response has locals');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('cleans up request', function(done){
|
||||||
|
var req = {headers: { host:'localhost' }, query: {dbuser:'hacker',dbname:'secret'}};
|
||||||
|
var res = {};
|
||||||
|
|
||||||
|
cleanUpQueryParams(prepareRequest(req), prepareResponse(res), function(err) {
|
||||||
|
if ( err ) { done(err); return; }
|
||||||
|
assert.ok(_.isObject(req.query), 'request has query');
|
||||||
|
assert.ok(!req.query.hasOwnProperty('dbuser'), 'dbuser was removed from query');
|
||||||
|
assert.ok(res.hasOwnProperty('locals'), 'response has locals');
|
||||||
|
assert.ok(!res.locals.hasOwnProperty('interactivity'), 'response locals do not have interactivity');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sets dbname from redis metadata', function(done){
|
||||||
|
var req = {headers: { host:'localhost' }, query: {} };
|
||||||
|
var res = { set: function () {} };
|
||||||
|
|
||||||
|
dbConnSetup(prepareRequest(req), prepareResponse(res), function(err) {
|
||||||
|
if ( err ) { done(err); return; }
|
||||||
|
assert.ok(_.isObject(req.query), 'request has query');
|
||||||
|
assert.ok(!req.query.hasOwnProperty('dbuser'), 'dbuser was removed from query');
|
||||||
|
assert.ok(res.hasOwnProperty('locals'), 'response has locals');
|
||||||
|
assert.ok(!res.locals.hasOwnProperty('interactivity'), 'response locals do not have interactivity');
|
||||||
|
assert.equal(res.locals.dbname, test_database);
|
||||||
|
assert.ok(res.locals.dbuser === test_pubuser, 'could inject dbuser ('+res.locals.dbuser+')');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sets also dbuser for authenticated requests', function(done){
|
||||||
|
var req = { headers: { host: 'localhost' }, query: { map_key: '1234' }};
|
||||||
|
var res = { set: function () {} };
|
||||||
|
|
||||||
|
// FIXME: review authorize-pgconnsetup workflow, It might we are doing authorization twice.
|
||||||
|
authorize(prepareRequest(req), prepareResponse(res), function (err) {
|
||||||
|
if (err) { done(err); return; }
|
||||||
|
dbConnSetup(req, res, function(err) {
|
||||||
|
if ( err ) { done(err); return; }
|
||||||
|
assert.ok(_.isObject(req.query), 'request has query');
|
||||||
|
assert.ok(!req.query.hasOwnProperty('dbuser'), 'dbuser was removed from query');
|
||||||
|
assert.ok(res.hasOwnProperty('locals'), 'response has locals');
|
||||||
|
assert.ok(!res.locals.hasOwnProperty('interactivity'), 'request params do not have interactivity');
|
||||||
|
assert.equal(res.locals.dbname, test_database);
|
||||||
|
assert.equal(res.locals.dbuser, test_user);
|
||||||
|
|
||||||
|
req = {
|
||||||
|
headers: {
|
||||||
|
host:'localhost'
|
||||||
|
},
|
||||||
|
query: {
|
||||||
|
map_key: '1235'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
res = { set: function () {} };
|
||||||
|
|
||||||
|
dbConnSetup(prepareRequest(req), prepareResponse(res), function() {
|
||||||
|
// wrong key resets params to no user
|
||||||
|
assert.ok(res.locals.dbuser === test_pubuser, 'could inject dbuser ('+res.locals.dbuser+')');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('it should remove invalid params', function(done) {
|
||||||
|
var config = {
|
||||||
|
version: '1.3.0'
|
||||||
|
};
|
||||||
|
var req = {
|
||||||
|
headers: {
|
||||||
|
host:'localhost'
|
||||||
|
},
|
||||||
|
query: {
|
||||||
|
non_included: 'toberemoved',
|
||||||
|
api_key: 'test',
|
||||||
|
style: 'override',
|
||||||
|
config: config
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var res = {};
|
||||||
|
|
||||||
|
cleanUpQueryParams(prepareRequest(req), prepareResponse(res), function (err) {
|
||||||
|
if ( err ) {
|
||||||
|
return done(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
var query = res.locals;
|
||||||
|
assert.deepEqual(config, query.config);
|
||||||
|
assert.equal('test', query.api_key);
|
||||||
|
assert.equal(undefined, query.non_included);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
@ -1,129 +0,0 @@
|
|||||||
var assert = require('assert');
|
|
||||||
var _ = require('underscore');
|
|
||||||
require('../../support/test_helper');
|
|
||||||
|
|
||||||
var RedisPool = require('redis-mpool');
|
|
||||||
var cartodbRedis = require('cartodb-redis');
|
|
||||||
var PgConnection = require('../../../lib/cartodb/backends/pg_connection');
|
|
||||||
var AuthApi = require('../../../lib/cartodb/api/auth_api');
|
|
||||||
var TemplateMaps = require('../../../lib/cartodb/backends/template_maps');
|
|
||||||
|
|
||||||
var BaseController = require('../../../lib/cartodb/controllers/base');
|
|
||||||
var windshaft = require('windshaft');
|
|
||||||
|
|
||||||
describe('req2params', function() {
|
|
||||||
|
|
||||||
var test_user = _.template(global.environment.postgres_auth_user, {user_id:1});
|
|
||||||
var test_pubuser = global.environment.postgres.user;
|
|
||||||
var test_database = test_user + '_db';
|
|
||||||
|
|
||||||
|
|
||||||
var baseController;
|
|
||||||
before(function() {
|
|
||||||
var redisPool = new RedisPool(global.environment.redis);
|
|
||||||
var mapStore = new windshaft.storage.MapStore();
|
|
||||||
var metadataBackend = cartodbRedis({pool: redisPool});
|
|
||||||
var pgConnection = new PgConnection(metadataBackend);
|
|
||||||
var templateMaps = new TemplateMaps(redisPool);
|
|
||||||
var authApi = new AuthApi(pgConnection, metadataBackend, mapStore, templateMaps);
|
|
||||||
|
|
||||||
baseController = new BaseController(authApi, pgConnection);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
it('can be found in server_options', function(){
|
|
||||||
assert.ok(_.isFunction(baseController.req2params));
|
|
||||||
});
|
|
||||||
|
|
||||||
function prepareRequest(req) {
|
|
||||||
req.profiler = {
|
|
||||||
done: function() {}
|
|
||||||
};
|
|
||||||
req.context = { user: 'localhost' };
|
|
||||||
return req;
|
|
||||||
}
|
|
||||||
|
|
||||||
it('cleans up request', function(done){
|
|
||||||
var req = {headers: { host:'localhost' }, query: {dbuser:'hacker',dbname:'secret'}};
|
|
||||||
baseController.req2params(prepareRequest(req), function(err, req) {
|
|
||||||
if ( err ) { done(err); return; }
|
|
||||||
assert.ok(_.isObject(req.query), 'request has query');
|
|
||||||
assert.ok(!req.query.hasOwnProperty('dbuser'), 'dbuser was removed from query');
|
|
||||||
assert.ok(req.hasOwnProperty('params'), 'request has params');
|
|
||||||
assert.ok(!req.params.hasOwnProperty('interactivity'), 'request params do not have interactivity');
|
|
||||||
assert.equal(req.params.dbname, test_database, 'could forge dbname: '+ req.params.dbname);
|
|
||||||
assert.ok(req.params.dbuser === test_pubuser, 'could inject dbuser ('+req.params.dbuser+')');
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('sets dbname from redis metadata', function(done){
|
|
||||||
var req = {headers: { host:'localhost' }, query: {} };
|
|
||||||
baseController.req2params(prepareRequest(req), function(err, req) {
|
|
||||||
if ( err ) { done(err); return; }
|
|
||||||
assert.ok(_.isObject(req.query), 'request has query');
|
|
||||||
assert.ok(!req.query.hasOwnProperty('dbuser'), 'dbuser was removed from query');
|
|
||||||
assert.ok(req.hasOwnProperty('params'), 'request has params');
|
|
||||||
assert.ok(!req.params.hasOwnProperty('interactivity'), 'request params do not have interactivity');
|
|
||||||
assert.equal(req.params.dbname, test_database);
|
|
||||||
assert.ok(req.params.dbuser === test_pubuser, 'could inject dbuser ('+req.params.dbuser+')');
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('sets also dbuser for authenticated requests', function(done){
|
|
||||||
var req = {headers: { host:'localhost' }, query: {map_key: '1234'} };
|
|
||||||
baseController.req2params(prepareRequest(req), function(err, req) {
|
|
||||||
if ( err ) { done(err); return; }
|
|
||||||
assert.ok(_.isObject(req.query), 'request has query');
|
|
||||||
assert.ok(!req.query.hasOwnProperty('dbuser'), 'dbuser was removed from query');
|
|
||||||
assert.ok(req.hasOwnProperty('params'), 'request has params');
|
|
||||||
assert.ok(!req.params.hasOwnProperty('interactivity'), 'request params do not have interactivity');
|
|
||||||
assert.equal(req.params.dbname, test_database);
|
|
||||||
assert.equal(req.params.dbuser, test_user);
|
|
||||||
|
|
||||||
req = {
|
|
||||||
headers: {
|
|
||||||
host:'localhost'
|
|
||||||
},
|
|
||||||
query: {
|
|
||||||
map_key: '1235'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
baseController.req2params(prepareRequest(req), function(err, req) {
|
|
||||||
// wrong key resets params to no user
|
|
||||||
assert.ok(req.params.dbuser === test_pubuser, 'could inject dbuser ('+req.params.dbuser+')');
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('it should remove invalid params', function(done) {
|
|
||||||
var config = {
|
|
||||||
version: '1.3.0'
|
|
||||||
};
|
|
||||||
var req = {
|
|
||||||
headers: {
|
|
||||||
host:'localhost'
|
|
||||||
},
|
|
||||||
query: {
|
|
||||||
non_included: 'toberemoved',
|
|
||||||
api_key: 'test',
|
|
||||||
style: 'override',
|
|
||||||
config: config
|
|
||||||
}
|
|
||||||
};
|
|
||||||
baseController.req2params(prepareRequest(req), function(err, req) {
|
|
||||||
if (err) {
|
|
||||||
return done(err);
|
|
||||||
}
|
|
||||||
var query = req.params;
|
|
||||||
assert.deepEqual(config, query.config);
|
|
||||||
assert.equal('test', query.api_key);
|
|
||||||
assert.equal(undefined, query.non_included);
|
|
||||||
assert.equal(undefined, query.style);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
Loading…
Reference in New Issue
Block a user