diff --git a/NEWS.md b/NEWS.md index ae75f88a..8b2aa565 100644 --- a/NEWS.md +++ b/NEWS.md @@ -5,6 +5,8 @@ Released 2018-mm-dd - Upgrades Camshaft to [0.62.2](https://github.com/CartoDB/camshaft/releases/tag/0.61.11): - Build query from node's cache to compute output columns when building analysis +- Remove use of `step` module to handle asynchronous code, now it's defined as development dependency. + ## 6.3.0 Released 2018-07-26 diff --git a/lib/cartodb/backends/dataview.js b/lib/cartodb/backends/dataview.js index 4eebb2a2..542bbb4e 100644 --- a/lib/cartodb/backends/dataview.js +++ b/lib/cartodb/backends/dataview.js @@ -1,7 +1,5 @@ -var assert = require('assert'); var _ = require('underscore'); var PSQL = require('cartodb-psql'); -var step = require('step'); var BBoxFilter = require('../models/filter/bbox'); var DataviewFactory = require('../models/dataview/factory'); var DataviewFactoryWithOverviews = require('../models/dataview/overviews/factory'); @@ -21,53 +19,76 @@ function DataviewBackend(analysisBackend) { module.exports = DataviewBackend; DataviewBackend.prototype.getDataview = function (mapConfigProvider, user, params, callback) { + const dataviewName = params.dataviewName; - var dataviewName = params.dataviewName; - step( - function getMapConfig() { - mapConfigProvider.getMapConfig(this); - }, - function runDataviewQuery(err, mapConfig) { - assert.ifError(err); + mapConfigProvider.getMapConfig(function (err, mapConfig) { + if (err) { + return callback(err); + } - var dataviewDefinition = getDataviewDefinition(mapConfig.obj(), dataviewName); - if (!dataviewDefinition) { - throw new Error("Dataview '" + dataviewName + "' does not exists"); - } + var dataviewDefinition = getDataviewDefinition(mapConfig.obj(), dataviewName); + if (!dataviewDefinition) { + const error = new Error(`Dataview '${dataviewName}' does not exists`); + error.type = 'dataview'; + error.http_status = 400; + return callback(error); + } + if (!validFilterParams(params)) { + const error = new Error('Both own_filter and no_filters cannot be sent in the same request'); + error.type = 'dataview'; + error.http_status = 400; + return callback(error); + } + + var pg; + var overrideParams; + var dataview; + + try { + pg = new PSQL(dbParamsFromReqParams(params)); + var query = getQueryWithFilters(dataviewDefinition, params); + var queryRewriteData = getQueryRewriteData(mapConfig, dataviewDefinition, params); + var dataviewFactory = DataviewFactoryWithOverviews.getFactory(overviewsQueryRewriter, queryRewriteData, { + bbox: params.bbox + }); + dataview = dataviewFactory.getDataview(query, dataviewDefinition); var ownFilter = +params.own_filter; - var noFilters = +params.no_filters; - if (Number.isFinite(ownFilter) && Number.isFinite(noFilters)) { - err = new Error(); - err.message = 'Both own_filter and no_filters cannot be sent in the same request'; - err.type = 'dataview'; - err.http_status = 400; + overrideParams = getOverrideParams(params, !!ownFilter); + } catch (error) { + return callback(error); + } + + dataview.getResult(pg, overrideParams, function (err, dataviewResult) { + if (err) { return callback(err); } - var pg = new PSQL(dbParamsFromReqParams(params)); - - var query = getDataviewQuery(dataviewDefinition, ownFilter, noFilters); - if (params.bbox) { - var bboxFilter = new BBoxFilter({column: 'the_geom_webmercator', srid: 3857}, {bbox: params.bbox}); - query = bboxFilter.sql(query); - } - - var queryRewriteData = getQueryRewriteData(mapConfig, dataviewDefinition, params); - - var dataviewFactory = DataviewFactoryWithOverviews.getFactory( - overviewsQueryRewriter, queryRewriteData, { bbox: params.bbox } - ); - - var dataview = dataviewFactory.getDataview(query, dataviewDefinition); - dataview.getResult(pg, getOverrideParams(params, !!ownFilter), this); - }, - function returnCallback(err, result) { - return callback(err, result); - } - ); + return callback(null, dataviewResult); + }); + }); }; +function validFilterParams (params) { + var ownFilter = +params.own_filter; + var noFilters = +params.no_filters; + + return !(Number.isFinite(ownFilter) && Number.isFinite(noFilters)); +} + +function getQueryWithFilters (dataviewDefinition, params) { + var ownFilter = +params.own_filter; + var noFilters = +params.no_filters; + var query = getDataviewQuery(dataviewDefinition, ownFilter, noFilters); + + if (params.bbox) { + var bboxFilter = new BBoxFilter({column: 'the_geom_webmercator', srid: 3857}, {bbox: params.bbox}); + query = bboxFilter.sql(query); + } + + return query; +} + function getDataviewQuery(dataviewDefinition, ownFilter, noFilters) { if (noFilters) { return dataviewDefinition.sql.no_filters; @@ -129,41 +150,56 @@ function getOverrideParams(params, ownFilter) { } DataviewBackend.prototype.search = function (mapConfigProvider, user, dataviewName, params, callback) { - step( - function getMapConfig() { - mapConfigProvider.getMapConfig(this); - }, - function runDataviewSearchQuery(err, mapConfig) { - assert.ifError(err); - - var dataviewDefinition = getDataviewDefinition(mapConfig.obj(), dataviewName); - if (!dataviewDefinition) { - throw new Error("Dataview '" + dataviewName + "' does not exists"); - } - - var pg = new PSQL(dbParamsFromReqParams(params)); - - var ownFilter = +params.own_filter; - ownFilter = !!ownFilter; - - var query = (ownFilter) ? dataviewDefinition.sql.own_filter_on : dataviewDefinition.sql.own_filter_off; - - if (params.bbox) { - var bboxFilter = new BBoxFilter({column: 'the_geom', srid: 4326}, {bbox: params.bbox}); - query = bboxFilter.sql(query); - } - - var userQuery = params.q; - - var dataview = DataviewFactory.getDataview(query, dataviewDefinition); - dataview.search(pg, userQuery, this); - }, - function returnCallback(err, result) { - return callback(err, result); + mapConfigProvider.getMapConfig(function (err, mapConfig) { + if (err) { + return callback(err); } - ); + + var dataviewDefinition = getDataviewDefinition(mapConfig.obj(), dataviewName); + if (!dataviewDefinition) { + const error = new Error(`Dataview '${dataviewName}' does not exists`); + error.type = 'dataview'; + error.http_status = 400; + return callback(error); + } + + var pg; + var query; + var dataview; + var userQuery = params.q; + + try { + pg = new PSQL(dbParamsFromReqParams(params)); + query = getQueryWithOwnFilters(dataviewDefinition, params); + dataview = DataviewFactory.getDataview(query, dataviewDefinition); + } catch (error) { + return callback(error); + } + + dataview.search(pg, userQuery, function (err, result) { + if (err) { + return callback(err); + } + + return callback(null, result); + }); + }); }; +function getQueryWithOwnFilters (dataviewDefinition, params) { + var ownFilter = +params.own_filter; + ownFilter = !!ownFilter; + + var query = (ownFilter) ? dataviewDefinition.sql.own_filter_on : dataviewDefinition.sql.own_filter_off; + + if (params.bbox) { + var bboxFilter = new BBoxFilter({ column: 'the_geom', srid: 4326 }, { bbox: params.bbox }); + query = bboxFilter.sql(query); + } + + return query; +} + function getDataviewDefinition(mapConfig, dataviewName) { var dataviews = mapConfig.dataviews || {}; return dataviews[dataviewName]; diff --git a/lib/cartodb/backends/filter-stats.js b/lib/cartodb/backends/filter-stats.js index ba427405..b37d8704 100644 --- a/lib/cartodb/backends/filter-stats.js +++ b/lib/cartodb/backends/filter-stats.js @@ -1,5 +1,4 @@ var _ = require('underscore'); -var step = require('step'); var AnalysisFilter = require('../models/filter/analysis'); function FilterStatsBackends(pgQueryRunner) { @@ -24,36 +23,29 @@ function getEstimatedRows(pgQueryRunner, username, query, callback) { } FilterStatsBackends.prototype.getFilterStats = function (username, unfiltered_query, filters, callback) { - var stats = {}; - var self = this; - step( - function getUnfilteredRows() { - getEstimatedRows(self.pgQueryRunner, username, unfiltered_query, this); - }, - function receiveUnfilteredRows(err, rows) { - if (err){ - callback(err); - return; - } - stats.unfiltered_rows = rows; - this(null, rows); - }, - function getFilteredRows() { - if ( filters && !_.isEmpty(filters)) { - var analysisFilter = new AnalysisFilter(filters); - var query = analysisFilter.sql(unfiltered_query); - getEstimatedRows(self.pgQueryRunner, username, query, this); - } else { - this(null, null); - } - }, - function receiveFilteredRows(err, rows) { - if (err){ - callback(err); - return; - } - stats.filtered_rows = rows; - callback(null, stats); - } - ); + var stats = {}; + + getEstimatedRows(this.pgQueryRunner, username, unfiltered_query, (err, rows) => { + if (err){ + return callback(err); + } + + stats.unfiltered_rows = rows; + + if (!filters || _.isEmpty(filters)) { + return callback(null, stats); + } + + var analysisFilter = new AnalysisFilter(filters); + var query = analysisFilter.sql(unfiltered_query); + + getEstimatedRows(this.pgQueryRunner, username, query, (err, rows) => { + if (err){ + return callback(err); + } + + stats.filtered_rows = rows; + return callback(null, stats); + }); + }); }; diff --git a/lib/cartodb/backends/template_maps.js b/lib/cartodb/backends/template_maps.js index 75a482fe..41e655bd 100644 --- a/lib/cartodb/backends/template_maps.js +++ b/lib/cartodb/backends/template_maps.js @@ -1,7 +1,5 @@ -var assert = require('assert'); var crypto = require('crypto'); var debug = require('debug')('windshaft:templates'); -var step = require('step'); var _ = require('underscore'); var dot = require('dot'); @@ -69,27 +67,19 @@ TemplateMaps.prototype._userTemplateLimit = function() { * @param callback - function to pass results too. */ TemplateMaps.prototype._redisCmd = function(redisFunc, redisArgs, callback) { - var redisClient; - var that = this; - var db = that.db_signatures; + this.redis_pool.acquire(this.db_signatures, (err, redisClient) => { + if (err) { + return callback(err); + } - step( - function getRedisClient() { - that.redis_pool.acquire(db, this); - }, - function executeQuery(err, data) { - assert.ifError(err); - redisClient = data; - redisArgs.push(this); - redisClient[redisFunc.toUpperCase()].apply(redisClient, redisArgs); - }, - function releaseRedisClient(err, data) { - if ( ! _.isUndefined(redisClient) ) { - that.redis_pool.release(db, redisClient); - } - callback(err, data); - } - ); + redisClient[redisFunc.toUpperCase()](...redisArgs, (err, data) => { + this.redis_pool.release(this.db_signatures, redisClient); + if (err) { + return callback(err); + } + return callback(null, data); + }); + }); }; var _reValidNameIdentifier = /^[a-z0-9][0-9a-z_\-]*$/i; @@ -184,6 +174,37 @@ function templateDefaults(template) { }); } +/** + * Checks if the if the user reaches the templetes limit + * + * @param userTemplatesKey user templat key in Redis + * @param owner cartodb username of the template owner + * @param callback returns error if the user reaches the limit + */ +TemplateMaps.prototype._checkUserTemplatesLimit = function(userTemplatesKey, owner, callback) { + const limit = this._userTemplateLimit(); + + if(!limit) { + return callback(); + } + + this._redisCmd('HLEN', [userTemplatesKey], (err, numberOfTemplates) => { + if (err) { + return callback(err); + } + + if (numberOfTemplates >= limit) { + const limitReachedError = new Error( + `User '${owner}' reached limit on number of templates (${numberOfTemplates}/${limit})` + ); + limitReachedError.http_status = 409; + return callback(limitReachedError); + } + + return callback(); + }); +}; + //--------------- PUBLIC API ------------------------------------- // Add a template @@ -199,52 +220,41 @@ function templateDefaults(template) { // Return template identifier (only valid for given user) // TemplateMaps.prototype.addTemplate = function(owner, template, callback) { - var self = this; - template = templateDefaults(template); var invalidError = this._checkInvalidTemplate(template); - if ( invalidError ) { + if (invalidError) { return callback(invalidError); } - var templateName = template.name; - var userTemplatesKey = this.key_usr_tpl({ owner:owner }); - var limit = this._userTemplateLimit(); + var userTemplatesKey = this.key_usr_tpl({ owner }); - step( - function checkLimit() { - if ( ! limit ) { - return 0; - } - self._redisCmd('HLEN', [ userTemplatesKey ], this); - }, - function installTemplateIfDoesNotExist(err, numberOfTemplates) { - assert.ifError(err); - if ( limit && numberOfTemplates >= limit ) { - var limitReachedError = new Error("User '" + owner + "' reached limit on number of templates (" + - numberOfTemplates + "/" + limit + ")"); - limitReachedError.http_status = 409; - throw limitReachedError; - } - self._redisCmd('HSETNX', [ userTemplatesKey, templateName, JSON.stringify(template) ], this); - }, - function validateInstallation(err, wasSet) { - assert.ifError(err); - if ( ! wasSet ) { - throw new Error("Template '" + templateName + "' of user '" + owner + "' already exists"); - } - - return true; - }, - function finish(err) { - if (!err) { - self.emit('add', owner, templateName, template); - } - - callback(err, templateName, template); + this._checkUserTemplatesLimit(userTemplatesKey, owner, err => { + if (err) { + return callback(err); } - ); + + let templateString; + try { + templateString = JSON.stringify(template); + } catch (error) { + return callback(error); + } + + this._redisCmd('HSETNX', [userTemplatesKey, template.name, templateString], (err, wasSet) => { + if (err) { + return callback(err); + } + + if (!wasSet) { + var templateExistsError = new Error(`Template '${template.name}' of user '${owner}' already exists`); + return callback(templateExistsError); + } + + this.emit('add', owner, template.name, template); + return callback(null, template.name, template); + }); + }); }; // Delete a template @@ -257,26 +267,18 @@ TemplateMaps.prototype.addTemplate = function(owner, template, callback) { // @param callback function(err) // TemplateMaps.prototype.delTemplate = function(owner, tpl_id, callback) { - var self = this; - step( - function deleteTemplate() { - self._redisCmd('HDEL', [ self.key_usr_tpl({ owner:owner }), tpl_id ], this); - }, - function handleDeletion(err, deleted) { - assert.ifError(err); - if (!deleted) { - throw new Error("Template '" + tpl_id + "' of user '" + owner + "' does not exist"); - } - return true; - }, - function finish(err) { - if (!err) { - self.emit('delete', owner, tpl_id); - } - - callback(err); + this._redisCmd('HDEL', [ this.key_usr_tpl({ owner:owner }), tpl_id ], (err, deleted) => { + if (err) { + return callback(err); } - ); + + if (!deleted) { + return callback(new Error(`Template '${tpl_id}' of user '${owner}' does not exist`)); + } + + this.emit('delete', owner, tpl_id); + return callback(); + }); }; // Update a template @@ -296,56 +298,58 @@ TemplateMaps.prototype.delTemplate = function(owner, tpl_id, callback) { // @param callback function(err) // TemplateMaps.prototype.updTemplate = function(owner, tpl_id, template, callback) { - - var self = this; - template = templateDefaults(template); var invalidError = this._checkInvalidTemplate(template); - - if ( invalidError ) { + if (invalidError) { return callback(invalidError); } - var templateName = template.name; - - if ( tpl_id !== templateName ) { - return callback(new Error("Cannot update name of a map template ('" + tpl_id + "' != '" + templateName + "')")); + if (tpl_id !== template.name) { + return callback(new Error(`Cannot update name of a map template ('${tpl_id}' != '${template.name}')`)); } - var userTemplatesKey = this.key_usr_tpl({ owner:owner }); + var userTemplatesKey = this.key_usr_tpl({ owner }); - var previousTemplate = null; + this._redisCmd('HGET', [userTemplatesKey, tpl_id], (err, beforeUpdateTemplate) => { + if (err) { + return callback(err); + } - step( - function getExistingTemplate() { - self._redisCmd('HGET', [ userTemplatesKey, tpl_id ], this); - }, - function updateTemplate(err, _currentTemplate) { - assert.ifError(err); - if (!_currentTemplate) { - throw new Error("Template '" + tpl_id + "' of user '" + owner + "' does not exist"); + if (!beforeUpdateTemplate) { + return callback(new Error(`Template '${tpl_id}' of user '${owner}' does not exist`)); + } + + let templateString; + try { + templateString = JSON.stringify(template); + } catch (error) { + return callback(error); + } + + this._redisCmd('HSET', [userTemplatesKey, template.name, templateString], (err, didSetNewField) => { + if (err) { + return callback(err); } - previousTemplate = _currentTemplate; - self._redisCmd('HSET', [ userTemplatesKey, templateName, JSON.stringify(template) ], this); - }, - function handleTemplateUpdate(err, didSetNewField) { - assert.ifError(err); + if (didSetNewField) { debug('New template created on update operation'); } - return true; - }, - function finish(err) { - if (!err) { - if (self.fingerPrint(JSON.parse(previousTemplate)) !== self.fingerPrint(template)) { - self.emit('update', owner, templateName, template); - } + + let beforeUpdateTemplateObject; + try { + beforeUpdateTemplateObject = JSON.parse(beforeUpdateTemplate); + } catch (error) { + return callback(error); } - callback(err, template); - } - ); + if (this.fingerPrint(beforeUpdateTemplateObject) !== this.fingerPrint(template)) { + this.emit('update', owner, template.name, template); + } + + return callback(null, template); + }); + }); }; // List user templates @@ -370,19 +374,20 @@ TemplateMaps.prototype.listTemplates = function(owner, callback) { // Return full template definition // TemplateMaps.prototype.getTemplate = function(owner, tpl_id, callback) { - var self = this; - step( - function getTemplate() { - self._redisCmd('HGET', [ self.key_usr_tpl({owner:owner}), tpl_id ], this); - }, - function parseTemplate(err, tpl_val) { - assert.ifError(err); - return JSON.parse(tpl_val); - }, - function finish(err, tpl) { - callback(err, tpl); + this._redisCmd('HGET', [this.key_usr_tpl({owner:owner}), tpl_id], (err, template) => { + if (err) { + return callback(err); } - ); + + let templateObject; + try { + templateObject = JSON.parse(template); + } catch (error) { + return callback(error); + } + + return callback(null, templateObject); + }); }; TemplateMaps.prototype.isAuthorized = function(template, authTokens) { diff --git a/lib/cartodb/backends/user-limits.js b/lib/cartodb/backends/user-limits.js index 519f1cd1..2dc70470 100644 --- a/lib/cartodb/backends/user-limits.js +++ b/lib/cartodb/backends/user-limits.js @@ -1,5 +1,3 @@ -var step = require('step'); - /** * * @param metadataBackend @@ -41,45 +39,38 @@ UserLimitsBackend.prototype.getRenderLimits = function (username, apiKey, callba }; UserLimitsBackend.prototype.getTimeoutRenderLimit = function (username, apiKey, callback) { - var self = this; - - step( - function isAuthorized() { - var next = this; - - if (!apiKey) { - return next(null, false); - } - - self.metadataBackend.getUserMapKey(username, function (err, userApiKey) { - if (err) { - return next(err); - } - - return next(null, userApiKey === apiKey); - }); - }, - function getUserTimeoutRenderLimits(err, authorized) { - var next = this; + isAuthorized(this.metadataBackend, username, apiKey, (err, authorized) => { + if (err) { + return callback(err); + } + this.metadataBackend.getUserTimeoutRenderLimits(username, (err, timeoutRenderLimit) => { if (err) { - return next(err); + return callback(err); } - self.metadataBackend.getUserTimeoutRenderLimits(username, function (err, timeoutRenderLimit) { - if (err) { - return next(err); - } - - next(null, { - render: authorized ? timeoutRenderLimit.render : timeoutRenderLimit.renderPublic - }); - }); - }, - callback - ); + return callback( + null, + { render: authorized ? timeoutRenderLimit.render : timeoutRenderLimit.renderPublic } + ); + }); + }); }; +function isAuthorized(metadataBackend, username, apiKey, callback) { + if (!apiKey) { + return callback(null, false); + } + + metadataBackend.getUserMapKey(username, function (err, userApiKey) { + if (err) { + return callback(err); + } + + return callback(null, userApiKey === apiKey); + }); +} + UserLimitsBackend.prototype.preprareRateLimit = function () { if (this.options.limits.rateLimitsEnabled) { this.metadataBackend.loadRateLimitsScript(); diff --git a/lib/cartodb/models/mapconfig/adapter/mapconfig-overviews-adapter.js b/lib/cartodb/models/mapconfig/adapter/mapconfig-overviews-adapter.js index a104c592..e67a2513 100644 --- a/lib/cartodb/models/mapconfig/adapter/mapconfig-overviews-adapter.js +++ b/lib/cartodb/models/mapconfig/adapter/mapconfig-overviews-adapter.js @@ -1,4 +1,3 @@ -var step = require('step'); var queue = require('queue-async'); var _ = require('underscore'); @@ -9,93 +8,119 @@ function MapConfigOverviewsAdapter(overviewsMetadataBackend, filterStatsBackend) module.exports = MapConfigOverviewsAdapter; -MapConfigOverviewsAdapter.prototype.getMapConfig = function(user, requestMapConfig, params, context, callback) { - var self = this; - - var layers = requestMapConfig.layers; - var analysesResults = context.analysesResults; - - if (!layers || layers.length === 0) { - return callback(null, requestMapConfig); - } - - var augmentLayersQueue = queue(layers.length); - - function augmentLayer(layer, done) { - if ( layer.type !== 'mapnik' && layer.type !== 'cartodb' ) { - return done(null, layer); - } - self.overviewsMetadataBackend.getOverviewsMetadata(user, layer.options.sql, function(err, metadata){ - if (err) { - done(err, layer); - } else { - var query_rewrite_data = { overviews: metadata }; - step( - function collectFiltersData() { - var filters, unfiltered_query; - if ( layer.options.source && analysesResults && !layer.options.sql_wrap) { - var sourceId = layer.options.source.id; - var node = _.find(analysesResults, function(a){ return a.rootNode.params.id === sourceId; }); - if ( node ) { - node = node.rootNode; - filters = node.getFilters(); - var filters_disabler = Object.keys(filters).reduce( - function(disabler, filter_id){ disabler[filter_id] = false; return disabler; }, - {} - ); - unfiltered_query = node.getQuery(filters_disabler); - query_rewrite_data.filters = filters; - query_rewrite_data.unfiltered_query = unfiltered_query; - } - } - this(null, filters, unfiltered_query); - }, - function collectStatsData(err, filters, unfiltered_query) { - var next_step = this; - if ( filters ) { - self.filterStatsBackend.getFilterStats( - user, - unfiltered_query, filters, - function(err, stats) { - if ( !err ) { - query_rewrite_data.filter_stats = stats; - } - return next_step(err); - } - ); - } else { - return next_step(null); - } - }, - function addDataToLayer(err) { - if ( !err && !_.isEmpty(metadata) ) { - layer = _.extend({}, layer); - layer.options = _.extend({}, layer.options, { query_rewrite_data: query_rewrite_data }); - } - done(null, layer); - } - ); - } - }); - } - - function layersAugmentQueueFinish(err, layers) { - if (err) { - return callback(err); - } +MapConfigOverviewsAdapter.prototype.getMapConfig = function (user, requestMapConfig, params, context, callback) { + var layers = requestMapConfig.layers; + var analysesResults = context.analysesResults; if (!layers || layers.length === 0) { - return callback(new Error('Missing layers array from layergroup config')); + return callback(null, requestMapConfig); } - requestMapConfig.layers = layers; + var augmentLayersQueue = queue(layers.length); - return callback(null, requestMapConfig); - } + layers.forEach(layer => augmentLayersQueue.defer(this._augmentLayer.bind(this), user, layer, analysesResults)); - layers.forEach(function(layer) { - augmentLayersQueue.defer(augmentLayer, layer); - }); - augmentLayersQueue.awaitAll(layersAugmentQueueFinish); + augmentLayersQueue.awaitAll(function layersAugmentQueueFinish (err, layers) { + if (err) { + return callback(err); + } + if (!layers || layers.length === 0) { + return callback(new Error('Missing layers array from layergroup config')); + } + + requestMapConfig.layers = layers; + + return callback(null, requestMapConfig); + }); }; + +MapConfigOverviewsAdapter.prototype._augmentLayer = function (user, layer, analysesResults, callback) { + if (layer.type !== 'mapnik' && layer.type !== 'cartodb') { + return callback(null, layer); + } + + this.overviewsMetadataBackend.getOverviewsMetadata(user, layer.options.sql, (err, metadata) => { + if (err) { + return callback(err, layer); + } + + if (_.isEmpty(metadata)) { + return callback(null, layer); + } + + var filters = getFilters(analysesResults, layer); + + if (!filters) { + layer.options = Object.assign({}, layer.options, getQueryRewriteData(layer, analysesResults, { + overviews: metadata + })); + + return callback(null, layer); + } + + var unfilteredQuery = getUnfilteredQuery(analysesResults, layer); + + this.filterStatsBackend.getFilterStats(user, unfilteredQuery, filters, function (err, stats) { + if (err) { + return callback(null, layer); + } + + layer.options = Object.assign({}, layer.options, getQueryRewriteData(layer, analysesResults, { + overviews: metadata, + filter_stats: stats + })); + + return callback(null, layer); + }); + }); +}; + +function getRootNode (analysesResults, sourceId) { + var node = _.find(analysesResults, function (a) { + return a.rootNode.params.id === sourceId; + }); + + return node ? node.rootNode : undefined; +} + +function getFilters (analysesResults, layer) { + if (layer.options.source && analysesResults && !layer.options.sql_wrap) { + var sourceId = layer.options.source.id; + var node = getRootNode(analysesResults, sourceId); + + if (node) { + return node.getFilters(); + } + } +} + +function getUnfilteredQuery (analysesResults, layer) { + if (layer.options.source && analysesResults && !layer.options.sql_wrap) { + var sourceId = layer.options.source.id; + var node = getRootNode(analysesResults, sourceId); + + if (node) { + var filters = node.getFilters(); + var filters_disabler = Object.keys(filters).reduce(function (disabler, filter_id) { + disabler[filter_id] = false; + return disabler; + }, {}); + + return node.getQuery(filters_disabler); + } + } +} + +function getQueryRewriteData (layer, analysesResults, extend = {}) { + var queryRewriteData = {}; + + if (layer.options.source && analysesResults && !layer.options.sql_wrap) { + queryRewriteData.filters = getFilters(analysesResults, layer); + queryRewriteData.unfiltered_query = getUnfilteredQuery(analysesResults, layer); + } + + queryRewriteData = Object.assign({}, queryRewriteData, extend); + + return { query_rewrite_data: queryRewriteData }; +} diff --git a/package.json b/package.json index 10ddb8cb..e85af46a 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,6 @@ "redis-mpool": "0.5.0", "request": "2.87.0", "semver": "5.5.0", - "step": "1.0.0", "step-profiler": "0.3.0", "turbo-carto": "0.20.4", "underscore": "1.6.0", @@ -59,6 +58,7 @@ "moment": "2.22.1", "nock": "9.2.6", "redis": "2.8.0", + "step": "1.0.0", "strftime": "0.10.0" }, "scripts": {