diff --git a/HOWTO_RELEASE b/HOWTO_RELEASE index 824d5062..eedee1d0 100644 --- a/HOWTO_RELEASE +++ b/HOWTO_RELEASE @@ -1,5 +1,5 @@ 1. Test (make clean all check), fix if broken before proceeding -2. Ensure proper version in package.json +2. Ensure proper version in package.json 3. Ensure NEWS section exists for the new version, review it, add release date 4. Recreate npm-shrinkwrap.json with: `npm install --no-shrinkwrap && npm shrinkwrap` 5. Commit package.json, npm-shrinwrap.json, NEWS diff --git a/docs/MapConfig-NamedMaps-extension.md b/docs/MapConfig-NamedMaps-extension.md index 2793faec..4d3d32bd 100644 --- a/docs/MapConfig-NamedMaps-extension.md +++ b/docs/MapConfig-NamedMaps-extension.md @@ -31,6 +31,13 @@ This extension introduces a new layer type so it's possible to use a Named Map b "color": "#000" }, + // OPTIONAL + // object, which own properties can be a layer id or the corresponding layer index of the layergroup. + // the values for these keys can be true or false and indicates if the layer is available (or not) to instantiate the named map. + layer_visibility: { + [ {layer.id} | {layerIndex} ]: [ true | false ]; + }, + // OPTIONAL // string array, the authorized tokens in case the Named Map has auth method set to `token` // See https://github.com/CartoDB/Windshaft-cartodb/blob/master/docs/Map-API.md#named-maps-1 for more details diff --git a/lib/cartodb/backends/template_maps.js b/lib/cartodb/backends/template_maps.js index a4e7dbe3..9518a334 100644 --- a/lib/cartodb/backends/template_maps.js +++ b/lib/cartodb/backends/template_maps.js @@ -490,6 +490,10 @@ o.instance = function(template, params) { // Anything else ? } + layergroup.layers = layergroup.layers.filter(function (layer) { + return layer.options.visibility !== false; + }); + // extra information about the template layergroup.template = { name: template.name, diff --git a/test/acceptance/named_layers_visibility.js b/test/acceptance/named_layers_visibility.js new file mode 100644 index 00000000..43dc4a77 --- /dev/null +++ b/test/acceptance/named_layers_visibility.js @@ -0,0 +1,216 @@ +var test_helper = require('../support/test_helper'); + +var assert = require('../support/assert'); +var CartodbWindshaft = require(__dirname + '/../../lib/cartodb/server'); +var serverOptions = require(__dirname + '/../../lib/cartodb/server_options'); +var server = new CartodbWindshaft(serverOptions); + +var LayergroupToken = require('../support/layergroup-token'); + +var RedisPool = require('redis-mpool'); +var TemplateMaps = require('../../lib/cartodb/backends/template_maps.js'); + +var step = require('step'); + +describe('named_layers', function() { + // configure redis pool instance to use in tests + var redisPool = new RedisPool(global.environment.redis); + + var templateMaps = new TemplateMaps(redisPool, { + max_user_templates: global.environment.maxUserTemplates + }); + + var username = 'localhost'; + + var wadusLayer = { + type: 'cartodb', + options: { + sql: 'select 1 cartodb_id, null::geometry the_geom_webmercator', + cartocss: '#layer { marker-fill: <%= color %>; }', + cartocss_version: '2.3.0' + } + }; + + + var templateName = 'valid_template'; + var template = { + version: '0.0.1', + name: templateName, + auth: { + method: 'open' + }, + "placeholders": { + "color": { + "type": "css_color", + "default": "#cc3300" + } + }, + layergroup: { + layers: [ + wadusLayer, + wadusLayer + ] + } + }; + + var namedMapLayer = { + type: 'named', + options: { + name: templateName, + config: {}, + auth_tokens: [] + } + }; + + var notVisibileLayer = { + type: 'cartodb', + options: { + sql: 'select 1 cartodb_id, null::geometry the_geom_webmercator', + cartocss: '#layer { marker-fill: <%= color %>; }', + cartocss_version: '2.3.0', + visibility: false + } + }; + + + var keysToDelete; + + beforeEach(function() { + keysToDelete = {}; + }); + + afterEach(function(done) { + test_helper.deleteRedisKeys(keysToDelete, done); + }); + + beforeEach(function(done) { + global.environment.enabledFeatures = {cdbQueryTablesFromPostgres: true}; + templateMaps.addTemplate(username, template, function(err) { + return done(err); + }); + }); + + afterEach(function(done) { + global.environment.enabledFeatures = {cdbQueryTablesFromPostgres: false}; + templateMaps.delTemplate(username, templateName, function(err) { + return done(err); + }); + }); + + + it('should work with visibility tiles', function(done) { + + var namedTilesTemplateName = 'named_tiles_template'; + var namedTilesTemplate = { + version: '0.0.1', + name: namedTilesTemplateName, + auth: { + method: 'open' + }, + layergroup: { + layers: [ + namedMapLayer, + { + type: 'mapnik', + options: { + sql: 'select * from test_table_private_1', + cartocss: '#layer { marker-fill: #cc3300; }', + cartocss_version: '2.3.0' + } + }, + notVisibileLayer + ] + } + }; + + step( + function createTemplate() { + templateMaps.addTemplate(username, namedTilesTemplate, this); + }, + function createLayergroup(err) { + if (err) { + throw err; + } + + var next = this; + assert.response(server, + { + url: '/api/v1/map/named/' + namedTilesTemplateName + '?api_key=1234', + method: 'POST', + headers: { + host: 'localhost', + 'Content-Type': 'application/json' + } + }, + { + status: 200 + }, + function(res, err) { + next(err, res); + } + ); + }, + function checkLayergroup(err, response) { + if (err) { + throw err; + } + + var parsedBody = JSON.parse(response.body); + assert.ok(parsedBody.layergroupid); + assert.ok(parsedBody.last_updated); + + assert.equal(parsedBody.metadata.layers[0].type, 'mapnik'); + assert.equal(parsedBody.metadata.layers[1].type, 'mapnik'); + + keysToDelete['map_cfg|' + LayergroupToken.parse(parsedBody.layergroupid).token] = 0; + keysToDelete['user:localhost:mapviews:global'] = 5; + + return parsedBody.layergroupid; + }, + function requestTile(err, layergroupId) { + if (err) { + throw err; + } + + var next = this; + assert.response(server, + { + url: '/api/v1/map/' + layergroupId + '/all/0/0/0.png', + method: 'GET', + headers: { + host: 'localhost' + }, + encoding: 'binary' + }, + { + status: 200, + headers: { + 'content-type': 'image/png' + } + }, + function(res, err) { + next(err, res); + } + ); + }, + function handleTileResponse(err, res) { + if (err) { + throw err; + } + test_helper.checkCache(res); + return true; + }, + function deleteTemplate(err) { + var next = this; + templateMaps.delTemplate(username, namedTilesTemplateName, function(/*delErr*/) { + // ignore deletion error + next(err); + }); + }, + function finish(err) { + done(err); + } + ); + + }); +});