Merge branch 'master' of github.com:Vizzuality/Windshaft-cartodb

This commit is contained in:
javi 2014-01-22 19:10:37 +01:00
commit e4e08db0b4
10 changed files with 415 additions and 88 deletions

22
NEWS.md
View File

@ -1,4 +1,24 @@
1.6.0 -- 20YY-MM-DD 1.6.2 -- 2014-MM-DD
-------------------
Bug fixes:
* Fix support for long (>64k chars) queries in layergroup creation (#111)
Enhancements:
* Enhance tools/show_style to accept an environment parameter and
print XML style now it is not in redis anymore (#110)
1.6.1 -- 2014-01-15
-------------------
Bug fixes:
* Drop cache headers from error responses (#107)
* Localize external CartoCSS resources at renderer creation time (#108)
1.6.0 -- 2014-01-10
------------------- -------------------
New features: New features:

View File

@ -1,7 +1,8 @@
Windshaft-CartoDB Windshaft-CartoDB
================== ==================
[![Build Status](https://travis-ci.org/CartoDB/Windshaft-cartodb.png)](http://travis-ci.org/CartoDB/Windshaft-cartodb) [![Build Status](http://travis-ci.org/CartoDB/Windshaft-cartodb.png)]
(http://travis-ci.org/CartoDB/Windshaft-cartodb)
This is the CartoDB map tiler. It extends Windshaft with some extra This is the CartoDB map tiler. It extends Windshaft with some extra
functionality and custom filters for authentication functionality and custom filters for authentication

View File

@ -42,6 +42,22 @@ var CartodbWindshaft = function(serverOptions) {
return version; return version;
} }
// Override sendError to drop added cache headers (if any)
// See http://github.com/CartoDB/Windshaft-cartodb/issues/107
var ws_sendError = ws.sendError;
ws.sendError = function(res) {
// NOTE: the "res" object will have no _headers when
// faked by Windshaft, see
// http://github.com/CartoDB/Windshaft-cartodb/issues/109
//
if ( res._headers ) {
delete res._headers['cache-control'];
delete res._headers['last-modified'];
delete res._headers['x-cache-channel'];
}
ws_sendError.apply(this, arguments);
};
/** /**
* Helper to allow access to the layer to be used in the maps infowindow popup. * Helper to allow access to the layer to be used in the maps infowindow popup.
*/ */

View File

@ -78,7 +78,15 @@ module.exports = function(){
if (_.isString(api_key) && api_key != '') { qs.api_key = api_key; } if (_.isString(api_key) && api_key != '') { qs.api_key = api_key; }
// call sql api // call sql api
request.get({url:sqlapi, qs:qs, json:true}, function(err, res, body){ //
// NOTE: using POST to avoid size limits:
// Seehttp://github.com/CartoDB/Windshaft-cartodb/issues/111
//
// TODO: use "host" header to allow IP based specification
// of sqlapi address (and avoid a DNS lookup)
//
request.post({url:sqlapi, body:qs, json:true},
function(err, res, body){
if (err){ if (err){
console.log('ERROR connecting to SQL API on ' + sqlapi + ': ' + err); console.log('ERROR connecting to SQL API on ' + sqlapi + ': ' + err);
callback(err); callback(err);
@ -188,7 +196,7 @@ module.exports = function(){
} }
var dbName = req.params.dbname; var dbName = req.params.dbname;
var username = req.headers.host.split('.')[0]; var username = this.userByReq(req);
// strip out windshaft/mapnik inserted sql if present // strip out windshaft/mapnik inserted sql if present
var sql = req.params.sql.match(/^\((.*)\)\sas\scdbq$/); var sql = req.params.sql.match(/^\((.*)\)\sas\scdbq$/);
@ -281,7 +289,7 @@ module.exports = function(){
sql = sql.join(';'); sql = sql.join(';');
var dbName = req.params.dbname; var dbName = req.params.dbname;
var usr = req.headers.host.split('.')[0]; var usr = this.userByReq(req);
var key = req.params.map_key; var key = req.params.map_key;
var cacheKey = dbName + ':' + token; var cacheKey = dbName + ':' + token;
@ -577,7 +585,7 @@ console.log("Checking authorization from signer " + signer + " for resource " +
if ( tksplit.length > 1 ) req.params.cache_buster= tksplit[1]; if ( tksplit.length > 1 ) req.params.cache_buster= tksplit[1];
tksplit = req.params.token.split('@'); tksplit = req.params.token.split('@');
if ( tksplit.length > 1 ) { if ( tksplit.length > 1 ) {
req.params.signer = req.headers.host.split('.')[0]; req.params.signer = this.userByReq(req);
if ( tksplit[0] ) req.params.signer = tksplit[0]; if ( tksplit[0] ) req.params.signer = tksplit[0];
req.params.token = tksplit[1]; req.params.token = tksplit[1];
//console.log("Request for token " + req.params.token + " with signature from " + req.params.signer); //console.log("Request for token " + req.params.token + " with signature from " + req.params.signer);

174
npm-shrinkwrap.json generated
View File

@ -1,6 +1,6 @@
{ {
"name": "windshaft-cartodb", "name": "windshaft-cartodb",
"version": "1.6.0", "version": "1.6.1",
"dependencies": { "dependencies": {
"node-varnish": { "node-varnish": {
"version": "0.1.1" "version": "0.1.1"
@ -9,14 +9,14 @@
"version": "1.3.3" "version": "1.3.3"
}, },
"windshaft": { "windshaft": {
"version": "0.14.5", "version": "0.15.0",
"dependencies": { "dependencies": {
"grainstore": { "grainstore": {
"version": "0.15.2", "version": "0.16.0",
"dependencies": { "dependencies": {
"carto": { "carto": {
"version": "0.9.5-cdb2", "version": "0.9.5-cdb2",
"from": "git://github.com/CartoDB/carto.git#0.9.5-cdb2", "from": "http://github.com/CartoDB/carto/tarball/0.9.5-cdb2",
"dependencies": { "dependencies": {
"underscore": { "underscore": {
"version": "1.4.4" "version": "1.4.4"
@ -25,7 +25,7 @@
"version": "0.2.8", "version": "0.2.8",
"dependencies": { "dependencies": {
"sax": { "sax": {
"version": "0.5.5" "version": "0.5.8"
} }
} }
}, },
@ -131,46 +131,136 @@
"version": "0.3.8" "version": "0.3.8"
}, },
"zipfile": { "zipfile": {
"version": "0.4.2" "version": "0.4.3"
}, },
"sqlite3": { "sqlite3": {
"version": "2.1.19", "version": "2.2.0",
"dependencies": { "dependencies": {
"tar.gz": { "node-pre-gyp": {
"version": "0.1.1", "version": "0.2.5",
"dependencies": { "dependencies": {
"fstream": { "nopt": {
"version": "0.1.25", "version": "2.1.2",
"dependencies": { "dependencies": {
"rimraf": { "abbrev": {
"version": "2.2.4" "version": "1.0.4"
},
"graceful-fs": {
"version": "2.0.1"
},
"inherits": {
"version": "2.0.1"
} }
} }
}, },
"npmlog": {
"version": "0.0.6",
"dependencies": {
"ansi": {
"version": "0.2.1"
}
}
},
"semver": {
"version": "2.1.0"
},
"tar": { "tar": {
"version": "0.1.18", "version": "0.1.19",
"dependencies": { "dependencies": {
"inherits": { "inherits": {
"version": "2.0.1" "version": "2.0.1"
}, },
"block-stream": { "block-stream": {
"version": "0.0.7" "version": "0.0.7"
},
"fstream": {
"version": "0.1.25",
"dependencies": {
"graceful-fs": {
"version": "2.0.1"
}
}
} }
} }
}, },
"commander": { "tar-pack": {
"version": "1.1.1", "version": "2.0.0",
"dependencies": { "dependencies": {
"keypress": { "uid-number": {
"version": "0.1.0" "version": "0.0.3"
},
"once": {
"version": "1.1.1"
},
"debug": {
"version": "0.7.4"
},
"fstream": {
"version": "0.1.25",
"dependencies": {
"graceful-fs": {
"version": "2.0.1"
},
"inherits": {
"version": "2.0.1"
}
}
},
"fstream-ignore": {
"version": "0.0.7",
"dependencies": {
"minimatch": {
"version": "0.2.14",
"dependencies": {
"sigmund": {
"version": "1.0.0"
}
}
},
"inherits": {
"version": "2.0.1"
}
}
},
"readable-stream": {
"version": "1.0.24"
},
"graceful-fs": {
"version": "1.2.3"
} }
} }
},
"aws-sdk": {
"version": "2.0.0-rc8",
"dependencies": {
"xml2js": {
"version": "0.2.4",
"dependencies": {
"sax": {
"version": "0.6.0"
}
}
},
"xmlbuilder": {
"version": "0.4.2"
}
}
},
"rc": {
"version": "0.3.2",
"dependencies": {
"optimist": {
"version": "0.3.7",
"dependencies": {
"wordwrap": {
"version": "0.0.2"
}
}
},
"deep-extend": {
"version": "0.2.6"
},
"ini": {
"version": "1.1.0"
}
}
},
"rimraf": {
"version": "2.2.5"
} }
} }
} }
@ -239,7 +329,7 @@
} }
}, },
"tilelive-mapnik": { "tilelive-mapnik": {
"version": "0.6.4", "version": "0.6.5",
"dependencies": { "dependencies": {
"eio": { "eio": {
"version": "0.2.2" "version": "0.2.2"
@ -267,10 +357,18 @@
"version": "0.3.0" "version": "0.3.0"
}, },
"redis-mpool": { "redis-mpool": {
"version": "0.0.2", "version": "0.0.3",
"dependencies": { "dependencies": {
"generic-pool": { "generic-pool": {
"version": "2.0.4" "version": "2.0.4"
},
"hiredis": {
"version": "0.1.16",
"dependencies": {
"bindings": {
"version": "1.1.1"
}
}
} }
} }
}, },
@ -280,31 +378,15 @@
"lzma": { "lzma": {
"version": "1.2.3" "version": "1.2.3"
}, },
"strftime": {
"version": "0.6.2"
},
"semver": { "semver": {
"version": "1.1.4" "version": "1.1.4"
}, },
"strftime": {
"version": "0.6.2"
},
"redis": { "redis": {
"version": "0.8.6" "version": "0.8.6"
}, },
"redis-mpool": {
"version": "0.0.2",
"dependencies": {
"generic-pool": {
"version": "2.0.4"
}
}
},
"hiredis": {
"version": "0.1.15",
"dependencies": {
"bindings": {
"version": "1.1.0"
}
}
},
"mocha": { "mocha": {
"version": "1.14.0", "version": "1.14.0",
"dependencies": { "dependencies": {
@ -338,7 +420,7 @@
"version": "3.2.3", "version": "3.2.3",
"dependencies": { "dependencies": {
"minimatch": { "minimatch": {
"version": "0.2.12", "version": "0.2.14",
"dependencies": { "dependencies": {
"lru-cache": { "lru-cache": {
"version": "2.5.0" "version": "2.5.0"

View File

@ -1,7 +1,7 @@
{ {
"private": true, "private": true,
"name": "windshaft-cartodb", "name": "windshaft-cartodb",
"version": "1.6.0", "version": "1.6.2",
"description": "A map tile server for CartoDB", "description": "A map tile server for CartoDB",
"keywords": [ "keywords": [
"cartodb" "cartodb"
@ -24,7 +24,7 @@
"dependencies": { "dependencies": {
"node-varnish": "0.1.1", "node-varnish": "0.1.1",
"underscore" : "~1.3.3", "underscore" : "~1.3.3",
"windshaft" : "~0.14.5", "windshaft" : "~0.15.0",
"step": "0.0.x", "step": "0.0.x",
"request": "2.9.202", "request": "2.9.202",
"cartodb-redis": "~0.3.0", "cartodb-redis": "~0.3.0",

View File

@ -19,6 +19,14 @@ var serverOptions = require(__dirname + '/../../lib/cartodb/server_options');
var server = new CartodbWindshaft(serverOptions); var server = new CartodbWindshaft(serverOptions);
server.setMaxListeners(0); server.setMaxListeners(0);
// Check that the response headers do not request caching
// Throws on failure
function checkNoCache(res) {
assert.ok(!res.headers.hasOwnProperty('x-cache-channel'));
assert.ok(!res.headers.hasOwnProperty('cache-control')); // is this correct ?
assert.ok(!res.headers.hasOwnProperty('last-modified')); // is this correct ?
}
suite('multilayer', function() { suite('multilayer', function() {
var redis_client = redis.createClient(global.environment.redis.port); var redis_client = redis.createClient(global.environment.redis.port);
@ -460,6 +468,35 @@ suite('multilayer', function() {
}); });
}); });
// Also tests that server doesn't crash:
// see http://github.com/CartoDB/Windshaft-cartodb/issues/109
test("layergroup creation fails if sql is bogus", function(done) {
var layergroup = {
stat_tag: 'random_tag',
version: '1.0.0',
layers: [
{ options: {
sql: 'select bogus(0,0) as the_geom_webmercator',
cartocss: '#layer { polygon-fill:red; }',
cartocss_version: '2.0.1'
} }
]
};
assert.response(server, {
url: '/tiles/layergroup',
method: 'POST',
headers: {host: 'localhost', 'Content-Type': 'application/json' },
data: JSON.stringify(layergroup)
}, {}, function(res) {
assert.equal(res.statusCode, 400, res.body);
var parsed = JSON.parse(res.body);
var msg = parsed.errors[0];
assert.ok(msg.match(/bogus.*exist/), msg);
checkNoCache(res);
done();
});
});
test("layergroup with 2 private-table layers", function(done) { test("layergroup with 2 private-table layers", function(done) {
var layergroup = { var layergroup = {
@ -863,6 +900,71 @@ suite('multilayer', function() {
); );
}); });
// SQL strings can be of arbitrary length, when using POST
// See https://github.com/CartoDB/Windshaft-cartodb/issues/111
test("sql string can be very long", function(done){
var long_val = 'pretty';
for (var i=0; i<1024; ++i) long_val += ' long'
long_val += ' string';
var sql = "SELECT ";
for (var i=0; i<16; ++i)
sql += "'" + long_val + "'::text as pretty_long_field_name_" + i + ", ";
sql += "cartodb_id, the_geom_webmercator FROM gadm4 g";
var layergroup = {
version: '1.0.0',
layers: [
{ options: {
sql: sql,
cartocss: '#layer { marker-fill:red; }',
cartocss_version: '2.0.1'
} }
]
};
var errors = [];
var expected_token;
Step(
function do_post()
{
var data = JSON.stringify(layergroup);
assert.ok(data.length > 1024*64);
var next = this;
assert.response(server, {
url: '/tiles/layergroup?api_key=1234',
method: 'POST',
headers: {host: 'localhost', 'Content-Type': 'application/json' },
data: data
}, {}, function(res) { next(null, res); });
},
function check_result(err, res) {
if ( err ) throw err;
assert.equal(res.statusCode, 200, res.statusCode + ': ' + res.body);
var parsedBody = JSON.parse(res.body);
var token_components = parsedBody.layergroupid.split(':');
expected_token = token_components[0];
return null;
},
function cleanup(err) {
if ( err ) errors.push(err.message);
if ( ! expected_token ) return null;
var next = this;
redis_client.keys("map_style|test_cartodb_user_1_db|~" + expected_token, function(err, matches) {
if ( err ) errors.push(err.message);
assert.equal(matches.length, 1, "Missing expected token " + expected_token + " from redis: " + matches);
redis_client.del(matches, function(err) {
if ( err ) errors.push(err.message);
next();
});
});
},
function finish(err) {
if ( err ) errors.push('' + err);
if ( errors.length ) done(new Error(errors.join(',')));
else done(null);
}
);
});
suiteTeardown(function(done) { suiteTeardown(function(done) {
// This test will add map_style records, like // This test will add map_style records, like

View File

@ -107,7 +107,7 @@ suite('server', function() {
}, function(res) { }, function(res) {
var parsed = JSON.parse(res.body); var parsed = JSON.parse(res.body);
assert.equal(parsed.style, _.template(default_style, {table: 'my_table'})); assert.equal(parsed.style, _.template(default_style, {table: 'my_table'}));
assert.equal(parsed.style_version, mapnik.versions.mapnik); assert.equal(parsed.style_version, mapnik_version);
done(); done();
}); });
}); });
@ -125,6 +125,7 @@ suite('server', function() {
assert.equal(res.statusCode, 400, res.body); assert.equal(res.statusCode, 400, res.body);
assert.deepEqual(JSON.parse(res.body), assert.deepEqual(JSON.parse(res.body),
{error: 'Sorry, you are unauthorized (permission denied)'}); {error: 'Sorry, you are unauthorized (permission denied)'});
assert.ok(!res.headers.hasOwnProperty('cache-control'));
done(); done();
}); });
}); });
@ -142,6 +143,7 @@ suite('server', function() {
assert.equal(res.statusCode, 400, res.statusCode + ': ' + res.body); assert.equal(res.statusCode, 400, res.statusCode + ': ' + res.body);
assert.deepEqual(JSON.parse(res.body), assert.deepEqual(JSON.parse(res.body),
{error:"missing unknown_user's database_name in redis (try CARTODB/script/restore_redis)"}); {error:"missing unknown_user's database_name in redis (try CARTODB/script/restore_redis)"});
assert.ok(!res.headers.hasOwnProperty('cache-control'));
done(); done();
}); });
}); });
@ -158,7 +160,7 @@ suite('server', function() {
var parsed = JSON.parse(res.body); var parsed = JSON.parse(res.body);
var style = _.template(default_style, {table: 'test_table_private_1'}); var style = _.template(default_style, {table: 'test_table_private_1'});
assert.equal(parsed.style, style); assert.equal(parsed.style, style);
assert.equal(parsed.style_version, mapnik.versions.mapnik); assert.equal(parsed.style_version, mapnik_version);
done(); done();
}); });
}); });
@ -212,9 +214,12 @@ suite('server', function() {
url: '/tiles/my_table/style', url: '/tiles/my_table/style',
method: 'POST' method: 'POST'
},{ },{
status: 400,
body: '{"error":"must send style information"}' body: '{"error":"must send style information"}'
}, function() { done(); }); }, function(res) {
assert.equal(res.statusCode, 400);
assert.ok(!res.headers.hasOwnProperty('cache-control'));
done();
});
}); });
test("post'ing bad style returns 400 with error", function(done){ test("post'ing bad style returns 400 with error", function(done){
@ -351,7 +356,7 @@ suite('server', function() {
assert.equal(res.statusCode, 200, res.body); assert.equal(res.statusCode, 200, res.body);
var parsed = JSON.parse(res.body); var parsed = JSON.parse(res.body);
assert.equal(parsed.style, style); assert.equal(parsed.style, style);
assert.equal(parsed.style_version, mapnik.versions.mapnik); assert.equal(parsed.style_version, mapnik_version);
done(); done();
}); });
}); });
@ -379,7 +384,7 @@ suite('server', function() {
var parsed = JSON.parse(res.body); var parsed = JSON.parse(res.body);
// NOTE: no transform expected for the specific style // NOTE: no transform expected for the specific style
assert.equal(parsed.style, style); assert.equal(parsed.style, style);
assert.equal(parsed.style_version, mapnik.versions.mapnik); assert.equal(parsed.style_version, mapnik_version);
done(); done();
}); });
}); });
@ -766,6 +771,8 @@ suite('server', function() {
assert.equal(res.statusCode, 400, res.statusCode + ': ' + res.body); assert.equal(res.statusCode, 400, res.statusCode + ': ' + res.body);
assert.deepEqual(JSON.parse(res.body), assert.deepEqual(JSON.parse(res.body),
{error:"missing unknown_user's database_name in redis (try CARTODB/script/restore_redis)"}); {error:"missing unknown_user's database_name in redis (try CARTODB/script/restore_redis)"});
assert.ok(!res.headers.hasOwnProperty('cache-control'),
"Unexpected Cache-Control: " + res.headers['cache-control']);
done(); done();
}); });
}); });
@ -786,6 +793,9 @@ suite('server', function() {
}, function(res) { }, function(res) {
// 401 Unauthorized // 401 Unauthorized
assert.equal(res.statusCode, 401, res.statusCode + ': ' + res.body); assert.equal(res.statusCode, 401, res.statusCode + ': ' + res.body);
// Failed in 1.6.0 of https://github.com/CartoDB/Windshaft-cartodb/issues/107
assert.ok(!res.headers.hasOwnProperty('cache-control'),
"Unexpected Cache-Control: " + res.headers['cache-control']);
done(); done();
}); });
}); });
@ -1179,6 +1189,7 @@ suite('server', function() {
method: 'DELETE' method: 'DELETE'
},{}, function(res) { },{}, function(res) {
assert.equal(res.statusCode, 404, res.statusCode + ': ' + res.body); assert.equal(res.statusCode, 404, res.statusCode + ': ' + res.body);
assert.ok(!res.headers.hasOwnProperty('cache-control'));
done(); done();
}); });
}); });
@ -1210,6 +1221,7 @@ suite('server', function() {
},{}, function(res) { },{}, function(res) {
// FIXME: should be 401 instead // FIXME: should be 401 instead
assert.equal(res.statusCode, 500, res.statusCode + ': ' + res.body); assert.equal(res.statusCode, 500, res.statusCode + ': ' + res.body);
assert.ok(!res.headers.hasOwnProperty('cache-control'));
done(); done();
}); });
}); });
@ -1262,6 +1274,7 @@ suite('server', function() {
method: 'DELETE' method: 'DELETE'
},{}, function(res) { },{}, function(res) {
assert.equal(res.statusCode, 404, res.statusCode + ': ' + res.body); assert.equal(res.statusCode, 404, res.statusCode + ': ' + res.body);
assert.ok(!res.headers.hasOwnProperty('cache-control'));
done(); done();
}); });
}); });

