Compare commits

..

317 Commits
6.3.0 ... 6.5.0

Author SHA1 Message Date
Daniel García Aubert
7347263dfd Release 6.5.0 2018-12-26 15:23:28 +01:00
Rafa de la Torre
2a3b6b830b Merge pull request #1059 from CartoDB/update-cartodb-psql-0.13.1
Update cartodb-psql to 0.13.1 and related reverse dependenciess (WIP)
2018-12-13 17:22:55 +01:00
Rafa de la Torre
11b299e116 Update NEWS.md 2018-12-13 16:28:37 +01:00
Rafa de la Torre
27eef4ce42 Update NEWS.md 2018-12-13 16:26:07 +01:00
Rafa de la Torre
59badc0137 Update yarn.lock deps 2018-12-13 15:25:57 +01:00
Rafa de la Torre
f49698efa1 Update package-lock.json deps 2018-12-13 15:23:11 +01:00
Rafa de la Torre
19d6cae10d Update cartodb-psql and rdeps (WIP) 2018-12-12 18:45:36 +01:00
Daniel G. Aubert
7057f5a5c2 Merge pull request #1056 from CartoDB/nodejs-10
Support Nodejs 10 LTS
2018-11-22 14:48:46 +01:00
Daniel García Aubert
3e336204df Update NEWS 2018-11-21 18:01:53 +01:00
Daniel García Aubert
3f4ecb195c Update NEWS 2018-11-21 17:51:56 +01:00
Daniel García Aubert
58b528d00a Update NEWS 2018-11-21 17:49:42 +01:00
Daniel García Aubert
2d2060088c Update yarn.lock 2018-11-21 17:43:50 +01:00
Daniel García Aubert
d1667fac73 Upgrade turbo-carto@0.21.0 2018-11-21 17:36:30 +01:00
Daniel García Aubert
98c0b1f9bd Upgrade cartodb-redis@2.1.0 2018-11-21 17:14:03 +01:00
Daniel García Aubert
1d2548a3e6 Upgrade cartodb-query-tables@0.4.0 2018-11-21 16:39:12 +01:00
Daniel García Aubert
3690959be4 Update camshaft@0.63.0 2018-11-21 15:42:12 +01:00
Daniel García Aubert
7de5fd1515 Update yarn.lock 2018-11-21 13:29:32 +01:00
Daniel García Aubert
f9d1e39b7b Update windshaft@4.12.0 2018-11-21 13:19:29 +01:00
Daniel García Aubert
16a0d9707b Update redis-mpool@0.7.0 2018-11-21 13:11:30 +01:00
Daniel García Aubert
6962abfd10 Update cartodb-psql@0.13.0 2018-11-21 13:09:18 +01:00
Daniel García Aubert
dc0d4f0011 Merge branch 'master' into nodejs-10 2018-11-20 11:20:20 +01:00
Daniel G. Aubert
6bcb535d3f Merge pull request #1057 from CartoDB/add-cpu-metrics
Add process CPU usage metrics
2018-11-15 17:59:24 +01:00
Daniel García Aubert
4ad9902601 Add process CPU usage metrics 2018-11-15 17:28:18 +01:00
Daniel García Aubert
2a933788fd Update yarn.lock 2018-11-06 13:27:30 +01:00
Daniel García Aubert
3febf3e357 Use 'npm ci' instead of 'npm install' to be able to install exactly what is in a lockfile 2018-11-06 12:58:51 +01:00
Daniel García Aubert
a8ca80c23c List dependency tree after install in docker tests 2018-11-06 12:35:10 +01:00
Daniel García Aubert
f3b1bb742a Ensure all pg connections are being refreshed 2018-11-06 12:17:39 +01:00
Daniel García Aubert
4d1ed0be27 Update package-lock.json 2018-11-05 19:47:21 +01:00
Daniel García Aubert
af4b9f57f5 Skip just torque database timeout limit 2018-11-05 19:10:08 +01:00
Daniel García Aubert
6e7bd2585f Skip database timeout test 2018-11-05 18:53:00 +01:00
Daniel García Aubert
40ccdfd9b3 Drain pool connection before 2018-11-05 18:37:20 +01:00
Daniel García Aubert
659b0ba889 Use pdql.end with callback 2018-11-05 18:14:26 +01:00
Daniel García Aubert
71b8699f47 Shut down the pool after setting the database timeout 2018-11-05 17:16:22 +01:00
Daniel García Aubert
24c5bbb182 Workaround to drain pg pool effectively 2018-11-05 16:50:52 +01:00
Daniel García Aubert
2eea20b161 Updated cartodb-psql 2018-11-05 16:03:43 +01:00
Daniel García Aubert
f2180576de Remove test filter 2018-11-02 13:57:56 +01:00
Daniel García Aubert
d9039569bd Output yarn version 2018-11-02 13:57:30 +01:00
Daniel García Aubert
4a82d18cc6 Fix docker command 2018-11-02 13:56:43 +01:00
Daniel García Aubert
f8fa78bb8b Output npm version 2018-11-02 13:30:24 +01:00
Daniel García Aubert
babfa9aae3 Revert "Skip test temporally"
This reverts commit ebf2f54cd5.
2018-11-02 13:30:04 +01:00
Daniel García Aubert
ebf2f54cd5 Skip test temporally 2018-11-02 12:39:34 +01:00
Daniel García Aubert
20c1e8ca05 Revert "Revert "Add cache-buster to hit database always""
This reverts commit 60724897cc.
2018-11-02 12:24:45 +01:00
Daniel García Aubert
2a35d51d45 Revert "Set DEBUG env variable to run test"
This reverts commit 7bd188dafb.
2018-11-02 12:24:01 +01:00
Daniel García Aubert
ebe2d2ddab Revert "Add log to debug server option"
This reverts commit b4e57438ed.
2018-11-02 12:23:48 +01:00
Daniel García Aubert
1ee30e9b53 Revert "Move log to debug server option"
This reverts commit 938e3b2b07.
2018-11-02 12:23:36 +01:00
Daniel García Aubert
7527003711 Revert "Move log to debug server option"
This reverts commit 6c3d8dbe64.
2018-11-02 12:23:05 +01:00
Daniel García Aubert
acd0bbc94f Revert "Add log"
This reverts commit 6cc746dc83.
2018-11-02 12:22:41 +01:00
Daniel García Aubert
00e3f331b4 Revert "Do not run test against node 6"
This reverts commit 7a6fbecac4.
2018-11-02 12:20:49 +01:00
Daniel García Aubert
7a6fbecac4 Do not run test against node 6 2018-11-02 12:17:28 +01:00
Daniel García Aubert
6cc746dc83 Add log 2018-11-02 12:01:59 +01:00
Daniel García Aubert
6c3d8dbe64 Move log to debug server option 2018-11-02 11:23:44 +01:00
Daniel García Aubert
938e3b2b07 Move log to debug server option 2018-11-02 11:12:32 +01:00
Daniel García Aubert
b4e57438ed Add log to debug server option 2018-11-02 10:58:17 +01:00
Daniel García Aubert
7bd188dafb Set DEBUG env variable to run test 2018-11-02 10:37:39 +01:00
Daniel García Aubert
60724897cc Revert "Add cache-buster to hit database always"
This reverts commit e03defc30f.
2018-11-02 10:29:19 +01:00
Daniel García Aubert
e03defc30f Add cache-buster to hit database always 2018-11-02 10:21:53 +01:00
Daniel García Aubert
3bb1f893af Test only 2018-11-01 15:05:38 +01:00
Daniel García Aubert
37a61f527c Do not set language settings for travis 2018-10-31 18:57:25 +01:00
Daniel García Aubert
5e3d546fb6 Add package-lock.json and link to development dependencies 2018-10-31 18:52:40 +01:00
Daniel García Aubert
b7b5f031f3 Update docker test scripts to support Node.js 6 and 10 buildings 2018-10-31 17:20:25 +01:00
Daniel García Aubert
e57e548c31 Run docker test against Node.js 10 2018-10-31 14:47:32 +01:00
Daniel G. Aubert
420c39337c Merge pull request #1052 from CartoDB/use-strict
Use strict mode
2018-10-26 11:21:37 +02:00
Javier Goizueta
214c796a4c Merge pull request #1054 from CartoDB/fix-time-dimension-iso-hours
Fix iso format for hours in time dimensions
2018-10-26 11:01:00 +02:00
Daniel García Aubert
8918d6bec0 Link dependencies to the released versions 2018-10-26 10:19:59 +02:00
Daniel García Aubert
ca7acb8339 Merge branch 'master' into use-strict 2018-10-25 11:11:30 +02:00
Daniel García Aubert
5083ccb605 Link development branches of related dependencies 2018-10-25 10:59:27 +02:00
Javier Goizueta
6908aa532c Fix iso format for hours in time dimensions
The HH specifies hour of day 01-12, we need HH24 for 00-23
2018-10-24 21:15:10 +02:00
Daniel García Aubert
a7daa077ac Update NEWS 2018-10-24 15:53:28 +02:00
Rafa de la Torre
9f0d4905b1 Merge pull request #1053 from CartoDB/fix-tests-undefined-server
Fix for non-deterministic test (undefined server)
2018-10-24 12:11:36 +02:00
Daniel García Aubert
89d10210be Fix undefined context error in test, it raised after forcing strict mode 2018-10-24 11:58:20 +02:00
Rafa de la Torre
545d387bb4 Fix for non-deterministic test (undefined server)
When running tests I got this error:

