Compare commits

...

611 Commits

Author SHA1 Message Date
Daniel García Aubert
35b9448e9a Release 3.9.8 2017-07-21 10:58:37 +02:00
Daniel
9959e009eb Merge pull request #714 from CartoDB/bump-windshaft-3.2.2
Bump windshaft version to 3.2.2
2017-07-21 10:44:35 +02:00
Daniel García Aubert
106b9a64b2 Update NEWS 2017-07-21 10:39:33 +02:00
Daniel García Aubert
42d05f29ee Bump windshaft version to 3.2.2 2017-07-20 19:47:01 +02:00
Daniel García Aubert
fc3a959da1 Stub next version 2017-07-20 15:00:39 +02:00
Daniel García Aubert
20003c49ce Release 3.9.7 2017-07-20 14:53:20 +02:00
Daniel
d1d9401539 Merge pull request #713 from CartoDB/upgrade-turbo-carto-to-0.19.2
Bump turbo-carto version to 0.19.2
2017-07-20 14:50:02 +02:00
Daniel García Aubert
cc8a1df388 Bump turbo-carto version to 0.19.2 2017-07-20 12:11:09 +02:00
Daniel García Aubert
a17916488b Update config to get the right key to enable stats metadate after layergroup creation 2017-07-18 17:40:36 +02:00
Daniel
868930de46 Merge pull request #712 from CartoDB/mvt-no-content
Respond with 204 when mvt is empty
2017-07-18 11:23:12 +02:00
Daniel García Aubert
f306c26da6 Update News 2017-07-18 11:08:39 +02:00
Daniel García Aubert
446e2d0802 Add empty line at the end of file 2017-07-18 11:05:45 +02:00
Daniel García Aubert
0aab434f13 Remove duplicated assertion 2017-07-18 10:52:24 +02:00
Daniel García Aubert
ff13996255 Add test to check that mvt returns 200 when tile has data 2017-07-18 10:44:27 +02:00
Daniel García Aubert
eccc3597aa Respond with 204 when vector tile is empty 2017-07-17 19:44:18 +02:00
Daniel García Aubert
a35b1e3e86 Stubs next version 2017-07-11 09:59:30 +02:00
Daniel García Aubert
5b8ecd3df0 Release 3.9.6 2017-07-11 09:53:12 +02:00
Daniel
5ea5c1b2dc Merge pull request #708 from CartoDB/11245-aggregation-search
widgets: support for aggregation in search results
2017-07-11 09:44:33 +02:00
Daniel García Aubert
e36266a80f Added test to check all aggregation operations work as expected when searching dataviews 2017-07-10 12:29:25 +02:00
Carlos Matallín
b1c9dd537e Update Makefile 2017-07-07 17:47:28 +02:00
Carlos Matallín
dd934a3913 linter 2017-07-07 17:44:32 +02:00
Carlos Matallín
7fa154c062 widgets: add aggregation for search results, specs 2017-07-07 17:38:15 +02:00
Carlos Matallín
f7a763b637 widgets: add aggregation for search results 2017-07-07 17:09:17 +02:00
Daniel García Aubert
f507f7a74b Stub next version 2017-06-27 17:18:08 +02:00
Daniel García Aubert
2f1cacdfc7 Release 3.9.5 2017-06-27 17:07:52 +02:00
Daniel García Aubert
3a442bea44 Update NEWS 2017-06-27 17:06:24 +02:00
Daniel
49f5b0b480 Merge pull request #700 from CartoDB/dataviews-special-float-values
Handle Infinity and NaN values for dataviews
2017-06-27 16:58:44 +02:00
Javier Goizueta
b2f3735e95 The formula widget wasn't using the no_filters query for checking column types 2017-06-23 18:59:51 +02:00
Javier Goizueta
166e29e8ce Forward queries parameter from overview dataviews to base dataviews 2017-06-23 16:53:16 +02:00
Daniel García Aubert
32274e66fd Dataview formula: count infinities and nans as we do with nulls 2017-06-23 12:24:22 +02:00
Rafa de la Torre
15b88c6a67 Stub next version 2017-06-22 18:26:33 +02:00
Rafa de la Torre
2dae09c35b Fix yarn.lock and update HOWTO_RELEASE
In previous commit I updated all the dependencies with the newest
versions when I really intended to update just one. After asking the
node gurus we agreed on changing the docs to reflect a less aggressive
strategy for dependency upgrades.
2017-06-22 18:11:58 +02:00
Rafa de la Torre
14e71b929a Release version 3.9.4 2017-06-22 17:48:55 +02:00
Daniel García Aubert
5555b8ad8e Going green: support numeric NaN values for dataviews 2017-06-21 18:59:36 +02:00
Daniel García Aubert
e44d418db3 Going red: Add test to check that aggregation dataview supports numeric special value 2017-06-21 18:44:21 +02:00
Daniel García Aubert
17aebf53e2 Merge branch 'master' into dataviews-special-float-values 2017-06-19 10:58:13 +02:00
Daniel García Aubert
02f117af1b Merge branch 'dataviews-special-float-values' of github.com:CartoDB/Windshaft-cartodb into dataviews-special-float-values 2017-06-16 13:01:39 +02:00
Daniel García Aubert
b1ac5b8ca9 Handle special float values only if column is float 2017-06-16 12:57:46 +02:00
Mario de Frutos
e2b976d9d0 Fix version 2017-06-16 11:16:17 +02:00
Mario de Frutos
8e95cf20c0 Upgrade camshaft to 0.55.5 (#703) 2017-06-16 11:13:40 +02:00
Daniel García Aubert
20d7f1a7c5 Handle special float values only when aggregation columns is float (overviews) 2017-06-15 19:22:26 +02:00
Daniel García Aubert
115d8fe685 Handle special float values only when aggregation columns is float 2017-06-15 19:07:31 +02:00
Mario de Frutos
2a366ec16f Stubs next version 2017-06-15 18:32:28 +02:00
Mario de Frutos
849caf9b58 Upgrade to cammshaft 0.55.4 (#702) 2017-06-15 18:30:13 +02:00
Daniel García Aubert
ad570ab6f2 Use dataview base to get column type in formula dataviews 2017-06-15 18:04:35 +02:00
Daniel García Aubert
443c1100d7 Formula dataview: support special values only if column is a float column 2017-06-15 16:31:31 +02:00
Daniel García Aubert
7d0af4e259 Going green: handle special float values for formula when overviews are involved 2017-06-14 19:44:48 +02:00
Daniel García Aubert
81f60959e5 Going green: handle special float values for formula when overviews are involved 2017-06-14 19:20:39 +02:00
Daniel García Aubert
ef849aec34 Going red: add test to check that special float values are not being filtered out in formula dataview when the layer uses overviews 2017-06-14 19:19:08 +02:00
Daniel García Aubert
dee00e6abd Going green: handle special float values for histogram when overviews are involved 2017-06-14 19:00:37 +02:00
Daniel García Aubert
06d40e8b1e Going red: add test to check that special float values are not being filtered out when the layer uses overviews 2017-06-14 18:57:32 +02:00
Daniel García Aubert
3f17c8b15a Filter out special float values before categorizing them 2017-06-14 15:05:46 +02:00
Daniel García Aubert
668b22628c Going green: support special float values in aggergation dataview with overviews 2017-06-13 19:01:59 +02:00
Daniel García Aubert
c08db78a0b Going red: implement test to check aggregation with overviews support special float values 2017-06-13 19:01:28 +02:00
Daniel García Aubert
551b6d409a Remove bad condition 2017-06-13 09:44:53 +02:00
Daniel García Aubert
3ae66e4143 Do not filter special values out if aggregation column is not defined 2017-06-13 09:30:43 +02:00
Daniel García Aubert
227937bf4c Remove test filter 2017-06-12 19:50:24 +02:00
Daniel García Aubert
cb7ec5d556 Fix jshint typos 2017-06-12 19:49:58 +02:00
Daniel García Aubert
8b2fa27ba7 Calculate aggregation filtering out special float values 2017-06-12 19:45:06 +02:00
Daniel García Aubert
962fa05574 Remove console 2017-06-12 19:28:49 +02:00
Daniel García Aubert
75d07745e6 Improve readability 2017-06-12 19:22:37 +02:00
Daniel García Aubert
7b5111614c Summarize special float values for ranked aggregation 2017-06-12 19:21:41 +02:00
Daniel García Aubert
e60bb770db Summarizes infinity and NaN values 2017-06-12 18:55:33 +02:00
Daniel García Aubert
ba6dc62a38 Going red: implementet test to check special float values support 2017-06-12 18:15:39 +02:00
Daniel García Aubert
e6aededf08 Fix typo 2017-06-12 17:19:05 +02:00
Daniel García Aubert
0aae29fb4b Fix jshint typo 2017-06-09 15:28:18 +02:00
Daniel García Aubert
9ba65bd5a4 Going green: Fix test for formula overviews flavour 2017-06-09 15:18:52 +02:00
Daniel García Aubert
7a3498e8ec Going red: formula does not work with Infinity or NaN values 2017-06-09 12:17:16 +02:00
Daniel
271887eb46 Merge pull request #696 from CartoDB/dataview-backend-small-refactor
Dataview backend small refactor
2017-06-08 18:24:59 +02:00
Daniel García Aubert
cd53eda0a5 Handle Infinity and NaN values for histograms 2017-06-08 16:01:41 +02:00
Daniel García Aubert
6c301403e3 Histogram going red: fails while quering Infinity and NanN values 2017-06-08 15:59:33 +02:00
Daniel García Aubert
02bf1dd2d7 Release 3.9.1 2017-06-06 17:34:38 +02:00
Daniel
d365e092b9 Merge pull request #699 from CartoDB/update_camshaft_0553
Bump camshaft version to  0.55.3
2017-06-06 17:32:18 +02:00
Daniel García Aubert
45d1d07ea2 Use released version of comshaft (0.55.3) 2017-06-06 17:21:33 +02:00
Mario de Frutos
f14a61528a Upgrade to camshaft 0.55.3 2017-06-06 10:52:37 +02:00
Raul Ochoa
4b562e6768 Merge conditions 2017-05-31 13:00:37 -04:00
Raul Ochoa
b4fbe0b8cf No in advance var definition 2017-05-31 12:58:35 -04:00
Raul Ochoa
62514fc563 Extract query rewrite data to function 2017-05-31 12:58:20 -04:00
Raul Ochoa
ef3cad6599 Extract to function 2017-05-31 12:52:41 -04:00
Raul Ochoa
4e53803b3b Create BBoxFilter independently from rewrite data 2017-05-31 12:51:07 -04:00
Raul Ochoa
40a73f2eaf Merge pull request #695 from CartoDB/fix-bboxes-filters
Add test to detect and fix incorrect bbox filter splitting
2017-05-31 11:23:42 -04:00
Javier Goizueta
31557b06be Add test to detect and fix incorrect bbox filter splitting
When bbox crosses date line and is split in two, the eastern box wasn't correct
2017-05-31 11:09:51 -04:00
Daniel García Aubert
c8ea595f47 Stubs next version 2017-05-31 12:14:58 +02:00
Daniel García Aubert
dbd0398d9b Release 3.9.0 2017-05-31 12:13:25 +02:00
Daniel
4f7ec0dd4d Merge pull request #683 from CartoDB/664-layergroup-stats
Use windshaft-stats to extract info about layer metadata
2017-05-31 11:58:00 +02:00
Daniel García Aubert
5a1623b667 Merge branch 'master' into 664-layergroup-stats 2017-05-31 11:50:32 +02:00
Daniel García Aubert
2c509d97b1 Point windshaft to the last released version 2017-05-31 11:46:46 +02:00
Daniel García Aubert
da3f30dd9f Update NEWS 2017-05-31 11:27:41 +02:00
Mario de Frutos
440953b1cd If we have the stats FF disbabled return empty array instead of null
But we keep checking for elements in the returned object because
we don't want to include the stats property if the FF is disabled
2017-05-30 10:47:47 +02:00
Raul Ochoa
45471597bb Merge pull request #693 from CartoDB/test-improvements
Do not assert inside assert.response
2017-05-26 13:20:44 +02:00
Raul Ochoa
882aeacac2 Rewrite test to take advantage of changes in assert.response/TestClient
This should avoid the issue of preventing the whole suite to halt, as
in https://travis-ci.org/CartoDB/Windshaft-cartodb/builds/236337027.
2017-05-26 13:13:19 +02:00
Raul Ochoa
248adab05b Catch response body if any to capture Redis keys 2017-05-26 13:06:04 +02:00
Raul Ochoa
f4e99629f6 Do not assert inside response, but pass error into callback
Preferably we should put response outside of assert and change its
callback signature. However, I don't think it is worth the effort
right now.
2017-05-26 13:02:02 +02:00
Daniel
df08463dcf Merge pull request #690 from CartoDB/allow-es6-syntax
Allow es6 syntax
2017-05-25 14:28:02 +02:00
Daniel García Aubert
b84bb469e5 Allow es6 syntax 2017-05-25 11:26:04 +02:00
Daniel
26e4d19635 Merge pull request #689 from CartoDB/upgrade-camshaft-to-0.55.2
Upgrade camshaft to 0.55.2
2017-05-24 12:01:39 +02:00
Daniel García Aubert
69984ed895 Upgrade camshaft to 0.55.2 2017-05-24 11:54:30 +02:00
Daniel García Aubert
d471905358 Merge branch 'master' into 664-layergroup-stats 2017-05-23 13:47:27 +02:00
Daniel García Aubert
e5223980cf Update NEWS 2017-05-23 13:46:56 +02:00
Daniel
1f65968de3 Merge pull request #688 from CartoDB/upgrade-camshaft-to-0.55.1
Upgrade camshaft to 0.55.1
2017-05-23 13:45:57 +02:00
Daniel García Aubert
51c11aff03 Upgrade camshaft to 0.55.1 2017-05-23 13:37:34 +02:00
Daniel García Aubert
87e6e64d42 Merge branch 'master' into 664-layergroup-stats 2017-05-23 12:41:14 +02:00
Raul Ochoa
7dfb7c605e Merge pull request #687 from CartoDB/remove-turbo-carto-promise-global
Remove Promise hack from turbo-carto adapter
2017-05-22 18:52:27 +02:00
Raul Ochoa
8b0964ad7e Remove Promise hack from turbo-carto adapter 2017-05-22 18:45:24 +02:00
Daniel García Aubert
5135b6e14a Stubs next version 2017-05-22 14:57:03 +02:00
Daniel García Aubert
dcb26560e3 Release 3.8.0 2017-05-22 14:50:41 +02:00
Daniel
921468668b Merge pull request #686 from CartoDB/upgrade-camshaft-to-0.55.0
Upgrade camshaft to 0.55.0 and turbo-carto to 0.19.1
2017-05-22 14:39:10 +02:00
Mario de Frutos
76c2f8dc0e Includes release changes of turbo-carto too 2017-05-22 14:33:14 +02:00
Daniel García Aubert
7efb196247 Upgrade camshaft to 0.55.0 2017-05-22 13:09:42 +02:00
Raul Ochoa
95c324ddd1 Merge pull request #685 from CartoDB/update-dev-deps
Update dev deps
2017-05-18 18:25:32 +02:00
Raul Ochoa
0a3d1fbdf9 Upgrade jshint and fix test 2017-05-18 17:39:56 +02:00
Raul Ochoa
45c1ccab9e Upgrade mocha dep 2017-05-18 17:39:26 +02:00
Daniel García Aubert
cc2a96579b Merge branch 'master' into 664-layergroup-stats 2017-05-18 16:39:52 +02:00
Daniel García Aubert
61b19856e9 Stubs next version 2017-05-18 16:36:50 +02:00
Daniel García Aubert
67aa2d1a00 Release 3.7.1 2017-05-18 16:34:56 +02:00
Daniel
c6ee2eac62 Merge pull request #684 from CartoDB/fix-buffersize-undefined-in-mapconfig
Fix buffersize undefined in mapconfig instantiation
2017-05-18 16:33:31 +02:00
Daniel García Aubert
3978d58d66 Remove empty line 2017-05-18 16:12:28 +02:00
Daniel García Aubert
cd86387fa7 Merge branch 'fix-buffersize-undefined-in-mapconfig' into 664-layergroup-stats 2017-05-18 15:58:12 +02:00
Daniel García Aubert
3ce38d7081 Going green: fix type error when no buffersize is defined in mapconfig 2017-05-18 15:49:57 +02:00
Daniel García Aubert
e9112da305 Going red: add test to check undefined buffersize in requested mapconfig throws error 2017-05-18 15:41:41 +02:00
Mario de Frutos
c9e6e921cb Merge branch 'master' into 664-layergroup-stats 2017-05-18 13:45:38 +02:00
Daniel García Aubert
4e715f6ba4 Stubs next version 2017-05-18 13:24:48 +02:00
Daniel García Aubert
8f156b9f13 Release 3.7.0 2017-05-18 13:23:22 +02:00
Daniel
954876f738 Merge pull request #673 from CartoDB/665-buffer-size
Manage multiple values of buffer-size for different formats
2017-05-18 13:20:25 +02:00
Daniel García Aubert
fd178bcf71 Upgrade windshaft to 3.2.0 2017-05-18 13:14:51 +02:00
Daniel García Aubert
acaff98da5 Merge branch 'master' into 665-buffer-size 2017-05-18 13:08:46 +02:00
Mario de Frutos
ed56094be2 PR changes 2017-05-18 11:51:12 +02:00
Daniel García Aubert
c65518cf41 Get back layer-stats from windshaft-stats 2017-05-17 20:16:43 +02:00
Daniel García Aubert
fb4ee61b83 Use setInmmediate vs process.nextTick 2017-05-17 12:55:05 +02:00
Daniel García Aubert
808c729a0e Now supported formats for buffer-size customization are bound to the adapter 2017-05-17 12:33:41 +02:00
Daniel García Aubert
4602fb3ecf Send stats for png32 tiles 2017-05-17 12:16:16 +02:00
Daniel García Aubert
c59996303d Send stats for mvt tiles 2017-05-17 12:04:11 +02:00
Daniel García Aubert
13b1978d49 Include layer param to reach the right tile for grid.json 2017-05-17 11:40:53 +02:00
Daniel García Aubert
e13ae8d5af Do not make optional layer param in URL template 2017-05-17 11:40:18 +02:00
Daniel García Aubert
0f16c0e396 Merge branch '664-layergroup-stats' of github.com:CartoDB/Windshaft-cartodb into 664-layergroup-stats 2017-05-16 12:15:40 +02:00
Daniel García Aubert
29361f5392 Use windhshaft with latest changes for layer stats 2017-05-16 12:07:17 +02:00
Mario de Frutos
422867762b package.json for staging 2017-05-12 17:14:07 +02:00
Mario de Frutos
5969c99e8a Removed not used parameters for layer stats 2017-05-12 16:42:10 +02:00
Mario de Frutos
5417933ecc Change to tests for layer stats because now uses CDB_EstimateRowCount fuction 2017-05-12 16:42:10 +02:00
Mario de Frutos
59585b5cd9 We leave only one feature flag for stats 2017-05-12 16:42:10 +02:00
Mario de Frutos
522c86e6f2 Change from branch to commits for staging dependencies 2017-05-12 16:42:04 +02:00
Mario de Frutos
1a7fd9bf31 Changed stats name from featureCount to estimatedFeatureCount 2017-05-12 16:40:34 +02:00
Mario de Frutos
7596df96ed package.json for staging 2017-05-12 12:58:13 +02:00
Mario de Frutos
44cca38538 Change to tests for layer stats because now uses CDB_EstimateRowCount fuction 2017-05-12 12:51:45 +02:00
Mario de Frutos
f6fff6953e We leave only one feature flag for stats 2017-05-12 12:50:57 +02:00
Mario de Frutos
35df0c3a68 Change from branch to commits for staging dependencies 2017-05-11 14:26:00 +02:00
Daniel García Aubert
f9c8178d99 Stubs next version 2017-05-11 13:46:34 +02:00
Daniel García Aubert
787ca1607a Release 3.6.6 2017-05-11 13:37:43 +02:00
Daniel
7179c0a5f1 Merge pull request #682 from CartoDB/camshaft-error-node-id
Upgrade camshaft to 0.54.4
2017-05-11 13:29:29 +02:00
Daniel García Aubert
b739db1023 Use released version of camshaft 2017-05-11 13:19:14 +02:00
Daniel García Aubert
66a898cdc2 Upgrade camshaft to get error node-id 2017-05-11 12:55:53 +02:00
Mario de Frutos
61f9ea6e86 Changed stats name from featureCount to estimatedFeatureCount 2017-05-11 12:47:35 +02:00
Daniel García Aubert
5a44d6c547 Drop geojson support for buffersize customization 2017-05-10 18:35:30 +02:00
Daniel García Aubert
53d1b2fbbf Rename mapconfig-named-map-adapter by mapconfig-buffer-size-adapter 2017-05-10 18:16:22 +02:00
Daniel García Aubert
2c9d30e042 Be more flexible validating buffer-size customization 2017-05-10 17:49:28 +02:00
Mario de Frutos
968677e275 package.json poiting to staging branches 2017-05-10 17:26:12 +02:00
Mario de Frutos
daf19c5e27 Stats backend only provides stats not metadata 2017-05-10 17:17:01 +02:00
Raul Ochoa
ac94118798 Merge pull request #678 from CartoDB/print-attributions
added print attributions from Docs FAQs to Static Maps API content
2017-05-10 09:26:07 +02:00
Mario de Frutos
7d5b6b0820 Lint changes and yarn.lock 2017-05-09 18:24:24 +02:00
Mario de Frutos
b87e442801 Remove vertex count from stats tests 2017-05-09 18:24:11 +02:00
Raul Ochoa
1a197bb9cf Stubs next version 2017-05-09 15:06:49 +02:00
Raul Ochoa
5b96db2ba2 Release 3.6.5 2017-05-09 15:06:00 +02:00
Raul Ochoa
3b687ce09a Merge pull request #681 from CartoDB/upgrade-camshaft
Upgrades camshaft to 0.54.3
2017-05-09 15:05:15 +02:00
Raul Ochoa
7bb039b13c Upgrades camshaft to 0.54.3 2017-05-09 14:58:25 +02:00
Mario de Frutos
474d68687c Include vertexCount in the stats tests 2017-05-09 14:47:39 +02:00
Mario de Frutos
b25540720c Added acceptance tests for stats functionallity 2017-05-09 14:36:35 +02:00
Mario de Frutos
759d28f12f Include feature flag to enable/disable stats 2017-05-09 12:51:22 +02:00
Mario de Frutos
15c68711aa Create stats backend to decouple stats logic from map controller 2017-05-09 12:31:16 +02:00
Mario de Frutos
568d6b5458 Include the layers metadata into the layergroup object 2017-05-09 11:59:11 +02:00
Mario de Frutos
525c0f2afa Added rendererCache as dependency for map controller 2017-05-09 11:58:48 +02:00
Mario de Frutos
3f6c8fa51c Use windshaft-stats to get layer stats 2017-05-08 18:42:40 +02:00
Raul Ochoa
0ac53db73a Stubs next version 2017-05-05 16:04:25 +02:00
Raul Ochoa
36e9239056 Release 3.6.4 2017-05-05 16:03:53 +02:00
Raul Ochoa
4e6e267f10 Fix news 2017-05-05 16:03:38 +02:00
Raul Ochoa
2c235b6629 Merge pull request #680 from CartoDB/upgrade-deps
Upgrade deps
2017-05-05 16:02:24 +02:00
Raul Ochoa
6bd7537467 Upgrade deps 2017-05-05 15:57:02 +02:00
Daniel García Aubert
55a351d751 Point windshaft to a specific buffer-size commit 2017-05-03 17:35:55 +02:00
Daniel García Aubert
05d3b3bf66 Point windshaft to specific commit for buffer-size configuration 2017-05-03 17:21:08 +02:00
Daniel García Aubert
e97466378e Add test for different formats to anonymous maps and named maps with placeholders 2017-05-03 11:17:51 +02:00
csobier
8426dd00f1 added print attributions from Docs FAQs to Static Maps API content 2017-05-02 12:48:55 -04:00
Daniel García Aubert
b2b6cf1f02 Merge branch '665-buffer-size' of github.com:CartoDB/Windshaft-cartodb into 665-buffer-size 2017-04-28 19:23:49 +02:00
Daniel García Aubert
c9af38ecd0 Fix issue when 'grid.json' format is not captured properly due to a weird behaviour in regex 2017-04-28 19:21:51 +02:00
Mario de Frutos
be58adb1b9 Be able to override buffer-size configuration without placeholders in named maps 2017-04-28 19:20:00 +02:00
Mario de Frutos
bfb283c5ba wip 2017-04-28 14:46:36 +02:00
Mario de Frutos
332a56b736 Mapconfig only support object for the buffer-size property 2017-04-28 14:22:16 +02:00
Daniel García Aubert
2f4e4246a4 Refactor test-client in order to use same interface for named and anonymous maps 2017-04-26 18:27:18 +02:00
Daniel García Aubert
c481d6473c Use parseInt instead of number constructor 2017-04-26 17:01:21 +02:00
Daniel García Aubert
40c0e306af Remove invalid assertions 2017-04-25 20:40:17 +02:00
Daniel García Aubert
0d840e6daf Javascript style typo 2017-04-25 19:41:30 +02:00
Daniel García Aubert
07e507e1aa Remove dictionary as placeholder type for named maps 2017-04-25 19:40:12 +02:00
Mario de Frutos
7ea7a991aa Buffersize customizable through named maps' placeholders 2017-04-25 19:27:31 +02:00
Daniel García Aubert
0577fa5308 Add test 2017-04-25 17:54:31 +02:00
Daniel García Aubert
f29ee1b4ac Add test to use placeholder buffer-size value 2017-04-25 15:48:23 +02:00
Daniel García Aubert
0c08713521 First attempt: support buffer-size configuration for named maps 2017-04-25 14:34:17 +02:00
Raul Ochoa
567928a7f5 Stubs next version 2017-04-25 12:36:29 +02:00
Raul Ochoa
ae9e211f30 Release 3.6.3 2017-04-25 12:35:34 +02:00
Raul Ochoa
b5b75df91a Merge pull request #674 from CartoDB/upgrade-windshaft
Upgrades windshaft to 3.1.1
2017-04-25 12:33:22 +02:00
Raul Ochoa
8ddccc0b0c Upgrades windshaft to 3.1.1 2017-04-25 12:19:49 +02:00
Daniel García Aubert
383a1a330a Test with buffer-size 0 2017-04-25 10:43:07 +02:00
Raul Ochoa
95195fff6f Stubs next version 2017-04-24 19:33:03 +02:00
Raul Ochoa
93b77dc4c1 Release 3.6.2 2017-04-24 19:32:10 +02:00
Raul Ochoa
4aee7fb1b8 Merge pull request #671 from CartoDB/upgrade-deps
Upgrades grainstore to 1.6.3
2017-04-24 19:31:20 +02:00
Raul Ochoa
a6d68dba5e Upgrades grainstore to 1.6.3 2017-04-24 19:11:53 +02:00
Daniel García Aubert
109c550187 Remove filter 2017-04-24 18:57:20 +02:00
Daniel García Aubert
06353941e6 Implement test to exercise buffer-size configuration by format 2017-04-24 18:56:15 +02:00
Daniel García Aubert
fed953d195 Support mvt tiles 2017-04-24 18:55:08 +02:00
Raul Ochoa
883f87c7c8 Document gc_interval configuration entry 2017-04-24 18:28:51 +02:00
Raul Ochoa
14d37268d6 Stubs next version 2017-04-24 14:44:38 +02:00
Raul Ochoa
4b6181039d Release 3.6.1 2017-04-24 14:43:46 +02:00
Raul Ochoa
47944671c6 fix release date 2017-04-24 14:43:23 +02:00
Raul Ochoa
f33a7dd665 Merge pull request #668 from CartoDB/upgrade-camshaft
Upgrade camshaft to 0.54.1
2017-04-24 14:41:06 +02:00
Raul Ochoa
781e5a71bf Upgrade camshaft to 0.54.1 2017-04-24 12:29:08 +02:00
Raul Ochoa
c4ff884ad0 Merge pull request #666 from CartoDB/upgrade-camshaft
Upgrades camshaft to 0.54.0
2017-04-20 18:33:07 +02:00
Raul Ochoa
02b9f85b16 Upgrades camshaft to 0.54.0 2017-04-20 18:25:57 +02:00
Daniel García Aubert
2756252368 Stubs next version 2017-04-11 19:23:04 +02:00
Daniel García Aubert
a386abf5a5 Release 3.5.1 2017-04-11 18:34:18 +02:00
Daniel
e5c2c35a81 Merge pull request #662 from CartoDB/upgrade-camshaft-to-0.53.1
Upgrade camshaft to 0.53.1
2017-04-11 18:31:49 +02:00
Daniel García Aubert
227112c7aa Update yarn.lock 2017-04-11 18:27:54 +02:00
Daniel García Aubert
a4ed37bdfc Upgrade camshaft to 0.53.1 2017-04-11 18:19:39 +02:00
Daniel García Aubert
c6a62cee61 Stubs next version 2017-04-10 16:13:30 +02:00
Daniel García Aubert
891bc818b2 Release 3.5.0 2017-04-10 12:20:44 +02:00
Daniel
ebe25d6f20 Merge pull request #638 from CartoDB/629_fix_cache_invalidation
Fix cache invalidation problem with affected tables
2017-04-10 11:55:57 +02:00
Daniel García Aubert
92ec17218b Upgrade camshaft to 0.53.0 2017-04-10 11:36:17 +02:00
Mario de Frutos
e8a0f6b7b6 Point to camshaft branch to test properly 2017-04-07 16:00:29 +02:00
Mario de Frutos
125c39967c Make the cache headers tests idempotent 2017-04-07 15:59:58 +02:00
Mario de Frutos
4132bc755d Add cdb_invalidate_varnish function fixture to tests 2017-04-07 15:59:58 +02:00
Mario de Frutos
9707881bf9 Include check for surrogate-key header and renamed the test file 2017-04-07 15:59:58 +02:00
Mario de Frutos
fa6493ae44 Affected tables are now included in X-Cache-Channel 2017-04-07 15:59:58 +02:00
Mario de Frutos
0c387cf6d9 Add more tests for x-cache-channel but with analysis 2017-04-07 15:59:58 +02:00
Mario de Frutos
5e4d1d5c1c Get affected tables and add it to the layergroup 2017-04-07 15:59:58 +02:00
Raul Ochoa
4d82fd65f6 Merge pull request #659 from CartoDB/upgrade-camshaft
Upgrades camshaft to 0.52.0
2017-04-07 14:41:14 +02:00
Raul Ochoa
6d3644f13b Upgrades camshaft to 0.52.0 2017-04-07 13:29:30 +02:00
Raul Ochoa
7a5aa7ba35 Stubs next version 2017-04-03 16:22:15 +02:00
Raul Ochoa
9c9609eb2b Release 3.4.0 2017-04-03 16:21:29 +02:00
Raul Ochoa
418d3c074f Merge pull request #657 from CartoDB/upgrade-camshaft
Upgrade camshaft to 0.51.0
2017-04-03 16:15:48 +02:00
Raul Ochoa
6bbda3d41e Upgrade camshaft to 0.51.0 2017-04-03 15:53:39 +02:00
Raul Ochoa
25669bb3f2 Stubs next version 2017-04-03 13:07:53 +02:00
Raul Ochoa
508d495a23 Release 3.3.0 2017-04-03 13:07:05 +02:00
Raul Ochoa
06427dc009 Bump version and add news 2017-04-03 13:06:37 +02:00
Raul Ochoa
c325df1414 Merge pull request #655 from CartoDB/static-maps-layers-filter
Static maps layers filter
2017-04-03 12:07:38 +02:00
Raul Ochoa
07447160e3 Merge pull request #654 from CartoDB/non-conditional-profiler
Remove all conditional branches to call req.profiler
2017-04-03 11:58:38 +02:00
Raul Ochoa
ededc73fd7 Throw on invalid params argument 2017-03-31 18:39:29 +02:00
Raul Ochoa
cad02bfad7 Remove all conditional branches to call req.profiler
req.profiler is created in a middleware for all requests.
2017-03-30 20:31:53 +02:00
Raul Ochoa
94299f0452 Configure extra allowed params per endpoint via middleware
Instead of making all params available in all endpoints, we control
what endpoints allow what extra params.

Dataviews endpoints should be migrated to this.
2017-03-30 20:12:55 +02:00
Raul Ochoa
ae5d82c41d Add test to go red 2017-03-30 20:12:20 +02:00
Raul Ochoa
6468822295 Remove layer param before creating a better solution 2017-03-30 20:08:45 +02:00
Raul Ochoa
777ae31426 Merge branch 'master' into static-maps-layers-filter 2017-03-30 19:33:59 +02:00
Raul Ochoa
1ca56fb81c Update news 2017-03-30 17:52:25 +02:00
Raul Ochoa
5d74e1eafe Stubs next version 2017-03-30 14:33:41 +02:00
Raul Ochoa
f3fdd7ff25 Release 3.2.0 2017-03-30 14:32:48 +02:00
Raul Ochoa
fbbe69dac0 Merge pull request #652 from CartoDB/upgrade-windshaft
Update windshaft to 3.1.0
2017-03-30 14:32:21 +02:00
Raul Ochoa
ac54179f14 Update windshaft to 3.1.0 2017-03-30 14:12:31 +02:00
Raul Ochoa
50d296e46c Merge pull request #650 from CartoDB/upgrade-grainstore-1.6.2
Upgrade dependencies: grainstore@1.6.2
2017-03-29 16:16:50 +02:00
Raul Ochoa
616ba6500c Merge pull request #649 from CartoDB/gc
Active GC interval
2017-03-29 16:11:48 +02:00
Daniel García Aubert
d9968f2c91 Upgrade dependencies: grainstore@1.6.2 2017-03-29 16:02:37 +02:00
Raul Ochoa
8ca9c5bcf7 Active GC interval
Interval timer is configurable, disabling by using <=0 value.
2017-03-29 15:56:30 +02:00
csobier
a7b0618f91 Merge pull request #643 from CartoDB/docs-fix-broken-hyperlinks
fixed docs example links, Andy changed his user name so links broke. …
2017-03-24 09:44:50 -04:00
csobier
e9896e34e1 found and fixed a typo 2017-03-24 09:33:15 -04:00
csobier
28bd03765a fixed docs example links, Andy changed his user name so links broke. I fixed. 2017-03-24 09:04:31 -04:00
Daniel García Aubert
24a86ae8df Stubs next version 2017-03-23 11:04:12 +01:00
Daniel García Aubert
f5c349e105 Release 3.1.1 2017-03-23 11:02:04 +01:00
Raul Ochoa
e8d2e28dba Merge pull request #642 from CartoDB/resources-urls-crc-hash
Use crc32 instead of md5 for computing subdomain candidate
2017-03-23 01:21:50 +01:00
Raul Ochoa
e0c2423ace Remove unused import 2017-03-23 01:14:56 +01:00
Raul Ochoa
5e429ba71f Use crc32 instead of md5 for computing subdomain candidate 2017-03-23 01:03:45 +01:00
Daniel García Aubert
64dfdba94d Release 3.1.0 2017-03-22 19:20:24 +01:00
Daniel
3866413504 Merge pull request #641 from CartoDB/resource-urls
Generate URLs for resources based on CDN + template rules
2017-03-22 19:15:08 +01:00
Raul Ochoa
2da834784f Generate URLs for resources based on CDN + template rules 2017-03-22 18:58:37 +01:00
Daniel García Aubert
d6181da32b Stubs next version 2017-03-22 13:53:48 +01:00
Daniel García Aubert
8287b94a25 Release 3.0.2 2017-03-22 13:20:59 +01:00
Daniel García Aubert
bc633301fe Update HOWTO_RELEASE 2017-03-22 13:11:12 +01:00
Daniel García Aubert
ed94fb4a66 Define yarn version of clients that must be used 2017-03-22 13:10:06 +01:00
Daniel García Aubert
fc27086052 Remove script to generate shrinkwrap 2017-03-22 13:08:12 +01:00
Daniel García Aubert
de1d1961e3 Upgrade yarn.lock depdendencies: depth > 1 2017-03-22 12:55:22 +01:00
Daniel García Aubert
a90a9383b4 Stubs next version 2017-03-21 19:45:25 +01:00
Daniel García Aubert
fd244287d5 Release 3.0.1 2017-03-21 19:43:46 +01:00
Daniel
fafe9e7e8a Merge pull request #640 from CartoDB/upgrade-windshaft-3.0.1
Bump windshaft version to 3.0.1
2017-03-21 19:35:54 +01:00
Daniel García Aubert
db37513206 Bump windshaft version to 3.0.1 2017-03-21 19:15:41 +01:00
Daniel García Aubert
c023088a3f Stubs next version 2017-03-21 15:44:33 +01:00
Daniel García Aubert
59f6217c4f Release 3.0.0 2017-03-21 15:34:47 +01:00
Daniel
0e43fbbb34 Merge pull request #635 from CartoDB/node-v6
Support Node v6 LTS and upgrade to Mapnik v3.x
2017-03-21 15:30:59 +01:00
Daniel García Aubert
1720f22247 Improve doc: 'yarn install' command is deprecated, use 'yarn' instead 2017-03-21 14:58:09 +01:00
Daniel García Aubert
3f791d25b5 Add a note about moving from npm to yarn 2017-03-17 17:58:33 +01:00
Daniel García Aubert
acd3047500 Update NEWS 2017-03-17 17:49:19 +01:00
Daniel García Aubert
7b3a4aa2a8 Update README.md 2017-03-17 17:41:20 +01:00
Daniel García Aubert
4bfaeeb44b Update INSTALL.md 2017-03-17 17:37:35 +01:00
Daniel García Aubert
a094ae7197 Update NEWS 2017-03-17 17:30:58 +01:00
Daniel García Aubert
ef0362d118 Fix windshaft version to 3.0.0 to avoid warning message while starting the service 2017-03-17 17:21:09 +01:00
Daniel García Aubert
5a5763684d Don't provide a npm's shrinkwrap anymore 2017-03-17 17:17:17 +01:00
Daniel García Aubert
6e575300e3 Update yarn.lock 2017-03-17 17:13:49 +01:00
Daniel García Aubert
8109fc4d46 Merge branch 'master' into node-v6 2017-03-17 17:06:58 +01:00
Daniel García Aubert
e0519e7851 Stubs next version 2017-03-17 17:06:39 +01:00
Daniel García Aubert
6334df5f5f Merge branch 'master' into node-v6 2017-03-17 17:03:53 +01:00
Daniel García Aubert
38294d29f5 Release 2.89.0 2017-03-17 16:45:18 +01:00
Daniel
5b131cc8a7 Merge pull request #639 from CartoDB/upgrade-windshaft-to-2.8.0
Upgrade windshaft to 2.8.0
2017-03-17 16:37:07 +01:00
Daniel García Aubert
5dee654132 Upgrade windshaft to 2.8.0 2017-03-17 16:26:26 +01:00
Daniel García Aubert
d902476780 Fix assertions, now MapCofig.getLayer() return {} if layer not found 2017-03-17 11:06:40 +00:00
Ubuntu
bc5dabef3c Revert "Fix assertions, now MapCofig.getLayer() return {} if layer not found"
This reverts commit c839a0b0a3.
2017-03-17 11:04:50 +00:00
Daniel García Aubert
024f1e4851 Fix assertions, now MapCofig.getLayer() return {} if layer not found 2017-03-17 10:45:59 +00:00
Raul Ochoa
5f87417d9e Merge pull request #637 from CartoDB/histogram-type-discovery
Histogram column type discovery query uses non-filtered query
2017-03-17 11:03:15 +01:00
Raul Ochoa
fa94550261 Include changes for overviews implementation 2017-03-16 19:15:34 +01:00
Daniel García Aubert
11efbf034e Drop support for Node 0.10 2017-03-16 17:18:44 +01:00
Daniel García Aubert
c839a0b0a3 Fix assertions, now MapCofig.getLayer() return {} if layer not found 2017-03-16 16:44:41 +01:00
Daniel García Aubert
420b657db8 Upgrade windshaft to 3.0.0 2017-03-16 16:42:43 +01:00
Raul Ochoa
2656a26272 Merge pull request #622 from strk/typo
Trip epoch is over...
2017-03-16 16:26:53 +01:00
Raul Ochoa
8694c120bc Allow to overwrite layers filter in static maps images 2017-03-15 11:00:10 +01:00
Raul Ochoa
992b2b6ba7 Histogram column type discovery query uses non-filtered query
Pass all queries to the dataview and use the no filters one for
discovering what is the column type associated to the histogram dataview.
2017-03-13 18:40:29 +01:00
Raul Ochoa
924f009390 Test for #606: function avg(timestamp with time zone) does not exist 2017-03-13 18:36:43 +01:00
Raul Ochoa
48a1244fa0 Stubs next version 2017-03-10 11:06:47 +01:00
Raul Ochoa
8789a959e5 Release 2.88.4 2017-03-10 11:06:06 +01:00
Raul Ochoa
5765ac59cc Merge pull request #636 from CartoDB/upgrade-camshaft
Upgrades camshaft to 0.50.3
2017-03-10 11:05:30 +01:00
Raul Ochoa
3b9bf96431 Upgrades camshaft to 0.50.3 2017-03-10 10:58:07 +01:00
Daniel García Aubert
a5fe5e7052 Update shrinkwrap 2017-03-08 10:56:01 +01:00
Daniel García Aubert
0f96b5a4a5 Update yarn.lock 2017-03-08 10:24:04 +01:00
Daniel García Aubert
25babeae56 Merge branch 'node-v6-yarn' into node-v6 2017-03-07 18:01:16 +01:00
Raul Ochoa
1951e79962 Make assertions compatible 2017-03-07 13:27:13 +01:00
Daniel García Aubert
1e0e31cc1c Merge branch 'node-v6' of github.com:CartoDB/Windshaft-cartodb into node-v6 2017-03-07 13:02:18 +01:00
Daniel García Aubert
8d35f72fcb Back to use current assertions as grainstore implements a fallback mechanism when translates styles 2017-03-07 12:59:52 +01:00
Daniel García Aubert
5f3e515131 Back test to use '=~' operator which is now supported by carto@0.15.1-cdb-3 in windshaft 2017-03-07 12:58:06 +01:00
Raul Ochoa
49236fce86 Upgrade locked deps 2017-03-02 11:38:09 +01:00
Raul Ochoa
9d2dde7a5a Merge branch 'node-v6' into node-v6-yarn 2017-03-02 11:07:52 +01:00
Raul Ochoa
c3e703237c Merge remote-tracking branch 'origin/master' into node-v6 2017-03-02 11:07:43 +01:00
Mario de Frutos
8868066445 Stubs next version 2017-03-02 11:00:53 +01:00
Mario de Frutos
b446c31cbc Other category now uses the selected aggregated function (#633)
* Other category in category widget uses selected aggregation function
Fixes https://github.com/CartoDB/Windshaft-cartodb/issues/628
2017-03-02 10:48:20 +01:00
Raul Ochoa
2d6e7070a6 Stubs next version 2017-02-23 18:12:54 +01:00
Raul Ochoa
473e0cb902 Release 2.88.2 2017-02-23 18:12:14 +01:00
Raul Ochoa
1a8fca0534 Formatting 2017-02-23 18:11:39 +01:00
Raul Ochoa
e24bc12fc9 Merge pull request #632 from CartoDB/upgrade-camshaft
Upgrades camshaft to 0.50.2
2017-02-23 18:10:22 +01:00
Raul Ochoa
e77c9141ed Upgrades camshaft to 0.50.2 2017-02-23 18:00:55 +01:00
Raul Ochoa
321157b17b Make target to generate shrinkwrap file applying prune
It removes dev dependencies from the local node_modules
and avoid getting those into shrinkwrap file.
2017-02-23 17:57:52 +01:00
Raul Ochoa
6ac6574b4c clean target to completely delete node_modules dir 2017-02-23 17:57:44 +01:00
Raul Ochoa
70ff0a9b8f Update freezed deps 2017-02-22 19:17:00 +01:00
Raul Ochoa
15bdb57a22 Merge remote-tracking branch 'origin/node-v6' into node-v6-yarn 2017-02-22 19:02:55 +01:00
Raul Ochoa
bb54e5520c Merge remote-tracking branch 'origin/master' into node-v6 2017-02-22 19:02:27 +01:00
Raul Ochoa
933d486a57 Merge pull request #630 from CartoDB/close-response-timer
Close timer for response preparation
2017-02-22 18:46:56 +01:00
Raul Ochoa
8a1c7f5b52 Close timer for response preparation
Timer for affectedTables is taking everything from response timing,
adding a tag to represent all the response preparation.
That way affectedTables only represents the time for retrieving the
affected tables themselves.
2017-02-22 18:38:25 +01:00
Rafa de la Torre
822954be5d Stub next version 2017-02-21 11:06:54 +01:00
Rafa de la Torre
57dc17518c Release 2.88.1 2017-02-21 11:01:15 +01:00
Mario de Frutos
3df8d4844e Stubs next version 2017-02-21 08:17:09 +01:00
Mario de Frutos
5a0443618f Merge pull request #626 from CartoDB/camshaft_v050
Camshaft 0.50.0
2017-02-21 08:08:34 +01:00
Raul Ochoa
8a76cd506f Fix issue with global Promise object 2017-02-20 18:46:48 +01:00
Mario de Frutos
50d05eae47 Version 2.88.0
Update camshaft version to 0.50.0
2017-02-20 18:46:47 +01:00
Raul Ochoa
ca41b3b600 Merge pull request #627 from CartoDB/change_release
We don't announce release deploy in google groups anymore
2017-02-20 10:31:39 +01:00
Mario de Frutos
43a17ddc7d We don't announce release deploy in google groups anymore 2017-02-17 18:01:59 +01:00
Mario de Frutos
dfa347f860 Update camshaft dependency to 0.50 2017-02-17 16:29:34 +01:00
Raul Ochoa
6033027812 Merge branch 'node-v6-shrinkwrap' into node-v6-yarn 2017-02-17 13:42:32 +01:00
Raul Ochoa
9ee6f7fbb8 Regenerate npm-shrinkwrap.json with npm@2.x 2017-02-17 13:41:58 +01:00
Raul Ochoa
60c0754800 Make assertions compatible 2017-02-17 13:36:15 +01:00
Raul Ochoa
a9251c5e71 Fix issue with global Promise object 2017-02-17 13:35:41 +01:00
Raul Ochoa
7daeddc946 Use yarn 2017-02-17 11:27:01 +01:00
Raul Ochoa
dbdb00070e Remove artificial node-zipfile dep 2017-02-17 11:26:46 +01:00
Raul Ochoa
e5c3c282ef Log Node.js version on startup 2017-02-10 10:03:06 +01:00
Raul Ochoa
caba79b5e2 Make target to generate shrinkwrap file applying prune
It removes dev dependencies from the local node_modules
and avoid getting those into shrinkwrap file.
2017-02-07 20:03:47 +01:00
Raul Ochoa
d359ea7fa6 clean target to completely delete node_modules dir 2017-02-07 19:55:37 +01:00
Raul Ochoa
c0abbe570f Add npm-shrinkwrap.json file 2017-02-07 18:56:45 +01:00
Raul Ochoa
229a2c0c3c Remove npm-shrinkwrap.json until we solve the issue associated to it 2017-02-07 18:50:57 +01:00
Daniel García Aubert
3f185c9c69 revert: disable cache for ci builds 2017-02-07 15:28:21 +01:00
Daniel García Aubert
f86f72ab27 disable cache for ci builds 2017-02-07 15:19:05 +01:00
Daniel García Aubert
74af17cc65 Add ubuntu toolchain for builds 2017-02-07 12:12:17 +01:00
Daniel García Aubert
aaa3e34c7f Use gcc 4.9 for build in travis 2017-02-07 11:55:21 +01:00
Daniel García Aubert
a053f198f5 Generate shrinkwrap 2017-02-07 11:45:48 +01:00
Daniel García Aubert
852ba68895 Upgrade redis in dev-dependency 2017-02-07 11:44:53 +01:00
Daniel García Aubert
1b22d176d6 Merge branch 'master' into node-v6 2017-02-06 19:03:16 +01:00
Daniel García Aubert
0ccbedf551 Use updated branch of windshaft 2017-02-06 18:25:32 +01:00
Raul Ochoa
28f1179336 Stubs next version 2017-02-02 16:44:41 +01:00
Raul Ochoa
de4d9e285e Release 2.87.5 2017-02-02 16:43:43 +01:00
Raul Ochoa
e0faaac822 Update news 2017-02-02 16:43:04 +01:00
Raul Ochoa
c84f27dd3f Merge pull request #623 from CartoDB/fix-dataviews-override-params-casting
Cast all dataview overrides values to Number
2017-02-02 16:42:48 +01:00
Raul Ochoa
12279d5c00 Cast dataview override values to Number or throw error
We were letting params expected as Numbers to be passed as any type
when they were not Numbers.
2017-02-02 16:20:16 +01:00
Raul Ochoa
281588abd2 Add test to validate bins param is casted as Number 2017-02-02 16:12:49 +01:00
Sandro Santilli
7e206b84aa Fix typo 2017-01-31 13:16:36 +01:00
Mario de Frutos
f69f999694 Fixed contributing link 2017-01-25 10:43:24 +01:00
Daniel García Aubert
c0c062592f Stubs next version 2017-01-20 11:40:00 +01:00
Daniel García Aubert
06885e2ba3 Release 2.87.4 2017-01-20 11:32:11 +01:00
Daniel
89a268d087 Merge pull request #617 from CartoDB/497-null-category
Be able to not compute NULL categories and null values
2017-01-20 11:26:12 +01:00
Daniel García Aubert
34424e713c Test on Node v6 2017-01-19 12:42:53 +01:00
Daniel García Aubert
89f381439f Pass test 2017-01-19 12:24:04 +01:00
Daniel García Aubert
6a80be9df3 Update windshaft dependency 2017-01-19 12:23:43 +01:00
Daniel García Aubert
fde1923acb Removed invalid selector 2017-01-19 11:31:55 +01:00
Daniel García Aubert
d486e1d34f Use released version of camshaft that supports node v6 2017-01-18 14:43:02 +01:00
csobier
3648b8b0b1 removed "default" from descriptions
As per a customer Support inquiry, I removed "default" from the Static Maps Limits section, as it was confusing to the user that it implied
2017-01-17 11:21:23 -05:00
Daniel García Aubert
83301238d2 Port changes to overviews 2017-01-17 17:10:08 +01:00
Daniel García Aubert
a4a1fb930a Be able to not compute NULL categories and null values wheter aggregation operation is not 'count' 2017-01-17 17:09:17 +01:00
Daniel García Aubert
6555353e0e Improve test to handle NULL values in category and aggregation columns using any operation 2017-01-16 19:23:08 +01:00
Daniel García Aubert
f5f0601e53 Add test to check if NULL category count values properly 2017-01-16 17:00:28 +01:00
Raul Ochoa
2598595e42 Merge pull request #614 from CartoDB/zipfile-cartodb
Use zipfile from cartodb
2016-12-23 14:47:40 +01:00
Raul Ochoa
49b78a85c9 Use zipfile from cartodb
Adding it as dependency in combination with loose definition in
millstone makes it to use the top level defined dependency.
2016-12-22 19:12:30 +01:00
Daniel García Aubert
35b12ebd6c Test unused directive just for mapnik 2.3.x 2016-12-19 17:29:24 +01:00
Javier Goizueta
0918c8e68c Stub next version 2016-12-19 17:20:18 +01:00
Javier Goizueta
1603a07de1 Release 2.87.3 2016-12-19 17:13:12 +01:00
Javier Goizueta
0a37aa4ba1 Merge pull request #604 from CartoDB/fix-overviews-dataviews
Fixes for dataviews using overviews
2016-12-19 16:55:13 +01:00
Javier Goizueta
b721a80fcc Merge branch 'master' into fix-overviews-dataviews 2016-12-19 16:45:28 +01:00
Rafa de la Torre
01365d035e Stub version 2.87.3 2016-12-19 16:38:27 +01:00
Rafa de la Torre
a4f059e20f Merge pull request #605 from CartoDB/update-camshaft-0.48.5
Update camshaft to 0.48.5
2016-12-19 16:33:27 +01:00
Daniel García Aubert
eb758bbf36 Use a valid port from env 2016-12-19 16:19:41 +01:00
Daniel García Aubert
bc2441e66a Use a valid port 2016-12-19 16:17:41 +01:00
Daniel García Aubert
7c1792bbd2 Test regression and unused directives only for mapnik ~2.3.x 2016-12-19 16:16:50 +01:00
Daniel García Aubert
2fdbc3e61c Fix error message in assertion 2016-12-19 16:15:07 +01:00
Daniel García Aubert
2ace705122 Fix error message in assertion 2016-12-19 16:13:55 +01:00
Daniel García Aubert
4b817062d8 Bump version of dependencies to be Node v6 compatible 2016-12-19 16:12:57 +01:00
Rafa de la Torre
79c35118d7 Update camshaft to 0.48.5
Use exception safe Dataservices API functions. See
https://github.com/CartoDB/dataservices-api/issues/314 and
https://github.com/CartoDB/camshaft/issues/242
2016-12-19 15:52:25 +01:00
Javier Goizueta
6a4f5d52ec Don't use overviews for date histograms 2016-12-16 17:51:36 +01:00
Javier Goizueta
ccaae2dd66 Remove spurious parameter from overviews dataviews functions
In the overviews-specialized dataview classes the sql-generating
functions had an unneeded parameter filters.
In some cases, since this parameter was not being paaased from
the base dataviews class it was masking the override parameter.
2016-12-16 17:37:05 +01:00
Raul Ochoa
d335e64f88 Stubs next version 2016-12-13 14:16:49 +01:00
Raul Ochoa
177d7ed07a Release 2.87.1 2016-12-13 14:15:50 +01:00
Raul Ochoa
85a1e15b58 Merge pull request #603 from CartoDB/upgrade-deps
Upgrade windshaft and request deps
2016-12-13 14:15:04 +01:00
Raul Ochoa
432b58a078 Upgrade windshaft and request deps 2016-12-13 14:10:42 +01:00
xavijam
75e3c5daef Ready for next release 2016-12-13 10:43:16 +01:00
Raul Ochoa
deb71c27b0 Merge pull request #601 from CartoDB/update-turbocarto
Update turbocarto package version
2016-12-12 18:56:54 +01:00
Raul Ochoa
8f5e1de6d8 Merge branch 'master' into update-turbocarto 2016-12-12 18:51:04 +01:00
Raul Ochoa
4836d62d7a Merge pull request #602 from CartoDB/travis-improvements
Travis improvements
2016-12-12 18:50:39 +01:00
Raul Ochoa
d27b0617b2 Remove comments 2016-12-12 18:44:25 +01:00
Raul Ochoa
28a2c29a39 Attempt to use postgis 2.3 2016-12-12 18:39:57 +01:00
Raul Ochoa
fcb6478407 Attempt to use postgis 2.2 2016-12-12 18:33:19 +01:00
Raul Ochoa
6d72afe40e Test sudoless travis 2016-12-12 17:30:14 +01:00
Raul Ochoa
e775266c64 Remove notifications 2016-12-12 17:29:32 +01:00
xavijam
12f25b38c0 Updated news 2016-12-12 17:03:56 +01:00
xavijam
c67a1107cb Package version updated with turbo-carto cahnges 2016-12-12 17:02:31 +01:00
xavijam
34bfb0d62c Updated news 2016-12-12 17:02:15 +01:00
Javier Goizueta
ede45cad1f Stub next version 2016-12-02 15:25:52 +01:00
Javier Goizueta
75fe4c8aed Release 2.86.1 2016-12-02 15:19:29 +01:00
Javier Goizueta
12e272a7e5 Merge pull request #599 from CartoDB/597-overviews-sql_wrap
Queries with sql_wrap should not be rewritten
2016-12-02 15:07:59 +01:00
Javier Goizueta
cfcba4e578 Wueries with sql_wrap should not be rewritten 2016-12-02 14:00:21 +01:00
Raul Ochoa
37ab898426 Stubs next version 2016-12-02 10:43:00 +01:00
Raul Ochoa
68865ea929 Release 2.86.0 2016-12-02 10:41:23 +01:00
Raul Ochoa
86674faa22 Update news and bump version 2016-12-02 10:41:02 +01:00
Raul Ochoa
f07947ce45 Merge pull request #597 from CartoDB/upgrade-windshaft
Upgrade windshaft
2016-12-02 10:32:44 +01:00
Raul Ochoa
6a50f59e25 Regenerate npm-shrinkwrap.json 2016-12-01 17:13:32 +01:00
Raul Ochoa
bfacd56800 Allow to use workers for transforming cartocss into mapnik XML 2016-12-01 17:02:40 +01:00
Raul Ochoa
45dea8b0c1 Stubs next version 2016-11-30 11:09:18 +01:00
Raul Ochoa
2f7f8cf2d8 Release 2.85.1 2016-11-30 11:07:29 +01:00
Raul Ochoa
31611b6a28 Upgrades camshaft to 0.48.4 2016-11-30 10:59:44 +01:00
Raul Ochoa
d1cd4b0c2b Stubs next version 2016-11-24 16:15:47 +01:00
Raul Ochoa
c8ba1c3e7c Release 2.85.0 2016-11-24 16:14:48 +01:00
Raul Ochoa
fbc8fe4c2d Update news and bump version 2016-11-24 16:11:08 +01:00
Raul Ochoa
54ec9b48db Ignore vscode settings dir 2016-11-24 15:41:09 +01:00
Raul Ochoa
488698d5e2 Merge pull request #594 from CartoDB/resources-url-templates
Allow to set resource URL templates with substitution tokens
2016-11-24 15:37:23 +01:00
Raul Ochoa
58c407aabb Alternative development example configuration for user in path instead of host 2016-11-24 12:57:45 +01:00
Raul Ochoa
fe750f23bc Closer to reality production example configuration 2016-11-24 12:56:38 +01:00
Raul Ochoa
87a01a5cfd Example configuration with just HTTP 2016-11-24 12:56:02 +01:00
Javier Goizueta
74dd669bb0 Stub next version 2016-11-23 15:29:50 +01:00
Javier Goizueta
36a50389f5 Release 2.84.2 2016-11-23 15:09:24 +01:00
Javier Goizueta
4f2d7434c7 Merge pull request #596 from CartoDB/upgrade-camshaft-to-0.48.3
Upgrade Camshaft version to 0.48.3
2016-11-23 15:05:43 +01:00
Javier Goizueta
b0a0848476 Upgrade Camshaft version to 0.48.3 2016-11-23 13:17:16 +01:00
Javier Goizueta
9fcd897e54 Stub next version 2016-11-23 11:57:05 +01:00
Javier Goizueta
daa8fff21e Release 2.84.1 2016-11-23 11:53:37 +01:00
Javier Goizueta
785229ddea Merge pull request #595 from CartoDB/upgrade-camshaft-to-0.48.2
Upgrade camshaft version to 0.48.2
2016-11-23 11:50:45 +01:00
Javier Goizueta
8bb11bf1d4 Upgrade camshaft version to 0.48.2 2016-11-23 11:36:38 +01:00
Raul Ochoa
1f975e15c1 Default to empty object for cdn URLs 2016-11-22 17:01:34 +01:00
Raul Ochoa
6c69ba54db Use actual CDN url for HTTP and HTTPS 2016-11-22 16:44:06 +01:00
Raul Ochoa
49f9904d00 Allow to set resource URL templates with substitution tokens 2016-11-22 16:41:31 +01:00
Raul Ochoa
7afd0dfa4e Remove prototype reference 2016-11-22 13:38:56 +01:00
Raul Ochoa
b1b6a437a7 Merge pull request #593 from CartoDB/592-doc-preview-layers
Fix DOC for named map  and preview layers
2016-11-22 12:52:24 +01:00
Daniel García Aubert
e4d5006591 Merge branch 'master' into 592-doc-preview-layers 2016-11-22 11:06:18 +01:00
Daniel García Aubert
627b3771d3 Fix doc for layer preview 2016-11-22 11:04:38 +01:00
Raul Ochoa
f4758e84e8 Stubs next version 2016-11-11 16:17:20 +01:00
Raul Ochoa
8dfe2098ed Release 2.84.0 2016-11-11 16:16:21 +01:00
Raul Ochoa
c56a4ee036 Update news and bump version 2016-11-11 16:15:59 +01:00
Raul Ochoa
c32623b821 Stubs next version 2016-11-11 16:15:08 +01:00
Raul Ochoa
3cd0a947f7 Merge pull request #586 from CartoDB/docs-switch-to-builder
switched gui tool from editor to builder
2016-11-11 12:52:38 +01:00
Raul Ochoa
8eea1cf4e7 Merge pull request #590 from CartoDB/analyses-limits-configuration
Analyses limit configuration allows to set other limits than timeout
2016-11-11 12:52:21 +01:00
Javier Goizueta
b5fccd5bbe Merge pull request #589 from CartoDB/upgrade-camshaft-to-0.48.1
Release 2.83.1
2016-11-10 18:48:14 +01:00
Raul Ochoa
e74ce9dfd8 Analyses limit configuration allows to set other limits than timeout
Configuration is now defined as a dictionary instead of just timeouts
per analysis type
2016-11-10 18:41:59 +01:00
Javier Goizueta
3743365a83 Release 2.83.1
Upgrade camshaft to 0.48.1
2016-11-10 18:16:42 +01:00
Javier Goizueta
8aeb2173d1 Release 2.83.0 2016-11-10 13:02:39 +01:00
Javier Goizueta
9a2b17d952 Merge pull request #588 from CartoDB/upgrade-camshaft-to-0.48.0
Upgrade camshaft to 0.48.0
2016-11-10 12:47:18 +01:00
Javier Goizueta
6f54cce01a Upgrade camshaft to 0.48.0
Upgrade camshaft to have analysis limits checking
2016-11-10 12:23:15 +01:00
Daniel García Aubert
6901b2049e Release 2.82.0 2016-11-08 18:48:01 +01:00
Daniel García Aubert
d0dcc027df Upgrade camshaft to version 0.47.0 2016-11-08 18:44:58 +01:00
Daniel García Aubert
b693005118 Stubs next version 2016-11-05 14:41:15 +01:00
Daniel García Aubert
ab4a0e836f Release 2.81.1 2016-11-05 14:36:49 +01:00
Daniel
abe02db6c6 Merge pull request #585 from CartoDB/fix-map-validator-basemap
Fix issues related to rollout
2016-11-05 14:35:06 +01:00
Daniel García Aubert
a2cd5dd32d Upgrade camshaft and windshaft 2016-11-05 14:12:03 +01:00
Daniel García Aubert
49b46a6096 Use address column in styles 2016-11-05 11:43:39 +01:00
csobier
94f420ca3f switched gui tool from editor to builder 2016-11-02 09:34:55 -04:00
Daniel García Aubert
5e530105df Stubs next version 2016-11-02 14:30:11 +01:00
Daniel García Aubert
2f82d34c4b Release 2.81.0 2016-11-02 10:36:19 +01:00
Daniel García Aubert
81fd01d0ac Upgrade camshaft to 0.46.2 2016-11-01 14:49:13 +01:00
Daniel García Aubert
9faac9f9fe Retrieve error with context if map validation fails 2016-11-01 11:00:58 +01:00
Daniel García Aubert
d04787a60c Fix test 2016-10-31 22:48:09 +01:00
Daniel García Aubert
f5dbf94b52 Stubs next version 2016-10-26 14:47:59 +02:00
Daniel García Aubert
5bec2d9b15 Release 2.80.2 2016-10-26 14:34:24 +02:00
Daniel García Aubert
fe64f0c63c Update NEWS 2016-10-26 14:30:56 +02:00
Daniel García Aubert
c20fd9691a Fix order in categories query to make it compatible with lenyends 2016-10-26 13:16:24 +02:00
Daniel García Aubert
eb323fbff9 Merge branch 'master' of github.com:CartoDB/Windshaft-cartodb 2016-10-25 16:28:34 +02:00
Daniel García Aubert
211f6b9a74 Stubs next version 2016-10-25 16:27:53 +02:00
Daniel García Aubert
b6c003ec63 Stubs next version 2016-10-25 16:15:03 +02:00
Daniel García Aubert
93d4bf2a72 Release 2.80.1 2016-10-25 16:02:32 +02:00
Daniel García Aubert
c6cb573383 Since crankshaft is installed by default in template-postgis we have to remove it before running test 2016-10-25 15:55:24 +02:00
Daniel García Aubert
f4ce671ea4 Upgrade camshaft version to 0.46.1 2016-10-25 14:54:52 +02:00
Raul Ochoa
147f7cbabb Stubs next version 2016-10-20 15:24:37 +02:00
Raul Ochoa
b05d5a141e Release 2.80.0 2016-10-20 15:23:17 +02:00
Raul Ochoa
d34e0306f8 Update news 2016-10-20 15:22:16 +02:00
Raul Ochoa
bd9f48dd24 Merge pull request #579 from CartoDB/upgrade-camshaft-to-0.46.0
Upgrade camshaft to 0.46.0
2016-10-20 15:20:14 +02:00
Javier Goizueta
9805990d79 Update npm-shrinkwrap 2016-10-20 15:11:53 +02:00
Raul Ochoa
dbbe60967c Bump version and update news 2016-10-20 15:04:46 +02:00
Raul Ochoa
0ef91c1904 Merge pull request #580 from CartoDB/analyses-config-limits
Default analyses limits can be defined in configuration
2016-10-20 15:02:44 +02:00
Raul Ochoa
376573459c Default analyses limits can be defined in configuration 2016-10-20 14:03:42 +02:00
Javier Goizueta
9c6d7c0ff9 Upgrade camshaft to 0.46.0
This version of camshaft requires a CDB_CheckAnalysisQuota function
to check analysis cache quota.
2016-10-20 12:56:18 +02:00
Raul Ochoa
30a95b7da3 Stubs next version 2016-10-11 16:56:22 +02:00
Raul Ochoa
e6a60aef9a Release 2.79.0 2016-10-11 16:55:15 +02:00
Raul Ochoa
5c2024581f Bump version 2016-10-11 16:55:03 +02:00
Raul Ochoa
f7ea2bb51e Merge pull request #578 from CartoDB/upgrade-turbo-carto
Upgrades turbo-carto to 0.18.0
2016-10-11 16:53:39 +02:00
Raul Ochoa
3e4da8ab57 Upgrades turbo-carto to 0.18.0 2016-10-11 16:41:26 +02:00
Raul Ochoa
7352a28908 Fix variable assigning to itself 2016-10-11 16:01:43 +02:00
Raul Ochoa
d1928ee578 Merge pull request #577 from CartoDB/analysis-limits-configuration
Retrieve analysis limits and pass them into camshaft
2016-10-11 16:00:07 +02:00
Raul Ochoa
cd978d7384 Retrieve analysis limits and pass them into camshaft 2016-10-11 15:46:11 +02:00
Raul Ochoa
cde0d8f5e2 Stubs next version 2016-09-30 18:26:33 +02:00
Raul Ochoa
7bacfcc2e4 Release 2.78.1 2016-09-30 18:25:00 +02:00
Raul Ochoa
241fe36103 Upgrades camshaft to 0.44.2 2016-09-30 18:24:44 +02:00
Raul Ochoa
441714a656 Stubs next version 2016-09-29 11:36:06 +02:00
Raul Ochoa
bd3fdb7f16 Release 2.78.0 2016-09-29 11:35:03 +02:00
Raul Ochoa
775af6feee Merge pull request #575 from CartoDB/cartocss-meta
Add metadata about processed turbo-carto rules
2016-09-28 19:44:52 +02:00
Raul Ochoa
adf5c17e0d Use context from adapters as provider context 2016-09-28 19:22:03 +02:00
Raul Ochoa
beb2d96a32 Upgrades turbo-carto to 0.17.1 2016-09-28 19:21:43 +02:00
Raul Ochoa
2a4ae88bc0 Merge remote-tracking branch 'origin/master' into cartocss-meta 2016-09-28 17:06:36 +02:00
Raul Ochoa
b76098ba45 Upgrades turbo-carto to 0.17.0 2016-09-28 16:57:04 +02:00
Raul Ochoa
c095027f8e Regenerate npm-shrinkwrap.json 2016-09-28 14:45:33 +02:00
Raul Ochoa
9d1db19907 Regenerate npm-shrinkwrap.json for turbo-carto dep 2016-09-28 11:39:58 +02:00
Daniel García Aubert
260e321537 Stub next version 2016-09-28 11:04:03 +02:00
Daniel García Aubert
073603b527 Release 2.77.1 2016-09-28 10:55:50 +02:00
Daniel García Aubert
17b259cf31 Upgrade camshaft to version 0.44.1 2016-09-28 10:54:27 +02:00
Daniel García Aubert
8f0f0026e9 Stubs next version 2016-09-27 12:28:44 +02:00
Daniel García Aubert
59dae2b545 Release 2.77.0 2016-09-26 19:11:12 +02:00
Daniel García Aubert
4670f69ead Upgrade camshaft to version 0.44.0 2016-09-26 19:05:16 +02:00
Daniel
16fbd25a34 Merge pull request #576 from CartoDB/conf-analysis-logger
Pass logger configuration to analysis backend and create a stream
2016-09-26 18:14:52 +02:00
Daniel García Aubert
2d75985cb3 Recreate stream on SIGHUP event 2016-09-26 17:39:27 +02:00
Daniel García Aubert
f963fb321e Set default config of analysis logger for test env 2016-09-23 17:35:46 +02:00
Daniel García Aubert
10feea0d48 Pass logger configuration to analysis backend and create a stream based on config 2016-09-23 17:11:04 +02:00
Raul Ochoa
b6b9b0ac36 Regenerate npm-shrinkwrap 2016-09-21 17:13:59 +02:00
Raul Ochoa
5551e85853 Update news and bump version 2016-09-21 16:06:43 +02:00
Raul Ochoa
1f0fa5031b Rely on turbo-carto metadata branch 2016-09-20 16:19:46 +02:00
Raul Ochoa
263294a3f5 Add metadata only for existing layers in turbocarto context 2016-09-20 16:19:22 +02:00
Raul Ochoa
f9df30f70b Append turbo-carto metadata per layer 2016-09-20 16:09:21 +02:00
Raul Ochoa
61d31ec054 Handle metadata from turbo-carto 2016-09-20 16:08:53 +02:00
Raul Ochoa
c8917bfc4c Return stats in callback 2016-09-20 16:00:33 +02:00
Raul Ochoa
36b69a05e5 Compute some stats in queries 2016-09-20 16:00:06 +02:00
Raul Ochoa
c8d2f66467 Stubs next version 2016-09-15 12:14:12 +02:00
Raul Ochoa
7416bb0e56 Release 2.76.0 2016-09-15 12:13:36 +02:00
Raul Ochoa
9182d0132d Merge pull request #574 from CartoDB/travis-addon
Travis' pg 9.5 addon
2016-09-15 11:17:51 +02:00
Raul Ochoa
9be9357ade Add libpango 2016-09-15 01:57:49 +02:00
Raul Ochoa
7f414f8adf Attempt to use travis' pg 9.5 addon 2016-09-15 01:51:36 +02:00
Raul Ochoa
3af9549939 Merge pull request #573 from CartoDB/external-config
Allow to use `--config /path/to/config.js` to specify configuration file
2016-09-15 01:49:43 +02:00
Raul Ochoa
41f248d731 Use logError 2016-09-15 01:35:38 +02:00
Raul Ochoa
18a517b7bf Allow to use --config /path/to/config.js to specify configuration file 2016-09-15 01:25:33 +02:00
Raul Ochoa
5150204389 Travis' Node.js 0.10 already uses npm 2.x 2016-09-14 21:22:40 +02:00
Raul Ochoa
3b16e7729d Merge pull request #572 from CartoDB/logs-conf
Allow to use absolute paths for log files
2016-09-14 21:04:19 +02:00
Raul Ochoa
76d27c9fce Allow to use absolute paths for log files
Fixes #570
2016-09-14 20:15:38 +02:00
Raul Ochoa
4ce6e41000 Stubs next version 2016-09-14 20:06:05 +02:00
Daniel García Aubert
33260cdbd9 Release 2.75.0 2016-09-14 09:48:20 +02:00
Raul Ochoa
85d81ba7fd Upgrades camshaft to 0.42.2 2016-09-09 13:40:31 +02:00
Raul Ochoa
7e8a3ca21f Stubs next version 2016-09-07 17:01:39 +02:00
Raul Ochoa
e9e4dc1f5c Release 2.74.1 2016-09-07 17:00:23 +02:00
Raul Ochoa
17c30e165a Upgrades camshaft to 0.42.1
Test fixture updated as it no longers generate bounds based on
table estimated bounds
2016-09-07 16:43:18 +02:00
Daniel García Aubert
c45c6ceb15 Stubs next version 2016-09-06 19:10:58 +02:00
Daniel García Aubert
d73c2c465f Release 2.74.0 2016-09-06 19:03:50 +02:00
Daniel
d4fc53939b Merge pull request #568 from CartoDB/567-show-hide-named-maps
Show & hide support in named maps
2016-09-06 18:53:23 +02:00
Daniel García Aubert
4becb65bec Stubs next version 2016-09-06 16:33:21 +02:00
Daniel García Aubert
f64e16c790 Release 2.73.1 2016-09-06 16:27:12 +02:00
Daniel García Aubert
1772011627 Add missinng column in fixture table cdb_analysis_catalog 2016-09-06 16:19:06 +02:00
Raul Ochoa
5b8f785e2b Regenerate npm-shrinkwrap.json 2016-09-06 16:01:05 +02:00
Daniel García Aubert
a0e3b77006 Release 2.73.0 2016-09-06 15:51:03 +02:00
Daniel García Aubert
908070ecd7 Fix jshint issue 2016-09-01 16:53:10 +02:00
Daniel García Aubert
7c6a58cd30 Made explicit to recreate named-map-provider 2016-09-01 16:42:06 +02:00
Daniel García Aubert
b0990a1132 Removed template-maps backend from named-maps controller 2016-09-01 16:31:30 +02:00
Daniel García Aubert
c6988cdb88 Improved and implemented new test for layer visibility suite 2016-09-01 12:37:56 +02:00
Daniel García Aubert
e0d304b033 Applied approach similar to static image options 2016-09-01 12:37:04 +02:00
Daniel García Aubert
e4a9f2d64c Added template map as dependency of named maps controller 2016-09-01 12:35:35 +02:00
Daniel García Aubert
0236fe3ca9 Implemented new scenario 2016-08-31 20:41:07 +02:00
Daniel García Aubert
1bed8623a2 Overwrites rendererparams instead of layergroup in mapconfig 2016-08-31 20:40:55 +02:00
Daniel García Aubert
df7d957914 Implemented acceptance test for layer visibility in previews 2016-08-31 19:41:23 +02:00
Daniel García Aubert
30c4b00f33 New approach: decorates named_map_provider to return the with visible layers 2016-08-31 19:40:00 +02:00
Daniel García Aubert
ab27886460 Renamed option by for name maps template 2016-08-31 19:37:48 +02:00
Daniel García Aubert
31e18d04d7 Undo unneeded optimization 2016-08-31 13:57:28 +02:00
Daniel García Aubert
8155484510 Improved test visibility layer with layerId 2016-08-29 10:46:55 +02:00
Daniel García Aubert
b61f1d2b53 Attached layer_visibility property to the named template 2016-08-26 17:30:03 +02:00
Daniel García Aubert
2e274b936a Improved test to check all possible values of visibility in named maps templates 2016-08-26 15:07:06 +02:00
Daniel García Aubert
bf3e311b57 Avoid unnecessary complexity 2016-08-26 14:46:23 +02:00
Daniel García Aubert
6a7613de6b Improved layergroup instantiation to filter non visible layers 2016-08-26 14:42:33 +02:00
Daniel García Aubert
ee46549e04 First approach 2016-08-25 20:04:23 +02:00
Raul Ochoa
377f3d4aff Removes constraint on filtered mapnik layers 2016-08-25 18:34:19 +02:00
Daniel García Aubert
752d47d71e Stubs next version 2016-08-23 11:25:39 +02:00
Daniel García Aubert
367157b80c Release 2.72.0 2016-08-23 11:19:48 +02:00
Daniel García Aubert
53542f1cd6 Stubs next version 2016-08-17 15:15:18 +02:00
Daniel García Aubert
7a8f156abf Release 2.71.0 2016-08-17 15:12:42 +02:00
Daniel García Aubert
c60cc57a0d Upgrades Windshaft to version 2.5.0 2016-08-17 15:07:29 +02:00
Raul Ochoa
8de6ec9a21 Stubs next version 2016-08-16 12:25:12 +02:00
Raul Ochoa
44b6f4be7e Release 2.70.0 2016-08-16 12:24:23 +02:00
Raul Ochoa
280be1751c Merge pull request #565 from CartoDB/update-camshaft
Upgrade camshaft to 0.40.0
2016-08-16 12:19:25 +02:00
Raul Ochoa
701a73a2c5 Upgrade camshaft to 0.40.0 2016-08-16 11:33:32 +02:00
Raul Ochoa
b578eada07 Stubs next version 2016-08-12 10:34:33 +02:00
Raul Ochoa
8100f155dc Release 2.69.1 2016-08-12 10:33:10 +02:00
Raul Ochoa
9f1a014004 Upgrades windshaft to 2.4.2 2016-08-12 10:24:06 +02:00
Raul Ochoa
e35e0e157c Stubs next version 2016-08-11 12:06:33 +02:00
Raul Ochoa
3aff328af3 Release 2.69.0 2016-08-11 12:05:56 +02:00
Raul Ochoa
ffb086045a Bump version 2016-08-11 12:05:37 +02:00
Raul Ochoa
c0786dfa6f Merge pull request #564 from CartoDB/fix-travis
Avoid 'No such file or directory'
2016-08-10 12:59:29 +02:00
Raul Ochoa
ddc33fa52b Avoid 'No such file or directory' 2016-08-10 10:30:04 +02:00
Raul Ochoa
9f2d6a5d41 Merge pull request #563 from CartoDB/upgrade-camshaft
Upgrade camshaft to 0.39.0
2016-08-09 17:01:09 +02:00
Raul Ochoa
64e884a092 force-yes for postgresql-plpython-9.5 2016-08-09 15:45:21 +02:00
Raul Ochoa
17ec174683 Use --force-yes 2016-08-09 15:41:17 +02:00
Raul Ochoa
3666cbee94 Upgrade camshaft to 0.39.0 2016-08-09 15:29:54 +02:00
Raul Ochoa
25de018f7d Add comment about row_limit config parameter 2016-08-01 12:59:38 +02:00
Raul Ochoa
6597851b48 Stubs next version 2016-07-21 21:04:39 +02:00
Raul Ochoa
0399131968 Release 2.68.0 2016-07-21 21:03:10 +02:00
Raul Ochoa
86836e7f89 Upgrades turbo-carto to 0.16.0 2016-07-21 21:02:06 +02:00
Daniel García Aubert
df346b11d3 Release 2.67.1 2016-07-21 16:05:12 +02:00
Daniel García Aubert
27f74b3fe2 Stubs next version 2016-07-21 10:41:42 +02:00
Daniel García Aubert
87dec64ad1 Release 2.67.0 2016-07-21 10:34:10 +02:00
Daniel García Aubert
54c787162a Update NEWS 2016-07-21 10:27:46 +02:00
Daniel García Aubert
6e92e699dc Upgrades camshaft version to 0.38.0 2016-07-21 10:25:53 +02:00
Raul Ochoa
7950f43db3 Stubs next version 2016-07-20 19:38:09 +02:00
116 changed files with 8093 additions and 5040 deletions

6
.gitignore vendored
View File

@@ -2,10 +2,12 @@ node_modules*
config.status*
config/environments/*.js
.idea
.vscode
.nvmrc
tools/munin/windshaft.conf
logs/
pids/
redis.pid
test.log
npm-debug.log
*.log
coverage/
.DS_Store

View File

@@ -40,7 +40,7 @@
"debug" : false, // true: Allow debugger statements e.g. browser breakpoints.
// "eqnull" : false, // true: Tolerate use of `== null`
// "es5" : false, // true: Allow ES5 syntax (ex: getters and setters)
// "esnext" : false, // true: Allow ES.next (ES6) syntax (ex: `const`)
"esnext" : true, // true: Allow ES.next (ES6) syntax (ex: `const`)
// "moz" : false, // true: Allow Mozilla specific syntax (extends and overrides esnext features)
// // (ex: `for each`, multiple try/catch, function expression…)
// "evil" : false, // true: Tolerate use of `eval` and `new Function()`

View File

@@ -1,37 +1,27 @@
dist: trusty
addons:
postgresql: "9.5"
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- postgresql-9.5-postgis-2.3
- postgresql-plpython-9.5
- pkg-config
- libcairo2-dev
- libjpeg8-dev
- libgif-dev
- libpango1.0-dev
- g++-4.9
before_install:
- npm install -g npm@2
- lsb_release -a
- sudo mv /etc/apt/sources.list.d/pgdg-source.list* /tmp
- sudo apt-get -qq purge postgis* postgresql*
- sudo rm -Rf /var/lib/postgresql /etc/postgresql
- sudo apt-add-repository --yes ppa:cartodb/postgresql-9.5
- sudo apt-add-repository --yes ppa:cartodb/gis
- sudo apt-get update
- sudo apt-get install -q postgresql-9.5-postgis-2.2
- sudo apt-get install -q postgresql-plpython-9.5
- echo -e "local\tall\tall\ttrust\nhost\tall\tall\t127.0.0.1/32\ttrust\nhost\tall\tall\t::1/128\ttrust" |sudo tee /etc/postgresql/9.5/main/pg_hba.conf
- sudo service postgresql restart
- createdb template_postgis
- createuser publicuser
- psql -c "CREATE EXTENSION postgis" template_postgis
env:
- NPROCS=1 JOBS=1 PGUSER=postgres
- NPROCS=1 JOBS=1 PGUSER=postgres CXX=g++-4.9
language: node_js
node_js:
- "0.10"
notifications:
irc:
channels:
- "irc.freenode.org#cartodb"
use_notice: true
- "6"

View File

@@ -8,4 +8,4 @@ We love pull requests from everyone, see [Contributing to Open Source on GitHub]
## Submitting Contributions
* You will need to sign a Contributor License Agreement (CLA) before making a submission. [Learn more here](https://cartodb.com/contributing).
* You will need to sign a Contributor License Agreement (CLA) before making a submission. [Learn more here](https://carto.com/contributions).

View File

@@ -1,11 +1,10 @@
1. Test (make clean all check), fix if broken before proceeding
2. Ensure proper version in package.json
2. Ensure proper version in package.json
3. Ensure NEWS section exists for the new version, review it, add release date
4. Recreate npm-shrinkwrap.json with: `npm install --no-shrinkwrap && npm shrinkwrap`
5. Commit package.json, npm-shrinwrap.json, NEWS
4. If there are modified dependencies in package.json, update them with `yarn upgrade {{package_name}}@{{version}}`
5. Commit package.json, yarn.lock, NEWS
6. git tag -a Major.Minor.Patch # use NEWS section as content
7. Announce on cartodb@googlegroups.com
8. Stub NEWS/package for next version
7. Stub NEWS/package for next version
Versions:

View File

@@ -4,11 +4,11 @@
Make sure that you have the requirements needed. These are
- Core
- Node.js >=0.8
- npm >=1.2.1 <2.0.0
- Node.js >=6.9.x
- yarn >=0.21.3
- PostgreSQL >8.3.x, PostGIS >1.5.x
- Redis >2.4.0 (http://www.redis.io)
- Mapnik 2.0.1, 2.0.2, 2.1.0, 2.2.0, 2.3.0. See [Installing Mapnik](https://github.com/CartoDB/Windshaft#installing-mapnik).
- Mapnik >3.x. See [Installing Mapnik](https://github.com/CartoDB/Windshaft#installing-mapnik).
- Windshaft: check [Windshaft dependencies and installation notes](https://github.com/CartoDB/Windshaft#dependencies)
- libcairo2-dev, libpango1.0-dev, libjpeg8-dev and libgif-dev for server side canvas support
@@ -43,11 +43,11 @@ psql -d template_postgis -c 'CREATE EXTENSION postgis;'
To fetch and build all node-based dependencies, run:
```
npm install
yarn
```
Note that the ```npm install``` step will populate the node_modules/
Note that the ```yarn``` step will populate the node_modules/
directory with modules, some of which being compiled on demand. If you
happen to have startup errors you may need to force rebuilding those
modules. At any time just wipe out the node_modules/ directory and run
```npm install``` again.
```yarn``` again.

View File

@@ -7,7 +7,7 @@ all:
@$(SHELL) ./scripts/install.sh
clean:
rm -rf node_modules/*
rm -rf node_modules/
distclean: clean
rm config.status*

583
NEWS.md
View File

@@ -1,5 +1,588 @@
# Changelog
## 3.9.8
Released 2017-07-21
- Upgrades windshaft to [3.2.2](https://github.com/CartoDB/windshaft/releases/tag/3.2.2).
## 3.9.7
Released 2017-07-20
Bug fixes:
- Respond with 204 (No content) when vector tile has no data #712
Announcements:
- Upgrades turbo-carto to [0.19.2](https://github.com/CartoDB/turbo-carto/releases/tag/0.19.2)
## 3.9.6
Released 2017-07-11
- Dataviews: support for aggregation in search results #708
## 3.9.5
Released 2017-06-27
- Dataviews: support special numeric values (±Infinity, NaN) #700
## 3.9.4
Released 2017-06-22
Announcements:
- Upgrades camshaft to [0.55.6](https://github.com/CartoDB/camshaft/releases/tag/0.55.6).
## 3.9.3
Released 2017-06-16
Announcements:
- Upgrades camshaft to [0.55.5](https://github.com/CartoDB/camshaft/releases/tag/0.55.5).
## 3.9.2
Released 2017-06-16
Announcements:
- Upgrades camshaft to [0.55.4](https://github.com/CartoDB/camshaft/releases/tag/0.55.4).
## 3.9.1
Released 2017-06-06
Announcements:
- Upgrades camshaft to [0.55.3](https://github.com/CartoDB/camshaft/releases/tag/0.55.3).
## 3.9.0
Released 2017-05-31
Announcements:
- Upgrades windshaft to [3.2.1](https://github.com/CartoDB/windshaft/releases/tag/3.2.1).
- Add support to retrieve info about layer stats in map instantiation.
- Upgrades camshaft to [0.55.2](https://github.com/CartoDB/camshaft/releases/tag/0.55.2).
- Remove promise polyfill from turbo-carto adapter
## 3.8.0
Released 2017-05-22
Announcements:
- Upgrades camshaft to [0.55.0](https://github.com/CartoDB/camshaft/releases/tag/0.55.0).
- Upgrades turbo-carto to [0.19.1](https://github.com/CartoDB/turbo-carto/releases/tag/0.19.1)
## 3.7.1
Released 2017-05-18
Bug fixes:
- Fix buffersize assignment when is not defined in requested mapconfig.
## 3.7.0
Released 2017-05-18
Announcements:
- Manage multiple values of buffer-size for different formats
- Upgrades windshaft to [3.2.0](https://github.com/CartoDB/windshaft/releases/tag/3.2.0).
## 3.6.6
Released 2017-05-11
Announcements:
- Upgrades camshaft to [0.54.4](https://github.com/CartoDB/camshaft/releases/tag/0.54.4).
## 3.6.5
Released 2017-05-09
Announcements:
- Upgrades camshaft to [0.54.3](https://github.com/CartoDB/camshaft/releases/tag/0.54.3).
## 3.6.4
Released 2017-05-05
Announcements:
- Upgrade cartodb-psql to [0.8.0](https://github.com/CartoDB/node-cartodb-psql/releases/tag/0.8.0).
- Upgrades camshaft to [0.54.2](https://github.com/CartoDB/camshaft/releases/tag/0.54.2).
- Upgrades windshaft to [3.1.2](https://github.com/CartoDB/windshaft/releases/tag/3.1.2).
## 3.6.3
Released 2017-04-25
Announcements:
- Upgrades windshaft to [3.1.1](https://github.com/CartoDB/windshaft/releases/tag/3.1.1).
## 3.6.2
Released 2017-04-24
Announcements:
- Upgrades grainstore to [1.6.3](https://github.com/CartoDB/grainstore/releases/tag/1.6.3).
## 3.6.1
Released 2017-04-24
Announcements:
- Upgrades camshaft to [0.54.1](https://github.com/CartoDB/camshaft/releases/tag/0.54.1).
## 3.6.0
Released 2017-04-20
Announcements:
- Upgrades camshaft to [0.54.0](https://github.com/CartoDB/camshaft/releases/tag/0.54.0).
## 3.5.1
Released 2017-04-11
Announcements:
- Upgrades camshaft to [0.53.1](https://github.com/CartoDB/camshaft/releases/tag/0.53.1).
## 3.5.0
Released 2017-04-10
Bug fixes:
- Fix invalidation of cache for maps with analyses #638.
Announcements:
- Upgrades camshaft to [0.53.0](https://github.com/CartoDB/camshaft/releases/tag/0.53.0).
## 3.4.0
Released 2017-04-03
Announcements:
- Upgrades camshaft to [0.51.0](https://github.com/CartoDB/camshaft/releases/tag/0.51.0).
## 3.3.0
Released 2017-04-03
New features:
- Static map endpoints allow specifying the layers to render #653.
## 3.2.0
Released 2017-03-30
Announcements:
- Upgrades windshaft to [3.1.0](https://github.com/CartoDB/windshaft/releases/tag/3.1.0).
- Active GC interval.
## 3.1.1
Released 2017-03-23
Bug fixes:
- Use crc32 instead of md5 for computing subdomain candidate #642
## 3.1.0
Released 2017-03-22
Features:
- Generate URLs for resources based on CDN and template rules
## 3.0.2
Released 2017-03-22
Bug fixes:
- Upgrade dependencies
- Improve docs: remove mentions to NPM and use yarn instead
- Remove script to generate npm-shrinkwrap file
## 3.0.1
Released 2017-03-21
Announcements:
- Upgrades windshaft to [3.0.1](https://github.com/CartoDB/windshaft/releases/tag/3.0.1).
## 3.0.0
Released 2017-03-21
Announcements:
- Supports Node v6.9.x
- Drops support for Node v0.10.x
- Upgrades windshaft to 3.0.0
- Upgrades cartodb-query-tables to 0.2.0
- Upgrades cartodb-redis to 0.13.2
- Upgrades redis-mpool to 0.4.1
**Note**: Due to this [issue](https://github.com/npm/npm/issues/15713), Windshaft-cartodb must be installed with `yarn` instead of `npm` providing just a `yarn.lock` to get consistent installs across machines.
## 2.89.0
Released 2017-03-17
**Deprecation warning**: v2.89.0 is the last release that supports Node v0.10.x. Next mayor release will support Node v6.9.x and further versions.
Announcements:
- Upgrades windshaft to [2.8.0](https://github.com/CartoDB/windshaft/releases/tag/2.8.0).
Bug fixes:
- Histogram column type discovery query uses non-filtered query #637
## 2.88.4
Released 2017-03-10
Announcements:
- Upgrades camshaft to [0.50.3](https://github.com/CartoDB/camshaft/releases/tag/0.50.3).
## 2.88.3
Released 2017-03-02
Bug fixes:
- Category dataviews now uses the proper aggregation function for the 'Other' category. See https://github.com/CartoDB/Windshaft-cartodb/issues/628
## 2.88.2
Released 2017-02-23
Announcements:
- Upgrades camshaft to [0.50.2](https://github.com/CartoDB/camshaft/releases/tag/0.50.2).
## 2.88.1
Released 2017-02-21
Announcements:
- Upgrades camshaft to [0.50.1](https://github.com/CartoDB/camshaft/releases/tag/0.50.1)
## 2.88.0
Released 2017-02-21
Announcements:
- Upgrades camshaft to [0.50.0](https://github.com/CartoDB/camshaft/releases/tag/0.50.0).
- Upgrades cartodb-psql to [0.7.1](https://github.com/CartoDB/node-cartodb-psql/releases/tag/0.7.1).
- Upgrades windshaft to [2.7.0](https://github.com/CartoDB/windshaft/releases/tag/2.7.0).
## 2.87.5
Released 2017-02-02
Bug fixes:
- Cast dataview override values to Number or throw error.
## 2.87.4
Released 2017-01-20
Bug fixes:
- Be able to not compute NULL categories and null values in aggregation dataviews #617.
## 2.87.3
Released 2016-12-19
Bug fixes:
- Fix overviews-related dataviews problems. See https://github.com/CartoDB/Windshaft-cartodb/pull/604
## 2.87.2
Released 2016-12-19
- Use exception safe Dataservices API functions. See https://github.com/CartoDB/dataservices-api/issues/314 and https://github.com/CartoDB/camshaft/issues/242
## 2.87.1
Released 2016-12-13
Announcements:
- Upgrades windshaft to [2.6.4](https://github.com/CartoDB/Windshaft/releases/tag/2.6.4).
- Upgrades request dependency.
- Regenerate npm-shrinkwrap.json: missing dependency updates.
## 2.87.0
Released 2016-12-12
Enhancements:
- Upgrade turbo-carto dependency to version 0.19.0.
## 2.86.1
Released 2016-12-02
Bug fixes:
- Maps with analyses and `sql_wrap` were broken #599.
## 2.86.0
Released 2016-12-02
Announcements:
- Upgrades windshaft to [2.6.3](https://github.com/CartoDB/Windshaft/releases/tag/2.6.3).
## 2.85.1
Released 2016-11-30
Announcements:
- Upgrades camshaft to [0.48.4](https://github.com/CartoDB/camshaft/releases/tag/0.48.4).
## 2.85.0
Released 2016-11-24
New features:
- Allow to set resource URL templates with substitution tokens #594.
## 2.84.2
Released 2016-11-23
Announcements:
- Upgrades camshaft to [0.48.3](https://github.com/CartoDB/camshaft/releases/tag/0.48.3).
## 2.84.1
Released 2016-11-23
Announcements:
- Upgrades camshaft to [0.48.2](https://github.com/CartoDB/camshaft/releases/tag/0.48.2).
## 2.84.0
Released 2016-11-11
New features:
- Analyses limit configuration allows to set other limits than timeout.
## 2.83.1
Released 2016-11-10
Announcements:
- Upgrades camshaft to [0.48.1](https://github.com/CartoDB/camshaft/releases/tag/0.48.1).
## 2.83.0
Released 2016-11-10
Announcements:
- Upgrades camshaft to [0.48.0](https://github.com/CartoDB/camshaft/releases/tag/0.48.0).
## 2.82.0
Released 2016-11-08
Announcements:
- Upgrades camshaft to [0.47.0](https://github.com/CartoDB/camshaft/releases/tag/0.47.0).
## 2.81.1
Released 2016-11-05
Announcements:
- Upgrades windshaft to [2.6.2](https://github.com/CartoDB/windshaft/releases/tag/2.6.2).
- Upgrades camshaft to [0.46.3](https://github.com/CartoDB/camshaft/releases/tag/0.46.3).
## 2.81.0
Released 2016-11-02
Enhancements:
- Returns errors with context when query layer does not retrieve geometry column
Announcements:
- Upgrades windshaft to [2.6.1](https://github.com/CartoDB/windshaft/releases/tag/2.6.1).
- Upgrades camshaft to [0.46.2](https://github.com/CartoDB/camshaft/releases/tag/0.46.2).
## 2.80.2
Released 2016-10-26
Bug fixes:
- Fix order in categories query to get ramps
## 2.80.1
Released 2016-10-25
Announcements:
- Upgrades camshaft to [0.46.1](https://github.com/CartoDB/camshaft/releases/tag/0.46.1).
## 2.80.0
Released 2016-10-20
Announcements:
- Upgrades camshaft to [0.46.0](https://github.com/CartoDB/camshaft/releases/tag/0.46.0).
New features:
- Default analyses limits can be defined in configuration.
## 2.79.0
Released 2016-10-11
New features:
- Retrieve analysis limits and pass them into camshaft.
Announcements:
- Upgrades turbo-carto to [0.18.0](https://github.com/CartoDB/turbo-carto/releases/tag/0.18.0).
- Upgrades camshaft to [0.45.0](https://github.com/CartoDB/camshaft/releases/tag/0.45.0).
## 2.78.1
Released 2016-09-30
Announcements:
- Upgrades camshaft to [0.44.2](https://github.com/CartoDB/camshaft/releases/tag/0.44.2).
## 2.78.0
Released 2016-09-29
New features:
- Add metadata about processed turbo-carto rules.
Announcements:
- Upgrades turbo-carto to [0.17.1](https://github.com/CartoDB/turbo-carto/releases/tag/0.17.1).
## 2.77.1
Released 2016-09-28
Announcements:
- Upgrades camshaft to [0.44.1](https://github.com/CartoDB/camshaft/releases/tag/0.44.1).
## 2.77.0
Released 2016-09-26
Announcements:
- Upgrades camshaft to [0.44.0](https://github.com/CartoDB/camshaft/releases/tag/0.44.0).
- Adds a new configuration for camshaft: logger stream.
## 2.76.0
Released 2016-09-15
New features:
- Allow to use `--config /path/to/config.js` to specify configuration file.
- Environment will be loaded from config file if `environment` key is present, otherwise it keeps current behaviour.
Bug fixes:
- Allow to use absolute paths for log files #570.
## 2.75.0
Released 2016-09-14
Announcements:
- Upgrades camshaft to [0.43.0](https://github.com/CartoDB/camshaft/releases/tag/0.43.0).
## 2.74.1
Released 2016-09-07
Announcements:
- Upgrades camshaft to [0.42.1](https://github.com/CartoDB/camshaft/releases/tag/0.42.1).
## 2.74.0
Released 2016-09-06
Enhancements:
- Layers in previews can be shown or hidden using `preview_layers` property in template map
## 2.73.1
Released 2016-09-06
Bug fixes:
- Fixes missing column in fixture table `cdb_analysis_catalog`.
## 2.73.0
Released 2016-09-06
Announcements:
- Upgrades camshaft to [0.42.0](https://github.com/CartoDB/camshaft/releases/tag/0.42.0).
## 2.72.0
Released 2016-08-23
Announcements:
- Upgrades camshaft to [0.41.0](https://github.com/CartoDB/camshaft/releases/tag/0.41.0).
## 2.71.0
Released 2016-08-17
Announcements:
- Upgrades windshaft to [2.5.0](https://github.com/CartoDB/windshaft/releases/tag/2.5.0).
## 2.70.0
Released 2016-08-16
Announcements:
- Upgrades camshaft to [0.40.0](https://github.com/CartoDB/camshaft/releases/tag/0.40.0).
## 2.69.1
Released 2016-08-12
Announcements:
- Upgrades windshaft to [2.4.2](https://github.com/CartoDB/windshaft/releases/tag/2.4.2).
## 2.69.0
Released 2016-08-11
Announcements:
- Upgrades camshaft to [0.39.0](https://github.com/CartoDB/camshaft/releases/tag/0.39.0).
## 2.68.0
Released 2016-07-21
Announcements:
- Upgrades turbo-carto to [0.16.0](https://github.com/CartoDB/turbo-carto/releases/tag/0.16.0).
## 2.67.1
Released 2016-07-21
Announcements:
- Upgrades camshaft to [0.38.1](https://github.com/CartoDB/camshaft/releases/tag/0.38.1).
## 2.67.0
Released 2016-07-21
Announcements:
- Upgrades camshaft to [0.38.0](https://github.com/CartoDB/camshaft/releases/tag/0.38.0).
## 2.66.2
Released 2016-07-20

View File

@@ -32,14 +32,14 @@ Upgrading
Checkout your commit/branch. If you need to reinstall dependencies (you can check [NEWS](NEWS.md)) do the following:
```
rm -rf node_modules; npm install
rm -rf node_modules; yarn
```
Run
---
```
node app.js <env>
node app.js <env>
```
Where <env> is the name of a configuration file under config/environments/.
@@ -71,12 +71,12 @@ See [CONTRIBUTING.md](CONTRIBUTING.md).
### Developing with a custom windshaft version
If you plan or want to use a custom / not released yet version of windshaft (or any other dependency) the best option is
to use `npm link`. You can read more about it at [npm-link: Symlink a package folder](https://docs.npmjs.com/cli/link).
to use `yarn link`. You can read more about it at [yarn-link: Symlink a package folder](https://yarnpkg.com/en/docs/cli/link).
**Quick start**:
```shell
~/windshaft-directory $ npm install
~/windshaft-directory $ npm link
~/windshaft-cartodb-directory $ npm link windshaft
~/windshaft-directory $ yarn
~/windshaft-directory $ yarn link
~/windshaft-cartodb-directory $ yarn link windshaft
```

97
app.js
View File

@@ -5,20 +5,35 @@ var fs = require('fs');
var _ = require('underscore');
var ENVIRONMENT;
if ( process.argv[2] ) {
ENVIRONMENT = process.argv[2];
} else if ( process.env.NODE_ENV ) {
ENVIRONMENT = process.env.NODE_ENV;
} else {
ENVIRONMENT = 'development';
}
// jshint undef:false
var log = console.log.bind(console);
var logError = console.error.bind(console);
// jshint undef:true
var argv = require('yargs')
.usage('Usage: $0 <environment> [options]')
.help('h')
.example(
'$0 production -c /etc/sql-api/config.js',
'start server in production environment with /etc/sql-api/config.js as config file'
)
.alias('h', 'help')
.alias('c', 'config')
.nargs('c', 1)
.describe('c', 'Load configuration from path')
.argv;
var environmentArg = argv._[0] || process.env.NODE_ENV || 'development';
var configurationFile = path.resolve(argv.config || './config/environments/' + environmentArg + '.js');
if (!fs.existsSync(configurationFile)) {
logError('Configuration file "%s" does not exist', configurationFile);
process.exit(1);
}
global.environment = require(configurationFile);
var ENVIRONMENT = argv._[0] || process.env.NODE_ENV || global.environment.environment;
process.env.NODE_ENV = ENVIRONMENT;
var availableEnvironments = {
production: true,
staging: true,
@@ -33,16 +48,6 @@ if (!availableEnvironments[ENVIRONMENT]){
}
process.env.NODE_ENV = ENVIRONMENT;
// set environment specific variables
global.environment = require('./config/environments/' + ENVIRONMENT);
global.log4js = require('log4js');
var log4js_config = {
appenders: [],
replaceConsole: true
};
if (global.environment.uv_threadpool_size) {
process.env.UV_THREADPOOL_SIZE = global.environment.uv_threadpool_size;
}
@@ -58,25 +63,31 @@ var agentOptions = _.defaults(global.environment.httpAgent || {}, {
http.globalAgent = new http.Agent(agentOptions);
https.globalAgent = new https.Agent(agentOptions);
global.log4js = require('log4js');
var log4jsConfig = {
appenders: [],
replaceConsole: true
};
if ( global.environment.log_filename ) {
var logdir = path.dirname(global.environment.log_filename);
// See cwd inlog4js.configure call below
logdir = path.resolve(__dirname, logdir);
if ( ! fs.existsSync(logdir) ) {
logError("Log filename directory does not exist: " + logdir);
process.exit(1);
}
log("Logs will be written to " + global.environment.log_filename);
log4js_config.appenders.push(
{ type: "file", filename: global.environment.log_filename }
);
var logFilename = path.resolve(global.environment.log_filename);
var logDirectory = path.dirname(logFilename);
if (!fs.existsSync(logDirectory)) {
logError("Log filename directory does not exist: " + logDirectory);
process.exit(1);
}
log("Logs will be written to " + logFilename);
log4jsConfig.appenders.push(
{ type: "file", absolute: true, filename: logFilename }
);
} else {
log4js_config.appenders.push(
{ type: "console", layout: { type:'basic' } }
);
log4jsConfig.appenders.push(
{ type: "console", layout: { type:'basic' } }
);
}
global.log4js.configure(log4js_config, { cwd: __dirname });
global.log4js.configure(log4jsConfig);
global.logger = global.log4js.getLogger();
global.environment.api_hostname = require('os').hostname().split('.')[0];
@@ -99,6 +110,8 @@ var listener = server.listen(serverOptions.bind.port, serverOptions.bind.host, b
var version = require("./package").version;
listener.on('listening', function() {
log("Using Node.js %s", process.version);
log('Using configuration file "%s"', configurationFile);
log(
"Windshaft tileserver %s started on %s:%s PID=%d (%s)",
version, serverOptions.bind.host, serverOptions.bind.port, process.pid, ENVIRONMENT
@@ -114,7 +127,7 @@ setInterval(function() {
process.on('SIGHUP', function() {
global.log4js.clearAndShutdownAppenders(function() {
global.log4js.configure(log4js_config);
global.log4js.configure(log4jsConfig);
global.logger = global.log4js.getLogger();
log('Log files reloaded');
});
@@ -123,3 +136,17 @@ process.on('SIGHUP', function() {
process.on('uncaughtException', function(err) {
global.logger.error('Uncaught exception: ' + err.stack);
});
if (global.gc) {
var gcInterval = Number.isFinite(global.environment.gc_interval) ?
global.environment.gc_interval :
10000;
if (gcInterval > 0) {
setInterval(function gcForcedCycle() {
var start = Date.now();
global.gc();
global.statsClient.timing('windshaft.gc', Date.now() - start);
}, gcInterval);
}
}

View File

@@ -6,6 +6,9 @@ var config = {
// Its default size is 4, but it can be changed at startup time (the absolute maximum is 128).
// See http://docs.libuv.org/en/latest/threadpool.html
,uv_threadpool_size: undefined
// Time in milliseconds to force GC cycle.
// Disable by using <=0 value.
,gc_interval: 10000
// Regular expression pattern to extract username
// from hostname. Must have a single grabbing block.
,user_from_host: '^(.*)\\.localhost'
@@ -23,6 +26,21 @@ var config = {
// "tiles/layergroup" is for compatibility with versions up to 1.6.x
,base_url_detached: '(?:/api/v1/map|/user/:user/api/v1/map|/tiles/layergroup)'
// Resource URLs expose endpoints to request/retrieve metadata associated to Maps: dataviews, analysis node status.
//
// This URLs depend on how `base_url_detached` and `user_from_host` are configured: the application can be
// configured to accept request with the {user} in the header host or in the request path.
// It also might depend on the configured cdn_url via `serverMetadata.cdn_url`.
//
// This template allows to make the endpoints generation more flexible, the template exposes the following params:
// 1. {{=it.cdn_url}}: will be used when `serverMetadata.cdn_url` exists.
// 2. {{=it.user}}: will use the username as extraced from `user_from_host` or `base_url_detached`.
// 3. {{=it.port}}: will use the `port` from this very same configuration file.
,resources_url_templates: {
http: 'http://{{=it.user}}.localhost.lan:{{=it.port}}/api/v1/map',
https: 'http://localhost.lan:{{=it.port}}/user/{{=it.user}}/api/v1/map'
}
// Maximum number of connections for one process
// 128 is a good value with a limit of 1024 open file descriptors
,maxConnections:128
@@ -62,6 +80,7 @@ var config = {
extent: "-180,-90,180,90",
srid: 4326,
*/
// max number of rows to return when querying data, 0 means no limit
row_limit: 65535,
simplify_geometries: true,
use_overviews: true, // use overviews to retrieve raster
@@ -95,6 +114,10 @@ var config = {
// Important: check the configuration of uv_threadpool_size to use suitable value
poolSize: 8,
// Whether grainstore will use a child process or not to transform CartoCSS into Mapnik XML.
// This will prevent blocking the main thread.
useCartocssWorkers: false,
// Metatile is the number of tiles-per-side that are going
// to be rendered at once. If all of them will be requested
// we'd have saved time. If only one will be used, we'd have
@@ -209,6 +232,18 @@ var config = {
endpoint: 'http://127.0.0.1:8080/api/v2/sql/job',
// the template to use for adding the host header in the batch api requests
hostHeaderTemplate: '{{=it.username}}.localhost.lan'
},
logger: {
// If filename is given logs comming from analysis client will be written
// there, in append mode. Otherwise 'log_filename' is used. Otherwise stdout is used (default).
// Log file will be re-opened on receiving the HUP signal
filename: '/tmp/analysis.log'
},
// Define max execution time in ms for analyses or tags
// If analysis or tag are not found in redis this values will be used as default.
limits: {
moran: { timeout: 120000, maxNumberOfRows: 1e5 },
cpu2x: { timeout: 60000 }
}
}
,millstone: {
@@ -289,8 +324,7 @@ var config = {
// whether the affected tables for a given SQL must query directly postgresql or use the SQL API
cdbQueryTablesFromPostgres: true,
// whether in mapconfig is available stats & metadata for each layer
layerMetadata: true
layerStats: true
}
};

View File

@@ -6,6 +6,9 @@ var config = {
// Its default size is 4, but it can be changed at startup time (the absolute maximum is 128).
// See http://docs.libuv.org/en/latest/threadpool.html
,uv_threadpool_size: undefined
// Time in milliseconds to force GC cycle.
// Disable by using <=0 value.
,gc_interval: 10000
// Regular expression pattern to extract username
// from hostname. Must have a single grabbing block.
,user_from_host: '^(.*)\\.cartodb\\.com$'
@@ -23,6 +26,21 @@ var config = {
// "tiles/layergroup" is for compatibility with versions up to 1.6.x
,base_url_detached: '(?:/api/v1/map|/user/:user/api/v1/map|/tiles/layergroup)'
// Resource URLs expose endpoints to request/retrieve metadata associated to Maps: dataviews, analysis node status.
//
// This URLs depend on how `base_url_detached` and `user_from_host` are configured: the application can be
// configured to accept request with the {user} in the header host or in the request path.
// It also might depend on the configured cdn_url via `serverMetadata.cdn_url`.
//
// This template allows to make the endpoints generation more flexible, the template exposes the following params:
// 1. {{=it.cdn_url}}: will be used when `serverMetadata.cdn_url` exists.
// 2. {{=it.user}}: will use the username as extraced from `user_from_host` or `base_url_detached`.
// 3. {{=it.port}}: will use the `port` from this very same configuration file.
,resources_url_templates: {
http: 'http://{{=it.cdn_url}}/{{=it.user}}/api/v1/map',
https: 'https://{{=it.cdn_url}}/{{=it.user}}/api/v1/map'
}
// Maximum number of connections for one process
// 128 is a good value with a limit of 1024 open file descriptors
,maxConnections:128
@@ -56,6 +74,7 @@ var config = {
host: '127.0.0.1',
port: 6432,
extent: "-20037508.3,-20037508.3,20037508.3,20037508.3",
// max number of rows to return when querying data, 0 means no limit
row_limit: 65535,
/*
* Set persist_connection to false if you want
@@ -89,6 +108,10 @@ var config = {
// Important: check the configuration of uv_threadpool_size to use suitable value
poolSize: 8,
// Whether grainstore will use a child process or not to transform CartoCSS into Mapnik XML.
// This will prevent blocking the main thread.
useCartocssWorkers: false,
// Metatile is the number of tiles-per-side that are going
// to be rendered at once. If all of them will be requested
// we'd have saved time. If only one will be used, we'd have
@@ -203,6 +226,18 @@ var config = {
endpoint: 'http://127.0.0.1:8080/api/v2/sql/job',
// the template to use for adding the host header in the batch api requests
hostHeaderTemplate: '{{=it.username}}.localhost.lan'
},
logger: {
// If filename is given logs comming from analysis client will be written
// there, in append mode. Otherwise 'log_filename' is used. Otherwise stdout is used (default).
// Log file will be re-opened on receiving the HUP signal
filename: 'logs/analysis.log'
},
// Define max execution time in ms for analyses or tags
// If analysis or tag are not found in redis this values will be used as default.
limits: {
moran: { timeout: 120000, maxNumberOfRows: 1e5 },
cpu2x: { timeout: 60000 }
}
}
,millstone: {
@@ -289,7 +324,7 @@ var config = {
// whether the affected tables for a given SQL must query directly postgresql or use the SQL API
cdbQueryTablesFromPostgres: true,
// whether in mapconfig is available stats & metadata for each layer
layerMetadata: false
layerStats: false
}
};

View File

@@ -6,6 +6,9 @@ var config = {
// Its default size is 4, but it can be changed at startup time (the absolute maximum is 128).
// See http://docs.libuv.org/en/latest/threadpool.html
,uv_threadpool_size: undefined
// Time in milliseconds to force GC cycle.
// Disable by using <=0 value.
,gc_interval: 10000
// Regular expression pattern to extract username
// from hostname. Must have a single grabbing block.
,user_from_host: '^(.*)\\.cartodb\\.com$'
@@ -23,6 +26,21 @@ var config = {
// "/tiles/layergroup" is for compatibility with versions up to 1.6.x
,base_url_detached: '(?:/api/v1/map|/user/:user/api/v1/map|/tiles/layergroup)'
// Resource URLs expose endpoints to request/retrieve metadata associated to Maps: dataviews, analysis node status.
//
// This URLs depend on how `base_url_detached` and `user_from_host` are configured: the application can be
// configured to accept request with the {user} in the header host or in the request path.
// It also might depend on the configured cdn_url via `serverMetadata.cdn_url`.
//
// This template allows to make the endpoints generation more flexible, the template exposes the following params:
// 1. {{=it.cdn_url}}: will be used when `serverMetadata.cdn_url` exists.
// 2. {{=it.user}}: will use the username as extraced from `user_from_host` or `base_url_detached`.
// 3. {{=it.port}}: will use the `port` from this very same configuration file.
,resources_url_templates: {
http: 'http://{{=it.user}}.localhost.lan:{{=it.port}}/api/v1/map',
https: 'https://{{=it.cdn_url}}/{{=it.user}}/api/v1/map'
}
// Maximum number of connections for one process
// 128 is a good value with a limit of 1024 open file descriptors
,maxConnections:128
@@ -56,6 +74,7 @@ var config = {
host: '127.0.0.1',
port: 6432,
extent: "-20037508.3,-20037508.3,20037508.3,20037508.3",
// max number of rows to return when querying data, 0 means no limit
row_limit: 65535,
simplify_geometries: true,
use_overviews: true, // use overviews to retrieve raster
@@ -89,6 +108,10 @@ var config = {
// Important: check the configuration of uv_threadpool_size to use suitable value
poolSize: 8,
// Whether grainstore will use a child process or not to transform CartoCSS into Mapnik XML.
// This will prevent blocking the main thread.
useCartocssWorkers: false,
// Metatile is the number of tiles-per-side that are going
// to be rendered at once. If all of them will be requested
// we'd have saved time. If only one will be used, we'd have
@@ -203,6 +226,18 @@ var config = {
endpoint: 'http://127.0.0.1:8080/api/v2/sql/job',
// the template to use for adding the host header in the batch api requests
hostHeaderTemplate: '{{=it.username}}.localhost.lan'
},
logger: {
// If filename is given logs comming from analysis client will be written
// there, in append mode. Otherwise 'log_filename' is used. Otherwise stdout is used (default).
// Log file will be re-opened on receiving the HUP signal
filename: 'logs/analysis.log'
},
// Define max execution time in ms for analyses or tags
// If analysis or tag are not found in redis this values will be used as default.
limits: {
moran: { timeout: 120000, maxNumberOfRows: 1e5 },
cpu2x: { timeout: 60000 }
}
}
,millstone: {
@@ -289,7 +324,7 @@ var config = {
// whether the affected tables for a given SQL must query directly postgresql or use the SQL API
cdbQueryTablesFromPostgres: true,
// whether in mapconfig is available stats & metadata for each layer
layerMetadata: true
layerStats: true
}
};

View File

@@ -6,6 +6,9 @@ var config = {
// Its default size is 4, but it can be changed at startup time (the absolute maximum is 128).
// See http://docs.libuv.org/en/latest/threadpool.html
,uv_threadpool_size: undefined
// Time in milliseconds to force GC cycle.
// Disable by using <=0 value.
,gc_interval: 10000
// Regular expression pattern to extract username
// from hostname. Must have a single grabbing block.
,user_from_host: '(.*)'
@@ -23,6 +26,20 @@ var config = {
// "tiles/layergroup" is for compatibility with versions up to 1.6.x
,base_url_detached: '(?:/api/v1/map|/user/:user/api/v1/map|/tiles/layergroup)'
// Resource URLs expose endpoints to request/retrieve metadata associated to Maps: dataviews, analysis node status.
//
// This URLs depend on how `base_url_detached` and `user_from_host` are configured: the application can be
// configured to accept request with the {user} in the header host or in the request path.
// It also might depend on the configured cdn_url via `serverMetadata.cdn_url`.
//
// This template allows to make the endpoints generation more flexible, the template exposes the following params:
// 1. {{=it.cdn_url}}: will be used when `serverMetadata.cdn_url` exists.
// 2. {{=it.user}}: will use the username as extraced from `user_from_host` or `base_url_detached`.
// 3. {{=it.port}}: will use the `port` from this very same configuration file.
,resources_url_templates: {
http: 'http://{{=it.user}}.localhost.lan:{{=it.port}}/api/v1/map'
}
// Maximum number of connections for one process
// 128 is a good value with a limit of 1024 open file descriptors
,maxConnections:128
@@ -56,6 +73,7 @@ var config = {
host: '127.0.0.1',
port: 5432,
extent: "-20037508.3,-20037508.3,20037508.3,20037508.3",
// max number of rows to return when querying data, 0 means no limit
row_limit: 65535,
simplify_geometries: true,
use_overviews: true, // use overviews to retrieve raster
@@ -89,6 +107,10 @@ var config = {
// Important: check the configuration of uv_threadpool_size to use suitable value
poolSize: 8,
// Whether grainstore will use a child process or not to transform CartoCSS into Mapnik XML.
// This will prevent blocking the main thread.
useCartocssWorkers: false,
// Metatile is the number of tiles-per-side that are going
// to be rendered at once. If all of them will be requested
// we'd have saved time. If only one will be used, we'd have
@@ -204,6 +226,18 @@ var config = {
endpoint: 'http://127.0.0.1:8080/api/v2/sql/job',
// the template to use for adding the host header in the batch api requests
hostHeaderTemplate: '{{=it.username}}.localhost.lan'
},
logger: {
// If filename is given logs comming from analysis client will be written
// there, in append mode. Otherwise 'log_filename' is used. Otherwise stdout is used (default).
// Log file will be re-opened on receiving the HUP signal
filename: 'node-windshaft.log'
},
// Define max execution time in ms for analyses or tags
// If analysis or tag are not found in redis this values will be used as default.
limits: {
moran: { timeout: 120000, maxNumberOfRows: 1e5 },
cpu2x: { timeout: 60000 }
}
}
,millstone: {
@@ -284,7 +318,7 @@ var config = {
// whether the affected tables for a given SQL must query directly postgresql or use the SQL API
cdbQueryTablesFromPostgres: true,
// whether in mapconfig is available stats & metadata for each layer
layerMetadata: true
layerStats: true
}
};

View File

@@ -141,9 +141,6 @@ https://{username}.carto.com/api/v1/map/{layergroupid}/0,3,4/{z}/{x}/{y}.png
Some notes about filtering:
- Invalid index values or out of bounds indexes will end in `Invalid layer filtering` errors.
- Once a Mapnik layer is selected, all Mapnik layers will get blended. As this may change in the future **it is
recommended** to always select all Mapnik layers if you want to select at least one so you will get a consistent
behavior in the future.
- Ordering is not considered. So right now filtering layers 0,3,4 is the very same thing as filtering 3,4,0. As this
may change in the future **it is recommended** to always select the layers in ascending order so you will get a
consistent behavior in the future.

View File

@@ -1,10 +1,10 @@
# Named Maps
Named Maps are essentially the same as Anonymous Maps except the MapConfig is stored on the server, and the map is given a unique name. You can create Named Maps from private data, and users without an API Key can view your Named Map (while keeping your data private).
Named Maps are essentially the same as Anonymous Maps except the MapConfig is stored on the server, and the map is given a unique name. You can create Named Maps from private data, and users without an API Key can view your Named Map (while keeping your data private).
The Named Map workflow consists of uploading a MapConfig file to CARTO servers, to select data from your CARTO user database by using SQL, and specifying the CartoCSS for your map.
The Named Map workflow consists of uploading a MapConfig file to CARTO servers, to select data from your CARTO user database by using SQL, and specifying the CartoCSS for your map.
The response back from the API provides the template_id of your Named Map as the `name` (the identifier of your Named Map), which is the name that you specified in the MapConfig. You can which you can then use to create your Named Map details, or [fetch XYZ tiles](#fetching-xyz-tiles-for-named-maps) directly for Named Maps.
The response back from the API provides the template_id of your Named Map as the `name` (the identifier of your Named Map), which is the name that you specified in the MapConfig. You can which you can then use to create your Named Map details, or [fetch XYZ tiles](#fetching-xyz-tiles-for-named-maps) directly for Named Maps.
**Tip:** You can also use a Named Map that you created (which is defined by its `name`), to create a map using CARTO.js. This is achieved by adding the [`namedmap` type](http://docs.carto.com/carto-engine/carto-js/layer-source-object/#named-maps-layer-source-object-type-namedmap) layer source object to draw the Named Map.
@@ -84,6 +84,10 @@ The `name` argument defines how to name this "template_name".json. Note that the
"south": -45,
"east": 45,
"north": 45
},
"preview_layers": {
"0": true,
"layer1": false
}
}
}
@@ -95,22 +99,22 @@ Params | Description
--- | ---
name | There can only be _one_ template with the same name for any user. Valid names start with a letter or a number, and only contain letters, numbers, dashes (-), or underscores (_). _This is specific to the name of your Named Map that is specified in the `name` property of the template file_.
auth |
auth |
--- | ---
&#124;_ method | `"token"` or `"open"` (`"open"` is the default if no method is specified. Use `"token"` to password-protect your map)
&#124;_ valid_tokens | when `"method"` is set to `"token"`, the values listed here allow you to instantiate the Named Map. See this [example](http://docs.carto.com/faqs/manipulating-your-data/#how-to-create-a-password-protected-named-map) for how to create a password-protected map.
placeholders | Placeholders are variables that can be placed in your template.json file's SQL or CartoCSS.
layergroup | the layergroup configurations, as specified in the template. See [MapConfig File Format](http://docs.carto.com/carto-engine/maps-api/mapconfig/) for more information.
view (optional) | extra keys to specify the view area for the map. It can be used to have a static preview of a Named Map without having to instantiate it. It is possible to specify it with `center` + `zoom` or with a bounding box `bbox`. Center+zoom takes precedence over bounding box.
view (optional) | extra keys to specify the view area for the map. It can be used to have a static preview of a Named Map without having to instantiate it. It is possible to specify it with `center` + `zoom` or with a bounding box `bbox`. Center+zoom takes precedence over bounding box. Also it is possible to choose which layers are visible or not with `preview_layers` indicating its visibility by layer index or id (visible by default).
--- | ---
&#124;_ zoom | The zoom level to use
&#124;_ center |
&#124;_ center |
--- | ---
&#124;_ &#124;_ lng | The longitude to use for the center
&#124;_ &#124;_ lat | The latitude to use for the center
&#124;_ bounds |
&#124;_ bounds |
--- | ---
&#124;_ &#124;_ west | LowerCorner longitude for the bounding box, in decimal degrees (aka most western)
&#124;_ &#124;_ south | LowerCorner latitude for the bounding box, in decimal degrees (aka most southern)
@@ -120,7 +124,7 @@ view (optional) | extra keys to specify the view area for the map. It can be use
### Placeholder Format
Placeholders are variables that can be placed in your template.json file. Placeholders need to be defined with a `type` and a default value for MapConfigs. See details about defining a MapConfig `type` for [Layergoup configurations](http://docs.carto.com/carto-engine/maps-api/mapconfig/#layergroup-configurations).
Placeholders are variables that can be placed in your template.json file. Placeholders need to be defined with a `type` and a default value for MapConfigs. See details about defining a MapConfig `type` for [Layergroup configurations](http://docs.carto.com/carto-engine/maps-api/mapconfig/#layergroup-configurations).
Valid placeholder names start with a letter and can only contain letters, numbers, or underscores. They have to be written between the `<%=` and `%>` strings in order to be replaced inside the Named Maps API.
@@ -160,7 +164,7 @@ curl -X POST \
#### Response
The response back from the API provides the name of your MapConfig as a template, enabling you to edit the Named Map details by inserting your variables into the template where placeholders are defined, and create custom queries using SQL.
The response back from the API provides the name of your MapConfig as a template, enabling you to edit the Named Map details by inserting your variables into the template where placeholders are defined, and create custom queries using SQL.
```javascript
{
@@ -516,18 +520,18 @@ If you are creating a Torque layer in a Named Map without using the Torque.js li
})
.addTo(map)
.done(function(layer) {
});
}
```
#### Examples of Named Maps created with CARTO.js
- [Named Map selectors with interaction](http://bl.ocks.org/ohasselblad/515a8af1f99d5e690484)
- [Named Map selectors with interaction](http://bl.ocks.org/andy-esch/515a8af1f99d5e690484)
- [Named Map with interactivity](http://bl.ocks.org/ohasselblad/d1a45b8ff5e7bd90cd68)
- [Named Map with interactivity](http://bl.ocks.org/andy-esch/d1a45b8ff5e7bd90cd68)
- [Toggling sublayers in a Named Map](http://bl.ocks.org/ohasselblad/c1a0f4913610eec53cd3)
- [Toggling sublayers in a Named Map](http://bl.ocks.org/andy-esch/c1a0f4913610eec53cd3)
## Fetching XYZ Tiles for Named Maps
@@ -535,7 +539,7 @@ Optionally, authenticated users can fetch projected tiles (XYZ tiles or Mapnik R
### Fetch XYZ Tiles Directly with a URL
Authenticated users, with an auth token, can use XYZ-based URLs to fetch tiles directly, and instantiate the Named Map as part of the request to your application. You do not have to do any other steps to initialize your map.
Authenticated users, with an auth token, can use XYZ-based URLs to fetch tiles directly, and instantiate the Named Map as part of the request to your application. You do not have to do any other steps to initialize your map.
To call a template_id in a URL:

View File

@@ -146,10 +146,14 @@ It is important to note that generated images are cached from the live data refe
### Limits
* While images can encompass an entirety of a map, the default limit for pixel range is 8192 x 8192.
* Image resolution by default is set to 72 DPI
* JPEG quality by default is 85%
* Timeout limits for generating static maps are the same across the CARTO Editor and CARTO Engine. It is important to ensure timely processing of queries.
* While images can encompass an entirety of a map, the limit for pixel range is 8192 x 8192.
* Image resolution is set to 72 DPI
* JPEG quality is 85%
* Timeout limits for generating static maps are the same across CARTO Builder and CARTO Engine. It is important to ensure timely processing of queries.
* If you are publishing your map as a static image with the API, you must manually add [attributions](https://carto.com/attribution) for your static map image. For example, add the following attribution code:
{% highlight javascript %}attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, &copy; <a href="https://carto.com/attributions">CARTO</a>
{% endhighlight %}
## Examples

View File

@@ -95,9 +95,7 @@ AuthApi.prototype.authorize = function(req, callback) {
self.authorizedByAPIKey(user, req, this);
},
function checkApiKey(err, authorized){
if (req.profiler) {
req.profiler.done('authorizedByAPIKey');
}
req.profiler.done('authorizedByAPIKey');
assert.ifError(err);
// if not authorized by api_key, continue
@@ -131,9 +129,7 @@ AuthApi.prototype.authorize = function(req, callback) {
}
self.pgConnection.setDBAuth(user, req.params, function(err) {
if (req.profiler) {
req.profiler.done('setDBAuth');
}
req.profiler.done('setDBAuth');
callback(err, true); // authorized (or error)
});
}

View File

@@ -1,19 +1,93 @@
var camshaft = require('camshaft');
'use strict';
function AnalysisBackend(options) {
var batchConfig = options.batch || {};
var _ = require('underscore');
var camshaft = require('camshaft');
var fs = require('fs');
var REDIS_LIMITS = {
DB: 5,
PREFIX: 'limits:analyses:' // + username
};
function AnalysisBackend (metadataBackend, options) {
this.metadataBackend = metadataBackend;
this.options = options || {};
this.options.limits = this.options.limits || {};
this.setBatchConfig(this.options.batch);
this.setLoggerConfig(this.options.logger);
}
module.exports = AnalysisBackend;
AnalysisBackend.prototype.setBatchConfig = function (options) {
var batchConfig = options || {};
batchConfig.endpoint = batchConfig.endpoint || 'http://127.0.0.1:8080/api/v1/sql/job';
batchConfig.inlineExecution = batchConfig.inlineExecution || false;
batchConfig.hostHeaderTemplate = batchConfig.hostHeaderTemplate || '{{=it.username}}.localhost.lan';
this.batchConfig = batchConfig;
}
};
module.exports = AnalysisBackend;
AnalysisBackend.prototype.setLoggerConfig = function (options) {
this.loggerConfig = options || {};
if (this.loggerConfig.filename) {
this.stream = fs.createWriteStream(this.loggerConfig.filename, { flags: 'a', encoding: 'utf8' });
process.on('SIGHUP', function () {
if (this.stream) {
this.stream.destroy();
}
this.stream = fs.createWriteStream(this.loggerConfig.filename, { flags: 'a', encoding: 'utf8' });
}.bind(this));
}
};
AnalysisBackend.prototype.create = function(analysisConfiguration, analysisDefinition, callback) {
analysisConfiguration.batch.endpoint = this.batchConfig.endpoint;
analysisConfiguration.batch.inlineExecution = this.batchConfig.inlineExecution;
analysisConfiguration.batch.hostHeaderTemplate = this.batchConfig.hostHeaderTemplate;
camshaft.create(analysisConfiguration, analysisDefinition, callback);
analysisConfiguration.logger = {
stream: this.stream ? this.stream : process.stdout
};
this.getAnalysesLimits(analysisConfiguration.user, function(err, limits) {
analysisConfiguration.limits = limits || {};
camshaft.create(analysisConfiguration, analysisDefinition, callback);
});
};
AnalysisBackend.prototype.getAnalysesLimits = function(username, callback) {
var self = this;
var analysesLimits = {
analyses: {
// buffer: {
// timeout: 1000,
// maxNumberOfRows: 1e6
// }
}
};
Object.keys(self.options.limits).forEach(function(analysisTypeOrTag) {
analysesLimits.analyses[analysisTypeOrTag] = _.extend({}, self.options.limits[analysisTypeOrTag]);
});
var analysesLimitsKey = REDIS_LIMITS.PREFIX + username;
this.metadataBackend.redisCmd(REDIS_LIMITS.DB, 'HGETALL', [analysesLimitsKey], function(err, analysesTimeouts) {
// analysesTimeouts wil be something like: { moran: 3000, intersection: 5000 }
analysesTimeouts = analysesTimeouts || {};
Object.keys(analysesTimeouts).forEach(function(analysisType) {
analysesLimits.analyses[analysisType] = _.defaults(
{
timeout: Number.isFinite(+analysesTimeouts[analysisType]) ? +analysesTimeouts[analysisType] : 0
},
analysesLimits.analyses[analysisType]
);
});
return callback(null, analysesLimits);
});
};

View File

@@ -43,50 +43,19 @@ DataviewBackend.prototype.getDataview = function (mapConfigProvider, user, param
ownFilter = !!ownFilter;
var query = (ownFilter) ? dataviewDefinition.sql.own_filter_on : dataviewDefinition.sql.own_filter_off;
var sourceId = dataviewDefinition.source.id; // node.id
var layer = _.find(mapConfig.obj().layers, function(l) {
return l.options.source && (l.options.source.id === sourceId);
});
var queryRewriteData = layer && layer.options.query_rewrite_data;
if (queryRewriteData && dataviewDefinition.node.type === 'source') {
queryRewriteData = _.extend({}, queryRewriteData, {
filters: dataviewDefinition.node.filters,
unfiltered_query: dataviewDefinition.sql.own_filter_on
});
}
if (params.bbox) {
var bboxFilter = new BBoxFilter({column: 'the_geom_webmercator', srid: 3857}, {bbox: params.bbox});
query = bboxFilter.sql(query);
if ( queryRewriteData ) {
var bbox_filter_definition = {
type: 'bbox',
options: {
column: 'the_geom_webmercator',
srid: 3857
},
params: {
bbox: params.bbox
}
};
queryRewriteData = _.extend(queryRewriteData, { bbox_filter: bbox_filter_definition });
}
}
var queryRewriteData = getQueryRewriteData(mapConfig, dataviewDefinition, params);
var dataviewFactory = DataviewFactoryWithOverviews.getFactory(
overviewsQueryRewriter, queryRewriteData, { bbox: params.bbox }
);
var overrideParams = _.reduce(_.pick(params, 'start', 'end', 'bins'),
function castNumbers(overrides, val, k) {
overrides[k] = Number.isFinite(+val) ? +val : val;
return overrides;
},
{ownFilter: ownFilter}
);
var dataview = dataviewFactory.getDataview(query, dataviewDefinition);
dataview.getResult(pg, overrideParams, this);
dataview.getResult(pg, getOverrideParams(params, ownFilter), this);
},
function returnCallback(err, result) {
return callback(err, result);
@@ -94,6 +63,49 @@ DataviewBackend.prototype.getDataview = function (mapConfigProvider, user, param
);
};
function getQueryRewriteData(mapConfig, dataviewDefinition, params) {
var sourceId = dataviewDefinition.source.id; // node.id
var layer = _.find(mapConfig.obj().layers, function(l) {
return l.options.source && (l.options.source.id === sourceId);
});
var queryRewriteData = layer && layer.options.query_rewrite_data;
if (queryRewriteData && dataviewDefinition.node.type === 'source') {
queryRewriteData = _.extend({}, queryRewriteData, {
filters: dataviewDefinition.node.filters,
unfiltered_query: dataviewDefinition.sql.own_filter_on
});
}
if (params.bbox && queryRewriteData) {
var bbox_filter_definition = {
type: 'bbox',
options: {
column: 'the_geom_webmercator',
srid: 3857
},
params: {
bbox: params.bbox
}
};
queryRewriteData = _.extend(queryRewriteData, { bbox_filter: bbox_filter_definition });
}
return queryRewriteData;
}
function getOverrideParams(params, ownFilter) {
return _.reduce(_.pick(params, 'start', 'end', 'bins'),
function castNumbers(overrides, val, k) {
if (!Number.isFinite(+val)) {
throw new Error('Invalid number format for parameter \'' + k + '\'');
}
overrides[k] = +val;
return overrides;
},
{ownFilter: ownFilter}
);
}
DataviewBackend.prototype.search = function (mapConfigProvider, user, params, callback) {
var dataviewName = params.dataviewName;

View File

@@ -0,0 +1,16 @@
function EmptyLayerStats(types) {
this._types = types || {};
}
EmptyLayerStats.prototype.is = function (type) {
return this._types[type] ? this._types[type] : false;
};
EmptyLayerStats.prototype.getStats =
function (layer, dbConnection, callback) {
setImmediate(function() {
callback(null, {});
});
};
module.exports = EmptyLayerStats;

View File

@@ -0,0 +1,23 @@
var LayerStats = require('./layer-stats');
var EmptyLayerStats = require('./empty-layer-stats');
var MapnikLayerStats = require('./mapnik-layer-stats');
var TorqueLayerStats = require('./torque-layer-stats');
module.exports = function LayerStatsFactory(type) {
var layerStatsIterator = [];
var selectedType = type || 'ALL';
if (selectedType === 'ALL') {
layerStatsIterator.push(new EmptyLayerStats({ http: true, plain: true }));
layerStatsIterator.push(new MapnikLayerStats());
layerStatsIterator.push(new TorqueLayerStats());
} else if (selectedType === 'mapnik') {
layerStatsIterator.push(new EmptyLayerStats({ http: true, plain: true, torque: true }));
layerStatsIterator.push(new MapnikLayerStats());
} else if (selectedType === 'torque') {
layerStatsIterator.push(new EmptyLayerStats({ http: true, plain: true, mapnik: true }));
layerStatsIterator.push(new TorqueLayerStats());
}
return new LayerStats(layerStatsIterator);
};

View File

@@ -0,0 +1,45 @@
var queue = require('queue-async');
function LayerStats(layerStatsIterator) {
this.layerStatsIterator = layerStatsIterator;
}
LayerStats.prototype.getStats = function (mapConfig, dbConnection, callback) {
var self = this;
var stats = [];
if (!mapConfig.getLayers().length) {
return callback(null, stats);
}
var metaQueue = queue(mapConfig.getLayers().length);
mapConfig.getLayers().forEach(function (layer, layerId) {
var layerType = mapConfig.layerType(layerId);
for (var i = 0; i < self.layerStatsIterator.length; i++) {
if (self.layerStatsIterator[i].is(layerType)) {
var getStats = self.layerStatsIterator[i].getStats.bind(self.layerStatsIterator[i]);
metaQueue.defer(getStats, layer, dbConnection);
break;
}
}
});
metaQueue.awaitAll(function (err, results) {
if (err) {
return callback(err);
}
if (!results) {
return callback(null, null);
}
mapConfig.getLayers().forEach(function (layer, layerIndex) {
stats[layerIndex] = results[layerIndex];
});
return callback(err, stats);
});
};
module.exports = LayerStats;

View File

@@ -0,0 +1,28 @@
var queryUtils = require('../../utils/query-utils');
function MapnikLayerStats () {
this._types = {
mapnik: true,
cartodb: true
};
}
MapnikLayerStats.prototype.is = function (type) {
return this._types[type] ? this._types[type] : false;
};
MapnikLayerStats.prototype.getStats =
function (layer, dbConnection, callback) {
var queryRowCountSql = queryUtils.getQueryRowCount(layer.options.sql);
// This query would gather stats for postgresql table if not exists
dbConnection.query(queryRowCountSql, function (err, res) {
if (err) {
return callback(null, {estimatedFeatureCount: -1});
} else {
// We decided that the relation is 1 row == 1 feature
return callback(null, {estimatedFeatureCount: res.rows[0].rows});
}
});
};
module.exports = MapnikLayerStats;

View File

@@ -0,0 +1,16 @@
function TorqueLayerStats() {
this._types = {
torque: true
};
}
TorqueLayerStats.prototype.is = function (type) {
return this._types[type] ? this._types[type] : false;
};
TorqueLayerStats.prototype.getStats =
function (layer, dbConnection, callback) {
return callback(null, {});
};
module.exports = TorqueLayerStats;

View File

@@ -0,0 +1,16 @@
var layerStats = require('./layer-stats/factory');
function StatsBackend() {
}
module.exports = StatsBackend;
StatsBackend.prototype.getStats = function(mapConfig, dbConnection, callback) {
var enabledFeatures = global.environment.enabledFeatures;
var layerStatsEnabled = enabledFeatures ? enabledFeatures.layerStats: false;
if (layerStatsEnabled) {
layerStats().getStats(mapConfig, dbConnection, callback);
} else {
return callback(null, []);
}
};

View File

@@ -55,11 +55,9 @@ util.inherits(TemplateMaps, EventEmitter);
module.exports = TemplateMaps;
var o = TemplateMaps.prototype;
//--------------- PRIVATE METHODS --------------------------------
o._userTemplateLimit = function() {
TemplateMaps.prototype._userTemplateLimit = function() {
return this.opts.max_user_templates || 0;
};
@@ -70,7 +68,7 @@ o._userTemplateLimit = function() {
* @param redisArgs - the arguments for the redis function in an array
* @param callback - function to pass results too.
*/
o._redisCmd = function(redisFunc, redisArgs, callback) {
TemplateMaps.prototype._redisCmd = function(redisFunc, redisArgs, callback) {
var redisClient;
var that = this;
var db = that.db_signatures;
@@ -97,7 +95,7 @@ o._redisCmd = function(redisFunc, redisArgs, callback) {
var _reValidNameIdentifier = /^[a-z0-9][0-9a-z_\-]*$/i;
var _reValidPlaceholderIdentifier = /^[a-z][0-9a-z_]*$/i;
// jshint maxcomplexity:15
o._checkInvalidTemplate = function(template) {
TemplateMaps.prototype._checkInvalidTemplate = function(template) {
if ( template.version !== '0.0.1' ) {
return new Error("Unsupported template version " + template.version);
}
@@ -200,7 +198,7 @@ function templateDefaults(template) {
// @param callback function(err, tpl_id)
// Return template identifier (only valid for given user)
//
o.addTemplate = function(owner, template, callback) {
TemplateMaps.prototype.addTemplate = function(owner, template, callback) {
var self = this;
template = templateDefaults(template);
@@ -258,7 +256,7 @@ o.addTemplate = function(owner, template, callback) {
//
// @param callback function(err)
//
o.delTemplate = function(owner, tpl_id, callback) {
TemplateMaps.prototype.delTemplate = function(owner, tpl_id, callback) {
var self = this;
step(
function deleteTemplate() {
@@ -297,7 +295,8 @@ o.delTemplate = function(owner, tpl_id, callback) {
//
// @param callback function(err)
//
o.updTemplate = function(owner, tpl_id, template, callback) {
TemplateMaps.prototype.updTemplate = function(owner, tpl_id, template, callback) {
var self = this;
template = templateDefaults(template);
@@ -356,7 +355,7 @@ o.updTemplate = function(owner, tpl_id, template, callback) {
// @param callback function(err, tpl_id_list)
// Returns a list of template identifiers
//
o.listTemplates = function(owner, callback) {
TemplateMaps.prototype.listTemplates = function(owner, callback) {
this._redisCmd('HKEYS', [ this.key_usr_tpl({owner:owner}) ], callback);
};
@@ -370,7 +369,7 @@ o.listTemplates = function(owner, callback) {
// @param callback function(err, template)
// Return full template definition
//
o.getTemplate = function(owner, tpl_id, callback) {
TemplateMaps.prototype.getTemplate = function(owner, tpl_id, callback) {
var self = this;
step(
function getTemplate() {
@@ -386,7 +385,7 @@ o.getTemplate = function(owner, tpl_id, callback) {
);
};
o.isAuthorized = function(template, authTokens) {
TemplateMaps.prototype.isAuthorized = function(template, authTokens) {
if (!template) {
return false;
}
@@ -431,14 +430,18 @@ var _reNumber = /^([-+]?[\d\.]?\d+([eE][+-]?\d+)?)$/,
_reCSSColorVal = /^#[0-9a-fA-F]{3,6}$/;
function _replaceVars (str, params) {
//return _.template(str, params); // lazy way, possibly dangerous
// Construct regular expressions for each param
// Construct regular expressions for each param
Object.keys(params).forEach(function(k) {
str = str.replace(new RegExp("<%=\\s*" + k + "\\s*%>", "g"), params[k]);
});
return str;
}
o.instance = function(template, params) {
function isObject(val) {
return ( _.isObject(val) && !_.isArray(val) && !_.isFunction(val));
}
TemplateMaps.prototype.instance = function(template, params) {
var all_params = {};
var phold = template.placeholders || {};
Object.keys(phold).forEach(function(k) {
@@ -475,6 +478,13 @@ o.instance = function(template, params) {
// NOTE: we're deep-cloning the layergroup here
var layergroup = JSON.parse(JSON.stringify(template.layergroup));
if (layergroup.buffersize && isObject(layergroup.buffersize)) {
Object.keys(layergroup.buffersize).forEach(function(k) {
layergroup.buffersize[k] = parseInt(_replaceVars(layergroup.buffersize[k], all_params), 10);
});
}
for (var i=0; i<layergroup.layers.length; ++i) {
var lyropt = layergroup.layers[i].options;
@@ -500,7 +510,7 @@ o.instance = function(template, params) {
};
// Return a fingerPrint of the object
o.fingerPrint = function(template) {
TemplateMaps.prototype.fingerPrint = function(template) {
return crypto.createHash('md5')
.update(JSON.stringify(template))
.digest('hex')

View File

@@ -6,6 +6,9 @@ dot.templateSettings.strip = false;
function createTemplate(method) {
return dot.template([
'SELECT',
'min({{=it._column}}) min_val,',
'max({{=it._column}}) max_val,',
'avg({{=it._column}}) avg_val,',
method,
'FROM ({{=it._sql}}) _table_sql WHERE {{=it._column}} IS NOT NULL'
].join('\n'));
@@ -29,7 +32,7 @@ methodTemplates.category = dot.template([
' SELECT {{=it._column}} AS category, count(1) AS value, row_number() OVER (ORDER BY count(1) desc) as rank',
' FROM ({{=it._sql}}) _cdb_aggregation_all',
' GROUP BY {{=it._column}}',
' ORDER BY 2 DESC',
' ORDER BY 2 DESC, 1 ASC',
'),',
'agg_categories AS (',
' SELECT category',
@@ -74,11 +77,14 @@ PostgresDatasource.prototype.getRamp = function (column, buckets, method, callba
return callback(err);
}
resultSet = resultSet || {};
var result = resultSet.rows || [];
var result = getResult(resultSet);
var strategy = method2strategy[methodName];
var ramp = result[0][methodName] || [];
var ramp = result[methodName] || [];
var stats = {
min_val: result.min_val,
max_val: result.max_val,
avg_val: result.avg_val
};
// Skip null values from ramp
// Generated turbo-carto won't be correct, but better to keep it working than failing
// TODO fix cartodb-postgres extension quantification functions
@@ -89,8 +95,16 @@ PostgresDatasource.prototype.getRamp = function (column, buckets, method, callba
});
}
return callback(null, { ramp: ramp, strategy: strategy });
return callback(null, { ramp: ramp, strategy: strategy, stats: stats });
}, true); // use read-only transaction
};
function getResult(resultSet) {
resultSet = resultSet || {};
var result = resultSet.rows || [];
result = result[0] || {};
return result;
}
module.exports = PostgresDatasource;

View File

@@ -36,7 +36,7 @@ function BaseController(authApi, pgConnection) {
module.exports = BaseController;
// jshint maxcomplexity:9
// jshint maxcomplexity:10
/**
* Whitelist input and get database name & default geometry type from
* subdomain/user metadata held in CartoDB Redis
@@ -61,9 +61,7 @@ BaseController.prototype.req2params = function(req, callback){
lzmaWorker.decompress(
lzma,
function(result) {
if (req.profiler) {
req.profiler.done('lzma');
}
req.profiler.done('lzma');
try {
delete req.query.lzma;
_.extend(req.query, JSON.parse(result));
@@ -77,7 +75,11 @@ BaseController.prototype.req2params = function(req, callback){
return;
}
req.query = _.pick(req.query, REQUEST_QUERY_PARAMS_WHITELIST);
var allowedQueryParams = REQUEST_QUERY_PARAMS_WHITELIST;
if (Array.isArray(req.context.allowedQueryParams)) {
allowedQueryParams = allowedQueryParams.concat(req.context.allowedQueryParams);
}
req.query = _.pick(req.query, allowedQueryParams);
req.params = _.extend({}, req.params); // shuffle things as request is a strange array/object
var user = req.context.user;
@@ -115,18 +117,14 @@ BaseController.prototype.req2params = function(req, callback){
// bring all query values onto req.params object
_.extend(req.params, req.query);
if (req.profiler) {
req.profiler.done('req2params.setup');
}
req.profiler.done('req2params.setup');
step(
function getPrivacy(){
self.authApi.authorize(req, this);
},
function validateAuthorization(err, authorized) {
if (req.profiler) {
req.profiler.done('authorize');
}
req.profiler.done('authorize');
assert.ifError(err);
if(!authorized) {
err = new Error("Sorry, you are unauthorized (permission denied)");
@@ -167,9 +165,7 @@ BaseController.prototype.send = function(req, res, body, status, headers) {
res.set('X-Served-By-DB-Host', req.params.dbhost);
}
if (req.profiler) {
res.set('X-Tiler-Profiler', req.profiler.toJSONString());
}
res.set('X-Tiler-Profiler', req.profiler.toJSONString());
if (headers) {
res.set(headers);
@@ -187,14 +183,12 @@ BaseController.prototype.send = function(req, res, body, status, headers) {
res.send(body);
}
if (req.profiler) {
try {
// May throw due to dns, see
// See http://github.com/CartoDB/Windshaft/issues/166
req.profiler.sendStats();
} catch (err) {
debug("error sending profiling stats: " + err);
}
try {
// May throw due to dns, see
// See http://github.com/CartoDB/Windshaft/issues/166
req.profiler.sendStats();
} catch (err) {
debug("error sending profiling stats: " + err);
}
};
// jshint maxcomplexity:6
@@ -207,6 +201,10 @@ BaseController.prototype.sendError = function(req, res, err, label) {
var statusCode = findStatusCode(err);
if (err.message === 'Tile does not exist' && req.params.format === 'mvt') {
statusCode = 204;
}
debug('[%s ERROR] -- %d: %s, %s', label, statusCode, err, err.stack);
// If a callback was requested, force status to 200

View File

@@ -6,6 +6,7 @@ var BaseController = require('./base');
var cors = require('../middleware/cors');
var userMiddleware = require('../middleware/user');
var allowQueryParams = require('../middleware/allow-query-params');
var DataviewBackend = require('../backends/dataview');
var AnalysisStatusBackend = require('../backends/analysis-status');
@@ -67,11 +68,13 @@ LayergroupController.prototype.register = function(app) {
this.attributes.bind(this));
app.get(app.base_url_mapconfig +
'/static/center/:token/:z/:lat/:lng/:width/:height.:format', cors(), userMiddleware,
'/static/center/:token/:z/:lat/:lng/:width/:height.:format',
cors(), userMiddleware, allowQueryParams(['layer']),
this.center.bind(this));
app.get(app.base_url_mapconfig +
'/static/bbox/:token/:west,:south,:east,:north/:width/:height.:format', cors(), userMiddleware,
'/static/bbox/:token/:west,:south,:east,:north/:width/:height.:format',
cors(), userMiddleware, allowQueryParams(['layer']),
this.bbox.bind(this));
// Undocumented/non-supported API endpoint methods.
@@ -250,7 +253,9 @@ LayergroupController.prototype.finalizeGetTileOrGrid = function(err, req, res, t
grid_json: true,
json_torque: true,
torque_json: true,
png: true
png: true,
png32: true,
mvt: true
};
var formatStat = 'invalid';
@@ -338,6 +343,8 @@ LayergroupController.prototype.staticMap = function(req, res, width, height, zoo
LayergroupController.prototype.sendResponse = function(req, res, body, status, headers) {
var self = this;
req.profiler.done('res');
res.set('Cache-Control', 'public,max-age=31536000');
// Set Last-Modified header
@@ -386,13 +393,15 @@ LayergroupController.prototype.getAffectedTables = function(user, dbName, layerg
function getSQL(err, mapConfig) {
assert.ifError(err);
var queries = mapConfig.getLayers()
.map(function(lyr) {
return lyr.options.sql;
})
.filter(function(sql) {
return !!sql;
});
var queries = [];
mapConfig.getLayers().forEach(function(layer) {
queries.push(layer.options.sql);
if (layer.options.affected_tables) {
layer.options.affected_tables.map(function(table) {
queries.push('SELECT * FROM ' + table + ' LIMIT 0');
});
}
});
return queries.length ? queries.join(';') : null;
},

View File

@@ -4,6 +4,8 @@ var step = require('step');
var windshaft = require('windshaft');
var QueryTables = require('cartodb-query-tables');
var ResourceLocator = require('../models/resource-locator');
var util = require('util');
var BaseController = require('./base');
@@ -18,6 +20,7 @@ var NamedMapsCacheEntry = require('../cache/model/named_maps_entry');
var NamedMapMapConfigProvider = require('../models/mapconfig/provider/named-map-provider');
var CreateLayergroupMapConfigProvider = require('../models/mapconfig/provider/create-layergroup-provider');
/**
* @param {AuthApi} authApi
* @param {PgConnection} pgConnection
@@ -28,10 +31,12 @@ var CreateLayergroupMapConfigProvider = require('../models/mapconfig/provider/cr
* @param {UserLimitsApi} userLimitsApi
* @param {LayergroupAffectedTables} layergroupAffectedTables
* @param {MapConfigAdapter} mapConfigAdapter
* @param {StatsBackend} statsBackend
* @constructor
*/
function MapController(authApi, pgConnection, templateMaps, mapBackend, metadataBackend,
surrogateKeysCache, userLimitsApi, layergroupAffectedTables, mapConfigAdapter) {
surrogateKeysCache, userLimitsApi, layergroupAffectedTables, mapConfigAdapter,
statsBackend) {
BaseController.call(this, authApi, pgConnection);
@@ -44,6 +49,9 @@ function MapController(authApi, pgConnection, templateMaps, mapBackend, metadata
this.layergroupAffectedTables = layergroupAffectedTables;
this.mapConfigAdapter = mapConfigAdapter;
this.resourceLocator = new ResourceLocator(global.environment);
this.statsBackend = statsBackend;
}
util.inherits(MapController, BaseController);
@@ -84,9 +92,7 @@ MapController.prototype.createPost = function(req, res) {
};
MapController.prototype.instantiate = function(req, res) {
if (req.profiler) {
req.profiler.start('windshaft-cartodb.instance_template_post');
}
req.profiler.start('windshaft-cartodb.instance_template_post');
this.instantiateTemplate(req, res, function prepareTemplateParams(callback) {
if (!req.is('application/json')) {
@@ -97,9 +103,7 @@ MapController.prototype.instantiate = function(req, res) {
};
MapController.prototype.jsonp = function(req, res) {
if (req.profiler) {
req.profiler.start('windshaft-cartodb.instance_template_get');
}
req.profiler.start('windshaft-cartodb.instance_template_get');
this.instantiateTemplate(req, res, function prepareJsonTemplateParams(callback) {
var err = null;
@@ -166,11 +170,30 @@ MapController.prototype.create = function(req, res, prepareConfigFn) {
},
function finish(err, layergroup) {
if (err) {
if (Number.isFinite(err.layerIndex)) {
var error = new Error(err.message);
error.http_status = err.http_status;
if (!err.http_status && err.message.indexOf('column "the_geom_webmercator" does not exist') >= 0) {
error.http_status = 400;
}
error.type = 'layer';
error.subtype = err.message.indexOf('Postgis Plugin') >= 0 ? 'query' : undefined;
error.layer = {
id: mapConfig.getLayerId(err.layerIndex),
index: err.layerIndex,
type: mapConfig.layerType(err.layerIndex)
};
err = error;
}
self.sendError(req, res, err, 'ANONYMOUS LAYERGROUP');
} else {
var analysesResults = context.analysesResults || [];
addDataviewsAndWidgetsUrls(req.context.user, layergroup, mapConfig.obj());
addAnalysesMetadata(req.context.user, layergroup, analysesResults, true);
self.addDataviewsAndWidgetsUrls(req.context.user, layergroup, mapConfig.obj());
self.addAnalysesMetadata(req.context.user, layergroup, analysesResults, true);
addContextMetadata(layergroup, mapConfig.obj(), context);
res.set('X-Layergroup-Id', layergroup.layergroupid);
self.send(req, res, layergroup, 200);
}
@@ -178,6 +201,17 @@ MapController.prototype.create = function(req, res, prepareConfigFn) {
);
};
function addContextMetadata(layergroup, mapConfig, context) {
if (layergroup.metadata && Array.isArray(layergroup.metadata.layers) && Array.isArray(mapConfig.layers)) {
layergroup.metadata.layers = layergroup.metadata.layers.map(function(layer, layerIndex) {
if (context.turboCarto && Array.isArray(context.turboCarto.layers)) {
layer.meta.cartocss_meta = context.turboCarto.layers[layerIndex];
}
return layer;
});
}
}
MapController.prototype.instantiateTemplate = function(req, res, prepareParamsFn) {
var self = this;
@@ -185,7 +219,6 @@ MapController.prototype.instantiateTemplate = function(req, res, prepareParamsFn
var mapConfigProvider;
var mapConfig;
step(
function setupParams(){
self.req2params(req, this);
@@ -220,7 +253,9 @@ MapController.prototype.instantiateTemplate = function(req, res, prepareParamsFn
},
function afterLayergroupCreate(err, layergroup) {
assert.ifError(err);
self.afterLayergroupCreate(req, res, mapConfig, layergroup, mapConfigProvider.analysesResults, this);
self.afterLayergroupCreate(req, res, mapConfig, layergroup,
mapConfigProvider.analysesResults,
this);
},
function finishTemplateInstantiation(err, layergroup) {
if (err) {
@@ -229,8 +264,10 @@ MapController.prototype.instantiateTemplate = function(req, res, prepareParamsFn
var templateHash = self.templateMaps.fingerPrint(mapConfigProvider.template).substring(0, 8);
layergroup.layergroupid = cdbuser + '@' + templateHash + '@' + layergroup.layergroupid;
addDataviewsAndWidgetsUrls(cdbuser, layergroup, mapConfig.obj());
addAnalysesMetadata(cdbuser, layergroup, mapConfigProvider.analysesResults);
var _mapConfig = mapConfig.obj();
self.addDataviewsAndWidgetsUrls(cdbuser, layergroup, _mapConfig);
self.addAnalysesMetadata(cdbuser, layergroup, mapConfigProvider.analysesResults);
addContextMetadata(layergroup, _mapConfig, mapConfigProvider.context);
res.set('X-Layergroup-Id', layergroup.layergroupid);
self.surrogateKeysCache.tag(res, new NamedMapsCacheEntry(cdbuser, mapConfigProvider.getTemplateName()));
@@ -241,7 +278,8 @@ MapController.prototype.instantiateTemplate = function(req, res, prepareParamsFn
);
};
MapController.prototype.afterLayergroupCreate = function(req, res, mapconfig, layergroup, analysesResults, callback) {
MapController.prototype.afterLayergroupCreate =
function(req, res, mapconfig, layergroup, analysesResults, callback) {
var self = this;
var username = req.context.user;
@@ -268,21 +306,26 @@ MapController.prototype.afterLayergroupCreate = function(req, res, mapconfig, la
// take place before proceeding. Error will be logged
// asynchronously
this.metadataBackend.incMapviewCount(username, mapconfig.obj().stat_tag, function(err) {
if (req.profiler) {
req.profiler.done('incMapviewCount');
}
req.profiler.done('incMapviewCount');
if ( err ) {
global.logger.log("ERROR: failed to increment mapview count for user '" + username + "': " + err);
}
done();
});
var sql = mapconfig.getLayers().map(function(layer) {
return layer.options.sql;
}).join(';');
var sql = [];
mapconfig.getLayers().forEach(function(layer) {
sql.push(layer.options.sql);
if (layer.options.affected_tables) {
layer.options.affected_tables.map(function(table) {
sql.push('SELECT * FROM ' + table + ' LIMIT 0');
});
}
});
var dbName = req.params.dbname;
var layergroupId = layergroup.layergroupid;
var dbConnection;
step(
function getPgConnection() {
@@ -290,12 +333,11 @@ MapController.prototype.afterLayergroupCreate = function(req, res, mapconfig, la
},
function getAffectedTablesAndLastUpdatedTime(err, connection) {
assert.ifError(err);
QueryTables.getAffectedTablesFromQuery(connection, sql, this);
dbConnection = connection;
QueryTables.getAffectedTablesFromQuery(dbConnection, sql.join(';'), this);
},
function handleAffectedTablesAndLastUpdatedTime(err, result) {
if (req.profiler) {
req.profiler.done('queryTablesAndLastUpdated');
}
req.profiler.done('queryTablesAndLastUpdated');
assert.ifError(err);
// feed affected tables cache so it can be reused from, for instance, layergroup controller
self.layergroupAffectedTables.set(dbName, layergroupId, result);
@@ -319,6 +361,21 @@ MapController.prototype.afterLayergroupCreate = function(req, res, mapconfig, la
return null;
},
function fetchLayersStats(err) {
assert.ifError(err);
var next = this;
self.statsBackend.getStats(mapconfig, dbConnection, function(err, layersStats) {
if (err) {
return next(err);
}
if (layersStats.length > 0) {
layergroup.metadata.layers.forEach(function (layer, index) {
layer.meta.stats = layersStats[index];
});
}
return next();
});
},
function finish(err) {
done(err);
}
@@ -338,7 +395,7 @@ function getLastUpdatedTime(analysesResults, lastUpdateTime) {
}, lastUpdateTime);
}
function addAnalysesMetadata(username, layergroup, analysesResults, includeQuery) {
MapController.prototype.addAnalysesMetadata = function(username, layergroup, analysesResults, includeQuery) {
includeQuery = includeQuery || false;
analysesResults = analysesResults || [];
layergroup.metadata.analyses = [];
@@ -351,7 +408,7 @@ function addAnalysesMetadata(username, layergroup, analysesResults, includeQuery
var nodeResource = layergroup.layergroupid + '/analysis/node/' + node.id();
var nodeRepr = {
status: node.getStatus(),
url: getUrls(username, nodeResource)
url: this.resourceLocator.getUrls(username, nodeResource)
};
if (includeQuery) {
nodeRepr.query = node.getQuery();
@@ -363,30 +420,30 @@ function addAnalysesMetadata(username, layergroup, analysesResults, includeQuery
}
return nodesIdMap;
}, {})
}.bind(this), {})
});
});
}
}.bind(this));
};
// TODO this should take into account several URL patterns
function addDataviewsAndWidgetsUrls(username, layergroup, mapConfig) {
addDataviewsUrls(username, layergroup, mapConfig);
addWidgetsUrl(username, layergroup, mapConfig);
}
MapController.prototype.addDataviewsAndWidgetsUrls = function(username, layergroup, mapConfig) {
this.addDataviewsUrls(username, layergroup, mapConfig);
this.addWidgetsUrl(username, layergroup, mapConfig);
};
function addDataviewsUrls(username, layergroup, mapConfig) {
MapController.prototype.addDataviewsUrls = function(username, layergroup, mapConfig) {
layergroup.metadata.dataviews = layergroup.metadata.dataviews || {};
var dataviews = mapConfig.dataviews || {};
Object.keys(dataviews).forEach(function(dataviewName) {
var resource = layergroup.layergroupid + '/dataview/' + dataviewName;
layergroup.metadata.dataviews[dataviewName] = {
url: getUrls(username, resource)
url: this.resourceLocator.getUrls(username, resource)
};
});
}
}.bind(this));
};
function addWidgetsUrl(username, layergroup, mapConfig) {
MapController.prototype.addWidgetsUrl = function(username, layergroup, mapConfig) {
if (layergroup.metadata && Array.isArray(layergroup.metadata.layers) && Array.isArray(mapConfig.layers)) {
layergroup.metadata.layers = layergroup.metadata.layers.map(function(layer, layerIndex) {
var mapConfigLayer = mapConfig.layers[layerIndex];
@@ -396,26 +453,11 @@ function addWidgetsUrl(username, layergroup, mapConfig) {
var resource = layergroup.layergroupid + '/' + layerIndex + '/widget/' + widgetName;
layer.widgets[widgetName] = {
type: mapConfigLayer.options.widgets[widgetName].type,
url: getUrls(username, resource)
url: this.resourceLocator.getUrls(username, resource)
};
});
}.bind(this));
}
return layer;
});
}.bind(this));
}
}
function getUrls(username, resource) {
var cdnUrl = global.environment.serverMetadata && global.environment.serverMetadata.cdn_url;
if (cdnUrl) {
return {
http: 'http://' + cdnUrl.http + '/' + username + '/api/v1/map/' + resource,
https: 'https://' + cdnUrl.https + '/' + username + '/api/v1/map/' + resource
};
} else {
var port = global.environment.port;
return {
http: 'http://' + username + '.' + 'localhost.lan:' + port + '/api/v1/map/' + resource
};
}
}
};

View File

@@ -8,6 +8,7 @@ var BaseController = require('./base');
var cors = require('../middleware/cors');
var userMiddleware = require('../middleware/user');
var allowQueryParams = require('../middleware/allow-query-params');
function NamedMapsController(authApi, pgConnection, namedMapProviderCache, tileBackend, previewBackend,
surrogateKeysCache, tablesExtentApi, metadataBackend) {
@@ -31,7 +32,7 @@ NamedMapsController.prototype.register = function(app) {
this.tile.bind(this));
app.get(app.base_url_mapconfig +
'/static/named/:template_id/:width/:height.:format', cors(), userMiddleware,
'/static/named/:template_id/:width/:height.:format', cors(), userMiddleware, allowQueryParams(['layer']),
this.staticMap.bind(this));
};
@@ -100,9 +101,7 @@ NamedMapsController.prototype.tile = function(req, res) {
self.tileBackend.getTile(namedMapProvider, req.params, this);
},
function handleImage(err, tile, headers, stats) {
if (req.profiler) {
req.profiler.add(stats);
}
req.profiler.add(stats);
if (err) {
self.sendError(req, res, err, 'NAMED_MAP_TILE');
} else {
@@ -137,9 +136,15 @@ NamedMapsController.prototype.staticMap = function(req, res) {
this
);
},
function prepareImageOptions(err, _namedMapProvider) {
function prepareLayerVisibility(err, _namedMapProvider) {
assert.ifError(err);
namedMapProvider = _namedMapProvider;
self.prepareLayerFilterFromPreviewLayers(cdbUser, req, namedMapProvider, this);
},
function prepareImageOptions(err) {
assert.ifError(err);
self.getStaticImageOptions(cdbUser, req.params, namedMapProvider, this);
},
function getImage(err, imageOpts) {
@@ -170,10 +175,8 @@ NamedMapsController.prototype.staticMap = function(req, res) {
});
},
function handleImage(err, image, headers, stats) {
if (req.profiler) {
req.profiler.done('render-' + format);
req.profiler.add(stats || {});
}
req.profiler.done('render-' + format);
req.profiler.add(stats || {});
if (err) {
self.sendError(req, res, err, 'STATIC_VIZ_MAP');
@@ -184,6 +187,45 @@ NamedMapsController.prototype.staticMap = function(req, res) {
);
};
NamedMapsController.prototype.prepareLayerFilterFromPreviewLayers = function (user, req, namedMapProvider, callback) {
var self = this;
namedMapProvider.getTemplate(function (err, template) {
if (err) {
return callback(err);
}
if (!template || !template.view || !template.view.preview_layers) {
return callback();
}
var previewLayers = template.view.preview_layers;
var layerVisibilityFilter = [];
template.layergroup.layers.forEach(function (layer, index) {
if (previewLayers[''+index] !== false && previewLayers[layer.id] !== false) {
layerVisibilityFilter.push(''+index);
}
});
if (!layerVisibilityFilter.length) {
return callback();
}
// overwrites 'all' default filter
req.params.layer = layerVisibilityFilter.join(',');
// recreates the provider
self.namedMapProviderCache.get(
user,
req.params.template_id,
req.query.config,
req.query.auth_token,
req.params,
callback
);
});
};
var DEFAULT_ZOOM_CENTER = {
zoom: 1,
center: {

View File

@@ -91,9 +91,7 @@ NamedMapsAdminController.prototype.update = function(req, res) {
NamedMapsAdminController.prototype.retrieve = function(req, res) {
var self = this;
if (req.profiler) {
req.profiler.start('windshaft-cartodb.get_template');
}
req.profiler.start('windshaft-cartodb.get_template');
var cdbuser = req.context.user;
var tpl_id;
@@ -127,9 +125,7 @@ NamedMapsAdminController.prototype.retrieve = function(req, res) {
NamedMapsAdminController.prototype.destroy = function(req, res) {
var self = this;
if (req.profiler) {
req.profiler.start('windshaft-cartodb.delete_template');
}
req.profiler.start('windshaft-cartodb.delete_template');
var cdbuser = req.context.user;
var tpl_id;
@@ -154,9 +150,7 @@ NamedMapsAdminController.prototype.destroy = function(req, res) {
NamedMapsAdminController.prototype.list = function(req, res) {
var self = this;
if ( req.profiler ) {
req.profiler.start('windshaft-cartodb.get_template_list');
}
req.profiler.start('windshaft-cartodb.get_template_list');
var cdbuser = req.context.user;

View File

@@ -0,0 +1,9 @@
module.exports = function allowQueryParams(params) {
if (!Array.isArray(params)) {
throw new Error('allowQueryParams must receive an Array of params');
}
return function allowQueryParamsMiddleware(req, res, next) {
req.context.allowedQueryParams = params;
next();
};
};

View File

@@ -5,11 +5,32 @@ var debug = require('debug')('windshaft:widget:aggregation');
var dot = require('dot');
dot.templateSettings.strip = false;
var filteredQueryTpl = dot.template([
'filtered_source AS (',
' SELECT *',
' FROM ({{=it._query}}) _cdb_filtered_source',
' {{?it._aggregationColumn && it._isFloatColumn}}WHERE',
' {{=it._aggregationColumn}} != \'infinity\'::float',
' AND',
' {{=it._aggregationColumn}} != \'-infinity\'::float',
' AND',
' {{=it._aggregationColumn}} != \'NaN\'::float{{?}}',
')'
].join(' \n'));
var summaryQueryTpl = dot.template([
'summary AS (',
' SELECT',
' count(1) AS count,',
' sum(CASE WHEN {{=it._column}} IS NULL THEN 1 ELSE 0 END) AS nulls_count',
' {{?it._isFloatColumn}},sum(',
' CASE',
' WHEN {{=it._aggregationColumn}} = \'infinity\'::float OR {{=it._aggregationColumn}} = \'-infinity\'::float',
' THEN 1',
' ELSE 0',
' END',
' ) AS infinities_count,',
' sum(CASE WHEN {{=it._aggregationColumn}} = \'NaN\'::float THEN 1 ELSE 0 END) AS nans_count{{?}}',
' FROM ({{=it._query}}) _cdb_aggregation_nulls',
')'
].join('\n'));
@@ -18,35 +39,51 @@ var rankedCategoriesQueryTpl = dot.template([
'categories AS(',
' SELECT {{=it._column}} AS category, {{=it._aggregation}} AS value,',
' row_number() OVER (ORDER BY {{=it._aggregation}} desc) as rank',
' FROM ({{=it._query}}) _cdb_aggregation_all',
' FROM filtered_source',
' {{?it._aggregationColumn!==null}}WHERE {{=it._aggregationColumn}} IS NOT NULL{{?}}',
' GROUP BY {{=it._column}}',
' ORDER BY 2 DESC',
')'
].join('\n'));
var categoriesSummaryQueryTpl = dot.template([
'categories_summary AS(',
' SELECT count(1) categories_count, max(value) max_val, min(value) min_val',
var categoriesSummaryMinMaxQueryTpl = dot.template([
'categories_summary_min_max AS(',
' SELECT max(value) max_val, min(value) min_val',
' FROM categories',
')'
].join('\n'));
var categoriesSummaryCountQueryTpl = dot.template([
'categories_summary_count AS(',
' SELECT count(1) AS categories_count',
' FROM (',
' SELECT {{=it._column}} AS category',
' FROM ({{=it._query}}) _cdb_categories',
' GROUP BY {{=it._column}}',
' ) _cdb_categories_count',
')'
].join('\n'));
var rankedAggregationQueryTpl = dot.template([
'SELECT CAST(category AS text), value, false as agg, nulls_count, min_val, max_val, count, categories_count',
' FROM categories, summary, categories_summary',
'SELECT CAST(category AS text), value, false as agg, nulls_count, min_val, max_val,',
' count, categories_count{{?it._isFloatColumn}}, nans_count, infinities_count{{?}}',
' FROM categories, summary, categories_summary_min_max, categories_summary_count',
' WHERE rank < {{=it._limit}}',
'UNION ALL',
'SELECT \'Other\' category, sum(value), true as agg, nulls_count, min_val, max_val, count, categories_count',
' FROM categories, summary, categories_summary',
'SELECT \'Other\' category, {{=it._aggregationFn}}(value) as value, true as agg, nulls_count,',
' min_val, max_val, count, categories_count{{?it._isFloatColumn}}, nans_count, infinities_count{{?}}',
' FROM categories, summary, categories_summary_min_max, categories_summary_count',
' WHERE rank >= {{=it._limit}}',
'GROUP BY nulls_count, min_val, max_val, count, categories_count'
'GROUP BY nulls_count, min_val, max_val, count,',
' categories_count{{?it._isFloatColumn}}, nans_count, infinities_count{{?}}'
].join('\n'));
var aggregationQueryTpl = dot.template([
'SELECT CAST({{=it._column}} AS text) AS category, {{=it._aggregation}} AS value, false as agg,',
' nulls_count, min_val, max_val, count, categories_count',
'FROM ({{=it._query}}) _cdb_aggregation_all, summary, categories_summary',
'GROUP BY category, nulls_count, min_val, max_val, count, categories_count',
' nulls_count, min_val, max_val, count, categories_count{{?it._isFloatColumn}}, nans_count, infinities_count{{?}}',
'FROM ({{=it._query}}) _cdb_aggregation_all, summary, categories_summary_min_max, categories_summary_count',
'GROUP BY category, nulls_count, min_val, max_val, count,',
' categories_count{{?it._isFloatColumn}}, nans_count, infinities_count{{?}}',
'ORDER BY value DESC'
].join('\n'));
@@ -71,7 +108,7 @@ var TYPE = 'aggregation';
}
}
*/
function Aggregation(query, options) {
function Aggregation(query, options, queries) {
if (!_.isString(options.column)) {
throw new Error('Aggregation expects `column` in widget options');
}
@@ -95,9 +132,11 @@ function Aggregation(query, options) {
BaseWidget.apply(this);
this.query = query;
this.queries = queries;
this.column = options.column;
this.aggregation = options.aggregation;
this.aggregationColumn = options.aggregationColumn;
this._isFloatColumn = null;
}
Aggregation.prototype = new BaseWidget();
@@ -106,33 +145,39 @@ Aggregation.prototype.constructor = Aggregation;
module.exports = Aggregation;
Aggregation.prototype.sql = function(psql, override, callback) {
var self = this;
if (!callback) {
callback = override;
override = {};
}
if (this.aggregationColumn && this._isFloatColumn === null) {
this._isFloatColumn = false;
this.getColumnType(psql, this.aggregationColumn, this.queries.no_filters, function (err, type) {
if (!err && !!type) {
self._isFloatColumn = type.float;
}
self.sql(psql, override, callback);
});
return null;
}
var _query = this.query;
var aggregationSql;
if (!!override.ownFilter) {
aggregationSql = [
"WITH",
[
summaryQueryTpl({
_query: _query,
_column: this.column
}),
rankedCategoriesQueryTpl({
_query: _query,
_column: this.column,
_aggregation: this.getAggregationSql()
}),
categoriesSummaryQueryTpl({
_query: _query,
_column: this.column
})
].join(',\n'),
this.getCategoriesCTESql(
_query,
this.column,
this.aggregation,
this.aggregationColumn,
this._isFloatColumn
),
aggregationQueryTpl({
_isFloatColumn: this._isFloatColumn,
_query: _query,
_column: this.column,
_aggregation: this.getAggregationSql(),
@@ -141,25 +186,18 @@ Aggregation.prototype.sql = function(psql, override, callback) {
].join('\n');
} else {
aggregationSql = [
"WITH",
[
summaryQueryTpl({
_query: _query,
_column: this.column
}),
rankedCategoriesQueryTpl({
_query: _query,
_column: this.column,
_aggregation: this.getAggregationSql()
}),
categoriesSummaryQueryTpl({
_query: _query,
_column: this.column
})
].join(',\n'),
this.getCategoriesCTESql(
_query,
this.column,
this.aggregation,
this.aggregationColumn,
this._isFloatColumn
),
rankedAggregationQueryTpl({
_isFloatColumn: this._isFloatColumn,
_query: _query,
_column: this.column,
_aggregationFn: this.aggregation !== 'count' ? this.aggregation : 'sum',
_limit: CATEGORIES_LIMIT
})
].join('\n');
@@ -170,6 +208,40 @@ Aggregation.prototype.sql = function(psql, override, callback) {
return callback(null, aggregationSql);
};
Aggregation.prototype.getCategoriesCTESql = function(query, column, aggregation, aggregationColumn, isFloatColumn) {
return [
"WITH",
[
filteredQueryTpl({
_isFloatColumn: isFloatColumn,
_query: this.query,
_column: this.column,
_aggregationColumn: aggregation !== 'count' ? aggregationColumn : null
}),
summaryQueryTpl({
_isFloatColumn: isFloatColumn,
_query: query,
_column: column,
_aggregationColumn: aggregation !== 'count' ? aggregationColumn : null
}),
rankedCategoriesQueryTpl({
_query: query,
_column: column,
_aggregation: this.getAggregationSql(),
_aggregationColumn: aggregation !== 'count' ? aggregationColumn : null
}),
categoriesSummaryMinMaxQueryTpl({
_query: query,
_column: column
}),
categoriesSummaryCountQueryTpl({
_query: query,
_column: column
})
].join(',\n')
].join('\n');
};
var aggregationFnQueryTpl = dot.template('{{=it._aggregationFn}}({{=it._aggregationColumn}})');
Aggregation.prototype.getAggregationSql = function() {
return aggregationFnQueryTpl({
@@ -182,6 +254,8 @@ Aggregation.prototype.format = function(result) {
var categories = [];
var count = 0;
var nulls = 0;
var nans = 0;
var infinities = 0;
var minValue = 0;
var maxValue = 0;
var categoriesCount = 0;
@@ -191,12 +265,15 @@ Aggregation.prototype.format = function(result) {
var firstRow = result.rows[0];
count = firstRow.count;
nulls = firstRow.nulls_count;
nans = firstRow.nans_count;
infinities = firstRow.infinities_count;
minValue = firstRow.min_val;
maxValue = firstRow.max_val;
categoriesCount = firstRow.categories_count;
result.rows.forEach(function(row) {
categories.push(_.omit(row, 'count', 'nulls_count', 'min_val', 'max_val', 'categories_count'));
categories.push(_.omit(row, 'count', 'nulls_count', 'min_val',
'max_val', 'categories_count', 'nans_count', 'infinities_count'));
});
}
@@ -204,6 +281,8 @@ Aggregation.prototype.format = function(result) {
aggregation: this.aggregation,
count: count,
nulls: nulls,
nans: nans,
infinities: infinities,
min: minValue,
max: maxValue,
categoriesCount: categoriesCount,
@@ -242,6 +321,8 @@ Aggregation.prototype.search = function(psql, userQuery, callback) {
var self = this;
var _userQuery = psql.escapeLiteral('%' + userQuery + '%');
var _value = this.aggregation !== 'count' && this.aggregationColumn ?
this.aggregation + '(' + this.aggregationColumn + ')' : 'count(1)';
// TODO unfiltered will be wrong as filters are already applied at this point
var query = searchQueryTpl({
@@ -254,7 +335,7 @@ Aggregation.prototype.search = function(psql, userQuery, callback) {
_searchFiltered: filterCategoriesQueryTpl({
_query: this.query,
_column: this.column,
_value: 'count(1)',
_value: _value,
_userQuery: _userQuery
})
});

View File

@@ -1,3 +1,6 @@
var dot = require('dot');
dot.templateSettings.strip = false;
function BaseDataview() {}
module.exports = BaseDataview;
@@ -24,3 +27,42 @@ BaseDataview.prototype.getResult = function(psql, override, callback) {
BaseDataview.prototype.search = function(psql, userQuery, callback) {
return callback(null, this.format({ rows: [] }));
};
var FLOAT_OIDS = {
700: true,
701: true,
1700: true
};
var DATE_OIDS = {
1082: true,
1114: true,
1184: true
};
var columnTypeQueryTpl = dot.template(
'SELECT pg_typeof({{=it.column}})::oid FROM ({{=it.query}}) _cdb_column_type limit 1'
);
BaseDataview.prototype.getColumnType = function (psql, column, query, callback) {
var readOnlyTransaction = true;
var columnTypeQuery = columnTypeQueryTpl({
column: column, query: query
});
psql.query(columnTypeQuery, function(err, result) {
if (err) {
return callback(err);
}
var pgType = result.rows[0].pg_typeof;
callback(null, getPGTypeName(pgType));
}, readOnlyTransaction);
};
function getPGTypeName (pgType) {
return {
float: FLOAT_OIDS.hasOwnProperty(pgType),
date: DATE_OIDS.hasOwnProperty(pgType)
};
}

View File

@@ -11,7 +11,7 @@ var DataviewFactory = {
if (!this.dataviews[type]) {
throw new Error('Invalid dataview type: "' + type + '"');
}
return new this.dataviews[type](query, dataviewDefinition.options);
return new this.dataviews[type](query, dataviewDefinition.options, dataviewDefinition.sql);
}
};

View File

@@ -7,9 +7,19 @@ dot.templateSettings.strip = false;
var formulaQueryTpl = dot.template([
'SELECT',
'{{=it._operation}}({{=it._column}}) AS result,',
'(SELECT count(1) FROM ({{=it._query}}) _cdb_formula_nulls WHERE {{=it._column}} IS NULL) AS nulls_count',
'FROM ({{=it._query}}) _cdb_formula'
' {{=it._operation}}({{=it._column}}) AS result,',
' (SELECT count(1) FROM ({{=it._query}}) _cdb_formula_nulls WHERE {{=it._column}} IS NULL) AS nulls_count',
' {{?it._isFloatColumn}},(SELECT count(1) FROM ({{=it._query}}) _cdb_formula_nulls',
' WHERE {{=it._column}} = \'infinity\'::float OR {{=it._column}} = \'-infinity\'::float) AS infinities_count',
' ,(SELECT count(1) FROM ({{=it._query}}) _cdb_formula_nulls',
' WHERE {{=it._column}} = \'NaN\'::float) AS nans_count{{?}}',
'FROM ({{=it._query}}) _cdb_formula',
'{{?it._isFloatColumn && it._operation !== \'count\'}}WHERE',
' {{=it._column}} != \'infinity\'::float',
'AND',
' {{=it._column}} != \'-infinity\'::float',
'AND',
' {{=it._column}} != \'NaN\'::float{{?}}'
].join('\n'));
var VALID_OPERATIONS = {
@@ -31,7 +41,7 @@ var TYPE = 'formula';
}
}
*/
function Formula(query, options) {
function Formula(query, options, queries) {
if (!_.isString(options.operation)) {
throw new Error('Formula expects `operation` in widget options');
}
@@ -47,8 +57,10 @@ function Formula(query, options) {
BaseWidget.apply(this);
this.query = query;
this.queries = queries;
this.column = options.column || '1';
this.operation = options.operation;
this._isFloatColumn = null;
}
Formula.prototype = new BaseWidget();
@@ -57,14 +69,27 @@ Formula.prototype.constructor = Formula;
module.exports = Formula;
Formula.prototype.sql = function(psql, override, callback) {
var self = this;
if (!callback) {
callback = override;
override = {};
}
var _query = this.query;
if (this._isFloatColumn === null) {
this._isFloatColumn = false;
this.getColumnType(psql, this.column, this.queries.no_filters, function (err, type) {
if (!err && !!type) {
self._isFloatColumn = type.float;
}
self.sql(psql, override, callback);
});
return null;
}
var formulaSql = formulaQueryTpl({
_query: _query,
_isFloatColumn: this._isFloatColumn,
_query: this.query,
_operation: this.operation,
_column: this.column
});
@@ -78,13 +103,17 @@ Formula.prototype.format = function(result) {
var formattedResult = {
operation: this.operation,
result: 0,
nulls: 0
nulls: 0,
nans: 0,
infinities: 0
};
if (result.rows.length) {
formattedResult.operation = this.operation;
formattedResult.result = result.rows[0].result;
formattedResult.nulls = result.rows[0].nulls_count;
formattedResult.nans = result.rows[0].nans_count;
formattedResult.infinities = result.rows[0].infinities_count;
}
return formattedResult;

View File

@@ -5,20 +5,32 @@ var debug = require('debug')('windshaft:dataview:histogram');
var dot = require('dot');
dot.templateSettings.strip = false;
var columnTypeQueryTpl = dot.template(
'SELECT pg_typeof({{=it.column}})::oid FROM ({{=it.query}}) _cdb_histogram_column_type limit 1'
);
var columnCastTpl = dot.template("date_part('epoch', {{=it.column}})");
var BIN_MIN_NUMBER = 6;
var BIN_MAX_NUMBER = 48;
var filteredQueryTpl = dot.template([
'filtered_source AS (',
' SELECT *',
' FROM ({{=it._query}}) _cdb_filtered_source',
' WHERE',
' {{=it._column}} IS NOT NULL',
' {{?it._isFloatColumn}}AND',
' {{=it._column}} != \'infinity\'::float',
' AND',
' {{=it._column}} != \'-infinity\'::float',
' AND',
' {{=it._column}} != \'NaN\'::float{{?}}',
')'
].join(' \n'));
var basicsQueryTpl = dot.template([
'basics AS (',
' SELECT',
' max({{=it._column}}) AS max_val, min({{=it._column}}) AS min_val,',
' avg({{=it._column}}) AS avg_val, count(1) AS total_rows',
' FROM ({{=it._query}}) _cdb_basics',
' FROM filtered_source',
')'
].join(' \n'));
@@ -27,7 +39,7 @@ var overrideBasicsQueryTpl = dot.template([
' SELECT',
' max({{=it._end}}) AS max_val, min({{=it._start}}) AS min_val,',
' avg({{=it._column}}) AS avg_val, count(1) AS total_rows',
' FROM ({{=it._query}}) _cdb_basics',
' FROM filtered_source',
')'
].join('\n'));
@@ -38,7 +50,7 @@ var iqrQueryTpl = dot.template([
' SELECT quartile, max(_cdb_iqr_column) AS quartile_max from (',
' SELECT {{=it._column}} AS _cdb_iqr_column, ntile(4) over (order by {{=it._column}}',
' ) AS quartile',
' FROM ({{=it._query}}) _cdb_rank) _cdb_quartiles',
' FROM filtered_source) _cdb_quartiles',
' WHERE quartile = 1 or quartile = 3',
' GROUP BY quartile',
' ) _cdb_iqr',
@@ -57,7 +69,7 @@ var binsQueryTpl = dot.template([
' )',
' )',
' END AS bins_number',
' FROM basics, iqrange, ({{=it._query}}) _cdb_bins',
' FROM basics, iqrange, filtered_source',
' LIMIT 1',
')'
].join('\n'));
@@ -77,11 +89,34 @@ var nullsQueryTpl = dot.template([
')'
].join('\n'));
var infinitiesQueryTpl = dot.template([
'infinities AS (',
' SELECT',
' count(*) AS infinities_count',
' FROM ({{=it._query}}) _cdb_histogram_infinities',
' WHERE',
' {{=it._column}} = \'infinity\'::float',
' OR',
' {{=it._column}} = \'-infinity\'::float',
')'
].join('\n'));
var nansQueryTpl = dot.template([
'nans AS (',
' SELECT',
' count(*) AS nans_count',
' FROM ({{=it._query}}) _cdb_histogram_infinities',
' WHERE {{=it._column}} = \'NaN\'::float',
')'
].join('\n'));
var histogramQueryTpl = dot.template([
'SELECT',
' (max_val - min_val) / cast(bins_number as float) AS bin_width,',
' bins_number,',
' nulls_count,',
' {{?it._isFloatColumn}}infinities_count,',
' nans_count,{{?}}',
' avg_val,',
' CASE WHEN min_val = max_val',
' THEN 0',
@@ -91,9 +126,9 @@ var histogramQueryTpl = dot.template([
' max({{=it._column}})::numeric AS max,',
' avg({{=it._column}})::numeric AS avg,',
' count(*) AS freq',
'FROM ({{=it._query}}) _cdb_histogram, basics, nulls, bins',
'WHERE {{=it._column}} IS NOT NULL',
'GROUP BY bin, bins_number, bin_width, nulls_count, avg_val',
'FROM filtered_source, basics, nulls, bins{{?it._isFloatColumn}}, infinities, nans{{?}}',
'GROUP BY bin, bins_number, bin_width, nulls_count,',
' avg_val{{?it._isFloatColumn}}, infinities_count, nans_count{{?}}',
'ORDER BY bin'
].join('\n'));
@@ -109,12 +144,13 @@ var TYPE = 'histogram';
}
}
*/
function Histogram(query, options) {
function Histogram(query, options, queries) {
if (!_.isString(options.column)) {
throw new Error('Histogram expects `column` in widget options');
}
this.query = query;
this.queries = queries;
this.column = options.column;
this.bins = options.bins;
@@ -126,50 +162,49 @@ Histogram.prototype.constructor = Histogram;
module.exports = Histogram;
var DATE_OIDS = {
1082: true,
1114: true,
1184: true
};
Histogram.prototype.sql = function(psql, override, callback) {
var self = this;
if (!callback) {
callback = override;
override = {};
}
var self = this;
var _column = this.column;
var columnTypeQuery = columnTypeQueryTpl({
column: _column, query: this.query
});
if (this._columnType === null) {
psql.query(columnTypeQuery, function(err, result) {
this.getColumnType(psql, this.column, this.queries.no_filters, function (err, type) {
// assume numeric, will fail later
self._columnType = 'numeric';
if (!err && !!result.rows[0]) {
var pgType = result.rows[0].pg_typeof;
if (DATE_OIDS.hasOwnProperty(pgType)) {
self._columnType = 'date';
}
if (!err && !!type) {
self._columnType = Object.keys(type).find(function (key) {
return type[key];
});
}
self.sql(psql, override, callback);
}, true); // use read-only transaction
return null;
}
var histogramSql = this._buildQuery(override);
return callback(null, histogramSql);
};
Histogram.prototype._buildQuery = function (override) {
var filteredQuery, basicsQuery, binsQuery;
var _column = this.column;
var _query = this.query;
if (this._columnType === 'date') {
_column = columnCastTpl({column: _column});
}
var _query = this.query;
filteredQuery = filteredQueryTpl({
_isFloatColumn: this._columnType === 'float',
_query: _query,
_column: _column
});
var basicsQuery, binsQuery;
if (override && _.has(override, 'start') && _.has(override, 'end') && _.has(override, 'bins')) {
if (this._shouldOverride(override)) {
debug('overriding with %j', override);
basicsQuery = overrideBasicsQueryTpl({
_query: _query,
@@ -189,7 +224,7 @@ Histogram.prototype.sql = function(psql, override, callback) {
_column: _column
});
if (override && _.has(override, 'bins')) {
if (this._shouldOverrideBins(override)) {
binsQuery = [
overrideBinsQueryTpl({
_bins: override.bins
@@ -210,18 +245,34 @@ Histogram.prototype.sql = function(psql, override, callback) {
}
}
var cteSql = [
filteredQuery,
basicsQuery,
binsQuery,
nullsQueryTpl({
_query: _query,
_column: _column
})
];
var histogramSql = [
"WITH",
[
basicsQuery,
binsQuery,
nullsQueryTpl({
if (this._columnType === 'float') {
cteSql.push(
infinitiesQueryTpl({
_query: _query,
_column: _column
}),
nansQueryTpl({
_query: _query,
_column: _column
})
].join(',\n'),
);
}
var histogramSql = [
"WITH",
cteSql.join(',\n'),
histogramQueryTpl({
_isFloatColumn: this._columnType === 'float',
_query: _query,
_column: _column
})
@@ -229,7 +280,15 @@ Histogram.prototype.sql = function(psql, override, callback) {
debug(histogramSql);
return callback(null, histogramSql);
return histogramSql;
};
Histogram.prototype._shouldOverride = function (override) {
return override && _.has(override, 'start') && _.has(override, 'end') && _.has(override, 'bins');
};
Histogram.prototype._shouldOverrideBins = function (override) {
return override && _.has(override, 'bins');
};
Histogram.prototype.format = function(result, override) {
@@ -240,6 +299,8 @@ Histogram.prototype.format = function(result, override) {
var width = getWidth(override);
var binsStart = getBinStart(override);
var nulls = 0;
var infinities = 0;
var nans = 0;
var avg;
if (result.rows.length) {
@@ -248,10 +309,12 @@ Histogram.prototype.format = function(result, override) {
width = firstRow.bin_width || width;
avg = firstRow.avg_val;
nulls = firstRow.nulls_count;
infinities = firstRow.infinities_count;
nans = firstRow.nans_count;
binsStart = override.hasOwnProperty('start') ? getBinStart(override) : firstRow.min;
buckets = result.rows.map(function(row) {
return _.omit(row, 'bins_number', 'bin_width', 'nulls_count', 'avg_val');
return _.omit(row, 'bins_number', 'bin_width', 'nulls_count', 'infinities_count', 'nans_count', 'avg_val');
});
}
@@ -260,6 +323,8 @@ Histogram.prototype.format = function(result, override) {
bins_count: binsCount,
bins_start: binsStart,
nulls: nulls,
infinities: infinities,
nans: nans,
avg: avg,
bins: buckets
};

View File

@@ -1,14 +1,36 @@
var BaseOverviewsDataview = require('./base');
var BaseDataview = require('../aggregation');
var debug = require('debug')('windshaft:widget:aggregation:overview');
var dot = require('dot');
dot.templateSettings.strip = false;
var filteredQueryTpl = dot.template([
'filtered_source AS (',
' SELECT *',
' FROM ({{=it._query}}) _cdb_filtered_source',
' {{?it._aggregationColumn && it._isFloatColumn}}WHERE',
' {{=it._aggregationColumn}} != \'infinity\'::float',
' AND',
' {{=it._aggregationColumn}} != \'-infinity\'::float',
' AND',
' {{=it._aggregationColumn}} != \'NaN\'::float{{?}}',
')'
].join(' \n'));
var summaryQueryTpl = dot.template([
'summary AS (',
' SELECT',
' sum(_feature_count) AS count,',
' sum(CASE WHEN {{=it._column}} IS NULL THEN 1 ELSE 0 END) AS nulls_count',
' {{?it._isFloatColumn}},sum(',
' CASE',
' WHEN {{=it._aggregationColumn}} = \'infinity\'::float OR {{=it._aggregationColumn}} = \'-infinity\'::float',
' THEN 1',
' ELSE 0',
' END',
' ) AS infinities_count,',
' sum(CASE WHEN {{=it._aggregationColumn}} = \'NaN\'::float THEN 1 ELSE 0 END) AS nans_count{{?}}',
' FROM ({{=it._query}}) _cdb_aggregation_nulls',
')'
].join('\n'));
@@ -17,47 +39,65 @@ var rankedCategoriesQueryTpl = dot.template([
'categories AS(',
' SELECT {{=it._column}} AS category, {{=it._aggregation}} AS value,',
' row_number() OVER (ORDER BY {{=it._aggregation}} desc) as rank',
' FROM ({{=it._query}}) _cdb_aggregation_all',
' FROM filtered_source',
' {{?it._aggregationColumn!==null}}WHERE {{=it._aggregationColumn}} IS NOT NULL{{?}}',
' GROUP BY {{=it._column}}',
' ORDER BY 2 DESC',
')'
].join('\n'));
var categoriesSummaryQueryTpl = dot.template([
'categories_summary AS(',
' SELECT count(1) categories_count, max(value) max_val, min(value) min_val',
var categoriesSummaryMinMaxQueryTpl = dot.template([
'categories_summary_min_max AS(',
' SELECT max(value) max_val, min(value) min_val',
' FROM categories',
')'
].join('\n'));
var categoriesSummaryCountQueryTpl = dot.template([
'categories_summary_count AS(',
' SELECT count(1) AS categories_count',
' FROM (',
' SELECT {{=it._column}} AS category',
' FROM filtered_source',
' GROUP BY {{=it._column}}',
' ) _cdb_categories_count',
')'
].join('\n'));
var rankedAggregationQueryTpl = dot.template([
'SELECT CAST(category AS text), value, false as agg, nulls_count, min_val, max_val, count, categories_count',
' FROM categories, summary, categories_summary',
'SELECT CAST(category AS text), value, false as agg, nulls_count, min_val, max_val,',
' count, categories_count{{?it._isFloatColumn}}, nans_count, infinities_count{{?}}',
' FROM categories, summary, categories_summary_min_max, categories_summary_count',
' WHERE rank < {{=it._limit}}',
'UNION ALL',
'SELECT \'Other\' category, sum(value), true as agg, nulls_count, min_val, max_val, count, categories_count',
' FROM categories, summary, categories_summary',
'SELECT \'Other\' category, sum(value), true as agg, nulls_count, min_val, max_val,',
' count, categories_count{{?it._isFloatColumn}}, nans_count, infinities_count{{?}}',
' FROM categories, summary, categories_summary_min_max, categories_summary_count',
' WHERE rank >= {{=it._limit}}',
'GROUP BY nulls_count, min_val, max_val, count, categories_count'
'GROUP BY nulls_count, min_val, max_val, count,',
' categories_count{{?it._isFloatColumn}}, nans_count, infinities_count{{?}}'
].join('\n'));
var aggregationQueryTpl = dot.template([
'SELECT CAST({{=it._column}} AS text) AS category, {{=it._aggregation}} AS value, false as agg,',
' nulls_count, min_val, max_val, count, categories_count',
'FROM ({{=it._query}}) _cdb_aggregation_all, summary, categories_summary',
'GROUP BY category, nulls_count, min_val, max_val, count, categories_count',
' nulls_count, min_val, max_val, count, categories_count{{?it._isFloatColumn}}, nans_count, infinities_count{{?}}',
'FROM filtered_source, summary, categories_summary_min_max, categories_summary_count',
'GROUP BY category, nulls_count, min_val, max_val, count,',
' categories_count{{?it._isFloatColumn}}, nans_count, infinities_count{{?}}',
'ORDER BY value DESC'
].join('\n'));
var CATEGORIES_LIMIT = 6;
function Aggregation(query, options, queryRewriter, queryRewriteData, params) {
BaseOverviewsDataview.call(this, query, options, BaseDataview, queryRewriter, queryRewriteData, params);
function Aggregation(query, options, queryRewriter, queryRewriteData, params, queries) {
BaseOverviewsDataview.call(this, query, options, BaseDataview, queryRewriter, queryRewriteData, params, queries);
this.query = query;
this.queries = queries;
this.column = options.column;
this.aggregation = options.aggregation;
this.aggregationColumn = options.aggregationColumn;
this._isFloatColumn = null;
}
Aggregation.prototype = Object.create(BaseOverviewsDataview.prototype);
@@ -65,34 +105,62 @@ Aggregation.prototype.constructor = Aggregation;
module.exports = Aggregation;
Aggregation.prototype.sql = function(psql, filters, override, callback) {
Aggregation.prototype.sql = function(psql, override, callback) {
var self = this;
if (!callback) {
callback = override;
override = {};
}
var _query = this.rewrittenQuery(this.query);
var _aggregationColumn = this.aggregation !== 'count' ? this.aggregationColumn : null;
if (this.aggregationColumn && this._isFloatColumn === null) {
this._isFloatColumn = false;
this.getColumnType(psql, this.aggregationColumn, this.queries.no_filters, function (err, type) {
if (!err && !!type) {
self._isFloatColumn = type.float;
}
self.sql(psql, override, callback);
});
return null;
}
var aggregationSql;
if (!!override.ownFilter) {
aggregationSql = [
"WITH",
[
summaryQueryTpl({
filteredQueryTpl({
_isFloatColumn: this._isFloatColumn,
_query: _query,
_column: this.column
_column: this.column,
_aggregationColumn: _aggregationColumn
}),
summaryQueryTpl({
_isFloatColumn: this._isFloatColumn,
_query: _query,
_column: this.column,
_aggregationColumn: _aggregationColumn
}),
rankedCategoriesQueryTpl({
_query: _query,
_column: this.column,
_aggregation: this.getAggregationSql()
_aggregation: this.getAggregationSql(),
_aggregationColumn: _aggregationColumn
}),
categoriesSummaryQueryTpl({
categoriesSummaryMinMaxQueryTpl({
_query: _query,
_column: this.column
}),
categoriesSummaryCountQueryTpl({
_query: _query,
_column: this.column
})
].join(',\n'),
aggregationQueryTpl({
_isFloatColumn: this._isFloatColumn,
_query: _query,
_column: this.column,
_aggregation: this.getAggregationSql(),
@@ -103,21 +171,35 @@ Aggregation.prototype.sql = function(psql, filters, override, callback) {
aggregationSql = [
"WITH",
[
summaryQueryTpl({
filteredQueryTpl({
_isFloatColumn: this._isFloatColumn,
_query: _query,
_column: this.column
_column: this.column,
_aggregationColumn: _aggregationColumn
}),
summaryQueryTpl({
_isFloatColumn: this._isFloatColumn,
_query: _query,
_column: this.column,
_aggregationColumn: _aggregationColumn
}),
rankedCategoriesQueryTpl({
_query: _query,
_column: this.column,
_aggregation: this.getAggregationSql()
_aggregation: this.getAggregationSql(),
_aggregationColumn: _aggregationColumn
}),
categoriesSummaryQueryTpl({
categoriesSummaryMinMaxQueryTpl({
_query: _query,
_column: this.column
}),
categoriesSummaryCountQueryTpl({
_query: _query,
_column: this.column
})
].join(',\n'),
rankedAggregationQueryTpl({
_isFloatColumn: this._isFloatColumn,
_query: _query,
_column: this.column,
_limit: CATEGORIES_LIMIT
@@ -125,6 +207,8 @@ Aggregation.prototype.sql = function(psql, filters, override, callback) {
].join('\n');
}
debug(aggregationSql);
return callback(null, aggregationSql);
};

View File

@@ -1,14 +1,15 @@
var _ = require('underscore');
var BaseDataview = require('../base');
function BaseOverviewsDataview(query, queryOptions, BaseDataview, queryRewriter, queryRewriteData, options) {
function BaseOverviewsDataview(query, queryOptions, BaseDataview, queryRewriter, queryRewriteData, options, queries) {
this.BaseDataview = BaseDataview;
this.query = query;
this.queryOptions = queryOptions;
this.queryRewriter = queryRewriter;
this.queryRewriteData = queryRewriteData;
this.options = options;
this.baseDataview = new this.BaseDataview(this.query, this.queryOptions);
this.queries = queries;
this.baseDataview = new this.BaseDataview(this.query, this.queryOptions, this.queries);
}
module.exports = BaseOverviewsDataview;
@@ -55,20 +56,20 @@ BaseOverviewsDataview.prototype.rewrittenQuery = function(query) {
};
// Default behaviour
BaseOverviewsDataview.prototype.defaultSql = function(psql, filters, override, callback) {
BaseOverviewsDataview.prototype.defaultSql = function(psql, override, callback) {
var query = this.query;
var dataview = this.baseDataview;
if ( SETTINGS.defaultOverviews ) {
query = this.rewrittenQuery(query);
dataview = new this.BaseDataview(query, this.queryOptions);
}
return dataview.sql(psql, filters, override, callback);
return dataview.sql(psql, override, callback);
};
// default implementation that can be override in derived classes:
BaseOverviewsDataview.prototype.sql = function(psql, filters, override, callback) {
return this.defaultSql(psql, filters, override, callback);
BaseOverviewsDataview.prototype.sql = function(psql, override, callback) {
return this.defaultSql(psql, override, callback);
};
BaseOverviewsDataview.prototype.search = function(psql, userQuery, callback) {

View File

@@ -14,7 +14,8 @@ OverviewsDataviewFactory.prototype.getDataview = function(query, dataviewDefinit
return parentFactory.getDataview(query, dataviewDefinition);
}
return new dataviews[type](
query, dataviewDefinition.options, this.queryRewriter, this.queryRewriteData, this.options
query, dataviewDefinition.options, this.queryRewriter, this.queryRewriteData, this.options,
dataviewDefinition.sql
);
};

View File

@@ -1,34 +1,61 @@
var BaseOverviewsDataview = require('./base');
var BaseDataview = require('../formula');
var debug = require('debug')('windshaft:widget:formula:overview');
var dot = require('dot');
dot.templateSettings.strip = false;
var formulaQueryTpls = {
'count': dot.template([
'SELECT',
'sum(_feature_count) AS result,',
'(SELECT count(1) FROM ({{=it._query}}) _cdb_formula_nulls WHERE {{=it._column}} IS NULL) AS nulls_count',
'FROM ({{=it._query}}) _cdb_formula'
].join('\n')),
'sum': dot.template([
'SELECT',
'sum({{=it._column}}*_feature_count) AS result,',
'(SELECT count(1) FROM ({{=it._query}}) _cdb_formula_nulls WHERE {{=it._column}} IS NULL) AS nulls_count',
'FROM ({{=it._query}}) _cdb_formula'
].join('\n')),
'avg': dot.template([
'SELECT',
'sum({{=it._column}}*_feature_count)/sum(_feature_count) AS result,',
'(SELECT count(1) FROM ({{=it._query}}) _cdb_formula_nulls WHERE {{=it._column}} IS NULL) AS nulls_count',
'FROM ({{=it._query}}) _cdb_formula'
].join('\n')),
'count': dot.template([
'SELECT',
'sum(_feature_count) AS result,',
'(SELECT count(1) FROM ({{=it._query}}) _cdb_formula_nulls WHERE {{=it._column}} IS NULL) AS nulls_count',
'{{?it._isFloatColumn}},(SELECT count(1) FROM ({{=it._query}}) _cdb_formula_infinities',
' WHERE {{=it._column}} = \'infinity\'::float OR {{=it._column}} = \'-infinity\'::float) AS infinities_count,',
'(SELECT count(1) FROM ({{=it._query}}) _cdb_formula_nans',
' WHERE {{=it._column}} = \'NaN\'::float) AS nans_count{{?}}',
'FROM ({{=it._query}}) _cdb_formula'
].join('\n')),
'sum': dot.template([
'SELECT',
'sum({{=it._column}}*_feature_count) AS result,',
'(SELECT count(1) FROM ({{=it._query}}) _cdb_formula_nulls WHERE {{=it._column}} IS NULL) AS nulls_count',
'{{?it._isFloatColumn}},(SELECT count(1) FROM ({{=it._query}}) _cdb_formula_infinities',
' WHERE {{=it._column}} = \'infinity\'::float OR {{=it._column}} = \'-infinity\'::float) AS infinities_count',
',(SELECT count(1) FROM ({{=it._query}}) _cdb_formula_nans',
' WHERE {{=it._column}} = \'NaN\'::float) AS nans_count{{?}}',
'FROM ({{=it._query}}) _cdb_formula',
'{{?it._isFloatColumn}}WHERE',
' {{=it._column}} != \'infinity\'::float',
'AND',
' {{=it._column}} != \'-infinity\'::float',
'AND',
' {{=it._column}} != \'NaN\'::float{{?}}'
].join('\n')),
'avg': dot.template([
'SELECT',
'sum({{=it._column}}*_feature_count)/sum(_feature_count) AS result,',
'(SELECT count(1) FROM ({{=it._query}}) _cdb_formula_nulls WHERE {{=it._column}} IS NULL) AS nulls_count',
'{{?it._isFloatColumn}},(SELECT count(1) FROM ({{=it._query}}) _cdb_formula_infinities',
' WHERE {{=it._column}} = \'infinity\'::float OR {{=it._column}} = \'-infinity\'::float) AS infinities_count',
',(SELECT count(1) FROM ({{=it._query}}) _cdb_formula_nans',
' WHERE {{=it._column}} = \'NaN\'::float) AS nans_count{{?}}',
'FROM ({{=it._query}}) _cdb_formula',
'{{?it._isFloatColumn}}WHERE',
' {{=it._column}} != \'infinity\'::float',
'AND',
' {{=it._column}} != \'-infinity\'::float',
'AND',
' {{=it._column}} != \'NaN\'::float{{?}}'
].join('\n')),
};
function Formula(query, options, queryRewriter, queryRewriteData, params) {
BaseOverviewsDataview.call(this, query, options, BaseDataview, queryRewriter, queryRewriteData, params);
function Formula(query, options, queryRewriter, queryRewriteData, params, queries) {
BaseOverviewsDataview.call(this, query, options, BaseDataview, queryRewriter, queryRewriteData, params, queries);
this.column = options.column || '1';
this.operation = options.operation;
this._isFloatColumn = null;
this.queries = queries;
}
Formula.prototype = Object.create(BaseOverviewsDataview.prototype);
@@ -36,21 +63,38 @@ Formula.prototype.constructor = Formula;
module.exports = Formula;
Formula.prototype.sql = function(psql, filters, override, callback) {
Formula.prototype.sql = function (psql, override, callback) {
var self = this;
var formulaQueryTpl = formulaQueryTpls[this.operation];
if ( formulaQueryTpl ) {
if (formulaQueryTpl) {
// supported formula for use with overviews
if (this._isFloatColumn === null) {
this._isFloatColumn = false;
this.getColumnType(psql, this.column, this.queries.no_filters, function (err, type) {
if (!err && !!type) {
self._isFloatColumn = type.float;
}
self.sql(psql, override, callback);
});
return null;
}
var formulaSql = formulaQueryTpl({
_query: this.rewrittenQuery(this.query),
_isFloatColumn: this._isFloatColumn,
_query: this.rewrittenQuery(this.query),
_operation: this.operation,
_column: this.column
_column: this.column
});
callback = callback || override;
debug(formulaSql);
return callback(null, formulaSql);
}
// default behaviour
return this.defaultSql(psql, filters, override, callback);
return this.defaultSql(psql, override, callback);
};

View File

@@ -1,24 +1,35 @@
var _ = require('underscore');
var BaseOverviewsDataview = require('./base');
var BaseDataview = require('../histogram');
var debug = require('debug')('windshaft:dataview:histogram:overview');
var dot = require('dot');
dot.templateSettings.strip = false;
var columnTypeQueryTpl = dot.template(
'SELECT pg_typeof({{=it.column}})::oid FROM ({{=it.query}}) _cdb_histogram_column_type limit 1'
);
var columnCastTpl = dot.template("date_part('epoch', {{=it.column}})");
var BIN_MIN_NUMBER = 6;
var BIN_MAX_NUMBER = 48;
var filteredQueryTpl = dot.template([
'filtered_source AS (',
' SELECT *',
' FROM ({{=it._query}}) _cdb_filtered_source',
' WHERE',
' {{=it._column}} IS NOT NULL',
' {{?it._isFloatColumn}}AND',
' {{=it._column}} != \'infinity\'::float',
' AND',
' {{=it._column}} != \'-infinity\'::float',
' AND',
' {{=it._column}} != \'NaN\'::float{{?}}',
')'
].join(' \n'));
var basicsQueryTpl = dot.template([
'basics AS (',
' SELECT',
' max({{=it._column}}) AS max_val, min({{=it._column}}) AS min_val,',
' sum({{=it._column}}*_feature_count)/sum(_feature_count) AS avg_val, sum(_feature_count) AS total_rows',
' FROM ({{=it._query}}) _cdb_basics',
' FROM filtered_source',
')'
].join(' \n'));
@@ -27,7 +38,7 @@ var overrideBasicsQueryTpl = dot.template([
' SELECT',
' max({{=it._end}}) AS max_val, min({{=it._start}}) AS min_val,',
' sum({{=it._column}}*_feature_count)/sum(_feature_count) AS avg_val, sum(_feature_count) AS total_rows',
' FROM ({{=it._query}}) _cdb_basics',
' FROM filtered_source',
')'
].join('\n'));
@@ -38,7 +49,7 @@ var iqrQueryTpl = dot.template([
' SELECT quartile, max(_cdb_iqr_column) AS quartile_max from (',
' SELECT {{=it._column}} AS _cdb_iqr_column, ntile(4) over (order by {{=it._column}}',
' ) AS quartile',
' FROM ({{=it._query}}) _cdb_rank) _cdb_quartiles',
' FROM filtered_source) _cdb_quartiles',
' WHERE quartile = 1 or quartile = 3',
' GROUP BY quartile',
' ) _cdb_iqr',
@@ -57,7 +68,7 @@ var binsQueryTpl = dot.template([
' )',
' )',
' END AS bins_number',
' FROM basics, iqrange, ({{=it._query}}) _cdb_bins',
' FROM basics, iqrange, filtered_source',
' LIMIT 1',
')'
].join('\n'));
@@ -77,11 +88,34 @@ var nullsQueryTpl = dot.template([
')'
].join('\n'));
var infinitiesQueryTpl = dot.template([
'infinities AS (',
' SELECT',
' count(*) AS infinities_count',
' FROM ({{=it._query}}) _cdb_histogram_infinities',
' WHERE',
' {{=it._column}} = \'infinity\'::float',
' OR',
' {{=it._column}} = \'-infinity\'::float',
')'
].join('\n'));
var nansQueryTpl = dot.template([
'nans AS (',
' SELECT',
' count(*) AS nans_count',
' FROM ({{=it._query}}) _cdb_histogram_infinities',
' WHERE {{=it._column}} = \'NaN\'::float',
')'
].join('\n'));
var histogramQueryTpl = dot.template([
'SELECT',
' (max_val - min_val) / cast(bins_number as float) AS bin_width,',
' bins_number,',
' nulls_count,',
' {{?it._isFloatColumn}}infinities_count,',
' nans_count,{{?}}',
' avg_val,',
' CASE WHEN min_val = max_val',
' THEN 0',
@@ -91,16 +125,17 @@ var histogramQueryTpl = dot.template([
' max({{=it._column}})::numeric AS max,',
' sum({{=it._column}}*_feature_count)/sum(_feature_count)::numeric AS avg,',
' sum(_feature_count) AS freq',
'FROM ({{=it._query}}) _cdb_histogram, basics, nulls, bins',
'WHERE {{=it._column}} IS NOT NULL',
'FROM filtered_source, basics, nulls, bins{{?it._isFloatColumn}},infinities, nans{{?}}',
'GROUP BY bin, bins_number, bin_width, nulls_count, avg_val',
' {{?it._isFloatColumn}}, infinities_count, nans_count{{?}}',
'ORDER BY bin'
].join('\n'));
function Histogram(query, options, queryRewriter, queryRewriteData, params) {
BaseOverviewsDataview.call(this, query, options, BaseDataview, queryRewriter, queryRewriteData, params);
function Histogram(query, options, queryRewriter, queryRewriteData, params, queries) {
BaseOverviewsDataview.call(this, query, options, BaseDataview, queryRewriter, queryRewriteData, params, queries);
this.query = query;
this.queries = queries;
this.column = options.column;
this.bins = options.bins;
@@ -112,36 +147,23 @@ Histogram.prototype.constructor = Histogram;
module.exports = Histogram;
var DATE_OIDS = {
1082: true,
1114: true,
1184: true
};
Histogram.prototype.sql = function(psql, override, callback) {
var self = this;
if (!callback) {
callback = override;
override = {};
}
var self = this;
var _column = this.column;
var columnTypeQuery = columnTypeQueryTpl({
column: _column, query: this.rewrittenQuery(this.query)
});
if (this._columnType === null) {
psql.query(columnTypeQuery, function(err, result) {
this.getColumnType(psql, this.column, this.queries.no_filters, function (err, type) {
// assume numeric, will fail later
self._columnType = 'numeric';
if (!err && !!result.rows[0]) {
var pgType = result.rows[0].pg_typeof;
if (DATE_OIDS.hasOwnProperty(pgType)) {
self._columnType = 'date';
}
if (!err && !!type) {
self._columnType = Object.keys(type).find(function (key) {
return type[key];
});
}
self.sql(psql, override, callback);
}, true); // use read-only transaction
@@ -149,14 +171,29 @@ Histogram.prototype.sql = function(psql, override, callback) {
}
if (this._columnType === 'date') {
_column = columnCastTpl({column: _column});
// overviews currently aggregate dates to NULL
// to avoid problem we don't use overviews for histograms of date columns
return this.defaultSql(psql, override, callback);
}
var histogramSql = this._buildQuery(override);
return callback(null, histogramSql);
};
Histogram.prototype._buildQuery = function (override) {
var filteredQuery, basicsQuery, binsQuery;
var _column = this.column;
var _query = this.rewrittenQuery(this.query);
var basicsQuery, binsQuery;
filteredQuery = filteredQueryTpl({
_isFloatColumn: this._columnType === 'float',
_query: _query,
_column: _column
});
if (override && _.has(override, 'start') && _.has(override, 'end') && _.has(override, 'bins')) {
if (this._shouldOverride(override)) {
debug('overriding with %j', override);
basicsQuery = overrideBasicsQueryTpl({
_query: _query,
_column: _column,
@@ -175,7 +212,7 @@ Histogram.prototype.sql = function(psql, override, callback) {
_column: _column
});
if (override && _.has(override, 'bins')) {
if (this._shouldOverrideBins(override)) {
binsQuery = [
overrideBinsQueryTpl({
_bins: override.bins
@@ -196,22 +233,50 @@ Histogram.prototype.sql = function(psql, override, callback) {
}
}
var cteSql = [
filteredQuery,
basicsQuery,
binsQuery,
nullsQueryTpl({
_query: _query,
_column: _column
})
];
var histogramSql = [
"WITH",
[
basicsQuery,
binsQuery,
nullsQueryTpl({
if (this._columnType === 'float') {
cteSql.push(
infinitiesQueryTpl({
_query: _query,
_column: _column
}),
nansQueryTpl({
_query: _query,
_column: _column
})
].join(',\n'),
);
}
var histogramSql = [
"WITH",
cteSql.join(',\n'),
histogramQueryTpl({
_isFloatColumn: this._columnType === 'float',
_query: _query,
_column: _column
})
].join('\n');
return callback(null, histogramSql);
debug(histogramSql);
return histogramSql;
};
Histogram.prototype._shouldOverride = function (override) {
return override && _.has(override, 'start') && _.has(override, 'end') && _.has(override, 'bins');
};
Histogram.prototype._shouldOverrideBins = function (override) {
return override && _.has(override, 'bins');
};

View File

@@ -1,8 +1,8 @@
var BaseOverviewsDataview = require('./base');
var BaseDataview = require('../list');
function List(query, options, queryRewriter, queryRewriteData, params) {
BaseOverviewsDataview.call(this, query, options, BaseDataview, queryRewriter, queryRewriteData, params);
function List(query, options, queryRewriter, queryRewriteData, params, queries) {
BaseOverviewsDataview.call(this, query, options, BaseDataview, queryRewriter, queryRewriteData, params, queries);
}
List.prototype = Object.create(BaseOverviewsDataview.prototype);

View File

@@ -66,7 +66,8 @@ function getBoundingBoxes(west, south, east, north) {
bboxes.push([west, south, east, north]);
} else {
bboxes.push([west, south, 180, north]);
bboxes.push([-180, south, east % 180, north]);
// here we assume west,east have been adjusted => west >= -180 => east > 180
bboxes.push([-180, south, east - 360, north]);
}
return bboxes;

View File

@@ -115,6 +115,7 @@ AnalysisMapConfigAdapter.prototype.getMapConfig = function(user, requestMapConfi
}
layer.options.sql = analysisSql;
layer.options.columns = getDataviewsColumns(getLayerDataviews(layer, dataviews));
layer.options.affected_tables = getAllAffectedTablesFromSourceNodes(layerNode);
} else {
missingNodesErrors.push(
new Error('Missing analysis node.id="' + layerSourceId +'" for layer='+layerIndex)
@@ -330,4 +331,13 @@ function AnalysisError(message) {
this.message = message;
}
function getAllAffectedTablesFromSourceNodes(node) {
var affectedTables = node.getAllInputNodes(function (node) {
return node.getType() === 'source';
}).reduce(function(list, node) {
return list.concat(node.getAffectedTables());
},[]);
return affectedTables;
}
require('util').inherits(AnalysisError, Error);

View File

@@ -0,0 +1,25 @@
function MapConfigBufferSizeAdapter() {
this.formats = ['png', 'png32', 'mvt', 'grid.json'];
}
module.exports = MapConfigBufferSizeAdapter;
MapConfigBufferSizeAdapter.prototype.getMapConfig = function (user, requestMapConfig, params, context, callback) {
if (!context.templateParams || !context.templateParams.buffersize) {
return callback(null, requestMapConfig);
}
this.formats.forEach(function (format) {
if (Number.isFinite(context.templateParams.buffersize[format])) {
if (requestMapConfig.buffersize === undefined) {
requestMapConfig.buffersize = {};
}
requestMapConfig.buffersize[format] = context.templateParams.buffersize[format];
}
});
setImmediate(function () {
callback(null, requestMapConfig);
});
};

View File

@@ -43,7 +43,6 @@ MapConfigNamedLayersAdapter.prototype.getMapConfig = function (user, requestMapC
if (nestedNamedLayers.length > 0) {
var nestedNamedMapsError = new Error('Nested named layers are not allowed');
// nestedNamedMapsError.http_status = 400;
return done(nestedNamedMapsError);
}

View File

@@ -33,7 +33,7 @@ MapConfigOverviewsAdapter.prototype.getMapConfig = function(user, requestMapConf
step(
function collectFiltersData() {
var filters, unfiltered_query;
if ( layer.options.source && analysesResults ) {
if ( layer.options.source && analysesResults && !layer.options.sql_wrap) {
var sourceId = layer.options.source.id;
var node = _.find(analysesResults, function(a){ return a.rootNode.params.id === sourceId; });
if ( node ) {

View File

@@ -48,6 +48,11 @@ TurboCartoAdapter.prototype.getMapConfig = function (user, requestMapConfig, par
}
requestMapConfig.layers = results.map(function(result) { return result.layer; });
context.turboCarto = {
layers: results.map(function(result) {
return result.meta;
})
};
return callback(null, requestMapConfig);
});
@@ -84,7 +89,7 @@ TurboCartoAdapter.prototype._parseCartoCss = function (username, params, layer,
}
var pg = new PSQL(dbParamsFromReqParams(params));
function processCallback(err, cartocss) {
function processCallback(err, cartocss, meta) {
// Only return turbo-carto errors
if (err && err.name === 'TurboCartoError') {
var error = new Error(err.message);
@@ -105,7 +110,7 @@ TurboCartoAdapter.prototype._parseCartoCss = function (username, params, layer,
if (cartocss) {
layer.options.cartocss = cartocss;
}
return callback(null, { layer: layer });
return callback(null, { layer: layer, meta: meta });
}
var layerSql = layer.options.sql;

View File

@@ -90,6 +90,7 @@ NamedMapMapConfigProvider.prototype.getMapConfig = function(callback) {
},
function instantiateTemplate(err, templateParams) {
assert.ifError(err);
context.templateParams = templateParams;
return self.templateMaps.instance(self.template, templateParams);
},
function prepareAdapterMapConfig(err, requestMapConfig) {
@@ -120,6 +121,7 @@ NamedMapMapConfigProvider.prototype.getMapConfig = function(callback) {
self.mapConfig = (mapConfig === null) ? null : new MapConfig(mapConfig, context.datasource);
self.analysesResults = context.analysesResults || [];
self.rendererParams = rendererParams;
self.context = context;
self.context.limits = renderLimits || {};
return callback(self.err, self.mapConfig, self.rendererParams, self.context);
}

View File

@@ -0,0 +1,119 @@
var dot = require('dot');
dot.templateSettings.strip = false;
function ResourceLocator(environment) {
this.environment = environment;
this.resourcesUrlTemplates = null;
if (this.environment.resources_url_templates) {
var templates = environment.resources_url_templates;
if (templates.http) {
this.resourcesUrlTemplates = this.resourcesUrlTemplates || {};
this.resourcesUrlTemplates.http = dot.template(templates.http + '/{{=it.resource}}');
}
if (templates.https) {
this.resourcesUrlTemplates = this.resourcesUrlTemplates || {};
this.resourcesUrlTemplates.https = dot.template(templates.https + '/{{=it.resource}}');
}
}
}
module.exports = ResourceLocator;
ResourceLocator.prototype.getUrls = function(username, resource) {
if (this.resourcesUrlTemplates) {
return this.getUrlsFromTemplate(username, resource);
}
var cdnDomain = getCdnDomain(this.environment.serverMetadata, resource);
if (cdnDomain) {
return {
http: 'http://' + cdnDomain.http + '/' + username + '/api/v1/map/' + resource,
https: 'https://' + cdnDomain.https + '/' + username + '/api/v1/map/' + resource
};
} else {
var port = this.environment.port;
return {
http: 'http://' + username + '.' + 'localhost.lan:' + port + '/api/v1/map/' + resource
};
}
};
ResourceLocator.prototype.getUrlsFromTemplate = function(username, resource) {
var urls = {};
var cdnDomain = getCdnDomain(this.environment.serverMetadata, resource) || {};
if (this.resourcesUrlTemplates.http) {
urls.http = this.resourcesUrlTemplates.http({
cdn_url: cdnDomain.http,
user: username,
port: this.environment.port,
resource: resource
});
}
if (this.resourcesUrlTemplates.https) {
urls.https = this.resourcesUrlTemplates.https({
cdn_url: cdnDomain.https,
user: username,
port: this.environment.port,
resource: resource
});
}
return urls;
};
function getCdnDomain(serverMetadata, resource) {
if (serverMetadata && serverMetadata.cdn_url) {
var cdnUrl = serverMetadata.cdn_url;
var http = cdnUrl.http;
var https = cdnUrl.https;
if (cdnUrl.templates) {
var templates = cdnUrl.templates;
var httpUrlTemplate = templates.http.url;
var httpsUrlTemplate = templates.https.url;
http = httpUrlTemplate
.replace(/^(http[s]*:\/\/)/, '')
.replace('{s}', subdomain(templates.http.subdomains, resource));
https = httpsUrlTemplate
.replace(/^(http[s]*:\/\/)/, '')
.replace('{s}', subdomain(templates.https.subdomains, resource));
}
return {
http: http,
https: https,
};
}
return null;
}
// ref https://jsperf.com/js-crc32
function crcTable() {
var c;
var table = [];
for (var n = 0; n < 256; n++) {
c = n;
for (var k = 0; k < 8; k++) {
c = ((c & 1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1));
}
table[n] = c;
}
return table;
}
var CRC_TABLE = crcTable();
function crc32(str) {
var crc = 0 ^ (-1);
for (var i = 0; i < str.length; i++) {
crc = (crc >>> 8) ^ CRC_TABLE[(crc ^ str.charCodeAt(i)) & 0xFF];
}
return (crc ^ (-1)) >>> 0;
}
function subdomain(subdomains, resource) {
var index = crc32(resource) % subdomains.length;
return subdomains[index];
}
module.exports.subdomain = subdomain;

View File

@@ -35,12 +35,15 @@ var timeoutErrorTile = require('fs').readFileSync(timeoutErrorTilePath, {encodin
var SqlWrapMapConfigAdapter = require('./models/mapconfig/adapter/sql-wrap-mapconfig-adapter');
var MapConfigNamedLayersAdapter = require('./models/mapconfig/adapter/mapconfig-named-layers-adapter');
var MapConfigBufferSizeAdapter = require('./models/mapconfig/adapter/mapconfig-buffer-size-adapter');
var AnalysisMapConfigAdapter = require('./models/mapconfig/adapter/analysis-mapconfig-adapter');
var MapConfigOverviewsAdapter = require('./models/mapconfig/adapter/mapconfig-overviews-adapter');
var TurboCartoAdapter = require('./models/mapconfig/adapter/turbo-carto-adapter');
var DataviewsWidgetsAdapter = require('./models/mapconfig/adapter/dataviews-widgets-adapter');
var MapConfigAdapter = require('./models/mapconfig/adapter');
var StatsBackend = require('./backends/stats');
module.exports = function(serverOptions) {
// Make stats client globally accessible
global.statsClient = StatsClient.getInstance(serverOptions.statsd);
@@ -147,13 +150,17 @@ module.exports = function(serverOptions) {
var tileBackend = new windshaft.backend.Tile(rendererCache);
var mapValidatorBackend = new windshaft.backend.MapValidator(tileBackend, attributesBackend);
var mapBackend = new windshaft.backend.Map(rendererCache, mapStore, mapValidatorBackend);
var analysisBackend = new AnalysisBackend(serverOptions.analysis);
var analysisBackend = new AnalysisBackend(metadataBackend, serverOptions.analysis);
var statsBackend = new StatsBackend();
var layergroupAffectedTablesCache = new LayergroupAffectedTablesCache();
app.layergroupAffectedTablesCache = layergroupAffectedTablesCache;
var mapConfigAdapter = new MapConfigAdapter(
new MapConfigNamedLayersAdapter(templateMaps, pgConnection),
new MapConfigBufferSizeAdapter(),
new SqlWrapMapConfigAdapter(),
new DataviewsWidgetsAdapter(),
new AnalysisMapConfigAdapter(analysisBackend),
@@ -206,7 +213,8 @@ module.exports = function(serverOptions) {
surrogateKeysCache,
userLimitsApi,
layergroupAffectedTablesCache,
mapConfigAdapter
mapConfigAdapter,
statsBackend
).register(app);
new controller.NamedMaps(

View File

@@ -36,7 +36,11 @@ var analysisConfig = _.defaults(global.environment.analysis || {}, {
inlineExecution: false,
endpoint: 'http://127.0.0.1:8080/api/v2/sql/job',
hostHeaderTemplate: '{{=it.username}}.localhost.lan'
}
},
logger: {
filename: undefined
},
limits: {}
});
module.exports = {
@@ -66,6 +70,7 @@ module.exports = {
},
datasource: global.environment.postgres,
cachedir: global.environment.millstone.cache_basedir,
use_workers: rendererConfig.mapnik.useCartocssWorkers || false,
mapnik_version: global.environment.mapnik_version,
mapnik_tile_format: global.environment.mapnik_tile_format || 'png',
default_layergroup_ttl: global.environment.mapConfigTTL || 7200
@@ -95,7 +100,11 @@ module.exports = {
inlineExecution: analysisConfig.batch.inlineExecution,
endpoint: analysisConfig.batch.endpoint,
hostHeaderTemplate: analysisConfig.batch.hostHeaderTemplate
}
},
logger: {
filename: analysisConfig.logger.filename
},
limits: analysisConfig.limits
},
// Do not send unwatch on release. See http://github.com/CartoDB/Windshaft-cartodb/issues/161
redis: _.extend(global.environment.redis, {unwatchOnRelease: false}),

View File

@@ -0,0 +1,26 @@
function prepareQuery(sql) {
var affectedTableRegexCache = {
bbox: /!bbox!/g,
scale_denominator: /!scale_denominator!/g,
pixel_width: /!pixel_width!/g,
pixel_height: /!pixel_height!/g
};
return sql
.replace(affectedTableRegexCache.bbox, 'ST_MakeEnvelope(0,0,0,0)')
.replace(affectedTableRegexCache.scale_denominator, '0')
.replace(affectedTableRegexCache.pixel_width, '1')
.replace(affectedTableRegexCache.pixel_height, '1');
}
module.exports.extractTableNames = function extractTableNames(query) {
return [
'SELECT * FROM CDB_QueryTablesText($windshaft$',
prepareQuery(query),
'$windshaft$) as tablenames'
].join('');
};
module.exports.getQueryRowCount = function getQueryRowEstimation(query) {
return 'select CDB_EstimateRowCount(\'' + query + '\') as rows';
};

4013
npm-shrinkwrap.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
{
"private": true,
"name": "windshaft-cartodb",
"version": "2.66.2",
"version": "3.9.8",
"description": "A map tile server for CartoDB",
"keywords": [
"cartodb"
@@ -16,14 +16,15 @@
"contributors": [
"Simon Tokumine <simon@vizzuality.com>",
"Javi Santana <jsantana@vizzuality.com>",
"Sandro Santilli <strk@vizzuality.com>"
"Sandro Santilli <strk@vizzuality.com>",
"Carlos Matallín <matallo@carto.com>"
],
"dependencies": {
"body-parser": "~1.14.0",
"camshaft": "0.37.1",
"cartodb-psql": "~0.6.1",
"cartodb-query-tables": "~0.1.0",
"cartodb-redis": "0.13.1",
"camshaft": "0.55.6",
"cartodb-psql": "0.8.0",
"cartodb-query-tables": "0.2.0",
"cartodb-redis": "0.13.2",
"debug": "~2.2.0",
"dot": "~1.0.2",
"express": "~4.13.3",
@@ -33,29 +34,31 @@
"lzma": "~2.3.2",
"node-statsd": "~0.0.7",
"queue-async": "~1.0.7",
"redis-mpool": "~0.4.0",
"request": "~2.62.0",
"redis-mpool": "0.4.1",
"request": "~2.79.0",
"step": "~0.0.6",
"step-profiler": "~0.3.0",
"turbo-carto": "0.15.1",
"turbo-carto": "0.19.2",
"underscore": "~1.6.0",
"windshaft": "2.4.0"
"windshaft": "3.2.2",
"yargs": "~5.0.0"
},
"devDependencies": {
"istanbul": "~0.4.3",
"jshint": "~2.6.0",
"mocha": "~1.21.4",
"jshint": "~2.9.4",
"mocha": "~3.4.1",
"nock": "~2.11.0",
"redis": "~0.8.6",
"redis": "~0.12.1",
"semver": "~1.1.4",
"strftime": "~0.8.2"
},
"scripts": {
"lint": "jshint lib test",
"preinstall": "make pre-install",
"test": "make test-all"
},
"engines": {
"node": ">=0.8 <0.11",
"npm": ">=2.14.16"
"node": ">=6.9",
"yarn": "^0.21.3"
}
}

View File

@@ -4,4 +4,4 @@ if [[ "$OSTYPE" == "darwin"* ]]; then
export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig:/opt/X11/lib/pkgconfig
fi
npm install
yarn

View File

@@ -373,5 +373,70 @@ describe('analysis-layers error cases', function() {
});
});
it('should return "function does not exist" indicating the node_id and context', function(done) {
var mapConfig = createMapConfig([{
"type": "cartodb",
"options": {
"source": {
"id": "HEAD"
},
"cartocss": '#polygons { polygon-fill: red; }',
"cartocss_version": "2.3.0"
}
}], {}, [{
"id": "HEAD",
"type": "buffer",
"params": {
"source": {
"id": "HEAD2",
"type": "buffer",
"params": {
"source": {
"id": "HEAD3",
"type": 'deprecated-sql-function',
"params": {
"id": "HEAD4",
"function_name": 'DEP_EXT_does_not_exist_fn',
"primary_source": {
"type": 'source',
"params": {
"query": "select * from populated_places_simple_reduced"
}
},
"function_args": ['wadus']
}
},
"radius": 10
}
},
"radius": 10
}
}]);
var testClient = new TestClient(mapConfig, 1234);
testClient.getLayergroup(ERROR_RESPONSE, function(err, layergroupResult) {
assert.ok(!err, err);
assert.equal(layergroupResult.errors.length, 1);
assert.equal(
layergroupResult.errors[0],
'function dep_ext_does_not_exist_fn(unknown, unknown, unknown, text[], unknown) does not exist'
);
assert.equal(layergroupResult.errors_with_context[0].type, 'analysis');
assert.equal(
layergroupResult.errors_with_context[0].message,
'function dep_ext_does_not_exist_fn(unknown, unknown, unknown, text[], unknown) does not exist'
);
assert.equal(layergroupResult.errors_with_context[0].analysis.id, 'HEAD');
assert.equal(layergroupResult.errors_with_context[0].analysis.type, 'buffer');
assert.equal(layergroupResult.errors_with_context[0].analysis.node_id, 'HEAD3');
testClient.drain(done);
});
});
});

View File

@@ -0,0 +1,441 @@
require('../support/test_helper');
var fs = require('fs');
var assert = require('../support/assert');
var TestClient = require('../support/test-client');
var mapnik = require('windshaft').mapnik;
var IMAGE_TOLERANCE_PER_MIL = 5;
var CARTOCSS_LABELS = [
'#layer {',
' polygon-fill: #374C70;',
' polygon-opacity: 0.9;',
' line-width: 1;',
' line-color: #FFF;',
' line-opacity: 0.5;',
'}',
'#layer::labels {',
' text-name: [name];',
' text-face-name: \'DejaVu Sans Book\';',
' text-size: 20;',
' text-fill: #FFFFFF;',
' text-label-position-tolerance: 0;',
' text-halo-radius: 1;',
' text-halo-fill: #6F808D;',
' text-dy: -10;',
' text-allow-overlap: true;',
' text-placement: point;',
' text-placement-type: dummy;',
'}'
].join('\n');
function createMapConfig (bufferSize, cartocss) {
cartocss = cartocss || CARTOCSS_LABELS;
return {
version: '1.6.0',
buffersize: bufferSize,
layers: [{
type: "cartodb",
options: {
sql: [
'select',
' *',
'from',
' populated_places_simple_reduced',
].join('\n'),
cartocss: cartocss,
cartocss_version: '2.3.0',
interactivity: 'cartodb_id'
}
}]
};
}
describe('buffer size per format', function () {
var testCases = [
{
desc: 'should get png tile using buffer-size 0',
coords: { z: 7, x: 64, y: 48 },
format: 'png',
fixturePath: './test/fixtures/buffer-size/tile-7.64.48-buffer-size-0.png',
mapConfig: createMapConfig({ png: 0, 'grid.json': 0 }),
assert: function (tile, callback) {
assert.imageIsSimilarToFile(tile, this.fixturePath, IMAGE_TOLERANCE_PER_MIL, callback);
}
},
{
desc: 'should get png tile using buffer-size 128',
coords: { z: 7, x: 64, y: 48 },
format: 'png',
fixturePath: './test/fixtures/buffer-size/tile-7.64.48-buffer-size-128.png',
mapConfig: createMapConfig({ png: 128, 'grid.json': 128 }),
assert: function (tile, callback) {
assert.imageIsSimilarToFile(tile, this.fixturePath, IMAGE_TOLERANCE_PER_MIL, callback);
}
},
{
desc: 'should get mvt tile using buffer-size 0',
coords: { z: 7, x: 64, y: 48 },
format: 'mvt',
fixturePath: './test/fixtures/buffer-size/tile-7.64.48-buffer-size-0.mvt',
mapConfig: createMapConfig({ mvt: 0 }),
assert: function (tile, callback) {
var tileJSON = tile.toJSON();
var features = tileJSON[0].features;
assert.equal(features.length, 1);
callback();
}
},
{
desc: 'should get mvt tile using buffer-size 128',
coords: { z: 7, x: 64, y: 48 },
format: 'mvt',
fixturePath: './test/fixtures/buffer-size/tile-7.64.48-buffer-size-128.mvt',
mapConfig: createMapConfig({ mvt: 128 }),
assert: function (tile, callback) {
var tileJSON = tile.toJSON();
var features = tileJSON[0].features;
assert.equal(features.length, 9);
callback();
}
},
{
desc: 'should get grid.json tile using buffer-size 0 overriden by template params',
coords: { z: 7, x: 64, y: 48 },
format: 'grid.json',
layers: [0],
fixturePath: './test/fixtures/buffer-size/tile-grid.json.7.64.48-buffer-size-0.grid.json',
mapConfig: createMapConfig({ 'grid.json': 0 }),
assert: function (tile, callback) {
assert.utfgridEqualsFile(tile, this.fixturePath, 2,callback);
}
},
{
desc: 'should get grid.json tile using buffer-size 128 overriden by template params',
coords: { z: 7, x: 64, y: 48 },
format: 'grid.json',
layers: [0],
fixturePath: './test/fixtures/buffer-size/tile-7.64.48-buffer-size-128.grid.json',
mapConfig: createMapConfig({ 'grid.json': 128 }),
assert: function (tile, callback) {
assert.utfgridEqualsFile(tile, this.fixturePath, 2, callback);
}
}
];
testCases.forEach(function (test) {
it(test.desc, function (done) {
var testClient = new TestClient(test.mapConfig, 1234);
var coords = test.coords;
var options = {
format: test.format,
layers: test.layers
};
testClient.getTile(coords.z, coords.x, coords.y, options, function (err, res, tile) {
assert.ifError(err);
// To generate images use:
// tile.save(test.fixturePath);
test.assert(tile, function (err) {
assert.ifError(err);
testClient.drain(done);
});
});
});
});
});
function createBufferSizeTemplate (name, buffersize, placeholders, cartocss) {
cartocss = cartocss || CARTOCSS_LABELS;
return {
"version": "0.0.1",
"name": name,
"placeholders": placeholders || {
"buffersize": {
"type": "number",
"default": 0
}
},
"layergroup": createMapConfig(buffersize)
};
}
describe('buffer size per format for named maps', function () {
var testCases = [
{
desc: 'should get png tile using buffer-size 0 (default value in template)',
coords: { z: 7, x: 64, y: 48 },
format: 'png',
fixturePath: './test/fixtures/buffer-size/tile-7.64.48-buffer-size-0.png',
template: createBufferSizeTemplate('named-default-buffer-size', {png: '<%= buffersize %>'}),
assert: function (tile, callback) {
assert.imageIsSimilarToFile(tile, this.fixturePath, IMAGE_TOLERANCE_PER_MIL, callback);
}
},
{
desc: 'should get png tile using buffer-size 128 (placehoder value)',
coords: { z: 7, x: 64, y: 48 },
format: 'png',
placeholders: { buffersize: 128 },
fixturePath: './test/fixtures/buffer-size/tile-7.64.48-buffer-size-128.png',
template: createBufferSizeTemplate('named-custom-buffer-size', { png: '<%= buffersize %>'}),
assert: function (tile, callback) {
assert.imageIsSimilarToFile(tile, this.fixturePath, IMAGE_TOLERANCE_PER_MIL, callback);
}
},
{
desc: 'should get png tile using buffer-size 0 (default value in template by format)',
coords: { z: 7, x: 64, y: 48 },
format: 'png',
placeholders: { buffersize_png: 0 },
fixturePath: './test/fixtures/buffer-size/tile-7.64.48-buffer-size-0.png',
template: createBufferSizeTemplate('named-default-buffer-size-by-format', {
png: '<%= buffersize_png %>'
}, {
"buffersize_png": {
"type": "number",
"default": "0"
}
}),
assert: function (tile, callback) {
assert.imageIsSimilarToFile(tile, this.fixturePath, IMAGE_TOLERANCE_PER_MIL, callback);
}
},
{
desc: 'should get png tile using buffer-size 128 (placehoder value in template by format)',
coords: { z: 7, x: 64, y: 48 },
format: 'png',
placeholders: { buffersize_png: 128 },
fixturePath: './test/fixtures/buffer-size/tile-7.64.48-buffer-size-128.png',
template: createBufferSizeTemplate('named-custom-buffer-size-by-format', {
png: '<%= buffersize_png %>'
}, {
"buffersize_png": {
"type": "number",
"default": "0"
}
}),
assert: function (tile, callback) {
assert.imageIsSimilarToFile(tile, this.fixturePath, IMAGE_TOLERANCE_PER_MIL, callback);
}
},
{
desc: 'should get grid.json tile using buffer-size 0 overriden by template params',
coords: { z: 7, x: 64, y: 48 },
format: 'grid.json',
layers: [0],
placeholders: { buffersize_gridjson: 0 },
fixturePath: './test/fixtures/buffer-size/tile-grid.json.7.64.48-buffer-size-0.grid.json',
template: createBufferSizeTemplate('named-default-buffer-size-by-format-gridjson', {
'grid.json': '<%= buffersize_gridjson %>'
}, {
"buffersize_gridjson": {
"type": "number",
"default": "0"
}
}),
assert: function (tile, callback) {
assert.utfgridEqualsFile(tile, this.fixturePath, 2,callback);
}
},
{
desc: 'should get grid.json tile using buffer-size 128 overriden by template params',
coords: { z: 7, x: 64, y: 48 },
format: 'grid.json',
layers: [0],
placeholders: { buffersize_gridjson: 128 },
fixturePath: './test/fixtures/buffer-size/tile-7.64.48-buffer-size-128.grid.json',
template: createBufferSizeTemplate('named-custom-buffer-size-by-format-gridjson', {
'grid.json': '<%= buffersize_gridjson %>'
}, {
"buffersize_gridjson": {
"type": "number",
"default": "0"
}
}),
assert: function (tile, callback) {
assert.utfgridEqualsFile(tile, this.fixturePath, 2, callback);
}
}
];
testCases.forEach(function (test) {
it(test.desc, function (done) {
var testClient = new TestClient(test.template, 1234);
var coords = test.coords;
var options = {
format: test.format,
placeholders: test.placeholders,
layers: test.layers
};
testClient.getTile(coords.z, coords.x, coords.y, options, function (err, res, tile) {
assert.ifError(err);
// To generate images use:
//tile.save('./test/fixtures/buffer-size/tile-7.64.48-buffer-size-0-test.png');
test.assert(tile, function (err) {
assert.ifError(err);
testClient.drain(done);
});
});
});
});
});
describe('buffer size per format for named maps w/o placeholders', function () {
var testCases = [
{
desc: 'should get png tile using buffer-size 0 overriden by template params',
coords: { z: 7, x: 64, y: 48 },
format: 'png',
placeholders: {
buffersize: {
png: 0
}
},
fixturePath: './test/fixtures/buffer-size/tile-7.64.48-buffer-size-0.png',
template: createBufferSizeTemplate('named-no-buffer-size-png-0', {}, {}),
assert: function (tile, callback) {
assert.imageIsSimilarToFile(tile, this.fixturePath, IMAGE_TOLERANCE_PER_MIL, callback);
}
},
{
desc: 'should get png tile using buffer-size 128 overriden by template params',
coords: { z: 7, x: 64, y: 48 },
format: 'png',
placeholders: {
buffersize: {
png: 128
}
},
fixturePath: './test/fixtures/buffer-size/tile-7.64.48-buffer-size-128.png',
template: createBufferSizeTemplate('named-no-buffer-size-png-128', {}, {}),
assert: function (tile, callback) {
assert.imageIsSimilarToFile(tile, this.fixturePath, IMAGE_TOLERANCE_PER_MIL, callback);
}
},
{
desc: 'should get mvt tile using buffer-size 0 overriden by template params',
coords: { z: 7, x: 64, y: 48 },
format: 'mvt',
placeholders: {
buffersize: {
mvt: 0
}
},
fixturePath: './test/fixtures/buffer-size/tile-mvt-7.64.48-buffer-size-0.mvt',
template: createBufferSizeTemplate('named-no-buffer-size-mvt', {}, {}),
assert: function (tile, callback) {
var tileJSON = tile.toJSON();
var features = tileJSON[0].features;
var dataFixture = fs.readFileSync(this.fixturePath);
var vtile = new mapnik.VectorTile(this.coords.z, this.coords.x, this.coords.y);
vtile.setDataSync(dataFixture);
var vtileJSON = vtile.toJSON();
var vtileFeatures = vtileJSON[0].features;
assert.equal(features.length, vtileFeatures.length);
callback();
}
},
{
desc: 'should get mvt tile using buffer-size 128 overriden by template params',
coords: { z: 7, x: 64, y: 48 },
format: 'mvt',
placeholders: {
buffersize: {
mvt: 128
}
},
fixturePath: './test/fixtures/buffer-size/tile-mvt-7.64.48-buffer-size-128.mvt',
template: createBufferSizeTemplate('named-no-buffer-size-mvt-128', {}, {}),
assert: function (tile, callback) {
var tileJSON = tile.toJSON();
var features = tileJSON[0].features;
var dataFixture = fs.readFileSync(this.fixturePath);
var vtile = new mapnik.VectorTile(this.coords.z, this.coords.x, this.coords.y);
vtile.setDataSync(dataFixture);
var vtileJSON = vtile.toJSON();
var vtileFeatures = vtileJSON[0].features;
assert.equal(features.length, vtileFeatures.length);
callback();
}
},
{
desc: 'should get grid.json tile using buffer-size 0 overriden by template params',
coords: { z: 7, x: 64, y: 48 },
format: 'grid.json',
layers: [0],
placeholders: {
buffersize: {
'grid.json': 0
}
},
fixturePath: './test/fixtures/buffer-size/tile-grid.json.7.64.48-buffer-size-0.grid.json',
template: createBufferSizeTemplate('named-no-buffer-size-grid-json-0', {}, {}),
assert: function (tile, callback) {
assert.utfgridEqualsFile(tile, this.fixturePath, 2,callback);
}
},
{
desc: 'should get grid.json tile using buffer-size 128 overriden by template params',
coords: { z: 7, x: 64, y: 48 },
format: 'grid.json',
layers: [0],
placeholders: {
buffersize: {
'grid.json': 128
}
},
fixturePath: './test/fixtures/buffer-size/tile-7.64.48-buffer-size-128.grid.json',
template: createBufferSizeTemplate('named-no-buffer-size-grid-json-128', {}, {}),
assert: function (tile, callback) {
assert.utfgridEqualsFile(tile, this.fixturePath, 2, callback);
}
},
{
desc: 'should get png tile using buffer-size 0' +
' overriden by template params with no buffersize in mapconfig',
coords: { z: 7, x: 64, y: 48 },
format: 'png',
placeholders: {
buffersize: {
png: 0
}
},
fixturePath: './test/fixtures/buffer-size/tile-7.64.48-buffer-size-0.png',
template: createBufferSizeTemplate('named-no-buffer-size-mapconfig-png-0', undefined, {}),
assert: function (tile, callback) {
assert.imageIsSimilarToFile(tile, this.fixturePath, IMAGE_TOLERANCE_PER_MIL, callback);
}
},
];
testCases.forEach(function (test) {
it(test.desc, function (done) {
var testClient = new TestClient(test.template, 1234);
var coords = test.coords;
var options = {
format: test.format,
placeholders: test.placeholders,
layers: test.layers
};
testClient.getTile(coords.z, coords.x, coords.y, options, function (err, res, tile) {
assert.ifError(err);
// To generate images use:
//tile.save(test.fixturePath);
// require('fs').writeFileSync(test.fixturePath, JSON.stringify(tile));
// require('fs').writeFileSync(test.fixturePath, tile.getDataSync());
test.assert(tile, function (err) {
assert.ifError(err);
testClient.drain(done);
});
});
});
});
});

393
test/acceptance/cache/cache_headers.js vendored Normal file
View File

@@ -0,0 +1,393 @@
var testHelper = require('../../support/test_helper');
var assert = require('../../support/assert');
var qs = require('querystring');
var CartodbWindshaft = require('../../../lib/cartodb/server');
var serverOptions = require('../../../lib/cartodb/server_options');
var server = new CartodbWindshaft(serverOptions);
server.setMaxListeners(0);
var LayergroupToken = require('../../support/layergroup-token');
describe('get requests with cache headers', function() {
var keysToDelete;
beforeEach(function() {
keysToDelete = {};
});
afterEach(function(done) {
testHelper.deleteRedisKeys(keysToDelete, done);
});
var statusOkResponse = {
status: 200
};
var mapConfigs = [
{
"description": "cache headers should be present",
"cache_headers": {
"x_cache_channel": {
"db_name": "test_windshaft_cartodb_user_1_db",
"tables": ["public.test_table"]
},
"surrogate_keys": "t:77pJnX"
},
"data":
{
version: '1.5.0',
layers: [
{
options: {
source: {
id: "2570e105-7b37-40d2-bdf4-1af889598745"
},
sql: 'select * from test_table limit 2',
cartocss: '#layer { marker-fill:red; }',
cartocss_version: '2.3.0',
attributes: {
id:'cartodb_id',
columns: [
'name',
'address'
]
}
}
}
],
analyses: [
{
"id": "2570e105-7b37-40d2-bdf4-1af889598745",
"type": "source",
"params": {
"query": "select * from test_table limit 2"
}
}
]
},
},
{
"description": "cache headers should be present and be composed with source table name",
"cache_headers": {
"x_cache_channel": {
"db_name": "test_windshaft_cartodb_user_1_db",
"tables": ["public.analysis_2f13a3dbd7_9eb239903a1afd8a69130d1ece0fc8b38de8592d",
"public.test_table"]
},
"surrogate_keys": "t:77pJnX t:iL4eth"
},
"data":
{
version: '1.5.0',
layers: [
{
options: {
source: {
id: "2570e105-7b37-40d2-bdf4-1af889598745"
},
sql: 'select * from test_table limit 2',
cartocss: '#layer { marker-fill:red; }',
cartocss_version: '2.3.0',
attributes: {
id:'cartodb_id',
columns: [
'name',
'address'
]
}
}
}
],
analyses: [
{
"id": "2570e105-7b37-40d2-bdf4-1af889598745",
"type": "buffer",
"params": {
"source": {
"type": "source",
"params": {
"query": "select * from test_table limit 2"
}
},
"radius": 50000
}
}
]
}
}];
var layergroupRequest = function(mapConfig) {
return {
url: '/api/v1/map?api_key=1234&config=' + encodeURIComponent(JSON.stringify(mapConfig)),
method: 'GET',
headers: {
host: 'localhost'
}
};
};
function getRequest(url, addApiKey, callbackName) {
var params = {};
if (!!addApiKey) {
params.api_key = '1234';
}
if (!!callbackName) {
params.callback = callbackName;
}
return {
url: url + '?' + qs.stringify(params),
method: 'GET',
headers: {
host: 'localhost',
'Content-Type': 'application/json'
}
};
}
function validateCacheHeaders(done, expectedCacheHeaders) {
return function(res, err) {
if (err) {
return done(err);
}
assert.ok(res.headers['x-cache-channel']);
assert.ok(res.headers['surrogate-key']);
if (expectedCacheHeaders) {
validateXChannelHeaders(res.headers, expectedCacheHeaders);
assert.equal(res.headers['surrogate-key'], expectedCacheHeaders.surrogate_keys);
}
done();
};
}
function validateXChannelHeaders(headers, expectedCacheHeaders) {
var dbName = headers['x-cache-channel'].split(':')[0];
var tables = headers['x-cache-channel'].split(':')[1].split(',').sort();
assert.equal(dbName, expectedCacheHeaders.x_cache_channel.db_name);
assert.deepEqual(tables, expectedCacheHeaders.x_cache_channel.tables.sort());
}
function noCacheHeaders(done) {
return function(res, err) {
if (err) {
return done(err);
}
assert.ok(
!res.headers['x-cache-channel'],
'did not expect x-cache-channel header, got: `' + res.headers['x-cache-channel'] + '`'
);
assert.ok(
!res.headers['surrogate-key'],
'did not expect surrogate-key header, got: `' + res.headers['surrogate-key'] + '`'
);
done();
};
}
function withLayergroupId(mapConfig, callback) {
assert.response(
server,
layergroupRequest(mapConfig),
statusOkResponse,
function(res, err) {
if (err) {
return callback(err);
}
var layergroupId = JSON.parse(res.body).layergroupid;
keysToDelete['map_cfg|' + LayergroupToken.parse(layergroupId).token] = 0;
keysToDelete['user:localhost:mapviews:global'] = 5;
callback(null, layergroupId, res);
}
);
}
mapConfigs.forEach(function(mapConfigData) {
describe(mapConfigData.description, function() {
var mapConfig = mapConfigData.data;
var expectedCacheHeaders = mapConfigData.cache_headers;
it('/api/v1/map Map instantiation', function(done) {
var testFn = validateCacheHeaders(done, expectedCacheHeaders);
withLayergroupId(mapConfig, function(err, layergroupId, res) {
testFn(res);
});
});
it ('/api/v1/map/:token/:z/:x/:y@:scale_factor?x.:format Mapnik retina tiles', function(done) {
withLayergroupId(mapConfig, function(err, layergroupId) {
assert.response(
server,
getRequest('/api/v1/map/' + layergroupId + '/0/0/0@2x.png', true),
validateCacheHeaders(done, expectedCacheHeaders)
);
});
});
it ('/api/v1/map/:token/:z/:x/:y@:scale_factor?x.:format Mapnik tiles', function(done) {
withLayergroupId(mapConfig, function(err, layergroupId) {
assert.response(
server,
getRequest('/api/v1/map/' + layergroupId + '/0/0/0.png', true),
validateCacheHeaders(done, expectedCacheHeaders)
);
});
});
it ('/api/v1/map/:token/:layer/:z/:x/:y.(:format) Per :layer rendering', function(done) {
withLayergroupId(mapConfig, function(err, layergroupId) {
assert.response(
server,
getRequest('/api/v1/map/' + layergroupId + '/0/0/0/0.png', true),
validateCacheHeaders(done, expectedCacheHeaders)
);
});
});
it ('/api/v1/map/:token/:layer/attributes/:fid endpoint for info windows', function(done) {
withLayergroupId(mapConfig, function(err, layergroupId) {
assert.response(
server,
getRequest('/api/v1/map/' + layergroupId + '/0/attributes/1', true),
validateCacheHeaders(done, expectedCacheHeaders)
);
});
});
it ('/api/v1/map/static/center/:token/:z/:lat/:lng/:width/:height.:format static maps', function(done) {
withLayergroupId(mapConfig, function(err, layergroupId) {
assert.response(
server,
getRequest('/api/v1/map/static/center/' + layergroupId + '/0/0/0/400/300.png', true),
validateCacheHeaders(done, expectedCacheHeaders)
);
});
});
it ('/api/v1/map/static/bbox/:token/:bbox/:width/:height.:format static maps', function(done) {
withLayergroupId(mapConfig, function(err, layergroupId) {
assert.response(
server,
getRequest('/api/v1/map/static/bbox/' + layergroupId + '/-45,-45,45,45/400/300.png', true),
validateCacheHeaders(done, expectedCacheHeaders)
);
});
});
});
});
describe('cache headers should NOT be present', function() {
it('/', function(done) {
assert.response(
server,
getRequest('/'),
statusOkResponse,
noCacheHeaders(done)
);
});
it('/version', function(done) {
assert.response(
server,
getRequest('/version'),
statusOkResponse,
noCacheHeaders(done)
);
});
it('/health', function(done) {
assert.response(
server,
getRequest('/health'),
statusOkResponse,
noCacheHeaders(done)
);
});
it('/api/v1/map/named list named maps', function(done) {
assert.response(
server,
getRequest('/api/v1/map/named', true),
statusOkResponse,
noCacheHeaders(done)
);
});
describe('with named maps', function() {
var templateName = 'x_cache';
beforeEach(function(done) {
var template = {
version: '0.0.1',
name: templateName,
auth: {
method: 'open'
},
layergroup: mapConfigs[0].data
};
var namedMapRequest = {
url: '/api/v1/map/named?api_key=1234',
method: 'POST',
headers: {
host: 'localhost',
'Content-Type': 'application/json'
},
data: JSON.stringify(template)
};
assert.response(
server,
namedMapRequest,
statusOkResponse,
function(res, err) {
done(err);
}
);
});
afterEach(function(done) {
assert.response(
server,
{
url: '/api/v1/map/named/' + templateName + '?api_key=1234',
method: 'DELETE',
headers: {
host: 'localhost'
}
},
{
status: 204
},
function(res, err) {
done(err);
}
);
});
it('/api/v1/map/named/:template_id Named map retrieval', function(done) {
assert.response(
server,
getRequest('/api/v1/map/named/' + templateName, true),
statusOkResponse,
noCacheHeaders(done)
);
});
it('/api/v1/map/named/:template_id/jsonp Named map retrieval', function(done) {
assert.response(
server,
getRequest('/api/v1/map/named/' + templateName, true, 'cb'),
statusOkResponse,
noCacheHeaders(done)
);
});
});
});
});

View File

@@ -1,9 +1,8 @@
require('../../support/test_helper');
var assert = require('../../support/assert');
var TestClient = require('../../support/test-client');
describe('aggregations', function() {
describe('aggregations happy cases', function() {
afterEach(function(done) {
if (this.testClient) {
@@ -13,30 +12,36 @@ describe('aggregations', function() {
}
});
function aggregationOperationMapConfig(operation) {
return {
function aggregationOperationMapConfig(operation, query, column, aggregationColumn) {
column = column || 'adm0name';
aggregationColumn = aggregationColumn || 'pop_max';
query = query || 'select * from populated_places_simple_reduced';
var mapConfig = {
version: '1.5.0',
layers: [
{
type: 'mapnik',
options: {
sql: 'select * from populated_places_simple_reduced',
sql: query,
cartocss: '#layer0 { marker-fill: red; marker-width: 10; }',
cartocss_version: '2.0.1',
widgets: {
adm0name: {
type: 'aggregation',
options: {
column: 'adm0name',
aggregation: operation,
aggregationColumn: 'pop_max'
}
}
}
widgets: {}
}
}
]
};
mapConfig.layers[0].options.widgets[column] = {
type: 'aggregation',
options: {
column: column,
aggregation: operation,
aggregationColumn: aggregationColumn
}
};
return mapConfig;
}
var operations = ['count', 'sum', 'avg', 'max', 'min'];
@@ -56,4 +61,266 @@ describe('aggregations', function() {
});
});
});
var query = [
'select 1 as val, \'a\' as cat, ST_Transform(ST_SetSRID(ST_MakePoint(0,0),4326),3857) as the_geom_webmercator',
'select null, \'b\', ST_Transform(ST_SetSRID(ST_MakePoint(0,1),4326),3857)',
'select null, \'b\', ST_Transform(ST_SetSRID(ST_MakePoint(1,0),4326),3857)',
'select null, null, ST_Transform(ST_SetSRID(ST_MakePoint(1,1),4326),3857)'
].join(' UNION ALL ');
operations.forEach(function (operation) {
var not = operation === 'count' ? ' not ' : ' ';
var description = 'should' +
not +
'handle NULL values in category and aggregation columns using "' +
operation +
'" as aggregation operation';
it(description, function (done) {
this.testClient = new TestClient(aggregationOperationMapConfig(operation, query, 'cat', 'val'));
this.testClient.getDataview('cat', { own_filter: 0 }, function (err, aggregation) {
assert.ifError(err);
assert.ok(aggregation);
assert.equal(aggregation.type, 'aggregation');
assert.ok(aggregation.categories);
assert.equal(aggregation.categoriesCount, 3);
assert.equal(aggregation.count, 4);
assert.equal(aggregation.nulls, 1);
var hasNullCategory = false;
aggregation.categories.forEach(function (category) {
if (category.category === null) {
hasNullCategory = true;
}
});
if (operation === 'count') {
assert.ok(hasNullCategory, 'aggregation has not a category NULL');
} else {
assert.ok(!hasNullCategory, 'aggregation has category NULL');
}
done();
});
});
});
var operations_and_values = {'count': 9, 'sum': 45, 'avg': 5, 'max': 9, 'min': 1};
var query_other = [
'select generate_series(1,3) as val, \'other_a\' as cat, NULL as the_geom_webmercator',
'select generate_series(4,6) as val, \'other_b\' as cat, NULL as the_geom_webmercator',
'select generate_series(7,9) as val, \'other_c\' as cat, NULL as the_geom_webmercator',
'select generate_series(10,12) as val, \'category_1\' as cat, NULL as the_geom_webmercator',
'select generate_series(10,12) as val, \'category_2\' as cat, NULL as the_geom_webmercator',
'select generate_series(10,12) as val, \'category_3\' as cat, NULL as the_geom_webmercator',
'select generate_series(10,12) as val, \'category_4\' as cat, NULL as the_geom_webmercator',
'select generate_series(10,12) as val, \'category_5\' as cat, NULL as the_geom_webmercator'
].join(' UNION ALL ');
Object.keys(operations_and_values).forEach(function (operation) {
var description = 'should aggregate OTHER category using "' + operation + '"';
it(description, function (done) {
this.testClient = new TestClient(aggregationOperationMapConfig(operation, query_other, 'cat', 'val'));
this.testClient.getDataview('cat', { own_filter: 0 }, function (err, aggregation) {
assert.ifError(err);
assert.ok(aggregation);
assert.equal(aggregation.type, 'aggregation');
assert.ok(aggregation.categories);
assert.equal(aggregation.categoriesCount, 8);
assert.equal(aggregation.count, 24);
assert.equal(aggregation.nulls, 0);
var aggregated_categories = aggregation.categories.filter( function(category) {
return category.agg === true;
});
assert.equal(aggregated_categories.length, 1);
assert.equal(aggregated_categories[0].value, operations_and_values[operation]);
done();
});
});
});
var widgetSearchExpects = {
'count': [ { category: 'other_a', value: 3 } ],
'sum': [ { category: 'other_a', value: 6 } ],
'avg': [ { category: 'other_a', value: 2 } ],
'max': [ { category: 'other_a', value: 3 } ],
'min': [ { category: 'other_a', value: 1 } ]
};
Object.keys(operations_and_values).forEach(function (operation) {
var description = 'should search OTHER category using "' + operation + '"';
it(description, function (done) {
this.testClient = new TestClient(aggregationOperationMapConfig(operation, query_other, 'cat', 'val'));
this.testClient.widgetSearch('cat', 'other_a', function (err, res, searchResult) {
assert.ifError(err);
assert.ok(searchResult);
assert.equal(searchResult.type, 'aggregation');
assert.equal(searchResult.categories.length, 1);
assert.deepEqual(
searchResult.categories,
widgetSearchExpects[operation]
);
done();
});
});
});
});
describe('aggregation-dataview: special float values', function() {
afterEach(function(done) {
if (this.testClient) {
this.testClient.drain(done);
} else {
done();
}
});
function createMapConfig(layers, dataviews, analysis) {
return {
version: '1.5.0',
layers: layers,
dataviews: dataviews || {},
analyses: analysis || []
};
}
var mapConfig = createMapConfig(
[
{
"type": "cartodb",
"options": {
"source": {
"id": "a0"
},
"cartocss": "#points { marker-width: 10; marker-fill: red; }",
"cartocss_version": "2.3.0"
}
}
],
{
val_aggregation: {
source: {
id: 'a0'
},
type: 'aggregation',
options: {
column: 'cat',
aggregation: 'avg',
aggregationColumn: 'val'
}
},
sum_aggregation_numeric: {
source: {
id: 'a1'
},
type: 'aggregation',
options: {
column: 'cat',
aggregation: 'sum',
aggregationColumn: 'val'
}
}
},
[
{
"id": "a0",
"type": "source",
"params": {
"query": [
'SELECT',
' null::geometry the_geom_webmercator,',
' CASE',
' WHEN x % 4 = 0 THEN \'infinity\'::float',
' WHEN x % 4 = 1 THEN \'-infinity\'::float',
' WHEN x % 4 = 2 THEN \'NaN\'::float',
' ELSE x',
' END AS val,',
' CASE',
' WHEN x % 2 = 0 THEN \'category_1\'',
' ELSE \'category_2\'',
' END AS cat',
'FROM generate_series(1, 1000) x'
].join('\n')
}
}, {
"id": "a1",
"type": "source",
"params": {
"query": [
'SELECT',
' null::geometry the_geom_webmercator,',
' CASE',
' WHEN x % 3 = 0 THEN \'NaN\'::numeric',
' WHEN x % 3 = 1 THEN x',
' ELSE x',
' END AS val,',
' CASE',
' WHEN x % 2 = 0 THEN \'category_1\'',
' ELSE \'category_2\'',
' END AS cat',
'FROM generate_series(1, 1000) x'
].join('\n')
}
}
]
);
// Source a0
// -----------------------------------------------
// the_geom_webmercator | val | cat
// ----------------------+-----------+------------
// | -Infinity | category_2
// | NaN | category_1
// | 3 | category_2
// | Infinity | category_1
// | -Infinity | category_2
// | NaN | category_1
// | 7 | category_2
// | Infinity | category_1
// | -Infinity | category_2
// | NaN | category_1
// | 11 | category_2
// | " | "
var filters = [{ own_filter: 0 }, {}];
filters.forEach(function (filter) {
it('should handle special float values using filter: ' + JSON.stringify(filter), function(done) {
this.testClient = new TestClient(mapConfig, 1234);
this.testClient.getDataview('val_aggregation', { own_filter: 0 }, function(err, dataview) {
assert.ifError(err);
assert.ok(dataview.infinities === (250 + 250));
assert.ok(dataview.nans === 250);
assert.ok(dataview.categories.length === 1);
dataview.categories.forEach(function (category) {
assert.ok(category.category === 'category_2');
assert.ok(category.value === 501);
});
done();
});
});
it('should handle special numeric values using filter: ' + JSON.stringify(filter), function(done) {
this.testClient = new TestClient(mapConfig, 1234);
this.testClient.getDataview('sum_aggregation_numeric', { own_filter: 0 }, function(err, dataview) {
assert.ifError(err);
assert.ok(dataview.nans === 333);
assert.ok(dataview.categories.length === 2);
dataview.categories.forEach(function (category) {
assert.ok(category.value !== null);
});
done();
});
});
});
});

View File

@@ -0,0 +1,80 @@
require('../../support/test_helper');
var assert = require('../../support/assert');
var TestClient = require('../../support/test-client');
function createMapConfig(layers, dataviews, analysis) {
return {
version: '1.5.0',
layers: layers,
dataviews: dataviews || {},
analyses: analysis || []
};
}
describe('formula-dataview: special float values', function() {
afterEach(function(done) {
if (this.testClient) {
this.testClient.drain(done);
} else {
done();
}
});
var mapConfig = createMapConfig(
[
{
"type": "cartodb",
"options": {
"source": {
"id": "a0"
},
"cartocss": "#points { marker-width: 10; marker-fill: red; }",
"cartocss_version": "2.3.0"
}
}
],
{
val_formula: {
source: {
id: 'a0'
},
type: 'formula',
options: {
column: 'val',
operation: 'avg'
}
}
},
[
{
"id": "a0",
"type": "source",
"params": {
"query": [
'SELECT',
' null::geometry the_geom_webmercator,',
' CASE',
' WHEN x % 4 = 0 THEN \'infinity\'::float',
' WHEN x % 4 = 1 THEN \'-infinity\'::float',
' WHEN x % 4 = 2 THEN \'NaN\'::float',
' ELSE x',
' END AS val',
'FROM generate_series(1, 1000) x'
].join('\n')
}
}
]
);
it('should filter infinities out and count them in the summary', function(done) {
this.testClient = new TestClient(mapConfig, 1234);
this.testClient.getDataview('val_formula', {}, function(err, dataview) {
assert.ok(!err, err);
assert.equal(dataview.result, 501);
assert.ok(dataview.infinities === (250 + 250));
assert.ok(dataview.nans === 250);
done();
});
});
});

View File

@@ -3,6 +3,15 @@ require('../../support/test_helper');
var assert = require('../../support/assert');
var TestClient = require('../../support/test-client');
function createMapConfig(layers, dataviews, analysis) {
return {
version: '1.5.0',
layers: layers,
dataviews: dataviews || {},
analyses: analysis || []
};
}
describe('histogram-dataview', function() {
afterEach(function(done) {
@@ -13,15 +22,6 @@ describe('histogram-dataview', function() {
}
});
function createMapConfig(layers, dataviews, analysis) {
return {
version: '1.5.0',
layers: layers,
dataviews: dataviews || {},
analyses: analysis || []
};
}
var mapConfig = createMapConfig(
[
{
@@ -77,4 +77,90 @@ describe('histogram-dataview', function() {
done();
});
});
it('should cast all overridable params to numbers', function(done) {
var params = {
bins: '256 AS other, (select 256 * 2) AS bins_number--',
start: 1e3,
end: 0,
response: TestClient.RESPONSE.ERROR
};
this.testClient = new TestClient(mapConfig, 1234);
this.testClient.getDataview('pop_max_histogram', params, function(err, res) {
assert.ok(!err, err);
assert.ok(res.errors);
assert.equal(res.errors.length, 1);
assert.ok(res.errors[0].match(/Invalid number format for parameter 'bins'/));
done();
});
});
});
describe('histogram-dataview: special float valuer', function() {
afterEach(function(done) {
if (this.testClient) {
this.testClient.drain(done);
} else {
done();
}
});
var mapConfig = createMapConfig(
[
{
"type": "cartodb",
"options": {
"source": {
"id": "a0"
},
"cartocss": "#points { marker-width: 10; marker-fill: red; }",
"cartocss_version": "2.3.0"
}
}
],
{
val_histogram: {
source: {
id: 'a0'
},
type: 'histogram',
options: {
column: 'val'
}
}
},
[
{
"id": "a0",
"type": "source",
"params": {
"query": [
'SELECT',
' null::geometry the_geom_webmercator,',
' CASE',
' WHEN x % 4 = 0 THEN \'infinity\'::float',
' WHEN x % 4 = 1 THEN \'-infinity\'::float',
' WHEN x % 4 = 2 THEN \'NaN\'::float',
' ELSE x',
' END AS val',
'FROM generate_series(1, 1000) x'
].join('\n')
}
}
]
);
it('should filter infinities out and count them in the summary', function(done) {
this.testClient = new TestClient(mapConfig, 1234);
this.testClient.getDataview('val_histogram', {}, function(err, dataview) {
assert.ok(!err, err);
assert.ok(dataview.infinities === (250 + 250));
assert.ok(dataview.nans === 250);
done();
});
});
});

View File

@@ -124,6 +124,13 @@ describe('dataviews using tables with overviews', function() {
params: {
query: 'select * from test_table_overviews'
}
},
{
id: 'data-source-special-float-values',
type: 'source',
params: {
query: 'select * from test_special_float_values_table_overviews'
}
}
],
dataviews: {
@@ -144,6 +151,43 @@ describe('dataviews using tables with overviews', function() {
aggregationColumn: 'name',
}
},
test_categories_special_values: {
type: 'aggregation',
source: {
id: 'data-source-special-float-values'
},
options: {
column: 'name',
aggregation: 'sum',
aggregationColumn: 'value',
}
},
test_histogram: {
type: 'histogram',
source: {id: 'data-source'},
options: {
column: 'value',
bins: 2
}
},
test_histogram_date: {
type: 'histogram',
source: {id: 'data-source'},
options: {
column: 'updated_at',
bins: 2
}
},
test_histogram_special_values: {
type: 'histogram',
source: {
id: 'data-source-special-float-values'
},
options: {
column: 'value',
bins: 2
}
},
test_avg: {
type: 'formula',
source: {id: 'data-source'},
@@ -152,6 +196,16 @@ describe('dataviews using tables with overviews', function() {
operation: 'avg'
}
},
test_formula_sum_special_values: {
type: 'formula',
source: {
id: 'data-source-special-float-values'
},
options: {
column: 'value',
operation: 'sum'
}
},
test_count: {
type: 'formula',
source: {id: 'data-source'},
@@ -186,6 +240,17 @@ describe('dataviews using tables with overviews', function() {
cartocss_version: '2.3.0',
source: { id: 'data-source' }
}
},
{
type: 'mapnik',
options: {
sql: 'select * from test_special_float_values_table_overviews',
cartocss: '#layer { marker-fill: red; marker-width: 32; marker-allow-overlap: true; }',
cartocss_version: '2.3.0',
source: {
id: 'data-source-special-float-values'
}
}
}
]
};
@@ -196,7 +261,14 @@ describe('dataviews using tables with overviews', function() {
if (err) {
return done(err);
}
assert.deepEqual(formula_result, {"operation":"sum","result":15,"nulls":0,"type":"formula"});
assert.deepEqual(formula_result, {
"operation":"sum",
"result":15,
"infinities": 0,
"nans": 0,
"nulls":0,
"type":"formula"
});
testClient.drain(done);
});
@@ -208,7 +280,14 @@ describe('dataviews using tables with overviews', function() {
if (err) {
return done(err);
}
assert.deepEqual(formula_result, {"operation":"avg","result":3,"nulls":0,"type":"formula"});
assert.deepEqual(formula_result, {
"operation":"avg",
"result":3,
"nulls":0,
"type":"formula",
"infinities": 0,
"nans": 0
});
testClient.drain(done);
});
@@ -220,7 +299,14 @@ describe('dataviews using tables with overviews', function() {
if (err) {
return done(err);
}
assert.deepEqual(formula_result, {"operation":"count","result":5,"nulls":0,"type":"formula"});
assert.deepEqual(formula_result, {
"operation":"count",
"result":5,
"nulls":0,
"type":"formula",
"infinities": 0,
"nans": 0
});
testClient.drain(done);
});
@@ -232,7 +318,14 @@ describe('dataviews using tables with overviews', function() {
if (err) {
return done(err);
}
assert.deepEqual(formula_result, {"operation":"max","result":5,"nulls":0,"type":"formula"});
assert.deepEqual(formula_result, {
"operation": "max",
"result": 5,
"nulls": 0,
"infinities": 0,
"nans": 0,
"type": "formula"
});
testClient.drain(done);
});
@@ -244,7 +337,14 @@ describe('dataviews using tables with overviews', function() {
if (err) {
return done(err);
}
assert.deepEqual(formula_result, {"operation":"min","result":1,"nulls":0,"type":"formula"});
assert.deepEqual(formula_result, {
"operation": "min",
"result": 1,
"nulls": 0,
"infinities": 0,
"nans": 0,
"type": "formula"
});
testClient.drain(done);
});
@@ -259,14 +359,96 @@ describe('dataviews using tables with overviews', function() {
if (err) {
return done(err);
}
assert.deepEqual(formula_result, {"operation":"sum","result":15,"nulls":0,"type":"formula"});
assert.deepEqual(formula_result, {
"operation":"sum",
"result":15,
"nulls":0,
"infinities": 0,
"nans": 0,
"type":"formula"
});
testClient.drain(done);
});
});
it("should expose a histogram", function (done) {
var testClient = new TestClient(overviewsMapConfig);
testClient.getDataview('test_histogram', function (err, histogram) {
if (err) {
return done(err);
}
assert.ok(histogram);
assert.equal(histogram.type, 'histogram');
assert.ok(Array.isArray(histogram.bins));
testClient.drain(done);
});
});
describe('filters', function() {
describe('histogram', function () {
it("should expose a filtered histogram", function (done) {
var params = {
filters: {
dataviews: { test_histogram: { min: 2 } }
}
};
var testClient = new TestClient(overviewsMapConfig);
testClient.getDataview('test_histogram', params, function (err, histogram) {
if (err) {
return done(err);
}
assert.ok(histogram);
assert.equal(histogram.type, 'histogram');
assert.ok(Array.isArray(histogram.bins));
assert.equal(histogram.bins.length, 4);
testClient.drain(done);
});
});
it("should expose a filtered histogram with no results", function (done) {
var params = {
filters: {
dataviews: { test_histogram: { max: -1 } }
}
};
var testClient = new TestClient(overviewsMapConfig);
testClient.getDataview('test_histogram', params, function (err, histogram) {
if (err) {
return done(err);
}
assert.ok(histogram);
assert.equal(histogram.type, 'histogram');
assert.ok(Array.isArray(histogram.bins));
assert.equal(histogram.bins.length, 0);
testClient.drain(done);
});
});
it("should expose a filtered date histogram with no results", function (done) {
// This most likely works because the overviews will pass
// the responsibility to the normal dataviews.
var params = {
filters: {
dataviews: { test_histogram_date: { max: -1 } }
}
};
var testClient = new TestClient(overviewsMapConfig);
testClient.getDataview('test_histogram_date', params, function (err, histogram) {
if (err) {
return done(err);
}
assert.ok(histogram);
assert.equal(histogram.type, 'histogram');
assert.ok(Array.isArray(histogram.bins));
assert.equal(histogram.bins.length, 0);
testClient.drain(done);
});
});
});
describe('category', function () {
var params = {
@@ -281,7 +463,14 @@ describe('dataviews using tables with overviews', function() {
if (err) {
return done(err);
}
assert.deepEqual(formula_result, {"operation":"sum","result":1,"nulls":0,"type":"formula"});
assert.deepEqual(formula_result, {
"operation":"sum",
"result":1,
"nulls":0,
"infinities": 0,
"nans": 0,
"type":"formula"
});
testClient.drain(done);
});
});
@@ -292,7 +481,14 @@ describe('dataviews using tables with overviews', function() {
if (err) {
return done(err);
}
assert.deepEqual(formula_result, {"operation":"avg","result":1,"nulls":0,"type":"formula"});
assert.deepEqual(formula_result, {
"operation":"avg",
"result":1,
"nulls":0,
"infinities": 0,
"nans": 0,
"type":"formula"
});
testClient.drain(done);
});
@@ -304,7 +500,14 @@ describe('dataviews using tables with overviews', function() {
if (err) {
return done(err);
}
assert.deepEqual(formula_result, {"operation":"count","result":1,"nulls":0,"type":"formula"});
assert.deepEqual(formula_result, {
"operation":"count",
"result":1,
"infinities": 0,
"nans": 0,
"nulls":0,
"type":"formula"
});
testClient.drain(done);
});
@@ -316,7 +519,14 @@ describe('dataviews using tables with overviews', function() {
if (err) {
return done(err);
}
assert.deepEqual(formula_result, {"operation":"max","result":1,"nulls":0,"type":"formula"});
assert.deepEqual(formula_result, {
"operation": "max",
"result": 1,
"nulls": 0,
"infinities": 0,
"nans": 0,
"type": "formula"
});
testClient.drain(done);
});
@@ -328,7 +538,14 @@ describe('dataviews using tables with overviews', function() {
if (err) {
return done(err);
}
assert.deepEqual(formula_result, {"operation":"min","result":1,"nulls":0,"type":"formula"});
assert.deepEqual(formula_result, {
"operation": "min",
"result": 1,
"nulls": 0,
"infinities": 0,
"nans": 0,
"type": "formula"
});
testClient.drain(done);
});
@@ -346,7 +563,14 @@ describe('dataviews using tables with overviews', function() {
if (err) {
return done(err);
}
assert.deepEqual(formula_result, {"operation":"sum","result":1,"nulls":0,"type":"formula"});
assert.deepEqual(formula_result, {
"operation":"sum",
"result":1,
"nulls":0,
"infinities": 0,
"nans": 0,
"type":"formula"
});
testClient.drain(done);
});
});
@@ -354,5 +578,69 @@ describe('dataviews using tables with overviews', function() {
});
describe('aggregation special float values', function () {
var params = {};
it("should expose an aggregation dataview filtering special float values out", function (done) {
var testClient = new TestClient(overviewsMapConfig);
testClient.getDataview('test_categories_special_values', params, function (err, dataview) {
if (err) {
return done(err);
}
assert.deepEqual(dataview, {
aggregation: 'sum',
count: 5,
nulls: 0,
nans: 1,
infinities: 1,
min: 6,
max: 6,
categoriesCount: 1,
categories: [ { category: 'Hawai', value: 6, agg: false } ],
type: 'aggregation'
});
testClient.drain(done);
});
});
it('should expose a histogram dataview filtering special float values out', function (done) {
var testClient = new TestClient(overviewsMapConfig);
testClient.getDataview('test_histogram_special_values', params, function (err, dataview) {
if (err) {
return done(err);
}
assert.deepEqual(dataview, {
bin_width: 0,
bins_count: 1,
bins_start: 3,
nulls: 0,
infinities: 1,
nans: 1,
avg: 3,
bins: [ { bin: 0, min: 3, max: 3, avg: 3, freq: 2 } ],
type: 'histogram'
});
testClient.drain(done);
});
});
it('should expose a formula (sum) dataview filtering special float values out', function (done) {
var testClient = new TestClient(overviewsMapConfig);
testClient.getDataview('test_formula_sum_special_values', params, function (err, dataview) {
if (err) {
return done(err);
}
assert.deepEqual(dataview, {
operation: 'sum',
result: 6,
nulls: 0,
nans: 1,
infinities: 1,
type: 'formula'
});
testClient.drain(done);
});
});
});
});
});

View File

@@ -0,0 +1,68 @@
var assert = require('../support/assert');
var CartodbWindshaft = require(__dirname + '/../../lib/cartodb/server');
var serverOptions = require(__dirname + '/../../lib/cartodb/server_options');
var server = new CartodbWindshaft(serverOptions);
describe('error with context', function () {
var layerOK = {
options: {
sql: 'select cartodb_id, ST_Translate(the_geom_webmercator, 5e6, 0) as the_geom_webmercator ' +
'from test_table',
cartocss: '#layer { marker-fill:red; marker-width:32; marker-allow-overlap:true; }',
cartocss_version: '2.0.1',
interactivity: 'cartodb_id'
}
};
var layerKO = {
options: {
sql: 'select cartodb_id from test_table offset 3', // it doesn't return the_geom_webmercator so it must fail
cartocss: '#layer { marker-fill:blue; marker-allow-overlap:true; }',
cartocss_version: '2.0.2',
interactivity: 'cartodb_id'
}
};
var DB_ERROR_MESSAGE = 'Postgis Plugin: ERROR: column "the_geom_webmercator" does not exist';
var scenarios = [{
description: 'layergroup with 2 layers, second one has query error',
layergroup: {
version: '1.0.0',
layers: [layerOK, layerKO]
},
expectedFailingLayer: { id: 'layer1', index: 1, type: 'mapnik' }
}, {
description: 'layergroup with 2 layers, first one has query error',
layergroup: {
version: '1.0.0',
layers: [layerKO, layerOK]
},
expectedFailingLayer: { id: 'layer0', index: 0, type: 'mapnik' }
}];
scenarios.forEach(function (scenario) {
it(scenario.description, function (done) {
assert.response(server, {
url: '/api/v1/map',
method: 'POST',
headers: {
host: 'localhost',
'Content-Type': 'application/json'
},
data: JSON.stringify(scenario.layergroup)
}, {
status: 400
}, function (res) {
var parsedBody = JSON.parse(res.body);
assert.ok(Array.isArray(parsedBody.errors_with_context));
var err = parsedBody.errors_with_context[0];
assert.equal(err.type, 'layer');
assert.equal(err.subtype, 'query');
assert.ok(err.message.indexOf(DB_ERROR_MESSAGE) >= 0);
assert.deepEqual(err.layer, scenario.expectedFailingLayer);
done();
});
});
});
});

View File

@@ -5,6 +5,9 @@ var step = require('step');
var strftime = require('strftime');
var redis_stats_db = 5;
var mapnik = require('windshaft').mapnik;
var semver = require('semver');
var helper = require(__dirname + '/../support/test_helper');
var LayergroupToken = require('../support/layergroup-token');
@@ -574,7 +577,7 @@ describe(suiteName, function() {
if ( err ) {
return done(err);
}
// trip epoch
// strip epoch
expected_token = expected_token.split(':')[0];
keysToDelete['map_cfg|' + expected_token] = 0;
keysToDelete['user:localhost:mapviews:global'] = 5;
@@ -973,69 +976,71 @@ describe(suiteName, function() {
});
// See https://github.com/CartoDB/Windshaft-cartodb/issues/93
it("accepts unused directives", function(done) {
var layergroup = {
version: '1.0.0',
layers: [
{ options: {
sql: "select 'SRID=3857;POINT(0 0)'::geometry as the_geom_webmercator",
cartocss: '#layer { point-transform:"scale(20)"; }',
cartocss_version: '2.0.1'
} }
]
};
var expected_token; // = "e34dd7e235138a062f8ba7ad051aa3a7";
step(
function do_post()
{
var next = this;
assert.response(server, {
url: layergroup_url,
method: 'POST',
headers: {host: 'localhost', 'Content-Type': 'application/json' },
data: JSON.stringify(layergroup)
}, {}, function(res) {
assert.equal(res.statusCode, 200, res.body);
var parsedBody = JSON.parse(res.body);
if ( expected_token ) {
assert.equal(parsedBody.layergroupid, expected_token + ':' + expected_last_updated_epoch);
assert.equal(res.headers['x-layergroup-id'], parsedBody.layergroupid);
}
else {
var token_components = parsedBody.layergroupid.split(':');
expected_token = token_components[0];
expected_last_updated_epoch = token_components[1];
}
next(null, res);
});
},
function do_get_tile(err)
{
assert.ifError(err);
var next = this;
assert.response(server, {
url: layergroup_url + "/" + expected_token + ':cb0/0/0/0.png',
method: 'GET',
headers: {host: 'localhost' },
encoding: 'binary'
}, {}, function(res) {
assert.equal(res.statusCode, 200, res.body);
assert.equal(res.headers['content-type'], "image/png");
assert.imageBufferIsSimilarToFile(res.body, windshaft_fixtures + '/test_default_mapnik_point.png',
IMAGE_EQUALS_TOLERANCE_PER_MIL, function(err/*, similarity*/) {
next(err);
if (semver.satisfies(mapnik.versions.mapnik, '2.3.x')) {
it("accepts unused directives", function(done) {
var layergroup = {
version: '1.0.0',
layers: [
{ options: {
sql: "select 'SRID=3857;POINT(0 0)'::geometry as the_geom_webmercator",
cartocss: '#layer { point-transform:"scale(20)"; }',
cartocss_version: '2.0.1'
} }
]
};
var expected_token; // = "e34dd7e235138a062f8ba7ad051aa3a7";
step(
function do_post()
{
var next = this;
assert.response(server, {
url: layergroup_url,
method: 'POST',
headers: {host: 'localhost', 'Content-Type': 'application/json' },
data: JSON.stringify(layergroup)
}, {}, function(res) {
assert.equal(res.statusCode, 200, res.body);
var parsedBody = JSON.parse(res.body);
if ( expected_token ) {
assert.equal(parsedBody.layergroupid, expected_token + ':' + expected_last_updated_epoch);
assert.equal(res.headers['x-layergroup-id'], parsedBody.layergroupid);
}
);
});
},
function finish(err) {
keysToDelete['user:localhost:mapviews:global'] = 5;
keysToDelete['map_cfg|' + expected_token] = 0;
else {
var token_components = parsedBody.layergroupid.split(':');
expected_token = token_components[0];
expected_last_updated_epoch = token_components[1];
}
next(null, res);
});
},
function do_get_tile(err)
{
assert.ifError(err);
var next = this;
assert.response(server, {
url: layergroup_url + "/" + expected_token + ':cb0/0/0/0.png',
method: 'GET',
headers: {host: 'localhost' },
encoding: 'binary'
}, {}, function(res) {
assert.equal(res.statusCode, 200, res.body);
assert.equal(res.headers['content-type'], "image/png");
assert.imageBufferIsSimilarToFile(res.body, windshaft_fixtures + '/test_default_mapnik_point.png',
IMAGE_EQUALS_TOLERANCE_PER_MIL, function(err/*, similarity*/) {
next(err);
}
);
});
},
function finish(err) {
keysToDelete['user:localhost:mapviews:global'] = 5;
keysToDelete['map_cfg|' + expected_token] = 0;
done(err);
}
);
});
done(err);
}
);
});
}
// See https://github.com/CartoDB/Windshaft-cartodb/issues/91
// and https://github.com/CartoDB/Windshaft-cartodb/issues/38

View File

@@ -228,7 +228,9 @@ describe('tests from old api translated to multilayer', function() {
},
function(res) {
var parsed = JSON.parse(res.body);
assert.deepEqual(parsed.errors, [ 'Unexpected token W' ]);
assert.ok(parsed.errors);
assert.equal(parsed.errors.length, 1);
assert.ok(parsed.errors[0].match(/^Unexpected token W/));
done();
}

53
test/acceptance/mvt.js Normal file
View File

@@ -0,0 +1,53 @@
require('../support/test_helper');
const assert = require('../support/assert');
const TestClient = require('../support/test-client');
function createMapConfig (sql = TestClient.SQL.ONE_POINT) {
return {
version: '1.6.0',
layers: [{
type: "cartodb",
options: {
sql: sql,
cartocss: TestClient.CARTOCSS.POINTS,
cartocss_version: '2.3.0',
interactivity: 'cartodb_id'
}
}]
};
}
describe('mvt', function () {
const testCases = [
{
desc: 'should get empty mvt with code 204 (no content)',
coords: { z: 0, x: 0, y: 0 },
format: 'mvt',
status: 204,
mapConfig: createMapConfig(TestClient.SQL.EMPTY)
},
{
desc: 'should get mvt tile with code 200 (ok)',
coords: { z: 0, x: 0, y: 0 },
format: 'mvt',
status: 200,
mapConfig: createMapConfig()
}
];
testCases.forEach(function (test) {
it(test.desc, done => {
const testClient = new TestClient(test.mapConfig, 1234);
const { z, x, y } = test.coords;
const { format, status } = test;
testClient.getTile(z, x, y, { format, status }, (err, res) => {
assert.ifError(err);
assert.equal(res.statusCode, test.status);
testClient.drain(done);
});
});
});
});

View File

@@ -0,0 +1,210 @@
var step = require('step');
var test_helper = require('../support/test_helper');
var assert = require('../support/assert');
var CartodbWindshaft = require(__dirname + '/../../lib/cartodb/server');
var serverOptions = require(__dirname + '/../../lib/cartodb/server_options');
var server = new CartodbWindshaft(serverOptions);
var RedisPool = require('redis-mpool');
var TemplateMaps = require('../../lib/cartodb/backends/template_maps.js');
var mapnik = require('windshaft').mapnik;
var IMAGE_TOLERANCE = 20;
describe('layers visibility for previews', function() {
// configure redis pool instance to use in tests
var redisPool = new RedisPool(global.environment.redis);
var templateMaps = new TemplateMaps(redisPool, {
max_user_templates: global.environment.maxUserTemplates
});
var username = 'localhost';
function createLayer (color, layerId) {
var mod;
if (color === 'red') {
mod = 0;
} else if (color === 'orange') {
mod = 1;
} else if (color === 'blue') {
mod = 2;
} else {
mod = 0;
}
return {
type: 'mapnik',
id: layerId,
options: {
sql: 'select * from populated_places_simple_reduced where cartodb_id % 3 = ' + mod,
cartocss: '#layer { marker-fill: ' + color + '; }',
cartocss_version: '2.3.0'
}
};
}
function createTemplate(scenario) {
return {
version: '0.0.1',
name: scenario.name,
auth: {
method: 'open'
},
view: {
bounds: {
west: 0,
south: 0,
east: 45,
north: 45
},
zoom: 4,
center: {
lng: 40,
lat: 20
},
preview_layers: scenario.layerPerview
},
layergroup: {
layers: scenario.layers
}
};
}
afterEach(function (done) {
test_helper.deleteRedisKeys({
'user:localhost:mapviews:global': 5
}, done);
});
function previewFixture(version) {
return './test/fixtures/previews/populated_places_simple_reduced-' + version + '.png';
}
var threeLayerPointDistintColor = [
createLayer('red'),
createLayer('orange'),
createLayer('blue', 'layer2')
];
var scenarios = [{
name: 'preview_layers_red',
layerPerview: {
'0': true,
'1': false,
'layer2': false
},
layers: threeLayerPointDistintColor
}, {
name: 'preview_layers_orange',
layerPerview: {
'0': false,
'1': true,
'layer2': false
},
layers: threeLayerPointDistintColor
}, {
name: 'preview_layers_blue',
layerPerview: {
'0': false,
'1': false,
'layer2': true
},
layers: threeLayerPointDistintColor
}, {
name: 'preview_layers_red_orange',
layerPerview: {
'0': true,
'1': true,
'layer2': false
},
layers: threeLayerPointDistintColor
}, {
name: 'preview_layers_red_blue',
layerPerview: {
'0': true,
'1': false,
'layer2': true
},
layers: threeLayerPointDistintColor
}, {
name: 'preview_layers_orange_blue',
layerPerview: {
'0': false,
'1': true,
'layer2': true
},
layers: threeLayerPointDistintColor
}, {
name: 'preview_layers_red_orange_blue',
layerPerview: {
'0': true,
'1': true,
'layer2': true
},
layers: threeLayerPointDistintColor
}, {
name: 'preview_layers_all',
layerPerview: {},
layers: threeLayerPointDistintColor
}, {
name: 'preview_layers_undefined',
layerPerview: undefined,
layers: threeLayerPointDistintColor
}];
scenarios.forEach(function (scenario) {
it('should filter layers for template: ' + scenario.name, function (done) {
step(
function addTemplate () {
var next = this;
var template = createTemplate(scenario);
templateMaps.addTemplate(username, template, next);
},
function requestPreview (err) {
assert.ifError(err);
var next = this;
assert.response(server, {
url: '/api/v1/map/static/named/' + scenario.name + '/640/480.png',
method: 'GET',
headers: {
host: 'localhost'
},
encoding: 'binary'
}, {
status: 200,
headers: {
'content-type': 'image/png'
}
}, function (res, err) {
next(err, res);
});
},
function checkPreview (err, res) {
assert.ifError(err);
var next = this;
var img = mapnik.Image.fromBytes(new Buffer(res.body, 'binary'));
var previewFixturePath = previewFixture(scenario.name);
assert.imageIsSimilarToFile(img, previewFixturePath, IMAGE_TOLERANCE, next);
},
function deleteTemplate(err) {
assert.ifError(err);
var next = this;
templateMaps.delTemplate(username, scenario.name, next);
},
function finish (err) {
done(err);
}
);
});
});
});

View File

@@ -21,7 +21,7 @@ describe('named maps static view', function() {
var IMAGE_TOLERANCE = 20;
function createTemplate(view) {
function createTemplate(view, layers) {
return {
version: '0.0.1',
name: templateName,
@@ -36,7 +36,7 @@ describe('named maps static view', function() {
},
view: view,
layergroup: {
layers: [
layers: layers || [
{
type: 'mapnik',
options: {
@@ -198,4 +198,43 @@ describe('named maps static view', function() {
});
});
it('should allow to select the layers to render', function (done) {
var view = {
bounds: {
west: 0,
south: 0,
east: 45,
north: 45
}
};
var layers = [
{
type: 'mapnik',
options: {
sql: 'select * from populated_places_simple_reduced',
cartocss: '#layer { marker-fill: <%= color %>; }',
cartocss_version: '2.3.0'
}
},
{
type: 'mapnik',
options: {
sql: 'select ST_Transform(ST_MakeEnvelope(-45, -45, 45, 45, 4326), 3857) the_geom_webmercator',
cartocss: '#layer { polygon-fill: <%= color %>; }',
cartocss_version: '2.3.0'
}
}
];
templateMaps.addTemplate(username, createTemplate(view, layers), function (err) {
if (err) {
return done(err);
}
getStaticMap({ layer: 0 }, function(err, img) {
assert.ok(!err);
assert.imageIsSimilarToFile(img, previewFixture('bounds'), IMAGE_TOLERANCE, done);
});
});
});
});

View File

@@ -5,6 +5,7 @@ var step = require('step');
var cartodbServer = require('../../../lib/cartodb/server');
var ServerOptions = require('./support/ported_server_options');
var testClient = require('./support/test_client');
var TestClient = require('../../support/test-client');
var BaseController = require('../../../lib/cartodb/controllers/base');
@@ -23,6 +24,14 @@ describe('multilayer error cases', function() {
BaseController.prototype.req2params = req2paramsFn;
});
// var client = null;
afterEach(function(done) {
if (this.client) {
return this.client.drain(done);
}
return done();
});
it("post layergroup with wrong Content-Type", function(done) {
assert.response(server, {
url: '/database/windshaft_test/layergroup',
@@ -153,24 +162,16 @@ describe('multilayer error cases', function() {
]
};
ServerOptions.afterLayergroupCreateCalls = 0;
assert.response(server, {
url: '/database/windshaft_test/layergroup',
method: 'POST',
headers: {'Content-Type': 'application/json' },
data: JSON.stringify(layergroup)
}, {}, function(res) {
try {
assert.equal(res.statusCode, 400, res.statusCode + ': ' + res.body);
// See http://github.com/CartoDB/Windshaft/issues/159
assert.equal(ServerOptions.afterLayergroupCreateCalls, 0);
var parsed = JSON.parse(res.body);
assert.ok(parsed);
assert.equal(parsed.errors.length, 1);
var error = parsed.errors[0];
assert.ok(error.match(/column "missing" does not exist/m), error);
// TODO: check which layer introduced the problem ?
done();
} catch (err) { done(err); }
this.client = new TestClient(layergroup);
this.client.getLayergroup({status: 400}, function(err, parsed) {
assert.ok(!err, err);
// See http://github.com/CartoDB/Windshaft/issues/159
assert.equal(ServerOptions.afterLayergroupCreateCalls, 0);
assert.ok(parsed);
assert.equal(parsed.errors.length, 1);
var error = parsed.errors[0];
assert.ok(error.match(/column "missing" does not exist/m), error);
done();
});
});
@@ -419,7 +420,9 @@ describe('multilayer error cases', function() {
},
function(res) {
var parsedBody = JSON.parse(res.body);
assert.deepEqual(parsedBody, { errors: ['SyntaxError: Unexpected token {'] });
assert.ok(parsedBody.errors);
assert.equal(parsedBody.errors.length, 1);
assert.ok(parsedBody.errors[0].match(/^SyntaxError: Unexpected token {/));
done();
}
);

View File

@@ -139,13 +139,15 @@ describe('server_gettile', function() {
13, 4011, 3088, imageCompareFn('test_table_13_4011_3088_styled_black.png', done));
});
// See http://github.com/CartoDB/Windshaft/issues/99
it("unused directives are tolerated", function(done){
var style = "#test_table{point-transform: 'scale(100)';}";
var sql = "SELECT 1 as cartodb_id, 'SRID=4326;POINT(0 0)'::geometry as the_geom";
testClient.getTile(testClient.singleLayerMapConfig(sql, style), 0, 0, 0,
imageCompareFn('test_default_mapnik_point.png', done));
});
if ( semver.satisfies(mapnik.versions.mapnik, '2.3.x') ) {
// See http://github.com/CartoDB/Windshaft/issues/99
it("unused directives are tolerated", function(done){
var style = "#test_table{point-transform: 'scale(100)';}";
var sql = "SELECT 1 as cartodb_id, 'SRID=4326;POINT(0 0)'::geometry as the_geom";
testClient.getTile(testClient.singleLayerMapConfig(sql, style), 0, 0, 0,
imageCompareFn('test_default_mapnik_point.png', done));
});
}
// See http://github.com/CartoDB/Windshaft/issues/100
var test_strictness = function(done) {
@@ -168,51 +170,53 @@ describe('server_gettile', function() {
// Strictness handling changed in 2.3.x, possibly a bug: see http://github.com/mapnik/mapnik/issues/2301
it.skip('[skipped due to http://github.com/mapnik/mapnik/issues/2301]' + test_strict_lbl, test_strictness);
}
else {
else if (!semver.satisfies(mapnik.versions.mapnik, '3.0.x')) {
it(test_strict_lbl, test_strictness);
}
it('high cpu regression with mapnik <2.3.x', function(done) {
var sql = [
"SELECT 'my polygon name here' AS name,",
"st_envelope(st_buffer(st_transform(",
"st_setsrid(st_makepoint(-26.6592894004,49.7990296995),4326),3857),10000000)) AS the_geom",
"FROM generate_series(-6,6) x",
"UNION ALL",
"SELECT 'my marker name here' AS name,",
" st_transform(st_setsrid(st_makepoint(49.6042060319,-49.0522997372),4326),3857) AS the_geom",
"FROM generate_series(-6,6) x"
].join(' ');
if ( semver.satisfies(mapnik.versions.mapnik, '2.3.x') ) {
var style = [
'#test_table {marker-fill:#ff7;',
' marker-max-error:0.447492761618;',
' marker-line-opacity:0.659371340628;',
' marker-allow-overlap:true;',
' polygon-fill:green;',
' marker-spacing:0.0;',
' marker-width:4.0;',
' marker-height:18.0;',
' marker-opacity:0.942312062822;',
' line-color:green;',
' line-gamma:0.945973211092;',
' line-cap:square;',
' polygon-opacity:0.12576055992;',
' marker-type:arrow;',
' polygon-gamma:0.46354913107;',
' line-dasharray:33,23;',
' line-join:bevel;',
' marker-placement:line;',
' line-width:1.0;',
' marker-line-color:#ff7;',
' line-opacity:0.39403752154;',
' marker-line-width:3.0;',
'}'
].join('');
it('high cpu regression with mapnik <2.3.x', function(done) {
var sql = [
"SELECT 'my polygon name here' AS name,",
"st_envelope(st_buffer(st_transform(",
"st_setsrid(st_makepoint(-26.6592894004,49.7990296995),4326),3857),10000000)) AS the_geom",
"FROM generate_series(-6,6) x",
"UNION ALL",
"SELECT 'my marker name here' AS name,",
" st_transform(st_setsrid(st_makepoint(49.6042060319,-49.0522997372),4326),3857) AS the_geom",
"FROM generate_series(-6,6) x"
].join(' ');
testClient.getTile(testClient.singleLayerMapConfig(sql, style), 13, 4011, 3088, done);
});
var style = [
'#test_table {marker-fill:#ff7;',
' marker-max-error:0.447492761618;',
' marker-line-opacity:0.659371340628;',
' marker-allow-overlap:true;',
' polygon-fill:green;',
' marker-spacing:0.0;',
' marker-width:4.0;',
' marker-height:18.0;',
' marker-opacity:0.942312062822;',
' line-color:green;',
' line-gamma:0.945973211092;',
' line-cap:square;',
' polygon-opacity:0.12576055992;',
' marker-type:arrow;',
' polygon-gamma:0.46354913107;',
' line-dasharray:33,23;',
' line-join:bevel;',
' marker-placement:line;',
' line-width:1.0;',
' marker-line-color:#ff7;',
' line-opacity:0.39403752154;',
' marker-line-width:3.0;',
'}'
].join('');
testClient.getTile(testClient.singleLayerMapConfig(sql, style), 13, 4011, 3088, done);
});
}
// https://github.com/CartoDB/Windshaft-cartodb/issues/316
it('should return errors with better formatting', function(done) {
var mapConfig = {

View File

@@ -16,13 +16,13 @@ describe('server_png8_format', function() {
var serverOptionsPng32 = ServerOptions;
serverOptionsPng32.grainstore = _.clone(ServerOptions.grainstore);
serverOptionsPng32.grainstore.mapnik_tile_format = 'png32';
var serverPng32 = new cartodbServer(serverOptionsPng32);
var serverPng32 = cartodbServer(serverOptionsPng32);
serverPng32.setMaxListeners(0);
var serverOptionsPng8 = ServerOptions;
serverOptionsPng8.grainstore = _.clone(ServerOptions.grainstore);
serverOptionsPng8.grainstore.mapnik_tile_format = 'png8:m=h';
var serverPng8 = new cartodbServer(serverOptionsPng8);
var serverPng8 = cartodbServer(serverOptionsPng8);
serverPng8.setMaxListeners(0);

View File

@@ -370,7 +370,7 @@ describe('torque', function() {
{
var next = this;
assert.response(server, {
url: '/database/windshaft_test/layergroup?dbport=1234567',
url: '/database/windshaft_test/layergroup?dbport=54777',
method: 'POST',
headers: {'Content-Type': 'application/json' },
data: JSON.stringify(mapconfig)

View File

@@ -0,0 +1,279 @@
require('../../support/test_helper');
var assert = require('../../support/assert');
var TestClient = require('../../support/test-client');
describe('Create mapnik layergroup', function() {
before(function() {
this.layerStatsConfig = global.environment.enabledFeatures.layerStats;
global.environment.enabledFeatures.layerStats = true;
});
after(function() {
global.environment.enabledFeatures.layerStats = this.layerStatsConfig;
});
var cartocssVersion = '2.3.0';
var cartocss = '#layer { line-width:16; }';
var mapnikLayer1 = {
type: 'mapnik',
options: {
sql: 'select * from test_table limit 1',
cartocss_version: cartocssVersion,
cartocss: cartocss
}
};
var mapnikLayer2 = {
type: 'mapnik',
options: {
sql: 'select * from test_table_2 limit 2',
cartocss_version: cartocssVersion,
cartocss: cartocss
}
};
var mapnikLayer3 = {
type: 'mapnik',
options: {
sql: 'select * from test_table_3 limit 3',
cartocss_version: cartocssVersion,
cartocss: cartocss
}
};
var mapnikLayer4 = {
type: 'mapnik',
options: {
sql: [
'select t1.cartodb_id, t1.the_geom, t1.the_geom_webmercator, t2.address',
' from test_table t1, test_table_2 t2',
' where t1.cartodb_id = t2.cartodb_id;'
].join(''),
cartocss_version: cartocssVersion,
cartocss: cartocss
}
};
var httpLayer = {
type: 'http',
options: {
urlTemplate: 'http://{s}.basemaps.cartocdn.com/dark_nolabels/{z}/{x}/{y}.png',
subdomains: ['a','b','c']
}
};
var mapnikLayerGeomColumn = {
type: 'mapnik',
options: {
sql: 'select *, the_geom as my_geom from test_table_3 limit 2',
geom_column: 'my_geom',
cartocss_version: cartocssVersion,
cartocss: cartocss
}
};
function mapnikBasicLayerId(index) {
return 'layer' + index;
}
function typeLayerId(type, index) {
return type + '-' + mapnikBasicLayerId(index);
}
it('with one mapnik layer should response with meta-stats for that layer', function(done) {
var testClient = new TestClient({
version: '1.4.0',
layers: [
mapnikLayer1
]
});
testClient.getLayergroup(function(err, layergroup) {
assert.ok(!err);
assert.equal(layergroup.metadata.layers[0].id, mapnikBasicLayerId(0));
assert.equal(layergroup.metadata.layers[0].meta.stats.estimatedFeatureCount, 1);
testClient.drain(done);
});
});
it('with two mapnik layer should response with meta-stats for every layer', function(done) {
var testClient = new TestClient({
version: '1.4.0',
layers: [
mapnikLayer1,
mapnikLayer2
]
});
testClient.getLayergroup(function(err, layergroup) {
assert.ok(!err);
assert.equal(layergroup.metadata.layers[0].id, mapnikBasicLayerId(0));
assert.equal(layergroup.metadata.layers[0].meta.stats.estimatedFeatureCount, 1);
assert.equal(layergroup.metadata.layers[1].id, mapnikBasicLayerId(1));
assert.equal(layergroup.metadata.layers[1].meta.stats.estimatedFeatureCount, 2);
testClient.drain(done);
});
});
it('with three mapnik layer should response with meta-stats for every layer', function(done) {
var testClient = new TestClient({
version: '1.4.0',
layers: [
mapnikLayer1,
mapnikLayer2,
mapnikLayer3
]
});
testClient.getLayergroup(function(err, layergroup) {
assert.ok(!err);
assert.equal(layergroup.metadata.layers[0].id, mapnikBasicLayerId(0));
assert.equal(layergroup.metadata.layers[0].meta.stats.estimatedFeatureCount, 1);
assert.equal(layergroup.metadata.layers[1].id, mapnikBasicLayerId(1));
assert.equal(layergroup.metadata.layers[1].meta.stats.estimatedFeatureCount, 2);
assert.equal(layergroup.metadata.layers[2].id, mapnikBasicLayerId(2));
assert.equal(layergroup.metadata.layers[2].meta.stats.estimatedFeatureCount, 3);
testClient.drain(done);
});
});
it('with one mapnik layer (sql with join) should response with meta-stats for that layer', function(done) {
var testClient = new TestClient({
version: '1.4.0',
layers: [
mapnikLayer4
]
});
testClient.getLayergroup(function(err, layergroup) {
assert.ok(!err);
assert.equal(layergroup.metadata.layers[0].id, mapnikBasicLayerId(0));
assert.equal(layergroup.metadata.layers[0].meta.stats.estimatedFeatureCount, 5);
testClient.drain(done);
});
});
it('with two mapnik layer (sql with join) should response with meta-stats for every layer', function(done) {
var testClient = new TestClient({
version: '1.4.0',
layers: [
mapnikLayer4,
mapnikLayer4
]
});
testClient.getLayergroup(function(err, layergroup) {
assert.ok(!err);
assert.equal(layergroup.metadata.layers[0].id, mapnikBasicLayerId(0));
assert.equal(layergroup.metadata.layers[0].meta.stats.estimatedFeatureCount, 5);
assert.equal(layergroup.metadata.layers[1].id, mapnikBasicLayerId(1));
assert.equal(layergroup.metadata.layers[1].meta.stats.estimatedFeatureCount, 5);
testClient.drain(done);
});
});
it('with two mapnik layer (with & without join) should response with meta-stats for every layer', function(done) {
var testClient = new TestClient({
version: '1.4.0',
layers: [
mapnikLayer3,
mapnikLayer4
]
});
testClient.getLayergroup(function(err, layergroup) {
assert.ok(!err);
assert.equal(layergroup.metadata.layers[0].id, mapnikBasicLayerId(0));
assert.equal(layergroup.metadata.layers[0].meta.stats.estimatedFeatureCount, 3);
assert.ok(!layergroup.metadata.layers[0].meta.stats[1]);
assert.equal(layergroup.metadata.layers[1].id, mapnikBasicLayerId(1));
assert.equal(layergroup.metadata.layers[1].meta.stats.estimatedFeatureCount, 5);
assert.ok(!layergroup.metadata.layers[2]);
testClient.drain(done);
});
});
it('with mapnik and layer and httplayer should response with layer metadata with same order', function(done) {
var testClient = new TestClient({
version: '1.4.0',
layers: [
mapnikLayer1,
httpLayer
]
});
testClient.getLayergroup(function(err, layergroup) {
assert.ok(!err);
assert.equal(layergroup.metadata.layers[0].id, mapnikBasicLayerId(0));
assert.equal(layergroup.metadata.layers[0].type, 'mapnik');
assert.equal(layergroup.metadata.layers[0].meta.stats.estimatedFeatureCount, 1);
assert.equal(layergroup.metadata.layers[1].id, typeLayerId('http', 0));
assert.equal(layergroup.metadata.layers[1].type, 'http');
testClient.drain(done);
});
});
it('with httpLayer and mapnik layer should response with layer metadata with same order', function(done) {
var testClient = new TestClient({
version: '1.4.0',
layers: [
httpLayer,
mapnikLayer1
]
});
testClient.getLayergroup(function (err, layergroup) {
assert.ok(!err);
assert.equal(layergroup.metadata.layers[0].id, typeLayerId('http', 0));
assert.equal(layergroup.metadata.layers[0].type, 'http');
assert.ok(!layergroup.metadata.layers[0].meta.cartocss);
assert.equal(layergroup.metadata.layers[1].meta.stats.estimatedFeatureCount, 1);
assert.equal(layergroup.metadata.layers[1].id, mapnikBasicLayerId(0));
assert.equal(layergroup.metadata.layers[1].type, 'mapnik');
assert.equal(layergroup.metadata.layers[1].meta.cartocss, cartocss);
testClient.drain(done);
});
});
it('should work with different geom_column', function(done) {
var testClient = new TestClient({
version: '1.4.0',
layers: [
mapnikLayerGeomColumn
]
});
testClient.getLayergroup(function(err, layergroup) {
assert.ok(!err);
assert.equal(layergroup.metadata.layers[0].id, mapnikBasicLayerId(0));
// we don't care about stats here as is an aliased column
assert.ok(layergroup.metadata.layers[0].meta.stats.hasOwnProperty('estimatedFeatureCount'));
testClient.drain(done);
});
});
it('should not include the stats part if the FF is disabled', function(done) {
global.environment.enabledFeatures.layerStats = false;
var testClient = new TestClient({
version: '1.4.0',
layers: [
httpLayer,
mapnikLayer1,
httpLayer
]
});
testClient.getLayergroup(function(err, layergroup) {
assert.ok(!err);
assert.equal(layergroup.metadata.layers[0].id, typeLayerId('http', 0));
assert.equal(layergroup.metadata.layers[0].type, 'http');
assert.equal(layergroup.metadata.layers[1].id, mapnikBasicLayerId(0));
assert.equal(layergroup.metadata.layers[1].type, 'mapnik');
assert.ok(!layergroup.metadata.layers[1].meta.hasOwnProperty('stats'));
assert.equal(layergroup.metadata.layers[2].id, typeLayerId('http', 1));
assert.equal(layergroup.metadata.layers[2].type, 'http');
testClient.drain(done);
});
});
});

View File

@@ -0,0 +1,225 @@
require('../../support/test_helper');
var assert = require('../../support/assert');
var TestClient = require('../../support/test-client');
describe('multilayer stats disabled', function() {
before(function () {
this.layerMetadataConfig = global.environment.enabledFeatures.layerMetadata;
this.layerStatsConfig = global.environment.enabledFeatures.layerStats;
global.environment.enabledFeatures.layerMetadata = true;
global.environment.enabledFeatures.layerStats = false;
});
after(function () {
global.environment.enabledFeatures.layerMetadata = this.layerMetadataConfig;
global.environment.enabledFeatures.layerStats = this.layerStatsConfig;
});
function testLayerMetadataStats(testScenario) {
it(testScenario.desc, function(done) {
var mapConfig = {
version: '1.3.0',
layers: testScenario.layers
};
var testClient = new TestClient(mapConfig);
testClient.getLayergroup(function(err, layergroup) {
assert.ifError(err);
layergroup.metadata.layers.forEach(function (layer) {
if (layer.type !== 'torque' && layer.type !== 'mapnik') {
assert.ok(!('stats' in layer.meta));
} else if (layer.type !== 'torque') {
assert.ok(!('stats' in layer.meta));
assert.ok('cartocss' in layer.meta);
} else {
assert.ok('cartocss' in layer.meta);
// check torque metadata at least match in number
var torqueLayers = mapConfig.layers.filter(function(layer) { return layer.type === 'torque'; });
if (torqueLayers.length) {
assert.equal(Object.keys(layergroup.metadata.torque).length, torqueLayers.length);
}
}
});
testClient.drain(done);
});
});
}
var cartocssVersion = '2.3.0';
var cartocss = '#layer { line-width:16; }';
var sql = "select 1 as i, st_setsrid('LINESTRING(0 0, 1 0)'::geometry, 4326) as the_geom, " +
"st_setsrid('LINESTRING(0 0, 1 0)'::geometry, 3857) as the_geom_webmercator";
var sqlWadus = "select 1 as wadus, st_setsrid('LINESTRING(0 0, 1 0)'::geometry, 4326) as the_geom, " +
"st_setsrid('LINESTRING(0 0, 1 0)'::geometry, 3857) as the_geom_webmercator";
var httpLayer = {
type: 'http',
options: {
urlTemplate: 'http://{s}.basemaps.cartocdn.com/dark_nolabels/{z}/{x}/{y}.png',
subdomains: ['a','b','c']
}
};
var torqueLayer = {
type: 'torque',
options: {
sql: "select 1 id, '1970-01-02'::date d, 'POINT(0 0)'::geometry the_geom_webmercator",
cartocss: [
"Map {",
"-torque-frame-count:2;",
"-torque-resolution:3;",
"-torque-time-attribute:d;",
"-torque-aggregation-function:'count(id)';",
"}"
].join(' '),
cartocss_version: '2.0.1'
}
};
var mapnikLayer = {
type: 'mapnik',
options: {
sql: sql,
cartocss_version: cartocssVersion,
cartocss: cartocss
}
};
var mapnikInteractivityLayer = {
type: 'mapnik',
options: {
sql: sql,
cartocss_version: cartocssVersion,
cartocss: cartocss,
interactivity: 'i'
}
};
var cartodbLayer = {
type: 'cartodb',
options: {
sql: sql,
cartocss_version: cartocssVersion,
cartocss: cartocss
}
};
var cartodbInteractivityLayer = {
type: 'cartodb',
options: {
sql: sql,
cartocss_version: cartocssVersion,
cartocss: cartocss,
interactivity: 'i'
}
};
var cartodbWadusInteractivityLayer = {
type: 'cartodb',
options: {
sql: sqlWadus,
cartocss_version: cartocssVersion,
cartocss: cartocss,
interactivity: 'wadus'
}
};
var noTypeLayer = {
options: {
sql: sql,
cartocss_version: cartocssVersion,
cartocss: cartocss
}
};
var noTypeInteractivityLayer = {
options: {
sql: sql,
cartocss_version: cartocssVersion,
cartocss: cartocss,
interactivity: 'i'
}
};
var testScenarios = [
{
desc: 'one layer, no interactivity',
layers: [cartodbLayer]
},
{
desc: 'two layers, different interactivity columns',
layers: [
cartodbWadusInteractivityLayer,
cartodbInteractivityLayer
]
},
{
desc: 'torque + interactivity layers',
layers: [
torqueLayer,
cartodbWadusInteractivityLayer,
cartodbInteractivityLayer
]
},
{
desc: 'interactivity + torque + interactivity',
layers: [
cartodbInteractivityLayer,
torqueLayer,
cartodbInteractivityLayer
]
},
{
desc: 'http + interactivity + torque + no interactivity + torque + interactivity',
layers: [
httpLayer,
cartodbInteractivityLayer,
torqueLayer,
cartodbLayer,
torqueLayer,
cartodbWadusInteractivityLayer
]
},
{
desc: 'mapnik type two layers, interactivity mix',
layers: [
mapnikLayer,
mapnikInteractivityLayer
]
},
{
desc: 'mapnik type http + interactivity + torque + interactivity',
layers: [
httpLayer,
mapnikInteractivityLayer,
torqueLayer,
cartodbInteractivityLayer
]
},
{
desc: 'no type two layers, interactivity mix',
layers: [
noTypeLayer,
noTypeInteractivityLayer
]
},
{
desc: 'no type http + interactivity + torque + interactivity',
layers: [
httpLayer,
noTypeInteractivityLayer,
torqueLayer,
noTypeInteractivityLayer
]
}
];
testScenarios.forEach(testLayerMetadataStats);
});

View File

@@ -1052,8 +1052,9 @@ describe('template_api', function() {
'Unexpected error for authorized instance: ' + res.statusCode + ' -- ' + res.body);
assert.equal(res.headers['content-type'], "application/json; charset=utf-8");
var cc = res.headers['x-cache-channel'];
var expectedCC = 'test_windshaft_cartodb_user_1_db:public.test_table_private_1';
assert.ok(cc);
assert.ok(cc.match, /ciao/, cc);
assert.equal(cc, expectedCC);
// hack simulating restart...
server.layergroupAffectedTablesCache.cache.reset(); // need to clean channel cache
var get_request = {
@@ -1072,8 +1073,9 @@ describe('template_api', function() {
'Unexpected error for authorized instance: ' + res.statusCode + ' -- ' + res.body);
assert.equal(res.headers['content-type'], "application/json; charset=utf-8");
var cc = res.headers['x-cache-channel'];
var expectedCC = 'test_windshaft_cartodb_user_1_db:public.test_table_private_1';
assert.ok(cc, "Missing X-Cache-Channel on fetch-after-restart");
assert.ok(cc.match, /ciao/, cc);
assert.equal(cc, expectedCC);
return null;
},
function deleteTemplate(err)

View File

@@ -322,6 +322,25 @@ describe('widgets', function() {
});
});
});
[adm0name].forEach(function(userQuery) {
it('should search with sum aggregation: ' + userQuery, function(done) {
this.testClient = new TestClient(aggregationSumMapConfig);
this.testClient.widgetSearch('adm0name', userQuery, function (err, res, searchResult) {
assert.ok(!err, err);
assert.ok(searchResult);
assert.equal(searchResult.type, 'aggregation');
assert.equal(searchResult.categories.length, 1);
assert.deepEqual(
searchResult.categories,
[{ category:"Argentina", value:28015640 }]
);
done();
});
});
});
});
});

View File

@@ -304,6 +304,37 @@ describe('widgets', function() {
});
});
it('can use a datetime filtered column with no results', function(done) {
this.testClient = new TestClient(histogramsMapConfig({
updated_at: {
type: 'histogram',
options: {
column: 'updated_at'
}
}
}));
var params = {
own_filter: 1,
filters: {
layers: [{
updated_at: {
// this will remove all results
max: -1
}
}]
}
};
this.testClient.getWidget('updated_at', params, function (err, res, histogram) {
assert.ok(!err, err);
assert.ok(histogram);
assert.equal(histogram.type, 'histogram');
assert.equal(histogram.bins.length, 0);
done();
});
});
it('can getTile with datetime filtered column', function(done) {
this.testClient = new TestClient(histogramsMapConfig({
updated_at: {

View File

@@ -1,307 +0,0 @@
var testHelper = require('../support/test_helper');
var assert = require('../support/assert');
var qs = require('querystring');
var CartodbWindshaft = require('../../lib/cartodb/server');
var serverOptions = require('../../lib/cartodb/server_options');
var server = new CartodbWindshaft(serverOptions);
server.setMaxListeners(0);
var LayergroupToken = require('../support/layergroup-token');
describe('get requests x-cache-channel', function() {
var keysToDelete;
beforeEach(function() {
keysToDelete = {};
});
afterEach(function(done) {
testHelper.deleteRedisKeys(keysToDelete, done);
});
var statusOkResponse = {
status: 200
};
var mapConfig = {
version: '1.3.0',
layers: [
{
options: {
sql: 'select * from test_table limit 2',
cartocss: '#layer { marker-fill:red; }',
cartocss_version: '2.3.0',
attributes: {
id:'cartodb_id',
columns: [
'name',
'address'
]
}
}
}
]
};
var layergroupRequest = {
url: '/api/v1/map?config=' + encodeURIComponent(JSON.stringify(mapConfig)),
method: 'GET',
headers: {
host: 'localhost'
}
};
function getRequest(url, addApiKey, callbackName) {
var params = {};
if (!!addApiKey) {
params.api_key = '1234';
}
if (!!callbackName) {
params.callback = callbackName;
}
return {
url: url + '?' + qs.stringify(params),
method: 'GET',
headers: {
host: 'localhost',
'Content-Type': 'application/json'
}
};
}
function validateXCacheChannel(done, expectedCacheChannel) {
return function(res, err) {
if (err) {
return done(err);
}
assert.ok(res.headers['x-cache-channel']);
if (expectedCacheChannel) {
assert.equal(res.headers['x-cache-channel'], expectedCacheChannel);
}
done();
};
}
function noXCacheChannelHeader(done) {
return function(res, err) {
if (err) {
return done(err);
}
assert.ok(
!res.headers['x-cache-channel'],
'did not expect x-cache-channel header, got: `' + res.headers['x-cache-channel'] + '`'
);
done();
};
}
function withLayergroupId(callback) {
assert.response(
server,
layergroupRequest,
statusOkResponse,
function(res, err) {
if (err) {
return callback(err);
}
var layergroupId = JSON.parse(res.body).layergroupid;
keysToDelete['map_cfg|' + LayergroupToken.parse(layergroupId).token] = 0;
keysToDelete['user:localhost:mapviews:global'] = 5;
callback(null, layergroupId, res);
}
);
}
describe('header should be present', function() {
it('/api/v1/map Map instantiation', function(done) {
var testFn = validateXCacheChannel(done, 'test_windshaft_cartodb_user_1_db:public.test_table');
withLayergroupId(function(err, layergroupId, res) {
testFn(res);
});
});
it ('/api/v1/map/:token/:z/:x/:y@:scale_factor?x.:format Mapnik retina tiles', function(done) {
withLayergroupId(function(err, layergroupId) {
assert.response(
server,
getRequest('/api/v1/map/' + layergroupId + '/0/0/0@2x.png'),
validateXCacheChannel(done, 'test_windshaft_cartodb_user_1_db:public.test_table')
);
});
});
it ('/api/v1/map/:token/:z/:x/:y@:scale_factor?x.:format Mapnik tiles', function(done) {
withLayergroupId(function(err, layergroupId) {
assert.response(
server,
getRequest('/api/v1/map/' + layergroupId + '/0/0/0.png'),
validateXCacheChannel(done, 'test_windshaft_cartodb_user_1_db:public.test_table')
);
});
});
it ('/api/v1/map/:token/:layer/:z/:x/:y.(:format) Per :layer rendering', function(done) {
withLayergroupId(function(err, layergroupId) {
assert.response(
server,
getRequest('/api/v1/map/' + layergroupId + '/0/0/0/0.png'),
validateXCacheChannel(done, 'test_windshaft_cartodb_user_1_db:public.test_table')
);
});
});
it ('/api/v1/map/:token/:layer/attributes/:fid endpoint for info windows', function(done) {
withLayergroupId(function(err, layergroupId) {
assert.response(
server,
getRequest('/api/v1/map/' + layergroupId + '/0/attributes/1'),
validateXCacheChannel(done, 'test_windshaft_cartodb_user_1_db:public.test_table')
);
});
});
it ('/api/v1/map/static/center/:token/:z/:lat/:lng/:width/:height.:format static maps', function(done) {
withLayergroupId(function(err, layergroupId) {
assert.response(
server,
getRequest('/api/v1/map/static/center/' + layergroupId + '/0/0/0/400/300.png'),
validateXCacheChannel(done, 'test_windshaft_cartodb_user_1_db:public.test_table')
);
});
});
it ('/api/v1/map/static/bbox/:token/:bbox/:width/:height.:format static maps', function(done) {
withLayergroupId(function(err, layergroupId) {
assert.response(
server,
getRequest('/api/v1/map/static/bbox/' + layergroupId + '/-45,-45,45,45/400/300.png'),
validateXCacheChannel(done, 'test_windshaft_cartodb_user_1_db:public.test_table')
);
});
});
});
describe('header should NOT be present', function() {
it('/', function(done) {
assert.response(
server,
getRequest('/'),
statusOkResponse,
noXCacheChannelHeader(done)
);
});
it('/version', function(done) {
assert.response(
server,
getRequest('/version'),
statusOkResponse,
noXCacheChannelHeader(done)
);
});
it('/health', function(done) {
assert.response(
server,
getRequest('/health'),
statusOkResponse,
noXCacheChannelHeader(done)
);
});
it('/api/v1/map/named list named maps', function(done) {
assert.response(
server,
getRequest('/api/v1/map/named', true),
statusOkResponse,
noXCacheChannelHeader(done)
);
});
describe('with named maps', function() {
var templateName = 'x_cache';
beforeEach(function(done) {
var template = {
version: '0.0.1',
name: templateName,
auth: {
method: 'open'
},
layergroup: mapConfig
};
var namedMapRequest = {
url: '/api/v1/map/named?api_key=1234',
method: 'POST',
headers: {
host: 'localhost',
'Content-Type': 'application/json'
},
data: JSON.stringify(template)
};
assert.response(
server,
namedMapRequest,
statusOkResponse,
function(res, err) {
done(err);
}
);
});
afterEach(function(done) {
assert.response(
server,
{
url: '/api/v1/map/named/' + templateName + '?api_key=1234',
method: 'DELETE',
headers: {
host: 'localhost'
}
},
{
status: 204
},
function(res, err) {
done(err);
}
);
});
it('/api/v1/map/named/:template_id Named map retrieval', function(done) {
assert.response(
server,
getRequest('/api/v1/map/named/' + templateName, true),
statusOkResponse,
noXCacheChannelHeader(done)
);
});
it('/api/v1/map/named/:template_id/jsonp Named map retrieval', function(done) {
assert.response(
server,
getRequest('/api/v1/map/named/' + templateName, true, 'cb'),
statusOkResponse,
noXCacheChannelHeader(done)
);
});
});
});
});

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -0,0 +1 @@
{"type":"FeatureCollection","features":[{"type":"Feature","geometry":{"type":"Point","coordinates":[-53839,4629161]},"properties":{"name":"Alicante","cartodb_id":1200}},{"type":"Feature","geometry":{"type":"Point","coordinates":[242835,5069332]},"properties":{"name":"Barcelona","cartodb_id":5330}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-5567,4861644]},"properties":{"name":"Castello","cartodb_id":1201}},{"type":"Feature","geometry":{"type":"Point","coordinates":[272735,5092314]},"properties":{"name":"Mataro","cartodb_id":615}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-125787,4576600]},"properties":{"name":"Murcia","cartodb_id":952}},{"type":"Feature","geometry":{"type":"Point","coordinates":[295469,4804267]},"properties":{"name":"Palma","cartodb_id":5500}},{"type":"Feature","geometry":{"type":"Point","coordinates":[139148,5030112]},"properties":{"name":"Tarragona","cartodb_id":616}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-44746,4791667]},"properties":{"name":"Valencia","cartodb_id":5942}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-99072,5108695]},"properties":{"name":"Zaragoza","cartodb_id":5932}}]}

View File

@@ -0,0 +1 @@
{"grid":[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," !! ","!!! !!!!! ","!!!!!!! ! ","!!! !!!!! "," !! ! "," "," "," "," "," "," "," "," ### # "," ####### ###"," ####### ## ","$ ## #### ## ","$$ ","$$ ","$$ "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "],"keys":["","9","2","1"],"data":{"1":{"cartodb_id":5942},"2":{"cartodb_id":5500},"9":{"cartodb_id":1201}}}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@@ -0,0 +1 @@
{"grid":[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," !!! ! "," !!!!!!! !!!"," !!!!!!! !! "," !! !!!! !! "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "],"keys":["","1"],"data":{"1":{"cartodb_id":5500}}}

View File

@@ -0,0 +1 @@
{"type":"FeatureCollection","features":[{"type":"Feature","geometry":{"type":"Point","coordinates":[295469,4804267]},"properties":{"name":"Palma","cartodb_id":5500}}]}

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

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