Compare commits

..

156 Commits
6.3.0 ... 6.4.0

Author SHA1 Message Date
Simon Martín
ef9ec5b262 Merge pull request #1032 from CartoDB/windshaft-remove-step
upgrades Windshaft (remove step)
2018-09-24 11:29:48 +02:00
Simon Martín
f9b8152f21 Merge branch 'master' into windshaft-remove-step 2018-09-24 11:16:45 +02:00
Simon Martín
e0ff8b4320 NEWS/package to version 6.4.0 and release date 2018-09-24 11:14:41 +02:00
Simon Martín
6a699ba51b windshaft version 4.10.0 2018-09-24 11:03:24 +02:00
Simon Martín
1899fd3813 updating to lastest windshaft version 2018-09-20 17:41:44 +02:00
Javier Goizueta
bd7c99f94f Merge pull request #1036 from CartoDB/1034-aggregation-bbox
Aggregation limits adjustment fix
2018-09-17 17:05:31 +02:00
Javier Goizueta
6ba9e50da7 Minor tests refactor 2018-09-13 08:54:59 +02:00
Javier Goizueta
21a2d9e82f Merge pull request #1035 from CartoDB/1033-no-the_geom
Avoid requiring the_geom for point-sample aggregation
2018-09-13 08:51:34 +02:00
Javier Goizueta
0f20cdaae1 More robust adjustment of spatial limits to the aggregation grid
See #1034
2018-09-12 19:27:21 +02:00
Javier Goizueta
5d813b6e43 Add more tests for aggregation accuracy
Some tests are skipped for Mapnik MVTs, because its bbox filtering isn't accurate
2018-09-12 19:26:09 +02:00
Raúl Marín
a842acfdb4 Merge pull request #1029 from Algunenano/update_640_vars
Update next release version according to the changes done
2018-09-12 19:09:13 +02:00
Javier Goizueta
1e65804a1b Avoid requiring the_geom for point-sample aggregation 2018-09-12 11:26:14 +02:00
Javier Goizueta
acf0b082b4 Perform some tests for all placements
The "only the_geom" and other aggregation tests were perform only for default aggregation.
2018-09-12 11:25:21 +02:00
Simon Martín
2c6305bcd4 windshaft version: remove-step 2018-09-07 12:23:09 +02:00
Raul Marin
bd153a0c87 Update next release version according to the changes done 2018-09-05 16:07:43 +02:00
Raúl Marín
fb6987e91a Merge pull request #1027 from CartoDB/carto_mvt_extent
Update Windshaft to 4.9.0
2018-09-05 13:30:12 +02:00
Eneko Lakasta
74c036483a Merge pull request #1028 from CartoDB/1024-log-overviews-mapconfig
use as flag overviewsAddedToMapconfig instead of overviewsUsed
2018-09-05 13:24:48 +02:00
Eneko Lakasta
65e10bc20d use as flag overviewsAddedToMapconfig instead of overviewsUsed 2018-09-05 13:14:55 +02:00
Eneko Lakasta
89a1e69bec Merge pull request #1026 from CartoDB/1024-log-overviews-mapconfig
Log overviews usage during map instantiation
2018-09-05 12:52:22 +02:00
Eneko Lakasta
564884797d add tests to check that flags for non overviews instantiation are correct 2018-09-05 12:39:51 +02:00
Eneko Lakasta
dd1ee56648 use .some instead of .map & .some 2018-09-05 11:56:45 +02:00
Eneko Lakasta
c54c3754ef fix indentation 2018-09-05 11:51:58 +02:00
Eneko Lakasta
d72a5075b9 move overviews flags in named map instantiation checks to its own specific tests 2018-09-05 11:48:21 +02:00
Eneko Lakasta
6dde5fc6f1 use .some instead of reduce 2018-09-04 18:22:48 +02:00
Eneko Lakasta
880ef63720 add to logs named maps overviews instantiation 2018-09-04 16:21:20 +02:00
Eneko Lakasta
b75150e91e set mapType in the controller instead of in the adapter 2018-09-04 16:20:38 +02:00
Eneko Lakasta
006e21379f please jshint line to long 2018-09-04 15:30:20 +02:00
Eneko Lakasta
03850fb31c Merge branch 'master' into 1024-log-overviews-mapconfig 2018-09-04 15:14:21 +02:00
Eneko Lakasta
7df0fb456b add to log anonymous maps instantiations that use overviews tables
Added to tiler profiler object:

overviewsAddedToMapconfig: true
mapType: 'anonymous'
2018-09-04 15:13:17 +02:00
Eneko Lakasta
1fe1b5fc4d Merge pull request #1024 from CartoDB/5201-log-overviews
Log overviews usage
2018-08-30 16:11:36 +02:00
Eneko Lakasta
95d179835c add to logs even if no overviews tables were used.
{usesOverviews:false}
2018-08-30 14:52:37 +02:00
Eneko Lakasta
7c52f504e5 add dataview type to overviews logs 2018-08-30 14:30:03 +02:00
Raul Marin
44bbfe3ba6 Update Windshaft to 4.9.0 2018-08-30 07:01:34 +02:00
Raul Marin
57258a9cd3 MVT (pg-mvt): Remove tests related to removed functionality 2018-09-03 14:59:20 +02:00
Raul Marin
cd25150056 MVT: Removed error parsing for empty tiles 2018-09-03 14:59:16 +02:00
Eneko Lakasta
c9d50c412d add tests for dataviews 2018-08-29 18:00:13 +02:00
Eneko Lakasta
732f891850 refactor in order not to tamper the dataview results data 2018-08-29 15:06:48 +02:00
Eneko Lakasta
8ef260972d add to log when overviews are being used in dataviews 2018-08-29 13:50:21 +02:00
Simon Martín
de30ab99ef Merge pull request #1021 from CartoDB/naming-fixes
Requirements version changes
2018-08-24 12:53:49 +02:00
Simon Martín
de6c651b60 Merge branch 'master' into naming-fixes 2018-08-24 12:24:52 +02:00
Javier Goizueta
b466937d68 Merge pull request #1022 from CartoDB/stats-spaces
Fix column stats to support spaces in names
2018-08-21 16:24:41 +02:00
Javier Goizueta
d338b5ca37 Fix column stats to support spaces in names
This complements changes in #1020
2018-08-21 12:55:30 +02:00
Javier Goizueta
9740b65fe7 Update NEWS 2018-08-20 18:34:29 +02:00
Jesús Arroyo Torrens
27e2d0baa5 Merge pull request #1020 from CartoDB/spaces-in-columns
Fix spaces in columns
2018-08-20 17:56:48 +02:00
Alejandro Guirao Rodríguez
4039221b4b Update carto-package.json 2018-08-17 10:42:56 +02:00
Alejandro Guirao Rodríguez
5bb58429b3 Requirements version changes 2018-08-17 10:34:25 +02:00
Simon Martín
204d246a8c mapnik to requires in carto-package 2018-08-16 16:17:01 +02:00
Jesús Arroyo Torrens
e0f49ca8f5 Fix aggregation-query dimension functions regarding to spaces in columns 2018-08-16 15:34:06 +02:00
Simon Martín
07cdb37deb Merge pull request #1019 from CartoDB/create-carto-package-json
create carto-package.json file
2018-08-16 15:03:21 +02:00
Simon Martín
5c3182e168 adding carto_postgresql_ext to carto-package 2018-08-16 14:44:43 +02:00
Jesús Arroyo Torrens
31263b7b22 Add unit test 2018-08-16 14:15:28 +02:00
Jesús Arroyo Torrens
925328c43b Fix bug in date-wrapper regarding to columns with spaces 2018-08-16 13:38:26 +02:00
Simon Martín
a36b93a473 mapnik 3.0.15 and removing gdal 2018-08-16 12:34:22 +02:00
Jesús Arroyo Torrens
14cf3c1093 Fix typo 2018-08-16 12:16:20 +02:00
Simon Martín
534808038e more versions 2018-08-14 18:29:42 +02:00
Simon Martín
83c7d38d42 adding versions, not finished 2018-08-14 14:36:49 +02:00
Simon Martín
964bfef6e7 create carto-package.json file 2018-08-14 12:26:33 +02:00
Juan Ignacio Sánchez Lara
657fb97d58 Merge pull request #1018 from CartoDB/camshaft_0_62_3
Camshaft 0.62.3
2018-08-03 12:56:22 +02:00
Juan Ignacio Sánchez Lara
2b36e8c68b Camshaft 0.62.3 2018-08-03 12:42:42 +02:00
Daniel G. Aubert
ed101e30fa Merge pull request #1015 from CartoDB/upgrade-camshaft-to-avoid-missing-columns
Upgrade camshaft to avoid missing columns when an analysis changes
2018-08-02 16:58:13 +02:00
Daniel García Aubert
9a3bd51664 Merge branch 'master' into upgrade-camshaft-to-avoid-missing-columns 2018-08-02 16:42:09 +02:00
Daniel G. Aubert
6576fa5ca0 Merge pull request #1017 from CartoDB/remove-step-globally
Move step as development dependency
2018-08-02 16:40:49 +02:00
Daniel García Aubert
45fc2dd07a Add empty line 2018-08-02 16:19:12 +02:00
Daniel García Aubert
e7a6ddb4ff Update NEWS 2018-08-02 16:15:10 +02:00
Daniel García Aubert
f719813c52 Update NEWS 2018-08-02 16:09:39 +02:00
Daniel García Aubert
9c4adef0c2 Update camshaft to a released version 2018-08-02 15:57:39 +02:00
Daniel García Aubert
9f831b2c40 Move step as development dependency 2018-08-02 15:14:10 +02:00
Daniel G. Aubert
dfa057d979 Merge pull request #1016 from CartoDB/remove-step-template-maps
Remove step template maps
2018-08-02 15:02:59 +02:00
Daniel G. Aubert
7ffac651b6 Merge pull request #1014 from CartoDB/remove-step-mapconfig-overviews-adapter
Remove step mapconfig overviews adapter
2018-08-02 14:54:38 +02:00
Daniel García Aubert
98ab237bf7 Merge branch 'master' into remove-step-template-maps 2018-08-02 14:10:26 +02:00
Daniel García Aubert
63e4bcebef Move function to class as private method 2018-08-02 13:13:48 +02:00
Daniel García Aubert
593a72a967 Reorganize code, estract methods and rename others 2018-08-02 11:40:41 +02:00
Simon Martín
6c8f38a241 improve var naming 2018-08-02 11:38:54 +02:00
Simon Martín
a910f1442e explicit error return 2018-08-02 11:35:54 +02:00
Daniel García Aubert
df14afb55f Remove unnecessary code 2018-08-01 19:15:28 +02:00
Daniel García Aubert
732a2d7742 Early return is the way to go. Avoid checking conditions later 2018-08-01 17:39:08 +02:00
Daniel García Aubert
78f4cf3155 Move code 2018-08-01 17:33:20 +02:00
Daniel García Aubert
8f763d655d Do not rename output values 2018-08-01 17:23:40 +02:00
Daniel García Aubert
64329c3fac Rename callback 2018-08-01 16:39:15 +02:00
Daniel García Aubert
c8c22a787e CamelCase 2018-08-01 16:35:19 +02:00
Daniel García Aubert
6a6ec4300b Extract functions to compose query-rewrite-data 2018-08-01 16:30:59 +02:00
Simon Martín
44ba5aa568 forgotten return 2018-08-01 16:22:43 +02:00
Simon Martín
01658c33fd remove step and assert dependencies 2018-08-01 16:03:36 +02:00
Simon Martín
efafd4cb3e success callback without err 2018-08-01 16:02:53 +02:00
Daniel García Aubert
d25740ed51 Extract function to avoid dynamic clousures 2018-08-01 16:02:05 +02:00
Simon Martín
3bb4ad86ff remove template 5 refactor 2018-08-01 16:00:31 +02:00
Simon Martín
b169c96f1c remove step 5 2018-08-01 15:57:22 +02:00
Daniel García Aubert
843f4b8e28 Use early return 2018-08-01 15:53:43 +02:00
Simon Martín
e5b75abc76 some details 2018-08-01 15:53:41 +02:00
Daniel García Aubert
83777540d0 Merge branch 'master' into remove-step-mapconfig-overviews-adapter 2018-08-01 15:51:43 +02:00
Simon Martín
0e28348e16 manage JSON parse and stringify sinc errors 2018-08-01 15:48:24 +02:00
Simon Martín
7b7bee2901 improve naming 2018-08-01 15:43:45 +02:00
Simon Martín
2a2f703abc style details 2018-08-01 15:40:40 +02:00
Simon Martín
e85f4e4129 selft to this 2018-08-01 15:39:25 +02:00
Simon Martín
bcad6dbe22 anage errors 2018-08-01 15:32:44 +02:00
Simon Martín
e9f88a78d5 remove step 4 2018-08-01 15:28:15 +02:00
Daniel G. Aubert
f601ed3806 Merge pull request #1012 from CartoDB/remove-step-filter-stats
Remove step filter stats
2018-08-01 14:59:36 +02:00
Simon Martín
085d26f1b2 remove step 3: refactor 2018-08-01 13:27:07 +02:00
Daniel García Aubert
559546d333 Merge branch 'master' into remove-step-filter-stats 2018-08-01 13:22:57 +02:00
Simon Martín
da8d92b78e remove step 3 2018-08-01 13:20:10 +02:00
Daniel G. Aubert
de5498c1b2 Merge pull request #1013 from CartoDB/remove-step-user-limits
Remove step user limits
2018-08-01 13:19:49 +02:00
Simon Martín
2134bf898a check user template limit in the right way 2018-08-01 13:13:25 +02:00
Daniel García Aubert
e27ed7e79d Merge branch 'master' into remove-step-user-limits 2018-08-01 12:46:47 +02:00
Daniel G. Aubert
efad5b20e8 Merge pull request #1011 from CartoDB/remove-step-dataviews
Remove step dataviews
2018-08-01 12:45:01 +02:00
Simon Martín
f27d5ba7d1 unneeded check 2018-08-01 12:40:22 +02:00
Simon Martín
09a67871fb manage JSON.stringify sync error 2018-08-01 12:38:50 +02:00
Simon Martín
cec9994add style details 2018-08-01 12:33:59 +02:00
Simon Martín
d45d0018d2 self to this 2018-08-01 12:32:20 +02:00
Simon Martín
410cbd082c manage errors 2018-08-01 12:26:40 +02:00
Simon Martín
fd875c41c7 remove step 2 2018-08-01 12:20:01 +02:00
Simon Martín
750798d0a3 ensuring redis_pool connection release 2018-08-01 12:10:09 +02:00
Simon Martín
04faaea10d remove unneded check 2018-08-01 11:49:40 +02:00
Simon Martín
74831c9b7f using ... operator instead of apply 2018-08-01 11:48:12 +02:00
Simon Martín
5471a218eb return errors 2018-08-01 11:39:07 +02:00
Simon Martín
79f2a8dde9 vars refactor: that to this, ... 2018-08-01 11:30:17 +02:00
Simon Martín
5c0b7487f7 using calbacks instead of step 2018-08-01 11:25:55 +02:00
Daniel García Aubert
c021f5ebdc Upgrade camshaft to avoid missing columns when an analysis changes its schema output 2018-07-31 19:15:08 +02:00
Daniel García Aubert
3a3baf3c85 Rename variable 2018-07-31 15:41:29 +02:00
Simon Martín
b8d320c434 return callback 2018-07-31 15:19:41 +02:00
Simon Martín
515e482886 early return 2018-07-31 15:07:34 +02:00
Simon Martín
ea0805b017 early return 2018-07-31 15:01:22 +02:00
Daniel García Aubert
ec0e90e8ce Avoid uncaught exceptions 2018-07-31 13:33:33 +02:00
Daniel García Aubert
d4de54f292 Extract get query with filters 2018-07-31 13:26:38 +02:00
Daniel García Aubert
9124a26a45 Move veriable declaration 2018-07-31 12:47:03 +02:00
Daniel García Aubert
18603ad24f Reduce cyclomatic complexity 2018-07-31 12:43:54 +02:00
Daniel García Aubert
70ac0587db Missing error 2018-07-31 12:15:20 +02:00
Daniel García Aubert
230b1bb3db Remove step .searchDataview() 2018-07-31 11:59:39 +02:00
Simon Martín
23ef884e9b remove step 1 2018-07-31 10:26:46 +02:00
Simon Martín
bb24b1dfcc indentation 2018-07-31 10:25:56 +02:00
Simon Martín
324e614902 change _.extend by Object.assign 2018-07-30 18:25:26 +02:00
Simon Martín
20c8d07a46 remove step: last function 2018-07-30 17:59:06 +02:00
Simon Martín
fe9f4939d5 remove step: function 2 2018-07-30 17:49:20 +02:00
Simon Martín
a89131c043 remove step: function 1 2018-07-30 17:34:14 +02:00
Simon Martín
1f6bb6839a remove step 2018-07-30 17:32:57 +02:00
Simon Martín
1e70717554 indentation 2018-07-30 17:31:42 +02:00
Simon Martín
3cf378e045 remove 'self' 2018-07-30 17:21:19 +02:00
Simon Martín
12ad4420aa remove step: function 2 refactor 2018-07-30 17:18:35 +02:00
Simon Martín
70000f9df1 remove step: function 2 2018-07-30 17:16:27 +02:00
Simon Martín
eaff11ef6e remove step: function 1 2018-07-30 17:15:04 +02:00
Simon Martín
6c3b546648 remove step 2018-07-30 17:09:34 +02:00
Simon Martín
384f4f74e0 remove 'self' with arrows functions 2018-07-30 16:44:30 +02:00
Simon Martín
898bac5b04 jshint and style 2018-07-30 16:42:17 +02:00
Simon Martín
cd08ad693f remove step: last function refactor 2018-07-30 16:38:04 +02:00
Simon Martín
c2577d1d25 remove step: last function 2018-07-30 16:02:04 +02:00
Daniel García Aubert
13075460ac Do not override incoming arguments 2018-07-30 16:00:30 +02:00
Daniel García Aubert
cca0848e6d Improve error 2018-07-30 15:59:43 +02:00
Simon Martín
150658d58d remove step: 3 function refactor 2018-07-30 15:59:37 +02:00
Daniel García Aubert
6be1a77a29 Use callback to return the error 2018-07-30 15:57:47 +02:00
Simon Martín
fb683e438d remove step: second function 2018-07-30 15:56:18 +02:00
Simon Martín
2196a89ec3 remove step: 2 function refactor 2018-07-30 15:55:24 +02:00
Simon Martín
bafeceeb6e remove step: first funcion 2018-07-30 15:54:26 +02:00
Simon Martín
eeafad7cd9 remove step 2018-07-30 15:52:41 +02:00
Daniel García Aubert
94b7353fbf Fix uncaught exception 2018-07-30 15:52:04 +02:00
Simon Martín
60cd91f144 indentation 2018-07-30 15:50:41 +02:00
Daniel García Aubert
1d199f8713 Remove step in method 2018-07-30 15:19:53 +02:00
Daniel García Aubert
42b79e5bc6 Stubs next version 2018-07-26 10:37:12 +02:00
29 changed files with 1023 additions and 903 deletions

22
NEWS.md
View File

@@ -1,5 +1,27 @@
# Changelog
## 6.4.0
Released 2018-09-24
- Upgrades Camshaft to [0.62.3](https://github.com/CartoDB/camshaft/releases/tag/0.61.11):
- Build query from node's cache to compute output columns when building analysis
- Adds metadata columns for street level geocoding
- Remove use of `step` module to handle asynchronous code, now it's defined as development dependency.
- Bug Fixes: (#1020)
- Fix bug in date-wrapper regarding columns with spaces
- Fix bug in aggregation-query regarding columns with spaces
- Upgrades Windshaft to [4.10.0](https://github.com/CartoDB/Windshaft/blob/4.10.0/NEWS.md#version-4100)
- `pg-mvt`:
- Now matches the behaviour of the `mapnik` renderer for MVTs.
- Removed undocummented filtering by `layer.options.columns`.
- Implement timeout in getTile.
- Several bugfixes.
- Dependency updates: Fixed a bug in Mapnik MVT renderer and cleanup in `tilelive-mapnik`.
- [MapConfig 1.8.0 released](https://github.com/CartoDB/Windshaft/blob/master/doc/MapConfig-1.8.0.md) with new options for MVTs:
- Add **`vector_extent`** option in MapConfig to setup the layer extent.
- Add **`vector_simplify_extent`** option in MapConfig to configure the simplification process.
- Remove use of `step` module to handle asynchronous code, now it's defined as development dependency.
## 6.3.0
Released 2018-07-26

16
carto-package.json Normal file
View File

@@ -0,0 +1,16 @@
{
"name": "carto_windshaft",
"current_version": {
"requires": {
"node": ">=6.9.2 <10.0.0",
"yarn": ">=0.27.5 <1.0.0",
"mapnik": ">=3.0.15"
},
"works_with": {
"redis": ">=3.0.0",
"postgresql": ">=9.5.0",
"postgis": ">=2.2.0.0",
"carto_postgresql_ext": ">=0.19.0"
}
}
}

View File

@@ -152,8 +152,17 @@ function prepareAdapterMapConfig (mapConfigAdapter) {
}
};
mapConfigAdapter.getMapConfig(user, requestMapConfig, params, context, (err, requestMapConfig) => {
mapConfigAdapter.getMapConfig(user,
requestMapConfig,
params,
context,
(err, requestMapConfig, stats = { overviewsAddedToMapconfig : false }) => {
req.profiler.done('anonymous.getMapConfig');
stats.mapType = 'anonymous';
req.profiler.add(stats);
if (err) {
return next(err);
}

View File

@@ -147,10 +147,6 @@ function incrementErrorMetrics (statsClient) {
function tileError () {
return function tileErrorMiddleware (err, req, res, next) {
if (err.message === 'Tile does not exist' && req.params.format === 'mvt') {
res.statusCode = 204;
return next();
}
// See https://github.com/Vizzuality/Windshaft-cartodb/issues/68
let errMsg = err.message ? ( '' + err.message ) : ( '' + err );

View File

@@ -165,8 +165,12 @@ function getTemplate (
params
);
mapConfigProvider.getMapConfig((err, mapConfig, rendererParams) => {
mapConfigProvider.getMapConfig((err, mapConfig, rendererParams, context, stats = {}) => {
req.profiler.done('named.getMapConfig');
stats.mapType = 'named';
req.profiler.add(stats);
if (err) {
return next(err);
}

View File

@@ -1,7 +1,5 @@
var assert = require('assert');
var _ = require('underscore');
var PSQL = require('cartodb-psql');
var step = require('step');
var BBoxFilter = require('../models/filter/bbox');
var DataviewFactory = require('../models/dataview/factory');
var DataviewFactoryWithOverviews = require('../models/dataview/overviews/factory');
@@ -21,53 +19,76 @@ function DataviewBackend(analysisBackend) {
module.exports = DataviewBackend;
DataviewBackend.prototype.getDataview = function (mapConfigProvider, user, params, callback) {
const dataviewName = params.dataviewName;
var dataviewName = params.dataviewName;
step(
function getMapConfig() {
mapConfigProvider.getMapConfig(this);
},
function runDataviewQuery(err, mapConfig) {
assert.ifError(err);
mapConfigProvider.getMapConfig(function (err, mapConfig) {
if (err) {
return callback(err);
}
var dataviewDefinition = getDataviewDefinition(mapConfig.obj(), dataviewName);
if (!dataviewDefinition) {
throw new Error("Dataview '" + dataviewName + "' does not exists");
}
var dataviewDefinition = getDataviewDefinition(mapConfig.obj(), dataviewName);
if (!dataviewDefinition) {
const error = new Error(`Dataview '${dataviewName}' does not exist`);
error.type = 'dataview';
error.http_status = 400;
return callback(error);
}
if (!validFilterParams(params)) {
const error = new Error('Both own_filter and no_filters cannot be sent in the same request');
error.type = 'dataview';
error.http_status = 400;
return callback(error);
}
var pg;
var overrideParams;
var dataview;
try {
pg = new PSQL(dbParamsFromReqParams(params));
var query = getQueryWithFilters(dataviewDefinition, params);
var queryRewriteData = getQueryRewriteData(mapConfig, dataviewDefinition, params);
var dataviewFactory = DataviewFactoryWithOverviews.getFactory(overviewsQueryRewriter, queryRewriteData, {
bbox: params.bbox
});
dataview = dataviewFactory.getDataview(query, dataviewDefinition);
var ownFilter = +params.own_filter;
var noFilters = +params.no_filters;
if (Number.isFinite(ownFilter) && Number.isFinite(noFilters)) {
err = new Error();
err.message = 'Both own_filter and no_filters cannot be sent in the same request';
err.type = 'dataview';
err.http_status = 400;
overrideParams = getOverrideParams(params, !!ownFilter);
} catch (error) {
return callback(error);
}
dataview.getResult(pg, overrideParams, function (err, dataviewResult, stats = {}) {
if (err) {
return callback(err);
}
var pg = new PSQL(dbParamsFromReqParams(params));
var query = getDataviewQuery(dataviewDefinition, ownFilter, noFilters);
if (params.bbox) {
var bboxFilter = new BBoxFilter({column: 'the_geom_webmercator', srid: 3857}, {bbox: params.bbox});
query = bboxFilter.sql(query);
}
var queryRewriteData = getQueryRewriteData(mapConfig, dataviewDefinition, params);
var dataviewFactory = DataviewFactoryWithOverviews.getFactory(
overviewsQueryRewriter, queryRewriteData, { bbox: params.bbox }
);
var dataview = dataviewFactory.getDataview(query, dataviewDefinition);
dataview.getResult(pg, getOverrideParams(params, !!ownFilter), this);
},
function returnCallback(err, result) {
return callback(err, result);
}
);
return callback(null, dataviewResult, stats);
});
});
};
function validFilterParams (params) {
var ownFilter = +params.own_filter;
var noFilters = +params.no_filters;
return !(Number.isFinite(ownFilter) && Number.isFinite(noFilters));
}
function getQueryWithFilters (dataviewDefinition, params) {
var ownFilter = +params.own_filter;
var noFilters = +params.no_filters;
var query = getDataviewQuery(dataviewDefinition, ownFilter, noFilters);
if (params.bbox) {
var bboxFilter = new BBoxFilter({column: 'the_geom_webmercator', srid: 3857}, {bbox: params.bbox});
query = bboxFilter.sql(query);
}
return query;
}
function getDataviewQuery(dataviewDefinition, ownFilter, noFilters) {
if (noFilters) {
return dataviewDefinition.sql.no_filters;
@@ -129,41 +150,56 @@ function getOverrideParams(params, ownFilter) {
}
DataviewBackend.prototype.search = function (mapConfigProvider, user, dataviewName, params, callback) {
step(
function getMapConfig() {
mapConfigProvider.getMapConfig(this);
},
function runDataviewSearchQuery(err, mapConfig) {
assert.ifError(err);
var dataviewDefinition = getDataviewDefinition(mapConfig.obj(), dataviewName);
if (!dataviewDefinition) {
throw new Error("Dataview '" + dataviewName + "' does not exists");
}
var pg = new PSQL(dbParamsFromReqParams(params));
var ownFilter = +params.own_filter;
ownFilter = !!ownFilter;
var query = (ownFilter) ? dataviewDefinition.sql.own_filter_on : dataviewDefinition.sql.own_filter_off;
if (params.bbox) {
var bboxFilter = new BBoxFilter({column: 'the_geom', srid: 4326}, {bbox: params.bbox});
query = bboxFilter.sql(query);
}
var userQuery = params.q;
var dataview = DataviewFactory.getDataview(query, dataviewDefinition);
dataview.search(pg, userQuery, this);
},
function returnCallback(err, result) {
return callback(err, result);
mapConfigProvider.getMapConfig(function (err, mapConfig) {
if (err) {
return callback(err);
}
);
var dataviewDefinition = getDataviewDefinition(mapConfig.obj(), dataviewName);
if (!dataviewDefinition) {
const error = new Error(`Dataview '${dataviewName}' does not exist`);
error.type = 'dataview';
error.http_status = 400;
return callback(error);
}
var pg;
var query;
var dataview;
var userQuery = params.q;
try {
pg = new PSQL(dbParamsFromReqParams(params));
query = getQueryWithOwnFilters(dataviewDefinition, params);
dataview = DataviewFactory.getDataview(query, dataviewDefinition);
} catch (error) {
return callback(error);
}
dataview.search(pg, userQuery, function (err, result) {
if (err) {
return callback(err);
}
return callback(null, result);
});
});
};
function getQueryWithOwnFilters (dataviewDefinition, params) {
var ownFilter = +params.own_filter;
ownFilter = !!ownFilter;
var query = (ownFilter) ? dataviewDefinition.sql.own_filter_on : dataviewDefinition.sql.own_filter_off;
if (params.bbox) {
var bboxFilter = new BBoxFilter({ column: 'the_geom', srid: 4326 }, { bbox: params.bbox });
query = bboxFilter.sql(query);
}
return query;
}
function getDataviewDefinition(mapConfig, dataviewName) {
var dataviews = mapConfig.dataviews || {};
return dataviews[dataviewName];

View File

@@ -1,5 +1,4 @@
var _ = require('underscore');
var step = require('step');
var AnalysisFilter = require('../models/filter/analysis');
function FilterStatsBackends(pgQueryRunner) {
@@ -24,36 +23,29 @@ function getEstimatedRows(pgQueryRunner, username, query, callback) {
}
FilterStatsBackends.prototype.getFilterStats = function (username, unfiltered_query, filters, callback) {
var stats = {};
var self = this;
step(
function getUnfilteredRows() {
getEstimatedRows(self.pgQueryRunner, username, unfiltered_query, this);
},
function receiveUnfilteredRows(err, rows) {
if (err){
callback(err);
return;
}
stats.unfiltered_rows = rows;
this(null, rows);
},
function getFilteredRows() {
if ( filters && !_.isEmpty(filters)) {
var analysisFilter = new AnalysisFilter(filters);
var query = analysisFilter.sql(unfiltered_query);
getEstimatedRows(self.pgQueryRunner, username, query, this);
} else {
this(null, null);
}
},
function receiveFilteredRows(err, rows) {
if (err){
callback(err);
return;
}
stats.filtered_rows = rows;
callback(null, stats);
}
);
var stats = {};
getEstimatedRows(this.pgQueryRunner, username, unfiltered_query, (err, rows) => {
if (err){
return callback(err);
}
stats.unfiltered_rows = rows;
if (!filters || _.isEmpty(filters)) {
return callback(null, stats);
}
var analysisFilter = new AnalysisFilter(filters);
var query = analysisFilter.sql(unfiltered_query);
getEstimatedRows(this.pgQueryRunner, username, query, (err, rows) => {
if (err){
return callback(err);
}
stats.filtered_rows = rows;
return callback(null, stats);
});
});
};

View File

@@ -148,7 +148,7 @@ function _columnStats(ctx, columns) {
Object.keys(columns).forEach(name => {
aggr = aggr.concat(
columnAggregations(columns[name])
.map(fn => `${fn}(${name}) AS ${name}_${fn}`)
.map(fn => `${fn}("${name}") AS "${name}_${fn}"`)
);
if (columns[name].type === 'string') {
const topN = ctx.metaOptions.columnStats.topCategories || 1024;

View File

@@ -1,7 +1,5 @@
var assert = require('assert');
var crypto = require('crypto');
var debug = require('debug')('windshaft:templates');
var step = require('step');
var _ = require('underscore');
var dot = require('dot');
@@ -69,27 +67,19 @@ TemplateMaps.prototype._userTemplateLimit = function() {
* @param callback - function to pass results too.
*/
TemplateMaps.prototype._redisCmd = function(redisFunc, redisArgs, callback) {
var redisClient;
var that = this;
var db = that.db_signatures;
this.redis_pool.acquire(this.db_signatures, (err, redisClient) => {
if (err) {
return callback(err);
}
step(
function getRedisClient() {
that.redis_pool.acquire(db, this);
},
function executeQuery(err, data) {
assert.ifError(err);
redisClient = data;
redisArgs.push(this);
redisClient[redisFunc.toUpperCase()].apply(redisClient, redisArgs);
},
function releaseRedisClient(err, data) {
if ( ! _.isUndefined(redisClient) ) {
that.redis_pool.release(db, redisClient);
}
callback(err, data);
}
);
redisClient[redisFunc.toUpperCase()](...redisArgs, (err, data) => {
this.redis_pool.release(this.db_signatures, redisClient);
if (err) {
return callback(err);
}
return callback(null, data);
});
});
};
var _reValidNameIdentifier = /^[a-z0-9][0-9a-z_\-]*$/i;
@@ -184,6 +174,37 @@ function templateDefaults(template) {
});
}
/**
* Checks if the if the user reaches the templetes limit
*
* @param userTemplatesKey user templat key in Redis
* @param owner cartodb username of the template owner
* @param callback returns error if the user reaches the limit
*/
TemplateMaps.prototype._checkUserTemplatesLimit = function(userTemplatesKey, owner, callback) {
const limit = this._userTemplateLimit();
if(!limit) {
return callback();
}
this._redisCmd('HLEN', [userTemplatesKey], (err, numberOfTemplates) => {
if (err) {
return callback(err);
}
if (numberOfTemplates >= limit) {
const limitReachedError = new Error(
`User '${owner}' reached limit on number of templates (${numberOfTemplates}/${limit})`
);
limitReachedError.http_status = 409;
return callback(limitReachedError);
}
return callback();
});
};
//--------------- PUBLIC API -------------------------------------
// Add a template
@@ -199,52 +220,41 @@ function templateDefaults(template) {
// Return template identifier (only valid for given user)
//
TemplateMaps.prototype.addTemplate = function(owner, template, callback) {
var self = this;
template = templateDefaults(template);
var invalidError = this._checkInvalidTemplate(template);
if ( invalidError ) {
if (invalidError) {
return callback(invalidError);
}
var templateName = template.name;
var userTemplatesKey = this.key_usr_tpl({ owner:owner });
var limit = this._userTemplateLimit();
var userTemplatesKey = this.key_usr_tpl({ owner });
step(
function checkLimit() {
if ( ! limit ) {
return 0;
}
self._redisCmd('HLEN', [ userTemplatesKey ], this);
},
function installTemplateIfDoesNotExist(err, numberOfTemplates) {
assert.ifError(err);
if ( limit && numberOfTemplates >= limit ) {
var limitReachedError = new Error("User '" + owner + "' reached limit on number of templates (" +
numberOfTemplates + "/" + limit + ")");
limitReachedError.http_status = 409;
throw limitReachedError;
}
self._redisCmd('HSETNX', [ userTemplatesKey, templateName, JSON.stringify(template) ], this);
},
function validateInstallation(err, wasSet) {
assert.ifError(err);
if ( ! wasSet ) {
throw new Error("Template '" + templateName + "' of user '" + owner + "' already exists");
}
return true;
},
function finish(err) {
if (!err) {
self.emit('add', owner, templateName, template);
}
callback(err, templateName, template);
this._checkUserTemplatesLimit(userTemplatesKey, owner, err => {
if (err) {
return callback(err);
}
);
let templateString;
try {
templateString = JSON.stringify(template);
} catch (error) {
return callback(error);
}
this._redisCmd('HSETNX', [userTemplatesKey, template.name, templateString], (err, wasSet) => {
if (err) {
return callback(err);
}
if (!wasSet) {
var templateExistsError = new Error(`Template '${template.name}' of user '${owner}' already exists`);
return callback(templateExistsError);
}
this.emit('add', owner, template.name, template);
return callback(null, template.name, template);
});
});
};
// Delete a template
@@ -257,26 +267,18 @@ TemplateMaps.prototype.addTemplate = function(owner, template, callback) {
// @param callback function(err)
//
TemplateMaps.prototype.delTemplate = function(owner, tpl_id, callback) {
var self = this;
step(
function deleteTemplate() {
self._redisCmd('HDEL', [ self.key_usr_tpl({ owner:owner }), tpl_id ], this);
},
function handleDeletion(err, deleted) {
assert.ifError(err);
if (!deleted) {
throw new Error("Template '" + tpl_id + "' of user '" + owner + "' does not exist");
}
return true;
},
function finish(err) {
if (!err) {
self.emit('delete', owner, tpl_id);
}
callback(err);
this._redisCmd('HDEL', [ this.key_usr_tpl({ owner:owner }), tpl_id ], (err, deleted) => {
if (err) {
return callback(err);
}
);
if (!deleted) {
return callback(new Error(`Template '${tpl_id}' of user '${owner}' does not exist`));
}
this.emit('delete', owner, tpl_id);
return callback();
});
};
// Update a template
@@ -296,56 +298,58 @@ TemplateMaps.prototype.delTemplate = function(owner, tpl_id, callback) {
// @param callback function(err)
//
TemplateMaps.prototype.updTemplate = function(owner, tpl_id, template, callback) {
var self = this;
template = templateDefaults(template);
var invalidError = this._checkInvalidTemplate(template);
if ( invalidError ) {
if (invalidError) {
return callback(invalidError);
}
var templateName = template.name;
if ( tpl_id !== templateName ) {
return callback(new Error("Cannot update name of a map template ('" + tpl_id + "' != '" + templateName + "')"));
if (tpl_id !== template.name) {
return callback(new Error(`Cannot update name of a map template ('${tpl_id}' != '${template.name}')`));
}
var userTemplatesKey = this.key_usr_tpl({ owner:owner });
var userTemplatesKey = this.key_usr_tpl({ owner });
var previousTemplate = null;
this._redisCmd('HGET', [userTemplatesKey, tpl_id], (err, beforeUpdateTemplate) => {
if (err) {
return callback(err);
}
step(
function getExistingTemplate() {
self._redisCmd('HGET', [ userTemplatesKey, tpl_id ], this);
},
function updateTemplate(err, _currentTemplate) {
assert.ifError(err);
if (!_currentTemplate) {
throw new Error("Template '" + tpl_id + "' of user '" + owner + "' does not exist");
if (!beforeUpdateTemplate) {
return callback(new Error(`Template '${tpl_id}' of user '${owner}' does not exist`));
}
let templateString;
try {
templateString = JSON.stringify(template);
} catch (error) {
return callback(error);
}
this._redisCmd('HSET', [userTemplatesKey, template.name, templateString], (err, didSetNewField) => {
if (err) {
return callback(err);
}
previousTemplate = _currentTemplate;
self._redisCmd('HSET', [ userTemplatesKey, templateName, JSON.stringify(template) ], this);
},
function handleTemplateUpdate(err, didSetNewField) {
assert.ifError(err);
if (didSetNewField) {
debug('New template created on update operation');
}
return true;
},
function finish(err) {
if (!err) {
if (self.fingerPrint(JSON.parse(previousTemplate)) !== self.fingerPrint(template)) {
self.emit('update', owner, templateName, template);
}
let beforeUpdateTemplateObject;
try {
beforeUpdateTemplateObject = JSON.parse(beforeUpdateTemplate);
} catch (error) {
return callback(error);
}
callback(err, template);
}
);
if (this.fingerPrint(beforeUpdateTemplateObject) !== this.fingerPrint(template)) {
this.emit('update', owner, template.name, template);
}
return callback(null, template);
});
});
};
// List user templates
@@ -370,19 +374,20 @@ TemplateMaps.prototype.listTemplates = function(owner, callback) {
// Return full template definition
//
TemplateMaps.prototype.getTemplate = function(owner, tpl_id, callback) {
var self = this;
step(
function getTemplate() {
self._redisCmd('HGET', [ self.key_usr_tpl({owner:owner}), tpl_id ], this);
},
function parseTemplate(err, tpl_val) {
assert.ifError(err);
return JSON.parse(tpl_val);
},
function finish(err, tpl) {
callback(err, tpl);
this._redisCmd('HGET', [this.key_usr_tpl({owner:owner}), tpl_id], (err, template) => {
if (err) {
return callback(err);
}
);
let templateObject;
try {
templateObject = JSON.parse(template);
} catch (error) {
return callback(error);
}
return callback(null, templateObject);
});
};
TemplateMaps.prototype.isAuthorized = function(template, authTokens) {

View File

@@ -1,5 +1,3 @@
var step = require('step');
/**
*
* @param metadataBackend
@@ -41,45 +39,38 @@ UserLimitsBackend.prototype.getRenderLimits = function (username, apiKey, callba
};
UserLimitsBackend.prototype.getTimeoutRenderLimit = function (username, apiKey, callback) {
var self = this;
step(
function isAuthorized() {
var next = this;
if (!apiKey) {
return next(null, false);
}
self.metadataBackend.getUserMapKey(username, function (err, userApiKey) {
if (err) {
return next(err);
}
return next(null, userApiKey === apiKey);
});
},
function getUserTimeoutRenderLimits(err, authorized) {
var next = this;
isAuthorized(this.metadataBackend, username, apiKey, (err, authorized) => {
if (err) {
return callback(err);
}
this.metadataBackend.getUserTimeoutRenderLimits(username, (err, timeoutRenderLimit) => {
if (err) {
return next(err);
return callback(err);
}
self.metadataBackend.getUserTimeoutRenderLimits(username, function (err, timeoutRenderLimit) {
if (err) {
return next(err);
}
next(null, {
render: authorized ? timeoutRenderLimit.render : timeoutRenderLimit.renderPublic
});
});
},
callback
);
return callback(
null,
{ render: authorized ? timeoutRenderLimit.render : timeoutRenderLimit.renderPublic }
);
});
});
};
function isAuthorized(metadataBackend, username, apiKey, callback) {
if (!apiKey) {
return callback(null, false);
}
metadataBackend.getUserMapKey(username, function (err, userApiKey) {
if (err) {
return callback(err);
}
return callback(null, userApiKey === apiKey);
});
}
UserLimitsBackend.prototype.preprareRateLimit = function () {
if (this.options.limits.rateLimitsEnabled) {
this.metadataBackend.loadRateLimitsScript();

View File

@@ -117,17 +117,19 @@ const dimensionNames = (ctx, table) => {
let dimensions = aggregateDimensions(ctx);
if (table) {
return sep(Object.keys(dimensions).map(
dimension_name => `${table}.${dimension_name}`
dimension_name => `${table}."${dimension_name}"`
));
}
return sep(Object.keys(dimensions));
return sep(Object.keys(dimensions).map(dimension_name => {
return `"${dimension_name}"`;
}));
};
const dimensionDefs = ctx => {
let dimensions = aggregateDimensions(ctx);
return sep(Object.keys(dimensions).map(dimension_name => {
const expression = dimensions[dimension_name];
return `${expression} AS ${dimension_name}`;
return `"${expression}" AS "${dimension_name}"`;
}));
};
@@ -274,23 +276,24 @@ const spatialFilter = `
// * This queries are used for rendering and the_geom is omitted in the results for better performance
// * If the MVT extent or tile buffer was 0 or a multiple of the resolution we could use directly
// the bbox for them, but in general we need to find the nearest cell limits inside the bbox.
// * bbox coordinates can have an error in the last digites; we apply a small correction before
// applying CEIL or FLOOR to compensate for this.
// * bbox coordinates can have an error in the last digits; we apply a small correction before
// applying CEIL or FLOOR to compensate for this, so that coordinates closer than a small (`eps`)
// fraction of the cell size to a cell limit are moved to the exact limit.
const sqlParams = (ctx) => `
_cdb_res AS (
SELECT
${gridResolution(ctx)} AS res,
!bbox! AS bbox,
(2*2.220446049250313e-16::double precision) AS eps
(1E-6::double precision) AS eps
),
_cdb_params AS (
SELECT
res,
bbox,
CEIL((ST_XMIN(bbox) - eps*ABS(ST_XMIN(bbox)))/res)*res AS xmin,
FLOOR((ST_XMAX(bbox) + eps*ABS(ST_XMAX(bbox)))/res)*res AS xmax,
CEIL((ST_YMIN(bbox) - eps*ABS(ST_YMIN(bbox)))/res)*res AS ymin,
FLOOR((ST_YMAX(bbox) + eps*ABS(ST_YMAX(bbox)))/res)*res AS ymax
CEIL((ST_XMIN(bbox) - eps*res)/res)*res AS xmin,
FLOOR((ST_XMAX(bbox) + eps*res)/res)*res AS xmax,
CEIL((ST_YMIN(bbox) - eps*res)/res)*res AS ymin,
FLOOR((ST_YMAX(bbox) + eps*res)/res)*res AS ymax
FROM _cdb_res
)
`;
@@ -379,7 +382,7 @@ const aggregationQueryTemplates = {
)
SELECT
_cdb_clusters.cartodb_id,
the_geom, the_geom_webmercator
the_geom_webmercator
${dimensionNames(ctx, '_cdb_clusters')}
${aggregateColumnNames(ctx, '_cdb_clusters')}
FROM

View File

@@ -21,7 +21,7 @@ function getPGTypeName (pgType) {
module.exports = class BaseDataview {
getResult (psql, override, callback) {
this.sql(psql, override, (err, query) => {
this.sql(psql, override, (err, query, flags = null) => {
if (err) {
return callback(err);
}
@@ -34,8 +34,20 @@ module.exports = class BaseDataview {
result = this.format(result, override);
result.type = this.getType();
return callback(null, result);
//Overviews logging
const stats = {};
if (flags && flags.usesOverviews !== undefined) {
stats.usesOverviews = flags.usesOverviews;
} else {
stats.usesOverviews = false;
}
if (this.getType) {
stats.dataviewType = this.getType();
}
return callback(null, result, stats);
}, true); // use read-only transaction
});
}

View File

@@ -209,7 +209,7 @@ Aggregation.prototype.sql = function(psql, override, callback) {
debug(aggregationSql);
return callback(null, aggregationSql);
return callback(null, aggregationSql, { usesOverviews: true });
};
var aggregationFnQueryTpl = {

View File

@@ -74,5 +74,5 @@ Formula.prototype.sql = function (psql, override, callback) {
debug(formulaSql);
return callback(null, formulaSql);
return callback(null, formulaSql, { usesOverviews: true });
};

View File

@@ -178,7 +178,7 @@ Histogram.prototype.sql = function(psql, override, callback) {
var histogramSql = this._buildQuery(override);
return callback(null, histogramSql);
return callback(null, histogramSql, { usesOverviews: true });
};
Histogram.prototype._buildQuery = function (override) {

View File

@@ -11,16 +11,21 @@ MapConfigAdapter.prototype.getMapConfig = function(user, requestMapConfig, param
var i = 0;
var tasksLeft = this.adapters.length;
function next(err, _requestMapConfig) {
let mapConfigStats = {};
function next(err, _requestMapConfig, adapterStats = {}) {
if (err) {
return callback(err);
}
mapConfigStats = Object.assign(mapConfigStats, adapterStats);
if (tasksLeft-- === 0) {
return callback(null, _requestMapConfig);
return callback(null, _requestMapConfig, mapConfigStats);
}
var nextAdapter = self.adapters[i++];
nextAdapter.getMapConfig(user, _requestMapConfig, params, context, next);
}
next(null, requestMapConfig);
next(null, requestMapConfig, mapConfigStats);
};

View File

@@ -1,4 +1,3 @@
var step = require('step');
var queue = require('queue-async');
var _ = require('underscore');
@@ -9,93 +8,127 @@ function MapConfigOverviewsAdapter(overviewsMetadataBackend, filterStatsBackend)
module.exports = MapConfigOverviewsAdapter;
MapConfigOverviewsAdapter.prototype.getMapConfig = function(user, requestMapConfig, params, context, callback) {
var self = this;
var layers = requestMapConfig.layers;
var analysesResults = context.analysesResults;
if (!layers || layers.length === 0) {
return callback(null, requestMapConfig);
}
var augmentLayersQueue = queue(layers.length);
function augmentLayer(layer, done) {
if ( layer.type !== 'mapnik' && layer.type !== 'cartodb' ) {
return done(null, layer);
}
self.overviewsMetadataBackend.getOverviewsMetadata(user, layer.options.sql, function(err, metadata){
if (err) {
done(err, layer);
} else {
var query_rewrite_data = { overviews: metadata };
step(
function collectFiltersData() {
var filters, unfiltered_query;
if ( layer.options.source && analysesResults && !layer.options.sql_wrap) {
var sourceId = layer.options.source.id;
var node = _.find(analysesResults, function(a){ return a.rootNode.params.id === sourceId; });
if ( node ) {
node = node.rootNode;
filters = node.getFilters();
var filters_disabler = Object.keys(filters).reduce(
function(disabler, filter_id){ disabler[filter_id] = false; return disabler; },
{}
);
unfiltered_query = node.getQuery(filters_disabler);
query_rewrite_data.filters = filters;
query_rewrite_data.unfiltered_query = unfiltered_query;
}
}
this(null, filters, unfiltered_query);
},
function collectStatsData(err, filters, unfiltered_query) {
var next_step = this;
if ( filters ) {
self.filterStatsBackend.getFilterStats(
user,
unfiltered_query, filters,
function(err, stats) {
if ( !err ) {
query_rewrite_data.filter_stats = stats;
}
return next_step(err);
}
);
} else {
return next_step(null);
}
},
function addDataToLayer(err) {
if ( !err && !_.isEmpty(metadata) ) {
layer = _.extend({}, layer);
layer.options = _.extend({}, layer.options, { query_rewrite_data: query_rewrite_data });
}
done(null, layer);
}
);
}
});
}
function layersAugmentQueueFinish(err, layers) {
if (err) {
return callback(err);
}
MapConfigOverviewsAdapter.prototype.getMapConfig = function (user, requestMapConfig, params, context, callback) {
var layers = requestMapConfig.layers;
var analysesResults = context.analysesResults;
if (!layers || layers.length === 0) {
return callback(new Error('Missing layers array from layergroup config'));
return callback(null, requestMapConfig);
}
requestMapConfig.layers = layers;
var augmentLayersQueue = queue(layers.length);
return callback(null, requestMapConfig);
}
layers.forEach(layer => augmentLayersQueue.defer(this._augmentLayer.bind(this), user, layer, analysesResults));
layers.forEach(function(layer) {
augmentLayersQueue.defer(augmentLayer, layer);
});
augmentLayersQueue.awaitAll(layersAugmentQueueFinish);
augmentLayersQueue.awaitAll(function layersAugmentQueueFinish (err, results) {
if (err) {
return callback(err);
}
const layers = results.map(result => result.layer);
const overviewsAddedToMapconfig = results.some(result => result.overviewsAddedToMapconfig);
if (!layers || layers.length === 0) {
return callback(new Error('Missing layers array from layergroup config'));
}
requestMapConfig.layers = layers;
const stats = { overviewsAddedToMapconfig };
return callback(null, requestMapConfig, stats);
});
};
MapConfigOverviewsAdapter.prototype._augmentLayer = function (user, layer, analysesResults, callback) {
let overviewsAddedToMapconfig = false;
if (layer.type !== 'mapnik' && layer.type !== 'cartodb') {
return callback(null, { layer, overviewsAddedToMapconfig });
}
this.overviewsMetadataBackend.getOverviewsMetadata(user, layer.options.sql, (err, metadata) => {
if (err) {
return callback(err, { layer, overviewsAddedToMapconfig });
}
if (_.isEmpty(metadata)) {
return callback(null, { layer, overviewsAddedToMapconfig });
}
var filters = getFilters(analysesResults, layer);
overviewsAddedToMapconfig = true;
if (!filters) {
layer.options = Object.assign({}, layer.options, getQueryRewriteData(layer, analysesResults, {
overviews: metadata
}));
return callback(null, { layer, overviewsAddedToMapconfig });
}
var unfilteredQuery = getUnfilteredQuery(analysesResults, layer);
this.filterStatsBackend.getFilterStats(user, unfilteredQuery, filters, function (err, stats) {
if (err) {
return callback(null, { layer, overviewsAddedToMapconfig });
}
layer.options = Object.assign({}, layer.options, getQueryRewriteData(layer, analysesResults, {
overviews: metadata,
filter_stats: stats
}));
return callback(null, { layer, overviewsAddedToMapconfig });
});
});
};
function getRootNode (analysesResults, sourceId) {
var node = _.find(analysesResults, function (a) {
return a.rootNode.params.id === sourceId;
});
return node ? node.rootNode : undefined;
}
function getFilters (analysesResults, layer) {
if (layer.options.source && analysesResults && !layer.options.sql_wrap) {
var sourceId = layer.options.source.id;
var node = getRootNode(analysesResults, sourceId);
if (node) {
return node.getFilters();
}
}
}
function getUnfilteredQuery (analysesResults, layer) {
if (layer.options.source && analysesResults && !layer.options.sql_wrap) {
var sourceId = layer.options.source.id;
var node = getRootNode(analysesResults, sourceId);
if (node) {
var filters = node.getFilters();
var filters_disabler = Object.keys(filters).reduce(function (disabler, filter_id) {
disabler[filter_id] = false;
return disabler;
}, {});
return node.getQuery(filters_disabler);
}
}
}
function getQueryRewriteData (layer, analysesResults, extend = {}) {
var queryRewriteData = {};
if (layer.options.source && analysesResults && !layer.options.sql_wrap) {
queryRewriteData.filters = getFilters(analysesResults, layer);
queryRewriteData.unfiltered_query = getUnfilteredQuery(analysesResults, layer);
}
queryRewriteData = Object.assign({}, queryRewriteData, extend);
return { query_rewrite_data: queryRewriteData };
}

View File

@@ -99,7 +99,7 @@ module.exports = class NamedMapMapConfigProvider extends BaseMapConfigProvider {
const { user, rendererParams } = this;
this.mapConfigAdapter.getMapConfig(
user, requestMapConfig, rendererParams, context, (err, mapConfig) => {
user, requestMapConfig, rendererParams, context, (err, mapConfig, stats = {}) => {
if (err) {
this.err = err;
return callback(err);
@@ -108,7 +108,7 @@ module.exports = class NamedMapMapConfigProvider extends BaseMapConfigProvider {
this.mapConfig = (mapConfig === null) ? null : new MapConfig(mapConfig, context.datasource);
this.analysesResults = context.analysesResults || [];
return callback(null, this.mapConfig, this.rendererParams, this.context);
return callback(null, this.mapConfig, this.rendererParams, this.context, stats);
});
});
});

View File

@@ -14,7 +14,7 @@ const DATE_OIDS = Object.freeze({
function wrapDates(originalQuery, fields) {
return `
SELECT
${fields.map(field => _isDateType(field) ? _castColumnToEpoch(field.name) : `${field.name}`).join(',')}
${fields.map(field => _isDateType(field) ? _castColumnToEpoch(field.name) : `"${field.name}"`).join(',')}
FROM
(${originalQuery}) _cdb_epoch_transformation `;
}

View File

@@ -1,7 +1,7 @@
{
"private": true,
"name": "windshaft-cartodb",
"version": "6.3.0",
"version": "6.4.0",
"description": "A map tile server for CartoDB",
"keywords": [
"cartodb"
@@ -27,7 +27,7 @@
"@carto/fqdn-sync": "0.2.2",
"basic-auth": "2.0.0",
"body-parser": "1.18.3",
"camshaft": "0.62.1",
"camshaft": "0.62.3",
"cartodb-psql": "0.11.0",
"cartodb-query-tables": "0.3.0",
"cartodb-redis": "2.0.1",
@@ -45,11 +45,10 @@
"redis-mpool": "0.5.0",
"request": "2.87.0",
"semver": "5.5.0",
"step": "1.0.0",
"step-profiler": "0.3.0",
"turbo-carto": "0.20.4",
"underscore": "1.6.0",
"windshaft": "4.8.3",
"windshaft": "4.10.0",
"yargs": "11.1.0"
},
"devDependencies": {
@@ -59,6 +58,7 @@
"moment": "2.22.1",
"nock": "9.2.6",
"redis": "2.8.0",
"step": "1.0.0",
"strftime": "0.10.0"
},
"scripts": {

View File

@@ -171,7 +171,7 @@ describe('aggregation', function () {
//
const POINTS_SQL_GRID = `
WITH params AS (
SELECT CDB_XYZ_Resolution(1) AS l -- cell size for Z=1 res=1
SELECT CDB_XYZ_Resolution($Z)*$resolution AS l -- cell size for Z, resolution
)
SELECT
row_number() OVER () AS cartodb_id,
@@ -1640,7 +1640,7 @@ describe('aggregation', function () {
});
});
['centroid', 'point-sample', 'point-grid'].forEach(placement => {
['centroid', 'point-sample', 'point-grid', 'default'].forEach(placement => {
it(`cartodb_id should be present in ${placement} aggregation`, function(done) {
this.mapConfig = createVectorMapConfig([
{
@@ -1648,8 +1648,8 @@ describe('aggregation', function () {
options: {
sql: POINTS_SQL_1,
aggregation: {
placement: placement,
threshold: 1
threshold: 1,
placement: placement !== 'default' ? placement : undefined
},
cartocss: '#layer { marker-width: 1; }',
cartocss_version: '2.3.0',
@@ -1673,71 +1673,72 @@ describe('aggregation', function () {
done();
});
});
});
it('should only require the_geom_webmercator for aggregation', function (done) {
this.mapConfig = createVectorMapConfig([
{
type: 'cartodb',
options: {
sql: POINTS_SQL_ONLY_WEBMERCATOR,
aggregation: {
threshold: 1
it(`should only require the_geom_webmercator for ${placement} aggregation`, function (done) {
this.mapConfig = createVectorMapConfig([
{
type: 'cartodb',
options: {
sql: POINTS_SQL_ONLY_WEBMERCATOR,
aggregation: {
threshold: 1,
placement: placement !== 'default' ? placement : undefined
}
}
}
}
]);
this.testClient = new TestClient(this.mapConfig);
]);
this.testClient = new TestClient(this.mapConfig);
this.testClient.getLayergroup((err, body) => {
if (err) {
return done(err);
}
this.testClient.getLayergroup((err, body) => {
if (err) {
return done(err);
}
assert.equal(typeof body.metadata, 'object');
assert.ok(Array.isArray(body.metadata.layers));
assert.equal(typeof body.metadata, 'object');
assert.ok(Array.isArray(body.metadata.layers));
body.metadata.layers.forEach(layer => assert.ok(layer.meta.aggregation.mvt));
body.metadata.layers.forEach(layer => assert.ok(!layer.meta.aggregation.png));
body.metadata.layers.forEach(layer => assert.ok(layer.meta.aggregation.mvt));
body.metadata.layers.forEach(layer => assert.ok(!layer.meta.aggregation.png));
done();
done();
});
});
});
it('aggregation should work with attributes', function (done) {
this.mapConfig = createVectorMapConfig([
{
type: 'cartodb',
options: {
sql: POINTS_SQL_1,
cartocss: '#layer { marker-width: 7; }',
cartocss_version: '2.3.0',
aggregation: {
threshold: 1
},
attributes: {
id: 'cartodb_id',
columns: [
'value'
]
it(`${placement} aggregation should work with attributes`, function (done) {
this.mapConfig = createVectorMapConfig([
{
type: 'cartodb',
options: {
sql: POINTS_SQL_1,
cartocss: '#layer { marker-width: 7; }',
cartocss_version: '2.3.0',
aggregation: {
threshold: 1
},
attributes: {
id: 'cartodb_id',
columns: [
'value'
]
}
}
}
}
]);
this.testClient = new TestClient(this.mapConfig);
]);
this.testClient = new TestClient(this.mapConfig);
this.testClient.getLayergroup((err, body) => {
if (err) {
return done(err);
}
this.testClient.getLayergroup((err, body) => {
if (err) {
return done(err);
}
assert.equal(typeof body.metadata, 'object');
assert.ok(Array.isArray(body.metadata.layers));
assert.equal(typeof body.metadata, 'object');
assert.ok(Array.isArray(body.metadata.layers));
body.metadata.layers.forEach(layer => assert.ok(layer.meta.aggregation.mvt));
body.metadata.layers.forEach(layer => assert.ok(layer.meta.aggregation.png));
body.metadata.layers.forEach(layer => assert.ok(layer.meta.aggregation.mvt));
body.metadata.layers.forEach(layer => assert.ok(layer.meta.aggregation.png));
done();
done();
});
});
});
@@ -2257,15 +2258,13 @@ describe('aggregation', function () {
sql: POINTS_SQL_0,
aggregation: {
threshold: 1,
resolution: 1
resolution: 1,
placement: placement !== 'default' ? placement : undefined
}
}
}
]
};
if (placement !== 'default') {
this.mapConfig.layers[0].options.aggregation.placement = placement;
}
this.testClient = new TestClient(this.mapConfig);
@@ -2312,15 +2311,13 @@ describe('aggregation', function () {
sql: POINTS_SQL_1,
resolution: 1,
aggregation: {
threshold: 1
threshold: 1,
placement: placement !== 'default' ? placement : undefined
}
}
}
]
};
if (placement !== 'default') {
this.mapConfig.layers[0].options.aggregation.placement = placement;
}
this.testClient = new TestClient(this.mapConfig);
@@ -2375,6 +2372,9 @@ describe('aggregation', function () {
});
it(`for ${placement} each aggr. cell is in a single tile`, function (done) {
const z = 1;
const resolution = 1;
const query = POINTS_SQL_GRID.replace('$Z', z).replace('$resolution', resolution);
this.mapConfig = {
version: '1.6.0',
buffersize: { 'mvt': 0 },
@@ -2383,37 +2383,37 @@ describe('aggregation', function () {
type: 'cartodb',
options: {
sql: POINTS_SQL_GRID,
sql: query,
aggregation: {
threshold: 1,
resolution: 1
resolution: resolution,
placement: placement !== 'default' ? placement : undefined
}
}
}
]
};
if (placement !== 'default') {
this.mapConfig.layers[0].options.aggregation.placement = placement;
}
this.testClient = new TestClient(this.mapConfig);
this.testClient.getTile(1, 0, 0, { format: 'mvt' }, (err, res, mvt) => {
const c = Math.pow(2, z - 1) - 1; // center tile coordinates
this.testClient.getTile(z, c + 0, c + 0, { format: 'mvt' }, (err, res, mvt) => {
if (err) {
return done(err);
}
const tile00 = JSON.parse(mvt.toGeoJSONSync(0));
this.testClient.getTile(1, 0, 1, { format: 'mvt' }, (err, res, mvt) => {
this.testClient.getTile(z, c + 0, c + 1, { format: 'mvt' }, (err, res, mvt) => {
if (err) {
return done(err);
}
const tile01 = JSON.parse(mvt.toGeoJSONSync(0));
this.testClient.getTile(1, 1, 0, { format: 'mvt' }, (err, res, mvt) => {
this.testClient.getTile(z, c + 1, c + 0, { format: 'mvt' }, (err, res, mvt) => {
if (err) {
return done(err);
}
const tile10 = JSON.parse(mvt.toGeoJSONSync(0));
this.testClient.getTile(1, 1, 1, { format: 'mvt' }, (err, res, mvt) => {
this.testClient.getTile(z, c + 1, c + 1, { format: 'mvt' }, (err, res, mvt) => {
if (err) {
return done(err);
}
@@ -2457,6 +2457,11 @@ describe('aggregation', function () {
it(`for ${placement} no partially aggregated cells`, function (done) {
// Use level 1 with resolution 2 tiles and buffersize 1 (half the cell size)
// Only the cells completely inside the buffer are aggregated
const z = 1;
const resolution = 2;
// space the test points by half the resolution:
const query = POINTS_SQL_GRID.replace('$Z', z).replace('$resolution', resolution/2);
this.mapConfig = {
version: '1.6.0',
buffersize: { 'mvt': 1 },
@@ -2465,37 +2470,37 @@ describe('aggregation', function () {
type: 'cartodb',
options: {
sql: POINTS_SQL_GRID,
sql: query,
aggregation: {
threshold: 1,
resolution: 2
resolution: resolution,
placement: placement !== 'default' ? placement : undefined
}
}
}
]
};
if (placement !== 'default') {
this.mapConfig.layers[0].options.aggregation.placement = placement;
}
this.testClient = new TestClient(this.mapConfig);
this.testClient.getTile(1, 0, 0, { format: 'mvt' }, (err, res, mvt) => {
const c = Math.pow(2, z - 1) - 1; // center tile coordinates
this.testClient.getTile(z, c, c, { format: 'mvt' }, (err, res, mvt) => {
if (err) {
return done(err);
}
const tile00 = JSON.parse(mvt.toGeoJSONSync(0));
this.testClient.getTile(1, 0, 1, { format: 'mvt' }, (err, res, mvt) => {
this.testClient.getTile(z, c, c + 1, { format: 'mvt' }, (err, res, mvt) => {
if (err) {
return done(err);
}
const tile01 = JSON.parse(mvt.toGeoJSONSync(0));
this.testClient.getTile(1, 1, 0, { format: 'mvt' }, (err, res, mvt) => {
this.testClient.getTile(z, c + 1, c, { format: 'mvt' }, (err, res, mvt) => {
if (err) {
return done(err);
}
const tile10 = JSON.parse(mvt.toGeoJSONSync(0));
this.testClient.getTile(1, 1, 1, { format: 'mvt' }, (err, res, mvt) => {
this.testClient.getTile(z, c + 1, c + 1, { format: 'mvt' }, (err, res, mvt) => {
if (err) {
return done(err);
}
@@ -2531,6 +2536,117 @@ describe('aggregation', function () {
});
});
it(`for ${placement} includes complete cells in buffer`, function (done) {
if (!usePostGIS && placement !== 'point-grid') {
// Mapnik seem to filter query results by its (inaccurate) bbox,
// which makes some aggregated clusters get lost here.
// The point-grid placement is resilient to this problem because the result
// coordinates are moved to cluster cell centers, so they're well within
// bbox limits.
this.testClient = new TestClient({});
return done();
}
// use buffersize coincident with resolution, the buffer should include neighbour cells
const z = 2;
const resolution = 1;
const query = POINTS_SQL_GRID.replace('$Z', z).replace('$resolution', resolution);
this.mapConfig = {
version: '1.6.0',
buffersize: { 'mvt': 1 },
layers: [
{
type: 'cartodb',
options: {
sql: query,
aggregation: {
threshold: 1,
resolution: resolution,
placement: placement !== 'default' ? placement : undefined
}
}
}
]
};
this.testClient = new TestClient(this.mapConfig);
const c = Math.pow(2, z - 1) - 1; // center tile coordinates
this.testClient.getTile(z, c, c, { format: 'mvt' }, (err, res, mvt) => {
if (err) {
return done(err);
}
const tile00 = JSON.parse(mvt.toGeoJSONSync(0));
this.testClient.getTile(z, c, c + 1, { format: 'mvt' }, (err, res, mvt) => {
if (err) {
return done(err);
}
const tile01 = JSON.parse(mvt.toGeoJSONSync(0));
this.testClient.getTile(z, c + 1, c, { format: 'mvt' }, (err, res, mvt) => {
if (err) {
return done(err);
}
const tile10 = JSON.parse(mvt.toGeoJSONSync(0));
this.testClient.getTile(z, c + 1, c + 1, { format: 'mvt' }, (err, res, mvt) => {
if (err) {
return done(err);
}
const tile11 = JSON.parse(mvt.toGeoJSONSync(0));
const tile00Expected = [
{ _cdb_feature_count: 2, cartodb_id: 1 },
{ _cdb_feature_count: 2, cartodb_id: 2 },
{ _cdb_feature_count: 2, cartodb_id: 4 },
{ _cdb_feature_count: 2, cartodb_id: 5 },
{ _cdb_feature_count: 1, cartodb_id: 7 },
{ _cdb_feature_count: 1, cartodb_id: 8 }
];
const tile10Expected = [
{ _cdb_feature_count: 2, cartodb_id: 1 },
{ _cdb_feature_count: 2, cartodb_id: 2 },
{ _cdb_feature_count: 1, cartodb_id: 3 },
{ _cdb_feature_count: 2, cartodb_id: 4 },
{ _cdb_feature_count: 2, cartodb_id: 5 },
{ _cdb_feature_count: 1, cartodb_id: 6 },
{ _cdb_feature_count: 1, cartodb_id: 7 },
{ _cdb_feature_count: 1, cartodb_id: 8 },
{ _cdb_feature_count: 1, cartodb_id: 9 }
];
const tile01Expected = [
{ _cdb_feature_count: 2, cartodb_id: 1 },
{ _cdb_feature_count: 2, cartodb_id: 2 },
{ _cdb_feature_count: 2, cartodb_id: 4 },
{ _cdb_feature_count: 2, cartodb_id: 5 }
];
const tile11Expected = [
{ _cdb_feature_count: 2, cartodb_id: 1 },
{ _cdb_feature_count: 2, cartodb_id: 2 },
{ _cdb_feature_count: 1, cartodb_id: 3 },
{ _cdb_feature_count: 2, cartodb_id: 4 },
{ _cdb_feature_count: 2, cartodb_id: 5 },
{ _cdb_feature_count: 1, cartodb_id: 6 }
];
const tile00Actual = tile00.features.map(f => f.properties);
const tile10Actual = tile10.features.map(f => f.properties);
const tile01Actual = tile01.features.map(f => f.properties);
const tile11Actual = tile11.features.map(f => f.properties);
const orderById = (a, b) => a.cartodb_id - b.cartodb_id;
assert.deepEqual(tile00Actual.sort(orderById), tile00Expected);
assert.deepEqual(tile10Actual.sort(orderById), tile10Expected);
assert.deepEqual(tile01Actual.sort(orderById), tile01Expected);
assert.deepEqual(tile11Actual.sort(orderById), tile11Expected);
done();
});
});
});
});
});
it(`for ${placement} points aggregated into corner cluster`, function (done) {
// this test will fail due to !bbox! lack of accuracy if strict cell filtering is in place
this.mapConfig = {
@@ -2544,15 +2660,13 @@ describe('aggregation', function () {
sql: POINTS_SQL_CELL,
aggregation: {
threshold: 1,
resolution: 1
resolution: 1,
placement: placement !== 'default' ? placement : undefined
}
}
}
]
};
if (placement !== 'default') {
this.mapConfig.layers[0].options.aggregation.placement = placement;
}
this.testClient = new TestClient(this.mapConfig);
@@ -2584,15 +2698,13 @@ describe('aggregation', function () {
sql: POINTS_SQL_CELL_INNER,
aggregation: {
threshold: 1,
resolution: 1
resolution: 1,
placement: placement !== 'default' ? placement : undefined
}
}
}
]
};
if (placement !== 'default') {
this.mapConfig.layers[0].options.aggregation.placement = placement;
}
this.testClient = new TestClient(this.mapConfig);
@@ -2627,15 +2739,13 @@ describe('aggregation', function () {
sql: POINTS_SQL_PAIRS,
aggregation: {
threshold: 1,
resolution: 1
resolution: 1,
placement: placement !== 'default' ? placement : undefined
}
}
}
]
};
if (placement !== 'default') {
this.mapConfig.layers[0].options.aggregation.placement = placement;
}
this.testClient = new TestClient(this.mapConfig);
this.testClient.getLayergroup((err, body) => {
@@ -2665,7 +2775,8 @@ describe('aggregation', function () {
sql: POINTS_SQL_PAIRS,
aggregation: {
threshold: 1,
resolution: 1
resolution: 1,
placement: placement !== 'default' ? placement : undefined
},
metadata: {
aggrFeatureCount: 10
@@ -2674,9 +2785,6 @@ describe('aggregation', function () {
}
]
};
if (placement !== 'default') {
this.mapConfig.layers[0].options.aggregation.placement = placement;
}
this.testClient = new TestClient(this.mapConfig);
this.testClient.getLayergroup((err, body) => {
@@ -2706,7 +2814,8 @@ describe('aggregation', function () {
sql: POINTS_SQL_PAIRS,
aggregation: {
threshold: 1,
resolution: 1
resolution: 1,
placement: placement !== 'default' ? placement : undefined
},
metadata: {
aggrFeatureCount: 0
@@ -2715,9 +2824,6 @@ describe('aggregation', function () {
}
]
};
if (placement !== 'default') {
this.mapConfig.layers[0].options.aggregation.placement = placement;
}
this.testClient = new TestClient(this.mapConfig);
this.testClient.getLayergroup((err, body) => {
@@ -2747,7 +2853,8 @@ describe('aggregation', function () {
sql: POINTS_SQL_PAIRS,
aggregation: {
threshold: 1,
resolution: 1
resolution: 1,
placement: placement !== 'default' ? placement : undefined
},
metadata: {
featureCount: true
@@ -2756,9 +2863,6 @@ describe('aggregation', function () {
}
]
};
if (placement !== 'default') {
this.mapConfig.layers[0].options.aggregation.placement = placement;
}
this.testClient = new TestClient(this.mapConfig);
this.testClient.getLayergroup((err, body) => {

View File

@@ -48,11 +48,12 @@ describe('dataviews using tables without overviews', function() {
it("should expose a formula", function(done) {
var testClient = new TestClient(nonOverviewsMapConfig);
testClient.getDataview('country_places_count', { own_filter: 0 }, function(err, formula_result) {
testClient.getDataview('country_places_count', { own_filter: 0 }, function(err, formula_result, headers) {
if (err) {
return done(err);
}
assert.deepEqual(formula_result, { operation: 'count', result: 7313, nulls: 0, type: 'formula' });
assert(getUsesOverviewsFromHeaders(headers) === false); //Overviews logging
testClient.drain(done);
});
@@ -257,7 +258,7 @@ describe('dataviews using tables with overviews', function() {
it("should expose a sum formula", function(done) {
var testClient = new TestClient(overviewsMapConfig);
testClient.getDataview('test_sum', { own_filter: 0 }, function(err, formula_result) {
testClient.getDataview('test_sum', { own_filter: 0 }, function(err, formula_result, headers) {
if (err) {
return done(err);
}
@@ -269,6 +270,8 @@ describe('dataviews using tables with overviews', function() {
"nulls":0,
"type":"formula"
});
assert.ok(getUsesOverviewsFromHeaders(headers)); //Overviews logging
assert(getDataviewTypeFromHeaders(headers) === 'formula'); //Overviews logging
testClient.drain(done);
});
@@ -276,7 +279,7 @@ describe('dataviews using tables with overviews', function() {
it("should expose an avg formula", function(done) {
var testClient = new TestClient(overviewsMapConfig);
testClient.getDataview('test_avg', { own_filter: 0 }, function(err, formula_result) {
testClient.getDataview('test_avg', { own_filter: 0 }, function(err, formula_result, headers) {
if (err) {
return done(err);
}
@@ -288,6 +291,8 @@ describe('dataviews using tables with overviews', function() {
"infinities": 0,
"nans": 0
});
assert.ok(getUsesOverviewsFromHeaders(headers)); //Overviews logging
assert(getDataviewTypeFromHeaders(headers) === 'formula'); //Overviews logging
testClient.drain(done);
});
@@ -295,7 +300,7 @@ describe('dataviews using tables with overviews', function() {
it("should expose a count formula", function(done) {
var testClient = new TestClient(overviewsMapConfig);
testClient.getDataview('test_count', { own_filter: 0 }, function(err, formula_result) {
testClient.getDataview('test_count', { own_filter: 0 }, function(err, formula_result, headers) {
if (err) {
return done(err);
}
@@ -307,6 +312,8 @@ describe('dataviews using tables with overviews', function() {
"infinities": 0,
"nans": 0
});
assert.ok(getUsesOverviewsFromHeaders(headers)); //Overviews logging
assert(getDataviewTypeFromHeaders(headers) === 'formula'); //Overviews logging
testClient.drain(done);
});
@@ -374,13 +381,16 @@ describe('dataviews using tables with overviews', function() {
it("should expose a histogram", function (done) {
var testClient = new TestClient(overviewsMapConfig);
testClient.getDataview('test_histogram', function (err, histogram) {
testClient.getDataview('test_histogram', function (err, histogram, headers) {
if (err) {
return done(err);
}
assert.ok(histogram);
assert.equal(histogram.type, 'histogram');
assert.ok(Array.isArray(histogram.bins));
assert.ok(getUsesOverviewsFromHeaders(headers)); //Overviews logging
assert(getDataviewTypeFromHeaders(headers) === 'histogram'); //Overviews logging
testClient.drain(done);
});
});
@@ -462,7 +472,7 @@ describe('dataviews using tables with overviews', function() {
it("should expose a filtered sum formula", function (done) {
var testClient = new TestClient(overviewsMapConfig);
testClient.getDataview('test_sum', params, function (err, formula_result) {
testClient.getDataview('test_sum', params, function (err, formula_result, headers) {
if (err) {
return done(err);
}
@@ -474,13 +484,14 @@ describe('dataviews using tables with overviews', function() {
"nans": 0,
"type":"formula"
});
assert.ok(getUsesOverviewsFromHeaders(headers)); //Overviews logging
testClient.drain(done);
});
});
it("should expose a filtered avg formula", function(done) {
var testClient = new TestClient(overviewsMapConfig);
testClient.getDataview('test_avg', params, function(err, formula_result) {
testClient.getDataview('test_avg', params, function(err, formula_result, headers) {
if (err) {
return done(err);
}
@@ -492,6 +503,7 @@ describe('dataviews using tables with overviews', function() {
"nans": 0,
"type":"formula"
});
assert.ok(getUsesOverviewsFromHeaders(headers)); //Overviews logging
testClient.drain(done);
});
@@ -499,7 +511,7 @@ describe('dataviews using tables with overviews', function() {
it("should expose a filtered count formula", function(done) {
var testClient = new TestClient(overviewsMapConfig);
testClient.getDataview('test_count', params, function(err, formula_result) {
testClient.getDataview('test_count', params, function(err, formula_result, headers) {
if (err) {
return done(err);
}
@@ -511,6 +523,7 @@ describe('dataviews using tables with overviews', function() {
"nulls":0,
"type":"formula"
});
assert.ok(getUsesOverviewsFromHeaders(headers)); //Overviews logging
testClient.drain(done);
});
@@ -586,10 +599,11 @@ describe('dataviews using tables with overviews', function() {
it("should expose an aggregation dataview filtering special float values out", function (done) {
var testClient = new TestClient(overviewsMapConfig);
testClient.getDataview('test_categories_special_values', params, function (err, dataview) {
testClient.getDataview('test_categories_special_values', params, function (err, dataview, headers) {
if (err) {
return done(err);
}
assert.deepEqual(dataview, {
aggregation: 'sum',
count: 5,
@@ -602,6 +616,10 @@ describe('dataviews using tables with overviews', function() {
categories: [ { category: 'Hawai', value: 6, agg: false } ],
type: 'aggregation'
});
assert.ok(getUsesOverviewsFromHeaders(headers)); //Overviews logging
assert(getDataviewTypeFromHeaders(headers) === 'aggregation'); //Overviews logging
testClient.drain(done);
});
});
@@ -647,3 +665,11 @@ describe('dataviews using tables with overviews', function() {
});
});
});
function getUsesOverviewsFromHeaders(headers) {
return headers && headers['x-tiler-profiler'] && JSON.parse(headers['x-tiler-profiler']).usesOverviews;
}
function getDataviewTypeFromHeaders(headers) {
return headers && headers['x-tiler-profiler'] && JSON.parse(headers['x-tiler-profiler']).dataviewType;
}

View File

@@ -96,7 +96,7 @@ describe('health checks', function () {
});
});
it('not err if disabled file does not exists', function(done) {
it('not err if disabled file does not exist', function(done) {
global.environment.disabled_file = '/tmp/ftreftrgtrccre';
var server = new CartodbWindshaft(serverOptions);

View File

@@ -61,6 +61,7 @@ return function () {
};
const testClient = new TestClient(template, apikeyToken);
testClient.keysToDelete['map_tpl|localhost'] = 0;
testClient.getNamedTile(templateName, 0, 0, 0, 'mvt', {}, (err, res, tile) => {
if (err) {
@@ -194,356 +195,5 @@ return function () {
});
});
});
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();
});
});
}
};
}

View File

@@ -72,6 +72,7 @@ describe('overviews metadata', function() {
data: JSON.stringify(layergroup)
}, {}, function(res) {
assert.equal(res.statusCode, 200, res.body);
var parsedBody = JSON.parse(res.body);
assert.equal(res.headers['x-layergroup-id'], parsedBody.layergroupid);
expected_token = parsedBody.layergroupid;
@@ -113,6 +114,83 @@ describe('overviews metadata', function() {
}
);
});
describe('Overviews Flags', function () {
it("Overviews used", function (done) {
var layergroup = {
version: '1.0.0',
layers: [overviews_layer, non_overviews_layer]
};
var layergroup_url = '/api/v1/map';
var expected_token;
step(
function do_post() {
var next = this;
assert.response(server, {
url: layergroup_url,
method: 'POST',
headers: { host: 'localhost', 'Content-Type': 'application/json' },
data: JSON.stringify(layergroup)
}, {}, function (res) {
assert.equal(res.statusCode, 200, res.body);
const headers = JSON.parse(res.headers['x-tiler-profiler']);
assert.ok(headers.overviewsAddedToMapconfig);
assert.equal(headers.mapType, 'anonymous');
const parsedBody = JSON.parse(res.body);
expected_token = parsedBody.layergroupid;
next();
});
},
function finish(err) {
keysToDelete['map_cfg|' + LayergroupToken.parse(expected_token).token] = 0;
keysToDelete['user:localhost:mapviews:global'] = 5;
done(err);
}
);
});
it("Overviews NOT used", function (done) {
var layergroup = {
version: '1.0.0',
layers: [non_overviews_layer]
};
var layergroup_url = '/api/v1/map';
var expected_token;
step(
function do_post() {
var next = this;
assert.response(server, {
url: layergroup_url,
method: 'POST',
headers: { host: 'localhost', 'Content-Type': 'application/json' },
data: JSON.stringify(layergroup)
}, {}, function (res) {
assert.equal(res.statusCode, 200, res.body);
const headers = JSON.parse(res.headers['x-tiler-profiler']);
assert.equal(headers.overviewsAddedToMapconfig, false);
assert.equal(headers.mapType, 'anonymous');
const parsedBody = JSON.parse(res.body);
expected_token = parsedBody.layergroupid;
next();
});
},
function finish(err) {
keysToDelete['map_cfg|' + LayergroupToken.parse(expected_token).token] = 0;
keysToDelete['user:localhost:mapviews:global'] = 5;
done(err);
}
);
});
});
});
describe('overviews metadata with filters', function() {

View File

@@ -176,4 +176,129 @@ describe('overviews metadata for named maps', function() {
}
);
});
describe('Overviews Flags', function() {
it("Overviews used", function (done) {
step(
function postTemplate() {
var next = this;
assert.response(server, {
url: '/api/v1/map/named?api_key=1234',
method: 'POST',
headers: { host: 'localhost', 'Content-Type': 'application/json' },
data: JSON.stringify(template)
}, {}, function (res, err) {
next(err, res);
});
},
function instantiateTemplate(err) {
assert.ifError(err);
var next = this;
assert.response(server, {
url: '/api/v1/map/named/' + templateId,
method: 'POST',
headers: {
host: 'localhost',
'Content-Type': 'application/json'
}
}, {},
function (res, err) {
return next(err, res);
});
},
function checkFlags(err, res) {
assert.ifError(err);
var next = this;
var parsedBody = JSON.parse(res.body);
keysToDelete['map_cfg|' + LayergroupToken.parse(parsedBody.layergroupid).token] = 0;
keysToDelete['user:localhost:mapviews:global'] = 5;
const headers = JSON.parse(res.headers['x-tiler-profiler']);
assert.ok(headers.overviewsAddedToMapconfig);
assert.equal(headers.mapType, 'named');
next();
},
function finish(err) {
done(err);
}
);
});
it("Overviews NOT used", function (done) {
const nonOverviewsTemplateId = 'non-overviews-template';
var nonOverviewsTemplate = {
version: '0.0.1',
name: nonOverviewsTemplateId,
auth: { method: 'open' },
layergroup: {
version: '1.0.0',
layers: [non_overviews_layer]
}
};
step(
function postTemplate() {
var next = this;
assert.response(server, {
url: '/api/v1/map/named?api_key=1234',
method: 'POST',
headers: { host: 'localhost', 'Content-Type': 'application/json' },
data: JSON.stringify(nonOverviewsTemplate)
}, {}, function (res, err) {
next(err, res);
});
},
function instantiateTemplate(err) {
assert.ifError(err);
var next = this;
assert.response(server, {
url: '/api/v1/map/named/' + nonOverviewsTemplateId,
method: 'POST',
headers: {
host: 'localhost',
'Content-Type': 'application/json'
}
}, {},
function (res, err) {
return next(err, res);
});
},
function checkFlags(err, res) {
assert.ifError(err);
var next = this;
var parsedBody = JSON.parse(res.body);
keysToDelete['map_cfg|' + LayergroupToken.parse(parsedBody.layergroupid).token] = 0;
keysToDelete['user:localhost:mapviews:global'] = 5;
const headers = JSON.parse(res.headers['x-tiler-profiler']);
assert.equal(headers.overviewsAddedToMapconfig, false);
assert.equal(headers.mapType, 'named');
next();
},
function finish(err) {
done(err);
}
);
});
});
});

View File

@@ -509,16 +509,15 @@ TestClient.prototype.getDataview = function(dataviewName, params, callback) {
if (err) {
return next(err);
}
next(null, JSON.parse(res.body));
next(null, JSON.parse(res.body), res.headers);
}
);
},
function finish(err, dataview) {
function finish(err, dataview, headers = null) {
if (err) {
return callback(err);
}
return callback(null, dataview);
return callback(null, dataview, headers);
}
);
};

View File

@@ -0,0 +1,18 @@
var assert = require('assert');
var dateWrapper = require('../../../../lib/cartodb/utils/date-wrapper');
describe('date-wrapper', function() {
it('should wrap property fields with spaces', function() {
const actual = dateWrapper.wrapDates(
'select * from table',
[{name: 'a'}, {name: 'b c'}]
);
const expected = `
SELECT
"a","b c"
FROM
(select * from table) _cdb_epoch_transformation `;
assert.equal(actual, expected);
});
});

102
yarn.lock
View File

@@ -6,20 +6,19 @@
version "0.2.2"
resolved "https://registry.yarnpkg.com/@carto/fqdn-sync/-/fqdn-sync-0.2.2.tgz#cd7c645ed66690a09249b554d254a0f53963b2dc"
"@carto/mapnik@3.6.2-carto.10":
version "3.6.2-carto.10"
resolved "https://registry.yarnpkg.com/@carto/mapnik/-/mapnik-3.6.2-carto.10.tgz#a97c951dcdac09d0eb35b3ea71e5eeaa206c1af6"
"@carto/mapnik@3.6.2-carto.11":
version "3.6.2-carto.11"
resolved "https://registry.yarnpkg.com/@carto/mapnik/-/mapnik-3.6.2-carto.11.tgz#f2f0bc4d0051080169267c5c729b90c6bc934661"
dependencies:
mapnik-vector-tile cartodb/mapnik-vector-tile#v1.6.1-carto.1
mapnik-vector-tile cartodb/mapnik-vector-tile#v1.6.1-carto.2
nan "2.10.0"
node-pre-gyp "0.10.0"
protozero "1.5.1"
"@carto/tilelive-bridge@cartodb/tilelive-bridge#2.5.1-cdb9":
version "2.5.1-cdb9"
resolved "https://codeload.github.com/cartodb/tilelive-bridge/tar.gz/5129e43223cb55daed31373c7a36c98eb6178fc1"
"@carto/tilelive-bridge@github:cartodb/tilelive-bridge#2.5.1-cdb10":
version "2.5.1-cdb10"
resolved "https://codeload.github.com/cartodb/tilelive-bridge/tar.gz/118ac7e7f6582ac7be0fc0246ea2afd1a7795a43"
dependencies:
"@carto/mapnik" "3.6.2-carto.10"
"@carto/mapnik" "3.6.2-carto.11"
"@mapbox/sphericalmercator" "~1.0.1"
mapnik-pool "~0.1.3"
@@ -27,13 +26,13 @@
version "1.0.5"
resolved "https://registry.yarnpkg.com/@mapbox/sphericalmercator/-/sphericalmercator-1.0.5.tgz#70237b9774095ed1cfdbcea7a8fd1fc82b2691f2"
abaculus@cartodb/abaculus#2.0.3-cdb10:
version "2.0.3-cdb10"
resolved "https://codeload.github.com/cartodb/abaculus/tar.gz/90d537028bb8af8a35e7a40c46493066dd8a76b3"
"abaculus@github:cartodb/abaculus#2.0.3-cdb11":
version "2.0.3-cdb11"
resolved "https://codeload.github.com/cartodb/abaculus/tar.gz/b2d4dce48c50fefc5b06f21c6365d82ccf75358d"
dependencies:
"@carto/mapnik" "3.6.2-carto.10"
"@carto/mapnik" "3.6.2-carto.11"
d3-queue "^2.0.2"
sphericalmercator "1.0.x"
sphericalmercator "1.0.5"
abbrev@1, abbrev@1.0.x:
version "1.0.9"
@@ -265,9 +264,9 @@ camelcase@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd"
camshaft@0.62.1:
version "0.62.1"
resolved "https://registry.yarnpkg.com/camshaft/-/camshaft-0.62.1.tgz#75f8734c4089aaeae3b9067eb94d3c669cebbcaf"
camshaft@0.62.3:
version "0.62.3"
resolved "https://registry.yarnpkg.com/camshaft/-/camshaft-0.62.3.tgz#f76c4dff45d00f7750675d0b573ae12975c9138f"
dependencies:
async "^1.5.2"
bunyan "1.8.1"
@@ -276,7 +275,7 @@ camshaft@0.62.1:
dot "^1.0.3"
request "2.85.0"
canvas@cartodb/node-canvas#1.6.2-cdb2:
"canvas@github:cartodb/node-canvas#1.6.2-cdb2":
version "1.6.2-cdb2"
resolved "https://codeload.github.com/cartodb/node-canvas/tar.gz/8acf04557005c633f9e68524488a2657c04f3766"
dependencies:
@@ -294,17 +293,17 @@ carto@0.16.3:
semver "^5.1.0"
yargs "^4.2.0"
carto@cartodb/carto#0.15.1-cdb3:
version "0.15.1-cdb3"
resolved "https://codeload.github.com/cartodb/carto/tar.gz/945f5efb74fd1af1f5e1f69f409f9567f94fb5a7"
carto@cartodb/carto#master:
version "0.15.1"
resolved "https://codeload.github.com/cartodb/carto/tar.gz/31abb8bee02df605521247b0223d508320f7d4c8"
dependencies:
mapnik-reference "~6.0.2"
optimist "~0.6.0"
underscore "1.8.3"
carto@cartodb/carto#master:
version "0.15.1"
resolved "https://codeload.github.com/cartodb/carto/tar.gz/31abb8bee02df605521247b0223d508320f7d4c8"
"carto@github:cartodb/carto#0.15.1-cdb3":
version "0.15.1-cdb3"
resolved "https://codeload.github.com/cartodb/carto/tar.gz/945f5efb74fd1af1f5e1f69f409f9567f94fb5a7"
dependencies:
mapnik-reference "~6.0.2"
optimist "~0.6.0"
@@ -879,6 +878,10 @@ generic-pool@2.4.3:
version "2.4.3"
resolved "https://registry.yarnpkg.com/generic-pool/-/generic-pool-2.4.3.tgz#780c36f69dfad05a5a045dd37be7adca11a4f6ff"
generic-pool@2.5.4:
version "2.5.4"
resolved "https://registry.yarnpkg.com/generic-pool/-/generic-pool-2.5.4.tgz#38c6188513e14030948ec6e5cf65523d9779299b"
generic-pool@~2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/generic-pool/-/generic-pool-2.1.1.tgz#af04dc2c325cfcb975023fa52bfce9617a7435fd"
@@ -887,7 +890,7 @@ 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:
generic-pool@~2.4.1:
version "2.4.6"
resolved "https://registry.yarnpkg.com/generic-pool/-/generic-pool-2.4.6.tgz#f1b55e572167dba2fe75d5aa91ebb1e9f72642d7"
@@ -1429,9 +1432,9 @@ mapnik-reference@~8.5.3:
dependencies:
semver "^5.1.0"
mapnik-vector-tile@cartodb/mapnik-vector-tile#v1.6.1-carto.1:
version "1.6.1-carto.1"
resolved "https://codeload.github.com/cartodb/mapnik-vector-tile/tar.gz/0111f7117946179d62ec7a6eba2f4e9fb355d05e"
mapnik-vector-tile@cartodb/mapnik-vector-tile#v1.6.1-carto.2:
version "1.6.1-carto.2"
resolved "https://codeload.github.com/cartodb/mapnik-vector-tile/tar.gz/e7ca5471f9e5de81243e6035e70444321fc0a82f"
media-typer@0.3.0:
version "0.3.0"
@@ -1480,14 +1483,14 @@ mime@1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6"
mime@2.3.1:
version "2.3.1"
resolved "https://registry.yarnpkg.com/mime/-/mime-2.3.1.tgz#b1621c54d63b97c47d3cfe7f7215f7d64517c369"
mime@~1.2.11:
version "1.2.11"
resolved "https://registry.yarnpkg.com/mime/-/mime-1.2.11.tgz#58203eed86e3a5ef17aed2b7d9ebd47f0a60dd10"
mime@~1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
mimic-fn@^1.0.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022"
@@ -1990,10 +1993,6 @@ propagate@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/propagate/-/propagate-1.0.0.tgz#00c2daeedda20e87e3782b344adba1cddd6ad709"
protozero@1.5.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/protozero/-/protozero-1.5.1.tgz#5a27df6fb6e1ed743f510812ae76c082f5b16638"
proxy-addr@~2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.3.tgz#355f262505a621646b3130a728eb647e22055341"
@@ -2398,7 +2397,7 @@ speedometer@~0.1.2:
version "0.1.4"
resolved "https://registry.yarnpkg.com/speedometer/-/speedometer-0.1.4.tgz#9876dbd2a169d3115402d48e6ea6329c8816a50d"
sphericalmercator@1.0.5, sphericalmercator@1.0.x, sphericalmercator@~1.0.1, sphericalmercator@~1.0.4:
sphericalmercator@1.0.5, sphericalmercator@~1.0.1:
version "1.0.5"
resolved "https://registry.yarnpkg.com/sphericalmercator/-/sphericalmercator-1.0.5.tgz#ddc5a049e360e000d0fad9fc22c4071882584980"
@@ -2581,15 +2580,13 @@ through@2:
version "2.3.8"
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
tilelive-mapnik@cartodb/tilelive-mapnik#0.6.18-cdb14:
version "0.6.18-cdb14"
resolved "https://codeload.github.com/cartodb/tilelive-mapnik/tar.gz/6d06f728833d3e34d1adcd05567b3f4379f547bb"
"tilelive-mapnik@github:cartodb/tilelive-mapnik#0.6.18-cdb15":
version "0.6.18-cdb15"
resolved "https://codeload.github.com/cartodb/tilelive-mapnik/tar.gz/b6c1404a9e37fc60e545d977081867a86eb42b2b"
dependencies:
"@carto/mapnik" "3.6.2-carto.10"
generic-pool "~2.4.0"
mime "~1.6.0"
sphericalmercator "~1.0.4"
step "~0.0.5"
"@carto/mapnik" "3.6.2-carto.11"
generic-pool "2.5.4"
mime "2.3.1"
tilelive@5.12.3:
version "5.12.3"
@@ -2762,13 +2759,13 @@ window-size@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.2.0.tgz#b4315bb4214a3d7058ebeee892e13fa24d98b075"
windshaft@4.8.3:
version "4.8.3"
resolved "https://registry.yarnpkg.com/windshaft/-/windshaft-4.8.3.tgz#9cc0ae9cea6b4e716858f4c15c138c1e9c158b9e"
windshaft@4.10.0:
version "4.10.0"
resolved "https://registry.yarnpkg.com/windshaft/-/windshaft-4.10.0.tgz#ed9e1117b7d9ab035c93dbb4f11c1b7046a43edf"
dependencies:
"@carto/mapnik" "3.6.2-carto.10"
"@carto/tilelive-bridge" cartodb/tilelive-bridge#2.5.1-cdb9
abaculus cartodb/abaculus#2.0.3-cdb10
"@carto/mapnik" "3.6.2-carto.11"
"@carto/tilelive-bridge" cartodb/tilelive-bridge#2.5.1-cdb10
abaculus cartodb/abaculus#2.0.3-cdb11
canvas cartodb/node-canvas#1.6.2-cdb2
carto cartodb/carto#0.15.1-cdb3
cartodb-psql "0.11.0"
@@ -2780,9 +2777,8 @@ windshaft@4.8.3:
request "2.87.0"
semver "5.5.0"
sphericalmercator "1.0.5"
step "1.0.0"
tilelive "5.12.3"
tilelive-mapnik cartodb/tilelive-mapnik#0.6.18-cdb14
tilelive-mapnik cartodb/tilelive-mapnik#0.6.18-cdb15
torque.js "2.16.2"
underscore "1.6.0"