Apply automatic eslint fixes

This commit is contained in:
Daniel García Aubert 2019-10-21 19:07:24 +02:00
parent 593d9e40f6
commit 4d70ac0894
246 changed files with 11782 additions and 11454 deletions

75
app.js
View File

@ -54,7 +54,7 @@ var availableEnvironments = {
}; };
// sanity check // sanity check
if (!availableEnvironments[ENVIRONMENT]){ if (!availableEnvironments[ENVIRONMENT]) {
logError('node app.js [environment]'); logError('node app.js [environment]');
logError('environments: %s', Object.keys(availableEnvironments).join(', ')); logError('environments: %s', Object.keys(availableEnvironments).join(', '));
process.exit(1); process.exit(1);
@ -76,27 +76,26 @@ var agentOptions = _.defaults(global.environment.httpAgent || {}, {
http.globalAgent = new http.Agent(agentOptions); http.globalAgent = new http.Agent(agentOptions);
https.globalAgent = new https.Agent(agentOptions); https.globalAgent = new https.Agent(agentOptions);
global.log4js = require('log4js'); global.log4js = require('log4js');
var log4jsConfig = { var log4jsConfig = {
appenders: [], appenders: [],
replaceConsole: true replaceConsole: true
}; };
if ( global.environment.log_filename ) { if (global.environment.log_filename) {
var logFilename = path.resolve(global.environment.log_filename); var logFilename = path.resolve(global.environment.log_filename);
var logDirectory = path.dirname(logFilename); var logDirectory = path.dirname(logFilename);
if (!fs.existsSync(logDirectory)) { if (!fs.existsSync(logDirectory)) {
logError("Log filename directory does not exist: " + logDirectory); logError('Log filename directory does not exist: ' + logDirectory);
process.exit(1); process.exit(1);
} }
log("Logs will be written to " + logFilename); log('Logs will be written to ' + logFilename);
log4jsConfig.appenders.push( log4jsConfig.appenders.push(
{ type: "file", absolute: true, filename: logFilename } { type: 'file', absolute: true, filename: logFilename }
); );
} else { } else {
log4jsConfig.appenders.push( log4jsConfig.appenders.push(
{ type: "console", layout: { type:'basic' } } { type: 'console', layout: { type: 'basic' } }
); );
} }
@ -118,13 +117,13 @@ var backlog = global.environment.maxConnections || 128;
var listener = server.listen(serverOptions.bind.port, serverOptions.bind.host, backlog); var listener = server.listen(serverOptions.bind.port, serverOptions.bind.host, backlog);
var version = require("./package").version; var version = require('./package').version;
listener.on('listening', function() { listener.on('listening', function () {
log("Using Node.js %s", process.version); log('Using Node.js %s', process.version);
log('Using configuration file "%s"', configurationFile); log('Using configuration file "%s"', configurationFile);
log( log(
"Windshaft tileserver %s started on %s:%s PID=%d (%s)", 'Windshaft tileserver %s started on %s:%s PID=%d (%s)',
version, serverOptions.bind.host, serverOptions.bind.port, process.pid, ENVIRONMENT version, serverOptions.bind.host, serverOptions.bind.port, process.pid, ENVIRONMENT
); );
}); });
@ -163,15 +162,15 @@ setInterval(function cpuUsageMetrics () {
previousCPUUsage = CPUUsage; previousCPUUsage = CPUUsage;
}, 5000); }, 5000);
setInterval(function() { setInterval(function () {
var memoryUsage = process.memoryUsage(); var memoryUsage = process.memoryUsage();
Object.keys(memoryUsage).forEach(function(k) { Object.keys(memoryUsage).forEach(function (k) {
global.statsClient.gauge('windshaft.memory.' + k, memoryUsage[k]); global.statsClient.gauge('windshaft.memory.' + k, memoryUsage[k]);
}); });
}, 5000); }, 5000);
process.on('SIGHUP', function() { process.on('SIGHUP', function () {
global.log4js.clearAndShutdownAppenders(function() { global.log4js.clearAndShutdownAppenders(function () {
global.log4js.configure(log4jsConfig); global.log4js.configure(log4jsConfig);
global.logger = global.log4js.getLogger(); global.logger = global.log4js.getLogger();
log('Log files reloaded'); log('Log files reloaded');
@ -179,12 +178,12 @@ process.on('SIGHUP', function() {
}); });
if (global.gc) { if (global.gc) {
var gcInterval = Number.isFinite(global.environment.gc_interval) ? var gcInterval = Number.isFinite(global.environment.gc_interval)
global.environment.gc_interval : ? global.environment.gc_interval
10000; : 10000;
if (gcInterval > 0) { if (gcInterval > 0) {
setInterval(function gcForcedCycle() { setInterval(function gcForcedCycle () {
global.gc(); global.gc();
}, gcInterval); }, gcInterval);
} }
@ -206,24 +205,24 @@ function getGCTypeValue (type) {
let value; let value;
switch (type) { switch (type) {
case 1: case 1:
value = 'Scavenge'; value = 'Scavenge';
break; break;
case 2: case 2:
value = 'MarkSweepCompact'; value = 'MarkSweepCompact';
break; break;
case 4: case 4:
value = 'IncrementalMarking'; value = 'IncrementalMarking';
break; break;
case 8: case 8:
value = 'ProcessWeakCallbacks'; value = 'ProcessWeakCallbacks';
break; break;
case 15: case 15:
value = 'All'; value = 'All';
break; break;
default: default:
value = 'Unkown'; value = 'Unkown';
break; break;
} }
return value; return value;
@ -231,7 +230,7 @@ function getGCTypeValue (type) {
addHandlers(listener, global.logger, 45000); addHandlers(listener, global.logger, 45000);
function addHandlers(listener, logger, killTimeout) { function addHandlers (listener, logger, killTimeout) {
process.on('uncaughtException', exitProcess(listener, logger, killTimeout)); process.on('uncaughtException', exitProcess(listener, logger, killTimeout));
process.on('unhandledRejection', exitProcess(listener, logger, killTimeout)); process.on('unhandledRejection', exitProcess(listener, logger, killTimeout));
process.on('ENOMEM', exitProcess(listener, logger, killTimeout)); process.on('ENOMEM', exitProcess(listener, logger, killTimeout));

View File

@ -232,7 +232,6 @@ module.exports = class ApiRouter {
} }
}; };
function createTemplateMaps ({ redisPool, surrogateKeysCache }) { function createTemplateMaps ({ redisPool, surrogateKeysCache }) {
const templateMaps = new TemplateMaps(redisPool, { const templateMaps = new TemplateMaps(redisPool, {
max_user_templates: global.environment.maxUserTemplates max_user_templates: global.environment.maxUserTemplates
@ -240,12 +239,12 @@ function createTemplateMaps ({ redisPool, surrogateKeysCache }) {
function invalidateNamedMap (owner, templateName) { function invalidateNamedMap (owner, templateName) {
var startTime = Date.now(); var startTime = Date.now();
surrogateKeysCache.invalidate(new NamedMapsCacheEntry(owner, templateName), function(err) { surrogateKeysCache.invalidate(new NamedMapsCacheEntry(owner, templateName), function (err) {
var logMessage = JSON.stringify({ var logMessage = JSON.stringify({
username: owner, username: owner,
type: 'named_map_invalidation', type: 'named_map_invalidation',
elapsed: Date.now() - startTime, elapsed: Date.now() - startTime,
error: !!err ? JSON.stringify(err.message) : undefined error: err ? JSON.stringify(err.message) : undefined
}); });
if (err) { if (err) {
global.logger.warn(logMessage); global.logger.warn(logMessage);
@ -255,15 +254,14 @@ function createTemplateMaps ({ redisPool, surrogateKeysCache }) {
}); });
} }
['update', 'delete'].forEach(function (eventType) {
['update', 'delete'].forEach(function(eventType) {
templateMaps.on(eventType, invalidateNamedMap); templateMaps.on(eventType, invalidateNamedMap);
}); });
return templateMaps; return templateMaps;
} }
function createSurrogateKeysCacheBackends(serverOptions) { function createSurrogateKeysCacheBackends (serverOptions) {
var cacheBackends = []; var cacheBackends = [];
if (serverOptions.varnish_purge_enabled) { if (serverOptions.varnish_purge_enabled) {
@ -283,13 +281,12 @@ function createSurrogateKeysCacheBackends(serverOptions) {
} }
const timeoutErrorTilePath = __dirname + '/../../assets/render-timeout-fallback.png'; const timeoutErrorTilePath = __dirname + '/../../assets/render-timeout-fallback.png';
const timeoutErrorTile = require('fs').readFileSync(timeoutErrorTilePath, {encoding: null}); const timeoutErrorTile = require('fs').readFileSync(timeoutErrorTilePath, { encoding: null });
function createRendererFactory ({ redisPool, serverOptions, environmentOptions }) { function createRendererFactory ({ redisPool, serverOptions, environmentOptions }) {
var onTileErrorStrategy; var onTileErrorStrategy;
if (environmentOptions.enabledFeatures.onTileErrorStrategy !== false) { if (environmentOptions.enabledFeatures.onTileErrorStrategy !== false) {
onTileErrorStrategy = function onTileErrorStrategy$TimeoutTile(err, tile, headers, stats, format, callback) { onTileErrorStrategy = function onTileErrorStrategy$TimeoutTile (err, tile, headers, stats, format, callback) {
function isRenderTimeoutError (err) { function isRenderTimeoutError (err) {
return err.message === 'Render timed out'; return err.message === 'Render timed out';
} }
@ -308,7 +305,7 @@ function createRendererFactory ({ redisPool, serverOptions, environmentOptions }
if (isTimeoutError(err) && isRasterFormat(format)) { if (isTimeoutError(err) && isRasterFormat(format)) {
return callback(null, timeoutErrorTile, { return callback(null, timeoutErrorTile, {
'Content-Type': 'image/png', 'Content-Type': 'image/png'
}, {}); }, {});
} else { } else {
return callback(err, tile, headers, stats); return callback(err, tile, headers, stats);

View File

@ -48,10 +48,10 @@ function createPGClient () {
}; };
} }
function getDataFromQuery({ queryTemplate, key }) { function getDataFromQuery ({ queryTemplate, key }) {
const readOnlyTransactionOn = true; const readOnlyTransactionOn = true;
return function getCatalogMiddleware(req, res, next) { return function getCatalogMiddleware (req, res, next) {
const { pg, user } = res.locals; const { pg, user } = res.locals;
const sql = queryTemplate({ _username: user }); const sql = queryTemplate({ _username: user });
@ -88,21 +88,21 @@ function prepareResponse () {
return analysis; return analysis;
}) })
.sort((analysisA, analysisB) => { .sort((analysisA, analysisB) => {
if (!!analysisA.table && !!analysisB.table) { if (!!analysisA.table && !!analysisB.table) {
return analysisB.table.size - analysisA.table.size; return analysisB.table.size - analysisA.table.size;
} }
if (analysisA.table) {
return -1;
}
if (analysisB.table) {
return 1;
}
if (!!analysisA.table) {
return -1; return -1;
} });
if (!!analysisB.table) {
return 1;
}
return -1;
});
res.statusCode = 200; res.statusCode = 200;
res.body = { catalog: analysisCatalog }; res.body = { catalog: analysisCatalog };
@ -112,7 +112,7 @@ function prepareResponse () {
} }
function unauthorizedError () { function unauthorizedError () {
return function unathorizedErrorMiddleware(err, req, res, next) { return function unathorizedErrorMiddleware (err, req, res, next) {
if (err.message.match(/permission\sdenied/)) { if (err.message.match(/permission\sdenied/)) {
err = new Error('Unauthorized'); err = new Error('Unauthorized');
err.http_status = 401; err.http_status = 401;

View File

@ -35,7 +35,7 @@ module.exports = class AnalysisLayergroupController {
}; };
function analysisNodeStatus (analysisStatusBackend) { function analysisNodeStatus (analysisStatusBackend) {
return function analysisNodeStatusMiddleware(req, res, next) { return function analysisNodeStatusMiddleware (req, res, next) {
const { nodeId } = req.params; const { nodeId } = req.params;
const dbParams = dbParamsFromResLocals(res.locals); const dbParams = dbParamsFromResLocals(res.locals);

View File

@ -87,7 +87,7 @@ module.exports = class AnonymousMapController {
checkJsonContentType(), checkJsonContentType(),
checkCreateLayergroup(), checkCreateLayergroup(),
prepareAdapterMapConfig(this.mapConfigAdapter), prepareAdapterMapConfig(this.mapConfigAdapter),
createLayergroup ( createLayergroup(
this.mapBackend, this.mapBackend,
this.userLimitsBackend, this.userLimitsBackend,
this.pgConnection, this.pgConnection,
@ -130,7 +130,7 @@ function checkCreateLayergroup () {
} }
function prepareAdapterMapConfig (mapConfigAdapter) { function prepareAdapterMapConfig (mapConfigAdapter) {
return function prepareAdapterMapConfigMiddleware(req, res, next) { return function prepareAdapterMapConfigMiddleware (req, res, next) {
const requestMapConfig = req.body; const requestMapConfig = req.body;
const { user, api_key } = res.locals; const { user, api_key } = res.locals;
@ -155,25 +155,24 @@ function prepareAdapterMapConfig (mapConfigAdapter) {
}; };
mapConfigAdapter.getMapConfig(user, mapConfigAdapter.getMapConfig(user,
requestMapConfig, requestMapConfig,
params, params,
context, context,
(err, requestMapConfig, stats = { overviewsAddedToMapconfig : false }) => { (err, requestMapConfig, stats = { overviewsAddedToMapconfig: false }) => {
req.profiler.done('anonymous.getMapConfig'); req.profiler.done('anonymous.getMapConfig');
stats.mapType = 'anonymous'; stats.mapType = 'anonymous';
req.profiler.add(stats); req.profiler.add(stats);
if (err) {
return next(err);
}
if (err) { req.body = requestMapConfig;
return next(err); res.locals.context = context;
}
req.body = requestMapConfig; next();
res.locals.context = context; });
next();
});
}; };
} }
@ -186,8 +185,13 @@ function createLayergroup (mapBackend, userLimitsBackend, pgConnection, affected
const { dbuser, dbname, dbpassword, dbhost, dbport } = res.locals; const { dbuser, dbname, dbpassword, dbhost, dbport } = res.locals;
const params = { const params = {
cache_buster, api_key, cache_buster,
dbuser, dbname, dbpassword, dbhost, dbport api_key,
dbuser,
dbname,
dbpassword,
dbhost,
dbport
}; };
const datasource = context.datasource || Datasource.EmptyDatasource(); const datasource = context.datasource || Datasource.EmptyDatasource();

View File

@ -70,8 +70,13 @@ function getFeatureAttributes (attributesBackend) {
const params = { const params = {
token, token,
dbuser, dbname, dbpassword, dbhost, dbport, dbuser,
layer, fid dbname,
dbpassword,
dbhost,
dbport,
layer,
fid
}; };
attributesBackend.getFeatureAttributes(mapConfigProvider, params, false, (err, tile, stats = {}) => { attributesBackend.getFeatureAttributes(mapConfigProvider, params, false, (err, tile, stats = {}) => {

View File

@ -44,7 +44,7 @@ module.exports = class AggregatedFeaturesLayergroupController {
dbConnSetup(this.pgConnection), dbConnSetup(this.pgConnection),
// TODO: create its rate limit // TODO: create its rate limit
rateLimit(this.userLimitsBackend, RATE_LIMIT_ENDPOINTS_GROUPS.ATTRIBUTES), rateLimit(this.userLimitsBackend, RATE_LIMIT_ENDPOINTS_GROUPS.ATTRIBUTES),
cleanUpQueryParams([ 'aggregation' ]), cleanUpQueryParams(['aggregation']),
createMapStoreMapConfigProvider( createMapStoreMapConfigProvider(
this.mapStore, this.mapStore,
this.userLimitsBackend, this.userLimitsBackend,
@ -71,9 +71,16 @@ function getClusteredFeatures (clusterBackend) {
const { aggregation } = req.query; const { aggregation } = req.query;
const params = { const params = {
user, token, user,
dbuser, dbname, dbpassword, dbhost, dbport, token,
layer, zoom, clusterId, dbuser,
dbname,
dbpassword,
dbhost,
dbport,
layer,
zoom,
clusterId,
aggregation aggregation
}; };

View File

@ -22,10 +22,10 @@ const ALLOWED_DATAVIEW_QUERY_PARAMS = [
'end', // number 'end', // number
'column_type', // string 'column_type', // string
'bins', // number 'bins', // number
'aggregation', //string 'aggregation', // string
'offset', // number 'offset', // number
'q', // widgets search 'q', // widgets search
'categories', // number 'categories' // number
]; ];
module.exports = class DataviewLayergroupController { module.exports = class DataviewLayergroupController {

View File

@ -21,7 +21,7 @@ const DEFAULT_ZOOM_CENTER = {
} }
}; };
function numMapper(n) { function numMapper (n) {
return +n; return +n;
} }
@ -60,7 +60,8 @@ module.exports = class PreviewTemplateController {
checkStaticImageFormat(), checkStaticImageFormat(),
namedMapProvider({ namedMapProvider({
namedMapProviderCache: this.namedMapProviderCache, namedMapProviderCache: this.namedMapProviderCache,
label: 'STATIC_VIZ_MAP', forcedFormat: 'png' label: 'STATIC_VIZ_MAP',
forcedFormat: 'png'
}), }),
getTemplate({ label: 'STATIC_VIZ_MAP' }), getTemplate({ label: 'STATIC_VIZ_MAP' }),
prepareLayerFilterFromPreviewLayers({ prepareLayerFilterFromPreviewLayers({
@ -109,8 +110,8 @@ function prepareLayerFilterFromPreviewLayers ({ namedMapProviderCache, label })
var layerVisibilityFilter = []; var layerVisibilityFilter = [];
template.layergroup.layers.forEach((layer, index) => { template.layergroup.layers.forEach((layer, index) => {
if (previewLayers[''+index] !== false && previewLayers[layer.id] !== false) { if (previewLayers['' + index] !== false && previewLayers[layer.id] !== false) {
layerVisibilityFilter.push(''+index); layerVisibilityFilter.push('' + index);
} }
}); });
@ -123,9 +124,17 @@ function prepareLayerFilterFromPreviewLayers ({ namedMapProviderCache, label })
const { template_id, format } = req.params; const { template_id, format } = req.params;
const params = { const params = {
user, token, cache_buster, api_key, user,
dbuser, dbname, dbpassword, dbhost, dbport, token,
template_id, format cache_buster,
api_key,
dbuser,
dbname,
dbpassword,
dbhost,
dbport,
template_id,
format
}; };
// overwrites 'all' default filter // overwrites 'all' default filter
@ -146,7 +155,7 @@ function prepareLayerFilterFromPreviewLayers ({ namedMapProviderCache, label })
} }
function getStaticImageOptions ({ tablesExtentBackend }) { function getStaticImageOptions ({ tablesExtentBackend }) {
return function getStaticImageOptionsMiddleware(req, res, next) { return function getStaticImageOptionsMiddleware (req, res, next) {
const { user, mapConfigProvider, template } = res.locals; const { user, mapConfigProvider, template } = res.locals;
const { zoom, lon, lat, bbox } = req.query; const { zoom, lon, lat, bbox } = req.query;
const params = { zoom, lon, lat, bbox }; const params = { zoom, lon, lat, bbox };
@ -248,7 +257,7 @@ function getImageOptionsFromBoundingBox (bbox = '') {
} }
} }
function getImage({ previewBackend, label }) { function getImage ({ previewBackend, label }) {
return function getImageMiddleware (req, res, next) { return function getImageMiddleware (req, res, next) {
const { imageOpts, mapConfigProvider } = res.locals; const { imageOpts, mapConfigProvider } = res.locals;
const { zoom, center, bbox } = imageOpts; const { zoom, center, bbox } = imageOpts;
@ -298,7 +307,7 @@ function getImage({ previewBackend, label }) {
} }
function setContentTypeHeader () { function setContentTypeHeader () {
return function setContentTypeHeaderMiddleware(req, res, next) { return function setContentTypeHeaderMiddleware (req, res, next) {
const format = req.params.format === 'jpg' ? 'jpeg' : 'png'; const format = req.params.format === 'jpg' ? 'jpeg' : 'png';
res.set('Content-Type', `image/${format}`); res.set('Content-Type', `image/${format}`);
@ -312,7 +321,7 @@ function incrementMapViewsError (ctx) {
} }
function incrementMapViews ({ metadataBackend }) { function incrementMapViews ({ metadataBackend }) {
return function incrementMapViewsMiddleware(req, res, next) { return function incrementMapViewsMiddleware (req, res, next) {
const { user, mapConfigProvider } = res.locals; const { user, mapConfigProvider } = res.locals;
mapConfigProvider.getMapConfig((err, mapConfig) => { mapConfigProvider.getMapConfig((err, mapConfig) => {
@ -334,7 +343,7 @@ function incrementMapViews ({ metadataBackend }) {
}; };
} }
function templateZoomCenter(view) { function templateZoomCenter (view) {
if (view.zoom !== undefined && view.center) { if (view.zoom !== undefined && view.center) {
return { return {
zoom: view.zoom, zoom: view.zoom,
@ -344,7 +353,7 @@ function templateZoomCenter(view) {
return false; return false;
} }
function templateBounds(view) { function templateBounds (view) {
if (view.bounds) { if (view.bounds) {
var hasAllBounds = ['west', 'south', 'east', 'north'].every(prop => Number.isFinite(view.bounds[prop])); var hasAllBounds = ['west', 'south', 'east', 'north'].every(prop => Number.isFinite(view.bounds[prop]));

View File

@ -51,8 +51,8 @@ module.exports = class TileLayergroupController {
// that performs only the middlewares of the first path that matches // that performs only the middlewares of the first path that matches
// for that we use one array to group all paths. // for that we use one array to group all paths.
mapRouter.get([ mapRouter.get([
`/:token/:z/:x/:y@:scale_factor?x.:format`, // 1 '/:token/:z/:x/:y@:scale_factor?x.:format', // 1
`/:token/:z/:x/:y.:format`, // 2 '/:token/:z/:x/:y.:format', // 2
`/:token${not('static')}/:layer/:z/:x/:y.(:format)` `/:token${not('static')}/:layer/:z/:x/:y.(:format)`
], this.middlewares()); ], this.middlewares());
} }
@ -90,7 +90,7 @@ function parseFormat (format = '') {
return SUPPORTED_FORMATS[prettyFormat] ? prettyFormat : 'invalid'; return SUPPORTED_FORMATS[prettyFormat] ? prettyFormat : 'invalid';
} }
function getStatusCode(tile, format){ function getStatusCode (tile, format) {
return tile.length === 0 && format === 'mvt' ? 204 : 200; return tile.length === 0 && format === 'mvt' ? 204 : 200;
} }
@ -149,9 +149,8 @@ function incrementErrorMetrics (statsClient) {
function tileError () { function tileError () {
return function tileErrorMiddleware (err, req, res, next) { return function tileErrorMiddleware (err, req, res, next) {
// See https://github.com/Vizzuality/Windshaft-cartodb/issues/68 // See https://github.com/Vizzuality/Windshaft-cartodb/issues/68
let errMsg = err.message ? ( '' + err.message ) : ( '' + err ); let errMsg = err.message ? ('' + err.message) : ('' + err);
// Rewrite mapnik parsing errors to start with layer number // Rewrite mapnik parsing errors to start with layer number
const matches = errMsg.match("(.*) in style 'layer([0-9]+)'"); const matches = errMsg.match("(.*) in style 'layer([0-9]+)'");

View File

@ -9,8 +9,8 @@ module.exports = function authorize (authBackend) {
return next(err); return next(err);
} }
if(!authorized) { if (!authorized) {
err = new Error("Sorry, you are unauthorized (permission denied)"); err = new Error('Sorry, you are unauthorized (permission denied)');
err.http_status = 403; err.http_status = 403;
return next(err); return next(err);
} }

View File

@ -48,7 +48,7 @@ module.exports = function setCacheControlHeader ({
return next(); return next();
} }
const directives = [ 'public' ]; const directives = ['public'];
if (everyAffectedTableCanBeInvalidated(affectedTables)) { if (everyAffectedTableCanBeInvalidated(affectedTables)) {
directives.push(`max-age=${ttl}`); directives.push(`max-age=${ttl}`);

View File

@ -1,7 +1,7 @@
'use strict'; 'use strict';
module.exports = function checkJsonContentType () { module.exports = function checkJsonContentType () {
return function checkJsonContentTypeMiddleware(req, res, next) { return function checkJsonContentTypeMiddleware (req, res, next) {
if (req.method === 'POST' && !req.is('application/json')) { if (req.method === 'POST' && !req.is('application/json')) {
return next(new Error('POST data must be of type application/json')); return next(new Error('POST data must be of type application/json'));
} }

View File

@ -4,7 +4,7 @@ const VALID_IMAGE_FORMATS = ['png', 'jpg'];
module.exports = function checkStaticImageFormat () { module.exports = function checkStaticImageFormat () {
return function checkStaticImageFormatMiddleware (req, res, next) { return function checkStaticImageFormatMiddleware (req, res, next) {
if(!VALID_IMAGE_FORMATS.includes(req.params.format)) { if (!VALID_IMAGE_FORMATS.includes(req.params.format)) {
return next(new Error(`Unsupported image format "${req.params.format}"`)); return next(new Error(`Unsupported image format "${req.params.format}"`));
} }

View File

@ -13,8 +13,8 @@ module.exports = function cors () {
headers.push('Content-Type'); headers.push('Content-Type');
} }
res.set("Access-Control-Allow-Origin", "*"); res.set('Access-Control-Allow-Origin', '*');
res.set("Access-Control-Allow-Headers", headers.join(', ')); res.set('Access-Control-Allow-Headers', headers.join(', '));
next(); next();
}; };

View File

@ -3,24 +3,24 @@
const basicAuth = require('basic-auth'); const basicAuth = require('basic-auth');
module.exports = function credentials () { module.exports = function credentials () {
return function credentialsMiddleware(req, res, next) { return function credentialsMiddleware (req, res, next) {
const apikeyCredentials = getApikeyCredentialsFromRequest(req); const apikeyCredentials = getApikeyCredentialsFromRequest(req);
res.locals.api_key = apikeyCredentials.token; res.locals.api_key = apikeyCredentials.token;
res.locals.basicAuthUsername = apikeyCredentials.username; res.locals.basicAuthUsername = apikeyCredentials.username;
res.set('vary', 'Authorization'); //Honor Authorization header when caching. res.set('vary', 'Authorization'); // Honor Authorization header when caching.
return next(); return next();
}; };
}; };
function getApikeyCredentialsFromRequest(req) { function getApikeyCredentialsFromRequest (req) {
let apikeyCredentials = { let apikeyCredentials = {
token: null, token: null,
username: null, username: null
}; };
for (let getter of apikeyGetters) { for (const getter of apikeyGetters) {
apikeyCredentials = getter(req); apikeyCredentials = getter(req);
if (apikeyTokenFound(apikeyCredentials)) { if (apikeyTokenFound(apikeyCredentials)) {
break; break;
@ -33,10 +33,10 @@ function getApikeyCredentialsFromRequest(req) {
const apikeyGetters = [ const apikeyGetters = [
getApikeyTokenFromHeaderAuthorization, getApikeyTokenFromHeaderAuthorization,
getApikeyTokenFromRequestQueryString, getApikeyTokenFromRequestQueryString,
getApikeyTokenFromRequestBody, getApikeyTokenFromRequestBody
]; ];
function getApikeyTokenFromHeaderAuthorization(req) { function getApikeyTokenFromHeaderAuthorization (req) {
const credentials = basicAuth(req); const credentials = basicAuth(req);
if (credentials) { if (credentials) {
@ -47,12 +47,12 @@ function getApikeyTokenFromHeaderAuthorization(req) {
} else { } else {
return { return {
username: null, username: null,
token: null, token: null
}; };
} }
} }
function getApikeyTokenFromRequestQueryString(req) { function getApikeyTokenFromRequestQueryString (req) {
let token = null; let token = null;
if (req.query && req.query.api_key) { if (req.query && req.query.api_key) {
@ -63,11 +63,11 @@ function getApikeyTokenFromRequestQueryString(req) {
return { return {
username: null, username: null,
token: token, token: token
}; };
} }
function getApikeyTokenFromRequestBody(req) { function getApikeyTokenFromRequestBody (req) {
let token = null; let token = null;
if (req.body && req.body.api_key) { if (req.body && req.body.api_key) {
@ -78,10 +78,10 @@ function getApikeyTokenFromRequestBody(req) {
return { return {
username: null, username: null,
token: token, token: token
}; };
} }
function apikeyTokenFound(apikey) { function apikeyTokenFound (apikey) {
return !!apikey && !!apikey.token; return !!apikey && !!apikey.token;
} }

View File

@ -10,7 +10,7 @@ module.exports = function dbConnSetup (pgConnection) {
req.profiler.done('dbConnSetup'); req.profiler.done('dbConnSetup');
if (err) { if (err) {
if (err.message && -1 !== err.message.indexOf('name not found')) { if (err.message && err.message.indexOf('name not found') !== -1) {
err.http_status = 404; err.http_status = 404;
} }

View File

@ -52,10 +52,10 @@ function isTimeoutError (errorTypes) {
return errorTypes.renderTimeoutError || errorTypes.datasourceTimeoutError; return errorTypes.renderTimeoutError || errorTypes.datasourceTimeoutError;
} }
function getErrorTypes(error) { function getErrorTypes (error) {
return { return {
renderTimeoutError: isRenderTimeoutError(error), renderTimeoutError: isRenderTimeoutError(error),
datasourceTimeoutError: isDatasourceTimeoutError(error), datasourceTimeoutError: isDatasourceTimeoutError(error)
}; };
} }
@ -99,9 +99,9 @@ function populateLimitErrors (errors) {
}); });
} }
function findStatusCode(err) { function findStatusCode (err) {
var statusCode; var statusCode;
if ( err.http_status ) { if (err.http_status) {
statusCode = err.http_status; statusCode = err.http_status;
} else { } else {
statusCode = statusFromErrorMessage('' + err); statusCode = statusFromErrorMessage('' + err);
@ -111,34 +111,30 @@ function findStatusCode(err) {
module.exports.findStatusCode = findStatusCode; module.exports.findStatusCode = findStatusCode;
function statusFromErrorMessage(errMsg) { function statusFromErrorMessage (errMsg) {
// Find an appropriate statusCode based on message // Find an appropriate statusCode based on message
// jshint maxcomplexity:7 // jshint maxcomplexity:7
var statusCode = 400; var statusCode = 400;
if ( -1 !== errMsg.indexOf('permission denied') ) { if (errMsg.indexOf('permission denied') !== -1) {
statusCode = 403; statusCode = 403;
} } else if (errMsg.indexOf('authentication failed') !== -1) {
else if ( -1 !== errMsg.indexOf('authentication failed') ) {
statusCode = 403; statusCode = 403;
} } else if (errMsg.match(/Postgis Plugin.*[\s|\n].*column.*does not exist/)) {
else if (errMsg.match(/Postgis Plugin.*[\s|\n].*column.*does not exist/)) {
statusCode = 400; statusCode = 400;
} } else if (errMsg.indexOf('does not exist') !== -1) {
else if ( -1 !== errMsg.indexOf('does not exist') ) { if (errMsg.indexOf(' role ') !== -1) {
if ( -1 !== errMsg.indexOf(' role ') ) {
statusCode = 403; // role 'xxx' does not exist statusCode = 403; // role 'xxx' does not exist
} else if ( errMsg.match(/function .* does not exist/) ) { } else if (errMsg.match(/function .* does not exist/)) {
statusCode = 400; // invalid SQL (SQL function does not exist) statusCode = 400; // invalid SQL (SQL function does not exist)
} else { } else {
statusCode = 404; statusCode = 404;
} }
} }
return statusCode; return statusCode;
} }
function errorMessage(err) { function errorMessage (err) {
// See https://github.com/Vizzuality/Windshaft-cartodb/issues/68 // See https://github.com/Vizzuality/Windshaft-cartodb/issues/68
var message = (_.isString(err) ? err : err.message) || 'Unknown error'; var message = (_.isString(err) ? err : err.message) || 'Unknown error';
@ -147,7 +143,7 @@ function errorMessage(err) {
module.exports.errorMessage = errorMessage; module.exports.errorMessage = errorMessage;
function stripConnectionInfo(message) { function stripConnectionInfo (message) {
// Strip connection info, if any // Strip connection info, if any
return message return message
// See https://github.com/CartoDB/Windshaft/issues/173 // See https://github.com/CartoDB/Windshaft/issues/173
@ -168,13 +164,13 @@ function shouldBeExposed (prop) {
return !!ERROR_INFO_TO_EXPOSE[prop]; return !!ERROR_INFO_TO_EXPOSE[prop];
} }
function errorMessageWithContext(err) { function errorMessageWithContext (err) {
// See https://github.com/Vizzuality/Windshaft-cartodb/issues/68 // See https://github.com/Vizzuality/Windshaft-cartodb/issues/68
var message = (_.isString(err) ? err : err.message) || 'Unknown error'; var message = (_.isString(err) ? err : err.message) || 'Unknown error';
var error = { var error = {
type: err.type || 'unknown', type: err.type || 'unknown',
message: stripConnectionInfo(message), message: stripConnectionInfo(message)
}; };
for (var prop in err) { for (var prop in err) {
@ -187,27 +183,27 @@ function errorMessageWithContext(err) {
return error; return error;
} }
function setErrorHeader(errors, statusCode, res) { function setErrorHeader (errors, statusCode, res) {
let errorsCopy = errors.slice(0); const errorsCopy = errors.slice(0);
const mainError = errorsCopy.shift(); const mainError = errorsCopy.shift();
let errorsLog = { const errorsLog = {
mainError: { mainError: {
statusCode: statusCode || 200, statusCode: statusCode || 200,
message: mainError.message, message: mainError.message,
name: mainError.name, name: mainError.name,
label: mainError.label, label: mainError.label,
type: mainError.type, type: mainError.type,
subtype: mainError.subtype subtype: mainError.subtype
} }
}; };
errorsLog.moreErrors = errorsCopy.map(error => { errorsLog.moreErrors = errorsCopy.map(error => {
return { return {
message: error.message, message: error.message,
name: error.name, name: error.name,
label: error.label, label: error.label,
type: error.type, type: error.type,
subtype: error.subtype subtype: error.subtype
}; };
}); });
@ -221,14 +217,14 @@ function setErrorHeader(errors, statusCode, res) {
* *
* @param {Object} object * @param {Object} object
*/ */
function stringifyForLogs(object) { function stringifyForLogs (object) {
Object.keys(object).map(key => { Object.keys(object).map(key => {
if(typeof object[key] === 'string') { if (typeof object[key] === 'string') {
object[key] = object[key].replace(/[^a-zA-Z0-9]/g, ' '); object[key] = object[key].replace(/[^a-zA-Z0-9]/g, ' ');
} else if (typeof object[key] === 'object') { } else if (typeof object[key] === 'object') {
stringifyForLogs(object[key]); stringifyForLogs(object[key]);
} else if (object[key] instanceof Array) { } else if (object[key] instanceof Array) {
for (let element of object[key]) { for (const element of object[key]) {
stringifyForLogs(element); stringifyForLogs(element);
} }
} }

View File

@ -1,7 +1,7 @@
'use strict'; 'use strict';
module.exports = function incrementMapViewCount (metadataBackend) { module.exports = function incrementMapViewCount (metadataBackend) {
return function incrementMapViewCountMiddleware(req, res, next) { return function incrementMapViewCountMiddleware (req, res, next) {
const { mapConfig, user } = res.locals; const { mapConfig, user } = res.locals;
// Error won't blow up, just be logged. // Error won't blow up, just be logged.

View File

@ -1,7 +1,7 @@
'use strict'; 'use strict';
module.exports = function setLastModifiedHeader () { module.exports = function setLastModifiedHeader () {
return function setLastModifiedHeaderMiddleware(req, res, next) { return function setLastModifiedHeaderMiddleware (req, res, next) {
if (req.method !== 'GET') { if (req.method !== 'GET') {
return next(); return next();
} }
@ -10,9 +10,9 @@ module.exports = function setLastModifiedHeader () {
if (cache_buster) { if (cache_buster) {
const cacheBuster = parseInt(cache_buster, 10); const cacheBuster = parseInt(cache_buster, 10);
const lastModifiedDate = Number.isFinite(cacheBuster) && cacheBuster !== 0 ? const lastModifiedDate = Number.isFinite(cacheBuster) && cacheBuster !== 0
new Date(cacheBuster) : ? new Date(cacheBuster)
new Date(); : new Date();
res.set('Last-Modified', lastModifiedDate.toUTCString()); res.set('Last-Modified', lastModifiedDate.toUTCString());

View File

@ -27,12 +27,12 @@ module.exports = function setLastUpdatedTimeToLayergroup () {
}; };
}; };
function getLastUpdatedTime(analysesResults, lastUpdateTime) { function getLastUpdatedTime (analysesResults, lastUpdateTime) {
if (!Array.isArray(analysesResults)) { if (!Array.isArray(analysesResults)) {
return lastUpdateTime; return lastUpdateTime;
} }
return analysesResults.reduce(function(lastUpdateTime, analysis) { return analysesResults.reduce(function (lastUpdateTime, analysis) {
return analysis.getNodes().reduce(function(lastNodeUpdatedAtTime, node) { return analysis.getNodes().reduce(function (lastNodeUpdatedAtTime, node) {
var nodeUpdatedAtDate = node.getUpdatedAt(); var nodeUpdatedAtDate = node.getUpdatedAt();
var nodeUpdatedTimeAt = (nodeUpdatedAtDate && nodeUpdatedAtDate.getTime()) || 0; var nodeUpdatedTimeAt = (nodeUpdatedAtDate && nodeUpdatedAtDate.getTime()) || 0;
return nodeUpdatedTimeAt > lastNodeUpdatedAtTime ? nodeUpdatedTimeAt : lastNodeUpdatedAtTime; return nodeUpdatedTimeAt > lastNodeUpdatedAtTime ? nodeUpdatedTimeAt : lastNodeUpdatedAtTime;

View File

@ -1,7 +1,7 @@
'use strict'; 'use strict';
module.exports = function setLayerStats (pgConnection, statsBackend) { module.exports = function setLayerStats (pgConnection, statsBackend) {
return function setLayerStatsMiddleware(req, res, next) { return function setLayerStatsMiddleware (req, res, next) {
const { user, mapConfig } = res.locals; const { user, mapConfig } = res.locals;
const layergroup = res.body; const layergroup = res.body;
@ -10,7 +10,7 @@ module.exports = function setLayerStats (pgConnection, statsBackend) {
return next(err); return next(err);
} }
statsBackend.getStats(mapConfig, connection, function(err, layersStats) { statsBackend.getStats(mapConfig, connection, function (err, layersStats) {
if (err) { if (err) {
return next(err); return next(err);
} }

View File

@ -9,7 +9,7 @@ module.exports = function setMetadataToLayergroup (layergroupMetadata, includeQu
layergroupMetadata.addAnalysesMetadata(user, layergroup, analysesResults, includeQuery); layergroupMetadata.addAnalysesMetadata(user, layergroup, analysesResults, includeQuery);
layergroupMetadata.addTurboCartoContextMetadata(layergroup, mapConfig.obj(), context); layergroupMetadata.addTurboCartoContextMetadata(layergroup, mapConfig.obj(), context);
layergroupMetadata.addAggregationContextMetadata(layergroup, mapConfig.obj(), context); layergroupMetadata.addAggregationContextMetadata(layergroup, mapConfig.obj(), context);
layergroupMetadata.addDateWrappingMetadata (layergroup, mapConfig.obj()); layergroupMetadata.addDateWrappingMetadata(layergroup, mapConfig.obj());
layergroupMetadata.addTileJsonMetadata(layergroup, user, mapConfig, userApiKey); layergroupMetadata.addTileJsonMetadata(layergroup, user, mapConfig, userApiKey);
next(); next();

View File

@ -19,7 +19,7 @@ module.exports = function layergroupToken () {
if (res.locals.signer !== user) { if (res.locals.signer !== user) {
const err = new Error(authErrorMessageTemplate(res.locals.signer, user)); const err = new Error(authErrorMessageTemplate(res.locals.signer, user));
err.type = 'auth'; err.type = 'auth';
err.http_status = (req.query && req.query.callback) ? 200: 403; err.http_status = (req.query && req.query.callback) ? 200 : 403;
return next(err); return next(err);
} }

View File

@ -14,12 +14,12 @@ module.exports = function lzma () {
var lzma = new Buffer(req.query.lzma, 'base64') var lzma = new Buffer(req.query.lzma, 'base64')
.toString('binary') .toString('binary')
.split('') .split('')
.map(function(c) { .map(function (c) {
return c.charCodeAt(0) - 128; return c.charCodeAt(0) - 128;
}); });
// Decompress // Decompress
lzmaWorker.decompress(lzma, function(result) { lzmaWorker.decompress(lzma, function (result) {
try { try {
delete req.query.lzma; delete req.query.lzma;
Object.assign(req.query, JSON.parse(result)); Object.assign(req.query, JSON.parse(result));

View File

@ -17,7 +17,7 @@ module.exports = function mapError (options) {
}; };
}; };
function populateError(err, mapConfig) { function populateError (err, mapConfig) {
var error = new Error(err.message); var error = new Error(err.message);
error.http_status = err.http_status; error.http_status = err.http_status;

View File

@ -16,9 +16,21 @@ module.exports = function createMapStoreMapConfigProvider (
const { layer: layerFromQuery } = req.query; const { layer: layerFromQuery } = req.query;
const params = { const params = {
user, token, cache_buster, api_key, user,
dbuser, dbname, dbpassword, dbhost, dbport, token,
layer: (layerFromQuery || layerFromParams), z, x, y, scale_factor, format cache_buster,
api_key,
dbuser,
dbname,
dbpassword,
dbhost,
dbport,
layer: (layerFromQuery || layerFromParams),
z,
x,
y,
scale_factor,
format
}; };
if (forcedFormat) { if (forcedFormat) {

View File

@ -8,9 +8,21 @@ module.exports = function getNamedMapProvider ({ namedMapProviderCache, label, f
const { layer: layerFromQuery } = req.query; const { layer: layerFromQuery } = req.query;
const params = { const params = {
user, token, cache_buster, api_key, user,
dbuser, dbname, dbpassword, dbhost, dbport, token,
template_id, layer: (layerFromQuery || layerFromParams), z, x, y, format cache_buster,
api_key,
dbuser,
dbname,
dbpassword,
dbhost,
dbport,
template_id,
layer: (layerFromQuery || layerFromParams),
z,
x,
y,
format
}; };
if (forcedFormat) { if (forcedFormat) {

View File

@ -19,12 +19,12 @@ const RATE_LIMIT_ENDPOINTS_GROUPS = {
NAMED_TILES: 'named_tiles' NAMED_TILES: 'named_tiles'
}; };
function rateLimit(userLimitsBackend, endpointGroup = null) { function rateLimit (userLimitsBackend, endpointGroup = null) {
if (!isRateLimitEnabled(endpointGroup)) { if (!isRateLimitEnabled(endpointGroup)) {
return function rateLimitDisabledMiddleware(req, res, next) { next(); }; return function rateLimitDisabledMiddleware (req, res, next) { next(); };
} }
return function rateLimitMiddleware(req, res, next) { return function rateLimitMiddleware (req, res, next) {
userLimitsBackend.getRateLimit(res.locals.user, endpointGroup, function (err, userRateLimit) { userLimitsBackend.getRateLimit(res.locals.user, endpointGroup, function (err, userRateLimit) {
if (err) { if (err) {
return next(err); return next(err);
@ -46,7 +46,7 @@ function rateLimit(userLimitsBackend, endpointGroup = null) {
// retry is floor rounded in seconds by redis-cell // retry is floor rounded in seconds by redis-cell
res.set('Retry-After', retry + 1); res.set('Retry-After', retry + 1);
let rateLimitError = new Error( const rateLimitError = new Error(
'You are over platform\'s limits: too many requests.' + 'You are over platform\'s limits: too many requests.' +
' Please contact us to know more details' ' Please contact us to know more details'
); );
@ -61,8 +61,7 @@ function rateLimit(userLimitsBackend, endpointGroup = null) {
}; };
} }
function isRateLimitEnabled (endpointGroup) {
function isRateLimitEnabled(endpointGroup) {
return global.environment.enabledFeatures.rateLimitsEnabled && return global.environment.enabledFeatures.rateLimitsEnabled &&
endpointGroup && endpointGroup &&
global.environment.enabledFeatures.rateLimitsByEndpoint[endpointGroup]; global.environment.enabledFeatures.rateLimitsByEndpoint[endpointGroup];

View File

@ -20,7 +20,7 @@ module.exports = function stats (options) {
// May throw due to dns, see: http://github.com/CartoDB/Windshaft/issues/166 // May throw due to dns, see: http://github.com/CartoDB/Windshaft/issues/166
req.profiler.sendStats(); req.profiler.sendStats();
} catch (err) { } catch (err) {
debug("error sending profiling stats: " + err); debug('error sending profiling stats: ' + err);
} }
}); });

View File

@ -4,7 +4,7 @@ const NamedMapsCacheEntry = require('../../cache/model/named-maps-entry');
const NamedMapMapConfigProvider = require('../../models/mapconfig/provider/named-map-provider'); const NamedMapMapConfigProvider = require('../../models/mapconfig/provider/named-map-provider');
module.exports = function setSurrogateKeyHeader ({ surrogateKeysCache }) { module.exports = function setSurrogateKeyHeader ({ surrogateKeysCache }) {
return function setSurrogateKeyHeaderMiddleware(req, res, next) { return function setSurrogateKeyHeaderMiddleware (req, res, next) {
const { user, mapConfigProvider } = res.locals; const { user, mapConfigProvider } = res.locals;
if (mapConfigProvider instanceof NamedMapMapConfigProvider) { if (mapConfigProvider instanceof NamedMapMapConfigProvider) {

View File

@ -5,7 +5,7 @@ const CdbRequest = require('../../models/cdb-request');
module.exports = function user () { module.exports = function user () {
const cdbRequest = new CdbRequest(); const cdbRequest = new CdbRequest();
return function userMiddleware(req, res, next) { return function userMiddleware (req, res, next) {
res.locals.user = cdbRequest.userByReq(req); res.locals.user = cdbRequest.userByReq(req);
next(); next();

View File

@ -3,10 +3,9 @@
const fs = require('fs'); const fs = require('fs');
const timeoutErrorVectorTile = fs.readFileSync(__dirname + '/../../../assets/render-timeout-fallback.mvt'); const timeoutErrorVectorTile = fs.readFileSync(__dirname + '/../../../assets/render-timeout-fallback.mvt');
module.exports = function vectorError() { module.exports = function vectorError () {
return function vectorErrorMiddleware(err, req, res, next) { return function vectorErrorMiddleware (err, req, res, next) {
if(req.params.format === 'mvt') { if (req.params.format === 'mvt') {
if (isTimeoutError(err) || isRateLimitError(err)) { if (isTimeoutError(err) || isRateLimitError(err)) {
res.set('Content-Type', 'application/x-protobuf'); res.set('Content-Type', 'application/x-protobuf');
return res.status(429).send(timeoutErrorVectorTile); return res.status(429).send(timeoutErrorVectorTile);
@ -17,7 +16,6 @@ module.exports = function vectorError() {
}; };
}; };
function isRenderTimeoutError (err) { function isRenderTimeoutError (err) {
return err.message === 'Render timed out'; return err.message === 'Render timed out';
} }

View File

@ -19,7 +19,7 @@ module.exports = class AdminTemplateController {
} }
route (templateRouter) { route (templateRouter) {
templateRouter.options(`/:template_id`); templateRouter.options('/:template_id');
templateRouter.post('/', this.middlewares({ templateRouter.post('/', this.middlewares({
action: 'create', action: 'create',

View File

@ -106,7 +106,7 @@ module.exports = class NamedMapController {
lastModifiedHeader(), lastModifiedHeader(),
lastUpdatedTimeLayergroup(), lastUpdatedTimeLayergroup(),
layerStats(this.pgConnection, this.statsBackend), layerStats(this.pgConnection, this.statsBackend),
layergroupIdHeader(this.templateMaps ,useTemplateHash), layergroupIdHeader(this.templateMaps, useTemplateHash),
layergroupMetadata(this.layergroupMetadata, includeQuery), layergroupMetadata(this.layergroupMetadata, includeQuery),
mapError({ label, addContext }) mapError({ label, addContext })
]; ];
@ -114,7 +114,7 @@ module.exports = class NamedMapController {
}; };
function checkInstantiteLayergroup () { function checkInstantiteLayergroup () {
return function checkInstantiteLayergroupMiddleware(req, res, next) { return function checkInstantiteLayergroupMiddleware (req, res, next) {
if (req.method === 'GET') { if (req.method === 'GET') {
const { callback, config } = req.query; const { callback, config } = req.query;
@ -125,7 +125,7 @@ function checkInstantiteLayergroup () {
if (config) { if (config) {
try { try {
req.body = JSON.parse(config); req.body = JSON.parse(config);
} catch(e) { } catch (e) {
return next(new Error('Invalid config parameter, should be a valid JSON')); return next(new Error('Invalid config parameter, should be a valid JSON'));
} }
} }

View File

@ -21,7 +21,7 @@ module.exports = class TemplateRouter {
authBackend, authBackend,
layergroupMetadata, layergroupMetadata,
namedMapProviderCache, namedMapProviderCache,
tileBackend, tileBackend
} = collaborators; } = collaborators;
this.namedMapController = new NamedMapController( this.namedMapController = new NamedMapController(

View File

@ -89,7 +89,7 @@ function getTile ({ tileBackend, label }) {
} }
function setContentTypeHeader () { function setContentTypeHeader () {
return function setContentTypeHeaderMiddleware(req, res, next) { return function setContentTypeHeaderMiddleware (req, res, next) {
res.set('Content-Type', res.get('content-type') || res.get('Content-Type') || 'image/png'); res.set('Content-Type', res.get('content-type') || res.get('Content-Type') || 'image/png');
next(); next();

View File

@ -2,7 +2,7 @@
var PSQL = require('cartodb-psql'); var PSQL = require('cartodb-psql');
function AnalysisStatusBackend() { function AnalysisStatusBackend () {
} }
module.exports = AnalysisStatusBackend; module.exports = AnalysisStatusBackend;
@ -15,7 +15,7 @@ AnalysisStatusBackend.prototype.getNodeStatus = function (nodeId, dbParams, call
var pg = new PSQL(dbParams); var pg = new PSQL(dbParams);
pg.query(statusQuery, function(err, result) { pg.query(statusQuery, function (err, result) {
if (err) { if (err) {
return callback(err, result); return callback(err, result);
} }

View File

@ -43,7 +43,7 @@ AnalysisBackend.prototype.setLoggerConfig = function (options) {
} }
}; };
AnalysisBackend.prototype.create = function(analysisConfiguration, analysisDefinition, callback) { AnalysisBackend.prototype.create = function (analysisConfiguration, analysisDefinition, callback) {
analysisConfiguration.batch.endpoint = this.batchConfig.endpoint; analysisConfiguration.batch.endpoint = this.batchConfig.endpoint;
analysisConfiguration.batch.inlineExecution = this.batchConfig.inlineExecution; analysisConfiguration.batch.inlineExecution = this.batchConfig.inlineExecution;
analysisConfiguration.batch.hostHeaderTemplate = this.batchConfig.hostHeaderTemplate; analysisConfiguration.batch.hostHeaderTemplate = this.batchConfig.hostHeaderTemplate;
@ -52,13 +52,13 @@ AnalysisBackend.prototype.create = function(analysisConfiguration, analysisDefin
stream: this.stream ? this.stream : process.stdout stream: this.stream ? this.stream : process.stdout
}; };
this.getAnalysesLimits(analysisConfiguration.user, function(err, limits) { this.getAnalysesLimits(analysisConfiguration.user, function (err, limits) {
analysisConfiguration.limits = limits || {}; analysisConfiguration.limits = limits || {};
camshaft.create(analysisConfiguration, analysisDefinition, callback); camshaft.create(analysisConfiguration, analysisDefinition, callback);
}); });
}; };
AnalysisBackend.prototype.getAnalysesLimits = function(username, callback) { AnalysisBackend.prototype.getAnalysesLimits = function (username, callback) {
var self = this; var self = this;
var analysesLimits = { var analysesLimits = {
@ -70,16 +70,16 @@ AnalysisBackend.prototype.getAnalysesLimits = function(username, callback) {
} }
}; };
Object.keys(self.options.limits).forEach(function(analysisTypeOrTag) { Object.keys(self.options.limits).forEach(function (analysisTypeOrTag) {
analysesLimits.analyses[analysisTypeOrTag] = _.extend({}, self.options.limits[analysisTypeOrTag]); analysesLimits.analyses[analysisTypeOrTag] = _.extend({}, self.options.limits[analysisTypeOrTag]);
}); });
var analysesLimitsKey = REDIS_LIMITS.PREFIX + username; var analysesLimitsKey = REDIS_LIMITS.PREFIX + username;
this.metadataBackend.redisCmd(REDIS_LIMITS.DB, 'HGETALL', [analysesLimitsKey], function(err, analysesTimeouts) { this.metadataBackend.redisCmd(REDIS_LIMITS.DB, 'HGETALL', [analysesLimitsKey], function (err, analysesTimeouts) {
// analysesTimeouts wil be something like: { moran: 3000, intersection: 5000 } // analysesTimeouts wil be something like: { moran: 3000, intersection: 5000 }
analysesTimeouts = analysesTimeouts || {}; analysesTimeouts = analysesTimeouts || {};
Object.keys(analysesTimeouts).forEach(function(analysisType) { Object.keys(analysesTimeouts).forEach(function (analysisType) {
analysesLimits.analyses[analysisType] = _.defaults( analysesLimits.analyses[analysisType] = _.defaults(
{ {
timeout: Number.isFinite(+analysesTimeouts[analysisType]) ? +analysesTimeouts[analysisType] : 0 timeout: Number.isFinite(+analysesTimeouts[analysisType]) ? +analysesTimeouts[analysisType] : 0

View File

@ -9,7 +9,7 @@
* @constructor * @constructor
* @type {AuthBackend} * @type {AuthBackend}
*/ */
function AuthBackend(pgConnection, metadataBackend, mapStore, templateMaps) { function AuthBackend (pgConnection, metadataBackend, mapStore, templateMaps) {
this.pgConnection = pgConnection; this.pgConnection = pgConnection;
this.metadataBackend = metadataBackend; this.metadataBackend = metadataBackend;
this.mapStore = mapStore; this.mapStore = mapStore;
@ -25,8 +25,8 @@ module.exports = AuthBackend;
// 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.
// //
AuthBackend.prototype.authorizedBySigner = function(req, res, callback) { AuthBackend.prototype.authorizedBySigner = function (req, res, callback) {
if ( ! res.locals.token || ! res.locals.signer ) { if (!res.locals.token || !res.locals.signer) {
return callback(null, false); // no signer requested return callback(null, false); // no signer requested
} }
@ -35,7 +35,7 @@ AuthBackend.prototype.authorizedBySigner = function(req, res, callback) {
var layergroup_id = res.locals.token; var layergroup_id = res.locals.token;
var auth_token = req.query.auth_token; var auth_token = req.query.auth_token;
this.mapStore.load(layergroup_id, function(err, mapConfig) { this.mapStore.load(layergroup_id, function (err, mapConfig) {
if (err) { if (err) {
return callback(err); return callback(err);
} }
@ -46,7 +46,7 @@ AuthBackend.prototype.authorizedBySigner = function(req, res, callback) {
}); });
}; };
function isValidApiKey(apikey) { function isValidApiKey (apikey) {
return apikey.type && return apikey.type &&
apikey.user && apikey.user &&
apikey.databasePassword && apikey.databasePassword &&
@ -60,11 +60,11 @@ function isValidApiKey(apikey) {
// @param callback function(err, authorized) // @param callback function(err, authorized)
// NOTE: authorized is expected to be 0 or 1 (integer) // NOTE: authorized is expected to be 0 or 1 (integer)
// //
AuthBackend.prototype.authorizedByAPIKey = function(user, res, callback) { AuthBackend.prototype.authorizedByAPIKey = function (user, res, callback) {
const apikeyToken = res.locals.api_key; const apikeyToken = res.locals.api_key;
const basicAuthUsername = res.locals.basicAuthUsername; const basicAuthUsername = res.locals.basicAuthUsername;
if ( ! apikeyToken ) { if (!apikeyToken) {
return callback(null, false); // no api key, no authorization... return callback(null, false); // no api key, no authorization...
} }
@ -77,7 +77,7 @@ AuthBackend.prototype.authorizedByAPIKey = function(user, res, callback) {
return callback(err); return callback(err);
} }
if ( !isValidApiKey(apikey)) { if (!isValidApiKey(apikey)) {
const error = new Error('Unauthorized'); const error = new Error('Unauthorized');
error.type = 'auth'; error.type = 'auth';
error.subtype = 'api-key-not-found'; error.subtype = 'api-key-not-found';
@ -109,7 +109,7 @@ AuthBackend.prototype.authorizedByAPIKey = function(user, res, callback) {
}; };
function isNameNotFoundError (err) { function isNameNotFoundError (err) {
return err.message && -1 !== err.message.indexOf('name not found'); return err.message && err.message.indexOf('name not found') !== -1;
} }
function usernameMatches (basicAuthUsername, requestUsername) { function usernameMatches (basicAuthUsername, requestUsername) {
@ -123,7 +123,7 @@ function usernameMatches (basicAuthUsername, requestUsername) {
* @param res - standard res object. Contains the auth parameters in locals * @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?
*/ */
AuthBackend.prototype.authorize = function(req, res, callback) { AuthBackend.prototype.authorize = function (req, res, callback) {
var user = res.locals.user; var user = res.locals.user;
this.authorizedByAPIKey(user, res, (err, isAuthorizedByApikey) => { this.authorizedByAPIKey(user, res, (err, isAuthorizedByApikey) => {

View File

@ -71,8 +71,8 @@ function getFeatures (pg, layer, params, callback) {
} }
const SKIP_COLUMNS = { const SKIP_COLUMNS = {
'the_geom': true, the_geom: true,
'the_geom_webmercator': true the_geom_webmercator: true
}; };
function getColumnsName (pg, query, callback) { function getColumnsName (pg, query, callback) {
@ -100,7 +100,7 @@ function getClusterFeatures (pg, zoom, clusterId, columns, query, resolution, ag
zoom: zoom, zoom: zoom,
id: clusterId, id: clusterId,
query: query, query: query,
res: 256/resolution, res: 256 / resolution,
columns: columns columns: columns
}); });
@ -127,7 +127,7 @@ function getClusterFeatures (pg, zoom, clusterId, columns, query, resolution, ag
} }
return callback(null, data); return callback(null, data);
} , true); // use read-only transaction }, true); // use read-only transaction
} }
const schemaQuery = ctx => `SELECT * FROM (${ctx.query}) __cdb_cluster_schema LIMIT 0`; const schemaQuery = ctx => `SELECT * FROM (${ctx.query}) __cdb_cluster_schema LIMIT 0`;
@ -159,8 +159,8 @@ const clusterFeaturesQuery = ctx => `
`; `;
const gridResolution = ctx => { const gridResolution = ctx => {
const zoomResolution = webmercator.getResolution({ z : Math.min(38, ctx.zoom) }); const zoomResolution = webmercator.getResolution({ z: Math.min(38, ctx.zoom) });
return `${256/ctx.res} * (${zoomResolution})::double precision`; return `${256 / ctx.res} * (${zoomResolution})::double precision`;
}; };
const aggregationQuery = ctx => ` const aggregationQuery = ctx => `
@ -194,9 +194,8 @@ function parseAggregation (aggregation) {
try { try {
aggregation = JSON.parse(aggregation); aggregation = JSON.parse(aggregation);
} catch (err) { } catch (err) {
throw new Error(`Invalid aggregation input, should be a a valid JSON`); throw new Error('Invalid aggregation input, should be a a valid JSON');
} }
} }
return aggregation; return aggregation;
@ -207,7 +206,7 @@ function validateAggregation (aggregation) {
const { columns, expressions } = aggregation; const { columns, expressions } = aggregation;
if (!hasColumns(columns)) { if (!hasColumns(columns)) {
throw new Error(`Invalid aggregation input, columns should be and array of column names`); throw new Error('Invalid aggregation input, columns should be and array of column names');
} }
validateExpressions(expressions); validateExpressions(expressions);
@ -221,16 +220,16 @@ function hasColumns (columns) {
function validateExpressions (expressions) { function validateExpressions (expressions) {
if (expressions !== undefined) { if (expressions !== undefined) {
if (!isValidExpression(expressions)) { if (!isValidExpression(expressions)) {
throw new Error(`Invalid aggregation input, expressions should be and object with valid functions`); throw new Error('Invalid aggregation input, expressions should be and object with valid functions');
} }
for (const { aggregate_function, aggregated_column } of Object.values(expressions)) { for (const { aggregate_function, aggregated_column } of Object.values(expressions)) {
if (typeof aggregated_column !== 'string') { if (typeof aggregated_column !== 'string') {
throw new Error(`Invalid aggregation input, aggregated column should be an string`); throw new Error('Invalid aggregation input, aggregated column should be an string');
} }
if (typeof aggregate_function !== 'string') { if (typeof aggregate_function !== 'string') {
throw new Error(`Invalid aggregation input, aggregate function should be an string`); throw new Error('Invalid aggregation input, aggregate function should be an string');
} }
} }
} }

View File

@ -14,7 +14,7 @@ var overviewsQueryRewriter = new OverviewsQueryRewriter({
var dot = require('dot'); var dot = require('dot');
dot.templateSettings.strip = false; dot.templateSettings.strip = false;
function DataviewBackend(analysisBackend) { function DataviewBackend (analysisBackend) {
this.analysisBackend = analysisBackend; this.analysisBackend = analysisBackend;
} }
@ -84,14 +84,14 @@ function getQueryWithFilters (dataviewDefinition, params) {
var query = getDataviewQuery(dataviewDefinition, ownFilter, noFilters); var query = getDataviewQuery(dataviewDefinition, ownFilter, noFilters);
if (params.bbox) { if (params.bbox) {
var bboxFilter = new BBoxFilter({column: 'the_geom_webmercator', srid: 3857}, {bbox: params.bbox}); var bboxFilter = new BBoxFilter({ column: 'the_geom_webmercator', srid: 3857 }, { bbox: params.bbox });
query = bboxFilter.sql(query); query = bboxFilter.sql(query);
} }
return query; return query;
} }
function getDataviewQuery(dataviewDefinition, ownFilter, noFilters) { function getDataviewQuery (dataviewDefinition, ownFilter, noFilters) {
if (noFilters) { if (noFilters) {
return dataviewDefinition.sql.no_filters; return dataviewDefinition.sql.no_filters;
} else if (ownFilter === 1) { } else if (ownFilter === 1) {
@ -101,9 +101,9 @@ function getDataviewQuery(dataviewDefinition, ownFilter, noFilters) {
} }
} }
function getQueryRewriteData(mapConfig, dataviewDefinition, params) { function getQueryRewriteData (mapConfig, dataviewDefinition, params) {
var sourceId = dataviewDefinition.source.id; // node.id var sourceId = dataviewDefinition.source.id; // node.id
var layer = _.find(mapConfig.obj().layers, function(l) { var layer = _.find(mapConfig.obj().layers, function (l) {
return l.options.source && (l.options.source.id === sourceId); return l.options.source && (l.options.source.id === sourceId);
}); });
var queryRewriteData = layer && layer.options.query_rewrite_data; var queryRewriteData = layer && layer.options.query_rewrite_data;
@ -131,16 +131,16 @@ function getQueryRewriteData(mapConfig, dataviewDefinition, params) {
return queryRewriteData; return queryRewriteData;
} }
function getOverrideParams(params, ownFilter) { function getOverrideParams (params, ownFilter) {
var overrideParams = _.reduce(_.pick(params, 'start', 'end', 'bins', 'offset', 'categories'), var overrideParams = _.reduce(_.pick(params, 'start', 'end', 'bins', 'offset', 'categories'),
function castNumbers(overrides, val, k) { function castNumbers (overrides, val, k) {
if (!Number.isFinite(+val)) { if (!Number.isFinite(+val)) {
throw new Error('Invalid number format for parameter \'' + k + '\''); throw new Error('Invalid number format for parameter \'' + k + '\'');
} }
overrides[k] = +val; overrides[k] = +val;
return overrides; return overrides;
}, },
{ownFilter: ownFilter} { ownFilter: ownFilter }
); );
// validation will be delegated to the proper dataview // validation will be delegated to the proper dataview
@ -202,7 +202,7 @@ function getQueryWithOwnFilters (dataviewDefinition, params) {
return query; return query;
} }
function getDataviewDefinition(mapConfig, dataviewName) { function getDataviewDefinition (mapConfig, dataviewName) {
var dataviews = mapConfig.dataviews || {}; var dataviews = mapConfig.dataviews || {};
return dataviews[dataviewName]; return dataviews[dataviewName];
} }

View File

@ -3,21 +3,21 @@
var _ = require('underscore'); var _ = require('underscore');
var AnalysisFilter = require('../models/filter/analysis'); var AnalysisFilter = require('../models/filter/analysis');
function FilterStatsBackends(pgQueryRunner) { function FilterStatsBackends (pgQueryRunner) {
this.pgQueryRunner = pgQueryRunner; this.pgQueryRunner = pgQueryRunner;
} }
module.exports = FilterStatsBackends; module.exports = FilterStatsBackends;
function getEstimatedRows(pgQueryRunner, username, query, callback) { function getEstimatedRows (pgQueryRunner, username, query, callback) {
pgQueryRunner.run(username, "EXPLAIN (FORMAT JSON)"+query, function(err, result_rows) { pgQueryRunner.run(username, 'EXPLAIN (FORMAT JSON)' + query, function (err, result_rows) {
if (err){ if (err) {
callback(err); callback(err);
return; return;
} }
var rows; var rows;
if ( result_rows[0] && result_rows[0]['QUERY PLAN'] && if (result_rows[0] && result_rows[0]['QUERY PLAN'] &&
result_rows[0]['QUERY PLAN'][0] && result_rows[0]['QUERY PLAN'][0].Plan ) { result_rows[0]['QUERY PLAN'][0] && result_rows[0]['QUERY PLAN'][0].Plan) {
rows = result_rows[0]['QUERY PLAN'][0].Plan['Plan Rows']; rows = result_rows[0]['QUERY PLAN'][0].Plan['Plan Rows'];
} }
return callback(null, rows); return callback(null, rows);
@ -28,7 +28,7 @@ FilterStatsBackends.prototype.getFilterStats = function (username, unfiltered_qu
var stats = {}; var stats = {};
getEstimatedRows(this.pgQueryRunner, username, unfiltered_query, (err, rows) => { getEstimatedRows(this.pgQueryRunner, username, unfiltered_query, (err, rows) => {
if (err){ if (err) {
return callback(err); return callback(err);
} }
@ -42,7 +42,7 @@ FilterStatsBackends.prototype.getFilterStats = function (username, unfiltered_qu
var query = analysisFilter.sql(unfiltered_query); var query = analysisFilter.sql(unfiltered_query);
getEstimatedRows(this.pgQueryRunner, username, query, (err, rows) => { getEstimatedRows(this.pgQueryRunner, username, query, (err, rows) => {
if (err){ if (err) {
return callback(err); return callback(err);
} }

View File

@ -1,6 +1,6 @@
'use strict'; 'use strict';
function EmptyLayerStats(types) { function EmptyLayerStats (types) {
this._types = types || {}; this._types = types || {};
} }
@ -10,7 +10,7 @@ EmptyLayerStats.prototype.is = function (type) {
EmptyLayerStats.prototype.getStats = EmptyLayerStats.prototype.getStats =
function (layer, dbConnection, callback) { function (layer, dbConnection, callback) {
setImmediate(function() { setImmediate(function () {
callback(null, {}); callback(null, {});
}); });
}; };

View File

@ -5,7 +5,7 @@ var EmptyLayerStats = require('./empty-layer-stats');
var MapnikLayerStats = require('./mapnik-layer-stats'); var MapnikLayerStats = require('./mapnik-layer-stats');
var TorqueLayerStats = require('./torque-layer-stats'); var TorqueLayerStats = require('./torque-layer-stats');
module.exports = function LayerStatsFactory(type) { module.exports = function LayerStatsFactory (type) {
var layerStatsIterator = []; var layerStatsIterator = [];
var selectedType = type || 'ALL'; var selectedType = type || 'ALL';

View File

@ -2,7 +2,7 @@
var queue = require('queue-async'); var queue = require('queue-async');
function LayerStats(layerStatsIterator) { function LayerStats (layerStatsIterator) {
this.layerStatsIterator = layerStatsIterator; this.layerStatsIterator = layerStatsIterator;
} }
@ -41,7 +41,6 @@ LayerStats.prototype.getStats = function (mapConfig, dbConnection, callback) {
return callback(err, stats); return callback(err, stats);
}); });
}; };
module.exports = LayerStats; module.exports = LayerStats;

View File

@ -15,7 +15,7 @@ MapnikLayerStats.prototype.is = function (type) {
return this._types[type] ? this._types[type] : false; return this._types[type] ? this._types[type] : false;
}; };
function columnAggregations(field) { function columnAggregations (field) {
if (field.type === 'number') { if (field.type === 'number') {
return ['min', 'max', 'avg', 'sum']; return ['min', 'max', 'avg', 'sum'];
} }
@ -28,25 +28,24 @@ function columnAggregations(field) {
return []; return [];
} }
function _getSQL(ctx, query, type='pre', zoom=0) { function _getSQL (ctx, query, type = 'pre', zoom = 0) {
let sql; let sql;
if (type === 'pre') { if (type === 'pre') {
sql = ctx.preQuery; sql = ctx.preQuery;
} } else {
else {
sql = ctx.aggrQuery; sql = ctx.aggrQuery;
} }
sql = queryUtils.substituteTokensForZoom(sql, zoom || 0); sql = queryUtils.substituteTokensForZoom(sql, zoom || 0);
return query(sql); return query(sql);
} }
function _estimatedFeatureCount(ctx) { function _estimatedFeatureCount (ctx) {
return queryUtils.queryPromise(ctx.dbConnection, _getSQL(ctx, queryUtils.getQueryRowEstimation)) return queryUtils.queryPromise(ctx.dbConnection, _getSQL(ctx, queryUtils.getQueryRowEstimation))
.then(res => ({ estimatedFeatureCount: res.rows[0].rows })) .then(res => ({ estimatedFeatureCount: res.rows[0].rows }))
.catch(() => ({ estimatedFeatureCount: -1 })); .catch(() => ({ estimatedFeatureCount: -1 }));
} }
function _featureCount(ctx) { function _featureCount (ctx) {
if (ctx.metaOptions.featureCount) { if (ctx.metaOptions.featureCount) {
// TODO: if ctx.metaOptions.columnStats we can combine this with column stats query // TODO: if ctx.metaOptions.columnStats we can combine this with column stats query
return queryUtils.queryPromise(ctx.dbConnection, _getSQL(ctx, queryUtils.getQueryActualRowCount)) return queryUtils.queryPromise(ctx.dbConnection, _getSQL(ctx, queryUtils.getQueryActualRowCount))
@ -55,20 +54,20 @@ function _featureCount(ctx) {
return Promise.resolve(); return Promise.resolve();
} }
function _aggrFeatureCount(ctx) { function _aggrFeatureCount (ctx) {
if (ctx.metaOptions.hasOwnProperty('aggrFeatureCount')) { if (ctx.metaOptions.hasOwnProperty('aggrFeatureCount')) {
// We expect as zoom level as the value of aggrFeatureCount // We expect as zoom level as the value of aggrFeatureCount
// TODO: it'd be nice to admit an array of zoom levels to // TODO: it'd be nice to admit an array of zoom levels to
// return metadata for multiple levels. // return metadata for multiple levels.
return queryUtils.queryPromise( return queryUtils.queryPromise(
ctx.dbConnection, ctx.dbConnection,
_getSQL(ctx, queryUtils.getQueryActualRowCount, 'post', ctx.metaOptions.aggrFeatureCount) _getSQL(ctx, queryUtils.getQueryActualRowCount, 'post', ctx.metaOptions.aggrFeatureCount)
).then(res => ({ aggrFeatureCount: res.rows[0].rows })); ).then(res => ({ aggrFeatureCount: res.rows[0].rows }));
} }
return Promise.resolve(); return Promise.resolve();
} }
function _geometryType(ctx) { function _geometryType (ctx) {
if (ctx.metaOptions.geometryType) { if (ctx.metaOptions.geometryType) {
const geometryColumn = AggregationMapConfig.getAggregationGeometryColumn(); const geometryColumn = AggregationMapConfig.getAggregationGeometryColumn();
const sqlQuery = _getSQL(ctx, sql => queryUtils.getQueryGeometryType(sql, geometryColumn)); const sqlQuery = _getSQL(ctx, sql => queryUtils.getQueryGeometryType(sql, geometryColumn));
@ -78,7 +77,7 @@ function _geometryType(ctx) {
return Promise.resolve(); return Promise.resolve();
} }
function _columns(ctx) { function _columns (ctx) {
if (ctx.metaOptions.columns || ctx.metaOptions.columnStats || ctx.metaOptions.dimensions) { if (ctx.metaOptions.columns || ctx.metaOptions.columnStats || ctx.metaOptions.dimensions) {
// note: post-aggregation columns are in layer.options.columns when aggregation is present // note: post-aggregation columns are in layer.options.columns when aggregation is present
return queryUtils.queryPromise(ctx.dbConnection, _getSQL(ctx, sql => queryUtils.getQueryLimited(sql, 0))) return queryUtils.queryPromise(ctx.dbConnection, _getSQL(ctx, sql => queryUtils.getQueryLimited(sql, 0)))
@ -89,7 +88,7 @@ function _columns(ctx) {
// combine a list of results merging the properties of all the objects // combine a list of results merging the properties of all the objects
// undefined results are admitted and ignored // undefined results are admitted and ignored
function mergeResults(results) { function mergeResults (results) {
if (results) { if (results) {
if (results.length === 0) { if (results.length === 0) {
return {}; return {};
@ -108,13 +107,13 @@ function mergeResults(results) {
// deeper (1 level) combination of a list of objects: // deeper (1 level) combination of a list of objects:
// mergeColumns([{ col1: { a: 1 }, col2: { a: 2 } }, { col1: { b: 3 } }]) => { col1: { a: 1, b: 3 }, col2: { a: 2 } } // mergeColumns([{ col1: { a: 1 }, col2: { a: 2 } }, { col1: { b: 3 } }]) => { col1: { a: 1, b: 3 }, col2: { a: 2 } }
function mergeColumns(results) { function mergeColumns (results) {
if (results) { if (results) {
if (results.length === 0) { if (results.length === 0) {
return {}; return {};
} }
return results.reduce((a, b) => { return results.reduce((a, b) => {
let c = Object.assign({}, b || {}, a || {}); const c = Object.assign({}, b || {}, a || {});
Object.keys(c).forEach(key => { Object.keys(c).forEach(key => {
if (b.hasOwnProperty(key)) { if (b.hasOwnProperty(key)) {
c[key] = Object.assign(c[key], b[key]); c[key] = Object.assign(c[key], b[key]);
@ -127,7 +126,7 @@ function mergeColumns(results) {
const DEFAULT_SAMPLE_ROWS = 100; const DEFAULT_SAMPLE_ROWS = 100;
function _sample(ctx) { function _sample (ctx) {
if (!ctx.metaOptions.sample) { if (!ctx.metaOptions.sample) {
return Promise.resolve(); return Promise.resolve();
} }
@ -164,32 +163,32 @@ function _getSampleValuesFromRange (min, span, limit) {
return Array.from(sample); return Array.from(sample);
} }
function _columnsMetadataRequired(options) { function _columnsMetadataRequired (options) {
// We need determine the columns of a query // We need determine the columns of a query
// if either column stats or dimension stats are required, // if either column stats or dimension stats are required,
// since we'll ultimately use the same query to fetch both // since we'll ultimately use the same query to fetch both
return options.columnStats || options.dimensions; return options.columnStats || options.dimensions;
} }
function _columnStats(ctx, columns, dimensions) { function _columnStats (ctx, columns, dimensions) {
if (!columns) { if (!columns) {
return Promise.resolve(); return Promise.resolve();
} }
if (_columnsMetadataRequired(ctx.metaOptions)) { if (_columnsMetadataRequired(ctx.metaOptions)) {
let queries = []; const queries = [];
let aggr = []; let aggr = [];
if (ctx.metaOptions.columnStats) { if (ctx.metaOptions.columnStats) {
queries.push(new Promise(resolve => resolve({ columns }))); // add columns as first result queries.push(new Promise(resolve => resolve({ columns }))); // add columns as first result
Object.keys(columns).forEach(name => { Object.keys(columns).forEach(name => {
aggr = aggr.concat( aggr = aggr.concat(
columnAggregations(columns[name]) columnAggregations(columns[name])
.map(fn => `${fn}("${name}") AS "${name}_${fn}"`) .map(fn => `${fn}("${name}") AS "${name}_${fn}"`)
); );
if (columns[name].type === 'string') { if (columns[name].type === 'string') {
const topN = ctx.metaOptions.columnStats.topCategories || 1024; const topN = ctx.metaOptions.columnStats.topCategories || 1024;
const includeNulls = ctx.metaOptions.columnStats.hasOwnProperty('includeNulls') ? const includeNulls = ctx.metaOptions.columnStats.hasOwnProperty('includeNulls')
ctx.metaOptions.columnStats.includeNulls : ? ctx.metaOptions.columnStats.includeNulls
true; : true;
// TODO: ctx.metaOptions.columnStats.maxCategories // TODO: ctx.metaOptions.columnStats.maxCategories
// => use PG stats to dismiss columns with more distinct values // => use PG stats to dismiss columns with more distinct values
@ -223,7 +222,7 @@ function _columnStats(ctx, columns, dimensions) {
ctx.dbConnection, ctx.dbConnection,
_getSQL(ctx, sql => `SELECT ${aggr.join(',')} FROM (${sql}) AS __cdb_query`) _getSQL(ctx, sql => `SELECT ${aggr.join(',')} FROM (${sql}) AS __cdb_query`)
).then(res => { ).then(res => {
let stats = { columns: {}, dimensions: {} }; const stats = { columns: {}, dimensions: {} };
Object.keys(columns).forEach(name => { Object.keys(columns).forEach(name => {
stats.columns[name] = {}; stats.columns[name] = {};
columnAggregations(columns[name]).forEach(fn => { columnAggregations(columns[name]).forEach(fn => {
@ -245,62 +244,62 @@ function _columnStats(ctx, columns, dimensions) {
); );
return Promise.all(queries).then(results => ({ return Promise.all(queries).then(results => ({
columns: mergeColumns(results.map(r => r.columns)), columns: mergeColumns(results.map(r => r.columns)),
dimensions: mergeColumns(results.map( r => r.dimensions)) dimensions: mergeColumns(results.map(r => r.dimensions))
})); }));
} }
return Promise.resolve({ columns }); return Promise.resolve({ columns });
} }
// This is adapted from SQL API: // This is adapted from SQL API:
function fieldType(cname) { function fieldType (cname) {
let tname; let tname;
switch (true) { switch (true) {
case /bool/.test(cname): case /bool/.test(cname):
tname = 'boolean'; tname = 'boolean';
break; break;
case /int|float|numeric/.test(cname): case /int|float|numeric/.test(cname):
tname = 'number'; tname = 'number';
break; break;
case /text|char|unknown/.test(cname): case /text|char|unknown/.test(cname):
tname = 'string'; tname = 'string';
break; break;
case /date|time/.test(cname): case /date|time/.test(cname):
tname = 'date'; tname = 'date';
break; break;
default: default:
tname = cname; tname = cname;
} }
if ( tname && cname.match(/^_/) ) { if (tname && cname.match(/^_/)) {
tname += '[]'; tname += '[]';
} }
return tname; return tname;
} }
function fieldTypeSafe(dbConnection, field) { function fieldTypeSafe (dbConnection, field) {
const cname = dbConnection.typeName(field.dataTypeID); const cname = dbConnection.typeName(field.dataTypeID);
return cname ? fieldType(cname) : `unknown(${field.dataTypeID})`; return cname ? fieldType(cname) : `unknown(${field.dataTypeID})`;
} }
// columns are returned as an object { columnName1: { type1: ...}, ..} // columns are returned as an object { columnName1: { type1: ...}, ..}
// for consistency with SQL API // for consistency with SQL API
function formatResultFields(dbConnection, fields = []) { function formatResultFields (dbConnection, fields = []) {
let nfields = {}; const nfields = {};
for (let field of fields) { for (const field of fields) {
nfields[field.name] = { type: fieldTypeSafe(dbConnection, field) }; nfields[field.name] = { type: fieldTypeSafe(dbConnection, field) };
} }
return nfields; return nfields;
} }
MapnikLayerStats.prototype.getStats = MapnikLayerStats.prototype.getStats =
function (layer, dbConnection, callback) { function (layer, dbConnection, callback) {
let aggrQuery = layer.options.sql; const aggrQuery = layer.options.sql;
let preQuery = layer.options.sql_raw || aggrQuery; const preQuery = layer.options.sql_raw || aggrQuery;
let ctx = { const ctx = {
dbConnection, dbConnection,
preQuery, preQuery,
aggrQuery, aggrQuery,
metaOptions: layer.options.metadata || {}, metaOptions: layer.options.metadata || {}
}; };
// TODO: could save some queries if queryUtils.getAggregationMetadata() has been used and kept somewhere // TODO: could save some queries if queryUtils.getAggregationMetadata() has been used and kept somewhere
@ -316,7 +315,7 @@ function (layer, dbConnection, callback) {
Promise.all([ Promise.all([
_estimatedFeatureCount(ctx).then( _estimatedFeatureCount(ctx).then(
({ estimatedFeatureCount }) => _sample(ctx) ({ estimatedFeatureCount }) => _sample(ctx)
.then(sampleResults => mergeResults([ sampleResults, { estimatedFeatureCount }] )) .then(sampleResults => mergeResults([sampleResults, { estimatedFeatureCount }]))
), ),
_featureCount(ctx), _featureCount(ctx),
_aggrFeatureCount(ctx), _aggrFeatureCount(ctx),

View File

@ -1,6 +1,6 @@
'use strict'; 'use strict';
function TorqueLayerStats() { function TorqueLayerStats () {
this._types = { this._types = {
torque: true torque: true
}; };

View File

@ -2,7 +2,7 @@
const queryUtils = require('../utils/query-utils'); const queryUtils = require('../utils/query-utils');
function OverviewsMetadataBackend(pgQueryRunner) { function OverviewsMetadataBackend (pgQueryRunner) {
this.pgQueryRunner = pgQueryRunner; this.pgQueryRunner = pgQueryRunner;
} }
@ -17,15 +17,15 @@ OverviewsMetadataBackend.prototype.getOverviewsMetadata = function (username, sq
CDB_QueryTablesText($windshaft$${queryUtils.substituteDummyTokens(sql)}$windshaft$) CDB_QueryTablesText($windshaft$${queryUtils.substituteDummyTokens(sql)}$windshaft$)
); );
`; `;
this.pgQueryRunner.run(username, query, function handleOverviewsRows(err, rows) { this.pgQueryRunner.run(username, query, function handleOverviewsRows (err, rows) {
if (err){ if (err) {
callback(err); callback(err);
return; return;
} }
var metadata = rows.reduce(function(metadata, row){ var metadata = rows.reduce(function (metadata, row) {
var table = row.base_table; var table = row.base_table;
var schema = row._cdb_schema_name; var schema = row._cdb_schema_name;
if ( !metadata[table] ) { if (!metadata[table]) {
metadata[table] = {}; metadata[table] = {};
} }
metadata[table][row.z] = { table: row.overview_table }; metadata[table][row.z] = { table: row.overview_table };

View File

@ -4,13 +4,12 @@ var PSQL = require('cartodb-psql');
var _ = require('underscore'); var _ = require('underscore');
const debug = require('debug')('cachechan'); const debug = require('debug')('cachechan');
function PgConnection(metadataBackend) { function PgConnection (metadataBackend) {
this.metadataBackend = metadataBackend; this.metadataBackend = metadataBackend;
} }
module.exports = PgConnection; module.exports = PgConnection;
// Set db authentication parameters to those of the given username // Set db authentication parameters to those of the given username
// //
// @param username the cartodb username, mapped to a database username // @param username the cartodb username, mapped to a database username
@ -21,7 +20,7 @@ module.exports = PgConnection;
// //
// @param callback function(err) // @param callback function(err)
// //
PgConnection.prototype.setDBAuth = function(username, params, apikeyType, callback) { PgConnection.prototype.setDBAuth = function (username, params, apikeyType, callback) {
if (apikeyType === 'master') { if (apikeyType === 'master') {
this.metadataBackend.getMasterApikey(username, (err, apikey) => { this.metadataBackend.getMasterApikey(username, (err, apikey) => {
if (err) { if (err) {
@ -36,7 +35,7 @@ PgConnection.prototype.setDBAuth = function(username, params, apikeyType, callba
return callback(); return callback();
}); });
} else if (apikeyType === 'regular') { //Actually it can be any type of api key } else if (apikeyType === 'regular') { // Actually it can be any type of api key
this.metadataBackend.getApikey(username, params.api_key, (err, apikey) => { this.metadataBackend.getApikey(username, params.api_key, (err, apikey) => {
if (err) { if (err) {
if (isNameNotFoundError(err)) { if (isNameNotFoundError(err)) {
@ -70,10 +69,9 @@ PgConnection.prototype.setDBAuth = function(username, params, apikeyType, callba
}; };
function isNameNotFoundError (err) { function isNameNotFoundError (err) {
return err.message && -1 !== err.message.indexOf('name not found'); return err.message && err.message.indexOf('name not found') !== -1;
} }
// Set db connection parameters to those for the given username // Set db connection parameters to those for the given username
// //
// @param dbowner cartodb username of database owner, // @param dbowner cartodb username of database owner,
@ -85,7 +83,7 @@ function isNameNotFoundError (err) {
// //
// @param callback function(err) // @param callback function(err)
// //
PgConnection.prototype.setDBConn = function(dbowner, params, callback) { PgConnection.prototype.setDBConn = function (dbowner, params, callback) {
_.defaults(params, { _.defaults(params, {
// dbuser: global.environment.postgres.user, // dbuser: global.environment.postgres.user,
// dbpassword: global.environment.postgres.password, // dbpassword: global.environment.postgres.password,
@ -117,8 +115,8 @@ PgConnection.prototype.setDBConn = function(dbowner, params, callback) {
* @param {Function} callback function({Error}, {PSQL}) * @param {Function} callback function({Error}, {PSQL})
*/ */
PgConnection.prototype.getConnection = function(username, callback) { PgConnection.prototype.getConnection = function (username, callback) {
debug("getConn1"); debug('getConn1');
this.getDatabaseParams(username, (err, databaseParams) => { this.getDatabaseParams(username, (err, databaseParams) => {
if (err) { if (err) {
@ -131,11 +129,10 @@ PgConnection.prototype.getConnection = function(username, callback) {
port: databaseParams.dbport, port: databaseParams.dbport,
dbname: databaseParams.dbname dbname: databaseParams.dbname
})); }));
}); });
}; };
PgConnection.prototype.getDatabaseParams = function(username, callback) { PgConnection.prototype.getDatabaseParams = function (username, callback) {
const databaseParams = {}; const databaseParams = {};
this.setDBAuth(username, databaseParams, 'master', err => { this.setDBAuth(username, databaseParams, 'master', err => {

View File

@ -2,7 +2,7 @@
var PSQL = require('cartodb-psql'); var PSQL = require('cartodb-psql');
function PgQueryRunner(pgConnection) { function PgQueryRunner (pgConnection) {
this.pgConnection = pgConnection; this.pgConnection = pgConnection;
} }
@ -15,8 +15,7 @@ module.exports = PgQueryRunner;
* @param {String} query * @param {String} query
* @param {Function} callback function({Error}, {Array}) second argument is guaranteed to be an array * @param {Function} callback function({Error}, {Array}) second argument is guaranteed to be an array
*/ */
PgQueryRunner.prototype.run = function(username, query, callback) { PgQueryRunner.prototype.run = function (username, query, callback) {
this.pgConnection.getDatabaseParams(username, (err, databaseParams) => { this.pgConnection.getDatabaseParams(username, (err, databaseParams) => {
if (err) { if (err) {
return callback(err); return callback(err);

View File

@ -2,14 +2,14 @@
var layerStats = require('./layer-stats/factory'); var layerStats = require('./layer-stats/factory');
function StatsBackend() { function StatsBackend () {
} }
module.exports = StatsBackend; module.exports = StatsBackend;
StatsBackend.prototype.getStats = function(mapConfig, dbConnection, callback) { StatsBackend.prototype.getStats = function (mapConfig, dbConnection, callback) {
var enabledFeatures = global.environment.enabledFeatures; var enabledFeatures = global.environment.enabledFeatures;
var layerStatsEnabled = enabledFeatures ? enabledFeatures.layerStats: false; var layerStatsEnabled = enabledFeatures ? enabledFeatures.layerStats : false;
if (layerStatsEnabled) { if (layerStatsEnabled) {
layerStats().getStats(mapConfig, dbConnection, callback); layerStats().getStats(mapConfig, dbConnection, callback);
} else { } else {

View File

@ -1,6 +1,6 @@
'use strict'; 'use strict';
function TablesExtentBackend(pgQueryRunner) { function TablesExtentBackend (pgQueryRunner) {
this.pgQueryRunner = pgQueryRunner; this.pgQueryRunner = pgQueryRunner;
} }
@ -16,21 +16,21 @@ module.exports = TablesExtentBackend;
* @param {Function} callback function(err, result) {Object} result with `west`, `south`, `east`, `north` * @param {Function} callback function(err, result) {Object} result with `west`, `south`, `east`, `north`
*/ */
TablesExtentBackend.prototype.getBounds = function (username, tables, callback) { TablesExtentBackend.prototype.getBounds = function (username, tables, callback) {
var estimatedExtentSQLs = tables.map(function(table) { var estimatedExtentSQLs = tables.map(function (table) {
return "ST_EstimatedExtent('" + table.schema_name + "', '" + table.table_name + "', 'the_geom_webmercator')"; return "ST_EstimatedExtent('" + table.schema_name + "', '" + table.table_name + "', 'the_geom_webmercator')";
}); });
var query = [ var query = [
"WITH ext as (" + 'WITH ext as (' +
"SELECT ST_Transform(ST_SetSRID(ST_Extent(ST_Union(ARRAY[", 'SELECT ST_Transform(ST_SetSRID(ST_Extent(ST_Union(ARRAY[',
estimatedExtentSQLs.join(','), estimatedExtentSQLs.join(','),
"])), 3857), 4326) geom)", '])), 3857), 4326) geom)',
"SELECT", 'SELECT',
"ST_XMin(geom) west,", 'ST_XMin(geom) west,',
"ST_YMin(geom) south,", 'ST_YMin(geom) south,',
"ST_XMax(geom) east,", 'ST_XMax(geom) east,',
"ST_YMax(geom) north", 'ST_YMax(geom) north',
"FROM ext" 'FROM ext'
].join(' '); ].join(' ');
this.pgQueryRunner.run(username, query, function handleBoundsResult (err, rows) { this.pgQueryRunner.run(username, query, function handleBoundsResult (err, rows) {

View File

@ -5,11 +5,9 @@ var debug = require('debug')('windshaft:templates');
var _ = require('underscore'); var _ = require('underscore');
var dot = require('dot'); var dot = require('dot');
var EventEmitter = require('events').EventEmitter; var EventEmitter = require('events').EventEmitter;
var util = require('util'); var util = require('util');
// Class handling map templates // Class handling map templates
// //
// See http://github.com/CartoDB/Windshaft-cartodb/wiki/Template-maps // See http://github.com/CartoDB/Windshaft-cartodb/wiki/Template-maps
@ -22,43 +20,42 @@ var util = require('util');
// 'max_user_templates' limit on the number of per-user // 'max_user_templates' limit on the number of per-user
// //
// //
function TemplateMaps(redis_pool, opts) { function TemplateMaps (redis_pool, opts) {
if (!(this instanceof TemplateMaps)) { if (!(this instanceof TemplateMaps)) {
return new TemplateMaps(); return new TemplateMaps();
} }
EventEmitter.call(this); EventEmitter.call(this);
this.redis_pool = redis_pool; this.redis_pool = redis_pool;
this.opts = opts || {}; this.opts = opts || {};
// Database containing templates // Database containing templates
// TODO: allow configuring ? // TODO: allow configuring ?
// NOTE: currently it is the same as // NOTE: currently it is the same as
// the one containing layergroups // the one containing layergroups
this.db_signatures = 0; this.db_signatures = 0;
// //
// Map templates are owned by a user that specifies access permissions // Map templates are owned by a user that specifies access permissions
// for their instances. // for their instances.
// //
// We have the following datastores: // We have the following datastores:
// //
// 1. User templates: set of per-user map templates // 1. User templates: set of per-user map templates
// User templates (HASH:tpl_id->tpl_val) // User templates (HASH:tpl_id->tpl_val)
this.key_usr_tpl = dot.template("map_tpl|{{=it.owner}}"); this.key_usr_tpl = dot.template('map_tpl|{{=it.owner}}');
} }
util.inherits(TemplateMaps, EventEmitter); util.inherits(TemplateMaps, EventEmitter);
module.exports = TemplateMaps; module.exports = TemplateMaps;
// --------------- PRIVATE METHODS --------------------------------
//--------------- PRIVATE METHODS -------------------------------- TemplateMaps.prototype._userTemplateLimit = function () {
return this.opts.max_user_templates || 0;
TemplateMaps.prototype._userTemplateLimit = function() {
return this.opts.max_user_templates || 0;
}; };
/** /**
@ -68,7 +65,7 @@ TemplateMaps.prototype._userTemplateLimit = function() {
* @param redisArgs - the arguments for the redis function in an array * @param redisArgs - the arguments for the redis function in an array
* @param callback - function to pass results too. * @param callback - function to pass results too.
*/ */
TemplateMaps.prototype._redisCmd = function(redisFunc, redisArgs, callback) { TemplateMaps.prototype._redisCmd = function (redisFunc, redisArgs, callback) {
this.redis_pool.acquire(this.db_signatures, (err, redisClient) => { this.redis_pool.acquire(this.db_signatures, (err, redisClient) => {
if (err) { if (err) {
return callback(err); return callback(err);
@ -87,61 +84,61 @@ TemplateMaps.prototype._redisCmd = function(redisFunc, redisArgs, callback) {
var _reValidNameIdentifier = /^[a-z0-9][0-9a-z_\-]*$/i; var _reValidNameIdentifier = /^[a-z0-9][0-9a-z_\-]*$/i;
var _reValidPlaceholderIdentifier = /^[a-z][0-9a-z_]*$/i; var _reValidPlaceholderIdentifier = /^[a-z][0-9a-z_]*$/i;
// jshint maxcomplexity:15 // jshint maxcomplexity:15
TemplateMaps.prototype._checkInvalidTemplate = function(template) { TemplateMaps.prototype._checkInvalidTemplate = function (template) {
if ( template.version !== '0.0.1' ) { if (template.version !== '0.0.1') {
return new Error("Unsupported template version " + template.version); return new Error('Unsupported template version ' + template.version);
} }
var tplname = template.name; var tplname = template.name;
if ( ! tplname ) { if (!tplname) {
return new Error("Missing template name"); return new Error('Missing template name');
} }
if ( ! tplname.match(_reValidNameIdentifier) ) { if (!tplname.match(_reValidNameIdentifier)) {
return new Error("Invalid characters in template name '" + tplname + "'"); return new Error("Invalid characters in template name '" + tplname + "'");
} }
var invalidError = isInvalidLayergroup(template.layergroup); var invalidError = isInvalidLayergroup(template.layergroup);
if (invalidError) { if (invalidError) {
return invalidError; return invalidError;
} }
var placeholders = template.placeholders || {}; var placeholders = template.placeholders || {};
var placeholderKeys = Object.keys(placeholders); var placeholderKeys = Object.keys(placeholders);
for (var i = 0, len = placeholderKeys.length; i < len; i++) { for (var i = 0, len = placeholderKeys.length; i < len; i++) {
var placeholderKey = placeholderKeys[i]; var placeholderKey = placeholderKeys[i];
if (!placeholderKey.match(_reValidPlaceholderIdentifier)) { if (!placeholderKey.match(_reValidPlaceholderIdentifier)) {
return new Error("Invalid characters in placeholder name '" + placeholderKey + "'"); return new Error("Invalid characters in placeholder name '" + placeholderKey + "'");
} }
if ( ! placeholders[placeholderKey].hasOwnProperty('default') ) { if (!placeholders[placeholderKey].hasOwnProperty('default')) {
return new Error("Missing default for placeholder '" + placeholderKey + "'"); return new Error("Missing default for placeholder '" + placeholderKey + "'");
} }
if ( ! placeholders[placeholderKey].hasOwnProperty('type') ) { if (!placeholders[placeholderKey].hasOwnProperty('type')) {
return new Error("Missing type for placeholder '" + placeholderKey + "'"); return new Error("Missing type for placeholder '" + placeholderKey + "'");
} }
} }
var auth = template.auth || {}; var auth = template.auth || {};
switch ( auth.method ) { switch (auth.method) {
case 'open': case 'open':
break; break;
case 'token': case 'token':
if ( ! _.isArray(auth.valid_tokens) ) { if (!_.isArray(auth.valid_tokens)) {
return new Error("Invalid 'token' authentication: missing valid_tokens"); return new Error("Invalid 'token' authentication: missing valid_tokens");
} }
if ( ! auth.valid_tokens.length ) { if (!auth.valid_tokens.length) {
return new Error("Invalid 'token' authentication: no valid_tokens"); return new Error("Invalid 'token' authentication: no valid_tokens");
} }
break; break;
default: default:
return new Error("Unsupported authentication method: " + auth.method); return new Error('Unsupported authentication method: ' + auth.method);
} }
return false; return false;
}; };
function isInvalidLayergroup(layergroup) { function isInvalidLayergroup (layergroup) {
if (!layergroup) { if (!layergroup) {
return new Error('Missing layergroup'); return new Error('Missing layergroup');
} }
@ -153,10 +150,10 @@ function isInvalidLayergroup(layergroup) {
} }
var invalidLayers = layers var invalidLayers = layers
.map(function(layer, layerIndex) { .map(function (layer, layerIndex) {
return layer.options ? null : layerIndex; return layer.options ? null : layerIndex;
}) })
.filter(function(layerIndex) { .filter(function (layerIndex) {
return layerIndex !== null; return layerIndex !== null;
}); });
@ -167,7 +164,7 @@ function isInvalidLayergroup(layergroup) {
return false; return false;
} }
function templateDefaults(template) { function templateDefaults (template) {
var templateAuth = _.defaults({}, template.auth || {}, { var templateAuth = _.defaults({}, template.auth || {}, {
method: 'open' method: 'open'
}); });
@ -183,10 +180,10 @@ function templateDefaults(template) {
* @param owner cartodb username of the template owner * @param owner cartodb username of the template owner
* @param callback returns error if the user reaches the limit * @param callback returns error if the user reaches the limit
*/ */
TemplateMaps.prototype._checkUserTemplatesLimit = function(userTemplatesKey, owner, callback) { TemplateMaps.prototype._checkUserTemplatesLimit = function (userTemplatesKey, owner, callback) {
const limit = this._userTemplateLimit(); const limit = this._userTemplateLimit();
if(!limit) { if (!limit) {
return callback(); return callback();
} }
@ -207,7 +204,7 @@ TemplateMaps.prototype._checkUserTemplatesLimit = function(userTemplatesKey, own
}); });
}; };
//--------------- PUBLIC API ------------------------------------- // --------------- PUBLIC API -------------------------------------
// Add a template // Add a template
// //
@ -221,7 +218,7 @@ TemplateMaps.prototype._checkUserTemplatesLimit = function(userTemplatesKey, own
// @param callback function(err, tpl_id) // @param callback function(err, tpl_id)
// Return template identifier (only valid for given user) // Return template identifier (only valid for given user)
// //
TemplateMaps.prototype.addTemplate = function(owner, template, callback) { TemplateMaps.prototype.addTemplate = function (owner, template, callback) {
template = templateDefaults(template); template = templateDefaults(template);
var invalidError = this._checkInvalidTemplate(template); var invalidError = this._checkInvalidTemplate(template);
@ -268,8 +265,8 @@ TemplateMaps.prototype.addTemplate = function(owner, template, callback) {
// //
// @param callback function(err) // @param callback function(err)
// //
TemplateMaps.prototype.delTemplate = function(owner, tpl_id, callback) { TemplateMaps.prototype.delTemplate = function (owner, tpl_id, callback) {
this._redisCmd('HDEL', [ this.key_usr_tpl({ owner:owner }), tpl_id ], (err, deleted) => { this._redisCmd('HDEL', [this.key_usr_tpl({ owner: owner }), tpl_id], (err, deleted) => {
if (err) { if (err) {
return callback(err); return callback(err);
} }
@ -299,7 +296,7 @@ TemplateMaps.prototype.delTemplate = function(owner, tpl_id, callback) {
// //
// @param callback function(err) // @param callback function(err)
// //
TemplateMaps.prototype.updTemplate = function(owner, tpl_id, template, callback) { TemplateMaps.prototype.updTemplate = function (owner, tpl_id, template, callback) {
template = templateDefaults(template); template = templateDefaults(template);
var invalidError = this._checkInvalidTemplate(template); var invalidError = this._checkInvalidTemplate(template);
@ -361,8 +358,8 @@ TemplateMaps.prototype.updTemplate = function(owner, tpl_id, template, callback)
// @param callback function(err, tpl_id_list) // @param callback function(err, tpl_id_list)
// Returns a list of template identifiers // Returns a list of template identifiers
// //
TemplateMaps.prototype.listTemplates = function(owner, callback) { TemplateMaps.prototype.listTemplates = function (owner, callback) {
this._redisCmd('HKEYS', [ this.key_usr_tpl({owner:owner}) ], callback); this._redisCmd('HKEYS', [this.key_usr_tpl({ owner: owner })], callback);
}; };
// Get a templates // Get a templates
@ -375,8 +372,8 @@ TemplateMaps.prototype.listTemplates = function(owner, callback) {
// @param callback function(err, template) // @param callback function(err, template)
// Return full template definition // Return full template definition
// //
TemplateMaps.prototype.getTemplate = function(owner, tpl_id, callback) { TemplateMaps.prototype.getTemplate = function (owner, tpl_id, callback) {
this._redisCmd('HGET', [this.key_usr_tpl({owner:owner}), tpl_id], (err, template) => { this._redisCmd('HGET', [this.key_usr_tpl({ owner: owner }), tpl_id], (err, template) => {
if (err) { if (err) {
return callback(err); return callback(err);
} }
@ -392,7 +389,7 @@ TemplateMaps.prototype.getTemplate = function(owner, tpl_id, callback) {
}); });
}; };
TemplateMaps.prototype.isAuthorized = function(template, authTokens) { TemplateMaps.prototype.isAuthorized = function (template, authTokens) {
if (!template) { if (!template) {
return false; return false;
} }
@ -432,99 +429,95 @@ TemplateMaps.prototype.isAuthorized = function(template, authTokens) {
// //
// @throws Error on malformed template or parameter // @throws Error on malformed template or parameter
// //
var _reNumber = /^([-+]?[\d\.]?\d+([eE][+-]?\d+)?)$/, var _reNumber = /^([-+]?[\d\.]?\d+([eE][+-]?\d+)?)$/;
_reCSSColorName = /^[a-zA-Z]+$/, var _reCSSColorName = /^[a-zA-Z]+$/;
_reCSSColorVal = /^#[0-9a-fA-F]{3,6}$/; var _reCSSColorVal = /^#[0-9a-fA-F]{3,6}$/;
function _replaceVars (str, params) { function _replaceVars (str, params) {
// Construct regular expressions for each param // Construct regular expressions for each param
Object.keys(params).forEach(function(k) { Object.keys(params).forEach(function (k) {
str = str.replace(new RegExp("<%=\\s*" + k + "\\s*%>", "g"), params[k]); str = str.replace(new RegExp('<%=\\s*' + k + '\\s*%>', 'g'), params[k]);
}); });
return str; return str;
} }
function isObject(val) { function isObject (val) {
return ( _.isObject(val) && !_.isArray(val) && !_.isFunction(val)); return (_.isObject(val) && !_.isArray(val) && !_.isFunction(val));
} }
TemplateMaps.prototype.instance = function(template, params) { TemplateMaps.prototype.instance = function (template, params) {
var all_params = {}; var all_params = {};
var phold = template.placeholders || {}; var phold = template.placeholders || {};
Object.keys(phold).forEach(function(k) { Object.keys(phold).forEach(function (k) {
var val = params.hasOwnProperty(k) ? params[k] : phold[k].default; var val = params.hasOwnProperty(k) ? params[k] : phold[k].default;
var type = phold[k].type; var type = phold[k].type;
// properly escape // properly escape
if ( type === 'sql_literal' ) { if (type === 'sql_literal') {
// duplicate any single-quote // duplicate any single-quote
val = val.replace(/'/g, "''"); val = val.replace(/'/g, "''");
} } else if (type === 'sql_ident') {
else if ( type === 'sql_ident' ) { // duplicate any double-quote
// duplicate any double-quote val = val.replace(/"/g, '""');
val = val.replace(/"/g, '""'); } else if (type === 'number') {
} // check it's a number
else if ( type === 'number' ) { if (typeof (val) !== 'number' && !val.match(_reNumber)) {
// check it's a number throw new Error("Invalid number value for template parameter '" + k + "': " + val);
if ( typeof(val) !== 'number' && ! val.match(_reNumber) ) { }
throw new Error("Invalid number value for template parameter '" + k + "': " + val); } else if (type === 'css_color') {
} // check it only contains letters or
} // starts with # and only contains hexdigits
else if ( type === 'css_color' ) { if (!val.match(_reCSSColorName) && !val.match(_reCSSColorVal)) {
// check it only contains letters or throw new Error("Invalid css_color value for template parameter '" + k + "': " + val);
// starts with # and only contains hexdigits }
if ( ! val.match(_reCSSColorName) && ! val.match(_reCSSColorVal) ) { } else {
throw new Error("Invalid css_color value for template parameter '" + k + "': " + val); // NOTE: should be checked at template create/update time
} throw new Error("Invalid placeholder type '" + type + "'");
} }
else { all_params[k] = val;
// NOTE: should be checked at template create/update time });
throw new Error("Invalid placeholder type '" + type + "'");
}
all_params[k] = val;
});
// NOTE: we're deep-cloning the layergroup here // NOTE: we're deep-cloning the layergroup here
var layergroup = JSON.parse(JSON.stringify(template.layergroup)); var layergroup = JSON.parse(JSON.stringify(template.layergroup));
if (layergroup.buffersize && isObject(layergroup.buffersize)) { if (layergroup.buffersize && isObject(layergroup.buffersize)) {
Object.keys(layergroup.buffersize).forEach(function(k) { Object.keys(layergroup.buffersize).forEach(function (k) {
layergroup.buffersize[k] = parseInt(_replaceVars(layergroup.buffersize[k], all_params), 10); layergroup.buffersize[k] = parseInt(_replaceVars(layergroup.buffersize[k], all_params), 10);
}); });
} }
for (var i=0; i<layergroup.layers.length; ++i) { for (var i = 0; i < layergroup.layers.length; ++i) {
var lyropt = layergroup.layers[i].options; var lyropt = layergroup.layers[i].options;
if ( params.styles && params.styles[i] ) { if (params.styles && params.styles[i]) {
// dynamic styling for this layer // dynamic styling for this layer
lyropt.cartocss = params.styles[i]; lyropt.cartocss = params.styles[i];
} else if ( lyropt.cartocss ) { } else if (lyropt.cartocss) {
lyropt.cartocss = _replaceVars(lyropt.cartocss, all_params); lyropt.cartocss = _replaceVars(lyropt.cartocss, all_params);
} }
if ( lyropt.sql) { if (lyropt.sql) {
lyropt.sql = _replaceVars(lyropt.sql, all_params); lyropt.sql = _replaceVars(lyropt.sql, all_params);
} }
// Anything else ? // Anything else ?
} }
// extra information about the template // extra information about the template
layergroup.template = { layergroup.template = {
name: template.name, name: template.name,
auth: template.auth auth: template.auth
}; };
return layergroup; return layergroup;
}; };
// Return a fingerPrint of the object // Return a fingerPrint of the object
TemplateMaps.prototype.fingerPrint = function(template) { TemplateMaps.prototype.fingerPrint = function (template) {
return crypto.createHash('md5') return crypto.createHash('md5')
.update(JSON.stringify(template)) .update(JSON.stringify(template))
.digest('hex') .digest('hex')
; ;
}; };
module.exports.templateName = function templateName(templateId) { module.exports.templateName = function templateName (templateId) {
var templateIdTokens = templateId.split('@'); var templateIdTokens = templateId.split('@');
var name = templateIdTokens[0]; var name = templateIdTokens[0];

View File

@ -3,7 +3,7 @@
var dot = require('dot'); var dot = require('dot');
dot.templateSettings.strip = false; dot.templateSettings.strip = false;
function createTemplate(method) { function createTemplate (method) {
return dot.template([ return dot.template([
'SELECT', 'SELECT',
'min({{=it._column}}) min_val,', 'min({{=it._column}}) min_val,',
@ -27,7 +27,7 @@ var methods = {
headtails: 'CDB_HeadsTailsBins(array_agg({{=it._column}}::numeric), {{=it._buckets}}) as headtails' headtails: 'CDB_HeadsTailsBins(array_agg({{=it._column}}::numeric), {{=it._buckets}}) as headtails'
}; };
var methodTemplates = Object.keys(methods).reduce(function(methodTemplates, methodName) { var methodTemplates = Object.keys(methods).reduce(function (methodTemplates, methodName) {
methodTemplates[methodName] = createTemplate(methods[methodName]); methodTemplates[methodName] = createTemplate(methods[methodName]);
return methodTemplates; return methodTemplates;
}, {}); }, {});
@ -94,9 +94,9 @@ PostgresDatasource.prototype.getRamp = function (column, buckets, method, callba
// Skip null values from ramp // Skip null values from ramp
// Generated turbo-carto won't be correct, but better to keep it working than failing // Generated turbo-carto won't be correct, but better to keep it working than failing
// TODO fix cartodb-postgres extension quantification functions // TODO fix cartodb-postgres extension quantification functions
ramp = ramp.filter(function(value) { return value !== null; }); ramp = ramp.filter(function (value) { return value !== null; });
if (strategy !== STRATEGY.EXACT) { if (strategy !== STRATEGY.EXACT) {
ramp = ramp.sort(function(a, b) { ramp = ramp.sort(function (a, b) {
return a - b; return a - b;
}); });
} }
@ -105,7 +105,7 @@ PostgresDatasource.prototype.getRamp = function (column, buckets, method, callba
}, true); // use read-only transaction }, true); // use read-only transaction
}; };
function getResult(resultSet) { function getResult (resultSet) {
resultSet = resultSet || {}; resultSet = resultSet || {};
var result = resultSet.rows || []; var result = resultSet.rows || [];
result = result[0] || {}; result = result[0] || {};

View File

@ -7,7 +7,7 @@
* @constructor * @constructor
* @type {UserLimitsBackend} * @type {UserLimitsBackend}
*/ */
function UserLimitsBackend(metadataBackend, options) { function UserLimitsBackend (metadataBackend, options) {
this.metadataBackend = metadataBackend; this.metadataBackend = metadataBackend;
this.options = options || {}; this.options = options || {};
this.options.limits = this.options.limits || {}; this.options.limits = this.options.limits || {};
@ -59,7 +59,7 @@ UserLimitsBackend.prototype.getTimeoutRenderLimit = function (username, apiKey,
}); });
}; };
function isAuthorized(metadataBackend, username, apiKey, callback) { function isAuthorized (metadataBackend, username, apiKey, callback) {
if (!apiKey) { if (!apiKey) {
return callback(null, false); return callback(null, false);
} }

View File

@ -2,7 +2,7 @@
var FastlyPurge = require('fastly-purge'); var FastlyPurge = require('fastly-purge');
function FastlyCacheBackend(apiKey, serviceId) { function FastlyCacheBackend (apiKey, serviceId) {
this.serviceId = serviceId; this.serviceId = serviceId;
this.fastlyPurge = new FastlyPurge(apiKey, { softPurge: false }); this.fastlyPurge = new FastlyPurge(apiKey, { softPurge: false });
} }
@ -13,6 +13,6 @@ module.exports = FastlyCacheBackend;
* @param cacheObject should respond to `key() -> String` method * @param cacheObject should respond to `key() -> String` method
* @param {Function} callback * @param {Function} callback
*/ */
FastlyCacheBackend.prototype.invalidate = function(cacheObject, callback) { FastlyCacheBackend.prototype.invalidate = function (cacheObject, callback) {
this.fastlyPurge.key(this.serviceId, cacheObject.key(), callback); this.fastlyPurge.key(this.serviceId, cacheObject.key(), callback);
}; };

View File

@ -2,7 +2,7 @@
var request = require('request'); var request = require('request');
function VarnishHttpCacheBackend(host, port) { function VarnishHttpCacheBackend (host, port) {
this.host = host; this.host = host;
this.port = port; this.port = port;
} }
@ -13,7 +13,7 @@ module.exports = VarnishHttpCacheBackend;
* @param cacheObject should respond to `key() -> String` method * @param cacheObject should respond to `key() -> String` method
* @param {Function} callback * @param {Function} callback
*/ */
VarnishHttpCacheBackend.prototype.invalidate = function(cacheObject, callback) { VarnishHttpCacheBackend.prototype.invalidate = function (cacheObject, callback) {
request( request(
{ {
method: 'PURGE', method: 'PURGE',
@ -22,7 +22,7 @@ VarnishHttpCacheBackend.prototype.invalidate = function(cacheObject, callback) {
'Invalidation-Match': '\\b' + cacheObject.key() + '\\b' 'Invalidation-Match': '\\b' + cacheObject.key() + '\\b'
} }
}, },
function(err, response) { function (err, response) {
if (err || response.statusCode !== 204) { if (err || response.statusCode !== 204) {
return callback(new Error('Unable to invalidate Varnish object')); return callback(new Error('Unable to invalidate Varnish object'));
} }

View File

@ -2,25 +2,25 @@
var LruCache = require('lru-cache'); var LruCache = require('lru-cache');
function LayergroupAffectedTables() { function LayergroupAffectedTables () {
// dbname + layergroupId -> affected tables cache // dbname + layergroupId -> affected tables cache
this.cache = new LruCache({ max: 2000 }); this.cache = new LruCache({ max: 2000 });
} }
module.exports = LayergroupAffectedTables; module.exports = LayergroupAffectedTables;
LayergroupAffectedTables.prototype.hasAffectedTables = function(dbName, layergroupId) { LayergroupAffectedTables.prototype.hasAffectedTables = function (dbName, layergroupId) {
return this.cache.has(createKey(dbName, layergroupId)); return this.cache.has(createKey(dbName, layergroupId));
}; };
LayergroupAffectedTables.prototype.set = function(dbName, layergroupId, affectedTables) { LayergroupAffectedTables.prototype.set = function (dbName, layergroupId, affectedTables) {
this.cache.set(createKey(dbName, layergroupId), affectedTables); this.cache.set(createKey(dbName, layergroupId), affectedTables);
}; };
LayergroupAffectedTables.prototype.get = function(dbName, layergroupId) { LayergroupAffectedTables.prototype.get = function (dbName, layergroupId) {
return this.cache.get(createKey(dbName, layergroupId)); return this.cache.get(createKey(dbName, layergroupId));
}; };
function createKey(dbName, layergroupId) { function createKey (dbName, layergroupId) {
return dbName + ':' + layergroupId; return dbName + ':' + layergroupId;
} }

View File

@ -2,7 +2,7 @@
var crypto = require('crypto'); var crypto = require('crypto');
function NamedMaps(owner, name) { function NamedMaps (owner, name) {
this.namespace = 'n'; this.namespace = 'n';
this.owner = owner; this.owner = owner;
this.name = name; this.name = name;
@ -10,11 +10,10 @@ function NamedMaps(owner, name) {
module.exports = NamedMaps; module.exports = NamedMaps;
NamedMaps.prototype.key = function () {
NamedMaps.prototype.key = function() {
return this.namespace + ':' + shortHashKey(this.owner + ':' + this.name); return this.namespace + ':' + shortHashKey(this.owner + ':' + this.name);
}; };
function shortHashKey(target) { function shortHashKey (target) {
return crypto.createHash('sha256').update(target).digest('base64').substring(0,6); return crypto.createHash('sha256').update(target).digest('base64').substring(0, 6);
} }

View File

@ -6,28 +6,26 @@ var queue = require('queue-async');
* @param {Array|Object} cacheBackends each backend backend should respond to `invalidate(cacheObject, callback)` method * @param {Array|Object} cacheBackends each backend backend should respond to `invalidate(cacheObject, callback)` method
* @constructor * @constructor
*/ */
function SurrogateKeysCache(cacheBackends) { function SurrogateKeysCache (cacheBackends) {
this.cacheBackends = Array.isArray(cacheBackends) ? cacheBackends : [cacheBackends]; this.cacheBackends = Array.isArray(cacheBackends) ? cacheBackends : [cacheBackends];
} }
module.exports = SurrogateKeysCache; module.exports = SurrogateKeysCache;
/** /**
* @param response should respond to `header(key, value)` method * @param response should respond to `header(key, value)` method
* @param cacheObject should respond to `key() -> String` method * @param cacheObject should respond to `key() -> String` method
*/ */
SurrogateKeysCache.prototype.tag = function(response, cacheObject) { SurrogateKeysCache.prototype.tag = function (response, cacheObject) {
var newKey = cacheObject.key(); var newKey = cacheObject.key();
response.set('Surrogate-Key', appendSurrogateKey( response.set('Surrogate-Key', appendSurrogateKey(
response.get('Surrogate-Key'), response.get('Surrogate-Key'),
Array.isArray(newKey) ? cacheObject.key().join(' ') : newKey Array.isArray(newKey) ? cacheObject.key().join(' ') : newKey
)); ));
}; };
function appendSurrogateKey(currentKey, newKey) { function appendSurrogateKey (currentKey, newKey) {
if (!!currentKey) { if (currentKey) {
newKey = currentKey + ' ' + newKey; newKey = currentKey + ' ' + newKey;
} }
return newKey; return newKey;
@ -37,16 +35,16 @@ function appendSurrogateKey(currentKey, newKey) {
* @param cacheObject should respond to `key() -> String` method * @param cacheObject should respond to `key() -> String` method
* @param {Function} callback * @param {Function} callback
*/ */
SurrogateKeysCache.prototype.invalidate = function(cacheObject, callback) { SurrogateKeysCache.prototype.invalidate = function (cacheObject, callback) {
var invalidationQueue = queue(this.cacheBackends.length); var invalidationQueue = queue(this.cacheBackends.length);
this.cacheBackends.forEach(function(cacheBackend) { this.cacheBackends.forEach(function (cacheBackend) {
invalidationQueue.defer(function(cacheBackend, done) { invalidationQueue.defer(function (cacheBackend, done) {
cacheBackend.invalidate(cacheObject, done); cacheBackend.invalidate(cacheObject, done);
}, cacheBackend); }, cacheBackend);
}); });
invalidationQueue.awaitAll(function(err, result) { invalidationQueue.awaitAll(function (err, result) {
if (err) { if (err) {
return callback(err); return callback(err);
} }

View File

@ -53,11 +53,11 @@ module.exports = class AggregationMapConfig extends MapConfig {
return null; return null;
} }
static supportsGeometryType(geometryType) { static supportsGeometryType (geometryType) {
return AggregationMapConfig.SUPPORTED_GEOMETRY_TYPES.includes(geometryType); return AggregationMapConfig.SUPPORTED_GEOMETRY_TYPES.includes(geometryType);
} }
static getAggregationGeometryColumn() { static getAggregationGeometryColumn () {
return aggregationQuery.GEOMETRY_COLUMN; return aggregationQuery.GEOMETRY_COLUMN;
} }
@ -108,7 +108,7 @@ module.exports = class AggregationMapConfig extends MapConfig {
} }
isAggregationLayer (index) { isAggregationLayer (index) {
let hasAggregation = this.hasLayerAggregation(index); const hasAggregation = this.hasLayerAggregation(index);
// for vector-only MapConfig are aggregated unless explicitly disabled // for vector-only MapConfig are aggregated unless explicitly disabled
return hasAggregation || ( return hasAggregation || (
this.isVectorOnlyMapConfig() && hasAggregation !== AggregationMapConfig.HAS_AGGREGATION_DISABLED this.isVectorOnlyMapConfig() && hasAggregation !== AggregationMapConfig.HAS_AGGREGATION_DISABLED
@ -176,7 +176,7 @@ module.exports = class AggregationMapConfig extends MapConfig {
_getLayerAggregationRequiredColumns (index) { _getLayerAggregationRequiredColumns (index) {
const { columns, dimensions } = this.getAggregation(index); const { columns, dimensions } = this.getAggregation(index);
let finalColumns = ['cartodb_id', '_cdb_feature_count']; const finalColumns = ['cartodb_id', '_cdb_feature_count'];
let aggregatedColumns = []; let aggregatedColumns = [];
if (columns) { if (columns) {
@ -191,10 +191,10 @@ module.exports = class AggregationMapConfig extends MapConfig {
return removeDuplicates(finalColumns.concat(aggregatedColumns).concat(dimensionsColumns)); return removeDuplicates(finalColumns.concat(aggregatedColumns).concat(dimensionsColumns));
} }
doesLayerReachThreshold(index, featureCount) { doesLayerReachThreshold (index, featureCount) {
const threshold = this.getAggregation(index) && this.getAggregation(index).threshold ? const threshold = this.getAggregation(index) && this.getAggregation(index).threshold
this.getAggregation(index).threshold : ? this.getAggregation(index).threshold
AggregationMapConfig.THRESHOLD; : AggregationMapConfig.THRESHOLD;
return featureCount >= threshold; return featureCount >= threshold;
} }
@ -243,7 +243,7 @@ module.exports = class AggregationMapConfig extends MapConfig {
this._isEmptyParameter(aggregation.filters); this._isEmptyParameter(aggregation.filters);
} }
_isEmptyParameter(parameter) { _isEmptyParameter (parameter) {
return parameter === undefined || parameter === null || this._isEmptyObject(parameter); return parameter === undefined || parameter === null || this._isEmptyObject(parameter);
} }

View File

@ -9,7 +9,7 @@ const webmercator = new WebMercatorHelper();
function optionsToParams (options) { function optionsToParams (options) {
return { return {
sourceQuery: options.query, sourceQuery: options.query,
res: 256/options.resolution, res: 256 / options.resolution,
columns: options.columns, columns: options.columns,
dimensions: options.dimensions, dimensions: options.dimensions,
filters: options.filters, filters: options.filters,
@ -50,22 +50,22 @@ module.exports.infoForOptions = (options) => {
}; };
const SUPPORTED_AGGREGATE_FUNCTIONS = { const SUPPORTED_AGGREGATE_FUNCTIONS = {
'count': { count: {
sql: (column_name, params) => `count(${params.aggregated_column || '*'})` sql: (column_name, params) => `count(${params.aggregated_column || '*'})`
}, },
'avg': { avg: {
sql: (column_name, params) => `avg(${params.aggregated_column || column_name})` sql: (column_name, params) => `avg(${params.aggregated_column || column_name})`
}, },
'sum': { sum: {
sql: (column_name, params) => `sum(${params.aggregated_column || column_name})` sql: (column_name, params) => `sum(${params.aggregated_column || column_name})`
}, },
'min': { min: {
sql: (column_name, params) => `min(${params.aggregated_column || column_name})` sql: (column_name, params) => `min(${params.aggregated_column || column_name})`
}, },
'max': { max: {
sql: (column_name, params) => `max(${params.aggregated_column || column_name})` sql: (column_name, params) => `max(${params.aggregated_column || column_name})`
}, },
'mode': { mode: {
sql: (column_name, params) => `mode() WITHIN GROUP (ORDER BY ${params.aggregated_column || column_name})` sql: (column_name, params) => `mode() WITHIN GROUP (ORDER BY ${params.aggregated_column || column_name})`
} }
}; };
@ -73,7 +73,7 @@ const SUPPORTED_AGGREGATE_FUNCTIONS = {
module.exports.SUPPORTED_AGGREGATE_FUNCTIONS = Object.keys(SUPPORTED_AGGREGATE_FUNCTIONS); module.exports.SUPPORTED_AGGREGATE_FUNCTIONS = Object.keys(SUPPORTED_AGGREGATE_FUNCTIONS);
const sep = (list) => { const sep = (list) => {
let expr = list.join(', '); const expr = list.join(', ');
return expr ? ', ' + expr : expr; return expr ? ', ' + expr : expr;
}; };
@ -95,7 +95,7 @@ const aggregateExpression = (column_name, column_parameters) => {
}; };
const aggregateColumnDefs = ctx => { const aggregateColumnDefs = ctx => {
let columns = aggregateColumns(ctx); const columns = aggregateColumns(ctx);
return sep(Object.keys(columns).map(column_name => { return sep(Object.keys(columns).map(column_name => {
const aggregate_expression = aggregateExpression(column_name, columns[column_name]); const aggregate_expression = aggregateExpression(column_name, columns[column_name]);
return `${aggregate_expression} AS ${column_name}`; return `${aggregate_expression} AS ${column_name}`;
@ -119,7 +119,7 @@ const timeDimensionParameters = definition => {
// Adapt old-style dimension definitions for backwards compatibility // Adapt old-style dimension definitions for backwards compatibility
const adaptDimensionDefinition = definition => { const adaptDimensionDefinition = definition => {
if (typeof(definition) === 'string') { if (typeof (definition) === 'string') {
return { column: definition }; return { column: definition };
} }
return definition; return definition;
@ -135,7 +135,7 @@ const dimensionExpression = definition => {
}; };
const dimensionNamesAndExpressions = (ctx) => { const dimensionNamesAndExpressions = (ctx) => {
let dimensions = aggregateDimensions(ctx); const dimensions = aggregateDimensions(ctx);
return Object.keys(dimensions).map(dimensionName => { return Object.keys(dimensions).map(dimensionName => {
const dimension = adaptDimensionDefinition(dimensions[dimensionName]); const dimension = adaptDimensionDefinition(dimensions[dimensionName]);
const expression = dimensionExpression(dimension); const expression = dimensionExpression(dimension);
@ -152,7 +152,7 @@ const dimensionNames = (ctx, table) => {
const dimensionDefs = ctx => { const dimensionDefs = ctx => {
return sep( return sep(
dimensionNamesAndExpressions(ctx) dimensionNamesAndExpressions(ctx)
.map(([dimensionName, expression]) => `${expression.sql} AS "${dimensionName}"`) .map(([dimensionName, expression]) => `${expression.sql} AS "${dimensionName}"`)
); );
}; };
@ -192,7 +192,7 @@ const sqlQ = (value) => {
const FILTERS = { const FILTERS = {
between: (expr, filter) => { between: (expr, filter) => {
const lo = filter.greater_than_or_equal_to, hi = filter.less_than_or_equal_to; const lo = filter.greater_than_or_equal_to; const hi = filter.less_than_or_equal_to;
if (lo != null && hi != null) { if (lo != null && hi != null) {
return `(${expr} BETWEEN ${sqlQ(lo)} AND ${sqlQ(hi)})`; return `(${expr} BETWEEN ${sqlQ(lo)} AND ${sqlQ(hi)})`;
} }
@ -218,7 +218,7 @@ const FILTERS = {
} }
}, },
range: (expr, filter) => { range: (expr, filter) => {
let conds = []; const conds = [];
if (filter.greater_than_or_equal_to != null) { if (filter.greater_than_or_equal_to != null) {
conds.push(`(${expr} >= ${sqlQ(filter.greater_than_or_equal_to)})`); conds.push(`(${expr} >= ${sqlQ(filter.greater_than_or_equal_to)})`);
} }
@ -238,15 +238,14 @@ const FILTERS = {
}; };
const filterConditions = ctx => { const filterConditions = ctx => {
let columns = aggregateColumns(ctx); const columns = aggregateColumns(ctx);
let dimensions = aggregateDimensions(ctx); const dimensions = aggregateDimensions(ctx);
let filters = aggregateFilters(ctx); const filters = aggregateFilters(ctx);
return Object.keys(filters).map(filtered_column => { return Object.keys(filters).map(filtered_column => {
let filtered_expr; let filtered_expr;
if (columns[filtered_column]) { if (columns[filtered_column]) {
filtered_expr = aggregateExpression(filtered_column, columns[filtered_column]); filtered_expr = aggregateExpression(filtered_column, columns[filtered_column]);
} } else if (dimensions[filtered_column]) {
else if (dimensions[filtered_column]) {
filtered_expr = dimensions[filtered_column]; filtered_expr = dimensions[filtered_column];
} }
if (!filtered_expr) { if (!filtered_expr) {
@ -257,7 +256,7 @@ const filterConditions = ctx => {
}; };
const havingClause = ctx => { const havingClause = ctx => {
let cond = filterConditions(ctx); const cond = filterConditions(ctx);
return cond ? `HAVING ${cond}` : ''; return cond ? `HAVING ${cond}` : '';
}; };
@ -274,8 +273,8 @@ const havingClause = ctx => {
// NOTE 2: The 0.00028 is used in Mapnik (and replicated in pg-mvt) and comes from // NOTE 2: The 0.00028 is used in Mapnik (and replicated in pg-mvt) and comes from
// OGC's Styled Layer Descriptor Implementation Specification // OGC's Styled Layer Descriptor Implementation Specification
const gridResolution = ctx => { const gridResolution = ctx => {
const minimumResolution = webmercator.getResolution({ z : 38 }); const minimumResolution = webmercator.getResolution({ z: 38 });
return `${256/ctx.res} * GREATEST(!scale_denominator! * 0.00028, ${minimumResolution})::double precision`; return `${256 / ctx.res} * GREATEST(!scale_denominator! * 0.00028, ${minimumResolution})::double precision`;
}; };
// SQL query to extract the boundaries of the area to be aggregated and the grid resolution // SQL query to extract the boundaries of the area to be aggregated and the grid resolution
@ -309,46 +308,43 @@ const gridInfoQuery = ctx => {
`; `;
}; };
// Function to generate the resulting point for a cell from the aggregated data // Function to generate the resulting point for a cell from the aggregated data
const aggregatedPointWebMercator = (ctx) => { const aggregatedPointWebMercator = (ctx) => {
switch (ctx.placement) { switch (ctx.placement) {
// For centroid, we return the average of the cell
// For centroid, we return the average of the cell case 'centroid':
case 'centroid': return ', ST_SetSRID(ST_MakePoint(AVG(cdb_x), AVG(cdb_y)), 3857) AS the_geom_webmercator';
return ', ST_SetSRID(ST_MakePoint(AVG(cdb_x), AVG(cdb_y)), 3857) AS the_geom_webmercator';
// Middle point of the cell // Middle point of the cell
case 'point-grid': case 'point-grid':
return `, ST_SetSRID(ST_MakePoint(cdb_pos_grid_x, cdb_pos_grid_y), 3857) AS the_geom_webmercator`; return ', ST_SetSRID(ST_MakePoint(cdb_pos_grid_x, cdb_pos_grid_y), 3857) AS the_geom_webmercator';
// For point-sample we'll get a single point directly from the source // For point-sample we'll get a single point directly from the source
// If it's default aggregation we'll add the extra columns to keep backwards compatibility // If it's default aggregation we'll add the extra columns to keep backwards compatibility
case 'point-sample': case 'point-sample':
return ''; return '';
default: default:
throw new Error(`Invalid aggregation placement "${ctx.placement}"`); throw new Error(`Invalid aggregation placement "${ctx.placement}"`);
} }
}; };
// Function to generate the resulting point for a cell from the a join with the source // Function to generate the resulting point for a cell from the a join with the source
const aggregatedPointJoin = (ctx) => { const aggregatedPointJoin = (ctx) => {
switch (ctx.placement) { switch (ctx.placement) {
case 'centroid':
return '';
case 'centroid': case 'point-grid':
return ''; return '';
case 'point-grid':
return '';
// For point-sample we'll get a single point directly from the source // For point-sample we'll get a single point directly from the source
// If it's default aggregation we'll add the extra columns to keep backwards compatibility // If it's default aggregation we'll add the extra columns to keep backwards compatibility
case 'point-sample': case 'point-sample':
return ` return `
NATURAL JOIN NATURAL JOIN
( (
SELECT ${ctx.isDefaultAggregation ? `*` : `cartodb_id, the_geom_webmercator`} SELECT ${ctx.isDefaultAggregation ? '*' : 'cartodb_id, the_geom_webmercator'}
FROM FROM
( (
${ctx.sourceQuery} ${ctx.sourceQuery}
@ -356,8 +352,8 @@ NATURAL JOIN
) __cdb_query_columns ) __cdb_query_columns
`; `;
default: default:
throw new Error('Invalid aggregation placement "${ctx.placement}"'); throw new Error('Invalid aggregation placement "${ctx.placement}"');
} }
}; };
@ -367,17 +363,16 @@ NATURAL JOIN
// which requires extra data in the group by clause // which requires extra data in the group by clause
const aggregatedPosCoordinate = (ctx, coordinate) => { const aggregatedPosCoordinate = (ctx, coordinate) => {
switch (ctx.placement) { switch (ctx.placement) {
// For point-grid we return the coordinate of the middle point of the grid // For point-grid we return the coordinate of the middle point of the grid
case `point-grid`: case 'point-grid':
return `(FLOOR(cdb_${coordinate} / __cdb_grid_params.cdb_res) + 0.5) * __cdb_grid_params.cdb_res`; return `(FLOOR(cdb_${coordinate} / __cdb_grid_params.cdb_res) + 0.5) * __cdb_grid_params.cdb_res`;
// For other, we return the cell position (relative to the world) // For other, we return the cell position (relative to the world)
default: default:
return `FLOOR(cdb_${coordinate} / __cdb_grid_params.cdb_res)`; return `FLOOR(cdb_${coordinate} / __cdb_grid_params.cdb_res)`;
} }
}; };
const aggregationQueryTemplate = ctx => ` const aggregationQueryTemplate = ctx => `
WITH __cdb_grid_params AS WITH __cdb_grid_params AS
( (
@ -445,5 +440,5 @@ const clusterFeaturesQuery = ctx => `
module.exports.featuresQuery = (id, options) => clusterFeaturesQuery({ module.exports.featuresQuery = (id, options) => clusterFeaturesQuery({
id, id,
sourceQuery: options.query, sourceQuery: options.query,
res: 256/options.resolution res: 256 / options.resolution
}); });

View File

@ -76,11 +76,11 @@ module.exports.createAggregationFiltersValidator = function (mapconfig, validPar
}; };
}; };
function createAggregationColumnNamesValidator(mapconfig) { function createAggregationColumnNamesValidator (mapconfig) {
return function validateAggregationColumnNames (value, key, index) { return function validateAggregationColumnNames (value, key, index) {
Object.keys(value).forEach((columnName) => { Object.keys(value).forEach((columnName) => {
if (columnName.length <= 0) { if (columnName.length <= 0) {
const message = `Invalid column name, should be a non empty string`; const message = 'Invalid column name, should be a non empty string';
throw createLayerError(message, mapconfig, index); throw createLayerError(message, mapconfig, index);
} }
}); });
@ -107,14 +107,14 @@ function createAggregatedColumnValidator (mapconfig) {
const { aggregated_column } = value[columnName]; const { aggregated_column } = value[columnName];
if (typeof aggregated_column !== 'string' || aggregated_column <= 0) { if (typeof aggregated_column !== 'string' || aggregated_column <= 0) {
const message = `Invalid aggregated column, should be a non empty string`; const message = 'Invalid aggregated column, should be a non empty string';
throw createLayerError(message, mapconfig, index); throw createLayerError(message, mapconfig, index);
} }
}); });
}; };
} }
function createLayerError(message, mapconfig, index) { function createLayerError (message, mapconfig, index) {
const error = new Error(message); const error = new Error(message);
error.type = 'layer'; error.type = 'layer';
error.layer = { error.layer = {

View File

@ -4,7 +4,7 @@
// a valid (case-insensitive) tz/PG name; // a valid (case-insensitive) tz/PG name;
// they include abbreviations defined by PG (which have precedence and // they include abbreviations defined by PG (which have precedence and
// are fixed offsets, not handling DST) or general names that can handle DST. // are fixed offsets, not handling DST) or general names that can handle DST.
function timezone(tz) { function timezone (tz) {
if (isFinite(tz)) { if (isFinite(tz)) {
return `INTERVAL '${tz} seconds'`; return `INTERVAL '${tz} seconds'`;
} }
@ -19,21 +19,21 @@ function timezone(tz) {
// So, for using this with aggregations, relying on dates & times // So, for using this with aggregations, relying on dates & times
// converted to UTC UNIX epoch numbers, apply `to_timestamp` to the // converted to UTC UNIX epoch numbers, apply `to_timestamp` to the
// (converted) column. // (converted) column.
function timeExpression(t, tz) { function timeExpression (t, tz) {
if (tz !== undefined) { if (tz !== undefined) {
return `timezone(${timezone(tz)}, ${t})`; return `timezone(${timezone(tz)}, ${t})`;
} }
return t; return t;
} }
function epochWithDefaults(epoch) { function epochWithDefaults (epoch) {
/* jshint maxcomplexity:9 */ // goddammit linter, I like this as is!! /* jshint maxcomplexity:9 */ // goddammit linter, I like this as is!!
const format = /^(\d\d\d\d)(?:\-?(\d\d)(?:\-?(\d\d)(?:[T\s]?(\d\d)(?:(\d\d)(?:\:(\d\d))?)?)?)?)?$/; const format = /^(\d\d\d\d)(?:\-?(\d\d)(?:\-?(\d\d)(?:[T\s]?(\d\d)(?:(\d\d)(?:\:(\d\d))?)?)?)?)?$/;
const match = (epoch || '').match(format) || []; const match = (epoch || '').match(format) || [];
const year = match[1] || '0001'; const year = match[1] || '0001';
const month = match[2] || '01'; const month = match[2] || '01';
const day = match[3] || '01'; const day = match[3] || '01';
const hour = match[4] || '00'; const hour = match[4] || '00';
const minute = match[5] || '00'; const minute = match[5] || '00';
const second = match[6] || '00'; const second = match[6] || '00';
return `${year}-${month}-${day}T${hour}:${minute}:${second}`; return `${year}-${month}-${day}T${hour}:${minute}:${second}`;
@ -44,9 +44,9 @@ function epochWithDefaults(epoch) {
// It can be partial, e.g. 'YYYY', 'YYYY-MM', 'YYYY-MM-DDTHH', etc. // It can be partial, e.g. 'YYYY', 'YYYY-MM', 'YYYY-MM-DDTHH', etc.
// Defaults are applied: YYYY=0001, MM=01, DD=01, HH=00, MM=00, S=00 // Defaults are applied: YYYY=0001, MM=01, DD=01, HH=00, MM=00, S=00
// It returns a timestamp without time zone // It returns a timestamp without time zone
function epochExpression(epoch) { function epochExpression (epoch) {
return `TIMESTAMP '${epoch}'`; return `TIMESTAMP '${epoch}'`;
} }
const YEARSPAN = "(date_part('year', $t)-date_part('year', $epoch))"; const YEARSPAN = "(date_part('year', $t)-date_part('year', $epoch))";
// Note that SECONDSPAN is not a UTC epoch, but an epoch in the specified TZ, // Note that SECONDSPAN is not a UTC epoch, but an epoch in the specified TZ,
@ -112,10 +112,10 @@ const serialParts = {
} }
}; };
function serialSqlExpr(params) { function serialSqlExpr (params) {
const { sql, zeroBased } = serialParts[params.units]; const { sql, zeroBased } = serialParts[params.units];
const column = timeExpression(params.time, params.timezone); const column = timeExpression(params.time, params.timezone);
const epoch = epochExpression(params.starting); const epoch = epochExpression(params.starting);
const serial = sql.replace(/\$t/g, column).replace(/\$epoch/g, epoch); const serial = sql.replace(/\$t/g, column).replace(/\$epoch/g, epoch);
let expr = serial; let expr = serial;
if (params.count !== 1) { if (params.count !== 1) {
@ -131,22 +131,22 @@ function serialSqlExpr(params) {
} }
const isoParts = { const isoParts = {
second: `to_char($t, 'YYYY-MM-DD"T"HH24:MI:SS')`, second: 'to_char($t, \'YYYY-MM-DD"T"HH24:MI:SS\')',
minute: `to_char($t, 'YYYY-MM-DD"T"HH24:MI')`, minute: 'to_char($t, \'YYYY-MM-DD"T"HH24:MI\')',
hour: `to_char($t, 'YYYY-MM-DD"T"HH24')`, hour: 'to_char($t, \'YYYY-MM-DD"T"HH24\')',
day: `to_char($t, 'YYYY-MM-DD')`, day: 'to_char($t, \'YYYY-MM-DD\')',
month: `to_char($t, 'YYYY-MM')`, month: 'to_char($t, \'YYYY-MM\')',
year: `to_char($t, 'YYYY')`, year: 'to_char($t, \'YYYY\')',
week: `to_char($t, 'IYYY-"W"IW')`, week: 'to_char($t, \'IYYY-"W"IW\')',
quarter: `to_char($t, 'YYYY-"Q"Q')`, quarter: 'to_char($t, \'YYYY-"Q"Q\')',
semester: `to_char($t, 'YYYY"S"') || to_char(CEIL(date_part('month', $t)/6), '9')`, semester: 'to_char($t, \'YYYY"S"\') || to_char(CEIL(date_part(\'month\', $t)/6), \'9\')',
trimester: `to_char($t, 'YYYY"t"') || to_char(CEIL(date_part('month', $t)/4), '9')`, trimester: 'to_char($t, \'YYYY"t"\') || to_char(CEIL(date_part(\'month\', $t)/4), \'9\')',
decade: `to_char(date_part('decade', $t), '"D"999')`, decade: 'to_char(date_part(\'decade\', $t), \'"D"999\')',
century: `to_char($t, '"C"CC')`, century: 'to_char($t, \'"C"CC\')',
millennium: `to_char(date_part('millennium', $t), '"M"999')` millennium: 'to_char(date_part(\'millennium\', $t), \'"M"999\')'
}; };
function isoSqlExpr(params) { function isoSqlExpr (params) {
const column = timeExpression(params.time, params.timezone); const column = timeExpression(params.time, params.timezone);
if (params.count > 1) { if (params.count > 1) {
// TODO: it would be sensible to return the ISO of the first unit in the period // TODO: it would be sensible to return the ISO of the first unit in the period
@ -156,19 +156,19 @@ function isoSqlExpr(params) {
} }
const cyclicParts = { const cyclicParts = {
dayOfWeek: `date_part('isodow', $t)`, // 1 = monday to 7 = sunday; dayOfWeek: 'date_part(\'isodow\', $t)', // 1 = monday to 7 = sunday;
dayOfMonth: `date_part('day', $t)`, // 1 to 31 dayOfMonth: 'date_part(\'day\', $t)', // 1 to 31
dayOfYear: `date_part('doy', $t)`, // 1 to 366 dayOfYear: 'date_part(\'doy\', $t)', // 1 to 366
hourOfDay: `date_part('hour', $t)`, // 0 to 23 hourOfDay: 'date_part(\'hour\', $t)', // 0 to 23
monthOfYear: `date_part('month', $t)`, // 1 to 12 monthOfYear: 'date_part(\'month\', $t)', // 1 to 12
quarterOfYear: `date_part('quarter', $t)`, // 1 to 4 quarterOfYear: 'date_part(\'quarter\', $t)', // 1 to 4
semesterOfYear: `FLOOR((date_part('month', $t)-1)/6.0) + 1`, // 1 to 2 semesterOfYear: 'FLOOR((date_part(\'month\', $t)-1)/6.0) + 1', // 1 to 2
trimesterOfYear: `FLOOR((date_part('month', $t)-1)/4.0) + 1`, // 1 to 3 trimesterOfYear: 'FLOOR((date_part(\'month\', $t)-1)/4.0) + 1', // 1 to 3
weekOfYear: `date_part('week', $t)`, // 1 to 53 weekOfYear: 'date_part(\'week\', $t)', // 1 to 53
minuteOfHour: `date_part('minute', $t)` // 0 to 59 minuteOfHour: 'date_part(\'minute\', $t)' // 0 to 59
}; };
function cyclicSqlExpr(params) { function cyclicSqlExpr (params) {
const column = timeExpression(params.time, params.timezone); const column = timeExpression(params.time, params.timezone);
return cyclicParts[params.units].replace(/\$t/g, column); return cyclicParts[params.units].replace(/\$t/g, column);
} }
@ -176,7 +176,7 @@ function cyclicSqlExpr(params) {
const ACCEPTED_PARAMETERS = ['time', 'units', 'timezone', 'count', 'starting', 'format']; const ACCEPTED_PARAMETERS = ['time', 'units', 'timezone', 'count', 'starting', 'format'];
const REQUIRED_PARAMETERS = ['time', 'units']; const REQUIRED_PARAMETERS = ['time', 'units'];
function validateParameters(params, checker) { function validateParameters (params, checker) {
const errors = []; const errors = [];
const presentParams = Object.keys(params); const presentParams = Object.keys(params);
const invalidParams = presentParams.filter(param => !ACCEPTED_PARAMETERS.includes(param)); const invalidParams = presentParams.filter(param => !ACCEPTED_PARAMETERS.includes(param));
@ -190,7 +190,7 @@ function validateParameters(params, checker) {
const params_errors = checker(params); const params_errors = checker(params);
errors.push(...params_errors.errors); errors.push(...params_errors.errors);
if (errors.length) { if (errors.length) {
throw new Error(`Invalid time dimension:\n${errors.join("\n")}`); throw new Error(`Invalid time dimension:\n${errors.join('\n')}`);
} }
return params_errors.params; return params_errors.params;
} }
@ -199,7 +199,7 @@ const VALID_CYCLIC_UNITS = Object.keys(cyclicParts);
const VALID_SERIAL_UNITS = Object.keys(serialParts); const VALID_SERIAL_UNITS = Object.keys(serialParts);
const VALID_ISO_UNITS = Object.keys(isoParts); const VALID_ISO_UNITS = Object.keys(isoParts);
function cyclicCheckParams(params) { function cyclicCheckParams (params) {
const errors = []; const errors = [];
if (!VALID_CYCLIC_UNITS.includes(params.units)) { if (!VALID_CYCLIC_UNITS.includes(params.units)) {
errors.push(`Invalid units "${params.units}"`); errors.push(`Invalid units "${params.units}"`);
@ -210,7 +210,7 @@ function cyclicCheckParams(params) {
return { errors: errors, params: params }; return { errors: errors, params: params };
} }
function serialCheckParams(params) { function serialCheckParams (params) {
const errors = []; const errors = [];
if (!VALID_SERIAL_UNITS.includes(params.units)) { if (!VALID_SERIAL_UNITS.includes(params.units)) {
errors.push(`Invalid grouping units "${params.units}"`); errors.push(`Invalid grouping units "${params.units}"`);
@ -218,7 +218,7 @@ function serialCheckParams(params) {
return { errors: errors, params: Object.assign({}, params, { starting: epochWithDefaults(params.starting) }) }; return { errors: errors, params: Object.assign({}, params, { starting: epochWithDefaults(params.starting) }) };
} }
function isoCheckParams(params) { function isoCheckParams (params) {
const errors = []; const errors = [];
if (!VALID_ISO_UNITS.includes(params.units)) { if (!VALID_ISO_UNITS.includes(params.units)) {
errors.push(`Invalid units "${params.units}"`); errors.push(`Invalid units "${params.units}"`);
@ -244,11 +244,11 @@ const CLASSIFIERS = {
} }
}; };
function isCyclic(units) { function isCyclic (units) {
return VALID_CYCLIC_UNITS.includes(units); return VALID_CYCLIC_UNITS.includes(units);
} }
function classifierFor(params) { function classifierFor (params) {
let classifier = 'serial'; let classifier = 'serial';
if (params.units && isCyclic(params.units)) { if (params.units && isCyclic(params.units)) {
classifier = 'cyclic'; classifier = 'cyclic';
@ -258,7 +258,7 @@ function classifierFor(params) {
return CLASSIFIERS[classifier]; return CLASSIFIERS[classifier];
} }
function classificationSql(params) { function classificationSql (params) {
const classifier = classifierFor(params); const classifier = classifierFor(params);
params = validateParameters(params, classifier.checkParams); params = validateParameters(params, classifier.checkParams);
return { sql: classifier.sqlExpr(params), effectiveParams: params }; return { sql: classifier.sqlExpr(params), effectiveParams: params };

View File

@ -1,6 +1,6 @@
'use strict'; 'use strict';
function CdbRequest() { function CdbRequest () {
this.RE_USER_FROM_HOST = new RegExp(global.environment.user_from_host || this.RE_USER_FROM_HOST = new RegExp(global.environment.user_from_host ||
'^([^\\.]+)\\.' // would extract "strk" from "strk.cartodb.com" '^([^\\.]+)\\.' // would extract "strk" from "strk.cartodb.com"
); );
@ -8,18 +8,17 @@ function CdbRequest() {
module.exports = CdbRequest; module.exports = CdbRequest;
CdbRequest.prototype.userByReq = function (req) {
CdbRequest.prototype.userByReq = function(req) {
var host = req.headers.host || ''; var host = req.headers.host || '';
if (req.params.user) { if (req.params.user) {
return req.params.user; return req.params.user;
} }
var mat = host.match(this.RE_USER_FROM_HOST); var mat = host.match(this.RE_USER_FROM_HOST);
if ( ! mat ) { if (!mat) {
global.logger.error("Pattern '%s' does not match hostname '%s'", this.RE_USER_FROM_HOST, host); global.logger.error("Pattern '%s' does not match hostname '%s'", this.RE_USER_FROM_HOST, host);
return; return;
} }
if ( mat.length !== 2 ) { if (mat.length !== 2) {
global.logger.error("Pattern '%s' gave unexpected matches against '%s': %s", this.RE_USER_FROM_HOST, host, mat); global.logger.error("Pattern '%s' gave unexpected matches against '%s': %s", this.RE_USER_FROM_HOST, host, mat);
return; return;
} }

View File

@ -12,9 +12,9 @@ const filteredQueryTpl = ctx => `
AND AND
${ctx.aggregationColumn} != '-infinity'::float ${ctx.aggregationColumn} != '-infinity'::float
AND AND
${ctx.aggregationColumn} != 'NaN'::float` : ${ctx.aggregationColumn} != 'NaN'::float`
'' : ''
} }
`; `;
const summaryQueryTpl = ctx => ` const summaryQueryTpl = ctx => `
@ -30,9 +30,9 @@ const summaryQueryTpl = ctx => `
ELSE 0 ELSE 0
END END
) AS infinities_count, ) AS infinities_count,
sum(CASE WHEN ${ctx.aggregationColumn} = 'NaN'::float THEN 1 ELSE 0 END) AS nans_count` : sum(CASE WHEN ${ctx.aggregationColumn} = 'NaN'::float THEN 1 ELSE 0 END) AS nans_count`
'' : ''
} }
FROM (${ctx.query}) _cdb_aggregation_nulls FROM (${ctx.query}) _cdb_aggregation_nulls
) )
`; `;
@ -44,7 +44,7 @@ const rankedCategoriesQueryTpl = ctx => `
${ctx.aggregationFn} AS value, ${ctx.aggregationFn} AS value,
row_number() OVER (ORDER BY ${ctx.aggregationFn} desc) as rank row_number() OVER (ORDER BY ${ctx.aggregationFn} desc) as rank
FROM (${filteredQueryTpl(ctx)}) filtered_source FROM (${filteredQueryTpl(ctx)}) filtered_source
WHERE ${ctx.aggregation === "count" ? `${ctx.column}` : `${ctx.aggregationColumn}`} IS NOT NULL WHERE ${ctx.aggregation === 'count' ? `${ctx.column}` : `${ctx.aggregationColumn}`} IS NOT NULL
GROUP BY ${ctx.column} GROUP BY ${ctx.column}
ORDER BY 2 DESC ORDER BY 2 DESC
) )
@ -70,7 +70,7 @@ const categoriesSummaryCountQueryTpl = ctx => `
) )
`; `;
const specialNumericValuesColumns = () => `, nans_count, infinities_count`; const specialNumericValuesColumns = () => ', nans_count, infinities_count';
const rankedAggregationQueryTpl = ctx => ` const rankedAggregationQueryTpl = ctx => `
SELECT SELECT
@ -82,7 +82,7 @@ const rankedAggregationQueryTpl = ctx => `
max_val, max_val,
count, count,
categories_count categories_count
${ctx.isFloatColumn ? `${specialNumericValuesColumns(ctx)}` : '' } ${ctx.isFloatColumn ? `${specialNumericValuesColumns(ctx)}` : ''}
FROM categories, summary, categories_summary_min_max, categories_summary_count FROM categories, summary, categories_summary_min_max, categories_summary_count
WHERE rank < ${ctx.limit} WHERE rank < ${ctx.limit}
UNION ALL UNION ALL
@ -95,7 +95,7 @@ const rankedAggregationQueryTpl = ctx => `
max_val, max_val,
count, count,
categories_count categories_count
${ctx.isFloatColumn ? `${specialNumericValuesColumns(ctx)}` : '' } ${ctx.isFloatColumn ? `${specialNumericValuesColumns(ctx)}` : ''}
FROM categories, summary, categories_summary_min_max, categories_summary_count FROM categories, summary, categories_summary_min_max, categories_summary_count
WHERE rank >= ${ctx.limit} WHERE rank >= ${ctx.limit}
GROUP BY GROUP BY
@ -104,7 +104,7 @@ const rankedAggregationQueryTpl = ctx => `
max_val, max_val,
count, count,
categories_count categories_count
${ctx.isFloatColumn ? `${specialNumericValuesColumns(ctx)}` : '' } ${ctx.isFloatColumn ? `${specialNumericValuesColumns(ctx)}` : ''}
`; `;
const aggregationQueryTpl = ctx => ` const aggregationQueryTpl = ctx => `
@ -117,7 +117,7 @@ const aggregationQueryTpl = ctx => `
max_val, max_val,
count, count,
categories_count categories_count
${ctx.isFloatColumn ? `${specialNumericValuesColumns(ctx)}` : '' } ${ctx.isFloatColumn ? `${specialNumericValuesColumns(ctx)}` : ''}
FROM (${ctx.query}) _cdb_aggregation_all, summary, categories_summary_min_max, categories_summary_count FROM (${ctx.query}) _cdb_aggregation_all, summary, categories_summary_min_max, categories_summary_count
GROUP BY GROUP BY
${ctx.column}, ${ctx.column},
@ -126,7 +126,7 @@ const aggregationQueryTpl = ctx => `
max_val, max_val,
count, count,
categories_count categories_count
${ctx.isFloatColumn ? `${specialNumericValuesColumns(ctx)}` : '' } ${ctx.isFloatColumn ? `${specialNumericValuesColumns(ctx)}` : ''}
ORDER BY value DESC ORDER BY value DESC
`; `;
@ -138,7 +138,7 @@ const aggregationDataviewQueryTpl = ctx => `
${rankedCategoriesQueryTpl(ctx)}, ${rankedCategoriesQueryTpl(ctx)},
${categoriesSummaryMinMaxQueryTpl(ctx)}, ${categoriesSummaryMinMaxQueryTpl(ctx)},
${categoriesSummaryCountQueryTpl(ctx)} ${categoriesSummaryCountQueryTpl(ctx)}
${!!ctx.override.ownFilter ? `${aggregationQueryTpl(ctx)}` : `${rankedAggregationQueryTpl(ctx)}`} ${ctx.override.ownFilter ? `${aggregationQueryTpl(ctx)}` : `${rankedAggregationQueryTpl(ctx)}`}
`; `;
const filterCategoriesQueryTpl = ctx => ` const filterCategoriesQueryTpl = ctx => `
@ -206,11 +206,11 @@ module.exports = class Aggregation extends BaseDataview {
_checkOptions (options) { _checkOptions (options) {
if (typeof options.column !== 'string') { if (typeof options.column !== 'string') {
throw new Error(`Aggregation expects 'column' in dataview options`); throw new Error('Aggregation expects \'column\' in dataview options');
} }
if (typeof options.aggregation !== 'string') { if (typeof options.aggregation !== 'string') {
throw new Error(`Aggregation expects 'aggregation' operation in dataview options`); throw new Error('Aggregation expects \'aggregation\' operation in dataview options');
} }
if (!VALID_OPERATIONS[options.aggregation]) { if (!VALID_OPERATIONS[options.aggregation]) {
@ -244,9 +244,9 @@ module.exports = class Aggregation extends BaseDataview {
return null; return null;
} }
const limit = Number.isFinite(override.categories) && override.categories > 0 ? const limit = Number.isFinite(override.categories) && override.categories > 0
override.categories : ? override.categories
CATEGORIES_LIMIT; : CATEGORIES_LIMIT;
const aggregationSql = aggregationDataviewQueryTpl({ const aggregationSql = aggregationDataviewQueryTpl({
override: override, override: override,
@ -280,7 +280,7 @@ module.exports = class Aggregation extends BaseDataview {
min_val = 0, min_val = 0,
max_val = 0, max_val = 0,
categories_count = 0 categories_count = 0
} = result.rows[0] || {}; } = result.rows[0] || {};
return { return {
aggregation: this.aggregation, aggregation: this.aggregation,
@ -303,9 +303,9 @@ module.exports = class Aggregation extends BaseDataview {
search (psql, userQuery, callback) { search (psql, userQuery, callback) {
const escapedUserQuery = psql.escapeLiteral(`%${userQuery}%`); const escapedUserQuery = psql.escapeLiteral(`%${userQuery}%`);
const value = this.aggregation !== 'count' && this.aggregationColumn ? const value = this.aggregation !== 'count' && this.aggregationColumn
`${this.aggregation}(${this.aggregationColumn})` : ? `${this.aggregation}(${this.aggregationColumn})`
'count(1)'; : 'count(1)';
// TODO unfiltered will be wrong as filters are already applied at this point // TODO unfiltered will be wrong as filters are already applied at this point
const query = searchQueryTpl({ const query = searchQueryTpl({
@ -330,7 +330,7 @@ module.exports = class Aggregation extends BaseDataview {
return callback(err, result); return callback(err, result);
} }
return callback(null, {type: this.getType(), categories: result.rows }); return callback(null, { type: this.getType(), categories: result.rows });
}, true); // use read-only transaction }, true); // use read-only transaction
} }

View File

@ -36,7 +36,7 @@ module.exports = class BaseDataview {
result = this.format(result, override); result = this.format(result, override);
result.type = this.getType(); result.type = this.getType();
//Overviews logging // Overviews logging
const stats = {}; const stats = {};
if (flags && flags.usesOverviews !== undefined) { if (flags && flags.usesOverviews !== undefined) {

View File

@ -3,7 +3,7 @@
const dataviews = require('.'); const dataviews = require('.');
module.exports = class DataviewFactory { module.exports = class DataviewFactory {
static get dataviews() { static get dataviews () {
return Object.keys(dataviews).reduce((allDataviews, dataviewClassName) => { return Object.keys(dataviews).reduce((allDataviews, dataviewClassName) => {
allDataviews[dataviewClassName.toLowerCase()] = dataviews[dataviewClassName]; allDataviews[dataviewClassName.toLowerCase()] = dataviews[dataviewClassName];
return allDataviews; return allDataviews;

View File

@ -5,11 +5,11 @@ const debug = require('debug')('windshaft:dataview:formula');
const utils = require('../../utils/query-utils'); const utils = require('../../utils/query-utils');
const formulaQueryTpl = ctx => const formulaQueryTpl = ctx =>
`SELECT `SELECT
${ctx.operation}(${utils.handleFloatColumn(ctx)}) AS result, ${ctx.operation}(${utils.handleFloatColumn(ctx)}) AS result,
${utils.countNULLs(ctx)} AS nulls_count ${utils.countNULLs(ctx)} AS nulls_count
${ctx.isFloatColumn ? `,${utils.countInfinites(ctx)} AS infinities_count,` : ``} ${ctx.isFloatColumn ? `,${utils.countInfinites(ctx)} AS infinities_count,` : ''}
${ctx.isFloatColumn ? `${utils.countNaNs(ctx)} AS nans_count` : ``} ${ctx.isFloatColumn ? `${utils.countNaNs(ctx)} AS nans_count` : ''}
FROM (${ctx.query}) __cdb_formula`; FROM (${ctx.query}) __cdb_formula`;
const VALID_OPERATIONS = { const VALID_OPERATIONS = {
@ -46,7 +46,7 @@ module.exports = class Formula extends BaseDataview {
_checkOptions (options) { _checkOptions (options) {
if (typeof options.operation !== 'string') { if (typeof options.operation !== 'string') {
throw new Error(`Formula expects 'operation' in dataview options`); throw new Error('Formula expects \'operation\' in dataview options');
} }
if (!VALID_OPERATIONS[options.operation]) { if (!VALID_OPERATIONS[options.operation]) {
@ -54,11 +54,10 @@ module.exports = class Formula extends BaseDataview {
} }
if (options.operation !== 'count' && typeof options.column !== 'string') { if (options.operation !== 'count' && typeof options.column !== 'string') {
throw new Error(`Formula expects 'column' in dataview options`); throw new Error('Formula expects \'column\' in dataview options');
} }
} }
sql (psql, override, callback) { sql (psql, override, callback) {
if (!callback) { if (!callback) {
callback = override; callback = override;

View File

@ -20,23 +20,23 @@ module.exports = class Histogram {
let implementation = null; let implementation = null;
switch (this._getHistogramSubtype(override)) { switch (this._getHistogramSubtype(override)) {
case DATE_HISTOGRAM: case DATE_HISTOGRAM:
debug('Delegating to DateHistogram with options: %j and overriding: %j', this.options, override); debug('Delegating to DateHistogram with options: %j and overriding: %j', this.options, override);
implementation = new DateHistogram(this.query, this.options, this.queries); implementation = new DateHistogram(this.query, this.options, this.queries);
break; break;
case NUMERIC_HISTOGRAM: case NUMERIC_HISTOGRAM:
debug('Delegating to NumericHistogram with options: %j and overriding: %j', this.options, override); debug('Delegating to NumericHistogram with options: %j and overriding: %j', this.options, override);
implementation = new NumericHistogram(this.query, this.options, this.queries); implementation = new NumericHistogram(this.query, this.options, this.queries);
break; break;
default: default:
throw new Error('Unsupported Histogram type'); throw new Error('Unsupported Histogram type');
} }
return implementation; return implementation;
} }
_getHistogramSubtype (override) { _getHistogramSubtype (override) {
if(this._isDateHistogram(override)) { if (this._isDateHistogram(override)) {
return DATE_HISTOGRAM; return DATE_HISTOGRAM;
} }

View File

@ -4,7 +4,6 @@ const BaseHistogram = require('./base-histogram');
const debug = require('debug')('windshaft:dataview:date-histogram'); const debug = require('debug')('windshaft:dataview:date-histogram');
const utils = require('../../../utils/query-utils'); const utils = require('../../../utils/query-utils');
/** /**
* Gets the name of a timezone with the same offset as the required * Gets the name of a timezone with the same offset as the required
* using the pg_timezone_names table. We do this because it's simpler to pass * using the pg_timezone_names table. We do this because it's simpler to pass
@ -32,7 +31,7 @@ WITH __wd_tz AS
* the aggregation. Since the data stored is in epoch we need to adapt it to * the aggregation. Since the data stored is in epoch we need to adapt it to
* our timezone so when calling date_trunc it falls into the correct bin * our timezone so when calling date_trunc it falls into the correct bin
*/ */
function dataBucketsQuery(ctx) { function dataBucketsQuery (ctx) {
var condition_str = ''; var condition_str = '';
if (ctx.start !== 0) { if (ctx.start !== 0) {
@ -41,8 +40,7 @@ function dataBucketsQuery(ctx) {
if (ctx.end !== 0) { if (ctx.end !== 0) {
if (condition_str === '') { if (condition_str === '') {
condition_str = `WHERE ${ctx.column} <= to_timestamp(${ctx.end})`; condition_str = `WHERE ${ctx.column} <= to_timestamp(${ctx.end})`;
} } else {
else {
condition_str += ` and ${ctx.column} <= to_timestamp(${ctx.end})`; condition_str += ` and ${ctx.column} <= to_timestamp(${ctx.end})`;
} }
} }
@ -68,21 +66,21 @@ __wd_buckets AS
* start and end date. If not provided we use the min and max generated from * start and end date. If not provided we use the min and max generated from
* the dataBucketsQuery * the dataBucketsQuery
*/ */
function allBucketsArrayQuery(ctx) { function allBucketsArrayQuery (ctx) {
var extra_from = ``; var extra_from = '';
var series_start = ``; var series_start = '';
var series_end = ``; var series_end = '';
if (ctx.start === 0) { if (ctx.start === 0) {
extra_from = `, __wd_buckets GROUP BY __wd_tz.name`; extra_from = ', __wd_buckets GROUP BY __wd_tz.name';
series_start = `min(__wd_buckets.timestamp)`; series_start = 'min(__wd_buckets.timestamp)';
} else { } else {
series_start = `date_trunc('${ctx.aggregation}', timezone(__wd_tz.name, to_timestamp(${ctx.start})))`; series_start = `date_trunc('${ctx.aggregation}', timezone(__wd_tz.name, to_timestamp(${ctx.start})))`;
} }
if (ctx.end === 0) { if (ctx.end === 0) {
extra_from = `, __wd_buckets GROUP BY __wd_tz.name`; extra_from = ', __wd_buckets GROUP BY __wd_tz.name';
series_end = `max(__wd_buckets.timestamp)`; series_end = 'max(__wd_buckets.timestamp)';
} else { } else {
series_end = `date_trunc('${ctx.aggregation}', timezone(__wd_tz.name, to_timestamp(${ctx.end})))`; series_end = `date_trunc('${ctx.aggregation}', timezone(__wd_tz.name, to_timestamp(${ctx.end})))`;
} }
@ -148,18 +146,18 @@ const dateIntervalQueryTpl = ctx => `
const MAX_INTERVAL_VALUE = 100; const MAX_INTERVAL_VALUE = 100;
const DATE_AGGREGATIONS = { const DATE_AGGREGATIONS = {
'auto': true, auto: true,
'second' : true, second: true,
'minute': true, minute: true,
'hour': true, hour: true,
'day': true, day: true,
'week': true, week: true,
'month': true, month: true,
'quarter': true, quarter: true,
'year': true, year: true,
'decade' : true, decade: true,
'century' : true, century: true,
'millennium' : true millennium: true
}; };
/** /**
@ -219,8 +217,8 @@ ORDER BY bin ASC;
return null; return null;
} }
var interval = this._getAggregation(override) === 'quarter' ? var interval = this._getAggregation(override) === 'quarter'
'3 months' : '1 ' + this._getAggregation(override); ? '3 months' : '1 ' + this._getAggregation(override);
const histogramSql = this._buildQueryTpl({ const histogramSql = this._buildQueryTpl({
override: override, override: override,
@ -294,7 +292,7 @@ ORDER BY bin ASC;
} }
_getBuckets (result) { _getBuckets (result) {
result.rows.forEach(function(row) { result.rows.forEach(function (row) {
row.min = row.max = row.avg = row.timestamp; row.min = row.max = row.avg = row.timestamp;
}); });

View File

@ -26,7 +26,7 @@ const irqQueryTpl = ctx => `
max(${ctx.column}) AS __cdb_max_val, max(${ctx.column}) AS __cdb_max_val,
min(${ctx.column}) AS __cdb_min_val, min(${ctx.column}) AS __cdb_min_val,
count(1) AS __cdb_total_rows, count(1) AS __cdb_total_rows,
${ctx.irq ? ctx.irq : `0`} AS __cdb_iqr ${ctx.irq ? ctx.irq : '0'} AS __cdb_iqr
FROM FROM
( (
SELECT * SELECT *
@ -72,8 +72,7 @@ module.exports = class NumericHistogram extends BaseHistogram {
return callback(null, histogramSql); return callback(null, histogramSql);
} }
/**
/**
* ctx: Object with the following values * ctx: Object with the following values
* ctx.column -- Column for the histogram * ctx.column -- Column for the histogram
* ctx.isFloatColumn - Whether the column is float or not * ctx.isFloatColumn - Whether the column is float or not
@ -85,10 +84,10 @@ module.exports = class NumericHistogram extends BaseHistogram {
* ctx.maxBins - If !full max bins to calculate [Optional] * ctx.maxBins - If !full max bins to calculate [Optional]
*/ */
_buildQueryTpl (ctx) { _buildQueryTpl (ctx) {
var extra_tables = ``; var extra_tables = '';
var extra_queries = ``; var extra_queries = '';
var extra_groupby = ``; var extra_groupby = '';
var extra_filter = ``; var extra_filter = '';
if (ctx.start < ctx.end) { if (ctx.start < ctx.end) {
extra_filter = ` extra_filter = `
@ -96,19 +95,19 @@ module.exports = class NumericHistogram extends BaseHistogram {
AND __ctx_query.${ctx.column} <= ${ctx.end} AND __ctx_query.${ctx.column} <= ${ctx.end}
`; `;
} else { } else {
ctx.end = `__cdb_basics.__cdb_max_val`; ctx.end = '__cdb_basics.__cdb_max_val';
ctx.start = `__cdb_basics.__cdb_min_val`; ctx.start = '__cdb_basics.__cdb_min_val';
extra_groupby = `, __cdb_basics.__cdb_max_val, __cdb_basics.__cdb_min_val`; extra_groupby = ', __cdb_basics.__cdb_max_val, __cdb_basics.__cdb_min_val';
extra_tables = `, __cdb_basics`; extra_tables = ', __cdb_basics';
extra_queries = `WITH ${irqQueryTpl(ctx)}`; extra_queries = `WITH ${irqQueryTpl(ctx)}`;
} }
if (ctx.bins <= 0) { if (ctx.bins <= 0) {
ctx.bins = `__cdb_basics.__cdb_bins_number`; ctx.bins = '__cdb_basics.__cdb_bins_number';
ctx.irq = `percentile_disc(0.75) within group (order by ${ctx.column}) ctx.irq = `percentile_disc(0.75) within group (order by ${ctx.column})
- percentile_disc(0.25) within group (order by ${ctx.column})`; - percentile_disc(0.25) within group (order by ${ctx.column})`;
extra_groupby += `, __cdb_basics.__cdb_bins_number`; extra_groupby += ', __cdb_basics.__cdb_bins_number';
extra_tables = `, __cdb_basics`; extra_tables = ', __cdb_basics';
extra_queries = `WITH ${irqQueryTpl(ctx)}`; extra_queries = `WITH ${irqQueryTpl(ctx)}`;
} }
@ -151,7 +150,7 @@ ORDER BY 10;`;
var total_avg = 0; var total_avg = 0;
var total_count = 0; var total_count = 0;
result.rows.forEach(function(row) { result.rows.forEach(function (row) {
total_nulls += row.nulls_count; total_nulls += row.nulls_count;
total_infinities += row.infinities_count; total_infinities += row.infinities_count;
total_nans += row.nans_count; total_nans += row.nans_count;
@ -188,5 +187,4 @@ ORDER BY 10;`;
return binStart; return binStart;
} }
}; };

View File

@ -91,7 +91,7 @@ var aggregationQueryTpl = dot.template([
var CATEGORIES_LIMIT = 6; var CATEGORIES_LIMIT = 6;
function Aggregation(query, options, queryRewriter, queryRewriteData, params, queries) { function Aggregation (query, options, queryRewriter, queryRewriteData, params, queries) {
BaseOverviewsDataview.call(this, query, options, BaseDataview, queryRewriter, queryRewriteData, params, queries); BaseOverviewsDataview.call(this, query, options, BaseDataview, queryRewriter, queryRewriteData, params, queries);
this.query = query; this.query = query;
@ -107,7 +107,7 @@ Aggregation.prototype.constructor = Aggregation;
module.exports = Aggregation; module.exports = Aggregation;
Aggregation.prototype.sql = function(psql, override, callback) { Aggregation.prototype.sql = function (psql, override, callback) {
var self = this; var self = this;
if (!callback) { if (!callback) {
@ -130,9 +130,9 @@ Aggregation.prototype.sql = function(psql, override, callback) {
} }
var aggregationSql; var aggregationSql;
if (!!override.ownFilter) { if (override.ownFilter) {
aggregationSql = [ aggregationSql = [
"WITH", 'WITH',
[ [
filteredQueryTpl({ filteredQueryTpl({
_isFloatColumn: this._isFloatColumn, _isFloatColumn: this._isFloatColumn,
@ -171,7 +171,7 @@ Aggregation.prototype.sql = function(psql, override, callback) {
].join('\n'); ].join('\n');
} else { } else {
aggregationSql = [ aggregationSql = [
"WITH", 'WITH',
[ [
filteredQueryTpl({ filteredQueryTpl({
_isFloatColumn: this._isFloatColumn, _isFloatColumn: this._isFloatColumn,
@ -216,10 +216,10 @@ Aggregation.prototype.sql = function(psql, override, callback) {
var aggregationFnQueryTpl = { var aggregationFnQueryTpl = {
count: dot.template('sum(_feature_count)'), count: dot.template('sum(_feature_count)'),
sum: dot.template('sum({{=it._aggregationColumn}}*_feature_count)') sum: dot.template('sum({{=it._aggregationColumn}}*_feature_count)')
}; };
Aggregation.prototype.getAggregationSql = function() { Aggregation.prototype.getAggregationSql = function () {
return aggregationFnQueryTpl[this.aggregation]({ return aggregationFnQueryTpl[this.aggregation]({
_aggregationFn: this.aggregation, _aggregationFn: this.aggregation,
_aggregationColumn: this.aggregationColumn || 1 _aggregationColumn: this.aggregationColumn || 1

View File

@ -3,15 +3,15 @@
var _ = require('underscore'); var _ = require('underscore');
var BaseDataview = require('../base'); var BaseDataview = require('../base');
function BaseOverviewsDataview(query, queryOptions, BaseDataview, queryRewriter, queryRewriteData, options, queries) { function BaseOverviewsDataview (query, queryOptions, BaseDataview, queryRewriter, queryRewriteData, options, queries) {
this.BaseDataview = BaseDataview; this.BaseDataview = BaseDataview;
this.query = query; this.query = query;
this.queryOptions = queryOptions; this.queryOptions = queryOptions;
this.queryRewriter = queryRewriter; this.queryRewriter = queryRewriter;
this.queryRewriteData = queryRewriteData; this.queryRewriteData = queryRewriteData;
this.options = options; this.options = options;
this.queries = queries; this.queries = queries;
this.baseDataview = new this.BaseDataview(this.query, this.queryOptions, this.queries); this.baseDataview = new this.BaseDataview(this.query, this.queryOptions, this.queries);
} }
module.exports = BaseOverviewsDataview; module.exports = BaseOverviewsDataview;
@ -32,36 +32,36 @@ var SETTINGS = {
// Compute zoom level so that the the resolution grid size of the // Compute zoom level so that the the resolution grid size of the
// selected overview is smaller (zoomLevelFactor times smaller at least) // selected overview is smaller (zoomLevelFactor times smaller at least)
// than the bounding box size. // than the bounding box size.
BaseOverviewsDataview.prototype.zoomLevelForBbox = function(bbox) { BaseOverviewsDataview.prototype.zoomLevelForBbox = function (bbox) {
var pxPerTile = 256.0; var pxPerTile = 256.0;
var earthWidth = 360.0; var earthWidth = 360.0;
// TODO: now we assume overviews are computed for 1-pixel tolerance; // TODO: now we assume overviews are computed for 1-pixel tolerance;
// should use extended overviews metadata to compute this properly. // should use extended overviews metadata to compute this properly.
if ( bbox ) { if (bbox) {
var bboxValues = _.map(bbox.split(','), function(v) { return +v; }); var bboxValues = _.map(bbox.split(','), function (v) { return +v; });
var w = Math.abs(bboxValues[2]-bboxValues[0]); var w = Math.abs(bboxValues[2] - bboxValues[0]);
var h = Math.abs(bboxValues[3]-bboxValues[1]); var h = Math.abs(bboxValues[3] - bboxValues[1]);
var maxDim = Math.min(w, h); var maxDim = Math.min(w, h);
// Find minimum suitable z // Find minimum suitable z
// note that the QueryRewirter will use the minimum level overview // note that the QueryRewirter will use the minimum level overview
// of level >= z if it exists, and otherwise the base table // of level >= z if it exists, and otherwise the base table
var z = Math.ceil(-Math.log(maxDim*pxPerTile/earthWidth/SETTINGS.zoomLevelFactor)/Math.log(2.0)); var z = Math.ceil(-Math.log(maxDim * pxPerTile / earthWidth / SETTINGS.zoomLevelFactor) / Math.log(2.0));
return Math.max(z, 0); return Math.max(z, 0);
} }
return 0; return 0;
}; };
BaseOverviewsDataview.prototype.rewrittenQuery = function(query) { BaseOverviewsDataview.prototype.rewrittenQuery = function (query) {
var zoom_level = this.zoomLevelForBbox(this.options.bbox); var zoom_level = this.zoomLevelForBbox(this.options.bbox);
return this.queryRewriter.query(query, this.queryRewriteData, { zoom_level: zoom_level }); return this.queryRewriter.query(query, this.queryRewriteData, { zoom_level: zoom_level });
}; };
// Default behaviour // Default behaviour
BaseOverviewsDataview.prototype.defaultSql = function(psql, override, callback) { BaseOverviewsDataview.prototype.defaultSql = function (psql, override, callback) {
var query = this.query; var query = this.query;
var dataview = this.baseDataview; var dataview = this.baseDataview;
if ( SETTINGS.defaultOverviews ) { if (SETTINGS.defaultOverviews) {
query = this.rewrittenQuery(query); query = this.rewrittenQuery(query);
dataview = new this.BaseDataview(query, this.queryOptions); dataview = new this.BaseDataview(query, this.queryOptions);
} }
@ -70,22 +70,22 @@ BaseOverviewsDataview.prototype.defaultSql = function(psql, override, callback)
// default implementation that can be override in derived classes: // default implementation that can be override in derived classes:
BaseOverviewsDataview.prototype.sql = function(psql, override, callback) { BaseOverviewsDataview.prototype.sql = function (psql, override, callback) {
return this.defaultSql(psql, override, callback); return this.defaultSql(psql, override, callback);
}; };
BaseOverviewsDataview.prototype.search = function(psql, userQuery, callback) { BaseOverviewsDataview.prototype.search = function (psql, userQuery, callback) {
return this.baseDataview.search(psql, userQuery, callback); return this.baseDataview.search(psql, userQuery, callback);
}; };
BaseOverviewsDataview.prototype.format = function(result) { BaseOverviewsDataview.prototype.format = function (result) {
return this.baseDataview.format(result); return this.baseDataview.format(result);
}; };
BaseOverviewsDataview.prototype.getType = function() { BaseOverviewsDataview.prototype.getType = function () {
return this.baseDataview.getType(); return this.baseDataview.getType();
}; };
BaseOverviewsDataview.prototype.toString = function() { BaseOverviewsDataview.prototype.toString = function () {
return this.baseDataview.toString(); return this.baseDataview.toString();
}; };

View File

@ -3,16 +3,16 @@
var parentFactory = require('../factory'); var parentFactory = require('../factory');
var dataviews = require('.'); var dataviews = require('.');
function OverviewsDataviewFactory(queryRewriter, queryRewriteData, options) { function OverviewsDataviewFactory (queryRewriter, queryRewriteData, options) {
this.queryRewriter = queryRewriter; this.queryRewriter = queryRewriter;
this.queryRewriteData = queryRewriteData; this.queryRewriteData = queryRewriteData;
this.options = options; this.options = options;
} }
OverviewsDataviewFactory.prototype.getDataview = function(query, dataviewDefinition) { OverviewsDataviewFactory.prototype.getDataview = function (query, dataviewDefinition) {
var type = dataviewDefinition.type; var type = dataviewDefinition.type;
var dataviews = OverviewsDataviewMetaFactory.dataviews; var dataviews = OverviewsDataviewMetaFactory.dataviews;
if ( !this.queryRewriter || !this.queryRewriteData || !dataviews[type] ) { if (!this.queryRewriter || !this.queryRewriteData || !dataviews[type]) {
return parentFactory.getDataview(query, dataviewDefinition); return parentFactory.getDataview(query, dataviewDefinition);
} }
return new dataviews[type]( return new dataviews[type](
@ -22,14 +22,14 @@ OverviewsDataviewFactory.prototype.getDataview = function(query, dataviewDefinit
}; };
var OverviewsDataviewMetaFactory = { var OverviewsDataviewMetaFactory = {
dataviews: Object.keys(dataviews).reduce(function(allDataviews, dataviewClassName) { dataviews: Object.keys(dataviews).reduce(function (allDataviews, dataviewClassName) {
allDataviews[dataviewClassName.toLowerCase()] = dataviews[dataviewClassName]; allDataviews[dataviewClassName.toLowerCase()] = dataviews[dataviewClassName];
return allDataviews; return allDataviews;
}, {}), }, {}),
getFactory: function(queryRewriter, queryRewriteData, options) { getFactory: function (queryRewriter, queryRewriteData, options) {
return new OverviewsDataviewFactory(queryRewriter, queryRewriteData, options); return new OverviewsDataviewFactory(queryRewriter, queryRewriteData, options);
}, }
}; };
module.exports = OverviewsDataviewMetaFactory; module.exports = OverviewsDataviewMetaFactory;

View File

@ -14,28 +14,28 @@ const VALID_OPERATIONS = {
avg: true avg: true
}; };
/** Formulae to calculate the end result using _feature_count from the overview table*/ /** Formulae to calculate the end result using _feature_count from the overview table */
function dataviewResult(ctx) { function dataviewResult (ctx) {
switch (ctx.operation) { switch (ctx.operation) {
case 'count': case 'count':
return `sum(_feature_count)`; return 'sum(_feature_count)';
case 'sum': case 'sum':
return `sum(${utils.handleFloatColumn(ctx)}*_feature_count)`; return `sum(${utils.handleFloatColumn(ctx)}*_feature_count)`;
case 'avg': case 'avg':
return `sum(${utils.handleFloatColumn(ctx)}*_feature_count)/sum(_feature_count) `; return `sum(${utils.handleFloatColumn(ctx)}*_feature_count)/sum(_feature_count) `;
} }
return `${ctx.operation}(${utils.handleFloatColumn(ctx)})`; return `${ctx.operation}(${utils.handleFloatColumn(ctx)})`;
} }
const formulaQueryTpl = ctx => const formulaQueryTpl = ctx =>
`SELECT `SELECT
${dataviewResult(ctx)} AS result, ${dataviewResult(ctx)} AS result,
${utils.countNULLs(ctx)} AS nulls_count ${utils.countNULLs(ctx)} AS nulls_count
${ctx.isFloatColumn ? `,${utils.countInfinites(ctx)} AS infinities_count,` : ``} ${ctx.isFloatColumn ? `,${utils.countInfinites(ctx)} AS infinities_count,` : ''}
${ctx.isFloatColumn ? `${utils.countNaNs(ctx)} AS nans_count` : ``} ${ctx.isFloatColumn ? `${utils.countNaNs(ctx)} AS nans_count` : ''}
FROM (${ctx.query}) __cdb_formula`; FROM (${ctx.query}) __cdb_formula`;
function Formula(query, options, queryRewriter, queryRewriteData, params, queries) { function Formula (query, options, queryRewriter, queryRewriteData, params, queries) {
BaseOverviewsDataview.call(this, query, options, BaseDataview, queryRewriter, queryRewriteData, params, queries); BaseOverviewsDataview.call(this, query, options, BaseDataview, queryRewriter, queryRewriteData, params, queries);
this.column = options.column || '1'; this.column = options.column || '1';
this.operation = options.operation; this.operation = options.operation;

View File

@ -133,7 +133,7 @@ var histogramQueryTpl = dot.template([
'ORDER BY bin' 'ORDER BY bin'
].join('\n')); ].join('\n'));
function Histogram(query, options, queryRewriter, queryRewriteData, params, queries) { function Histogram (query, options, queryRewriter, queryRewriteData, params, queries) {
BaseOverviewsDataview.call(this, query, options, BaseDataview, queryRewriter, queryRewriteData, params, queries); BaseOverviewsDataview.call(this, query, options, BaseDataview, queryRewriter, queryRewriteData, params, queries);
this.query = query; this.query = query;
@ -149,7 +149,7 @@ Histogram.prototype.constructor = Histogram;
module.exports = Histogram; module.exports = Histogram;
Histogram.prototype.sql = function(psql, override, callback) { Histogram.prototype.sql = function (psql, override, callback) {
var self = this; var self = this;
if (!callback) { if (!callback) {
@ -157,7 +157,6 @@ Histogram.prototype.sql = function(psql, override, callback) {
override = {}; override = {};
} }
if (this._columnType === null) { if (this._columnType === null) {
this.getColumnType(psql, this.column, this.queries.no_filters, function (err, type) { this.getColumnType(psql, this.column, this.queries.no_filters, function (err, type) {
// assume numeric, will fail later // assume numeric, will fail later
@ -259,7 +258,7 @@ Histogram.prototype._buildQuery = function (override) {
} }
var histogramSql = [ var histogramSql = [
"WITH", 'WITH',
cteSql.join(',\n'), cteSql.join(',\n'),
histogramQueryTpl({ histogramQueryTpl({
_isFloatColumn: this._columnType === 'float', _isFloatColumn: this._columnType === 'float',

View File

@ -1,7 +1,7 @@
'use strict'; 'use strict';
module.exports = { module.exports = {
Aggregation: require('./aggregation'), Aggregation: require('./aggregation'),
Formula: require('./formula'), Formula: require('./formula'),
Histogram: require('./histogram') Histogram: require('./histogram')
}; };

View File

@ -5,7 +5,7 @@ var filters = {
range: require('./analysis/range') range: require('./analysis/range')
}; };
function createFilter(filterDefinition) { function createFilter (filterDefinition) {
var filterType = filterDefinition.type.toLowerCase(); var filterType = filterDefinition.type.toLowerCase();
if (!filters.hasOwnProperty(filterType)) { if (!filters.hasOwnProperty(filterType)) {
throw new Error('Unknown filter type: ' + filterType); throw new Error('Unknown filter type: ' + filterType);
@ -13,25 +13,25 @@ function createFilter(filterDefinition) {
return new filters[filterType](filterDefinition.column, filterDefinition.params); return new filters[filterType](filterDefinition.column, filterDefinition.params);
} }
function AnalysisFilters(filters) { function AnalysisFilters (filters) {
this.filters = filters; this.filters = filters;
} }
AnalysisFilters.prototype.sql = function(rawSql) { AnalysisFilters.prototype.sql = function (rawSql) {
var filters = this.filters || {}; var filters = this.filters || {};
var applyFilters = {}; var applyFilters = {};
return Object.keys(filters) return Object.keys(filters)
.filter(function(filterName) { .filter(function (filterName) {
return applyFilters.hasOwnProperty(filterName) ? applyFilters[filterName] : true; return applyFilters.hasOwnProperty(filterName) ? applyFilters[filterName] : true;
}) })
.map(function(filterName) { .map(function (filterName) {
var filterDefinition = filters[filterName]; var filterDefinition = filters[filterName];
return createFilter(filterDefinition); return createFilter(filterDefinition);
}) })
.reduce(function(sql, filter) { .reduce(function (sql, filter) {
return filter.sql(sql); return filter.sql(sql);
}, rawSql); }, rawSql);
}; };
module.exports = AnalysisFilters; module.exports = AnalysisFilters;

View File

@ -13,7 +13,7 @@ var escapeStringTpl = dot.template('$escape_{{=it._i}}${{=it._value}}$escape_{{=
var inConditionTpl = dot.template('{{=it._column}} IN ({{=it._values}})'); var inConditionTpl = dot.template('{{=it._column}} IN ({{=it._values}})');
var notInConditionTpl = dot.template('{{=it._column}} NOT IN ({{=it._values}})'); var notInConditionTpl = dot.template('{{=it._column}} NOT IN ({{=it._values}})');
function Category(column, filterParams) { function Category (column, filterParams) {
this.column = column; this.column = column;
if (!Array.isArray(filterParams.accept) && !Array.isArray(filterParams.reject)) { if (!Array.isArray(filterParams.accept) && !Array.isArray(filterParams.reject)) {
@ -38,15 +38,15 @@ module.exports = Category;
- accept: [] => reject all - accept: [] => reject all
- reject: [] => accept all - reject: [] => accept all
*/ */
Category.prototype.sql = function(rawSql) { Category.prototype.sql = function (rawSql) {
var valueFilters = []; var valueFilters = [];
if (Array.isArray(this.accept)) { if (Array.isArray(this.accept)) {
if (this.accept.length > 0) { if (this.accept.length > 0) {
valueFilters.push(inConditionTpl({ valueFilters.push(inConditionTpl({
_column: this.column, _column: this.column,
_values: this.accept.map(function(value, i) { _values: this.accept.map(function (value, i) {
return Number.isFinite(value) ? value : escapeStringTpl({_i: i, _value: value}); return Number.isFinite(value) ? value : escapeStringTpl({ _i: i, _value: value });
}).join(',') }).join(',')
})); }));
} else { } else {
@ -59,7 +59,7 @@ Category.prototype.sql = function(rawSql) {
valueFilters.push(notInConditionTpl({ valueFilters.push(notInConditionTpl({
_column: this.column, _column: this.column,
_values: this.reject.map(function (value, i) { _values: this.reject.map(function (value, i) {
return Number.isFinite(value) ? value : escapeStringTpl({_i: i, _value: value}); return Number.isFinite(value) ? value : escapeStringTpl({ _i: i, _value: value });
}).join(',') }).join(',')
})); }));
} else { } else {

View File

@ -8,7 +8,7 @@ var minFilterTpl = dot.template('{{=it._column}} >= {{=it._min}}');
var maxFilterTpl = dot.template('{{=it._column}} <= {{=it._max}}'); var maxFilterTpl = dot.template('{{=it._column}} <= {{=it._max}}');
var filterQueryTpl = dot.template('SELECT * FROM ({{=it._sql}}) _analysis_range_filter WHERE {{=it._filter}}'); var filterQueryTpl = dot.template('SELECT * FROM ({{=it._sql}}) _analysis_range_filter WHERE {{=it._filter}}');
function Range(column, filterParams) { function Range (column, filterParams) {
this.column = column; this.column = column;
if (!Number.isFinite(filterParams.min) && !Number.isFinite(filterParams.max)) { if (!Number.isFinite(filterParams.min) && !Number.isFinite(filterParams.max)) {
@ -22,7 +22,7 @@ function Range(column, filterParams) {
module.exports = Range; module.exports = Range;
Range.prototype.sql = function(rawSql) { Range.prototype.sql = function (rawSql) {
var minMaxFilter; var minMaxFilter;
if (Number.isFinite(this.min) && Number.isFinite(this.max)) { if (Number.isFinite(this.min) && Number.isFinite(this.max)) {
minMaxFilter = betweenFilterTpl({ minMaxFilter = betweenFilterTpl({

View File

@ -33,14 +33,14 @@ var LONGITUDE_RANGE = LONGITUDE_UPPER_BOUND - LONGITUDE_LOWER_BOUND;
bbox: "west,south,east,north" bbox: "west,south,east,north"
} }
*/ */
function BBox(filterDefinition, filterParams) { function BBox (filterDefinition, filterParams) {
var bbox = filterParams.bbox; var bbox = filterParams.bbox;
if (!bbox) { if (!bbox) {
throw new Error('BBox filter expects to have a bbox param'); throw new Error('BBox filter expects to have a bbox param');
} }
var bboxElements = bbox.split(',').map(function(e) { return +e; }); var bboxElements = bbox.split(',').map(function (e) { return +e; });
validateBboxElements(bboxElements); validateBboxElements(bboxElements);
@ -59,7 +59,7 @@ function BBox(filterDefinition, filterParams) {
this.bboxes = getBoundingBoxes(west, south, east, north); this.bboxes = getBoundingBoxes(west, south, east, north);
} }
function getBoundingBoxes(west, south, east, north) { function getBoundingBoxes (west, south, east, north) {
var bboxes = []; var bboxes = [];
if (east - west >= 360) { if (east - west >= 360) {
@ -75,10 +75,10 @@ function getBoundingBoxes(west, south, east, north) {
return bboxes; return bboxes;
} }
function validateBboxElements(bboxElements) { function validateBboxElements (bboxElements) {
var isNumericBbox = bboxElements var isNumericBbox = bboxElements
.map(function(n) { return Number.isFinite(n); }) .map(function (n) { return Number.isFinite(n); })
.reduce(function(allFinite, isFinite) { .reduce(function (allFinite, isFinite) {
if (!allFinite) { if (!allFinite) {
return false; return false;
} }
@ -90,7 +90,7 @@ function validateBboxElements(bboxElements) {
} }
} }
function adjustLongitudeRange(we) { function adjustLongitudeRange (we) {
var west = we[0]; var west = we[0];
west -= LONGITUDE_LOWER_BOUND; west -= LONGITUDE_LOWER_BOUND;
west = west - (LONGITUDE_RANGE * Math.floor(west / LONGITUDE_RANGE)) + LONGITUDE_LOWER_BOUND; west = west - (LONGITUDE_RANGE * Math.floor(west / LONGITUDE_RANGE)) + LONGITUDE_LOWER_BOUND;
@ -106,11 +106,10 @@ module.exports.adjustLongitudeRange = adjustLongitudeRange;
module.exports.LATITUDE_MAX_VALUE = LATITUDE_MAX_VALUE; module.exports.LATITUDE_MAX_VALUE = LATITUDE_MAX_VALUE;
module.exports.LONGITUDE_MAX_VALUE = LONGITUDE_UPPER_BOUND; module.exports.LONGITUDE_MAX_VALUE = LONGITUDE_UPPER_BOUND;
BBox.prototype.sql = function (rawSql) {
BBox.prototype.sql = function(rawSql) {
var bboxSql = filterQueryTpl({ var bboxSql = filterQueryTpl({
_sql: rawSql, _sql: rawSql,
_filters: this.bboxes.map(function(bbox) { _filters: this.bboxes.map(function (bbox) {
return bboxFilterTpl({ return bboxFilterTpl({
_column: this.column, _column: this.column,
_bbox: bbox.join(','), _bbox: bbox.join(','),

View File

@ -3,7 +3,7 @@
/** /**
* @param {String} token might match the following pattern: {user}@{tpl_id}@{token}:{cache_buster} * @param {String} token might match the following pattern: {user}@{tpl_id}@{token}:{cache_buster}
*/ */
function parse(token) { function parse (token) {
var signer, cacheBuster; var signer, cacheBuster;
var tokenSplit = token.split(':'); var tokenSplit = token.split(':');
@ -14,10 +14,10 @@ function parse(token) {
} }
tokenSplit = token.split('@'); tokenSplit = token.split('@');
if ( tokenSplit.length > 1 ) { if (tokenSplit.length > 1) {
signer = tokenSplit.shift(); signer = tokenSplit.shift();
if ( tokenSplit.length > 1 ) { if (tokenSplit.length > 1) {
/*var template_hash = */tokenSplit.shift(); // unused /* var template_hash = */tokenSplit.shift(); // unused
} }
token = tokenSplit.shift(); token = tokenSplit.shift();
} }

View File

@ -4,11 +4,11 @@ const AggregationMapConfig = require('../../aggregation/aggregation-mapconfig');
const queryUtils = require('../../../utils/query-utils'); const queryUtils = require('../../../utils/query-utils');
const unsupportedGeometryTypeErrorMessage = ctx => const unsupportedGeometryTypeErrorMessage = ctx =>
`Unsupported geometry type: ${ctx.geometryType}. ` + `Unsupported geometry type: ${ctx.geometryType}. ` +
`Aggregation is available only for geometry type: ${AggregationMapConfig.SUPPORTED_GEOMETRY_TYPES}`; `Aggregation is available only for geometry type: ${AggregationMapConfig.SUPPORTED_GEOMETRY_TYPES}`;
const invalidAggregationParamValueErrorMessage = ctx => const invalidAggregationParamValueErrorMessage = ctx =>
`Invalid value for 'aggregation' query param: ${ctx.value}. Valid ones are 'true' or 'false'`; `Invalid value for 'aggregation' query param: ${ctx.value}. Valid ones are 'true' or 'false'`;
module.exports = class AggregationMapConfigAdapter { module.exports = class AggregationMapConfigAdapter {
constructor (pgConnection) { constructor (pgConnection) {
@ -27,7 +27,6 @@ module.exports = class AggregationMapConfigAdapter {
return callback(err); return callback(err);
} }
if (!this._shouldAdapt(mapConfig, params)) { if (!this._shouldAdapt(mapConfig, params)) {
return callback(null, requestMapConfig); return callback(null, requestMapConfig);
} }
@ -101,8 +100,7 @@ module.exports = class AggregationMapConfigAdapter {
try { try {
aggregationSql = mapConfig.getAggregatedQuery(index); aggregationSql = mapConfig.getAggregatedQuery(index);
} } catch (error) {
catch (error) {
return reject(error); return reject(error);
} }

View File

@ -7,13 +7,13 @@ var camshaft = require('camshaft');
var dot = require('dot'); var dot = require('dot');
dot.templateSettings.strip = false; dot.templateSettings.strip = false;
function AnalysisMapConfigAdapter(analysisBackend) { function AnalysisMapConfigAdapter (analysisBackend) {
this.analysisBackend = analysisBackend; this.analysisBackend = analysisBackend;
} }
module.exports = AnalysisMapConfigAdapter; module.exports = AnalysisMapConfigAdapter;
AnalysisMapConfigAdapter.prototype.getMapConfig = function(user, requestMapConfig, params, context, callback) { AnalysisMapConfigAdapter.prototype.getMapConfig = function (user, requestMapConfig, params, context, callback) {
// jshint maxcomplexity:7 // jshint maxcomplexity:7
var self = this; var self = this;
@ -41,7 +41,7 @@ AnalysisMapConfigAdapter.prototype.getMapConfig = function(user, requestMapConfi
return callback(errors); return callback(errors);
} }
var dataviewsFiltersBySourceId = Object.keys(dataviewsFilters).reduce(function(bySourceId, dataviewName) { var dataviewsFiltersBySourceId = Object.keys(dataviewsFilters).reduce(function (bySourceId, dataviewName) {
var dataview = dataviews[dataviewName]; var dataview = dataviews[dataviewName];
if (dataview) { if (dataview) {
var sourceId = dataview.source.id; var sourceId = dataview.source.id;
@ -67,7 +67,7 @@ AnalysisMapConfigAdapter.prototype.getMapConfig = function(user, requestMapConfi
// }} // }}
requestMapConfig = appendFiltersToNodes(requestMapConfig, filters.analyses); requestMapConfig = appendFiltersToNodes(requestMapConfig, filters.analyses);
function createAnalysis(analysisDefinition, done) { function createAnalysis (analysisDefinition, done) {
self.analysisBackend.create(analysisConfiguration, analysisDefinition, function (err, analysis) { self.analysisBackend.create(analysisConfiguration, analysisDefinition, function (err, analysis) {
if (err) { if (err) {
var error = new Error(err.message); var error = new Error(err.message);
@ -85,22 +85,22 @@ AnalysisMapConfigAdapter.prototype.getMapConfig = function(user, requestMapConfi
} }
var analysesQueue = queue(1); var analysesQueue = queue(1);
requestMapConfig.analyses.forEach(function(analysis) { requestMapConfig.analyses.forEach(function (analysis) {
analysesQueue.defer(createAnalysis, analysis); analysesQueue.defer(createAnalysis, analysis);
}); });
analysesQueue.awaitAll(function(err, analysesResults) { analysesQueue.awaitAll(function (err, analysesResults) {
if (err) { if (err) {
return callback(err); return callback(err);
} }
var sourceId2Node = analysesResults.reduce(function(sourceId2Query, analysis) { var sourceId2Node = analysesResults.reduce(function (sourceId2Query, analysis) {
var rootNode = analysis.getRoot(); var rootNode = analysis.getRoot();
if (rootNode.params && rootNode.params.id) { if (rootNode.params && rootNode.params.id) {
sourceId2Query[rootNode.params.id] = rootNode; sourceId2Query[rootNode.params.id] = rootNode;
} }
analysis.getNodes().forEach(function(node) { analysis.getNodes().forEach(function (node) {
if (node.params && node.params.id) { if (node.params && node.params.id) {
sourceId2Query[node.params.id] = node; sourceId2Query[node.params.id] = node;
} }
@ -111,7 +111,7 @@ AnalysisMapConfigAdapter.prototype.getMapConfig = function(user, requestMapConfi
var analysesErrors = []; var analysesErrors = [];
requestMapConfig.layers = requestMapConfig.layers.map(function(layer, layerIndex) { requestMapConfig.layers = requestMapConfig.layers.map(function (layer, layerIndex) {
if (getLayerSourceId(layer)) { if (getLayerSourceId(layer)) {
var layerSourceId = getLayerSourceId(layer); var layerSourceId = getLayerSourceId(layer);
var layerNode = sourceId2Node[layerSourceId]; var layerNode = sourceId2Node[layerSourceId];
@ -135,7 +135,7 @@ AnalysisMapConfigAdapter.prototype.getMapConfig = function(user, requestMapConfi
} }
} else { } else {
analysesErrors.push( analysesErrors.push(
new Error('Missing analysis node.id="' + layerSourceId +'" for layer='+layerIndex) new Error('Missing analysis node.id="' + layerSourceId + '" for layer=' + layerIndex)
); );
} }
} }
@ -148,7 +148,7 @@ AnalysisMapConfigAdapter.prototype.getMapConfig = function(user, requestMapConfi
} }
// Augment dataviews with sql from analyses // Augment dataviews with sql from analyses
Object.keys(dataviews).forEach(function(dataviewName) { Object.keys(dataviews).forEach(function (dataviewName) {
var dataview = requestMapConfig.dataviews[dataviewName]; var dataview = requestMapConfig.dataviews[dataviewName];
var dataviewSourceId = dataview.source.id; var dataviewSourceId = dataview.source.id;
var dataviewNode = sourceId2Node[dataviewSourceId]; var dataviewNode = sourceId2Node[dataviewSourceId];
@ -160,7 +160,7 @@ AnalysisMapConfigAdapter.prototype.getMapConfig = function(user, requestMapConfi
own_filter_on: dataviewQuery(dataviewNode, dataviewName, true), own_filter_on: dataviewQuery(dataviewNode, dataviewName, true),
own_filter_off: dataviewQuery(dataviewNode, dataviewName, false), own_filter_off: dataviewQuery(dataviewNode, dataviewName, false),
no_filters: dataviewNode.getQuery(Object.keys(dataviewNode.getFilters()) no_filters: dataviewNode.getQuery(Object.keys(dataviewNode.getFilters())
.reduce(function(applyFilters, filterId) { .reduce(function (applyFilters, filterId) {
applyFilters[filterId] = false; applyFilters[filterId] = false;
return applyFilters; return applyFilters;
}, {}) }, {})
@ -180,13 +180,13 @@ AnalysisMapConfigAdapter.prototype.getMapConfig = function(user, requestMapConfi
}; };
var SKIP_COLUMNS = { var SKIP_COLUMNS = {
'the_geom': true, the_geom: true,
'the_geom_webmercator': true the_geom_webmercator: true
}; };
function skipColumns(columnNames) { function skipColumns (columnNames) {
return columnNames return columnNames
.filter(function(columnName) { return !SKIP_COLUMNS[columnName]; }); .filter(function (columnName) { return !SKIP_COLUMNS[columnName]; });
} }
var wrappedQueryTpl = dot.template([ var wrappedQueryTpl = dot.template([
@ -194,7 +194,7 @@ var wrappedQueryTpl = dot.template([
'FROM ({{=it._query}}) _cdb_analysis_query' 'FROM ({{=it._query}}) _cdb_analysis_query'
].join('\n')); ].join('\n'));
function layerQuery(node) { function layerQuery (node) {
if (node.type === 'source') { if (node.type === 'source') {
return node.getQuery(); return node.getQuery();
} }
@ -202,7 +202,7 @@ function layerQuery(node) {
return wrappedQueryTpl({ _query: node.getQuery(), _columns: _columns.join(', ') }); return wrappedQueryTpl({ _query: node.getQuery(), _columns: _columns.join(', ') });
} }
function dataviewQuery(node, dataviewName, ownFilter) { function dataviewQuery (node, dataviewName, ownFilter) {
var applyFilters = {}; var applyFilters = {};
if (!ownFilter) { if (!ownFilter) {
applyFilters[dataviewName] = false; applyFilters[dataviewName] = false;
@ -215,15 +215,15 @@ function dataviewQuery(node, dataviewName, ownFilter) {
return wrappedQueryTpl({ _query: node.getQuery(applyFilters), _columns: _columns.join(', ') }); return wrappedQueryTpl({ _query: node.getQuery(applyFilters), _columns: _columns.join(', ') });
} }
function appendFiltersToNodes(requestMapConfig, dataviewsFiltersBySourceId) { function appendFiltersToNodes (requestMapConfig, dataviewsFiltersBySourceId) {
var analyses = requestMapConfig.analyses || []; var analyses = requestMapConfig.analyses || [];
dataviewsFiltersBySourceId = dataviewsFiltersBySourceId || {}; dataviewsFiltersBySourceId = dataviewsFiltersBySourceId || {};
requestMapConfig.analyses = analyses.map(function(analysisDefinition) { requestMapConfig.analyses = analyses.map(function (analysisDefinition) {
var analysisGraph = new camshaft.reference.AnalysisGraph(analysisDefinition); var analysisGraph = new camshaft.reference.AnalysisGraph(analysisDefinition);
var definition = analysisDefinition; var definition = analysisDefinition;
Object.keys(dataviewsFiltersBySourceId).forEach(function(sourceId) { Object.keys(dataviewsFiltersBySourceId).forEach(function (sourceId) {
definition = analysisGraph.getDefinitionWith(sourceId, {filters: dataviewsFiltersBySourceId[sourceId] }); definition = analysisGraph.getDefinitionWith(sourceId, { filters: dataviewsFiltersBySourceId[sourceId] });
}); });
return definition; return definition;
@ -232,7 +232,7 @@ function appendFiltersToNodes(requestMapConfig, dataviewsFiltersBySourceId) {
return requestMapConfig; return requestMapConfig;
} }
function shouldAdaptLayers(requestMapConfig) { function shouldAdaptLayers (requestMapConfig) {
return Array.isArray(requestMapConfig.layers) && requestMapConfig.layers.some(getLayerSourceId) || return Array.isArray(requestMapConfig.layers) && requestMapConfig.layers.some(getLayerSourceId) ||
(Array.isArray(requestMapConfig.analyses) && requestMapConfig.analyses.length > 0) || (Array.isArray(requestMapConfig.analyses) && requestMapConfig.analyses.length > 0) ||
requestMapConfig.dataviews; requestMapConfig.dataviews;
@ -242,7 +242,7 @@ var DATAVIEW_TYPE_2_FILTER_TYPE = {
aggregation: 'category', aggregation: 'category',
histogram: 'range' histogram: 'range'
}; };
function getFilter(dataview, params) { function getFilter (dataview, params) {
var type = dataview.type; var type = dataview.type;
return { return {
@ -252,21 +252,21 @@ function getFilter(dataview, params) {
}; };
} }
function getLayerSourceId(layer) { function getLayerSourceId (layer) {
return layer.options.source && layer.options.source.id; return layer.options.source && layer.options.source.id;
} }
function getDataviewSourceId(dataview) { function getDataviewSourceId (dataview) {
return dataview.source && dataview.source.id; return dataview.source && dataview.source.id;
} }
function getLayerDataviews(layer, dataviews) { function getLayerDataviews (layer, dataviews) {
var layerDataviews = []; var layerDataviews = [];
var layerSourceId = getLayerSourceId(layer); var layerSourceId = getLayerSourceId(layer);
if (layerSourceId) { if (layerSourceId) {
var dataviewsList = getDataviewsList(dataviews); var dataviewsList = getDataviewsList(dataviews);
dataviewsList.forEach(function(dataview) { dataviewsList.forEach(function (dataview) {
if (getDataviewSourceId(dataview) === layerSourceId) { if (getDataviewSourceId(dataview) === layerSourceId) {
layerDataviews.push(dataview); layerDataviews.push(dataview);
} }
@ -276,10 +276,10 @@ function getLayerDataviews(layer, dataviews) {
return layerDataviews; return layerDataviews;
} }
function getDataviewsColumns(dataviews) { function getDataviewsColumns (dataviews) {
return Object.keys(dataviews.reduce(function(columnsDict, dataview) { return Object.keys(dataviews.reduce(function (columnsDict, dataview) {
getDataviewColumns(dataview).forEach(function(columnName) { getDataviewColumns(dataview).forEach(function (columnName) {
if (!!columnName) { if (columnName) {
columnsDict[columnName] = true; columnsDict[columnName] = true;
} }
}); });
@ -287,10 +287,10 @@ function getDataviewsColumns(dataviews) {
}, {})); }, {}));
} }
function getDataviewColumns(dataview) { function getDataviewColumns (dataview) {
var columns = []; var columns = [];
var options = dataview.options; var options = dataview.options;
['column', 'aggregationColumn'].forEach(function(opt) { ['column', 'aggregationColumn'].forEach(function (opt) {
if (options.hasOwnProperty(opt) && !!options[opt]) { if (options.hasOwnProperty(opt) && !!options[opt]) {
columns.push(options[opt]); columns.push(options[opt]);
} }
@ -298,11 +298,11 @@ function getDataviewColumns(dataview) {
return columns; return columns;
} }
function getDataviewsList(dataviews) { function getDataviewsList (dataviews) {
return Object.keys(dataviews).map(function(dataviewKey) { return dataviews[dataviewKey]; }); return Object.keys(dataviews).map(function (dataviewKey) { return dataviews[dataviewKey]; });
} }
function getDataviewsErrors(dataviews) { function getDataviewsErrors (dataviews) {
var dataviewType = typeof dataviews; var dataviewType = typeof dataviews;
if (dataviewType !== 'object') { if (dataviewType !== 'object') {
return [new Error('"dataviews" must be a valid JSON object: "' + dataviewType + '" type found')]; return [new Error('"dataviews" must be a valid JSON object: "' + dataviewType + '" type found')];
@ -314,7 +314,7 @@ function getDataviewsErrors(dataviews) {
var errors = []; var errors = [];
Object.keys(dataviews).forEach(function(dataviewName) { Object.keys(dataviews).forEach(function (dataviewName) {
var dataview = dataviews[dataviewName]; var dataview = dataviews[dataviewName];
if (!dataview.hasOwnProperty('source') || !dataview.source.id) { if (!dataview.hasOwnProperty('source') || !dataview.source.id) {
errors.push(new Error('Dataview "' + dataviewName + '" is missing `source.id` attribute')); errors.push(new Error('Dataview "' + dataviewName + '" is missing `source.id` attribute'));
@ -328,13 +328,13 @@ function getDataviewsErrors(dataviews) {
return errors; return errors;
} }
function getMissingDataviewsSourceIds(dataviews, sourceId2Node) { function getMissingDataviewsSourceIds (dataviews, sourceId2Node) {
var missingDataviewsSourceIds = []; var missingDataviewsSourceIds = [];
Object.keys(dataviews).forEach(function(dataviewName) { Object.keys(dataviews).forEach(function (dataviewName) {
var dataview = dataviews[dataviewName]; var dataview = dataviews[dataviewName];
var dataviewSourceId = getDataviewSourceId(dataview); var dataviewSourceId = getDataviewSourceId(dataview);
if (!sourceId2Node.hasOwnProperty(dataviewSourceId)) { if (!sourceId2Node.hasOwnProperty(dataviewSourceId)) {
missingDataviewsSourceIds.push(new AnalysisError('Node with `source.id="' + dataviewSourceId +'"`' + missingDataviewsSourceIds.push(new AnalysisError('Node with `source.id="' + dataviewSourceId + '"`' +
' not found in analyses for dataview "' + dataviewName + '"')); ' not found in analyses for dataview "' + dataviewName + '"'));
} }
}); });
@ -342,19 +342,19 @@ function getMissingDataviewsSourceIds(dataviews, sourceId2Node) {
return missingDataviewsSourceIds; return missingDataviewsSourceIds;
} }
function AnalysisError(message) { function AnalysisError (message) {
Error.captureStackTrace(this, this.constructor); Error.captureStackTrace(this, this.constructor);
this.name = this.constructor.name; this.name = this.constructor.name;
this.type = 'analysis'; this.type = 'analysis';
this.message = message; this.message = message;
} }
function getAllAffectedTablesFromSourceNodes(node) { function getAllAffectedTablesFromSourceNodes (node) {
var affectedTables = node.getAllInputNodes(function (node) { var affectedTables = node.getAllInputNodes(function (node) {
return node.getType() === 'source'; return node.getType() === 'source';
}).reduce(function(list, node) { }).reduce(function (list, node) {
return list.concat(node.getAffectedTables()); return list.concat(node.getAffectedTables());
},[]); }, []);
return affectedTables; return affectedTables;
} }

View File

@ -1,12 +1,11 @@
'use strict'; 'use strict';
function DataviewsWidgetsMapConfigAdapter() { function DataviewsWidgetsMapConfigAdapter () {
} }
module.exports = DataviewsWidgetsMapConfigAdapter; module.exports = DataviewsWidgetsMapConfigAdapter;
DataviewsWidgetsMapConfigAdapter.prototype.getMapConfig = function (user, requestMapConfig, params, context, callback) {
DataviewsWidgetsMapConfigAdapter.prototype.getMapConfig = function(user, requestMapConfig, params, context, callback) {
if (!shouldAdapt(requestMapConfig)) { if (!shouldAdapt(requestMapConfig)) {
return callback(null, requestMapConfig); return callback(null, requestMapConfig);
} }
@ -15,7 +14,7 @@ DataviewsWidgetsMapConfigAdapter.prototype.getMapConfig = function(user, request
requestMapConfig.analyses = requestMapConfig.analyses || []; requestMapConfig.analyses = requestMapConfig.analyses || [];
requestMapConfig.dataviews = requestMapConfig.dataviews || {}; requestMapConfig.dataviews = requestMapConfig.dataviews || {};
requestMapConfig.layers.forEach(function(layer, index) { requestMapConfig.layers.forEach(function (layer, index) {
var layerSourceId = getLayerSourceId(layer); var layerSourceId = getLayerSourceId(layer);
if (!layer.options.widgets) { if (!layer.options.widgets) {
@ -41,7 +40,7 @@ DataviewsWidgetsMapConfigAdapter.prototype.getMapConfig = function(user, request
} }
var source = { id: dataviewSourceId }; var source = { id: dataviewSourceId };
var layerWidgets = layer.options.widgets || {}; var layerWidgets = layer.options.widgets || {};
Object.keys(layerWidgets).forEach(function(widgetId) { Object.keys(layerWidgets).forEach(function (widgetId) {
var dataview = layerWidgets[widgetId]; var dataview = layerWidgets[widgetId];
requestMapConfig.dataviews[widgetId] = { requestMapConfig.dataviews[widgetId] = {
source: source, source: source,
@ -54,7 +53,7 @@ DataviewsWidgetsMapConfigAdapter.prototype.getMapConfig = function(user, request
delete layer.options.sql; delete layer.options.sql;
// don't delete widgets for now as it might be useful for old clients // don't delete widgets for now as it might be useful for old clients
//delete layer.options.widgets; // delete layer.options.widgets;
}); });
// filters have to be rewritten also // filters have to be rewritten also
@ -62,8 +61,8 @@ DataviewsWidgetsMapConfigAdapter.prototype.getMapConfig = function(user, request
var layersFilters = filters.layers || []; var layersFilters = filters.layers || [];
filters.dataviews = filters.dataviews || {}; filters.dataviews = filters.dataviews || {};
layersFilters.forEach(function(layerFilters) { layersFilters.forEach(function (layerFilters) {
Object.keys(layerFilters).forEach(function(filterName) { Object.keys(layerFilters).forEach(function (filterName) {
if (!filters.dataviews.hasOwnProperty(filterName)) { if (!filters.dataviews.hasOwnProperty(filterName)) {
filters.dataviews[filterName] = layerFilters[filterName]; filters.dataviews[filterName] = layerFilters[filterName];
} }
@ -77,17 +76,17 @@ DataviewsWidgetsMapConfigAdapter.prototype.getMapConfig = function(user, request
return callback(null, requestMapConfig); return callback(null, requestMapConfig);
}; };
function shouldAdapt(requestMapConfig) { function shouldAdapt (requestMapConfig) {
return Array.isArray(requestMapConfig.layers) && requestMapConfig.layers.some(function hasWidgets(layer) { return Array.isArray(requestMapConfig.layers) && requestMapConfig.layers.some(function hasWidgets (layer) {
return layer.options && layer.options.widgets && Object.keys(layer.options.widgets).length > 0; return layer.options && layer.options.widgets && Object.keys(layer.options.widgets).length > 0;
}); });
} }
function getLayerSourceId(layer) { function getLayerSourceId (layer) {
return layer.options.source && layer.options.source.id; return layer.options.source && layer.options.source.id;
} }
function getFilters(params) { function getFilters (params) {
var filters = {}; var filters = {};
if (params.filters) { if (params.filters) {
try { try {

View File

@ -1,19 +1,19 @@
'use strict'; 'use strict';
function MapConfigAdapter(adapters) { function MapConfigAdapter (adapters) {
this.adapters = Array.isArray(adapters) ? adapters : Array.apply(null, arguments); this.adapters = Array.isArray(adapters) ? adapters : Array.apply(null, arguments);
} }
module.exports = MapConfigAdapter; module.exports = MapConfigAdapter;
MapConfigAdapter.prototype.getMapConfig = function(user, requestMapConfig, params, context, callback) { MapConfigAdapter.prototype.getMapConfig = function (user, requestMapConfig, params, context, callback) {
var self = this; var self = this;
var i = 0; var i = 0;
var tasksLeft = this.adapters.length; var tasksLeft = this.adapters.length;
let mapConfigStats = {}; let mapConfigStats = {};
function next(err, _requestMapConfig, adapterStats = {}) { function next (err, _requestMapConfig, adapterStats = {}) {
if (err) { if (err) {
return callback(err); return callback(err);
} }

View File

@ -1,6 +1,6 @@
'use strict'; 'use strict';
function MapConfigBufferSizeAdapter() { function MapConfigBufferSizeAdapter () {
this.formats = ['png', 'png32', 'mvt', 'grid.json']; this.formats = ['png', 'png32', 'mvt', 'grid.json'];
} }

View File

@ -4,7 +4,7 @@ var queue = require('queue-async');
var _ = require('underscore'); var _ = require('underscore');
var Datasource = require('windshaft').model.Datasource; var Datasource = require('windshaft').model.Datasource;
function MapConfigNamedLayersAdapter(templateMaps, pgConnection) { function MapConfigNamedLayersAdapter (templateMaps, pgConnection) {
this.templateMaps = templateMaps; this.templateMaps = templateMaps;
this.pgConnection = pgConnection; this.pgConnection = pgConnection;
} }
@ -22,9 +22,8 @@ MapConfigNamedLayersAdapter.prototype.getMapConfig = function (user, requestMapC
var adaptLayersQueue = queue(layers.length); var adaptLayersQueue = queue(layers.length);
function adaptLayer(layer, done) { function adaptLayer (layer, done) {
if (isNamedTypeLayer(layer)) { if (isNamedTypeLayer(layer)) {
if (!layer.options.name) { if (!layer.options.name) {
return done(new Error('Missing Named Map `name` in layer options')); return done(new Error('Missing Named Map `name` in layer options'));
} }
@ -33,13 +32,13 @@ MapConfigNamedLayersAdapter.prototype.getMapConfig = function (user, requestMapC
var templateConfigParams = layer.options.config || {}; var templateConfigParams = layer.options.config || {};
var templateAuthTokens = layer.options.auth_tokens; var templateAuthTokens = layer.options.auth_tokens;
self.templateMaps.getTemplate(user, templateName, function(err, template) { self.templateMaps.getTemplate(user, templateName, function (err, template) {
if (err || !template) { if (err || !template) {
return done(new Error("Template '" + templateName + "' of user '" + user + "' not found")); return done(new Error("Template '" + templateName + "' of user '" + user + "' not found"));
} }
if (self.templateMaps.isAuthorized(template, templateAuthTokens)) { if (self.templateMaps.isAuthorized(template, templateAuthTokens)) {
var nestedNamedLayers = template.layergroup.layers.filter(function(layer) { var nestedNamedLayers = template.layergroup.layers.filter(function (layer) {
return layer.type === 'named'; return layer.type === 'named';
}); });
@ -63,7 +62,6 @@ MapConfigNamedLayersAdapter.prototype.getMapConfig = function (user, requestMapC
return done(unauthorizedError); return done(unauthorizedError);
} }
}); });
} else { } else {
return done(null, { return done(null, {
datasource: false, datasource: false,
@ -74,7 +72,7 @@ MapConfigNamedLayersAdapter.prototype.getMapConfig = function (user, requestMapC
var datasourceBuilder = new Datasource.Builder(); var datasourceBuilder = new Datasource.Builder();
function layersAdaptQueueFinish(err, layersResults) { function layersAdaptQueueFinish (err, layersResults) {
if (err) { if (err) {
return callback(err); return callback(err);
} }
@ -83,12 +81,11 @@ MapConfigNamedLayersAdapter.prototype.getMapConfig = function (user, requestMapC
return callback(new Error('Missing layers array from layergroup config')); return callback(new Error('Missing layers array from layergroup config'));
} }
var layers = [], var layers = [];
currentLayerIndex = 0; var currentLayerIndex = 0;
layersResults.forEach(function(layersResult) { layersResults.forEach(function (layersResult) {
layersResult.layers.forEach(function (layer) {
layersResult.layers.forEach(function(layer) {
layers.push(layer); layers.push(layer);
if (layersResult.datasource) { if (layersResult.datasource) {
datasourceBuilder.withLayerDatasource(currentLayerIndex, { datasourceBuilder.withLayerDatasource(currentLayerIndex, {
@ -97,7 +94,6 @@ MapConfigNamedLayersAdapter.prototype.getMapConfig = function (user, requestMapC
} }
currentLayerIndex++; currentLayerIndex++;
}); });
}); });
requestMapConfig.layers = layers; requestMapConfig.layers = layers;
@ -106,15 +102,14 @@ MapConfigNamedLayersAdapter.prototype.getMapConfig = function (user, requestMapC
return callback(null, requestMapConfig); return callback(null, requestMapConfig);
} }
var dbAuth = {}; var dbAuth = {};
if (_.some(layers, isNamedTypeLayer)) { if (_.some(layers, isNamedTypeLayer)) {
this.pgConnection.setDBAuth(user, dbAuth, 'master', function(err) { this.pgConnection.setDBAuth(user, dbAuth, 'master', function (err) {
if (err) { if (err) {
return callback(err); return callback(err);
} }
layers.forEach(function(layer) { layers.forEach(function (layer) {
adaptLayersQueue.defer(adaptLayer, layer); adaptLayersQueue.defer(adaptLayer, layer);
}); });
adaptLayersQueue.awaitAll(layersAdaptQueueFinish); adaptLayersQueue.awaitAll(layersAdaptQueueFinish);
@ -123,9 +118,8 @@ MapConfigNamedLayersAdapter.prototype.getMapConfig = function (user, requestMapC
context.datasource = datasourceBuilder.build(); context.datasource = datasourceBuilder.build();
return callback(null, requestMapConfig); return callback(null, requestMapConfig);
} }
}; };
function isNamedTypeLayer(layer) { function isNamedTypeLayer (layer) {
return layer.type === 'named'; return layer.type === 'named';
} }

View File

@ -4,7 +4,7 @@ var queue = require('queue-async');
var _ = require('underscore'); var _ = require('underscore');
const AggregationMapConfig = require('../../aggregation/aggregation-mapconfig'); const AggregationMapConfig = require('../../aggregation/aggregation-mapconfig');
function MapConfigOverviewsAdapter(overviewsMetadataBackend, filterStatsBackend) { function MapConfigOverviewsAdapter (overviewsMetadataBackend, filterStatsBackend) {
this.overviewsMetadataBackend = overviewsMetadataBackend; this.overviewsMetadataBackend = overviewsMetadataBackend;
this.filterStatsBackend = filterStatsBackend; this.filterStatsBackend = filterStatsBackend;
} }

View File

@ -1,14 +1,13 @@
'use strict'; 'use strict';
function SqlWrapMapConfigAdapter() { function SqlWrapMapConfigAdapter () {
} }
module.exports = SqlWrapMapConfigAdapter; module.exports = SqlWrapMapConfigAdapter;
SqlWrapMapConfigAdapter.prototype.getMapConfig = function (user, requestMapConfig, params, context, callback) {
SqlWrapMapConfigAdapter.prototype.getMapConfig = function(user, requestMapConfig, params, context, callback) {
if (requestMapConfig && Array.isArray(requestMapConfig.layers)) { if (requestMapConfig && Array.isArray(requestMapConfig.layers)) {
requestMapConfig.layers = requestMapConfig.layers.map(function(layer) { requestMapConfig.layers = requestMapConfig.layers.map(function (layer) {
if (layer.options) { if (layer.options) {
var sqlQueryWrap = layer.options.sql_wrap; var sqlQueryWrap = layer.options.sql_wrap;
if (sqlQueryWrap) { if (sqlQueryWrap) {

View File

@ -13,7 +13,7 @@ var MapConfig = require('windshaft').model.MapConfig;
const dbParamsFromReqParams = require('../../../utils/database-params'); const dbParamsFromReqParams = require('../../../utils/database-params');
function TurboCartoAdapter() { function TurboCartoAdapter () {
} }
module.exports = TurboCartoAdapter; module.exports = TurboCartoAdapter;
@ -29,7 +29,7 @@ TurboCartoAdapter.prototype.getMapConfig = function (user, requestMapConfig, par
var parseCartoQueue = queue(layers.length); var parseCartoQueue = queue(layers.length);
layers.forEach(function(layer, index) { layers.forEach(function (layer, index) {
var layerId = MapConfig.getLayerId(requestMapConfig, index); var layerId = MapConfig.getLayerId(requestMapConfig, index);
parseCartoQueue.defer(self._parseCartoCss.bind(self), user, params, layer, index, layerId); parseCartoQueue.defer(self._parseCartoCss.bind(self), user, params, layer, index, layerId);
}); });
@ -39,7 +39,7 @@ TurboCartoAdapter.prototype.getMapConfig = function (user, requestMapConfig, par
return callback(err); return callback(err);
} }
var errors = results.reduce(function(errors, result) { var errors = results.reduce(function (errors, result) {
if (result.error) { if (result.error) {
errors.push(result.error); errors.push(result.error);
} }
@ -49,9 +49,9 @@ TurboCartoAdapter.prototype.getMapConfig = function (user, requestMapConfig, par
return callback(errors); return callback(errors);
} }
requestMapConfig.layers = results.map(function(result) { return result.layer; }); requestMapConfig.layers = results.map(function (result) { return result.layer; });
context.turboCarto = { context.turboCarto = {
layers: results.map(function(result) { layers: results.map(function (result) {
return result.meta; return result.meta;
}) })
}; };
@ -91,7 +91,7 @@ TurboCartoAdapter.prototype._parseCartoCss = function (username, params, layer,
} }
var pg = new PSQL(dbParamsFromReqParams(params)); var pg = new PSQL(dbParamsFromReqParams(params));
function processCallback(err, cartocss, meta) { function processCallback (err, cartocss, meta) {
// Only return turbo-carto errors // Only return turbo-carto errors
if (err && err.name === 'TurboCartoError') { if (err && err.name === 'TurboCartoError') {
var error = new Error(err.message); var error = new Error(err.message);
@ -121,8 +121,8 @@ TurboCartoAdapter.prototype._parseCartoCss = function (username, params, layer,
// For wrapped queries we'll derive the tokens from the data extent // For wrapped queries we'll derive the tokens from the data extent
// instead of the whole Earth/root tile. // instead of the whole Earth/root tile.
var self = this; var self = this;
var tokensQuery = tokensQueryTpl({_sql: layerRawSql}); var tokensQuery = tokensQueryTpl({ _sql: layerRawSql });
return pg.query(tokensQuery, function(err, resultSet) { return pg.query(tokensQuery, function (err, resultSet) {
if (err) { if (err) {
return processCallback(err); return processCallback(err);
} }
@ -159,6 +159,6 @@ TurboCartoAdapter.prototype.process = function (psql, cartocss, sql, callback) {
turboCarto(cartocss, datasource, callback); turboCarto(cartocss, datasource, callback);
}; };
function shouldParseLayerCartocss(layer) { function shouldParseLayerCartocss (layer) {
return layer && layer.options && layer.options.cartocss && layer.options.sql; return layer && layer.options && layer.options.cartocss && layer.options.sql;
} }

View File

@ -10,11 +10,11 @@ const dateWrapper = require('../../../utils/date-wrapper');
* - This middleware is ONLY activated when the `dates_as_numbers` option is enabled for some layer in the mapConfig. * - This middleware is ONLY activated when the `dates_as_numbers` option is enabled for some layer in the mapConfig.
*/ */
class VectorMapConfigAdapter { class VectorMapConfigAdapter {
constructor(pgConnection) { constructor (pgConnection) {
this.pgConnection = pgConnection; this.pgConnection = pgConnection;
} }
getMapConfig(user, requestMapConfig, params, context, callback) { getMapConfig (user, requestMapConfig, params, context, callback) {
if (!this._isDatesAsNumbersFlagEnabled(requestMapConfig)) { if (!this._isDatesAsNumbersFlagEnabled(requestMapConfig)) {
return callback(null, requestMapConfig); return callback(null, requestMapConfig);
} }
@ -24,12 +24,12 @@ class VectorMapConfigAdapter {
.catch(callback); .catch(callback);
} }
_wrapDates(requestMapConfig, user) { _wrapDates (requestMapConfig, user) {
return Promise.all(requestMapConfig.layers.map(layer => this._wrapLayer(layer, user))) return Promise.all(requestMapConfig.layers.map(layer => this._wrapLayer(layer, user)))
.then(() => requestMapConfig); .then(() => requestMapConfig);
} }
_wrapLayer(layer, user) { _wrapLayer (layer, user) {
if (!layer.options.dates_as_numbers || !layer.options.sql) { if (!layer.options.dates_as_numbers || !layer.options.sql) {
return Promise.resolve(layer); return Promise.resolve(layer);
} }
@ -42,7 +42,7 @@ class VectorMapConfigAdapter {
}); });
} }
_getColumns(user, originalQuery) { _getColumns (user, originalQuery) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.pgConnection.getConnection(user, (err, connection) => { this.pgConnection.getConnection(user, (err, connection) => {
if (err) { if (err) {
@ -56,10 +56,9 @@ class VectorMapConfigAdapter {
}); });
} }
_isDatesAsNumbersFlagEnabled(requestMapConfig) { _isDatesAsNumbersFlagEnabled (requestMapConfig) {
return requestMapConfig.layers && requestMapConfig.layers.some(layer => layer.options.dates_as_numbers); return requestMapConfig.layers && requestMapConfig.layers.some(layer => layer.options.dates_as_numbers);
} }
} }
module.exports = VectorMapConfigAdapter; module.exports = VectorMapConfigAdapter;

View File

@ -4,7 +4,7 @@ const BaseMapConfigProvider = require('./base-mapconfig-adapter');
const dot = require('dot'); const dot = require('dot');
// Configure bases for cache keys suitable for string interpolation // Configure bases for cache keys suitable for string interpolation
const baseKey = '{{=it.dbname}}:{{=it.token}}'; const baseKey = '{{=it.dbname}}:{{=it.token}}';
const rendererKey = baseKey + ':{{=it.dbuser}}:{{=it.format}}:{{=it.layer}}:{{=it.scale_factor}}'; const rendererKey = baseKey + ':{{=it.dbuser}}:{{=it.format}}:{{=it.layer}}:{{=it.scale_factor}}';
const baseKeyTpl = dot.template(baseKey); const baseKeyTpl = dot.template(baseKey);

View File

@ -68,9 +68,9 @@ module.exports = class NamedMapMapConfigProvider extends BaseMapConfigProvider {
if (this.config) { if (this.config) {
try { try {
templateParams = Object.prototype.toString.call(this.config) === '[object String]' ? templateParams = Object.prototype.toString.call(this.config) === '[object String]'
JSON.parse(this.config) : ? JSON.parse(this.config)
this.config; : this.config;
} catch (e) { } catch (e) {
const err = new Error('malformed config parameter, should be a valid JSON'); const err = new Error('malformed config parameter, should be a valid JSON');
return callback(err); return callback(err);
@ -96,15 +96,15 @@ module.exports = class NamedMapMapConfigProvider extends BaseMapConfigProvider {
this.mapConfigAdapter.getMapConfig( this.mapConfigAdapter.getMapConfig(
user, requestMapConfig, rendererParams, context, (err, mapConfig, stats = {}) => { user, requestMapConfig, rendererParams, context, (err, mapConfig, stats = {}) => {
if (err) { if (err) {
return callback(err); return callback(err);
} }
this.mapConfig = (mapConfig === null) ? null : new MapConfig(mapConfig, context.datasource); this.mapConfig = (mapConfig === null) ? null : new MapConfig(mapConfig, context.datasource);
this.analysesResults = context.analysesResults || []; this.analysesResults = context.analysesResults || [];
return callback(null, this.mapConfig, this.rendererParams, this.context, stats); return callback(null, this.mapConfig, this.rendererParams, this.context, stats);
}); });
}); });
}); });
} }
@ -256,12 +256,12 @@ module.exports = class NamedMapMapConfigProvider extends BaseMapConfigProvider {
} }
}; };
function createConfigHash(config) { function createConfigHash (config) {
if (!config) { if (!config) {
return ''; return '';
} }
return crypto.createHash('md5').update(JSON.stringify(config)).digest('hex').substring(0,8); return crypto.createHash('md5').update(JSON.stringify(config)).digest('hex').substring(0, 8);
} }
module.exports.configHash = createConfigHash; module.exports.configHash = createConfigHash;

View File

@ -3,7 +3,7 @@
var dot = require('dot'); var dot = require('dot');
dot.templateSettings.strip = false; dot.templateSettings.strip = false;
function ResourceLocator(environment) { function ResourceLocator (environment) {
this.environment = environment; this.environment = environment;
this.resourcesUrlTemplates = null; this.resourcesUrlTemplates = null;
@ -23,7 +23,7 @@ function ResourceLocator(environment) {
module.exports = ResourceLocator; module.exports = ResourceLocator;
ResourceLocator.prototype.getTileUrls = function(username, resourcePath) { ResourceLocator.prototype.getTileUrls = function (username, resourcePath) {
if (this.resourcesUrlTemplates) { if (this.resourcesUrlTemplates) {
const urls = this.getUrlsFromTemplate(username, new TileResource(resourcePath)); const urls = this.getUrlsFromTemplate(username, new TileResource(resourcePath));
return { return {
@ -42,7 +42,7 @@ ResourceLocator.prototype.getTileUrls = function(username, resourcePath) {
} }
}; };
ResourceLocator.prototype.getTemplateUrls = function(username, resourcePath) { ResourceLocator.prototype.getTemplateUrls = function (username, resourcePath) {
if (this.resourcesUrlTemplates) { if (this.resourcesUrlTemplates) {
return this.getUrlsFromTemplate(username, new TemplateResource(resourcePath), true); return this.getUrlsFromTemplate(username, new TemplateResource(resourcePath), true);
} }
@ -60,7 +60,7 @@ ResourceLocator.prototype.getTemplateUrls = function(username, resourcePath) {
} }
}; };
ResourceLocator.prototype.getUrls = function(username, resourcePath) { ResourceLocator.prototype.getUrls = function (username, resourcePath) {
if (this.resourcesUrlTemplates) { if (this.resourcesUrlTemplates) {
return this.getUrlsFromTemplate(username, new Resource(resourcePath)); return this.getUrlsFromTemplate(username, new Resource(resourcePath));
} }
@ -105,7 +105,7 @@ ResourceLocator.prototype.urlForTemplate = function (tpl, username, cdnDomain, r
} }
}; };
ResourceLocator.prototype.getUrlsFromTemplate = function(username, resource, templated) { ResourceLocator.prototype.getUrlsFromTemplate = function (username, resource, templated) {
var urls = {}; var urls = {};
var cdnDomain = getCdnDomain(this.environment.serverMetadata, resource) || {}; var cdnDomain = getCdnDomain(this.environment.serverMetadata, resource) || {};
if (this.resourcesUrlTemplates.http) { if (this.resourcesUrlTemplates.http) {
@ -198,11 +198,11 @@ class TemplateResource extends Resource {
} }
} }
function getUrl(baseUrl, username, path) { function getUrl (baseUrl, username, path) {
return `${baseUrl}/${username}/api/v1/map/${path}`; return `${baseUrl}/${username}/api/v1/map/${path}`;
} }
function getCdnUrls(serverMetadata, username, resource) { function getCdnUrls (serverMetadata, username, resource) {
if (serverMetadata && serverMetadata.cdn_url) { if (serverMetadata && serverMetadata.cdn_url) {
var cdnUrl = serverMetadata.cdn_url; var cdnUrl = serverMetadata.cdn_url;
var httpUrls = resource.getUrl(`http://${cdnUrl.http}`, username); var httpUrls = resource.getUrl(`http://${cdnUrl.http}`, username);
@ -214,13 +214,13 @@ function getCdnUrls(serverMetadata, username, resource) {
} }
return { return {
http: httpUrls, http: httpUrls,
https: httpsUrls, https: httpsUrls
}; };
} }
return null; return null;
} }
function getCdnDomain(serverMetadata, resource) { function getCdnDomain (serverMetadata, resource) {
if (serverMetadata && serverMetadata.cdn_url) { if (serverMetadata && serverMetadata.cdn_url) {
var cdnUrl = serverMetadata.cdn_url; var cdnUrl = serverMetadata.cdn_url;
var httpDomain = resource.getDomain(cdnUrl.http); var httpDomain = resource.getDomain(cdnUrl.http);
@ -236,36 +236,36 @@ function getCdnDomain(serverMetadata, resource) {
} }
return { return {
http: httpDomain, http: httpDomain,
https: httpsDomain, https: httpsDomain
}; };
} }
return null; return null;
} }
// ref https://jsperf.com/js-crc32 // ref https://jsperf.com/js-crc32
function crcTable() { function crcTable () {
var c; var c;
var table = []; var table = [];
for (var n = 0; n < 256; n++) { for (var n = 0; n < 256; n++) {
c = n; c = n;
for (var k = 0; k < 8; k++) { for (var k = 0; k < 8; k++) {
c = ((c & 1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1)); c = ((c & 1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1));
} }
table[n] = c; table[n] = c;
} }
return table; return table;
} }
var CRC_TABLE = crcTable(); var CRC_TABLE = crcTable();
function crc32(str) { function crc32 (str) {
var crc = 0 ^ (-1); var crc = 0 ^ (-1);
for (var i = 0; i < str.length; i++) { for (var i = 0; i < str.length; i++) {
crc = (crc >>> 8) ^ CRC_TABLE[(crc ^ str.charCodeAt(i)) & 0xFF]; crc = (crc >>> 8) ^ CRC_TABLE[(crc ^ str.charCodeAt(i)) & 0xFF];
} }
return (crc ^ (-1)) >>> 0; return (crc ^ (-1)) >>> 0;
} }
function subdomain(subdomains, resource) { function subdomain (subdomains, resource) {
var index = crc32(resource) % subdomains.length; var index = crc32(resource) % subdomains.length;
return subdomains[index]; return subdomains[index];
} }

View File

@ -2,15 +2,14 @@
var fs = require('fs'); var fs = require('fs');
function HealthCheck(disableFile) { function HealthCheck (disableFile) {
this.disableFile = disableFile; this.disableFile = disableFile;
} }
module.exports = HealthCheck; module.exports = HealthCheck;
HealthCheck.prototype.check = function (callback) {
HealthCheck.prototype.check = function(callback) { fs.readFile(this.disableFile, function handleDisabledFile (err, data) {
fs.readFile(this.disableFile, function handleDisabledFile(err, data) {
var disabledError = null; var disabledError = null;
if (!err) { if (!err) {
disabledError = new Error(data || 'Unknown error'); disabledError = new Error(data || 'Unknown error');

View File

@ -2,11 +2,10 @@
var HealthCheck = require('./monitoring/health-check'); var HealthCheck = require('./monitoring/health-check');
var WELCOME_MSG = "This is the CartoDB Maps API, " + var WELCOME_MSG = 'This is the CartoDB Maps API, ' +
"see the documentation at http://docs.cartodb.com/cartodb-platform/maps-api.html"; 'see the documentation at http://docs.cartodb.com/cartodb-platform/maps-api.html';
function ServerInfoController (versions) {
function ServerInfoController(versions) {
this.healthConfig = global.environment.health || {}; this.healthConfig = global.environment.health || {};
this.healthCheck = new HealthCheck(global.environment.disabled_file); this.healthCheck = new HealthCheck(global.environment.disabled_file);
this.versions = versions || {}; this.versions = versions || {};
@ -14,24 +13,24 @@ function ServerInfoController(versions) {
module.exports = ServerInfoController; module.exports = ServerInfoController;
ServerInfoController.prototype.route = function(monitorRouter) { ServerInfoController.prototype.route = function (monitorRouter) {
monitorRouter.get('/health', this.health.bind(this)); monitorRouter.get('/health', this.health.bind(this));
monitorRouter.get('/', this.welcome.bind(this)); monitorRouter.get('/', this.welcome.bind(this));
monitorRouter.get('/version', this.version.bind(this)); monitorRouter.get('/version', this.version.bind(this));
}; };
ServerInfoController.prototype.welcome = function(req, res) { ServerInfoController.prototype.welcome = function (req, res) {
res.status(200).send(WELCOME_MSG); res.status(200).send(WELCOME_MSG);
}; };
ServerInfoController.prototype.version = function(req, res) { ServerInfoController.prototype.version = function (req, res) {
res.status(200).send(this.versions); res.status(200).send(this.versions);
}; };
ServerInfoController.prototype.health = function(req, res) { ServerInfoController.prototype.health = function (req, res) {
if (!!this.healthConfig.enabled) { if (this.healthConfig.enabled) {
var startTime = Date.now(); var startTime = Date.now();
this.healthCheck.check(function(err) { this.healthCheck.check(function (err) {
var ok = !err; var ok = !err;
var response = { var response = {
enabled: true, enabled: true,
@ -42,9 +41,8 @@ ServerInfoController.prototype.health = function(req, res) {
response.err = err.message; response.err = err.message;
} }
res.status(ok ? 200 : 503).send(response); res.status(ok ? 200 : 503).send(response);
}); });
} else { } else {
res.status(200).send({enabled: false, ok: true}); res.status(200).send({ enabled: false, ok: true });
} }
}; };

Some files were not shown because too many files have changed in this diff Show More