Merge branch 'release/v2.0'
This commit is contained in:
commit
ae9298e04e
5
NEWS.md
5
NEWS.md
@ -1,3 +1,8 @@
|
|||||||
|
1.3.2 (30/11/12)
|
||||||
|
-----
|
||||||
|
* Fix KML export truncation (#70)
|
||||||
|
* Fix UTF8 in shapefile export (#66)
|
||||||
|
|
||||||
1.3.1 (DD/MM/YY)
|
1.3.1 (DD/MM/YY)
|
||||||
-----
|
-----
|
||||||
* Support 'format' and 'filename' params in POST
|
* Support 'format' and 'filename' params in POST
|
||||||
|
@ -471,6 +471,7 @@ function toOGR(dbname, user_id, gcol, sql, skipfields, res, out_format, out_file
|
|||||||
|
|
||||||
var child = spawn(ogr2ogr, [
|
var child = spawn(ogr2ogr, [
|
||||||
'-f', out_format,
|
'-f', out_format,
|
||||||
|
'-lco', 'ENCODING=UTF-8',
|
||||||
out_filename,
|
out_filename,
|
||||||
"PG:host=" + dbhost
|
"PG:host=" + dbhost
|
||||||
+ " user=" + dbuser
|
+ " user=" + dbuser
|
||||||
@ -563,6 +564,7 @@ function toSHP(dbname, user_id, gcol, sql, skipfields, filename, res, callback)
|
|||||||
|
|
||||||
var child = spawn(zip, ['-qrj', '-', dir ]);
|
var child = spawn(zip, ['-qrj', '-', dir ]);
|
||||||
|
|
||||||
|
// TODO: convert to a stream operation
|
||||||
child.stdout.on('data', function(data) {
|
child.stdout.on('data', function(data) {
|
||||||
res.write(data);
|
res.write(data);
|
||||||
});
|
});
|
||||||
@ -663,8 +665,7 @@ function toKML(dbname, user_id, gcol, sql, skipfields, res, callback) {
|
|||||||
function sendResults(err) {
|
function sendResults(err) {
|
||||||
|
|
||||||
if ( ! err ) {
|
if ( ! err ) {
|
||||||
var stream = fs.createReadStream(dumpfile);
|
fs.createReadStream(dumpfile).pipe(res);
|
||||||
util.pump(stream, res);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// cleanup output dir (should be safe to unlink)
|
// cleanup output dir (should be safe to unlink)
|
||||||
@ -706,7 +707,6 @@ function toKML(dbname, user_id, gcol, sql, skipfields, res, callback) {
|
|||||||
function finish(err) {
|
function finish(err) {
|
||||||
if ( err ) callback(err);
|
if ( err ) callback(err);
|
||||||
else {
|
else {
|
||||||
res.end();
|
|
||||||
callback(null);
|
callback(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"name": "cartodb_api",
|
"name": "cartodb_api",
|
||||||
"description": "high speed SQL api for cartodb",
|
"description": "high speed SQL api for cartodb",
|
||||||
"version": "1.3.1",
|
"version": "1.3.2",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "Simon Tokumine, Sandro Santilli, Vizzuality",
|
"name": "Simon Tokumine, Sandro Santilli, Vizzuality",
|
||||||
"url": "http://vizzuality.com",
|
"url": "http://vizzuality.com",
|
||||||
|
@ -726,6 +726,22 @@ test('CSV format', function(done){
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('CSV format, bigger than 81920 bytes', function(done){
|
||||||
|
assert.response(app, {
|
||||||
|
url: '/api/v1/sql',
|
||||||
|
data: querystring.stringify({
|
||||||
|
q: 'SELECT 0 as fname FROM generate_series(0,81920)',
|
||||||
|
format: 'csv'
|
||||||
|
}),
|
||||||
|
headers: {host: 'vizzuality.cartodb.com', 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||||
|
method: 'POST'
|
||||||
|
},{ }, function(res){
|
||||||
|
assert.ok(res.body.length > 81920, 'CSV smaller than expected: ' + res.body.length);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
test('CSV format from POST', function(done){
|
test('CSV format from POST', function(done){
|
||||||
assert.response(app, {
|
assert.response(app, {
|
||||||
url: '/api/v1/sql',
|
url: '/api/v1/sql',
|
||||||
@ -996,6 +1012,25 @@ test('SHP format, unauthenticated, POST', function(done){
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('SHP format, big size, POST', function(done){
|
||||||
|
assert.response(app, {
|
||||||
|
url: '/api/v1/sql',
|
||||||
|
data: querystring.stringify({
|
||||||
|
q: 'SELECT 0 as fname FROM generate_series(0,81920)',
|
||||||
|
format: 'shp'
|
||||||
|
}),
|
||||||
|
headers: {host: 'vizzuality.cartodb.com', 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||||
|
method: 'POST'
|
||||||
|
},{ }, function(res){
|
||||||
|
assert.equal(res.statusCode, 200, res.body);
|
||||||
|
var cd = res.header('Content-Disposition');
|
||||||
|
assert.equal(true, /^attachment/.test(cd), 'SHP is not disposed as attachment: ' + cd);
|
||||||
|
assert.equal(true, /filename=cartodb-query.zip/gi.test(cd), 'Unexpected SHP filename: ' + cd);
|
||||||
|
assert.ok(res.body.length > 81920, 'SHP smaller than expected: ' + res.body.length);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
test('SHP format, unauthenticated, with custom filename', function(done){
|
test('SHP format, unauthenticated, with custom filename', function(done){
|
||||||
assert.response(app, {
|
assert.response(app, {
|
||||||
url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4%20LIMIT%201&format=shp&filename=myshape',
|
url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4%20LIMIT%201&format=shp&filename=myshape',
|
||||||
@ -1072,6 +1107,33 @@ test('SHP format, authenticated', function(done){
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// See https://github.com/Vizzuality/CartoDB-SQL-API/issues/66
|
||||||
|
test('SHP format, unauthenticated, with utf8 data', function(done){
|
||||||
|
var query = querystring.stringify({
|
||||||
|
q: "SELECT '♥♦♣♠' as f, st_makepoint(0,0,4326) as the_geom",
|
||||||
|
format: 'shp',
|
||||||
|
filename: 'myshape'
|
||||||
|
});
|
||||||
|
assert.response(app, {
|
||||||
|
url: '/api/v1/sql?' + query,
|
||||||
|
headers: {host: 'vizzuality.cartodb.com'},
|
||||||
|
encoding: 'binary',
|
||||||
|
method: 'GET'
|
||||||
|
},{ }, function(res){
|
||||||
|
assert.equal(res.statusCode, 200, res.body);
|
||||||
|
var tmpfile = '/tmp/myshape.zip';
|
||||||
|
var err = fs.writeFileSync(tmpfile, res.body, 'binary');
|
||||||
|
if (err) { done(err); return }
|
||||||
|
var zf = new zipfile.ZipFile(tmpfile);
|
||||||
|
var buffer = zf.readFileSync('myshape.dbf');
|
||||||
|
fs.unlinkSync(tmpfile);
|
||||||
|
var strings = buffer.toString();
|
||||||
|
assert.ok(/♥♦♣♠/.exec(strings), "Cannot find '♥♦♣♠' in here:\n" + strings);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// KML tests
|
// KML tests
|
||||||
|
|
||||||
test('KML format, unauthenticated', function(done){
|
test('KML format, unauthenticated', function(done){
|
||||||
@ -1112,6 +1174,25 @@ test('KML format, unauthenticated, POST', function(done){
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('KML format, bigger than 81920 bytes', function(done){
|
||||||
|
assert.response(app, {
|
||||||
|
url: '/api/v1/sql',
|
||||||
|
data: querystring.stringify({
|
||||||
|
q: 'SELECT 0 as fname FROM generate_series(0,81920)',
|
||||||
|
format: 'kml'
|
||||||
|
}),
|
||||||
|
headers: {host: 'vizzuality.cartodb.com', 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||||
|
method: 'POST'
|
||||||
|
},{ }, function(res){
|
||||||
|
assert.equal(res.statusCode, 200, res.body);
|
||||||
|
var cd = res.header('Content-Disposition');
|
||||||
|
assert.equal(true, /^attachment/.test(cd), 'KML is not disposed as attachment: ' + cd);
|
||||||
|
assert.equal(true, /filename=cartodb-query.kml/gi.test(cd), 'Unexpected KML filename: ' + cd);
|
||||||
|
assert.ok(res.body.length > 81920, 'KML smaller than expected: ' + res.body.length);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
test('KML format, skipfields', function(done){
|
test('KML format, skipfields', function(done){
|
||||||
assert.response(app, {
|
assert.response(app, {
|
||||||
url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4%20LIMIT%201&format=kml&skipfields=address,cartodb_id',
|
url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4%20LIMIT%201&format=kml&skipfields=address,cartodb_id',
|
||||||
|
37
tools/cdbsql
37
tools/cdbsql
@ -14,6 +14,7 @@ var hasReadline = parseInt(nodevers[0]) > 0 || parseInt(nodevers[1]) >= 8;
|
|||||||
//console.log('Node version ' + nodevers.join(',') + ( hasReadline ? ' has' : ' does not have' ) + ' readline support');
|
//console.log('Node version ' + nodevers.join(',') + ( hasReadline ? ' has' : ' does not have' ) + ' readline support');
|
||||||
|
|
||||||
var readline = hasReadline ? require('readline') : null;
|
var readline = hasReadline ? require('readline') : null;
|
||||||
|
var tty = require('tty');
|
||||||
|
|
||||||
var me = process.argv[1];
|
var me = process.argv[1];
|
||||||
|
|
||||||
@ -32,6 +33,7 @@ function usage(exit_code) {
|
|||||||
console.log(" --key <string> API authentication key (none)");
|
console.log(" --key <string> API authentication key (none)");
|
||||||
console.log(" --format <string> Response format (json)");
|
console.log(" --format <string> Response format (json)");
|
||||||
console.log(" --dp <num> Decimal places in geojson format (unspecified)");
|
console.log(" --dp <num> Decimal places in geojson format (unspecified)");
|
||||||
|
console.log(" --echo-queries echo commands sent to server");
|
||||||
if ( hasReadline ) {
|
if ( hasReadline ) {
|
||||||
console.log(" --batch Send all read queries at once (off)");
|
console.log(" --batch Send all read queries at once (off)");
|
||||||
}
|
}
|
||||||
@ -50,6 +52,7 @@ var api_version = 1;
|
|||||||
var api_key;
|
var api_key;
|
||||||
var sql;
|
var sql;
|
||||||
var decimal_places;
|
var decimal_places;
|
||||||
|
var echo_queries = false;
|
||||||
|
|
||||||
var arg;
|
var arg;
|
||||||
while ( arg = process.argv.shift() ) {
|
while ( arg = process.argv.shift() ) {
|
||||||
@ -83,6 +86,9 @@ while ( arg = process.argv.shift() ) {
|
|||||||
else if ( arg == '--batch' ) {
|
else if ( arg == '--batch' ) {
|
||||||
batch_mode = true;
|
batch_mode = true;
|
||||||
}
|
}
|
||||||
|
else if ( arg == '--echo-queries' ) {
|
||||||
|
echo_queries = true;
|
||||||
|
}
|
||||||
else if ( ! sql ) {
|
else if ( ! sql ) {
|
||||||
sql = arg;
|
sql = arg;
|
||||||
}
|
}
|
||||||
@ -93,23 +99,27 @@ while ( arg = process.argv.shift() ) {
|
|||||||
|
|
||||||
var hostname = username + '.' + domain;
|
var hostname = username + '.' + domain;
|
||||||
|
|
||||||
|
if ( ! tty.isatty(process.stdin) || ! tty.isatty(process.stdout) ) {
|
||||||
|
batch_mode = true;
|
||||||
|
}
|
||||||
|
|
||||||
if ( ! sql ) {
|
if ( ! sql ) {
|
||||||
|
|
||||||
|
if ( ! batch_mode ) {
|
||||||
|
|
||||||
if ( readline ) {
|
if ( readline ) {
|
||||||
|
|
||||||
var rl = readline.createInterface({
|
var rl = readline.createInterface({
|
||||||
input: process.stdin,
|
input: process.stdin,
|
||||||
output: process.stdout
|
output: process.stdout,
|
||||||
});
|
});
|
||||||
|
|
||||||
if ( ! batch_mode ) {
|
|
||||||
rl.setPrompt(hostname + '> ');
|
rl.setPrompt(hostname + '> ');
|
||||||
rl.prompt();
|
rl.prompt();
|
||||||
}
|
|
||||||
|
|
||||||
sql = '';
|
sql = '';
|
||||||
rl.on('line', function(line) {
|
rl.on('line', function(line) {
|
||||||
sql += line;
|
sql += line;
|
||||||
if ( ! batch_mode ) {
|
|
||||||
// TODO: some sanity checking, like trim the line or check if it ends with semicolon
|
// TODO: some sanity checking, like trim the line or check if it ends with semicolon
|
||||||
if ( sql.length ) {
|
if ( sql.length ) {
|
||||||
processQuery(sql, function() {
|
processQuery(sql, function() {
|
||||||
@ -117,19 +127,11 @@ if ( ! sql ) {
|
|||||||
rl.prompt();
|
rl.prompt();
|
||||||
});
|
});
|
||||||
} else rl.prompt();
|
} else rl.prompt();
|
||||||
}
|
|
||||||
}).on('close', function() {
|
}).on('close', function() {
|
||||||
if ( batch_mode ) {
|
|
||||||
if ( sql.length ) {
|
|
||||||
processQuery(sql);
|
|
||||||
sql = '';
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if ( sql.length ) {
|
if ( sql.length ) {
|
||||||
console.warn("Unprocessed sql left: [" + sql + "]");
|
console.warn("Unprocessed sql left: [" + sql + "]");
|
||||||
}
|
}
|
||||||
console.log("Good bye");
|
console.log("Good bye");
|
||||||
}
|
|
||||||
}).on('SIGCONT', function() {
|
}).on('SIGCONT', function() {
|
||||||
// this is needed so not to exit on stop/resume
|
// this is needed so not to exit on stop/resume
|
||||||
rl.prompt();
|
rl.prompt();
|
||||||
@ -137,6 +139,15 @@ if ( ! sql ) {
|
|||||||
} else {
|
} else {
|
||||||
usage(1);
|
usage(1);
|
||||||
}
|
}
|
||||||
|
} else { // batch mode
|
||||||
|
process.stdin.resume();
|
||||||
|
process.stdin.setEncoding('utf8');
|
||||||
|
sql = '';
|
||||||
|
process.stdin.on('data', function(chunk) {
|
||||||
|
// TODO: some sanity checking, like trim the line or check if it ends with semicolon
|
||||||
|
processQuery(chunk);
|
||||||
|
});
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
processQuery(sql);
|
processQuery(sql);
|
||||||
}
|
}
|
||||||
@ -177,7 +188,7 @@ function processQuery(sql, callback)
|
|||||||
console.log("Request:", request);
|
console.log("Request:", request);
|
||||||
var sqlprint = sql.length > 100 ? sql.substring(0, 100) + ' ... [truncated ' + (sql.length-100) + ' bytes]' : sql;
|
var sqlprint = sql.length > 100 ? sql.substring(0, 100) + ' ... [truncated ' + (sql.length-100) + ' bytes]' : sql;
|
||||||
sqlprint = sqlprint.split('\n').join(' ');
|
sqlprint = sqlprint.split('\n').join(' ');
|
||||||
console.log("Query:", sqlprint);
|
if ( echo_queries ) console.log("Query:", sqlprint);
|
||||||
console.log("Response status: " + res.statusCode);
|
console.log("Response status: " + res.statusCode);
|
||||||
console.log('Response body:');
|
console.log('Response body:');
|
||||||
console.dir(body);
|
console.dir(body);
|
||||||
|
Loading…
Reference in New Issue
Block a user