```
  1) multilayer error cases bogus sql raises 400 status code:
     TypeError: Cannot read property 'listen' of undefined
      at Function.assert.response (test/support/assert.js:93:26)
      at Function.requestLayergroup (test/acceptance/ported/support/test_client.js:79:20)
      at next (node_modules/step/lib/step.js:51:23)
      at Step (node_modules/step/lib/step.js:122:3)
      at Object.createLayergroup (test/acceptance/ported/support/test_client.js:75:5)
      at Context.<anonymous> (test/acceptance/ported/multilayer_error_cases.js:304:20)
```

The problem is that `server` is declared but its initialization
may depend on the order of execution of suites, which is basically
that of the filesystem/checkouts.

That is fixed by returning `getServer()`, which seems to be the
original intent: return a singleton of `CartodbServer` properly
initialized in case it is not overriden through options.
2018-10-23 18:55:31 +02:00
Daniel García Aubert
e2d27db828 Ise strict mode for moduler under test folder 2018-10-23 18:39:02 +02:00
Raul Ochoa
33bcac189f Merge pull request #1050 from CartoDB/prevent-layert-stats-undefined-access
Prevent "Cannot read property 'geom_type' of undefined" on layer stats
2018-10-23 18:37:00 +02:00
Daniel García Aubert
361e99006b Ignore line 2018-10-23 18:20:11 +02:00
Daniel García Aubert
7162ab1631 Add app.js to be linted 2018-10-23 18:19:55 +02:00
Daniel García Aubert
9374e0fe18 Add strict mode to main file (app.js) 2018-10-23 18:09:58 +02:00
Raul Ochoa
b13ae62d0f Do not assert the estimated count as it seems to change over pg versions 2018-10-23 16:02:37 +00:00
Raul Ochoa
49de289a9c Merge pull request #1051 from CartoDB/output-versions
Output PostgreSQL and PostGIS versions
2018-10-23 17:58:30 +02:00
Daniel García Aubert
b6dcf72268 Missing semicolon 2018-10-23 17:55:35 +02:00
Daniel García Aubert
76cfd185de Please jshint 2018-10-23 17:54:11 +02:00
Daniel García Aubert
79820a0f05 Add function as method of to be stric mode compilant 2018-10-23 17:52:19 +02:00
Daniel García Aubert
a0126f6a15 Use stric mode for resource-locator module 2018-10-23 17:48:53 +02:00
Raul Ochoa
abd378e5f6 Run tests based on PostGIS version 2018-10-23 15:47:48 +00:00
Daniel García Aubert
e7e3d612a1 Use strict mode in modules under lib folder (except lib/cartodb/models/resource-locator.js) 2018-10-23 17:45:42 +02:00
Raul Ochoa
208dbfd951 Output PostgreSQL and PostGIS versions 2018-10-23 14:34:17 +00:00
Raul Ochoa
26e4a05276 Going green: prevent TypeError for empty tables/results
This is the intial step to fix https://github.com/CartoDB/carto-vl/issues/1049.
2018-10-23 13:50:49 +00:00
Raul Ochoa
3e261fb353 Going red: fails with 400 due to "Cannot read property 'geom_type' of undefined" 2018-10-23 15:49:34 +02:00
Daniel G. Aubert
4775c73aee Merge pull request #1049 from CartoDB/upgrade-windshaft-4.11.4
Upgrade windshaft to version 4.11.4
2018-10-23 13:03:17 +02:00
Daniel García Aubert
1ece97d0a1 Update NEWS 2018-10-23 12:36:03 +02:00
Daniel García Aubert
87ef8d1977 Upgrade windshaft to version 4.11.4 2018-10-23 12:25:36 +02:00
Daniel G. Aubert
2ebb1728ee Merge pull request #1048 from CartoDB/fix-uncaught-error
Prevent from uncaught exception
2018-10-23 11:54:55 +02:00
Daniel García Aubert
621b11ebd6 Update NEWS 2018-10-23 10:05:53 +02:00
Daniel García Aubert
12d58f3af2 Prevent from uncaught exception: Range filter Error from camshaft when getting analysis query 2018-10-22 15:26:59 +02:00
Daniel G. Aubert
211e815d9c Merge pull request #1047 from CartoDB/upgrade-windshaft-4.11.3
Upgrade windshaft 4.11.3
2018-10-19 13:26:54 +02:00
Daniel García Aubert
465fd2ec0a Merge branch 'master' into upgrade-windshaft-4.11.3 2018-10-19 13:08:34 +02:00
Daniel García Aubert
d0c405ae46 Upgrade windshaft to version 4.11.3 2018-10-19 13:04:03 +02:00
Daniel G. Aubert
953d831d5f Merge pull request #1046 from CartoDB/fix-create-key
Do not use 'Object.assign' as '_.defautls' equivalent
2018-10-19 12:49:17 +02:00
Daniel García Aubert
5573db2bc1 Do not use Object.assign as _.defautls equivalent 2018-10-19 11:57:54 +02:00
Rafa de la Torre
195b23248b Merge pull request #1041 from CartoDB/config-postgis-for-mvts
Sample configs: use PostGIS to generate MVT's
2018-10-17 18:37:27 +02:00
Rafa de la Torre
83897293c6 Fix test by giving redis enough time to delete 2018-10-17 17:35:36 +02:00
Rafa de la Torre
f26ddef244 Make rate limit tests work in dual mode 2018-10-17 17:35:36 +02:00
Rafa de la Torre
d25e8e9798 Make the test work in dual mode (mapnik/pgis) 2018-10-17 17:35:36 +02:00
Rafa de la Torre
bfbd9a8f22 Fix another suite (compat mapnik/pgis) 2018-10-17 17:35:36 +02:00
Rafa de la Torre
bd17f9f5e1 A better implementation of mvt suites 2018-10-17 17:35:36 +02:00
Rafa de la Torre
8491b86c17 Extract test generation function 2018-10-17 17:35:36 +02:00
Rafa de la Torre
376a3743c1 Fix buffer size per format tests 2018-10-17 17:35:36 +02:00
Rafa de la Torre
a42af5e0d5 Do not run test if ST_AsMvt not avail. 2018-10-17 17:35:36 +02:00
Rafa de la Torre
e157649571 Use of postgis renderer based on availabilty 2018-10-17 17:35:36 +02:00
Rafa de la Torre
e50d1a10d0 Skip tests if they cannot be run
If configured with `mvt.usePostGIS` but with no postgis version
supporting it, they should be skipped.
2018-10-17 17:35:36 +02:00
Rafa de la Torre
d474d49ce8 Do not use point in world's border 2018-10-17 17:35:36 +02:00
Rafa de la Torre
4dba4ef641 Tweak the scale denominator for pg-mvt renderer
The scale denominator is calculated with float values and more
precision, resulting in different (but more accurate) values
2018-10-17 17:35:36 +02:00
Rafa de la Torre
be08fa3bfa Tweak id's to test against pg-mvt renderer
Actually, the ID's are not generated by ST_AsMVT. They appear as an
artifact of testing, when using toGeoJSONSync (implemented in mapnik).
2018-10-17 17:35:36 +02:00
Simon Martín
945b151712 using docker tag postgis-2.4.4.5 in travis 2018-10-17 17:35:36 +02:00
Simon Martín
2af6486f73 new docker tags 2018-10-17 17:35:36 +02:00
Rafa de la Torre
9cffc8781a Sample configs: use PostGIS to generate MVT's 2018-10-17 17:35:36 +02:00
Daniel García Aubert
b75c1f7f08 Update NEWS 2018-10-17 15:42:13 +02:00
Rafa de la Torre
c5d22bf9e3 Update package version 2018-10-17 15:29:07 +02:00
Rafa de la Torre
1baae5e709 Update dependencies (windshaft@4.11.2) 2018-10-17 15:28:56 +02:00
Rafa de la Torre
da3239cfa1 Merge pull request #1045 from CartoDB/update-windshaft-pg-mvt-trailing-semicolons
Update windshaft with a patch fo pg-mvt renderer
2018-10-17 15:25:27 +02:00
Rafa de la Torre
ba0078c51c Update windshaft with a patch fo pg-mvt renderer
Update windshaft to version 4.11.2, which contains a patch that makes
the pg-mvt renderer accept a trailing semicolon in the input query.
2018-10-17 15:12:33 +02:00
Daniel G. Aubert
47f64401a7 Merge pull request #1044 from CartoDB/upgrade-windshaft-4.11.1
Upgrade windshaft to version 4.11.1
2018-10-16 17:28:22 +02:00
Daniel García Aubert
8bdbe7c9b7 Update NEWS 2018-10-16 17:18:21 +02:00
Daniel García Aubert
0637018cca Upgrade windshaft to version 4.11.1 2018-10-16 17:16:57 +02:00
Daniel G. Aubert
8a7bef673b Merge pull request #1043 from CartoDB/mvt-query-rewrite
Use overviews query rewriter for mvt-renderer
2018-10-15 16:02:39 +02:00
Daniel García Aubert
a0e71ac396 Upgrade windshaft to version 4.11.0 2018-10-15 15:50:48 +02:00
Daniel García Aubert
184a804367 Improve assertions 2018-10-15 15:25:23 +02:00
Daniel García Aubert
c234b4ea91 Organize params 2018-10-15 08:30:51 +02:00
Daniel García Aubert
db13f5e4f3 ES6 tweaks 2018-10-15 08:25:44 +02:00
Daniel García Aubert
f9a8b3c827 Add acceptance test 2018-10-15 08:23:39 +02:00
Daniel García Aubert
17886d0e43 Revert "Use early return pattern"
This reverts commit 297e56f4e1.
2018-10-11 18:17:16 +02:00
Daniel García Aubert
1f112d587f Revert "Rename function"
This reverts commit 12dc1626a7.
2018-10-11 18:17:03 +02:00
Daniel García Aubert
5c56ea6b22 Add test to validate that Postgis and Mapnik renderers are using overviews tables 2018-10-11 17:59:49 +02:00
Daniel García Aubert
3c76dfbbb3 Use devel branch of windshaft 2018-10-11 15:41:58 +02:00
Daniel García Aubert
e158e3e426 Pass overview-query-rewriter to mvt renderer 2018-10-11 15:41:38 +02:00
Daniel García Aubert
12dc1626a7 Rename function 2018-10-10 15:07:47 +02:00
Daniel García Aubert
297e56f4e1 Use early return pattern 2018-10-10 15:06:56 +02:00
Javier Goizueta
09f75441ba Merge pull request #1039 from CartoDB/time-dimensions
Time dimensions
2018-10-09 16:06:06 +02:00
Javier Goizueta
41bd69d050 Remove public docs for the time being
We might want to make changes to the API after initial test usage
2018-10-09 15:17:29 +02:00
Javier Goizueta
73b3402d85 Refactor stats collection 2018-10-09 13:24:08 +02:00
Javier Goizueta
d66a304b00 Merge branch 'master' into time-dimensions
# Conflicts:
#	NEWS.md
2018-10-08 19:35:38 +02:00
Javier Goizueta
ee63b247cd Slight refactor 2018-10-08 19:25:04 +02:00
Javier Goizueta
418e0e2aa3 Documentation corrections 2018-10-08 19:16:50 +02:00
Javier Goizueta
d4bd706fe2 Clarify some tests 2018-10-08 19:16:32 +02:00
Javier Goizueta
a4dfc09c71 Aggregation dimensions documentation 2018-10-07 23:12:41 +02:00
Javier Goizueta
9ed39f149b Time dimension tests 2018-10-07 22:46:02 +02:00
Javier Goizueta
0e85aa56da Fix test 2018-10-07 11:35:28 +02:00
Javier Goizueta
2f59919f84 Dimension metadata test 2018-10-07 00:29:12 +02:00
Javier Goizueta
10baf43ede Fix dimension metadata bug 2018-10-07 00:28:53 +02:00
Javier Goizueta
996d7fc90d Lint fixes 2018-10-06 18:26:43 +02:00
Javier Goizueta
c0febf2fd1 Rename time dimension parameters 2018-10-05 20:08:40 +02:00
Javier Goizueta
f841f65a1e Dimensions metadata 2018-10-04 19:50:14 +02:00
Javier Goizueta
c9786ee3f6 Catch aggregation query errors 2018-10-03 23:13:22 +02:00
Javier Goizueta
99b62edcbd Bug fixes 2018-10-03 23:12:58 +02:00
Javier Goizueta
c588d4139e Refactor time dimensions 2018-10-03 21:02:22 +02:00
Javier Goizueta
aff55351ad Unify parameter names 2018-10-03 19:07:47 +02:00
Javier Goizueta
96ba075698 Unify handling of cyclic time groupings
Remove generic cyclic grouping
2018-10-03 18:57:00 +02:00
Javier Goizueta
a7d5415f64 Remove offsets from time dimension computations 2018-10-03 17:12:01 +02:00
Javier Goizueta
dede22c915 Changes in time dimensions API
Use single `starting` epoch instead of various offsets.
Add ISO text representation.
Adopt ISO conventions for day of week and week of year.
Rename internal parameters for consistency with external API.
2018-10-03 17:05:58 +02:00
Javier Goizueta
fbf3fd9d8c Support old and new dimension definitions 2018-09-25 19:10:56 +02:00
Simon Martín
e70de80cdf Stub next version 2018-09-24 11:35:10 +02:00
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
Javier Goizueta
fbcfc7a582 WIP: time dimensions for aggregation 2018-09-20 21:12:54 +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
269 changed files with 7701 additions and 1732 deletions

