Compare commits

...

35 Commits
3.2.0 ... 3.5.1

Author SHA1 Message Date
Daniel García Aubert
a386abf5a5 Release 3.5.1 2017-04-11 18:34:18 +02:00
Daniel
e5c2c35a81 Merge pull request #662 from CartoDB/upgrade-camshaft-to-0.53.1
Upgrade camshaft to 0.53.1
2017-04-11 18:31:49 +02:00
Daniel García Aubert
227112c7aa Update yarn.lock 2017-04-11 18:27:54 +02:00
Daniel García Aubert
a4ed37bdfc Upgrade camshaft to 0.53.1 2017-04-11 18:19:39 +02:00
Daniel García Aubert
c6a62cee61 Stubs next version 2017-04-10 16:13:30 +02:00
Daniel García Aubert
891bc818b2 Release 3.5.0 2017-04-10 12:20:44 +02:00
Daniel
ebe25d6f20 Merge pull request #638 from CartoDB/629_fix_cache_invalidation
Fix cache invalidation problem with affected tables
2017-04-10 11:55:57 +02:00
Daniel García Aubert
92ec17218b Upgrade camshaft to 0.53.0 2017-04-10 11:36:17 +02:00
Mario de Frutos
e8a0f6b7b6 Point to camshaft branch to test properly 2017-04-07 16:00:29 +02:00
Mario de Frutos
125c39967c Make the cache headers tests idempotent 2017-04-07 15:59:58 +02:00
Mario de Frutos
4132bc755d Add cdb_invalidate_varnish function fixture to tests 2017-04-07 15:59:58 +02:00
Mario de Frutos
9707881bf9 Include check for surrogate-key header and renamed the test file 2017-04-07 15:59:58 +02:00
Mario de Frutos
fa6493ae44 Affected tables are now included in X-Cache-Channel 2017-04-07 15:59:58 +02:00
Mario de Frutos
0c387cf6d9 Add more tests for x-cache-channel but with analysis 2017-04-07 15:59:58 +02:00
Mario de Frutos
5e4d1d5c1c Get affected tables and add it to the layergroup 2017-04-07 15:59:58 +02:00
Raul Ochoa
4d82fd65f6 Merge pull request #659 from CartoDB/upgrade-camshaft
Upgrades camshaft to 0.52.0
2017-04-07 14:41:14 +02:00
Raul Ochoa
6d3644f13b Upgrades camshaft to 0.52.0 2017-04-07 13:29:30 +02:00
Raul Ochoa
7a5aa7ba35 Stubs next version 2017-04-03 16:22:15 +02:00
Raul Ochoa
9c9609eb2b Release 3.4.0 2017-04-03 16:21:29 +02:00
Raul Ochoa
418d3c074f Merge pull request #657 from CartoDB/upgrade-camshaft
Upgrade camshaft to 0.51.0
2017-04-03 16:15:48 +02:00
Raul Ochoa
6bbda3d41e Upgrade camshaft to 0.51.0 2017-04-03 15:53:39 +02:00
Raul Ochoa
25669bb3f2 Stubs next version 2017-04-03 13:07:53 +02:00
Raul Ochoa
508d495a23 Release 3.3.0 2017-04-03 13:07:05 +02:00
Raul Ochoa
06427dc009 Bump version and add news 2017-04-03 13:06:37 +02:00
Raul Ochoa
c325df1414 Merge pull request #655 from CartoDB/static-maps-layers-filter
Static maps layers filter
2017-04-03 12:07:38 +02:00
Raul Ochoa
07447160e3 Merge pull request #654 from CartoDB/non-conditional-profiler
Remove all conditional branches to call req.profiler
2017-04-03 11:58:38 +02:00
Raul Ochoa
ededc73fd7 Throw on invalid params argument 2017-03-31 18:39:29 +02:00
Raul Ochoa
cad02bfad7 Remove all conditional branches to call req.profiler
req.profiler is created in a middleware for all requests.
2017-03-30 20:31:53 +02:00
Raul Ochoa
94299f0452 Configure extra allowed params per endpoint via middleware
Instead of making all params available in all endpoints, we control
what endpoints allow what extra params.

Dataviews endpoints should be migrated to this.
2017-03-30 20:12:55 +02:00
Raul Ochoa
ae5d82c41d Add test to go red 2017-03-30 20:12:20 +02:00
Raul Ochoa
6468822295 Remove layer param before creating a better solution 2017-03-30 20:08:45 +02:00
Raul Ochoa
777ae31426 Merge branch 'master' into static-maps-layers-filter 2017-03-30 19:33:59 +02:00
Raul Ochoa
1ca56fb81c Update news 2017-03-30 17:52:25 +02:00
Raul Ochoa
5d74e1eafe Stubs next version 2017-03-30 14:33:41 +02:00
Raul Ochoa
8694c120bc Allow to overwrite layers filter in static maps images 2017-03-15 11:00:10 +01:00
18 changed files with 565 additions and 394 deletions

31
NEWS.md
View File

