diff --git a/lib/cartodb/controllers/named_maps.js b/lib/cartodb/controllers/named_maps.js index 686be507..b93517a7 100644 --- a/lib/cartodb/controllers/named_maps.js +++ b/lib/cartodb/controllers/named_maps.js @@ -6,11 +6,14 @@ 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, mapStore, templateMaps, metadataBackend, mapBackend, tileBackend, previewBackend, - templateBaseUrl, surrogateKeysCache, layergroupRequestDecorator, tablesExtentApi) { +function NamedMapsController(app, pgConnection, mapStore, templateMaps, metadataBackend, mapBackend, tileBackend, + previewBackend, templateBaseUrl, surrogateKeysCache, layergroupRequestDecorator, + tablesExtentApi) { this.app = app; this.mapStore = mapStore; + this.pgConnection = pgConnection; this.templateMaps = templateMaps; this.metadataBackend = metadataBackend; this.mapBackend = mapBackend; @@ -398,76 +401,34 @@ NamedMapsController.prototype.instantiateTemplate = function(req, res, template_ } }; - var template; - var layergroup; var cdbuser = req.context.user; - var params = { - user: req.params.user - }; - // Format of template_id: []@ - var tpl_id = templateName(req.params.template_id); - var auth_token = req.query.auth_token; + var mapConfigProvider = new NamedMapMapConfigProvider( + this.templateMaps, + this.pgConnection, + cdbuser, + req.params.template_id, + template_params, + req.query.auth_token, + req.params + ); + step( function getTemplate(){ - self.templateMaps.getTemplate(cdbuser, tpl_id, this); + mapConfigProvider.getMapConfig(this); }, - function checkAuthorized(err, templateValue) { - if ( req.profiler ) req.profiler.done('getTemplate'); - if ( err ) throw err; - if ( ! templateValue ) { - err = new Error("Template '" + tpl_id + "' of user '" + cdbuser + "' not found"); - err.http_status = 404; - throw err; - } - - template = templateValue; - - var authorized = false; - try { - authorized = self.templateMaps.isAuthorized(template, auth_token); - } catch (err) { - // we catch to add http_status - err.http_status = 403; - throw err; - } - if ( ! authorized ) { - err = new Error('Unauthorized template instantiation'); - err.http_status = 403; - throw err; - } - - if (req.profiler) { - req.profiler.done('authorizedByCert'); - } - - return self.templateMaps.instance(template, template_params); - }, - function prepareParams(err, instance){ - if ( req.profiler ) req.profiler.done('TemplateMaps_instance'); - if ( err ) throw err; - layergroup = instance; - self.app.setDBParams(cdbuser, params, this); - }, - function setApiKey(err){ - if ( req.profiler ) req.profiler.done('setDBParams'); - if ( err ) throw err; - self.metadataBackend.getUserMapKey(cdbuser, this); - }, - function createLayergroup(err, val) { - if ( req.profiler ) req.profiler.done('getUserMapKey'); - if ( err ) throw err; - params.api_key = val; - self.mapBackend.createLayergroup(layergroup, params, layergroupDecorator, this); + function createLayergroup(err, mapConfig, rendererParams/*, context*/) { + assert.ifError(err); + self.mapBackend.createLayergroup(mapConfig, rendererParams, layergroupDecorator, this); }, function prepareResponse(err, layergroup) { - if ( err ) { + if (err) { return callback(err, { errors: [''+err] }); } - var tplhash = self.templateMaps.fingerPrint(template).substring(0,8); - layergroup.layergroupid = cdbuser + '@' + tplhash + '@' + layergroup.layergroupid; + var templateHash = self.templateMaps.fingerPrint(mapConfigProvider.template).substring(0,8); + layergroup.layergroupid = cdbuser + '@' + templateHash + '@' + layergroup.layergroupid; res.header('X-Layergroup-Id', layergroup.layergroupid); - self.surrogateKeysCache.tag(res, new NamedMapsCacheEntry(cdbuser, template.name)); + self.surrogateKeysCache.tag(res, new NamedMapsCacheEntry(cdbuser, mapConfigProvider.template.name)); callback(null, layergroup); } diff --git a/lib/cartodb/models/mapconfig/named_map_provider.js b/lib/cartodb/models/mapconfig/named_map_provider.js new file mode 100644 index 00000000..7abfa2b3 --- /dev/null +++ b/lib/cartodb/models/mapconfig/named_map_provider.js @@ -0,0 +1,154 @@ +var _ = require('underscore'); +var assert = require('assert'); +var step = require('step'); +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.config = config; + this.authToken = authToken; + this.params = params; + + // use template after call to mapConfig + this.template = null; + + // providing + this.err = null; + this.mapConfig = null; + this.rendererParams = null; + this.context = null; +} + +module.exports = NamedMapMapConfigProvider; + +NamedMapMapConfigProvider.prototype.getMapConfig = function(callback) { + if (this.err !== null || this.mapConfig !== null) { + return callback(this.err, this.mapConfig, this.rendererParams, this.context); + } + + var self = this; + + var mapConfig; + 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 prepareParams(err, tpl) { + assert.ifError(err); + + self.template = tpl; + + var templateParams = {}; + if (self.config) { + try { + templateParams = _.isString(self.config) ? JSON.parse(self.config) : self.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(self.template, templateParams); + }, + function prepareLayergroup(err, layergroup) { + assert.ifError(err); + mapConfig = layergroup; + rendererParams = _.extend({}, self.params, { + user: self.owner + }); + self.setDBParams(self.owner, rendererParams, this); + }, + function cacheAndReturnMapConfig(err) { + self.err = err; + self.mapConfig = mapConfig; + self.rendererParams = rendererParams; + self.context = context; + return callback(self.err, self.mapConfig, self.rendererParams, self.context); + } + ); +}; + +NamedMapMapConfigProvider.prototype.getKey = function() { + return createKey(this.params); +}; + +NamedMapMapConfigProvider.prototype.getCacheBuster = function() { + return 0; +}; + +NamedMapMapConfigProvider.prototype.filter = function(key) { + var regex = new RegExp('^' + createKey(this.params, 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, { + dbname: '', + owner: '', + templateName: '', + authToken: '', + configHash: '', + scale_factor: 1 + })); +} + +NamedMapMapConfigProvider.prototype.setDBParams = function(cdbuser, params, callback) { + var self = this; + step( + function setAuth() { + self.pgConnection.setDBAuth(cdbuser, params, this); + }, + function setConn(err) { + if ( err ) throw err; + self.pgConnection.setDBConn(cdbuser, params, this); + }, + function finish(err) { + callback(err); + } + ); +}; \ No newline at end of file diff --git a/lib/cartodb/server.js b/lib/cartodb/server.js index bba292af..aa55f39a 100644 --- a/lib/cartodb/server.js +++ b/lib/cartodb/server.js @@ -321,6 +321,7 @@ module.exports = function(serverOptions) { new controller.NamedMaps( app, + pgConnection, mapStore, templateMaps, metadataBackend,