Merge pull request #220 from CartoDB/jshint

Add jshint linting as part of the test suite
This commit is contained in:
Raul Ochoa 2015-05-13 14:55:06 +02:00
commit 80e485d475
41 changed files with 1041 additions and 824 deletions

3
.jshintignore Normal file
View File

@ -0,0 +1,3 @@
test/support/
test/websocket_test/
app/models/formats/topojson.js

98
.jshintrc Normal file
View File

@ -0,0 +1,98 @@
{
// // JSHint Default Configuration File (as on JSHint website)
// // See http://jshint.com/docs/ for more details
//
// "maxerr" : 50, // {int} Maximum error before stopping
//
// // Enforcing
// "bitwise" : true, // true: Prohibit bitwise operators (&, |, ^, etc.)
// "camelcase" : false, // true: Identifiers must be in camelCase
"curly" : true, // true: Require {} for every new block or scope
"eqeqeq" : true, // true: Require triple equals (===) for comparison
"forin" : true, // true: Require filtering for..in loops with obj.hasOwnProperty()
"freeze" : true, // true: prohibits overwriting prototypes of native objects such as Array, Date etc.
"immed" : true, // true: Require immediate invocations to be wrapped in parens e.g. `(function () { } ());`
// "indent" : 4, // {int} Number of spaces to use for indentation
// "latedef" : false, // true: Require variables/functions to be defined before being used
"newcap" : true, // true: Require capitalization of all constructor functions e.g. `new F()`
"noarg" : true, // true: Prohibit use of `arguments.caller` and `arguments.callee`
// "noempty" : true, // true: Prohibit use of empty blocks
"nonbsp" : true, // true: Prohibit "non-breaking whitespace" characters.
"nonew" : true, // true: Prohibit use of constructors for side-effects (without assignment)
// "plusplus" : false, // true: Prohibit use of `++` & `--`
// "quotmark" : false, // Quotation mark consistency:
// // false : do nothing (default)
// // true : ensure whatever is used is consistent
// // "single" : require single quotes
// // "double" : require double quotes
"undef" : true, // true: Require all non-global variables to be declared (prevents global leaks)
"unused" : true, // true: Require all defined variables be used
// "strict" : true, // true: Requires all functions run in ES5 Strict Mode
// "maxparams" : false, // {int} Max number of formal params allowed per function
// "maxdepth" : false, // {int} Max depth of nested blocks (within functions)
// "maxstatements" : false, // {int} Max number statements per function
"maxcomplexity" : 6, // {int} Max cyclomatic complexity per function
"maxlen" : 120, // {int} Max number of characters per line
//
// // Relaxing
// "asi" : false, // true: Tolerate Automatic Semicolon Insertion (no semicolons)
// "boss" : false, // true: Tolerate assignments where comparisons would be expected
"debug" : false, // true: Allow debugger statements e.g. browser breakpoints.
// "eqnull" : false, // true: Tolerate use of `== null`
// "es5" : false, // true: Allow ES5 syntax (ex: getters and setters)
// "esnext" : false, // true: Allow ES.next (ES6) syntax (ex: `const`)
// "moz" : false, // true: Allow Mozilla specific syntax (extends and overrides esnext features)
// // (ex: `for each`, multiple try/catch, function expression…)
// "evil" : false, // true: Tolerate use of `eval` and `new Function()`
// "expr" : false, // true: Tolerate `ExpressionStatement` as Programs
// "funcscope" : false, // true: Tolerate defining variables inside control statements
// "globalstrict" : false, // true: Allow global "use strict" (also enables 'strict')
// "iterator" : false, // true: Tolerate using the `__iterator__` property
// "lastsemic" : false, // true: Tolerate omitting a semicolon for the last statement of a 1-line block
// "laxbreak" : false, // true: Tolerate possibly unsafe line breakings
// "laxcomma" : false, // true: Tolerate comma-first style coding
// "loopfunc" : false, // true: Tolerate functions being defined in loops
// "multistr" : false, // true: Tolerate multi-line strings
// "noyield" : false, // true: Tolerate generator functions with no yield statement in them.
// "notypeof" : false, // true: Tolerate invalid typeof operator values
// "proto" : false, // true: Tolerate using the `__proto__` property
// "scripturl" : false, // true: Tolerate script-targeted URLs
// "shadow" : false, // true: Allows re-define variables later in code e.g. `var x=1; x=2;`
// "sub" : false, // true: Tolerate using `[]` notation when it can still be expressed in dot notation
// "supernew" : false, // true: Tolerate `new function () { ... };` and `new Object;`
// "validthis" : false, // true: Tolerate using this in a non-constructor function
//
// // Environments
// "browser" : true, // Web Browser (window, document, etc)
// "browserify" : false, // Browserify (node.js code in the browser)
// "couch" : false, // CouchDB
// "devel" : true, // Development/debugging (alert, confirm, etc)
// "dojo" : false, // Dojo Toolkit
// "jasmine" : false, // Jasmine
// "jquery" : false, // jQuery
"mocha" : true, // Mocha
// "mootools" : false, // MooTools
"node" : true, // Node.js
// "nonstandard" : false, // Widely adopted globals (escape, unescape, etc)
// "prototypejs" : false, // Prototype and Scriptaculous
// "qunit" : false, // QUnit
// "rhino" : false, // Rhino
// "shelljs" : false, // ShellJS
// "worker" : false, // Web Workers
// "wsh" : false, // Windows Scripting Host
// "yui" : false, // Yahoo User Interface
// Custom Globals
"globals" : { // additional predefined global variables
"suite": true,
"suiteSetup": true,
"test": true,
"suiteTeardown": true,
"beforeEach": true,
"afterEach": true,
"before": true,
"after": true,
"describe": true,
"it": true
}
}

View File

@ -22,5 +22,4 @@ env:
language: node_js
node_js:
- "0.8"
- "0.10"

View File

@ -6,3 +6,23 @@ clean:
check:
npm test
jshint:
@echo "***jshint***"
@./node_modules/.bin/jshint app/ test/ app.js
test:
@echo "***tests***"
test/run_tests.sh ${RUNTESTFLAGS} test/unit/*.js test/unit/model/*.js test/acceptance/*.js test/acceptance/export/*.js
test-unit:
@echo "***unit tests***"
test/run_tests.sh ${RUNTESTFLAGS} test/unit/*.js test/unit/model/*.js
test-acceptance:
@echo "***acceptance tests***"
test/run_tests.sh ${RUNTESTFLAGS} test/acceptance/*.js test/acceptance/export/*.js
test-all: jshint test
.PHONY: test

46
app.js
View File

@ -9,20 +9,24 @@
* environments: [development, test, production]
*
*/
var _ = require('underscore'),
fs = require('fs'),
path = require('path');
var _ = require('underscore');
var fs = require('fs');
var path = require('path');
if ( process.argv[2] ) ENV = process.argv[2];
else if ( process.env['NODE_ENV'] ) ENV = process.env['NODE_ENV'];
else ENV = 'development';
var ENV = process.env.NODE_ENV || 'development';
process.env['NODE_ENV'] = ENV;
if (process.argv[2]) {
ENV = process.argv[2];
}
process.env.NODE_ENV = ENV;
var availableEnvironments = ['development', 'production', 'test', 'staging'];
// sanity check arguments
if (ENV != 'development' && ENV != 'production' && ENV != 'test' && ENV != 'staging' ) {
if (availableEnvironments.indexOf(ENV) === -1) {
console.error("\nnode app.js [environment]");
console.error("environments: development, staging, production, test");
console.error("environments: " + availableEnvironments.join(', '));
process.exit(1);
}
@ -32,8 +36,8 @@ var env = require(__dirname + '/config/environments/' + ENV);
env.api_hostname = require('os').hostname().split('.')[0];
_.extend(global.settings, env);
global.log4js = require('log4js')
log4js_config = {
global.log4js = require('log4js');
var log4js_config = {
appenders: [],
replaceConsole:true
};
@ -63,31 +67,33 @@ if ( global.settings.rollbar ) {
});
}
log4js.configure(log4js_config, { cwd: __dirname });
global.logger = log4js.getLogger();
global.log4js.configure(log4js_config, { cwd: __dirname });
global.logger = global.log4js.getLogger();
// kick off controller
if ( ! global.settings.base_url ) global.settings.base_url = '/api/*';
if ( ! global.settings.base_url ) {
global.settings.base_url = '/api/*';
}
var version = require("./package").version;
var app = require(global.settings.app_root + '/app/controllers/app')();
app.listen(global.settings.node_port, global.settings.node_host, function() {
console.log("CartoDB SQL API " + version + " listening on " +
global.settings.node_host + ":" + global.settings.node_port +
" with base_url " + global.settings.base_url
+ " (" + ENV + ")");
console.log(
"CartoDB SQL API %s listening on %s:%s with base_url %s (%s)",
version, global.settings.node_host, global.settings.node_port, global.settings.base_url, ENV
);
});
process.on('uncaughtException', function(err) {
logger.error('Uncaught exception: ' + err.stack);
global.logger.error('Uncaught exception: ' + err.stack);
});
process.on('SIGHUP', function() {
global.log4js.clearAndShutdownAppenders(function() {
global.log4js.configure(log4js_config);
global.logger = log4js.getLogger();
global.logger = global.log4js.getLogger();
console.log('Log files reloaded');
});
});

View File

