From 5aad6243463dedeaa38b40508145d2563929d17b Mon Sep 17 00:00:00 2001 From: Raul Ochoa Date: Thu, 9 Jul 2015 18:47:21 +0200 Subject: [PATCH] NamedMaps controller using NamedMapMapConfigProvider PreviewBackend with format param --- lib/cartodb/controllers/map.js | 6 +- lib/cartodb/controllers/named_maps.js | 236 +++--------------- .../models/mapconfig/named_map_provider.js | 133 ++++++---- 3 files changed, 119 insertions(+), 256 deletions(-) diff --git a/lib/cartodb/controllers/map.js b/lib/cartodb/controllers/map.js index f80c860f..585cf4f8 100644 --- a/lib/cartodb/controllers/map.js +++ b/lib/cartodb/controllers/map.js @@ -233,6 +233,8 @@ MapController.prototype.center = function(req, res) { MapController.prototype.staticMap = function(req, res, width, height, zoom /* bounds */, center) { var format = req.params.format === 'jpg' ? 'jpeg' : 'png'; + req.params.layer = 'all'; + req.params.format = 'png'; var self = this; @@ -245,10 +247,10 @@ MapController.prototype.staticMap = function(req, res, width, height, zoom /* bo assert.ifError(err); if (center) { self.previewBackend.getImage(new MapStoreMapConfigProvider(self.mapStore, req.params), - req.params, width, height, zoom, center, this); + format, width, height, zoom, center, this); } else { self.previewBackend.getImage(new MapStoreMapConfigProvider(self.mapStore, req.params), - req.params, width, height, zoom /* bounds */, this); + format, width, height, zoom /* bounds */, this); } }, function handleImage(err, image, headers, stats) { diff --git a/lib/cartodb/controllers/named_maps.js b/lib/cartodb/controllers/named_maps.js index b93517a7..4509dec7 100644 --- a/lib/cartodb/controllers/named_maps.js +++ b/lib/cartodb/controllers/named_maps.js @@ -1,11 +1,9 @@ var step = require('step'); var assert = require('assert'); var _ = require('underscore'); -var templateName = require('../backends/template_maps').templateName; var NamedMapsCacheEntry = require('../cache/model/named_maps_entry'); var cors = require('../middleware/cors'); -var MapStoreMapConfigProvider = require('windshaft').model.provider.MapStoreMapConfig; var NamedMapMapConfigProvider = require('../models/mapconfig/named_map_provider'); function NamedMapsController(app, pgConnection, mapStore, templateMaps, metadataBackend, mapBackend, tileBackend, @@ -28,7 +26,7 @@ function NamedMapsController(app, pgConnection, mapStore, templateMaps, metadata module.exports = NamedMapsController; NamedMapsController.prototype.register = function(app) { - app.get(this.templateBaseUrl + '/:template_id/:layer/:z/:x/:y.:format', cors(), this.tile.bind(this)); + app.get(this.templateBaseUrl + '/:template_id/:layer/:z/:x/:y.(:format)', cors(), this.tile.bind(this)); app.get(this.templateBaseUrl + '/:template_id/jsonp', cors(), this.jsonp.bind(this)); app.get( app.base_url_mapconfig + '/static/named/:template_id/:width/:height.:format', cors(), this.staticMap.bind(this) @@ -87,109 +85,23 @@ NamedMapsController.prototype.tile = function(req, res) { var self = this; var cdbUser = req.context.user; - var template; - var layergroupConfig; - var layergroupId; - var params; - var cacheChannel; - - var layergroupDecorator = { - beforeLayergroupCreate: function(requestMapConfig, callback) { - self.layergroupRequestDecorator.beforeLayergroupCreate(req, requestMapConfig, callback); - }, - afterLayergroupCreate: function(layergroup, response, callback) { - self.layergroupRequestDecorator.afterLayergroupCreate(req, layergroup, response, callback); - } - }; + var namedMapProvider; step( function reqParams() { self.app.req2params(req, this); }, - function getTemplate(err) { - assert.ifError(err); - self.templateMaps.getTemplate(cdbUser, templateName(req.params.template_id), this); - }, - function checkExists(err, tpl) { - assert.ifError(err); - if (!tpl) { - var notFoundErr = new Error( - "Template '" + templateName(req.params.template_id) + "' of user '" + cdbUser + "' not found" - ); - notFoundErr.http_status = 404; - throw notFoundErr; - } - return tpl; - }, - function checkAuthorized(err, tpl) { - assert.ifError(err); - - var authorized = false; - try { - authorized = self.templateMaps.isAuthorized(tpl, req.query.auth_token); - } catch (err) { - // we catch to add http_status - var authorizationFailedErr = new Error('Failed to authorize template'); - authorizationFailedErr.http_status = 403; - throw authorizationFailedErr; - } - if ( ! authorized ) { - var unauthorizedErr = new Error('Unauthorized template instantiation'); - unauthorizedErr.http_status = 403; - throw unauthorizedErr; - } - - return tpl; - }, - function prepareParams(err, tpl) { - assert.ifError(err); - - template = tpl; - - var templateParams = {}; - if (req.query.config) { - try { - templateParams = JSON.parse(req.query.config); - } catch (e) { - throw new Error('malformed config parameter, should be a valid JSON'); - } - } - - return templateParams; - }, - function instantiateTemplate(err, templateParams) { - assert.ifError(err); - return self.templateMaps.instance(template, templateParams); - }, - function prepareLayergroup(err, layergroup) { - assert.ifError(err); - layergroupConfig = layergroup; - params = _.extend({}, req.params, { - user: req.params.user - }); - self.app.setDBParams(cdbUser, params, this); - }, - function setApiKey(err){ - assert.ifError(err); - self.mapBackend.createLayergroup(layergroupConfig, params, layergroupDecorator, this); - }, - function prepareResponse(err, layergroup) { - assert.ifError(err); - - // added by createLayergroup - cacheChannel = res.header('X-Cache-Channel'); - res.removeHeader('X-Cache-Channel'); - self.surrogateKeysCache.tag(res, new NamedMapsCacheEntry(cdbUser, template.name)); - - layergroupId = layergroup.layergroupid.split(":")[0]; - - return null; - }, - function getImage(err) { - assert.ifError(err); - - req.params.token = layergroupId; - self.tileBackend.getTile(new MapStoreMapConfigProvider(self.mapStore, req.params), params, this); + function getTile() { + namedMapProvider = new NamedMapMapConfigProvider( + self.templateMaps, + self.pgConnection, + cdbUser, + req.params.template_id, + req.query.config, + req.query.auth_token, + req.params + ); + self.tileBackend.getTile(namedMapProvider, req.params, this); }, function handleImage(err, tile, headers, stats) { if (req.profiler) { @@ -201,6 +113,7 @@ NamedMapsController.prototype.tile = function(req, res) { } self.app.sendError(res, err, self.app.findStatusCode(err), 'NAMED_MAP_TILE', err); } else { + self.surrogateKeysCache.tag(res, new NamedMapsCacheEntry(cdbUser, namedMapProvider.getTemplateName())); res.setHeader('Content-Type', headers['Content-Type']); res.setHeader('Cache-Control', 'public,max-age=7200,must-revalidate'); self.app.sendResponse(res, [tile, 200]); @@ -215,106 +128,28 @@ NamedMapsController.prototype.staticMap = function(req, res) { var cdbUser = req.context.user; var format = req.params.format === 'jpg' ? 'jpeg' : 'png'; + req.params.format = 'png'; + req.params.layer = 'all'; - var template; - var layergroupConfig; - var layergroupId; - var params; - var cacheChannel; - - var layergroupDecorator = { - beforeLayergroupCreate: function(requestMapConfig, callback) { - self.layergroupRequestDecorator.beforeLayergroupCreate(req, requestMapConfig, callback); - }, - afterLayergroupCreate: function(layergroup, response, callback) { - self.layergroupRequestDecorator.afterLayergroupCreate(req, layergroup, response, callback); - } - }; - + var namedMapProvider; step( function reqParams() { self.app.req2params(req, this); }, function getTemplate(err) { assert.ifError(err); - self.templateMaps.getTemplate(cdbUser, templateName(req.params.template_id), this); + namedMapProvider = new NamedMapMapConfigProvider( + self.templateMaps, + self.pgConnection, + cdbUser, + req.params.template_id, + req.query.config, + req.query.auth_token, + req.params + ); + namedMapProvider.getTemplate(this); }, - function checkExists(err, tpl) { - assert.ifError(err); - if (!tpl) { - var notFoundErr = new Error( - "Template '" + templateName(req.params.template_id) + "' of user '" + cdbUser + "' not found" - ); - notFoundErr.http_status = 404; - throw notFoundErr; - } - return tpl; - }, - function checkAuthorized(err, tpl) { - assert.ifError(err); - - var authorized = false; - try { - authorized = self.templateMaps.isAuthorized(tpl, req.query.auth_token); - } catch (err) { - // we catch to add http_status - var authorizationFailedErr = new Error('Failed to authorize template'); - authorizationFailedErr.http_status = 403; - throw authorizationFailedErr; - } - if ( ! authorized ) { - var unauthorizedErr = new Error('Unauthorized template instantiation'); - unauthorizedErr.http_status = 403; - throw unauthorizedErr; - } - - return tpl; - }, - function prepareParams(err, tpl) { - assert.ifError(err); - - template = tpl; - - var templateParams = {}; - if (req.query.config) { - try { - templateParams = JSON.parse(req.query.config); - } catch (e) { - throw new Error('malformed config parameter, should be a valid JSON'); - } - } - - return templateParams; - }, - function instantiateTemplate(err, templateParams) { - assert.ifError(err); - return self.templateMaps.instance(template, templateParams); - }, - function prepareLayergroup(err, layergroup) { - assert.ifError(err); - layergroupConfig = layergroup; - params = { - user: req.params.user - }; - self.app.setDBParams(cdbUser, params, this); - }, - function setApiKey(err){ - assert.ifError(err); - self.mapBackend.createLayergroup(layergroupConfig, params, layergroupDecorator, this); - }, - function prepareResponse(err, layergroup) { - assert.ifError(err); - - // added by createLayergroup - cacheChannel = res.header('X-Cache-Channel'); - res.removeHeader('X-Cache-Channel'); - self.surrogateKeysCache.tag(res, new NamedMapsCacheEntry(cdbUser, template.name)); - - layergroupId = layergroup.layergroupid.split(":")[0]; - - return null; - }, - function staticImageOptions(err) { + function prepareStaticImageOptions(err, template) { assert.ifError(err); getStaticImageOptions(template, this); }, @@ -332,6 +167,8 @@ NamedMapsController.prototype.staticMap = function(req, res) { } }; + var cacheChannel = ''; + var dbTables = cacheChannel.split(':'); if (dbTables.length <= 1 || dbTables[1].length === 0) { return defaultZoomCenter; @@ -350,21 +187,15 @@ NamedMapsController.prototype.staticMap = function(req, res) { function getImage(err, imageOpts) { assert.ifError(err); - var imageParams = _.extend({}, params, { - token: layergroupId, - format: req.params.format - }); - var width = +req.params.width; var height = +req.params.height; - var provider = new MapStoreMapConfigProvider(self.mapStore, imageParams); if (!_.isUndefined(imageOpts.zoom) && imageOpts.center) { self.previewBackend.getImage( - provider, imageParams, width, height, imageOpts.zoom, imageOpts.center, this); + namedMapProvider, format, width, height, imageOpts.zoom, imageOpts.center, this); } else { self.previewBackend.getImage( - provider, imageParams, width, height, imageOpts.bounds, this); + namedMapProvider, format, width, height, imageOpts.bounds, this); } }, function handleImage(err, image, headers, stats) { @@ -379,6 +210,7 @@ NamedMapsController.prototype.staticMap = function(req, res) { } self.app.sendError(res, err, self.app.findStatusCode(err), 'STATIC_VIZ_MAP', err); } else { + self.surrogateKeysCache.tag(res, new NamedMapsCacheEntry(cdbUser, namedMapProvider.getTemplateName())); res.setHeader('Content-Type', headers['Content-Type'] || 'image/' + format); res.setHeader('Cache-Control', 'public,max-age=7200,must-revalidate'); self.app.sendResponse(res, [image, 200]); @@ -418,7 +250,7 @@ NamedMapsController.prototype.instantiateTemplate = function(req, res, template_ }, function createLayergroup(err, mapConfig, rendererParams/*, context*/) { assert.ifError(err); - self.mapBackend.createLayergroup(mapConfig, rendererParams, layergroupDecorator, this); + self.mapBackend.createLayergroup(mapConfig.obj(), rendererParams, layergroupDecorator, this); }, function prepareResponse(err, layergroup) { if (err) { diff --git a/lib/cartodb/models/mapconfig/named_map_provider.js b/lib/cartodb/models/mapconfig/named_map_provider.js index 7abfa2b3..d8830dd4 100644 --- a/lib/cartodb/models/mapconfig/named_map_provider.js +++ b/lib/cartodb/models/mapconfig/named_map_provider.js @@ -1,13 +1,15 @@ var _ = require('underscore'); var assert = require('assert'); +var dot = require('dot'); var step = require('step'); +var MapConfig = require('windshaft').model.MapConfig; var templateName = require('../../backends/template_maps').templateName; function NamedMapMapConfigProvider(templateMaps, pgConnection, owner, templateId, config, authToken, params) { this.templateMaps = templateMaps; this.pgConnection = pgConnection; this.owner = owner; - this.templateId = templateId; + this.templateName = templateName(templateId); this.config = config; this.authToken = authToken; this.params = params; @@ -25,51 +27,19 @@ function NamedMapMapConfigProvider(templateMaps, pgConnection, owner, templateId module.exports = NamedMapMapConfigProvider; NamedMapMapConfigProvider.prototype.getMapConfig = function(callback) { - if (this.err !== null || this.mapConfig !== null) { + if (!!this.err || this.mapConfig !== null) { return callback(this.err, this.mapConfig, this.rendererParams, this.context); } var self = this; - var mapConfig; + var mapConfig = null; var rendererParams; var context = {}; step( - function getTemplate(err) { - assert.ifError(err); - self.templateMaps.getTemplate(self.owner, templateName(self.templateId), this); - }, - function checkExists(err, tpl) { - assert.ifError(err); - if (!tpl) { - var notFoundErr = new Error( - "Template '" + templateName(self.templateId) + "' of user '" + self.owner + "' not found" - ); - notFoundErr.http_status = 404; - throw notFoundErr; - } - return tpl; - }, - function checkAuthorized(err, tpl) { - assert.ifError(err); - - var authorized = false; - try { - authorized = self.templateMaps.isAuthorized(tpl, self.authToken); - } catch (err) { - // we catch to add http_status - var authorizationFailedErr = new Error('Failed to authorize template'); - authorizationFailedErr.http_status = 403; - throw authorizationFailedErr; - } - if ( ! authorized ) { - var unauthorizedErr = new Error('Unauthorized template instantiation'); - unauthorizedErr.http_status = 403; - throw unauthorizedErr; - } - - return tpl; + function getTemplate() { + self.getTemplate(this); }, function prepareParams(err, tpl) { assert.ifError(err); @@ -101,7 +71,7 @@ NamedMapMapConfigProvider.prototype.getMapConfig = function(callback) { }, function cacheAndReturnMapConfig(err) { self.err = err; - self.mapConfig = mapConfig; + self.mapConfig = (mapConfig === null) ? null : MapConfig.create(mapConfig); self.rendererParams = rendererParams; self.context = context; return callback(self.err, self.mapConfig, self.rendererParams, self.context); @@ -109,8 +79,58 @@ NamedMapMapConfigProvider.prototype.getMapConfig = function(callback) { ); }; +NamedMapMapConfigProvider.prototype.getTemplate = function(callback) { + var self = this; + + if (!!this.err || this.template !== null) { + return callback(this.err, this.template); + } + + step( + function getTemplate() { + self.templateMaps.getTemplate(self.owner, self.templateName, this); + }, + function checkExists(err, tpl) { + assert.ifError(err); + if (!tpl) { + var notFoundErr = new Error( + "Template '" + self.templateName + "' of user '" + self.owner + "' not found" + ); + notFoundErr.http_status = 404; + throw notFoundErr; + } + return tpl; + }, + function checkAuthorized(err, tpl) { + assert.ifError(err); + + var authorized = false; + try { + authorized = self.templateMaps.isAuthorized(tpl, self.authToken); + } catch (err) { + // we catch to add http_status + var authorizationFailedErr = new Error('Failed to authorize template'); + authorizationFailedErr.http_status = 403; + throw authorizationFailedErr; + } + if ( ! authorized ) { + var unauthorizedErr = new Error('Unauthorized template instantiation'); + unauthorizedErr.http_status = 403; + throw unauthorizedErr; + } + + return tpl; + }, + function cacheAndReturnTemplate(err, template) { + self.err = err; + self.template = template; + return callback(self.err, self.template); + } + ); +}; + NamedMapMapConfigProvider.prototype.getKey = function() { - return createKey(this.params); + return this.createKey(); }; NamedMapMapConfigProvider.prototype.getCacheBuster = function() { @@ -118,24 +138,29 @@ NamedMapMapConfigProvider.prototype.getCacheBuster = function() { }; NamedMapMapConfigProvider.prototype.filter = function(key) { - var regex = new RegExp('^' + createKey(this.params, true) + '.*'); + var regex = new RegExp('^' + this.createKey(true) + '.*'); return key && key.match(regex); }; // Configure bases for cache keys suitable for string interpolation -var baseKey = '<%= dbname %>:<%= owner %>:<%= templateName %>'; -var renderKey = baseKey + ':<%= authToken %>:<%= configHash %>:<%= layer %>:<%= scale_factor %>'; -// Create a string ID/key from a set of params -function createKey(params, base) { - return _.template(base ? baseKey : renderKey, _.defaults({}, params, { +var baseKey = '{{=it.dbname}}:{{=it.owner}}:{{=it.templateName}}'; +var rendererKey = baseKey + ':{{=it.authToken}}:{{=it.configHash}}:{{=it.format}}:{{=it.layer}}:{{=it.scale_factor}}'; + +var baseKeyTpl = dot.template(baseKey); +var rendererKeyTpl = dot.template(rendererKey); + +NamedMapMapConfigProvider.prototype.createKey = function(base) { + var tplValues = _.defaults({}, this.params, { dbname: '', - owner: '', - templateName: '', - authToken: '', - configHash: '', + owner: this.owner, + templateName: this.templateName, + authToken: this.authToken || '', + configHash: this.config && this.config.toString() || '', + layer: '', scale_factor: 1 - })); -} + }); + return (base) ? baseKeyTpl(tplValues) : rendererKeyTpl(tplValues); +}; NamedMapMapConfigProvider.prototype.setDBParams = function(cdbuser, params, callback) { var self = this; @@ -151,4 +176,8 @@ NamedMapMapConfigProvider.prototype.setDBParams = function(cdbuser, params, call callback(err); } ); -}; \ No newline at end of file +}; + +NamedMapMapConfigProvider.prototype.getTemplateName = function() { + return this.templateName; +};