From dab4b6d56b79baf2ea7585ada2d831ea1b13fa16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa=20Aubert?= Date: Thu, 10 Mar 2016 20:45:00 +0100 Subject: [PATCH] Implemented integration of turbo-cartocss for named maps --- lib/cartodb/controllers/map.js | 16 -- lib/cartodb/controllers/named_maps_admin.js | 24 ++- lib/cartodb/server.js | 2 +- .../utils/style/turbo-cartocss-adapter.js | 6 +- ...ss.js => turbo-cartocss-anonymous-maps.js} | 2 +- test/acceptance/turbo-cartocss-named-maps.js | 182 ++++++++++++++++++ 6 files changed, 210 insertions(+), 22 deletions(-) rename test/acceptance/{turbo-cartocss.js => turbo-cartocss-anonymous-maps.js} (98%) create mode 100644 test/acceptance/turbo-cartocss-named-maps.js diff --git a/lib/cartodb/controllers/map.js b/lib/cartodb/controllers/map.js index aac8679c..2f93caa0 100644 --- a/lib/cartodb/controllers/map.js +++ b/lib/cartodb/controllers/map.js @@ -255,22 +255,6 @@ MapController.prototype.instantiateTemplate = function(req, res, prepareParamsFn } ); }, - function parseTurboCartoCss(err, requestMapConfig, datasource) { - assert.ifError(err); - - var next = this; - self.turboCartoCssAdapter.getLayers(req.context.user, requestMapConfig.layers, function (err, layers) { - if (err) { - return next(err); - } - - if (layers) { - requestMapConfig.layers = layers; - } - - return next(null, requestMapConfig, datasource); - }); - }, function createLayergroup(err, mapConfig_, rendererParams) { assert.ifError(err); mapConfig = mapConfig_; diff --git a/lib/cartodb/controllers/named_maps_admin.js b/lib/cartodb/controllers/named_maps_admin.js index cd17b5c7..5a644ea5 100644 --- a/lib/cartodb/controllers/named_maps_admin.js +++ b/lib/cartodb/controllers/named_maps_admin.js @@ -15,11 +15,12 @@ var userMiddleware = require('../middleware/user'); * @param {TemplateMaps} templateMaps * @constructor */ -function NamedMapsAdminController(authApi, pgConnection, templateMaps) { +function NamedMapsAdminController(authApi, pgConnection, templateMaps, turboCartoCssAdapter) { BaseController.call(this, authApi, pgConnection); this.authApi = authApi; this.templateMaps = templateMaps; + this.turboCartoCssAdapter = turboCartoCssAdapter; } util.inherits(NamedMapsAdminController, BaseController); @@ -44,11 +45,30 @@ NamedMapsAdminController.prototype.create = function(req, res) { function checkPerms(){ self.authApi.authorizedByAPIKey(cdbuser, req, this); }, - function addTemplate(err, authenticated) { + function parseTurboCartoCss(err, authenticated) { assert.ifError(err); + ifUnauthenticated(authenticated, 'Only authenticated users can get template maps'); ifInvalidContentType(req, 'template POST data must be of type application/json'); + + var next = this; var cfg = req.body; + + self.turboCartoCssAdapter.getLayers(req.context.user, cfg.layergroup.layers, function (err, layers) { + if (err) { + return next(err); + } + + if (layers) { + cfg.layergroup.layers = layers; + } + + return next(null, cfg); + }); + }, + function addTemplate(err, cfg) { + assert.ifError(err); + self.templateMaps.addTemplate(cdbuser, cfg, this); }, function prepareResponse(err, tpl_id){ diff --git a/lib/cartodb/server.js b/lib/cartodb/server.js index ac1f185d..dd599485 100644 --- a/lib/cartodb/server.js +++ b/lib/cartodb/server.js @@ -196,7 +196,7 @@ module.exports = function(serverOptions) { metadataBackend ).register(app); - new controller.NamedMapsAdmin(authApi, pgConnection, templateMaps).register(app); + new controller.NamedMapsAdmin(authApi, pgConnection, templateMaps, turboCartocssAdapter).register(app); new controller.ServerInfo().register(app); diff --git a/lib/cartodb/utils/style/turbo-cartocss-adapter.js b/lib/cartodb/utils/style/turbo-cartocss-adapter.js index 44500c9e..7f181c4e 100644 --- a/lib/cartodb/utils/style/turbo-cartocss-adapter.js +++ b/lib/cartodb/utils/style/turbo-cartocss-adapter.js @@ -32,7 +32,9 @@ TurboCartocssAdapter.prototype.getLayers = function (username, layers, callback) TurboCartocssAdapter.prototype._parseCartoCss = function (username, layer, callback) { if (isNotLayerToParseCartocss(layer)) { - return callback(null, layer); + return process.nextTick(function () { + callback(null, layer); + }); } this.turboCartocssParser.process(username, layer.options.cartocss, layer.options.sql, function (err, cartocss) { @@ -46,7 +48,7 @@ TurboCartocssAdapter.prototype._parseCartoCss = function (username, layer, callb }; function isNotLayerToParseCartocss(layer) { - if ( layer.type !== 'mapnik' && layer.type !== 'cartodb' && layer.type !== 'torque' ) { + if (!layer || !layer.options || !layer.options.cartocss || !layer.options.sql) { return true; } diff --git a/test/acceptance/turbo-cartocss.js b/test/acceptance/turbo-cartocss-anonymous-maps.js similarity index 98% rename from test/acceptance/turbo-cartocss.js rename to test/acceptance/turbo-cartocss-anonymous-maps.js index ae2de717..32134d94 100644 --- a/test/acceptance/turbo-cartocss.js +++ b/test/acceptance/turbo-cartocss-anonymous-maps.js @@ -38,7 +38,7 @@ function makeMapconfig(cartocss) { }; } -describe('turbo-cartocss', function() { +describe('turbo-cartocss for anonymous maps', function() { describe('parsing ramp function with colorbrewer for greens and mapnik renderer', function () { beforeEach(function () { var turboCartocss = '#layer { marker-fill: ramp([price], colorbrewer(Greens)); }'; diff --git a/test/acceptance/turbo-cartocss-named-maps.js b/test/acceptance/turbo-cartocss-named-maps.js new file mode 100644 index 00000000..6e277520 --- /dev/null +++ b/test/acceptance/turbo-cartocss-named-maps.js @@ -0,0 +1,182 @@ +var assert = require('../support/assert'); +var step = require('step'); +var LayergroupToken = require('../../lib/cartodb/models/layergroup_token'); +var testHelper = require(__dirname + '/../support/test_helper'); +var CartodbWindshaft = require(__dirname + '/../../lib/cartodb/server'); +var serverOptions = require(__dirname + '/../../lib/cartodb/server_options'); +var server = new CartodbWindshaft(serverOptions); + +describe('turbo-cartocss for named maps', function() { + + var keysToDelete; + + beforeEach(function() { + keysToDelete = {}; + }); + + afterEach(function(done) { + testHelper.deleteRedisKeys(keysToDelete, done); + }); + + var expectedCartocss = [ + '#layer {', + ' marker-allow-overlap:true;', + ' marker-fill:#fee5d9;', + ' [ price > 10.25 ] { marker-fill:#fcae91}', + ' [ price > 10.75 ] { marker-fill:#fb6a4a}', + ' [ price > 11.5 ] { marker-fill:#de2d26}', + ' [ price > 16.5 ] { marker-fill:#a50f15}', + '}' + ].join(''); + + var templateId = 'turbo-cartocss-template-1'; + + var template = { + version: '0.0.1', + name: templateId, + auth: { method: 'open' }, + layergroup: { + version: '1.0.0', + layers: [{ + options: { + sql: [ + 'SELECT test_table.*, _prices.price FROM test_table JOIN (' + + ' SELECT 1 AS cartodb_id, 10.00 AS price', + ' UNION', + ' SELECT 2, 10.50', + ' UNION', + ' SELECT 3, 11.00', + ' UNION', + ' SELECT 4, 12.00', + ' UNION', + ' SELECT 5, 21.00', + ') _prices ON _prices.cartodb_id = test_table.cartodb_id' + ].join('\n'), + cartocss: [ + '#layer {' + + ' marker-fill: ramp([price], colorbrewer(Reds));' + + ' marker-allow-overlap:true;' + + '}' + ].join(''), + cartocss_version: '2.0.2' + } + } + ] + } + }; + + var layergroup = { + version: '1.3.0', + layers: [{ + type: 'named', + options: { + name: templateId, + } + }] + }; + + it('should create a template with turbo-cartocss parsed properly', function (done) { + step( + function postTemplate() { + var next = this; + + assert.response(server, { + url: '/api/v1/map/named?api_key=1234', + method: 'POST', + headers: { host: 'localhost', 'Content-Type': 'application/json' }, + data: JSON.stringify(template) + }, {}, + function (res, err) { + next(err, res); + }); + }, + function checkTemplate(err, res) { + assert.ifError(err); + assert.equal(res.statusCode, 200); + assert.deepEqual(JSON.parse(res.body), { + template_id: templateId + }); + + return null; + }, + function createLayergroup(err) { + assert.ifError(err); + + var next = this; + + assert.response(server, { + url: '/api/v1/map', + method: 'POST', + headers: { host: 'localhost', 'Content-Type': 'application/json' }, + data: JSON.stringify(layergroup) + }, {}, + function (res, err) { + next(err, res); + }); + }, + function checkLayergroup(err, res) { + assert.ifError(err); + assert.equal(res.statusCode, 200); + + var parsedBody = JSON.parse(res.body); + + keysToDelete['map_cfg|' + LayergroupToken.parse(parsedBody.layergroupid).token] = 0; + keysToDelete['user:localhost:mapviews:global'] = 5; + + assert.ok(parsedBody.layergroupid); + assert.ok(parsedBody.last_updated); + assert.deepEqual(parsedBody.metadata.layers[0].meta.cartocss, expectedCartocss); + + return parsedBody.layergroupid; + }, + function requestTile(err, layergroupId) { + assert.ifError(err); + + var next = this; + + assert.response(server, { + url: '/api/v1/map/' + layergroupId + '/0/0/0.png', + method: 'GET', + headers: { host: 'localhost' }, + encoding: 'binary' + }, {}, + function(res, err) { + next(err, res); + }); + }, + function checkTile(err, res) { + assert.ifError(err); + + assert.equal(res.statusCode, 200); + assert.equal(res.headers['content-type'], 'image/png'); + + testHelper.checkCache(res); + + return null; + }, + function deleteTemplate(err) { + assert.ifError(err); + + var next = this; + + assert.response(server, { + url: '/api/v1/map/named/' + templateId + '?api_key=1234', + method: 'DELETE', + headers: { host: 'localhost' } + }, {}, function (res, err) { + next(err, res); + }); + }, + function checkDeleteTemplate(err, res) { + assert.ifError(err); + assert.equal(res.statusCode, 204); + assert.ok(!res.body); + + return null; + }, + function finish(err) { + done(err); + } + ); + }); +});