View File

@ -5,31 +5,60 @@ var o = function(port, cb) {
this.queries = []; this.queries = [];
var that = this; var that = this;
this.sqlapi_server = http.createServer(function(req,res) { this.sqlapi_server = http.createServer(function(req,res) {
var query = url.parse(req.url, true).query; //console.log("server got request with method " + req.method);
that.queries.push(query); var query;
if ( query.q.match('SQLAPIERROR') ) { if ( req.method == 'GET' ) {
res.statusCode = 400; query = url.parse(req.url, true).query;
res.write(JSON.stringify({'error':'Some error occurred'})); that.handleQuery(query, res);
} else if ( query.q.match('EPOCH.* as max') ) { }
// This is the structure of the known query sent by tiler else if ( req.method == 'POST') {
var row = { var data = '';
'max': 1234567890.123 req.on('data', function(chunk) {
}; //console.log("GOT Chunk " + chunk);
res.write(JSON.stringify({rows: [ row ]})); data += chunk;
} else { });
var qs = JSON.stringify(query); req.on('end', function() {
var row = { //console.log("Data is: "); console.dir(data);
// This is the structure of the known query sent by tiler query = JSON.parse(data);
'cdb_querytables': '{' + qs + '}', //console.log("Parsed is: "); console.dir(query);
'max': qs //console.log("handleQuery is " + that.handleQuery);
}; that.handleQuery(query, res);
res.write(JSON.stringify({rows: [ row ]})); });
} }
res.end(); else {
that.handleQuery('SQLAPIEmu does not support method' + req.method, res);
}
}).listen(port, cb); }).listen(port, cb);
}; };
o.prototype.handleQuery = function(query, res) {
this.queries.push(query);
if ( query.q.match('SQLAPIERROR') ) {
res.statusCode = 400;
res.write(JSON.stringify({'error':'Some error occurred'}));
} else if ( query.q.match('EPOCH.* as max') ) {
// This is the structure of the known query sent by tiler
var row = {
'max': 1234567890.123
};
res.write(JSON.stringify({rows: [ row ]}));
} else {
var qs = JSON.stringify(query);
var row = {
// This is the structure of the known query sent by tiler
'cdb_querytables': '{' + qs + '}',
'max': qs
};
var out_obj = {rows: [ row ]};
var out = JSON.stringify(out_obj);
res.write(out);
}
res.end();
};
o.prototype.close = function(cb) { o.prototype.close = function(cb) {
this.sqlapi_server.close(cb); this.sqlapi_server.close(cb);
}; };

