Compare commits

...

70 Commits

Author SHA1 Message Date
Raul Ochoa
723a184086 Release 1.17.2 2014-10-01 20:27:11 +02:00
Raul Ochoa
db34a4ffff Stubs next version 2014-09-30 15:09:56 +02:00
Raul Ochoa
0b896fb935 Release 1.17.1 2014-09-30 15:08:26 +02:00
Raul Ochoa
e0331ec022 Upgrades mocha 2014-09-30 15:03:14 +02:00
Raul Ochoa
e31ef916f0 Upgrades windshaft to 0.27.1 which downgrades node-mapnik to 1.4.10 2014-09-30 15:02:24 +02:00
Raul Ochoa
55669f88ff Updates news 2014-09-26 11:21:28 +02:00
Raul Ochoa
c13dbc9a57 Merge pull request #214 from CartoDB/CDB-4008
TTL for template locks so they are not kept forever
2014-09-26 11:16:05 +02:00
Raul Ochoa
e8e03585ff Adds dot.js dependency 2014-09-25 19:17:02 +02:00
Raul Ochoa
b4bee864d2 Lock now considers the creation time and compares against a ttl so
a lock is not keep forever in case of failure.

Pending: lazy removal of expired locks.
2014-09-25 19:00:35 +02:00
Raul Ochoa
b41d1e84da Stubs next version 2014-09-25 16:17:10 +02:00
Raul Ochoa
b5d5d7c2b0 Release 1.17.0 2014-09-25 16:15:51 +02:00
Raul Ochoa
3e571b4ce8 Use object.keys to iterate over objects 2014-09-25 12:17:32 +02:00
Raul Ochoa
fb8fd5121e Do not expose internal implementation 2014-09-25 12:16:34 +02:00
Raul Ochoa
ac2a3243b5 Don't cache regexes and avoid the _re hack 2014-09-25 12:04:52 +02:00
Raul Ochoa
1c10b8193b Adds dot to compile templates 2014-09-24 19:17:51 +02:00
Raul Ochoa
abf0fa1b32 Remove unused var 2014-09-24 19:12:43 +02:00
Raul Ochoa
4c5bc13c7f Check style fixes 2014-09-24 19:11:53 +02:00
Raul Ochoa
c33b81e3ad Merge pull request #213 from CartoDB/mapnik-2.3.x
Starts using mapnik 2.3.x by upgrading windshaft dependency
2014-09-24 16:26:21 +02:00
Raul Ochoa
f88f0b5019 Modify travis configuration to be able to compile node-mapnik with mapnik 2.3.x 2014-09-24 16:02:31 +02:00
Raul Ochoa
3b96f0d535 Starts using mapnik 2.3.x via windshaft upgrade 2014-09-24 15:54:13 +02:00
Raul Ochoa
243672ead5 Merge branch 'master' into mapnik-2.3.x 2014-09-24 15:27:41 +02:00
Raul Ochoa
9d36ae293c Run check before any assert so server is stopped 2014-09-18 19:07:15 +02:00
Raul Ochoa
9496d83d1c Adds poolSize configuration for mapnik 2014-09-18 19:06:45 +02:00
Raul Ochoa
18233e9ea1 Upgrades windshaft and cartodb-redis 2014-09-18 19:05:20 +02:00
Raul Ochoa
6523c4bbbb Fixes template.json in documentation to be a valid JSON 2014-09-03 12:34:28 +02:00
Raul Ochoa
5bafbdfaa0 Changes mapnik dependency in windshaft module to use node-mapnik 1.4.13
which is based on mapnik-2.3.x
2014-08-29 17:03:31 +02:00
Raul Ochoa
7afa869833 Use double quote to be consistent 2014-08-29 16:48:28 +02:00
Raul Ochoa
0d32036523 Merge pull request #211 from CartoDB/CDB-3659
Remove some metrics
2014-08-26 13:44:21 +02:00
Raul Ochoa
a084ec19ff Update news 2014-08-26 12:23:56 +02:00
Raul Ochoa
7faff8f887 Removes cors metric 2014-08-26 11:53:59 +02:00
Raul Ochoa
f406001315 Removes get_infowindow and get_map_metadata metrics 2014-08-25 19:15:31 +02:00
Raul Ochoa
2b2020b43b Removes getTableGeometryType metric 2014-08-25 19:09:54 +02:00
Raul Ochoa
4886e4b34a Merge pull request #210 from CartoDB/improve-query-tables-api
Removes duplicated code in query_tables_api
2014-08-22 13:17:02 +02:00
Raul Ochoa
65e0364d37 Removes duplicated code in query_tables_api 2014-08-22 12:52:05 +02:00
Raul Ochoa
13e16e0a26 Merge pull request #209 from CartoDB/scale_denominator-support
Supports `!scale_denominator!` dynamic param in SQL queries
2014-08-22 10:30:14 +02:00
Raul Ochoa
965e1cd0c4 Supports !scale_denominator! dynamic param in SQL queries 2014-08-22 10:16:39 +02:00
Raul Ochoa
3750b67110 Merge pull request #208 from CartoDB/CDB-1843
Upgrades windshaft
2014-08-19 19:06:25 +02:00
Raul Ochoa
7e1f48f212 CDB-1843 upgrades windshaft 2014-08-19 18:42:48 +02:00
Raul Ochoa
42457df2c1 Stubs next version 2014-08-19 14:28:24 +02:00
Raul Ochoa
56d8f1acfe Release 1.16.1 2014-08-19 14:26:19 +02:00
Raul Ochoa
56ac4acc9f Upgrades cartodb-redis 2014-08-19 14:25:45 +02:00
javi santana
d91b0f1af5 fixed missing comma 2014-08-19 09:25:45 +02:00
Raul Ochoa
f986516379 Stubs next version 2014-08-18 15:29:47 +02:00
Raul Ochoa
fa59255556 Release 1.16.0 2014-08-18 15:28:38 +02:00
Raul Ochoa
ab1c11faf2 Merge pull request #207 from CartoDB/upgrade-windshaft
Upgrade windshaft
2014-08-18 15:26:58 +02:00
Raul Ochoa
307f220de4 Updates news 2014-08-18 15:14:01 +02:00
Raul Ochoa
50c8a2dc69 Defaults mapnik version for test 2014-08-18 14:48:45 +02:00
Raul Ochoa
3f726dc4c5 Upgrades windshaft 2014-08-18 14:41:29 +02:00
Raul Ochoa
105d50f1f4 Merge pull request #206 from CartoDB/upgrade-dependencies
Upgrades dependencies
2014-08-15 01:51:16 +02:00
Raul Ochoa
a3a5964926 Upgrades dependencies 2014-08-14 19:54:45 +02:00
Raul Ochoa
6a8cff6fcd Merge branch 'remove-mapnik-dependency'
Conflicts:
	NEWS.md
2014-08-14 19:26:52 +02:00
Raul Ochoa
b42e9c80f7 Adds windshaft's mapnik dependency to npm-shrinkwrap.json 2014-08-14 18:56:33 +02:00
Raul Ochoa
23a7684208 Removes mapnik dependency as it now relies on Windshaft to check mapnik version 2014-08-14 18:27:54 +02:00
Raul Ochoa
c52c245b9d Merge pull request #204 from CartoDB/CDB-3686
Configurable QueryTablesAPI to call directly postgresql
2014-08-14 13:44:07 +02:00
Raul Ochoa
4555b2107a Updates news 2014-08-14 13:32:22 +02:00
Raul Ochoa
49b0120c6d Formats NEWS 2014-08-14 13:32:04 +02:00
Raul Ochoa
f2541d8cae Merge branch 'master' into CDB-3686
Conflicts:
	package.json
2014-08-13 15:31:22 +02:00
Raul Ochoa
d1fb792709 Stubs next version 2014-08-13 15:27:16 +02:00
Raul Ochoa
86fb58155a Release 1.15.0 2014-08-13 15:26:25 +02:00
Raul Ochoa
d3c656893c Merge pull request #202 from CartoDB/CDB-3135
Upgrades dependencies to get redis-mpool improvements
2014-08-13 15:24:59 +02:00
Raul Ochoa
713e394e7b Slow pool configuration in example configurations 2014-08-13 15:15:30 +02:00
Raul Ochoa
40acf533ae Specifies name in the redis pool 2014-08-13 15:12:46 +02:00
Raul Ochoa
13fdfc602e Upgrades dependencies 2014-08-13 15:10:58 +02:00
Raul Ochoa
8255f3eb51 Upgrades cartodb-psql dependency 2014-08-11 20:18:27 +02:00
Raul Ochoa
e7ab71c606 Merge branch 'master' into CDB-3686 2014-08-11 12:19:11 +02:00
Raul Ochoa
3eab0d6349 Stubs next version 2014-08-07 12:35:38 +02:00
Raul Ochoa
5cf79c82bb Configurable QueryTablesAPI to call directly postgresql using cartodb-psql
or to keep using a request to the SQL API
2014-08-06 21:48:08 +02:00
Raul Ochoa
d1373bec66 Improves SQL query for affected tables and last updated time 2014-08-04 17:48:59 +02:00
Raul Ochoa
325a0503cb Merge branch 'master' into cdb 2014-08-04 14:14:53 +02:00
Raul Ochoa
5d9afc18f5 Merge tag '1.13.1' into cdb 2014-08-04 13:11:01 +02:00
21 changed files with 631 additions and 356 deletions

