From 077f19d5061db24bea16e6e7340f83885c144253 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa=20Aubert?= Date: Mon, 4 Dec 2017 12:40:53 +0100 Subject: [PATCH] Integrate aggregation and get metadata for layergroup --- lib/cartodb/controllers/map.js | 27 ++++- .../models/aggregation/aggregation-proxy.js | 6 +- .../adapter/aggregation-mapconfig-adapter.js | 26 +++-- test/acceptance/aggregation.js | 99 +++++++++++++++++++ 4 files changed, 145 insertions(+), 13 deletions(-) create mode 100644 test/acceptance/aggregation.js diff --git a/lib/cartodb/controllers/map.js b/lib/cartodb/controllers/map.js index 6b50fc47..1689fbd1 100644 --- a/lib/cartodb/controllers/map.js +++ b/lib/cartodb/controllers/map.js @@ -87,6 +87,7 @@ MapController.prototype.composeCreateMapMiddleware = function (useTemplate = fal this.setDataviewsAndWidgetsUrlsToLayergroupMetadata(), this.setAnalysesMetadataToLayergroup(includeQuery), this.setTurboCartoMetadataToLayergroup(), + this.setAggregationMetadataToLayergroup(), this.setSurrogateKeyHeader(), this.sendResponse(), this.augmentError({ label, addContext }) @@ -540,13 +541,13 @@ MapController.prototype.setTurboCartoMetadataToLayergroup = function () { return function setTurboCartoMetadataToLayergroupMiddleware (req, res, next) { const { layergroup, mapconfig, context } = res.locals; - addContextMetadata(layergroup, mapconfig.obj(), context); + addTurboCartoContextMetadata(layergroup, mapconfig.obj(), context); next(); }; }; -function addContextMetadata(layergroup, mapConfig, context) { +function addTurboCartoContextMetadata(layergroup, mapConfig, context) { if (layergroup.metadata && Array.isArray(layergroup.metadata.layers) && Array.isArray(mapConfig.layers)) { layergroup.metadata.layers = layergroup.metadata.layers.map(function(layer, layerIndex) { if (context.turboCarto && Array.isArray(context.turboCarto.layers)) { @@ -557,6 +558,28 @@ function addContextMetadata(layergroup, mapConfig, context) { } } +// TODO: see how evolve this function, it's a good candidate to be refactored +MapController.prototype.setAggregationMetadataToLayergroup = function () { + return function setAggregationMetadataToLayergroupMiddleware (req, res, next) { + const { layergroup, mapconfig, context } = res.locals; + + addAggregationContextMetadata(layergroup, mapconfig.obj(), context); + + next(); + }; +}; + +function addAggregationContextMetadata(layergroup, mapConfig, context) { + if (layergroup.metadata && Array.isArray(layergroup.metadata.layers) && Array.isArray(mapConfig.layers)) { + layergroup.metadata.layers = layergroup.metadata.layers.map(function(layer, layerIndex) { + if (context.aggregation && Array.isArray(context.aggregation.layers)) { + layer.meta.aggregation = context.aggregation.layers[layerIndex]; + } + return layer; + }); + } +} + MapController.prototype.setSurrogateKeyHeader = function () { return function setSurrogateKeyHeaderMiddleware(req, res, next) { const { affectedTables, user, templateName } = res.locals; diff --git a/lib/cartodb/models/aggregation/aggregation-proxy.js b/lib/cartodb/models/aggregation/aggregation-proxy.js index 7fff63f9..9de82887 100644 --- a/lib/cartodb/models/aggregation/aggregation-proxy.js +++ b/lib/cartodb/models/aggregation/aggregation-proxy.js @@ -31,14 +31,14 @@ module.exports = class AggregationProxy { } _getAggregationType () { - if (this.mapconfig.isVetorLayergroup()) { + if (this.mapconfig.isVectorOnlyMapConfig()) { return VECTOR_AGGREGATION; } return RASTER_AGGREGATION; } - sql () { - return this.implementation.sql(); + sql (options) { + return this.implementation.sql(options); } }; diff --git a/lib/cartodb/models/mapconfig/adapter/aggregation-mapconfig-adapter.js b/lib/cartodb/models/mapconfig/adapter/aggregation-mapconfig-adapter.js index 4e7d229a..7b7468b7 100644 --- a/lib/cartodb/models/mapconfig/adapter/aggregation-mapconfig-adapter.js +++ b/lib/cartodb/models/mapconfig/adapter/aggregation-mapconfig-adapter.js @@ -3,11 +3,16 @@ const { MapConfig } = require('windshaft').model; module.exports = class AggregationMapConfigAdapter { getMapConfig (user, requestMapConfig, params, context, callback) { + this.mapConfig = new MapConfig(requestMapConfig); + if (!this._shouldAdaptLayers(requestMapConfig, params)) { return callback(null, requestMapConfig); } requestMapConfig.layers = this._adaptLayers(requestMapConfig); + context.aggregation = { + layers: this._getAggregationMetadata(requestMapConfig), + }; callback(null, requestMapConfig); } @@ -19,10 +24,8 @@ module.exports = class AggregationMapConfigAdapter { shouldAdapt = params.aggregation; } - const mapConfig = new MapConfig(requestMapConfig); - if (params.aggregation === undefined) { - if (mapConfig.isVectorOnlyMapConfig()) { + if (this.mapConfig.isVectorOnlyMapConfig()) { shouldAdapt = true; } else if (this._hasAggregation(requestMapConfig)){ shouldAdapt = true; @@ -44,18 +47,17 @@ module.exports = class AggregationMapConfigAdapter { _hasLayerAggregation (layer) { const { aggregation } = layer.options; - return aggregation !== undefined && (typeof aggregation === 'object' && typeof aggregation === 'boolean'); + return aggregation !== undefined && (typeof aggregation === 'object' || typeof aggregation === 'boolean'); } _adaptLayers (requestMapConfig) { return requestMapConfig.layers.map(layer => { if (this._hasLayerAggregation(layer)) { - const aggregation = new AggregationProxy(requestMapConfig, layer.options.aggregation); - - let aggregationSql = aggregation.sql(); - + const aggregation = new AggregationProxy(this.mapConfig, layer.options.aggregation); const sqlQueryWrap = layer.options.sql_wrap; + let aggregationSql = aggregation.sql(layer.options); + if (sqlQueryWrap) { layer.options.sql_raw = aggregationSql; aggregationSql = sqlQueryWrap.replace(/<%=\s*sql\s*%>/g, aggregationSql); @@ -67,4 +69,12 @@ module.exports = class AggregationMapConfigAdapter { return layer; }); } + + _getAggregationMetadata (requestMapConfig) { + return requestMapConfig.layers.map(layer => { + return this._hasLayerAggregation(layer) ? + { aggregated: true } : + { aggregated: false }; + }); + } }; diff --git a/test/acceptance/aggregation.js b/test/acceptance/aggregation.js new file mode 100644 index 00000000..eeee18f0 --- /dev/null +++ b/test/acceptance/aggregation.js @@ -0,0 +1,99 @@ +require('../support/test_helper'); + +const assert = require('../support/assert'); +const TestClient = require('../support/test-client'); +const serverOptions = require('../../lib/cartodb/server_options'); + +const suites = [{ + desc: 'mvt (mapnik)', + usePostGIS: false +}]; + +if (process.env.POSTGIS_VERSION === '2.4') { + suites.push({ + desc: 'mvt (postgis)', + usePostGIS: true + }); +} + +describe('aggregation', function () { + + const POINTS_SQL_1 = ` + select + st_setsrid(st_makepoint(x*10, x*10), 4326) as the_geom, + st_transform(st_setsrid(st_makepoint(x*10, x*10), 4326), 3857) as the_geom_webmercator, + x as value + from generate_series(-3, 3) x + `; + + const POINTS_SQL_2 = ` + select + st_setsrid(st_makepoint(x*10, x*10*(-1)), 4326) as the_geom, + st_transform(st_setsrid(st_makepoint(x*10, x*10*(-1)), 4326), 3857) as the_geom_webmercator, + x as value + from generate_series(-3, 3) x + `; + + function createVectorMapConfig (layers = [ + { + type: 'cartodb', + options: { + sql: POINTS_SQL_1, + aggregation: true + } + }, + { + type: 'cartodb', + options: { + sql: POINTS_SQL_2, + aggregation: true + } + } + ]) { + return { + version: '1.6.0', + layers: layers + }; + } + + suites.forEach((suite) => { + const { desc, usePostGIS } = suite; + + describe(desc, function () { + const originalUsePostGIS = serverOptions.renderer.mvt.usePostGIS; + + before(function () { + serverOptions.renderer.mvt.usePostGIS = usePostGIS; + }); + + after(function (){ + serverOptions.renderer.mvt.usePostGIS = originalUsePostGIS; + }); + + + beforeEach(function () { + this.mapConfig = createVectorMapConfig(); + this.testClient = new TestClient(this.mapConfig); + }); + + afterEach(function (done) { + this.testClient.drain(done); + }); + + it('should return a layergroup indicating that was aggregated', function (done) { + this.testClient.getLayergroup((err, body) => { + if (err) { + return done(err); + } + + assert.equal(typeof body.metadata, 'object'); + assert.ok(Array.isArray(body.metadata.layers)); + + body.metadata.layers.forEach(layer => assert.ok(layer.meta.aggregation.aggregated)); + + done(); + }); + }); + }); + }); +});