View File

@ -2,28 +2,84 @@
# TODO: port to node, if you really need it # TODO: port to node, if you really need it
REDIS_PORT=6379 # default port ENV='development';
BASEDIR=`cd $(dirname $0)/../; pwd`
if test -z "$1"; then if test -z "$1"; then
echo "Usage: $0 <username> [<tablename>|~<token>]" >&2 echo "Usage: $0 [--env <environment>] <username> [<tablename>|~<token>]" >&2
echo " environment defaults to 'development'"
exit 1
fi
username=""
token=""
while test -n "$1"; do
if test "$1" = "--env"; then
shift; ENV="$1"; shift
elif test -z "$username"; then
username="$1"; shift
elif test -z "$token"; then
token="$1"; shift
else
echo "Unused option $1" >&2
shift
fi
done
echo "Using environment '${ENV}'"
CONFIG="${BASEDIR}/config/environments/${ENV}.js"
REDIS_PORT=`node -e "console.log(require('${CONFIG}').redis.port)"`
if test $? -ne 0; then
exit 1 exit 1
fi fi
username="$1"
token="$2"
dbname=`redis-cli -p ${REDIS_PORT} -n 5 hget "rails:users:${username}" "database_name"` dbname=`redis-cli -p ${REDIS_PORT} -n 5 hget "rails:users:${username}" "database_name"`
if test $? -ne 0; then if test $? -ne 0; then
exit 1 exit 1
fi fi
if test -z "${dbname}"; then if test -z "${dbname}"; then
echo "Username ${username} unknown by redis (try CARTODB/script/restore_redis?)" >&2 echo "Username ${username} unknown by redis on port ${REDIS_PORT} (try CARTODB/script/restore_redis?)" >&2
exit 1 exit 1
fi fi
echo "Database name for user ${username}: ${dbname}" # only if verbose? echo "Database name for user ${username}: ${dbname}" # only if verbose?
if test -n "$token"; then if test -n "$token"; then
redis-cli get "map_style|${dbname}|${token}" | sed -e 's/\\n/\n/g' -e 's/\\//g' rec=`redis-cli get "map_style|${dbname}|${token}"`
if test -z "${rec}"; then
echo "${token}: no such map style known by redis on port ${REDIS_PORT}" >&2
exit 1
fi
#echo "${rec}"
escrec=`echo "${rec}" | sed -e 's/\\\\/\\\\\\\\/g'`
#echo "${escrec}"
node <<EOF
var x=JSON.parse('${escrec}');
console.log('style: ' + x.style);
console.log('version: ' + x.version);
global.environment = require('${CONFIG}');
var serverOptions = require('${BASEDIR}/lib/cartodb/server_options'); // _after_ setting global.environment
var grainstore = require('${BASEDIR}/node_modules/windshaft/node_modules/grainstore/lib/grainstore');
var mml_store = new grainstore.MMLStore(serverOptions.redis, serverOptions.grainstore);
var builderconfig = {dbname:'${dbname}'};
if ( '${token}'.match(/^~/) ) {
builderconfig.token = '${token}'.substring(1);
} else {
builderconfig.table = '${token}';
}
var mml_builder = mml_store.mml_builder(builderconfig,
function(err, payload) {
if ( err ) throw err;
mml_builder.toXML(function(err, xml) {
if ( err ) throw err;
console.log('- XML - ');
console.log(xml);
});
});
EOF
#echo "${rec}" | sed -e 's/\\n/\n/g' -e 's/\\//g'
else else
redis-cli keys "map_style|${dbname}|*" redis-cli keys "map_style|${dbname}|*"
fi fi