View File

@@ -4,11 +4,13 @@ before_install:
- sudo apt-add-repository --yes ppa:cartodb/postgresql-9.3
- sudo apt-add-repository --yes ppa:cartodb/gis
- sudo rm -Rf /var/lib/postgresql /etc/postgresql
- sudo apt-add-repository --yes ppa:mapnik/v2.1.0
- sudo apt-add-repository --yes ppa:mapnik/nightly-2.3
- sudo apt-get update
- sudo apt-get install -y postgresql-9.3-postgis-2.1
- sudo apt-get install -y postgresql-contrib-9.3
- sudo apt-get install -y libmapnik-dev
- sudo apt-get install -q libprotobuf-dev protobuf-compiler
- sudo apt-get install -q libmapnik-dev
- sudo apt-get install -q mapnik-input-plugin-gdal mapnik-input-plugin-ogr mapnik-input-plugin-postgis
- sudo apt-get install -y gdal-bin
- echo -e "local\tall\tall\ttrust\nhost\tall\tall\t127.0.0.1/32\ttrust\nhost\tall\tall\t::1/128\ttrust" |sudo tee /etc/postgresql/9.3/main/pg_hba.conf
- sudo service postgresql restart

70
NEWS.md
View File

@@ -1,3 +1,67 @@
1.17.2 -- 2014-10-01
--------------------
Announcements:
- Upgrades windshaft to 0.27.2 which downgrades node-mapnik to 0.7.26-cdb1
1.17.1 -- 2014-09-30
--------------------
Announcements:
- Upgrades windshaft to 0.27.1 which downgrades node-mapnik to 1.4.10
Enhancements:
- TTL for template locks so they are not kept forever
- Upgrades mocha
1.17.0 -- 2014-09-25
--------------------
New features:
- Starts using mapnik 2.3.x
Enhancements:
- Upgrades windshaft and cartodb-redis
- Supports `!scale_denominator!` dynamic param in SQL queries
- Metrics revamp: removes and adds some metrics
- Adds poolSize configuration for mapnik
1.16.1 -- 2014-08-19
--------------------
Enhancements:
- Upgrades cartodb-redis
1.16.0 -- 2014-08-18
--------------------
New features:
- Configurable QueryTablesAPI to call directly postgresql using cartodb-psql
or to keep using a request to the SQL API
Enhancements:
- Removes mapnik dependency as it now relies on Windshaft to check mapnik version
- Upgrades dependencies:
- underscore
- lzma
- log4js
- rollbar
- windshaft
- request
1.15.0 -- 2014-08-13
--------------------
Enhancements:
- Upgrades dependencies:
- redis-mpool
- cartodb-redis
- windshaft
- Specifies name in the redis pool
- Slow pool configuration in example configurations
1.14.0 -- 2014-08-07
--------------------
@@ -5,8 +69,10 @@ Enhancements:
- SQL API requests moved to its own entity
New features:
- Affected tables and last updated time for a query are performed in a single request to the SQL API
- Allow specifying the tile format, upgrades windshaft and grainstore dependencies for this matter
- Affected tables and last updated time for a query are performed in a single
request to the SQL API
- Allow specifying the tile format, upgrades windshaft and grainstore
dependencies for this matter
1.13.1 -- 2014-08-04

View File