View File

@@ -4,8 +4,14 @@ jobs:
services:
- docker
language: generic
before_install: docker pull carto/nodejs6-xenial-pg101
script: npm run docker-test
before_install: docker pull carto/nodejs6-xenial-pg101:postgis-2.4.4.5
script: npm run docker-test -- nodejs6
- sudo: required
services:
- docker
language: generic
before_install: docker pull carto/nodejs10-xenial-pg101:postgis-2.4.4.5
script: npm run docker-test -- nodejs10
- dist: precise
addons:
postgresql: "9.5"
@@ -28,7 +34,7 @@ jobs:
- sudo add-apt-repository -y ppa:cartodb/gis-testing
- sudo apt-get update
# Force instalation of libgeos-3.5.0 (presumably needed because of existing version of postgis)
- sudo apt-get -y install libgeos-3.5.0=3.5.0-1cdb2
@@ -59,9 +65,12 @@ jobs:
- createdb template_postgis
- createuser publicuser
- psql -c "CREATE EXTENSION postgis" template_postgis
- psql -c "select version();" template_postgis
- psql -c "select postgis_version();" template_postgis
# install yarn 0.27.5
- curl -o- -L https://yarnpkg.com/install.sh | bash -s -- --version 0.27.5
- curl -o- -L https://yarnpkg.com/install.sh | bash -s -- --version 0.27.5
- export PATH="$HOME/.yarn/bin:$PATH"
# instal redis 4

48
NEWS.md
View File