@ -12,10 +12,8 @@ ApikeyAuth.prototype.verifyCredentials = function(options, callback) {
};
ApikeyAuth.prototype.hasCredentials = function() {
return !!(this.req.query.api_key
|| this.req.query.map_key
|| (this.req.body && this.req.body.api_key)
|| (this.req.body && this.req.body.map_key));
return !!(this.req.query.api_key || this.req.query.map_key ||
(this.req.body && this.req.body.api_key) || (this.req.body && this.req.body.map_key));
};
/**
@ -30,14 +28,14 @@ function verifyRequest(req, requiredApi, callback) {
var valid = false;
if ( requiredApi ) {
if ( requiredApi == req.query.map_key ) {
if ( requiredApi === req.query.map_key ) {
valid = true;
} else if ( requiredApi == req.query.api_key ) {
} else if ( requiredApi === req.query.api_key ) {
valid = true;
// check also in request body
} else if ( req.body && req.body.map_key && requiredApi == req.body.map_key ) {
} else if ( req.body && req.body.map_key && requiredApi === req.body.map_key ) {
valid = true;
} else if ( req.body && req.body.api_key && requiredApi == req.body.api_key ) {
} else if ( req.body && req.body.api_key && requiredApi === req.body.api_key ) {
valid = true;
}
}

View File

@ -1,10 +1,10 @@
// too bound to the request object, but ok for now
var _ = require('underscore')
, OAuthUtil = require('oauth-client')
, url = require('url')
, Step = require('step');
var _ = require('underscore');
var OAuthUtil = require('oauth-client');
var step = require('step');
var assert = require('assert');
var oAuth = function(){
var oAuth = (function(){
var me = {
oauth_database: 3,
oauth_user_key: "rails:oauth_access_tokens:<%= oauth_access_key %>",
@ -15,7 +15,7 @@ var oAuth = function(){
// * in GET request
// * in header
me.parseTokens = function(req){
var query_oauth = _.clone(req.method == "POST" ? req.body: req.query);
var query_oauth = _.clone(req.method === "POST" ? req.body: req.query);
var header_oauth = {};
var oauth_variables = ['oauth_body_hash',
'oauth_consumer_key',
@ -34,9 +34,10 @@ var oAuth = function(){
var header_string = req.headers.authorization;
if (!_.isUndefined(header_string)) {
_.each(oauth_variables, function(oauth_key){
var matched_string = header_string.match(new RegExp(oauth_key + '=\"([^\"]+)\"'))
if (!_.isNull(matched_string))
var matched_string = header_string.match(new RegExp(oauth_key + '=\"([^\"]+)\"'));
if (!_.isNull(matched_string)) {
header_oauth[oauth_key] = decodeURIComponent(matched_string[1]);
}
});
}
@ -69,12 +70,12 @@ var oAuth = function(){
var ohash;
var signature;
Step(
step(
function getTokensFromURL(){
return oAuth.parseTokens(req);
},
function getOAuthHash(err, data){
if (err) throw err;
assert.ifError(err);
// this is oauth request only if oauth headers are present
this.is_oauth_request = !_.isEmpty(data);
@ -87,8 +88,10 @@ var oAuth = function(){
}
},
function regenerateSignature(err, data){
if (err) throw err;
if (!this.is_oauth_request) return null;
assert.ifError(err);
if (!this.is_oauth_request) {
return null;
}
ohash = data;
var consumer = OAuthUtil.createConsumer(ohash.consumer_key, ohash.consumer_secret);
@ -98,7 +101,7 @@ var oAuth = function(){
var method = req.method;
var host = req.headers.host;
if(!httpProto || (httpProto != 'http' && httpProto != 'https')) {
if(!httpProto || (httpProto !== 'http' && httpProto !== 'https')) {
var msg = "Unknown HTTP protocol " + httpProto + ".";
err = new Error(msg);
err.http_status = 500;
@ -111,13 +114,13 @@ var oAuth = function(){
// remove signature from passed_tokens
signature = passed_tokens.oauth_signature;
delete passed_tokens['oauth_signature'];
delete passed_tokens.oauth_signature;
var joined = {};
// remove oauth_signature from body
if(req.body) {
delete req.body['oauth_signature'];
delete req.body.oauth_signature;
}
_.extend(joined, req.body ? req.body : null);
_.extend(joined, passed_tokens);
@ -126,7 +129,7 @@ var oAuth = function(){
return signer.sign(method, path, joined);
},
function checkSignature(err, data){
if (err) throw err;
assert.ifError(err);
//console.log(data + " should equal the provided signature: " + signature);
callback(err, (signature === data && !_.isUndefined(data)) ? true : null);
@ -139,7 +142,7 @@ var oAuth = function(){
};
return me;
}();
})();
function OAuthAuth(req) {
this.req = req;

View File

@ -14,36 +14,47 @@
// eg. vizzuality.cartodb.com/api/v1/?sql=SELECT * from my_table
//
//
var express = require('express');
var path = require('path');
var step = require('step');
var crypto = require('crypto');
var os = require('os');
var Profiler = require('step-profiler');
var StatsD = require('node-statsd').StatsD;
var PSQL = require('cartodb-psql');
var _ = require('underscore');
var LRU = require('lru-cache');
var assert = require('assert');
if ( ! process.env['PGAPPNAME'] )
process.env['PGAPPNAME']='cartodb_sqlapi';
var CdbRequest = require('../models/cartodb_request');
var AuthApi = require('../auth/auth_api');
var formats = require('../models/formats');
var HealthCheck = require('../monitoring/health_check');
var PgErrorHandler = require('../postgresql/error_handler');
process.env.PGAPPNAME = process.env.PGAPPNAME || 'cartodb_sqlapi';
// jshint ignore:start
function pad(n) { return n < 10 ? '0' + n : n; }
Date.prototype.toJSON = function() {
var s = this.getFullYear() + '-' + pad(this.getMonth() + 1) + '-' + pad(this.getDate()) + 'T' +
pad(this.getHours()) + ':' + pad(this.getMinutes()) + ':' + pad(this.getSeconds());
var offset = this.getTimezoneOffset();
if (offset === 0) {
s += 'Z';
} else {
s += ( offset < 0 ? '+' : '-' ) + pad(Math.abs(offset / 60)) + pad(Math.abs(offset % 60));
}
return s;
};
// jshint ignore:end
// jshint maxcomplexity:21
function App() {
var path = require('path');
var app = express.createServer();
var express = require('express')
, app = express.createServer()
, Step = require('step')
, crypto = require('crypto')
, fs = require('fs')
, os = require('os')
, zlib = require('zlib')
, util = require('util')
, Profiler = require('step-profiler')
, StatsD = require('node-statsd').StatsD
, MetadataDB = require('cartodb-redis')
, PSQL = require('cartodb-psql')
, CdbRequest = require(global.settings.app_root + '/app/models/cartodb_request')
, AuthApi = require(global.settings.app_root + '/app/auth/auth_api')
, _ = require('underscore')
, LRU = require('lru-cache')
, formats = require(global.settings.app_root + '/app/models/formats')
, HealthCheck = require(global.settings.app_root + '/app/monitoring/health_check')
, PgErrorHandler = require(global.settings.app_root + '/app/postgresql/error_handler')
;
var metadataBackend = MetadataDB({
var metadataBackend = require('cartodb-redis')({
host: global.settings.redis_host,
port: global.settings.redis_port,
max: global.settings.redisPool,
@ -63,25 +74,6 @@ var tableCache = LRU({
maxAge: global.settings.tableCacheMaxAge || 1000*60*10
});
function pad(n) { return n < 10 ? '0' + n : n }
Date.prototype.toJSON = function() {
var s = this.getFullYear() + '-'
+ pad(this.getMonth() + 1) + '-'
+ pad(this.getDate()) + 'T'
+ pad(this.getHours()) + ':'
+ pad(this.getMinutes()) + ':'
+ pad(this.getSeconds());
var offset = this.getTimezoneOffset();
if (offset == 0) s += 'Z';
else {
s += ( offset < 0 ? '+' : '-' )
+ pad(Math.abs(offset / 60))
+ pad(Math.abs(offset % 60))
}
return s;
};
var loggerOpts = {
buffer: true,
format: global.settings.log_format ||
@ -89,7 +81,7 @@ var loggerOpts = {
};
if ( global.log4js ) {
app.use(log4js.connectLogger(log4js.getLogger(), _.defaults(loggerOpts, {level:'info'})));
app.use(global.log4js.connectLogger(global.log4js.getLogger(), _.defaults(loggerOpts, {level:'info'})));
} else {
app.use(express.logger(loggerOpts));
}
@ -110,7 +102,7 @@ if ( global.settings.statsd ) {
var last_err = statsd_client.last_error;
var last_msg = last_err.msg;
var this_msg = ''+err;
if ( this_msg != last_msg ) {
if ( this_msg !== last_msg ) {
console.error("statsd client socket error: " + err);
statsd_client.last_error.count = 1;
statsd_client.last_error.msg = this_msg;
@ -147,7 +139,7 @@ if ( global.settings.hasOwnProperty('node_socket_timeout') ) {
var timeout = parseInt(global.settings.node_socket_timeout);
app.use(function(req, res, next) {
req.connection.setTimeout(timeout);
next()
next();
});
}
@ -198,25 +190,27 @@ function handleVersion(req, res) {
function handleQuery(req, res) {
// extract input
var body = (req.body) ? req.body : {};
var params = _.extend({}, req.query, body); // clone so don't modify req.params or req.body so oauth is not broken
var sql = params.q;
var limit = parseInt(params.rows_per_page);
var offset = parseInt(params.page);
var orderBy = params.order_by;
var body = (req.body) ? req.body : {};
var params = _.extend({}, req.query, body); // clone so don't modify req.params or req.body so oauth is not broken
var sql = params.q;
var limit = parseInt(params.rows_per_page);
var offset = parseInt(params.page);
var orderBy = params.order_by;
var sortOrder = params.sort_order;
var requestedFormat = params.format;
var format = _.isArray(requestedFormat) ? _.last(requestedFormat) : requestedFormat;
var format = _.isArray(requestedFormat) ? _.last(requestedFormat) : requestedFormat;
var requestedFilename = params.filename;
var filename = requestedFilename;
var filename = requestedFilename;
var requestedSkipfields = params.skipfields;
var cdbUsername = cdbReq.userByReq(req);
var skipfields;
var dp = params.dp; // decimal point digits (defaults to 6)
var gn = "the_geom"; // TODO: read from configuration file
var dp = params.dp; // decimal point digits (defaults to 6)
var gn = "the_geom"; // TODO: read from configuration file
var tableCacheItem;
if ( req.profiler ) req.profiler.start('sqlapi.query');
if ( req.profiler ) {
req.profiler.start('sqlapi.query');
}
req.aborted = false;
req.on("close", function() {
@ -248,8 +242,9 @@ function handleQuery(req, res) {
// Accept both comma-separated string or array of comma-separated strings
if ( requestedSkipfields ) {
if ( _.isString(requestedSkipfields) ) skipfields = requestedSkipfields.split(',');
else if ( _.isArray(requestedSkipfields) ) {
if ( _.isString(requestedSkipfields) ) {
skipfields = requestedSkipfields.split(',');
} else if ( _.isArray(requestedSkipfields) ) {
skipfields = [];
_.each(requestedSkipfields, function(ele) {
skipfields = skipfields.concat(ele.split(','));
@ -287,14 +282,16 @@ function handleQuery(req, res) {
var authApi = new AuthApi(req, params),
dbParams;
if ( req.profiler ) req.profiler.done('init');
if ( req.profiler ) {
req.profiler.done('init');
}
// 1. Get database from redis via the username stored in the host header subdomain
// 2. Run the request through OAuth to get R/W user id if signed
// 3. Get the list of tables affected by the query
// 4. Setup headers
// 5. Send formatted results back
Step(
step(
function getDatabaseConnectionParams() {
checkAborted('getDatabaseConnectionParams');
// If the request is providing credentials it may require every DB parameters
@ -307,12 +304,14 @@ function handleQuery(req, res) {
function authenticate(err, userDBParams) {
if (err) {
err.http_status = 404;
err.message = "Sorry, we can't find CartoDB user '" + cdbUsername
+ "'. Please check that you have entered the correct domain.";
err.message = "Sorry, we can't find CartoDB user '" + cdbUsername + "'. " +
"Please check that you have entered the correct domain.";
throw err;
}
if ( req.profiler ) req.profiler.done('getDBParams');
if ( req.profiler ) {
req.profiler.done('getDBParams');
}
dbParams = userDBParams;
@ -330,7 +329,9 @@ function handleQuery(req, res) {
throw err;
}
if ( req.profiler ) req.profiler.done('authenticate');
if ( req.profiler ) {
req.profiler.done('authenticate');
}
if (_.isBoolean(isAuthenticated) && isAuthenticated) {
authenticated = isAuthenticated;
@ -349,9 +350,11 @@ function handleQuery(req, res) {
function queryExplain(err){
var self = this;
if (err) throw err;
assert.ifError(err);
if ( req.profiler ) req.profiler.done('setDBAuth');
if ( req.profiler ) {
req.profiler.done('setDBAuth');
}
checkAborted('queryExplain');
@ -373,16 +376,20 @@ function handleQuery(req, res) {
var tables = raw_tables.split(/^\{(.*)\}$/)[1].split(',');
self(null, tables);
} else {
console.error("Unexpected result from CDB_QueryTables($quotesql$" + sql + "$quotesql$): " + result);
console.error(
"Unexpected result from CDB_QueryTables($quotesql$" + sql + "$quotesql$): " + result
);
self(null, []);
}
});
}
},
function setHeaders(err, tables){
if (err) throw err;
assert.ifError(err);
if ( req.profiler ) req.profiler.done('queryExplain');
if ( req.profiler ) {
req.profiler.done('queryExplain');
}
checkAborted('setHeaders');
@ -410,8 +417,8 @@ function handleQuery(req, res) {
}
}
var fClass = formats[format];
formatter = new fClass();
var FormatClass = formats[format];
formatter = new FormatClass();
req.formatter = formatter;
@ -454,7 +461,7 @@ function handleQuery(req, res) {
return null;
},
function generateFormat(err){
if (err) throw err;
assert.ifError(err);
checkAborted('generateFormat');
// TODO: drop this, fix UI!
@ -493,25 +500,32 @@ function handleQuery(req, res) {
function errorHandle(err){
formatter = null;
if ( err ) handleException(err, res);
if ( err ) {
handleException(err, res);
}
if ( req.profiler ) {
req.profiler.sendStats(); // TODO: do on nextTick ?
}
if (statsd_client) {
if ( err ) statsd_client.increment('sqlapi.query.error');
else statsd_client.increment('sqlapi.query.success');
if ( err ) {
statsd_client.increment('sqlapi.query.error');
} else {
statsd_client.increment('sqlapi.query.success');
}
}
}
);
} catch (err) {
handleException(err, res);
if (statsd_client) statsd_client.increment('sqlapi.query.error');
if (statsd_client) {
statsd_client.increment('sqlapi.query.error');
}
}
}
function handleCacheStatus(req, res){
var tableCacheValues = tableCache.values();
var totalExplainHits = _.reduce(tableCacheValues, function(memo, res) { return memo + res.hits}, 0);
var totalExplainHits = _.reduce(tableCacheValues, function(memo, res) { return memo + res.hits; }, 0);
var totalExplainKeys = tableCacheValues.length;
res.send({explain: {pid: process.pid, hits: totalExplainHits, keys : totalExplainKeys }});
}
@ -543,7 +557,8 @@ function handleHealthCheck(req, res) {
function getContentDisposition(formatter, filename, inline) {
var ext = formatter.getFileExtension();
var time = new Date().toUTCString();
return ( inline ? 'inline' : 'attachment' ) +'; filename=' + filename + '.' + ext + '; modification-date="' + time + '";';
return ( inline ? 'inline' : 'attachment' ) + '; filename=' + filename + '.' + ext + '; ' +
'modification-date="' + time + '";';
}
function setCrossDomain(res){
@ -575,13 +590,13 @@ function handleException(err, res) {
_.defaults(msg, pgErrorHandler.getFields());
if (global.settings.environment == 'development') {
if (global.settings.environment === 'development') {
msg.stack = err.stack;
}
if (global.settings.environment !== 'test'){
// TODO: email this Exception report
console.error("EXCEPTION REPORT: " + err.stack)
console.error("EXCEPTION REPORT: " + err.stack);
}
// allow cross site post

View File

@ -1,6 +1,8 @@
function ArrayBufferSer(type, data, options) {
if(type === undefined) throw "ArrayBufferSer should be created with a type";
if(type === undefined) {
throw "ArrayBufferSer should be created with a type";
}
this.options = options || {};
this._initFunctions();
this.headerSize = 8;
@ -14,13 +16,14 @@ function ArrayBufferSer(type, data, options) {
var w = this.writeFn[type];
var i;
if(!this.options.delta) {
for(var i = 0; i < data.length; ++i) {
for(i = 0; i < data.length; ++i) {
this[w](data[i]);
}
} else {
this[w](data[0]);
for(var i = 1; i < data.length; ++i) {
for(i = 1; i < data.length; ++i) {
this[w](data[i] - data[i - 1]);
}
}
@ -66,7 +69,7 @@ ArrayBufferSer.prototype = {
var s = this.sizes[type];
if(s) {
var r = off % s;
return r == 0 ? 0 : s - r;
return r === 0 ? 0 : s - r;
}
return 0;
},
@ -78,7 +81,7 @@ ArrayBufferSer.prototype = {
return s*t.length;
}
s = 0;
if(this.type == ArrayBufferSer.STRING) {
if(this.type === ArrayBufferSer.STRING) {
// calculate size with padding
t.forEach(function(arr) {
var pad = self._paddingFor(offset, ArrayBufferSer.MAX_PADDING);
@ -108,16 +111,30 @@ ArrayBufferSer.prototype = {
return this.headerSize + this._sizeFor(this.headerSize, this.data);
},
writeFn: ['', 'writeInt8', 'writeUInt8','writeUInt8Clamp', 'writeInt16LE', 'writeUInt16LE', 'writeUInt32LE', 'writeUInt32LE', 'writeFloatLE', 'writeDoubleLE', 'writeString', 'writteBuffer'],
writeFn: [
'',
'writeInt8',
'writeUInt8',
'writeUInt8Clamp',
'writeInt16LE',
'writeUInt16LE',
'writeUInt32LE',
'writeUInt32LE',
'writeFloatLE',
'writeDoubleLE',
'writeString',
'writteBuffer'
],
_initFunctions: function() {
var self = this;
this.writeFn.forEach(function(fn) {
if(self[fn] === undefined)
if(self[fn] === undefined) {
self[fn] = function(d) {
self.buffer[fn](d, self.offset);
self.offset += self.sizes[self.type];
}
};
}
});
},

View File

@ -30,7 +30,9 @@ function userByHostName(host) {
}
if (mat.length !== 2) {
console.error("ERROR: pattern '" + re_userFromHost + "' gave unexpected matches against '" + host + "': " + mat);
console.error(
"ERROR: pattern '" + re_userFromHost + "' gave unexpected matches against '" + host + "': " + mat
);
return;
}
return mat[1];

View File

@ -1,6 +1,7 @@
var pg = require('./pg'),
ArrayBufferSer = require("../bin_encoder"),
_ = require('underscore');
var _ = require('underscore');
var pg = require('./pg');
var ArrayBufferSer = require("../bin_encoder");
function BinaryFormat() {}
@ -9,13 +10,14 @@ BinaryFormat.prototype = new pg('arraybuffer');
BinaryFormat.prototype._contentType = "application/octet-stream";
BinaryFormat.prototype._extractTypeFromName = function(name) {
var g = name.match(new RegExp(/.*__(uintclamp|uint|int|float)(8|16|32)/i))
if(g && g.length == 3) {
var g = name.match(/.*__(uintclamp|uint|int|float)(8|16|32)/i);
if(g && g.length === 3) {
var typeName = g[1] + g[2];
return ArrayBufferSer.typeNames[typeName];
}
};
// jshint maxcomplexity:12
BinaryFormat.prototype.transform = function(result, options, callback) {
var total_rows = result.rowCount;
var rows = result.rows;
@ -35,27 +37,28 @@ BinaryFormat.prototype.transform = function(result, options, callback) {
}
try {
var i;
var t;
// get header types (and guess from name)
for(var i = 0; i < headersNames.length; ++i) {
var r = rows[0];
var n = headersNames[i];
if(typeof(r[n]) == 'string') {
for(i = 0; i < headersNames.length; ++i) {
r = rows[0];
n = headersNames[i];
if(typeof(r[n]) === 'string') {
headerTypes.push(ArrayBufferSer.STRING);
} else if(typeof(r[n]) == 'object') {
var t = this._extractTypeFromName(n);
t = t == undefined ? ArrayBufferSer.FLOAT32: t;
} else if(typeof(r[n]) === 'object') {
t = this._extractTypeFromName(n);
t = t || ArrayBufferSer.FLOAT32;
headerTypes.push(ArrayBufferSer.BUFFER + t);
} else {
var t = this._extractTypeFromName(n);
headerTypes.push(t == undefined ? ArrayBufferSer.FLOAT32: t);
t = this._extractTypeFromName(n);
headerTypes.push(t || ArrayBufferSer.FLOAT32);
}
}
// pack the data
var header = new ArrayBufferSer(ArrayBufferSer.STRING, headersNames);
var data = [header];
for(var i = 0; i < headersNames.length; ++i) {
for(i = 0; i < headersNames.length; ++i) {
var d = [];
var n = headersNames[i];
for(var r = 0; r < total_rows; ++r) {

View File

@ -1,5 +1,6 @@
var _ = require('underscore'),
pg = require('./pg');
var _ = require('underscore');
var pg = require('./pg');
function GeoJsonFormat() {
this.buffer = '';
@ -40,7 +41,7 @@ GeoJsonFormat.prototype.handleQueryRow = function(row) {
'"properties":'
];
delete row[this.opts.gn];
delete row['the_geom_webmercator'];
delete row.the_geom_webmercator;
geojson.push(JSON.stringify(row));
geojson.push('}');
@ -52,13 +53,15 @@ GeoJsonFormat.prototype.handleQueryRow = function(row) {
}
};
GeoJsonFormat.prototype.handleQueryEnd = function(result) {
GeoJsonFormat.prototype.handleQueryEnd = function(/*result*/) {
if (this.error) {
this.callback(this.error);
return;
}
if ( this.opts.profiler ) this.opts.profiler.done('gotRows');
if ( this.opts.profiler ) {
this.opts.profiler.done('gotRows');
}
if ( ! this._streamingStarted ) {
this.startStreaming();
@ -91,7 +94,7 @@ function _toGeoJSON(data, gn, callback){
};
_geojson.geometry = JSON.parse(ele[gn]);
delete ele[gn];
delete ele["the_geom_webmercator"]; // TODO: use skipfields
delete ele.the_geom_webmercator; // TODO: use skipfields
_geojson.properties = ele;
out.features.push(_geojson);
});

View File

