diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 00000000..10caa2a8 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,73 @@ +on: + pull_request: + paths-ignore: + - 'LICENSE' + - 'README**' + - 'HOW_TO_RELEASE**' + - 'LOGGING**' + +env: + GCLOUD_VERSION: '306.0.0' + ARTIFACTS_PROJECT_ID: cartodb-on-gcp-main-artifacts + NODE_VERSION: '12.18.3' + +jobs: + build-and-test: + runs-on: ubuntu-18.04 + timeout-minutes: 10 + + steps: + - uses: actions/checkout@v2 + with: + submodules: true + token: ${{ secrets.CARTOFANTE_PERSONAL_TOKEN }} + + - name: Setup gcloud authentication + uses: google-github-actions/setup-gcloud@master + with: + version: ${{env.GCLOUD_VERSION}} + service_account_key: ${{ secrets.ARTIFACTS_GCLOUD_ACCOUNT_BASE64 }} + + - name: Configure docker and pull images + run: | + gcloud auth configure-docker + docker pull gcr.io/cartodb-on-gcp-main-artifacts/postgres:latest + docker pull gcr.io/cartodb-on-gcp-main-artifacts/redis:latest + + - name: Run deps (Redis and Postgres) + run: | + docker run -d --name postgres -p 5432:5432 gcr.io/cartodb-on-gcp-main-artifacts/postgres:latest + docker run -d --name redis -p 6379:6379 gcr.io/cartodb-on-gcp-main-artifacts/redis:latest + + - name: Use Node.js + uses: actions/setup-node@v1 + with: + node-version: ${{env.NODE_VERSION}} + + - run: npm ci + + - name: install redis tools + run: sudo apt-get -yqq install redis-tools gdal-bin + + - run: npm test + env: + CI: true + CARTO_SQL_API_ENV_BASED_CONF: true + CARTO_SQL_API_POSTGRES_HOST: localhost + CARTO_SQL_API_POSTGRES_PORT: 5432 + CARTO_SQL_API_REDIS_HOST: localhost + CARTO_SQL_API_REDIS_PORT: 6379 + CARTO_SQL_API_NODE_ENV: test + NODE_ENV: test + + - name: Build image + run: | + echo ${GITHUB_SHA::7} + echo ${GITHUB_REF##*/} + docker build -f private/Dockerfile -t gcr.io/$ARTIFACTS_PROJECT_ID/sql-api:${GITHUB_REF##*/} -t gcr.io/$ARTIFACTS_PROJECT_ID/sql-api:${GITHUB_SHA::7} -t gcr.io/$ARTIFACTS_PROJECT_ID/sql-api:${GITHUB_REF##*/}--${GITHUB_SHA::7} . + + - name: Upload image + run: | + docker push gcr.io/$ARTIFACTS_PROJECT_ID/sql-api:${GITHUB_REF##*/} + docker push gcr.io/$ARTIFACTS_PROJECT_ID/sql-api:${GITHUB_SHA::7} + docker push gcr.io/$ARTIFACTS_PROJECT_ID/sql-api:${GITHUB_REF##*/}--${GITHUB_SHA::7} diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml new file mode 100644 index 00000000..0e913143 --- /dev/null +++ b/.github/workflows/master.yml @@ -0,0 +1,43 @@ +# in this workflow we don't run the tests. Only build image, tag (also latests) and upload. The tests are not run because they are run +# on each pull request, and there is a branch protection that forces to have branch up to date before merging, so tests are always run +# with the latest code + +name: master build image +on: + push: + branches: + - master + +env: + GCLOUD_VERSION: '306.0.0' + ARTIFACTS_PROJECT_ID: cartodb-on-gcp-main-artifacts + +jobs: + build-master: + runs-on: ubuntu-18.04 + timeout-minutes: 5 + + steps: + - uses: actions/checkout@v2 + with: + submodules: true + token: ${{ secrets.CARTOFANTE_PERSONAL_TOKEN }} + + - name: Build image + run: | + echo ${GITHUB_SHA::7} + echo ${GITHUB_REF##*/} + docker build -f private/Dockerfile -t gcr.io/$ARTIFACTS_PROJECT_ID/sql-api:latest -t gcr.io/$ARTIFACTS_PROJECT_ID/sql-api:${GITHUB_REF##*/} -t gcr.io/$ARTIFACTS_PROJECT_ID/sql-api:${GITHUB_SHA::7} . + + - name: Setup gcloud authentication + uses: google-github-actions/setup-gcloud@master + with: + version: ${{env.GCLOUD_VERSION}} + service_account_key: ${{ secrets.ARTIFACTS_GCLOUD_ACCOUNT_BASE64 }} + + - name: Upload image + run: | + docker push gcr.io/$ARTIFACTS_PROJECT_ID/sql-api:${GITHUB_REF##*/} + docker push gcr.io/$ARTIFACTS_PROJECT_ID/sql-api:${GITHUB_SHA::7} + docker push gcr.io/$ARTIFACTS_PROJECT_ID/sql-api:latest + \ No newline at end of file diff --git a/.gitignore b/.gitignore index 8a13ea5c..72e9fc83 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,4 @@ yarn.lock build_resources/ .dockerignore Dockerfile +docker_node_modules diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..4b13029c --- /dev/null +++ b/.gitmodules @@ -0,0 +1,4 @@ +[submodule "private"] + path = private + url = git@github.com:CartoDB/CartoDB-SQL-API-private.git + branch = master diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 7085540d..00000000 --- a/.travis.yml +++ /dev/null @@ -1,61 +0,0 @@ -sudo: false - -language: node_js - -services: - - redis-server - -env: - global: - - PGUSER=postgres - - PGDATABASE=postgres - - PGOPTIONS='-c client_min_messages=NOTICE' - -jobs: - include: - - env: POSTGRESQL_VERSION="10" POSTGIS_VERSION="2.4" - dist: xenial - - env: POSTGRESQL_VERSION="11" POSTGIS_VERSION="2.5" - dist: xenial - - env: POSTGRESQL_VERSION="12" POSTGIS_VERSION="3" - dist: bionic - -node_js: - - "12" - -install: - - npm ci - -script: - -# Remove old packages - - sudo apt-get remove postgresql-$POSTGRESQL_VERSION postgresql-client-$POSTGRESQL_VERSION postgresql-server-dev-$POSTGRESQL_VERSION postgresql-common postgresql-client-common postgresql-$POSTGRESQL_VERSION-postgis-$POSTGIS_VERSION postgresql-$POSTGRESQL_VERSION-postgis-$POSTGIS_VERSION-scripts postgis - -# Install CARTO packages - - if [[ $POSTGRESQL_VERSION == '10' ]]; then sudo add-apt-repository -y ppa:cartodb/gis; fi; - - sudo add-apt-repository -y ppa:cartodb/postgresql-$POSTGRESQL_VERSION - - sudo apt-get -q update -# We use -t $TRAVIS_DIST to give preference to our ppa's (which are called as the ${dist}), instead of -# pgdg repos (which are called ${dist}-pgdg. Nasty but it works. - - sudo apt-get install -y --allow-unauthenticated --no-install-recommends --no-install-suggests postgresql-$POSTGRESQL_VERSION postgresql-client-$POSTGRESQL_VERSION postgresql-server-dev-$POSTGRESQL_VERSION postgresql-common postgresql-$POSTGRESQL_VERSION-postgis-$POSTGIS_VERSION postgresql-$POSTGRESQL_VERSION-postgis-$POSTGIS_VERSION-scripts postgis gdal-bin -t $TRAVIS_DIST -# For pre12, install plpython2. For PG12 install plpython3 - - if [[ $POSTGRESQL_VERSION != '12' ]]; then sudo apt-get install -y postgresql-plpython-$POSTGRESQL_VERSION python python-redis -t $TRAVIS_DIST; else sudo apt-get install -y postgresql-plpython3-12 python3 python3-redis -t $TRAVIS_DIST; fi; - -# Remove old clusters and create the new one - - for i in $(pg_lsclusters | tail -n +2 | awk '{print $1}'); do sudo pg_dropcluster --stop $i main; done; - - sudo rm -rf /etc/postgresql/$POSTGRESQL_VERSION /var/lib/postgresql/$POSTGRESQL_VERSION /var/ramfs/postgresql/$POSTGRESQL_VERSION - - sudo pg_createcluster -u postgres $POSTGRESQL_VERSION main --start -p 5432 -- --auth-local trust - - - git clone https://github.com/CartoDB/cartodb-postgresql.git - - cd cartodb-postgresql && make && sudo make install && cd .. - - - createdb template_postgis - - psql -c "CREATE EXTENSION postgis" template_postgis - - if [[ $POSTGRESQL_VERSION == '12' ]]; then psql -c "CREATE EXTENSION postgis_raster" template_postgis; fi; - - - cp config/environments/test.js.example config/environments/test.js - - npm test - -after_failure: - - pg_lsclusters - - sudo cat /var/log/postgresql/postgresql-$POSTGRESQL_VERSION-main.log diff --git a/README.md b/README.md index d4d8679f..38306211 100644 --- a/README.md +++ b/README.md @@ -11,8 +11,8 @@ The [`CARTO’s SQL API`](https://carto.com/developers/sql-api/) allows you to i Requirements: -* [`Node 10.x (npm 6.x)`](https://nodejs.org/dist/latest-v10.x/) -* [`PostgreSQL >= 10.0`](https://www.postgresql.org/download/) +* [`Node 12.x`](https://nodejs.org/dist/latest-v12.x/) +* [`PostgreSQL >= 11.0`](https://www.postgresql.org/download/) * [`PostGIS >= 2.4`](https://postgis.net/install/) * [`CARTO Postgres Extension >= 0.24.1`](https://github.com/CartoDB/cartodb-postgresql) * [`Redis >= 4`](https://redis.io/download) @@ -43,7 +43,11 @@ $ npm install ### Run -Create the `./config/environments/.js` file (there are `.example` files to start from). Look at `./lib/server-options.js` for more on config. +You can inject the configuration through environment variables at run time. Check the file `./config/environments/config.js` to see the ones you have available. + +While the migration to the new environment based configuration, you can still use the old method of copying a config file. To enabled the one with environment variables you need to pass `CARTO_SQL_API_ENV_BASED_CONF=true`. You can use the docker image to run it. + +Old way: ```shell $ node app.js @@ -57,6 +61,28 @@ Where `` is the name of a configuration file under `./config/environments/` $ npm test ``` +You can try to run the tests against the dependencies from the `dev-env`. To do so, you need to build the test docker image: + +```shell +$ docker-compose -f private/docker-compose.yml build +``` + +Then you can run the tests like: + +```shell +$ docker-compose -f private/docker-compose.yml run sql-api-tests +``` + +It will mount your code inside a volume. In case you want to play and run `npm test` or something else you can do: + +```shell +$ docker-compose -f private/docker-compose.yml run --entrypoint bash sql-api-tests +``` + +So you will have a bash shell inside the test container, with the code from your host. + +⚠️ *WARNING* Some tests still fail inside the docker environment. Inside CI they don't yet use the `ci` folder to run the tests either. There is a failing test which prevents it. + ### Coverage ```shell @@ -65,34 +91,6 @@ $ npm run cover Open `./coverage/lcov-report/index.html`. -### Docker support - -We provide docker images just for testing and continuous integration purposes: - -* [`nodejs-xenial-pg1121`](https://hub.docker.com/r/carto/nodejs-xenial-pg1121/tags) -* [`nodejs-xenial-pg101`](https://hub.docker.com/r/carto/nodejs-xenial-pg101/tags) - -You can find instructions to install Docker, download, and update images [here](https://github.com/CartoDB/Windshaft-cartodb/blob/master/docker/reference.md). - -### Useful `npm` scripts - -Run test in a docker image with a specific Node.js version: - -```shell -$ DOCKER_IMAGE= NODE_VERSION= npm run test:docker -``` - -Where: - -* ``: the tag of required docker image, e.g. `carto/nodejs-xenial-pg1121:latest` -* ``: the Node.js version, e.g. `10.15.1` - -In case you need to debug: - -```shell -$ DOCKER_IMAGE= npm run docker:bash -``` - ## Documentation You can find an overview, guides, full reference, and support in [`CARTO's developer center`](https://carto.com/developers/sql-api/). The [docs directory](https://github.com/CartoDB/CartoDB-SQL-API/tree/master/docs) contains different documentation resources, from a higher level to more detailed ones. diff --git a/app.js b/app.js index b5c9e189..1d2d866e 100755 --- a/app.js +++ b/app.js @@ -21,14 +21,17 @@ const argv = require('yargs') .describe('c', 'Load configuration from path') .argv; -const environmentArg = argv._[0] || process.env.NODE_ENV || 'development'; +let environmentArg = argv._[0] || process.env.NODE_ENV || 'development'; +if (process.env.CARTO_SQL_API_ENV_BASED_CONF) { + // we override the file with the one with env vars + environmentArg = 'config'; +} const configurationFile = path.resolve(argv.config || './config/environments/' + environmentArg + '.js'); if (!fs.existsSync(configurationFile)) { logger.fatal(new Error(`Configuration file "${configurationFile}" does not exist`)); process.exit(1); } - global.settings = require(configurationFile); const ENVIRONMENT = argv._[0] || process.env.NODE_ENV || global.settings.environment; @@ -37,7 +40,7 @@ process.env.NODE_ENV = ENVIRONMENT; const availableEnvironments = ['development', 'production', 'test', 'staging']; if (!availableEnvironments.includes(ENVIRONMENT)) { - logger.fatal(new Error(`Invalid environment argument, valid ones: ${Object.keys(availableEnvironments).join(', ')}`)); + logger.fatal(new Error(`Invalid environment ${ENVIRONMENT} argument, valid ones: ${Object.values(availableEnvironments).join(', ')}`)); process.exit(1); } @@ -57,6 +60,7 @@ const { version, name } = require('./package'); const createServer = require('./lib/server'); const server = createServer(statsClient); + const listener = server.listen(global.settings.node_port, global.settings.node_host); listener.on('listening', function () { const { address, port } = listener.address(); @@ -118,8 +122,9 @@ function scheduleForcedExit (killTimeout, finalLogger) { killTimer.unref(); } +const regex = /[a-z]?([0-9]*)/; function isGteMinVersion (version, minVersion) { - const versionMatch = /[a-z]?([0-9]*)/.exec(version); + const versionMatch = regex.exec(version); if (versionMatch) { const majorVersion = parseInt(versionMatch[1], 10); if (Number.isFinite(majorVersion)) { diff --git a/config/environments/config.js b/config/environments/config.js new file mode 100644 index 00000000..ab3e4982 --- /dev/null +++ b/config/environments/config.js @@ -0,0 +1,198 @@ +// This is the file that has the generic configuration and you can override the chnages +// in different environments with env. vars + +// Time in milliseconds to force GC cycle. +// Disable by using <=0 value. +module.exports.gc_interval = 10000; +module.exports.routes = { + // Each entry corresponds with an express' router. + // You must define at least one path. However, middlewares are optional. + api: [{ + // Required: path where other "routers" or "controllers" will be attached to. + paths: [ + // In case the path has a :user param the username will be the one specified in the URL, + // otherwise it will fallback to extract the username from the host header. + '/api/:version', + '/user/:user/api/:version', + ], + // Optional: attach middlewares at the begining of the router + // to perform custom operations. + middlewares: [ + function noop () { + return function noopMiddleware (req, res, next) { + next(); + } + } + ], + sql: [{ + // Required + paths: [ + '/sql' + ], + // Optional + middlewares: [] + }] + }] +}; +// 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; +// Regular expression pattern to extract username +// from hostname. Must have a single grabbing block. +// for dev-env you need to use '^(.*)\\.localhost'; +module.exports.user_from_host = process.env.CARTO_SQL_API_USER_FROM_HOST || '^(.*)\\.cartodb\\.com$'; +module.exports.node_port = 8080; +module.exports.node_host = null; // null on purpouse so it listens to whatever address docker assigns +// idle socket timeout, in miliseconds +module.exports.node_socket_timeout = 600000; +module.exports.environment = process.env.CARTO_SQL_API_NODE_ENV || 'development'; +// Supported labels: 'user_id' (read from redis) +module.exports.db_base_name = process.env.CARTO_SQL_API_DB_BASE_NAME || 'cartodb_user_<%= user_id %>_db'; +// Supported labels: 'user_id' (read from redis) +module.exports.db_user = process.env.CARTO_SQL_API_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 = process.env.CARTO_SQL_API_POSTGRES_HOST || 'localhost'; +module.exports.db_port = process.env.CARTO_SQL_API_POSTGRES_PORT || '6432'; +module.exports.db_batch_port = process.env.CARTO_SQL_API_POSTGRES_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.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'; +module.exports.copy_from_minimum_input_speed = 0; // 1 byte per second +module.exports.copy_from_maximum_slow_input_speed_interval = 15; // 15 seconds +// 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 // Not used yet +}; +module.exports.redis_host = process.env.CARTO_SQL_API_REDIS_HOST || '127.0.0.1'; +module.exports.redis_port = process.env.CARTO_SQL_API_REDIS_PORT || 6379; +module.exports.redisPool = 50; +module.exports.redisIdleTimeoutMillis = 10000; +module.exports.redisReapIntervalMillis = 1000; +module.exports.redisLog = false; + +// 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' +}; + +let allowedHosts = ['carto.com', 'cartodb.com']; +if (process.env.CARTO_SQL_API_OAUTH_HOSTS) { + const hosts = process.env.CARTO_SQL_API_OAUTH_HOSTS.split(','); + if (hosts.length > 0) { + allowedHosts = hosts; + } +} +module.exports.oauth = { + + allowedHosts: allowedHosts +}; +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, + copy_from: false, + copy_to: false + } +}; + +module.exports.validatePGEntitiesAccess = false; +module.exports.logQueries = true; +module.exports.maxQueriesLogLength = 1024; + +module.exports.cache = { + ttl: 60 * 60 * 24 * 365, // one year in seconds + fallbackTtl: 60 * 5 // five minutes in seconds +}; + +module.exports.pubSubMetrics = { + enabled: process.env.CARTO_SQL_API_METRICS_ENABLED === 'true' || false, + project_id: process.env.CARTO_SQL_API_METRICS_PROJECT_ID || 'avid-wavelet-844', + credentials: '', + topic: process.env.CARTO_SQL_API_METRICS_PROJECT_ID || 'raw-metric-events' +}; + +// override some defaults for tests +if (process.env.NODE_ENV === 'test') { + module.exports.redisIdleTimeoutMillis = 1; + module.exports.redisReapIntervalMillis = 1; + module.exports.db_pubuser = 'testpublicuser'; + module.exports.batch_query_timeout = 5 * 1000; // 5 seconds in milliseconds + module.exports.redisIdleTimeoutMillis = 1; + module.exports.redisReapIntervalMillis = 1; + 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'; + module.exports.user_from_host = '^([^.]*)\\.'; + module.exports.oauth = { + allowedHosts: ['localhost.lan:8080', 'localhostdb.lan:8080'] + }; + module.exports.health = { + enabled: true, + username: 'vizzuality', + query: 'select 1' + }; +} diff --git a/config/environments/test.js.example b/config/environments/test.js.example index cb57f4ff..d490030b 100644 --- a/config/environments/test.js.example +++ b/config/environments/test.js.example @@ -50,7 +50,7 @@ module.exports.db_user_pass = 'test_cartodb_user_<%= user_id %>_pass'; 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_host = process.env.PGHOST || 'localhost'; module.exports.db_port = '5432'; module.exports.db_batch_port = '5432'; module.exports.finished_jobs_ttl_in_seconds = 2 * 3600; // 2 hours @@ -98,8 +98,8 @@ module.exports.db_keep_alive = { enabled: true, initialDelay: 5000 // Not used yet }; -module.exports.redis_host = '127.0.0.1'; -module.exports.redis_port = 6336; +module.exports.redis_host = process.env.REDIS_HOST || '127.0.0.1'; +module.exports.redis_port = process.env.REDIS_PORT || 6336; module.exports.redisPool = 50; module.exports.redisIdleTimeoutMillis = 1; module.exports.redisReapIntervalMillis = 1; diff --git a/docker/scripts/test-setup.sh b/docker/scripts/test-setup.sh deleted file mode 100644 index 9d735d9a..00000000 --- a/docker/scripts/test-setup.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/bash - -/etc/init.d/postgresql start - -source /src/nodejs-install.sh - -# Install cartodb-postgresql extension -git clone https://github.com/CartoDB/cartodb-postgresql.git -cd cartodb-postgresql && make && make install && cd .. - -cp config/environments/test.js.example config/environments/test.js - -npm ci -npm test diff --git a/lib/utils/query-info.js b/lib/utils/query-info.js index 12718435..d840aec4 100644 --- a/lib/utils/query-info.js +++ b/lib/utils/query-info.js @@ -2,6 +2,8 @@ const COPY_FORMATS = ['TEXT', 'CSV', 'BINARY']; +const regex = /\bFORMAT\s+(\w+)/; + module.exports = { getFormatFromCopyQuery (copyQuery) { let format = 'TEXT'; // Postgres default format @@ -13,7 +15,6 @@ module.exports = { } if (copyQuery.includes(' WITH') && copyQuery.includes('FORMAT ')) { - const regex = /\bFORMAT\s+(\w+)/; const result = regex.exec(copyQuery); if (result && result.length === 2) { diff --git a/package-lock.json b/package-lock.json index 25ca62f8..e0cf037d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -375,6 +375,12 @@ "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", "integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=" }, + "@sindresorhus/is": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", + "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", + "dev": true + }, "@sinonjs/commons": { "version": "1.7.1", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.7.1.tgz", @@ -433,6 +439,15 @@ "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==", "dev": true }, + "@szmarczak/http-timer": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", + "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", + "dev": true, + "requires": { + "defer-to-connect": "^1.0.1" + } + }, "@types/color-name": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", @@ -531,6 +546,55 @@ "uri-js": "^4.2.2" } }, + "ansi-align": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.0.tgz", + "integrity": "sha512-ZpClVKqXN3RGBmKibdfWzqCY4lnjEuoNzU5T0oEFpfd/z5qJHVarukridD4juLO2FXMiwUQxr9WqQtaYa8XRYw==", + "dev": true, + "requires": { + "string-width": "^3.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, "ansi-colors": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", @@ -859,6 +923,99 @@ } } }, + "boxen": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-4.2.0.tgz", + "integrity": "sha512-eB4uT9RGzg2odpER62bBwSLvUeGC+WbRjjyyFhGsKnc8wp/m0+hQsMUvUe3H2V0D5vw0nBdO1hCJoZo5mKeuIQ==", + "dev": true, + "requires": { + "ansi-align": "^3.0.0", + "camelcase": "^5.3.1", + "chalk": "^3.0.0", + "cli-boxes": "^2.2.0", + "string-width": "^4.1.0", + "term-size": "^2.1.0", + "type-fest": "^0.8.1", + "widest-line": "^3.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -942,6 +1099,38 @@ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" }, + "cacheable-request": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", + "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", + "dev": true, + "requires": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^3.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^4.1.0", + "responselike": "^1.0.2" + }, + "dependencies": { + "get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "dev": true + } + } + }, "caching-transform": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", @@ -1047,12 +1236,24 @@ "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", "dev": true }, + "ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true + }, "clean-stack": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", "dev": true }, + "cli-boxes": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", + "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==", + "dev": true + }, "cli-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", @@ -1108,6 +1309,15 @@ } } }, + "clone-response": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", + "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "dev": true, + "requires": { + "mimic-response": "^1.0.0" + } + }, "code-point-at": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", @@ -1188,6 +1398,20 @@ } } }, + "configstore": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", + "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", + "dev": true, + "requires": { + "dot-prop": "^5.2.0", + "graceful-fs": "^4.1.2", + "make-dir": "^3.0.0", + "unique-string": "^2.0.0", + "write-file-atomic": "^3.0.0", + "xdg-basedir": "^4.0.0" + } + }, "console-control-strings": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", @@ -1255,6 +1479,12 @@ } } }, + "crypto-random-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", + "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", + "dev": true + }, "dashdash": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", @@ -1287,6 +1517,15 @@ "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.2.0.tgz", "integrity": "sha512-vDPw+rDgn3bZe1+F/pyEwb1oMG2XTlRVgAa6B4KccTEpYgF8w6eQllVbQcfIJnZyvzFtFpxnpGtx8dd7DJp/Rw==" }, + "decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "dev": true, + "requires": { + "mimic-response": "^1.0.0" + } + }, "deep-extend": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", @@ -1316,6 +1555,12 @@ } } }, + "defer-to-connect": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", + "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==", + "dev": true + }, "define-properties": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", @@ -1399,6 +1644,15 @@ "resolved": "https://registry.npmjs.org/dot/-/dot-1.0.3.tgz", "integrity": "sha1-+HUL+2sDx2ZOsObLHrTGZBmvlCc=" }, + "dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "dev": true, + "requires": { + "is-obj": "^2.0.0" + } + }, "dtrace-provider": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.6.0.tgz", @@ -1408,6 +1662,12 @@ "nan": "^2.0.8" } }, + "duplexer3": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", + "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", + "dev": true + }, "duplexify": { "version": "3.7.1", "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", @@ -1533,6 +1793,12 @@ "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", "dev": true }, + "escape-goat": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz", + "integrity": "sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==", + "dev": true + }, "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -2914,6 +3180,15 @@ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, "getpass": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", @@ -2944,6 +3219,15 @@ "is-glob": "^4.0.1" } }, + "global-dirs": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-2.0.1.tgz", + "integrity": "sha512-5HqUqdhkEovj2Of/ms3IeS/EekcO54ytHRLV4PEY2rhRwrHXLQjeVEES0Lhka0xwNDtGYn58wyC4s5+MHsOO6A==", + "dev": true, + "requires": { + "ini": "^1.3.5" + } + }, "globals": { "version": "12.3.0", "resolved": "https://registry.npmjs.org/globals/-/globals-12.3.0.tgz", @@ -3015,6 +3299,25 @@ "node-forge": "^0.9.0" } }, + "got": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", + "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", + "dev": true, + "requires": { + "@sindresorhus/is": "^0.14.0", + "@szmarczak/http-timer": "^1.1.2", + "cacheable-request": "^6.0.0", + "decompress-response": "^3.3.0", + "duplexer3": "^0.1.4", + "get-stream": "^4.1.0", + "lowercase-keys": "^1.0.1", + "mimic-response": "^1.0.1", + "p-cancelable": "^1.0.0", + "to-readable-stream": "^1.0.0", + "url-parse-lax": "^3.0.0" + } + }, "graceful-fs": { "version": "4.1.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", @@ -3086,6 +3389,12 @@ "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", "dev": true }, + "has-yarn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", + "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==", + "dev": true + }, "hasha": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.1.0.tgz", @@ -3114,6 +3423,12 @@ "integrity": "sha512-a4u9BeERWGu/S8JiWEAQcdrg9v4QArtP9keViQjGMdff20fBdd8waotXaNmODqBe6uZ3Nafi7K/ho4gCQHV3Ig==", "dev": true }, + "http-cache-semantics": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", + "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", + "dev": true + }, "http-errors": { "version": "1.6.3", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", @@ -3156,6 +3471,12 @@ "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", "dev": true }, + "ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk=", + "dev": true + }, "ignore-walk": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.1.tgz", @@ -3175,6 +3496,12 @@ "resolve-from": "^4.0.0" } }, + "import-lazy": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", + "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=", + "dev": true + }, "imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -3322,6 +3649,15 @@ "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==", "dev": true }, + "is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "dev": true, + "requires": { + "ci-info": "^2.0.0" + } + }, "is-date-object": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", @@ -3352,12 +3688,40 @@ "is-extglob": "^2.1.1" } }, + "is-installed-globally": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.3.2.tgz", + "integrity": "sha512-wZ8x1js7Ia0kecP/CHM/3ABkAmujX7WPvQk6uu3Fly/Mk44pySulQpnHG46OMjHGXApINnV4QhY3SWnECO2z5g==", + "dev": true, + "requires": { + "global-dirs": "^2.0.1", + "is-path-inside": "^3.0.1" + } + }, + "is-npm": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-4.0.0.tgz", + "integrity": "sha512-96ECIfh9xtDDlPylNPXhzjsykHsMJZ18ASpaWzQyBr4YRTcVjUvzaHayDAES2oU/3KpljhHUjtSRNiDwi0F0ig==", + "dev": true + }, "is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true }, + "is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true + }, + "is-path-inside": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.2.tgz", + "integrity": "sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg==", + "dev": true + }, "is-promise": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", @@ -3409,6 +3773,12 @@ "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", "dev": true }, + "is-yarn-global": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz", + "integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==", + "dev": true + }, "isarray": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", @@ -3661,6 +4031,12 @@ "bignumber.js": "^7.0.0" } }, + "json-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", + "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=", + "dev": true + }, "json-schema": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", @@ -3735,6 +4111,24 @@ "safe-buffer": "^5.0.1" } }, + "keyv": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", + "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", + "dev": true, + "requires": { + "json-buffer": "3.0.0" + } + }, + "latest-version": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz", + "integrity": "sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==", + "dev": true, + "requires": { + "package-json": "^6.3.0" + } + }, "leven": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz", @@ -3862,6 +4256,12 @@ "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" }, + "lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "dev": true + }, "lru-cache": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.5.2.tgz", @@ -3923,6 +4323,12 @@ "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "dev": true }, + "mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "dev": true + }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", @@ -4503,6 +4909,65 @@ "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz", "integrity": "sha1-sEDrCSOWivq/jTL7HxfxFn/auQc=" }, + "nodemon": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.6.tgz", + "integrity": "sha512-4I3YDSKXg6ltYpcnZeHompqac4E6JeAMpGm8tJnB9Y3T0ehasLa4139dJOcCrB93HHrUMsCrKtoAlXTqT5n4AQ==", + "dev": true, + "requires": { + "chokidar": "^3.2.2", + "debug": "^3.2.6", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.0.4", + "pstree.remy": "^1.1.7", + "semver": "^5.7.1", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.3", + "update-notifier": "^4.1.0" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "nopt": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", + "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=", + "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", @@ -4521,6 +4986,12 @@ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true }, + "normalize-url": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz", + "integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==", + "dev": true + }, "npm-bundled": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.0.5.tgz", @@ -4950,6 +5421,12 @@ "os-tmpdir": "^1.0.0" } }, + "p-cancelable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", + "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==", + "dev": true + }, "p-defer": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-3.0.0.tgz", @@ -5008,6 +5485,26 @@ } } }, + "package-json": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz", + "integrity": "sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==", + "dev": true, + "requires": { + "got": "^9.6.0", + "registry-auth-token": "^4.0.0", + "registry-url": "^5.0.0", + "semver": "^6.2.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, "packet-reader": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-0.3.1.tgz", @@ -5302,6 +5799,12 @@ "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", "dev": true }, + "prepend-http": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", + "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", + "dev": true + }, "process-nextick-args": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", @@ -5371,6 +5874,12 @@ "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.31.tgz", "integrity": "sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw==" }, + "pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", + "dev": true + }, "pump": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", @@ -5386,6 +5895,15 @@ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" }, + "pupa": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.1.1.tgz", + "integrity": "sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==", + "dev": true, + "requires": { + "escape-goat": "^2.0.0" + } + }, "qs": { "version": "6.2.3", "resolved": "https://registry.npmjs.org/qs/-/qs-6.2.3.tgz", @@ -5532,6 +6050,24 @@ "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", "dev": true }, + "registry-auth-token": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.1.tgz", + "integrity": "sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw==", + "dev": true, + "requires": { + "rc": "^1.2.8" + } + }, + "registry-url": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz", + "integrity": "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==", + "dev": true, + "requires": { + "rc": "^1.2.8" + } + }, "release-zalgo": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", @@ -5605,6 +6141,15 @@ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true }, + "responselike": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", + "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", + "dev": true, + "requires": { + "lowercase-keys": "^1.0.0" + } + }, "restore-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", @@ -5678,6 +6223,23 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-4.3.2.tgz", "integrity": "sha1-x6BxWKgL7dBSNVt3DYLWZA+AO+c=" }, + "semver-diff": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz", + "integrity": "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==", + "dev": true, + "requires": { + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, "send": { "version": "0.16.2", "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", @@ -6199,6 +6761,12 @@ "bintrees": "1.0.1" } }, + "term-size": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.1.tgz", + "integrity": "sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==", + "dev": true + }, "test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", @@ -6285,6 +6853,12 @@ "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", "dev": true }, + "to-readable-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", + "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==", + "dev": true + }, "to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -6302,6 +6876,15 @@ "optimist": "0.3.5" } }, + "touch": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", + "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", + "dev": true, + "requires": { + "nopt": "~1.0.10" + } + }, "tough-cookie": { "version": "2.4.3", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", @@ -6381,16 +6964,117 @@ "is-typedarray": "^1.0.0" } }, + "undefsafe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.3.tgz", + "integrity": "sha512-nrXZwwXrD/T/JXeygJqdCO6NZZ1L66HrxM/Z7mIq2oPanoN0F1nLx3lwJMu6AwJY69hdixaFQOuoYsMjE5/C2A==", + "dev": true, + "requires": { + "debug": "^2.2.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, "underscore": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag=" }, + "unique-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", + "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", + "dev": true, + "requires": { + "crypto-random-string": "^2.0.0" + } + }, "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" }, + "update-notifier": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-4.1.3.tgz", + "integrity": "sha512-Yld6Z0RyCYGB6ckIjffGOSOmHXj1gMeE7aROz4MG+XMkmixBX4jUngrGXNYz7wPKBmtoD4MnBa2Anu7RSKht/A==", + "dev": true, + "requires": { + "boxen": "^4.2.0", + "chalk": "^3.0.0", + "configstore": "^5.0.1", + "has-yarn": "^2.1.0", + "import-lazy": "^2.1.0", + "is-ci": "^2.0.0", + "is-installed-globally": "^0.3.1", + "is-npm": "^4.0.0", + "is-yarn-global": "^0.3.0", + "latest-version": "^5.0.0", + "pupa": "^2.0.1", + "semver-diff": "^3.1.1", + "xdg-basedir": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, "uri-js": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", @@ -6399,6 +7083,15 @@ "punycode": "^2.1.0" } }, + "url-parse-lax": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", + "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", + "dev": true, + "requires": { + "prepend-http": "^2.0.0" + } + }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -6473,6 +7166,49 @@ "string-width": "^1.0.2 || 2" } }, + "widest-line": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", + "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", + "dev": true, + "requires": { + "string-width": "^4.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + } + } + }, "word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", @@ -6572,6 +7308,12 @@ "typedarray-to-buffer": "^3.1.5" } }, + "xdg-basedir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", + "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", + "dev": true + }, "xtend": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", diff --git a/package.json b/package.json index cbd698f5..a5045aca 100644 --- a/package.json +++ b/package.json @@ -71,6 +71,7 @@ "libxmljs": "^0.19.7", "mocha": "^7.2.0", "mockdate": "^2.0.2", + "nodemon": "^2.0.6", "nyc": "^15.0.0", "pino-pretty": "^4.0.0", "shapefile": "0.3.0", @@ -87,8 +88,7 @@ "posttest": "npm run test:teardown", "test:teardown": "NODE_ENV=test node test teardown", "cover": "nyc --reporter=lcov npm test", - "test:docker": "docker run -e \"NODEJS_VERSION=$NODE_VERSION\" -v `pwd`:/srv $DOCKER_IMAGE bash docker/scripts/test-setup.sh && docker ps --filter status=dead --filter status=exited -aq | xargs docker rm -v", - "docker:bash": "docker run -it -v `pwd`:/srv $DOCKER_IMAGE bash" + "dev": "NODE_ENV=development nodemon app.js" }, "engines": { "node": "^10.15.1", diff --git a/private b/private new file mode 160000 index 00000000..2e20a0f0 --- /dev/null +++ b/private @@ -0,0 +1 @@ +Subproject commit 2e20a0f0206fe7121162ef2db2ea292d971aabf0 diff --git a/test/acceptance/app-configuration-test.js b/test/acceptance/app-configuration-test.js index 966ad244..0b465aac 100644 --- a/test/acceptance/app-configuration-test.js +++ b/test/acceptance/app-configuration-test.js @@ -6,20 +6,11 @@ var server = require('../../lib/server')(); var assert = require('../support/assert'); const accessControlHeaders = [ - 'X-Requested-With', - 'X-Prototype-Version', - 'X-CSRF-Token', - 'Authorization', - 'Carto-Event', - 'Carto-Event-Source', - 'Carto-Event-Group-Id' + '*' ].join(', '); const exposedHeaders = [ - 'Carto-Rate-Limit-Limit', - 'Carto-Rate-Limit-Remaining', - 'Carto-Rate-Limit-Reset', - 'Retry-After' + '*' ].join(', '); describe('app-configuration', function () { diff --git a/test/acceptance/batch/batch-drain-test.js b/test/acceptance/batch/batch-drain-test.js index d6d683ea..892bfeef 100644 --- a/test/acceptance/batch/batch-drain-test.js +++ b/test/acceptance/batch/batch-drain-test.js @@ -13,8 +13,11 @@ var JobService = require('../../../lib/batch/job-service'); var JobCanceller = require('../../../lib/batch/job-canceller'); var metadataBackend = require('cartodb-redis')({ pool: redisUtils.getPool() }); +const TEST_USER_ID = 1; +const TEST_USER = global.settings.db_user.replace('<%= user_id %>', TEST_USER_ID); +const TEST_DB = global.settings.db_base_name.replace('<%= user_id %>', TEST_USER_ID); + describe('batch module', function () { - var dbInstance = 'localhost'; var username = 'vizzuality'; var pool = redisUtils.getPool(); var logger = new Logger(); @@ -39,11 +42,11 @@ describe('batch module', function () { 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' + host: global.settings.db_host, + dbname: TEST_DB, + dbuser: TEST_USER, + port: global.settings.db_batch_port, + pass: global.settings.db_user_pass }; jobService.create(data, function (err, job) { diff --git a/test/acceptance/copy-endpoints-test.js b/test/acceptance/copy-endpoints-test.js index 3d0cf778..5c6a4aa8 100644 --- a/test/acceptance/copy-endpoints-test.js +++ b/test/acceptance/copy-endpoints-test.js @@ -21,6 +21,10 @@ if (global.settings.statsd) { const statsClient = StatsClient.getInstance(global.settings.statsd); const server = require('../../lib/server')(statsClient); +const TEST_USER_ID = 1; +const TEST_USER = 'postgres'; +const TEST_DB = global.settings.db_base_name.replace('<%= user_id %>', TEST_USER_ID); + // 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; @@ -43,10 +47,10 @@ const assertCanReuseCanceledConnection = function (done) { describe('copy-endpoints', function () { before(function () { this.client = new Client({ - user: 'postgres', - host: 'localhost', - database: 'cartodb_test_user_1_db', - port: 5432 + user: TEST_USER, + host: global.settings.db_host, + database: TEST_DB, + port: global.settings.db_batch_port }); this.client.connect(); }); diff --git a/test/acceptance/rate-limit-test.js b/test/acceptance/rate-limit-test.js index 3130a1a9..0219c8c6 100644 --- a/test/acceptance/rate-limit-test.js +++ b/test/acceptance/rate-limit-test.js @@ -77,7 +77,10 @@ describe('rate limit', function () { global.settings.ratelimits.endpoints.query = true; server = app(); - redisClient = redis.createClient(global.settings.redis_port); + redisClient = redis.createClient({ + port: global.settings.redis_port, + host: global.settings.redis_host + }); const count = 1; const period = 1; diff --git a/test/helper.js b/test/helper.js index a19de981..872b3556 100644 --- a/test/helper.js +++ b/test/helper.js @@ -1,4 +1,10 @@ 'use strict'; -global.settings = require('../config/environments/test'); +let configFileName = process.env.NODE_ENV; +if (process.env.CARTO_SQL_API_ENV_BASED_CONF) { + // we override the file with the one with env vars + configFileName = 'config'; +} + +global.settings = require(`../config/environments/${configFileName}.js`); process.env.NODE_ENV = 'test'; diff --git a/test/index.js b/test/index.js index b30aa3e8..8821bce7 100644 --- a/test/index.js +++ b/test/index.js @@ -1,7 +1,7 @@ 'use strict'; const util = require('util'); -const path = require('path'); +// const path = require('path'); const exec = util.promisify(require('child_process').exec); if (!process.env.NODE_ENV) { @@ -9,13 +9,15 @@ if (!process.env.NODE_ENV) { process.exit(1); } -const environment = require(`../config/environments/${process.env.NODE_ENV}.js`); +let configFileName = process.env.NODE_ENV; +if (process.env.CARTO_SQL_API_ENV_BASED_CONF) { + // we override the file with the one with env vars + configFileName = 'config'; +} + +const environment = require(`../config/environments/${configFileName}.js`); const REDIS_PORT = environment.redis_port; -const REDIS_CELL_PATH = path.resolve( - process.platform === 'darwin' - ? './test/support/libredis_cell.dylib' - : './test/support/libredis_cell.so' -); +const REDIS_HOST = environment.redis_host; const TEST_USER_ID = 1; const TEST_USER = environment.db_user.replace('<%= user_id %>', TEST_USER_ID); @@ -24,29 +26,22 @@ const PUBLIC_USER = environment.db_pubuser; const PUBLIC_USER_PASSWORD = environment.db_pubuser_pass; const TEST_DB = environment.db_base_name.replace('<%= user_id %>', TEST_USER_ID); const PGHOST = environment.db_host; - -async function startRedis () { - await exec(`redis-server --port ${REDIS_PORT} --loadmodule ${REDIS_CELL_PATH} --logfile ${__dirname}/redis-server.log --daemonize yes`); -} - -async function stopRedis () { - await exec(`redis-cli -p ${REDIS_PORT} shutdown`); -} +const PGPORT = environment.db_port; async function dropDatabase () { - await exec(`dropdb --if-exists ${TEST_DB}`, { + await exec(`dropdb -p "${PGPORT}" -h "${PGHOST}" --if-exists ${TEST_DB}`, { env: Object.assign({ PGUSER: 'postgres' }, process.env) }); } async function createDatabase () { - await exec(`createdb -T template_postgis -EUTF8 "${TEST_DB}"`, { + await exec(`createdb -p "${PGPORT}" -h "${PGHOST}" -T template_postgis -EUTF8 "${TEST_DB}"`, { env: Object.assign({ PGUSER: 'postgres' }, process.env) }); } async function createDatabaseExtension () { - await exec(`psql -c "CREATE EXTENSION IF NOT EXISTS cartodb CASCADE;" ${TEST_DB}`, { + await exec(`psql -p "${PGPORT}" -h "${PGHOST}" -c "CREATE EXTENSION IF NOT EXISTS cartodb CASCADE;" ${TEST_DB}`, { env: Object.assign({ PGUSER: 'postgres' }, process.env) }); } @@ -64,7 +59,7 @@ async function populateDatabase () { sed -e "s/:PUBLICPASS/${PUBLIC_USER_PASSWORD}/g" | sed -e "s/:TESTUSER/${TEST_USER}/g" | sed -e "s/:TESTPASS/${TEST_PASSWORD}/g" | - PGOPTIONS='--client-min-messages=WARNING' psql -q -v ON_ERROR_STOP=1 ${TEST_DB} + PGOPTIONS='--client-min-messages=WARNING' psql -h "${PGHOST}" -p "${PGPORT}" -q -v ON_ERROR_STOP=1 ${TEST_DB} `; await exec(populateDatabaseCmd, { @@ -140,8 +135,8 @@ async function populateRedis () { time sometime `; - await exec(`echo "${commands}" | redis-cli -p ${REDIS_PORT} -n 5`); - await exec(`echo "${oauthCommands}" | redis-cli -p ${REDIS_PORT} -n 3`); + await exec(`echo "${commands}" | redis-cli -h ${REDIS_HOST} -p ${REDIS_PORT} -n 5`); + await exec(`echo "${oauthCommands}" | redis-cli -h ${REDIS_HOST} -p ${REDIS_PORT} -n 3`); } async function main (args) { @@ -150,7 +145,6 @@ async function main (args) { try { switch (args[0]) { case 'setup': - await startRedis(); await populateRedis(); await dropDatabase(); await createDatabase(); @@ -158,7 +152,6 @@ async function main (args) { await populateDatabase(); break; case 'teardown': - await stopRedis(); break; default: throw new Error('Missing "mode" argument. Valid ones: "setup" or "teardown"'); diff --git a/test/integration/batch/job-canceller-test.js b/test/integration/batch/job-canceller-test.js index ee32f05c..f8805225 100644 --- a/test/integration/batch/job-canceller-test.js +++ b/test/integration/batch/job-canceller-test.js @@ -22,9 +22,12 @@ var jobPublisher = new JobPublisher(redisUtils.getPool()); var jobQueue = new JobQueue(metadataBackend, jobPublisher, logger); var jobBackend = new JobBackend(metadataBackend, jobQueue, logger); +const TEST_USER_ID = 1; +const TEST_USER = global.settings.db_user.replace('<%= user_id %>', TEST_USER_ID); +const TEST_DB = global.settings.db_base_name.replace('<%= user_id %>', TEST_USER_ID); + 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 @@ -66,11 +69,12 @@ function createWadusJob (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' + host: global.settings.db_host, + dbname: TEST_DB, + dbuser: TEST_USER, + port: global.settings.db_batch_port, + pass: global.settings.db_user_pass + }))); } diff --git a/test/integration/batch/job-runner-test.js b/test/integration/batch/job-runner-test.js index 56f45af8..72774ab1 100644 --- a/test/integration/batch/job-runner-test.js +++ b/test/integration/batch/job-runner-test.js @@ -30,17 +30,20 @@ var queryRunner = new QueryRunner(userDatabaseMetadataService, logger); var StatsD = require('node-statsd').StatsD; var statsdClient = new StatsD(global.settings.statsd); +const TEST_USER_ID = 1; +const TEST_USER = global.settings.db_user.replace('<%= user_id %>', TEST_USER_ID); +const TEST_DB = global.settings.db_base_name.replace('<%= user_id %>', TEST_USER_ID); + 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' + host: global.settings.db_host, + dbname: TEST_DB, + dbuser: TEST_USER, + port: global.settings.db_batch_port, + pass: global.settings.db_user_pass }; describe('job runner', function () { diff --git a/test/integration/batch/job-service-test.js b/test/integration/batch/job-service-test.js index c1bba972..8a9d86b6 100644 --- a/test/integration/batch/job-service-test.js +++ b/test/integration/batch/job-service-test.js @@ -23,18 +23,20 @@ var jobQueue = new JobQueue(metadataBackend, jobPublisher, logger); var jobBackend = new JobBackend(metadataBackend, jobQueue, logger); var jobCanceller = new JobCanceller(); +const TEST_USER_ID = 1; +const TEST_USER = global.settings.db_user.replace('<%= user_id %>', TEST_USER_ID); +const TEST_DB = global.settings.db_base_name.replace('<%= user_id %>', TEST_USER_ID); + 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' - + host: global.settings.db_host, + dbname: TEST_DB, + dbuser: TEST_USER, + port: global.settings.db_batch_port, + pass: global.settings.db_user_pass }; function createWadusDataJob () {