From 0bca3d6f337a6690867d6d92aeb7583802373978 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa=20Aubert?= Date: Mon, 18 Dec 2017 13:42:27 +0100 Subject: [PATCH] Validate placement, threshold and resolution --- .../aggregation/aggregation-map-config.js | 89 ++++++++++++ lib/cartodb/models/aggregation/aggregation.js | 8 ++ test/acceptance/aggregation.js | 129 ++++++++++++++++++ 3 files changed, 226 insertions(+) diff --git a/lib/cartodb/models/aggregation/aggregation-map-config.js b/lib/cartodb/models/aggregation/aggregation-map-config.js index e6524e3d..9b12722a 100644 --- a/lib/cartodb/models/aggregation/aggregation-map-config.js +++ b/lib/cartodb/models/aggregation/aggregation-map-config.js @@ -1,8 +1,13 @@ const MapConfig = require('windshaft').model.MapConfig; +const Aggregation = require('./aggregation'); module.exports = class AggregationMapConfig extends MapConfig { constructor (config, datasource) { super(config, datasource); + + this.validateResolution(); + this.validatePlacement(); + this.validateThreshold(); } isAggregationMapConfig () { @@ -31,4 +36,88 @@ module.exports = class AggregationMapConfig extends MapConfig { return aggregation !== undefined && (typeof aggregation === 'object' || typeof aggregation === 'boolean'); } + + getAggregation (index) { + if (!this.hasLayerAggregation(index)) { + return; + } + + const { aggregation } = this.getLayer(index).options; + + if (typeof aggregation === 'boolean') { + return {}; + } + + return aggregation; + } + + validateResolution () { + for (let index = 0; index < this.getLayers().length; index++) { + const aggregation = this.getAggregation(index); + + if (aggregation === undefined || aggregation.resolution === undefined) { + continue; + } + + const resolution = parseInt(aggregation.resolution, 10); + + if (!Number.isFinite(resolution) || resolution <= 0) { + const error = new Error(`Invalid resolution, should be a number greather than 0`); + error.type = 'layer'; + error.layer = { + id: this.getLayerId(index), + index: index, + type: this.layerType(index) + }; + + throw error; + } + } + } + + validatePlacement () { + for (let index = 0; index < this.getLayers().length; index++) { + const aggregation = this.getAggregation(index); + + if (aggregation === undefined || aggregation.placement === undefined) { + continue; + } + + if (!Aggregation.PLACEMENTS.includes(aggregation.placement)) { + const error = new Error(`Invalid placement. Valid values: ${Aggregation.PLACEMENTS.join(', ')}`); + error.type = 'layer'; + error.layer = { + id: this.getLayerId(index), + index: index, + type: this.layerType(index) + }; + + throw error; + } + } + } + + validateThreshold () { + for (let index = 0; index < this.getLayers().length; index++) { + const aggregation = this.getAggregation(index); + + if (aggregation === undefined || aggregation.threshold === undefined) { + continue; + } + + const threshold = parseInt(aggregation.threshold, 10); + + if (!Number.isFinite(threshold) || threshold <= 0) { + const error = new Error(`Invalid threshold, should be a number greather than 0`); + error.type = 'layer'; + error.layer = { + id: this.getLayerId(index), + index: index, + type: this.layerType(index) + }; + + throw error; + } + } + } }; diff --git a/lib/cartodb/models/aggregation/aggregation.js b/lib/cartodb/models/aggregation/aggregation.js index 2642898c..b916ffa2 100644 --- a/lib/cartodb/models/aggregation/aggregation.js +++ b/lib/cartodb/models/aggregation/aggregation.js @@ -5,6 +5,14 @@ module.exports = class Aggregation { return 1e5; // 100K } + static get PLACEMENTS() { + return [ + 'centroid', + 'point-grid', + 'point-sample' + ]; + } + constructor (mapconfig, query, { resolution = 1, threshold = Aggregation.THRESHOLD, diff --git a/test/acceptance/aggregation.js b/test/acceptance/aggregation.js index 561291dc..42990434 100644 --- a/test/acceptance/aggregation.js +++ b/test/acceptance/aggregation.js @@ -686,6 +686,135 @@ describe('aggregation', function () { done(); }); }); + + it('should fail with bad resolution', function (done) { + this.mapConfig = createVectorMapConfig([ + { + id: 'wadus', + type: 'cartodb', + options: { + sql: POINTS_SQL_1, + aggregation: { + resolution: 'wadus', + } + } + } + ]); + + this.testClient = new TestClient(this.mapConfig); + + const options = { + response: { + status: 400 + } + }; + + this.testClient.getLayergroup(options, (err, body) => { + if (err) { + return done(err); + } + + assert.deepEqual(body, { + errors: [ 'Invalid resolution, should be a number greather than 0' ], + errors_with_context:[{ + type: 'layer', + message: 'Invalid resolution, should be a number greather than 0', + layer: { + "id": "wadus", + "index": 0, + "type": "mapnik" + } + }] + }); + done(); + }); + }); + + it('should fail with bad placement', function (done) { + this.mapConfig = createVectorMapConfig([ + { + type: 'cartodb', + options: { + sql: POINTS_SQL_1, + aggregation: { + placement: 'wadus', + } + } + } + ]); + + this.testClient = new TestClient(this.mapConfig); + + const options = { + response: { + status: 400 + } + }; + + this.testClient.getLayergroup(options, (err, body) => { + if (err) { + return done(err); + } + + assert.deepEqual(body, { + errors: [ 'Invalid placement. Valid values: centroid, point-grid, point-sample'], + errors_with_context:[{ + type: 'layer', + message: 'Invalid placement. Valid values: centroid, point-grid, point-sample', + layer: { + id: "layer0", + index: 0, + type: "mapnik", + } + }] + }); + + done(); + }); + }); + + it('should fail with bad threshold', function (done) { + this.mapConfig = createVectorMapConfig([ + { + type: 'cartodb', + options: { + sql: POINTS_SQL_1, + aggregation: { + threshold: 'wadus', + } + } + } + ]); + + this.testClient = new TestClient(this.mapConfig); + + const options = { + response: { + status: 400 + } + }; + + this.testClient.getLayergroup(options, (err, body) => { + if (err) { + return done(err); + } + + assert.deepEqual(body, { + errors: [ 'Invalid threshold, should be a number greather than 0' ], + errors_with_context:[{ + type: 'layer', + message: 'Invalid threshold, should be a number greather than 0', + layer: { + "id": "layer0", + "index": 0, + "type": "mapnik" + } + }] + }); + + done(); + }); + }); }); }); });