@@ -1,11 +1,42 @@
# Changelog
## 3.5.1
Released 2017-04-11
Announcements:
- Upgrades camshaft to [0.53.1](https://github.com/CartoDB/camshaft/releases/tag/0.53.1).
## 3.5.0
Released 2017-04-10
Bug fixes:
- Fix invalidation of cache for maps with analyses #638.
Announcements:
- Upgrades camshaft to [0.53.0](https://github.com/CartoDB/camshaft/releases/tag/0.53.0).
## 3.4.0
Released 2017-04-03
Announcements:
- Upgrades camshaft to [0.51.0](https://github.com/CartoDB/camshaft/releases/tag/0.51.0).
## 3.3.0
Released 2017-04-03
New features:
- Static map endpoints allow specifying the layers to render #653.
## 3.2.0
Released 2017-03-30
Announcements:
- Upgrades windshaft to [3.1.0](https://github.com/CartoDB/windshaft/releases/tag/3.1.0).
- Active GC interval.
## 3.1.1

View File

@@ -95,9 +95,7 @@ AuthApi.prototype.authorize = function(req, callback) {
self.authorizedByAPIKey(user, req, this);
},
function checkApiKey(err, authorized){
if (req.profiler) {
req.profiler.done('authorizedByAPIKey');
}
req.profiler.done('authorizedByAPIKey');
assert.ifError(err);
// if not authorized by api_key, continue
@@ -131,9 +129,7 @@ AuthApi.prototype.authorize = function(req, callback) {
}
self.pgConnection.setDBAuth(user, req.params, function(err) {
if (req.profiler) {
req.profiler.done('setDBAuth');
}
req.profiler.done('setDBAuth');
callback(err, true); // authorized (or error)
});
}

View File

@@ -36,7 +36,7 @@ function BaseController(authApi, pgConnection) {
module.exports = BaseController;
// jshint maxcomplexity:9
// jshint maxcomplexity:10
/**
* Whitelist input and get database name & default geometry type from
* subdomain/user metadata held in CartoDB Redis
@@ -61,9 +61,7 @@ BaseController.prototype.req2params = function(req, callback){
lzmaWorker.decompress(
lzma,
function(result) {
if (req.profiler) {
req.profiler.done('lzma');
}
req.profiler.done('lzma');
try {
delete req.query.lzma;
_.extend(req.query, JSON.parse(result));
@@ -77,7 +75,11 @@ BaseController.prototype.req2params = function(req, callback){
return;
}
req.query = _.pick(req.query, REQUEST_QUERY_PARAMS_WHITELIST);
var allowedQueryParams = REQUEST_QUERY_PARAMS_WHITELIST;
if (Array.isArray(req.context.allowedQueryParams)) {
allowedQueryParams = allowedQueryParams.concat(req.context.allowedQueryParams);
}
req.query = _.pick(req.query, allowedQueryParams);
req.params = _.extend({}, req.params); // shuffle things as request is a strange array/object
var user = req.context.user;
@@ -115,18 +117,14 @@ BaseController.prototype.req2params = function(req, callback){
// bring all query values onto req.params object
_.extend(req.params, req.query);
if (req.profiler) {
req.profiler.done('req2params.setup');
}
req.profiler.done('req2params.setup');
step(
function getPrivacy(){
self.authApi.authorize(req, this);
},
function validateAuthorization(err, authorized) {
if (req.profiler) {
req.profiler.done('authorize');
}
req.profiler.done('authorize');
assert.ifError(err);
if(!authorized) {
err = new Error("Sorry, you are unauthorized (permission denied)");
@@ -167,9 +165,7 @@ BaseController.prototype.send = function(req, res, body, status, headers) {
res.set('X-Served-By-DB-Host', req.params.dbhost);
}
if (req.profiler) {
res.set('X-Tiler-Profiler', req.profiler.toJSONString());
}
res.set('X-Tiler-Profiler', req.profiler.toJSONString());
if (headers) {
res.set(headers);
@@ -187,14 +183,12 @@ BaseController.prototype.send = function(req, res, body, status, headers) {
res.send(body);
}
if (req.profiler) {
try {
// May throw due to dns, see
// See http://github.com/CartoDB/Windshaft/issues/166
req.profiler.sendStats();
} catch (err) {
debug("error sending profiling stats: " + err);
}
try {
// May throw due to dns, see
// See http://github.com/CartoDB/Windshaft/issues/166
req.profiler.sendStats();
} catch (err) {
debug("error sending profiling stats: " + err);
}
};
// jshint maxcomplexity:6

View File

@@ -6,6 +6,7 @@ var BaseController = require('./base');
var cors = require('../middleware/cors');
var userMiddleware = require('../middleware/user');
var allowQueryParams = require('../middleware/allow-query-params');
var DataviewBackend = require('../backends/dataview');
var AnalysisStatusBackend = require('../backends/analysis-status');
@@ -67,11 +68,13 @@ LayergroupController.prototype.register = function(app) {
this.attributes.bind(this));
app.get(app.base_url_mapconfig +
'/static/center/:token/:z/:lat/:lng/:width/:height.:format', cors(), userMiddleware,
'/static/center/:token/:z/:lat/:lng/:width/:height.:format',
cors(), userMiddleware, allowQueryParams(['layer']),
this.center.bind(this));
app.get(app.base_url_mapconfig +
'/static/bbox/:token/:west,:south,:east,:north/:width/:height.:format', cors(), userMiddleware,
'/static/bbox/:token/:west,:south,:east,:north/:width/:height.:format',
cors(), userMiddleware, allowQueryParams(['layer']),
this.bbox.bind(this));
// Undocumented/non-supported API endpoint methods.
@@ -388,13 +391,15 @@ LayergroupController.prototype.getAffectedTables = function(user, dbName, layerg
function getSQL(err, mapConfig) {
assert.ifError(err);
var queries = mapConfig.getLayers()
.map(function(lyr) {
return lyr.options.sql;
})
.filter(function(sql) {
return !!sql;
});
var queries = [];
mapConfig.getLayers().forEach(function(layer) {
queries.push(layer.options.sql);
if (layer.options.affected_tables) {
layer.options.affected_tables.map(function(table) {
queries.push('SELECT * FROM ' + table + ' LIMIT 0');
});
}
});
return queries.length ? queries.join(';') : null;
},

View File

@@ -87,9 +87,7 @@ MapController.prototype.createPost = function(req, res) {
};
MapController.prototype.instantiate = function(req, res) {
if (req.profiler) {
req.profiler.start('windshaft-cartodb.instance_template_post');
}
req.profiler.start('windshaft-cartodb.instance_template_post');
this.instantiateTemplate(req, res, function prepareTemplateParams(callback) {
if (!req.is('application/json')) {
@@ -100,9 +98,7 @@ MapController.prototype.instantiate = function(req, res) {
};
MapController.prototype.jsonp = function(req, res) {
if (req.profiler) {
req.profiler.start('windshaft-cartodb.instance_template_get');
}
req.profiler.start('windshaft-cartodb.instance_template_get');
this.instantiateTemplate(req, res, function prepareJsonTemplateParams(callback) {
var err = null;
@@ -303,18 +299,22 @@ MapController.prototype.afterLayergroupCreate = function(req, res, mapconfig, la
// take place before proceeding. Error will be logged
// asynchronously
this.metadataBackend.incMapviewCount(username, mapconfig.obj().stat_tag, function(err) {
if (req.profiler) {
req.profiler.done('incMapviewCount');
}
req.profiler.done('incMapviewCount');
if ( err ) {
global.logger.log("ERROR: failed to increment mapview count for user '" + username + "': " + err);
}
done();
});
var sql = mapconfig.getLayers().map(function(layer) {
return layer.options.sql;
}).join(';');
var sql = [];
mapconfig.getLayers().forEach(function(layer) {
sql.push(layer.options.sql);
if (layer.options.affected_tables) {
layer.options.affected_tables.map(function(table) {
sql.push('SELECT * FROM ' + table + ' LIMIT 0');
});
}
});
var dbName = req.params.dbname;
var layergroupId = layergroup.layergroupid;
@@ -325,12 +325,10 @@ MapController.prototype.afterLayergroupCreate = function(req, res, mapconfig, la
},
function getAffectedTablesAndLastUpdatedTime(err, connection) {
assert.ifError(err);
QueryTables.getAffectedTablesFromQuery(connection, sql, this);
QueryTables.getAffectedTablesFromQuery(connection, sql.join(';'), this);
},
function handleAffectedTablesAndLastUpdatedTime(err, result) {
if (req.profiler) {
req.profiler.done('queryTablesAndLastUpdated');
}
req.profiler.done('queryTablesAndLastUpdated');
assert.ifError(err);
// feed affected tables cache so it can be reused from, for instance, layergroup controller
self.layergroupAffectedTables.set(dbName, layergroupId, result);

View File

@@ -8,6 +8,7 @@ var BaseController = require('./base');
var cors = require('../middleware/cors');
var userMiddleware = require('../middleware/user');
var allowQueryParams = require('../middleware/allow-query-params');
function NamedMapsController(authApi, pgConnection, namedMapProviderCache, tileBackend, previewBackend,
surrogateKeysCache, tablesExtentApi, metadataBackend) {
@@ -31,7 +32,7 @@ NamedMapsController.prototype.register = function(app) {
this.tile.bind(this));
app.get(app.base_url_mapconfig +
'/static/named/:template_id/:width/:height.:format', cors(), userMiddleware,
'/static/named/:template_id/:width/:height.:format', cors(), userMiddleware, allowQueryParams(['layer']),
this.staticMap.bind(this));
};
@@ -100,9 +101,7 @@ NamedMapsController.prototype.tile = function(req, res) {
self.tileBackend.getTile(namedMapProvider, req.params, this);
},
function handleImage(err, tile, headers, stats) {
if (req.profiler) {
req.profiler.add(stats);
}
req.profiler.add(stats);
if (err) {
self.sendError(req, res, err, 'NAMED_MAP_TILE');
} else {
@@ -176,10 +175,8 @@ NamedMapsController.prototype.staticMap = function(req, res) {
});
},
function handleImage(err, image, headers, stats) {
if (req.profiler) {
req.profiler.done('render-' + format);
req.profiler.add(stats || {});
}
req.profiler.done('render-' + format);
req.profiler.add(stats || {});
if (err) {
self.sendError(req, res, err, 'STATIC_VIZ_MAP');

View File

@@ -91,9 +91,7 @@ NamedMapsAdminController.prototype.update = function(req, res) {
NamedMapsAdminController.prototype.retrieve = function(req, res) {
var self = this;
if (req.profiler) {
req.profiler.start('windshaft-cartodb.get_template');
}
req.profiler.start('windshaft-cartodb.get_template');
var cdbuser = req.context.user;
var tpl_id;
@@ -127,9 +125,7 @@ NamedMapsAdminController.prototype.retrieve = function(req, res) {
NamedMapsAdminController.prototype.destroy = function(req, res) {
var self = this;
if (req.profiler) {
req.profiler.start('windshaft-cartodb.delete_template');
}
req.profiler.start('windshaft-cartodb.delete_template');
var cdbuser = req.context.user;
var tpl_id;
@@ -154,9 +150,7 @@ NamedMapsAdminController.prototype.destroy = function(req, res) {
NamedMapsAdminController.prototype.list = function(req, res) {
var self = this;
if ( req.profiler ) {
req.profiler.start('windshaft-cartodb.get_template_list');
}
req.profiler.start('windshaft-cartodb.get_template_list');
var cdbuser = req.context.user;

View File

@@ -0,0 +1,9 @@
module.exports = function allowQueryParams(params) {
if (!Array.isArray(params)) {
throw new Error('allowQueryParams must receive an Array of params');
}
return function allowQueryParamsMiddleware(req, res, next) {
req.context.allowedQueryParams = params;
next();
};
};

View File

@@ -115,6 +115,7 @@ AnalysisMapConfigAdapter.prototype.getMapConfig = function(user, requestMapConfi
}
layer.options.sql = analysisSql;
layer.options.columns = getDataviewsColumns(getLayerDataviews(layer, dataviews));
layer.options.affected_tables = getAllAffectedTablesFromSourceNodes(layerNode);
} else {
missingNodesErrors.push(
new Error('Missing analysis node.id="' + layerSourceId +'" for layer='+layerIndex)
@@ -330,4 +331,13 @@ function AnalysisError(message) {
this.message = message;
}
function getAllAffectedTablesFromSourceNodes(node) {
var affectedTables = node.getAllInputNodes(function (node) {
return node.getType() === 'source';
}).reduce(function(list, node) {
return list.concat(node.getAffectedTables());
},[]);
return affectedTables;
}
require('util').inherits(AnalysisError, Error);

View File

@@ -1,7 +1,7 @@
{
"private": true,
"name": "windshaft-cartodb",
"version": "3.2.0",
"version": "3.5.1",
"description": "A map tile server for CartoDB",
"keywords": [
"cartodb"
@@ -20,7 +20,7 @@
],
"dependencies": {
"body-parser": "~1.14.0",
"camshaft": "0.50.3",
"camshaft": "0.53.1",
"cartodb-psql": "~0.7.1",
"cartodb-query-tables": "0.2.0",
"cartodb-redis": "0.13.2",

393
test/acceptance/cache/cache_headers.js vendored Normal file
View File

@@ -0,0 +1,393 @@
var testHelper = require('../../support/test_helper');
var assert = require('../../support/assert');
var qs = require('querystring');
var CartodbWindshaft = require('../../../lib/cartodb/server');
var serverOptions = require('../../../lib/cartodb/server_options');
var server = new CartodbWindshaft(serverOptions);
server.setMaxListeners(0);
var LayergroupToken = require('../../support/layergroup-token');
describe('get requests with cache headers', function() {
var keysToDelete;
beforeEach(function() {
keysToDelete = {};
});
afterEach(function(done) {
testHelper.deleteRedisKeys(keysToDelete, done);
});
var statusOkResponse = {
status: 200
};
var mapConfigs = [
{
"description": "cache headers should be present",
"cache_headers": {
"x_cache_channel": {
"db_name": "test_windshaft_cartodb_user_1_db",
"tables": ["public.test_table"]
},
"surrogate_keys": "t:77pJnX"
},
"data":
{
version: '1.5.0',
layers: [
{
options: {
source: {
id: "2570e105-7b37-40d2-bdf4-1af889598745"
},
sql: 'select * from test_table limit 2',
cartocss: '#layer { marker-fill:red; }',
cartocss_version: '2.3.0',
attributes: {
id:'cartodb_id',
columns: [
'name',
'address'
]
}
}
}
],
analyses: [
{
"id": "2570e105-7b37-40d2-bdf4-1af889598745",
"type": "source",
"params": {
"query": "select * from test_table limit 2"
}
}
]
},
},
{
"description": "cache headers should be present and be composed with source table name",
"cache_headers": {
"x_cache_channel": {
"db_name": "test_windshaft_cartodb_user_1_db",
"tables": ["public.analysis_2f13a3dbd7_9eb239903a1afd8a69130d1ece0fc8b38de8592d",
"public.test_table"]
},
"surrogate_keys": "t:77pJnX t:iL4eth"
},
"data":
{
version: '1.5.0',
layers: [
{
options: {
source: {
id: "2570e105-7b37-40d2-bdf4-1af889598745"
},
sql: 'select * from test_table limit 2',
cartocss: '#layer { marker-fill:red; }',
cartocss_version: '2.3.0',
attributes: {
id:'cartodb_id',
columns: [
'name',
'address'
]
}
}
}
],
analyses: [
{
"id": "2570e105-7b37-40d2-bdf4-1af889598745",
"type": "buffer",
"params": {
"source": {
"type": "source",
"params": {
"query": "select * from test_table limit 2"
}
},
"radius": 50000
}
}
]
}
}];
var layergroupRequest = function(mapConfig) {
return {
url: '/api/v1/map?api_key=1234&config=' + encodeURIComponent(JSON.stringify(mapConfig)),
method: 'GET',
headers: {
host: 'localhost'
}
};
};
function getRequest(url, addApiKey, callbackName) {
var params = {};
if (!!addApiKey) {
params.api_key = '1234';
}
if (!!callbackName) {
params.callback = callbackName;
}
return {
url: url + '?' + qs.stringify(params),
method: 'GET',
headers: {
host: 'localhost',
'Content-Type': 'application/json'
}
};
}
function validateCacheHeaders(done, expectedCacheHeaders) {
return function(res, err) {
if (err) {
return done(err);
}
assert.ok(res.headers['x-cache-channel']);
assert.ok(res.headers['surrogate-key']);
if (expectedCacheHeaders) {
validateXChannelHeaders(res.headers, expectedCacheHeaders);
assert.equal(res.headers['surrogate-key'], expectedCacheHeaders.surrogate_keys);
}
done();
};
}
function validateXChannelHeaders(headers, expectedCacheHeaders) {
var dbName = headers['x-cache-channel'].split(':')[0];
var tables = headers['x-cache-channel'].split(':')[1].split(',').sort();
assert.equal(dbName, expectedCacheHeaders.x_cache_channel.db_name);
assert.deepEqual(tables, expectedCacheHeaders.x_cache_channel.tables.sort());
}
function noCacheHeaders(done) {
return function(res, err) {
if (err) {
return done(err);
}
assert.ok(
!res.headers['x-cache-channel'],
'did not expect x-cache-channel header, got: `' + res.headers['x-cache-channel'] + '`'
);
assert.ok(
!res.headers['surrogate-key'],
'did not expect surrogate-key header, got: `' + res.headers['surrogate-key'] + '`'
);
done();
};
}
function withLayergroupId(mapConfig, callback) {
assert.response(
server,
layergroupRequest(mapConfig),
statusOkResponse,
function(res, err) {
if (err) {
return callback(err);
}
var layergroupId = JSON.parse(res.body).layergroupid;
keysToDelete['map_cfg|' + LayergroupToken.parse(layergroupId).token] = 0;
keysToDelete['user:localhost:mapviews:global'] = 5;
callback(null, layergroupId, res);
}
);
}
mapConfigs.forEach(function(mapConfigData) {
describe(mapConfigData.description, function() {
var mapConfig = mapConfigData.data;
var expectedCacheHeaders = mapConfigData.cache_headers;
it('/api/v1/map Map instantiation', function(done) {
var testFn = validateCacheHeaders(done, expectedCacheHeaders);
withLayergroupId(mapConfig, function(err, layergroupId, res) {
testFn(res);
});
});
it ('/api/v1/map/:token/:z/:x/:y@:scale_factor?x.:format Mapnik retina tiles', function(done) {
withLayergroupId(mapConfig, function(err, layergroupId) {
assert.response(
server,
getRequest('/api/v1/map/' + layergroupId + '/0/0/0@2x.png', true),
validateCacheHeaders(done, expectedCacheHeaders)
);
});
});
it ('/api/v1/map/:token/:z/:x/:y@:scale_factor?x.:format Mapnik tiles', function(done) {
withLayergroupId(mapConfig, function(err, layergroupId) {
assert.response(
server,
getRequest('/api/v1/map/' + layergroupId + '/0/0/0.png', true),
validateCacheHeaders(done, expectedCacheHeaders)
);
});
});
it ('/api/v1/map/:token/:layer/:z/:x/:y.(:format) Per :layer rendering', function(done) {
withLayergroupId(mapConfig, function(err, layergroupId) {
assert.response(
server,
getRequest('/api/v1/map/' + layergroupId + '/0/0/0/0.png', true),
validateCacheHeaders(done, expectedCacheHeaders)
);
});
});
it ('/api/v1/map/:token/:layer/attributes/:fid endpoint for info windows', function(done) {
withLayergroupId(mapConfig, function(err, layergroupId) {
assert.response(
server,
getRequest('/api/v1/map/' + layergroupId + '/0/attributes/1', true),
validateCacheHeaders(done, expectedCacheHeaders)
);
});
});
it ('/api/v1/map/static/center/:token/:z/:lat/:lng/:width/:height.:format static maps', function(done) {
withLayergroupId(mapConfig, function(err, layergroupId) {
assert.response(
server,
getRequest('/api/v1/map/static/center/' + layergroupId + '/0/0/0/400/300.png', true),
validateCacheHeaders(done, expectedCacheHeaders)
);
});
});
it ('/api/v1/map/static/bbox/:token/:bbox/:width/:height.:format static maps', function(done) {
withLayergroupId(mapConfig, function(err, layergroupId) {
assert.response(
server,
getRequest('/api/v1/map/static/bbox/' + layergroupId + '/-45,-45,45,45/400/300.png', true),
validateCacheHeaders(done, expectedCacheHeaders)
);
});
});
});
});
describe('cache headers should NOT be present', function() {
it('/', function(done) {
assert.response(
server,
getRequest('/'),
statusOkResponse,
noCacheHeaders(done)
);
});
it('/version', function(done) {
assert.response(
server,
getRequest('/version'),
statusOkResponse,
noCacheHeaders(done)
);
});
it('/health', function(done) {
assert.response(
server,
getRequest('/health'),
statusOkResponse,
noCacheHeaders(done)
);
});
it('/api/v1/map/named list named maps', function(done) {
assert.response(
server,
getRequest('/api/v1/map/named', true),
statusOkResponse,
noCacheHeaders(done)
);
});
describe('with named maps', function() {
var templateName = 'x_cache';
beforeEach(function(done) {
var template = {
version: '0.0.1',
name: templateName,
auth: {
method: 'open'
},
layergroup: mapConfigs[0].data
};
var namedMapRequest = {
url: '/api/v1/map/named?api_key=1234',
method: 'POST',
headers: {
host: 'localhost',
'Content-Type': 'application/json'
},
data: JSON.stringify(template)
};
assert.response(
server,
namedMapRequest,
statusOkResponse,
function(res, err) {
done(err);
}
);
});
afterEach(function(done) {
assert.response(
server,
{
url: '/api/v1/map/named/' + templateName + '?api_key=1234',
method: 'DELETE',
headers: {
host: 'localhost'
}
},
{
status: 204
},
function(res, err) {
done(err);
}
);
});
it('/api/v1/map/named/:template_id Named map retrieval', function(done) {
assert.response(
server,
getRequest('/api/v1/map/named/' + templateName, true),
statusOkResponse,
noCacheHeaders(done)
);
});
it('/api/v1/map/named/:template_id/jsonp Named map retrieval', function(done) {
assert.response(
server,
getRequest('/api/v1/map/named/' + templateName, true, 'cb'),
statusOkResponse,
noCacheHeaders(done)
);
});
});
});
});

View File

@@ -21,7 +21,7 @@ describe('named maps static view', function() {
var IMAGE_TOLERANCE = 20;
function createTemplate(view) {
function createTemplate(view, layers) {
return {
version: '0.0.1',
name: templateName,
@@ -36,7 +36,7 @@ describe('named maps static view', function() {
},
view: view,
layergroup: {
layers: [
layers: layers || [
{
type: 'mapnik',
options: {
@@ -198,4 +198,43 @@ describe('named maps static view', function() {
});
});
it('should allow to select the layers to render', function (done) {
var view = {
bounds: {
west: 0,
south: 0,
east: 45,
north: 45
}
};
var layers = [
{
type: 'mapnik',
options: {
sql: 'select * from populated_places_simple_reduced',
cartocss: '#layer { marker-fill: <%= color %>; }',
cartocss_version: '2.3.0'
}
},
{
type: 'mapnik',
options: {
sql: 'select ST_Transform(ST_MakeEnvelope(-45, -45, 45, 45, 4326), 3857) the_geom_webmercator',
cartocss: '#layer { polygon-fill: <%= color %>; }',
cartocss_version: '2.3.0'
}
}
];
templateMaps.addTemplate(username, createTemplate(view, layers), function (err) {
if (err) {
return done(err);
}
getStaticMap({ layer: 0 }, function(err, img) {
assert.ok(!err);
assert.imageIsSimilarToFile(img, previewFixture('bounds'), IMAGE_TOLERANCE, done);
});
});
});
});

View File

@@ -1052,8 +1052,9 @@ describe('template_api', function() {
'Unexpected error for authorized instance: ' + res.statusCode + ' -- ' + res.body);
assert.equal(res.headers['content-type'], "application/json; charset=utf-8");
var cc = res.headers['x-cache-channel'];
var expectedCC = 'test_windshaft_cartodb_user_1_db:public.test_table_private_1';
assert.ok(cc);
assert.ok(cc.match, /ciao/, cc);
assert.equal(cc, expectedCC);
// hack simulating restart...
server.layergroupAffectedTablesCache.cache.reset(); // need to clean channel cache
var get_request = {
@@ -1072,8 +1073,9 @@ describe('template_api', function() {
'Unexpected error for authorized instance: ' + res.statusCode + ' -- ' + res.body);
assert.equal(res.headers['content-type'], "application/json; charset=utf-8");
var cc = res.headers['x-cache-channel'];
var expectedCC = 'test_windshaft_cartodb_user_1_db:public.test_table_private_1';
assert.ok(cc, "Missing X-Cache-Channel on fetch-after-restart");
assert.ok(cc.match, /ciao/, cc);
assert.equal(cc, expectedCC);
return null;
},
function deleteTemplate(err)

View File

@@ -1,307 +0,0 @@
var testHelper = require('../support/test_helper');
var assert = require('../support/assert');
var qs = require('querystring');
var CartodbWindshaft = require('../../lib/cartodb/server');
var serverOptions = require('../../lib/cartodb/server_options');
var server = new CartodbWindshaft(serverOptions);
server.setMaxListeners(0);
var LayergroupToken = require('../support/layergroup-token');
describe('get requests x-cache-channel', function() {
var keysToDelete;
beforeEach(function() {
keysToDelete = {};
});
afterEach(function(done) {
testHelper.deleteRedisKeys(keysToDelete, done);
});
var statusOkResponse = {
status: 200
};
var mapConfig = {
version: '1.3.0',
layers: [
{
options: {
sql: 'select * from test_table limit 2',
cartocss: '#layer { marker-fill:red; }',
cartocss_version: '2.3.0',
attributes: {
id:'cartodb_id',
columns: [
'name',
'address'
]
}
}
}
]
};
var layergroupRequest = {
url: '/api/v1/map?config=' + encodeURIComponent(JSON.stringify(mapConfig)),
method: 'GET',
headers: {
host: 'localhost'
}
};
function getRequest(url, addApiKey, callbackName) {
var params = {};
if (!!addApiKey) {
params.api_key = '1234';
}
if (!!callbackName) {
params.callback = callbackName;
}
return {
url: url + '?' + qs.stringify(params),
method: 'GET',
headers: {
host: 'localhost',
'Content-Type': 'application/json'
}
};
}
function validateXCacheChannel(done, expectedCacheChannel) {
return function(res, err) {
if (err) {
return done(err);
}
assert.ok(res.headers['x-cache-channel']);
if (expectedCacheChannel) {
assert.equal(res.headers['x-cache-channel'], expectedCacheChannel);
}
done();
};
}
function noXCacheChannelHeader(done) {
return function(res, err) {
if (err) {
return done(err);
}
assert.ok(
!res.headers['x-cache-channel'],
'did not expect x-cache-channel header, got: `' + res.headers['x-cache-channel'] + '`'
);
done();
};
}
function withLayergroupId(callback) {
assert.response(
server,
layergroupRequest,
statusOkResponse,
function(res, err) {
if (err) {
return callback(err);
}
var layergroupId = JSON.parse(res.body).layergroupid;
keysToDelete['map_cfg|' + LayergroupToken.parse(layergroupId).token] = 0;
keysToDelete['user:localhost:mapviews:global'] = 5;
callback(null, layergroupId, res);
}
);
}
describe('header should be present', function() {
it('/api/v1/map Map instantiation', function(done) {
var testFn = validateXCacheChannel(done, 'test_windshaft_cartodb_user_1_db:public.test_table');
withLayergroupId(function(err, layergroupId, res) {
testFn(res);
});
});
it ('/api/v1/map/:token/:z/:x/:y@:scale_factor?x.:format Mapnik retina tiles', function(done) {
withLayergroupId(function(err, layergroupId) {
assert.response(
server,
getRequest('/api/v1/map/' + layergroupId + '/0/0/0@2x.png'),
validateXCacheChannel(done, 'test_windshaft_cartodb_user_1_db:public.test_table')
);
});
});
it ('/api/v1/map/:token/:z/:x/:y@:scale_factor?x.:format Mapnik tiles', function(done) {
withLayergroupId(function(err, layergroupId) {
assert.response(
server,
getRequest('/api/v1/map/' + layergroupId + '/0/0/0.png'),
validateXCacheChannel(done, 'test_windshaft_cartodb_user_1_db:public.test_table')
);
});
});
it ('/api/v1/map/:token/:layer/:z/:x/:y.(:format) Per :layer rendering', function(done) {
withLayergroupId(function(err, layergroupId) {
assert.response(
server,
getRequest('/api/v1/map/' + layergroupId + '/0/0/0/0.png'),
validateXCacheChannel(done, 'test_windshaft_cartodb_user_1_db:public.test_table')
);
});
});
it ('/api/v1/map/:token/:layer/attributes/:fid endpoint for info windows', function(done) {
withLayergroupId(function(err, layergroupId) {
assert.response(
server,
getRequest('/api/v1/map/' + layergroupId + '/0/attributes/1'),
validateXCacheChannel(done, 'test_windshaft_cartodb_user_1_db:public.test_table')
);
});
});
it ('/api/v1/map/static/center/:token/:z/:lat/:lng/:width/:height.:format static maps', function(done) {
withLayergroupId(function(err, layergroupId) {
assert.response(
server,
getRequest('/api/v1/map/static/center/' + layergroupId + '/0/0/0/400/300.png'),
validateXCacheChannel(done, 'test_windshaft_cartodb_user_1_db:public.test_table')
);
});
});
it ('/api/v1/map/static/bbox/:token/:bbox/:width/:height.:format static maps', function(done) {
withLayergroupId(function(err, layergroupId) {
assert.response(
server,
getRequest('/api/v1/map/static/bbox/' + layergroupId + '/-45,-45,45,45/400/300.png'),
validateXCacheChannel(done, 'test_windshaft_cartodb_user_1_db:public.test_table')
);
});
});
});
describe('header should NOT be present', function() {
it('/', function(done) {
assert.response(
server,
getRequest('/'),
statusOkResponse,
noXCacheChannelHeader(done)
);
});
it('/version', function(done) {
assert.response(
server,
getRequest('/version'),
statusOkResponse,
noXCacheChannelHeader(done)
);
});
it('/health', function(done) {
assert.response(
server,
getRequest('/health'),
statusOkResponse,
noXCacheChannelHeader(done)
);
});
it('/api/v1/map/named list named maps', function(done) {
assert.response(
server,
getRequest('/api/v1/map/named', true),
statusOkResponse,
noXCacheChannelHeader(done)
);
});
describe('with named maps', function() {
var templateName = 'x_cache';
beforeEach(function(done) {
var template = {
version: '0.0.1',
name: templateName,
auth: {
method: 'open'
},
layergroup: mapConfig
};
var namedMapRequest = {
url: '/api/v1/map/named?api_key=1234',
method: 'POST',
headers: {
host: 'localhost',
'Content-Type': 'application/json'
},
data: JSON.stringify(template)
};
assert.response(
server,
namedMapRequest,
statusOkResponse,
function(res, err) {
done(err);
}
);
});
afterEach(function(done) {
assert.response(
server,
{
url: '/api/v1/map/named/' + templateName + '?api_key=1234',
method: 'DELETE',
headers: {
host: 'localhost'
}
},
{
status: 204
},
function(res, err) {
done(err);
}
);
});
it('/api/v1/map/named/:template_id Named map retrieval', function(done) {
assert.response(
server,
getRequest('/api/v1/map/named/' + templateName, true),
statusOkResponse,
noXCacheChannelHeader(done)
);
});
it('/api/v1/map/named/:template_id/jsonp Named map retrieval', function(done) {
assert.response(
server,
getRequest('/api/v1/map/named/' + templateName, true, 'cb'),
statusOkResponse,
noXCacheChannelHeader(done)
);
});
});
});
});

View File

@@ -75,7 +75,7 @@ if test x"$PREPARE_PGSQL" = xyes; then
dropdb "${TEST_DB}"
createdb -Ttemplate_postgis -EUTF8 "${TEST_DB}" || die "Could not create test database"
LOCAL_SQL_SCRIPTS='analysis_catalog windshaft.test gadm4 ported/populated_places_simple_reduced cdb_analysis_check'
LOCAL_SQL_SCRIPTS='analysis_catalog windshaft.test gadm4 ported/populated_places_simple_reduced cdb_analysis_check cdb_invalidate_varnish'
REMOTE_SQL_SCRIPTS='CDB_QueryStatements CDB_QueryTables CDB_CartodbfyTable CDB_TableMetadata CDB_ForeignTable CDB_UserTables CDB_ColumnNames CDB_ZoomFromScale CDB_OverviewsSupport CDB_Overviews CDB_QuantileBins CDB_JenksBins CDB_HeadsTailsBins CDB_EqualIntervalBins CDB_Hexagon CDB_XYZ'
CURL_ARGS=""

View File

@@ -0,0 +1,6 @@
CREATE OR REPLACE FUNCTION CDB_Invalidate_Varnish(table_name TEXT)
RETURNS void AS
$$
BEGIN
END;
$$ LANGUAGE PLPGSQL;

View File

@@ -26,12 +26,14 @@ describe('tile stats', function() {
var layergroupController = new LayergroupController();
var reqMock = {
profiler: { toJSONString:function() {} },
params: {
format: invalidFormat
}
};
var resMock = {
status: function() { return this; },
set: function() {},
json: function() {},
jsonp: function() {},
send: function() {}
@@ -54,12 +56,14 @@ describe('tile stats', function() {
}
});
var reqMock = {
profiler: { toJSONString:function() {} },
params: {
format: validFormat
}
};
var resMock = {
status: function() { return this; },
set: function() {},
json: function() {},
jsonp: function() {},
send: function() {}

View File

@@ -2,7 +2,7 @@
# yarn lockfile v1
"abaculus@github:cartodb/abaculus#2.0.3-cdb1":
abaculus@cartodb/abaculus#2.0.3-cdb1:
version "2.0.3-cdb1"
resolved "https://codeload.github.com/cartodb/abaculus/tar.gz/f5f34e1c80cdd8d49edd1d6fe3b2220ab2e23aaf"
dependencies:
@@ -194,9 +194,9 @@ camelcase@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a"
camshaft@0.50.3:
version "0.50.3"
resolved "https://registry.yarnpkg.com/camshaft/-/camshaft-0.50.3.tgz#dc6c5e3b0b39bc970c8ab6e66a61d905954eb85a"
camshaft@0.53.1:
version "0.53.1"
resolved "https://registry.yarnpkg.com/camshaft/-/camshaft-0.53.1.tgz#ba280be034e161c4989e1c81bafcdeb42d10ef7b"
dependencies:
async "^1.5.2"
bunyan "1.8.1"
@@ -205,7 +205,7 @@ camshaft@0.50.3:
dot "^1.0.3"
request "^2.69.0"
"canvas@github:cartodb/node-canvas#1.6.2-cdb2":
canvas@cartodb/node-canvas#1.6.2-cdb2:
version "1.6.2-cdb2"
resolved "https://codeload.github.com/cartodb/node-canvas/tar.gz/8acf04557005c633f9e68524488a2657c04f3766"
dependencies:
@@ -231,7 +231,7 @@ carto@CartoDB/carto#0.15.1-cdb1:
optimist "~0.6.0"
underscore "~1.6.0"
"carto@github:cartodb/carto#0.15.1-cdb3":
carto@cartodb/carto#0.15.1-cdb3:
version "0.15.1-cdb3"
resolved "https://codeload.github.com/cartodb/carto/tar.gz/945f5efb74fd1af1f5e1f69f409f9567f94fb5a7"
dependencies:
@@ -1730,7 +1730,7 @@ repeat-string@^1.5.2:
version "1.6.1"
resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637"
request@2.x, request@^2.55.0, request@^2.69.0, request@~2.79.0:
request@2.x, request@^2.55.0, request@~2.79.0:
version "2.79.0"
resolved "https://registry.yarnpkg.com/request/-/request-2.79.0.tgz#4dfe5bf6be8b8cdc37fcf93e04b65577722710de"
dependencies:
@@ -1755,7 +1755,7 @@ request@2.x, request@^2.55.0, request@^2.69.0, request@~2.79.0:
tunnel-agent "~0.4.1"
uuid "^3.0.0"
request@^2.81.0:
request@^2.69.0, request@^2.81.0:
version "2.81.0"
resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0"
dependencies:
@@ -2084,7 +2084,7 @@ through@2:
version "2.3.8"
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
"tilelive-bridge@github:cartodb/tilelive-bridge#2.3.1-cdb1":
tilelive-bridge@cartodb/tilelive-bridge#2.3.1-cdb1:
version "2.3.1-cdb1"
resolved "https://codeload.github.com/cartodb/tilelive-bridge/tar.gz/3f76c278c782e93d79045870387a0a06bace720b"
dependencies:
@@ -2092,7 +2092,7 @@ through@2:
mapnik-pool "~0.1.3"
sphericalmercator "1.0.x"
"tilelive-mapnik@github:cartodb/tilelive-mapnik#0.6.18-cdb1":
tilelive-mapnik@cartodb/tilelive-mapnik#0.6.18-cdb1:
version "0.6.18-cdb1"
resolved "https://codeload.github.com/cartodb/tilelive-mapnik/tar.gz/cf7e5b4633db653a889a6c6e6a5ddcbcf4ddc3b5"
dependencies: