Windshaft-cartodb/lib/models/mapconfig/provider/named-map-provider.js

272 lines
8.1 KiB
JavaScript
Raw Normal View History

'use strict';
const BaseMapConfigProvider = require('./base-mapconfig-adapter');
2018-05-31 01:50:07 +08:00
const crypto = require('crypto');
const dot = require('dot');
const MapConfig = require('windshaft').model.MapConfig;
2019-10-07 16:50:14 +08:00
const templateName = require('../../../backends/template-maps').templateName;
2018-05-31 02:02:38 +08:00
// Configure bases for cache keys suitable for string interpolation
const baseKey = '{{=it.dbname}}:{{=it.user}}:{{=it.templateName}}';
2018-05-31 02:02:38 +08:00
const rendererKey = baseKey + ':{{=it.authToken}}:{{=it.configHash}}:{{=it.format}}:{{=it.layer}}:{{=it.scale_factor}}';
2018-05-31 02:02:38 +08:00
const baseKeyTpl = dot.template(baseKey);
const rendererKeyTpl = dot.template(rendererKey);
module.exports = class NamedMapMapConfigProvider extends BaseMapConfigProvider {
2018-05-31 02:02:38 +08:00
constructor (
templateMaps,
pgConnection,
metadataBackend,
userLimitsBackend,
mapConfigAdapter,
affectedTablesCache,
user,
2018-05-31 02:02:38 +08:00
templateId,
config,
authToken,
params
) {
super();
2018-05-31 02:02:38 +08:00
this.templateMaps = templateMaps;
this.pgConnection = pgConnection;
this.metadataBackend = metadataBackend;
this.userLimitsBackend = userLimitsBackend;
this.mapConfigAdapter = mapConfigAdapter;
this.user = user;
2018-05-31 02:02:38 +08:00
this.templateName = templateName(templateId);
this.config = config;
this.authToken = authToken;
this.params = params;
// FIXME: why is this different thah that:
// this.cacheBuster = params.cache_buster || 0;
// test: "should fail to use template from named map provider after template deletion"
// check named-map-provider-cache invalidation
2018-05-31 02:02:38 +08:00
this.cacheBuster = Date.now();
// use template after call to mapConfig
this.template = null;
this.affectedTablesCache = affectedTablesCache;
// providing
this.mapConfig = null;
this.rendererParams = null;
this.context = {};
this.analysesResults = [];
}
2018-05-31 02:02:38 +08:00
getMapConfig (callback) {
if (this.mapConfig !== null) {
return callback(null, this.mapConfig, this.rendererParams, this.context);
}
2018-05-31 02:02:38 +08:00
this.getContext((err, context) => {
if (err) {
return callback(err);
}
2018-05-31 02:02:38 +08:00
let templateParams = {};
2018-05-31 02:02:38 +08:00
if (this.config) {
try {
2019-10-22 01:07:24 +08:00
templateParams = Object.prototype.toString.call(this.config) === '[object String]'
? JSON.parse(this.config)
: this.config;
2018-05-31 02:02:38 +08:00
} catch (e) {
const err = new Error('malformed config parameter, should be a valid JSON');
2018-05-31 02:02:38 +08:00
return callback(err);
}
}
2018-05-31 02:02:38 +08:00
context.templateParams = templateParams;
2018-05-31 01:43:23 +08:00
2018-05-31 02:02:38 +08:00
this.getTemplate((err, template) => {
if (err) {
return callback(err);
}
2018-05-31 02:02:38 +08:00
let requestMapConfig;
2018-05-31 02:02:38 +08:00
try {
requestMapConfig = this.templateMaps.instance(template, templateParams);
} catch (err) {
return callback(err);
}
const { user, rendererParams } = this;
2018-05-31 02:02:38 +08:00
this.mapConfigAdapter.getMapConfig(
user, requestMapConfig, rendererParams, context, (err, mapConfig, stats = {}) => {
2019-10-22 01:07:24 +08:00
if (err) {
return callback(err);
}
2019-10-22 01:07:24 +08:00
this.mapConfig = (mapConfig === null) ? null : new MapConfig(mapConfig, context.datasource);
this.analysesResults = context.analysesResults || [];
2018-05-31 01:07:28 +08:00
2019-10-22 01:07:24 +08:00
return callback(null, this.mapConfig, this.rendererParams, this.context, stats);
});
2018-05-31 02:02:38 +08:00
});
});
}
getContext (callback) {
this.getDBParams(this.user, (err, rendererParams) => {
2018-05-31 00:15:51 +08:00
if (err) {
return callback(err);
}
2018-05-31 02:02:38 +08:00
this.rendererParams = rendererParams;
2018-05-31 00:15:51 +08:00
this.metadataBackend.getUserMapKey(this.user, (err, apiKey) => {
2018-05-31 01:07:28 +08:00
if (err) {
return callback(err);
}
2018-05-31 00:15:51 +08:00
2018-05-31 02:02:38 +08:00
const context = {};
context.analysisConfiguration = {
user: this.user,
2018-05-31 02:02:38 +08:00
db: {
host: rendererParams.dbhost,
port: rendererParams.dbport,
dbname: rendererParams.dbname,
user: rendererParams.dbuser,
pass: rendererParams.dbpassword
},
batch: {
username: this.user,
2019-11-14 18:36:47 +08:00
apiKey
2018-05-31 02:02:38 +08:00
}
};
this.userLimitsBackend.getRenderLimits(this.user, this.params.api_key, (err, renderLimits) => {
2018-05-31 02:02:38 +08:00
if (err) {
return callback(err);
}
context.limits = renderLimits || {};
this.context = context;
return callback(null, context);
});
2018-05-31 00:15:51 +08:00
});
});
}
2018-05-31 02:02:38 +08:00
getTemplate (callback) {
if (this.template !== null) {
return callback(null, this.template);
2018-05-31 00:15:51 +08:00
}
this.templateMaps.getTemplate(this.user, this.templateName, (err, tpl) => {
2018-05-31 02:02:38 +08:00
if (err) {
return callback(err);
}
2018-05-31 00:15:51 +08:00
2018-05-31 02:02:38 +08:00
if (!tpl) {
const error = new Error(`Template '${this.templateName}' of user '${this.user}' not found`);
2018-05-31 02:02:38 +08:00
error.http_status = 404;
2018-05-31 00:15:51 +08:00
2018-05-31 02:02:38 +08:00
return callback(error);
}
2018-05-31 00:15:51 +08:00
2018-05-31 02:02:38 +08:00
let authorized = false;
2018-05-31 00:15:51 +08:00
2018-05-31 02:02:38 +08:00
try {
authorized = this.templateMaps.isAuthorized(tpl, this.authToken);
} catch (err) {
const error = new Error('Failed to authorize template');
error.http_status = 403;
2018-05-31 00:15:51 +08:00
2018-05-31 02:02:38 +08:00
return callback(error);
}
2018-05-31 00:15:51 +08:00
2018-05-31 02:02:38 +08:00
if (!authorized) {
const error = new Error('Unauthorized template instantiation');
error.http_status = 403;
2018-05-31 00:15:51 +08:00
2018-05-31 02:02:38 +08:00
return callback(error);
}
2018-05-31 00:15:51 +08:00
2018-05-31 02:02:38 +08:00
this.template = tpl;
2018-05-31 02:02:38 +08:00
return callback(null, this.template);
});
}
2018-05-31 02:02:38 +08:00
getKey () {
return this.createKey(false);
}
2018-05-31 02:02:38 +08:00
getCacheBuster () {
return this.cacheBuster;
}
2018-05-31 02:02:38 +08:00
reset () {
this.template = null;
2018-05-31 02:02:38 +08:00
this.affectedTables = null;
2018-05-31 02:02:38 +08:00
this.mapConfig = null;
2018-05-31 02:02:38 +08:00
this.cacheBuster = Date.now();
}
2018-05-31 02:02:38 +08:00
filter (key) {
const regex = new RegExp('^' + this.createKey(true) + '.*');
return key && key.match(regex);
}
2018-05-31 02:02:38 +08:00
createKey (base) {
const {
dbname = '',
user = this.user,
templateName = this.templateName,
authToken = this.authToken || '',
configHash = createConfigHash(this.config),
layer = '',
scale_factor = 1
} = this.params;
const tplValues = { dbname, user, templateName, authToken, configHash, layer, scale_factor };
2018-05-31 02:02:38 +08:00
return (base) ? baseKeyTpl(tplValues) : rendererKeyTpl(tplValues);
}
2018-05-31 02:02:38 +08:00
getDBParams (cdbuser, callback) {
const dbParams = Object.assign({ user: this.user }, this.params);
2018-05-31 02:02:38 +08:00
this.pgConnection.getDatabaseParams(cdbuser, (err, databaseParams) => {
if (err) {
return callback(err);
}
dbParams.dbuser = databaseParams.dbuser;
2019-11-12 20:12:01 +08:00
dbParams.dbpassword = databaseParams.dbpassword;
2018-05-31 02:02:38 +08:00
dbParams.dbhost = databaseParams.dbhost;
dbParams.dbport = databaseParams.dbport;
dbParams.dbname = databaseParams.dbname;
return callback(null, dbParams);
});
}
2018-05-31 02:02:38 +08:00
getTemplateName () {
return this.templateName;
}
};
2018-04-06 18:59:53 +08:00
2019-10-22 01:07:24 +08:00
function createConfigHash (config) {
2018-05-31 02:02:38 +08:00
if (!config) {
return '';
}
2018-04-06 18:59:53 +08:00
2019-10-22 01:07:24 +08:00
return crypto.createHash('md5').update(JSON.stringify(config)).digest('hex').substring(0, 8);
2018-05-31 02:02:38 +08:00
}
2018-04-06 18:59:53 +08:00
module.exports.configHash = createConfigHash;