@@ -1,5 +1,53 @@
# Changelog
## 6.5.0
Released 2018-12-26
New features
- Suport Node.js 10
- Configure travis to run docker tests against Node.js 6 & 10 versions
- Aggregation time dimensions
- Update sample configurations to use PostGIS to generate MVT's by default (as in production)
- Upgrades Windshaft to [4.12.1](https://github.com/CartoDB/Windshaft/blob/4.12.1/NEWS.md#version-4121)
- `pg-mvt`: Use `query-rewriter` to compose the query to render a MVT tile. If not defined, it will use a Default Query Rewriter.
- `pg-mvt`: Fix bug while building query and there is no columns defined for the layer.
- `pg-mvt`: Accept trailing semicolon in input queries.
- `Renderer Cache Entry`: Do not throw errors for integrity checks.
- Fix bug when releasing the renderer cache entry in some scenarios.
- Upgrade grainstore to [1.10.0](https://github.com/CartoDB/grainstore/releases/tag/1.10.0)
- Upgrade cartodb-redis to [2.1.0](https://github.com/CartoDB/node-cartodb-redis/releases/tag/2.1.0)
- Upgrade cartodb-query-tables to [0.4.0](https://github.com/CartoDB/node-cartodb-query-tables/releases/tag/0.4.0)
- Upgrade cartodb-psql to [0.13.1](https://github.com/CartoDB/node-cartodb-psql/releases/tag/0.13.1)
- Upgrade turbo-carto to [0.21.0](https://github.com/CartoDB/turbo-carto/releases/tag/0.21.0)
- Upgrade camshaft to [0.63.1](https://github.com/CartoDB/camshaft/releases/tag/0.63.1)
- Upgrade redis-mpool to [0.7.0](https://github.com/CartoDB/node-redis-mpool/releases/tag/0.7.0)
Bug Fixes:
- Prevent from uncaught exception: Range filter Error from camshaft when getting analysis query.
- Make all modules to use strict mode semantics.
## 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

36
app.js
View File

@@ -1,3 +1,5 @@
'use strict';
var http = require('http');
var https = require('https');
var path = require('path');
@@ -126,6 +128,40 @@ listener.on('listening', function() {
);
});
function getCPUUsage (oldUsage) {
let usage;
if (oldUsage && oldUsage._start) {
usage = Object.assign({}, process.cpuUsage(oldUsage._start.cpuUsage));
usage.time = Date.now() - oldUsage._start.time;
} else {
usage = Object.assign({}, process.cpuUsage());
usage.time = process.uptime() * 1000; // s to ms
}
usage.percent = (usage.system + usage.user) / (usage.time * 10);
Object.defineProperty(usage, '_start', {
value: {
cpuUsage: process.cpuUsage(),
time: Date.now()
}
});
return usage;
}
let previousCPUUsage = getCPUUsage();
setInterval(function cpuUsageMetrics () {
const CPUUsage = getCPUUsage(previousCPUUsage);
Object.keys(CPUUsage).forEach(property => {
global.statsClient.gauge(`windshaft.cpu.${property}`, CPUUsage[property]);
});
previousCPUUsage = CPUUsage;
}, 5000);
setInterval(function() {
var memoryUsage = process.memoryUsage();
Object.keys(memoryUsage).forEach(function(k) {

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

@@ -130,7 +130,7 @@ var config = {
//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
usePostGIS: true
},
mapnik: {
// The size of the pool of internal mapnik backend

View File

@@ -130,7 +130,7 @@ var config = {
//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
usePostGIS: true
},
mapnik: {
// The size of the pool of internal mapnik backend

View File

@@ -130,7 +130,7 @@ var config = {
//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
usePostGIS: true
},
mapnik: {
// The size of the pool of internal mapnik backend

View File

@@ -130,7 +130,7 @@ var config = {
//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
usePostGIS: true
},
mapnik: {
// The size of the pool of internal mapnik backend

3
docker-bash.sh Executable file
View File

@@ -0,0 +1,3 @@
#!/bin/bash
docker run -it -v `pwd`:/srv carto/${1:-nodejs10-xenial-pg101:postgis-2.4.4.5} bash

23
docker-test.sh Executable file
View File

@@ -0,0 +1,23 @@
#!/bin/bash
usage() {
echo "Usage: $0 [nodejs6|nodejs10]"
exit 1
}
echo "$0 $1"
NODEJS_VERSION=${1-nodejs10}
if [ "$NODEJS_VERSION" = "nodejs10" ];
then
DOCKER='nodejs10-xenial-pg101:postgis-2.4.4.5'
elif [ "$NODEJS_VERSION" = "nodejs6" ];
then
DOCKER='nodejs6-xenial-pg101:postgis-2.4.4.5'
else
usage
fi
docker run -v `pwd`:/srv carto/${DOCKER} bash run_tests_docker.sh ${NODEJS_VERSION} && \
docker ps --filter status=dead --filter status=exited -aq | xargs docker rm -v

View File

@@ -0,0 +1,88 @@
FROM ubuntu:xenial
# Use UTF8 to avoid encoding problems with pgsql
ENV LANG C.UTF-8
ENV NPROCS 1
ENV JOBS 1
ENV CXX g++-4.9
ENV PGUSER postgres
# Add external repos
RUN set -ex \
&& apt-get update \
&& apt-get install -y \
curl \
software-properties-common \
locales \
&& add-apt-repository -y ppa:ubuntu-toolchain-r/test \
&& add-apt-repository -y ppa:cartodb/postgresql-10 \
&& add-apt-repository -y ppa:cartodb/gis \
&& curl -sL https://deb.nodesource.com/setup_10.x | bash \
&& locale-gen en_US.UTF-8 \
&& update-locale LANG=en_US.UTF-8
RUN set -ex \
&& apt-get update \
&& apt-get install -y \
g++-4.9 \
gcc-4.9 \
git \
libcairo2-dev \
libgdal-dev \
libgdal1i \
libgdal20 \
libgeos-dev \
libgif-dev \
libjpeg8-dev \
libjson-c-dev \
libpango1.0-dev \
libpixman-1-dev \
libproj-dev \
libprotobuf-c-dev \
libxml2-dev \
gdal-bin \
make \
nodejs \
protobuf-c-compiler \
pkg-config \
wget \
zip \
postgresql-10 \
postgresql-10-plproxy \
postgis=2.4.4.5+carto-1 \
postgresql-10-postgis-2.4=2.4.4.5+carto-1 \
postgresql-10-postgis-2.4-scripts=2.4.4.5+carto-1 \
postgresql-10-postgis-scripts=2.4.4.5+carto-1 \
postgresql-client-10 \
postgresql-client-common \
postgresql-common \
postgresql-contrib \
postgresql-plpython-10 \
postgresql-server-dev-10 \
&& wget http://download.redis.io/releases/redis-4.0.8.tar.gz \
&& tar xvzf redis-4.0.8.tar.gz \
&& cd redis-4.0.8 \
&& make \
&& make install \
&& cd .. \
&& rm redis-4.0.8.tar.gz \
&& rm -R redis-4.0.8 \
&& apt-get purge -y wget protobuf-c-compiler \
&& apt-get autoremove -y
# Configure PostgreSQL
RUN set -ex \
&& echo "listen_addresses='*'" >> /etc/postgresql/10/main/postgresql.conf \
&& echo "local all all trust" > /etc/postgresql/10/main/pg_hba.conf \
&& echo "host all all 0.0.0.0/0 trust" >> /etc/postgresql/10/main/pg_hba.conf \
&& echo "host all all ::1/128 trust" >> /etc/postgresql/10/main/pg_hba.conf \
&& /etc/init.d/postgresql start \
&& createdb template_postgis \
&& createuser publicuser \
&& psql -c "CREATE EXTENSION postgis" template_postgis \
&& /etc/init.d/postgresql stop
WORKDIR /srv
EXPOSE 5858
CMD /etc/init.d/postgresql start

View File

@@ -0,0 +1,89 @@
FROM ubuntu:xenial
# Use UTF8 to avoid encoding problems with pgsql
ENV LANG C.UTF-8
ENV NPROCS 1
ENV JOBS 1
ENV CXX g++-4.9
ENV PGUSER postgres
# Add external repos
RUN set -ex \
&& apt-get update \
&& apt-get install -y \
curl \
software-properties-common \
locales \
&& add-apt-repository -y ppa:ubuntu-toolchain-r/test \
&& add-apt-repository -y ppa:cartodb/postgresql-10 \
&& add-apt-repository -y ppa:cartodb/gis \
&& curl -sL https://deb.nodesource.com/setup_6.x | bash \
&& locale-gen en_US.UTF-8 \
&& update-locale LANG=en_US.UTF-8
# Install dependencies and PostGIS 2.4 from sources
RUN set -ex \
&& apt-get update \
&& apt-get install -y \
g++-4.9 \
gcc-4.9 \
git \
libcairo2-dev \
libgdal-dev \
libgdal1i \
libgdal20 \
libgeos-dev \
libgif-dev \
libjpeg8-dev \
libjson-c-dev \
libpango1.0-dev \
libpixman-1-dev \
libproj-dev \
libprotobuf-c-dev \
libxml2-dev \
gdal-bin \
make \
nodejs \
protobuf-c-compiler \
pkg-config \
wget \
zip \
postgresql-10 \
postgresql-10-plproxy \
postgresql-10-postgis-2.4 \
postgresql-10-postgis-2.4-scripts \
postgresql-10-postgis-scripts \
postgresql-client-10 \
postgresql-client-common \
postgresql-common \
postgresql-contrib \
postgresql-plpython-10 \
postgresql-server-dev-10 \
postgis \
&& wget http://download.redis.io/releases/redis-4.0.8.tar.gz \
&& tar xvzf redis-4.0.8.tar.gz \
&& cd redis-4.0.8 \
&& make \
&& make install \
&& cd .. \
&& rm redis-4.0.8.tar.gz \
&& rm -R redis-4.0.8 \
&& apt-get purge -y wget protobuf-c-compiler \
&& apt-get autoremove -y
# Configure PostgreSQL
RUN set -ex \
&& echo "listen_addresses='*'" >> /etc/postgresql/10/main/postgresql.conf \
&& echo "local all all trust" > /etc/postgresql/10/main/pg_hba.conf \
&& echo "host all all 0.0.0.0/0 trust" >> /etc/postgresql/10/main/pg_hba.conf \
&& echo "host all all ::1/128 trust" >> /etc/postgresql/10/main/pg_hba.conf \
&& /etc/init.d/postgresql start \
&& createdb template_postgis \
&& createuser publicuser \
&& psql -c "CREATE EXTENSION postgis" template_postgis \
&& /etc/init.d/postgresql stop
WORKDIR /srv
EXPOSE 5858
CMD /etc/init.d/postgresql start

View File

@@ -0,0 +1,88 @@
FROM ubuntu:xenial
# Use UTF8 to avoid encoding problems with pgsql
ENV LANG C.UTF-8
ENV NPROCS 1
ENV JOBS 1
ENV CXX g++-4.9
ENV PGUSER postgres
# Add external repos
RUN set -ex \
&& apt-get update \
&& apt-get install -y \
curl \
software-properties-common \
locales \
&& add-apt-repository -y ppa:ubuntu-toolchain-r/test \
&& add-apt-repository -y ppa:cartodb/postgresql-10 \
&& add-apt-repository -y ppa:cartodb/gis \
&& curl -sL https://deb.nodesource.com/setup_6.x | bash \
&& locale-gen en_US.UTF-8 \
&& update-locale LANG=en_US.UTF-8
RUN set -ex \
&& apt-get update \
&& apt-get install -y \
g++-4.9 \
gcc-4.9 \
git \
libcairo2-dev \
libgdal-dev \
libgdal1i \
libgdal20 \
libgeos-dev \
libgif-dev \
libjpeg8-dev \
libjson-c-dev \
libpango1.0-dev \
libpixman-1-dev \
libproj-dev \
libprotobuf-c-dev \
libxml2-dev \
gdal-bin \
make \
nodejs \
protobuf-c-compiler \
pkg-config \
wget \
zip \
postgresql-10 \
postgresql-10-plproxy \
postgis=2.4.4.5+carto-1 \
postgresql-10-postgis-2.4=2.4.4.5+carto-1 \
postgresql-10-postgis-2.4-scripts=2.4.4.5+carto-1 \
postgresql-10-postgis-scripts=2.4.4.5+carto-1 \
postgresql-client-10 \
postgresql-client-common \
postgresql-common \
postgresql-contrib \
postgresql-plpython-10 \
postgresql-server-dev-10 \
&& wget http://download.redis.io/releases/redis-4.0.8.tar.gz \
&& tar xvzf redis-4.0.8.tar.gz \
&& cd redis-4.0.8 \
&& make \
&& make install \
&& cd .. \
&& rm redis-4.0.8.tar.gz \
&& rm -R redis-4.0.8 \
&& apt-get purge -y wget protobuf-c-compiler \
&& apt-get autoremove -y
# Configure PostgreSQL
RUN set -ex \
&& echo "listen_addresses='*'" >> /etc/postgresql/10/main/postgresql.conf \
&& echo "local all all trust" > /etc/postgresql/10/main/pg_hba.conf \
&& echo "host all all 0.0.0.0/0 trust" >> /etc/postgresql/10/main/pg_hba.conf \
&& echo "host all all ::1/128 trust" >> /etc/postgresql/10/main/pg_hba.conf \
&& /etc/init.d/postgresql start \
&& createdb template_postgis \
&& createuser publicuser \
&& psql -c "CREATE EXTENSION postgis" template_postgis \
&& /etc/init.d/postgresql stop
WORKDIR /srv
EXPOSE 5858
CMD /etc/init.d/postgresql start

View File

@@ -11,13 +11,13 @@ https://hub.docker.com/r/carto/
## Update image
- Edit the docker image file with your desired changes
- Build image:
- Build image:
- `docker build -t carto/IMAGE -f docker/DOCKER_FILE docker/`
- Upload to docker hub:
- Login into docker hub:
- Login into docker hub:
- `docker login`
- Create tag:
- Create tag:
- `docker tag carto/IMAGE carto/IMAGE`
- Upload:
- Upload:
- `docker push carto/IMAGE`

View File

@@ -134,6 +134,10 @@ of the original dataset applying three different aggregate functions.
> Note that you can use the original column names as names of the result, but all the result column names must be unique. In particular, the names `cartodb_id`, `the_geom`, `the_geom_webmercator` and `_cdb_feature_count` cannot be used for aggregated columns, as they correspond to columns always present in the result.
#### Limitations:
* The iso text format does not admit `starting` or `count` parameters
* Cyclic units (day of the week, etc.) don't admit `count` or `starting` either.
### `resolution`
Defines the cell-size of the spatial aggregation grid. This is equivalent to the [CartoCSS `-torque-resolution`](https://carto.com/docs/carto-engine/cartocss/properties-for-torque/#-torque-resolution-float) property of Torque maps.

View File

@@ -1,3 +1,5 @@
'use strict';
const { Router: router } = require('express');
const RedisPool = require('redis-mpool');

View File

@@ -1,3 +1,5 @@
'use strict';
const PSQL = require('cartodb-psql');
const cleanUpQueryParams = require('../middlewares/clean-up-query-params');
const credentials = require('../middlewares/credentials');

View File

@@ -1,3 +1,5 @@
'use strict';
const layergroupToken = require('../middlewares/layergroup-token');
const cleanUpQueryParams = require('../middlewares/clean-up-query-params');
const credentials = require('../middlewares/credentials');

View File

@@ -1,3 +1,5 @@
'use strict';
const windshaft = require('windshaft');
const MapConfig = windshaft.model.MapConfig;
const Datasource = windshaft.model.Datasource;
@@ -152,8 +154,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

@@ -1,3 +1,5 @@
'use strict';
const layergroupToken = require('../middlewares/layergroup-token');
const cleanUpQueryParams = require('../middlewares/clean-up-query-params');
const credentials = require('../middlewares/credentials');

View File

@@ -1,3 +1,5 @@
'use strict';
const layergroupToken = require('../middlewares/layergroup-token');
const cleanUpQueryParams = require('../middlewares/clean-up-query-params');
const credentials = require('../middlewares/credentials');

View File

@@ -1,3 +1,5 @@
'use strict';
const { Router: router } = require('express');
const AnalysisLayergroupController = require('./analysis-layergroup-controller');

View File

@@ -1,3 +1,5 @@
'use strict';
const layergroupToken = require('../middlewares/layergroup-token');
const coordinates = require('../middlewares/coordinates');
const cleanUpQueryParams = require('../middlewares/clean-up-query-params');

View File

@@ -1,3 +1,5 @@
'use strict';
const cleanUpQueryParams = require('../middlewares/clean-up-query-params');
const credentials = require('../middlewares/credentials');
const dbConnSetup = require('../middlewares/db-conn-setup');

View File

@@ -1,3 +1,5 @@
'use strict';
const layergroupToken = require('../middlewares/layergroup-token');
const coordinates = require('../middlewares/coordinates');
const cleanUpQueryParams = require('../middlewares/clean-up-query-params');
@@ -147,10 +149,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

@@ -1,3 +1,5 @@
'use strict';
const _ = require('underscore');
module.exports = function augmentLayergroupData () {

View File

@@ -1,3 +1,5 @@
'use strict';
module.exports = function authorize (authBackend) {
return function authorizeMiddleware (req, res, next) {
authBackend.authorize(req, res, (err, authorized) => {

View File

@@ -1,3 +1,5 @@
'use strict';
module.exports = function setCacheChannelHeader () {
return function setCacheChannelHeaderMiddleware (req, res, next) {
if (req.method !== 'GET') {

View File

@@ -1,3 +1,5 @@
'use strict';
const ONE_YEAR_IN_SECONDS = 60 * 60 * 24 * 365;
module.exports = function setCacheControlHeader ({ ttl = ONE_YEAR_IN_SECONDS, revalidate = false } = {}) {

View File

@@ -1,3 +1,5 @@
'use strict';
module.exports = function checkJsonContentType () {
return function checkJsonContentTypeMiddleware(req, res, next) {
if (req.method === 'POST' && !req.is('application/json')) {

View File

@@ -1,3 +1,5 @@
'use strict';
const VALID_IMAGE_FORMATS = ['png', 'jpg'];
module.exports = function checkStaticImageFormat () {

View File

@@ -1,3 +1,5 @@
'use strict';
const _ = require('underscore');
// Whitelist query parameters and attach format

View File

@@ -1,3 +1,5 @@
'use strict';
const positiveIntegerNumberRegExp = /^\d+$/;
const integerNumberRegExp = /^-?\d+$/;
const invalidZoomMessage = function (zoom) {

View File

@@ -1,3 +1,5 @@
'use strict';
module.exports = function cors () {
return function corsMiddleware (req, res, next) {
const headers = [

View File

@@ -1,3 +1,5 @@
'use strict';
const basicAuth = require('basic-auth');
module.exports = function credentials () {

View File

@@ -1,3 +1,5 @@
'use strict';
const _ = require('underscore');
module.exports = function dbConnSetup (pgConnection) {

View File

@@ -1,3 +1,5 @@
'use strict';
const _ = require('underscore');
const debug = require('debug')('windshaft:cartodb:error-middleware');

View File

@@ -1,3 +1,5 @@
'use strict';
module.exports = function incrementMapViewCount (metadataBackend) {
return function incrementMapViewCountMiddleware(req, res, next) {
const { mapConfig, user } = res.locals;

View File

@@ -1,3 +1,5 @@
'use strict';
module.exports = function initProfiler (isTemplateInstantiation) {
const operation = isTemplateInstantiation ? 'instance_template' : 'createmap';

View File

@@ -1,3 +1,5 @@
'use strict';
module.exports = function initializeStatusCode () {
return function initializeStatusCodeMiddleware (req, res, next) {
if (req.method !== 'OPTIONS') {

View File

@@ -1,3 +1,5 @@
'use strict';
module.exports = function setLastModifiedHeader () {
return function setLastModifiedHeaderMiddleware(req, res, next) {
if (req.method !== 'GET') {

View File

@@ -1,3 +1,5 @@
'use strict';
module.exports = function setLastUpdatedTimeToLayergroup () {
return function setLastUpdatedTimeToLayergroupMiddleware (req, res, next) {
const { mapConfigProvider, analysesResults } = res.locals;

View File

@@ -1,3 +1,5 @@
'use strict';
module.exports = function setLayerStats (pgConnection, statsBackend) {
return function setLayerStatsMiddleware(req, res, next) {
const { user, mapConfig } = res.locals;

View File

@@ -1,3 +1,5 @@
'use strict';
module.exports = function setLayergroupIdHeader (templateMaps, useTemplateHash) {
return function setLayergroupIdHeaderMiddleware (req, res, next) {
const { user, template } = res.locals;

View File

@@ -1,3 +1,5 @@
'use strict';
module.exports = function setMetadataToLayergroup (layergroupMetadata, includeQuery) {
return function setMetadataToLayergroupMiddleware (req, res, next) {
const { user, mapConfig, analysesResults = [], context, api_key: userApiKey } = res.locals;

View File

@@ -1,3 +1,5 @@
'use strict';
const LayergroupToken = require('../../models/layergroup-token');
const authErrorMessageTemplate = function (signer, user) {
return `Cannot use map signature of user "${signer}" on db of user "${user}"`;

View File

@@ -1,3 +1,5 @@
'use strict';
module.exports = function logger (options) {
if (!global.log4js || !options.log_format) {
return function dummyLoggerMiddleware (req, res, next) {

View File

@@ -1,3 +1,5 @@
'use strict';
const LZMA = require('lzma').LZMA;
module.exports = function lzma () {

View File

@@ -1,3 +1,5 @@
'use strict';
module.exports = function mapError (options) {
const { addContext = false, label = 'MAPS CONTROLLER' } = options;

View File

@@ -1,3 +1,5 @@
'use strict';
const MapStoreMapConfigProvider = require('../../models/mapconfig/provider/map-store-provider');
module.exports = function createMapStoreMapConfigProvider (

View File

@@ -1,3 +1,5 @@
'use strict';
module.exports = function getNamedMapProvider ({ namedMapProviderCache, label, forcedFormat = null }) {
return function getNamedMapProviderMiddleware (req, res, next) {
const { user, token, cache_buster, api_key } = res.locals;

View File

@@ -1,3 +1,5 @@
'use strict';
module.exports = function noop () {
return function noopMiddleware (req, res, next) {
next();

View File

@@ -1,3 +1,5 @@
'use strict';
module.exports = function sendResponse () {
return function sendResponseMiddleware (req, res) {
req.profiler.done('res');

View File

@@ -1,3 +1,5 @@
'use strict';
const os = require('os');
module.exports = function servedByHostHeader () {

View File

@@ -1,3 +1,5 @@
'use strict';
const Profiler = require('../../stats/profiler_proxy');
const debug = require('debug')('windshaft:cartodb:stats');
const onHeaders = require('on-headers');

View File

@@ -1,3 +1,5 @@
'use strict';
const NamedMapsCacheEntry = require('../../cache/model/named_maps_entry');
const NamedMapMapConfigProvider = require('../../models/mapconfig/provider/named-map-provider');

View File

@@ -1,3 +1,5 @@
'use strict';
module.exports = function syntaxError () {
return function syntaxErrorMiddleware (err, req, res, next) {
if (err.name === 'SyntaxError') {

View File

@@ -1,3 +1,5 @@
'use strict';
const CdbRequest = require('../../models/cdb_request');
module.exports = function user () {

View File

@@ -1,3 +1,5 @@
'use strict';
const fs = require('fs');
const timeoutErrorVectorTile = fs.readFileSync(__dirname + '/../../../../assets/render-timeout-fallback.mvt');

View File

@@ -1,3 +1,5 @@
'use strict';
const { templateName } = require('../../backends/template_maps');
const credentials = require('../middlewares/credentials');
const rateLimit = require('../middlewares/rate-limit');

View File

@@ -1,3 +1,5 @@
'use strict';
const cleanUpQueryParams = require('../middlewares/clean-up-query-params');
const credentials = require('../middlewares/credentials');
const dbConnSetup = require('../middlewares/db-conn-setup');
@@ -165,8 +167,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,3 +1,5 @@
'use strict';
const { Router: router } = require('express');
const NamedMapController = require('./named-template-controller');

View File

@@ -1,3 +1,5 @@
'use strict';
const coordinates = require('../middlewares/coordinates');
const cleanUpQueryParams = require('../middlewares/clean-up-query-params');
const credentials = require('../middlewares/credentials');

View File

@@ -1,3 +1,5 @@
'use strict';
var PSQL = require('cartodb-psql');
function AnalysisStatusBackend() {

View File

@@ -1,3 +1,5 @@
'use strict';
/**
*
* @param {PgConnection} pgConnection

View File

@@ -1,7 +1,7 @@
var assert = require('assert');
'use strict';
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 +21,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 +152,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,6 @@
'use strict';
var _ = require('underscore');
var step = require('step');
var AnalysisFilter = require('../models/filter/analysis');
function FilterStatsBackends(pgQueryRunner) {
@@ -24,36 +25,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

@@ -1,3 +1,5 @@
'use strict';
function EmptyLayerStats(types) {
this._types = types || {};
}

View File

@@ -1,3 +1,5 @@
'use strict';
var LayerStats = require('./layer-stats');
var EmptyLayerStats = require('./empty-layer-stats');
var MapnikLayerStats = require('./mapnik-layer-stats');

View File

@@ -1,3 +1,5 @@
'use strict';
var queue = require('queue-async');
function LayerStats(layerStatsIterator) {

View File

@@ -1,5 +1,8 @@
'use strict';
const queryUtils = require('../../utils/query-utils');
const AggregationMapConfig = require('../../models/aggregation/aggregation-mapconfig');
const aggregationQuery = require('../../models/aggregation/aggregation-query');
function MapnikLayerStats () {
this._types = {
@@ -19,6 +22,9 @@ function columnAggregations(field) {
if (field.type === 'date') { // TODO other types too?
return ['min', 'max'];
}
if (field.type === 'timeDimension') {
return ['min', 'max'];
}
return [];
}
@@ -67,13 +73,13 @@ function _geometryType(ctx) {
const geometryColumn = AggregationMapConfig.getAggregationGeometryColumn();
const sqlQuery = _getSQL(ctx, sql => queryUtils.getQueryGeometryType(sql, geometryColumn));
return queryUtils.queryPromise(ctx.dbConnection, sqlQuery)
.then(res => ({ geometryType: res.rows[0].geom_type }));
.then(res => ({ geometryType: (res.rows[0] || {}).geom_type }));
}
return Promise.resolve();
}
function _columns(ctx) {
if (ctx.metaOptions.columns || ctx.metaOptions.columnStats) {
if (ctx.metaOptions.columns || ctx.metaOptions.columnStats || ctx.metaOptions.dimensions) {
// note: post-aggregation columns are in layer.options.columns when aggregation is present
return queryUtils.queryPromise(ctx.dbConnection, _getSQL(ctx, sql => queryUtils.getQueryLimited(sql, 0)))
.then(res => formatResultFields(ctx.dbConnection, res.fields));
@@ -137,51 +143,89 @@ function _sample(ctx, numRows) {
return Promise.resolve();
}
function _columnStats(ctx, columns) {
function _columnsMetadataRequired(options) {
// We need determine the columns of a query
// if either column stats or dimension stats are required,
// since we'll ultimately use the same query to fetch both
return options.columnStats || options.dimensions;
}
function _columnStats(ctx, columns, dimensions) {
if (!columns) {
return Promise.resolve();
}
if (ctx.metaOptions.columnStats) {
if (_columnsMetadataRequired(ctx.metaOptions)) {
let queries = [];
let aggr = [];
queries.push(new Promise(resolve => resolve(columns))); // add columns as first result
Object.keys(columns).forEach(name => {
aggr = aggr.concat(
columnAggregations(columns[name])
.map(fn => `${fn}(${name}) AS ${name}_${fn}`)
);
if (columns[name].type === 'string') {
const topN = ctx.metaOptions.columnStats.topCategories || 1024;
const includeNulls = ctx.metaOptions.columnStats.hasOwnProperty('includeNulls') ?
ctx.metaOptions.columnStats.includeNulls :
true;
// TODO: ctx.metaOptions.columnStats.maxCategories
// => use PG stats to dismiss columns with more distinct values
queries.push(
queryUtils.queryPromise(
ctx.dbConnection,
_getSQL(ctx, sql => queryUtils.getQueryTopCategories(sql, name, topN, includeNulls))
).then(res => ({ [name]: { categories: res.rows } }))
if (ctx.metaOptions.columnStats) {
queries.push(new Promise(resolve => resolve({ columns }))); // add columns as first result
Object.keys(columns).forEach(name => {
aggr = aggr.concat(
columnAggregations(columns[name])
.map(fn => `${fn}("${name}") AS "${name}_${fn}"`)
);
}
});
if (columns[name].type === 'string') {
const topN = ctx.metaOptions.columnStats.topCategories || 1024;
const includeNulls = ctx.metaOptions.columnStats.hasOwnProperty('includeNulls') ?
ctx.metaOptions.columnStats.includeNulls :
true;
// TODO: ctx.metaOptions.columnStats.maxCategories
// => use PG stats to dismiss columns with more distinct values
queries.push(
queryUtils.queryPromise(
ctx.dbConnection,
_getSQL(ctx, sql => queryUtils.getQueryTopCategories(sql, name, topN, includeNulls))
).then(res => ({ columns: { [name]: { categories: res.rows } } }))
);
}
});
}
const dimensionsStats = {};
let dimensionsInfo = {};
if (ctx.metaOptions.dimensions && dimensions) {
dimensionsInfo = aggregationQuery.infoForOptions({ dimensions });
Object.keys(dimensionsInfo).forEach(dimName => {
const info = dimensionsInfo[dimName];
if (info.type === 'timeDimension') {
dimensionsStats[dimName] = {
params: info.params
};
aggr = aggr.concat(
columnAggregations(info).map(fn => `${fn}(${info.sql}) AS "${dimName}_${fn}"`)
);
}
});
}
queries.push(
queryUtils.queryPromise(
ctx.dbConnection,
_getSQL(ctx, sql => `SELECT ${aggr.join(',')} FROM (${sql}) AS __cdb_query`)
).then(res => {
let stats = {};
let stats = { columns: {}, dimensions: {} };
Object.keys(columns).forEach(name => {
stats[name] = {};
stats.columns[name] = {};
columnAggregations(columns[name]).forEach(fn => {
stats[name][fn] = res.rows[0][`${name}_${fn}`];
stats.columns[name][fn] = res.rows[0][`${name}_${fn}`];
});
});
Object.keys(dimensionsInfo).forEach(name => {
stats.dimensions[name] = stats.dimensions[name] || Object.assign({}, dimensionsStats[name]);
let type = null;
columnAggregations(dimensionsInfo[name]).forEach(fn => {
type = type ||
fieldTypeSafe(ctx.dbConnection, res.fields.find(f => f.name === `${name}_${fn}`));
stats.dimensions[name][fn] = res.rows[0][`${name}_${fn}`];
});
stats.dimensions[name].type = type;
});
return stats;
})
);
return Promise.all(queries).then(results => ({ columns: mergeColumns(results) }));
return Promise.all(queries).then(results => ({
columns: mergeColumns(results.map(r => r.columns)),
dimensions: mergeColumns(results.map( r => r.dimensions))
}));
}
return Promise.resolve({ columns });
}
@@ -211,19 +255,17 @@ function fieldType(cname) {
return tname;
}
function fieldTypeSafe(dbConnection, field) {
const cname = dbConnection.typeName(field.dataTypeID);
return cname ? fieldType(cname) : `unknown(${field.dataTypeID})`;
}
// columns are returned as an object { columnName1: { type1: ...}, ..}
// for consistency with SQL API
function formatResultFields(dbConnection, fields = []) {
let nfields = {};
for (let field of fields) {
const cname = dbConnection.typeName(field.dataTypeID);
let tname;
if ( ! cname ) {
tname = 'unknown(' + field.dataTypeID + ')';
} else {
tname = fieldType(cname);
}
nfields[field.name] = { type: tname };
nfields[field.name] = { type: fieldTypeSafe(dbConnection, field) };
}
return nfields;
}
@@ -237,7 +279,7 @@ function (layer, dbConnection, callback) {
dbConnection,
preQuery,
aggrQuery,
metaOptions: layer.options.metadata || {}
metaOptions: layer.options.metadata || {},
};
// TODO: could save some queries if queryUtils.getAggregationMetadata() has been used and kept somewhere
@@ -248,6 +290,8 @@ function (layer, dbConnection, callback) {
// TODO: add support for sample.exclude option by, in that case, forcing the columns query and
// passing the results to the sample query function.
const dimensions = (layer.options.aggregation || {}).dimensions;
Promise.all([
_estimatedFeatureCount(ctx).then(
({ estimatedFeatureCount }) => _sample(ctx, estimatedFeatureCount)
@@ -256,9 +300,10 @@ function (layer, dbConnection, callback) {
_featureCount(ctx),
_aggrFeatureCount(ctx),
_geometryType(ctx),
_columns(ctx).then(columns => _columnStats(ctx, columns))
_columns(ctx).then(columns => _columnStats(ctx, columns, dimensions))
]).then(results => {
callback(null, mergeResults(results));
results = mergeResults(results);
callback(null, results);
}).catch(error => {
callback(error);
});

View File

@@ -1,3 +1,5 @@
'use strict';
function TorqueLayerStats() {
this._types = {
torque: true

View File

@@ -1,3 +1,5 @@
'use strict';
const queryUtils = require('../utils/query-utils');
function OverviewsMetadataBackend(pgQueryRunner) {

View File

@@ -1,3 +1,5 @@
'use strict';
var PSQL = require('cartodb-psql');
var _ = require('underscore');
const debug = require('debug')('cachechan');

View File

@@ -1,3 +1,5 @@
'use strict';
var PSQL = require('cartodb-psql');
function PgQueryRunner(pgConnection) {

View File

@@ -1,3 +1,5 @@
'use strict';
var layerStats = require('./layer-stats/factory');
function StatsBackend() {

View File

@@ -1,3 +1,5 @@
'use strict';
function TablesExtentBackend(pgQueryRunner) {
this.pgQueryRunner = pgQueryRunner;
}

View File

@@ -1,7 +1,7 @@
var assert = require('assert');
'use strict';
var crypto = require('crypto');
var debug = require('debug')('windshaft:templates');
var step = require('step');
var _ = require('underscore');
var dot = require('dot');
@@ -69,27 +69,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 +176,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 +222,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 +269,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 +300,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 +376,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,4 +1,4 @@
var step = require('step');
'use strict';
/**
*
@@ -41,45 +41,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

@@ -1,3 +1,5 @@
'use strict';
var FastlyPurge = require('fastly-purge');
function FastlyCacheBackend(apiKey, serviceId) {

View File

@@ -1,3 +1,5 @@
'use strict';
var request = require('request');
function VarnishHttpCacheBackend(host, port) {

View File

@@ -1,3 +1,5 @@
'use strict';
var LruCache = require('lru-cache');
function LayergroupAffectedTables() {

View File

@@ -1,3 +1,5 @@
'use strict';
var crypto = require('crypto');
function NamedMaps(owner, name) {

View File

@@ -1,3 +1,5 @@
'use strict';
var _ = require('underscore');
var dot = require('dot');
var NamedMapMapConfigProvider = require('../models/mapconfig/provider/named-map-provider');

View File

@@ -1,3 +1,5 @@
'use strict';
var queue = require('queue-async');
/**

View File

@@ -1,3 +1,5 @@
'use strict';
const MapConfig = require('windshaft').model.MapConfig;
const aggregationQuery = require('./aggregation-query');
const aggregationValidator = require('./aggregation-validator');

View File

@@ -1,5 +1,10 @@
'use strict';
const timeDimension = require('./time-dimension');
const DEFAULT_PLACEMENT = 'point-sample';
/**
* Returns a template function (function that accepts template parameters and returns a string)
* to generate an aggregation query.
@@ -24,6 +29,16 @@ const templateForOptions = (options) => {
return templateFn;
};
function optionsToParams (options) {
return {
sourceQuery: options.query,
res: 256/options.resolution,
columns: options.columns,
dimensions: options.dimensions,
filters: options.filters
};
}
/**
* Generates an aggregation query given the aggregation options:
* - query
@@ -38,16 +53,23 @@ const templateForOptions = (options) => {
* When placement, columns or dimensions are specified, columns are aggregated as requested
* (by default only _cdb_feature_count) and with the_geom_webmercator as defined by placement.
*/
const queryForOptions = (options) => templateForOptions(options)({
sourceQuery: options.query,
res: 256/options.resolution,
columns: options.columns,
dimensions: options.dimensions,
filters: options.filters
});
const queryForOptions = (options) => templateForOptions(options)(optionsToParams(options));
module.exports = queryForOptions;
module.exports.infoForOptions = (options) => {
const params = optionsToParams(options);
const dimensions = {};
dimensionNamesAndExpressions(params).forEach(([dimensionName, info]) => {
dimensions[dimensionName] = {
sql: info.sql,
params: info.effectiveParams,
type: info.type
};
});
return dimensions;
};
const SUPPORTED_AGGREGATE_FUNCTIONS = {
'count': {
sql: (column_name, params) => `count(${params.aggregated_column || '*'})`
@@ -113,22 +135,56 @@ const aggregateColumnDefs = ctx => {
const aggregateDimensions = ctx => ctx.dimensions || {};
const dimensionNames = (ctx, table) => {
let dimensions = aggregateDimensions(ctx);
if (table) {
return sep(Object.keys(dimensions).map(
dimension_name => `${table}.${dimension_name}`
));
const timeDimensionParameters = definition => {
// definition.column should correspond to a wrapped date column
const group = definition.group || {};
return {
time: `to_timestamp("${definition.column}")`,
timezone: group.timezone || 'utc',
units: group.units,
count: group.count || 1,
starting: group.starting,
format: definition.format
};
};
// Adapt old-style dimension definitions for backwards compatibility
const adaptDimensionDefinition = definition => {
if (typeof(definition) === 'string') {
return { column: definition };
}
return sep(Object.keys(dimensions));
return definition;
};
const dimensionExpression = definition => {
if (definition.group) {
// Currently only time dimensions are supported with parameters
return Object.assign({ type: 'timeDimension' }, timeDimension(timeDimensionParameters(definition)));
} else {
return { sql: `"${definition.column}"` };
}
};
const dimensionNamesAndExpressions = (ctx) => {
let dimensions = aggregateDimensions(ctx);
return Object.keys(dimensions).map(dimensionName => {
const dimension = adaptDimensionDefinition(dimensions[dimensionName]);
const expression = dimensionExpression(dimension);
return [dimensionName, expression];
});
};
const dimensionNames = (ctx, table) => {
return sep(dimensionNamesAndExpressions(ctx).map(([dimensionName]) => {
return table ? `${table}."${dimensionName}"` : `"${dimensionName}"`;
}));
};
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 sep(
dimensionNamesAndExpressions(ctx)
.map(([dimensionName, expression]) => `${expression.sql} AS "${dimensionName}"`)
);
};
const aggregateFilters = ctx => ctx.filters || {};
@@ -274,23 +330,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 +436,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

@@ -1,3 +1,5 @@
'use strict';
module.exports = function aggregationValidator (mapconfig) {
return function validateProperty (key, validator) {
for (let index = 0; index < mapconfig.getLayers().length; index++) {

View File

@@ -0,0 +1,267 @@
'use strict';
// timezones can be defined either by an numeric offset in seconds or by
// a valid (case-insensitive) tz/PG name;
// they include abbreviations defined by PG (which have precedence and
// are fixed offsets, not handling DST) or general names that can handle DST.
function timezone(tz) {
if (isFinite(tz)) {
return `INTERVAL '${tz} seconds'`;
}
return `'${tz}'`;
}
// We assume t is a TIMESTAMP WITH TIME ZONE.
// If this was to be used with a t which is a TIMESTAMP or TIME (no time zone)
// it should be converted with `timezone('utc',t)` to a type with time zone.
// Note that by default CARTO uses timestamp with time zone columns for dates
// and VectorMapConfigAdapter converts them to epoch numbers.
// So, for using this with aggregations, relying on dates & times
// converted to UTC UNIX epoch numbers, apply `to_timestamp` to the
// (converted) column.
function timeExpression(t, tz) {
if (tz !== undefined) {
return `timezone(${timezone(tz)}, ${t})`;
}
return t;
}
function epochWithDefaults(epoch) {
/* jshint maxcomplexity:9 */ // goddammit linter, I like this as is!!
const format = /^(\d\d\d\d)(?:\-?(\d\d)(?:\-?(\d\d)(?:[T\s]?(\d\d)(?:(\d\d)(?:\:(\d\d))?)?)?)?)?$/;
const match = (epoch || '').match(format) || [];
const year = match[1] || '0001';
const month = match[2] || '01';
const day = match[3] || '01';
const hour = match[4] || '00';
const minute = match[5] || '00';
const second = match[6] || '00';
return `${year}-${month}-${day}T${hour}:${minute}:${second}`;
}
// Epoch should be an ISO timestamp literal without time zone
// (it is interpreted as in the defined timzezone for the input time)
// It can be partial, e.g. 'YYYY', 'YYYY-MM', 'YYYY-MM-DDTHH', etc.
// Defaults are applied: YYYY=0001, MM=01, DD=01, HH=00, MM=00, S=00
// It returns a timestamp without time zone
function epochExpression(epoch) {
return `TIMESTAMP '${epoch}'`;
}
const YEARSPAN = "(date_part('year', $t)-date_part('year', $epoch))";
// Note that SECONDSPAN is not a UTC epoch, but an epoch in the specified TZ,
// so we can use it to compute any multiple of seconds with it without using date_part or date_trunc
const SECONDSPAN = "(date_part('epoch', $t) - date_part('epoch', $epoch))";
const serialParts = {
second: {
sql: `FLOOR(${SECONDSPAN})`,
zeroBased: true
},
minute: {
sql: `FLOOR(${SECONDSPAN}/60)`,
zeroBased: true
},
hour: {
sql: `FLOOR(${SECONDSPAN}/3600)`,
zeroBased: true
},
day: {
sql: `1 + FLOOR(${SECONDSPAN}/86400)`,
zeroBased: false
},
week: {
sql: `1 + FLOOR(${SECONDSPAN}/(7*86400))`,
zeroBased: false
},
month: {
sql: `1 + date_part('month', $t) - date_part('month', $epoch) + 12*${YEARSPAN}`,
zeroBased: false
},
quarter: {
sql: `1 + date_part('quarter', $t) - date_part('quarter', $epoch) + 4*${YEARSPAN}`,
zeroBased: false
},
semester: {
sql: `1 + FLOOR((date_part('month', $t) - date_part('month', $epoch))/6) + 2*${YEARSPAN}`,
zeroBased: false
},
trimester: {
sql: `1 + FLOOR((date_part('month', $t) - date_part('month', $epoch))/4) + 3*${YEARSPAN}`,
zeroBased: false
},
year: {
// for the default epoch this coincides with date_part('year', $t)
sql: `1 + ${YEARSPAN}`,
zeroBased: false
},
decade: {
// for the default epoch this coincides with date_part('decade', $t)
sql: `FLOOR((${YEARSPAN} + 1)/10)`,
zeroBased: true
},
century: {
// for the default epoch this coincides with date_part('century', $t)
sql: `1 + FLOOR(${YEARSPAN}/100)`,
zeroBased: false
},
millennium: {
// for the default epoch this coincides with date_part('millennium', $t)
sql: `1 + FLOOR(${YEARSPAN}/1000)`,
zeroBased: false
}
};
function serialSqlExpr(params) {
const { sql, zeroBased } = serialParts[params.units];
const column = timeExpression(params.time, params.timezone);
const epoch = epochExpression(params.starting);
const serial = sql.replace(/\$t/g, column).replace(/\$epoch/g, epoch);
let expr = serial;
if (params.count !== 1) {
if (zeroBased) {
expr = `FLOOR((${expr})/(${params.count}::double precision))::int`;
} else {
expr = `CEIL((${expr})/(${params.count}::double precision))::int`;
}
} else {
expr = `(${expr})::int`;
}
return expr;
}
const isoParts = {
second: `to_char($t, 'YYYY-MM-DD"T"HH24:MI:SS')`,
minute: `to_char($t, 'YYYY-MM-DD"T"HH24:MI')`,
hour: `to_char($t, 'YYYY-MM-DD"T"HH24')`,
day: `to_char($t, 'YYYY-MM-DD')`,
month: `to_char($t, 'YYYY-MM')`,
year: `to_char($t, 'YYYY')`,
week: `to_char($t, 'IYYY-"W"IW')`,
quarter: `to_char($t, 'YYYY-"Q"Q')`,
semester: `to_char($t, 'YYYY"S"') || to_char(CEIL(date_part('month', $t)/6), '9')`,
trimester: `to_char($t, 'YYYY"t"') || to_char(CEIL(date_part('month', $t)/4), '9')`,
decade: `to_char(date_part('decade', $t), '"D"999')`,
century: `to_char($t, '"C"CC')`,
millennium: `to_char(date_part('millennium', $t), '"M"999')`
};
function isoSqlExpr(params) {
const column = timeExpression(params.time, params.timezone);
if (params.count > 1) {
// TODO: it would be sensible to return the ISO of the first unit in the period
throw new Error('Multiple time units not supported for ISO format');
}
return isoParts[params.units].replace(/\$t/g, column);
}
const cyclicParts = {
dayOfWeek: `date_part('isodow', $t)`, // 1 = monday to 7 = sunday;
dayOfMonth: `date_part('day', $t)`, // 1 to 31
dayOfYear: `date_part('doy', $t)`, // 1 to 366
hourOfDay: `date_part('hour', $t)`, // 0 to 23
monthOfYear: `date_part('month', $t)`, // 1 to 12
quarterOfYear: `date_part('quarter', $t)`, // 1 to 4
semesterOfYear: `FLOOR((date_part('month', $t)-1)/6.0) + 1`, // 1 to 2
trimesterOfYear: `FLOOR((date_part('month', $t)-1)/4.0) + 1`, // 1 to 3
weekOfYear: `date_part('week', $t)`, // 1 to 53
minuteOfHour: `date_part('minute', $t)` // 0 to 59
};
function cyclicSqlExpr(params) {
const column = timeExpression(params.time, params.timezone);
return cyclicParts[params.units].replace(/\$t/g, column);
}
const ACCEPTED_PARAMETERS = ['time', 'units', 'timezone', 'count', 'starting', 'format'];
const REQUIRED_PARAMETERS = ['time', 'units'];
function validateParameters(params, checker) {
const errors = [];
const presentParams = Object.keys(params);
const invalidParams = presentParams.filter(param => !ACCEPTED_PARAMETERS.includes(param));
if (invalidParams.length) {
errors.push(`Invalid parameters: ${invalidParams.join(', ')}`);
}
const missingParams = REQUIRED_PARAMETERS.filter(param => !presentParams.includes(param));
if (missingParams.length) {
errors.push(`Missing parameters: ${missingParams.join(', ')}`);
}
const params_errors = checker(params);
errors.push(...params_errors.errors);
if (errors.length) {
throw new Error(`Invalid time dimension:\n${errors.join("\n")}`);
}
return params_errors.params;
}
const VALID_CYCLIC_UNITS = Object.keys(cyclicParts);
const VALID_SERIAL_UNITS = Object.keys(serialParts);
const VALID_ISO_UNITS = Object.keys(isoParts);
function cyclicCheckParams(params) {
const errors = [];
if (!VALID_CYCLIC_UNITS.includes(params.units)) {
errors.push(`Invalid units "${params.units}"`);
}
if (params.count && params.count > 1) {
errors.push(`Count ${params.count} not supported for cyclic ${params.units}`);
}
return { errors: errors, params: params };
}
function serialCheckParams(params) {
const errors = [];
if (!VALID_SERIAL_UNITS.includes(params.units)) {
errors.push(`Invalid grouping units "${params.units}"`);
}
return { errors: errors, params: Object.assign({}, params, { starting: epochWithDefaults(params.starting) }) };
}
function isoCheckParams(params) {
const errors = [];
if (!VALID_ISO_UNITS.includes(params.units)) {
errors.push(`Invalid units "${params.units}"`);
}
if (params.starting) {
errors.push("Parameter 'starting' not supported for ISO format");
}
return { errors: errors, params: params };
}
const CLASSIFIERS = {
cyclic: {
sqlExpr: cyclicSqlExpr,
checkParams: cyclicCheckParams
},
iso: {
sqlExpr: isoSqlExpr,
checkParams: isoCheckParams
},
serial: {
sqlExpr: serialSqlExpr,
checkParams: serialCheckParams
}
};
function isCyclic(units) {
return VALID_CYCLIC_UNITS.includes(units);
}
function classifierFor(params) {
let classifier = 'serial';
if (params.units && isCyclic(params.units)) {
classifier = 'cyclic';
} else if (params.format === 'iso') {
classifier = 'iso';
}
return CLASSIFIERS[classifier];
}
function classificationSql(params) {
const classifier = classifierFor(params);
params = validateParameters(params, classifier.checkParams);
return { sql: classifier.sqlExpr(params), effectiveParams: params };
}
module.exports = classificationSql;

View File

@@ -1,3 +1,5 @@
'use strict';
function CdbRequest() {
this.RE_USER_FROM_HOST = new RegExp(global.environment.user_from_host ||
'^([^\\.]+)\\.' // would extract "strk" from "strk.cartodb.com"

View File

@@ -1,3 +1,5 @@
'use strict';
const BaseDataview = require('./base');
const debug = require('debug')('windshaft:dataview:aggregation');

View File

@@ -1,3 +1,5 @@
'use strict';
const FLOAT_OIDS = {
700: true,
701: true,
@@ -21,7 +23,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 +36,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

@@ -1,3 +1,5 @@
'use strict';
const dataviews = require('./');
module.exports = class DataviewFactory {

View File

@@ -1,3 +1,5 @@
'use strict';
const BaseDataview = require('./base');
const debug = require('debug')('windshaft:dataview:formula');
const utils = require('../../utils/query-utils');

View File

@@ -1,3 +1,5 @@
'use strict';
const debug = require('debug')('windshaft:dataview:histogram');
const NumericHistogram = require('./histograms/numeric-histogram');
const DateHistogram = require('./histograms/date-histogram');

View File

@@ -1,3 +1,5 @@
'use strict';
const BaseDataview = require('../base');
const TYPE = 'histogram';

View File

@@ -1,3 +1,5 @@
'use strict';
const BaseHistogram = require('./base-histogram');
const debug = require('debug')('windshaft:dataview:date-histogram');
const utils = require('../../../utils/query-utils');

View File

@@ -1,3 +1,5 @@
'use strict';
const BaseHistogram = require('./base-histogram');
const debug = require('debug')('windshaft:dataview:numeric-histogram');
const utils = require('../../../utils/query-utils');

View File

@@ -1,3 +1,5 @@
'use strict';
module.exports = {
Aggregation: require('./aggregation'),
Formula: require('./formula'),

Some files were not shown because too many files have changed in this diff Show More