Merge pull request #970 from CartoDB/mapconfig-provirers-refactor
Mapconfig providers refactor
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
const QueryTables = require('cartodb-query-tables');
|
||||
|
||||
module.exports = class BaseMapConfigProvider {
|
||||
createAffectedTables (callback) {
|
||||
this.getMapConfig((err, mapConfig) => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
const { dbname } = this.params;
|
||||
const token = mapConfig.id();
|
||||
|
||||
const queries = [];
|
||||
|
||||
this.mapConfig.getLayers().forEach(layer => {
|
||||
queries.push(layer.options.sql);
|
||||
if (layer.options.affected_tables) {
|
||||
layer.options.affected_tables.map(table => {
|
||||
queries.push(`SELECT * FROM ${table} LIMIT 0`);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const sql = queries.length ? queries.join(';') : null;
|
||||
|
||||
if (!sql) {
|
||||
return callback();
|
||||
}
|
||||
|
||||
this.pgConnection.getConnection(this.user, (err, connection) => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
QueryTables.getAffectedTablesFromQuery(connection, sql, (err, affectedTables) => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
this.affectedTablesCache.set(dbname, token, affectedTables);
|
||||
|
||||
callback(null, affectedTables);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
getAffectedTables (callback) {
|
||||
this.getMapConfig((err, mapConfig) => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
const { dbname } = this.params;
|
||||
const token = mapConfig.id();
|
||||
|
||||
if (this.affectedTablesCache.hasAffectedTables(dbname, token)) {
|
||||
const affectedTables = this.affectedTablesCache.get(dbname, token);
|
||||
return callback(null, affectedTables);
|
||||
}
|
||||
|
||||
return this.createAffectedTables(callback);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -1,128 +1,27 @@
|
||||
var assert = require('assert');
|
||||
var step = require('step');
|
||||
const MapStoreMapConfigProvider = require('./map-store-provider');
|
||||
|
||||
var MapStoreMapConfigProvider = require('./map-store-provider');
|
||||
const QueryTables = require('cartodb-query-tables');
|
||||
|
||||
/**
|
||||
* @param {MapConfig} mapConfig
|
||||
* @param {String} user
|
||||
* @param {UserLimitsBackend} userLimitsBackend
|
||||
* @param {Object} params
|
||||
* @constructor
|
||||
* @type {CreateLayergroupMapConfigProvider}
|
||||
*/
|
||||
|
||||
function CreateLayergroupMapConfigProvider(
|
||||
mapConfig,
|
||||
user,
|
||||
userLimitsBackend,
|
||||
pgConnection,
|
||||
affectedTablesCache,
|
||||
params
|
||||
) {
|
||||
this.mapConfig = mapConfig;
|
||||
this.user = user;
|
||||
this.userLimitsBackend = userLimitsBackend;
|
||||
this.pgConnection = pgConnection;
|
||||
this.affectedTablesCache = affectedTablesCache;
|
||||
this.params = params;
|
||||
this.cacheBuster = params.cache_buster || 0;
|
||||
}
|
||||
|
||||
module.exports = CreateLayergroupMapConfigProvider;
|
||||
|
||||
CreateLayergroupMapConfigProvider.prototype.getMapConfig = function(callback) {
|
||||
var self = this;
|
||||
|
||||
if (this.mapConfig && this.params && this.context) {
|
||||
return callback(null, this.mapConfig, this.params, this.context);
|
||||
module.exports = class CreateLayergroupMapConfigProvider extends MapStoreMapConfigProvider {
|
||||
constructor (mapConfig, user, userLimitsBackend, pgConnection, affectedTablesCache, params) {
|
||||
super(null, user, userLimitsBackend, pgConnection, affectedTablesCache, params);
|
||||
this.mapConfig = mapConfig;
|
||||
}
|
||||
|
||||
var context = {};
|
||||
|
||||
step(
|
||||
function prepareContextLimits() {
|
||||
self.userLimitsBackend.getRenderLimits(self.user, self.params.api_key, this);
|
||||
},
|
||||
function handleRenderLimits(err, renderLimits) {
|
||||
assert.ifError(err);
|
||||
context.limits = renderLimits;
|
||||
self.context = context;
|
||||
return null;
|
||||
},
|
||||
function finish(err) {
|
||||
return callback(err, self.mapConfig, self.params, context);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
CreateLayergroupMapConfigProvider.prototype.getKey = MapStoreMapConfigProvider.prototype.getKey;
|
||||
|
||||
CreateLayergroupMapConfigProvider.prototype.getCacheBuster = MapStoreMapConfigProvider.prototype.getCacheBuster;
|
||||
|
||||
CreateLayergroupMapConfigProvider.prototype.filter = MapStoreMapConfigProvider.prototype.filter;
|
||||
|
||||
CreateLayergroupMapConfigProvider.prototype.createKey = MapStoreMapConfigProvider.prototype.createKey;
|
||||
|
||||
CreateLayergroupMapConfigProvider.prototype.createAffectedTables = function (callback) {
|
||||
this.getMapConfig((err, mapConfig) => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
getMapConfig (callback) {
|
||||
if (this.mapConfig && this.params && this.context) {
|
||||
return callback(null, this.mapConfig, this.params, this.context);
|
||||
}
|
||||
|
||||
const { dbname } = this.params;
|
||||
const token = mapConfig.id();
|
||||
const context = {};
|
||||
|
||||
const queries = [];
|
||||
|
||||
this.mapConfig.getLayers().forEach(layer => {
|
||||
queries.push(layer.options.sql);
|
||||
if (layer.options.affected_tables) {
|
||||
layer.options.affected_tables.map(table => {
|
||||
queries.push(`SELECT * FROM ${table} LIMIT 0`);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const sql = queries.length ? queries.join(';') : null;
|
||||
|
||||
if (!sql) {
|
||||
return callback();
|
||||
}
|
||||
|
||||
this.pgConnection.getConnection(this.user, (err, connection) => {
|
||||
this.userLimitsBackend.getRenderLimits(this.user, this.params.api_key, (err, renderLimits) => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
QueryTables.getAffectedTablesFromQuery(connection, sql, (err, affectedTables) => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
context.limits = renderLimits;
|
||||
this.context = context;
|
||||
|
||||
this.affectedTablesCache.set(dbname, token, affectedTables);
|
||||
|
||||
callback(null, affectedTables);
|
||||
});
|
||||
return callback(null, this.mapConfig, this.params, context);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
CreateLayergroupMapConfigProvider.prototype.getAffectedTables = function (callback) {
|
||||
this.getMapConfig((err, mapConfig) => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
const { dbname } = this.params;
|
||||
const token = mapConfig.id();
|
||||
|
||||
if (this.affectedTablesCache.hasAffectedTables(dbname, token)) {
|
||||
const affectedTables = this.affectedTablesCache.get(dbname, token);
|
||||
return callback(null, affectedTables);
|
||||
}
|
||||
|
||||
return this.createAffectedTables(callback);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,152 +1,86 @@
|
||||
var _ = require('underscore');
|
||||
var assert = require('assert');
|
||||
var dot = require('dot');
|
||||
var step = require('step');
|
||||
const QueryTables = require('cartodb-query-tables');
|
||||
|
||||
/**
|
||||
* @param {MapStore} mapStore
|
||||
* @param {String} user
|
||||
* @param {UserLimitsBackend} userLimitsBackend
|
||||
* @param {Object} params
|
||||
* @constructor
|
||||
* @type {MapStoreMapConfigProvider}
|
||||
*/
|
||||
function MapStoreMapConfigProvider(mapStore, user, userLimitsBackend, pgConnection, affectedTablesCache, params) {
|
||||
this.mapStore = mapStore;
|
||||
this.user = user;
|
||||
this.userLimitsBackend = userLimitsBackend;
|
||||
this.pgConnection = pgConnection;
|
||||
this.affectedTablesCache = affectedTablesCache;
|
||||
this.token = params.token;
|
||||
this.cacheBuster = params.cache_buster || 0;
|
||||
this.mapConfig = null;
|
||||
this.params = params;
|
||||
this.context = null;
|
||||
}
|
||||
|
||||
module.exports = MapStoreMapConfigProvider;
|
||||
|
||||
MapStoreMapConfigProvider.prototype.getMapConfig = function(callback) {
|
||||
var self = this;
|
||||
|
||||
if (this.mapConfig !== null) {
|
||||
return callback(null, this.mapConfig, this.params, this.context);
|
||||
}
|
||||
|
||||
var context = {};
|
||||
|
||||
step(
|
||||
function prepareContextLimits() {
|
||||
self.userLimitsBackend.getRenderLimits(self.user, self.params.api_key, this);
|
||||
},
|
||||
function handleRenderLimits(err, renderLimits) {
|
||||
assert.ifError(err);
|
||||
context.limits = renderLimits;
|
||||
return null;
|
||||
},
|
||||
function loadMapConfig(err) {
|
||||
assert.ifError(err);
|
||||
self.mapStore.load(self.token, this);
|
||||
},
|
||||
function finish(err, mapConfig) {
|
||||
self.mapConfig = mapConfig;
|
||||
self.context = context;
|
||||
return callback(err, mapConfig, self.params, context);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
MapStoreMapConfigProvider.prototype.getKey = function() {
|
||||
return this.createKey(false);
|
||||
};
|
||||
|
||||
MapStoreMapConfigProvider.prototype.getCacheBuster = function() {
|
||||
return this.cacheBuster;
|
||||
};
|
||||
|
||||
MapStoreMapConfigProvider.prototype.filter = function(key) {
|
||||
var regex = new RegExp('^' + this.createKey(true) + '.*');
|
||||
return key && key.match(regex);
|
||||
};
|
||||
const BaseMapConfigProvider = require('./base-mapconfig-adapter');
|
||||
const dot = require('dot');
|
||||
|
||||
// Configure bases for cache keys suitable for string interpolation
|
||||
var baseKey = '{{=it.dbname}}:{{=it.token}}';
|
||||
var rendererKey = baseKey + ':{{=it.dbuser}}:{{=it.format}}:{{=it.layer}}:{{=it.scale_factor}}';
|
||||
const baseKey = '{{=it.dbname}}:{{=it.token}}';
|
||||
const rendererKey = baseKey + ':{{=it.dbuser}}:{{=it.format}}:{{=it.layer}}:{{=it.scale_factor}}';
|
||||
|
||||
var baseKeyTpl = dot.template(baseKey);
|
||||
var rendererKeyTpl = dot.template(rendererKey);
|
||||
const baseKeyTpl = dot.template(baseKey);
|
||||
const rendererKeyTpl = dot.template(rendererKey);
|
||||
|
||||
MapStoreMapConfigProvider.prototype.createKey = function(base) {
|
||||
var tplValues = _.defaults({}, this.params, {
|
||||
dbname: '',
|
||||
token: '',
|
||||
dbuser: '',
|
||||
format: '',
|
||||
layer: '',
|
||||
scale_factor: 1
|
||||
});
|
||||
return (base) ? baseKeyTpl(tplValues) : rendererKeyTpl(tplValues);
|
||||
};
|
||||
module.exports = class MapStoreMapConfigProvider extends BaseMapConfigProvider {
|
||||
/**
|
||||
* @param {MapStore} mapStore
|
||||
* @param {String} user
|
||||
* @param {UserLimitsBackend} userLimitsBackend
|
||||
* @param {Object} params
|
||||
* @constructor
|
||||
* @type {MapStoreMapConfigProvider}
|
||||
*/
|
||||
constructor (mapStore, user, userLimitsBackend, pgConnection, affectedTablesCache, params) {
|
||||
super();
|
||||
this.mapStore = mapStore;
|
||||
this.user = user;
|
||||
this.userLimitsBackend = userLimitsBackend;
|
||||
this.pgConnection = pgConnection;
|
||||
this.affectedTablesCache = affectedTablesCache;
|
||||
this.params = params;
|
||||
this.token = params.token;
|
||||
this.cacheBuster = params.cache_buster || 0;
|
||||
this.mapConfig = null;
|
||||
this.context = null;
|
||||
}
|
||||
|
||||
MapStoreMapConfigProvider.prototype.createAffectedTables = function(callback) {
|
||||
this.getMapConfig((err, mapConfig) => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
getMapConfig (callback) {
|
||||
if (this.mapConfig !== null) {
|
||||
return callback(null, this.mapConfig, this.params, this.context);
|
||||
}
|
||||
|
||||
const { dbname } = this.params;
|
||||
const token = mapConfig.id();
|
||||
const context = {};
|
||||
|
||||
const queries = [];
|
||||
|
||||
mapConfig.getLayers().forEach(layer => {
|
||||
queries.push(layer.options.sql);
|
||||
if (layer.options.affected_tables) {
|
||||
layer.options.affected_tables.map(table => {
|
||||
queries.push(`SELECT * FROM ${table} LIMIT 0`);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const sql = queries.length ? queries.join(';') : null;
|
||||
|
||||
if (!sql) {
|
||||
return callback();
|
||||
}
|
||||
|
||||
this.pgConnection.getConnection(this.user, (err, connection) => {
|
||||
this.userLimitsBackend.getRenderLimits(this.user, this.params.api_key, (err, renderLimits) => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
QueryTables.getAffectedTablesFromQuery(connection, sql, (err, affectedTables) => {
|
||||
context.limits = renderLimits;
|
||||
|
||||
this.mapStore.load(this.token, (err, mapConfig) => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
this.affectedTablesCache.set(dbname, token, affectedTables);
|
||||
this.mapConfig = mapConfig;
|
||||
this.context = context;
|
||||
|
||||
callback(err, affectedTables);
|
||||
return callback(null, mapConfig, this.params, context);
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
MapStoreMapConfigProvider.prototype.getAffectedTables = function (callback) {
|
||||
this.getMapConfig((err, mapConfig) => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
getKey () {
|
||||
return this.createKey(false);
|
||||
}
|
||||
|
||||
const { dbname } = this.params;
|
||||
const token = mapConfig.id();
|
||||
getCacheBuster () {
|
||||
return this.cacheBuster;
|
||||
}
|
||||
|
||||
if (this.affectedTablesCache.hasAffectedTables(dbname, token)) {
|
||||
const affectedTables = this.affectedTablesCache.get(dbname, token);
|
||||
return callback(null, affectedTables);
|
||||
}
|
||||
filter (key) {
|
||||
const regex = new RegExp('^' + this.createKey(true) + '.*');
|
||||
return key && key.match(regex);
|
||||
}
|
||||
|
||||
return this.createAffectedTables(callback);
|
||||
});
|
||||
createKey (base) {
|
||||
const tplValues = Object.assign({
|
||||
dbname: '',
|
||||
token: '',
|
||||
dbuser: '',
|
||||
format: '',
|
||||
layer: '',
|
||||
scale_factor: 1
|
||||
}, this.params);
|
||||
|
||||
return (base) ? baseKeyTpl(tplValues) : rendererKeyTpl(tplValues);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,325 +1,279 @@
|
||||
var _ = require('underscore');
|
||||
var assert = require('assert');
|
||||
var crypto = require('crypto');
|
||||
var dot = require('dot');
|
||||
var step = require('step');
|
||||
var MapConfig = require('windshaft').model.MapConfig;
|
||||
var templateName = require('../../../backends/template_maps').templateName;
|
||||
var QueryTables = require('cartodb-query-tables');
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @type {NamedMapMapConfigProvider}
|
||||
*/
|
||||
function NamedMapMapConfigProvider(
|
||||
templateMaps,
|
||||
pgConnection,
|
||||
metadataBackend,
|
||||
userLimitsBackend,
|
||||
mapConfigAdapter,
|
||||
affectedTablesCache,
|
||||
owner,
|
||||
templateId,
|
||||
config,
|
||||
authToken,
|
||||
params
|
||||
) {
|
||||
this.templateMaps = templateMaps;
|
||||
this.pgConnection = pgConnection;
|
||||
this.metadataBackend = metadataBackend;
|
||||
this.userLimitsBackend = userLimitsBackend;
|
||||
this.mapConfigAdapter = mapConfigAdapter;
|
||||
|
||||
this.owner = owner;
|
||||
this.templateName = templateName(templateId);
|
||||
this.config = config;
|
||||
this.authToken = authToken;
|
||||
this.params = params;
|
||||
|
||||
this.cacheBuster = Date.now();
|
||||
|
||||
// use template after call to mapConfig
|
||||
this.template = null;
|
||||
|
||||
this.affectedTablesCache = affectedTablesCache;
|
||||
|
||||
// providing
|
||||
this.err = null;
|
||||
this.mapConfig = null;
|
||||
this.rendererParams = null;
|
||||
this.context = {};
|
||||
this.analysesResults = [];
|
||||
}
|
||||
|
||||
module.exports = NamedMapMapConfigProvider;
|
||||
|
||||
NamedMapMapConfigProvider.prototype.getMapConfig = function(callback) {
|
||||
if (!!this.err || this.mapConfig !== null) {
|
||||
return callback(this.err, this.mapConfig, this.rendererParams, this.context);
|
||||
}
|
||||
|
||||
var self = this;
|
||||
|
||||
var mapConfig = null;
|
||||
var rendererParams;
|
||||
var apiKey;
|
||||
|
||||
var context = {};
|
||||
|
||||
step(
|
||||
function getTemplate() {
|
||||
self.getTemplate(this);
|
||||
},
|
||||
function prepareDbParams(err, tpl) {
|
||||
assert.ifError(err);
|
||||
self.template = tpl;
|
||||
|
||||
rendererParams = _.extend({}, self.params, {
|
||||
user: self.owner
|
||||
});
|
||||
self.setDBParams(self.owner, rendererParams, this);
|
||||
},
|
||||
function getUserApiKey(err) {
|
||||
assert.ifError(err);
|
||||
self.metadataBackend.getUserMapKey(self.owner, this);
|
||||
},
|
||||
function prepareParams(err, _apiKey) {
|
||||
assert.ifError(err);
|
||||
|
||||
apiKey = _apiKey;
|
||||
|
||||
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);
|
||||
context.templateParams = templateParams;
|
||||
return self.templateMaps.instance(self.template, templateParams);
|
||||
},
|
||||
function prepareAdapterMapConfig(err, requestMapConfig) {
|
||||
assert.ifError(err);
|
||||
context.analysisConfiguration = {
|
||||
user: self.owner,
|
||||
db: {
|
||||
host: rendererParams.dbhost,
|
||||
port: rendererParams.dbport,
|
||||
dbname: rendererParams.dbname,
|
||||
user: rendererParams.dbuser,
|
||||
pass: rendererParams.dbpassword
|
||||
},
|
||||
batch: {
|
||||
username: self.owner,
|
||||
apiKey: apiKey
|
||||
}
|
||||
};
|
||||
self.mapConfigAdapter.getMapConfig(self.owner, requestMapConfig, rendererParams, context, this);
|
||||
},
|
||||
function prepareContextLimits(err, _mapConfig) {
|
||||
assert.ifError(err);
|
||||
mapConfig = _mapConfig;
|
||||
self.userLimitsBackend.getRenderLimits(self.owner, self.params.api_key, this);
|
||||
},
|
||||
function cacheAndReturnMapConfig(err, renderLimits) {
|
||||
self.err = err;
|
||||
self.mapConfig = (mapConfig === null) ? null : new MapConfig(mapConfig, context.datasource);
|
||||
self.analysesResults = context.analysesResults || [];
|
||||
self.rendererParams = rendererParams;
|
||||
self.context = context;
|
||||
self.context.limits = renderLimits || {};
|
||||
return callback(self.err, self.mapConfig, self.rendererParams, self.context);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
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 this.createKey(false);
|
||||
};
|
||||
|
||||
NamedMapMapConfigProvider.prototype.getCacheBuster = function() {
|
||||
return this.cacheBuster;
|
||||
};
|
||||
|
||||
NamedMapMapConfigProvider.prototype.reset = function() {
|
||||
this.template = null;
|
||||
|
||||
this.affectedTables = null;
|
||||
|
||||
this.err = null;
|
||||
this.mapConfig = null;
|
||||
|
||||
this.cacheBuster = Date.now();
|
||||
};
|
||||
|
||||
NamedMapMapConfigProvider.prototype.filter = function(key) {
|
||||
var regex = new RegExp('^' + this.createKey(true) + '.*');
|
||||
return key && key.match(regex);
|
||||
};
|
||||
const BaseMapConfigProvider = require('./base-mapconfig-adapter');
|
||||
const crypto = require('crypto');
|
||||
const dot = require('dot');
|
||||
const MapConfig = require('windshaft').model.MapConfig;
|
||||
const templateName = require('../../../backends/template_maps').templateName;
|
||||
|
||||
// Configure bases for cache keys suitable for string interpolation
|
||||
var baseKey = '{{=it.dbname}}:{{=it.owner}}:{{=it.templateName}}';
|
||||
var rendererKey = baseKey + ':{{=it.authToken}}:{{=it.configHash}}:{{=it.format}}:{{=it.layer}}:{{=it.scale_factor}}';
|
||||
const baseKey = '{{=it.dbname}}:{{=it.user}}:{{=it.templateName}}';
|
||||
const rendererKey = baseKey + ':{{=it.authToken}}:{{=it.configHash}}:{{=it.format}}:{{=it.layer}}:{{=it.scale_factor}}';
|
||||
|
||||
var baseKeyTpl = dot.template(baseKey);
|
||||
var rendererKeyTpl = dot.template(rendererKey);
|
||||
const baseKeyTpl = dot.template(baseKey);
|
||||
const rendererKeyTpl = dot.template(rendererKey);
|
||||
|
||||
NamedMapMapConfigProvider.prototype.createKey = function(base) {
|
||||
var tplValues = _.defaults({}, this.params, {
|
||||
dbname: '',
|
||||
owner: this.owner,
|
||||
templateName: this.templateName,
|
||||
authToken: this.authToken || '',
|
||||
configHash: configHash(this.config),
|
||||
layer: '',
|
||||
scale_factor: 1
|
||||
});
|
||||
return (base) ? baseKeyTpl(tplValues) : rendererKeyTpl(tplValues);
|
||||
module.exports = class NamedMapMapConfigProvider extends BaseMapConfigProvider {
|
||||
constructor (
|
||||
templateMaps,
|
||||
pgConnection,
|
||||
metadataBackend,
|
||||
userLimitsBackend,
|
||||
mapConfigAdapter,
|
||||
affectedTablesCache,
|
||||
user,
|
||||
templateId,
|
||||
config,
|
||||
authToken,
|
||||
params
|
||||
) {
|
||||
super();
|
||||
this.templateMaps = templateMaps;
|
||||
this.pgConnection = pgConnection;
|
||||
this.metadataBackend = metadataBackend;
|
||||
this.userLimitsBackend = userLimitsBackend;
|
||||
this.mapConfigAdapter = mapConfigAdapter;
|
||||
|
||||
this.user = user;
|
||||
this.templateName = templateName(templateId);
|
||||
this.config = config;
|
||||
this.authToken = authToken;
|
||||
this.params = params;
|
||||
|
||||
this.cacheBuster = Date.now();
|
||||
|
||||
// use template after call to mapConfig
|
||||
this.template = null;
|
||||
|
||||
this.affectedTablesCache = affectedTablesCache;
|
||||
|
||||
// providing
|
||||
this.err = null;
|
||||
this.mapConfig = null;
|
||||
this.rendererParams = null;
|
||||
this.context = {};
|
||||
this.analysesResults = [];
|
||||
}
|
||||
|
||||
getMapConfig (callback) {
|
||||
if (!!this.err || this.mapConfig !== null) {
|
||||
return callback(this.err, this.mapConfig, this.rendererParams, this.context);
|
||||
}
|
||||
|
||||
this.getContext((err, context) => {
|
||||
if (err) {
|
||||
this.err = err;
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
let templateParams = {};
|
||||
|
||||
if (this.config) {
|
||||
try {
|
||||
templateParams = Object.prototype.toString.call(this.config) === '[object String]' ?
|
||||
JSON.parse(this.config) :
|
||||
this.config;
|
||||
} catch (e) {
|
||||
const error = new Error('malformed config parameter, should be a valid JSON');
|
||||
this.err = error;
|
||||
|
||||
return callback(err);
|
||||
}
|
||||
}
|
||||
|
||||
context.templateParams = templateParams;
|
||||
|
||||
this.getTemplate((err, template) => {
|
||||
if (err) {
|
||||
this.err = err;
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
let requestMapConfig;
|
||||
|
||||
try {
|
||||
requestMapConfig = this.templateMaps.instance(template, templateParams);
|
||||
} catch (err) {
|
||||
this.err = err;
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
const { user, rendererParams } = this;
|
||||
|
||||
this.mapConfigAdapter.getMapConfig(
|
||||
user, requestMapConfig, rendererParams, context, (err, mapConfig) => {
|
||||
if (err) {
|
||||
this.err = err;
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
this.mapConfig = (mapConfig === null) ? null : new MapConfig(mapConfig, context.datasource);
|
||||
this.analysesResults = context.analysesResults || [];
|
||||
|
||||
return callback(null, this.mapConfig, this.rendererParams, this.context);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
getContext (callback) {
|
||||
this.getDBParams(this.user, (err, rendererParams) => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
this.rendererParams = rendererParams;
|
||||
|
||||
this.metadataBackend.getUserMapKey(this.user, (err, apiKey) => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
const context = {};
|
||||
|
||||
context.analysisConfiguration = {
|
||||
user: this.user,
|
||||
db: {
|
||||
host: rendererParams.dbhost,
|
||||
port: rendererParams.dbport,
|
||||
dbname: rendererParams.dbname,
|
||||
user: rendererParams.dbuser,
|
||||
pass: rendererParams.dbpassword
|
||||
},
|
||||
batch: {
|
||||
username: this.user,
|
||||
apiKey: apiKey
|
||||
}
|
||||
};
|
||||
|
||||
this.userLimitsBackend.getRenderLimits(this.user, this.params.api_key, (err, renderLimits) => {
|
||||
if (err) {
|
||||
this.err = err;
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
context.limits = renderLimits || {};
|
||||
|
||||
this.context = context;
|
||||
|
||||
return callback(null, context);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
getTemplate (callback) {
|
||||
if (!!this.err || this.template !== null) {
|
||||
return callback(this.err, this.template);
|
||||
}
|
||||
|
||||
this.templateMaps.getTemplate(this.user, this.templateName, (err, tpl) => {
|
||||
if (err) {
|
||||
this.err = err;
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
if (!tpl) {
|
||||
const error = new Error(`Template '${this.templateName}' of user '${this.user}' not found`);
|
||||
error.http_status = 404;
|
||||
|
||||
this.err = error;
|
||||
|
||||
return callback(error);
|
||||
}
|
||||
|
||||
let authorized = false;
|
||||
|
||||
try {
|
||||
authorized = this.templateMaps.isAuthorized(tpl, this.authToken);
|
||||
} catch (err) {
|
||||
const error = new Error('Failed to authorize template');
|
||||
error.http_status = 403;
|
||||
|
||||
this.err = error;
|
||||
|
||||
return callback(error);
|
||||
}
|
||||
|
||||
if (!authorized) {
|
||||
const error = new Error('Unauthorized template instantiation');
|
||||
error.http_status = 403;
|
||||
this.err = error;
|
||||
|
||||
return callback(error);
|
||||
}
|
||||
|
||||
this.template = tpl;
|
||||
|
||||
return callback(null, this.template);
|
||||
});
|
||||
}
|
||||
|
||||
getKey () {
|
||||
return this.createKey(false);
|
||||
}
|
||||
|
||||
getCacheBuster () {
|
||||
return this.cacheBuster;
|
||||
}
|
||||
|
||||
reset () {
|
||||
this.template = null;
|
||||
|
||||
this.affectedTables = null;
|
||||
|
||||
this.err = null;
|
||||
this.mapConfig = null;
|
||||
|
||||
this.cacheBuster = Date.now();
|
||||
}
|
||||
|
||||
filter (key) {
|
||||
const regex = new RegExp('^' + this.createKey(true) + '.*');
|
||||
return key && key.match(regex);
|
||||
}
|
||||
|
||||
createKey (base) {
|
||||
const tplValues = Object.assign({
|
||||
dbname: '',
|
||||
user: this.user,
|
||||
templateName: this.templateName,
|
||||
authToken: this.authToken || '',
|
||||
configHash: configHash(this.config),
|
||||
layer: '',
|
||||
scale_factor: 1
|
||||
}, this.params);
|
||||
|
||||
return (base) ? baseKeyTpl(tplValues) : rendererKeyTpl(tplValues);
|
||||
}
|
||||
|
||||
getDBParams (cdbuser, callback) {
|
||||
const dbParams = Object.assign({ user: this.user }, this.params);
|
||||
|
||||
this.pgConnection.getDatabaseParams(cdbuser, (err, databaseParams) => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
dbParams.dbuser = databaseParams.dbuser;
|
||||
dbParams.dbpass = databaseParams.dbpass;
|
||||
dbParams.dbhost = databaseParams.dbhost;
|
||||
dbParams.dbport = databaseParams.dbport;
|
||||
dbParams.dbname = databaseParams.dbname;
|
||||
|
||||
return callback(null, dbParams);
|
||||
});
|
||||
}
|
||||
|
||||
getTemplateName () {
|
||||
return this.templateName;
|
||||
}
|
||||
};
|
||||
|
||||
function configHash(config) {
|
||||
if (!config) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return crypto.createHash('md5').update(JSON.stringify(config)).digest('hex').substring(0,8);
|
||||
}
|
||||
|
||||
module.exports.configHash = configHash;
|
||||
|
||||
NamedMapMapConfigProvider.prototype.setDBParams = function(cdbuser, params, callback) {
|
||||
this.pgConnection.getDatabaseParams(cdbuser, (err, databaseParams) => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
params.dbuser = databaseParams.dbuser;
|
||||
params.dbpass = databaseParams.dbpass;
|
||||
params.dbhost = databaseParams.dbhost;
|
||||
params.dbport = databaseParams.dbport;
|
||||
params.dbname = databaseParams.dbname;
|
||||
|
||||
callback();
|
||||
});
|
||||
};
|
||||
|
||||
NamedMapMapConfigProvider.prototype.getTemplateName = function() {
|
||||
return this.templateName;
|
||||
};
|
||||
|
||||
NamedMapMapConfigProvider.prototype.createAffectedTables = function(callback) {
|
||||
this.getMapConfig((err, mapConfig) => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
const { dbname } = this.rendererParams;
|
||||
const token = mapConfig.id();
|
||||
|
||||
const queries = [];
|
||||
|
||||
mapConfig.getLayers().forEach(layer => {
|
||||
queries.push(layer.options.sql);
|
||||
if (layer.options.affected_tables) {
|
||||
layer.options.affected_tables.map(table => {
|
||||
queries.push(`SELECT * FROM ${table} LIMIT 0`);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const sql = queries.length ? queries.join(';') : null;
|
||||
|
||||
if (!sql) {
|
||||
return callback();
|
||||
}
|
||||
|
||||
this.pgConnection.getConnection(this.owner, (err, connection) => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
QueryTables.getAffectedTablesFromQuery(connection, sql, (err, affectedTables) => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
this.affectedTablesCache.set(dbname, token, affectedTables);
|
||||
|
||||
callback(err, affectedTables);
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
NamedMapMapConfigProvider.prototype.getAffectedTables = function (callback) {
|
||||
this.getMapConfig((err, mapConfig) => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
const { dbname } = this.params;
|
||||
const token = mapConfig.id();
|
||||
|
||||
if (this.affectedTablesCache.hasAffectedTables(dbname, token)) {
|
||||
const affectedTables = this.affectedTablesCache.get(dbname, token);
|
||||
return callback(null, affectedTables);
|
||||
}
|
||||
|
||||
return this.createAffectedTables(callback);
|
||||
});
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user