From 53de9c6c510f18088eaba11cfa614c6243fc9e07 Mon Sep 17 00:00:00 2001 From: zhongjin Date: Fri, 19 May 2023 00:42:48 +0800 Subject: [PATCH] first commit --- .gitignore | 16 + .jshintignore | 3 + .jshintrc | 98 + .travis.yml | 79 + CONTRIBUTING.md | 11 + HOWTO_RELEASE | 17 + LICENSE | 27 + Makefile | 47 + NEWS.md | 1037 +++ README.md | 82 + app.js | 241 + app/auth/apikey.js | 74 + app/auth/auth_api.js | 48 + app/auth/oauth.js | 192 + app/controllers/cache_status_controller.js | 29 + app/controllers/copy_controller.js | 207 + app/controllers/generic_controller.js | 14 + app/controllers/health_check_controller.js | 35 + app/controllers/job_controller.js | 253 + app/controllers/query_controller.js | 275 + app/controllers/version_controller.js | 18 + app/middlewares/authorization.js | 125 + app/middlewares/body-parser.js | 146 + app/middlewares/connection-params.js | 23 + app/middlewares/cors.js | 16 + app/middlewares/db-quota.js | 26 + app/middlewares/error.js | 91 + app/middlewares/profiler.js | 24 + app/middlewares/rate-limit.js | 61 + app/middlewares/timeout-limits.js | 25 + app/middlewares/user.js | 38 + app/models/bin_encoder.js | 166 + app/models/cartodb_request.js | 41 + app/models/formats/README | 18 + app/models/formats/index.js | 22 + app/models/formats/ogr.js | 346 + app/models/formats/ogr/csv.js | 16 + app/models/formats/ogr/geopackage.js | 25 + app/models/formats/ogr/kml.js | 24 + app/models/formats/ogr/shp.js | 135 + app/models/formats/ogr/spatialite.js | 25 + app/models/formats/pg.js | 164 + app/models/formats/pg/arraybuffer.js | 87 + app/models/formats/pg/geojson.js | 120 + app/models/formats/pg/json.js | 174 + app/models/formats/pg/svg.js | 166 + app/models/formats/pg/topojson.js | 138 + app/monitoring/health_check.js | 34 + app/postgresql/error_codes.js | 285 + app/server.js | 226 + app/services/cached-query-tables.js | 44 + app/services/error_handler.js | 36 + app/services/error_handler_factory.js | 41 + app/services/logger.js | 38 + app/services/pg-entities-access-validator.js | 63 + app/services/stream_copy.js | 77 + app/services/stream_copy_metrics.js | 85 + app/services/user_database_service.js | 107 + app/services/user_limits.js | 27 + app/stats/client.js | 75 + app/stats/profiler-proxy.js | 55 + app/utils/cache_key_generator.js | 5 + app/utils/content_disposition.js | 8 + app/utils/date_to_json.js | 19 + app/utils/filename_sanitizer.js | 9 + app/utils/md5.js | 9 + app/utils/no_cache.js | 49 + app/utils/query_info.js | 31 + app/utils/query_may_write.js | 14 + app/utils/table_cache_factory.js | 32 + batch/README.md | 122 + batch/batch-logger.js | 16 + batch/batch.js | 217 + batch/index.js | 39 + batch/job_backend.js | 290 + batch/job_canceller.js | 78 + batch/job_queue.js | 165 + batch/job_runner.js | 144 + batch/job_service.js | 136 + batch/job_status.js | 23 + batch/leader/locker.js | 74 + batch/leader/provider/redis-distlock.js | 111 + batch/maintenance/host-user-queue-mover.js | 144 + batch/models/job_base.js | 122 + batch/models/job_factory.js | 26 + batch/models/job_fallback.js | 279 + batch/models/job_multiple.js | 91 + batch/models/job_simple.js | 34 + batch/models/job_state_machine.js | 39 + batch/models/query/fallback.js | 78 + batch/models/query/main_fallback.js | 74 + batch/models/query/query.js | 57 + batch/models/query/query_base.js | 31 + batch/models/query/query_factory.js | 16 + batch/models/query/query_fallback.js | 75 + batch/pubsub/channel.js | 4 + batch/pubsub/job-publisher.js | 31 + batch/pubsub/job-subscriber.js | 54 + batch/query_runner.js | 54 + batch/scheduler/capacity/fixed.js | 11 + batch/scheduler/capacity/http-load.js | 32 + batch/scheduler/capacity/http-simple.js | 62 + batch/scheduler/host-scheduler.js | 85 + batch/scheduler/scheduler.js | 201 + batch/user_database_metadata_service.js | 31 + batch/util/debug.js | 7 + batch/util/forever.js | 11 + carto-package.json | 16 + client/bin_decoder.js | 104 + client/test.html | 32 + config/environments/development.js.example | 127 + config/environments/production.js.example | 131 + config/environments/staging.js.example | 128 + config/environments/test.js.example | 128 + configure | 36 + doc/API.md | 25 + doc/authentication.md | 17 + doc/batch_queries.md | 491 ++ doc/copy_queries.md | 249 + doc/creating_tables.md | 55 + doc/handling_geospatial_data.md | 63 + doc/libraries_support.md | 32 + doc/making_calls.md | 203 + doc/metrics.md | 19 + doc/query_optimizations.md | 26 + doc/tips_and_tricks.md | 97 + doc/version.md | 3 + docker-bash.sh | 3 + docker-test.sh | 23 + logs/.gitneedsme | 0 npm-shrinkwrap.json | 1620 ++++ package-lock.json | 5009 +++++++++++ package.json | 70 + test/README.md | 21 + test/acceptance/app-configuration.js | 169 + test/acceptance/app.auth.test.js | 67 + test/acceptance/app.test.js | 885 ++ test/acceptance/auth-api.js | 275 + test/acceptance/backend_crash.js | 83 + test/acceptance/batch/batch-drain.test.js | 83 + test/acceptance/batch/batch-limits.test.js | 52 + .../acceptance/batch/batch.multiquery.test.js | 236 + test/acceptance/batch/batch.test.js | 197 + test/acceptance/batch/batch.wip.test.js | 99 + .../batch/job.callback-template.test.js | 126 + test/acceptance/batch/job.fallback.test.js | 934 +++ test/acceptance/batch/job.query.limit.test.js | 125 + test/acceptance/batch/job.query.order.test.js | 59 + .../batch/job.query.timeout.test.js | 99 + test/acceptance/batch/job.test.js | 219 + test/acceptance/batch/job.timing.test.js | 115 + .../leader-multiple-users-query-order.test.js | 129 + .../batch/leader.job.query.order.test.js | 88 + .../batch/queued-jobs-limit.test.js | 68 + test/acceptance/batch/scheduler-basic.test.js | 99 + test/acceptance/batch/use-cases.test.js | 190 + test/acceptance/cache.js | 21 + test/acceptance/copy-abort.js | 242 + test/acceptance/copy-endpoints.js | 604 ++ test/acceptance/copy-statements.js | 80 + test/acceptance/error-handler.js | 33 + test/acceptance/export/arraybuffer.js | 45 + test/acceptance/export/csv.js | 209 + test/acceptance/export/folder.js | 35 + test/acceptance/export/geojson.js | 212 + test/acceptance/export/geopackage.js | 82 + test/acceptance/export/kml.js | 403 + test/acceptance/export/shapefile.js | 405 + test/acceptance/export/spatialite.js | 56 + test/acceptance/export/svg.js | 199 + test/acceptance/export/timeout.js | 202 + test/acceptance/export/topojson.js | 267 + test/acceptance/frontend_abort.js | 79 + test/acceptance/health_check.js | 68 + test/acceptance/last-modified-header.js | 114 + test/acceptance/logging.js | 152 + test/acceptance/oauth/oauth_test.py | 26 + test/acceptance/pagination.js | 175 + .../pg-entities-access-validator.js | 66 + test/acceptance/query-float-values.js | 72 + test/acceptance/query-multipart.js | 26 + test/acceptance/query-returning.js | 159 + test/acceptance/query-tables-api-cache.js | 101 + test/acceptance/rate-limit.js | 107 + test/acceptance/regressions.js | 66 + test/acceptance/skipfields.js | 94 + test/acceptance/stream-responses.js | 73 + test/acceptance/surrogate-key.js | 111 + test/acceptance/system-queries.js | 64 + test/acceptance/timeout.js | 55 + test/acceptance/transaction.js | 56 + test/acceptance/x-cache-channel.js | 112 + test/helper.js | 4 + test/integration/batch/job-queue.test.js | 179 + test/integration/batch/job_backend.test.js | 219 + test/integration/batch/job_canceller.test.js | 119 + test/integration/batch/job_publisher.test.js | 39 + test/integration/batch/job_runner.test.js | 80 + test/integration/batch/job_service.test.js | 205 + test/integration/batch/locker.js | 60 + test/integration/batch/scheduler.js | 204 + test/integration/stream_copy.test.js | 21 + .../utils/table_cache_factory.test.js | 41 + test/prepare_db.sh | 250 + test/run_tests.sh | 160 + test/run_tests_docker.sh | 47 + test/support/.gitignore | 1 + test/support/assert.js | 127 + test/support/batch-test-client.js | 300 + test/support/csv/copy_test_table.csv | 7 + test/support/csv/copy_test_table.csv.gz | Bin 0 -> 96 bytes test/support/db_utils.js | 36 + test/support/libredis_cell.dylib | Bin 0 -> 1852524 bytes test/support/libredis_cell.so | Bin 0 -> 4496020 bytes test/support/redis_utils.js | 39 + .../sql/populated_places_simple_reduced.sql | 7385 +++++++++++++++++ test/support/sql/py_sleep.sql | 5 + test/support/sql/quota_mock.sql | 17 + test/support/sql/test.sql | 228 + test/support/test-client.js | 150 + test/unit/apikeyauth.test.js | 93 + test/unit/batch/job_publisher.js | 39 + test/unit/batch/job_queue.js | 49 + test/unit/batch/job_subscriber.js | 71 + test/unit/error_handler.test.js | 143 + test/unit/error_handler_factory.test.js | 71 + test/unit/health_check.js | 47 + test/unit/model/bin_encoder.js | 76 + test/unit/oauth.test.js | 176 + .../unit/pg-entities-access-validator.test.js | 175 + test/unit/query_info.test.js | 66 + test/websocket_test/app.js | 59 + test/websocket_test/public/gmaps_mercator.js | 151 + test/websocket_test/public/index.html | 59 + test/websocket_test/public/map.js | 266 + test/websocket_test/public/run_server.sh | 1 + test/websocket_test/public/test.html | 111 + test/websocket_test/public/test/map.test.js | 59 + test/websocket_test/public/test/suite.html | 62 + tools/cdbsql | 216 + tools/munin/Makefile | 19 + tools/munin/cdbsqlapi | 74 + tools/munin/cdbsqlapi.conf.in | 5 + 243 files changed, 39485 insertions(+) create mode 100644 .gitignore create mode 100644 .jshintignore create mode 100644 .jshintrc create mode 100644 .travis.yml create mode 100644 CONTRIBUTING.md create mode 100644 HOWTO_RELEASE create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 NEWS.md create mode 100644 README.md create mode 100755 app.js create mode 100644 app/auth/apikey.js create mode 100644 app/auth/auth_api.js create mode 100644 app/auth/oauth.js create mode 100644 app/controllers/cache_status_controller.js create mode 100644 app/controllers/copy_controller.js create mode 100644 app/controllers/generic_controller.js create mode 100644 app/controllers/health_check_controller.js create mode 100644 app/controllers/job_controller.js create mode 100644 app/controllers/query_controller.js create mode 100644 app/controllers/version_controller.js create mode 100644 app/middlewares/authorization.js create mode 100644 app/middlewares/body-parser.js create mode 100644 app/middlewares/connection-params.js create mode 100644 app/middlewares/cors.js create mode 100644 app/middlewares/db-quota.js create mode 100644 app/middlewares/error.js create mode 100644 app/middlewares/profiler.js create mode 100644 app/middlewares/rate-limit.js create mode 100644 app/middlewares/timeout-limits.js create mode 100644 app/middlewares/user.js create mode 100644 app/models/bin_encoder.js create mode 100644 app/models/cartodb_request.js create mode 100644 app/models/formats/README create mode 100644 app/models/formats/index.js create mode 100644 app/models/formats/ogr.js create mode 100644 app/models/formats/ogr/csv.js create mode 100644 app/models/formats/ogr/geopackage.js create mode 100644 app/models/formats/ogr/kml.js create mode 100644 app/models/formats/ogr/shp.js create mode 100644 app/models/formats/ogr/spatialite.js create mode 100644 app/models/formats/pg.js create mode 100644 app/models/formats/pg/arraybuffer.js create mode 100644 app/models/formats/pg/geojson.js create mode 100644 app/models/formats/pg/json.js create mode 100644 app/models/formats/pg/svg.js create mode 100644 app/models/formats/pg/topojson.js create mode 100644 app/monitoring/health_check.js create mode 100644 app/postgresql/error_codes.js create mode 100644 app/server.js create mode 100644 app/services/cached-query-tables.js create mode 100644 app/services/error_handler.js create mode 100644 app/services/error_handler_factory.js create mode 100644 app/services/logger.js create mode 100644 app/services/pg-entities-access-validator.js create mode 100644 app/services/stream_copy.js create mode 100644 app/services/stream_copy_metrics.js create mode 100644 app/services/user_database_service.js create mode 100644 app/services/user_limits.js create mode 100644 app/stats/client.js create mode 100644 app/stats/profiler-proxy.js create mode 100644 app/utils/cache_key_generator.js create mode 100644 app/utils/content_disposition.js create mode 100644 app/utils/date_to_json.js create mode 100644 app/utils/filename_sanitizer.js create mode 100644 app/utils/md5.js create mode 100644 app/utils/no_cache.js create mode 100644 app/utils/query_info.js create mode 100644 app/utils/query_may_write.js create mode 100644 app/utils/table_cache_factory.js create mode 100644 batch/README.md create mode 100644 batch/batch-logger.js create mode 100644 batch/batch.js create mode 100644 batch/index.js create mode 100644 batch/job_backend.js create mode 100644 batch/job_canceller.js create mode 100644 batch/job_queue.js create mode 100644 batch/job_runner.js create mode 100644 batch/job_service.js create mode 100644 batch/job_status.js create mode 100644 batch/leader/locker.js create mode 100644 batch/leader/provider/redis-distlock.js create mode 100644 batch/maintenance/host-user-queue-mover.js create mode 100644 batch/models/job_base.js create mode 100644 batch/models/job_factory.js create mode 100644 batch/models/job_fallback.js create mode 100644 batch/models/job_multiple.js create mode 100644 batch/models/job_simple.js create mode 100644 batch/models/job_state_machine.js create mode 100644 batch/models/query/fallback.js create mode 100644 batch/models/query/main_fallback.js create mode 100644 batch/models/query/query.js create mode 100644 batch/models/query/query_base.js create mode 100644 batch/models/query/query_factory.js create mode 100644 batch/models/query/query_fallback.js create mode 100644 batch/pubsub/channel.js create mode 100644 batch/pubsub/job-publisher.js create mode 100644 batch/pubsub/job-subscriber.js create mode 100644 batch/query_runner.js create mode 100644 batch/scheduler/capacity/fixed.js create mode 100644 batch/scheduler/capacity/http-load.js create mode 100644 batch/scheduler/capacity/http-simple.js create mode 100644 batch/scheduler/host-scheduler.js create mode 100644 batch/scheduler/scheduler.js create mode 100644 batch/user_database_metadata_service.js create mode 100644 batch/util/debug.js create mode 100644 batch/util/forever.js create mode 100644 carto-package.json create mode 100644 client/bin_decoder.js create mode 100644 client/test.html create mode 100644 config/environments/development.js.example create mode 100644 config/environments/production.js.example create mode 100644 config/environments/staging.js.example create mode 100644 config/environments/test.js.example create mode 100755 configure create mode 100644 doc/API.md create mode 100644 doc/authentication.md create mode 100644 doc/batch_queries.md create mode 100644 doc/copy_queries.md create mode 100644 doc/creating_tables.md create mode 100644 doc/handling_geospatial_data.md create mode 100644 doc/libraries_support.md create mode 100644 doc/making_calls.md create mode 100644 doc/metrics.md create mode 100644 doc/query_optimizations.md create mode 100644 doc/tips_and_tricks.md create mode 100644 doc/version.md create mode 100755 docker-bash.sh create mode 100755 docker-test.sh create mode 100644 logs/.gitneedsme create mode 100644 npm-shrinkwrap.json create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 test/README.md create mode 100644 test/acceptance/app-configuration.js create mode 100644 test/acceptance/app.auth.test.js create mode 100644 test/acceptance/app.test.js create mode 100644 test/acceptance/auth-api.js create mode 100644 test/acceptance/backend_crash.js create mode 100644 test/acceptance/batch/batch-drain.test.js create mode 100644 test/acceptance/batch/batch-limits.test.js create mode 100644 test/acceptance/batch/batch.multiquery.test.js create mode 100644 test/acceptance/batch/batch.test.js create mode 100644 test/acceptance/batch/batch.wip.test.js create mode 100644 test/acceptance/batch/job.callback-template.test.js create mode 100644 test/acceptance/batch/job.fallback.test.js create mode 100644 test/acceptance/batch/job.query.limit.test.js create mode 100644 test/acceptance/batch/job.query.order.test.js create mode 100644 test/acceptance/batch/job.query.timeout.test.js create mode 100644 test/acceptance/batch/job.test.js create mode 100644 test/acceptance/batch/job.timing.test.js create mode 100644 test/acceptance/batch/leader-multiple-users-query-order.test.js create mode 100644 test/acceptance/batch/leader.job.query.order.test.js create mode 100644 test/acceptance/batch/queued-jobs-limit.test.js create mode 100644 test/acceptance/batch/scheduler-basic.test.js create mode 100644 test/acceptance/batch/use-cases.test.js create mode 100644 test/acceptance/cache.js create mode 100644 test/acceptance/copy-abort.js create mode 100644 test/acceptance/copy-endpoints.js create mode 100644 test/acceptance/copy-statements.js create mode 100644 test/acceptance/error-handler.js create mode 100644 test/acceptance/export/arraybuffer.js create mode 100644 test/acceptance/export/csv.js create mode 100644 test/acceptance/export/folder.js create mode 100644 test/acceptance/export/geojson.js create mode 100644 test/acceptance/export/geopackage.js create mode 100644 test/acceptance/export/kml.js create mode 100644 test/acceptance/export/shapefile.js create mode 100644 test/acceptance/export/spatialite.js create mode 100644 test/acceptance/export/svg.js create mode 100644 test/acceptance/export/timeout.js create mode 100644 test/acceptance/export/topojson.js create mode 100644 test/acceptance/frontend_abort.js create mode 100644 test/acceptance/health_check.js create mode 100644 test/acceptance/last-modified-header.js create mode 100644 test/acceptance/logging.js create mode 100644 test/acceptance/oauth/oauth_test.py create mode 100644 test/acceptance/pagination.js create mode 100644 test/acceptance/pg-entities-access-validator.js create mode 100644 test/acceptance/query-float-values.js create mode 100644 test/acceptance/query-multipart.js create mode 100644 test/acceptance/query-returning.js create mode 100644 test/acceptance/query-tables-api-cache.js create mode 100644 test/acceptance/rate-limit.js create mode 100644 test/acceptance/regressions.js create mode 100644 test/acceptance/skipfields.js create mode 100644 test/acceptance/stream-responses.js create mode 100644 test/acceptance/surrogate-key.js create mode 100644 test/acceptance/system-queries.js create mode 100644 test/acceptance/timeout.js create mode 100644 test/acceptance/transaction.js create mode 100644 test/acceptance/x-cache-channel.js create mode 100644 test/helper.js create mode 100644 test/integration/batch/job-queue.test.js create mode 100644 test/integration/batch/job_backend.test.js create mode 100644 test/integration/batch/job_canceller.test.js create mode 100644 test/integration/batch/job_publisher.test.js create mode 100644 test/integration/batch/job_runner.test.js create mode 100644 test/integration/batch/job_service.test.js create mode 100644 test/integration/batch/locker.js create mode 100644 test/integration/batch/scheduler.js create mode 100644 test/integration/stream_copy.test.js create mode 100644 test/integration/utils/table_cache_factory.test.js create mode 100755 test/prepare_db.sh create mode 100755 test/run_tests.sh create mode 100644 test/run_tests_docker.sh create mode 100644 test/support/.gitignore create mode 100644 test/support/assert.js create mode 100644 test/support/batch-test-client.js create mode 100644 test/support/csv/copy_test_table.csv create mode 100644 test/support/csv/copy_test_table.csv.gz create mode 100644 test/support/db_utils.js create mode 100755 test/support/libredis_cell.dylib create mode 100755 test/support/libredis_cell.so create mode 100644 test/support/redis_utils.js create mode 100644 test/support/sql/populated_places_simple_reduced.sql create mode 100644 test/support/sql/py_sleep.sql create mode 100644 test/support/sql/quota_mock.sql create mode 100644 test/support/sql/test.sql create mode 100644 test/support/test-client.js create mode 100644 test/unit/apikeyauth.test.js create mode 100644 test/unit/batch/job_publisher.js create mode 100644 test/unit/batch/job_queue.js create mode 100644 test/unit/batch/job_subscriber.js create mode 100644 test/unit/error_handler.test.js create mode 100644 test/unit/error_handler_factory.test.js create mode 100644 test/unit/health_check.js create mode 100644 test/unit/model/bin_encoder.js create mode 100644 test/unit/oauth.test.js create mode 100644 test/unit/pg-entities-access-validator.test.js create mode 100644 test/unit/query_info.test.js create mode 100644 test/websocket_test/app.js create mode 100644 test/websocket_test/public/gmaps_mercator.js create mode 100644 test/websocket_test/public/index.html create mode 100644 test/websocket_test/public/map.js create mode 100755 test/websocket_test/public/run_server.sh create mode 100644 test/websocket_test/public/test.html create mode 100644 test/websocket_test/public/test/map.test.js create mode 100644 test/websocket_test/public/test/suite.html create mode 100755 tools/cdbsql create mode 100644 tools/munin/Makefile create mode 100755 tools/munin/cdbsqlapi create mode 100644 tools/munin/cdbsqlapi.conf.in diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..147f234 --- /dev/null +++ b/.gitignore @@ -0,0 +1,16 @@ +config/environments/*.js +logs/*.log +pids/*.pid +*.sock +test/tmp/* +node_modules* +.idea/* +.vscode/ +tools/munin/cdbsqlapi.conf +test/redis.pid +test/test.log +test/acceptance/oauth/venv/* +coverage/ +npm-debug.log +log/*.log +yarn.lock \ No newline at end of file diff --git a/.jshintignore b/.jshintignore new file mode 100644 index 0000000..a39c522 --- /dev/null +++ b/.jshintignore @@ -0,0 +1,3 @@ +test/support/ +test/websocket_test/ +app/models/formats/pg/topojson.js diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 0000000..99beee4 --- /dev/null +++ b/.jshintrc @@ -0,0 +1,98 @@ +{ +// // JSHint Default Configuration File (as on JSHint website) +// // See http://jshint.com/docs/ for more details +// +// "maxerr" : 50, // {int} Maximum error before stopping +// +// // Enforcing +// "bitwise" : true, // true: Prohibit bitwise operators (&, |, ^, etc.) +// "camelcase" : false, // true: Identifiers must be in camelCase + "curly" : true, // true: Require {} for every new block or scope + "eqeqeq" : true, // true: Require triple equals (===) for comparison + "forin" : true, // true: Require filtering for..in loops with obj.hasOwnProperty() + "freeze" : true, // true: prohibits overwriting prototypes of native objects such as Array, Date etc. + "immed" : true, // true: Require immediate invocations to be wrapped in parens e.g. `(function () { } ());` +// "indent" : 4, // {int} Number of spaces to use for indentation +// "latedef" : false, // true: Require variables/functions to be defined before being used + "newcap" : true, // true: Require capitalization of all constructor functions e.g. `new F()` + "noarg" : true, // true: Prohibit use of `arguments.caller` and `arguments.callee` +// "noempty" : true, // true: Prohibit use of empty blocks + "nonbsp" : true, // true: Prohibit "non-breaking whitespace" characters. + "nonew" : true, // true: Prohibit use of constructors for side-effects (without assignment) +// "plusplus" : false, // true: Prohibit use of `++` & `--` +// "quotmark" : false, // Quotation mark consistency: +// // false : do nothing (default) +// // true : ensure whatever is used is consistent +// // "single" : require single quotes +// // "double" : require double quotes + "undef" : true, // true: Require all non-global variables to be declared (prevents global leaks) + "unused" : true, // true: Require all defined variables be used +// "strict" : true, // true: Requires all functions run in ES5 Strict Mode +// "maxparams" : false, // {int} Max number of formal params allowed per function +// "maxdepth" : false, // {int} Max depth of nested blocks (within functions) +// "maxstatements" : false, // {int} Max number statements per function + "maxcomplexity" : 6, // {int} Max cyclomatic complexity per function + "maxlen" : 120, // {int} Max number of characters per line +// +// // Relaxing +// "asi" : false, // true: Tolerate Automatic Semicolon Insertion (no semicolons) +// "boss" : false, // true: Tolerate assignments where comparisons would be expected + "debug" : false, // true: Allow debugger statements e.g. browser breakpoints. +// "eqnull" : false, // true: Tolerate use of `== null` +// "es5" : false, // true: Allow ES5 syntax (ex: getters and setters) + "esnext" : true, // true: Allow ES.next (ES6) syntax (ex: `const`) +// "moz" : false, // true: Allow Mozilla specific syntax (extends and overrides esnext features) +// // (ex: `for each`, multiple try/catch, function expression…) +// "evil" : false, // true: Tolerate use of `eval` and `new Function()` +// "expr" : false, // true: Tolerate `ExpressionStatement` as Programs +// "funcscope" : false, // true: Tolerate defining variables inside control statements +// "globalstrict" : false, // true: Allow global "use strict" (also enables 'strict') +// "iterator" : false, // true: Tolerate using the `__iterator__` property +// "lastsemic" : false, // true: Tolerate omitting a semicolon for the last statement of a 1-line block +// "laxbreak" : false, // true: Tolerate possibly unsafe line breakings +// "laxcomma" : false, // true: Tolerate comma-first style coding +// "loopfunc" : false, // true: Tolerate functions being defined in loops +// "multistr" : false, // true: Tolerate multi-line strings +// "noyield" : false, // true: Tolerate generator functions with no yield statement in them. +// "notypeof" : false, // true: Tolerate invalid typeof operator values +// "proto" : false, // true: Tolerate using the `__proto__` property +// "scripturl" : false, // true: Tolerate script-targeted URLs +// "shadow" : false, // true: Allows re-define variables later in code e.g. `var x=1; x=2;` +// "sub" : false, // true: Tolerate using `[]` notation when it can still be expressed in dot notation +// "supernew" : false, // true: Tolerate `new function () { ... };` and `new Object;` +// "validthis" : false, // true: Tolerate using this in a non-constructor function +// +// // Environments +// "browser" : true, // Web Browser (window, document, etc) +// "browserify" : false, // Browserify (node.js code in the browser) +// "couch" : false, // CouchDB +// "devel" : true, // Development/debugging (alert, confirm, etc) +// "dojo" : false, // Dojo Toolkit +// "jasmine" : false, // Jasmine +// "jquery" : false, // jQuery + "mocha" : true, // Mocha +// "mootools" : false, // MooTools + "node" : true, // Node.js +// "nonstandard" : false, // Widely adopted globals (escape, unescape, etc) +// "prototypejs" : false, // Prototype and Scriptaculous +// "qunit" : false, // QUnit +// "rhino" : false, // Rhino +// "shelljs" : false, // ShellJS +// "worker" : false, // Web Workers +// "wsh" : false, // Windows Scripting Host +// "yui" : false, // Yahoo User Interface + + // Custom Globals + "globals" : { // additional predefined global variables + "suite": true, + "suiteSetup": true, + "test": true, + "suiteTeardown": true, + "beforeEach": true, + "afterEach": true, + "before": true, + "after": true, + "describe": true, + "it": true + } +} diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..f1c8253 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,79 @@ +jobs: + include: + - sudo: required + services: + - docker + language: generic + before_install: docker pull carto/nodejs6-xenial-pg101 + script: npm run docker-test -- nodejs6 + - sudo: required + services: + - docker + language: generic + before_install: docker pull carto/nodejs10-xenial-pg101:postgis-2.4.4.5 + script: npm run docker-test -- nodejs10 + - dist: precise + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-4.8 + postgresql: 9.5 + + before_install: + # Add custom PPAs from cartodb + - sudo add-apt-repository -y ppa:cartodb/postgresql-9.5 + - sudo add-apt-repository -y ppa:cartodb/gis + - sudo add-apt-repository -y ppa:cartodb/gis-testing + - sudo apt-get update + + # Force instalation of libgeos-3.5.0 (presumably needed because of existing version of postgis) + - sudo apt-get -y install libgeos-3.5.0=3.5.0-1cdb2 + + # Install postgres db and build deps + - sudo /etc/init.d/postgresql stop # stop travis default instance + - sudo apt-get -y remove --purge postgresql-9.1 + - sudo apt-get -y remove --purge postgresql-9.2 + - sudo apt-get -y remove --purge postgresql-9.3 + - sudo apt-get -y remove --purge postgresql-9.4 + - sudo apt-get -y remove --purge postgresql-9.5 + - sudo apt-get -y remove --purge postgresql-9.6 + - sudo rm -rf /var/lib/postgresql/ + - sudo rm -rf /var/log/postgresql/ + - sudo rm -rf /etc/postgresql/ + - sudo apt-get -y remove --purge postgis-2.2 + - sudo apt-get -y autoremove + + - sudo apt-get -y install postgresql-9.5-postgis-scripts=2.2.2.0-cdb2 + - sudo apt-get -y install postgresql-9.5=9.5.2-3cdb3 --allow-downgrades + - sudo apt-get -y install postgresql-server-dev-9.5=9.5.2-3cdb3 + - sudo apt-get -y install postgresql-plpython-9.5=9.5.2-3cdb3 --allow-downgrades + - sudo apt-get -y install postgresql-9.5-postgis-2.2=2.2.2.0-cdb2 + - sudo apt-get install -q gdal-bin + - sudo apt-get install -q ogr2ogr2-static-bin + - sudo apt-get install -y libc6 + + - wget http://download.redis.io/releases/redis-4.0.8.tar.gz + - tar xvzf redis-4.0.8.tar.gz + - cd redis-4.0.8 + - make + - sudo make install + - cd .. + - rm redis-4.0.8.tar.gz + + # configure it to accept local connections from postgres + - echo -e "# TYPE DATABASE USER ADDRESS METHOD \nlocal all postgres trust\nlocal all all trust\nhost all all 127.0.0.1/32 trust" \ + | sudo tee /etc/postgresql/9.5/main/pg_hba.conf + - sudo /etc/init.d/postgresql restart 9.5 + + - psql -c 'create database template_postgis;' -U postgres + - psql -c 'CREATE EXTENSION postgis;' -U postgres -d template_postgis + - ./configure + + env: + - PGUSER=postgres CXX=g++-4.8 + + language: node_js + node_js: + - "6" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..b327c10 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,11 @@ +Contributing +--- + +The issue tracker is at [github.com/CartoDB/CartoDB-SQL-API](https://github.com/CartoDB/CartoDB-SQL-API). + +We love pull requests from everyone, see [Contributing to Open Source on GitHub](https://guides.github.com/activities/contributing-to-open-source/#contributing). + + +## Submitting Contributions + +* You will need to sign a Contributor License Agreement (CLA) before making a submission. [Learn more here](https://carto.com/contributions). diff --git a/HOWTO_RELEASE b/HOWTO_RELEASE new file mode 100644 index 0000000..2701a54 --- /dev/null +++ b/HOWTO_RELEASE @@ -0,0 +1,17 @@ +1. Test (make clean all check), fix if broken before proceeding +2. Ensure proper version in package.json and package-lock.json +3. Ensure NEWS section exists for the new version, review it, add release date +4. Commit package.json, npm-shrinwrap.json, NEWS +5. git tag -a Major.Minor.Patch # use NEWS section as content +6. Stub NEWS/package for next version + +Versions: + +Bugfix releases increment Patch component of version. +Feature releases increment Minor and set Patch to zero. +If backward compatibility is broken, increment Major and +set to zero Minor and Patch. + +Branches named 'b.' are kept for any critical +fix that might need to be shipped before next feature release +is ready. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d939626 --- /dev/null +++ b/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2015, CartoDB +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors +may be used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..b2c773f --- /dev/null +++ b/Makefile @@ -0,0 +1,47 @@ +SHELL=/bin/bash + +all: + npm install + +clean: + rm -rf node_modules/* + +check: + npm test + +jshint: + @echo "***jshint***" + @./node_modules/.bin/jshint app/ batch/ test/ app.js + +TEST_SUITE := $(shell find test/{unit,integration,acceptance} -name "*.js") +TEST_SUITE_UNIT := $(shell find test/unit -name "*.js") +TEST_SUITE_INTEGRATION := $(shell find test/integration -name "*.js") +TEST_SUITE_ACCEPTANCE := $(shell find test/acceptance -name "*.js") +TEST_SUITE_BATCH := $(shell find test/*/batch -name "*.js") + +test: + @echo "***tests***" + @$(SHELL) test/run_tests.sh ${RUNTESTFLAGS} $(TEST_SUITE) + +test-unit: + @echo "***unit tests***" + @$(SHELL) test/run_tests.sh ${RUNTESTFLAGS} $(TEST_SUITE_UNIT) + +test-integration: + @echo "***integration tests***" + @$(SHELL) test/run_tests.sh ${RUNTESTFLAGS} $(TEST_SUITE_INTEGRATION) + +test-acceptance: + @echo "***acceptance tests***" + @$(SHELL) test/run_tests.sh ${RUNTESTFLAGS} $(TEST_SUITE_ACCEPTANCE) + +test-batch: + @echo "***batch queries tests***" + @$(SHELL) test/run_tests.sh ${RUNTESTFLAGS} $(TEST_SUITE_BATCH) + +test-all: test jshint + +coverage: + @RUNTESTFLAGS=--with-coverage make test + +.PHONY: test coverage diff --git a/NEWS.md b/NEWS.md new file mode 100644 index 0000000..7788321 --- /dev/null +++ b/NEWS.md @@ -0,0 +1,1037 @@ +# Changelog + +**Deprecation warning**: Next major release will drop support for `Node.js 6 LTS` and `npm 3.x`. You'll be able to use the latest ES features as soon as we release 3.0.0. In the meantime, as a developer, you should keep compatibility with Node.js 6 LTS and keep updated both `package-lock.json` and `npm-shrinkwrap.json` files. + +## 2.4.0 +Released 2019-01-16 + +Announcements: + * Update docs: compatible Node.js and npm versions + * Set platform limits message also on streaming responses + * Consider cancelled queries as platform limits. + * Report fine-grained Garbage Collector stats + * Both query endpoints as the same one in rate limits terms + * Adding Authorization to Access-Control-Allow-Headers (https://github.com/CartoDB/CartoDB-SQL-API/issues/534) + + +## 2.3.1 +Released 2018-12-23 + +Bug fixes: +* Update carto-package.json + + +## 2.3.0 +Released 2018-12-26 + +Announcements: + * Support Node.js 10 + * Add package-lock.json + * Configure Travis CI to run docker tests against Node.js 6 & 10 versions + * Update cartodb-psql to 0.13.1 (type cache depends now on db host) + +Bug fixes: + * Do not use `assert` to throw erros as in Node.js > 6 wraps the original error, the keyword 'throw' does the trick and it's backwards compatible + * Make all modules to use strict mode semantics. + * Avoid too long messages in `X-SQLAPI-Errors` header #543 + + +## 2.2.1 +Released 2018-09-18 + +Bug fixes: + * Errors from zlib while gunzipping (`/sql/copyfrom` compressed requests) are now handled correctly. + * Ensure exports temporal folder + * Fix an issue with COPY TO returning paused DB connections to the pool #537 + + +## 2.2.0 +Released 2018-07-25 + +Announcements: + * Improve error message when the DB query is over the user's limits + * Updated cartodb-redis to 2.0.1 + * Modify the COPY query limits: + - Instead of the generic timeout, it now uses a 5h timeout. + - For COPY FROM, the limit is size-based, up to the remaining DB quota + - The largest COPY FROM that can be made in a single POST request is limited to 2GB + + +## 2.1.0 +Released 2018-06-13 + +Notice: +- This release changes the way that authentication works internally. You'll need to run `bundle exec rake carto:api_key:create_default` in your development environment to keep working. + +New features: + * CI tests with Ubuntu Xenial + PostgreSQL 10.1 and Ubuntu Precise + PostgreSQL 9.5 + * Making version 2.0.0 configuration parameters backwards compatible + * New endpoint for COPY commands + +Announcements: + * Updated carto-psql to 0.12.0 + * [Test] Update sqlite3 to 4.0.0 + + +## 2.0.0 +Released 2018-03-22 + +Breaking changes: + * Needs Redis v4 + +Features: + * Implemented middleware to authenticate users throug the new Authorization System. + * Upgrades cartodb-redis to 1.0.0 + * Rate limit feature (disabled by default) + + +## 1.48.1 +Released 2018-02-27 + +Announcements: + * Added `RESIZE=yes` param to gdal shapefile driver, wich optimizes size of exported shapefiles [#462](https://github.com/CartoDB/CartoDB-SQL-API/pull/462) + + +## 1.48.0 +Released 2018-02-12 + +Announcements: + * Change work in progress jobs endpoint from `[..]/job/wip` to `[..]/jobs-wip` + * Documentation updates for Docs repo issue #840, GPKG Export. + * Fix SHP exports, now it uses "the_geom" column by default when a dataset has more than one geometry column. + * Logging all errors + * Fix Postgres version in travis + * Fix Python timeout error + * Upgrades redis-mpool to 0.5.0 + * Upgrades cartodb-redis to 0.15.0 + + +## 1.47.1 +Released 2017-08-13 + +Announcements: + * Upgrade cartodb-psql to 0.10.1. + * Content edits to doc/version.md. + + +## 1.47.0 +Released 2017-08-10 + +Announcements: + * Now export and query APIs respond with `429 You are over the limits` when a query or export command overcomes the pre-configured user's timeout. + + +## 1.46.1 +Released 2017-07-01 + +Announcements: + * Now tableCache evicts keys based on their set time #244 + + +## 1.46.0 +Released 2017-06-27 + +Announcements: + * Disable tableCache in-memory LRU by default [#422](https://github.com/CartoDB/CartoDB-SQL-API/issues/422) + +## 1.45.1 +Released 2017-06-27 + +Bug fixes: + * Support special float values (NaN and ±Infinity) in query responses + + +## 1.45.0 +Released 2017-04-18 + +Bug fixes: + * Add error callback to ogr command while spawning #419 + +Announcements: + * Make the zip command configurable #418 + + +## 1.44.2 +Released 2017-04-05 + +Bug fixes: + * Update queue index while enqueueing jobs to the top of queue. + + +## 1.44.1 +Released 2017-04-04 + +Bug fixes: + * Avoid to scan the whole meta-database to discover active job queues. Now Batch Queries uses a set as index to know what queues are being processed #415 + + +## 1.44.0 +Released 2017-03-30 + +Announcements: + * Active GC interval for Node.js >=v6. + + +## 1.43.1 +Released 2017-01-16 + +Announcements: + * Upgrade cartodb-psql to 0.7.1. + + +## 1.43.0 +Released 2017-01-16 + +Announcements: + * Upgrade cartodb-psql to 0.7.0. + + +## 1.42.7 +Released 2017-01-12 + +Enhancements: + * Avoid gpkg fid column #404. + + +## 1.42.6 +Released 2016-12-19 + +Announcements: + * Upgrade cartodb-redis to 0.13.2. + * Upgrade redis-mpool to 0.4.1. + + +## 1.42.5 +Released 2016-12-12 + +Enhancements: + * Improvements in testing environment/tests. + + +## 1.42.4 +Released 2016-11-30 + +Enhancements: + * Include query status in batch queries log entries. + + +## 1.42.3 +Released 2016-11-07 + +Announcements: + * Raise payload limit for batch-queries to 16kb. + + +## 1.42.2 +Released 2016-11-07 + +Bug fixes: + * Improve error handling while registering jobs to be tracked. + + +## 1.42.1 +Released 2016-11-03 + +Bug fixes: + * Avoid to use SCAN command to find work-in-progress queues. + + +## 1.42.0 +Released 2016-11-02 + +Announcements: + * Adds endpoint to check running batch queries + + +## 1.41.0 +Released 2016-10-21 + +Announcements: + * Stop migrating old queues by default. + +Bug fixes: + * Fix some scenarios where batch queries got stuck waiting for available slots. + + +## 1.40.0 +Released 2016-10-20 + +New features: + * Batch queries are handled per db host. + - There is an scheduler controlling how many queries and in what order they are run. + - Priority is based on: number of queries already ran, and oldest user in queue. + * Batch queries capacity: allow to configure how many jobs to run per db host. + + +## 1.39.1 +Released 2016-10-17 + +Enhancements: + * Log creation and waiting time for fallback jobs' queries. + + +## 1.39.0 +Released 2016-10-17 + +Enhancements: + * Use just one Redis pool across the whole application. + +New features: + * Batch queries use per user-queues. + * Batch queries queues can limit the number of queued jobs per user. + - Default is 64 jobs. + - Configuration key `batch_max_queued_jobs` allows to modify the limit. + + +## 1.38.2 +Released 2016-10-13 + +Bug fixes: + * Batch queries: release redis clients to pool from locker and seeker. + + +## 1.38.1 +Released 2016-10-13 + +Enhancements: + * Batch queries: improvements over leader locking. + + +## 1.38.0 +Released 2016-10-11 + +Announcements: + * Allow to set statement timeout per query in multi query batch queries. + * Batch queries default statement timeout set to 12 hours. + * Multiple queries jobs pushed as first job between queries. + + +## 1.37.1 +Released 2016-10-05 + +Bug fixes: + * Body parser accepting multipart requests. + + +## 1.37.0 +Released 2016-10-04 + +Enhancements: + * Migrate to Express.js 4.x series. + + +## 1.36.2 +Released 2016-10-03 + +Bug fixes: + - Batch Queries logs: use path instead of stream to be able to reopen FD. + +## 1.36.1 +Released 2016-09-30 + +Enhancements: + * Tag fallback jobs logs. + + +## 1.36.0 +Released 2016-09-30 + +New features: + * Log queries from batch fallback jobs. + +Enhancements: + * assert.response following callback(err, obj) pattern. + + +## 1.35.0 +Released 2016-09-15 + +New features: + * Allow to use `--config /path/to/config.js` to specify configuration file. + - Environment will be loaded from config file if `environment` key is present, otherwise it keeps current behaviour. + +Bug fixes: + * Allow to use absolute paths for log files. + +Announcements: + * Removes support for optional rollbar logging. + + +## 1.34.2 +Released 2016-08-30 + +Announcements: + * Upgrades cartodb-redis to 0.13.1. + * Set TTL of finished job to 2h + + +## 1.34.1 +Released 2016-07-11 + +Bug fixes: + * Fixed issue with redis connections in Batch API #326 + + +## 1.34.0 +Released 2016-07-11 + +New features: + * Skip tables with no updated_at registered in cdb_tablemetadata. + * Allow to setup more than one domain to validate oauth against. + + +## 1.33.0 +Released 2016-07-01 + +New features: + * Add `<%= job_id %>` template support for onerror and onsuccess fallback queries. + + +## 1.32.0 +Released 2016-06-30 + +New features: + * Broadcast after enqueueing jobs to improve query distribution load. + * Batch pub-sub channel handles its connections using `redis-mpool`. + + +## 1.31.0 +Released 2016-06-29 + +New features: + * Adds start and end time for batch queries with fallback. + * Add `<%= error_message %>` template support for onerror fallback queries. + + +## 1.30.1 +Released 2016-06-23 + +Bug fixes: + * Fixed issue with profiling in Batch API #318 + + +## 1.30.0 +Released 2016-06-14 + +Announcements: + * Now Batch API sends stats metrics to statsd server #312 + * Now Batch API sets "skipped" instead of "pending" to queries that won't be performed #311 + + Bug fixes: + * Fixed issue with error handling in Batch API #316 + + +## 1.29.2 +Released 2016-05-25 + +Bug fixes: + * Fixed issue with status transition in fallback jobs #308 + + +## 1.29.1 +Released 2016-05-24 + +Announcements: + * Change Batch API size limit: 8kb per job. + + +## 1.29.0 +Released 2016-05-24 + +New features: + * Add support for fallback-jobs in Batch API #296 + +Bug fixes: + * Fix issue in Batch API when a 'no longer running' job reports as 'running' before and after a job cancel #293 + + +## 1.28.1 +Released 2016-05-12 + +Bug fixes: + * OGR with _needSRS=true_ fails for empty tables #299 + + +## 1.28.0 +Released 2016-05-11 + +Announcements: + - Upgrades step-profiler to 0.3.0 to avoid dots in json keys #294 + + New features: + * Add support for geopackage format (`format=gpkg` at the URL) #291 + + +## 1.27.1 +Released 2016-04-18 + +Bug fixes: + * Size of queries in Batch API is limited to 4kb per job + + +## 1.27.0 +Released 2016-04-05 + +New features: + * Add support for multiquery-jobs in Batch API #280 + * Add queue discovering for Batch processing at service startup #282 + +Bug fixes: + * Fix issue in Batch API when after a period of inactivity it does not process jobs + + +## 1.26.0 +Released 2016-03-08 + +New features: + * Add [Surrogate-Key](https://github.com/CartoDB/cartodb/wiki/CartoDB-Surrogate-Keys) headers to responses + +Enhancements: + * Use new `node-cartodb-query-tables` library to obtain affected tables in queries + + +## 1.25.3 +Released 2016-02-02 + +Enhancements: + * QueryTablesApi caches affected tables and retrieves last modification #269 + + +## 1.25.2 +Released 2016-02-01 + +Bug fixes: + * Skip query-tables-api for authenticated requests + + +## 1.25.1 +Released 2016-01-28 + +Bug fixes: + * Fix X-Cache-Channel generation when request are not authenticated #266 + + +## 1.25.0 +Released 2016-01-26 + +Bug fixes: + * Stop adding X-Cache-Channel header when no tables involved #250 + +New features: + * Set `Last-Modified` header based on affected tables (#101) + * Batch API (#261): + - New endpoint to create, read, update and delete long-running queries (jobs). + - Batch service to process jobs. + * Set Last-Modified header based on affected tables #247 + +Announcements: + * Upgrades cartodb-psql to [0.6.1](https://github.com/CartoDB/node-cartodb-psql/releases/tag/0.6.1) + + +## 1.24.0 +Released 2015-08-04 + +New features: + * Client is removed from pool after error happens. This help to avoid issues with transactions (#241). + +Announcements: + * Upgrades cartodb-psql to [0.6.0](https://github.com/CartoDB/node-cartodb-psql/releases/tag/0.6.0) + + +## 1.23.0 +Released 2015-06-16 + +Announcements: + * Reverts tables= for ogr2ogr commands + Ref https://github.com/CartoDB/CartoDB-SQL-API/commit/b6e53f732672369d7f9be26555ef412edc202727 + +Enhancements: + * Makes ogr2ogr command configurable so it's possible to change path/bin + * Uses :remote-addr instead of :req[X-Real-IP] \(#197) + +New features: + * Adds SpatiaLite as export format (#226) + + +## 1.22.2 +Released 2015-05-26 + +Bug fixes: + * Reintroduces tables= param in ogr2ogr exports (#204) + This will avoid running a heavy ogr2ogr query when the pg catalog is big + Ref https://github.com/CartoDB/CartoDB-SQL-API/commit/84c422c505391ef0e743aed2204214d4286d7e30 + + +## 1.22.1 +Released 2015-05-14 + +Bug fixes: + * Close stream responses on error (#219) + +Enhancements: + * Format files split into pg and ogr directories + + +## 1.22.0 +Released 2015-04-09 + +Announcements: + * Now health check only validates against a disabling file + * Supports user extraction from request params via base_url config + + +## 1.21.1 +Released 2015-03-02 + +Enhancements: + * Improve row size limit error message + + +## 1.21.0 +Released 2015-03-02 + +New features: + * Logs with console.error too large row errors + + +## 1.20.0 +Released 2015-02-26 + +Announcements: + * Upgrades cartodb-psql to 0.5.1 for keep alive configuration + * Dependencies from npm registry when available + + +## 1.19.1 +Released 2014-12-15 + +Bug fixes: + * Closes stream responses on error (#188) + * Closes fd for log files on `kill -HUP` (#187) + + +## 1.19.0 +Released 2014-11-21 + +New features: + * Add more fields to error responses with hint, detail and context for SQL errors. + +Enhancements: + * Don't loop twice over svg rows + * Improve statement timeout error messages + * Improve topojson output by streaming json + + +## 1.18.0 +Released 2014-10-14 + +Announcements: + * Dropping support for npm <1.2.1 + npm-shrinkwrap.json is incompatible when generated with npm >=1.2.1 and consumed by npm <1.2.1 + + +## 1.17.1 +Released 2014-09-23 + +Enhancements: + * Removes tables=fake wadus param in ogr2ogr command so it can go to geometry + columns view to retrieve the column data type. This requires to grant select + permission on geometry_columns and geography_columns to the public user. + * Removes query tables console.log + +Bug fixes: + * Fixes "make check" on systems with non-default PostgreSQL superuser (#152) + + +## 1.17.0 +Released 2014-09-17 + +Bug fixes: + * Returns 401 Unauthorized for queries without permission + +New features: + * New header for database host serving the request + * Health check endpoint + +Enhancements: + * Upgrades dependencies: + * cartodb-redis + * cartodb-psql + * log4js + + +## 1.16.0 +Released 2014-08-19 + +Enhancements: + * Metrics revamp: removes and adds some metrics + +## 1.15.0 +Released 2014-08-18 + +Enhancements: + * Upgrades cartodb-redis + * Upgrades underscore, removes underscore.string dependency + * Uses https endpoints for dependencies + + +## 1.14.1 +Released 2014-08-08 + +Other changes: + * Constraint for pg_ queries if request is non authenticated + +## 1.14.0 +Released 2014-08-07 + +Other changes: + * Removes sql statements restriction on pg_ queries + + +## 1.13.0 +Released 2014-08-07 + +New features: + * New authentication mechanism: checks in advance if credentials are provided + in order to do a single request to redis to retrieve the required database + connection parameters. + * Retrieves OAuth hash values using new cartodb-redis method so it will reuse + the redis pool from cartodb-redis instead of using just one pool for oauth. + + +## 1.12.1 +Released 2014-08-05 + +Bug fixes: + * Fixes GeoJSON stream error responses + * Fixes GeoJSON stream empty responses + * JSONP callbacks return with 200 status error code + +Enhancements: + * Re-enables tests + +## 1.12.0 +Released 2014-08-04 + +New features: + * Add header for host serving the request + * Stream JSON/GeoJSON responses + +## 1.11.0 +Released 2014-07-30 + +New features: + * Support for different schemas and different public users + +Enhancements: + * Profiler header sent as JSON string + +Other changes: + * Revamped documentation + +Bug fixes: + * Pick redis pool configuration values + +## 1.10.1 +Released 2014-06-05 + +Bug fixes: + * Backing out Stream JSON responses + +## 1.10.0 +Released 2014-06-04 + +New features: + * Order by and sort order through http query params + * Cancelling queries in Postgresql when HTTP request is aborted/closed + +Enhancements: + * Stream JSON responses + * Pre-compiling may write regex + * Set default PostgreSQL application name to "cartodb_sqlapi" + +Bug fixes: + * Support trailing semicolons (#147) + +## 1.9.1 +Released 2014-03-27 + +Bug fixes: + * Fix paging with queries starting with comments (#144) + +## 1.9.0 +Released 2014-03-20 + +New features: + * Add optional support for rollbar (#137) + * Add '/version' endpoint (#138) + * Add profiler support (#142) + * Add statsd support (#133) + +Enhancements: + * Allow configuring log_format (#131) + * Use log4js for logging (#136) + * Include version in startup log + * Allow passing environment configuration name via NODE_ENV to app.js + * Print environment configuration name on app start + * Upgrade node-zipfile to ~0.5.0 + * Add support for node-0.10 (#132) + * Fix lack of response on backend crash (#135) + * Reduce work on aborted requests (#129) + +Other changes: + * Switch to 3-clause BSD license (#143) + +## 1.8.3 +Released 2014-02-10 + +Bug fixes: + * Honour the 'node_socket_timeout' configuration directive (#128) + +Enhancements: + * Add support for error handling in assert.request + * Stop using ANSI colors in the logs (#130) + +## 1.8.2 +Released 2014-01-20 + +Bug fixes: + * Restore compatibility with 1.6.x configuration + * Use db_port in ogr2ogr + +## 1.8.1 +Released 2014-01-10 + +Bug fixes: + * Fix use of "SELECT .. INTO" with windowing params (#127) + + +## 1.8.0 +Released 2013-12-18 + +New features: +* Add 'user_from_host' directive to generalize username extraction (#124) + +Improvements: +* Enhance error message on unknown cartodb username (#126) + +## 1.7.1 +Released 2013-12-02 + +* Fix documentation for CSV export format: geoms are in hexewkb, not ewkt. +* Fix field types names lookup after PSQL model refactoring + NOTE: fixes missing .prj in shapefile export regression (#122) + +## 1.7.0 +Released 2013-11-19 + +New features: +* Optionally read user-specific database_host and database_password + from redis, as per CartoDB-2.5.0 model (#120, #121) +* Add warnings and notices to JSON response (#104) + +Other changes: +* CartoDB redis interaction delegated to "cartodb-redis" module + +## 1.6.3 +Released 2013-11-10 + +* JSON format: correctly recognize "numeric" type columns (#119) + +## 1.6.2 +Released 2013-11-07 + +* JSON format: correctly recognize "date" type columns (#117) +* Allow access to tables whose name contains (but does not start with) + the "pg_" substring (#118) + +## 1.6.1 +Released 2013-11-05 + +* Still set a meaningful X-Cache-Channel with cache_policy=persist (#105) +* Fix wrong projection in KML exports for manually altered tables (#116) +* Set KML folder name to the requested filename (#115) +* Make public PostgreSQL user name a configuration parameter (#56) + +## 1.6.0 +Released 2013-10-02 + +* Fix shapefile export for non-linestring results starting with NULLs (#111) +* Fix missing .prj in shapefile export (#110) +* Improve recognition of non-standard field types names by db lookup (#112) +* Upgrade node-pg dependency to 2.6.2 +* Drop support for cluster + +## 1.5.4 +Released 2013-10-01 + +* Honour skipfields in JSON schema response (#109) + +## 1.5.3 +Released yyyy-mm-dd + +* Set Last-Modified header with cache_policy=persist +* Raise max-age to one year for all cacheable queries +* Set max-age to 0 for uncacheable (mutating) queries +* Add REINDEX to the list of uncacheable queries +* Support all parameters with POST as well as GET +* Ensure testsuite passes with both GDAL-0.9 and GDAL-0.10 +* JSON output: report boolean types as boolean, not string (#106) + +## 1.5.2 +Released yyyy-mm-dd + +* Keep numbers as such in JSON output (#100) +* Revert max-age=0 in Cache-Control when using no-cache + +## 1.5.1 +Released yyyy-mm-dd + +* Improve cacheability of queries selecting "updated_at" fields (#99) + +## 1.5.0 +Released yyyy-mm-dd + +* Add "fields" member in JSON return (#97) +* Add --skipfields switch to cdbsql +* Fix windowing with CTE +* Retain UTC offset in JSON date output +* Set max-age=0 in Cache-Control when using no-cache + +## 1.4.1 +Released yyyy-mm-dd + +* Fix windowing support for non-uppercased SELECT queries +* Fix oAuth testcase + +## 1.4.0 +Released yyyy-mm-dd + +* Add arraybuffer format +* Fix filesystem access conflict among clustered processes +* Fix discard of queued export requests on error +* Really fix problem identifying OAuth requests + +## 1.3.10 +Released yyyy-mm-dd + +* Fixed problem identifying OAuth request protocol +* Make base url configurable +* Update underscore dependency +* Add munin plugin +* Make PostgreSQL client pooling settings configurable (#47) +* Do not execute queries on OPTIONS (#94) +* Survive postgresql connection losses (#95) + +## 1.3.9 +Released yyyy-mm-dd + +* Do not choke on multiple `skipfields` parameter +* Do not request caching of TRUNCATE queries + +## 1.3.8 +Released yyyy-mm-dd + +* Make using SET or querying system catalogues harder +* Allow sql queries to end with a semicolon (#90) +* Testsuite fixes, jenkins ready :) + +## 1.3.7 +Released yyyy-mm-dd + +* Fix parsing of numeric arrays (#88) +* node-pool upgraded to 2.0.3 +* Reduce memory use on KML export +* Fix concurrent request for KML and Shapefile exports +* Send an empty-like KML when exporting zero-rows queries +* Make temporary dir a configuration setting +* Use OGR for CSV output, reducing memory requirement + +## 1.3.6 +Released yyyy-mm-dd + +* Do not confuse warnings with errors on shapefile output (#87) + +## 1.3.5 +Released 2013-02-19 + +* Fix skipfields use with SHP output format (#81) +* Fix Content-Disposition for error responses (#82) +* Add pid to /cachestatus (#83) +* Check CDB_QueryTable response before saving into cache (#83) +* Use an expiring LRU cache for query tables (#83) +* Fix X-Cache-Channel computation with paging parameters (#85) + +## 1.3.4 +Released 2013-01-21 + +* Improve mixed-geometry export error message (#78) +* Remove NULL the_geom features from topojson output (#80) +* Fix crash when issuing SQL "COPY" command +* Return an error when "the_geom" is in skipfield for SVG output (#73) + +## 1.3.3 +Released 2013-01-11 + +* Fix Date format in CSV output (#77) +* Add TopoJSON output format (#79) + +## 1.3.2 +Released 2012-11-30 + +* Fix KML export truncation (#70) +* Fix UTF8 in shapefile export (#66) + +## 1.3.1 +Released yyyy-mm-dd + +* Support 'format' and 'filename' params in POST +* Fix oAuth bug introduced by 'skipfields' param in 1.3.0 (#69) + +## 1.3.0 +Released yyyy-mm-dd + +* Support for specifying a filename for exports (#64) +* Support for specifying a list of fields to skip from output (#63) +* Add 'cache_policy' parameter (#62) + +## 1.2.1 +Released yyyy-mm-dd + +* Added timeout default to 600 miliseconds in cluster.js + +## 1.2.0 +Released yyyy-mm-dd + +* New output formats: + * ESRI Shapefile (format=shp) + * SVG (format=svg) + * KML (format=kml) +* Advertise header presence in CSV Content-Type +* Fix CSV output with no rows (#60) +* Use "attachment" Content-Disposition for all output formats (#61) +* Only use last format parameter when multiple are requested +* Return a 400 response on unsupported format request +* Added X-Prototype-Version, X-CSRF-Token to Access-Control-Allow-Headers + +## 1.1.0 +Released 2012-10-30 + +* Fixed problem in cluster2 with pidfile name +* SVG output format +* Enhancement to the cdbsql tool: + - New switches: --format, --key, --dp + - Interactive mode +* API documentation +* ./configure script +* Restrict listening to a node host + +## 1.0.0 +Released 2012-10-03 + +* Migrated to node 0.8 version + +## 0.9.0 +Released 2012-09-18 + +* Fix INSERT and UPDATE with RETURNING clause diff --git a/README.md b/README.md new file mode 100644 index 0000000..5dc17c3 --- /dev/null +++ b/README.md @@ -0,0 +1,82 @@ +SQL API for carto.com +======================== + +[![Build Status](https://travis-ci.org/CartoDB/CartoDB-SQL-API.png?branch=master)](https://travis-ci.org/CartoDB/CartoDB-SQL-API) + +Provides a node.js based API for running SQL queries against CartoDB. + +* Users are authenticated over OAuth or via an API KEY. +* Authenticated requests to this API should always be made over SSL. + + +core requirements +----------------- +* Node >= 10.14.2 or 6.9.2 +* npm >= 6.4.1 || 3.10.9 || 3.10.10 +* Postgres `9.3+`. +* Postgis `2.2`. +* [CartoDB Postgres Extension](https://github.com/CartoDB/cartodb-postgresql/blob/0.19.2/README.md) `0.19+`. +* GDAL `1.11.0` (bin utils). See [installing GDAL](http://trac.osgeo.org/gdal/wiki/DownloadingGdalBinaries) +* zip commandline tool. +* Redis `3`, recommended reversion `3.0.2`. + + +Install dependencies +-------------------- + +- Node.js >= 10.14.2: +``` +$ mv npm-shrinkwrap.json npm-shrinkwrap.json.backup +$ npm ci +$ mv npm-shrinkwrap.json.backup npm-shrinkwrap.json +``` + +- Node.js 6.9.2: +```sh +npm install +``` + + +usage +----- + +Create and edit config/environments/.js from .js.example files. +You may find the ./configure script useful to make an edited copy for you, +see ```./configure --help``` for a list of supported switches. + +Make sure redis is running and knows about active cartodb user. + +Make sure your PostgreSQL server is running, is accessible on +the host and port specified in the file, has +a 'publicuser' role (or whatever you set ``db_pubuser`` configuration +directive to) and trusts user authentication from localhost +connections. + +```sh +node app.js +``` + +Supported values are development, test, production + +See doc/API.md for API documentation. +For examples of use, see under test/. + + +tests +----- + +Run with: + +```sh +npm test +``` + +If any issue arise see test/README.md + +Note that the environment should be set to ensure the default +PostgreSQL user is superuser (PGUSER=postgres make check). + +Contributing +--- + +See [CONTRIBUTING.md](CONTRIBUTING.md). diff --git a/app.js b/app.js new file mode 100755 index 0000000..0c60a0f --- /dev/null +++ b/app.js @@ -0,0 +1,241 @@ +#!/usr/bin/env node + +'use strict'; + +/* +* SQL API loader +* =============== +* +* node app [environment] +* +* environments: [development, test, production] +* +*/ +var fs = require('fs'); +var path = require('path'); +const fqdn = require('@carto/fqdn-sync'); + +var argv = require('yargs') + .usage('Usage: $0 [options]') + .help('h') + .example( + '$0 production -c /etc/sql-api/config.js', + 'start server in production environment with /etc/sql-api/config.js as config file' + ) + .alias('h', 'help') + .alias('c', 'config') + .nargs('c', 1) + .describe('c', 'Load configuration from path') + .argv; + +var environmentArg = argv._[0] || process.env.NODE_ENV || 'development'; +var configurationFile = path.resolve(argv.config || './config/environments/' + environmentArg + '.js'); +if (!fs.existsSync(configurationFile)) { + console.error('Configuration file "%s" does not exist', configurationFile); + process.exit(1); +} + +global.settings = require(configurationFile); +var ENVIRONMENT = argv._[0] || process.env.NODE_ENV || global.settings.environment; +process.env.NODE_ENV = ENVIRONMENT; + +var 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); +} + +global.settings.api_hostname = fqdn.hostname(); + +global.log4js = require('log4js'); +var log4jsConfig = { + appenders: [], + replaceConsole: true +}; + +if ( global.settings.log_filename ) { + var logFilename = path.resolve(global.settings.log_filename); + var logDirectory = path.dirname(logFilename); + if (!fs.existsSync(logDirectory)) { + console.error("Log filename directory does not exist: " + logDirectory); + process.exit(1); + } + console.log("Logs will be written to " + logFilename); + log4jsConfig.appenders.push( + { type: "file", absolute: true, filename: logFilename } + ); +} else { + log4jsConfig.appenders.push( + { type: "console", layout: { type:'basic' } } + ); +} +global.log4js.configure(log4jsConfig); +global.logger = global.log4js.getLogger(); + + +// kick off controller +if ( ! global.settings.base_url ) { + global.settings.base_url = '/api/*'; +} + +var version = require("./package").version; + +var StatsClient = require('./app/stats/client'); +if (global.settings.statsd) { + // Perform keyword substitution in statsd + if (global.settings.statsd.prefix) { + global.settings.statsd.prefix = global.settings.statsd.prefix.replace(/:host/, fqdn.reverse()); + } +} +var statsClient = StatsClient.getInstance(global.settings.statsd); + +var server = require('./app/server')(statsClient); +var listener = server.listen(global.settings.node_port, global.settings.node_host); +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)", + version, global.settings.node_host, global.settings.node_port, process.pid, ENVIRONMENT + ); +}); + +process.on('uncaughtException', function(err) { + global.logger.error('Uncaught exception: ' + err.stack); +}); + +process.on('SIGHUP', function() { + global.log4js.clearAndShutdownAppenders(function() { + global.log4js.configure(log4jsConfig); + global.logger = global.log4js.getLogger(); + console.log('Log files reloaded'); + }); + + if (server.batch && server.batch.logger) { + server.batch.logger.reopenFileStreams(); + } + + if (server.dataIngestionLogger) { + server.dataIngestionLogger.reopenFileStreams(); + } +}); + +process.on('SIGTERM', function () { + server.batch.stop(); + server.batch.drain(function (err) { + if (err) { + console.log('Exit with error'); + return process.exit(1); + } + + console.log('Exit gracefully'); + process.exit(0); + }); +}); + +function isGteMinVersion(version, minVersion) { + var versionMatch = /[a-z]?([0-9]*)/.exec(version); + if (versionMatch) { + var majorVersion = parseInt(versionMatch[1], 10); + if (Number.isFinite(majorVersion)) { + return majorVersion >= minVersion; + } + } + return false; +} + +setInterval(function memoryUsageMetrics () { + let memoryUsage = process.memoryUsage(); + + Object.keys(memoryUsage).forEach(property => { + statsClient.gauge(`sqlapi.memory.${property}`, memoryUsage[property]); + }); +}, 5000); + +function getCPUUsage (oldUsage) { + let usage; + + if (oldUsage && oldUsage._start) { + usage = Object.assign({}, process.cpuUsage(oldUsage._start.cpuUsage)); + usage.time = Date.now() - oldUsage._start.time; + } else { + usage = Object.assign({}, process.cpuUsage()); + usage.time = process.uptime() * 1000; // s to ms + } + + usage.percent = (usage.system + usage.user) / (usage.time * 10); + + Object.defineProperty(usage, '_start', { + value: { + cpuUsage: process.cpuUsage(), + time: Date.now() + } + }); + + return usage; +} + +let previousCPUUsage = getCPUUsage(); +setInterval(function cpuUsageMetrics () { + const CPUUsage = getCPUUsage(previousCPUUsage); + + Object.keys(CPUUsage).forEach(property => { + statsClient.gauge(`sqlapi.cpu.${property}`, CPUUsage[property]); + }); + + previousCPUUsage = CPUUsage; +}, 5000); + +if (global.gc && isGteMinVersion(process.version, 6)) { + var gcInterval = Number.isFinite(global.settings.gc_interval) ? + global.settings.gc_interval : + 10000; + + if (gcInterval > 0) { + setInterval(function gcForcedCycle() { + global.gc(); + }, gcInterval); + } +} + +const gcStats = require('gc-stats')(); + +gcStats.on('stats', function ({ pauseMS, gctype }) { + statsClient.timing('sqlapi.gc', pauseMS); + statsClient.timing(`sqlapi.gctype.${getGCTypeValue(gctype)}`, pauseMS); +}); + +function getGCTypeValue (type) { + // 1: Scavenge (minor GC) + // 2: Mark/Sweep/Compact (major GC) + // 4: Incremental marking + // 8: Weak/Phantom callback processing + // 15: All + 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; + } + + return value; +} diff --git a/app/auth/apikey.js b/app/auth/apikey.js new file mode 100644 index 0000000..f1176cb --- /dev/null +++ b/app/auth/apikey.js @@ -0,0 +1,74 @@ +'use strict'; + +/** + * this module allows to auth user using an pregenerated api key + */ +function ApikeyAuth(req, metadataBackend, username, apikeyToken) { + this.req = req; + this.metadataBackend = metadataBackend; + this.username = username; + this.apikeyToken = apikeyToken; +} + +module.exports = ApikeyAuth; + +function usernameMatches(basicAuthUsername, requestUsername) { + return !(basicAuthUsername && (basicAuthUsername !== requestUsername)); +} + +ApikeyAuth.prototype.verifyCredentials = function (callback) { + this.metadataBackend.getApikey(this.username, this.apikeyToken, (err, apikey) => { + if (err) { + err.http_status = 500; + err.message = 'Unexpected error'; + + return callback(err); + } + + if (isApiKeyFound(apikey)) { + if (!usernameMatches(apikey.user, this.username)) { + const usernameError = new Error('Forbidden'); + usernameError.type = 'auth'; + usernameError.subtype = 'api-key-username-mismatch'; + usernameError.http_status = 403; + + return callback(usernameError); + } + + if (!apikey.grantsSql) { + const forbiddenError = new Error('forbidden'); + forbiddenError.http_status = 403; + + return callback(forbiddenError); + } + + return callback(null, getAuthorizationLevel(apikey)); + } else { + const apiKeyNotFoundError = new Error('Unauthorized'); + apiKeyNotFoundError.type = 'auth'; + apiKeyNotFoundError.subtype = 'api-key-not-found'; + apiKeyNotFoundError.http_status = 401; + + return callback(apiKeyNotFoundError); + } + }); +}; + +ApikeyAuth.prototype.hasCredentials = function () { + return !!this.apikeyToken; +}; + +ApikeyAuth.prototype.getCredentials = function () { + return this.apikeyToken; +}; + +function getAuthorizationLevel(apikey) { + return apikey.type; +} + +function isApiKeyFound(apikey) { + return apikey.type !== null && + apikey.user !== null && + apikey.databasePassword !== null && + apikey.databaseRole !== null; +} diff --git a/app/auth/auth_api.js b/app/auth/auth_api.js new file mode 100644 index 0000000..fe6c327 --- /dev/null +++ b/app/auth/auth_api.js @@ -0,0 +1,48 @@ +'use strict'; + +var ApiKeyAuth = require('./apikey'), + OAuthAuth = require('./oauth'); + +function AuthApi(req, requestParams) { + this.req = req; + this.authBackend = getAuthBackend(req, requestParams); + + this._hasCredentials = null; +} + +AuthApi.prototype.getType = function () { + if (this.authBackend instanceof ApiKeyAuth) { + return 'apiKey'; + } else if (this.authBackend instanceof OAuthAuth) { + return 'oAuth'; + } +}; + +AuthApi.prototype.hasCredentials = function() { + if (this._hasCredentials === null) { + this._hasCredentials = this.authBackend.hasCredentials(); + } + return this._hasCredentials; +}; + +AuthApi.prototype.getCredentials = function() { + return this.authBackend.getCredentials(); +}; + +AuthApi.prototype.verifyCredentials = function(callback) { + if (this.hasCredentials()) { + this.authBackend.verifyCredentials(callback); + } else { + callback(null, false); + } +}; + +function getAuthBackend(req, requestParams) { + if (requestParams.api_key) { + return new ApiKeyAuth(req, requestParams.metadataBackend, requestParams.user, requestParams.api_key); + } else { + return new OAuthAuth(req, requestParams.metadataBackend); + } +} + +module.exports = AuthApi; diff --git a/app/auth/oauth.js b/app/auth/oauth.js new file mode 100644 index 0000000..298ef3c --- /dev/null +++ b/app/auth/oauth.js @@ -0,0 +1,192 @@ +'use strict'; + +// too bound to the request object, but ok for now +var _ = require('underscore'); +var OAuthUtil = require('oauth-client'); +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 + }; + + // 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 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); + }; + + // 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]; + } + } + return removed; + }; + + 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; + } + + // this is oauth request only if oauth headers are present + this.is_oauth_request = !_.isEmpty(_requestTokens); + + 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; + } + + 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 method = req.method; + var hostsToValidate = {}; + var requestHost = req.headers.host; + hostsToValidate[requestHost] = true; + that.getAllowedHosts().forEach(function(allowedHost) { + hostsToValidate[username + '.' + allowedHost] = 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); + } + ); + }; + + me.getOAuthHash = function(metadataBackend, oAuthAccessKey, callback){ + metadataBackend.getOAuthHash(oAuthAccessKey, callback); + }; + + return me; +})(); + +function OAuthAuth(req, metadataBackend) { + this.req = req; + this.metadataBackend = metadataBackend; + this.isOAuthRequest = null; +} + +OAuthAuth.prototype.verifyCredentials = function(callback) { + if (this.hasCredentials()) { + oAuth.verifyRequest(this.req, this.metadataBackend, callback); + } else { + callback(null, false); + } +}; + +OAuthAuth.prototype.getCredentials = function() { + return oAuth.parseTokens(this.req); +}; + +OAuthAuth.prototype.hasCredentials = function() { + if (this.isOAuthRequest === null) { + var passed_tokens = oAuth.parseTokens(this.req); + this.isOAuthRequest = !_.isEmpty(passed_tokens); + } + + return this.isOAuthRequest; +}; + + +module.exports = OAuthAuth; +module.exports.backend = oAuth; diff --git a/app/controllers/cache_status_controller.js b/app/controllers/cache_status_controller.js new file mode 100644 index 0000000..2b7a4cf --- /dev/null +++ b/app/controllers/cache_status_controller.js @@ -0,0 +1,29 @@ +'use strict'; + +var _ = require('underscore'); + +function CacheStatusController(tableCache) { + this.tableCache = tableCache; +} + +CacheStatusController.prototype.route = function (app) { + app.get(global.settings.base_url + '/cachestatus', this.handleCacheStatus.bind(this)); +}; + +CacheStatusController.prototype.handleCacheStatus = function (req, res) { + var tableCacheValues = this.tableCache.values(); + var totalExplainKeys = tableCacheValues.length; + var totalExplainHits = _.reduce(tableCacheValues, function(memo, res) { + return memo + res.hits; + }, 0); + + res.send({ + explain: { + pid: process.pid, + hits: totalExplainHits, + keys : totalExplainKeys + } + }); +}; + +module.exports = CacheStatusController; diff --git a/app/controllers/copy_controller.js b/app/controllers/copy_controller.js new file mode 100644 index 0000000..9e9bc72 --- /dev/null +++ b/app/controllers/copy_controller.js @@ -0,0 +1,207 @@ +'use strict'; + +const userMiddleware = require('../middlewares/user'); +const errorMiddleware = require('../middlewares/error'); +const authorizationMiddleware = require('../middlewares/authorization'); +const connectionParamsMiddleware = require('../middlewares/connection-params'); +const { initializeProfilerMiddleware } = require('../middlewares/profiler'); +const rateLimitsMiddleware = require('../middlewares/rate-limit'); +const dbQuotaMiddleware = require('../middlewares/db-quota'); +const { RATE_LIMIT_ENDPOINTS_GROUPS } = rateLimitsMiddleware; +const errorHandlerFactory = require('../services/error_handler_factory'); +const StreamCopy = require('../services/stream_copy'); +const StreamCopyMetrics = require('../services/stream_copy_metrics'); +const zlib = require('zlib'); +const { PassThrough } = require('stream'); + +function CopyController(metadataBackend, userDatabaseService, userLimitsService, logger) { + this.metadataBackend = metadataBackend; + this.userDatabaseService = userDatabaseService; + this.userLimitsService = userLimitsService; + this.logger = logger; +} + +CopyController.prototype.route = function (app) { + const { base_url } = global.settings; + + const copyFromMiddlewares = endpointGroup => { + return [ + initializeProfilerMiddleware('copyfrom'), + userMiddleware(this.metadataBackend), + rateLimitsMiddleware(this.userLimitsService, endpointGroup), + authorizationMiddleware(this.metadataBackend), + connectionParamsMiddleware(this.userDatabaseService), + validateCopyQuery(), + dbQuotaMiddleware(), + handleCopyFrom(this.logger), + errorHandler(), + errorMiddleware() + ]; + }; + + const copyToMiddlewares = endpointGroup => { + return [ + initializeProfilerMiddleware('copyto'), + userMiddleware(this.metadataBackend), + rateLimitsMiddleware(this.userLimitsService, endpointGroup), + authorizationMiddleware(this.metadataBackend), + connectionParamsMiddleware(this.userDatabaseService), + validateCopyQuery(), + handleCopyTo(this.logger), + errorHandler(), + errorMiddleware() + ]; + }; + + app.post(`${base_url}/sql/copyfrom`, copyFromMiddlewares(RATE_LIMIT_ENDPOINTS_GROUPS.COPY_FROM)); + app.get(`${base_url}/sql/copyto`, copyToMiddlewares(RATE_LIMIT_ENDPOINTS_GROUPS.COPY_TO)); +}; + + +function handleCopyTo (logger) { + return function handleCopyToMiddleware (req, res, next) { + const sql = req.query.q; + const { userDbParams, user } = res.locals; + const filename = req.query.filename || 'carto-sql-copyto.dmp'; + + // it is not sure, nginx may choose not to compress the body + // but we want to know it and save it in the metrics + // https://github.com/CartoDB/CartoDB-SQL-API/issues/515 + const isGzip = req.get('accept-encoding') && req.get('accept-encoding').includes('gzip'); + + const streamCopy = new StreamCopy(sql, userDbParams); + const metrics = new StreamCopyMetrics(logger, 'copyto', sql, user, isGzip); + + res.header("Content-Disposition", `attachment; filename=${encodeURIComponent(filename)}`); + res.header("Content-Type", "application/octet-stream"); + + streamCopy.getPGStream(StreamCopy.ACTION_TO, (err, pgstream) => { + if (err) { + return next(err); + } + + pgstream + .on('data', data => metrics.addSize(data.length)) + .on('error', err => { + metrics.end(null, err); + pgstream.unpipe(res); + + return next(err); + }) + .on('end', () => metrics.end( streamCopy.getRowCount(StreamCopy.ACTION_TO) )) + .pipe(res) + .on('close', () => { + const err = new Error('Connection closed by client'); + pgstream.emit('cancelQuery', err); + pgstream.emit('error', err); + }) + .on('error', err => { + pgstream.emit('error', err); + }); + }); + }; +} + +function handleCopyFrom (logger) { + return function handleCopyFromMiddleware (req, res, next) { + const sql = req.query.q; + const { userDbParams, user, dbRemainingQuota } = res.locals; + const isGzip = req.get('content-encoding') === 'gzip'; + const COPY_FROM_MAX_POST_SIZE = global.settings.copy_from_max_post_size || 2 * 1024 * 1024 * 1024; // 2 GB + const COPY_FROM_MAX_POST_SIZE_PRETTY = global.settings.copy_from_max_post_size_pretty || '2 GB'; + + const streamCopy = new StreamCopy(sql, userDbParams); + const metrics = new StreamCopyMetrics(logger, 'copyfrom', sql, user, isGzip); + + streamCopy.getPGStream(StreamCopy.ACTION_FROM, (err, pgstream) => { + if (err) { + return next(err); + } + + req + .on('data', data => isGzip ? metrics.addGzipSize(data.length) : undefined) + .on('error', err => { + metrics.end(null, err); + pgstream.emit('error', err); + }) + .on('close', () => { + const err = new Error('Connection closed by client'); + pgstream.emit('cancelQuery', err); + pgstream.emit('error', err); + }) + .pipe(isGzip ? zlib.createGunzip() : new PassThrough()) + .on('error', err => { + err.message = `Error while gunzipping: ${err.message}`; + metrics.end(null, err); + pgstream.emit('error', err); + }) + .on('data', data => { + metrics.addSize(data.length); + + if(metrics.size > dbRemainingQuota) { + const quotaError = new Error('DB Quota exceeded'); + pgstream.emit('cancelQuery', err); + pgstream.emit('error', quotaError); + } + if((metrics.gzipSize || metrics.size) > COPY_FROM_MAX_POST_SIZE) { + const maxPostSizeError = new Error( + `COPY FROM maximum POST size of ${COPY_FROM_MAX_POST_SIZE_PRETTY} exceeded` + ); + pgstream.emit('cancelQuery', err); + pgstream.emit('error', maxPostSizeError); + } + }) + .pipe(pgstream) + .on('error', err => { + metrics.end(null, err); + req.unpipe(pgstream); + return next(err); + }) + .on('end', () => { + metrics.end( streamCopy.getRowCount(StreamCopy.ACTION_FROM) ); + + const { time, rows } = metrics; + + if (!rows) { + return next(new Error("No rows copied")); + } + + res.send({ + time, + total_rows: rows + }); + }); + }); + }; +} + +function validateCopyQuery () { + return function validateCopyQueryMiddleware (req, res, next) { + const sql = req.query.q; + + if (!sql) { + return next(new Error("SQL is missing")); + } + + if (!sql.toUpperCase().startsWith("COPY ")) { + return next(new Error("SQL must start with COPY")); + } + + next(); + }; +} + +function errorHandler () { + return function errorHandlerMiddleware (err, req, res, next) { + if (res.headersSent) { + console.error("EXCEPTION REPORT: " + err.stack); + const errorHandler = errorHandlerFactory(err); + res.write(JSON.stringify(errorHandler.getResponse())); + res.end(); + } else { + return next(err); + } + }; +} + +module.exports = CopyController; diff --git a/app/controllers/generic_controller.js b/app/controllers/generic_controller.js new file mode 100644 index 0000000..ab3c560 --- /dev/null +++ b/app/controllers/generic_controller.js @@ -0,0 +1,14 @@ +'use strict'; + +function GenericController() { +} + +GenericController.prototype.route = function (app) { + app.options('*', this.handleRequest.bind(this)); +}; + +GenericController.prototype.handleRequest = function(req, res) { + res.end(); +}; + +module.exports = GenericController; diff --git a/app/controllers/health_check_controller.js b/app/controllers/health_check_controller.js new file mode 100644 index 0000000..a5f84f1 --- /dev/null +++ b/app/controllers/health_check_controller.js @@ -0,0 +1,35 @@ +'use strict'; + +var HealthCheck = require('../monitoring/health_check'); + +function HealthCheckController() { + this.healthCheck = new HealthCheck(global.settings.disabled_file); +} + +HealthCheckController.prototype.route = function (app) { + app.get(global.settings.base_url + '/health', this.handleHealthCheck.bind(this)); +}; + +HealthCheckController.prototype.handleHealthCheck = function (req, res) { + var healthConfig = global.settings.health || {}; + if (!!healthConfig.enabled) { + var startTime = Date.now(); + this.healthCheck.check(function(err) { + var ok = !err; + var response = { + enabled: true, + ok: ok, + elapsed: Date.now() - startTime + }; + if (err) { + response.err = err.message; + } + res.status(ok ? 200 : 503).send(response); + + }); + } else { + res.status(200).send({enabled: false, ok: true}); + } +}; + +module.exports = HealthCheckController; diff --git a/app/controllers/job_controller.js b/app/controllers/job_controller.js new file mode 100644 index 0000000..a169684 --- /dev/null +++ b/app/controllers/job_controller.js @@ -0,0 +1,253 @@ +'use strict'; + +const util = require('util'); + +const bodyParserMiddleware = require('../middlewares/body-parser'); +const userMiddleware = require('../middlewares/user'); +const { initializeProfilerMiddleware, finishProfilerMiddleware } = require('../middlewares/profiler'); +const authorizationMiddleware = require('../middlewares/authorization'); +const connectionParamsMiddleware = require('../middlewares/connection-params'); +const errorMiddleware = require('../middlewares/error'); +const rateLimitsMiddleware = require('../middlewares/rate-limit'); +const { RATE_LIMIT_ENDPOINTS_GROUPS } = rateLimitsMiddleware; + +function JobController(metadataBackend, userDatabaseService, jobService, statsdClient, userLimitsService) { + this.metadataBackend = metadataBackend; + this.userDatabaseService = userDatabaseService; + this.jobService = jobService; + this.statsdClient = statsdClient; + this.userLimitsService = userLimitsService; +} + +module.exports = JobController; + +JobController.prototype.route = function (app) { + const { base_url } = global.settings; + const jobMiddlewares = composeJobMiddlewares( + this.metadataBackend, + this.userDatabaseService, + this.jobService, + this.statsdClient, + this.userLimitsService + ); + + app.get( + `${base_url}/jobs-wip`, + bodyParserMiddleware(), + listWorkInProgressJobs(this.jobService), + sendResponse(), + errorMiddleware() + ); + app.post( + `${base_url}/sql/job`, + bodyParserMiddleware(), + checkBodyPayloadSize(), + jobMiddlewares('create', createJob, RATE_LIMIT_ENDPOINTS_GROUPS.JOB_CREATE) + ); + app.get( + `${base_url}/sql/job/:job_id`, + bodyParserMiddleware(), + jobMiddlewares('retrieve', getJob, RATE_LIMIT_ENDPOINTS_GROUPS.JOB_GET) + ); + app.delete( + `${base_url}/sql/job/:job_id`, + bodyParserMiddleware(), + jobMiddlewares('cancel', cancelJob, RATE_LIMIT_ENDPOINTS_GROUPS.JOB_DELETE) + ); +}; + +function composeJobMiddlewares (metadataBackend, userDatabaseService, jobService, statsdClient, userLimitsService) { + return function jobMiddlewares (action, jobMiddleware, endpointGroup) { + const forceToBeMaster = true; + + return [ + initializeProfilerMiddleware('job'), + userMiddleware(metadataBackend), + rateLimitsMiddleware(userLimitsService, endpointGroup), + authorizationMiddleware(metadataBackend, forceToBeMaster), + connectionParamsMiddleware(userDatabaseService), + jobMiddleware(jobService), + setServedByDBHostHeader(), + finishProfilerMiddleware(), + logJobResult(action), + incrementSuccessMetrics(statsdClient), + sendResponse(), + incrementErrorMetrics(statsdClient), + errorMiddleware() + ]; + }; +} + +function cancelJob (jobService) { + return function cancelJobMiddleware (req, res, next) { + const { job_id } = req.params; + + jobService.cancel(job_id, (err, job) => { + if (req.profiler) { + req.profiler.done('cancelJob'); + } + + if (err) { + return next(err); + } + + res.body = job.serialize(); + + next(); + }); + }; +} + +function getJob (jobService) { + return function getJobMiddleware (req, res, next) { + const { job_id } = req.params; + + jobService.get(job_id, (err, job) => { + if (req.profiler) { + req.profiler.done('getJob'); + } + + if (err) { + return next(err); + } + + res.body = job.serialize(); + + next(); + }); + }; +} + +function createJob (jobService) { + return function createJobMiddleware (req, res, next) { + const params = Object.assign({}, req.query, req.body); + + var data = { + user: res.locals.user, + query: params.query, + host: res.locals.userDbParams.host, + port: global.settings.db_batch_port || res.locals.userDbParams.port, + pass: res.locals.userDbParams.pass, + dbname: res.locals.userDbParams.dbname, + dbuser: res.locals.userDbParams.user + }; + + jobService.create(data, (err, job) => { + if (req.profiler) { + req.profiler.done('createJob'); + } + + if (err) { + return next(err); + } + + res.locals.job_id = job.job_id; + + res.statusCode = 201; + res.body = job.serialize(); + + next(); + }); + }; +} + +function listWorkInProgressJobs (jobService) { + return function listWorkInProgressJobsMiddleware (req, res, next) { + jobService.listWorkInProgressJobs((err, list) => { + if (err) { + return next(err); + } + + res.body = list; + + next(); + }); + }; +} + + +function checkBodyPayloadSize () { + return function checkBodyPayloadSizeMiddleware(req, res, next) { + const payload = JSON.stringify(req.body); + + if (payload.length > MAX_LIMIT_QUERY_SIZE_IN_BYTES) { + return next(new Error(getMaxSizeErrorMessage(payload)), res); + } + + next(); + }; +} + +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) { + 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) + ); +} + +module.exports.MAX_LIMIT_QUERY_SIZE_IN_BYTES = MAX_LIMIT_QUERY_SIZE_IN_BYTES; +module.exports.getMaxSizeErrorMessage = getMaxSizeErrorMessage; + +function setServedByDBHostHeader () { + return function setServedByDBHostHeaderMiddleware (req, res, next) { + const { userDbParams } = res.locals; + + if (userDbParams.host) { + res.header('X-Served-By-DB-Host', res.locals.userDbParams.host); + } + + next(); + }; +} + +function logJobResult (action) { + return function logJobResultMiddleware (req, res, next) { + if (process.env.NODE_ENV !== 'test') { + console.info(JSON.stringify({ + type: 'sql_api_batch_job', + username: res.locals.user, + action: action, + job_id: req.params.job_id || res.locals.job_id + })); + } + + next(); + }; +} + +const METRICS_PREFIX = 'sqlapi.job'; + +function incrementSuccessMetrics (statsdClient) { + return function incrementSuccessMetricsMiddleware (req, res, next) { + if (statsdClient !== undefined) { + statsdClient.increment(`${METRICS_PREFIX}.success`); + } + + next(); + }; +} + +function incrementErrorMetrics (statsdClient) { + return function incrementErrorMetricsMiddleware (err, req, res, next) { + if (statsdClient !== undefined) { + statsdClient.increment(`${METRICS_PREFIX}.error`); + } + + next(err); + }; +} + +function sendResponse () { + return function sendResponseMiddleware (req, res) { + res.status(res.statusCode || 200).send(res.body); + }; +} diff --git a/app/controllers/query_controller.js b/app/controllers/query_controller.js new file mode 100644 index 0000000..c38ad11 --- /dev/null +++ b/app/controllers/query_controller.js @@ -0,0 +1,275 @@ +'use strict'; + +var _ = require('underscore'); +var step = require('step'); +var PSQL = require('cartodb-psql'); +var CachedQueryTables = require('../services/cached-query-tables'); +const pgEntitiesAccessValidator = require('../services/pg-entities-access-validator'); +var queryMayWrite = require('../utils/query_may_write'); + +var formats = require('../models/formats'); + +var sanitize_filename = require('../utils/filename_sanitizer'); +var getContentDisposition = require('../utils/content_disposition'); +const bodyParserMiddleware = require('../middlewares/body-parser'); +const userMiddleware = require('../middlewares/user'); +const errorMiddleware = require('../middlewares/error'); +const authorizationMiddleware = require('../middlewares/authorization'); +const connectionParamsMiddleware = require('../middlewares/connection-params'); +const timeoutLimitsMiddleware = require('../middlewares/timeout-limits'); +const { initializeProfilerMiddleware } = require('../middlewares/profiler'); +const rateLimitsMiddleware = require('../middlewares/rate-limit'); +const { RATE_LIMIT_ENDPOINTS_GROUPS } = rateLimitsMiddleware; + +var ONE_YEAR_IN_SECONDS = 31536000; // 1 year time to live by default + +function QueryController(metadataBackend, userDatabaseService, tableCache, statsd_client, userLimitsService) { + this.metadataBackend = metadataBackend; + this.statsd_client = statsd_client; + this.userDatabaseService = userDatabaseService; + this.queryTables = new CachedQueryTables(tableCache); + this.userLimitsService = userLimitsService; +} + +QueryController.prototype.route = function (app) { + const { base_url } = global.settings; + const forceToBeMaster = false; + + const queryMiddlewares = () => { + return [ + bodyParserMiddleware(), + initializeProfilerMiddleware('query'), + userMiddleware(this.metadataBackend), + rateLimitsMiddleware(this.userLimitsService, RATE_LIMIT_ENDPOINTS_GROUPS.QUERY), + authorizationMiddleware(this.metadataBackend, forceToBeMaster), + connectionParamsMiddleware(this.userDatabaseService), + timeoutLimitsMiddleware(this.metadataBackend), + this.handleQuery.bind(this), + errorMiddleware() + ]; + }; + + app.all(`${base_url}/sql`, queryMiddlewares()); + app.all(`${base_url}/sql.:f`, queryMiddlewares()); +}; + +// jshint maxcomplexity:21 +QueryController.prototype.handleQuery = function (req, res, next) { + var self = this; + // extract input + var body = (req.body) ? req.body : {}; + // clone so don't modify req.params or req.body so oauth is not broken + var params = _.extend({}, req.query, body); + var sql = params.q; + var limit = parseInt(params.rows_per_page); + var offset = parseInt(params.page); + var orderBy = params.order_by; + var sortOrder = params.sort_order; + var requestedFormat = params.format; + var format = _.isArray(requestedFormat) ? _.last(requestedFormat) : requestedFormat; + var requestedFilename = params.filename; + var filename = requestedFilename; + var requestedSkipfields = params.skipfields; + + const { user: username, userDbParams: dbopts, authDbParams, userLimits, authorizationLevel } = res.locals; + + var skipfields; + var dp = params.dp; // decimal point digits (defaults to 6) + var gn = "the_geom"; // TODO: read from configuration FILE + + req.aborted = false; + req.on("close", function() { + if (req.formatter && _.isFunction(req.formatter.cancel)) { + req.formatter.cancel(); + } + req.aborted = true; // TODO: there must be a builtin way to check this + }); + + function checkAborted(step) { + if ( req.aborted ) { + var err = new Error("Request aborted during " + step); + // We'll use status 499, same as ngnix in these cases + // see http://en.wikipedia.org/wiki/List_of_HTTP_status_codes#4xx_Client_Error + err.http_status = 499; + throw err; + } + } + + try { + + // sanitize and apply defaults to input + dp = (dp === "" || _.isUndefined(dp)) ? '6' : dp; + format = (format === "" || _.isUndefined(format)) ? 'json' : format.toLowerCase(); + filename = (filename === "" || _.isUndefined(filename)) ? 'cartodb-query' : sanitize_filename(filename); + sql = (sql === "" || _.isUndefined(sql)) ? null : sql; + limit = (!_.isNaN(limit)) ? limit : null; + offset = (!_.isNaN(offset)) ? offset * limit : null; + + // Accept both comma-separated string or array of comma-separated strings + if ( requestedSkipfields ) { + if ( _.isString(requestedSkipfields) ) { + skipfields = requestedSkipfields.split(','); + } else if ( _.isArray(requestedSkipfields) ) { + skipfields = []; + _.each(requestedSkipfields, function(ele) { + skipfields = skipfields.concat(ele.split(',')); + }); + } + } else { + skipfields = []; + } + + //if ( -1 === supportedFormats.indexOf(format) ) + if ( ! formats.hasOwnProperty(format) ) { + throw new Error("Invalid format: " + format); + } + + if (!_.isString(sql)) { + throw new Error("You must indicate a sql query"); + } + + var formatter; + + if ( req.profiler ) { + req.profiler.done('init'); + } + + // 1. Get the list of tables affected by the query + // 2. Setup headers + // 3. Send formatted results back + // 4. Handle error + step( + function queryExplain() { + var next = this; + + checkAborted('queryExplain'); + + var pg = new PSQL(authDbParams); + + var skipCache = authorizationLevel === 'master'; + + self.queryTables.getAffectedTablesFromQuery(pg, sql, skipCache, function(err, result) { + if (err) { + var errorMessage = (err && err.message) || 'unknown error'; + console.error("Error on query explain '%s': %s", sql, errorMessage); + } + return next(null, result); + }); + }, + function setHeaders(err, affectedTables) { + if (err) { + throw err; + } + + var mayWrite = queryMayWrite(sql); + if ( req.profiler ) { + req.profiler.done('queryExplain'); + } + + checkAborted('setHeaders'); + if(!pgEntitiesAccessValidator.validate(affectedTables, authorizationLevel)) { + const syntaxError = new SyntaxError("system tables are forbidden"); + syntaxError.http_status = 403; + throw(syntaxError); + } + + var FormatClass = formats[format]; + formatter = new FormatClass(); + req.formatter = formatter; + + + // configure headers for given format + var use_inline = !requestedFormat && !requestedFilename; + res.header("Content-Disposition", getContentDisposition(formatter, filename, use_inline)); + res.header("Content-Type", formatter.getContentType()); + + // set cache headers + var cachePolicy = req.query.cache_policy; + if (cachePolicy === 'persist') { + res.header('Cache-Control', 'public,max-age=' + ONE_YEAR_IN_SECONDS); + } else { + var maxAge = (mayWrite) ? 0 : ONE_YEAR_IN_SECONDS; + res.header('Cache-Control', 'no-cache,max-age='+maxAge+',must-revalidate,public'); + } + + // Only set an X-Cache-Channel for responses we want Varnish to cache. + var skipNotUpdatedAtTables = true; + if (!!affectedTables && affectedTables.getTables(skipNotUpdatedAtTables).length > 0 && !mayWrite) { + res.header('X-Cache-Channel', affectedTables.getCacheChannel(skipNotUpdatedAtTables)); + res.header('Surrogate-Key', affectedTables.key(skipNotUpdatedAtTables).join(' ')); + } + + if(!!affectedTables) { + res.header('Last-Modified', + new Date(affectedTables.getLastUpdatedAt(Number(new Date()))).toUTCString()); + } + + return null; + }, + function generateFormat(err){ + if (err) { + throw err; + } + checkAborted('generateFormat'); + + // TODO: drop this, fix UI! + sql = new PSQL.QueryWrapper(sql).orderBy(orderBy, sortOrder).window(limit, offset).query(); + + var opts = { + username: username, + dbopts: dbopts, + sink: res, + gn: gn, + dp: dp, + skipfields: skipfields, + sql: sql, + filename: filename, + bufferedRows: global.settings.bufferedRows, + callback: params.callback, + abortChecker: checkAborted, + timeout: userLimits.timeout + }; + + if ( req.profiler ) { + opts.profiler = req.profiler; + opts.beforeSink = function() { + req.profiler.done('beforeSink'); + res.header('X-SQLAPI-Profiler', req.profiler.toJSONString()); + }; + } + + if (dbopts.host) { + res.header('X-Served-By-DB-Host', dbopts.host); + } + formatter.sendResponse(opts, this); + }, + function errorHandle(err){ + formatter = null; + + if (err) { + next(err); + } + + if ( req.profiler ) { + req.profiler.sendStats(); + } + if (self.statsd_client) { + if ( err ) { + self.statsd_client.increment('sqlapi.query.error'); + } else { + self.statsd_client.increment('sqlapi.query.success'); + } + } + } + ); + } catch (err) { + next(err); + + if (self.statsd_client) { + self.statsd_client.increment('sqlapi.query.error'); + } + } + +}; + +module.exports = QueryController; diff --git a/app/controllers/version_controller.js b/app/controllers/version_controller.js new file mode 100644 index 0000000..853ff89 --- /dev/null +++ b/app/controllers/version_controller.js @@ -0,0 +1,18 @@ +'use strict'; + +var version = { + cartodb_sql_api: require(__dirname + '/../../package.json').version +}; + +function VersionController() { +} + +VersionController.prototype.route = function (app) { + app.get(global.settings.base_url + '/version', this.handleVersion.bind(this)); +}; + +VersionController.prototype.handleVersion = function (req, res) { + res.send(version); +}; + +module.exports = VersionController; diff --git a/app/middlewares/authorization.js b/app/middlewares/authorization.js new file mode 100644 index 0000000..cbed545 --- /dev/null +++ b/app/middlewares/authorization.js @@ -0,0 +1,125 @@ +'use strict'; + +const AuthApi = require('../auth/auth_api'); +const basicAuth = require('basic-auth'); + +module.exports = function authorization (metadataBackend, forceToBeMaster = false) { + return function authorizationMiddleware (req, res, next) { + const { user } = res.locals; + const credentials = getCredentialsFromRequest(req); + + if (!userMatches(credentials, user)) { + if (req.profiler) { + req.profiler.done('authorization'); + } + + return next(new Error('permission denied')); + } + + res.locals.api_key = credentials.apiKeyToken; + + const params = Object.assign({ metadataBackend }, res.locals, req.query, req.body); + const authApi = new AuthApi(req, params); + + authApi.verifyCredentials(function (err, authorizationLevel) { + if (req.profiler) { + req.profiler.done('authorization'); + } + + if (err) { + return next(err); + } + + res.locals.authorizationLevel = authorizationLevel; + + if (forceToBeMaster && authorizationLevel !== 'master') { + return next(new Error('permission denied')); + } + + res.set('vary', 'Authorization'); //Honor Authorization header when caching. + + next(); + }); + }; +}; + +const credentialsGetters = [ + getCredentialsFromHeaderAuthorization, + getCredentialsFromRequestQueryString, + getCredentialsFromRequestBody, +]; + +function getCredentialsFromRequest (req) { + let credentials = null; + + for (var getter of credentialsGetters) { + credentials = getter(req); + + if (apiKeyTokenFound(credentials)) { + break; + } + } + + return credentials; +} + +function getCredentialsFromHeaderAuthorization(req) { + const { pass, name } = basicAuth(req) || {}; + + if (pass !== undefined && name !== undefined) { + return { + apiKeyToken: pass, + user: name + }; + } + + return false; +} + +function getCredentialsFromRequestQueryString(req) { + if (req.query.api_key) { + return { + apiKeyToken: req.query.api_key + }; + } + + if (req.query.map_key) { + return { + apiKeyToken: req.query.map_key + }; + } + + return false; +} + +function getCredentialsFromRequestBody(req) { + if (req.body && req.body.api_key) { + return { + apiKeyToken: req.body.api_key + }; + } + + if (req.body && req.body.map_key) { + return { + apiKeyToken: req.body.map_key + }; + } + + return false; +} + +function apiKeyTokenFound(credentials) { + if (typeof credentials === 'boolean') { + return credentials; + } + + if (credentials.apiKeyToken !== undefined) { + return true; + } + + return false; +} + +function userMatches (credentials, user) { + return !(credentials.user !== undefined && credentials.user !== user); +} diff --git a/app/middlewares/body-parser.js b/app/middlewares/body-parser.js new file mode 100644 index 0000000..f23c451 --- /dev/null +++ b/app/middlewares/body-parser.js @@ -0,0 +1,146 @@ +'use strict'; + +/*! + * Connect - bodyParser + * Copyright(c) 2010 Sencha Inc. + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var qs = require('qs'); +var multer = require('multer'); + +/** + * Extract the mime type from the given request's + * _Content-Type_ header. + * + * @param {IncomingMessage} req + * @return {String} + * @api private + */ + +function mime(req) { + var str = req.headers['content-type'] || ''; + return str.split(';')[0]; +} + +/** + * Parse request bodies. + * + * By default _application/json_, _application/x-www-form-urlencoded_, + * and _multipart/form-data_ are supported, however you may map `connect.bodyParser.parse[contentType]` + * to a function receiving `(req, options, callback)`. + * + * Examples: + * + * connect.createServer( + * connect.bodyParser() + * , function(req, res) { + * res.end('viewing user ' + req.body.user.name); + * } + * ); + * + * $ curl -d 'user[name]=tj' http://localhost/ + * $ curl -d '{"user":{"name":"tj"}}' -H "Content-Type: application/json" http://localhost/ + * + * Multipart req.files: + * + * As a security measure files are stored in a separate object, stored + * as `req.files`. This prevents attacks that may potentially alter + * filenames, and depending on the application gain access to restricted files. + * + * Multipart configuration: + * + * The `options` passed are provided to each parser function. + * The _multipart/form-data_ parser merges these with formidable's + * IncomingForm object, allowing you to tweak the upload directory, + * size limits, etc. For example you may wish to retain the file extension + * and change the upload directory: + * + * server.use(bodyParser({ uploadDir: '/www/mysite.com/uploads' })); + * + * View [node-formidable](https://github.com/felixge/node-formidable) for more information. + * + * If you wish to use formidable directly within your app, and do not + * desire this behaviour for multipart requests simply remove the + * parser: + * + * delete connect.bodyParser.parse['multipart/form-data']; + * + * Or + * + * delete express.bodyParser.parse['multipart/form-data']; + * + * @param {Object} options + * @return {Function} + * @api public + */ + +exports = module.exports = function bodyParser(options){ + options = options || {}; + return function bodyParser(req, res, next) { + if (req.body) { + return next(); + } + req.body = {}; + + if ('GET' === req.method || 'HEAD' === req.method) { + return next(); + } + var parser = exports.parse[mime(req)]; + if (parser) { + parser(req, options, next); + } else { + next(); + } + }; +}; + +/** + * Parsers. + */ + +exports.parse = {}; + +/** + * Parse application/x-www-form-urlencoded. + */ + +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(){ + try { + req.body = buf.length ? qs.parse(buf) : {}; + fn(); + } catch (err){ + fn(err); + } + }); +}; + +/** + * Parse application/json. + */ + +exports.parse['application/json'] = function(req, options, fn){ + var buf = ''; + req.setEncoding('utf8'); + req.on('data', function(chunk){ buf += chunk; }); + req.on('end', function(){ + try { + req.body = buf.length ? JSON.parse(buf) : {}; + fn(); + } catch (err){ + fn(err); + } + }); +}; + +var multipartMiddleware = multer({ limits: { fieldSize: Infinity } }); +exports.parse['multipart/form-data'] = multipartMiddleware.none(); diff --git a/app/middlewares/connection-params.js b/app/middlewares/connection-params.js new file mode 100644 index 0000000..f050055 --- /dev/null +++ b/app/middlewares/connection-params.js @@ -0,0 +1,23 @@ +'use strict'; + +module.exports = function connectionParams (userDatabaseService) { + return function connectionParamsMiddleware (req, res, next) { + const { user, api_key: apikeyToken, authorizationLevel } = res.locals; + + userDatabaseService.getConnectionParams(user, apikeyToken, authorizationLevel, + function (err, userDbParams, authDbParams) { + if (req.profiler) { + req.profiler.done('getConnectionParams'); + } + + if (err) { + return next(err); + } + + res.locals.userDbParams = userDbParams; + res.locals.authDbParams = authDbParams; + + next(); + }); + }; +}; diff --git a/app/middlewares/cors.js b/app/middlewares/cors.js new file mode 100644 index 0000000..286ced9 --- /dev/null +++ b/app/middlewares/cors.js @@ -0,0 +1,16 @@ +'use strict'; + +module.exports = function cors(extraHeaders) { + return function(req, res, next) { + var baseHeaders = 'X-Requested-With, X-Prototype-Version, X-CSRF-Token, Authorization'; + + if(extraHeaders) { + baseHeaders += ', ' + extraHeaders; + } + + res.header('Access-Control-Allow-Origin', '*'); + res.header('Access-Control-Allow-Headers', baseHeaders); + + next(); + }; +}; diff --git a/app/middlewares/db-quota.js b/app/middlewares/db-quota.js new file mode 100644 index 0000000..42fb896 --- /dev/null +++ b/app/middlewares/db-quota.js @@ -0,0 +1,26 @@ +'use strict'; + +const PSQL = require('cartodb-psql'); + +const remainingQuotaQuery = 'SELECT _CDB_UserQuotaInBytes() - CDB_UserDataSize(current_schema()) AS remaining_quota'; + +module.exports = function dbQuota () { + return function dbQuotaMiddleware (req, res, next) { + const { userDbParams } = res.locals; + const pg = new PSQL(userDbParams); + pg.connect((err, client, done) => { + if (err) { + return next(err); + } + client.query(remainingQuotaQuery, (err, result) => { + if(err) { + return next(err); + } + const remainingQuota = result.rows[0].remaining_quota; + res.locals.dbRemainingQuota = remainingQuota; + done(); + next(); + }); + }); + }; +}; diff --git a/app/middlewares/error.js b/app/middlewares/error.js new file mode 100644 index 0000000..b2a1eb9 --- /dev/null +++ b/app/middlewares/error.js @@ -0,0 +1,91 @@ +'use strict'; + +const errorHandlerFactory = require('../services/error_handler_factory'); +const MAX_ERROR_STRING_LENGTH = 1024; + +module.exports = function error() { + return function errorMiddleware(err, req, res, next) { + const errorHandler = errorHandlerFactory(err); + let errorResponse = errorHandler.getResponse(); + + if (global.settings.environment === 'development') { + errorResponse.stack = err.stack; + } + + if (global.settings.environment !== 'test') { + // TODO: email this Exception report + console.error("EXCEPTION REPORT: " + err.stack); + } + + // Force inline content disposition + res.header("Content-Disposition", 'inline'); + + if (req && req.profiler) { + req.profiler.done('finish'); + res.header('X-SQLAPI-Profiler', req.profiler.toJSONString()); + } + + setErrorHeader(errorHandler, res); + + res.header('Content-Type', 'application/json; charset=utf-8'); + res.status(getStatusError(errorHandler, req)); + if (req.query && req.query.callback) { + res.jsonp(errorResponse); + } else { + res.json(errorResponse); + } + + if (req && req.profiler) { + res.req.profiler.sendStats(); + } + + next(); + }; +}; + +function getStatusError(errorHandler, req) { + let statusError = errorHandler.http_status; + + // JSONP has to return 200 status error + if (req && req.query && req.query.callback) { + statusError = 200; + } + + return statusError; +} + +function setErrorHeader(errorHandler, res) { + const errorsLog = { + context: errorHandler.context, + detail: errorHandler.detail, + hint: errorHandler.hint, + statusCode: errorHandler.http_status, + message: errorHandler.message + }; + + res.set('X-SQLAPI-Errors', stringifyForLogs(errorsLog)); +} + +/** + * Remove problematic nested characters + * from object for logs RegEx + * + * @param {Object} object + */ +function stringifyForLogs(object) { + Object.keys(object).map(key => { + if (typeof object[key] === 'string') { + object[key] = object[key] + .substring(0, MAX_ERROR_STRING_LENGTH) + .replace(/[^a-zA-Z0-9]/g, ' '); + } else if (typeof object[key] === 'object') { + stringifyForLogs(object[key]); + } else if (object[key] instanceof Array) { + for (let element of object[key]) { + stringifyForLogs(element); + } + } + }); + + return JSON.stringify(object); +} diff --git a/app/middlewares/profiler.js b/app/middlewares/profiler.js new file mode 100644 index 0000000..fac63a9 --- /dev/null +++ b/app/middlewares/profiler.js @@ -0,0 +1,24 @@ +'use strict'; + +module.exports.initializeProfilerMiddleware = function initializeProfiler (label) { + return function initializeProfilerMiddleware (req, res, next) { + if (req.profiler) { + req.profiler.start(`sqlapi.${label}`); + } + + next(); + }; +}; + +module.exports.finishProfilerMiddleware = function finishProfiler () { + return function finishProfilerMiddleware (req, res, next) { + if (req.profiler) { + req.profiler.end(); + req.profiler.sendStats(); + + res.header('X-SQLAPI-Profiler', req.profiler.toJSONString()); + } + + next(); + }; +}; diff --git a/app/middlewares/rate-limit.js b/app/middlewares/rate-limit.js new file mode 100644 index 0000000..2a1cdae --- /dev/null +++ b/app/middlewares/rate-limit.js @@ -0,0 +1,61 @@ +'use strict'; + +const RATE_LIMIT_ENDPOINTS_GROUPS = { + QUERY: 'query', + JOB_CREATE: 'job_create', + JOB_GET: 'job_get', + JOB_DELETE: 'job_delete', + COPY_FROM: 'copy_from', + COPY_TO: 'copy_to' +}; + + +function rateLimit(userLimits, endpointGroup = null) { + if (!isRateLimitEnabled(endpointGroup)) { + return function rateLimitDisabledMiddleware(req, res, next) { next(); }; + } + + return function rateLimitMiddleware(req, res, next) { + userLimits.getRateLimit(res.locals.user, endpointGroup, function(err, userRateLimit) { + if (err) { + return next(err); + } + + if (!userRateLimit) { + return next(); + } + + const [isBlocked, limit, remaining, retry, reset] = userRateLimit; + + res.set({ + 'Carto-Rate-Limit-Limit': limit, + 'Carto-Rate-Limit-Remaining': remaining, + 'Carto-Rate-Limit-Reset': reset + }); + + if (isBlocked) { + // retry is floor rounded in seconds by redis-cell + res.set('Retry-After', retry + 1); + + const rateLimitError = new Error( + 'You are over platform\'s limits. Please contact us to know more details' + ); + rateLimitError.http_status = 429; + rateLimitError.context = 'limit'; + rateLimitError.detail = 'rate-limit'; + return next(rateLimitError); + } + + return next(); + }); + }; +} + +function isRateLimitEnabled(endpointGroup) { + return global.settings.ratelimits.rateLimitsEnabled && + endpointGroup && + global.settings.ratelimits.endpoints[endpointGroup]; +} + +module.exports = rateLimit; +module.exports.RATE_LIMIT_ENDPOINTS_GROUPS = RATE_LIMIT_ENDPOINTS_GROUPS; diff --git a/app/middlewares/timeout-limits.js b/app/middlewares/timeout-limits.js new file mode 100644 index 0000000..cd0275b --- /dev/null +++ b/app/middlewares/timeout-limits.js @@ -0,0 +1,25 @@ +'use strict'; + +module.exports = function timeoutLimits (metadataBackend) { + return function timeoutLimitsMiddleware (req, res, next) { + const { user, authorizationLevel } = res.locals; + + metadataBackend.getUserTimeoutRenderLimits(user, function (err, timeoutRenderLimit) { + if (req.profiler) { + req.profiler.done('getUserTimeoutLimits'); + } + + if (err) { + return next(err); + } + + const userLimits = { + timeout: (authorizationLevel === 'master') ? timeoutRenderLimit.render : timeoutRenderLimit.renderPublic + }; + + res.locals.userLimits = userLimits; + + next(); + }); + }; +}; diff --git a/app/middlewares/user.js b/app/middlewares/user.js new file mode 100644 index 0000000..87e71d0 --- /dev/null +++ b/app/middlewares/user.js @@ -0,0 +1,38 @@ +'use strict'; + +const CdbRequest = require('../models/cartodb_request'); + +module.exports = function user(metadataBackend) { + const cdbRequest = new CdbRequest(); + + return function userMiddleware (req, res, next) { + res.locals.user = getUserNameFromRequest(req, cdbRequest); + + checkUserExists(metadataBackend, res.locals.user, function(err, userExists) { + if (err || !userExists) { + const error = new Error('Unauthorized'); + error.type = 'auth'; + error.subtype = 'user-not-found'; + error.http_status = 404; + error.message = errorUserNotFoundMessageTemplate(res.locals.user); + next(error); + } + + return next(); + }); + }; +}; + +function getUserNameFromRequest(req, cdbRequest) { + return cdbRequest.userByReq(req); +} + +function checkUserExists(metadataBackend, userName, callback) { + metadataBackend.getUserId(userName, function(err) { + callback(err, !err); + }); +} + +function errorUserNotFoundMessageTemplate(user) { + return `Sorry, we can't find CARTO user '${user}'. Please check that you have entered the correct domain.`; +} diff --git a/app/models/bin_encoder.js b/app/models/bin_encoder.js new file mode 100644 index 0000000..c764a44 --- /dev/null +++ b/app/models/bin_encoder.js @@ -0,0 +1,166 @@ +'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]); + } + } else { + this[w](data[0]); + for(i = 1; i < data.length; ++i) { + this[w](data[i] - data[i - 1]); + } + } +} + +// +// constants +// +ArrayBufferSer.INT8 = 1; +ArrayBufferSer.UINT8 = 2; +ArrayBufferSer.UINT8_CLAMP = 3; +ArrayBufferSer.INT16 = 4; +ArrayBufferSer.UINT16 = 5; +ArrayBufferSer.INT32 = 6; +ArrayBufferSer.UINT32 = 7; +ArrayBufferSer.FLOAT32 = 8; +//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 +}; + +ArrayBufferSer.prototype = { + + // 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; + } + 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/app/models/cartodb_request.js b/app/models/cartodb_request.js new file mode 100644 index 0000000..8ba9b89 --- /dev/null +++ b/app/models/cartodb_request.js @@ -0,0 +1,41 @@ +'use strict'; + +/** + * this module provides cartodb-specific interpretation + * of request headers + */ + +function CartodbRequest() { +} + +module.exports = CartodbRequest; + +/** + * If the request contains the user use it, if not guess from the host + */ +CartodbRequest.prototype.userByReq = function(req) { + if (req.params.user) { + return req.params.user; + } + return userByHostName(req.headers.host); +}; + +var re_userFromHost = new RegExp( + global.settings.user_from_host || '^([^\\.]+)\\.' // would extract "strk" from "strk.cartodb.com" +); + +function userByHostName(host) { + var mat = host.match(re_userFromHost); + if (!mat) { + console.error("ERROR: user pattern '" + re_userFromHost + "' does not match hostname '" + host + "'"); + return; + } + + if (mat.length !== 2) { + console.error( + "ERROR: pattern '" + re_userFromHost + "' gave unexpected matches against '" + host + "': " + mat + ); + return; + } + return mat[1]; +} diff --git a/app/models/formats/README b/app/models/formats/README new file mode 100644 index 0000000..02652e6 --- /dev/null +++ b/app/models/formats/README @@ -0,0 +1,18 @@ +Format classes are required to expose a constructor with no arguments, +a getFileExtension() and a sendResponse(opts, callback) method. + +The ``opts`` parameter contains: + + sink Output stream to send the reponse to + sql SQL query requested by the user + skipfields Comma separate list of fields to skip from output + really only needed with "SELECT *" queries + gn Name of the geometry column (for formats requiring one) + dp Number of decimal points of precision for geometries (if used) + database Name of the database to connect to + user_id Identifier of the user + filename Name to use for attachment disposition + +The ``callback`` parameter is a function that is invoked when the +format object finished with sending the result to the sink. +If an error occurs the callback is invoked with an Error argument. diff --git a/app/models/formats/index.js b/app/models/formats/index.js new file mode 100644 index 0000000..68e65c6 --- /dev/null +++ b/app/models/formats/index.js @@ -0,0 +1,22 @@ +'use strict'; + +var fs = require("fs"); +var formats = {}; + +function formatFilesWithPath(dir) { + var formatDir = __dirname + '/' + dir; + return fs.readdirSync(formatDir).map(function(formatFile) { + return formatDir + '/' + formatFile; + }); +} + +var formatFilesPaths = [] + .concat(formatFilesWithPath('ogr')) + .concat(formatFilesWithPath('pg')); + +formatFilesPaths.forEach(function(file) { + var format = require(file); + formats[format.prototype.id] = format; +}); + +module.exports = formats; diff --git a/app/models/formats/ogr.js b/app/models/formats/ogr.js new file mode 100644 index 0000000..c2c4499 --- /dev/null +++ b/app/models/formats/ogr.js @@ -0,0 +1,346 @@ +'use strict'; + +var crypto = require('crypto'); +var step = require('step'); +var fs = require('fs'); +var _ = require('underscore'); +var PSQL = require('cartodb-psql'); +var spawn = require('child_process').spawn; + +// Keeps track of what's waiting baking for export +var bakingExports = {}; + +function OgrFormat(id) { + this.id = id; +} + +OgrFormat.prototype = { + + id: "ogr", + + is_file: true, + + getQuery: function(/*sql, options*/) { + return null; // dont execute the query + }, + + transform: function(/*result, options, callback*/) { + throw "should not be called for file formats"; + }, + + getContentType: function(){ return this._contentType; }, + + 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(':'); + }, + + 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) { + + //var gcol = options.gn; + var sql = options.sql; + var skipfields = options.skipfields; + var out_layername = options.filename; + + 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 timeout = options.timeout; + + var that = this; + + 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; + }; + + 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; + } + + 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; + } + + 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; + } + + var next = this; + + 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); + }); + }; + + 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; + + 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(); + } + }); +} + +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(); +}; + +//------ } + +module.exports = OgrFormat; diff --git a/app/models/formats/ogr/csv.js b/app/models/formats/ogr/csv.js new file mode 100644 index 0000000..655ff28 --- /dev/null +++ b/app/models/formats/ogr/csv.js @@ -0,0 +1,16 @@ +'use strict'; + +var ogr = require('./../ogr'); + +function CsvFormat() {} + +CsvFormat.prototype = new ogr('csv'); + +CsvFormat.prototype._contentType = "text/csv; charset=utf-8; header=present"; +CsvFormat.prototype._fileExtension = "csv"; + +CsvFormat.prototype.generate = function(options, callback) { + this.toOGR_SingleFile(options, 'CSV', callback); +}; + +module.exports = CsvFormat; diff --git a/app/models/formats/ogr/geopackage.js b/app/models/formats/ogr/geopackage.js new file mode 100644 index 0000000..0702b6b --- /dev/null +++ b/app/models/formats/ogr/geopackage.js @@ -0,0 +1,25 @@ +'use strict'; + +var ogr = require('./../ogr'); + +function GeoPackageFormat() {} + +GeoPackageFormat.prototype = new ogr('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 +// http://trac.osgeo.org/gdal/ticket/5287 +// http://github.com/CartoDB/CartoDB-SQL-API/issues/110 +// http://github.com/CartoDB/CartoDB-SQL-API/issues/116 +// Bug was fixed in GDAL 1.10.2 +GeoPackageFormat.prototype._needSRS = true; + +GeoPackageFormat.prototype.generate = function(options, callback) { + options.cmd_params = ['-lco', 'FID=cartodb_id']; + this.toOGR_SingleFile(options, 'GPKG', callback); +}; + +module.exports = GeoPackageFormat; diff --git a/app/models/formats/ogr/kml.js b/app/models/formats/ogr/kml.js new file mode 100644 index 0000000..d7a2929 --- /dev/null +++ b/app/models/formats/ogr/kml.js @@ -0,0 +1,24 @@ +'use strict'; + +var ogr = require('./../ogr'); + +function KmlFormat() {} + +KmlFormat.prototype = new ogr('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 +// http://trac.osgeo.org/gdal/ticket/5287 +// http://github.com/CartoDB/CartoDB-SQL-API/issues/110 +// http://github.com/CartoDB/CartoDB-SQL-API/issues/116 +// Bug was fixed in GDAL 1.10.2 +KmlFormat.prototype._needSRS = true; + +KmlFormat.prototype.generate = function(options, callback) { + this.toOGR_SingleFile(options, 'KML', callback); +}; + +module.exports = KmlFormat; diff --git a/app/models/formats/ogr/shp.js b/app/models/formats/ogr/shp.js new file mode 100644 index 0000000..64e5b62 --- /dev/null +++ b/app/models/formats/ogr/shp.js @@ -0,0 +1,135 @@ +'use strict'; + +var step = require('step'); +var fs = require('fs'); +var spawn = require('child_process').spawn; + +var ogr = require('./../ogr'); + +function ShpFormat() { +} + +ShpFormat.prototype = new ogr('shp'); + +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 +// http://trac.osgeo.org/gdal/ticket/5287 +// http://github.com/CartoDB/CartoDB-SQL-API/issues/110 +// 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.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 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 + + 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; + } + + var next = this; + + 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); + }); + }); + } + }); + }, + function finalStep(err, zipfile) { + callback(err, zipfile); + } + ); +}; + + +module.exports = ShpFormat; diff --git a/app/models/formats/ogr/spatialite.js b/app/models/formats/ogr/spatialite.js new file mode 100644 index 0000000..8912082 --- /dev/null +++ b/app/models/formats/ogr/spatialite.js @@ -0,0 +1,25 @@ +'use strict'; + +var ogr = require('./../ogr'); + +function SpatiaLiteFormat() {} + +SpatiaLiteFormat.prototype = new ogr('spatialite'); + +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 +// http://trac.osgeo.org/gdal/ticket/5287 +// http://github.com/CartoDB/CartoDB-SQL-API/issues/110 +// http://github.com/CartoDB/CartoDB-SQL-API/issues/116 +// Bug was fixed in GDAL 1.10.2 +SpatiaLiteFormat.prototype._needSRS = true; + +SpatiaLiteFormat.prototype.generate = function(options, callback) { + this.toOGR_SingleFile(options, 'SQLite', callback); + options.cmd_params = ['SPATIALITE=yes']; +}; + +module.exports = SpatiaLiteFormat; diff --git a/app/models/formats/pg.js b/app/models/formats/pg.js new file mode 100644 index 0000000..22198e8 --- /dev/null +++ b/app/models/formats/pg.js @@ -0,0 +1,164 @@ +'use strict'; + +var step = require('step'); +var PSQL = require('cartodb-psql'); + +function PostgresFormat(id) { + this.id = id; +} + +PostgresFormat.prototype = { + + getQuery: function(sql/*, options*/) { + return sql; + }, + + getContentType: function(){ + return this._contentType; + }, + + getFileExtension: function() { + return this.id; + } + +}; + +PostgresFormat.prototype.handleQueryRow = function(row, result) { + result.addRow(row); +}; + +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); + } + 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); + } +}; + +module.exports = BinaryFormat; diff --git a/app/models/formats/pg/geojson.js b/app/models/formats/pg/geojson.js new file mode 100644 index 0000000..b5241f7 --- /dev/null +++ b/app/models/formats/pg/geojson.js @@ -0,0 +1,120 @@ +'use strict'; + +var _ = require('underscore'); + +var pg = require('./../pg'); +const errorHandlerFactory = require('../../../services/error_handler_factory'); + +function GeoJsonFormat() { + this.buffer = ''; +} + +GeoJsonFormat.prototype = new pg('geojson'); + +GeoJsonFormat.prototype._contentType = "application/json; charset=utf-8"; + +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() { + this.total_rows = 0; + if (this.opts.beforeSink) { + this.opts.beforeSink(); + } + if (this.opts.callback) { + this.buffer += this.opts.callback + '('; + } + this.buffer += '{"type": "FeatureCollection", "features": ['; + this._streamingStarted = true; +}; + +GeoJsonFormat.prototype.handleQueryRow = function(row) { + + if ( ! this._streamingStarted ) { + this.startStreaming(); + } + + var geojson = [ + '{', + '"type":"Feature",', + '"geometry":' + row[this.opts.gn] + ',', + '"properties":' + ]; + delete row[this.opts.gn]; + delete row.the_geom_webmercator; + geojson.push(JSON.stringify(row)); + geojson.push('}'); + + this.buffer += (this.total_rows++ ? ',' : '') + geojson.join(''); + + if (this.total_rows % (this.opts.bufferedRows || 1000)) { + this.opts.sink.write(this.buffer); + this.buffer = ''; + } +}; + +GeoJsonFormat.prototype.handleQueryEnd = function(/*result*/) { + if (this.error && !this._streamingStarted) { + this.callback(this.error); + return; + } + + if ( this.opts.profiler ) { + this.opts.profiler.done('gotRows'); + } + + if ( ! this._streamingStarted ) { + this.startStreaming(); + } + + this.buffer += ']'; // end of features + + if (this.error) { + this.buffer += ',"error":' + JSON.stringify(errorHandlerFactory(this.error).getResponse().error); + } + + this.buffer += '}'; // end of root object + + if (this.opts.callback) { + this.buffer += ')'; + } + + this.opts.sink.write(this.buffer); + this.opts.sink.end(); + this.buffer = ''; + + this.callback(); +}; + +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); + }); + + // return payload + callback(null, out); + } catch (err) { + callback(err,null); + } +} + +module.exports = GeoJsonFormat; +module.exports.toGeoJSON = _toGeoJSON; diff --git a/app/models/formats/pg/json.js b/app/models/formats/pg/json.js new file mode 100644 index 0000000..d4dff9e --- /dev/null +++ b/app/models/formats/pg/json.js @@ -0,0 +1,174 @@ +'use strict'; + +var _ = require('underscore'); + +var pg = require('./../pg'); +const errorHandlerFactory = require('../../../services/error_handler_factory'); + +function JsonFormat() { + this.buffer = ''; + this.lastKnownResult = {}; +} + +JsonFormat.prototype = new pg('json'); + +JsonFormat.prototype._contentType = "application/json; charset=utf-8"; + +// jshint maxcomplexity:9 +JsonFormat.prototype.formatResultFields = function(flds) { + flds = flds || []; + var nfields = {}; + for (var i=0; i 0 ) { + var notices = {}, + severities = []; + _.each(result.notices, function(notice) { + var severity = notice.severity.toLowerCase() + 's'; + if (!notices[severity]) { + severities.push(severity); + notices[severity] = []; + } + notices[severity].push(notice.message); + }); + _.each(severities, function(severity) { + out.push(','); + out.push(JSON.stringify(severity)); + out.push(':'); + out.push(JSON.stringify(notices[severity])); + }); + } + + out.push('}'); + + + this.buffer += out.join(''); + + if (this.opts.callback) { + this.buffer += ')'; + } + + this.opts.sink.write(this.buffer); + this.opts.sink.end(); + this.buffer = ''; + + this.callback(); +}; + +module.exports = JsonFormat; diff --git a/app/models/formats/pg/svg.js b/app/models/formats/pg/svg.js new file mode 100644 index 0000000..442c041 --- /dev/null +++ b/app/models/formats/pg/svg.js @@ -0,0 +1,166 @@ +'use strict'; + +var pg = require('./../pg'); + +var svg_width = 1024.0; +var svg_height = 768.0; +var svg_ratio = svg_width/svg_height; + +var radius = 5; // in pixels (based on svg_width and svg_height) + +var stroke_width = 1; // in pixels (based on svg_width and svg_height) +var stroke_color = 'black'; +// fill settings affect polygons and points (circles) +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() { + this.totalRows = 0; + + this.bbox = null; // will be computed during the results scan + this.buffer = ''; + + this._streamingStarted = false; +} + +SvgFormat.prototype = new pg('svg'); +SvgFormat.prototype._contentType = "image/svg+xml; charset=utf-8"; + +SvgFormat.prototype.getQuery = function(sql, options) { + var gn = options.gn; + var dp = options.dp; + return 'WITH source AS ( ' + sql + '), extent AS ( ' + + ' SELECT ST_Extent(' + gn + ') AS e FROM source ' + + '), extent_info AS ( SELECT e, ' + + 'st_xmin(e) as ex0, st_ymax(e) as ey0, ' + + 'st_xmax(e)-st_xmin(e) as ew, ' + + 'st_ymax(e)-st_ymin(e) as eh FROM extent )' + + ', trans AS ( SELECT CASE WHEN ' + + 'eh = 0 THEN ' + svg_width + + '/ COALESCE(NULLIF(ew,0),' + svg_width +') WHEN ' + + svg_ratio + ' <= (ew / eh) THEN (' + + svg_width + '/ew ) ELSE (' + + svg_height + '/eh ) END as s ' + + ', ex0 as x0, ey0 as y0 FROM extent_info ) ' + + 'SELECT st_TransScale(e, -x0, -y0, s, s)::box2d as ' + + gn + '_box, ST_Dimension(' + gn + ') as ' + gn + + '_dimension, ST_AsSVG(ST_TransScale(' + gn + ', ' + + '-x0, -y0, s, s), 0, ' + dp + ') as ' + gn + + //+ ', ex0, ey0, ew, eh, s ' // DEBUG ONLY + + ' FROM trans, extent_info, source' + + ' ORDER BY the_geom_dimension ASC'; +}; + +SvgFormat.prototype.startStreaming = function() { + if (this.opts.beforeSink) { + this.opts.beforeSink(); + } + + var header = [ + '', + '' + ]; + + var rootTag = '\n'; + } else if ( gdims == '1' ) { + // Avoid filling closed linestrings + this.buffer += '\n'; + } else if ( gdims == '2' ) { + this.buffer += '\n'; + } + // jshint ignore:end + + 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 + // ST_AsSVG call (in queryResult) + // + var bbox = row[this.opts.gn + '_box']; + bbox = bbox.match(/BOX\(([^ ]*) ([^ ,]*),([^ ]*) ([^)]*)\)/); + this.bbox = { + xmin: parseFloat(bbox[1]), + ymin: parseFloat(bbox[2]), + xmax: parseFloat(bbox[3]), + ymax: parseFloat(bbox[4]) + }; + } + + if (!this._streamingStarted && this.bbox) { + this.startStreaming(); + } + + if (this._streamingStarted && (this.totalRows % (this.opts.bufferedRows || 1000))) { + this.opts.sink.write(this.buffer); + this.buffer = ''; + } +}; + +SvgFormat.prototype.handleQueryEnd = function() { + if ( this.error && !this._streamingStarted) { + this.callback(this.error); + return; + } + + if ( this.opts.profiler ) { + this.opts.profiler.done('gotRows'); + } + + if (!this._streamingStarted) { + this.startStreaming(); + } + + // rootTag close + this.buffer += '\n'; + + this.opts.sink.write(this.buffer); + this.opts.sink.end(); + + this.callback(); +}; + +module.exports = SvgFormat; diff --git a/app/models/formats/pg/topojson.js b/app/models/formats/pg/topojson.js new file mode 100644 index 0000000..08e4f94 --- /dev/null +++ b/app/models/formats/pg/topojson.js @@ -0,0 +1,138 @@ +'use strict'; + +var pg = require('./../pg'); +var _ = require('underscore'); +var geojson = require('./geojson'); +var TopoJSON = require('topojson'); + +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.handleQueryRow = function(row) { + var _geojson = { + type: "Feature" + }; + _geojson.geometry = JSON.parse(row[this.opts.gn]); + delete row[this.opts.gn]; + delete row.the_geom_webmercator; + _geojson.properties = row; + this.features.push(_geojson); +}; + +TopoJsonFormat.prototype.handleQueryEnd = function() { + if (this.error) { + this.callback(this.error); + return; + } + + if ( this.opts.profiler ) { + this.opts.profiler.done('gotRows'); + } + + var topology = TopoJSON.topology(this.features, { + "quantization": 1e4, + "force-clockwise": true, + "property-filter": function(d) { + return d; + } + }); + + this.features = []; + + var stream = this.opts.sink; + var jsonpCallback = this.opts.callback; + var bufferedRows = this.opts.bufferedRows; + var buffer = ''; + + var immediately = global.setImmediate || process.nextTick; + + function streamObjectSubtree(obj, key, done) { + buffer += '"' + key + '":'; + + var isObject = _.isObject(obj[key]), + isArray = _.isArray(obj[key]), + isIterable = isArray || isObject; + + if (isIterable) { + buffer += isArray ? '[' : '{'; + var subtreeKeys = Object.keys(obj[key]); + var pos = 0; + function streamNext() { + immediately(function() { + var subtreeKey = subtreeKeys.shift(); + if (!isArray) { + buffer += '"' + subtreeKey + '":'; + } + buffer += JSON.stringify(obj[key][subtreeKey]); + + if (pos++ % (bufferedRows || 1000)) { + stream.write(buffer); + buffer = ''; + } + + if (subtreeKeys.length > 0) { + delete obj[key][subtreeKey]; + buffer += ','; + streamNext(); + } else { + buffer += isArray ? ']' : '}'; + stream.write(buffer); + buffer = ''; + done(); + } + }); + } + streamNext(); + } else { + buffer += JSON.stringify(obj[key]); + done(); + } + } + + if (jsonpCallback) { + buffer += jsonpCallback + '('; + } + buffer += '{'; + var keys = Object.keys(topology); + function sendResponse() { + immediately(function () { + var key = keys.shift(); + function done() { + if (keys.length > 0) { + delete topology[key]; + buffer += ','; + sendResponse(); + } else { + buffer += '}'; + if (jsonpCallback) { + buffer += ')'; + } + stream.write(buffer); + stream.end(); + topology = null; + } + } + streamObjectSubtree(topology, key, done); + }); + } + sendResponse(); + + this.callback(); +}; + +TopoJsonFormat.prototype.cancel = function() { + if (this.queryCanceller) { + this.queryCanceller.call(); + } +}; + + + +module.exports = TopoJsonFormat; diff --git a/app/monitoring/health_check.js b/app/monitoring/health_check.js new file mode 100644 index 0000000..5aa068d --- /dev/null +++ b/app/monitoring/health_check.js @@ -0,0 +1,34 @@ +'use strict'; + +var step = require('step'), + fs = require('fs'); + +function HealthCheck(disableFile) { + this.disableFile = disableFile; +} + +module.exports = HealthCheck; + +HealthCheck.prototype.check = function(callback) { + var self = this; + + step( + 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 handleResult(err) { + callback(err); + } + ); +}; diff --git a/app/postgresql/error_codes.js b/app/postgresql/error_codes.js new file mode 100644 index 0000000..cd673d8 --- /dev/null +++ b/app/postgresql/error_codes.js @@ -0,0 +1,285 @@ +'use strict'; + +var _ = require('underscore'); + +// reference http://www.postgresql.org/docs/9.3/static/errcodes-appendix.html +// 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", +// 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", +// 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", + "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", +// Class XX — Internal Error + "XX000": "internal_error", + "XX001": "data_corrupted", + "XX002": "index_corrupted" +}; + +module.exports.codeToCondition = codeToCondition; +module.exports.conditionToCode = _.invert(codeToCondition); diff --git a/app/server.js b/app/server.js new file mode 100644 index 0000000..e76c930 --- /dev/null +++ b/app/server.js @@ -0,0 +1,226 @@ +'use strict'; + +// CartoDB SQL API +// +// all requests expect the following URL args: +// - `sql` {String} SQL to execute +// +// for private (read/write) queries: +// - OAuth. Must have proper OAuth 1.1 headers. For OAuth 1.1 spec see Google +// +// eg. /api/v1/?sql=SELECT 1 as one (with a load of OAuth headers or URL arguments) +// +// for public (read only) queries: +// - sql only, provided the subdomain exists in CartoDB and the table's sharing options are public +// +// eg. vizzuality.cartodb.com/api/v1/?sql=SELECT * from my_table +// + +var express = require('express'); +var Profiler = require('./stats/profiler-proxy'); +var _ = require('underscore'); +var fs = require('fs'); +var mkdirp = require('mkdirp'); +var TableCacheFactory = require('./utils/table_cache_factory'); + +var RedisPool = require('redis-mpool'); +var cartodbRedis = require('cartodb-redis'); +var UserDatabaseService = require('./services/user_database_service'); +var UserLimitsService = require('./services/user_limits'); +var JobPublisher = require('../batch/pubsub/job-publisher'); +var JobQueue = require('../batch/job_queue'); +var JobBackend = require('../batch/job_backend'); +var JobCanceller = require('../batch/job_canceller'); +var JobService = require('../batch/job_service'); +const Logger = require('./services/logger'); + +var cors = require('./middlewares/cors'); + +var GenericController = require('./controllers/generic_controller'); +var QueryController = require('./controllers/query_controller'); +var CopyController = require('./controllers/copy_controller'); +var JobController = require('./controllers/job_controller'); +var CacheStatusController = require('./controllers/cache_status_controller'); +var HealthCheckController = require('./controllers/health_check_controller'); +var VersionController = require('./controllers/version_controller'); + +var batchFactory = require('../batch'); + +process.env.PGAPPNAME = process.env.PGAPPNAME || 'cartodb_sqlapi'; + +// override Date.toJSON +require('./utils/date_to_json'); + +// jshint maxcomplexity:9 +function App(statsClient) { + + var app = express(); + + var redisPool = new RedisPool({ + name: 'sql-api', + host: global.settings.redis_host, + port: global.settings.redis_port, + max: global.settings.redisPool, + idleTimeoutMillis: global.settings.redisIdleTimeoutMillis, + reapIntervalMillis: global.settings.redisReapIntervalMillis + }); + var metadataBackend = cartodbRedis({ pool: redisPool }); + + // Set default configuration + global.settings.db_pubuser = global.settings.db_pubuser || "publicuser"; + global.settings.bufferedRows = global.settings.bufferedRows || 1000; + global.settings.ratelimits = Object.assign( + { + rateLimitsEnabled: false, + endpoints: { + query: false, + job_create: false, + job_get: false, + job_delete: false + } + }, + global.settings.ratelimits + ); + + global.settings.tmpDir = global.settings.tmpDir || '/tmp'; + if (!fs.existsSync(global.settings.tmpDir)) { + mkdirp.sync(global.settings.tmpDir); + } + + var tableCache = new TableCacheFactory().build(global.settings); + + // Size based on https://github.com/CartoDB/cartodb.js/blob/3.15.2/src/geo/layer_definition.js#L72 + var SQL_QUERY_BODY_LOG_MAX_LENGTH = 2000; + app.getSqlQueryFromRequestBody = function(req) { + var sqlQuery = req.body && req.body.q; + if (!sqlQuery) { + return ''; + } + + if (sqlQuery.length > SQL_QUERY_BODY_LOG_MAX_LENGTH) { + sqlQuery = sqlQuery.substring(0, SQL_QUERY_BODY_LOG_MAX_LENGTH) + ' [...]'; + } + return JSON.stringify({q: sqlQuery}); + }; + + if ( global.log4js ) { + var loggerOpts = { + buffer: true, + // log4js provides a tokens solution as expess but in does not provide the request/response in the callback. + // Thus it is not possible to extract relevant information from them. + // This is a workaround to be able to access request/response. + format: function(req, res, format) { + var logFormat = global.settings.log_format || + ':remote-addr :method :req[Host]:url :status :response-time ms -> :res[Content-Type]'; + + logFormat = logFormat.replace(/:sql/, app.getSqlQueryFromRequestBody(req)); + return format(logFormat); + } + }; + app.use(global.log4js.connectLogger(global.log4js.getLogger(), _.defaults(loggerOpts, {level:'info'}))); + } + + app.use(cors()); + + // Use step-profiler + app.use(function bootstrap$prepareRequestResponse(req, res, next) { + res.locals = res.locals || {}; + + if (global.settings.api_hostname) { + res.header('X-Served-By-Host', global.settings.api_hostname); + } + + var profile = global.settings.useProfiler; + req.profiler = new Profiler({ + profile: profile, + statsd_client: statsClient + }); + next(); + }); + + // Set connection timeout + if ( global.settings.hasOwnProperty('node_socket_timeout') ) { + var timeout = parseInt(global.settings.node_socket_timeout); + app.use(function(req, res, next) { + req.connection.setTimeout(timeout); + next(); + }); + } + + app.enable('jsonp callback'); + app.set("trust proxy", true); + app.disable('x-powered-by'); + app.disable('etag'); + + // basic routing + + var userDatabaseService = new UserDatabaseService(metadataBackend); + + const userLimitsServiceOptions = { + limits: { + rateLimitsEnabled: global.settings.ratelimits.rateLimitsEnabled + } + }; + const userLimitsService = new UserLimitsService(metadataBackend, userLimitsServiceOptions); + + const dataIngestionLogger = new Logger(global.settings.dataIngestionLogPath, 'data-ingestion'); + app.dataIngestionLogger = dataIngestionLogger; + + var jobPublisher = new JobPublisher(redisPool); + var jobQueue = new JobQueue(metadataBackend, jobPublisher); + var jobBackend = new JobBackend(metadataBackend, jobQueue); + var jobCanceller = new JobCanceller(); + var jobService = new JobService(jobBackend, jobCanceller); + + var genericController = new GenericController(); + genericController.route(app); + + var queryController = new QueryController( + metadataBackend, + userDatabaseService, + tableCache, + statsClient, + userLimitsService + ); + queryController.route(app); + + var copyController = new CopyController( + metadataBackend, + userDatabaseService, + userLimitsService, + dataIngestionLogger + ); + copyController.route(app); + + var jobController = new JobController( + metadataBackend, + userDatabaseService, + jobService, + statsClient, + userLimitsService + ); + jobController.route(app); + + var cacheStatusController = new CacheStatusController(tableCache); + cacheStatusController.route(app); + + var healthCheckController = new HealthCheckController(); + healthCheckController.route(app); + + var versionController = new VersionController(); + versionController.route(app); + + var isBatchProcess = process.argv.indexOf('--no-batch') === -1; + + if (global.settings.environment !== 'test' && isBatchProcess) { + var batchName = global.settings.api_hostname || 'batch'; + app.batch = batchFactory( + metadataBackend, redisPool, batchName, statsClient, global.settings.batch_log_filename + ); + app.batch.start(); + } + + return app; +} + +module.exports = App; diff --git a/app/services/cached-query-tables.js b/app/services/cached-query-tables.js new file mode 100644 index 0000000..cdda805 --- /dev/null +++ b/app/services/cached-query-tables.js @@ -0,0 +1,44 @@ +'use strict'; + +var QueryTables = require('cartodb-query-tables'); + +var generateMD5 = require('../utils/md5'); + +function CachedQueryTables(tableCache) { + this.tableCache = tableCache; +} + +module.exports = CachedQueryTables; + +CachedQueryTables.prototype.getAffectedTablesFromQuery = function(pg, sql, skipCache, callback) { + var self = this; + + var cacheKey = sqlCacheKey(pg.username(), sql); + + var cachedResult; + if (!skipCache) { + cachedResult = this.tableCache.peek(cacheKey); + } + + if (cachedResult) { + cachedResult.hits++; + return callback(null, cachedResult.result); + } else { + QueryTables.getAffectedTablesFromQuery(pg, sql, function(err, result) { + if (err) { + return callback(err); + } + + self.tableCache.set(cacheKey, { + result: result, + hits: 0 + }); + + return callback(null, result); + }); + } +}; + +function sqlCacheKey(user, sql) { + return user + ':' + generateMD5(sql); +} diff --git a/app/services/error_handler.js b/app/services/error_handler.js new file mode 100644 index 0000000..0f14740 --- /dev/null +++ b/app/services/error_handler.js @@ -0,0 +1,36 @@ +'use strict'; + +class ErrorHandler extends Error { + constructor({ message, context, detail, hint, http_status, name }) { + super(message); + + this.http_status = this.getHttpStatus(http_status); + this.context = context; + this.detail = detail; + this.hint = hint; + + if (name) { + this.name = name; + } + } + + getResponse() { + return { + error: [this.message], + context: this.context, + detail: this.detail, + hint: this.hint + }; + } + + getHttpStatus(http_status = 400) { + if (this.message.includes('permission denied')) { + return 403; + } + + return http_status; + } + +} + +module.exports = ErrorHandler; diff --git a/app/services/error_handler_factory.js b/app/services/error_handler_factory.js new file mode 100644 index 0000000..5d9429c --- /dev/null +++ b/app/services/error_handler_factory.js @@ -0,0 +1,41 @@ +'use strict'; + +const ErrorHandler = require('./error_handler'); +const { codeToCondition } = require('../postgresql/error_codes'); + +module.exports = function ErrorHandlerFactory (err) { + if (isTimeoutError(err)) { + return createTimeoutError(); + } else { + return createGenericError(err); + } +}; + +function isTimeoutError(err) { + return err.message && ( + err.message.indexOf('statement timeout') > -1 || + err.message.indexOf('RuntimeError: Execution of function interrupted by signal') > -1 || + err.message.indexOf('canceling statement due to user request') > -1 + ); +} + +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.', + context: 'limit', + detail: 'datasource', + http_status: 429 + }); +} + +function createGenericError(err) { + return new ErrorHandler({ + message: err.message, + context: err.context, + detail: err.detail, + hint: err.hint, + http_status: err.http_status, + name: codeToCondition[err.code] || err.name + }); +} diff --git a/app/services/logger.js b/app/services/logger.js new file mode 100644 index 0000000..400941b --- /dev/null +++ b/app/services/logger.js @@ -0,0 +1,38 @@ +'use strict'; + +const bunyan = require('bunyan'); + +class Logger { + constructor (path, name) { + const stream = { + level: process.env.NODE_ENV === 'test' ? 'fatal' : 'info' + }; + + if (path) { + stream.path = path; + } else { + stream.stream = process.stdout; + } + + this.path = path; + this.logger = bunyan.createLogger({ + name, + streams: [stream] + }); + } + + info (log, message) { + this.logger.info(log, message); + } + + warn (log, message) { + this.logger.warn(log, message); + } + + reopenFileStreams () { + console.log('Reloading log file', this.path); + this.logger.reopenFileStreams(); + } +} + +module.exports = Logger; diff --git a/app/services/pg-entities-access-validator.js b/app/services/pg-entities-access-validator.js new file mode 100644 index 0000000..bf0ed36 --- /dev/null +++ b/app/services/pg-entities-access-validator.js @@ -0,0 +1,63 @@ +'use strict'; + +const FORBIDDEN_ENTITIES = { + carto: ['*'], + cartodb: [ + 'cdb_analysis_catalog', + 'cdb_conf', + 'cdb_tablemetadata' + ], + pg_catalog: ['*'], + information_schema: ['*'], + public: ['spatial_ref_sys'], + topology: [ + 'layer', + 'topology' + ] +}; + +const Validator = { + validate(affectedTables, authorizationLevel) { + let hardValidationResult = true; + let softValidationResult = true; + + if (!!affectedTables && affectedTables.tables) { + if (global.settings.validatePGEntitiesAccess) { + hardValidationResult = this.hardValidation(affectedTables.tables); + } + + if (authorizationLevel !== 'master') { + softValidationResult = this.softValidation(affectedTables.tables); + } + } + + return hardValidationResult && softValidationResult; + }, + + hardValidation(tables) { + for (let table of tables) { + if (FORBIDDEN_ENTITIES[table.schema_name] && FORBIDDEN_ENTITIES[table.schema_name].length && + ( + FORBIDDEN_ENTITIES[table.schema_name][0] === '*' || + FORBIDDEN_ENTITIES[table.schema_name].includes(table.table_name) + ) + ) { + return false; + } + } + + return true; + }, + + softValidation(tables) { + for (let table of tables) { + if (table.table_name.match(/\bpg_/)) { + return false; + } + } + + return true; + } +}; + +module.exports = Validator; diff --git a/app/services/stream_copy.js b/app/services/stream_copy.js new file mode 100644 index 0000000..34f91d2 --- /dev/null +++ b/app/services/stream_copy.js @@ -0,0 +1,77 @@ +'use strict'; + +const PSQL = require('cartodb-psql'); +const copyTo = require('pg-copy-streams').to; +const copyFrom = require('pg-copy-streams').from; +const { Client } = require('pg'); + +const ACTION_TO = 'to'; +const ACTION_FROM = 'from'; +const DEFAULT_TIMEOUT = "'5h'"; + +module.exports = class StreamCopy { + + constructor(sql, userDbParams) { + const dbParams = Object.assign({}, userDbParams, { + port: global.settings.db_batch_port || userDbParams.port + }); + this.pg = new PSQL(dbParams); + this.sql = sql; + this.stream = null; + this.timeout = global.settings.copy_timeout || DEFAULT_TIMEOUT; + } + + static get ACTION_TO() { + return ACTION_TO; + } + + static get ACTION_FROM() { + return ACTION_FROM; + } + + getPGStream(action, cb) { + this.pg.connect((err, client, done) => { + if (err) { + return cb(err); + } + + client.query('SET statement_timeout=' + this.timeout, (err) => { + + if (err) { + return cb(err); + } + + const streamMaker = action === ACTION_TO ? copyTo : copyFrom; + this.stream = streamMaker(this.sql); + const pgstream = client.query(this.stream); + + pgstream + .on('end', () => { + if(action === ACTION_TO) { + pgstream.connection.stream.resume(); + } + done(); + }) + .on('error', err => done(err)) + .on('cancelQuery', err => { + if(action === ACTION_TO) { + // See https://www.postgresql.org/docs/9.5/static/protocol-flow.html#PROTOCOL-COPY + const cancelingClient = new Client(client.connectionParameters); + cancelingClient.cancel(client, pgstream); + + // see https://node-postgres.com/api/pool#releasecallback + return done(err); + } else if (action === ACTION_FROM) { + client.connection.sendCopyFail('CARTO SQL API: Connection closed by client'); + } + }); + + cb(null, pgstream); + }); + }); + } + + getRowCount() { + return this.stream.rowCount; + } +}; diff --git a/app/services/stream_copy_metrics.js b/app/services/stream_copy_metrics.js new file mode 100644 index 0000000..295b3c7 --- /dev/null +++ b/app/services/stream_copy_metrics.js @@ -0,0 +1,85 @@ +'use strict'; + +const { getFormatFromCopyQuery } = require('../utils/query_info'); + +module.exports = class StreamCopyMetrics { + constructor(logger, type, sql, user, isGzip = false) { + this.logger = logger; + + this.type = type; + this.format = getFormatFromCopyQuery(sql); + this.isGzip = isGzip; + this.username = user; + this.size = 0; + this.gzipSize = 0; + this.rows = 0; + + this.startTime = new Date(); + this.endTime = null; + this.time = null; + + this.success = true; + this.error = null; + + this.ended = false; + } + + addSize(size) { + this.size += size; + } + + addGzipSize(size) { + this.gzipSize += size; + } + + end(rows = null, error = null) { + if (this.ended) { + return; + } + + this.ended = true; + + if (Number.isInteger(rows)) { + this.rows = rows; + } + + if (error instanceof Error) { + this.error = error; + } + + this.endTime = new Date(); + this.time = (this.endTime.getTime() - this.startTime.getTime()) / 1000; + + this._log( + this.startTime.toISOString(), + this.isGzip && this.gzipSize ? this.gzipSize : null, + this.error ? this.error.message : null + ); + } + + _log(timestamp, gzipSize = null, errorMessage = null) { + let logData = { + type: this.type, + format: this.format, + size: this.size, + rows: this.rows, + gzip: this.isGzip, + 'cdb-user': this.username, + time: this.time, + timestamp + }; + + if (gzipSize) { + logData.gzipSize = gzipSize; + } + + if (errorMessage) { + logData.error = errorMessage; + this.success = false; + } + + logData.success = this.success; + + this.logger.info(logData); + } +}; diff --git a/app/services/user_database_service.js b/app/services/user_database_service.js new file mode 100644 index 0000000..26790a2 --- /dev/null +++ b/app/services/user_database_service.js @@ -0,0 +1,107 @@ +'use strict'; + +function isApiKeyFound(apikey) { + return apikey.type !== null && + apikey.user !== null && + apikey.databasePassword !== null && + apikey.databaseRole !== null; +} + +function UserDatabaseService(metadataBackend) { + this.metadataBackend = metadataBackend; +} + +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 }) { + return (authorizationLevel === 'master') && !apikeyToken; +} + +/** + * Callback is invoked with `dbParams` and `authDbParams`. + * `dbParams` depends on AuthApi verification so it might return a public user with just SELECT permission, where + * `authDbParams` will always return connection params as AuthApi had authorized the connection. + * That might be useful when you have to run a query with and without permissions. + * + * @param {AuthApi} authApi + * @param {String} cdbUsername + * @param {Function} callback (err, dbParams, authDbParams) + */ +UserDatabaseService.prototype.getConnectionParams = function (username, apikeyToken, authorizationLevel, callback) { + this.metadataBackend.getAllUserDBParams(username, (err, dbParams) => { + if (err) { + err.http_status = 404; + err.message = errorUserNotFoundMessageTemplate(username); + + return callback(err); + } + + const commonDBConfiguration = { + port: global.settings.db_port, + host: dbParams.dbhost, + dbname: dbParams.dbname, + }; + + this.metadataBackend.getMasterApikey(username, (err, masterApikey) => { + + if (err) { + err.http_status = 404; + err.message = errorUserNotFoundMessageTemplate(username); + + return callback(err); + } + + if (!isApiKeyFound(masterApikey)) { + const apiKeyNotFoundError = new Error('Unauthorized'); + apiKeyNotFoundError.type = 'auth'; + apiKeyNotFoundError.subtype = 'api-key-not-found'; + apiKeyNotFoundError.http_status = 401; + + return callback(apiKeyNotFoundError); + } + + const masterDBConfiguration = Object.assign({ + user: masterApikey.databaseRole, + pass: masterApikey.databasePassword + }, + commonDBConfiguration); + + if (isOauthAuthorization({ apikeyToken, authorizationLevel})) { + return callback(null, masterDBConfiguration, masterDBConfiguration); + } + + // Default Api key fallback + apikeyToken = apikeyToken || 'default_public'; + + this.metadataBackend.getApikey(username, apikeyToken, (err, apikey) => { + if (err) { + err.http_status = 404; + err.message = errorUserNotFoundMessageTemplate(username); + + return callback(err); + } + + if (!isApiKeyFound(apikey)) { + const apiKeyNotFoundError = new Error('Unauthorized'); + apiKeyNotFoundError.type = 'auth'; + apiKeyNotFoundError.subtype = 'api-key-not-found'; + apiKeyNotFoundError.http_status = 401; + + return callback(apiKeyNotFoundError); + } + + const DBConfiguration = Object.assign({ + user: apikey.databaseRole, + pass: apikey.databasePassword + }, + commonDBConfiguration); + + callback(null, DBConfiguration, masterDBConfiguration); + }); + }); + }); +}; + +module.exports = UserDatabaseService; diff --git a/app/services/user_limits.js b/app/services/user_limits.js new file mode 100644 index 0000000..91faa2e --- /dev/null +++ b/app/services/user_limits.js @@ -0,0 +1,27 @@ +'use strict'; + +/** + * UserLimits + * @param {cartodb-redis} metadataBackend + * @param {object} options + */ +class UserLimits { + constructor(metadataBackend, options = {}) { + this.metadataBackend = metadataBackend; + this.options = options; + + this.preprareRateLimit(); + } + + preprareRateLimit() { + if (this.options.limits.rateLimitsEnabled) { + this.metadataBackend.loadRateLimitsScript(); + } + } + + getRateLimit(user, endpointGroup, callback) { + this.metadataBackend.getRateLimit(user, 'sql', endpointGroup, callback); + } +} + +module.exports = UserLimits; diff --git a/app/stats/client.js b/app/stats/client.js new file mode 100644 index 0000000..be6f6c1 --- /dev/null +++ b/app/stats/client.js @@ -0,0 +1,75 @@ +'use strict'; + +var _ = require('underscore'); +var debug = require('debug')('windshaft:stats_client'); +var StatsD = require('node-statsd').StatsD; + +module.exports = { + /** + * Returns an StatsD instance or an stub object that replicates the StatsD public interface so there is no need to + * keep checking if the stats_client is instantiated or not. + * + * The first call to this method implies all future calls will use the config specified in the very first call. + * + * TODO: It's far from ideal to use make this a singleton, improvement desired. + * We proceed this way to be able to use StatsD from several places sharing one single StatsD instance. + * + * @param config Configuration for StatsD, if undefined it will return an stub + * @returns {StatsD|Object} + */ + getInstance: function(config) { + + if (!this.instance) { + + var instance; + + if (config) { + instance = new StatsD(config); + instance.last_error = { msg: '', count: 0 }; + instance.socket.on('error', function (err) { + var last_err = instance.last_error; + var last_msg = last_err.msg; + var this_msg = '' + err; + if (this_msg !== last_msg) { + debug("statsd client socket error: " + err); + instance.last_error.count = 1; + instance.last_error.msg = this_msg; + } else { + ++last_err.count; + if (!last_err.interval) { + instance.last_error.interval = setInterval(function () { + var count = instance.last_error.count; + if (count > 1) { + debug("last statsd client socket error repeated " + count + " times"); + instance.last_error.count = 1; + clearInterval(instance.last_error.interval); + instance.last_error.interval = null; + } + }, 1000); + } + } + }); + } else { + var stubFunc = function (stat, value, sampleRate, callback) { + if (_.isFunction(callback)) { + callback(null, 0); + } + }; + instance = { + timing: stubFunc, + increment: stubFunc, + decrement: stubFunc, + gauge: stubFunc, + unique: stubFunc, + set: stubFunc, + sendAll: stubFunc, + send: stubFunc + }; + } + + this.instance = instance; + } + + return this.instance; + } +}; diff --git a/app/stats/profiler-proxy.js b/app/stats/profiler-proxy.js new file mode 100644 index 0000000..5ac60d5 --- /dev/null +++ b/app/stats/profiler-proxy.js @@ -0,0 +1,55 @@ +'use strict'; + +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) { + this.profile = !!opts.profile; + + this.profiler = null; + if (!!opts.profile) { + this.profiler = new Profiler({statsd_client: opts.statsd_client}); + } +} + +ProfilerProxy.prototype.done = function(what) { + if (this.profile) { + this.profiler.done(what); + } +}; + +ProfilerProxy.prototype.end = function() { + if (this.profile) { + this.profiler.end(); + } +}; + +ProfilerProxy.prototype.start = function(what) { + if (this.profile) { + this.profiler.start(what); + } +}; + +ProfilerProxy.prototype.add = function(what) { + if (this.profile) { + this.profiler.add(what || {}); + } +}; + +ProfilerProxy.prototype.sendStats = function() { + if (this.profile) { + this.profiler.sendStats(); + } +}; + +ProfilerProxy.prototype.toString = function() { + return this.profile ? this.profiler.toString() : ""; +}; + +ProfilerProxy.prototype.toJSONString = function() { + return this.profile ? this.profiler.toJSONString() : "{}"; +}; + +module.exports = ProfilerProxy; diff --git a/app/utils/cache_key_generator.js b/app/utils/cache_key_generator.js new file mode 100644 index 0000000..5f1f4bb --- /dev/null +++ b/app/utils/cache_key_generator.js @@ -0,0 +1,5 @@ +'use strict'; + +module.exports = function generateCacheKey(database, affectedTables) { + return database + ":" + affectedTables.join(','); +}; diff --git a/app/utils/content_disposition.js b/app/utils/content_disposition.js new file mode 100644 index 0000000..77eca29 --- /dev/null +++ b/app/utils/content_disposition.js @@ -0,0 +1,8 @@ +'use strict'; + +module.exports = function getContentDisposition(formatter, filename, inline) { + var ext = formatter.getFileExtension(); + var time = new Date().toUTCString(); + return ( inline ? 'inline' : 'attachment' ) + '; filename=' + filename + '.' + ext + '; ' + + 'modification-date="' + time + '";'; +}; diff --git a/app/utils/date_to_json.js b/app/utils/date_to_json.js new file mode 100644 index 0000000..ce09602 --- /dev/null +++ b/app/utils/date_to_json.js @@ -0,0 +1,19 @@ +'use strict'; + +// jshint ignore:start +function pad(n) { + return n < 10 ? '0' + n : n; +} + +Date.prototype.toJSON = function() { + var s = this.getFullYear() + '-' + pad(this.getMonth() + 1) + '-' + pad(this.getDate()) + 'T' + + pad(this.getHours()) + ':' + pad(this.getMinutes()) + ':' + pad(this.getSeconds()); + var offset = this.getTimezoneOffset(); + if (offset === 0) { + s += 'Z'; + } else { + s += ( offset < 0 ? '+' : '-' ) + pad(Math.abs(offset / 60)) + pad(Math.abs(offset % 60)); + } + return s; +}; +// jshint ignore:end diff --git a/app/utils/filename_sanitizer.js b/app/utils/filename_sanitizer.js new file mode 100644 index 0000000..fd97dc9 --- /dev/null +++ b/app/utils/filename_sanitizer.js @@ -0,0 +1,9 @@ +'use strict'; + +var path = require('path'); + +module.exports = function sanitize_filename(filename) { + filename = path.basename(filename, path.extname(filename)); + filename = filename.replace(/[;()\[\]<>'"\s]/g, '_'); + return filename; +}; diff --git a/app/utils/md5.js b/app/utils/md5.js new file mode 100644 index 0000000..946017b --- /dev/null +++ b/app/utils/md5.js @@ -0,0 +1,9 @@ +'use strict'; + +var crypto = require('crypto'); + +module.exports = function generateMD5(data){ + var hash = crypto.createHash('md5'); + hash.update(data); + return hash.digest('hex'); +}; diff --git a/app/utils/no_cache.js b/app/utils/no_cache.js new file mode 100644 index 0000000..7c26803 --- /dev/null +++ b/app/utils/no_cache.js @@ -0,0 +1,49 @@ +'use strict'; + +/** + * This module provides an object with the interface of an LRU cache + * but that actually does not store anything. + * + * See https://github.com/isaacs/node-lru-cache/tree/v2.5.0 + */ + +function NoCache() { +} + +module.exports = NoCache; + +NoCache.prototype.set = function (/* key, value */) { + return true; +}; + +NoCache.prototype.get = function (/* key */) { + return undefined; +}; + +NoCache.prototype.peek = function (/* key */) { + return undefined; +}; + +NoCache.prototype.del = function (/* key */) { + return undefined; +}; + +NoCache.prototype.reset = function () { + return undefined; +}; + +NoCache.prototype.has = function (/* key */) { + return false; +}; + +NoCache.prototype.forEach = function (/* fn, thisp */) { + return undefined; +}; + +NoCache.prototype.keys = function () { + return []; +}; + +NoCache.prototype.values = function () { + return []; +}; diff --git a/app/utils/query_info.js b/app/utils/query_info.js new file mode 100644 index 0000000..d1ad0b9 --- /dev/null +++ b/app/utils/query_info.js @@ -0,0 +1,31 @@ +'use strict'; + +const COPY_FORMATS = ['TEXT', 'CSV', 'BINARY']; + +module.exports = { + getFormatFromCopyQuery(copyQuery) { + let format = 'TEXT'; // Postgres default format + + copyQuery = copyQuery.toUpperCase(); + + if (!copyQuery.startsWith("COPY ")) { + return false; + } + + if(copyQuery.includes(' WITH') && copyQuery.includes('FORMAT ')) { + const regex = /\bFORMAT\s+(\w+)/; + const result = regex.exec(copyQuery); + + if (result && result.length === 2) { + if (COPY_FORMATS.includes(result[1])) { + format = result[1]; + format = format.toUpperCase(); + } else { + format = false; + } + } + } + + return format; + } +}; diff --git a/app/utils/query_may_write.js b/app/utils/query_may_write.js new file mode 100644 index 0000000..76d6a3b --- /dev/null +++ b/app/utils/query_may_write.js @@ -0,0 +1,14 @@ +'use strict'; + +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 + * pretty sure of a false return. + * + * @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) { + return sqlQueryMayWriteRegex.test(sql); +}; diff --git a/app/utils/table_cache_factory.js b/app/utils/table_cache_factory.js new file mode 100644 index 0000000..7c83c8b --- /dev/null +++ b/app/utils/table_cache_factory.js @@ -0,0 +1,32 @@ +'use strict'; + +var LRU = require('lru-cache'); +var NoCache = require('./no_cache'); + +/** + * This module abstracts the creation of a tableCache, + * depending on the configuration passed along + */ + +function TableCacheFactory() { +} + +module.exports = TableCacheFactory; + +TableCacheFactory.prototype.build = function (settings) { + var enabled = settings.tableCacheEnabled || false; + var tableCache = null; + + if(enabled) { + tableCache = LRU({ + // store no more than these many items in the cache + max: settings.tableCacheMax || 8192, + // consider entries expired after these many milliseconds (10 minutes by default) + maxAge: settings.tableCacheMaxAge || 1000*60*10 + }); + } else { + tableCache = new NoCache(); + } + + return tableCache; +}; diff --git a/batch/README.md b/batch/README.md new file mode 100644 index 0000000..60cc64e --- /dev/null +++ b/batch/README.md @@ -0,0 +1,122 @@ +# Batch Queries + +This document describes features from Batch Queries, it also details some internals that might be useful for maintainers +and developers. + + +## Redis data structures + +### Jobs definition + +Redis Hash: `batch:jobs:{UUID}`. + +Redis DB: 5. + +It stores the job definition, the user, and some metadata like the final status, the failure reason, and so. + +### Job queues + +Redis List: `batch:queue:{username}`. + +Redis DB: 5. + +It stores a pending list of jobs per user. It points to a job definition with the `{UUID}`. + +### Job notifications + +Redis Pub/Sub channel: `batch:users`. + +Redis DB: 0. + +In order to notify new jobs, it uses a Pub/Sub channel were the username for the queued job is published. + + +## Job types + +Format for the currently supported query types, and what they are missing in terms of features. + +### Simple + +```json +{ + "query": "update ..." +} +``` + +Does not support main fallback queries. Ideally it should support something like: + +```json +{ + "query": "update ...", + "onsuccess": "select 'general success fallback'", + "onerror": "select 'general error fallback'" +} +``` + +### Multiple + +```json +{ + "query": [ + "update ...", + "select ... into ..." + ] +} +``` + +Does not support main fallback queries. Ideally it should support something like: + +```json +{ + "query": [ + "update ...", + "select ... into ..." + ], + "onsuccess": "select 'general success fallback'", + "onerror": "select 'general error fallback'" +} +``` + +### Fallback + +```json +{ + "query": { + "query": [ + { + "query": "select 1", + "onsuccess": "select 'success fallback query 1'", + "onerror": "select 'error fallback query 1'" + }, + { + "query": "select 2", + "onerror": "select 'error fallback query 2'" + } + ], + "onsuccess": "select 'general success fallback'", + "onerror": "select 'general error fallback'" + } +} +``` + +It's weird to have two nested `query` attributes. Also, it's not possible to mix _plain_ with _fallback_ ones. +Ideally it should support something like: + +```json +{ + "query": [ + { + "query": "select 1", + "onsuccess": "select 'success fallback query 1'", + "onerror": "select 'error fallback query 1'" + }, + "select 2" + ], + "onsuccess": "select 'general success fallback'", + "onerror": "select 'general error fallback'" + } +} +``` + +Where you don't need a nested `query` attribute, it's just an array as in Multiple job type, and you can mix objects and +plain queries. diff --git a/batch/batch-logger.js b/batch/batch-logger.js new file mode 100644 index 0000000..11d1ade --- /dev/null +++ b/batch/batch-logger.js @@ -0,0 +1,16 @@ +'use strict'; + +const Logger = require('../app/services/logger'); + +class BatchLogger extends Logger { + constructor (path, name) { + super(path, name); + } + + log (job) { + return job.log(this.logger); + } + +} + +module.exports = BatchLogger; diff --git a/batch/batch.js b/batch/batch.js new file mode 100644 index 0000000..a7a5e72 --- /dev/null +++ b/batch/batch.js @@ -0,0 +1,217 @@ +'use strict'; + +var util = require('util'); +var EventEmitter = require('events').EventEmitter; +var debug = require('./util/debug')('batch'); +var queue = require('queue-async'); +var HostScheduler = require('./scheduler/host-scheduler'); + +var EMPTY_QUEUE = true; + +var MINUTE = 60 * 1000; +var SCHEDULE_INTERVAL = 1 * MINUTE; + +function Batch(name, userDatabaseMetadataService, jobSubscriber, jobQueue, jobRunner, jobService, redisPool, logger) { + EventEmitter.call(this); + this.name = name || 'batch'; + this.userDatabaseMetadataService = userDatabaseMetadataService; + this.jobSubscriber = jobSubscriber; + this.jobQueue = jobQueue; + this.jobRunner = jobRunner; + this.jobService = jobService; + this.logger = logger; + this.hostScheduler = new HostScheduler(this.name, { run: this.processJob.bind(this) }, redisPool); + + // map: user => jobId. Will be used for draining jobs. + this.workInProgressJobs = {}; +} +util.inherits(Batch, EventEmitter); + +module.exports = Batch; + +Batch.prototype.start = function () { + var self = this; + var onJobHandler = createJobHandler(self.name, self.userDatabaseMetadataService, self.hostScheduler); + + self.jobQueue.scanQueues(function (err, queues) { + if (err) { + return self.emit('error', err); + } + + queues.forEach(onJobHandler); + self._startScheduleInterval(onJobHandler); + + self.jobSubscriber.subscribe(onJobHandler, function (err) { + if (err) { + return self.emit('error', err); + } + + self.emit('ready'); + }); + }); +}; + +function createJobHandler (name, userDatabaseMetadataService, hostScheduler) { + return function onJobHandler(user) { + userDatabaseMetadataService.getUserMetadata(user, function (err, userDatabaseMetadata) { + if (err) { + return debug('Could not get host user=%s from %s. Reason: %s', user, name, err.message); + } + + var host = userDatabaseMetadata.host; + + debug('[%s] onJobHandler(%s, %s)', name, user, host); + hostScheduler.add(host, user, function(err) { + if (err) { + return debug( + 'Could not schedule host=%s user=%s from %s. Reason: %s', host, user, name, err.message + ); + } + }); + }); + }; +} + +Batch.prototype._startScheduleInterval = function (onJobHandler) { + var self = this; + + self.scheduleInterval = setInterval(function () { + self.jobQueue.getQueues(function (err, queues) { + if (err) { + return debug('Could not get queues from %s. Reason: %s', self.name, err.message); + } + + queues.forEach(onJobHandler); + }); + }, SCHEDULE_INTERVAL); +}; + +Batch.prototype._stopScheduleInterval = function () { + if (this.scheduleInterval) { + clearInterval(this.scheduleInterval); + } +}; + +Batch.prototype.processJob = function (user, callback) { + var self = this; + + self.jobQueue.dequeue(user, function (err, jobId) { + if (err) { + return callback(new Error('Could not get job from "' + user + '". Reason: ' + err.message), !EMPTY_QUEUE); + } + + if (!jobId) { + debug('Queue empty user=%s', user); + return callback(null, EMPTY_QUEUE); + } + + self._processWorkInProgressJob(user, jobId, function (err, job) { + if (err) { + debug(err); + if (err.name === 'JobNotRunnable') { + return callback(null, !EMPTY_QUEUE); + } + return callback(err, !EMPTY_QUEUE); + } + + debug( + '[%s] Job=%s status=%s user=%s (failed_reason=%s)', + self.name, jobId, job.data.status, user, job.failed_reason + ); + + self.logger.log(job); + + return callback(null, !EMPTY_QUEUE); + }); + }); +}; + +Batch.prototype._processWorkInProgressJob = function (user, jobId, callback) { + var self = this; + + self.setWorkInProgressJob(user, jobId, function (errSet) { + if (errSet) { + debug(new Error('Could not add job to work-in-progress list. Reason: ' + errSet.message)); + } + + self.jobRunner.run(jobId, function (err, job) { + self.clearWorkInProgressJob(user, jobId, function (errClear) { + if (errClear) { + debug(new Error('Could not clear job from work-in-progress list. Reason: ' + errClear.message)); + } + + return callback(err, job); + }); + }); + }); +}; + +Batch.prototype.drain = function (callback) { + var self = this; + var workingUsers = this.getWorkInProgressUsers(); + var batchQueues = queue(workingUsers.length); + + workingUsers.forEach(function (user) { + batchQueues.defer(self._drainJob.bind(self), user); + }); + + batchQueues.awaitAll(function (err) { + if (err) { + debug('Something went wrong draining', err); + } else { + debug('Drain complete'); + } + + callback(); + }); +}; + +Batch.prototype._drainJob = function (user, callback) { + var self = this; + var job_id = this.getWorkInProgressJob(user); + + if (!job_id) { + return process.nextTick(function () { + return callback(); + }); + } + + this.jobService.drain(job_id, function (err) { + if (err && err.name === 'CancelNotAllowedError') { + return callback(); + } + + if (err) { + return callback(err); + } + + self.jobQueue.enqueueFirst(user, job_id, callback); + }); +}; + +Batch.prototype.stop = function (callback) { + this.removeAllListeners(); + this._stopScheduleInterval(); + this.jobSubscriber.unsubscribe(callback); +}; + + +/* Work in progress jobs */ + +Batch.prototype.setWorkInProgressJob = function(user, jobId, callback) { + this.workInProgressJobs[user] = jobId; + this.jobService.addWorkInProgressJob(user, jobId, callback); +}; + +Batch.prototype.getWorkInProgressJob = function(user) { + return this.workInProgressJobs[user]; +}; + +Batch.prototype.clearWorkInProgressJob = function(user, jobId, callback) { + delete this.workInProgressJobs[user]; + this.jobService.clearWorkInProgressJob(user, jobId, callback); +}; + +Batch.prototype.getWorkInProgressUsers = function() { + return Object.keys(this.workInProgressJobs); +}; diff --git a/batch/index.js b/batch/index.js new file mode 100644 index 0000000..6ef5422 --- /dev/null +++ b/batch/index.js @@ -0,0 +1,39 @@ +'use strict'; + +var JobRunner = require('./job_runner'); +var QueryRunner = require('./query_runner'); +var JobCanceller = require('./job_canceller'); +var JobSubscriber = require('./pubsub/job-subscriber'); +var UserDatabaseMetadataService = require('./user_database_metadata_service'); +var JobPublisher = require('./pubsub/job-publisher'); +var JobQueue = require('./job_queue'); +var JobBackend = require('./job_backend'); +var JobService = require('./job_service'); +var BatchLogger = require('./batch-logger'); +var Batch = require('./batch'); + +module.exports = function batchFactory (metadataBackend, redisPool, name, statsdClient, loggerPath) { + var userDatabaseMetadataService = new UserDatabaseMetadataService(metadataBackend); + + var jobSubscriber = new JobSubscriber(redisPool); + var jobPublisher = new JobPublisher(redisPool); + + var jobQueue = new JobQueue(metadataBackend, jobPublisher); + var jobBackend = new JobBackend(metadataBackend, jobQueue); + var queryRunner = new QueryRunner(userDatabaseMetadataService); + var jobCanceller = new JobCanceller(); + var jobService = new JobService(jobBackend, jobCanceller); + var jobRunner = new JobRunner(jobService, jobQueue, queryRunner, metadataBackend, statsdClient); + var logger = new BatchLogger(loggerPath, 'batch-queries'); + + return new Batch( + name, + userDatabaseMetadataService, + jobSubscriber, + jobQueue, + jobRunner, + jobService, + redisPool, + logger + ); +}; diff --git a/batch/job_backend.js b/batch/job_backend.js new file mode 100644 index 0000000..b10d3a9 --- /dev/null +++ b/batch/job_backend.js @@ -0,0 +1,290 @@ +'use strict'; + +var REDIS_PREFIX = 'batch:jobs:'; +var REDIS_DB = 5; +var JobStatus = require('./job_status'); +var queue = require('queue-async'); +var debug = require('./util/debug')('job-backend'); + +function JobBackend(metadataBackend, jobQueue) { + this.metadataBackend = metadataBackend; + this.jobQueue = jobQueue; + this.maxNumberOfQueuedJobs = global.settings.batch_max_queued_jobs || 64; + this.inSecondsJobTTLAfterFinished = global.settings.finished_jobs_ttl_in_seconds || 2 * 3600; // 2 hours + this.hostname = global.settings.api_hostname || 'batch'; +} + +function toRedisParams(job) { + var redisParams = [REDIS_PREFIX + job.job_id]; + var obj = JSON.parse(JSON.stringify(job)); + delete obj.job_id; + + for (var property in obj) { + if (obj.hasOwnProperty(property)) { + redisParams.push(property); + if (property === 'query' && typeof obj[property] !== 'string') { + redisParams.push(JSON.stringify(obj[property])); + } else { + redisParams.push(obj[property]); + } + } + } + + return redisParams; +} + +function toObject(job_id, redisParams, redisValues) { + var obj = {}; + + redisParams.shift(); // job_id value + redisParams.pop(); // WARN: weird function pushed by metadataBackend + + for (var i = 0; i < redisParams.length; i++) { + // TODO: this should be moved to job model + if (redisParams[i] === 'query') { + try { + obj[redisParams[i]] = JSON.parse(redisValues[i]); + } catch (e) { + obj[redisParams[i]] = redisValues[i]; + } + } else if (redisValues[i]) { + obj[redisParams[i]] = redisValues[i]; + } + } + + obj.job_id = job_id; // adds redisKey as object property + + return obj; +} + +function isJobFound(redisValues) { + return !!(redisValues[0] && redisValues[1] && redisValues[2] && redisValues[3] && redisValues[4]); +} + +function getNotFoundError(job_id) { + var notFoundError = new Error('Job with id ' + job_id + ' not found'); + notFoundError.name = 'NotFoundError'; + return notFoundError; +} + +JobBackend.prototype.get = function (job_id, callback) { + if (!job_id) { + return callback(getNotFoundError(job_id)); + } + + var self = this; + var redisParams = [ + REDIS_PREFIX + job_id, + 'user', + 'status', + 'query', + 'created_at', + 'updated_at', + 'host', + 'failed_reason', + 'fallback_status', + 'host', + 'port', + 'pass', + 'dbname', + 'dbuser' + ]; + + self.metadataBackend.redisCmd(REDIS_DB, 'HMGET', redisParams , function (err, redisValues) { + if (err) { + return callback(err); + } + + if (!isJobFound(redisValues)) { + return callback(getNotFoundError(job_id)); + } + + var jobData = toObject(job_id, redisParams, redisValues); + + callback(null, jobData); + }); +}; + +JobBackend.prototype.create = function (job, callback) { + var self = this; + + this.jobQueue.size(job.user, function(err, size) { + if (err) { + return callback(new Error('Failed to create job, could not determine user queue size')); + } + + if (size >= self.maxNumberOfQueuedJobs) { + return callback(new Error( + 'Failed to create job. ' + + 'Max number of jobs (' + self.maxNumberOfQueuedJobs + ') queued reached' + )); + } + + self.get(job.job_id, function (err) { + if (err && err.name !== 'NotFoundError') { + return callback(err); + } + + self.save(job, function (err, jobSaved) { + if (err) { + return callback(err); + } + + self.jobQueue.enqueue(job.user, job.job_id, function (err) { + if (err) { + return callback(err); + } + + return callback(null, jobSaved); + }); + }); + }); + }); +}; + +JobBackend.prototype.update = function (job, callback) { + var self = this; + + self.get(job.job_id, function (err) { + + if (err) { + return callback(err); + } + + self.save(job, callback); + }); +}; + +JobBackend.prototype.save = function (job, callback) { + var self = this; + var redisParams = toRedisParams(job); + + self.metadataBackend.redisCmd(REDIS_DB, 'HMSET', redisParams , function (err) { + if (err) { + return callback(err); + } + + self.setTTL(job, function (err) { + if (err) { + return callback(err); + } + + self.get(job.job_id, function (err, job) { + if (err) { + return callback(err); + } + + callback(null, job); + }); + }); + }); +}; + +var WORK_IN_PROGRESS_JOB = { + DB: 5, + PREFIX_USER: 'batch:wip:user:', + USER_INDEX_KEY: 'batch:wip:users' +}; + +JobBackend.prototype.addWorkInProgressJob = function (user, jobId, callback) { + var userWIPKey = WORK_IN_PROGRESS_JOB.PREFIX_USER + user; + debug('add job %s to user %s (%s)', jobId, user, userWIPKey); + this.metadataBackend.redisMultiCmd(WORK_IN_PROGRESS_JOB.DB, [ + ['SADD', WORK_IN_PROGRESS_JOB.USER_INDEX_KEY, user], + ['RPUSH', userWIPKey, jobId] + ], callback); +}; + +JobBackend.prototype.clearWorkInProgressJob = function (user, jobId, callback) { + var self = this; + var DB = WORK_IN_PROGRESS_JOB.DB; + var userWIPKey = WORK_IN_PROGRESS_JOB.PREFIX_USER + user; + + var params = [userWIPKey, 0, jobId]; + self.metadataBackend.redisCmd(DB, 'LREM', params, function (err) { + if (err) { + return callback(err); + } + + params = [userWIPKey, 0, -1]; + self.metadataBackend.redisCmd(DB, 'LRANGE', params, function (err, workInProgressJobs) { + if (err) { + return callback(err); + } + + debug('user %s has work in progress jobs %j', user, workInProgressJobs); + + if (workInProgressJobs.length < 0) { + return callback(); + } + + debug('delete user %s from index', user); + + params = [WORK_IN_PROGRESS_JOB.USER_INDEX_KEY, user]; + self.metadataBackend.redisCmd(DB, 'SREM', params, function (err) { + if (err) { + return callback(err); + } + + return callback(); + }); + }); + }); +}; + +JobBackend.prototype.listWorkInProgressJobByUser = function (user, callback) { + var userWIPKey = WORK_IN_PROGRESS_JOB.PREFIX_USER + user; + var params = [userWIPKey, 0, -1]; + this.metadataBackend.redisCmd(WORK_IN_PROGRESS_JOB.DB, 'LRANGE', params, callback); +}; + +JobBackend.prototype.listWorkInProgressJobs = function (callback) { + var self = this; + var DB = WORK_IN_PROGRESS_JOB.DB; + + var params = [WORK_IN_PROGRESS_JOB.USER_INDEX_KEY]; + this.metadataBackend.redisCmd(DB, 'SMEMBERS', params, function (err, workInProgressUsers) { + if (err) { + return callback(err); + } + + if (workInProgressUsers < 1) { + return callback(null, {}); + } + + debug('found %j work in progress users', workInProgressUsers); + + var usersQueue = queue(4); + + workInProgressUsers.forEach(function (user) { + usersQueue.defer(self.listWorkInProgressJobByUser.bind(self), user); + }); + + usersQueue.awaitAll(function (err, userWorkInProgressJobs) { + if (err) { + return callback(err); + } + + var workInProgressJobs = workInProgressUsers.reduce(function (users, user, index) { + users[user] = userWorkInProgressJobs[index]; + debug('found %j work in progress jobs for user %s', userWorkInProgressJobs[index], user); + return users; + }, {}); + + callback(null, workInProgressJobs); + }); + }); +}; + +JobBackend.prototype.setTTL = function (job, callback) { + var self = this; + var redisKey = REDIS_PREFIX + job.job_id; + + if (!JobStatus.isFinal(job.status)) { + return callback(); + } + + self.metadataBackend.redisCmd(REDIS_DB, 'EXPIRE', [ redisKey, this.inSecondsJobTTLAfterFinished ], callback); +}; + +module.exports = JobBackend; diff --git a/batch/job_canceller.js b/batch/job_canceller.js new file mode 100644 index 0000000..ca7abd6 --- /dev/null +++ b/batch/job_canceller.js @@ -0,0 +1,78 @@ +'use strict'; + +var PSQL = require('cartodb-psql'); + +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, + }; + + doCancel(job.data.job_id, dbConfiguration, callback); +}; + +function doCancel(job_id, dbConfiguration, callback) { + var pg = new PSQL(dbConfiguration); + + getQueryPID(pg, job_id, function (err, pid) { + if (err) { + return callback(err); + } + + if (!pid) { + return callback(); + } + + doCancelQuery(pg, pid, function (err, isCancelled) { + if (err) { + return callback(err); + } + + if (!isCancelled) { + return callback(new Error('Query has not been cancelled')); + } + + 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) { + if (err) { + return callback(err); + } + + if (!result.rows[0] || !result.rows[0].pid) { + // query is not running actually, but we have to callback w/o error to cancel the job anyway. + return callback(); + } + + callback(null, result.rows[0].pid); + }); +} + +function doCancelQuery(pg, pid, callback) { + var cancelQuery = 'SELECT pg_cancel_backend(' + pid + ')'; + + pg.query(cancelQuery, function (err, result) { + if (err) { + return callback(err); + } + + var isCancelled = result.rows[0].pg_cancel_backend; + + callback(null, isCancelled); + }); +} diff --git a/batch/job_queue.js b/batch/job_queue.js new file mode 100644 index 0000000..10d848b --- /dev/null +++ b/batch/job_queue.js @@ -0,0 +1,165 @@ +'use strict'; + +var debug = require('./util/debug')('queue'); +var queueAsync = require('queue-async'); + +function JobQueue(metadataBackend, jobPublisher) { + this.metadataBackend = metadataBackend; + this.jobPublisher = jobPublisher; +} + +module.exports = JobQueue; + +var QUEUE = { + DB: 5, + PREFIX: 'batch:queue:', + INDEX: 'batch:indexes:queue' +}; + +module.exports.QUEUE = QUEUE; + +JobQueue.prototype.enqueue = function (user, jobId, callback) { + debug('JobQueue.enqueue user=%s, jobId=%s', user, jobId); + + this.metadataBackend.redisMultiCmd(QUEUE.DB, [ + [ 'LPUSH', QUEUE.PREFIX + user, jobId ], + [ 'SADD', QUEUE.INDEX, user ] + ], function (err) { + if (err) { + return callback(err); + } + + this.jobPublisher.publish(user); + callback(); + }.bind(this)); +}; + +JobQueue.prototype.size = function (user, callback) { + this.metadataBackend.redisCmd(QUEUE.DB, 'LLEN', [ QUEUE.PREFIX + user ], callback); +}; + +JobQueue.prototype.dequeue = function (user, callback) { + var dequeueScript = [ + 'local job_id = redis.call("RPOP", KEYS[1])', + 'if redis.call("LLEN", KEYS[1]) == 0 then', + ' redis.call("SREM", KEYS[2], ARGV[1])', + 'end', + 'return job_id' + ].join('\n'); + + var redisParams = [ + 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 + user // ARGV[1] - value of the element to remove from the index + ]; + + this.metadataBackend.redisCmd(QUEUE.DB, 'EVAL', redisParams, function (err, jobId) { + debug('JobQueue.dequeued user=%s, jobId=%s', user, jobId); + return callback(err, jobId); + }); +}; + +JobQueue.prototype.enqueueFirst = function (user, jobId, callback) { + debug('JobQueue.enqueueFirst user=%s, jobId=%s', user, jobId); + this.metadataBackend.redisMultiCmd(QUEUE.DB, [ + [ 'RPUSH', QUEUE.PREFIX + user, jobId ], + [ 'SADD', QUEUE.INDEX, user ] + ], function (err) { + if (err) { + return callback(err); + } + + this.jobPublisher.publish(user); + callback(); + }.bind(this)); +}; + + +JobQueue.prototype.getQueues = function (callback) { + this.metadataBackend.redisCmd(QUEUE.DB, 'SMEMBERS', [ QUEUE.INDEX ], function (err, queues) { + if (err) { + return callback(err); + } + + callback(null, queues); + }); +}; + +JobQueue.prototype.scanQueues = function (callback) { + var self = this; + + self.scan(function (err, queues) { + if (err) { + return callback(err); + } + + self.addToQueueIndex(queues, function (err) { + if (err) { + return callback(err); + } + + callback(null, queues); + }); + }); +}; + +JobQueue.prototype.scan = function (callback) { + var self = this; + var initialCursor = ['0']; + var users = {}; + + self._scan(initialCursor, users, function(err, users) { + if (err) { + return callback(err); + } + + callback(null, Object.keys(users)); + }); +}; + +JobQueue.prototype._scan = function (cursor, users, callback) { + var self = this; + var redisParams = [cursor[0], 'MATCH', QUEUE.PREFIX + '*']; + + self.metadataBackend.redisCmd(QUEUE.DB, 'SCAN', redisParams, function (err, currentCursor) { + if (err) { + return callback(null, users); + } + + var queues = currentCursor[1]; + if (queues) { + queues.forEach(function (queue) { + var user = queue.substr(QUEUE.PREFIX.length); + users[user] = true; + }); + } + + var hasMore = currentCursor[0] !== '0'; + if (!hasMore) { + return callback(null, users); + } + + self._scan(currentCursor, users, callback); + }); +}; + +JobQueue.prototype.addToQueueIndex = function (users, callback) { + var self = this; + var usersQueues = queueAsync(users.length); + + users.forEach(function (user) { + usersQueues.defer(function (user, callback) { + self.metadataBackend.redisCmd(QUEUE.DB, 'SADD', [ QUEUE.INDEX, user], callback); + }, user); + }); + + usersQueues.awaitAll(function (err) { + if (err) { + return callback(err); + } + + callback(null); + }); +}; diff --git a/batch/job_runner.js b/batch/job_runner.js new file mode 100644 index 0000000..87762fe --- /dev/null +++ b/batch/job_runner.js @@ -0,0 +1,144 @@ +'use strict'; + +var errorCodes = require('../app/postgresql/error_codes').codeToCondition; +var jobStatus = require('./job_status'); +var Profiler = require('step-profiler'); +var _ = require('underscore'); + +var REDIS_LIMITS = { + DB: 5, + PREFIX: 'limits:batch:' // + username +}; + +function JobRunner(jobService, jobQueue, queryRunner, metadataBackend, statsdClient) { + this.jobService = jobService; + this.jobQueue = jobQueue; + this.queryRunner = queryRunner; + this.metadataBackend = metadataBackend; + this.statsdClient = statsdClient; +} + +JobRunner.prototype.run = function (job_id, callback) { + var self = this; + + var profiler = new Profiler({ statsd_client: self.statsdClient }); + profiler.start('sqlapi.batch.job'); + + self.jobService.get(job_id, function (err, job) { + if (err) { + return callback(err); + } + + self.getQueryStatementTimeout(job.data.user, function(err, timeout) { + if (err) { + return callback(err); + } + + var query = job.getNextQuery(); + + if (_.isObject(query)) { + if (Number.isFinite(query.timeout) && query.timeout > 0) { + timeout = Math.min(timeout, query.timeout); + } + query = query.query; + } + + try { + job.setStatus(jobStatus.RUNNING); + } catch (err) { + return callback(err); + } + + self.jobService.save(job, function (err, job) { + if (err) { + return callback(err); + } + + profiler.done('running'); + + self._run(job, query, timeout, profiler, 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) { + if (timeoutLimit !== null && Number.isFinite(+timeoutLimit)) { + timeout = +timeoutLimit; + } + + return callback(null, timeout); + }); +}; + +JobRunner.prototype._run = function (job, query, timeout, profiler, callback) { + var self = this; + + const dbparams = { + pass: job.data.pass, + user: job.data.dbuser, + dbname: job.data.dbname, + port: job.data.port, + host: job.data.host + }; + + self.queryRunner.run(job.data.job_id, query, job.data.user, timeout, dbparams, function (err /*, result */) { + if (err) { + if (!err.code) { + return callback(err); + } + // if query has been cancelled then it's going to get the current + // job status saved by query_canceller + if (cancelledByUser(err)) { + return self.jobService.get(job.data.job_id, callback); + } + } + + try { + if (err) { + profiler.done('failed'); + job.setStatus(jobStatus.FAILED, err.message); + } else { + profiler.done('success'); + job.setStatus(jobStatus.DONE); + } + } catch (err) { + return callback(err); + } + + self.jobService.save(job, function (err, job) { + if (err) { + return callback(err); + } + + profiler.done('done'); + profiler.end(); + profiler.sendStats(); + + if (!job.hasNextQuery()) { + return callback(null, job); + } + + self.jobQueue.enqueueFirst(job.data.user, job.data.job_id, function (err) { + if (err) { + return callback(err); + } + + callback(null, job); + }); + }); + }); +}; + +function cancelledByUser(err) { + return errorCodes[err.code.toString()] === 'query_canceled' && err.message.match(/user.*request/); +} + +module.exports = JobRunner; diff --git a/batch/job_service.js b/batch/job_service.js new file mode 100644 index 0000000..d81b3cd --- /dev/null +++ b/batch/job_service.js @@ -0,0 +1,136 @@ +'use strict'; + +var debug = require('./util/debug')('job-service'); +var JobFactory = require('./models/job_factory'); +var jobStatus = require('./job_status'); + +function JobService(jobBackend, jobCanceller) { + this.jobBackend = jobBackend; + this.jobCanceller = jobCanceller; +} + +module.exports = JobService; + +JobService.prototype.get = function (job_id, callback) { + this.jobBackend.get(job_id, function (err, data) { + if (err) { + return callback(err); + } + + var job; + + try { + job = JobFactory.create(data); + } catch (err) { + return callback(err); + } + + callback(null, job); + }); +}; + +JobService.prototype.create = function (data, callback) { + try { + var job = JobFactory.create(data); + job.validate(); + this.jobBackend.create(job.data, function (err) { + if (err) { + return callback(err); + } + callback(null, job); + }); + } catch (err) { + return callback(err); + } +}; + +JobService.prototype.save = function (job, callback) { + var self = this; + + try { + job.validate(); + } catch (err) { + return callback(err); + } + + self.jobBackend.update(job.data, function (err, data) { + if (err) { + return callback(err); + } + + try { + job = JobFactory.create(data); + } catch (err) { + return callback(err); + } + + callback(null, job); + }); +}; + +JobService.prototype.cancel = function (job_id, callback) { + var self = this; + + self.get(job_id, function (err, job) { + if (err) { + return callback(err); + } + + var isPending = job.isPending(); + + try { + job.setStatus(jobStatus.CANCELLED); + } catch (err) { + return callback(err); + } + + if (isPending) { + return self.save(job, callback); + } + + self.jobCanceller.cancel(job, function (err) { + if (err) { + return callback(err); + } + + self.save(job, callback); + }); + }); +}; + +JobService.prototype.drain = function (job_id, callback) { + var self = this; + + self.get(job_id, function (err, job) { + if (err) { + return callback(err); + } + + self.jobCanceller.cancel(job, function (err) { + if (err) { + debug('There was an error while draining job %s, %s ', job_id, err); + return callback(err); + } + + try { + job.setStatus(jobStatus.PENDING); + } catch (err) { + return callback(err); + } + + self.jobBackend.update(job.data, callback); + }); + }); +}; + +JobService.prototype.addWorkInProgressJob = function (user, jobId, callback) { + this.jobBackend.addWorkInProgressJob(user, jobId, callback); +}; + +JobService.prototype.clearWorkInProgressJob = function (user, jobId, callback) { + this.jobBackend.clearWorkInProgressJob(user, jobId, callback); +}; + +JobService.prototype.listWorkInProgressJobs = function (callback) { + this.jobBackend.listWorkInProgressJobs(callback); +}; diff --git a/batch/job_status.js b/batch/job_status.js new file mode 100644 index 0000000..ef5ad17 --- /dev/null +++ b/batch/job_status.js @@ -0,0 +1,23 @@ +'use strict'; + +var JOB_STATUS_ENUM = { + PENDING: 'pending', + RUNNING: 'running', + DONE: 'done', + CANCELLED: 'cancelled', + FAILED: 'failed', + SKIPPED: 'skipped', + UNKNOWN: 'unknown' +}; + +module.exports = JOB_STATUS_ENUM; + +var finalStatus = [ + JOB_STATUS_ENUM.CANCELLED, + JOB_STATUS_ENUM.DONE, + JOB_STATUS_ENUM.FAILED, + JOB_STATUS_ENUM.UNKNOWN +]; +module.exports.isFinal = function(status) { + return finalStatus.indexOf(status) !== -1; +}; diff --git a/batch/leader/locker.js b/batch/leader/locker.js new file mode 100644 index 0000000..463e74c --- /dev/null +++ b/batch/leader/locker.js @@ -0,0 +1,74 @@ +'use strict'; + +var RedisDistlockLocker = require('./provider/redis-distlock'); +var debug = require('../util/debug')('leader-locker'); +var EventEmitter = require('events').EventEmitter; +var util = require('util'); + +var LOCK = { + TTL: 5000 +}; + +function Locker(locker, ttl) { + EventEmitter.call(this); + this.locker = locker; + this.ttl = (Number.isFinite(ttl) && ttl > 0) ? ttl : LOCK.TTL; + this.renewInterval = this.ttl / 5; + this.intervalIds = {}; +} +util.inherits(Locker, EventEmitter); + +module.exports = Locker; + +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) { + if (!err) { + self.startRenewal(resource); + } + return callback(err, lock); + }); +}; + +Locker.prototype.unlock = function(resource, callback) { + var self = this; + debug('Locker.unlock(%s)', resource); + this.locker.unlock(resource, function(err) { + self.stopRenewal(resource); + return callback(err); + }); +}; + +Locker.prototype.startRenewal = function(resource) { + var self = this; + if (!this.intervalIds.hasOwnProperty(resource)) { + this.intervalIds[resource] = setInterval(function() { + debug('Trying to extend lock resource=%s', resource); + self.locker.lock(resource, self.ttl, function(err, _lock) { + if (err) { + self.emit('error', err, resource); + return self.stopRenewal(resource); + } + if (_lock) { + debug('Extended lock resource=%s', resource); + } + }); + }, this.renewInterval); + } +}; + +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) { + if (type !== 'redis-distlock') { + throw new Error('Invalid type Locker type. Valid types are: "redis-distlock"'); + } + var locker = new RedisDistlockLocker(config.pool); + return new Locker(locker, config.ttl); +}; diff --git a/batch/leader/provider/redis-distlock.js b/batch/leader/provider/redis-distlock.js new file mode 100644 index 0000000..01e944f --- /dev/null +++ b/batch/leader/provider/redis-distlock.js @@ -0,0 +1,111 @@ +'use strict'; + +var REDIS_DISTLOCK = { + DB: 5, + PREFIX: 'batch:locks:' +}; + +var Redlock = require('redlock'); +var debug = require('../../util/debug')('leader:redis-distlock'); + +function RedisDistlockLocker(redisPool) { + this.pool = redisPool; + this.redlock = new Redlock([{}], { + // see http://redis.io/topics/distlock + driftFactor: 0.01, // time in ms + // the max number of times Redlock will attempt to lock a resource before failing + retryCount: 3, + // the time in ms between attempts + retryDelay: 100 + }); + this._locks = {}; +} + +module.exports = RedisDistlockLocker; +module.exports.type = 'redis-distlock'; + +function resourceId(resource) { + return REDIS_DISTLOCK.PREFIX + resource; +} + +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) { + if (err) { + return callback(err); + } + self._setLock(lockId, _lock); + return callback(null, _lock); + } + if (lock) { + return this._tryExtend(lock, ttl, function(err, _lock) { + if (err) { + return self._tryAcquire(lockId, ttl, acquireCallback); + } + + return callback(null, _lock); + }); + } else { + return this._tryAcquire(lockId, ttl, acquireCallback); + } +}; + +RedisDistlockLocker.prototype.unlock = function(resource, callback) { + var self = this; + var lock = this._getLock(resourceId(resource)); + if (lock) { + this.pool.acquire(REDIS_DISTLOCK.DB, function (err, client) { + if (err) { + return callback(err); + } + self.redlock.servers = [client]; + return self.redlock.unlock(lock, function(err) { + self.pool.release(REDIS_DISTLOCK.DB, client); + return callback(err); + }); + }); + } +}; + +RedisDistlockLocker.prototype._getLock = function(resource) { + if (this._locks.hasOwnProperty(resource)) { + return this._locks[resource]; + } + return null; +}; + +RedisDistlockLocker.prototype._setLock = function(resource, lock) { + this._locks[resource] = lock; +}; + +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) { + self.pool.release(REDIS_DISTLOCK.DB, client); + return callback(err, _lock); + }); + }); +}; + +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) { + self.pool.release(REDIS_DISTLOCK.DB, client); + return callback(err, _lock); + }); + }); +}; diff --git a/batch/maintenance/host-user-queue-mover.js b/batch/maintenance/host-user-queue-mover.js new file mode 100644 index 0000000..c2cec1d --- /dev/null +++ b/batch/maintenance/host-user-queue-mover.js @@ -0,0 +1,144 @@ +'use strict'; + +var asyncQ = require('queue-async'); +var debug = require('../util/debug')('queue-mover'); +var forever = require('../util/forever'); + +var QUEUE = { + OLD: { + DB: 5, + PREFIX: 'batch:queues:' // host + }, + NEW: { + DB: 5, + PREFIX: 'batch:queue:' // user + } +}; + +function HostUserQueueMover(jobQueue, jobService, locker, redisPool) { + this.jobQueue = jobQueue; + this.jobService = jobService; + this.locker = locker; + this.pool = redisPool; +} + +module.exports = HostUserQueueMover; + +HostUserQueueMover.prototype.moveOldJobs = function(callback) { + var self = this; + this.getOldQueues(function(err, hosts) { + var async = asyncQ(4); + hosts.forEach(function(host) { + async.defer(self.moveOldQueueJobs.bind(self), host); + }); + + async.awaitAll(function (err) { + if (err) { + debug('Something went wrong moving jobs', err); + } else { + debug('Finished moving all jobs'); + } + + 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) { + // we didn't get the lock for the host + if (err) { + debug('Could not lock host=%s. Reason: %s', host, err.message); + return next(err); + } + debug('Locked host=%s', host); + self.processNextJob(host, next); + }); + }, + function (err) { + if (err) { + debug(err.name === 'EmptyQueue' ? err.message : err); + } + self.locker.unlock(host, 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) { + if (err) { + return callback(err); + } + + 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) { + var emptyQueueError = new Error('Empty queue'); + emptyQueueError.name = 'EmptyQueue'; + return callback(emptyQueueError); + } + 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 callback(); + }); + } + return callback(); + }); + }); + }); +}; + +HostUserQueueMover.prototype.getOldQueues = function(callback) { + var initialCursor = ['0']; + var hosts = {}; + var self = this; + + this.pool.acquire(QUEUE.OLD.DB, function(err, client) { + if (err) { + return callback(err); + } + self._getOldQueues(client, initialCursor, hosts, function(err, hosts) { + self.pool.release(QUEUE.DB, client); + return callback(err, Object.keys(hosts)); + }); + }); +}; + +HostUserQueueMover.prototype._getOldQueues = function (client, cursor, hosts, callback) { + var self = this; + var redisParams = [cursor[0], 'MATCH', QUEUE.OLD.PREFIX + '*']; + + client.scan(redisParams, function(err, currentCursor) { + if (err) { + return callback(null, hosts); + } + + var queues = currentCursor[1]; + if (queues) { + queues.forEach(function (queue) { + var user = queue.substr(QUEUE.OLD.PREFIX.length); + hosts[user] = true; + }); + } + + var hasMore = currentCursor[0] !== '0'; + if (!hasMore) { + return callback(null, hosts); + } + + self._getOldQueues(client, currentCursor, hosts, callback); + }); +}; diff --git a/batch/models/job_base.js b/batch/models/job_base.js new file mode 100644 index 0000000..9eda131 --- /dev/null +++ b/batch/models/job_base.js @@ -0,0 +1,122 @@ +'use strict'; + +var util = require('util'); +var uuid = require('node-uuid'); +var JobStateMachine = require('./job_state_machine'); +var jobStatus = require('../job_status'); +var mandatoryProperties = [ + 'job_id', + 'status', + 'query', + 'created_at', + 'updated_at', + 'host', + 'user' +]; + +function JobBase(data) { + JobStateMachine.call(this); + + var now = new Date().toISOString(); + + this.data = data; + + if (!this.data.job_id) { + this.data.job_id = uuid.v4(); + } + + if (!this.data.created_at) { + this.data.created_at = now; + } + + if (!this.data.updated_at) { + this.data.updated_at = now; + } +} +util.inherits(JobBase, JobStateMachine); + +module.exports = JobBase; + +// should be implemented by childs +JobBase.prototype.getNextQuery = function () { + throw new Error('Unimplemented method'); +}; + +JobBase.prototype.hasNextQuery = function () { + return !!this.getNextQuery(); +}; + +JobBase.prototype.isPending = function () { + return this.data.status === jobStatus.PENDING; +}; + +JobBase.prototype.isRunning = function () { + return this.data.status === jobStatus.RUNNING; +}; + +JobBase.prototype.isDone = function () { + return this.data.status === jobStatus.DONE; +}; + +JobBase.prototype.isCancelled = function () { + return this.data.status === jobStatus.CANCELLED; +}; + +JobBase.prototype.isFailed = function () { + return this.data.status === jobStatus.FAILED; +}; + +JobBase.prototype.isUnknown = function () { + return this.data.status === jobStatus.UNKNOWN; +}; + +JobBase.prototype.setQuery = function (query) { + var now = new Date().toISOString(); + + if (!this.isPending()) { + throw new Error('Job is not pending, it cannot be updated'); + } + + this.data.updated_at = now; + this.data.query = query; +}; + +JobBase.prototype.setStatus = function (finalStatus, errorMesssage) { + var now = new Date().toISOString(); + var initialStatus = this.data.status; + var isValid = this.isValidTransition(initialStatus, finalStatus); + + if (!isValid) { + throw new Error('Cannot set status from ' + initialStatus + ' to ' + finalStatus); + } + + this.data.updated_at = now; + this.data.status = finalStatus; + if (finalStatus === jobStatus.FAILED && errorMesssage) { + this.data.failed_reason = errorMesssage; + } +}; + +JobBase.prototype.validate = function () { + for (var i = 0; i < mandatoryProperties.length; i++) { + if (!this.data[mandatoryProperties[i]]) { + throw new Error('property "' + mandatoryProperties[i] + '" is mandatory'); + } + } +}; + +JobBase.prototype.serialize = function () { + var data = JSON.parse(JSON.stringify(this.data)); + + delete data.host; + delete data.dbuser; + delete data.port; + delete data.dbname; + delete data.pass; + + return data; +}; + +JobBase.prototype.log = function(/*logger*/) { + return false; +}; diff --git a/batch/models/job_factory.js b/batch/models/job_factory.js new file mode 100644 index 0000000..7994314 --- /dev/null +++ b/batch/models/job_factory.js @@ -0,0 +1,26 @@ +'use strict'; + +var JobSimple = require('./job_simple'); +var JobMultiple = require('./job_multiple'); +var JobFallback = require('./job_fallback'); + +var Models = [ JobSimple, JobMultiple, JobFallback ]; + +function JobFactory() { +} + +module.exports = JobFactory; + +JobFactory.create = function (data) { + if (!data.query) { + throw new Error('You must indicate a valid SQL'); + } + + for (var i = 0; i < Models.length; i++) { + if (Models[i].is(data.query)) { + return new Models[i](data); + } + } + + throw new Error('there is no job class for the provided query'); +}; diff --git a/batch/models/job_fallback.js b/batch/models/job_fallback.js new file mode 100644 index 0000000..9caa0d1 --- /dev/null +++ b/batch/models/job_fallback.js @@ -0,0 +1,279 @@ +'use strict'; + +var util = require('util'); +var JobBase = require('./job_base'); +var JobStatus = require('../job_status'); +var QueryFallback = require('./query/query_fallback'); +var MainFallback = require('./query/main_fallback'); +var QueryFactory = require('./query/query_factory'); + +function JobFallback(jobDefinition) { + JobBase.call(this, jobDefinition); + + this.init(); + + this.queries = []; + for (var i = 0; i < this.data.query.query.length; i++) { + this.queries[i] = QueryFactory.create(this.data, i); + } + + if (MainFallback.is(this.data)) { + this.fallback = new MainFallback(); + } +} +util.inherits(JobFallback, JobBase); + +module.exports = JobFallback; + +// 1. from user: { +// query: { +// query: [{ +// query: 'select ...', +// onsuccess: 'select ..' +// }], +// onerror: 'select ...' +// } +// } +// +// 2. from redis: { +// status: 'pending', +// fallback_status: 'pending' +// query: { +// query: [{ +// query: 'select ...', +// onsuccess: 'select ..' +// status: 'pending', +// fallback_status: 'pending', +// }], +// onerror: 'select ...' +// } +// } + +JobFallback.is = function (query) { + if (!query.query) { + return false; + } + + if (!Array.isArray(query.query)) { + return false; + } + + for (var i = 0; i < query.query.length; i++) { + if (!QueryFallback.is(query.query[i])) { + return false; + } + } + + return true; +}; + +JobFallback.prototype.init = function () { + for (var i = 0; i < this.data.query.query.length; i++) { + if (shouldInitStatus(this.data.query.query[i])){ + this.data.query.query[i].status = JobStatus.PENDING; + } + if (shouldInitQueryFallbackStatus(this.data.query.query[i])) { + this.data.query.query[i].fallback_status = JobStatus.PENDING; + } + } + + if (shouldInitStatus(this.data)) { + this.data.status = JobStatus.PENDING; + } + + if (shouldInitFallbackStatus(this.data)) { + this.data.fallback_status = JobStatus.PENDING; + } +}; + +function shouldInitStatus(jobOrQuery) { + return !jobOrQuery.status; +} + +function shouldInitQueryFallbackStatus(query) { + return (query.onsuccess || query.onerror) && !query.fallback_status; +} + +function shouldInitFallbackStatus(job) { + return (job.query.onsuccess || job.query.onerror) && !job.fallback_status; +} + +JobFallback.prototype.getNextQueryFromQueries = function () { + for (var i = 0; i < this.queries.length; i++) { + if (this.queries[i].hasNextQuery(this.data)) { + return this.queries[i].getNextQuery(this.data); + } + } +}; + +JobFallback.prototype.hasNextQueryFromQueries = function () { + return !!this.getNextQueryFromQueries(); +}; + +JobFallback.prototype.getNextQueryFromFallback = function () { + if (this.fallback && this.fallback.hasNextQuery(this.data)) { + + return this.fallback.getNextQuery(this.data); + } +}; + +JobFallback.prototype.getNextQuery = function () { + var query = this.getNextQueryFromQueries(); + + if (!query) { + query = this.getNextQueryFromFallback(); + } + + return query; +}; + +JobFallback.prototype.setQuery = function (query) { + if (!JobFallback.is(query)) { + throw new Error('You must indicate a valid SQL'); + } + + JobFallback.super_.prototype.setQuery.call(this, query); +}; + +JobFallback.prototype.setStatus = function (status, errorMesssage) { + var now = new Date().toISOString(); + + var hasChanged = this.setQueryStatus(status, this.data, errorMesssage); + hasChanged = this.setJobStatus(status, this.data, hasChanged, errorMesssage); + hasChanged = this.setFallbackStatus(status, this.data, hasChanged); + + if (!hasChanged.isValid) { + throw new Error('Cannot set status to ' + status); + } + + this.data.updated_at = now; +}; + +JobFallback.prototype.setQueryStatus = function (status, job, errorMesssage) { + return this.queries.reduce(function (hasChanged, query) { + var result = query.setStatus(status, this.data, hasChanged, errorMesssage); + return result.isValid ? result : hasChanged; + }.bind(this), { isValid: false, appliedToFallback: false }); +}; + +JobFallback.prototype.setJobStatus = function (status, job, hasChanged, errorMesssage) { + var result = { + isValid: false, + appliedToFallback: false + }; + + status = this.shiftStatus(status, hasChanged); + + result.isValid = this.isValidTransition(job.status, status); + if (result.isValid) { + job.status = status; + if (status === JobStatus.FAILED && errorMesssage && !hasChanged.appliedToFallback) { + job.failed_reason = errorMesssage; + } + } + + return result.isValid ? result : hasChanged; +}; + +JobFallback.prototype.setFallbackStatus = function (status, job, hasChanged) { + var result = hasChanged; + + if (this.fallback && !this.hasNextQueryFromQueries()) { + result = this.fallback.setStatus(status, job, hasChanged); + } + + return result.isValid ? result : hasChanged; +}; + +JobFallback.prototype.shiftStatus = function (status, hasChanged) { + // jshint maxcomplexity: 7 + if (hasChanged.appliedToFallback) { + if (!this.hasNextQueryFromQueries() && (status === JobStatus.DONE || status === JobStatus.FAILED)) { + status = this.getLastFinishedStatus(); + } else if (status === JobStatus.DONE || status === JobStatus.FAILED){ + status = JobStatus.PENDING; + } + } else if (this.hasNextQueryFromQueries() && status !== JobStatus.RUNNING) { + status = JobStatus.PENDING; + } + + return status; +}; + +JobFallback.prototype.getLastFinishedStatus = function () { + return this.queries.reduce(function (lastFinished, query) { + var status = query.getStatus(this.data); + return this.isFinalStatus(status) ? status : lastFinished; + }.bind(this), JobStatus.DONE); +}; + +JobFallback.prototype.log = function(logger) { + if (!isFinished(this)) { + return false; + } + + var queries = this.data.query.query; + + for (var i = 0; i < queries.length; i++) { + var query = queries[i]; + + var logEntry = { + created: this.data.created_at, + waiting: elapsedTime(this.data.created_at, query.started_at), + time: query.started_at, + endtime: query.ended_at, + username: this.data.user, + dbhost: this.data.host, + job: this.data.job_id, + status: query.status, + elapsed: elapsedTime(query.started_at, query.ended_at) + }; + + var queryId = query.id; + + var tag = 'query'; + if (queryId) { + logEntry.query_id = queryId; + + var node = parseQueryId(queryId); + if (node) { + logEntry.analysis = node.analysisId; + logEntry.node = node.nodeId; + logEntry.type = node.nodeType; + tag = 'analysis'; + } + } + + logger.info(logEntry, tag); + } + + return true; +}; + +function isFinished (job) { + return JobStatus.isFinal(job.data.status) && + (!job.data.fallback_status || JobStatus.isFinal(job.data.fallback_status)); +} + +function parseQueryId (queryId) { + var data = queryId.split(':'); + + if (data.length === 3) { + return { + analysisId: data[0], + nodeId: data[1], + nodeType: data[2] + }; + } + return null; +} + +function elapsedTime (started_at, ended_at) { + if (!started_at || !ended_at) { + return; + } + + var start = new Date(started_at); + var end = new Date(ended_at); + return end.getTime() - start.getTime(); +} diff --git a/batch/models/job_multiple.js b/batch/models/job_multiple.js new file mode 100644 index 0000000..85cf1d8 --- /dev/null +++ b/batch/models/job_multiple.js @@ -0,0 +1,91 @@ +'use strict'; + +var util = require('util'); +var JobBase = require('./job_base'); +var jobStatus = require('../job_status'); + +function JobMultiple(jobDefinition) { + JobBase.call(this, jobDefinition); + + this.init(); +} +util.inherits(JobMultiple, JobBase); + +module.exports = JobMultiple; + +JobMultiple.is = function (query) { + if (!Array.isArray(query)) { + return false; + } + + // 1. From user: ['select * from ...', 'select * from ...'] + // 2. From redis: [ { query: 'select * from ...', status: 'pending' }, + // { query: 'select * from ...', status: 'pending' } ] + for (var i = 0; i < query.length; i++) { + if (typeof query[i] !== 'string') { + if (typeof query[i].query !== 'string') { + return false; + } + } + } + + return true; +}; + +JobMultiple.prototype.init = function () { + + if (!this.data.status) { + this.data.status = jobStatus.PENDING; + } + + for (var i = 0; i < this.data.query.length; i++) { + if (!this.data.query[i].query && !this.data.query[i].status) { + this.data.query[i] = { + query: this.data.query[i], + status: jobStatus.PENDING + }; + } + } +}; + +JobMultiple.prototype.getNextQuery = function () { + for (var i = 0; i < this.data.query.length; i++) { + if (this.data.query[i].status === jobStatus.PENDING) { + return this.data.query[i].query; + } + } +}; + +JobMultiple.prototype.setQuery = function (query) { + if (!JobMultiple.is(query)) { + throw new Error('You must indicate a valid SQL'); + } + + JobMultiple.super_.prototype.setQuery.call(this, query); +}; + +JobMultiple.prototype.setStatus = function (finalStatus, errorMesssage) { + var initialStatus = this.data.status; + // if transition is to "done" and there are more queries to run + // then job status must be "pending" instead of "done" + // else job status transition to done (if "running") + if (finalStatus === jobStatus.DONE && this.hasNextQuery()) { + JobMultiple.super_.prototype.setStatus.call(this, jobStatus.PENDING); + } else { + JobMultiple.super_.prototype.setStatus.call(this, finalStatus, errorMesssage); + } + + for (var i = 0; i < this.data.query.length; i++) { + var isValid = JobMultiple.super_.prototype.isValidTransition(this.data.query[i].status, finalStatus); + + if (isValid) { + this.data.query[i].status = finalStatus; + if (finalStatus === jobStatus.FAILED && errorMesssage) { + this.data.query[i].failed_reason = errorMesssage; + } + return; + } + } + + throw new Error('Cannot set status from ' + initialStatus + ' to ' + finalStatus); +}; diff --git a/batch/models/job_simple.js b/batch/models/job_simple.js new file mode 100644 index 0000000..ad982c3 --- /dev/null +++ b/batch/models/job_simple.js @@ -0,0 +1,34 @@ +'use strict'; + +var util = require('util'); +var JobBase = require('./job_base'); +var jobStatus = require('../job_status'); + +function JobSimple(jobDefinition) { + JobBase.call(this, jobDefinition); + + if (!this.data.status) { + this.data.status = jobStatus.PENDING; + } +} +util.inherits(JobSimple, JobBase); + +module.exports = JobSimple; + +JobSimple.is = function (query) { + return typeof query === 'string'; +}; + +JobSimple.prototype.getNextQuery = function () { + if (this.isPending()) { + return this.data.query; + } +}; + +JobSimple.prototype.setQuery = function (query) { + if (!JobSimple.is(query)) { + throw new Error('You must indicate a valid SQL'); + } + + JobSimple.super_.prototype.setQuery.call(this, query); +}; diff --git a/batch/models/job_state_machine.js b/batch/models/job_state_machine.js new file mode 100644 index 0000000..f476b8a --- /dev/null +++ b/batch/models/job_state_machine.js @@ -0,0 +1,39 @@ +'use strict'; + +var assert = require('assert'); +var JobStatus = require('../job_status'); +var validStatusTransitions = [ + [JobStatus.PENDING, JobStatus.RUNNING], + [JobStatus.PENDING, JobStatus.CANCELLED], + [JobStatus.PENDING, JobStatus.UNKNOWN], + [JobStatus.PENDING, JobStatus.SKIPPED], + [JobStatus.RUNNING, JobStatus.DONE], + [JobStatus.RUNNING, JobStatus.FAILED], + [JobStatus.RUNNING, JobStatus.CANCELLED], + [JobStatus.RUNNING, JobStatus.PENDING], + [JobStatus.RUNNING, JobStatus.UNKNOWN] +]; + +function JobStateMachine () { +} + +module.exports = JobStateMachine; + +JobStateMachine.prototype.isValidTransition = function (initialStatus, finalStatus) { + var transition = [ initialStatus, finalStatus ]; + + for (var i = 0; i < validStatusTransitions.length; i++) { + try { + assert.deepEqual(transition, validStatusTransitions[i]); + return true; + } catch (e) { + continue; + } + } + + return false; +}; + +JobStateMachine.prototype.isFinalStatus = function (status) { + return JobStatus.isFinal(status); +}; diff --git a/batch/models/query/fallback.js b/batch/models/query/fallback.js new file mode 100644 index 0000000..1ce5f22 --- /dev/null +++ b/batch/models/query/fallback.js @@ -0,0 +1,78 @@ +'use strict'; + +var util = require('util'); +var QueryBase = require('./query_base'); +var jobStatus = require('../../job_status'); + +function Fallback(index) { + QueryBase.call(this, index); +} +util.inherits(Fallback, QueryBase); + +module.exports = Fallback; + +Fallback.is = function (query) { + if (query.onsuccess || query.onerror) { + return true; + } + return false; +}; + +Fallback.prototype.getNextQuery = function (job) { + if (this.hasOnSuccess(job)) { + return this.getOnSuccess(job); + } + if (this.hasOnError(job)) { + return this.getOnError(job); + } +}; + +Fallback.prototype.getOnSuccess = function (job) { + if (job.query.query[this.index].status === jobStatus.DONE && + job.query.query[this.index].fallback_status === jobStatus.PENDING) { + var onsuccessQuery = job.query.query[this.index].onsuccess; + if (onsuccessQuery) { + onsuccessQuery = onsuccessQuery.replace(/<%=\s*job_id\s*%>/g, job.job_id); + } + return onsuccessQuery; + } +}; + +Fallback.prototype.hasOnSuccess = function (job) { + return !!this.getOnSuccess(job); +}; + +Fallback.prototype.getOnError = function (job) { + if (job.query.query[this.index].status === jobStatus.FAILED && + job.query.query[this.index].fallback_status === jobStatus.PENDING) { + var onerrorQuery = job.query.query[this.index].onerror; + if (onerrorQuery) { + onerrorQuery = onerrorQuery.replace(/<%=\s*job_id\s*%>/g, job.job_id); + onerrorQuery = onerrorQuery.replace(/<%=\s*error_message\s*%>/g, job.query.query[this.index].failed_reason); + } + return onerrorQuery; + } +}; + +Fallback.prototype.hasOnError = function (job) { + return !!this.getOnError(job); +}; + +Fallback.prototype.setStatus = function (status, job, errorMessage) { + var isValid = false; + + isValid = this.isValidTransition(job.query.query[this.index].fallback_status, status); + + if (isValid) { + job.query.query[this.index].fallback_status = status; + if (status === jobStatus.FAILED && errorMessage) { + job.query.query[this.index].failed_reason = errorMessage; + } + } + + return isValid; +}; + +Fallback.prototype.getStatus = function (job) { + return job.query.query[this.index].fallback_status; +}; diff --git a/batch/models/query/main_fallback.js b/batch/models/query/main_fallback.js new file mode 100644 index 0000000..7c52194 --- /dev/null +++ b/batch/models/query/main_fallback.js @@ -0,0 +1,74 @@ +'use strict'; + +var util = require('util'); +var QueryBase = require('./query_base'); +var jobStatus = require('../../job_status'); + +function MainFallback() { + QueryBase.call(this); +} +util.inherits(MainFallback, QueryBase); + +module.exports = MainFallback; + +MainFallback.is = function (job) { + if (job.query.onsuccess || job.query.onerror) { + return true; + } + return false; +}; + +MainFallback.prototype.getNextQuery = function (job) { + if (this.hasOnSuccess(job)) { + return this.getOnSuccess(job); + } + + if (this.hasOnError(job)) { + return this.getOnError(job); + } +}; + +MainFallback.prototype.getOnSuccess = function (job) { + if (job.status === jobStatus.DONE && job.fallback_status === jobStatus.PENDING) { + return job.query.onsuccess; + } +}; + +MainFallback.prototype.hasOnSuccess = function (job) { + return !!this.getOnSuccess(job); +}; + +MainFallback.prototype.getOnError = function (job) { + if (job.status === jobStatus.FAILED && job.fallback_status === jobStatus.PENDING) { + return job.query.onerror; + } +}; + +MainFallback.prototype.hasOnError = function (job) { + return !!this.getOnError(job); +}; + +MainFallback.prototype.setStatus = function (status, job, previous) { + var isValid = false; + var appliedToFallback = false; + + if (previous.isValid && !previous.appliedToFallback) { + if (this.isFinalStatus(status) && !this.hasNextQuery(job)) { + isValid = this.isValidTransition(job.fallback_status, jobStatus.SKIPPED); + + if (isValid) { + job.fallback_status = jobStatus.SKIPPED; + appliedToFallback = true; + } + } + } else if (!previous.isValid) { + isValid = this.isValidTransition(job.fallback_status, status); + + if (isValid) { + job.fallback_status = status; + appliedToFallback = true; + } + } + + return { isValid: isValid, appliedToFallback: appliedToFallback }; +}; diff --git a/batch/models/query/query.js b/batch/models/query/query.js new file mode 100644 index 0000000..6970ae9 --- /dev/null +++ b/batch/models/query/query.js @@ -0,0 +1,57 @@ +'use strict'; + +var util = require('util'); +var QueryBase = require('./query_base'); +var jobStatus = require('../../job_status'); + +function Query(index) { + QueryBase.call(this, index); +} +util.inherits(Query, QueryBase); + +module.exports = Query; + +Query.is = function (query) { + if (query.query && typeof query.query === 'string') { + return true; + } + + return false; +}; + +Query.prototype.getNextQuery = function (job) { + if (job.query.query[this.index].status === jobStatus.PENDING) { + var query = { + query: job.query.query[this.index].query + }; + if (Number.isFinite(job.query.query[this.index].timeout)) { + query.timeout = job.query.query[this.index].timeout; + } + return query; + } +}; + +Query.prototype.setStatus = function (status, job, errorMesssage) { + var isValid = false; + + isValid = this.isValidTransition(job.query.query[this.index].status, status); + + if (isValid) { + job.query.query[this.index].status = status; + if (status === jobStatus.RUNNING) { + job.query.query[this.index].started_at = new Date().toISOString(); + } + if (this.isFinalStatus(status)) { + job.query.query[this.index].ended_at = new Date().toISOString(); + } + if (status === jobStatus.FAILED && errorMesssage) { + job.query.query[this.index].failed_reason = errorMesssage; + } + } + + return isValid; +}; + +Query.prototype.getStatus = function (job) { + return job.query.query[this.index].status; +}; diff --git a/batch/models/query/query_base.js b/batch/models/query/query_base.js new file mode 100644 index 0000000..737e1bf --- /dev/null +++ b/batch/models/query/query_base.js @@ -0,0 +1,31 @@ +'use strict'; + +var util = require('util'); +var JobStateMachine = require('../job_state_machine'); + +function QueryBase(index) { + JobStateMachine.call(this); + + this.index = index; +} +util.inherits(QueryBase, JobStateMachine); + +module.exports = QueryBase; + +// should be implemented +QueryBase.prototype.setStatus = function () { + throw new Error('Unimplemented method'); +}; + +// should be implemented +QueryBase.prototype.getNextQuery = function () { + throw new Error('Unimplemented method'); +}; + +QueryBase.prototype.hasNextQuery = function (job) { + return !!this.getNextQuery(job); +}; + +QueryBase.prototype.getStatus = function () { + throw new Error('Unimplemented method'); +}; diff --git a/batch/models/query/query_factory.js b/batch/models/query/query_factory.js new file mode 100644 index 0000000..c33534e --- /dev/null +++ b/batch/models/query/query_factory.js @@ -0,0 +1,16 @@ +'use strict'; + +var QueryFallback = require('./query_fallback'); + +function QueryFactory() { +} + +module.exports = QueryFactory; + +QueryFactory.create = function (job, index) { + if (QueryFallback.is(job.query.query[index])) { + return new QueryFallback(job, index); + } + + throw new Error('there is no query class for the provided query'); +}; diff --git a/batch/models/query/query_fallback.js b/batch/models/query/query_fallback.js new file mode 100644 index 0000000..cb0579a --- /dev/null +++ b/batch/models/query/query_fallback.js @@ -0,0 +1,75 @@ +'use strict'; + +var util = require('util'); +var QueryBase = require('./query_base'); +var Query = require('./query'); +var Fallback = require('./fallback'); +var jobStatus = require('../../job_status'); + +function QueryFallback(job, index) { + QueryBase.call(this, index); + + this.init(job, index); +} + +util.inherits(QueryFallback, QueryBase); + +QueryFallback.is = function (query) { + if (Query.is(query)) { + return true; + } + return false; +}; + +QueryFallback.prototype.init = function (job, index) { + this.query = new Query(index); + + if (Fallback.is(job.query.query[index])) { + this.fallback = new Fallback(index); + } +}; + +QueryFallback.prototype.getNextQuery = function (job) { + if (this.query.hasNextQuery(job)) { + return this.query.getNextQuery(job); + } + + if (this.fallback && this.fallback.hasNextQuery(job)) { + return this.fallback.getNextQuery(job); + } +}; + +QueryFallback.prototype.setStatus = function (status, job, previous, errorMesssage) { + // jshint maxcomplexity: 9 + var isValid = false; + var appliedToFallback = false; + + if (previous.isValid && !previous.appliedToFallback) { + if (status === jobStatus.FAILED || status === jobStatus.CANCELLED) { + this.query.setStatus(jobStatus.SKIPPED, job, errorMesssage); + + if (this.fallback) { + this.fallback.setStatus(jobStatus.SKIPPED, job); + } + } + } else if (!previous.isValid) { + isValid = this.query.setStatus(status, job, errorMesssage); + + if (this.fallback) { + if (!isValid) { + isValid = this.fallback.setStatus(status, job, errorMesssage); + appliedToFallback = true; + } else if (isValid && this.isFinalStatus(status) && !this.fallback.hasNextQuery(job)) { + this.fallback.setStatus(jobStatus.SKIPPED, job); + } + } + } + + return { isValid: isValid, appliedToFallback: appliedToFallback }; +}; + +QueryFallback.prototype.getStatus = function (job) { + return this.query.getStatus(job); +}; + +module.exports = QueryFallback; diff --git a/batch/pubsub/channel.js b/batch/pubsub/channel.js new file mode 100644 index 0000000..f719c84 --- /dev/null +++ b/batch/pubsub/channel.js @@ -0,0 +1,4 @@ +module.exports = { + DB: 0, + NAME: 'batch:users' +}; diff --git a/batch/pubsub/job-publisher.js b/batch/pubsub/job-publisher.js new file mode 100644 index 0000000..4230a71 --- /dev/null +++ b/batch/pubsub/job-publisher.js @@ -0,0 +1,31 @@ +'use strict'; + +var Channel = require('./channel'); +var debug = require('./../util/debug')('pubsub:publisher'); +var error = require('./../util/debug')('pubsub:publisher:error'); + +function JobPublisher(pool) { + this.pool = pool; +} + +JobPublisher.prototype.publish = function (user) { + var self = this; + + this.pool.acquire(Channel.DB, function (err, client) { + if (err) { + return error('Error adquiring redis client: ' + err.message); + } + + client.publish(Channel.NAME, user, function (err) { + self.pool.release(Channel.DB, client); + + if (err) { + return error('Error publishing to ' + Channel.NAME + ':' + user + ', ' + err.message); + } + + debug('publish to ' + Channel.NAME + ':' + user); + }); + }); +}; + +module.exports = JobPublisher; diff --git a/batch/pubsub/job-subscriber.js b/batch/pubsub/job-subscriber.js new file mode 100644 index 0000000..9c3d686 --- /dev/null +++ b/batch/pubsub/job-subscriber.js @@ -0,0 +1,54 @@ +'use strict'; + +var Channel = require('./channel'); +var debug = require('./../util/debug')('pubsub:subscriber'); +var error = require('./../util/debug')('pubsub:subscriber:error'); + +function JobSubscriber(pool) { + this.pool = pool; +} + +module.exports = JobSubscriber; + +JobSubscriber.prototype.subscribe = function (onJobHandler, callback) { + var self = this; + + self.pool.acquire(Channel.DB, function(err, client) { + if (err) { + if (callback) { + callback(err); + } + return error('Error adquiring redis client: ' + err.message); + } + + self.client = client; + client.removeAllListeners('message'); + client.unsubscribe(Channel.NAME); + client.subscribe(Channel.NAME); + + client.on('message', function (channel, user) { + debug('message received in channel=%s from user=%s', channel, user); + onJobHandler(user); + }); + + client.on('error', function () { + self.unsubscribe(); + self.pool.release(Channel.DB, client); + self.subscribe(onJobHandler); + }); + + if (callback) { + callback(); + } + }); +}; + +JobSubscriber.prototype.unsubscribe = function (callback) { + if (this.client && this.client.connected) { + this.client.unsubscribe(Channel.NAME, callback); + } else { + if (callback) { + return callback(null); + } + } +}; diff --git a/batch/query_runner.js b/batch/query_runner.js new file mode 100644 index 0000000..e82c55d --- /dev/null +++ b/batch/query_runner.js @@ -0,0 +1,54 @@ +'use strict'; + +var PSQL = require('cartodb-psql'); +var debug = require('./util/debug')('query-runner'); + +function QueryRunner(userDatabaseMetadataService) { + this.userDatabaseMetadataService = userDatabaseMetadataService; +} + +module.exports = QueryRunner; + +function hasDBParams (dbparams) { + return (dbparams.user && dbparams.host && dbparams.port && dbparams.dbname && dbparams.pass); +} + +QueryRunner.prototype.run = function (job_id, sql, user, timeout, dbparams, callback) { + if (hasDBParams(dbparams)) { + return this._run(dbparams, job_id, sql, timeout, callback); + } + + const dbConfigurationError = new Error('Batch Job DB misconfiguration'); + + return callback(dbConfigurationError); +}; + +QueryRunner.prototype._run = function (dbparams, job_id, sql, timeout, callback) { + var pg = new PSQL(dbparams); + + pg.query('SET statement_timeout=' + timeout, function (err) { + if(err) { + return callback(err); + } + + // mark query to allow to users cancel their queries + sql = '/* ' + job_id + ' */ ' + sql; + + debug('Running query [timeout=%d] %s', timeout, sql); + pg.eventedQuery(sql, function (err, query) { + if (err) { + return callback(err); + } + + query.on('error', callback); + + query.on('end', function (result) { + // only if result is present then query is done sucessfully otherwise an error has happened + // and it was handled by error listener + if (result) { + callback(null, result); + } + }); + }); + }); +}; diff --git a/batch/scheduler/capacity/fixed.js b/batch/scheduler/capacity/fixed.js new file mode 100644 index 0000000..aef83be --- /dev/null +++ b/batch/scheduler/capacity/fixed.js @@ -0,0 +1,11 @@ +'use strict'; + +function FixedCapacity(capacity) { + this.capacity = Math.max(1, capacity); +} + +module.exports = FixedCapacity; + +FixedCapacity.prototype.getCapacity = function(callback) { + return callback(null, this.capacity); +}; diff --git a/batch/scheduler/capacity/http-load.js b/batch/scheduler/capacity/http-load.js new file mode 100644 index 0000000..4fd6c1a --- /dev/null +++ b/batch/scheduler/capacity/http-load.js @@ -0,0 +1,32 @@ +'use strict'; + +var util = require('util'); +var debug = require('../../util/debug')('capacity-http-load'); +var HttpSimpleCapacity = require('./http-simple'); + +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) { + var capacity = 1; + + if (err) { + return callback(null, capacity); + } + + var cores = parseInt(values.cores, 10); + var relativeLoad = parseFloat(values.relative_load); + + capacity = Math.max(1, Math.floor(((1 - relativeLoad) * cores) - 1)); + + capacity = Number.isFinite(capacity) ? capacity : 1; + + debug('host=%s, capacity=%s', this.host, capacity); + return callback(null, capacity); + }.bind(this)); +}; diff --git a/batch/scheduler/capacity/http-simple.js b/batch/scheduler/capacity/http-simple.js new file mode 100644 index 0000000..fe73a7a --- /dev/null +++ b/batch/scheduler/capacity/http-simple.js @@ -0,0 +1,62 @@ +'use strict'; + +var request = require('request'); +var debug = require('../../util/debug')('capacity-http-simple'); + +function HttpSimpleCapacity(host, capacityEndpoint) { + this.host = host; + this.capacityEndpoint = capacityEndpoint; + + this.lastResponse = null; + this.lastResponseTime = 0; +} + +module.exports = HttpSimpleCapacity; + +HttpSimpleCapacity.prototype.getCapacity = function(callback) { + this.getResponse(function(err, values) { + var capacity = 1; + + if (err) { + return callback(null, capacity); + } + + var availableCores = parseInt(values.available_cores, 10); + + capacity = Math.max(availableCores, 1); + capacity = Number.isFinite(capacity) ? capacity : 1; + + debug('host=%s, capacity=%s', this.host, capacity); + return callback(null, capacity); + }.bind(this)); +}; + +HttpSimpleCapacity.prototype.getResponse = function(callback) { + var requestParams = { + method: 'POST', + url: this.capacityEndpoint, + timeout: 2000, + json: true + }; + debug('getCapacity(%s)', this.host); + + // throttle requests for 500 ms + var now = Date.now(); + if (this.lastResponse !== null && ((now - this.lastResponseTime) < 500)) { + return callback(null, this.lastResponse); + } + + request.post(requestParams, function(err, res, jsonRes) { + if (err) { + return callback(err); + } + if (jsonRes && jsonRes.retcode === 0) { + this.lastResponse = jsonRes.return_values || {}; + // We could go more aggressive by updating lastResponseTime on failures. + this.lastResponseTime = now; + + return callback(null, this.lastResponse); + } + return callback(new Error('Could not retrieve information from endpoint')); + }.bind(this)); +}; diff --git a/batch/scheduler/host-scheduler.js b/batch/scheduler/host-scheduler.js new file mode 100644 index 0000000..99526e8 --- /dev/null +++ b/batch/scheduler/host-scheduler.js @@ -0,0 +1,85 @@ +'use strict'; + +var _ = require('underscore'); +var debug = require('../util/debug')('host-scheduler'); +var Scheduler = require('./scheduler'); +var Locker = require('../leader/locker'); +var FixedCapacity = require('./capacity/fixed'); +var HttpSimpleCapacity = require('./capacity/http-simple'); +var HttpLoadCapacity = require('./capacity/http-load'); + +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) { + debug('[%s] Locker.error %s', this.name, err.message); + this.unlock(host); + }.bind(this)); + // host => Scheduler + this.schedulers = {}; +} + +module.exports = HostScheduler; + +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); + } + scheduler.add(user); + var wasRunning = scheduler.schedule(); + debug('[%s] Scheduler host=%s was running=%s', this.name, host, wasRunning); + return callback(err, wasRunning); + }.bind(this)); +}; + +HostScheduler.prototype.getCapacityProvider = function(host) { + var strategy = global.settings.batch_capacity_strategy; + + if (strategy === 'http-simple' || strategy === 'http-load') { + if (global.settings.batch_capacity_http_url_template) { + var endpoint = _.template(global.settings.batch_capacity_http_url_template, { dbhost: host }); + debug('Using strategy=%s capacity. Endpoint=%s', strategy, endpoint); + + if (strategy === 'http-simple') { + return new HttpSimpleCapacity(host, endpoint); + } + return new HttpLoadCapacity(host, endpoint); + } + } + + var fixedCapacity = global.settings.batch_capacity_fixed_amount || 4; + debug('Using strategy=fixed capacity=%d', fixedCapacity); + return new FixedCapacity(fixedCapacity); +}; + +HostScheduler.prototype.lock = function(host, callback) { + debug('[%s] lock(%s)', this.name, host); + var self = this; + 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); + } + + if (!self.schedulers.hasOwnProperty(host)) { + var scheduler = new Scheduler(self.getCapacityProvider(host), self.taskRunner); + scheduler.on('done', self.unlock.bind(self, host)); + self.schedulers[host] = scheduler; + } + + debug('[%s] Locked host=%s', self.name, host); + return callback(null, self.schedulers[host]); + }); +}; + +HostScheduler.prototype.unlock = function(host) { + debug('[%s] unlock(%s)', this.name, host); + if (this.schedulers.hasOwnProperty(host)) { + // TODO stop scheduler? + delete this.schedulers[host]; + } + this.locker.unlock(host, debug); +}; diff --git a/batch/scheduler/scheduler.js b/batch/scheduler/scheduler.js new file mode 100644 index 0000000..a8e18c9 --- /dev/null +++ b/batch/scheduler/scheduler.js @@ -0,0 +1,201 @@ +'use strict'; + +// Inspiration from: +// - https://www.kernel.org/doc/Documentation/scheduler/sched-design-CFS.txt +// - https://www.kernel.org/doc/Documentation/rbtree.txt +// - http://www.ibm.com/developerworks/linux/library/l-completely-fair-scheduler/ + +var util = require('util'); +var EventEmitter = require('events').EventEmitter; +var RBTree = require('bintrees').RBTree; + +var debug = require('../util/debug')('scheduler'); + +var forever = require('../util/forever'); + +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) { + // if the user is the same it's the same entity + if (taskEntityA.user === taskEntityB.user) { + return 0; + } + + // priority for entity with less executed jobs + if (taskEntityA.jobs !== taskEntityB.jobs) { + return taskEntityA.jobs - taskEntityB.jobs; + } + + // priority for oldest job + if (taskEntityA.createdAt !== taskEntityB.createdAt) { + return taskEntityA.createdAt - taskEntityB.createdAt; + } + + // we don't care if we arrive here + return -1; + }); +} +util.inherits(Scheduler, EventEmitter); + +module.exports = Scheduler; + +Scheduler.prototype.add = function(user) { + debug('add(%s)', user); + var taskEntity = this.users[user]; + if (taskEntity) { + if (taskEntity.status === STATUS.DONE) { + taskEntity.status = STATUS.PENDING; + this.tasksTree.insert(taskEntity); + this.emit('add'); + } + + return true; + } else { + taskEntity = new TaskEntity(user, this.tasks.length); + this.tasks.push(taskEntity); + this.users[user] = taskEntity; + this.tasksTree.insert(taskEntity); + + this.emit('add'); + + return false; + } +}; + +Scheduler.prototype.schedule = function() { + if (this.running) { + return true; + } + this.running = true; + + var self = this; + forever( + function (next) { + debug('Waiting for task'); + self.acquire(function(err, taskEntity) { + debug('Acquired user=%j', taskEntity); + + if (!taskEntity) { + return next(new Error('all users finished')); + } + + self.tasksTree.remove(taskEntity); + taskEntity.running(); + + debug('Running task for user=%s', taskEntity.user); + self.taskRunner.run(taskEntity.user, function(err, userQueueIsEmpty) { + debug('Run task=%j, done=%s', taskEntity, userQueueIsEmpty); + taskEntity.ran(userQueueIsEmpty); + self.release(err, taskEntity); + }); + + // try to acquire next user + // will block until capacity slot is available + next(); + }); + }, + function (err) { + debug('done: %s', err.message); + self.running = false; + self.emit('done'); + self.removeAllListeners(); + } + ); + + return false; +}; + +Scheduler.prototype.acquire = function(callback) { + this.removeAllListeners('add'); + this.removeAllListeners('release'); + + if (this.tasks.every(is(STATUS.DONE))) { + return callback(null, null); + } + + var self = this; + this.capacity.getCapacity(function(err, capacity) { + if (err) { + return callback(err); + } + + debug('Trying to acquire task'); + var running = self.tasks.filter(is(STATUS.RUNNING)); + debug('[capacity=%d, running=%d] candidates=%j', capacity, running.length, self.tasks); + + self.once('add', function() { + debug('Got a new task'); + self.acquire(callback); + }); + self.once('release', function() { + debug('Slot was released'); + self.acquire(callback); + }); + + if (running.length >= capacity) { + debug('Not enough capacity'); + return null; + } + + var isRunningAny = self.tasks.some(is(STATUS.RUNNING)); + var candidate = self.tasksTree.min(); + if (isRunningAny && candidate === null) { + debug('Waiting for last task to finish'); + return null; + } + + if (candidate) { + self.emit('acquired', candidate.user); + } + + return callback(null, candidate); + }); +}; + +Scheduler.prototype.release = function(err, taskEntity) { + debug('Released %j', taskEntity); + if (taskEntity.is(STATUS.PENDING)) { + this.tasksTree.insert(taskEntity); + } + this.emit('release'); +}; + + +/* Task entities */ + +var STATUS = { + PENDING: 'pending', + RUNNING: 'running', + DONE: 'done' +}; + +function TaskEntity(user, createdAt) { + this.user = user; + this.createdAt = createdAt; + this.status = STATUS.PENDING; + this.jobs = 0; +} + +TaskEntity.prototype.is = function(status) { + return this.status === status; +}; + +TaskEntity.prototype.running = function() { + this.status = STATUS.RUNNING; +}; + +TaskEntity.prototype.ran = function(userQueueIsEmpty) { + this.jobs++; + this.status = userQueueIsEmpty ? STATUS.DONE : STATUS.PENDING; +}; + +function is(status) { + return function(taskEntity) { + return taskEntity.is(status); + }; +} diff --git a/batch/user_database_metadata_service.js b/batch/user_database_metadata_service.js new file mode 100644 index 0000000..6a083a5 --- /dev/null +++ b/batch/user_database_metadata_service.js @@ -0,0 +1,31 @@ +'use strict'; + +function UserDatabaseMetadataService(metadataBackend) { + this.metadataBackend = metadataBackend; +} + +UserDatabaseMetadataService.prototype.getUserMetadata = function (username, callback) { + var self = this; + + this.metadataBackend.getAllUserDBParams(username, function (err, userDatabaseMetadata) { + if (err) { + return callback(err); + } + + callback(null, self.parseMetadataToDatabase(userDatabaseMetadata)); + }); +}; + +UserDatabaseMetadataService.prototype.parseMetadataToDatabase = function (userDatabaseMetadata) { + var dbParams = userDatabaseMetadata; + + var dbopts = {}; + + dbopts.port = dbParams.dbport || global.settings.db_batch_port || global.settings.db_port; + dbopts.host = dbParams.dbhost; + dbopts.dbname = dbParams.dbname; + + return dbopts; +}; + +module.exports = UserDatabaseMetadataService; diff --git a/batch/util/debug.js b/batch/util/debug.js new file mode 100644 index 0000000..2d9116e --- /dev/null +++ b/batch/util/debug.js @@ -0,0 +1,7 @@ +'use strict'; + +var debug = require('debug'); + +module.exports = function batchDebug (ns) { + return debug(['batch', ns].join(':')); +}; diff --git a/batch/util/forever.js b/batch/util/forever.js new file mode 100644 index 0000000..6246abe --- /dev/null +++ b/batch/util/forever.js @@ -0,0 +1,11 @@ +'use strict'; + +module.exports = function forever(fn, done) { + function next(err) { + if (err) { + return done(err); + } + fn(next); + } + next(); +}; diff --git a/carto-package.json b/carto-package.json new file mode 100644 index 0000000..c3f61e3 --- /dev/null +++ b/carto-package.json @@ -0,0 +1,16 @@ +{ + "name": "carto_sql_api", + "current_version": { + "requires": { + "node": ">=6.9.2 <11.0.0", + "npm": ">=3.10.9", + "gdal":">=1.11.0" + }, + "works_with": { + "redis": ">=3.0.0", + "postgresql": ">=9.5.0", + "postgis": ">=2.2.0.0", + "carto_postgresql_ext": ">=0.19.0" + } + } +} diff --git a/client/bin_decoder.js b/client/bin_decoder.js new file mode 100644 index 0000000..81ac126 --- /dev/null +++ b/client/bin_decoder.js @@ -0,0 +1,104 @@ +'use strict'; + +function ArrayBufferSer(arrayBuffer) { + this.buffer = arrayBuffer; + this.offset = 0; + this.sections = this.readArray(); + this.headers = this.sections[0]; + this._headersIndex = {}; + for(var i = 0; i < this.headers.length; ++i) { + this._headersIndex[this.headers[i]] = i; + } + if(this.sections.length > 1) { + this.length = this.sections[1].length; + } +} + +ArrayBufferSer.INT8 = 1; +ArrayBufferSer.UINT8 = 2; +ArrayBufferSer.UINT8_CLAMP = 3; +ArrayBufferSer.INT16 = 4; +ArrayBufferSer.UINT16 = 5; +ArrayBufferSer.INT32 = 6; +ArrayBufferSer.UINT32 = 7; +ArrayBufferSer.FLOAT32 = 8; +//ArrayBufferSer.FLOAT64 = 9; not supported +ArrayBufferSer.STRING = 10; +ArrayBufferSer.BUFFER = 11; + + +ArrayBufferSer.prototype = { + + sizes: [NaN, 1, 1, 1, 2, 2, 4, 4, 4, 8], + + types: [ + null, + Int8Array, + Uint8Array, + Uint8ClampedArray, + Int16Array, + Uint16Array, + Int32Array, + Uint32Array, + Float32Array, + Float64Array + ], + + get: function(columnName) { + var i = this._headersIndex[columnName] + if(i != undefined) { + return this.sections[i + 1] + } + return; + }, + + _paddingFor: function(offset, type) { + var s = this.sizes[type] + if(s) { + var r = offset % s; + return r == 0 ? 0 : s - r; + } + return 0; + }, + + readUInt32: function() { + var i = new DataView(this.buffer).getUint32(this.offset); + this.offset += 4; + return i + }, + + readArray: function() { + var type = this.readUInt32(); + var size = this.readUInt32(); + if(type < ArrayBufferSer.STRING) { + var a = new this.types[type](this.buffer, this.offset, size/this.sizes[type]); + this.offset += size; + return a; + } else if(type == ArrayBufferSer.STRING) { + var target = this.offset + size; + var b = []; + while(this.offset < target) { + this.offset += this._paddingFor(this.offset, ArrayBufferSer.INT32); + var arr = this.readArray(); + if(arr) { + var str = ''; + for(var i = 0; i < arr.length; ++i) { + str += String.fromCharCode(arr[i]); + } + b.push(str); + } + // parse sttring + } + return b; + } else if(type == ArrayBufferSer.BUFFER) { + var b = []; + var target = this.offset + size; + while(this.offset < target) { + this.offset += this._paddingFor(this.offset, ArrayBufferSer.INT32); + b.push(this.readArray()); + } + return b; + } + } + +}; diff --git a/client/test.html b/client/test.html new file mode 100644 index 0000000..683aa97 --- /dev/null +++ b/client/test.html @@ -0,0 +1,32 @@ + + + + + + + diff --git a/config/environments/development.js.example b/config/environments/development.js.example new file mode 100644 index 0000000..0e89e67 --- /dev/null +++ b/config/environments/development.js.example @@ -0,0 +1,127 @@ +// Time in milliseconds to force GC cycle. +// Disable by using <=0 value. +module.exports.gc_interval = 10000; +// In case the base_url 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. +module.exports.base_url = '(?:/api/:version|/user/:user/api/:version)'; +// If useProfiler is true every response will be served with an +// X-SQLAPI-Profile header containing elapsed timing for various +// steps taken for producing the response. +module.exports.useProfiler = true; +module.exports.log_format = '[:date] :remote-addr :method :req[Host]:url :status :response-time ms -> :res[Content-Type] (:res[X-SQLAPI-Profiler]) (:res[X-SQLAPI-Errors])'; +// If log_filename is given logs will be written there, in append mode. Otherwise stdout is used (default). +// Log file will be re-opened on receiving the HUP signal +module.exports.log_filename = 'logs/cartodb-sql-api.log'; +// Regular expression pattern to extract username +// from hostname. Must have a single grabbing block. +module.exports.user_from_host = '^(.*)\\.localhost'; +module.exports.node_port = 8080; +module.exports.node_host = '127.0.0.1'; +// idle socket timeout, in miliseconds +module.exports.node_socket_timeout = 600000; +module.exports.environment = 'development'; +module.exports.db_base_name = 'cartodb_dev_user_<%= user_id %>_db'; +// Supported labels: 'user_id' (read from redis) +module.exports.db_user = 'development_cartodb_user_<%= user_id %>'; +// Supported labels: 'user_id', 'user_password' (both read from redis) +module.exports.db_user_pass = '<%= user_password %>' +// Name of the anonymous PostgreSQL user +module.exports.db_pubuser = 'publicuser'; +// Password for the anonymous PostgreSQL user +module.exports.db_pubuser_pass = 'public'; +module.exports.db_host = 'localhost'; +module.exports.db_port = '5432'; +module.exports.db_batch_port = '5432'; +module.exports.finished_jobs_ttl_in_seconds = 2 * 3600; // 2 hours +module.exports.batch_query_timeout = 12 * 3600 * 1000; // 12 hours in milliseconds +module.exports.batch_log_filename = 'logs/batch-queries.log'; +module.exports.copy_timeout = "'5h'"; +module.exports.copy_from_max_post_size = 2 * 1024 * 1024 * 1024 // 2 GB; +module.exports.copy_from_max_post_size_pretty = '2 GB'; +// Max number of queued jobs a user can have at a given time +module.exports.batch_max_queued_jobs = 64; +// Capacity strategy to use. +// It allows to tune how many queries run at a db host at the same time. +// Options: 'fixed', 'http-simple', 'http-load' +module.exports.batch_capacity_strategy = 'fixed'; +// Applies when strategy='fixed'. +// Number of simultaneous users running queries in the same host. +// It will use 1 as min. +// Default 4. +module.exports.batch_capacity_fixed_amount = 4; +// Applies when strategy='http-simple' or strategy='http-load'. +// HTTP endpoint to check db host load. +// Helps to decide the number of simultaneous users running queries in that host. +// 'http-simple' will use 'available_cores' to decide the number. +// 'http-load' will use 'cores' and 'relative_load' to decide the number. +// It will use 1 as min. +// If no template is provided it will default to 'fixed' strategy. +module.exports.batch_capacity_http_url_template = 'http://<%= dbhost %>:9999/load'; +// Max database connections in the pool +// Subsequent connections will wait for a free slot. +// NOTE: not used by OGR-mediated accesses +module.exports.db_pool_size = 500; +// Milliseconds before a connection is removed from pool +module.exports.db_pool_idleTimeout = 30000; +// Milliseconds between idle client checking +module.exports.db_pool_reapInterval = 1000; +// max number of bytes for a row, when exceeded the query will throw an error +//module.exports.db_max_row_size = 10 * 1024 * 1024; +// allows to use an object to connect with node-postgres instead of a connection string +//module.exports.db_use_config_object = true; +// requires enabling db_use_config_object=true +// allows to enable/disable keep alive for database connections +// by default is not enabled +//module.exports.db_keep_alive = { +// enabled: true, +// initialDelay: 5000 +//}; +module.exports.redis_host = '127.0.0.1'; +module.exports.redis_port = 6379; +module.exports.redisPool = 50; +module.exports.redisIdleTimeoutMillis = 100; +module.exports.redisReapIntervalMillis = 10; +module.exports.redisLog = false; + +// tableCache settings +module.exports.tableCacheEnabled = false; // false by default +// Max number of entries in the query tables cache +module.exports.tableCacheMax = 8192; +// Max age of query table cache items, in milliseconds +module.exports.tableCacheMaxAge = 1000*60*10; + +// Temporary directory, make sure it is writable by server user +module.exports.tmpDir = '/tmp'; +// change ogr2ogr command or path +module.exports.ogr2ogrCommand = 'ogr2ogr'; +// change zip command or path +module.exports.zipCommand = 'zip'; +// Optional statsd support +module.exports.statsd = { + host: 'localhost', + port: 8125, + prefix: 'dev.:host.', + cacheDns: true + // support all allowed node-statsd options +}; +module.exports.health = { + enabled: true, + username: 'development', + query: 'select 1' +}; +module.exports.disabled_file = 'pids/disabled'; + +module.exports.ratelimits = { + // whether it should rate limit endpoints (global configuration) + rateLimitsEnabled: false, + // whether it should rate limit one or more endpoints (only if rateLimitsEnabled = true) + endpoints: { + query: false, + job_create: false, + job_get: false, + job_delete: false + } +} + +module.exports.validatePGEntitiesAccess = false; +module.exports.dataIngestionLogPath = 'logs/data-ingestion.log'; diff --git a/config/environments/production.js.example b/config/environments/production.js.example new file mode 100644 index 0000000..bf706da --- /dev/null +++ b/config/environments/production.js.example @@ -0,0 +1,131 @@ +// Time in milliseconds to force GC cycle. +// Disable by using <=0 value. +module.exports.gc_interval = 10000; +// In case the base_url 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. +module.exports.base_url = '(?:/api/:version|/user/:user/api/:version)'; +// If useProfiler is true every response will be served with an +// X-SQLAPI-Profile header containing elapsed timing for various +// steps taken for producing the response. +module.exports.useProfiler = true; +module.exports.log_format = '[:date] :remote-addr :method :req[Host]:url :status :response-time ms -> :res[Content-Type] (:res[X-SQLAPI-Profiler]) (:res[X-SQLAPI-Errors])'; +// If log_filename is given logs will be written there, in append mode. Otherwise stdout is used (default). +// Log file will be re-opened on receiving the HUP signal +module.exports.log_filename = 'logs/cartodb-sql-api.log'; +// Regular expression pattern to extract username +// from hostname. Must have a single grabbing block. +module.exports.user_from_host = '^(.*)\\.cartodb\\.com$'; +module.exports.node_port = 8080; +module.exports.node_host = '127.0.0.1'; +// idle socket timeout, in miliseconds +module.exports.node_socket_timeout = 600000; +module.exports.environment = 'production'; +// Supported labels: 'user_id' (read from redis) +module.exports.db_base_name = 'cartodb_user_<%= user_id %>_db'; +// Supported labels: 'user_id' (read from redis) +module.exports.db_user = 'cartodb_user_<%= user_id %>'; +// Supported labels: 'user_id', 'user_password' (both read from redis) +module.exports.db_user_pass = '<%= user_password %>' +// Name of the anonymous PostgreSQL user +module.exports.db_pubuser = 'publicuser'; +// Password for the anonymous PostgreSQL user +module.exports.db_pubuser_pass = 'public'; +module.exports.db_host = 'localhost'; +module.exports.db_port = '6432'; +module.exports.db_batch_port = '5432'; +module.exports.finished_jobs_ttl_in_seconds = 2 * 3600; // 2 hours +module.exports.batch_query_timeout = 12 * 3600 * 1000; // 12 hours in milliseconds +module.exports.batch_log_filename = 'logs/batch-queries.log'; +module.exports.copy_timeout = "'5h'"; +module.exports.copy_from_max_post_size = 2 * 1024 * 1024 * 1024 // 2 GB; +module.exports.copy_from_max_post_size_pretty = '2 GB'; +// Max number of queued jobs a user can have at a given time +module.exports.batch_max_queued_jobs = 64; +// Capacity strategy to use. +// It allows to tune how many queries run at a db host at the same time. +// Options: 'fixed', 'http-simple', 'http-load' +module.exports.batch_capacity_strategy = 'fixed'; +// Applies when strategy='fixed'. +// Number of simultaneous users running queries in the same host. +// It will use 1 as min. +// Default 4. +module.exports.batch_capacity_fixed_amount = 4; +// Applies when strategy='http-simple' or strategy='http-load'. +// HTTP endpoint to check db host load. +// Helps to decide the number of simultaneous users running queries in that host. +// 'http-simple' will use 'available_cores' to decide the number. +// 'http-load' will use 'cores' and 'relative_load' to decide the number. +// It will use 1 as min. +// If no template is provided it will default to 'fixed' strategy. +module.exports.batch_capacity_http_url_template = 'http://<%= dbhost %>:9999/load'; +// Max database connections in the pool +// Subsequent connections will wait for a free slot.i +// NOTE: not used by OGR-mediated accesses +module.exports.db_pool_size = 500; +// Milliseconds before a connection is removed from pool +module.exports.db_pool_idleTimeout = 30000; +// Milliseconds between idle client checking +module.exports.db_pool_reapInterval = 1000; +// max number of bytes for a row, when exceeded the query will throw an error +//module.exports.db_max_row_size = 10 * 1024 * 1024; +// allows to use an object to connect with node-postgres instead of a connection string +//module.exports.db_use_config_object = true; +// requires enabling db_use_config_object=true +// allows to enable/disable keep alive for database connections +// by default is not enabled +//module.exports.db_keep_alive = { +// enabled: true, +// initialDelay: 5000 +//}; +module.exports.redis_host = '127.0.0.1'; +module.exports.redis_port = 6379; +module.exports.redisPool = 50; +module.exports.redisIdleTimeoutMillis = 10000; +module.exports.redisReapIntervalMillis = 1000; +module.exports.redisLog = false; + +// tableCache settings +module.exports.tableCacheEnabled = false; // false by default +// Max number of entries in the query tables cache +module.exports.tableCacheMax = 8192; +// Max age of query table cache items, in milliseconds +module.exports.tableCacheMaxAge = 1000*60*10; + +// Temporary directory, make sure it is writable by server user +module.exports.tmpDir = '/tmp'; +// change ogr2ogr command or path +module.exports.ogr2ogrCommand = 'ogr2ogr'; +// change zip command or path +module.exports.zipCommand = 'zip'; +// Optional statsd support +module.exports.statsd = { + host: 'localhost', + port: 8125, + prefix: 'dev.:host.', + cacheDns: true + // support all allowed node-statsd options +}; +module.exports.health = { + enabled: true, + username: 'development', + query: 'select 1' +}; +module.exports.oauth = { + allowedHosts: ['carto.com', 'cartodb.com'] +}; +module.exports.disabled_file = 'pids/disabled'; + +module.exports.ratelimits = { + // whether it should rate limit endpoints (global configuration) + rateLimitsEnabled: false, + // whether it should rate limit one or more endpoints (only if rateLimitsEnabled = true) + endpoints: { + query: false, + job_create: false, + job_get: false, + job_delete: false + } +} + +module.exports.validatePGEntitiesAccess = false; +module.exports.dataIngestionLogPath = 'logs/data-ingestion.log'; diff --git a/config/environments/staging.js.example b/config/environments/staging.js.example new file mode 100644 index 0000000..91913d9 --- /dev/null +++ b/config/environments/staging.js.example @@ -0,0 +1,128 @@ +// Time in milliseconds to force GC cycle. +// Disable by using <=0 value. +module.exports.gc_interval = 10000; +// In case the base_url 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. +module.exports.base_url = '(?:/api/:version|/user/:user/api/:version)'; +// If useProfiler is true every response will be served with an +// X-SQLAPI-Profile header containing elapsed timing for various +// steps taken for producing the response. +module.exports.useProfiler = true; +module.exports.log_format = '[:date] :req[X-Real-IP] :method :req[Host]:url :status :response-time ms -> :res[Content-Type] (:res[X-SQLAPI-Profiler]) (:res[X-SQLAPI-Errors])'; +// If log_filename is given logs will be written there, in append mode. Otherwise stdout is used (default). +// Log file will be re-opened on receiving the HUP signal +module.exports.log_filename = 'logs/cartodb-sql-api.log'; +// Regular expression pattern to extract username +// from hostname. Must have a single grabbing block. +module.exports.user_from_host = '^(.*)\\.cartodb\\.com$'; +module.exports.node_port = 8080; +module.exports.node_host = '127.0.0.1'; +// idle socket timeout, in miliseconds +module.exports.node_socket_timeout = 600000; +module.exports.environment = 'staging'; +// Supported labels: 'user_id' (read from redis) +module.exports.db_base_name = 'cartodb_staging_user_<%= user_id %>_db'; +// Supported labels: 'user_id' (read from redis) +module.exports.db_user = 'cartodb_staging_user_<%= user_id %>'; +// Supported labels: 'user_id', 'user_password' (both read from redis) +module.exports.db_user_pass = '<%= user_password %>' +// Name of the anonymous PostgreSQL user +module.exports.db_pubuser = 'publicuser'; +// Password for the anonymous PostgreSQL user +module.exports.db_pubuser_pass = 'public'; +module.exports.db_host = 'localhost'; +module.exports.db_port = '6432'; +module.exports.db_batch_port = '5432'; +module.exports.finished_jobs_ttl_in_seconds = 2 * 3600; // 2 hours +module.exports.batch_query_timeout = 12 * 3600 * 1000; // 12 hours in milliseconds +module.exports.batch_log_filename = 'logs/batch-queries.log'; +module.exports.copy_timeout = "'5h'"; +module.exports.copy_from_max_post_size = 2 * 1024 * 1024 * 1024 // 2 GB; +module.exports.copy_from_max_post_size_pretty = '2 GB'; +// Max number of queued jobs a user can have at a given time +module.exports.batch_max_queued_jobs = 64; +// Capacity strategy to use. +// It allows to tune how many queries run at a db host at the same time. +// Options: 'fixed', 'http-simple', 'http-load' +module.exports.batch_capacity_strategy = 'fixed'; +// Applies when strategy='fixed'. +// Number of simultaneous users running queries in the same host. +// It will use 1 as min. +// Default 4. +module.exports.batch_capacity_fixed_amount = 4; +// Applies when strategy='http-simple' or strategy='http-load'. +// HTTP endpoint to check db host load. +// Helps to decide the number of simultaneous users running queries in that host. +// 'http-simple' will use 'available_cores' to decide the number. +// 'http-load' will use 'cores' and 'relative_load' to decide the number. +// It will use 1 as min. +// If no template is provided it will default to 'fixed' strategy. +module.exports.batch_capacity_http_url_template = 'http://<%= dbhost %>:9999/load'; +// Max database connections in the pool +// Subsequent connections will wait for a free slot. +// NOTE: not used by OGR-mediated accesses +module.exports.db_pool_size = 500; +// Milliseconds before a connection is removed from pool +module.exports.db_pool_idleTimeout = 30000; +// Milliseconds between idle client checking +module.exports.db_pool_reapInterval = 1000; +// max number of bytes for a row, when exceeded the query will throw an error +//module.exports.db_max_row_size = 10 * 1024 * 1024; +// allows to use an object to connect with node-postgres instead of a connection string +//module.exports.db_use_config_object = true; +// requires enabling db_use_config_object=true +// allows to enable/disable keep alive for database connections +// by default is not enabled +//module.exports.db_keep_alive = { +// enabled: true, +// initialDelay: 5000 +//}; +module.exports.redis_host = '127.0.0.1'; +module.exports.redis_port = 6379; +module.exports.redisPool = 50; +module.exports.redisIdleTimeoutMillis = 10000; +module.exports.redisReapIntervalMillis = 1000; +module.exports.redisLog = false; + +// tableCache settings +module.exports.tableCacheEnabled = false; // false by default +// Max number of entries in the query tables cache +module.exports.tableCacheMax = 8192; +// Max age of query table cache items, in milliseconds +module.exports.tableCacheMaxAge = 1000*60*10; + +// Temporary directory, make sure it is writable by server user +module.exports.tmpDir = '/tmp'; +// change ogr2ogr command or path +module.exports.ogr2ogrCommand = 'ogr2ogr'; +// change zip command or path +module.exports.zipCommand = 'zip'; +// Optional statsd support +module.exports.statsd = { + host: 'localhost', + port: 8125, + prefix: 'dev.:host.', + cacheDns: true + // support all allowed node-statsd options +}; +module.exports.health = { + enabled: true, + username: 'development', + query: 'select 1' +}; +module.exports.disabled_file = 'pids/disabled'; + +module.exports.ratelimits = { + // whether it should rate limit endpoints (global configuration) + rateLimitsEnabled: false, + // whether it should rate limit one or more endpoints (only if rateLimitsEnabled = true) + endpoints: { + query: false, + job_create: false, + job_get: false, + job_delete: false + } +} + +module.exports.validatePGEntitiesAccess = false; +module.exports.dataIngestionLogPath = 'logs/data-ingestion.log'; diff --git a/config/environments/test.js.example b/config/environments/test.js.example new file mode 100644 index 0000000..c8fba86 --- /dev/null +++ b/config/environments/test.js.example @@ -0,0 +1,128 @@ +// Time in milliseconds to force GC cycle. +// Disable by using <=0 value. +module.exports.gc_interval = 10000; +// In case the base_url 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. +module.exports.base_url = '(?:/api/:version|/user/:user/api/:version)'; +// If useProfiler is true every response will be served with an +// X-SQLAPI-Profile header containing elapsed timing for various +// steps taken for producing the response. +module.exports.useProfiler = true; +module.exports.log_format = '[:date] :remote-addr :method :req[Host]:url :status :response-time ms -> :res[Content-Type] (:res[X-SQLAPI-Profiler]) (:res[X-SQLAPI-Errors])'; +// If log_filename is given logs will be written there, in append mode. Otherwise stdout is used (default). +// Log file will be re-opened on receiving the HUP signal +// module.exports.log_filename = 'logs/cartodb-sql-api.log'; +// Regular expression pattern to extract username +// from hostname. Must have a single grabbing block. +module.exports.user_from_host = '^([^.]*)\\.'; +module.exports.node_port = 8080; +module.exports.node_host = '127.0.0.1'; +// idle socket timeout, in miliseconds +module.exports.node_socket_timeout = 600000; +module.exports.environment = 'test'; +module.exports.db_base_name = 'cartodb_test_user_<%= user_id %>_db'; +module.exports.db_user = 'test_cartodb_user_<%= user_id %>'; +module.exports.db_user_pass = 'test_cartodb_user_<%= user_id %>_pass'; +// Name of the anonymous PostgreSQL user +module.exports.db_pubuser = 'testpublicuser'; +// Password for the anonymous PostgreSQL user +module.exports.db_pubuser_pass = 'public'; +module.exports.db_host = 'localhost'; +module.exports.db_port = '5432'; +module.exports.db_batch_port = '5432'; +module.exports.finished_jobs_ttl_in_seconds = 2 * 3600; // 2 hours +module.exports.batch_query_timeout = 5 * 1000; // 5 seconds in milliseconds +module.exports.batch_log_filename = 'logs/batch-queries.log'; +module.exports.copy_timeout = "'5h'"; +module.exports.copy_from_max_post_size = 2 * 1024 * 1024 * 1024 // 2 GB; +module.exports.copy_from_max_post_size_pretty = '2 GB'; +// Max number of queued jobs a user can have at a given time +module.exports.batch_max_queued_jobs = 64; +// Capacity strategy to use. +// It allows to tune how many queries run at a db host at the same time. +// Options: 'fixed', 'http-simple', 'http-load' +module.exports.batch_capacity_strategy = 'fixed'; +// Applies when strategy='fixed'. +// Number of simultaneous users running queries in the same host. +// It will use 1 as min. +// Default 4. +module.exports.batch_capacity_fixed_amount = 4; +// Applies when strategy='http-simple' or strategy='http-load'. +// HTTP endpoint to check db host load. +// Helps to decide the number of simultaneous users running queries in that host. +// 'http-simple' will use 'available_cores' to decide the number. +// 'http-load' will use 'cores' and 'relative_load' to decide the number. +// It will use 1 as min. +// If no template is provided it will default to 'fixed' strategy. +module.exports.batch_capacity_http_url_template = 'http://<%= dbhost %>:9999/load'; +// Max database connections in the pool +// Subsequent connections will wait for a free slot. +// NOTE: not used by OGR-mediated accesses +module.exports.db_pool_size = 500; +// Milliseconds before a connection is removed from pool +module.exports.db_pool_idleTimeout = 30000; +// Milliseconds between idle client checking +module.exports.db_pool_reapInterval = 1000; +// max number of bytes for a row, when exceeded the query will throw an error +//module.exports.db_max_row_size = 10 * 1024 * 1024; +// allows to use an object to connect with node-postgres instead of a connection string +//module.exports.db_use_config_object = true; +// requires enabling db_use_config_object=true +// allows to enable/disable keep alive for database connections +// by default is not enabled +//module.exports.db_keep_alive = { +// enabled: true, +// initialDelay: 5000 +//}; +module.exports.redis_host = '127.0.0.1'; +module.exports.redis_port = 6336; +module.exports.redisPool = 50; +module.exports.redisIdleTimeoutMillis = 1; +module.exports.redisReapIntervalMillis = 1; +module.exports.redisLog = false; + +// tableCache settings +module.exports.tableCacheEnabled = true; // false by default. We want to make sure some functionallity is there until we fully deprecate it. +// Max number of entries in the query tables cache +module.exports.tableCacheMax = 8192; +// Max age of query table cache items, in milliseconds +module.exports.tableCacheMaxAge = 1000*60*10; + +// Temporary directory, make sure it is writable by server user +module.exports.tmpDir = '/tmp'; +// change ogr2ogr command or path +module.exports.ogr2ogrCommand = 'ogr2ogr'; +// change zip command or path +module.exports.zipCommand = 'zip'; +// Optional statsd support +module.exports.statsd = { + host: 'localhost', + port: 8125, + prefix: 'dev.:host.', + cacheDns: true + // support all allowed node-statsd options +}; +module.exports.health = { + enabled: true, + username: 'vizzuality', + query: 'select 1' +}; +module.exports.oauth = { + allowedHosts: ['localhost.lan:8080', 'localhostdb.lan:8080'] +}; +module.exports.disabled_file = 'pids/disabled'; + +module.exports.ratelimits = { + // whether it should rate limit endpoints (global configuration) + rateLimitsEnabled: false, + // whether it should rate limit one or more endpoints (only if rateLimitsEnabled = true) + endpoints: { + query: false, + job_create: false, + job_get: false, + job_delete: false + } +} + +module.exports.validatePGEntitiesAccess = false; +module.exports.dataIngestionLogPath = 'logs/data-ingestion.log'; diff --git a/configure b/configure new file mode 100755 index 0000000..3ae5ca0 --- /dev/null +++ b/configure @@ -0,0 +1,36 @@ +#!/bin/sh + +usage() { + echo "Usage: $0 [OPTION]" + echo + echo "Configuration:" + echo " --help display this help and exit" + echo " --with-pgport=NUM access PostgreSQL server on TCP port NUM" +} + +PGPORT=5432 + +while test -n "$1"; do + case "$1" in + --help|-h) + usage + exit 0 + ;; + --with-pgport=*) + PGPORT=`echo "$1" | cut -d= -f2` + ;; + *) + echo "Unused option '$1'" >&2 + ;; + esac + shift +done + +echo "PGPORT: $PGPORT" + +# TODO: allow specifying configuration settings ! +for f in config/environments/*.example; do + o=`dirname "$f"`/`basename "$f" .example` + echo "Writing $o" + sed "s/\( *module.exports.db_port[ \t]*= *'\?\)[^';]*\('\?;\)/\1$PGPORT\2/" < "$f" > "$o" +done diff --git a/doc/API.md b/doc/API.md new file mode 100644 index 0000000..f367730 --- /dev/null +++ b/doc/API.md @@ -0,0 +1,25 @@ +# SQL API + +CARTO's SQL API allows you to interact with your tables and data inside CARTO, as if you were running SQL statements against a normal database. The database behind CARTO is PostgreSQL so if you need help with specific SQL statements or you want to learn more about it, visit the [official documentation](http://www.postgresql.org/docs/9.1/static/sql.html). + +There are two main situations in which you would want to use the SQL API: + +- You want to **insert, update** or **delete** data. For example, you would like to insert a new column with a latitude and longitude data. + +- You want to **select** data from public tables in order to use it on your website or in your app. For example, you need to find the 10 closest records to a particular location. + +Remember that in order to access, read or modify data in private tables, you will need to authenticate your requests. When a table is public, you can do non-authenticated queries that read data, but you cannot write or modify data without authentication. + +## Documentation +[The following doc files are being pulled into the live documentation:](https://github.com/CartoDB/docs/tree/master/_app/_sqlapi): + +* [Authentication](authentication.md) +* [Making calls to the SQL API](making_calls.md) +* [Creating Tables with the SQL API](creating_tables.md) +* [Batch Queries](batch_queries.md) +* [Copy Queries](copy_queries.md) +* [Handling Geospatial Data](handling_geospatial_data.md) +* [Query Optimizations](query_optimizations.md) +* [API Version Vumber](version.md) +* [Libraries in Different Languages](libraries_support.md) +* [Other Tips and Questions](tips_and_tricks.md) diff --git a/doc/authentication.md b/doc/authentication.md new file mode 100644 index 0000000..818f0d5 --- /dev/null +++ b/doc/authentication.md @@ -0,0 +1,17 @@ +# Authentication + +For all access to private tables, and for write access to public tables, CARTO enforces secure API access that requires you to authorize your queries. In order to authorize queries, you can use an API Key or a Consumer Key. + +## API Key + +The API Key offers the simplest way to access private data, or perform writes and updates to your public data. Remember that your API Key protects access to your data, so keep it confidential and only share it if you want others to have this access. If necessary, you can reset your API Key from your CARTO dashboard. + +**Tip:** For details about how to access, or reset, your API Key, see [Your Account](http://docs.carto.com/carto-editor/your-account/#api-key) details. + +To use your API Key, pass it as a parameter in an URL call to the CARTO API. For example, to perform an insert into your table, you would use the following URL structure. + +#### Example + +```bash +https://{username}.carto.com/api/v2/sql?q={SQL statement}&api_key={api_key} +``` diff --git a/doc/batch_queries.md b/doc/batch_queries.md new file mode 100644 index 0000000..d115edf --- /dev/null +++ b/doc/batch_queries.md @@ -0,0 +1,491 @@ +# Batch Queries + +A Batch Query enables you to request queries with long-running CPU processing times. Typically, these kind of requests raise timeout errors when using the SQL API. In order to avoid timeouts, you can use Batch Queries to [create](#create-a-job), [read](#read-a-job) and [cancel](#cancel-a-job) queries. You can also run a [chained batch query](#chaining-batch-queries) to chain several SQL queries into one job. A Batch Query schedules the incoming jobs and allows you to request the job status for each query. + +_Batch Queries are not intended to be used for large query payloads that contain over 16384 characters (16kb). For instance, if you are inserting a large number of rows into your table, you still need to use the [Import API](https://carto.com/docs/carto-engine/import-api/) or [SQL API](https://carto.com/docs/carto-engine/sql-api/) for this type of data management. Batch Queries are specific to queries and CPU usage._ + +**Note:** In order to use Batch Queries, you **must** be [authenticated](https://carto.com/docs/carto-engine/sql-api/authentication/) using API keys. + +## Authentication + +An API Key is required to manage your jobs. The following error message appears if you are not [authenticated](https://carto.com/docs/carto-engine/sql-api/authentication/): + +```bash +{ + "error": [ + "permission denied" + ] +} +``` + +In order to get full access, you must use your API Key. + +Using cURL tool: + +```bash +curl -X POST -H "Content-Type: application/json" -d '{ + "query": "{query}" +}' "http://{username}.carto.com/api/v2/sql/job?api_key={api_key}" +``` + +Using Node.js request client: + +```bash +var request = require("request"); + +var options = { + method: "POST", + url: "http://{username}.carto.com/api/v2/sql/job", + qs: { + "api_key": "{api_key}" + }, + headers: { + "content-type": "application/json" + }, + body: { + query: "{query}" + }, + json: true +}; + +request(options, function (error, response, body) { + if (error) throw new Error(error); + + console.log(body); +}); +``` + +## Batch Queries Job Schema + +A Batch Query request to your CARTO account includes the following job schema elements. _Only the `query` element can be modified._ All other elements of the job schema are defined by the Batch Query and are read-only. + +Name | Description +--- | --- +`job_id` | a universally unique identifier (uuid). +`user` | user identifier, as displayed by the username. +`status` | displays the result of the long-running query. The possible status results are: +--- | --- +|_ `pending` | job waiting to be executed. +|_ `running` | indicates that the job is currently running. +|_ `done` | job executed successfully. +|_ `failed` | job executed but failed, with errors. +|_ `canceled` | job canceled by user request. +|_ `unknown` | appears when it is not possible to determine what exactly happened with the job. +`query` | the SQL statement to be executed in a database. _You can modify the select SQL statement to be used in the job schema._

**Tip:** In some scenarios, you may need to retrieve the query results from a finished job. See [Fetching Job Results](#fetching-job-results) for details. +`created_at` | the date and time when the job schema was created. +`updated_at` | the date and time of when the job schema was last updated, or modified. +`failed_reason` | displays the database error message, if something went wrong. + +#### Example + +```bash +HEADERS: 201 CREATED; application/json +BODY: { + "job_id": "de305d54-75b4-431b-adb2-eb6b9e546014", + "user": "cartofante", + "query": "UPDATE airports SET type = 'international'", + "status": "pending", + "created_at": "2015-12-15T07:36:25Z", + "updated_at": "2015-12-15T07:36:25Z" +} +``` + +### Create a Job + +To create a Batch Query job, make a POST request with the following parameters. + +Creates a Batch Query job request. + +```bash +HEADERS: POST /api/v2/sql/job +BODY: { + "query": "UPDATE airports SET type = 'international'" +} +``` + +#### Response + +```bash +HEADERS: 201 CREATED; application/json +BODY: { + "job_id": "de305d54-75b4-431b-adb2-eb6b9e546014", + "user": "cartofante" + "query": "UPDATE airports SET type = 'international'", + "status": "pending", + "created_at": "2015-12-15T07:36:25Z", + "updated_at": "2015-12-15T07:36:25Z" +} +``` + +##### POST Examples + +If you are using the Batch Query create operation for a cURL POST request, use the following code: + +```bash +curl -X POST -H "Content-Type: application/json" -d '{ + "query": "CREATE TABLE world_airports AS SELECT a.cartodb_id, a.the_geom, a.the_geom_webmercator, a.name airport, b.name country FROM world_borders b JOIN airports a ON ST_Contains(b.the_geom, a.the_geom)" +}' "http://{username}.carto.com/api/v2/sql/job" +``` + +If you are using the Batch Query create operation for a Node.js client POST request, use the following code: + +```bash +var request = require("request"); + +var options = { + method: "POST", + url: "http://{username}.carto.com/api/v2/sql/job", + headers: { "content-type": "application/json" }, + body: { + query: "CREATE TABLE world_airports AS SELECT a.cartodb_id, a.the_geom, a.the_geom_webmercator, a.name airport, b.name country FROM world_borders b JOIN airports a ON ST_Contains(b.the_geom, a.the_geom)" + }, + json: true +}; + +request(options, function (error, response, body) { + if (error) throw new Error(error); + + console.log(body); +}); +``` + +### Read a Job + +To read a Batch Query job, make a GET request with the following parameters. + +```bash +HEADERS: GET /api/v2/sql/job/de305d54-75b4-431b-adb2-eb6b9e546014 +BODY: {} +``` + +#### Response + +```bash +HEADERS: 200 OK; application/json +BODY: { + "job_id": "de305d54-75b4-431b-adb2-eb6b9e546014", + "user": "cartofante" + "query": "UPDATE airports SET type = 'international'", + "status": "pending", + "created_at": "2015-12-15T07:36:25Z", + "updated_at": "2015-12-15T07:36:25Z" +} +``` + +##### GET Examples + +If you are using the Batch Query read operation for a cURL GET request, use the following code: + +```bash +curl -X GET "http://{username}.carto.com/api/v2/sql/job/{job_id}" +``` + +If you are a Batch Query read operation for a Node.js client GET request, use the following code: + +```bash +var request = require("request"); + +var options = { + method: "GET", + url: "http://{username}.carto.com/api/v2/sql/job/{job_id}" +}; + +request(options, function (error, response, body) { + if (error) throw new Error(error); + + console.log(body); +}); +``` + +### Cancel a Job + +To cancel a Batch Query, make a DELETE request with the following parameters. + +```bash +HEADERS: DELETE /api/v2/sql/job/de305d54-75b4-431b-adb2-eb6b9e546014 +BODY: {} +``` + +**Note:** Be mindful when canceling a job when the status: `pending` or `running`. + +- If the job is `pending`, the job will never be executed +- If the job is `running`, the job will be terminated immediately + +#### Response + +```bash +HEADERS: 200 OK; application/json +BODY: { + "job_id": "de305d54-75b4-431b-adb2-eb6b9e546014", + "user": "cartofante" + "query": "UPDATE airports SET type = 'international'", + "status": "cancelled", + "created_at": "2015-12-15T07:36:25Z", + "updated_at": "2015-12-17T06:22:42Z" +} +``` + +**Note:** Jobs can only be canceled while the `status: "running"` or `status: "pending"`, otherwise the Batch Query operation is not allowed. You will receive an error if the job status is anything but "running" or "pending". + +```bash +errors: [ + "The job status is done, cancel is not allowed" +] +``` + +##### DELETE Examples + +If you are using the Batch Query cancel operation for cURL DELETE request, use the following code: + +```bash +curl -X DELETE "http://{username}.carto.com/api/v2/sql/job/{job_id}" +``` + +If you are using the Batch Query cancel operation for a Node.js client DELETE request, use the following code: + +```bash +var request = require("request"); + +var options = { + method: "DELETE", + url: "http://{username}.carto.com/api/v2/sql/job/{job_id}", +}; + +request(options, function (error, response, body) { + if (error) throw new Error(error); + + console.log(body); +}); +``` + +### Chaining Batch Queries + +In some cases, you may need to chain queries into one job. The Chaining Batch Query option enables you run an array of SQL statements, and define the order in which the queries are executed. You can use any of the operations (create, read, list, update, cancel) for the queries in a chained batch query. + +```bash +HEADERS: POST /api/v2/sql/job +BODY: { + query: [ + "CREATE TABLE world_airports AS SELECT a.cartodb_id, a.the_geom, a.the_geom_webmercator, a.name airport, b.name country FROM world_borders b JOIN airports a ON ST_Contains(b.the_geom, a.the_geom)", + "DROP TABLE airports", + "ALTER TABLE world_airports RENAME TO airport" + ] +} +``` + +#### Response + +```bash +HEADERS: 201 CREATED; application/json +BODY: { + "job_id": "de305d54-75b4-431b-adb2-eb6b9e546014", + "user": "cartofante" + "query": [{ + "query": "CREATE TABLE world_airports AS SELECT a.cartodb_id, a.the_geom, a.the_geom_webmercator, a.name airport, b.name country FROM world_borders b JOIN airports a ON ST_Contains(b.the_geom, a.the_geom)", + "status": "pending" + }, { + "query": "DROP TABLE airports", + "status": "pending" + }, { + "query": "ALTER TABLE world_airports RENAME TO airport", + "status": "pending" + }], + "status": "pending", + "created_at": "2015-12-15T07:36:25Z", + "updated_at": "2015-12-15T07:36:25Z" +} +``` + +**Note:** The Batch Query returns a job status for both the parent Chained Batch Query request, and for each child query within the request. The order in which each query is executed is guaranteed. Here are the possible status results for Chained Batch Queries: + +- If one query within the Chained Batch Query fails, the `"status": "failed"` is returned for both the job and the query, and any "pending" queries will not be processed + +- If you cancel the Chained Batch Query job, the job status changes to `"status": "cancelled"`. Any running queries within the job will be stopped and changed to `"status": "pending"`, and will not be processed + +- Suppose the first query job status is `"status": "done"`, the second query is `"status": "running"`, and the third query `"status": "pending"`. If the second query fails for some reason, the job status changes to `"status": "failed"` and the last query will not be processed. It is indicated which query failed in the Chained Batch Query job + +- Creating several jobs does not guarantee that jobs are going to be executed in the same order that they were created. If you need run queries in a specific order, you may want use [Chaining Batch Queries](#chaining-batch-queries). + +##### POST Examples + +If you are using the Chained Batch Query operation for cURL POST request, use the following code: + +```bash +curl -X POST -H "Content-Type: application/json" -d '{ + "query": [ + "CREATE TABLE world_airports AS SELECT a.cartodb_id, a.the_geom, a.the_geom_webmercator, a.name airport, b.name country FROM world_borders b JOIN airports a ON ST_Contains(b.the_geom, a.the_geom)", + "DROP TABLE airports", + "ALTER TABLE world_airports RENAME TO airport" + ] +}' "http://{username}.carto.com/api/v2/sql/job" +``` + +If you are using the Chained Batch Query operation for a Node.js client POST request, use the following code: + +```bash +var request = require("request"); + +var options = { + method: "POST", + url: "http://{username}.carto.com/api/v2/sql/job", + headers: { "content-type": "application/json" }, + body: { + "query": [ + "CREATE TABLE world_airports AS SELECT a.cartodb_id, a.the_geom, a.the_geom_webmercator, a.name airport, b.name country FROM world_borders b JOIN airports a ON ST_Contains(b.the_geom, a.the_geom)", + "DROP TABLE airports", + "ALTER TABLE world_airports RENAME TO airport" + ] + }, + json: true +}; + +request(options, function (error, response, body) { + if (error) throw new Error(error); + + console.log(body); +}); +``` + +##### PUT Examples + +If you are using the Chained Batch Query operation for cURL PUT request, use the following code: + +```bash +curl -X PUT -H "Content-Type: application/json" -d '{ + "query": [ + "CREATE TABLE world_airports AS SELECT a.cartodb_id, a.the_geom, a.the_geom_webmercator, a.name airport, b.name country FROM world_borders b JOIN airports a ON ST_Contains(b.the_geom, a.the_geom)", + "DROP TABLE airports", + "ALTER TABLE world_airports RENAME TO airport", + "UPDATE airports SET airport = upper(airport)" + ] +}' "http://{username}.carto.com/api/v2/sql/job/{job_id}" +``` + +If you are using the Chained Batch Query operation for a Node.js client PUT request, use the following code: + +```bash +var request = require("request"); + +var options = { + method: "PUT", + url: "http://{username}.carto.com/api/v2/sql/job/{job_id}", + headers: { "content-type": "application/json" }, + body: { + query: [ + "CREATE TABLE world_airports AS SELECT a.cartodb_id, a.the_geom, a.the_geom_webmercator, a.name airport, b.name country FROM world_borders b JOIN airports a ON ST_Contains(b.the_geom, a.the_geom)", + "DROP TABLE airports", + "ALTER TABLE world_airports RENAME TO airport", + "UPDATE airports SET airport = upper(airport)" + ] + }, + json: true +}; + +request(options, function (error, response, body) { + if (error) throw new Error(error); + + console.log(body); +}); +``` + +## Chaining Batch Queries with fallbacks + +When you need to run an extra query based on how a chaining query finished, Batch Queries enable you to define onerror and onsuccess fallbacks. This powerful feature opens a huge range of possibilities, for instance: + +- You can create jobs periodically in order to get updated data and create a new table where you can check the status of your tables. + +For this example, you can create the following job: + +```bash +curl -X POST -H "Content-Type: application/json" -d '{ + "query": { + "query": [{ + "query": "UPDATE nasdaq SET price = '$100.00' WHERE company = 'CARTO'", + "onsuccess": "UPDATE market_status SET status = 'updated', updated_at = NOW() WHERE table_name = 'nasdaq'" + "onerror": "UPDATE market_status SET status = 'outdated' WHERE table_name = 'nasdaq'" + }] + } +}' "http://{username}.carto.com/api/v2/sql/job" +``` + +If query finishes successfully, then onsuccess fallback will be fired. Otherwise, onerror will be fired. You can define fallbacks per query: + +```bash +curl -X POST -H "Content-Type: application/json" -d '{ + "query": { + "query": [{ + "query": "UPDATE nasdaq SET price = '$101.00' WHERE company = 'CARTO'", + "onsuccess": "UPDATE market_status SET status = 'updated', updated_at = NOW() WHERE table_name = 'nasdaq'", + "onerror": "UPDATE market_status SET status = 'outdated' WHERE table_name = 'nasdaq'" + }, { + "query": "UPDATE down_jones SET price = '$100.00' WHERE company = 'Esri'", + "onsuccess": "UPDATE market_status SET status = 'updated', updated_at = NOW() WHERE table_name = 'down_jones'", + "onerror": "UPDATE market_status SET status = 'outdated' WHERE table_name = 'down_jones'" + }] + } +}' "http://{username}.carto.com/api/v2/sql/job" +``` + +...at the job level.. + +```bash +curl -X POST -H "Content-Type: application/json" -d '{ + "query": { + "query": [{ + "query": "UPDATE nasdaq SET price = '$101.00' WHERE company = 'CARTO'" + }, { + "query": "UPDATE down_jones SET price = '$100.00' WHERE company = 'Esri'" + }], + "onsuccess": "UPDATE market_status SET status = 'updated', updated_at = NOW()", + "onerror": "UPDATE market_status SET status = 'outdated'" + } +}' "http://{username}.carto.com/api/v2/sql/job" +``` + +If a query of a job fails (and onerror fallbacks for that query and job are defined), then Batch Queries runs the first fallback for that query. The job fallback runs next and sets the job as failed. Remaining queries will not be executed. Furthermore, Batch Queries will run the onsuccess fallback at the job level, if (and only if), every query has finished successfully. + +### Templates + +Batch Queries provide a simple way to get the error message and the job identifier to be used in your fallbacks, by using the following templates: + + - `<%= error_message %>`: will be replaced by the error message raised by the database. + - `<%= job_id %>`: will be replaced by the job identifier that Batch Queries provides. + +This is helpful when you want to save error messages into a table: + +```bash +curl -X POST -H "Content-Type: application/json" -d '{ + "query": { + "query": [{ + "query": "UPDATE wrong_table SET price = '$100.00' WHERE company = 'CARTO'" + }], + "onerror": "INSERT INTO errors_log (job_id, error_message, date) VALUES ('<%= job_id %>', '<%= error_message %>', NOW())" + } +}' "http://{username}.carto.com/api/v2/sql/job" +``` + +More templates are coming soon. + +## Fetching Job Results + +In some scenarios, you may need to fetch the output of a job. If that is the case, wrap the query with `SELECT * INTO`, or `CREATE TABLE AS`. The output is stored in a new table in your database. For example, if using the query `SELECT * FROM airports`: + +1. Wrap the query `SELECT * INTO job_result FROM (SELECT * FROM airports) AS job` + +2. [Create a job](#create-a-job), as described previously + +3. Once the job is done, fetch the results through the [CARTO SQL API](https://carto.com/docs/carto-engine/sql-api/), `SELECT * FROM job_result` + +**Note:** If you need to create a map or analysis with the new table, use the [CDB_CartodbfyTable function](https://github.com/CartoDB/cartodb-postgresql/blob/master/doc/cartodbfy-requirements.rst). + +## Best Practices + +For best practices, follow these recommended usage notes when using Batch Queries: + +- Batch Queries are recommended for INSERT, UPDATE, and CREATE queries that manipulate and create new data, such as creating expensive indexes, applying updates over large tables, and creating tables from complex queries. Batch queries have no effect for SELECT queries that retrieve data but do not store the results in a table. For example, running a batch query using `SELECT * from my_dataset` will not produce any results. + +- Batch Queries are not intended for large query payloads (e.g: inserting thousands of rows), use the [Import API](https://carto.com/docs/carto-engine/import-api/) for this type of data management. + +- There is a limit of 16kb per job. The following error message appears if your job exceeds this size: + + `Your payload is too large. Max size allowed is 16384 (16kb)` diff --git a/doc/copy_queries.md b/doc/copy_queries.md new file mode 100644 index 0000000..b061731 --- /dev/null +++ b/doc/copy_queries.md @@ -0,0 +1,249 @@ +# Copy Queries + +Copy queries allow you to use the [PostgreSQL copy command](https://www.postgresql.org/docs/10/static/sql-copy.html) for efficient streaming of data to and from CARTO. + +The support for copy is split across two API end points: + +* `https://{username}.carto.com/api/v2/sql/copyfrom` for uploading data to CARTO +* `https://{username}.carto.com/api/v2/sql/copyto` for exporting data out of CARTO + +## Copy From + +The PostgreSQL `COPY` command is extremely fast, but requires very precise inputs: + +* A `COPY` command that describes the table and columns of the upload file, and the format of the file. +* An upload file that exactly matches the `COPY` command. + +If the `COPY` command, the supplied file, and the target table do not all match, the upload will fail. + +"Copy from" copies data "from" your file, "to" CARTO. "Copy from" uses chunked encoding (`Transfer-Encoding: chunked`) to stream an upload file to the server. This avoids limitations around file size and any need for temporary storage: the data travels from your file straight into the database. + +Parameter | Description +--- | --- +`api_key` | a write-enabled key +`q` | the `COPY` command to load the data + +**The actual COPY file content must be sent as the body of the POST request.** + +Composing a chunked POST is moderately complicated, so most developers will use a tool or scripting language to upload data to CARTO via "copy from". + +### Example + +#### Table preparation + +First of all, you'll need to have **a table with the right schema** to copy your data into. For a table to be readable by CARTO, it must have a minimum of three columns with specific data types: + +* `cartodb_id`, a `serial` primary key +* `the_geom`, a `geometry` in the ESPG:4326 projection (aka long/lat) +* `the_geom_webmercator`, a `geometry` in the ESPG:3857 projection (aka web mercator) + +Creating a new CARTO table with all the right triggers and columns can be tricky, so here is an example: + +```sql +-- Create the table using the *required* columns and a couple more +CREATE TABLE upload_example ( + the_geom geometry, + name text, + age integer +); + +-- adds the 'cartodb_id' and 'the_geom_webmercator' +-- adds the required triggers and indexes +SELECT CDB_CartodbfyTable(current_schema, 'upload_example'); +``` + +you can get these queries executed directly through the SQL API this way, just by replacing the `{username}` and `{api_key}` by yours: + +```sh +# Table creation +curl -v "https://{user}.carto.com/api/v2/sql?api_key={api_key}&q=CREATE+TABLE+upload_example+(the_geom+geometry,name+text,age+integer)" + +# Cartodbfy table (optional, but allows for table registartion in Builder dashboard) +curl -v "http://{user}.carto.com/api/v2/sql?api_key={api_key}&q=SELECT+CDB_CartodbfyTable(current_schema,'upload_example')" +``` + + +Now you are ready to upload your file. Suppose you have a CSV file like this: + + the_geom,name,age + SRID=4326;POINT(-126 54),North West,89 + SRID=4326;POINT(-96 34),South East,99 + SRID=4326;POINT(-6 -25),Souther Easter,124 + +The `COPY` command to upload this file needs to specify the file format (CSV), the fact that there is a header line before the actual data begins, and to enumerate the columns that are in the file so they can be matched to the table columns. + + COPY upload_example (the_geom, name, age) + FROM STDIN WITH (FORMAT csv, HEADER true) + +The `FROM STDIN` option tells the database that the input will come from a data stream, and the SQL API will read our uploaded file and use it to feed the stream. + +To actually run upload, you will need a tool or script that can generate a chunked POST, so here are a few examples in different languages. + +#### CURL Example + +The [curl](https://curl.haxx.se/) utility makes it easy to run web requests from the command-line, and supports chunked POST upload, so it can feed the `copyfrom` end point. + +Assuming that you have already created the table, and that the CSV file is named "upload_example.csv": + + curl -X POST \ + -H "Transfer-Encoding: chunked" \ + -H "Content-Type: application/octet-stream" \ + --data-binary @upload_example.csv \ + "https://{username}.carto.com/api/v2/sql/copyfrom?api_key={api_key}&q=COPY+upload_example+(the_geom,+name,+age)+FROM+STDIN+WITH+(FORMAT+csv,+HEADER+true)" + +To upload a larger file, using compression for a faster transfer, first compress the file, and then upload it with the content encoding set: + + curl -X POST \ + -H "Content-Encoding: gzip" \ + -H "Transfer-Encoding: chunked" \ + -H "Content-Type: application/octet-stream" \ + --data-binary @upload_example.csv.gz \ + "https://{username}.carto.com/api/v2/sql/copyfrom?api_key={api_key}&q=COPY+upload_example+(the_geom,+name,+age)+FROM+STDIN+WITH+(FORMAT+csv,+HEADER+true)" + + +#### Python Example + +The [Requests](http://docs.python-requests.org/en/master/user/quickstart/) library for HTTP makes doing a file upload relatively terse. + +```python +import requests + +api_key = {api_key} +username = {api_key} +upload_file = 'upload_example.csv' +q = "COPY upload_example (the_geom, name, age) FROM STDIN WITH (FORMAT csv, HEADER true)" + +def read_in_chunks(file_object, chunk_size=8192): + while True: + data = file_object.read(chunk_size) + if not data: + break + yield data + +url = "https://%s.carto.com/api/v2/sql/copyfrom" % username +with open(upload_file, 'rb') as f: + resp = requests.post(url, + params={'api_key': api_key, 'q': q}, + data=read_in_chunks(f), + headers={'Content-Type': 'application/octet-stream'}, + stream=True) +print("status: %d" % resp.status_code) +data = resp.json() +print(data) +``` + +A slightly more sophisticated script could read the headers from the CSV and compose the `COPY` command on the fly. However, you will still need to make sure that the table schema (`CREATE TABLE`) is suitable for receiving the data from the `COPY` query. + +### CSV files and column ordering + +When using the **CSV format, please note that [PostgreSQL ignores the header](https://www.postgresql.org/docs/10/static/sql-copy.html)**. + +> HEADER +> +> Specifies that the file contains a header line with the names of each column in the file. On output, the first line contains the column names from the table, and **on input, the first line is ignored**. This option is allowed only when using CSV format. + +If the ordering of the columns does not match the table definition, you must specify it as part of the query. + +For example, if your table is defined as: + +```sql +CREATE TABLE upload_example ( + the_geom geometry, + name text, + age integer +); +``` + +but your CSV file has the following structure (note `name` and `age` columns are swapped): + +```csv +#the_geom,age,name +SRID=4326;POINT(-126 54),89,North West +SRID=4326;POINT(-96 34),99,South East +SRID=4326;POINT(-6 -25),124,Souther Easter +``` + +your query has to specify the correct ordering, regardless of the header in the CSV: + +```sql +COPY upload_example (the_geom, age, name) FROM stdin WITH (FORMAT csv, HEADER true); +``` + +### Response Format + +A successful upload will return with status code 200, and a small JSON with information about the upload. + + {"time":0.046,"total_rows":10} + +A failed upload will return with status code 400 and a larger JSON with the PostgreSQL error string, and a stack trace from the SQL API. + + {"error":["Unexpected field"]} + +## Copy To + +"Copy to" copies data "to" your desired output file, "from" CARTO. + +Using the `copyto` end point to extract data bypasses the usual JSON formatting applied by the SQL API, so it can dump more data, faster. However, it has the restriction that it will only output in a handful of formats: + +* PgSQL [text format](https://www.postgresql.org/docs/10/static/sql-copy.html#id-1.9.3.52.9.2), +* [CSV](https://www.postgresql.org/docs/10/static/sql-copy.html#id-1.9.3.52.9.3), and +* PgSQL [binary format](https://www.postgresql.org/docs/10/static/sql-copy.html#id-1.9.3.52.9.4). + +"Copy to" is a simple HTTP GET end point, so any tool or language can be easily used to download data, supplying the following parameters in the URL. + +Parameter | Description +--- | --- +`api_key` | your API key for reading non-public tables +`q` | the `COPY` command to extract the data +`filename` | filename to put in the "Content-disposition" HTTP header, useful for tools that automatically save the download to a file name + + +### CURL Example + +The SQL to start a "copy to" can specify + +* a table to read, +* a table and subset of columns to read, or +* an arbitrary SQL query to execute and read. + +For our example, we'll read back just the three columns we originally loaded: + + COPY upload_example (the_geom, name, age) TO stdout WITH (FORMAT csv, HEADER true) + +The SQL needs to be URL-encoded before being embedded in the CURL command, so the final result looks like this: + + curl \ + --output upload_example_dl.csv \ + --compressed \ + "http://{username}.carto.com/api/v2/sql/copyto?q=COPY+upload_example+(the_geom,name,age)+TO+stdout+WITH(FORMAT+csv,HEADER+true)&api_key={api_key}" + +### Python Example + +The Python to "copy to" is very simple, because the HTTP call is a simple get. The only complexity in this example is at the end, where the result is streamed back block-by-block, to avoid pulling the entire download into memory before writing to file. + +```python +import requests + +api_key = {api_key} +username = {api_key} +download_filename = 'download_example.csv' +q = "COPY upload_example (the_geom, name, age) TO stdout WITH (FORMAT csv, HEADER true)" + +# request the download +url = "http://%s.carto.com/api/v2/sql/copyto" % username +r = requests.get(url, params={'api_key': api_key, 'q': q}, stream=True) +r.raise_for_status() + +with open(download_filename, 'wb') as handle: + for block in r.iter_content(1024): + handle.write(block) +print("Downloaded to: %s" % savefilename) +``` + +## Limits + +There's a **5 hours timeout** limit for the `/copyfrom` and `/copyto` endpoints. The idea behind is that, in practice, COPY operations should not be limited by your regular query timeout. + +Aside, you cannot exceed your **database quota** in `/copyfrom` operations. Trying to do so will result in a `DB Quota exceeded` error, and the `COPY FROM` transaction will be rolled back. + +The maximum payload size of a `/copyfrom` that can be made in a single `POST` request is **limited to 2 GB**. Any attempt exceeding that size will result in a `COPY FROM maximum POST size of 2 GB exceeded` error, and again the whole transaction will be rolled back. diff --git a/doc/creating_tables.md b/doc/creating_tables.md new file mode 100644 index 0000000..6953b79 --- /dev/null +++ b/doc/creating_tables.md @@ -0,0 +1,55 @@ +# Creating Tables with the SQL API + +[Writing data to your CARTO account](https://carto.com/docs/carto-engine/sql-api/making-calls#write-data-to-your-carto-account) enables you to manage data through SQL queries, it does not automatically connect tables as datasets to _Your datasets_ dashboard in CARTO. + +You must apply the `CDB_CartodbfyTable`function to a target table in order to create and display connected datasets in your account. This additional step of "CARTOfying" data is the process of converting an arbitrary PostgreSQL table into a valid CARTO table, and registering it into the system so that it can be used in the graphical user interface, and the CARTO Engine, to generate maps and analysis. + +## Create Tables + +To create a visible table in CARTO, run the following SQL query with the SQL API: + +```bash +CREATE TABLE {table_name} + ( + {column1} {data type}, + {column2} {data type}, + {column3} {data type}, + ... + ); +``` + +While this begins the process of creating the structure for the table, it is still not visible in your dashboard. Run the following request to make the table visible. + +```bash +SELECT cdb_cartodbfytable({table_name}); +``` + +**Tip:** If you are an developer using an Enterprise account, you must also include the organization username as part of the request. For example: + +```bash +SELECT cdb_cartodbfytable({org_username}, {table_name}); +``` + +The table is created and added as a connected dataset in _Your datasets_ dashboard. Refresh your browser to ensure that you can visualize it in your account. Once a table is connected to _Your datasets_ dashboard in CARTO, any modifications that you apply to your data through the SQL API are automatically updated. + +## Rename Tables + +To rename a connected dataset in _Your datasets_ dashboard, run the following SQL query with the SQL API: + +```bash +ALTER TABLE {table_name} RENAME to {renamed table_name}; +``` + +It may take a few seconds for the connected table to appear renamed. Refresh your browser to ensure that you can visualize the changes in _Your datasets_ dashboard. + +## Remove a Table + +If you remove a table, **any maps using the connected dataset will be affected**. The deleted dataset cannot be recovered. Even if you create a new table with the same name as a removed table, CARTO still internalizes it as a different table. + +To remove a connected dataset from _Your datasets_ dashboard, run the following SQL query with the SQL API: + +```bash +DROP TABLE {table_name}; +``` + +This removes the connected table from _Your datasets_ dashboard. Refresh your browser to ensure that the connected dataset was removed. diff --git a/doc/handling_geospatial_data.md b/doc/handling_geospatial_data.md new file mode 100644 index 0000000..f0cebf0 --- /dev/null +++ b/doc/handling_geospatial_data.md @@ -0,0 +1,63 @@ +# Handling Geospatial Data + +Handling geospatial data through the SQL API is easy. By default, *the_geom* is returned straight from the database, in a format called Well-Known Binary. There are a handful of ways you can transform your geometries into more useful formats. + +The first is to use the format=GeoJSON method described above. Others can be handled through your SQL statements directly. For example, enclosing your the_geom in a function called [ST_AsGeoJSON](http://www.postgis.org/documentation/manual-svn/ST_AsGeoJSON.html) will allow you to use JSON for your data but a GeoJSON string for your geometry column only. Alternatively, using a the [ST_AsText](http://www.postgis.org/documentation/manual-svn/ST_AsGeoJSON.html) function will return your geometry as Well-Known Text. + +### ST_AsGeoJSON + +#### Call + +```bash +https://{username}.carto.com/api/v2/sql?q=SELECT cartodb_id,ST_AsGeoJSON(the_geom) as the_geom FROM {table_name} LIMIT 1 +``` + +#### Result + +```javascript +{ + time: 0.003, + total_rows: 1, + rows: [ + { + cartodb_id: 1, + the_geom: "{"type":"Point","coordinates":[-97.3349,35.4979]}" + } + ] +} +``` + +### ST_AsText + +#### Call + +```bash +https://{username}.carto.com/api/v2/sql?q=SELECT cartodb_id,ST_AsText(the_geom) FROM {table_name} LIMIT 1 +``` + +#### Result + +```javascript +{ + time: 0.003, + total_rows: 1, + rows: [ + { + cartodb_id: 1, + the_geom: "POINT(-74.0004162 40.6920918)", + } + ] +} +``` + +More advanced methods exist in the PostGIS library to extract meaningful data from your geometry. Explore the PostGIS documentation and get familiar with functions such as, [ST_XMin](http://www.postgis.org/docs/ST_XMin.html), [ST_XMax](http://www.postgis.org/docs/ST_XMax.html), [ST_AsText](http://www.postgis.org/docs/ST_AsText.html), and so on. + +All data returned from *the_geom* column is in WGS 84 (EPSG:4326). You can change this quickly on the fly, by using SQL. For example, if you prefer geometries using the Hanoi 1972 (EPSG:4147) projection, use [ST_Transform](http://www.postgis.org/docs/ST_Transform.html), + +### ST_Transform + +```bash +https://{username}.carto.com/api/v2/sql?q=SELECT ST_Transform(the_geom,4147) FROM {table_name} LIMIT 1 +``` + +CARTO also stores a second geometry column, *the_geom_webmercator*. We use this internally to build your map tiles as fast as we can. In the user-interface it is hidden, but it is visible and available for use. In this column, we store a reprojected version of all your geometries using Web Mercator (EPSG:3857). diff --git a/doc/libraries_support.md b/doc/libraries_support.md new file mode 100644 index 0000000..9749d2e --- /dev/null +++ b/doc/libraries_support.md @@ -0,0 +1,32 @@ +# Libraries in Different Languages + +To make things easier for developers, we provide client libraries for different programming languages and caching functionalities. + +**Note:** These libraries are externally developed and maintained. Use caution when using libraries in different languages, as some of these resources may be out-of-date. + +- **R** + To help more researchers use CARTO to drive their geospatial data, we have released the R client library. [Fork it on GitHub!](https://github.com/Vizzuality/cartodb-r) + +- **NODE.js** + This demo app authenticates with your CARTO and shows how to perform read and write queries using the SQL API. [Fork it on GitHub!](https://github.com/Vizzuality/cartodb-nodejs) + +- **PHP** + The PHP library provides a wrapper around the SQL API to get PHP objects straight from SQL calls to CARTO. [Fork it on GitHub!](https://github.com/Vizzuality/cartodbclient-php) + +- **PYTHON** + Provides API Key access to SQL API. [Fork it on GitHub!](https://github.com/vizzuality/cartodb-python) + +- **JAVA** + Very basic example of how to access CARTO SQL API. [Fork it on GitHub!](https://github.com/cartodb/cartodb-java-client) + +- **NET** + .NET library for authenticating with CARTO using an API Key, based on work started by [The Data Republic](http://www.thedatarepublic.com/). [Fork it on GitHub!](https://github.com/thedatarepublic/CartoDBClientDotNET) + +- **Clojure** + Clojure library for authenticating with CARTO, maintained by [REDD Metrics](http://www.reddmetrics.com/). [Fork it on GitHub!](https://github.com/reddmetrics/cartodb-clj) + +- **iOS** + Objective-C library for interacting with CARTO in native iOS applications. [Fork it on GitHub!](https://github.com/jmnavarro/cartodb-objectivec-client) + +- **Golang** + A Go client for the CARTO SQL API that supports authentication using an API key. [Fork it on GitHub!](https://github.com/agonzalezro/cartodb_go) diff --git a/doc/making_calls.md b/doc/making_calls.md new file mode 100644 index 0000000..37d3383 --- /dev/null +++ b/doc/making_calls.md @@ -0,0 +1,203 @@ +# Making Calls to the SQL API + +CARTO is based on the rock solid PostgreSQL database. All of your tables reside in a single database, which means you can perform complex queries joining tables, or carrying out geospatial operations. The best place to learn about PostgreSQL's SQL language is the [official documentation](http://www.postgresql.org/docs/9.1/static/). + +CARTO is also based on PostGIS, so you can view the [official PostGIS reference](https://postgis.net/documentation/) to know what functionality we support in terms of geospatial operations. All of our tables include a column called *the_geom,* which is a geometry field that indexes geometries in the EPSG:4326 (WGS 1984) coordinate system. All tables also have an automatically generated and updated column called *the_geom_webmercator*. We use the column internally to quickly create tiles for maps. + + +## URL Endpoints + +All SQL API requests to your CARTO account should follow this general pattern: + +#### SQL Query Example + +```bash +https://{username}.carto.com/api/v2/sql?q={SQL statement} +``` + +If you encounter errors, double-check that you are using the correct account name, and that your SQL statement is valid. A simple example of this pattern is conducting a count of all the records in your table: + +#### Count Example + +```bash +https://{username}.carto.com/api/v2/sql?q=SELECT count(*) FROM {table_name} +``` + +#### Result + +```javascript +{ + time: 0.007, + total_rows: 1, + rows: [ + { + count: 4994 + } + ] +} +``` + +Finally, remember that in order to use the SQL API, either your table must be public, or you must be [authenticated](http://docs.carto.com/carto-engine/sql-api/authentication/#authentication) using API Keys. + + +## POST and GET + +The CARTO SQL API is setup to handle both GET and POST requests. You can test the GET method directly in your browser. Below is an example of a jQuery SQL API request to CARTO: + +### jQuery + +#### Call + +```javascript +$.getJSON('https://{username}.carto.com/api/v2/sql/?q='+sql_statement, function(data) { + $.each(data.rows, function(key, val) { + // do something! + }); +}); +``` + +By default, GET requests work from anywhere. In CARTO, POST requests work from any website as well. We achieve this by hosting a cross-domain policy file at the root of all of our servers. This allows you the greatest level of flexibility when developing your application. + + +## Response Formats + +The SQL API accepts many output formats that can be useful to export data, such as: + +- GPKG +- CSV +- SHP +- SVG +- KML +- SpatiaLite +- GeoJSON + +The most common response format used is JSON. For example, if you are building a web-application, the lightweight JSON format allows you to quickly integrate data from the SQL API. This section focuses on the call and response functions for generating the JSON output format. + +### JSON + +#### Call + +```bash +https://{username}.carto.com/api/v2/sql?q=SELECT * FROM {table_name} LIMIT 1 +``` + +#### Result + +```javascript +{ + time: 0.006, + total_rows: 1, + rows: [ + { + year: " 2011", + month: 10, + day: "11", + the_geom: "0101000020E610...", + cartodb_id: 1, + the_geom_webmercator: "0101000020110F000..." + } + ] +} +``` + +Alternatively, you can use the [GeoJSON specification](http://www.geojson.org/geojson-spec.html) to return data from the API. To do so, simply supply the `format` parameter as GeoJSON: + +### GeoJSON + +#### Call + +```bash +https://{username}.carto.com/api/v2/sql?format=GeoJSON&q=SELECT * FROM {table_name} LIMIT 1 +``` + +#### Result + +```javascript +{ + type: "FeatureCollection", + features: [ + { + type: "Feature", + properties: { + year: " 2011", + month: 10, + day: "11", + cartodb_id: 1 + }, + geometry: { + type: "Point", + coordinates: [ + -97.335, + 35.498 + ] + } + } + ] +} +``` + +## Output Filename + +To customize the output filename, add the `filename` parameter to your URL: + +#### Call + +```bash +https://{username}.carto.com/api/v2/sql?filename={custom_filename}&q=SELECT * FROM {table_name} LIMIT 1 +``` + + +## Getting Table Information + +Currently, there is no public method to access your table schemas. The simplest way to retrieve table structure is to access the first row of the data, + +#### Call + +```bash +https://{username}.carto.com/api/v2/sql?q=SELECT * FROM {table_name} LIMIT 1 +``` + + +## Response Errors + +To help you debug your SQL queries, the CARTO SQL API returns the full error provided by PostgreSQL, as part of the JSON response. Error responses appear in the following format, + +#### Result + +```javascript +{ + error: [ + "syntax error at or near "LIMIT"" + ] +} +``` + +**WARNING**: If the database finds an error after it has started streaming the response, the error header will still be set but the HTTP status code will be **200**. + +You can use these errors to help understand your SQL. If you encounter errors executing SQL, either through CARTO Builder, or through the SQL API, it is suggested to Google search the error for independent troubleshooting. + +## Write Data to your CARTO Account + +When writing data to your CARTO account, you are executing SQL queries to manage data in a table. Performing inserts or updates on your data is achieved by using your [API Key](https://carto.com/docs/carto-engine/sql-api/authentication/). Simply supply a well-formatted SQL [INSERT](http://www.postgresql.org/docs/9.1/static/sql-insert.html) or [UPDATE](http://www.postgresql.org/docs/9.1/static/sql-update.html) statement for your table, along with the api_key parameter for your account. + +**Tip:** All requests should be private, as anyone with your API Key will be able to modify your tables. + +A well-formatted SQL insert statement means that all of the columns that you want to insert to your data already exist in your table, and all the values for those columns are the correct type (quoted string, unquoted string for geoms and dates, or numbers). + +### Insert + +#### Call + +```bash +https://{username}.carto.com/api/v2/sql?q=INSERT INTO test_table (column_name, column_name_2, the_geom) VALUES ('this is a string', 11, ST_SetSRID(ST_Point(-110, 43),4326))&api_key={api_key} +``` + +Updates are just as simple. The following example displays how to update a row based on the value of the `cartodb_id` column. + +### Update + +#### Call + +```bash +https://{username}.carto.com/api/v2/sql?q=UPDATE test_table SET column_name = 'my new string value' WHERE cartodb_id = 1 &api_key={api_key} +``` diff --git a/doc/metrics.md b/doc/metrics.md new file mode 100644 index 0000000..c4a8e63 --- /dev/null +++ b/doc/metrics.md @@ -0,0 +1,19 @@ +CartoDB-SQL-API metrics +======================= + +## Timers +- **sqlapi.query**: time to return a query resultset from the API, splitted into: + + **sqlapi.query.init**: time to prepare params from the request + + **sqlapi.query.getDBParams**: time to retrieve the database connection params + + **sqlapi.query.authenticate**: time to determine if request is authenticated + + **sqlapi.query.setDBAuth**: time to set the authenticated connection params + + **sqlapi.query.queryExplain**: time to retrieve affected tables from the query + + **sqlapi.query.eventedQuery**: (pg) Time to prepare and execute the query + + **sqlapi.query.beforeSink**: time to start sending the response. + + **sqlapi.query.gotRows**: Time until it finished processing all rows in the resultset. + + **sqlapi.query.generate**: Time to prepare and generate a response from ogr + + **sqlapi.query.finish**: time to handle an exception + +## Counters +- **sqlapi.query.success**: number of successful queries +- **sqlapi.query.error**: number of failed queries diff --git a/doc/query_optimizations.md b/doc/query_optimizations.md new file mode 100644 index 0000000..7517d2e --- /dev/null +++ b/doc/query_optimizations.md @@ -0,0 +1,26 @@ +# Query Optimizations + +There are some tricks to consider when using the SQL API that might make your application a little faster. + +* Only request the fields you need. Selecting all columns will return a full version of your geometry in *the_geom*, as well as a reprojected version in *the_geom_webmercator* +* Use PostGIS functions to simplify and filter out unneeded geometries when possible. One very handy function is, [ST_Simplify](http://www.postgis.org/docs/ST_Simplify.html) +* Remember to build indexes that will speed up some of your more common queries. For details, see [Creating Indexes](#creating-indexes) +* Use *cartodb_id* to retrieve specific rows of your data, this is the unique key column added to every CARTO table. For a sample use case, view the [_Faster data updates with CARTO](https://carto.com/blog/faster-data-updates-with-cartodb/) blogpost + +## Creating Indexes + +In order to better improve map performance, advanced users can use the SQL API to add custom indexes to their data. Creating indexes is useful if you have a large dataset with filtered data. By indexing select data, you are improving the performance of the map and generating the results faster. The index functionality is useful in the following scenarios: + +- If you are filtering a dataset by values in one or a more columns +- If you are regularly querying data through the SQL API, and filtering by one or a more columns +- If you are creating Torque maps on very large datasets. Since Torque maps are based on time-sensitive data (i.e. a date or numeric column), creating an index on the time data is optimal + +Indexed data is typically a single column representing filtered data. To create a single column index, apply this SQL query to your dataset: + +{% highlight bash %} +CREATE INDEX idx_{DATASET NAME}_{COLUMN_NAME} ON {DATASET_NAME} ({COLUMN_NAME}) +{% endhighlight %} + +**Tip:** You can also apply more advanced, multi-column indexes. Please review the full documentation about [PostgreSQL Indexes](http://www.postgresql.org/docs/9.1/static/sql-createindex.html) before proceeding. + +**Note:** Indexes are allocated towards the amount of data storage associated with your account. Be mindful when creating custom indexes. Note that indexes automatically generated by CARTO are _not_ counted against your quota. For example, `the_geom` and `cartodb_id` columns. These columns are used to index geometries for your dataset and are not associated with storage. diff --git a/doc/tips_and_tricks.md b/doc/tips_and_tricks.md new file mode 100644 index 0000000..cfa643e --- /dev/null +++ b/doc/tips_and_tricks.md @@ -0,0 +1,97 @@ +# Other Tips and Questions + +## What does CARTO do to prevent SQL injection? + +CARTO uses the database access mechanism for security. Every writable connection is verified by an API Key. If you have the correct API Key, you can write-access to the database. If you do not have the correct API Key, your client is "logged in" as a low privilege user, and you have read-only access to the database (if the database allows you to read). + +SQL injection works by tricking a database user, so that running a query retrieves database wide results, even though the database is protected. + +Because CARTO enforces roles and access at the database level, the idea of a "SQL injection attack" is not possible with CARTO. Injection is possible, but clients will still run into our security wall at the database level. The SQL API already lets you _attempt_ to run any query you want. The database will reject your SQL API request if it finds your user/role does not have the requisite permissions. In other words, you can ask any question of the database you like; the CARTO Engine does not guarantee it will be answered. + +If a user's API Key found its way out into the wild, that could be a problem, but it is not something CARTO can prevent. _This is why it is very important for all CARTO users to secure their API Keys_. In the event a user's API Key is compromised, the user (or the CARTO Enterprise administrator), can regenerate the API Key in their account settings. + +**Note:** While the SQL API is SQL injection secure, if you build additional layers to allow another person to run queries (i.e., building a proxy so that others can indirectly perform authenticated queries through the SQL API), the security of those newly added layers are the responsibility of the creator. + +## What levels of database access can roles/users have? + +There are three levels of access with CARTO: + +1. __API Key level:__ Do whatever you want in your account on the tables you own (or have been shared with you in Enterprise/multi-user accounts). +2. __"publicuser" level:__ Do whatever has been granted to you. The publicuser level is normally read-only, but you could GRANT INSERT/UPDATE/DELETE permissions to publicuser if needed for some reason - for API Key-less write operations. Use with caution. +3. __postgres superadmin level:__ This third access level, the actual PostgreSQL system user, is only accessible from a direct database connection via the command line, which is only available currently via [CARTO On-Premises](https://carto.com/on-premises/). + +## If a user has write access and makes a `DROP TABLE` query, is that data gone? + +Yes. Grant write access with caution and keep backups of your data elsewhere / as duplicate CARTO tables. + +## Is there a permission available where a user can write but not `DROP` or `DELETE`? + +Yes. Create the table, and GRANT INSERT/UPDATE to the user. + +## Is there an actual PostgreSQL account for each CARTO login/username? + +Yes, there is. Unfortunately, the names are different - though there is a way to determine the name of the PostgreSQL user account. Every CARTO user gets their own PostgreSQL database. But there is a system database too, with the name mappings in `username` and `database_name` columns. `database_name` is the name of the database that user belongs to. It will be `cartodb_user_ID`. `id` holds long hashkey. The `database_name` is derived from this ID hash too, but in case of an Enterprise/multi-user account it will come from the user ID of the owner of the organization - and `database_name` will hold the same value for every user in an Enterprise/multi-user account. + +You can also just do `select user` using the SQL API (without an API Key to get the publicuser name and with an API Key to get the CARTO user's PostgreSQL user name), to determine the name of the corresponding PostgreSQL user. + +## Can I configure my CARTO database permissions exactly the same way I do on my own PostgreSQL instance? + +Yes, through using GRANT statements to the SQL API. There are a few caveats to be aware of, including the aforementioned naming differences. Also, you will be limited to permissions a user has with their own tables. Users do not have PostgreSQL superuser privileges. So they cannot be creating languages, or C functions, or anything that requires superuser or CREATEUSER privileges. + +## How can I export CARTO datasets with the SQL API? + +You can use the SQL API to run any query and export the results in different formats, such as a CSV or GeoPackage. This is helpful for accessing your datasets offline. + +**Note:** View the [response formats](https://carto.com/docs/carto-engine/sql-api/making-calls/#response-formats) that are available with the SQL API and ensure that your dataset does not exceed the maximum file size for [SQL API exports](https://carto.com/docs/faqs/carto-engine-usage-limits/#sql-api-limits). + +### Export Datasets as a GeoPackage + +You can easily export CARTO datasets using the [GeoPackage](http://www.geopackage.org/) file format, which is an "open, standards-based, platform-independent, portable, self-describing, compact format for transferring geospatial information- ©". A .gpkg file itself is a [type](http://www.geopackage.org/spec/#table_column_data_types) of database, more complex than a plain file. + +_**Tip:** GeoPackage is the recommended format since it exports your dataset in smaller pieces; typically avoiding error messages that might appear due to long file names and/or large datasets._. If exporting a map with the SQL API, the `GPKG` format does not include any visualization or styling, which helps reduce the file size during the export process. + +```bash +https://{username}.carto.com/api/v2/sql?q=SELECT * FROM {table_name}&format=gpkg&filename={file_name}.gpkg +``` + +The response is `file_name.gpkg` that you can download for use offline. + +#### Example of `GPKG` File + +- From your Internet browser, copy and paste the following link into a new tab and press Enter. + +```bash +https://builder-demo.carto.com/api/v2/sql?q=SELECT+*+FROM+san_francisco_airbnbs&format=gpkg&filename=san_francisco_airbnbs.gpkg +``` + +A gpkg file is downloaded, based on your web browser process, and available for use offline. + + +### Download Datasets as a URL + +You can use your table URL to run a response query and export downloads in different formats. For example, the following sample code shows the CSV export format for an SQL API request. + + +```bash +https://{username}.carto.com/api/v2/sql?format=csv&q=SELECT+*+FROM+tm_world_borders_sim +``` + +The response creates a direct dataset URL that you can download for use offline. + +### Why can't I see my created tables in my CARTO account? + +The SQL API automatically displays tables in CARTO if you follow these steps: + +- Create a table that has been "cartodbfied", which prepares your table to be compatible with CARTO. View [Creating Tables with the SQL API](https://carto.com/docs/carto-engine/sql-api/creating-tables/#creating-tables-with-the-sql-api) to learn about this function. + +- After creating your "cartodbfied" table, you must login to your CARTO account and open _Your datasets_ dashboard. Logging in initiates a check between the database and your account. + +**Note:** There is an expected refresh time while the database is checking your account and your tables may not appear at this time, especially if there are a lot of tables or tables with a large amount of rows. + +- Once the database updates, CARTO will display your created or changed tables as connected datasets! + +### What happens if I remove a table that is used in an existing map? + +If you [drop](https://carto.com/docs/carto-engine/sql-api/creating-tables/#remove-a-table) a table using the SQL API, be mindful that there is no warning that the table may be used in an existing map. This is by design. Any maps using a removed table will be missing data and thus, will be deleted. + +If you are unsure about which tables are connected to maps, it is suggested to remove tables from _Your datasets_ dashboard in CARTO, which automatically notifies you of any connected maps in use. diff --git a/doc/version.md b/doc/version.md new file mode 100644 index 0000000..2df8d75 --- /dev/null +++ b/doc/version.md @@ -0,0 +1,3 @@ +# API Version Number + +Ensure that you are using the [latest version](https://github.com/CartoDB/CartoDB-SQL-API) of our SQL API. For example, you can check that you are using **Version 2** by looking at your request URLS. They should all contain **/v2/** in the URLs as follows, `https://{username}.carto.com/api/v2/sql` diff --git a/docker-bash.sh b/docker-bash.sh new file mode 100755 index 0000000..cf98130 --- /dev/null +++ b/docker-bash.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +docker run -it -v `pwd`:/srv carto/${1:-nodejs10-xenial-pg101:postgis-2.4.4.5} bash diff --git a/docker-test.sh b/docker-test.sh new file mode 100755 index 0000000..0c0fa5a --- /dev/null +++ b/docker-test.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +usage() { + echo "Usage: $0 [nodejs6|nodejs10]" + exit 1 +} + +echo "$0 $1" + +NODEJS_VERSION=${1-nodejs10} + +if [ "$NODEJS_VERSION" = "nodejs10" ]; +then + DOCKER='nodejs10-xenial-pg101:postgis-2.4.4.5' +elif [ "$NODEJS_VERSION" = "nodejs6" ]; +then + DOCKER='nodejs6-xenial-pg101' +else + usage +fi + +docker run -v `pwd`:/srv carto/${DOCKER} bash test/run_tests_docker.sh ${NODEJS_VERSION} && \ + docker ps --filter status=dead --filter status=exited -aq | xargs docker rm -v diff --git a/logs/.gitneedsme b/logs/.gitneedsme new file mode 100644 index 0000000..e69de29 diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json new file mode 100644 index 0000000..a3782f1 --- /dev/null +++ b/npm-shrinkwrap.json @@ -0,0 +1,1620 @@ +{ + "name": "cartodb_sql_api", + "version": "2.4.0", + "dependencies": { + "@carto/fqdn-sync": { + "version": "0.2.2", + "from": "@carto/fqdn-sync@0.2.2", + "resolved": "https://registry.npmjs.org/@carto/fqdn-sync/-/fqdn-sync-0.2.2.tgz" + }, + "accepts": { + "version": "1.2.13", + "from": "accepts@>=1.2.12 <1.3.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.2.13.tgz" + }, + "ansi-regex": { + "version": "2.1.1", + "from": "ansi-regex@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz" + }, + "ansi-styles": { + "version": "2.2.1", + "from": "ansi-styles@>=2.2.1 <3.0.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz" + }, + "append-field": { + "version": "0.1.0", + "from": "append-field@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/append-field/-/append-field-0.1.0.tgz" + }, + "array-flatten": { + "version": "1.1.1", + "from": "array-flatten@1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz" + }, + "asn1": { + "version": "0.2.4", + "from": "asn1@>=0.2.3 <0.3.0", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz" + }, + "assert-plus": { + "version": "0.2.0", + "from": "assert-plus@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz" + }, + "async": { + "version": "0.2.10", + "from": "async@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz" + }, + "asynckit": { + "version": "0.4.0", + "from": "asynckit@>=0.4.0 <0.5.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz" + }, + "aws-sign2": { + "version": "0.6.0", + "from": "aws-sign2@>=0.6.0 <0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz" + }, + "aws4": { + "version": "1.8.0", + "from": "aws4@>=1.2.1 <2.0.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz" + }, + "balanced-match": { + "version": "1.0.0", + "from": "balanced-match@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz" + }, + "basic-auth": { + "version": "2.0.1", + "from": "basic-auth@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz" + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "from": "bcrypt-pbkdf@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz" + }, + "bindings": { + "version": "1.3.1", + "from": "bindings@>=1.2.1 <2.0.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.3.1.tgz" + }, + "bintrees": { + "version": "1.0.1", + "from": "bintrees@1.0.1", + "resolved": "https://registry.npmjs.org/bintrees/-/bintrees-1.0.1.tgz" + }, + "bl": { + "version": "1.1.2", + "from": "bl@>=1.1.2 <1.2.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-1.1.2.tgz", + "dependencies": { + "isarray": { + "version": "1.0.0", + "from": "isarray@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" + }, + "process-nextick-args": { + "version": "1.0.7", + "from": "process-nextick-args@>=1.0.6 <1.1.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz" + }, + "readable-stream": { + "version": "2.0.6", + "from": "readable-stream@>=2.0.5 <2.1.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz" + } + } + }, + "bluebird": { + "version": "3.5.3", + "from": "bluebird@>=3.3.3 <4.0.0", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.3.tgz" + }, + "boom": { + "version": "2.10.1", + "from": "boom@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz" + }, + "brace-expansion": { + "version": "1.1.11", + "from": "brace-expansion@>=1.1.7 <2.0.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz" + }, + "buffer-from": { + "version": "1.1.1", + "from": "buffer-from@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz" + }, + "buffer-writer": { + "version": "1.0.1", + "from": "buffer-writer@1.0.1", + "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-1.0.1.tgz" + }, + "builtin-modules": { + "version": "1.1.1", + "from": "builtin-modules@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz" + }, + "bunyan": { + "version": "1.8.1", + "from": "bunyan@1.8.1", + "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.8.1.tgz" + }, + "busboy": { + "version": "0.2.14", + "from": "busboy@>=0.2.11 <0.3.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.14.tgz", + "dependencies": { + "readable-stream": { + "version": "1.1.14", + "from": "readable-stream@>=1.1.0 <1.2.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz" + } + } + }, + "camelcase": { + "version": "3.0.0", + "from": "camelcase@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz" + }, + "cartodb-psql": { + "version": "0.13.1", + "from": "cartodb-psql@0.13.1", + "resolved": "https://registry.npmjs.org/cartodb-psql/-/cartodb-psql-0.13.1.tgz", + "dependencies": { + "debug": { + "version": "3.2.6", + "from": "debug@>=3.1.0 <4.0.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz" + } + } + }, + "cartodb-query-tables": { + "version": "0.4.0", + "from": "cartodb-query-tables@0.4.0", + "resolved": "https://registry.npmjs.org/cartodb-query-tables/-/cartodb-query-tables-0.4.0.tgz" + }, + "cartodb-redis": { + "version": "2.1.0", + "from": "cartodb-redis@2.1.0", + "resolved": "https://registry.npmjs.org/cartodb-redis/-/cartodb-redis-2.1.0.tgz" + }, + "caseless": { + "version": "0.11.0", + "from": "caseless@>=0.11.0 <0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.11.0.tgz" + }, + "chalk": { + "version": "1.1.3", + "from": "chalk@>=1.1.1 <2.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz" + }, + "cliui": { + "version": "3.2.0", + "from": "cliui@>=3.2.0 <4.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz" + }, + "code-point-at": { + "version": "1.1.0", + "from": "code-point-at@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz" + }, + "combined-stream": { + "version": "1.0.7", + "from": "combined-stream@>=1.0.5 <1.1.0", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz" + }, + "commander": { + "version": "2.19.0", + "from": "commander@>=2.9.0 <3.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz" + }, + "concat-map": { + "version": "0.0.1", + "from": "concat-map@0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + }, + "concat-stream": { + "version": "1.6.2", + "from": "concat-stream@>=1.5.0 <2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "dependencies": { + "isarray": { + "version": "1.0.0", + "from": "isarray@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" + }, + "readable-stream": { + "version": "2.3.6", + "from": "readable-stream@>=2.2.2 <3.0.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz" + }, + "string_decoder": { + "version": "1.1.1", + "from": "string_decoder@>=1.1.1 <1.2.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz" + } + } + }, + "content-disposition": { + "version": "0.5.1", + "from": "content-disposition@0.5.1", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.1.tgz" + }, + "content-type": { + "version": "1.0.4", + "from": "content-type@>=1.0.1 <1.1.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz" + }, + "cookie": { + "version": "0.1.5", + "from": "cookie@0.1.5", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.1.5.tgz" + }, + "cookie-signature": { + "version": "1.0.6", + "from": "cookie-signature@1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz" + }, + "core-util-is": { + "version": "1.0.2", + "from": "core-util-is@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz" + }, + "cryptiles": { + "version": "2.0.5", + "from": "cryptiles@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz" + }, + "dashdash": { + "version": "1.14.1", + "from": "dashdash@>=1.12.0 <2.0.0", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "from": "assert-plus@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" + } + } + }, + "debug": { + "version": "2.2.0", + "from": "debug@2.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "dependencies": { + "ms": { + "version": "0.7.1", + "from": "ms@0.7.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz" + } + } + }, + "decamelize": { + "version": "1.2.0", + "from": "decamelize@>=1.1.1 <2.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz" + }, + "delayed-stream": { + "version": "1.0.0", + "from": "delayed-stream@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" + }, + "depd": { + "version": "1.1.2", + "from": "depd@>=1.1.0 <1.2.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz" + }, + "destroy": { + "version": "1.0.4", + "from": "destroy@>=1.0.4 <1.1.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz" + }, + "dicer": { + "version": "0.2.5", + "from": "dicer@0.2.5", + "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.2.5.tgz", + "dependencies": { + "readable-stream": { + "version": "1.1.14", + "from": "readable-stream@>=1.1.0 <1.2.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz" + } + } + }, + "dot": { + "version": "1.0.3", + "from": "dot@>=1.0.2 <1.1.0", + "resolved": "https://registry.npmjs.org/dot/-/dot-1.0.3.tgz" + }, + "double-ended-queue": { + "version": "2.1.0-0", + "from": "double-ended-queue@>=2.1.0-0 <3.0.0", + "resolved": "https://registry.npmjs.org/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz" + }, + "dtrace-provider": { + "version": "0.6.0", + "from": "dtrace-provider@>=0.6.0 <0.7.0", + "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.6.0.tgz", + "optional": true + }, + "ecc-jsbn": { + "version": "0.1.2", + "from": "ecc-jsbn@>=0.1.1 <0.2.0", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz" + }, + "ee-first": { + "version": "1.1.1", + "from": "ee-first@1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz" + }, + "error-ex": { + "version": "1.3.2", + "from": "error-ex@>=1.2.0 <2.0.0", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz" + }, + "escape-html": { + "version": "1.0.3", + "from": "escape-html@>=1.0.3 <1.1.0", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz" + }, + "escape-string-regexp": { + "version": "1.0.5", + "from": "escape-string-regexp@>=1.0.2 <2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" + }, + "etag": { + "version": "1.7.0", + "from": "etag@>=1.7.0 <1.8.0", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.7.0.tgz" + }, + "express": { + "version": "4.13.4", + "from": "express@>=4.13.3 <4.14.0", + "resolved": "https://registry.npmjs.org/express/-/express-4.13.4.tgz", + "dependencies": { + "qs": { + "version": "4.0.0", + "from": "qs@4.0.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-4.0.0.tgz" + } + } + }, + "extend": { + "version": "3.0.2", + "from": "extend@>=3.0.0 <3.1.0", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz" + }, + "extsprintf": { + "version": "1.3.0", + "from": "extsprintf@1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz" + }, + "finalhandler": { + "version": "0.4.1", + "from": "finalhandler@0.4.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-0.4.1.tgz" + }, + "find-up": { + "version": "1.1.2", + "from": "find-up@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz" + }, + "forever-agent": { + "version": "0.6.1", + "from": "forever-agent@>=0.6.1 <0.7.0", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz" + }, + "form-data": { + "version": "2.0.0", + "from": "form-data@>=2.0.0 <2.1.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.0.0.tgz" + }, + "forwarded": { + "version": "0.1.2", + "from": "forwarded@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz" + }, + "fresh": { + "version": "0.3.0", + "from": "fresh@0.3.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.3.0.tgz" + }, + "gc-stats": { + "version": "1.2.1", + "from": "gc-stats@1.2.1", + "resolved": "https://registry.npmjs.org/gc-stats/-/gc-stats-1.2.1.tgz", + "dependencies": { + "abbrev": { + "version": "1.1.1", + "from": "abbrev@1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz" + }, + "ansi-regex": { + "version": "2.1.1", + "from": "ansi-regex@2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz" + }, + "aproba": { + "version": "1.2.0", + "from": "aproba@1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz" + }, + "are-we-there-yet": { + "version": "1.1.5", + "from": "are-we-there-yet@1.1.5", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz" + }, + "balanced-match": { + "version": "1.0.0", + "from": "balanced-match@1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz" + }, + "brace-expansion": { + "version": "1.1.11", + "from": "brace-expansion@1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz" + }, + "chownr": { + "version": "1.1.1", + "from": "chownr@1.1.1", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz" + }, + "code-point-at": { + "version": "1.1.0", + "from": "code-point-at@1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz" + }, + "concat-map": { + "version": "0.0.1", + "from": "concat-map@0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + }, + "console-control-strings": { + "version": "1.1.0", + "from": "console-control-strings@1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz" + }, + "core-util-is": { + "version": "1.0.2", + "from": "core-util-is@1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz" + }, + "debug": { + "version": "2.6.9", + "from": "debug@2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" + }, + "deep-extend": { + "version": "0.6.0", + "from": "deep-extend@0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz" + }, + "delegates": { + "version": "1.0.0", + "from": "delegates@1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz" + }, + "detect-libc": { + "version": "1.0.3", + "from": "detect-libc@1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz" + }, + "fs-minipass": { + "version": "1.2.5", + "from": "fs-minipass@1.2.5", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.5.tgz" + }, + "fs.realpath": { + "version": "1.0.0", + "from": "fs.realpath@1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" + }, + "gauge": { + "version": "2.7.4", + "from": "gauge@2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz" + }, + "glob": { + "version": "7.1.2", + "from": "glob@7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz" + }, + "has-unicode": { + "version": "2.0.1", + "from": "has-unicode@2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz" + }, + "iconv-lite": { + "version": "0.4.24", + "from": "iconv-lite@0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz" + }, + "ignore-walk": { + "version": "3.0.1", + "from": "ignore-walk@3.0.1", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.1.tgz" + }, + "inflight": { + "version": "1.0.6", + "from": "inflight@1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz" + }, + "inherits": { + "version": "2.0.3", + "from": "inherits@2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" + }, + "ini": { + "version": "1.3.5", + "from": "ini@1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz" + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "from": "is-fullwidth-code-point@1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz" + }, + "isarray": { + "version": "1.0.0", + "from": "isarray@1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" + }, + "minimatch": { + "version": "3.0.4", + "from": "minimatch@3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz" + }, + "minimist": { + "version": "0.0.8", + "from": "minimist@0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" + }, + "minipass": { + "version": "2.3.5", + "from": "minipass@2.3.5", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.5.tgz" + }, + "minizlib": { + "version": "1.1.1", + "from": "minizlib@1.1.1", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.1.1.tgz" + }, + "mkdirp": { + "version": "0.5.1", + "from": "mkdirp@0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz" + }, + "ms": { + "version": "2.0.0", + "from": "ms@2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" + }, + "needle": { + "version": "2.2.4", + "from": "needle@2.2.4", + "resolved": "https://registry.npmjs.org/needle/-/needle-2.2.4.tgz", + "dependencies": { + "sax": { + "version": "1.2.4", + "from": "sax@1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz" + } + } + }, + "node-pre-gyp": { + "version": "0.11.0", + "from": "node-pre-gyp@0.11.0", + "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.11.0.tgz" + }, + "nopt": { + "version": "4.0.1", + "from": "nopt@4.0.1", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz" + }, + "npm-bundled": { + "version": "1.0.5", + "from": "npm-bundled@1.0.5", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.0.5.tgz" + }, + "npm-packlist": { + "version": "1.1.12", + "from": "npm-packlist@1.1.12", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.1.12.tgz" + }, + "npmlog": { + "version": "4.1.2", + "from": "npmlog@4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz" + }, + "number-is-nan": { + "version": "1.0.1", + "from": "number-is-nan@1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz" + }, + "object-assign": { + "version": "4.1.1", + "from": "object-assign@4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" + }, + "once": { + "version": "1.4.0", + "from": "once@1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz" + }, + "os-homedir": { + "version": "1.0.2", + "from": "os-homedir@1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz" + }, + "os-tmpdir": { + "version": "1.0.2", + "from": "os-tmpdir@1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz" + }, + "osenv": { + "version": "0.1.5", + "from": "osenv@0.1.5", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz" + }, + "path-is-absolute": { + "version": "1.0.1", + "from": "path-is-absolute@1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" + }, + "process-nextick-args": { + "version": "2.0.0", + "from": "process-nextick-args@2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz" + }, + "rc": { + "version": "1.2.8", + "from": "rc@1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "dependencies": { + "minimist": { + "version": "1.2.0", + "from": "minimist@1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" + } + } + }, + "readable-stream": { + "version": "2.3.6", + "from": "readable-stream@2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz" + }, + "rimraf": { + "version": "2.6.2", + "from": "rimraf@2.6.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz" + }, + "safe-buffer": { + "version": "5.1.2", + "from": "safe-buffer@5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" + }, + "safer-buffer": { + "version": "2.1.2", + "from": "safer-buffer@2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz" + }, + "semver": { + "version": "5.5.0", + "from": "semver@5.5.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz" + }, + "set-blocking": { + "version": "2.0.0", + "from": "set-blocking@2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz" + }, + "signal-exit": { + "version": "3.0.2", + "from": "signal-exit@3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz" + }, + "string_decoder": { + "version": "1.1.1", + "from": "string_decoder@1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz" + }, + "string-width": { + "version": "1.0.2", + "from": "string-width@1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz" + }, + "strip-ansi": { + "version": "3.0.1", + "from": "strip-ansi@3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz" + }, + "strip-json-comments": { + "version": "2.0.1", + "from": "strip-json-comments@2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz" + }, + "tar": { + "version": "4.4.6", + "from": "tar@4.4.6", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.6.tgz" + }, + "util-deprecate": { + "version": "1.0.2", + "from": "util-deprecate@1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" + }, + "wide-align": { + "version": "1.1.3", + "from": "wide-align@1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz" + }, + "wrappy": { + "version": "1.0.2", + "from": "wrappy@1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" + }, + "yallist": { + "version": "3.0.2", + "from": "yallist@3.0.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.2.tgz" + } + } + }, + "generate-function": { + "version": "2.3.1", + "from": "generate-function@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz" + }, + "generate-object-property": { + "version": "1.2.0", + "from": "generate-object-property@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz" + }, + "generic-pool": { + "version": "2.4.3", + "from": "generic-pool@2.4.3", + "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-2.4.3.tgz" + }, + "get-caller-file": { + "version": "1.0.3", + "from": "get-caller-file@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz" + }, + "getpass": { + "version": "0.1.7", + "from": "getpass@>=0.1.1 <0.2.0", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "from": "assert-plus@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" + } + } + }, + "glob": { + "version": "6.0.4", + "from": "glob@>=6.0.1 <7.0.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", + "optional": true + }, + "graceful-fs": { + "version": "4.1.15", + "from": "graceful-fs@>=4.1.2 <5.0.0", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz" + }, + "har-validator": { + "version": "2.0.6", + "from": "har-validator@>=2.0.6 <2.1.0", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-2.0.6.tgz" + }, + "has-ansi": { + "version": "2.0.0", + "from": "has-ansi@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz" + }, + "hawk": { + "version": "3.1.3", + "from": "hawk@>=3.1.3 <3.2.0", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz" + }, + "hiredis": { + "version": "0.5.0", + "from": "hiredis@>=0.5.0 <0.6.0", + "resolved": "https://registry.npmjs.org/hiredis/-/hiredis-0.5.0.tgz" + }, + "hoek": { + "version": "2.16.3", + "from": "hoek@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz" + }, + "hosted-git-info": { + "version": "2.7.1", + "from": "hosted-git-info@>=2.1.4 <3.0.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz" + }, + "http-errors": { + "version": "1.3.1", + "from": "http-errors@>=1.3.1 <1.4.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.3.1.tgz" + }, + "http-signature": { + "version": "1.1.1", + "from": "http-signature@>=1.1.0 <1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz" + }, + "inflight": { + "version": "1.0.6", + "from": "inflight@>=1.0.4 <2.0.0", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz" + }, + "inherits": { + "version": "2.0.3", + "from": "inherits@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" + }, + "invert-kv": { + "version": "1.0.0", + "from": "invert-kv@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz" + }, + "ipaddr.js": { + "version": "1.0.5", + "from": "ipaddr.js@1.0.5", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.0.5.tgz" + }, + "is-arrayish": { + "version": "0.2.1", + "from": "is-arrayish@>=0.2.1 <0.3.0", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz" + }, + "is-builtin-module": { + "version": "1.0.0", + "from": "is-builtin-module@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz" + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "from": "is-fullwidth-code-point@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz" + }, + "is-my-ip-valid": { + "version": "1.0.0", + "from": "is-my-ip-valid@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-my-ip-valid/-/is-my-ip-valid-1.0.0.tgz" + }, + "is-my-json-valid": { + "version": "2.19.0", + "from": "is-my-json-valid@>=2.12.4 <3.0.0", + "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.19.0.tgz" + }, + "is-property": { + "version": "1.0.2", + "from": "is-property@>=1.0.2 <2.0.0", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz" + }, + "is-typedarray": { + "version": "1.0.0", + "from": "is-typedarray@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz" + }, + "is-utf8": { + "version": "0.2.1", + "from": "is-utf8@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "isstream": { + "version": "0.1.2", + "from": "isstream@>=0.1.2 <0.2.0", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz" + }, + "js-string-escape": { + "version": "1.0.1", + "from": "js-string-escape@1.0.1", + "resolved": "https://registry.npmjs.org/js-string-escape/-/js-string-escape-1.0.1.tgz" + }, + "jsbn": { + "version": "0.1.1", + "from": "jsbn@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz" + }, + "json-schema": { + "version": "0.2.3", + "from": "json-schema@0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz" + }, + "json-stringify-safe": { + "version": "5.0.1", + "from": "json-stringify-safe@>=5.0.1 <5.1.0", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz" + }, + "jsonpointer": { + "version": "4.0.1", + "from": "jsonpointer@>=4.0.0 <5.0.0", + "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.0.1.tgz" + }, + "jsprim": { + "version": "1.4.1", + "from": "jsprim@>=1.2.2 <2.0.0", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "from": "assert-plus@1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" + } + } + }, + "lcid": { + "version": "1.0.0", + "from": "lcid@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz" + }, + "load-json-file": { + "version": "1.1.0", + "from": "load-json-file@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz" + }, + "lodash.assign": { + "version": "4.2.0", + "from": "lodash.assign@>=4.2.0 <5.0.0", + "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz" + }, + "log4js": { + "version": "0.6.25", + "from": "cartodb/log4js-node#cdb", + "resolved": "git://github.com/cartodb/log4js-node.git#145d5f91e35e7fb14a6278cbf7a711ced6603727", + "dependencies": { + "semver": { + "version": "4.3.6", + "from": "semver@>=4.3.3 <4.4.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-4.3.6.tgz" + }, + "underscore": { + "version": "1.8.2", + "from": "underscore@1.8.2", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.2.tgz" + } + } + }, + "lru-cache": { + "version": "2.5.2", + "from": "lru-cache@>=2.5.0 <2.6.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.5.2.tgz" + }, + "media-typer": { + "version": "0.3.0", + "from": "media-typer@0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz" + }, + "merge-descriptors": { + "version": "1.0.1", + "from": "merge-descriptors@1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz" + }, + "methods": { + "version": "1.1.2", + "from": "methods@>=1.1.2 <1.2.0", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz" + }, + "mime": { + "version": "1.3.4", + "from": "mime@1.3.4", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.3.4.tgz" + }, + "mime-db": { + "version": "1.37.0", + "from": "mime-db@>=1.37.0 <1.38.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.37.0.tgz" + }, + "mime-types": { + "version": "2.1.21", + "from": "mime-types@>=2.1.6 <2.2.0", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.21.tgz" + }, + "minimatch": { + "version": "3.0.4", + "from": "minimatch@>=2.0.0 <3.0.0||>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz" + }, + "minimist": { + "version": "0.0.8", + "from": "minimist@0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" + }, + "mkdirp": { + "version": "0.5.1", + "from": "mkdirp@0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz" + }, + "moment": { + "version": "2.23.0", + "from": "moment@>=2.10.6 <3.0.0", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.23.0.tgz", + "optional": true + }, + "ms": { + "version": "2.1.1", + "from": "ms@>=2.1.1 <3.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz" + }, + "multer": { + "version": "1.2.1", + "from": "multer@>=1.2.0 <1.3.0", + "resolved": "https://registry.npmjs.org/multer/-/multer-1.2.1.tgz", + "dependencies": { + "object-assign": { + "version": "3.0.0", + "from": "object-assign@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz" + } + } + }, + "mv": { + "version": "2.1.1", + "from": "mv@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz", + "optional": true + }, + "nan": { + "version": "2.12.1", + "from": "nan@>=2.0.8 <3.0.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.12.1.tgz" + }, + "ncp": { + "version": "2.0.0", + "from": "ncp@>=2.0.0 <2.1.0", + "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", + "optional": true + }, + "negotiator": { + "version": "0.5.3", + "from": "negotiator@0.5.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.5.3.tgz" + }, + "node-statsd": { + "version": "0.0.7", + "from": "node-statsd@>=0.0.7 <0.1.0", + "resolved": "https://registry.npmjs.org/node-statsd/-/node-statsd-0.0.7.tgz" + }, + "node-uuid": { + "version": "1.4.8", + "from": "node-uuid@>=1.4.7 <2.0.0", + "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz" + }, + "normalize-package-data": { + "version": "2.4.0", + "from": "normalize-package-data@>=2.3.2 <3.0.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz" + }, + "number-is-nan": { + "version": "1.0.1", + "from": "number-is-nan@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz" + }, + "oauth-client": { + "version": "0.3.0", + "from": "oauth-client@0.3.0", + "resolved": "https://registry.npmjs.org/oauth-client/-/oauth-client-0.3.0.tgz", + "dependencies": { + "node-uuid": { + "version": "1.1.0", + "from": "node-uuid@1.1.0", + "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.1.0.tgz" + } + } + }, + "oauth-sign": { + "version": "0.8.2", + "from": "oauth-sign@>=0.8.1 <0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz" + }, + "object-assign": { + "version": "4.1.0", + "from": "object-assign@4.1.0", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.0.tgz" + }, + "on-finished": { + "version": "2.3.0", + "from": "on-finished@>=2.3.0 <2.4.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz" + }, + "once": { + "version": "1.4.0", + "from": "once@>=1.3.0 <2.0.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz" + }, + "optimist": { + "version": "0.3.5", + "from": "optimist@0.3.5", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.3.5.tgz" + }, + "os-locale": { + "version": "1.4.0", + "from": "os-locale@>=1.4.0 <2.0.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz" + }, + "packet-reader": { + "version": "0.3.1", + "from": "packet-reader@0.3.1", + "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-0.3.1.tgz" + }, + "parse-json": { + "version": "2.2.0", + "from": "parse-json@>=2.2.0 <3.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz" + }, + "parseurl": { + "version": "1.3.2", + "from": "parseurl@>=1.3.1 <1.4.0", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz" + }, + "path-exists": { + "version": "2.1.0", + "from": "path-exists@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz" + }, + "path-is-absolute": { + "version": "1.0.1", + "from": "path-is-absolute@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" + }, + "path-to-regexp": { + "version": "0.1.7", + "from": "path-to-regexp@0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz" + }, + "path-type": { + "version": "1.1.0", + "from": "path-type@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz" + }, + "pg": { + "version": "6.4.2-cdb2", + "from": "cartodb/node-postgres#6.4.2-cdb2", + "resolved": "git://github.com/cartodb/node-postgres.git#5417d7b29b7272ca2e71bb396899ab3f177a9ae6" + }, + "pg-connection-string": { + "version": "0.1.3", + "from": "pg-connection-string@0.1.3", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-0.1.3.tgz" + }, + "pg-copy-streams": { + "version": "1.2.0-carto.3", + "from": "cartodb/node-pg-copy-streams#v1.2.0-carto.3", + "resolved": "git://github.com/cartodb/node-pg-copy-streams.git#94c8d89abaea54bd4e6f61f63f47ef81cdc44253" + }, + "pg-int8": { + "version": "1.0.1", + "from": "pg-int8@1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz" + }, + "pg-pool": { + "version": "1.8.0", + "from": "pg-pool@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-1.8.0.tgz" + }, + "pg-types": { + "version": "1.13.0", + "from": "pg-types@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-1.13.0.tgz" + }, + "pgpass": { + "version": "1.0.2", + "from": "pgpass@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.2.tgz" + }, + "pify": { + "version": "2.3.0", + "from": "pify@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz" + }, + "pinkie": { + "version": "2.0.4", + "from": "pinkie@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz" + }, + "pinkie-promise": { + "version": "2.0.1", + "from": "pinkie-promise@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz" + }, + "postgres-array": { + "version": "1.0.3", + "from": "postgres-array@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-1.0.3.tgz" + }, + "postgres-bytea": { + "version": "1.0.0", + "from": "postgres-bytea@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz" + }, + "postgres-date": { + "version": "1.0.3", + "from": "postgres-date@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.3.tgz" + }, + "postgres-interval": { + "version": "1.1.2", + "from": "postgres-interval@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.1.2.tgz" + }, + "process-nextick-args": { + "version": "2.0.0", + "from": "process-nextick-args@>=2.0.0 <2.1.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz" + }, + "proxy-addr": { + "version": "1.0.10", + "from": "proxy-addr@>=1.0.10 <1.1.0", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-1.0.10.tgz" + }, + "punycode": { + "version": "1.4.1", + "from": "punycode@>=1.4.1 <2.0.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz" + }, + "qs": { + "version": "6.2.3", + "from": "qs@>=6.2.1 <6.3.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.2.3.tgz" + }, + "queue-async": { + "version": "1.0.7", + "from": "queue-async@>=1.0.7 <1.1.0", + "resolved": "https://registry.npmjs.org/queue-async/-/queue-async-1.0.7.tgz" + }, + "range-parser": { + "version": "1.0.3", + "from": "range-parser@>=1.0.3 <1.1.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.0.3.tgz" + }, + "read-pkg": { + "version": "1.1.0", + "from": "read-pkg@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz" + }, + "read-pkg-up": { + "version": "1.0.1", + "from": "read-pkg-up@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz" + }, + "readable-stream": { + "version": "1.0.34", + "from": "readable-stream@>=1.0.2 <1.1.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz" + }, + "redis": { + "version": "2.8.0", + "from": "redis@>=2.8.0 <3.0.0", + "resolved": "https://registry.npmjs.org/redis/-/redis-2.8.0.tgz" + }, + "redis-commands": { + "version": "1.4.0", + "from": "redis-commands@>=1.2.0 <2.0.0", + "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.4.0.tgz" + }, + "redis-mpool": { + "version": "0.7.0", + "from": "redis-mpool@0.7.0", + "resolved": "https://registry.npmjs.org/redis-mpool/-/redis-mpool-0.7.0.tgz", + "dependencies": { + "generic-pool": { + "version": "2.1.1", + "from": "generic-pool@>=2.1.1 <2.2.0", + "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-2.1.1.tgz" + } + } + }, + "redis-parser": { + "version": "2.6.0", + "from": "redis-parser@>=2.6.0 <3.0.0", + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-2.6.0.tgz" + }, + "redlock": { + "version": "2.0.1", + "from": "redlock@2.0.1", + "resolved": "https://registry.npmjs.org/redlock/-/redlock-2.0.1.tgz" + }, + "request": { + "version": "2.75.0", + "from": "request@>=2.75.0 <2.76.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.75.0.tgz" + }, + "require-directory": { + "version": "2.1.1", + "from": "require-directory@>=2.1.1 <3.0.0", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz" + }, + "require-main-filename": { + "version": "1.0.1", + "from": "require-main-filename@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz" + }, + "rimraf": { + "version": "2.4.5", + "from": "rimraf@>=2.4.0 <2.5.0", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", + "optional": true + }, + "safe-buffer": { + "version": "5.1.2", + "from": "safe-buffer@5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" + }, + "safe-json-stringify": { + "version": "1.2.0", + "from": "safe-json-stringify@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.2.0.tgz", + "optional": true + }, + "safer-buffer": { + "version": "2.1.2", + "from": "safer-buffer@>=2.0.2 <3.0.0", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz" + }, + "semver": { + "version": "4.3.2", + "from": "semver@4.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-4.3.2.tgz" + }, + "send": { + "version": "0.13.1", + "from": "send@0.13.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.13.1.tgz", + "dependencies": { + "ms": { + "version": "0.7.1", + "from": "ms@0.7.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz" + } + } + }, + "serve-static": { + "version": "1.10.3", + "from": "serve-static@>=1.10.2 <1.11.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.10.3.tgz", + "dependencies": { + "ms": { + "version": "0.7.1", + "from": "ms@0.7.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz" + }, + "send": { + "version": "0.13.2", + "from": "send@0.13.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.13.2.tgz" + } + } + }, + "set-blocking": { + "version": "2.0.0", + "from": "set-blocking@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz" + }, + "sntp": { + "version": "1.0.9", + "from": "sntp@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz" + }, + "spdx-correct": { + "version": "3.1.0", + "from": "spdx-correct@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz" + }, + "spdx-exceptions": { + "version": "2.2.0", + "from": "spdx-exceptions@>=2.1.0 <3.0.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz" + }, + "spdx-expression-parse": { + "version": "3.0.0", + "from": "spdx-expression-parse@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz" + }, + "spdx-license-ids": { + "version": "3.0.3", + "from": "spdx-license-ids@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.3.tgz" + }, + "split": { + "version": "1.0.1", + "from": "split@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz" + }, + "sshpk": { + "version": "1.16.0", + "from": "sshpk@>=1.7.0 <2.0.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.0.tgz", + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "from": "assert-plus@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" + } + } + }, + "statuses": { + "version": "1.2.1", + "from": "statuses@>=1.2.1 <1.3.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.2.1.tgz" + }, + "step": { + "version": "0.0.6", + "from": "step@>=0.0.5 <0.1.0", + "resolved": "https://registry.npmjs.org/step/-/step-0.0.6.tgz" + }, + "step-profiler": { + "version": "0.3.0", + "from": "step-profiler@>=0.3.0 <0.4.0", + "resolved": "https://registry.npmjs.org/step-profiler/-/step-profiler-0.3.0.tgz" + }, + "streamsearch": { + "version": "0.1.2", + "from": "streamsearch@0.1.2", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "string_decoder@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + }, + "string-width": { + "version": "1.0.2", + "from": "string-width@>=1.0.2 <2.0.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz" + }, + "stringstream": { + "version": "0.0.6", + "from": "stringstream@>=0.0.4 <0.1.0", + "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.6.tgz" + }, + "strip-ansi": { + "version": "3.0.1", + "from": "strip-ansi@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz" + }, + "strip-bom": { + "version": "2.0.0", + "from": "strip-bom@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz" + }, + "supports-color": { + "version": "2.0.0", + "from": "supports-color@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz" + }, + "through": { + "version": "2.3.8", + "from": "through@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz" + }, + "topojson": { + "version": "0.0.8", + "from": "topojson@0.0.8", + "resolved": "https://registry.npmjs.org/topojson/-/topojson-0.0.8.tgz" + }, + "tough-cookie": { + "version": "2.3.4", + "from": "tough-cookie@>=2.3.0 <2.4.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz" + }, + "tunnel-agent": { + "version": "0.4.3", + "from": "tunnel-agent@>=0.4.1 <0.5.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.3.tgz" + }, + "tweetnacl": { + "version": "0.14.5", + "from": "tweetnacl@>=0.14.0 <0.15.0", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz" + }, + "type-is": { + "version": "1.6.16", + "from": "type-is@>=1.6.6 <1.7.0", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz" + }, + "typedarray": { + "version": "0.0.6", + "from": "typedarray@>=0.0.6 <0.0.7", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz" + }, + "underscore": { + "version": "1.6.0", + "from": "underscore@>=1.6.0 <1.7.0", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz" + }, + "unpipe": { + "version": "1.0.0", + "from": "unpipe@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz" + }, + "util-deprecate": { + "version": "1.0.2", + "from": "util-deprecate@>=1.0.1 <1.1.0", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" + }, + "utils-merge": { + "version": "1.0.0", + "from": "utils-merge@1.0.0", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.0.tgz" + }, + "validate-npm-package-license": { + "version": "3.0.4", + "from": "validate-npm-package-license@>=3.0.1 <4.0.0", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz" + }, + "vary": { + "version": "1.0.1", + "from": "vary@>=1.0.1 <1.1.0", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.0.1.tgz" + }, + "verror": { + "version": "1.10.0", + "from": "verror@1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "from": "assert-plus@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" + } + } + }, + "which-module": { + "version": "1.0.0", + "from": "which-module@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz" + }, + "window-size": { + "version": "0.2.0", + "from": "window-size@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.2.0.tgz" + }, + "wordwrap": { + "version": "0.0.3", + "from": "wordwrap@>=0.0.2 <0.1.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz" + }, + "wrap-ansi": { + "version": "2.1.0", + "from": "wrap-ansi@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz" + }, + "wrappy": { + "version": "1.0.2", + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" + }, + "xtend": { + "version": "4.0.1", + "from": "xtend@>=4.0.0 <5.0.0", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz" + }, + "y18n": { + "version": "3.2.1", + "from": "y18n@>=3.2.1 <4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz" + }, + "yargs": { + "version": "5.0.0", + "from": "yargs@>=5.0.0 <5.1.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-5.0.0.tgz" + }, + "yargs-parser": { + "version": "3.2.0", + "from": "yargs-parser@>=3.2.0 <4.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-3.2.0.tgz" + } + } +} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..483c204 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,5009 @@ +{ + "name": "cartodb_sql_api", + "version": "2.4.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@carto/fqdn-sync": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@carto/fqdn-sync/-/fqdn-sync-0.2.2.tgz", + "integrity": "sha1-zXxkXtZmkKCSSbVU0lSg9Tljstw=" + }, + "abbrev": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz", + "integrity": "sha1-kbR5JYinc4wl813W9jdSovh3YTU=", + "dev": true + }, + "accepts": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.2.13.tgz", + "integrity": "sha1-5fHzkoxtlf2WVYw27D2dDeSm7Oo=", + "requires": { + "mime-types": "~2.1.6", + "negotiator": "0.5.3" + } + }, + "amdefine": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", + "dev": true, + "optional": true + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" + }, + "append-field": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/append-field/-/append-field-0.1.0.tgz", + "integrity": "sha1-bdxY+gg8e8VF08WZWygwzCNm1Eo=" + }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", + "dev": true + }, + "are-we-there-yet": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", + "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", + "dev": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha1-vNZ5HqWuCXJeF+WtmIE0zUCz2RE=", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha1-jSR136tVO7M+d7VOWeiAu4ziMTY=", + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "assert-plus": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", + "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=" + }, + "async": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", + "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=" + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "aws-sign2": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", + "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=" + }, + "aws4": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", + "integrity": "sha1-8OAD2cqef1nHpQiUXXsu+aBKVC8=" + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "basic-auth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha1-uZgnm/R844NEtPPPkW1Gebv1Hjo=", + "requires": { + "safe-buffer": "5.1.2" + } + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "bindings": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.3.0.tgz", + "integrity": "sha1-s0b27PapX1qBXFg5/HzbIlAvHtc=" + }, + "bintrees": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/bintrees/-/bintrees-1.0.1.tgz", + "integrity": "sha1-DmVcm5wkNeqraL9AJyJtK1WjRSQ=" + }, + "bl": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/bl/-/bl-1.1.2.tgz", + "integrity": "sha1-/cqHGplxOqANGeO7ukHER4emU5g=", + "requires": { + "readable-stream": "~2.0.5" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "process-nextick-args": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=" + }, + "readable-stream": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", + "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "~1.0.0", + "process-nextick-args": "~1.0.6", + "string_decoder": "~0.10.x", + "util-deprecate": "~1.0.1" + } + } + } + }, + "bluebird": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.2.tgz", + "integrity": "sha1-G+CQjgVKdRdUVJwnBInBUF1KsVo=" + }, + "boom": { + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", + "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", + "requires": { + "hoek": "2.x.x" + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha1-PH/L9SnYcibz0vUrlm/1Jx60Qd0=", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "browser-stdout": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.0.tgz", + "integrity": "sha1-81HTKWnTL6XXpVZxVCY9korjvR8=", + "dev": true + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha1-MnE7wCj3XAL9txDXx7zsHyxgcO8=" + }, + "buffer-writer": { + "version": "1.0.1", + "resolved": "http://registry.npmjs.org/buffer-writer/-/buffer-writer-1.0.1.tgz", + "integrity": "sha1-Iqk2kB4wKa/NdUfrRIfOtpejvwg=" + }, + "builtin-modules": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=" + }, + "bunyan": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.8.1.tgz", + "integrity": "sha1-aMakpQLVYgvJ9y1nNoEMGxiYCX8=", + "requires": { + "dtrace-provider": "~0.6", + "moment": "^2.10.6", + "mv": "~2", + "safe-json-stringify": "~1" + } + }, + "busboy": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.14.tgz", + "integrity": "sha1-bCpiLvz0fFe7vh4qnDetNseSVFM=", + "requires": { + "dicer": "0.2.5", + "readable-stream": "1.1.x" + }, + "dependencies": { + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + } + } + }, + "camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=" + }, + "cartodb-psql": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/cartodb-psql/-/cartodb-psql-0.13.1.tgz", + "integrity": "sha512-1z3Dk9G8KQlNGurbcmGBvNj8DVCh1Keue9uzyyvB6hKOYzBHMxixAMG0D+8nSsA7oQmWUsx/xkZZ5ZxT9toEHA==", + "requires": { + "debug": "^3.1.0", + "pg": "github:cartodb/node-postgres#5417d7b29b7272ca2e71bb396899ab3f177a9ae6", + "underscore": "~1.6.0" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "cartodb-query-tables": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cartodb-query-tables/-/cartodb-query-tables-0.4.0.tgz", + "integrity": "sha512-abhhz2juO1IO9hZvv10WAtgt77UTk2Fc/6BK7pml+0Cnd0HMfDC+XKzfY+r/wEjyEa20IXTc//cPsP0BgevyMw==" + }, + "cartodb-redis": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cartodb-redis/-/cartodb-redis-2.1.0.tgz", + "integrity": "sha512-VT9SWWad5eyLkDZKO7W6jjPuboJB3mZSf29WTJqLVRVh9IYzwgGBfpXFBtXaRfVTaNMg/TplBL1+qffMsQtUfA==", + "requires": { + "dot": "~1.0.2", + "redis-mpool": "0.7.0", + "underscore": "~1.6.0" + } + }, + "caseless": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.11.0.tgz", + "integrity": "sha1-cVuW6phBWTzDMGeSP17GDr2k99c=" + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "chownr": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz", + "integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==", + "dev": true + }, + "cli": { + "version": "0.6.6", + "resolved": "https://registry.npmjs.org/cli/-/cli-0.6.6.tgz", + "integrity": "sha1-Aq1Eo4Cr8nraxebwzdewQ9dMU+M=", + "dev": true, + "requires": { + "exit": "0.1.2", + "glob": "~ 3.2.1" + }, + "dependencies": { + "glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz", + "integrity": "sha1-Spc/Y1uRkPcV0QmH1cAP0oFevj0=", + "dev": true, + "requires": { + "inherits": "2", + "minimatch": "0.3" + } + }, + "minimatch": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz", + "integrity": "sha1-J12O2qxPG7MyZHIInnlJyDlGmd0=", + "dev": true, + "requires": { + "lru-cache": "2", + "sigmund": "~1.0.0" + } + } + } + }, + "cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" + } + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" + }, + "combined-stream": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz", + "integrity": "sha1-LR0kMXr7ir6V1tLAsHtXgTU52Cg=", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz", + "integrity": "sha1-9hmKqE5bg8RgVLlN3tv+1e6f8So=" + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha1-kEvfGUzTEi/Gdcd/xKw9T/D9GjQ=", + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha1-sRwn2IuP8fvgcGQ8+UsMea4bCq8=", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "console-browserify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", + "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", + "dev": true, + "requires": { + "date-now": "^0.1.4" + } + }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", + "dev": true + }, + "content-disposition": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.1.tgz", + "integrity": "sha1-h0dsamfI2qh+Muh2Ft+IO6f7Bxs=" + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha1-4TjMdeBAxyexlm/l5fjJruJW/js=" + }, + "cookie": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.1.5.tgz", + "integrity": "sha1-armUiksa4hlSzSWIUwpHItQETXw=" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "cryptiles": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", + "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=", + "requires": { + "boom": "2.x.x" + } + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "requires": { + "assert-plus": "^1.0.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + } + } + }, + "date-now": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", + "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=", + "dev": true + }, + "debug": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", + "requires": { + "ms": "0.7.1" + }, + "dependencies": { + "ms": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", + "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=" + } + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" + }, + "deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true + }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", + "dev": true + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", + "dev": true + }, + "dicer": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.2.5.tgz", + "integrity": "sha1-WZbAhrszIYyBLAkL3cCc0S+stw8=", + "requires": { + "readable-stream": "1.1.x", + "streamsearch": "0.1.2" + }, + "dependencies": { + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + } + } + }, + "diff": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-1.4.0.tgz", + "integrity": "sha1-fyjS657nsVqX79ic5j3P2qPMur8=", + "dev": true + }, + "dom-serializer": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz", + "integrity": "sha1-BzxpdUbOB4DOI75KKOKT5AvDDII=", + "dev": true, + "requires": { + "domelementtype": "~1.1.1", + "entities": "~1.1.1" + }, + "dependencies": { + "domelementtype": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz", + "integrity": "sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs=", + "dev": true + }, + "entities": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha1-vfpzUplmTfr9NFKe1PhSKidf6lY=", + "dev": true + } + } + }, + "domelementtype": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.2.1.tgz", + "integrity": "sha1-V4VY7yO++sBDoauw2wdjVQk5NHk=", + "dev": true + }, + "domhandler": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.3.0.tgz", + "integrity": "sha1-LeWaCCLVAn+r/28DLCsloqir5zg=", + "dev": true, + "requires": { + "domelementtype": "1" + } + }, + "domutils": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", + "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", + "dev": true, + "requires": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "dot": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/dot/-/dot-1.0.3.tgz", + "integrity": "sha1-+HUL+2sDx2ZOsObLHrTGZBmvlCc=" + }, + "double-ended-queue": { + "version": "2.1.0-0", + "resolved": "https://registry.npmjs.org/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz", + "integrity": "sha1-ED01J/0xUo9AGIEwyEHv3XgmTlw=" + }, + "dtrace-provider": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.6.0.tgz", + "integrity": "sha1-CweNVReTfYcxAUUtkUZzdVe3XlE=", + "optional": true, + "requires": { + "nan": "^2.0.8" + } + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "entities": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.0.0.tgz", + "integrity": "sha1-sph6o4ITR/zeZCsk/fyeT7cSvyY=", + "dev": true + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha1-tKxAZIEH/c3PriQvQovqihTU8b8=", + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + }, + "escodegen": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.8.1.tgz", + "integrity": "sha1-WltTr0aTEQvrsIZ6o0MN07cKEBg=", + "dev": true, + "requires": { + "esprima": "^2.7.1", + "estraverse": "^1.9.1", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.2.0" + } + }, + "esprima": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", + "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", + "dev": true + }, + "estraverse": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.9.3.tgz", + "integrity": "sha1-r2fy3JIlgkFZUJJgkaQAXSnJu0Q=", + "dev": true + }, + "esutils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", + "dev": true + }, + "etag": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.7.0.tgz", + "integrity": "sha1-A9MLX2fdbmMtKUXTDWZScxo01dg=" + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", + "dev": true + }, + "express": { + "version": "4.13.4", + "resolved": "https://registry.npmjs.org/express/-/express-4.13.4.tgz", + "integrity": "sha1-PAt288d1kMg0VzkGHsC9O6Bn7CQ=", + "requires": { + "accepts": "~1.2.12", + "array-flatten": "1.1.1", + "content-disposition": "0.5.1", + "content-type": "~1.0.1", + "cookie": "0.1.5", + "cookie-signature": "1.0.6", + "debug": "~2.2.0", + "depd": "~1.1.0", + "escape-html": "~1.0.3", + "etag": "~1.7.0", + "finalhandler": "0.4.1", + "fresh": "0.3.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.1", + "path-to-regexp": "0.1.7", + "proxy-addr": "~1.0.10", + "qs": "4.0.0", + "range-parser": "~1.0.3", + "send": "0.13.1", + "serve-static": "~1.10.2", + "type-is": "~1.6.6", + "utils-merge": "1.0.0", + "vary": "~1.0.1" + }, + "dependencies": { + "qs": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-4.0.0.tgz", + "integrity": "sha1-wx2bdOwn33XlQ6hseHKO2NRiNgc=" + } + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha1-+LETa0Bx+9jrFAr/hYsQGewpFfo=" + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "finalhandler": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-0.4.1.tgz", + "integrity": "sha1-haF8bFmpRxfSYtYSMNSw6+PUoU0=", + "requires": { + "debug": "~2.2.0", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "unpipe": "~1.0.0" + } + }, + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "requires": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + }, + "form-data": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.0.0.tgz", + "integrity": "sha1-bwrrrcxdoWwT4ezBETfYX5uIOyU=", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.5", + "mime-types": "^2.1.11" + } + }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" + }, + "fresh": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.3.0.tgz", + "integrity": "sha1-ZR+DjiJCTnVm3hYdg1jKoZn4PU8=" + }, + "fs-minipass": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.5.tgz", + "integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==", + "dev": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "dev": true, + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "gc-stats": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/gc-stats/-/gc-stats-1.2.1.tgz", + "integrity": "sha512-CPQfMBQPGkqG4upxCn4zHxYZo20woPClSeqnC/WK8pFqlfAtz6zpxbOfnmxOIDYiC26H/pYlWQfdoPVGoqxFUA==", + "requires": { + "nan": "^2.10.0", + "node-pre-gyp": "^0.11.0" + }, + "dependencies": { + "abbrev": { + "version": "1.1.1", + "bundled": true + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true + }, + "aproba": { + "version": "1.2.0", + "bundled": true + }, + "are-we-there-yet": { + "version": "1.1.5", + "bundled": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "balanced-match": { + "version": "1.0.0", + "bundled": true + }, + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "chownr": { + "version": "1.1.1", + "bundled": true + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true + }, + "debug": { + "version": "2.6.9", + "bundled": true, + "requires": { + "ms": "2.0.0" + } + }, + "deep-extend": { + "version": "0.6.0", + "bundled": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true + }, + "detect-libc": { + "version": "1.0.3", + "bundled": true + }, + "fs-minipass": { + "version": "1.2.5", + "bundled": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "glob": { + "version": "7.1.2", + "bundled": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true + }, + "iconv-lite": { + "version": "0.4.24", + "bundled": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ignore-walk": { + "version": "3.0.1", + "bundled": true, + "requires": { + "minimatch": "^3.0.4" + } + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "bundled": true + }, + "ini": { + "version": "1.3.5", + "bundled": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "bundled": true + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "bundled": true + }, + "minipass": { + "version": "2.3.5", + "bundled": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + }, + "minizlib": { + "version": "1.1.1", + "bundled": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "mkdirp": { + "version": "0.5.1", + "bundled": true, + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.0.0", + "bundled": true + }, + "needle": { + "version": "2.2.4", + "bundled": true, + "requires": { + "debug": "^2.1.2", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + }, + "dependencies": { + "sax": { + "version": "1.2.4", + "bundled": true + } + } + }, + "node-pre-gyp": { + "version": "0.11.0", + "bundled": true, + "requires": { + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.1", + "needle": "^2.2.1", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.2.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4" + } + }, + "nopt": { + "version": "4.0.1", + "bundled": true, + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + }, + "npm-bundled": { + "version": "1.0.5", + "bundled": true + }, + "npm-packlist": { + "version": "1.1.12", + "bundled": true, + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" + } + }, + "npmlog": { + "version": "4.1.2", + "bundled": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true + }, + "once": { + "version": "1.4.0", + "bundled": true, + "requires": { + "wrappy": "1" + } + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true + }, + "os-tmpdir": { + "version": "1.0.2", + "bundled": true + }, + "osenv": { + "version": "0.1.5", + "bundled": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true + }, + "process-nextick-args": { + "version": "2.0.0", + "bundled": true + }, + "rc": { + "version": "1.2.8", + "bundled": true, + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "bundled": true + } + } + }, + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "rimraf": { + "version": "2.6.2", + "bundled": true, + "requires": { + "glob": "^7.0.5" + } + }, + "safe-buffer": { + "version": "5.1.2", + "bundled": true + }, + "safer-buffer": { + "version": "2.1.2", + "bundled": true + }, + "semver": { + "version": "5.5.0", + "bundled": true + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true + }, + "tar": { + "version": "4.4.6", + "bundled": true, + "requires": { + "chownr": "^1.0.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.3.3", + "minizlib": "^1.1.0", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.2" + } + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true + }, + "wide-align": { + "version": "1.1.3", + "bundled": true, + "requires": { + "string-width": "^1.0.2 || 2" + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true + }, + "yallist": { + "version": "3.0.2", + "bundled": true + } + } + }, + "generate-function": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", + "integrity": "sha1-8GlhdpDBDIaOc7hGV0Z2T5fDR58=", + "requires": { + "is-property": "^1.0.2" + } + }, + "generate-object-property": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz", + "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=", + "requires": { + "is-property": "^1.0.0" + } + }, + "generic-pool": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-2.4.3.tgz", + "integrity": "sha1-eAw29p360FpaBF3Te+etyhGk9v8=" + }, + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha1-+Xj6TJDR3+f/LWvtoqUV5xO9z0o=" + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "requires": { + "assert-plus": "^1.0.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + } + } + }, + "glob": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", + "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=", + "optional": true, + "requires": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=" + }, + "graceful-readlink": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", + "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=", + "dev": true + }, + "growl": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.9.2.tgz", + "integrity": "sha1-Dqd0NxXbjY3ixe3hd14bRayFwC8=", + "dev": true + }, + "handlebars": { + "version": "4.0.12", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.0.12.tgz", + "integrity": "sha1-LBXIqW1G2l4mZwBRi6jLjZGdW8U=", + "dev": true, + "requires": { + "async": "^2.5.0", + "optimist": "^0.6.1", + "source-map": "^0.6.1", + "uglify-js": "^3.1.4" + }, + "dependencies": { + "async": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz", + "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==", + "dev": true, + "requires": { + "lodash": "^4.17.10" + } + }, + "optimist": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", + "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", + "dev": true, + "requires": { + "minimist": "~0.0.1", + "wordwrap": "~0.0.2" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", + "dev": true + } + } + }, + "har-validator": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-2.0.6.tgz", + "integrity": "sha1-zcvAgYgmWtEZtqWnyKtw7s+10n0=", + "requires": { + "chalk": "^1.1.1", + "commander": "^2.9.0", + "is-my-json-valid": "^2.12.4", + "pinkie-promise": "^2.0.0" + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", + "dev": true + }, + "hawk": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", + "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=", + "requires": { + "boom": "2.x.x", + "cryptiles": "2.x.x", + "hoek": "2.x.x", + "sntp": "1.x.x" + } + }, + "hiredis": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/hiredis/-/hiredis-0.5.0.tgz", + "integrity": "sha1-2wOpi+zXAD0TwmAEOs7s+s31m4c=", + "requires": { + "bindings": "^1.2.1", + "nan": "^2.3.4" + } + }, + "hoek": { + "version": "2.16.3", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", + "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=" + }, + "hosted-git-info": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", + "integrity": "sha1-l/I2l3vW4SVAiTD/bePuxigewEc=" + }, + "htmlparser2": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.3.tgz", + "integrity": "sha1-mWwosZFRaovoZQGn15dX5ccMEGg=", + "dev": true, + "requires": { + "domelementtype": "1", + "domhandler": "2.3", + "domutils": "1.5", + "entities": "1.0", + "readable-stream": "1.1" + }, + "dependencies": { + "readable-stream": { + "version": "1.1.14", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + } + } + }, + "http-errors": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.3.1.tgz", + "integrity": "sha1-GX4izevUGYWF6GlO9nhhl7ke2UI=", + "requires": { + "inherits": "~2.0.1", + "statuses": "1" + } + }, + "http-signature": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", + "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=", + "requires": { + "assert-plus": "^0.2.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "iconv-lite": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.2.11.tgz", + "integrity": "sha1-HOYKOleGSiktEyH/RgnKS7llrcg=", + "dev": true + }, + "ignore-walk": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.1.tgz", + "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==", + "dev": true, + "requires": { + "minimatch": "^3.0.4" + } + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "dev": true + }, + "invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=" + }, + "ipaddr.js": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.0.5.tgz", + "integrity": "sha1-X6eM8wG4JceKvDBC2BJyMEnqI8c=" + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" + }, + "is-builtin-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", + "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", + "requires": { + "builtin-modules": "^1.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-my-ip-valid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-my-ip-valid/-/is-my-ip-valid-1.0.0.tgz", + "integrity": "sha1-ezUbjo7dTTmV1NBmaA5mTZRpaCQ=" + }, + "is-my-json-valid": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.19.0.tgz", + "integrity": "sha1-j9bkA2PNBrlj+od9REv7Xt3GIXU=", + "requires": { + "generate-function": "^2.0.0", + "generate-object-property": "^1.1.0", + "is-my-ip-valid": "^1.0.0", + "jsonpointer": "^4.0.0", + "xtend": "^4.0.0" + } + }, + "is-property": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=" + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + }, + "is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=" + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + }, + "istanbul": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/istanbul/-/istanbul-0.4.5.tgz", + "integrity": "sha1-ZcfXPUxNqE1POsMQuRj7C4Azczs=", + "dev": true, + "requires": { + "abbrev": "1.0.x", + "async": "1.x", + "escodegen": "1.8.x", + "esprima": "2.7.x", + "glob": "^5.0.15", + "handlebars": "^4.0.1", + "js-yaml": "3.x", + "mkdirp": "0.5.x", + "nopt": "3.x", + "once": "1.x", + "resolve": "1.1.x", + "supports-color": "^3.1.0", + "which": "^1.1.1", + "wordwrap": "^1.0.0" + }, + "dependencies": { + "async": { + "version": "1.5.2", + "resolved": "http://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", + "dev": true + }, + "glob": { + "version": "5.0.15", + "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", + "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", + "dev": true, + "requires": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "requires": { + "has-flag": "^1.0.0" + } + }, + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "dev": true + } + } + }, + "js-string-escape": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/js-string-escape/-/js-string-escape-1.0.1.tgz", + "integrity": "sha1-4mJbrbwNZ8dTPp7cEGjFh65BN+8=" + }, + "js-yaml": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz", + "integrity": "sha1-6u1lbsg0TxD1J8a/obbiJE3hZ9E=", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "dependencies": { + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha1-E7BM2z5sXRnfkatph6hpVhmwqnE=", + "dev": true + } + } + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" + }, + "jshint": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.6.3.tgz", + "integrity": "sha1-hLRwuOXVzXrfCjvUl1JQRDydMRo=", + "dev": true, + "requires": { + "cli": "0.6.x", + "console-browserify": "1.1.x", + "exit": "0.1.x", + "htmlparser2": "3.8.x", + "minimatch": "1.0.x", + "shelljs": "0.3.x", + "strip-json-comments": "1.0.x", + "underscore": "1.6.x" + }, + "dependencies": { + "minimatch": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-1.0.0.tgz", + "integrity": "sha1-4N0hILSeG3JM6NcUxSCCKpQ4V20=", + "dev": true, + "requires": { + "lru-cache": "2", + "sigmund": "~1.0.0" + } + } + } + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + }, + "json3": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz", + "integrity": "sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE=", + "dev": true + }, + "jsonpointer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.0.1.tgz", + "integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk=" + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + } + } + }, + "lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "requires": { + "invert-kv": "^1.0.0" + } + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, + "libxmljs": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/libxmljs/-/libxmljs-0.19.5.tgz", + "integrity": "sha512-mKlafbSIsgZCXcha6loyMN/Q1kcbNJ5N3VC/rS0vrJXdI99TzEI2i2rPp10HN/B70vSPh6bq6nHhoCT19eGtTw==", + "dev": true, + "requires": { + "bindings": "~1.3.0", + "nan": "~2.10.0", + "node-pre-gyp": "~0.11.0" + }, + "dependencies": { + "nan": { + "version": "2.10.0", + "resolved": "http://registry.npmjs.org/nan/-/nan-2.10.0.tgz", + "integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==", + "dev": true + } + } + }, + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + } + }, + "lodash": { + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", + "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", + "dev": true + }, + "lodash._baseassign": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz", + "integrity": "sha1-jDigmVAPIVrQnlnxci/QxSv+Ck4=", + "dev": true, + "requires": { + "lodash._basecopy": "^3.0.0", + "lodash.keys": "^3.0.0" + } + }, + "lodash._basecopy": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz", + "integrity": "sha1-jaDmqHbPNEwK2KVIghEd08XHyjY=", + "dev": true + }, + "lodash._basecreate": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash._basecreate/-/lodash._basecreate-3.0.3.tgz", + "integrity": "sha1-G8ZhYU2qf8MRt9A78WgGoCE8+CE=", + "dev": true + }, + "lodash._getnative": { + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz", + "integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=", + "dev": true + }, + "lodash._isiterateecall": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz", + "integrity": "sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw=", + "dev": true + }, + "lodash.assign": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz", + "integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=" + }, + "lodash.create": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lodash.create/-/lodash.create-3.1.1.tgz", + "integrity": "sha1-1/KEnw29p+BGgruM1yqwIkYd6+c=", + "dev": true, + "requires": { + "lodash._baseassign": "^3.0.0", + "lodash._basecreate": "^3.0.0", + "lodash._isiterateecall": "^3.0.0" + } + }, + "lodash.isarguments": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", + "integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=", + "dev": true + }, + "lodash.isarray": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz", + "integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=", + "dev": true + }, + "lodash.keys": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", + "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", + "dev": true, + "requires": { + "lodash._getnative": "^3.0.0", + "lodash.isarguments": "^3.0.0", + "lodash.isarray": "^3.0.0" + } + }, + "log4js": { + "version": "git://github.com/cartodb/log4js-node.git#145d5f91e35e7fb14a6278cbf7a711ced6603727", + "from": "cartodb/log4js-node#cdb", + "requires": { + "async": "~0.2.0", + "readable-stream": "~1.0.2", + "semver": "~4.3.3", + "underscore": "1.8.2" + }, + "dependencies": { + "semver": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/semver/-/semver-4.3.6.tgz", + "integrity": "sha1-MAvG4OhjdPe6YQaLWx7NV/xlMto=" + }, + "underscore": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.2.tgz", + "integrity": "sha1-ZN8utZCJnelQeC83NRkLpC6/MR0=" + } + } + }, + "lru-cache": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.5.2.tgz", + "integrity": "sha1-H92tk4quEmPOE4aAvhs/WRwKtBw=" + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + }, + "mime": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.3.4.tgz", + "integrity": "sha1-EV+eO2s9rylZmDyzjxSaLUDrXVM=" + }, + "mime-db": { + "version": "1.37.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.37.0.tgz", + "integrity": "sha1-C2oM5v2+lXbiXx8tL96IMNwK0Ng=" + }, + "mime-types": { + "version": "2.1.21", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.21.tgz", + "integrity": "sha1-KJlaoey3cHQv5q5+WPkYHHRLP5Y=", + "requires": { + "mime-db": "~1.37.0" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + }, + "minipass": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.5.tgz", + "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + }, + "minizlib": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.1.1.tgz", + "integrity": "sha512-TrfjCjk4jLhcJyGMYymBH6oTXcWjYbUAXTHDbtnWHjZC25h0cdajHuPE1zxb4DVmu8crfh+HwH/WMuyLG0nHBg==", + "dev": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "requires": { + "minimist": "0.0.8" + } + }, + "mocha": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-3.2.0.tgz", + "integrity": "sha1-fcT0XlCIB1FxpoiWgU5q6et6heM=", + "dev": true, + "requires": { + "browser-stdout": "1.3.0", + "commander": "2.9.0", + "debug": "2.2.0", + "diff": "1.4.0", + "escape-string-regexp": "1.0.5", + "glob": "7.0.5", + "growl": "1.9.2", + "json3": "3.3.2", + "lodash.create": "3.1.1", + "mkdirp": "0.5.1", + "supports-color": "3.1.2" + }, + "dependencies": { + "commander": { + "version": "2.9.0", + "resolved": "http://registry.npmjs.org/commander/-/commander-2.9.0.tgz", + "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=", + "dev": true, + "requires": { + "graceful-readlink": ">= 1.0.0" + } + }, + "glob": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.0.5.tgz", + "integrity": "sha1-tCAqaQmbu00pKnwblbZoK2fr3JU=", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.2", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "supports-color": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.1.2.tgz", + "integrity": "sha1-cqJiiU2dQIuVbKBf83su2KbiotU=", + "dev": true, + "requires": { + "has-flag": "^1.0.0" + } + } + } + }, + "mockdate": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/mockdate/-/mockdate-2.0.2.tgz", + "integrity": "sha1-WuDA6vj+I+AJzQH5iJtCxPY0rxI=", + "dev": true + }, + "moment": { + "version": "2.22.2", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.22.2.tgz", + "integrity": "sha1-PCV/mDn8DpP/UxSWMiOeuQeD/2Y=", + "optional": true + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + }, + "multer": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/multer/-/multer-1.2.1.tgz", + "integrity": "sha1-dZlZxsGff7A9Ajnx3wsezsiEc6M=", + "requires": { + "append-field": "^0.1.0", + "busboy": "^0.2.11", + "concat-stream": "^1.5.0", + "mkdirp": "^0.5.1", + "object-assign": "^3.0.0", + "on-finished": "^2.3.0", + "type-is": "^1.6.4", + "xtend": "^4.0.0" + }, + "dependencies": { + "object-assign": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz", + "integrity": "sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I=" + } + } + }, + "mv": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz", + "integrity": "sha1-rmzg1vbV4KT32JN5jQPB6pVZtqI=", + "optional": true, + "requires": { + "mkdirp": "~0.5.1", + "ncp": "~2.0.0", + "rimraf": "~2.4.0" + } + }, + "nan": { + "version": "2.11.1", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.11.1.tgz", + "integrity": "sha1-kOIrzLjKV+pM03zIPTgZtS7qZ2Y=" + }, + "ncp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", + "integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=", + "optional": true + }, + "needle": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/needle/-/needle-2.2.4.tgz", + "integrity": "sha512-HyoqEb4wr/rsoaIDfTH2aVL9nWtQqba2/HvMv+++m8u0dz808MaagKILxtfeSN7QU7nvbQ79zk3vYOJp9zsNEA==", + "dev": true, + "requires": { + "debug": "^2.1.2", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + }, + "dependencies": { + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + } + } + }, + "negotiator": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.5.3.tgz", + "integrity": "sha1-Jp1cR2gQ7JLtvntsLygxY4T5p+g=" + }, + "node-pre-gyp": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.11.0.tgz", + "integrity": "sha512-TwWAOZb0j7e9eGaf9esRx3ZcLaE5tQ2lvYy1pb5IAaG1a2e2Kv5Lms1Y4hpj+ciXJRofIxxlt5haeQ/2ANeE0Q==", + "dev": true, + "requires": { + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.1", + "needle": "^2.2.1", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.2.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4" + }, + "dependencies": { + "glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "nopt": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", + "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", + "dev": true, + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + }, + "rimraf": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", + "dev": true, + "requires": { + "glob": "^7.0.5" + } + }, + "semver": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", + "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==", + "dev": true + } + } + }, + "node-statsd": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/node-statsd/-/node-statsd-0.0.7.tgz", + "integrity": "sha1-ltS70h3/LXmLM3Sj6TKc/szjr6g=" + }, + "node-uuid": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz", + "integrity": "sha1-sEDrCSOWivq/jTL7HxfxFn/auQc=" + }, + "nopt": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", + "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", + "dev": true, + "requires": { + "abbrev": "1" + } + }, + "normalize-package-data": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", + "integrity": "sha1-EvlaMH1YNSB1oEkHuErIvpisAS8=", + "requires": { + "hosted-git-info": "^2.1.4", + "is-builtin-module": "^1.0.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "npm-bundled": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.0.5.tgz", + "integrity": "sha512-m/e6jgWu8/v5niCUKQi9qQl8QdeEduFA96xHDDzFGqly0OOjI7c+60KM/2sppfnUU9JJagf+zs+yGhqSOFj71g==", + "dev": true + }, + "npm-packlist": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.1.12.tgz", + "integrity": "sha512-WJKFOVMeAlsU/pjXuqVdzU0WfgtIBCupkEVwn+1Y0ERAbUfWw8R4GjgVbaKnUjRoD2FoQbHOCbOyT5Mbs9Lw4g==", + "dev": true, + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" + } + }, + "npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "dev": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" + }, + "oauth-client": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/oauth-client/-/oauth-client-0.3.0.tgz", + "integrity": "sha1-IHgdIHOnu9p/WFmhz+voXGaYItU=", + "requires": { + "node-uuid": "1.1.0" + }, + "dependencies": { + "node-uuid": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.1.0.tgz", + "integrity": "sha1-URffgF7lzEAQ9UIpkYAULj/ySnA=" + } + } + }, + "oauth-sign": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", + "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "optimist": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.3.5.tgz", + "integrity": "sha1-A2VLUkFwMDEtEJ85sVmCW2AwkwQ=", + "requires": { + "wordwrap": "~0.0.2" + } + }, + "optionator": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", + "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", + "dev": true, + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.4", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "wordwrap": "~1.0.0" + }, + "dependencies": { + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "dev": true + } + } + }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", + "dev": true + }, + "os-locale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", + "requires": { + "lcid": "^1.0.0" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true + }, + "osenv": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", + "dev": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "packet-reader": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-0.3.1.tgz", + "integrity": "sha1-zWLmCvjX/qinBexP+ZCHHEaHHyc=" + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "requires": { + "error-ex": "^1.2.0" + } + }, + "parseurl": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", + "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=" + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "requires": { + "pinkie-promise": "^2.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "requires": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "pg": { + "version": "github:cartodb/node-postgres#5417d7b29b7272ca2e71bb396899ab3f177a9ae6", + "from": "github:cartodb/node-postgres#6.4.2-cdb2", + "requires": { + "buffer-writer": "1.0.1", + "js-string-escape": "1.0.1", + "packet-reader": "0.3.1", + "pg-connection-string": "0.1.3", + "pg-pool": "1.*", + "pg-types": "1.*", + "pgpass": "1.*", + "semver": "4.3.2" + } + }, + "pg-connection-string": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-0.1.3.tgz", + "integrity": "sha1-2hhHsglA5C7hSSvq9l1J2RskXfc=" + }, + "pg-copy-streams": { + "version": "github:cartodb/node-pg-copy-streams#94c8d89abaea54bd4e6f61f63f47ef81cdc44253", + "from": "github:cartodb/node-pg-copy-streams#v1.2.0-carto.3" + }, + "pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==" + }, + "pg-pool": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-1.8.0.tgz", + "integrity": "sha1-9+xzgkw3oD8Hb1G/33DjQBR8Tzc=", + "requires": { + "generic-pool": "2.4.3", + "object-assign": "4.1.0" + }, + "dependencies": { + "object-assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.0.tgz", + "integrity": "sha1-ejs9DpgGPUP0wD8uiubNUahog6A=" + } + } + }, + "pg-types": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-1.13.0.tgz", + "integrity": "sha512-lfKli0Gkl/+za/+b6lzENajczwZHc7D5kiUCZfgm914jipD2kIOIvEkAhZ8GrW3/TUoP9w8FHjwpPObBye5KQQ==", + "requires": { + "pg-int8": "1.0.1", + "postgres-array": "~1.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.0", + "postgres-interval": "^1.1.0" + } + }, + "pgpass": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.2.tgz", + "integrity": "sha1-Knu0G2BltnkH6R2hsHwYR8h3swY=", + "requires": { + "split": "^1.0.0" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "requires": { + "pinkie": "^2.0.0" + } + }, + "postgres-array": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-1.0.3.tgz", + "integrity": "sha512-5wClXrAP0+78mcsNX3/ithQ5exKvCyK5lr5NEEEeGwwM6NJdQgzIJBVxLvRW+huFpX92F2QnZ5CcokH0VhK2qQ==" + }, + "postgres-bytea": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha1-AntTPAqokOJtFy1Hz5zOzFIazTU=" + }, + "postgres-date": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.3.tgz", + "integrity": "sha1-4tiXAu/bJY/52c7g/pG9BpdSV6g=" + }, + "postgres-interval": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.1.2.tgz", + "integrity": "sha512-fC3xNHeTskCxL1dC8KOtxXt7YeFmlbTYtn7ul8MkVERuTmf7pI4DrkAxcw3kh1fQ9uz4wQmd03a1mRiXUZChfQ==", + "requires": { + "xtend": "^4.0.0" + } + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha1-o31zL0JxtKsa0HDTVQjoKQeI/6o=" + }, + "proxy-addr": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-1.0.10.tgz", + "integrity": "sha1-DUCoL4Afw1VWfS7LZe/j8HfxIcU=", + "requires": { + "forwarded": "~0.1.0", + "ipaddr.js": "1.0.5" + } + }, + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + }, + "qs": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.2.3.tgz", + "integrity": "sha1-HPyyXBCpsrSDBT/zn138kjOQjP4=" + }, + "queue-async": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/queue-async/-/queue-async-1.0.7.tgz", + "integrity": "sha1-Iq4KHaxKkvW81GNPmTxoKiqBCUU=" + }, + "range-parser": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.0.3.tgz", + "integrity": "sha1-aHKCNTXGkuLCoBA4Jq/YLC4P8XU=" + }, + "rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true + } + } + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "requires": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + } + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "requires": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + } + }, + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "redis": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/redis/-/redis-2.8.0.tgz", + "integrity": "sha512-M1OkonEQwtRmZv4tEWF2VgpG0JWJ8Fv1PhlgT5+B+uNq2cA3Rt1Yt/ryoR+vQNOQcIEgdCdfH0jr3bDpihAw1A==", + "requires": { + "double-ended-queue": "^2.1.0-0", + "redis-commands": "^1.2.0", + "redis-parser": "^2.6.0" + } + }, + "redis-commands": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.4.0.tgz", + "integrity": "sha512-cu8EF+MtkwI4DLIT0x9P8qNTLFhQD4jLfxLR0cCNkeGzs87FN6879JOJwNQR/1zD7aSYNbU0hgsV9zGY71Itvw==" + }, + "redis-mpool": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/redis-mpool/-/redis-mpool-0.7.0.tgz", + "integrity": "sha512-+Tk3LXK5YkmseZjsz6Bx6/uRHy0CBGVvl6GU5tlzugYRn1qArlxK9wSEhadIfWh96Qx9yngvtacb7g8GNi78rA==", + "requires": { + "generic-pool": "~2.1.1", + "hiredis": "~0.5.0", + "redis": "^2.8.0", + "underscore": "~1.6.0" + }, + "dependencies": { + "generic-pool": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-2.1.1.tgz", + "integrity": "sha1-rwTcLDJc/Ll1Aj+lK/zpYXp0Nf0=" + } + } + }, + "redis-parser": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-2.6.0.tgz", + "integrity": "sha1-Uu0J2srBCPGmMcB+m2mUHnoZUEs=" + }, + "redlock": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/redlock/-/redlock-2.0.1.tgz", + "integrity": "sha1-kHo5LuZsf65RZ8xcnv8izPXll1U=", + "requires": { + "bluebird": "^3.3.3" + } + }, + "request": { + "version": "2.75.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.75.0.tgz", + "integrity": "sha1-0rgmiihtoT6qXQGt9dGMyQ9lfZM=", + "requires": { + "aws-sign2": "~0.6.0", + "aws4": "^1.2.1", + "bl": "~1.1.2", + "caseless": "~0.11.0", + "combined-stream": "~1.0.5", + "extend": "~3.0.0", + "forever-agent": "~0.6.1", + "form-data": "~2.0.0", + "har-validator": "~2.0.6", + "hawk": "~3.1.3", + "http-signature": "~1.1.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.7", + "node-uuid": "~1.4.7", + "oauth-sign": "~0.8.1", + "qs": "~6.2.0", + "stringstream": "~0.0.4", + "tough-cookie": "~2.3.0", + "tunnel-agent": "~0.4.1" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=" + }, + "resolve": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", + "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", + "dev": true + }, + "rimraf": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", + "integrity": "sha1-7nEM5dk6j9uFb7Xqj/Di11k0sto=", + "optional": true, + "requires": { + "glob": "^6.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha1-mR7GnSluAxN0fVm9/St0XDX4go0=" + }, + "safe-json-stringify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.2.0.tgz", + "integrity": "sha1-NW5EvJjx+TzkXfFLzXwBzahuCv0=", + "optional": true + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha1-RPoWGwGHuVSd2Eu5GAL5vYOFzWo=" + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true + }, + "semver": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-4.3.2.tgz", + "integrity": "sha1-x6BxWKgL7dBSNVt3DYLWZA+AO+c=" + }, + "send": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.13.1.tgz", + "integrity": "sha1-ow1fTILIqbrprQCh2bG9vm8Zntc=", + "requires": { + "debug": "~2.2.0", + "depd": "~1.1.0", + "destroy": "~1.0.4", + "escape-html": "~1.0.3", + "etag": "~1.7.0", + "fresh": "0.3.0", + "http-errors": "~1.3.1", + "mime": "1.3.4", + "ms": "0.7.1", + "on-finished": "~2.3.0", + "range-parser": "~1.0.3", + "statuses": "~1.2.1" + }, + "dependencies": { + "ms": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", + "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=" + } + } + }, + "serve-static": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.10.3.tgz", + "integrity": "sha1-zlpuzTEB/tXsCYJ9rCKpwpv7BTU=", + "requires": { + "escape-html": "~1.0.3", + "parseurl": "~1.3.1", + "send": "0.13.2" + }, + "dependencies": { + "ms": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", + "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=" + }, + "send": { + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.13.2.tgz", + "integrity": "sha1-dl52B8gFVFK7pvCwUllTUJhgNt4=", + "requires": { + "debug": "~2.2.0", + "depd": "~1.1.0", + "destroy": "~1.0.4", + "escape-html": "~1.0.3", + "etag": "~1.7.0", + "fresh": "0.3.0", + "http-errors": "~1.3.1", + "mime": "1.3.4", + "ms": "0.7.1", + "on-finished": "~2.3.0", + "range-parser": "~1.0.3", + "statuses": "~1.2.1" + } + } + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + }, + "shapefile": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/shapefile/-/shapefile-0.3.0.tgz", + "integrity": "sha1-rEQSyblnaWLSM6IzkzjbRCypIwM=", + "dev": true, + "requires": { + "iconv-lite": "0.2", + "optimist": "0.3", + "queue-async": "1" + } + }, + "shelljs": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz", + "integrity": "sha1-NZbmMHp4FUT1kfN9phg2DzHbV7E=", + "dev": true + }, + "sigmund": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", + "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=", + "dev": true + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true + }, + "sntp": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", + "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=", + "requires": { + "hoek": "2.x.x" + } + }, + "source-map": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.2.0.tgz", + "integrity": "sha1-2rc/vPwrqBm03gO9b26qSBZLP50=", + "dev": true, + "optional": true, + "requires": { + "amdefine": ">=0.0.4" + } + }, + "spdx-correct": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.2.tgz", + "integrity": "sha1-GbtAnpG0exrVQVkkP3MSqFjbPC4=", + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", + "integrity": "sha1-LqRQrudPKom/uUUZwH/Nb0EyKXc=" + }, + "spdx-expression-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", + "integrity": "sha1-meEZt6XaAOBUkcn6M4t5BII7QdA=", + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.1.tgz", + "integrity": "sha1-4qMDI2ysVLBAMfp6WnnH5wHfhS8=" + }, + "split": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", + "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", + "requires": { + "through": "2" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "sqlite3": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-4.0.0.tgz", + "integrity": "sha1-zA4JOrUYc/UNnfxBJvy+8V1IZXA=", + "dev": true, + "requires": { + "nan": "~2.9.2", + "node-pre-gyp": "~0.9.0" + }, + "dependencies": { + "abbrev": { + "version": "1.1.1", + "resolved": "", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "aproba": { + "version": "1.2.0", + "resolved": "", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", + "dev": true + }, + "are-we-there-yet": { + "version": "1.1.4", + "resolved": "", + "integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=", + "dev": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "chownr": { + "version": "1.0.1", + "resolved": "", + "integrity": "sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE=", + "dev": true + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "debug": { + "version": "2.6.9", + "resolved": "", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "deep-extend": { + "version": "0.4.2", + "resolved": "", + "integrity": "sha1-SLaZwn4zS/ifEIkr5DL25MfTSn8=", + "dev": true + }, + "delegates": { + "version": "1.0.0", + "resolved": "", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", + "dev": true + }, + "detect-libc": { + "version": "1.0.3", + "resolved": "", + "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", + "dev": true + }, + "fs-minipass": { + "version": "1.2.5", + "resolved": "", + "integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==", + "dev": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "gauge": { + "version": "2.7.4", + "resolved": "", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "dev": true, + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "glob": { + "version": "7.1.2", + "resolved": "", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has-unicode": { + "version": "2.0.1", + "resolved": "", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", + "dev": true + }, + "iconv-lite": { + "version": "0.4.19", + "resolved": "", + "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==", + "dev": true + }, + "ignore-walk": { + "version": "3.0.1", + "resolved": "", + "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==", + "dev": true, + "requires": { + "minimatch": "^3.0.4" + } + }, + "inflight": { + "version": "1.0.6", + "resolved": "", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "ini": { + "version": "1.3.5", + "resolved": "", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "resolved": "", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + }, + "minipass": { + "version": "2.2.1", + "resolved": "", + "integrity": "sha512-u1aUllxPJUI07cOqzR7reGmQxmCqlH88uIIsf6XZFEWgw7gXKpJdR+5R9Y3KEDmWYkdIz9wXZs3C0jOPxejk/Q==", + "dev": true, + "requires": { + "yallist": "^3.0.0" + } + }, + "minizlib": { + "version": "1.1.0", + "resolved": "", + "integrity": "sha512-4T6Ur/GctZ27nHfpt9THOdRZNgyJ9FZchYO1ceg5S8Q3DNLCKYy44nCZzgCJgcvx2UM8czmqak5BCxJMrq37lA==", + "dev": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "nan": { + "version": "2.9.2", + "resolved": "http://registry.npmjs.org/nan/-/nan-2.9.2.tgz", + "integrity": "sha512-ltW65co7f3PQWBDbqVvaU1WtFJUsNW7sWWm4HINhbMQIyVyzIeyZ8toX5TC5eeooE6piZoaEh4cZkueSKG3KYw==", + "dev": true + }, + "needle": { + "version": "2.2.0", + "resolved": "", + "integrity": "sha512-eFagy6c+TYayorXw/qtAdSvaUpEbBsDwDyxYFgLZ0lTojfH7K+OdBqAF7TAFwDokJaGpubpSGG0wO3iC0XPi8w==", + "dev": true, + "requires": { + "debug": "^2.1.2", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + } + }, + "node-pre-gyp": { + "version": "0.9.0", + "resolved": "", + "integrity": "sha1-vdTDr6ybGx6/8Kn/M2KFnrZ4G7g=", + "dev": true, + "requires": { + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.1", + "needle": "^2.2.0", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.1.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4" + } + }, + "nopt": { + "version": "4.0.1", + "resolved": "", + "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", + "dev": true, + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + }, + "npm-bundled": { + "version": "1.0.3", + "resolved": "", + "integrity": "sha512-ByQ3oJ/5ETLyglU2+8dBObvhfWXX8dtPZDMePCahptliFX2iIuhyEszyFk401PZUNQH20vvdW5MLjJxkwU80Ow==", + "dev": true + }, + "npm-packlist": { + "version": "1.1.10", + "resolved": "", + "integrity": "sha512-AQC0Dyhzn4EiYEfIUjCdMl0JJ61I2ER9ukf/sLxJUcZHfo+VyEfz2rMJgLZSS1v30OxPQe1cN0LZA1xbcaVfWA==", + "dev": true, + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" + } + }, + "npmlog": { + "version": "4.1.2", + "resolved": "", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "dev": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "os-homedir": { + "version": "1.0.2", + "resolved": "", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", + "dev": true + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true + }, + "osenv": { + "version": "0.1.5", + "resolved": "", + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", + "dev": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.0", + "resolved": "", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", + "dev": true + }, + "rc": { + "version": "1.2.6", + "resolved": "", + "integrity": "sha1-6xiYnG1PTxYsOZ953dKfODVWgJI=", + "dev": true, + "requires": { + "deep-extend": "~0.4.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } + } + }, + "readable-stream": { + "version": "2.3.5", + "resolved": "", + "integrity": "sha512-tK0yDhrkygt/knjowCUiWP9YdV7c5R+8cR0r/kt9ZhBU906Fs6RpQJCEilamRJj1Nx2rWI6LkW9gKqjTkshhEw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.0.3", + "util-deprecate": "~1.0.1" + } + }, + "rimraf": { + "version": "2.6.2", + "resolved": "", + "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", + "dev": true, + "requires": { + "glob": "^7.0.5" + } + }, + "safe-buffer": { + "version": "5.1.1", + "resolved": "", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", + "dev": true + }, + "sax": { + "version": "1.2.4", + "resolved": "", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true + }, + "semver": { + "version": "5.5.0", + "resolved": "", + "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", + "dev": true + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true + }, + "string-width": { + "version": "1.0.2", + "resolved": "", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "string_decoder": { + "version": "1.0.3", + "resolved": "", + "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true + }, + "tar": { + "version": "4.4.0", + "resolved": "", + "integrity": "sha512-gJlTiiErwo96K904FnoYWl+5+FBgS+FimU6GMh66XLdLa55al8+d4jeDfPoGwSNHdtWI5FJP6xurmVqhBuGJpQ==", + "dev": true, + "requires": { + "chownr": "^1.0.1", + "fs-minipass": "^1.2.3", + "minipass": "^2.2.1", + "minizlib": "^1.1.0", + "mkdirp": "^0.5.0", + "yallist": "^3.0.2" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "wide-align": { + "version": "1.1.2", + "resolved": "", + "integrity": "sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==", + "dev": true, + "requires": { + "string-width": "^1.0.2" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "yallist": { + "version": "3.0.2", + "resolved": "", + "integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k=", + "dev": true + } + } + }, + "sshpk": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.15.1.tgz", + "integrity": "sha1-t5oImnMuNGxuBxSDDzYoXNOBkaI=", + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + } + } + }, + "statuses": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.2.1.tgz", + "integrity": "sha1-3e1FzBglbVHtQK7BQkidXGECbSg=" + }, + "step": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/step/-/step-0.0.6.tgz", + "integrity": "sha1-FD54SaXX0/SgiP4pr5SRUhbu7eI=" + }, + "step-profiler": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/step-profiler/-/step-profiler-0.3.0.tgz", + "integrity": "sha1-hBNozkTyMwyGLt1exg5/vsFI8LM=", + "requires": { + "debug": "^2.2.0" + } + }, + "streamsearch": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz", + "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=" + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + }, + "stringstream": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.6.tgz", + "integrity": "sha1-eIAiWw1K0Q4wkn0Weh1vL9OzOnI=" + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "requires": { + "is-utf8": "^0.2.0" + } + }, + "strip-json-comments": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.4.tgz", + "integrity": "sha1-HhX7ysl9Pumb8tc7TGVrCCu6+5E=", + "dev": true + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" + }, + "tar": { + "version": "4.4.7", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.7.tgz", + "integrity": "sha512-mR3MzsCdN0IEWjZRuF/J9gaWHnTwOvzjqPTcvi1xXgfKTDQRp39gRETPQEfPByAdEOGmZfx1HrRsn8estaEvtA==", + "dev": true, + "requires": { + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.3.4", + "minizlib": "^1.1.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.2" + } + }, + "through": { + "version": "2.3.8", + "resolved": "http://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" + }, + "topojson": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/topojson/-/topojson-0.0.8.tgz", + "integrity": "sha1-MMnG/6s1wgNUuNRO+iFD+IPxIm4=", + "requires": { + "optimist": "0.3.5" + } + }, + "tough-cookie": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz", + "integrity": "sha1-7GDO44rGdQY//JelwYlwV47oNlU=", + "requires": { + "punycode": "^1.4.1" + } + }, + "tunnel-agent": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.3.tgz", + "integrity": "sha1-Y3PbdpCf5XDgjXNYM2Xtgop07us=" + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2" + } + }, + "type-is": { + "version": "1.6.16", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", + "integrity": "sha1-+JzjQVQcZysl7nrjxz3uOyvlAZQ=", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.18" + } + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" + }, + "uglify-js": { + "version": "3.4.9", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.9.tgz", + "integrity": "sha1-rwLxgMEgfXZDLkc+0koo9KeCuuM=", + "dev": true, + "optional": true, + "requires": { + "commander": "~2.17.1", + "source-map": "~0.6.1" + }, + "dependencies": { + "commander": { + "version": "2.17.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", + "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==", + "dev": true, + "optional": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", + "dev": true, + "optional": true + } + } + }, + "underscore": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", + "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag=" + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "utils-merge": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.0.tgz", + "integrity": "sha1-ApT7kiu5N1FTVBxPcJYjHyh8ivg=" + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha1-/JH2uce6FchX9MssXe/uw51PQQo=", + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "vary": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.0.1.tgz", + "integrity": "sha1-meSYFWaihhGN+yuBc1ffeZM3bRA=" + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + } + } + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha1-pFBD1U9YBTFtqNYvn1CRjT2nCwo=", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", + "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=" + }, + "wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "dev": true, + "requires": { + "string-width": "^1.0.2 || 2" + } + }, + "window-size": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.2.0.tgz", + "integrity": "sha1-tDFbtCFKPXBY6+7okuE/ok2YsHU=" + }, + "wordwrap": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", + "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=" + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" + }, + "y18n": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=" + }, + "yallist": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.2.tgz", + "integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k=", + "dev": true + }, + "yargs": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-5.0.0.tgz", + "integrity": "sha1-M1UUSXfQV1fbuG1uOOwFYSOzpm4=", + "requires": { + "cliui": "^3.2.0", + "decamelize": "^1.1.1", + "get-caller-file": "^1.0.1", + "lodash.assign": "^4.2.0", + "os-locale": "^1.4.0", + "read-pkg-up": "^1.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^1.0.2", + "which-module": "^1.0.0", + "window-size": "^0.2.0", + "y18n": "^3.2.1", + "yargs-parser": "^3.2.0" + } + }, + "yargs-parser": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-3.2.0.tgz", + "integrity": "sha1-UIE1XRnZ0MjF2BrakIy05tGGZk8=", + "requires": { + "camelcase": "^3.0.0", + "lodash.assign": "^4.1.0" + } + }, + "zipfile": { + "version": "0.5.11", + "resolved": "https://registry.npmjs.org/zipfile/-/zipfile-0.5.11.tgz", + "integrity": "sha1-pGwbdnBhOPubz4OJAZPK7GefaF4=", + "dev": true, + "requires": { + "nan": "~2.4.0", + "node-pre-gyp": "~0.6.30" + }, + "dependencies": { + "nan": { + "version": "2.4.0", + "resolved": "http://registry.npmjs.org/nan/-/nan-2.4.0.tgz", + "integrity": "sha1-+zxZ1F/k7/4hXwuJD4rfbrMtIjI=", + "dev": true + }, + "node-pre-gyp": { + "version": "0.6.32", + "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.6.32.tgz", + "integrity": "sha1-/EUrN25zGbPSVfXzSFPvb9j+H9U=", + "dev": true, + "requires": { + "mkdirp": "~0.5.1", + "nopt": "~3.0.6", + "npmlog": "^4.0.1", + "rc": "~1.1.6", + "request": "^2.79.0", + "rimraf": "~2.5.4", + "semver": "~5.3.0", + "tar": "~2.2.1", + "tar-pack": "~3.3.0" + }, + "dependencies": { + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + }, + "dependencies": { + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + } + } + }, + "nopt": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", + "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", + "dev": true, + "requires": { + "abbrev": "1" + }, + "dependencies": { + "abbrev": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz", + "integrity": "sha1-kbR5JYinc4wl813W9jdSovh3YTU=", + "dev": true + } + } + }, + "npmlog": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.0.2.tgz", + "integrity": "sha1-0DlQ4OeM4VJ7om0qdZLpNIrD518=", + "dev": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.1", + "set-blocking": "~2.0.0" + }, + "dependencies": { + "are-we-there-yet": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.2.tgz", + "integrity": "sha1-gORw6VoIR5T+GJkmLFZnxuiN4bM=", + "dev": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.0 || ^1.1.13" + }, + "dependencies": { + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", + "dev": true + }, + "readable-stream": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.2.tgz", + "integrity": "sha1-qeb+w8fdqF+LsbO6cChgRVb8gl4=", + "dev": true, + "requires": { + "buffer-shims": "^1.0.0", + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "~1.0.0", + "process-nextick-args": "~1.0.6", + "string_decoder": "~0.10.x", + "util-deprecate": "~1.0.1" + }, + "dependencies": { + "buffer-shims": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz", + "integrity": "sha1-mXjOMXOIxkmth5MCjDR37wRKi1E=", + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "process-nextick-args": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", + "dev": true + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + } + } + } + } + }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", + "dev": true + }, + "gauge": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.2.tgz", + "integrity": "sha1-Fc7MMbAtBTRaXWsOFxzbOtIwd3Q=", + "dev": true, + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "supports-color": "^0.2.0", + "wide-align": "^1.1.0" + }, + "dependencies": { + "aproba": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.0.4.tgz", + "integrity": "sha1-JxNoB3XnYUyLoYbAZdTi5S0QcsA=", + "dev": true + }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", + "dev": true + }, + "object-assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.0.tgz", + "integrity": "sha1-ejs9DpgGPUP0wD8uiubNUahog6A=", + "dev": true + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + }, + "dependencies": { + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + }, + "dependencies": { + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true + } + } + } + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.0.0.tgz", + "integrity": "sha1-xQYbbg74qBd15Q9dZhUb9r83EQc=", + "dev": true + } + } + }, + "supports-color": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-0.2.0.tgz", + "integrity": "sha1-2S3iaU6z9nMjlz1649i1W0wiGQo=", + "dev": true + }, + "wide-align": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.0.tgz", + "integrity": "sha1-QO3egCpx/qHwcNo+YtzaLnrdlq0=", + "dev": true, + "requires": { + "string-width": "^1.0.1" + } + } + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + } + } + }, + "rc": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.1.6.tgz", + "integrity": "sha1-Q2UbdrauU7XIAvEVH6P8OwWZack=", + "dev": true, + "requires": { + "deep-extend": "~0.4.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~1.0.4" + }, + "dependencies": { + "deep-extend": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.1.tgz", + "integrity": "sha1-7+QRPQgIX05vlod1mBD4B0aeIlM=", + "dev": true + }, + "ini": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.4.tgz", + "integrity": "sha1-BTfLedr1m1mhpRff9wbIbsA5Fi4=", + "dev": true + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + }, + "strip-json-comments": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.4.tgz", + "integrity": "sha1-HhX7ysl9Pumb8tc7TGVrCCu6+5E=", + "dev": true + } + } + }, + "request": { + "version": "2.79.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.79.0.tgz", + "integrity": "sha1-Tf5b9r6LjNw3/Pk+BLZVd3InEN4=", + "dev": true, + "requires": { + "aws-sign2": "~0.6.0", + "aws4": "^1.2.1", + "caseless": "~0.11.0", + "combined-stream": "~1.0.5", + "extend": "~3.0.0", + "forever-agent": "~0.6.1", + "form-data": "~2.1.1", + "har-validator": "~2.0.6", + "hawk": "~3.1.3", + "http-signature": "~1.1.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.7", + "oauth-sign": "~0.8.1", + "qs": "~6.3.0", + "stringstream": "~0.0.4", + "tough-cookie": "~2.3.0", + "tunnel-agent": "~0.4.1", + "uuid": "^3.0.0" + }, + "dependencies": { + "aws-sign2": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", + "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=", + "dev": true + }, + "aws4": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.5.0.tgz", + "integrity": "sha1-Cin/t5wxyecS7rCH6OemS0pW11U=", + "dev": true + }, + "caseless": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.11.0.tgz", + "integrity": "sha1-cVuW6phBWTzDMGeSP17GDr2k99c=", + "dev": true + }, + "combined-stream": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", + "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=", + "dev": true, + "requires": { + "delayed-stream": "~1.0.0" + }, + "dependencies": { + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true + } + } + }, + "extend": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.0.tgz", + "integrity": "sha1-WkdDU7nzNT3dgXbf03uRyDpG8dQ=", + "dev": true + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "dev": true + }, + "form-data": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.2.tgz", + "integrity": "sha1-icNTQAi5fq2ky7FX1Y9vXfAl6uQ=", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.5", + "mime-types": "^2.1.12" + }, + "dependencies": { + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + } + } + }, + "har-validator": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-2.0.6.tgz", + "integrity": "sha1-zcvAgYgmWtEZtqWnyKtw7s+10n0=", + "dev": true, + "requires": { + "chalk": "^1.1.1", + "commander": "^2.9.0", + "is-my-json-valid": "^2.12.4", + "pinkie-promise": "^2.0.0" + }, + "dependencies": { + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.0.0.tgz", + "integrity": "sha1-xQYbbg74qBd15Q9dZhUb9r83EQc=", + "dev": true + } + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.0.0.tgz", + "integrity": "sha1-xQYbbg74qBd15Q9dZhUb9r83EQc=", + "dev": true + } + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, + "commander": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", + "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=", + "dev": true, + "requires": { + "graceful-readlink": ">= 1.0.0" + }, + "dependencies": { + "graceful-readlink": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", + "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=", + "dev": true + } + } + }, + "is-my-json-valid": { + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.15.0.tgz", + "integrity": "sha1-k27do8o8IR/ZjzstPgjaQ/eykVs=", + "dev": true, + "requires": { + "generate-function": "^2.0.0", + "generate-object-property": "^1.1.0", + "jsonpointer": "^4.0.0", + "xtend": "^4.0.0" + }, + "dependencies": { + "generate-function": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz", + "integrity": "sha1-aFj+fAlpt9TpCTM3ZHrHn2DfvnQ=", + "dev": true + }, + "generate-object-property": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz", + "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=", + "dev": true, + "requires": { + "is-property": "^1.0.0" + }, + "dependencies": { + "is-property": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=", + "dev": true + } + } + }, + "jsonpointer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.0.0.tgz", + "integrity": "sha1-ZmHhYdL8RF8Z+YQwIxNDci4fy9U=", + "dev": true + }, + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", + "dev": true + } + } + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "requires": { + "pinkie": "^2.0.0" + }, + "dependencies": { + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true + } + } + } + } + }, + "hawk": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", + "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=", + "dev": true, + "requires": { + "boom": "2.x.x", + "cryptiles": "2.x.x", + "hoek": "2.x.x", + "sntp": "1.x.x" + }, + "dependencies": { + "boom": { + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", + "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", + "dev": true, + "requires": { + "hoek": "2.x.x" + } + }, + "cryptiles": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", + "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=", + "dev": true, + "requires": { + "boom": "2.x.x" + } + }, + "hoek": { + "version": "2.16.3", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", + "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=", + "dev": true + }, + "sntp": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", + "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=", + "dev": true, + "requires": { + "hoek": "2.x.x" + } + } + } + }, + "http-signature": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", + "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=", + "dev": true, + "requires": { + "assert-plus": "^0.2.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + }, + "dependencies": { + "assert-plus": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", + "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=", + "dev": true + }, + "jsprim": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.3.1.tgz", + "integrity": "sha1-KnJW9wQSop7jZwqspiWZTE3P8lI=", + "dev": true, + "requires": { + "extsprintf": "1.0.2", + "json-schema": "0.2.3", + "verror": "1.3.6" + }, + "dependencies": { + "extsprintf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.0.2.tgz", + "integrity": "sha1-4QgOBljjALBilJkMxw4VAiNf1VA=", + "dev": true + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "dev": true + }, + "verror": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.3.6.tgz", + "integrity": "sha1-z/XfEpRtKX0rqu+qJoniW+AcAFw=", + "dev": true, + "requires": { + "extsprintf": "1.0.2" + } + } + } + }, + "sshpk": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.10.1.tgz", + "integrity": "sha1-MOGl0ykkSXShr2FREznVla9mOLA=", + "dev": true, + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jodid25519": "^1.0.0", + "jsbn": "~0.1.0", + "tweetnacl": "~0.14.0" + }, + "dependencies": { + "asn1": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", + "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=", + "dev": true + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + }, + "bcrypt-pbkdf": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.0.tgz", + "integrity": "sha1-PKdrhSQccXC/fZcD57mqdGMAQNQ=", + "dev": true, + "optional": true, + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "ecc-jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", + "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", + "dev": true, + "optional": true, + "requires": { + "jsbn": "~0.1.0" + } + }, + "getpass": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.6.tgz", + "integrity": "sha1-KD/9n8ElaECHUxHBtg6MQBhxEOY=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "jodid25519": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/jodid25519/-/jodid25519-1.0.2.tgz", + "integrity": "sha1-BtSRIlUJNBlHfUJWM2BuDpB4KWc=", + "dev": true, + "optional": true, + "requires": { + "jsbn": "~0.1.0" + } + }, + "jsbn": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.0.tgz", + "integrity": "sha1-ZQmH2g3XT06/WhE3eiqi0nPpff0=", + "dev": true, + "optional": true + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "dev": true, + "optional": true + } + } + } + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "dev": true + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "dev": true + }, + "mime-types": { + "version": "2.1.13", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.13.tgz", + "integrity": "sha1-4HqqnGxrmnyjASxpADrSWjnpKog=", + "dev": true, + "requires": { + "mime-db": "~1.25.0" + }, + "dependencies": { + "mime-db": { + "version": "1.25.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.25.0.tgz", + "integrity": "sha1-wY29fHOl2/b0SgJNwNFloeexw5I=", + "dev": true + } + } + }, + "oauth-sign": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", + "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=", + "dev": true + }, + "qs": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.3.0.tgz", + "integrity": "sha1-9AOyZPI7wBIox0ExtAfxjV6l1EI=", + "dev": true + }, + "stringstream": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", + "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=", + "dev": true + }, + "tough-cookie": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.2.tgz", + "integrity": "sha1-8IH3bkyFcg5sN6X6ztc3FQ2EByo=", + "dev": true, + "requires": { + "punycode": "^1.4.1" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + } + } + }, + "tunnel-agent": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.3.tgz", + "integrity": "sha1-Y3PbdpCf5XDgjXNYM2Xtgop07us=", + "dev": true + }, + "uuid": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.0.1.tgz", + "integrity": "sha1-ZUS7ot/ajBzxfmKaOjBeK7H+5sE=", + "dev": true + } + } + }, + "rimraf": { + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.5.4.tgz", + "integrity": "sha1-loAAk8vxoMhr2VtGJUZ1NcKd+gQ=", + "dev": true, + "requires": { + "glob": "^7.0.5" + }, + "dependencies": { + "glob": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz", + "integrity": "sha1-gFIR3wT6rxxjo2ADBs31reULLsg=", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.2", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "dependencies": { + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + }, + "dependencies": { + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + } + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "minimatch": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz", + "integrity": "sha1-Kk5AkLlrLbBqnX3wEFWmKnfJt3Q=", + "dev": true, + "requires": { + "brace-expansion": "^1.0.0" + }, + "dependencies": { + "brace-expansion": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.6.tgz", + "integrity": "sha1-cZfX6qm4fmSDkOph/GbIRCdCDfk=", + "dev": true, + "requires": { + "balanced-match": "^0.4.1", + "concat-map": "0.0.1" + }, + "dependencies": { + "balanced-match": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", + "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + } + } + } + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + }, + "dependencies": { + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + } + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + } + } + } + } + }, + "semver": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", + "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=", + "dev": true + }, + "tar": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", + "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=", + "dev": true, + "requires": { + "block-stream": "*", + "fstream": "^1.0.2", + "inherits": "2" + }, + "dependencies": { + "block-stream": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", + "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", + "dev": true, + "requires": { + "inherits": "~2.0.0" + } + }, + "fstream": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.10.tgz", + "integrity": "sha1-YE6Kkv4m/9n2+uMDmdSYThqyKCI=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" + }, + "dependencies": { + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + "dev": true + } + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + } + } + }, + "tar-pack": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/tar-pack/-/tar-pack-3.3.0.tgz", + "integrity": "sha1-MJMYFkGPVa/E0hd1r91nIM7kXa4=", + "dev": true, + "requires": { + "debug": "~2.2.0", + "fstream": "~1.0.10", + "fstream-ignore": "~1.0.5", + "once": "~1.3.3", + "readable-stream": "~2.1.4", + "rimraf": "~2.5.1", + "tar": "~2.2.1", + "uid-number": "~0.0.6" + }, + "dependencies": { + "debug": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", + "dev": true, + "requires": { + "ms": "0.7.1" + }, + "dependencies": { + "ms": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", + "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=", + "dev": true + } + } + }, + "fstream": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.10.tgz", + "integrity": "sha1-YE6Kkv4m/9n2+uMDmdSYThqyKCI=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" + }, + "dependencies": { + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + "dev": true + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + } + } + }, + "fstream-ignore": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/fstream-ignore/-/fstream-ignore-1.0.5.tgz", + "integrity": "sha1-nDHa40dnAY/h0kmyTa2mfQktoQU=", + "dev": true, + "requires": { + "fstream": "^1.0.0", + "inherits": "2", + "minimatch": "^3.0.0" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "minimatch": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz", + "integrity": "sha1-Kk5AkLlrLbBqnX3wEFWmKnfJt3Q=", + "dev": true, + "requires": { + "brace-expansion": "^1.0.0" + }, + "dependencies": { + "brace-expansion": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.6.tgz", + "integrity": "sha1-cZfX6qm4fmSDkOph/GbIRCdCDfk=", + "dev": true, + "requires": { + "balanced-match": "^0.4.1", + "concat-map": "0.0.1" + }, + "dependencies": { + "balanced-match": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", + "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + } + } + } + } + } + } + }, + "once": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz", + "integrity": "sha1-suJhVXzkwxTsgwTz+oJmPkKXyiA=", + "dev": true, + "requires": { + "wrappy": "1" + }, + "dependencies": { + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + } + } + }, + "readable-stream": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.1.5.tgz", + "integrity": "sha1-ZvqLcg4UOLNkaB8q0aY8YYRIydA=", + "dev": true, + "requires": { + "buffer-shims": "^1.0.0", + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "~1.0.0", + "process-nextick-args": "~1.0.6", + "string_decoder": "~0.10.x", + "util-deprecate": "~1.0.1" + }, + "dependencies": { + "buffer-shims": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz", + "integrity": "sha1-mXjOMXOIxkmth5MCjDR37wRKi1E=", + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "process-nextick-args": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", + "dev": true + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + } + } + }, + "uid-number": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/uid-number/-/uid-number-0.0.6.tgz", + "integrity": "sha1-DqEOgDXo61uOREnwbaHHMGY7qoE=", + "dev": true + } + } + } + } + } + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..7e52f59 --- /dev/null +++ b/package.json @@ -0,0 +1,70 @@ +{ + "private": true, + "name": "cartodb_sql_api", + "description": "high speed SQL api for cartodb", + "keywords": [ + "cartodb" + ], + "version": "2.4.0", + "repository": { + "type": "git", + "url": "git://github.com/CartoDB/CartoDB-SQL-API.git" + }, + "license": "BSD-3-Clause", + "author": "Vizzuality (http://vizzuality.com)", + "contributors": [ + "Simon Tokumine ", + "Sandro Santilli " + ], + "dependencies": { + "@carto/fqdn-sync": "0.2.2", + "basic-auth": "^2.0.0", + "bintrees": "1.0.1", + "bunyan": "1.8.1", + "cartodb-psql": "0.13.1", + "cartodb-query-tables": "0.4.0", + "cartodb-redis": "2.1.0", + "debug": "2.2.0", + "express": "~4.13.3", + "gc-stats": "1.2.1", + "log4js": "cartodb/log4js-node#cdb", + "lru-cache": "~2.5.0", + "mkdirp": "0.5.1", + "multer": "~1.2.0", + "node-statsd": "~0.0.7", + "node-uuid": "^1.4.7", + "oauth-client": "0.3.0", + "pg-copy-streams": "cartodb/node-pg-copy-streams#v1.2.0-carto.3", + "qs": "~6.2.1", + "queue-async": "~1.0.7", + "redis-mpool": "0.7.0", + "redlock": "2.0.1", + "request": "~2.75.0", + "step": "~0.0.5", + "step-profiler": "~0.3.0", + "topojson": "0.0.8", + "underscore": "~1.6.0", + "yargs": "~5.0.0" + }, + "devDependencies": { + "istanbul": "0.4.5", + "jshint": "~2.6.0", + "libxmljs": "0.19.5", + "mocha": "3.2.0", + "mockdate": "^2.0.2", + "shapefile": "0.3.0", + "sqlite3": "4.0.0", + "zipfile": "0.5.11" + }, + "scripts": { + "test": "make test-all", + "test:unit": "mocha test/unit/**/*.js", + "test:unit:watch": "npm run test:unit -- -w", + "docker-test": "./docker-test.sh", + "docker-bash": "./docker-bash.sh" + }, + "engines": { + "node": "6.9.2 || >= 10.14.2", + "npm": "3.10.9 || 3.10.10 || >= 6.4.1" + } +} diff --git a/test/README.md b/test/README.md new file mode 100644 index 0000000..ea52e44 --- /dev/null +++ b/test/README.md @@ -0,0 +1,21 @@ +cartodb-sql-api tests +--------------------- +Tests require you create a test database and set some redis keys before, +you can execute prepare_db.sh script, it will create database, users +and redis stuff for you. Be sure postgres and redis are running. + +> cd test && ./prepare_db.sh + +Note that "make check" from top-level dir will try to do everything +needed to prepare & run the tests. + + +Acceptance tests (need ctrl-C to exit) +-------------------------------------- +> mocha -u tdd test/acceptance/app.test.js +> mocha -u tdd test/acceptance/app.auth.test.js + + +Unit tests +-------------------------------- +> mocha -u tdd test/unit/*.js (or run the tests individually) diff --git a/test/acceptance/app-configuration.js b/test/acceptance/app-configuration.js new file mode 100644 index 0000000..418ccd3 --- /dev/null +++ b/test/acceptance/app-configuration.js @@ -0,0 +1,169 @@ +'use strict'; + +require('../helper'); + +var server = require('../../app/server')(); +var assert = require('../support/assert'); + + +describe('app-configuration', function() { + + var RESPONSE_OK = { + statusCode: 200 + }; + + 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){ + assert.response(server, { + url: '/api/v1/version', + method: 'GET' + }, 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); + assert.equal(parsed.cartodb_sql_api, sqlapi_version); + done(); + }); + }); + + it('GET /api/v1/sql', function(done){ + assert.response(server, { + url: '/api/v1/sql', + headers: {host: 'vizzuality.cartodb.com'}, + method: 'GET' + },{ + status: 400 + }, 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"]}); + done(); + }); + }); + + // Test base_url setting + it('GET /api/whatever/sql', function(done){ + assert.response(server, { + url: '/api/whatever/sql?q=SELECT%201', + headers: {host: 'vizzuality.cartodb.com'}, + method: 'GET' + }, RESPONSE_OK, done); + }); + + // Test CORS headers with GET + it('GET /api/whatever/sql', function(done){ + assert.response(server, { + url: '/api/whatever/sql?q=SELECT%201', + headers: {host: 'vizzuality.cartodb.com'}, + method: 'GET' + }, RESPONSE_OK, function(err, res) { + assert.equal( + res.headers['access-control-allow-headers'], + 'X-Requested-With, X-Prototype-Version, X-CSRF-Token, Authorization' + ); + assert.equal(res.headers['access-control-allow-origin'], '*'); + done(); + }); + }); + + // Test that OPTIONS does not run queries + it('OPTIONS /api/x/sql', function(done){ + assert.response(server, { + url: '/api/x/sql?q=syntax%20error', + headers: {host: 'vizzuality.cartodb.com'}, + method: 'OPTIONS' + }, RESPONSE_OK, function(err, res) { + assert.equal(res.body, ''); + assert.equal( + res.headers['access-control-allow-headers'], + 'X-Requested-With, X-Prototype-Version, X-CSRF-Token, Authorization' + ); + assert.equal(res.headers['access-control-allow-origin'], '*'); + 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'}, + method: 'GET' + }, 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 + 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_persist); + done(); + }); + }); + + // See https://github.com/CartoDB/CartoDB-SQL-API/issues/121 + 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'}, + method: 'GET' + }, RESPONSE_OK, function(err, res) { + global.settings.db_host = backupDBHost; + try { + var parsed = JSON.parse(res.body); + assert.equal(parsed.rows.length, 1); + assert.equal(parsed.rows[0].n, 2); + } catch (e) { + return done(e); + } + done(); + }); + }); + + // See https://github.com/CartoDB/CartoDB-SQL-API/issues/120 + 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'}, + method: 'GET' + }, 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); + } catch (e) { + return done(e); + } + return done(); + }); + }); + + /** + * CORS + */ + 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'}, + method: 'GET' + }, 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); + assert.equal(res.headers['access-control-allow-origin'], '*'); + assert.equal( + res.headers['access-control-allow-headers'], + "X-Requested-With, X-Prototype-Version, X-CSRF-Token, Authorization" + ); + done(); + }); + }); + +}); diff --git a/test/acceptance/app.auth.test.js b/test/acceptance/app.auth.test.js new file mode 100644 index 0000000..34bdc2f --- /dev/null +++ b/test/acceptance/app.auth.test.js @@ -0,0 +1,67 @@ +'use strict'; + +require('../helper'); + +var server = require('../../app/server')(); +var assert = require('../support/assert'); + +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", + 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", + statusCode: 401 + }, + { + desc: 'valid api key should allow insert in protected tables', + url: "/api/v1/sql?api_key=1234&q=INSERT%20INTO%20private_table%20(name)%20VALUES%20('app_auth_test1')", + statusCode: 200 + }, + { + desc: 'valid api key should allow delete in protected tables', + url: "/api/v1/sql?api_key=1234&q=DELETE%20FROM%20private_table%20WHERE%20name%3d'app_auth_test1'", + statusCode: 200 + }, + { + desc: 'invalid api key should NOT allow insert in protected tables', + url: "/api/v1/sql?api_key=THIS_API_KEY_NOT_EXIST&q=INSERT%20INTO%20private_table%20(name)%20VALUES%20('R')", + statusCode: 401 + }, + { + desc: 'no api key should NOT allow insert in protected tables', + url: "/api/v1/sql?q=INSERT%20INTO%20private_table%20(name)%20VALUES%20('RAMBO')", + statusCode: 403 + }, + { + desc: 'no api key should NOT allow insert in public tables', + url: "/api/v1/sql?q=INSERT%20INTO%20untitle_table_4%20(name)%20VALUES%20('RAMBO')", + statusCode: 403 + } + ]; + + 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' + }, + {}, + function(err, res) { + assert.equal(res.statusCode, scenario.statusCode, res.statusCode + ': ' + res.body); + done(); + } + ); + }); + }); + +}); diff --git a/test/acceptance/app.test.js b/test/acceptance/app.test.js new file mode 100644 index 0000000..5e67d37 --- /dev/null +++ b/test/acceptance/app.test.js @@ -0,0 +1,885 @@ +'use strict'; + +/** + * + * Requires the database and tables setup in config/environments/test.js to exist + * Ensure the user is present in the pgbouncer auth file too + * TODO: Add OAuth tests. + * + * To run this test, ensure that cartodb_test_user_1_db metadata exists + * in Redis for the vizzuality.cartodb.com domain + * + * SELECT 5 + * HSET rails:users:vizzuality id 1 + * HSET rails:users:vizzuality database_name cartodb_test_user_1_db + * + */ +require('../helper'); + +var server = require('../../app/server')(); +var assert = require('../support/assert'); +var querystring = require('querystring'); +var _ = require('underscore'); +var step = require('step'); + + +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'; + +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 /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('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 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 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, { + // 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(); + }); +}); + +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('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(); + }); +}); + +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')); + assert.equal(res.headers['cache-control'], expected_cache_control); + var pbody = JSON.parse(res.body); + assert.equal(pbody.total_rows, 1); + assert.equal(pbody.rows[0].count, 0); + 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('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('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('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 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(); + }); +}); + +// 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(); + }); +}); + +// 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, " + + "4::char as d, now() as e, 'a'::text as f" + + ", 1::bool as g" + + ", 'POINT(0 0)'::geometry as h" + + // See https://github.com/CartoDB/CartoDB-SQL-API/issues/117 + ", 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(); + }); +}); + +// 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); + } + ); +}); + +// 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'), " + + "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 { + 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(); + }); +}); + +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'}, + method: 'GET' + }, + { + // status: 429, ---> Both 200 and 429 are valid + headers: { + 'Content-Type': 'application/json; charset=utf-8' + } + }, + function(err, res) { + var error = JSON.parse(res.body); + assert.deepEqual(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.' + ]); + + 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'}, + method: 'GET' + }, + { + // status: 429, ---> Both 200 and 429 are valid + headers: { + 'Content-Type': 'application/json; charset=utf-8' + } + }, + function(err, res) { + var error = JSON.parse(res.body); + assert.deepEqual(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.' + ]); + + 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'}, + method: 'GET' + }, + { + // status: 429, ---> Both 200 and 429 are valid + headers: { + 'Content-Type': 'application/json; charset=utf-8' + } + }, + function(err, res) { + var error = JSON.parse(res.body); + assert.deepEqual(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.' + ]); + + 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) { + hit = true; + consoleError = what; + }; + assert.response( + server, + { + url: "/api/v1/sql?" + querystring.stringify({ + q: "SELECT * FROM untitle_table_4" + }), + headers: { + host: 'vizzuality.cartodb.com' + }, + method: 'GET' + }, + { + status: 400 + }, + 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.equal(parsedError.username, 'vizzuality'); + assert.equal(parsedError.type, 'row_size_limit_exceeded'); + + global.settings.db_max_row_size = dbMaxRowSize; + console.error = consoleErrorFn; + + done(); + } + ); + }); + + +}); diff --git a/test/acceptance/auth-api.js b/test/acceptance/auth-api.js new file mode 100644 index 0000000..8230498 --- /dev/null +++ b/test/acceptance/auth-api.js @@ -0,0 +1,275 @@ +'use strict'; + +const assert = require('../support/assert'); +const TestClient = require('../support/test-client'); +const BatchTestClient = require('../support/batch-test-client'); +const JobStatus = require('../../batch/job_status'); + +describe('Auth API', function () { + const publicSQL = 'select * from untitle_table_4'; + const scopedSQL = 'select * from scoped_table_1'; + const privateSQL = 'select * from private_table'; + const systemSQL = 'select * from information_schema.tables'; + + it('should get result from query using the default API key', function (done) { + this.testClient = new TestClient(); + this.testClient.getResult(publicSQL, (err, result) => { + assert.ifError(err); + assert.equal(result.length, 6); + done(); + }); + }); + + it('should fail when using a wrong API key', function (done) { + this.testClient = new TestClient({ apiKey: 'THIS_API_KEY_DOES_NOT_EXIST' }); + + const expectedResponse = { + response: { + status: 401 + } + }; + + this.testClient.getResult(publicSQL, expectedResponse, (err, result) => { + assert.ifError(err); + assert.equal(result.error, 'Unauthorized'); + done(); + }); + }); + + it('should fail while fetching data (private dataset) and using the default API key', function (done) { + this.testClient = new TestClient(); + const expectedResponse = { + response: { + status: 403 + }, + anonymous: true + }; + this.testClient.getResult(privateSQL, expectedResponse, (err, result) => { + assert.ifError(err); + assert.ok(result.error[0].match(/permission denied for .+? private_table/)); + done(); + }); + }); + + it('should get result from query using the master API key and public dataset', function (done) { + this.testClient = new TestClient({ apiKey: 1234 }); + this.testClient.getResult(publicSQL, (err, result) => { + assert.ifError(err); + assert.equal(result.length, 6); + done(); + }); + }); + + it('should get result from query using the master API key and private dataset', function (done) { + this.testClient = new TestClient({ apiKey: 1234 }); + this.testClient.getResult(privateSQL, (err, result) => { + assert.ifError(err); + assert.equal(result.length, 5); + done(); + }); + }); + + it('should get result from query using the regular API key and scoped dataset', function (done) { + this.testClient = new TestClient({ apiKey: 'regular1' }); + this.testClient.getResult(scopedSQL, (err, result) => { + assert.ifError(err); + assert.equal(result.length, 4); + done(); + }); + }); + + it('should fail while fetching data (scoped dataset) and using regular API key', function (done) { + this.testClient = new TestClient({ apiKey: 'regular2' }); + const expectedResponse = { + response: { + status: 403 + } + }; + + this.testClient.getResult(scopedSQL, expectedResponse, (err, result) => { + assert.ifError(err); + assert.ok(result.error[0].match(/permission denied for .+? scoped_table_1/)); + done(); + }); + }); + + describe('Batch API', function () { + it('should create a job with master api key and get it done', function (done) { + this.testClient = new BatchTestClient({ apiKey: '1234' }); + + this.testClient.createJob({ query: scopedSQL }, (err, jobResult) => { + if (err) { + return done(err); + } + + jobResult.getStatus(function (err, job) { + if (err) { + return done(err); + } + + assert.equal(job.status, JobStatus.DONE); + + done(); + }); + }); + }); + + it('should create a job with regular api key and get it failed', function (done) { + this.testClient = new BatchTestClient({ apiKey: 'regular1' }); + + this.testClient.createJob({ query: privateSQL }, { response: 403 }, (err, response) => { + if (err) { + return done(err); + } + + const body = JSON.parse(response.body); + assert.equal(body.error, 'permission denied'); + done(); + }); + }); + + it('should create a job with default public api key and get it failed', function (done) { + this.testClient = new BatchTestClient({ apiKey: 'default_public' }); + + this.testClient.createJob({ query: publicSQL }, { response: 403 }, (err, response) => { + if (err) { + return done(err); + } + + const body = JSON.parse(response.body); + assert.equal(body.error, 'permission denied'); + done(); + }); + }); + + it('should create a job with fallback default public api key and get it failed', function (done) { + this.testClient = new BatchTestClient(); + + this.testClient.createJob({ query: publicSQL }, { response: 403, anonymous: true }, (err, response) => { + if (err) { + return done(err); + } + + const body = JSON.parse(response.body); + assert.equal(body.error, 'permission denied'); + done(); + }); + }); + + afterEach(function (done) { + this.testClient.drain(done); + }); + }); + + describe('Basic Auth', function () { + it('should get result from query using the regular API key and scoped dataset', function (done) { + this.testClient = new TestClient({ authorization: 'vizzuality:regular1' }); + + this.testClient.getResult(scopedSQL, { anonymous: true }, (err, result) => { + assert.ifError(err); + assert.equal(result.length, 4); + done(); + }); + }); + + it('should fail while fetching data (scoped dataset) and using regular API key', function (done) { + this.testClient = new TestClient({ authorization: 'vizzuality:regular2' }); + const expectedResponse = { + response: { + status: 403 + }, + anonymous: true + }; + + this.testClient.getResult(scopedSQL, expectedResponse, (err, result) => { + assert.ifError(err); + assert.ok(result.error[0].match(/permission denied for .+? scoped_table_1/)); + done(); + }); + }); + + + it('should fail while fetching information schema and using default API key', function (done) { + this.testClient = new TestClient({ authorization: 'vizzuality:default_public' }); + const expectedResponse = { + response: { + status: 403 + }, + anonymous: true + }; + + this.testClient.getResult(systemSQL, expectedResponse, (err, result) => { + assert.ifError(err); + assert.equal(result.error, 'system tables are forbidden'); + done(); + }); + }); + + it('should fail when basic auth name does not match with user\'s', function (done) { + this.testClient = new TestClient({ authorization: 'wadus:regular2' }); + const expectedResponse = { + response: { + status: 403 + }, + anonymous: true + }; + + this.testClient.getResult(scopedSQL, expectedResponse, (err, result) => { + assert.ifError(err); + assert.equal(result.error, 'permission denied'); + done(); + }); + }); + + it('should fail when querying using a wrong API key', function (done) { + this.testClient = new TestClient({ authorization: 'vizzuality:THIS_API_KEY_DOES_NOT_EXIST' }); + + const expectedResponse = { + response: { + status: 401 + }, + anonymous: true + }; + + this.testClient.getResult(publicSQL, expectedResponse, (err, result) => { + assert.ifError(err); + assert.equal(result.error, 'Unauthorized'); + done(); + }); + }); + + describe('Batch API', function () { + it('should create a job with regular api key and get it failed', function (done) { + this.testClient = new BatchTestClient({ authorization: 'vizzuality:regular1', response: 403 }); + + this.testClient.createJob({ query: scopedSQL }, { anonymous: true }, (err, response) => { + if (err) { + return done(err); + } + + const body = JSON.parse(response.body); + assert.equal(body.error, 'permission denied'); + done(); + }); + }); + + it('should create a job with default api key and get it failed', function (done) { + this.testClient = new BatchTestClient({ authorization: 'vizzuality:default_public', response: 403 }); + + this.testClient.createJob({ query: privateSQL }, { anonymous: true }, (err, response) => { + if (err) { + return done(err); + } + + const body = JSON.parse(response.body); + assert.equal(body.error, 'permission denied'); + done(); + }); + }); + + afterEach(function (done) { + this.testClient.drain(done); + }); + }); + }); +}); diff --git a/test/acceptance/backend_crash.js b/test/acceptance/backend_crash.js new file mode 100644 index 0000000..7da4bd6 --- /dev/null +++ b/test/acceptance/backend_crash.js @@ -0,0 +1,83 @@ +'use strict'; + +require('../helper'); + +var assert = require('../support/assert'); +var step = require('step'); +var net = require('net'); + +var sql_server_port = 5540; +var sql_server = net.createServer(function(c) { + console.log('server connected'); + c.destroy(); + console.log('server socket destroyed.'); + sql_server.close(function() { + console.log('server closed'); + }); +}); + +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){ +//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('../../app/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 new file mode 100644 index 0000000..4d23189 --- /dev/null +++ b/test/acceptance/batch/batch-drain.test.js @@ -0,0 +1,83 @@ +'use strict'; + +require('../../helper'); +var assert = require('../../support/assert'); +var redisUtils = require('../../support/redis_utils'); +var batchFactory = require('../../../batch/index'); + +var JobPublisher = require('../../../batch/pubsub/job-publisher'); +var JobQueue = require('../../../batch/job_queue'); +var JobBackend = require('../../../batch/job_backend'); +var JobService = require('../../../batch/job_service'); +var JobCanceller = require('../../../batch/job_canceller'); +var metadataBackend = require('cartodb-redis')({ pool: redisUtils.getPool() }); + +describe('batch module', function() { + var dbInstance = 'localhost'; + var username = 'vizzuality'; + var pool = redisUtils.getPool(); + var jobPublisher = new JobPublisher(pool); + var jobQueue = new JobQueue(metadataBackend, jobPublisher); + var jobBackend = new JobBackend(metadataBackend, jobQueue); + var jobCanceller = new JobCanceller(); + var jobService = new JobService(jobBackend, jobCanceller); + + before(function (done) { + this.batch = batchFactory(metadataBackend, pool); + this.batch.start(); + this.batch.on('ready', done); + }); + + after(function (done) { + this.batch.stop(); + redisUtils.clean('batch:*', done); + }); + + function createJob(sql, done) { + var data = { + user: username, + query: sql, + host: dbInstance, + dbname: 'cartodb_test_user_1_db', + dbuser: 'test_cartodb_user_1', + port: 5432, + pass: 'test_cartodb_user_1_pass', + }; + + jobService.create(data, function (err, job) { + if (err) { + return done(err); + } + + done(null, job.serialize()); + }); + } + + it('should drain the current job', function (done) { + var self = this; + createJob('select pg_sleep(3)', function (err, job) { + if (err) { + return done(err); + } + setTimeout(function () { + jobBackend.get(job.job_id, function (err, job) { + if (err) { + done(err); + } + assert.equal(job.status, 'running'); + + self.batch.drain(function () { + jobBackend.get(job.job_id, function (err, job) { + if (err) { + done(err); + } + assert.equal(job.status, 'pending'); + done(); + }); + }); + }); + }, 50); + }); + }); + +}); diff --git a/test/acceptance/batch/batch-limits.test.js b/test/acceptance/batch/batch-limits.test.js new file mode 100644 index 0000000..fa83d8f --- /dev/null +++ b/test/acceptance/batch/batch-limits.test.js @@ -0,0 +1,52 @@ +'use strict'; + +require('../../helper'); + +var assert = require('../../support/assert'); +var BatchTestClient = require('../../support/batch-test-client'); +var JobStatus = require('../../../batch/job_status'); +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) { + 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) { + global.settings.batch_query_timeout = this.batchQueryTimeout; + redisUtils.clean('limits:batch:*', function() { + this.batchTestClient.drain(done); + }.bind(this)); + }); + after(db_utils.resetPgBouncerConnections); + + function jobPayload(query) { + return { + query: query + }; + } + + it('should cancel with user statement_timeout limit', function (done) { + var payload = jobPayload('select pg_sleep(10)'); + this.batchTestClient.createJob(payload, function(err, jobResult) { + if (err) { + return done(err); + } + jobResult.getStatus(function (err, job) { + if (err) { + return done(err); + } + assert.equal(job.status, JobStatus.FAILED); + assert.ok(job.failed_reason.match(/statement.*timeout/)); + return done(); + }); + }); + }); + +}); diff --git a/test/acceptance/batch/batch.multiquery.test.js b/test/acceptance/batch/batch.multiquery.test.js new file mode 100644 index 0000000..1214c4a --- /dev/null +++ b/test/acceptance/batch/batch.multiquery.test.js @@ -0,0 +1,236 @@ +'use strict'; + +require('../../helper'); + +var BatchTestClient = require('../../support/batch-test-client'); +var JobStatus = require('../../../batch/job_status'); + +var assert = require('../../support/assert'); +var queue = require('queue-async'); + +describe('batch multiquery', function() { + function jobPayload(query) { + return { + query: query + }; + } + + before(function() { + this.batchTestClient = new BatchTestClient(); + }); + + after(function (done) { + this.batchTestClient.drain(done); + }); + + it('should perform one multiquery job with two queries', function (done) { + var queries = [ + 'select pg_sleep(0)', + 'select pg_sleep(0)' + ]; + + var payload = jobPayload(queries); + this.batchTestClient.createJob(payload, function(err, jobResult) { + if (err) { + return done(err); + } + jobResult.getStatus(function (err, job) { + if (err) { + return done(err); + } + assert.equal(job.status, JobStatus.DONE); + return done(); + }); + }); + }); + + it('should perform one multiquery job with two queries and fail on last one', function (done) { + var queries = [ + 'select pg_sleep(0)', + 'select shouldFail()' + ]; + + var payload = jobPayload(queries); + this.batchTestClient.createJob(payload, function(err, jobResult) { + if (err) { + return done(err); + } + jobResult.getStatus(function (err, job) { + if (err) { + return done(err); + } + assert.equal(job.status, JobStatus.FAILED); + return done(); + }); + }); + }); + + it('should perform one multiquery job with three queries and fail on last one', function (done) { + var queries = [ + 'select pg_sleep(0)', + 'select pg_sleep(0)', + 'select shouldFail()' + ]; + + var payload = jobPayload(queries); + this.batchTestClient.createJob(payload, function(err, jobResult) { + if (err) { + return done(err); + } + jobResult.getStatus(function (err, job) { + if (err) { + return done(err); + } + assert.equal(job.status, JobStatus.FAILED); + return done(); + }); + }); + }); + + + it('should perform one multiquery job with three queries and fail on second one', function (done) { + var queries = [ + 'select pg_sleep(0)', + 'select shouldFail()', + 'select pg_sleep(0)' + ]; + + var payload = jobPayload(queries); + this.batchTestClient.createJob(payload, function(err, jobResult) { + if (err) { + return done(err); + } + jobResult.getStatus(function (err, job) { + if (err) { + return done(err); + } + assert.equal(job.status, JobStatus.FAILED); + return done(); + }); + }); + }); + + it('should perform two multiquery job with two queries for each one', function (done) { + var self = this; + + var jobs = [ + [ + 'select pg_sleep(0)', + 'select pg_sleep(0)' + ], + [ + 'select pg_sleep(0)', + 'select pg_sleep(0)' + ] + ]; + + var jobsQueue = queue(1); + + jobs.forEach(function(job) { + jobsQueue.defer(function(payload, done) { + self.batchTestClient.createJob(payload, function(err, jobResult) { + if (err) { + return done(err); + } + jobResult.getStatus(done); + }); + }, jobPayload(job)); + }); + + jobsQueue.awaitAll(function (err, jobsCreated) { + if (err) { + return done(err); + } + + jobsCreated.forEach(function(job) { + assert.equal(job.status, JobStatus.DONE); + }); + + return done(); + }); + }); + + it('should perform two multiquery job with two queries for each one and fail the first one', function (done) { + var self = this; + + var jobs = [ + [ + 'select pg_sleep(0)', + 'select shouldFail()' + ], + [ + 'select pg_sleep(0)', + 'select pg_sleep(0)' + ] + ]; + + 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) { + if (err) { + return done(err); + } + jobResult.getStatus(done); + }); + }, jobPayload(job)); + }); + + jobsQueue.awaitAll(function (err, jobsCreated) { + if (err) { + return done(err); + } + + var statuses = jobsCreated.map(function(job) { + return job.status; + }); + assert.deepEqual(statuses, expectedStatus); + + return done(); + }); + }); + + it('should perform two multiquery job with two queries for each one and fail the second one', function (done) { + var self = this; + + var jobs = [ + [ + 'select pg_sleep(0)', + 'select pg_sleep(0)' + ], + [ + 'select pg_sleep(0)', + 'select shouldFail()' + ] + ]; + + 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) { + if (err) { + return done(err); + } + jobResult.getStatus(done); + }); + }, jobPayload(job)); + }); + + jobsQueue.awaitAll(function (err, jobsCreated) { + if (err) { + return done(err); + } + + var statuses = jobsCreated.map(function(job) { + return job.status; + }); + assert.deepEqual(statuses, expectedStatus); + + return done(); + }); + }); +}); diff --git a/test/acceptance/batch/batch.test.js b/test/acceptance/batch/batch.test.js new file mode 100644 index 0000000..6b3d4bb --- /dev/null +++ b/test/acceptance/batch/batch.test.js @@ -0,0 +1,197 @@ +'use strict'; + +require('../../helper'); + +var assert = require('../../support/assert'); +var queue = require('queue-async'); +var BatchTestClient = require('../../support/batch-test-client'); +var JobStatus = require('../../../batch/job_status'); + +describe('batch happy cases', function() { + + before(function() { + this.batchTestClient = new BatchTestClient(); + }); + + after(function(done) { + this.batchTestClient.drain(done); + }); + + function jobPayload(query) { + return { + query: query + }; + } + + it('should perform job with select', function (done) { + var payload = jobPayload('select * from private_table'); + this.batchTestClient.createJob(payload, function(err, jobResult) { + if (err) { + return done(err); + } + jobResult.getStatus(function (err, job) { + if (err) { + return done(err); + } + assert.equal(job.status, JobStatus.DONE); + return done(); + }); + }); + }); + + it('should perform job with select into', function (done) { + var payload = jobPayload('select * into batch_test_table from (select * from private_table) as job'); + this.batchTestClient.createJob(payload, function(err, jobResult) { + if (err) { + return done(err); + } + jobResult.getStatus(function (err, job) { + if (err) { + return done(err); + } + assert.equal(job.status, JobStatus.DONE); + return done(); + }); + }); + }); + + 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) { + if (err) { + return done(err); + } + jobResult.getStatus(function (err, job) { + if (err) { + return done(err); + } + assert.equal(job.status, JobStatus.DONE); + return done(); + }); + }); + }); + + it('should perform all enqueued jobs', function (done) { + var self = this; + + var jobs = [ + '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) { + if (err) { + return done(err); + } + jobResult.getStatus(done); + }); + }, jobPayload(job)); + }); + + jobsQueue.awaitAll(function (err, jobsCreated) { + if (err) { + return done(err); + } + + jobsCreated.forEach(function(job) { + assert.equal(job.status, JobStatus.DONE); + }); + + return done(); + }); + }); + + it('should set all job as failed', function (done) { + var self = this; + + var jobs = [ + 'select * from unexistent_table', + 'select * from unexistent_table', + 'select * from unexistent_table' + ]; + + var jobsQueue = queue(1); + + jobs.forEach(function(job) { + jobsQueue.defer(function(payload, done) { + self.batchTestClient.createJob(payload, function(err, jobResult) { + if (err) { + return done(err); + } + jobResult.getStatus(done); + }); + }, jobPayload(job)); + }); + + jobsQueue.awaitAll(function (err, jobsCreated) { + if (err) { + return done(err); + } + + jobsCreated.forEach(function(job) { + assert.equal(job.status, JobStatus.FAILED); + }); + + return done(); + }); + }); + + it('should perform job with array of select', function (done) { + var queries = ['select * from private_table limit 1', 'select * from private_table']; + + var payload = jobPayload(queries); + this.batchTestClient.createJob(payload, function(err, jobResult) { + if (err) { + return done(err); + } + jobResult.getStatus(function (err, job) { + if (err) { + return done(err); + } + assert.equal(job.status, JobStatus.DONE); + return done(); + }); + }); + }); + + it('should set job as failed if last query fails', function (done) { + var queries = ['select * from private_table', 'select * from undefined_table']; + + var payload = jobPayload(queries); + this.batchTestClient.createJob(payload, function(err, jobResult) { + if (err) { + return done(err); + } + jobResult.getStatus(function (err, job) { + if (err) { + return done(err); + } + assert.equal(job.status, JobStatus.FAILED); + return done(); + }); + }); + }); + + it('should set job as failed if first query fails', function (done) { + var queries = ['select * from undefined_table', 'select * from private_table']; + + var payload = jobPayload(queries); + this.batchTestClient.createJob(payload, function(err, jobResult) { + if (err) { + return done(err); + } + jobResult.getStatus(function (err, job) { + if (err) { + return done(err); + } + assert.equal(job.status, JobStatus.FAILED); + return done(); + }); + }); + }); +}); diff --git a/test/acceptance/batch/batch.wip.test.js b/test/acceptance/batch/batch.wip.test.js new file mode 100644 index 0000000..e41e3ab --- /dev/null +++ b/test/acceptance/batch/batch.wip.test.js @@ -0,0 +1,99 @@ +'use strict'; + +require('../../helper'); + +var assert = require('../../support/assert'); +var BatchTestClient = require('../../support/batch-test-client'); +var JobStatus = require('../../../batch/job_status'); + +describe('batch work in progress endpoint happy cases', function() { + + before(function() { + this.batchTestClient = new BatchTestClient(); + }); + + after(function(done) { + this.batchTestClient.drain(done); + }); + + function jobPayload(query) { + return { + query: query + }; + } + + it('should get a list of work in progress jobs group by user', function (done) { + var self = this; + var user = 'vizzuality'; + var queries = ['select pg_sleep(3)']; + var payload = jobPayload(queries); + + self.batchTestClient.createJob(payload, function(err, jobResult) { + if (err) { + return done(err); + } + + jobResult.getStatus(JobStatus.RUNNING, function (err) { + if (err) { + return done(err); + } + + self.batchTestClient.getWorkInProgressJobs(function (err, workInProgressJobs) { + if (err) { + return done(err); + } + + if (!workInProgressJobs[user]) { + return done(new Error('User should be in work-in-progress list')); + } + + assert.ok(Array.isArray(workInProgressJobs[user])); + assert.ok(workInProgressJobs[user].length >= 1); + for (var i = 0; i < workInProgressJobs[user].length; i++) { + if (workInProgressJobs[user][i] === jobResult.job.job_id) { + return jobResult.cancel(done); + } + } + + return done(new Error('Job should not be in work-in-progress list')); + }); + }); + }); + }); + + it('should get a list of work in progress jobs w/o the finished ones', function (done) { + var self = this; + var user = 'vizzuality'; + var queries = ['select pg_sleep(0)']; + var payload = jobPayload(queries); + + self.batchTestClient.createJob(payload, function(err, jobResult) { + if (err) { + return done(err); + } + + jobResult.getStatus(function (err) { + if (err) { + return done(err); + } + + self.batchTestClient.getWorkInProgressJobs(function (err, workInProgressJobs) { + if (err) { + return done(err); + } + + if (workInProgressJobs[user]) { + assert.ok(Array.isArray(workInProgressJobs[user])); + assert.ok(workInProgressJobs[user].length >= 1); + for (var i = 0; i < workInProgressJobs[user].length; i++) { + if (workInProgressJobs[user][i] === jobResult.job.job_id) { + return done(new Error('Job should not be in work-in-progress list')); + } + } + } + done(); + }); + }); + }); + }); +}); diff --git a/test/acceptance/batch/job.callback-template.test.js b/test/acceptance/batch/job.callback-template.test.js new file mode 100644 index 0000000..9514e7b --- /dev/null +++ b/test/acceptance/batch/job.callback-template.test.js @@ -0,0 +1,126 @@ +'use strict'; + +require('../../helper'); + +var assert = require('../../support/assert'); +var TestClient = require('../../support/test-client'); +var JobStatus = require('../../../batch/job_status'); +var BatchTestClient = require('../../support/batch-test-client'); + +describe('Batch API callback templates', function () { + before(function () { + this.batchTestClient = new BatchTestClient(); + this.testClient = new TestClient(); + }); + + after(function (done) { + this.batchTestClient.drain(done); + }); + + it('should use templates for error_message and job_id onerror callback' + + ' and keep the original templated query but use the error message', function (done) { + var self = this; + var payload = { + "query": { + "query": [ + { + "query": "SELECT * FROM invalid_table", + "onerror": "INSERT INTO test_batch_errors " + + "values ('<%= job_id %>', '<%= error_message %>')" + } + ] + } + }; + var expectedQuery = { + query: [ + { + "query": "SELECT * FROM invalid_table", + "onerror": "INSERT INTO test_batch_errors values ('<%= job_id %>', '<%= error_message %>')", + status: 'failed', + fallback_status: 'done' + } + ] + }; + + self.testClient.getResult( + 'create table test_batch_errors (job_id text, error_message text)', 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) { + if (err) { + return done(err); + } + 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: "create table batch_jobs (job_id text)" + }, + { + "query": "SELECT 1", + "onsuccess": "INSERT INTO batch_jobs values ('<%= job_id %>')" + } + ] + } + }; + var expectedQuery = { + query: [ + { + query: "create table batch_jobs (job_id text)", + status: 'done' + }, + { + query: "SELECT 1", + onsuccess: "INSERT INTO batch_jobs values ('<%= job_id %>')", + status: 'done', + fallback_status: 'done' + } + ] + }; + + self.batchTestClient.createJob(payload, function(err, jobResult) { + if (err) { + return done(err); + } + jobResult.getStatus(function (err, job) { + if (err) { + return done(err); + } + + jobResult.validateExpectedResponse(expectedQuery); + self.testClient.getResult('select * from batch_jobs', function(err, rows) { + if (err) { + return done(err); + } + assert.equal(rows[0].job_id, job.job_id); + + self.testClient.getResult('drop table batch_jobs', done); + }); + }); + }); + }); +}); diff --git a/test/acceptance/batch/job.fallback.test.js b/test/acceptance/batch/job.fallback.test.js new file mode 100644 index 0000000..dc9e724 --- /dev/null +++ b/test/acceptance/batch/job.fallback.test.js @@ -0,0 +1,934 @@ +'use strict'; + +require('../../helper'); + +var assert = require('../../support/assert'); +var JobStatus = require('../../../batch/job_status'); +var BatchTestClient = require('../../support/batch-test-client'); + +describe('Batc API fallback job', function () { + + before(function() { + this.batchTestClient = new BatchTestClient(); + }); + + after(function(done) { + this.batchTestClient.drain(done); + }); + + it('"onsuccess" on first query should be triggered', function (done) { + var payload = { + query: { + query: [{ + 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" + }] + }; + + this.batchTestClient.createJob(payload, function(err, jobResult) { + if (err) { + return done(err); + } + jobResult.getStatus(function (err, job) { + if (err) { + return done(err); + } + assert.equal(job.status, JobStatus.DONE); + jobResult.validateExpectedResponse(expectedQuery); + return done(); + }); + }); + }); + + it('"onerror" on first query should not be triggered', function (done) { + var payload = { + query: { + query: [{ + 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" + }] + }; + + this.batchTestClient.createJob(payload, function(err, jobResult) { + if (err) { + return done(err); + } + jobResult.getStatus(function (err, job) { + if (err) { + return done(err); + } + assert.equal(job.status, JobStatus.DONE); + jobResult.validateExpectedResponse(expectedQuery); + return done(); + }); + }); + }); + + it('"onerror" on first query should be triggered', function (done) { + var payload = { + query: { + query: [{ + query: "SELECT * FROM nonexistent_table /* query should fail */", + onerror: "SELECT * FROM untitle_table_4 limit 1" + }] + } + }; + var expectedQuery = { + query: [{ + query: 'SELECT * FROM nonexistent_table /* query should fail */', + onerror: 'SELECT * FROM untitle_table_4 limit 1', + status: 'failed', + fallback_status: 'done', + failed_reason: 'relation "nonexistent_table" does not exist' + }] + }; + + this.batchTestClient.createJob(payload, function(err, jobResult) { + if (err) { + return done(err); + } + + jobResult.getStatus(function (err, job) { + if (err) { + return done(err); + } + assert.equal(job.status, JobStatus.FAILED); + jobResult.validateExpectedResponse(expectedQuery); + return done(); + }); + }); + }); + + it('"onsuccess" on first query should not be triggered', function (done) { + var payload = { + query: { + query: [{ + query: "SELECT * FROM nonexistent_table /* query should fail */", + onsuccess: "SELECT * FROM untitle_table_4 limit 1" + }] + } + }; + var expectedQuery = { + query: [{ + query: 'SELECT * FROM nonexistent_table /* query should fail */', + onsuccess: 'SELECT * FROM untitle_table_4 limit 1', + status: 'failed', + fallback_status: 'skipped', + failed_reason: 'relation "nonexistent_table" does not exist' + }] + }; + + this.batchTestClient.createJob(payload, function(err, jobResult) { + if (err) { + return done(err); + } + + jobResult.getStatus(function (err, job) { + if (err) { + return done(err); + } + assert.equal(job.status, JobStatus.FAILED); + jobResult.validateExpectedResponse(expectedQuery); + return done(); + }); + }); + }); + + it('"onsuccess" should be triggered', function (done) { + var payload = { + query: { + query: [{ + query: "SELECT * FROM untitle_table_4", + }], + onsuccess: "SELECT * FROM untitle_table_4 limit 1" + } + }; + var expectedQuery = { + "query": [{ + "query": "SELECT * FROM untitle_table_4", + "status": "done" + }], + "onsuccess": "SELECT * FROM untitle_table_4 limit 1" + }; + + this.batchTestClient.createJob(payload, function(err, jobResult) { + if (err) { + return done(err); + } + + jobResult.getStatus(function (err, job) { + if (err) { + return done(err); + } + assert.equal(job.status, JobStatus.DONE); + assert.equal(job.fallback_status, JobStatus.DONE); + jobResult.validateExpectedResponse(expectedQuery); + return done(); + }); + }); + }); + + it('"onsuccess" should not be triggered', function (done) { + var payload = { + query: { + query: [{ + query: "SELECT * FROM nonexistent_table /* query should fail */", + }], + 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' + }], + "onsuccess": "SELECT * FROM untitle_table_4 limit 1" + }; + + this.batchTestClient.createJob(payload, function(err, jobResult) { + if (err) { + return done(err); + } + + jobResult.getStatus(function (err, job) { + if (err) { + return done(err); + } + assert.equal(job.status, JobStatus.FAILED); + assert.equal(job.fallback_status, JobStatus.SKIPPED); + jobResult.validateExpectedResponse(expectedQuery); + return done(); + }); + }); + }); + + it('"onerror" should be triggered', function (done) { + var payload = { + query: { + query: [{ + query: "SELECT * FROM nonexistent_table /* query should fail */" + }], + 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' + }], + "onerror": "SELECT * FROM untitle_table_4 limit 1" + }; + + this.batchTestClient.createJob(payload, function(err, jobResult) { + if (err) { + return done(err); + } + + jobResult.getStatus(function (err, job) { + if (err) { + return done(err); + } + assert.equal(job.status, JobStatus.FAILED); + assert.equal(job.fallback_status, JobStatus.DONE); + jobResult.validateExpectedResponse(expectedQuery); + return done(); + }); + }); + }); + + it('"onerror" should not be triggered', function (done) { + var payload = { + query: { + query: [{ + query: "SELECT * FROM untitle_table_4", + }], + onerror: "SELECT * FROM untitle_table_4 limit 1" + } + }; + var expectedQuery = { + "query": [{ + "query": "SELECT * FROM untitle_table_4", + "status": "done" + }], + "onerror": "SELECT * FROM untitle_table_4 limit 1" + }; + + this.batchTestClient.createJob(payload, function(err, jobResult) { + if (err) { + return done(err); + } + + jobResult.getStatus(function (err, job) { + if (err) { + return done(err); + } + assert.equal(job.status, JobStatus.DONE); + assert.equal(job.fallback_status, JobStatus.SKIPPED); + jobResult.validateExpectedResponse(expectedQuery); + return done(); + }); + }); + }); + + it('"onsuccess" & "onsuccess" on query should be triggered', function (done) { + var payload = { + query: { + query: [{ + query: "SELECT * FROM untitle_table_4", + onsuccess: "SELECT * FROM untitle_table_4 limit 1" + }], + 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" + }], + "onsuccess": "SELECT * FROM untitle_table_4 limit 2" + }; + + this.batchTestClient.createJob(payload, function(err, jobResult) { + if (err) { + return done(err); + } + + jobResult.getStatus(function (err, job) { + if (err) { + return done(err); + } + assert.equal(job.status, JobStatus.DONE); + assert.equal(job.fallback_status, JobStatus.DONE); + jobResult.validateExpectedResponse(expectedQuery); + return done(); + }); + }); + }); + + it('"onsuccess" for each query should be triggered', function (done) { + var payload = { + query: { + query: [{ + 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" + }] + } + }; + var expectedQuery = { + "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" + }] + }; + + this.batchTestClient.createJob(payload, function(err, jobResult) { + if (err) { + return done(err); + } + + jobResult.getStatus(function (err, job) { + if (err) { + return done(err); + } + assert.equal(job.status, JobStatus.DONE); + jobResult.validateExpectedResponse(expectedQuery); + return done(); + }); + }); + }); + + it('"onsuccess" for each query should not be triggered', function (done) { + var payload = { + query: { + query: [{ + 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" + }] + } + }; + 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": "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) { + if (err) { + return done(err); + } + + jobResult.getStatus(function (err, job) { + if (err) { + return done(err); + } + assert.equal(job.status, JobStatus.FAILED); + jobResult.validateExpectedResponse(expectedQuery); + return done(); + }); + }); + }); + + it('"onsuccess" for second query should not be triggered', function (done) { + var payload = { + query: { + query: [{ + 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" + }] + } + }; + 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": "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) { + if (err) { + return done(err); + } + + jobResult.getStatus(function (err, job) { + if (err) { + return done(err); + } + assert.equal(job.status, JobStatus.FAILED); + jobResult.validateExpectedResponse(expectedQuery); + return done(); + }); + }); + }); + + it('"onerror" should not be triggered for any query and "skipped"', function (done) { + 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 3", + onerror: "SELECT * FROM untitle_table_4 limit 4" + }] + } + }; + var expectedQuery = { + query: [{ + query: 'SELECT * FROM untitle_table_4 limit 1', + onerror: 'SELECT * FROM untitle_table_4 limit 2', + status: 'done', + fallback_status: 'skipped' + }, { + query: 'SELECT * FROM untitle_table_4 limit 3', + onerror: 'SELECT * FROM untitle_table_4 limit 4', + status: 'done', + fallback_status: 'skipped' + }] + }; + + this.batchTestClient.createJob(payload, function(err, jobResult) { + if (err) { + return done(err); + } + + jobResult.getStatus(function (err, job) { + if (err) { + return done(err); + } + assert.equal(job.status, JobStatus.DONE); + jobResult.validateExpectedResponse(expectedQuery); + return done(); + }); + }); + }); + + it('"onsuccess" should be "skipped"', function (done) { + var payload = { + query: { + query: [{ + query: "SELECT * FROM untitle_table_4 limit 1, /* should fail */", + onsuccess: "SELECT * FROM untitle_table_4 limit 2" + }] + } + }; + var expectedQuery = { + query: [{ + query: 'SELECT * FROM untitle_table_4 limit 1, /* should fail */', + onsuccess: 'SELECT * FROM untitle_table_4 limit 2', + status: 'failed', + fallback_status: 'skipped', + failed_reason: 'syntax error at end of input' + }] + }; + + this.batchTestClient.createJob(payload, function(err, jobResult) { + if (err) { + return done(err); + } + + jobResult.getStatus(function (err, job) { + if (err) { + return done(err); + } + assert.equal(job.status, JobStatus.FAILED); + jobResult.validateExpectedResponse(expectedQuery); + return done(); + }); + }); + }); + + it('"onsuccess" should not be triggered and "skipped"', function (done) { + var payload = { + query: { + query: [{ + query: "SELECT * FROM untitle_table_4 limit 1, /* should fail */", + }], + onsuccess: "SELECT * FROM untitle_table_4 limit 2" + } + }; + var expectedQuery = { + query: [{ + query: 'SELECT * FROM untitle_table_4 limit 1, /* should fail */', + status: 'failed', + failed_reason: 'syntax error at end of input' + }], + onsuccess: 'SELECT * FROM untitle_table_4 limit 2' + }; + + this.batchTestClient.createJob(payload, function(err, jobResult) { + if (err) { + return done(err); + } + + jobResult.getStatus(function (err, job) { + if (err) { + return done(err); + } + assert.equal(job.status, JobStatus.FAILED); + jobResult.validateExpectedResponse(expectedQuery); + return done(); + }); + }); + }); + + it('"onsuccess" for first query should fail', function (done) { + 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 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": "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) { + if (err) { + return done(err); + } + + jobResult.getStatus(function (err, job) { + if (err) { + return done(err); + } + assert.equal(job.status, JobStatus.DONE); + jobResult.validateExpectedResponse(expectedQuery); + return done(); + }); + }); + }); + + it('"onsuccess" for second query should fail', function (done) { + 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 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": "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) { + if (err) { + return done(err); + } + + jobResult.getStatus(function (err, job) { + if (err) { + return done(err); + } + assert.equal(job.status, JobStatus.DONE); + jobResult.validateExpectedResponse(expectedQuery); + return done(); + }); + }); + }); + + it('"onsuccess" for job & "onsuccess" for each query should be triggered', function (done) { + 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 3", + onsuccess: "SELECT * FROM untitle_table_4 limit 4" + }], + 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": "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" + }; + + this.batchTestClient.createJob(payload, function(err, jobResult) { + if (err) { + return done(err); + } + + jobResult.getStatus(function (err, job) { + if (err) { + return done(err); + } + assert.equal(job.status, JobStatus.DONE); + assert.equal(job.fallback_status, JobStatus.DONE); + jobResult.validateExpectedResponse(expectedQuery); + return done(); + }); + }); + }); + + it('"onsuccess" for job & "onsuccess" for each query should be triggered ' + + '(even second "onsuccess" fails job should be done)', function (done) { + 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 3", + onsuccess: "SELECT * FROM nonexistent_table /* should fail */" + }], + 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": "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" + }; + + this.batchTestClient.createJob(payload, function(err, jobResult) { + if (err) { + return done(err); + } + + jobResult.getStatus(function (err, job) { + if (err) { + return done(err); + } + assert.equal(job.status, JobStatus.DONE); + assert.equal(job.fallback_status, JobStatus.DONE); + jobResult.validateExpectedResponse(expectedQuery); + return done(); + }); + }); + }); + + 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": "SELECT * FROM atm_madrid limit 3", + "onerror": "SELECT * FROM atm_madrid limit 4" + }], + "onerror": "SELECT * FROM atm_madrid limit 5" + } + }; + var expectedQuery = { + query: [{ + query: 'SELECT * FROM atm_madrid limit 1, should fail', + onerror: 'SELECT * FROM atm_madrid limit 2', + status: 'failed', + fallback_status: 'failed', + failed_reason: 'relation "atm_madrid" does not exist' + }, { + query: 'SELECT * FROM atm_madrid limit 3', + onerror: 'SELECT * FROM atm_madrid limit 4', + status: 'skipped', + fallback_status: 'skipped' + }], + onerror: 'SELECT * FROM atm_madrid limit 5' + }; + + this.batchTestClient.createJob(payload, function(err, jobResult) { + if (err) { + return done(err); + } + + jobResult.getStatus(function (err, job) { + if (err) { + return done(err); + } + assert.equal(job.status, JobStatus.FAILED); + assert.equal(job.fallback_status, JobStatus.FAILED); + jobResult.validateExpectedResponse(expectedQuery); + return done(); + }); + }); + }); + + 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": "SELECT * FROM untitle_table_4 limit 3", + "onerror": "SELECT * FROM untitle_table_4 limit 4" + }], + "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" + } + ], + "onerror": "SELECT * FROM untitle_table_4 limit 5" + }; + + this.batchTestClient.createJob(payload, function(err, jobResult) { + if (err) { + return done(err); + } + + jobResult.getStatus(function (err, job) { + if (err) { + return done(err); + } + assert.equal(job.status, JobStatus.FAILED); + assert.equal(job.fallback_status, JobStatus.DONE); + jobResult.validateExpectedResponse(expectedQuery); + return done(); + }); + }); + }); + + it('"onsuccess" for job & "onsuccess" for each query should not be triggered ' + + ' because it has been cancelled', function (done) { + var payload = { + query: { + query: [{ + query: "SELECT pg_sleep(3)", + 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" + }], + "onsuccess": "SELECT pg_sleep(0)" + }; + + this.batchTestClient.createJob(payload, function(err, jobResult) { + if (err) { + return done(err); + } + + jobResult.getStatus(JobStatus.RUNNING, function (err, job) { + if (err) { + return done(err); + } + + assert.equal(job.status, JobStatus.RUNNING); + + jobResult.cancel(function (err, job) { + if (err) { + return done(err); + } + + assert.equal(job.status, JobStatus.CANCELLED); + assert.equal(job.fallback_status, JobStatus.SKIPPED); + jobResult.validateExpectedResponse(expectedQuery); + return done(); + }); + }); + }); + }); + + it('first "onsuccess" should be triggered and it will be cancelled', function (done) { + var payload = { + query: { + query: [{ + query: "SELECT pg_sleep(0)", + onsuccess: "SELECT pg_sleep(3)" + }], + onsuccess: "SELECT pg_sleep(0)" + } + }; + var expectedQuery = { + "query": [{ + "query": "SELECT pg_sleep(0)", + "onsuccess": "SELECT pg_sleep(3)", + "status": "done", + "fallback_status": "cancelled" + }], + "onsuccess": "SELECT pg_sleep(0)" + }; + + this.batchTestClient.createJob(payload, function(err, jobResult) { + if (err) { + return done(err); + } + + jobResult.getStatus(JobStatus.RUNNING, function (err, job) { + if (err) { + return done(err); + } + + assert.equal(job.status, JobStatus.RUNNING); + + jobResult.cancel(function (err, job) { + if (err) { + return done(err); + } + + assert.equal(job.status, JobStatus.CANCELLED); + assert.equal(job.fallback_status, JobStatus.SKIPPED); + jobResult.validateExpectedResponse(expectedQuery); + return done(); + }); + }); + }); + }); +}); diff --git a/test/acceptance/batch/job.query.limit.test.js b/test/acceptance/batch/job.query.limit.test.js new file mode 100644 index 0000000..b02959f --- /dev/null +++ b/test/acceptance/batch/job.query.limit.test.js @@ -0,0 +1,125 @@ +'use strict'; + +/** + * + * Requires the database and tables setup in config/environments/test.js to exist + * Ensure the user is present in the pgbouncer auth file too + * TODO: Add OAuth tests. + * + * To run this test, ensure that cartodb_test_user_1_db metadata exists + * in Redis for the vizzuality.cartodb.com domain + * + * SELECT 5 + * HSET rails:users:vizzuality id 1 + * HSET rails:users:vizzuality database_name cartodb_test_user_1_db + * + */ +require('../../helper'); +var JobController = require('../../../app/controllers/job_controller'); +var redisUtils = require('../../support/redis_utils'); +var server = require('../../../app/server')(); +var assert = require('../../support/assert'); +var querystring = require('qs'); + +function payload(query) { + return JSON.stringify({query: query}); +} +function payloadSize(query) { + return payload(query).length; +} + +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) { + return JobController.getMaxSizeErrorMessage(payload(query)); + } + + after(function (done) { + redisUtils.clean('batch:*', 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' }, + method: 'POST', + data: querystring.stringify({ + query: queryTooLong + }) + }, { + status: 400 + }, function (err, res) { + var error = JSON.parse(res.body); + assert.deepEqual(error, { error: [expectedErrorMessage(queryTooLong)] }); + 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' }, + method: 'POST', + data: querystring.stringify({ + query: queryMaxSize + }) + }, { + status: 201 + }, function (err, res) { + var job = JSON.parse(res.body); + assert.ok(job.job_id); + 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' }, + method: 'POST', + data: querystring.stringify({ + query: queries + }) + }, { + status: 400 + }, function (err, res) { + var error = JSON.parse(res.body); + assert.deepEqual(error, { error: [expectedErrorMessage(queries)] }); + 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" + }, { + 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' }, + method: 'POST', + data: querystring.stringify({ + query: fallbackQueries + }) + }, { + status: 400 + }, function (err, res) { + var error = JSON.parse(res.body); + assert.deepEqual(error, { error: [expectedErrorMessage(fallbackQueries)] }); + done(); + }); + }); + +}); diff --git a/test/acceptance/batch/job.query.order.test.js b/test/acceptance/batch/job.query.order.test.js new file mode 100644 index 0000000..709692a --- /dev/null +++ b/test/acceptance/batch/job.query.order.test.js @@ -0,0 +1,59 @@ +'use strict'; + +require('../../helper'); +var assert = require('../../support/assert'); + +var BatchTestClient = require('../../support/batch-test-client'); +var JobStatus = require('../../../batch/job_status'); + +describe('job query order', function() { + + before(function() { + this.batchTestClient = new BatchTestClient(); + }); + + after(function (done) { + return this.batchTestClient.drain(done); + }); + + 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"]); + + this.batchTestClient.createJob(jobRequest1, function(err, jobResult1) { + if (err) { + return done(err); + } + this.batchTestClient.createJob(jobRequest2, function(err, jobResult2) { + if (err) { + return done(err); + } + + jobResult1.getStatus(function (err, job1) { + if (err) { + return done(err); + } + jobResult2.getStatus(function(err, job2) { + if (err) { + return done(err); + } + assert.equal(job1.status, JobStatus.DONE); + assert.equal(job2.status, JobStatus.DONE); + assert.ok( + new Date(job1.updated_at).getTime() < new Date(job2.updated_at).getTime(), + 'job1 (' + job1.updated_at + ') should finish before job2 (' + job2.updated_at + ')' + ); + done(); + }); + }); + }); + }.bind(this)); + }); + +}); diff --git a/test/acceptance/batch/job.query.timeout.test.js b/test/acceptance/batch/job.query.timeout.test.js new file mode 100644 index 0000000..e55fe1c --- /dev/null +++ b/test/acceptance/batch/job.query.timeout.test.js @@ -0,0 +1,99 @@ +'use strict'; + +require('../../helper'); +var assert = require('../../support/assert'); + +var BatchTestClient = require('../../support/batch-test-client'); +var JobStatus = require('../../../batch/job_status'); + +describe('job query timeout', function() { + + before(function() { + this.batchQueryTimeout = global.settings.batch_query_timeout; + this.batchTestClient = new BatchTestClient(); + }); + + after(function (done) { + global.settings.batch_query_timeout = this.batchQueryTimeout; + return this.batchTestClient.drain(done); + }); + + function createTimeoutQuery(query, timeout) { + return { + query: { + query: [ + { + timeout: timeout, + query: query + } + ] + } + }; + } + + 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) { + if (err) { + return done(err); + } + jobResult.getStatus(function(err, job) { + if (err) { + return done(err); + } + assert.equal(job.status, JobStatus.DONE); + done(); + }); + }); + }); + + 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) { + if (err) { + return done(err); + } + jobResult.getStatus(function(err, job) { + if (err) { + return done(err); + } + assert.equal(job.status, JobStatus.FAILED); + done(); + }); + }); + }); + + 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) { + if (err) { + return done(err); + } + jobResult.getStatus(function(err, job) { + if (err) { + return done(err); + } + assert.equal(job.status, JobStatus.FAILED); + done(); + }); + }); + }); + + 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) { + if (err) { + return done(err); + } + jobResult.getStatus(function(err, job) { + if (err) { + return done(err); + } + assert.equal(job.status, JobStatus.FAILED); + done(); + }); + }); + }); +}); diff --git a/test/acceptance/batch/job.test.js b/test/acceptance/batch/job.test.js new file mode 100644 index 0000000..d8d8288 --- /dev/null +++ b/test/acceptance/batch/job.test.js @@ -0,0 +1,219 @@ +'use strict'; + +/** + * + * Requires the database and tables setup in config/environments/test.js to exist + * Ensure the user is present in the pgbouncer auth file too + * TODO: Add OAuth tests. + * + * To run this test, ensure that cartodb_test_user_1_db metadata exists + * in Redis for the vizzuality.cartodb.com domain + * + * SELECT 5 + * HSET rails:users:vizzuality id 1 + * HSET rails:users:vizzuality database_name cartodb_test_user_1_db + * + */ +require('../../helper'); + +var server = require('../../../app/server')(); +var assert = require('../../support/assert'); +var redisUtils = require('../../support/redis_utils'); +var querystring = require('querystring'); + +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){ + 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({ + query: "SELECT * FROM untitle_table_4" + }) + }, { + status: 201 + }, 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"); + 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(); + }); + }); + + 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' }, + method: 'POST', + data: querystring.stringify({ + q: "SELECT * FROM untitle_table_4" + }) + }, { + 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 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' }, + method: 'POST', + data: querystring.stringify({ + query: "SELECT * FROM untitle_table_4" + }) + }, { + status: 401 + }, function(err, res) { + var error = JSON.parse(res.body); + assert.deepEqual(error, { error: [ 'Unauthorized' ] }); + 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' }, + method: 'POST', + data: querystring.stringify({ + query: "SELECT * FROM untitle_table_4" + }) + }, { + status: 404 + }, function(err, res) { + var error = JSON.parse(res.body); + assert.deepEqual(error, { + error: [ + 'Sorry, we can\'t find CARTO user \'wrong-host\'. ' + + 'Please check that you have entered the correct domain.' + ] + }); + 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' }, + method: 'GET' + }, { + status: 200 + }, 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"); + 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' }, + method: 'GET' + }, { + status: 401 + }, 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){ + assert.response(server, { + url: '/api/v2/sql/job/irrelevantJob?api_key=1234', + headers: { 'host': 'vizzuality.cartodb.com', 'Content-Type': 'application/x-www-form-urlencoded' }, + method: 'GET' + }, { + status: 400 + }, function(err, res) { + var error = JSON.parse(res.body); + 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){ + 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' }, + method: 'DELETE' + }, { + status: 200 + }, 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"); + 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' }, + method: 'DELETE' + }, { + status: 401 + }, 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){ + 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' }, + method: 'DELETE' + }, { + status: 404 + }, function(err, res) { + var error = JSON.parse(res.body); + assert.deepEqual(error , { + error: [ + 'Sorry, we can\'t find CARTO user \'wrong-host\'. ' + + 'Please check that you have entered the correct domain.' + ] + }); + done(); + }); + }); +}); diff --git a/test/acceptance/batch/job.timing.test.js b/test/acceptance/batch/job.timing.test.js new file mode 100644 index 0000000..d2482ca --- /dev/null +++ b/test/acceptance/batch/job.timing.test.js @@ -0,0 +1,115 @@ +'use strict'; + +require('../../helper'); + +var BatchTestClient = require('../../support/batch-test-client'); +var JobStatus = require('../../../batch/job_status'); + +describe('Batch API query timing', function () { + before(function() { + this.batchTestClient = new BatchTestClient(); + }); + + after(function(done) { + this.batchTestClient.drain(done); + }); + + it('should report start and end time for each query with fallback queries' + + 'and expose started_at and ended_at for all queries with fallback mechanism', function (done) { + var expectedQuery = { + query: [{ + query: 'SELECT * FROM untitle_table_4 limit 1', + onerror: 'SELECT * FROM untitle_table_4 limit 2', + status: 'done', + fallback_status: 'skipped' + }, { + query: 'SELECT * FROM untitle_table_4 limit 3', + onerror: 'SELECT * FROM untitle_table_4 limit 4', + status: 'done', + fallback_status: 'skipped' + }], + onerror: 'SELECT * FROM untitle_table_4 limit 5' + }; + + 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 3", + "onerror": "SELECT * FROM untitle_table_4 limit 4" + } + ], + "onerror": "SELECT * FROM untitle_table_4 limit 5" + } + }; + + this.batchTestClient.createJob(payload, function(err, jobResult) { + if (err) { + return done(err); + } + + jobResult.getStatus(function (err) { + if (err) { + return done(err); + } + + jobResult.validateExpectedResponse(expectedQuery); + done(); + }); + }); + }); + + + 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 = { + query: [{ + query: 'SELECT * FROM untitle_table_4 limit 1', + onerror: 'SELECT * FROM untitle_table_4 limit 2', + status: 'done', + fallback_status: 'skipped' + }, { + query: 'SELECT * FROM untitle_table_4 limit 3 failed', + onerror: 'SELECT * FROM untitle_table_4 limit 4', + status: 'failed', + fallback_status: 'done' + }], + onerror: 'SELECT * FROM untitle_table_4 limit 5' + }; + + 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 3 failed", + "onerror": "SELECT * FROM untitle_table_4 limit 4" + } + ], + "onerror": "SELECT * FROM untitle_table_4 limit 5" + } + }; + + this.batchTestClient.createJob(payload, function(err, jobResult) { + if (err) { + return done(err); + } + + jobResult.getStatus(JobStatus.FAILED, function (err) { + if (err) { + return done(err); + } + + jobResult.validateExpectedResponse(expectedQuery); + done(); + }); + }); + }); +}); diff --git a/test/acceptance/batch/leader-multiple-users-query-order.test.js b/test/acceptance/batch/leader-multiple-users-query-order.test.js new file mode 100644 index 0000000..0a5c360 --- /dev/null +++ b/test/acceptance/batch/leader-multiple-users-query-order.test.js @@ -0,0 +1,129 @@ +'use strict'; + +require('../../helper'); +var assert = require('../../support/assert'); + +var TestClient = require('../../support/test-client'); +var BatchTestClient = require('../../support/batch-test-client'); +var JobStatus = require('../../../batch/job_status'); + +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' }); + + this.testClient = new TestClient(); + this.testClient.getResult( + [ + 'drop table if exists ordered_inserts_a', + 'drop table if exists ordered_inserts_bbbbb', + 'create table ordered_inserts_a (status numeric)', + 'create table ordered_inserts_bbbbb (status numeric)' + ].join(';'), + done + ); + }); + + after(function (done) { + this.batchTestClientA.drain(function(err) { + if (err) { + return done(err); + } + + this.batchTestClientB.drain(done); + }.bind(this)); + }); + + function createJob(queries) { + return { + query: queries + }; + } + + 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)" + ]); + var jobRequestA2 = createJob([ + "insert into ordered_inserts_a values(3)" + ]); + + var jobRequestB1 = createJob([ + "insert into ordered_inserts_bbbbb values(1)" + ]); + + var self = this; + + 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) { + if (err) { + return done(err); + } + + // we don't care about the producer + self.batchTestClientB.createJob(jobRequestA2, function(err, jobResultA2) { + if (err) { + return done(err); + } + + jobResultA1.getStatus(function (err, jobA1) { + if (err) { + return done(err); + } + jobResultA2.getStatus(function(err, jobA2) { + if (err) { + return done(err); + } + jobResultB1.getStatus(function(err, jobB1) { + assert.equal(jobA1.status, JobStatus.DONE); + assert.equal(jobA2.status, JobStatus.DONE); + assert.equal(jobB1.status, JobStatus.DONE); + + assert.ok( + new Date(jobA1.updated_at).getTime() < new Date(jobA2.updated_at).getTime(), + 'A1 (' + jobA1.updated_at + ') ' + + 'should finish before A2 (' + jobA2.updated_at + ')' + ); + assert.ok( + new Date(jobB1.updated_at).getTime() < new Date(jobA1.updated_at).getTime(), + 'B1 (' + jobA1.updated_at + ') ' + + 'should finish before A1 (' + jobA1.updated_at + ')' + ); + + function statusMapper (status) { return { status: status }; } + + self.testClient.getResult('select * from ordered_inserts_a', function(err, rows) { + assert.ok(!err); + + // cartodb250user and vizzuality test users share database + var expectedRows = [1, 2, 3].map(statusMapper); + assert.deepEqual(rows, expectedRows); + + var query = 'select * from ordered_inserts_bbbbb'; + self.testClient.getResult(query, override, function(err, rows) { + assert.ok(!err); + + var expectedRows = [1].map(statusMapper); + assert.deepEqual(rows, expectedRows); + + done(); + }); + }); + }); + + }); + }); + }); + }); + }); + }); + +}); diff --git a/test/acceptance/batch/leader.job.query.order.test.js b/test/acceptance/batch/leader.job.query.order.test.js new file mode 100644 index 0000000..e80b204 --- /dev/null +++ b/test/acceptance/batch/leader.job.query.order.test.js @@ -0,0 +1,88 @@ +'use strict'; + +require('../../helper'); +var assert = require('../../support/assert'); + +var TestClient = require('../../support/test-client'); +var BatchTestClient = require('../../support/batch-test-client'); +var JobStatus = require('../../../batch/job_status'); + +describe('multiple batch clients job query order', function() { + + before(function(done) { + this.batchTestClient1 = new BatchTestClient({ name: 'consumerA' }); + this.batchTestClient2 = new BatchTestClient({ name: 'consumerB' }); + + this.testClient = new TestClient(); + this.testClient.getResult( + 'drop table if exists ordered_inserts; create table ordered_inserts (status numeric)', + done + ); + }); + + after(function (done) { + this.batchTestClient1.drain(function(err) { + if (err) { + return done(err); + } + + this.batchTestClient2.drain(done); + }.bind(this)); + }); + + function createJob(queries) { + return { + query: queries + }; + } + + 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)" + ]); + var jobRequest2 = createJob([ + "insert into ordered_inserts values(3)" + ]); + + var self = this; + + this.batchTestClient1.createJob(jobRequest1, function(err, jobResult1) { + if (err) { + return done(err); + } + this.batchTestClient2.createJob(jobRequest2, function(err, jobResult2) { + if (err) { + return done(err); + } + + jobResult1.getStatus(function (err, job1) { + if (err) { + return done(err); + } + 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) { + assert.ok(!err); + + assert.deepEqual(rows, [{ status: 1 }, { status: 2 }, { status: 3 }]); + assert.ok( + new Date(job1.updated_at).getTime() < new Date(job2.updated_at).getTime(), + 'job1 (' + job1.updated_at + ') should finish before job2 (' + job2.updated_at + ')' + ); + done(); + }); + + }); + }); + }); + }.bind(this)); + }); + +}); diff --git a/test/acceptance/batch/queued-jobs-limit.test.js b/test/acceptance/batch/queued-jobs-limit.test.js new file mode 100644 index 0000000..35a0aa5 --- /dev/null +++ b/test/acceptance/batch/queued-jobs-limit.test.js @@ -0,0 +1,68 @@ +'use strict'; + +require('../../helper'); + +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) { + this.batch_max_queued_jobs = global.settings.batch_max_queued_jobs; + global.settings.batch_max_queued_jobs = 1; + this.server = require('../../../app/server')(); + this.testClient = new TestClient(); + this.testClient.getResult( + 'drop table if exists max_queued_jobs_inserts; create table max_queued_jobs_inserts (status numeric)', + done + ); + }); + + after(function (done) { + global.settings.batch_max_queued_jobs = this.batch_max_queued_jobs; + redisUtils.clean('batch:*', done); + }); + + function createJob(server, status, callback) { + assert.response( + server, + { + url: '/api/v2/sql/job?api_key=1234', + headers: { + host: 'vizzuality.cartodb.com', + 'Content-Type': 'application/json' + }, + method: 'POST', + data: JSON.stringify({ + query: "insert into max_queued_jobs_inserts values (1)" + }) + }, + { + status: status + }, + function(err, res) { + if (err) { + return callback(err); + } + + return callback(null, JSON.parse(res.body)); + } + ); + } + + 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) { + assert.ok(!err); + + 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"); + done(); + }); + }); + }); + +}); diff --git a/test/acceptance/batch/scheduler-basic.test.js b/test/acceptance/batch/scheduler-basic.test.js new file mode 100644 index 0000000..7cee1e0 --- /dev/null +++ b/test/acceptance/batch/scheduler-basic.test.js @@ -0,0 +1,99 @@ +'use strict'; + +require('../../helper'); +var assert = require('../../support/assert'); + +var TestClient = require('../../support/test-client'); +var BatchTestClient = require('../../support/batch-test-client'); +var JobStatus = require('../../../batch/job_status'); + +describe('basic scheduling', function() { + + before(function(done) { + this.batchTestClientA = new BatchTestClient({ name: 'consumerA' }); + this.batchTestClientB = new BatchTestClient({ name: 'consumerB' }); + + this.testClient = new TestClient(); + this.testClient.getResult( + [ + 'drop table if exists ordered_inserts_a', + 'create table ordered_inserts_a (status numeric)' + ].join(';'), + done + ); + }); + + after(function (done) { + this.batchTestClientA.drain(function(err) { + if (err) { + return done(err); + } + + this.batchTestClientB.drain(done); + }.bind(this)); + }); + + function createJob(queries) { + return { + query: queries + }; + } + + 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)" + ]); + var jobRequestA2 = createJob([ + "insert into ordered_inserts_a values(3)" + ]); + + var self = this; + + 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) { + if (err) { + return done(err); + } + + jobResultA1.getStatus(function (err, jobA1) { + if (err) { + return done(err); + } + + jobResultA2.getStatus(function(err, jobA2) { + if (err) { + return done(err); + } + assert.equal(jobA1.status, JobStatus.DONE); + assert.equal(jobA2.status, JobStatus.DONE); + + assert.ok( + 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) { + assert.ok(!err); + + // cartodb250user and vizzuality test users share database + var expectedRows = [1, 2, 3].map(statusMapper); + assert.deepEqual(rows, expectedRows); + + return done(); + }); + }); + }); + }); + }); + }); +}); diff --git a/test/acceptance/batch/use-cases.test.js b/test/acceptance/batch/use-cases.test.js new file mode 100644 index 0000000..255d905 --- /dev/null +++ b/test/acceptance/batch/use-cases.test.js @@ -0,0 +1,190 @@ +'use strict'; + +require('../../helper'); + +var assert = require('../../support/assert'); +var JobStatus = require('../../../batch/job_status'); +var BatchTestClient = require('../../support/batch-test-client'); + +describe('Use cases', function () { + before(function() { + this.batchTestClient = new BatchTestClient(); + }); + + 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" + }; + + this.batchTestClient.createJob(payload, function(err, jobResult) { + if (err) { + return done(err); + } + + jobResult.getStatus(JobStatus.DONE, function (err, job) { + if (err) { + return done(err); + } + + assert.equal(job.status, JobStatus.DONE); + + jobResult.tryCancel(function (err, body) { + assert.equal(body.error[0], "Cannot set status from done to cancelled"); + done(); + }); + }); + }); + }); + + it('cancel a running job', function (done) { + var payload = { + query: "SELECT * FROM untitle_table_4; select pg_sleep(3)" + }; + + this.batchTestClient.createJob(payload, function(err, jobResult) { + if (err) { + return done(err); + } + + jobResult.getStatus(JobStatus.RUNNING, function (err, job) { + if (err) { + return done(err); + } + + assert.equal(job.status, JobStatus.RUNNING); + + jobResult.cancel(function (err, job) { + if (err) { + return done(err); + } + + assert.equal(job.status, JobStatus.CANCELLED); + + jobResult.tryCancel(function (err, body) { + assert.equal(body.error[0], "Cannot set status from cancelled to cancelled"); + done(); + }); + }); + }); + }); + }); + + it('cancel a pending job', function (done) { + var self = this; + var payload1 = { + query: "SELECT * FROM untitle_table_4; select pg_sleep(3)" + }; + + this.batchTestClient.createJob(payload1, function(err, jobResult1) { + if (err) { + return done(err); + } + + var payload2 = { + query: "SELECT * FROM untitle_table_4" + }; + + self.batchTestClient.createJob(payload2, function(err, jobResult2) { + if (err) { + return done(err); + } + + jobResult2.getStatus(JobStatus.PENDING, function (err, job) { + if (err) { + return done(err); + } + + assert.equal(job.status, JobStatus.PENDING); + + jobResult2.cancel(function (err, job) { + if (err) { + return done(err); + } + + assert.equal(job.status, JobStatus.CANCELLED); + + jobResult1.cancel(function (err, job) { + if (err) { + return done(err); + } + + assert.equal(job.status, JobStatus.CANCELLED); + done(); + }); + + }); + }); + }); + }); + }); + + it('cancel a job with quotes', function (done) { + var payload = { + query: "SELECT name FROM untitle_table_4 WHERE name = 'Hawai'; select pg_sleep(3)" + }; + + this.batchTestClient.createJob(payload, function(err, jobResult) { + if (err) { + return done(err); + } + + jobResult.getStatus(JobStatus.RUNNING, function (err, job) { + if (err) { + return done(err); + } + + assert.equal(job.status, JobStatus.RUNNING); + + jobResult.cancel(function (err, job) { + if (err) { + return done(err); + } + + assert.equal(job.status, JobStatus.CANCELLED); + done(); + }); + }); + }); + }); + + it('cancel a running multiquery job', function (done) { + var payload = { + query: [ + "select pg_sleep(1)", + "select pg_sleep(1)", + "select pg_sleep(1)" + ] + }; + + this.batchTestClient.createJob(payload, function(err, jobResult) { + if (err) { + return done(err); + } + + jobResult.getStatus(JobStatus.RUNNING, function (err, job) { + if (err) { + return done(err); + } + + assert.equal(job.status, JobStatus.RUNNING); + + jobResult.cancel(function (err, job) { + if (err) { + return done(err); + } + + assert.equal(job.status, JobStatus.CANCELLED); + + jobResult.tryCancel(function (err, body) { + assert.equal(body.error[0], "Cannot set status from cancelled to cancelled"); + done(); + }); + }); + }); + }); + }); +}); diff --git a/test/acceptance/cache.js b/test/acceptance/cache.js new file mode 100644 index 0000000..f8914ca --- /dev/null +++ b/test/acceptance/cache.js @@ -0,0 +1,21 @@ +'use strict'; + +var server = require('../../app/server')(); +const assert = require('../support/assert'); + +describe('Cache', function () { + it('should return a Vary header', function (done) { + assert.response(server, { + url: '/api/v1/sql?api_key=1234&g=select%20*%20from%20untitle_table_4', + headers: { + host: 'vizzuality.cartodb.com' + }, + method: 'GET' + }, + {}, + function(err, res) { + assert.equal(res.headers.vary, 'Authorization'); + done(); + }); + }); +}); diff --git a/test/acceptance/copy-abort.js b/test/acceptance/copy-abort.js new file mode 100644 index 0000000..c47368b --- /dev/null +++ b/test/acceptance/copy-abort.js @@ -0,0 +1,242 @@ +'use strict'; + +const querystring = require('querystring'); +const StatsClient = require('../../app/stats/client'); +const statsClient = StatsClient.getInstance(global.settings.statsd); +const server = require('../../app/server')(statsClient); +const request = require('request'); +const assert = require('assert'); + +const copyQuery = `COPY ( + INSERT INTO copy_to_test + SELECT updated_at + FROM generate_series( + '1984-06-14 01:00:00'::timestamp, + '2018-06-14 01:00:00'::timestamp, + '1 hour'::interval + ) updated_at + RETURNING updated_at +) TO STDOUT`; + +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 countQuery = `SELECT count(1) as count FROM copy_to_test`; + +function countInsertedRows (host, port, callback) { + setTimeout(function () { + const count = querystring.stringify({ q: countQuery, api_key: 1234 }); + + const options = { + url: `http://${host}:${port}/api/v1/sql?${count}`, + headers: { host: 'vizzuality.cartodb.com' }, + method: 'GET' + }; + + request(options, function (err, res, body) { + if (err) { + return callback(err); + } + + assert.equal(res.statusCode, 200); + const result = JSON.parse(body); + callback(null, result); + }); + }, 100); +} + +describe('Cancel "copy to" commands', function () { + before(function() { + this.db_pool_size = global.settings.db_pool_size; + global.settings.db_pool_size = 1; + }); + + after(function() { + global.settings.db_pool_size = this.db_pool_size; + }); + + + beforeEach(function (done) { + this.listener = server.listen(0, '127.0.0.1'); + + this.listener.on('error', done); + + this.listener.on('listening', () => { + const { address, port } = this.listener.address(); + + this.host = address; + this.port = port; + + done(); + }); + }); + + beforeEach(function (done) { + const { host, port } = this; + + const createTable = querystring.stringify({ q: createTableQuery, api_key: 1234}); + + const createTableOptions = { + url: `http://${host}:${port}/api/v1/sql?${createTable}`, + headers: { host: 'vizzuality.cartodb.com' }, + method: 'GET' + }; + + request(createTableOptions, function (err, res) { + if (err) { + return done(err); + } + + assert.equal(res.statusCode, 200); + + done(); + }); + }); + + afterEach(function (done) { + const { host, port } = this; + + const dropTable = querystring.stringify({ q: dropTableQuery, api_key: 1234 }); + + const dropTableOptions = { + url: `http://${host}:${port}/api/v1/sql?${dropTable}`, + headers: { host: 'vizzuality.cartodb.com' }, + method: 'GET' + }; + + request(dropTableOptions, function (err, res) { + if (err) { + return done(err); + } + + assert.equal(res.statusCode, 200); + + done(); + }); + }); + + afterEach(function (done) { + this.listener.close(done); + }); + + it('abort on response', function (done) { + const { host, port } = this; + + const copy = querystring.stringify({ q: copyQuery, api_key: 1234 }); + + const options = { + url: `http://${host}:${port}/api/v1/sql/copyto?${copy}`, + headers: { host: 'vizzuality.cartodb.com' }, + method: 'GET' + }; + + const req = request(options); + + req.on('response', function () { + req.abort(); + + countInsertedRows(host, port, function (err, result) { + if (err) { + return done(err); + } + + assert.equal(result.rows[0].count, 1); + + done(); + }); + }); + }); + + it('abort on data', function (done) { + const { host, port } = this; + + const copy = querystring.stringify({ q: copyQuery, api_key: 1234 }); + + const options = { + url: `http://${host}:${port}/api/v1/sql/copyto?${copy}`, + headers: { host: 'vizzuality.cartodb.com' }, + method: 'GET' + }; + + const req = request(options); + + req.once('data', function () { + req.abort(); + + countInsertedRows(host, port, function (err, result) { + if (err) { + return done(err); + } + + assert.equal(result.rows[0].count, 1); + + done(); + }); + }); + }); + + + it('destroy on data', function (done) { + const { host, port } = this; + + const copy = querystring.stringify({ q: copyQuery, api_key: 1234 }); + + const options = { + url: `http://${host}:${port}/api/v1/sql/copyto?${copy}`, + headers: { host: 'vizzuality.cartodb.com' }, + method: 'GET' + }; + + const req = request(options); + + let response; + + req.on('response', function (res) { + response = res; + }); + + req.once('data', function () { + response.destroy(); + + countInsertedRows(host, port, function (err, result) { + if (err) { + return done(err); + } + + assert.equal(result.rows[0].count, 1); + + done(); + }); + }); + }); + + it('destroy on response', function (done) { + const { host, port } = this; + + const copy = querystring.stringify({ q: copyQuery, api_key: 1234 }); + + const options = { + url: `http://${host}:${port}/api/v1/sql/copyto?${copy}`, + headers: { host: 'vizzuality.cartodb.com' }, + method: 'GET' + }; + + const req = request(options); + + req.on('response', function (response) { + response.destroy(); + + countInsertedRows(host, port, function (err, result) { + if (err) { + return done(err); + } + + assert.equal(result.rows[0].count, 1); + + done(); + }); + }); + }); +}); diff --git a/test/acceptance/copy-endpoints.js b/test/acceptance/copy-endpoints.js new file mode 100644 index 0000000..b0f415e --- /dev/null +++ b/test/acceptance/copy-endpoints.js @@ -0,0 +1,604 @@ +'use strict'; + +require('../helper'); + +const fs = require('fs'); +const querystring = require('querystring'); +const assert = require('../support/assert'); +const os = require('os'); +const { Client } = require('pg'); +const request = require('request'); + +const StatsClient = require('../../app/stats/client'); +if (global.settings.statsd) { + // Perform keyword substitution in statsd + if (global.settings.statsd.prefix) { + const hostToken = os.hostname().split('.').reverse().join('.'); + global.settings.statsd.prefix = global.settings.statsd.prefix.replace(/:host/, hostToken); + } +} +const statsClient = StatsClient.getInstance(global.settings.statsd); +const server = require('../../app/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', + }), + headers: { host: 'vizzuality.cartodb.com' }, + method: 'GET' + }, {}, function(err, res) { + assert.ifError(err); + assert.ok(res.statusCode === 200); + const result = JSON.parse(res.body); + assert.strictEqual(result.rows[0].count, 0); + done(); + }); +}; + + +describe('copy-endpoints', function() { + before(function() { + this.client = new Client({ + user: 'postgres', + host: 'localhost', + database: 'cartodb_test_user_1_db', + port: 5432, + }); + this.client.connect(); + }); + + after(function() { + this.client.end(); + }); + + afterEach(function (done) { + this.client.query('TRUNCATE copy_endpoints_test', err => { + done(err); + }); + }); + + describe('general', function() { + it('should work with copyfrom endpoint', function(done){ + assert.response(server, { + 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'}, + method: 'POST' + },{}, function(err, res) { + assert.ifError(err); + const response = JSON.parse(res.body); + assert.equal(!!response.time, true); + assert.strictEqual(response.total_rows, 6); + done(); + }); + }); + + it('should fail with copyfrom endpoint and unexisting table', function(done){ + assert.response(server, { + 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'}, + method: 'POST' + },{}, function(err, res) { + assert.ifError(err); + assert.deepEqual( + JSON.parse(res.body), + { + error:['relation \"unexisting_table\" does not exist'] + } + ); + done(); + }); + }); + + it('should fail with copyfrom endpoint and without csv', function(done){ + assert.response(server, { + 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'}, + method: 'POST' + },{}, function(err, res) { + assert.ifError(err); + assert.deepEqual( + JSON.parse(res.body), + { + error:['No rows copied'] + } + ); + done(); + }); + }); + + it('should fail with copyfrom endpoint and without q', function(done){ + assert.response(server, { + url: "/api/v1/sql/copyfrom", + data: fs.createReadStream(__dirname + '/../support/csv/copy_test_table.csv'), + headers: {host: 'vizzuality.cartodb.com'}, + method: 'POST' + },{}, function(err, res) { + assert.ifError(err); + assert.deepEqual( + JSON.parse(res.body), + { + error:["SQL is missing"] + } + ); + done(); + }); + }); + + it('should work with copyto endpoint', function(done){ + assert.response(server, { + 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'}, + method: 'POST' + },{}, function(err) { + assert.ifError(err); + + assert.response(server, { + url: "/api/v1/sql/copyto?" + querystring.stringify({ + q: 'COPY copy_endpoints_test TO STDOUT', + filename: '/tmp/output.dmp' + }), + headers: {host: 'vizzuality.cartodb.com'}, + method: 'GET' + },{}, function(err, res) { + assert.ifError(err); + assert.strictEqual( + res.body, + '11\tPaul\t10\n12\tPeter\t10\n13\tMatthew\t10\n14\t\\N\t10\n15\tJames\t10\n16\tJohn\t10\n' + ); + + assert.equal(res.headers['content-disposition'], 'attachment; filename=%2Ftmp%2Foutput.dmp'); + assert.equal(res.headers['content-type'], 'application/octet-stream'); + + done(); + }); + }); + }); + + it('should fail with copyto endpoint and without sql', function(done){ + assert.response(server, { + url: "/api/v1/sql/copyto?" + querystring.stringify({ + filename: '/tmp/output.dmp' + }), + headers: {host: 'vizzuality.cartodb.com'}, + method: 'GET' + },{}, function(err, res) { + assert.ifError(err); + assert.deepEqual( + JSON.parse(res.body), + { + error:["SQL is missing"] + } + ); + done(); + }); + }); + + it('should work with copyfrom and gzip', function(done){ + assert.response(server, { + 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'), + headers: { + host: 'vizzuality.cartodb.com', + 'content-encoding': 'gzip' + }, + method: 'POST' + },{}, function(err, res) { + assert.ifError(err); + const response = JSON.parse(res.body); + assert.equal(!!response.time, true); + assert.strictEqual(response.total_rows, 6); + done(); + }); + }); + + it('should return an error when gzip headers are not correct', function(done) { + assert.response(server, { + 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', + 'content-encoding': 'gzip' + }, + method: 'POST' + },{}, function(err, res) { + assert.ifError(err); + assert.deepEqual( + JSON.parse(res.body), + { + error:["Error while gunzipping: incorrect header check"] + } + ); + done(); + }); + }); + }); + + + describe('timeout', function() { + before('set a 10 ms timeout', function() { + this.previous_timeout = global.settings.copy_timeout; + global.settings.copy_timeout = 10; + }); + + after('restore previous timeout', function() { + global.settings.copy_timeout = this.previous_timeout; + }); + + it('should fail with copyfrom and timeout', function(done) { + assert.response(server, { + 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'}, + method: 'POST' + }, + { + status: 429, + headers: { 'Content-Type': 'application/json; charset=utf-8' } + }, + 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.', + ], + context: 'limit', + detail: 'datasource' + }); + done(); + }); + }); + + it('should fail with copyto and timeout', function(done){ + assert.response(server, { + url: "/api/v1/sql/copyto?" + querystring.stringify({ + q: 'COPY populated_places_simple_reduced TO STDOUT', + filename: '/tmp/output.dmp' + }), + headers: {host: 'vizzuality.cartodb.com'}, + method: 'GET' + },{}, 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" + }; + const expectedError = res.body.substring(res.body.length - JSON.stringify(error).length); + assert.deepEqual(JSON.parse(expectedError), error); + done(); + }); + }); + }); + + + describe('db connections', function() { + before(function() { + this.db_pool_size = global.settings.db_pool_size; + global.settings.db_pool_size = 1; + }); + + after(function() { + global.settings.db_pool_size = this.db_pool_size; + }); + + it('copyfrom', function(done) { + function doCopyFrom() { + return new Promise(resolve => { + assert.response(server, { + 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'}, + method: 'POST' + },{}, function(err, res) { + assert.ifError(err); + const response = JSON.parse(res.body); + assert.ok(response.time); + resolve(); + }); + }); + } + + Promise.all([doCopyFrom(), doCopyFrom(), doCopyFrom()]).then(function() { + done(); + }); + }); + + 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`, + filename: '/tmp/output.dmp' + }), + headers: {host: 'vizzuality.cartodb.com'}, + method: 'GET' + },{}, function(err, res) { + assert.ifError(err); + assert.ok(res.body); + resolve(); + }); + }); + } + + assert.response(server, { + 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'}, + method: 'POST' + },{}, function(err) { + assert.ifError(err); + + Promise.all([doCopyTo(), doCopyTo(), doCopyTo()]).then(function() { + done(); + }); + }); + }); + }); + + describe('client disconnection', function() { + before(function() { + this.db_pool_size = global.settings.db_pool_size; + global.settings.db_pool_size = 1; + }); + + 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', + }), + headers: { host: 'vizzuality.cartodb.com' }, + method: 'GET' + }, {}, 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) { + 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` + }); + + const options = { + url: `http://${address}:${port}/api/v1/sql/copyto?${query}`, + headers: { host: 'vizzuality.cartodb.com' }, + method: 'GET' + }; + + const req = request(options); + + req.once('data', () => req.abort()); + req.on('response', response => { + response.on('end', () => { + assertCanReuseConnection(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)` + }); + + const options = { + url: `http://${address}:${port}/api/v1/sql/copyfrom?${query}`, + headers: { host: 'vizzuality.cartodb.com' }, + method: 'POST', + data: fs.createReadStream(__dirname + '/../support/csv/copy_test_table.csv') + }; + + const req = request(options); + + setTimeout(() => { + req.abort(); + assertCanReuseCanceledConnection(done); + }, 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) { + assert.response(server, { + url: '/api/v1/sql?q=set statement_timeout = 10', + headers: {host: 'vizzuality.cartodb.com'}, + method: 'GET' + }, 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'}, + method: 'GET' + }, done); + }); + + it('COPY FROM can take longer than regular statement_timeout', function(done) { + assert.response(server, { + 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'}, + method: 'POST' + }, { + status: 200, + headers: { 'Content-Type': 'application/json; charset=utf-8' } + }, function(err, res) { + assert.ifError(err); + const response = JSON.parse(res.body); + assert.strictEqual(response.total_rows, 6); + done(); + }); + }); + + it('COPY TO can take longer than regular statement_timeout', function(done) { + assert.response(server, { + url: "/api/v1/sql/copyto?" + querystring.stringify({ + q: 'COPY copy_endpoints_test TO STDOUT', + filename: '/tmp/output.dmp' + }), + headers: {host: 'vizzuality.cartodb.com'}, + method: 'GET' + }, {}, function(err, res) { + assert.ifError(err); + assert.ok(res.statusCode === 200); + 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 + $$ + BEGIN + RETURN 250 * 1024 * 1024 - 1; + END; + $$ LANGUAGE 'plpgsql' VOLATILE; + `, err => done(err)); + + this.db_pool_size = global.settings.db_pool_size; + global.settings.db_pool_size = 1; + }); + + 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 + $$ + BEGIN + RETURN 200 * 1024 * 1024; + END; + $$ LANGUAGE 'plpgsql' VOLATILE; + `, err => done(err)); + + global.settings.db_pool_size = this.db_pool_size; + }); + + it('COPY FROM fails with an error if DB quota is exhausted', function(done) { + assert.response(server, { + 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'}, + method: 'POST' + }, { + status: 400, + headers: { 'Content-Type': 'application/json; charset=utf-8' } + }, function(err, res) { + const response = JSON.parse(res.body); + 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) { + assert.response(server, { + url: "/api/v1/sql/copyto?" + querystring.stringify({ + q: 'COPY copy_endpoints_test TO STDOUT', + filename: '/tmp/output.dmp' + }), + headers: {host: 'vizzuality.cartodb.com'}, + method: 'GET' + }, {}, function(err, res) { + assert.ifError(err); + assert.ok(res.statusCode === 200); + done(); + }); + }); + }); + + 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; + global.settings.copy_from_max_post_size_pretty = '10 bytes'; + this.db_pool_size = global.settings.db_pool_size; + global.settings.db_pool_size = 1; + }); + 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) { + assert.response(server, { + 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'}, + method: 'POST' + }, { + status: 400, + headers: { 'Content-Type': 'application/json; charset=utf-8' } + }, function(err, res) { + const response = JSON.parse(res.body); + 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.js b/test/acceptance/copy-statements.js new file mode 100644 index 0000000..6fb5c8f --- /dev/null +++ b/test/acceptance/copy-statements.js @@ -0,0 +1,80 @@ +'use strict'; + +require('../helper'); + +var server = require('../../app/server')(); +var assert = require('../support/assert'); +var querystring = require('querystring'); + + +describe('copy-statements', function() { + + var RESPONSE_OK = { + statusCode: 200 + }; + + before(function(done) { + assert.response(server, { + url: "/api/v1/sql?" + querystring.stringify({ + q: 'CREATE TABLE copy_test_table(a int)', + api_key: 1234 + }), + headers: {host: 'vizzuality.cartodb.com'}, + method: 'GET' + }, RESPONSE_OK, done); + }); + + after(function(done) { + assert.response(server, { + url: "/api/v1/sql?" + querystring.stringify({ + q: 'DROP TABLE IF EXISTS copy_test_table', + api_key: 1234 + }), + 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){ + assert.response(server, { + url: "/api/v1/sql?" + querystring.stringify({ + q: 'COPY copy_test_table FROM stdin;', + api_key: 1234 + }), + headers: {host: 'vizzuality.cartodb.com'}, + method: 'GET' + },{}, 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(); + }); + }); + + it('COPY TABLE with GET and auth', function(done){ + assert.response(server, { + url: "/api/v1/sql?" + querystring.stringify({ + q: "COPY copy_test_table to '/tmp/x';", + api_key: 1234 + }), + headers: {host: 'vizzuality.cartodb.com'}, + method: 'GET' + },{}, 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(); + }); + }); + + +}); diff --git a/test/acceptance/error-handler.js b/test/acceptance/error-handler.js new file mode 100644 index 0000000..c6d9754 --- /dev/null +++ b/test/acceptance/error-handler.js @@ -0,0 +1,33 @@ +'use strict'; + +var server = require('../../app/server')(); +var assert = require('../support/assert'); + +describe('error handler', function () { + it('should returns a errors header', function (done) { + const errorHeader = { + detail: undefined, + hint: undefined, + context: undefined, + statusCode: 400, + message: 'You must indicate a sql query' + }; + + assert.response(server, { + url: '/api/v1/sql', + headers: {host: 'vizzuality.cartodb.com'}, + method: 'GET' + }, + { + status: 400, + headers: { + 'Content-Type': 'application/json; charset=utf-8', + 'X-SQLAPI-Errors': JSON.stringify(errorHeader) + } + }, + function(err){ + assert.ifError(err); + done(); + }); + }); +}); diff --git a/test/acceptance/export/arraybuffer.js b/test/acceptance/export/arraybuffer.js new file mode 100644 index 0000000..8e42679 --- /dev/null +++ b/test/acceptance/export/arraybuffer.js @@ -0,0 +1,45 @@ +'use strict'; + +require('../../helper'); +require('../../support/assert'); + + +var server = require('../../../app/server')(); +var assert = require('assert'); +var querystring = require('querystring'); + +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 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.js b/test/acceptance/export/csv.js new file mode 100644 index 0000000..7f37b42 --- /dev/null +++ b/test/acceptance/export/csv.js @@ -0,0 +1,209 @@ +'use strict'; + +require('../../helper'); +require('../../support/assert'); + + +var server = require('../../../app/server')(); +var assert = require('assert'); +var querystring = require('querystring'); + +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); + + 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'); + + 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-1) { + return tmp.length - tmp.indexOf(dec_sep) - 1; + } else { + return 0; + } +}; + +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('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.js b/test/acceptance/export/geopackage.js new file mode 100644 index 0000000..392171c --- /dev/null +++ b/test/acceptance/export/geopackage.js @@ -0,0 +1,82 @@ +'use strict'; + +require('../../helper'); + +var server = require('../../../app/server')(); +var assert = require('../../support/assert'); +var sqlite = require('sqlite3'); +var fs = require('fs'); + +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){ + assert.response(server, { + url: base_url, + headers: {host: 'vizzuality.cartodb.com'}, + method: 'GET' + },{ }, 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); + var db = new sqlite.Database(':memory:', res.body); + 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){ + assert.response(server, { + url: base_url, + headers: {host: 'vizzuality.cartodb.com'}, + encoding: 'binary', + method: 'GET' + },{ }, function(err, res) { + var tmpfile = '/tmp/a_geopackage_file.gpkg'; + try { + fs.writeFileSync(tmpfile, res.body, 'binary'); + } catch(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); + + 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(); + }); + assert.notEqual(dqr, undefined); + }); + }); + }); + }); + +}); diff --git a/test/acceptance/export/kml.js b/test/acceptance/export/kml.js new file mode 100644 index 0000000..4447363 --- /dev/null +++ b/test/acceptance/export/kml.js @@ -0,0 +1,403 @@ +'use strict'; + +require('../../helper'); + +var server = require('../../../app/server')(); +var assert = require('../../support/assert'); +var querystring = require('querystring'); +var libxmljs = require('libxmljs'); + +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) { + + // Strip namespace: + //https://github.com/polotek/libxmljs/issues/212 + kml = kml.replace(/ xmlns=[^>]*>/, '>'); + + 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, "' + + '' + + 'cartodb_query' + + '$'); + 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" \\?>' + + '' + + 'cartodb_query' + + '$'); + 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, 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){ + + 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(); + } + ); + }); + + 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(); + } + ); + }); + + 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", + format: 'kml' + }), + headers: { + host: 'vizzuality.cartodb.com' + }, + encoding: 'binary', + method: 'GET' + }, + { + status: 200 + }, + function(err, res) { + assert.equal(res.body.match(//g), null); + done(); + } + ); + }); + +}); diff --git a/test/acceptance/export/shapefile.js b/test/acceptance/export/shapefile.js new file mode 100644 index 0000000..7fd8d1e --- /dev/null +++ b/test/acceptance/export/shapefile.js @@ -0,0 +1,405 @@ +'use strict'; + +require('../../helper'); + +var server = require('../../../app/server')(); +var assert = require('../../support/assert'); +var querystring = require('querystring'); +var shapefile = require('shapefile'); +var _ = require('underscore'); +var zipfile = require('zipfile'); +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') > 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; + 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", + format: 'svg' + }), + headers: { + host: 'vizzuality.cartodb.com' + }, + method: 'GET' + }, + { + status: 400 + }, + function(err, res) { + var parsedBody = JSON.parse(res.body); + assert.deepEqual(Object.keys(parsedBody), ['error']); + assert.deepEqual(parsedBody.error, ["division by zero"]); + done(); + } + ); + }); + + + +}); diff --git a/test/acceptance/export/timeout.js b/test/acceptance/export/timeout.js new file mode 100644 index 0000000..3590da1 --- /dev/null +++ b/test/acceptance/export/timeout.js @@ -0,0 +1,202 @@ +'use strict'; + +const TestClient = require('../../support/test-client'); + +require('../../support/assert'); + +var assert = require('assert'); +var querystring = require('querystring'); +const db_utils = require('../../support/db_utils'); + +describe('timeout', function () { + describe('export database', function () { + before(db_utils.resetPgBouncerConnections); + after(db_utils.resetPgBouncerConnections); + + const databaseTimeoutQuery = ` + select + ST_SetSRID(ST_Point(0, 0), 4326) as the_geom, + pg_sleep(0.2) as sleep, + 1 as value + `; + + const scenarios = [ + { + desc: 'CSV', + format: 'csv', + contentType: 'application/x-www-form-urlencoded', + parser: querystring.stringify, + // only: true, + skip: true + }, + { + query: databaseTimeoutQuery, + desc: 'Geopackage', + format: 'gpkg' + }, + { + query: databaseTimeoutQuery, + desc: 'KML', + format: 'kml' + }, + { + query: databaseTimeoutQuery, + desc: 'Shapefile', + format: 'shp' + }, + { + query: databaseTimeoutQuery, + desc: 'Spatialite', + format: 'spatialite' + }, + { + query: databaseTimeoutQuery, + desc: 'Array Buffer', + format: 'arraybuffer' + }, + { + query: databaseTimeoutQuery, + desc: 'GeoJSON', + format: 'geojson' + }, + { + query: databaseTimeoutQuery, + desc: 'JSON', + format: 'json' + }, + { + query: databaseTimeoutQuery, + desc: 'SVG', + format: 'svg' + }, + { + query: databaseTimeoutQuery, + desc: 'TopoJSON', + format: 'topojson' + } + ]; + + beforeEach(function (done) { + this.testClient = new TestClient(); + this.testClient.setUserDatabaseTimeoutLimit('localhost', 100, done); + }); + + afterEach(function (done) { + this.testClient.setUserDatabaseTimeoutLimit('localhost', 2000, done); + }); + + scenarios.forEach((scenario) => { + const test = scenario.only ? it.only : scenario.skip ? it.skip : it; + + test(`${scenario.desc} export exceeding statement timeout responds 429 Over Limits`, function (done) { + const override = { + 'Content-Type': scenario.contentType, + parser: scenario.parser, + anonymous: true, + format: scenario.format, + response: { + status: 429 + } + }; + + this.testClient.getResult(scenario.query, override, (err, res) => { + assert.ifError(err); + + 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.', + ], + context: 'limit', + detail: 'datasource' + }); + + done(); + }); + }); + }); + }); + + describe('export ogr command timeout', function () { + const ogrCommandTimeoutQuery = ` + select + ST_SetSRID(ST_Point(0, 0), 4326) as the_geom, + pg_sleep(0.2) as sleep, + 1 as value + `; + + const scenarios = [ + { + query: ogrCommandTimeoutQuery, + desc: 'CSV', + format: 'csv', + contentType: 'application/x-www-form-urlencoded', + parser: querystring.stringify, + // only: true, + // skip: true + }, + { + query: ogrCommandTimeoutQuery, + filename: 'wadus_gpkg_filename', + desc: 'Geopackage', + format: 'gpkg' + }, + { + query: ogrCommandTimeoutQuery, + desc: 'KML', + format: 'kml' + }, + { + query: ogrCommandTimeoutQuery, + desc: 'Shapefile', + format: 'shp' + }, + { + query: ogrCommandTimeoutQuery, + desc: 'Spatialite', + format: 'spatialite' + } + ]; + + beforeEach(function (done) { + this.testClient = new TestClient(); + this.testClient.setUserRenderTimeoutLimit('vizzuality', 100, done); + }); + + afterEach(function (done) { + this.testClient.setUserRenderTimeoutLimit('vizzuality', 0, done); + }); + + scenarios.forEach((scenario) => { + const test = scenario.only ? it.only : scenario.skip ? it.skip : it; + + test(`${scenario.desc} export exceeding statement timeout responds 429 Over Limits`, function (done) { + const override = { + 'Content-Type': scenario.contentType, + parser: scenario.parser, + anonymous: true, + format: scenario.format, + filename: scenario.filename, + response: { + status: 429 + } + }; + + this.testClient.getResult(scenario.query, override, (err, res) => { + assert.ifError(err); + + 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.', + ], + context: 'limit', + detail: 'datasource' + }); + + done(); + }); + }); + }); + }); +}); diff --git a/test/acceptance/export/topojson.js b/test/acceptance/export/topojson.js new file mode 100644 index 0000000..61dd6d7 --- /dev/null +++ b/test/acceptance/export/topojson.js @@ -0,0 +1,267 @@ +'use strict'; + +require('../../helper'); + +var server = require('../../../app/server')(); +var assert = require('../../support/assert'); +var querystring = require('querystring'); +var _ = require('underscore'); + +describe('export.topojson', function() { + +// TOPOJSON tests + + function getRequest(query, extraParams) { + var params = { + q: query, + format: 'topojson' + }; + + params = _.extend(params, extraParams || {}); + + return { + url: '/api/v1/sql?' + querystring.stringify(params), + 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 " + + "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'); + + // 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); + + 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'); + + // 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(); + }); +}); + +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 " + + "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'); + + // 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); + + 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 ] ]); + + 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", + { + skipfields: 'name' + } + ), + { + status: 200 + }, + 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); + done(); + } + ); + }); + + it('jsonp callback is invoked', 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", + { + callback: 'foo_jsonp' + } + ), + { + status: 200 + }, + function(err, res) { + assert.equal(res.statusCode, 200, res.statusCode + ': ' + res.body); + var didRunJsonCallback = false; + // jshint ignore:start + function foo_jsonp(body) { + didRunJsonCallback = true; + } + eval(res.body); + // jshint ignore:end + assert.ok(didRunJsonCallback); + done(); + } + ); + }); + + + 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", + format: 'topojson' + }), + headers: { + host: 'vizzuality.cartodb.com' + }, + method: 'GET' + }, + { + status: 400 + }, + function(err, res) { + var parsedBody = JSON.parse(res.body); + assert.deepEqual(Object.keys(parsedBody), ['error']); + assert.deepEqual(parsedBody.error, ["division by zero"]); + done(); + } + ); + }); + + +}); diff --git a/test/acceptance/frontend_abort.js b/test/acceptance/frontend_abort.js new file mode 100644 index 0000000..7ba7f63 --- /dev/null +++ b/test/acceptance/frontend_abort.js @@ -0,0 +1,79 @@ +'use strict'; + +require('../helper'); + +var assert = require('../support/assert'); +var step = require('step'); +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(); + }); +}); + +describe('frontend abort', function() { + +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('../../app/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/health_check.js b/test/acceptance/health_check.js new file mode 100644 index 0000000..bd29eb1 --- /dev/null +++ b/test/acceptance/health_check.js @@ -0,0 +1,68 @@ +'use strict'; + +require('../helper'); +require('../support/assert'); + +var assert = require('assert'); +var server = require('../../app/server')(); + +describe('health checks', function() { + + beforeEach(function(done) { + global.settings.health = { + enabled: true + //username: 'vizzuality', + //query: 'select 1::text' + }; + done(); + }); + + var healthCheckRequest = { + url: '/api/v1/health', + method: 'GET', + headers: { + host: 'vizzuality.localhost' + } + }; + + it('returns 200 and ok=true with disabled configuration', function(done) { + global.settings.health.enabled = false; + + assert.response(server, + healthCheckRequest, + { + status: 200 + }, + function(err, res) { + assert.ok(!err); + + var parsed = JSON.parse(res.body); + + assert.equal(parsed.enabled, false); + assert.ok(parsed.ok); + + done(); + } + ); + }); + + it('returns 200 and ok=true with enabled configuration', function(done) { + assert.response(server, + healthCheckRequest, + { + status: 200 + }, + function(err, res) { + assert.ok(!err); + + var parsed = JSON.parse(res.body); + + assert.ok(parsed.enabled); + assert.ok(parsed.ok); + + done(); + } + ); + }); + +}); diff --git a/test/acceptance/last-modified-header.js b/test/acceptance/last-modified-header.js new file mode 100644 index 0000000..28799a1 --- /dev/null +++ b/test/acceptance/last-modified-header.js @@ -0,0 +1,114 @@ +'use strict'; + +require('../helper'); + +var server = require('../../app/server')(); +var assert = require('../support/assert'); +var qs = require('querystring'); +var MockDate = require('mockdate'); + +describe('last modified header', function() { + + var scenarios = [ + { + tables: ['untitle_table_4'], + desc: 'should use last updated time from public table', + expectedLastModified: 'Wed, 01 Jan 2014 23:31:30 GMT' + }, + { + tables: ['private_table'], + desc: 'should use last updated time from private table', + expectedLastModified: 'Thu, 01 Jan 2015 23:31:30 GMT' + }, + { + tables: ['untitle_table_4', 'private_table'], + desc: 'should use most recent last updated time from private and public table', + expectedLastModified: 'Thu, 01 Jan 2015 23:31:30 GMT' + }, + { + tables: ['populated_places_simple_reduced', 'private_table'], + desc: 'should use last updated time from table in cdb_tablemetadata instead of now() from unknown table', + expectedLastModified: 'Thu, 01 Jan 2015 23:31:30 GMT' + } + ]; + + scenarios.forEach(function(scenario) { + it(scenario.desc, function(done) { + var query = qs.stringify({ + q: scenario.tables.map(function(table) { + return 'select cartodb_id from ' + table; + }).join(' UNION ALL '), + api_key: 1234 + }); + assert.response(server, + { + url: '/api/v1/sql?' + query, + headers: { + host: 'vizzuality.cartodb.com' + }, + method: 'GET' + }, + { + statusCode: 200 + }, + function(err, res) { + assert.equal(res.headers['last-modified'], scenario.expectedLastModified); + 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); + assert.response(server, + { + url: '/api/v1/sql?' + query, + headers: { + host: 'vizzuality.cartodb.com' + }, + method: 'GET' + }, + { + statusCode: 200 + }, + function(err, res) { + MockDate.reset(); + assert.equal(res.headers['last-modified'], new Date(fixedDateNow).toUTCString()); + 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); + assert.response(server, + { + url: '/api/v1/sql?' + query, + headers: { + host: 'vizzuality.cartodb.com' + }, + method: 'GET' + }, + { + statusCode: 200 + }, + function(err, res) { + MockDate.reset(); + assert.equal(res.headers['last-modified'], new Date(fixedDateNow).toUTCString()); + done(); + } + ); + }); + +}); diff --git a/test/acceptance/logging.js b/test/acceptance/logging.js new file mode 100644 index 0000000..b12b018 --- /dev/null +++ b/test/acceptance/logging.js @@ -0,0 +1,152 @@ +'use strict'; + +require('../helper'); + +var appServer = require('../../app/server'); +var assert = require('../support/assert'); +var qs = require('querystring'); +var log4js = require('log4js'); + +describe('Logging SQL query on POST requests', function() { + + var SQL_QUERY = "SELECT 'wadus'"; + var API_KEY = 1234; + var BODY_PAYLOAD = { + q: SQL_QUERY, + api_key: API_KEY + }; + + var RESPONSE_OK = { + statusCode: 200 + }; + + var server; + before(function() { + global.settings.log_format = ':method :req[Host]:url :status :sql'; + global.log4js = log4js; + global.log4js.configure({ + appenders: [ + { + type: "console", + layout: { + type:'basic' + } + } + ] + }); + server = appServer(); + }); + + after(function() { + global.log4js = null; + delete global.log4js; + }); + + function createPostRequest(body, contentType, getParams) { + var url = '/api/v1/sql'; + if (getParams) { + url += '?' + qs.stringify(getParams); + } + return { + method: 'POST', + url: url, + data: body, + headers: { + host: 'vizzuality.cartodb.com', + 'Content-Type': contentType + } + }; + } + + var LENGHTY_SUFFIX = ' [...]'; + + var postScenariosRequests = [ + { + desc: 'should return json string for application/x-www-form-urlencoded', + request: createPostRequest( + qs.stringify(BODY_PAYLOAD), 'application/x-www-form-urlencoded' + ) + }, + { + desc: 'should return json string for application/x-www-form-urlencoded, with API key in GET param', + request: createPostRequest( + qs.stringify({q: SQL_QUERY}), 'application/x-www-form-urlencoded', {api_key: API_KEY} + ) + }, + { + desc: 'should return json string for application/json', + request: createPostRequest( + JSON.stringify(BODY_PAYLOAD), 'application/json' + ) + }, + { + desc: 'should return json string for application/json, with API key in GET param', + request: createPostRequest( + JSON.stringify({q: SQL_QUERY}), 'application/json', {api_key: API_KEY} + ) + }, + { + desc: 'should return a substring when sql query is very long', + request: createPostRequest( + JSON.stringify({q: "select '" + new Array(2500).join('a') + "'"}), 'application/json' + ), + expectedSQLQueryToLog: "select '" + (new Array(2000 + 1 - "select '".length).join('a')) + LENGHTY_SUFFIX + } + ]; + + postScenariosRequests.forEach(function(scenario) { + it(scenario.desc, function(done) { + var called = 0; + + var getSqlQueryFromRequestBodyFn = server.getSqlQueryFromRequestBody; + + server.getSqlQueryFromRequestBody = function(req) { + called++; + var result = getSqlQueryFromRequestBodyFn(req); + assert.deepEqual(JSON.parse(result), {q: scenario.expectedSQLQueryToLog || SQL_QUERY}); + return result; + }; + + assert.response(server, scenario.request, RESPONSE_OK, function(err) { + assert.ok(!err); + assert.equal(called, 1); + + server.getSqlQueryFromRequestBody = getSqlQueryFromRequestBodyFn; + + done(); + }); + }); + }); + + it('should not log sql query in GET requests', function(done) { + var called = 0; + + var getSqlQueryFromRequestBodyFn = server.getSqlQueryFromRequestBody; + + server.getSqlQueryFromRequestBody = function(req) { + called++; + var result = getSqlQueryFromRequestBodyFn(req); + assert.equal(result, ''); + return result; + }; + + assert.response(server, + { + method: 'GET', + url: '/api/v1/sql?' + qs.stringify(BODY_PAYLOAD), + headers: { + host: 'vizzuality.cartodb.com' + } + }, + RESPONSE_OK, + function(err) { + assert.ok(!err); + assert.equal(called, 1); + + server.getSqlQueryFromRequestBody = getSqlQueryFromRequestBodyFn; + + done(); + } + ); + }); +}); diff --git a/test/acceptance/oauth/oauth_test.py b/test/acceptance/oauth/oauth_test.py new file mode 100644 index 0000000..a933566 --- /dev/null +++ b/test/acceptance/oauth/oauth_test.py @@ -0,0 +1,26 @@ +# TO RUN +# > virtualenv env +# > . env/bin/activate +# > pip install oauth2 +# > pip install cartodb +# +# FILL IN THINGS BELOW +# > python oauth_test.py + +from cartodb import CartoDB, CartoDBException + +import httplib2 +import oauth2 as oauth +if __name__ == '__main__': + + user = '' + password = '' + CONSUMER_KEY= '' + CONSUMER_SECRET= '' + cl = CartoDB(CONSUMER_KEY, CONSUMER_SECRET, user, password, 'simon') + try: + print cl.sql('select * from do_not_exist') + except CartoDBException as e: + print ("some error ocurred", e) + print cl.sql('select * from table'); + diff --git a/test/acceptance/pagination.js b/test/acceptance/pagination.js new file mode 100644 index 0000000..b371e3f --- /dev/null +++ b/test/acceptance/pagination.js @@ -0,0 +1,175 @@ +'use strict'; + +require('../helper'); + +var server = require('../../app/server')(); +var assert = require('../support/assert'); +var querystring = require('querystring'); +var step = require('step'); + + +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) { + 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 + }), + headers: {host: 'vizzuality.cartodb.com'}, + method: 'GET' + }, 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); + done(); + }); + }); + + // Test page and rows_per_page params + it("paging", function(done){ + var sql = 'SELECT * FROM (VALUES(1),(2),(3),(4),(5),(6),(7),(8),(9)) t(v)'; + var pr = [ [2,3], [0,4] ]; // page and rows + var methods = [ 'GET', 'POST' ]; + var authorized = 0; + var testing = 0; + var method = 0; + // jshint maxcomplexity:7 + var testNext = function() { + if ( testing >= pr.length ) { + if ( method+1 >= methods.length ) { + if ( authorized ) { + done(); + return; + } else { + authorized = 1; + method = 0; + testing = 0; + } + } else { + testing = 0; + ++method; + } + } + var prcur = pr[testing++]; + console.log("Test " + testing + "/" + pr.length + " method " + methods[method] + " " + + ( authorized ? "authenticated" : "" ) ); + 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'); + done(); + }); + } + + describe('validatePGEntitiesAccess enabled', function() { + before(function(){ + global.settings.validatePGEntitiesAccess = true; + }); + + forbiddenQueries.forEach(query => { + it(`testClientApiKey: query: ${query}`, function(done) { + assertQuery(query, testClientApiKey, done); + }); + + it(`testClientAuthorized: query: ${query}`, function(done) { + assertQuery(query, testClientAuthorized, done); + }); + }); + }); + + describe('validatePGEntitiesAccess disabled', function() { + before(function(){ + global.settings.validatePGEntitiesAccess = false; + }); + + forbiddenQueries.forEach(query => { + it(`testClientApiKey: query: ${query}`, function(done) { + testClientApiKey.getResult(query, err => { + assert.ifError(err); + done(); + }); + }); + + it(`testClientAuthorized: query: ${query}`, function(done) { + testClientAuthorized.getResult(query, err => { + assert.ifError(err); + done(); + }); + }); + }); + }); +}); diff --git a/test/acceptance/query-float-values.js b/test/acceptance/query-float-values.js new file mode 100644 index 0000000..5e0a632 --- /dev/null +++ b/test/acceptance/query-float-values.js @@ -0,0 +1,72 @@ +'use strict'; + +require('../helper'); + +var server = require('../../app/server')(); +var assert = require('../support/assert'); +var querystring = require('querystring'); +var step = require('step'); + +describe('special numeric (float) values', function() { + var RESPONSE_OK = { + statusCode: 200 + }; + var HEADERS = { + host: 'vizzuality.localhost.lan:8080' + }; + var METHOD = 'GET'; + var URL = '/api/v1/sql?api_key=1234&'; + + it('should cast Infinity and NaN values properly', function (done) { + step( + function createTable () { + var next = this; + var opts = { + url: URL + querystring.stringify({ + q: 'create table numbers_test(val float)' + }), + headers: HEADERS, + method: METHOD + }; + assert.response(server, opts, RESPONSE_OK, next); + }, + function insertData (err) { + assert.ifError(err); + var next = this; + var opts = { + url: URL + querystring.stringify({ + q: [ + 'insert into numbers_test', + ' values (\'NaN\'::float), (\'infinity\'::float), (\'-infinity\'::float), (1::float)' + ].join('') + }), + headers: HEADERS, + method: METHOD + }; + assert.response(server, opts, RESPONSE_OK, next); + }, + function queryData (err) { + assert.ifError(err); + var next = this; + var opts = { + url: URL + querystring.stringify({ + q: 'select * from numbers_test' + }), + headers: HEADERS, + method: METHOD + }; + assert.response(server, opts, RESPONSE_OK, next); + }, + function assertResult (err, res) { + assert.ifError(err); + var result = JSON.parse(res.body); + assert.ok(Array.isArray(result.rows)); + assert.equal(result.rows[0].val, 'NaN'); + assert.equal(result.rows[1].val, 'Infinity'); + assert.equal(result.rows[2].val, '-Infinity'); + assert.equal(result.rows[3].val, 1); + done(); + } + ); + }); +}); diff --git a/test/acceptance/query-multipart.js b/test/acceptance/query-multipart.js new file mode 100644 index 0000000..a048fae --- /dev/null +++ b/test/acceptance/query-multipart.js @@ -0,0 +1,26 @@ +'use strict'; + +require('../helper'); + +const server = require('../../app/server')(); +const assert = require('../support/assert'); + +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'}, + method: 'POST' + },{}, function(err, res) { + assert.ifError(err); + const response = JSON.parse(res.body); + assert.equal(typeof(response.time) !== 'undefined', true); + assert.strictEqual(response.total_rows, 1); + assert.deepStrictEqual(response.rows, [{n:2}]); + done(); + }); + }); +}); diff --git a/test/acceptance/query-returning.js b/test/acceptance/query-returning.js new file mode 100644 index 0000000..cfbf921 --- /dev/null +++ b/test/acceptance/query-returning.js @@ -0,0 +1,159 @@ +'use strict'; + +require('../helper'); + +var server = require('../../app/server')(); +var assert = require('../support/assert'); +var querystring = require('querystring'); +var _ = require('underscore'); + + +describe('query-returning', function() { + + var RESPONSE_OK = { + statusCode: 200 + }; + + var expected_rw_cache_control = 'no-cache,max-age=0,must-revalidate,public'; + + // Check results from INSERT + // + // See https://github.com/Vizzuality/CartoDB-SQL-API/issues/13 + 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: + "INSERT INTO private_table(name) VALUES('noret1') UNION VALUES('noret2')" + }), + headers: {host: 'vizzuality.localhost.lan:8080' }, + method: 'GET' + }, RESPONSE_OK, function(err, res) { + var out = JSON.parse(res.body); + assert.ok(out.hasOwnProperty('time')); + assert.equal(out.total_rows, 2); + assert.equal(out.rows.length, 0); + // 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(); + }); + }); + + // Check results from UPDATE + // + // See https://github.com/Vizzuality/CartoDB-SQL-API/issues/13 + 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: + "UPDATE private_table SET name = upper(name) WHERE name in ('noret1', 'noret2')" + }), + headers: {host: 'vizzuality.localhost.lan:8080' }, + method: 'GET' + }, RESPONSE_OK, function(err, res) { + var out = JSON.parse(res.body); + assert.ok(out.hasOwnProperty('time')); + assert.equal(out.total_rows, 2); + assert.equal(out.rows.length, 0); + // 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(); + }); + }); + + // Check results from DELETE + // + // See https://github.com/Vizzuality/CartoDB-SQL-API/issues/13 + 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: + "DELETE FROM private_table WHERE name in ('NORET1', 'NORET2')" + }), + headers: {host: 'vizzuality.localhost.lan:8080' }, + method: 'GET' + }, RESPONSE_OK, function(err, res) { + var out = JSON.parse(res.body); + assert.ok(out.hasOwnProperty('time')); + assert.equal(out.total_rows, 2); + assert.equal(out.rows.length, 0); + // 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(); + }); + }); + + // Check results from INSERT .. RETURNING + // + // See https://github.com/Vizzuality/CartoDB-SQL-API/issues/50 + 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: + "INSERT INTO private_table(name) VALUES('test') RETURNING upper(name), reverse(name)" + }), + headers: {host: 'vizzuality.localhost.lan:8080' }, + method: 'GET' + }, RESPONSE_OK, function(err, res) { + var out = JSON.parse(res.body); + assert.ok(out.hasOwnProperty('time')); + assert.equal(out.total_rows, 1); + assert.equal(out.rows.length, 1); + assert.equal(_.keys(out.rows[0]).length, 2); + assert.equal(out.rows[0].upper, 'TEST'); + assert.equal(out.rows[0].reverse, 'tset'); + done(); + }); + }); + + // Check results from UPDATE .. RETURNING + // + // See https://github.com/Vizzuality/CartoDB-SQL-API/issues/50 + 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: + "UPDATE private_table SET name = 'tost' WHERE name = 'test' RETURNING upper(name), reverse(name)" + }), + headers: {host: 'vizzuality.localhost.lan:8080' }, + method: 'GET' + }, RESPONSE_OK, function(err, res) { + var out = JSON.parse(res.body); + assert.ok(out.hasOwnProperty('time')); + assert.equal(out.total_rows, 1); + assert.equal(out.rows.length, 1); + assert.equal(_.keys(out.rows[0]).length, 2); + assert.equal(out.rows[0].upper, 'TOST'); + assert.equal(out.rows[0].reverse, 'tsot'); + done(); + }); + }); + + // Check results from DELETE .. RETURNING + // + // See https://github.com/Vizzuality/CartoDB-SQL-API/issues/50 + 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: + "DELETE FROM private_table WHERE name = 'tost' RETURNING name" + }), + headers: {host: 'vizzuality.localhost.lan:8080' }, + method: 'GET' + }, RESPONSE_OK, function(err, res) { + var out = JSON.parse(res.body); + assert.ok(out.hasOwnProperty('time')); + assert.equal(out.total_rows, 1); + assert.equal(out.rows.length, 1); + assert.equal(_.keys(out.rows[0]).length, 1); + assert.equal(out.rows[0].name, 'tost'); + done(); + }); + }); + +}); diff --git a/test/acceptance/query-tables-api-cache.js b/test/acceptance/query-tables-api-cache.js new file mode 100644 index 0000000..e18375f --- /dev/null +++ b/test/acceptance/query-tables-api-cache.js @@ -0,0 +1,101 @@ +'use strict'; + +require('../helper'); + +var qs = require('querystring'); + +var server = require('../../app/server')(); +var assert = require('../support/assert'); + +describe('query-tables-api', function() { + + beforeEach(function(done) { + var tableCacheEnabled = global.settings.tableCacheEnabled || false; + if(!tableCacheEnabled) { + this.skip("tableCache is disabled"); + } + done(); + }); + + function getCacheStatus(callback) { + assert.response( + server, + { + method: 'GET', + url: '/api/v1/cachestatus' + }, + { + status: 200 + }, + function(err, res) { + callback(null, JSON.parse(res.body)); + } + ); + } + + var request = { + url: '/api/v1/sql?' + qs.stringify({ + q: 'SELECT * FROM untitle_table_4' + }), + headers: { + host: 'vizzuality.cartodb.com' + }, + method: 'GET' + }; + + var RESPONSE_OK = { + status: 200 + }; + + it('should create a key in affected tables cache', function(done) { + assert.response(server, request, RESPONSE_OK, function(err) { + assert.ok(!err, err); + + getCacheStatus(function(err, cacheStatus) { + assert.ok(!err, err); + assert.equal(cacheStatus.explain.keys, 1); + assert.equal(cacheStatus.explain.hits, 0); + + done(); + }); + }); + }); + + it('should use cache to retrieve affected tables', function(done) { + assert.response(server, request, RESPONSE_OK, function(err) { + assert.ok(!err, err); + + getCacheStatus(function(err, cacheStatus) { + assert.ok(!err, err); + assert.equal(cacheStatus.explain.keys, 1); + assert.equal(cacheStatus.explain.hits, 1); + + done(); + }); + }); + }); + + it('should skip cache to retrieve affected tables', function(done) { + var masterRequest = { + url: '/api/v1/sql?' + qs.stringify({ + q: 'SELECT * FROM untitle_table_4', + api_key: '1234' + }), + headers: { + host: 'vizzuality.cartodb.com' + }, + method: 'GET' + }; + assert.response(server, masterRequest, RESPONSE_OK, function(err) { + assert.ok(!err, err); + + getCacheStatus(function(err, cacheStatus) { + assert.ok(!err, err); + assert.equal(cacheStatus.explain.keys, 1); + assert.equal(cacheStatus.explain.hits, 0); + + done(); + }); + }); + }); +}); diff --git a/test/acceptance/rate-limit.js b/test/acceptance/rate-limit.js new file mode 100644 index 0000000..afb3cd9 --- /dev/null +++ b/test/acceptance/rate-limit.js @@ -0,0 +1,107 @@ +'use strict'; + +require('../helper'); + +const qs = require('querystring'); +const assert = require('../support/assert'); +const redis = require('redis'); +const rateLimitMiddleware = require('../../app/middlewares/rate-limit'); +const { RATE_LIMIT_ENDPOINTS_GROUPS } = rateLimitMiddleware; + +const app = require('../../app/server'); +let server; + +let redisClient; +let keysToDelete = []; +const user = 'vizzuality'; + +var request = { + url: '/api/v1/sql?' + qs.stringify({ + q: 'SELECT * FROM untitle_table_4' + }), + headers: { + host: 'vizzuality.cartodb.com' + }, + method: 'GET' +}; + + +function setLimit(count, period, burst) { + redisClient.SELECT(8, err => { + if (err) { + return; + } + + const key = `limits:rate:store:${user}:sql:${RATE_LIMIT_ENDPOINTS_GROUPS.QUERY}`; + redisClient.rpush(key, burst); + redisClient.rpush(key, count); + redisClient.rpush(key, period); + keysToDelete.push(key); + }); +} + +function assertRequest (status, limit, remaining, reset, retry, done = null) { + assert.response( + server, + request, + { status }, + function(err, res) { + assert.ifError(err); + assert.equal(res.headers['carto-rate-limit-limit'], limit); + assert.equal(res.headers['carto-rate-limit-remaining'], remaining); + assert.equal(res.headers['carto-rate-limit-reset'], reset); + + if (retry) { + assert.equal(res.headers['retry-after'], retry); + } + + if(status === 429) { + const expectedResponse = { + error: ["You are over platform\'s limits. Please contact us to know more details"], + context: "limit", + detail: "rate-limit" + }; + + assert.deepEqual(JSON.parse(res.body), expectedResponse); + } + + if (done) { + setTimeout(done, 1000); + } + } + ); +} + +describe('rate limit', function() { + before(function() { + global.settings.ratelimits.rateLimitsEnabled = true; + global.settings.ratelimits.endpoints.query = true; + + server = app(); + redisClient = redis.createClient(global.settings.redis_port); + + const count = 1; + const period = 1; + const burst = 1; + setLimit(count, period, burst); + }); + + after(function() { + global.settings.ratelimits.rateLimitsEnabled = false; + global.settings.ratelimits.endpoints.query = false; + + keysToDelete.forEach( key => { + redisClient.del(key); + }); + }); + + 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 ); + }); + +}); diff --git a/test/acceptance/regressions.js b/test/acceptance/regressions.js new file mode 100644 index 0000000..adb27e6 --- /dev/null +++ b/test/acceptance/regressions.js @@ -0,0 +1,66 @@ +'use strict'; + +require('../helper'); + +var server = require('../../app/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) { + return { + url: '/api/v1/sql?' + qs.stringify({ + q: sqlQuery, + api_key: 1234 + }), + headers: { + host: 'vizzuality.cartodb.com' + }, + method: 'GET' + }; + } + + var responseOk = { + statusCode: 200 + }; + + assert.response(server, createRequest('CREATE TABLE "foo.bar" (a int);'), responseOk, + function(err) { + if (err) { + return done(err); + } + + assert.response(server, createRequest('INSERT INTO "foo.bar" (a) values (1), (2)'), responseOk, + function(err, res) { + if (err) { + return done(err); + } + var parsedBody = JSON.parse(res.body); + assert.equal(parsedBody.total_rows, 2); + + assert.response(server, createRequest('SELECT * FROM "foo.bar"'), responseOk, + function(err, res) { + if (err) { + return done(err); + } + + // table should not get a cache channel as it won't get invalidated + assert.ok(!res.headers.hasOwnProperty('x-cache-channel')); + var parsedBody = JSON.parse(res.body); + assert.equal(parsedBody.total_rows, 2); + assert.deepEqual(parsedBody.rows, [{ a: 1 }, { a: 2 }]); + + // delete table + assert.response(server, createRequest('DROP TABLE "foo.bar"'), responseOk, done); + } + ); + } + ); + } + ); + }); + +}); diff --git a/test/acceptance/skipfields.js b/test/acceptance/skipfields.js new file mode 100644 index 0000000..7524f22 --- /dev/null +++ b/test/acceptance/skipfields.js @@ -0,0 +1,94 @@ +'use strict'; + +require('../helper'); + +var server = require('../../app/server')(); +var assert = require('../support/assert'); +var querystring = require('querystring'); +var _ = require('underscore'); + + +describe('skipfields', function() { + + var RESPONSE_OK = { + statusCode: 200 + }; + + 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'}, + method: 'GET' + }, 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 + "'"); + } + } + 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'}, + method: 'GET' + }, 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 + "'"); + } + } + done(); + }); + }); + + // See https://github.com/CartoDB/CartoDB-SQL-API/issues/109 + 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' + }), + headers: {host: 'vizzuality.cartodb.com'}, + method: 'GET' + }, RESPONSE_OK, function(err, res) { + var parsedBody = JSON.parse(res.body); + assert.equal(_.keys(parsedBody.fields).length, 2); + assert.ok(parsedBody.fields.hasOwnProperty('a')); + assert.ok(!parsedBody.fields.hasOwnProperty('b')); + assert.ok(parsedBody.fields.hasOwnProperty('c')); + 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'}, + method: 'GET' + },{ }, 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 + "'"); + } + } + done(); + }); + }); +}); diff --git a/test/acceptance/stream-responses.js b/test/acceptance/stream-responses.js new file mode 100644 index 0000000..7a47ffa --- /dev/null +++ b/test/acceptance/stream-responses.js @@ -0,0 +1,73 @@ +'use strict'; + +require('../helper'); + +var server = require('../../app/server')(); +var assert = require('../support/assert'); +var querystring = require('querystring'); + +describe('stream-responses', function() { + + function createFailingQueryRequest(format) { + var params = { + q: "SELECT the_geom, 100/(cartodb_id - 3) cdb_ratio FROM untitle_table_4" + }; + + if (format) { + params.format = format; + } + + return { + url: "/api/v1/sql?" + querystring.stringify(params), + headers: { + host: 'vizzuality.cartodb.com' + }, + method: 'GET' + }; + } + + var okResponse = { + status: 200 + }; + + 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) { + var parsedBody = JSON.parse(res.body); + assert.equal(parsedBody.rows.length, 2); + assert.deepEqual(parsedBody.fields, { + the_geom: { type: "geometry" }, + cdb_ratio: { type: "number" } + }); + 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) { + assert.response( + server, + createFailingQueryRequest('geojson'), + okResponse, + function(err, res) { + var parsedBody = JSON.parse(res.body); + assert.equal(parsedBody.features.length, 2); + assert.deepEqual(parsedBody.error, ["division by zero"]); + done(); + } + ); + }); + + }); + +}); diff --git a/test/acceptance/surrogate-key.js b/test/acceptance/surrogate-key.js new file mode 100644 index 0000000..aac3569 --- /dev/null +++ b/test/acceptance/surrogate-key.js @@ -0,0 +1,111 @@ +'use strict'; + +require('../helper'); + +var server = require('../../app/server')(); +var assert = require('../support/assert'); +var querystring = require('querystring'); +var QueryTables = require('cartodb-query-tables'); +var _ = require('underscore'); + +describe('Surrogate-Key header', function() { + + function createGetRequest(sqlQuery) { + var query = querystring.stringify({ + q: sqlQuery, + api_key: 1234 + }); + return { + url: '/api/v1/sql?' + query, + headers: { + host: 'vizzuality.cartodb.com' + }, + method: 'GET' + }; + } + + var RESPONSE_OK = { + statusCode: 200 + }; + + function surrogateKeyHasTables(surrogateKey, expectedTables) { + + var surrogateKeys = surrogateKey.split(" "); + + var expectedSurrogateKeys = new QueryTables.DatabaseTablesEntry(expectedTables).key(); + + assert.equal(surrogateKeys.length, expectedSurrogateKeys.length); + + var tablesDiff = _.difference(surrogateKeys, expectedSurrogateKeys); + assert.equal(tablesDiff.length, 0, 'Surrogate-Key missing tables: ' + tablesDiff.join(',')); + } + + + 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)"; + + 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'} + ], done)); + }); + + 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'} + ], done)); + }); + + 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'} + ], done)); + }); + + 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'} + ], 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.ok(!res.headers.hasOwnProperty('surrogate-key'), res.headers['surrogate-key']); + 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.ok(!res.headers.hasOwnProperty('surrogate-key'), res.headers['surrogate-key']); + 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.ok(!res.headers.hasOwnProperty('surrogate-key'), res.headers['surrogate-key']); + done(); + }); + }); + +}); diff --git a/test/acceptance/system-queries.js b/test/acceptance/system-queries.js new file mode 100644 index 0000000..8526aad --- /dev/null +++ b/test/acceptance/system-queries.js @@ -0,0 +1,64 @@ +'use strict'; + +require('../helper'); + +var server = require('../../app/server')(); +var assert = require('../support/assert'); +var querystring = require('querystring'); + + +describe('system-queries', function() { + + var systemQueriesSuitesToTest = [ + { + desc: 'pg_ queries work with api_key and fail otherwise', + queries: [ + 'SELECT * FROM pg_attribute', + 'SELECT * FROM PG_attribute', + 'SELECT * FROM "pg_attribute"', + 'SELECT a.* FROM untitle_table_4 a,pg_attribute', + 'SELECT * FROM geometry_columns' + ], + api_key_works: true, + no_api_key_works: false + }, + { + desc: 'Possible false positive queries will work with api_key and without it', + queries: [ + "SELECT 'pg_'", + 'SELECT pg_attribute FROM ( select 1 as pg_attribute ) as f', + 'SELECT * FROM cpg_test' + ], + api_key_works: true, + no_api_key_works: true + } + ]; + + 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; + 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) { + queryStringParams.api_key = apiKey; + } + var request = { + headers: {host: 'vizzuality.cartodb.com'}, + method: 'GET', + url: '/api/v1/sql?' + querystring.stringify(queryStringParams) + }; + assert.response(server, request, function(err, response) { + assert.equal(response.statusCode, statusErrorCode); + done(); + }); + }); + }); + } + +}); diff --git a/test/acceptance/timeout.js b/test/acceptance/timeout.js new file mode 100644 index 0000000..99c2d7e --- /dev/null +++ b/test/acceptance/timeout.js @@ -0,0 +1,55 @@ +'use strict'; + +/** + * + * Requires the database and tables setup in config/environments/test.js to exist + * Ensure the user is present in the pgbouncer auth file too + * TODO: Add OAuth tests. + * + * To run this test, ensure that cartodb_test_user_1_db metadata exists + * in Redis for the vizzuality.cartodb.com domain + * + * SELECT 5 + * HSET rails:users:vizzuality id 1 + * HSET rails:users:vizzuality database_name cartodb_test_user_1_db + * + */ +require('../helper'); + + +var assert = require('../support/assert'); +var step = require('step'); + +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('../../app/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.js b/test/acceptance/transaction.js new file mode 100644 index 0000000..0ee54d5 --- /dev/null +++ b/test/acceptance/transaction.js @@ -0,0 +1,56 @@ +'use strict'; + +require('../helper'); + +var assert = require('../support/assert'); +var qs = require('querystring'); +var request = require('request'); + +describe('transaction', function() { + + var SERVER_PORT = 5554; + + var server; + before(function(done) { + server = require('../../app/server')(); + this.listener = server.listen(SERVER_PORT, '127.0.0.1'); + this.listener.on('listening', done); + }); + + after(function(done) { + this.listener.close(done); + }); + + var sqlRequest = request.defaults({ + headers: { host: 'vizzuality.localhost' } + }); + + 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) { + assert.ok(!err); + assert.equal(response.statusCode, 400); + + var parsedBody = JSON.parse(body); + assert.ok(parsedBody); + assert.deepEqual(parsedBody, { error: ['column "error" does not exist'] }); + + sqlRequest(requestUrl('select 1 as foo'), function (err, response, body) { + assert.ok(!err); + assert.equal(response.statusCode, 200); + + var parsedBody = JSON.parse(body); + assert.ok(parsedBody); + assert.deepEqual(parsedBody.rows, [{ foo: 1 }]); + + done(); + }); + }); + }); + +}); diff --git a/test/acceptance/x-cache-channel.js b/test/acceptance/x-cache-channel.js new file mode 100644 index 0000000..dfcc059 --- /dev/null +++ b/test/acceptance/x-cache-channel.js @@ -0,0 +1,112 @@ +'use strict'; + +require('../helper'); + +var server = require('../../app/server')(); +var assert = require('../support/assert'); +var querystring = require('querystring'); +var _ = require('underscore'); + +describe('X-Cache-Channel header', function() { + + function createGetRequest(sqlQuery) { + var query = querystring.stringify({ + q: sqlQuery, + api_key: 1234 + }); + return { + url: '/api/v1/sql?' + query, + headers: { + host: 'vizzuality.cartodb.com' + }, + method: 'GET' + }; + } + + var RESPONSE_OK = { + statusCode: 200 + }; + + function xCacheChannelHeaderHasTables(xCacheChannel, expectedTablesNames) { + var databaseAndTables = xCacheChannel.split(':'); + var databaseName = databaseAndTables[0]; + + assert.equal(databaseName, 'cartodb_test_user_1_db'); + + var headerTableNames = databaseAndTables[1].split(','); + assert.equal(headerTableNames.length, expectedTablesNames.length); + + var tablesDiff = _.difference(expectedTablesNames, headerTableNames); + assert.equal(tablesDiff.length, 0, 'X-Cache-Channel header missing tables: ' + tablesDiff.join(',')); + } + + 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)"; + + assert.response(server, createGetRequest(sql), RESPONSE_OK, tableNamesInCacheChannelHeader([ + 'public.private_table', + 'public.untitle_table_4' + ], done)); + }); + + 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', + 'public.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;"; + + assert.response(server, createGetRequest(sql), RESPONSE_OK, tableNamesInCacheChannelHeader([ + 'public.private_table', + 'public.untitle_table_4' + ], done)); + }); + + 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) { + var sql = "SELECT format('%s', 'wadus')"; + 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) { + var sql = "SELECT CDB_QueryTablesText('select * from untitle_table_4')"; + 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) { + var sql = "SELECT 'wadus'::text"; + 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/helper.js b/test/helper.js new file mode 100644 index 0000000..a19de98 --- /dev/null +++ b/test/helper.js @@ -0,0 +1,4 @@ +'use strict'; + +global.settings = require('../config/environments/test'); +process.env.NODE_ENV = 'test'; diff --git a/test/integration/batch/job-queue.test.js b/test/integration/batch/job-queue.test.js new file mode 100644 index 0000000..892a54a --- /dev/null +++ b/test/integration/batch/job-queue.test.js @@ -0,0 +1,179 @@ +'use strict'; + +require('../../helper'); +var assert = require('../../support/assert'); +var redisUtils = require('../../support/redis_utils'); + +var metadataBackend = require('cartodb-redis')({ pool: redisUtils.getPool() }); +var JobPublisher = require('../../../batch/pubsub/job-publisher'); +var JobQueue = require('../../../batch/job_queue'); + +var JobBackend = require('../../../batch/job_backend'); +var JobService = require('../../../batch/job_service'); +var JobCanceller = require('../../../batch/job_canceller'); +var metadataBackend = require('cartodb-redis')({ pool: redisUtils.getPool() }); + +describe('job queue', function () { + var pool = redisUtils.getPool(); + var jobPublisher = new JobPublisher(pool); + var jobQueue = new JobQueue(metadataBackend, jobPublisher); + var jobBackend = new JobBackend(metadataBackend, jobQueue); + var jobCanceller = new JobCanceller(); + var jobService = new JobService(jobBackend, jobCanceller); + + var userA = 'userA'; + var userB = 'userB'; + + beforeEach(function () { + this.jobQueue = new JobQueue(metadataBackend, jobPublisher); + }); + + afterEach(function (done) { + redisUtils.clean('batch:*', done); + }); + + it('should find queues for one user', function (done) { + var self = this; + + this.jobQueue.enqueue(userA, 'wadus-wadus-wadus-wadus', function(err) { + if (err) { + return done(err); + } + + self.jobQueue.scanQueues(function (err, queues) { + assert.ifError(err); + assert.equal(queues.length, 1); + assert.equal(queues[0], userA); + return done(); + }); + }); + }); + + it('should find queues for more than one user', function (done) { + var self = this; + + 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) { + if (err) { + return done(err); + } + + self.jobQueue.scanQueues(function (err, queues) { + assert.ifError(err); + assert.equal(queues.length, 2); + assert.ok(queues[0] === userA || queues[0] === userB); + assert.ok(queues[1] === userA || queues[1] === userB); + + return 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) { + assert.ok(!err, err); + self.jobQueue.scanQueues(function (err, queues) { + assert.ok(!err, err); + + assert.equal(queues.length, 1); + assert.equal(queues[0], userA); + + return done(); + }); + }); + }); + + it('.scanQueues() should feed queue index', function (done) { + var self = this; + + var data = { + user: 'vizzuality', + query: 'select 1 as cartodb_id', + host: 'localhost' + }; + + jobService.create(data, function (err) { + if (err) { + return done(err); + } + + self.jobQueue.scanQueues(function (err, queuesFromScan) { + if (err) { + return done(err); + } + + assert.equal(queuesFromScan.length, 1); + assert.ok(queuesFromScan.indexOf(data.user) >= 0); + + self.jobQueue.getQueues(function (err, queuesFromIndex) { + if (err) { + done(err); + } + + assert.equal(queuesFromIndex.length, 1); + assert.ok(queuesFromIndex.indexOf(data.user) >= 0); + + redisUtils.clean('batch:*', done); + }); + + }); + }); + }); + + it('.scanQueues() should feed queue index with two users', function (done) { + var self = this; + + var jobVizzuality = { + user: 'vizzuality', + query: 'select 1 as cartodb_id', + host: 'localhost' + }; + + var jobWadus = { + user: 'wadus', + query: 'select 1 as cartodb_id', + host: 'localhost' + }; + + jobService.create(jobVizzuality, function (err) { + if (err) { + return done(err); + } + + jobService.create(jobWadus, function (err) { + if (err) { + return done(err); + } + + self.jobQueue.scanQueues(function (err, queuesFromScan) { + if (err) { + return done(err); + } + + assert.equal(queuesFromScan.length, 2); + assert.ok(queuesFromScan.indexOf(jobVizzuality.user) >= 0); + assert.ok(queuesFromScan.indexOf(jobWadus.user) >= 0); + + self.jobQueue.getQueues(function (err, queuesFromIndex) { + if (err) { + done(err); + } + + assert.equal(queuesFromIndex.length, 2); + assert.ok(queuesFromIndex.indexOf(jobVizzuality.user) >= 0); + assert.ok(queuesFromIndex.indexOf(jobWadus.user) >= 0); + + redisUtils.clean('batch:*', done); + }); + + }); + }); + }); + }); +}); diff --git a/test/integration/batch/job_backend.test.js b/test/integration/batch/job_backend.test.js new file mode 100644 index 0000000..75088e1 --- /dev/null +++ b/test/integration/batch/job_backend.test.js @@ -0,0 +1,219 @@ +'use strict'; + +require('../../helper'); + +var BATCH_SOURCE = '../../../batch/'; + +var assert = require('../../support/assert'); +var redisUtils = require('../../support/redis_utils'); + +var JobQueue = require(BATCH_SOURCE + 'job_queue'); +var JobBackend = require(BATCH_SOURCE + 'job_backend'); +var JobPublisher = require(BATCH_SOURCE + 'pubsub/job-publisher'); +var JobFactory = require(BATCH_SOURCE + 'models/job_factory'); +var jobStatus = require(BATCH_SOURCE + 'job_status'); + +var metadataBackend = require('cartodb-redis')({ pool: redisUtils.getPool() }); +var jobPublisher = new JobPublisher(redisUtils.getPool()); +var jobQueue = new JobQueue(metadataBackend, jobPublisher); + +var queue = require('queue-async'); + +var USER = 'vizzuality'; +var QUERY = 'select pg_sleep(0)'; +var HOST = 'localhost'; +var JOB = { + user: USER, + query: QUERY, + host: HOST +}; + +function createWadusJob() { + return JobFactory.create(JSON.parse(JSON.stringify(JOB))); +} + +describe('job backend', function() { + var jobBackend = new JobBackend(metadataBackend, jobQueue); + + after(function (done) { + redisUtils.clean('batch:*', done); + }); + + it('.create() should persist a job', function (done) { + var job = createWadusJob(); + + jobBackend.create(job.data, function (err, jobCreated) { + if (err) { + return done(err); + } + + assert.ok(jobCreated.job_id); + assert.equal(jobCreated.status, jobStatus.PENDING); + done(); + }); + }); + + it('.create() should return error', function (done) { + var job = createWadusJob(); + + delete job.data.job_id; + + jobBackend.create(job.data, function (err) { + assert.ok(err); + assert.equal(err.name, 'NotFoundError'); + assert.equal(err.message, 'Job with id undefined not found'); + done(); + }); + }); + + it('.get() should return a job with the given id', function (done) { + var jobData = createWadusJob(); + + jobBackend.create(jobData.data, function (err, jobCreated) { + if (err) { + return done(err); + } + + assert.ok(jobCreated.job_id); + + jobBackend.get(jobCreated.job_id, function (err, job) { + if (err) { + return done(err); + } + + assert.equal(job.job_id, jobCreated.job_id); + assert.equal(job.user, jobData.data.user); + assert.equal(job.query, jobData.data.query); + assert.equal(job.host, jobData.data.host); + assert.equal(job.status, jobStatus.PENDING); + done(); + }); + }); + }); + + it('.update() should update an existent job', function (done) { + var job = createWadusJob(); + + jobBackend.create(job.data, function (err, jobCreated) { + if (err) { + return done(err); + } + + jobCreated.query = 'select pg_sleep(1)'; + + var job = JobFactory.create(jobCreated); + + jobBackend.update(job.data, function (err, jobUpdated) { + if (err) { + return done(err); + } + + assert.equal(jobUpdated.query, 'select pg_sleep(1)'); + done(); + }); + }); + }); + + it('.update() should return error when updates a nonexistent job', function (done) { + var job = createWadusJob(); + + jobBackend.update(job.data, function (err) { + assert.ok(err, err); + assert.equal(err.name, 'NotFoundError'); + assert.equal(err.message, 'Job with id ' + job.data.job_id + ' not found'); + done(); + }); + }); + + it('.save() should save a job', function (done) { + var job = createWadusJob(); + + jobBackend.save(job.data, function (err, jobSaved) { + if (err) { + return done(err); + } + + assert.ok(jobSaved.job_id); + + assert.equal(jobSaved.user, job.data.user); + assert.equal(jobSaved.query, job.data.query); + assert.equal(jobSaved.host, job.data.host); + assert.equal(jobSaved.status, jobStatus.PENDING); + done(); + }); + }); + + it('.addWorkInProgressJob() should add current job to user and host lists', function (done) { + var job = createWadusJob(); + + jobBackend.addWorkInProgressJob(job.data.user, job.data.job_id, function (err) { + if (err) { + return done(err); + } + done(); + }); + }); + + it('.listWorkInProgressJobByUser() should retrieve WIP jobs of given user', function (done) { + var testStepsQueue = queue(1); + + testStepsQueue.defer(redisUtils.clean, 'batch:wip:user:*'); + testStepsQueue.defer(jobBackend.addWorkInProgressJob.bind(jobBackend), 'vizzuality', 'wadus'); + testStepsQueue.defer(jobBackend.listWorkInProgressJobByUser.bind(jobBackend), 'vizzuality'); + + testStepsQueue.awaitAll(function (err, results) { + if (err) { + return done(err); + } + assert.deepEqual(results[2], ['wadus']); + done(); + }); + }); + + it('.listWorkInProgressJobs() should retrieve WIP users', function (done) { + var jobs = [{ user: 'userA', id: 'jobId1' }, { user: 'userA', id: 'jobId2' }, { user: 'userB', id: 'jobId3' }]; + + var testStepsQueue = queue(1); + + jobs.forEach(function (job) { + testStepsQueue.defer(jobBackend.addWorkInProgressJob.bind(jobBackend), job.user, job.id); + }); + + testStepsQueue.awaitAll(function (err) { + if (err) { + done(err); + } + + jobBackend.listWorkInProgressJobs(function (err, users) { + if (err) { + return done(err); + } + + assert.ok(users.userA); + assert.deepEqual(users.userA, [ 'jobId1', 'jobId2' ]); + assert.ok(users.userB); + assert.deepEqual(users.userB, [ 'jobId3' ]); + done(); + }); + + }); + }); + + it('.clearWorkInProgressJob() should remove job from work in progress list', function (done) { + var job = createWadusJob(); + + jobBackend.addWorkInProgressJob(job.data.user, job.data.job_id, function (err) { + if (err) { + return done(err); + } + + jobBackend.clearWorkInProgressJob(job.data.user, job.data.job_id, function (err) { + if (err) { + return done(err); + } + + done(); + }); + }); + }); +}); diff --git a/test/integration/batch/job_canceller.test.js b/test/integration/batch/job_canceller.test.js new file mode 100644 index 0000000..c9ad89f --- /dev/null +++ b/test/integration/batch/job_canceller.test.js @@ -0,0 +1,119 @@ +'use strict'; + +require('../../helper'); + +var BATCH_SOURCE = '../../../batch/'; + +var assert = require('../../support/assert'); +var redisUtils = require('../../support/redis_utils'); + +var JobQueue = require(BATCH_SOURCE + 'job_queue'); +var JobBackend = require(BATCH_SOURCE + 'job_backend'); +var JobPublisher = require(BATCH_SOURCE + 'pubsub/job-publisher'); +var jobStatus = require(BATCH_SOURCE + 'job_status'); +var JobCanceller = require(BATCH_SOURCE + 'job_canceller'); +var PSQL = require('cartodb-psql'); + +var metadataBackend = require('cartodb-redis')({ pool: redisUtils.getPool() }); +var jobPublisher = new JobPublisher(redisUtils.getPool()); +var jobQueue = new JobQueue(metadataBackend, jobPublisher); +var jobBackend = new JobBackend(metadataBackend, jobQueue); +var JobFactory = require(BATCH_SOURCE + 'models/job_factory'); + +var USER = 'vizzuality'; +var QUERY = 'select pg_sleep(0)'; +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) { + var job_id = job.job_id; + var sql = job.query; + + job.status = jobStatus.RUNNING; + + jobBackend.update(job, function (err) { + if (err) { + return callback(err); + } + + const dbConfiguration = { + host: job.host, + port: job.port, + dbname: job.dbname, + user: job.dbuser, + pass: job.pass, + }; + + const pg = new PSQL(dbConfiguration); + + sql = '/* ' + job_id + ' */ ' + sql; + + pg.eventedQuery(sql, function (err, query) { + if (err) { + return callback(err); + } + + callback(null, query); + }); + }); +} + +function createWadusJob(query) { + query = query || QUERY; + return JobFactory.create(JSON.parse(JSON.stringify({ + user: USER, + query: query, + host: HOST, + dbname: 'cartodb_test_user_1_db', + dbuser: 'test_cartodb_user_1', + port: 5432, + pass: 'test_cartodb_user_1_pass', + }))); +} + +describe('job canceller', function() { + var jobCanceller = new JobCanceller(); + + after(function (done) { + redisUtils.clean('batch:*', done); + }); + + it('.cancel() should cancel a job', function (done) { + var job = createWadusJob('select pg_sleep(1)'); + + jobBackend.create(job.data, function (err, jobCreated) { + if (err) { + return done(err); + } + + assert.equal(job.data.job_id, jobCreated.job_id); + + runQueryHelper(job.data, function (err) { + if (err) { + return done(err); + } + + jobCanceller.cancel(job, function (err) { + if (err) { + return done(err); + } + + done(); + }); + }); + }); + }); + + it('.cancel() a non running job should not return an error', function (done) { + var job = createWadusJob(); + + jobCanceller.cancel(job, function (err) { + if (err) { + return done(err); + } + + done(); + }); + }); +}); diff --git a/test/integration/batch/job_publisher.test.js b/test/integration/batch/job_publisher.test.js new file mode 100644 index 0000000..50c9f5c --- /dev/null +++ b/test/integration/batch/job_publisher.test.js @@ -0,0 +1,39 @@ +'use strict'; + +require('../../helper'); + +var BATCH_SOURCE = '../../../batch/'; + +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() { + var jobPublisher = new JobPublisher(redisUtils.getPool()); + + it('.publish() should publish in job channel', function (done) { + redisUtils.getPool().acquire(Channel.DB, function (err, client) { + if (err) { + return done(err); + } + + client.subscribe(Channel.NAME); + + client.on('message', function (channel, host) { + assert.equal(host, HOST); + assert.equal(channel, Channel.NAME); + client.unsubscribe(Channel.NAME); + done(); + }); + + jobPublisher.publish(HOST); + }); + }); + +}); diff --git a/test/integration/batch/job_runner.test.js b/test/integration/batch/job_runner.test.js new file mode 100644 index 0000000..ef8e3dd --- /dev/null +++ b/test/integration/batch/job_runner.test.js @@ -0,0 +1,80 @@ +'use strict'; + +require('../../helper'); + +var BATCH_SOURCE = '../../../batch/'; + +var assert = require('../../support/assert'); +var redisUtils = require('../../support/redis_utils'); + +var JobQueue = require(BATCH_SOURCE + 'job_queue'); +var JobBackend = require(BATCH_SOURCE + 'job_backend'); +var JobPublisher = require(BATCH_SOURCE + 'pubsub/job-publisher'); +var jobStatus = require(BATCH_SOURCE + 'job_status'); +var UserDatabaseMetadataService = require(BATCH_SOURCE + 'user_database_metadata_service'); +var JobCanceller = require(BATCH_SOURCE + 'job_canceller'); +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 jobPublisher = new JobPublisher(redisUtils.getPool()); +var jobQueue = new JobQueue(metadataBackend, jobPublisher); +var jobBackend = new JobBackend(metadataBackend, jobQueue); +var userDatabaseMetadataService = new UserDatabaseMetadataService(metadataBackend); +var jobCanceller = new JobCanceller(); +var jobService = new JobService(jobBackend, jobCanceller); +var queryRunner = new QueryRunner(userDatabaseMetadataService); +var StatsD = require('node-statsd').StatsD; +var statsdClient = new StatsD(global.settings.statsd); + +var USER = 'vizzuality'; +var QUERY = 'select pg_sleep(0)'; +var HOST = 'localhost'; +var JOB = { + user: USER, + query: QUERY, + host: HOST, + dbname: 'cartodb_test_user_1_db', + dbuser: 'test_cartodb_user_1', + port: 5432, + pass: 'test_cartodb_user_1_pass', +}; + +describe('job runner', function() { + var jobRunner = new JobRunner(jobService, jobQueue, queryRunner, metadataBackend, statsdClient); + + after(function (done) { + redisUtils.clean('batch:*', function() { + redisUtils.clean('limits:batch:*', done); + }); + }); + + it('.run() should run a job', function (done) { + jobService.create(JOB, function (err, job) { + if (err) { + return done(err); + } + + jobRunner.run(job.data.job_id, function (err, job) { + if (err) { + return done(err); + } + + assert.equal(job.data.status, jobStatus.DONE); + done(); + }); + }); + }); + + it('.run() should return a job not found error', function (done) { + jobRunner.run('wadus_job_id', function (err) { + assert.ok(err, err); + assert.equal(err.name, 'NotFoundError'); + assert.equal(err.message, 'Job with id wadus_job_id not found'); + done(); + }); + }); + +}); diff --git a/test/integration/batch/job_service.test.js b/test/integration/batch/job_service.test.js new file mode 100644 index 0000000..2b9290f --- /dev/null +++ b/test/integration/batch/job_service.test.js @@ -0,0 +1,205 @@ +'use strict'; + +require('../../helper'); + +var BATCH_SOURCE = '../../../batch/'; + +var assert = require('../../support/assert'); +var redisUtils = require('../../support/redis_utils'); + +var JobQueue = require(BATCH_SOURCE + 'job_queue'); +var JobBackend = require(BATCH_SOURCE + 'job_backend'); +var JobPublisher = require(BATCH_SOURCE + 'pubsub/job-publisher'); +var jobStatus = require(BATCH_SOURCE + 'job_status'); +var JobCanceller = require(BATCH_SOURCE + 'job_canceller'); +var JobService = require(BATCH_SOURCE + 'job_service'); +var PSQL = require('cartodb-psql'); + +var metadataBackend = require('cartodb-redis')({ pool: redisUtils.getPool() }); +var jobPublisher = new JobPublisher(redisUtils.getPool()); +var jobQueue = new JobQueue(metadataBackend, jobPublisher); +var jobBackend = new JobBackend(metadataBackend, jobQueue); +var jobCanceller = new JobCanceller(); + +var USER = 'vizzuality'; +var QUERY = 'select pg_sleep(0)'; +var HOST = 'localhost'; +var JOB = { + user: USER, + query: QUERY, + host: HOST, + dbname: 'cartodb_test_user_1_db', + dbuser: 'test_cartodb_user_1', + port: 5432, + pass: 'test_cartodb_user_1_pass', + +}; + +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) { + var job_id = job.job_id; + var sql = job.query; + + job.status = jobStatus.RUNNING; + + jobBackend.update(job, function (err) { + if (err) { + return callback(err); + } + + const dbConfiguration = { + host: job.host, + port: job.port, + dbname: job.dbname, + user: job.dbuser, + pass: job.pass, + }; + + var pg = new PSQL(dbConfiguration); + + sql = '/* ' + job_id + ' */ ' + sql; + + pg.eventedQuery(sql, function (err, query) { + if (err) { + return callback(err); + } + + callback(null, query); + }); + }); +} + +describe('job service', function() { + var jobService = new JobService(jobBackend, jobCanceller); + + after(function (done) { + redisUtils.clean('batch:*', done); + }); + + it('.get() should return a job', function (done) { + jobService.create(createWadusDataJob(), function (err, jobCreated) { + if (err) { + return done(err); + } + + jobService.get(jobCreated.data.job_id, function (err, job) { + if (err) { + return done(err); + } + + assert.equal(job.data.job_id, jobCreated.data.job_id); + done(); + }); + }); + }); + + it('.get() should return a not found error', function (done) { + jobService.get('wadus_job_id', function (err) { + assert.ok(err); + assert.equal(err.message, 'Job with id wadus_job_id not found'); + done(); + }); + }); + + it('.create() should persist a job', function (done) { + jobService.create(createWadusDataJob(), function (err, jobCreated) { + if (err) { + return done(err); + } + + assert.ok(jobCreated.data.job_id); + assert.equal(jobCreated.data.status, jobStatus.PENDING); + done(); + }); + }); + + it('.create() should return error with invalid job data', function (done) { + var job = createWadusDataJob(); + + delete job.query; + + jobService.create(job, function (err) { + assert.ok(err); + assert.equal(err.message, 'You must indicate a valid SQL'); + done(); + }); + }); + + it('.cancel() should cancel a running job', function (done) { + var job = createWadusDataJob(); + job.query = 'select pg_sleep(3)'; + + jobService.create(job, function (err, job) { + if (err) { + return done(err); + } + + runQueryHelper(job.data, function (err) { + if (err) { + return done(err); + } + + jobService.cancel(job.data.job_id, function (err, jobCancelled) { + if (err) { + return done(err); + } + + assert.equal(jobCancelled.data.job_id, job.data.job_id); + assert.equal(jobCancelled.data.status, jobStatus.CANCELLED); + done(); + }); + }); + }); + }); + + it('.cancel() should return a job not found error', function (done) { + jobService.cancel('wadus_job_id', function (err) { + assert.ok(err, err); + assert.equal(err.name, 'NotFoundError'); + assert.equal(err.message, 'Job with id wadus_job_id not found'); + done(); + }); + }); + + it('.drain() should draing a running job', function (done) { + var job = createWadusDataJob(); + job.query = 'select pg_sleep(3)'; + + jobService.create(job, function (err, job) { + if (err) { + return done(err); + } + + runQueryHelper(job.data, function (err) { + if (err) { + return done(err); + } + + jobService.drain(job.data.job_id, function (err, jobDrained) { + if (err) { + return done(err); + } + + assert.equal(jobDrained.job_id, job.data.job_id); + assert.equal(jobDrained.status, jobStatus.PENDING); + done(); + }); + }); + }); + }); + + it('.drain() should return a job not found error', function (done) { + jobService.drain('wadus_job_id', function (err) { + assert.ok(err, err); + assert.equal(err.name, 'NotFoundError'); + assert.equal(err.message, 'Job with id wadus_job_id not found'); + done(); + }); + }); + +}); diff --git a/test/integration/batch/locker.js b/test/integration/batch/locker.js new file mode 100644 index 0000000..f1f2116 --- /dev/null +++ b/test/integration/batch/locker.js @@ -0,0 +1,60 @@ +'use strict'; + +require('../../helper'); + +var assert = require('../../support/assert'); +var redisUtils = require('../../support/redis_utils'); +var Locker = require('../../../batch/leader/locker'); + +describe('locker', function() { + var host = 'localhost'; + + var TTL = 500; + + var config = { ttl: TTL, pool: redisUtils.getPool() }; + + 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) { + if (err) { + return done(err); + } + assert.ok(lock); + + // others can't lock on same host + lockerB.lock(host, function(err) { + assert.ok(err); + assert.equal(err.name, 'LockError'); + + lockerA.unlock(host, function(err) { + assert.ok(!err); + // others can lock after unlock + lockerB.lock(host, function(err, lock2) { + assert.ok(!err); + assert.ok(lock2); + lockerB.unlock(host, done); + }); + }); + }); + }); + }); + + 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) { + if (err) { + return done(err); + } + setTimeout(function() { + lockerB.lock(host, function(err) { + assert.ok(err); + + assert.ok(lock); + lockerA.unlock(host, done); + }); + }, 2 * TTL); + }); + }); +}); diff --git a/test/integration/batch/scheduler.js b/test/integration/batch/scheduler.js new file mode 100644 index 0000000..512ef28 --- /dev/null +++ b/test/integration/batch/scheduler.js @@ -0,0 +1,204 @@ +'use strict'; + +require('../../helper'); +var debug = require('../../../batch/util/debug')('scheduler-test'); +var assert = require('../../support/assert'); +var Scheduler = require('../../../batch/scheduler/scheduler'); +var FixedCapacity = require('../../../batch/scheduler/capacity/fixed'); + +describe('scheduler', function() { + + var USER_FINISHED = true; + + var USER_A = 'userA'; + var USER_B = 'userB'; + var USER_C = 'userC'; + + function TaskRunner(userTasks) { + this.results = []; + this.userTasks = userTasks; + } + + TaskRunner.prototype.run = function(user, callback) { + this.results.push(user); + this.userTasks[user]--; + setTimeout(function() { + return callback(null, this.userTasks[user] === 0); + }.bind(this), 50); + }; + + function ManualTaskRunner() { + this.userTasks = {}; + } + + 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) { + if (this.userTasks.hasOwnProperty(user)) { + var cb = this.userTasks[user].shift(); + if (cb) { + return cb(null, isDone); + } + } + }; + + + // simulate one by one or infinity capacity + var capacities = [new FixedCapacity(1), new FixedCapacity(2), new FixedCapacity(Infinity)]; + + capacities.forEach(function(capacity) { + + it('regression #1', function (done) { + var taskRunner = new TaskRunner({ + userA: 2, + userB: 2 + }); + var scheduler = new Scheduler(capacity, taskRunner); + scheduler.add(USER_A); + scheduler.add(USER_B); + + scheduler.on('done', function() { + var results = taskRunner.results; + + assert.equal(results.length, 4); + + assert.equal(results[0], USER_A); + assert.equal(results[1], USER_B); + assert.equal(results[2], USER_A); + assert.equal(results[3], USER_B); + + return done(); + }); + + scheduler.schedule(); + }); + + it('regression #2: it should restart task after it was done but got re-scheduled', function (done) { + var taskRunner = new ManualTaskRunner(); + var scheduler = new Scheduler(capacity, taskRunner); + debug('Adding users A and B'); + scheduler.add(USER_A); + scheduler.add(USER_B); + + var acquiredUsers = []; + + scheduler.on('done', function() { + debug('Users %j', acquiredUsers); + assert.equal(acquiredUsers[0], USER_A); + assert.equal(acquiredUsers[1], USER_B); + assert.equal(acquiredUsers[2], USER_A); + assert.equal(acquiredUsers[3], USER_B); + + assert.equal(acquiredUsers.length, 4); + + return done(); + }); + + scheduler.on('acquired', function(user) { + debug('Acquired user %s', user); + acquiredUsers.push(user); + }); + + scheduler.schedule(); + + debug('User A will be mark as DONE'); + taskRunner.dispatch(USER_A, USER_FINISHED); + + debug('User B should be running'); + debug('User A submit a new task'); + scheduler.add(USER_A); + + debug('User B will get another task to run'); + taskRunner.dispatch(USER_B); + + debug('User A should start working on this new task'); + taskRunner.dispatch(USER_A, USER_FINISHED); + taskRunner.dispatch(USER_B, USER_FINISHED); + }); + + it('should run tasks', function (done) { + var taskRunner = new TaskRunner({ + userA: 1 + }); + var scheduler = new Scheduler(capacity, taskRunner); + scheduler.add(USER_A); + + scheduler.on('done', function() { + var results = taskRunner.results; + + assert.equal(results.length, 1); + + assert.equal(results[0], USER_A); + + return done(); + }); + + scheduler.schedule(); + }); + + + it('should run tasks for different users', function (done) { + var taskRunner = new TaskRunner({ + userA: 1, + userB: 1, + userC: 1 + }); + var scheduler = new Scheduler(capacity, taskRunner); + scheduler.add(USER_A); + scheduler.add(USER_B); + scheduler.add(USER_C); + + scheduler.on('done', function() { + var results = taskRunner.results; + + assert.equal(results.length, 3); + + assert.equal(results[0], USER_A); + assert.equal(results[1], USER_B); + assert.equal(results[2], USER_C); + + return done(); + }); + + scheduler.schedule(); + }); + + it('should be fair when scheduling tasks', function (done) { + var taskRunner = new TaskRunner({ + userA: 3, + userB: 2, + userC: 1 + }); + + var scheduler = new Scheduler(capacity, taskRunner); + scheduler.add(USER_A); + scheduler.add(USER_A); + scheduler.add(USER_A); + scheduler.add(USER_B); + scheduler.add(USER_B); + scheduler.add(USER_C); + + scheduler.on('done', function() { + var results = taskRunner.results; + + assert.equal(results.length, 6); + + assert.equal(results[0], USER_A); + assert.equal(results[1], USER_B); + assert.equal(results[2], USER_C); + assert.equal(results[3], USER_A); + assert.equal(results[4], USER_B); + assert.equal(results[5], USER_A); + + return done(); + }); + + scheduler.schedule(); + }); + }); +}); diff --git a/test/integration/stream_copy.test.js b/test/integration/stream_copy.test.js new file mode 100644 index 0000000..0e23566 --- /dev/null +++ b/test/integration/stream_copy.test.js @@ -0,0 +1,21 @@ +'use strict'; + +require('../helper'); +const assert = require('assert'); + +const StreamCopy = require('../../app/services/stream_copy'); + +describe('stream copy', function() { + it('uses batch api port', function(done) { + const userDbParams = { + dbname: 'cartodb_test_user_1_db', + dbuser: 'test_cartodb_user_1', + pass: 'test_cartodb_user_1_pass', + port: 'invalid_port' + }; + const sql = 'COPY dummy_table FROM STDIN'; + const streamCopy = new StreamCopy(sql, userDbParams); + assert.equal(streamCopy.pg.dbopts.port, global.settings.db_batch_port); + done(); + }); +}); diff --git a/test/integration/utils/table_cache_factory.test.js b/test/integration/utils/table_cache_factory.test.js new file mode 100644 index 0000000..546152c --- /dev/null +++ b/test/integration/utils/table_cache_factory.test.js @@ -0,0 +1,41 @@ +'use strict'; + +require('../../helper'); +var assert = require('assert'); +var LRU = require('lru-cache'); +var NoCache = require('../../../app/utils/no_cache'); + +var TableCacheFactory = require('../../../app/utils/table_cache_factory'); +var factory = new TableCacheFactory(); + +describe('TableCacheFactory', function() { + + it('returns a NoCache by default', function() { + var tableCache = factory.build({}); + assert(tableCache instanceof NoCache); + }); + + it('returns a NoCache if it is disabled in settings', function() { + var tableCache = factory.build({tableCacheEnabled: false}); + assert(tableCache instanceof NoCache); + }); + + it('returns an LRU if enabled in settings, with its default settings', function() { + var tableCache = factory.build({tableCacheEnabled: true}); + assert(tableCache instanceof LRU); + assert.equal(tableCache._max, 8192); + assert.equal(tableCache._maxAge, 1000*60*10); + }); + + it('returns an LRU if enabled in settings, with the passed settings', function() { + var tableCache = factory.build({ + tableCacheEnabled: true, + tableCacheMax: 42, + tableCacheMaxAge: 1000 + }); + assert(tableCache instanceof LRU); + assert.equal(tableCache._max, 42); + assert.equal(tableCache._maxAge, 1000); + }); + +}); diff --git a/test/prepare_db.sh b/test/prepare_db.sh new file mode 100755 index 0000000..a162dad --- /dev/null +++ b/test/prepare_db.sh @@ -0,0 +1,250 @@ +#!/bin/sh + +# this script prepare database and redis instance to run acceptance test +# +# NOTE: assumes existance of a "template_postgis" loaded with +# compatible version of postgis (legacy.sql included) + +PREPARE_REDIS=yes +PREPARE_PGSQL=yes +OFFLINE=no + +while [ -n "$1" ]; do + if test "$1" = "--skip-pg"; then + PREPARE_PGSQL=no + shift; continue + elif test "$1" = "--skip-redis"; then + PREPARE_REDIS=no + shift; continue + elif test "$1" = "--offline"; then + OFFLINE=yes + shift; continue + fi +done + +die() { + msg=$1 + echo "${msg}" >&2 + exit 1 +} + +# This is where postgresql connection parameters are read from +TESTENV=../config/environments/test.js + +# Extract postgres configuration +PGHOST=`node -e "console.log(require('${TESTENV}').db_host || '')"` +echo "PGHOST: [$PGHOST]" +PGPORT=`node -e "console.log(require('${TESTENV}').db_port || '')"` +echo "PGPORT: [$PGPORT]" + +PUBLICUSER=`node -e "console.log(require('${TESTENV}').db_pubuser || 'xxx')"` +PUBLICPASS=`node -e "console.log(require('${TESTENV}').db_pubuser_pass || 'xxx')"` +echo "PUBLICUSER: [${PUBLICUSER}]" +echo "PUBLICPASS: [${PUBLICPASS}]" + + +TESTUSERID=1 + +TESTUSER=`node -e "console.log(require('${TESTENV}').db_user || '')"` +if test -z "$TESTUSER"; then + echo "Missing db_user from ${TESTENV}" >&2 + exit 1 +fi +TESTUSER=`echo ${TESTUSER} | sed "s/<%= user_id %>/${TESTUSERID}/"` +echo "TESTUSER: [${TESTUSER}]" + +TESTPASS=`node -e "console.log(require('${TESTENV}').db_user_pass || '')"` +TESTPASS=`echo ${TESTPASS} | sed "s/<%= user_id %>/${TESTUSERID}/"` +echo "TESTPASS: [${TESTPASS}]" + +TEST_DB=`node -e "console.log(require('${TESTENV}').db_base_name || '')"` +if test -z "$TEST_DB"; then + echo "Missing db_base_name from ${TESTENV}" >&2 + exit 1 +fi +TEST_DB=`echo ${TEST_DB} | sed "s/<%= user_id %>/${TESTUSERID}/"` + +export PGHOST PGPORT + +if test x"$PREPARE_PGSQL" = xyes; then + + echo "preparing postgres..." + echo "PostgreSQL server version: `psql -A -t -c 'select version()'`" + echo "PAUSE; RESUME;" | psql -p 6432 pgbouncer # make sure there are no connections pgbouncer -> test_db + dropdb ${TEST_DB} # 2> /dev/null # error expected if doesn't exist, but not otherwise + createdb -Ttemplate_postgis -EUTF8 ${TEST_DB} || die "Could not create test database" + psql -c 'CREATE EXTENSION IF NOT EXISTS "uuid-ossp";' ${TEST_DB} + psql -c "CREATE EXTENSION IF NOT EXISTS plpythonu;" ${TEST_DB} + + LOCAL_SQL_SCRIPTS='test populated_places_simple_reduced py_sleep quota_mock' + REMOTE_SQL_SCRIPTS='CDB_QueryStatements CDB_QueryTables CDB_CartodbfyTable CDB_TableMetadata CDB_ForeignTable CDB_UserTables CDB_ColumnNames CDB_ZoomFromScale CDB_OverviewsSupport CDB_Overviews' + + if test x"$OFFLINE" = xno; then + CURL_ARGS="" + for i in ${REMOTE_SQL_SCRIPTS} + do + CURL_ARGS="${CURL_ARGS}\"https://github.com/CartoDB/cartodb-postgresql/raw/master/scripts-available/$i.sql\" -o support/sql/$i.sql " + done + echo "Downloading and updating: ${REMOTE_SQL_SCRIPTS}" + echo ${CURL_ARGS} | xargs curl -L -s + fi + + PG_PARALLEL=$(pg_config --version | (awk '{$2*=1000; if ($2 >= 9600) print 1; else print 0;}' 2> /dev/null || echo 0)) + + psql -c "CREATE EXTENSION IF NOT EXISTS plpythonu;" ${TEST_DB} + ALL_SQL_SCRIPTS="${REMOTE_SQL_SCRIPTS} ${LOCAL_SQL_SCRIPTS}" + for i in ${ALL_SQL_SCRIPTS} + do + # Strip PARALLEL labels for PostgreSQL releases before 9.6 + if [ $PG_PARALLEL -eq 0 ]; then + TMPFILE=$(mktemp /tmp/$(basename $0).XXXXXXXX) + sed -e 's/PARALLEL \= [A-Z]*,/''/g' \ + -e 's/PARALLEL [A-Z]*/''/g' support/sql/${i}.sql > $TMPFILE + mv $TMPFILE support/sql/${i}.sql + fi + cat support/sql/${i}.sql | + sed -e 's/cartodb\./public./g' -e "s/''cartodb''/''public''/g" | + sed "s/:PUBLICUSER/${PUBLICUSER}/" | + sed "s/:PUBLICPASS/${PUBLICPASS}/" | + sed "s/:TESTUSER/${TESTUSER}/" | + sed "s/:TESTPASS/${TESTPASS}/" | + psql -q -v ON_ERROR_STOP=1 ${TEST_DB} > /dev/null || exit 1 + done + +fi + +if test x"$PREPARE_REDIS" = xyes; then + + REDIS_HOST=`node -e "console.log(require('${TESTENV}').redis_host || '127.0.0.1')"` + REDIS_PORT=`node -e "console.log(require('${TESTENV}').redis_port || '6336')"` + + echo "preparing redis..." + + # delete previous publicuser + cat <&2 + cleanup + exit 1 +} + +trap 'cleanup_and_exit' 1 2 3 5 9 13 + +while [ -n "$1" ]; do + if test "$1" = "--nodrop"; then + OPT_DROP_REDIS=no + OPT_DROP_PGSQL=no + shift + continue + elif test "$1" = "--nodrop-pg"; then + OPT_DROP_PGSQL=no + shift + continue + elif test "$1" = "--nodrop-redis"; then + OPT_DROP_REDIS=no + shift + continue + elif test "$1" = "--nocreate"; then + OPT_CREATE_REDIS=no + OPT_CREATE_PGSQL=no + shift + continue + elif test "$1" = "--nocreate-pg"; then + OPT_CREATE_PGSQL=no + shift + continue + elif test "$1" = "--nocreate-redis"; then + OPT_CREATE_REDIS=no + shift + continue + elif test "$1" = "--with-coverage"; then + OPT_COVERAGE=yes + shift + continue + elif test "$1" = "--offline"; then + OPT_OFFLINE=yes + shift + continue + else + break + fi +done + +if [ -z "$1" ]; then + echo "Usage: $0 [] []" >&2 + echo "Options:" >&2 + echo " --nocreate do not create the test environment on start" >&2 + echo " --nocreate-pg do not create the pgsql test environment" >&2 + echo " --nocreate-redis do not create the redis test environment" >&2 + echo " --nodrop do not drop the test environment on exit" >&2 + echo " --nodrop-pg do not drop the pgsql test environment" >&2 + echo " --nodrop-redis do not drop the redis test environment" >&2 + echo " --with-coverage use istanbul to determine code coverage" >&2 + exit 1 +fi + +TESTS=$@ + +if test x"$OPT_CREATE_REDIS" = xyes; then + echo "Starting redis on port ${REDIS_PORT}" + REDIS_CELL_PATH="${BASEDIR}/support/libredis_cell.so" + if [[ "$OSTYPE" == "darwin"* ]]; then + REDIS_CELL_PATH="${BASEDIR}/support/libredis_cell.dylib" + fi + echo "port ${REDIS_PORT}" | redis-server - --loadmodule ${REDIS_CELL_PATH} > ${BASEDIR}/test.log & + PID_REDIS=$! + echo ${PID_REDIS} > ${BASEDIR}/redis.pid +fi + +PREPARE_DB_OPTS= +if test x"$OPT_CREATE_PGSQL" != xyes; then + PREPARE_DB_OPTS="$PREPARE_DB_OPTS --skip-pg" +fi +if test x"$OPT_CREATE_REDIS" != xyes; then + PREPARE_DB_OPTS="$PREPARE_DB_OPTS --skip-redis" +fi +if test x"$OPT_OFFLINE" == xyes; then + PREPARE_DB_OPTS="$PREPARE_DB_OPTS --offline" +fi + +echo "Preparing the environment" +cd ${BASEDIR} +sh prepare_db.sh ${PREPARE_DB_OPTS} || die "database preparation failure" +cd - + +PATH=node_modules/.bin/:node_modules/mocha/bin:$PATH + +echo +echo "Environment:" +echo +echo " ogr2ogr version: "`ogr2ogr --version` +echo + +if test x"$OPT_COVERAGE" = xyes; then + echo "Running tests with coverage" + ./node_modules/.bin/istanbul cover node_modules/.bin/_mocha -- -u tdd --trace -t 5000 ${TESTS} +else + echo "Running tests" + mocha -u tdd -t 5000 ${TESTS} +fi +ret=$? + +cleanup || exit 1 + +exit $ret diff --git a/test/run_tests_docker.sh b/test/run_tests_docker.sh new file mode 100644 index 0000000..e17323c --- /dev/null +++ b/test/run_tests_docker.sh @@ -0,0 +1,47 @@ +#!/bin/bash + +usage() { + /etc/init.d/postgresql stop + echo "Usage: $0 [nodejs10|nodejs6]" + exit 1 +} + +echo "$0 $1" + +# start PostgreSQL +/etc/init.d/postgresql start + +# Configure +./configure + +echo "Node.js version:" +node -v + +# install dependencies +NODEJS_VERSION=${1-nodejs10} + +if [ "$NODEJS_VERSION" = "nodejs10" ]; +then + echo "npm version on install:" + npm -v + mv npm-shrinkwrap.json npm-shrinkwrap.json.backup + npm ci + npm ls + mv npm-shrinkwrap.json.backup npm-shrinkwrap.json +elif [ "$NODEJS_VERSION" = "nodejs6" ]; +then + echo "npm version on install:" + npm -v + mv package-lock.json package-lock.json.backup + npm i + npm ls + mv package-lock.json.backup package-lock.json +else + usage +fi + +# run tests +echo "npm version on tests:" +npm -v + +npm test diff --git a/test/support/.gitignore b/test/support/.gitignore new file mode 100644 index 0000000..68d2db3 --- /dev/null +++ b/test/support/.gitignore @@ -0,0 +1 @@ +CDB_*.sql diff --git a/test/support/assert.js b/test/support/assert.js new file mode 100644 index 0000000..b681335 --- /dev/null +++ b/test/support/assert.js @@ -0,0 +1,127 @@ +'use strict'; + +var assert = module.exports = exports = require('assert'); +var request = require('request'); +var debug = require('debug')('assert-response'); + +assert.response = function(server, req, res, callback) { + if (!callback) { + callback = res; + res = {}; + } + + var port = 5555, + host = '127.0.0.1'; + + var listeningAttempts = 0; + var listener; + 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() { + port++; + listeningAttempts++; + listen(); + }); + listener.on('listening', onServerListening); + } + + listen(); + + debug('Request definition', req); + + // jshint maxcomplexity:10 + function onServerListening() { + debug('Server listening on port = %d', port); + var status = res.status || res.statusCode; + var requestParams = { + url: 'http://' + host + ':' + port + req.url, + method: req.method || 'GET', + headers: req.headers || {}, + timeout: req.timeout || 5000, + encoding: req.encoding || 'utf8' + }; + + if (req.body || req.data) { + requestParams.body = req.body || req.data; + } + + if (req.formData) { + requestParams.formData = req.formData; + } + + debug('Request params', requestParams); + request(requestParams, function assert$response$requestHandler(error, response, body) { + debug('Request response', error); + listener.close(function() { + debug('Server closed'); + if (error) { + return callback(error); + } + + response = response || {}; + response.body = response.body || body; + debug('Response status', response.statusCode) + + // Assert response body + if (res.body) { + var eql = res.body instanceof RegExp ? res.body.test(response.body) : res.body === response.body; + assert.ok( + eql, + colorize('[red]{Invalid response body.}\n' + + ' Expected: [green]{' + res.body + '}\n' + + ' Got: [red]{' + response.body + '}') + ); + } + + // Assert response status + if (typeof status === 'number') { + assert.equal(response.statusCode, status, + colorize('[red]{Invalid response status code.}\n' + + ' Expected: [green]{' + status + '}\n' + + ' Got: [red]{' + response.statusCode + '}\n' + + ' Body: ' + response.body) + ); + } + + // Assert response headers + 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; + assert.ok(headerEql, + colorize('Invalid response header [bold]{' + name + '}.\n' + + ' Expected: [green]{' + expected + '}\n' + + ' Got: [red]{' + actual + '}') + ); + } + } + + // Callback + return callback(null, response); + }); + }); + + } +}; + +/** + * Colorize the given string using ansi-escape sequences. + * Disabled when --boring is set. + * + * @param {String} str + * @return {String} + */ +function colorize(str) { + var colors = { bold: 1, red: 31, green: 32, yellow: 33 }; + 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 new file mode 100644 index 0000000..99559ca --- /dev/null +++ b/test/support/batch-test-client.js @@ -0,0 +1,300 @@ +'use strict'; + +require('../helper'); +var assert = require('assert'); +var appServer = require('../../app/server'); +var redisUtils = require('./redis_utils'); +var debug = require('debug')('batch-test-client'); + +var JobStatus = require('../../batch/job_status'); +var metadataBackend = require('cartodb-redis')({ pool: redisUtils.getPool() }); +var batchFactory = require('../../batch/index'); + +function response(code) { + return { + status: code + }; +} + +var RESPONSE = { + OK: response(200), + CREATED: response(201), + BAD_REQUEST: response(400) +}; + + +function BatchTestClient(config) { + this.config = config || {}; + this.server = appServer(); + + this.batch = batchFactory(metadataBackend, redisUtils.getPool(), this.config.name); + this.batch.start(); + + this.pendingJobs = []; + this.ready = false; + this.batch.on('ready', function() { + this.ready = true; + this.pendingJobs.forEach(function(pendingJob) { + this.createJob(pendingJob.job, pendingJob.override, pendingJob.callback); + }.bind(this)); + }.bind(this)); +} + +module.exports = BatchTestClient; + +BatchTestClient.prototype.isReady = function() { + return this.ready; +}; + +BatchTestClient.prototype.getExpectedResponse = function (override) { + return override.response || this.config.response || RESPONSE.CREATED; +}; + +BatchTestClient.prototype.createJob = function(job, override, callback) { + if (!callback) { + callback = override; + override = {}; + } + if (!this.isReady()) { + this.pendingJobs.push({ + job: job, + override: override || {}, + callback: callback + }); + return debug('Waiting for Batch service to be ready'); + } + assert.response( + this.server, + { + url: this.getUrl(override), + headers: { + host: this.getHost(override), + 'Content-Type': 'application/json', + authorization: this.getAuthorization(override) + }, + method: 'POST', + data: JSON.stringify(job) + }, + this.getExpectedResponse(override), + function (err, res) { + if (err) { + return callback(err); + } + + if (res.statusCode < 400) { + return callback(null, new JobResult(JSON.parse(res.body), this, override)); + } else { + return callback(null, res); + } + }.bind(this) + ); +}; + +BatchTestClient.prototype.getJobStatus = function(jobId, override, callback) { + assert.response( + this.server, + { + url: this.getUrl(override, jobId), + headers: { + host: this.getHost(override), + authorization: this.getAuthorization(override) + }, + method: 'GET', + timeout: override.timeout + }, + RESPONSE.OK, + function (err, res) { + if (err) { + return callback(err); + } + return callback(null, JSON.parse(res.body)); + } + ); +}; + +BatchTestClient.prototype.getWorkInProgressJobs = function(override, callback) { + if (!callback) { + callback = override; + override = {}; + } + + assert.response( + this.server, + { + url: '/api/v1/jobs-wip', + headers: { + host: this.getHost(override) + }, + method: 'GET' + }, + RESPONSE.OK, + function (err, res) { + if (err) { + return callback(err); + } + return callback(null, JSON.parse(res.body)); + } + ); +}; + +BatchTestClient.prototype.cancelJob = function(jobId, override, callback) { + assert.response( + this.server, + { + url: this.getUrl(override, jobId), + headers: { + host: this.getHost(override) + }, + method: 'DELETE' + }, + override.statusCode, + function (err, res) { + if (err) { + return callback(err); + } + return callback(null, JSON.parse(res.body)); + } + ); +}; + +BatchTestClient.prototype.drain = function(callback) { + this.batch.stop(function() { + return redisUtils.clean('batch:*', callback); + }); +}; + +BatchTestClient.prototype.getHost = function(override) { + return override.host || this.config.host || 'vizzuality.cartodb.com'; +}; + +BatchTestClient.prototype.getAuthorization = function (override) { + const auth = override.authorization || this.config.authorization; + + if (auth) { + return `Basic ${new Buffer(auth).toString('base64')}`; + } +}; + +BatchTestClient.prototype.getUrl = function(override, jobId) { + var urlParts = ['/api/v2/sql/job']; + if (jobId) { + urlParts.push(jobId); + } + return `${urlParts.join('/')}${override.anonymous ? '' : '?api_key=' + this.getApiKey(override)}`; +}; + +BatchTestClient.prototype.getApiKey = function(override) { + return override.apiKey || this.config.apiKey || '1234'; +}; + +/****************** JobResult ******************/ + + +function JobResult(job, batchTestClient, override) { + this.job = job; + this.batchTestClient = batchTestClient; + this.override = override; +} + +JobResult.prototype.getStatus = function(requiredStatus, callback) { + if (!callback) { + callback = requiredStatus; + requiredStatus = undefined; + } + + var self = this; + var attempts = 1; + self.override.timeout = 1000; + + var interval = setInterval(function () { + self.batchTestClient.getJobStatus(self.job.job_id, self.override, function (err, job) { + if (err) { + clearInterval(interval); + return callback(err); + } + attempts += 1; + + if (attempts > 20) { + clearInterval(interval); + return callback(new Error('Reached maximum number of request (20) to check job status')); + } + + if (hasRequiredStatus(job, requiredStatus)) { + clearInterval(interval); + self.job = job; + return callback(null, job); + } else { + debug('Job %s [status=%s] waiting to be done', self.job.job_id, job.status); + } + }); + }, 100); +}; + +function hasRequiredStatus(job, requiredStatus) { + if (requiredStatus) { + return job.status === requiredStatus; + } + + if (JobStatus.isFinal(job.status)) { + if (job.fallback_status !== undefined) { + if (JobStatus.isFinal(job.fallback_status) || job.fallback_status === JobStatus.SKIPPED) { + return true; + } + } else { + return true; + } + } + + return false; +} + +JobResult.prototype.cancel = function (callback) { + var self = this; + this.override.statusCode = response(RESPONSE.OK); + this.batchTestClient.cancelJob(this.job.job_id, this.override, function (err, job) { + if (err) { + return callback(err); + } + self.job = job; + callback(null, job); + }); +}; + +JobResult.prototype.tryCancel = function (callback) { + var self = this; + this.override.statusCode = response(); + this.batchTestClient.cancelJob(this.job.job_id, this.override, function (err, job) { + if (err) { + return callback(err); + } + self.job = job; + callback(null, job); + }); +}; + +JobResult.prototype.validateExpectedResponse = function (expected) { + var actual = this.job.query; + + actual.query.forEach(function(actualQuery, index) { + var expectedQuery = expected.query[index]; + assert.ok(expectedQuery); + Object.keys(expectedQuery).forEach(function(expectedKey) { + assert.equal( + actualQuery[expectedKey], + expectedQuery[expectedKey], + 'Expected value for key "' + expectedKey + '" does not match: ' + actualQuery[expectedKey] + ' ==' + + expectedQuery[expectedKey] + ' at query index=' + index + '. Full response: ' + + JSON.stringify(actual, null, 4) + ); + }); + var propsToCheckDate = ['started_at', 'ended_at']; + propsToCheckDate.forEach(function(propToCheckDate) { + if (actualQuery.hasOwnProperty(propToCheckDate)) { + assert.ok(new Date(actualQuery[propToCheckDate])); + } + }); + }); + + assert.equal(actual.onsuccess, expected.onsuccess); + assert.equal(actual.onerror, expected.onerror); +}; diff --git a/test/support/csv/copy_test_table.csv b/test/support/csv/copy_test_table.csv new file mode 100644 index 0000000..79d2f3e --- /dev/null +++ b/test/support/csv/copy_test_table.csv @@ -0,0 +1,7 @@ +id,name +11,Paul +12,Peter +13,Matthew +14, +15,James +16,John diff --git a/test/support/csv/copy_test_table.csv.gz b/test/support/csv/copy_test_table.csv.gz new file mode 100644 index 0000000000000000000000000000000000000000..bac1a2d17a8c736b185da5e9f7fd274832ded14e GIT binary patch literal 96 zcmV-m0H6OKiwForLG@Yy17mM+d0%v8b97&HVPb4$E@N|c0Lx6#$xF;l z;W9MR2}mtTE#fjX*6~d&DalAJ=Q1?W;W9MU@dBzV<}x(X@ygH0;{pH_5%|J60000` CkSI3* literal 0 HcmV?d00001 diff --git a/test/support/db_utils.js b/test/support/db_utils.js new file mode 100644 index 0000000..decfc9f --- /dev/null +++ b/test/support/db_utils.js @@ -0,0 +1,36 @@ +'use strict'; + +const { Client } = require('pg'); + +const dbConfig = { + db_user: process.env.PGUSER || 'postgres', + db_host: global.settings.db_host, + db_port: global.settings.db_port, + db_batch_port: global.settings.db_batch_port +}; + +module.exports.resetPgBouncerConnections = function (callback) { + // We assume there's no pgbouncer if db_port === db_batch_port + if (dbConfig.db_port === dbConfig.db_batch_port) { + return callback(); + } + + const client = new Client({ + database: 'pgbouncer', + user: dbConfig.db_user, + host: dbConfig.db_host, + port: dbConfig.db_port + }); + + // We just chain a PAUSE followed by a RESUME to reset internal pool connections of PgBouncer + client.connect(); + client.query('PAUSE', err => { + if (err) { + return callback(err); + } + client.query('RESUME', err => { + client.end(); + return callback(err); + }); + }); +}; diff --git a/test/support/libredis_cell.dylib b/test/support/libredis_cell.dylib new file mode 100755 index 0000000000000000000000000000000000000000..7c7d6c8880ceb7be7f407db072f684e35f6a297e GIT binary patch literal 1852524 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%0DRJjA%GKbkgW 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

Z7$GrS^Z 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

biTqLcpHB=7gT&O7Gfe7UP^KL9f7@vt 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ȷA8`#+1^*|ef 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

eM^jsGA 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≦6-`(hZd^ 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

%E$n8Ru4o-(^T*mI>icVLvaufJH~MK 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%0Ao_UP;FvCtEYxBGLgVV@eIAC3xki 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