diff --git a/.travis.yml b/.travis.yml index 8f3993a2..b946378f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,27 +1,14 @@ +sudo: required dist: trusty -addons: - postgresql: "9.5" - apt: - sources: - - ubuntu-toolchain-r-test - packages: - - postgresql-9.5-postgis-2.3 - - postgresql-plpython-9.5 - - pkg-config - - libcairo2-dev - - libjpeg8-dev - - libgif-dev - - libpango1.0-dev - - g++-4.9 + +services: + - docker before_install: - - createdb template_postgis - - createuser publicuser - - psql -c "CREATE EXTENSION postgis" template_postgis + - docker pull cartoimages/windshaft-carto-testing -env: - - NPROCS=1 JOBS=1 PGUSER=postgres CXX=g++-4.9 +script: + - docker run -v `pwd`:/srv cartoimages/windshaft-carto-testing + +language: generic -language: node_js -node_js: - - "6" diff --git a/config/environments/development.js.example b/config/environments/development.js.example index 48d03592..91cf2a39 100644 --- a/config/environments/development.js.example +++ b/config/environments/development.js.example @@ -107,6 +107,20 @@ var config = { // Milliseconds since last access before renderer cache item expires cache_ttl: 60000, statsInterval: 5000, // milliseconds between each report to statsd about number of renderers and mapnik pool status + mvt: { + //If enabled, MVTs will be generated with PostGIS directly, instead of using Mapnik, + //PostGIS 2.4 is required for this to work + //If disabled it will use Mapnik MVT generation + usePostGIS: false, + dbPoolParams: { + // maximum number of resources to create at any given time + size: 16, + // max milliseconds a resource can go unused before it should be destroyed + idleTimeout: 3000, + // frequency to check for idle resources + reapInterval: 1000 + } + }, mapnik: { // The size of the pool of internal mapnik backend // This pool size is per mapnik renderer created in Windshaft's RendererFactory diff --git a/config/environments/production.js.example b/config/environments/production.js.example index ba7fec6f..7d7a00cb 100644 --- a/config/environments/production.js.example +++ b/config/environments/production.js.example @@ -101,6 +101,20 @@ var config = { // Milliseconds since last access before renderer cache item expires cache_ttl: 60000, statsInterval: 5000, // milliseconds between each report to statsd about number of renderers and mapnik pool status + mvt: { + //If enabled, MVTs will be generated with PostGIS directly, instead of using Mapnik, + //PostGIS 2.4 is required for this to work + //If disabled it will use Mapnik MVT generation + usePostGIS: false, + dbPoolParams: { + // maximum number of resources to create at any given time + size: 16, + // max milliseconds a resource can go unused before it should be destroyed + idleTimeout: 3000, + // frequency to check for idle resources + reapInterval: 1000 + } + }, mapnik: { // The size of the pool of internal mapnik backend // This pool size is per mapnik renderer created in Windshaft's RendererFactory diff --git a/config/environments/staging.js.example b/config/environments/staging.js.example index 1792779c..3d1098fb 100644 --- a/config/environments/staging.js.example +++ b/config/environments/staging.js.example @@ -101,6 +101,20 @@ var config = { // Milliseconds since last access before renderer cache item expires cache_ttl: 60000, statsInterval: 5000, // milliseconds between each report to statsd about number of renderers and mapnik pool status + mvt: { + //If enabled, MVTs will be generated with PostGIS directly, instead of using Mapnik, + //PostGIS 2.4 is required for this to work + //If disabled it will use Mapnik MVT generation + usePostGIS: false, + dbPoolParams: { + // maximum number of resources to create at any given time + size: 16, + // max milliseconds a resource can go unused before it should be destroyed + idleTimeout: 3000, + // frequency to check for idle resources + reapInterval: 1000 + } + }, mapnik: { // The size of the pool of internal mapnik backend // This pool size is per mapnik renderer created in Windshaft's RendererFactory diff --git a/config/environments/test.js.example b/config/environments/test.js.example index 374467c0..ea788a22 100644 --- a/config/environments/test.js.example +++ b/config/environments/test.js.example @@ -100,6 +100,20 @@ var config = { // Milliseconds since last access before renderer cache item expires cache_ttl: 60000, statsInterval: 5000, // milliseconds between each report to statsd about number of renderers and mapnik pool status + mvt: { + //If enabled, MVTs will be generated with PostGIS directly, instead of using Mapnik, + //PostGIS 2.4 is required for this to work + //If disabled it will use Mapnik MVT generation + usePostGIS: false, + dbPoolParams: { + // maximum number of resources to create at any given time + size: 16, + // max milliseconds a resource can go unused before it should be destroyed + idleTimeout: 3000, + // frequency to check for idle resources + reapInterval: 1000 + } + }, mapnik: { // The size of the pool of internal mapnik backend // This pool size is per mapnik renderer created in Windshaft's RendererFactory diff --git a/docker-test.sh b/docker-test.sh new file mode 100644 index 00000000..6bf5360e --- /dev/null +++ b/docker-test.sh @@ -0,0 +1,11 @@ +export NPROCS=1 && export JOBS=1 && export CXX=g++-4.9 && export PGUSER=postgres + +npm install -g yarn@0.27.5 +yarn + + /etc/init.d/postgresql start + +createdb template_postgis && createuser publicuser +psql -c "CREATE EXTENSION postgis" template_postgis + +npm test \ No newline at end of file diff --git a/lib/cartodb/controllers/layergroup.js b/lib/cartodb/controllers/layergroup.js index a814ee43..c5e4c53a 100644 --- a/lib/cartodb/controllers/layergroup.js +++ b/lib/cartodb/controllers/layergroup.js @@ -294,6 +294,10 @@ LayergroupController.prototype.tileOrLayer = function (req, res, next) { ); }; +function getStatusCode(tile, format){ + return tile.length===0 && format==='mvt'? 204:200; +} + // This function is meant for being called as the very last // step by all endpoints serving tiles or grids LayergroupController.prototype.finalizeGetTileOrGrid = function(err, req, res, tile, headers, next) { @@ -331,7 +335,7 @@ LayergroupController.prototype.finalizeGetTileOrGrid = function(err, req, res, t global.statsClient.increment('windshaft.tiles.error'); global.statsClient.increment('windshaft.tiles.' + formatStat + '.error'); } else { - this.sendResponse(req, res, tile, 200, headers); + this.sendResponse(req, res, tile, getStatusCode(tile, formatStat), headers); global.statsClient.increment('windshaft.tiles.success'); global.statsClient.increment('windshaft.tiles.' + formatStat + '.success'); } diff --git a/lib/cartodb/server.js b/lib/cartodb/server.js index 5f47b99f..1ce6439f 100644 --- a/lib/cartodb/server.js +++ b/lib/cartodb/server.js @@ -157,7 +157,8 @@ module.exports = function(serverOptions) { grainstore: serverOptions.grainstore, mapnik: serverOptions.renderer.mapnik }, - http: serverOptions.renderer.http + http: serverOptions.renderer.http, + mvt: serverOptions.renderer.mvt }); // initialize render cache diff --git a/lib/cartodb/server_options.js b/lib/cartodb/server_options.js index 6d8575a7..25e60c2f 100644 --- a/lib/cartodb/server_options.js +++ b/lib/cartodb/server_options.js @@ -81,6 +81,7 @@ module.exports = { statsInterval: rendererConfig.statsInterval }, renderer: { + mvt: rendererConfig.mvt, mapnik: _.defaults(rendererConfig.mapnik, { geojson: { dbPoolParams: { diff --git a/package.json b/package.json index 530a4a1f..b65a64d1 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ "step-profiler": "~0.3.0", "turbo-carto": "0.20.1", "underscore": "~1.6.0", - "windshaft": "3.3.3", + "windshaft": "cartodb/windshaft", "yargs": "~5.0.0" }, "devDependencies": { @@ -58,7 +58,13 @@ "scripts": { "lint": "jshint lib test", "preinstall": "make pre-install", - "test": "make test-all" + "test": "make test-all", + "update-internal-deps": "rm -rf node_modules && rm -f yarn.lock && yarn", + "docker-install": "sudo apt install docker.io && sudo usermod -aG docker $(whoami)", + "docker-pull": "docker pull cartoimages/windshaft-testing", + "docker-test": "docker run -v `pwd`:/srv cartoimages/windshaft-testing bash docker-test.sh", + "docker-bash": "docker run -it -v `pwd`:/srv cartoimages/windshaft-testing bash", + "docker-publish": "docker push cartoimages/windshaft-carto-testing" }, "engines": { "node": ">=6.9", diff --git a/run_tests.sh b/run_tests.sh index 92e8621b..d1c63614 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -137,7 +137,7 @@ if test x"$OPT_COVERAGE" = xyes; then ./node_modules/.bin/istanbul cover node_modules/.bin/_mocha -- -u tdd -t 5000 ${TESTS} else echo "Running tests" - mocha -u tdd -t 5000 ${TESTS} + ./node_modules/.bin/_mocha -c -u tdd -t 5000 ${TESTS} fi ret=$? diff --git a/test/acceptance/analysis/analysis-layers-geojson.js b/test/acceptance/analysis/analysis-layers-geojson.js deleted file mode 100644 index 59a0900b..00000000 --- a/test/acceptance/analysis/analysis-layers-geojson.js +++ /dev/null @@ -1,81 +0,0 @@ -require('../../support/test_helper'); - -var assert = require('../../support/assert'); -var TestClient = require('../../support/test-client'); - -describe('analysis-layers-dataviews-geojson', function() { - - function createMapConfig(layers, dataviews, analysis) { - return { - version: '1.5.0', - layers: layers, - dataviews: dataviews || {}, - analyses: analysis || [] - }; - } - - var CARTOCSS = [ - "#points {", - " marker-fill-opacity: 1.0;", - " marker-line-color: #FFF;", - " marker-line-width: 0.5;", - " marker-line-opacity: 1.0;", - " marker-placement: point;", - " marker-type: ellipse;", - " marker-width: 8;", - " marker-fill: red;", - " marker-allow-overlap: true;", - "}" - ].join('\n'); - - var mapConfig = createMapConfig( - [ - { - "type": "cartodb", - "options": { - "source": { - "id": "2570e105-7b37-40d2-bdf4-1af889598745" - }, - "cartocss": CARTOCSS, - "cartocss_version": "2.3.0" - } - } - ], - { - pop_max_histogram: { - source: { - id: '2570e105-7b37-40d2-bdf4-1af889598745' - }, - type: 'histogram', - options: { - column: 'pop_max' - } - } - }, - [ - { - "id": "2570e105-7b37-40d2-bdf4-1af889598745", - "type": "source", - "params": { - "query": "select * from populated_places_simple_reduced" - } - } - ] - ); - - it('should get pop_max column from dataview', function(done) { - var testClient = new TestClient(mapConfig, 1234); - - testClient.getTile(0, 0, 0, {format: 'geojson', layers: 0}, function(err, res, geojson) { - assert.ok(!err, err); - - assert.ok(Array.isArray(geojson.features)); - assert.ok(geojson.features.length > 0); - var feature = geojson.features[0]; - assert.ok(feature.properties.hasOwnProperty('pop_max'), 'Missing pop_max property'); - - testClient.drain(done); - }); - }); - -}); diff --git a/test/acceptance/buffer-size-format.js b/test/acceptance/buffer-size-format.js index c32f62d1..2366d035 100644 --- a/test/acceptance/buffer-size-format.js +++ b/test/acceptance/buffer-size-format.js @@ -3,6 +3,7 @@ require('../support/test_helper'); var fs = require('fs'); var assert = require('../support/assert'); var TestClient = require('../support/test-client'); +var serverOptions = require('../../lib/cartodb/server_options'); var mapnik = require('windshaft').mapnik; var IMAGE_TOLERANCE_PER_MIL = 5; @@ -124,24 +125,37 @@ describe('buffer size per format', function () { } ]; + afterEach(function(done) { + if (this.testClient) { + return this.testClient.drain(done); + } + return done(); + }); + + const originalUsePostGIS = serverOptions.renderer.mvt.usePostGIS; testCases.forEach(function (test) { - it(test.desc, function (done) { - var testClient = new TestClient(test.mapConfig, 1234); - var coords = test.coords; - var options = { - format: test.format, - layers: test.layers - }; - testClient.getTile(coords.z, coords.x, coords.y, options, function (err, res, tile) { - assert.ifError(err); - // To generate images use: - // tile.save(test.fixturePath); - test.assert(tile, function (err) { + var testFn = (usePostGIS) => { + it(test.desc, function (done) { + serverOptions.renderer.mvt.usePostGIS = usePostGIS; + this.testClient = new TestClient(test.mapConfig, 1234); + serverOptions.renderer.mvt.usePostGIS = originalUsePostGIS; + var coords = test.coords; + var options = { + format: test.format, + layers: test.layers + }; + this.testClient.getTile(coords.z, coords.x, coords.y, options, function (err, res, tile) { assert.ifError(err); - testClient.drain(done); + // To generate images use: + // tile.save(test.fixturePath); + test.assert(tile, done); }); }); - }); + }; + if (test.format === 'mvt'){ + testFn(true); + } + testFn(false); }); }); @@ -260,23 +274,27 @@ describe('buffer size per format for named maps', function () { } ]; + afterEach(function(done) { + if (this.testClient) { + return this.testClient.drain(done); + } + return done(); + }); + testCases.forEach(function (test) { it(test.desc, function (done) { - var testClient = new TestClient(test.template, 1234); + this.testClient = new TestClient(test.template, 1234); var coords = test.coords; var options = { format: test.format, placeholders: test.placeholders, layers: test.layers }; - testClient.getTile(coords.z, coords.x, coords.y, options, function (err, res, tile) { + this.testClient.getTile(coords.z, coords.x, coords.y, options, function (err, res, tile) { assert.ifError(err); // To generate images use: //tile.save('./test/fixtures/buffer-size/tile-7.64.48-buffer-size-0-test.png'); - test.assert(tile, function (err) { - assert.ifError(err); - testClient.drain(done); - }); + test.assert(tile, done); }); }); }); @@ -416,26 +434,40 @@ describe('buffer size per format for named maps w/o placeholders', function () { ]; + afterEach(function(done) { + if (this.testClient) { + return this.testClient.drain(done); + } + return done(); + }); + + const originalUsePostGIS = serverOptions.renderer.mvt.usePostGIS; testCases.forEach(function (test) { - it(test.desc, function (done) { - var testClient = new TestClient(test.template, 1234); - var coords = test.coords; - var options = { - format: test.format, - placeholders: test.placeholders, - layers: test.layers - }; - testClient.getTile(coords.z, coords.x, coords.y, options, function (err, res, tile) { - assert.ifError(err); - // To generate images use: - //tile.save(test.fixturePath); - // require('fs').writeFileSync(test.fixturePath, JSON.stringify(tile)); - // require('fs').writeFileSync(test.fixturePath, tile.getDataSync()); - test.assert(tile, function (err) { - assert.ifError(err); - testClient.drain(done); + var testFn = (usePostGIS) => { + it(test.desc + `(${usePostGIS? 'PostGIS':'mapnik'})`, function (done) { + serverOptions.renderer.mvt.usePostGIS = usePostGIS; + test.template.name += '_1'; + this.testClient = new TestClient(test.template, 1234); + serverOptions.renderer.mvt.usePostGIS = originalUsePostGIS; + var coords = test.coords; + var options = { + format: test.format, + placeholders: test.placeholders, + layers: test.layers + }; + this.testClient.getTile(coords.z, coords.x, coords.y, options, function (err, res, tile) { + assert.ifError(err); + // To generate images use: + //tile.save(test.fixturePath); + // require('fs').writeFileSync(test.fixturePath, JSON.stringify(tile)); + // require('fs').writeFileSync(test.fixturePath, tile.getDataSync()); + test.assert(tile, done); + }); }); - }); - }); + }; + if (test.format === 'mvt'){ + testFn(true); + } + testFn(false); }); }); diff --git a/test/acceptance/geojson-renderer.js b/test/acceptance/geojson-renderer.js deleted file mode 100644 index 089e09b5..00000000 --- a/test/acceptance/geojson-renderer.js +++ /dev/null @@ -1,343 +0,0 @@ -require('../support/test_helper'); - -var assert = require('../support/assert'); -var TestClient = require('../support/test-client'); - - -describe('use only needed columns', function() { - - function getFeatureByCartodbId(features, cartodbId) { - for (var i = 0, len = features.length; i < len; i++) { - if (features[i].properties.cartodb_id === cartodbId) { - return features[i]; - } - } - return {}; - } - - var options = { format: 'geojson', layer: 0 }; - - afterEach(function(done) { - if (this.testClient) { - this.testClient.drain(done); - } else { - done(); - } - }); - - it('with aggregation widget, interactivity and cartocss columns', function(done) { - var widgetMapConfig = { - version: '1.5.0', - layers: [{ - type: 'mapnik', - options: { - sql: 'select * from populated_places_simple_reduced', - cartocss: '#layer0 { marker-fill: red; marker-width: 10; [name="Madrid"] { marker-fill: green; } }', - cartocss_version: '2.0.1', - widgets: { - adm0name: { - type: 'aggregation', - options: { - column: 'adm0name', - aggregation: 'sum', - aggregationColumn: 'pop_max' - } - } - }, - interactivity: "cartodb_id,pop_min" - } - }] - }; - - this.testClient = new TestClient(widgetMapConfig); - this.testClient.getTile(0, 0, 0, options, function (err, res, geojsonTile) { - assert.ok(!err, err); - assert.deepEqual(getFeatureByCartodbId(geojsonTile.features, 1109).properties, { - cartodb_id: 1109, - name: 'Mardin', - adm0name: 'Turkey', - pop_max: 71373, - pop_min: 57586 - }); - done(); - }); - }); - - it('should not duplicate columns', function(done) { - var widgetMapConfig = { - version: '1.5.0', - layers: [{ - type: 'mapnik', - options: { - sql: 'select * from populated_places_simple_reduced', - cartocss: ['#layer0 {', - 'marker-fill: red;', - 'marker-width: 10;', - '[name="Madrid"] { marker-fill: green; } ', - '[pop_max>100000] { marker-fill: black; } ', - '}'].join('\n'), - cartocss_version: '2.3.0', - widgets: { - adm0name: { - type: 'aggregation', - options: { - column: 'adm0name', - aggregation: 'sum', - aggregationColumn: 'pop_max' - } - } - }, - interactivity: "cartodb_id,pop_max" - } - }] - }; - - this.testClient = new TestClient(widgetMapConfig); - this.testClient.getTile(0, 0, 0, options, function (err, res, geojsonTile) { - assert.ok(!err, err); - assert.deepEqual(getFeatureByCartodbId(geojsonTile.features, 1109).properties, { - cartodb_id: 1109, - name: 'Mardin', - adm0name: 'Turkey', - pop_max: 71373 - }); - done(); - }); - }); - - it('with formula widget, no interactivity and no cartocss columns', function(done) { - var formulaWidgetMapConfig = { - version: '1.5.0', - layers: [{ - type: 'mapnik', - options: { - sql: 'select * from populated_places_simple_reduced where pop_max > 0 and pop_max < 600000', - cartocss: '#layer0 { marker-fill: red; marker-width: 10; }', - cartocss_version: '2.0.1', - interactivity: 'cartodb_id', - widgets: { - pop_max_f: { - type: 'formula', - options: { - column: 'pop_max', - operation: 'count' - } - } - } - } - }] - }; - - this.testClient = new TestClient(formulaWidgetMapConfig); - this.testClient.getTile(0, 0, 0, options, function (err, res, geojsonTile) { - assert.ok(!err, err); - assert.deepEqual(getFeatureByCartodbId(geojsonTile.features, 1109).properties, { - cartodb_id: 1109, - pop_max: 71373 - }); - done(); - }); - }); - it('with cartocss with multiple expressions', function(done) { - var formulaWidgetMapConfig = { - version: '1.5.0', - layers: [{ - type: 'mapnik', - options: { - sql: 'select * from populated_places_simple_reduced where pop_max > 0 and pop_max < 600000', - cartocss: '#layer0 { marker-fill: red; marker-width: 10; }' + - '#layer0 { marker-width: 14; [name="Madrid"] { marker-width: 20; } }' + - '#layer0[pop_max>1000] { marker-width: 14; [name="Madrid"] { marker-width: 20; } }' + - '#layer0[adm0name=~".*Turkey*"] { marker-width: 14; [name="Madrid"] { marker-width: 20; } }', - cartocss_version: '2.0.1', - interactivity: 'cartodb_id' - } - }] - }; - - this.testClient = new TestClient(formulaWidgetMapConfig); - this.testClient.getTile(0, 0, 0, options, function (err, res, geojsonTile) { - assert.ok(!err, err); - assert.deepEqual(getFeatureByCartodbId(geojsonTile.features, 1109).properties, { - cartodb_id: 1109, - pop_max:71373, - name:"Mardin", - adm0name:"Turkey" - }); - done(); - }); - }); - - it('should work with mapnik substitution tokens', function(done) { - var cartocss = [ - "#layer {", - " line-width: 2;", - " line-color: #3B3B58;", - " line-opacity: 1;", - " polygon-opacity: 0.7;", - " polygon-fill: ramp([points_count], (#E5F5F9,#99D8C9,#2CA25F))", - "}" - ].join('\n'); - - var sql = [ - 'WITH hgrid AS (', - ' SELECT CDB_HexagonGrid(', - ' ST_Expand(!bbox!, greatest(!pixel_width!,!pixel_height!) * 100),', - ' greatest(!pixel_width!,!pixel_height!) * 100', - ' ) as cell', - ')', - 'SELECT', - ' hgrid.cell as the_geom_webmercator,', - ' count(1) as points_count,', - ' count(1)/power(100 * CDB_XYZ_Resolution(CDB_ZoomFromScale(!scale_denominator!)), 2) as points_density,', - ' 1 as cartodb_id', - 'FROM hgrid, (SELECT * FROM populated_places_simple_reduced) i', - 'where ST_Intersects(i.the_geom_webmercator, hgrid.cell)', - 'GROUP BY hgrid.cell' - ].join('\n'); - - var mapConfig = { - "version": "1.4.0", - "layers": [ - { - "type": 'mapnik', - "options": { - "cartocss_version": '2.3.0', - "sql": sql, - "cartocss": cartocss - } - } - ] - }; - - this.testClient = new TestClient(mapConfig); - this.testClient.getTile(0, 0, 0, { format: 'geojson', layer: 0 }, function(err, res, geojson) { - assert.ok(!err, err); - - assert.ok(geojson); - assert.equal(geojson.features.length, 5); - - done(); - }); - }); - - it('should skip empty and null columns for geojson tiles', function(done) { - - var mapConfig = { - "analyses": [ - { - "id": "a0", - "params": { - "query": "SELECT * FROM test_table" - }, - "type": "source" - } - ], - "dataviews": { - "4e7b0e07-6d21-4b83-9adb-6d7e17eea6ca": { - "options": { - "aggregationColumn": null, - "column": "cartodb_id", - "operation": "avg" - }, - "source": { - "id": "a0" - }, - "type": "formula" - }, - "74f590f8-625c-4e95-922f-34ad3e9919c0": { - "options": { - "aggregation": "sum", - "aggregationColumn": "cartodb_id", - "column": "name" - }, - "source": { - "id": "a0" - }, - "type": "aggregation" - }, - "98a75757-3006-400a-b028-fb613a6c0b69": { - "options": { - "aggregationColumn": null, - "column": "cartodb_id", - "operation": "sum" - }, - "source": { - "id": "a0" - }, - "type": "formula" - }, - "ebbc97b2-87d2-4895-9e1f-2f012df3679d": { - "options": { - "aggregationColumn": null, - "bins": "12", - "column": "cartodb_id" - }, - "source": { - "id": "a0" - }, - "type": "histogram" - }, - "ebc0653f-3581-469c-8b31-c969e440a865": { - "options": { - "aggregationColumn": null, - "column": "cartodb_id", - "operation": "avg" - }, - "source": { - "id": "a0" - }, - "type": "formula" - } - }, - "layers": [ - { - "options": { - "subdomains": "abcd", - "urlTemplate": "http://{s}.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}.png" - }, - "type": "http" - }, - { - "options": { - "attributes": { - "columns": [ - "name", - "address" - ], - "id": "cartodb_id" - }, - "cartocss": "#layer { marker-width: 10; marker-fill: red; }", - "cartocss_version": "2.3.0", - "interactivity": "cartodb_id", - "layer_name": "wadus", - "source": { - "id": "a0" - } - }, - "type": "cartodb" - }, - { - "options": { - "subdomains": "abcd", - "urlTemplate": "http://{s}.basemaps.cartocdn.com/light_only_labels/{z}/{x}/{y}.png" - }, - "type": "http" - } - ] - }; - - this.testClient = new TestClient(mapConfig); - this.testClient.getTile(0, 0, 0, { format: 'geojson', layer: 0 }, function(err, res, geojson) { - assert.ok(!err, err); - - assert.ok(geojson); - assert.equal(geojson.features.length, 5); - - assert.deepEqual(Object.keys(geojson.features[0].properties), ['cartodb_id', 'name']); - - done(); - }); - }); - -}); diff --git a/test/acceptance/mvt.js b/test/acceptance/mvt.js index b5a59cd8..883f050f 100644 --- a/test/acceptance/mvt.js +++ b/test/acceptance/mvt.js @@ -1,9 +1,10 @@ require('../support/test_helper'); -const assert = require('../support/assert'); -const TestClient = require('../support/test-client'); +var assert = require('../support/assert'); +var TestClient = require('../support/test-client'); +var serverOptions = require('../../lib/cartodb/server_options'); -function createMapConfig (sql = TestClient.SQL.ONE_POINT) { +function createMapConfig(sql = TestClient.SQL.ONE_POINT) { return { version: '1.6.0', layers: [{ @@ -18,7 +19,98 @@ function createMapConfig (sql = TestClient.SQL.ONE_POINT) { }; } -describe('mvt', function () { +describe('mvt (mapnik)', mvt(false)); +describe('mvt (postgis)', mvt(true)); + +function mvt(usePostGIS) { +return function () { + const originalUsePostGIS = serverOptions.renderer.mvt.usePostGIS; + before(function () { + serverOptions.renderer.mvt.usePostGIS = usePostGIS; + }); + after(function (){ + serverOptions.renderer.mvt.usePostGIS = originalUsePostGIS; + }); + + describe('analysis-layers-dataviews-mvt', function () { + + function createMapConfig(layers, dataviews, analysis) { + return { + version: '1.5.0', + layers: layers, + dataviews: dataviews || {}, + analyses: analysis || [] + }; + } + + var CARTOCSS = [ + "#points {", + " marker-fill-opacity: 1.0;", + " marker-line-color: #FFF;", + " marker-line-width: 0.5;", + " marker-line-opacity: 1.0;", + " marker-placement: point;", + " marker-type: ellipse;", + " marker-width: 8;", + " marker-fill: red;", + " marker-allow-overlap: true;", + "}" + ].join('\n'); + + var mapConfig = createMapConfig( + [ + { + "type": "cartodb", + "options": { + "source": { + "id": "2570e105-7b37-40d2-bdf4-1af889598745" + }, + "cartocss": CARTOCSS, + "cartocss_version": "2.3.0" + } + } + ], + { + pop_max_histogram: { + source: { + id: '2570e105-7b37-40d2-bdf4-1af889598745' + }, + type: 'histogram', + options: { + column: 'pop_max' + } + } + }, + [ + { + "id": "2570e105-7b37-40d2-bdf4-1af889598745", + "type": "source", + "params": { + "query": "select * from populated_places_simple_reduced" + } + } + ] + ); + + it('should get pop_max column from dataview', function (done) { + var testClient = new TestClient(mapConfig); + + testClient.getTile(0, 0, 0, { format: 'mvt', layers: 0 }, function (err, res, MVT) { + var geojsonTile = JSON.parse(MVT.toGeoJSONSync(0)); + assert.ok(!err, err); + + assert.ok(Array.isArray(geojsonTile.features)); + assert.ok(geojsonTile.features.length > 0); + var feature = geojsonTile.features[0]; + assert.ok(feature.properties.hasOwnProperty('pop_max'), 'Missing pop_max property'); + + testClient.drain(done); + }); + }); + + }); + + const testCases = [ { desc: 'should get empty mvt with code 204 (no content)', @@ -48,16 +140,366 @@ describe('mvt', function () { testCases.forEach(function (test) { it(test.desc, done => { - const testClient = new TestClient(test.mapConfig, 1234); + var testClient = new TestClient(test.mapConfig); const { z, x, y } = test.coords; const { format, response } = test; - testClient.getTile(z, x, y, { format, response }, (err, res) => { + testClient.getTile(z, x, y, { format, response }, err => { assert.ifError(err); - - assert.equal(res.statusCode, test.response.status); testClient.drain(done); }); }); }); -}); + + + if (usePostGIS){ + describe('use only needed columns', onlyNeededColumns); + }else{ + describe.skip('use only needed columns', onlyNeededColumns); + } + + function onlyNeededColumns() { + function getFeatureByCartodbId(features, cartodbId) { + for (var i = 0, len = features.length; i < len; i++) { + if (features[i].properties.cartodb_id === cartodbId) { + return features[i]; + } + } + return {}; + } + + var options = { format: 'mvt', layer: 0 }; + + afterEach(function (done) { + if (this.testClient) { + this.testClient.drain(done); + } else { + done(); + } + }); + + it('with aggregation widget, interactivity and cartocss columns', function (done) { + var widgetMapConfig = { + version: '1.5.0', + layers: [{ + type: 'mapnik', + options: { + sql: 'select * from populated_places_simple_reduced', + cartocss: + '#layer0 { marker-fill: red; marker-width: 10; [name="Madrid"] { marker-fill: green; } }', + cartocss_version: '2.0.1', + widgets: { + adm0name: { + type: 'aggregation', + options: { + column: 'adm0name', + aggregation: 'sum', + aggregationColumn: 'pop_max' + } + } + }, + interactivity: "cartodb_id,pop_min" + } + }] + }; + + this.testClient = new TestClient(widgetMapConfig); + this.testClient.getTile(0, 0, 0, options, function (err, res, MVT) { + var geojsonTile = JSON.parse(MVT.toGeoJSONSync(0)); + assert.ok(!err, err); + assert.deepEqual(getFeatureByCartodbId(geojsonTile.features, 1109).properties, { + cartodb_id: 1109, + name: 'Mardin', + adm0name: 'Turkey', + pop_max: 71373, + pop_min: 57586 + }); + done(); + }); + }); + + it('should not duplicate columns', function (done) { + var widgetMapConfig = { + version: '1.5.0', + layers: [{ + type: 'mapnik', + options: { + sql: 'select * from populated_places_simple_reduced', + cartocss: ['#layer0 {', + 'marker-fill: red;', + 'marker-width: 10;', + '[name="Madrid"] { marker-fill: green; } ', + '[pop_max>100000] { marker-fill: black; } ', + '}'].join('\n'), + cartocss_version: '2.3.0', + widgets: { + adm0name: { + type: 'aggregation', + options: { + column: 'adm0name', + aggregation: 'sum', + aggregationColumn: 'pop_max' + } + } + }, + interactivity: "cartodb_id,pop_max" + } + }] + }; + + this.testClient = new TestClient(widgetMapConfig); + this.testClient.getTile(0, 0, 0, options, function (err, res, MVT) { + var geojsonTile = JSON.parse(MVT.toGeoJSONSync(0)); + assert.ok(!err, err); + assert.deepEqual(getFeatureByCartodbId(geojsonTile.features, 1109).properties, { + cartodb_id: 1109, + name: 'Mardin', + adm0name: 'Turkey', + pop_max: 71373 + }); + done(); + }); + }); + + it('with formula widget, no interactivity and no cartocss columns', function (done) { + var formulaWidgetMapConfig = { + version: '1.5.0', + layers: [{ + type: 'mapnik', + options: { + sql: 'select * from populated_places_simple_reduced where pop_max > 0 and pop_max < 600000', + cartocss: '#layer0 { marker-fill: red; marker-width: 10; }', + cartocss_version: '2.0.1', + interactivity: 'cartodb_id', + widgets: { + pop_max_f: { + type: 'formula', + options: { + column: 'pop_max', + operation: 'count' + } + } + } + } + }] + }; + this.testClient = new TestClient(formulaWidgetMapConfig); + this.testClient.getTile(0, 0, 0, options, function (err, res, MVT) { + var geojsonTile = JSON.parse(MVT.toGeoJSONSync(0)); + assert.ok(!err, err); + assert.deepEqual(getFeatureByCartodbId(geojsonTile.features, 1109).properties, { + cartodb_id: 1109, + pop_max: 71373 + }); + done(); + }); + }); + it('with cartocss with multiple expressions', function (done) { + var formulaWidgetMapConfig = { + version: '1.5.0', + layers: [{ + type: 'mapnik', + options: { + sql: 'select * from populated_places_simple_reduced where pop_max > 0 and pop_max < 600000', + cartocss: '#layer0 { marker-fill: red; marker-width: 10; }' + + '#layer0 { marker-width: 14; [name="Madrid"] { marker-width: 20; } }' + + '#layer0[pop_max>1000] { marker-width: 14; [name="Madrid"] { marker-width: 20; } }' + + '#layer0[adm0name=~".*Turkey*"] { marker-width: 14; [name="Madrid"] { marker-width: 20; } }', + cartocss_version: '2.0.1', + interactivity: 'cartodb_id' + } + }] + }; + + this.testClient = new TestClient(formulaWidgetMapConfig); + this.testClient.getTile(0, 0, 0, options, function (err, res, MVT) { + var geojsonTile = JSON.parse(MVT.toGeoJSONSync(0)); + assert.ok(!err, err); + assert.deepEqual(getFeatureByCartodbId(geojsonTile.features, 1109).properties, { + cartodb_id: 1109, + pop_max: 71373, + name: "Mardin", + adm0name: "Turkey" + }); + done(); + }); + }); + + var skipOnPostGIS = usePostGIS ? it.skip: it; + skipOnPostGIS('should work with mapnik substitution tokens', function (done) { + var cartocss = [ + "#layer {", + " line-width: 2;", + " line-color: #3B3B58;", + " line-opacity: 1;", + " polygon-opacity: 0.7;", + " polygon-fill: ramp([points_count], (#E5F5F9,#99D8C9,#2CA25F))", + "}" + ].join('\n'); + + var sql = [ + 'WITH hgrid AS (', + ' SELECT CDB_HexagonGrid(', + ' ST_Expand(!bbox!, greatest(!pixel_width!,!pixel_height!) * 100),', + ' greatest(!pixel_width!,!pixel_height!) * 100', + ' ) as cell', + ')', + 'SELECT', + ' hgrid.cell as the_geom_webmercator,', + ' count(1) as points_count,', + ' count(1)/power(100 * CDB_XYZ_Resolution(CDB_ZoomFromScale(!scale_denominator!)), 2) as points_density,', + ' 1 as cartodb_id', + 'FROM hgrid, (SELECT * FROM populated_places_simple_reduced) i', + 'where ST_Intersects(i.the_geom_webmercator, hgrid.cell)', + 'GROUP BY hgrid.cell' + ].join('\n'); + + var mapConfig = { + "version": "1.4.0", + "layers": [ + { + "type": 'mapnik', + "options": { + "cartocss_version": '2.3.0', + "sql": sql, + "cartocss": cartocss + } + } + ] + }; + + this.testClient = new TestClient(mapConfig); + this.testClient.getTile(0, 0, 0, options, function (err, res, MVT) { + var geojsonTile = JSON.parse(MVT.toGeoJSONSync(0)); + assert.ok(!err, err); + + assert.ok(geojsonTile); + assert.equal(geojsonTile.features.length, 5); + + done(); + }); + }); + + it('should skip empty and null columns for geojson tiles', function (done) { + + var mapConfig = { + "analyses": [ + { + "id": "a0", + "params": { + "query": "SELECT * FROM test_table" + }, + "type": "source" + } + ], + "dataviews": { + "4e7b0e07-6d21-4b83-9adb-6d7e17eea6ca": { + "options": { + "aggregationColumn": null, + "column": "cartodb_id", + "operation": "avg" + }, + "source": { + "id": "a0" + }, + "type": "formula" + }, + "74f590f8-625c-4e95-922f-34ad3e9919c0": { + "options": { + "aggregation": "sum", + "aggregationColumn": "cartodb_id", + "column": "name" + }, + "source": { + "id": "a0" + }, + "type": "aggregation" + }, + "98a75757-3006-400a-b028-fb613a6c0b69": { + "options": { + "aggregationColumn": null, + "column": "cartodb_id", + "operation": "sum" + }, + "source": { + "id": "a0" + }, + "type": "formula" + }, + "ebbc97b2-87d2-4895-9e1f-2f012df3679d": { + "options": { + "aggregationColumn": null, + "bins": "12", + "column": "cartodb_id" + }, + "source": { + "id": "a0" + }, + "type": "histogram" + }, + "ebc0653f-3581-469c-8b31-c969e440a865": { + "options": { + "aggregationColumn": null, + "column": "cartodb_id", + "operation": "avg" + }, + "source": { + "id": "a0" + }, + "type": "formula" + } + }, + "layers": [ + { + "options": { + "subdomains": "abcd", + "urlTemplate": "http://{s}.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}.png" + }, + "type": "http" + }, + { + "options": { + "attributes": { + "columns": [ + "name", + "address" + ], + "id": "cartodb_id" + }, + "cartocss": "#layer { marker-width: 10; marker-fill: red; }", + "cartocss_version": "2.3.0", + "interactivity": "cartodb_id", + "layer_name": "wadus", + "source": { + "id": "a0" + } + }, + "type": "cartodb" + }, + { + "options": { + "subdomains": "abcd", + "urlTemplate": "http://{s}.basemaps.cartocdn.com/light_only_labels/{z}/{x}/{y}.png" + }, + "type": "http" + } + ] + }; + + this.testClient = new TestClient(mapConfig); + this.testClient.getTile(0, 0, 0, options, function (err, res, MVT) { + var geojsonTile = JSON.parse(MVT.toGeoJSONSync(0)); + assert.ok(!err, err); + + assert.ok(geojsonTile); + assert.equal(geojsonTile.features.length, 5); + + assert.deepEqual(Object.keys(geojsonTile.features[0].properties), ['cartodb_id', 'name']); + + done(); + }); + }); + + } +}; +} \ No newline at end of file diff --git a/test/acceptance/user-render-timeout-limit.js b/test/acceptance/user-render-timeout-limit.js index 45e41f89..eaf98805 100644 --- a/test/acceptance/user-render-timeout-limit.js +++ b/test/acceptance/user-render-timeout-limit.js @@ -2,6 +2,7 @@ require('../support/test_helper'); const assert = require('../support/assert'); const TestClient = require('../support/test-client'); +var serverOptions = require('../../lib/cartodb/server_options'); const timeoutErrorTilePath = `${process.cwd()}/assets/render-timeout-fallback.png`; @@ -200,49 +201,56 @@ describe('user render timeout limit', function () { }); }); - describe('vector', function () { - beforeEach(function (done) { - const mapconfig = createMapConfig(); - this.testClient = new TestClient(mapconfig, 1234); - this.testClient.setUserRenderTimeoutLimit('localhost', 50, done); - }); - - afterEach(function (done) { - this.testClient.setUserRenderTimeoutLimit('localhost', 0, (err) => { - if (err) { - return done(err); - } - this.testClient.drain(done); + describe('vector (PostGIS)', vector(true)); + describe('vector (mapnik)', vector(false)); + function vector(usePostGIS) { + const originalUsePostGIS = serverOptions.renderer.mvt.usePostGIS; + return function () { + beforeEach(function (done) { + serverOptions.renderer.mvt.usePostGIS = usePostGIS; + const mapconfig = createMapConfig(); + this.testClient = new TestClient(mapconfig, 1234); + this.testClient.setUserDatabaseTimeoutLimit(50, done); }); - }); - it('layergroup creation works but vector tile request fails due to render timeout', function (done) { - const params = { - format: 'mvt', - response: { - status: 429, - headers: { - 'Content-Type': 'application/json; charset=utf-8' + afterEach(function (done) { + serverOptions.renderer.mvt.usePostGIS = originalUsePostGIS; + this.testClient.setUserDatabaseTimeoutLimit(0, (err) => { + if (err) { + return done(err); } - } - }; - - this.testClient.getTile(0, 0, 0, params, (err, res, tile) => { - assert.ifError(err); - - assert.deepEqual(tile, { - errors: ['You are over platform\'s limits. Please contact us to know more details'], - errors_with_context: [{ - type: 'limit', - subtype: 'render', - message: 'You are over platform\'s limits. Please contact us to know more details' - }] + this.testClient.drain(done); }); - - done(); }); - }); - }); + + it('layergroup creation works but vector tile request fails due to render timeout', function (done) { + const params = { + format: 'mvt', + response: { + status: 429, + headers: { + 'Content-Type': 'application/json; charset=utf-8' + } + } + }; + + this.testClient.getTile(0, 0, 0, params, (err, res, tile) => { + assert.ifError(err); + + assert.deepEqual(tile, { + errors: ['You are over platform\'s limits. Please contact us to know more details'], + errors_with_context: [{ + type: 'limit', + subtype: 'datasource', + message: 'You are over platform\'s limits. Please contact us to know more details' + }] + }); + + done(); + }); + }); + }; + } describe('interativity', function () { beforeEach(function (done) { diff --git a/test/support/test-client.js b/test/support/test-client.js index e027537e..cd7b4f60 100644 --- a/test/support/test-client.js +++ b/test/support/test-client.js @@ -20,7 +20,6 @@ const MAPNIK_SUPPORTED_FORMATS = { 'png': true, 'png32': true, 'grid.json': true, - 'geojson': true, 'mvt': true }; diff --git a/yarn.lock b/yarn.lock index e7d9b20c..cf95c05b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10,11 +10,7 @@ abaculus@cartodb/abaculus#2.0.3-cdb1: mapnik "~3.5.0" sphericalmercator "1.0.x" -abbrev@1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" - -abbrev@1.0.x: +abbrev@1, abbrev@1.0.x: version "1.0.9" resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.0.9.tgz#91b4792588a7738c25f35dd6f63752a2f8776135" @@ -520,11 +516,7 @@ domutils@1.5: dom-serializer "0" domelementtype "1" -dot@^1.0.3: - version "1.1.2" - resolved "https://registry.yarnpkg.com/dot/-/dot-1.1.2.tgz#c7377019fc4e550798928b2b9afeb66abfa1f2f9" - -dot@~1.0.2: +dot@^1.0.3, dot@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/dot/-/dot-1.0.3.tgz#f8750bfb6b03c7664eb0e6cb1eb4c66419af9427" @@ -754,7 +746,7 @@ gdal@~0.9.2: nan "~2.6.2" node-pre-gyp "~0.6.36" -generic-pool@2.4.3: +generic-pool@2.4.3, generic-pool@~2.4.0, generic-pool@~2.4.1: version "2.4.3" resolved "https://registry.yarnpkg.com/generic-pool/-/generic-pool-2.4.3.tgz#780c36f69dfad05a5a045dd37be7adca11a4f6ff" @@ -766,10 +758,6 @@ generic-pool@~2.2.0, generic-pool@~2.2.1: version "2.2.2" resolved "https://registry.yarnpkg.com/generic-pool/-/generic-pool-2.2.2.tgz#7a89f491d575b42f9f069a0e8e2c6dbaa3c241be" -generic-pool@~2.4.0, generic-pool@~2.4.1: - version "2.4.6" - resolved "https://registry.yarnpkg.com/generic-pool/-/generic-pool-2.4.6.tgz#f1b55e572167dba2fe75d5aa91ebb1e9f72642d7" - get-caller-file@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.2.tgz#f702e63127e7e231c160a80c1554acb70d5047e5" @@ -780,7 +768,7 @@ getpass@^0.1.1: dependencies: assert-plus "^1.0.0" -glob@7.1.1: +glob@7.1.1, glob@^7.0.5, glob@^7.1.1: version "7.1.1" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8" dependencies: @@ -811,17 +799,6 @@ glob@^6.0.1: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^7.0.5, glob@^7.1.1: - version "7.1.2" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" - dependencies: - 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" - graceful-fs@^4.1.2: version "4.1.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" @@ -1323,13 +1300,13 @@ mime@~1.3.4: version "1.3.6" resolved "https://registry.yarnpkg.com/mime/-/mime-1.3.6.tgz#591d84d3653a6b0b4a3b9df8de5aa8108e72e5e0" -"minimatch@2 || 3", minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.4, minimatch@~3.0.2: +"minimatch@2 || 3", minimatch@^3.0.0, minimatch@^3.0.2, minimatch@~3.0.2: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" dependencies: brace-expansion "^1.1.7" -minimist@0.0.8: +minimist@0.0.8, minimist@~0.0.1: version "0.0.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" @@ -1337,10 +1314,6 @@ minimist@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" -minimist@~0.0.1: - version "0.0.10" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf" - minimist@~0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.2.0.tgz#4dffe525dae2b864c66c2e23c6271d7afdecefce" @@ -1476,14 +1449,10 @@ oauth-sign@~0.8.1, oauth-sign@~0.8.2: version "0.8.2" resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" -object-assign@4.1.0: +object-assign@4.1.0, object-assign@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.0.tgz#7a3b3d0e98063d43f4c03f2e8ae6cd51a86883a0" -object-assign@^4.1.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" - object-keys@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-0.4.0.tgz#28a6aae7428dd2c3a92f3d95f21335dd204e0336" @@ -1661,8 +1630,8 @@ postcss@5.0.19: supports-color "^3.1.2" postcss@^5.0.18, postcss@^5.2.5, postcss@~5.2.8: - version "5.2.17" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-5.2.17.tgz#cf4f597b864d65c8a492b2eabe9d706c879c388b" + version "5.2.18" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-5.2.18.tgz#badfa1497d46244f6390f58b319830d9107853c5" dependencies: chalk "^1.1.3" js-base64 "^2.1.9" @@ -1771,7 +1740,7 @@ read-pkg@^1.0.0: normalize-package-data "^2.3.2" path-type "^1.0.0" -readable-stream@1.1: +readable-stream@1.1, readable-stream@~1.1.9: version "1.1.13" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.13.tgz#f6eef764f514c89e2b9e23146a75ba106756d23e" dependencies: @@ -1801,15 +1770,6 @@ readable-stream@~1.0.2: isarray "0.0.1" string_decoder "~0.10.x" -readable-stream@~1.1.9: - version "1.1.14" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.1" - isarray "0.0.1" - string_decoder "~0.10.x" - redis-mpool@0.4.1, redis-mpool@~0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/redis-mpool/-/redis-mpool-0.4.1.tgz#d917c0a4ed57a1291a9c6eb35434e6c0b7046f80" @@ -1919,9 +1879,9 @@ safe-json-stringify@~1: version "1.0.4" resolved "https://registry.yarnpkg.com/safe-json-stringify/-/safe-json-stringify-1.0.4.tgz#81a098f447e4bbc3ff3312a243521bc060ef5911" -"semver@2 || 3 || 4 || 5", semver@^5.1.0, semver@^5.3.0: - version "5.4.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.4.1.tgz#e059c09d8571f0540823733433505d3a2f00b18e" +"semver@2 || 3 || 4 || 5", semver@^5.1.0, semver@^5.3.0, semver@~5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" semver@4.3.2: version "4.3.2" @@ -1935,10 +1895,6 @@ semver@~5.0.3: version "5.0.3" resolved "https://registry.yarnpkg.com/semver/-/semver-5.0.3.tgz#77466de589cd5d3c95f138aa78bc569a3cb5d27a" -semver@~5.3.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" - send@0.16.1: version "0.16.1" resolved "https://registry.yarnpkg.com/send/-/send-0.16.1.tgz#a70e1ca21d1382c11d0d9f6231deb281080d7ab3" @@ -2036,14 +1992,10 @@ speedometer@~0.1.2: version "0.1.4" resolved "https://registry.yarnpkg.com/speedometer/-/speedometer-0.1.4.tgz#9876dbd2a169d3115402d48e6ea6329c8816a50d" -sphericalmercator@1.0.4: +sphericalmercator@1.0.4, sphericalmercator@1.0.x, sphericalmercator@~1.0.1, sphericalmercator@~1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/sphericalmercator/-/sphericalmercator-1.0.4.tgz#baad4e34187f06e87f2e92fc1280199fa1b01d4e" -sphericalmercator@1.0.x, sphericalmercator@~1.0.1, sphericalmercator@~1.0.4: - version "1.0.5" - resolved "https://registry.yarnpkg.com/sphericalmercator/-/sphericalmercator-1.0.5.tgz#ddc5a049e360e000d0fad9fc22c4071882584980" - split@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/split/-/split-1.0.1.tgz#605bd9be303aa59fb35f9229fbea0ddec9ea07d9" @@ -2141,7 +2093,7 @@ strip-json-comments@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" -supports-color@3.1.2: +supports-color@3.1.2, supports-color@^3.1.0, supports-color@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.1.2.tgz#72a262894d9d408b956ca05ff37b2ed8a6e2a2d5" dependencies: @@ -2151,7 +2103,7 @@ supports-color@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" -supports-color@^3.1.0, supports-color@^3.1.2, supports-color@^3.2.3: +supports-color@^3.2.3: version "3.2.3" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6" dependencies: @@ -2358,9 +2310,9 @@ window-size@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.2.0.tgz#b4315bb4214a3d7058ebeee892e13fa24d98b075" -windshaft@3.3.3: - version "3.3.3" - resolved "https://registry.yarnpkg.com/windshaft/-/windshaft-3.3.3.tgz#0582e6a0d9cf91c533134787ace64a3337200e33" +windshaft@cartodb/windshaft: + version "4.0.0" + resolved "https://codeload.github.com/cartodb/windshaft/tar.gz/5b21e24e66c8e24cb88640b606aaf5fefcd3f365" dependencies: abaculus cartodb/abaculus#2.0.3-cdb1 canvas cartodb/node-canvas#1.6.2-cdb2