From 5bd30b6b5fd1d1369939ecfd6a300452ccd51740 Mon Sep 17 00:00:00 2001 From: Raul Ochoa Date: Thu, 17 Mar 2016 12:50:42 +0100 Subject: [PATCH] Analysis layers adapter skips analysis if there is only source nodes --- .../models/analysis_mapconfig_adapter.js | 36 ++++++++++ test/acceptance/analysis/analysis-layers.js | 67 +++++++++++++++--- test/fixtures/analysis/buffer-over-source.png | Bin 0 -> 9439 bytes 3 files changed, 95 insertions(+), 8 deletions(-) create mode 100644 test/fixtures/analysis/buffer-over-source.png diff --git a/lib/cartodb/models/analysis_mapconfig_adapter.js b/lib/cartodb/models/analysis_mapconfig_adapter.js index 99d34f5a..6c437849 100644 --- a/lib/cartodb/models/analysis_mapconfig_adapter.js +++ b/lib/cartodb/models/analysis_mapconfig_adapter.js @@ -1,4 +1,5 @@ var queue = require('queue-async'); +var debug = require('debug')('windshaft:analysis'); var camshaft = require('camshaft'); var dot = require('dot'); @@ -29,6 +30,32 @@ function layerQuery(query, columnNames) { return layerQueryTemplate({ _query: query, _columns: skipColumns(columnNames).join(', ') }); } +function replaceSourceRootQueries(requestMapConfig) { + var analysisToRemove = {}; + var analysisSourceRootsIds = requestMapConfig.analyses.reduce(function(sourceRootsIds, analysis, analysisIndex) { + if (analysis.type === 'source' && !!analysis.id) { + sourceRootsIds[analysis.id] = analysis; + analysisToRemove[analysisIndex] = true; + } + return sourceRootsIds; + }, {}); + + requestMapConfig.analyses = requestMapConfig.analyses.filter(function(analysis, index) { + return !analysisToRemove.hasOwnProperty(index); + }); + + requestMapConfig.layers = requestMapConfig.layers.map(function(layer) { + var sourceId = layer.options && layer.options.source && layer.options.source.id; + if (sourceId && analysisSourceRootsIds.hasOwnProperty(sourceId)) { + delete layer.options.source; + layer.options.sql = analysisSourceRootsIds[sourceId].params.query; + } + return layer; + }); + + return requestMapConfig; +} + function shouldAdaptLayers(requestMapConfig) { return Array.isArray(requestMapConfig.layers) && Array.isArray(requestMapConfig.analyses) && requestMapConfig.analyses.length > 0; @@ -36,7 +63,14 @@ function shouldAdaptLayers(requestMapConfig) { AnalysisMapConfigAdapter.prototype.getLayers = function(analysisConfiguration, requestMapConfig, callback) { + debug('mapconfig input', JSON.stringify(requestMapConfig, null, 4)); + + if (Array.isArray(requestMapConfig.analyses)) { + requestMapConfig = replaceSourceRootQueries(requestMapConfig); + } + if (!shouldAdaptLayers(requestMapConfig)) { + debug('mapconfig output', JSON.stringify(requestMapConfig, null, 4)); return callback(null, requestMapConfig); } @@ -77,6 +111,8 @@ AnalysisMapConfigAdapter.prototype.getLayers = function(analysisConfiguration, r return layer; }); + debug('mapconfig output', JSON.stringify(requestMapConfig, null, 4)); + return callback(null, requestMapConfig); }); }; diff --git a/test/acceptance/analysis/analysis-layers.js b/test/acceptance/analysis/analysis-layers.js index d78d0077..2d089cfc 100644 --- a/test/acceptance/analysis/analysis-layers.js +++ b/test/acceptance/analysis/analysis-layers.js @@ -53,11 +53,12 @@ describe('analysis-layers', function() { var DEFAULT_MULTITYPE_STYLE = cartocss(); - var TILE_ANALYSIS_TABLES = { z: 14, x: 8023, y: 6177 }; + var TILE_ANALYSIS_TABLES = { z: 0, x: 0, y: 0 }; var useCases = [ { desc: 'basic source-id mapnik layer', + fixture: 'basic-source-id-mapnik-layer.png', mapConfig: mapConfig( [ { @@ -67,8 +68,7 @@ describe('analysis-layers', function() { "id": "2570e105-7b37-40d2-bdf4-1af889598745" }, "cartocss": DEFAULT_MULTITYPE_STYLE, - "cartocss_version": "2.1.1", - "interactivity": [] + "cartocss_version": "2.3.0" } } ], @@ -82,8 +82,44 @@ describe('analysis-layers', function() { } } ] - ), - tile: { z: 0, x: 0, y: 0 } + ) + }, + + { + desc: 'buffer over source', + fixture: 'buffer-over-source.png', + tile: { z: 7, x: 61, y: 47 }, + mapConfig: mapConfig( + [ + { + "type": "cartodb", + "options": { + "source": { + "id": "HEAD" + }, + "cartocss": DEFAULT_MULTITYPE_STYLE, + "cartocss_version": "2.3.0" + } + } + ], + {}, + [ + { + "id": "HEAD", + "type": "buffer", + "params": { + "source": { + "id": "2570e105-7b37-40d2-bdf4-1af889598745", + "type": "source", + "params": { + "query": "select * from populated_places_simple_reduced" + } + }, + "radio": 50000 + } + } + ] + ) } ]; @@ -100,7 +136,7 @@ describe('analysis-layers', function() { // To generate images use: // image.save('/tmp/' + useCase.desc.replace(/\s/g, '-') + '.png'); - var fixturePath = './test/fixtures/analysis/basic-source-id-mapnik-layer.png'; + var fixturePath = './test/fixtures/analysis/' + useCase.fixture; assert.imageIsSimilarToFile(image, fixturePath, IMAGE_TOLERANCE_PER_MIL, function(err) { assert.ok(!err, err); @@ -110,12 +146,27 @@ describe('analysis-layers', function() { }); }); - it('should fail for non-authenticated requests', function(done) { + it('should NOT fail for non-authenticated requests when it is just source', function(done) { var useCase = useCases[0]; // No API key here var testClient = new TestClient(useCase.mapConfig); + testClient.getLayergroup(function(err, layergroupResult) { + assert.ok(!err, err); + + assert.equal(layergroupResult.metadata.layers.length, 1); + + testClient.drain(done); + }); + }); + + it('should fail for non-authenticated requests that has a node other than "source"', function(done) { + var useCase = useCases[1]; + + // No API key here + var testClient = new TestClient(useCase.mapConfig); + var PERMISSION_DENIED_RESPONSE = { status: 403, headers: { @@ -128,7 +179,7 @@ describe('analysis-layers', function() { assert.deepEqual(layergroupResult.errors, ["permission denied for relation cdb_tablemetadata"]); - done(); + testClient.drain(done); }); }); }); diff --git a/test/fixtures/analysis/buffer-over-source.png b/test/fixtures/analysis/buffer-over-source.png new file mode 100644 index 0000000000000000000000000000000000000000..06aeb50aea7fd2de65c6ceffa0296d37905f6216 GIT binary patch literal 9439 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