@ -2,9 +2,9 @@
// load all the formats
//
var formats = {},
path = require('path'),
folder = __dirname + "/";
var formats = {};
var path = require('path');
var folder = __dirname + "/";
require("fs").readdirSync(folder).forEach(function(file) {
if (path.extname(file) === '.js' && file !== 'index.js' && file !== 'ogr.js' && file !== 'pg.js' ) {

View File

@ -1,7 +1,7 @@
var pg = require('./pg'),
util = require('util'),
PgErrorHandler = require(global.settings.app_root + '/app/postgresql/error_handler'),
_ = require('underscore');
var _ = require('underscore');
var pg = require('./pg');
var PgErrorHandler = require('../../postgresql/error_handler');
function JsonFormat() {
this.buffer = '';
@ -12,6 +12,7 @@ JsonFormat.prototype = new pg('json');
JsonFormat.prototype._contentType = "application/json; charset=utf-8";
// jshint maxcomplexity:9
JsonFormat.prototype.formatResultFields = function(flds) {
flds = flds || [];
var nfields = {};
@ -73,13 +74,16 @@ JsonFormat.prototype.handleQueryRow = function(row, result) {
}
};
// jshint maxcomplexity:13
JsonFormat.prototype.handleQueryEnd = function(result) {
if (this.error && !this._streamingStarted) {
this.callback(this.error);
return;
}
if ( this.opts.profiler ) this.opts.profiler.done('gotRows');
if ( this.opts.profiler ) {
this.opts.profiler.done('gotRows');
}
if ( ! this._streamingStarted ) {
this.startStreaming();
@ -95,7 +99,9 @@ JsonFormat.prototype.handleQueryEnd = function(result) {
var sf = this.opts.skipfields;
for (var i = 0; i < result.fields.length; i++) {
var f = result.fields[i];
if ( sf.indexOf(f.name) == -1 ) newfields.push(f);
if ( sf.indexOf(f.name) === -1 ) {
newfields.push(f);
}
}
result.fields = newfields;
}
@ -124,7 +130,7 @@ JsonFormat.prototype.handleQueryEnd = function(result) {
severities.push(severity);
notices[severity] = [];
}
notices[severity].push(notice.message)
notices[severity].push(notice.message);
});
_.each(severities, function(severity) {
out.push(',');

View File

@ -1,9 +1,10 @@
var crypto = require('crypto'),
Step = require('step'),
fs = require('fs'),
_ = require('underscore'),
PSQL = require('cartodb-psql'),
spawn = require('child_process').spawn;
var crypto = require('crypto');
var step = require('step');
var fs = require('fs');
var _ = require('underscore');
var PSQL = require('cartodb-psql');
var spawn = require('child_process').spawn;
var assert = require('assert');
// Keeps track of what's waiting baking for export
var bakingExports = {};
@ -18,12 +19,12 @@ OgrFormat.prototype = {
is_file: true,
getQuery: function(sql, options) {
getQuery: function(/*sql, options*/) {
return null; // dont execute the query
},
transform: function(result, options, callback) {
throw "should not be called for file formats"
transform: function(/*result, options, callback*/) {
throw "should not be called for file formats";
},
getContentType: function(){ return this._contentType; },
@ -50,7 +51,7 @@ OgrFormat.prototype = {
// Internal function usable by all OGR-driven outputs
OgrFormat.prototype.toOGR = function(options, out_format, out_filename, callback) {
var gcol = options.gn;
//var gcol = options.gn;
var sql = options.sql;
var skipfields = options.skipfields;
var out_layername = options.filename;
@ -73,15 +74,16 @@ OgrFormat.prototype.toOGR = function(options, out_format, out_filename, callback
// Drop ending semicolon (ogr doens't like it)
sql = sql.replace(/;\s*$/, '');
Step (
step (
function fetchColumns() {
var colsql = 'SELECT * FROM (' + sql + ') as _cartodbsqlapi LIMIT 0';
pg = new PSQL(dbopts);
pg.query(colsql, this);
},
// jshint maxcomplexity:9
function findSRS(err, result) {
if (err) throw err;
assert.ifError(err);
//if ( ! result.rows.length ) throw new Error("Query returns no rows");
@ -91,20 +93,29 @@ OgrFormat.prototype.toOGR = function(options, out_format, out_filename, callback
for (var i=0; i<result.fields.length; ++i) {
var field = result.fields[i];
var k = field.name;
if ( skipfields.indexOf(k) != -1 ) continue;
if ( out_format != 'CSV' && k == "the_geom_webmercator" ) continue; // TODO: drop ?
if ( out_format == 'CSV' ) columns.push(pg.quoteIdentifier(k)+'::text');
else columns.push(pg.quoteIdentifier(k));
if ( skipfields.indexOf(k) !== -1 ) {
continue;
}
if ( out_format !== 'CSV' && k === "the_geom_webmercator" ) {
continue;
} // TODO: drop ?
if ( out_format === 'CSV' ) {
columns.push(pg.quoteIdentifier(k)+'::text');
} else {
columns.push(pg.quoteIdentifier(k));
}
if ( needSRS ) {
if ( ! geocol && pg.typeName(field.dataTypeID) == 'geometry' ) {
geocol = k
if ( ! geocol && pg.typeName(field.dataTypeID) === 'geometry' ) {
geocol = k;
}
}
}
//console.log(columns.join(','));
if ( ! needSRS || ! geocol ) return null;
if ( ! needSRS || ! geocol ) {
return null;
}
var next = this;
@ -124,23 +135,18 @@ OgrFormat.prototype.toOGR = function(options, out_format, out_filename, callback
},
function spawnDumper(err, srid, type) {
if (err) throw err;
assert.ifError(err);
var next = this;
var ogrsql = 'SELECT ' + columns.join(',')
+ ' FROM (' + sql + ') as _cartodbsqlapi';
var ogrsql = 'SELECT ' + columns.join(',') + ' FROM (' + sql + ') as _cartodbsqlapi';
var ogrargs = [
'-f', out_format,
'-lco', 'ENCODING=UTF-8',
'-lco', 'LINEFORMAT=CRLF',
out_filename,
"PG:host=" + dbhost
+ " port=" + dbport
+ " user=" + dbuser
+ " dbname=" + dbname
+ " password=" + dbpass,
"PG:host=" + dbhost + " port=" + dbport + " user=" + dbuser + " dbname=" + dbname + " password=" + dbpass,
'-sql', ogrsql
];
@ -171,7 +177,9 @@ console.log('ogr2ogr ' + _.map(ogrargs, function(x) { return "'" + x + "'"; }).j
child.stderr.on('data', function(data) {
data = data.toString(); // know of a faster way ?
// Store only the first ERROR line
if ( ! stderr && data.match(logErrPat) ) stderr = data;
if ( ! stderr && data.match(logErrPat) ) {
stderr = data;
}
console.log('ogr2ogr stderr: ' + data);
});
@ -204,7 +212,14 @@ OgrFormat.prototype.toOGR_SingleFile = function(options, fmt, callback) {
var layername = options.filename;
var tmpdir = global.settings.tmpDir || '/tmp';
var reqKey = [ fmt, dbname, user_id, gcol, this.generateMD5(layername), this.generateMD5(sql) ].concat(skipfields).join(':');
var reqKey = [
fmt,
dbname,
user_id,
gcol,
this.generateMD5(layername),
this.generateMD5(sql)
].concat(skipfields).join(':');
var outdirpath = tmpdir + '/sqlapi-' + process.pid + '-' + reqKey;
var dumpfile = outdirpath + ':cartodb-query.' + ext;
@ -214,7 +229,7 @@ OgrFormat.prototype.toOGR_SingleFile = function(options, fmt, callback) {
};
OgrFormat.prototype.sendResponse = function(opts, callback) {
var next = callback;
//var next = callback;
var reqKey = this.getKey(opts);
var qElem = new ExportRequest(opts.sink, callback, opts.beforeSink);
var baking = bakingExports[reqKey];
@ -223,8 +238,10 @@ OgrFormat.prototype.sendResponse = function(opts, callback) {
} else {
baking = bakingExports[reqKey] = { req: [ qElem ] };
this.generate(opts, function(err, dumpfile) {
if ( opts.profiler ) opts.profiler.done('generate');
Step (
if ( opts.profiler ) {
opts.profiler.done('generate');
}
step (
function sendResults() {
var nextPipe = function(finish) {
var r = baking.req.shift();
@ -234,28 +251,29 @@ OgrFormat.prototype.sendResponse = function(opts, callback) {
});
};
if ( ! err ) nextPipe(this);
else {
if ( ! err ) {
nextPipe(this);
} else {
_.each(baking.req, function(r) {
r.cb(err);
});
return true;
}
},
function cleanup(err) {
function cleanup(/*err*/) {
delete bakingExports[reqKey];
// unlink dump file (sync to avoid race condition)
console.log("removing", dumpfile);
try { fs.unlinkSync(dumpfile); }
catch (e) {
if ( e.code != 'ENOENT' ) {
if ( e.code !== 'ENOENT' ) {
console.log("Could not unlink dumpfile " + dumpfile + ": " + e);
}
}
}
);
})
});
}
};
@ -284,8 +302,10 @@ ExportRequest.prototype.sendFile = function (err, filename, callback) {
if ( ! this.canceled ) {
//console.log("Creating readable stream out of dumpfile");
this.istream = fs.createReadStream(filename)
.on('open', function(fd) {
if ( that.beforeSink ) that.beforeSink();
.on('open', function(/*fd*/) {
if ( that.beforeSink ) {
that.beforeSink();
}
that.istream.pipe(that.ostream);
callback();
})

View File

@ -1,5 +1,6 @@
var Step = require('step'),
PSQL = require('cartodb-psql');
var step = require('step');
var PSQL = require('cartodb-psql');
var assert = require('assert');
function PostgresFormat(id) {
this.id = id;
@ -7,7 +8,7 @@ function PostgresFormat(id) {
PostgresFormat.prototype = {
getQuery: function(sql, options) {
getQuery: function(sql/*, options*/) {
return sql;
},
@ -34,7 +35,9 @@ PostgresFormat.prototype.handleQueryRowWithSkipFields = function(row, result) {
};
PostgresFormat.prototype.handleNotice = function(msg, result) {
if ( ! result.notices ) result.notices = [];
if ( ! result.notices ) {
result.notices = [];
}
for (var i=0; i<msg.length; i++) {
result.notices.push(msg[i]);
}
@ -48,7 +51,9 @@ PostgresFormat.prototype.handleQueryEnd = function(result) {
return;
}
if ( this.opts.profiler ) this.opts.profiler.done('gotRows');
if ( this.opts.profiler ) {
this.opts.profiler.done('gotRows');
}
this.opts.total_time = (Date.now() - this.start_time)/1000;
@ -58,14 +63,16 @@ PostgresFormat.prototype.handleQueryEnd = function(result) {
var newfields = [];
for ( var j=0; j<result.fields.length; ++j ) {
var f = result.fields[j];
if ( sf.indexOf(f.name) == -1 ) newfields.push(f);
if ( sf.indexOf(f.name) === -1 ) {
newfields.push(f);
}
}
result.fields = newfields;
}
var that = this;
Step (
step (
function packageResult() {
if ( that.opts.abortChecker ) {
that.opts.abortChecker('packageResult');
@ -74,11 +81,13 @@ PostgresFormat.prototype.handleQueryEnd = function(result) {
},
function sendResults(err, out){
if (err) throw err;
assert.ifError(err);
// return to browser
if ( out ) {
if ( that.opts.beforeSink ) that.opts.beforeSink();
if ( that.opts.beforeSink ) {
that.opts.beforeSink();
}
that.opts.sink.send(out);
} else {
console.error("No output from transform, doing nothing ?!");
@ -117,7 +126,9 @@ PostgresFormat.prototype.sendResponse = function(opts, callback) {
callback(err);
return;
}
if ( that.opts.profiler ) that.opts.profiler.done('eventedQuery');
if ( that.opts.profiler ) {
that.opts.profiler.done('eventedQuery');
}
if (that.hasSkipFields) {
query.on('row', that.handleQueryRowWithSkipFields.bind(that));

View File

@ -1,8 +1,9 @@
var crypto = require('crypto'),
Step = require('step'),
fs = require('fs'),
spawn = require('child_process').spawn,
ogr = require('./ogr');
var step = require('step');
var fs = require('fs');
var spawn = require('child_process').spawn;
var assert = require('assert');
var ogr = require('./ogr');
function ShpFormat() {
}
@ -42,16 +43,17 @@ ShpFormat.prototype.toSHP = function (options, callback) {
// TODO: following tests:
// - fetch query with no "the_geom" column
Step (
step (
function createOutDir() {
fs.mkdir(outdirpath, 0777, this);
},
function spawnDumper(err) {
if ( err ) throw err;
assert.ifError(err);
fmtObj.toOGR(options, 'ESRI Shapefile', shapefile, this);
},
function doZip(err) {
if ( err ) throw err;
assert.ifError(err);
var next = this;
@ -82,21 +84,24 @@ ShpFormat.prototype.toSHP = function (options, callback) {
if ( err ) {
console.log("Unlinking " + fn + ": " + err);
finish(err);
} else {
unlinkall(dir, files, finish);
}
else unlinkall(dir, files, finish)
});
};
fs.readdir(outdirpath, function(err, files) {
if ( err ) {
if ( err.code != 'ENOENT' ) {
if ( err.code !== 'ENOENT' ) {
next(new Error([topError, err].join('\n')));
} else {
next(topError);
}
} else {
unlinkall(outdirpath, files, function(err) {
unlinkall(outdirpath, files, function(/*err*/) {
fs.rmdir(outdirpath, function(err) {
if ( err ) console.log("Removing dir " + path + ": " + err);
if ( err ) {
console.log("Removing dir " + outdirpath + ": " + err);
}
next(topError, zipfile);
});
});

View File

@ -1,5 +1,4 @@
var pg = require('./pg'),
_ = require('underscore');
var pg = require('./pg');
var svg_width = 1024.0;
var svg_height = 768.0;
@ -29,26 +28,26 @@ SvgFormat.prototype._contentType = "image/svg+xml; charset=utf-8";
SvgFormat.prototype.getQuery = function(sql, options) {
var gn = options.gn;
var dp = options.dp;
return 'WITH source AS ( ' + sql + '), extent AS ( '
+ ' SELECT ST_Extent(' + gn + ') AS e FROM source '
+ '), extent_info AS ( SELECT e, '
+ 'st_xmin(e) as ex0, st_ymax(e) as ey0, '
+ 'st_xmax(e)-st_xmin(e) as ew, '
+ 'st_ymax(e)-st_ymin(e) as eh FROM extent )'
+ ', trans AS ( SELECT CASE WHEN '
+ 'eh = 0 THEN ' + svg_width
+ '/ COALESCE(NULLIF(ew,0),' + svg_width +') WHEN '
+ svg_ratio + ' <= (ew / eh) THEN ('
+ svg_width + '/ew ) ELSE ('
+ svg_height + '/eh ) END as s '
+ ', ex0 as x0, ey0 as y0 FROM extent_info ) '
+ 'SELECT st_TransScale(e, -x0, -y0, s, s)::box2d as '
+ gn + '_box, ST_Dimension(' + gn + ') as ' + gn
+ '_dimension, ST_AsSVG(ST_TransScale(' + gn + ', '
+ '-x0, -y0, s, s), 0, ' + dp + ') as ' + gn
//+ ', ex0, ey0, ew, eh, s ' // DEBUG ONLY
+ ' FROM trans, extent_info, source'
+ ' ORDER BY the_geom_dimension ASC';
return 'WITH source AS ( ' + sql + '), extent AS ( ' +
' SELECT ST_Extent(' + gn + ') AS e FROM source ' +
'), extent_info AS ( SELECT e, ' +
'st_xmin(e) as ex0, st_ymax(e) as ey0, ' +
'st_xmax(e)-st_xmin(e) as ew, ' +
'st_ymax(e)-st_ymin(e) as eh FROM extent )' +
', trans AS ( SELECT CASE WHEN ' +
'eh = 0 THEN ' + svg_width +
'/ COALESCE(NULLIF(ew,0),' + svg_width +') WHEN ' +
svg_ratio + ' <= (ew / eh) THEN (' +
svg_width + '/ew ) ELSE (' +
svg_height + '/eh ) END as s ' +
', ex0 as x0, ey0 as y0 FROM extent_info ) ' +
'SELECT st_TransScale(e, -x0, -y0, s, s)::box2d as ' +
gn + '_box, ST_Dimension(' + gn + ') as ' + gn +
'_dimension, ST_AsSVG(ST_TransScale(' + gn + ', ' +
'-x0, -y0, s, s), 0, ' + dp + ') as ' + gn +
//+ ', ex0, ey0, ew, eh, s ' // DEBUG ONLY +
' FROM trans, extent_info, source' +
' ORDER BY the_geom_dimension ASC';
};
SvgFormat.prototype.startStreaming = function() {
@ -72,14 +71,11 @@ SvgFormat.prototype.startStreaming = function() {
this.bbox.ymax += growby;
this.bbox.width = this.bbox.xmax - this.bbox.xmin;
this.bbox.height = this.bbox.ymax - this.bbox.ymin;
rootTag += 'viewBox="' + this.bbox.xmin + ' ' + (-this.bbox.ymax) + ' '
+ this.bbox.width + ' ' + this.bbox.height + '" ';
rootTag += 'viewBox="' + this.bbox.xmin + ' ' + (-this.bbox.ymax) + ' ' +
this.bbox.width + ' ' + this.bbox.height + '" ';
}
rootTag += 'style="fill-opacity:' + fill_opacity
+ '; stroke:' + stroke_color
+ '; stroke-width:' + stroke_width
+ '; fill:' + fill_color
+ '" ';
rootTag += 'style="fill-opacity:' + fill_opacity + '; stroke:' + stroke_color + '; ' +
'stroke-width:' + stroke_width + '; fill:' + fill_color + '" ';
rootTag += 'xmlns="http://www.w3.org/2000/svg" version="1.1">\n';
header.push(rootTag);
@ -89,6 +85,7 @@ SvgFormat.prototype.startStreaming = function() {
this._streamingStarted = true;
};
// jshint maxcomplexity:11
SvgFormat.prototype.handleQueryRow = function(row) {
this.totalRows++;
@ -97,8 +94,11 @@ SvgFormat.prototype.handleQueryRow = function(row) {
}
var g = row[this.opts.gn];
if ( ! g ) return; // null or empty
if ( ! g ) {
return;
} // null or empty
// jshint ignore:start
var gdims = row[this.opts.gn + '_dimension'];
// TODO: add an identifier, if any of "cartodb_id", "oid", "id", "gid" are found
// TODO: add "class" attribute to help with styling ?
@ -106,10 +106,11 @@ SvgFormat.prototype.handleQueryRow = function(row) {
this.buffer += '<circle r="' + radius + '" ' + g + ' />\n';
} else if ( gdims == '1' ) {
// Avoid filling closed linestrings
this.buffer += '<path ' + ( fill_color != 'none' ? 'fill="none" ' : '' ) + 'd="' + g + '" />\n';
this.buffer += '<path ' + ( fill_color !== 'none' ? 'fill="none" ' : '' ) + 'd="' + g + '" />\n';
} else if ( gdims == '2' ) {
this.buffer += '<path d="' + g + '" />\n';
}
// jshint ignore:end
if ( ! this.bbox ) {
// Parse layer extent: "BOX(x y, X Y)"

View File

@ -1,7 +1,7 @@
var pg = require('./pg'),
_ = require('underscore'),
geojson = require('./geojson'),
TopoJSON = require('topojson');
var pg = require('./pg');
var _ = require('underscore');
var geojson = require('./geojson');
var TopoJSON = require('topojson');
function TopoJsonFormat() {
this.features = [];
@ -19,7 +19,7 @@ TopoJsonFormat.prototype.handleQueryRow = function(row) {
};
_geojson.geometry = JSON.parse(row[this.opts.gn]);
delete row[this.opts.gn];
delete row["the_geom_webmercator"];
delete row.the_geom_webmercator;
_geojson.properties = row;
this.features.push(_geojson);
};
@ -30,7 +30,9 @@ TopoJsonFormat.prototype.handleQueryEnd = function() {
return;
}
if ( this.opts.profiler ) this.opts.profiler.done('gotRows');
if ( this.opts.profiler ) {
this.opts.profiler.done('gotRows');
}
var topology = TopoJSON.topology(this.features, {
"quantization": 1e4,

View File

@ -34,7 +34,9 @@ For logger.trace('one','two','three'):
//
// We only log error and higher errors
//
if ( loggingEvent.level.level < 40000 ) return;
if ( loggingEvent.level.level < 40000 ) {
return;
}
rollbar.reportMessage(loggingEvent.data);
};

View File

@ -1,23 +1,19 @@
var Step = require('step'),
_ = require('underscore'),
var step = require('step'),
fs = require('fs');
function HealthCheck(metadataBackend, psqlClass) {
function HealthCheck(metadataBackend) {
this.metadataBackend = metadataBackend;
this.psqlClass = psqlClass;
}
module.exports = HealthCheck;
HealthCheck.prototype.check = function(username, query, callback) {
var self = this,
startTime,
result = {
var result = {
redis: {},
postgresql: {}
};
Step(
step(
function getManualDisable() {
fs.readFile(global.settings.disabled_file, this);
},

11
npm-shrinkwrap.json generated
View File

@ -143,8 +143,7 @@
},
"inherits": {
"version": "2.0.1",
"from": "inherits@~2.0.1",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
"from": "inherits@~2.0.1"
}
}
},
@ -156,9 +155,9 @@
}
},
"lru-cache": {
"version": "2.5.0",
"version": "2.5.2",
"from": "lru-cache@~2.5.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.5.0.tgz"
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.5.2.tgz"
},
"node-statsd": {
"version": "0.0.7",
@ -220,9 +219,9 @@
"resolved": "https://registry.npmjs.org/optimist/-/optimist-0.3.5.tgz",
"dependencies": {
"wordwrap": {
"version": "0.0.2",
"version": "0.0.3",
"from": "wordwrap@~0.0.2",
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz"
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz"
}
}
}

View File

@ -32,11 +32,12 @@
"devDependencies": {
"redis": "0.7.1",
"mocha": "~1.21.4",
"jshint": "~2.6.0",
"zipfile": "~0.5.0",
"libxmljs": "~0.8.1"
},
"scripts": {
"test": "test/run_tests.sh ${RUNTESTFLAGS} test/unit/*.js test/unit/model/*.js test/acceptance/*.js test/acceptance/export/*.js"
"test": "make test-all"
},
"engines": {
"node": ">=0.8 <0.11",

View File

@ -1,40 +1,37 @@
require('../helper');
require('../support/assert');
var app = require(global.settings.app_root + '/app/controllers/app')()
, assert = require('assert')
, tests = module.exports = {}
, querystring = require('querystring');
var app = require(global.settings.app_root + '/app/controllers/app')();
var assert = require('../support/assert');
suite('app.auth', function() {
describe('app.auth', function() {
var scenarios = [
{
desc: 'valid api key should allow insert in protected tables',
url: "/api/v1/sql?api_key=1234&q=INSERT%20INTO%20private_table%20(name)%20VALUES%20('app_auth_test1')",
statusCode: 200
}
,{
},
{
desc: 'valid api key should allow delete in protected tables',
url: "/api/v1/sql?api_key=1234&q=DELETE%20FROM%20private_table%20WHERE%20name%3d'app_auth_test1'",
statusCode: 200
}
,{
},
{
desc: 'invalid api key should NOT allow insert in protected tables',
url: "/api/v1/sql?api_key=RAMBO&q=INSERT%20INTO%20private_table%20(name)%20VALUES%20('RAMBO')",
statusCode: 401
}
,{
},
{
desc: 'invalid api key (old redis location) should NOT allow insert in protected tables',
url: "/api/v1/sql?api_key=1235&q=INSERT%20INTO%20private_table%20(name)%20VALUES%20('RAMBO')",
statusCode: 401
}
,{
},
{
desc: 'no api key should NOT allow insert in protected tables',
url: "/api/v1/sql?q=INSERT%20INTO%20private_table%20(name)%20VALUES%20('RAMBO')",
statusCode: 401
}
,{
},
{
desc: 'no api key should NOT allow insert in public tables',
url: "/api/v1/sql?q=INSERT%20INTO%20untitle_table_4%20(name)%20VALUES%20('RAMBO')",
statusCode: 401
@ -42,7 +39,7 @@ suite('app.auth', function() {
];
scenarios.forEach(function(scenario) {
test(scenario.desc, function(done) {
it(scenario.desc, function(done) {
assert.response(app, {
// view prepare_db.sh to find public table name and structure
url: scenario.url,

View File

@ -13,27 +13,21 @@
*
*/
require('../helper');
require('../support/assert');
var app = require(global.settings.app_root + '/app/controllers/app')();
var assert = require('../support/assert');
var querystring = require('querystring');
var _ = require('underscore');
var step = require('step');
var app = require(global.settings.app_root + '/app/controllers/app')()
, assert = require('assert')
, querystring = require('querystring')
, _ = require('underscore')
, zipfile = require('zipfile')
, fs = require('fs')
, libxmljs = require('libxmljs')
, Step = require('step')
;
suite('app.test', function() {
describe('app.test', function() {
var expected_cache_control = 'no-cache,max-age=31536000,must-revalidate,public';
var expected_rw_cache_control = 'no-cache,max-age=0,must-revalidate,public';
var expected_cache_control_persist = 'public,max-age=31536000';
test('GET /api/v1/version', function(done){
it('GET /api/v1/version', function(done){
assert.response(app, {
url: '/api/v1/version',
method: 'GET'
@ -47,7 +41,7 @@ test('GET /api/v1/version', function(done){
});
});
test('GET /api/v1/sql', function(done){
it('GET /api/v1/sql', function(done){
assert.response(app, {
url: '/api/v1/sql',
method: 'GET'
@ -62,7 +56,7 @@ test('GET /api/v1/sql', function(done){
});
// Test base_url setting
test('GET /api/whatever/sql', function(done){
it('GET /api/whatever/sql', function(done){
assert.response(app, {
url: '/api/whatever/sql?q=SELECT%201',
headers: {host: 'vizzuality.cartodb.com'},
@ -75,7 +69,7 @@ test('GET /api/whatever/sql', function(done){
});
// Test CORS headers with GET
test('GET /api/whatever/sql', function(done){
it('GET /api/whatever/sql', function(done){
assert.response(app, {
url: '/api/whatever/sql?q=SELECT%201',
headers: {host: 'vizzuality.cartodb.com'},
@ -83,14 +77,16 @@ test('GET /api/whatever/sql', function(done){
},{
}, function(res) {
assert.equal(res.statusCode, 200, res.body);
assert.equal(res.headers['access-control-allow-headers'], 'X-Requested-With, X-Prototype-Version, X-CSRF-Token');
assert.equal(
res.headers['access-control-allow-headers'], 'X-Requested-With, X-Prototype-Version, X-CSRF-Token'
);
assert.equal(res.headers['access-control-allow-origin'], '*');
done();
});
});
// Test that OPTIONS does not run queries
test('OPTIONS /api/x/sql', function(done){
it('OPTIONS /api/x/sql', function(done){
assert.response(app, {
url: '/api/x/sql?q=syntax%20error',
headers: {host: 'vizzuality.cartodb.com'},
@ -98,7 +94,9 @@ test('OPTIONS /api/x/sql', function(done){
},{}, function(res) {
assert.equal(res.statusCode, 200, res.body);
assert.equal(res.body, '');
assert.equal(res.headers['access-control-allow-headers'], 'X-Requested-With, X-Prototype-Version, X-CSRF-Token');
assert.equal(
res.headers['access-control-allow-headers'], 'X-Requested-With, X-Prototype-Version, X-CSRF-Token'
);
assert.equal(res.headers['access-control-allow-origin'], '*');
done();
});
@ -106,7 +104,7 @@ test('OPTIONS /api/x/sql', function(done){
test('GET /api/v1/sql with SQL parameter on SELECT only. No oAuth included ', function(done){
it('GET /api/v1/sql with SQL parameter on SELECT only. No oAuth included ', function(done){
assert.response(app, {
url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4&database=cartodb_test_user_1_db',
headers: {host: 'vizzuality.cartodb.com'},
@ -120,7 +118,7 @@ test('GET /api/v1/sql with SQL parameter on SELECT only. No oAuth included ', fu
});
});
test('cache_policy=persist', function(done){
it('cache_policy=persist', function(done){
assert.response(app, {
url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4&database=cartodb_test_user_1_db&cache_policy=persist',
headers: {host: 'vizzuality.cartodb.com'},
@ -136,7 +134,7 @@ test('cache_policy=persist', function(done){
});
});
test('GET /api/v1/sql with SQL parameter on SELECT only. no database param, just id using headers', function(done){
it('GET /api/v1/sql with SQL parameter on SELECT only. no database param, just id using headers', function(done){
assert.response(app, {
url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4',
headers: {host: 'vizzuality.cartodb.com'},
@ -147,7 +145,7 @@ test('GET /api/v1/sql with SQL parameter on SELECT only. no database param, just
});
});
test('GET /user/vizzuality/api/v1/sql with SQL parameter on SELECT only', function(done){
it('GET /user/vizzuality/api/v1/sql with SQL parameter on SELECT only', function(done){
assert.response(app, {
url: '/user/vizzuality/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4',
method: 'GET'
@ -159,7 +157,7 @@ test('GET /user/vizzuality/api/v1/sql with SQL parameter on SELECT only', functi
// See https://github.com/CartoDB/CartoDB-SQL-API/issues/121
test('SELECT from user-specific database', function(done){
it('SELECT from user-specific database', function(done){
var backupDBHost = global.settings.db_host;
global.settings.db_host = '6.6.6.6';
assert.response(app, {
@ -182,7 +180,7 @@ test('SELECT from user-specific database', function(done){
});
// See https://github.com/CartoDB/CartoDB-SQL-API/issues/120
test('SELECT with user-specific password', function(done){
it('SELECT with user-specific password', function(done){
var backupDBUserPass = global.settings.db_user_pass;
global.settings.db_user_pass = '<%= user_password %>';
assert.response(app, {
@ -204,7 +202,7 @@ test('SELECT with user-specific password', function(done){
});
});
test('GET /api/v1/sql with SQL parameter on SELECT only. no database param, just id using headers. Authenticated.',
it('GET /api/v1/sql with SQL parameter on SELECT only. no database param, just id using headers. Authenticated.',
function(done){
assert.response(app, {
url: '/api/v1/sql?q=SELECT%20cartodb_id*2%20FROM%20untitle_table_4&api_key=1234',
@ -220,7 +218,7 @@ function(done){
});
// Test for https://github.com/Vizzuality/CartoDB-SQL-API/issues/85
test("paging doesn't break x-cache-channel",
it("paging doesn't break x-cache-channel",
function(done){
assert.response(app, {
url: '/api/v1/sql?' + querystring.stringify({
@ -242,13 +240,14 @@ function(done){
});
// Test page and rows_per_page params
test("paging", function(done){
it("paging", function(done){
var sql = 'SELECT * FROM (VALUES(1),(2),(3),(4),(5),(6),(7),(8),(9)) t(v)';
var pr = [ [2,3], [0,4] ]; // page and rows
var methods = [ 'GET', 'POST' ];
var authorized = 0;
var testing = 0;
var method = 0;
// jshint maxcomplexity:7
var testNext = function() {
if ( testing >= pr.length ) {
if ( method+1 >= methods.length ) {
@ -266,9 +265,8 @@ test("paging", function(done){
}
}
var prcur = pr[testing++];
console.log("Test " + testing + "/" + pr.length
+ " method " + methods[method] + " "
+ ( authorized ? "authenticated" : "" ) );
console.log("Test " + testing + "/" + pr.length + " method " + methods[method] + " " +
( authorized ? "authenticated" : "" ) );
var page = prcur[0];
var nrows = prcur[1];
var data_obj = {
@ -276,13 +274,15 @@ test("paging", function(done){
rows_per_page: nrows,
page: page
};
if ( authorized ) data_obj['api_key'] = '1234';
if ( authorized ) {
data_obj.api_key = '1234';
}
var data = querystring.stringify(data_obj);
var req = {
url: '/api/v1/sql',
headers: {host: 'vizzuality.cartodb.com'}
};
if ( methods[method] == 'GET' ) {
if ( methods[method] === 'GET' ) {
req.method = 'GET';
req.url += '?' + data;
} else {
@ -306,7 +306,7 @@ test("paging", function(done){
});
// Test paging with WITH queries
test("paging starting with comment", function(done){
it("paging starting with comment", function(done){
var sql = "-- this is a comment\n" +
"SELECT * FROM (VALUES(1),(2),(3),(4),(5),(6),(7),(8),(9)) t(v)";
var nrows = 3;
@ -332,7 +332,7 @@ test("paging starting with comment", function(done){
});
});
test('POST /api/v1/sql with SQL parameter on SELECT only. no database param, just id using headers', function(done){
it('POST /api/v1/sql with SQL parameter on SELECT only. no database param, just id using headers', function(done){
assert.response(app, {
url: '/api/v1/sql',
data: querystring.stringify({q: "SELECT * FROM untitle_table_4"}),
@ -344,9 +344,10 @@ test('POST /api/v1/sql with SQL parameter on SELECT only. no database param, jus
});
});
test('GET /api/v1/sql with INSERT. oAuth not used, so public user - should fail', function(done){
it('GET /api/v1/sql with INSERT. oAuth not used, so public user - should fail', function(done){
assert.response(app, {
url: "/api/v1/sql?q=INSERT%20INTO%20untitle_table_4%20(cartodb_id)%20VALUES%20(1e4)&database=cartodb_test_user_1_db",
url: "/api/v1/sql?q=INSERT%20INTO%20untitle_table_4%20(cartodb_id)%20VALUES%20(1e4)" +
"&database=cartodb_test_user_1_db",
headers: {host: 'vizzuality.cartodb.com'},
method: 'GET'
},{
@ -361,7 +362,7 @@ test('GET /api/v1/sql with INSERT. oAuth not used, so public user - should fail'
});
});
test('GET /api/v1/sql with DROP TABLE. oAuth not used, so public user - should fail', function(done){
it('GET /api/v1/sql with DROP TABLE. oAuth not used, so public user - should fail', function(done){
assert.response(app, {
url: "/api/v1/sql?q=DROP%20TABLE%20untitle_table_4&database=cartodb_test_user_1_db",
headers: {host: 'vizzuality.cartodb.com'},
@ -378,7 +379,7 @@ test('GET /api/v1/sql with DROP TABLE. oAuth not used, so public user - should f
});
});
test('GET /api/v1/sql with INSERT. header based db - should fail', function(){
it('GET /api/v1/sql with INSERT. header based db - should fail', function(){
assert.response(app, {
url: "/api/v1/sql?q=INSERT%20INTO%20untitle_table_4%20(id)%20VALUES%20(1)",
headers: {host: 'vizzuality.cartodb.com'},
@ -391,11 +392,10 @@ test('GET /api/v1/sql with INSERT. header based db - should fail', function(){
// Check results from INSERT
//
// See https://github.com/Vizzuality/CartoDB-SQL-API/issues/13
test('INSERT returns affected rows', function(done){
it('INSERT returns affected rows', function(done){
assert.response(app, {
// view prepare_db.sh to see where to set api_key
url: "/api/v1/sql?api_key=1234&"
+ querystring.stringify({q:
url: "/api/v1/sql?api_key=1234&" + querystring.stringify({q:
"INSERT INTO private_table(name) VALUES('noret1') UNION VALUES('noret2')"
}),
headers: {host: 'vizzuality.localhost.lan:8080' },
@ -417,11 +417,10 @@ test('INSERT returns affected rows', function(done){
// Check results from UPDATE
//
// See https://github.com/Vizzuality/CartoDB-SQL-API/issues/13
test('UPDATE returns affected rows', function(done){
it('UPDATE returns affected rows', function(done){
assert.response(app, {
// view prepare_db.sh to see where to set api_key
url: "/api/v1/sql?api_key=1234&"
+ querystring.stringify({q:
url: "/api/v1/sql?api_key=1234&" + querystring.stringify({q:
"UPDATE private_table SET name = upper(name) WHERE name in ('noret1', 'noret2')"
}),
headers: {host: 'vizzuality.localhost.lan:8080' },
@ -443,11 +442,10 @@ test('UPDATE returns affected rows', function(done){
// Check results from DELETE
//
// See https://github.com/Vizzuality/CartoDB-SQL-API/issues/13
test('DELETE returns affected rows', function(done){
it('DELETE returns affected rows', function(done){
assert.response(app, {
// view prepare_db.sh to see where to set api_key
url: "/api/v1/sql?api_key=1234&"
+ querystring.stringify({q:
url: "/api/v1/sql?api_key=1234&" + querystring.stringify({q:
"DELETE FROM private_table WHERE name in ('NORET1', 'NORET2')"
}),
headers: {host: 'vizzuality.localhost.lan:8080' },
@ -469,11 +467,10 @@ test('DELETE returns affected rows', function(done){
// Check results from INSERT .. RETURNING
//
// See https://github.com/Vizzuality/CartoDB-SQL-API/issues/50
test('INSERT with RETURNING returns all results', function(done){
it('INSERT with RETURNING returns all results', function(done){
assert.response(app, {
// view prepare_db.sh to see where to set api_key
url: "/api/v1/sql?api_key=1234&"
+ querystring.stringify({q:
url: "/api/v1/sql?api_key=1234&" + querystring.stringify({q:
"INSERT INTO private_table(name) VALUES('test') RETURNING upper(name), reverse(name)"
}),
headers: {host: 'vizzuality.localhost.lan:8080' },
@ -494,11 +491,10 @@ test('INSERT with RETURNING returns all results', function(done){
// Check results from UPDATE .. RETURNING
//
// See https://github.com/Vizzuality/CartoDB-SQL-API/issues/50
test('UPDATE with RETURNING returns all results', function(done){
it('UPDATE with RETURNING returns all results', function(done){
assert.response(app, {
// view prepare_db.sh to see where to set api_key
url: "/api/v1/sql?api_key=1234&"
+ querystring.stringify({q:
url: "/api/v1/sql?api_key=1234&" + querystring.stringify({q:
"UPDATE private_table SET name = 'tost' WHERE name = 'test' RETURNING upper(name), reverse(name)"
}),
headers: {host: 'vizzuality.localhost.lan:8080' },
@ -519,11 +515,10 @@ test('UPDATE with RETURNING returns all results', function(done){
// Check results from DELETE .. RETURNING
//
// See https://github.com/Vizzuality/CartoDB-SQL-API/issues/50
test('DELETE with RETURNING returns all results', function(done){
it('DELETE with RETURNING returns all results', function(done){
assert.response(app, {
// view prepare_db.sh to see where to set api_key
url: "/api/v1/sql?api_key=1234&"
+ querystring.stringify({q:
url: "/api/v1/sql?api_key=1234&" + querystring.stringify({q:
"DELETE FROM private_table WHERE name = 'tost' RETURNING name"
}),
headers: {host: 'vizzuality.localhost.lan:8080' },
@ -540,7 +535,7 @@ test('DELETE with RETURNING returns all results', function(done){
});
});
test('GET /api/v1/sql with SQL parameter on DROP TABLE. should fail', function(done){
it('GET /api/v1/sql with SQL parameter on DROP TABLE. should fail', function(done){
assert.response(app, {
url: "/api/v1/sql?q=DROP%20TABLE%20untitle_table_4",
headers: {host: 'vizzuality.cartodb.com'},
@ -559,11 +554,10 @@ test('GET /api/v1/sql with SQL parameter on DROP TABLE. should fail', function(d
// Check X-Cache-Channel when querying "updated_at" fields
//
// See https://github.com/Vizzuality/CartoDB-SQL-API/issues/99
test('Field name is not confused with UPDATE operation', function(done){
it('Field name is not confused with UPDATE operation', function(done){
assert.response(app, {
// view prepare_db.sh to see where to set api_key
url: "/api/v1/sql?api_key=1234&"
+ querystring.stringify({q:
url: "/api/v1/sql?api_key=1234&" + querystring.stringify({q:
"SELECT min(updated_at) FROM private_table"
}),
headers: {host: 'vizzuality.localhost.lan:8080' },
@ -575,7 +569,7 @@ test('Field name is not confused with UPDATE operation', function(done){
});
});
test('CREATE TABLE with GET and auth', function(done){
it('CREATE TABLE with GET and auth', function(done){
assert.response(app, {
url: "/api/v1/sql?" + querystring.stringify({
q: 'CREATE TABLE test_table(a int)',
@ -594,9 +588,9 @@ test('CREATE TABLE with GET and auth', function(done){
});
// See http://github.com/CartoDB/CartoDB-SQL-API/issues/127
test('SELECT INTO with paging ', function(done){
it('SELECT INTO with paging ', function(done){
var esc_tabname = 'test ""select into""'; // escaped ident
Step(
step(
function select_into() {
var next = this;
assert.response(app, {
@ -610,7 +604,7 @@ test('SELECT INTO with paging ', function(done){
},{}, function(res) { next(null, res); });
},
function check_res_test_fake_into_1(err, res) {
if ( err ) throw err;
assert.ifError(err);
assert.equal(res.statusCode, 200, res.statusCode + ': ' + res.body);
var next = this;
assert.response(app, {
@ -624,7 +618,7 @@ test('SELECT INTO with paging ', function(done){
},{}, function(res) { next(null, res); });
},
function check_res_drop_table(err, res) {
if ( err ) throw err;
assert.ifError(err);
assert.equal(res.statusCode, 200, res.statusCode + ': ' + res.body);
var out = JSON.parse(res.body);
assert.equal(out.total_rows, 1); // windowing works
@ -639,19 +633,19 @@ test('SELECT INTO with paging ', function(done){
},{}, function(res) { next(null, res); });
},
function check_drop(err, res) {
if ( err ) throw err;
assert.ifError(err);
assert.equal(res.statusCode, 200, res.statusCode + ': ' + res.body);
return null;
},
function finish(err) {
done(err)
done(err);
}
);
});
// Test effects of COPY
// See https://github.com/Vizzuality/cartodb-management/issues/1502
test('COPY TABLE with GET and auth', function(done){
it('COPY TABLE with GET and auth', function(done){
assert.response(app, {
url: "/api/v1/sql?" + querystring.stringify({
q: 'COPY test_table FROM stdin;',
@ -669,7 +663,7 @@ test('COPY TABLE with GET and auth', function(done){
});
});
test('COPY TABLE with GET and auth', function(done){
it('COPY TABLE with GET and auth', function(done){
assert.response(app, {
url: "/api/v1/sql?" + querystring.stringify({
q: "COPY test_table to '/tmp/x';",
@ -690,7 +684,7 @@ test('COPY TABLE with GET and auth', function(done){
});
});
test('ALTER TABLE with GET and auth', function(done){
it('ALTER TABLE with GET and auth', function(done){
assert.response(app, {
url: "/api/v1/sql?" + querystring.stringify({
q: 'ALTER TABLE test_table ADD b int',
@ -708,10 +702,11 @@ test('ALTER TABLE with GET and auth', function(done){
});
});
test('multistatement insert, alter, select, begin, commit', function(done){
it('multistatement insert, alter, select, begin, commit', function(done){
assert.response(app, {
url: "/api/v1/sql?" + querystring.stringify({
q: 'BEGIN; DELETE FROM test_table; COMMIT; BEGIN; INSERT INTO test_table(b) values (5); COMMIT; ALTER TABLE test_table ALTER b TYPE float USING b::float/2; SELECT b FROM test_table;',
q: 'BEGIN; DELETE FROM test_table; COMMIT; BEGIN; INSERT INTO test_table(b) values (5); COMMIT; ' +
'ALTER TABLE test_table ALTER b TYPE float USING b::float/2; SELECT b FROM test_table;',
api_key: 1234
}),
headers: {host: 'vizzuality.cartodb.com'},
@ -725,7 +720,7 @@ test('multistatement insert, alter, select, begin, commit', function(done){
});
});
test('TRUNCATE TABLE with GET and auth', function(done){
it('TRUNCATE TABLE with GET and auth', function(done){
assert.response(app, {
url: "/api/v1/sql?" + querystring.stringify({
q: 'TRUNCATE TABLE test_table',
@ -752,13 +747,13 @@ test('TRUNCATE TABLE with GET and auth', function(done){
assert.equal(res.headers['cache-control'], expected_cache_control);
var pbody = JSON.parse(res.body);
assert.equal(pbody.total_rows, 1);
assert.equal(pbody.rows[0]['count'], 0);
assert.equal(pbody.rows[0].count, 0);
done();
});
});
});
test('REINDEX TABLE with GET and auth', function(done){
it('REINDEX TABLE with GET and auth', function(done){
assert.response(app, {
url: "/api/v1/sql?" + querystring.stringify({
q: ' ReINdEX TABLE test_table',
@ -776,7 +771,7 @@ test('REINDEX TABLE with GET and auth', function(done){
});
});
test('DROP TABLE with GET and auth', function(done){
it('DROP TABLE with GET and auth', function(done){
assert.response(app, {
url: "/api/v1/sql?" + querystring.stringify({
q: 'DROP TABLE test_table',
@ -812,7 +807,7 @@ test('CREATE FUNCTION with GET and auth', function(done){
});
});
test('DROP FUNCTION with GET and auth', function(done){
it('DROP FUNCTION with GET and auth', function(done){
assert.response(app, {
url: "/api/v1/sql?" + querystring.stringify({
q: 'DROP FUNCTION create_func_test(a int)',
@ -830,7 +825,7 @@ test('DROP FUNCTION with GET and auth', function(done){
});
});
test('sends a 400 when an unsupported format is requested', function(done){
it('sends a 400 when an unsupported format is requested', function(done){
assert.response(app, {
url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4&format=unknown',
headers: {host: 'vizzuality.cartodb.com'},
@ -844,7 +839,7 @@ test('sends a 400 when an unsupported format is requested', function(done){
});
});
test('GET /api/v1/sql with SQL parameter and no format, ensuring content-disposition set to json', function(done){
it('GET /api/v1/sql with SQL parameter and no format, ensuring content-disposition set to json', function(done){
assert.response(app, {
url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4',
headers: {host: 'vizzuality.cartodb.com'},
@ -860,7 +855,7 @@ test('GET /api/v1/sql with SQL parameter and no format, ensuring content-disposi
});
});
test('POST /api/v1/sql with SQL parameter and no format, ensuring content-disposition set to json', function(done){
it('POST /api/v1/sql with SQL parameter and no format, ensuring content-disposition set to json', function(done){
assert.response(app, {
url: '/api/v1/sql',
data: querystring.stringify({q: "SELECT * FROM untitle_table_4" }),
@ -877,7 +872,7 @@ test('POST /api/v1/sql with SQL parameter and no format, ensuring content-dispos
});
});
test('GET /api/v1/sql with SQL parameter and no format, but a filename', function(done){
it('GET /api/v1/sql with SQL parameter and no format, but a filename', function(done){
assert.response(app, {
url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4&filename=x',
headers: {host: 'vizzuality.cartodb.com'},
@ -893,7 +888,7 @@ test('GET /api/v1/sql with SQL parameter and no format, but a filename', functio
});
});
test('field named "the_geom_webmercator" is not skipped by default', function(done){
it('field named "the_geom_webmercator" is not skipped by default', function(done){
assert.response(app, {
url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4',
headers: {host: 'vizzuality.cartodb.com'},
@ -913,7 +908,7 @@ test('field named "the_geom_webmercator" is not skipped by default', function(do
});
});
test('skipfields controls included fields', function(done){
it('skipfields controls included fields', function(done){
assert.response(app, {
url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4&skipfields=the_geom_webmercator,cartodb_id,unexistant',
headers: {host: 'vizzuality.cartodb.com'},
@ -933,9 +928,10 @@ test('skipfields controls included fields', function(done){
});
});
test('multiple skipfields parameter do not kill the backend', function(done){
it('multiple skipfields parameter do not kill the backend', function(done){
assert.response(app, {
url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4&skipfields=unexistent,the_geom_webmercator&skipfields=cartodb_id,unexistant',
url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4&skipfields=unexistent,the_geom_webmercator' +
'&skipfields=cartodb_id,unexistant',
headers: {host: 'vizzuality.cartodb.com'},
method: 'GET'
},{ }, function(res){
@ -953,7 +949,7 @@ test('multiple skipfields parameter do not kill the backend', function(done){
});
});
test('GET /api/v1/sql ensure cross domain set on errors', function(done){
it('GET /api/v1/sql ensure cross domain set on errors', function(done){
assert.response(app, {
url: '/api/v1/sql?q=SELECT%20*gadfgadfg%20FROM%20untitle_table_4',
headers: {host: 'vizzuality.cartodb.com'},
@ -1013,7 +1009,7 @@ var systemQueriesSuitesToTest = [
function testSystemQueries(description, queries, statusErrorCode, apiKey) {
queries.forEach(function(query) {
test('[' + description + '] query: ' + query, function(done) {
it('[' + description + '] query: ' + query, function(done) {
var queryStringParams = {q: query};
if (!!apiKey) {
queryStringParams.api_key = apiKey;
@ -1031,23 +1027,26 @@ function testSystemQueries(description, queries, statusErrorCode, apiKey) {
});
}
test('GET decent error if domain is incorrect', function(done){
it('GET decent error if domain is incorrect', function(done){
assert.response(app, {
url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4&format=geojson',
headers: {host: 'vizzualinot.cartodb.com'},
method: 'GET'
}, {}, function(res){
assert.equal(res.statusCode, 404, res.statusCode + ( res.statusCode != 200 ? ( ': ' + res.body ) : ''));
assert.equal(res.statusCode, 404, res.statusCode + ( res.statusCode !== 200 ? ( ': ' + res.body ) : ''));
assert.deepEqual(res.headers['content-type'], 'application/json; charset=utf-8');
assert.deepEqual(res.headers['content-disposition'], 'inline');
var result = JSON.parse(res.body);
assert.equal(result.error[0],"Sorry, we can't find CartoDB user 'vizzualinot'. Please check that you have entered the correct domain.");
assert.equal(
result.error[0],
"Sorry, we can't find CartoDB user 'vizzualinot'. Please check that you have entered the correct domain."
);
done();
});
});
// this test does not make sense with the current CDB_QueryTables implementation
test('GET decent error if SQL is broken', function(done){
it('GET decent error if SQL is broken', function(done){
assert.response(app, {
url: '/api/v1/sql?' + querystring.stringify({q:
'SELECT star FROM this and that'
@ -1066,10 +1065,9 @@ test('GET decent error if SQL is broken', function(done){
});
// See https://github.com/Vizzuality/CartoDB-SQL-API/issues/88
test('numeric arrays are rendered as such', function(done){
it('numeric arrays are rendered as such', function(done){
assert.response(app, {
url: "/api/v1/sql?"
+ querystring.stringify({q:
url: "/api/v1/sql?" + querystring.stringify({q:
"SELECT ARRAY[8.7,4.3]::numeric[] as x"
}),
headers: {host: 'vizzuality.localhost.lan:8080' },
@ -1090,7 +1088,7 @@ test('numeric arrays are rendered as such', function(done){
});
// See https://github.com/Vizzuality/CartoDB-SQL-API/issues/97
test('field names and types are exposed', function(done){
it('field names and types are exposed', function(done){
assert.response(app, {
url: '/api/v1/sql?' + querystring.stringify({
q: "SELECT 1::int as a, 2::float8 as b, 3::varchar as c, " +
@ -1123,7 +1121,7 @@ test('field names and types are exposed', function(done){
});
// See https://github.com/CartoDB/CartoDB-SQL-API/issues/109
test('schema response takes skipfields into account', function(done){
it('schema response takes skipfields into account', function(done){
assert.response(app, {
url: '/api/v1/sql?' + querystring.stringify({
q: "SELECT 1 as a, 2 as b, 3 as c ",
@ -1143,7 +1141,7 @@ test('schema response takes skipfields into account', function(done){
});
// See https://github.com/Vizzuality/CartoDB-SQL-API/issues/100
test('numeric fields are rendered as numbers in JSON', function(done){
it('numeric fields are rendered as numbers in JSON', function(done){
assert.response(app, {
url: '/api/v1/sql?' + querystring.stringify({
q: "WITH inp AS ( SELECT 1::int2 as a, 2::int4 as b, " +
@ -1191,8 +1189,8 @@ test('numeric fields are rendered as numbers in JSON', function(done){
// numbers, but it'd currently take running the
// test again (new mocha run) with a different TZ
//
test('timezone info in JSON output', function(done){
Step(
it('timezone info in JSON output', function(done){
step(
function testEuropeRomeExplicit() {
var next = this;
assert.response(app, {
@ -1213,7 +1211,7 @@ test('timezone info in JSON output', function(done){
});
},
function testEuropeRomeImplicit(err) {
if ( err ) throw err;
assert.ifError(err);
var next = this;
assert.response(app, {
url: '/api/v1/sql?' + querystring.stringify({
@ -1233,7 +1231,7 @@ test('timezone info in JSON output', function(done){
});
},
function testUTCExplicit(err) {
if ( err ) throw err;
assert.ifError(err);
var next = this;
assert.response(app, {
url: '/api/v1/sql?' + querystring.stringify({
@ -1253,7 +1251,7 @@ test('timezone info in JSON output', function(done){
});
},
function testUTCImplicit(err) {
if ( err ) throw err;
assert.ifError(err);
var next = this;
assert.response(app, {
url: '/api/v1/sql?' + querystring.stringify({
@ -1280,13 +1278,15 @@ test('timezone info in JSON output', function(done){
// WARNING and NOTICE in JSON output
// See https://github.com/CartoDB/CartoDB-SQL-API/issues/104
test('notice and warning info in JSON output', function(done){
Step(
it('notice and warning info in JSON output', function(done){
step(
function addRaiseFunction() {
var next = this;
assert.response(app, {
url: '/api/v1/sql?' + querystring.stringify({
q: "create or replace function raise(lvl text, msg text) returns void as $$ begin if lvl = 'notice' then raise notice '%', msg; elsif lvl = 'warning' then raise warning '%', msg; else raise exception '%', msg; end if; end; $$ language plpgsql;",
q: "create or replace function raise(lvl text, msg text) returns void as $$ begin if lvl = 'notice' " +
"then raise notice '%', msg; elsif lvl = 'warning' then raise warning '%', msg; " +
"else raise exception '%', msg; end if; end; $$ language plpgsql;",
api_key: '1234'
}),
headers: {host: 'vizzuality.cartodb.com'},
@ -1300,7 +1300,7 @@ test('notice and warning info in JSON output', function(done){
});
},
function raiseNotice(err) {
if ( err ) throw err;
assert.ifError(err);
var next = this;
assert.response(app, {
url: '/api/v1/sql?' + querystring.stringify({
@ -1321,7 +1321,7 @@ test('notice and warning info in JSON output', function(done){
});
},
function raiseWarning(err) {
if ( err ) throw err;
assert.ifError(err);
var next = this;
assert.response(app, {
url: '/api/v1/sql?' + querystring.stringify({
@ -1342,11 +1342,12 @@ test('notice and warning info in JSON output', function(done){
});
},
function raiseBothWarningAndNotice(err) {
if ( err ) throw err;
assert.ifError(err);
var next = this;
assert.response(app, {
url: '/api/v1/sql?' + querystring.stringify({
q: "SET client_min_messages TO 'notice'; select raise('warning', 'hello again warning'), raise('notice', 'hello again notice');"
q: "SET client_min_messages TO 'notice'; select raise('warning', 'hello again warning'), " +
"raise('notice', 'hello again notice');"
}),
headers: {host: 'vizzuality.cartodb.com'},
method: 'GET'
@ -1377,7 +1378,7 @@ test('notice and warning info in JSON output', function(done){
},{ }, function(res) {
try {
assert.equal(res.statusCode, 200, res.body);
var parsedBody = JSON.parse(res.body);
JSON.parse(res.body);
} catch (e) { err = new Error(err + ',' + e); }
next(err);
});
@ -1391,7 +1392,7 @@ test('notice and warning info in JSON output', function(done){
/**
* CORS
*/
test('GET /api/v1/sql with SQL parameter on SELECT only should return CORS headers ', function(done){
it('GET /api/v1/sql with SQL parameter on SELECT only should return CORS headers ', function(done){
assert.response(app, {
url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4&database=cartodb_test_user_1_db',
headers: {host: 'vizzuality.cartodb.com'},
@ -1402,12 +1403,15 @@ test('GET /api/v1/sql with SQL parameter on SELECT only should return CORS heade
assert.equal(res.headers['x-cache-channel'], 'cartodb_test_user_1_db:public.untitle_table_4');
assert.equal(res.headers['cache-control'], expected_cache_control);
assert.equal(res.headers['access-control-allow-origin'], '*');
assert.equal(res.headers['access-control-allow-headers'], "X-Requested-With, X-Prototype-Version, X-CSRF-Token");
assert.equal(
res.headers['access-control-allow-headers'],
"X-Requested-With, X-Prototype-Version, X-CSRF-Token"
);
done();
});
});
test('GET with callback param returns wrapped result set with callback as jsonp', function(done) {
it('GET with callback param returns wrapped result set with callback as jsonp', function(done) {
assert.response(app, {
url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4&callback=foo_jsonp',
headers: {host: 'vizzuality.cartodb.com'},
@ -1419,7 +1423,7 @@ test('GET with callback param returns wrapped result set with callback as jsonp'
});
});
test('GET with callback must return 200 status error even if it is an error', function(done){
it('GET with callback must return 200 status error even if it is an error', function(done){
assert.response(app, {
url: "/api/v1/sql?q=DROP%20TABLE%20untitle_table_4&callback=foo_jsonp",
headers: {host: 'vizzuality.cartodb.com'},
@ -1427,17 +1431,19 @@ test('GET with callback must return 200 status error even if it is an error', fu
},{}, function(res) {
assert.equal(res.statusCode, 200, res.statusCode + ': ' + res.body);
var didRunJsonCallback = false;
// jshint ignore:start
function foo_jsonp(body) {
assert.deepEqual(body, {"error":["must be owner of relation untitle_table_4"]});
didRunJsonCallback = true;
}
eval(res.body);
// jshint ignore:end
assert.ok(didRunJsonCallback);
done();
});
});
test('GET with slow query exceeding statement timeout returns proper error message', function(done){
it('GET with slow query exceeding statement timeout returns proper error message', function(done){
assert.response(app, {
url: "/api/v1/sql?q=select%20pg_sleep(2.1)%20as%20sleep",
headers: {host: 'vizzuality.cartodb.com'},
@ -1452,7 +1458,7 @@ test('GET with callback must return 200 status error even if it is an error', fu
});
});
test('stream response is closed on error and error message is part of the response', function(done){
it('stream response is closed on error and error message is part of the response', function(done){
assert.response(
app,
{
@ -1475,7 +1481,7 @@ test('GET with callback must return 200 status error even if it is an error', fu
);
});
test('too large rows get into error log', function(done){
it('too large rows get into error log', function(done){
var dbMaxRowSize = global.settings.db_max_row_size;
global.settings.db_max_row_size = 4;

View File

@ -1,15 +1,10 @@
require('../helper');
require('../support/assert');
var assert = require('assert')
, App = require(global.settings.app_root + '/app/controllers/app')
, querystring = require('querystring')
, _ = require('underscore')
, Step = require('step')
, net = require('net')
;
var assert = require('../support/assert');
var step = require('step');
var net = require('net');
var sql_server_port = 5556;
var sql_server_port = 5540;
var sql_server = net.createServer(function(c) {
console.log('server connected');
c.destroy();
@ -19,21 +14,21 @@ var sql_server = net.createServer(function(c) {
});
});
suite('backend crash', function() {
describe('backend crash', function() {
suiteSetup(function(done){
before(function(done){
sql_server.listen(sql_server_port, done);
});
// See https://github.com/CartoDB/CartoDB-SQL-API/issues/135
test('does not hang server', function(done){
it('does not hang server', function(done){
//console.log("settings:"); console.dir(global.settings);
var db_host_backup = global.settings.db_host;
var db_port_backup = global.settings.db_port;
global.settings.db_host = 'localhost';
global.settings.db_port = sql_server_port;
var app = App();
Step(
var app = require(global.settings.app_root + '/app/controllers/app')();
step(
function sendQuery() {
var next = this;
assert.response(app, {
@ -45,7 +40,7 @@ test('does not hang server', function(done){
});
},
function checkResponse(err, res) {
if ( err ) throw err;
assert.ifError(err);
assert.equal(res.statusCode, 500, res.statusCode + ': ' + res.body);
var parsed = JSON.parse(res.body);
assert.ok(parsed.error);
@ -64,7 +59,7 @@ test('does not hang server', function(done){
});
},
function checkResponse(err, res) {
if ( err ) throw err;
assert.ifError(err);
assert.equal(res.statusCode, 500, res.statusCode + ': ' + res.body);
var parsed = JSON.parse(res.body);
assert.ok(parsed.error);
@ -80,7 +75,7 @@ test('does not hang server', function(done){
);
});
suiteTeardown(function(done) {
after(function(done) {
try {
sql_server.close(done);
} catch (er) {

View File

@ -2,34 +2,16 @@ require('../../helper');
require('../../support/assert');
var app = require(global.settings.app_root + '/app/controllers/app')()
, assert = require('assert')
, querystring = require('querystring')
, _ = require('underscore')
, zipfile = require('zipfile')
, fs = require('fs')
, libxmljs = require('libxmljs')
, Step = require('step')
;
var app = require(global.settings.app_root + '/app/controllers/app')();
var assert = require('assert');
var querystring = require('querystring');
// allow lots of emitters to be set to silence warning
app.setMaxListeners(0);
suite('export.arraybuffer', function() {
describe('export.arraybuffer', function() {
var expected_cache_control = 'no-cache,max-age=3600,must-revalidate,public';
var expected_cache_control_persist = 'public,max-age=31536000';
// use dec_sep for internationalization
var checkDecimals = function(x, dec_sep){
var tmp='' + x;
if (tmp.indexOf(dec_sep)>-1)
return tmp.length-tmp.indexOf(dec_sep)-1;
else
return 0;
}
test('GET /api/v1/sql as arraybuffer ', function(done){
it('GET /api/v1/sql as arraybuffer ', function(done){
assert.response(app, {
url: '/api/v1/sql?' + querystring.stringify({
q: 'SELECT cartodb_id,name,1::integer,187.9 FROM untitle_table_4',
@ -39,12 +21,12 @@ test('GET /api/v1/sql as arraybuffer ', function(done){
method: 'GET'
},{ }, function(res){
assert.equal(res.statusCode, 200, res.body);
assert.equal(res.headers['content-type'], "application/octet-stream")
assert.equal(res.headers['content-type'], "application/octet-stream");
done();
});
});
test('GET /api/v1/sql as arraybuffer does not support geometry types ', function(done){
it('GET /api/v1/sql as arraybuffer does not support geometry types ', function(done){
assert.response(app, {
url: '/api/v1/sql?' + querystring.stringify({
q: 'SELECT cartodb_id, the_geom FROM untitle_table_4',

View File

@ -2,21 +2,16 @@ require('../../helper');
require('../../support/assert');
var app = require(global.settings.app_root + '/app/controllers/app')()
, assert = require('assert')
, querystring = require('querystring')
, _ = require('underscore')
, zipfile = require('zipfile')
, fs = require('fs')
, libxmljs = require('libxmljs')
;
var app = require(global.settings.app_root + '/app/controllers/app')();
var assert = require('assert');
var querystring = require('querystring');
// allow lots of emitters to be set to silence warning
app.setMaxListeners(0);
suite('export.csv', function() {
describe('export.csv', function() {
test('CSV format', function(done){
it('CSV format', function(done){
assert.response(app, {
url: '/api/v1/sql?' + querystring.stringify({
q: 'SELECT * FROM untitle_table_4 WHERE cartodb_id = 1',
@ -43,7 +38,7 @@ test('CSV format', function(done){
});
});
test('CSV format, bigger than 81920 bytes', function(done){
it('CSV format, bigger than 81920 bytes', function(done){
assert.response(app, {
url: '/api/v1/sql',
data: querystring.stringify({
@ -59,7 +54,7 @@ test('CSV format, bigger than 81920 bytes', function(done){
});
test('CSV format from POST', function(done){
it('CSV format from POST', function(done){
assert.response(app, {
url: '/api/v1/sql',
data: querystring.stringify({q: "SELECT * FROM untitle_table_4 LIMIT 1", format: 'csv'}),
@ -76,7 +71,7 @@ test('CSV format from POST', function(done){
});
});
test('CSV format, custom filename', function(done){
it('CSV format, custom filename', function(done){
assert.response(app, {
url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4%20LIMIT%201&format=csv&filename=mycsv.csv',
headers: {host: 'vizzuality.cartodb.com'},
@ -89,43 +84,45 @@ test('CSV format, custom filename', function(done){
var ct = res.header('Content-Type');
assert.equal(true, /header=present/.test(ct), "CSV doesn't advertise header presence: " + ct);
var row0 = res.body.substring(0, res.body.search(/[\n\r]/)).split(',');
var checkfields = {'name':1, 'cartodb_id':1, 'the_geom':1, 'the_geom_webmercator':1};
for ( var f in checkfields ) {
var idx = row0.indexOf(f);
if ( checkfields[f] ) {
assert.ok(idx != -1, "result does not include '" + f + "'");
} else {
assert.ok(idx == -1, "result includes '" + f + "' ("+idx+")");
}
}
var checkFields = { name: true, cartodb_id: true, the_geom: true, the_geom_webmercator: true };
Object.keys(checkFields).forEach(function(f) {
var idx = row0.indexOf(f);
if ( checkFields[f] ) {
assert.ok(idx !== -1, "result does not include '" + f + "'");
} else {
assert.ok(idx === -1, "result includes '" + f + "' ("+idx+")");
}
});
done();
});
});
test('skipfields controls fields included in CSV output', function(done){
it('skipfields controls fields included in CSV output', function(done){
assert.response(app, {
url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4%20LIMIT%201&format=csv&skipfields=unexistant,cartodb_id',
url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4%20LIMIT%201&format=csv' +
'&skipfields=unexistant,cartodb_id',
headers: {host: 'vizzuality.cartodb.com'},
method: 'GET'
},{ }, function(res){
assert.equal(res.statusCode, 200, res.body);
var row0 = res.body.substring(0, res.body.search(/[\n\r]/)).split(',');
var checkfields = {'name':1, 'cartodb_id':0, 'the_geom':1, 'the_geom_webmercator':1};
for ( var f in checkfields ) {
var idx = row0.indexOf(f);
if ( checkfields[f] ) {
assert.ok(idx != -1, "result does not include '" + f + "'");
} else {
assert.ok(idx == -1, "result includes '" + f + "' ("+idx+")");
}
}
var checkFields = { name: true, cartodb_id: false, the_geom: true, the_geom_webmercator: true };
Object.keys(checkFields).forEach(function(f) {
var idx = row0.indexOf(f);
if ( checkFields[f] ) {
assert.ok(idx !== -1, "result does not include '" + f + "'");
} else {
assert.ok(idx === -1, "result includes '" + f + "' ("+idx+")");
}
});
done();
});
});
test('GET /api/v1/sql as csv', function(done){
it('GET /api/v1/sql as csv', function(done){
assert.response(app, {
url: '/api/v1/sql?q=SELECT%20cartodb_id,ST_AsEWKT(the_geom)%20as%20geom%20FROM%20untitle_table_4%20LIMIT%201&format=csv',
url: '/api/v1/sql?q=SELECT%20cartodb_id,ST_AsEWKT(the_geom)%20as%20geom%20FROM%20untitle_table_4%20LIMIT%201' +
'&format=csv',
headers: {host: 'vizzuality.cartodb.com'},
method: 'GET'
},{ }, function(res){
@ -137,7 +134,7 @@ test('GET /api/v1/sql as csv', function(done){
});
// See https://github.com/Vizzuality/CartoDB-SQL-API/issues/60
test('GET /api/v1/sql as csv with no rows', function(done){
it('GET /api/v1/sql as csv with no rows', function(done){
assert.response(app, {
url: '/api/v1/sql?q=SELECT%20true%20WHERE%20false&format=csv',
headers: {host: 'vizzuality.cartodb.com'},
@ -147,13 +144,12 @@ test('GET /api/v1/sql as csv with no rows', function(done){
var obtained_lines = res.body.split('\r\n');
assert.ok(obtained_lines.length <= 2, // may or may not have an header
// See http://trac.osgeo.org/gdal/ticket/5234
'Too many lines in output (' + obtained_lines.length + '): '
+ obtained_lines.join('\n'));
'Too many lines in output (' + obtained_lines.length + '): ' + obtained_lines.join('\n'));
done();
});
});
test('GET /api/v1/sql as csv, properly escaped', function(done){
it('GET /api/v1/sql as csv, properly escaped', function(done){
assert.response(app, {
url: '/api/v1/sql?q=SELECT%20cartodb_id,%20address%20FROM%20untitle_table_4%20LIMIT%201&format=csv',
headers: {host: 'vizzuality.cartodb.com'},
@ -166,23 +162,29 @@ test('GET /api/v1/sql as csv, properly escaped', function(done){
});
});
test('GET /api/v1/sql as csv, concurrently', function(done){
it('GET /api/v1/sql as csv, concurrently', function(done){
var concurrency = 4;
var waiting = concurrency;
function validate(res){
var expected = 'cartodb_id,address\r\n1,"Calle de Pérez Galdós 9, Madrid, Spain"\r\n';
assert.equal(res.body, expected);
if ( ! --waiting ) {
done();
}
}
for (var i=0; i<concurrency; ++i) {
assert.response(app, {
url: '/api/v1/sql?q=SELECT%20cartodb_id,%20address%20FROM%20untitle_table_4%20LIMIT%201&format=csv',
headers: {host: 'vizzuality.cartodb.com'},
method: 'GET'
},{ }, function(res){
assert.equal(res.statusCode, 200, res.body);
var expected = 'cartodb_id,address\r\n1,"Calle de Pérez Galdós 9, Madrid, Spain"\r\n';
assert.equal(res.body, expected);
if ( ! --waiting ) done();
});
assert.response(app,
{
url: '/api/v1/sql?q=SELECT%20cartodb_id,%20address%20FROM%20untitle_table_4%20LIMIT%201&format=csv',
headers: {host: 'vizzuality.cartodb.com'},
method: 'GET'
},
{
status: 200
},
validate
);
}
});

View File

@ -1,15 +1,8 @@
require('../../helper');
require('../../support/assert');
var app = require(global.settings.app_root + '/app/controllers/app')()
, assert = require('assert')
, querystring = require('querystring')
, _ = require('underscore')
, zipfile = require('zipfile')
, fs = require('fs')
, libxmljs = require('libxmljs')
;
var app = require(global.settings.app_root + '/app/controllers/app')();
var assert = require('../../support/assert');
var querystring = require('querystring');
// allow lots of emitters to be set to silence warning
// TODO: check if still needed ...
@ -18,17 +11,18 @@ app.setMaxListeners(0);
// use dec_sep for internationalization
var checkDecimals = function(x, dec_sep){
var tmp='' + x;
if (tmp.indexOf(dec_sep)>-1)
return tmp.length-tmp.indexOf(dec_sep)-1;
else
if (tmp.indexOf(dec_sep)>-1) {
return tmp.length - tmp.indexOf(dec_sep) - 1;
} else {
return 0;
}
}
};
suite('export.geojson', function() {
describe('export.geojson', function() {
// GEOJSON tests
test('GET /api/v1/sql with SQL parameter and geojson format, ensuring content-disposition set to geojson', function(done){
it('GET /api/v1/sql with SQL parameter, ensuring content-disposition set to geojson', function(done) {
assert.response(app, {
url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4&format=geojson',
headers: {host: 'vizzuality.cartodb.com'},
@ -42,7 +36,7 @@ test('GET /api/v1/sql with SQL parameter and geojson format, ensuring content-di
});
});
test('POST /api/v1/sql with SQL parameter and geojson format, ensuring content-disposition set to geojson', function(done){
it('POST /api/v1/sql with SQL parameter, ensuring content-disposition set to geojson', function(done) {
assert.response(app, {
url: '/api/v1/sql',
data: querystring.stringify({q: "SELECT * FROM untitle_table_4", format: 'geojson' }),
@ -57,7 +51,7 @@ test('POST /api/v1/sql with SQL parameter and geojson format, ensuring content-d
});
});
test('uses the last format parameter when multiple are used', function(done){
it('uses the last format parameter when multiple are used', function(done){
assert.response(app, {
url: '/api/v1/sql?format=csv&q=SELECT%20*%20FROM%20untitle_table_4&format=geojson',
headers: {host: 'vizzuality.cartodb.com'},
@ -70,7 +64,7 @@ test('uses the last format parameter when multiple are used', function(done){
});
});
test('uses custom filename', function(done){
it('uses custom filename', function(done){
assert.response(app, {
url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4&format=geojson&filename=x',
headers: {host: 'vizzuality.cartodb.com'},
@ -83,7 +77,7 @@ test('uses custom filename', function(done){
});
});
test('does not include the_geom and the_geom_webmercator properties by default', function(done){
it('does not include the_geom and the_geom_webmercator properties by default', function(done){
assert.response(app, {
url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4&format=geojson',
headers: {host: 'vizzuality.cartodb.com'},
@ -104,7 +98,7 @@ test('does not include the_geom and the_geom_webmercator properties by default',
});
});
test('skipfields controls fields included in GeoJSON output', function(done){
it('skipfields controls fields included in GeoJSON output', function(done){
assert.response(app, {
url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4&format=geojson&skipfields=unexistant,cartodb_id',
headers: {host: 'vizzuality.cartodb.com'},
@ -126,7 +120,7 @@ test('skipfields controls fields included in GeoJSON output', function(done){
});
test('GET /api/v1/sql as geojson limiting decimal places', function(done){
it('GET /api/v1/sql as geojson limiting decimal places', function(done){
assert.response(app, {
url: '/api/v1/sql?' + querystring.stringify({
q: 'SELECT ST_MakePoint(0.123,2.3456) as the_geom',
@ -142,7 +136,7 @@ test('GET /api/v1/sql as geojson limiting decimal places', function(done){
});
});
test('GET /api/v1/sql as geojson with default dp as 6', function(done){
it('GET /api/v1/sql as geojson with default dp as 6', function(done){
assert.response(app, {
url: '/api/v1/sql?' + querystring.stringify({
q: 'SELECT ST_MakePoint(0.12345678,2.3456787654) as the_geom',
@ -157,7 +151,7 @@ test('GET /api/v1/sql as geojson with default dp as 6', function(done){
});
});
test('null geometries in geojson output', function(done){
it('null geometries in geojson output', function(done){
assert.response(app, {
url: '/api/v1/sql?' + querystring.stringify({
q: "SELECT 1 as gid, 'U' as name, null::geometry as the_geom ",
@ -182,7 +176,7 @@ test('null geometries in geojson output', function(done){
});
});
test('stream response handle errors', function(done) {
it('stream response handle errors', function(done) {
assert.response(app, {
url: '/api/v1/sql?' + querystring.stringify({
q: "SELECTT 1 as gid, null::geometry as the_geom ",
@ -191,7 +185,6 @@ test('stream response handle errors', function(done) {
headers: {host: 'vizzuality.cartodb.com'},
method: 'GET'
},{ }, function(res){
console.log(res);
assert.equal(res.statusCode, 400, res.body);
var geoJson = JSON.parse(res.body);
assert.ok(geoJson.error);
@ -201,7 +194,7 @@ test('stream response handle errors', function(done) {
});
});
test('stream response with empty result set has valid output', function(done) {
it('stream response with empty result set has valid output', function(done) {
assert.response(app, {
url: '/api/v1/sql?' + querystring.stringify({
q: "SELECT 1 as gid, null::geometry as the_geom limit 0",

View File

@ -1,22 +1,16 @@
require('../../helper');
require('../../support/assert');
var app = require(global.settings.app_root + '/app/controllers/app')()
, assert = require('assert')
, querystring = require('querystring')
, _ = require('underscore')
, zipfile = require('zipfile')
, fs = require('fs')
, libxmljs = require('libxmljs')
, http = require('http')
, server_utils = require('../../support/server_utils')
;
var app = require(global.settings.app_root + '/app/controllers/app')();
var assert = require('../../support/assert');
var querystring = require('querystring');
var libxmljs = require('libxmljs');
var http = require('http');
var server_utils = require('../../support/server_utils');
// allow lots of emitters to be set to silence warning
app.setMaxListeners(0);
suite('export.kml', function() {
describe('export.kml', function() {
// Check if an attribute is in the KML output
//
@ -34,15 +28,21 @@ var hasAttribute = function(kml, att) {
var xpath;
xpath = "//SimpleField[@name='" + att + "']";
if ( doc.get(xpath) ) return true;
if ( doc.get(xpath) ) {
return true;
}
xpath = "//Placemark/" + att;
if ( doc.get(xpath) ) return true;
if ( doc.get(xpath) ) {
return true;
}
var lcatt = att.toLowerCase();
if ( lcatt == 'name' || lcatt == 'description' ) {
if ( lcatt === 'name' || lcatt === 'description' ) {
xpath = "//Placemark/" + lcatt;
if ( doc.get(xpath) ) return true;
if ( doc.get(xpath) ) {
return true;
}
}
//if ( lowerkml.indexOf('simplefield name="'+ loweratt + '"') != -1 ) return true;
@ -59,13 +59,19 @@ var extractCoordinates = function(kml) {
var doc = libxmljs.parseXmlString(kml);
//console.log("doc: " + doc);
if ( ! doc ) return;
if ( ! doc ) {
return;
}
var coo = doc.get("//coordinates");
//console.log("coo: " + coo);
if ( ! coo ) return;
if ( ! coo ) {
return;
}
coo = coo.text();
//console.log("coo: " + coo);
if ( ! coo ) return;
if ( ! coo ) {
return;
}
coo = coo.split(' ');
//console.log("coo: " + coo);
for (var i=0; i<coo.length; ++i) {
@ -84,19 +90,25 @@ var extractFolderName = function(kml) {
var doc = libxmljs.parseXmlString(kml);
//console.log("doc: " + doc);
if ( ! doc ) return;
if ( ! doc ) {
return;
}
var coo = doc.get("//Document/Folder/name");
//console.log("coo: " + coo);
if ( ! coo ) return;
if ( ! coo ) {
return;
}
coo = coo.text();
//console.log("coo: " + coo);
if ( ! coo ) return;
if ( ! coo ) {
return;
}
return coo;
};
// KML tests
test('KML format, unauthenticated', function(done){
it('KML format, unauthenticated', function(done){
assert.response(app, {
url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4%20LIMIT%201&format=kml',
headers: {host: 'vizzuality.cartodb.com'},
@ -119,7 +131,7 @@ test('KML format, unauthenticated', function(done){
});
});
test('KML format, unauthenticated, POST', function(done){
it('KML format, unauthenticated, POST', function(done){
assert.response(app, {
url: '/api/v1/sql',
data: 'q=SELECT%20*%20FROM%20untitle_table_4%20LIMIT%201&format=kml',
@ -134,7 +146,7 @@ test('KML format, unauthenticated, POST', function(done){
});
});
test('KML format, bigger than 81920 bytes', function(done){
it('KML format, bigger than 81920 bytes', function(done){
assert.response(app, {
url: '/api/v1/sql',
data: querystring.stringify({
@ -153,7 +165,7 @@ test('KML format, bigger than 81920 bytes', function(done){
});
});
test('KML format, skipfields', function(done){
it('KML format, skipfields', function(done){
assert.response(app, {
url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4%20LIMIT%201&format=kml&skipfields=address,cartodb_id',
headers: {host: 'vizzuality.cartodb.com'},
@ -176,7 +188,7 @@ test('KML format, skipfields', function(done){
});
});
test('KML format, unauthenticated, custom filename', function(done){
it('KML format, unauthenticated, custom filename', function(done){
assert.response(app, {
url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4%20LIMIT%201&format=kml&filename=kmltest',
headers: {host: 'vizzuality.cartodb.com'},
@ -192,7 +204,7 @@ test('KML format, unauthenticated, custom filename', function(done){
});
});
test('KML format, authenticated', function(done){
it('KML format, authenticated', function(done){
assert.response(app, {
url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4%20LIMIT%201&format=kml&api_key=1234',
headers: {host: 'vizzuality.cartodb.com'},
@ -205,15 +217,42 @@ test('KML format, authenticated', function(done){
});
});
test('KML format, unauthenticated, concurrent requests', function(done){
it('KML format, unauthenticated, concurrent requests', function(done){
var query = querystring.stringify({
q: "SELECT 'val', x, y, st_setsrid(st_makepoint(x,y),4326) as the_geom FROM generate_series(-180, 180) as x, generate_series(-90,90) y",
q: "SELECT 'val', x, y, st_setsrid(st_makepoint(x,y),4326) as the_geom " +
"FROM generate_series(-180, 180) as x, generate_series(-90,90) y",
format: 'kml',
filename: 'multi'
});
var concurrency = 4;
var waiting = concurrency;
function onResponse(res) {
//console.log("Response started");
res.body = '';
//res.setEncoding('binary');
res.on('data', function(chunk){ res.body += chunk; });
res.on('end', function(){
//console.log("Response ended");
assert.equal(res.statusCode, 200, res.body);
assert.ok(res.body);
var snippet = res.body.substr(0, 5);
assert.equal(snippet, "<?xml");
var cd = res.headers['content-disposition'];
assert.equal(true, /^attachment/.test(cd), 'KML is not disposed as attachment: ' + cd);
assert.equal(true, /filename=multi.kml/gi.test(cd), 'Unexpected KML filename: ' + cd);
if ( ! --waiting ) {
app.close();
done();
}
});
}
function onError(err) {
console.log("Response error" + err);
}
server_utils.startOnNextPort(app, function() {
var port = app.address().port;
//console.log("Listening on port " + port);
@ -225,34 +264,16 @@ test('KML format, unauthenticated, concurrent requests', function(done){
path: '/api/v1/sql?' + query,
headers: {host: 'vizzuality.cartodb.com'},
agent: false // or should this be true ?
}).on('response', function(res) {
//console.log("Response started");
res.body = '';
//res.setEncoding('binary');
res.on('data', function(chunk){ res.body += chunk; });
res.on('end', function(){
//console.log("Response ended");
assert.equal(res.statusCode, 200, res.body);
assert.ok(res.body);
var snippet = res.body.substr(0, 5);
assert.equal(snippet, "<?xml");
var cd = res.headers['content-disposition'];
assert.equal(true, /^attachment/.test(cd), 'KML is not disposed as attachment: ' + cd);
assert.equal(true, /filename=multi.kml/gi.test(cd), 'Unexpected KML filename: ' + cd);
if ( ! --waiting ) {
app.close();
done();
}
});
}).on('error', function(err) {
console.log("Response error" + err);
}).end();
})
.on('response', onResponse)
.on('error', onError)
.end();
}
});
});
// See https://github.com/Vizzuality/CartoDB-SQL-API/issues/60
test('GET /api/v1/sql as kml with no rows', function(done){
it('GET /api/v1/sql as kml with no rows', function(done){
assert.response(app, {
url: '/api/v1/sql?q=SELECT%20true%20WHERE%20false&format=kml',
headers: {host: 'vizzuality.cartodb.com'},
@ -260,7 +281,10 @@ test('GET /api/v1/sql as kml with no rows', function(done){
},{ }, function(res){
assert.equal(res.statusCode, 200, res.body);
// NOTE: GDAL-1.11+ added 'id="root_doc"' attribute to the output
var pat = new RegExp('^<\\?xml version="1.0" encoding="utf-8" \\?><kml xmlns="http://www.opengis.net/kml/2.2"><Document( id="root_doc")?><Folder><name>cartodb_query</name></Folder></Document></kml>$');
var pat = new RegExp('^<\\?xml version="1.0" encoding="utf-8" \\?>' +
'<kml xmlns="http://www.opengis.net/kml/2.2">' +
'<Document( id="root_doc")?><Folder><name>cartodb_query</name></Folder></Document>' +
'</kml>$');
var body = res.body.replace(/\n/g,'');
assert.ok(body.match(pat),
"Response:\n" + body + '\ndoes not match pattern:\n' + pat);
@ -269,7 +293,7 @@ test('GET /api/v1/sql as kml with no rows', function(done){
});
// See https://github.com/Vizzuality/CartoDB-SQL-API/issues/90
test('GET /api/v1/sql as kml with ending semicolon', function(done){
it('GET /api/v1/sql as kml with ending semicolon', function(done){
assert.response(app, {
url: '/api/v1/sql?' + querystring.stringify({
q: 'SELECT true WHERE false;',
@ -280,7 +304,10 @@ test('GET /api/v1/sql as kml with ending semicolon', function(done){
},{ }, function(res){
assert.equal(res.statusCode, 200, res.body);
// NOTE: GDAL-1.11+ added 'id="root_doc"' attribute to the output
var pat = new RegExp('^<\\?xml version="1.0" encoding="utf-8" \\?><kml xmlns="http://www.opengis.net/kml/2.2"><Document( id="root_doc")?><Folder><name>cartodb_query</name></Folder></Document></kml>$');
var pat = new RegExp('^<\\?xml version="1.0" encoding="utf-8" \\?>' +
'<kml xmlns="http://www.opengis.net/kml/2.2">' +
'<Document( id="root_doc")?><Folder><name>cartodb_query</name></Folder></Document>' +
'</kml>$');
var body = res.body.replace(/\n/g,'');
assert.ok(body.match(pat),
"Response:\n" + body + '\ndoes not match pattern:\n' + pat);
@ -289,7 +316,7 @@ test('GET /api/v1/sql as kml with ending semicolon', function(done){
});
// See https://github.com/CartoDB/cartodb/issues/276
test('check point coordinates, unauthenticated', function(done){
it('check point coordinates, unauthenticated', function(done){
assert.response(app, {
url: '/api/v1/sql?' + querystring.stringify({
q: 'SELECT * from untitle_table_4 WHERE cartodb_id = -1',
@ -307,7 +334,7 @@ test('check point coordinates, unauthenticated', function(done){
});
// See https://github.com/CartoDB/cartodb/issues/276
test('check point coordinates, authenticated', function(done){
it('check point coordinates, authenticated', function(done){
assert.response(app, {
url: '/api/v1/sql?' + querystring.stringify({
q: 'SELECT * from untitle_table_4 WHERE cartodb_id = -1',
@ -325,7 +352,7 @@ test('check point coordinates, authenticated', function(done){
});
});
test('expects 1000 placemarks in public table', function(done){
it('expects 1000 placemarks in public table', function(done){
var numberOfRowsInPublicTable = 6,
seriesLimit = 200,
expectedRows = numberOfRowsInPublicTable * seriesLimit;
@ -349,7 +376,7 @@ test('check point coordinates, authenticated', function(done){
);
});
test('expects 1000 placemarks in private table using the API KEY', function(done){
it('expects 1000 placemarks in private table using the API KEY', function(done){
var numberOfRowsInPrivateTable = 5,
seriesLimit = 200,
expectedRows = numberOfRowsInPrivateTable * seriesLimit;

View File

@ -1,24 +1,20 @@
require('../../helper');
require('../../support/assert');
var app = require(global.settings.app_root + '/app/controllers/app')()
, assert = require('assert')
, querystring = require('querystring')
, _ = require('underscore')
, zipfile = require('zipfile')
, fs = require('fs')
, libxmljs = require('libxmljs')
;
var app = require(global.settings.app_root + '/app/controllers/app')();
var assert = require('../../support/assert');
var querystring = require('querystring');
var _ = require('underscore');
var zipfile = require('zipfile');
var fs = require('fs');
// allow lots of emitters to be set to silence warning
app.setMaxListeners(0);
suite('export.shapefile', function() {
describe('export.shapefile', function() {
// SHP tests
test('SHP format, unauthenticated', function(done){
it('SHP format, unauthenticated', function(done){
assert.response(app, {
url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4%20LIMIT%201&format=shp',
headers: {host: 'vizzuality.cartodb.com'},
@ -31,7 +27,9 @@ test('SHP format, unauthenticated', function(done){
assert.equal(true, /filename=cartodb-query.zip/gi.test(cd));
var tmpfile = '/tmp/myshape.zip';
var err = fs.writeFileSync(tmpfile, res.body, 'binary');
if (err) { done(err); return }
if (err) {
return done(err);
}
var zf = new zipfile.ZipFile(tmpfile);
assert.ok(_.contains(zf.names, 'cartodb-query.shp'), 'SHP zipfile does not contain .shp: ' + zf.names);
assert.ok(_.contains(zf.names, 'cartodb-query.shx'), 'SHP zipfile does not contain .shx: ' + zf.names);
@ -43,7 +41,7 @@ test('SHP format, unauthenticated', function(done){
});
});
test('SHP format, unauthenticated, POST', function(done){
it('SHP format, unauthenticated, POST', function(done){
assert.response(app, {
url: '/api/v1/sql',
data: 'q=SELECT%20*%20FROM%20untitle_table_4%20LIMIT%201&format=shp',
@ -58,7 +56,7 @@ test('SHP format, unauthenticated, POST', function(done){
});
});
test('SHP format, big size, POST', function(done){
it('SHP format, big size, POST', function(done){
assert.response(app, {
url: '/api/v1/sql',
data: querystring.stringify({
@ -77,7 +75,7 @@ test('SHP format, big size, POST', function(done){
});
});
test('SHP format, unauthenticated, with custom filename', function(done){
it('SHP format, unauthenticated, with custom filename', function(done){
assert.response(app, {
url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4%20LIMIT%201&format=shp&filename=myshape',
headers: {host: 'vizzuality.cartodb.com'},
@ -90,7 +88,9 @@ test('SHP format, unauthenticated, with custom filename', function(done){
assert.equal(true, /filename=myshape.zip/gi.test(cd));
var tmpfile = '/tmp/myshape.zip';
var err = fs.writeFileSync(tmpfile, res.body, 'binary');
if (err) { done(err); return }
if (err) {
return done(err);
}
var zf = new zipfile.ZipFile(tmpfile);
assert.ok(_.contains(zf.names, 'myshape.shp'), 'SHP zipfile does not contain .shp: ' + zf.names);
assert.ok(_.contains(zf.names, 'myshape.shx'), 'SHP zipfile does not contain .shx: ' + zf.names);
@ -101,7 +101,7 @@ test('SHP format, unauthenticated, with custom filename', function(done){
});
});
test('SHP format, unauthenticated, with custom, dangerous filename', function(done){
it('SHP format, unauthenticated, with custom, dangerous filename', function(done){
assert.response(app, {
url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4%20LIMIT%201&format=shp&filename=b;"%20()[]a',
headers: {host: 'vizzuality.cartodb.com'},
@ -115,7 +115,9 @@ test('SHP format, unauthenticated, with custom, dangerous filename', function(do
assert.equal(true, /filename=b_______a.zip/gi.test(cd), 'Unexpected SHP filename: ' + cd);
var tmpfile = '/tmp/myshape.zip';
var err = fs.writeFileSync(tmpfile, res.body, 'binary');
if (err) { done(err); return }
if (err) {
return done(err);
}
var zf = new zipfile.ZipFile(tmpfile);
assert.ok(_.contains(zf.names, fname + '.shp'), 'SHP zipfile does not contain .shp: ' + zf.names);
assert.ok(_.contains(zf.names, fname + '.shx'), 'SHP zipfile does not contain .shx: ' + zf.names);
@ -126,7 +128,7 @@ test('SHP format, unauthenticated, with custom, dangerous filename', function(do
});
});
test('SHP format, authenticated', function(done){
it('SHP format, authenticated', function(done){
assert.response(app, {
url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4%20LIMIT%201&format=shp&api_key=1234',
headers: {host: 'vizzuality.cartodb.com'},
@ -138,7 +140,9 @@ test('SHP format, authenticated', function(done){
assert.equal(true, /filename=cartodb-query.zip/gi.test(cd));
var tmpfile = '/tmp/myshape.zip';
var err = fs.writeFileSync(tmpfile, res.body, 'binary');
if (err) { done(err); return }
if (err) {
return done(err);
}
var zf = new zipfile.ZipFile(tmpfile);
assert.ok(_.contains(zf.names, 'cartodb-query.shp'), 'SHP zipfile does not contain .shp: ' + zf.names);
assert.ok(_.contains(zf.names, 'cartodb-query.shx'), 'SHP zipfile does not contain .shx: ' + zf.names);
@ -152,7 +156,7 @@ 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){
it('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',
@ -167,7 +171,9 @@ test('SHP format, unauthenticated, with utf8 data', function(done){
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 }
if (err) {
return done(err);
}
var zf = new zipfile.ZipFile(tmpfile);
var buffer = zf.readFileSync('myshape.dbf');
fs.unlinkSync(tmpfile);
@ -178,10 +184,9 @@ test('SHP format, unauthenticated, with utf8 data', function(done){
});
// See https://github.com/Vizzuality/CartoDB-SQL-API/issues/66
test('mixed type geometry', function(done){
it('mixed type geometry', function(done){
var query = querystring.stringify({
q: "SELECT 'POINT(0 0)'::geometry as g UNION ALL "
+ "SELECT 'LINESTRING(0 0, 1 0)'::geometry",
q: "SELECT 'POINT(0 0)'::geometry as g UNION ALL SELECT 'LINESTRING(0 0, 1 0)'::geometry",
format: 'shp'
});
assert.response(app, {
@ -194,19 +199,19 @@ test('mixed type geometry', function(done){
assert.deepEqual(res.headers['content-disposition'], 'inline');
assert.equal(res.statusCode, 400, res.statusCode + ': ' +res.body);
var parsedBody = JSON.parse(res.body);
var expectedBody = {"error":["ERROR 1: Attempt to write non-point (LINESTRING) geometry to point shapefile."]}
var expectedBody = {"error":["ERROR 1: Attempt to write non-point (LINESTRING) geometry to point shapefile."]};
assert.deepEqual(parsedBody, expectedBody);
done();
});
});
// See https://github.com/Vizzuality/CartoDB-SQL-API/issues/87
test('errors are not confused with warnings', function(done){
it('errors are not confused with warnings', function(done){
var query = querystring.stringify({
q: "SELECT 'POINT(0 0)'::geometry as g"
+ ", 1 as a_very_very_very_long_field_name"
+ " UNION ALL "
+ "SELECT 'LINESTRING(0 0, 1 0)'::geometry, 2",
q: [
"SELECT 'POINT(0 0)'::geometry as g, 1 as a_very_very_very_long_field_name",
"SELECT 'LINESTRING(0 0, 1 0)'::geometry, 2"
].join(" UNION ALL "),
format: 'shp'
});
assert.response(app, {
@ -219,13 +224,13 @@ test('errors are not confused with warnings', function(done){
assert.deepEqual(res.headers['content-disposition'], 'inline');
assert.equal(res.statusCode, 400, res.statusCode + ': ' +res.body);
var parsedBody = JSON.parse(res.body);
var expectedBody = {"error":["ERROR 1: Attempt to write non-point (LINESTRING) geometry to point shapefile."]}
var expectedBody = {"error":["ERROR 1: Attempt to write non-point (LINESTRING) geometry to point shapefile."]};
assert.deepEqual(parsedBody, expectedBody);
done();
});
});
test('skipfields controls fields included in SHP output', function(done){
it('skipfields controls fields included in SHP output', function(done){
var query = querystring.stringify({
q: "SELECT 111 as skipme, 222 as keepme, 'POINT(0 0)'::geometry as g",
format: 'shp',
@ -241,7 +246,9 @@ test('skipfields controls fields included in SHP output', function(done){
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 }
if (err) {
return done(err);
}
var zf = new zipfile.ZipFile(tmpfile);
var buffer = zf.readFileSync('myshape.dbf');
fs.unlinkSync(tmpfile);
@ -251,37 +258,48 @@ test('skipfields controls fields included in SHP output', function(done){
});
});
test('SHP format, concurrently', function(done){
it('SHP format, concurrently', function(done){
var concurrency = 1;
var waiting = concurrency;
function validate(res){
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));
var tmpfile = '/tmp/myshape.zip';
var err = fs.writeFileSync(tmpfile, res.body, 'binary');
if (err) {
return done(err);
}
var zf = new zipfile.ZipFile(tmpfile);
assert.ok(_.contains(zf.names, 'cartodb-query.shp'), 'SHP zipfile does not contain .shp: ' + zf.names);
assert.ok(_.contains(zf.names, 'cartodb-query.shx'), 'SHP zipfile does not contain .shx: ' + zf.names);
assert.ok(_.contains(zf.names, 'cartodb-query.dbf'), 'SHP zipfile does not contain .dbf: ' + zf.names);
assert.ok(_.contains(zf.names, 'cartodb-query.prj'), 'SHP zipfile does not contain .prj: ' + zf.names);
// TODO: check DBF contents
fs.unlinkSync(tmpfile);
if ( ! --waiting ) {
done();
}
}
for (var i=0; i<concurrency; ++i) {
assert.response(app, {
url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4%20LIMIT%201&format=shp',
headers: {host: 'vizzuality.cartodb.com'},
encoding: 'binary',
method: 'GET'
},{ }, 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));
var tmpfile = '/tmp/myshape.zip';
var err = fs.writeFileSync(tmpfile, res.body, 'binary');
if (err) { done(err); return }
var zf = new zipfile.ZipFile(tmpfile);
assert.ok(_.contains(zf.names, 'cartodb-query.shp'), 'SHP zipfile does not contain .shp: ' + zf.names);
assert.ok(_.contains(zf.names, 'cartodb-query.shx'), 'SHP zipfile does not contain .shx: ' + zf.names);
assert.ok(_.contains(zf.names, 'cartodb-query.dbf'), 'SHP zipfile does not contain .dbf: ' + zf.names);
assert.ok(_.contains(zf.names, 'cartodb-query.prj'), 'SHP zipfile does not contain .prj: ' + zf.names);
// TODO: check DBF contents
fs.unlinkSync(tmpfile);
if ( ! --waiting ) done();
});
assert.response(
app,
{
url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4%20LIMIT%201&format=shp',
headers: {host: 'vizzuality.cartodb.com'},
encoding: 'binary',
method: 'GET'
},
{
status: 200
},
validate
);
}
});
// See https://github.com/CartoDB/CartoDB-SQL-API/issues/111
test('point with null first', function(done){
it('point with null first', function(done){
var query = querystring.stringify({
q: "SELECT null::geometry as g UNION ALL SELECT 'SRID=4326;POINT(0 0)'::geometry",
format: 'shp'
@ -297,7 +315,9 @@ test('point with null first', function(done){
assert.equal(true, /filename=cartodb-query.zip/gi.test(cd));
var tmpfile = '/tmp/myshape.zip';
var err = fs.writeFileSync(tmpfile, res.body, 'binary');
if (err) { done(err); return }
if (err) {
return done(err);
}
var zf = new zipfile.ZipFile(tmpfile);
assert.ok(_.contains(zf.names, 'cartodb-query.shp'), 'SHP zipfile does not contain .shp: ' + zf.names);
assert.ok(_.contains(zf.names, 'cartodb-query.shx'), 'SHP zipfile does not contain .shx: ' + zf.names);

View File

@ -1,22 +1,15 @@
require('../../helper');
require('../../support/assert');
var app = require(global.settings.app_root + '/app/controllers/app')()
, assert = require('assert')
, querystring = require('querystring')
, _ = require('underscore')
, zipfile = require('zipfile')
, fs = require('fs')
, libxmljs = require('libxmljs')
;
var app = require(global.settings.app_root + '/app/controllers/app')();
var assert = require('../../support/assert');
var querystring = require('querystring');
// allow lots of emitters to be set to silence warning
app.setMaxListeners(0);
suite('export.svg', function() {
describe('export.svg', function() {
test('GET /api/v1/sql with SVG format', function(done){
it('GET /api/v1/sql with SVG format', function(done){
var query = querystring.stringify({
q: "SELECT 1 as cartodb_id, ST_MakeLine(ST_MakePoint(10, 10), ST_MakePoint(1034, 778)) AS the_geom ",
format: "svg"
@ -36,7 +29,7 @@ test('GET /api/v1/sql with SVG format', function(done){
});
});
test('POST /api/v1/sql with SVG format', function(done){
it('POST /api/v1/sql with SVG format', function(done){
var query = querystring.stringify({
q: "SELECT 1 as cartodb_id, ST_MakeLine(ST_MakePoint(10, 10), ST_MakePoint(1034, 778)) AS the_geom ",
format: "svg"
@ -58,7 +51,7 @@ test('POST /api/v1/sql with SVG format', function(done){
});
});
test('GET /api/v1/sql with SVG format and custom filename', function(done){
it('GET /api/v1/sql with SVG format and custom filename', function(done){
var query = querystring.stringify({
q: "SELECT 1 as cartodb_id, ST_MakeLine(ST_MakePoint(10, 10), ST_MakePoint(1034, 778)) AS the_geom ",
format: "svg",
@ -79,7 +72,7 @@ test('GET /api/v1/sql with SVG format and custom filename', function(done){
});
});
test('GET /api/v1/sql with SVG format and centered point', function(done){
it('GET /api/v1/sql with SVG format and centered point', function(done){
var query = querystring.stringify({
q: "SELECT 1 as cartodb_id, ST_MakePoint(5000, -54) AS the_geom ",
format: "svg"
@ -100,7 +93,7 @@ test('GET /api/v1/sql with SVG format and centered point', function(done){
});
});
test('GET /api/v1/sql with SVG format and trimmed decimals', function(done){
it('GET /api/v1/sql with SVG format and trimmed decimals', function(done){
var queryobj = {
q: "SELECT 1 as cartodb_id, 'LINESTRING(0 0, 1024 768, 500.123456 600.98765432)'::geometry AS the_geom ",
format: "svg",
@ -138,7 +131,7 @@ test('GET /api/v1/sql with SVG format and trimmed decimals', function(done){
// Test adding "the_geom" to skipfields
// See http://github.com/Vizzuality/CartoDB-SQL-API/issues/73
test('SVG format with "the_geom" in skipfields', function(done){
it('SVG format with "the_geom" in skipfields', function(done){
var query = querystring.stringify({
q: "SELECT 1 as cartodb_id, ST_MakePoint(5000, -54) AS the_geom ",
format: "svg",
@ -159,7 +152,7 @@ test('SVG format with "the_geom" in skipfields', function(done){
});
});
test('SVG format with missing "the_geom" field', function(done){
it('SVG format with missing "the_geom" field', function(done){
var query = querystring.stringify({
q: "SELECT 1 as cartodb_id, ST_MakePoint(5000, -54) AS something_else ",
format: "svg"

View File

@ -1,21 +1,15 @@
require('../../helper');
require('../../support/assert');
var app = require(global.settings.app_root + '/app/controllers/app')()
, assert = require('assert')
, querystring = require('querystring')
, _ = require('underscore')
, zipfile = require('zipfile')
, fs = require('fs')
, libxmljs = require('libxmljs')
;
var app = require(global.settings.app_root + '/app/controllers/app')();
var assert = require('../../support/assert');
var querystring = require('querystring');
var _ = require('underscore');
// allow lots of emitters to be set to silence warning
app.setMaxListeners(0);
suite('export.topojson', function() {
describe('export.topojson', function() {
// TOPOJSON tests
@ -34,7 +28,7 @@ suite('export.topojson', function() {
};
}
test('GET two polygons sharing an edge as topojson', function(done){
it('GET two polygons sharing an edge as topojson', function(done){
assert.response(app,
getRequest(
"SELECT 1 as gid, 'U' as name, 'POLYGON((-5 0,5 0,0 5,-5 0))'::geometry as the_geom " +
@ -81,8 +75,8 @@ test('GET two polygons sharing an edge as topojson', function(done){
assert.equal(shell[1], 1); /* non-shared arc */
var props = obj.properties;
assert.equal(_.keys(props).length, 2); // gid, name
assert.equal(props['gid'], 1);
assert.equal(props['name'], 'U');
assert.equal(props.gid, 1);
assert.equal(props.name, 'U');
obj = topojson.objects[1];
//console.dir(obj);
@ -99,8 +93,8 @@ test('GET two polygons sharing an edge as topojson', function(done){
assert.equal(shell[1], 2); /* non-shared arc */
props = obj.properties;
assert.equal(_.keys(props).length, 2); // gid, name
assert.equal(props['gid'], 2);
assert.equal(props['name'], 'D');
assert.equal(props.gid, 2);
assert.equal(props.name, 'D');
// Check arcs
assert.ok(topojson.hasOwnProperty('arcs'));
@ -140,7 +134,7 @@ test('GET two polygons sharing an edge as topojson', function(done){
});
});
test('null geometries', function(done){
it('null geometries', function(done){
assert.response(app, getRequest(
"SELECT 1 as gid, 'U' as name, 'POLYGON((-5 0,5 0,0 5,-5 0))'::geometry as the_geom " +
" UNION ALL " +
@ -185,8 +179,8 @@ test('null geometries', function(done){
assert.equal(shell[0], 0); /* non-shared arc */
var props = obj.properties;
assert.equal(_.keys(props).length, 2); // gid, name
assert.equal(props['gid'], 1);
assert.equal(props['name'], 'U');
assert.equal(props.gid, 1);
assert.equal(props.name, 'U');
// Check arcs
assert.ok(topojson.hasOwnProperty('arcs'));
@ -198,7 +192,7 @@ test('null geometries', function(done){
});
});
test('skipped fields are not returned', function(done) {
it('skipped fields are not returned', function(done) {
assert.response(app,
getRequest(
"SELECT 1 as gid, 'U' as name, 'POLYGON((-5 0,5 0,0 5,-5 0))'::geometry as the_geom",
@ -218,7 +212,7 @@ test('null geometries', function(done){
);
});
test('jsonp callback is invoked', function(done){
it('jsonp callback is invoked', function(done){
assert.response(
app,
getRequest(
@ -233,10 +227,12 @@ test('null geometries', function(done){
function(res) {
assert.equal(res.statusCode, 200, res.statusCode + ': ' + res.body);
var didRunJsonCallback = false;
// jshint ignore:start
function foo_jsonp(body) {
didRunJsonCallback = true;
}
eval(res.body);
// jshint ignore:end
assert.ok(didRunJsonCallback);
done();
}

View File

@ -1,14 +1,8 @@
require('../helper');
require('../support/assert');
var assert = require('assert')
, App = require(global.settings.app_root + '/app/controllers/app')
, querystring = require('querystring')
, _ = require('underscore')
, Step = require('step')
, net = require('net')
, http = require('http')
;
var assert = require('../support/assert');
var step = require('step');
var net = require('net');
var sql_server_data_handler;
var sql_server_port = 5556;
@ -23,22 +17,22 @@ var sql_server = net.createServer(function(c) {
});
});
suite('frontend abort', function() {
describe('frontend abort', function() {
suiteSetup(function(done){
before(function(done){
sql_server.listen(sql_server_port, done);
});
// See https://github.com/CartoDB/CartoDB-SQL-API/issues/129
test('aborts request', function(done){
it('aborts request', function(done){
//console.log("settings:"); console.dir(global.settings);
var db_host_backup = global.settings.db_host;
var db_port_backup = global.settings.db_port;
global.settings.db_host = 'localhost';
global.settings.db_port = sql_server_port;
var app = App();
var app = require(global.settings.app_root + '/app/controllers/app')();
var timeout;
Step(
step(
function sendQuery() {
var next = this;
assert.response(app, {
@ -50,7 +44,7 @@ test('aborts request', function(done){
next(err, res);
});
},
function checkResponse(err, res) {
function checkResponse(err/*, res*/) {
assert(err); // expect timeout
assert.ok((''+err).match(/socket/), err);
sql_server_data_handler = this;
@ -74,7 +68,7 @@ test('aborts request', function(done){
);
});
suiteTeardown(function(done) {
after(function(done) {
try {
sql_server.close(done);
} catch (er) {

View File

@ -1,12 +1,10 @@
require('../helper');
require('../support/assert');
var assert = require('assert'),
App = require(global.settings.app_root + '/app/controllers/app');
var assert = require('assert');
var app = require(global.settings.app_root + '/app/controllers/app')();
var app = App();
suite('health checks', function() {
describe('health checks', function() {
beforeEach(function(done) {
global.settings.health = {
@ -25,7 +23,7 @@ suite('health checks', function() {
}
};
test('returns 200 and ok=true with disabled configuration', function(done) {
it('returns 200 and ok=true with disabled configuration', function(done) {
global.settings.health.enabled = false;
assert.response(app,
@ -46,7 +44,7 @@ suite('health checks', function() {
);
});
test('returns 200 and ok=true with enabled configuration', function(done) {
it('returns 200 and ok=true with enabled configuration', function(done) {
assert.response(app,
healthCheckRequest,
{

View File

@ -13,25 +13,21 @@
*
*/
require('../helper');
require('../support/assert');
var assert = require('assert')
, App = require(global.settings.app_root + '/app/controllers/app')
, querystring = require('querystring')
, _ = require('underscore')
, Step = require('step')
;
suite('timeout', function() {
var assert = require('../support/assert');
var step = require('step');
describe('timeout', function() {
// See https://github.com/CartoDB/CartoDB-SQL-API/issues/128
test('after configured milliseconds', function(done){
it('after configured milliseconds', function(done){
var testTimeout = 10;
//console.log("settings:"); console.dir(global.settings);
var timeoutBackup = global.settings.node_socket_timeout;
global.settings.node_socket_timeout = testTimeout;
var app = App();
Step(
var app = require(global.settings.app_root + '/app/controllers/app')();
step(
function sendLongQuery() {
var next = this;
assert.response(app, {
@ -42,7 +38,7 @@ test('after configured milliseconds', function(done){
next(err, res);
});
},
function checkResponse(err, res) {
function checkResponse(err/*, res*/) {
assert.ok(err);
assert.ok(err.message.match(/hang up/), err);
return null;

View File

@ -1,24 +1,23 @@
require('../helper');
require('../support/assert');
var app = require(global.settings.app_root + '/app/controllers/app')()
, assert = require('assert')
, querystring = require('querystring')
, _ = require('underscore')
;
var app = require(global.settings.app_root + '/app/controllers/app')();
var assert = require('../support/assert');
var querystring = require('querystring');
var _ = require('underscore');
// allow lots of emitters to be set to silence warning
app.setMaxListeners(0);
suite('x_cache_channel', function() {
describe('x_cache_channel', function() {
assert.contains = function(ary, elem) {
assert.ok(_.contains(ary,elem), 'missing "' + elem +'" from x-cache-channel: '+ ary);
};
test('supports joins', function(done) {
it('supports joins', function(done) {
var query = querystring.stringify({
q: "SELECT a.name as an, b.name as bn FROM untitle_table_4 a left join private_table b ON (a.cartodb_id = b.cartodb_id)",
q: "SELECT a.name as an, b.name as bn FROM untitle_table_4 a " +
"left join private_table b ON (a.cartodb_id = b.cartodb_id)",
api_key: 1234
});
assert.response(app, {
@ -38,7 +37,7 @@ test('supports joins', function(done) {
});
});
test('supports multistatements', function(done) {
it('supports multistatements', function(done) {
var query = querystring.stringify({
q: "SELECT * FROM untitle_table_4; SELECT * FROM private_table",
api_key: 1234
@ -60,7 +59,7 @@ test('supports multistatements', function(done) {
});
});
test('supports explicit transactions', function(done) {
it('supports explicit transactions', function(done) {
var query = querystring.stringify({
q: "BEGIN; SELECT * FROM untitle_table_4; COMMIT; BEGIN; SELECT * FROM private_table; COMMIT;",
api_key: 1234
@ -82,7 +81,7 @@ test('supports explicit transactions', function(done) {
});
});
test('survives partial transactions', function(done) {
it('survives partial transactions', function(done) {
var query = querystring.stringify({
q: "BEGIN; SELECT * FROM untitle_table_4",
api_key: 1234

View File

@ -1,11 +1,9 @@
require('../helper');
var _ = require('underscore')
, ApikeyAuth = require('../../app/auth/apikey')
, assert = require('assert')
;
var ApikeyAuth = require('../../app/auth/apikey');
var assert = require('assert');
suite('has credentials', function() {
describe('has credentials', function() {
var noCredentialsRequests = [
{
@ -35,8 +33,8 @@ suite('has credentials', function() {
];
noCredentialsRequests.forEach(function(request) {
test('has no credentials if ' + request.des, function() {
testCredentials(request.req, false)
it('has no credentials if ' + request.des, function() {
testCredentials(request.req, false);
});
});
@ -60,8 +58,8 @@ suite('has credentials', function() {
];
credentialsRequests.forEach(function(request) {
test('has credentials if ' + request.des, function() {
testCredentials(request.req, true)
it('has credentials if ' + request.des, function() {
testCredentials(request.req, true);
});
});
@ -72,13 +70,13 @@ suite('has credentials', function() {
});
suite('verify credentials', function() {
describe('verifyCredentials', function() {
test('verifyCredentials callbacks with true value when request api_key is the same', function(done) {
it('callbacks with true value when request api_key is the same', function(done) {
testVerifyCredentials({query:{api_key: 'foo'}}, {apiKey: 'foo'}, true, done);
});
test('verifyCredentials callbacks with true value when request api_key is different', function(done) {
it('callbacks with false value when request api_key is different', function(done) {
testVerifyCredentials({query:{api_key: 'foo'}}, {apiKey: 'bar'}, false, done);
});

View File

@ -1,8 +1,7 @@
require('../helper')
require('../helper');
var assert = require('assert'),
_ = require('underscore'),
HealthCheck = require('../../app/monitoring/health_check');
var assert = require('assert');
var HealthCheck = require('../../app/monitoring/health_check');
var metadataBackend = {};
@ -12,34 +11,34 @@ function PSQL(dbParams) {
var healthCheck = new HealthCheck(metadataBackend, PSQL);
suite('health checks', function() {
describe('health checks', function() {
test('error if disabled file exists', function(done) {
it('errors if disabled file exists', function(done) {
var fs = require('fs');
var readFileFn = fs.readFile;
fs.readFile = function(filename, callback) {
callback(null, "Maintenance");
}
healthCheck.check('fake', 'select 1', function(err, result) {
};
healthCheck.check('fake', 'select 1', function(err/*, result*/) {
assert.equal(err.message, "Maintenance");
assert.equal(err.http_status, 503);
done();
fs.readFile = readFileFn;
done();
});
});
test('not err if disabled file does not exists', function(done) {
it('does not err if disabled file does not exists', function(done) {
var fs = require('fs');
var readFileFn = fs.readFile;
fs.readFile = function(filename, callback) {
callback(new Error("ENOENT"), null);
}
healthCheck.check('fake', 'select 1', function(err, result) {
};
healthCheck.check('fake', 'select 1', function(err/*, result*/) {
assert.equal(err, null);
done();
fs.readFile = readFileFn;
done();
});
});

View File

@ -1,30 +1,30 @@
require('../../helper');
var assert = require('assert');
var ArrayBufferSer = require('../../../app/models/bin_encoder')
var ArrayBufferSer = require('../../../app/models/bin_encoder');
suite('ArrayBufferSer', function() {
describe('ArrayBufferSer', function() {
test('calculate size for basic types', function() {
var b = new ArrayBufferSer(ArrayBufferSer.INT16, [1,2,3,4])
it('calculate size for basic types', function() {
var b = new ArrayBufferSer(ArrayBufferSer.INT16, [1,2,3,4]);
assert.equal(4*2, b.getDataSize());
b = new ArrayBufferSer(ArrayBufferSer.INT8, [1,2,3,4])
assert.equal(4*1, b.getDataSize());
b = new ArrayBufferSer(ArrayBufferSer.INT8, [1,2,3,4]);
assert.equal(4, b.getDataSize());
b = new ArrayBufferSer(ArrayBufferSer.INT32, [1,2,3,4])
b = new ArrayBufferSer(ArrayBufferSer.INT32, [1,2,3,4]);
assert.equal(4*4, b.getDataSize());
});
test('calculate size for arrays', function() {
var b = new ArrayBufferSer(ArrayBufferSer.STRING, ["test","kease"])
it('calculate size for arrays', function() {
var b = new ArrayBufferSer(ArrayBufferSer.STRING, ["test","kease"]);
assert.equal((b.headerSize + 4 + 5)*2, b.getDataSize());
var ba = new ArrayBufferSer(ArrayBufferSer.INT16, [1,2,3,4])
var bc = new ArrayBufferSer(ArrayBufferSer.INT16, [1,4])
var ba = new ArrayBufferSer(ArrayBufferSer.INT16, [1,2,3,4]);
var bc = new ArrayBufferSer(ArrayBufferSer.INT16, [1,4]);
b = new ArrayBufferSer(ArrayBufferSer.BUFFER, [ba, bc])
b = new ArrayBufferSer(ArrayBufferSer.BUFFER, [ba, bc]);
assert.equal((b.headerSize + 4 + 2)*2, b.getDataSize());
assert.equal(b.type, ArrayBufferSer.BUFFER);
});
@ -36,28 +36,28 @@ suite('ArrayBufferSer', function() {
}
}
test('binary data is ok', function() {
var b = new ArrayBufferSer(ArrayBufferSer.INT16, [1,2,3,4])
it('binary data is ok', function() {
var b = new ArrayBufferSer(ArrayBufferSer.INT16, [1,2,3,4]);
var bf = new Buffer([0, 0, 0, ArrayBufferSer.INT16, 0, 0, 0, 8, 1, 0, 2, 0, 3, 0, 4, 0]);
assert_buffer_equals(bf, b.buffer);
});
test('binary data is ok with arrays', function() {
var ba = new ArrayBufferSer(ArrayBufferSer.INT16, [1,2, 3, 4])
var bc = new ArrayBufferSer(ArrayBufferSer.INT16, [1,4])
it('binary data is ok with arrays', function() {
var ba = new ArrayBufferSer(ArrayBufferSer.INT16, [1,2, 3, 4]);
var bc = new ArrayBufferSer(ArrayBufferSer.INT16, [1,4]);
var b = new ArrayBufferSer(ArrayBufferSer.BUFFER, [ba, bc])
var b = new ArrayBufferSer(ArrayBufferSer.BUFFER, [ba, bc]);
var bf = new Buffer([
0, 0, 0, ArrayBufferSer.BUFFER, // type
0, 0, 0, 28,
0, 0, 0, ArrayBufferSer.INT16, 0, 0, 0, 8, 1, 0, 2, 0, 3, 0, 4, 0,
0, 0, 0, ArrayBufferSer.INT16, 0, 0, 0, 4, 1, 0, 4, 0])
0, 0, 0, ArrayBufferSer.INT16, 0, 0, 0, 4, 1, 0, 4, 0]);
assert_buffer_equals(bf, b.buffer);
});
test('binary data is ok with strings', function() {
var s = 'test'
var b = new ArrayBufferSer(ArrayBufferSer.STRING, [s])
it('binary data is ok with strings', function() {
var s = 'test';
var b = new ArrayBufferSer(ArrayBufferSer.STRING, [s]);
var bf = new Buffer([
0, 0, 0, ArrayBufferSer.STRING, // type
0, 0, 0, 16,

View File

@ -1,26 +1,39 @@
require('../helper');
var _ = require('underscore')
, OAuthAuth = require('../../app/auth/oauth')
, MetadataDB = require('cartodb-redis')
, oAuth = require('../../app/auth/oauth').backend
, assert = require('assert')
, tests = module.exports = {}
, oauth_data_1 = {
var _ = require('underscore');
var OAuthAuth = require('../../app/auth/oauth');
var MetadataDB = require('cartodb-redis');
var oAuth = require('../../app/auth/oauth').backend;
var assert = require('assert');
var oauth_data_1 = {
oauth_consumer_key: "dpf43f3p2l4k3l03",
oauth_token: "nnch734d00sl2jdk",
oauth_signature_method: "HMAC-SHA1",
oauth_signature: "tR3%2BTy81lMeYAr%2FFid0kMTYa%2FWM%3D",
oauth_timestamp:"1191242096",
oauth_nonce:"kllo9940pd9333jh"
}
, oauth_data_2 = { oauth_version:"1.0" }
, oauth_data = _.extend(oauth_data_1, oauth_data_2)
, real_oauth_header = 'OAuth realm="http://vizzuality.testhost.lan/",oauth_consumer_key="fZeNGv5iYayvItgDYHUbot1Ukb5rVyX6QAg8GaY2",oauth_token="l0lPbtP68ao8NfStCiA3V3neqfM03JKhToxhUQTR",oauth_signature_method="HMAC-SHA1", oauth_signature="o4hx4hWP6KtLyFwggnYB4yPK8xI%3D",oauth_timestamp="1313581372",oauth_nonce="W0zUmvyC4eVL8cBd4YwlH1nnPTbxW0QBYcWkXTwe4",oauth_version="1.0"'
, oauth_header_tokens = 'oauth_consumer_key="dpf43f3p2l4k3l03",oauth_token="nnch734d00sl2jdk",oauth_signature_method="HMAC-SHA1", oauth_signature="tR3%2BTy81lMeYAr%2FFid0kMTYa%2FWM%3D",oauth_timestamp="1191242096",oauth_nonce="kllo9940pd9333jh",oauth_version="1.0"'
, full_oauth_header = 'OAuth realm="http://photos.example.net/"' + oauth_header_tokens;
};
var oauth_data_2 = { oauth_version:"1.0" };
var oauth_data = _.extend(oauth_data_1, oauth_data_2);
var real_oauth_header = 'OAuth ' +
'realm="http://vizzuality.testhost.lan/",' +
'oauth_consumer_key="fZeNGv5iYayvItgDYHUbot1Ukb5rVyX6QAg8GaY2",' +
'oauth_token="l0lPbtP68ao8NfStCiA3V3neqfM03JKhToxhUQTR",' +
'oauth_signature_method="HMAC-SHA1", ' +
'oauth_signature="o4hx4hWP6KtLyFwggnYB4yPK8xI%3D",' +
'oauth_timestamp="1313581372",' +
'oauth_nonce="W0zUmvyC4eVL8cBd4YwlH1nnPTbxW0QBYcWkXTwe4",' +
'oauth_version="1.0"';
var oauth_header_tokens = 'oauth_consumer_key="dpf43f3p2l4k3l03",' +
'oauth_token="nnch734d00sl2jdk",' +
'oauth_signature_method="HMAC-SHA1", ' +
'oauth_signature="tR3%2BTy81lMeYAr%2FFid0kMTYa%2FWM%3D",' +
'oauth_timestamp="1191242096",' +
'oauth_nonce="kllo9940pd9333jh",' +
'oauth_version="1.0"';
var full_oauth_header = 'OAuth realm="http://photos.example.net/"' + oauth_header_tokens;
var metadataBackend = MetadataDB({
var metadataBackend = new MetadataDB({
host: global.settings.redis_host,
port: global.settings.redis_port,
max: global.settings.redisPool,
@ -28,44 +41,43 @@ var metadataBackend = MetadataDB({
reapIntervalMillis: global.settings.redisReapIntervalMillis
});
suite('oauth', function() {
describe('oauth', function() {
test('test database number', function(){
it('test database number', function(){
assert.equal(oAuth.oauth_database, 3);
});
test('test oauth database key', function(){
it('test oauth database key', function(){
assert.equal(oAuth.oauth_user_key, "rails:oauth_access_tokens:<%= oauth_access_key %>");
});
test('test parse tokens from full headers does not raise exception', function(){
it('test parse tokens from full headers does not raise exception', function(){
var req = {query:{}, headers:{authorization:full_oauth_header}};
assert.doesNotThrow(function(){ oAuth.parseTokens(req) }, /incomplete oauth tokens in request/);
assert.doesNotThrow(function(){ oAuth.parseTokens(req); }, /incomplete oauth tokens in request/);
});
test('test parse all normal tokens raises no exception', function(){
it('test parse all normal tokens raises no exception', function(){
var req = {query:oauth_data, headers:{}};
assert.doesNotThrow(function(){ oAuth.parseTokens(req) }, /incomplete oauth tokens in request/);
assert.doesNotThrow(function(){ oAuth.parseTokens(req); }, /incomplete oauth tokens in request/);
});
test('test headers take presedence over query parameters', function(){
it('test headers take presedence over query parameters', function(){
var req = {query:{oauth_signature_method: "MY_HASH"}, headers:{authorization:full_oauth_header}};
var tokens = oAuth.parseTokens(req);
assert.equal(tokens.oauth_signature_method, "HMAC-SHA1");
});
test('test can access oauth hash for a user based on access token (oauth_token)', function(done){
it('test can access oauth hash for a user based on access token (oauth_token)', function(done){
var req = {query:{}, headers:{authorization:real_oauth_header}};
var tokens = oAuth.parseTokens(req);
oAuth.getOAuthHash(metadataBackend, tokens.oauth_token, function(err, data){
console.log(data);
assert.equal(tokens.oauth_consumer_key, data.consumer_key);
done();
});
});
test('test non existant oauth hash for a user based on oauth_token returns empty hash', function(done){
it('test non existant oauth hash for a user based on oauth_token returns empty hash', function(done){
var req = {query:{}, headers:{authorization:full_oauth_header}};
var tokens = oAuth.parseTokens(req);
@ -76,7 +88,7 @@ test('test non existant oauth hash for a user based on oauth_token returns empty
});
});
test('can return user for verified signature', function(done){
it('can return user for verified signature', function(done){
var req = {query:{},
headers:{authorization:real_oauth_header, host: 'vizzuality.testhost.lan' },
protocol: 'http',
@ -91,7 +103,7 @@ test('can return user for verified signature', function(done){
});
});
test('returns null user for unverified signatures', function(done){
it('returns null user for unverified signatures', function(done){
var req = {query:{},
headers:{authorization:real_oauth_header, host: 'vizzuality.testyhost.lan' },
protocol: 'http',
@ -105,7 +117,7 @@ test('returns null user for unverified signatures', function(done){
});
});
test('returns null user for no oauth', function(done){
it('returns null user for no oauth', function(done){
var req = {
query:{},
headers:{},
@ -120,14 +132,14 @@ test('returns null user for no oauth', function(done){
});
});
test('OAuthAuth reports it has credentials', function(done) {
it('OAuthAuth reports it has credentials', function(done) {
var req = {query:{}, headers:{authorization:real_oauth_header}};
var oAuthAuth = new OAuthAuth(req);
assert.ok(oAuthAuth.hasCredentials());
done();
});
test('OAuthAuth reports it has no credentials', function(done) {
it('OAuthAuth reports it has no credentials', function(done) {
var req = {query:{}, headers:{}};
var oAuthAuth = new OAuthAuth(req);
assert.equal(oAuthAuth.hasCredentials(), false);