@@ -103,7 +103,11 @@ var config = {
// kept open by the server. The default is 50.
max: 50,
idleTimeoutMillis: 1, // idle time before dropping connection
reapIntervalMillis: 1 // time between cleanups
reapIntervalMillis: 1, // time between cleanups
slowPool: {
log: true, // whether a slow acquire must be logged or not
elapsedThreshold: 25 // the threshold to determine an slow acquire must be reported or not
}
}
,sqlapi: {
protocol: 'http',

View File

@@ -97,7 +97,11 @@ var config = {
// kept open by the server. The default is 50.
max: 50,
idleTimeoutMillis: 30000, // idle time before dropping connection
reapIntervalMillis: 1000 // time between cleanups
reapIntervalMillis: 1000, // time between cleanups
slowPool: {
log: true, // whether a slow acquire must be logged or not
elapsedThreshold: 25 // the threshold to determine an slow acquire must be reported or not
}
}
,sqlapi: {
protocol: 'https',

View File

@@ -97,7 +97,11 @@ var config = {
// kept open by the server. The default is 50.
max: 50,
idleTimeoutMillis: 30000, // idle time before dropping connection
reapIntervalMillis: 1000 // time between cleanups
reapIntervalMillis: 1000, // time between cleanups
slowPool: {
log: true, // whether a slow acquire must be logged or not
elapsedThreshold: 25 // the threshold to determine an slow acquire must be reported or not
}
}
,sqlapi: {
protocol: 'https',

View File

@@ -97,7 +97,11 @@ var config = {
// kept open by the server. The default is 50.
max: 50,
idleTimeoutMillis: 1, // idle time before dropping connection
reapIntervalMillis: 1 // time between cleanups
reapIntervalMillis: 1, // time between cleanups
slowPool: {
log: true, // whether a slow acquire must be logged or not
elapsedThreshold: 25 // the threshold to determine an slow acquire must be reported or not
}
}
,sqlapi: {
protocol: 'http',

View File

@@ -51,7 +51,7 @@ The following API call creates a map of European countries that have a white fil
{% highlight javascript %}
// mapconfig.json
{
"version": "0.0.1"
"version": "0.0.1",
"name": "test",
"auth": {
"method": "open"
@@ -292,53 +292,52 @@ POST /api/v1/map/named
#### Params
<div class="code-title">template.json</div>
{% highlight javascript %}
// template.json
{
version: '0.0.1',
// there can be at most 1 template with the same name for any user
// valid names start with a letter and only contains letter, numbers
// or underscores
name: 'template_name',
auth: {
method: 'token', // or "open" (the default if no "method" is given)
valid_tokens: ['auth_token1','auth_token2'] // only (required and non empty) for 'token' method
"version": "0.0.1",
"name": "template_name",
"auth": {
"method": "token",
"valid_tokens": [
"auth_token1",
"auth_token2"
]
},
// Variables not listed here are not substituted
// Variable not provided at instantiation time trigger an error
// A default is required for optional variables
// Type specification is used for quoting, to avoid injections
// see template format section below
placeholders: {
color: {
type:'css_color',
default:'red'
"placeholders": {
"color": {
"type": "css_color",
"default": "red"
},
cartodb_id: {
type:'number',
default: 1
"cartodb_id": {
"type": "number",
"default": 1
}
},
// the layer list definition
layergroup: {
// this is the MapConfig explained in anonymous maps
// see https://github.com/CartoDB/Windshaft/blob/0.19.1/doc/MapConfig-1.1.0.md)
"layergroup": {
"version": "1.0.1",
"layers": [{
"type": "cartodb",
"options": {
"cartocss_version": "2.1.1",
"cartocss": "#layer { polygon-fill: <%= color %>; }",
"sql": "select * from european_countries_e WHERE cartodb_id = <%= cartodb_id %>"
"layers": [
{
"type": "cartodb",
"options": {
"cartocss_version": "2.1.1",
"cartocss": "#layer { polygon-fill: <%= color %>; }",
"sql": "select * from european_countries_e WHERE cartodb_id = <%= cartodb_id %>"
}
}
}]
]
}
}
{% endhighlight %}
##### Arguments
- **name**: there can be at most 1 template with the same name for any user valid names start with a letter and only contains letter, numbers or underscores
- **auth**:
- **method** `"token"` or `"open"` (the default if no `"method"` is given)
- **placeholders**: Variables not listed here are not substituted. Variable not provided at instantiation time trigger an error. A default is required for optional variables. Type specification is used for quoting, to avoid injections see template format section below.
- **layergroup**: the layer list definition. This is the MapConfig explained in anonymous maps see https://github.com/CartoDB/Windshaft/blob/master/doc/MapConfig-1.1.0.md
#### Template Format
A templated `layergroup` allows using placeholders in the "cartocss" and "sql" elements of the "option" object in any "layer" of a layergroup configuration

View File

@@ -4,8 +4,6 @@ See [Windshaft metrics documentation](https://github.com/CartoDB/Windshaft/blob/
The next list includes the API endpoints, each endpoint may have several inner timers, some of them are displayed within this list as subitems. Find the description for them in the Inner timers section.
## Timers
- **windshaft-cartodb.get_infowindow**: time to retrieve an infowindow popup
- **windshaft-cartodb.get_map_metadata**: time to retrieve metadata for embedded maps
- **windshaft-cartodb.flush_cache**: time to flush the tile and sql cache
- **windshaft-cartodb.get_template**: time to retrieve an specific template
- **windshaft-cartodb.delete_template**: time to delete an specific template
@@ -30,8 +28,6 @@ Again, each inner timer may have several inner timers.
- **authorizedByAPIKey**: time to authorize using an API KEY
- **authorizedByCert**: time to authorize a request by a cert, see [signed map](https://github.com/CartoDB/Windshaft-cartodb/wiki/Signed-maps)
- **authorizedBySigner**: time to authorize a request for a [signed map](https://github.com/CartoDB/Windshaft-cartodb/wiki/Signed-maps)
- **cartoData.getTableGeometryType**: time to retrieve from redis the geom type for a given table
- **cors**: time to set the CORS headers
- **findLastUpdated**: time to retrieve the last update time for a list of tables, see *affectedTables*
- **fingerPrint**: time to create a fingerprint for a signed map
- **generateCacheChannel**: time to generate the headers for the cache channel based on the request, see *addCacheChannel*

View File

@@ -1,10 +1,12 @@
var sqlApi = require('../sql/sql_api');
var sqlApi = require('../sql/sql_api'),
PSQL = require('cartodb-psql');
function QueryTablesApi() {
}
var affectedTableRegexCache = {
bbox: /!bbox!/g,
scale_denominator: /!scale_denominator!/g,
pixel_width: /!pixel_width!/g,
pixel_height: /!pixel_height!/g
};
@@ -33,63 +35,87 @@ QueryTablesApi.prototype.getLastUpdatedTime = function (username, api_key, table
});
};
QueryTablesApi.prototype.getAffectedTablesInQuery = function (username, api_key, sql, callback) {
// Replace mapnik tokens
sql = sql
.replace(affectedTableRegexCache.bbox, 'ST_MakeEnvelope(0,0,0,0)')
.replace(affectedTableRegexCache.pixel_width, '1')
.replace(affectedTableRegexCache.pixel_height, '1')
;
QueryTablesApi.prototype.getAffectedTablesInQuery = function (username, options, sql, callback) {
// Pass to CDB_QueryTables
sql = 'SELECT CDB_QueryTables($windshaft$' + sql + '$windshaft$)';
var query = 'SELECT CDB_QueryTables($windshaft$' + prepareSql(sql) + '$windshaft$)';
// call sql api
sqlApi.query(username, api_key, sql, function(err, rows){
if (err){
var msg = err.message ? err.message : err;
callback(new Error('could not fetch source tables: ' + msg));
return;
}
var qtables = rows[0].cdb_querytables;
var tableNames = qtables.split(/^\{(.*)\}$/)[1];
tableNames = tableNames ? tableNames.split(',') : [];
callback(null, tableNames);
});
runQuery(username, options, query, handleAffectedTablesInQueryRows, callback);
};
QueryTablesApi.prototype.getAffectedTablesAndLastUpdatedTime = function (username, api_key, sql, callback) {
sql = sql
.replace(affectedTableRegexCache.bbox, 'ST_MakeEnvelope(0,0,0,0)')
.replace(affectedTableRegexCache.pixel_width, '1')
.replace(affectedTableRegexCache.pixel_height, '1')
;
function handleAffectedTablesInQueryRows(err, rows, callback) {
if (err){
var msg = err.message ? err.message : err;
callback(new Error('could not fetch source tables: ' + msg));
return;
}
var qtables = rows[0].cdb_querytables;
var tableNames = qtables.split(/^\{(.*)\}$/)[1];
tableNames = tableNames ? tableNames.split(',') : [];
callback(null, tableNames);
}
QueryTablesApi.prototype.getAffectedTablesAndLastUpdatedTime = function (username, options, sql, callback) {
var query = [
'SELECT',
'CDB_QueryTables($windshaft$' + sql + '$windshaft$) as tablenames,',
'EXTRACT(EPOCH FROM max(updated_at)) as max',
'WITH querytables AS (',
'SELECT * FROM CDB_QueryTables($windshaft$' + prepareSql(sql) + '$windshaft$) as tablenames',
')',
'SELECT (SELECT tablenames FROM querytables), EXTRACT(EPOCH FROM max(updated_at)) as max',
'FROM CDB_TableMetadata m',
'WHERE m.tabname = any (CDB_QueryTables($windshaft$' + sql + '$windshaft$)::regclass[])'
'WHERE m.tabname = any ((SELECT tablenames from querytables)::regclass[])'
].join(' ');
sqlApi.query(username, api_key, query, function(err, rows){
if (err || rows.length === 0) {
var msg = err.message ? err.message : err;
callback(new Error('could not fetch affected tables and last updated time: ' + msg));
return;
}
var result = rows[0];
var tableNames = result.tablenames.split(/^\{(.*)\}$/)[1];
tableNames = tableNames ? tableNames.split(',') : [];
var lastUpdatedTime = result.max || 0;
callback(null, {
affectedTables: tableNames,
lastUpdatedTime: lastUpdatedTime * 1000
});
});
runQuery(username, options, query, handleAffectedTablesAndLastUpdatedTimeRows, callback);
};
function handleAffectedTablesAndLastUpdatedTimeRows(err, rows, callback) {
if (err || rows.length === 0) {
var msg = err.message ? err.message : err;
callback(new Error('could not fetch affected tables and last updated time: ' + msg));
return;
}
var result = rows[0];
var tableNames = result.tablenames.split(/^\{(.*)\}$/)[1];
tableNames = tableNames ? tableNames.split(',') : [];
var lastUpdatedTime = result.max || 0;
callback(null, {
affectedTables: tableNames,
lastUpdatedTime: lastUpdatedTime * 1000
});
}
function runQuery(username, options, query, queryHandler, callback) {
if (shouldQueryPostgresDirectly()) {
var psql = new PSQL(options);
psql.query(query, function(err, resultSet) {
var rows = resultSet.rows || [];
queryHandler(err, rows, callback);
});
} else {
sqlApi.query(username, options.api_key, query, function(err, rows) {
queryHandler(err, rows, callback);
});
}
}
function prepareSql(sql) {
return sql
.replace(affectedTableRegexCache.bbox, 'ST_MakeEnvelope(0,0,0,0)')
.replace(affectedTableRegexCache.scale_denominator, '0')
.replace(affectedTableRegexCache.pixel_width, '1')
.replace(affectedTableRegexCache.pixel_height, '1')
;
}
function shouldQueryPostgresDirectly() {
return global.environment
&& global.environment.enabledFeatures
&& global.environment.enabledFeatures.cdbQueryTablesFromPostgres;
}

View File

@@ -2,7 +2,7 @@
var _ = require('underscore')
, Step = require('step')
, Windshaft = require('windshaft')
, redisPool = require('redis-mpool')(global.environment.redis)
, redisPool = require('redis-mpool')(_.extend(global.environment.redis, {name: 'windshaft:cartodb'}))
// TODO: instanciate cartoData with redisPool
, cartoData = require('cartodb-redis')(global.environment.redis)
, SignedMaps = require('./signed_maps.js')
@@ -137,9 +137,6 @@ var CartodbWindshaft = function(serverOptions) {
* Helper to allow access to the layer to be used in the maps infowindow popup.
*/
ws.get(serverOptions.base_url + '/infowindow', function(req, res){
if ( req.profiler && req.profiler.statsd_client ) {
req.profiler.start('windshaft-cartodb.get_infowindow');
}
ws.doCORS(res);
Step(
function(){
@@ -161,9 +158,6 @@ var CartodbWindshaft = function(serverOptions) {
* Helper to allow access to metadata to be used in embedded maps.
*/
ws.get(serverOptions.base_url + '/map_metadata', function(req, res){
if ( req.profiler && req.profiler.statsd_client ) {
req.profiler.start('windshaft-cartodb.get_map_metadata');
}
ws.doCORS(res);
Step(
function(){
@@ -492,7 +486,6 @@ var CartodbWindshaft = function(serverOptions) {
// Instantiate a template
function instanciateTemplate(req, res, template_params, callback) {
ws.doCORS(res);
if ( req.profiler ) req.profiler.done('cors');
var that = this;
var response = {};
var template;

View File

@@ -3,9 +3,8 @@ var _ = require('underscore')
, cartoData = require('cartodb-redis')(global.environment.redis)
, Cache = require('./cache_validator')
, QueryTablesApi = require('./api/query_tables_api')
, mapnik = require('mapnik')
, crypto = require('crypto')
, LZMA = require('lzma/lzma_worker.js').LZMA
, LZMA = require('lzma').LZMA;
;
// This is for backward compatibility with 1.3.3
@@ -19,6 +18,8 @@ if ( _.isUndefined(global.environment.sqlapi.domain) ) {
module.exports = function(){
var lzmaWorker = new LZMA();
var queryTablesApi = new QueryTablesApi();
var rendererConfig = _.defaults(global.environment.renderer || {}, {
@@ -48,12 +49,13 @@ module.exports = function(){
},
datasource: global.environment.postgres,
cachedir: global.environment.millstone.cache_basedir,
mapnik_version: global.environment.mapnik_version || mapnik.versions.mapnik,
mapnik_version: global.environment.mapnik_version,
mapnik_tile_format: global.environment.mapnik_tile_format || 'png',
default_layergroup_ttl: global.environment.mapConfigTTL || 7200,
gc_prob: 0.01 // @deprecated since Windshaft-1.8.0
},
mapnik: {
poolSize: rendererConfig.poolSize,
metatile: rendererConfig.metatile,
bufferSize: rendererConfig.bufferSize
},
@@ -75,14 +77,6 @@ module.exports = function(){
// See http://github.com/CartoDB/Windshaft-cartodb/issues/161
me.redis.unwatchOnRelease = false;
// Be nice and warn if configured mapnik version
// is != instaled mapnik version
if ( mapnik.versions.mapnik != me.grainstore.mapnik_version ) {
console.warn("WARNING: detected mapnik version ("
+ mapnik.versions.mapnik + ") != configured mapnik version ("
+ me.grainstore.mapnik_version + ")");
}
/* This whole block is about generating X-Cache-Channel { */
// TODO: review lifetime of elements of this cache
@@ -192,7 +186,14 @@ module.exports = function(){
if ( req.profiler ) req.profiler.done('getSignerMapKey');
key = data;
}
queryTablesApi.getAffectedTablesInQuery(user, key, sql, this); // in addCacheChannel
queryTablesApi.getAffectedTablesInQuery(user, {
user: req.params.dbuser,
pass: req.params.dbpass,
host: req.params.dbhost,
port: req.params.dbport,
dbname: req.params.dbname,
api_key: key
}, sql, this); // in addCacheChannel
},
function finish(err, data) {
next(err,data);
@@ -317,7 +318,14 @@ module.exports = function(){
Step(
function getAffectedTablesAndLastUpdatedTime() {
queryTablesApi.getAffectedTablesAndLastUpdatedTime(usr, key, sql, this);
queryTablesApi.getAffectedTablesAndLastUpdatedTime(usr, {
user: req.params.dbuser,
pass: req.params.dbpass,
host: req.params.dbhost,
port: req.params.dbport,
dbname: req.params.dbname,
api_key: key
}, sql, this);
},
function handleAffectedTablesAndLastUpdatedTime(err, result) {
if (req.profiler) req.profiler.done('queryTablesAndLastUpdated');
@@ -625,7 +633,7 @@ module.exports = function(){
var lzma = (new Buffer(req.query.lzma, 'base64').toString('binary')).split('').map(function(c) { return c.charCodeAt(0) - 128 });
// Decompress
LZMA.decompress(
lzmaWorker.decompress(
lzma,
function(result) {
if (req.profiler) req.profiler.done('LZMA decompress');
@@ -711,7 +719,6 @@ module.exports = function(){
cartoData.getTableGeometryType(req.params.dbname, req.params.table, this);
},
function finishSetup(err, data){
if (req.profiler) req.profiler.done('cartoData.getTableGeometryType');
if ( err ) { callback(err, req); return; }
if (!_.isNull(data))

View File

@@ -1,10 +1,7 @@
var crypto = require('crypto');
var Step = require('step');
var _ = require('underscore');
// Templates in this hash (keyed as <username>@<template_name>)
// are being worked on.
var user_template_locks = {};
var crypto = require('crypto'),
Step = require('step'),
_ = require('underscore'),
dot = require('dot');
// Class handling map templates
//
@@ -38,17 +35,18 @@ function TemplateMaps(redis_pool, signed_maps, opts) {
//
// We have the following datastores:
//
// 1. User teplates: set of per-user map templates
// 1. User templates: set of per-user map templates
// NOTE: each template would have an associated auth
// reference, see signed_maps.js
// User templates (HASH:tpl_id->tpl_val)
this.key_usr_tpl = "map_tpl|<%= owner %>";
this.key_usr_tpl = dot.template("map_tpl|{{=it.owner}}");
// User template locks (HASH:tpl_id->ctime)
this.key_usr_tpl_lck = "map_tpl|<%= owner %>|locks";
this.key_usr_tpl_lck = dot.template("map_tpl|{{=it.owner}}|locks");
};
this.lock_ttl = this.opts['lock_ttl'] || 5000;
}
var o = TemplateMaps.prototype;
@@ -97,36 +95,34 @@ o._redisCmd = function(redisFunc, redisArgs, callback) {
// @param callback function(err, obtained)
o._obtainTemplateLock = function(owner, tpl_id, callback) {
var usr_tpl_lck_key = _.template(this.key_usr_tpl_lck, {owner:owner});
var that = this;
var gotLock = false;
Step (
function obtainLock() {
var ctime = Date.now();
that._redisCmd('HSETNX', [usr_tpl_lck_key, tpl_id, ctime], this);
},
function checkLock(err, locked) {
if ( err ) throw err;
if ( ! locked ) {
// Already locked
// TODO: unlock if expired ?
throw new Error("Template '" + tpl_id + "' of user '" + owner + "' is locked");
}
return gotLock = true;
},
function finish(err) {
callback(err, gotLock);
}
);
var that = this,
lockKey = this.key_usr_tpl_lck({owner:owner});
Step (
function obtainLock() {
that._redisCmd('HGET', [lockKey, tpl_id], this);
},
function checkLock(err, lockTime) {
if (err) { throw err; }
var _newLockTime = Date.now();
if (!lockTime || ((_newLockTime - lockTime) > that.lock_ttl)) {
that._redisCmd('HSET', [lockKey, tpl_id, _newLockTime], this);
} else {
throw new Error("Template '" + tpl_id + "' of user '" + owner + "' is locked");
}
},
function finish(err, hsetValue) {
callback(err, !!hsetValue);
}
);
};
// @param callback function(err, deleted)
o._releaseTemplateLock = function(owner, tpl_id, callback) {
var usr_tpl_lck_key = _.template(this.key_usr_tpl_lck, {owner:owner});
this._redisCmd('HDEL', [usr_tpl_lck_key, tpl_id], callback);
this._redisCmd('HDEL', [this.key_usr_tpl_lck({owner:owner}), tpl_id], callback);
};
o._reValidIdentifier = /^[a-zA-Z][0-9a-zA-Z_]*$/;
var _reValidIdentifier = /^[a-zA-Z][0-9a-zA-Z_]*$/;
o._checkInvalidTemplate = function(template) {
if ( template.version != '0.0.1' ) {
return new Error("Unsupported template version " + template.version);
@@ -135,22 +131,26 @@ o._checkInvalidTemplate = function(template) {
if ( ! tplname ) {
return new Error("Missing template name");
}
if ( ! tplname.match(this._reValidIdentifier) ) {
if ( ! tplname.match(_reValidIdentifier) ) {
return new Error("Invalid characters in template name '" + tplname + "'");
}
var phold = template.placeholders;
for (var k in phold) {
if ( ! k.match(this._reValidIdentifier) ) {
return new Error("Invalid characters in placeholder name '" + k + "'");
}
if ( ! phold[k].hasOwnProperty('default') ) {
return new Error("Missing default for placeholder '" + k + "'");
}
if ( ! phold[k].hasOwnProperty('type') ) {
return new Error("Missing type for placeholder '" + k + "'");
}
};
var placeholders = template.placeholders || {};
var placeholderKeys = Object.keys(placeholders);
for (var i = 0, len = placeholderKeys.length; i < len; i++) {
var placeholderKey = placeholderKeys[i];
if (!placeholderKey.match(_reValidIdentifier)) {
return new Error("Invalid characters in placeholder name '" + placeholderKey + "'");
}
if ( ! placeholders[placeholderKey].hasOwnProperty('default') ) {
return new Error("Missing default for placeholder '" + placeholderKey + "'");
}
if ( ! placeholders[placeholderKey].hasOwnProperty('type') ) {
return new Error("Missing type for placeholder '" + placeholderKey + "'");
}
}
// Check certificate validity
var cert = this.getTemplateCertificate(template);
@@ -168,12 +168,11 @@ o._checkInvalidTemplate = function(template) {
// SignedMaps.addCertificate or SignedMaps.authorizedByCert
//
o.getTemplateCertificate = function(template) {
var cert = {
version: '0.0.1',
template_id: template.name,
auth: template.auth
return {
version: '0.0.1',
template_id: template.name,
auth: template.auth
};
return cert;
};
// Add a template
@@ -209,7 +208,7 @@ o.addTemplate = function(owner, template, callback) {
//
//
var usr_tpl_key = _.template(this.key_usr_tpl, {owner:owner});
var usr_tpl_key = this.key_usr_tpl({owner:owner});
var gotLock = false;
var that = this;
var limit = that._userTemplateLimit();
@@ -293,7 +292,7 @@ o.addTemplate = function(owner, template, callback) {
// @param callback function(err)
//
o.delTemplate = function(owner, tpl_id, callback) {
var usr_tpl_key = _.template(this.key_usr_tpl, {owner:owner});
var usr_tpl_key = this.key_usr_tpl({owner:owner});
var gotLock = false;
var that = this;
Step(
@@ -402,7 +401,7 @@ o.updTemplate = function(owner, tpl_id, template, callback) {
return;
}
var usr_tpl_key = _.template(this.key_usr_tpl, {owner:owner});
var usr_tpl_key = this.key_usr_tpl({owner:owner});
var gotLock = false;
var that = this;
Step(
@@ -496,8 +495,7 @@ o.updTemplate = function(owner, tpl_id, template, callback) {
// Returns a list of template identifiers
//
o.listTemplates = function(owner, callback) {
var usr_tpl_key = _.template(this.key_usr_tpl, {owner:owner});
this._redisCmd('HKEYS', [ usr_tpl_key ], callback);
this._redisCmd('HKEYS', [ this.key_usr_tpl({owner:owner}) ], callback);
};
// Get a templates
@@ -511,17 +509,15 @@ o.listTemplates = function(owner, callback) {
// Return full template definition
//
o.getTemplate = function(owner, tpl_id, callback) {
var usr_tpl_key = _.template(this.key_usr_tpl, {owner:owner});
var that = this;
Step(
function getTemplate() {
that._redisCmd('HGET', [ usr_tpl_key, tpl_id ], this);
that._redisCmd('HGET', [ that.key_usr_tpl({owner:owner}), tpl_id ], this);
},
function parseTemplate(err, tpl_val) {
if ( err ) throw err;
var tpl = JSON.parse(tpl_val);
// Should we strip auth_id ?
return tpl;
return JSON.parse(tpl_val);
},
function finish(err, tpl) {
callback(err, tpl);
@@ -541,25 +537,22 @@ o.getTemplate = function(owner, tpl_id, callback) {
//
// @throws Error on malformed template or parameter
//
o._reNumber = /^([-+]?[\d\.]?\d+([eE][+-]?\d+)?)$/;
o._reCSSColorName = /^[a-zA-Z]+$/;
o._reCSSColorVal = /^#[0-9a-fA-F]{3,6}$/;
o._replaceVars = function(str, params) {
var _reNumber = /^([-+]?[\d\.]?\d+([eE][+-]?\d+)?)$/,
_reCSSColorName = /^[a-zA-Z]+$/,
_reCSSColorVal = /^#[0-9a-fA-F]{3,6}$/;
_replaceVars = function(str, params) {
//return _.template(str, params); // lazy way, possibly dangerous
// Construct regular expressions for each param
if ( ! params._re ) {
params._re = {};
for (var k in params) {
params._re[k] = RegExp("<%=\\s*" + k + "\\s*%>", "g");
}
}
for (var k in params) str = str.replace(params._re[k], params[k]);
return str;
Object.keys(params).forEach(function(k) {
str = str.replace(new RegExp("<%=\\s*" + k + "\\s*%>", "g"), params[k]);
});
return str;
};
o.instance = function(template, params) {
var all_params = {};
var phold = template.placeholders;
for (var k in phold) {
var phold = template.placeholders || {};
Object.keys(phold).forEach(function(k) {
var val = params.hasOwnProperty(k) ? params[k] : phold[k].default;
var type = phold[k].type;
// properly escape
@@ -573,7 +566,7 @@ o.instance = function(template, params) {
}
else if ( type === 'number' ) {
// check it's a number
if ( typeof(val) !== 'number' && ! val.match(this._reNumber) ) {
if ( typeof(val) !== 'number' && ! val.match(_reNumber) ) {
throw new Error("Invalid number value for template parameter '"
+ k + "': " + val);
}
@@ -581,7 +574,7 @@ o.instance = function(template, params) {
else if ( type === 'css_color' ) {
// check it only contains letters or
// starts with # and only contains hexdigits
if ( ! val.match(this._reCSSColorName) && ! val.match(this._reCSSColorVal) ) {
if ( ! val.match(_reCSSColorName) && ! val.match(_reCSSColorVal) ) {
throw new Error("Invalid css_color value for template parameter '"
+ k + "': " + val);
}
@@ -591,14 +584,14 @@ o.instance = function(template, params) {
throw new Error("Invalid placeholder type '" + type + "'");
}
all_params[k] = val;
}
});
// NOTE: we're deep-cloning the layergroup here
var layergroup = JSON.parse(JSON.stringify(template.layergroup));
for (var i=0; i<layergroup.layers.length; ++i) {
var lyropt = layergroup.layers[i].options;
if ( lyropt.cartocss ) lyropt.cartocss = this._replaceVars(lyropt.cartocss, all_params);
if ( lyropt.sql) lyropt.sql = this._replaceVars(lyropt.sql, all_params);
if ( lyropt.cartocss ) lyropt.cartocss = _replaceVars(lyropt.cartocss, all_params);
if ( lyropt.sql) lyropt.sql = _replaceVars(lyropt.sql, all_params);
// Anything else ?
}
return layergroup;

273
npm-shrinkwrap.json generated
View File

@@ -1,29 +1,39 @@
{
"name": "windshaft-cartodb",
"version": "1.14.0",
"version": "1.17.2",
"dependencies": {
"node-varnish": {
"version": "0.3.0",
"from": "http://github.com/Vizzuality/node-varnish/tarball/0.3.0"
"from": "https://github.com/Vizzuality/node-varnish/tarball/0.3.0"
},
"underscore": {
"version": "1.3.3"
"version": "1.6.0"
},
"dot": {
"version": "1.0.2"
},
"windshaft": {
"version": "0.23.0",
"from": "https://github.com/CartoDB/Windshaft/tarball/0.23.0",
"version": "0.27.2",
"from": "https://github.com/CartoDB/Windshaft/tarball/0.27.2",
"dependencies": {
"chronograph": {
"version": "0.1.0",
"from": "git://github.com/CartoDB/chronographjs.git#0.1.0"
},
"grainstore": {
"version": "0.19.0",
"from": "git://github.com/CartoDB/grainstore.git#0.19.0",
"version": "0.22.0",
"from": "https://github.com/CartoDB/grainstore/tarball/0.22.0",
"dependencies": {
"carto": {
"version": "0.9.5-cdb2",
"from": "http://github.com/CartoDB/carto/tarball/0.9.5-cdb2",
"from": "https://github.com/CartoDB/carto/tarball/0.9.5-cdb2",
"dependencies": {
"underscore": {
"version": "1.4.4"
},
"mapnik-reference": {
"version": "5.0.9"
},
"xml2js": {
"version": "0.2.8",
"dependencies": {
@@ -45,14 +55,11 @@
}
}
},
"mapnik-reference": {
"version": "5.0.9"
},
"millstone": {
"version": "0.6.14",
"dependencies": {
"underscore": {
"version": "1.6.0"
"generic-pool": {
"version": "2.0.4"
},
"request": {
"version": "2.34.0",
@@ -73,7 +80,7 @@
"version": "0.12.1",
"dependencies": {
"punycode": {
"version": "1.2.4"
"version": "1.3.1"
}
}
},
@@ -369,16 +376,19 @@
}
},
"zipfile": {
"version": "0.5.2",
"version": "0.5.3",
"dependencies": {
"nan": {
"version": "1.2.0"
},
"node-pre-gyp": {
"version": "0.5.8",
"version": "0.5.19",
"dependencies": {
"nopt": {
"version": "2.2.0",
"version": "2.2.1",
"dependencies": {
"abbrev": {
"version": "1.0.4"
"version": "1.0.5"
}
}
},
@@ -391,7 +401,7 @@
}
},
"request": {
"version": "2.34.0",
"version": "2.36.0",
"dependencies": {
"qs": {
"version": "0.6.6"
@@ -399,15 +409,15 @@
"json-stringify-safe": {
"version": "5.0.0"
},
"mime": {
"version": "1.2.11"
},
"forever-agent": {
"version": "0.5.2"
},
"node-uuid": {
"version": "1.4.1"
},
"mime": {
"version": "1.2.11"
},
"tough-cookie": {
"version": "0.12.1",
"dependencies": {
@@ -433,7 +443,7 @@
}
},
"tunnel-agent": {
"version": "0.3.0"
"version": "0.4.0"
},
"http-signature": {
"version": "0.10.0",
@@ -475,7 +485,7 @@
}
},
"semver": {
"version": "2.2.1"
"version": "2.3.0"
},
"tar": {
"version": "0.1.19",
@@ -489,6 +499,9 @@
"fstream": {
"version": "0.1.25",
"dependencies": {
"mkdirp": {
"version": "0.3.5"
},
"graceful-fs": {
"version": "2.0.3"
}
@@ -511,6 +524,9 @@
"fstream": {
"version": "0.1.25",
"dependencies": {
"mkdirp": {
"version": "0.3.5"
},
"graceful-fs": {
"version": "2.0.3"
},
@@ -539,7 +555,7 @@
}
},
"readable-stream": {
"version": "1.0.26-4",
"version": "1.0.27-1",
"dependencies": {
"core-util-is": {
"version": "1.0.1"
@@ -561,16 +577,24 @@
}
},
"mkdirp": {
"version": "0.3.5"
},
"rc": {
"version": "0.3.4",
"version": "0.5.0",
"dependencies": {
"minimist": {
"version": "0.0.8"
}
}
},
"rc": {
"version": "0.4.0",
"dependencies": {
"minimist": {
"version": "0.0.10"
},
"deep-extend": {
"version": "0.2.8"
"version": "0.2.10"
},
"strip-json-comments": {
"version": "0.1.3"
},
"ini": {
"version": "1.1.0"
@@ -578,36 +602,39 @@
}
},
"rimraf": {
"version": "2.2.6"
"version": "2.2.8"
}
}
}
}
},
"sqlite3": {
"version": "2.2.3",
"version": "2.2.7",
"dependencies": {
"nan": {
"version": "1.1.2"
},
"node-pre-gyp": {
"version": "0.5.9",
"version": "0.5.22",
"dependencies": {
"nopt": {
"version": "2.2.0",
"version": "3.0.1",
"dependencies": {
"abbrev": {
"version": "1.0.4"
"version": "1.0.5"
}
}
},
"npmlog": {
"version": "0.0.6",
"version": "0.1.1",
"dependencies": {
"ansi": {
"version": "0.2.1"
"version": "0.3.0"
}
}
},
"request": {
"version": "2.34.0",
"version": "2.39.0",
"dependencies": {
"qs": {
"version": "0.6.6"
@@ -615,41 +642,44 @@
"json-stringify-safe": {
"version": "5.0.0"
},
"mime-types": {
"version": "1.0.2"
},
"forever-agent": {
"version": "0.5.2"
},
"node-uuid": {
"version": "1.4.1"
},
"mime": {
"version": "1.2.11"
},
"tough-cookie": {
"version": "0.12.1",
"dependencies": {
"punycode": {
"version": "1.2.4"
"version": "1.3.0"
}
}
},
"form-data": {
"version": "0.1.2",
"version": "0.1.4",
"dependencies": {
"combined-stream": {
"version": "0.0.4",
"version": "0.0.5",
"dependencies": {
"delayed-stream": {
"version": "0.0.5"
}
}
},
"mime": {
"version": "1.2.11"
},
"async": {
"version": "0.2.10"
"version": "0.9.0"
}
}
},
"tunnel-agent": {
"version": "0.3.0"
"version": "0.4.0"
},
"http-signature": {
"version": "0.10.0",
@@ -669,7 +699,7 @@
"version": "0.3.0"
},
"hawk": {
"version": "1.0.0",
"version": "1.1.1",
"dependencies": {
"hoek": {
"version": "0.9.1"
@@ -687,28 +717,31 @@
},
"aws-sign2": {
"version": "0.5.0"
},
"stringstream": {
"version": "0.0.4"
}
}
},
"semver": {
"version": "2.2.1"
"version": "3.0.1"
},
"tar": {
"version": "0.1.19",
"version": "1.0.0",
"dependencies": {
"inherits": {
"version": "2.0.1"
},
"block-stream": {
"version": "0.0.7"
},
"fstream": {
"version": "0.1.25",
"version": "1.0.1",
"dependencies": {
"graceful-fs": {
"version": "2.0.3"
"version": "3.0.2"
}
}
},
"inherits": {
"version": "2.0.1"
}
}
},
@@ -725,10 +758,21 @@
"version": "0.7.4"
},
"fstream": {
"version": "0.1.25",
"version": "0.1.31",
"dependencies": {
"graceful-fs": {
"version": "2.0.3"
"version": "3.0.2"
},
"inherits": {
"version": "2.0.1"
}
}
},
"tar": {
"version": "0.1.20",
"dependencies": {
"block-stream": {
"version": "0.0.7"
},
"inherits": {
"version": "2.0.1"
@@ -755,7 +799,7 @@
}
},
"readable-stream": {
"version": "1.0.26-4",
"version": "1.0.27-1",
"dependencies": {
"core-util-is": {
"version": "1.0.1"
@@ -777,16 +821,24 @@
}
},
"mkdirp": {
"version": "0.3.5"
},
"rc": {
"version": "0.3.4",
"version": "0.5.0",
"dependencies": {
"minimist": {
"version": "0.0.8"
}
}
},
"rc": {
"version": "0.5.0",
"dependencies": {
"minimist": {
"version": "0.0.10"
},
"deep-extend": {
"version": "0.2.8"
"version": "0.2.11"
},
"strip-json-comments": {
"version": "0.1.3"
},
"ini": {
"version": "1.1.0"
@@ -794,9 +846,12 @@
}
},
"rimraf": {
"version": "2.2.6"
"version": "2.2.8"
}
}
},
"set-immediate": {
"version": "0.1.1"
}
}
},
@@ -821,9 +876,6 @@
}
}
},
"generic-pool": {
"version": "2.0.4"
},
"express": {
"version": "2.5.11",
"dependencies": {
@@ -846,14 +898,21 @@
}
}
},
"mapnik": {
"version": "0.7.26-cdb1",
"from": "http://github.com/Vizzuality/node-mapnik/tarball/0.7.26-cdb1"
},
"tilelive": {
"version": "4.4.3",
"version": "4.5.3",
"dependencies": {
"optimist": {
"version": "0.3.7",
"version": "0.6.1",
"dependencies": {
"wordwrap": {
"version": "0.0.2"
},
"minimist": {
"version": "0.0.10"
}
}
},
@@ -863,8 +922,14 @@
}
},
"tilelive-mapnik": {
"version": "0.6.9",
"version": "0.6.11",
"dependencies": {
"underscore": {
"version": "1.3.3"
},
"generic-pool": {
"version": "2.0.4"
},
"mime": {
"version": "1.2.11"
},
@@ -908,17 +973,6 @@
"version": "0.1.0",
"from": "git://github.com/CartoDB/node-step-profiler.git#0.1.0"
},
"pg": {
"version": "2.6.2",
"dependencies": {
"generic-pool": {
"version": "2.0.3"
},
"buffer-writer": {
"version": "1.0.0"
}
}
},
"torque.js": {
"version": "2.2.0"
},
@@ -931,24 +985,45 @@
"version": "0.0.5"
},
"request": {
"version": "2.9.202"
"version": "2.9.203"
},
"cartodb-redis": {
"version": "0.5.0",
"from": "git://github.com/CartoDB/node-cartodb-redis.git#0.5.0"
"version": "0.11.0",
"from": "https://github.com/CartoDB/node-cartodb-redis/tarball/0.11.0"
},
"cartodb-psql": {
"version": "0.4.0",
"from": "https://github.com/CartoDB/node-cartodb-psql/tarball/0.4.0",
"dependencies": {
"pg": {
"version": "2.6.2-cdb1",
"from": "git://github.com/CartoDB/node-postgres.git#2.6.2-cdb1",
"dependencies": {
"generic-pool": {
"version": "2.0.3"
},
"buffer-writer": {
"version": "1.0.0"
}
}
}
}
},
"redis-mpool": {
"version": "0.0.4",
"from": "http://github.com/CartoDB/node-redis-mpool/tarball/0.0.4",
"version": "0.1.0",
"from": "https://github.com/CartoDB/node-redis-mpool/tarball/0.1.0",
"dependencies": {
"generic-pool": {
"version": "2.0.4"
"version": "2.1.1"
},
"redis": {
"version": "0.12.1"
},
"hiredis": {
"version": "0.1.17",
"dependencies": {
"bindings": {
"version": "1.2.0"
"version": "1.2.1"
},
"nan": {
"version": "1.1.2"
@@ -957,21 +1032,17 @@
}
}
},
"mapnik": {
"version": "0.7.26-cdb1",
"from": "http://github.com/Vizzuality/node-mapnik/tarball/0.7.26-cdb1"
},
"lzma": {
"version": "1.2.3"
"version": "1.3.7"
},
"log4js": {
"version": "0.6.14",
"version": "0.6.21",
"dependencies": {
"async": {
"version": "0.1.15"
"version": "0.2.10"
},
"readable-stream": {
"version": "1.0.27-1",
"version": "1.0.32",
"dependencies": {
"core-util-is": {
"version": "1.0.1"
@@ -980,7 +1051,7 @@
"version": "0.0.1"
},
"string_decoder": {
"version": "0.10.25-1"
"version": "0.10.31"
},
"inherits": {
"version": "2.0.1"
@@ -990,7 +1061,7 @@
}
},
"rollbar": {
"version": "0.3.8",
"version": "0.3.13",
"dependencies": {
"node-uuid": {
"version": "1.4.1"
@@ -1010,16 +1081,16 @@
"version": "1.1.4"
},
"strftime": {
"version": "0.6.2"
"version": "0.8.2"
},
"mocha": {
"version": "1.14.0",
"version": "1.21.4",
"dependencies": {
"commander": {
"version": "2.0.0"
},
"growl": {
"version": "1.7.0"
"version": "1.8.1"
},
"jade": {
"version": "0.26.3",
@@ -1036,7 +1107,7 @@
"version": "1.0.7"
},
"debug": {
"version": "1.0.2",
"version": "2.0.0",
"dependencies": {
"ms": {
"version": "0.6.2"

View File

@@ -1,7 +1,7 @@
{
"private": true,
"name": "windshaft-cartodb",
"version": "1.14.0",
"version": "1.17.2",
"description": "A map tile server for CartoDB",
"keywords": [
"cartodb"
@@ -22,23 +22,24 @@
"Sandro Santilli <strk@vizzuality.com>"
],
"dependencies": {
"node-varnish": "http://github.com/Vizzuality/node-varnish/tarball/0.3.0",
"underscore" : "~1.3.3",
"windshaft": "https://github.com/CartoDB/Windshaft/tarball/0.23.0",
"step": "0.0.x",
"request": "2.9.202",
"cartodb-redis": "git://github.com/CartoDB/node-cartodb-redis.git#0.5.0",
"redis-mpool": "http://github.com/CartoDB/node-redis-mpool/tarball/0.0.4",
"mapnik": "http://github.com/Vizzuality/node-mapnik/tarball/0.7.26-cdb1",
"lzma": "~1.2.3",
"log4js": "~0.6.10",
"rollbar": "~0.3.1"
"node-varnish": "https://github.com/Vizzuality/node-varnish/tarball/0.3.0",
"underscore" : "~1.6.0",
"dot": "~1.0.2",
"windshaft": "https://github.com/CartoDB/Windshaft/tarball/0.27.2",
"step": "~0.0.5",
"request": "~2.9.203",
"cartodb-redis": "https://github.com/CartoDB/node-cartodb-redis/tarball/0.11.0",
"cartodb-psql": "https://github.com/CartoDB/node-cartodb-psql/tarball/0.4.0",
"redis-mpool": "https://github.com/CartoDB/node-redis-mpool/tarball/0.1.0",
"lzma": "~1.3.7",
"log4js": "~0.6.17",
"rollbar": "~0.3.13"
},
"devDependencies": {
"mocha": "1.14.0",
"redis": "~0.8.3",
"strftime": "~0.6.0",
"semver": "~1.1.0"
"mocha": "~1.21.4",
"redis": "~0.8.6",
"strftime": "~0.8.2",
"semver": "~1.1.4"
},
"scripts": {
"test": "make check"

View File

@@ -4,7 +4,6 @@ var _ = require('underscore');
var redis = require('redis');
var querystring = require('querystring');
var semver = require('semver');
var mapnik = require('mapnik');
var Step = require('step');
var strftime = require('strftime');
var SQLAPIEmu = require(__dirname + '/../support/SQLAPIEmu.js');
@@ -23,7 +22,11 @@ serverOptions = ServerOptions();
var server = new CartodbWindshaft(serverOptions);
server.setMaxListeners(0);
suite('multilayer', function() {
[true, false].forEach(function(cdbQueryTablesFromPostgresEnabledValue) {
global.environment.enabledFeatures = {cdbQueryTablesFromPostgres: cdbQueryTablesFromPostgresEnabledValue};
suite('multilayer:postgres=' + cdbQueryTablesFromPostgresEnabledValue, function() {
var redis_client = redis.createClient(global.environment.redis.port);
var sqlapi_server;
@@ -112,13 +115,12 @@ suite('multilayer', function() {
var jsonquery = cc.substring(dbname.length+1);
var sentquery = JSON.parse(jsonquery);
var expectedQuery = [layergroup.layers[0].options.sql, ';', layergroup.layers[1].options.sql].join('');
assert.equal(sentquery.q, 'SELECT CDB_QueryTables($windshaft$'
assert.equal(sentquery.q, 'WITH querytables AS ( SELECT * FROM CDB_QueryTables($windshaft$'
+ expectedQuery
+ '$windshaft$) as tablenames, EXTRACT(EPOCH FROM max(updated_at)) as max'
+ '$windshaft$) as tablenames )'
+ ' SELECT (SELECT tablenames FROM querytables), EXTRACT(EPOCH FROM max(updated_at)) as max'
+ ' FROM CDB_TableMetadata m'
+ ' WHERE m.tabname = any (CDB_QueryTables($windshaft$'
+ expectedQuery
+ '$windshaft$)::regclass[])');
+ ' WHERE m.tabname = any ((SELECT tablenames from querytables)::regclass[])');
assert.imageEqualsFile(res.body, 'test/fixtures/test_table_0_0_0_multilayer1.png', IMAGE_EQUALS_HIGHER_TOLERANCE_PER_MIL,
function(err, similarity) {
@@ -155,7 +157,7 @@ suite('multilayer', function() {
method: 'GET'
}, {}, function(res) {
assert.equal(res.statusCode, 200, res.body);
assert.equal(res.headers['content-type'], "text/javascript; charset=utf-8; charset=utf-8");
assert.equal(res.headers['content-type'], "application/json; charset=utf-8");
assert.utfgridEqualsFile(res.body, 'test/fixtures/test_table_0_0_0_multilayer1.layer0.grid.json', 2,
function(err, similarity) {
next(err);
@@ -173,7 +175,7 @@ suite('multilayer', function() {
method: 'GET'
}, {}, function(res) {
assert.equal(res.statusCode, 200, res.body);
assert.equal(res.headers['content-type'], "text/javascript; charset=utf-8; charset=utf-8");
assert.equal(res.headers['content-type'], "application/json; charset=utf-8");
assert.utfgridEqualsFile(res.body, 'test/fixtures/test_table_0_0_0_multilayer1.layer1.grid.json', 2,
function(err, similarity) {
next(err);
@@ -395,13 +397,12 @@ suite('multilayer', function() {
.replace(/!bbox!/g, 'ST_MakeEnvelope(0,0,0,0)')
.replace(/!pixel_width!/g, '1')
.replace(/!pixel_height!/g, '1');
assert.equal(sentquery.q, 'SELECT CDB_QueryTables($windshaft$'
assert.equal(sentquery.q, 'WITH querytables AS ( SELECT * FROM CDB_QueryTables($windshaft$'
+ expectedQuery
+ '$windshaft$) as tablenames, EXTRACT(EPOCH FROM max(updated_at)) as max'
+ '$windshaft$) as tablenames )'
+ ' SELECT (SELECT tablenames FROM querytables), EXTRACT(EPOCH FROM max(updated_at)) as max'
+ ' FROM CDB_TableMetadata m'
+ ' WHERE m.tabname = any (CDB_QueryTables($windshaft$'
+ expectedQuery
+ '$windshaft$)::regclass[])');
+ ' WHERE m.tabname = any ((SELECT tablenames from querytables)::regclass[])');
assert.imageEqualsFile(res.body, 'test/fixtures/test_multilayer_bbox.png', IMAGE_EQUALS_TOLERANCE_PER_MIL,
function(err, similarity) {
@@ -433,13 +434,12 @@ suite('multilayer', function() {
.replace('!bbox!', 'ST_MakeEnvelope(0,0,0,0)')
.replace('!pixel_width!', '1')
.replace('!pixel_height!', '1');
assert.equal(sentquery.q, 'SELECT CDB_QueryTables($windshaft$'
+ expectedQuery
+ '$windshaft$) as tablenames, EXTRACT(EPOCH FROM max(updated_at)) as max'
+ ' FROM CDB_TableMetadata m'
+ ' WHERE m.tabname = any (CDB_QueryTables($windshaft$'
assert.equal(sentquery.q, 'WITH querytables AS ( SELECT * FROM CDB_QueryTables($windshaft$'
+ expectedQuery
+ '$windshaft$)::regclass[])');
+ '$windshaft$) as tablenames )'
+ ' SELECT (SELECT tablenames FROM querytables), EXTRACT(EPOCH FROM max(updated_at)) as max'
+ ' FROM CDB_TableMetadata m'
+ ' WHERE m.tabname = any ((SELECT tablenames from querytables)::regclass[])');
assert.imageEqualsFile(res.body, 'test/fixtures/test_multilayer_bbox.png', IMAGE_EQUALS_TOLERANCE_PER_MIL,
function(err, similarity) {
@@ -458,7 +458,7 @@ suite('multilayer', function() {
method: 'GET'
}, {}, function(res) {
assert.equal(res.statusCode, 200, res.body);
assert.equal(res.headers['content-type'], "text/javascript; charset=utf-8; charset=utf-8");
assert.equal(res.headers['content-type'], "application/json; charset=utf-8");
assert.utfgridEqualsFile(res.body, 'test/fixtures/test_multilayer_bbox.grid.json', 2,
function(err, similarity) {
next(err);
@@ -476,7 +476,7 @@ suite('multilayer', function() {
method: 'GET'
}, {}, function(res) {
assert.equal(res.statusCode, 200, res.body);
assert.equal(res.headers['content-type'], "text/javascript; charset=utf-8; charset=utf-8");
assert.equal(res.headers['content-type'], "application/json; charset=utf-8");
assert.utfgridEqualsFile(res.body, 'test/fixtures/test_multilayer_bbox.grid.json', 2,
function(err, similarity) {
next(err);
@@ -752,7 +752,7 @@ suite('multilayer', function() {
method: 'GET'
}, {}, function(res) {
assert.equal(res.statusCode, 200, res.body);
assert.equal(res.headers['content-type'], "text/javascript; charset=utf-8; charset=utf-8");
assert.equal(res.headers['content-type'], "application/json; charset=utf-8");
next(err);
});
},
@@ -1345,3 +1345,4 @@ suite('multilayer', function() {
});
});

View File

@@ -4,7 +4,6 @@ var _ = require('underscore');
var redis = require('redis');
var querystring = require('querystring');
var semver = require('semver');
var mapnik = require('mapnik');
var Step = require('step');
var http = require('http');
var SQLAPIEmu = require(__dirname + '/../support/SQLAPIEmu.js');
@@ -19,12 +18,17 @@ var serverOptions = require(__dirname + '/../../lib/cartodb/server_options')();
var server = new CartodbWindshaft(serverOptions);
server.setMaxListeners(0);
suite('server', function() {
[true, false].forEach(function(cdbQueryTablesFromPostgresEnabledValue) {
global.environment.enabledFeatures = {cdbQueryTablesFromPostgres: cdbQueryTablesFromPostgresEnabledValue};
suite('multilayer:postgres=' + cdbQueryTablesFromPostgresEnabledValue, function() {
var redis_client = redis.createClient(global.environment.redis.port);
var sqlapi_server;
var mapnik_version = global.environment.mapnik_version || mapnik.versions.mapnik;
var mapnik_version = global.environment.mapnik_version || server.getVersion().mapnik;
var test_database = _.template(global.environment.postgres_auth_user, {user_id:1}) + '_db';
var default_style;
if ( semver.satisfies(mapnik_version, '<2.1.0') ) {
@@ -582,7 +586,7 @@ suite('server', function() {
method: 'GET'
},{
status: 200,
headers: { 'Content-Type': 'text/javascript; charset=utf-8; charset=utf-8',
headers: { 'Content-Type': 'application/json; charset=utf-8',
'X-Cache-Channel': test_database+':gadm4' }
}, function() { done(); });
});
@@ -594,7 +598,7 @@ suite('server', function() {
method: 'GET'
},{
status: 200,
headers: { 'Content-Type': 'text/javascript; charset=utf-8; charset=utf-8' }
headers: { 'Content-Type': 'application/json; charset=utf-8' }
}, function() { done(); });
});
@@ -606,7 +610,7 @@ suite('server', function() {
method: 'GET'
},{
status: 200,
headers: { 'Content-Type': 'text/javascript; charset=utf-8; charset=utf-8' }
headers: { 'Content-Type': 'application/json; charset=utf-8' }
}, function() { done(); });
});
@@ -1389,3 +1393,4 @@ suite('server', function() {
});
});

View File

@@ -3,7 +3,6 @@ var _ = require('underscore');
var redis = require('redis');
var querystring = require('querystring');
var semver = require('semver');
var mapnik = require('mapnik');
var Step = require('step');
var strftime = require('strftime');
var SQLAPIEmu = require(__dirname + '/../support/SQLAPIEmu.js');
@@ -25,7 +24,11 @@ var serverOptions = ServerOptions();
var server = new CartodbWindshaft(serverOptions);
server.setMaxListeners(0);
suite('template_api', function() {
[true, false].forEach(function(cdbQueryTablesFromPostgresEnabledValue) {
global.environment.enabledFeatures = {cdbQueryTablesFromPostgres: cdbQueryTablesFromPostgresEnabledValue};
suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, function() {
var redis_client = redis.createClient(global.environment.redis.port);
var sqlapi_server;
@@ -1948,3 +1951,4 @@ suite('template_api', function() {
});
});

View File

@@ -226,6 +226,8 @@ assert.response = function(server, req, res, msg){
response.on('end', function(){
if (timer) clearTimeout(timer);
check();
// Assert response body
if (res.body !== undefined) {
var eql = res.body instanceof RegExp
@@ -271,7 +273,6 @@ assert.response = function(server, req, res, msg){
// Callback
callback(response);
check();
});
});

View File

@@ -86,7 +86,7 @@ if test x"$PREPARE_REDIS" = xyes; then
cat <<EOF | redis-cli -p ${REDIS_PORT} -n 5
HMSET rails:users:localhost id ${TESTUSERID} \
database_name '${TEST_DB}' \
database_name "${TEST_DB}" \
database_host localhost \
map_key 1234
SADD rails:users:localhost:map_key 1235

View File

@@ -7,7 +7,9 @@
var _ = require('underscore');
var assert = require('assert');
var LZMA = require('lzma/lzma_worker.js').LZMA;
var LZMA = require('lzma').LZMA;
var lzmaWorker = new LZMA();
// set environment specific variables
global.environment = require(__dirname + '/../../config/environments/test');
@@ -16,7 +18,7 @@ process.env.NODE_ENV = 'test';
// Utility function to compress & encode LZMA
function lzma_compress_to_base64(payload, mode, callback) {
LZMA.compress(payload, mode,
lzmaWorker.compress(payload, mode,
function(ints) {
ints = ints.map(function(c) { return String.fromCharCode(c + 128) }).join('')
var base64 = new Buffer(ints, 'binary').toString('base64');

View File

@@ -12,6 +12,14 @@ suite('template_maps', function() {
// configure redis pool instance to use in tests
var redis_pool = RedisPool(global.environment.redis);
var signed_maps = new SignedMaps(redis_pool);
var validTemplate = {
version:'0.0.1',
name: 'first',
auth: {},
layergroup: {}
};
var owner = 'me';
test('does not accept template with unsupported version', function(done) {
var tmap = new TemplateMaps(redis_pool, signed_maps);
@@ -502,5 +510,89 @@ suite('template_maps', function() {
}
);
});
var redisCmdFunc = TemplateMaps.prototype._redisCmd;
function runWithRedisStubbed(stubbedCommands, func) {
TemplateMaps.prototype._redisCmd = function(redisFunc, redisArgs, callback) {
redisFunc = redisFunc.toLowerCase();
if (stubbedCommands.hasOwnProperty(redisFunc)) {
callback(null, stubbedCommands[redisFunc]);
} else {
throw 'Unknown command';
}
};
func();
TemplateMaps.prototype._redisCmd = redisCmdFunc;
}
test('_obtainTemplateLock with no previous value, happy case', function(done) {
runWithRedisStubbed({hget: null, hset: 1}, function() {
var templateMaps = new TemplateMaps(redis_pool, signed_maps);
templateMaps._obtainTemplateLock(owner, validTemplate.name, function(err, gotLock) {
assert.ok(!err);
assert.ok(gotLock);
done();
});
});
});
test('_obtainTemplateLock no lock for non expired ttl, simulates obtaining two locks at same time', function(done) {
runWithRedisStubbed({hget: Date.now()}, function() {
var templateMaps = new TemplateMaps(redis_pool, signed_maps);
templateMaps._obtainTemplateLock(owner, validTemplate.name, function(err, gotLock) {
assert.ok(!!err);
assert.equal(gotLock, false);
done();
});
});
});
test('_obtainTemplateLock no lock for non expired ttl, last millisecond of valid ttl', function(done) {
var nowValue = Date.now(),
nowFunc = Date.now;
Date.now = function() {
return nowValue;
};
var lockTtl = 1000;
runWithRedisStubbed({hget: Date.now() - lockTtl, hset: true}, function() {
var templateMaps = new TemplateMaps(redis_pool, signed_maps, {lock_ttl: lockTtl});
templateMaps._obtainTemplateLock(owner, validTemplate.name, function(err, gotLock) {
assert.ok(!!err);
assert.equal(gotLock, false);
Date.now = nowFunc;
done();
});
});
});
test('_obtainTemplateLock gets lock for expired ttl, first millisecond of invalid ttl', function(done) {
var nowValue = Date.now(),
nowFunc = Date.now;
Date.now = function() {
return nowValue;
};
var lockTtl = 1000;
runWithRedisStubbed({hget: Date.now() - lockTtl - 1, hset: true}, function() {
var templateMaps = new TemplateMaps(redis_pool, signed_maps, {lock_ttl: lockTtl});
templateMaps._obtainTemplateLock(owner, validTemplate.name, function(err, gotLock) {
assert.ok(!err);
assert.ok(gotLock);
Date.now = nowFunc;
done();
});
});
});
});