Apply automatic eslint fixes
This commit is contained in:
parent
593d9e40f6
commit
4d70ac0894
75
app.js
75
app.js
@ -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));
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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();
|
||||||
|
@ -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 = {}) => {
|
||||||
|
@ -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
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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 {
|
||||||
|
@ -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]));
|
||||||
|
|
||||||
|
@ -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]+)'");
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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}`);
|
||||||
|
@ -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'));
|
||||||
}
|
}
|
||||||
|
@ -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}"`));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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();
|
||||||
};
|
};
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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.
|
||||||
|
@ -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());
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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));
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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) {
|
||||||
|
@ -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) {
|
||||||
|
@ -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];
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -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) {
|
||||||
|
@ -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();
|
||||||
|
@ -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';
|
||||||
}
|
}
|
||||||
|
@ -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',
|
||||||
|
@ -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'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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(
|
||||||
|
@ -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();
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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) => {
|
||||||
|
@ -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');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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];
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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, {});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -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';
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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),
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
function TorqueLayerStats() {
|
function TorqueLayerStats () {
|
||||||
this._types = {
|
this._types = {
|
||||||
torque: true
|
torque: true
|
||||||
};
|
};
|
||||||
|
@ -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 };
|
||||||
|
@ -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 => {
|
||||||
|
@ -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);
|
||||||
|
@ -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 {
|
||||||
|
@ -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) {
|
||||||
|
@ -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];
|
||||||
|
|
||||||
|
@ -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] || {};
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
4
lib/cache/backend/fastly.js
vendored
4
lib/cache/backend/fastly.js
vendored
@ -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);
|
||||||
};
|
};
|
||||||
|
6
lib/cache/backend/varnish-http.js
vendored
6
lib/cache/backend/varnish-http.js
vendored
@ -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'));
|
||||||
}
|
}
|
||||||
|
10
lib/cache/layergroup-affected-tables.js
vendored
10
lib/cache/layergroup-affected-tables.js
vendored
@ -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;
|
||||||
}
|
}
|
||||||
|
9
lib/cache/model/named-maps-entry.js
vendored
9
lib/cache/model/named-maps-entry.js
vendored
@ -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);
|
||||||
}
|
}
|
||||||
|
18
lib/cache/surrogate-keys-cache.js
vendored
18
lib/cache/surrogate-keys-cache.js
vendored
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
});
|
});
|
||||||
|
@ -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 = {
|
||||||
|
@ -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 };
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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) {
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -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
|
||||||
|
@ -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();
|
||||||
};
|
};
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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',
|
||||||
|
@ -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')
|
||||||
};
|
};
|
||||||
|
@ -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;
|
||||||
|
@ -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 {
|
||||||
|
@ -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({
|
||||||
|
@ -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(','),
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 {
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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';
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
|
@ -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];
|
||||||
}
|
}
|
||||||
|
@ -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');
|
||||||
|
@ -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
Loading…
Reference in New Issue
Block a user