From 5a96dbb59c938936a4ec78a796c47016faa34455 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa=20Aubert?= Date: Mon, 23 Dec 2019 18:19:08 +0100 Subject: [PATCH] Run eslint --fix --- app.js | 78 +- lib/api/health-check-controller.js | 2 +- lib/api/middlewares/access-validator.js | 2 +- lib/api/middlewares/authorization.js | 12 +- lib/api/middlewares/body-parser.js | 24 +- lib/api/middlewares/connection-params.js | 20 +- lib/api/middlewares/content.js | 4 +- lib/api/middlewares/cors.js | 2 +- lib/api/middlewares/db-quota.js | 2 +- lib/api/middlewares/error.js | 14 +- lib/api/middlewares/formatter.js | 2 +- lib/api/middlewares/last-modified.js | 2 +- lib/api/middlewares/log.js | 11 +- lib/api/middlewares/params.js | 38 +- lib/api/middlewares/rate-limit.js | 11 +- lib/api/middlewares/user.js | 4 +- lib/api/sql/copy-controller.js | 18 +- lib/api/sql/job-controller.js | 18 +- lib/api/sql/query-controller.js | 1 - lib/auth/apikey.js | 10 +- lib/auth/auth-api.js | 14 +- lib/auth/oauth.js | 271 ++-- lib/batch/batch.js | 15 +- lib/batch/index.js | 2 +- lib/batch/job-backend.js | 19 +- lib/batch/job-canceller.js | 13 +- lib/batch/job-queue.js | 25 +- lib/batch/job-runner.js | 10 +- lib/batch/job-service.js | 2 +- lib/batch/job-status.js | 2 +- lib/batch/leader/locker.js | 18 +- lib/batch/leader/provider/redis-distlock.js | 26 +- .../maintenance/host-user-queue-mover.js | 30 +- .../maintenance/remove-old-batch-jobs.js | 16 +- lib/batch/models/job-base.js | 4 +- lib/batch/models/job-factory.js | 4 +- lib/batch/models/job-fallback.js | 15 +- lib/batch/models/job-multiple.js | 3 +- lib/batch/models/job-simple.js | 2 +- lib/batch/models/job-state-machine.js | 2 +- lib/batch/models/query/fallback.js | 2 +- lib/batch/models/query/main-fallback.js | 2 +- lib/batch/models/query/query-base.js | 2 +- lib/batch/models/query/query-factory.js | 2 +- lib/batch/models/query/query-fallback.js | 2 +- lib/batch/models/query/query.js | 2 +- lib/batch/pubsub/job-publisher.js | 2 +- lib/batch/pubsub/job-subscriber.js | 4 +- lib/batch/query-runner.js | 2 +- lib/batch/scheduler/capacity/fixed.js | 4 +- lib/batch/scheduler/capacity/http-load.js | 6 +- lib/batch/scheduler/capacity/http-simple.js | 10 +- lib/batch/scheduler/host-scheduler.js | 16 +- lib/batch/scheduler/scheduler.js | 35 +- lib/batch/user-database-metadata-service.js | 2 +- lib/batch/util/forever.js | 4 +- lib/models/bin-encoder.js | 264 ++- lib/models/cartodb-request.js | 6 +- lib/models/formats/index.js | 8 +- lib/models/formats/ogr.js | 566 ++++--- lib/models/formats/ogr/csv.js | 8 +- lib/models/formats/ogr/geopackage.js | 8 +- lib/models/formats/ogr/kml.js | 8 +- lib/models/formats/ogr/shp.js | 198 ++- lib/models/formats/ogr/spatialite.js | 8 +- lib/models/formats/pg.js | 240 +-- lib/models/formats/pg/arraybuffer.js | 137 +- lib/models/formats/pg/geojson.js | 65 +- lib/models/formats/pg/json.js | 107 +- lib/models/formats/pg/svg.js | 50 +- lib/models/formats/pg/topojson.js | 40 +- lib/monitoring/health-check.js | 36 +- lib/postgresql/error-codes.js | 554 +++---- lib/server-options.js | 2 +- lib/server.js | 4 +- lib/services/error-handler-factory.js | 6 +- lib/services/error-handler.js | 7 +- lib/services/logger.js | 2 +- lib/services/pg-entities-access-validator.js | 10 +- lib/services/stream-copy-metrics.js | 12 +- lib/services/stream-copy.js | 12 +- lib/services/throttler-stream.js | 2 +- lib/services/user-database-service.js | 15 +- lib/services/user-limits.js | 6 +- lib/stats/client.js | 8 +- lib/stats/profiler-proxy.js | 24 +- lib/utils/content-disposition.js | 4 +- lib/utils/date-to-json.js | 6 +- lib/utils/filename-sanitizer.js | 8 +- lib/utils/logs.js | 8 +- lib/utils/md5.js | 2 +- lib/utils/query-info.js | 6 +- lib/utils/query-may-write.js | 4 +- test/acceptance/app-auth-test.js | 34 +- test/acceptance/app-configuration-test.js | 69 +- test/acceptance/app-test.js | 1420 ++++++++--------- test/acceptance/auth-api-test.js | 1 - test/acceptance/backend-crash-test.js | 130 +- test/acceptance/batch/batch-drain-test.js | 9 +- test/acceptance/batch/batch-limits-test.js | 14 +- .../acceptance/batch/batch-multiquery-test.js | 39 +- test/acceptance/batch/batch-test.js | 39 +- test/acceptance/batch/batch-wip-test.js | 13 +- .../batch/job-callback-template-test.js | 54 +- test/acceptance/batch/job-fallback-test.js | 467 +++--- test/acceptance/batch/job-query-limit-test.js | 36 +- test/acceptance/batch/job-query-order-test.js | 18 +- .../batch/job-query-timeout-test.js | 31 +- test/acceptance/batch/job-test.js | 118 +- test/acceptance/batch/job-timing-test.js | 37 +- .../batch/leader-job-query-order-test.js | 27 +- .../leader-multiple-users-query-order-test.js | 35 +- .../batch/queued-jobs-limit-test.js | 20 +- test/acceptance/batch/scheduler-basic-test.js | 29 +- test/acceptance/batch/use-cases-test.js | 37 +- test/acceptance/cache-headers-test.js | 14 +- test/acceptance/copy-abort-test.js | 12 +- test/acceptance/copy-endpoints-test.js | 257 ++- test/acceptance/copy-statements-test.js | 60 +- test/acceptance/copy-throttle-test.js | 15 +- test/acceptance/custom-middlewares-test.js | 6 +- test/acceptance/error-handler-test.js | 4 +- test/acceptance/export/arraybuffer-test.js | 63 +- test/acceptance/export/csv-test.js | 373 +++-- test/acceptance/export/folder-test.js | 8 +- test/acceptance/export/geojson-test.js | 387 ++--- test/acceptance/export/geopackage-test.js | 82 +- test/acceptance/export/kml-test.js | 676 ++++---- test/acceptance/export/shapefile-test.js | 742 +++++---- test/acceptance/export/spatialite-test.js | 32 +- test/acceptance/export/svg-test.js | 322 ++-- test/acceptance/export/timeout-test.js | 6 +- test/acceptance/export/topojson-test.js | 314 ++-- test/acceptance/frontend_abort-test.js | 126 +- test/acceptance/handle-query-test.js | 28 +- test/acceptance/health-check-test.js | 18 +- test/acceptance/last-modified-header-test.js | 24 +- test/acceptance/log-test.js | 56 +- test/acceptance/pagination-test.js | 217 ++- .../pg-entities-access-validator-test.js | 18 +- test/acceptance/pg-types-test.js | 3 +- test/acceptance/query-float-values-test.js | 2 +- test/acceptance/query-multipart-test.js | 12 +- test/acceptance/query-returning-test.js | 59 +- test/acceptance/rate-limit-test.js | 34 +- test/acceptance/regressions-test.js | 15 +- test/acceptance/skipfields-test.js | 74 +- test/acceptance/stream-responses-test.js | 30 +- test/acceptance/surrogate-key-test.js | 60 +- test/acceptance/system-queries-test.js | 25 +- test/acceptance/timeout-test.js | 59 +- test/acceptance/transaction-test.js | 14 +- test/acceptance/x-cache-channel-test.js | 44 +- test/integration/batch/job-backend-test.js | 11 +- test/integration/batch/job-canceller-test.js | 12 +- test/integration/batch/job-publisher-test.js | 4 +- test/integration/batch/job-queue-test.js | 12 +- test/integration/batch/job-runner-test.js | 10 +- test/integration/batch/job-service-test.js | 13 +- test/integration/batch/locker-test.js | 16 +- test/integration/batch/scheduler-test.js | 32 +- test/integration/stream-copy-test.js | 4 +- test/support/assert.js | 32 +- test/support/batch-test-client.js | 44 +- test/support/redis-utils.js | 12 +- test/support/test-client.js | 22 +- test/unit/apikeyauth-test.js | 50 +- test/unit/batch/job-publisher-test.js | 1 - test/unit/batch/job-queue-test.js | 4 +- test/unit/batch/job-subscriber-test.js | 7 +- test/unit/error-handler-factory-test.js | 6 +- test/unit/error-handler-test.js | 13 +- test/unit/health-check-test.js | 54 +- test/unit/model/bin-encoder-test.js | 111 +- test/unit/oauth-test.js | 256 +-- .../unit/pg-entities-access-validator-test.js | 8 +- test/unit/query-info-test.js | 28 +- test/websocket_test/public/gmaps_mercator.js | 188 ++- test/websocket_test/public/map.js | 138 +- test/websocket_test/public/test/map.test.js | 28 +- 180 files changed, 5603 insertions(+), 5797 deletions(-) diff --git a/app.js b/app.js index fe839904..a36179d8 100755 --- a/app.js +++ b/app.js @@ -36,9 +36,9 @@ const availableEnvironments = ['development', 'production', 'test', 'staging']; // sanity check arguments if (availableEnvironments.indexOf(ENVIRONMENT) === -1) { - console.error("node app.js [environment]"); - console.error("Available environments: " + availableEnvironments.join(', ')); - process.exit(1); + console.error('node app.js [environment]'); + console.error('Available environments: ' + availableEnvironments.join(', ')); + process.exit(1); } global.settings.api_hostname = fqdn.hostname(); @@ -53,23 +53,23 @@ if (global.settings.log_filename) { const logFilename = path.resolve(global.settings.log_filename); const logDirectory = path.dirname(logFilename); if (!fs.existsSync(logDirectory)) { - console.error("Log filename directory does not exist: " + logDirectory); + console.error('Log filename directory does not exist: ' + logDirectory); process.exit(1); } - console.log("Logs will be written to " + logFilename); + console.log('Logs will be written to ' + logFilename); log4jsConfig.appenders.push( - { type: "file", absolute: true, filename: logFilename } + { type: 'file', absolute: true, filename: logFilename } ); } else { log4jsConfig.appenders.push( - { type: "console", layout: { type:'basic' } } + { type: 'console', layout: { type: 'basic' } } ); } global.log4js.configure(log4jsConfig); global.logger = global.log4js.getLogger(); -const version = require("./package").version; +const version = require('./package').version; const StatsClient = require('./lib/stats/client'); @@ -85,21 +85,21 @@ const createServer = require('./lib/server'); const server = createServer(statsClient); const listener = server.listen(global.settings.node_port, global.settings.node_host); -listener.on('listening', function() { - console.info("Using Node.js %s", process.version); +listener.on('listening', function () { + console.info('Using Node.js %s', process.version); console.info('Using configuration file "%s"', configurationFile); console.log( - "CartoDB SQL API %s listening on %s:%s PID=%d (%s)", + 'CartoDB SQL API %s listening on %s:%s PID=%d (%s)', version, global.settings.node_host, global.settings.node_port, process.pid, ENVIRONMENT ); }); -process.on('uncaughtException', function(err) { +process.on('uncaughtException', function (err) { global.logger.error('Uncaught exception: ' + err.stack); }); -process.on('SIGHUP', function() { - global.log4js.clearAndShutdownAppenders(function() { +process.on('SIGHUP', function () { + global.log4js.clearAndShutdownAppenders(function () { global.log4js.configure(log4jsConfig); global.logger = global.log4js.getLogger(); console.log('Log files reloaded'); @@ -116,7 +116,7 @@ process.on('SIGHUP', function() { addHandlers({ killTimeout: 45000 }); -function addHandlers({ killTimeout }) { +function addHandlers ({ killTimeout }) { // FIXME: minimize the number of 'uncaughtException' before uncomment the following line // process.on('uncaughtException', exitProcess(listener, logger, killTimeout)); process.on('unhandledRejection', exitProcess({ killTimeout })); @@ -171,7 +171,7 @@ function scheduleForcedExit ({ killTimeout }) { killTimer.unref(); } -function isGteMinVersion(version, minVersion) { +function isGteMinVersion (version, minVersion) { const versionMatch = /[a-z]?([0-9]*)/.exec(version); if (versionMatch) { const majorVersion = parseInt(versionMatch[1], 10); @@ -183,7 +183,7 @@ function isGteMinVersion(version, minVersion) { } setInterval(function memoryUsageMetrics () { - let memoryUsage = process.memoryUsage(); + const memoryUsage = process.memoryUsage(); Object.keys(memoryUsage).forEach(property => { statsClient.gauge(`sqlapi.memory.${property}`, memoryUsage[property]); @@ -225,12 +225,12 @@ setInterval(function cpuUsageMetrics () { }, 5000); if (global.gc && isGteMinVersion(process.version, 6)) { - const gcInterval = Number.isFinite(global.settings.gc_interval) ? - global.settings.gc_interval : - 10000; + const gcInterval = Number.isFinite(global.settings.gc_interval) + ? global.settings.gc_interval + : 10000; if (gcInterval > 0) { - setInterval(function gcForcedCycle() { + setInterval(function gcForcedCycle () { global.gc(); }, gcInterval); } @@ -252,24 +252,24 @@ function getGCTypeValue (type) { let value; switch (type) { - case 1: - value = 'Scavenge'; - break; - case 2: - value = 'MarkSweepCompact'; - break; - case 4: - value = 'IncrementalMarking'; - break; - case 8: - value = 'ProcessWeakCallbacks'; - break; - case 15: - value = 'All'; - break; - default: - value = 'Unkown'; - break; + case 1: + value = 'Scavenge'; + break; + case 2: + value = 'MarkSweepCompact'; + break; + case 4: + value = 'IncrementalMarking'; + break; + case 8: + value = 'ProcessWeakCallbacks'; + break; + case 15: + value = 'All'; + break; + default: + value = 'Unkown'; + break; } return value; diff --git a/lib/api/health-check-controller.js b/lib/api/health-check-controller.js index 4137d087..8a4b82d8 100644 --- a/lib/api/health-check-controller.js +++ b/lib/api/health-check-controller.js @@ -17,7 +17,7 @@ function healthCheck ({ healthCheckBackend }) { const healthConfig = global.settings.health || {}; if (!healthConfig.enabled) { - return res.status(200).send({enabled: false, ok: true}); + return res.status(200).send({ enabled: false, ok: true }); } const startTime = Date.now(); diff --git a/lib/api/middlewares/access-validator.js b/lib/api/middlewares/access-validator.js index 70fd33bf..14262935 100644 --- a/lib/api/middlewares/access-validator.js +++ b/lib/api/middlewares/access-validator.js @@ -6,7 +6,7 @@ module.exports = function accessValidator () { return function accessValidatorMiddleware (req, res, next) { const { affectedTables, authorizationLevel } = res.locals; - if(!pgEntitiesAccessValidator.validate(affectedTables, authorizationLevel)) { + if (!pgEntitiesAccessValidator.validate(affectedTables, authorizationLevel)) { const error = new SyntaxError('system tables are forbidden'); error.http_status = 403; diff --git a/lib/api/middlewares/authorization.js b/lib/api/middlewares/authorization.js index 8a97928c..32ed985a 100644 --- a/lib/api/middlewares/authorization.js +++ b/lib/api/middlewares/authorization.js @@ -36,7 +36,7 @@ module.exports = function authorization (metadataBackend, forceToBeMaster = fals return next(new Error('permission denied')); } - res.set('vary', 'Authorization'); //Honor Authorization header when caching. + res.set('vary', 'Authorization'); // Honor Authorization header when caching. next(); }); @@ -46,7 +46,7 @@ module.exports = function authorization (metadataBackend, forceToBeMaster = fals const credentialsGetters = [ getCredentialsFromHeaderAuthorization, getCredentialsFromRequestQueryString, - getCredentialsFromRequestBody, + getCredentialsFromRequestBody ]; function getCredentialsFromRequest (req) { @@ -63,7 +63,7 @@ function getCredentialsFromRequest (req) { return credentials; } -function getCredentialsFromHeaderAuthorization(req) { +function getCredentialsFromHeaderAuthorization (req) { const { pass, name } = basicAuth(req) || {}; if (pass !== undefined && name !== undefined) { @@ -76,7 +76,7 @@ function getCredentialsFromHeaderAuthorization(req) { return false; } -function getCredentialsFromRequestQueryString(req) { +function getCredentialsFromRequestQueryString (req) { if (req.query.api_key) { return { apiKeyToken: req.query.api_key @@ -92,7 +92,7 @@ function getCredentialsFromRequestQueryString(req) { return false; } -function getCredentialsFromRequestBody(req) { +function getCredentialsFromRequestBody (req) { if (req.body && req.body.api_key) { return { apiKeyToken: req.body.api_key @@ -108,7 +108,7 @@ function getCredentialsFromRequestBody(req) { return false; } -function apiKeyTokenFound(credentials) { +function apiKeyTokenFound (credentials) { if (typeof credentials === 'boolean') { return credentials; } diff --git a/lib/api/middlewares/body-parser.js b/lib/api/middlewares/body-parser.js index f23c4512..68ba902b 100644 --- a/lib/api/middlewares/body-parser.js +++ b/lib/api/middlewares/body-parser.js @@ -23,7 +23,7 @@ var multer = require('multer'); * @api private */ -function mime(req) { +function mime (req) { var str = req.headers['content-type'] || ''; return str.split(';')[0]; } @@ -80,15 +80,15 @@ function mime(req) { * @api public */ -exports = module.exports = function bodyParser(options){ +exports = module.exports = function bodyParser (options) { options = options || {}; - return function bodyParser(req, res, next) { + return function bodyParser (req, res, next) { if (req.body) { return next(); } req.body = {}; - if ('GET' === req.method || 'HEAD' === req.method) { + if (req.method === 'GET' || req.method === 'HEAD') { return next(); } var parser = exports.parse[mime(req)]; @@ -110,15 +110,15 @@ exports.parse = {}; * Parse application/x-www-form-urlencoded. */ -exports.parse['application/x-www-form-urlencoded'] = function(req, options, fn){ +exports.parse['application/x-www-form-urlencoded'] = function (req, options, fn) { var buf = ''; req.setEncoding('utf8'); - req.on('data', function(chunk){ buf += chunk; }); - req.on('end', function(){ + req.on('data', function (chunk) { buf += chunk; }); + req.on('end', function () { try { req.body = buf.length ? qs.parse(buf) : {}; fn(); - } catch (err){ + } catch (err) { fn(err); } }); @@ -128,15 +128,15 @@ exports.parse['application/x-www-form-urlencoded'] = function(req, options, fn){ * Parse application/json. */ -exports.parse['application/json'] = function(req, options, fn){ +exports.parse['application/json'] = function (req, options, fn) { var buf = ''; req.setEncoding('utf8'); - req.on('data', function(chunk){ buf += chunk; }); - req.on('end', function(){ + req.on('data', function (chunk) { buf += chunk; }); + req.on('end', function () { try { req.body = buf.length ? JSON.parse(buf) : {}; fn(); - } catch (err){ + } catch (err) { fn(err); } }); diff --git a/lib/api/middlewares/connection-params.js b/lib/api/middlewares/connection-params.js index f0500552..1b8c7aa7 100644 --- a/lib/api/middlewares/connection-params.js +++ b/lib/api/middlewares/connection-params.js @@ -6,18 +6,18 @@ module.exports = function connectionParams (userDatabaseService) { userDatabaseService.getConnectionParams(user, apikeyToken, authorizationLevel, function (err, userDbParams, authDbParams) { - if (req.profiler) { - req.profiler.done('getConnectionParams'); - } + if (req.profiler) { + req.profiler.done('getConnectionParams'); + } - if (err) { - return next(err); - } + if (err) { + return next(err); + } - res.locals.userDbParams = userDbParams; - res.locals.authDbParams = authDbParams; + res.locals.userDbParams = userDbParams; + res.locals.authDbParams = authDbParams; - next(); - }); + next(); + }); }; }; diff --git a/lib/api/middlewares/content.js b/lib/api/middlewares/content.js index 7aeb7375..54d113fe 100644 --- a/lib/api/middlewares/content.js +++ b/lib/api/middlewares/content.js @@ -8,8 +8,8 @@ module.exports = function content () { const { formatter } = req; const useInline = !req.query.format && !req.body.format && !req.query.filename && !req.body.filename; - res.header("Content-Disposition", getContentDisposition(formatter, filename, useInline)); - res.header("Content-Type", formatter.getContentType()); + res.header('Content-Disposition', getContentDisposition(formatter, filename, useInline)); + res.header('Content-Type', formatter.getContentType()); next(); }; diff --git a/lib/api/middlewares/cors.js b/lib/api/middlewares/cors.js index a3c80853..c83375a3 100644 --- a/lib/api/middlewares/cors.js +++ b/lib/api/middlewares/cors.js @@ -1,6 +1,6 @@ 'use strict'; -module.exports = function cors(extraHeaders = []) { +module.exports = function cors (extraHeaders = []) { return function (req, res, next) { const headers = [ 'X-Requested-With', diff --git a/lib/api/middlewares/db-quota.js b/lib/api/middlewares/db-quota.js index 42fb8961..8b43632a 100644 --- a/lib/api/middlewares/db-quota.js +++ b/lib/api/middlewares/db-quota.js @@ -13,7 +13,7 @@ module.exports = function dbQuota () { return next(err); } client.query(remainingQuotaQuery, (err, result) => { - if(err) { + if (err) { return next(err); } const remainingQuota = result.rows[0].remaining_quota; diff --git a/lib/api/middlewares/error.js b/lib/api/middlewares/error.js index c63876b3..6ea75aea 100644 --- a/lib/api/middlewares/error.js +++ b/lib/api/middlewares/error.js @@ -4,10 +4,10 @@ const errorHandlerFactory = require('../../services/error-handler-factory'); const { stringifyForLogs } = require('../../utils/logs'); const MAX_ERROR_STRING_LENGTH = 1024; -module.exports = function error() { - return function errorMiddleware(err, req, res, next) { +module.exports = function error () { + return function errorMiddleware (err, req, res, next) { const errorHandler = errorHandlerFactory(err); - let errorResponse = errorHandler.getResponse(); + const errorResponse = errorHandler.getResponse(); if (global.settings.environment === 'development') { errorResponse.stack = err.stack; @@ -15,11 +15,11 @@ module.exports = function error() { if (global.settings.environment !== 'test') { // TODO: email this Exception report - console.error("EXCEPTION REPORT: " + err.stack); + console.error('EXCEPTION REPORT: ' + err.stack); } // Force inline content disposition - res.header("Content-Disposition", 'inline'); + res.header('Content-Disposition', 'inline'); if (req && req.profiler) { req.profiler.done('finish'); @@ -44,7 +44,7 @@ module.exports = function error() { }; }; -function getStatusError(errorHandler, req) { +function getStatusError (errorHandler, req) { let statusError = errorHandler.http_status; // JSONP has to return 200 status error @@ -55,7 +55,7 @@ function getStatusError(errorHandler, req) { return statusError; } -function setErrorHeader(errorHandler, res) { +function setErrorHeader (errorHandler, res) { const errorsLog = { context: errorHandler.context, detail: errorHandler.detail, diff --git a/lib/api/middlewares/formatter.js b/lib/api/middlewares/formatter.js index baee0347..d9b0e741 100644 --- a/lib/api/middlewares/formatter.js +++ b/lib/api/middlewares/formatter.js @@ -2,7 +2,7 @@ const formats = require('../../models/formats'); -module.exports = function formatter () { +module.exports = function formatter () { return function formatterMiddleware (req, res, next) { const { format } = res.locals.params; diff --git a/lib/api/middlewares/last-modified.js b/lib/api/middlewares/last-modified.js index d190e652..1142a27e 100644 --- a/lib/api/middlewares/last-modified.js +++ b/lib/api/middlewares/last-modified.js @@ -4,7 +4,7 @@ module.exports = function lastModified () { return function lastModifiedMiddleware (req, res, next) { const { affectedTables } = res.locals; - if(!!affectedTables) { + if (affectedTables) { const lastUpdatedAt = affectedTables.getLastUpdatedAt(Date.now()); res.header('Last-Modified', new Date(lastUpdatedAt).toUTCString()); } diff --git a/lib/api/middlewares/log.js b/lib/api/middlewares/log.js index e3faf869..5028c8b6 100644 --- a/lib/api/middlewares/log.js +++ b/lib/api/middlewares/log.js @@ -14,8 +14,8 @@ const TYPES = { JOB: 'job' }; -module.exports = function log(sqlType = TYPES.QUERY) { - return function logMiddleware(req, res, next) { +module.exports = function log (sqlType = TYPES.QUERY) { + return function logMiddleware (req, res, next) { const logObj = { request: { sql: prepareSQL(res.locals.params.sql, sqlType) @@ -30,12 +30,11 @@ module.exports = function log(sqlType = TYPES.QUERY) { module.exports.TYPES = TYPES; -function prepareSQL(sql, sqlType) { +function prepareSQL (sql, sqlType) { if (!sql || !global.settings.logQueries) { return null; } - if (typeof sql === 'string') { return { type: sqlType, @@ -65,7 +64,7 @@ function prepareSQL(sql, sqlType) { * * @param {Object} sql */ -function prepareBatchFallbackQuery(sql) { +function prepareBatchFallbackQuery (sql) { const fallbackQuery = {}; if (sql.onsuccess) { @@ -95,6 +94,6 @@ function prepareBatchFallbackQuery(sql) { return fallbackQuery; } -function ensureMaxQueryLength(sql, length = MAX_SQL_LENGTH) { +function ensureMaxQueryLength (sql, length = MAX_SQL_LENGTH) { return sql.substring(0, length); } diff --git a/lib/api/middlewares/params.js b/lib/api/middlewares/params.js index a241a17c..2304858a 100644 --- a/lib/api/middlewares/params.js +++ b/lib/api/middlewares/params.js @@ -22,20 +22,20 @@ function getParamsFromStrategy (strategy) { let fn; switch (strategy) { - case('query'): - fn = queryParamsStrategy; - break; - case('job'): - fn = jobParamsStrategy; - break; - case('copyfrom'): - fn = copyFromParamsStrategy; - break; - case('copyto'): - fn = copyToParamsStrategy; - break; - default: - throw new Error('Missig parameter strategy'); + case ('query'): + fn = queryParamsStrategy; + break; + case ('job'): + fn = jobParamsStrategy; + break; + case ('copyfrom'): + fn = copyFromParamsStrategy; + break; + case ('copyto'): + fn = copyToParamsStrategy; + break; + default: + throw new Error('Missig parameter strategy'); } return fn; @@ -52,7 +52,7 @@ function queryParamsStrategy (input) { params.format = parseFormat(input.format); - if (!formats.hasOwnProperty(params.format) ) { + if (!formats.hasOwnProperty(params.format)) { throw new Error(`Invalid format: ${params.format}`); } @@ -102,7 +102,7 @@ function copyToParamsStrategy (input) { throw new Error('SQL is missing'); } - if (!params.sql .toUpperCase().startsWith('COPY ')) { + if (!params.sql.toUpperCase().startsWith('COPY ')) { throw new Error('SQL must start with COPY'); } @@ -150,7 +150,7 @@ function parseFormat (inputFormat) { if (inputFormat === '' || inputFormat === undefined) { format = 'json'; - } else if (typeof inputFormat === 'string'){ + } else if (typeof inputFormat === 'string') { format = inputFormat.toLowerCase(); } @@ -166,12 +166,12 @@ function parseSkipFiles (inputSkippedFiles) { return skipfields; } - if (typeof inputSkippedFiles === 'string' ) { + if (typeof inputSkippedFiles === 'string') { skipfields = inputSkippedFiles.split(','); return skipfields; } - if (Array.isArray(inputSkippedFiles) ) { + if (Array.isArray(inputSkippedFiles)) { skipfields = []; inputSkippedFiles.forEach(e => { diff --git a/lib/api/middlewares/rate-limit.js b/lib/api/middlewares/rate-limit.js index 2a1cdae9..c2739d2f 100644 --- a/lib/api/middlewares/rate-limit.js +++ b/lib/api/middlewares/rate-limit.js @@ -9,14 +9,13 @@ const RATE_LIMIT_ENDPOINTS_GROUPS = { COPY_TO: 'copy_to' }; - -function rateLimit(userLimits, endpointGroup = null) { +function rateLimit (userLimits, endpointGroup = null) { if (!isRateLimitEnabled(endpointGroup)) { - return function rateLimitDisabledMiddleware(req, res, next) { next(); }; + return function rateLimitDisabledMiddleware (req, res, next) { next(); }; } - return function rateLimitMiddleware(req, res, next) { - userLimits.getRateLimit(res.locals.user, endpointGroup, function(err, userRateLimit) { + return function rateLimitMiddleware (req, res, next) { + userLimits.getRateLimit(res.locals.user, endpointGroup, function (err, userRateLimit) { if (err) { return next(err); } @@ -51,7 +50,7 @@ function rateLimit(userLimits, endpointGroup = null) { }; } -function isRateLimitEnabled(endpointGroup) { +function isRateLimitEnabled (endpointGroup) { return global.settings.ratelimits.rateLimitsEnabled && endpointGroup && global.settings.ratelimits.endpoints[endpointGroup]; diff --git a/lib/api/middlewares/user.js b/lib/api/middlewares/user.js index 6685ca8a..8fdc0d46 100644 --- a/lib/api/middlewares/user.js +++ b/lib/api/middlewares/user.js @@ -8,7 +8,7 @@ module.exports = function user (metadataBackend) { return function userMiddleware (req, res, next) { res.locals.user = getUserNameFromRequest(req, cdbRequest); - checkUserExists(metadataBackend, res.locals.user, function(err, userExists) { + checkUserExists(metadataBackend, res.locals.user, function (err, userExists) { if (err || !userExists) { const error = new Error('Unauthorized'); error.type = 'auth'; @@ -28,7 +28,7 @@ function getUserNameFromRequest (req, cdbRequest) { } function checkUserExists (metadataBackend, userName, callback) { - metadataBackend.getUserId(userName, function(err) { + metadataBackend.getUserId(userName, function (err) { callback(err, !err); }); } diff --git a/lib/api/sql/copy-controller.js b/lib/api/sql/copy-controller.js index 73494f3d..3af6fd6c 100644 --- a/lib/api/sql/copy-controller.js +++ b/lib/api/sql/copy-controller.js @@ -72,8 +72,8 @@ function handleCopyTo (logger) { const streamCopy = new StreamCopy(sql, userDbParams, logger); const metrics = new StreamCopyMetrics(logger, 'copyto', sql, user, isGzip); - res.header("Content-Disposition", `attachment; filename=${encodeURIComponent(filename)}`); - res.header("Content-Type", "application/octet-stream"); + res.header('Content-Disposition', `attachment; filename=${encodeURIComponent(filename)}`); + res.header('Content-Type', 'application/octet-stream'); streamCopy.getPGStream(StreamCopy.ACTION_TO, (err, pgstream) => { if (err) { @@ -88,7 +88,7 @@ function handleCopyTo (logger) { return next(err); }) .on('end', () => metrics.end(streamCopy.getRowCount())) - .pipe(res) + .pipe(res) .on('close', () => pgstream.emit('error', new Error('Connection closed by client'))) .on('error', err => pgstream.emit('error', err)); }); @@ -121,16 +121,16 @@ function handleCopyFrom (logger) { pgstream.emit('error', err); }) .on('close', () => pgstream.emit('error', new Error('Connection closed by client'))) - .pipe(throttle) - .pipe(decompress) + .pipe(throttle) + .pipe(decompress) .on('data', data => { metrics.addSize(data.length); - if(metrics.size > dbRemainingQuota) { + if (metrics.size > dbRemainingQuota) { return pgstream.emit('error', new Error('DB Quota exceeded')); } - if((metrics.gzipSize || metrics.size) > COPY_FROM_MAX_POST_SIZE) { + if ((metrics.gzipSize || metrics.size) > COPY_FROM_MAX_POST_SIZE) { return pgstream.emit('error', new Error( `COPY FROM maximum POST size of ${COPY_FROM_MAX_POST_SIZE_PRETTY} exceeded` )); @@ -141,7 +141,7 @@ function handleCopyFrom (logger) { metrics.end(null, err); pgstream.emit('error', err); }) - .pipe(pgstream) + .pipe(pgstream) .on('error', err => { metrics.end(null, err); @@ -153,7 +153,7 @@ function handleCopyFrom (logger) { const { time, rows } = metrics; if (!rows) { - return next(new Error("No rows copied")); + return next(new Error('No rows copied')); } res.send({ diff --git a/lib/api/sql/job-controller.js b/lib/api/sql/job-controller.js index bca7704f..a52801b2 100644 --- a/lib/api/sql/job-controller.js +++ b/lib/api/sql/job-controller.js @@ -143,7 +143,7 @@ function createJob (jobService) { } function checkBodyPayloadSize () { - return function checkBodyPayloadSizeMiddleware(req, res, next) { + return function checkBodyPayloadSizeMiddleware (req, res, next) { const payload = JSON.stringify(req.body); if (payload.length > MAX_LIMIT_QUERY_SIZE_IN_BYTES) { @@ -158,15 +158,15 @@ const ONE_KILOBYTE_IN_BYTES = 1024; const MAX_LIMIT_QUERY_SIZE_IN_KB = 16; const MAX_LIMIT_QUERY_SIZE_IN_BYTES = MAX_LIMIT_QUERY_SIZE_IN_KB * ONE_KILOBYTE_IN_BYTES; -function getMaxSizeErrorMessage(sql) { +function getMaxSizeErrorMessage (sql) { return util.format([ - 'Your payload is too large: %s bytes. Max size allowed is %s bytes (%skb).', - 'Are you trying to import data?.', - 'Please, check out import api http://docs.cartodb.com/cartodb-platform/import-api/' - ].join(' '), - sql.length, - MAX_LIMIT_QUERY_SIZE_IN_BYTES, - Math.round(MAX_LIMIT_QUERY_SIZE_IN_BYTES / ONE_KILOBYTE_IN_BYTES) + 'Your payload is too large: %s bytes. Max size allowed is %s bytes (%skb).', + 'Are you trying to import data?.', + 'Please, check out import api http://docs.cartodb.com/cartodb-platform/import-api/' + ].join(' '), + sql.length, + MAX_LIMIT_QUERY_SIZE_IN_BYTES, + Math.round(MAX_LIMIT_QUERY_SIZE_IN_BYTES / ONE_KILOBYTE_IN_BYTES) ); } diff --git a/lib/api/sql/query-controller.js b/lib/api/sql/query-controller.js index 070cead7..d1b6e9b1 100644 --- a/lib/api/sql/query-controller.js +++ b/lib/api/sql/query-controller.js @@ -121,7 +121,6 @@ function handleQuery ({ stats } = {}) { stats.increment('sqlapi.query.success'); } } - }); } catch (err) { next(err); diff --git a/lib/auth/apikey.js b/lib/auth/apikey.js index f1176cb6..867c2fc2 100644 --- a/lib/auth/apikey.js +++ b/lib/auth/apikey.js @@ -3,7 +3,7 @@ /** * this module allows to auth user using an pregenerated api key */ -function ApikeyAuth(req, metadataBackend, username, apikeyToken) { +function ApikeyAuth (req, metadataBackend, username, apikeyToken) { this.req = req; this.metadataBackend = metadataBackend; this.username = username; @@ -12,7 +12,7 @@ function ApikeyAuth(req, metadataBackend, username, apikeyToken) { module.exports = ApikeyAuth; -function usernameMatches(basicAuthUsername, requestUsername) { +function usernameMatches (basicAuthUsername, requestUsername) { return !(basicAuthUsername && (basicAuthUsername !== requestUsername)); } @@ -43,7 +43,7 @@ ApikeyAuth.prototype.verifyCredentials = function (callback) { } return callback(null, getAuthorizationLevel(apikey)); - } else { + } else { const apiKeyNotFoundError = new Error('Unauthorized'); apiKeyNotFoundError.type = 'auth'; apiKeyNotFoundError.subtype = 'api-key-not-found'; @@ -62,11 +62,11 @@ ApikeyAuth.prototype.getCredentials = function () { return this.apikeyToken; }; -function getAuthorizationLevel(apikey) { +function getAuthorizationLevel (apikey) { return apikey.type; } -function isApiKeyFound(apikey) { +function isApiKeyFound (apikey) { return apikey.type !== null && apikey.user !== null && apikey.databasePassword !== null && diff --git a/lib/auth/auth-api.js b/lib/auth/auth-api.js index fe6c3275..34c6384d 100644 --- a/lib/auth/auth-api.js +++ b/lib/auth/auth-api.js @@ -1,9 +1,9 @@ 'use strict'; -var ApiKeyAuth = require('./apikey'), - OAuthAuth = require('./oauth'); +var ApiKeyAuth = require('./apikey'); +var OAuthAuth = require('./oauth'); -function AuthApi(req, requestParams) { +function AuthApi (req, requestParams) { this.req = req; this.authBackend = getAuthBackend(req, requestParams); @@ -18,18 +18,18 @@ AuthApi.prototype.getType = function () { } }; -AuthApi.prototype.hasCredentials = function() { +AuthApi.prototype.hasCredentials = function () { if (this._hasCredentials === null) { this._hasCredentials = this.authBackend.hasCredentials(); } return this._hasCredentials; }; -AuthApi.prototype.getCredentials = function() { +AuthApi.prototype.getCredentials = function () { return this.authBackend.getCredentials(); }; -AuthApi.prototype.verifyCredentials = function(callback) { +AuthApi.prototype.verifyCredentials = function (callback) { if (this.hasCredentials()) { this.authBackend.verifyCredentials(callback); } else { @@ -37,7 +37,7 @@ AuthApi.prototype.verifyCredentials = function(callback) { } }; -function getAuthBackend(req, requestParams) { +function getAuthBackend (req, requestParams) { if (requestParams.api_key) { return new ApiKeyAuth(req, requestParams.metadataBackend, requestParams.user, requestParams.api_key); } else { diff --git a/lib/auth/oauth.js b/lib/auth/oauth.js index 812f5458..e7c64668 100644 --- a/lib/auth/oauth.js +++ b/lib/auth/oauth.js @@ -7,166 +7,166 @@ var step = require('step'); var CdbRequest = require('../models/cartodb-request'); var cdbReq = new CdbRequest(); -var oAuth = (function(){ - var me = { - oauth_database: 3, - oauth_user_key: "rails:oauth_access_tokens:<%= oauth_access_key %>", - is_oauth_request: true - }; +var oAuth = (function () { + var me = { + oauth_database: 3, + oauth_user_key: 'rails:oauth_access_tokens:<%= oauth_access_key %>', + is_oauth_request: true + }; - // oauth token cases: - // * in GET request - // * in header - me.parseTokens = function(req){ - var query_oauth = _.clone(req.method === "POST" ? req.body: req.query); - var header_oauth = {}; - var oauth_variables = ['oauth_body_hash', - 'oauth_consumer_key', - 'oauth_token', - 'oauth_signature_method', - 'oauth_signature', - 'oauth_timestamp', - 'oauth_nonce', - 'oauth_version']; + // oauth token cases: + // * in GET request + // * in header + me.parseTokens = function (req) { + var query_oauth = _.clone(req.method === 'POST' ? req.body : req.query); + var header_oauth = {}; + var oauth_variables = ['oauth_body_hash', + 'oauth_consumer_key', + 'oauth_token', + 'oauth_signature_method', + 'oauth_signature', + 'oauth_timestamp', + 'oauth_nonce', + 'oauth_version']; - // pull only oauth tokens out of query - var non_oauth = _.difference(_.keys(query_oauth), oauth_variables); - _.each(non_oauth, function(key){ delete query_oauth[key]; }); + // pull only oauth tokens out of query + var non_oauth = _.difference(_.keys(query_oauth), oauth_variables); + _.each(non_oauth, function (key) { delete query_oauth[key]; }); - // pull oauth tokens out of header - 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)) { - header_oauth[oauth_key] = decodeURIComponent(matched_string[1]); + // pull oauth tokens out of header + 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)) { + header_oauth[oauth_key] = decodeURIComponent(matched_string[1]); + } + }); } - }); - } - //merge header and query oauth tokens. preference given to header oauth - return _.defaults(header_oauth, query_oauth); - }; + // merge header and query oauth tokens. preference given to header oauth + return _.defaults(header_oauth, query_oauth); + }; - // remove oauthy tokens from an object - me.splitParams = function(obj) { - var removed = null; - for (var prop in obj) { - if (/^oauth_\w+$/.test(prop)) { - if(!removed) { - removed = {}; + // remove oauthy tokens from an object + me.splitParams = function (obj) { + var removed = null; + for (var prop in obj) { + if (/^oauth_\w+$/.test(prop)) { + if (!removed) { + removed = {}; + } + removed[prop] = obj[prop]; + delete obj[prop]; } - removed[prop] = obj[prop]; - delete obj[prop]; } - } - return removed; - }; + return removed; + }; - me.getAllowedHosts= function() { - var oauthConfig = global.settings.oauth || {}; - return oauthConfig.allowedHosts || ['carto.com', 'cartodb.com']; - }; + me.getAllowedHosts = function () { + var oauthConfig = global.settings.oauth || {}; + return oauthConfig.allowedHosts || ['carto.com', 'cartodb.com']; + }; - // do new fancy get User ID - me.verifyRequest = function(req, metadataBackend, callback) { - var that = this; - //TODO: review this - var httpProto = req.protocol; - if(!httpProto || (httpProto !== 'http' && httpProto !== 'https')) { - var msg = "Unknown HTTP protocol " + httpProto + "."; - var unknownProtocolErr = new Error(msg); - unknownProtocolErr.http_status = 500; - return callback(unknownProtocolErr); - } - - var username = cdbReq.userByReq(req); - var requestTokens; - var signature; - - step( - function getTokensFromURL(){ - return oAuth.parseTokens(req); - }, - function getOAuthHash(err, _requestTokens) { - if (err) { - throw err; + // do new fancy get User ID + me.verifyRequest = function (req, metadataBackend, callback) { + var that = this; + // TODO: review this + var httpProto = req.protocol; + if (!httpProto || (httpProto !== 'http' && httpProto !== 'https')) { + var msg = 'Unknown HTTP protocol ' + httpProto + '.'; + var unknownProtocolErr = new Error(msg); + unknownProtocolErr.http_status = 500; + return callback(unknownProtocolErr); } - // this is oauth request only if oauth headers are present - this.is_oauth_request = !_.isEmpty(_requestTokens); + var username = cdbReq.userByReq(req); + var requestTokens; + var signature; - if (this.is_oauth_request) { - requestTokens = _requestTokens; - that.getOAuthHash(metadataBackend, requestTokens.oauth_token, this); - } else { - return null; - } - }, - function regenerateSignature(err, oAuthHash){ - if (err) { - throw err; - } - if (!this.is_oauth_request) { - return null; - } + step( + function getTokensFromURL () { + return oAuth.parseTokens(req); + }, + function getOAuthHash (err, _requestTokens) { + if (err) { + throw err; + } - var consumer = OAuthUtil.createConsumer(oAuthHash.consumer_key, oAuthHash.consumer_secret); - var access_token = OAuthUtil.createToken(oAuthHash.access_token_token, oAuthHash.access_token_secret); - var signer = OAuthUtil.createHmac(consumer, access_token); + // this is oauth request only if oauth headers are present + this.is_oauth_request = !_.isEmpty(_requestTokens); - var method = req.method; - var hostsToValidate = {}; - var requestHost = req.headers.host; - hostsToValidate[requestHost] = true; - that.getAllowedHosts().forEach(function(allowedHost) { - hostsToValidate[username + '.' + allowedHost] = true; - }); + if (this.is_oauth_request) { + requestTokens = _requestTokens; + that.getOAuthHash(metadataBackend, requestTokens.oauth_token, this); + } else { + return null; + } + }, + function regenerateSignature (err, oAuthHash) { + if (err) { + throw err; + } + if (!this.is_oauth_request) { + return null; + } - that.splitParams(req.query); - // remove oauth_signature from body - if(req.body) { - delete req.body.oauth_signature; - } - signature = requestTokens.oauth_signature; - // remove signature from requestTokens - delete requestTokens.oauth_signature; - var requestParams = _.extend({}, req.body, requestTokens, req.query); + var consumer = OAuthUtil.createConsumer(oAuthHash.consumer_key, oAuthHash.consumer_secret); + var access_token = OAuthUtil.createToken(oAuthHash.access_token_token, oAuthHash.access_token_secret); + var signer = OAuthUtil.createHmac(consumer, access_token); - var hosts = Object.keys(hostsToValidate); - var requestSignatures = hosts.map(function(host) { - var url = httpProto + '://' + host + req.path; - return signer.sign(method, url, requestParams); - }); + var method = req.method; + var hostsToValidate = {}; + var requestHost = req.headers.host; + hostsToValidate[requestHost] = true; + that.getAllowedHosts().forEach(function (allowedHost) { + hostsToValidate[username + '.' + allowedHost] = true; + }); - return requestSignatures.reduce(function(validSignature, requestSignature) { - if (signature === requestSignature && !_.isUndefined(requestSignature)) { - validSignature = true; + that.splitParams(req.query); + // remove oauth_signature from body + if (req.body) { + delete req.body.oauth_signature; + } + signature = requestTokens.oauth_signature; + // remove signature from requestTokens + delete requestTokens.oauth_signature; + var requestParams = _.extend({}, req.body, requestTokens, req.query); + + var hosts = Object.keys(hostsToValidate); + var requestSignatures = hosts.map(function (host) { + var url = httpProto + '://' + host + req.path; + return signer.sign(method, url, requestParams); + }); + + return requestSignatures.reduce(function (validSignature, requestSignature) { + if (signature === requestSignature && !_.isUndefined(requestSignature)) { + validSignature = true; + } + return validSignature; + }, false); + }, + function finishValidation (err, hasValidSignature) { + const authorizationLevel = hasValidSignature ? 'master' : null; + return callback(err, authorizationLevel); } - return validSignature; - }, false); - }, - function finishValidation(err, hasValidSignature) { - const authorizationLevel = hasValidSignature ? 'master' : null; - return callback(err, authorizationLevel); - } - ); - }; + ); + }; - me.getOAuthHash = function(metadataBackend, oAuthAccessKey, callback){ - metadataBackend.getOAuthHash(oAuthAccessKey, callback); - }; + me.getOAuthHash = function (metadataBackend, oAuthAccessKey, callback) { + metadataBackend.getOAuthHash(oAuthAccessKey, callback); + }; - return me; + return me; })(); -function OAuthAuth(req, metadataBackend) { +function OAuthAuth (req, metadataBackend) { this.req = req; this.metadataBackend = metadataBackend; this.isOAuthRequest = null; } -OAuthAuth.prototype.verifyCredentials = function(callback) { +OAuthAuth.prototype.verifyCredentials = function (callback) { if (this.hasCredentials()) { oAuth.verifyRequest(this.req, this.metadataBackend, callback); } else { @@ -174,11 +174,11 @@ OAuthAuth.prototype.verifyCredentials = function(callback) { } }; -OAuthAuth.prototype.getCredentials = function() { - return oAuth.parseTokens(this.req); +OAuthAuth.prototype.getCredentials = function () { + return oAuth.parseTokens(this.req); }; -OAuthAuth.prototype.hasCredentials = function() { +OAuthAuth.prototype.hasCredentials = function () { if (this.isOAuthRequest === null) { var passed_tokens = oAuth.parseTokens(this.req); this.isOAuthRequest = !_.isEmpty(passed_tokens); @@ -187,6 +187,5 @@ OAuthAuth.prototype.hasCredentials = function() { return this.isOAuthRequest; }; - module.exports = OAuthAuth; module.exports.backend = oAuth; diff --git a/lib/batch/batch.js b/lib/batch/batch.js index 99e5f718..3e99695c 100644 --- a/lib/batch/batch.js +++ b/lib/batch/batch.js @@ -10,7 +10,7 @@ var EMPTY_QUEUE = true; var MINUTE = 60 * 1000; var SCHEDULE_INTERVAL = 1 * MINUTE; -function Batch(name, userDatabaseMetadataService, jobSubscriber, jobQueue, jobRunner, jobService, redisPool, logger) { +function Batch (name, userDatabaseMetadataService, jobSubscriber, jobQueue, jobRunner, jobService, redisPool, logger) { EventEmitter.call(this); this.name = name || 'batch'; this.userDatabaseMetadataService = userDatabaseMetadataService; @@ -52,7 +52,7 @@ Batch.prototype.start = function () { }; function createJobHandler (name, userDatabaseMetadataService, hostScheduler, logger) { - return function onJobHandler(user) { + return function onJobHandler (user) { userDatabaseMetadataService.getUserMetadata(user, function (err, userDatabaseMetadata) { if (err) { return logger.debug('Could not get host user=%s from %s. Reason: %s', user, name, err.message); @@ -61,7 +61,7 @@ function createJobHandler (name, userDatabaseMetadataService, hostScheduler, log var host = userDatabaseMetadata.host; logger.debug('[%s] onJobHandler(%s, %s)', name, user, host); - hostScheduler.add(host, user, function(err) { + hostScheduler.add(host, user, function (err) { if (err) { return logger.debug( 'Could not schedule host=%s user=%s from %s. Reason: %s', host, user, name, err.message @@ -206,23 +206,22 @@ Batch.prototype.stop = function (callback) { this.jobSubscriber.unsubscribe(callback); }; - /* Work in progress jobs */ -Batch.prototype.setWorkInProgressJob = function(user, jobId, callback) { +Batch.prototype.setWorkInProgressJob = function (user, jobId, callback) { this.workInProgressJobs[user] = jobId; this.jobService.addWorkInProgressJob(user, jobId, callback); }; -Batch.prototype.getWorkInProgressJob = function(user) { +Batch.prototype.getWorkInProgressJob = function (user) { return this.workInProgressJobs[user]; }; -Batch.prototype.clearWorkInProgressJob = function(user, jobId, callback) { +Batch.prototype.clearWorkInProgressJob = function (user, jobId, callback) { delete this.workInProgressJobs[user]; this.jobService.clearWorkInProgressJob(user, jobId, callback); }; -Batch.prototype.getWorkInProgressUsers = function() { +Batch.prototype.getWorkInProgressUsers = function () { return Object.keys(this.workInProgressJobs); }; diff --git a/lib/batch/index.js b/lib/batch/index.js index 9268612c..8e35b687 100644 --- a/lib/batch/index.js +++ b/lib/batch/index.js @@ -20,7 +20,7 @@ module.exports = function batchFactory (metadataBackend, redisPool, name, statsd var jobSubscriber = new JobSubscriber(redisPool); var jobPublisher = new JobPublisher(redisPool); - var jobQueue = new JobQueue(metadataBackend, jobPublisher, logger); + var jobQueue = new JobQueue(metadataBackend, jobPublisher, logger); var jobBackend = new JobBackend(metadataBackend, jobQueue, logger); var queryRunner = new QueryRunner(userDatabaseMetadataService, logger); var jobCanceller = new JobCanceller(); diff --git a/lib/batch/job-backend.js b/lib/batch/job-backend.js index ea079736..6c914aa0 100644 --- a/lib/batch/job-backend.js +++ b/lib/batch/job-backend.js @@ -5,7 +5,7 @@ var REDIS_DB = 5; var JobStatus = require('./job-status'); var queue = require('queue-async'); -function JobBackend(metadataBackend, jobQueue, logger) { +function JobBackend (metadataBackend, jobQueue, logger) { this.metadataBackend = metadataBackend; this.jobQueue = jobQueue; this.maxNumberOfQueuedJobs = global.settings.batch_max_queued_jobs || 64; @@ -14,7 +14,7 @@ function JobBackend(metadataBackend, jobQueue, logger) { this.logger = logger; } -function toRedisParams(job) { +function toRedisParams (job) { var redisParams = [REDIS_PREFIX + job.job_id]; var obj = JSON.parse(JSON.stringify(job)); delete obj.job_id; @@ -33,7 +33,7 @@ function toRedisParams(job) { return redisParams; } -function toObject(job_id, redisParams, redisValues) { +function toObject (job_id, redisParams, redisValues) { var obj = {}; redisParams.shift(); // job_id value @@ -57,11 +57,11 @@ function toObject(job_id, redisParams, redisValues) { return obj; } -function isJobFound(redisValues) { +function isJobFound (redisValues) { return !!(redisValues[0] && redisValues[1] && redisValues[2] && redisValues[3] && redisValues[4]); } -function getNotFoundError(job_id) { +function getNotFoundError (job_id) { var notFoundError = new Error('Job with id ' + job_id + ' not found'); notFoundError.name = 'NotFoundError'; return notFoundError; @@ -90,7 +90,7 @@ JobBackend.prototype.get = function (job_id, callback) { 'dbuser' ]; - self.metadataBackend.redisCmd(REDIS_DB, 'HMGET', redisParams , function (err, redisValues) { + self.metadataBackend.redisCmd(REDIS_DB, 'HMGET', redisParams, function (err, redisValues) { if (err) { return callback(err); } @@ -108,7 +108,7 @@ JobBackend.prototype.get = function (job_id, callback) { JobBackend.prototype.create = function (job, callback) { var self = this; - this.jobQueue.size(job.user, function(err, size) { + this.jobQueue.size(job.user, function (err, size) { if (err) { return callback(new Error('Failed to create job, could not determine user queue size')); } @@ -146,7 +146,6 @@ JobBackend.prototype.update = function (job, callback) { var self = this; self.get(job.job_id, function (err) { - if (err) { return callback(err); } @@ -159,7 +158,7 @@ JobBackend.prototype.save = function (job, callback) { var self = this; var redisParams = toRedisParams(job); - self.metadataBackend.redisCmd(REDIS_DB, 'HMSET', redisParams , function (err) { + self.metadataBackend.redisCmd(REDIS_DB, 'HMSET', redisParams, function (err) { if (err) { return callback(err); } @@ -284,7 +283,7 @@ JobBackend.prototype.setTTL = function (job, callback) { return callback(); } - self.metadataBackend.redisCmd(REDIS_DB, 'EXPIRE', [ redisKey, this.inSecondsJobTTLAfterFinished ], callback); + self.metadataBackend.redisCmd(REDIS_DB, 'EXPIRE', [redisKey, this.inSecondsJobTTLAfterFinished], callback); }; module.exports = JobBackend; diff --git a/lib/batch/job-canceller.js b/lib/batch/job-canceller.js index ca7abd6c..dc00fdb8 100644 --- a/lib/batch/job-canceller.js +++ b/lib/batch/job-canceller.js @@ -2,25 +2,24 @@ var PSQL = require('cartodb-psql'); -function JobCanceller() { +function JobCanceller () { } module.exports = JobCanceller; JobCanceller.prototype.cancel = function (job, callback) { - const dbConfiguration = { host: job.data.host, port: job.data.port, dbname: job.data.dbname, user: job.data.dbuser, - pass: job.data.pass, + pass: job.data.pass }; doCancel(job.data.job_id, dbConfiguration, callback); }; -function doCancel(job_id, dbConfiguration, callback) { +function doCancel (job_id, dbConfiguration, callback) { var pg = new PSQL(dbConfiguration); getQueryPID(pg, job_id, function (err, pid) { @@ -46,10 +45,10 @@ function doCancel(job_id, dbConfiguration, callback) { }); } -function getQueryPID(pg, job_id, callback) { +function getQueryPID (pg, job_id, callback) { var getPIDQuery = "SELECT pid FROM pg_stat_activity WHERE query LIKE '/* " + job_id + " */%'"; - pg.query(getPIDQuery, function(err, result) { + pg.query(getPIDQuery, function (err, result) { if (err) { return callback(err); } @@ -63,7 +62,7 @@ function getQueryPID(pg, job_id, callback) { }); } -function doCancelQuery(pg, pid, callback) { +function doCancelQuery (pg, pid, callback) { var cancelQuery = 'SELECT pg_cancel_backend(' + pid + ')'; pg.query(cancelQuery, function (err, result) { diff --git a/lib/batch/job-queue.js b/lib/batch/job-queue.js index 4fce962d..5c844ceb 100644 --- a/lib/batch/job-queue.js +++ b/lib/batch/job-queue.js @@ -2,7 +2,7 @@ var queueAsync = require('queue-async'); -function JobQueue(metadataBackend, jobPublisher, logger) { +function JobQueue (metadataBackend, jobPublisher, logger) { this.metadataBackend = metadataBackend; this.jobPublisher = jobPublisher; this.logger = logger; @@ -22,8 +22,8 @@ JobQueue.prototype.enqueue = function (user, jobId, callback) { this.logger.debug('JobQueue.enqueue user=%s, jobId=%s', user, jobId); this.metadataBackend.redisMultiCmd(QUEUE.DB, [ - [ 'LPUSH', QUEUE.PREFIX + user, jobId ], - [ 'SADD', QUEUE.INDEX, user ] + ['LPUSH', QUEUE.PREFIX + user, jobId], + ['SADD', QUEUE.INDEX, user] ], function (err) { if (err) { return callback(err); @@ -35,7 +35,7 @@ JobQueue.prototype.enqueue = function (user, jobId, callback) { }; JobQueue.prototype.size = function (user, callback) { - this.metadataBackend.redisCmd(QUEUE.DB, 'LLEN', [ QUEUE.PREFIX + user ], callback); + this.metadataBackend.redisCmd(QUEUE.DB, 'LLEN', [QUEUE.PREFIX + user], callback); }; JobQueue.prototype.dequeue = function (user, callback) { @@ -49,10 +49,10 @@ JobQueue.prototype.dequeue = function (user, callback) { ].join('\n'); var redisParams = [ - dequeueScript, //lua source code + dequeueScript, // lua source code 2, // Two "keys" to pass - QUEUE.PREFIX + user, //KEYS[1], the key of the queue - QUEUE.INDEX, //KEYS[2], the key of the index + QUEUE.PREFIX + user, // KEYS[1], the key of the queue + QUEUE.INDEX, // KEYS[2], the key of the index user // ARGV[1] - value of the element to remove from the index ]; @@ -65,8 +65,8 @@ JobQueue.prototype.dequeue = function (user, callback) { JobQueue.prototype.enqueueFirst = function (user, jobId, callback) { this.logger.debug('JobQueue.enqueueFirst user=%s, jobId=%s', user, jobId); this.metadataBackend.redisMultiCmd(QUEUE.DB, [ - [ 'RPUSH', QUEUE.PREFIX + user, jobId ], - [ 'SADD', QUEUE.INDEX, user ] + ['RPUSH', QUEUE.PREFIX + user, jobId], + ['SADD', QUEUE.INDEX, user] ], function (err) { if (err) { return callback(err); @@ -77,9 +77,8 @@ JobQueue.prototype.enqueueFirst = function (user, jobId, callback) { }.bind(this)); }; - JobQueue.prototype.getQueues = function (callback) { - this.metadataBackend.redisCmd(QUEUE.DB, 'SMEMBERS', [ QUEUE.INDEX ], function (err, queues) { + this.metadataBackend.redisCmd(QUEUE.DB, 'SMEMBERS', [QUEUE.INDEX], function (err, queues) { if (err) { return callback(err); } @@ -112,7 +111,7 @@ JobQueue.prototype.scan = function (callback) { var initialCursor = ['0']; var users = {}; - self._scan(initialCursor, users, function(err, users) { + self._scan(initialCursor, users, function (err, users) { if (err) { return callback(err); } @@ -153,7 +152,7 @@ JobQueue.prototype.addToQueueIndex = function (users, callback) { users.forEach(function (user) { usersQueues.defer(function (user, callback) { - self.metadataBackend.redisCmd(QUEUE.DB, 'SADD', [ QUEUE.INDEX, user], callback); + self.metadataBackend.redisCmd(QUEUE.DB, 'SADD', [QUEUE.INDEX, user], callback); }, user); }); diff --git a/lib/batch/job-runner.js b/lib/batch/job-runner.js index 86610808..79dff67f 100644 --- a/lib/batch/job-runner.js +++ b/lib/batch/job-runner.js @@ -10,7 +10,7 @@ var REDIS_LIMITS = { PREFIX: 'limits:batch:' // + username }; -function JobRunner(jobService, jobQueue, queryRunner, metadataBackend, statsdClient) { +function JobRunner (jobService, jobQueue, queryRunner, metadataBackend, statsdClient) { this.jobService = jobService; this.jobQueue = jobQueue; this.queryRunner = queryRunner; @@ -29,7 +29,7 @@ JobRunner.prototype.run = function (job_id, callback) { return callback(err); } - self.getQueryStatementTimeout(job.data.user, function(err, timeout) { + self.getQueryStatementTimeout(job.data.user, function (err, timeout) { if (err) { return callback(err); } @@ -62,14 +62,14 @@ JobRunner.prototype.run = function (job_id, callback) { }); }; -JobRunner.prototype.getQueryStatementTimeout = function(username, callback) { +JobRunner.prototype.getQueryStatementTimeout = function (username, callback) { var timeout = 12 * 3600 * 1000; if (Number.isFinite(global.settings.batch_query_timeout)) { timeout = global.settings.batch_query_timeout; } var batchLimitsKey = REDIS_LIMITS.PREFIX + username; - this.metadataBackend.redisCmd(REDIS_LIMITS.DB, 'HGET', [batchLimitsKey, 'timeout'], function(err, timeoutLimit) { + this.metadataBackend.redisCmd(REDIS_LIMITS.DB, 'HGET', [batchLimitsKey, 'timeout'], function (err, timeoutLimit) { if (timeoutLimit !== null && Number.isFinite(+timeoutLimit)) { timeout = +timeoutLimit; } @@ -137,7 +137,7 @@ JobRunner.prototype._run = function (job, query, timeout, profiler, callback) { }); }; -function cancelledByUser(err) { +function cancelledByUser (err) { return errorCodes[err.code.toString()] === 'query_canceled' && err.message.match(/user.*request/); } diff --git a/lib/batch/job-service.js b/lib/batch/job-service.js index c4ad55ae..ff64b039 100644 --- a/lib/batch/job-service.js +++ b/lib/batch/job-service.js @@ -3,7 +3,7 @@ var JobFactory = require('./models/job-factory'); var jobStatus = require('./job-status'); -function JobService(jobBackend, jobCanceller, logger) { +function JobService (jobBackend, jobCanceller, logger) { this.jobBackend = jobBackend; this.jobCanceller = jobCanceller; this.logger = logger; diff --git a/lib/batch/job-status.js b/lib/batch/job-status.js index ef5ad171..fd2e10b4 100644 --- a/lib/batch/job-status.js +++ b/lib/batch/job-status.js @@ -18,6 +18,6 @@ var finalStatus = [ JOB_STATUS_ENUM.FAILED, JOB_STATUS_ENUM.UNKNOWN ]; -module.exports.isFinal = function(status) { +module.exports.isFinal = function (status) { return finalStatus.indexOf(status) !== -1; }; diff --git a/lib/batch/leader/locker.js b/lib/batch/leader/locker.js index 463e74ce..d707533e 100644 --- a/lib/batch/leader/locker.js +++ b/lib/batch/leader/locker.js @@ -9,7 +9,7 @@ var LOCK = { TTL: 5000 }; -function Locker(locker, ttl) { +function Locker (locker, ttl) { EventEmitter.call(this); this.locker = locker; this.ttl = (Number.isFinite(ttl) && ttl > 0) ? ttl : LOCK.TTL; @@ -20,7 +20,7 @@ util.inherits(Locker, EventEmitter); module.exports = Locker; -Locker.prototype.lock = function(resource, callback) { +Locker.prototype.lock = function (resource, callback) { var self = this; debug('Locker.lock(%s, %d)', resource, this.ttl); this.locker.lock(resource, this.ttl, function (err, lock) { @@ -31,21 +31,21 @@ Locker.prototype.lock = function(resource, callback) { }); }; -Locker.prototype.unlock = function(resource, callback) { +Locker.prototype.unlock = function (resource, callback) { var self = this; debug('Locker.unlock(%s)', resource); - this.locker.unlock(resource, function(err) { + this.locker.unlock(resource, function (err) { self.stopRenewal(resource); return callback(err); }); }; -Locker.prototype.startRenewal = function(resource) { +Locker.prototype.startRenewal = function (resource) { var self = this; if (!this.intervalIds.hasOwnProperty(resource)) { - this.intervalIds[resource] = setInterval(function() { + this.intervalIds[resource] = setInterval(function () { debug('Trying to extend lock resource=%s', resource); - self.locker.lock(resource, self.ttl, function(err, _lock) { + self.locker.lock(resource, self.ttl, function (err, _lock) { if (err) { self.emit('error', err, resource); return self.stopRenewal(resource); @@ -58,14 +58,14 @@ Locker.prototype.startRenewal = function(resource) { } }; -Locker.prototype.stopRenewal = function(resource) { +Locker.prototype.stopRenewal = function (resource) { if (this.intervalIds.hasOwnProperty(resource)) { clearInterval(this.intervalIds[resource]); delete this.intervalIds[resource]; } }; -module.exports.create = function createLocker(type, config) { +module.exports.create = function createLocker (type, config) { if (type !== 'redis-distlock') { throw new Error('Invalid type Locker type. Valid types are: "redis-distlock"'); } diff --git a/lib/batch/leader/provider/redis-distlock.js b/lib/batch/leader/provider/redis-distlock.js index 01e944f7..3758c7d1 100644 --- a/lib/batch/leader/provider/redis-distlock.js +++ b/lib/batch/leader/provider/redis-distlock.js @@ -8,7 +8,7 @@ var REDIS_DISTLOCK = { var Redlock = require('redlock'); var debug = require('../../util/debug')('leader:redis-distlock'); -function RedisDistlockLocker(redisPool) { +function RedisDistlockLocker (redisPool) { this.pool = redisPool; this.redlock = new Redlock([{}], { // see http://redis.io/topics/distlock @@ -24,17 +24,17 @@ function RedisDistlockLocker(redisPool) { module.exports = RedisDistlockLocker; module.exports.type = 'redis-distlock'; -function resourceId(resource) { +function resourceId (resource) { return REDIS_DISTLOCK.PREFIX + resource; } -RedisDistlockLocker.prototype.lock = function(resource, ttl, callback) { +RedisDistlockLocker.prototype.lock = function (resource, ttl, callback) { var self = this; debug('RedisDistlockLocker.lock(%s, %d)', resource, ttl); var lockId = resourceId(resource); var lock = this._getLock(lockId); - function acquireCallback(err, _lock) { + function acquireCallback (err, _lock) { if (err) { return callback(err); } @@ -42,7 +42,7 @@ RedisDistlockLocker.prototype.lock = function(resource, ttl, callback) { return callback(null, _lock); } if (lock) { - return this._tryExtend(lock, ttl, function(err, _lock) { + return this._tryExtend(lock, ttl, function (err, _lock) { if (err) { return self._tryAcquire(lockId, ttl, acquireCallback); } @@ -54,7 +54,7 @@ RedisDistlockLocker.prototype.lock = function(resource, ttl, callback) { } }; -RedisDistlockLocker.prototype.unlock = function(resource, callback) { +RedisDistlockLocker.prototype.unlock = function (resource, callback) { var self = this; var lock = this._getLock(resourceId(resource)); if (lock) { @@ -63,7 +63,7 @@ RedisDistlockLocker.prototype.unlock = function(resource, callback) { return callback(err); } self.redlock.servers = [client]; - return self.redlock.unlock(lock, function(err) { + return self.redlock.unlock(lock, function (err) { self.pool.release(REDIS_DISTLOCK.DB, client); return callback(err); }); @@ -71,39 +71,39 @@ RedisDistlockLocker.prototype.unlock = function(resource, callback) { } }; -RedisDistlockLocker.prototype._getLock = function(resource) { +RedisDistlockLocker.prototype._getLock = function (resource) { if (this._locks.hasOwnProperty(resource)) { return this._locks[resource]; } return null; }; -RedisDistlockLocker.prototype._setLock = function(resource, lock) { +RedisDistlockLocker.prototype._setLock = function (resource, lock) { this._locks[resource] = lock; }; -RedisDistlockLocker.prototype._tryExtend = function(lock, ttl, callback) { +RedisDistlockLocker.prototype._tryExtend = function (lock, ttl, callback) { var self = this; this.pool.acquire(REDIS_DISTLOCK.DB, function (err, client) { if (err) { return callback(err); } self.redlock.servers = [client]; - return lock.extend(ttl, function(err, _lock) { + return lock.extend(ttl, function (err, _lock) { self.pool.release(REDIS_DISTLOCK.DB, client); return callback(err, _lock); }); }); }; -RedisDistlockLocker.prototype._tryAcquire = function(resource, ttl, callback) { +RedisDistlockLocker.prototype._tryAcquire = function (resource, ttl, callback) { var self = this; this.pool.acquire(REDIS_DISTLOCK.DB, function (err, client) { if (err) { return callback(err); } self.redlock.servers = [client]; - return self.redlock.lock(resource, ttl, function(err, _lock) { + return self.redlock.lock(resource, ttl, function (err, _lock) { self.pool.release(REDIS_DISTLOCK.DB, client); return callback(err, _lock); }); diff --git a/lib/batch/maintenance/host-user-queue-mover.js b/lib/batch/maintenance/host-user-queue-mover.js index c2cec1d3..ce78ddaf 100644 --- a/lib/batch/maintenance/host-user-queue-mover.js +++ b/lib/batch/maintenance/host-user-queue-mover.js @@ -15,7 +15,7 @@ var QUEUE = { } }; -function HostUserQueueMover(jobQueue, jobService, locker, redisPool) { +function HostUserQueueMover (jobQueue, jobService, locker, redisPool) { this.jobQueue = jobQueue; this.jobService = jobService; this.locker = locker; @@ -24,11 +24,11 @@ function HostUserQueueMover(jobQueue, jobService, locker, redisPool) { module.exports = HostUserQueueMover; -HostUserQueueMover.prototype.moveOldJobs = function(callback) { +HostUserQueueMover.prototype.moveOldJobs = function (callback) { var self = this; - this.getOldQueues(function(err, hosts) { + this.getOldQueues(function (err, hosts) { var async = asyncQ(4); - hosts.forEach(function(host) { + hosts.forEach(function (host) { async.defer(self.moveOldQueueJobs.bind(self), host); }); @@ -44,12 +44,12 @@ HostUserQueueMover.prototype.moveOldJobs = function(callback) { }); }; -HostUserQueueMover.prototype.moveOldQueueJobs = function(host, callback) { +HostUserQueueMover.prototype.moveOldQueueJobs = function (host, callback) { var self = this; // do forever, it does not throw a stack overflow forever( function (next) { - self.locker.lock(host, function(err) { + self.locker.lock(host, function (err) { // we didn't get the lock for the host if (err) { debug('Could not lock host=%s. Reason: %s', host, err.message); @@ -68,16 +68,16 @@ HostUserQueueMover.prototype.moveOldQueueJobs = function(host, callback) { ); }; -//this.metadataBackend.redisCmd(QUEUE.DB, 'RPOP', [ QUEUE.PREFIX + user ], callback); +// this.metadataBackend.redisCmd(QUEUE.DB, 'RPOP', [ QUEUE.PREFIX + user ], callback); HostUserQueueMover.prototype.processNextJob = function (host, callback) { var self = this; - this.pool.acquire(QUEUE.OLD.DB, function(err, client) { + this.pool.acquire(QUEUE.OLD.DB, function (err, client) { if (err) { return callback(err); } - client.lpop(QUEUE.OLD.PREFIX + host, function(err, jobId) { + client.lpop(QUEUE.OLD.PREFIX + host, function (err, jobId) { self.pool.release(QUEUE.OLD.DB, client); debug('Found jobId=%s at queue=%s', jobId, host); if (!jobId) { @@ -85,13 +85,13 @@ HostUserQueueMover.prototype.processNextJob = function (host, callback) { emptyQueueError.name = 'EmptyQueue'; return callback(emptyQueueError); } - self.jobService.get(jobId, function(err, job) { + self.jobService.get(jobId, function (err, job) { if (err) { debug(err); return callback(); } if (job) { - return self.jobQueue.enqueueFirst(job.data.user, jobId, function() { + return self.jobQueue.enqueueFirst(job.data.user, jobId, function () { return callback(); }); } @@ -101,16 +101,16 @@ HostUserQueueMover.prototype.processNextJob = function (host, callback) { }); }; -HostUserQueueMover.prototype.getOldQueues = function(callback) { +HostUserQueueMover.prototype.getOldQueues = function (callback) { var initialCursor = ['0']; var hosts = {}; var self = this; - this.pool.acquire(QUEUE.OLD.DB, function(err, client) { + this.pool.acquire(QUEUE.OLD.DB, function (err, client) { if (err) { return callback(err); } - self._getOldQueues(client, initialCursor, hosts, function(err, hosts) { + self._getOldQueues(client, initialCursor, hosts, function (err, hosts) { self.pool.release(QUEUE.DB, client); return callback(err, Object.keys(hosts)); }); @@ -121,7 +121,7 @@ HostUserQueueMover.prototype._getOldQueues = function (client, cursor, hosts, ca var self = this; var redisParams = [cursor[0], 'MATCH', QUEUE.OLD.PREFIX + '*']; - client.scan(redisParams, function(err, currentCursor) { + client.scan(redisParams, function (err, currentCursor) { if (err) { return callback(null, hosts); } diff --git a/lib/batch/maintenance/remove-old-batch-jobs.js b/lib/batch/maintenance/remove-old-batch-jobs.js index 0adc2196..9badc69c 100644 --- a/lib/batch/maintenance/remove-old-batch-jobs.js +++ b/lib/batch/maintenance/remove-old-batch-jobs.js @@ -45,7 +45,7 @@ async function scan () { async function _scan (cursor, jobs) { const redisParams = [cursor, 'MATCH', `${JOBS.PREFIX}:*`]; - const [ _cursor, _jobs ] = await redisCmd(JOBS.DB, 'SCAN', redisParams); + const [_cursor, _jobs] = await redisCmd(JOBS.DB, 'SCAN', redisParams); if (_jobs && _jobs.length) { jobs = jobs.concat(_jobs); @@ -66,12 +66,12 @@ async function getJob (key) { 'updated_at' ]; - const redisParams = [ `${key}`, ...props ]; + const redisParams = [`${key}`, ...props]; const values = await redisCmd(JOBS.DB, 'HMGET', redisParams); const job = {}; - for (const [ i, v ] of values.entries()) { + for (const [i, v] of values.entries()) { job[props[i]] = v; } @@ -79,7 +79,7 @@ async function getJob (key) { } async function removeJob (key) { - const redisParams = [ key ]; + const redisParams = [key]; const done = await redisCmd(JOBS.DB, 'DEL', redisParams); @@ -90,14 +90,14 @@ async function main () { const summary = { found: 0, removed: 0 - } + }; try { - debug(`going to scan jobs`); + debug('going to scan jobs'); const jobKeys = await scan(); summary.found = jobKeys.length; debug(`found "${jobKeys.length}" jobs`); - debug('--------------------------------------------------') + debug('--------------------------------------------------'); for (const key of jobKeys) { debug(`fetching job "${key}"`); @@ -118,7 +118,7 @@ async function main () { debug(`job "${key}" is younger than two days, keeping it`); } - debug('--------------------------------------------------') + debug('--------------------------------------------------'); } debug('summary:', summary); diff --git a/lib/batch/models/job-base.js b/lib/batch/models/job-base.js index 0827c586..eefe9eb3 100644 --- a/lib/batch/models/job-base.js +++ b/lib/batch/models/job-base.js @@ -14,7 +14,7 @@ var mandatoryProperties = [ 'user' ]; -function JobBase(data) { +function JobBase (data) { JobStateMachine.call(this); var now = new Date().toISOString(); @@ -117,6 +117,6 @@ JobBase.prototype.serialize = function () { return data; }; -JobBase.prototype.log = function(/*logger*/) { +JobBase.prototype.log = function (/* logger */) { return false; }; diff --git a/lib/batch/models/job-factory.js b/lib/batch/models/job-factory.js index e802615f..89dc44fe 100644 --- a/lib/batch/models/job-factory.js +++ b/lib/batch/models/job-factory.js @@ -4,9 +4,9 @@ var JobSimple = require('./job-simple'); var JobMultiple = require('./job-multiple'); var JobFallback = require('./job-fallback'); -var Models = [ JobSimple, JobMultiple, JobFallback ]; +var Models = [JobSimple, JobMultiple, JobFallback]; -function JobFactory() { +function JobFactory () { } module.exports = JobFactory; diff --git a/lib/batch/models/job-fallback.js b/lib/batch/models/job-fallback.js index 83d6ff74..8a265820 100644 --- a/lib/batch/models/job-fallback.js +++ b/lib/batch/models/job-fallback.js @@ -7,7 +7,7 @@ var QueryFallback = require('./query/query-fallback'); var MainFallback = require('./query/main-fallback'); var QueryFactory = require('./query/query-factory'); -function JobFallback(jobDefinition) { +function JobFallback (jobDefinition) { JobBase.call(this, jobDefinition); this.init(); @@ -69,7 +69,7 @@ JobFallback.is = function (query) { JobFallback.prototype.init = function () { for (var i = 0; i < this.data.query.query.length; i++) { - if (shouldInitStatus(this.data.query.query[i])){ + if (shouldInitStatus(this.data.query.query[i])) { this.data.query.query[i].status = JobStatus.PENDING; } if (shouldInitQueryFallbackStatus(this.data.query.query[i])) { @@ -86,15 +86,15 @@ JobFallback.prototype.init = function () { } }; -function shouldInitStatus(jobOrQuery) { +function shouldInitStatus (jobOrQuery) { return !jobOrQuery.status; } -function shouldInitQueryFallbackStatus(query) { +function shouldInitQueryFallbackStatus (query) { return (query.onsuccess || query.onerror) && !query.fallback_status; } -function shouldInitFallbackStatus(job) { +function shouldInitFallbackStatus (job) { return (job.query.onsuccess || job.query.onerror) && !job.fallback_status; } @@ -112,7 +112,6 @@ JobFallback.prototype.hasNextQueryFromQueries = function () { JobFallback.prototype.getNextQueryFromFallback = function () { if (this.fallback && this.fallback.hasNextQuery(this.data)) { - return this.fallback.getNextQuery(this.data); } }; @@ -190,7 +189,7 @@ JobFallback.prototype.shiftStatus = function (status, hasChanged) { if (hasChanged.appliedToFallback) { if (!this.hasNextQueryFromQueries() && (status === JobStatus.DONE || status === JobStatus.FAILED)) { status = this.getLastFinishedStatus(); - } else if (status === JobStatus.DONE || status === JobStatus.FAILED){ + } else if (status === JobStatus.DONE || status === JobStatus.FAILED) { status = JobStatus.PENDING; } } else if (this.hasNextQueryFromQueries() && status !== JobStatus.RUNNING) { @@ -207,7 +206,7 @@ JobFallback.prototype.getLastFinishedStatus = function () { }.bind(this), JobStatus.DONE); }; -JobFallback.prototype.log = function(logger) { +JobFallback.prototype.log = function (logger) { if (!isFinished(this)) { return false; } diff --git a/lib/batch/models/job-multiple.js b/lib/batch/models/job-multiple.js index 16042d69..710f0e4c 100644 --- a/lib/batch/models/job-multiple.js +++ b/lib/batch/models/job-multiple.js @@ -4,7 +4,7 @@ var util = require('util'); var JobBase = require('./job-base'); var jobStatus = require('../job-status'); -function JobMultiple(jobDefinition) { +function JobMultiple (jobDefinition) { JobBase.call(this, jobDefinition); this.init(); @@ -33,7 +33,6 @@ JobMultiple.is = function (query) { }; JobMultiple.prototype.init = function () { - if (!this.data.status) { this.data.status = jobStatus.PENDING; } diff --git a/lib/batch/models/job-simple.js b/lib/batch/models/job-simple.js index 644ea058..464e8150 100644 --- a/lib/batch/models/job-simple.js +++ b/lib/batch/models/job-simple.js @@ -4,7 +4,7 @@ var util = require('util'); var JobBase = require('./job-base'); var jobStatus = require('../job-status'); -function JobSimple(jobDefinition) { +function JobSimple (jobDefinition) { JobBase.call(this, jobDefinition); if (!this.data.status) { diff --git a/lib/batch/models/job-state-machine.js b/lib/batch/models/job-state-machine.js index 08bfd538..750d5d20 100644 --- a/lib/batch/models/job-state-machine.js +++ b/lib/batch/models/job-state-machine.js @@ -20,7 +20,7 @@ function JobStateMachine () { module.exports = JobStateMachine; JobStateMachine.prototype.isValidTransition = function (initialStatus, finalStatus) { - var transition = [ initialStatus, finalStatus ]; + var transition = [initialStatus, finalStatus]; for (var i = 0; i < validStatusTransitions.length; i++) { try { diff --git a/lib/batch/models/query/fallback.js b/lib/batch/models/query/fallback.js index 373c1020..d6ea8c0f 100644 --- a/lib/batch/models/query/fallback.js +++ b/lib/batch/models/query/fallback.js @@ -4,7 +4,7 @@ var util = require('util'); var QueryBase = require('./query-base'); var jobStatus = require('../../job-status'); -function Fallback(index) { +function Fallback (index) { QueryBase.call(this, index); } util.inherits(Fallback, QueryBase); diff --git a/lib/batch/models/query/main-fallback.js b/lib/batch/models/query/main-fallback.js index 8dd1b172..ae9b7b22 100644 --- a/lib/batch/models/query/main-fallback.js +++ b/lib/batch/models/query/main-fallback.js @@ -4,7 +4,7 @@ var util = require('util'); var QueryBase = require('./query-base'); var jobStatus = require('../../job-status'); -function MainFallback() { +function MainFallback () { QueryBase.call(this); } util.inherits(MainFallback, QueryBase); diff --git a/lib/batch/models/query/query-base.js b/lib/batch/models/query/query-base.js index cf36c006..0430b61f 100644 --- a/lib/batch/models/query/query-base.js +++ b/lib/batch/models/query/query-base.js @@ -3,7 +3,7 @@ var util = require('util'); var JobStateMachine = require('../job-state-machine'); -function QueryBase(index) { +function QueryBase (index) { JobStateMachine.call(this); this.index = index; diff --git a/lib/batch/models/query/query-factory.js b/lib/batch/models/query/query-factory.js index c60a2ab2..ca748461 100644 --- a/lib/batch/models/query/query-factory.js +++ b/lib/batch/models/query/query-factory.js @@ -2,7 +2,7 @@ var QueryFallback = require('./query-fallback'); -function QueryFactory() { +function QueryFactory () { } module.exports = QueryFactory; diff --git a/lib/batch/models/query/query-fallback.js b/lib/batch/models/query/query-fallback.js index 4dd48a65..75af4c7d 100644 --- a/lib/batch/models/query/query-fallback.js +++ b/lib/batch/models/query/query-fallback.js @@ -6,7 +6,7 @@ var Query = require('./query'); var Fallback = require('./fallback'); var jobStatus = require('../../job-status'); -function QueryFallback(job, index) { +function QueryFallback (job, index) { QueryBase.call(this, index); this.init(job, index); diff --git a/lib/batch/models/query/query.js b/lib/batch/models/query/query.js index 2d11ee7f..66a98d93 100644 --- a/lib/batch/models/query/query.js +++ b/lib/batch/models/query/query.js @@ -4,7 +4,7 @@ var util = require('util'); var QueryBase = require('./query-base'); var jobStatus = require('../../job-status'); -function Query(index) { +function Query (index) { QueryBase.call(this, index); } util.inherits(Query, QueryBase); diff --git a/lib/batch/pubsub/job-publisher.js b/lib/batch/pubsub/job-publisher.js index 4230a71d..77a4f52b 100644 --- a/lib/batch/pubsub/job-publisher.js +++ b/lib/batch/pubsub/job-publisher.js @@ -4,7 +4,7 @@ var Channel = require('./channel'); var debug = require('./../util/debug')('pubsub:publisher'); var error = require('./../util/debug')('pubsub:publisher:error'); -function JobPublisher(pool) { +function JobPublisher (pool) { this.pool = pool; } diff --git a/lib/batch/pubsub/job-subscriber.js b/lib/batch/pubsub/job-subscriber.js index 9c3d6865..0867f69e 100644 --- a/lib/batch/pubsub/job-subscriber.js +++ b/lib/batch/pubsub/job-subscriber.js @@ -4,7 +4,7 @@ var Channel = require('./channel'); var debug = require('./../util/debug')('pubsub:subscriber'); var error = require('./../util/debug')('pubsub:subscriber:error'); -function JobSubscriber(pool) { +function JobSubscriber (pool) { this.pool = pool; } @@ -13,7 +13,7 @@ module.exports = JobSubscriber; JobSubscriber.prototype.subscribe = function (onJobHandler, callback) { var self = this; - self.pool.acquire(Channel.DB, function(err, client) { + self.pool.acquire(Channel.DB, function (err, client) { if (err) { if (callback) { callback(err); diff --git a/lib/batch/query-runner.js b/lib/batch/query-runner.js index 1f876a2a..8bf9de68 100644 --- a/lib/batch/query-runner.js +++ b/lib/batch/query-runner.js @@ -2,7 +2,7 @@ var PSQL = require('cartodb-psql'); -function QueryRunner(userDatabaseMetadataService, logger) { +function QueryRunner (userDatabaseMetadataService, logger) { this.userDatabaseMetadataService = userDatabaseMetadataService; this.logger = logger; } diff --git a/lib/batch/scheduler/capacity/fixed.js b/lib/batch/scheduler/capacity/fixed.js index aef83be8..d8f09440 100644 --- a/lib/batch/scheduler/capacity/fixed.js +++ b/lib/batch/scheduler/capacity/fixed.js @@ -1,11 +1,11 @@ 'use strict'; -function FixedCapacity(capacity) { +function FixedCapacity (capacity) { this.capacity = Math.max(1, capacity); } module.exports = FixedCapacity; -FixedCapacity.prototype.getCapacity = function(callback) { +FixedCapacity.prototype.getCapacity = function (callback) { return callback(null, this.capacity); }; diff --git a/lib/batch/scheduler/capacity/http-load.js b/lib/batch/scheduler/capacity/http-load.js index 4fd6c1ae..bdf1298e 100644 --- a/lib/batch/scheduler/capacity/http-load.js +++ b/lib/batch/scheduler/capacity/http-load.js @@ -4,15 +4,15 @@ var util = require('util'); var debug = require('../../util/debug')('capacity-http-load'); var HttpSimpleCapacity = require('./http-simple'); -function HttpLoadCapacity(host, capacityEndpoint) { +function HttpLoadCapacity (host, capacityEndpoint) { HttpSimpleCapacity.call(this, host, capacityEndpoint); } util.inherits(HttpLoadCapacity, HttpSimpleCapacity); module.exports = HttpLoadCapacity; -HttpLoadCapacity.prototype.getCapacity = function(callback) { - this.getResponse(function(err, values) { +HttpLoadCapacity.prototype.getCapacity = function (callback) { + this.getResponse(function (err, values) { var capacity = 1; if (err) { diff --git a/lib/batch/scheduler/capacity/http-simple.js b/lib/batch/scheduler/capacity/http-simple.js index fe73a7a3..73ef71f9 100644 --- a/lib/batch/scheduler/capacity/http-simple.js +++ b/lib/batch/scheduler/capacity/http-simple.js @@ -3,7 +3,7 @@ var request = require('request'); var debug = require('../../util/debug')('capacity-http-simple'); -function HttpSimpleCapacity(host, capacityEndpoint) { +function HttpSimpleCapacity (host, capacityEndpoint) { this.host = host; this.capacityEndpoint = capacityEndpoint; @@ -13,8 +13,8 @@ function HttpSimpleCapacity(host, capacityEndpoint) { module.exports = HttpSimpleCapacity; -HttpSimpleCapacity.prototype.getCapacity = function(callback) { - this.getResponse(function(err, values) { +HttpSimpleCapacity.prototype.getCapacity = function (callback) { + this.getResponse(function (err, values) { var capacity = 1; if (err) { @@ -31,7 +31,7 @@ HttpSimpleCapacity.prototype.getCapacity = function(callback) { }.bind(this)); }; -HttpSimpleCapacity.prototype.getResponse = function(callback) { +HttpSimpleCapacity.prototype.getResponse = function (callback) { var requestParams = { method: 'POST', url: this.capacityEndpoint, @@ -46,7 +46,7 @@ HttpSimpleCapacity.prototype.getResponse = function(callback) { return callback(null, this.lastResponse); } - request.post(requestParams, function(err, res, jsonRes) { + request.post(requestParams, function (err, res, jsonRes) { if (err) { return callback(err); } diff --git a/lib/batch/scheduler/host-scheduler.js b/lib/batch/scheduler/host-scheduler.js index 99526e81..6419fa95 100644 --- a/lib/batch/scheduler/host-scheduler.js +++ b/lib/batch/scheduler/host-scheduler.js @@ -8,11 +8,11 @@ var FixedCapacity = require('./capacity/fixed'); var HttpSimpleCapacity = require('./capacity/http-simple'); var HttpLoadCapacity = require('./capacity/http-load'); -function HostScheduler(name, taskRunner, redisPool) { +function HostScheduler (name, taskRunner, redisPool) { this.name = name || 'scheduler'; this.taskRunner = taskRunner; this.locker = Locker.create('redis-distlock', { pool: redisPool }); - this.locker.on('error', function(err, host) { + this.locker.on('error', function (err, host) { debug('[%s] Locker.error %s', this.name, err.message); this.unlock(host); }.bind(this)); @@ -22,8 +22,8 @@ function HostScheduler(name, taskRunner, redisPool) { module.exports = HostScheduler; -HostScheduler.prototype.add = function(host, user, callback) { - this.lock(host, function(err, scheduler) { +HostScheduler.prototype.add = function (host, user, callback) { + this.lock(host, function (err, scheduler) { if (err) { debug('[%s] Could not lock host=%s', this.name, host); return callback(err); @@ -35,7 +35,7 @@ HostScheduler.prototype.add = function(host, user, callback) { }.bind(this)); }; -HostScheduler.prototype.getCapacityProvider = function(host) { +HostScheduler.prototype.getCapacityProvider = function (host) { var strategy = global.settings.batch_capacity_strategy; if (strategy === 'http-simple' || strategy === 'http-load') { @@ -55,10 +55,10 @@ HostScheduler.prototype.getCapacityProvider = function(host) { return new FixedCapacity(fixedCapacity); }; -HostScheduler.prototype.lock = function(host, callback) { +HostScheduler.prototype.lock = function (host, callback) { debug('[%s] lock(%s)', this.name, host); var self = this; - this.locker.lock(host, function(err) { + this.locker.lock(host, function (err) { if (err) { debug('[%s] Could not lock host=%s. Reason: %s', self.name, host, err.message); return callback(err); @@ -75,7 +75,7 @@ HostScheduler.prototype.lock = function(host, callback) { }); }; -HostScheduler.prototype.unlock = function(host) { +HostScheduler.prototype.unlock = function (host) { debug('[%s] unlock(%s)', this.name, host); if (this.schedulers.hasOwnProperty(host)) { // TODO stop scheduler? diff --git a/lib/batch/scheduler/scheduler.js b/lib/batch/scheduler/scheduler.js index a8e18c9a..1103b970 100644 --- a/lib/batch/scheduler/scheduler.js +++ b/lib/batch/scheduler/scheduler.js @@ -13,14 +13,14 @@ var debug = require('../util/debug')('scheduler'); var forever = require('../util/forever'); -function Scheduler(capacity, taskRunner) { +function Scheduler (capacity, taskRunner) { EventEmitter.call(this); debug('new Scheduler'); this.taskRunner = taskRunner; this.capacity = capacity; this.tasks = []; this.users = {}; - this.tasksTree = new RBTree(function(taskEntityA, taskEntityB) { + this.tasksTree = new RBTree(function (taskEntityA, taskEntityB) { // if the user is the same it's the same entity if (taskEntityA.user === taskEntityB.user) { return 0; @@ -44,7 +44,7 @@ util.inherits(Scheduler, EventEmitter); module.exports = Scheduler; -Scheduler.prototype.add = function(user) { +Scheduler.prototype.add = function (user) { debug('add(%s)', user); var taskEntity = this.users[user]; if (taskEntity) { @@ -67,7 +67,7 @@ Scheduler.prototype.add = function(user) { } }; -Scheduler.prototype.schedule = function() { +Scheduler.prototype.schedule = function () { if (this.running) { return true; } @@ -77,7 +77,7 @@ Scheduler.prototype.schedule = function() { forever( function (next) { debug('Waiting for task'); - self.acquire(function(err, taskEntity) { + self.acquire(function (err, taskEntity) { debug('Acquired user=%j', taskEntity); if (!taskEntity) { @@ -88,7 +88,7 @@ Scheduler.prototype.schedule = function() { taskEntity.running(); debug('Running task for user=%s', taskEntity.user); - self.taskRunner.run(taskEntity.user, function(err, userQueueIsEmpty) { + self.taskRunner.run(taskEntity.user, function (err, userQueueIsEmpty) { debug('Run task=%j, done=%s', taskEntity, userQueueIsEmpty); taskEntity.ran(userQueueIsEmpty); self.release(err, taskEntity); @@ -110,7 +110,7 @@ Scheduler.prototype.schedule = function() { return false; }; -Scheduler.prototype.acquire = function(callback) { +Scheduler.prototype.acquire = function (callback) { this.removeAllListeners('add'); this.removeAllListeners('release'); @@ -119,7 +119,7 @@ Scheduler.prototype.acquire = function(callback) { } var self = this; - this.capacity.getCapacity(function(err, capacity) { + this.capacity.getCapacity(function (err, capacity) { if (err) { return callback(err); } @@ -128,11 +128,11 @@ Scheduler.prototype.acquire = function(callback) { var running = self.tasks.filter(is(STATUS.RUNNING)); debug('[capacity=%d, running=%d] candidates=%j', capacity, running.length, self.tasks); - self.once('add', function() { + self.once('add', function () { debug('Got a new task'); self.acquire(callback); }); - self.once('release', function() { + self.once('release', function () { debug('Slot was released'); self.acquire(callback); }); @@ -157,7 +157,7 @@ Scheduler.prototype.acquire = function(callback) { }); }; -Scheduler.prototype.release = function(err, taskEntity) { +Scheduler.prototype.release = function (err, taskEntity) { debug('Released %j', taskEntity); if (taskEntity.is(STATUS.PENDING)) { this.tasksTree.insert(taskEntity); @@ -165,7 +165,6 @@ Scheduler.prototype.release = function(err, taskEntity) { this.emit('release'); }; - /* Task entities */ var STATUS = { @@ -174,28 +173,28 @@ var STATUS = { DONE: 'done' }; -function TaskEntity(user, createdAt) { +function TaskEntity (user, createdAt) { this.user = user; this.createdAt = createdAt; this.status = STATUS.PENDING; this.jobs = 0; } -TaskEntity.prototype.is = function(status) { +TaskEntity.prototype.is = function (status) { return this.status === status; }; -TaskEntity.prototype.running = function() { +TaskEntity.prototype.running = function () { this.status = STATUS.RUNNING; }; -TaskEntity.prototype.ran = function(userQueueIsEmpty) { +TaskEntity.prototype.ran = function (userQueueIsEmpty) { this.jobs++; this.status = userQueueIsEmpty ? STATUS.DONE : STATUS.PENDING; }; -function is(status) { - return function(taskEntity) { +function is (status) { + return function (taskEntity) { return taskEntity.is(status); }; } diff --git a/lib/batch/user-database-metadata-service.js b/lib/batch/user-database-metadata-service.js index 6a083a5a..004bc261 100644 --- a/lib/batch/user-database-metadata-service.js +++ b/lib/batch/user-database-metadata-service.js @@ -1,6 +1,6 @@ 'use strict'; -function UserDatabaseMetadataService(metadataBackend) { +function UserDatabaseMetadataService (metadataBackend) { this.metadataBackend = metadataBackend; } diff --git a/lib/batch/util/forever.js b/lib/batch/util/forever.js index 6246abe5..463f2e01 100644 --- a/lib/batch/util/forever.js +++ b/lib/batch/util/forever.js @@ -1,7 +1,7 @@ 'use strict'; -module.exports = function forever(fn, done) { - function next(err) { +module.exports = function forever (fn, done) { + function next (err) { if (err) { return done(err); } diff --git a/lib/models/bin-encoder.js b/lib/models/bin-encoder.js index c764a447..c028d7fe 100644 --- a/lib/models/bin-encoder.js +++ b/lib/models/bin-encoder.js @@ -1,33 +1,33 @@ 'use strict'; -function ArrayBufferSer(type, data, options) { - if(type === undefined) { - throw "ArrayBufferSer should be created with a type"; - } - this.options = options || {}; - this._initFunctions(); - this.headerSize = 8; - this.data = data; - this.type = type = Math.min(type, ArrayBufferSer.BUFFER); - var size = this._sizeFor(this.headerSize, data); - this.buffer = new Buffer(this.headerSize + size); - this.buffer.writeUInt32BE(type, 0); // this could be one byte but for byte padding is better to be 4 bytes - this.buffer.writeUInt32BE(size, 4); - this.offset = this.headerSize; - - var w = this.writeFn[type]; - - var i; - if(!this.options.delta) { - for(i = 0; i < data.length; ++i) { - this[w](data[i]); +function ArrayBufferSer (type, data, options) { + if (type === undefined) { + throw 'ArrayBufferSer should be created with a type'; } - } else { - this[w](data[0]); - for(i = 1; i < data.length; ++i) { - this[w](data[i] - data[i - 1]); + this.options = options || {}; + this._initFunctions(); + this.headerSize = 8; + this.data = data; + this.type = type = Math.min(type, ArrayBufferSer.BUFFER); + var size = this._sizeFor(this.headerSize, data); + this.buffer = new Buffer(this.headerSize + size); + this.buffer.writeUInt32BE(type, 0); // this could be one byte but for byte padding is better to be 4 bytes + this.buffer.writeUInt32BE(size, 4); + this.offset = this.headerSize; + + var w = this.writeFn[type]; + + var i; + if (!this.options.delta) { + for (i = 0; i < data.length; ++i) { + this[w](data[i]); + } + } else { + this[w](data[0]); + for (i = 1; i < data.length; ++i) { + this[w](data[i] - data[i - 1]); + } } - } } // @@ -41,126 +41,124 @@ ArrayBufferSer.UINT16 = 5; ArrayBufferSer.INT32 = 6; ArrayBufferSer.UINT32 = 7; ArrayBufferSer.FLOAT32 = 8; -//ArrayBufferSer.FLOAT64 = 9; not supported +// ArrayBufferSer.FLOAT64 = 9; not supported ArrayBufferSer.STRING = 10; ArrayBufferSer.BUFFER = 11; ArrayBufferSer.MAX_PADDING = ArrayBufferSer.INT32; - ArrayBufferSer.typeNames = { - 'int8': ArrayBufferSer.INT8, - 'uint8': ArrayBufferSer.UINT8, - 'uintclamp': ArrayBufferSer.UINT8_CLAMP, - 'int16': ArrayBufferSer.INT16, - 'uint16': ArrayBufferSer.UINT16, - 'int32': ArrayBufferSer.INT32, - 'uint32': ArrayBufferSer.UINT32, - 'float32': ArrayBufferSer.FLOAT32, - 'string': ArrayBufferSer.STRING, - 'buffer': ArrayBufferSer.BUFFER + int8: ArrayBufferSer.INT8, + uint8: ArrayBufferSer.UINT8, + uintclamp: ArrayBufferSer.UINT8_CLAMP, + int16: ArrayBufferSer.INT16, + uint16: ArrayBufferSer.UINT16, + int32: ArrayBufferSer.INT32, + uint32: ArrayBufferSer.UINT32, + float32: ArrayBufferSer.FLOAT32, + string: ArrayBufferSer.STRING, + buffer: ArrayBufferSer.BUFFER }; ArrayBufferSer.prototype = { - // 0 not used - sizes: [NaN, 1, 1, 1, 2, 2, 4, 4, 4, 8], + // 0 not used + sizes: [NaN, 1, 1, 1, 2, 2, 4, 4, 4, 8], - _paddingFor: function(off, type) { - var s = this.sizes[type]; - if(s) { - var r = off % s; - return r === 0 ? 0 : s - r; + _paddingFor: function (off, type) { + var s = this.sizes[type]; + if (s) { + var r = off % s; + return r === 0 ? 0 : s - r; + } + return 0; + }, + + _sizeFor: function (offset, t) { + var self = this; + var s = this.sizes[this.type]; + if (s) { + return s * t.length; + } + s = 0; + if (this.type === ArrayBufferSer.STRING) { + // calculate size with padding + t.forEach(function (arr) { + var pad = self._paddingFor(offset, ArrayBufferSer.MAX_PADDING); + s += pad; + offset += pad; + var len = (self.headerSize + arr.length * 2); + s += len; + offset += len; + }); + } else { + t.forEach(function (arr) { + var pad = self._paddingFor(offset, ArrayBufferSer.MAX_PADDING); + s += pad; + offset += pad; + s += arr.getSize(); + offset += arr.getSize(); + }); + } + return s; + }, + + getDataSize: function () { + return this._sizeFor(0, this.data); + }, + + getSize: function () { + return this.headerSize + this._sizeFor(this.headerSize, this.data); + }, + + 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) { + self[fn] = function (d) { + self.buffer[fn](d, self.offset); + self.offset += self.sizes[self.type]; + }; + } + }); + }, + + writeUInt8Clamp: function (c) { + this.buffer.writeUInt8(Math.min(255, c), this.offset); + this.offset += 1; + }, + + writeString: function (s) { + var arr = []; + for (var i = 0, len = s.length; i < len; ++i) { + arr.push(s.charCodeAt(i)); + } + var str = new ArrayBufferSer(ArrayBufferSer.UINT16, arr); + this.writteBuffer(str); + }, + + writteBuffer: function (b) { + this.offset += this._paddingFor(this.offset, ArrayBufferSer.MAX_PADDING); + // copy header + b.buffer.copy(this.buffer, this.offset); + this.offset += b.buffer.length; } - return 0; - }, - - _sizeFor: function(offset, t) { - var self = this; - var s = this.sizes[this.type]; - if(s) { - return s*t.length; - } - s = 0; - if(this.type === ArrayBufferSer.STRING) { - // calculate size with padding - t.forEach(function(arr) { - var pad = self._paddingFor(offset, ArrayBufferSer.MAX_PADDING); - s += pad; - offset += pad; - var len = (self.headerSize + arr.length*2); - s += len; - offset += len; - }); - } else { - t.forEach(function(arr) { - var pad = self._paddingFor(offset, ArrayBufferSer.MAX_PADDING); - s += pad; - offset += pad; - s += arr.getSize(); - offset += arr.getSize(); - }); - } - return s; - }, - - getDataSize: function() { - return this._sizeFor(0, this.data); - }, - - getSize: function() { - return this.headerSize + this._sizeFor(this.headerSize, this.data); - }, - - 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) { - self[fn] = function(d) { - self.buffer[fn](d, self.offset); - self.offset += self.sizes[self.type]; - }; - } - }); - }, - - writeUInt8Clamp: function(c) { - this.buffer.writeUInt8(Math.min(255, c), this.offset); - this.offset += 1; - }, - - writeString: function(s) { - var arr = []; - for(var i = 0, len = s.length; i < len; ++i) { - arr.push(s.charCodeAt(i)); - } - var str = new ArrayBufferSer(ArrayBufferSer.UINT16, arr); - this.writteBuffer(str); - }, - - writteBuffer: function(b) { - this.offset += this._paddingFor(this.offset, ArrayBufferSer.MAX_PADDING); - // copy header - b.buffer.copy(this.buffer, this.offset); - this.offset += b.buffer.length; - } }; - module.exports = ArrayBufferSer; diff --git a/lib/models/cartodb-request.js b/lib/models/cartodb-request.js index 8ba9b890..12034936 100644 --- a/lib/models/cartodb-request.js +++ b/lib/models/cartodb-request.js @@ -5,7 +5,7 @@ * of request headers */ -function CartodbRequest() { +function CartodbRequest () { } module.exports = CartodbRequest; @@ -13,7 +13,7 @@ module.exports = CartodbRequest; /** * If the request contains the user use it, if not guess from the host */ -CartodbRequest.prototype.userByReq = function(req) { +CartodbRequest.prototype.userByReq = function (req) { if (req.params.user) { return req.params.user; } @@ -24,7 +24,7 @@ var re_userFromHost = new RegExp( global.settings.user_from_host || '^([^\\.]+)\\.' // would extract "strk" from "strk.cartodb.com" ); -function userByHostName(host) { +function userByHostName (host) { var mat = host.match(re_userFromHost); if (!mat) { console.error("ERROR: user pattern '" + re_userFromHost + "' does not match hostname '" + host + "'"); diff --git a/lib/models/formats/index.js b/lib/models/formats/index.js index 68e65c69..a8f1f33a 100644 --- a/lib/models/formats/index.js +++ b/lib/models/formats/index.js @@ -1,11 +1,11 @@ 'use strict'; -var fs = require("fs"); +var fs = require('fs'); var formats = {}; -function formatFilesWithPath(dir) { +function formatFilesWithPath (dir) { var formatDir = __dirname + '/' + dir; - return fs.readdirSync(formatDir).map(function(formatFile) { + return fs.readdirSync(formatDir).map(function (formatFile) { return formatDir + '/' + formatFile; }); } @@ -14,7 +14,7 @@ var formatFilesPaths = [] .concat(formatFilesWithPath('ogr')) .concat(formatFilesWithPath('pg')); -formatFilesPaths.forEach(function(file) { +formatFilesPaths.forEach(function (file) { var format = require(file); formats[format.prototype.id] = format; }); diff --git a/lib/models/formats/ogr.js b/lib/models/formats/ogr.js index c2c44992..bb87a6db 100644 --- a/lib/models/formats/ogr.js +++ b/lib/models/formats/ogr.js @@ -10,337 +10,333 @@ var spawn = require('child_process').spawn; // Keeps track of what's waiting baking for export var bakingExports = {}; -function OgrFormat(id) { - this.id = id; +function OgrFormat (id) { + this.id = id; } OgrFormat.prototype = { - id: "ogr", + id: 'ogr', - is_file: true, + is_file: true, - getQuery: function(/*sql, options*/) { - return null; // dont execute the query - }, + 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; }, + getContentType: function () { return this._contentType; }, - getFileExtension: function(){ return this._fileExtension; }, + getFileExtension: function () { return this._fileExtension; }, - getKey: function(options) { - return [this.id, - options.dbopts.dbname, - options.dbopts.user, - options.gn, - this.generateMD5(options.filename), - this.generateMD5(options.sql)].concat(options.skipfields).join(':'); - }, + getKey: function (options) { + return [this.id, + options.dbopts.dbname, + options.dbopts.user, + options.gn, + this.generateMD5(options.filename), + this.generateMD5(options.sql)].concat(options.skipfields).join(':'); + }, - generateMD5: function (data){ - var hash = crypto.createHash('md5'); - hash.update(data); - return hash.digest('hex'); - } + generateMD5: function (data) { + var hash = crypto.createHash('md5'); + hash.update(data); + return hash.digest('hex'); + } }; // Internal function usable by all OGR-driven outputs -OgrFormat.prototype.toOGR = function(options, out_format, out_filename, callback) { +OgrFormat.prototype.toOGR = function (options, out_format, out_filename, callback) { + // var gcol = options.gn; + var sql = options.sql; + var skipfields = options.skipfields; + var out_layername = options.filename; - //var gcol = options.gn; - var sql = options.sql; - var skipfields = options.skipfields; - var out_layername = options.filename; + var dbopts = options.dbopts; - var dbopts = options.dbopts; + var ogr2ogr = global.settings.ogr2ogrCommand || 'ogr2ogr'; + var dbhost = dbopts.host; + var dbport = dbopts.port; + var dbuser = dbopts.user; + var dbpass = dbopts.pass; + var dbname = dbopts.dbname; - var ogr2ogr = global.settings.ogr2ogrCommand || 'ogr2ogr'; - var dbhost = dbopts.host; - var dbport = dbopts.port; - var dbuser = dbopts.user; - var dbpass = dbopts.pass; - var dbname = dbopts.dbname; + var timeout = options.timeout; - var timeout = options.timeout; + var that = this; - var that = this; + var columns = []; + var geocol; + var pg; + // Drop ending semicolon (ogr doens't like it) + sql = sql.replace(/;\s*$/, ''); - var columns = []; - var geocol; - var pg; - // Drop ending semicolon (ogr doens't like it) - sql = sql.replace(/;\s*$/, ''); + const theGeomFirst = (fieldA, fieldB) => { + if (fieldA.name === 'the_geom') { + return -1; + } + if (fieldB.name === 'the_geom') { + return 1; + } + return 0; + }; - const theGeomFirst = (fieldA, fieldB) => { - if (fieldA.name === 'the_geom') { - return -1; - } - if (fieldB.name === 'the_geom') { - return 1; - } - return 0; - }; + step( - step ( + function fetchColumns () { + var colsql = 'SELECT * FROM (' + sql + ') as _cartodbsqlapi LIMIT 0'; + pg = new PSQL(dbopts); + pg.query(colsql, this); + }, + function findSRS (err, result) { + if (err) { + throw err; + } - function fetchColumns() { - var colsql = 'SELECT * FROM (' + sql + ') as _cartodbsqlapi LIMIT 0'; - pg = new PSQL(dbopts); - pg.query(colsql, this); - }, - function findSRS(err, result) { - if (err) { - throw err; - } + var needSRS = that._needSRS; - var needSRS = that._needSRS; + columns = result.fields + // skip columns + .filter(field => skipfields.indexOf(field.name) === -1) + // put "the_geom" first (if exists) + .sort(theGeomFirst) + // get first geometry to calculate SRID ("the_geom" if exists) + .map(field => { + if (needSRS && !geocol && pg.typeName(field.dataTypeID) === 'geometry') { + geocol = field.name; + } - columns = result.fields - // skip columns - .filter(field => skipfields.indexOf(field.name) === -1) - // put "the_geom" first (if exists) - .sort(theGeomFirst) - // get first geometry to calculate SRID ("the_geom" if exists) - .map(field => { - if (needSRS && !geocol && pg.typeName(field.dataTypeID) === 'geometry') { - geocol = field.name; - } + return field; + }) + // apply quotes to columns + .map(field => out_format === 'CSV' ? pg.quoteIdentifier(field.name) + '::text' : pg.quoteIdentifier(field.name)); - return field; - }) - // apply quotes to columns - .map(field => out_format === 'CSV' ? pg.quoteIdentifier(field.name)+'::text' : pg.quoteIdentifier(field.name)); + if (!needSRS || !geocol) { + return null; + } - if ( ! needSRS || ! geocol ) { - return null; - } + var next = this; - var next = this; - - var qgeocol = pg.quoteIdentifier(geocol); - var sridsql = 'SELECT ST_Srid(' + qgeocol + ') as srid, GeometryType(' + + var qgeocol = pg.quoteIdentifier(geocol); + var sridsql = 'SELECT ST_Srid(' + qgeocol + ') as srid, GeometryType(' + qgeocol + ') as type FROM (' + sql + ') as _cartodbsqlapi WHERE ' + qgeocol + ' is not null limit 1'; - pg.query(sridsql, function(err, result) { - if ( err ) { next(err); return; } - if ( result.rows.length ) { - var srid = result.rows[0].srid; - var type = result.rows[0].type; - next(null, srid, type); - } else { - // continue as srid and geom type are not critical when there are no results - next(null); - } - }); - }, - function spawnDumper(err, srid, type) { - if (err) { - throw err; - } - - var next = this; - - var ogrsql = 'SELECT ' + columns.join(',') + ' FROM (' + sql + ') as _cartodbsqlapi'; - - var ogrargs = [ - '-f', out_format, - '-lco', 'RESIZE=YES', - '-lco', 'ENCODING=UTF-8', - '-lco', 'LINEFORMAT=CRLF', - out_filename, - "PG:host=" + dbhost + " port=" + dbport + " user=" + dbuser + " dbname=" + dbname + " password=" + dbpass, - '-sql', ogrsql - ]; - - if ( srid ) { - ogrargs.push('-a_srs', 'EPSG:'+srid); - } - - if ( type ) { - ogrargs.push('-nlt', type); - } - - if (options.cmd_params){ - ogrargs = ogrargs.concat(options.cmd_params); - } - - ogrargs.push('-nln', out_layername); - - // TODO: research if `exec` could fit better than `spawn` - var child = spawn(ogr2ogr, ogrargs); - - var timedOut = false; - var ogrTimeout; - if (timeout > 0) { - ogrTimeout = setTimeout(function () { - timedOut = true; - child.kill(); - }, timeout); - } - - child.on('error', function (err) { - clearTimeout(ogrTimeout); - next(err); - }); - - var stderrData = []; - child.stderr.setEncoding('utf8'); - child.stderr.on('data', function (data) { - stderrData.push(data); - }); - - child.on('exit', function(code) { - clearTimeout(ogrTimeout); - - if (timedOut) { - return next(new Error('statement timeout')); - } - - if (code !== 0) { - var errMessage = 'ogr2ogr command return code ' + code; - if (stderrData.length > 0) { - errMessage += ', Error: ' + stderrData.join('\n'); - } - - return next(new Error(errMessage)); - } - - return next(); - }); - - }, - function finish(err) { - callback(err, out_filename); - } - ); -}; - -OgrFormat.prototype.toOGR_SingleFile = function(options, fmt, callback) { - - var dbname = options.dbopts.dbname; - var user_id = options.dbopts.user; - var gcol = options.gcol; - var sql = options.sql; - var skipfields = options.skipfields; - var ext = this._fileExtension; - 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 outdirpath = tmpdir + '/sqlapi-' + process.pid + '-' + reqKey; - var dumpfile = outdirpath + ':cartodb-query.' + ext; - - // TODO: following tests: - // - fetch query with no "the_geom" column - this.toOGR(options, fmt, dumpfile, callback); -}; - -OgrFormat.prototype.sendResponse = function(opts, callback) { - //var next = callback; - var reqKey = this.getKey(opts); - var qElem = new ExportRequest(opts.sink, callback, opts.beforeSink); - var baking = bakingExports[reqKey]; - if ( baking ) { - baking.req.push( qElem ); - } else { - baking = bakingExports[reqKey] = { req: [ qElem ] }; - this.generate(opts, function(err, dumpfile) { - if ( opts.profiler ) { - opts.profiler.done('generate'); - } - step ( - function sendResults() { - var nextPipe = function(finish) { - var r = baking.req.shift(); - if ( ! r ) { finish(null); return; } - r.sendFile(err, dumpfile, function() { - nextPipe(finish); + pg.query(sridsql, function (err, result) { + if (err) { next(err); return; } + if (result.rows.length) { + var srid = result.rows[0].srid; + var type = result.rows[0].type; + next(null, srid, type); + } else { + // continue as srid and geom type are not critical when there are no results + next(null); + } }); - }; - - if ( ! err ) { - nextPipe(this); - } else { - _.each(baking.req, function(r) { - r.cb(err); - }); - return true; - } }, - 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' ) { - console.log("Could not unlink dumpfile " + dumpfile + ": " + e); + function spawnDumper (err, srid, type) { + if (err) { + throw err; } - } + + var next = this; + + var ogrsql = 'SELECT ' + columns.join(',') + ' FROM (' + sql + ') as _cartodbsqlapi'; + + var ogrargs = [ + '-f', out_format, + '-lco', 'RESIZE=YES', + '-lco', 'ENCODING=UTF-8', + '-lco', 'LINEFORMAT=CRLF', + out_filename, + 'PG:host=' + dbhost + ' port=' + dbport + ' user=' + dbuser + ' dbname=' + dbname + ' password=' + dbpass, + '-sql', ogrsql + ]; + + if (srid) { + ogrargs.push('-a_srs', 'EPSG:' + srid); + } + + if (type) { + ogrargs.push('-nlt', type); + } + + if (options.cmd_params) { + ogrargs = ogrargs.concat(options.cmd_params); + } + + ogrargs.push('-nln', out_layername); + + // TODO: research if `exec` could fit better than `spawn` + var child = spawn(ogr2ogr, ogrargs); + + var timedOut = false; + var ogrTimeout; + if (timeout > 0) { + ogrTimeout = setTimeout(function () { + timedOut = true; + child.kill(); + }, timeout); + } + + child.on('error', function (err) { + clearTimeout(ogrTimeout); + next(err); + }); + + var stderrData = []; + child.stderr.setEncoding('utf8'); + child.stderr.on('data', function (data) { + stderrData.push(data); + }); + + child.on('exit', function (code) { + clearTimeout(ogrTimeout); + + if (timedOut) { + return next(new Error('statement timeout')); + } + + if (code !== 0) { + var errMessage = 'ogr2ogr command return code ' + code; + if (stderrData.length > 0) { + errMessage += ', Error: ' + stderrData.join('\n'); + } + + return next(new Error(errMessage)); + } + + return next(); + }); + }, + function finish (err) { + callback(err, out_filename); } - ); - }); - } + ); +}; + +OgrFormat.prototype.toOGR_SingleFile = function (options, fmt, callback) { + var dbname = options.dbopts.dbname; + var user_id = options.dbopts.user; + var gcol = options.gcol; + var sql = options.sql; + var skipfields = options.skipfields; + var ext = this._fileExtension; + 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 outdirpath = tmpdir + '/sqlapi-' + process.pid + '-' + reqKey; + var dumpfile = outdirpath + ':cartodb-query.' + ext; + + // TODO: following tests: + // - fetch query with no "the_geom" column + this.toOGR(options, fmt, dumpfile, callback); +}; + +OgrFormat.prototype.sendResponse = function (opts, callback) { + // var next = callback; + var reqKey = this.getKey(opts); + var qElem = new ExportRequest(opts.sink, callback, opts.beforeSink); + var baking = bakingExports[reqKey]; + if (baking) { + baking.req.push(qElem); + } else { + baking = bakingExports[reqKey] = { req: [qElem] }; + this.generate(opts, function (err, dumpfile) { + if (opts.profiler) { + opts.profiler.done('generate'); + } + step( + function sendResults () { + var nextPipe = function (finish) { + var r = baking.req.shift(); + if (!r) { finish(null); return; } + r.sendFile(err, dumpfile, function () { + nextPipe(finish); + }); + }; + + if (!err) { + nextPipe(this); + } else { + _.each(baking.req, function (r) { + r.cb(err); + }); + return true; + } + }, + 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') { + console.log('Could not unlink dumpfile ' + dumpfile + ': ' + e); + } + } + } + ); + }); + } }; // TODO: put in an ExportRequest.js ----- { -function ExportRequest(ostream, callback, beforeSink) { - this.cb = callback; - this.beforeSink = beforeSink; - this.ostream = ostream; - this.istream = null; - this.canceled = false; +function ExportRequest (ostream, callback, beforeSink) { + this.cb = callback; + this.beforeSink = beforeSink; + this.ostream = ostream; + this.istream = null; + this.canceled = false; - var that = this; + var that = this; - this.ostream.on('close', function() { - //console.log("Request close event, qElem.stream is " + qElem.stream); - that.canceled = true; - if ( that.istream ) { - that.istream.destroy(); - } - }); + this.ostream.on('close', function () { + // console.log("Request close event, qElem.stream is " + qElem.stream); + that.canceled = true; + if (that.istream) { + that.istream.destroy(); + } + }); } ExportRequest.prototype.sendFile = function (err, filename, callback) { - var that = this; - 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(); - } - that.istream.pipe(that.ostream); - callback(); - }) - .on('error', function(e) { - console.log("Can't send response: " + e); - that.ostream.end(); - callback(); - }); - } else { - //console.log("Response was canceled, not streaming the file"); - callback(); - } - this.cb(); + var that = this; + 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(); + } + that.istream.pipe(that.ostream); + callback(); + }) + .on('error', function (e) { + console.log("Can't send response: " + e); + that.ostream.end(); + callback(); + }); + } else { + // console.log("Response was canceled, not streaming the file"); + callback(); + } + this.cb(); }; -//------ } +// ------ } module.exports = OgrFormat; diff --git a/lib/models/formats/ogr/csv.js b/lib/models/formats/ogr/csv.js index 74638d20..425f3b5c 100644 --- a/lib/models/formats/ogr/csv.js +++ b/lib/models/formats/ogr/csv.js @@ -2,14 +2,14 @@ var Ogr = require('./../ogr'); -function CsvFormat() {} +function CsvFormat () {} CsvFormat.prototype = new Ogr('csv'); -CsvFormat.prototype._contentType = "text/csv; charset=utf-8; header=present"; -CsvFormat.prototype._fileExtension = "csv"; +CsvFormat.prototype._contentType = 'text/csv; charset=utf-8; header=present'; +CsvFormat.prototype._fileExtension = 'csv'; -CsvFormat.prototype.generate = function(options, callback) { +CsvFormat.prototype.generate = function (options, callback) { this.toOGR_SingleFile(options, 'CSV', callback); }; diff --git a/lib/models/formats/ogr/geopackage.js b/lib/models/formats/ogr/geopackage.js index 652158ff..debbbf86 100644 --- a/lib/models/formats/ogr/geopackage.js +++ b/lib/models/formats/ogr/geopackage.js @@ -2,12 +2,12 @@ var Ogr = require('./../ogr'); -function GeoPackageFormat() {} +function GeoPackageFormat () {} GeoPackageFormat.prototype = new Ogr('gpkg'); -GeoPackageFormat.prototype._contentType = "application/x-sqlite3; charset=utf-8"; -GeoPackageFormat.prototype._fileExtension = "gpkg"; +GeoPackageFormat.prototype._contentType = 'application/x-sqlite3; charset=utf-8'; +GeoPackageFormat.prototype._fileExtension = 'gpkg'; // As of GDAL 1.10.1 SRID detection is bogus, so we use // our own method. See: // http://trac.osgeo.org/gdal/ticket/5131 @@ -17,7 +17,7 @@ GeoPackageFormat.prototype._fileExtension = "gpkg"; // Bug was fixed in GDAL 1.10.2 GeoPackageFormat.prototype._needSRS = true; -GeoPackageFormat.prototype.generate = function(options, callback) { +GeoPackageFormat.prototype.generate = function (options, callback) { options.cmd_params = ['-lco', 'FID=cartodb_id']; this.toOGR_SingleFile(options, 'GPKG', callback); }; diff --git a/lib/models/formats/ogr/kml.js b/lib/models/formats/ogr/kml.js index 2bace95c..620f0555 100644 --- a/lib/models/formats/ogr/kml.js +++ b/lib/models/formats/ogr/kml.js @@ -2,12 +2,12 @@ var Ogr = require('./../ogr'); -function KmlFormat() {} +function KmlFormat () {} KmlFormat.prototype = new Ogr('kml'); -KmlFormat.prototype._contentType = "application/kml; charset=utf-8"; -KmlFormat.prototype._fileExtension = "kml"; +KmlFormat.prototype._contentType = 'application/kml; charset=utf-8'; +KmlFormat.prototype._fileExtension = 'kml'; // As of GDAL 1.10.1 SRID detection is bogus, so we use // our own method. See: // http://trac.osgeo.org/gdal/ticket/5131 @@ -17,7 +17,7 @@ KmlFormat.prototype._fileExtension = "kml"; // Bug was fixed in GDAL 1.10.2 KmlFormat.prototype._needSRS = true; -KmlFormat.prototype.generate = function(options, callback) { +KmlFormat.prototype.generate = function (options, callback) { this.toOGR_SingleFile(options, 'KML', callback); }; diff --git a/lib/models/formats/ogr/shp.js b/lib/models/formats/ogr/shp.js index 68c30aa3..28f60d54 100644 --- a/lib/models/formats/ogr/shp.js +++ b/lib/models/formats/ogr/shp.js @@ -6,13 +6,13 @@ var spawn = require('child_process').spawn; var Ogr = require('./../ogr'); -function ShpFormat() { +function ShpFormat () { } ShpFormat.prototype = new Ogr('shp'); -ShpFormat.prototype._contentType = "application/zip; charset=utf-8"; -ShpFormat.prototype._fileExtension = "zip"; +ShpFormat.prototype._contentType = 'application/zip; charset=utf-8'; +ShpFormat.prototype._fileExtension = 'zip'; // As of GDAL 1.10 SRID detection is bogus, so we use // our own method. See: // http://trac.osgeo.org/gdal/ticket/5131 @@ -21,115 +21,113 @@ ShpFormat.prototype._fileExtension = "zip"; // http://github.com/CartoDB/CartoDB-SQL-API/issues/116 ShpFormat.prototype._needSRS = true; -ShpFormat.prototype.generate = function(options, callback) { - this.toSHP(options, callback); +ShpFormat.prototype.generate = function (options, callback) { + this.toSHP(options, callback); }; ShpFormat.prototype.toSHP = function (options, callback) { - var dbname = options.database; - var user_id = options.user_id; - var gcol = options.gn; - var sql = options.sql; - var skipfields = options.skipfields; - var filename = options.filename; + var dbname = options.database; + var user_id = options.user_id; + var gcol = options.gn; + var sql = options.sql; + var skipfields = options.skipfields; + var filename = options.filename; - var fmtObj = this; - var zip = global.settings.zipCommand || 'zip'; - var zipOptions = '-qrj'; - var tmpdir = global.settings.tmpDir || '/tmp'; - var reqKey = [ 'shp', dbname, user_id, gcol, this.generateMD5(sql) ].concat(skipfields).join(':'); - var outdirpath = tmpdir + '/sqlapi-' + process.pid + '-' + reqKey; - var zipfile = outdirpath + '.zip'; - var shapefile = outdirpath + '/' + filename + '.shp'; + var fmtObj = this; + var zip = global.settings.zipCommand || 'zip'; + var zipOptions = '-qrj'; + var tmpdir = global.settings.tmpDir || '/tmp'; + var reqKey = ['shp', dbname, user_id, gcol, this.generateMD5(sql)].concat(skipfields).join(':'); + var outdirpath = tmpdir + '/sqlapi-' + process.pid + '-' + reqKey; + var zipfile = outdirpath + '.zip'; + var shapefile = outdirpath + '/' + filename + '.shp'; - // TODO: following tests: - // - fetch query with no "the_geom" column + // TODO: following tests: + // - fetch query with no "the_geom" column - step ( - function createOutDir() { - fs.mkdir(outdirpath, 0o777, this); - }, - function spawnDumper(err) { - if (err) { - throw err; - } + step( + function createOutDir () { + fs.mkdir(outdirpath, 0o777, this); + }, + function spawnDumper (err) { + if (err) { + throw err; + } - fmtObj.toOGR(options, 'ESRI Shapefile', shapefile, this); - }, - function doZip(err) { - if (err) { - throw err; - } + fmtObj.toOGR(options, 'ESRI Shapefile', shapefile, this); + }, + function doZip (err) { + if (err) { + throw err; + } - var next = this; + var next = this; - var child = spawn(zip, [zipOptions, zipfile, outdirpath ]); + var child = spawn(zip, [zipOptions, zipfile, outdirpath]); - child.on('error', function (err) { - next(new Error('Error executing zip command, ' + err)); - }); - - var stderrData = []; - child.stderr.setEncoding('utf8'); - child.stderr.on('data', function (data) { - stderrData.push(data); - }); - - child.on('exit', function(code) { - if (code !== 0) { - var errMessage = 'Zip command return code ' + code; - if (stderrData.length) { - errMessage += ', Error: ' + stderrData.join('\n'); - } - - return next(new Error(errMessage)); - } - - return next(); - }); - }, - function cleanupDir(topError) { - - var next = this; - - // Unlink the dir content - var unlinkall = function(dir, files, finish) { - var f = files.shift(); - if ( ! f ) { finish(null); return; } - var fn = dir + '/' + f; - fs.unlink(fn, function(err) { - if ( err ) { - console.log("Unlinking " + fn + ": " + err); - finish(err); - } else { - unlinkall(dir, files, finish); - } - }); - }; - fs.readdir(outdirpath, function(err, files) { - if ( err ) { - if ( err.code !== 'ENOENT' ) { - next(new Error([topError, err].join('\n'))); - } else { - next(topError); - } - } else { - unlinkall(outdirpath, files, function(/*err*/) { - fs.rmdir(outdirpath, function(err) { - if ( err ) { - console.log("Removing dir " + outdirpath + ": " + err); - } - next(topError, zipfile); + child.on('error', function (err) { + next(new Error('Error executing zip command, ' + err)); }); - }); + + var stderrData = []; + child.stderr.setEncoding('utf8'); + child.stderr.on('data', function (data) { + stderrData.push(data); + }); + + child.on('exit', function (code) { + if (code !== 0) { + var errMessage = 'Zip command return code ' + code; + if (stderrData.length) { + errMessage += ', Error: ' + stderrData.join('\n'); + } + + return next(new Error(errMessage)); + } + + return next(); + }); + }, + function cleanupDir (topError) { + var next = this; + + // Unlink the dir content + var unlinkall = function (dir, files, finish) { + var f = files.shift(); + if (!f) { finish(null); return; } + var fn = dir + '/' + f; + fs.unlink(fn, function (err) { + if (err) { + console.log('Unlinking ' + fn + ': ' + err); + finish(err); + } else { + unlinkall(dir, files, finish); + } + }); + }; + fs.readdir(outdirpath, function (err, files) { + if (err) { + if (err.code !== 'ENOENT') { + next(new Error([topError, err].join('\n'))); + } else { + next(topError); + } + } else { + unlinkall(outdirpath, files, function (/* err */) { + fs.rmdir(outdirpath, function (err) { + if (err) { + console.log('Removing dir ' + outdirpath + ': ' + err); + } + next(topError, zipfile); + }); + }); + } + }); + }, + function finalStep (err, zipfile) { + callback(err, zipfile); } - }); - }, - function finalStep(err, zipfile) { - callback(err, zipfile); - } - ); + ); }; - module.exports = ShpFormat; diff --git a/lib/models/formats/ogr/spatialite.js b/lib/models/formats/ogr/spatialite.js index 34819b18..98514cb6 100644 --- a/lib/models/formats/ogr/spatialite.js +++ b/lib/models/formats/ogr/spatialite.js @@ -2,12 +2,12 @@ var Ogr = require('./../ogr'); -function SpatiaLiteFormat() {} +function SpatiaLiteFormat () {} SpatiaLiteFormat.prototype = new Ogr('spatialite'); -SpatiaLiteFormat.prototype._contentType = "application/x-sqlite3; charset=utf-8"; -SpatiaLiteFormat.prototype._fileExtension = "sqlite"; +SpatiaLiteFormat.prototype._contentType = 'application/x-sqlite3; charset=utf-8'; +SpatiaLiteFormat.prototype._fileExtension = 'sqlite'; // As of GDAL 1.10.1 SRID detection is bogus, so we use // our own method. See: // http://trac.osgeo.org/gdal/ticket/5131 @@ -17,7 +17,7 @@ SpatiaLiteFormat.prototype._fileExtension = "sqlite"; // Bug was fixed in GDAL 1.10.2 SpatiaLiteFormat.prototype._needSRS = true; -SpatiaLiteFormat.prototype.generate = function(options, callback) { +SpatiaLiteFormat.prototype.generate = function (options, callback) { this.toOGR_SingleFile(options, 'SQLite', callback); options.cmd_params = ['SPATIALITE=yes']; }; diff --git a/lib/models/formats/pg.js b/lib/models/formats/pg.js index 56b44926..6a798b42 100644 --- a/lib/models/formats/pg.js +++ b/lib/models/formats/pg.js @@ -3,156 +3,156 @@ var step = require('step'); var PSQL = require('cartodb-psql'); -function PostgresFormat(id) { +function PostgresFormat (id) { this.id = id; } PostgresFormat.prototype = { - getQuery: function(sql/*, options*/) { - return sql; - }, + getQuery: function (sql/*, options */) { + return sql; + }, - getContentType: function(){ - return this._contentType; - }, + getContentType: function () { + return this._contentType; + }, - getFileExtension: function() { - return this.id; - } + getFileExtension: function () { + return this.id; + } }; -PostgresFormat.prototype.handleQueryRow = function(row, result) { +PostgresFormat.prototype.handleQueryRow = function (row, result) { result.addRow(row); }; -PostgresFormat.prototype.handleQueryRowWithSkipFields = function(row, result) { +PostgresFormat.prototype.handleQueryRowWithSkipFields = function (row, result) { var sf = this.opts.skipfields; - for ( var j=0; j ArrayBufferSer.BUFFER) { - row = new ArrayBufferSer(headerTypes[i] - ArrayBufferSer.BUFFER, row); + var headersNames = Object.keys(rows[0]); + var headerTypes = []; + + if (_.contains(headersNames, 'the_geom')) { + callback(new Error('geometry types are not supported'), null); + return; + } + + try { + var i; + var r; + var n; + var t; + // get header types (and guess from name) + 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') { + t = this._extractTypeFromName(n); + t = t || ArrayBufferSer.FLOAT32; + headerTypes.push(ArrayBufferSer.BUFFER + t); + } else { + t = this._extractTypeFromName(n); + headerTypes.push(t || ArrayBufferSer.FLOAT32); + } } - d.push(row); - } - var b = new ArrayBufferSer(headerTypes[i], d); - data.push(b); + + // pack the data + var header = new ArrayBufferSer(ArrayBufferSer.STRING, headersNames); + var data = [header]; + for (i = 0; i < headersNames.length; ++i) { + var d = []; + n = headersNames[i]; + for (r = 0; r < total_rows; ++r) { + var row = rows[r][n]; + if (headerTypes[i] > ArrayBufferSer.BUFFER) { + row = new ArrayBufferSer(headerTypes[i] - ArrayBufferSer.BUFFER, row); + } + d.push(row); + } + var b = new ArrayBufferSer(headerTypes[i], d); + data.push(b); + } + + // create the final buffer + var all = new ArrayBufferSer(ArrayBufferSer.BUFFER, data); + + callback(null, all.buffer); + } catch (e) { + callback(e, null); } - - // create the final buffer - var all = new ArrayBufferSer(ArrayBufferSer.BUFFER, data); - - callback(null, all.buffer); - - } catch(e) { - callback(e, null); - } }; module.exports = BinaryFormat; diff --git a/lib/models/formats/pg/geojson.js b/lib/models/formats/pg/geojson.js index 30f44600..9985656f 100644 --- a/lib/models/formats/pg/geojson.js +++ b/lib/models/formats/pg/geojson.js @@ -5,21 +5,21 @@ var _ = require('underscore'); var Pg = require('./../pg'); const errorHandlerFactory = require('../../../services/error-handler-factory'); -function GeoJsonFormat() { +function GeoJsonFormat () { this.buffer = ''; } GeoJsonFormat.prototype = new Pg('geojson'); -GeoJsonFormat.prototype._contentType = "application/json; charset=utf-8"; +GeoJsonFormat.prototype._contentType = 'application/json; charset=utf-8'; -GeoJsonFormat.prototype.getQuery = function(sql, options) { +GeoJsonFormat.prototype.getQuery = function (sql, options) { var gn = options.gn; var dp = options.dp; return 'SELECT *, ST_AsGeoJSON(' + gn + ',' + dp + ') as the_geom FROM (' + sql + ') as foo'; }; -GeoJsonFormat.prototype.startStreaming = function() { +GeoJsonFormat.prototype.startStreaming = function () { this.total_rows = 0; if (this.opts.beforeSink) { this.opts.beforeSink(); @@ -31,9 +31,8 @@ GeoJsonFormat.prototype.startStreaming = function() { this._streamingStarted = true; }; -GeoJsonFormat.prototype.handleQueryRow = function(row) { - - if ( ! this._streamingStarted ) { +GeoJsonFormat.prototype.handleQueryRow = function (row) { + if (!this._streamingStarted) { this.startStreaming(); } @@ -56,17 +55,17 @@ GeoJsonFormat.prototype.handleQueryRow = function(row) { } }; -GeoJsonFormat.prototype.handleQueryEnd = function(/*result*/) { +GeoJsonFormat.prototype.handleQueryEnd = function (/* result */) { if (this.error && !this._streamingStarted) { this.callback(this.error); return; } - if ( this.opts.profiler ) { + if (this.opts.profiler) { this.opts.profiler.done('gotRows'); } - if ( ! this._streamingStarted ) { + if (!this._streamingStarted) { this.startStreaming(); } @@ -89,31 +88,31 @@ GeoJsonFormat.prototype.handleQueryEnd = function(/*result*/) { this.callback(); }; -function _toGeoJSON(data, gn, callback){ - try { - var out = { - type: "FeatureCollection", - features: [] - }; +function _toGeoJSON (data, gn, callback) { + try { + var out = { + type: 'FeatureCollection', + features: [] + }; - _.each(data.rows, function(ele){ - var _geojson = { - type: "Feature", - properties: { }, - geometry: { } - }; - _geojson.geometry = JSON.parse(ele[gn]); - delete ele[gn]; - delete ele.the_geom_webmercator; // TODO: use skipfields - _geojson.properties = ele; - out.features.push(_geojson); - }); + _.each(data.rows, function (ele) { + var _geojson = { + type: 'Feature', + properties: { }, + geometry: { } + }; + _geojson.geometry = JSON.parse(ele[gn]); + delete ele[gn]; + delete ele.the_geom_webmercator; // TODO: use skipfields + _geojson.properties = ele; + out.features.push(_geojson); + }); - // return payload - callback(null, out); - } catch (err) { - callback(err,null); - } + // return payload + callback(null, out); + } catch (err) { + callback(err, null); + } } module.exports = GeoJsonFormat; diff --git a/lib/models/formats/pg/json.js b/lib/models/formats/pg/json.js index 26fdda71..dc11b868 100644 --- a/lib/models/formats/pg/json.js +++ b/lib/models/formats/pg/json.js @@ -2,62 +2,57 @@ var _ = require('underscore'); -var Pg = require('./../pg'); +var Pg = require('./../pg'); const errorHandlerFactory = require('../../../services/error-handler-factory'); -function JsonFormat() { +function JsonFormat () { this.buffer = ''; this.lastKnownResult = {}; } JsonFormat.prototype = new Pg('json'); -JsonFormat.prototype._contentType = "application/json; charset=utf-8"; +JsonFormat.prototype._contentType = 'application/json; charset=utf-8'; // jshint maxcomplexity:10 -JsonFormat.prototype.formatResultFields = function(flds) { - flds = flds || []; - var nfields = {}; - for (var i=0; i 0 ) { - var notices = {}, - severities = []; - _.each(result.notices, function(notice) { + if (result.notices && result.notices.length > 0) { + var notices = {}; + var severities = []; + _.each(result.notices, function (notice) { var severity = notice.severity.toLowerCase() + 's'; if (!notices[severity]) { severities.push(severity); @@ -155,7 +149,7 @@ JsonFormat.prototype.handleQueryEnd = function(result) { } notices[severity].push(notice.message); }); - _.each(severities, function(severity) { + _.each(severities, function (severity) { out.push(','); out.push(JSON.stringify(severity)); out.push(':'); @@ -165,7 +159,6 @@ JsonFormat.prototype.handleQueryEnd = function(result) { out.push('}'); - this.buffer += out.join(''); if (this.opts.callback) { diff --git a/lib/models/formats/pg/svg.js b/lib/models/formats/pg/svg.js index 6eaa8823..f6e0e45a 100644 --- a/lib/models/formats/pg/svg.js +++ b/lib/models/formats/pg/svg.js @@ -1,10 +1,10 @@ 'use strict'; -var Pg = require('./../pg'); +var Pg = require('./../pg'); -var svg_width = 1024.0; +var svg_width = 1024.0; var svg_height = 768.0; -var svg_ratio = svg_width/svg_height; +var svg_ratio = svg_width / svg_height; var radius = 5; // in pixels (based on svg_width and svg_height) @@ -15,7 +15,7 @@ var fill_opacity = 0.5; // 0.0 is fully transparent, 1.0 is fully opaque // unused if fill_color='none' var fill_color = 'none'; // affects polygons and circles -function SvgFormat() { +function SvgFormat () { this.totalRows = 0; this.bbox = null; // will be computed during the results scan @@ -25,12 +25,12 @@ function SvgFormat() { } SvgFormat.prototype = new Pg('svg'); -SvgFormat.prototype._contentType = "image/svg+xml; charset=utf-8"; +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 ( ' + +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, ' + @@ -38,21 +38,21 @@ SvgFormat.prototype.getQuery = function(sql, options) { '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 ' + + '/ COALESCE(NULLIF(ew,0),' + svg_width + ') WHEN ' + svg_ratio + ' <= (ew / eh) THEN (' + - svg_width + '/ew ) ELSE (' + + 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 + + // + ', ex0, ey0, ew, eh, s ' // DEBUG ONLY + ' FROM trans, extent_info, source' + ' ORDER BY the_geom_dimension ASC'; }; -SvgFormat.prototype.startStreaming = function() { +SvgFormat.prototype.startStreaming = function () { if (this.opts.beforeSink) { this.opts.beforeSink(); } @@ -63,7 +63,7 @@ SvgFormat.prototype.startStreaming = function() { ]; var rootTag = '\n'; - } else if ( gdims == '1' ) { + } else if (gdims == '1') { // Avoid filling closed linestrings - this.buffer += '\n'; - } else if ( gdims == '2' ) { + this.buffer += '\n'; + } else if (gdims == '2') { this.buffer += '\n'; } // jshint ignore:end - if ( ! this.bbox ) { + if (!this.bbox) { // Parse layer extent: "BOX(x y, X Y)" // NOTE: the name of the extent field is // determined by the same code adding the @@ -140,13 +140,13 @@ SvgFormat.prototype.handleQueryRow = function(row) { } }; -SvgFormat.prototype.handleQueryEnd = function() { - if ( this.error && !this._streamingStarted) { +SvgFormat.prototype.handleQueryEnd = function () { + if (this.error && !this._streamingStarted) { this.callback(this.error); return; } - if ( this.opts.profiler ) { + if (this.opts.profiler) { this.opts.profiler.done('gotRows'); } diff --git a/lib/models/formats/pg/topojson.js b/lib/models/formats/pg/topojson.js index cb58ef1e..4a8535a9 100644 --- a/lib/models/formats/pg/topojson.js +++ b/lib/models/formats/pg/topojson.js @@ -2,24 +2,24 @@ // jshint ignore:start -var Pg = require('./../pg'); +var Pg = require('./../pg'); var _ = require('underscore'); var geojson = require('./geojson'); var TopoJSON = require('topojson'); -function TopoJsonFormat() { +function TopoJsonFormat () { this.features = []; } TopoJsonFormat.prototype = new Pg('topojson'); -TopoJsonFormat.prototype.getQuery = function(sql, options) { - return geojson.prototype.getQuery(sql, options) + ' where ' + options.gn + ' is not null'; +TopoJsonFormat.prototype.getQuery = function (sql, options) { + return geojson.prototype.getQuery(sql, options) + ' where ' + options.gn + ' is not null'; }; -TopoJsonFormat.prototype.handleQueryRow = function(row) { +TopoJsonFormat.prototype.handleQueryRow = function (row) { var _geojson = { - type: "Feature" + type: 'Feature' }; _geojson.geometry = JSON.parse(row[this.opts.gn]); delete row[this.opts.gn]; @@ -28,20 +28,20 @@ TopoJsonFormat.prototype.handleQueryRow = function(row) { this.features.push(_geojson); }; -TopoJsonFormat.prototype.handleQueryEnd = function() { +TopoJsonFormat.prototype.handleQueryEnd = function () { if (this.error) { this.callback(this.error); return; } - if ( this.opts.profiler ) { + if (this.opts.profiler) { this.opts.profiler.done('gotRows'); } var topology = TopoJSON.topology(this.features, { - "quantization": 1e4, - "force-clockwise": true, - "property-filter": function(d) { + quantization: 1e4, + 'force-clockwise': true, + 'property-filter': function (d) { return d; } }); @@ -55,19 +55,19 @@ TopoJsonFormat.prototype.handleQueryEnd = function() { var immediately = global.setImmediate || process.nextTick; - function streamObjectSubtree(obj, key, done) { + function streamObjectSubtree (obj, key, done) { buffer += '"' + key + '":'; - var isObject = _.isObject(obj[key]), - isArray = _.isArray(obj[key]), - isIterable = isArray || isObject; + var isObject = _.isObject(obj[key]); + var isArray = _.isArray(obj[key]); + var isIterable = isArray || isObject; if (isIterable) { buffer += isArray ? '[' : '{'; var subtreeKeys = Object.keys(obj[key]); var pos = 0; - function streamNext() { - immediately(function() { + function streamNext () { + immediately(function () { var subtreeKey = subtreeKeys.shift(); if (!isArray) { buffer += '"' + subtreeKey + '":'; @@ -103,10 +103,10 @@ TopoJsonFormat.prototype.handleQueryEnd = function() { } buffer += '{'; var keys = Object.keys(topology); - function sendResponse() { + function sendResponse () { immediately(function () { var key = keys.shift(); - function done() { + function done () { if (keys.length > 0) { delete topology[key]; buffer += ','; @@ -129,7 +129,7 @@ TopoJsonFormat.prototype.handleQueryEnd = function() { this.callback(); }; -TopoJsonFormat.prototype.cancel = function() { +TopoJsonFormat.prototype.cancel = function () { if (this.queryCanceller) { this.queryCanceller.call(); } diff --git a/lib/monitoring/health-check.js b/lib/monitoring/health-check.js index 5aa068d1..c579b771 100644 --- a/lib/monitoring/health-check.js +++ b/lib/monitoring/health-check.js @@ -1,34 +1,34 @@ 'use strict'; -var step = require('step'), - fs = require('fs'); +var step = require('step'); +var fs = require('fs'); -function HealthCheck(disableFile) { +function HealthCheck (disableFile) { this.disableFile = disableFile; } module.exports = HealthCheck; -HealthCheck.prototype.check = function(callback) { +HealthCheck.prototype.check = function (callback) { var self = this; step( - function getManualDisable() { - fs.readFile(self.disableFile, this); + function getManualDisable () { + fs.readFile(self.disableFile, this); }, - function handleDisabledFile(err, data) { - var next = this; - if (err) { - return next(); - } - if (!!data) { - err = new Error(data); - err.http_status = 503; - throw err; - } + function handleDisabledFile (err, data) { + var next = this; + if (err) { + return next(); + } + if (data) { + err = new Error(data); + err.http_status = 503; + throw err; + } }, - function handleResult(err) { - callback(err); + function handleResult (err) { + callback(err); } ); }; diff --git a/lib/postgresql/error-codes.js b/lib/postgresql/error-codes.js index 1bc14e6a..07f0a386 100644 --- a/lib/postgresql/error-codes.js +++ b/lib/postgresql/error-codes.js @@ -6,283 +6,283 @@ var _ = require('underscore'); // Used `^([A-Z0-9]*)\s(.*)` -> `"$1": "$2"` to create the JS object var codeToCondition = { // Class 00 — Successful Completion - "00000": "successful_completion", -// Class 01 — Warning - "01000": "warning", - "0100C": "dynamic_result_sets_returned", - "01008": "implicit_zero_bit_padding", - "01003": "null_value_eliminated_in_set_function", - "01007": "privilege_not_granted", - "01006": "privilege_not_revoked", - "01004": "string_data_right_truncation", - "01P01": "deprecated_feature", -// Class 02 — No Data (this is also a warning class per the SQL standard) - "02000": "no_data", - "02001": "no_additional_dynamic_result_sets_returned", -// Class 03 — SQL Statement Not Yet Complete - "03000": "sql_statement_not_yet_complete", -// Class 08 — Connection Exception - "08000": "connection_exception", - "08003": "connection_does_not_exist", - "08006": "connection_failure", - "08001": "sqlclient_unable_to_establish_sqlconnection", - "08004": "sqlserver_rejected_establishment_of_sqlconnection", - "08007": "transaction_resolution_unknown", - "08P01": "protocol_violation", -// Class 09 — Triggered Action Exception - "09000": "triggered_action_exception", -// Class 0A — Feature Not Supported - "0A000": "feature_not_supported", -// Class 0B — Invalid Transaction Initiation - "0B000": "invalid_transaction_initiation", -// Class 0F — Locator Exception - "0F000": "locator_exception", - "0F001": "invalid_locator_specification", -// Class 0L — Invalid Grantor - "0L000": "invalid_grantor", - "0LP01": "invalid_grant_operation", -// Class 0P — Invalid Role Specification - "0P000": "invalid_role_specification", -// Class 0Z — Diagnostics Exception - "0Z000": "diagnostics_exception", - "0Z002": "stacked_diagnostics_accessed_without_active_handler", -// Class 20 — Case Not Found - "20000": "case_not_found", -// Class 21 — Cardinality Violation - "21000": "cardinality_violation", -// Class 22 — Data Exception - "22000": "data_exception", - "2202E": "array_subscript_error", - "22021": "character_not_in_repertoire", - "22008": "datetime_field_overflow", - "22012": "division_by_zero", - "22005": "error_in_assignment", - "2200B": "escape_character_conflict", - "22022": "indicator_overflow", - "22015": "interval_field_overflow", - "2201E": "invalid_argument_for_logarithm", - "22014": "invalid_argument_for_ntile_function", - "22016": "invalid_argument_for_nth_value_function", - "2201F": "invalid_argument_for_power_function", - "2201G": "invalid_argument_for_width_bucket_function", - "22018": "invalid_character_value_for_cast", - "22007": "invalid_datetime_format", - "22019": "invalid_escape_character", - "2200D": "invalid_escape_octet", - "22025": "invalid_escape_sequence", - "22P06": "nonstandard_use_of_escape_character", - "22010": "invalid_indicator_parameter_value", - "22023": "invalid_parameter_value", - "2201B": "invalid_regular_expression", - "2201W": "invalid_row_count_in_limit_clause", - "2201X": "invalid_row_count_in_result_offset_clause", - "22009": "invalid_time_zone_displacement_value", - "2200C": "invalid_use_of_escape_character", - "2200G": "most_specific_type_mismatch", - "22004": "null_value_not_allowed", - "22002": "null_value_no_indicator_parameter", - "22003": "numeric_value_out_of_range", - "22026": "string_data_length_mismatch", - "22001": "string_data_right_truncation", - "22011": "substring_error", - "22027": "trim_error", - "22024": "unterminated_c_string", - "2200F": "zero_length_character_string", - "22P01": "floating_point_exception", - "22P02": "invalid_text_representation", - "22P03": "invalid_binary_representation", - "22P04": "bad_copy_file_format", - "22P05": "untranslatable_character", - "2200L": "not_an_xml_document", - "2200M": "invalid_xml_document", - "2200N": "invalid_xml_content", - "2200S": "invalid_xml_comment", - "2200T": "invalid_xml_processing_instruction", -// Class 23 — Integrity Constraint Violation - "23000": "integrity_constraint_violation", - "23001": "restrict_violation", - "23502": "not_null_violation", - "23503": "foreign_key_violation", - "23505": "unique_violation", - "23514": "check_violation", - "23P01": "exclusion_violation", -// Class 24 — Invalid Cursor State - "24000": "invalid_cursor_state", -// Class 25 — Invalid Transaction State - "25000": "invalid_transaction_state", - "25001": "active_sql_transaction", - "25002": "branch_transaction_already_active", - "25008": "held_cursor_requires_same_isolation_level", - "25003": "inappropriate_access_mode_for_branch_transaction", - "25004": "inappropriate_isolation_level_for_branch_transaction", - "25005": "no_active_sql_transaction_for_branch_transaction", - "25006": "read_only_sql_transaction", - "25007": "schema_and_data_statement_mixing_not_supported", - "25P01": "no_active_sql_transaction", - "25P02": "in_failed_sql_transaction", - "25P03": "idle_in_transaction_session_timeout", -// Class 26 — Invalid SQL Statement Name - "26000": "invalid_sql_statement_name", -// Class 27 — Triggered Data Change Violation - "27000": "triggered_data_change_violation", -// Class 28 — Invalid Authorization Specification - "28000": "invalid_authorization_specification", - "28P01": "invalid_password", -// Class 2B — Dependent Privilege Descriptors Still Exist - "2B000": "dependent_privilege_descriptors_still_exist", - "2BP01": "dependent_objects_still_exist", -// Class 2D — Invalid Transaction Termination - "2D000": "invalid_transaction_termination", -// Class 2F — SQL Routine Exception - "2F000": "sql_routine_exception", - "2F005": "function_executed_no_return_statement", - "2F002": "modifying_sql_data_not_permitted", - "2F003": "prohibited_sql_statement_attempted", - "2F004": "reading_sql_data_not_permitted", -// Class 34 — Invalid Cursor Name - "34000": "invalid_cursor_name", -// Class 38 — External Routine Exception - "38000": "external_routine_exception", - "38001": "containing_sql_not_permitted", - "38002": "modifying_sql_data_not_permitted", - "38003": "prohibited_sql_statement_attempted", - "38004": "reading_sql_data_not_permitted", -// Class 39 — External Routine Invocation Exception - "39000": "external_routine_invocation_exception", - "39001": "invalid_sqlstate_returned", - "39004": "null_value_not_allowed", - "39P01": "trigger_protocol_violated", - "39P02": "srf_protocol_violated", - "39P03": "event_trigger_protocol_violated", -// Class 3B — Savepoint Exception - "3B000": "savepoint_exception", - "3B001": "invalid_savepoint_specification", -// Class 3D — Invalid Catalog Name - "3D000": "invalid_catalog_name", -// Class 3F — Invalid Schema Name - "3F000": "invalid_schema_name", -// Class 40 — Transaction Rollback - "40000": "transaction_rollback", - "40002": "transaction_integrity_constraint_violation", - "40001": "serialization_failure", - "40003": "statement_completion_unknown", - "40P01": "deadlock_detected", -// Class 42 — Syntax Error or Access Rule Violation - "42000": "syntax_error_or_access_rule_violation", - "42601": "syntax_error", - "42501": "insufficient_privilege", - "42846": "cannot_coerce", - "42803": "grouping_error", - "42P20": "windowing_error", - "42P19": "invalid_recursion", - "42830": "invalid_foreign_key", - "42602": "invalid_name", - "42622": "name_too_long", - "42939": "reserved_name", - "42804": "datatype_mismatch", - "42P18": "indeterminate_datatype", - "42P21": "collation_mismatch", - "42P22": "indeterminate_collation", - "42809": "wrong_object_type", - "428C9": "generated_always", - "42703": "undefined_column", - "42883": "undefined_function", - "42P01": "undefined_table", - "42P02": "undefined_parameter", - "42704": "undefined_object", - "42701": "duplicate_column", - "42P03": "duplicate_cursor", - "42P04": "duplicate_database", - "42723": "duplicate_function", - "42P05": "duplicate_prepared_statement", - "42P06": "duplicate_schema", - "42P07": "duplicate_table", - "42712": "duplicate_alias", - "42710": "duplicate_object", - "42702": "ambiguous_column", - "42725": "ambiguous_function", - "42P08": "ambiguous_parameter", - "42P09": "ambiguous_alias", - "42P10": "invalid_column_reference", - "42611": "invalid_column_definition", - "42P11": "invalid_cursor_definition", - "42P12": "invalid_database_definition", - "42P13": "invalid_function_definition", - "42P14": "invalid_prepared_statement_definition", - "42P15": "invalid_schema_definition", - "42P16": "invalid_table_definition", - "42P17": "invalid_object_definition", -// Class 44 — WITH CHECK OPTION Violation - "44000": "with_check_option_violation", -// Class 53 — Insufficient Resources - "53000": "insufficient_resources", - "53100": "disk_full", - "53200": "out_of_memory", - "53300": "too_many_connections", - "53400": "configuration_limit_exceeded", -// Class 54 — Program Limit Exceeded - "54000": "program_limit_exceeded", - "54001": "statement_too_complex", - "54011": "too_many_columns", - "54023": "too_many_arguments", -// Class 55 — Object Not In Prerequisite State - "55000": "object_not_in_prerequisite_state", - "55006": "object_in_use", - "55P02": "cant_change_runtime_param", - "55P03": "lock_not_available", -// Class 57 — Operator Intervention - "57000": "operator_intervention", - "57014": "query_canceled", - "57P01": "admin_shutdown", - "57P02": "crash_shutdown", - "57P03": "cannot_connect_now", - "57P04": "database_dropped", -// Class 58 — System Error (errors external to PostgreSQL itself) - "58000": "system_error", - "58030": "io_error", - "58P01": "undefined_file", - "58P02": "duplicate_file", -// Class F0 — Configuration File Error - "F0000": "config_file_error", - "F0001": "lock_file_exists", -// Class HV — Foreign Data Wrapper Error (SQL/MED) - "HV000": "fdw_error", - "HV005": "fdw_column_name_not_found", - "HV002": "fdw_dynamic_parameter_value_needed", - "HV010": "fdw_function_sequence_error", - "HV021": "fdw_inconsistent_descriptor_information", - "HV024": "fdw_invalid_attribute_value", - "HV007": "fdw_invalid_column_name", - "HV008": "fdw_invalid_column_number", - "HV004": "fdw_invalid_data_type", - "HV006": "fdw_invalid_data_type_descriptors", - "HV091": "fdw_invalid_descriptor_field_identifier", - "HV00B": "fdw_invalid_handle", - "HV00C": "fdw_invalid_option_index", - "HV00D": "fdw_invalid_option_name", - "HV090": "fdw_invalid_string_length_or_buffer_length", - "HV00A": "fdw_invalid_string_format", - "HV009": "fdw_invalid_use_of_null_pointer", - "HV014": "fdw_too_many_handles", - "HV001": "fdw_out_of_memory", - "HV00P": "fdw_no_schemas", - "HV00J": "fdw_option_name_not_found", - "HV00K": "fdw_reply_handle", - "HV00Q": "fdw_schema_not_found", - "HV00R": "fdw_table_not_found", - "HV00L": "fdw_unable_to_create_execution", - "HV00M": "fdw_unable_to_create_reply", - "HV00N": "fdw_unable_to_establish_connection", -// Class P0 — PL/pgSQL Error - "P0000": "plpgsql_error", - "P0001": "raise_exception", - "P0002": "no_data_found", - "P0003": "too_many_rows", - "P0004": "assert_failure", -// Class XX — Internal Error - "XX000": "internal_error", - "XX001": "data_corrupted", - "XX002": "index_corrupted" + '00000': 'successful_completion', + // Class 01 — Warning + '01000': 'warning', + '0100C': 'dynamic_result_sets_returned', + '01008': 'implicit_zero_bit_padding', + '01003': 'null_value_eliminated_in_set_function', + '01007': 'privilege_not_granted', + '01006': 'privilege_not_revoked', + '01004': 'string_data_right_truncation', + '01P01': 'deprecated_feature', + // Class 02 — No Data (this is also a warning class per the SQL standard) + '02000': 'no_data', + '02001': 'no_additional_dynamic_result_sets_returned', + // Class 03 — SQL Statement Not Yet Complete + '03000': 'sql_statement_not_yet_complete', + // Class 08 — Connection Exception + '08000': 'connection_exception', + '08003': 'connection_does_not_exist', + '08006': 'connection_failure', + '08001': 'sqlclient_unable_to_establish_sqlconnection', + '08004': 'sqlserver_rejected_establishment_of_sqlconnection', + '08007': 'transaction_resolution_unknown', + '08P01': 'protocol_violation', + // Class 09 — Triggered Action Exception + '09000': 'triggered_action_exception', + // Class 0A — Feature Not Supported + '0A000': 'feature_not_supported', + // Class 0B — Invalid Transaction Initiation + '0B000': 'invalid_transaction_initiation', + // Class 0F — Locator Exception + '0F000': 'locator_exception', + '0F001': 'invalid_locator_specification', + // Class 0L — Invalid Grantor + '0L000': 'invalid_grantor', + '0LP01': 'invalid_grant_operation', + // Class 0P — Invalid Role Specification + '0P000': 'invalid_role_specification', + // Class 0Z — Diagnostics Exception + '0Z000': 'diagnostics_exception', + '0Z002': 'stacked_diagnostics_accessed_without_active_handler', + // Class 20 — Case Not Found + 20000: 'case_not_found', + // Class 21 — Cardinality Violation + 21000: 'cardinality_violation', + // Class 22 — Data Exception + 22000: 'data_exception', + '2202E': 'array_subscript_error', + 22021: 'character_not_in_repertoire', + 22008: 'datetime_field_overflow', + 22012: 'division_by_zero', + 22005: 'error_in_assignment', + '2200B': 'escape_character_conflict', + 22022: 'indicator_overflow', + 22015: 'interval_field_overflow', + '2201E': 'invalid_argument_for_logarithm', + 22014: 'invalid_argument_for_ntile_function', + 22016: 'invalid_argument_for_nth_value_function', + '2201F': 'invalid_argument_for_power_function', + '2201G': 'invalid_argument_for_width_bucket_function', + 22018: 'invalid_character_value_for_cast', + 22007: 'invalid_datetime_format', + 22019: 'invalid_escape_character', + '2200D': 'invalid_escape_octet', + 22025: 'invalid_escape_sequence', + '22P06': 'nonstandard_use_of_escape_character', + 22010: 'invalid_indicator_parameter_value', + 22023: 'invalid_parameter_value', + '2201B': 'invalid_regular_expression', + '2201W': 'invalid_row_count_in_limit_clause', + '2201X': 'invalid_row_count_in_result_offset_clause', + 22009: 'invalid_time_zone_displacement_value', + '2200C': 'invalid_use_of_escape_character', + '2200G': 'most_specific_type_mismatch', + 22004: 'null_value_not_allowed', + 22002: 'null_value_no_indicator_parameter', + 22003: 'numeric_value_out_of_range', + 22026: 'string_data_length_mismatch', + 22001: 'string_data_right_truncation', + 22011: 'substring_error', + 22027: 'trim_error', + 22024: 'unterminated_c_string', + '2200F': 'zero_length_character_string', + '22P01': 'floating_point_exception', + '22P02': 'invalid_text_representation', + '22P03': 'invalid_binary_representation', + '22P04': 'bad_copy_file_format', + '22P05': 'untranslatable_character', + '2200L': 'not_an_xml_document', + '2200M': 'invalid_xml_document', + '2200N': 'invalid_xml_content', + '2200S': 'invalid_xml_comment', + '2200T': 'invalid_xml_processing_instruction', + // Class 23 — Integrity Constraint Violation + 23000: 'integrity_constraint_violation', + 23001: 'restrict_violation', + 23502: 'not_null_violation', + 23503: 'foreign_key_violation', + 23505: 'unique_violation', + 23514: 'check_violation', + '23P01': 'exclusion_violation', + // Class 24 — Invalid Cursor State + 24000: 'invalid_cursor_state', + // Class 25 — Invalid Transaction State + 25000: 'invalid_transaction_state', + 25001: 'active_sql_transaction', + 25002: 'branch_transaction_already_active', + 25008: 'held_cursor_requires_same_isolation_level', + 25003: 'inappropriate_access_mode_for_branch_transaction', + 25004: 'inappropriate_isolation_level_for_branch_transaction', + 25005: 'no_active_sql_transaction_for_branch_transaction', + 25006: 'read_only_sql_transaction', + 25007: 'schema_and_data_statement_mixing_not_supported', + '25P01': 'no_active_sql_transaction', + '25P02': 'in_failed_sql_transaction', + '25P03': 'idle_in_transaction_session_timeout', + // Class 26 — Invalid SQL Statement Name + 26000: 'invalid_sql_statement_name', + // Class 27 — Triggered Data Change Violation + 27000: 'triggered_data_change_violation', + // Class 28 — Invalid Authorization Specification + 28000: 'invalid_authorization_specification', + '28P01': 'invalid_password', + // Class 2B — Dependent Privilege Descriptors Still Exist + '2B000': 'dependent_privilege_descriptors_still_exist', + '2BP01': 'dependent_objects_still_exist', + // Class 2D — Invalid Transaction Termination + '2D000': 'invalid_transaction_termination', + // Class 2F — SQL Routine Exception + '2F000': 'sql_routine_exception', + '2F005': 'function_executed_no_return_statement', + '2F002': 'modifying_sql_data_not_permitted', + '2F003': 'prohibited_sql_statement_attempted', + '2F004': 'reading_sql_data_not_permitted', + // Class 34 — Invalid Cursor Name + 34000: 'invalid_cursor_name', + // Class 38 — External Routine Exception + 38000: 'external_routine_exception', + 38001: 'containing_sql_not_permitted', + 38002: 'modifying_sql_data_not_permitted', + 38003: 'prohibited_sql_statement_attempted', + 38004: 'reading_sql_data_not_permitted', + // Class 39 — External Routine Invocation Exception + 39000: 'external_routine_invocation_exception', + 39001: 'invalid_sqlstate_returned', + 39004: 'null_value_not_allowed', + '39P01': 'trigger_protocol_violated', + '39P02': 'srf_protocol_violated', + '39P03': 'event_trigger_protocol_violated', + // Class 3B — Savepoint Exception + '3B000': 'savepoint_exception', + '3B001': 'invalid_savepoint_specification', + // Class 3D — Invalid Catalog Name + '3D000': 'invalid_catalog_name', + // Class 3F — Invalid Schema Name + '3F000': 'invalid_schema_name', + // Class 40 — Transaction Rollback + 40000: 'transaction_rollback', + 40002: 'transaction_integrity_constraint_violation', + 40001: 'serialization_failure', + 40003: 'statement_completion_unknown', + '40P01': 'deadlock_detected', + // Class 42 — Syntax Error or Access Rule Violation + 42000: 'syntax_error_or_access_rule_violation', + 42601: 'syntax_error', + 42501: 'insufficient_privilege', + 42846: 'cannot_coerce', + 42803: 'grouping_error', + '42P20': 'windowing_error', + '42P19': 'invalid_recursion', + 42830: 'invalid_foreign_key', + 42602: 'invalid_name', + 42622: 'name_too_long', + 42939: 'reserved_name', + 42804: 'datatype_mismatch', + '42P18': 'indeterminate_datatype', + '42P21': 'collation_mismatch', + '42P22': 'indeterminate_collation', + 42809: 'wrong_object_type', + '428C9': 'generated_always', + 42703: 'undefined_column', + 42883: 'undefined_function', + '42P01': 'undefined_table', + '42P02': 'undefined_parameter', + 42704: 'undefined_object', + 42701: 'duplicate_column', + '42P03': 'duplicate_cursor', + '42P04': 'duplicate_database', + 42723: 'duplicate_function', + '42P05': 'duplicate_prepared_statement', + '42P06': 'duplicate_schema', + '42P07': 'duplicate_table', + 42712: 'duplicate_alias', + 42710: 'duplicate_object', + 42702: 'ambiguous_column', + 42725: 'ambiguous_function', + '42P08': 'ambiguous_parameter', + '42P09': 'ambiguous_alias', + '42P10': 'invalid_column_reference', + 42611: 'invalid_column_definition', + '42P11': 'invalid_cursor_definition', + '42P12': 'invalid_database_definition', + '42P13': 'invalid_function_definition', + '42P14': 'invalid_prepared_statement_definition', + '42P15': 'invalid_schema_definition', + '42P16': 'invalid_table_definition', + '42P17': 'invalid_object_definition', + // Class 44 — WITH CHECK OPTION Violation + 44000: 'with_check_option_violation', + // Class 53 — Insufficient Resources + 53000: 'insufficient_resources', + 53100: 'disk_full', + 53200: 'out_of_memory', + 53300: 'too_many_connections', + 53400: 'configuration_limit_exceeded', + // Class 54 — Program Limit Exceeded + 54000: 'program_limit_exceeded', + 54001: 'statement_too_complex', + 54011: 'too_many_columns', + 54023: 'too_many_arguments', + // Class 55 — Object Not In Prerequisite State + 55000: 'object_not_in_prerequisite_state', + 55006: 'object_in_use', + '55P02': 'cant_change_runtime_param', + '55P03': 'lock_not_available', + // Class 57 — Operator Intervention + 57000: 'operator_intervention', + 57014: 'query_canceled', + '57P01': 'admin_shutdown', + '57P02': 'crash_shutdown', + '57P03': 'cannot_connect_now', + '57P04': 'database_dropped', + // Class 58 — System Error (errors external to PostgreSQL itself) + 58000: 'system_error', + 58030: 'io_error', + '58P01': 'undefined_file', + '58P02': 'duplicate_file', + // Class F0 — Configuration File Error + F0000: 'config_file_error', + F0001: 'lock_file_exists', + // Class HV — Foreign Data Wrapper Error (SQL/MED) + HV000: 'fdw_error', + HV005: 'fdw_column_name_not_found', + HV002: 'fdw_dynamic_parameter_value_needed', + HV010: 'fdw_function_sequence_error', + HV021: 'fdw_inconsistent_descriptor_information', + HV024: 'fdw_invalid_attribute_value', + HV007: 'fdw_invalid_column_name', + HV008: 'fdw_invalid_column_number', + HV004: 'fdw_invalid_data_type', + HV006: 'fdw_invalid_data_type_descriptors', + HV091: 'fdw_invalid_descriptor_field_identifier', + HV00B: 'fdw_invalid_handle', + HV00C: 'fdw_invalid_option_index', + HV00D: 'fdw_invalid_option_name', + HV090: 'fdw_invalid_string_length_or_buffer_length', + HV00A: 'fdw_invalid_string_format', + HV009: 'fdw_invalid_use_of_null_pointer', + HV014: 'fdw_too_many_handles', + HV001: 'fdw_out_of_memory', + HV00P: 'fdw_no_schemas', + HV00J: 'fdw_option_name_not_found', + HV00K: 'fdw_reply_handle', + HV00Q: 'fdw_schema_not_found', + HV00R: 'fdw_table_not_found', + HV00L: 'fdw_unable_to_create_execution', + HV00M: 'fdw_unable_to_create_reply', + HV00N: 'fdw_unable_to_establish_connection', + // Class P0 — PL/pgSQL Error + P0000: 'plpgsql_error', + P0001: 'raise_exception', + P0002: 'no_data_found', + P0003: 'too_many_rows', + P0004: 'assert_failure', + // Class XX — Internal Error + XX000: 'internal_error', + XX001: 'data_corrupted', + XX002: 'index_corrupted' }; module.exports.codeToCondition = codeToCondition; diff --git a/lib/server-options.js b/lib/server-options.js index 448e35de..51448a4e 100644 --- a/lib/server-options.js +++ b/lib/server-options.js @@ -11,7 +11,7 @@ module.exports = function getServerOptions () { // In case the path has a :user param the username will be the one specified in the URL, // otherwise it will fallback to extract the username from the host header. '/api/:version', - '/user/:user/api/:version', + '/user/:user/api/:version' ], // Optional: attach middlewares at the begining of the router // to perform custom operations. diff --git a/lib/server.js b/lib/server.js index 114fde69..9fc17b33 100644 --- a/lib/server.js +++ b/lib/server.js @@ -30,7 +30,7 @@ module.exports = function createServer (statsClient) { const metadataBackend = cartodbRedis({ pool: redisPool }); // Set default configuration - global.settings.db_pubuser = global.settings.db_pubuser || "publicuser"; + global.settings.db_pubuser = global.settings.db_pubuser || 'publicuser'; global.settings.bufferedRows = global.settings.bufferedRows || 1000; global.settings.ratelimits = Object.assign( { @@ -54,7 +54,7 @@ module.exports = function createServer (statsClient) { } app.enable('jsonp callback'); - app.set("trust proxy", true); + app.set('trust proxy', true); app.disable('x-powered-by'); app.disable('etag'); diff --git a/lib/services/error-handler-factory.js b/lib/services/error-handler-factory.js index 2186ea77..44e2adf2 100644 --- a/lib/services/error-handler-factory.js +++ b/lib/services/error-handler-factory.js @@ -11,7 +11,7 @@ module.exports = function ErrorHandlerFactory (err) { } }; -function isTimeoutError(err) { +function isTimeoutError (err) { return err.message && ( err.message.indexOf('statement timeout') > -1 || err.message.indexOf('RuntimeError: Execution of function interrupted by signal') > -1 || @@ -19,7 +19,7 @@ function isTimeoutError(err) { ); } -function createTimeoutError() { +function createTimeoutError () { return new ErrorHandler({ message: 'You are over platform\'s limits: SQL query timeout error.' + ' Refactor your query before running again or contact CARTO support for more details.', @@ -29,7 +29,7 @@ function createTimeoutError() { }); } -function createGenericError(err) { +function createGenericError (err) { return new ErrorHandler({ message: err.message, context: err.context, diff --git a/lib/services/error-handler.js b/lib/services/error-handler.js index 0f147403..b59bb08c 100644 --- a/lib/services/error-handler.js +++ b/lib/services/error-handler.js @@ -1,7 +1,7 @@ 'use strict'; class ErrorHandler extends Error { - constructor({ message, context, detail, hint, http_status, name }) { + constructor ({ message, context, detail, hint, http_status, name }) { super(message); this.http_status = this.getHttpStatus(http_status); @@ -14,7 +14,7 @@ class ErrorHandler extends Error { } } - getResponse() { + getResponse () { return { error: [this.message], context: this.context, @@ -23,14 +23,13 @@ class ErrorHandler extends Error { }; } - getHttpStatus(http_status = 400) { + getHttpStatus (http_status = 400) { if (this.message.includes('permission denied')) { return 403; } return http_status; } - } module.exports = ErrorHandler; diff --git a/lib/services/logger.js b/lib/services/logger.js index 17924864..9966a490 100644 --- a/lib/services/logger.js +++ b/lib/services/logger.js @@ -7,7 +7,7 @@ class Logger { const env = process.env.NODE_ENV; const logLevel = process.env.LOG_LEVEL; const stream = { - level: logLevel ? logLevel : (env === 'test') ? 'fatal' : (env === 'development') ? 'debug' : 'info' + level: logLevel || ((env === 'test') ? 'fatal' : (env === 'development') ? 'debug' : 'info') }; if (path) { diff --git a/lib/services/pg-entities-access-validator.js b/lib/services/pg-entities-access-validator.js index bf0ed369..ae26fef3 100644 --- a/lib/services/pg-entities-access-validator.js +++ b/lib/services/pg-entities-access-validator.js @@ -17,7 +17,7 @@ const FORBIDDEN_ENTITIES = { }; const Validator = { - validate(affectedTables, authorizationLevel) { + validate (affectedTables, authorizationLevel) { let hardValidationResult = true; let softValidationResult = true; @@ -34,8 +34,8 @@ const Validator = { return hardValidationResult && softValidationResult; }, - hardValidation(tables) { - for (let table of tables) { + hardValidation (tables) { + for (const table of tables) { if (FORBIDDEN_ENTITIES[table.schema_name] && FORBIDDEN_ENTITIES[table.schema_name].length && ( FORBIDDEN_ENTITIES[table.schema_name][0] === '*' || @@ -49,8 +49,8 @@ const Validator = { return true; }, - softValidation(tables) { - for (let table of tables) { + softValidation (tables) { + for (const table of tables) { if (table.table_name.match(/\bpg_/)) { return false; } diff --git a/lib/services/stream-copy-metrics.js b/lib/services/stream-copy-metrics.js index 13f3f417..45d8ecc2 100644 --- a/lib/services/stream-copy-metrics.js +++ b/lib/services/stream-copy-metrics.js @@ -3,7 +3,7 @@ const { getFormatFromCopyQuery } = require('../utils/query-info'); module.exports = class StreamCopyMetrics { - constructor(logger, type, sql, user, isGzip = false) { + constructor (logger, type, sql, user, isGzip = false) { this.logger = logger; this.type = type; @@ -25,15 +25,15 @@ module.exports = class StreamCopyMetrics { this.ended = false; } - addSize(size) { + addSize (size) { this.size += size; } - addGzipSize(size) { + addGzipSize (size) { this.gzipSize += size; } - end(rows = null, error = null) { + end (rows = null, error = null) { if (this.ended) { return; } @@ -58,8 +58,8 @@ module.exports = class StreamCopyMetrics { ); } - _log(timestamp, gzipSize = null, errorMessage = null) { - let logData = { + _log (timestamp, gzipSize = null, errorMessage = null) { + const logData = { type: this.type, format: this.format, size: this.size, diff --git a/lib/services/stream-copy.js b/lib/services/stream-copy.js index 3ac92956..48e69f85 100644 --- a/lib/services/stream-copy.js +++ b/lib/services/stream-copy.js @@ -13,7 +13,7 @@ const terminateQuery = pid => `SELECT pg_terminate_backend(${pid}) as terminated const timeoutQuery = timeout => `SET statement_timeout=${timeout}`; module.exports = class StreamCopy { - constructor(sql, userDbParams, logger) { + constructor (sql, userDbParams, logger) { this.dbParams = Object.assign({}, userDbParams, { port: global.settings.db_batch_port || userDbParams.port }); @@ -23,15 +23,15 @@ module.exports = class StreamCopy { this.logger = logger; } - static get ACTION_TO() { + static get ACTION_TO () { return ACTION_TO; } - static get ACTION_FROM() { + static get ACTION_FROM () { return ACTION_FROM; } - getPGStream(action, callback) { + getPGStream (action, callback) { const pg = new PSQL(this.dbParams); pg.connect((err, client, done) => { @@ -56,7 +56,7 @@ module.exports = class StreamCopy { pgstream.on('warning', (msg) => this.logger.warn(msg)); } else if (action === ACTION_FROM) { pgstream.on('finish', () => done()); - pgstream.on('error', err => client.connection.sendCopyFail(err.message)); + pgstream.on('error', err => client.connection.sendCopyFail(err.message)); } pgstream.on('error', err => done(err)); @@ -66,7 +66,7 @@ module.exports = class StreamCopy { }); } - getRowCount() { + getRowCount () { return this.stream.rowCount; } diff --git a/lib/services/throttler-stream.js b/lib/services/throttler-stream.js index 9f326ea7..b759c1cb 100644 --- a/lib/services/throttler-stream.js +++ b/lib/services/throttler-stream.js @@ -12,7 +12,7 @@ module.exports = class Throttler extends Transform { this.minimunBytesPerSampleThreshold = global.settings.copy_from_minimum_input_speed || 0; this.byteCount = 0; - this._interval = setInterval(this._updateMetrics.bind(this), this.sampleSeconds*1000); + this._interval = setInterval(this._updateMetrics.bind(this), this.sampleSeconds * 1000); } _updateMetrics () { diff --git a/lib/services/user-database-service.js b/lib/services/user-database-service.js index 26790a2f..02aa73ac 100644 --- a/lib/services/user-database-service.js +++ b/lib/services/user-database-service.js @@ -1,13 +1,13 @@ 'use strict'; -function isApiKeyFound(apikey) { +function isApiKeyFound (apikey) { return apikey.type !== null && apikey.user !== null && apikey.databasePassword !== null && apikey.databaseRole !== null; } -function UserDatabaseService(metadataBackend) { +function UserDatabaseService (metadataBackend) { this.metadataBackend = metadataBackend; } @@ -15,7 +15,7 @@ function errorUserNotFoundMessageTemplate (user) { return `Sorry, we can't find CARTO user '${user}'. Please check that you have entered the correct domain.`; } -function isOauthAuthorization({ apikeyToken, authorizationLevel }) { +function isOauthAuthorization ({ apikeyToken, authorizationLevel }) { return (authorizationLevel === 'master') && !apikeyToken; } @@ -41,11 +41,10 @@ UserDatabaseService.prototype.getConnectionParams = function (username, apikeyTo const commonDBConfiguration = { port: global.settings.db_port, host: dbParams.dbhost, - dbname: dbParams.dbname, + dbname: dbParams.dbname }; this.metadataBackend.getMasterApikey(username, (err, masterApikey) => { - if (err) { err.http_status = 404; err.message = errorUserNotFoundMessageTemplate(username); @@ -66,9 +65,9 @@ UserDatabaseService.prototype.getConnectionParams = function (username, apikeyTo user: masterApikey.databaseRole, pass: masterApikey.databasePassword }, - commonDBConfiguration); + commonDBConfiguration); - if (isOauthAuthorization({ apikeyToken, authorizationLevel})) { + if (isOauthAuthorization({ apikeyToken, authorizationLevel })) { return callback(null, masterDBConfiguration, masterDBConfiguration); } @@ -96,7 +95,7 @@ UserDatabaseService.prototype.getConnectionParams = function (username, apikeyTo user: apikey.databaseRole, pass: apikey.databasePassword }, - commonDBConfiguration); + commonDBConfiguration); callback(null, DBConfiguration, masterDBConfiguration); }); diff --git a/lib/services/user-limits.js b/lib/services/user-limits.js index 91faa2e2..e63446ab 100644 --- a/lib/services/user-limits.js +++ b/lib/services/user-limits.js @@ -6,20 +6,20 @@ * @param {object} options */ class UserLimits { - constructor(metadataBackend, options = {}) { + constructor (metadataBackend, options = {}) { this.metadataBackend = metadataBackend; this.options = options; this.preprareRateLimit(); } - preprareRateLimit() { + preprareRateLimit () { if (this.options.limits.rateLimitsEnabled) { this.metadataBackend.loadRateLimitsScript(); } } - getRateLimit(user, endpointGroup, callback) { + getRateLimit (user, endpointGroup, callback) { this.metadataBackend.getRateLimit(user, 'sql', endpointGroup, callback); } } diff --git a/lib/stats/client.js b/lib/stats/client.js index be6f6c13..00b47b34 100644 --- a/lib/stats/client.js +++ b/lib/stats/client.js @@ -17,10 +17,8 @@ module.exports = { * @param config Configuration for StatsD, if undefined it will return an stub * @returns {StatsD|Object} */ - getInstance: function(config) { - + getInstance: function (config) { if (!this.instance) { - var instance; if (config) { @@ -31,7 +29,7 @@ module.exports = { var last_msg = last_err.msg; var this_msg = '' + err; if (this_msg !== last_msg) { - debug("statsd client socket error: " + err); + debug('statsd client socket error: ' + err); instance.last_error.count = 1; instance.last_error.msg = this_msg; } else { @@ -40,7 +38,7 @@ module.exports = { instance.last_error.interval = setInterval(function () { var count = instance.last_error.count; if (count > 1) { - debug("last statsd client socket error repeated " + count + " times"); + debug('last statsd client socket error repeated ' + count + ' times'); instance.last_error.count = 1; clearInterval(instance.last_error.interval); instance.last_error.interval = null; diff --git a/lib/stats/profiler-proxy.js b/lib/stats/profiler-proxy.js index 5ac60d57..9dad81f5 100644 --- a/lib/stats/profiler-proxy.js +++ b/lib/stats/profiler-proxy.js @@ -5,51 +5,51 @@ var Profiler = require('step-profiler'); /** * Proxy to encapsulate node-step-profiler module so there is no need to check if there is an instance */ -function ProfilerProxy(opts) { +function ProfilerProxy (opts) { this.profile = !!opts.profile; this.profiler = null; - if (!!opts.profile) { - this.profiler = new Profiler({statsd_client: opts.statsd_client}); + if (opts.profile) { + this.profiler = new Profiler({ statsd_client: opts.statsd_client }); } } -ProfilerProxy.prototype.done = function(what) { +ProfilerProxy.prototype.done = function (what) { if (this.profile) { this.profiler.done(what); } }; -ProfilerProxy.prototype.end = function() { +ProfilerProxy.prototype.end = function () { if (this.profile) { this.profiler.end(); } }; -ProfilerProxy.prototype.start = function(what) { +ProfilerProxy.prototype.start = function (what) { if (this.profile) { this.profiler.start(what); } }; -ProfilerProxy.prototype.add = function(what) { +ProfilerProxy.prototype.add = function (what) { if (this.profile) { this.profiler.add(what || {}); } }; -ProfilerProxy.prototype.sendStats = function() { +ProfilerProxy.prototype.sendStats = function () { if (this.profile) { this.profiler.sendStats(); } }; -ProfilerProxy.prototype.toString = function() { - return this.profile ? this.profiler.toString() : ""; +ProfilerProxy.prototype.toString = function () { + return this.profile ? this.profiler.toString() : ''; }; -ProfilerProxy.prototype.toJSONString = function() { - return this.profile ? this.profiler.toJSONString() : "{}"; +ProfilerProxy.prototype.toJSONString = function () { + return this.profile ? this.profiler.toJSONString() : '{}'; }; module.exports = ProfilerProxy; diff --git a/lib/utils/content-disposition.js b/lib/utils/content-disposition.js index 77eca29a..6b25a15f 100644 --- a/lib/utils/content-disposition.js +++ b/lib/utils/content-disposition.js @@ -1,8 +1,8 @@ 'use strict'; -module.exports = function getContentDisposition(formatter, filename, inline) { +module.exports = function getContentDisposition (formatter, filename, inline) { var ext = formatter.getFileExtension(); var time = new Date().toUTCString(); - return ( inline ? 'inline' : 'attachment' ) + '; filename=' + filename + '.' + ext + '; ' + + return (inline ? 'inline' : 'attachment') + '; filename=' + filename + '.' + ext + '; ' + 'modification-date="' + time + '";'; }; diff --git a/lib/utils/date-to-json.js b/lib/utils/date-to-json.js index ce09602b..c8657e5e 100644 --- a/lib/utils/date-to-json.js +++ b/lib/utils/date-to-json.js @@ -1,18 +1,18 @@ 'use strict'; // jshint ignore:start -function pad(n) { +function pad (n) { return n < 10 ? '0' + n : n; } -Date.prototype.toJSON = function() { +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)); + s += (offset < 0 ? '+' : '-') + pad(Math.abs(offset / 60)) + pad(Math.abs(offset % 60)); } return s; }; diff --git a/lib/utils/filename-sanitizer.js b/lib/utils/filename-sanitizer.js index fd97dc93..13565cf0 100644 --- a/lib/utils/filename-sanitizer.js +++ b/lib/utils/filename-sanitizer.js @@ -2,8 +2,8 @@ var path = require('path'); -module.exports = function sanitize_filename(filename) { - filename = path.basename(filename, path.extname(filename)); - filename = filename.replace(/[;()\[\]<>'"\s]/g, '_'); - return filename; +module.exports = function sanitize_filename (filename) { + filename = path.basename(filename, path.extname(filename)); + filename = filename.replace(/[;()\[\]<>'"\s]/g, '_'); + return filename; }; diff --git a/lib/utils/logs.js b/lib/utils/logs.js index 1ac3c0e4..2750acc8 100644 --- a/lib/utils/logs.js +++ b/lib/utils/logs.js @@ -8,12 +8,12 @@ module.exports = { * @param {Object} object * @param {Number} max_string_length */ - stringifyForLogs(object, max_string_length = 1024) { + stringifyForLogs (object, max_string_length = 1024) { return JSON.stringify(cloneAndFilter(object, max_string_length)); } }; -function cloneAndFilter(object, max_string_length) { +function cloneAndFilter (object, max_string_length) { if (!object || !(object instanceof Object)) { return null; } @@ -27,7 +27,7 @@ function cloneAndFilter(object, max_string_length) { newObject[key] = cloneAndFilter(object[key], max_string_length); } else if (object[key] instanceof Array) { newObject[key] = []; - for (let element of object[key]) { + for (const element of object[key]) { newObject[key].push(cloneAndFilter(element, max_string_length)); } } else { @@ -38,7 +38,7 @@ function cloneAndFilter(object, max_string_length) { return newObject; } -function filterString(s, max_string_length) { +function filterString (s, max_string_length) { return s .substring(0, max_string_length) .replace(/[^a-zA-Z0-9]/g, ' '); diff --git a/lib/utils/md5.js b/lib/utils/md5.js index 946017b1..a42f6886 100644 --- a/lib/utils/md5.js +++ b/lib/utils/md5.js @@ -2,7 +2,7 @@ var crypto = require('crypto'); -module.exports = function generateMD5(data){ +module.exports = function generateMD5 (data) { var hash = crypto.createHash('md5'); hash.update(data); return hash.digest('hex'); diff --git a/lib/utils/query-info.js b/lib/utils/query-info.js index d1ad0b96..12718435 100644 --- a/lib/utils/query-info.js +++ b/lib/utils/query-info.js @@ -3,16 +3,16 @@ const COPY_FORMATS = ['TEXT', 'CSV', 'BINARY']; module.exports = { - getFormatFromCopyQuery(copyQuery) { + getFormatFromCopyQuery (copyQuery) { let format = 'TEXT'; // Postgres default format copyQuery = copyQuery.toUpperCase(); - if (!copyQuery.startsWith("COPY ")) { + if (!copyQuery.startsWith('COPY ')) { return false; } - if(copyQuery.includes(' WITH') && copyQuery.includes('FORMAT ')) { + if (copyQuery.includes(' WITH') && copyQuery.includes('FORMAT ')) { const regex = /\bFORMAT\s+(\w+)/; const result = regex.exec(copyQuery); diff --git a/lib/utils/query-may-write.js b/lib/utils/query-may-write.js index 76d6a3b9..46abb878 100644 --- a/lib/utils/query-may-write.js +++ b/lib/utils/query-may-write.js @@ -1,6 +1,6 @@ 'use strict'; -var sqlQueryMayWriteRegex = new RegExp("\\b(alter|insert|update|delete|create|drop|reindex|truncate|refresh)\\b", "i"); +var sqlQueryMayWriteRegex = new RegExp('\\b(alter|insert|update|delete|create|drop|reindex|truncate|refresh)\\b', 'i'); /** * This is a fuzzy check, the return could be true even if the query doesn't really write anything. But you can be @@ -9,6 +9,6 @@ var sqlQueryMayWriteRegex = new RegExp("\\b(alter|insert|update|delete|create|dr * @param sql The SQL statement to check against * @returns {boolean} Return true of the given query may write to the database */ -module.exports = function queryMayWrite(sql) { +module.exports = function queryMayWrite (sql) { return sqlQueryMayWriteRegex.test(sql); }; diff --git a/test/acceptance/app-auth-test.js b/test/acceptance/app-auth-test.js index 2299cf93..dea6b222 100644 --- a/test/acceptance/app-auth-test.js +++ b/test/acceptance/app-auth-test.js @@ -5,17 +5,16 @@ require('../helper'); var server = require('../../lib/server')(); var assert = require('../support/assert'); -describe('app.auth', function() { - +describe('app.auth', function () { var scenarios = [ { desc: 'no api key should fallback to default api key', - url: "/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4", + url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4', statusCode: 200 }, { desc: 'invalid api key should return 401', - url: "/api/v1/sql?api_key=THIS_API_KEY_NOT_EXIST&q=SELECT%20*%20FROM%20untitle_table_4", + url: '/api/v1/sql?api_key=THIS_API_KEY_NOT_EXIST&q=SELECT%20*%20FROM%20untitle_table_4', statusCode: 401 }, { @@ -45,23 +44,22 @@ describe('app.auth', function() { } ]; - scenarios.forEach(function(scenario) { - it(scenario.desc, function(done) { + scenarios.forEach(function (scenario) { + it(scenario.desc, function (done) { assert.response(server, { - // view prepare_db.sh to find public table name and structure - url: scenario.url, - headers: { - host: 'vizzuality.cartodb.com' - }, - method: 'GET' + // view prepare_db.sh to find public table name and structure + url: scenario.url, + headers: { + host: 'vizzuality.cartodb.com' }, - {}, - function(err, res) { - assert.equal(res.statusCode, scenario.statusCode, res.statusCode + ': ' + res.body); - done(); - } + method: 'GET' + }, + {}, + function (err, res) { + assert.equal(res.statusCode, scenario.statusCode, res.statusCode + ': ' + res.body); + done(); + } ); }); }); - }); diff --git a/test/acceptance/app-configuration-test.js b/test/acceptance/app-configuration-test.js index 0c48806c..47f74de1 100644 --- a/test/acceptance/app-configuration-test.js +++ b/test/acceptance/app-configuration-test.js @@ -19,8 +19,7 @@ const exposedHeaders = [ 'Retry-After' ].join(', '); -describe('app-configuration', function() { - +describe('app-configuration', function () { var RESPONSE_OK = { statusCode: 200 }; @@ -28,11 +27,11 @@ describe('app-configuration', function() { var expected_cache_control = 'no-cache,max-age=31536000,must-revalidate,public'; var expected_cache_control_persist = 'public,max-age=31536000'; - it('GET /api/v1/version', function(done){ + it('GET /api/v1/version', function (done) { assert.response(server, { url: '/api/v1/version', method: 'GET' - }, RESPONSE_OK, function(err, res) { + }, RESPONSE_OK, function (err, res) { var parsed = JSON.parse(res.body); var sqlapi_version = require(__dirname + '/../../package.json').version; assert.ok(parsed.hasOwnProperty('cartodb_sql_api'), "No 'cartodb_sql_api' version in " + parsed); @@ -41,37 +40,37 @@ describe('app-configuration', function() { }); }); - it('GET /api/v1/sql', function(done){ + it('GET /api/v1/sql', function (done) { assert.response(server, { url: '/api/v1/sql', - headers: {host: 'vizzuality.cartodb.com'}, + headers: { host: 'vizzuality.cartodb.com' }, method: 'GET' - },{ + }, { status: 400 - }, function(err, res) { + }, function (err, res) { assert.deepEqual(res.headers['content-type'], 'application/json; charset=utf-8'); assert.deepEqual(res.headers['content-disposition'], 'inline'); - assert.deepEqual(JSON.parse(res.body), {"error":["You must indicate a sql query"]}); + assert.deepEqual(JSON.parse(res.body), { error: ['You must indicate a sql query'] }); done(); }); }); // Test base_url setting - it('GET /api/whatever/sql', function(done){ + it('GET /api/whatever/sql', function (done) { assert.response(server, { url: '/api/whatever/sql?q=SELECT%201', - headers: {host: 'vizzuality.cartodb.com'}, + headers: { host: 'vizzuality.cartodb.com' }, method: 'GET' }, RESPONSE_OK, done); }); // Test CORS headers with GET - it('GET /api/whatever/sql', function(done){ + it('GET /api/whatever/sql', function (done) { assert.response(server, { url: '/api/whatever/sql?q=SELECT%201', - headers: {host: 'vizzuality.cartodb.com'}, + headers: { host: 'vizzuality.cartodb.com' }, method: 'GET' - }, RESPONSE_OK, function(err, res) { + }, RESPONSE_OK, function (err, res) { assert.equal( res.headers['access-control-allow-headers'], accessControlHeaders @@ -86,12 +85,12 @@ describe('app-configuration', function() { }); // Test that OPTIONS does not run queries - it('OPTIONS /api/x/sql', function(done){ + it('OPTIONS /api/x/sql', function (done) { assert.response(server, { url: '/api/x/sql?q=syntax%20error', - headers: {host: 'vizzuality.cartodb.com'}, + headers: { host: 'vizzuality.cartodb.com' }, method: 'OPTIONS' - }, RESPONSE_OK, function(err, res) { + }, RESPONSE_OK, function (err, res) { assert.equal(res.body, ''); assert.equal( res.headers['access-control-allow-headers'], @@ -106,14 +105,13 @@ describe('app-configuration', function() { }); }); - - it('cache_policy=persist', function(done){ + it('cache_policy=persist', function (done) { assert.response(server, { 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'}, + headers: { host: 'vizzuality.cartodb.com' }, method: 'GET' - }, RESPONSE_OK, function(err, res) { + }, RESPONSE_OK, function (err, res) { // Check cache headers assert.ok(res.headers.hasOwnProperty('x-cache-channel')); // See https://github.com/CartoDB/CartoDB-SQL-API/issues/105 @@ -124,14 +122,14 @@ describe('app-configuration', function() { }); // See https://github.com/CartoDB/CartoDB-SQL-API/issues/121 - it('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(server, { url: '/api/v1/sql?q=SELECT+2+as+n', - headers: {host: 'cartodb250user.cartodb.com'}, + headers: { host: 'cartodb250user.cartodb.com' }, method: 'GET' - }, RESPONSE_OK, function(err, res) { + }, RESPONSE_OK, function (err, res) { global.settings.db_host = backupDBHost; try { var parsed = JSON.parse(res.body); @@ -145,22 +143,22 @@ describe('app-configuration', function() { }); // See https://github.com/CartoDB/CartoDB-SQL-API/issues/120 - it('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(server, { url: '/api/v1/sql?q=SELECT+2+as+n&api_key=1234', - headers: {host: 'cartodb250user.cartodb.com'}, + headers: { host: 'cartodb250user.cartodb.com' }, method: 'GET' - }, RESPONSE_OK, function(err, res) { + }, RESPONSE_OK, function (err, res) { global.settings.db_user_pass = backupDBUserPass; try { - assert.equal(res.statusCode, 200, res.statusCode + ": " + res.body); - var parsed = JSON.parse(res.body); - assert.equal(parsed.rows.length, 1); - assert.equal(parsed.rows[0].n, 2); + assert.equal(res.statusCode, 200, res.statusCode + ': ' + res.body); + var parsed = JSON.parse(res.body); + assert.equal(parsed.rows.length, 1); + assert.equal(parsed.rows[0].n, 2); } catch (e) { - return done(e); + return done(e); } return done(); }); @@ -169,12 +167,12 @@ describe('app-configuration', function() { /** * CORS */ - it('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(server, { url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4&database=cartodb_test_user_1_db', - headers: {host: 'vizzuality.cartodb.com'}, + headers: { host: 'vizzuality.cartodb.com' }, method: 'GET' - }, RESPONSE_OK, function(err, res) { + }, RESPONSE_OK, function (err, res) { // Check cache headers 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); @@ -190,5 +188,4 @@ describe('app-configuration', function() { done(); }); }); - }); diff --git a/test/acceptance/app-test.js b/test/acceptance/app-test.js index 38af31e8..fc0e94f5 100644 --- a/test/acceptance/app-test.js +++ b/test/acceptance/app-test.js @@ -22,766 +22,767 @@ var querystring = require('querystring'); var _ = require('underscore'); var step = require('step'); - -describe('app.test', function() { - +describe('app.test', function () { var RESPONSE_OK = { statusCode: 200 }; -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 = 'no-cache,max-age=31536000,must-revalidate,public'; + var expected_rw_cache_control = 'no-cache,max-age=0,must-revalidate,public'; -it('GET /api/v1/sql with SQL parameter on SELECT only. No oAuth included ', function(done){ - assert.response(server, { - url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4&database=cartodb_test_user_1_db', - headers: {host: 'vizzuality.cartodb.com'}, - method: 'GET' - },{ }, function(err, res) { - assert.equal(res.statusCode, 200, res.body); - // Check cache headers - 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); - done(); + it('GET /api/v1/sql with SQL parameter on SELECT only. No oAuth included ', function (done) { + assert.response(server, { + url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4&database=cartodb_test_user_1_db', + headers: { host: 'vizzuality.cartodb.com' }, + method: 'GET' + }, { }, function (err, res) { + assert.equal(res.statusCode, 200, res.body); + // Check cache headers + 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); + done(); + }); }); -}); -it('GET /api/v1/sql with SQL parameter on SELECT only. no database param, just id using headers', function(done){ - assert.response(server, { - url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4', - headers: {host: 'vizzuality.cartodb.com'}, - method: 'GET' - },{ }, function(err, res) { - assert.equal(res.statusCode, 200, res.body); - done(); + it('GET /api/v1/sql with SQL parameter on SELECT only. no database param, just id using headers', function (done) { + assert.response(server, { + url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4', + headers: { host: 'vizzuality.cartodb.com' }, + method: 'GET' + }, { }, function (err, res) { + assert.equal(res.statusCode, 200, res.body); + done(); + }); }); -}); -it('GET /user/vizzuality/api/v1/sql with SQL parameter on SELECT only', function(done){ - assert.response(server, { - url: '/user/vizzuality/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4', - method: 'GET' - },{ }, function(err, res) { - assert.equal(res.statusCode, 200, res.body); - done(); + it('GET /user/vizzuality/api/v1/sql with SQL parameter on SELECT only', function (done) { + assert.response(server, { + url: '/user/vizzuality/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4', + method: 'GET' + }, { }, function (err, res) { + assert.equal(res.statusCode, 200, res.body); + done(); + }); }); -}); -it('GET /api/v1/sql with SQL parameter on SELECT only. no database param, just id using headers. Authenticated.', -function(done){ - assert.response(server, { - url: '/api/v1/sql?q=SELECT%20cartodb_id*2%20FROM%20untitle_table_4&api_key=1234', - headers: {host: 'vizzuality.cartodb.com'}, - method: 'GET' - },{ }, function(err, res) { - assert.equal(res.statusCode, 200, res.body); - // Check cache headers - 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); - done(); + it('GET /api/v1/sql with SQL parameter on SELECT only. no database param, just id using headers. Authenticated.', + function (done) { + assert.response(server, { + url: '/api/v1/sql?q=SELECT%20cartodb_id*2%20FROM%20untitle_table_4&api_key=1234', + headers: { host: 'vizzuality.cartodb.com' }, + method: 'GET' + }, { }, function (err, res) { + assert.equal(res.statusCode, 200, res.body); + // Check cache headers + 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); + done(); + }); + }); + + it('POST /api/v1/sql with SQL parameter on SELECT only. no database param, just id using headers', function (done) { + assert.response(server, { + url: '/api/v1/sql', + data: querystring.stringify({ q: 'SELECT * FROM untitle_table_4' }), + headers: { host: 'vizzuality.cartodb.com', 'Content-Type': 'application/x-www-form-urlencoded' }, + method: 'POST' + }, { }, function (err, res) { + assert.equal(res.statusCode, 200, res.body); + done(); + }); }); -}); -it('POST /api/v1/sql with SQL parameter on SELECT only. no database param, just id using headers', function(done){ - assert.response(server, { - url: '/api/v1/sql', - data: querystring.stringify({q: "SELECT * FROM untitle_table_4"}), - headers: {host: 'vizzuality.cartodb.com', 'Content-Type': 'application/x-www-form-urlencoded' }, - method: 'POST' - },{ }, function(err, res) { - assert.equal(res.statusCode, 200, res.body); - done(); + it('GET /api/v1/sql with INSERT. oAuth not used, so public user - should fail', function (done) { + assert.response(server, { + 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' + }, { + }, function (err, res) { + assert.equal(res.statusCode, 403, res.statusCode + ': ' + res.body); + assert.deepEqual(res.headers['content-type'], 'application/json; charset=utf-8'); + assert.deepEqual(res.headers['content-disposition'], 'inline'); + assert.ok(JSON.parse(res.body).error[0].match(/permission denied for .+? untitle_table_4/)); + done(); + }); }); -}); -it('GET /api/v1/sql with INSERT. oAuth not used, so public user - should fail', function(done){ - assert.response(server, { - 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' - },{ - }, function(err, res) { - assert.equal(res.statusCode, 403, res.statusCode + ': ' + res.body); - assert.deepEqual(res.headers['content-type'], 'application/json; charset=utf-8'); - assert.deepEqual(res.headers['content-disposition'], 'inline'); - assert.ok(JSON.parse(res.body).error[0].match(/permission denied for .+? untitle_table_4/)); - done(); + it('GET /api/v1/sql with DROP TABLE. oAuth not used, so public user - should fail', function (done) { + assert.response(server, { + url: '/api/v1/sql?q=DROP%20TABLE%20untitle_table_4&database=cartodb_test_user_1_db', + headers: { host: 'vizzuality.cartodb.com' }, + method: 'GET' + }, { + }, function (err, res) { + assert.equal(res.statusCode, 400, res.statusCode + ': ' + res.body); + assert.deepEqual(res.headers['content-type'], 'application/json; charset=utf-8'); + assert.deepEqual(res.headers['content-disposition'], 'inline'); + assert.ok(JSON.parse(res.body).error[0].match(/must be owner of.+? untitle_table_4/)); + done(); + }); }); -}); -it('GET /api/v1/sql with DROP TABLE. oAuth not used, so public user - should fail', function(done){ - assert.response(server, { - url: "/api/v1/sql?q=DROP%20TABLE%20untitle_table_4&database=cartodb_test_user_1_db", - headers: {host: 'vizzuality.cartodb.com'}, - method: 'GET' - },{ - }, function(err, res) { - assert.equal(res.statusCode, 400, res.statusCode + ': ' + res.body); - assert.deepEqual(res.headers['content-type'], 'application/json; charset=utf-8'); - assert.deepEqual(res.headers['content-disposition'], 'inline'); - assert.ok(JSON.parse(res.body).error[0].match(/must be owner of.+? untitle_table_4/)); - done(); + it('GET /api/v1/sql with INSERT. header based db - should fail', function (done) { + assert.response(server, { + url: '/api/v1/sql?q=INSERT%20INTO%20untitle_table_4%20(id)%20VALUES%20(1)', + headers: { host: 'vizzuality.cartodb.com' }, + method: 'GET' + }, { + status: 400 + }, done); }); -}); -it('GET /api/v1/sql with INSERT. header based db - should fail', function (done) { - assert.response(server, { - url: "/api/v1/sql?q=INSERT%20INTO%20untitle_table_4%20(id)%20VALUES%20(1)", - headers: {host: 'vizzuality.cartodb.com'}, - method: 'GET' - }, { - status: 400 - }, done); -}); - -it('GET /api/v1/sql with SQL parameter on DROP TABLE. should fail', function(done){ - assert.response(server, { - url: "/api/v1/sql?q=DROP%20TABLE%20untitle_table_4", - headers: {host: 'vizzuality.cartodb.com'}, - method: 'GET' - },{}, function(err, res) { - assert.equal(res.statusCode, 400, res.statusCode + ': ' + res.body); - assert.deepEqual(res.headers['content-type'], 'application/json; charset=utf-8'); - assert.deepEqual(res.headers['content-disposition'], 'inline'); - assert.ok(JSON.parse(res.body).error[0].match(/must be owner of.+? untitle_table_4/)); - done(); + it('GET /api/v1/sql with SQL parameter on DROP TABLE. should fail', function (done) { + assert.response(server, { + url: '/api/v1/sql?q=DROP%20TABLE%20untitle_table_4', + headers: { host: 'vizzuality.cartodb.com' }, + method: 'GET' + }, {}, function (err, res) { + assert.equal(res.statusCode, 400, res.statusCode + ': ' + res.body); + assert.deepEqual(res.headers['content-type'], 'application/json; charset=utf-8'); + assert.deepEqual(res.headers['content-disposition'], 'inline'); + assert.ok(JSON.parse(res.body).error[0].match(/must be owner of.+? untitle_table_4/)); + done(); + }); }); -}); -// Check X-Cache-Channel when querying "updated_at" fields -// -// See https://github.com/Vizzuality/CartoDB-SQL-API/issues/99 -it('Field name is not confused with UPDATE operation', function(done){ - assert.response(server, { + // Check X-Cache-Channel when querying "updated_at" fields + // + // See https://github.com/Vizzuality/CartoDB-SQL-API/issues/99 + it('Field name is not confused with UPDATE operation', function (done) { + assert.response(server, { // view prepare_db.sh to see where to set api_key - url: "/api/v1/sql?api_key=1234&" + querystring.stringify({q: - "SELECT min(updated_at) FROM private_table" - }), - headers: {host: 'vizzuality.localhost.lan:8080' }, - method: 'GET' - },{}, function(err, res) { - assert.equal(res.statusCode, 200, res.statusCode + ': ' + res.body); - assert.equal(res.headers['x-cache-channel'], 'cartodb_test_user_1_db:public.private_table'); - done(); + url: '/api/v1/sql?api_key=1234&' + querystring.stringify({ + q: + 'SELECT min(updated_at) FROM private_table' + }), + headers: { host: 'vizzuality.localhost.lan:8080' }, + method: 'GET' + }, {}, function (err, res) { + assert.equal(res.statusCode, 200, res.statusCode + ': ' + res.body); + assert.equal(res.headers['x-cache-channel'], 'cartodb_test_user_1_db:public.private_table'); + done(); + }); }); -}); -it('CREATE TABLE with GET and auth', function(done){ - assert.response(server, { - url: "/api/v1/sql?" + querystring.stringify({ - q: 'CREATE TABLE test_table(a int)', - api_key: 1234 - }), - headers: {host: 'vizzuality.cartodb.com'}, - method: 'GET' - },{}, function(err, res) { - assert.equal(res.statusCode, 200, res.statusCode + ': ' + res.body); - // Check cache headers - // See https://github.com/Vizzuality/CartoDB-SQL-API/issues/43 - assert.ok(!res.hasOwnProperty('x-cache-channel')); - assert.equal(res.headers['cache-control'], expected_rw_cache_control); - done(); + it('CREATE TABLE with GET and auth', function (done) { + assert.response(server, { + url: '/api/v1/sql?' + querystring.stringify({ + q: 'CREATE TABLE test_table(a int)', + api_key: 1234 + }), + headers: { host: 'vizzuality.cartodb.com' }, + method: 'GET' + }, {}, function (err, res) { + assert.equal(res.statusCode, 200, res.statusCode + ': ' + res.body); + // Check cache headers + // See https://github.com/Vizzuality/CartoDB-SQL-API/issues/43 + assert.ok(!res.hasOwnProperty('x-cache-channel')); + assert.equal(res.headers['cache-control'], expected_rw_cache_control); + done(); + }); }); -}); -it('ALTER TABLE with GET and auth', function(done){ - assert.response(server, { - url: "/api/v1/sql?" + querystring.stringify({ - q: 'ALTER TABLE test_table ADD b int', - api_key: 1234 - }), - headers: {host: 'vizzuality.cartodb.com'}, - method: 'GET' - },{}, function(err, res) { - assert.equal(res.statusCode, 200, res.statusCode + ': ' + res.body); - // Check cache headers - // See https://github.com/Vizzuality/CartoDB-SQL-API/issues/43 - assert.ok(!res.hasOwnProperty('x-cache-channel')); - assert.equal(res.headers['cache-control'], expected_rw_cache_control); - done(); + it('ALTER TABLE with GET and auth', function (done) { + assert.response(server, { + url: '/api/v1/sql?' + querystring.stringify({ + q: 'ALTER TABLE test_table ADD b int', + api_key: 1234 + }), + headers: { host: 'vizzuality.cartodb.com' }, + method: 'GET' + }, {}, function (err, res) { + assert.equal(res.statusCode, 200, res.statusCode + ': ' + res.body); + // Check cache headers + // See https://github.com/Vizzuality/CartoDB-SQL-API/issues/43 + assert.ok(!res.hasOwnProperty('x-cache-channel')); + assert.equal(res.headers['cache-control'], expected_rw_cache_control); + done(); + }); }); -}); -it('multistatement insert, alter, select, begin, commit', function(done){ - assert.response(server, { - url: "/api/v1/sql?" + querystring.stringify({ - q: 'BEGIN; DELETE FROM test_table; COMMIT; BEGIN; INSERT INTO test_table(b) values (5); COMMIT; ' + + it('multistatement insert, alter, select, begin, commit', function (done) { + assert.response(server, { + 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;', - api_key: 1234 - }), - headers: {host: 'vizzuality.cartodb.com'}, - method: 'GET' - },{}, function(err, res) { - assert.equal(res.statusCode, 200, res.statusCode + ': ' + res.body); - var parsedBody = JSON.parse(res.body); - assert.equal(parsedBody.total_rows, 1); - assert.deepEqual(parsedBody.rows[0], {b:2.5}); - done(); + api_key: 1234 + }), + headers: { host: 'vizzuality.cartodb.com' }, + method: 'GET' + }, {}, function (err, res) { + assert.equal(res.statusCode, 200, res.statusCode + ': ' + res.body); + var parsedBody = JSON.parse(res.body); + assert.equal(parsedBody.total_rows, 1); + assert.deepEqual(parsedBody.rows[0], { b: 2.5 }); + done(); + }); }); -}); -it('TRUNCATE TABLE with GET and auth', function(done){ - assert.response(server, { - url: "/api/v1/sql?" + querystring.stringify({ - q: 'TRUNCATE TABLE test_table', - api_key: 1234 - }), - headers: {host: 'vizzuality.cartodb.com'}, - method: 'GET' - },{}, function(err, res) { - assert.equal(res.statusCode, 200, res.statusCode + ': ' + res.body); - assert.ok(!res.hasOwnProperty('x-cache-channel')); - assert.equal(res.headers['cache-control'], expected_rw_cache_control); - var pbody = JSON.parse(res.body); - assert.equal(pbody.rows.length, 0); - assert.response(server, { - url: "/api/v1/sql?" + querystring.stringify({ - q: 'SELECT count(*) FROM test_table', - api_key: 1234 - }), - headers: {host: 'vizzuality.cartodb.com'}, - method: 'GET' - },{}, function(err, res) { - assert.equal(res.statusCode, 200, res.statusCode + ': ' + res.body); - // table should not get a cache channel as it won't get invalidated - assert.ok(!res.headers.hasOwnProperty('x-cache-channel')); + it('TRUNCATE TABLE with GET and auth', function (done) { + assert.response(server, { + url: '/api/v1/sql?' + querystring.stringify({ + q: 'TRUNCATE TABLE test_table', + api_key: 1234 + }), + headers: { host: 'vizzuality.cartodb.com' }, + method: 'GET' + }, {}, function (err, res) { + assert.equal(res.statusCode, 200, res.statusCode + ': ' + res.body); + assert.ok(!res.hasOwnProperty('x-cache-channel')); + assert.equal(res.headers['cache-control'], expected_rw_cache_control); + var pbody = JSON.parse(res.body); + assert.equal(pbody.rows.length, 0); + assert.response(server, { + url: '/api/v1/sql?' + querystring.stringify({ + q: 'SELECT count(*) FROM test_table', + api_key: 1234 + }), + headers: { host: 'vizzuality.cartodb.com' }, + method: 'GET' + }, {}, function (err, res) { + assert.equal(res.statusCode, 200, res.statusCode + ': ' + res.body); + // table should not get a cache channel as it won't get invalidated + assert.ok(!res.headers.hasOwnProperty('x-cache-channel')); - const fallbackTtl = global.settings.cache.fallbackTtl || 300; - const cacheControl = res.headers['cache-control']; - const [ , maxAge ] = cacheControl.split(','); - const [ , value ] = maxAge.split('='); + const fallbackTtl = global.settings.cache.fallbackTtl || 300; + const cacheControl = res.headers['cache-control']; + const [, maxAge] = cacheControl.split(','); + const [, value] = maxAge.split('='); - assert.ok(Number(value) <= fallbackTtl); + assert.ok(Number(value) <= fallbackTtl); - var pbody = JSON.parse(res.body); - assert.equal(pbody.total_rows, 1); - assert.equal(pbody.rows[0].count, 0); - done(); - }); + var pbody = JSON.parse(res.body); + assert.equal(pbody.total_rows, 1); + assert.equal(pbody.rows[0].count, 0); + done(); + }); + }); }); -}); -it('REINDEX TABLE with GET and auth', function(done){ - assert.response(server, { - url: "/api/v1/sql?" + querystring.stringify({ - q: ' ReINdEX TABLE test_table', - api_key: 1234 - }), - headers: {host: 'vizzuality.cartodb.com'}, - method: 'GET' - },{}, function(err, res) { - assert.equal(res.statusCode, 200, res.statusCode + ': ' + res.body); - assert.ok(!res.hasOwnProperty('x-cache-channel')); - assert.equal(res.headers['cache-control'], expected_rw_cache_control); - var pbody = JSON.parse(res.body); - assert.equal(pbody.rows.length, 0); - done(); + it('REINDEX TABLE with GET and auth', function (done) { + assert.response(server, { + url: '/api/v1/sql?' + querystring.stringify({ + q: ' ReINdEX TABLE test_table', + api_key: 1234 + }), + headers: { host: 'vizzuality.cartodb.com' }, + method: 'GET' + }, {}, function (err, res) { + assert.equal(res.statusCode, 200, res.statusCode + ': ' + res.body); + assert.ok(!res.hasOwnProperty('x-cache-channel')); + assert.equal(res.headers['cache-control'], expected_rw_cache_control); + var pbody = JSON.parse(res.body); + assert.equal(pbody.rows.length, 0); + done(); + }); }); -}); -it('DROP TABLE with GET and auth', function(done){ - assert.response(server, { - url: "/api/v1/sql?" + querystring.stringify({ - q: 'DROP TABLE test_table', - api_key: 1234 - }), - headers: {host: 'vizzuality.cartodb.com'}, - method: 'GET' - },{}, function(err, res) { - assert.equal(res.statusCode, 200, res.statusCode + ': ' + res.body); - // Check cache headers - // See https://github.com/Vizzuality/CartoDB-SQL-API/issues/43 - assert.ok(!res.hasOwnProperty('x-cache-channel')); - assert.equal(res.headers['cache-control'], expected_rw_cache_control); - done(); + it('DROP TABLE with GET and auth', function (done) { + assert.response(server, { + url: '/api/v1/sql?' + querystring.stringify({ + q: 'DROP TABLE test_table', + api_key: 1234 + }), + headers: { host: 'vizzuality.cartodb.com' }, + method: 'GET' + }, {}, function (err, res) { + assert.equal(res.statusCode, 200, res.statusCode + ': ' + res.body); + // Check cache headers + // See https://github.com/Vizzuality/CartoDB-SQL-API/issues/43 + assert.ok(!res.hasOwnProperty('x-cache-channel')); + assert.equal(res.headers['cache-control'], expected_rw_cache_control); + done(); + }); }); -}); -it('CREATE FUNCTION with GET and auth', function(done){ - assert.response(server, { - url: "/api/v1/sql?" + querystring.stringify({ - q: 'CREATE FUNCTION create_func_test(a int) RETURNS INT AS \'SELECT 1\' LANGUAGE \'sql\'', - api_key: 1234 - }), - headers: {host: 'vizzuality.cartodb.com'}, - method: 'GET' - },{}, function(err, res) { - assert.equal(res.statusCode, 200, res.statusCode + ': ' + res.body); - // Check cache headers - // See https://github.com/Vizzuality/CartoDB-SQL-API/issues/43 - assert.ok(!res.hasOwnProperty('x-cache-channel')); - assert.equal(res.headers['cache-control'], expected_rw_cache_control); - done(); + it('CREATE FUNCTION with GET and auth', function (done) { + assert.response(server, { + url: '/api/v1/sql?' + querystring.stringify({ + q: 'CREATE FUNCTION create_func_test(a int) RETURNS INT AS \'SELECT 1\' LANGUAGE \'sql\'', + api_key: 1234 + }), + headers: { host: 'vizzuality.cartodb.com' }, + method: 'GET' + }, {}, function (err, res) { + assert.equal(res.statusCode, 200, res.statusCode + ': ' + res.body); + // Check cache headers + // See https://github.com/Vizzuality/CartoDB-SQL-API/issues/43 + assert.ok(!res.hasOwnProperty('x-cache-channel')); + assert.equal(res.headers['cache-control'], expected_rw_cache_control); + done(); + }); }); -}); -it('DROP FUNCTION with GET and auth', function(done){ - assert.response(server, { - url: "/api/v1/sql?" + querystring.stringify({ - q: 'DROP FUNCTION create_func_test(a int)', - api_key: 1234 - }), - headers: {host: 'vizzuality.cartodb.com'}, - method: 'GET' - },{}, function(err, res) { - assert.equal(res.statusCode, 200, res.statusCode + ': ' + res.body); - // Check cache headers - // See https://github.com/Vizzuality/CartoDB-SQL-API/issues/43 - assert.ok(!res.hasOwnProperty('x-cache-channel')); - assert.equal(res.headers['cache-control'], expected_rw_cache_control); - done(); + it('DROP FUNCTION with GET and auth', function (done) { + assert.response(server, { + url: '/api/v1/sql?' + querystring.stringify({ + q: 'DROP FUNCTION create_func_test(a int)', + api_key: 1234 + }), + headers: { host: 'vizzuality.cartodb.com' }, + method: 'GET' + }, {}, function (err, res) { + assert.equal(res.statusCode, 200, res.statusCode + ': ' + res.body); + // Check cache headers + // See https://github.com/Vizzuality/CartoDB-SQL-API/issues/43 + assert.ok(!res.hasOwnProperty('x-cache-channel')); + assert.equal(res.headers['cache-control'], expected_rw_cache_control); + done(); + }); }); -}); -it('sends a 400 when an unsupported format is requested', function(done){ - assert.response(server, { - url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4&format=unknown', - headers: {host: 'vizzuality.cartodb.com'}, - method: 'GET' - },{ }, function(err, res){ - assert.equal(res.statusCode, 400, res.body); - assert.deepEqual(res.headers['content-type'], 'application/json; charset=utf-8'); - assert.deepEqual(res.headers['content-disposition'], 'inline'); - assert.deepEqual(JSON.parse(res.body), {"error":[ "Invalid format: unknown" ]}); - done(); + it('sends a 400 when an unsupported format is requested', function (done) { + assert.response(server, { + url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4&format=unknown', + headers: { host: 'vizzuality.cartodb.com' }, + method: 'GET' + }, { }, function (err, res) { + assert.equal(res.statusCode, 400, res.body); + assert.deepEqual(res.headers['content-type'], 'application/json; charset=utf-8'); + assert.deepEqual(res.headers['content-disposition'], 'inline'); + assert.deepEqual(JSON.parse(res.body), { error: ['Invalid format: unknown'] }); + done(); + }); }); -}); -it('GET /api/v1/sql with SQL parameter and no format, ensuring content-disposition set to json', function(done){ - assert.response(server, { - url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4', - headers: {host: 'vizzuality.cartodb.com'}, - method: 'GET' - },{ }, function(err, res){ - assert.equal(res.statusCode, 200, res.body); - var ct = res.headers['content-type']; - assert.ok(/json/.test(ct), 'Default format is not JSON: ' + ct); - var cd = res.headers['content-disposition']; - assert.equal(true, /^inline/.test(cd), 'Default format is not disposed inline: ' + cd); - assert.equal(true, /filename=cartodb-query.json/gi.test(cd), 'Unexpected JSON filename: ' + cd); - done(); + it('GET /api/v1/sql with SQL parameter and no format, ensuring content-disposition set to json', function (done) { + assert.response(server, { + url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4', + headers: { host: 'vizzuality.cartodb.com' }, + method: 'GET' + }, { }, function (err, res) { + assert.equal(res.statusCode, 200, res.body); + var ct = res.headers['content-type']; + assert.ok(/json/.test(ct), 'Default format is not JSON: ' + ct); + var cd = res.headers['content-disposition']; + assert.equal(true, /^inline/.test(cd), 'Default format is not disposed inline: ' + cd); + assert.equal(true, /filename=cartodb-query.json/gi.test(cd), 'Unexpected JSON filename: ' + cd); + done(); + }); }); -}); -it('POST /api/v1/sql with SQL parameter and no format, ensuring content-disposition set to json', function(done){ - assert.response(server, { - url: '/api/v1/sql', - data: querystring.stringify({q: "SELECT * FROM untitle_table_4" }), - headers: {host: 'vizzuality.cartodb.com', 'Content-Type': 'application/x-www-form-urlencoded' }, - method: 'POST' - },{ }, function(err, res){ - assert.equal(res.statusCode, 200, res.body); - var ct = res.headers['content-type']; - assert.ok(/json/.test(ct), 'Default format is not JSON: ' + ct); - var cd = res.headers['content-disposition']; - assert.equal(true, /^inline/.test(cd), 'Default format is not disposed inline: ' + cd); - assert.equal(true, /filename=cartodb-query.json/gi.test(cd), 'Unexpected JSON filename: ' + cd); - done(); + it('POST /api/v1/sql with SQL parameter and no format, ensuring content-disposition set to json', function (done) { + assert.response(server, { + url: '/api/v1/sql', + data: querystring.stringify({ q: 'SELECT * FROM untitle_table_4' }), + headers: { host: 'vizzuality.cartodb.com', 'Content-Type': 'application/x-www-form-urlencoded' }, + method: 'POST' + }, { }, function (err, res) { + assert.equal(res.statusCode, 200, res.body); + var ct = res.headers['content-type']; + assert.ok(/json/.test(ct), 'Default format is not JSON: ' + ct); + var cd = res.headers['content-disposition']; + assert.equal(true, /^inline/.test(cd), 'Default format is not disposed inline: ' + cd); + assert.equal(true, /filename=cartodb-query.json/gi.test(cd), 'Unexpected JSON filename: ' + cd); + done(); + }); }); -}); -it('GET /api/v1/sql with SQL parameter and no format, but a filename', function(done){ - assert.response(server, { - url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4&filename=x', - headers: {host: 'vizzuality.cartodb.com'}, - method: 'GET' - },{ }, function(err, res){ - assert.equal(res.statusCode, 200, res.body); - var ct = res.headers['content-type']; - assert.ok(/json/.test(ct), 'Default format is not JSON: ' + ct); - var cd = res.headers['content-disposition']; - assert.equal(true, /^attachment/.test(cd), 'Format with filename is not disposed as attachment: ' + cd); - assert.equal(true, /filename=x.json/gi.test(cd), 'Unexpected JSON filename: ' + cd); - done(); + it('GET /api/v1/sql with SQL parameter and no format, but a filename', function (done) { + assert.response(server, { + url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4&filename=x', + headers: { host: 'vizzuality.cartodb.com' }, + method: 'GET' + }, { }, function (err, res) { + assert.equal(res.statusCode, 200, res.body); + var ct = res.headers['content-type']; + assert.ok(/json/.test(ct), 'Default format is not JSON: ' + ct); + var cd = res.headers['content-disposition']; + assert.equal(true, /^attachment/.test(cd), 'Format with filename is not disposed as attachment: ' + cd); + assert.equal(true, /filename=x.json/gi.test(cd), 'Unexpected JSON filename: ' + cd); + done(); + }); }); -}); -it('GET /api/v1/sql ensure cross domain set on errors', function(done){ - assert.response(server, { - url: '/api/v1/sql?q=SELECT%20*gadfgadfg%20FROM%20untitle_table_4', - headers: {host: 'vizzuality.cartodb.com'}, - method: 'GET' - },{ - status: 400 - }, function(err, res){ - var cd = res.headers['access-control-allow-origin']; - assert.deepEqual(res.headers['content-type'], 'application/json; charset=utf-8'); - assert.deepEqual(res.headers['content-disposition'], 'inline'); - assert.equal(cd, '*'); - done(); + it('GET /api/v1/sql ensure cross domain set on errors', function (done) { + assert.response(server, { + url: '/api/v1/sql?q=SELECT%20*gadfgadfg%20FROM%20untitle_table_4', + headers: { host: 'vizzuality.cartodb.com' }, + method: 'GET' + }, { + status: 400 + }, function (err, res) { + var cd = res.headers['access-control-allow-origin']; + assert.deepEqual(res.headers['content-type'], 'application/json; charset=utf-8'); + assert.deepEqual(res.headers['content-disposition'], 'inline'); + assert.equal(cd, '*'); + done(); + }); }); -}); -it('GET decent error if domain is incorrect', function(done){ - assert.response(server, { - url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4&format=geojson', - headers: {host: 'vizzualinot.cartodb.com'}, - method: 'GET' - }, {}, function(err, res){ - 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 CARTO user 'vizzualinot'. Please check that you have entered the correct domain." - ); - done(); + it('GET decent error if domain is incorrect', function (done) { + assert.response(server, { + url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4&format=geojson', + headers: { host: 'vizzualinot.cartodb.com' }, + method: 'GET' + }, {}, function (err, res) { + 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 CARTO user 'vizzualinot'. Please check that you have entered the correct domain." + ); + done(); + }); }); -}); -// this test does not make sense with the current CDB_QueryTables implementation -it('GET decent error if SQL is broken', function(done){ - assert.response(server, { - url: '/api/v1/sql?' + querystring.stringify({q: + // this test does not make sense with the current CDB_QueryTables implementation + it('GET decent error if SQL is broken', function (done) { + assert.response(server, { + url: '/api/v1/sql?' + querystring.stringify({ + q: 'SELECT star FROM this and that' - }), - headers: {host: 'vizzuality.cartodb.com'}, - method: 'GET' - },{}, function(err, res){ - assert.equal(res.statusCode, 400, res.statusCode + ': ' + 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); - // NOTE: actual error message may be slighly different, possibly worth a regexp here - assert.equal(result.error[0], 'syntax error at or near "and"'); - done(); + }), + headers: { host: 'vizzuality.cartodb.com' }, + method: 'GET' + }, {}, function (err, res) { + assert.equal(res.statusCode, 400, res.statusCode + ': ' + 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); + // NOTE: actual error message may be slighly different, possibly worth a regexp here + assert.equal(result.error[0], 'syntax error at or near "and"'); + done(); + }); }); -}); -// See https://github.com/Vizzuality/CartoDB-SQL-API/issues/88 -it('numeric arrays are rendered as such', function(done){ - assert.response(server, { - url: "/api/v1/sql?" + querystring.stringify({q: - "SELECT ARRAY[8.7,4.3]::numeric[] as x" - }), - headers: {host: 'vizzuality.localhost.lan:8080' }, - method: 'GET' - },{}, function(err, res) { - assert.equal(res.statusCode, 200, res.statusCode + ': ' + res.body); - var out = JSON.parse(res.body); - assert.ok(out.hasOwnProperty('time')); - assert.equal(out.total_rows, 1); - assert.equal(out.rows.length, 1); - assert.ok(out.rows[0].hasOwnProperty('x')); - assert.equal(out.rows[0].x.length, 2); - assert.equal(out.rows[0].x[0], '8.7'); - assert.equal(out.rows[0].x[1], '4.3'); - assert.equal(res.headers.hasOwnProperty('x-cache-channel'), false); - done(); + // See https://github.com/Vizzuality/CartoDB-SQL-API/issues/88 + it('numeric arrays are rendered as such', function (done) { + assert.response(server, { + url: '/api/v1/sql?' + querystring.stringify({ + q: + 'SELECT ARRAY[8.7,4.3]::numeric[] as x' + }), + headers: { host: 'vizzuality.localhost.lan:8080' }, + method: 'GET' + }, {}, function (err, res) { + assert.equal(res.statusCode, 200, res.statusCode + ': ' + res.body); + var out = JSON.parse(res.body); + assert.ok(out.hasOwnProperty('time')); + assert.equal(out.total_rows, 1); + assert.equal(out.rows.length, 1); + assert.ok(out.rows[0].hasOwnProperty('x')); + assert.equal(out.rows[0].x.length, 2); + assert.equal(out.rows[0].x[0], '8.7'); + assert.equal(out.rows[0].x[1], '4.3'); + assert.equal(res.headers.hasOwnProperty('x-cache-channel'), false); + done(); + }); }); -}); -// See https://github.com/Vizzuality/CartoDB-SQL-API/issues/97 -it('field names and types are exposed', function(done){ - assert.response(server, { - url: '/api/v1/sql?' + querystring.stringify({ - q: "SELECT 1::int as a, 2::float8 as b, 3::varchar as c, " + + // See https://github.com/Vizzuality/CartoDB-SQL-API/issues/97 + it('field names and types are exposed', function (done) { + assert.response(server, { + url: '/api/v1/sql?' + querystring.stringify({ + q: 'SELECT 1::int as a, 2::float8 as b, 3::varchar as c, ' + "4::char as d, now() as e, 'a'::text as f" + - ", 1::bool as g" + + ', 1::bool as g' + ", 'POINT(0 0)'::geometry as h" + // See https://github.com/CartoDB/CartoDB-SQL-API/issues/117 - ", now()::date as i" + + ', now()::date as i' + ", '1'::numeric as j" + - " LIMIT 0" - }), - headers: {host: 'vizzuality.cartodb.com'}, - method: 'GET' - },{ }, function(err, res) { - assert.equal(res.statusCode, 200, res.body); - var parsedBody = JSON.parse(res.body); - assert.equal(_.keys(parsedBody.fields).length, 10); - assert.equal(parsedBody.fields.a.type, 'number'); - assert.equal(parsedBody.fields.b.type, 'number'); - assert.equal(parsedBody.fields.c.type, 'string'); - assert.equal(parsedBody.fields.d.type, 'string'); - assert.equal(parsedBody.fields.e.type, 'date'); - assert.equal(parsedBody.fields.f.type, 'string'); - assert.equal(parsedBody.fields.g.type, 'boolean'); - assert.equal(parsedBody.fields.h.type, 'geometry'); - assert.equal(parsedBody.fields.i.type, 'date'); - assert.equal(parsedBody.fields.j.type, 'number'); - done(); + ' LIMIT 0' + }), + headers: { host: 'vizzuality.cartodb.com' }, + method: 'GET' + }, { }, function (err, res) { + assert.equal(res.statusCode, 200, res.body); + var parsedBody = JSON.parse(res.body); + assert.equal(_.keys(parsedBody.fields).length, 10); + assert.equal(parsedBody.fields.a.type, 'number'); + assert.equal(parsedBody.fields.b.type, 'number'); + assert.equal(parsedBody.fields.c.type, 'string'); + assert.equal(parsedBody.fields.d.type, 'string'); + assert.equal(parsedBody.fields.e.type, 'date'); + assert.equal(parsedBody.fields.f.type, 'string'); + assert.equal(parsedBody.fields.g.type, 'boolean'); + assert.equal(parsedBody.fields.h.type, 'geometry'); + assert.equal(parsedBody.fields.i.type, 'date'); + assert.equal(parsedBody.fields.j.type, 'number'); + done(); + }); }); -}); -// See https://github.com/Vizzuality/CartoDB-SQL-API/issues/100 -it('numeric fields are rendered as numbers in JSON', function(done){ - assert.response(server, { - url: '/api/v1/sql?' + querystring.stringify({ - q: "WITH inp AS ( SELECT 1::int2 as a, 2::int4 as b, " + - "3::int8 as c, 4::float4 as d, " + - "5::float8 as e, 6::numeric as f" + - ") SELECT a,b,c,d,e,f," + - " ARRAY[a] AS _a, " + - " ARRAY[b] AS _b, " + - " ARRAY[c] AS _c, " + - " ARRAY[d] AS _d, " + - " ARRAY[e] AS _e, " + - " ARRAY[f] AS _f " + - "FROM inp" - }), - headers: {host: 'vizzuality.cartodb.com'}, - method: 'GET' - },{ }, function(err, res) { - assert.equal(res.statusCode, 200, res.body); - var parsedBody = JSON.parse(res.body); - var row = parsedBody.rows[0]; - assert.equal(typeof(row.a), 'number'); - assert.equal(typeof(row.b), 'number'); - assert.equal(typeof(row.c), 'number'); - assert.equal(typeof(row.d), 'number'); - assert.equal(typeof(row.e), 'number'); - assert.equal(typeof(row.f), 'number'); - assert.equal(typeof(row._a[0]), 'number'); - assert.equal(typeof(row._b[0]), 'number'); - assert.equal(typeof(row._c[0]), 'number'); - assert.equal(typeof(row._d[0]), 'number'); - assert.equal(typeof(row._e[0]), 'number'); - assert.equal(typeof(row._f[0]), 'number'); - done(); + // See https://github.com/Vizzuality/CartoDB-SQL-API/issues/100 + it('numeric fields are rendered as numbers in JSON', function (done) { + assert.response(server, { + url: '/api/v1/sql?' + querystring.stringify({ + q: 'WITH inp AS ( SELECT 1::int2 as a, 2::int4 as b, ' + + '3::int8 as c, 4::float4 as d, ' + + '5::float8 as e, 6::numeric as f' + + ') SELECT a,b,c,d,e,f,' + + ' ARRAY[a] AS _a, ' + + ' ARRAY[b] AS _b, ' + + ' ARRAY[c] AS _c, ' + + ' ARRAY[d] AS _d, ' + + ' ARRAY[e] AS _e, ' + + ' ARRAY[f] AS _f ' + + 'FROM inp' + }), + headers: { host: 'vizzuality.cartodb.com' }, + method: 'GET' + }, { }, function (err, res) { + assert.equal(res.statusCode, 200, res.body); + var parsedBody = JSON.parse(res.body); + var row = parsedBody.rows[0]; + assert.equal(typeof (row.a), 'number'); + assert.equal(typeof (row.b), 'number'); + assert.equal(typeof (row.c), 'number'); + assert.equal(typeof (row.d), 'number'); + assert.equal(typeof (row.e), 'number'); + assert.equal(typeof (row.f), 'number'); + assert.equal(typeof (row._a[0]), 'number'); + assert.equal(typeof (row._b[0]), 'number'); + assert.equal(typeof (row._c[0]), 'number'); + assert.equal(typeof (row._d[0]), 'number'); + assert.equal(typeof (row._e[0]), 'number'); + assert.equal(typeof (row._f[0]), 'number'); + done(); + }); }); -}); -// Timezone information is retained with JSON output -// -// NOTE: results of these tests rely on the TZ env variable -// being set to 'Europe/Rome'. The env variable cannot -// be set within this test in a reliable way, see -// https://github.com/joyent/node/issues/3286 -// -// FIXME: we'd like to also test UTC outputs of these -// numbers, but it'd currently take running the -// test again (new mocha run) with a different TZ -// -it('timezone info in JSON output', function(done){ - step( - function testEuropeRomeExplicit() { - var next = this; - assert.response(server, { - url: '/api/v1/sql?' + querystring.stringify({ - q: "SET timezone TO 'Europe/Rome'; SELECT '2000-01-01T00:00:00+01'::timestamptz as d" - }), - headers: {host: 'vizzuality.cartodb.com'}, - method: 'GET' - },{ }, function(err, res) { - try { - assert.equal(res.statusCode, 200, res.body); - var parsedBody = JSON.parse(res.body); - assert.equal(parsedBody.rows[0].d, '2000-01-01T00:00:00+0100'); - next(); - } catch (err) { - next(err); - } - }); - }, - function testEuropeRomeImplicit(err) { - assert.ifError(err); - var next = this; - assert.response(server, { - url: '/api/v1/sql?' + querystring.stringify({ - q: "SET timezone TO 'Europe/Rome'; SELECT '2000-01-01T00:00:00'::timestamp as d" - }), - headers: {host: 'vizzuality.cartodb.com'}, - method: 'GET' - },{ }, function(err, res) { - try { - assert.equal(res.statusCode, 200, res.body); - var parsedBody = JSON.parse(res.body); - assert.equal(parsedBody.rows[0].d, '2000-01-01T00:00:00+0100'); - next(); - } catch (err) { - next(err); - } - }); - }, - function testUTCExplicit(err) { - assert.ifError(err); - var next = this; - assert.response(server, { - url: '/api/v1/sql?' + querystring.stringify({ - q: "SET timezone TO 'UTC'; SELECT '2000-01-01T00:00:00+00'::timestamptz as d" - }), - headers: {host: 'vizzuality.cartodb.com'}, - method: 'GET' - },{ }, function(err, res) { - try { - assert.equal(res.statusCode, 200, res.body); - var parsedBody = JSON.parse(res.body); - assert.equal(parsedBody.rows[0].d, '2000-01-01T01:00:00+0100'); - next(); - } catch (err) { - next(err); - } - }); - }, - function testUTCImplicit(err) { - assert.ifError(err); - var next = this; - assert.response(server, { - url: '/api/v1/sql?' + querystring.stringify({ - q: "SET timezone TO 'UTC'; SELECT '2000-01-01T00:00:00'::timestamp as d" - }), - headers: {host: 'vizzuality.cartodb.com'}, - method: 'GET' - },{ }, function(err, res) { - try { - assert.equal(res.statusCode, 200, res.body); - var parsedBody = JSON.parse(res.body); - assert.equal(parsedBody.rows[0].d, '2000-01-01T00:00:00+0100'); - next(); - } catch (err) { - next(err); - } - }); - }, - function finish(err) { - done(err); - } - ); -}); + // Timezone information is retained with JSON output + // + // NOTE: results of these tests rely on the TZ env variable + // being set to 'Europe/Rome'. The env variable cannot + // be set within this test in a reliable way, see + // https://github.com/joyent/node/issues/3286 + // + // FIXME: we'd like to also test UTC outputs of these + // numbers, but it'd currently take running the + // test again (new mocha run) with a different TZ + // + it('timezone info in JSON output', function (done) { + step( + function testEuropeRomeExplicit () { + var next = this; + assert.response(server, { + url: '/api/v1/sql?' + querystring.stringify({ + q: "SET timezone TO 'Europe/Rome'; SELECT '2000-01-01T00:00:00+01'::timestamptz as d" + }), + headers: { host: 'vizzuality.cartodb.com' }, + method: 'GET' + }, { }, function (err, res) { + try { + assert.equal(res.statusCode, 200, res.body); + var parsedBody = JSON.parse(res.body); + assert.equal(parsedBody.rows[0].d, '2000-01-01T00:00:00+0100'); + next(); + } catch (err) { + next(err); + } + }); + }, + function testEuropeRomeImplicit (err) { + assert.ifError(err); + var next = this; + assert.response(server, { + url: '/api/v1/sql?' + querystring.stringify({ + q: "SET timezone TO 'Europe/Rome'; SELECT '2000-01-01T00:00:00'::timestamp as d" + }), + headers: { host: 'vizzuality.cartodb.com' }, + method: 'GET' + }, { }, function (err, res) { + try { + assert.equal(res.statusCode, 200, res.body); + var parsedBody = JSON.parse(res.body); + assert.equal(parsedBody.rows[0].d, '2000-01-01T00:00:00+0100'); + next(); + } catch (err) { + next(err); + } + }); + }, + function testUTCExplicit (err) { + assert.ifError(err); + var next = this; + assert.response(server, { + url: '/api/v1/sql?' + querystring.stringify({ + q: "SET timezone TO 'UTC'; SELECT '2000-01-01T00:00:00+00'::timestamptz as d" + }), + headers: { host: 'vizzuality.cartodb.com' }, + method: 'GET' + }, { }, function (err, res) { + try { + assert.equal(res.statusCode, 200, res.body); + var parsedBody = JSON.parse(res.body); + assert.equal(parsedBody.rows[0].d, '2000-01-01T01:00:00+0100'); + next(); + } catch (err) { + next(err); + } + }); + }, + function testUTCImplicit (err) { + assert.ifError(err); + var next = this; + assert.response(server, { + url: '/api/v1/sql?' + querystring.stringify({ + q: "SET timezone TO 'UTC'; SELECT '2000-01-01T00:00:00'::timestamp as d" + }), + headers: { host: 'vizzuality.cartodb.com' }, + method: 'GET' + }, { }, function (err, res) { + try { + assert.equal(res.statusCode, 200, res.body); + var parsedBody = JSON.parse(res.body); + assert.equal(parsedBody.rows[0].d, '2000-01-01T00:00:00+0100'); + next(); + } catch (err) { + next(err); + } + }); + }, + function finish (err) { + done(err); + } + ); + }); -// WARNING and NOTICE in JSON output -// See https://github.com/CartoDB/CartoDB-SQL-API/issues/104 -it('notice and warning info in JSON output', function(done){ - step( - function addRaiseFunction() { - assert.response(server, { - url: '/api/v1/sql?' + querystring.stringify({ - q: "create or replace function raise(lvl text, msg text) returns void as $$ begin if lvl = 'notice' " + + // WARNING and NOTICE in JSON output + // See https://github.com/CartoDB/CartoDB-SQL-API/issues/104 + it('notice and warning info in JSON output', function (done) { + step( + function addRaiseFunction () { + assert.response(server, { + 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;", - api_key: '1234' - }), - headers: {host: 'vizzuality.cartodb.com'}, - method: 'GET' - }, RESPONSE_OK, this); - }, - function raiseNotice(err) { - assert.ifError(err); - var next = this; - assert.response(server, { - url: '/api/v1/sql?' + querystring.stringify({ - q: "SET client_min_messages TO 'notice'; select raise('notice', 'hello notice')" - }), - headers: {host: 'vizzuality.cartodb.com'}, - method: 'GET' - }, RESPONSE_OK, function(err, res) { - try { - var parsedBody = JSON.parse(res.body); - assert.ok(parsedBody.hasOwnProperty('notices'), 'Missing notices from result'); - assert.equal(parsedBody.notices.length, 1); - assert.equal(parsedBody.notices[0], 'hello notice'); - } catch (e) { - return next(e); - } - next(err); - }); - }, - function raiseWarning(err) { - assert.ifError(err); - var next = this; - assert.response(server, { - url: '/api/v1/sql?' + querystring.stringify({ - q: "SET client_min_messages TO 'notice'; select raise('warning', 'hello warning')" - }), - headers: {host: 'vizzuality.cartodb.com'}, - method: 'GET' - }, RESPONSE_OK, function(err, res) { - try { - var parsedBody = JSON.parse(res.body); - assert.ok(parsedBody.hasOwnProperty('warnings'), 'Missing warnings from result'); - assert.equal(parsedBody.warnings.length, 1); - assert.equal(parsedBody.warnings[0], 'hello warning'); - } catch (e) { - return next(e); - } - next(err); - }); - }, - function raiseBothWarningAndNotice(err) { - assert.ifError(err); - var next = this; - assert.response(server, { - url: '/api/v1/sql?' + querystring.stringify({ - q: "SET client_min_messages TO 'notice'; select raise('warning', 'hello again warning'), " + + api_key: '1234' + }), + headers: { host: 'vizzuality.cartodb.com' }, + method: 'GET' + }, RESPONSE_OK, this); + }, + function raiseNotice (err) { + assert.ifError(err); + var next = this; + assert.response(server, { + url: '/api/v1/sql?' + querystring.stringify({ + q: "SET client_min_messages TO 'notice'; select raise('notice', 'hello notice')" + }), + headers: { host: 'vizzuality.cartodb.com' }, + method: 'GET' + }, RESPONSE_OK, function (err, res) { + try { + var parsedBody = JSON.parse(res.body); + assert.ok(parsedBody.hasOwnProperty('notices'), 'Missing notices from result'); + assert.equal(parsedBody.notices.length, 1); + assert.equal(parsedBody.notices[0], 'hello notice'); + } catch (e) { + return next(e); + } + next(err); + }); + }, + function raiseWarning (err) { + assert.ifError(err); + var next = this; + assert.response(server, { + url: '/api/v1/sql?' + querystring.stringify({ + q: "SET client_min_messages TO 'notice'; select raise('warning', 'hello warning')" + }), + headers: { host: 'vizzuality.cartodb.com' }, + method: 'GET' + }, RESPONSE_OK, function (err, res) { + try { + var parsedBody = JSON.parse(res.body); + assert.ok(parsedBody.hasOwnProperty('warnings'), 'Missing warnings from result'); + assert.equal(parsedBody.warnings.length, 1); + assert.equal(parsedBody.warnings[0], 'hello warning'); + } catch (e) { + return next(e); + } + next(err); + }); + }, + function raiseBothWarningAndNotice (err) { + assert.ifError(err); + var next = this; + assert.response(server, { + url: '/api/v1/sql?' + querystring.stringify({ + 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' - }, RESPONSE_OK, function(err, res) { - try { - var parsedBody = JSON.parse(res.body); - assert.ok(parsedBody.hasOwnProperty('warnings'), 'Missing warnings from result'); - assert.equal(parsedBody.warnings.length, 1); - assert.equal(parsedBody.warnings[0], 'hello again warning'); - assert.ok(parsedBody.hasOwnProperty('notices'), 'Missing notices from result'); - assert.equal(parsedBody.notices.length, 1); - assert.equal(parsedBody.notices[0], 'hello again notice'); - } catch (e) { - return next(e); - } - next(err); - }); - }, - function delRaiseFunction() { - assert.response(server, { - url: '/api/v1/sql?' + querystring.stringify({ - q: "DROP function raise(text, text)", - api_key: '1234' - }), - headers: {host: 'vizzuality.cartodb.com'}, - method: 'GET' - }, RESPONSE_OK, function(err, res) { - try { + }), + headers: { host: 'vizzuality.cartodb.com' }, + method: 'GET' + }, RESPONSE_OK, function (err, res) { + try { + var parsedBody = JSON.parse(res.body); + assert.ok(parsedBody.hasOwnProperty('warnings'), 'Missing warnings from result'); + assert.equal(parsedBody.warnings.length, 1); + assert.equal(parsedBody.warnings[0], 'hello again warning'); + assert.ok(parsedBody.hasOwnProperty('notices'), 'Missing notices from result'); + assert.equal(parsedBody.notices.length, 1); + assert.equal(parsedBody.notices[0], 'hello again notice'); + } catch (e) { + return next(e); + } + next(err); + }); + }, + function delRaiseFunction () { + assert.response(server, { + url: '/api/v1/sql?' + querystring.stringify({ + q: 'DROP function raise(text, text)', + api_key: '1234' + }), + headers: { host: 'vizzuality.cartodb.com' }, + method: 'GET' + }, RESPONSE_OK, function (err, res) { + try { + assert.equal(res.statusCode, 200, res.body); + JSON.parse(res.body); + } catch (e) { + err = new Error(err + ',' + e); + } + done(err); + }); + } + ); + }); + + it('GET with callback param returns wrapped result set with callback as jsonp', function (done) { + assert.response(server, { + url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4&callback=foo_jsonp', + headers: { host: 'vizzuality.cartodb.com' }, + method: 'GET' + }, { }, function (err, res) { assert.equal(res.statusCode, 200, res.body); - JSON.parse(res.body); - } catch (e) { - err = new Error(err + ',' + e); - } - done(err); - }); - } - ); -}); - -it('GET with callback param returns wrapped result set with callback as jsonp', function(done) { - assert.response(server, { - url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4&callback=foo_jsonp', - headers: {host: 'vizzuality.cartodb.com'}, - method: 'GET' - },{ }, function(err, res) { - assert.equal(res.statusCode, 200, res.body); - assert.ok(res.body.match(/foo\_jsonp\(.*\)/)); - done(); + assert.ok(res.body.match(/foo\_jsonp\(.*\)/)); + done(); + }); }); -}); -it('GET with callback must return 200 status error even if it is an error', function(done){ - assert.response(server, { - url: "/api/v1/sql?q=DROP%20TABLE%20untitle_table_4&callback=foo_jsonp", - headers: {host: 'vizzuality.cartodb.com'}, - method: 'GET' - },{}, function(err, res) { - assert.equal(res.statusCode, 200, res.statusCode + ': ' + res.body); - var didRunJsonCallback = false; - // jshint ignore:start - function foo_jsonp(body) { - assert.ok(body.error[0].match(/must be owner of.+? untitle_table_4/)); - didRunJsonCallback = true; - } - eval(res.body); - // jshint ignore:end - assert.ok(didRunJsonCallback); - done(); + it('GET with callback must return 200 status error even if it is an error', function (done) { + assert.response(server, { + url: '/api/v1/sql?q=DROP%20TABLE%20untitle_table_4&callback=foo_jsonp', + headers: { host: 'vizzuality.cartodb.com' }, + method: 'GET' + }, {}, function (err, res) { + assert.equal(res.statusCode, 200, res.statusCode + ': ' + res.body); + var didRunJsonCallback = false; + // jshint ignore:start + function foo_jsonp (body) { + assert.ok(body.error[0].match(/must be owner of.+? untitle_table_4/)); + didRunJsonCallback = true; + } + eval(res.body); + // jshint ignore:end + assert.ok(didRunJsonCallback); + done(); + }); }); -}); -it('GET with slow query exceeding statement timeout returns proper error message', function(done){ - assert.response(server, { - url: "/api/v1/sql?q=select%20pg_sleep(2.1)%20as%20sleep", - headers: {host: 'vizzuality.cartodb.com'}, + it('GET with slow query exceeding statement timeout returns proper error message', function (done) { + assert.response(server, { + url: '/api/v1/sql?q=select%20pg_sleep(2.1)%20as%20sleep', + headers: { host: 'vizzuality.cartodb.com' }, method: 'GET' }, { @@ -790,21 +791,21 @@ it('GET with slow query exceeding statement timeout returns proper error message 'Content-Type': 'application/json; charset=utf-8' } }, - function(err, res) { + function (err, res) { var error = JSON.parse(res.body); assert.deepEqual(error.error, [ - 'You are over platform\'s limits: SQL query timeout error.' + + 'You are over platform\'s limits: SQL query timeout error.' + ' Refactor your query before running again or contact CARTO support for more details.' - ]); + ]); done(); }); -}); + }); -it('GET with slow query exceeding statement timeout returns proper error message (streaming)', function(done){ - assert.response(server, { - url: "/api/v1/sql?q=SELECT%20pg_sleep(generate_series(2,10)/10.0)", - headers: {host: 'vizzuality.cartodb.com'}, + it('GET with slow query exceeding statement timeout returns proper error message (streaming)', function (done) { + assert.response(server, { + url: '/api/v1/sql?q=SELECT%20pg_sleep(generate_series(2,10)/10.0)', + headers: { host: 'vizzuality.cartodb.com' }, method: 'GET' }, { @@ -813,21 +814,21 @@ it('GET with slow query exceeding statement timeout returns proper error message 'Content-Type': 'application/json; charset=utf-8' } }, - function(err, res) { + function (err, res) { var error = JSON.parse(res.body); assert.deepEqual(error.error, [ - 'You are over platform\'s limits: SQL query timeout error.' + + 'You are over platform\'s limits: SQL query timeout error.' + ' Refactor your query before running again or contact CARTO support for more details.' - ]); + ]); done(); }); -}); + }); -it('GET with slow python script exceeding statement timeout returns proper error message', function(done){ - assert.response(server, { - url: "/api/v1/sql?q=select%20py_sleep(2.1)", - headers: {host: 'vizzuality.cartodb.com'}, + it('GET with slow python script exceeding statement timeout returns proper error message', function (done) { + assert.response(server, { + url: '/api/v1/sql?q=select%20py_sleep(2.1)', + headers: { host: 'vizzuality.cartodb.com' }, method: 'GET' }, { @@ -836,34 +837,33 @@ it('GET with slow python script exceeding statement timeout returns proper error 'Content-Type': 'application/json; charset=utf-8' } }, - function(err, res) { + function (err, res) { var error = JSON.parse(res.body); assert.deepEqual(error.error, [ - 'You are over platform\'s limits: SQL query timeout error.' + + 'You are over platform\'s limits: SQL query timeout error.' + ' Refactor your query before running again or contact CARTO support for more details.' - ]); + ]); done(); }); -}); - - it('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; var consoleErrorFn = console.error; var hit = false; var consoleError; - console.error = function(what) { + console.error = function (what) { hit = true; consoleError = what; }; assert.response( server, { - url: "/api/v1/sql?" + querystring.stringify({ - q: "SELECT * FROM untitle_table_4" + url: '/api/v1/sql?' + querystring.stringify({ + q: 'SELECT * FROM untitle_table_4' }), headers: { host: 'vizzuality.cartodb.com' @@ -873,10 +873,10 @@ it('GET with slow python script exceeding statement timeout returns proper error { status: 400 }, - function() { + function () { assert.equal(hit, true); var parsedError = JSON.parse(consoleError); - assert.ok(parsedError.error.match(/^row too large.*/i), "Expecting row size limit error"); + assert.ok(parsedError.error.match(/^row too large.*/i), 'Expecting row size limit error'); assert.equal(parsedError.username, 'vizzuality'); assert.equal(parsedError.type, 'row_size_limit_exceeded'); @@ -887,6 +887,4 @@ it('GET with slow python script exceeding statement timeout returns proper error } ); }); - - }); diff --git a/test/acceptance/auth-api-test.js b/test/acceptance/auth-api-test.js index 4c9654a8..a196d81b 100644 --- a/test/acceptance/auth-api-test.js +++ b/test/acceptance/auth-api-test.js @@ -188,7 +188,6 @@ describe('Auth API', function () { }); }); - it('should fail while fetching information schema and using default API key', function (done) { this.testClient = new TestClient({ authorization: 'vizzuality:default_public' }); const expectedResponse = { diff --git a/test/acceptance/backend-crash-test.js b/test/acceptance/backend-crash-test.js index 5e11c27f..85ff76da 100644 --- a/test/acceptance/backend-crash-test.js +++ b/test/acceptance/backend-crash-test.js @@ -7,73 +7,71 @@ var step = require('step'); var net = require('net'); var sql_server_port = 5540; -var sql_server = net.createServer(function(c) { - c.destroy(); - sql_server.close(function() { - }); +var sql_server = net.createServer(function (c) { + c.destroy(); + sql_server.close(function () { + }); }); -describe('backend crash', function() { - -before(function(done){ - sql_server.listen(sql_server_port, done); -}); - -// See https://github.com/CartoDB/CartoDB-SQL-API/issues/135 -it('does not hang server', function(done){ - 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 server = require('../../lib/server')(); - step( - function sendQuery() { - assert.response(server, { - url: '/api/v1/sql?q=SELECT+1', - method: 'GET', - headers: {host: 'vizzuality.localhost' } - },{}, this); - }, - function checkResponse(err, res) { - assert.ifError(err); - assert.equal(res.statusCode, 500, res.statusCode + ': ' + res.body); - var parsed = JSON.parse(res.body); - assert.ok(parsed.error); - var msg = parsed.error[0]; - assert.ok(msg.match(/unexpected.*end/), msg); - return null; - }, - function sendAnotherQuery() { - assert.response(server, { - url: '/api/v1/sql?q=SELECT+2', - method: 'GET', - headers: {host: 'vizzuality.localhost' } - },{}, this); - }, - function checkResponse(err, res) { - assert.ifError(err); - assert.equal(res.statusCode, 500, res.statusCode + ': ' + res.body); - var parsed = JSON.parse(res.body); - assert.ok(parsed.error); - var msg = parsed.error[0]; - assert.ok(msg.match(/connect/), msg); - return null; - }, - function finish(err) { - global.settings.db_host = db_host_backup; - global.settings.db_port = db_port_backup; - done(err); - } - ); -}); - -after(function(done) { - // be sure the sql_server is closed - if (sql_server.listening) { - return sql_server.close(done); - } - - done(); -}); +describe('backend crash', function () { + before(function (done) { + sql_server.listen(sql_server_port, done); + }); + // See https://github.com/CartoDB/CartoDB-SQL-API/issues/135 + it('does not hang server', function (done) { + 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 server = require('../../lib/server')(); + step( + function sendQuery () { + assert.response(server, { + url: '/api/v1/sql?q=SELECT+1', + method: 'GET', + headers: { host: 'vizzuality.localhost' } + }, {}, this); + }, + function checkResponse (err, res) { + assert.ifError(err); + assert.equal(res.statusCode, 500, res.statusCode + ': ' + res.body); + var parsed = JSON.parse(res.body); + assert.ok(parsed.error); + var msg = parsed.error[0]; + assert.ok(msg.match(/unexpected.*end/), msg); + return null; + }, + function sendAnotherQuery () { + assert.response(server, { + url: '/api/v1/sql?q=SELECT+2', + method: 'GET', + headers: { host: 'vizzuality.localhost' } + }, {}, this); + }, + function checkResponse (err, res) { + assert.ifError(err); + assert.equal(res.statusCode, 500, res.statusCode + ': ' + res.body); + var parsed = JSON.parse(res.body); + assert.ok(parsed.error); + var msg = parsed.error[0]; + assert.ok(msg.match(/connect/), msg); + return null; + }, + function finish (err) { + global.settings.db_host = db_host_backup; + global.settings.db_port = db_port_backup; + done(err); + } + ); + }); + + after(function (done) { + // be sure the sql_server is closed + if (sql_server.listening) { + return sql_server.close(done); + } + + done(); + }); }); diff --git a/test/acceptance/batch/batch-drain-test.js b/test/acceptance/batch/batch-drain-test.js index 33dbdb7e..4db82299 100644 --- a/test/acceptance/batch/batch-drain-test.js +++ b/test/acceptance/batch/batch-drain-test.js @@ -13,13 +13,13 @@ var JobService = require('../../../lib/batch/job-service'); var JobCanceller = require('../../../lib/batch/job-canceller'); var metadataBackend = require('cartodb-redis')({ pool: redisUtils.getPool() }); -describe('batch module', function() { +describe('batch module', function () { var dbInstance = 'localhost'; var username = 'vizzuality'; var pool = redisUtils.getPool(); var logger = new BatchLogger(null, 'batch-queries'); var jobPublisher = new JobPublisher(pool); - var jobQueue = new JobQueue(metadataBackend, jobPublisher, logger); + var jobQueue = new JobQueue(metadataBackend, jobPublisher, logger); var jobBackend = new JobBackend(metadataBackend, jobQueue, logger); var jobCanceller = new JobCanceller(); var jobService = new JobService(jobBackend, jobCanceller, logger); @@ -35,7 +35,7 @@ describe('batch module', function() { redisUtils.clean('batch:*', done); }); - function createJob(sql, done) { + function createJob (sql, done) { var data = { user: username, query: sql, @@ -43,7 +43,7 @@ describe('batch module', function() { dbname: 'cartodb_test_user_1_db', dbuser: 'test_cartodb_user_1', port: 5432, - pass: 'test_cartodb_user_1_pass', + pass: 'test_cartodb_user_1_pass' }; jobService.create(data, function (err, job) { @@ -81,5 +81,4 @@ describe('batch module', function() { }, 50); }); }); - }); diff --git a/test/acceptance/batch/batch-limits-test.js b/test/acceptance/batch/batch-limits-test.js index 9b74c7a0..46644320 100644 --- a/test/acceptance/batch/batch-limits-test.js +++ b/test/acceptance/batch/batch-limits-test.js @@ -9,24 +9,23 @@ var redisUtils = require('../../support/redis-utils'); var metadataBackend = require('cartodb-redis')({ pool: redisUtils.getPool() }); const db_utils = require('../../support/db_utils'); -describe('batch query statement_timeout limit', function() { - - before(function(done) { +describe('batch query statement_timeout limit', function () { + before(function (done) { this.batchTestClient = new BatchTestClient(); this.batchQueryTimeout = global.settings.batch_query_timeout; global.settings.batch_query_timeout = 15000; metadataBackend.redisCmd(5, 'HMSET', ['limits:batch:vizzuality', 'timeout', 100], done); }); before(db_utils.resetPgBouncerConnections); - after(function(done) { + after(function (done) { global.settings.batch_query_timeout = this.batchQueryTimeout; - redisUtils.clean('limits:batch:*', function() { + redisUtils.clean('limits:batch:*', function () { this.batchTestClient.drain(done); }.bind(this)); }); after(db_utils.resetPgBouncerConnections); - function jobPayload(query) { + function jobPayload (query) { return { query: query }; @@ -34,7 +33,7 @@ describe('batch query statement_timeout limit', function() { it('should cancel with user statement_timeout limit', function (done) { var payload = jobPayload('select pg_sleep(10)'); - this.batchTestClient.createJob(payload, function(err, jobResult) { + this.batchTestClient.createJob(payload, function (err, jobResult) { if (err) { return done(err); } @@ -48,5 +47,4 @@ describe('batch query statement_timeout limit', function() { }); }); }); - }); diff --git a/test/acceptance/batch/batch-multiquery-test.js b/test/acceptance/batch/batch-multiquery-test.js index 3a52e554..b93d7aac 100644 --- a/test/acceptance/batch/batch-multiquery-test.js +++ b/test/acceptance/batch/batch-multiquery-test.js @@ -8,14 +8,14 @@ var JobStatus = require('../../../lib/batch/job-status'); var assert = require('../../support/assert'); var queue = require('queue-async'); -describe('batch multiquery', function() { - function jobPayload(query) { +describe('batch multiquery', function () { + function jobPayload (query) { return { query: query }; } - before(function() { + before(function () { this.batchTestClient = new BatchTestClient(); }); @@ -30,7 +30,7 @@ describe('batch multiquery', function() { ]; var payload = jobPayload(queries); - this.batchTestClient.createJob(payload, function(err, jobResult) { + this.batchTestClient.createJob(payload, function (err, jobResult) { if (err) { return done(err); } @@ -51,7 +51,7 @@ describe('batch multiquery', function() { ]; var payload = jobPayload(queries); - this.batchTestClient.createJob(payload, function(err, jobResult) { + this.batchTestClient.createJob(payload, function (err, jobResult) { if (err) { return done(err); } @@ -73,7 +73,7 @@ describe('batch multiquery', function() { ]; var payload = jobPayload(queries); - this.batchTestClient.createJob(payload, function(err, jobResult) { + this.batchTestClient.createJob(payload, function (err, jobResult) { if (err) { return done(err); } @@ -87,7 +87,6 @@ describe('batch multiquery', function() { }); }); - it('should perform one multiquery job with three queries and fail on second one', function (done) { var queries = [ 'select pg_sleep(0)', @@ -96,7 +95,7 @@ describe('batch multiquery', function() { ]; var payload = jobPayload(queries); - this.batchTestClient.createJob(payload, function(err, jobResult) { + this.batchTestClient.createJob(payload, function (err, jobResult) { if (err) { return done(err); } @@ -126,9 +125,9 @@ describe('batch multiquery', function() { var jobsQueue = queue(1); - jobs.forEach(function(job) { - jobsQueue.defer(function(payload, done) { - self.batchTestClient.createJob(payload, function(err, jobResult) { + jobs.forEach(function (job) { + jobsQueue.defer(function (payload, done) { + self.batchTestClient.createJob(payload, function (err, jobResult) { if (err) { return done(err); } @@ -142,7 +141,7 @@ describe('batch multiquery', function() { return done(err); } - jobsCreated.forEach(function(job) { + jobsCreated.forEach(function (job) { assert.equal(job.status, JobStatus.DONE); }); @@ -167,9 +166,9 @@ describe('batch multiquery', function() { var expectedStatus = [JobStatus.FAILED, JobStatus.DONE]; var jobsQueue = queue(1); - jobs.forEach(function(job) { - jobsQueue.defer(function(payload, done) { - self.batchTestClient.createJob(payload, function(err, jobResult) { + jobs.forEach(function (job) { + jobsQueue.defer(function (payload, done) { + self.batchTestClient.createJob(payload, function (err, jobResult) { if (err) { return done(err); } @@ -183,7 +182,7 @@ describe('batch multiquery', function() { return done(err); } - var statuses = jobsCreated.map(function(job) { + var statuses = jobsCreated.map(function (job) { return job.status; }); assert.deepEqual(statuses, expectedStatus); @@ -209,9 +208,9 @@ describe('batch multiquery', function() { var expectedStatus = [JobStatus.DONE, JobStatus.FAILED]; var jobsQueue = queue(1); - jobs.forEach(function(job) { - jobsQueue.defer(function(payload, done) { - self.batchTestClient.createJob(payload, function(err, jobResult) { + jobs.forEach(function (job) { + jobsQueue.defer(function (payload, done) { + self.batchTestClient.createJob(payload, function (err, jobResult) { if (err) { return done(err); } @@ -225,7 +224,7 @@ describe('batch multiquery', function() { return done(err); } - var statuses = jobsCreated.map(function(job) { + var statuses = jobsCreated.map(function (job) { return job.status; }); assert.deepEqual(statuses, expectedStatus); diff --git a/test/acceptance/batch/batch-test.js b/test/acceptance/batch/batch-test.js index 9da854af..256beb47 100644 --- a/test/acceptance/batch/batch-test.js +++ b/test/acceptance/batch/batch-test.js @@ -7,17 +7,16 @@ var queue = require('queue-async'); var BatchTestClient = require('../../support/batch-test-client'); var JobStatus = require('../../../lib/batch/job-status'); -describe('batch happy cases', function() { - - before(function() { +describe('batch happy cases', function () { + before(function () { this.batchTestClient = new BatchTestClient(); }); - after(function(done) { + after(function (done) { this.batchTestClient.drain(done); }); - function jobPayload(query) { + function jobPayload (query) { return { query: query }; @@ -25,7 +24,7 @@ describe('batch happy cases', function() { it('should perform job with select', function (done) { var payload = jobPayload('select * from private_table'); - this.batchTestClient.createJob(payload, function(err, jobResult) { + this.batchTestClient.createJob(payload, function (err, jobResult) { if (err) { return done(err); } @@ -43,7 +42,7 @@ describe('batch happy cases', function() { var payload = jobPayload(` DROP TABLE IF EXISTS batch_test_table; SELECT * INTO batch_test_table FROM (SELECT * from private_table) AS job`); - this.batchTestClient.createJob(payload, function(err, jobResult) { + this.batchTestClient.createJob(payload, function (err, jobResult) { if (err) { return done(err); } @@ -59,7 +58,7 @@ describe('batch happy cases', function() { it('should perform job with select from result table', function (done) { var payload = jobPayload('select * from batch_test_table'); - this.batchTestClient.createJob(payload, function(err, jobResult) { + this.batchTestClient.createJob(payload, function (err, jobResult) { if (err) { return done(err); } @@ -79,14 +78,14 @@ describe('batch happy cases', function() { var jobs = [ 'select * from private_table', 'select * from private_table', - 'select * from private_table', + 'select * from private_table' ]; var jobsQueue = queue(1); - jobs.forEach(function(job) { - jobsQueue.defer(function(payload, done) { - self.batchTestClient.createJob(payload, function(err, jobResult) { + jobs.forEach(function (job) { + jobsQueue.defer(function (payload, done) { + self.batchTestClient.createJob(payload, function (err, jobResult) { if (err) { return done(err); } @@ -100,7 +99,7 @@ describe('batch happy cases', function() { return done(err); } - jobsCreated.forEach(function(job) { + jobsCreated.forEach(function (job) { assert.equal(job.status, JobStatus.DONE); }); @@ -119,9 +118,9 @@ describe('batch happy cases', function() { var jobsQueue = queue(1); - jobs.forEach(function(job) { - jobsQueue.defer(function(payload, done) { - self.batchTestClient.createJob(payload, function(err, jobResult) { + jobs.forEach(function (job) { + jobsQueue.defer(function (payload, done) { + self.batchTestClient.createJob(payload, function (err, jobResult) { if (err) { return done(err); } @@ -135,7 +134,7 @@ describe('batch happy cases', function() { return done(err); } - jobsCreated.forEach(function(job) { + jobsCreated.forEach(function (job) { assert.equal(job.status, JobStatus.FAILED); }); @@ -147,7 +146,7 @@ describe('batch happy cases', function() { var queries = ['select * from private_table limit 1', 'select * from private_table']; var payload = jobPayload(queries); - this.batchTestClient.createJob(payload, function(err, jobResult) { + this.batchTestClient.createJob(payload, function (err, jobResult) { if (err) { return done(err); } @@ -165,7 +164,7 @@ describe('batch happy cases', function() { var queries = ['select * from private_table', 'select * from undefined_table']; var payload = jobPayload(queries); - this.batchTestClient.createJob(payload, function(err, jobResult) { + this.batchTestClient.createJob(payload, function (err, jobResult) { if (err) { return done(err); } @@ -183,7 +182,7 @@ describe('batch happy cases', function() { var queries = ['select * from undefined_table', 'select * from private_table']; var payload = jobPayload(queries); - this.batchTestClient.createJob(payload, function(err, jobResult) { + this.batchTestClient.createJob(payload, function (err, jobResult) { if (err) { return done(err); } diff --git a/test/acceptance/batch/batch-wip-test.js b/test/acceptance/batch/batch-wip-test.js index 63e0303d..464728ed 100644 --- a/test/acceptance/batch/batch-wip-test.js +++ b/test/acceptance/batch/batch-wip-test.js @@ -6,17 +6,16 @@ var assert = require('../../support/assert'); var BatchTestClient = require('../../support/batch-test-client'); var JobStatus = require('../../../lib/batch/job-status'); -describe('batch work in progress endpoint happy cases', function() { - - before(function() { +describe('batch work in progress endpoint happy cases', function () { + before(function () { this.batchTestClient = new BatchTestClient(); }); - after(function(done) { + after(function (done) { this.batchTestClient.drain(done); }); - function jobPayload(query) { + function jobPayload (query) { return { query: query }; @@ -28,7 +27,7 @@ describe('batch work in progress endpoint happy cases', function() { var queries = ['select pg_sleep(3)']; var payload = jobPayload(queries); - self.batchTestClient.createJob(payload, function(err, jobResult) { + self.batchTestClient.createJob(payload, function (err, jobResult) { if (err) { return done(err); } @@ -67,7 +66,7 @@ describe('batch work in progress endpoint happy cases', function() { var queries = ['select pg_sleep(0)']; var payload = jobPayload(queries); - self.batchTestClient.createJob(payload, function(err, jobResult) { + self.batchTestClient.createJob(payload, function (err, jobResult) { if (err) { return done(err); } diff --git a/test/acceptance/batch/job-callback-template-test.js b/test/acceptance/batch/job-callback-template-test.js index 94545650..e77c4ea9 100644 --- a/test/acceptance/batch/job-callback-template-test.js +++ b/test/acceptance/batch/job-callback-template-test.js @@ -21,11 +21,11 @@ describe('Batch API callback templates', function () { ' and keep the original templated query but use the error message', function (done) { var self = this; var payload = { - "query": { - "query": [ + query: { + query: [ { - "query": "SELECT * FROM invalid_table", - "onerror": "INSERT INTO test_batch_errors " + + query: 'SELECT * FROM invalid_table', + onerror: 'INSERT INTO test_batch_errors ' + "values ('<%= job_id %>', '<%= error_message %>')" } ] @@ -34,8 +34,8 @@ describe('Batch API callback templates', function () { var expectedQuery = { query: [ { - "query": "SELECT * FROM invalid_table", - "onerror": "INSERT INTO test_batch_errors values ('<%= job_id %>', '<%= error_message %>')", + query: 'SELECT * FROM invalid_table', + onerror: "INSERT INTO test_batch_errors values ('<%= job_id %>', '<%= error_message %>')", status: 'failed', fallback_status: 'done' } @@ -45,45 +45,45 @@ describe('Batch API callback templates', function () { self.testClient.getResult( 'BEGIN; DROP TABLE IF EXISTS test_batch_errors; ' + 'CREATE TABLE test_batch_errors (job_id text, error_message text); COMMIT', function (err) { - if (err) { - return done(err); - } - - self.batchTestClient.createJob(payload, function(err, jobResult) { if (err) { return done(err); } - jobResult.getStatus(JobStatus.FAILED, function (err, job) { + self.batchTestClient.createJob(payload, function (err, jobResult) { if (err) { return done(err); } - jobResult.validateExpectedResponse(expectedQuery); - self.testClient.getResult('select * from test_batch_errors', function(err, rows) { + + jobResult.getStatus(JobStatus.FAILED, function (err, job) { if (err) { return done(err); } - assert.equal(rows[0].job_id, job.job_id); - assert.equal(rows[0].error_message, 'relation "invalid_table" does not exist'); - self.testClient.getResult('drop table test_batch_errors', done); + jobResult.validateExpectedResponse(expectedQuery); + self.testClient.getResult('select * from test_batch_errors', function (err, rows) { + if (err) { + return done(err); + } + assert.equal(rows[0].job_id, job.job_id); + assert.equal(rows[0].error_message, 'relation "invalid_table" does not exist'); + self.testClient.getResult('drop table test_batch_errors', done); + }); }); }); }); - }); }); it('should use template for job_id onsuccess callback ' + 'and keep the original templated query but use the job_id', function (done) { var self = this; var payload = { - "query": { - "query": [ + query: { + query: [ { - query: "drop table if exists batch_jobs; create table batch_jobs (job_id text)" + query: 'drop table if exists batch_jobs; create table batch_jobs (job_id text)' }, { - "query": "SELECT 1", - "onsuccess": "INSERT INTO batch_jobs values ('<%= job_id %>')" + query: 'SELECT 1', + onsuccess: "INSERT INTO batch_jobs values ('<%= job_id %>')" } ] } @@ -91,11 +91,11 @@ describe('Batch API callback templates', function () { var expectedQuery = { query: [ { - query: "drop table if exists batch_jobs; create table batch_jobs (job_id text)", + query: 'drop table if exists batch_jobs; create table batch_jobs (job_id text)', status: 'done' }, { - query: "SELECT 1", + query: 'SELECT 1', onsuccess: "INSERT INTO batch_jobs values ('<%= job_id %>')", status: 'done', fallback_status: 'done' @@ -103,7 +103,7 @@ describe('Batch API callback templates', function () { ] }; - self.batchTestClient.createJob(payload, function(err, jobResult) { + self.batchTestClient.createJob(payload, function (err, jobResult) { if (err) { return done(err); } @@ -113,7 +113,7 @@ describe('Batch API callback templates', function () { } jobResult.validateExpectedResponse(expectedQuery); - self.testClient.getResult('select * from batch_jobs', function(err, rows) { + self.testClient.getResult('select * from batch_jobs', function (err, rows) { if (err) { return done(err); } diff --git a/test/acceptance/batch/job-fallback-test.js b/test/acceptance/batch/job-fallback-test.js index a68ec186..92f7d138 100644 --- a/test/acceptance/batch/job-fallback-test.js +++ b/test/acceptance/batch/job-fallback-test.js @@ -7,12 +7,11 @@ var JobStatus = require('../../../lib/batch/job-status'); var BatchTestClient = require('../../support/batch-test-client'); describe('Batch API fallback job', function () { - - before(function() { + before(function () { this.batchTestClient = new BatchTestClient(); }); - after(function(done) { + after(function (done) { this.batchTestClient.drain(done); }); @@ -20,21 +19,21 @@ describe('Batch API fallback job', function () { var payload = { query: { query: [{ - query: "SELECT * FROM untitle_table_4", - onsuccess: "SELECT * FROM untitle_table_4 limit 1" + query: 'SELECT * FROM untitle_table_4', + onsuccess: 'SELECT * FROM untitle_table_4 limit 1' }] } }; var expectedQuery = { - "query": [{ - "query": "SELECT * FROM untitle_table_4", - "onsuccess": "SELECT * FROM untitle_table_4 limit 1", - "status": "done", - "fallback_status": "done" + query: [{ + query: 'SELECT * FROM untitle_table_4', + onsuccess: 'SELECT * FROM untitle_table_4 limit 1', + status: 'done', + fallback_status: 'done' }] }; - this.batchTestClient.createJob(payload, function(err, jobResult) { + this.batchTestClient.createJob(payload, function (err, jobResult) { if (err) { return done(err); } @@ -53,21 +52,21 @@ describe('Batch API fallback job', function () { var payload = { query: { query: [{ - query: "SELECT * FROM untitle_table_4", - onerror: "SELECT * FROM untitle_table_4 limit 1" + query: 'SELECT * FROM untitle_table_4', + onerror: 'SELECT * FROM untitle_table_4 limit 1' }] } }; var expectedQuery = { - "query": [{ - "query": "SELECT * FROM untitle_table_4", - "onerror": "SELECT * FROM untitle_table_4 limit 1", - "status": "done", - "fallback_status": "skipped" + query: [{ + query: 'SELECT * FROM untitle_table_4', + onerror: 'SELECT * FROM untitle_table_4 limit 1', + status: 'done', + fallback_status: 'skipped' }] }; - this.batchTestClient.createJob(payload, function(err, jobResult) { + this.batchTestClient.createJob(payload, function (err, jobResult) { if (err) { return done(err); } @@ -86,8 +85,8 @@ describe('Batch API fallback job', function () { var payload = { query: { query: [{ - query: "SELECT * FROM nonexistent_table /* query should fail */", - onerror: "SELECT * FROM untitle_table_4 limit 1" + query: 'SELECT * FROM nonexistent_table /* query should fail */', + onerror: 'SELECT * FROM untitle_table_4 limit 1' }] } }; @@ -101,7 +100,7 @@ describe('Batch API fallback job', function () { }] }; - this.batchTestClient.createJob(payload, function(err, jobResult) { + this.batchTestClient.createJob(payload, function (err, jobResult) { if (err) { return done(err); } @@ -121,8 +120,8 @@ describe('Batch API fallback job', function () { var payload = { query: { query: [{ - query: "SELECT * FROM nonexistent_table /* query should fail */", - onsuccess: "SELECT * FROM untitle_table_4 limit 1" + query: 'SELECT * FROM nonexistent_table /* query should fail */', + onsuccess: 'SELECT * FROM untitle_table_4 limit 1' }] } }; @@ -136,7 +135,7 @@ describe('Batch API fallback job', function () { }] }; - this.batchTestClient.createJob(payload, function(err, jobResult) { + this.batchTestClient.createJob(payload, function (err, jobResult) { if (err) { return done(err); } @@ -156,20 +155,20 @@ describe('Batch API fallback job', function () { var payload = { query: { query: [{ - query: "SELECT * FROM untitle_table_4", + query: 'SELECT * FROM untitle_table_4' }], - onsuccess: "SELECT * FROM untitle_table_4 limit 1" + onsuccess: 'SELECT * FROM untitle_table_4 limit 1' } }; var expectedQuery = { - "query": [{ - "query": "SELECT * FROM untitle_table_4", - "status": "done" + query: [{ + query: 'SELECT * FROM untitle_table_4', + status: 'done' }], - "onsuccess": "SELECT * FROM untitle_table_4 limit 1" + onsuccess: 'SELECT * FROM untitle_table_4 limit 1' }; - this.batchTestClient.createJob(payload, function(err, jobResult) { + this.batchTestClient.createJob(payload, function (err, jobResult) { if (err) { return done(err); } @@ -190,21 +189,21 @@ describe('Batch API fallback job', function () { var payload = { query: { query: [{ - query: "SELECT * FROM nonexistent_table /* query should fail */", + query: 'SELECT * FROM nonexistent_table /* query should fail */' }], - onsuccess: "SELECT * FROM untitle_table_4 limit 1" + onsuccess: 'SELECT * FROM untitle_table_4 limit 1' } }; var expectedQuery = { - "query": [{ - "query": "SELECT * FROM nonexistent_table /* query should fail */", - "status": "failed", - "failed_reason": 'relation "nonexistent_table" does not exist' + query: [{ + query: 'SELECT * FROM nonexistent_table /* query should fail */', + status: 'failed', + failed_reason: 'relation "nonexistent_table" does not exist' }], - "onsuccess": "SELECT * FROM untitle_table_4 limit 1" + onsuccess: 'SELECT * FROM untitle_table_4 limit 1' }; - this.batchTestClient.createJob(payload, function(err, jobResult) { + this.batchTestClient.createJob(payload, function (err, jobResult) { if (err) { return done(err); } @@ -225,21 +224,21 @@ describe('Batch API fallback job', function () { var payload = { query: { query: [{ - query: "SELECT * FROM nonexistent_table /* query should fail */" + query: 'SELECT * FROM nonexistent_table /* query should fail */' }], - onerror: "SELECT * FROM untitle_table_4 limit 1" + onerror: 'SELECT * FROM untitle_table_4 limit 1' } }; var expectedQuery = { - "query": [{ - "query": "SELECT * FROM nonexistent_table /* query should fail */", - "status": "failed", - "failed_reason": 'relation "nonexistent_table" does not exist' + query: [{ + query: 'SELECT * FROM nonexistent_table /* query should fail */', + status: 'failed', + failed_reason: 'relation "nonexistent_table" does not exist' }], - "onerror": "SELECT * FROM untitle_table_4 limit 1" + onerror: 'SELECT * FROM untitle_table_4 limit 1' }; - this.batchTestClient.createJob(payload, function(err, jobResult) { + this.batchTestClient.createJob(payload, function (err, jobResult) { if (err) { return done(err); } @@ -260,20 +259,20 @@ describe('Batch API fallback job', function () { var payload = { query: { query: [{ - query: "SELECT * FROM untitle_table_4", + query: 'SELECT * FROM untitle_table_4' }], - onerror: "SELECT * FROM untitle_table_4 limit 1" + onerror: 'SELECT * FROM untitle_table_4 limit 1' } }; var expectedQuery = { - "query": [{ - "query": "SELECT * FROM untitle_table_4", - "status": "done" + query: [{ + query: 'SELECT * FROM untitle_table_4', + status: 'done' }], - "onerror": "SELECT * FROM untitle_table_4 limit 1" + onerror: 'SELECT * FROM untitle_table_4 limit 1' }; - this.batchTestClient.createJob(payload, function(err, jobResult) { + this.batchTestClient.createJob(payload, function (err, jobResult) { if (err) { return done(err); } @@ -294,23 +293,23 @@ describe('Batch API fallback job', function () { var payload = { query: { query: [{ - query: "SELECT * FROM untitle_table_4", - onsuccess: "SELECT * FROM untitle_table_4 limit 1" + query: 'SELECT * FROM untitle_table_4', + onsuccess: 'SELECT * FROM untitle_table_4 limit 1' }], - onsuccess: "SELECT * FROM untitle_table_4 limit 2" + onsuccess: 'SELECT * FROM untitle_table_4 limit 2' } }; var expectedQuery = { - "query": [{ - "query": "SELECT * FROM untitle_table_4", - "onsuccess": "SELECT * FROM untitle_table_4 limit 1", - "status": "done", - "fallback_status": "done" + query: [{ + query: 'SELECT * FROM untitle_table_4', + onsuccess: 'SELECT * FROM untitle_table_4 limit 1', + status: 'done', + fallback_status: 'done' }], - "onsuccess": "SELECT * FROM untitle_table_4 limit 2" + onsuccess: 'SELECT * FROM untitle_table_4 limit 2' }; - this.batchTestClient.createJob(payload, function(err, jobResult) { + this.batchTestClient.createJob(payload, function (err, jobResult) { if (err) { return done(err); } @@ -331,29 +330,29 @@ describe('Batch API fallback job', function () { var payload = { query: { query: [{ - query: "SELECT * FROM untitle_table_4", - onsuccess: "SELECT * FROM untitle_table_4 limit 1" + query: 'SELECT * FROM untitle_table_4', + onsuccess: 'SELECT * FROM untitle_table_4 limit 1' }, { - query: "SELECT * FROM untitle_table_4 limit 2", - onsuccess: "SELECT * FROM untitle_table_4 limit 3" + query: 'SELECT * FROM untitle_table_4 limit 2', + onsuccess: 'SELECT * FROM untitle_table_4 limit 3' }] } }; var expectedQuery = { - "query": [{ - "query": "SELECT * FROM untitle_table_4", - "onsuccess": "SELECT * FROM untitle_table_4 limit 1", - "status": "done", - "fallback_status": "done" + query: [{ + query: 'SELECT * FROM untitle_table_4', + onsuccess: 'SELECT * FROM untitle_table_4 limit 1', + status: 'done', + fallback_status: 'done' }, { - "query": "SELECT * FROM untitle_table_4 limit 2", - "onsuccess": "SELECT * FROM untitle_table_4 limit 3", - "status": "done", - "fallback_status": "done" + query: 'SELECT * FROM untitle_table_4 limit 2', + onsuccess: 'SELECT * FROM untitle_table_4 limit 3', + status: 'done', + fallback_status: 'done' }] }; - this.batchTestClient.createJob(payload, function(err, jobResult) { + this.batchTestClient.createJob(payload, function (err, jobResult) { if (err) { return done(err); } @@ -373,30 +372,30 @@ describe('Batch API fallback job', function () { var payload = { query: { query: [{ - query: "SELECT * FROM nonexistent_table /* should fail */", - onsuccess: "SELECT * FROM untitle_table_4 limit 1" + query: 'SELECT * FROM nonexistent_table /* should fail */', + onsuccess: 'SELECT * FROM untitle_table_4 limit 1' }, { - query: "SELECT * FROM untitle_table_4 limit 2", - onsuccess: "SELECT * FROM untitle_table_4 limit 3" + query: 'SELECT * FROM untitle_table_4 limit 2', + onsuccess: 'SELECT * FROM untitle_table_4 limit 3' }] } }; var expectedQuery = { - "query": [{ - "query": "SELECT * FROM nonexistent_table /* should fail */", - "onsuccess": "SELECT * FROM untitle_table_4 limit 1", - "status": "failed", - "fallback_status": "skipped", - "failed_reason": 'relation "nonexistent_table" does not exist' + query: [{ + query: 'SELECT * FROM nonexistent_table /* should fail */', + onsuccess: 'SELECT * FROM untitle_table_4 limit 1', + status: 'failed', + fallback_status: 'skipped', + failed_reason: 'relation "nonexistent_table" does not exist' }, { - "query": "SELECT * FROM untitle_table_4 limit 2", - "onsuccess": "SELECT * FROM untitle_table_4 limit 3", - "status": "skipped", - "fallback_status": "skipped" + query: 'SELECT * FROM untitle_table_4 limit 2', + onsuccess: 'SELECT * FROM untitle_table_4 limit 3', + status: 'skipped', + fallback_status: 'skipped' }] }; - this.batchTestClient.createJob(payload, function(err, jobResult) { + this.batchTestClient.createJob(payload, function (err, jobResult) { if (err) { return done(err); } @@ -416,30 +415,30 @@ describe('Batch API fallback job', function () { var payload = { query: { query: [{ - query: "SELECT * FROM untitle_table_4 limit 2", - onsuccess: "SELECT * FROM untitle_table_4 limit 1" + query: 'SELECT * FROM untitle_table_4 limit 2', + onsuccess: 'SELECT * FROM untitle_table_4 limit 1' }, { - query: "SELECT * FROM nonexistent_table /* should fail */", - onsuccess: "SELECT * FROM untitle_table_4 limit 3" + query: 'SELECT * FROM nonexistent_table /* should fail */', + onsuccess: 'SELECT * FROM untitle_table_4 limit 3' }] } }; var expectedQuery = { - "query": [{ - "query": "SELECT * FROM untitle_table_4 limit 2", - "onsuccess": "SELECT * FROM untitle_table_4 limit 1", - "status": "done", - "fallback_status": "done" + query: [{ + query: 'SELECT * FROM untitle_table_4 limit 2', + onsuccess: 'SELECT * FROM untitle_table_4 limit 1', + status: 'done', + fallback_status: 'done' }, { - "query": "SELECT * FROM nonexistent_table /* should fail */", - "onsuccess": "SELECT * FROM untitle_table_4 limit 3", - "status": "failed", - "fallback_status": "skipped", - "failed_reason": 'relation "nonexistent_table" does not exist' + query: 'SELECT * FROM nonexistent_table /* should fail */', + onsuccess: 'SELECT * FROM untitle_table_4 limit 3', + status: 'failed', + fallback_status: 'skipped', + failed_reason: 'relation "nonexistent_table" does not exist' }] }; - this.batchTestClient.createJob(payload, function(err, jobResult) { + this.batchTestClient.createJob(payload, function (err, jobResult) { if (err) { return done(err); } @@ -459,11 +458,11 @@ describe('Batch API fallback job', function () { var payload = { query: { query: [{ - query: "SELECT * FROM untitle_table_4 limit 1", - onerror: "SELECT * FROM untitle_table_4 limit 2" + query: 'SELECT * FROM untitle_table_4 limit 1', + onerror: 'SELECT * FROM untitle_table_4 limit 2' }, { - query: "SELECT * FROM untitle_table_4 limit 3", - onerror: "SELECT * FROM untitle_table_4 limit 4" + query: 'SELECT * FROM untitle_table_4 limit 3', + onerror: 'SELECT * FROM untitle_table_4 limit 4' }] } }; @@ -481,7 +480,7 @@ describe('Batch API fallback job', function () { }] }; - this.batchTestClient.createJob(payload, function(err, jobResult) { + this.batchTestClient.createJob(payload, function (err, jobResult) { if (err) { return done(err); } @@ -501,8 +500,8 @@ describe('Batch API fallback job', function () { var payload = { query: { query: [{ - query: "SELECT * FROM untitle_table_4 limit 1, /* should fail */", - onsuccess: "SELECT * FROM untitle_table_4 limit 2" + query: 'SELECT * FROM untitle_table_4 limit 1, /* should fail */', + onsuccess: 'SELECT * FROM untitle_table_4 limit 2' }] } }; @@ -516,7 +515,7 @@ describe('Batch API fallback job', function () { }] }; - this.batchTestClient.createJob(payload, function(err, jobResult) { + this.batchTestClient.createJob(payload, function (err, jobResult) { if (err) { return done(err); } @@ -536,9 +535,9 @@ describe('Batch API fallback job', function () { var payload = { query: { query: [{ - query: "SELECT * FROM untitle_table_4 limit 1, /* should fail */", + query: 'SELECT * FROM untitle_table_4 limit 1, /* should fail */' }], - onsuccess: "SELECT * FROM untitle_table_4 limit 2" + onsuccess: 'SELECT * FROM untitle_table_4 limit 2' } }; var expectedQuery = { @@ -550,7 +549,7 @@ describe('Batch API fallback job', function () { onsuccess: 'SELECT * FROM untitle_table_4 limit 2' }; - this.batchTestClient.createJob(payload, function(err, jobResult) { + this.batchTestClient.createJob(payload, function (err, jobResult) { if (err) { return done(err); } @@ -570,29 +569,29 @@ describe('Batch API fallback job', function () { var payload = { query: { query: [{ - query: "SELECT * FROM untitle_table_4 limit 1", - onsuccess: "SELECT * FROM nonexistent_table /* should fail */" + query: 'SELECT * FROM untitle_table_4 limit 1', + onsuccess: 'SELECT * FROM nonexistent_table /* should fail */' }, { - query: "SELECT * FROM untitle_table_4 limit 2", - onsuccess: "SELECT * FROM untitle_table_4 limit 3" + query: 'SELECT * FROM untitle_table_4 limit 2', + onsuccess: 'SELECT * FROM untitle_table_4 limit 3' }] } }; var expectedQuery = { - "query": [{ - "query": "SELECT * FROM untitle_table_4 limit 1", - "onsuccess": "SELECT * FROM nonexistent_table /* should fail */", - "status": "done", - "fallback_status": "failed", - "failed_reason": 'relation "nonexistent_table" does not exist' + query: [{ + query: 'SELECT * FROM untitle_table_4 limit 1', + onsuccess: 'SELECT * FROM nonexistent_table /* should fail */', + status: 'done', + fallback_status: 'failed', + failed_reason: 'relation "nonexistent_table" does not exist' }, { - "query": "SELECT * FROM untitle_table_4 limit 2", - "onsuccess": "SELECT * FROM untitle_table_4 limit 3", - "status": "done", - "fallback_status": "done" + query: 'SELECT * FROM untitle_table_4 limit 2', + onsuccess: 'SELECT * FROM untitle_table_4 limit 3', + status: 'done', + fallback_status: 'done' }] }; - this.batchTestClient.createJob(payload, function(err, jobResult) { + this.batchTestClient.createJob(payload, function (err, jobResult) { if (err) { return done(err); } @@ -612,30 +611,30 @@ describe('Batch API fallback job', function () { var payload = { query: { query: [{ - query: "SELECT * FROM untitle_table_4 limit 1", - onsuccess: "SELECT * FROM untitle_table_4 limit 2" + query: 'SELECT * FROM untitle_table_4 limit 1', + onsuccess: 'SELECT * FROM untitle_table_4 limit 2' }, { - query: "SELECT * FROM untitle_table_4 limit 3", - onsuccess: "SELECT * FROM nonexistent_table /* should fail */" + query: 'SELECT * FROM untitle_table_4 limit 3', + onsuccess: 'SELECT * FROM nonexistent_table /* should fail */' }] } }; var expectedQuery = { - "query": [{ - "query": "SELECT * FROM untitle_table_4 limit 1", - "onsuccess": "SELECT * FROM untitle_table_4 limit 2", - "status": "done", - "fallback_status": "done" + query: [{ + query: 'SELECT * FROM untitle_table_4 limit 1', + onsuccess: 'SELECT * FROM untitle_table_4 limit 2', + status: 'done', + fallback_status: 'done' }, { - "query": "SELECT * FROM untitle_table_4 limit 3", - "onsuccess": "SELECT * FROM nonexistent_table /* should fail */", - "status": "done", - "fallback_status": "failed", - "failed_reason": 'relation "nonexistent_table" does not exist' + query: 'SELECT * FROM untitle_table_4 limit 3', + onsuccess: 'SELECT * FROM nonexistent_table /* should fail */', + status: 'done', + fallback_status: 'failed', + failed_reason: 'relation "nonexistent_table" does not exist' }] }; - this.batchTestClient.createJob(payload, function(err, jobResult) { + this.batchTestClient.createJob(payload, function (err, jobResult) { if (err) { return done(err); } @@ -655,31 +654,31 @@ describe('Batch API fallback job', function () { var payload = { query: { query: [{ - query: "SELECT * FROM untitle_table_4 limit 1", - onsuccess: "SELECT * FROM untitle_table_4 limit 2" + query: 'SELECT * FROM untitle_table_4 limit 1', + onsuccess: 'SELECT * FROM untitle_table_4 limit 2' }, { - query: "SELECT * FROM untitle_table_4 limit 3", - onsuccess: "SELECT * FROM untitle_table_4 limit 4" + query: 'SELECT * FROM untitle_table_4 limit 3', + onsuccess: 'SELECT * FROM untitle_table_4 limit 4' }], - onsuccess: "SELECT * FROM untitle_table_4 limit 5" + onsuccess: 'SELECT * FROM untitle_table_4 limit 5' } }; var expectedQuery = { - "query": [{ - "query": "SELECT * FROM untitle_table_4 limit 1", - "onsuccess": "SELECT * FROM untitle_table_4 limit 2", - "status": "done", - "fallback_status": "done" + query: [{ + query: 'SELECT * FROM untitle_table_4 limit 1', + onsuccess: 'SELECT * FROM untitle_table_4 limit 2', + status: 'done', + fallback_status: 'done' }, { - "query": "SELECT * FROM untitle_table_4 limit 3", - "onsuccess": "SELECT * FROM untitle_table_4 limit 4", - "status": "done", - "fallback_status": "done" + query: 'SELECT * FROM untitle_table_4 limit 3', + onsuccess: 'SELECT * FROM untitle_table_4 limit 4', + status: 'done', + fallback_status: 'done' }], - onsuccess: "SELECT * FROM untitle_table_4 limit 5" + onsuccess: 'SELECT * FROM untitle_table_4 limit 5' }; - this.batchTestClient.createJob(payload, function(err, jobResult) { + this.batchTestClient.createJob(payload, function (err, jobResult) { if (err) { return done(err); } @@ -701,32 +700,32 @@ describe('Batch API fallback job', function () { var payload = { query: { query: [{ - query: "SELECT * FROM untitle_table_4 limit 1", - onsuccess: "SELECT * FROM untitle_table_4 limit 2" + query: 'SELECT * FROM untitle_table_4 limit 1', + onsuccess: 'SELECT * FROM untitle_table_4 limit 2' }, { - query: "SELECT * FROM untitle_table_4 limit 3", - onsuccess: "SELECT * FROM nonexistent_table /* should fail */" + query: 'SELECT * FROM untitle_table_4 limit 3', + onsuccess: 'SELECT * FROM nonexistent_table /* should fail */' }], - onsuccess: "SELECT * FROM untitle_table_4 limit 5" + onsuccess: 'SELECT * FROM untitle_table_4 limit 5' } }; var expectedQuery = { - "query": [{ - "query": "SELECT * FROM untitle_table_4 limit 1", - "onsuccess": "SELECT * FROM untitle_table_4 limit 2", - "status": "done", - "fallback_status": "done" + query: [{ + query: 'SELECT * FROM untitle_table_4 limit 1', + onsuccess: 'SELECT * FROM untitle_table_4 limit 2', + status: 'done', + fallback_status: 'done' }, { - "query": "SELECT * FROM untitle_table_4 limit 3", - "onsuccess": "SELECT * FROM nonexistent_table /* should fail */", - "status": "done", - "fallback_status": "failed", - "failed_reason": 'relation "nonexistent_table" does not exist' + query: 'SELECT * FROM untitle_table_4 limit 3', + onsuccess: 'SELECT * FROM nonexistent_table /* should fail */', + status: 'done', + fallback_status: 'failed', + failed_reason: 'relation "nonexistent_table" does not exist' }], - "onsuccess": "SELECT * FROM untitle_table_4 limit 5" + onsuccess: 'SELECT * FROM untitle_table_4 limit 5' }; - this.batchTestClient.createJob(payload, function(err, jobResult) { + this.batchTestClient.createJob(payload, function (err, jobResult) { if (err) { return done(err); } @@ -745,15 +744,15 @@ describe('Batch API fallback job', function () { it('should fail first "onerror" and job "onerror" and skip the other ones', function (done) { var payload = { - "query": { - "query": [{ - "query": "SELECT * FROM atm_madrid limit 1, should fail", - "onerror": "SELECT * FROM atm_madrid limit 2" + query: { + query: [{ + query: 'SELECT * FROM atm_madrid limit 1, should fail', + onerror: 'SELECT * FROM atm_madrid limit 2' }, { - "query": "SELECT * FROM atm_madrid limit 3", - "onerror": "SELECT * FROM atm_madrid limit 4" + query: 'SELECT * FROM atm_madrid limit 3', + onerror: 'SELECT * FROM atm_madrid limit 4' }], - "onerror": "SELECT * FROM atm_madrid limit 5" + onerror: 'SELECT * FROM atm_madrid limit 5' } }; var expectedQuery = { @@ -772,7 +771,7 @@ describe('Batch API fallback job', function () { onerror: 'SELECT * FROM atm_madrid limit 5' }; - this.batchTestClient.createJob(payload, function(err, jobResult) { + this.batchTestClient.createJob(payload, function (err, jobResult) { if (err) { return done(err); } @@ -791,38 +790,38 @@ describe('Batch API fallback job', function () { it('should run first "onerror" and job "onerror" and skip the other ones', function (done) { var payload = { - "query": { - "query": [{ - "query": "SELECT * FROM untitle_table_4 limit 1, should fail", - "onerror": "SELECT * FROM untitle_table_4 limit 2" + query: { + query: [{ + query: 'SELECT * FROM untitle_table_4 limit 1, should fail', + onerror: 'SELECT * FROM untitle_table_4 limit 2' }, { - "query": "SELECT * FROM untitle_table_4 limit 3", - "onerror": "SELECT * FROM untitle_table_4 limit 4" + query: 'SELECT * FROM untitle_table_4 limit 3', + onerror: 'SELECT * FROM untitle_table_4 limit 4' }], - "onerror": "SELECT * FROM untitle_table_4 limit 5" + onerror: 'SELECT * FROM untitle_table_4 limit 5' } }; var expectedQuery = { - "query": [ - { - "query": "SELECT * FROM untitle_table_4 limit 1, should fail", - "onerror": "SELECT * FROM untitle_table_4 limit 2", - "status": "failed", - "fallback_status": "done", - "failed_reason": "LIMIT #,# syntax is not supported" - }, - { - "query": "SELECT * FROM untitle_table_4 limit 3", - "onerror": "SELECT * FROM untitle_table_4 limit 4", - "status": "skipped", - "fallback_status": "skipped" - } + query: [ + { + query: 'SELECT * FROM untitle_table_4 limit 1, should fail', + onerror: 'SELECT * FROM untitle_table_4 limit 2', + status: 'failed', + fallback_status: 'done', + failed_reason: 'LIMIT #,# syntax is not supported' + }, + { + query: 'SELECT * FROM untitle_table_4 limit 3', + onerror: 'SELECT * FROM untitle_table_4 limit 4', + status: 'skipped', + fallback_status: 'skipped' + } ], - "onerror": "SELECT * FROM untitle_table_4 limit 5" + onerror: 'SELECT * FROM untitle_table_4 limit 5' }; - this.batchTestClient.createJob(payload, function(err, jobResult) { + this.batchTestClient.createJob(payload, function (err, jobResult) { if (err) { return done(err); } @@ -844,23 +843,23 @@ describe('Batch API fallback job', function () { var payload = { query: { query: [{ - query: "SELECT pg_sleep(3)", - onsuccess: "SELECT pg_sleep(0)" + query: 'SELECT pg_sleep(3)', + onsuccess: 'SELECT pg_sleep(0)' }], - onsuccess: "SELECT pg_sleep(0)" + onsuccess: 'SELECT pg_sleep(0)' } }; var expectedQuery = { - "query": [{ - "query": "SELECT pg_sleep(3)", - "onsuccess": "SELECT pg_sleep(0)", - "status": "cancelled", - "fallback_status": "skipped" + query: [{ + query: 'SELECT pg_sleep(3)', + onsuccess: 'SELECT pg_sleep(0)', + status: 'cancelled', + fallback_status: 'skipped' }], - "onsuccess": "SELECT pg_sleep(0)" + onsuccess: 'SELECT pg_sleep(0)' }; - this.batchTestClient.createJob(payload, function(err, jobResult) { + this.batchTestClient.createJob(payload, function (err, jobResult) { if (err) { return done(err); } @@ -890,23 +889,23 @@ describe('Batch API fallback job', function () { var payload = { query: { query: [{ - query: "SELECT pg_sleep(0)", - onsuccess: "SELECT pg_sleep(3)" + query: 'SELECT pg_sleep(0)', + onsuccess: 'SELECT pg_sleep(3)' }], - onsuccess: "SELECT pg_sleep(0)" + onsuccess: 'SELECT pg_sleep(0)' } }; var expectedQuery = { - "query": [{ - "query": "SELECT pg_sleep(0)", - "onsuccess": "SELECT pg_sleep(3)", - "status": "done", - "fallback_status": "cancelled" + query: [{ + query: 'SELECT pg_sleep(0)', + onsuccess: 'SELECT pg_sleep(3)', + status: 'done', + fallback_status: 'cancelled' }], - "onsuccess": "SELECT pg_sleep(0)" + onsuccess: 'SELECT pg_sleep(0)' }; - this.batchTestClient.createJob(payload, function(err, jobResult) { + this.batchTestClient.createJob(payload, function (err, jobResult) { if (err) { return done(err); } diff --git a/test/acceptance/batch/job-query-limit-test.js b/test/acceptance/batch/job-query-limit-test.js index ec02c9e0..e50d73a0 100644 --- a/test/acceptance/batch/job-query-limit-test.js +++ b/test/acceptance/batch/job-query-limit-test.js @@ -21,10 +21,10 @@ var server = require('../../../lib/server')(); var assert = require('../../support/assert'); var querystring = require('qs'); -function payload(query) { - return JSON.stringify({query: query}); +function payload (query) { + return JSON.stringify({ query: query }); } -function payloadSize(query) { +function payloadSize (query) { return payload(query).length; } @@ -32,9 +32,8 @@ var minPayloadSize = payloadSize(''); var queryMaxSize = new Array(JobController.MAX_LIMIT_QUERY_SIZE_IN_BYTES - minPayloadSize + 1).join('a'); var queryTooLong = queryMaxSize.concat('a'); -describe('job query limit', function() { - - function expectedErrorMessage(query) { +describe('job query limit', function () { + function expectedErrorMessage (query) { return JobController.getMaxSizeErrorMessage(payload(query)); } @@ -42,11 +41,10 @@ describe('job query limit', function() { redisUtils.clean('batch:*', done); }); - it('POST /api/v2/sql/job with a invalid query size should respond with 400 query too long', function (done){ - + it('POST /api/v2/sql/job with a invalid query size should respond with 400 query too long', function (done) { assert.response(server, { url: '/api/v2/sql/job?api_key=1234', - headers: { 'host': 'vizzuality.cartodb.com', 'Content-Type': 'application/x-www-form-urlencoded' }, + headers: { host: 'vizzuality.cartodb.com', 'Content-Type': 'application/x-www-form-urlencoded' }, method: 'POST', data: querystring.stringify({ query: queryTooLong @@ -60,11 +58,10 @@ describe('job query limit', function() { }); }); - it('POST /api/v2/sql/job with a valid query size should respond with 201 created', function (done){ - + it('POST /api/v2/sql/job with a valid query size should respond with 201 created', function (done) { assert.response(server, { url: '/api/v2/sql/job?api_key=1234', - headers: { 'host': 'vizzuality.cartodb.com', 'Content-Type': 'application/x-www-form-urlencoded' }, + headers: { host: 'vizzuality.cartodb.com', 'Content-Type': 'application/x-www-form-urlencoded' }, method: 'POST', data: querystring.stringify({ query: queryMaxSize @@ -78,11 +75,11 @@ describe('job query limit', function() { }); }); - it('POST /api/v2/sql/job with a invalid query size should consider multiple queries', function (done){ + it('POST /api/v2/sql/job with a invalid query size should consider multiple queries', function (done) { var queries = [queryTooLong, 'select 1']; assert.response(server, { url: '/api/v2/sql/job?api_key=1234', - headers: { 'host': 'vizzuality.cartodb.com', 'Content-Type': 'application/x-www-form-urlencoded' }, + headers: { host: 'vizzuality.cartodb.com', 'Content-Type': 'application/x-www-form-urlencoded' }, method: 'POST', data: querystring.stringify({ query: queries @@ -96,19 +93,19 @@ describe('job query limit', function() { }); }); - it('POST /api/v2/sql/job with a invalid query size should consider fallback queries/callbacks', function (done){ + it('POST /api/v2/sql/job with a invalid query size should consider fallback queries/callbacks', function (done) { var fallbackQueries = { query: [{ query: queryTooLong, - onsuccess: "SELECT * FROM untitle_table_4 limit 1" + onsuccess: 'SELECT * FROM untitle_table_4 limit 1' }, { - query: "SELECT * FROM untitle_table_4 limit 2", - onsuccess: "SELECT * FROM untitle_table_4 limit 3" + query: 'SELECT * FROM untitle_table_4 limit 2', + onsuccess: 'SELECT * FROM untitle_table_4 limit 3' }] }; assert.response(server, { url: '/api/v2/sql/job?api_key=1234', - headers: { 'host': 'vizzuality.cartodb.com', 'Content-Type': 'application/x-www-form-urlencoded' }, + headers: { host: 'vizzuality.cartodb.com', 'Content-Type': 'application/x-www-form-urlencoded' }, method: 'POST', data: querystring.stringify({ query: fallbackQueries @@ -121,5 +118,4 @@ describe('job query limit', function() { done(); }); }); - }); diff --git a/test/acceptance/batch/job-query-order-test.js b/test/acceptance/batch/job-query-order-test.js index bf280e86..ba45074f 100644 --- a/test/acceptance/batch/job-query-order-test.js +++ b/test/acceptance/batch/job-query-order-test.js @@ -6,9 +6,8 @@ var assert = require('../../support/assert'); var BatchTestClient = require('../../support/batch-test-client'); var JobStatus = require('../../../lib/batch/job-status'); -describe('job query order', function() { - - before(function() { +describe('job query order', function () { + before(function () { this.batchTestClient = new BatchTestClient(); }); @@ -16,21 +15,21 @@ describe('job query order', function() { return this.batchTestClient.drain(done); }); - function createJob(queries) { + function createJob (queries) { return { query: queries }; } it('should run job queries in order (single consumer)', function (done) { - var jobRequest1 = createJob(["select 1", "select 2"]); - var jobRequest2 = createJob(["select 3"]); + var jobRequest1 = createJob(['select 1', 'select 2']); + var jobRequest2 = createJob(['select 3']); - this.batchTestClient.createJob(jobRequest1, function(err, jobResult1) { + this.batchTestClient.createJob(jobRequest1, function (err, jobResult1) { if (err) { return done(err); } - this.batchTestClient.createJob(jobRequest2, function(err, jobResult2) { + this.batchTestClient.createJob(jobRequest2, function (err, jobResult2) { if (err) { return done(err); } @@ -39,7 +38,7 @@ describe('job query order', function() { if (err) { return done(err); } - jobResult2.getStatus(function(err, job2) { + jobResult2.getStatus(function (err, job2) { if (err) { return done(err); } @@ -55,5 +54,4 @@ describe('job query order', function() { }); }.bind(this)); }); - }); diff --git a/test/acceptance/batch/job-query-timeout-test.js b/test/acceptance/batch/job-query-timeout-test.js index a449ff5f..d1a246a4 100644 --- a/test/acceptance/batch/job-query-timeout-test.js +++ b/test/acceptance/batch/job-query-timeout-test.js @@ -6,9 +6,8 @@ var assert = require('../../support/assert'); var BatchTestClient = require('../../support/batch-test-client'); var JobStatus = require('../../../lib/batch/job-status'); -describe('job query timeout', function() { - - before(function() { +describe('job query timeout', function () { + before(function () { this.batchQueryTimeout = global.settings.batch_query_timeout; this.batchTestClient = new BatchTestClient(); }); @@ -18,7 +17,7 @@ describe('job query timeout', function() { return this.batchTestClient.drain(done); }); - function createTimeoutQuery(query, timeout) { + function createTimeoutQuery (query, timeout) { return { query: { query: [ @@ -32,12 +31,12 @@ describe('job query timeout', function() { } it('should run query with higher user timeout', function (done) { - var jobRequest = createTimeoutQuery("select pg_sleep(0.1)", 200); - this.batchTestClient.createJob(jobRequest, function(err, jobResult) { + var jobRequest = createTimeoutQuery('select pg_sleep(0.1)', 200); + this.batchTestClient.createJob(jobRequest, function (err, jobResult) { if (err) { return done(err); } - jobResult.getStatus(function(err, job) { + jobResult.getStatus(function (err, job) { if (err) { return done(err); } @@ -48,12 +47,12 @@ describe('job query timeout', function() { }); it('should fail to run query with lower user timeout', function (done) { - var jobRequest = createTimeoutQuery("select pg_sleep(0.1)", 50); - this.batchTestClient.createJob(jobRequest, function(err, jobResult) { + var jobRequest = createTimeoutQuery('select pg_sleep(0.1)', 50); + this.batchTestClient.createJob(jobRequest, function (err, jobResult) { if (err) { return done(err); } - jobResult.getStatus(function(err, job) { + jobResult.getStatus(function (err, job) { if (err) { return done(err); } @@ -65,12 +64,12 @@ describe('job query timeout', function() { it('should fail to run query with user timeout if it is higher than config', function (done) { global.settings.batch_query_timeout = 100; - var jobRequest = createTimeoutQuery("select pg_sleep(1)", 2000); - this.batchTestClient.createJob(jobRequest, function(err, jobResult) { + var jobRequest = createTimeoutQuery('select pg_sleep(1)', 2000); + this.batchTestClient.createJob(jobRequest, function (err, jobResult) { if (err) { return done(err); } - jobResult.getStatus(function(err, job) { + jobResult.getStatus(function (err, job) { if (err) { return done(err); } @@ -82,12 +81,12 @@ describe('job query timeout', function() { it('should fail to run query with user timeout if set to 0 (ignored timeout)', function (done) { global.settings.batch_query_timeout = 100; - var jobRequest = createTimeoutQuery("select pg_sleep(1)", 0); - this.batchTestClient.createJob(jobRequest, function(err, jobResult) { + var jobRequest = createTimeoutQuery('select pg_sleep(1)', 0); + this.batchTestClient.createJob(jobRequest, function (err, jobResult) { if (err) { return done(err); } - jobResult.getStatus(function(err, job) { + jobResult.getStatus(function (err, job) { if (err) { return done(err); } diff --git a/test/acceptance/batch/job-test.js b/test/acceptance/batch/job-test.js index fa1d5472..32d3e171 100644 --- a/test/acceptance/batch/job-test.js +++ b/test/acceptance/batch/job-test.js @@ -21,94 +21,94 @@ var assert = require('../../support/assert'); var redisUtils = require('../../support/redis-utils'); var querystring = require('querystring'); -describe('job module', function() { +describe('job module', function () { var job = {}; after(function (done) { redisUtils.clean('batch:*', done); }); - it('POST /api/v2/sql/job should respond with 200 and the created job', function (done){ + it('POST /api/v2/sql/job should respond with 200 and the created job', function (done) { assert.response(server, { url: '/api/v2/sql/job?api_key=1234', - headers: { 'host': 'vizzuality.cartodb.com', 'Content-Type': 'application/x-www-form-urlencoded' }, + headers: { host: 'vizzuality.cartodb.com', 'Content-Type': 'application/x-www-form-urlencoded' }, method: 'POST', data: querystring.stringify({ - query: "SELECT * FROM untitle_table_4" + query: 'SELECT * FROM untitle_table_4' }) }, { status: 201 - }, function(err, res) { + }, function (err, res) { job = JSON.parse(res.body); assert.deepEqual(res.headers['content-type'], 'application/json; charset=utf-8'); assert.ok(job.job_id); - assert.equal(job.query, "SELECT * FROM untitle_table_4"); - assert.equal(job.user, "vizzuality"); + assert.equal(job.query, 'SELECT * FROM untitle_table_4'); + assert.equal(job.user, 'vizzuality'); done(); }); }); it('POST /api/v2/sql/job without query should respond with 400 and the corresponding message of error', - function (done){ - assert.response(server, { - url: '/api/v2/sql/job?api_key=1234', - headers: { 'host': 'vizzuality.cartodb.com', 'Content-Type': 'application/x-www-form-urlencoded' }, - method: 'POST', - data: querystring.stringify({}) - }, { - status: 400 - }, function(err, res) { - var error = JSON.parse(res.body); - assert.deepEqual(error, { error: [ 'You must indicate a valid SQL' ] }); - done(); + function (done) { + assert.response(server, { + url: '/api/v2/sql/job?api_key=1234', + headers: { host: 'vizzuality.cartodb.com', 'Content-Type': 'application/x-www-form-urlencoded' }, + method: 'POST', + data: querystring.stringify({}) + }, { + status: 400 + }, function (err, res) { + var error = JSON.parse(res.body); + assert.deepEqual(error, { error: ['You must indicate a valid SQL'] }); + done(); + }); }); - }); - it('POST /api/v2/sql/job with bad query param should respond with 400 and message of error', function (done){ + it('POST /api/v2/sql/job with bad query param should respond with 400 and message of error', function (done) { assert.response(server, { url: '/api/v2/sql/job?api_key=1234', - headers: { 'host': 'vizzuality.cartodb.com', 'Content-Type': 'application/x-www-form-urlencoded' }, + headers: { host: 'vizzuality.cartodb.com', 'Content-Type': 'application/x-www-form-urlencoded' }, method: 'POST', data: querystring.stringify({ - q: "SELECT * FROM untitle_table_4" + q: 'SELECT * FROM untitle_table_4' }) }, { status: 400 - }, function(err, res) { + }, function (err, res) { var error = JSON.parse(res.body); - assert.deepEqual(error, { error: [ 'You must indicate a valid SQL' ] }); + assert.deepEqual(error, { error: ['You must indicate a valid SQL'] }); done(); }); }); - it('POST /api/v2/sql/job with wrong api key should respond with 401 permission denied', function (done){ + it('POST /api/v2/sql/job with wrong api key should respond with 401 permission denied', function (done) { assert.response(server, { url: '/api/v2/sql/job?api_key=wrong', - headers: { 'host': 'vizzuality.cartodb.com', 'Content-Type': 'application/x-www-form-urlencoded' }, + headers: { host: 'vizzuality.cartodb.com', 'Content-Type': 'application/x-www-form-urlencoded' }, method: 'POST', data: querystring.stringify({ - query: "SELECT * FROM untitle_table_4" + query: 'SELECT * FROM untitle_table_4' }) }, { status: 401 - }, function(err, res) { + }, function (err, res) { var error = JSON.parse(res.body); - assert.deepEqual(error, { error: [ 'Unauthorized' ] }); + assert.deepEqual(error, { error: ['Unauthorized'] }); done(); }); }); - it('POST /api/v2/sql/job with wrong host header should respond with 404 not found', function (done){ + it('POST /api/v2/sql/job with wrong host header should respond with 404 not found', function (done) { assert.response(server, { url: '/api/v2/sql/job?api_key=wrong', - headers: { 'host': 'wrong-host.cartodb.com', 'Content-Type': 'application/x-www-form-urlencoded' }, + headers: { host: 'wrong-host.cartodb.com', 'Content-Type': 'application/x-www-form-urlencoded' }, method: 'POST', data: querystring.stringify({ - query: "SELECT * FROM untitle_table_4" + query: 'SELECT * FROM untitle_table_4' }) }, { status: 404 - }, function(err, res) { + }, function (err, res) { var error = JSON.parse(res.body); assert.deepEqual(error, { error: [ @@ -120,94 +120,94 @@ describe('job module', function() { }); }); - it('GET /api/v2/sql/job/:job_id should respond with 200 and the requested job', function (done){ + it('GET /api/v2/sql/job/:job_id should respond with 200 and the requested job', function (done) { assert.response(server, { url: '/api/v2/sql/job/' + job.job_id + '?api_key=1234', - headers: { 'host': 'vizzuality.cartodb.com', 'Content-Type': 'application/x-www-form-urlencoded' }, + headers: { host: 'vizzuality.cartodb.com', 'Content-Type': 'application/x-www-form-urlencoded' }, method: 'GET' }, { status: 200 - }, function(err, res) { + }, function (err, res) { var jobGot = JSON.parse(res.body); assert.deepEqual(res.headers['content-type'], 'application/json; charset=utf-8'); - assert.equal(jobGot.query, "SELECT * FROM untitle_table_4"); - assert.equal(jobGot.user, "vizzuality"); + assert.equal(jobGot.query, 'SELECT * FROM untitle_table_4'); + assert.equal(jobGot.user, 'vizzuality'); done(); }); }); - it('GET /api/v2/sql/job/:job_id with wrong api key should respond with 401 permission denied', function (done){ + it('GET /api/v2/sql/job/:job_id with wrong api key should respond with 401 permission denied', function (done) { assert.response(server, { url: '/api/v2/sql/job/' + job.job_id + '?api_key=wrong', - headers: { 'host': 'vizzuality.cartodb.com', 'Content-Type': 'application/x-www-form-urlencoded' }, + headers: { host: 'vizzuality.cartodb.com', 'Content-Type': 'application/x-www-form-urlencoded' }, method: 'GET' }, { status: 401 - }, function(err, res) { + }, function (err, res) { var error = JSON.parse(res.body); assert.deepEqual(error, { error: ['Unauthorized'] }); done(); }); }); - it('GET /api/v2/sql/job/:jobId with wrong jobId header respond with 400 and an error', function (done){ + it('GET /api/v2/sql/job/:jobId with wrong jobId header respond with 400 and an error', function (done) { assert.response(server, { url: '/api/v2/sql/job/irrelevantJob?api_key=1234', - headers: { 'host': 'vizzuality.cartodb.com', 'Content-Type': 'application/x-www-form-urlencoded' }, + headers: { host: 'vizzuality.cartodb.com', 'Content-Type': 'application/x-www-form-urlencoded' }, method: 'GET' }, { status: 400 - }, function(err, res) { + }, function (err, res) { var error = JSON.parse(res.body); - assert.deepEqual(error , { + assert.deepEqual(error, { error: ['Job with id irrelevantJob not found'] }); done(); }); }); - it('DELETE /api/v2/sql/job/:job_id should respond with 200 and the requested job', function (done){ + it('DELETE /api/v2/sql/job/:job_id should respond with 200 and the requested job', function (done) { assert.response(server, { url: '/api/v2/sql/job/' + job.job_id + '?api_key=1234', - headers: { 'host': 'vizzuality.cartodb.com', 'Content-Type': 'application/x-www-form-urlencoded' }, + headers: { host: 'vizzuality.cartodb.com', 'Content-Type': 'application/x-www-form-urlencoded' }, method: 'DELETE' }, { status: 200 - }, function(err, res) { + }, function (err, res) { var jobCancelled = JSON.parse(res.body); assert.deepEqual(res.headers['content-type'], 'application/json; charset=utf-8'); assert.equal(jobCancelled.job_id, job.job_id); - assert.equal(jobCancelled.query, "SELECT * FROM untitle_table_4"); - assert.equal(jobCancelled.user, "vizzuality"); - assert.equal(jobCancelled.status, "cancelled"); + assert.equal(jobCancelled.query, 'SELECT * FROM untitle_table_4'); + assert.equal(jobCancelled.user, 'vizzuality'); + assert.equal(jobCancelled.status, 'cancelled'); done(); }); }); - it('DELETE /api/v2/sql/job/:job_id with wrong api key should respond with 401 permission denied', function (done){ + it('DELETE /api/v2/sql/job/:job_id with wrong api key should respond with 401 permission denied', function (done) { assert.response(server, { url: '/api/v2/sql/job/' + job.job_id + '?api_key=wrong', - headers: { 'host': 'vizzuality.cartodb.com', 'Content-Type': 'application/x-www-form-urlencoded' }, + headers: { host: 'vizzuality.cartodb.com', 'Content-Type': 'application/x-www-form-urlencoded' }, method: 'DELETE' }, { status: 401 - }, function(err, res) { + }, function (err, res) { var error = JSON.parse(res.body); assert.deepEqual(error, { error: ['Unauthorized'] }); done(); }); }); - it('DELETE /api/v2/sql/job/ with wrong host header respond with 404 not found', function (done){ + it('DELETE /api/v2/sql/job/ with wrong host header respond with 404 not found', function (done) { assert.response(server, { url: '/api/v2/sql/job/' + job.job_id + '?api_key=1234', - headers: { 'host': 'wrong-host.cartodb.com', 'Content-Type': 'application/x-www-form-urlencoded' }, + headers: { host: 'wrong-host.cartodb.com', 'Content-Type': 'application/x-www-form-urlencoded' }, method: 'DELETE' }, { status: 404 - }, function(err, res) { + }, function (err, res) { var error = JSON.parse(res.body); - assert.deepEqual(error , { + assert.deepEqual(error, { error: [ 'Sorry, we can\'t find CARTO user \'wrong-host\'. ' + 'Please check that you have entered the correct domain.' diff --git a/test/acceptance/batch/job-timing-test.js b/test/acceptance/batch/job-timing-test.js index 1391fb14..48ac5389 100644 --- a/test/acceptance/batch/job-timing-test.js +++ b/test/acceptance/batch/job-timing-test.js @@ -6,11 +6,11 @@ var BatchTestClient = require('../../support/batch-test-client'); var JobStatus = require('../../../lib/batch/job-status'); describe('Batch API query timing', function () { - before(function() { + before(function () { this.batchTestClient = new BatchTestClient(); }); - after(function(done) { + after(function (done) { this.batchTestClient.drain(done); }); @@ -32,22 +32,22 @@ describe('Batch API query timing', function () { }; var payload = { - "query": { - "query": [ + query: { + query: [ { - "query": "SELECT * FROM untitle_table_4 limit 1", - "onerror": "SELECT * FROM untitle_table_4 limit 2" + query: 'SELECT * FROM untitle_table_4 limit 1', + onerror: 'SELECT * FROM untitle_table_4 limit 2' }, { - "query": "SELECT * FROM untitle_table_4 limit 3", - "onerror": "SELECT * FROM untitle_table_4 limit 4" + query: 'SELECT * FROM untitle_table_4 limit 3', + onerror: 'SELECT * FROM untitle_table_4 limit 4' } ], - "onerror": "SELECT * FROM untitle_table_4 limit 5" + onerror: 'SELECT * FROM untitle_table_4 limit 5' } }; - this.batchTestClient.createJob(payload, function(err, jobResult) { + this.batchTestClient.createJob(payload, function (err, jobResult) { if (err) { return done(err); } @@ -63,7 +63,6 @@ describe('Batch API query timing', function () { }); }); - it('should report start and end time for each query also for failing queries' + 'and expose started_at and ended_at for all queries with fallback mechanism (failed)', function (done) { var expectedQuery = { @@ -82,22 +81,22 @@ describe('Batch API query timing', function () { }; var payload = { - "query": { - "query": [ + query: { + query: [ { - "query": "SELECT * FROM untitle_table_4 limit 1", - "onerror": "SELECT * FROM untitle_table_4 limit 2" + query: 'SELECT * FROM untitle_table_4 limit 1', + onerror: 'SELECT * FROM untitle_table_4 limit 2' }, { - "query": "SELECT * FROM untitle_table_4 limit 3 failed", - "onerror": "SELECT * FROM untitle_table_4 limit 4" + query: 'SELECT * FROM untitle_table_4 limit 3 failed', + onerror: 'SELECT * FROM untitle_table_4 limit 4' } ], - "onerror": "SELECT * FROM untitle_table_4 limit 5" + onerror: 'SELECT * FROM untitle_table_4 limit 5' } }; - this.batchTestClient.createJob(payload, function(err, jobResult) { + this.batchTestClient.createJob(payload, function (err, jobResult) { if (err) { return done(err); } diff --git a/test/acceptance/batch/leader-job-query-order-test.js b/test/acceptance/batch/leader-job-query-order-test.js index df441c7e..1f6e224b 100644 --- a/test/acceptance/batch/leader-job-query-order-test.js +++ b/test/acceptance/batch/leader-job-query-order-test.js @@ -7,9 +7,8 @@ var TestClient = require('../../support/test-client'); var BatchTestClient = require('../../support/batch-test-client'); var JobStatus = require('../../../lib/batch/job-status'); -describe('multiple batch clients job query order', function() { - - before(function(done) { +describe('multiple batch clients job query order', function () { + before(function (done) { this.batchTestClient1 = new BatchTestClient({ name: 'consumerA' }); this.batchTestClient2 = new BatchTestClient({ name: 'consumerB' }); @@ -21,7 +20,7 @@ describe('multiple batch clients job query order', function() { }); after(function (done) { - this.batchTestClient1.drain(function(err) { + this.batchTestClient1.drain(function (err) { if (err) { return done(err); } @@ -30,7 +29,7 @@ describe('multiple batch clients job query order', function() { }.bind(this)); }); - function createJob(queries) { + function createJob (queries) { return { query: queries }; @@ -38,21 +37,21 @@ describe('multiple batch clients job query order', function() { it('should run job queries in order (multiple consumers)', function (done) { var jobRequest1 = createJob([ - "insert into ordered_inserts values(1)", - "select pg_sleep(0.25)", - "insert into ordered_inserts values(2)" + 'insert into ordered_inserts values(1)', + 'select pg_sleep(0.25)', + 'insert into ordered_inserts values(2)' ]); var jobRequest2 = createJob([ - "insert into ordered_inserts values(3)" + 'insert into ordered_inserts values(3)' ]); var self = this; - this.batchTestClient1.createJob(jobRequest1, function(err, jobResult1) { + this.batchTestClient1.createJob(jobRequest1, function (err, jobResult1) { if (err) { return done(err); } - this.batchTestClient2.createJob(jobRequest2, function(err, jobResult2) { + this.batchTestClient2.createJob(jobRequest2, function (err, jobResult2) { if (err) { return done(err); } @@ -61,14 +60,14 @@ describe('multiple batch clients job query order', function() { if (err) { return done(err); } - jobResult2.getStatus(function(err, job2) { + jobResult2.getStatus(function (err, job2) { if (err) { return done(err); } assert.equal(job1.status, JobStatus.DONE); assert.equal(job2.status, JobStatus.DONE); - self.testClient.getResult('select * from ordered_inserts', function(err, rows) { + self.testClient.getResult('select * from ordered_inserts', function (err, rows) { assert.ok(!err); assert.deepEqual(rows, [{ status: 1 }, { status: 2 }, { status: 3 }]); @@ -78,11 +77,9 @@ describe('multiple batch clients job query order', function() { ); done(); }); - }); }); }); }.bind(this)); }); - }); diff --git a/test/acceptance/batch/leader-multiple-users-query-order-test.js b/test/acceptance/batch/leader-multiple-users-query-order-test.js index a7b08108..14e9ccb4 100644 --- a/test/acceptance/batch/leader-multiple-users-query-order-test.js +++ b/test/acceptance/batch/leader-multiple-users-query-order-test.js @@ -7,9 +7,8 @@ var TestClient = require('../../support/test-client'); var BatchTestClient = require('../../support/batch-test-client'); var JobStatus = require('../../../lib/batch/job-status'); -describe('multiple batch clients and users, job query order', function() { - - before(function(done) { +describe('multiple batch clients and users, job query order', function () { + before(function (done) { this.batchTestClientA = new BatchTestClient({ name: 'consumerA' }); this.batchTestClientB = new BatchTestClient({ name: 'consumerB' }); @@ -26,7 +25,7 @@ describe('multiple batch clients and users, job query order', function() { }); after(function (done) { - this.batchTestClientA.drain(function(err) { + this.batchTestClientA.drain(function (err) { if (err) { return done(err); } @@ -35,7 +34,7 @@ describe('multiple batch clients and users, job query order', function() { }.bind(this)); }); - function createJob(queries) { + function createJob (queries) { return { query: queries }; @@ -43,33 +42,33 @@ describe('multiple batch clients and users, job query order', function() { it('should run job queries in order (multiple consumers)', function (done) { var jobRequestA1 = createJob([ - "insert into ordered_inserts_a values(1)", - "select pg_sleep(0.25)", - "insert into ordered_inserts_a values(2)" + 'insert into ordered_inserts_a values(1)', + 'select pg_sleep(0.25)', + 'insert into ordered_inserts_a values(2)' ]); var jobRequestA2 = createJob([ - "insert into ordered_inserts_a values(3)" + 'insert into ordered_inserts_a values(3)' ]); var jobRequestB1 = createJob([ - "insert into ordered_inserts_bbbbb values(1)" + 'insert into ordered_inserts_bbbbb values(1)' ]); var self = this; - this.batchTestClientA.createJob(jobRequestA1, function(err, jobResultA1) { + this.batchTestClientA.createJob(jobRequestA1, function (err, jobResultA1) { if (err) { return done(err); } var override = { host: 'cartodb250user.cartodb.com' }; - self.batchTestClientB.createJob(jobRequestB1, override, function(err, jobResultB1) { + self.batchTestClientB.createJob(jobRequestB1, override, function (err, jobResultB1) { if (err) { return done(err); } // we don't care about the producer - self.batchTestClientB.createJob(jobRequestA2, function(err, jobResultA2) { + self.batchTestClientB.createJob(jobRequestA2, function (err, jobResultA2) { if (err) { return done(err); } @@ -78,11 +77,11 @@ describe('multiple batch clients and users, job query order', function() { if (err) { return done(err); } - jobResultA2.getStatus(function(err, jobA2) { + jobResultA2.getStatus(function (err, jobA2) { if (err) { return done(err); } - jobResultB1.getStatus(function(err, jobB1) { + jobResultB1.getStatus(function (err, jobB1) { assert.equal(jobA1.status, JobStatus.DONE); assert.equal(jobA2.status, JobStatus.DONE); assert.equal(jobB1.status, JobStatus.DONE); @@ -100,7 +99,7 @@ describe('multiple batch clients and users, job query order', function() { function statusMapper (status) { return { status: status }; } - self.testClient.getResult('select * from ordered_inserts_a', function(err, rows) { + self.testClient.getResult('select * from ordered_inserts_a', function (err, rows) { assert.ok(!err); // cartodb250user and vizzuality test users share database @@ -108,7 +107,7 @@ describe('multiple batch clients and users, job query order', function() { assert.deepEqual(rows, expectedRows); var query = 'select * from ordered_inserts_bbbbb'; - self.testClient.getResult(query, override, function(err, rows) { + self.testClient.getResult(query, override, function (err, rows) { assert.ok(!err); var expectedRows = [1].map(statusMapper); @@ -118,12 +117,10 @@ describe('multiple batch clients and users, job query order', function() { }); }); }); - }); }); }); }); }); }); - }); diff --git a/test/acceptance/batch/queued-jobs-limit-test.js b/test/acceptance/batch/queued-jobs-limit-test.js index f9d52df2..4cc65e97 100644 --- a/test/acceptance/batch/queued-jobs-limit-test.js +++ b/test/acceptance/batch/queued-jobs-limit-test.js @@ -6,9 +6,8 @@ var assert = require('../../support/assert'); var redisUtils = require('../../support/redis-utils'); var TestClient = require('../../support/test-client'); -describe('max queued jobs', function() { - - before(function(done) { +describe('max queued jobs', function () { + before(function (done) { this.batch_max_queued_jobs = global.settings.batch_max_queued_jobs; global.settings.batch_max_queued_jobs = 1; this.server = require('../../../lib/server')(); @@ -24,7 +23,7 @@ describe('max queued jobs', function() { redisUtils.clean('batch:*', done); }); - function createJob(server, status, callback) { + function createJob (server, status, callback) { assert.response( server, { @@ -35,13 +34,13 @@ describe('max queued jobs', function() { }, method: 'POST', data: JSON.stringify({ - query: "insert into max_queued_jobs_inserts values (1)" + query: 'insert into max_queued_jobs_inserts values (1)' }) }, { status: status }, - function(err, res) { + function (err, res) { if (err) { return callback(err); } @@ -53,16 +52,15 @@ describe('max queued jobs', function() { it('POST /api/v2/sql/job should respond with 200 and the created job', function (done) { var self = this; - createJob(this.server, 201, function(err) { + createJob(this.server, 201, function (err) { assert.ok(!err); - createJob(self.server, 400, function(err, res) { + createJob(self.server, 400, function (err, res) { assert.ok(!err); - assert.equal(res.error[0], "Failed to create job. Max number of jobs (" + - global.settings.batch_max_queued_jobs + ") queued reached"); + assert.equal(res.error[0], 'Failed to create job. Max number of jobs (' + + global.settings.batch_max_queued_jobs + ') queued reached'); done(); }); }); }); - }); diff --git a/test/acceptance/batch/scheduler-basic-test.js b/test/acceptance/batch/scheduler-basic-test.js index 46c35d7a..62c1e03b 100644 --- a/test/acceptance/batch/scheduler-basic-test.js +++ b/test/acceptance/batch/scheduler-basic-test.js @@ -7,9 +7,8 @@ var TestClient = require('../../support/test-client'); var BatchTestClient = require('../../support/batch-test-client'); var JobStatus = require('../../../lib/batch/job-status'); -describe('basic scheduling', function() { - - before(function(done) { +describe('basic scheduling', function () { + before(function (done) { this.batchTestClientA = new BatchTestClient({ name: 'consumerA' }); this.batchTestClientB = new BatchTestClient({ name: 'consumerB' }); @@ -24,7 +23,7 @@ describe('basic scheduling', function() { }); after(function (done) { - this.batchTestClientA.drain(function(err) { + this.batchTestClientA.drain(function (err) { if (err) { return done(err); } @@ -33,7 +32,7 @@ describe('basic scheduling', function() { }.bind(this)); }); - function createJob(queries) { + function createJob (queries) { return { query: queries }; @@ -41,23 +40,23 @@ describe('basic scheduling', function() { it('should run job queries in order (multiple consumers)', function (done) { var jobRequestA1 = createJob([ - "insert into ordered_inserts_a values(1)", - "select pg_sleep(0.25)", - "insert into ordered_inserts_a values(2)" + 'insert into ordered_inserts_a values(1)', + 'select pg_sleep(0.25)', + 'insert into ordered_inserts_a values(2)' ]); var jobRequestA2 = createJob([ - "insert into ordered_inserts_a values(3)" + 'insert into ordered_inserts_a values(3)' ]); var self = this; - this.batchTestClientA.createJob(jobRequestA1, function(err, jobResultA1) { + this.batchTestClientA.createJob(jobRequestA1, function (err, jobResultA1) { if (err) { return done(err); } // we don't care about the producer - self.batchTestClientB.createJob(jobRequestA2, function(err, jobResultA2) { + self.batchTestClientB.createJob(jobRequestA2, function (err, jobResultA2) { if (err) { return done(err); } @@ -67,7 +66,7 @@ describe('basic scheduling', function() { return done(err); } - jobResultA2.getStatus(function(err, jobA2) { + jobResultA2.getStatus(function (err, jobA2) { if (err) { return done(err); } @@ -75,14 +74,14 @@ describe('basic scheduling', function() { assert.equal(jobA2.status, JobStatus.DONE); assert.ok( - new Date(jobA1.updated_at).getTime() < new Date(jobA2.updated_at).getTime(), - 'A1 (' + jobA1.updated_at + ') ' + + new Date(jobA1.updated_at).getTime() < new Date(jobA2.updated_at).getTime(), + 'A1 (' + jobA1.updated_at + ') ' + 'should finish before A2 (' + jobA2.updated_at + ')' ); function statusMapper (status) { return { status: status }; } - self.testClient.getResult('select * from ordered_inserts_a', function(err, rows) { + self.testClient.getResult('select * from ordered_inserts_a', function (err, rows) { assert.ok(!err); // cartodb250user and vizzuality test users share database diff --git a/test/acceptance/batch/use-cases-test.js b/test/acceptance/batch/use-cases-test.js index f1fdf3be..19e47546 100644 --- a/test/acceptance/batch/use-cases-test.js +++ b/test/acceptance/batch/use-cases-test.js @@ -7,20 +7,20 @@ var JobStatus = require('../../../lib/batch/job-status'); var BatchTestClient = require('../../support/batch-test-client'); describe('Use cases', function () { - before(function() { + before(function () { this.batchTestClient = new BatchTestClient(); }); - after(function(done) { + after(function (done) { this.batchTestClient.drain(done); }); it('cancel a done job should return an error', function (done) { var payload = { - query: "SELECT * FROM untitle_table_4" + query: 'SELECT * FROM untitle_table_4' }; - this.batchTestClient.createJob(payload, function(err, jobResult) { + this.batchTestClient.createJob(payload, function (err, jobResult) { if (err) { return done(err); } @@ -33,7 +33,7 @@ describe('Use cases', function () { assert.equal(job.status, JobStatus.DONE); jobResult.tryCancel(function (err, body) { - assert.equal(body.error[0], "Cannot set status from done to cancelled"); + assert.equal(body.error[0], 'Cannot set status from done to cancelled'); done(); }); }); @@ -42,10 +42,10 @@ describe('Use cases', function () { it('cancel a running job', function (done) { var payload = { - query: "SELECT * FROM untitle_table_4; select pg_sleep(3)" + query: 'SELECT * FROM untitle_table_4; select pg_sleep(3)' }; - this.batchTestClient.createJob(payload, function(err, jobResult) { + this.batchTestClient.createJob(payload, function (err, jobResult) { if (err) { return done(err); } @@ -65,7 +65,7 @@ describe('Use cases', function () { assert.equal(job.status, JobStatus.CANCELLED); jobResult.tryCancel(function (err, body) { - assert.equal(body.error[0], "Cannot set status from cancelled to cancelled"); + assert.equal(body.error[0], 'Cannot set status from cancelled to cancelled'); done(); }); }); @@ -76,19 +76,19 @@ describe('Use cases', function () { it('cancel a pending job', function (done) { var self = this; var payload1 = { - query: "SELECT * FROM untitle_table_4; select pg_sleep(3)" + query: 'SELECT * FROM untitle_table_4; select pg_sleep(3)' }; - this.batchTestClient.createJob(payload1, function(err, jobResult1) { + this.batchTestClient.createJob(payload1, function (err, jobResult1) { if (err) { return done(err); } var payload2 = { - query: "SELECT * FROM untitle_table_4" + query: 'SELECT * FROM untitle_table_4' }; - self.batchTestClient.createJob(payload2, function(err, jobResult2) { + self.batchTestClient.createJob(payload2, function (err, jobResult2) { if (err) { return done(err); } @@ -115,7 +115,6 @@ describe('Use cases', function () { assert.equal(job.status, JobStatus.CANCELLED); done(); }); - }); }); }); @@ -127,7 +126,7 @@ describe('Use cases', function () { query: "SELECT name FROM untitle_table_4 WHERE name = 'Hawai'; select pg_sleep(3)" }; - this.batchTestClient.createJob(payload, function(err, jobResult) { + this.batchTestClient.createJob(payload, function (err, jobResult) { if (err) { return done(err); } @@ -154,13 +153,13 @@ describe('Use cases', function () { it('cancel a running multiquery job', function (done) { var payload = { query: [ - "select pg_sleep(1)", - "select pg_sleep(1)", - "select pg_sleep(1)" + 'select pg_sleep(1)', + 'select pg_sleep(1)', + 'select pg_sleep(1)' ] }; - this.batchTestClient.createJob(payload, function(err, jobResult) { + this.batchTestClient.createJob(payload, function (err, jobResult) { if (err) { return done(err); } @@ -180,7 +179,7 @@ describe('Use cases', function () { assert.equal(job.status, JobStatus.CANCELLED); jobResult.tryCancel(function (err, body) { - assert.equal(body.error[0], "Cannot set status from cancelled to cancelled"); + assert.equal(body.error[0], 'Cannot set status from cancelled to cancelled'); done(); }); }); diff --git a/test/acceptance/cache-headers-test.js b/test/acceptance/cache-headers-test.js index 44bb476d..21d4d379 100644 --- a/test/acceptance/cache-headers-test.js +++ b/test/acceptance/cache-headers-test.js @@ -41,7 +41,7 @@ describe('cache headers', function () { method: 'GET' }, {}, - function(err, res) { + function (err, res) { assert.equal(res.headers['cache-control'], `no-cache,max-age=${noTtl},must-revalidate,public`); assert.response(server, { @@ -54,10 +54,10 @@ describe('cache headers', function () { }, method: 'GET' }, {}, - function(err, res) { + function (err, res) { const cacheControl = res.headers['cache-control']; - const [ , maxAge ] = cacheControl.split(','); - const [ , value ] = maxAge.split('='); + const [, maxAge] = cacheControl.split(','); + const [, value] = maxAge.split('='); assert.ok(Number(value) <= fallbackTtl); @@ -71,7 +71,7 @@ describe('cache headers', function () { }, method: 'GET' }, {}, - function(err, res) { + function (err, res) { assert.equal(res.headers['cache-control'], `no-cache,max-age=${ttl},must-revalidate,public`); assert.response(server, { @@ -84,7 +84,7 @@ describe('cache headers', function () { }, method: 'GET' }, {}, - function(err, res) { + function (err, res) { assert.equal(res.headers['cache-control'], `no-cache,max-age=${ttl},must-revalidate,public`); done(); }); @@ -100,7 +100,7 @@ describe('cache headers', function () { assert.response(server, { url: `/api/v1/sql?${qs.encode({ api_key: '1234', - q: `select 1` + q: 'select 1' })}`, headers: { host: 'vizzuality.cartodb.com' diff --git a/test/acceptance/copy-abort-test.js b/test/acceptance/copy-abort-test.js index 5bdcde46..64d4f49a 100644 --- a/test/acceptance/copy-abort-test.js +++ b/test/acceptance/copy-abort-test.js @@ -21,9 +21,9 @@ const copyQuery = `COPY ( const createTableQuery = `CREATE TABLE copy_to_test AS (SELECT '2018-06-15 14:49:05.126415+00'::timestamp AS updated_at)`; -const dropTableQuery = `DROP TABLE copy_to_test`; +const dropTableQuery = 'DROP TABLE copy_to_test'; -const countQuery = `SELECT count(1) as count FROM copy_to_test`; +const countQuery = 'SELECT count(1) as count FROM copy_to_test'; function countInsertedRows (host, port, callback) { setTimeout(function () { @@ -48,16 +48,15 @@ function countInsertedRows (host, port, callback) { } describe('Cancel "copy to" commands', function () { - before(function() { + before(function () { this.db_pool_size = global.settings.db_pool_size; global.settings.db_pool_size = 1; }); - after(function() { + after(function () { global.settings.db_pool_size = this.db_pool_size; }); - beforeEach(function (done) { this.listener = server.listen(0, '127.0.0.1'); @@ -76,7 +75,7 @@ describe('Cancel "copy to" commands', function () { beforeEach(function (done) { const { host, port } = this; - const createTable = querystring.stringify({ q: createTableQuery, api_key: 1234}); + const createTable = querystring.stringify({ q: createTableQuery, api_key: 1234 }); const createTableOptions = { url: `http://${host}:${port}/api/v1/sql?${createTable}`, @@ -177,7 +176,6 @@ describe('Cancel "copy to" commands', function () { }); }); - it('destroy on data', function (done) { const { host, port } = this; diff --git a/test/acceptance/copy-endpoints-test.js b/test/acceptance/copy-endpoints-test.js index 2afbbd9d..f487511d 100644 --- a/test/acceptance/copy-endpoints-test.js +++ b/test/acceptance/copy-endpoints-test.js @@ -20,18 +20,17 @@ if (global.settings.statsd) { const statsClient = StatsClient.getInstance(global.settings.statsd); const server = require('../../lib/server')(statsClient); - // Give it enough time to connect and issue the query // but not too much so as to disconnect in the middle of the query. const CLIENT_DISCONNECT_TIMEOUT = 100; const assertCanReuseCanceledConnection = function (done) { assert.response(server, { url: '/api/v1/sql?' + querystring.stringify({ - q: 'SELECT count(*) FROM copy_endpoints_test', + q: 'SELECT count(*) FROM copy_endpoints_test' }), headers: { host: 'vizzuality.cartodb.com' }, method: 'GET' - }, {}, function(err, res) { + }, {}, function (err, res) { assert.ifError(err); assert.ok(res.statusCode === 200); const result = JSON.parse(res.body); @@ -40,19 +39,18 @@ const assertCanReuseCanceledConnection = function (done) { }); }; - -describe('copy-endpoints', function() { - before(function() { +describe('copy-endpoints', function () { + before(function () { this.client = new Client({ user: 'postgres', host: 'localhost', database: 'cartodb_test_user_1_db', - port: 5432, + port: 5432 }); this.client.connect(); }); - after(function() { + after(function () { this.client.end(); }); @@ -62,16 +60,16 @@ describe('copy-endpoints', function() { }); }); - describe('general', function() { - it('should work with copyfrom endpoint', function(done){ + describe('general', function () { + it('should work with copyfrom endpoint', function (done) { assert.response(server, { - url: "/api/v1/sql/copyfrom?" + querystring.stringify({ + url: '/api/v1/sql/copyfrom?' + querystring.stringify({ q: "COPY copy_endpoints_test (id, name) FROM STDIN WITH (FORMAT CSV, DELIMITER ',', HEADER true)" }), data: fs.createReadStream(__dirname + '/../support/csv/copy_test_table.csv'), - headers: {host: 'vizzuality.cartodb.com'}, + headers: { host: 'vizzuality.cartodb.com' }, method: 'POST' - },{}, function(err, res) { + }, {}, function (err, res) { assert.ifError(err); const response = JSON.parse(res.body); assert.equal(!!response.time, true); @@ -80,82 +78,82 @@ describe('copy-endpoints', function() { }); }); - it('should fail with copyfrom endpoint and unexisting table', function(done){ + it('should fail with copyfrom endpoint and unexisting table', function (done) { assert.response(server, { - url: "/api/v1/sql/copyfrom?" + querystring.stringify({ + url: '/api/v1/sql/copyfrom?' + querystring.stringify({ q: "COPY unexisting_table (id, name) FROM STDIN WITH (FORMAT CSV, DELIMITER ',', HEADER true)" }), data: fs.createReadStream(__dirname + '/../support/csv/copy_test_table.csv'), - headers: {host: 'vizzuality.cartodb.com'}, + headers: { host: 'vizzuality.cartodb.com' }, method: 'POST' - },{}, function(err, res) { + }, {}, function (err, res) { assert.ifError(err); assert.deepEqual( JSON.parse(res.body), { - error:['relation \"unexisting_table\" does not exist'] + error: ['relation \"unexisting_table\" does not exist'] } ); done(); }); }); - it('should fail with copyfrom endpoint and without csv', function(done){ + it('should fail with copyfrom endpoint and without csv', function (done) { assert.response(server, { - url: "/api/v1/sql/copyfrom?" + querystring.stringify({ + url: '/api/v1/sql/copyfrom?' + querystring.stringify({ q: "COPY copy_endpoints_test (id, name) FROM STDIN WITH (FORMAT CSV, DELIMITER ',', HEADER true)" }), - headers: {host: 'vizzuality.cartodb.com'}, + headers: { host: 'vizzuality.cartodb.com' }, method: 'POST' - },{}, function(err, res) { + }, {}, function (err, res) { assert.ifError(err); assert.deepEqual( JSON.parse(res.body), { - error:['No rows copied'] + error: ['No rows copied'] } ); done(); }); }); - it('should fail with copyfrom endpoint and without q', function(done){ + it('should fail with copyfrom endpoint and without q', function (done) { assert.response(server, { - url: "/api/v1/sql/copyfrom", + url: '/api/v1/sql/copyfrom', data: fs.createReadStream(__dirname + '/../support/csv/copy_test_table.csv'), - headers: {host: 'vizzuality.cartodb.com'}, + headers: { host: 'vizzuality.cartodb.com' }, method: 'POST' - },{}, function(err, res) { + }, {}, function (err, res) { assert.ifError(err); assert.deepEqual( JSON.parse(res.body), { - error:["SQL is missing"] + error: ['SQL is missing'] } ); done(); }); }); - it('should work with copyto endpoint', function(done){ + it('should work with copyto endpoint', function (done) { assert.response(server, { - url: "/api/v1/sql/copyfrom?" + querystring.stringify({ + url: '/api/v1/sql/copyfrom?' + querystring.stringify({ q: "COPY copy_endpoints_test (id, name) FROM STDIN WITH (FORMAT CSV, DELIMITER ',', HEADER true)" }), data: fs.createReadStream(__dirname + '/../support/csv/copy_test_table.csv'), - headers: {host: 'vizzuality.cartodb.com'}, + headers: { host: 'vizzuality.cartodb.com' }, method: 'POST' - },{}, function(err) { + }, {}, function (err) { assert.ifError(err); assert.response(server, { - url: "/api/v1/sql/copyto?" + querystring.stringify({ + url: '/api/v1/sql/copyto?' + querystring.stringify({ q: 'COPY copy_endpoints_test TO STDOUT', filename: '/tmp/output.dmp' }), - headers: {host: 'vizzuality.cartodb.com'}, + headers: { host: 'vizzuality.cartodb.com' }, method: 'GET' - },{}, function(err, res) { + }, {}, function (err, res) { assert.ifError(err); const regex = /11\tPaul\t10\n12\tPeter\t10\n13\tMatthew\t10\n14\t\\N\t10\n15\tJames\t10\n16\t*/g; assert.ok(res.body.match(regex)); @@ -168,9 +166,9 @@ describe('copy-endpoints', function() { }); }); - it('should work with copyto endpoint and POST method', function(done){ + it('should work with copyto endpoint and POST method', function (done) { assert.response(server, { - url: "/api/v1/sql/copyfrom?" + querystring.stringify({ + url: '/api/v1/sql/copyfrom?' + querystring.stringify({ q: "COPY copy_endpoints_test (id, name) FROM STDIN WITH (FORMAT CSV, DELIMITER ',', HEADER true)" }), data: fs.createReadStream(__dirname + '/../support/csv/copy_test_table.csv'), @@ -178,7 +176,7 @@ describe('copy-endpoints', function() { host: 'vizzuality.cartodb.com' }, method: 'POST' - }, {}, function(err) { + }, {}, function (err) { assert.ifError(err); assert.response(server, { @@ -192,7 +190,7 @@ describe('copy-endpoints', function() { 'Content-Type': 'application/x-www-form-urlencoded' }, method: 'POST' - }, {}, function(err, res) { + }, {}, function (err, res) { assert.ifError(err); const regex = /11\tPaul\t10\n12\tPeter\t10\n13\tMatthew\t10\n14\t\\N\t10\n15\tJames\t10\n16\t*/g; assert.ok(res.body.match(regex)); @@ -205,28 +203,28 @@ describe('copy-endpoints', function() { }); }); - it('should fail with copyto endpoint and without sql', function(done){ + it('should fail with copyto endpoint and without sql', function (done) { assert.response(server, { - url: "/api/v1/sql/copyto?" + querystring.stringify({ + url: '/api/v1/sql/copyto?' + querystring.stringify({ filename: '/tmp/output.dmp' }), - headers: {host: 'vizzuality.cartodb.com'}, + headers: { host: 'vizzuality.cartodb.com' }, method: 'GET' - },{}, function(err, res) { + }, {}, function (err, res) { assert.ifError(err); assert.deepEqual( JSON.parse(res.body), { - error:["SQL is missing"] + error: ['SQL is missing'] } ); done(); }); }); - it('should work with copyfrom and gzip', function(done){ + it('should work with copyfrom and gzip', function (done) { assert.response(server, { - url: "/api/v1/sql/copyfrom?" + querystring.stringify({ + url: '/api/v1/sql/copyfrom?' + querystring.stringify({ q: "COPY copy_endpoints_test (id, name) FROM STDIN WITH (FORMAT CSV, DELIMITER ',', HEADER true)" }), data: fs.createReadStream(__dirname + '/../support/csv/copy_test_table.csv.gz'), @@ -235,7 +233,7 @@ describe('copy-endpoints', function() { 'content-encoding': 'gzip' }, method: 'POST' - },{}, function(err, res) { + }, {}, function (err, res) { assert.ifError(err); const response = JSON.parse(res.body); assert.equal(!!response.time, true); @@ -244,9 +242,9 @@ describe('copy-endpoints', function() { }); }); - it('should return an error when gzip headers are not correct', function(done) { + it('should return an error when gzip headers are not correct', function (done) { assert.response(server, { - url: "/api/v1/sql/copyfrom?" + querystring.stringify({ + url: '/api/v1/sql/copyfrom?' + querystring.stringify({ q: "COPY copy_endpoints_test (id, name) FROM STDIN WITH (FORMAT CSV, DELIMITER ',', HEADER true)" }), data: fs.createReadStream(__dirname + '/../support/csv/copy_test_table.csv'), @@ -255,12 +253,12 @@ describe('copy-endpoints', function() { 'content-encoding': 'gzip' }, method: 'POST' - },{}, function(err, res) { + }, {}, function (err, res) { assert.ifError(err); assert.deepEqual( JSON.parse(res.body), { - error:["Error while gunzipping: incorrect header check"] + error: ['Error while gunzipping: incorrect header check'] } ); done(); @@ -268,37 +266,36 @@ describe('copy-endpoints', function() { }); }); - - describe('timeout', function() { - before('set a 1 ms timeout', function() { + describe('timeout', function () { + before('set a 1 ms timeout', function () { this.previous_timeout = global.settings.copy_timeout; global.settings.copy_timeout = 1; }); - after('restore previous timeout', function() { + after('restore previous timeout', function () { global.settings.copy_timeout = this.previous_timeout; }); - it('should fail with copyfrom and timeout', function(done) { + it('should fail with copyfrom and timeout', function (done) { assert.response(server, { - url: "/api/v1/sql/copyfrom?" + querystring.stringify({ + url: '/api/v1/sql/copyfrom?' + querystring.stringify({ q: `COPY copy_endpoints_test (id, name) FROM STDIN WITH (FORMAT CSV, DELIMITER ',', HEADER true)` }), data: fs.createReadStream(__dirname + '/../support/csv/copy_test_table.csv'), - headers: {host: 'vizzuality.cartodb.com'}, + headers: { host: 'vizzuality.cartodb.com' }, method: 'POST' }, { status: 429, headers: { 'Content-Type': 'application/json; charset=utf-8' } }, - function(err, res) { + function (err, res) { assert.ifError(err); assert.deepEqual(JSON.parse(res.body), { error: [ 'You are over platform\'s limits: SQL query timeout error.' + - ' Refactor your query before running again or contact CARTO support for more details.', + ' Refactor your query before running again or contact CARTO support for more details.' ], context: 'limit', detail: 'datasource' @@ -307,21 +304,21 @@ describe('copy-endpoints', function() { }); }); - it('should fail with copyto and timeout', function(done){ + it('should fail with copyto and timeout', function (done) { assert.response(server, { - url: "/api/v1/sql/copyto?" + querystring.stringify({ + url: '/api/v1/sql/copyto?' + querystring.stringify({ q: 'COPY populated_places_simple_reduced TO STDOUT', filename: '/tmp/output.dmp' }), - headers: {host: 'vizzuality.cartodb.com'}, + headers: { host: 'vizzuality.cartodb.com' }, method: 'GET' - },{}, function(err, res) { + }, {}, function (err, res) { assert.ifError(err); const error = { error: ['You are over platform\'s limits: SQL query timeout error.' + - ' Refactor your query before running again or contact CARTO support for more details.',], - context:"limit", - detail:"datasource" + ' Refactor your query before running again or contact CARTO support for more details.'], + context: 'limit', + detail: 'datasource' }; const expectedError = res.body.substring(res.body.length - JSON.stringify(error).length); assert.deepEqual(JSON.parse(expectedError), error); @@ -330,29 +327,28 @@ describe('copy-endpoints', function() { }); }); - - describe('db connections', function() { - before(function() { + describe('db connections', function () { + before(function () { this.db_pool_size = global.settings.db_pool_size; global.settings.db_pool_size = 1; }); - after(function() { + after(function () { global.settings.db_pool_size = this.db_pool_size; }); - it('copyfrom', function(done) { - function doCopyFrom() { + it('copyfrom', function (done) { + function doCopyFrom () { return new Promise(resolve => { assert.response(server, { - url: "/api/v1/sql/copyfrom?" + querystring.stringify({ + url: '/api/v1/sql/copyfrom?' + querystring.stringify({ q: `COPY copy_endpoints_test (id, name) FROM STDIN WITH (FORMAT CSV, DELIMITER ',', HEADER true)` }), data: fs.createReadStream(__dirname + '/../support/csv/copy_test_table.csv'), - headers: {host: 'vizzuality.cartodb.com'}, + headers: { host: 'vizzuality.cartodb.com' }, method: 'POST' - },{}, function(err, res) { + }, {}, function (err, res) { assert.ifError(err); const response = JSON.parse(res.body); assert.ok(response.time); @@ -361,22 +357,22 @@ describe('copy-endpoints', function() { }); } - Promise.all([doCopyFrom(), doCopyFrom(), doCopyFrom()]).then(function() { + Promise.all([doCopyFrom(), doCopyFrom(), doCopyFrom()]).then(function () { done(); }); }); - it('copyto', function(done) { - function doCopyTo() { + it('copyto', function (done) { + function doCopyTo () { return new Promise(resolve => { assert.response(server, { - url: "/api/v1/sql/copyto?" + querystring.stringify({ - q: `COPY (SELECT * FROM generate_series(1, 10000)) TO STDOUT`, + url: '/api/v1/sql/copyto?' + querystring.stringify({ + q: 'COPY (SELECT * FROM generate_series(1, 10000)) TO STDOUT', filename: '/tmp/output.dmp' }), - headers: {host: 'vizzuality.cartodb.com'}, + headers: { host: 'vizzuality.cartodb.com' }, method: 'GET' - },{}, function(err, res) { + }, {}, function (err, res) { assert.ifError(err); assert.ok(res.body); resolve(); @@ -385,55 +381,54 @@ describe('copy-endpoints', function() { } assert.response(server, { - url: "/api/v1/sql/copyfrom?" + querystring.stringify({ + url: '/api/v1/sql/copyfrom?' + querystring.stringify({ q: "COPY copy_endpoints_test (id, name) FROM STDIN WITH (FORMAT CSV, DELIMITER ',', HEADER true)" }), data: fs.createReadStream(__dirname + '/../support/csv/copy_test_table.csv'), - headers: {host: 'vizzuality.cartodb.com'}, + headers: { host: 'vizzuality.cartodb.com' }, method: 'POST' - },{}, function(err) { + }, {}, function (err) { assert.ifError(err); - Promise.all([doCopyTo(), doCopyTo(), doCopyTo()]).then(function() { + Promise.all([doCopyTo(), doCopyTo(), doCopyTo()]).then(function () { done(); }); }); }); }); - describe('client disconnection', function() { - before(function() { + describe('client disconnection', function () { + before(function () { this.db_pool_size = global.settings.db_pool_size; global.settings.db_pool_size = 1; }); - after(function() { + after(function () { global.settings.db_pool_size = this.db_pool_size; }); const assertCanReuseConnection = function (done) { assert.response(server, { url: '/api/v1/sql?' + querystring.stringify({ - q: 'SELECT 1', + q: 'SELECT 1' }), headers: { host: 'vizzuality.cartodb.com' }, method: 'GET' - }, {}, function(err, res) { + }, {}, function (err, res) { assert.ifError(err); assert.ok(res.statusCode === 200); done(); }); }; - it('COPY TO returns the connection to the pool if the client disconnects', function(done) { + it('COPY TO returns the connection to the pool if the client disconnects', function (done) { const listener = server.listen(0, '127.0.0.1'); listener.on('error', done); listener.on('listening', function onServerListening () { - const { address, port } = listener.address(); const query = querystring.stringify({ - q: `COPY (SELECT * FROM generate_series(1, 1000)) TO STDOUT` + q: 'COPY (SELECT * FROM generate_series(1, 1000)) TO STDOUT' }); const options = { @@ -453,15 +448,14 @@ describe('copy-endpoints', function() { }); }); - it('COPY FROM returns the connection to the pool if the client disconnects', function(done) { + it('COPY FROM returns the connection to the pool if the client disconnects', function (done) { const listener = server.listen(0, '127.0.0.1'); listener.on('error', done); listener.on('listening', function onServerListening () { - const { address, port } = listener.address(); const query = querystring.stringify({ - q: `COPY copy_endpoints_test (id, name) FROM STDIN WITH (FORMAT CSV, DELIMITER ',', HEADER true)` + q: 'COPY copy_endpoints_test (id, name) FROM STDIN WITH (FORMAT CSV, DELIMITER \',\', HEADER true)' }); const options = { @@ -479,39 +473,38 @@ describe('copy-endpoints', function() { }, CLIENT_DISCONNECT_TIMEOUT); }); }); - }); - describe('COPY timeouts: they can take longer than statement_timeout', function() { - before('set a very small statement_timeout for regular queries', function(done) { + describe('COPY timeouts: they can take longer than statement_timeout', function () { + before('set a very small statement_timeout for regular queries', function (done) { assert.response(server, { url: '/api/v1/sql?q=set statement_timeout = 10', - headers: {host: 'vizzuality.cartodb.com'}, + headers: { host: 'vizzuality.cartodb.com' }, method: 'GET' }, done); }); - after('restore normal statement_timeout for regular queries', function(done) { + after('restore normal statement_timeout for regular queries', function (done) { assert.response(server, { url: '/api/v1/sql?q=set statement_timeout = 2000', - headers: {host: 'vizzuality.cartodb.com'}, + headers: { host: 'vizzuality.cartodb.com' }, method: 'GET' }, done); }); - it('COPY FROM can take longer than regular statement_timeout', function(done) { + it('COPY FROM can take longer than regular statement_timeout', function (done) { assert.response(server, { - url: "/api/v1/sql/copyfrom?" + querystring.stringify({ + url: '/api/v1/sql/copyfrom?' + querystring.stringify({ q: `COPY copy_endpoints_test (id, name) FROM STDIN WITH (FORMAT CSV, DELIMITER ',', HEADER true)` }), data: fs.createReadStream(__dirname + '/../support/csv/copy_test_table.csv'), - headers: {host: 'vizzuality.cartodb.com'}, + headers: { host: 'vizzuality.cartodb.com' }, method: 'POST' }, { status: 200, headers: { 'Content-Type': 'application/json; charset=utf-8' } - }, function(err, res) { + }, function (err, res) { assert.ifError(err); const response = JSON.parse(res.body); assert.strictEqual(response.total_rows, 2016); @@ -519,15 +512,15 @@ describe('copy-endpoints', function() { }); }); - it('COPY TO can take longer than regular statement_timeout', function(done) { + it('COPY TO can take longer than regular statement_timeout', function (done) { assert.response(server, { - url: "/api/v1/sql/copyto?" + querystring.stringify({ + url: '/api/v1/sql/copyto?' + querystring.stringify({ q: 'COPY copy_endpoints_test TO STDOUT', filename: '/tmp/output.dmp' }), - headers: {host: 'vizzuality.cartodb.com'}, + headers: { host: 'vizzuality.cartodb.com' }, method: 'GET' - }, {}, function(err, res) { + }, {}, function (err, res) { assert.ifError(err); assert.ok(res.statusCode === 200); done(); @@ -535,8 +528,8 @@ describe('copy-endpoints', function() { }); }); - describe('dbQuotaMiddleware', function() { - before('Set the remaining quota to 1 byte', function(done) { + describe('dbQuotaMiddleware', function () { + before('Set the remaining quota to 1 byte', function (done) { // See the test/support/sql/quota_mock.sql this.client.query(`CREATE OR REPLACE FUNCTION CDB_UserDataSize(schema_name TEXT) RETURNS bigint AS @@ -551,7 +544,7 @@ describe('copy-endpoints', function() { global.settings.db_pool_size = 1; }); - after('Restore the old quota', function(done) { + after('Restore the old quota', function (done) { // See the test/support/sql/quota_mock.sql this.client.query(`CREATE OR REPLACE FUNCTION CDB_UserDataSize(schema_name TEXT) RETURNS bigint AS @@ -565,35 +558,35 @@ describe('copy-endpoints', function() { global.settings.db_pool_size = this.db_pool_size; }); - it('COPY FROM fails with an error if DB quota is exhausted', function(done) { + it('COPY FROM fails with an error if DB quota is exhausted', function (done) { assert.response(server, { - url: "/api/v1/sql/copyfrom?" + querystring.stringify({ + url: '/api/v1/sql/copyfrom?' + querystring.stringify({ q: `COPY copy_endpoints_test (id, name) FROM STDIN WITH (FORMAT CSV, DELIMITER ',', HEADER true)` }), data: fs.createReadStream(__dirname + '/../support/csv/copy_test_table.csv'), - headers: {host: 'vizzuality.cartodb.com'}, + headers: { host: 'vizzuality.cartodb.com' }, method: 'POST' }, { status: 400, headers: { 'Content-Type': 'application/json; charset=utf-8' } - }, function(err, res) { + }, function (err, res) { const response = JSON.parse(res.body); - assert.deepEqual(response, { error: ["DB Quota exceeded"] }); + assert.deepEqual(response, { error: ['DB Quota exceeded'] }); setTimeout(() => assertCanReuseCanceledConnection(done), CLIENT_DISCONNECT_TIMEOUT); }); }); - it('COPY TO is not affected by remaining DB quota', function(done) { + it('COPY TO is not affected by remaining DB quota', function (done) { assert.response(server, { - url: "/api/v1/sql/copyto?" + querystring.stringify({ + url: '/api/v1/sql/copyto?' + querystring.stringify({ q: 'COPY copy_endpoints_test TO STDOUT', filename: '/tmp/output.dmp' }), - headers: {host: 'vizzuality.cartodb.com'}, + headers: { host: 'vizzuality.cartodb.com' }, method: 'GET' - }, {}, function(err, res) { + }, {}, function (err, res) { assert.ifError(err); assert.ok(res.statusCode === 200); done(); @@ -601,8 +594,8 @@ describe('copy-endpoints', function() { }); }); - describe('COPY FROM max POST size', function() { - before('Set a ridiculously small POST size limit', function() { + describe('COPY FROM max POST size', function () { + before('Set a ridiculously small POST size limit', function () { this.previous_max_post_size = global.settings.copy_from_max_post_size; this.previous_max_post_size_pretty = global.settings.copy_from_max_post_size_pretty; global.settings.copy_from_max_post_size = 10; @@ -610,27 +603,27 @@ describe('copy-endpoints', function() { this.db_pool_size = global.settings.db_pool_size; global.settings.db_pool_size = 1; }); - after('Restore the max POST size limit values', function() { + after('Restore the max POST size limit values', function () { global.settings.copy_from_max_post_size = this.previous_max_post_size; global.settings.copy_from_max_post_size_pretty = this.previous_max_post_size_pretty; global.settings.db_pool_size = this.db_pool_size; }); - it('honors the max POST size limit', function(done) { + it('honors the max POST size limit', function (done) { assert.response(server, { - url: "/api/v1/sql/copyfrom?" + querystring.stringify({ + url: '/api/v1/sql/copyfrom?' + querystring.stringify({ q: `COPY copy_endpoints_test (id, name) FROM STDIN WITH (FORMAT CSV, DELIMITER ',', HEADER true)` }), data: fs.createReadStream(__dirname + '/../support/csv/copy_test_table.csv'), - headers: {host: 'vizzuality.cartodb.com'}, + headers: { host: 'vizzuality.cartodb.com' }, method: 'POST' }, { status: 400, headers: { 'Content-Type': 'application/json; charset=utf-8' } - }, function(err, res) { + }, function (err, res) { const response = JSON.parse(res.body); - assert.deepEqual(response, { error: ["COPY FROM maximum POST size of 10 bytes exceeded"] }); + assert.deepEqual(response, { error: ['COPY FROM maximum POST size of 10 bytes exceeded'] }); setTimeout(() => assertCanReuseCanceledConnection(done), CLIENT_DISCONNECT_TIMEOUT); }); diff --git a/test/acceptance/copy-statements-test.js b/test/acceptance/copy-statements-test.js index 5055c7a7..2ea1154a 100644 --- a/test/acceptance/copy-statements-test.js +++ b/test/acceptance/copy-statements-test.js @@ -6,75 +6,71 @@ var server = require('../../lib/server')(); var assert = require('../support/assert'); var querystring = require('querystring'); - -describe('copy-statements', function() { - +describe('copy-statements', function () { var RESPONSE_OK = { statusCode: 200 }; - before(function(done) { + before(function (done) { assert.response(server, { - url: "/api/v1/sql?" + querystring.stringify({ + url: '/api/v1/sql?' + querystring.stringify({ q: 'CREATE TABLE copy_test_table(a int)', api_key: 1234 }), - headers: {host: 'vizzuality.cartodb.com'}, + headers: { host: 'vizzuality.cartodb.com' }, method: 'GET' }, RESPONSE_OK, done); }); - after(function(done) { + after(function (done) { assert.response(server, { - url: "/api/v1/sql?" + querystring.stringify({ + url: '/api/v1/sql?' + querystring.stringify({ q: 'DROP TABLE IF EXISTS copy_test_table', api_key: 1234 }), - headers: {host: 'vizzuality.cartodb.com'}, + headers: { host: 'vizzuality.cartodb.com' }, method: 'GET' }, RESPONSE_OK, done); }); // Test effects of COPY // See https://github.com/Vizzuality/cartodb-management/issues/1502 - it('COPY TABLE with GET and auth', function(done){ + it('COPY TABLE with GET and auth', function (done) { assert.response(server, { - url: "/api/v1/sql?" + querystring.stringify({ + url: '/api/v1/sql?' + querystring.stringify({ q: 'COPY copy_test_table FROM stdin;', api_key: 1234 }), - headers: {host: 'vizzuality.cartodb.com'}, + headers: { host: 'vizzuality.cartodb.com' }, method: 'GET' - },{}, function(err, res) { + }, {}, function (err, res) { // We expect a problem, actually - assert.equal(res.statusCode, 400, res.statusCode + ': ' + res.body); - assert.deepEqual(res.headers['content-type'], 'application/json; charset=utf-8'); - assert.deepEqual(res.headers['content-disposition'], 'inline'); - assert.deepEqual(JSON.parse(res.body), {"error":["COPY from stdin failed: No source stream defined"]}); - done(); + assert.equal(res.statusCode, 400, res.statusCode + ': ' + res.body); + assert.deepEqual(res.headers['content-type'], 'application/json; charset=utf-8'); + assert.deepEqual(res.headers['content-disposition'], 'inline'); + assert.deepEqual(JSON.parse(res.body), { error: ['COPY from stdin failed: No source stream defined'] }); + done(); }); }); - it('COPY TABLE with GET and auth', function(done){ + it('COPY TABLE with GET and auth', function (done) { assert.response(server, { - url: "/api/v1/sql?" + querystring.stringify({ + url: '/api/v1/sql?' + querystring.stringify({ q: "COPY copy_test_table to '/tmp/x';", api_key: 1234 }), - headers: {host: 'vizzuality.cartodb.com'}, + headers: { host: 'vizzuality.cartodb.com' }, method: 'GET' - },{}, function(err, res) { + }, {}, function (err, res) { // We expect a problem, actually - assert.equal(res.statusCode, 400, res.statusCode + ': ' + res.body); - assert.deepEqual(res.headers['content-type'], 'application/json; charset=utf-8'); - assert.deepEqual(res.headers['content-disposition'], 'inline'); - const error_exp = /must be superuser.* to COPY.* a file/; - const hint_exp = /Anyone can COPY to stdout or from stdin. psql's \\copy command also works for anyone./; - assert.ok(JSON.parse(res.body).error[0].match(error_exp)); - assert.ok(JSON.parse(res.body).hint.match(hint_exp)); - done(); + assert.equal(res.statusCode, 400, res.statusCode + ': ' + res.body); + assert.deepEqual(res.headers['content-type'], 'application/json; charset=utf-8'); + assert.deepEqual(res.headers['content-disposition'], 'inline'); + const error_exp = /must be superuser.* to COPY.* a file/; + const hint_exp = /Anyone can COPY to stdout or from stdin. psql's \\copy command also works for anyone./; + assert.ok(JSON.parse(res.body).error[0].match(error_exp)); + assert.ok(JSON.parse(res.body).hint.match(hint_exp)); + done(); }); }); - - }); diff --git a/test/acceptance/copy-throttle-test.js b/test/acceptance/copy-throttle-test.js index c6fd4dfa..624dbd5a 100644 --- a/test/acceptance/copy-throttle-test.js +++ b/test/acceptance/copy-throttle-test.js @@ -8,8 +8,8 @@ const request = require('request'); const assert = require('assert'); const { Readable } = require('stream'); -const createTableQuery = `CREATE TABLE copy_from_throttle AS (SELECT 0::text AS counter)`; -const dropTableQuery = `DROP TABLE copy_from_throttle`; +const createTableQuery = 'CREATE TABLE copy_from_throttle AS (SELECT 0::text AS counter)'; +const dropTableQuery = 'DROP TABLE copy_from_throttle'; function * counterGenerator (timeout, max_count) { let counter = 0; @@ -22,7 +22,7 @@ function * counterGenerator (timeout, max_count) { } class CounterStream extends Readable { - constructor(generator, ...args) { + constructor (generator, ...args) { super(...args); this.generator = generator; } @@ -36,16 +36,15 @@ class CounterStream extends Readable { } describe('COPY FROM throttle', function () { - before(function() { + before(function () { this.copy_from_minimum_input_speed = global.settings.copy_from_minimum_input_speed; global.settings.copy_from_minimum_input_speed = 2; this.copy_from_maximum_slow_input_speed_interval = global.settings.copy_from_maximum_slow_input_speed_interval; global.settings.copy_from_maximum_slow_input_speed_interval = 1; - }); - after(function() { + after(function () { global.settings.copy_from_first_chunk_timeout = this.copy_from_first_chunk_timeout; global.settings.copy_from_maximum_slow_input_speed_interval = this.copy_from_maximum_slow_input_speed_interval; }); @@ -68,7 +67,7 @@ describe('COPY FROM throttle', function () { beforeEach(function (done) { const { host, port } = this; - const createTable = querystring.stringify({ q: createTableQuery, api_key: 1234}); + const createTable = querystring.stringify({ q: createTableQuery, api_key: 1234 }); const createTableOptions = { url: `http://${host}:${port}/api/v1/sql?${createTable}`, @@ -133,7 +132,7 @@ describe('COPY FROM throttle', function () { assert.equal(res.statusCode, 400); body = JSON.parse(body); - assert.deepEqual(body, { error: ["Connection closed by server: input data too slow"] }); + assert.deepEqual(body, { error: ['Connection closed by server: input data too slow'] }); done(); }); diff --git a/test/acceptance/custom-middlewares-test.js b/test/acceptance/custom-middlewares-test.js index 7635e366..188663e5 100644 --- a/test/acceptance/custom-middlewares-test.js +++ b/test/acceptance/custom-middlewares-test.js @@ -30,7 +30,7 @@ describe('custom middlewares', function () { api: [{ paths: [ '/api/:version', - '/user/:user/api/:version', + '/user/:user/api/:version' ], middlewares: [ customMiddleware @@ -189,13 +189,13 @@ describe('custom middlewares', function () { api: [{ paths: [ '/api/:version', - '/user/:user/api/:version', + '/user/:user/api/:version' ], sql: [{ paths: [ '/sql' ], - middlewares: [ customMiddleware ] + middlewares: [customMiddleware] }] }] }; diff --git a/test/acceptance/error-handler-test.js b/test/acceptance/error-handler-test.js index 6bc5043e..67491bf2 100644 --- a/test/acceptance/error-handler-test.js +++ b/test/acceptance/error-handler-test.js @@ -15,7 +15,7 @@ describe('error handler', function () { assert.response(server, { url: '/api/v1/sql', - headers: {host: 'vizzuality.cartodb.com'}, + headers: { host: 'vizzuality.cartodb.com' }, method: 'GET' }, { @@ -25,7 +25,7 @@ describe('error handler', function () { 'X-SQLAPI-Errors': JSON.stringify(errorHeader) } }, - function(err){ + function (err) { assert.ifError(err); done(); }); diff --git a/test/acceptance/export/arraybuffer-test.js b/test/acceptance/export/arraybuffer-test.js index b891df3f..736f48a7 100644 --- a/test/acceptance/export/arraybuffer-test.js +++ b/test/acceptance/export/arraybuffer-test.js @@ -3,43 +3,40 @@ require('../../helper'); require('../../support/assert'); - var server = require('../../../lib/server')(); var assert = require('assert'); var querystring = require('querystring'); -describe('export.arraybuffer', function() { +describe('export.arraybuffer', function () { + it('GET /api/v1/sql as arraybuffer ', function (done) { + assert.response(server, { + url: '/api/v1/sql?' + querystring.stringify({ + q: 'SELECT cartodb_id,name,1::integer,187.9 FROM untitle_table_4', + format: 'arraybuffer' + }), + headers: { host: 'vizzuality.cartodb.com' }, + method: 'GET' + }, { }, function (err, res) { + assert.equal(res.statusCode, 200, res.body); + assert.equal(res.headers['content-type'], 'application/octet-stream'); + done(); + }); + }); -it('GET /api/v1/sql as arraybuffer ', function(done){ - assert.response(server, { - url: '/api/v1/sql?' + querystring.stringify({ - q: 'SELECT cartodb_id,name,1::integer,187.9 FROM untitle_table_4', - format: 'arraybuffer' - }), - headers: {host: 'vizzuality.cartodb.com'}, - method: 'GET' - },{ }, function(err, res){ - assert.equal(res.statusCode, 200, res.body); - assert.equal(res.headers['content-type'], "application/octet-stream"); - done(); + it('GET /api/v1/sql as arraybuffer does not support geometry types ', function (done) { + assert.response(server, { + url: '/api/v1/sql?' + querystring.stringify({ + q: 'SELECT cartodb_id, the_geom FROM untitle_table_4', + format: 'arraybuffer' + }), + headers: { host: 'vizzuality.cartodb.com' }, + method: 'GET' + }, { }, function (err, res) { + assert.equal(res.statusCode, 400, res.body); + var result = JSON.parse(res.body); + assert.equal(result.error[0], 'geometry types are not supported'); + + done(); + }); }); }); - -it('GET /api/v1/sql as arraybuffer does not support geometry types ', function(done){ - assert.response(server, { - url: '/api/v1/sql?' + querystring.stringify({ - q: 'SELECT cartodb_id, the_geom FROM untitle_table_4', - format: 'arraybuffer' - }), - headers: {host: 'vizzuality.cartodb.com'}, - method: 'GET' - },{ }, function(err, res){ - assert.equal(res.statusCode, 400, res.body); - var result = JSON.parse(res.body); - assert.equal(result.error[0], "geometry types are not supported"); - - done(); - }); -}); - -}); diff --git a/test/acceptance/export/csv-test.js b/test/acceptance/export/csv-test.js index 8a4c5671..f8e61681 100644 --- a/test/acceptance/export/csv-test.js +++ b/test/acceptance/export/csv-test.js @@ -3,207 +3,202 @@ require('../../helper'); require('../../support/assert'); - var server = require('../../../lib/server')(); var assert = require('assert'); var querystring = require('querystring'); -describe('export.csv', function() { +describe('export.csv', function () { + it('CSV format', function (done) { + assert.response(server, { + url: '/api/v1/sql?' + querystring.stringify({ + q: 'SELECT * FROM untitle_table_4 WHERE cartodb_id = 1', + format: 'csv' + }), + headers: { host: 'vizzuality.cartodb.com' }, + method: 'GET' + }, { }, function (err, res) { + assert.equal(res.statusCode, 200, res.body); + var cd = res.headers['content-disposition']; + assert.equal(true, /^attachment/.test(cd), 'CSV is not disposed as attachment: ' + cd); + assert.equal(true, /filename=cartodb-query.csv/gi.test(cd)); + var ct = res.headers['content-type']; + assert.equal(true, /header=present/.test(ct), "CSV doesn't advertise header presence: " + ct); -it('CSV format', function(done){ - assert.response(server, { - url: '/api/v1/sql?' + querystring.stringify({ - q: 'SELECT * FROM untitle_table_4 WHERE cartodb_id = 1', - format: 'csv' - }), - headers: {host: 'vizzuality.cartodb.com'}, - method: 'GET' - },{ }, function(err, res){ - assert.equal(res.statusCode, 200, res.body); - var cd = res.headers['content-disposition']; - assert.equal(true, /^attachment/.test(cd), 'CSV is not disposed as attachment: ' + cd); - assert.equal(true, /filename=cartodb-query.csv/gi.test(cd)); - var ct = res.headers['content-type']; - assert.equal(true, /header=present/.test(ct), "CSV doesn't advertise header presence: " + ct); + var rows = res.body.split(/\r\n/); + var row0 = rows[0].split(','); + var row1 = rows[1].split(','); - var rows = res.body.split(/\r\n/); - var row0 = rows[0].split(','); - var row1 = rows[1].split(','); + assert.equal(row0[2], 'created_at'); + assert.equal(row1[2], '2011-09-21 14:02:21.314252'); - assert.equal(row0[2], 'created_at'); - assert.equal(row1[2], '2011-09-21 14:02:21.314252'); - - done(); - }); -}); - -it('CSV format, bigger than 81920 bytes', function(done){ - assert.response(server, { - url: '/api/v1/sql', - data: querystring.stringify({ - q: 'SELECT 0 as fname FROM generate_series(0,81920)', - format: 'csv' - }), - headers: {host: 'vizzuality.cartodb.com', 'Content-Type': 'application/x-www-form-urlencoded' }, - method: 'POST' - },{ }, function(err, res){ - assert.ok(res.body.length > 81920, 'CSV smaller than expected: ' + res.body.length); - done(); - }); -}); - - -it('CSV format from POST', function(done){ - assert.response(server, { - url: '/api/v1/sql', - data: querystring.stringify({q: "SELECT * FROM untitle_table_4 LIMIT 1", format: 'csv'}), - headers: {host: 'vizzuality.cartodb.com', 'Content-Type': 'application/x-www-form-urlencoded' }, - method: 'POST' - },{ }, function(err, res){ - assert.equal(res.statusCode, 200, res.body); - var cd = res.headers['content-disposition']; - assert.equal(true, /^attachment/.test(cd), 'CSV is not disposed as attachment: ' + cd); - assert.equal(true, /filename=cartodb-query.csv/gi.test(cd)); - var ct = res.headers['content-type']; - assert.equal(true, /header=present/.test(ct), "CSV doesn't advertise header presence: " + ct); - done(); - }); -}); - -it('CSV format, custom filename', function(done){ - assert.response(server, { - url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4%20LIMIT%201&format=csv&filename=mycsv.csv', - headers: {host: 'vizzuality.cartodb.com'}, - method: 'GET' - },{ }, function(err, res){ - assert.equal(res.statusCode, 200, res.body); - var cd = res.headers['content-disposition']; - assert.equal(true, /^attachment/.test(cd), 'CSV is not disposed as attachment: ' + cd); - assert.equal(true, /filename=mycsv.csv/gi.test(cd), cd); - var ct = res.headers['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: 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(); - }); -}); - -it('skipfields controls fields included in CSV output', function(done){ - assert.response(server, { - 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(err, res){ - assert.equal(res.statusCode, 200, res.body); - var row0 = res.body.substring(0, res.body.search(/[\n\r]/)).split(','); - 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(); - }); -}); - -it('GET /api/v1/sql as csv', function(done){ - assert.response(server, { - 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(err, res){ - assert.equal(res.statusCode, 200, res.body); - assert.ok(res.body.match(/cartodb_id,geom\r\n.?1.?,"SRID=4326;POINT(.*)"\r\n/)); - done(); - }); -}); - -// See https://github.com/Vizzuality/CartoDB-SQL-API/issues/60 -it('GET /api/v1/sql as csv with no rows', function(done){ - assert.response(server, { - url: '/api/v1/sql?q=SELECT%20true%20WHERE%20false&format=csv', - headers: {host: 'vizzuality.cartodb.com'}, - method: 'GET' - },{ }, function(err, res){ - assert.equal(res.statusCode, 200, res.body); - 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')); - done(); - }); -}); - -it('GET /api/v1/sql as csv, properly escaped', function(done){ - assert.response(server, { - 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(err, res){ - assert.equal(res.statusCode, 200, res.body); - assert.ok(res.body.match(/cartodb_id,address\r\n.?1.?,"Calle de Pérez Galdós 9, Madrid, Spain"\r\n/)); - done(); - }); -}); - -it('GET /api/v1/sql as csv, concurrently', function(done){ - - var concurrency = 4; - var waiting = concurrency; - function validate(err, res){ - assert.ok(res.body.match(/cartodb_id,address\r\n.?1.?,"Calle de Pérez Galdós 9, Madrid, Spain"\r\n/)); - if ( ! --waiting ) { done(); - } - } - for (var i=0; i 81920, 'CSV smaller than expected: ' + res.body.length); + done(); + }); + }); + + it('CSV format from POST', function (done) { + assert.response(server, { + url: '/api/v1/sql', + data: querystring.stringify({ q: 'SELECT * FROM untitle_table_4 LIMIT 1', format: 'csv' }), + headers: { host: 'vizzuality.cartodb.com', 'Content-Type': 'application/x-www-form-urlencoded' }, + method: 'POST' + }, { }, function (err, res) { + assert.equal(res.statusCode, 200, res.body); + var cd = res.headers['content-disposition']; + assert.equal(true, /^attachment/.test(cd), 'CSV is not disposed as attachment: ' + cd); + assert.equal(true, /filename=cartodb-query.csv/gi.test(cd)); + var ct = res.headers['content-type']; + assert.equal(true, /header=present/.test(ct), "CSV doesn't advertise header presence: " + ct); + done(); + }); + }); + + it('CSV format, custom filename', function (done) { + assert.response(server, { + url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4%20LIMIT%201&format=csv&filename=mycsv.csv', + headers: { host: 'vizzuality.cartodb.com' }, + method: 'GET' + }, { }, function (err, res) { + assert.equal(res.statusCode, 200, res.body); + var cd = res.headers['content-disposition']; + assert.equal(true, /^attachment/.test(cd), 'CSV is not disposed as attachment: ' + cd); + assert.equal(true, /filename=mycsv.csv/gi.test(cd), cd); + var ct = res.headers['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: 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(); + }); + }); + + it('skipfields controls fields included in CSV output', function (done) { + assert.response(server, { + 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 (err, res) { + assert.equal(res.statusCode, 200, res.body); + var row0 = res.body.substring(0, res.body.search(/[\n\r]/)).split(','); + 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(); + }); + }); + + it('GET /api/v1/sql as csv', function (done) { + assert.response(server, { + 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 (err, res) { + assert.equal(res.statusCode, 200, res.body); + assert.ok(res.body.match(/cartodb_id,geom\r\n.?1.?,"SRID=4326;POINT(.*)"\r\n/)); + done(); + }); + }); + + // See https://github.com/Vizzuality/CartoDB-SQL-API/issues/60 + it('GET /api/v1/sql as csv with no rows', function (done) { + assert.response(server, { + url: '/api/v1/sql?q=SELECT%20true%20WHERE%20false&format=csv', + headers: { host: 'vizzuality.cartodb.com' }, + method: 'GET' + }, { }, function (err, res) { + assert.equal(res.statusCode, 200, res.body); + 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')); + done(); + }); + }); + + it('GET /api/v1/sql as csv, properly escaped', function (done) { + assert.response(server, { + 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 (err, res) { + assert.equal(res.statusCode, 200, res.body); + assert.ok(res.body.match(/cartodb_id,address\r\n.?1.?,"Calle de Pérez Galdós 9, Madrid, Spain"\r\n/)); + done(); + }); + }); + + it('GET /api/v1/sql as csv, concurrently', function (done) { + var concurrency = 4; + var waiting = concurrency; + function validate (err, res) { + assert.ok(res.body.match(/cartodb_id,address\r\n.?1.?,"Calle de Pérez Galdós 9, Madrid, Spain"\r\n/)); + if (!--waiting) { + done(); + } + } + for (var i = 0; i < concurrency; ++i) { + assert.response(server, + { + 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 + ); + } + }); + + it('expects 1200 rows in public table', function (done) { var limit = 1200; assert.response(server, { - url: '/api/v1/sql?' + querystring.stringify({ - q: "SELECT * from populated_places_simple_reduced limit " + limit, - format: 'csv' - }), - headers: {host: 'vizzuality.cartodb.com'}, - method: 'GET' - }, - { - status: 200 - }, - function(err, res) { - var headersPlusExtraLine = 2; - assert.equal(res.body.split('\n').length, limit + headersPlusExtraLine); - done(); - } + url: '/api/v1/sql?' + querystring.stringify({ + q: 'SELECT * from populated_places_simple_reduced limit ' + limit, + format: 'csv' + }), + headers: { host: 'vizzuality.cartodb.com' }, + method: 'GET' + }, + { + status: 200 + }, + function (err, res) { + var headersPlusExtraLine = 2; + assert.equal(res.body.split('\n').length, limit + headersPlusExtraLine); + done(); + } ); }); - }); diff --git a/test/acceptance/export/folder-test.js b/test/acceptance/export/folder-test.js index 59d0e389..daa73f0e 100644 --- a/test/acceptance/export/folder-test.js +++ b/test/acceptance/export/folder-test.js @@ -8,8 +8,8 @@ let server = require('../../../lib/server'); const assert = require('assert'); const querystring = require('querystring'); -describe('export folder', function() { - it('folder exists', function(done){ +describe('export folder', function () { + it('folder exists', function (done) { const currentTmpDir = global.settings.tmpDir; const dynamicTmpDir = `/tmp/${new Date().getTime()}/a/b/c`; @@ -18,11 +18,11 @@ describe('export folder', function() { assert.response(server, { url: '/api/v1/sql?' + querystring.stringify({ - q: 'SELECT 1', + q: 'SELECT 1' }), headers: { host: 'vizzuality.cartodb.com' }, method: 'GET' - }, {}, function(err, res) { + }, {}, function (err, res) { assert.ifError(err); assert.ok(res.statusCode === 200); assert.ok(fs.existsSync(dynamicTmpDir)); diff --git a/test/acceptance/export/geojson-test.js b/test/acceptance/export/geojson-test.js index bf19df34..bdaeed3f 100644 --- a/test/acceptance/export/geojson-test.js +++ b/test/acceptance/export/geojson-test.js @@ -7,206 +7,207 @@ var assert = require('../../support/assert'); var querystring = require('querystring'); // use dec_sep for internationalization -var checkDecimals = function(x, dec_sep){ - var tmp='' + x; - if (tmp.indexOf(dec_sep)>-1) { +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; } }; -describe('export.geojson', function() { - +describe('export.geojson', function () { // GEOJSON tests -it('GET /api/v1/sql with SQL parameter, ensuring content-disposition set to geojson', function(done) { - assert.response(server, { - url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4&format=geojson', - headers: {host: 'vizzuality.cartodb.com'}, - method: 'GET' - },{ }, function(err, res){ - assert.equal(res.statusCode, 200, res.body); - var cd = res.headers['content-disposition']; - assert.equal(true, /^attachment/.test(cd), 'GEOJSON is not disposed as attachment: ' + cd); - assert.equal(true, /filename=cartodb-query.geojson/gi.test(cd)); - done(); + it('GET /api/v1/sql with SQL parameter, ensuring content-disposition set to geojson', function (done) { + assert.response(server, { + url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4&format=geojson', + headers: { host: 'vizzuality.cartodb.com' }, + method: 'GET' + }, { }, function (err, res) { + assert.equal(res.statusCode, 200, res.body); + var cd = res.headers['content-disposition']; + assert.equal(true, /^attachment/.test(cd), 'GEOJSON is not disposed as attachment: ' + cd); + assert.equal(true, /filename=cartodb-query.geojson/gi.test(cd)); + done(); + }); + }); + + it('POST /api/v1/sql with SQL parameter, ensuring content-disposition set to geojson', function (done) { + assert.response(server, { + url: '/api/v1/sql', + data: querystring.stringify({ q: 'SELECT * FROM untitle_table_4', format: 'geojson' }), + headers: { host: 'vizzuality.cartodb.com', 'Content-Type': 'application/x-www-form-urlencoded' }, + method: 'POST' + }, { }, function (err, res) { + assert.equal(res.statusCode, 200, res.body); + var cd = res.headers['content-disposition']; + assert.equal(true, /^attachment/.test(cd), 'GEOJSON is not disposed as attachment: ' + cd); + assert.equal(true, /filename=cartodb-query.geojson/gi.test(cd)); + done(); + }); + }); + + it('uses the last format parameter when multiple are used', function (done) { + assert.response(server, { + url: '/api/v1/sql?format=csv&q=SELECT%20*%20FROM%20untitle_table_4&format=geojson', + headers: { host: 'vizzuality.cartodb.com' }, + method: 'GET' + }, { }, function (err, res) { + assert.equal(res.statusCode, 200, res.body); + var cd = res.headers['content-disposition']; + assert.equal(true, /filename=cartodb-query.geojson/gi.test(cd)); + done(); + }); + }); + + it('uses custom filename', function (done) { + assert.response(server, { + url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4&format=geojson&filename=x', + headers: { host: 'vizzuality.cartodb.com' }, + method: 'GET' + }, { }, function (err, res) { + assert.equal(res.statusCode, 200, res.body); + var cd = res.headers['content-disposition']; + assert.equal(true, /filename=x.geojson/gi.test(cd), cd); + done(); + }); + }); + + it('does not include the_geom and the_geom_webmercator properties by default', function (done) { + assert.response(server, { + url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4&format=geojson', + headers: { host: 'vizzuality.cartodb.com' }, + method: 'GET' + }, { }, function (err, res) { + assert.equal(res.statusCode, 200, res.body); + var parsed_body = JSON.parse(res.body); + var row0 = parsed_body.features[0].properties; + var checkfields = { name: 1, cartodb_id: 1, the_geom: 0, the_geom_webmercator: 0 }; + for (var f in checkfields) { + if (checkfields[f]) { + assert.ok(row0.hasOwnProperty(f), "result does not include '" + f + "'"); + } else { + assert.ok(!row0.hasOwnProperty(f), "result includes '" + f + "'"); + } + } + done(); + }); + }); + + it('skipfields controls fields included in GeoJSON output', function (done) { + assert.response(server, { + url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4&format=geojson&skipfields=unexistant,cartodb_id', + headers: { host: 'vizzuality.cartodb.com' }, + method: 'GET' + }, { }, function (err, res) { + assert.equal(res.statusCode, 200, res.body); + var parsed_body = JSON.parse(res.body); + var row0 = parsed_body.features[0].properties; + var checkfields = { name: 1, cartodb_id: 0, the_geom: 0, the_geom_webmercator: 0 }; + for (var f in checkfields) { + if (checkfields[f]) { + assert.ok(row0.hasOwnProperty(f), "result does not include '" + f + "'"); + } else { + assert.ok(!row0.hasOwnProperty(f), "result includes '" + f + "'"); + } + } + done(); + }); + }); + + it('GET /api/v1/sql as geojson limiting decimal places', function (done) { + assert.response(server, { + url: '/api/v1/sql?' + querystring.stringify({ + q: 'SELECT ST_MakePoint(0.123,2.3456) as the_geom', + format: 'geojson', + dp: '1' + }), + headers: { host: 'vizzuality.cartodb.com' }, + method: 'GET' + }, { }, function (err, res) { + assert.equal(res.statusCode, 200, res.body); + var result = JSON.parse(res.body); + assert.equal(1, checkDecimals(result.features[0].geometry.coordinates[0], '.')); + done(); + }); + }); + + it('GET /api/v1/sql as geojson with default dp as 6', function (done) { + assert.response(server, { + url: '/api/v1/sql?' + querystring.stringify({ + q: 'SELECT ST_MakePoint(0.12345678,2.3456787654) as the_geom', + format: 'geojson' + }), + headers: { host: 'vizzuality.cartodb.com' }, + method: 'GET' + }, { }, function (err, res) { + assert.equal(res.statusCode, 200, res.body); + var result = JSON.parse(res.body); + assert.equal(6, checkDecimals(result.features[0].geometry.coordinates[0], '.')); + done(); + }); + }); + + it('null geometries in geojson output', function (done) { + assert.response(server, { + url: '/api/v1/sql?' + querystring.stringify({ + q: "SELECT 1 as gid, 'U' as name, null::geometry as the_geom ", + format: 'geojson' + }), + headers: { host: 'vizzuality.cartodb.com' }, + method: 'GET' + }, { }, function (err, res) { + assert.equal(res.statusCode, 200, res.body); + var cd = res.headers['content-disposition']; + assert.equal(true, /^attachment/.test(cd), 'GEOJSON is not disposed as attachment: ' + cd); + assert.equal(true, /filename=cartodb-query.geojson/gi.test(cd)); + var gjson = JSON.parse(res.body); + var expected = { + type: 'FeatureCollection', + features: [{ + type: 'Feature', + properties: { gid: 1, name: 'U' }, + geometry: null + }] + }; + assert.deepEqual(gjson, expected); + done(); + }); + }); + + it('stream response handle errors', function (done) { + assert.response(server, { + url: '/api/v1/sql?' + querystring.stringify({ + q: 'SELECTT 1 as gid, null::geometry as the_geom ', + format: 'geojson' + }), + headers: { host: 'vizzuality.cartodb.com' }, + method: 'GET' + }, { }, function (err, res) { + assert.equal(res.statusCode, 400, res.body); + var geoJson = JSON.parse(res.body); + assert.ok(geoJson.error); + assert.equal(geoJson.error.length, 1); + assert.ok(geoJson.error[0].match(/^syntax error at or near.*/)); + done(); + }); + }); + + it('stream response with empty result set has valid output', function (done) { + assert.response(server, { + url: '/api/v1/sql?' + querystring.stringify({ + q: 'SELECT 1 as gid, null::geometry as the_geom limit 0', + format: 'geojson' + }), + headers: { host: 'vizzuality.cartodb.com' }, + method: 'GET' + }, { }, function (err, res) { + assert.equal(res.statusCode, 200, res.body); + var geoJson = JSON.parse(res.body); + var expectedGeoJson = { type: 'FeatureCollection', features: [] }; + assert.deepEqual(geoJson, expectedGeoJson); + done(); + }); }); }); - -it('POST /api/v1/sql with SQL parameter, ensuring content-disposition set to geojson', function(done) { - assert.response(server, { - url: '/api/v1/sql', - data: querystring.stringify({q: "SELECT * FROM untitle_table_4", format: 'geojson' }), - headers: {host: 'vizzuality.cartodb.com', 'Content-Type': 'application/x-www-form-urlencoded' }, - method: 'POST' - },{ }, function(err, res){ - assert.equal(res.statusCode, 200, res.body); - var cd = res.headers['content-disposition']; - assert.equal(true, /^attachment/.test(cd), 'GEOJSON is not disposed as attachment: ' + cd); - assert.equal(true, /filename=cartodb-query.geojson/gi.test(cd)); - done(); - }); -}); - -it('uses the last format parameter when multiple are used', function(done){ - assert.response(server, { - url: '/api/v1/sql?format=csv&q=SELECT%20*%20FROM%20untitle_table_4&format=geojson', - headers: {host: 'vizzuality.cartodb.com'}, - method: 'GET' - },{ }, function(err, res){ - assert.equal(res.statusCode, 200, res.body); - var cd = res.headers['content-disposition']; - assert.equal(true, /filename=cartodb-query.geojson/gi.test(cd)); - done(); - }); -}); - -it('uses custom filename', function(done){ - assert.response(server, { - url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4&format=geojson&filename=x', - headers: {host: 'vizzuality.cartodb.com'}, - method: 'GET' - },{ }, function(err, res){ - assert.equal(res.statusCode, 200, res.body); - var cd = res.headers['content-disposition']; - assert.equal(true, /filename=x.geojson/gi.test(cd), cd); - done(); - }); -}); - -it('does not include the_geom and the_geom_webmercator properties by default', function(done){ - assert.response(server, { - url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4&format=geojson', - headers: {host: 'vizzuality.cartodb.com'}, - method: 'GET' - },{ }, function(err, res){ - assert.equal(res.statusCode, 200, res.body); - var parsed_body = JSON.parse(res.body); - var row0 = parsed_body.features[0].properties; - var checkfields = {'name':1, 'cartodb_id':1, 'the_geom':0, 'the_geom_webmercator':0}; - for ( var f in checkfields ) { - if ( checkfields[f] ) { - assert.ok(row0.hasOwnProperty(f), "result does not include '" + f + "'"); - } else { - assert.ok(!row0.hasOwnProperty(f), "result includes '" + f + "'"); - } - } - done(); - }); -}); - -it('skipfields controls fields included in GeoJSON output', function(done){ - assert.response(server, { - url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4&format=geojson&skipfields=unexistant,cartodb_id', - headers: {host: 'vizzuality.cartodb.com'}, - method: 'GET' - },{ }, function(err, res){ - assert.equal(res.statusCode, 200, res.body); - var parsed_body = JSON.parse(res.body); - var row0 = parsed_body.features[0].properties; - var checkfields = {'name':1, 'cartodb_id':0, 'the_geom':0, 'the_geom_webmercator':0}; - for ( var f in checkfields ) { - if ( checkfields[f] ) { - assert.ok(row0.hasOwnProperty(f), "result does not include '" + f + "'"); - } else { - assert.ok(!row0.hasOwnProperty(f), "result includes '" + f + "'"); - } - } - done(); - }); -}); - - -it('GET /api/v1/sql as geojson limiting decimal places', function(done){ - assert.response(server, { - url: '/api/v1/sql?' + querystring.stringify({ - q: 'SELECT ST_MakePoint(0.123,2.3456) as the_geom', - format: 'geojson', - dp: '1'}), - headers: {host: 'vizzuality.cartodb.com'}, - method: 'GET' - },{ }, function(err, res){ - assert.equal(res.statusCode, 200, res.body); - var result = JSON.parse(res.body); - assert.equal(1, checkDecimals(result.features[0].geometry.coordinates[0], '.')); - done(); - }); -}); - -it('GET /api/v1/sql as geojson with default dp as 6', function(done){ - assert.response(server, { - url: '/api/v1/sql?' + querystring.stringify({ - q: 'SELECT ST_MakePoint(0.12345678,2.3456787654) as the_geom', - format: 'geojson'}), - headers: {host: 'vizzuality.cartodb.com'}, - method: 'GET' - },{ }, function(err, res){ - assert.equal(res.statusCode, 200, res.body); - var result = JSON.parse(res.body); - assert.equal(6, checkDecimals(result.features[0].geometry.coordinates[0], '.')); - done(); - }); -}); - -it('null geometries in geojson output', function(done){ - assert.response(server, { - url: '/api/v1/sql?' + querystring.stringify({ - q: "SELECT 1 as gid, 'U' as name, null::geometry as the_geom ", - format: 'geojson' - }), - headers: {host: 'vizzuality.cartodb.com'}, - method: 'GET' - },{ }, function(err, res){ - assert.equal(res.statusCode, 200, res.body); - var cd = res.headers['content-disposition']; - assert.equal(true, /^attachment/.test(cd), 'GEOJSON is not disposed as attachment: ' + cd); - assert.equal(true, /filename=cartodb-query.geojson/gi.test(cd)); - var gjson = JSON.parse(res.body); - var expected = { - type: 'FeatureCollection', - features: [ { type: 'Feature', - properties: { gid: 1, name: 'U' }, - geometry: null } ] - }; - assert.deepEqual(gjson, expected); - done(); - }); -}); - -it('stream response handle errors', function(done) { - assert.response(server, { - url: '/api/v1/sql?' + querystring.stringify({ - q: "SELECTT 1 as gid, null::geometry as the_geom ", - format: 'geojson' - }), - headers: {host: 'vizzuality.cartodb.com'}, - method: 'GET' - },{ }, function(err, res){ - assert.equal(res.statusCode, 400, res.body); - var geoJson = JSON.parse(res.body); - assert.ok(geoJson.error); - assert.equal(geoJson.error.length, 1); - assert.ok(geoJson.error[0].match(/^syntax error at or near.*/)); - done(); - }); -}); - -it('stream response with empty result set has valid output', function(done) { - assert.response(server, { - url: '/api/v1/sql?' + querystring.stringify({ - q: "SELECT 1 as gid, null::geometry as the_geom limit 0", - format: 'geojson' - }), - headers: {host: 'vizzuality.cartodb.com'}, - method: 'GET' - },{ }, function(err, res){ - assert.equal(res.statusCode, 200, res.body); - var geoJson = JSON.parse(res.body); - var expectedGeoJson = {"type": "FeatureCollection", "features": []}; - assert.deepEqual(geoJson, expectedGeoJson); - done(); - }); -}); - -}); diff --git a/test/acceptance/export/geopackage-test.js b/test/acceptance/export/geopackage-test.js index 9ae9b7ae..97ab7eee 100644 --- a/test/acceptance/export/geopackage-test.js +++ b/test/acceptance/export/geopackage-test.js @@ -5,78 +5,76 @@ require('../../helper'); var server = require('../../../lib/server')(); var assert = require('../../support/assert'); var sqlite = require('sqlite3'); -var fs = require('fs'); +var fs = require('fs'); -describe('geopackage query', function(){ +describe('geopackage query', function () { // Default name, cartodb-query, fails because of the hyphen. var table_name = 'a_gpkg_table'; var base_url = '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4%20LIMIT%201&format=gpkg&filename=' + table_name; - it('returns a valid geopackage database', function(done){ + it('returns a valid geopackage database', function (done) { assert.response(server, { url: base_url, - headers: {host: 'vizzuality.cartodb.com'}, + headers: { host: 'vizzuality.cartodb.com' }, method: 'GET' - },{ }, function(err, res) { + }, { }, function (err, res) { assert.equal(res.statusCode, 200, res.body); - assert.equal(res.headers["content-type"], "application/x-sqlite3; charset=utf-8"); - assert.notEqual(res.headers["content-disposition"].indexOf(table_name + ".gpkg"), -1); + assert.equal(res.headers['content-type'], 'application/x-sqlite3; charset=utf-8'); + assert.notEqual(res.headers['content-disposition'].indexOf(table_name + '.gpkg'), -1); var db = new sqlite.Database(':memory:', res.body); - var qr = db.get("PRAGMA database_list", function(err) { + var qr = db.get('PRAGMA database_list', function (err) { assert.equal(err, null); done(); }); assert.notEqual(qr, undefined); }); - }); - it('gets database and geopackage schema', function(done){ + it('gets database and geopackage schema', function (done) { assert.response(server, { url: base_url, - headers: {host: 'vizzuality.cartodb.com'}, + headers: { host: 'vizzuality.cartodb.com' }, encoding: 'binary', method: 'GET' - },{ }, function(err, res) { + }, { }, function (err, res) { var tmpfile = '/tmp/a_geopackage_file.gpkg'; try { - fs.writeFileSync(tmpfile, res.body, 'binary'); - } catch(err) { - return done(err); + fs.writeFileSync(tmpfile, res.body, 'binary'); + } catch (err) { + return done(err); } - var db = new sqlite.Database(tmpfile, function(err) { - if(!!err) { - return done(err); - } + var db = new sqlite.Database(tmpfile, function (err) { + if (err) { + return done(err); + } - db.serialize(function() { - var schemaQuery = "SELECT name FROM sqlite_master WHERE type='table' ORDER BY name"; - var sqr = db.get(schemaQuery, function(err, row) { - assert.equal(err, null); - assert.equal(row.name, table_name); - }); - assert.notEqual(sqr, undefined); + db.serialize(function () { + var schemaQuery = "SELECT name FROM sqlite_master WHERE type='table' ORDER BY name"; + var sqr = db.get(schemaQuery, function (err, row) { + assert.equal(err, null); + assert.equal(row.name, table_name); + }); + assert.notEqual(sqr, undefined); - var gpkgQuery = "SELECT table_name FROM gpkg_contents"; - var gqr = db.get(gpkgQuery, function(err, row) { - assert.equal(row.table_name, table_name); - assert.equal(err, null); - }); - assert.notEqual(gqr, undefined); + var gpkgQuery = 'SELECT table_name FROM gpkg_contents'; + var gqr = db.get(gpkgQuery, function (err, row) { + assert.equal(row.table_name, table_name); + assert.equal(err, null); + }); + assert.notEqual(gqr, undefined); - var dataQuery = "SELECT * FROM " + table_name + " order by cartodb_id"; - var dqr = db.get(dataQuery, function(err, row) { - assert.equal(err, null); - assert.equal(row.cartodb_id, 1); - assert.equal(row.name, 'Hawai'); - assert.equal(row.fid, undefined); - done(); + var dataQuery = 'SELECT * FROM ' + table_name + ' order by cartodb_id'; + var dqr = db.get(dataQuery, function (err, row) { + assert.equal(err, null); + assert.equal(row.cartodb_id, 1); + assert.equal(row.name, 'Hawai'); + assert.equal(row.fid, undefined); + done(); + }); + assert.notEqual(dqr, undefined); }); - assert.notEqual(dqr, undefined); - }); }); }); }); - }); diff --git a/test/acceptance/export/kml-test.js b/test/acceptance/export/kml-test.js index d84f7e40..dd2a6913 100644 --- a/test/acceptance/export/kml-test.js +++ b/test/acceptance/export/kml-test.js @@ -7,381 +7,374 @@ var assert = require('../../support/assert'); var querystring = require('querystring'); var libxmljs = require('libxmljs'); -describe('export.kml', function() { - +describe('export.kml', function () { // Check if an attribute is in the KML output -// -// NOTE: "name" and "description" attributes are threated specially -// in that they are matched in case-insensitive way -// -var hasAttribute = function(kml, att) { + // + // NOTE: "name" and "description" attributes are threated specially + // in that they are matched in case-insensitive way + // + var hasAttribute = function (kml, att) { + // Strip namespace: + // https://github.com/polotek/libxmljs/issues/212 + kml = kml.replace(/ xmlns=[^>]*>/, '>'); - // Strip namespace: - //https://github.com/polotek/libxmljs/issues/212 - kml = kml.replace(/ xmlns=[^>]*>/, '>'); + var doc = libxmljs.parseXmlString(kml); + // console.log("doc: " + doc); + var xpath; - var doc = libxmljs.parseXmlString(kml); - //console.log("doc: " + doc); - var xpath; - - xpath = "//SimpleField[@name='" + att + "']"; - if ( doc.get(xpath) ) { - return true; - } - - xpath = "//Placemark/" + att; - if ( doc.get(xpath) ) { - return true; - } - - var lcatt = att.toLowerCase(); - if ( lcatt === 'name' || lcatt === 'description' ) { - xpath = "//Placemark/" + lcatt; - if ( doc.get(xpath) ) { - return true; - } - } - - //if ( lowerkml.indexOf('simplefield name="'+ loweratt + '"') != -1 ) return true; - //if ( lowerkml.indexOf('<'+loweratt+'>') != -1 ) return true; - return false; -}; - -// Return the first coordinate array found in KML -var extractCoordinates = function(kml) { - - // Strip namespace: - //https://github.com/polotek/libxmljs/issues/212 - kml = kml.replace(/ xmlns=[^>]*>/, '>'); - - var doc = libxmljs.parseXmlString(kml); - //console.log("doc: " + doc); - if ( ! doc ) { - return; - } - var coo = doc.get("//coordinates"); - //console.log("coo: " + coo); - if ( ! coo ) { - return; - } - coo = coo.text(); - //console.log("coo: " + coo); - if ( ! coo ) { - return; - } - coo = coo.split(' '); - //console.log("coo: " + coo); - for (var i=0; i]*>/, '>'); - - var doc = libxmljs.parseXmlString(kml); - //console.log("doc: " + doc); - if ( ! doc ) { - return; - } - var coo = doc.get("//Document/Folder/name"); - //console.log("coo: " + coo); - if ( ! coo ) { - return; - } - coo = coo.text(); - //console.log("coo: " + coo); - if ( ! coo ) { - return; - } - return coo; -}; - -// KML tests - -it('KML format, unauthenticated', function(done){ - assert.response(server, { - url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4%20LIMIT%201&format=kml', - headers: {host: 'vizzuality.cartodb.com'}, - method: 'GET' - },{ }, function(err, res){ - assert.equal(res.statusCode, 200, res.body); - var cd = res.headers['content-disposition']; - assert.equal(true, /^attachment/.test(cd), 'KML is not disposed as attachment: ' + cd); - assert.equal(true, /filename=cartodb-query.kml/gi.test(cd), 'Unexpected KML filename: ' + cd); - var row0 = res.body; - var checkfields = {'Name':1, 'address':1, 'cartodb_id':1, 'the_geom':0, 'the_geom_webmercator':0}; - Object.keys(checkfields).forEach(function(f) { - if ( checkfields[f] ) { - assert.ok(hasAttribute(row0, f), "result does not include '" + f + "': " + row0); - } else { - assert.ok(!hasAttribute(row0, f), "result includes '" + f + "'"); - } - }); - done(); - }); -}); - -it('KML format, unauthenticated, POST', function(done){ - assert.response(server, { - url: '/api/v1/sql', - data: 'q=SELECT%20*%20FROM%20untitle_table_4%20LIMIT%201&format=kml', - headers: {host: 'vizzuality.cartodb.com', 'Content-Type': 'application/x-www-form-urlencoded' }, - method: 'POST' - },{ }, function(err, res){ - assert.equal(res.statusCode, 200, res.body); - var cd = res.headers['content-disposition']; - assert.equal(true, /^attachment/.test(cd), 'KML is not disposed as attachment: ' + cd); - assert.equal(true, /filename=cartodb-query.kml/gi.test(cd), 'Unexpected KML filename: ' + cd); - done(); - }); -}); - -it('KML format, bigger than 81920 bytes', function(done){ - assert.response(server, { - url: '/api/v1/sql', - data: querystring.stringify({ - q: 'SELECT 0 as fname FROM generate_series(0,81920)', - format: 'kml' - }), - headers: {host: 'vizzuality.cartodb.com', 'Content-Type': 'application/x-www-form-urlencoded' }, - method: 'POST' - },{ }, function(err, res){ - assert.equal(res.statusCode, 200, res.body); - var cd = res.headers['content-disposition']; - assert.equal(true, /^attachment/.test(cd), 'KML is not disposed as attachment: ' + cd); - assert.equal(true, /filename=cartodb-query.kml/gi.test(cd), 'Unexpected KML filename: ' + cd); - assert.ok(res.body.length > 81920, 'KML smaller than expected: ' + res.body.length); - done(); - }); -}); - -it('KML format, skipfields', function(done){ - assert.response(server, { - url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4%20LIMIT%201&format=kml&skipfields=address,cartodb_id', - headers: {host: 'vizzuality.cartodb.com'}, - method: 'GET' - },{ }, function(err, res){ - assert.equal(res.statusCode, 200, res.body); - var cd = res.headers['content-disposition']; - assert.equal(true, /^attachment/.test(cd), 'KML is not disposed as attachment: ' + cd); - assert.equal(true, /filename=cartodb-query.kml/gi.test(cd), 'Unexpected KML filename: ' + cd); - var row0 = res.body; - var checkFields = {'Name':1, 'address':0, 'cartodb_id':0, 'the_geom':0, 'the_geom_webmercator':0}; - Object.keys(checkFields).forEach(function(f) { - if ( checkFields[f] ) { - assert.ok(hasAttribute(row0, f), "result does not include '" + f + "': " + row0); - } else { - assert.ok(!hasAttribute(row0, f), "result includes '" + f + "'"); - } - }); - done(); - }); -}); - -it('KML format, unauthenticated, custom filename', function(done){ - assert.response(server, { - url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4%20LIMIT%201&format=kml&filename=kmltest', - headers: {host: 'vizzuality.cartodb.com'}, - method: 'GET' - },{ }, function(err, res){ - assert.equal(res.statusCode, 200, res.body); - var cd = res.headers['content-disposition']; - assert.equal(true, /^attachment/.test(cd), 'KML is not disposed as attachment: ' + cd); - assert.equal(true, /filename=kmltest.kml/gi.test(cd), 'Unexpected KML filename: ' + cd); - var name = extractFolderName(res.body); - assert.equal(name, "kmltest"); - done(); - }); -}); - -it('KML format, authenticated', function(done){ - assert.response(server, { - url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4%20LIMIT%201&format=kml&api_key=1234', - headers: {host: 'vizzuality.cartodb.com'}, - method: 'GET' - },{ }, function(err, res){ - assert.equal(res.statusCode, 200, res.body); - var cd = res.headers['content-disposition']; - assert.equal(true, /filename=cartodb-query.kml/gi.test(cd), 'Unexpected KML filename: ' + cd); - 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", - format: 'kml', - filename: 'multi' - }); - - var concurrency = 4; - var waiting = concurrency; - - function validate(err, res) { - //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, "') != -1 ) return true; + return false; }; - for (var i=0; i]*>/, '>'); -// See https://github.com/Vizzuality/CartoDB-SQL-API/issues/60 -it('GET /api/v1/sql as kml with no rows', function(done){ - assert.response(server, { - url: '/api/v1/sql?q=SELECT%20true%20WHERE%20false&format=kml', - headers: {host: 'vizzuality.cartodb.com'}, - method: 'GET' - },{ }, function(err, 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" \\?>' + + var doc = libxmljs.parseXmlString(kml); + // console.log("doc: " + doc); + if (!doc) { + return; + } + var coo = doc.get('//coordinates'); + // console.log("coo: " + coo); + if (!coo) { + return; + } + coo = coo.text(); + // console.log("coo: " + coo); + if (!coo) { + return; + } + coo = coo.split(' '); + // console.log("coo: " + coo); + for (var i = 0; i < coo.length; ++i) { + coo[i] = coo[i].split(','); + } + + return coo; + }; + + // Return the first folder name in KML + var extractFolderName = function (kml) { + // Strip namespace: + // https://github.com/polotek/libxmljs/issues/212 + kml = kml.replace(/ xmlns=[^>]*>/, '>'); + + var doc = libxmljs.parseXmlString(kml); + // console.log("doc: " + doc); + if (!doc) { + return; + } + var coo = doc.get('//Document/Folder/name'); + // console.log("coo: " + coo); + if (!coo) { + return; + } + coo = coo.text(); + // console.log("coo: " + coo); + if (!coo) { + return; + } + return coo; + }; + + // KML tests + + it('KML format, unauthenticated', function (done) { + assert.response(server, { + url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4%20LIMIT%201&format=kml', + headers: { host: 'vizzuality.cartodb.com' }, + method: 'GET' + }, { }, function (err, res) { + assert.equal(res.statusCode, 200, res.body); + var cd = res.headers['content-disposition']; + assert.equal(true, /^attachment/.test(cd), 'KML is not disposed as attachment: ' + cd); + assert.equal(true, /filename=cartodb-query.kml/gi.test(cd), 'Unexpected KML filename: ' + cd); + var row0 = res.body; + var checkfields = { Name: 1, address: 1, cartodb_id: 1, the_geom: 0, the_geom_webmercator: 0 }; + Object.keys(checkfields).forEach(function (f) { + if (checkfields[f]) { + assert.ok(hasAttribute(row0, f), "result does not include '" + f + "': " + row0); + } else { + assert.ok(!hasAttribute(row0, f), "result includes '" + f + "'"); + } + }); + done(); + }); + }); + + it('KML format, unauthenticated, POST', function (done) { + assert.response(server, { + url: '/api/v1/sql', + data: 'q=SELECT%20*%20FROM%20untitle_table_4%20LIMIT%201&format=kml', + headers: { host: 'vizzuality.cartodb.com', 'Content-Type': 'application/x-www-form-urlencoded' }, + method: 'POST' + }, { }, function (err, res) { + assert.equal(res.statusCode, 200, res.body); + var cd = res.headers['content-disposition']; + assert.equal(true, /^attachment/.test(cd), 'KML is not disposed as attachment: ' + cd); + assert.equal(true, /filename=cartodb-query.kml/gi.test(cd), 'Unexpected KML filename: ' + cd); + done(); + }); + }); + + it('KML format, bigger than 81920 bytes', function (done) { + assert.response(server, { + url: '/api/v1/sql', + data: querystring.stringify({ + q: 'SELECT 0 as fname FROM generate_series(0,81920)', + format: 'kml' + }), + headers: { host: 'vizzuality.cartodb.com', 'Content-Type': 'application/x-www-form-urlencoded' }, + method: 'POST' + }, { }, function (err, res) { + assert.equal(res.statusCode, 200, res.body); + var cd = res.headers['content-disposition']; + assert.equal(true, /^attachment/.test(cd), 'KML is not disposed as attachment: ' + cd); + assert.equal(true, /filename=cartodb-query.kml/gi.test(cd), 'Unexpected KML filename: ' + cd); + assert.ok(res.body.length > 81920, 'KML smaller than expected: ' + res.body.length); + done(); + }); + }); + + it('KML format, skipfields', function (done) { + assert.response(server, { + url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4%20LIMIT%201&format=kml&skipfields=address,cartodb_id', + headers: { host: 'vizzuality.cartodb.com' }, + method: 'GET' + }, { }, function (err, res) { + assert.equal(res.statusCode, 200, res.body); + var cd = res.headers['content-disposition']; + assert.equal(true, /^attachment/.test(cd), 'KML is not disposed as attachment: ' + cd); + assert.equal(true, /filename=cartodb-query.kml/gi.test(cd), 'Unexpected KML filename: ' + cd); + var row0 = res.body; + var checkFields = { Name: 1, address: 0, cartodb_id: 0, the_geom: 0, the_geom_webmercator: 0 }; + Object.keys(checkFields).forEach(function (f) { + if (checkFields[f]) { + assert.ok(hasAttribute(row0, f), "result does not include '" + f + "': " + row0); + } else { + assert.ok(!hasAttribute(row0, f), "result includes '" + f + "'"); + } + }); + done(); + }); + }); + + it('KML format, unauthenticated, custom filename', function (done) { + assert.response(server, { + url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4%20LIMIT%201&format=kml&filename=kmltest', + headers: { host: 'vizzuality.cartodb.com' }, + method: 'GET' + }, { }, function (err, res) { + assert.equal(res.statusCode, 200, res.body); + var cd = res.headers['content-disposition']; + assert.equal(true, /^attachment/.test(cd), 'KML is not disposed as attachment: ' + cd); + assert.equal(true, /filename=kmltest.kml/gi.test(cd), 'Unexpected KML filename: ' + cd); + var name = extractFolderName(res.body); + assert.equal(name, 'kmltest'); + done(); + }); + }); + + it('KML format, authenticated', function (done) { + assert.response(server, { + url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4%20LIMIT%201&format=kml&api_key=1234', + headers: { host: 'vizzuality.cartodb.com' }, + method: 'GET' + }, { }, function (err, res) { + assert.equal(res.statusCode, 200, res.body); + var cd = res.headers['content-disposition']; + assert.equal(true, /filename=cartodb-query.kml/gi.test(cd), 'Unexpected KML filename: ' + cd); + 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', + format: 'kml', + filename: 'multi' + }); + + var concurrency = 4; + var waiting = concurrency; + + function validate (err, res) { + // 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, '' + '' + 'cartodb_query' + '$'); - var body = res.body.replace(/\n/g,''); - assert.ok(body.match(pat), - "Response:\n" + body + '\ndoes not match pattern:\n' + pat); - done(); + var body = res.body.replace(/\n/g, ''); + assert.ok(body.match(pat), + 'Response:\n' + body + '\ndoes not match pattern:\n' + pat); + done(); + }); }); -}); -// See https://github.com/Vizzuality/CartoDB-SQL-API/issues/90 -it('GET /api/v1/sql as kml with ending semicolon', function(done){ - assert.response(server, { - url: '/api/v1/sql?' + querystring.stringify({ - q: 'SELECT true WHERE false;', - format: 'kml' - }), - headers: {host: 'vizzuality.cartodb.com'}, - method: 'GET' - },{ }, function(err, 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" \\?>' + + // See https://github.com/Vizzuality/CartoDB-SQL-API/issues/90 + it('GET /api/v1/sql as kml with ending semicolon', function (done) { + assert.response(server, { + url: '/api/v1/sql?' + querystring.stringify({ + q: 'SELECT true WHERE false;', + format: 'kml' + }), + headers: { host: 'vizzuality.cartodb.com' }, + method: 'GET' + }, { }, function (err, 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" \\?>' + '' + 'cartodb_query' + '$'); - var body = res.body.replace(/\n/g,''); - assert.ok(body.match(pat), - "Response:\n" + body + '\ndoes not match pattern:\n' + pat); - done(); + var body = res.body.replace(/\n/g, ''); + assert.ok(body.match(pat), + 'Response:\n' + body + '\ndoes not match pattern:\n' + pat); + done(); + }); }); -}); -// See https://github.com/CartoDB/cartodb/issues/276 -it('check point coordinates, unauthenticated', function(done){ - assert.response(server, { - url: '/api/v1/sql?' + querystring.stringify({ - q: 'SELECT * from untitle_table_4 WHERE cartodb_id = -1', - format: 'kml' - }), - headers: {host: 'vizzuality.cartodb.com'}, - method: 'GET' - },{ }, function(err, res){ - assert.equal(res.statusCode, 200, res.body); - var coords = extractCoordinates(res.body); - assert(coords, 'No coordinates in ' + res.body); - assert.deepEqual(coords, [[33,16]]); - done(); + // See https://github.com/CartoDB/cartodb/issues/276 + it('check point coordinates, unauthenticated', function (done) { + assert.response(server, { + url: '/api/v1/sql?' + querystring.stringify({ + q: 'SELECT * from untitle_table_4 WHERE cartodb_id = -1', + format: 'kml' + }), + headers: { host: 'vizzuality.cartodb.com' }, + method: 'GET' + }, { }, function (err, res) { + assert.equal(res.statusCode, 200, res.body); + var coords = extractCoordinates(res.body); + assert(coords, 'No coordinates in ' + res.body); + assert.deepEqual(coords, [[33, 16]]); + done(); + }); }); -}); -// See https://github.com/CartoDB/cartodb/issues/276 -it('check point coordinates, authenticated', function(done){ - assert.response(server, { - url: '/api/v1/sql?' + querystring.stringify({ - q: 'SELECT * from untitle_table_4 WHERE cartodb_id = -1', - api_key: 1234, - format: 'kml' - }), - headers: {host: 'vizzuality.cartodb.com'}, - method: 'GET' - },{ }, function(err, res){ - assert.equal(res.statusCode, 200, res.body); - var coords = extractCoordinates(res.body); - assert(coords, 'No coordinates in ' + res.body); - assert.deepEqual(coords, [[33,16]]); - done(); + // See https://github.com/CartoDB/cartodb/issues/276 + it('check point coordinates, authenticated', function (done) { + assert.response(server, { + url: '/api/v1/sql?' + querystring.stringify({ + q: 'SELECT * from untitle_table_4 WHERE cartodb_id = -1', + api_key: 1234, + format: 'kml' + }), + headers: { host: 'vizzuality.cartodb.com' }, + method: 'GET' + }, { }, function (err, res) { + assert.equal(res.statusCode, 200, res.body); + var coords = extractCoordinates(res.body); + assert(coords, 'No coordinates in ' + res.body); + assert.deepEqual(coords, [[33, 16]]); + done(); + }); }); -}); - var limit = 1200; - it('expects ' + limit + ' placemarks in public table', function(done){ - + it('expects ' + limit + ' placemarks in public table', function (done) { assert.response(server, { - url: '/api/v1/sql', - data: querystring.stringify({ - q: "SELECT * from populated_places_simple_reduced limit " + limit, - format: 'kml' - }), - headers: {host: 'vizzuality.cartodb.com', 'Content-Type': 'application/x-www-form-urlencoded' }, - method: 'POST' - }, - { - status: 200 - }, - function(err, res) { - assert.equal(res.body.match(//g).length, limit); - done(); - } + url: '/api/v1/sql', + data: querystring.stringify({ + q: 'SELECT * from populated_places_simple_reduced limit ' + limit, + format: 'kml' + }), + headers: { host: 'vizzuality.cartodb.com', 'Content-Type': 'application/x-www-form-urlencoded' }, + method: 'POST' + }, + { + status: 200 + }, + function (err, res) { + assert.equal(res.body.match(//g).length, limit); + done(); + } ); }); - it('expects ' + limit + ' placemarks in private table using the API KEY', function(done){ - + it('expects ' + limit + ' placemarks in private table using the API KEY', function (done) { assert.response(server, { - url: '/api/v1/sql?' + querystring.stringify({ - q: "SELECT * from populated_places_simple_reduced limit " + limit, - api_key: 1234, - format: 'kml' - }), - headers: {host: 'vizzuality.cartodb.com'}, - method: 'GET' - }, - { - status: 200 - }, - function(err, res) { - assert.equal(res.body.match(//g).length, limit); - done(); - } + url: '/api/v1/sql?' + querystring.stringify({ + q: 'SELECT * from populated_places_simple_reduced limit ' + limit, + api_key: 1234, + format: 'kml' + }), + headers: { host: 'vizzuality.cartodb.com' }, + method: 'GET' + }, + { + status: 200 + }, + function (err, res) { + assert.equal(res.body.match(//g).length, limit); + done(); + } ); }); - it('should work with queries returning no results', function(done) { + it('should work with queries returning no results', function (done) { assert.response( server, { - url: "/api/v1/sql?" + querystring.stringify({ - q: "SELECT * FROM populated_places_simple_reduced LIMIT 0", + url: '/api/v1/sql?' + querystring.stringify({ + q: 'SELECT * FROM populated_places_simple_reduced LIMIT 0', format: 'kml' }), headers: { @@ -393,11 +386,10 @@ it('check point coordinates, authenticated', function(done){ { status: 200 }, - function(err, res) { + function (err, res) { assert.equal(res.body.match(//g), null); done(); } ); }); - }); diff --git a/test/acceptance/export/shapefile-test.js b/test/acceptance/export/shapefile-test.js index 33d26951..8ddf561c 100644 --- a/test/acceptance/export/shapefile-test.js +++ b/test/acceptance/export/shapefile-test.js @@ -8,396 +8,392 @@ var querystring = require('querystring'); var shapefile = require('shapefile'); var _ = require('underscore'); var zipfile = require('zipfile'); -var fs = require('fs'); - -describe('export.shapefile', function() { +var fs = require('fs'); +describe('export.shapefile', function () { // SHP tests -it('SHP format, unauthenticated', function(done){ - assert.response(server, { - 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(err, res){ - assert.equal(res.statusCode, 200, res.body); - var cd = res.headers['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 writeErr = fs.writeFileSync(tmpfile, res.body, 'binary'); - if (writeErr) { - return done(writeErr); - } - 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); - done(); - }); -}); - -it('SHP format, unauthenticated, POST', function(done){ - assert.response(server, { - url: '/api/v1/sql', - data: 'q=SELECT%20*%20FROM%20untitle_table_4%20LIMIT%201&format=shp', - headers: {host: 'vizzuality.cartodb.com', 'Content-Type': 'application/x-www-form-urlencoded' }, - method: 'POST' - },{ }, function(err, res){ - assert.equal(res.statusCode, 200, res.body); - var cd = res.headers['content-disposition']; - assert.equal(true, /^attachment/.test(cd), 'SHP is not disposed as attachment: ' + cd); - assert.equal(true, /filename=cartodb-query.zip/gi.test(cd), 'Unexpected SHP filename: ' + cd); - done(); - }); -}); - -it('SHP format, big size, POST', function(done){ - assert.response(server, { - url: '/api/v1/sql', - data: querystring.stringify({ - q: 'SELECT 0 as fname, st_makepoint(i,i) FROM generate_series(0,81920) i', - format: 'shp' - }), - headers: {host: 'vizzuality.cartodb.com', 'Content-Type': 'application/x-www-form-urlencoded' }, - method: 'POST' - },{ }, function(err, res){ - assert.equal(res.statusCode, 200, res.body); - var cd = res.headers['content-disposition']; - assert.equal(true, /^attachment/.test(cd), 'SHP is not disposed as attachment: ' + cd); - assert.equal(true, /filename=cartodb-query.zip/gi.test(cd), 'Unexpected SHP filename: ' + cd); - assert.ok(res.body.length > 81920, 'SHP smaller than expected: ' + res.body.length); - done(); - }); -}); - -it('SHP format, unauthenticated, with custom filename', function(done){ - assert.response(server, { - url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4%20LIMIT%201&format=shp&filename=myshape', - headers: {host: 'vizzuality.cartodb.com'}, - encoding: 'binary', - method: 'GET' - },{ }, function(err, res){ - assert.equal(res.statusCode, 200, res.body); - var cd = res.headers['content-disposition']; - assert.equal(true, /^attachment/.test(cd), 'SHP is not disposed as attachment: ' + cd); - assert.equal(true, /filename=myshape.zip/gi.test(cd)); - var tmpfile = '/tmp/myshape.zip'; - var writeErr = fs.writeFileSync(tmpfile, res.body, 'binary'); - if (writeErr) { - return done(writeErr); - } - 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); - assert.ok(_.contains(zf.names, 'myshape.dbf'), 'SHP zipfile does not contain .dbf: ' + zf.names); - assert.ok(_.contains(zf.names, 'myshape.prj'), 'SHP zipfile does not contain .prj: ' + zf.names); - fs.unlinkSync(tmpfile); - done(); - }); -}); - -it('SHP format, unauthenticated, with custom, dangerous filename', function(done){ - assert.response(server, { - url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4%20LIMIT%201&format=shp&filename=b;"%20()[]a', - headers: {host: 'vizzuality.cartodb.com'}, - encoding: 'binary', - method: 'GET' - },{ }, function(err, res){ - assert.equal(res.statusCode, 200, res.body); - var fname = "b_______a"; - var cd = res.headers['content-disposition']; - assert.equal(true, /^attachment/.test(cd), 'SHP is not disposed as attachment: ' + cd); - assert.equal(true, /filename=b_______a.zip/gi.test(cd), 'Unexpected SHP filename: ' + cd); - var tmpfile = '/tmp/myshape.zip'; - var writeErr = fs.writeFileSync(tmpfile, res.body, 'binary'); - if (writeErr) { - return done(writeErr); - } - 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); - assert.ok(_.contains(zf.names, fname + '.dbf'), 'SHP zipfile does not contain .dbf: ' + zf.names); - assert.ok(_.contains(zf.names, fname+ '.prj'), 'SHP zipfile does not contain .prj: ' + zf.names); - fs.unlinkSync(tmpfile); - done(); - }); -}); - -it('SHP format, authenticated', function(done){ - assert.response(server, { - url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4%20LIMIT%201&format=shp&api_key=1234', - headers: {host: 'vizzuality.cartodb.com'}, - encoding: 'binary', - method: 'GET' - },{ }, function(err, res){ - assert.equal(res.statusCode, 200, res.body); - var cd = res.headers['content-disposition']; - assert.equal(true, /filename=cartodb-query.zip/gi.test(cd)); - var tmpfile = '/tmp/myshape.zip'; - var writeErr = fs.writeFileSync(tmpfile, res.body, 'binary'); - if (writeErr) { - return done(writeErr); - } - 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 contents of the DBF - fs.unlinkSync(tmpfile); - done(); - }); -}); - - -// See https://github.com/Vizzuality/CartoDB-SQL-API/issues/66 -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', - filename: 'myshape' - }); - assert.response(server, { - url: '/api/v1/sql?' + query, - headers: {host: 'vizzuality.cartodb.com'}, - encoding: 'binary', - method: 'GET' - },{ }, function(err, res){ - assert.equal(res.statusCode, 200, res.body); - var tmpfile = '/tmp/myshape.zip'; - var writeErr = fs.writeFileSync(tmpfile, res.body, 'binary'); - if (writeErr) { - return done(writeErr); - } - var zf = new zipfile.ZipFile(tmpfile); - var buffer = zf.readFileSync('myshape.dbf'); - fs.unlinkSync(tmpfile); - var strings = buffer.toString(); - assert.ok(/♥♦♣♠/.exec(strings), "Cannot find '♥♦♣♠' in here:\n" + strings); - done(); - }); -}); - -// See https://github.com/Vizzuality/CartoDB-SQL-API/issues/66 -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", - format: 'shp' - }); - assert.response(server, { - url: '/api/v1/sql?' + query, - headers: {host: 'vizzuality.cartodb.com'}, - encoding: 'binary', - method: 'GET' - },{ }, function(err, res){ - assert.deepEqual(res.headers['content-type'], 'application/json; charset=utf-8'); - assert.deepEqual(res.headers['content-disposition'], 'inline'); - assert.equal(res.statusCode, 400, res.statusCode + ': ' +res.body); - var parsedBody = JSON.parse(res.body); - var error = parsedBody.error[0]; - var expectedError = /Attempt to write non-point \(LINESTRING\) geometry to point shapefile/g; - assert.ok(expectedError.test(error), error); - done(); - }); -}); - -// See https://github.com/Vizzuality/CartoDB-SQL-API/issues/87 -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", - "SELECT 'LINESTRING(0 0, 1 0)'::geometry, 2" - ].join(" UNION ALL "), - format: 'shp' - }); - assert.response(server, { - url: '/api/v1/sql?' + query, - headers: {host: 'vizzuality.cartodb.com'}, - encoding: 'binary', - method: 'GET' - },{ }, function(err, res){ - assert.deepEqual(res.headers['content-type'], 'application/json; charset=utf-8'); - assert.deepEqual(res.headers['content-disposition'], 'inline'); - assert.equal(res.statusCode, 400, res.statusCode + ': ' +res.body); - var parsedBody = JSON.parse(res.body); - var error = parsedBody.error[0]; - var expectedError = /Attempt to write non-point \(LINESTRING\) geometry to point shapefile/g; - assert.ok(expectedError.test(error), error); - 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', - skipfields: 'skipme', - filename: 'myshape' - }); - assert.response(server, { - url: '/api/v1/sql?' + query, - headers: {host: 'vizzuality.cartodb.com'}, - encoding: 'binary', - method: 'GET' - },{ }, function(err, res){ - assert.equal(res.statusCode, 200, res.body); - var tmpfile = '/tmp/myshape.zip'; - var writeErr = fs.writeFileSync(tmpfile, res.body, 'binary'); - if (writeErr) { - return done(writeErr); - } - var zf = new zipfile.ZipFile(tmpfile); - var buffer = zf.readFileSync('myshape.dbf'); - fs.unlinkSync(tmpfile); - var strings = buffer.toString(); - assert.ok(!/skipme/.exec(strings), "Could not skip 'skipme' field:\n" + strings); - done(); - }); -}); - -it('SHP format, concurrently', function(done){ - var concurrency = 1; - var waiting = concurrency; - function validate(err, res){ - var cd = res.headers['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 writeErr = fs.writeFileSync(tmpfile, res.body, 'binary'); - if (writeErr) { - return done(writeErr); - } - 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 81920, 'SHP smaller than expected: ' + res.body.length); + done(); + }); + }); + + it('SHP format, unauthenticated, with custom filename', function (done) { + assert.response(server, { + url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4%20LIMIT%201&format=shp&filename=myshape', + headers: { host: 'vizzuality.cartodb.com' }, + encoding: 'binary', + method: 'GET' + }, { }, function (err, res) { + assert.equal(res.statusCode, 200, res.body); + var cd = res.headers['content-disposition']; + assert.equal(true, /^attachment/.test(cd), 'SHP is not disposed as attachment: ' + cd); + assert.equal(true, /filename=myshape.zip/gi.test(cd)); + var tmpfile = '/tmp/myshape.zip'; + var writeErr = fs.writeFileSync(tmpfile, res.body, 'binary'); + if (writeErr) { + return done(writeErr); + } + 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); + assert.ok(_.contains(zf.names, 'myshape.dbf'), 'SHP zipfile does not contain .dbf: ' + zf.names); + assert.ok(_.contains(zf.names, 'myshape.prj'), 'SHP zipfile does not contain .prj: ' + zf.names); + fs.unlinkSync(tmpfile); + done(); + }); + }); + + it('SHP format, unauthenticated, with custom, dangerous filename', function (done) { + assert.response(server, { + url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4%20LIMIT%201&format=shp&filename=b;"%20()[]a', + headers: { host: 'vizzuality.cartodb.com' }, + encoding: 'binary', + method: 'GET' + }, { }, function (err, res) { + assert.equal(res.statusCode, 200, res.body); + var fname = 'b_______a'; + var cd = res.headers['content-disposition']; + assert.equal(true, /^attachment/.test(cd), 'SHP is not disposed as attachment: ' + cd); + assert.equal(true, /filename=b_______a.zip/gi.test(cd), 'Unexpected SHP filename: ' + cd); + var tmpfile = '/tmp/myshape.zip'; + var writeErr = fs.writeFileSync(tmpfile, res.body, 'binary'); + if (writeErr) { + return done(writeErr); + } + 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); + assert.ok(_.contains(zf.names, fname + '.dbf'), 'SHP zipfile does not contain .dbf: ' + zf.names); + assert.ok(_.contains(zf.names, fname + '.prj'), 'SHP zipfile does not contain .prj: ' + zf.names); + fs.unlinkSync(tmpfile); + done(); + }); + }); + + it('SHP format, authenticated', function (done) { + assert.response(server, { + url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4%20LIMIT%201&format=shp&api_key=1234', + headers: { host: 'vizzuality.cartodb.com' }, + encoding: 'binary', + method: 'GET' + }, { }, function (err, res) { + assert.equal(res.statusCode, 200, res.body); + var cd = res.headers['content-disposition']; + assert.equal(true, /filename=cartodb-query.zip/gi.test(cd)); + var tmpfile = '/tmp/myshape.zip'; + var writeErr = fs.writeFileSync(tmpfile, res.body, 'binary'); + if (writeErr) { + return done(writeErr); + } + 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 contents of the DBF + fs.unlinkSync(tmpfile); + done(); + }); + }); + + // See https://github.com/Vizzuality/CartoDB-SQL-API/issues/66 + it('SHP format, unauthenticated, with utf8 data', function (done) { var query = querystring.stringify({ - q: "SELECT st_makepoint(0,0,4326) as the_geom", + q: "SELECT '♥♦♣♠' as f, st_makepoint(0,0,4326) as the_geom", format: 'shp', filename: 'myshape' }); assert.response(server, { url: '/api/v1/sql?' + query, - headers: {host: 'vizzuality.cartodb.com'}, + headers: { host: 'vizzuality.cartodb.com' }, encoding: 'binary', method: 'GET' - },{ }, function(err, res){ + }, { }, function (err, res) { + assert.equal(res.statusCode, 200, res.body); + var tmpfile = '/tmp/myshape.zip'; + var writeErr = fs.writeFileSync(tmpfile, res.body, 'binary'); + if (writeErr) { + return done(writeErr); + } + var zf = new zipfile.ZipFile(tmpfile); + var buffer = zf.readFileSync('myshape.dbf'); + fs.unlinkSync(tmpfile); + var strings = buffer.toString(); + assert.ok(/♥♦♣♠/.exec(strings), "Cannot find '♥♦♣♠' in here:\n" + strings); + done(); + }); + }); + + // See https://github.com/Vizzuality/CartoDB-SQL-API/issues/66 + 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", + format: 'shp' + }); + assert.response(server, { + url: '/api/v1/sql?' + query, + headers: { host: 'vizzuality.cartodb.com' }, + encoding: 'binary', + method: 'GET' + }, { }, function (err, res) { + assert.deepEqual(res.headers['content-type'], 'application/json; charset=utf-8'); + assert.deepEqual(res.headers['content-disposition'], 'inline'); + assert.equal(res.statusCode, 400, res.statusCode + ': ' + res.body); + var parsedBody = JSON.parse(res.body); + var error = parsedBody.error[0]; + var expectedError = /Attempt to write non-point \(LINESTRING\) geometry to point shapefile/g; + assert.ok(expectedError.test(error), error); + done(); + }); + }); + + // See https://github.com/Vizzuality/CartoDB-SQL-API/issues/87 + 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", + "SELECT 'LINESTRING(0 0, 1 0)'::geometry, 2" + ].join(' UNION ALL '), + format: 'shp' + }); + assert.response(server, { + url: '/api/v1/sql?' + query, + headers: { host: 'vizzuality.cartodb.com' }, + encoding: 'binary', + method: 'GET' + }, { }, function (err, res) { + assert.deepEqual(res.headers['content-type'], 'application/json; charset=utf-8'); + assert.deepEqual(res.headers['content-disposition'], 'inline'); + assert.equal(res.statusCode, 400, res.statusCode + ': ' + res.body); + var parsedBody = JSON.parse(res.body); + var error = parsedBody.error[0]; + var expectedError = /Attempt to write non-point \(LINESTRING\) geometry to point shapefile/g; + assert.ok(expectedError.test(error), error); + 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', + skipfields: 'skipme', + filename: 'myshape' + }); + assert.response(server, { + url: '/api/v1/sql?' + query, + headers: { host: 'vizzuality.cartodb.com' }, + encoding: 'binary', + method: 'GET' + }, { }, function (err, res) { + assert.equal(res.statusCode, 200, res.body); + var tmpfile = '/tmp/myshape.zip'; + var writeErr = fs.writeFileSync(tmpfile, res.body, 'binary'); + if (writeErr) { + return done(writeErr); + } + var zf = new zipfile.ZipFile(tmpfile); + var buffer = zf.readFileSync('myshape.dbf'); + fs.unlinkSync(tmpfile); + var strings = buffer.toString(); + assert.ok(!/skipme/.exec(strings), "Could not skip 'skipme' field:\n" + strings); + done(); + }); + }); + + it('SHP format, concurrently', function (done) { + var concurrency = 1; + var waiting = concurrency; + function validate (err, res) { + var cd = res.headers['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 writeErr = fs.writeFileSync(tmpfile, res.body, 'binary'); + if (writeErr) { + return done(writeErr); + } + 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( + server, + { + 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 + 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' + }); + assert.response(server, { + url: '/api/v1/sql?' + query, + headers: { host: 'vizzuality.cartodb.com' }, + encoding: 'binary', + method: 'GET' + }, { }, function (err, res) { + assert.equal(res.statusCode, 200, res.body); + var cd = res.headers['content-disposition']; + assert.equal(true, /filename=cartodb-query.zip/gi.test(cd)); + var tmpfile = '/tmp/myshape.zip'; + var writeErr = fs.writeFileSync(tmpfile, res.body, 'binary'); + if (writeErr) { + return done(writeErr); + } + 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 contents of the DBF + fs.unlinkSync(tmpfile); + done(); + }); + }); + + var limit = 1200; + + it('expects ' + limit + ' rows in public table', function (done) { + var filename = 'test_1200'; + + assert.response(server, { + url: '/api/v1/sql?' + querystring.stringify({ + q: 'SELECT * from populated_places_simple_reduced limit ' + limit, + format: 'shp', + filename: filename + }), + headers: { host: 'vizzuality.cartodb.com' }, + encoding: 'binary', + method: 'GET' + }, + { + status: 200 + }, + function (err, res) { + if (err) { + return done(err); + } + + var tmpShpPath = '/tmp/' + filename + '.zip'; + err = fs.writeFileSync(tmpShpPath, res.body, 'binary'); + if (err) { + return done(err); + } + + var zf = new zipfile.ZipFile(tmpShpPath); + zf.names.forEach(function (name) { + var buffer = zf.readFileSync(name); + var tmpDbfPath = '/tmp/' + name; + err = fs.writeFileSync(tmpDbfPath, buffer); + if (err) { + return done(err); + } + }); + shapefile.read('/tmp/' + filename, function (err, collection) { + if (err) { + return done(err); + } + assert.equal(collection.features.length, limit); + done(); + }); + } + ); + }); + + it('SHP zip, wrong path for zip command should return error', function (done) { + global.settings.zipCommand = '/wrong/path'; + var query = querystring.stringify({ + q: 'SELECT st_makepoint(0,0,4326) as the_geom', + format: 'shp', + filename: 'myshape' + }); + assert.response(server, { + url: '/api/v1/sql?' + query, + headers: { host: 'vizzuality.cartodb.com' }, + encoding: 'binary', + method: 'GET' + }, { }, function (err, res) { assert.equal(res.statusCode, 400, res.body); var parsedBody = JSON.parse(res.body); - var respBodyPattern = new RegExp('Error executing zip command, Error: spawn(.*)ENOENT', 'i'); + var respBodyPattern = new RegExp('Error executing zip command, {2}Error: spawn(.*)ENOENT', 'i'); assert.equal(respBodyPattern.test(parsedBody.error[0]), true); done(); }); diff --git a/test/acceptance/export/spatialite-test.js b/test/acceptance/export/spatialite-test.js index dcfa5854..22cf5f6e 100644 --- a/test/acceptance/export/spatialite-test.js +++ b/test/acceptance/export/spatialite-test.js @@ -6,47 +6,45 @@ var server = require('../../../lib/server')(); var assert = require('../../support/assert'); var sqlite = require('sqlite3'); -describe('spatialite query', function(){ - - it('returns a valid sqlite database', function(done){ +describe('spatialite query', function () { + it('returns a valid sqlite database', function (done) { assert.response(server, { url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4%20LIMIT%201&format=spatialite', - headers: {host: 'vizzuality.cartodb.com'}, + headers: { host: 'vizzuality.cartodb.com' }, method: 'GET' - },{ }, function(err, res) { + }, { }, function (err, res) { assert.equal(res.statusCode, 200, res.body); - assert.equal(res.headers["content-type"], "application/x-sqlite3; charset=utf-8"); + assert.equal(res.headers['content-type'], 'application/x-sqlite3; charset=utf-8'); var db = new sqlite.Database(':memory:', res.body); - var qr = db.get("PRAGMA database_list", function(err){ + var qr = db.get('PRAGMA database_list', function (err) { assert.equal(err, null); done(); }); assert.notEqual(qr, undefined); }); - }); - it('different file name', function(done){ + it('different file name', function (done) { assert.response(server, { url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4%20LIMIT%201&format=spatialite&filename=manolo', - headers: {host: 'vizzuality.cartodb.com'}, + headers: { host: 'vizzuality.cartodb.com' }, method: 'GET' - },{ }, function(err, res) { - assert.equal(res.headers["content-type"], "application/x-sqlite3; charset=utf-8"); - assert.notEqual(res.headers["content-disposition"].indexOf("manolo.sqlite"), -1); + }, { }, function (err, res) { + assert.equal(res.headers['content-type'], 'application/x-sqlite3; charset=utf-8'); + assert.notEqual(res.headers['content-disposition'].indexOf('manolo.sqlite'), -1); done(); }); }); - it('gets database schema', function(done){ + it('gets database schema', function (done) { assert.response(server, { url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4%20LIMIT%201&format=spatialite', - headers: {host: 'vizzuality.cartodb.com'}, + headers: { host: 'vizzuality.cartodb.com' }, method: 'GET' - },{ }, function(err, res) { + }, { }, function (err, res) { var db = new sqlite.Database(':memory:', res.body); var schemaQuery = "SELECT name, sql FROM sqlite_master WHERE type='table' ORDER BY name"; - var qr = db.get(schemaQuery, function(err){ + var qr = db.get(schemaQuery, function (err) { assert.equal(err, null); done(); }); diff --git a/test/acceptance/export/svg-test.js b/test/acceptance/export/svg-test.js index bc266442..ad5ac7d7 100644 --- a/test/acceptance/export/svg-test.js +++ b/test/acceptance/export/svg-test.js @@ -6,175 +6,174 @@ var server = require('../../../lib/server')(); var assert = require('../../support/assert'); var querystring = require('querystring'); -describe('export.svg', function() { - -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" - }); - assert.response(server, { - url: '/api/v1/sql?' + query, - headers: {host: 'vizzuality.cartodb.com'}, - method: 'GET' - },{ }, function(err, res){ - assert.equal(res.statusCode, 200, res.body); - var cd = res.headers['content-disposition']; - assert.ok(/filename=cartodb-query.svg/gi.test(cd), cd); - assert.equal(res.headers['content-type'], 'image/svg+xml; charset=utf-8'); - assert.ok( res.body.indexOf('') > 0, res.body ); - // TODO: test viewBox - 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" - }); - assert.response(server, { - url: '/api/v1/sql', - data: query, - headers: {host: 'vizzuality.cartodb.com', 'Content-Type': 'application/x-www-form-urlencoded' }, - method: 'POST' - },{ }, function(err, res){ - assert.equal(res.statusCode, 200, res.body); - var cd = res.headers['content-disposition']; - assert.equal(true, /^attachment/.test(cd), 'SVG is not disposed as attachment: ' + cd); - assert.ok(/filename=cartodb-query.svg/gi.test(cd), cd); - assert.equal(res.headers['content-type'], 'image/svg+xml; charset=utf-8'); - assert.ok( res.body.indexOf('') > 0, res.body ); - // TODO: test viewBox - 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", - filename: 'mysvg' - }); - assert.response(server, { - url: '/api/v1/sql?' + query, - headers: {host: 'vizzuality.cartodb.com'}, - method: 'GET' - },{ }, function(err, res){ - assert.equal(res.statusCode, 200, res.body); - var cd = res.headers['content-disposition']; - assert.ok(/filename=mysvg.svg/gi.test(cd), cd); - assert.equal(res.headers['content-type'], 'image/svg+xml; charset=utf-8'); - assert.ok( res.body.indexOf('') > 0, res.body ); - // TODO: test viewBox - 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" - }); - assert.response(server, { - url: '/api/v1/sql?' + query, - headers: {host: 'vizzuality.cartodb.com'}, - method: 'GET' - },{ }, function(err, res){ - assert.equal(res.statusCode, 200, res.body); - var cd = res.headers['content-disposition']; - assert.ok(/filename=cartodb-query.svg/gi.test(cd), cd); - assert.equal(res.headers['content-type'], 'image/svg+xml; charset=utf-8'); - assert.ok( res.body.indexOf('cx="0" cy="0"') > 0, res.body ); - // TODO: test viewBox - // TODO: test radius - 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", - dp: 2 - }; - assert.response(server, { - url: '/api/v1/sql?' + querystring.stringify(queryobj), - headers: {host: 'vizzuality.cartodb.com'}, - method: 'GET' - },{ }, function(err, res){ - assert.equal(res.statusCode, 200, res.body); - var cd = res.headers['content-disposition']; - assert.ok(/filename=cartodb-query.svg/gi.test(cd), cd); - assert.equal(res.headers['content-type'], 'image/svg+xml; charset=utf-8'); - assert.ok( res.body.indexOf('') > 0, res.body ); - // TODO: test viewBox - - queryobj.dp = 3; +describe('export.svg', function () { + 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' + }); assert.response(server, { - url: '/api/v1/sql?' + querystring.stringify(queryobj), - headers: {host: 'vizzuality.cartodb.com'}, - method: 'GET' - },{}, function(err, res) { - assert.equal(res.statusCode, 200, res.body); - var cd = res.headers['content-disposition']; - assert.equal(true, /^attachment/.test(cd), 'SVG is not disposed as attachment: ' + cd); - assert.ok(/filename=cartodb-query.svg/gi.test(cd), cd); - assert.equal(res.headers['content-type'], 'image/svg+xml; charset=utf-8'); - assert.ok( res.body.indexOf('') > 0, res.body ); - // TODO: test viewBox - done(); + url: '/api/v1/sql?' + query, + headers: { host: 'vizzuality.cartodb.com' }, + method: 'GET' + }, { }, function (err, res) { + assert.equal(res.statusCode, 200, res.body); + var cd = res.headers['content-disposition']; + assert.ok(/filename=cartodb-query.svg/gi.test(cd), cd); + assert.equal(res.headers['content-type'], 'image/svg+xml; charset=utf-8'); + assert.ok(res.body.indexOf('') > 0, res.body); + // TODO: test viewBox + done(); }); }); -}); -// Test adding "the_geom" to skipfields -// See http://github.com/Vizzuality/CartoDB-SQL-API/issues/73 -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", - skipfields: "the_geom" - }); - assert.response(server, { - url: '/api/v1/sql?' + query, - headers: {host: 'vizzuality.cartodb.com'}, - method: 'GET' - },{ }, function(err, res){ - assert.equal(res.statusCode, 400, res.statusCode + ': ' + res.body); - assert.deepEqual(res.headers['content-type'], 'application/json; charset=utf-8'); - assert.deepEqual(res.headers['content-disposition'], 'inline'); - assert.deepEqual(JSON.parse(res.body), { - error:['column "the_geom" does not exist'] + 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' }); - 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" - }); - assert.response(server, { - url: '/api/v1/sql?' + query, - headers: {host: 'vizzuality.cartodb.com'}, - method: 'GET' - },{ }, function(err, res){ - assert.equal(res.statusCode, 400, res.statusCode + ': ' + res.body); - assert.deepEqual(JSON.parse(res.body), { - error:['column "the_geom" does not exist'] + assert.response(server, { + url: '/api/v1/sql', + data: query, + headers: { host: 'vizzuality.cartodb.com', 'Content-Type': 'application/x-www-form-urlencoded' }, + method: 'POST' + }, { }, function (err, res) { + assert.equal(res.statusCode, 200, res.body); + var cd = res.headers['content-disposition']; + assert.equal(true, /^attachment/.test(cd), 'SVG is not disposed as attachment: ' + cd); + assert.ok(/filename=cartodb-query.svg/gi.test(cd), cd); + assert.equal(res.headers['content-type'], 'image/svg+xml; charset=utf-8'); + assert.ok(res.body.indexOf('') > 0, res.body); + // TODO: test viewBox + done(); }); - done(); }); -}); - it('should close on error and error must be the only key in the body', 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', + filename: 'mysvg' + }); + assert.response(server, { + url: '/api/v1/sql?' + query, + headers: { host: 'vizzuality.cartodb.com' }, + method: 'GET' + }, { }, function (err, res) { + assert.equal(res.statusCode, 200, res.body); + var cd = res.headers['content-disposition']; + assert.ok(/filename=mysvg.svg/gi.test(cd), cd); + assert.equal(res.headers['content-type'], 'image/svg+xml; charset=utf-8'); + assert.ok(res.body.indexOf('') > 0, res.body); + // TODO: test viewBox + 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' + }); + assert.response(server, { + url: '/api/v1/sql?' + query, + headers: { host: 'vizzuality.cartodb.com' }, + method: 'GET' + }, { }, function (err, res) { + assert.equal(res.statusCode, 200, res.body); + var cd = res.headers['content-disposition']; + assert.ok(/filename=cartodb-query.svg/gi.test(cd), cd); + assert.equal(res.headers['content-type'], 'image/svg+xml; charset=utf-8'); + assert.ok(res.body.indexOf('cx="0" cy="0"') > 0, res.body); + // TODO: test viewBox + // TODO: test radius + 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', + dp: 2 + }; + assert.response(server, { + url: '/api/v1/sql?' + querystring.stringify(queryobj), + headers: { host: 'vizzuality.cartodb.com' }, + method: 'GET' + }, { }, function (err, res) { + assert.equal(res.statusCode, 200, res.body); + var cd = res.headers['content-disposition']; + assert.ok(/filename=cartodb-query.svg/gi.test(cd), cd); + assert.equal(res.headers['content-type'], 'image/svg+xml; charset=utf-8'); + assert.ok(res.body.indexOf('') > 0, res.body); + // TODO: test viewBox + + queryobj.dp = 3; + assert.response(server, { + url: '/api/v1/sql?' + querystring.stringify(queryobj), + headers: { host: 'vizzuality.cartodb.com' }, + method: 'GET' + }, {}, function (err, res) { + assert.equal(res.statusCode, 200, res.body); + var cd = res.headers['content-disposition']; + assert.equal(true, /^attachment/.test(cd), 'SVG is not disposed as attachment: ' + cd); + assert.ok(/filename=cartodb-query.svg/gi.test(cd), cd); + assert.equal(res.headers['content-type'], 'image/svg+xml; charset=utf-8'); + assert.ok(res.body.indexOf('') > 0, res.body); + // TODO: test viewBox + done(); + }); + }); + }); + + // Test adding "the_geom" to skipfields + // See http://github.com/Vizzuality/CartoDB-SQL-API/issues/73 + 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', + skipfields: 'the_geom' + }); + assert.response(server, { + url: '/api/v1/sql?' + query, + headers: { host: 'vizzuality.cartodb.com' }, + method: 'GET' + }, { }, function (err, res) { + assert.equal(res.statusCode, 400, res.statusCode + ': ' + res.body); + assert.deepEqual(res.headers['content-type'], 'application/json; charset=utf-8'); + assert.deepEqual(res.headers['content-disposition'], 'inline'); + assert.deepEqual(JSON.parse(res.body), { + error: ['column "the_geom" does not exist'] + }); + 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' + }); + assert.response(server, { + url: '/api/v1/sql?' + query, + headers: { host: 'vizzuality.cartodb.com' }, + method: 'GET' + }, { }, function (err, res) { + assert.equal(res.statusCode, 400, res.statusCode + ': ' + res.body); + assert.deepEqual(JSON.parse(res.body), { + error: ['column "the_geom" does not exist'] + }); + done(); + }); + }); + + it('should close on error and error must be the only key in the body', function (done) { assert.response( server, { - url: "/api/v1/sql?" + querystring.stringify({ - q: "SELECT the_geom, 100/(cartodb_id - 3) cdb_ratio FROM untitle_table_4", + url: '/api/v1/sql?' + querystring.stringify({ + q: 'SELECT the_geom, 100/(cartodb_id - 3) cdb_ratio FROM untitle_table_4', format: 'svg' }), headers: { @@ -185,15 +184,12 @@ it('SVG format with missing "the_geom" field', function(done){ { status: 400 }, - function(err, res) { + function (err, res) { var parsedBody = JSON.parse(res.body); assert.deepEqual(Object.keys(parsedBody), ['error']); - assert.deepEqual(parsedBody.error, ["division by zero"]); + assert.deepEqual(parsedBody.error, ['division by zero']); done(); } ); }); - - - }); diff --git a/test/acceptance/export/timeout-test.js b/test/acceptance/export/timeout-test.js index 3590da1e..827ab636 100644 --- a/test/acceptance/export/timeout-test.js +++ b/test/acceptance/export/timeout-test.js @@ -105,7 +105,7 @@ describe('timeout', function () { assert.deepEqual(res, { error: [ 'You are over platform\'s limits: SQL query timeout error.' + - ' Refactor your query before running again or contact CARTO support for more details.', + ' Refactor your query before running again or contact CARTO support for more details.' ], context: 'limit', detail: 'datasource' @@ -131,7 +131,7 @@ describe('timeout', function () { desc: 'CSV', format: 'csv', contentType: 'application/x-www-form-urlencoded', - parser: querystring.stringify, + parser: querystring.stringify // only: true, // skip: true }, @@ -188,7 +188,7 @@ describe('timeout', function () { assert.deepEqual(res, { error: [ 'You are over platform\'s limits: SQL query timeout error.' + - ' Refactor your query before running again or contact CARTO support for more details.', + ' Refactor your query before running again or contact CARTO support for more details.' ], context: 'limit', detail: 'datasource' diff --git a/test/acceptance/export/topojson-test.js b/test/acceptance/export/topojson-test.js index 14053a7e..6b91245e 100644 --- a/test/acceptance/export/topojson-test.js +++ b/test/acceptance/export/topojson-test.js @@ -7,11 +7,10 @@ var assert = require('../../support/assert'); var querystring = require('querystring'); var _ = require('underscore'); -describe('export.topojson', function() { - +describe('export.topojson', function () { // TOPOJSON tests - function getRequest(query, extraParams) { + function getRequest (query, extraParams) { var params = { q: query, format: 'topojson' @@ -21,176 +20,176 @@ describe('export.topojson', function() { return { url: '/api/v1/sql?' + querystring.stringify(params), - headers: {host: 'vizzuality.cartodb.com'}, + headers: { host: 'vizzuality.cartodb.com' }, method: 'GET' }; } -it('GET two polygons sharing an edge as topojson', function(done){ - assert.response(server, - getRequest( - "SELECT 1 as gid, 'U' as name, 'POLYGON((-5 0,5 0,0 5,-5 0))'::geometry as the_geom " + - " UNION ALL " + + it('GET two polygons sharing an edge as topojson', function (done) { + assert.response(server, + getRequest( + "SELECT 1 as gid, 'U' as name, 'POLYGON((-5 0,5 0,0 5,-5 0))'::geometry as the_geom " + + ' UNION ALL ' + "SELECT 2, 'D', 'POLYGON((0 -5,0 5,-5 0,0 -5))'::geometry as the_geom " - ), - { - status: 200 - }, - function(err, res) { - var cd = res.headers['content-disposition']; - assert.equal(true, /^attachment/.test(cd), 'TOPOJSON is not disposed as attachment: ' + cd); - assert.equal(true, /filename=cartodb-query.topojson/gi.test(cd)); - var topojson = JSON.parse(res.body); - assert.equal(topojson.type, 'Topology'); + ), + { + status: 200 + }, + function (err, res) { + var cd = res.headers['content-disposition']; + assert.equal(true, /^attachment/.test(cd), 'TOPOJSON is not disposed as attachment: ' + cd); + assert.equal(true, /filename=cartodb-query.topojson/gi.test(cd)); + var topojson = JSON.parse(res.body); + assert.equal(topojson.type, 'Topology'); - // Check transform - assert.ok(topojson.hasOwnProperty('transform')); - var trans = topojson.transform; - assert.equal(_.keys(trans).length, 2); // only scale and translate - assert.equal(trans.scale.length, 2); // scalex, scaley - assert.equal(Math.round(trans.scale[0]*1e6), 1000); - assert.equal(Math.round(trans.scale[1]*1e6), 1000); - assert.equal(trans.translate.length, 2); // translatex, translatey - assert.equal(trans.translate[0], -5); - assert.equal(trans.translate[1], -5); + // Check transform + assert.ok(topojson.hasOwnProperty('transform')); + var trans = topojson.transform; + assert.equal(_.keys(trans).length, 2); // only scale and translate + assert.equal(trans.scale.length, 2); // scalex, scaley + assert.equal(Math.round(trans.scale[0] * 1e6), 1000); + assert.equal(Math.round(trans.scale[1] * 1e6), 1000); + assert.equal(trans.translate.length, 2); // translatex, translatey + assert.equal(trans.translate[0], -5); + assert.equal(trans.translate[1], -5); - // Check objects - assert.ok(topojson.hasOwnProperty('objects')); - assert.equal(_.keys(topojson.objects).length, 2); + // Check objects + assert.ok(topojson.hasOwnProperty('objects')); + assert.equal(_.keys(topojson.objects).length, 2); - var obj = topojson.objects[0]; - //console.dir(obj); - // Expected: - // { type: 'Polygon', - // arcs: [ [ 0, 1 ] ], - // properties: { gid: 1, nam: 'U' } } - assert.equal(_.keys(obj).length, 3); // type, arcs, properties - assert.equal(obj.type, 'Polygon'); - assert.equal(obj.arcs.length, 1); /* only shell, no holes */ - var shell = obj.arcs[0]; - assert.equal(shell.length, 2); /* one shared arc, one non-shared */ - assert.equal(shell[0], 0); /* shared arc */ - 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'); + var obj = topojson.objects[0]; + // console.dir(obj); + // Expected: + // { type: 'Polygon', + // arcs: [ [ 0, 1 ] ], + // properties: { gid: 1, nam: 'U' } } + assert.equal(_.keys(obj).length, 3); // type, arcs, properties + assert.equal(obj.type, 'Polygon'); + assert.equal(obj.arcs.length, 1); /* only shell, no holes */ + var shell = obj.arcs[0]; + assert.equal(shell.length, 2); /* one shared arc, one non-shared */ + assert.equal(shell[0], 0); /* shared arc */ + 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'); - obj = topojson.objects[1]; - //console.dir(obj); - // Expected: - // { type: 'Polygon', - // arcs: [ [ 0, 2 ] ], - // properties: { gid: 2, nam: 'D' } } - assert.equal(_.keys(obj).length, 3); // type, arcs, properties - assert.equal(obj.type, 'Polygon'); - assert.equal(obj.arcs.length, 1); /* only shell, no holes */ - shell = obj.arcs[0]; - assert.equal(shell.length, 2); /* one shared arc, one non-shared */ - assert.equal(shell[0], 0); /* shared arc */ - 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'); + obj = topojson.objects[1]; + // console.dir(obj); + // Expected: + // { type: 'Polygon', + // arcs: [ [ 0, 2 ] ], + // properties: { gid: 2, nam: 'D' } } + assert.equal(_.keys(obj).length, 3); // type, arcs, properties + assert.equal(obj.type, 'Polygon'); + assert.equal(obj.arcs.length, 1); /* only shell, no holes */ + shell = obj.arcs[0]; + assert.equal(shell.length, 2); /* one shared arc, one non-shared */ + assert.equal(shell[0], 0); /* shared arc */ + 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'); - // Check arcs - assert.ok(topojson.hasOwnProperty('arcs')); - assert.equal(topojson.arcs.length, 3); // one shared, two non-shared - var arc = topojson.arcs[0]; // shared arc - assert.equal(arc.length, 2); // shared arc has two vertices - var p = arc[0]; - assert.equal(Math.round(p[0]*trans.scale[0]), 0); - assert.equal(Math.round(p[1]*trans.scale[1]), 5); - p = arc[1]; - assert.equal(Math.round(p[0]*trans.scale[0]), 5); - assert.equal(Math.round(p[1]*trans.scale[1]), 5); - arc = topojson.arcs[1]; // non shared arc - assert.equal(arc.length, 3); // non shared arcs have three vertices - p = arc[0]; - assert.equal(Math.round(p[0]*trans.scale[0]), 5); - assert.equal(Math.round(p[1]*trans.scale[1]), 10); - p = arc[1]; - assert.equal(Math.round(p[0]*trans.scale[0]), 5); - assert.equal(Math.round(p[1]*trans.scale[1]), -5); - p = arc[2]; - assert.equal(Math.round(p[0]*trans.scale[0]), -10); - assert.equal(Math.round(p[1]*trans.scale[1]), 0); - arc = topojson.arcs[2]; // non shared arc - assert.equal(arc.length, 3); // non shared arcs have three vertices - p = arc[0]; - assert.equal(Math.round(p[0]*trans.scale[0]), 5); - assert.equal(Math.round(p[1]*trans.scale[1]), 10); - p = arc[1]; - assert.equal(Math.round(p[0]*trans.scale[0]), 0); - assert.equal(Math.round(p[1]*trans.scale[1]), -10); - p = arc[2]; - assert.equal(Math.round(p[0]*trans.scale[0]), -5); - assert.equal(Math.round(p[1]*trans.scale[1]), 5); + // Check arcs + assert.ok(topojson.hasOwnProperty('arcs')); + assert.equal(topojson.arcs.length, 3); // one shared, two non-shared + var arc = topojson.arcs[0]; // shared arc + assert.equal(arc.length, 2); // shared arc has two vertices + var p = arc[0]; + assert.equal(Math.round(p[0] * trans.scale[0]), 0); + assert.equal(Math.round(p[1] * trans.scale[1]), 5); + p = arc[1]; + assert.equal(Math.round(p[0] * trans.scale[0]), 5); + assert.equal(Math.round(p[1] * trans.scale[1]), 5); + arc = topojson.arcs[1]; // non shared arc + assert.equal(arc.length, 3); // non shared arcs have three vertices + p = arc[0]; + assert.equal(Math.round(p[0] * trans.scale[0]), 5); + assert.equal(Math.round(p[1] * trans.scale[1]), 10); + p = arc[1]; + assert.equal(Math.round(p[0] * trans.scale[0]), 5); + assert.equal(Math.round(p[1] * trans.scale[1]), -5); + p = arc[2]; + assert.equal(Math.round(p[0] * trans.scale[0]), -10); + assert.equal(Math.round(p[1] * trans.scale[1]), 0); + arc = topojson.arcs[2]; // non shared arc + assert.equal(arc.length, 3); // non shared arcs have three vertices + p = arc[0]; + assert.equal(Math.round(p[0] * trans.scale[0]), 5); + assert.equal(Math.round(p[1] * trans.scale[1]), 10); + p = arc[1]; + assert.equal(Math.round(p[0] * trans.scale[0]), 0); + assert.equal(Math.round(p[1] * trans.scale[1]), -10); + p = arc[2]; + assert.equal(Math.round(p[0] * trans.scale[0]), -5); + assert.equal(Math.round(p[1] * trans.scale[1]), 5); - done(); + done(); + }); }); -}); -it('null geometries', function(done){ - assert.response(server, getRequest( + it('null geometries', function (done) { + assert.response(server, getRequest( "SELECT 1 as gid, 'U' as name, 'POLYGON((-5 0,5 0,0 5,-5 0))'::geometry as the_geom " + - " UNION ALL " + + ' UNION ALL ' + "SELECT 2, 'D', null::geometry as the_geom " ), { status: 200 }, - function(err, res) { - var cd = res.headers['content-disposition']; - assert.equal(true, /^attachment/.test(cd), 'TOPOJSON is not disposed as attachment: ' + cd); - assert.equal(true, /filename=cartodb-query.topojson/gi.test(cd)); - var topojson = JSON.parse(res.body); - assert.equal(topojson.type, 'Topology'); + function (err, res) { + var cd = res.headers['content-disposition']; + assert.equal(true, /^attachment/.test(cd), 'TOPOJSON is not disposed as attachment: ' + cd); + assert.equal(true, /filename=cartodb-query.topojson/gi.test(cd)); + var topojson = JSON.parse(res.body); + assert.equal(topojson.type, 'Topology'); - // Check transform - assert.ok(topojson.hasOwnProperty('transform')); - var trans = topojson.transform; - assert.equal(_.keys(trans).length, 2); // only scale and translate - assert.equal(trans.scale.length, 2); // scalex, scaley - assert.equal(Math.round(trans.scale[0]*1e6), 1000); - assert.equal(Math.round(trans.scale[1]*1e6), 500); - assert.equal(trans.translate.length, 2); // translatex, translatey - assert.equal(trans.translate[0], -5); - assert.equal(trans.translate[1], 0); + // Check transform + assert.ok(topojson.hasOwnProperty('transform')); + var trans = topojson.transform; + assert.equal(_.keys(trans).length, 2); // only scale and translate + assert.equal(trans.scale.length, 2); // scalex, scaley + assert.equal(Math.round(trans.scale[0] * 1e6), 1000); + assert.equal(Math.round(trans.scale[1] * 1e6), 500); + assert.equal(trans.translate.length, 2); // translatex, translatey + assert.equal(trans.translate[0], -5); + assert.equal(trans.translate[1], 0); - // Check objects - assert.ok(topojson.hasOwnProperty('objects')); - assert.equal(_.keys(topojson.objects).length, 1); + // Check objects + assert.ok(topojson.hasOwnProperty('objects')); + assert.equal(_.keys(topojson.objects).length, 1); - var obj = topojson.objects[0]; - //console.dir(obj); - // Expected: - // { type: 'Polygon', - // arcs: [ [ 0, 1 ] ], - // properties: { gid: 1, nam: 'U' } } - assert.equal(_.keys(obj).length, 3); // type, arcs, properties - assert.equal(obj.type, 'Polygon'); - assert.equal(obj.arcs.length, 1); /* only shell, no holes */ - var shell = obj.arcs[0]; - assert.equal(shell.length, 1); /* one non shared arc */ - 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'); + var obj = topojson.objects[0]; + // console.dir(obj); + // Expected: + // { type: 'Polygon', + // arcs: [ [ 0, 1 ] ], + // properties: { gid: 1, nam: 'U' } } + assert.equal(_.keys(obj).length, 3); // type, arcs, properties + assert.equal(obj.type, 'Polygon'); + assert.equal(obj.arcs.length, 1); /* only shell, no holes */ + var shell = obj.arcs[0]; + assert.equal(shell.length, 1); /* one non shared arc */ + 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'); - // Check arcs - assert.ok(topojson.hasOwnProperty('arcs')); - assert.equal(topojson.arcs.length, 1); - var arc = topojson.arcs[0]; - assert.deepEqual(arc, [ [ 0, 0 ], [ 4999, 9999 ], [ 5000, -9999 ], [ -9999, 0 ] ]); + // Check arcs + assert.ok(topojson.hasOwnProperty('arcs')); + assert.equal(topojson.arcs.length, 1); + var arc = topojson.arcs[0]; + assert.deepEqual(arc, [[0, 0], [4999, 9999], [5000, -9999], [-9999, 0]]); - done(); + done(); + }); }); -}); - it('skipped fields are not returned', function(done) { + it('skipped fields are not returned', function (done) { assert.response(server, getRequest( "SELECT 1 as gid, 'U' as name, 'POLYGON((-5 0,5 0,0 5,-5 0))'::geometry as the_geom", @@ -201,7 +200,7 @@ it('null geometries', function(done){ { status: 200 }, - function(err, res) { + function (err, res) { var parsedBody = JSON.parse(res.body); assert.equal(parsedBody.objects[0].properties.gid, 1, 'gid was expected property'); assert.ok(!parsedBody.objects[0].properties.name); @@ -210,7 +209,7 @@ it('null geometries', function(done){ ); }); - it('jsonp callback is invoked', function(done){ + it('jsonp callback is invoked', function (done) { assert.response( server, getRequest( @@ -222,11 +221,11 @@ it('null geometries', function(done){ { status: 200 }, - function(err, res) { + function (err, res) { assert.equal(res.statusCode, 200, res.statusCode + ': ' + res.body); var didRunJsonCallback = false; // jshint ignore:start - function foo_jsonp(body) { + function foo_jsonp (body) { didRunJsonCallback = true; } eval(res.body); @@ -237,13 +236,12 @@ it('null geometries', function(done){ ); }); - - it('should close on error and error must be the only key in the body', function(done) { + it('should close on error and error must be the only key in the body', function (done) { assert.response( server, { - url: "/api/v1/sql?" + querystring.stringify({ - q: "SELECT the_geom, 100/(cartodb_id - 3) cdb_ratio FROM untitle_table_4", + url: '/api/v1/sql?' + querystring.stringify({ + q: 'SELECT the_geom, 100/(cartodb_id - 3) cdb_ratio FROM untitle_table_4', format: 'topojson' }), headers: { @@ -254,14 +252,12 @@ it('null geometries', function(done){ { status: 400 }, - function(err, res) { + function (err, res) { var parsedBody = JSON.parse(res.body); assert.deepEqual(Object.keys(parsedBody), ['error']); - assert.deepEqual(parsedBody.error, ["division by zero"]); + assert.deepEqual(parsedBody.error, ['division by zero']); done(); } ); }); - - }); diff --git a/test/acceptance/frontend_abort-test.js b/test/acceptance/frontend_abort-test.js index fe4f73a9..7b554438 100644 --- a/test/acceptance/frontend_abort-test.js +++ b/test/acceptance/frontend_abort-test.js @@ -8,72 +8,70 @@ var net = require('net'); var sql_server_data_handler; var sql_server_port = 5556; -var sql_server = net.createServer(function(c) { - c.on('data', function(d) { - console.log("SQL Server got data: " + d); - if ( sql_server_data_handler ) { - console.log("Sending data to sql_server_data_handler"); - sql_server_data_handler(null, d); - } - c.destroy(); - }); +var sql_server = net.createServer(function (c) { + c.on('data', function (d) { + console.log('SQL Server got data: ' + d); + if (sql_server_data_handler) { + console.log('Sending data to sql_server_data_handler'); + sql_server_data_handler(null, d); + } + c.destroy(); + }); }); -describe('frontend abort', function() { +describe('frontend abort', function () { + before(function (done) { + sql_server.listen(sql_server_port, done); + }); -before(function(done){ - sql_server.listen(sql_server_port, done); -}); - -// See https://github.com/CartoDB/CartoDB-SQL-API/issues/129 -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 server = require('../../lib/server')(); - var timeout; - step( - function sendQuery() { - assert.response(server, { - url: '/api/v1/sql?q=SELECT+1', - method: 'GET', - timeout: 1, - headers: {host: 'vizzuality.localhost' } - },{}, this); - }, - function checkResponse(err/*, res*/) { - assert(err); // expect timeout - assert.ok((''+err).match(/socket/), err); - sql_server_data_handler = this; - var next = this; - // If a call does not arrive to the sql server within - // the given timeout we're confident it means the request - // was successfully aborted - timeout = setTimeout(function() { next(null); }, 500); - }, - function checkSqlServerData(err, data) { - clearTimeout(timeout); - assert.ok(!data, "SQL Server was contacted no matter client abort"); - // TODO: intercept logs ? - return null; - }, - function finish(err) { - global.settings.db_host = db_host_backup; - global.settings.db_port = db_port_backup; - done(err); - } - ); -}); - -after(function(done) { - try { - sql_server.close(done); - } catch (er) { - console.log(er); - done(); // error expected as server is probably closed already - } -}); + // See https://github.com/CartoDB/CartoDB-SQL-API/issues/129 + 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 server = require('../../lib/server')(); + var timeout; + step( + function sendQuery () { + assert.response(server, { + url: '/api/v1/sql?q=SELECT+1', + method: 'GET', + timeout: 1, + headers: { host: 'vizzuality.localhost' } + }, {}, this); + }, + function checkResponse (err/*, res */) { + assert(err); // expect timeout + assert.ok(('' + err).match(/socket/), err); + sql_server_data_handler = this; + var next = this; + // If a call does not arrive to the sql server within + // the given timeout we're confident it means the request + // was successfully aborted + timeout = setTimeout(function () { next(null); }, 500); + }, + function checkSqlServerData (err, data) { + clearTimeout(timeout); + assert.ok(!data, 'SQL Server was contacted no matter client abort'); + // TODO: intercept logs ? + return null; + }, + function finish (err) { + global.settings.db_host = db_host_backup; + global.settings.db_port = db_port_backup; + done(err); + } + ); + }); + after(function (done) { + try { + sql_server.close(done); + } catch (er) { + console.log(er); + done(); // error expected as server is probably closed already + } + }); }); diff --git a/test/acceptance/handle-query-test.js b/test/acceptance/handle-query-test.js index 6adc4579..c2474c28 100644 --- a/test/acceptance/handle-query-test.js +++ b/test/acceptance/handle-query-test.js @@ -8,13 +8,13 @@ const qs = require('querystring'); const BatchTestClient = require('../support/batch-test-client'); const JobStatus = require('../../lib/batch/job-status'); -const QUERY = `SELECT 14 as foo`; +const QUERY = 'SELECT 14 as foo'; const API_KEY = 1234; -describe('Handle query middleware', function() { - describe('regular queries endpoint', function() { +describe('Handle query middleware', function () { + describe('regular queries endpoint', function () { ['GET', 'POST'].forEach(method => { - it(`${method} without query fails`, function(done) { + it(`${method} without query fails`, function (done) { assert.response(server, { method, @@ -26,18 +26,18 @@ describe('Handle query middleware', function() { } }, { statusCode: 400 }, - function(err, res) { + function (err, res) { assert.ok(!err); const response = JSON.parse(res.body); - assert.deepEqual(response, { error: ["You must indicate a sql query"] }); + assert.deepEqual(response, { error: ['You must indicate a sql query'] }); return done(); } ); }); - it(`${method} query`, function(done) { + it(`${method} query`, function (done) { assert.response(server, { method, @@ -50,7 +50,7 @@ describe('Handle query middleware', function() { } }, { statusCode: 200 }, - function(err, res) { + function (err, res) { assert.ok(!err); const response = JSON.parse(res.body); @@ -64,18 +64,18 @@ describe('Handle query middleware', function() { }); }); - describe('batch api queries', function() { - before(function() { + describe('batch api queries', function () { + before(function () { this.batchTestClient = new BatchTestClient(); }); - after(function(done) { + after(function (done) { this.batchTestClient.drain(done); }); it('one query', function (done) { var payload = { query: QUERY }; - this.batchTestClient.createJob(payload, function(err, jobResult) { + this.batchTestClient.createJob(payload, function (err, jobResult) { assert.ok(!err); jobResult.getStatus(function (err, job) { @@ -91,7 +91,7 @@ describe('Handle query middleware', function() { it('multiquery job with two queries', function (done) { var payload = { query: [QUERY, QUERY] }; - this.batchTestClient.createJob(payload, function(err, jobResult) { + this.batchTestClient.createJob(payload, function (err, jobResult) { assert.ok(!err); jobResult.getStatus(function (err, job) { @@ -100,7 +100,7 @@ describe('Handle query middleware', function() { assert.equal(job.status, JobStatus.DONE); assert.deepEqual(job.query, [ { query: QUERY, status: JobStatus.DONE }, - { query: QUERY, status: JobStatus.DONE }, + { query: QUERY, status: JobStatus.DONE } ]); return done(); diff --git a/test/acceptance/health-check-test.js b/test/acceptance/health-check-test.js index be06eca7..f09b1315 100644 --- a/test/acceptance/health-check-test.js +++ b/test/acceptance/health-check-test.js @@ -6,13 +6,12 @@ require('../support/assert'); var assert = require('assert'); var server = require('../../lib/server')(); -describe('health checks', function() { - - beforeEach(function(done) { +describe('health checks', function () { + beforeEach(function (done) { global.settings.health = { enabled: true - //username: 'vizzuality', - //query: 'select 1::text' + // username: 'vizzuality', + // query: 'select 1::text' }; done(); }); @@ -25,7 +24,7 @@ describe('health checks', function() { } }; - it('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(server, @@ -33,7 +32,7 @@ describe('health checks', function() { { status: 200 }, - function(err, res) { + function (err, res) { assert.ok(!err); var parsed = JSON.parse(res.body); @@ -46,13 +45,13 @@ describe('health checks', function() { ); }); - it('returns 200 and ok=true with enabled configuration', function(done) { + it('returns 200 and ok=true with enabled configuration', function (done) { assert.response(server, healthCheckRequest, { status: 200 }, - function(err, res) { + function (err, res) { assert.ok(!err); var parsed = JSON.parse(res.body); @@ -64,5 +63,4 @@ describe('health checks', function() { } ); }); - }); diff --git a/test/acceptance/last-modified-header-test.js b/test/acceptance/last-modified-header-test.js index 6c389d66..8c82fa34 100644 --- a/test/acceptance/last-modified-header-test.js +++ b/test/acceptance/last-modified-header-test.js @@ -7,8 +7,7 @@ var assert = require('../support/assert'); var qs = require('querystring'); var MockDate = require('mockdate'); -describe('last modified header', function() { - +describe('last modified header', function () { var scenarios = [ { tables: ['untitle_table_4'], @@ -32,10 +31,10 @@ describe('last modified header', function() { } ]; - scenarios.forEach(function(scenario) { - it(scenario.desc, function(done) { + scenarios.forEach(function (scenario) { + it(scenario.desc, function (done) { var query = qs.stringify({ - q: scenario.tables.map(function(table) { + q: scenario.tables.map(function (table) { return 'select cartodb_id from ' + table; }).join(' UNION ALL '), api_key: 1234 @@ -51,7 +50,7 @@ describe('last modified header', function() { { statusCode: 200 }, - function(err, res) { + function (err, res) { assert.equal(res.headers['last-modified'], scenario.expectedLastModified); done(); } @@ -59,13 +58,13 @@ describe('last modified header', function() { }); }); - it('should use Date.now() for tables not present in cdb_tablemetadata', function(done) { + it('should use Date.now() for tables not present in cdb_tablemetadata', function (done) { var query = qs.stringify({ q: 'select cartodb_id from populated_places_simple_reduced limit 1', api_key: 1234 }); var fixedDateNow = Date.now(); - MockDate.set(fixedDateNow); + MockDate.set(fixedDateNow); assert.response(server, { url: '/api/v1/sql?' + query, @@ -77,7 +76,7 @@ describe('last modified header', function() { { statusCode: 200 }, - function(err, res) { + function (err, res) { MockDate.reset(); assert.equal(res.headers['last-modified'], new Date(fixedDateNow).toUTCString()); done(); @@ -85,13 +84,13 @@ describe('last modified header', function() { ); }); - it('should use Date.now() for functions or results with no table associated', function(done) { + it('should use Date.now() for functions or results with no table associated', function (done) { var query = qs.stringify({ q: 'select 1', api_key: 1234 }); var fixedDateNow = Date.now(); - MockDate.set(fixedDateNow); + MockDate.set(fixedDateNow); assert.response(server, { url: '/api/v1/sql?' + query, @@ -103,12 +102,11 @@ describe('last modified header', function() { { statusCode: 200 }, - function(err, res) { + function (err, res) { MockDate.reset(); assert.equal(res.headers['last-modified'], new Date(fixedDateNow).toUTCString()); done(); } ); }); - }); diff --git a/test/acceptance/log-test.js b/test/acceptance/log-test.js index efea437b..dbcf375f 100644 --- a/test/acceptance/log-test.js +++ b/test/acceptance/log-test.js @@ -8,23 +8,23 @@ const qs = require('querystring'); const BatchTestClient = require('../support/batch-test-client'); const { TYPES } = require('../../lib/api/middlewares/log'); -const QUERY = `SELECT 14 as foo`; +const QUERY = 'SELECT 14 as foo'; const API_KEY = 1234; const logQueries = global.settings.logQueries; -describe('Log middleware', function() { - before(function() { +describe('Log middleware', function () { + before(function () { global.settings.logQueries = true; }); - after(function() { + after(function () { global.settings.logQueries = logQueries; }); - describe('regular queries endpoint', function() { + describe('regular queries endpoint', function () { ['GET', 'POST'].forEach(method => { - it(`${method} query`, function(done) { + it(`${method} query`, function (done) { assert.response(server, { method, @@ -37,7 +37,7 @@ describe('Log middleware', function() { } }, { statusCode: 200 }, - function(err, res) { + function (err, res) { assert.ok(!err); assert.ok(res.headers['x-sqlapi-log']); @@ -59,7 +59,7 @@ describe('Log middleware', function() { it(`${method} Respects max header size with long queries`, function (done) { let longQuery = "Select '"; for (let i = 0; i < 7000; i++) { - longQuery += "a"; + longQuery += 'a'; } longQuery += "' as foo"; assert.response(server, @@ -74,7 +74,7 @@ describe('Log middleware', function() { } }, { statusCode: 200 }, - function(err, res) { + function (err, res) { assert.ok(!err); assert.ok(res.headers['x-sqlapi-log']); @@ -87,19 +87,19 @@ describe('Log middleware', function() { }); }); - describe('batch api queries', function() { - before(function() { + describe('batch api queries', function () { + before(function () { this.batchTestClient = new BatchTestClient(); assert.ok(this.batchTestClient); }); - after(function(done) { + after(function (done) { this.batchTestClient.drain(done); }); it('one query', function (done) { const payload = { query: QUERY }; - this.batchTestClient.createJob(payload, function(err, jobResult, res) { + this.batchTestClient.createJob(payload, function (err, jobResult, res) { assert.ok(!err); assert.ok(res.headers['x-sqlapi-log']); @@ -117,15 +117,15 @@ describe('Log middleware', function() { }); }); - it(`Respects max header size with long queries`, function (done) { + it('Respects max header size with long queries', function (done) { let longQuery = "Select '"; for (let i = 0; i < 7000; i++) { - longQuery += "a"; + longQuery += 'a'; } longQuery += "' as foo"; const payload = { query: QUERY }; - this.batchTestClient.createJob(payload, function(err, jobResult, res) { + this.batchTestClient.createJob(payload, function (err, jobResult, res) { assert.ok(!err); assert.ok(res.headers['x-sqlapi-log']); @@ -137,7 +137,7 @@ describe('Log middleware', function() { it('multiquery job with two queries', function (done) { const payload = { query: [QUERY, QUERY] }; - this.batchTestClient.createJob(payload, function(err, jobResult, res) { + this.batchTestClient.createJob(payload, function (err, jobResult, res) { assert.ok(!err); assert.ok(res.headers['x-sqlapi-log']); @@ -155,10 +155,10 @@ describe('Log middleware', function() { }); }); - it(`Respects max header size with long multiqueries`, function (done) { + it('Respects max header size with long multiqueries', function (done) { let longQuery = "Select '"; for (let i = 0; i < 100; i++) { - longQuery += "a"; + longQuery += 'a'; } longQuery += "' as foo"; @@ -168,7 +168,7 @@ describe('Log middleware', function() { } const payload = { query: queries }; - this.batchTestClient.createJob(payload, function(err, jobResult, res) { + this.batchTestClient.createJob(payload, function (err, jobResult, res) { assert.ok(!err); assert.ok(res.headers['x-sqlapi-log']); @@ -178,14 +178,14 @@ describe('Log middleware', function() { }); }); - it(`Respects max header size with lots of multiqueries`, function (done) { + it('Respects max header size with lots of multiqueries', function (done) { const queries = []; for (let i = 0; i < 1000; i++) { queries.push('Select 1'); } const payload = { query: queries }; - this.batchTestClient.createJob(payload, function(err, jobResult, res) { + this.batchTestClient.createJob(payload, function (err, jobResult, res) { assert.ok(!err); assert.ok(res.headers['x-sqlapi-log']); @@ -207,7 +207,7 @@ describe('Log middleware', function() { }; const payload = { query: FALLBACK_QUERY }; - this.batchTestClient.createJob(payload, function(err, jobResult, res) { + this.batchTestClient.createJob(payload, function (err, jobResult, res) { assert.ok(!err); assert.ok(res.headers['x-sqlapi-log']); @@ -226,16 +226,16 @@ describe('Log middleware', function() { }); }); - describe('disable queries log', function() { - before(function() { + describe('disable queries log', function () { + before(function () { global.settings.logQueries = false; }); - after(function() { + after(function () { global.settings.logQueries = true; }); - it(`GET query`, function(done) { + it('GET query', function (done) { assert.response(server, { method: 'GET', @@ -248,7 +248,7 @@ describe('Log middleware', function() { } }, { statusCode: 200 }, - function(err, res) { + function (err, res) { assert.ok(!err); assert.ok(res.headers['x-sqlapi-log']); diff --git a/test/acceptance/pagination-test.js b/test/acceptance/pagination-test.js index a7603b18..76dcd981 100644 --- a/test/acceptance/pagination-test.js +++ b/test/acceptance/pagination-test.js @@ -7,26 +7,24 @@ var assert = require('../support/assert'); var querystring = require('querystring'); var step = require('step'); - -describe('results-pagination', function() { - +describe('results-pagination', function () { var RESPONSE_OK = { statusCode: 200 }; // Test for https://github.com/Vizzuality/CartoDB-SQL-API/issues/85 - it("paging doesn't break x-cache-channel", function(done) { + it("paging doesn't break x-cache-channel", function (done) { assert.response(server, { url: '/api/v1/sql?' + querystring.stringify({ // note: select casing intentionally mixed - q: 'selECT cartodb_id*3 FROM untitle_table_4', - api_key: '1234', - rows_per_page: 1, - page: 2 + q: 'selECT cartodb_id*3 FROM untitle_table_4', + api_key: '1234', + rows_per_page: 1, + page: 2 }), - headers: {host: 'vizzuality.cartodb.com'}, + headers: { host: 'vizzuality.cartodb.com' }, method: 'GET' - }, RESPONSE_OK, function(err, res) { + }, RESPONSE_OK, function (err, res) { assert.equal(res.headers['x-cache-channel'], 'cartodb_test_user_1_db:public.untitle_table_4'); var parsed = JSON.parse(res.body); assert.equal(parsed.rows.length, 1); @@ -35,139 +33,140 @@ describe('results-pagination', function() { }); // Test page and rows_per_page params - it("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 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 ) { - if ( authorized ) { - done(); - return; - } else { - authorized = 1; - method = 0; - testing = 0; + var testNext = function () { + if (testing >= pr.length) { + if (method + 1 >= methods.length) { + if (authorized) { + done(); + return; + } else { + authorized = 1; + method = 0; + testing = 0; + } + } else { + testing = 0; + ++method; + } } - } else { - testing = 0; - ++method; - } - } - var prcur = pr[testing++]; - var page = prcur[0]; - var nrows = prcur[1]; - var data_obj = { + var prcur = pr[testing++]; + var page = prcur[0]; + var nrows = prcur[1]; + var data_obj = { q: sql, rows_per_page: nrows, page: page }; - 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' ) { - req.method = 'GET'; - req.url += '?' + data; - } else { - req.method = 'POST'; - req.headers['Content-Type'] = 'application/x-www-form-urlencoded'; - req.data = data; - } - assert.response(server, req, RESPONSE_OK, function(err, res) { - var parsed = JSON.parse(res.body); - assert.equal(parsed.rows.length, nrows); - for (var i=0; i { assert.ifError(err); assert.equal(result.error, 'system tables are forbidden'); @@ -26,36 +26,36 @@ describe('PG entities access validator', function () { }); } - describe('validatePGEntitiesAccess enabled', function() { - before(function(){ + describe('validatePGEntitiesAccess enabled', function () { + before(function () { global.settings.validatePGEntitiesAccess = true; }); forbiddenQueries.forEach(query => { - it(`testClientApiKey: query: ${query}`, function(done) { + it(`testClientApiKey: query: ${query}`, function (done) { assertQuery(query, testClientApiKey, done); }); - it(`testClientAuthorized: query: ${query}`, function(done) { + it(`testClientAuthorized: query: ${query}`, function (done) { assertQuery(query, testClientAuthorized, done); }); }); }); - describe('validatePGEntitiesAccess disabled', function() { - before(function(){ + describe('validatePGEntitiesAccess disabled', function () { + before(function () { global.settings.validatePGEntitiesAccess = false; }); forbiddenQueries.forEach(query => { - it(`testClientApiKey: query: ${query}`, function(done) { + it(`testClientApiKey: query: ${query}`, function (done) { testClientApiKey.getResult(query, err => { assert.ifError(err); done(); }); }); - it(`testClientAuthorized: query: ${query}`, function(done) { + it(`testClientAuthorized: query: ${query}`, function (done) { testClientAuthorized.getResult(query, err => { assert.ifError(err); done(); diff --git a/test/acceptance/pg-types-test.js b/test/acceptance/pg-types-test.js index 34a611e2..4f4db63b 100644 --- a/test/acceptance/pg-types-test.js +++ b/test/acceptance/pg-types-test.js @@ -15,7 +15,7 @@ describe('PG field type information', function () { assert.response( server, { - url: `/api/v1/sql?${querystring.stringify({ q: 'select * from pgtypes_table'})}`, + url: `/api/v1/sql?${querystring.stringify({ q: 'select * from pgtypes_table' })}`, headers: { host: 'vizzuality.cartodb.com' }, method: 'GET' }, @@ -161,6 +161,5 @@ describe('PG field type information', function () { done(); } ); - }); }); diff --git a/test/acceptance/query-float-values-test.js b/test/acceptance/query-float-values-test.js index 7aafc9d3..55892477 100644 --- a/test/acceptance/query-float-values-test.js +++ b/test/acceptance/query-float-values-test.js @@ -7,7 +7,7 @@ var assert = require('../support/assert'); var querystring = require('querystring'); var step = require('step'); -describe('special numeric (float) values', function() { +describe('special numeric (float) values', function () { var RESPONSE_OK = { statusCode: 200 }; diff --git a/test/acceptance/query-multipart-test.js b/test/acceptance/query-multipart-test.js index 90ac65fa..733d49e8 100644 --- a/test/acceptance/query-multipart-test.js +++ b/test/acceptance/query-multipart-test.js @@ -5,21 +5,21 @@ require('../helper'); const server = require('../../lib/server')(); const assert = require('../support/assert'); -describe('query-multipart', function() { - it('make query from a multipart form', function(done){ +describe('query-multipart', function () { + it('make query from a multipart form', function (done) { assert.response(server, { url: '/api/v1/sql', formData: { q: 'SELECT 2 as n' }, - headers: {host: 'vizzuality.cartodb.com'}, + headers: { host: 'vizzuality.cartodb.com' }, method: 'POST' - },{}, function(err, res) { + }, {}, function (err, res) { assert.ifError(err); const response = JSON.parse(res.body); - assert.equal(typeof(response.time) !== 'undefined', true); + assert.equal(typeof (response.time) !== 'undefined', true); assert.strictEqual(response.total_rows, 1); - assert.deepStrictEqual(response.rows, [{n:2}]); + assert.deepStrictEqual(response.rows, [{ n: 2 }]); done(); }); }); diff --git a/test/acceptance/query-returning-test.js b/test/acceptance/query-returning-test.js index 313df9be..56f31b85 100644 --- a/test/acceptance/query-returning-test.js +++ b/test/acceptance/query-returning-test.js @@ -7,9 +7,7 @@ var assert = require('../support/assert'); var querystring = require('querystring'); var _ = require('underscore'); - -describe('query-returning', function() { - +describe('query-returning', function () { var RESPONSE_OK = { statusCode: 200 }; @@ -19,15 +17,16 @@ describe('query-returning', function() { // Check results from INSERT // // See https://github.com/Vizzuality/CartoDB-SQL-API/issues/13 - it('INSERT returns affected rows', function(done){ + it('INSERT returns affected rows', function (done) { assert.response(server, { // 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' }, + headers: { host: 'vizzuality.localhost.lan:8080' }, method: 'GET' - }, RESPONSE_OK, function(err, res) { + }, RESPONSE_OK, function (err, res) { var out = JSON.parse(res.body); assert.ok(out.hasOwnProperty('time')); assert.equal(out.total_rows, 2); @@ -43,15 +42,16 @@ describe('query-returning', function() { // Check results from UPDATE // // See https://github.com/Vizzuality/CartoDB-SQL-API/issues/13 - it('UPDATE returns affected rows', function(done){ + it('UPDATE returns affected rows', function (done) { assert.response(server, { // 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' }, + headers: { host: 'vizzuality.localhost.lan:8080' }, method: 'GET' - }, RESPONSE_OK, function(err, res) { + }, RESPONSE_OK, function (err, res) { var out = JSON.parse(res.body); assert.ok(out.hasOwnProperty('time')); assert.equal(out.total_rows, 2); @@ -67,15 +67,16 @@ describe('query-returning', function() { // Check results from DELETE // // See https://github.com/Vizzuality/CartoDB-SQL-API/issues/13 - it('DELETE returns affected rows', function(done){ + it('DELETE returns affected rows', function (done) { assert.response(server, { // 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' }, + headers: { host: 'vizzuality.localhost.lan:8080' }, method: 'GET' - }, RESPONSE_OK, function(err, res) { + }, RESPONSE_OK, function (err, res) { var out = JSON.parse(res.body); assert.ok(out.hasOwnProperty('time')); assert.equal(out.total_rows, 2); @@ -91,15 +92,16 @@ describe('query-returning', function() { // Check results from INSERT .. RETURNING // // See https://github.com/Vizzuality/CartoDB-SQL-API/issues/50 - it('INSERT with RETURNING returns all results', function(done){ + it('INSERT with RETURNING returns all results', function (done) { assert.response(server, { // 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' }, + headers: { host: 'vizzuality.localhost.lan:8080' }, method: 'GET' - }, RESPONSE_OK, function(err, res) { + }, RESPONSE_OK, function (err, res) { var out = JSON.parse(res.body); assert.ok(out.hasOwnProperty('time')); assert.equal(out.total_rows, 1); @@ -114,15 +116,16 @@ describe('query-returning', function() { // Check results from UPDATE .. RETURNING // // See https://github.com/Vizzuality/CartoDB-SQL-API/issues/50 - it('UPDATE with RETURNING returns all results', function(done){ + it('UPDATE with RETURNING returns all results', function (done) { assert.response(server, { // 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' }, + headers: { host: 'vizzuality.localhost.lan:8080' }, method: 'GET' - }, RESPONSE_OK, function(err, res) { + }, RESPONSE_OK, function (err, res) { var out = JSON.parse(res.body); assert.ok(out.hasOwnProperty('time')); assert.equal(out.total_rows, 1); @@ -137,15 +140,16 @@ describe('query-returning', function() { // Check results from DELETE .. RETURNING // // See https://github.com/Vizzuality/CartoDB-SQL-API/issues/50 - it('DELETE with RETURNING returns all results', function(done){ + it('DELETE with RETURNING returns all results', function (done) { assert.response(server, { // 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' }, + headers: { host: 'vizzuality.localhost.lan:8080' }, method: 'GET' - }, RESPONSE_OK, function(err, res) { + }, RESPONSE_OK, function (err, res) { var out = JSON.parse(res.body); assert.ok(out.hasOwnProperty('time')); assert.equal(out.total_rows, 1); @@ -155,5 +159,4 @@ describe('query-returning', function() { done(); }); }); - }); diff --git a/test/acceptance/rate-limit-test.js b/test/acceptance/rate-limit-test.js index aa708c08..ad5ed047 100644 --- a/test/acceptance/rate-limit-test.js +++ b/test/acceptance/rate-limit-test.js @@ -12,7 +12,7 @@ const app = require('../../lib/server'); let server; let redisClient; -let keysToDelete = []; +const keysToDelete = []; const user = 'vizzuality'; var request = { @@ -25,8 +25,7 @@ var request = { method: 'GET' }; - -function setLimit(count, period, burst) { +function setLimit (count, period, burst) { redisClient.SELECT(8, err => { if (err) { return; @@ -45,7 +44,7 @@ function assertRequest (status, limit, remaining, reset, retry, done = null) { server, request, { status }, - function(err, res) { + function (err, res) { assert.ifError(err); assert.equal(res.headers['carto-rate-limit-limit'], limit); assert.equal(res.headers['carto-rate-limit-remaining'], remaining); @@ -55,11 +54,11 @@ function assertRequest (status, limit, remaining, reset, retry, done = null) { assert.equal(res.headers['retry-after'], retry); } - if(status === 429) { + if (status === 429) { const expectedResponse = { error: ["You are over platform\'s limits. Please contact us to know more details"], - context: "limit", - detail: "rate-limit" + context: 'limit', + detail: 'rate-limit' }; assert.deepEqual(JSON.parse(res.body), expectedResponse); @@ -72,8 +71,8 @@ function assertRequest (status, limit, remaining, reset, retry, done = null) { ); } -describe('rate limit', function() { - before(function() { +describe('rate limit', function () { + before(function () { global.settings.ratelimits.rateLimitsEnabled = true; global.settings.ratelimits.endpoints.query = true; @@ -86,22 +85,21 @@ describe('rate limit', function() { setLimit(count, period, burst); }); - after(function() { + after(function () { global.settings.ratelimits.rateLimitsEnabled = false; global.settings.ratelimits.endpoints.query = false; - keysToDelete.forEach( key => { + keysToDelete.forEach(key => { redisClient.del(key); }); }); - it("1 req/sec: 2 req/seg should be limited", function(done) { + it('1 req/sec: 2 req/seg should be limited', function (done) { assertRequest(200, 2, 1, 1); - setTimeout( () => assertRequest(200, 2, 0, 1, null), 250 ); - setTimeout( () => assertRequest(429, 2, 0, 1, 1), 500 ); - setTimeout( () => assertRequest(429, 2, 0, 1, 1), 750 ); - setTimeout( () => assertRequest(429, 2, 0, 1, 1), 950 ); - setTimeout( () => assertRequest(200, 2, 0, 1, null, done), 1050 ); + setTimeout(() => assertRequest(200, 2, 0, 1, null), 250); + setTimeout(() => assertRequest(429, 2, 0, 1, 1), 500); + setTimeout(() => assertRequest(429, 2, 0, 1, 1), 750); + setTimeout(() => assertRequest(429, 2, 0, 1, 1), 950); + setTimeout(() => assertRequest(200, 2, 0, 1, null, done), 1050); }); - }); diff --git a/test/acceptance/regressions-test.js b/test/acceptance/regressions-test.js index 31b4c43b..ba64a268 100644 --- a/test/acceptance/regressions-test.js +++ b/test/acceptance/regressions-test.js @@ -6,11 +6,9 @@ var server = require('../../lib/server')(); var assert = require('../support/assert'); var qs = require('querystring'); -describe('regressions', function() { - - it('issue #224: tables with . (dot) in name works and can be queried', function(done) { - - function createRequest(sqlQuery) { +describe('regressions', function () { + it('issue #224: tables with . (dot) in name works and can be queried', function (done) { + function createRequest (sqlQuery) { return { url: '/api/v1/sql?' + qs.stringify({ q: sqlQuery, @@ -28,13 +26,13 @@ describe('regressions', function() { }; assert.response(server, createRequest('CREATE TABLE "foo.bar" (a int);'), responseOk, - function(err) { + function (err) { if (err) { return done(err); } assert.response(server, createRequest('INSERT INTO "foo.bar" (a) values (1), (2)'), responseOk, - function(err, res) { + function (err, res) { if (err) { return done(err); } @@ -42,7 +40,7 @@ describe('regressions', function() { assert.equal(parsedBody.total_rows, 2); assert.response(server, createRequest('SELECT * FROM "foo.bar"'), responseOk, - function(err, res) { + function (err, res) { if (err) { return done(err); } @@ -62,5 +60,4 @@ describe('regressions', function() { } ); }); - }); diff --git a/test/acceptance/skipfields-test.js b/test/acceptance/skipfields-test.js index 9b3429bd..4ce223d2 100644 --- a/test/acceptance/skipfields-test.js +++ b/test/acceptance/skipfields-test.js @@ -7,63 +7,61 @@ var assert = require('../support/assert'); var querystring = require('querystring'); var _ = require('underscore'); - -describe('skipfields', function() { - +describe('skipfields', function () { var RESPONSE_OK = { statusCode: 200 }; - it('skipfields controls included fields', function(done){ + it('skipfields controls included fields', function (done) { assert.response(server, { url: '/api/v1/sql?q=' + 'SELECT%20*%20FROM%20untitle_table_4&skipfields=the_geom_webmercator,cartodb_id,unexistant', - headers: {host: 'vizzuality.cartodb.com'}, + headers: { host: 'vizzuality.cartodb.com' }, method: 'GET' - }, RESPONSE_OK, function(err, res){ + }, RESPONSE_OK, function (err, res) { var row0 = JSON.parse(res.body).rows[0]; - var checkfields = {'name':1, 'cartodb_id':0, 'the_geom':1, 'the_geom_webmercator':0}; - for ( var f in checkfields ) { - if ( checkfields[f] ) { - assert.ok(row0.hasOwnProperty(f), "result does not include '" + f + "'"); - } else { - assert.ok(!row0.hasOwnProperty(f), "result includes '" + f + "'"); - } + var checkfields = { name: 1, cartodb_id: 0, the_geom: 1, the_geom_webmercator: 0 }; + for (var f in checkfields) { + if (checkfields[f]) { + assert.ok(row0.hasOwnProperty(f), "result does not include '" + f + "'"); + } else { + assert.ok(!row0.hasOwnProperty(f), "result includes '" + f + "'"); + } } done(); }); }); - it('multiple skipfields parameter do not kill the backend', function(done){ + it('multiple skipfields parameter do not kill the backend', function (done) { assert.response(server, { 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'}, + headers: { host: 'vizzuality.cartodb.com' }, method: 'GET' - }, RESPONSE_OK, function(err, res){ + }, RESPONSE_OK, function (err, res) { var row0 = JSON.parse(res.body).rows[0]; - var checkfields = {'name':1, 'cartodb_id':0, 'the_geom':1, 'the_geom_webmercator':0}; - for ( var f in checkfields ) { - if ( checkfields[f] ) { - assert.ok(row0.hasOwnProperty(f), "result does not include '" + f + "'"); - } else { - assert.ok(!row0.hasOwnProperty(f), "result includes '" + f + "'"); - } + var checkfields = { name: 1, cartodb_id: 0, the_geom: 1, the_geom_webmercator: 0 }; + for (var f in checkfields) { + if (checkfields[f]) { + assert.ok(row0.hasOwnProperty(f), "result does not include '" + f + "'"); + } else { + assert.ok(!row0.hasOwnProperty(f), "result includes '" + f + "'"); + } } done(); }); }); // See https://github.com/CartoDB/CartoDB-SQL-API/issues/109 - it('schema response takes skipfields into account', function(done){ + it('schema response takes skipfields into account', function (done) { assert.response(server, { url: '/api/v1/sql?' + querystring.stringify({ - q: "SELECT 1 as a, 2 as b, 3 as c ", - skipfields: 'b' + q: 'SELECT 1 as a, 2 as b, 3 as c ', + skipfields: 'b' }), - headers: {host: 'vizzuality.cartodb.com'}, + headers: { host: 'vizzuality.cartodb.com' }, method: 'GET' - }, RESPONSE_OK, function(err, res) { + }, RESPONSE_OK, function (err, res) { var parsedBody = JSON.parse(res.body); assert.equal(_.keys(parsedBody.fields).length, 2); assert.ok(parsedBody.fields.hasOwnProperty('a')); @@ -72,21 +70,21 @@ describe('skipfields', function() { done(); }); }); - it('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(server, { url: '/api/v1/sql?q=SELECT%20*%20FROM%20untitle_table_4', - headers: {host: 'vizzuality.cartodb.com'}, + headers: { host: 'vizzuality.cartodb.com' }, method: 'GET' - },{ }, function(err, res){ + }, { }, function (err, res) { assert.equal(res.statusCode, 200, res.body); var row0 = JSON.parse(res.body).rows[0]; - var checkfields = {'name':1, 'cartodb_id':1, 'the_geom':1, 'the_geom_webmercator':1}; - for ( var f in checkfields ) { - if ( checkfields[f] ) { - assert.ok(row0.hasOwnProperty(f), "result does not include '" + f + "'"); - } else { - assert.ok(!row0.hasOwnProperty(f), "result includes '" + f + "'"); - } + var checkfields = { name: 1, cartodb_id: 1, the_geom: 1, the_geom_webmercator: 1 }; + for (var f in checkfields) { + if (checkfields[f]) { + assert.ok(row0.hasOwnProperty(f), "result does not include '" + f + "'"); + } else { + assert.ok(!row0.hasOwnProperty(f), "result includes '" + f + "'"); + } } done(); }); diff --git a/test/acceptance/stream-responses-test.js b/test/acceptance/stream-responses-test.js index 88b4d068..b71f88df 100644 --- a/test/acceptance/stream-responses-test.js +++ b/test/acceptance/stream-responses-test.js @@ -6,11 +6,10 @@ var server = require('../../lib/server')(); var assert = require('../support/assert'); var querystring = require('querystring'); -describe('stream-responses', function() { - - function createFailingQueryRequest(format) { +describe('stream-responses', function () { + function createFailingQueryRequest (format) { var params = { - q: "SELECT the_geom, 100/(cartodb_id - 3) cdb_ratio FROM untitle_table_4" + q: 'SELECT the_geom, 100/(cartodb_id - 3) cdb_ratio FROM untitle_table_4' }; if (format) { @@ -18,7 +17,7 @@ describe('stream-responses', function() { } return { - url: "/api/v1/sql?" + querystring.stringify(params), + url: '/api/v1/sql?' + querystring.stringify(params), headers: { host: 'vizzuality.cartodb.com' }, @@ -30,40 +29,35 @@ describe('stream-responses', function() { status: 200 }; - describe('format-json', function() { - - it('should close on error and error message must be part of the response', function(done) { + describe('format-json', function () { + it('should close on error and error message must be part of the response', function (done) { assert.response( server, createFailingQueryRequest(), okResponse, - function(err, res) { + function (err, res) { var parsedBody = JSON.parse(res.body); assert.equal(parsedBody.rows.length, 2); - assert.deepEqual(parsedBody.error, ["division by zero"]); + assert.deepEqual(parsedBody.error, ['division by zero']); done(); } ); }); - }); - describe('format-geojson', function() { - - it('should close on error and error message must be part of the response', function(done) { + describe('format-geojson', function () { + it('should close on error and error message must be part of the response', function (done) { assert.response( server, createFailingQueryRequest('geojson'), okResponse, - function(err, res) { + function (err, res) { var parsedBody = JSON.parse(res.body); assert.equal(parsedBody.features.length, 2); - assert.deepEqual(parsedBody.error, ["division by zero"]); + assert.deepEqual(parsedBody.error, ['division by zero']); done(); } ); }); - }); - }); diff --git a/test/acceptance/surrogate-key-test.js b/test/acceptance/surrogate-key-test.js index aac7a758..0647da8d 100644 --- a/test/acceptance/surrogate-key-test.js +++ b/test/acceptance/surrogate-key-test.js @@ -8,9 +8,8 @@ var querystring = require('querystring'); var QueryTables = require('cartodb-query-tables').queryTables; var _ = require('underscore'); -describe('Surrogate-Key header', function() { - - function createGetRequest(sqlQuery) { +describe('Surrogate-Key header', function () { + function createGetRequest (sqlQuery) { var query = querystring.stringify({ q: sqlQuery, api_key: 1234 @@ -28,9 +27,8 @@ describe('Surrogate-Key header', function() { statusCode: 200 }; - function surrogateKeyHasTables(surrogateKey, expectedTables) { - - var surrogateKeys = surrogateKey.split(" "); + function surrogateKeyHasTables (surrogateKey, expectedTables) { + var surrogateKeys = surrogateKey.split(' '); var expectedSurrogateKeys = new QueryTables.QueryMetadata(expectedTables).key(); @@ -40,72 +38,70 @@ describe('Surrogate-Key header', function() { assert.equal(tablesDiff.length, 0, 'Surrogate-Key missing tables: ' + tablesDiff.join(',')); } - - function tableNamesInSurrogateKeyHeader(expectedTableNames, done) { - return function(err, res) { + function tableNamesInSurrogateKeyHeader (expectedTableNames, done) { + return function (err, res) { surrogateKeyHasTables(res.headers['surrogate-key'], expectedTableNames); done(); }; } - it('supports joins', function(done) { - var sql = "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)"; + it('supports joins', function (done) { + var sql = '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)'; assert.response(server, createGetRequest(sql), RESPONSE_OK, tableNamesInSurrogateKeyHeader([ - {dbname: 'cartodb_test_user_1_db', schema_name: 'public', table_name: 'private_table'}, - {dbname: 'cartodb_test_user_1_db', schema_name: 'public', table_name: 'untitle_table_4'} + { dbname: 'cartodb_test_user_1_db', schema_name: 'public', table_name: 'private_table' }, + { dbname: 'cartodb_test_user_1_db', schema_name: 'public', table_name: 'untitle_table_4' } ], done)); }); - it('supports multistatements', function(done) { - var sql = "SELECT * FROM untitle_table_4; SELECT * FROM private_table"; + it('supports multistatements', function (done) { + var sql = 'SELECT * FROM untitle_table_4; SELECT * FROM private_table'; assert.response(server, createGetRequest(sql), RESPONSE_OK, tableNamesInSurrogateKeyHeader([ - {dbname: 'cartodb_test_user_1_db', schema_name: 'public', table_name: 'private_table'}, - {dbname: 'cartodb_test_user_1_db', schema_name: 'public', table_name: 'untitle_table_4'} + { dbname: 'cartodb_test_user_1_db', schema_name: 'public', table_name: 'private_table' }, + { dbname: 'cartodb_test_user_1_db', schema_name: 'public', table_name: 'untitle_table_4' } ], done)); }); - it('supports explicit transactions', function(done) { - var sql = "BEGIN; SELECT * FROM untitle_table_4; COMMIT; BEGIN; SELECT * FROM private_table; COMMIT;"; + it('supports explicit transactions', function (done) { + var sql = 'BEGIN; SELECT * FROM untitle_table_4; COMMIT; BEGIN; SELECT * FROM private_table; COMMIT;'; assert.response(server, createGetRequest(sql), RESPONSE_OK, tableNamesInSurrogateKeyHeader([ - {dbname: 'cartodb_test_user_1_db', schema_name: 'public', table_name: 'private_table'}, - {dbname: 'cartodb_test_user_1_db', schema_name: 'public', table_name: 'untitle_table_4'} + { dbname: 'cartodb_test_user_1_db', schema_name: 'public', table_name: 'private_table' }, + { dbname: 'cartodb_test_user_1_db', schema_name: 'public', table_name: 'untitle_table_4' } ], done)); }); - it('survives partial transactions', function(done) { - var sql = "BEGIN; SELECT * FROM untitle_table_4"; + it('survives partial transactions', function (done) { + var sql = 'BEGIN; SELECT * FROM untitle_table_4'; assert.response(server, createGetRequest(sql), RESPONSE_OK, tableNamesInSurrogateKeyHeader([ - {dbname: 'cartodb_test_user_1_db', schema_name: 'public', table_name: 'untitle_table_4'} + { dbname: 'cartodb_test_user_1_db', schema_name: 'public', table_name: 'untitle_table_4' } ], done)); }); - it('should not add header for functions', function(done) { + it('should not add header for functions', function (done) { var sql = "SELECT format('%s', 'wadus')"; - assert.response(server, createGetRequest(sql), RESPONSE_OK, function(err, res) { + assert.response(server, createGetRequest(sql), RESPONSE_OK, function (err, res) { assert.ok(!res.headers.hasOwnProperty('surrogate-key'), res.headers['surrogate-key']); done(); }); }); - it('should not add header for CDB_QueryTables', function(done) { + it('should not add header for CDB_QueryTables', function (done) { var sql = "SELECT CDB_QueryTablesText('select * from untitle_table_4')"; - assert.response(server, createGetRequest(sql), RESPONSE_OK, function(err, res) { + assert.response(server, createGetRequest(sql), RESPONSE_OK, function (err, res) { assert.ok(!res.headers.hasOwnProperty('surrogate-key'), res.headers['surrogate-key']); done(); }); }); - it('should not add header for non table results', function(done) { + it('should not add header for non table results', function (done) { var sql = "SELECT 'wadus'::text"; - assert.response(server, createGetRequest(sql), RESPONSE_OK, function(err, res) { + assert.response(server, createGetRequest(sql), RESPONSE_OK, function (err, res) { assert.ok(!res.headers.hasOwnProperty('surrogate-key'), res.headers['surrogate-key']); done(); }); }); - }); diff --git a/test/acceptance/system-queries-test.js b/test/acceptance/system-queries-test.js index 2c6853e1..d898020f 100644 --- a/test/acceptance/system-queries-test.js +++ b/test/acceptance/system-queries-test.js @@ -6,9 +6,7 @@ var server = require('../../lib/server')(); var assert = require('../support/assert'); var querystring = require('querystring'); - -describe('system-queries', function() { - +describe('system-queries', function () { var systemQueriesSuitesToTest = [ { desc: 'pg_ queries work with api_key and fail otherwise', @@ -34,31 +32,30 @@ describe('system-queries', function() { } ]; - systemQueriesSuitesToTest.forEach(function(suiteToTest) { - var apiKeyStatusErrorCode = !!suiteToTest.api_key_works ? 200 : 403; + systemQueriesSuitesToTest.forEach(function (suiteToTest) { + var apiKeyStatusErrorCode = suiteToTest.api_key_works ? 200 : 403; testSystemQueries(suiteToTest.desc + ' with api_key', suiteToTest.queries, apiKeyStatusErrorCode, '1234'); - var noApiKeyStatusErrorCode = !!suiteToTest.no_api_key_works ? 200 : 403; + var noApiKeyStatusErrorCode = suiteToTest.no_api_key_works ? 200 : 403; testSystemQueries(suiteToTest.desc, suiteToTest.queries, noApiKeyStatusErrorCode); }); - function testSystemQueries(description, queries, statusErrorCode, apiKey) { - queries.forEach(function(query) { - it('[' + description + '] query: ' + query, function(done) { - var queryStringParams = {q: query}; - if (!!apiKey) { + function testSystemQueries (description, queries, statusErrorCode, apiKey) { + queries.forEach(function (query) { + it('[' + description + '] query: ' + query, function (done) { + var queryStringParams = { q: query }; + if (apiKey) { queryStringParams.api_key = apiKey; } var request = { - headers: {host: 'vizzuality.cartodb.com'}, + headers: { host: 'vizzuality.cartodb.com' }, method: 'GET', url: '/api/v1/sql?' + querystring.stringify(queryStringParams) }; - assert.response(server, request, function(err, response) { + assert.response(server, request, function (err, response) { assert.equal(response.statusCode, statusErrorCode); done(); }); }); }); } - }); diff --git a/test/acceptance/timeout-test.js b/test/acceptance/timeout-test.js index 95270a60..24d2d0cc 100644 --- a/test/acceptance/timeout-test.js +++ b/test/acceptance/timeout-test.js @@ -16,40 +16,37 @@ */ require('../helper'); - var assert = require('../support/assert'); var step = require('step'); -describe('timeout', function() { - +describe('timeout', function () { // See https://github.com/CartoDB/CartoDB-SQL-API/issues/128 -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 server = require('../../lib/server')(); - step( - function sendLongQuery() { - assert.response(server, { - url: '/api/v1/sql?q=SELECT+count(*)+FROM+generate_series(1,100000)', - method: 'GET', - headers: {host: 'vizzuality.localhost' } - },{}, this); - }, - function checkResponse(err/*, res*/) { - assert.ok(err); - assert.ok(err.message.match(/hang up/), err); - return null; - }, - function finish(err) { - global.settings.node_socket_timeout = timeoutBackup; - done(err); - } - ); -}); - -// TODO: check that the query is interrupted on timeout! -//See #129 + 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 server = require('../../lib/server')(); + step( + function sendLongQuery () { + assert.response(server, { + url: '/api/v1/sql?q=SELECT+count(*)+FROM+generate_series(1,100000)', + method: 'GET', + headers: { host: 'vizzuality.localhost' } + }, {}, this); + }, + function checkResponse (err/*, res */) { + assert.ok(err); + assert.ok(err.message.match(/hang up/), err); + return null; + }, + function finish (err) { + global.settings.node_socket_timeout = timeoutBackup; + done(err); + } + ); + }); + // TODO: check that the query is interrupted on timeout! + // See #129 }); diff --git a/test/acceptance/transaction-test.js b/test/acceptance/transaction-test.js index 8fc9d150..a503a59b 100644 --- a/test/acceptance/transaction-test.js +++ b/test/acceptance/transaction-test.js @@ -6,18 +6,17 @@ var assert = require('../support/assert'); var qs = require('querystring'); var request = require('request'); -describe('transaction', function() { - +describe('transaction', function () { var SERVER_PORT = 5554; var server; - before(function(done) { + before(function (done) { server = require('../../lib/server')(); this.listener = server.listen(SERVER_PORT, '127.0.0.1'); this.listener.on('listening', done); }); - after(function(done) { + after(function (done) { this.listener.close(done); }); @@ -25,14 +24,14 @@ describe('transaction', function() { headers: { host: 'vizzuality.localhost' } }); - function requestUrl(query) { + function requestUrl (query) { return 'http://127.0.0.1:' + SERVER_PORT + '/api/v1/sql?' + qs.stringify({ q: query }); } var errorQuery = 'BEGIN; PREPARE _pstm AS select error; EXECUTE _pstm; COMMIT;'; - it('should NOT fail to second request after error in transaction', function(done) { - sqlRequest(requestUrl(errorQuery), function(err, response, body) { + it('should NOT fail to second request after error in transaction', function (done) { + sqlRequest(requestUrl(errorQuery), function (err, response, body) { assert.ok(!err); assert.equal(response.statusCode, 400); @@ -52,5 +51,4 @@ describe('transaction', function() { }); }); }); - }); diff --git a/test/acceptance/x-cache-channel-test.js b/test/acceptance/x-cache-channel-test.js index 0bf18f16..b1f99f98 100644 --- a/test/acceptance/x-cache-channel-test.js +++ b/test/acceptance/x-cache-channel-test.js @@ -7,9 +7,8 @@ var assert = require('../support/assert'); var querystring = require('querystring'); var _ = require('underscore'); -describe('X-Cache-Channel header', function() { - - function createGetRequest(sqlQuery) { +describe('X-Cache-Channel header', function () { + function createGetRequest (sqlQuery) { var query = querystring.stringify({ q: sqlQuery, api_key: 1234 @@ -27,7 +26,7 @@ describe('X-Cache-Channel header', function() { statusCode: 200 }; - function xCacheChannelHeaderHasTables(xCacheChannel, expectedTablesNames) { + function xCacheChannelHeaderHasTables (xCacheChannel, expectedTablesNames) { var databaseAndTables = xCacheChannel.split(':'); var databaseName = databaseAndTables[0]; @@ -40,16 +39,16 @@ describe('X-Cache-Channel header', function() { assert.equal(tablesDiff.length, 0, 'X-Cache-Channel header missing tables: ' + tablesDiff.join(',')); } - function tableNamesInCacheChannelHeader(expectedTableNames, done) { - return function(err, res) { + function tableNamesInCacheChannelHeader (expectedTableNames, done) { + return function (err, res) { xCacheChannelHeaderHasTables(res.headers['x-cache-channel'], expectedTableNames); done(); }; } - it('supports joins', function(done) { - var sql = "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)"; + it('supports joins', function (done) { + var sql = '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)'; assert.response(server, createGetRequest(sql), RESPONSE_OK, tableNamesInCacheChannelHeader([ 'public.private_table', @@ -57,8 +56,8 @@ describe('X-Cache-Channel header', function() { ], done)); }); - it('supports multistatements', function(done) { - var sql = "SELECT * FROM untitle_table_4; SELECT * FROM private_table"; + it('supports multistatements', function (done) { + var sql = 'SELECT * FROM untitle_table_4; SELECT * FROM private_table'; assert.response(server, createGetRequest(sql), RESPONSE_OK, tableNamesInCacheChannelHeader([ 'public.private_table', @@ -66,8 +65,8 @@ describe('X-Cache-Channel header', function() { ], done)); }); - it('supports explicit transactions', function(done) { - var sql = "BEGIN; SELECT * FROM untitle_table_4; COMMIT; BEGIN; SELECT * FROM private_table; COMMIT;"; + it('supports explicit transactions', function (done) { + var sql = 'BEGIN; SELECT * FROM untitle_table_4; COMMIT; BEGIN; SELECT * FROM private_table; COMMIT;'; assert.response(server, createGetRequest(sql), RESPONSE_OK, tableNamesInCacheChannelHeader([ 'public.private_table', @@ -75,38 +74,35 @@ describe('X-Cache-Channel header', function() { ], done)); }); - it('survives partial transactions', function(done) { - var sql = "BEGIN; SELECT * FROM untitle_table_4"; + it('survives partial transactions', function (done) { + var sql = 'BEGIN; SELECT * FROM untitle_table_4'; assert.response(server, createGetRequest(sql), RESPONSE_OK, tableNamesInCacheChannelHeader([ 'public.untitle_table_4' ], done)); }); - it('should not add header for functions', function(done) { + it('should not add header for functions', function (done) { var sql = "SELECT format('%s', 'wadus')"; - assert.response(server, createGetRequest(sql), RESPONSE_OK, function(err, res) { + assert.response(server, createGetRequest(sql), RESPONSE_OK, function (err, res) { assert.ok(!res.headers.hasOwnProperty('x-cache-channel'), res.headers['x-cache-channel']); done(); }); }); - it('should not add header for CDB_QueryTables', function(done) { + it('should not add header for CDB_QueryTables', function (done) { var sql = "SELECT CDB_QueryTablesText('select * from untitle_table_4')"; - assert.response(server, createGetRequest(sql), RESPONSE_OK, function(err, res) { + assert.response(server, createGetRequest(sql), RESPONSE_OK, function (err, res) { assert.ok(!res.headers.hasOwnProperty('x-cache-channel'), res.headers['x-cache-channel']); done(); }); }); - it('should not add header for non table results', function(done) { + it('should not add header for non table results', function (done) { var sql = "SELECT 'wadus'::text"; - assert.response(server, createGetRequest(sql), RESPONSE_OK, function(err, res) { + assert.response(server, createGetRequest(sql), RESPONSE_OK, function (err, res) { assert.ok(!res.headers.hasOwnProperty('x-cache-channel'), res.headers['x-cache-channel']); done(); }); }); - - - }); diff --git a/test/integration/batch/job-backend-test.js b/test/integration/batch/job-backend-test.js index 8588a6fa..087f587c 100644 --- a/test/integration/batch/job-backend-test.js +++ b/test/integration/batch/job-backend-test.js @@ -17,7 +17,7 @@ var jobStatus = require(BATCH_SOURCE + 'job-status'); var logger = new BatchLogger(null, 'batch-queries'); var metadataBackend = require('cartodb-redis')({ pool: redisUtils.getPool() }); var jobPublisher = new JobPublisher(redisUtils.getPool()); -var jobQueue = new JobQueue(metadataBackend, jobPublisher, logger); +var jobQueue = new JobQueue(metadataBackend, jobPublisher, logger); var queue = require('queue-async'); @@ -30,11 +30,11 @@ var JOB = { host: HOST }; -function createWadusJob() { +function createWadusJob () { return JobFactory.create(JSON.parse(JSON.stringify(JOB))); } -describe('job backend', function() { +describe('job backend', function () { var jobBackend = new JobBackend(metadataBackend, jobQueue, logger); after(function (done) { @@ -192,12 +192,11 @@ describe('job backend', function() { } assert.ok(users.userA); - assert.deepEqual(users.userA, [ 'jobId1', 'jobId2' ]); + assert.deepEqual(users.userA, ['jobId1', 'jobId2']); assert.ok(users.userB); - assert.deepEqual(users.userB, [ 'jobId3' ]); + assert.deepEqual(users.userB, ['jobId3']); done(); }); - }); }); diff --git a/test/integration/batch/job-canceller-test.js b/test/integration/batch/job-canceller-test.js index b9c3d6c7..34211c00 100644 --- a/test/integration/batch/job-canceller-test.js +++ b/test/integration/batch/job-canceller-test.js @@ -19,7 +19,7 @@ var PSQL = require('cartodb-psql'); var logger = new BatchLogger(null, 'batch-queries'); var metadataBackend = require('cartodb-redis')({ pool: redisUtils.getPool() }); var jobPublisher = new JobPublisher(redisUtils.getPool()); -var jobQueue = new JobQueue(metadataBackend, jobPublisher, logger); +var jobQueue = new JobQueue(metadataBackend, jobPublisher, logger); var jobBackend = new JobBackend(metadataBackend, jobQueue, logger); var USER = 'vizzuality'; @@ -28,7 +28,7 @@ var HOST = 'localhost'; // sets job to running, run its query and returns inmediatly (don't wait for query finishes) // in order to test query cancelation/draining -function runQueryHelper(job, callback) { +function runQueryHelper (job, callback) { var job_id = job.job_id; var sql = job.query; @@ -44,7 +44,7 @@ function runQueryHelper(job, callback) { port: job.port, dbname: job.dbname, user: job.dbuser, - pass: job.pass, + pass: job.pass }; const pg = new PSQL(dbConfiguration); @@ -61,7 +61,7 @@ function runQueryHelper(job, callback) { }); } -function createWadusJob(query) { +function createWadusJob (query) { query = query || QUERY; return JobFactory.create(JSON.parse(JSON.stringify({ user: USER, @@ -70,11 +70,11 @@ function createWadusJob(query) { dbname: 'cartodb_test_user_1_db', dbuser: 'test_cartodb_user_1', port: 5432, - pass: 'test_cartodb_user_1_pass', + pass: 'test_cartodb_user_1_pass' }))); } -describe('job canceller', function() { +describe('job canceller', function () { var jobCanceller = new JobCanceller(); after(function (done) { diff --git a/test/integration/batch/job-publisher-test.js b/test/integration/batch/job-publisher-test.js index 74b252ae..82e43d88 100644 --- a/test/integration/batch/job-publisher-test.js +++ b/test/integration/batch/job-publisher-test.js @@ -8,13 +8,12 @@ var assert = require('../../support/assert'); var redisUtils = require('../../support/redis-utils'); - var Channel = require(BATCH_SOURCE + 'pubsub/channel'); var JobPublisher = require(BATCH_SOURCE + 'pubsub/job-publisher'); var HOST = 'wadus'; -describe('job publisher', function() { +describe('job publisher', function () { var jobPublisher = new JobPublisher(redisUtils.getPool()); it('.publish() should publish in job channel', function (done) { @@ -35,5 +34,4 @@ describe('job publisher', function() { jobPublisher.publish(HOST); }); }); - }); diff --git a/test/integration/batch/job-queue-test.js b/test/integration/batch/job-queue-test.js index 34053ef9..1eb343aa 100644 --- a/test/integration/batch/job-queue-test.js +++ b/test/integration/batch/job-queue-test.js @@ -18,7 +18,7 @@ describe('job queue', function () { var pool = redisUtils.getPool(); var logger = new BatchLogger(null, 'batch-queries'); var jobPublisher = new JobPublisher(pool); - var jobQueue = new JobQueue(metadataBackend, jobPublisher, logger); + var jobQueue = new JobQueue(metadataBackend, jobPublisher, logger); var jobBackend = new JobBackend(metadataBackend, jobQueue, logger); var jobCanceller = new JobCanceller(); var jobService = new JobService(jobBackend, jobCanceller, logger); @@ -37,7 +37,7 @@ describe('job queue', function () { it('should find queues for one user', function (done) { var self = this; - this.jobQueue.enqueue(userA, 'wadus-wadus-wadus-wadus', function(err) { + this.jobQueue.enqueue(userA, 'wadus-wadus-wadus-wadus', function (err) { if (err) { return done(err); } @@ -54,11 +54,11 @@ describe('job queue', function () { it('should find queues for more than one user', function (done) { var self = this; - this.jobQueue.enqueue(userA, 'wadus-wadus-wadus-wadus', function(err) { + this.jobQueue.enqueue(userA, 'wadus-wadus-wadus-wadus', function (err) { if (err) { return done(err); } - self.jobQueue.enqueue(userB, 'wadus-wadus-wadus-wadus', function(err) { + self.jobQueue.enqueue(userB, 'wadus-wadus-wadus-wadus', function (err) { if (err) { return done(err); } @@ -75,7 +75,7 @@ describe('job queue', function () { }); }); - it('should find queues from jobs not using new Redis SETs for users', function(done) { + it('should find queues from jobs not using new Redis SETs for users', function (done) { var self = this; var redisArgs = [JobQueue.QUEUE.PREFIX + userA, 'wadus-id']; metadataBackend.redisCmd(JobQueue.QUEUE.DB, 'LPUSH', redisArgs, function (err) { @@ -123,7 +123,6 @@ describe('job queue', function () { redisUtils.clean('batch:*', done); }); - }); }); }); @@ -173,7 +172,6 @@ describe('job queue', function () { redisUtils.clean('batch:*', done); }); - }); }); }); diff --git a/test/integration/batch/job-runner-test.js b/test/integration/batch/job-runner-test.js index cfa50a34..a1420517 100644 --- a/test/integration/batch/job-runner-test.js +++ b/test/integration/batch/job-runner-test.js @@ -18,11 +18,10 @@ var JobService = require(BATCH_SOURCE + 'job-service'); var JobRunner = require(BATCH_SOURCE + 'job-runner'); var QueryRunner = require(BATCH_SOURCE + 'query-runner'); - var metadataBackend = require('cartodb-redis')({ pool: redisUtils.getPool() }); var logger = new BatchLogger(null, 'batch-queries'); var jobPublisher = new JobPublisher(redisUtils.getPool()); -var jobQueue = new JobQueue(metadataBackend, jobPublisher, logger); +var jobQueue = new JobQueue(metadataBackend, jobPublisher, logger); var jobBackend = new JobBackend(metadataBackend, jobQueue, logger); var userDatabaseMetadataService = new UserDatabaseMetadataService(metadataBackend); var jobCanceller = new JobCanceller(); @@ -41,14 +40,14 @@ var JOB = { dbname: 'cartodb_test_user_1_db', dbuser: 'test_cartodb_user_1', port: 5432, - pass: 'test_cartodb_user_1_pass', + pass: 'test_cartodb_user_1_pass' }; -describe('job runner', function() { +describe('job runner', function () { var jobRunner = new JobRunner(jobService, jobQueue, queryRunner, metadataBackend, statsdClient); after(function (done) { - redisUtils.clean('batch:*', function() { + redisUtils.clean('batch:*', function () { redisUtils.clean('limits:batch:*', done); }); }); @@ -78,5 +77,4 @@ describe('job runner', function() { done(); }); }); - }); diff --git a/test/integration/batch/job-service-test.js b/test/integration/batch/job-service-test.js index efadac90..34035a80 100644 --- a/test/integration/batch/job-service-test.js +++ b/test/integration/batch/job-service-test.js @@ -19,7 +19,7 @@ var PSQL = require('cartodb-psql'); var metadataBackend = require('cartodb-redis')({ pool: redisUtils.getPool() }); var logger = new BatchLogger(null, 'batch-queries'); var jobPublisher = new JobPublisher(redisUtils.getPool()); -var jobQueue = new JobQueue(metadataBackend, jobPublisher, logger); +var jobQueue = new JobQueue(metadataBackend, jobPublisher, logger); var jobBackend = new JobBackend(metadataBackend, jobQueue, logger); var jobCanceller = new JobCanceller(); @@ -33,17 +33,17 @@ var JOB = { dbname: 'cartodb_test_user_1_db', dbuser: 'test_cartodb_user_1', port: 5432, - pass: 'test_cartodb_user_1_pass', + pass: 'test_cartodb_user_1_pass' }; -function createWadusDataJob() { +function createWadusDataJob () { return JSON.parse(JSON.stringify(JOB)); } // sets job to running, run its query and returns inmediatly (don't wait for query finishes) // in order to test query cancelation/draining -function runQueryHelper(job, callback) { +function runQueryHelper (job, callback) { var job_id = job.job_id; var sql = job.query; @@ -59,7 +59,7 @@ function runQueryHelper(job, callback) { port: job.port, dbname: job.dbname, user: job.dbuser, - pass: job.pass, + pass: job.pass }; var pg = new PSQL(dbConfiguration); @@ -76,7 +76,7 @@ function runQueryHelper(job, callback) { }); } -describe('job service', function() { +describe('job service', function () { var jobService = new JobService(jobBackend, jobCanceller); after(function (done) { @@ -203,5 +203,4 @@ describe('job service', function() { done(); }); }); - }); diff --git a/test/integration/batch/locker-test.js b/test/integration/batch/locker-test.js index f1e5c534..e17bae04 100644 --- a/test/integration/batch/locker-test.js +++ b/test/integration/batch/locker-test.js @@ -6,7 +6,7 @@ var assert = require('../../support/assert'); var redisUtils = require('../../support/redis-utils'); var Locker = require('../../../lib/batch/leader/locker'); -describe('locker', function() { +describe('locker', function () { var host = 'localhost'; var TTL = 500; @@ -16,21 +16,21 @@ describe('locker', function() { it('should lock and unlock', function (done) { var lockerA = Locker.create('redis-distlock', config); var lockerB = Locker.create('redis-distlock', config); - lockerA.lock(host, function(err, lock) { + lockerA.lock(host, function (err, lock) { if (err) { return done(err); } assert.ok(lock); // others can't lock on same host - lockerB.lock(host, function(err) { + lockerB.lock(host, function (err) { assert.ok(err); assert.equal(err.name, 'LockError'); - lockerA.unlock(host, function(err) { + lockerA.unlock(host, function (err) { assert.ok(!err); // others can lock after unlock - lockerB.lock(host, function(err, lock2) { + lockerB.lock(host, function (err, lock2) { assert.ok(!err); assert.ok(lock2); lockerB.unlock(host, done); @@ -43,12 +43,12 @@ describe('locker', function() { it('should lock and keep locking until unlock', function (done) { var lockerA = Locker.create('redis-distlock', config); var lockerB = Locker.create('redis-distlock', config); - lockerA.lock(host, function(err, lock) { + lockerA.lock(host, function (err, lock) { if (err) { return done(err); } - setTimeout(function() { - lockerB.lock(host, function(err) { + setTimeout(function () { + lockerB.lock(host, function (err) { assert.ok(err); assert.ok(lock); diff --git a/test/integration/batch/scheduler-test.js b/test/integration/batch/scheduler-test.js index 2be1807b..f6b3689a 100644 --- a/test/integration/batch/scheduler-test.js +++ b/test/integration/batch/scheduler-test.js @@ -6,39 +6,38 @@ var assert = require('../../support/assert'); var Scheduler = require('../../../lib/batch/scheduler/scheduler'); var FixedCapacity = require('../../../lib/batch/scheduler/capacity/fixed'); -describe('scheduler', function() { - +describe('scheduler', function () { var USER_FINISHED = true; var USER_A = 'userA'; var USER_B = 'userB'; var USER_C = 'userC'; - function TaskRunner(userTasks) { + function TaskRunner (userTasks) { this.results = []; this.userTasks = userTasks; } - TaskRunner.prototype.run = function(user, callback) { + TaskRunner.prototype.run = function (user, callback) { this.results.push(user); this.userTasks[user]--; - setTimeout(function() { + setTimeout(function () { return callback(null, this.userTasks[user] === 0); }.bind(this), 50); }; - function ManualTaskRunner() { + function ManualTaskRunner () { this.userTasks = {}; } - ManualTaskRunner.prototype.run = function(user, callback) { + ManualTaskRunner.prototype.run = function (user, callback) { if (!this.userTasks.hasOwnProperty(user)) { this.userTasks[user] = []; } this.userTasks[user].push(callback); }; - ManualTaskRunner.prototype.dispatch = function(user, isDone) { + ManualTaskRunner.prototype.dispatch = function (user, isDone) { if (this.userTasks.hasOwnProperty(user)) { var cb = this.userTasks[user].shift(); if (cb) { @@ -47,12 +46,10 @@ describe('scheduler', function() { } }; - // simulate one by one or infinity capacity var capacities = [new FixedCapacity(1), new FixedCapacity(2), new FixedCapacity(Infinity)]; - capacities.forEach(function(capacity) { - + capacities.forEach(function (capacity) { it('regression #1', function (done) { var taskRunner = new TaskRunner({ userA: 2, @@ -62,7 +59,7 @@ describe('scheduler', function() { scheduler.add(USER_A); scheduler.add(USER_B); - scheduler.on('done', function() { + scheduler.on('done', function () { var results = taskRunner.results; assert.equal(results.length, 4); @@ -87,7 +84,7 @@ describe('scheduler', function() { var acquiredUsers = []; - scheduler.on('done', function() { + scheduler.on('done', function () { debug('Users %j', acquiredUsers); assert.equal(acquiredUsers[0], USER_A); assert.equal(acquiredUsers[1], USER_B); @@ -99,7 +96,7 @@ describe('scheduler', function() { return done(); }); - scheduler.on('acquired', function(user) { + scheduler.on('acquired', function (user) { debug('Acquired user %s', user); acquiredUsers.push(user); }); @@ -128,7 +125,7 @@ describe('scheduler', function() { var scheduler = new Scheduler(capacity, taskRunner); scheduler.add(USER_A); - scheduler.on('done', function() { + scheduler.on('done', function () { var results = taskRunner.results; assert.equal(results.length, 1); @@ -141,7 +138,6 @@ describe('scheduler', function() { scheduler.schedule(); }); - it('should run tasks for different users', function (done) { var taskRunner = new TaskRunner({ userA: 1, @@ -153,7 +149,7 @@ describe('scheduler', function() { scheduler.add(USER_B); scheduler.add(USER_C); - scheduler.on('done', function() { + scheduler.on('done', function () { var results = taskRunner.results; assert.equal(results.length, 3); @@ -183,7 +179,7 @@ describe('scheduler', function() { scheduler.add(USER_B); scheduler.add(USER_C); - scheduler.on('done', function() { + scheduler.on('done', function () { var results = taskRunner.results; assert.equal(results.length, 6); diff --git a/test/integration/stream-copy-test.js b/test/integration/stream-copy-test.js index f73c740b..7a9f82f8 100644 --- a/test/integration/stream-copy-test.js +++ b/test/integration/stream-copy-test.js @@ -5,8 +5,8 @@ const assert = require('assert'); const StreamCopy = require('../../lib/services/stream-copy'); -describe('stream copy', function() { - it('uses batch api port', function(done) { +describe('stream copy', function () { + it('uses batch api port', function (done) { const userDbParams = { dbname: 'cartodb_test_user_1_db', dbuser: 'test_cartodb_user_1', diff --git a/test/support/assert.js b/test/support/assert.js index 8de42b76..f2b5ea75 100644 --- a/test/support/assert.js +++ b/test/support/assert.js @@ -4,25 +4,25 @@ var assert = module.exports = exports = require('assert'); var request = require('request'); var debug = require('debug')('assert-response'); -assert.response = function(server, req, res, callback) { +assert.response = function (server, req, res, callback) { if (!callback) { callback = res; res = {}; } - var port = 5555, - host = '127.0.0.1'; + var port = 5555; + var host = '127.0.0.1'; var listeningAttempts = 0; var listener; - function listen() { + function listen () { if (listeningAttempts > 25) { var message = 'Tried too many ports'; debug(message); return callback(new Error(message)); } listener = server.listen(port, host); - listener.on('error', function() { + listener.on('error', function () { port++; listeningAttempts++; listen(); @@ -35,7 +35,7 @@ assert.response = function(server, req, res, callback) { debug('Request definition', req); // jshint maxcomplexity:10 - function onServerListening() { + function onServerListening () { debug('Server listening on port = %d', port); var status = res.status || res.statusCode; var requestParams = { @@ -55,9 +55,9 @@ assert.response = function(server, req, res, callback) { } debug('Request params', requestParams); - request(requestParams, function assert$response$requestHandler(error, response, body) { + request(requestParams, function assert$response$requestHandler (error, response, body) { debug('Request response', error); - listener.close(function() { + listener.close(function () { debug('Server closed'); if (error) { return callback(error); @@ -65,7 +65,7 @@ assert.response = function(server, req, res, callback) { response = response || {}; response.body = response.body || body; - debug('Response status', response.statusCode) + debug('Response status', response.statusCode); // Assert response body if (res.body) { @@ -77,7 +77,6 @@ assert.response = function(server, req, res, callback) { ' Got: [red]{' + response.body + '}') ); } - // Assert response status if (typeof status === 'number') { @@ -93,10 +92,10 @@ assert.response = function(server, req, res, callback) { if (res.headers) { var keys = Object.keys(res.headers); for (var i = 0, len = keys.length; i < len; ++i) { - var name = keys[i], - actual = response.headers[name.toLowerCase()], - expected = res.headers[name], - headerEql = expected instanceof RegExp ? expected.test(actual) : expected === actual; + var name = keys[i]; + var actual = response.headers[name.toLowerCase()]; + var expected = res.headers[name]; + var headerEql = expected instanceof RegExp ? expected.test(actual) : expected === actual; assert.ok(headerEql, colorize('Invalid response header [bold]{' + name + '}.\n' + ' Expected: [green]{' + expected + '}\n' + @@ -109,7 +108,6 @@ assert.response = function(server, req, res, callback) { return callback(null, response); }); }); - } }; @@ -120,9 +118,9 @@ assert.response = function(server, req, res, callback) { * @param {String} str * @return {String} */ -function colorize(str) { +function colorize (str) { var colors = { bold: 1, red: 31, green: 32, yellow: 33 }; - return str.replace(/\[(\w+)\]\{([^]*?)\}/g, function(_, color, str) { + return str.replace(/\[(\w+)\]\{([^]*?)\}/g, function (_, color, str) { return '\x1B[' + colors[color] + 'm' + str + '\x1B[0m'; }); } diff --git a/test/support/batch-test-client.js b/test/support/batch-test-client.js index 82962354..a28930c9 100644 --- a/test/support/batch-test-client.js +++ b/test/support/batch-test-client.js @@ -10,7 +10,7 @@ var JobStatus = require('../../lib/batch/job-status'); var metadataBackend = require('cartodb-redis')({ pool: redisUtils.getPool() }); var batchFactory = require('../../lib/batch/index'); -function response(code) { +function response (code) { return { status: code }; @@ -22,8 +22,7 @@ var RESPONSE = { BAD_REQUEST: response(400) }; - -function BatchTestClient(config) { +function BatchTestClient (config) { this.config = config || {}; this.server = appServer(); @@ -32,9 +31,9 @@ function BatchTestClient(config) { this.pendingJobs = []; this.ready = false; - this.batch.on('ready', function() { + this.batch.on('ready', function () { this.ready = true; - this.pendingJobs.forEach(function(pendingJob) { + this.pendingJobs.forEach(function (pendingJob) { this.createJob(pendingJob.job, pendingJob.override, pendingJob.callback); }.bind(this)); }.bind(this)); @@ -42,7 +41,7 @@ function BatchTestClient(config) { module.exports = BatchTestClient; -BatchTestClient.prototype.isReady = function() { +BatchTestClient.prototype.isReady = function () { return this.ready; }; @@ -50,7 +49,7 @@ BatchTestClient.prototype.getExpectedResponse = function (override) { return override.response || this.config.response || RESPONSE.CREATED; }; -BatchTestClient.prototype.createJob = function(job, override, callback) { +BatchTestClient.prototype.createJob = function (job, override, callback) { if (!callback) { callback = override; override = {}; @@ -90,7 +89,7 @@ BatchTestClient.prototype.createJob = function(job, override, callback) { ); }; -BatchTestClient.prototype.getJobStatus = function(jobId, override, callback) { +BatchTestClient.prototype.getJobStatus = function (jobId, override, callback) { assert.response( this.server, { @@ -112,7 +111,7 @@ BatchTestClient.prototype.getJobStatus = function(jobId, override, callback) { ); }; -BatchTestClient.prototype.getWorkInProgressJobs = function(override, callback) { +BatchTestClient.prototype.getWorkInProgressJobs = function (override, callback) { if (!callback) { callback = override; override = {}; @@ -137,7 +136,7 @@ BatchTestClient.prototype.getWorkInProgressJobs = function(override, callback) { ); }; -BatchTestClient.prototype.cancelJob = function(jobId, override, callback) { +BatchTestClient.prototype.cancelJob = function (jobId, override, callback) { assert.response( this.server, { @@ -157,13 +156,13 @@ BatchTestClient.prototype.cancelJob = function(jobId, override, callback) { ); }; -BatchTestClient.prototype.drain = function(callback) { - this.batch.stop(function() { +BatchTestClient.prototype.drain = function (callback) { + this.batch.stop(function () { return redisUtils.clean('batch:*', callback); }); }; -BatchTestClient.prototype.getHost = function(override) { +BatchTestClient.prototype.getHost = function (override) { return override.host || this.config.host || 'vizzuality.cartodb.com'; }; @@ -175,7 +174,7 @@ BatchTestClient.prototype.getAuthorization = function (override) { } }; -BatchTestClient.prototype.getUrl = function(override, jobId) { +BatchTestClient.prototype.getUrl = function (override, jobId) { var urlParts = ['/api/v2/sql/job']; if (jobId) { urlParts.push(jobId); @@ -183,20 +182,19 @@ BatchTestClient.prototype.getUrl = function(override, jobId) { return `${urlParts.join('/')}${override.anonymous ? '' : '?api_key=' + this.getApiKey(override)}`; }; -BatchTestClient.prototype.getApiKey = function(override) { +BatchTestClient.prototype.getApiKey = function (override) { return override.apiKey || this.config.apiKey || '1234'; }; -/****************** JobResult ******************/ +/** **************** JobResult ******************/ - -function JobResult(job, batchTestClient, override) { +function JobResult (job, batchTestClient, override) { this.job = job; this.batchTestClient = batchTestClient; this.override = override; } -JobResult.prototype.getStatus = function(requiredStatus, callback) { +JobResult.prototype.getStatus = function (requiredStatus, callback) { if (!callback) { callback = requiredStatus; requiredStatus = undefined; @@ -230,7 +228,7 @@ JobResult.prototype.getStatus = function(requiredStatus, callback) { }, 100); }; -function hasRequiredStatus(job, requiredStatus) { +function hasRequiredStatus (job, requiredStatus) { if (requiredStatus) { return job.status === requiredStatus; } @@ -275,10 +273,10 @@ JobResult.prototype.tryCancel = function (callback) { JobResult.prototype.validateExpectedResponse = function (expected) { var actual = this.job.query; - actual.query.forEach(function(actualQuery, index) { + actual.query.forEach(function (actualQuery, index) { var expectedQuery = expected.query[index]; assert.ok(expectedQuery); - Object.keys(expectedQuery).forEach(function(expectedKey) { + Object.keys(expectedQuery).forEach(function (expectedKey) { assert.equal( actualQuery[expectedKey], expectedQuery[expectedKey], @@ -288,7 +286,7 @@ JobResult.prototype.validateExpectedResponse = function (expected) { ); }); var propsToCheckDate = ['started_at', 'ended_at']; - propsToCheckDate.forEach(function(propToCheckDate) { + propsToCheckDate.forEach(function (propToCheckDate) { if (actualQuery.hasOwnProperty(propToCheckDate)) { assert.ok(new Date(actualQuery[propToCheckDate])); } diff --git a/test/support/redis-utils.js b/test/support/redis-utils.js index 63dc98d9..40808eaa 100644 --- a/test/support/redis-utils.js +++ b/test/support/redis-utils.js @@ -11,8 +11,8 @@ var redisConfig = { }; var metadataBackend = require('cartodb-redis')(redisConfig); -module.exports.clean = function clean(pattern, callback) { - metadataBackend.redisCmd(5, 'KEYS', [ pattern ], function (err, keys) { +module.exports.clean = function clean (pattern, callback) { + metadataBackend.redisCmd(5, 'KEYS', [pattern], function (err, keys) { if (err) { return callback(err); } @@ -25,15 +25,15 @@ module.exports.clean = function clean(pattern, callback) { }); }; -module.exports.getConfig = function getConfig() { +module.exports.getConfig = function getConfig () { return redisConfig; }; var pool = new RedisPool(redisConfig); -module.exports.getPool = function getPool() { +module.exports.getPool = function getPool () { return pool; }; -module.exports.configureUserMetadata = function configureUserMetadata(action, params, callback) { +module.exports.configureUserMetadata = function configureUserMetadata (action, params, callback) { metadataBackend.redisCmd(5, action, params, callback); -} +}; diff --git a/test/support/test-client.js b/test/support/test-client.js index 66bbc3aa..915cd01e 100644 --- a/test/support/test-client.js +++ b/test/support/test-client.js @@ -8,7 +8,7 @@ const step = require('step'); const PSQL = require('cartodb-psql'); const _ = require('underscore'); -function response(code) { +function response (code) { return { status: code }; @@ -19,16 +19,14 @@ var RESPONSE = { CREATED: response(201) }; - -function TestClient(config) { +function TestClient (config) { this.config = config || {}; this.server = appServer(); } module.exports = TestClient; - -TestClient.prototype.getResult = function(query, override, callback) { +TestClient.prototype.getResult = function (query, override, callback) { if (!callback) { callback = override; override = {}; @@ -66,7 +64,7 @@ TestClient.prototype.getResult = function(query, override, callback) { ); }; -TestClient.prototype.getHost = function(override) { +TestClient.prototype.getHost = function (override) { return override.host || this.config.host || 'vizzuality.cartodb.com'; }; @@ -78,15 +76,15 @@ TestClient.prototype.getAuthorization = function (override) { } }; -TestClient.prototype.getContentType = function(override) { +TestClient.prototype.getContentType = function (override) { return override['Content-Type'] || this.config['Content-Type'] || 'application/json'; }; TestClient.prototype.getParser = function (override) { - return override.parser || this.config.parser || JSON.stringify -} + return override.parser || this.config.parser || JSON.stringify; +}; -TestClient.prototype.getUrl = function(override) { +TestClient.prototype.getUrl = function (override) { if (override.anonymous) { return '/api/v1/sql?'; } @@ -119,8 +117,8 @@ TestClient.prototype.setUserRenderTimeoutLimit = function (user, userTimeoutLimi TestClient.prototype.setUserDatabaseTimeoutLimit = function (user, timeoutLimit, callback) { const dbname = _.template(global.settings.db_base_name, { user_id: 1 }); - const dbuser = _.template(global.settings.db_user, { user_id: 1 }) - const pass = _.template(global.settings.db_user_pass, { user_id: 1 }) + const dbuser = _.template(global.settings.db_user, { user_id: 1 }); + const pass = _.template(global.settings.db_user_pass, { user_id: 1 }); const publicuser = global.settings.db_pubuser; const psql = new PSQL({ diff --git a/test/unit/apikeyauth-test.js b/test/unit/apikeyauth-test.js index 1fab72e5..d4ae985b 100644 --- a/test/unit/apikeyauth-test.js +++ b/test/unit/apikeyauth-test.js @@ -5,37 +5,36 @@ require('../helper'); var ApikeyAuth = require('../../lib/auth/apikey'); var assert = require('assert'); -describe.skip('has credentials', function() { - +describe.skip('has credentials', function () { var noCredentialsRequests = [ { des: 'there is not api_key/map_key in the request query', - req: {query:{}} + req: { query: {} } }, { des: 'api_key is undefined`ish in the request query', - req: {query:{api_key:null}} + req: { query: { api_key: null } } }, { des: 'map_key is undefined`ish in the request query', - req: {query:{map_key:null}} + req: { query: { map_key: null } } }, { des: 'there is not api_key/map_key in the request body', - req: {query:{}, body:{}} + req: { query: {}, body: {} } }, { des: 'api_key is undefined`ish in the request body', - req: {query:{}, body:{api_key:null}} + req: { query: {}, body: { api_key: null } } }, { des: 'map_key is undefined`ish in the request body', - req: {query:{}, body:{map_key:null}} + req: { query: {}, body: { map_key: null } } } ]; - noCredentialsRequests.forEach(function(request) { - it('has no credentials if ' + request.des, function() { + noCredentialsRequests.forEach(function (request) { + it('has no credentials if ' + request.des, function () { testCredentials(request.req, false); }); }); @@ -43,51 +42,48 @@ describe.skip('has credentials', function() { var credentialsRequests = [ { des: 'there is api_key in the request query', - req: {query:{api_key: 'foo'}} + req: { query: { api_key: 'foo' } } }, { des: 'there is api_key in the request query', - req: {query:{map_key: 'foo'}} + req: { query: { map_key: 'foo' } } }, { des: 'there is api_key in the request body', - req: {query:{}, body:{api_key:'foo'}} + req: { query: {}, body: { api_key: 'foo' } } }, { des: 'there is map_key in the request body', - req: {query:{}, body:{map_key:'foo'}} + req: { query: {}, body: { map_key: 'foo' } } } ]; - credentialsRequests.forEach(function(request) { - it('has credentials if ' + request.des, function() { + credentialsRequests.forEach(function (request) { + it('has credentials if ' + request.des, function () { testCredentials(request.req, true); }); }); - function testCredentials(req, hasCredentials) { + function testCredentials (req, hasCredentials) { var apiKeyAuth = new ApikeyAuth(req); assert.equal(apiKeyAuth.hasCredentials(), hasCredentials); } - }); -describe.skip('verifyCredentials', function() { - - it('callbacks with true value when request api_key is the same', function(done) { - testVerifyCredentials({query:{api_key: 'foo'}}, {apiKey: 'foo'}, true, done); +describe.skip('verifyCredentials', function () { + it('callbacks with true value when request api_key is the same', function (done) { + testVerifyCredentials({ query: { api_key: 'foo' } }, { apiKey: 'foo' }, true, done); }); - it('callbacks with false value when request api_key is different', function(done) { - testVerifyCredentials({query:{api_key: 'foo'}}, {apiKey: 'bar'}, false, done); + it('callbacks with false value when request api_key is different', function (done) { + testVerifyCredentials({ query: { api_key: 'foo' } }, { apiKey: 'bar' }, false, done); }); - function testVerifyCredentials(req, options, shouldBeValid, done) { + function testVerifyCredentials (req, options, shouldBeValid, done) { var apiKeyAuth = new ApikeyAuth(req); - apiKeyAuth.verifyCredentials(options, function(err, validCredentials) { + apiKeyAuth.verifyCredentials(options, function (err, validCredentials) { assert.equal(validCredentials, shouldBeValid); done(); }); } - }); diff --git a/test/unit/batch/job-publisher-test.js b/test/unit/batch/job-publisher-test.js index 58817213..90f2d011 100644 --- a/test/unit/batch/job-publisher-test.js +++ b/test/unit/batch/job-publisher-test.js @@ -35,5 +35,4 @@ describe('batch API job publisher', function () { this.jobPublisher.publish(this.host); assert.ok(this.redis.publishIsCalledWithValidArgs); }); - }); diff --git a/test/unit/batch/job-queue-test.js b/test/unit/batch/job-queue-test.js index 29b4e43f..19d0c554 100644 --- a/test/unit/batch/job-queue-test.js +++ b/test/unit/batch/job-queue-test.js @@ -7,13 +7,13 @@ describe('batch API job queue', function () { beforeEach(function () { this.metadataBackend = { redisCmd: function () { - var callback = arguments[arguments.length -1]; + var callback = arguments[arguments.length - 1]; process.nextTick(function () { callback(null, 'irrelevantJob'); }); }, redisMultiCmd: function () { - var callback = arguments[arguments.length -1]; + var callback = arguments[arguments.length - 1]; process.nextTick(function () { callback(null, 'irrelevantJob'); }); diff --git a/test/unit/batch/job-subscriber-test.js b/test/unit/batch/job-subscriber-test.js index 2c3a192a..b5e593d9 100644 --- a/test/unit/batch/job-subscriber-test.js +++ b/test/unit/batch/job-subscriber-test.js @@ -26,7 +26,7 @@ describe('batch API job subscriber', function () { var isValidFirstArg = arguments[0] === Channel.NAME; self.redis.unsubscribeIsCalledWithValidArgs = isValidFirstArg; }, - scan: function(params, callback) { + scan: function (params, callback) { return callback(null, ['0']); }, removeAllListeners: function () { @@ -35,13 +35,13 @@ describe('batch API job subscriber', function () { smembers: function (key, callback) { callback(null, []); }, - connected: true, + connected: true }; this.pool = { acquire: function (db, cb) { cb(null, self.redis); }, - release: function(/*db, client*/) { + release: function (/* db, client */) { } }; @@ -67,5 +67,4 @@ describe('batch API job subscriber', function () { this.jobSubscriber.unsubscribe(); assert.ok(this.redis.unsubscribeIsCalledWithValidArgs); }); - }); diff --git a/test/unit/error-handler-factory-test.js b/test/unit/error-handler-factory-test.js index c1d988b5..82e4f3d1 100644 --- a/test/unit/error-handler-factory-test.js +++ b/test/unit/error-handler-factory-test.js @@ -5,7 +5,7 @@ const errorHandlerFactory = require('../../lib/services/error-handler-factory'); const ErrorHandler = require('../../lib/services/error-handler'); const { codeToCondition } = require('../../lib/postgresql/error-codes'); -let rateLimitError = new Error( +const rateLimitError = new Error( 'You are over platform\'s limits. Please contact us to know more details' ); rateLimitError.http_status = 429; @@ -40,7 +40,7 @@ describe('error-handler-factory', function () { }); }); - it('timeout error', function() { + it('timeout error', function () { const error = new Error('statement timeout'); const errorHandler = errorHandlerFactory(error); const expectedError = new ErrorHandler({ @@ -54,7 +54,7 @@ describe('error-handler-factory', function () { assert.deepEqual(errorHandler, expectedError); }); - it('permission denied error', function() { + it('permission denied error', function () { const error = new Error('permission denied'); const errorHandler = errorHandlerFactory(error); const expectedError = new ErrorHandler({ diff --git a/test/unit/error-handler-test.js b/test/unit/error-handler-test.js index 6f89f95f..13e9e115 100644 --- a/test/unit/error-handler-test.js +++ b/test/unit/error-handler-test.js @@ -34,10 +34,9 @@ const getErrorHeader = (context, detail, hint, message) => { }; }; -describe('error-handler', function() { +describe('error-handler', function () { it('should return a header with errors', function (done) { - - let error = new Error('error test'); + const error = new Error('error test'); error.detail = 'test detail'; error.hint = 'test hint'; error.context = 'test context'; @@ -63,7 +62,7 @@ describe('error-handler', function() { }); it('JSONP should return a header with error statuscode', function (done) { - let error = new Error('error test'); + const error = new Error('error test'); error.detail = 'test detail'; error.hint = 'test hint'; error.context = 'test context'; @@ -92,7 +91,7 @@ describe('error-handler', function() { const badString = 'error: ( ) = " \" \' * $ & |'; const escapedString = 'error '; - let error = new Error(badString); + const error = new Error(badString); error.detail = badString; error.hint = badString; error.context = badString; @@ -121,12 +120,12 @@ describe('error-handler', function() { const veryLongString = 'Very long error message '.repeat(1000); const truncatedString = veryLongString.substring(0, 1024); - let error = new Error(veryLongString); + const error = new Error(veryLongString); const expectedErrorHeader = { statusCode: 400, message: truncatedString - }; + }; const res = getRes(); diff --git a/test/unit/health-check-test.js b/test/unit/health-check-test.js index d776a795..ea2bbd51 100644 --- a/test/unit/health-check-test.js +++ b/test/unit/health-check-test.js @@ -2,46 +2,44 @@ require('../helper'); -var assert = require('assert'); +var assert = require('assert'); var HealthCheck = require('../../lib/monitoring/health-check'); var metadataBackend = {}; -function PSQL(dbParams) { +function PSQL (dbParams) { this.params = dbParams; } var healthCheck = new HealthCheck(metadataBackend, PSQL); -describe('health checks', function() { +describe('health checks', function () { + it('errors if disabled file exists', function (done) { + var fs = require('fs'); - 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(function(err) { - assert.equal(err.message, "Maintenance"); - assert.equal(err.http_status, 503); - fs.readFile = readFileFn; - done(); - }); + var readFileFn = fs.readFile; + fs.readFile = function (filename, callback) { + callback(null, 'Maintenance'); + }; + healthCheck.check(function (err) { + assert.equal(err.message, 'Maintenance'); + assert.equal(err.http_status, 503); + fs.readFile = readFileFn; + done(); + }); }); - it('does not err if disabled file does not exists', function(done) { - var fs = require('fs'); + 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(function(err) { - assert.equal(err, null); - fs.readFile = readFileFn; - done(); - }); + var readFileFn = fs.readFile; + fs.readFile = function (filename, callback) { + callback(new Error('ENOENT'), null); + }; + healthCheck.check(function (err) { + assert.equal(err, null); + fs.readFile = readFileFn; + done(); + }); }); - }); diff --git a/test/unit/model/bin-encoder-test.js b/test/unit/model/bin-encoder-test.js index a9b4dd8d..73adfa9d 100644 --- a/test/unit/model/bin-encoder-test.js +++ b/test/unit/model/bin-encoder-test.js @@ -5,72 +5,69 @@ var assert = require('assert'); var ArrayBufferSer = require('../../../lib/models/bin-encoder'); -describe('ArrayBufferSer', function() { +describe('ArrayBufferSer', function () { + it('calculate size for basic types', function () { + var b = new ArrayBufferSer(ArrayBufferSer.INT16, [1, 2, 3, 4]); + assert.equal(4 * 2, b.getDataSize()); - 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, 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]); + assert.equal(4 * 4, b.getDataSize()); + }); - b = new ArrayBufferSer(ArrayBufferSer.INT32, [1,2,3,4]); - assert.equal(4*4, b.getDataSize()); - }); + 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]); - it('calculate size for arrays', function() { - var b = new ArrayBufferSer(ArrayBufferSer.STRING, ["test","kease"]); - assert.equal((b.headerSize + 4 + 5)*2, b.getDataSize()); + b = new ArrayBufferSer(ArrayBufferSer.BUFFER, [ba, bc]); + assert.equal((b.headerSize + 4 + 2) * 2, b.getDataSize()); + assert.equal(b.type, ArrayBufferSer.BUFFER); + }); - 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]); - assert.equal((b.headerSize + 4 + 2)*2, b.getDataSize()); - assert.equal(b.type, ArrayBufferSer.BUFFER); - }); - - function assert_buffer_equals(a, b) { - assert.equal(a.length, b.length); - for(var i = 0; i < a.length; ++i) { - assert.equal(a[i], b[i], "byte i " + i + " is different: " + a[i] + " != " + b[i]); + function assert_buffer_equals (a, b) { + assert.equal(a.length, b.length); + for (var i = 0; i < a.length; ++i) { + assert.equal(a[i], b[i], 'byte i ' + i + ' is different: ' + a[i] + ' != ' + b[i]); + } } - } - 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); - }); + 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); + }); - 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]); + 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 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]); - assert_buffer_equals(bf, b.buffer); - }); - - 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, - 0, 0, 0, ArrayBufferSer.UINT16, - 0, 0, 0, 8, - s.charCodeAt(0), 0, - s.charCodeAt(1), 0, - s.charCodeAt(2), 0, - s.charCodeAt(3), 0 - ]); - assert_buffer_equals(bf, b.buffer); - }); + 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]); + assert_buffer_equals(bf, b.buffer); + }); + 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, + 0, 0, 0, ArrayBufferSer.UINT16, + 0, 0, 0, 8, + s.charCodeAt(0), 0, + s.charCodeAt(1), 0, + s.charCodeAt(2), 0, + s.charCodeAt(3), 0 + ]); + assert_buffer_equals(bf, b.buffer); + }); }); diff --git a/test/unit/oauth-test.js b/test/unit/oauth-test.js index e90b7133..fcb6d17e 100644 --- a/test/unit/oauth-test.js +++ b/test/unit/oauth-test.js @@ -8,14 +8,14 @@ var MetadataDB = require('cartodb-redis'); var oAuth = require('../../lib/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_consumer_key: 'dpf43f3p2l4k3l03', + oauth_token: 'nnch734d00sl2jdk', + oauth_signature_method: 'HMAC-SHA1', + oauth_signature: 'tR3%2BTy81lMeYAr%2FFid0kMTYa%2FWM%3D', + oauth_timestamp: '1191242096', + oauth_nonce: 'kllo9940pd9333jh' }; -var oauth_data_2 = { oauth_version:"1.0" }; +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/",' + @@ -43,134 +43,134 @@ var metadataBackend = new MetadataDB({ reapIntervalMillis: global.settings.redisReapIntervalMillis }); -describe('oauth', function() { +describe('oauth', function () { + it('test database number', function () { + assert.equal(oAuth.oauth_database, 3); + }); -it('test database number', function(){ - assert.equal(oAuth.oauth_database, 3); -}); + it('test oauth database key', function () { + assert.equal(oAuth.oauth_user_key, 'rails:oauth_access_tokens:<%= oauth_access_key %>'); + }); -it('test oauth database key', function(){ - assert.equal(oAuth.oauth_user_key, "rails:oauth_access_tokens:<%= oauth_access_key %>"); -}); + 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/); + }); -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/); -}); + 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/); + }); -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/); -}); + 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'); + }); -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"); -}); + 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); -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) { + assert.equal(tokens.oauth_consumer_key, data.consumer_key); + done(); + }); + }); - oAuth.getOAuthHash(metadataBackend, tokens.oauth_token, function(err, data){ - assert.equal(tokens.oauth_consumer_key, data.consumer_key); + it('test non existant oauth hash for a user based on oauth_token returns empty hash', function (done) { + var req = { query: {}, params: { user: 'vizzuality' }, headers: { authorization: full_oauth_header } }; + var tokens = oAuth.parseTokens(req); + + oAuth.getOAuthHash(metadataBackend, tokens.oauth_token, function (err, data) { + assert.ok(!err, err); + assert.deepEqual(data, {}); + done(); + }); + }); + + it('can return user for verified signature', function (done) { + var req = { + query: {}, + headers: { authorization: real_oauth_header, host: 'vizzuality.testhost.lan' }, + params: { user: 'vizzuality' }, + protocol: 'http', + method: 'GET', + path: '/api/v1/tables' + }; + + oAuth.verifyRequest(req, metadataBackend, function (err, data) { + assert.ok(!err, err); + assert.equal(data, 'master'); + done(); + }); + }); + + it('can return user for verified signature (for other allowed domains)', function (done) { + var oAuthGetAllowedHostsFn = oAuth.getAllowedHosts; + oAuth.getAllowedHosts = function () { + return ['testhost.lan', 'testhostdb.lan']; + }; + var req = { + query: {}, + headers: { authorization: real_oauth_header, host: 'vizzuality.testhostdb.lan' }, + params: { user: 'vizzuality' }, + protocol: 'http', + method: 'GET', + path: '/api/v1/tables' + }; + + oAuth.verifyRequest(req, metadataBackend, function (err, data) { + oAuth.getAllowedHosts = oAuthGetAllowedHostsFn; + assert.ok(!err, err); + assert.equal(data, 'master'); + done(); + }); + }); + + it('returns null user for unverified signatures', function (done) { + var req = { + query: {}, + headers: { authorization: real_oauth_header, host: 'vizzuality.testyhost.lan' }, + params: { user: 'vizzuality' }, + protocol: 'http', + method: 'GET', + path: '/api/v1/tables' + }; + + oAuth.verifyRequest(req, metadataBackend, function (err, data) { + assert.equal(data, null); + done(); + }); + }); + + it('returns null user for no oauth', function (done) { + var req = { + query: {}, + headers: {}, + params: { user: 'vizzuality' }, + protocol: 'http', + method: 'GET', + path: '/api/v1/tables' + }; + + oAuth.verifyRequest(req, metadataBackend, function (err, data) { + assert.equal(data, null); + 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(); + }); + + it('OAuthAuth reports it has no credentials', function (done) { + var req = { query: {}, headers: {} }; + var oAuthAuth = new OAuthAuth(req); + assert.equal(oAuthAuth.hasCredentials(), false); done(); }); }); - -it('test non existant oauth hash for a user based on oauth_token returns empty hash', function(done){ - var req = {query:{}, params: { user: 'vizzuality' }, headers:{authorization:full_oauth_header}}; - var tokens = oAuth.parseTokens(req); - - oAuth.getOAuthHash(metadataBackend, tokens.oauth_token, function(err, data){ - assert.ok(!err, err); - assert.deepEqual(data, {}); - done(); - }); -}); - -it('can return user for verified signature', function(done){ - var req = {query:{}, - headers:{authorization:real_oauth_header, host: 'vizzuality.testhost.lan' }, - params: { user: 'vizzuality' }, - protocol: 'http', - method: 'GET', - path: '/api/v1/tables' - }; - - oAuth.verifyRequest(req, metadataBackend, function(err, data){ - assert.ok(!err, err); - assert.equal(data, 'master'); - done(); - }); -}); - -it('can return user for verified signature (for other allowed domains)', function(done){ - var oAuthGetAllowedHostsFn = oAuth.getAllowedHosts; - oAuth.getAllowedHosts = function() { - return ['testhost.lan', 'testhostdb.lan']; - }; - var req = {query:{}, - headers:{authorization:real_oauth_header, host: 'vizzuality.testhostdb.lan' }, - params: { user: 'vizzuality' }, - protocol: 'http', - method: 'GET', - path: '/api/v1/tables' - }; - - oAuth.verifyRequest(req, metadataBackend, function(err, data){ - oAuth.getAllowedHosts = oAuthGetAllowedHostsFn; - assert.ok(!err, err); - assert.equal(data, 'master'); - done(); - }); -}); - -it('returns null user for unverified signatures', function(done){ - var req = {query:{}, - headers:{authorization:real_oauth_header, host: 'vizzuality.testyhost.lan' }, - params: { user: 'vizzuality' }, - protocol: 'http', - method: 'GET', - path: '/api/v1/tables' - }; - - oAuth.verifyRequest(req, metadataBackend, function(err, data){ - assert.equal(data, null); - done(); - }); -}); - -it('returns null user for no oauth', function(done){ - var req = { - query:{}, - headers:{}, - params: { user: 'vizzuality' }, - protocol: 'http', - method: 'GET', - path: '/api/v1/tables' - }; - - oAuth.verifyRequest(req, metadataBackend, function(err,data){ - assert.equal(data, null); - 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(); -}); - -it('OAuthAuth reports it has no credentials', function(done) { - var req = {query:{}, headers:{}}; - var oAuthAuth = new OAuthAuth(req); - assert.equal(oAuthAuth.hasCredentials(), false); - done(); -}); - - -}); diff --git a/test/unit/pg-entities-access-validator-test.js b/test/unit/pg-entities-access-validator-test.js index f859b04b..bdcd8a7c 100644 --- a/test/unit/pg-entities-access-validator-test.js +++ b/test/unit/pg-entities-access-validator-test.js @@ -71,14 +71,12 @@ const fakeAffectedTablesTopologyKO = [ } ]; - - describe('pg entities access validator with validatePGEntitiesAccess enabled', function () { - before(function() { + before(function () { global.settings.validatePGEntitiesAccess = true; }); - after(function() { + after(function () { global.settings.validatePGEntitiesAccess = false; }); @@ -125,7 +123,6 @@ describe('pg entities access validator with validatePGEntitiesAccess enabled', f false ); - authorizationLevel = 'regular'; assert.strictEqual( pgEntitiesAccessValidator.validate({ tables: fakeAffectedTablesCarto }, authorizationLevel), @@ -171,5 +168,4 @@ describe('pg entities access validator with validatePGEntitiesAccess enabled', f assert.strictEqual(pgEntitiesAccessValidator.softValidation(fakeAffectedTablesCartodbKO), true); assert.strictEqual(pgEntitiesAccessValidator.softValidation(fakeAffectedTablesPgcatalog), false); }); - }); diff --git a/test/unit/query-info-test.js b/test/unit/query-info-test.js index d797d8ec..715197f9 100644 --- a/test/unit/query-info-test.js +++ b/test/unit/query-info-test.js @@ -10,53 +10,53 @@ describe('query info', function () { "COPY copy_endpoints_test (id, name) FROM STDIN WITH (FORMAT CSV, DELIMITER ',', HEADER true)", "COPY copy_endpoints_test (id, name) FROM STDIN WITH (FORMAT CSV, DELIMITER ',', HEADER true)", "COPY copy_endpoints_test (id, name) FROM STDIN WITH (FORMAT CSV , DELIMITER ',', HEADER true)", - "COPY copy_endpoints_test (id, name) FROM STDIN WITH (FORMAT CSV)", - "COPY copy_endpoints_test FROM STDIN WITH(FORMAT csv,HEADER true)" + 'COPY copy_endpoints_test (id, name) FROM STDIN WITH (FORMAT CSV)', + 'COPY copy_endpoints_test FROM STDIN WITH(FORMAT csv,HEADER true)' ]; validQueries.forEach(query => { - it(query, function() { + it(query, function () { const result = queryInfo.getFormatFromCopyQuery(query); assert.equal(result, 'CSV'); }); }); }); - describe('text', function() { + describe('text', function () { const validQueries = [ - "COPY copy_endpoints_test (id, name) FROM STDIN WITH (FORMAT TEXT)", - "COPY copy_endpoints_test (id, name) FROM STDIN", + 'COPY copy_endpoints_test (id, name) FROM STDIN WITH (FORMAT TEXT)', + 'COPY copy_endpoints_test (id, name) FROM STDIN' ]; validQueries.forEach(query => { - it(query, function() { + it(query, function () { const result = queryInfo.getFormatFromCopyQuery(query); assert.equal(result, 'TEXT'); }); }); }); - describe('binary', function() { + describe('binary', function () { const validQueries = [ - "COPY copy_endpoints_test (id, name) FROM STDIN WITH (FORMAT BINARY)", + 'COPY copy_endpoints_test (id, name) FROM STDIN WITH (FORMAT BINARY)' ]; validQueries.forEach(query => { - it(query, function() { + it(query, function () { const result = queryInfo.getFormatFromCopyQuery(query); assert.equal(result, 'BINARY'); }); }); }); - describe('should fail', function() { + describe('should fail', function () { const validQueries = [ - "COPY copy_endpoints_test (id, name) FROM STDIN WITH (FORMAT ERROR)", - "SELECT * from copy_endpoints_test" + 'COPY copy_endpoints_test (id, name) FROM STDIN WITH (FORMAT ERROR)', + 'SELECT * from copy_endpoints_test' ]; validQueries.forEach(query => { - it(query, function() { + it(query, function () { const result = queryInfo.getFormatFromCopyQuery(query); assert.strictEqual(result, false); }); diff --git a/test/websocket_test/public/gmaps_mercator.js b/test/websocket_test/public/gmaps_mercator.js index a236508a..109e4b54 100644 --- a/test/websocket_test/public/gmaps_mercator.js +++ b/test/websocket_test/public/gmaps_mercator.js @@ -1,56 +1,56 @@ 'use strict'; - function Point(x, y) { - this.x = x; - this.y = y; - } +function Point (x, y) { + this.x = x; + this.y = y; +} - /** return a copy of this point with coordinates as int */ - Point.prototype.floor = function() { - return new Point(this.x>>0, this.y>>0); - } +/** return a copy of this point with coordinates as int */ +Point.prototype.floor = function () { + return new Point(this.x >> 0, this.y >> 0); +}; - function LatLng(lat, lng) { - this.lat = lat; - this.lng = lng; - } +function LatLng (lat, lng) { + this.lat = lat; + this.lng = lng; +} - LatLng.prototype.clone = function() { - return new LatLng(this.lat, this.lng); - } +LatLng.prototype.clone = function () { + return new LatLng(this.lat, this.lng); +}; - var TILE_SIZE = 256; +var TILE_SIZE = 256; - MercatorProjection.prototype.TILE_SIZE = TILE_SIZE; +MercatorProjection.prototype.TILE_SIZE = TILE_SIZE; - function bound(value, opt_min, opt_max) { +function bound (value, opt_min, opt_max) { if (opt_min != null) value = Math.max(value, opt_min); if (opt_max != null) value = Math.min(value, opt_max); return value; - } +} - function degreesToRadians(deg) { +function degreesToRadians (deg) { return deg * (Math.PI / 180); - } +} - function radiansToDegrees(rad) { +function radiansToDegrees (rad) { return rad / (Math.PI / 180); - } +} - function MercatorProjection() { +function MercatorProjection () { this.pixelOrigin_ = new Point(TILE_SIZE / 2, TILE_SIZE / 2); this.pixelsPerLonDegree_ = TILE_SIZE / 360; this.pixelsPerLonRadian_ = TILE_SIZE / (2 * Math.PI); - } +} - MercatorProjection.prototype.fromLatLngToPixel = function(latLng, zoom) { - var p = this.fromLatLngToPoint(latLng); - return this.toPixelCoordinate(p, zoom); - }; +MercatorProjection.prototype.fromLatLngToPixel = function (latLng, zoom) { + var p = this.fromLatLngToPoint(latLng); + return this.toPixelCoordinate(p, zoom); +}; - MercatorProjection.prototype.fromLatLngToPoint = function(latLng, - opt_point) { +MercatorProjection.prototype.fromLatLngToPoint = function (latLng, + opt_point) { var me = this; var point = opt_point || new Point(0, 0); var origin = me.pixelOrigin_; @@ -65,9 +65,9 @@ point.y = origin.y + 0.5 * Math.log((1 + siny) / (1 - siny)) * -me.pixelsPerLonRadian_; return point; - }; +}; - MercatorProjection.prototype.fromPointToLatLng = function(point) { +MercatorProjection.prototype.fromPointToLatLng = function (point) { var me = this; var origin = me.pixelOrigin_; var lng = (point.x - origin.x) / me.pixelsPerLonDegree_; @@ -75,77 +75,75 @@ var lat = radiansToDegrees(2 * Math.atan(Math.exp(latRadians)) - Math.PI / 2); return new LatLng(lat, lng); - }; +}; - MercatorProjection.prototype.tileBBox = function(x, y, zoom) { +MercatorProjection.prototype.tileBBox = function (x, y, zoom) { var numTiles = 1 << zoom; - var inc = TILE_SIZE/numTiles; - var px = x*TILE_SIZE/numTiles; - var py = y*TILE_SIZE/numTiles; + var inc = TILE_SIZE / numTiles; + var px = x * TILE_SIZE / numTiles; + var py = y * TILE_SIZE / numTiles; return [ this.fromPointToLatLng(new Point(px, py + inc)), this.fromPointToLatLng(new Point(px + inc, py)) ]; - }; +}; - MercatorProjection.prototype.tilePoint = function(x, y, zoom) { - var numTiles = 1 << zoom; - var px = x*TILE_SIZE; - var py = y*TILE_SIZE; - return [px, py]; - } - MercatorProjection.prototype.fromPixelToLatLng = function(pixel, zoom) { - var numTiles = 1 << zoom; - var p = new Point( - pixel.x/numTiles, - pixel.y/numTiles); - return this.fromPointToLatLng(p); +MercatorProjection.prototype.tilePoint = function (x, y, zoom) { + var numTiles = 1 << zoom; + var px = x * TILE_SIZE; + var py = y * TILE_SIZE; + return [px, py]; +}; +MercatorProjection.prototype.fromPixelToLatLng = function (pixel, zoom) { + var numTiles = 1 << zoom; + var p = new Point( + pixel.x / numTiles, + pixel.y / numTiles); + return this.fromPointToLatLng(p); +}; +MercatorProjection.prototype.toPixelCoordinate = function (worldCoordinate, zoom) { + var numTiles = 1 << zoom; + return new Point( + worldCoordinate.x * numTiles, + worldCoordinate.y * numTiles); +}; - } +MercatorProjection.prototype.latLngToTilePoint = function (latLng, x, y, zoom) { + var numTiles = 1 << zoom; + var projection = this; + var worldCoordinate = projection.fromLatLngToPoint(latLng); + var pixelCoordinate = new Point( + worldCoordinate.x * numTiles, + worldCoordinate.y * numTiles); + var tp = this.tilePoint(x, y, zoom); + return new Point( + Math.floor(pixelCoordinate.x - tp[0]), + Math.floor(pixelCoordinate.y - tp[1])); +}; - MercatorProjection.prototype.toPixelCoordinate = function(worldCoordinate, zoom) { - var numTiles = 1 << zoom; - return new Point( - worldCoordinate.x * numTiles, - worldCoordinate.y * numTiles); - } +MercatorProjection.prototype.pixelToTile = function (pixelCoordinate) { + return new Point( + Math.floor(pixelCoordinate.x / TILE_SIZE), + Math.floor(pixelCoordinate.y / TILE_SIZE)); +}; - MercatorProjection.prototype.latLngToTilePoint = function(latLng, x, y, zoom) { - var numTiles = 1 << zoom; - var projection = this; - var worldCoordinate = projection.fromLatLngToPoint(latLng); - var pixelCoordinate = new Point( - worldCoordinate.x * numTiles, - worldCoordinate.y * numTiles); - var tp = this.tilePoint(x, y, zoom); - return new Point( - Math.floor(pixelCoordinate.x - tp[0]), - Math.floor(pixelCoordinate.y - tp[1])); - } +MercatorProjection.prototype.pointToTile = function (point, zoom) { + var numTiles = 1 << zoom; + var pixelCoordinate = new Point( + point.x * numTiles, + point.y * numTiles); + return this.pixelToTile(pixelCoordinate); +}; - MercatorProjection.prototype.pixelToTile = function(pixelCoordinate) { - return new Point( - Math.floor(pixelCoordinate.x / TILE_SIZE), - Math.floor(pixelCoordinate.y / TILE_SIZE)); - }; - - MercatorProjection.prototype.pointToTile = function(point, zoom) { - var numTiles = 1 << zoom; - var pixelCoordinate = new Point( - point.x * numTiles, - point.y * numTiles); - return this.pixelToTile(pixelCoordinate); - }; - - MercatorProjection.prototype.latLngToTile = function(latLng, zoom) { - var numTiles = 1 << zoom; - var projection = this; - var worldCoordinate = projection.fromLatLngToPoint(latLng); - var pixelCoordinate = new Point( - worldCoordinate.x * numTiles, - worldCoordinate.y * numTiles); - return new Point( - Math.floor(pixelCoordinate.x / TILE_SIZE), - Math.floor(pixelCoordinate.y / TILE_SIZE)); - } +MercatorProjection.prototype.latLngToTile = function (latLng, zoom) { + var numTiles = 1 << zoom; + var projection = this; + var worldCoordinate = projection.fromLatLngToPoint(latLng); + var pixelCoordinate = new Point( + worldCoordinate.x * numTiles, + worldCoordinate.y * numTiles); + return new Point( + Math.floor(pixelCoordinate.x / TILE_SIZE), + Math.floor(pixelCoordinate.y / TILE_SIZE)); +}; diff --git a/test/websocket_test/public/map.js b/test/websocket_test/public/map.js index 04bdd26d..aea44a16 100644 --- a/test/websocket_test/public/map.js +++ b/test/websocket_test/public/map.js @@ -1,67 +1,65 @@ 'use strict'; -window.requestAnimFrame = (function(){ - return window.requestAnimationFrame || +window.requestAnimFrame = (function () { + return window.requestAnimationFrame || window.webkitRequestAnimationFrame || - window.mozRequestAnimationFrame || - window.oRequestAnimationFrame || - window.msRequestAnimationFrame || - function( callback ){ - window.setTimeout(callback, 1000 / 60); + window.mozRequestAnimationFrame || + window.oRequestAnimationFrame || + window.msRequestAnimationFrame || + function (callback) { + window.setTimeout(callback, 1000 / 60); }; - })(); +})(); -function Event() {} -Event.prototype.on = function(evt, callback) { +function Event () {} +Event.prototype.on = function (evt, callback) { var cb = this.callbacks = this.callbacks || {}; var l = cb[evt] || (cb[evt] = []); l.push(callback); }; -Event.prototype.emit = function(evt) { +Event.prototype.emit = function (evt) { var c = this.callbacks && this.callbacks[evt]; - for(var i = 0; c && i < c.length; ++i) { + for (var i = 0; c && i < c.length; ++i) { c[i].apply(this, Array.prototype.slice.call(arguments, 1)); } }; - - -function MapModel(opts) { +function MapModel (opts) { opts = opts || {}; this.projection = new MercatorProjection(); - this.setCenter(opts.center || new LatLng(0,0)); + this.setCenter(opts.center || new LatLng(0, 0)); this.setZoom(opts.zoom || 1); } MapModel.prototype = new Event(); -MapModel.prototype.setCenter = function(center) { +MapModel.prototype.setCenter = function (center) { this.center = new LatLng(center.lat, center.lng); this.center_pixel = this.projection.fromLatLngToPixel(this.center, this.zoom).floor(); this.emit('center_changed', this.center); }; -MapModel.prototype.setZoom = function(zoom) { +MapModel.prototype.setZoom = function (zoom) { this.zoom = zoom; this.center_pixel = this.projection.fromLatLngToPixel(this.center, this.zoom).floor(); this.emit('zoom_changed', this.center); }; -MapModel.prototype.getCenterPixel = function() { +MapModel.prototype.getCenterPixel = function () { var center_point = this.projection.fromLatLngToPixel(this.center, this.zoom); return center_point; -} +}; -MapModel.prototype.getTopLeft = function(width, height) { +MapModel.prototype.getTopLeft = function (width, height) { var center_point = this.projection.fromLatLngToPixel(this.center, this.zoom); var widthHalf = width / 2; var heightHalf = height / 2; center_point.x -= widthHalf; center_point.y -= heightHalf; return center_point; -} -MapModel.prototype.getBBox = function(width, height) { +}; +MapModel.prototype.getBBox = function (width, height) { var center_point = this.projection.fromLatLngToPixel(this.center, this.zoom); var widthHalf = width / 2; var heightHalf = height / 2; @@ -71,16 +69,14 @@ MapModel.prototype.getBBox = function(width, height) { center_point.x += width; center_point.y -= height; var topRight = this.projection.fromPixelToLatLng(center_point, this.zoom); - return [bottomleft, topRight] - -} - + return [bottomleft, topRight]; +}; /** * return a list of tiles inside the spcified zone * the center will be placed on the center of that zone */ -MapModel.prototype.visibleTiles = function(width, height) { +MapModel.prototype.visibleTiles = function (width, height) { var self = this; var widthHalf = width / 2; var heightHalf = height / 2; @@ -88,15 +84,15 @@ MapModel.prototype.visibleTiles = function(width, height) { center_point.x -= widthHalf; center_point.y -= heightHalf; var tile = this.projection.pixelToTile(center_point, self.zoom); - var offset_x = center_point.x%this.projection.TILE_SIZE; - var offset_y = center_point.y%this.projection.TILE_SIZE; + var offset_x = center_point.x % this.projection.TILE_SIZE; + var offset_y = center_point.y % this.projection.TILE_SIZE; - var num_tiles_x = Math.ceil((width + offset_x)/this.projection.TILE_SIZE); - var num_tiles_y = Math.ceil((height + offset_y)/this.projection.TILE_SIZE); + var num_tiles_x = Math.ceil((width + offset_x) / this.projection.TILE_SIZE); + var num_tiles_y = Math.ceil((height + offset_y) / this.projection.TILE_SIZE); var tiles = []; - for(var i = 0; i < num_tiles_x; ++i) { - for(var j = 0; j < num_tiles_y; ++j) { + for (var i = 0; i < num_tiles_x; ++i) { + for (var j = 0; j < num_tiles_y; ++j) { var tile_x = tile.x + i; var tile_y = tile.y + j; tiles.push({ @@ -109,16 +105,14 @@ MapModel.prototype.visibleTiles = function(width, height) { } } return tiles; +}; -} - -function dragger(el) { - +function dragger (el) { var self = this; var dragging = false; var x, y; - el.ontouchstart = el.onmousedown = function(e) { + el.ontouchstart = el.onmousedown = function (e) { dragging = true; if (e.touches) { var p = e.touches[0]; @@ -131,9 +125,9 @@ function dragger(el) { self.emit('startdrag', x, y); }; - el.ontouchmove = el.onmousemove = function(e) { + el.ontouchmove = el.onmousemove = function (e) { var xx, yy; - if(!dragging) return; + if (!dragging) return; if (e.touches) { var p = e.touches[0]; xx = p.pageX; @@ -146,7 +140,7 @@ function dragger(el) { return false; }; - el.ontouchend = el.onmouseup = function(e) { + el.ontouchend = el.onmouseup = function (e) { dragging = false; self.emit('enddrag', x, y); }; @@ -154,7 +148,7 @@ function dragger(el) { dragger.prototype = new Event(); -function CanvasRenderer(el, map) { +function CanvasRenderer (el, map) { var self = this; this.el = el; this.tiles = {}; @@ -165,18 +159,18 @@ function CanvasRenderer(el, map) { var canvas = this.canvas = document.createElement('canvas'); canvas.style.padding = '0'; - canvas.style.margin= '0'; + canvas.style.margin = '0'; canvas.style.position = 'absolute'; canvas.width = this.width; canvas.height = this.height; - var context = canvas.getContext( '2d' ); - context.translate( widthHalf, heightHalf ); + var context = canvas.getContext('2d'); + context.translate(widthHalf, heightHalf); this.context = context; var div = document.createElement('div'); - div.style.width = this.width + "px"; - div.style.height= this.height + "px"; + div.style.width = this.width + 'px'; + div.style.height = this.height + 'px'; div.style.position = 'relative'; div.appendChild(canvas); el.appendChild(div); @@ -184,70 +178,68 @@ function CanvasRenderer(el, map) { this.center_init = null; this.target_center = new LatLng(); this.drag = new dragger(div); - this.drag.on('startdrag', function() { + this.drag.on('startdrag', function () { self.center_init = map.center.clone(); }); - this.drag.on('enddrag', function() { + this.drag.on('enddrag', function () { map.emit('end_move'); }); - function go_to_target() { + function go_to_target () { var c = map.center; var t = self.target_center; var dlat = t.lat - c.lat; var dlon = t.lng - c.lng; - t.lat += dlat*0.0001; - t.lng += dlon*0.0001; + t.lat += dlat * 0.0001; + t.lng += dlon * 0.0001; map.setCenter(t); - if(Math.abs(dlat) + Math.abs(dlon) > 0.001) { + if (Math.abs(dlat) + Math.abs(dlon) > 0.001) { requestAnimFrame(go_to_target); } else { - //map.emit('end_move'); + // map.emit('end_move'); } } - this.drag.on('move', function(dx, dy) { + this.drag.on('move', function (dx, dy) { var t = 1 << map.zoom; - var s = 1/t; - s = s/map.projection.pixelsPerLonDegree_; - self.target_center.lat = self.center_init.lat + dy*s; - self.target_center.lng = self.center_init.lng - dx*s; + var s = 1 / t; + s = s / map.projection.pixelsPerLonDegree_; + self.target_center.lat = self.center_init.lat + dy * s; + self.target_center.lng = self.center_init.lng - dx * s; requestAnimFrame(go_to_target); }); - } - -CanvasRenderer.prototype.renderTile = function(tile, at) { +CanvasRenderer.prototype.renderTile = function (tile, at) { var self = this; - var key = at.x + '_' + at.y - if(a=self.tiles[key]) { + var key = at.x + '_' + at.y; + if (a = self.tiles[key]) { self.context.drawImage(a, at.x, at.y); return; } - //var layer = 'http://a.tile.cloudmade.com/BC9A493B41014CAABB98F0471D759707/997/256/{{z}}/{{x}}/{{y}}.png'; + // var layer = 'http://a.tile.cloudmade.com/BC9A493B41014CAABB98F0471D759707/997/256/{{z}}/{{x}}/{{y}}.png'; var layer = 'http://b.tiles.mapbox.com/v3/mapbox.mapbox-light/{{z}}/{{x}}/{{y}}.png64'; var url = layer.replace('{{z}}', tile.zoom).replace('{{x}}', tile.i).replace('{{y}}', tile.j); var img = new Image(); img.src = url; - img.onload = function() { + img.onload = function () { self.context.drawImage(img, at.x, at.y); self.tiles[key] = img; }; -} +}; -CanvasRenderer.prototype.renderTiles = function(tiles, center) { - for(var i = 0; i < tiles.length; ++i) { +CanvasRenderer.prototype.renderTiles = function (tiles, center) { + for (var i = 0; i < tiles.length; ++i) { var tile = tiles[i]; var p = new Point(tile.x, tile.y); p.x -= center.x; p.y -= center.y; this.renderTile(tile, p); } -} +}; -function Map(el, opts) { +function Map (el, opts) { opts = opts || {}; var self = this; this.model = new MapModel({ @@ -255,7 +247,7 @@ function Map(el, opts) { zoom: opts.zoom || 1 }); this.view = new CanvasRenderer(el, this.model); - /*function render() { + /* function render() { var tiles = self.model.visibleTiles(self.view.width, self.view.height); self.view.renderTiles(tiles, this.center_pixel); } diff --git a/test/websocket_test/public/test/map.test.js b/test/websocket_test/public/test/map.test.js index ccbeaddb..30f1c482 100644 --- a/test/websocket_test/public/test/map.test.js +++ b/test/websocket_test/public/test/map.test.js @@ -1,14 +1,13 @@ 'use strict'; -describe("Event", function() { - - before_each(function() { +describe('Event', function () { + before_each(function () { this.evt = new Event(); }); - it("call event binded", function() { + it('call event binded', function () { var c = 0; - function callback() { + function callback () { c++; } this.evt.on('test', callback); @@ -16,36 +15,35 @@ describe("Event", function() { assert(c == 1); }); - it("should works when call non existing event", function() { + it('should works when call non existing event', function () { this.evt.emit('test_no_exists'); }); }); -describe("MapModel", function() { - - before_each(function() { +describe('MapModel', function () { + before_each(function () { this.map_model = new MapModel(new LatLng(0, 0)); }); - it("center_changed should be called", function() { + it('center_changed should be called', function () { var c = 0; - this.map_model.on('center_changed', function() { + this.map_model.on('center_changed', function () { ++c; }); this.map_model.setCenter(new LatLng(0, 0)); assert(c == 1); }); - it("zoom_changed should be called", function() { + it('zoom_changed should be called', function () { var c = 0; - this.map_model.on('zoom_changed', function() { + this.map_model.on('zoom_changed', function () { ++c; }); this.map_model.setZoom(2); assert(c == 1); }); - it("visibleTiles", function() { + it('visibleTiles', function () { var ts = this.map_model.projection.TILE_SIZE; this.map_model.setZoom(10); var tiles = this.map_model.visibleTiles(ts, ts); @@ -54,6 +52,4 @@ describe("MapModel", function() { tiles = this.map_model.visibleTiles(ts, ts); assert(tiles.length == 4); }); - - });