Compare commits

..

1075 Commits

Author SHA1 Message Date
Álvaro
c792421687 undefined might come as a string (#1196)
so a hack to check and cast it to a real undefined

Co-authored-by: Álvaro Manera <amanera@cartodb.com>
2021-01-15 10:44:05 +01:00
Álvaro
15135b475c add missing env vars (#1195)
* add missing env vars

* add missing space

Co-authored-by: Álvaro Manera <amanera@cartodb.com>
Co-authored-by: alberhander <albertoh@carto.com>
2021-01-14 10:08:25 +01:00
Álvaro Manera
fd9f935676 submodule bump 2020-12-23 07:37:54 +01:00
Álvaro Manera
71f5886a4d 🤬 yaml 2020-12-21 13:21:44 +01:00
Álvaro Manera
bc8c9f973c yaml typo 2020-12-21 13:19:52 +01:00
Álvaro Manera
ec40614f4b configure docker before push 2020-12-21 13:14:36 +01:00
Álvaro
5ed1a3a2d1 Merge pull request #1194 from CartoDB/clean
Use Github actions for builds
2020-12-21 09:06:52 +01:00
Álvaro Manera
0aa5f394e2 update submodule 2020-12-18 16:03:31 +01:00
Álvaro Manera
2e1a3c7fb1 small fixes 2020-12-18 15:22:26 +01:00
Álvaro Manera
27eb00223d minor PR comments 2020-12-18 07:27:59 +01:00
Álvaro Manera
8d46780006 fix master build 2020-12-17 07:22:31 +01:00
Álvaro Manera
6ffd2c090e fix build 2020-12-17 07:15:57 +01:00
Álvaro Manera
3995787c02 use token to pull repos 2020-12-17 07:07:09 +01:00
Álvaro Manera
ddb1b0c0d8 udpate paths and pull submodule 2020-12-16 16:47:19 +01:00
Álvaro Manera
a03d268260 add submodule 2020-12-16 16:40:41 +01:00
Álvaro
5c491a25cf Use env vars and fix tests 2020-12-16 16:32:32 +01:00
Shylpx
92be27e700 Merge pull request #1192 from CartoDB/feature/ch89482/mr-jeff-if-a-column-name-has-an-uppercase
[ch89482] Update 'camshaft' to version 0.67.2
2020-11-23 10:26:20 +00:00
cgonzalez
6b61f5e168 Update 'camshaft' to version 0.67.2 2020-11-18 12:37:38 +00:00
Daniel G. Aubert
d79f1b41d0 Merge pull request #1190 from CartoDB/feature/ch101625/node-windshaft-exiting-because-of-typeerror
Fix logger error serializer when the exception stack is not set
2020-09-09 14:07:23 +02:00
Jorge Tarrero
e039204638 Fix linter 2020-09-09 11:43:33 +02:00
Jorge Tarrero
dc1becd15c Fix logger error serializer when the exception stack is not set 2020-09-09 11:29:32 +02:00
Daniel G. Aubert
a121fd75ab Merge pull request #1189 from CartoDB/fix-kibana-index-bis
Update camshaft to version 0.67.1
2020-08-26 11:50:50 +02:00
Daniel García Aubert
f85417a886 Update NEWS 2020-08-26 11:39:34 +02:00
Daniel García Aubert
8ad72ff2ce Update camshaft to version 0.67.1 2020-08-26 11:36:38 +02:00
Daniel García Aubert
4dd6bc466a Use development version of camshaft 2020-08-26 11:06:26 +02:00
Daniel García Aubert
c119c92de6 Use development version of camshaft 2020-08-26 10:50:16 +02:00
Daniel García Aubert
a3f7acb213 Use development version of camshaft 2020-08-26 10:41:34 +02:00
Daniel García Aubert
0f14ed55db Use development version of camshaft 2020-08-26 09:35:00 +02:00
Rafa de la Torre
528395103b Merge pull request #1186 from CartoDB/feature/ch94770/node-minimal-doc-in-the-repos-about-how-to
[Node] Minimal doc in the repos about how to add new log traces
2020-08-04 09:20:45 +02:00
Rafa de la Torre
288cd9584f Markdown about how to write log traces 2020-08-03 16:31:00 +02:00
Alberto Asuero
cf82e1954e Merge pull request #1185 from CartoDB/alasarr/gitignore
Adding docker ressources to .gitignore
2020-07-29 21:43:13 +02:00
Alberto Asuero
3b00cffc3b New line at .gitignore 2020-07-28 08:58:33 +00:00
Alberto Asuero
95bf39cada Adding docker ressources to .gitignore 2020-07-28 08:56:38 +00:00
Daniel G. Aubert
f9ad3c8acf Merge pull request #1184 from CartoDB/feature/ch91877/remove-log-aggregation-in-metro
Logger: rename key 'msg' => 'event_message'
2020-07-23 14:12:17 +02:00
Daniel García Aubert
28f70f6877 Logger: rename key 'msg' => 'event_message' 2020-07-23 14:01:34 +02:00
Daniel G. Aubert
d5c5d07507 Merge pull request #1183 from CartoDB/feature/ch91877/remove-log-aggregation-in-metro
Metro: stop aggregating logs per request id
2020-07-22 16:00:41 +02:00
Daniel García Aubert
b646f71394 Don't miss the header 2020-07-22 13:35:43 +02:00
Daniel García Aubert
38fe2169aa Update camshaft to version 0.67.0 2020-07-22 13:03:51 +02:00
Daniel García Aubert
a749d4fb43 Typo 2020-07-22 11:40:45 +02:00
Daniel García Aubert
b9198b59a1 Logger: rename 'error' => 'exception' to avoid name clashing in E/S 2020-07-21 17:53:46 +02:00
Daniel García Aubert
3102d895f2 Update camshaft devel version 2020-07-21 17:15:50 +02:00
Daniel García Aubert
b60a69e7d2 Logger: rename level => levelname to avoid name collision 2020-07-21 17:09:19 +02:00
Daniel García Aubert
3937b8c271 Adapt JSON output to the standard structure 2020-07-21 16:36:23 +02:00
Daniel García Aubert
b32a073ac3 Metro: stop aggregating log per request id, use new config.json file 2020-07-20 19:33:33 +02:00
Daniel García Aubert
afd4ad500f Lint 2020-07-01 10:53:04 +02:00
Daniel G. Aubert
cb17bba3f5 Merge pull request #1181 from CartoDB/feature/ch88712/node-windshaft-metro-service-is-not-started
Fix: TypeError: Cannot read property 'level' of undefined
2020-07-01 08:58:26 +02:00
Daniel García Aubert
5b7341c0e9 Fix: TypeError: Cannot read property 'level' of undefined
Feature: dump unfinished log into a file while exiting
2020-06-29 21:01:48 +02:00
Daniel G. Aubert
d65565c091 Merge pull request #1170 from CartoDB/dgaubert/ch78384/maps-api-replace-log4js-logger-by-pino
Replace log4js logger by pino
2020-06-23 11:56:03 +02:00
Daniel García Aubert
360b98254b Upgrade camshaft to version 0.66.0 2020-06-22 17:01:29 +02:00
Daniel García Aubert
43a603922d Update NEWS 2020-06-22 12:24:06 +02:00
Daniel García Aubert
74116523b4 Merge branch 'master' into dgaubert/ch78384/maps-api-replace-log4js-logger-by-pino 2020-06-22 12:12:45 +02:00
Daniel García Aubert
6cddec562a Profiler don't log times if there is no one task done at least 2020-06-19 11:42:46 +02:00
Daniel García Aubert
22086ba914 Count requests even when the info is not complete 2020-06-19 10:18:06 +02:00
Daniel García Aubert
a68618c336 Prepare init log to be kibana friendly 2020-06-12 10:12:32 +02:00
Daniel García Aubert
578f543c01 log user 2020-06-11 18:21:13 +02:00
Daniel García Aubert
49735308de Do not rename level and error fields 2020-06-11 18:15:44 +02:00
Daniel García Aubert
2444b4c008 rename error => errors to avoid type clashing in ES 2020-06-11 13:04:19 +02:00
Daniel García Aubert
bf250e592a rename level => levelname to avoid type clashing in ES 2020-06-11 12:30:15 +02:00
Daniel García Aubert
f6c8796c8a Do not duplicate timer 2020-06-11 10:12:27 +02:00
Daniel G. Aubert
649f8d701e Merge pull request #1173 from CartoDB/dgaubert/ch78384/maps-api-replace-log4js-logger-by-pino-bis
Logger improvements
2020-06-11 09:39:07 +02:00
Daniel G. Aubert
568e428a58 Merge pull request #1174 from CartoDB/dgaubert/ch78384/maps-api-replace-log4js-logger-by-pino-bis-bis
Create log-collector utilility
2020-06-11 09:38:34 +02:00
Daniel G. Aubert
ff00fed43e Merge pull request #1175 from CartoDB/dgaubert/ch78384/maps-api-replace-log4js-logger-by-pino-bis-bis-bis
Tags Middleware
2020-06-11 09:38:14 +02:00
Daniel G. Aubert
561bc8aef0 Merge pull request #1177 from CartoDB/dgaubert/ch78384/maps-api-replace-log4js-logger-by-pino-bis-bis-bis-bis
Introducing @carto/metro the CARTO's logs and metrics transport.
2020-06-11 09:37:58 +02:00
Daniel García Aubert
e49ecda321 Don't create a new metric for each request, use the same label to send to statsd 2020-06-10 17:21:35 +02:00
Daniel García Aubert
18525a60cd Use 9145 as default port for metics 2020-06-09 16:35:08 +02:00
Daniel G. Aubert
b8d3971c8a Merge pull request #1178 from CartoDB/fix-layergroup-structure
Layergroup Id should have cache buster defined always
2020-06-09 15:02:28 +02:00
Daniel García Aubert
23839f5b4a Update NEWS 2020-06-09 15:00:53 +02:00
Daniel García Aubert
f235dcdeda Add test 2020-06-09 13:05:39 +02:00
Daniel García Aubert
9c21194c68 Set cache buster equal to 0 when there is no affected tables in the mapconfig 2020-06-09 12:21:47 +02:00
Daniel García Aubert
7acbfc1e9b Typos 2020-06-09 10:00:54 +02:00
Daniel García Aubert
6f9580bae2 Allow the metro to exit if this is the only active server in the event loop system 2020-06-09 09:52:56 +02:00
Daniel García Aubert
3583e064be User native http server 2020-06-09 09:40:24 +02:00
Daniel García Aubert
9e14185990 Rename 2020-06-09 09:30:11 +02:00
Daniel García Aubert
a5c83edef6 Introducing @carto/metro, the carto logs and metrics transport. 2020-06-08 20:16:00 +02:00
Daniel García Aubert
04d0f2e530 Merge branch 'dgaubert/ch78384/maps-api-replace-log4js-logger-by-pino-bis-bis' into dgaubert/ch78384/maps-api-replace-log4js-logger-by-pino-bis-bis-bis 2020-06-08 19:31:18 +02:00
Daniel García Aubert
e206a1bca3 Check whether the log is a pino's log and skip them when they aren't 2020-06-08 16:34:08 +02:00
Daniel García Aubert
b115bca07e Be able to tag requests with labels as easier way to provide business metrics 2020-06-08 16:29:22 +02:00
Daniel García Aubert
07b9decb03 Add log-collector utlity, it will be moved to its onw repository. Attaching it here fro development purposes. Try it with the following command LOG_LEVEL=info npm t | node ./log-collector.js 2020-06-05 20:12:20 +02:00
Daniel García Aubert
02c8e28494 Finalize request's log 2020-06-05 20:08:40 +02:00
Daniel García Aubert
d28744a5e3 Be able to pass the logger to the analysis creation (camshaft) while instantiating a named map with analysis 2020-06-05 20:08:08 +02:00
Daniel García Aubert
a19e9a79b8 Release 9.0.0 2020-06-05 14:10:24 +02:00
Daniel García Aubert
4d7eb555a8 Update carto-package.json 2020-06-05 14:09:40 +02:00
Daniel García Aubert
6f9f53dd03 Be able to reduce the footprint in the final log file depending on the environment 2020-06-04 20:28:06 +02:00
Daniel García Aubert
63bc8f75b9 Typo 2020-06-04 18:43:21 +02:00
Daniel García Aubert
adeffd2018 Centralize common headers, this will help up to move biz metrics out of the process 2020-06-04 17:45:15 +02:00
Daniel G. Aubert
b2da00900f Merge pull request #1171 from CartoDB/dgaubert/ch78384/maps-api-replace-log4js-logger-by-pino-bis
Do not bind logger to global object
2020-06-04 12:14:21 +02:00
Daniel G. Aubert
0c6d5a1e18 Merge pull request #1172 from CartoDB/dgaubert/ch78384/maps-api-replace-log4js-logger-by-pino-bis-bis
Stop using profiler wrongly
2020-06-04 12:13:43 +02:00
Daniel García Aubert
6945cfc93c Add TODO 2020-06-04 12:10:15 +02:00
Daniel García Aubert
7b53b7c30a Stop using profiling wrongly. Now it only saves custom events from backends (tile, map, attributes, etc..) and calculates the response time. Besides, removed tags to know whether overviews are being used. 2020-06-03 19:51:56 +02:00
Daniel García Aubert
d073f7e3dd typo 2020-06-03 17:34:30 +02:00
Daniel García Aubert
210f5b01ec Make sure all errors use the serializer set for the logger 2020-06-03 17:32:16 +02:00
Daniel García Aubert
1dda183a31 typo 2020-06-03 16:19:42 +02:00
Daniel García Aubert
0eadfe6ee9 Simpligy error middleware 2020-06-03 15:52:24 +02:00
Daniel García Aubert
c37e3f173d Handle error properly in user middleware, it will logged in error middleware 2020-06-03 15:39:02 +02:00
Daniel García Aubert
107a97aa9e Honor @oleurud's comment 2020-06-03 15:11:08 +02:00
Daniel García Aubert
219d2c9044 Shortcuts for serializers 2020-06-03 15:10:31 +02:00
Daniel García Aubert
1e89821d97 Use standard serializers for error, request, and response 2020-06-03 14:28:35 +02:00
Daniel García Aubert
29c6505252 Do not set header 'x-tiler-profiler' and log it instead 2020-06-02 17:09:06 +02:00
Daniel García Aubert
7d8d05b865 Log errors and do not send 'X-Tiler-Errors' header 2020-06-02 16:15:01 +02:00
Daniel García Aubert
afeb91dc86 Bring back logger for windshaft 2020-06-02 13:20:57 +02:00
Daniel García Aubert
b7b3392bdd Be able to set log level from env variable LOG_LEVEL 2020-06-02 13:16:26 +02:00
Daniel García Aubert
b60116410a Use req/res logger instead of the one bound to global object 2020-06-02 12:31:18 +02:00
Daniel García Aubert
ffe19827fd Rename factory and don't use the keyword 'new' to create server while testing 2020-06-02 11:57:11 +02:00
Daniel García Aubert
48c28aea0b Do not bind logger to global object, now it's a part of serverOptions 2020-06-02 11:49:54 +02:00
Daniel García Aubert
62d66f2dbc Do not use global logger in middlewares, use the one initialized in res.locals instead 2020-06-02 09:00:45 +02:00
Daniel García Aubert
e644201756 Merge branch 'master' into dgaubert/ch78384/maps-api-replace-log4js-logger-by-pino 2020-06-01 19:23:21 +02:00
Daniel G. Aubert
481a5928c4 Merge pull request #1169 from CartoDB/update-deps
Update deps
2020-06-01 19:21:34 +02:00
Daniel García Aubert
163c546236 Replace log4js by pino as logger:
- Logs to stdout, disabled while testing
- Change log calls signature when needed
- Use development version of camshaft
- Removes unused log cofiguration
- Bind request id to log req/res
- Log req at the begining of the cycle and res at the end
2020-06-01 19:18:15 +02:00
Daniel García Aubert
656bc9344b Update deps 2020-06-01 13:50:09 +02:00
Daniel García Aubert
b79a8587fa Update deps to fix some security vuln 2020-06-01 12:35:31 +02:00
Daniel G. Aubert
17337974a2 Merge pull request #1168 from CartoDB/dgaubert/ch77050/data-in-headers
Avoid custom headers to be undefined
2020-05-29 16:16:19 +02:00
Daniel García Aubert
6bcf477532 Avoid custom headers to be undefined 2020-05-29 16:06:16 +02:00
Daniel G. Aubert
bf7e8a6ec6 Merge pull request #1167 from CartoDB/dgaubert/ch77050/data-in-headers
Add 'Carto-Stat-Tag', 'Carto-User-Id', and 'Carto-Client' headers
2020-05-26 17:14:32 +02:00
Daniel García Aubert
f31e8b43b6 Duplicate 2020-05-26 17:03:53 +02:00
Daniel García Aubert
0090811510 Typo 2020-05-26 16:56:50 +02:00
Daniel García Aubert
b97aeda53c Adapt test-client to handle client query param 2020-05-26 16:52:13 +02:00
Daniel García Aubert
f82232194c Under if 2020-05-26 16:31:53 +02:00
Daniel García Aubert
aff5c9a614 Add test to check the headers exist while instantiating a map 2020-05-26 16:28:44 +02:00
Daniel García Aubert
ddefb1a6ca Add 'Carto-Stat-Tag', 'Carto-User-Id', and 'Carto-Client' headers 2020-05-26 13:15:35 +02:00
Daniel G. Aubert
4d06fee1e2 Merge pull request #1164 from CartoDB/node-12
Support Node.js 12
2020-05-20 15:57:46 +02:00
Daniel García Aubert
8febd81ed2 Merge branch 'master' into node-12 2020-05-20 09:15:05 +02:00
Daniel García Aubert
e575f01bef Upgrade gc-stats to version 1.4.0 2020-05-14 19:32:47 +02:00
Raúl Marín
f25f507945 Merge pull request #1165 from Algunenano/clang9
Force our packages to be used
2020-05-14 18:11:15 +02:00
Raúl Marín
bdbb529ea8 Force clang-9 to be used 2020-05-14 17:52:06 +02:00
Daniel García Aubert
0aac942aa1 Make query idempotent among PG versions 2020-05-14 13:13:32 +02:00
Daniel García Aubert
8cc24bc665 - Drop support for Node.js < 12
- Support Node.js 12
- Upgrade `windshaft` to version `7.0.0`
- Upgrade `camshaft` to version `0.65.3`
- Upgrade `cartodb-redis` to version `3.0.0`
2020-05-14 13:00:23 +02:00
Daniel G. Aubert
478ea66678 Merge pull request #1162 from CartoDB/dgaubert/ch71093/update-maps-api-to-new-event-format
New event format for metrics
2020-05-01 13:34:57 +02:00
Daniel García Aubert
4dfc898587 Don't log when metrics where sent successfully 2020-05-01 13:25:03 +02:00
Daniel García Aubert
05e77b2aed Add test with mapconfig's query against a table to ensure cache buster metrics are sent with the right values. 2020-05-01 11:43:37 +02:00
Daniel García Aubert
24863b6393 Update NEWS 2020-05-01 10:51:33 +02:00
Daniel García Aubert
3cf17c8bab typo 2020-05-01 10:40:56 +02:00
Daniel García Aubert
8c38ecf808 Missing substring 2020-04-30 13:24:41 +02:00
Daniel García Aubert
a196a26ab4 Get templateHash for static tile request and errored named map instantiations 2020-04-30 13:09:12 +02:00
Daniel García Aubert
8d73571f5b Simplify assertions 2020-04-30 12:31:12 +02:00
Daniel García Aubert
d5348dd9d4 Rename fields from headers of metrics 2020-04-29 18:48:10 +02:00
Daniel García Aubert
7e31b956bf Send stat_tag metric when available 2020-04-29 18:25:01 +02:00
Daniel García Aubert
dbc5d65d90 Send template_hash as part of the metrics event 2020-04-29 17:26:33 +02:00
Daniel García Aubert
c91d78fe51 Also export template hash 2020-04-29 16:44:14 +02:00
Daniel García Aubert
798d010776 Ensure "map_id" and "cache_buster" as part of the event 2020-04-29 14:32:08 +02:00
Daniel García Aubert
70f0b6ea50 Avoid to use "pubsub" for the name of modules, middlewares, variables, etc.. 2020-04-29 10:40:45 +02:00
Daniel García Aubert
4e3ef96374 Add test to chek we still send events when errored map static tile 2020-04-29 10:28:10 +02:00
Daniel García Aubert
c88a14bf43 Send metrics for map instantiations (named, anonymous and static) with the new format. 2020-04-28 19:17:00 +02:00
Daniel García Aubert
7f5ed58a79 Add test 2020-04-27 18:40:28 +02:00
Daniel García Aubert
89e349146d Fix tests and stop using sinon as a dev dependency 2020-04-27 18:02:06 +02:00
Daniel García Aubert
c5cb2ea4cb Add FIXME comment 2020-04-27 13:35:19 +02:00
Daniel García Aubert
fe9610abe9 Missing logger argument 2020-04-27 13:35:07 +02:00
Daniel García Aubert
1bbde4f5e3 Let to the caller to choose how to handle the call to a method 2020-04-27 13:27:05 +02:00
Daniel García Aubert
e90c196598 Simplified metrics middleware and backend 2020-04-27 12:46:27 +02:00
Daniel García Aubert
6a2333be64 Topic name's lifetime is longer than pubsub backend, we can keep it as property. 2020-04-27 12:13:54 +02:00
Daniel García Aubert
7d6a64d383 Do not expose functions just to be able to mock them while testing 2020-04-27 11:59:36 +02:00
Daniel García Aubert
42dc2915ea Send pubsub metrics once the response has finished 2020-04-27 11:41:37 +02:00
Daniel García Aubert
3cec6b5a90 Missing callback 2020-04-27 11:06:09 +02:00
Daniel García Aubert
c31e3d6e3f Consistent interface when returning no event for eventa data in metrics 2020-04-27 10:58:37 +02:00
Daniel García Aubert
6e4c8a6639 Follow Node.js callback pattern 2020-04-27 10:23:11 +02:00
Manuel J. Morillo
809c267419 Merge pull request #1161 from CartoDB/fix_parsing_columns_histograms_1160
Fixes 1160: Prevent using cast column as part of __ctx_query
2020-04-23 13:12:32 +02:00
manmorjim
5ac27d1002 Update NEWS 2020-04-10 14:34:02 +02:00
manmorjim
7237fb04a8 Adding test for column date type in numeric histograms 2020-04-10 14:33:38 +02:00
manmorjim
d1696425fd Prevent using cast column from alias __ctx_query
Fixes #1160 by keep the original name of the column and using it if the
column type is date.
2020-04-10 14:14:24 +02:00
Raúl Marín
a614fb1ef6 Merge pull request #1159 from Algunenano/travis_12
Travis: Add pg12
2020-04-09 15:09:07 +02:00
Raúl Marín
aa38dd3b59 Travis: Add pg12 2020-04-09 13:20:51 +02:00
Daniel G. Aubert
2ac050501b Merge pull request #1158 from CartoDB/get-tile-promises
Version  9.0.0
2020-04-05 13:23:18 +02:00
Daniel García Aubert
03abe187ce Update NEWS and prepera next major release version 2020-04-05 13:16:45 +02:00
Daniel García Aubert
a83d0cf7af Update windshaft to released version 6.0.0 2020-04-05 12:59:58 +02:00
Daniel García Aubert
8bb4fbec12 Get the rendererCache's config right and avoid to set the NamedMapCacheReporter's interval to 'undefined' 2020-04-04 18:51:22 +02:00
Daniel García Aubert
a8fb51ba25 - Rename NamedMapProviderReporter by NamedMapProviderCacheReporter
- Extract getOnTileErrorStrategy to a module
- Stop using MapStore from windshaft while testing and create a custom one instead
2020-04-04 17:46:08 +02:00
Daniel García Aubert
24efc37737 Update windshaft development version 2020-04-04 17:42:52 +02:00
Daniel García Aubert
c25678cc28 Remove /version endpoint and bootstrapFonts at process startup (now done in windshaft) 2020-04-04 17:42:26 +02:00
Daniel García Aubert
44970b78a1 TODO 2020-04-04 17:35:09 +02:00
Daniel García Aubert
a3bdbf6202 In tests, stop using mapnik module exposed by windshaft and require it from development dependencies 2020-04-04 17:34:22 +02:00
Daniel García Aubert
f583a4240a Remove jshint comments 2020-04-04 17:29:33 +02:00
Daniel García Aubert
4054c6923f Use new signature for onTileErrorStrategy 2020-03-27 19:38:28 +01:00
Daniel García Aubert
7a1d84a3fb Update windshaft 2020-03-27 16:59:30 +01:00
Daniel García Aubert
58ed7c0093 Lint 2020-03-23 10:07:24 +01:00
Daniel García Aubert
f56e79ed1f Update windshaft 2020-03-23 10:01:54 +01:00
Daniel García Aubert
45c423bbaf Update windshaft 2020-03-21 18:53:32 +01:00
Daniel García Aubert
78f47e5873 Update windshaft and send more metrics 2020-03-21 18:30:38 +01:00
Daniel García Aubert
21d1a56953 Update windshaft and use the new method that reports stats about cached renderers 2020-03-21 14:13:53 +01:00
Daniel García Aubert
69a02bcee0 Fix stat named map providers cache count 2020-03-20 18:50:22 +01:00
Daniel García Aubert
d2c0f553fc Update windshaft to development version 2020-03-18 19:50:35 +01:00
Daniel García Aubert
3967aecfdc Fix test where http-fallback-image renderer was failing quietly 2020-03-18 19:45:31 +01:00
Esther Lozano
7b8cc0a8b8 Add response time to pubsub events (#1155) 2020-03-10 11:40:01 +01:00
Daniel G. Aubert
28c4e89ab5 Merge pull request #1156 from CartoDB/camshaft-0.65.3
Upgrade camshaft to version 0.65.3
2020-03-05 12:14:52 +01:00
Daniel García Aubert
8c42ac9053 Update NEWS and project version 2020-03-05 11:40:46 +01:00
Daniel García Aubert
86987f9e69 Upgade camshaft to version 0.65.3 2020-03-05 11:36:23 +01:00
Simon Martín
33a8267d2c Merge pull request #1154 from CartoDB/add-pubsub-metrics
Add pubsub metrics
2020-02-27 11:14:32 +01:00
Esther Lozano
779a8a8927 Fix linter 2020-02-26 17:44:53 +01:00
Esther Lozano
1888302cee Avoid normalizing empty fields 2020-02-26 17:41:41 +01:00
Esther Lozano
34c446909e Trim fields when normalizing 2020-02-26 14:50:41 +01:00
Esther Lozano
583765a298 Normalize headers values for pubsub 2020-02-26 13:24:46 +01:00
Esther Lozano
4b1f0b5775 Add unit and integration tests for pubsub 2020-02-25 14:14:44 +01:00
Esther Lozano
8f81c810e0 Continue middleware chain after response or error 2020-02-25 14:14:20 +01:00
Esther Lozano
970be73052 Allow extra headers in the requests of test client 2020-02-24 12:30:46 +01:00
Esther Lozano
e85469cc3c Use middleware for all requests 2020-02-20 15:25:53 +01:00
Esther Lozano
4a41ee8f75 Add backend and middleware for pubsub metrics 2020-02-20 11:48:32 +01:00
Esther Lozano
9591a5a2b0 Store userId in res.locals 2020-02-20 11:47:44 +01:00
Javier Goizueta
8f510f401e Release 8.1.1 2020-02-17 18:49:24 +01:00
Javier Goizueta
92678c3dae Merge pull request #1152 from CartoDB/1117-camshaft-update
Upgrade camshaft to 0.65.2
2020-02-17 18:46:14 +01:00
Javier Goizueta
9f2d1f90d0 Update NEWS 2020-02-17 18:36:18 +01:00
Javier Goizueta
23e331610d Upgrade camshaft to 0.65.2 2020-02-17 18:36:05 +01:00
Esther Lozano
59cb6f9c9c Rename headers for metrics 2020-02-17 17:07:26 +01:00
Esther Lozano
98325495ea Allow metrics custom headers in cors 2020-02-13 12:52:20 +01:00
Daniel García Aubert
576518b2c8 Stubs next vesion 2020-01-27 12:56:44 +01:00
Daniel García Aubert
0631bafbbf Release 8.1.0 2020-01-27 12:47:57 +01:00
Daniel García Aubert
d9b6284914 Update carto_postgresql_ext minor dependency version 2020-01-27 12:45:35 +01:00
Daniel García Aubert
111b927033 Minor doc fixes 2019-12-30 12:43:00 +01:00
Daniel G. Aubert
d63337f06f Merge pull request #1150 from CartoDB/new-filters
New dataview filters: circle & polygon
2019-12-20 09:40:09 +01:00
Daniel García Aubert
7012e6a66a Update NEWS 2019-12-20 09:37:27 +01:00
Daniel García Aubert
726e1a2268 Add test to validate parameters 2019-12-16 16:12:57 +01:00
Daniel García Aubert
6e455a1205 Better condition 2019-12-16 12:54:17 +01:00
Daniel García Aubert
da07d550d2 Use ST_DWithin() 2019-12-16 12:30:52 +01:00
Daniel García Aubert
1829a634e9 Add formula dataview test 2019-12-16 09:28:11 +01:00
Daniel García Aubert
95f66b8c4b Transform from 3857 2019-12-13 12:36:13 +01:00
Daniel García Aubert
ea1f43bec7 Fix query to make the proper transformations 2019-12-11 11:02:31 +01:00
Daniel García Aubert
c877d0b964 Implement polygon filter 2019-12-03 10:58:55 +01:00
Daniel García Aubert
caf09ac644 Rename file 2019-12-03 10:02:51 +01:00
Daniel García Aubert
17f151cd5a Implement circle filter for dataviews 2019-12-02 18:36:41 +01:00
Daniel García Aubert
0940158d01 Implemented tests, happy cases 2019-12-02 16:17:55 +01:00
Daniel G. Aubert
e6bbe8351d Merge pull request #1148 from CartoDB/no-makefile
Simplify npm scripts
2019-12-02 15:14:17 +01:00
Raúl Marín
031bae2564 Merge pull request #1149 from Algunenano/find_cartodb
Update camshaft to 0.65.1
2019-12-02 14:54:05 +01:00
Raúl Marín
b8d790caab Update camshaft to 0.65.1 2019-12-02 14:22:41 +01:00
Daniel García Aubert
267557eb90 Simplify npm scripts 2019-12-02 14:15:40 +01:00
Daniel García Aubert
b2af93dfec Remove reference to unexistent file 2019-12-02 14:14:40 +01:00
Raúl Marín
7e81618769 Merge pull request #1147 from Algunenano/find_cartodb
Enforce the usage of cartodb schema
2019-12-02 13:58:33 +01:00
Daniel G. Aubert
eeac5ce998 Merge pull request #1146 from CartoDB/no-makefile
Leftovers from #1145
2019-12-02 13:55:00 +01:00
Raúl Marín
fcf2fd1455 Enforce the usage of cartodb schema 2019-12-02 13:43:21 +01:00
Daniel García Aubert
fb9dce0386 Lint 2019-12-02 12:56:21 +01:00
Daniel García Aubert
4c09a70647 Avoid to overwrite env, just extend it with new env variables 2019-12-02 12:51:16 +01:00
Daniel García Aubert
eee59abfa1 Remove unused bash script 2019-12-02 12:43:59 +01:00
Daniel G. Aubert
c7effbccb4 Merge pull request #1145 from CartoDB/no-makefile
Improve project usability
2019-12-02 12:36:41 +01:00
Daniel García Aubert
2912e4fea6 Update NEWS and Release version 2019-12-02 12:30:19 +01:00
Daniel García Aubert
2d09a214ae Leftovers from other PR 2019-12-02 11:02:35 +01:00
Daniel García Aubert
a88c085278 Update 'how to release' document 2019-12-01 19:03:47 +01:00
Daniel García Aubert
5dcca3e088 Update 'how to release' document 2019-12-01 19:00:27 +01:00
Daniel García Aubert
413a1685aa Set default timeout 2019-12-01 14:20:01 +01:00
Daniel García Aubert
7081a7ec3c Better script organization 2019-12-01 13:04:56 +01:00
Daniel García Aubert
33143ea28e Update doc 2019-12-01 13:04:31 +01:00
Daniel García Aubert
f8c86f3b72 Don't be bombastic 2019-11-29 17:23:27 +01:00
Daniel García Aubert
ae53cc736b Merge branch 'master' into no-makefile 2019-11-29 16:39:44 +01:00
Daniel García Aubert
eca75d1365 Removed unused docker files 2019-11-29 16:33:47 +01:00
Daniel García Aubert
ef201e6fcf Improve docker section in docs 2019-11-29 16:30:48 +01:00
Daniel García Aubert
38a556b7d6 Improve spelling in documentation 2019-11-29 15:55:41 +01:00
Daniel García Aubert
c071746768 npm install 2019-11-29 13:51:09 +01:00
Daniel García Aubert
57512ba48b Format 2019-11-29 13:47:38 +01:00
Daniel García Aubert
dcf765efda Merge pre-install scripts 2019-11-29 13:43:05 +01:00
Daniel García Aubert
525d41e63c Merge pre-install scripts 2019-11-29 13:41:18 +01:00
Daniel García Aubert
7d7ca0de4a Add run perms to pre-install script 2019-11-29 13:32:05 +01:00
Daniel García Aubert
11e5726ea9 Improve coverage section 2019-11-29 13:24:12 +01:00
Daniel García Aubert
d3f0c52474 Add test and coverage sections 2019-11-29 13:19:47 +01:00
Daniel García Aubert
8523f835dc Format 2019-11-29 13:13:31 +01:00
Daniel García Aubert
63ccfac599 Add links 2019-11-29 13:11:19 +01:00
Daniel García Aubert
283baa4a3f remove death link 2019-11-29 13:00:54 +01:00
Daniel García Aubert
c7bd132e2f Add developers center link 2019-11-29 12:58:16 +01:00
Daniel García Aubert
3c92e186d6 Missing optional requirement 2019-11-29 12:51:12 +01:00
Daniel García Aubert
67d8919f8a Typos 2019-11-29 12:48:00 +01:00
Daniel García Aubert
06c0b28d37 Add versioning and license sections 2019-11-29 12:44:31 +01:00
Daniel García Aubert
dfedb45254 Update description and keywords 2019-11-29 12:37:33 +01:00
Daniel García Aubert
b373965510 Improve format contributing section 2019-11-29 12:30:56 +01:00
Daniel García Aubert
52d887f3b4 typos 2019-11-29 12:27:15 +01:00
Daniel García Aubert
a6ca480210 Improve section 2019-11-29 11:27:11 +01:00
Daniel García Aubert
16e80424e0 Typo 2019-11-29 11:23:42 +01:00
Daniel García Aubert
6c72d3adbe Typo 2019-11-29 11:22:32 +01:00
Daniel García Aubert
bbc9c9fb9b Merge documents into README 2019-11-29 11:18:25 +01:00
Daniel García Aubert
42d0c4c040 Remove unused makefile scripts 2019-11-28 19:56:49 +01:00
Daniel García Aubert
8f99886d62 Clean script 2019-11-28 19:46:22 +01:00
Daniel García Aubert
60c01e583f Remove deprecated coverage dep. Use nyc instead 2019-11-28 19:46:02 +01:00
Daniel García Aubert
f21f89f561 Move script to docker folder 2019-11-28 18:52:22 +01:00
Daniel García Aubert
5f900a3b3c Update command 2019-11-28 18:45:05 +01:00
Daniel García Aubert
60db55b122 Missin configuration file in CI 2019-11-28 18:42:04 +01:00
Daniel García Aubert
d9c05a9333 Don't use bash script to run ci test 2019-11-28 18:28:01 +01:00
Daniel García Aubert
ab66ad83fd Exec psql commands in batches 2019-11-28 18:07:44 +01:00
Daniel García Aubert
3498fceb6a Improve npm script hooks 2019-11-28 18:07:25 +01:00
Daniel García Aubert
e841774978 Set node env for test 2019-11-28 17:30:29 +01:00
Daniel García Aubert
f297044203 Exec redis comands in batches 2019-11-28 17:30:06 +01:00
Daniel García Aubert
c7e803a94c missing middleware 2019-11-26 15:46:56 +01:00
Raúl Marín
ac198d5b5a Merge pull request #1143 from CartoDB/quote_columns
Quote columns
2019-11-25 13:55:38 +01:00
Raúl Marín
6eb66de94e Unify stripQuotes 2019-11-25 13:38:42 +01:00
Raúl Marín
f545b4d002 camshaft quote_columns 2019-11-25 13:17:09 +01:00
Daniel García Aubert
eee3e8b63c Draft: added script to setup and tear down tests 2019-11-22 19:47:00 +01:00
Daniel G. Aubert
69afee61e0 Merge pull request #1136 from CartoDB/eslint
Eslint
2019-11-14 16:16:47 +01:00
Daniel García Aubert
724f67d381 Update NEWS 2019-11-14 16:09:38 +01:00
Daniel García Aubert
8d69af4445 Merge branch 'master' into eslint 2019-11-14 14:53:53 +01:00
Daniel G. Aubert
3c301ce742 Merge pull request #1141 from CartoDB/fix-undefinded-layergroupTTL
Fix undefinded layergroup ttl
2019-11-14 14:50:32 +01:00
Daniel García Aubert
f87c432744 Use good defaults 2019-11-14 13:14:31 +01:00
Daniel García Aubert
d446ba9c1b Merge branch 'eslint' of github.com:CartoDB/Windshaft-cartodb into eslint 2019-11-14 12:34:47 +01:00
Daniel García Aubert
dc669f5cd4 Do not use object built-ins 2019-11-14 12:18:13 +01:00
Daniel García Aubert
d4719d5707 camel case 2019-11-14 12:17:26 +01:00
Daniel García Aubert
f9082dad94 Merge branch 'eslint' of github.com:CartoDB/Windshaft-cartodb into eslint 2019-11-14 11:13:04 +01:00
Daniel García Aubert
a8d421c9cc Merge branch 'eslint' of github.com:CartoDB/Windshaft-cartodb into eslint 2019-11-13 20:08:04 +01:00
Daniel García Aubert
7b13c12ab4 Automatically lint fixes 2019-11-13 20:07:41 +01:00
Daniel García Aubert
97f4adbc1a camel case 2019-11-13 20:05:19 +01:00
Daniel García Aubert
602ab44375 camel case 2019-11-13 19:47:29 +01:00
Daniel García Aubert
77e6fb8225 Avoid to pass undefined layergroupTTL 2019-11-13 19:29:41 +01:00
Daniel García Aubert
2c8a030ecb Merge branch 'master' into eslint 2019-11-13 19:00:50 +01:00
Daniel García Aubert
ccd01e6da5 Stubs next version 2019-11-13 13:17:47 +01:00
Daniel García Aubert
df10cfe641 Release 8.0.0 2019-11-13 13:07:00 +01:00
Daniel G. Aubert
09d3e8aabb Merge pull request #1139 from CartoDB/fix/bad-aggregation-method-overview
Validate aggregation method is either sum or count
2019-11-13 10:11:28 +01:00
Esther Lozano
f17411916f Remove unnecessary config in tests 2019-11-12 17:43:03 +01:00
Esther Lozano
75583f67c5 Use last version for map config in tests 2019-11-12 14:54:25 +01:00
Esther Lozano
bb745b0318 Update test/acceptance/dataviews/overviews-test.js
Co-Authored-By: Daniel G. Aubert <danielgarciaaubert@gmail.com>
2019-11-12 14:48:37 +01:00
Raúl Marín
3834aeb73f Merge pull request #1140 from Algunenano/local_password
Fix multiple DB login issues
2019-11-12 13:23:34 +01:00
Raúl Marín
aa09c079f6 Fix multiple DB login issues 2019-11-12 13:15:32 +01:00
Raúl Marín
3c586caba4 Qualify calls to cartodb extension so having it in the search_path isn't necessary 2019-11-12 13:15:32 +01:00
Esther Lozano
b05740048c Update NEWS.md 2019-11-12 12:48:22 +01:00
Esther Lozano
2b5ed21207 Remove only in tests :P 2019-11-12 12:37:24 +01:00
Esther Lozano
acecb88efb Validate aggregation method is either sum or count 2019-11-11 18:14:30 +01:00
Daniel García Aubert
734c373f3d Camel case 2019-11-11 12:26:04 +01:00
Daniel García Aubert
e49cb524a8 Update makefile 2019-11-06 14:02:37 +01:00
Daniel García Aubert
cc24228511 Fix eslint issues 2019-11-06 13:56:59 +01:00
Daniel García Aubert
27106fea57 Avoid regular string contains what looks like a template literal placeholder 2019-11-06 13:29:03 +01:00
Simon Martín
990aaadc16 Merge pull request #1134 from CartoDB/better-redis-logging
Adding a logger to MapStore
2019-10-29 12:10:00 +01:00
Simon Martín
0c572b5947 NEWS 2019-10-29 11:37:16 +01:00
Simon Martín
3e7c294989 linter 2019-10-29 11:32:27 +01:00
Simon Martín
8a02156ac0 fix tests 2019-10-29 10:31:31 +01:00
Simon Martín
c4a75de0d8 windshaft 5.6.4 2019-10-28 18:15:15 +01:00
Simon Martín
db03bcdf8f windshaft logger by config param 2019-10-28 15:41:46 +01:00
Simon Martín
dd5825c770 using a new logger 2019-10-28 11:55:43 +01:00
Daniel García Aubert
8fbe8f9f2a Remove unused vars 2019-10-25 10:58:00 +02:00
Daniel García Aubert
3bc3d19f40 Stop using legacy URL api 2019-10-25 09:38:05 +02:00
Daniel García Aubert
575fe8e350 Remove unnecessary escape usage 2019-10-24 19:18:47 +02:00
Daniel García Aubert
d5218a86f6 Enforce callback error handling 2019-10-24 18:38:37 +02:00
Simon Martín
080f93f6de passing logger to MapStore 2019-10-24 17:34:46 +02:00
Simon Martín
df931d95a3 using github:cartodb/windshaft#better-redis-logging 2019-10-24 17:34:30 +02:00
Daniel García Aubert
d5406d5b50 remove jshint comment 2019-10-22 19:22:38 +02:00
Daniel García Aubert
f7e877ce60 Use template string istead of ES5 string line break 2019-10-22 19:11:32 +02:00
Daniel García Aubert
ad4a1ada45 Do not use string concatenation when using __dirname and __filename 2019-10-22 18:22:33 +02:00
Daniel García Aubert
da0d0d21e3 Remove jshintrc 2019-10-21 23:33:52 +02:00
Daniel García Aubert
7a1d2ca205 Avoid calling Object.prototype methods directly on object instances 2019-10-21 23:33:27 +02:00
Daniel García Aubert
d89e785440 Stop using deprecated Buffer constructor 2019-10-21 20:05:51 +02:00
Daniel García Aubert
2423b5a4c4 Replace assert.deepEqual() by assert.deepStrictEqual() 2019-10-21 19:52:51 +02:00
Daniel García Aubert
1bee877b24 Replace assert.equal() by assert.strictEqual() 2019-10-21 19:41:03 +02:00
Daniel García Aubert
4d70ac0894 Apply automatic eslint fixes 2019-10-21 19:07:24 +02:00
Daniel García Aubert
593d9e40f6 Remove jshint, add eslint and config 2019-10-21 18:50:01 +02:00
Daniel G. Aubert
9fd1a3c663 Merge pull request #1131 from CartoDB/coherent-cache-invalidation
Coherent cache invalidation
2019-10-21 16:41:28 +02:00
Daniel García Aubert
8a781d241c Typo 2019-10-21 16:22:26 +02:00
Daniel García Aubert
be4d610de1 Use released version of cartodb-query-tables 0.7.0 2019-10-21 16:17:54 +02:00
Daniel García Aubert
736d3460d9 Update development branch 2019-10-21 13:46:29 +02:00
Daniel García Aubert
f844d70275 Replace http --> https. I swear, I'm not a spy 2019-10-21 11:32:56 +02:00
Daniel García Aubert
0c9cfefcd0 Please jshint, can you be a regular linter? 2019-10-21 11:13:54 +02:00
Daniel García Aubert
8ed187b0f5 Do not set Last-Modifed to January 1st 1970 when cache buster ins layergroup token is 0. In this case, 0 means we don't know when the resource was updated for the last time. 2019-10-21 11:01:05 +02:00
owayss
e5bada81dc Merge pull request #1132 from CartoDB/default_to_stdout_logging
Default to stdout logging on dev environment
2019-10-17 14:13:28 +02:00
Owayss Kabtoul
655f817033 Default to stdout logging on dev environment 2019-10-17 13:04:10 +02:00
Daniel García Aubert
ebff2ac9f2 Please JSHint 2019-10-15 13:27:40 +02:00
Daniel García Aubert
5a7ffcf499 Be able to synchronize the TTL of cache-control header to expire in a coherent way 2019-10-15 12:48:50 +02:00
Daniel García Aubert
f8e117a7b7 JSHint is not ready for modern javascript 2019-10-15 11:46:44 +02:00
Daniel García Aubert
c4054f0ac9 Use develop branch of query-tables 2019-10-15 10:39:31 +02:00
Daniel García Aubert
f7707141d6 Rename variable 2019-10-08 17:22:24 +02:00
Daniel G. Aubert
c40c42fc10 Merge pull request #1130 from CartoDB/remove-routes-adapter
Remove environment configuration adapter
2019-10-08 16:34:24 +02:00
Daniel García Aubert
6cad976078 Remove environment configuration adapter 2019-10-08 11:02:32 +02:00
Daniel G. Aubert
c82f17e5d2 Merge pull request #1126 from CartoDB/gears
Be able to inject middlewares from configuration
2019-10-07 17:35:01 +02:00
Daniel G. Aubert
1054bde7fd Merge pull request #1127 from CartoDB/folders
Standardize folder structure and filenames
2019-10-07 17:34:27 +02:00
Daniel G. Aubert
9e23b91f3f Merge pull request #1129 from CartoDB/fix-routes-config
Convert from v1 to api in routes config
2019-10-07 17:34:00 +02:00
Daniel García Aubert
ea6e064e42 Convert from v1 to api in routes config 2019-10-07 14:08:59 +02:00
Raúl Marín
cf0858f5b9 Merge pull request #1128 from Algunenano/sec
Tests: Remove unnecessary extra qualification
2019-10-07 14:03:06 +02:00
Raul Marin
69b11a8412 sec 2019-10-07 13:36:18 +02:00
Daniel García Aubert
55aad4254c Remove cartodb folder in unit test 2019-10-07 11:29:07 +02:00
Daniel García Aubert
73e1659378 Add suffix '-test' to every test-suite file 2019-10-07 11:16:48 +02:00
Daniel García Aubert
98f3e8159e Stop using __dirname in requires 2019-10-07 10:55:26 +02:00
Daniel García Aubert
e8cff194fc Rename template_maps -> template-maps 2019-10-07 10:50:14 +02:00
Daniel García Aubert
f1de1b3b91 Rename test files: stop using underscores, use hyphens instead 2019-10-07 10:44:45 +02:00
Daniel García Aubert
a134ab3012 Rename server_options -> server-options 2019-10-07 10:10:51 +02:00
Daniel García Aubert
5a84d7233b Rename table_name_parser -> table-name-parser 2019-10-07 10:07:25 +02:00
Daniel García Aubert
8fe0112568 Rename overviews_query_rewriter -> overviews-query-rewriter 2019-10-07 10:06:01 +02:00
Daniel García Aubert
3acaac5403 Rename icu_data_env_setter -> icu-data-env-setter 2019-10-07 10:04:39 +02:00
Daniel García Aubert
7dbac5a565 Rename profiler_proxy -> profiler-proxy 2019-10-07 10:01:18 +02:00
Daniel García Aubert
8fb4f4063f Rename health_check -> health-check 2019-10-07 09:59:54 +02:00
Daniel García Aubert
808718fb26 Rename cdb_request -> cdb-request 2019-10-07 09:58:21 +02:00
Daniel García Aubert
6dc8de315a Rename surrogate_keys_cache -> surrogate-keys-cache 2019-10-07 09:56:20 +02:00
Daniel García Aubert
afb9b08925 Rename surrogate_keys_cache -> surrogate-keys-cache 2019-10-07 09:55:55 +02:00
Daniel García Aubert
2bed034e64 Rename named_map_provider_cache -> named-map-provider-cache 2019-10-07 09:54:54 +02:00
Daniel García Aubert
2328bb6261 Rename layergroup_affected_tables -> layergroup-affected-tables 2019-10-07 09:53:06 +02:00
Daniel García Aubert
06357fa3f9 Rename named_maps_entry -> named-maps-entry 2019-10-07 09:51:51 +02:00
Daniel García Aubert
83f58288f9 Rename varnish_http -> varnish-http 2019-10-07 09:47:42 +02:00
Daniel García Aubert
b1d5f0f9e8 Rename pg_query_runner -> pg-query-runner 2019-10-07 09:45:46 +02:00
Daniel García Aubert
7142e4db37 Rename pg_connection -> pg-connection 2019-10-07 09:43:40 +02:00
Daniel García Aubert
281a079a62 Simplify folder structure 2019-10-07 09:40:50 +02:00
Daniel García Aubert
0d638e6bad Fix command example 2019-10-06 19:12:11 +02:00
Daniel García Aubert
43a63feaca Keep the backwards compatibility for routing configuration 2019-10-06 18:58:21 +02:00
Daniel García Aubert
4aa6ffe28c Add tests to check custom middlewares behavior 2019-10-04 17:54:32 +02:00
Daniel García Aubert
2ce688ee2a Missed to apply configuration changes to default server options 2019-10-04 12:56:36 +02:00
Daniel García Aubert
4e967980a3 Prepare next release ans update news 2019-10-04 12:41:27 +02:00
Daniel García Aubert
93edf07da8 Temporary workaround to not depend on configuration changes 2019-10-04 12:35:12 +02:00
Daniel García Aubert
a684bead92 Rename method 'register' -> 'route' 2019-10-04 12:22:23 +02:00
Daniel García Aubert
dd06de2632 Use new routes configuration 2019-10-04 12:07:58 +02:00
Daniel García Aubert
975f07df99 Use Object.values() 2019-10-02 10:42:29 +02:00
Daniel García Aubert
5fe6845d7c Add comments 2019-10-01 19:48:16 +02:00
Daniel García Aubert
4aa844946d Update environment example files 2019-10-01 19:42:04 +02:00
Daniel García Aubert
3220e3de31 Remove old api configuration paths 2019-10-01 19:34:03 +02:00
Daniel García Aubert
26bba3c5f5 Prepare next release version 2019-10-01 19:31:14 +02:00
Daniel García Aubert
c82a5c38df Fix indentation 2019-10-01 18:20:17 +02:00
Daniel García Aubert
9cfaf6eefc Draft: be able to inject middlewares from configuration 2019-09-30 19:18:36 +02:00
Daniel García Aubert
b881bec668 Release 7.2.0 2019-09-30 17:24:36 +02:00
Raúl Marín
e6f3c63675 Merge pull request #1125 from CartoDB/update_querytables
Update cartodb-query-tables to 0.6.2
2019-09-19 15:08:00 +02:00
Raul Marin
f2afece658 Update cartodb-query-tables to 0.6.3 2019-09-19 13:40:31 +02:00
Raul Marin
b69ceeeee8 Update cartodb-query-tables to 0.6.2 2019-09-18 18:21:38 +02:00
Raúl Marín
b93caa7410 Merge pull request #1123 from Algunenano/query_tables_update
Query tables update
2019-09-18 11:26:51 +02:00
Raul Marin
7476879bde Update cartodb-query-tables to 0.6.1 2019-09-17 13:20:08 +02:00
Daniel G. Aubert
ce884732f3 Merge pull request #1122 from CartoDB/fix-missing-template
Improve named map provider cache
2019-09-16 13:37:52 +02:00
Daniel García Aubert
455932b032 Update NEWS 2019-09-16 13:26:50 +02:00
Daniel García Aubert
68a9b4ccae Typo 2019-09-16 13:09:23 +02:00
Daniel García Aubert
f6c205baf9 Typo 2019-09-16 13:08:10 +02:00
Daniel García Aubert
738d10409f Style 2019-09-16 11:35:40 +02:00
Daniel García Aubert
89c5a3e0a9 Merge branch 'master' into fix-missing-template 2019-09-16 11:30:01 +02:00
Daniel García Aubert
5dac9d956c Add test 2019-09-16 11:18:50 +02:00
Daniel García Aubert
c19c723795 Add named map providers reporter to gather some stats 2019-09-13 20:01:03 +02:00
Daniel García Aubert
788cd9d6fb Be explicit while forwarding parametes 2019-09-13 18:15:11 +02:00
Daniel García Aubert
824d41ef0f Consitent quotes 2019-09-13 18:11:47 +02:00
Daniel García Aubert
329b5d9b9e Use ES6 class syntax 2019-09-13 18:07:50 +02:00
Daniel García Aubert
b55c2ec55c Use template string 2019-09-13 18:03:39 +02:00
Daniel García Aubert
d99e5a44f5 Use const instead of var 2019-09-13 18:00:13 +02:00
Daniel García Aubert
e8d5e42300 Use Object.assign() instead of _.defaults() 2019-09-13 17:58:23 +02:00
Daniel García Aubert
c0afd42fa2 Use template strings instead of dot module 2019-09-13 17:53:10 +02:00
Daniel García Aubert
1bb6a2ac0d Move invalidation closer to its definition 2019-09-13 17:42:56 +02:00
Daniel García Aubert
3d2f554be9 Use early return pattern 2019-09-13 16:53:53 +02:00
Raul Marin
a673e6d138 Update dependencies 2019-09-13 16:32:37 +02:00
Raul Marin
f9b6e92745 Stop throwing up warnings during testing
Yeah, I test with different repos and branches. Big deal. Shut up.
2019-09-13 16:26:46 +02:00
Daniel García Aubert
6229455d25 Remove mechanism to reset named map's provider as, in the end, it's reading from storage (redis) always so cache isn't doing its job. There is already a mechanism to invalidate cache entry when a template is modified (see template-maps emits on "update" and "delete", and listeners attached at server startup) 2019-09-12 20:34:18 +02:00
Daniel García Aubert
9d6726227a Add maxAge param to lru-cache to be able to refresh entries when staled 2019-09-12 18:02:13 +02:00
Daniel García Aubert
64b4efef17 Do not cache map template CRUD errors in Named Map provider 2019-09-12 17:23:19 +02:00
Pablo Alonso
eb71601cd6 Merge pull request #1121 from CartoDB/professional-plan-renaming
Professional -> Individual
2019-09-10 16:31:01 +02:00
Daniel G. Aubert
0297e09c17 Merge pull request #1120 from CartoDB/improve-metadata-sample
Improve efficiency of query samples (esp. for FDW's)
2019-09-04 12:37:41 +02:00
Daniel García Aubert
8f6447b67e Update NEWS 2019-09-04 12:30:12 +02:00
Pablo Alonso Garcia
4ae4ce477f Professional -> Individual 2019-09-02 17:32:06 +02:00
Daniel García Aubert
7bf5deb4c1 Styling 2019-09-02 16:40:43 +02:00
Daniel García Aubert
2fbd9893bd Going green: do not fail when source is empty 2019-09-02 14:09:13 +02:00
Daniel García Aubert
5a01c1c5eb Going red: test to avoid error when the source table is empty 2019-09-02 13:38:28 +02:00
Daniel García Aubert
9a85b661b0 Remove safe limit 2019-08-23 18:04:19 +02:00
Daniel García Aubert
d4d981909b Remove unused code 2019-08-23 17:43:20 +02:00
Daniel García Aubert
16035131bc Rename method 2019-08-23 17:25:37 +02:00
Daniel García Aubert
b2adb8f058 Use substituteDummyTokens 2019-08-23 17:21:01 +02:00
Daniel García Aubert
780cb80c8c Move method 2019-08-23 17:16:35 +02:00
Daniel García Aubert
2a8a8f6e6a Export methods to get max, min, values od a column and sample based on a range 2019-08-23 17:10:46 +02:00
Daniel García Aubert
850bda9669 Use modified sample method 2019-08-23 17:09:53 +02:00
Daniel García Aubert
25e3395580 Modify sample metadata 2019-08-23 17:09:24 +02:00
Román Jiménez
71dba04d83 Merge pull request #1119 from CartoDB/fix-curl-docs-windows
Fix cURL docs for Windows users by using files
2019-08-23 11:08:15 +02:00
Román Jiménez
2a3312e779 Fix cURL docs for Windows users by using files 2019-08-22 18:12:27 +02:00
Daniel G. Aubert
c4484dcc54 Merge pull request #1115 from CartoDB/upgrade-windshaft-5.6.0
Update windshaft to version 5.6.0
2019-07-30 12:24:08 +02:00
Daniel García Aubert
61883b13ef Update windshaft to version 5.6.0 2019-07-30 12:00:28 +02:00
Daniel García Aubert
a220af4fad Please jshint 2019-07-29 19:20:38 +02:00
Daniel García Aubert
945c122dda Update windshaft devel branch 2019-07-29 19:14:27 +02:00
Daniel García Aubert
61b66e88d5 Update NEWS 2019-07-23 18:08:48 +02:00
Daniel G. Aubert
9dfd5f3012 Merge pull request #1114 from CartoDB/upgrade-windshaft-5.5.1
Upgrade windshaft to version 5.5.1
2019-07-23 15:22:28 +02:00
Daniel García Aubert
ff634e32db Upgrade windshaft to version 5.5.1 2019-07-23 13:47:56 +02:00
Daniel García Aubert
3b583ebd56 Update windshaft#master 2019-07-23 12:15:49 +02:00
Daniel García Aubert
1a9b410540 Update to master branch 2019-07-23 11:14:40 +02:00
Daniel García Aubert
d7a439477c Use https 2019-07-23 11:03:55 +02:00
Daniel García Aubert
c7f3da237c Upgrade windshaft to devel branch with fix 2019-07-23 09:13:09 +02:00
Daniel García Aubert
da144de57b Update windshaft devel branch 2019-07-22 17:14:31 +02:00
Raúl Marín
58d38682eb Merge pull request #1113 from Algunenano/audit_dependencies
package-lock.json: Update js-yaml and lodash
2019-07-16 16:29:44 +02:00
Raul Marin
402579f7e2 package-lock.json: Update js-yaml and lodash 2019-07-16 16:11:03 +02:00
Raúl Marín
ebf373e680 Merge pull request #1111 from Algunenano/cartodbless
Render MVTs and aggregations without cartodb-postgresql
2019-07-16 16:06:40 +02:00
Raul Marin
ffe347cfdc package.json: Revert changes to match master 2019-07-16 13:44:13 +02:00
Raul Marin
b572b979a1 Style 2019-07-16 13:43:46 +02:00
Raul Marin
de49aa0bd4 Aggregation: Style improvements 2019-07-16 12:49:09 +02:00
Raul Marin
286daa9bec Simplify aggregation templates 2019-07-16 12:49:09 +02:00
Raul Marin
65beb6e460 Tweak the tests to accept Mapnik precision as valid 2019-07-16 12:49:09 +02:00
Raul Marin
63b6af2ac7 Query utils: Use webmercator utils, reuse code and always substitute tokens 2019-07-16 12:49:09 +02:00
Raul Marin
bdbe132311 Update lodash 2019-07-16 12:49:09 +02:00
Raul Marin
492bcbfdaa Aggregation tests: Enable mapnik and rework the complete cells test
Before it used to test exact tiles, now it test that if a cell/value is included
it will contain exactly the features that we expect
2019-07-16 12:48:58 +02:00
Raul Marin
46600bf4fc Please jshint 2019-07-16 12:48:58 +02:00
Raul Marin
aed456bf32 Cluster: Use new webmercator utilities 2019-07-16 12:48:58 +02:00
Raul Marin
d3e807583a Use proper mode 2019-07-16 12:48:58 +02:00
Raul Marin
262f957218 Aggregation: Improve speeeeeeed 2019-07-16 12:48:58 +02:00
Raul Marin
8454eef6e9 Aggregation: Extract the query to get the grid data 2019-07-16 12:48:58 +02:00
Raul Marin
892479d9b9 API changes 2019-07-16 12:48:58 +02:00
Raul Marin
5e24f650af Rework how aggregations are calculated
Pending fixing Mapnik tiles (pg-mvt work ok)
2019-07-16 12:48:12 +02:00
Raul Marin
de38f1f6fd Tests: Avoid using st_buffer(geography)
It's really slow with PROJ6 since it involves creating different projections for almost each point,
and that's particularly slow in the new release
2019-07-16 12:48:12 +02:00
Raul Marin
a3e8f45552 WIP to change how aggregations are calculated 2019-07-16 12:48:12 +02:00
Raul Marin
cd8624ae2d Improve subquery naming 2019-07-16 12:48:12 +02:00
Raul Marin
7b731a24d1 Overviews Adapter: Do not check overviews if using MVT pure style or aggregations 2019-07-16 12:48:12 +02:00
Daniel G. Aubert
f9bd3d39f0 Merge pull request #1112 from CartoDB/upgrade-windshaft-5.5.0
Upgrade windshaft to version 5.5.0
2019-07-16 12:38:52 +02:00
Daniel García Aubert
dbc926b0b8 Upgrade windshaft to version 5.5.0 2019-07-16 12:27:49 +02:00
Daniel G. Aubert
0f1a3dfb34 Merge pull request #1055 from CartoDB/1054-test
Test for time dimension hour format
2019-07-11 16:04:06 +02:00
Daniel G. Aubert
cbb9285fb3 Merge pull request #863 from CartoDB/cartofante
removing raster image on timeout error
2019-07-11 15:59:38 +02:00
Daniel García Aubert
d7412aab45 Update NEWS 2019-07-09 16:53:19 +02:00
Daniel G. Aubert
d8ca29509f Merge pull request #1109 from CartoDB/update-query-tables
[WIP] Update cartodb-query-tables
2019-07-09 16:50:16 +02:00
Daniel García Aubert
619cad9c35 Typo 2019-07-09 15:44:34 +02:00
Daniel García Aubert
bca723bcf3 PLease jshint 2019-07-09 14:59:16 +02:00
Daniel García Aubert
ef39f23d1f Fix tests to use map-config builder properly 2019-07-09 14:47:40 +02:00
Daniel García Aubert
c066e2c3cf Update cartodb-query-tables to version 0.5.0 2019-07-09 12:45:33 +02:00
Daniel García Aubert
45af291e6a Remove test filter 2019-07-08 18:08:08 +02:00
Daniel García Aubert
a84184852e Going green: skip analyses table while computing max-age directive 2019-07-08 18:05:30 +02:00
Daniel García Aubert
6cdb872bb5 Going red: add test to check the bad behavour of max-age directive when an analysis table is in affected tables of the map 2019-07-08 17:55:04 +02:00
Daniel G. Aubert
27b76aefd2 Merge pull request #1107 from CartoDB/max-age-directive
Set directive 'max-age' to a fallback value
2019-07-05 15:38:33 +02:00
Daniel García Aubert
8820e34870 Update news 2019-07-05 15:31:17 +02:00
Daniel García Aubert
4def4b0341 Improve condition 2019-07-04 16:41:30 +02:00
Daniel García Aubert
ec0c0eb810 Improve readability 2019-07-04 16:24:17 +02:00
Daniel García Aubert
5ca498d0f3 Typo 2019-07-04 16:18:43 +02:00
Daniel García Aubert
a894194b6b Add more specific tests 2019-07-04 15:58:39 +02:00
Daniel García Aubert
2e8a5d0d86 Add test 2019-07-04 15:34:37 +02:00
Daniel García Aubert
a374deaf30 Please linter 2019-07-03 17:16:09 +02:00
Daniel García Aubert
bc29587c55 Set directive 'max-age' to 5 min when there are affacted tables where we can't know when were updated for the last time, e.g: non cartodified tables or foreing tables without cartodb support 2019-07-03 17:15:14 +02:00
Simon Martín
9cb149fa32 Merge pull request #1103 from CartoDB/fix-torque-cartocss
Upgrade windshaft to version 5.4.0
2019-06-25 11:17:14 +02:00
Simon Martín
412c4af7b0 Upgrade windshaft to version 5.4.0 2019-06-25 11:01:02 +02:00
Simon Martín
28ff0bdfc4 updating lock2 2019-06-24 17:33:32 +02:00
Simon Martín
f9250cda1a updating lock 2019-06-24 17:24:37 +02:00
Raúl Marín
b821b9b038 Merge pull request #1105 from Algunenano/carto-package-mapnik16
carto-package.json: Update mapnik dependency to match what's installed
2019-06-24 17:13:22 +02:00
Raul Marin
6f35f9cbbb carto-package.json: Update mapnik dependency to match what's installed 2019-06-24 17:05:06 +02:00
Simon Martín
23d9cd81c8 test torque fix 2019-06-21 15:26:22 +02:00
Daniel G. Aubert
dc63edf0c7 Merge pull request #1099 from CartoDB/abaculus-cartofy
Upgrade windshaft: integrate abaculus into cartonik
2019-06-17 15:52:46 +02:00
Daniel García Aubert
ab6ee52b43 Merge branch 'master' into abaculus-cartofy 2019-06-17 15:42:12 +02:00
Daniel García Aubert
3121d907c1 Update NEWS 2019-06-17 15:40:31 +02:00
Daniel García Aubert
b09a35f272 Upgrade windshaft to version 5.3.0 2019-06-17 15:37:51 +02:00
Daniel García Aubert
f21eda2b40 Adapt endpoints to the new preview interface 2019-06-17 10:43:30 +02:00
Daniel García Aubert
ef4370c213 Update devel branch 2019-06-12 18:33:00 +02:00
Rafa de la Torre
9f03e978dd Merge pull request #1102 from CartoDB/update-turbo-carto-0.21.2
Update turbo-carto to 0.21.2
2019-06-12 10:54:04 +02:00
Rafa de la Torre
54190a9cd2 Update turbo-carto to 0.21.2 2019-06-12 10:37:53 +02:00
Daniel García Aubert
f43ccd4c4e Update devel branch 2019-06-10 19:04:22 +02:00
Daniel García Aubert
20fe04de38 Do not get headers from abaculus 2019-06-10 13:16:05 +02:00
Daniel García Aubert
dc16f4cebf Update windshaft devel branch 2019-06-10 12:49:56 +02:00
Raúl Marín
bfaf764f13 Merge pull request #1098 from Algunenano/extension_changes
Adapt to extension changes
2019-06-03 17:53:53 +02:00
Raul Marin
62d9fb1365 Docker: Install cartodb extension for testing purposes 2019-06-03 16:44:59 +02:00
Raul Marin
9f2b5330d5 Install the cartodb extension directly 2019-06-03 16:23:41 +02:00
Raul Marin
9a552a7cc4 Adapt to fully qualification in the extension 2019-06-03 13:41:22 +02:00
Raúl Marín
08998a3d17 Merge pull request #1096 from CartoDB/mapnik15
Mapnik 3.0.15
2019-05-21 12:34:12 +02:00
Raul Marin
b41f43f4bf Mapnik 3.0.15 2019-05-21 12:06:07 +02:00
Daniel García Aubert
90740146ff Release 7.1.0 2019-05-06 10:40:02 +02:00
Daniel G. Aubert
778eb54890 Merge pull request #1088 from CartoDB/cartonik
Upgrade windshaft to version 5.2.0
2019-04-29 13:24:01 +02:00
Daniel García Aubert
5196d2fed0 Update NEWS 2019-04-29 13:03:15 +02:00
Daniel García Aubert
5e969aa41a Upgrade windshaft to version 5.2.0 2019-04-29 12:46:06 +02:00
Daniel García Aubert
1ac03eeb91 Update windshaft devel branch 2019-04-25 17:55:57 +02:00
Daniel García Aubert
261e6e3307 Merge branch 'master' into cartonik 2019-04-25 15:41:28 +02:00
Raúl Marín
e38f8b6133 Merge pull request #1095 from Algunenano/blessed_fix_deps
Update package-lock.json to point to CARTOs forks
2019-04-25 15:38:48 +02:00
Raul Marin
bc4204243c Update package-lock.json to point to CARTOs forks 2019-04-25 14:50:11 +02:00
Daniel García Aubert
f93ea0e95c Merge branch 'master' into cartonik 2019-04-25 14:14:16 +02:00
Raúl Marín
b90e15207f Merge pull request #1094 from CartoDB/mapnik13
Update node-mapnik to 3.6.2-carto.13
2019-04-15 13:43:27 +02:00
Raúl Marín
19da16229c Merge pull request #1092 from CartoDB/fix-typo-patch-1
fix typo
2019-04-15 13:27:37 +02:00
Raul Marin
73c29660d6 Update @carto/mapnik to 3.6.2-carto.13 2019-04-15 13:23:06 +02:00
Raul Marin
6549a3a023 Agg tests: Sort before compare to avoid failures due to paralellism 2019-04-10 19:32:32 +02:00
Mario de Frutos Dieguez
4d30fb57d8 Remove unused travisci variable 2019-04-10 16:43:24 +02:00
Mario de Frutos Dieguez
022e6a2f89 Tests using PG11 and Postgis 2.5 (#1093) 2019-04-10 13:06:39 +02:00
Daniel García Aubert
da613df71c Update windshaft devel branch 2019-04-02 17:01:04 +02:00
Daniel García Aubert
66882b3c25 Merge branch 'master' into cartonik 2019-04-02 16:45:27 +02:00
Daniel G. Aubert
9285764e31 Merge pull request #1091 from CartoDB/upgrade-windshaft-next
Upgrade windshaft to version 5.1.0
2019-04-02 12:52:34 +02:00
Daniel García Aubert
835f55e563 Update NEWS 2019-04-02 12:42:13 +02:00
Daniel García Aubert
ef1bd7502f Use released version of Windhshaft 2019-04-02 12:34:31 +02:00
Daniel García Aubert
320d3c196d Update windshaft devel branch 2019-04-01 11:34:06 +02:00
Iñigo Medina (aka MacGyver)
9d88a4659f fix typo
The output in the Developer Center is not being properly rendered just because of a typo in the headline format.
2019-03-29 19:03:25 +01:00
Daniel García Aubert
dae33bab5e Update fixtures 2019-03-29 18:56:18 +01:00
Daniel García Aubert
c0a305babf Update windshaft devel branch 2019-03-29 18:20:48 +01:00
Daniel G. Aubert
4ffbcea819 Merge pull request #1090 from CartoDB/graceful-shutdown
Use semver for cheking dependencies version
2019-03-29 15:40:12 +01:00
Daniel García Aubert
6808ab496a Use semver for cheking dependencies version 2019-03-29 15:30:29 +01:00
Daniel G. Aubert
40d43331e4 Merge pull request #1089 from CartoDB/graceful-shutdown
Implement graceful shutdown
2019-03-29 15:23:27 +01:00
Daniel García Aubert
674c276b57 Upgrade windshaft to version 5 2019-03-29 13:36:16 +01:00
Daniel García Aubert
5fdc501836 Upgrade camshaft to version 0.64.0 2019-03-29 13:26:38 +01:00
Daniel García Aubert
c3b1b469b2 Update NEWS 2019-03-29 13:21:35 +01:00
Daniel García Aubert
c30a81acbe Update NEWS 2019-03-29 13:18:40 +01:00
Daniel García Aubert
035acb343e Fix uncaught exception: TypeError: Cannot read property 'id' of undefined 2019-03-27 11:45:47 +01:00
Daniel García Aubert
53b19d576c Update dynamic Node.js version check 2019-03-26 19:14:39 +01:00
Daniel García Aubert
7efb8abd97 Use code while exiting 2019-03-26 19:06:10 +01:00
Daniel García Aubert
b314acf5b5 PLase jshint 2019-03-26 18:57:49 +01:00
Daniel García Aubert
b222dd15c0 Handle ENOMEM signal emitted by grainstore 2019-03-26 18:54:42 +01:00
Daniel García Aubert
01212920ae Draft 2019-03-26 18:47:57 +01:00
Daniel García Aubert
4b2d736f0e Update windshaft devel branch 2019-03-26 17:33:45 +01:00
Daniel García Aubert
57d5b17a84 Update windshaft devel branch 2019-03-26 09:54:19 +01:00
Daniel García Aubert
791d0f5787 Update windshaft devel branch 2019-03-25 19:27:52 +01:00
Daniel García Aubert
fa9d604fa3 Fix broken test 2019-03-22 19:09:58 +01:00
Daniel García Aubert
bdb55e4b21 Update windshaft devel branch 2019-03-22 18:17:06 +01:00
Daniel García Aubert
e2d707b158 Update windshaft devel branch 2019-03-22 17:36:01 +01:00
Daniel García Aubert
7963681585 Update windshaft devel branch 2019-03-21 17:18:48 +01:00
Daniel García Aubert
b0158d9388 Update cartonik 2019-03-21 12:08:34 +01:00
Daniel G. Aubert
0e86c4aae8 Merge pull request #1087 from CartoDB/upgrade-windshaft-4.13.5
Upgrade windshaft to version 4.13.5
2019-03-20 10:25:08 +01:00
Daniel García Aubert
2f838f0b6a Update NEWS 2019-03-19 18:39:56 +01:00
Daniel García Aubert
362d48ab81 Upgrade windshaft to version 4.13.5 2019-03-19 18:34:11 +01:00
Daniel G. Aubert
8d7b3beb09 Merge pull request #1086 from CartoDB/cartofy
Upgrade windshaft to version 4.13.4
2019-03-19 15:55:56 +01:00
Daniel García Aubert
6d360a8b03 Update NEWs 2019-03-19 15:45:20 +01:00
Daniel García Aubert
bf53a905a2 Upgrade windshaft to version 4.13.4 2019-03-19 13:41:07 +01:00
Daniel García Aubert
290cdd2692 Update windshaft 2019-03-18 12:46:27 +01:00
Daniel García Aubert
00570a21f5 Use devel branch of windshaft 2019-03-17 18:01:41 +01:00
Raúl Marín
9a98422076 Merge pull request #1082 from CartoDB/1073-aggregation-false
Fix boolean aggregation layer option
2019-03-14 17:14:26 +01:00
Raul Marin
ab9cfee7b1 Update NEWS 2019-03-14 17:02:23 +01:00
Raul Marin
ae59c4f996 Merge remote-tracking branch 'blessed/master' into 1073-aggregation-false 2019-03-14 17:01:07 +01:00
Raúl Marín
514fc908b9 Merge pull request #1080 from Algunenano/histogram_irq_improvement
Numeric histogram performance improvement
2019-03-14 15:23:44 +01:00
Raul Marin
59107496b4 Update NEWS 2019-03-14 15:17:42 +01:00
Raul Marin
8db090ae9c Numeric histogram: Test when start and end are provided but not bins 2019-03-14 15:16:23 +01:00
Raul Marin
730076469e Numeric histogram: Simplify bin calculation 2019-03-14 15:16:23 +01:00
Raul Marin
6241b23d4f Histogram: Speed up IRQ calculation 2019-03-14 15:16:23 +01:00
Daniel G. Aubert
e65872d5df Merge pull request #1085 from CartoDB/upgrade-windshaft-4.13.3
Upgrade windshaft to version 4.13.3
2019-03-14 12:00:01 +01:00
Daniel García Aubert
eaa38b7676 Upgrade windshaft to version 4.13.3 2019-03-13 18:52:17 +01:00
Daniel G. Aubert
f76fe9efc1 Merge pull request #1084 from CartoDB/fix-cluster-test-for-pg11
Place points out of boundaries to avoid conficlts with PG11
2019-03-12 15:27:54 +01:00
Daniel García Aubert
6b2ad8826b Move points to avoid x/y axes 2019-03-12 15:18:31 +01:00
Daniel García Aubert
8324d4c4c2 Place points out of boundaries to avoid conficlts with PG11 2019-03-12 10:35:16 +01:00
Daniel G. Aubert
2821d797a9 Merge pull request #1083 from CartoDB/cluster-refactor
Cluster refactor
2019-03-11 19:10:40 +01:00
Daniel García Aubert
367ca399c8 Improve readability 2019-03-11 18:53:47 +01:00
Daniel García Aubert
d86b01ba33 Add debug statement 2019-03-11 18:09:41 +01:00
Daniel García Aubert
0aa3b288a0 Improve validation 2019-03-11 18:04:47 +01:00
Daniel García Aubert
589996b79c Reduce complexity 2019-03-11 17:53:34 +01:00
Daniel García Aubert
49104a6add Reduce complexity by extracting function to validate expressions 2019-03-11 17:25:29 +01:00
Daniel García Aubert
8051dc5110 Reduce complexity by extracting validation condition to its function 2019-03-11 17:14:07 +01:00
Daniel García Aubert
fecedfdc68 Reduce complexity by extracting validation to a function 2019-03-11 17:06:04 +01:00
Daniel García Aubert
f0c82f21d2 Reduce complexity by extracting a complex condition to a function 2019-03-11 17:01:13 +01:00
cillas
9f71fcf255 Merge pull request #879 from CartoDB/developer-center
New docs folder for developer center
2019-03-07 18:35:20 +01:00
Javier Goizueta
c91b28ee92 Lint fixes 2019-03-06 18:32:17 +01:00
Javier Goizueta
113b3728b1 Fix support of boolean aggregation layer option 2019-03-06 18:14:15 +01:00
Javier Goizueta
cf193a71b2 Add tests for enabling default aggregation 2019-03-06 15:28:38 +01:00
csubira
2d37d4bf9d Fix typo in named maps guide 2019-03-06 15:26:14 +01:00
csubira
a768575fea Add multilayer api file as internal doc 2019-03-06 14:55:41 +01:00
csubira
51ff565c5f Update links to deprecated docs site 2019-03-06 14:54:48 +01:00
Javier Goizueta
c5f5f43ccb Fix test for disabling aggregations
See #1073
2019-03-05 22:17:04 +01:00
csubira
df7b5db47b Remove extra line 2019-03-04 12:26:04 +01:00
csubira
98121e0e64 Add liquid raw to fix block code 2019-03-04 12:20:51 +01:00
csubira
e74f734546 Revert addition due to conflict when deploying devcenter 2019-03-01 18:24:11 +01:00
Daniel G. Aubert
d836b75dc4 Merge pull request #1077 from CartoDB/mvt-list-aggregated-features
Experimental support for listing features in a grid
2019-03-01 18:06:38 +01:00
Daniel García Aubert
47321aebc4 Updates NEWS and next release version 2019-03-01 17:53:15 +01:00
Daniel García Aubert
2a0287b358 Add todos 2019-03-01 17:49:26 +01:00
Daniel García Aubert
f5fb60aa56 Filter output 2019-03-01 17:34:32 +01:00
Daniel García Aubert
a412e37a32 Improve test descriptions 2019-03-01 17:21:55 +01:00
Daniel García Aubert
dcf147cdfb linter 2019-03-01 17:05:35 +01:00
Daniel García Aubert
f9e5d9d0a9 Validate aggregation expresions 2019-03-01 17:02:06 +01:00
Daniel García Aubert
57a229655c Implement aggregation functions 2019-03-01 15:45:38 +01:00
csubira
a92a2b7291 Remove older doc files 2019-03-01 15:25:16 +01:00
csubira
d9fe5bf388 Update with latest changes 2019-03-01 15:19:27 +01:00
Daniel García Aubert
6dadb1bf6f Validate aggregation input 2019-03-01 15:17:22 +01:00
csubira
b7cf5ca174 Merge branch 'master' of github.com:CartoDB/Windshaft-cartodb into developer-center 2019-03-01 15:06:41 +01:00
Daniel García Aubert
77d5d8ebd4 Be able to aggregate by a field 2019-03-01 11:21:18 +01:00
Daniel García Aubert
92e62069d4 Improve error handling 2019-02-28 12:13:15 +01:00
Daniel García Aubert
32938eeab7 Validated only aggregated layers can be requested by the new endpoint 2019-02-27 18:54:21 +01:00
Daniel García Aubert
6531770e48 Remove subtitution tokens 2019-02-27 12:47:20 +01:00
Daniel García Aubert
561be2e5e8 Add tests 2019-02-27 12:43:26 +01:00
Daniel García Aubert
b87298bad9 Fix resolution definition and use zoom as path param 2019-02-27 12:42:54 +01:00
Daniel García Aubert
c3df075d91 Draft 2019-02-26 19:19:44 +01:00
Daniel García Aubert
f82e403180 Implement query to get features of a cluster for an aggregated map from @jgoizueta's cluster query 2019-02-25 19:40:18 +01:00
Daniel García Aubert
afd81a7814 Stubs next version 2019-02-22 13:09:00 +01:00
Daniel García Aubert
ca7f0ad4a6 Release 7.0.0 2019-02-22 12:52:10 +01:00
Daniel G. Aubert
4fc4390173 Merge pull request #1076 from CartoDB/drop-support-nodejs-6
Drop suppor for Node.js 6, npm 3, yarn and redis 3
2019-02-22 12:17:05 +01:00
Daniel García Aubert
fff54f021c Improve doc 2019-02-22 12:11:14 +01:00
Daniel García Aubert
6fee16fe5e Update comments and config 2019-02-22 10:49:22 +01:00
Daniel García Aubert
5f43db2e36 Remove POSTGIS_VERSION env variable and run test using mvt renderer always 2019-02-22 08:31:22 +01:00
Daniel García Aubert
561bdb3938 Improve MD formatting 2019-02-22 07:53:18 +01:00
Daniel García Aubert
47576358a2 Remove hack of stripping PARALLEL labels for PG releases before 9.6 2019-02-22 07:52:39 +01:00
Daniel García Aubert
582947accd Update release date 2019-02-21 18:45:19 +01:00
Daniel García Aubert
30f4b58ced Add npm as dependency 2019-02-21 18:26:56 +01:00
Daniel García Aubert
227a271bea Typo 2019-02-21 18:21:37 +01:00
Daniel García Aubert
5455d2997f Remove items in requirements list 2019-02-21 18:11:27 +01:00
Daniel García Aubert
241bb511ea Drop support for Redis 3, Postgres 9.5 and PostGIS 2.2 2019-02-21 17:55:58 +01:00
Daniel García Aubert
85e19bf16c Drop suppor for Node.js 6, npm 3, yarn and redis 3 2019-02-21 17:34:29 +01:00
Rafa de la Torre
e93dd982b9 Merge pull request #1072 from CartoDB/upgrade-camshaft-0.63.4
Upgrade camshaft 0.63.4
2019-02-13 14:57:47 +01:00
Rafa de la Torre
535b52df55 Update package-lock.json (node 10) camshaft@0.63.4 2019-02-13 14:39:44 +01:00
Rafa de la Torre
a04782b63e Update yarn camshaft@0.63.4 2019-02-13 14:39:44 +01:00
Rafa de la Torre
feb1e53a4d Upgrade camshaft 0.63.4 2019-02-13 14:39:30 +01:00
Simon Martín
705c001681 Merge pull request #1070 from CartoDB/docker-new-node-version
Dynamic node version in Dockerfile
2019-02-13 10:55:47 +01:00
Rafa de la Torre
a973fc981a Merge pull request #1071 from CartoDB/upgrade-camshaft-0.63.3
Upgrade camshaft 0.63.3
2019-02-13 09:36:52 +01:00
Daniel G. Aubert
77cbe2b545 version 6 to 6.9.2 in travis
Co-Authored-By: oleurud <oleurud@pm.me>
2019-02-13 09:27:16 +01:00
Daniel G. Aubert
0b5ef6a5e1 version 6 to 6.9.2 in travis
Co-Authored-By: oleurud <oleurud@pm.me>
2019-02-13 09:27:08 +01:00
Simon Martín
97f813a777 extracting postgres start from Node.js installation file 2019-02-12 18:51:33 +01:00
Rafa de la Torre
a242588d95 Update package-lock.json (node 10) camshaft@0.63.3 2019-02-12 18:43:32 +01:00
Simon Martín
b2f5e72bf1 renaming var 2019-02-12 18:34:25 +01:00
Rafa de la Torre
0daf8dcd8d Update yarn camshaft@0.63.3 2019-02-12 18:34:17 +01:00
Rafa de la Torre
79e886cfb9 Update camshaft@0.63.3 2019-02-12 18:29:05 +01:00
Simon Martín
4f6d04a3ae docker bash with dynamic image 2019-02-12 18:21:00 +01:00
Simon Martín
0e26d5ba5a travis with dynamic image 2019-02-12 18:20:43 +01:00
Simon Martín
915e0f8483 running tests with dynamic image 2019-02-12 18:20:32 +01:00
Simon Martín
4058d9fbc7 typo 2019-02-12 18:15:32 +01:00
Simon Martín
6c6ff42879 renaming docker nodejs installation script 2019-02-12 18:13:49 +01:00
Simon Martín
450f74b387 dockerfile CMD sh file 2019-02-12 16:55:17 +01:00
Simon Martín
f684994868 new docker file with nvm 2019-02-12 16:54:50 +01:00
Alejandro Guirao
be4240623a Pin Mapnik version and set crankshaft minor version 2019-02-12 15:30:55 +01:00
Simon Martín
4b5e003f33 fix bad file name 2019-02-12 15:06:33 +01:00
Simon Martín
6c7c0eb7e7 making nodejs version dynamic and LTS as default 2019-02-12 15:02:59 +01:00
Simon Martín
1ba6480110 using nvm 2019-02-12 14:46:41 +01:00
Daniel G. Aubert
af31962b2d Merge pull request #1069 from CartoDB/refactor
Upgrade windshaft to version 4.13.1
2019-02-11 13:49:50 +01:00
Daniel García Aubert
f134bd459e Update yarn.lock 2019-02-11 13:35:53 +01:00
Daniel García Aubert
01d02f8c2e Update windshaft to version 4.13.1 2019-02-11 13:33:50 +01:00
Daniel García Aubert
092bed6d9d Do not use caret symbol 2019-02-11 13:25:13 +01:00
Daniel García Aubert
8d9b8aced2 Upgrade windshaft to version 4.13.1 2019-02-11 13:19:34 +01:00
Daniel García Aubert
2856703acc Update windshaft devel branch 2019-02-08 18:44:59 +01:00
Daniel García Aubert
14b8a72551 Update windshaft devel branch 2019-02-07 19:16:36 +01:00
Daniel García Aubert
0125dcdd1d Update windshaft devel branch 2019-02-07 18:32:15 +01:00
Daniel García Aubert
6a15d30579 Update windshaft devel branch 2019-02-06 17:05:33 +01:00
Daniel García Aubert
3dcefa3ea1 Update windshaft devel dep 2019-02-05 08:55:34 +01:00
Daniel García Aubert
b3a995f880 Merge branch 'master' into refactor 2019-02-05 08:49:59 +01:00
Daniel García Aubert
d40be45e9a Update windshaft devel dep 2019-02-04 18:53:38 +01:00
Daniel G. Aubert
ec952d88cc Merge pull request #1068 from CartoDB/dynamic-map-pool
Handle 'max waitingClients count exceeded' error as: "429 You are over platfor's limits"
2019-02-04 14:09:52 +01:00
Daniel García Aubert
3983dfe004 Update windshaft to version 4.13.0 2019-02-04 13:54:41 +01:00
Daniel García Aubert
e1c4f8445c Update tilelive-mapnik to development version 2019-02-04 13:52:03 +01:00
Daniel García Aubert
2a68e0565e Update windshaft dep 2019-02-01 17:43:47 +01:00
Daniel García Aubert
f4b6173da9 Fix test 2019-01-30 16:54:19 +01:00
Daniel García Aubert
63c95df81c Lint 2019-01-30 16:36:08 +01:00
Daniel García Aubert
065f9c0a53 Add subtype to the limit error 2019-01-30 16:15:29 +01:00
Daniel García Aubert
abe28937ca Lint 2019-01-30 15:28:43 +01:00
Daniel García Aubert
77b7e03869 Update NEWS 2019-01-30 15:18:41 +01:00
Daniel García Aubert
d5af1bd9a2 Implement tests to check the limit error is the expected one 2019-01-30 15:15:07 +01:00
Daniel García Aubert
86f313ec52 Update windshaft 2019-01-30 10:49:57 +01:00
Daniel García Aubert
2e85e130c8 Be able to customize max waiting workers parameter 2019-01-30 10:41:39 +01:00
Daniel García Aubert
d70a87b299 Handle 'max waitingClients count exceeded' error as 429 Too many requests 2019-01-30 10:27:00 +01:00
Daniel García Aubert
c4576564e5 Update windshaft to a development version 2019-01-29 17:47:12 +01:00
Daniel G. Aubert
f82a7a148b Merge pull request #1067 from CartoDB/audit-dependencies
Audit dependencies and fix critical vulns
2019-01-24 13:26:38 +01:00
Daniel García Aubert
b4431d823c Update NEWS 2019-01-24 13:02:42 +01:00
Daniel García Aubert
0168aa3a61 Update winshaft to version 4.12.3 2019-01-24 13:02:09 +01:00
Daniel García Aubert
2064b14015 Update engine compatible versions 2019-01-24 12:14:32 +01:00
Daniel García Aubert
aeea83d5f5 Update NEWS 2019-01-23 19:03:21 +01:00
Daniel García Aubert
221bf0cefd Update winshaft to version 4.12.2 2019-01-23 19:01:51 +01:00
Daniel García Aubert
2899f62d95 Don't use carets 2019-01-17 17:19:42 +01:00
Daniel García Aubert
442ca34eb1 Update NEWS 2019-01-17 17:17:21 +01:00
Daniel García Aubert
0202a17138 Upgrade jshint to version 2.9.7 2019-01-17 16:08:01 +01:00
Daniel García Aubert
37645ec663 Update mocha to version 5.2.0 2019-01-17 15:53:03 +01:00
Iñigo Medina (aka MacGyver)
b7aed2a85d Merge pull request #1064 from CartoDB/dc-updating-rate-limits
rate limits new values
2019-01-11 14:23:58 +01:00
Simon Martín
a47c5b5568 rate limits new values 2019-01-11 13:53:11 +01:00
Alberto Romeu
56eb4a8dc3 Merge pull request #1063 from CartoDB/cors-authorization-headers
Adding Authorization to Access-Control-Allow-Headers
2019-01-11 12:35:36 +01:00
Simon Martín
7b029c890a NEWS 2019-01-10 16:57:30 +01:00
Simon Martín
644b4232ca adding Authorization to Access-Control-Allow-Headers 2019-01-10 16:56:07 +01:00
Daniel G. Aubert
ff27e6744e Merge pull request #1062 from CartoDB/gc-stats
Report fine-grained Garbage Collector stats
2019-01-03 16:10:54 +01:00
Daniel García Aubert
fb929e71fc Update docs 2019-01-03 12:27:12 +01:00
Daniel García Aubert
0bd7bd1621 Update yarn.lock 2019-01-03 11:50:17 +01:00
Daniel García Aubert
5cad6e40c4 Get GC stats from dedicated binding 2019-01-03 11:47:54 +01:00
Daniel G. Aubert
3a4984c1ce Merge pull request #1061 from CartoDB/update-doc-nodejs-npm-versions
Update docs
2019-01-02 13:04:20 +01:00
Daniel García Aubert
caa5a648df Add deprecation warning 2019-01-02 12:54:39 +01:00
Daniel García Aubert
8487e4fb52 Update docs 2019-01-02 11:33:23 +01:00
Daniel García Aubert
b508689b53 Stubs next version 2018-12-26 16:57:19 +01:00
Daniel García Aubert
3b4d1bf72a Release 6.5.1 2018-12-26 16:55:35 +01:00
Daniel García Aubert
0bbbdfa092 Update NEWS 2018-12-26 16:54:14 +01:00
Daniel G. Aubert
c1e769ade2 Merge pull request #1060 from CartoDB/fix-carto-package
Fix carto-package.json
2018-12-26 16:52:49 +01:00
Daniel García Aubert
a20ce69028 Fix carto-package.json 2018-12-26 16:48:51 +01:00
Daniel García Aubert
80519cb397 Stubs next version 2018-12-26 15:26:09 +01:00
Daniel García Aubert
7347263dfd Release 6.5.0 2018-12-26 15:23:28 +01:00
Rafa de la Torre
2a3b6b830b Merge pull request #1059 from CartoDB/update-cartodb-psql-0.13.1
Update cartodb-psql to 0.13.1 and related reverse dependenciess (WIP)
2018-12-13 17:22:55 +01:00
Rafa de la Torre
11b299e116 Update NEWS.md 2018-12-13 16:28:37 +01:00
Rafa de la Torre
27eef4ce42 Update NEWS.md 2018-12-13 16:26:07 +01:00
Rafa de la Torre
59badc0137 Update yarn.lock deps 2018-12-13 15:25:57 +01:00
Rafa de la Torre
f49698efa1 Update package-lock.json deps 2018-12-13 15:23:11 +01:00
Rafa de la Torre
19d6cae10d Update cartodb-psql and rdeps (WIP) 2018-12-12 18:45:36 +01:00
Daniel G. Aubert
7057f5a5c2 Merge pull request #1056 from CartoDB/nodejs-10
Support Nodejs 10 LTS
2018-11-22 14:48:46 +01:00
Daniel García Aubert
3e336204df Update NEWS 2018-11-21 18:01:53 +01:00
Daniel García Aubert
3f4ecb195c Update NEWS 2018-11-21 17:51:56 +01:00
Daniel García Aubert
58b528d00a Update NEWS 2018-11-21 17:49:42 +01:00
Daniel García Aubert
2d2060088c Update yarn.lock 2018-11-21 17:43:50 +01:00
Daniel García Aubert
d1667fac73 Upgrade turbo-carto@0.21.0 2018-11-21 17:36:30 +01:00
Daniel García Aubert
98c0b1f9bd Upgrade cartodb-redis@2.1.0 2018-11-21 17:14:03 +01:00
Daniel García Aubert
1d2548a3e6 Upgrade cartodb-query-tables@0.4.0 2018-11-21 16:39:12 +01:00
Daniel García Aubert
3690959be4 Update camshaft@0.63.0 2018-11-21 15:42:12 +01:00
Daniel García Aubert
7de5fd1515 Update yarn.lock 2018-11-21 13:29:32 +01:00
Daniel García Aubert
f9d1e39b7b Update windshaft@4.12.0 2018-11-21 13:19:29 +01:00
Daniel García Aubert
16a0d9707b Update redis-mpool@0.7.0 2018-11-21 13:11:30 +01:00
Daniel García Aubert
6962abfd10 Update cartodb-psql@0.13.0 2018-11-21 13:09:18 +01:00
Daniel García Aubert
dc0d4f0011 Merge branch 'master' into nodejs-10 2018-11-20 11:20:20 +01:00
Daniel G. Aubert
6bcb535d3f Merge pull request #1057 from CartoDB/add-cpu-metrics
Add process CPU usage metrics
2018-11-15 17:59:24 +01:00
Daniel García Aubert
4ad9902601 Add process CPU usage metrics 2018-11-15 17:28:18 +01:00
Daniel García Aubert
2a933788fd Update yarn.lock 2018-11-06 13:27:30 +01:00
Daniel García Aubert
3febf3e357 Use 'npm ci' instead of 'npm install' to be able to install exactly what is in a lockfile 2018-11-06 12:58:51 +01:00
Daniel García Aubert
a8ca80c23c List dependency tree after install in docker tests 2018-11-06 12:35:10 +01:00
Daniel García Aubert
f3b1bb742a Ensure all pg connections are being refreshed 2018-11-06 12:17:39 +01:00
Daniel García Aubert
4d1ed0be27 Update package-lock.json 2018-11-05 19:47:21 +01:00
Daniel García Aubert
af4b9f57f5 Skip just torque database timeout limit 2018-11-05 19:10:08 +01:00
Daniel García Aubert
6e7bd2585f Skip database timeout test 2018-11-05 18:53:00 +01:00
Daniel García Aubert
40ccdfd9b3 Drain pool connection before 2018-11-05 18:37:20 +01:00
Daniel García Aubert
659b0ba889 Use pdql.end with callback 2018-11-05 18:14:26 +01:00
Daniel García Aubert
71b8699f47 Shut down the pool after setting the database timeout 2018-11-05 17:16:22 +01:00
Daniel García Aubert
24c5bbb182 Workaround to drain pg pool effectively 2018-11-05 16:50:52 +01:00
Daniel García Aubert
2eea20b161 Updated cartodb-psql 2018-11-05 16:03:43 +01:00
Daniel García Aubert
f2180576de Remove test filter 2018-11-02 13:57:56 +01:00
Daniel García Aubert
d9039569bd Output yarn version 2018-11-02 13:57:30 +01:00
Daniel García Aubert
4a82d18cc6 Fix docker command 2018-11-02 13:56:43 +01:00
Daniel García Aubert
f8fa78bb8b Output npm version 2018-11-02 13:30:24 +01:00
Daniel García Aubert
babfa9aae3 Revert "Skip test temporally"
This reverts commit ebf2f54cd5.
2018-11-02 13:30:04 +01:00
Daniel García Aubert
ebf2f54cd5 Skip test temporally 2018-11-02 12:39:34 +01:00
Daniel García Aubert
20c1e8ca05 Revert "Revert "Add cache-buster to hit database always""
This reverts commit 60724897cc.
2018-11-02 12:24:45 +01:00
Daniel García Aubert
2a35d51d45 Revert "Set DEBUG env variable to run test"
This reverts commit 7bd188dafb.
2018-11-02 12:24:01 +01:00
Daniel García Aubert
ebe2d2ddab Revert "Add log to debug server option"
This reverts commit b4e57438ed.
2018-11-02 12:23:48 +01:00
Daniel García Aubert
1ee30e9b53 Revert "Move log to debug server option"
This reverts commit 938e3b2b07.
2018-11-02 12:23:36 +01:00
Daniel García Aubert
7527003711 Revert "Move log to debug server option"
This reverts commit 6c3d8dbe64.
2018-11-02 12:23:05 +01:00
Daniel García Aubert
acd0bbc94f Revert "Add log"
This reverts commit 6cc746dc83.
2018-11-02 12:22:41 +01:00
Daniel García Aubert
00e3f331b4 Revert "Do not run test against node 6"
This reverts commit 7a6fbecac4.
2018-11-02 12:20:49 +01:00
Daniel García Aubert
7a6fbecac4 Do not run test against node 6 2018-11-02 12:17:28 +01:00
Daniel García Aubert
6cc746dc83 Add log 2018-11-02 12:01:59 +01:00
Daniel García Aubert
6c3d8dbe64 Move log to debug server option 2018-11-02 11:23:44 +01:00
Daniel García Aubert
938e3b2b07 Move log to debug server option 2018-11-02 11:12:32 +01:00
Daniel García Aubert
b4e57438ed Add log to debug server option 2018-11-02 10:58:17 +01:00
Daniel García Aubert
7bd188dafb Set DEBUG env variable to run test 2018-11-02 10:37:39 +01:00
Daniel García Aubert
60724897cc Revert "Add cache-buster to hit database always"
This reverts commit e03defc30f.
2018-11-02 10:29:19 +01:00
Daniel García Aubert
e03defc30f Add cache-buster to hit database always 2018-11-02 10:21:53 +01:00
Daniel García Aubert
3bb1f893af Test only 2018-11-01 15:05:38 +01:00
Daniel García Aubert
37a61f527c Do not set language settings for travis 2018-10-31 18:57:25 +01:00
Daniel García Aubert
5e3d546fb6 Add package-lock.json and link to development dependencies 2018-10-31 18:52:40 +01:00
Daniel García Aubert
b7b5f031f3 Update docker test scripts to support Node.js 6 and 10 buildings 2018-10-31 17:20:25 +01:00
Daniel García Aubert
e57e548c31 Run docker test against Node.js 10 2018-10-31 14:47:32 +01:00
Javier Goizueta
b0d1d5a07a Fix test 2018-10-31 12:39:06 +01:00
Javier Goizueta
c63380427e Fix comment 2018-10-31 12:02:23 +01:00
Javier Goizueta
102e75ce95 Add test for hours time dimension
It tests the problem solved in #1054
2018-10-31 12:00:40 +01:00
Daniel G. Aubert
420c39337c Merge pull request #1052 from CartoDB/use-strict
Use strict mode
2018-10-26 11:21:37 +02:00
Javier Goizueta
214c796a4c Merge pull request #1054 from CartoDB/fix-time-dimension-iso-hours
Fix iso format for hours in time dimensions
2018-10-26 11:01:00 +02:00
Daniel García Aubert
8918d6bec0 Link dependencies to the released versions 2018-10-26 10:19:59 +02:00
Daniel García Aubert
ca7acb8339 Merge branch 'master' into use-strict 2018-10-25 11:11:30 +02:00
Daniel García Aubert
5083ccb605 Link development branches of related dependencies 2018-10-25 10:59:27 +02:00
Javier Goizueta
6908aa532c Fix iso format for hours in time dimensions
The HH specifies hour of day 01-12, we need HH24 for 00-23
2018-10-24 21:15:10 +02:00
Daniel García Aubert
a7daa077ac Update NEWS 2018-10-24 15:53:28 +02:00
Rafa de la Torre
9f0d4905b1 Merge pull request #1053 from CartoDB/fix-tests-undefined-server
Fix for non-deterministic test (undefined server)
2018-10-24 12:11:36 +02:00
Daniel García Aubert
89d10210be Fix undefined context error in test, it raised after forcing strict mode 2018-10-24 11:58:20 +02:00
Rafa de la Torre
545d387bb4 Fix for non-deterministic test (undefined server)
When running tests I got this error:

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

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

That is fixed by returning `getServer()`, which seems to be the
original intent: return a singleton of `CartodbServer` properly
initialized in case it is not overriden through options.
2018-10-23 18:55:31 +02:00
Daniel García Aubert
e2d27db828 Ise strict mode for moduler under test folder 2018-10-23 18:39:02 +02:00
Raul Ochoa
33bcac189f Merge pull request #1050 from CartoDB/prevent-layert-stats-undefined-access
Prevent "Cannot read property 'geom_type' of undefined" on layer stats
2018-10-23 18:37:00 +02:00
Daniel García Aubert
361e99006b Ignore line 2018-10-23 18:20:11 +02:00
Daniel García Aubert
7162ab1631 Add app.js to be linted 2018-10-23 18:19:55 +02:00
Daniel García Aubert
9374e0fe18 Add strict mode to main file (app.js) 2018-10-23 18:09:58 +02:00
Raul Ochoa
b13ae62d0f Do not assert the estimated count as it seems to change over pg versions 2018-10-23 16:02:37 +00:00
Raul Ochoa
49de289a9c Merge pull request #1051 from CartoDB/output-versions
Output PostgreSQL and PostGIS versions
2018-10-23 17:58:30 +02:00
Daniel García Aubert
b6dcf72268 Missing semicolon 2018-10-23 17:55:35 +02:00
Daniel García Aubert
76cfd185de Please jshint 2018-10-23 17:54:11 +02:00
Daniel García Aubert
79820a0f05 Add function as method of to be stric mode compilant 2018-10-23 17:52:19 +02:00
Daniel García Aubert
a0126f6a15 Use stric mode for resource-locator module 2018-10-23 17:48:53 +02:00
Raul Ochoa
abd378e5f6 Run tests based on PostGIS version 2018-10-23 15:47:48 +00:00
Daniel García Aubert
e7e3d612a1 Use strict mode in modules under lib folder (except lib/cartodb/models/resource-locator.js) 2018-10-23 17:45:42 +02:00
Raul Ochoa
208dbfd951 Output PostgreSQL and PostGIS versions 2018-10-23 14:34:17 +00:00
Raul Ochoa
26e4a05276 Going green: prevent TypeError for empty tables/results
This is the intial step to fix https://github.com/CartoDB/carto-vl/issues/1049.
2018-10-23 13:50:49 +00:00
Raul Ochoa
3e261fb353 Going red: fails with 400 due to "Cannot read property 'geom_type' of undefined" 2018-10-23 15:49:34 +02:00
Daniel G. Aubert
4775c73aee Merge pull request #1049 from CartoDB/upgrade-windshaft-4.11.4
Upgrade windshaft to version 4.11.4
2018-10-23 13:03:17 +02:00
Daniel García Aubert
1ece97d0a1 Update NEWS 2018-10-23 12:36:03 +02:00
Daniel García Aubert
87ef8d1977 Upgrade windshaft to version 4.11.4 2018-10-23 12:25:36 +02:00
Daniel G. Aubert
2ebb1728ee Merge pull request #1048 from CartoDB/fix-uncaught-error
Prevent from uncaught exception
2018-10-23 11:54:55 +02:00
Daniel García Aubert
621b11ebd6 Update NEWS 2018-10-23 10:05:53 +02:00
Daniel García Aubert
12d58f3af2 Prevent from uncaught exception: Range filter Error from camshaft when getting analysis query 2018-10-22 15:26:59 +02:00
Daniel G. Aubert
211e815d9c Merge pull request #1047 from CartoDB/upgrade-windshaft-4.11.3
Upgrade windshaft 4.11.3
2018-10-19 13:26:54 +02:00
Daniel García Aubert
465fd2ec0a Merge branch 'master' into upgrade-windshaft-4.11.3 2018-10-19 13:08:34 +02:00
Daniel García Aubert
d0c405ae46 Upgrade windshaft to version 4.11.3 2018-10-19 13:04:03 +02:00
Daniel G. Aubert
953d831d5f Merge pull request #1046 from CartoDB/fix-create-key
Do not use 'Object.assign' as '_.defautls' equivalent
2018-10-19 12:49:17 +02:00
Daniel García Aubert
5573db2bc1 Do not use Object.assign as _.defautls equivalent 2018-10-19 11:57:54 +02:00
Rafa de la Torre
195b23248b Merge pull request #1041 from CartoDB/config-postgis-for-mvts
Sample configs: use PostGIS to generate MVT's
2018-10-17 18:37:27 +02:00
Rafa de la Torre
83897293c6 Fix test by giving redis enough time to delete 2018-10-17 17:35:36 +02:00
Rafa de la Torre
f26ddef244 Make rate limit tests work in dual mode 2018-10-17 17:35:36 +02:00
Rafa de la Torre
d25e8e9798 Make the test work in dual mode (mapnik/pgis) 2018-10-17 17:35:36 +02:00
Rafa de la Torre
bfbd9a8f22 Fix another suite (compat mapnik/pgis) 2018-10-17 17:35:36 +02:00
Rafa de la Torre
bd17f9f5e1 A better implementation of mvt suites 2018-10-17 17:35:36 +02:00
Rafa de la Torre
8491b86c17 Extract test generation function 2018-10-17 17:35:36 +02:00
Rafa de la Torre
376a3743c1 Fix buffer size per format tests 2018-10-17 17:35:36 +02:00
Rafa de la Torre
a42af5e0d5 Do not run test if ST_AsMvt not avail. 2018-10-17 17:35:36 +02:00
Rafa de la Torre
e157649571 Use of postgis renderer based on availabilty 2018-10-17 17:35:36 +02:00
Rafa de la Torre
e50d1a10d0 Skip tests if they cannot be run
If configured with `mvt.usePostGIS` but with no postgis version
supporting it, they should be skipped.
2018-10-17 17:35:36 +02:00
Rafa de la Torre
d474d49ce8 Do not use point in world's border 2018-10-17 17:35:36 +02:00
Rafa de la Torre
4dba4ef641 Tweak the scale denominator for pg-mvt renderer
The scale denominator is calculated with float values and more
precision, resulting in different (but more accurate) values
2018-10-17 17:35:36 +02:00
Rafa de la Torre
be08fa3bfa Tweak id's to test against pg-mvt renderer
Actually, the ID's are not generated by ST_AsMVT. They appear as an
artifact of testing, when using toGeoJSONSync (implemented in mapnik).
2018-10-17 17:35:36 +02:00
Simon Martín
945b151712 using docker tag postgis-2.4.4.5 in travis 2018-10-17 17:35:36 +02:00
Simon Martín
2af6486f73 new docker tags 2018-10-17 17:35:36 +02:00
Rafa de la Torre
9cffc8781a Sample configs: use PostGIS to generate MVT's 2018-10-17 17:35:36 +02:00
Daniel García Aubert
b75c1f7f08 Update NEWS 2018-10-17 15:42:13 +02:00
Rafa de la Torre
c5d22bf9e3 Update package version 2018-10-17 15:29:07 +02:00
Rafa de la Torre
1baae5e709 Update dependencies (windshaft@4.11.2) 2018-10-17 15:28:56 +02:00
Rafa de la Torre
da3239cfa1 Merge pull request #1045 from CartoDB/update-windshaft-pg-mvt-trailing-semicolons
Update windshaft with a patch fo pg-mvt renderer
2018-10-17 15:25:27 +02:00
Rafa de la Torre
ba0078c51c Update windshaft with a patch fo pg-mvt renderer
Update windshaft to version 4.11.2, which contains a patch that makes
the pg-mvt renderer accept a trailing semicolon in the input query.
2018-10-17 15:12:33 +02:00
Daniel G. Aubert
47f64401a7 Merge pull request #1044 from CartoDB/upgrade-windshaft-4.11.1
Upgrade windshaft to version 4.11.1
2018-10-16 17:28:22 +02:00
Daniel García Aubert
8bdbe7c9b7 Update NEWS 2018-10-16 17:18:21 +02:00
Daniel García Aubert
0637018cca Upgrade windshaft to version 4.11.1 2018-10-16 17:16:57 +02:00
Daniel G. Aubert
8a7bef673b Merge pull request #1043 from CartoDB/mvt-query-rewrite
Use overviews query rewriter for mvt-renderer
2018-10-15 16:02:39 +02:00
Daniel García Aubert
a0e71ac396 Upgrade windshaft to version 4.11.0 2018-10-15 15:50:48 +02:00
Daniel García Aubert
184a804367 Improve assertions 2018-10-15 15:25:23 +02:00
Daniel García Aubert
c234b4ea91 Organize params 2018-10-15 08:30:51 +02:00
Daniel García Aubert
db13f5e4f3 ES6 tweaks 2018-10-15 08:25:44 +02:00
Daniel García Aubert
f9a8b3c827 Add acceptance test 2018-10-15 08:23:39 +02:00
Daniel García Aubert
17886d0e43 Revert "Use early return pattern"
This reverts commit 297e56f4e1.
2018-10-11 18:17:16 +02:00
Daniel García Aubert
1f112d587f Revert "Rename function"
This reverts commit 12dc1626a7.
2018-10-11 18:17:03 +02:00
Daniel García Aubert
5c56ea6b22 Add test to validate that Postgis and Mapnik renderers are using overviews tables 2018-10-11 17:59:49 +02:00
Daniel García Aubert
3c76dfbbb3 Use devel branch of windshaft 2018-10-11 15:41:58 +02:00
Daniel García Aubert
e158e3e426 Pass overview-query-rewriter to mvt renderer 2018-10-11 15:41:38 +02:00
Daniel García Aubert
12dc1626a7 Rename function 2018-10-10 15:07:47 +02:00
Daniel García Aubert
297e56f4e1 Use early return pattern 2018-10-10 15:06:56 +02:00
Javier Goizueta
09f75441ba Merge pull request #1039 from CartoDB/time-dimensions
Time dimensions
2018-10-09 16:06:06 +02:00
Javier Goizueta
41bd69d050 Remove public docs for the time being
We might want to make changes to the API after initial test usage
2018-10-09 15:17:29 +02:00
Javier Goizueta
73b3402d85 Refactor stats collection 2018-10-09 13:24:08 +02:00
Javier Goizueta
d66a304b00 Merge branch 'master' into time-dimensions
# Conflicts:
#	NEWS.md
2018-10-08 19:35:38 +02:00
Javier Goizueta
ee63b247cd Slight refactor 2018-10-08 19:25:04 +02:00
Javier Goizueta
418e0e2aa3 Documentation corrections 2018-10-08 19:16:50 +02:00
Javier Goizueta
d4bd706fe2 Clarify some tests 2018-10-08 19:16:32 +02:00
Javier Goizueta
a4dfc09c71 Aggregation dimensions documentation 2018-10-07 23:12:41 +02:00
Javier Goizueta
9ed39f149b Time dimension tests 2018-10-07 22:46:02 +02:00
Javier Goizueta
0e85aa56da Fix test 2018-10-07 11:35:28 +02:00
Javier Goizueta
2f59919f84 Dimension metadata test 2018-10-07 00:29:12 +02:00
Javier Goizueta
10baf43ede Fix dimension metadata bug 2018-10-07 00:28:53 +02:00
Javier Goizueta
996d7fc90d Lint fixes 2018-10-06 18:26:43 +02:00
Javier Goizueta
c0febf2fd1 Rename time dimension parameters 2018-10-05 20:08:40 +02:00
Javier Goizueta
f841f65a1e Dimensions metadata 2018-10-04 19:50:14 +02:00
Javier Goizueta
c9786ee3f6 Catch aggregation query errors 2018-10-03 23:13:22 +02:00
Javier Goizueta
99b62edcbd Bug fixes 2018-10-03 23:12:58 +02:00
Javier Goizueta
c588d4139e Refactor time dimensions 2018-10-03 21:02:22 +02:00
Javier Goizueta
aff55351ad Unify parameter names 2018-10-03 19:07:47 +02:00
Javier Goizueta
96ba075698 Unify handling of cyclic time groupings
Remove generic cyclic grouping
2018-10-03 18:57:00 +02:00
Javier Goizueta
a7d5415f64 Remove offsets from time dimension computations 2018-10-03 17:12:01 +02:00
Javier Goizueta
dede22c915 Changes in time dimensions API
Use single `starting` epoch instead of various offsets.
Add ISO text representation.
Adopt ISO conventions for day of week and week of year.
Rename internal parameters for consistency with external API.
2018-10-03 17:05:58 +02:00
Javier Goizueta
fbf3fd9d8c Support old and new dimension definitions 2018-09-25 19:10:56 +02:00
Simon Martín
e70de80cdf Stub next version 2018-09-24 11:35:10 +02:00
Simon Martín
ef9ec5b262 Merge pull request #1032 from CartoDB/windshaft-remove-step
upgrades Windshaft (remove step)
2018-09-24 11:29:48 +02:00
Simon Martín
f9b8152f21 Merge branch 'master' into windshaft-remove-step 2018-09-24 11:16:45 +02:00
Simon Martín
e0ff8b4320 NEWS/package to version 6.4.0 and release date 2018-09-24 11:14:41 +02:00
Simon Martín
6a699ba51b windshaft version 4.10.0 2018-09-24 11:03:24 +02:00
Javier Goizueta
fbcfc7a582 WIP: time dimensions for aggregation 2018-09-20 21:12:54 +02:00
Simon Martín
1899fd3813 updating to lastest windshaft version 2018-09-20 17:41:44 +02:00
Javier Goizueta
bd7c99f94f Merge pull request #1036 from CartoDB/1034-aggregation-bbox
Aggregation limits adjustment fix
2018-09-17 17:05:31 +02:00
Javier Goizueta
6ba9e50da7 Minor tests refactor 2018-09-13 08:54:59 +02:00
Javier Goizueta
21a2d9e82f Merge pull request #1035 from CartoDB/1033-no-the_geom
Avoid requiring the_geom for point-sample aggregation
2018-09-13 08:51:34 +02:00
Javier Goizueta
0f20cdaae1 More robust adjustment of spatial limits to the aggregation grid
See #1034
2018-09-12 19:27:21 +02:00
Javier Goizueta
5d813b6e43 Add more tests for aggregation accuracy
Some tests are skipped for Mapnik MVTs, because its bbox filtering isn't accurate
2018-09-12 19:26:09 +02:00
Raúl Marín
a842acfdb4 Merge pull request #1029 from Algunenano/update_640_vars
Update next release version according to the changes done
2018-09-12 19:09:13 +02:00
Javier Goizueta
1e65804a1b Avoid requiring the_geom for point-sample aggregation 2018-09-12 11:26:14 +02:00
Javier Goizueta
acf0b082b4 Perform some tests for all placements
The "only the_geom" and other aggregation tests were perform only for default aggregation.
2018-09-12 11:25:21 +02:00
Simon Martín
2c6305bcd4 windshaft version: remove-step 2018-09-07 12:23:09 +02:00
Raul Marin
bd153a0c87 Update next release version according to the changes done 2018-09-05 16:07:43 +02:00
Raúl Marín
fb6987e91a Merge pull request #1027 from CartoDB/carto_mvt_extent
Update Windshaft to 4.9.0
2018-09-05 13:30:12 +02:00
Eneko Lakasta
74c036483a Merge pull request #1028 from CartoDB/1024-log-overviews-mapconfig
use as flag overviewsAddedToMapconfig instead of overviewsUsed
2018-09-05 13:24:48 +02:00
Eneko Lakasta
65e10bc20d use as flag overviewsAddedToMapconfig instead of overviewsUsed 2018-09-05 13:14:55 +02:00
Eneko Lakasta
89a1e69bec Merge pull request #1026 from CartoDB/1024-log-overviews-mapconfig
Log overviews usage during map instantiation
2018-09-05 12:52:22 +02:00
Eneko Lakasta
564884797d add tests to check that flags for non overviews instantiation are correct 2018-09-05 12:39:51 +02:00
Eneko Lakasta
dd1ee56648 use .some instead of .map & .some 2018-09-05 11:56:45 +02:00
Eneko Lakasta
c54c3754ef fix indentation 2018-09-05 11:51:58 +02:00
Eneko Lakasta
d72a5075b9 move overviews flags in named map instantiation checks to its own specific tests 2018-09-05 11:48:21 +02:00
Eneko Lakasta
6dde5fc6f1 use .some instead of reduce 2018-09-04 18:22:48 +02:00
Eneko Lakasta
880ef63720 add to logs named maps overviews instantiation 2018-09-04 16:21:20 +02:00
Eneko Lakasta
b75150e91e set mapType in the controller instead of in the adapter 2018-09-04 16:20:38 +02:00
Eneko Lakasta
006e21379f please jshint line to long 2018-09-04 15:30:20 +02:00
Eneko Lakasta
03850fb31c Merge branch 'master' into 1024-log-overviews-mapconfig 2018-09-04 15:14:21 +02:00
Eneko Lakasta
7df0fb456b add to log anonymous maps instantiations that use overviews tables
Added to tiler profiler object:

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

22
.eslintrc.js Normal file
View File

@@ -0,0 +1,22 @@
module.exports = {
env: {
commonjs: true,
es6: true,
node: true,
mocha: true
},
extends: [
'standard'
],
globals: {
Atomics: 'readonly',
SharedArrayBuffer: 'readonly'
},
parserOptions: {
ecmaVersion: 2018
},
rules: {
"indent": ["error", 4],
"semi": ["error", "always"]
}
}

58
.github/workflows/main.yml vendored Normal file
View File

@@ -0,0 +1,58 @@
name: continuous integration
on:
pull_request:
paths-ignore:
- 'LICENSE'
- 'README**'
- 'HOW_TO_RELEASE**'
- 'LOGGING**'
env:
GCLOUD_VERSION: '306.0.0'
ARTIFACTS_PROJECT_ID: cartodb-on-gcp-main-artifacts
jobs:
build-test-docker:
runs-on: ubuntu-18.04
timeout-minutes: 10
steps:
- uses: actions/checkout@v2
with:
submodules: true
token: ${{ secrets.CARTOFANTE_PERSONAL_TOKEN }}
- name: Build image
# we tag with "latest" but we don't push it on purpose. We use it as a base for the testing image
run: |
echo ${GITHUB_SHA::7}
echo ${GITHUB_REF##*/}
docker build -f private/Dockerfile -t gcr.io/$ARTIFACTS_PROJECT_ID/windshaft:latest -t gcr.io/$ARTIFACTS_PROJECT_ID/windshaft:${GITHUB_REF##*/} -t gcr.io/$ARTIFACTS_PROJECT_ID/windshaft:${GITHUB_SHA::7} -t gcr.io/$ARTIFACTS_PROJECT_ID/windshaft:${GITHUB_REF##*/}--${GITHUB_SHA::7} .
- name: Build testing image
# here it uses the lastest from prev step to add the needed parts on top
run: |
docker build -t gcr.io/$ARTIFACTS_PROJECT_ID/windshaft-test:latest -f private/Dockerfile.test .
- name: Setup gcloud authentication
uses: google-github-actions/setup-gcloud@master
with:
version: ${{env.GCLOUD_VERSION}}
service_account_key: ${{ secrets.ARTIFACTS_GCLOUD_ACCOUNT_BASE64 }}
- name: Configure docker and pull images
# we pull images manually, as if done in next step using docker-compose it fails because missing openssl
run: |
gcloud auth configure-docker
docker pull gcr.io/cartodb-on-gcp-main-artifacts/postgres:latest
docker pull gcr.io/cartodb-on-gcp-main-artifacts/redis:latest
- name: Run tests inside container
run: docker-compose -f private/ci/docker-compose.yml run windshaft-tests
- name: Upload image
run: |
docker push gcr.io/$ARTIFACTS_PROJECT_ID/windshaft:${GITHUB_REF##*/}
docker push gcr.io/$ARTIFACTS_PROJECT_ID/windshaft:${GITHUB_SHA::7}
docker push gcr.io/$ARTIFACTS_PROJECT_ID/windshaft:${GITHUB_REF##*/}--${GITHUB_SHA::7}

47
.github/workflows/master.yml vendored Normal file
View File

@@ -0,0 +1,47 @@
# in this workflow we don't run the tests. Only build image, tag (also latests) and upload. The tests are not run because they are run
# on each pull request, and there is a branch protection that forces to have branch up to date before merging, so tests are always run
# with the latest code
name: master build image
on:
push:
branches:
- master
env:
GCLOUD_VERSION: '306.0.0'
ARTIFACTS_PROJECT_ID: cartodb-on-gcp-main-artifacts
jobs:
build-master:
runs-on: ubuntu-18.04
timeout-minutes: 5
steps:
- uses: actions/checkout@v2
with:
submodules: true
token: ${{ secrets.CARTOFANTE_PERSONAL_TOKEN }}
- name: Build image
run: |
echo ${GITHUB_SHA::7}
echo ${GITHUB_REF##*/}
docker build -f private/Dockerfile -t gcr.io/$ARTIFACTS_PROJECT_ID/windshaft:latest -t gcr.io/$ARTIFACTS_PROJECT_ID/windshaft:${GITHUB_REF##*/} -t gcr.io/$ARTIFACTS_PROJECT_ID/windshaft:${GITHUB_SHA::7} .
- name: Setup gcloud authentication
uses: google-github-actions/setup-gcloud@master
with:
version: ${{env.GCLOUD_VERSION}}
service_account_key: ${{ secrets.ARTIFACTS_GCLOUD_ACCOUNT_BASE64 }}
- name: Configure docker
run: |
gcloud auth configure-docker
- name: Upload image
run: |
docker push gcr.io/$ARTIFACTS_PROJECT_ID/windshaft:${GITHUB_REF##*/}
docker push gcr.io/$ARTIFACTS_PROJECT_ID/windshaft:${GITHUB_SHA::7}
docker push gcr.io/$ARTIFACTS_PROJECT_ID/windshaft:latest

3
.gitignore vendored
View File

@@ -11,3 +11,6 @@ redis.pid
*.log
coverage/
.DS_Store
.nyc_output
build_resources/
.dockerignore

4
.gitmodules vendored Normal file
View File

@@ -0,0 +1,4 @@
[submodule "private"]
path = private
url = git@github.com:CartoDB/Windshaft-cartodb-private.git
branch = master

View File

@@ -1,95 +0,0 @@
{
// // JSHint Default Configuration File (as on JSHint website)
// // See http://jshint.com/docs/ for more details
//
// "maxerr" : 50, // {int} Maximum error before stopping
//
// // Enforcing
// "bitwise" : true, // true: Prohibit bitwise operators (&, |, ^, etc.)
// "camelcase" : false, // true: Identifiers must be in camelCase
"curly" : true, // true: Require {} for every new block or scope
"eqeqeq" : true, // true: Require triple equals (===) for comparison
"forin" : true, // true: Require filtering for..in loops with obj.hasOwnProperty()
"freeze" : true, // true: prohibits overwriting prototypes of native objects such as Array, Date etc.
"immed" : true, // true: Require immediate invocations to be wrapped in parens e.g. `(function () { } ());`
// "indent" : 4, // {int} Number of spaces to use for indentation
// "latedef" : false, // true: Require variables/functions to be defined before being used
"newcap" : true, // true: Require capitalization of all constructor functions e.g. `new F()`
"noarg" : true, // true: Prohibit use of `arguments.caller` and `arguments.callee`
// "noempty" : true, // true: Prohibit use of empty blocks
"nonbsp" : true, // true: Prohibit "non-breaking whitespace" characters.
"nonew" : true, // true: Prohibit use of constructors for side-effects (without assignment)
// "plusplus" : false, // true: Prohibit use of `++` & `--`
// "quotmark" : false, // Quotation mark consistency:
// // false : do nothing (default)
// // true : ensure whatever is used is consistent
// // "single" : require single quotes
// // "double" : require double quotes
"undef" : true, // true: Require all non-global variables to be declared (prevents global leaks)
"unused" : true, // true: Require all defined variables be used
// "strict" : true, // true: Requires all functions run in ES5 Strict Mode
// "maxparams" : false, // {int} Max number of formal params allowed per function
// "maxdepth" : false, // {int} Max depth of nested blocks (within functions)
// "maxstatements" : false, // {int} Max number statements per function
"maxcomplexity" : 6, // {int} Max cyclomatic complexity per function
"maxlen" : 120, // {int} Max number of characters per line
//
// // Relaxing
// "asi" : false, // true: Tolerate Automatic Semicolon Insertion (no semicolons)
// "boss" : false, // true: Tolerate assignments where comparisons would be expected
"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" : 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()`
// "expr" : false, // true: Tolerate `ExpressionStatement` as Programs
// "funcscope" : false, // true: Tolerate defining variables inside control statements
// "globalstrict" : false, // true: Allow global "use strict" (also enables 'strict')
// "iterator" : false, // true: Tolerate using the `__iterator__` property
// "lastsemic" : false, // true: Tolerate omitting a semicolon for the last statement of a 1-line block
// "laxbreak" : false, // true: Tolerate possibly unsafe line breakings
// "laxcomma" : false, // true: Tolerate comma-first style coding
// "loopfunc" : false, // true: Tolerate functions being defined in loops
// "multistr" : false, // true: Tolerate multi-line strings
// "noyield" : false, // true: Tolerate generator functions with no yield statement in them.
// "notypeof" : false, // true: Tolerate invalid typeof operator values
// "proto" : false, // true: Tolerate using the `__proto__` property
// "scripturl" : false, // true: Tolerate script-targeted URLs
// "shadow" : false, // true: Allows re-define variables later in code e.g. `var x=1; x=2;`
// "sub" : false, // true: Tolerate using `[]` notation when it can still be expressed in dot notation
// "supernew" : false, // true: Tolerate `new function () { ... };` and `new Object;`
// "validthis" : false, // true: Tolerate using this in a non-constructor function
//
// // Environments
// "browser" : true, // Web Browser (window, document, etc)
// "browserify" : false, // Browserify (node.js code in the browser)
// "couch" : false, // CouchDB
// "devel" : true, // Development/debugging (alert, confirm, etc)
// "dojo" : false, // Dojo Toolkit
// "jasmine" : false, // Jasmine
// "jquery" : false, // jQuery
// "mocha" : true, // Mocha
// "mootools" : false, // MooTools
"node" : true, // Node.js
// "nonstandard" : false, // Widely adopted globals (escape, unescape, etc)
// "prototypejs" : false, // Prototype and Scriptaculous
// "qunit" : false, // QUnit
// "rhino" : false, // Rhino
// "shelljs" : false, // ShellJS
// "worker" : false, // Web Workers
// "wsh" : false, // Windows Scripting Host
// "yui" : false, // Yahoo User Interface
// Custom predefined global variables
"predef": [
"-console", // disallows console, use debug
"beforeEach",
"afterEach",
"before",
"after",
"describe",
"it"
]
}

View File

@@ -1,81 +0,0 @@
jobs:
include:
- sudo: required
services:
- docker
language: generic
before_install: docker pull carto/nodejs6-xenial-pg101
script: npm run docker-test
- dist: precise
addons:
postgresql: "9.5"
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- pkg-config
- libcairo2-dev
- libjpeg8-dev
- libgif-dev
- libpango1.0-dev
- g++-4.9
- wget
before_install:
# Add custom PPAs from cartodb
- sudo add-apt-repository -y ppa:cartodb/postgresql-9.5
- sudo add-apt-repository -y ppa:cartodb/gis
- sudo add-apt-repository -y ppa:cartodb/gis-testing
- sudo apt-get update
# Force instalation of libgeos-3.5.0 (presumably needed because of existing version of postgis)
- sudo apt-get -y install libgeos-3.5.0=3.5.0-1cdb2
# Install postgres db and build deps
- sudo /etc/init.d/postgresql stop # stop travis default instance
- sudo apt-get -y remove --purge postgresql-9.1
- sudo apt-get -y remove --purge postgresql-9.2
- sudo apt-get -y remove --purge postgresql-9.3
- sudo apt-get -y remove --purge postgresql-9.4
- sudo apt-get -y remove --purge postgresql-9.5
- sudo apt-get -y remove --purge postgresql-9.6
- sudo rm -rf /var/lib/postgresql/
- sudo rm -rf /var/log/postgresql/
- sudo rm -rf /etc/postgresql/
- sudo apt-get -y remove --purge postgis-2.2
- sudo apt-get -y autoremove
- sudo apt-get -y install postgresql-9.5=9.5.2-3cdb3
- sudo apt-get -y install postgresql-server-dev-9.5=9.5.2-3cdb3
- sudo apt-get -y install postgresql-plpython-9.5=9.5.2-3cdb3
- sudo apt-get -y install postgresql-9.5-postgis-scripts=2.2.2.0-cdb2
- sudo apt-get -y install postgresql-9.5-postgis-2.2=2.2.2.0-cdb2
# configure it to accept local connections from postgres
- echo -e "# TYPE DATABASE USER ADDRESS METHOD \nlocal all postgres trust\nlocal all all trust\nhost all all 127.0.0.1/32 trust" \
| sudo tee /etc/postgresql/9.5/main/pg_hba.conf
- sudo /etc/init.d/postgresql restart 9.5
- createdb template_postgis
- createuser publicuser
- psql -c "CREATE EXTENSION postgis" template_postgis
# install yarn 0.27.5
- curl -o- -L https://yarnpkg.com/install.sh | bash -s -- --version 0.27.5
- export PATH="$HOME/.yarn/bin:$PATH"
# instal redis 4
- wget http://download.redis.io/releases/redis-4.0.8.tar.gz
- tar xvzf redis-4.0.8.tar.gz
- cd redis-4.0.8
- make
- sudo make install
- cd ..
- rm redis-4.0.8.tar.gz
env:
- NPROCS=1 JOBS=1 PGUSER=postgres CXX=g++-4.9
language: node_js
node_js:
- "6"

View File

@@ -1,11 +0,0 @@
Contributing
---
The issue tracker is at [github.com/CartoDB/Windshaft-cartodb](https://github.com/CartoDB/Windshaft-cartodb).
We love pull requests from everyone, see [Contributing to Open Source on GitHub](https://guides.github.com/activities/contributing-to-open-source/#contributing).
## Submitting Contributions
* You will need to sign a Contributor License Agreement (CLA) before making a submission. [Learn more here](https://carto.com/contributions).

View File

@@ -1,18 +0,0 @@
1. Test (make clean all check), fix if broken before proceeding
2. Ensure proper version in package.json
3. Ensure NEWS section exists for the new version, review it, add release date
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. Stub NEWS/package for next version
Versions:
Bugfix releases increment Patch component of version.
Feature releases increment Minor and set Patch to zero.
If backward compatibility is broken, increment Major and
set to zero Minor and Patch.
Branches named 'b<Major>.<Minor>' are kept for any critical
fix that might need to be shipped before next feature release
is ready.

16
HOW_TO_RELEASE.md Normal file
View File

@@ -0,0 +1,16 @@
# How to release
1. Test (npm test), fix if broken before proceeding.
2. Ensure proper version in `package.json` and `package-lock.json`.
3. Ensure NEWS section exists for the new version, review it, add release date.
4. If there are modified dependencies in `package.json`, update them with `npm upgrade {{package_name}}@{{version}}`.
5. Commit `package.json`, `package-lock.json`, NEWS.
6. Run `git tag -a Major.Minor.Patch`. Use NEWS section as content.
7. Stub NEWS/package for next version.
## Version:
* Bugfix releases increment Patch component of version.
* Feature releases increment Minor and set Patch to zero.
* If backward compatibility is broken, increment Major and set to zero Minor and Patch.
* Branches named 'b<Major>.<Minor>' are kept for any critical fix that might need to be shipped before next feature release is ready.

View File

@@ -1,53 +0,0 @@
# Installing Windshaft-CartoDB #
## Requirements ##
Make sure that you have the requirements needed. These are
- Core
- Node.js >=6.9.x
- yarn >=0.27.5 <1.0.0
- PostgreSQL >8.3.x, PostGIS >1.5.x
- Redis >2.4.0 (http://www.redis.io)
- 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
- For cache control (optional)
- CartoDB 0.9.5+ (for `CDB_QueryTables`)
- Varnish (http://www.varnish-cache.org)
On Ubuntu 14.04 the dependencies can be installed with
```shell
sudo apt-get update
sudo apt-get install -y make g++ pkg-config git-core \
libgif-dev libjpeg-dev libcairo2-dev \
libhiredis-dev redis-server \
nodejs nodejs-legacy npm \
postgresql-9.3-postgis-2.1 postgresql-plpython-9.3 postgresql-server-dev-9.3
```
On Ubuntu 12.04 the [cartodb/cairo PPA](https://launchpad.net/~cartodb/+archive/ubuntu/cairo) may be useful.
## PostGIS setup ##
A `template_postgis` database is expected. One can be set up with
```shell
createdb --owner postgres --template template0 template_postgis
psql -d template_postgis -c 'CREATE EXTENSION postgis;'
```
## Build/install ##
To fetch and build all node-based dependencies, run:
```
yarn
```
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
```yarn``` again.

21
LOGGING.md Normal file
View File

@@ -0,0 +1,21 @@
# Logging structured traces
In order to have meaningful and useful log traces, you should follow
some general guidelines described in the [Project Guidelines](http://doc-internal.cartodb.net/platform/guidelines.html#structured-logging).
In this project there is a specific logger in place that takes care of
format and context of the traces for you. Take a look at [logger.js](https://github.com/CartoDB/Windshaft-cartodb/blob/cf82e1954e2244861e47fce0c2223ee466a5cd64/lib/utils/logger.js)
(NOTE: that file will be moved soon to a common module).
The logger is instantiated as part of the [app startup process](https://github.com/CartoDB/Windshaft-cartodb/blob/cf82e1954e2244861e47fce0c2223ee466a5cd64/app.js#L53),
then passed to middlewares and other client classes.
There are many examples of how to use the logger to generate traces
throughout the code. Here are a few of them:
```js
lib/api/middlewares/logger.js: res.locals.logger.info({ client_request: req }, 'Incoming request');
lib/api/middlewares/logger.js: res.on('finish', () => res.locals.logger.info({ server_response: res, status: res.statusCode }, 'Response sent'));
lib/api/middlewares/profiler.js: logger.info({ stats, duration: stats.response / 1000, duration_ms: stats.response }, 'Request profiling stats');
lib/api/middlewares/tag.js: res.on('finish', () => logger.info({ tags: res.locals.tags }, 'Request tagged'));
```

View File

@@ -1,53 +0,0 @@
SHELL=/bin/bash
pre-install:
@$(SHELL) ./scripts/check-node-canvas.sh
all:
@$(SHELL) ./scripts/install.sh
clean:
rm -rf node_modules/
distclean: clean
rm config.status*
config.status--test:
./configure --environment=test
config/environments/test.js: config.status--test
./config.status--test
TEST_SUITE := $(shell find test/{acceptance,integration,unit} -name "*.js")
TEST_SUITE_UNIT := $(shell find test/unit -name "*.js")
TEST_SUITE_INTEGRATION := $(shell find test/integration -name "*.js")
TEST_SUITE_ACCEPTANCE := $(shell find test/acceptance -name "*.js")
test: config/environments/test.js
@echo "***tests***"
@$(SHELL) ./run_tests.sh ${RUNTESTFLAGS} $(TEST_SUITE)
test-unit: config/environments/test.js
@echo "***tests***"
@$(SHELL) ./run_tests.sh ${RUNTESTFLAGS} $(TEST_SUITE_UNIT)
test-integration: config/environments/test.js
@echo "***tests***"
@$(SHELL) ./run_tests.sh ${RUNTESTFLAGS} $(TEST_SUITE_INTEGRATION)
test-acceptance: config/environments/test.js
@echo "***tests***"
@$(SHELL) ./run_tests.sh ${RUNTESTFLAGS} $(TEST_SUITE_ACCEPTANCE)
jshint:
@echo "***jshint***"
@./node_modules/.bin/jshint lib/ test/ app.js
test-all: test jshint
coverage:
@RUNTESTFLAGS=--with-coverage make test
check: test
.PHONY: pre-install test jshint coverage

221
NEWS.md
View File

@@ -1,5 +1,226 @@
# Changelog
## 10.0.0
Released 2020-mm-dd
Breaking changes:
- Log system revamp:
- Logs to stdout, disabled while testing
- Upgrade `camshaft` to version [`0.67.2`](https://github.com/CartoDB/camshaft/releases/tag/0.67.2)
- Use header `X-Request-Id`, or create a new `uuid` when no present, to identyfy log entries
- Be able to set log level from env variable `LOG_LEVEL`, useful while testing: `LOG_LEVEL=info npm test`; even more human-readable: `LOG_LEVEL=info npm t | ./node_modules/.bin/pino-pretty`
- Stop responding with `X-Tiler-Errors` header. Now errors are properly logged and will end up in ELK as usual.
- Stop responding with `X-Tiler-Profiler` header. Now profiling stats are properly logged and will end up in ELK as usual.
- Be able to reduce the footprint in the final log file depending on the environment
- Be able to pass the logger to the analysis creation (camshaft) while instantiating a named map with analysis.
- Be able to tag requests with labels as an easier way to provide business metrics
- Metro: Add log-collector utility (`metro`), it will be moved to its own repository. Attaching it here fro development purposes. Try it with the following command `LOG_LEVEL=info npm t | node metro`
- Metro: Creates `metrics-collector.js` a stream to update Prometheus' counters and histograms and exposes them via Express' app (`:9145/metrics`). Use the ones defined in `grok_exporter`
Bug Fixes:
- While instantiating a map, set the `cache buster` equal to `0` when there are no affected tables in the MapConfig. Thus `layergroupid` has the same structure always:
- `${map_id}:${cache_buster}` for anonymous map
- `${user}@${template_hash}@${map_id}:${cache_buster}` for named map
## 9.0.0
Released 2020-06-05
Breaking changes:
- Remove `/version` endpoint
- Drop support for Node.js < 12
Announcements:
- Support Node.js 12
- Upgrade `windshaft` to version [`7.0.1`](https://github.com/CartoDB/Windshaft/releases/tag/7.0.1)
- Upgrade `camshaft` to version [`0.65.3`](https://github.com/CartoDB/camshaft/blob/0.65.3/CHANGELOG.md#0653):
- Fix noisy message logs while checking analyses' limits
- Fix CI setup, explicit use of PGPORT while creating the PostgreSQL cluster
- Upgrade `cartodb-redis` to version [`3.0.0`](https://github.com/CartoDB/node-cartodb-redis/releases/tag/3.0.0)
- Fix test where `http-fallback-image` renderer was failing quietly
- Fix stat `named map providers` cache count
- Use new signature for `onTileErrorStrategy`. Required by `windshaft@6.0.0`
- Extract `onTileErrorStrategy` to a module
- In tests, stop using mapnik module exposed by windshaft and require it from development dependencies
- Stop using `MapStore` from `windshaft` while testing and create a custom one instead
- Rename NamedMapProviderReporter by NamedMapProviderCacheReporter
- Remove `bootstrapFonts` at process startup (now done in `windshaft@6.0.0`)
- Stop checking the installed version of some dependencies while testing
- Send metrics about `map views` (#1162)
- Add custom headers in responses to allow to other components to be able to get insights about user activity
- Update dependencies to avoid security vulnerabilities
Bug Fixes:
- Parsing date column in numeric histograms (#1160)
- Use `Array.prototype.sort()`'s callback properly while testing. It should return a number not a boolean.
## 8.1.1
Released 2020-02-17
Announcements:
- Upgrade camshaft to [`0.65.2`](https://github.com/CartoDB/camshaft/blob/69c9447c9fccf00a70a67d713d1ce777775a17ff/CHANGELOG.md#0652): Fixes uncatched errors problem (#1117)
## 8.1.0
Released 2020-01-27
Announcements:
- Removed `jshint` as linter in favour of `eslint` to check syntax, find problems, and enforce code style.
- Upgrade `camshaft` to [`0.65.1`](https://github.com/CartoDB/camshaft/blob/a2836c15fd2830f8364a222eeafdb4dc2f41b580/CHANGELOG.md#0651): Use quoted identifiers for column names and enforce the usage of the cartodb schema when using cartodb extension functions and tables.
- Stop using two different tools for package management, testing, and any other developer workflow.
- Removes Makefile and related bash scripts
- Use npm scripts as the only tool for testing, CI and linting.
- Simplified CI configuration.
- Improved documentation:
- Centralized several documents into README.md
- Remove outdated sections
- Update old sections
- Added missing sections.
- Remove deprecated coverage tool istanbul, using nyc instead.
- Removed unused dockerfiles
- Use cartodb schema when using cartodb extension functions and tables.
- Implemented circle and polygon dataview filters.
## 8.0.0
Released 2019-11-13
Breaking changes:
- Schema change for "routes" in configuration file, each "router" is now an array instead of an object. See [`dd06de2`](https://github.com/CartoDB/Windshaft-cartodb/pull/1126/commits/dd06de2632661e19d64c9fbc2be0ba1a8059f54c) for more details.
Announcements:
- Added validation to only allow "count" and "sum" aggregations in dataview overview.
- Added mechanism to inject custom middlewares through configuration.
- Stop requiring unused config properties: "base_url", "base_url_mapconfig", and "base_url_templated".
- Upgraded cartodb-query-tables to version [0.7.0](https://github.com/CartoDB/node-cartodb-query-tables/blob/0.7.0/NEWS.md#version-0.7.0).
- Be able to set a coherent TTL in Cache-Control header to expire all resources belonging to a map simultaneously.
- When `cache buster` in request path is `0` set header `Last-Modified` to now, it avoids stalled content in 3rd party cache providers when they add `If-Modified-Since` header into the request.
- Adding a logger to MapStore (#1134)
- Qualify calls to cartodb extension so having it in the search_path isn't necessary.
- Fix multiple DB login issues.
## 7.2.0
Released 2019-09-30
Announcements:
- Stop caching map template errors in Named Map Provider Cache
- Gather metrics from Named Maps Providers Cache
- Improved efficiency of query samples while instatiating a map (#1120).
- Cache control header fine tuning. Set a shorter value for "max-age" directive if there is no way to know when to trigger the invalidation.
- Update deps:
- Update `cartodb-query-tables` to version [`0.6.3`](https://github.com/CartoDB/node-cartodb-query-tables/blob/0.6.3/NEWS.md#version-063).
- Update `cartodb-psql` to [`0.14.0`](https://github.com/CartoDB/node-cartodb-psql/blob/0.14.0/NEWS.md#version-0140-2019-09-10)
- Upgrade `windshaft` to [`5.6.3`](https://github.com/CartoDB/Windshaft/blob/master/NEWS.md#version-563):
- Upgrade grainstore to [`2.0.1`](https://github.com/CartoDB/grainstore/releases/tag/2.0.1)
- Update @carto/mapnik to [`3.6.2-carto.16`](https://github.com/CartoDB/node-mapnik/blob/v3.6.2-carto.16/CHANGELOG.carto.md#362-carto16).
- Update turbo-carto to [`0.21.2`](https://github.com/CartoDB/turbo-carto/releases/tag/0.21.2)
- Upgrade `@carto/cartonik` to version [`0.7.0`](https://github.com/CartoDB/cartonik/blob/v0.7.0/CHANGELOG.md#cartonik-changelog).
- Upgrade `camshaft` to [`0.64.2`](https://github.com/CartoDB/camshaft/blob/8b89fcff276da20a71269bed28b7ad6704392898/CHANGELOG.md#0642) to update dependencies.
## 7.1.0
Released 2019-05-06
Announcements:
- Fix uncaught exception: TypeError: Cannot read property 'id' of undefined
- Implements graceful shutdown for:
- system signals `SIGINT` and `SIGTERM`
- events `uncaughtException`, `unhandledRejection` and, `ENOMEM`
- Experimental support for listing features in a grid when the map uses the dynamic agregation.
- Numeric histogram performance improvement (#1080)
- Fix boolean aggregation layer option not working when numbers of rows are above the threshold (#1082)
- Update deps:
- camshat@0.64.0
- windshaft@5.2.0:
- Use [`@carto/cartonik`](https://github.com/CartoDB/cartonik/releases/tag/v0.5.0) instead of `@mapbox/tilelive` to fetch raster/vertor tiles.
- Upgrade `grainstore` to version `2.0.0`
- Upgrade `torque.js` to version `3.1.0`
- Upgrade `canvas` to version `2.4.1`
- Update @carto/mapnik to [`3.6.2-carto.13`](https://github.com/CartoDB/node-mapnik/blob/v3.6.2-carto.13/CHANGELOG.carto.md#362-carto13).
## 7.0.0
Released 2019-02-22
Breaking changes:
- Drop support for Node.js 6
- Drop support for npm 3
- Stop supporting `yarn.lock`
- Drop support for Postgres 9.5
- Drop support for PosGIS 2.2
- Drop support for Redis 3
Announcements:
- In configuration, set `clipByBox2d` to true by default
- Update docs: compatible Node.js and npm versions
- Report fine-grained Garbage Collector stats
- Adding Authorization to Access-Control-Allow-Headers (https://github.com/CartoDB/CartoDB-SQL-API/issues/534)
- Update deps:
- windshaft@4.13.1: Upgrade tilelive-mapnik to version 0.6.18-cdb18
- camshaft@0.63.4: Improve error message for exceeded batch SQL API payload size: add suggestions about what the user can do about it.
- Update dev deps:
- jshint@2.9.7
- mocha@5.2.0
- Be able to customize max waiting workers parameter
- Handle 'max waitingClients count exceeded' error as "429, You are over platform's limits"
## 6.5.1
Released 2018-12-26
Bug Fixes:
- Update carto-package.json
## 6.5.0
Released 2018-12-26
New features
- Suport Node.js 10
- Configure travis to run docker tests against Node.js 6 & 10 versions
- Aggregation time dimensions
- Update sample configurations to use PostGIS to generate MVT's by default (as in production)
- Upgrades Windshaft to [4.12.1](https://github.com/CartoDB/Windshaft/blob/4.12.1/NEWS.md#version-4121)
- `pg-mvt`: Use `query-rewriter` to compose the query to render a MVT tile. If not defined, it will use a Default Query Rewriter.
- `pg-mvt`: Fix bug while building query and there is no columns defined for the layer.
- `pg-mvt`: Accept trailing semicolon in input queries.
- `Renderer Cache Entry`: Do not throw errors for integrity checks.
- Fix bug when releasing the renderer cache entry in some scenarios.
- Upgrade grainstore to [1.10.0](https://github.com/CartoDB/grainstore/releases/tag/1.10.0)
- Upgrade cartodb-redis to [2.1.0](https://github.com/CartoDB/node-cartodb-redis/releases/tag/2.1.0)
- Upgrade cartodb-query-tables to [0.4.0](https://github.com/CartoDB/node-cartodb-query-tables/releases/tag/0.4.0)
- Upgrade cartodb-psql to [0.13.1](https://github.com/CartoDB/node-cartodb-psql/releases/tag/0.13.1)
- Upgrade turbo-carto to [0.21.0](https://github.com/CartoDB/turbo-carto/releases/tag/0.21.0)
- Upgrade camshaft to [0.63.1](https://github.com/CartoDB/camshaft/releases/tag/0.63.1)
- Upgrade redis-mpool to [0.7.0](https://github.com/CartoDB/node-redis-mpool/releases/tag/0.7.0)
Bug Fixes:
- Prevent from uncaught exception: Range filter Error from camshaft when getting analysis query.
- Make all modules to use strict mode semantics.
## 6.4.0
Released 2018-09-24
- Upgrades Camshaft to [0.62.3](https://github.com/CartoDB/camshaft/releases/tag/0.61.11):
- Build query from node's cache to compute output columns when building analysis
- Adds metadata columns for street level geocoding
- Remove use of `step` module to handle asynchronous code, now it's defined as development dependency.
- Bug Fixes: (#1020)
- Fix bug in date-wrapper regarding columns with spaces
- Fix bug in aggregation-query regarding columns with spaces
- Upgrades Windshaft to [4.10.0](https://github.com/CartoDB/Windshaft/blob/4.10.0/NEWS.md#version-4100)
- `pg-mvt`:
- Now matches the behaviour of the `mapnik` renderer for MVTs.
- Removed undocummented filtering by `layer.options.columns`.
- Implement timeout in getTile.
- Several bugfixes.
- Dependency updates: Fixed a bug in Mapnik MVT renderer and cleanup in `tilelive-mapnik`.
- [MapConfig 1.8.0 released](https://github.com/CartoDB/Windshaft/blob/master/doc/MapConfig-1.8.0.md) with new options for MVTs:
- Add **`vector_extent`** option in MapConfig to setup the layer extent.
- Add **`vector_simplify_extent`** option in MapConfig to configure the simplification process.
- Remove use of `step` module to handle asynchronous code, now it's defined as development dependency.
## 6.3.0
Released 2018-07-26
- Upgrades Camshaft to [0.62.1](https://github.com/CartoDB/camshaft/releases/tag/0.62.1):
- Support for batch street-level geocoding. [0.62.1](https://github.com/CartoDB/camshaft/releases/tag/0.62.1)
## 6.2.0
Released 2018-07-20

204
README.md
View File

@@ -1,82 +1,146 @@
Windshaft-CartoDB
==================
# Windshaft-CartoDB [![Build Status](https://travis-ci.org/CartoDB/Windshaft-cartodb.svg?branch=master)](https://travis-ci.org/CartoDB/Windshaft-cartodb)
[![Build Status](https://travis-ci.org/CartoDB/Windshaft-cartodb.svg?branch=master)](https://travis-ci.org/CartoDB/Windshaft-cartodb)
The [`CARTO Maps API`](https://carto.com/developers/maps-api/) tiler. It extends [`Windshaft`](https://github.com/CartoDB/Windshaft) and exposes a web service with extra functionality:
This is the [CartoDB Maps API](http://docs.cartodb.com/cartodb-platform/maps-api.html) tiler. It extends
[Windshaft](https://github.com/CartoDB/Windshaft) with some extra functionality and custom filters for authentication.
* Instantiate [`Anonymous Maps`](https://github.com/CartoDB/Windshaft-cartodb/blob/master/docs/guides/03-anonymous-maps.md) through CARTO's map configuration ([`MapConfig`](https://github.com/CartoDB/Windshaft/blob/master/doc/MapConfig-specification.md)).
* Create [`Named Maps`](https://github.com/CartoDB/Windshaft-cartodb/blob/master/docs/guides/04-named-maps.md) based on customizable templates.
* Get map previews through [`Static Maps`](https://github.com/CartoDB/Windshaft-cartodb/blob/master/docs/guides/05-static-maps-API.md) API.
* Render maps with a large amount of data faster using [`Tile Aggregation`](https://github.com/CartoDB/Windshaft-cartodb/blob/master/docs/guides/06-tile-aggregation.md).
* Build advanced maps with enriched data through [`Analyses Extension`](https://github.com/CartoDB/Windshaft-cartodb/blob/master/docs/guides/09-MapConfig-analyses-extension.md).
* Fetch tabular data from analysis nodes with [`Dataviews`](https://github.com/CartoDB/Windshaft-cartodb/blob/master/docs/guides/10-MapConfig-dataviews-extension.md)
* reads dbname from subdomain and cartodb redis for pretty tile urls
* configures windshaft to publish `cartodb_id` as the interactivity layer
* gets the default geometry type from the cartodb redis store
* allows tiles to be styled individually
* provides a link to varnish high speed cache
* provides a [template maps API](https://github.com/CartoDB/Windshaft-cartodb/blob/master/docs/Template-maps.md)
## Build
Install
-------
See [INSTALL.md](INSTALL.md) for detailed installation instructions.
Requirements:
Configure
---------
* [`Node 12.x `](https://nodejs.org/dist/latest-v10.x/)
* [`PostgreSQL >= 11.0`](https://www.postgresql.org/download/)
* [`PostGIS >= 2.4`](https://postgis.net/install/)
* [`CARTO Postgres Extension >= 0.24.1`](https://github.com/CartoDB/cartodb-postgresql)
* [`Redis >= 4`](https://redis.io/download)
* `libcairo2-dev`, `libpango1.0-dev`, `libjpeg8-dev` and `libgif-dev` for server side canvas support
* `C++11` to build internal dependencies. When there's no pre-built binaries for your OS/architecture distribution.
Create the config/environments/<env>.js files (there are .example files
to start from). You can optionally use the ./configure script for this,
see ```./configure --help``` to see available options.
Optional:
Look at lib/cartodb/server_options.js for more on config
* [`Varnish`](http://www.varnish-cache.org)
* [`Statsd`](https://github.com/statsd/statsd)
Upgrading
---------
### PostGIS setup
Checkout your commit/branch. If you need to reinstall dependencies (you can check [NEWS](NEWS.md)) do the following:
```
rm -rf node_modules; yarn
```
Run
---
```
node app.js <env>
```
Where <env> is the name of a configuration file under config/environments/.
Note that caches are kept in redis. If you're not seeing what you expect
there may be out-of-sync records in there.
Take a look: http://redis.io/commands
Documentation
-------------
The [docs directory](https://github.com/CartoDB/Windshaft-cartodb/tree/master/docs) contains different documentation
resources, from higher level to more detailed ones:
The [Maps API](https://github.com/CartoDB/Windshaft-cartodb/blob/master/docs/Map-API.md) defined the endpoints and their
expected parameters and outputs.
Examples
--------
[CartoDB's Map Gallery](http://cartodb.com/gallery/) showcases several examples of visualisations built on top of this.
Contributing
---
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 `yarn link`. You can read more about it at [yarn-link: Symlink a package folder](https://yarnpkg.com/en/docs/cli/link).
**Quick start**:
A `template_postgis` database is expected. One can be set up with
```shell
~/windshaft-directory $ yarn
~/windshaft-directory $ yarn link
~/windshaft-cartodb-directory $ yarn link windshaft
$ createdb --owner postgres --template template0 template_postgis
$ psql -d template_postgis -c 'CREATE EXTENSION postgis;'
```
### Install
To fetch and build all node-based dependencies, run:
```shell
$ npm install
```
### Run
You can inject the configuration through environment variables at run time. Check the file `./config/environments/config.js` to see the ones you have available.
While the migration to the new environment based configuration, you can still use the old method of copying a config file. To enabled the one with environment variables you need to pass `CARTO_WINDSHAFT_ENV_BASED_CONF=true`. You can use the docker image to run it.
Old way:
```shell
$ node app.js <env>
```
Where `<env>` is the name of a configuration file under `./config/environments/`.
### Test
You can easily run the tests against the dependencies from the `dev-env`. To do so, you need to build the test docker image:
```shell
$ docker-compose build
```
Then you can run the tests like:
```shell
$ docker-compose run windshaft-tests
```
It will mount your code inside a volume. In case you want to play and run `npm test` or something else you can do:
```shell
$ docker-compose run --entrypoint bash windshaft-tests
```
So you will have a bash shell inside the test container, with the code from your host.
### Coverage
```shell
$ npm run cover
```
Open `./coverage/lcov-report/index.html`.
### Docker support
We provide docker images just for testing and continuous integration purposes:
* [`nodejs-xenial-pg1121`](https://hub.docker.com/r/carto/nodejs-xenial-pg1121/tags)
* [`nodejs-xenial-pg101`](https://hub.docker.com/r/carto/nodejs-xenial-pg101/tags)
You can find instructions to install Docker, download, and update images [here](https://github.com/CartoDB/Windshaft-cartodb/blob/master/docker/reference.md).
### Useful `npm` scripts
Run test in a docker image with a specific Node.js version:
```shell
$ DOCKER_IMAGE=<docker-image-tag> NODE_VERSION=<nodejs-version> npm run test:docker
```
Where:
* `<docker-image-tag>`: the tag of required docker image, e.g. `carto/nodejs-xenial-pg1121:latest`
* `<nodejs-version>`: the Node.js version, e.g. `10.15.1`
In case you need to debug:
```shell
$ DOCKER_IMAGE=<docker-image-tag> npm run docker:bash
```
## Documentation
You can find an overview, guides, full reference, and support in [`CARTO's developer center`](https://carto.com/developers/maps-api/). The [docs directory](https://github.com/CartoDB/Windshaft-cartodb/tree/master/docs) contains different documentation resources, from a higher level to more detailed ones.
## Contributing
* The issue tracker: [`Github`](https://github.com/CartoDB/Windshaft-cartodb/issues).
* We love Pull Requests from everyone, see [contributing to Open Source on GitHub](https://guides.github.com/activities/contributing-to-open-source/#contributing).
* You'll need to sign a Contributor License Agreement (CLA) before submitting a Pull Request. [Learn more here](https://carto.com/contributions).
## 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.html).
```shell
$ cd /path/to/Windshaft
$ npm install
$ npm link
$ cd /path/to/Windshaft-cartodb
$ npm link windshaft
```
## Versioning
We follow [`SemVer`](http://semver.org/) for versioning. For available versions, see the [tags on this repository](https://github.com/CartoDB/Windshaft-cartodb/tags).
## License
This project is licensed under the BSD 3-clause "New" or "Revised" License. See the [LICENSE](LICENSE) file for details.

280
app.js
View File

@@ -1,160 +1,220 @@
var http = require('http');
var https = require('https');
var path = require('path');
var fs = require('fs');
var _ = require('underscore');
var semver = require('semver');
const setICUEnvVariable = require('./lib/cartodb/utils/icu_data_env_setter');
'use strict';
// jshint undef:false
var log = console.log.bind(console);
var logError = console.error.bind(console);
// jshint undef:true
const http = require('http');
const https = require('https');
const path = require('path');
const semver = require('semver');
var nodejsVersion = process.versions.node;
if (!semver.satisfies(nodejsVersion, '>=6.9.0')) {
logError(`Node version ${nodejsVersion} is not supported, please use Node.js 6.9 or higher.`);
process.exit(1);
}
// TODO: research it it's still needed
const setICUEnvVariable = require('./lib/utils/icu-data-env-setter');
// This function should be called before the require('yargs').
setICUEnvVariable();
var argv = require('yargs')
.usage('Usage: $0 <environment> [options]')
const argv = require('yargs')
.usage('Usage: node $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'
)
'node $0 production -c /etc/windshaft-cartodb/config.js',
'start server in production environment with /etc/windshaft-cartodb/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);
const environmentArg = argv._[0] || process.env.NODE_ENV || 'development';
let configFileName = environmentArg;
if (process.env.CARTO_WINDSHAFT_ENV_BASED_CONF) {
// we override the file with the one with env vars
configFileName = 'config';
}
const configurationFile = path.resolve(argv.config || `./config/environments/${configFileName}.js`);
global.environment = require(configurationFile);
var ENVIRONMENT = argv._[0] || process.env.NODE_ENV || global.environment.environment;
process.env.NODE_ENV = ENVIRONMENT;
process.env.NODE_ENV = argv._[0] || process.env.NODE_ENV || global.environment.environment;
var availableEnvironments = {
production: true,
staging: true,
development: true
};
// sanity check
if (!availableEnvironments[ENVIRONMENT]){
logError('node app.js [environment]');
logError('environments: %s', Object.keys(availableEnvironments).join(', '));
process.exit(1);
}
process.env.NODE_ENV = ENVIRONMENT;
if (global.environment.uv_threadpool_size) {
process.env.UV_THREADPOOL_SIZE = global.environment.uv_threadpool_size;
}
// set global HTTP and HTTPS agent default configurations
// ref https://nodejs.org/api/http.html#http_new_agent_options
var agentOptions = _.defaults(global.environment.httpAgent || {}, {
const agentOptions = Object.assign({
keepAlive: false,
keepAliveMsecs: 1000,
maxSockets: Infinity,
maxFreeSockets: 256
});
}, 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 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 {
log4jsConfig.appenders.push(
{ type: "console", layout: { type:'basic' } }
);
}
global.log4js.configure(log4jsConfig);
global.logger = global.log4js.getLogger();
// Include cartodb_windshaft only _after_ the "global" variable is set
// See https://github.com/Vizzuality/Windshaft-cartodb/issues/28
var cartodbWindshaft = require('./lib/cartodb/server');
var serverOptions = require('./lib/cartodb/server_options');
const createServer = require('./lib/server');
const serverOptions = require('./lib/server-options');
const { logger } = serverOptions;
var server = cartodbWindshaft(serverOptions);
const availableEnvironments = {
production: true,
staging: true,
development: true
};
// Maximum number of connections for one process
// 128 is a good number if you have up to 1024 filedescriptors
// 4 is good if you have max 32 filedescriptors
// 1 is good if you have max 16 filedescriptors
var backlog = global.environment.maxConnections || 128;
if (!availableEnvironments[process.env.NODE_ENV]) {
logger.fatal(new Error(`Invalid environment ${process.env.NODE_ENV} argument, valid ones: ${Object.keys(availableEnvironments).join(', ')}`));
process.exit(1);
}
var listener = server.listen(serverOptions.bind.port, serverOptions.bind.host, backlog);
const { engines } = require('./package.json');
if (!semver.satisfies(process.versions.node, engines.node)) {
logger.fatal(new Error(`Node version ${process.versions.node} is not supported, please use Node.js ${engines.node}.`));
process.exit(1);
}
var version = require("./package").version;
const server = createServer(serverOptions);
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
);
// Specify the maximum length of the queue of pending connections for the HTTP server.
// The actual length will be determined by the OS through sysctl settings such as tcp_max_syn_backlog and somaxconn on Linux.
// The default value of this parameter is 511 (not 512).
// See: https://nodejs.org/docs/latest/api/net.html#net_server_listen
const backlog = global.environment.maxConnections || 128;
const listener = server.listen(serverOptions.bind.port, serverOptions.bind.host, backlog);
const { version, name } = require('./package');
listener.on('listening', function () {
const { address, port } = listener.address();
logger.info({ 'Node.js': process.version, pid: process.pid, environment: process.env.NODE_ENV, [name]: version, address, port, config: configurationFile }, `${name} initialized successfully`);
});
setInterval(function() {
function getCPUUsage (oldUsage) {
let usage;
if (oldUsage && oldUsage._start) {
usage = Object.assign({}, process.cpuUsage(oldUsage._start.cpuUsage));
usage.time = Date.now() - oldUsage._start.time;
} else {
usage = Object.assign({}, process.cpuUsage());
usage.time = process.uptime() * 1000; // s to ms
}
usage.percent = (usage.system + usage.user) / (usage.time * 10);
Object.defineProperty(usage, '_start', {
value: {
cpuUsage: process.cpuUsage(),
time: Date.now()
}
});
return usage;
}
let previousCPUUsage = getCPUUsage();
setInterval(function cpuUsageMetrics () {
const CPUUsage = getCPUUsage(previousCPUUsage);
Object.keys(CPUUsage).forEach(property => {
global.statsClient.gauge(`windshaft.cpu.${property}`, CPUUsage[property]);
});
previousCPUUsage = CPUUsage;
}, 5000).unref();
setInterval(function () {
var memoryUsage = process.memoryUsage();
Object.keys(memoryUsage).forEach(function(k) {
Object.keys(memoryUsage).forEach(function (k) {
global.statsClient.gauge('windshaft.memory.' + k, memoryUsage[k]);
});
}, 5000);
process.on('SIGHUP', function() {
global.log4js.clearAndShutdownAppenders(function() {
global.log4js.configure(log4jsConfig);
global.logger = global.log4js.getLogger();
log('Log files reloaded');
});
});
process.on('uncaughtException', function(err) {
global.logger.error('Uncaught exception: ' + err.stack);
});
}, 5000).unref();
if (global.gc) {
var gcInterval = Number.isFinite(global.environment.gc_interval) ?
global.environment.gc_interval :
10000;
var gcInterval = Number.isFinite(global.environment.gc_interval)
? global.environment.gc_interval
: 10000;
if (gcInterval > 0) {
setInterval(function gcForcedCycle() {
var start = Date.now();
setInterval(function gcForcedCycle () {
global.gc();
global.statsClient.timing('windshaft.gc', Date.now() - start);
}, gcInterval);
}, gcInterval).unref();
}
}
const gcStats = require('gc-stats')();
gcStats.on('stats', function ({ pauseMS, gctype }) {
global.statsClient.timing('windshaft.gc', pauseMS);
global.statsClient.timing(`windshaft.gctype.${getGCTypeValue(gctype)}`, pauseMS);
});
function getGCTypeValue (type) {
// 1: Scavenge (minor GC)
// 2: Mark/Sweep/Compact (major GC)
// 4: Incremental marking
// 8: Weak/Phantom callback processing
// 15: All
let value;
switch (type) {
case 1:
value = 'Scavenge';
break;
case 2:
value = 'MarkSweepCompact';
break;
case 4:
value = 'IncrementalMarking';
break;
case 8:
value = 'ProcessWeakCallbacks';
break;
case 15:
value = 'All';
break;
default:
value = 'Unkown';
break;
}
return value;
}
const exitProcess = logger.finish((err, finalLogger, listener, signal, killTimeout) => {
scheduleForcedExit(killTimeout, finalLogger);
finalLogger.info(`Process has received signal: ${signal}`);
let code = 0;
if (err) {
code = 1;
finalLogger.fatal(err);
}
finalLogger.info(`Process is going to exit with code: ${code}`);
listener.close(() => process.exit(code));
});
function addHandlers (listener, killTimeout) {
process.on('uncaughtException', (err) => exitProcess(err, listener, 'uncaughtException', killTimeout));
process.on('unhandledRejection', (err) => exitProcess(err, listener, 'unhandledRejection', killTimeout));
process.on('ENOMEM', (err) => exitProcess(err, listener, 'ENOMEM', killTimeout));
process.on('SIGINT', () => exitProcess(null, listener, 'SIGINT', killTimeout));
process.on('SIGTERM', () => exitProcess(null, listener, 'SIGTERM', killTimeout));
}
addHandlers(listener, 45000);
function scheduleForcedExit (killTimeout, finalLogger) {
// Schedule exit if there is still ongoing work to deal with
const killTimer = setTimeout(() => {
finalLogger.info('Process didn\'t close on time. Force exit');
process.exit(1);
}, killTimeout);
// Don't keep the process open just for this
killTimer.unref();
}

17
carto-package.json Normal file
View File

@@ -0,0 +1,17 @@
{
"name": "carto_windshaft",
"current_version": {
"requires": {
"node": "^12.16.3",
"npm": "^6.14.4",
"mapnik": "==3.0.15.16",
"crankshaft": "~0.8.1"
},
"works_with": {
"redis": ">=4.0.0",
"postgresql": ">=10.0.0",
"postgis": ">=2.4.4.5",
"carto_postgresql_ext": ">=0.35.0"
}
}
}

View File

@@ -0,0 +1,411 @@
var config = {
environment: process.env.CARTO_WINDSHAFT_NODE_ENV,
port: 8181,
host: null, // null on purpouse so it listens to whatever address docker assigns
// Size of the threadpool which can be used to run user code and get notified in the loop thread
// 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: process.env.CARTO_WINDSHAFT_USER_FROM_HOST || '^(.*)\\.cartodb\\.com$',
// Base URLs for the APIs
//
// See https://github.com/CartoDB/Windshaft-cartodb/wiki/Unified-Map-API
//
// Note: each entry corresponds with an express' router.
// You must define at least one path. However, middlewares are optional.
routes: {
api: [{
paths: [
'/api/v1',
'/user/:user/api/v1'
],
// Optional: attach middlewares at the begining of the router
// to perform custom operations.
middlewares: [
function noop () {
return function noopMiddleware (req, res, next) {
next();
};
}
],
// Base url for the Detached Maps API
// "/api/v1/map" is the new API,
map: [{
paths: [
'/map'
],
middlewares: [] // Optional
}],
// Base url for the Templated Maps API
// "/api/v1/map/named" is the new API,
template: [{
paths: [
'/map/named'
],
middlewares: [] // Optional
}]
}]
},
// Resource URLs expose endpoints to request/retrieve metadata associated to Maps: dataviews, analysis node status.
//
// This URLs depend on how `routes` 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 `routes`.
// 3. {{=it.port}}: will use the `port` from this very same configuration file.
resources_url_templates: {
http: process.env.CARTO_WINDSHAFT_RESOURCE_URL_TEMPLATE_HTTP || 'http://{{=it.cdn_url}}/{{=it.user}}/api/v1/map',
https: process.env.CARTO_WINDSHAFT_RESOURCE_URL_TEMPLATE_HTTPS || 'https://{{=it.cdn_url}}/{{=it.user}}/api/v1/map'
},
// Specify the maximum length of the queue of pending connections for the HTTP server.
// The actual length will be determined by the OS through sysctl settings such as tcp_max_syn_backlog and somaxconn on Linux.
// The default value of this parameter is 511 (not 512).
// See: https://nodejs.org/docs/latest/api/net.html#net_server_listen
maxConnections: 128,
// Maximum number of templates per user. Unlimited by default.
maxUserTemplates: 1024,
// Seconds since "last creation" before a detached
// or template instance map expires. Or: how long do you want
// to be able to navigate the map without a reload ?
// Defaults to 7200 (2 hours)
mapConfigTTL: 7200,
// idle socket timeout, in milliseconds
socket_timeout: 600000,
enable_cors: true,
cache_enabled: true,
// Templated database username for authorized user
// Supported labels: 'user_id' (read from redis)
postgres_auth_user: process.env.CARTO_WINDSHAFT_DB_USER || 'cartodb_user_<%= user_id %>',
// Templated database password for authorized user
// Supported labels: 'user_id', 'user_password' (both read from redis)
postgres_auth_pass: '<%= user_password %>',
postgres: {
user: 'publicuser',
password: 'public',
host: process.env.CARTO_WINDSHAFT_POSTGRES_HOST || 'localhost',
port: process.env.CARTO_WINDSHAFT_POSTGRES_PORT || 5432,
pool: {
// maximum number of resources to create at any given time
size: 16,
// max milliseconds a resource can go unused before it should be destroyed
idleTimeout: 3000,
// frequency to check for idle resources
reapInterval: 1000
}
},
mapnik_version: undefined,
mapnik_tile_format: 'png8:m=h',
statsd: {
host: process.env.CARTO_WINDSHAFT_STATSD_HOST || 'localhost',
port: 8125,
prefix: process.env.CARTO_WINDSHAFT_STATSD_PREFIX || ':host.', // could be hostname, better not containing dots
cacheDns: true
// support all allowed node-statsd options
},
renderer: {
// Milliseconds since last access before renderer cache item expires
cache_ttl: 60000,
statsInterval: 5000, // milliseconds between each report to statsd about number of renderers and mapnik pool status
mvt: {
// If enabled, MVTs will be generated with PostGIS directly
// If disabled, MVTs will be generated with Mapnik MVT
usePostGIS: true
},
mapnik: {
// The size of the pool of internal mapnik backend
// This pool size is per mapnik renderer created in Windshaft's RendererFactory
// See https://github.com/CartoDB/Windshaft/blob/master/lib/windshaft/renderers/renderer_factory.js
// Important: check the configuration of uv_threadpool_size to use suitable value
poolSize: 8,
// The maximum number of waiting clients of the pool of internal mapnik backend
// This maximum number is per mapnik renderer created in Windshaft's RendererFactory
poolMaxWaitingClients: 64,
// 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
// wasted time.
metatile: 2,
// tilelive-mapnik uses an internal cache to store tiles/grids
// generated when using metatile. This options allow to tune
// the behaviour for that internal cache.
metatileCache: {
// Time an object must stay in the cache until is removed
ttl: 0,
// Whether an object must be removed after the first hit
// Usually you want to use `true` here when ttl>0.
deleteOnHit: false
},
// Override metatile behaviour depending on the format
formatMetatile: {
png: 2,
'grid.json': 1
},
// Buffer size is the tickness in pixel of a buffer
// around the rendered (meta?)tile.
//
// This is important for labels and other marker that overlap tile boundaries.
// Setting to 128 ensures no render artifacts.
// 64 may have artifacts but is faster.
// Less important if we can turn metatiling on.
bufferSize: 64,
// SQL queries will be wrapped with ST_SnapToGrid
// Snapping all points of the geometry to a regular grid
snapToGrid: false,
// SQL queries will be wrapped with ST_ClipByBox2D
// Returning the portion of a geometry falling within a rectangle
// It will only work if snapToGrid is enabled
clipByBox2d: true,
postgis: {
// Parameters to pass to datasource plugin of mapnik
// See http://github.com/mapnik/mapnik/wiki/PostGIS
user: 'publicuser',
password: 'public',
host: process.env.CARTO_WINDSHAFT_POSTGRES_HOST || '127.0.0.1',
port: process.env.CARTO_WINDSHAFT_POSTGRES_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,
/*
* Set persist_connection to false if you want
* database connections to be closed on renderer
* expiration (1 minute after last use).
* Setting to true (the default) would never
* close any connection for the server's lifetime
*/
persist_connection: false,
simplify_geometries: true,
use_overviews: true, // use overviews to retrieve raster
max_size: 500,
twkb_encoding: true
},
limits: {
// Time in milliseconds a render request can take before it fails, some notes:
// - 0 means no render limit
// - it considers metatiling, naive implementation: (render timeout) * (number of tiles in metatile)
render: 0,
// As the render request will finish even if timed out, whether it should be placed in the internal
// cache or it should be fully discarded. When placed in the internal cache another attempt to retrieve
// the same tile will result in an immediate response, however that will use a lot of more application
// memory. If we want to enforce this behaviour we have to implement a cache eviction policy for the
// internal cache.
cacheOnTimeout: true
},
// If enabled Mapnik will reuse the features retrieved from the database
// instead of requesting them once per style inside a layer
'cache-features': true,
// Require metrics to the renderer
metrics: false,
// Options for markers attributes, ellipses and images caches
markers_symbolizer_caches: {
disabled: false
}
},
http: {
timeout: 2000, // the timeout in ms for a http tile request
proxy: undefined, // the url for a proxy server
whitelist: [ // the whitelist of urlTemplates that can be used
'.*', // will enable any URL
'http://{s}.example.com/{z}/{x}/{y}.png'
],
// image to use as placeholder when urlTemplate is not in the whitelist
// if provided the http renderer will use it instead of throw an error
fallbackImage: {
type: 'fs', // 'fs' and 'url' supported
src: __dirname + '/../../assets/default-placeholder.png'
}
},
torque: {}
},
// anything analyses related
analysis: {
// batch configuration
batch: {
// Inline execution avoid the use of SQL API as batch endpoint
// When set to true it will run all analysis queries in series, with a direct connection to the DB
// This might be useful for:
// - testing
// - running an standalone server without any dependency on external services
inlineExecution: false,
// where the SQL API is running, it will use a custom Host header to specify the username.
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'
},
// 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: {
// Needs to be writable by server user
cache_basedir: process.env.CARTO_WINDSHAFT_TILE_CACHE || '/home/ubuntu/tile_assets/'
},
redis: {
host: process.env.CARTO_WINDSHAFT_REDIS_HOST || '127.0.0.1',
port: process.env.CARTO_WINDSHAFT_REDIS_PORT || 6379,
// Max number of connections in each pool.
// Users will be put on a queue when the limit is hit.
// Set to maxConnection to have no possible queues.
// There are currently 2 pools involved in serving
// windshaft-cartodb requests so multiply this number
// by 2 to know how many possible connections will be
// kept open by the servelsr. The default is 50.
max: 50,
returnToHead: true, // defines the behaviour of the pool: false => queue, true => stack
idleTimeoutMillis: 30000, // idle time before dropping connection
reapIntervalMillis: 1000, // time between cleanups
slowQueries: {
log: true,
elapsedThreshold: 200
},
slowPool: {
log: true, // whether a slow acquire must be logged or not
elapsedThreshold: 25 // the threshold to determine an slow acquire must be reported or not
},
emitter: {
statusInterval: 5000 // time, in ms, between each status report is emitted from the pool, status is sent to statsd
},
unwatchOnRelease: false, // Send unwatch on release, see http://github.com/CartoDB/Windshaft-cartodb/issues/161
noReadyCheck: true // Check `no_ready_check` at https://github.com/mranney/node_redis/tree/v0.12.1#overloading
},
// For more details about this options check https://nodejs.org/api/http.html#http_new_agent_options
httpAgent: {
keepAlive: true,
keepAliveMsecs: 1000,
maxSockets: 25,
maxFreeSockets: 256
},
varnish: {
host: process.env.CARTO_WINDSHAFT_VARNISH_PORT || 'localhost',
port: process.env.CARTO_WINDSHAFT_VARNISH_PORT || 6082, // the por for the telnet interface where varnish is listening to
http_port: 6081, // the port for the HTTP interface where varnish is listening to
purge_enabled: process.env.CARTO_WINDSHAFT_VARNISH_PURGE_ENABLED === 'true' || false, // whether the purge/invalidation mechanism is enabled in varnish or not
secret: 'xxx',
ttl: 86400,
fallbackTtl: 300,
layergroupTtl: 86400 // the max-age for cache-control header in layergroup responses
},
// this [OPTIONAL] configuration enables invalidating by surrogate key in fastly
fastly: {
// whether the invalidation is enabled or not
enabled: false,
// the fastly api key
apiKey: 'wadus_api_key',
// the service that will get surrogate key invalidation
serviceId: 'wadus_service_id'
},
// If useProfiler is true every response will be served with an
// X-Tiler-Profile header containing elapsed timing for various
// steps taken for producing the response.
useProfiler: false,
serverMetadata: {
cdn_url: {
http: process.env.CARTO_WINDSHAFT_SERVER_CDN_URL_HTTP === 'undefined' ? undefined : process.env.CARTO_WINDSHAFT_SERVER_CDN_URL_HTTP || 'api.cartocdn.com',
https: process.env.CARTO_WINDSHAFT_SERVER_CDN_URL_HTTPS === 'undefined' ? undefined : process.env.CARTO_WINDSHAFT_SERVER_CDN_URL_HTTPS || 'cartocdn.global.ssl.fastly.net'
}
},
// Settings for the health check available at /health
health: {
enabled: process.env.CARTO_WINDSHAFT_HEALTH_ENABLED === 'true' || false,
username: 'localhost',
z: 0,
x: 0,
y: 0
},
disabled_file: 'pids/disabled',
// Use this as a feature flags enabling/disabling mechanism
enabledFeatures: {
// whether it should intercept tile render errors an act based on them, enabled by default.
onTileErrorStrategy: false,
// 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
layerStats: process.env.CARTO_WINDSHAFT_LAYERSTATS_ENABLED === 'true' || false,
// whether it should rate limit endpoints (global configuration)
rateLimitsEnabled: false,
// whether it should rate limit one or more endpoints (only if rateLimitsEnabled = true)
rateLimitsByEndpoint: {
anonymous: false,
static: false,
static_named: false,
dataview: false,
dataview_search: false,
analysis: false,
analysis_catalog: false,
tile: false,
attributes: false,
named_list: false,
named_create: false,
named_get: false,
named: false,
named_update: false,
named_delete: false,
named_tiles: false
}
},
pubSubMetrics: {
enabled: process.env.CARTO_WINDSHAFT_METRICS_ENABLED === 'true' || false,
project_id: process.env.CARTO_WINDSHAFT_METRICS_PROJECT_ID || 'avid-wavelet-844',
credentials: '',
topic: process.env.CARTO_WINDSHAFT_METRICS_PROJECT_ID || 'raw-metric-events'
}
};
// override some defaults for tests
if (process.env.NODE_ENV === 'test') {
config.user_from_host = '(.*)';
config.postgres_auth_pass = 'test_windshaft_cartodb_user_<%= user_id %>_pass';
config.millstone.cache_basedir = '/tmp/tile_assets';
config.postgres.user = 'test_windshaft_publicuser';
config.resources_url_templates = {
http: 'http://{{=it.user}}.localhost.lan:{{=it.port}}/api/v1/map',
https: 'https://{{=it.user}}.localhost.lan:{{=it.port}}/api/v1/map'
};
config.cache_enabled = false;
config.postgres_auth_user = 'test_windshaft_cartodb_user_<%= user_id %>';
config.renderer.mapnik.postgis.twkb_encoding = false;
config.renderer.mapnik['cache-features'] = false;
config.renderer.http.whitelist = [ // the whitelist of urlTemplates that can be used
'.*', // will enable any URL
'http://{s}.example.com/{z}/{x}/{y}.png',
// for testing purposes
'http://{s}.basemaps.cartocdn.com/dark_nolabels/{z}/{x}/{y}.png'
];
config.analysis.batch.inlineExecution = true;
config.redis.idleTimeoutMillis = 1;
config.redis.reapIntervalMillis = 1;
config.varnish.purge_enabled = false;
config.health.enabled = false;
config.enabledFeatures.layerStats = true;
}
module.exports = config;

View File

@@ -16,47 +16,41 @@ var config = {
// Base URLs for the APIs
//
// See https://github.com/CartoDB/Windshaft-cartodb/wiki/Unified-Map-API
//
// Note: each entry corresponds with an express' router.
// You must define at least one path. However, middlewares are optional.
,routes: {
v1: {
api: [{
paths: [
'/api/v1',
'/user/:user/api/v1',
],
// Base url for the Detached Maps API
// "/api/v1/map" is the new API,
map: {
paths: [
'/map',
]
},
// Base url for the Templated Maps API
// "/api/v1/map/named" is the new API,
template: {
paths: [
'/map/named'
]
}
},
// For compatibility with versions up to 1.6.x
v0: {
paths: [
'/tiles'
// Optional: attach middlewares at the begining of the router
// to perform custom operations.
middlewares: [
function noop () {
return function noopMiddleware (req, res, next) {
next();
}
}
],
// Base url for the Detached Maps API
// "/tiles/layergroup" is for compatibility with versions up to 1.6.x
map: {
// "/api/v1/map" is the new API,
map: [{
paths: [
'/layergroup'
]
},
'/map',
],
middlewares: [] // Optional
}],
// Base url for the Templated Maps API
// "/tiles/template" is for compatibility with versions up to 1.6.x
template: {
// "/api/v1/map/named" is the new API,
template: [{
paths: [
'/template'
]
}
}
'/map/named'
],
middlewares: [] // Optional
}]
}]
}
// Resource URLs expose endpoints to request/retrieve metadata associated to Maps: dataviews, analysis node status.
@@ -73,9 +67,10 @@ var config = {
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
// Specify the maximum length of the queue of pending connections for the HTTP server.
// The actual length will be determined by the OS through sysctl settings such as tcp_max_syn_backlog and somaxconn on Linux.
// The default value of this parameter is 511 (not 512).
// See: https://nodejs.org/docs/latest/api/net.html#net_server_listen
,maxConnections:128
// Maximum number of templates per user. Unlimited by default.
,maxUserTemplates:1024
@@ -88,11 +83,6 @@ var config = {
,socket_timeout: 600000
,enable_cors: true
,cache_enabled: true
,log_format: ':req[X-Real-IP] :method :req[Host]:url :status :response-time ms -> :res[Content-Type] (:res[X-Tiler-Profiler]) (:res[X-Tiler-Errors])'
// If log_filename is given logs will be written
// there, in append mode. Otherwise stdout is used (default).
// Log file will be re-opened on receiving the HUP signal
,log_filename: 'logs/node-windshaft.log'
// Templated database username for authorized user
// Supported labels: 'user_id' (read from redis)
,postgres_auth_user: 'development_cartodb_user_<%= user_id %>'
@@ -127,10 +117,9 @@ var config = {
cache_ttl: 60000,
statsInterval: 5000, // milliseconds between each report to statsd about number of renderers and mapnik pool status
mvt: {
//If enabled, MVTs will be generated with PostGIS directly, instead of using Mapnik,
//PostGIS 2.4 is required for this to work
//If disabled it will use Mapnik MVT generation
usePostGIS: false
//If enabled, MVTs will be generated with PostGIS directly
//If disabled, MVTs will be generated with Mapnik MVT
usePostGIS: true
},
mapnik: {
// The size of the pool of internal mapnik backend
@@ -139,6 +128,10 @@ var config = {
// Important: check the configuration of uv_threadpool_size to use suitable value
poolSize: 8,
// The maximum number of waiting clients of the pool of internal mapnik backend
// This maximum number is per mapnik renderer created in Windshaft's RendererFactory
poolMaxWaitingClients: 64,
// Whether grainstore will use a child process or not to transform CartoCSS into Mapnik XML.
// This will prevent blocking the main thread.
useCartocssWorkers: false,
@@ -182,7 +175,7 @@ var config = {
// SQL queries will be wrapped with ST_ClipByBox2D
// Returning the portion of a geometry falling within a rectangle
// It will only work if snapToGrid is enabled
clipByBox2d: false, // this requires postgis >=2.2 and geos >=3.5
clipByBox2d: true,
postgis: {
// Parameters to pass to datasource plugin of mapnik
@@ -264,12 +257,6 @@ var config = {
// 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/node-windshaft-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: {
@@ -323,6 +310,7 @@ var config = {
purge_enabled: false, // whether the purge/invalidation mechanism is enabled in varnish or not
secret: 'xxx',
ttl: 86400,
fallbackTtl: 300,
layergroupTtl: 86400 // the max-age for cache-control header in layergroup responses
}
// this [OPTIONAL] configuration enables invalidating by surrogate key in fastly
@@ -357,7 +345,7 @@ var config = {
// Use this as a feature flags enabling/disabling mechanism
,enabledFeatures: {
// whether it should intercept tile render errors an act based on them, enabled by default.
onTileErrorStrategy: true,
onTileErrorStrategy: false,
// 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
@@ -384,6 +372,12 @@ var config = {
named_tiles: false
}
}
,pubSubMetrics: {
enabled: false,
project_id: '',
credentials: '',
topic: ''
}
};
module.exports = config;

View File

@@ -16,47 +16,41 @@ var config = {
// Base URLs for the APIs
//
// See https://github.com/CartoDB/Windshaft-cartodb/wiki/Unified-Map-API
//
// Note: each entry corresponds with an express' router.
// You must define at least one path. However, middlewares are optional.
,routes: {
v1: {
api: [{
paths: [
'/api/v1',
'/user/:user/api/v1',
],
// Base url for the Detached Maps API
// "/api/v1/map" is the new API,
map: {
paths: [
'/map',
]
},
// Base url for the Templated Maps API
// "/api/v1/map/named" is the new API,
template: {
paths: [
'/map/named'
]
}
},
// For compatibility with versions up to 1.6.x
v0: {
paths: [
'/tiles'
// Optional: attach middlewares at the begining of the router
// to perform custom operations.
middlewares: [
function noop () {
return function noopMiddleware (req, res, next) {
next();
}
}
],
// Base url for the Detached Maps API
// "/tiles/layergroup" is for compatibility with versions up to 1.6.x
map: {
// "/api/v1/map" is the new API,
map: [{
paths: [
'/layergroup'
]
},
'/map',
],
middlewares: [] // Optional
}],
// Base url for the Templated Maps API
// "/tiles/template" is for compatibility with versions up to 1.6.x
template: {
// "/api/v1/map/named" is the new API,
template: [{
paths: [
'/template'
]
}
}
'/map/named'
],
middlewares: [] // Optional
}]
}]
}
// Resource URLs expose endpoints to request/retrieve metadata associated to Maps: dataviews, analysis node status.
@@ -73,9 +67,10 @@ var config = {
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
// Specify the maximum length of the queue of pending connections for the HTTP server.
// The actual length will be determined by the OS through sysctl settings such as tcp_max_syn_backlog and somaxconn on Linux.
// The default value of this parameter is 511 (not 512).
// See: https://nodejs.org/docs/latest/api/net.html#net_server_listen
,maxConnections:128
// Maximum number of templates per user. Unlimited by default.
,maxUserTemplates:1024
@@ -88,11 +83,6 @@ var config = {
,socket_timeout: 600000
,enable_cors: true
,cache_enabled: true
,log_format: ':req[X-Real-IP] :method :req[Host]:url :status :response-time ms -> :res[Content-Type] (:res[X-Tiler-Profiler]) (:res[X-Tiler-Errors])'
// If log_filename is given logs will be written
// there, in append mode. Otherwise stdout is used (default).
// Log file will be re-opened on receiving the HUP signal
,log_filename: 'logs/node-windshaft.log'
// Templated database username for authorized user
// Supported labels: 'user_id' (read from redis)
,postgres_auth_user: 'cartodb_user_<%= user_id %>'
@@ -127,10 +117,9 @@ var config = {
cache_ttl: 60000,
statsInterval: 5000, // milliseconds between each report to statsd about number of renderers and mapnik pool status
mvt: {
//If enabled, MVTs will be generated with PostGIS directly, instead of using Mapnik,
//PostGIS 2.4 is required for this to work
//If disabled it will use Mapnik MVT generation
usePostGIS: false
//If enabled, MVTs will be generated with PostGIS directly
//If disabled, MVTs will be generated with Mapnik MVT
usePostGIS: true
},
mapnik: {
// The size of the pool of internal mapnik backend
@@ -139,6 +128,10 @@ var config = {
// Important: check the configuration of uv_threadpool_size to use suitable value
poolSize: 8,
// The maximum number of waiting clients of the pool of internal mapnik backend
// This maximum number is per mapnik renderer created in Windshaft's RendererFactory
poolMaxWaitingClients: 64,
// Whether grainstore will use a child process or not to transform CartoCSS into Mapnik XML.
// This will prevent blocking the main thread.
useCartocssWorkers: false,
@@ -182,7 +175,7 @@ var config = {
// SQL queries will be wrapped with ST_ClipByBox2D
// Returning the portion of a geometry falling within a rectangle
// It will only work if snapToGrid is enabled
clipByBox2d: false, // this requires postgis >=2.2 and geos >=3.5
clipByBox2d: true,
postgis: {
// Parameters to pass to datasource plugin of mapnik
@@ -264,12 +257,6 @@ var config = {
// 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/node-windshaft-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: {
@@ -323,6 +310,7 @@ var config = {
purge_enabled: false, // whether the purge/invalidation mechanism is enabled in varnish or not
secret: 'xxx',
ttl: 86400,
fallbackTtl: 300,
layergroupTtl: 86400 // the max-age for cache-control header in layergroup responses
}
// this [OPTIONAL] configuration enables invalidating by surrogate key in fastly
@@ -357,7 +345,7 @@ var config = {
// Use this as a feature flags enabling/disabling mechanism
,enabledFeatures: {
// whether it should intercept tile render errors an act based on them, enabled by default.
onTileErrorStrategy: true,
onTileErrorStrategy: false,
// 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
@@ -384,6 +372,12 @@ var config = {
named_tiles: false
}
}
,pubSubMetrics: {
enabled: true,
project_id: 'avid-wavelet-844',
credentials: '',
topic: 'raw-metric-events'
}
};
module.exports = config;

View File

@@ -16,47 +16,41 @@ var config = {
// Base URLs for the APIs
//
// See https://github.com/CartoDB/Windshaft-cartodb/wiki/Unified-Map-API
//
// Note: each entry corresponds with an express' router.
// You must define at least one path. However, middlewares are optional.
,routes: {
v1: {
api: [{
paths: [
'/api/v1',
'/user/:user/api/v1',
],
// Base url for the Detached Maps API
// "/api/v1/map" is the new API,
map: {
paths: [
'/map',
]
},
// Base url for the Templated Maps API
// "/api/v1/map/named" is the new API,
template: {
paths: [
'/map/named'
]
}
},
// For compatibility with versions up to 1.6.x
v0: {
paths: [
'/tiles'
// Optional: attach middlewares at the begining of the router
// to perform custom operations.
middlewares: [
function noop () {
return function noopMiddleware (req, res, next) {
next();
}
}
],
// Base url for the Detached Maps API
// "/tiles/layergroup" is for compatibility with versions up to 1.6.x
map: {
// "/api/v1/map" is the new API,
map: [{
paths: [
'/layergroup'
]
},
'/map',
],
middlewares: [] // Optional
}],
// Base url for the Templated Maps API
// "/tiles/template" is for compatibility with versions up to 1.6.x
template: {
// "/api/v1/map/named" is the new API,
template: [{
paths: [
'/template'
]
}
}
'/map/named'
],
middlewares: [] // Optional
}]
}]
}
// Resource URLs expose endpoints to request/retrieve metadata associated to Maps: dataviews, analysis node status.
@@ -73,9 +67,9 @@ var config = {
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
// Specify the maximum length of the queue of pending connections for the HTTP server.
// The actual length will be determined by the OS through sysctl settings such as tcp_max_syn_backlog and somaxconn on Linux.
// The default value of this parameter is 511 (not 512).
,maxConnections:128
// Maximum number of templates per user. Unlimited by default.
,maxUserTemplates:1024
@@ -88,11 +82,6 @@ var config = {
,socket_timeout: 600000
,enable_cors: true
,cache_enabled: true
,log_format: ':req[X-Real-IP] :method :req[Host]:url :status :response-time ms -> :res[Content-Type] (:res[X-Tiler-Profiler]) (:res[X-Tiler-Errors])'
// If log_filename is given logs will be written
// there, in append mode. Otherwise stdout is used (default).
// Log file will be re-opened on receiving the HUP signal
,log_filename: 'logs/node-windshaft.log'
// Templated database username for authorized user
// Supported labels: 'user_id' (read from redis)
,postgres_auth_user: 'cartodb_staging_user_<%= user_id %>'
@@ -127,10 +116,9 @@ var config = {
cache_ttl: 60000,
statsInterval: 5000, // milliseconds between each report to statsd about number of renderers and mapnik pool status
mvt: {
//If enabled, MVTs will be generated with PostGIS directly, instead of using Mapnik,
//PostGIS 2.4 is required for this to work
//If disabled it will use Mapnik MVT generation
usePostGIS: false
//If enabled, MVTs will be generated with PostGIS directly
//If disabled, MVTs will be generated with Mapnik MVT
usePostGIS: true
},
mapnik: {
// The size of the pool of internal mapnik backend
@@ -139,6 +127,10 @@ var config = {
// Important: check the configuration of uv_threadpool_size to use suitable value
poolSize: 8,
// The maximum number of waiting clients of the pool of internal mapnik backend
// This maximum number is per mapnik renderer created in Windshaft's RendererFactory
poolMaxWaitingClients: 64,
// Whether grainstore will use a child process or not to transform CartoCSS into Mapnik XML.
// This will prevent blocking the main thread.
useCartocssWorkers: false,
@@ -182,7 +174,7 @@ var config = {
// SQL queries will be wrapped with ST_ClipByBox2D
// Returning the portion of a geometry falling within a rectangle
// It will only work if snapToGrid is enabled
clipByBox2d: false, // this requires postgis >=2.2 and geos >=3.5
clipByBox2d: true,
postgis: {
// Parameters to pass to datasource plugin of mapnik
@@ -264,12 +256,6 @@ var config = {
// 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/node-windshaft-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: {
@@ -323,6 +309,7 @@ var config = {
purge_enabled: false, // whether the purge/invalidation mechanism is enabled in varnish or not
secret: 'xxx',
ttl: 86400,
fallbackTtl: 300,
layergroupTtl: 86400 // the max-age for cache-control header in layergroup responses
}
// this [OPTIONAL] configuration enables invalidating by surrogate key in fastly
@@ -357,7 +344,7 @@ var config = {
// Use this as a feature flags enabling/disabling mechanism
,enabledFeatures: {
// whether it should intercept tile render errors an act based on them, enabled by default.
onTileErrorStrategy: true,
onTileErrorStrategy: false,
// 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
@@ -384,6 +371,12 @@ var config = {
named_tiles: false
}
}
,pubSubMetrics: {
enabled: true,
project_id: '',
credentials: '',
topic: 'raw-metric-events'
}
};
module.exports = config;

View File

@@ -16,47 +16,41 @@ var config = {
// Base URLs for the APIs
//
// See https://github.com/CartoDB/Windshaft-cartodb/wiki/Unified-Map-API
//
// Note: each entry corresponds with an express' router.
// You must define at least one path. However, middlewares are optional.
,routes: {
v1: {
api: [{
paths: [
'/api/v1',
'/user/:user/api/v1',
],
// Base url for the Detached Maps API
// "/api/v1/map" is the new API,
map: {
paths: [
'/map',
]
},
// Base url for the Templated Maps API
// "/api/v1/map/named" is the new API,
template: {
paths: [
'/map/named'
]
}
},
// For compatibility with versions up to 1.6.x
v0: {
paths: [
'/tiles'
// Optional: attach middlewares at the begining of the router
// to perform custom operations.
middlewares: [
function noop () {
return function noopMiddleware (req, res, next) {
next();
}
}
],
// Base url for the Detached Maps API
// "/tiles/layergroup" is for compatibility with versions up to 1.6.x
map: {
// "/api/v1/map" is the new API,
map: [{
paths: [
'/layergroup'
]
},
'/map',
],
middlewares: [] // Optional
}],
// Base url for the Templated Maps API
// "/tiles/template" is for compatibility with versions up to 1.6.x
template: {
// "/api/v1/map/named" is the new API,
template: [{
paths: [
'/template'
]
}
}
'/map/named'
],
middlewares: [] // Optional
}]
}]
}
// Resource URLs expose endpoints to request/retrieve metadata associated to Maps: dataviews, analysis node status.
@@ -73,9 +67,10 @@ var config = {
http: 'http://{{=it.user}}.localhost.lan:{{=it.port}}/api/v1/map',
https: 'https://{{=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
// Specify the maximum length of the queue of pending connections for the HTTP server.
// The actual length will be determined by the OS through sysctl settings such as tcp_max_syn_backlog and somaxconn on Linux.
// The default value of this parameter is 511 (not 512).
// See: https://nodejs.org/docs/latest/api/net.html#net_server_listen
,maxConnections:128
// Maximum number of templates per user. Unlimited by default.
,maxUserTemplates:1024
@@ -88,11 +83,6 @@ var config = {
,socket_timeout: 600000
,enable_cors: true
,cache_enabled: false
,log_format: ':req[X-Real-IP] :method :req[Host]:url :status :response-time ms -> :res[Content-Type] (:res[X-Tiler-Profiler]) (:res[X-Tiler-Errors])'
// If log_filename is given logs will be written
// there, in append mode. Otherwise stdout is used (default).
// Log file will be re-opened on receiving the HUP signal
,log_filename: '/tmp/node-windshaft.log'
// Templated database username for authorized user
// Supported labels: 'user_id' (read from redis)
,postgres_auth_user: 'test_windshaft_cartodb_user_<%= user_id %>'
@@ -127,10 +117,9 @@ var config = {
cache_ttl: 60000,
statsInterval: 5000, // milliseconds between each report to statsd about number of renderers and mapnik pool status
mvt: {
//If enabled, MVTs will be generated with PostGIS directly, instead of using Mapnik,
//PostGIS 2.4 is required for this to work
//If disabled it will use Mapnik MVT generation
usePostGIS: false
//If enabled, MVTs will be generated with PostGIS directly
//If disabled, MVTs will be generated with Mapnik MVT
usePostGIS: true
},
mapnik: {
// The size of the pool of internal mapnik backend
@@ -139,6 +128,10 @@ var config = {
// Important: check the configuration of uv_threadpool_size to use suitable value
poolSize: 8,
// The maximum number of waiting clients of the pool of internal mapnik backend
// This maximum number is per mapnik renderer created in Windshaft's RendererFactory
poolMaxWaitingClients: 64,
// Whether grainstore will use a child process or not to transform CartoCSS into Mapnik XML.
// This will prevent blocking the main thread.
useCartocssWorkers: false,
@@ -182,7 +175,7 @@ var config = {
// SQL queries will be wrapped with ST_ClipByBox2D
// Returning the portion of a geometry falling within a rectangle
// It will only work if snapToGrid is enabled
clipByBox2d: false, // this requires postgis >=2.2 and geos >=3.5
clipByBox2d: true,
postgis: {
// Parameters to pass to datasource plugin of mapnik
@@ -266,12 +259,6 @@ var config = {
// 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/node-windshaft-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: {
@@ -325,6 +312,7 @@ var config = {
purge_enabled: false, // whether the purge/invalidation mechanism is enabled in varnish or not
secret: 'xxx',
ttl: 86400,
fallbackTtl: 300,
layergroupTtl: 86400 // the max-age for cache-control header in layergroup responses
}
// this [OPTIONAL] configuration enables invalidating by surrogate key in fastly
@@ -359,7 +347,7 @@ var config = {
// Use this as a feature flags enabling/disabling mechanism
,enabledFeatures: {
// whether it should intercept tile render errors an act based on them, enabled by default.
onTileErrorStrategy: true,
onTileErrorStrategy: false,
// 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
@@ -386,6 +374,12 @@ var config = {
named_tiles: false
}
}
,pubSubMetrics: {
enabled: false,
project_id: '',
credentials: '',
topic: ''
}
};
module.exports = config;

81
configure vendored
View File

@@ -1,81 +0,0 @@
#!/bin/sh
#
# This script creates config/environments/*.js files using
# config/environments/*.js.example files as input and performing
# settings substitutions.
#
# It relies on a known format of the .js.example files which haven't
# been made easier to parse to still let humans copy them manually and
# do further editing or leave them as such to get the same setup as before
# the introduction of this script.
#
# The script is a work in progress. Available switches are printed
# by invoking with the --help switch. More switches will be added
# as the need/request for them arises.
#
# --strk(2012-07-23)
#
ENVDIR=config/environments
PGPORT=
MAPNIK_VERSION=
ENVIRONMENT=development
STATUS="$0 $*"
usage() {
echo "Usage: $0 [OPTION]"
echo
echo "Configuration:"
echo " --help display this help and exit"
echo " --with-pgport=NUM access PostgreSQL server on TCP port NUM [$PGPORT]"
echo " --with-mapnik-version=STRING set mapnik version string [$MAPNIK_VERSION]"
echo " --environment=STRING set output environment name [$ENVIRONMENT]"
}
while test -n "$1"; do
case "$1" in
--help|-h)
usage
exit 0
;;
--with-pgport=*)
PGPORT=`echo "$1" | cut -d= -f2`
;;
--with-mapnik-version=*)
MAPNIK_VERSION=`echo "$1" | cut -d= -f2`
;;
--environment=*)
ENVIRONMENT=`echo "$1" | cut -d= -f2`
;;
*)
echo "Unused option '$1'" >&2
;;
esac
shift
done
ENVEX=./${ENVDIR}/${ENVIRONMENT}.js.example
if [ -z "$PGPORT" ]; then
PGPORT=`node -e "console.log(require('${ENVEX}').postgres.port)"`
fi
echo "PGPORT: $PGPORT"
echo "MAPNIK_VERSION: $MAPNIK_VERSION"
echo "ENVIRONMENT: $ENVIRONMENT"
o=`dirname "${ENVEX}"`/`basename "${ENVEX}" .example`
echo "Writing $o"
# See http://austinmatzko.com/2008/04/26/sed-multi-line-search-and-replace/
sed -n "1h;1!H;\${;g;s/\(,postgres: {[^}]*port: *'\?\)[^',]*\('\?,\)/\1$PGPORT\2/;p;}" < "${ENVEX}" \
| sed "s/mapnik_version:.*/mapnik_version: '$MAPNIK_VERSION'/" \
> "$o"
STATUSFILE=config.status--${ENVIRONMENT}
echo "Writing ${STATUSFILE}"
echo ${STATUS} > ${STATUSFILE} && chmod +x ${STATUSFILE}

View File

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

View File

@@ -1,23 +0,0 @@
After running the tests with docker, you will need Docker installed and the docker image downloaded.
## Install docker
`sudo apt install docker.io && sudo usermod -aG docker $(whoami)`
## Download image
`docker pull carto/IMAGE`
## Carto account
https://hub.docker.com/r/carto/
## Update image
- Edit the docker image file with your desired changes
- Build image:
- `docker build -t carto/IMAGE -f docker/DOCKER_FILE docker/`
- Upload to docker hub:
- Login into docker hub:
- `docker login`
- Create tag:
- `docker tag carto/IMAGE carto/IMAGE`
- Upload:
- `docker push carto/IMAGE`

View File

@@ -1,20 +0,0 @@
# Maps API
The CARTO Maps API allows you to generate maps based on data hosted in your CARTO account and apply custom SQL and CartoCSS to the data. The API generates a XYZ-based URL to fetch Web Mercator projected tiles, using web clients such as [Leaflet](http://leafletjs.com), [Google Maps](https://developers.google.com/maps/), or [OpenLayers](http://openlayers.org/).
You can create two types of maps with the Maps API:
- **Anonymous Maps**
You can create maps using your CARTO public data. Any client can change the read-only SQL and CartoCSS parameters that generate the map tiles. These maps can be created from a JavaScript application alone and no authenticated calls are needed. See [this CARTO.js example](/carto-engine/carto-js/getting-started/).
- **Named Maps**
There are also maps that have access to your private data. These maps require an owner to setup and modify any SQL and CartoCSS parameters and are not modifiable without new setup calls.
## Documentation
* [Quickstart](quickstart.md)
* [General Concepts](general_concepts.md)
* [Anonymous Maps](anonymous_maps.md)
* [Named Maps](named_maps.md)
* [Static Maps API](static_maps_api.md)
* [MapConfig File Format]([local file in the docs repo](https://github.com/CartoDB/docs/blob/master/_app/_mapsapi/06-mapconfig.md))

View File

@@ -1,114 +0,0 @@
This document list all routes available in Windshaft-cartodb Maps API server.
## Routes list
1. `GET (?:/api/v1/map|/user/:user/api/v1/map|/tiles/layergroup)/:token/:z/:x/:y@:scale_factor?x.:format {:user(f),:token(f),:z(f),:x(f),:y(f),:scale_factor(t),:format(f)} (1)`
<br/>Notes: Mapnik retina tiles [0]
1. `GET (?:/api/v1/map|/user/:user/api/v1/map|/tiles/layergroup)/:token/:z/:x/:y.:format {:user(f),:token(f),:z(f),:x(f),:y(f),:format(f)} (1)`
<br/>Notes: Mapnik tiles [0]
1. `GET (?:/api/v1/map|/user/:user/api/v1/map|/tiles/layergroup)/:token/:layer/:z/:x/:y.(:format) {:user(f),:token(f),:layer(f),:z(f),:x(f),:y(f),:format(f)} (1)`
<br/>Notes: Per :layer rendering based on :format [0]
1. `GET (?:/api/v1/map|/user/:user/api/v1/map|/tiles/layergroup)/:token/:layer/attributes/:fid {:user(f),:token(f),:layer(f),:fid(f)} (1)`
<br/>Notes: Endpoint for info windows data, alternative for sql api when tables are private [0]
1. `GET (?:/api/v1/map|/user/:user/api/v1/map|/tiles/layergroup)/static/center/:token/:z/:lat/:lng/:width/:height.:format {:user(f),:token(f),:z(f),:lat(f),:lng(f),:width(f),:height(f),:format(f)} (1)`
<br/>Notes: Static Maps API [0]
1. `GET (?:/api/v1/map|/user/:user/api/v1/map|/tiles/layergroup)/static/bbox/:token/:west,:south,:east,:north/:width/:height.:format {:user(f),:token(f),:west(f),:south(f),:east(f),:north(f),:width(f),:height(f),:format(f)} (1)`
<br/>Notes: Static Maps API [0]
1. `GET (?:/api/v1/map|/user/:user/api/v1/map|/tiles/layergroup)/:token/:layer/widget/:widgetName {:user(f),:token(f),:layer(f),:widgetName(f)} (1)`
<br/>Notes: By :widgetName per :layer widget [0]
1. `GET (?:/api/v1/map|/user/:user/api/v1/map|/tiles/layergroup)/:token/:layer/widget/:widgetName/search {:user(f),:token(f),:layer(f),:widgetName(f)} (1)`
<br/>Notes: By :widgetName per :layer widget search [0]
1. `GET (?:/api/v1/map|/user/:user/api/v1/map|/tiles/layergroup) {:user(f)} (1)`
<br/>Notes: Map instantiation [0]
1. `POST (?:/api/v1/map|/user/:user/api/v1/map|/tiles/layergroup) {:user(f)} (1)`
<br/>Notes: Map instantiation [0]
1. `GET (?:/api/v1/map/named|/user/:user/api/v1/map/named|/tiles/template)/:template_id/jsonp {:user(f),:template_id(f)} (1)`
<br/>Notes: Named maps JSONP instantiation [1]
1. `POST (?:/api/v1/map/named|/user/:user/api/v1/map/named|/tiles/template)/:template_id {:user(f),:template_id(f)} (1)`
<br/>Notes: Instantiate named map [1]
1. `OPTIONS (?:/api/v1/map|/user/:user/api/v1/map|/tiles/layergroup) {:user(f)} (1)`
<br/>Notes: CORS [0]
1. `GET (?:/api/v1/map/named|/user/:user/api/v1/map/named|/tiles/template)/:template_id/:layer/:z/:x/:y.(:format) {:user(f),:template_id(f),:layer(f),:z(f),:x(f),:y(f),:0(f),:format(f)} (1)`
<br/>Notes: Per :layer fixed URL named map tiles [1]
1. `GET (?:/api/v1/map|/user/:user/api/v1/map|/tiles/layergroup)/static/named/:template_id/:width/:height.:format {:user(f),:template_id(f),:width(f),:height(f),:format(f)} (1)`
<br/>Notes: Static map for named maps [1]
1. `POST (?:/api/v1/map/named|/user/:user/api/v1/map/named|/tiles/template) {:user(f)} (1)`
<br/>Notes: Create named map (w/ API KEY) [1]
1. `PUT (?:/api/v1/map/named|/user/:user/api/v1/map/named|/tiles/template)/:template_id {:user(f),:template_id(f)} (1)`
<br/>Notes: Update a named map (w/ API KEY) [1]
1. `GET (?:/api/v1/map/named|/user/:user/api/v1/map/named|/tiles/template)/:template_id {:user(f),:template_id(f)} (1)`
<br/>Notes: Named map retrieval (w/ API KEY) [1]
1. `DELETE (?:/api/v1/map/named|/user/:user/api/v1/map/named|/tiles/template)/:template_id {:user(f),:template_id(f)} (1)`
<br/>Notes: Delete named map (w/ API KEY) [1]
1. `GET (?:/api/v1/map/named|/user/:user/api/v1/map/named|/tiles/template) {:user(f)} (1)`
<br/>Notes: List named maps (w/ API KEY) [1]
1. `OPTIONS (?:/api/v1/map/named|/user/:user/api/v1/map/named|/tiles/template)/:template_id {:user(f),:template_id(f)} (1)`
<br/>Notes: CORS [1]
1. `GET /health {} (1)`
<br/>Notes: Health check
1. `GET / {} (1)`
<br/>Notes: Welcome message
1. `GET /version {} (1)`
<br/>Notes: Return relevant module versions: mapnik, grainstore, etc
## Optional deprecated routes
- [0] `/tiles/layergroup` is deprecated and `/api/v1/map` should be used but we keep it for now.
- [1] `/tiles/template` is deprecated and `/api/v1/map/named` should be used but we keep it for now.
## How to generate the list of routes
Something like the following patch should do the trick
```javascript
diff --git a/lib/cartodb/server.js b/lib/cartodb/server.js
index 5f62850..bca377d 100644
--- a/lib/cartodb/server.js
+++ b/lib/cartodb/server.js
@@ -215,6 +215,20 @@ module.exports = function(serverOptions) {
* END Routing
******************************************************************************************************************/
+ var format = require('util').format;
+ var routesNotes = app._router.stack
+ .filter(function(handler) { return !!handler.route; })
+ .map(function(handler) {
+ return format("\n1. `%s %s {%s} (1)`\n<br/>Notes: [DEPRECATED]? ",
+ Object.keys(handler.route.methods)[0].toUpperCase(),
+ handler.route.path,
+ handler.keys.map(function(k) {
+ return format(':%s(%s)', k.name, k.optional ? 't' : 'f');
+ }).join(',')
+ );
+ });
+ console.log(routesNotes.join('\n'));
+
return app;
};
```

View File

@@ -0,0 +1 @@
## Example 1

View File

@@ -1,27 +0,0 @@
# General Concepts
The following concepts are the same for every endpoint in the API except when it's noted explicitly.
## Auth
By default, users do not have access to private tables in CARTO. In order to instantiate a map from private table data an API Key is required. Additionally, to include some endpoints, an API Key must be included (e.g. creating a Named Map).
To execute an authorized request, `api_key=YOURAPIKEY` should be added to the request URL. The param can be also passed as POST param. Using HTTPS is mandatory when you are performing requests that include your `api_key`.
## Errors
Errors are reported using standard HTTP codes and extended information encoded in JSON with this format:
```javascript
{
"errors": [
"access forbidden to table TABLE"
]
}
```
If you use JSONP, the 200 HTTP code is always returned so the JavaScript client can receive errors from the JSON object.
## CORS Support
All the endpoints, which might be accessed using a web browser, add CORS headers and allow OPTIONS method.

View File

@@ -1,6 +1,6 @@
# Quickstart
## Quickstart
## Anonymous Maps
### Anonymous Maps
Here is an example of how to create an Anonymous Map with JavaScript:
@@ -31,7 +31,7 @@ $.ajax({
})
```
## Named Maps
### Named Maps
Let's create a Named Map using some private tables in a CARTO account.
The following map config sets up a map of European countries that have a white fill color:
@@ -58,7 +58,7 @@ The following map config sets up a map of European countries that have a white f
The MapConfig needs to be sent to CARTO's Map API using an authenticated call. Here we will use a command line tool called `curl`. For more info about this tool, see [this blog post](http://quickleft.com/blog/command-line-tutorials-curl), or type `man curl` in bash. Using `curl`, and storing the config from above in a file `MapConfig.json`, the call would look like:
#### Call
##### Call
```bash
curl 'https://{username}.carto.com/api/v1/map/named?api_key={api_key}' -H 'Content-Type: application/json' -d @mapconfig.json
@@ -66,7 +66,7 @@ curl 'https://{username}.carto.com/api/v1/map/named?api_key={api_key}' -H 'Conte
To get the `URL` to fetch the tiles you need to instantiate the map, where `template_id` is the template name from the previous response.
#### Call
##### Call
```bash
curl -X POST 'https://{username}.carto.com/api/v1/map/named/{template_id}' -H 'Content-Type: application/json'
@@ -76,7 +76,7 @@ The response will return JSON with properties for the `layergroupid`, the timest
Note: all `layers` in `metadata` will always have a `type` string and a `meta` dictionary with the key/value pairs.
#### Response
##### Response
```javascript
{

View File

@@ -0,0 +1,46 @@
## General Concepts
The following concepts are the same for every endpoint in the API except when it's noted explicitly.
### Auth
By default, users do not have access to private tables in CARTO. In order to instantiate a map from private table data an API Key is required. Additionally, an API Key is also required to use some of the API endpoints (e.g. to create a Named Map).
To execute an authorized request, `api_key=YOURAPIKEY` should be added to the request URL. The param can be also passed as POST param. Using HTTPS is mandatory when you are performing requests that include your `api_key`.
### Errors
Errors are reported using standard HTTP codes and extended information encoded in JSON with this format:
```javascript
{
"errors": [
"access forbidden to table TABLE"
]
}
```
If you use JSONP, the 200 HTTP code is always returned so the JavaScript client can receive errors from the JSON object.
### CORS Support
All the endpoints, which might be accessed using a web browser, add CORS headers and allow OPTIONS method.
### Map Tile Rendering
Map tiles create the graphical representation of your map in a web browser. The performance rendering of map tiles is dependent on the type of geospatial data model (raster or vector) that you are using.
- **Raster**: Generates map tiles based on a grid of pixels to represent your data. Each cell is a fixed size and contains values for particular map features. On the server-side, each request queries a dataset to retrieve data for each map tile. The grid size of map tiles can often lead to graphic quality issues.
- **Vector**: Generates map tiles based on pre-defined coordinates to represent your data, similar to how basemap image tiles are rendered. On the client-side, map tiles represent real-world geometries of a map. Depending on the coordinates, vertices are used to connect the data and display points, lines, or polygons for the map tiles.
**Note:** By default, CARTO uses vector graphics for map rendering. Please [contact us](mailto:support@carto.com) if you need raster rendering enabled as part of your requirements.
### Mapbox Vector Tiles (MVT)
[Mapbox Vector Tiles (MVT)](https://www.mapbox.com/vector-tiles/specification/) are map tiles that store geographic vector data on the client-side. Browser performance is fast since you can pan and zoom without having to query the server.
CARTO uses a Web Graphics Library (WebGL) to process MVT files. This is useful since WebGL's are compatible with most web browsers, include support for multiple client-side mapping engines, and do not require additional information from the server; which makes it more efficient for rendering map tiles.
**Tip:** You can process MVT files with the [`ST_AsMVT` PostGIS function](https://postgis.net/docs/manual-dev/ST_AsMVT.html) with the [Maps API Windshaft renderer](https://github.com/CartoDB/Windshaft/blob/1000x/lib/windshaft/renderers/pg_mvt/renderer.js).

View File

@@ -1,18 +1,18 @@
# Anonymous Maps
## Anonymous Maps
Anonymous Maps allows you to instantiate a map given SQL and CartoCSS. It also allows you to add interaction capabilities using [UTF Grid.](https://github.com/mapbox/utfgrid-spec).
Alternatively, you can get the data for the map (geometry and attributes for each layer) using vector tiles (in which case CartoCSS is not required).
## Instantiate
### Instantiate
#### Definition
##### Definition
```html
POST /api/v1/map
```
#### Params
##### Params
```javascript
{
@@ -29,9 +29,9 @@ POST /api/v1/map
}
```
See [MapConfig File Formats](http://docs.carto.com/carto-engine/maps-api/mapconfig/) for details.
See [MapConfig File Formats]({{site.mapsapi_docs}}/guides/MapConfig-file-format/) for details.
#### Response
##### Response
The response includes:
@@ -49,15 +49,15 @@ Now, for convenience, the layergroup includes the final URLs in two formats:
1. Leaflet's urlTemplate alike: useful when working with raster tiles or with libraries with an API similar to Leaflet's one.
1. [TileJSON spec](https://github.com/mapbox/tilejson-spec): useful when working with Mapbox GL or any other library that supports TileJSON.
### Example
#### Example
#### Call
##### Call
```bash
curl 'https://{username}.carto.com/api/v1/map' -H 'Content-Type: application/json' -d @mapconfig.json
```
#### Response
##### Response
```javascript
{
@@ -97,7 +97,7 @@ curl 'https://{username}.carto.com/api/v1/map' -H 'Content-Type: application/jso
}
```
## Map Tile Rendering
### Map Tile Rendering
Map tiles are used to create the graphic representation of your map in a web browser. Tiles can be requested either as pre-rendered *raster* tiles (images) or as *vector* map data to be rendered by the client (browser).
@@ -105,11 +105,11 @@ Map tiles are used to create the graphic representation of your map in a web bro
- **Vector**: Tiles can also be requested as MVT (Mapbox Vector Tiles). In this case, only the geospatial vector data, without any styling, is returned. These tiles should be processed in the client-side to render the map. In this case layers do not need to define CartoCSS, as any rendering and styling will be performed on the client side. The vector data of a tile represents real-world geometries by defining the vertices of points, lines or polygons in a tile-specific coordinate system.
## Retrieve resources from the layergroup
### Retrieve resources from the layergroup
When you have a layergroup, there are several resources for retrieving layergoup details such as, accessing Mapnik tiles, getting individual layers, accessing defined Attributes, and blending and layer selection.
### Raster tiles
#### Raster tiles
These raster tiles are PNG images that represent only the Mapnik layers of a map. See [individual layers](#individual-layers) for details about how to retrieve other layers.
@@ -117,7 +117,7 @@ These raster tiles are PNG images that represent only the Mapnik layers of a map
https://{username}.carto.com/api/v1/map/{layergroupid}/{z}/{x}/{y}.png
```
### Mapbox Vector Tiles (MVT)
#### Mapbox Vector Tiles (MVT)
[Mapbox Vector Tiles (MVT)](https://www.mapbox.com/vector-tiles/specification/) are map tiles that transfer geographic vector data to the client-side. Browser performance is fast since you can pan and zoom without having to query the server.
@@ -125,7 +125,7 @@ CARTO uses Web Graphics Library (WebGL) to process MVT files on the browser. Thi
The following examples describe how to fetch MVT tiles with a cURL request.
#### MVT and Windshaft
##### MVT and Windshaft
CARTO uses Windshaft as the map tiler library to render multilayer maps with the Maps API. You can use Windshaft to request MVT using the same layer type that is used for requesting raster tiles (Mapnik layer). Simply change the file format `.mvt` in the URL.
@@ -149,7 +149,7 @@ The following example instantiates an anonymous map with layer options:
**Note**: If no layer type is specified, Mapnik tiles are used by default. To access MVT tiles, specify `https://{username}.cartodb.com/api/v1/map/HASH/{z}/{x}/{y}.mvt` as the `maps_api_template` variable.
**Tip:** If you are using [Named Maps](https://carto.com/docs/carto-engine/maps-api/named-maps/) to instantiate a layer, indicate the MVT file format and layer in the response:
**Tip:** If you are using [Named Maps]({{site.mapasapi_docs}}/guides/named-maps/) to instantiate a layer, indicate the MVT file format and layer in the response:
```bash
https://{username}.cartodb.com/api/v1/map/named/:templateId/:layer/{z}/{x}/{y}.mvt
@@ -161,7 +161,7 @@ For all layers in a Named Map, you must indicate Mapnik as the layer filter:
https://{username}.cartodb.com/api/v1/map/named/:templateId/mapnik/{z}/{x}/{y}.mvt
```
#### Layergroup Filter for MVT Tiles
##### Layergroup Filter for MVT Tiles
To filter layers using Windshaft, use the following request where layers are numbered:
@@ -181,7 +181,7 @@ To filter a specific layer:
https://{username}.cartodb.com/api/v1/map/HASH/2/{z}/{x}/{y}.mvt
```
#### Example 1: MVT Tiles with Windshaft, CARTO.js, and MapboxGL
##### Example 1: MVT Tiles with Windshaft, CARTO.js, and MapboxGL
1) Import the required libraries:
@@ -223,7 +223,7 @@ cartocss: "...",
5) Request Tiles (from CARTO) and Set to Map Object (Mapbox):
**Note:** By default, [CARTO core functions](https://carto.com/docs/carto-engine/carto-js/core-api/) retrieve URLs for fully rendered tiles. You must replace the default format (.png) with the MVT format (.mvt).
**Note:** By default, [CARTO core functions]({{site.cartojs_docs}}/v3/guides/core-API-functionality/) retrieve URLs for fully rendered tiles. You must replace the default format (.png) with the MVT format (.mvt).
```bash
@@ -237,7 +237,7 @@ map.setStyle(simpleStyle(tiles));
});
```
#### Example 2: MVT Libraries with Windshaft and MapboxGL
##### Example 2: MVT Libraries with Windshaft and MapboxGL
When you are not including CARTO.js to implement MVT tiles, you must use the `map.setStyle` parameter to specify vector map rendering.
@@ -291,7 +291,7 @@ map.setStyle({
- [MapboxGL Style Specifications](https://www.mapbox.com/mapbox-gl-js/style-spec/)
- [Example of MapboxGL Implementation](https://www.mapbox.com/mapbox-gl-js/examples/)
### Individual layers
#### Individual layers
The MapConfig specification holds the layers definition in a 0-based index. Layers can be requested individually, in different formats, depending on the layer type.
@@ -309,7 +309,7 @@ If the MapConfig had a Torque layer at index 1 it could be possible to request i
https://{username}.carto.com/api/v1/map/{layergroupid}/1/{z}/{x}/{y}.torque.json
```
### Attributes defined in `attributes` section
#### Attributes defined in `attributes` section
```bash
https://{username}.carto.com/api/v1/map/{layergroupid}/{layer}/attributes/{feature_id}
@@ -321,7 +321,7 @@ Which returns JSON with the attributes defined, such as:
{ "c": 1, "d": 2 }
```
### Blending and layer selection
#### Blending and layer selection
```bash
https://{username}.carto.com/api/v1/map/{layergroupid}/{layer_filter}/{z}/{x}/{y}.png
@@ -352,17 +352,17 @@ Some notes about filtering:
- Invalid index values or out of bounds indexes will end in `Invalid layer filtering` errors.
- 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 that you will always get consistent behavior.
## Create JSONP
### Create JSONP
The JSONP endpoint is provided in order to allow web browsers access which don't support CORS.
#### Definition
##### Definition
```bash
GET /api/v1/map?callback=method
```
#### Params
##### Params
Param | Description
--- | ---
@@ -370,15 +370,15 @@ config | Encoded JSON with the params for creating Named Maps (the variables def
lmza | This attribute contains the same as config but LZMA compressed. It cannot be used at the same time as `config`.
callback | JSON callback name.
### Example
#### Example
#### Call
##### Call
```bash
curl "https://{username}.carto.com/api/v1/map?callback=callback&config=%7B%22version%22%3A%221.0.1%22%2C%22layers%22%3A%5B%7B%22type%22%3A%22cartodb%22%2C%22options%22%3A%7B%22sql%22%3A%22select+%2A+from+european_countries_e%22%2C%22cartocss%22%3A%22%23european_countries_e%7B+polygon-fill%3A+%23FF6600%3B+%7D%22%2C%22cartocss_version%22%3A%222.3.0%22%2C%22interactivity%22%3A%5B%22cartodb_id%22%5D%7D%7D%5D%7D"
```
#### Response
##### Response
```javascript
callback({
@@ -391,6 +391,6 @@ callback({
})
```
## Remove
### Remove
Anonymous Maps cannot be removed by an API call. They will expire after about five minutes, or sometimes longer. If an Anonymous Map expires and tiles are requested from it, an error will be raised. This could happen if a user leaves a map open and after time, returns to the map and attempts to interact with it in a way that requires new tiles (e.g. zoom). The client will need to go through the steps of creating the map again to fix the problem.

View File

@@ -1,4 +1,4 @@
# Named Maps
## 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).
@@ -6,7 +6,7 @@ The Named Map workflow consists of uploading a MapConfig file to CARTO servers,
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.
**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]({{site.cartojs_docs}}/v3/guides/layer-source-object/#named-maps-layer-source-object-type-namedmap) layer source object to draw the Named Map.
The main differences, compared to Anonymous Maps, is that Named Maps include:
@@ -16,26 +16,26 @@ The main differences, compared to Anonymous Maps, is that Named Maps include:
- **template map**
The template map is static and may contain placeholders, enabling you to modify your maps appearance by using variables. Templates maps are persistent with no preset expiration. They can only be created, or deleted, by a CARTO user with a valid API KEY (See [auth argument](#arguments)).
Uploading a MapConfig creates a Named Map. MapConfigs are uploaded to the server by sending the server a "template".json file, which contain the [MapConfig specifications](http://docs.carto.com/carto-engine/maps-api/mapconfig/).
Uploading a MapConfig creates a Named Map. MapConfigs are uploaded to the server by sending the server a "template".json file, which contain the [MapConfig specifications]({{site.mapsapi_docs}}/guides/MapConfig-file-format/).
**Note:** There is a limit of 4,096 Named Maps allowed per account. If you need to create more Named Maps, it is recommended to use a single Named Map and change the variables using [placeholders](#placeholder-format), instead of uploading multiple [Named Map MapConfigs](http://docs.carto.com/carto-engine/maps-api/mapconfig/#named-map-layer-options).
**Note:** There is a limit of 4,096 Named Maps allowed per account. If you need to create more Named Maps, it is recommended to use a single Named Map and change the variables using [placeholders](#placeholder-format), instead of uploading multiple [Named Map MapConfigs]({{site.mapsapi_docs}}/guides/MapConfig-file-format/#named-map-layer-options).
## Create
### Create
#### Definition
##### Definition
```html
POST /api/v1/map/named
```
#### Params
##### Params
Params | Description
--- | ---
api_key | is required
MapConfig | a [Named Map MapConfig](http://docs.carto.com/carto-engine/maps-api/mapconfig/#named-map-layer-options) is required to create a Named Map
MapConfig | a [Named Map MapConfig]({{site.mapsapi_docs}}/guides/MapConfig-file-format/#named-map-layer-options) is required to create a Named Map
#### template.json
##### template.json
The `name` argument defines how to name this "template_name".json. Note that there are some requirements for how to name a Named Map template. See the [`name`](#arguments) argument description for details.
@@ -93,7 +93,7 @@ The `name` argument defines how to name this "template_name".json. Note that the
}
```
#### Arguments
##### Arguments
Params | Description
--- | ---
@@ -104,7 +104,7 @@ 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.
layergroup | the layergroup configurations, as specified in the template. See [MapConfig File Format]({{site.mapsapi_docs}}/guides/MapConfig-file-format/) 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. 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
@@ -122,13 +122,13 @@ view (optional) | extra keys to specify the view area for the map. It can be use
&#124;_ &#124;_ north | UpperCorner latitude for the bounding box, in decimal degrees (aka most northern)
### Placeholder Format
#### 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 [Layergroup 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]({{site.mapsapi_docs}}/guides/MapConfig-file-format/#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.
#### Example
##### Example
```javascript
<%= my_color %>
@@ -136,7 +136,7 @@ Valid placeholder names start with a letter and can only contain letters, number
The set of supported placeholders for a template need to be explicitly defined with a specific type, and default value, for each placeholder.
### Placeholder Types
#### Placeholder Types
The placeholder type will determine the kind of escaping for the associated value. Supported types are:
@@ -151,7 +151,7 @@ Placeholder default values will be used whenever new values are not provided as
When using templates, be very careful about your selections as they can give broad access to your data if they are defined loosely.
#### Call
##### Call
This is the call for creating the Named Map. It is sending the template.json file to the service, and the server responds with the template id.
@@ -162,7 +162,7 @@ curl -X POST \
'https://{username}.carto.com/api/v1/map/named?api_key={api_key}'
```
#### Response
##### 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.
@@ -172,17 +172,17 @@ The response back from the API provides the name of your MapConfig as a template
}
```
## Instantiate
### Instantiate
Instantiating a Named Map allows you to fetch the map tiles. You can use the Maps API to instantiate, or use the CARTO.js `createLayer()` function. The result is an Anonymous Map.
#### Definition
##### Definition
```html
POST /api/v1/map/named/{template_name}
```
#### Param
##### Param
Param | Description
--- | ---
@@ -200,14 +200,14 @@ The fields you pass as `params.json` depend on the variables allowed by the Name
**Note:** It is required that you include a `params.json` file to instantiate a Named Map that contains variables, even if you have no fields to pass and the JSON is empty. (This is specific to when a Named Map allows variables (if placeholders were defined in the template.json by the user).
#### Example
##### Example
You can initialize a template map by passing all of the required parameters in a POST to `/api/v1/map/named/{template_name}`.
Valid auth token will be needed, if required by the template.
#### Call
##### Call
```bash
curl -X POST \
@@ -216,7 +216,7 @@ curl -X POST \
'https://{username}.carto.com/api/v1/map/named/{template_name}?auth_token={auth_token}'
```
#### Response
##### Response
```javascript
{
@@ -225,7 +225,7 @@ curl -X POST \
}
```
#### Error
##### Error
```javascript
{
@@ -233,33 +233,33 @@ curl -X POST \
}
```
You can then use the `layergroupid` for fetching tiles and grids as you would normally (see [Anonymous Maps](http://docs.carto.com/carto-engine/maps-api/anonymous-maps/)).
You can then use the `layergroupid` for fetching tiles and grids as you would normally (see [Anonymous Maps]({{site.mapsapi_docs}}/guides/anonymous-maps/)).
## Update
### Update
#### Definition
##### Definition
```bash
PUT /api/v1/map/named/{template_name}
```
#### Params
##### Params
Param | Description
--- | ---
api_key | is required
#### Response
##### Response
Same as updating a map.
### Other Information
#### Other Information
Updating a Named Map removes all the Named Map instances, so they need to be initialized again.
### Example
#### Example
#### Call
##### Call
```bash
curl -X PUT \
@@ -268,7 +268,7 @@ curl -X PUT \
'https://{username}.carto.com/api/v1/map/named/{template_name}?api_key={api_key}'
```
#### Response
##### Response
```javascript
{
@@ -286,31 +286,31 @@ If a template with the same name does NOT exist, a 400 HTTP response is generate
}
```
## Delete
### Delete
Deletes the specified template map from the server, and disables any previously initialized versions of the map.
#### Definition
##### Definition
```bash
DELETE /api/v1/map/named/{template_name}
```
#### Params
##### Params
Param | Description
--- | ---
api_key | is required
### Example
#### Example
#### Call
##### Call
```bash
curl -X DELETE 'https://{username}.carto.com/api/v1/map/named/{template_name}?api_key={api_key}'
```
#### Response
##### Response
```javascript
{
@@ -320,31 +320,31 @@ curl -X DELETE 'https://{username}.carto.com/api/v1/map/named/{template_name}?ap
On success, a 204 (No Content) response will be issued. Otherwise a 4xx response with an error will be returned.
## Listing Available Templates
### Listing Available Templates
This allows you to get a list of all available templates.
#### Definition
##### Definition
```bash
GET /api/v1/map/named/
```
#### Params
##### Params
Param | Description
--- | ---
api_key | is required
### Example
#### Example
#### Call
##### Call
```bash
curl -X GET 'https://{username}.carto.com/api/v1/map/named?api_key={api_key}'
```
#### Response
##### Response
```javascript
{
@@ -352,7 +352,7 @@ curl -X GET 'https://{username}.carto.com/api/v1/map/named?api_key={api_key}'
}
```
#### Error
##### Error
```javascript
{
@@ -360,31 +360,31 @@ curl -X GET 'https://{username}.carto.com/api/v1/map/named?api_key={api_key}'
}
```
## Get Template Definition
### Get Template Definition
This gets the definition of a requested template.
#### Definition
##### Definition
```bash
GET /api/v1/map/named/{template_name}
```
#### Params
##### Params
Param | Description
--- | ---
api_key | is required
### Example
#### Example
#### Call
##### Call
```bash
curl -X GET 'https://{username}.carto.com/api/v1/map/named/{template_name}?api_key={api_key}'
```
#### Response
##### Response
```javascript
{
@@ -392,7 +392,7 @@ curl -X GET 'https://{username}.carto.com/api/v1/map/named/{template_name}?api_k
}
```
#### Error
##### Error
```javascript
{
@@ -400,17 +400,17 @@ curl -X GET 'https://{username}.carto.com/api/v1/map/named/{template_name}?api_k
}
```
## JSONP for Named Maps
### JSONP for Named Maps
If using a [JSONP](https://en.wikipedia.org/wiki/JSONP) (for old browsers) request, there is a special endpoint used to initialize and create a Named Map.
#### Definition
##### Definition
```bash
GET /api/v1/map/named/{template_name}/jsonp
```
#### Params
##### Params
Params | Description
--- | ---
@@ -419,13 +419,13 @@ params | Encoded JSON with the params (variables) needed for the Named Map
lmza | You can use an LZMA compressed file instead of a params JSON file
callback | JSON callback name
#### Call
##### Call
```bash
curl 'https://{username}.carto.com/api/v1/map/named/{template_name}/jsonp?auth_token={auth_token}&callback=callback&config=template_params_json'
```
#### Response
##### Response
```javascript
callback({
@@ -454,9 +454,9 @@ callback({
})
```
## CARTO.js for Named Maps
### CARTO.js for Named Maps
You can 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.
You can 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]({{site.cartojs_docs}}/v3/guides/layer-source-object/#named-maps-layer-source-object-type-namedmap) layer source object to draw the Named Map.
```javascript
{
@@ -486,15 +486,15 @@ You can use a Named Map that you created (which is defined by its `name`), to cr
**Note:** Instantiating a Named Map over a `createLayer` does not require an API Key and by default, does not include auth tokens. _If_ you defined auth tokens for the Named Map configuration, then you will have to include them.
[CARTO.js](http://docs.carto.com/carto-engine/carto-js/) has methods for accessing your Named Maps.
[CARTO.js]({{site.cartojs_docs}}/v3/) has methods for accessing your Named Maps.
1. [layer.setParams()](http://docs.carto.com/carto-engine/carto-js/api-methods/#layersetparamskey-value) allows you to change the template variables (in the placeholders object) via JavaScript
1. [layer.setParams()]({{site.cartojs_docs}}/v3/reference/#layersetparamskey-value) allows you to change the template variables (in the placeholders object) via JavaScript
**Note:** The CARTO.js `layer.setParams()` function is not supported when using Named Maps for Torque. Alternatively, you can create a [Torque layer in a Named Map](http://bl.ocks.org/iriberri/de37be6406f9cc7cfe5a)
2. [layer.setAuthToken()](http://docs.carto.com/carto-engine/carto-js/api-methods/#layersetauthtokenauthtoken) allows you to set the auth tokens to create the layer
2. [layer.setAuthToken()](h{{site.cartojs_docs}}/v3/reference/#layersetauthtokenauth_token) allows you to set the auth tokens to create the layer
### Torque Layer in a Named Map
#### Torque Layer in a Named Map
If you are creating a Torque layer in a Named Map without using the Torque.js library, you can apply the Torque layer by applying the following code with CARTO.js:
@@ -525,7 +525,7 @@ If you are creating a Torque layer in a Named Map without using the Torque.js li
}
```
#### Examples of Named Maps created with CARTO.js
##### Examples of Named Maps created with CARTO.js
- [Named Map selectors with interaction](http://bl.ocks.org/andy-esch/515a8af1f99d5e690484)
@@ -533,11 +533,11 @@ If you are creating a Torque layer in a Named Map without using the Torque.js li
- [Toggling sublayers in a Named Map](http://bl.ocks.org/andy-esch/c1a0f4913610eec53cd3)
## Fetching XYZ Tiles for Named Maps
### Fetching XYZ Tiles for Named Maps
Optionally, authenticated users can fetch projected tiles (XYZ tiles or Mapnik Retina tiles) for your Named Map.
### Fetch XYZ Tiles Directly with a URL
#### 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.
@@ -551,17 +551,17 @@ For example, a complete URL might appear as:
The placeholders indicate the following:
- [`template_id`](http://docs.carto.com/carto-engine/maps-api/named-maps/#response) is the response of your Named Map.
- layers can be a number (referring to the # layer of your map), all layers of your map, or a list of layers.
- [`template_id`]({{site.mapsapi_docs}}/guides/named-maps/#response) is the response of your Named Map.
- layers can be a number (referring to the ## layer of your map), all layers of your map, or a list of layers.
- To show just the basemap layer, enter the number value `0` in the layer placeholder "https://{username}.carto.com/api/v1/map/named/{template_id}/0/{z}/{x}/{y}.png"
- To show the first layer, enter the number value `1` in the layer placeholder "https://{username}.carto.com/api/v1/map/named/{template_id}/1/{z}/{x}/{y}.png"
- To show all layers, enter the value `all` for the layer placeholder "https://{username}.carto.com/api/v1/map/named/{template_id}/all/{z}/{x}/{y}.png"
- To show a [list of layers](http://docs.carto.com/carto-engine/maps-api/anonymous-maps/#blending-and-layer-selection), enter the comma separated layer value as 0,1,2 in the layer placeholder. For example, to show the basemap and the first layer, "https://{username}.carto.com/api/v1/map/named/{template_id}/0,1/{z}/{x}/{y}.png"
- To show a [list of layers]({{site.mapsapi_docs}}/guides/anonymous-maps/#blending-and-layer-selection), enter the comma separated layer value as 0,1,2 in the layer placeholder. For example, to show the basemap and the first layer, "https://{username}.carto.com/api/v1/map/named/{template_id}/0,1/{z}/{x}/{y}.png"
### Get Mapnik Retina Tiles
#### Get Mapnik Retina Tiles
Mapnik Retina tiles are not directly supported for Named Maps, so you cannot use the Named Map template_id. To fetch Mapnik Retina tiles, get the [layergroupid](http://docs.carto.com/carto-engine/maps-api/named-maps/#response-1) to initialize the map.
Mapnik Retina tiles are not directly supported for Named Maps, so you cannot use the Named Map template_id. To fetch Mapnik Retina tiles, get the [layergroupid]({{site.mapsapi_docs}}/guides/named-maps/#response-1) to initialize the map.
Instantiate the map by using your `layergroupid` in the token placeholder:

View File

@@ -1,24 +1,24 @@
# Static Maps API
## Static Maps API
The Static Maps API can be initiated using both Named and Anonymous Maps using the 'layergroupid' token. The API can be used to create static images of parts of maps and thumbnails for use in web design, graphic design, print, field work, and many other applications that require standard image formats.
The Static Maps API can be initiated using both Named and Anonymous Maps using the `layergroupid` token. The API can be used to create static images of parts of maps and thumbnails for use in web design, graphic design, print, field work, and many other applications that require standard image formats.
## Maps API endpoints
### Maps API endpoints
Begin by instantiating either a Named or Anonymous Map using the `layergroupid token` as demonstrated in the Maps API documentation above. The `layergroupid` token calls to the map and allows for parameters in the definition to generate static images.
Begin by instantiating either a Named or Anonymous Map using the `layergroupid` token as demonstrated in the Maps API documentation above. The `layergroupid` token calls to the map and allows for parameters in the definition to generate static images.
### Zoom + center
#### Zoom + center
#### Definition
##### Definition
```bash
GET /api/v1/map/static/center/{token}/{z}/{lat}/{lng}/{width}/{height}.{format}{{?}extra_options}
{% raw %}GET /api/v1/map/static/center/{token}/{z}/{lat}/{lng}/{width}/{height}.{format}{{?}extra_options}{% endraw %}
```
#### Params
##### Params
Param | Description
--- | ---
token | the layergroupid token from the map instantiation
token | the `layergroupid` token from the map instantiation
z | the zoom level of the map
lat | the latitude for the center of the map
@@ -26,19 +26,19 @@ format | the format for the image, supported types: `png`, `jpg`
--- | ---
&#124;_ jpg | will have a default quality of 85.
### Bounding Box
#### Bounding Box
#### Definition
##### Definition
```bash
GET /api/v1/map/static/bbox/{token}/{bbox}/{width}/{height}.{format}`
```
#### Params
##### Params
Param | Description
--- | ---
token | the layergroupid token from the map instantiation
token | the `layergroupid` token from the map instantiation
bbox | the bounding box in WGS 84 (EPSG:4326), comma separated values for:
--- | ---
@@ -57,19 +57,19 @@ Note: you can see this endpoint as
```bash
GET /api/v1/map/static/bbox/{token}/{west},{south},{east},{north}/{width}/{height}.{format}`
```
#### Extra options
* Layer: List of layers to be shown in the image (by default `all`), for example `?layer=0,1`.
### Named Map
#### Definition
#### Named Map
##### Definition
```bash
GET /api/v1/map/static/named/{name}/{width}/{height}.{format}
```
#### Params
##### Params
Param | Description
--- | ---
@@ -81,9 +81,9 @@ format | the format for the image, supported types: `png`, `jpg`
--- | ---
&#124;_ jpg | will have a default quality of 85.
A Named Maps static image will get its constraints from the [`view` argument of the Create Named Map function](http://docs.carto.com/carto-engine/maps-api/named-maps/#arguments). If `view` is not defined, it will estimate the extent based on the involved tables, otherwise it fallbacks to `"zoom": 1`, `"lng": 0` and `"lat": 0`.
A Named Maps static image will get its constraints from the [`view` argument of the Create Named Map function]({{site.mapasapi_docs}}/guides/named-maps/). If `view` is not defined, it will estimate the extent based on the involved tables, otherwise it fallbacks to `"zoom": 1`, `"lng": 0` and `"lat": 0`.
#### Layers
##### Layers
The Static Maps API allows for multiple layers of incorporation into the `MapConfig` to allow for maximum versatility in creating a static map. The examples below were used to generate the static image example in the next section, and appear in the specific order designated.
@@ -127,7 +127,7 @@ By manipulating the `"urlTemplate"` custom basemaps can be used in generating st
**CARTO**
As described in the [MapConfig File Format](http://docs.carto.com/carto-engine/maps-api/mapconfig/), a "cartodb" type layer is now just an alias to a "mapnik" type layer as above, intended for backwards compatibility.
As described in the [MapConfig File Format]({{site.mapsapi_docs}}/guides/MapConfig-file-format/), a "cartodb" type layer is now just an alias to a "mapnik" type layer as above, intended for backwards compatibility.
```javascript
{
@@ -143,11 +143,11 @@ As described in the [MapConfig File Format](http://docs.carto.com/carto-engine/m
Additionally, static images from Torque maps and other map layers can be used together to generate highly customizable and versatile static maps.
### Caching
#### Caching
It is important to note that generated images are cached from the live data referenced with the `layergroupid token` on the specified CARTO account. This means that if the data changes, the cached image will also change. When linking dynamically, it is important to take into consideration the state of the data and longevity of the static image to avoid broken images or changes in how the image is displayed. To obtain a static snapshot of the map as it is today and preserve the image long-term regardless of changes in data, the image must be saved and stored locally.
It is important to note that generated images are cached from the live data referenced with the `layergroupid` token on the specified CARTO account. This means that if the data changes, the cached image will also change. When linking dynamically, it is important to take into consideration the state of the data and longevity of the static image to avoid broken images or changes in how the image is displayed. To obtain a static snapshot of the map as it is today and preserve the image long-term regardless of changes in data, the image must be saved and stored locally.
### Limits
#### Limits
* 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
@@ -159,21 +159,21 @@ It is important to note that generated images are cached from the live data refe
attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, &copy; <a href="https://carto.com/attributions">CARTO</a>
{% endhighlight %}
## Examples
### Examples
After instantiating a map from a CARTO account:
#### Call
##### Call
```bash
GET /api/v1/map/static/center/{layergroupid}/{z}/{x}/{y}/{width}/{height}.png
```
#### Response
##### Response
<p class="wrap-border"><img src="https://raw.githubusercontent.com/namessanti/Pictures/master/static_api.png" alt="static-api"/></p>
### MapConfig
#### MapConfig
For this map, the multiple layers, order, and stylings are defined by the MapConfig.

View File

@@ -1,4 +1,4 @@
# Tile Aggregation
## Tile Aggregation
To be able to represent a large amount of data (say, hundred of thousands to millions of points) in a tile. This can be useful both for raster tiles (where the aggregation reduces the number of features to be rendered) and vector tiles (the tile contais less features).
@@ -6,7 +6,7 @@ Aggregation is available only for point geometries. During aggregation the point
- The position of the aggregated point is controlled by the `placement` parameter.
- The aggregated rows always contain at least a column, named `_cdb_feature_count`, which contains the number of the original points that the aggregated point represents.
### Special default aggregation
#### Special default aggregation
When no placement or columns are specified a special default aggregation is performed.
@@ -16,13 +16,13 @@ Regarding the randomness of the sample: currently we use the row with the minimu
The rationale behind having this special aggregation with all the original columns is to provide a mostly transparent way to handle large datasets without having to provide special map configurations for those cases (i.e. preserving the logic used to produce the maps with smaller datasets). [Overviews have been used so far with this intent](https://carto.com/docs/tips-and-tricks/back-end-data-performance/), but they are inflexible.
### User defined aggregations
#### User defined aggregations
When either a explicit placement or columns are requested we no longer use the special, query; we use one determined by the placement (which will default to "centroid"), and it will have as columns only the aggregated columns specified, in addition to `_cdb_feature_count`, which is always present.
We might decide in the future to allow sampling column values for any of the different placement modes.
### Behaviour for raster and vector tiles
#### Behaviour for raster and vector tiles
The vector tiles from a vector-only map will be aggregated by default.
However, Raster tiles (or vector tiles from a map which defines CartoCSS styles) will be aggregated only upon request.
@@ -79,7 +79,7 @@ layer will be aggregated when tiles are requested, both for vector (mvt) and ras
}
```
## Aggregation parameters
### Aggregation parameters
The aggregation parameters for a layer are defined inside an `aggregation` option of the layer:
@@ -96,24 +96,24 @@ The aggregation parameters for a layer are defined inside an `aggregation` optio
}
```
### `placement`
#### `placement`
Determines the kind of aggregated geometry generated:
#### `point-sample`
##### `point-sample`
This is the default placement. It will place the aggregated point at a random sample of the grouped points,
like the default aggregation does. No other attribute is sampled, though, the point will contain the aggregated attributes determined by the `columns` parameter.
#### `point-grid`
##### `point-grid`
Generates points at the center of the aggregation grid cells (squares).
#### `centroid`
##### `centroid`
Generates points with the averaged coordinated of the grouped points (i.e. the points inside each grid cell).
### `columns`
#### `columns`
The aggregated attributes defined by `columns` are computed by a applying an _aggregate function_ to all the points in each group.
Valid aggregate functions are `sum`, `avg` (average), `min` (minimum), `max` (maximum) and `mode` (the most frequent value in the group).
@@ -134,9 +134,9 @@ of the original dataset applying three different aggregate functions.
> Note that you can use the original column names as names of the result, but all the result column names must be unique. In particular, the names `cartodb_id`, `the_geom`, `the_geom_webmercator` and `_cdb_feature_count` cannot be used for aggregated columns, as they correspond to columns always present in the result.
### `resolution`
#### `resolution`
Defines the cell-size of the spatial aggregation grid. This is equivalent to the [CartoCSS `-torque-resolution`](https://carto.com/docs/carto-engine/cartocss/properties-for-torque/#-torque-resolution-float) property of Torque maps.
Defines the cell-size of the spatial aggregation grid. This is equivalent to the [CartoCSS `-torque-resolution`]({{site.styling_cartocss}}/#-torque-resolution-float) property of Torque maps.
The aggregation cells are `resolution`×`resolution` pixels in size, where pixels here are defined to be 1/256 of the (linear) size of a tile.
The default value is 1, so that aggregation coincides with raster pixels. A value of 2 would make each cell to be 4 (2×2) pixels, and a value of
@@ -145,11 +145,11 @@ The default value is 1, so that aggregation coincides with raster pixels. A valu
> Note that is independent of the number of pixels for raster tile or the coordinate resolution (mvt_extent) of vector tiles.
### `threshold`
#### `threshold`
This is the minimum number of (estimated) rows in the dataset (query results) for aggregation to be applied. If the number of rows estimate is less than the threshold aggregation will be disabled for the layer; the instantiation response will reflect that and tiles will be generated without aggregation.
### Example
#### Example
```json
{
@@ -261,4 +261,4 @@ Note that the filtered columns have to be defined with the `columns` parameter,
}
]
}
```
```

View File

@@ -0,0 +1,270 @@
{% comment %}
The original resource for this was:
https://github.com/CartoDB/Windshaft/blob/master/doc/MapConfig-1.4.0.md. However this is internal documenation only. This file (07-mapconfig.md) contains select content from the Windshaft internal doc. *I instructed @rochoa to add new Doc issues if/when they make a change to this content - so that the public docs can also be updated.
{% endcomment %}
## MapConfig File Format
CARTO uses Windshaft as the map tiler library to render multilayer maps with the [Maps API]({{ site.mapsapi_docs }}/). The MapConfig file is where these Windshaft layers are stored and applied. You can configure tiles and use the MapConfig document to request different resources for your map.
This section describes the MapConfig specifications, and required formats, when using the Maps API.
### Layergroup Configurations
The following MapConfig Layergroup configurations are applied using the [RFC 4627](http://www.ietf.org/rfc/rfc4627.txt) JSON format.
Layergroup Configuration | Description | Optional or Required?
--- | ---
`version` | Spec version to use for validation.<br /><br />**Note:** The default value is `"1.0.0"`. | Optional
`extent` | The default map extent for the map projection.<br /><br />**Note:** Currently, only webmercator is supported. | Optional
`srid` | The spatial reference identifier for the map. The default is `3857`. | Optional
`maxzoom` | The maximum zoom level for your map. A request beyond the defined maxzoom returns a 404 error.<br /><br />**Note:** The default value is undefined (infinite). | Optional
`minzoom` | The minimum zoom level for your map. A request beyond the defined minzoom returns a 404 error.<br /><br />**Note:** The default value is `0`. | Optional
`layers` | Defines the layer type, and the layers, in rendering order.<br /><br />**Note:** The following layers options are available: |
--- | ---
<i class="Icon Icon--s5 Icon--cGrey Icon--mAlign Icon--indent"></i> type | A string value that defines the layer type. You can define up to four values:<br /><br />`mapnik`, rasterized tiles<br /><br />`cartodb`, an alias for mapnik (for backward compatibility)<br /><br />`torque`, render vector tiles in torque format<br /><br />`http`, load tiles over HTTP<br /><br />`plain`, color or background image url<br /><br />`named`, use a Named Map as a layer | Required
<i class="Icon Icon--s5 Icon--cGrey Icon--mAlign Icon--indent"></i> options | An object value that sets different options for each layer type.<br /><br />**Note:** Options that are not defined in different layers will be discarded. | Required
#### Example of MapConfig
{% highlight json %}
{
"version": "1.7.0",
"extent": [-20037508.5, -20037508.5, 20037508.5, 20037508.5],
"srid": 3857,
"maxzoom": 18,
"minzoom": 3,
"layers": [
{
"type": "mapnik",
"options": {
"sql": "select * from table",
"cartocss": "#table { marker-placement: point; }",
"cartocss_version": "2.3.0"
}
}
]
}
{% endhighlight %}
---
### Mapnik Layer Options
If you are using Mapnik as a layer resource, the following configurations are required in your MapConfig file.
Mapnik Layer Option | Description | Optional or Required?
--- | ---
`sql` | A string value, the SQL request to the user database that will fetch the rendered data.<br /><br />**Tip:** The SQL request should include the following Mapnik layer configurations: `geom_column`, `interactivity`, and `attributes`, as described in this section.<br /><br />**Note:** The SQL request may contain substitutions tokens, such as `!bbox!`, `!pixel_width!`, and `!pixel_height!`. It is suggested to define the layergroup `minzoom` and `extent` variables to prevent errors. | Required
`cartocss` | A string value, specifying the CartoCSS style to render the tiles. If this is not present, only vector tiles can be requested for this layer. For a map to be valid either all the layers or none of them must have CartoCSS style.<br /><br />**Note:** The CartoCSS specification is dependent on the layer type. For details, see [mapnik-reference.json](https://github.com/mapnik/mapnik-reference). | Optional
`cartocss_version` | A string value, specifying the CartoCSS style version of the CartoCSS attribute.<br /><br />**Note:** The CartoCSS version is specific to the layer type. | Optional
`geom_column` | The name of the column containing the geometry. The default is `the_geom_webmercator`.<br /><br />*You must specify this value as part of the Mapnik layer `SQL`configuration. | *Optional
`geom_type` | Defines the type of column as either `geometry` (the default) or `raster`.<br /><br />**Note:** `geom_type` is not compatible with the Mapnik layer `interactivity` option. | Optional
`raster_band` | Defines the raster band (this option is only applicable when the `geom_type=raster`. The default value is `0`.<br /><br />**Note:** If the default, or no value is specified, raster bands are interpreted as either: grayscale (for single bands), RGB (for 3 bands), or RGBA (for 4 bands). | Optional
`srid` | The spatial reference identifier for the geometry column. The default is `3857`. | Optional
`affected_tables` | A string of values containing the tables that the Mapnik layer `SQL` configuration is using. This value is used if there is a problem guessing what the affected tables are from the SQL configuration (i.e. when using PL/SQL functions). | Optional
`interactivity` | A string of values that contains the fields rendered inside grid.json. All the parameters should be exposed as a result of executing the Mapnik layer `SQL` query.<br /><br />**Note:** `interactivity` is not compatible with the Mapnik layer `geom_type` option. For example, you cannot create a layergroup instance with a raster layer by defining the `geom_type=raster`.<br /><br />*You must specify this value as part of the Mapnik layer `SQL` configuration. | *Optional
`attributes`<a name="attributes"></a> | The id and column values returned by the Mapnik attributes service. (This option is disabled if no configuration is defined).<br /><br />*You must specify this value as part of the Mapnik layer `SQL`configuration.| *Optional
--- | ---
<i class="Icon Icon--s5 Icon--cGrey Icon--mAlign Icon--indent"></i> id | The key value used to fetch columns. | Required
<i class="Icon Icon--s5 Icon--cGrey Icon--mAlign Icon--indent"></i> columns | A string of values (columns) returned by the Mapnik attribute service. | Required
#### Example of Mapnik MapConfig
{% highlight json %}
{
"type": "mapnik",
"options": {
"sql": "select * from table",
"cartocss": "#layer { marker-placement: point; }",
"cartocss_version": "2.3.0",
"geom_column": "the_geom_webmercator",
"geom_type": "geometry",
"interactivity": [ "column1", "column2", "..."],
"attributes": {
"id": "cartodb_id",
"columns": ["column1", "column2"]
}
}
}
{% endhighlight %}
### Torque Layer Options
If you are using Torque as a layer resource, the following configurations are required in your MapConfig file. For more details about Torque layers in general, see the [Torque API]({{ site.torque_docs}}/reference/) documentation.
Torque Layer Option | Description | Optional or Required?
--- | ---
`sql` | A string value, the SQL request to the user database that will fetch the rendered data.<br /><br />**Tip:** The SQL request should include the following Torque layer configurations: `geom_column`, `interactivity`, and `attributes`, as described in this section. | Required
`cartocss` | A string value, specifying the CartoCSS style to render the tiles.<br /><br />**Note:** The CartoCSS specification is dependent on the layer type. For details, see [Torque cartocss-reference.js](https://github.com/CartoDB/torque/blob/master/lib/torque/cartocss_reference.js).| Required
`cartocss_version` | A string value, specifying the CartoCSS style version of the CartoCSS attribute.<br /><br />**Note:** The CartoCSS version is specific to the layer type. | Required
`step` | The number of [animation steps]({{site.styling_cartocss}}/-#torque-frame-count-number) to render when requesting a torque.png tile. The default value is `0`. | Optional
`geom_column` | The name of the column containing the geometry. The default is `the_geom_webmercator`.<br /><br />*You must specify this value as part of the Torque layer `SQL`configuration. | *Optional
`srid` | The spatial reference identifier for the geometry column. The default is `3857`. | Optional
`affected_tables` | A string of values containing the tables that the Mapnik layer `SQL` configuration is using. This value is used if there is a problem guessing what the affected tables are from the SQL configuration (i.e. when using PL/SQL functions). | Optional
`attributes` | The id and column values returned by the Torque attributes service. (This option is disabled if no configuration is defined).<br /><br />*You must specify this value as part of the Torque layer `SQL`configuration.| *Optional
--- | ---
<i class="Icon Icon--s5 Icon--cGrey Icon--mAlign Icon--indent"></i> id | The key value used to fetch columns. | Required
<i class="Icon Icon--s5 Icon--cGrey Icon--mAlign Icon--indent"></i> columns | A string of values (columns) returned by the Torque attribute service. | Required
#### Example of Torque MapConfig
{% highlight json %}
{
"type": "torque",
"options": {
"sql": "select * from table",
"cartocss": "#layer { ... }",
"cartocss_version": "1.0.0",
"geom_column": "the_geom_webmercator"
}
}
{% endhighlight %}
### HTTP Layer Options
If you are using an HTTP destination as the resource for a map layer, the following configurations are required in your MapConfig file.
HTTP Layer Option | Description | Optional or Required?
--- | ---
`urlTemplate` | A string value, end URL, from where the tile data is retrieved. _URLs must be included in the configuration whitelist to be valid._ <br /><br />**Note:** The {String} value includes:<br /><br />`{z}` as the zoom level<br /><br />`{x} and {y}` as the tile coordinates<br /><br />Optionally, the subdomain `{s}` may be included as part of the `urlTemplate` configuration. Otherwise, you can define the `subdomains` separately, as shown below. | Required
`subdomains` | A string of values used to retrieve tiles from different subdomains. The default value is [`a`, `b`, `c`] when `{s}` is defined in the `urlTemplate` configuration. Otherwise, the default value is `[ ]`.<br /><br />**Note:** The subdomains value will consistently replace the `{s}` value defined in the `urlTemplate`.| Optional
`tms` | A boolean value that specifies whether the tile is using Tile Map Service format. The default value is `false`.<br /><br />**Note:** If the value is `true`, the TMS inverses the Y axis numbering for tiles. | Optional
#### Example of HTTP MapConfig
{% highlight json %}
{
"type": "http",
"options": {
"urlTemplate": "http://{s}.example.com/{z}/{x}/{y}.png",
"subdomains": ["a", "b", "c"],
"tms": false
}
}
{% endhighlight %}
### Plain Layer Options
If you are using plain layer options as your map resource, the following configurations are required in your MapConfig file.
_**Note:** At least one of the plain layer options (either `color` or `imageUrl`) must be defined. If both options are defined, only `color` is used as part of the plain layer configuration._
Plain Layer Option | Description | Optional or Required?
--- | ---
`color` | A string value of numbers that defines the valid colors to include. The default value is `null`. Valid colors include:<br /><br />- A string value that includes CSS colors (i.e. `blue`) or a hex color string (i.e. `#0000ff`)<br /><br />- An integer array of r,g,b values (i.e. `[255,0,0]`)<br /><br />- An integer array of r,g,b,a values (i.e. `[255,0,0,128]`)<br /><br />* If **only** the `color` value is used for a plain layer, this value is Required.<br /><br />* If **both** `color` and `imageUrl` are defined, only the `color` value is used for the plain layer configuration.| *Both
`imageUrl` | A string value, end URL, from where the image is retrieved. The default value is `null`.<br /><br />* If **only** the `imageUrl` value is used for a plain layer, this value is Required.<br /><br />* If `color` is defined, this `imageUrl` value is ignored. | *Both
#### Example of Plain MapConfig
{% highlight json %}
{
"type": "plain",
"options": {
"color": "blue",
"imageUrl": "http://example.com/background.png"
}
}
{% endhighlight %}
### Named Map Layer Options
You can use a [Named Map]({{site.mapsapi_docs}}/guides/named-maps/) as a map layer. Note the following limitations before referencing the MapConfig options for a Named Map layer.
_**Limitations:**_
- A Named Map will not allow you to have `named` type layers inside of your template layergroup's layers definition
- A `named` layer does not allow Named Maps from other accounts. You can only use Named Maps from the _same_ user account
If you are using `named` layer options as your map resource, the following configurations are required in your MapConfig file.
Named Layer Option | Description | Optional or Required?
--- | ---
`name` | A string value, the name for the Named Map to use. | Required
`config` | An object, the replacement values for the Named Map's template placeholders. | Optional
`auth_tokens` | Strings array, the authorized tokens in case the Named Map has auth method set to `token`. | Optional
#### Example of Named MapConfig
{% highlight json %}
{
"type": "named",
"options": {
"name": "world_borders",
"config": {
"color": "#000"
},
"auth_tokens": ["token1", "token2"]
}
}
{% endhighlight %}
### Aggregation Options
The data used to render tiles, or contained in the tiles (for the case of vector tiles), can be spatially [aggregated]({{site.mapsapi_docs}}/guides/named-maps/) under some circumstances.
An `aggregation` attribute can be used in the layer `options` to control the aggregation. A value of `false` will disable aggregation for the layer. Otherwise, an object can be passed with the following aggregation parameters:
Parameter|Description|Default value
`placement`|Determines the kind of aggregated geometry generated ("point-sample", "point-grind" or "centroid").|"centroid"
`columns`|Defines aggregated columns; each one by an "aggregate_function" ("sum", "avg", "min, "max", "mode", "count") and "aggregated_column" name.|
`resolution`|Defines the cell-size of the spatial aggregation grid.|1 (for 256x256 cells per tile)
`threshold`|Minimum rows in the dataset to apply aggregation.
#### Example of Aggregation MapConfig
{% highlight json %}
{
"version": "1.7.0",
"extent": [-20037508.5, -20037508.5, 20037508.5, 20037508.5],
"srid": 3857,
"maxzoom": 18,
"minzoom": 3,
"layers": [
{
"type": "mapnik",
"options": {
"sql": "select * from table",
"cartocss": "#table { marker-width: [total]; marker-fill: ramp(value, (red, green, blue), jenks); }",
"cartocss_version": "2.3.0",
"aggregation": {
"placement": "centroid",s
"columns": {
"value": {
"aggregate_function": "avg",
"aggregated_column": "value"
},
"total": {
"aggregate_function": "sum",
"aggregated_column": "value"
}
},
"resolution": 2, // Aggregation cell is 2x2 pixels
"threshold": 500000
}
}
}
]
}
{% endhighlight %}
### MapConfig Requirements
All of these are MapConfig requirements for [Anonymous Maps]({{site.mapsapi_docs}}/guides/anonymous-maps/#retrieve-resources-from-the-layergroup).
- Identified by `{z}/{x}/{y}` path
- If applicable, additionally identified by `LAYER_NUMBER`
- Can be of different formats:
- png
- grid.json
- torque.json
- Static images/previews
- With a center or a bounding box
- Attributes
-Identified by LAYER_NUMBER and FEATURE_ID
**Tip:** The MapConfig file may be extended for specific uses. For example, [Windshaft-CartoDB](https://github.com/CartoDB/Windshaft-cartodb/blob/master/docs/MultiLayer-API.md) defines the addition of a `stat_tag` element in the config. This extension is also covered as part of the [Named Map Layer Options](#named-map-layer-options).

View File

@@ -1,14 +1,16 @@
# 1. Purpose
## MapConfig Aggregation Extension
### 1. Purpose
This specification describes an extension for
[MapConfig 1.7.0](https://github.com/CartoDB/Windshaft/blob/master/doc/MapConfig-1.7.0.md) version.
# 2. Changes over specification
### 2. Changes over specification
This extension introduces a new layer options for aggregated data tile generation.
## 2.1 Aggregation options
#### 2.1 Aggregation options
The layer options attribute is extended with a new optional `aggregation` attribute.
The value of this attribute can be `false` to explicitly disable aggregation for the layer.
@@ -55,8 +57,8 @@ The value of this attribute can be `false` to explicitly disable aggregation for
}
```
# History
### History
## 1.0.0
#### 1.0.0
- Initial version

View File

@@ -1,16 +1,18 @@
# 1. Purpose
## MapConfig Analyses Extension
### 1. Purpose
This specification describes an extension for
[MapConfig 1.4.0](https://github.com/CartoDB/Windshaft/blob/master/doc/MapConfig-1.4.0.md) version.
# 2. Changes over specification
### 2. Changes over specification
This extension targets layers with `sql` option, including layer types: `cartodb`, `mapnik`, and `torque`.
It extends MapConfig with a new attribute: `analyses`.
## 2.1 Analyses attribute
#### 2.1 Analyses attribute
The new analyses attribute must be an array of analyses as per [camshaft](https://github.com/CartoDB/camshaft). Each
analysis must adhere to the [camshaft-reference](https://github.com/CartoDB/camshaft/blob/0.8.0/reference/versions/0.7.0/reference.json) specification.
@@ -37,7 +39,7 @@ Basic analyses example:
]
```
# 2.2. Integration with layers
### 2.2. Integration with layers
As pointed before an analysis node id can be referenced from layers to consume its output query.
@@ -57,7 +59,7 @@ The layer consuming the output must reference it with the following option:
}
```
## 2.3. Complete example
#### 2.3. Complete example
```
{
@@ -86,8 +88,8 @@ The layer consuming the output must reference it with the following option:
}
```
# History
### History
## 1.0.0
#### 1.0.0
- Initial version

View File

@@ -1,18 +1,20 @@
# 1. Purpose
## MapConfig Dataviews Extension
### 1. Purpose
This specification describes an extension for
[MapConfig 1.4.0](https://github.com/CartoDB/Windshaft/blob/master/doc/MapConfig-1.4.0.md) version.
# 2. Changes over specification
### 2. Changes over specification
This extension depends on Analyses extension. It extends MapConfig with a new attribute: `dataviews`.
It makes possible to get tabular data from analysis nodes: aggregated lists, aggregations, and histograms.
## 2.1. Dataview types
#### 2.1. Dataview types
### Aggregation
##### Aggregation
An aggregation is a list with aggregated results by a column and a given aggregation function.
@@ -56,7 +58,7 @@ Expected output
}
```
### Histograms
##### Histograms
Histograms represent the data distribution for a column.
@@ -88,7 +90,7 @@ Expected output
}
```
### Formula
##### Formula
Formulas given a final value representing the whole dataset.
@@ -124,7 +126,7 @@ Result
```
## 2.2 Dataviews attribute
#### 2.2 Dataviews attribute
The new dataviews attribute must be a dictionary of dataviews.
@@ -145,7 +147,7 @@ The layer consuming the output must reference it with the following option:
}
```
## 2.3. Complete example
#### 2.3. Complete example
```
{
@@ -185,16 +187,16 @@ The layer consuming the output must reference it with the following option:
}
```
## 3. Filters
#### 3. Filters
Camshaft's analyses expose a filtering capability and `aggregation` and `histogram` dataviews get them for free with
this extension. Filters are available with the very dataview id, so if you have a "basic_histogram" histogram dataview
you can filter with a range filter with "basic_histogram" name.
## 3.1 Filter types
#### 3.1 Filter types
### Category
##### Category
Allows to remove results that are not contained within a set of elements.
Initially this filter can be applied to a `numeric` or `text` columns.
@@ -208,7 +210,7 @@ Params
}
```
### Range filter
##### Range filter
Allows to remove results that dont satisfy numeric min and max values.
Filter is applied to a numeric column.
@@ -222,13 +224,13 @@ Params
}
```
## 3.2. How to apply filters
#### 3.2. How to apply filters
Filters must be applied at map instantiation time.
With :mapconfig as a valid MapConfig and with :filters (a valid JSON) as:
### Anonymous map
##### Anonymous map
`GET /api/v1/map?config=:mapconfig&filters=:filters`
@@ -241,7 +243,7 @@ If in the future we need to support a bigger filters param and it doesnt fit
`POST /api/v1/map`
with `BODY={“config”: :mapconfig, “filters”: :filters}`
### Named map
##### Named map
Assume :params (a valid JSON) as named maps params, like in: `{“color”: “red”}`
@@ -257,7 +259,7 @@ If, again, in the future we need to support a bigger filters param that doesn
with `BODY={“config”: :params, “filters”: :filters}`
## 3.3 Bounding box special filter
#### 3.3 Bounding box special filter
A bounding box filter allows to remove results that dont satisfy a geospatial range.
@@ -270,8 +272,8 @@ param must be in the form `west,south,east,north`.
So applying a bbox filter to a dataview looks like:
GET /api/v1/map/:layergroupid/dataview/:dataview_name?bbox=-90,-45,90,45
# History
### History
## 1.0.0-alpha
#### 1.0.0-alpha
- WIP document

View File

@@ -1,14 +1,16 @@
# 1. Purpose
## MapConfig Named Maps Extension
### 1. Purpose
This specification describes an extension for
[MapConfig 1.3.0](https://github.com/CartoDB/Windshaft/blob/master/doc/MapConfig-1.3.0.md) version.
# 2. Changes over specification
### 2. Changes over specification
This extension introduces a new layer type so it's possible to use a Named Map by its name as a layer.
## 2.1 Named layers definition
#### 2.1 Named layers definition
```javascript
{
@@ -42,15 +44,15 @@ This extension introduces a new layer type so it's possible to use a Named Map b
}
```
## 2.2 Limitations
#### 2.2 Limitations
1. A Named Map will not allow to have `named` type layers inside their templates layergroup's layers definition.
2. A `named` layer does not allow Named Maps form other accounts, it's only possible to use Named Maps from the very
same user account.
# History
### History
## 1.0.0
#### 1.0.0
- Initial version

View File

@@ -1,4 +1,4 @@
The Windshaft-CartoDB MultiLayer API extends the [Windshaft MultiLayer API](https://github.com/CartoDB/Windshaft/blob/master/doc/Multilayer-API.md) in a few ways.
The Windshaft-CartoDB MultiLayer API extends the [Windshaft MultiLayer API](https://github.com/CartoDB/Windshaft/blob/master/doc/internal/multilayer-API.md) in a few ways.
## Last modification timestamp embedded in the token
@@ -25,4 +25,4 @@ Windshaft-CartoDB adds the following attributes in the response object
## Stats tag
Windshaft-CartoDB adds support for a ``stat_tag`` element in the multilayer configuration to help [stats](https://github.com/CartoDB/Windshaft-cartodb/wiki/Redis-stats-format) gathering.
Windshaft-CartoDB adds support for a ``stat_tag`` element in the multilayer configuration to help [stats](https://github.com/CartoDB/Windshaft-cartodb/wiki/Redis-stats-format) gathering.

1497
docs/reference/swagger.yaml Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,33 @@
## Support Options
Feeling stuck? There are many ways to find help.
* Ask a question on [GIS StackExchange](https://gis.stackexchange.com/questions/tagged/carto) using the `CARTO` tag.
* [Report an issue](https://github.com/CartoDB/cartodb/issues) in Github.
* Engine Plan customers have additional access to enterprise-level support through CARTO's support representatives.
If you just want to describe an issue or share an idea, just <a class="typeform-share" href="https://cartohq.typeform.com/to/mH6RRl" data-mode="popup" target="_blank"> send your feedback</a>
### Issues on Github
If you think you may have found a bug, or if you have a feature request that you would like to share with the Maps API team, please [open an issue](https://github.com/CartoDB/Windshaft-cartodb/issues/new).
### Community support on GIS Stack Exchange
GIS Stack Exchange is the most popular community in the geospatial industry. This is a collaboratively-edited question and answer site for geospatial programmers and technicians. It is a fantastic resource for asking technical questions about developing and maintaining your application.
When posting a new question, please consider the following:
* Read the GIS Stack Exchange [help](https://gis.stackexchange.com/help) and [how to ask](https://gis.stackexchange.com/help/how-to-ask) pages for guidelines and tips about posting questions.
* Be very clear about your question in the subject. A clear explanation helps those trying to answer your question, as well as those who may be looking for information in the future.
* Be informative in your post. Details, code snippets, logs, screenshots, etc. help others to understand your problem.
* Use code that demonstrates the problem. It is very hard to debug errors without sample code to reproduce the problem.
### Engine Plan Customers
Engine Plan customers have additional support options beyond general community support. As per your account Terms of Service, you have access to enterprise-level support through CARTO's support representatives available at [enterprise-support@carto.com](mailto:enterprise-support@carto.com)
In order to speed up the resolution of your issue, provide as much information as possible (even if it is a link from community support). This allows our engineers to investigate your problem as soon as possible.
If you are not yet CARTO customer, browse our [plans & pricing](https://carto.com/pricing/) and find the right plan for you.

View File

@@ -0,0 +1,36 @@
## Contribute
CARTO platform is an open-source ecosystem. You can read about the [fundamentals]({{site.fundamental_docs}}/components/) of CARTO architecture and its components.
We are more than happy to receive your contributions to the code and the documentation as well.
## Filling a ticket
If you want to open a new issue in our repository, please follow these instructions:
1. Descriptive title.
2. Write a good description, it always helps.
3. Specify the steps to reproduce the problem.
4. Try to add an example showing the problem.
## Contributing code
Best part of open source, collaborate in Maps API code!. We like hearing from you, so if you have any bug fixed, or a new feature ready to be merged, those are the steps you should follow:
1. Fork the repository.
2. Create a new branch in your forked repository.
3. Commit your changes. Add new tests if it is necessary.
4. Open a pull request.
5. Any of the maintainers will take a look.
6. If everything works, it will merged and released \o/.
If you want more detailed information, this [GitHub guide](https://guides.github.com/activities/contributing-to-open-source/) is a must.
## Completing documentation
Maps API documentation is located in ```docs/```. That folder is the content that appears in the [Developer Center](https://carto.com/developers/maps-api/). Just follow the instructions described in [contributing code](#contributing-code) and after accepting your pull request, we will make it appear online :).
**Tip:** A convenient, easy way of proposing changes in documentation is by using the GitHub editor directly on the web. You can easily create a branch with your changes and make a PR from there.
## Submitting contributions
You will need to sign a Contributor License Agreement (CLA) before making a submission. [Learn more here](https://carto.com/contributions).

View File

@@ -0,0 +1,121 @@
## Rate limiting
Rate limits ensure that CARTO platform is not flooded with so many requests it does not have the time and resources to service them all.
Of course, there is nothing we can do to prevent people from actually sending as many requests to our platform as they want, but requests over a user's rate limit will be acknowledged with an error so that the sender understands they need to lower the rate at which requests are sent before they are serviced again.
Currently, Maps API is affected by rate limiting.
### Per user and endpoint
Rate limit is on a per-user basis (or more accurately described, per user access) and by endpoint. For example, suppose you have 2 different apps (with 2 different maps) and both call to the same endpoint that allows 100 requests per second. Both apps/maps "share" 100 requests per second regardless the map calling to this endpoint.
### How it works
We are using the [generic cell rate algorithm](https://en.wikipedia.org/wiki/Generic_cell_rate_algorithm), a [leaky bucket](https://en.wikipedia.org/wiki/Leaky_bucket) algorithm type.
The main keys to keep in mind about this algorithm and our implementation are:
- We allow a request every a certain time period
```
If an endpoint has a limit of 5 requests per second, you will have a request available every 200ms and when you spend all the available requests, you will need to wait 200ms to have another available request, instead of 1 second
```
- Most of the endpoints are limited per second
```
If an endpoint has a limit of 5 requests per second, after a second without requests, you will have at least 5 available requests
```
- Most of the endpoints allow an initial burst equal to the number of requests per second
```
If an endpoint has a limit of 5 requests per second, initially you will have 5 available requests
```
### Caches
In computing, a cache is a high-speed data storage layer which stores data, typically a set of data, so that future requests for that data are served up faster than by accessing the original location.
CARTO caching allows you to efficiently reuse previously retrieved or computed data, as the data in a cache is stored by CARTO in fast access hardware in combination with specific software to manage this.
Resources accessed by caches don't count against the limits. That is, any request that is handled by any cache layer is out of limits. You can always know which resources are served through cache looking at the `X-Cache` HTTP Header.
### HTTP Headers and Response Codes
When an application exceeds the rate limit for a given API endpoint, the API will return an HTTP `429 Too Many Requests` error.
Use the HTTP headers in order to understand where the application is at for a given rate limit, on the method that was just utilized. Note that the HTTP headers are contextual. That is, they indicate the rate limit for the user context. If you have multiple apps (maps) accessing to their resources with the same user, HTTP headers are related to that user.
- **Carto-Rate-Limit-Limit**: total allowed requests
- **Carto-Rate-Limit-Remaining**: remaining requests
- **Retry-After**: seconds until next available request (returns `-1` if the current request is allowed)
- **Carto-Rate-Limit-Reset**: seconds until the limit will reset to its maximum capacity
### Tips
We only have 1 tip:
- If you receive a rate limit error, you must wait the seconds indicated by the `Retry-After` HTTP header (most of the time will be 1 second)
### Rate Limits Chart
Below, you can find the values of the rate limit by user account type and endpoint. Note that endpoints not listed in the chart are disabled by default.
#### Enterprise plans
|Endpoint |Request |Time period |Burst |
| :--- | ---: | ---: | ---: |
| GET /api/v1/map <br> POST /api/v1/map |10 |1 |10 |
| GET /api/v1/map/static/center/{token}/{z}/{lat}/{lng}/{width}/{height}.{format} <br> GET /api/v1/map/static/bbox/{token}/{west},{south},{east},{north}/{width}/{height}.{format} |3 |1 |3 |
| GET /api/v1/map/static/named/{template_id}/{width}/{height}.{format} |3 |1 |3 |
| GET /api/v1/map/{token}/{layer}/widget/{dataviewName} <br> GET /api/v1/map/{token}/dataview/{dataviewName} |25 |1 |25 |
| GET /{token}/{layer}/widget/{dataviewName}/search <br> GET /{token}/dataview/{dataviewName}/search |3 |1 |3 |
| GET /api/v1/map/{token}/analysis/node/{nodeId} |3 |1 |3 |
| GET /api/v1/map/{token}/{z}/{x}/{y}@{scale_factor}?x.{format} <br> GET /api/v1/map/{token}/{z}/{x}/{y}.{format} <br> GET /api/v1/map/{token}/{layer}/{z}/{x}/{y}.{format} |120<br> 1500 |1<br> 60 |120<br> 750 |
| GET /api/v1/map/{token}/{layer}/attributes/{fid} |10 |1 |10 |
| GET /api/v1/map/named |3 |1 |3 |
| POST /api/v1/map/named |3 |1 |3 |
| GET /api/v1/map/named/{template_id} |10 |1 |10 |
| POST /api/v1/map/named/{template_id} <br> GET /api/v1/map/named/{template_id}/jsonp |10 |1 |10 |
| PUT /api/v1/map/named/{template_id} |10 |1 |10 |
| DELETE /api/v1/map/named/{template_id} |3 |1 |3 |
| GET /api/v1/map/named/{template_id}/{layer}/{z}/{x}/{y}.{format} |25 |1 |25 |
#### Individual plans
|Endpoint |Request |Time period |Burst |
| :--- | ---: | ---: | ---: |
| GET /api/v1/map <br> POST /api/v1/map |5 |1 |5 |
| GET /api/v1/map/static/center/{token}/{z}/{lat}/{lng}/{width}/{height}.{format} <br> GET /api/v1/map/static/bbox/{token}/{west},{south},{east},{north}/{width}/{height}.{format} |1 |1 |1 |
| GET /api/v1/map/static/named/{template_id}/{width}/{height}.{format} |1 |1 |1 |
| GET /api/v1/map/{token}/{layer}/widget/{dataviewName} <br> GET /api/v1/map/{token}/dataview/{dataviewName} |15 |1 |15 |
| GET /{token}/{layer}/widget/{dataviewName}/search <br> GET /{token}/dataview/{dataviewName}/search |1 |1 |1 |
| GET /api/v1/map/{token}/analysis/node/{nodeId} |1 |1 |1 |
| GET /api/v1/map/{token}/{z}/{x}/{y}@{scale_factor}?x.{format} <br> GET /api/v1/map/{token}/{z}/{x}/{y}.{format} <br> GET /api/v1/map/{token}/{layer}/{z}/{x}/{y}.{format} |40<br> 600 |1<br> 60 |40<br> 300 |
| GET /api/v1/map/{token}/{layer}/attributes/{fid} |5 |1 |5 |
| GET /api/v1/map/named |1 |1 |1 |
| POST /api/v1/map/named |1 |1 |1 |
| GET /api/v1/map/named/{template_id} |5 |1 |5 |
| POST /api/v1/map/named/{template_id} <br> GET /api/v1/map/named/{template_id}/jsonp |5 |1 |5 |
| PUT /api/v1/map/named/{template_id} |5 |1 |5 |
| DELETE /api/v1/map/named/{template_id} |1 |1 |1 |
| GET /api/v1/map/named/{template_id}/{layer}/{z}/{x}/{y}.{format} |10 |1 |10 |
#### Free plans
|Endpoint |Request |Time period |Burst |
| :--- | ---: | ---: | ---: |
| GET /api/v1/map <br> POST /api/v1/map |2 |1 |2 |
| GET /api/v1/map/static/center/{token}/{z}/{lat}/{lng}/{width}/{height}.{format} <br> GET /api/v1/map/static/bbox/{token}/{west},{south},{east},{north}/{width}/{height}.{format} |1 |1 |1 |
| GET /api/v1/map/static/named/{template_id}/{width}/{height}.{format} |1 |1 |1 |
| GET /api/v1/map/{token}/{layer}/widget/{dataviewName} <br> GET /api/v1/map/{token}/dataview/{dataviewName} |10 |1 |10 |
| GET /{token}/{layer}/widget/{dataviewName}/search <br> GET /{token}/dataview/{dataviewName}/search |1 |1 |1 |
| GET /api/v1/map/{token}/analysis/node/{nodeId} |1 |1 |1 |
| GET /api/v1/map/{token}/{z}/{x}/{y}@{scale_factor}?x.{format} <br> GET /api/v1/map/{token}/{z}/{x}/{y}.{format} <br> GET /api/v1/map/{token}/{layer}/{z}/{x}/{y}.{format} |20<br> 600 |1<br> 60 |20<br> 300 |
| GET /api/v1/map/{token}/{layer}/attributes/{fid} |2 |1 |2 |
| GET /api/v1/map/named |1 |1 |1 |
| POST /api/v1/map/named |1 |1 |1 |
| GET /api/v1/map/named/{template_id} |2 |1 |2 |
| POST /api/v1/map/named/{template_id} <br> GET /api/v1/map/named/{template_id}/jsonp |2 |1 |2 |
| PUT /api/v1/map/named/{template_id} |2 |1 |2 |
| DELETE /api/v1/map/named/{template_id} |1 |1 |1 |
| GET /api/v1/map/named/{template_id}/{layer}/{z}/{x}/{y}.{format} |10 |1 |10 |

View File

@@ -0,0 +1,32 @@
## Timeout limit
Our APIs work following a request <-> response model. While CARTO is busy getting that action done or retrieving that information, part of our infrastructure is devoted to that process and is therefore unavailable for any other user. Typically this is not a problem, as most requests get serviced quickly enough. However, certain requests can take a long time to process, either by design (e.g., updating a huge table) or by mistake. To prevent this long-running queries from effectively blocking the usage of our platform resources, CARTO will discard requests that cannot be fulfilled in less than a certain amount of time.
Maps API is affected by this kind of limiting.
### Per User
Timeout limit is on a per-user basis (or more accurately described, per user access).
### How it works
Every query has a statement timeout. When a request reaches that value, the response returns an error.
### Response Codes
When query exceeds the timeout limit, the API will return an HTTP `429 Too Many Requests` error.
### Tips
You are able to avoid common issues that trigger timeout limits following these actions:
- Always use database indexes
- Try to use batch API to insert/update/delete data
### Timeout Limits Chart
Below, you can find the values of the timeout limit by user account type.
|Enterprise plans |Individual plans |Free plans |
| --- | --- | --- |
| 25 seconds | 15 seconds | 5 seconds |

View File

@@ -0,0 +1,16 @@
## Quota limiting
CARTO platform imposes limits on how much data you can store at CARTO, for every user account and organization. You can learn more about this topic by reading the [fundamentals about limits]({{site.fundamental_docs}}/limits/of the CARTO platform.
Maps API is affected by this kind of limiting.
### Quota Limits Chart
Below, you can find the values of the different quota limits by user account type.
|Limit |Enterprise plans |Individual plans |Free plans |
| :--- | ---: | ---: | ---: |
| Maximum Static Map image size |4000 X 4000 pixels |4000 X 4000 pixels |4000 X 4000 pixels |
| Maximum number of Named Maps |4096 |4096 |4096 |
| Maximum number of layers |10 |8 |8 |
| Maximum number of layers |16 |8 |8 |

View File

@@ -1,9 +1,10 @@
Windshaft-cartodb metrics
=========================
See [Windshaft metrics documentation](https://github.com/CartoDB/Windshaft/blob/master/doc/metrics.md) to understand the full picture.
## Metrics
See [metrics guide](https://github.com/CartoDB/Windshaft/blob/master/doc/metrics.md) to understand the full picture.
The next list includes the API endpoints, each endpoint may have several inner timers, some of them are displayed within this list as subitems. Find the description for them in the Inner timers section.
## Timers
### Timers
- **windshaft-cartodb.flush_cache**: time to flush the tile and sql cache
- **windshaft-cartodb.get_template**: time to retrieve an specific template
- **windshaft-cartodb.delete_template**: time to delete an specific template
@@ -39,4 +40,3 @@ Again, each inner timer may have several inner timers.
- **setDBConn**: time to retrieve from redis and set db host and db name from a user
- **setDBParams**: time to prepare all db params to be able to connect/query a database, see *setDBAuth* and *setDBConn*
- **tablePrivacy_getUserDBName**: time to retrieve from redis the database for a user

View File

@@ -1,16 +1,18 @@
'use strict';
const { Router: router } = require('express');
const RedisPool = require('redis-mpool');
const cartodbRedis = require('cartodb-redis');
const windshaft = require('windshaft');
const { factory: windshaftFactory } = require('windshaft');
const PgConnection = require('../backends/pg_connection');
const PgConnection = require('../backends/pg-connection');
const AnalysisBackend = require('../backends/analysis');
const AnalysisStatusBackend = require('../backends/analysis-status');
const DataviewBackend = require('../backends/dataview');
const TemplateMaps = require('../backends/template_maps.js');
const PgQueryRunner = require('../backends/pg_query_runner');
const TemplateMaps = require('../backends/template-maps');
const PgQueryRunner = require('../backends/pg-query-runner');
const StatsBackend = require('../backends/stats');
const AuthBackend = require('../backends/auth');
@@ -18,13 +20,16 @@ const UserLimitsBackend = require('../backends/user-limits');
const OverviewsMetadataBackend = require('../backends/overviews-metadata');
const FilterStatsApi = require('../backends/filter-stats');
const TablesExtentBackend = require('../backends/tables-extent');
const ClusterBackend = require('../backends/cluster');
const PubSubMetricsBackend = require('../backends/metrics');
const LayergroupAffectedTablesCache = require('../cache/layergroup_affected_tables');
const SurrogateKeysCache = require('../cache/surrogate_keys_cache');
const VarnishHttpCacheBackend = require('../cache/backend/varnish_http');
const LayergroupAffectedTablesCache = require('../cache/layergroup-affected-tables');
const SurrogateKeysCache = require('../cache/surrogate-keys-cache');
const VarnishHttpCacheBackend = require('../cache/backend/varnish-http');
const FastlyCacheBackend = require('../cache/backend/fastly');
const NamedMapProviderCache = require('../cache/named_map_provider_cache');
const NamedMapsCacheEntry = require('../cache/model/named_maps_entry');
const NamedMapProviderCache = require('../cache/named-map-provider-cache');
const NamedMapsCacheEntry = require('../cache/model/named-maps-entry');
const NamedMapProviderCacheReporter = require('../stats/reporter/named-map-provider-cache');
const SqlWrapMapConfigAdapter = require('../models/mapconfig/adapter/sql-wrap-mapconfig-adapter');
const MapConfigNamedLayersAdapter = require('../models/mapconfig/adapter/mapconfig-named-layers-adapter');
@@ -42,20 +47,23 @@ const LayergroupMetadata = require('../utils/layergroup-metadata');
const RendererStatsReporter = require('../stats/reporter/renderer');
const initializeStatusCode = require('./middlewares/initialize-status-code');
const logger = require('./middlewares/logger');
const initLogger = require('./middlewares/logger');
const bodyParser = require('body-parser');
const servedByHostHeader = require('./middlewares/served-by-host-header');
const stats = require('./middlewares/stats');
const profiler = require('./middlewares/profiler');
const lzmaMiddleware = require('./middlewares/lzma');
const cors = require('./middlewares/cors');
const user = require('./middlewares/user');
const sendResponse = require('./middlewares/send-response');
const syntaxError = require('./middlewares/syntax-error');
const errorMiddleware = require('./middlewares/error-middleware');
const clientHeader = require('./middlewares/client-header');
const MapRouter = require('./map/map-router');
const TemplateRouter = require('./template/template-router');
const getOnTileErrorStrategy = require('../utils/on-tile-error-strategy');
module.exports = class ApiRouter {
constructor ({ serverOptions, environmentOptions }) {
this.serverOptions = serverOptions;
@@ -75,39 +83,28 @@ module.exports = class ApiRouter {
global.statsClient.gauge(keyPrefix + 'waiting', status.waiting);
});
const { rendererCache, tileBackend, attributesBackend, previewBackend, mapBackend, mapStore } = windshaftFactory({
rendererOptions: serverOptions,
redisPool,
onTileErrorStrategy: getOnTileErrorStrategy({ enabled: environmentOptions.enabledFeatures.onTileErrorStrategy }),
logger: this.serverOptions.logger
});
const rendererStatsReporter = new RendererStatsReporter(rendererCache, serverOptions.renderCache.statsInterval);
rendererStatsReporter.start();
const metadataBackend = cartodbRedis({ pool: redisPool });
const pgConnection = new PgConnection(metadataBackend);
const mapStore = new windshaft.storage.MapStore({
pool: redisPool,
expire_time: serverOptions.grainstore.default_layergroup_ttl
});
const rendererFactory = createRendererFactory({ redisPool, serverOptions, environmentOptions });
const rendererCacheOpts = Object.assign({
ttl: 60000, // 60 seconds TTL by default
statsInterval: 60000 // reports stats every milliseconds defined here
}, serverOptions.renderCache || {});
const rendererCache = new windshaft.cache.RendererCache(rendererFactory, rendererCacheOpts);
const rendererStatsReporter = new RendererStatsReporter(rendererCache, rendererCacheOpts.statsInterval);
rendererStatsReporter.start();
const tileBackend = new windshaft.backend.Tile(rendererCache);
const attributesBackend = new windshaft.backend.Attributes();
const previewBackend = new windshaft.backend.Preview(rendererCache);
const mapValidatorBackend = new windshaft.backend.MapValidator(tileBackend, attributesBackend);
const mapBackend = new windshaft.backend.Map(rendererCache, mapStore, mapValidatorBackend);
const surrogateKeysCacheBackends = createSurrogateKeysCacheBackends(serverOptions);
const surrogateKeysCache = new SurrogateKeysCache(surrogateKeysCacheBackends);
const templateMaps = createTemplateMaps({ redisPool, surrogateKeysCache });
const templateMaps = createTemplateMaps({ redisPool, surrogateKeysCache, logger: this.serverOptions.logger });
const analysisStatusBackend = new AnalysisStatusBackend();
const analysisBackend = new AnalysisBackend(metadataBackend, serverOptions.analysis);
const dataviewBackend = new DataviewBackend(analysisBackend);
const statsBackend = new StatsBackend();
const clusterBackend = new ClusterBackend();
const userLimitsBackend = new UserLimitsBackend(metadataBackend, {
limits: {
@@ -154,11 +151,16 @@ module.exports = class ApiRouter {
layergroupAffectedTablesCache
);
['update', 'delete'].forEach(function(eventType) {
templateMaps.on(eventType, namedMapProviderCache.invalidate.bind(namedMapProviderCache));
const namedMapProviderCacheReporter = new NamedMapProviderCacheReporter({
namedMapProviderCache,
intervalInMilliseconds: serverOptions.renderCache.statsInterval
});
namedMapProviderCacheReporter.start();
const metricsBackend = new PubSubMetricsBackend(serverOptions.pubSubMetrics);
const collaborators = {
config: serverOptions,
analysisStatusBackend,
attributesBackend,
dataviewBackend,
@@ -177,82 +179,79 @@ module.exports = class ApiRouter {
statsBackend,
layergroupMetadata,
namedMapProviderCache,
tablesExtentBackend
tablesExtentBackend,
clusterBackend,
metricsBackend
};
this.metadataBackend = metadataBackend;
this.mapRouter = new MapRouter({ collaborators });
this.templateRouter = new TemplateRouter({ collaborators });
}
register (app) {
route (app, routes) {
// FIXME: we need a better way to reset cache while running tests
if (process.env.NODE_ENV === 'test') {
app.layergroupAffectedTablesCache = this.layergroupAffectedTablesCache;
}
Object.keys(this.serverOptions.routes).forEach(apiVersion => {
const routes = this.serverOptions.routes[apiVersion];
routes.forEach(route => {
const apiRouter = router({ mergeParams: true });
const { paths, middlewares = [] } = route;
apiRouter.use(logger(this.serverOptions));
apiRouter.use(initializeStatusCode());
apiRouter.use(bodyParser.json());
apiRouter.use(servedByHostHeader());
apiRouter.use(stats({
apiRouter.use(initLogger({ logger: this.serverOptions.logger }));
apiRouter.use(user(this.metadataBackend));
apiRouter.use(profiler({
enabled: this.serverOptions.useProfiler,
statsClient: global.statsClient
}));
middlewares.forEach(middleware => apiRouter.use(middleware()));
apiRouter.use(initializeStatusCode());
apiRouter.use(bodyParser.json());
apiRouter.use(servedByHostHeader());
apiRouter.use(clientHeader());
apiRouter.use(lzmaMiddleware());
apiRouter.use(cors());
apiRouter.use(user());
this.templateRouter.register(apiRouter, routes.template.paths);
this.mapRouter.register(apiRouter, routes.map.paths);
this.templateRouter.route(apiRouter, route.template);
this.mapRouter.route(apiRouter, route.map);
apiRouter.use(sendResponse());
apiRouter.use(syntaxError());
apiRouter.use(errorMiddleware());
const apiPaths = routes.paths;
apiPaths.forEach(path => app.use(path, apiRouter));
paths.forEach(path => app.use(path, apiRouter));
});
}
};
function createTemplateMaps ({ redisPool, surrogateKeysCache }) {
function createTemplateMaps ({ redisPool, surrogateKeysCache, logger }) {
const templateMaps = new TemplateMaps(redisPool, {
max_user_templates: global.environment.maxUserTemplates
});
function invalidateNamedMap (owner, templateName) {
var startTime = Date.now();
surrogateKeysCache.invalidate(new NamedMapsCacheEntry(owner, templateName), function(err) {
var logMessage = JSON.stringify({
username: owner,
type: 'named_map_invalidation',
elapsed: Date.now() - startTime,
error: !!err ? JSON.stringify(err.message) : undefined
});
function invalidateNamedMap (user, templateName) {
const startTime = Date.now();
surrogateKeysCache.invalidate(new NamedMapsCacheEntry(user, templateName), (err) => {
if (err) {
global.logger.warn(logMessage);
} else {
global.logger.info(logMessage);
return logger.error({ exception: err, 'cdb-user': user, template_id: templateName }, 'Named map invalidation failed');
}
const elapsed = Date.now() - startTime;
logger.info({ 'cdb-user': user, template_id: templateName, duration: elapsed / 1000, duration_ms: elapsed }, 'Named map invalidation success');
});
}
['update', 'delete'].forEach(function(eventType) {
['update', 'delete'].forEach(function (eventType) {
templateMaps.on(eventType, invalidateNamedMap);
});
return templateMaps;
}
function createSurrogateKeysCacheBackends(serverOptions) {
function createSurrogateKeysCacheBackends (serverOptions) {
var cacheBackends = [];
if (serverOptions.varnish_purge_enabled) {
@@ -270,52 +269,3 @@ function createSurrogateKeysCacheBackends(serverOptions) {
return cacheBackends;
}
const timeoutErrorTilePath = __dirname + '/../../../assets/render-timeout-fallback.png';
const timeoutErrorTile = require('fs').readFileSync(timeoutErrorTilePath, {encoding: null});
function createRendererFactory ({ redisPool, serverOptions, environmentOptions }) {
var onTileErrorStrategy;
if (environmentOptions.enabledFeatures.onTileErrorStrategy !== false) {
onTileErrorStrategy = function onTileErrorStrategy$TimeoutTile(err, tile, headers, stats, format, callback) {
function isRenderTimeoutError (err) {
return err.message === 'Render timed out';
}
function isDatasourceTimeoutError (err) {
return err.message && err.message.match(/canceling statement due to statement timeout/i);
}
function isTimeoutError (err) {
return isRenderTimeoutError(err) || isDatasourceTimeoutError(err);
}
function isRasterFormat (format) {
return format === 'png' || format === 'jpg';
}
if (isTimeoutError(err) && isRasterFormat(format)) {
return callback(null, timeoutErrorTile, {
'Content-Type': 'image/png',
}, {});
} else {
return callback(err, tile, headers, stats);
}
};
}
const rendererFactory = new windshaft.renderer.Factory({
onTileErrorStrategy: onTileErrorStrategy,
mapnik: {
redisPool: redisPool,
grainstore: serverOptions.grainstore,
mapnik: serverOptions.renderer.mapnik
},
http: serverOptions.renderer.http,
mvt: serverOptions.renderer.mvt,
torque: serverOptions.renderer.torque
});
return rendererFactory;
}

View File

@@ -1,4 +1,7 @@
'use strict';
const PSQL = require('cartodb-psql');
const tag = require('../middlewares/tag');
const cleanUpQueryParams = require('../middlewares/clean-up-query-params');
const credentials = require('../middlewares/credentials');
const authorize = require('../middlewares/authorize');
@@ -15,12 +18,13 @@ module.exports = class AnalysesController {
this.userLimitsBackend = userLimitsBackend;
}
register (mapRouter) {
route (mapRouter) {
mapRouter.get('/analyses/catalog', this.middlewares());
}
middlewares () {
return [
tag({ tags: ['analysis', 'catalog'] }),
credentials(),
authorize(this.authBackend),
dbConnSetup(this.pgConnection),
@@ -46,10 +50,10 @@ function createPGClient () {
};
}
function getDataFromQuery({ queryTemplate, key }) {
function getDataFromQuery ({ queryTemplate, key }) {
const readOnlyTransactionOn = true;
return function getCatalogMiddleware(req, res, next) {
return function getCatalogMiddleware (req, res, next) {
const { pg, user } = res.locals;
const sql = queryTemplate({ _username: user });
@@ -80,27 +84,27 @@ function prepareResponse () {
}, {});
const analysisCatalog = catalog.map(analysis => {
if (analysisIdToTable.hasOwnProperty(analysis.node_id)) {
if (Object.prototype.hasOwnProperty.call(analysisIdToTable, analysis.node_id)) {
analysis.table = analysisIdToTable[analysis.node_id];
}
return analysis;
})
.sort((analysisA, analysisB) => {
if (!!analysisA.table && !!analysisB.table) {
return analysisB.table.size - analysisA.table.size;
}
.sort((analysisA, analysisB) => {
if (!!analysisA.table && !!analysisB.table) {
return analysisB.table.size - analysisA.table.size;
}
if (analysisA.table) {
return -1;
}
if (analysisB.table) {
return 1;
}
if (!!analysisA.table) {
return -1;
}
if (!!analysisB.table) {
return 1;
}
return -1;
});
});
res.statusCode = 200;
res.body = { catalog: analysisCatalog };
@@ -110,7 +114,7 @@ function prepareResponse () {
}
function unauthorizedError () {
return function unathorizedErrorMiddleware(err, req, res, next) {
return function unathorizedErrorMiddleware (err, req, res, next) {
if (err.message.match(/permission\sdenied/)) {
err = new Error('Unauthorized');
err.http_status = 401;
@@ -121,7 +125,7 @@ function unauthorizedError () {
}
const catalogQueryTpl = ctx => `
SELECT analysis_def->>'type' as type, * FROM cdb_analysis_catalog WHERE username = '${ctx._username}'
SELECT analysis_def->>'type' as type, * FROM cartodb.cdb_analysis_catalog WHERE username = '${ctx._username}'
`;
var tablesQueryTpl = ctx => `

View File

@@ -1,3 +1,6 @@
'use strict';
const tag = require('../middlewares/tag');
const layergroupToken = require('../middlewares/layergroup-token');
const cleanUpQueryParams = require('../middlewares/clean-up-query-params');
const credentials = require('../middlewares/credentials');
@@ -15,12 +18,13 @@ module.exports = class AnalysisLayergroupController {
this.authBackend = authBackend;
}
register (mapRouter) {
route (mapRouter) {
mapRouter.get('/:token/analysis/node/:nodeId', this.middlewares());
}
middlewares () {
return [
tag({ tags: ['analysis', 'node'] }),
layergroupToken(),
credentials(),
authorize(this.authBackend),
@@ -33,7 +37,7 @@ module.exports = class AnalysisLayergroupController {
};
function analysisNodeStatus (analysisStatusBackend) {
return function analysisNodeStatusMiddleware(req, res, next) {
return function analysisNodeStatusMiddleware (req, res, next) {
const { nodeId } = req.params;
const dbParams = dbParamsFromResLocals(res.locals);

View File

@@ -1,11 +1,13 @@
'use strict';
const windshaft = require('windshaft');
const MapConfig = windshaft.model.MapConfig;
const Datasource = windshaft.model.Datasource;
const tag = require('../middlewares/tag');
const cleanUpQueryParams = require('../middlewares/clean-up-query-params');
const credentials = require('../middlewares/credentials');
const dbConnSetup = require('../middlewares/db-conn-setup');
const authorize = require('../middlewares/authorize');
const initProfiler = require('../middlewares/init-profiler');
const checkJsonContentType = require('../middlewares/check-json-content-type');
const incrementMapViewCount = require('../middlewares/increment-map-view-count');
const augmentLayergroupData = require('../middlewares/augment-layergroup-data');
@@ -21,6 +23,7 @@ const mapError = require('../middlewares/map-error');
const CreateLayergroupMapConfigProvider = require('../../models/mapconfig/provider/create-layergroup-provider');
const rateLimit = require('../middlewares/rate-limit');
const { RATE_LIMIT_ENDPOINTS_GROUPS } = rateLimit;
const metrics = require('../middlewares/metrics');
module.exports = class AnonymousMapController {
/**
@@ -37,6 +40,7 @@ module.exports = class AnonymousMapController {
* @constructor
*/
constructor (
config,
pgConnection,
templateMaps,
mapBackend,
@@ -47,8 +51,10 @@ module.exports = class AnonymousMapController {
mapConfigAdapter,
statsBackend,
authBackend,
layergroupMetadata
layergroupMetadata,
metricsBackend
) {
this.config = config;
this.pgConnection = pgConnection;
this.templateMaps = templateMaps;
this.mapBackend = mapBackend;
@@ -60,32 +66,46 @@ module.exports = class AnonymousMapController {
this.statsBackend = statsBackend;
this.authBackend = authBackend;
this.layergroupMetadata = layergroupMetadata;
this.metricsBackend = metricsBackend;
}
register (mapRouter) {
route (mapRouter) {
mapRouter.options('/');
mapRouter.get('/', this.middlewares());
mapRouter.post('/', this.middlewares());
}
middlewares () {
const isTemplateInstantiation = false;
const useTemplateHash = false;
const includeQuery = true;
const label = 'ANONYMOUS LAYERGROUP';
const addContext = true;
const metricsTags = {
event: 'map_view',
attributes: { map_type: 'anonymous' },
from: {
req: {
query: { client: 'client' }
}
}
};
return [
tag({ tags: ['map', 'anonymous'] }),
metrics({
enabled: this.config.pubSubMetrics.enabled,
metricsBackend: this.metricsBackend,
tags: metricsTags
}),
credentials(),
authorize(this.authBackend),
dbConnSetup(this.pgConnection),
rateLimit(this.userLimitsBackend, RATE_LIMIT_ENDPOINTS_GROUPS.ANONYMOUS),
cleanUpQueryParams(['aggregation']),
initProfiler(isTemplateInstantiation),
checkJsonContentType(),
checkCreateLayergroup(),
prepareAdapterMapConfig(this.mapConfigAdapter),
createLayergroup (
createLayergroup(
this.mapBackend,
this.userLimitsBackend,
this.pgConnection,
@@ -122,22 +142,23 @@ function checkCreateLayergroup () {
}
}
req.profiler.done('checkCreateLayergroup');
return next();
};
}
function prepareAdapterMapConfig (mapConfigAdapter) {
return function prepareAdapterMapConfigMiddleware(req, res, next) {
return function prepareAdapterMapConfigMiddleware (req, res, next) {
const requestMapConfig = req.body;
const { user, api_key } = res.locals;
const { logger } = res.locals;
const { user, api_key: apiKey } = res.locals;
const { dbuser, dbname, dbpassword, dbhost, dbport } = res.locals;
const params = Object.assign({ dbuser, dbname, dbpassword, dbhost, dbport }, req.query);
const context = {
analysisConfiguration: {
user,
logger,
db: {
host: dbhost,
port: dbport,
@@ -147,22 +168,25 @@ function prepareAdapterMapConfig (mapConfigAdapter) {
},
batch: {
username: user,
apiKey: api_key
apiKey
}
}
};
mapConfigAdapter.getMapConfig(user, requestMapConfig, params, context, (err, requestMapConfig) => {
req.profiler.done('anonymous.getMapConfig');
if (err) {
return next(err);
}
mapConfigAdapter.getMapConfig(user,
requestMapConfig,
params,
context,
(err, requestMapConfig) => {
if (err) {
return next(err);
}
req.body = requestMapConfig;
res.locals.context = context;
req.body = requestMapConfig;
res.locals.context = context;
next();
});
next();
});
};
}
@@ -171,12 +195,17 @@ function createLayergroup (mapBackend, userLimitsBackend, pgConnection, affected
const requestMapConfig = req.body;
const { context } = res.locals;
const { user, cache_buster, api_key } = res.locals;
const { user, cache_buster: cacheBuster, api_key: apiKey } = res.locals;
const { dbuser, dbname, dbpassword, dbhost, dbport } = res.locals;
const params = {
cache_buster, api_key,
dbuser, dbname, dbpassword, dbhost, dbport
cache_buster: cacheBuster,
api_key: apiKey,
dbuser,
dbname,
dbpassword,
dbhost,
dbport
};
const datasource = context.datasource || Datasource.EmptyDatasource();
@@ -192,6 +221,7 @@ function createLayergroup (mapBackend, userLimitsBackend, pgConnection, affected
);
res.locals.mapConfig = mapConfig;
res.locals.mapConfigProvider = mapConfigProvider;
res.locals.analysesResults = context.analysesResults;
const mapParams = { dbuser, dbname, dbpassword, dbhost, dbport };
@@ -205,7 +235,6 @@ function createLayergroup (mapBackend, userLimitsBackend, pgConnection, affected
res.statusCode = 200;
res.body = layergroup;
res.locals.mapConfigProvider = mapConfigProvider;
next();
});

View File

@@ -1,3 +1,6 @@
'use strict';
const tag = require('../middlewares/tag');
const layergroupToken = require('../middlewares/layergroup-token');
const cleanUpQueryParams = require('../middlewares/clean-up-query-params');
const credentials = require('../middlewares/credentials');
@@ -30,12 +33,13 @@ module.exports = class AttributesLayergroupController {
this.surrogateKeysCache = surrogateKeysCache;
}
register (mapRouter) {
route (mapRouter) {
mapRouter.get('/:token/:layer/attributes/:fid', this.middlewares());
}
middlewares () {
return [
tag({ tags: ['attributes'] }),
layergroupToken(),
credentials(),
authorize(this.authBackend),
@@ -59,8 +63,6 @@ module.exports = class AttributesLayergroupController {
function getFeatureAttributes (attributesBackend) {
return function getFeatureAttributesMiddleware (req, res, next) {
req.profiler.start('windshaft.maplayer_attribute');
const { mapConfigProvider } = res.locals;
const { token } = res.locals;
const { dbuser, dbname, dbpassword, dbhost, dbport } = res.locals;
@@ -68,8 +70,13 @@ function getFeatureAttributes (attributesBackend) {
const params = {
token,
dbuser, dbname, dbpassword, dbhost, dbport,
layer, fid
dbuser,
dbname,
dbpassword,
dbhost,
dbport,
layer,
fid
};
attributesBackend.getFeatureAttributes(mapConfigProvider, params, false, (err, tile, stats = {}) => {

View File

@@ -0,0 +1,102 @@
'use strict';
const tag = require('../middlewares/tag');
const layergroupToken = require('../middlewares/layergroup-token');
const cleanUpQueryParams = require('../middlewares/clean-up-query-params');
const credentials = require('../middlewares/credentials');
const dbConnSetup = require('../middlewares/db-conn-setup');
const authorize = require('../middlewares/authorize');
const rateLimit = require('../middlewares/rate-limit');
const { RATE_LIMIT_ENDPOINTS_GROUPS } = rateLimit;
const createMapStoreMapConfigProvider = require('../middlewares/map-store-map-config-provider');
const cacheControlHeader = require('../middlewares/cache-control-header');
const cacheChannelHeader = require('../middlewares/cache-channel-header');
const surrogateKeyHeader = require('../middlewares/surrogate-key-header');
const lastModifiedHeader = require('../middlewares/last-modified-header');
module.exports = class AggregatedFeaturesLayergroupController {
constructor (
clusterBackend,
pgConnection,
mapStore,
userLimitsBackend,
layergroupAffectedTablesCache,
authBackend,
surrogateKeysCache
) {
this.clusterBackend = clusterBackend;
this.pgConnection = pgConnection;
this.mapStore = mapStore;
this.userLimitsBackend = userLimitsBackend;
this.layergroupAffectedTablesCache = layergroupAffectedTablesCache;
this.authBackend = authBackend;
this.surrogateKeysCache = surrogateKeysCache;
}
route (mapRouter) {
mapRouter.get('/:token/:layer/:z/cluster/:clusterId', this.middlewares());
}
middlewares () {
return [
tag({ tags: ['cluster'] }),
layergroupToken(),
credentials(),
authorize(this.authBackend),
dbConnSetup(this.pgConnection),
// TODO: create its rate limit
rateLimit(this.userLimitsBackend, RATE_LIMIT_ENDPOINTS_GROUPS.ATTRIBUTES),
cleanUpQueryParams(['aggregation']),
createMapStoreMapConfigProvider(
this.mapStore,
this.userLimitsBackend,
this.pgConnection,
this.layergroupAffectedTablesCache
),
getClusteredFeatures(this.clusterBackend),
cacheControlHeader(),
cacheChannelHeader(),
surrogateKeyHeader({ surrogateKeysCache: this.surrogateKeysCache }),
lastModifiedHeader()
];
}
};
function getClusteredFeatures (clusterBackend) {
return function getFeatureAttributesMiddleware (req, res, next) {
const { mapConfigProvider } = res.locals;
const { user, token } = res.locals;
const { dbuser, dbname, dbpassword, dbhost, dbport } = res.locals;
const { layer, z: zoom, clusterId } = req.params;
const { aggregation } = req.query;
const params = {
user,
token,
dbuser,
dbname,
dbpassword,
dbhost,
dbport,
layer,
zoom,
clusterId,
aggregation
};
clusterBackend.getClusterFeatures(mapConfigProvider, params, (err, features, stats = {}) => {
req.profiler.add(stats);
if (err) {
err.label = 'GET CLUSTERED FEATURES';
return next(err);
}
res.statusCode = 200;
const { rows, fields } = features;
res.body = { rows, fields };
next();
});
};
}

View File

@@ -1,3 +1,6 @@
'use strict';
const tag = require('../middlewares/tag');
const layergroupToken = require('../middlewares/layergroup-token');
const cleanUpQueryParams = require('../middlewares/clean-up-query-params');
const credentials = require('../middlewares/credentials');
@@ -16,14 +19,16 @@ const ALLOWED_DATAVIEW_QUERY_PARAMS = [
'own_filter', // 0, 1
'no_filters', // 0, 1
'bbox', // w,s,e,n
'circle', // json
'polygon', // json
'start', // number
'end', // number
'column_type', // string
'bins', // number
'aggregation', //string
'aggregation', // string
'offset', // number
'q', // widgets search
'categories', // number
'categories' // number
];
module.exports = class DataviewLayergroupController {
@@ -45,7 +50,7 @@ module.exports = class DataviewLayergroupController {
this.surrogateKeysCache = surrogateKeysCache;
}
register (mapRouter) {
route (mapRouter) {
// Undocumented/non-supported API endpoint methods.
// Use at your own peril.
@@ -72,6 +77,7 @@ module.exports = class DataviewLayergroupController {
middlewares ({ action, rateLimitGroup }) {
return [
tag({ tags: ['dataview', action] }),
layergroupToken(),
credentials(),
authorize(this.authBackend),

View File

@@ -1,3 +1,5 @@
'use strict';
const { Router: router } = require('express');
const AnalysisLayergroupController = require('./analysis-layergroup-controller');
@@ -8,10 +10,12 @@ const TileLayergroupController = require('./tile-layergroup-controller');
const AnonymousMapController = require('./anonymous-map-controller');
const PreviewTemplateController = require('./preview-template-controller');
const AnalysesCatalogController = require('./analyses-catalog-controller');
const ClusteredFeaturesLayergroupController = require('./clustered-features-layergroup-controller');
module.exports = class MapRouter {
constructor ({ collaborators }) {
const {
config,
analysisStatusBackend,
attributesBackend,
dataviewBackend,
@@ -30,7 +34,9 @@ module.exports = class MapRouter {
statsBackend,
layergroupMetadata,
namedMapProviderCache,
tablesExtentBackend
tablesExtentBackend,
clusterBackend,
metricsBackend
} = collaborators;
this.analysisLayergroupController = new AnalysisLayergroupController(
@@ -81,6 +87,7 @@ module.exports = class MapRouter {
);
this.anonymousMapController = new AnonymousMapController(
config,
pgConnection,
templateMaps,
mapBackend,
@@ -91,10 +98,12 @@ module.exports = class MapRouter {
mapConfigAdapter,
statsBackend,
authBackend,
layergroupMetadata
layergroupMetadata,
metricsBackend
);
this.previewTemplateController = new PreviewTemplateController(
config,
namedMapProviderCache,
previewBackend,
surrogateKeysCache,
@@ -102,7 +111,8 @@ module.exports = class MapRouter {
metadataBackend,
pgConnection,
authBackend,
userLimitsBackend
userLimitsBackend,
metricsBackend
);
this.analysesController = new AnalysesCatalogController(
@@ -110,20 +120,37 @@ module.exports = class MapRouter {
authBackend,
userLimitsBackend
);
this.clusteredFeaturesLayergroupController = new ClusteredFeaturesLayergroupController(
clusterBackend,
pgConnection,
mapStore,
userLimitsBackend,
layergroupAffectedTablesCache,
authBackend,
surrogateKeysCache
);
}
register (apiRouter, mapPaths) {
route (apiRouter, routes) {
const mapRouter = router({ mergeParams: true });
this.analysisLayergroupController.register(mapRouter);
this.attributesLayergroupController.register(mapRouter);
this.dataviewLayergroupController.register(mapRouter);
this.previewLayergroupController.register(mapRouter);
this.tileLayergroupController.register(mapRouter);
this.anonymousMapController.register(mapRouter);
this.previewTemplateController.register(mapRouter);
this.analysesController.register(mapRouter);
routes.forEach(route => {
const { paths, middlewares = [] } = route;
mapPaths.forEach(path => apiRouter.use(path, mapRouter));
middlewares.forEach(middleware => mapRouter.use(middleware()));
this.analysisLayergroupController.route(mapRouter);
this.attributesLayergroupController.route(mapRouter);
this.dataviewLayergroupController.route(mapRouter);
this.previewLayergroupController.route(mapRouter);
this.tileLayergroupController.route(mapRouter);
this.anonymousMapController.route(mapRouter);
this.previewTemplateController.route(mapRouter);
this.analysesController.route(mapRouter);
this.clusteredFeaturesLayergroupController.route(mapRouter);
paths.forEach(path => apiRouter.use(path, mapRouter));
});
}
};

View File

@@ -1,3 +1,6 @@
'use strict';
const tag = require('../middlewares/tag');
const layergroupToken = require('../middlewares/layergroup-token');
const coordinates = require('../middlewares/coordinates');
const cleanUpQueryParams = require('../middlewares/clean-up-query-params');
@@ -33,7 +36,7 @@ module.exports = class PreviewLayergroupController {
this.surrogateKeysCache = surrogateKeysCache;
}
register (mapRouter) {
route (mapRouter) {
mapRouter.get('/static/center/:token/:z/:lat/:lng/:width/:height.:format', this.middlewares({
validateZoom: true,
previewType: 'centered'
@@ -59,6 +62,7 @@ module.exports = class PreviewLayergroupController {
}
return [
tag({ tags: ['static', 'tile'] }),
layergroupToken(),
validateZoom ? coordinates({ z: true, x: false, y: false }) : noop(),
credentials(),
@@ -94,10 +98,10 @@ function getPreviewImageByCenter (previewBackend) {
};
const format = req.params.format === 'jpg' ? 'jpeg' : 'png';
const { mapConfigProvider: provider } = res.locals;
const { mapConfigProvider } = res.locals;
const options = { mapConfigProvider, format, width, height, zoom, center };
previewBackend.getImage(provider, format, width, height, zoom, center, (err, image, headers, stats = {}) => {
req.profiler.done(`render-${format}`);
previewBackend.getImage(options, (err, image, stats = {}) => {
req.profiler.add(stats);
if (err) {
@@ -105,11 +109,7 @@ function getPreviewImageByCenter (previewBackend) {
return next(err);
}
if (headers) {
res.set(headers);
}
res.set('Content-Type', headers['Content-Type'] || `image/${format}`);
res.set('Content-Type', `image/${format}`);
res.statusCode = 200;
res.body = image;
@@ -123,17 +123,17 @@ function getPreviewImageByBoundingBox (previewBackend) {
return function getPreviewImageByBoundingBoxMiddleware (req, res, next) {
const width = +req.params.width;
const height = +req.params.height;
const bounds = {
const bbox = {
west: +req.params.west,
north: +req.params.north,
east: +req.params.east,
south: +req.params.south
};
const format = req.params.format === 'jpg' ? 'jpeg' : 'png';
const { mapConfigProvider: provider } = res.locals;
const { mapConfigProvider } = res.locals;
const options = { mapConfigProvider, format, width, height, bbox };
previewBackend.getImage(provider, format, width, height, bounds, (err, image, headers, stats = {}) => {
req.profiler.done(`render-${format}`);
previewBackend.getImage(options, (err, image, stats = {}) => {
req.profiler.add(stats);
if (err) {
@@ -141,11 +141,7 @@ function getPreviewImageByBoundingBox (previewBackend) {
return next(err);
}
if (headers) {
res.set(headers);
}
res.set('Content-Type', headers['Content-Type'] || `image/${format}`);
res.set('Content-Type', `image/${format}`);
res.statusCode = 200;
res.body = image;

View File

@@ -1,3 +1,6 @@
'use strict';
const tag = require('../middlewares/tag');
const cleanUpQueryParams = require('../middlewares/clean-up-query-params');
const credentials = require('../middlewares/credentials');
const dbConnSetup = require('../middlewares/db-conn-setup');
@@ -10,6 +13,7 @@ const lastModifiedHeader = require('../middlewares/last-modified-header');
const checkStaticImageFormat = require('../middlewares/check-static-image-format');
const rateLimit = require('../middlewares/rate-limit');
const { RATE_LIMIT_ENDPOINTS_GROUPS } = rateLimit;
const metrics = require('../middlewares/metrics');
const DEFAULT_ZOOM_CENTER = {
zoom: 1,
@@ -19,12 +23,13 @@ const DEFAULT_ZOOM_CENTER = {
}
};
function numMapper(n) {
function numMapper (n) {
return +n;
}
module.exports = class PreviewTemplateController {
constructor (
config,
namedMapProviderCache,
previewBackend,
surrogateKeysCache,
@@ -32,8 +37,10 @@ module.exports = class PreviewTemplateController {
metadataBackend,
pgConnection,
authBackend,
userLimitsBackend
userLimitsBackend,
metricsBackend
) {
this.config = config;
this.namedMapProviderCache = namedMapProviderCache;
this.previewBackend = previewBackend;
this.surrogateKeysCache = surrogateKeysCache;
@@ -42,14 +49,31 @@ module.exports = class PreviewTemplateController {
this.pgConnection = pgConnection;
this.authBackend = authBackend;
this.userLimitsBackend = userLimitsBackend;
this.metricsBackend = metricsBackend;
}
register (mapRouter) {
route (mapRouter) {
mapRouter.get('/static/named/:template_id/:width/:height.:format', this.middlewares());
}
middlewares () {
const metricsTags = {
event: 'map_view',
attributes: { map_type: 'static' },
from: {
req: {
query: { client: 'client' }
}
}
};
return [
tag({ tags: ['named', 'static', 'tile'] }),
metrics({
enabled: this.config.pubSubMetrics.enabled,
metricsBackend: this.metricsBackend,
tags: metricsTags
}),
credentials(),
authorize(this.authBackend),
dbConnSetup(this.pgConnection),
@@ -58,7 +82,8 @@ module.exports = class PreviewTemplateController {
checkStaticImageFormat(),
namedMapProvider({
namedMapProviderCache: this.namedMapProviderCache,
label: 'STATIC_VIZ_MAP', forcedFormat: 'png'
label: 'STATIC_VIZ_MAP',
forcedFormat: 'png'
}),
getTemplate({ label: 'STATIC_VIZ_MAP' }),
prepareLayerFilterFromPreviewLayers({
@@ -97,7 +122,7 @@ function getTemplate ({ label }) {
function prepareLayerFilterFromPreviewLayers ({ namedMapProviderCache, label }) {
return function prepareLayerFilterFromPreviewLayersMiddleware (req, res, next) {
const { template } = res.locals;
const { config, auth_token } = req.query;
const { config, auth_token: authToken } = req.query;
if (!template || !template.view || !template.view.preview_layers) {
return next();
@@ -107,8 +132,8 @@ function prepareLayerFilterFromPreviewLayers ({ namedMapProviderCache, label })
var layerVisibilityFilter = [];
template.layergroup.layers.forEach((layer, index) => {
if (previewLayers[''+index] !== false && previewLayers[layer.id] !== false) {
layerVisibilityFilter.push(''+index);
if (previewLayers['' + index] !== false && previewLayers[layer.id] !== false) {
layerVisibilityFilter.push('' + index);
}
});
@@ -116,21 +141,29 @@ function prepareLayerFilterFromPreviewLayers ({ namedMapProviderCache, label })
return next();
}
const { user, token, cache_buster, api_key } = res.locals;
const { user, token, cache_buster: cacheBuster, api_key: apiKey } = res.locals;
const { dbuser, dbname, dbpassword, dbhost, dbport } = res.locals;
const { template_id, format } = req.params;
const { template_id: templateId, format } = req.params;
const params = {
user, token, cache_buster, api_key,
dbuser, dbname, dbpassword, dbhost, dbport,
template_id, format
user,
token,
cache_buster: cacheBuster,
api_key: apiKey,
dbuser,
dbname,
dbpassword,
dbhost,
dbport,
template_id: templateId,
format
};
// overwrites 'all' default filter
params.layer = layerVisibilityFilter.join(',');
// recreates the provider
namedMapProviderCache.get(user, template_id, config, auth_token, params, (err, provider) => {
namedMapProviderCache.get(user, templateId, config, authToken, params, (err, provider) => {
if (err) {
err.label = label;
return next(err);
@@ -144,7 +177,7 @@ function prepareLayerFilterFromPreviewLayers ({ namedMapProviderCache, label })
}
function getStaticImageOptions ({ tablesExtentBackend }) {
return function getStaticImageOptionsMiddleware(req, res, next) {
return function getStaticImageOptionsMiddleware (req, res, next) {
const { user, mapConfigProvider, template } = res.locals;
const { zoom, lon, lat, bbox } = req.query;
const params = { zoom, lon, lat, bbox };
@@ -213,7 +246,6 @@ function getImageOptionsFromCoordinates (zoom, lon, lat) {
}
}
function getImageOptionsFromTemplate (template, zoom) {
if (template.view) {
var zoomCenter = templateZoomCenter(template.view);
@@ -237,7 +269,7 @@ function getImageOptionsFromBoundingBox (bbox = '') {
if (_bbox.length === 4 && _bbox.every(Number.isFinite)) {
return {
bounds: {
bbox: {
west: _bbox[0],
south: _bbox[1],
east: _bbox[2],
@@ -247,10 +279,10 @@ function getImageOptionsFromBoundingBox (bbox = '') {
}
}
function getImage({ previewBackend, label }) {
function getImage ({ previewBackend, label }) {
return function getImageMiddleware (req, res, next) {
const { imageOpts, mapConfigProvider } = res.locals;
const { zoom, center, bounds } = imageOpts;
const { zoom, center, bbox } = imageOpts;
let { width, height } = req.params;
@@ -260,8 +292,9 @@ function getImage({ previewBackend, label }) {
const format = req.params.format === 'jpg' ? 'jpeg' : 'png';
if (zoom !== undefined && center) {
return previewBackend.getImage(mapConfigProvider, format, width, height, zoom, center,
(err, image, headers, stats) => {
const options = { mapConfigProvider, format, width, height, zoom, center };
return previewBackend.getImage(options, (err, image, stats = {}) => {
req.profiler.add(stats);
if (err) {
@@ -269,10 +302,6 @@ function getImage({ previewBackend, label }) {
return next(err);
}
if (headers) {
res.set(headers);
}
res.statusCode = 200;
res.body = image;
@@ -280,19 +309,16 @@ function getImage({ previewBackend, label }) {
});
}
previewBackend.getImage(mapConfigProvider, format, width, height, bounds, (err, image, headers, stats) => {
const options = { mapConfigProvider, format, width, height, bbox };
previewBackend.getImage(options, (err, image, stats = {}) => {
req.profiler.add(stats);
req.profiler.done('render-' + format);
if (err) {
err.label = label;
return next(err);
}
if (headers) {
res.set(headers);
}
res.statusCode = 200;
res.body = image;
@@ -302,32 +328,32 @@ function getImage({ previewBackend, label }) {
}
function setContentTypeHeader () {
return function setContentTypeHeaderMiddleware(req, res, next) {
res.set('Content-Type', res.get('content-type') || res.get('Content-Type') || 'image/png');
return function setContentTypeHeaderMiddleware (req, res, next) {
const format = req.params.format === 'jpg' ? 'jpeg' : 'png';
res.set('Content-Type', `image/${format}`);
next();
};
}
function incrementMapViewsError (ctx) {
return `ERROR: failed to increment mapview count for user '${ctx.user}': ${ctx.err}`;
}
function incrementMapViews ({ metadataBackend }) {
return function incrementMapViewsMiddleware(req, res, next) {
const { user, mapConfigProvider } = res.locals;
return function incrementMapViewsMiddleware (req, res, next) {
const { user, mapConfigProvider, logger } = res.locals;
mapConfigProvider.getMapConfig((err, mapConfig) => {
if (err) {
global.logger.log(incrementMapViewsError({ user, err }));
logger.warn({ exception: err }, 'Failed to increment mapview count');
return next();
}
res.locals.mapConfig = mapConfig;
const statTag = mapConfig.obj().stat_tag;
metadataBackend.incMapviewCount(user, statTag, (err) => {
if (err) {
global.logger.log(incrementMapViewsError({ user, err }));
logger.warn({ exception: err }, 'Failed to increment mapview count');
}
next();
@@ -336,7 +362,7 @@ function incrementMapViews ({ metadataBackend }) {
};
}
function templateZoomCenter(view) {
function templateZoomCenter (view) {
if (view.zoom !== undefined && view.center) {
return {
zoom: view.zoom,
@@ -346,13 +372,13 @@ function templateZoomCenter(view) {
return false;
}
function templateBounds(view) {
function templateBounds (view) {
if (view.bounds) {
var hasAllBounds = ['west', 'south', 'east', 'north'].every(prop => Number.isFinite(view.bounds[prop]));
if (hasAllBounds) {
return {
bounds: {
bbox: {
west: view.bounds.west,
south: view.bounds.south,
east: view.bounds.east,

View File

@@ -1,3 +1,6 @@
'use strict';
const tag = require('../middlewares/tag');
const layergroupToken = require('../middlewares/layergroup-token');
const coordinates = require('../middlewares/coordinates');
const cleanUpQueryParams = require('../middlewares/clean-up-query-params');
@@ -41,22 +44,23 @@ module.exports = class TileLayergroupController {
this.surrogateKeysCache = surrogateKeysCache;
}
register (mapRouter) {
route (mapRouter) {
// REGEXP: doesn't match with `val`
const not = (val) => `(?!${val})([^\/]+?)`;
const not = (val) => `(?!${val})([^\/]+?)`; // eslint-disable-line no-useless-escape
// Sadly the path that matches 1 also matches with 2 so we need to tell to express
// that performs only the middlewares of the first path that matches
// for that we use one array to group all paths.
mapRouter.get([
`/:token/:z/:x/:y@:scale_factor?x.:format`, // 1
`/:token/:z/:x/:y.:format`, // 2
'/:token/:z/:x/:y@:scale_factor?x.:format', // 1
'/:token/:z/:x/:y.:format', // 2
`/:token${not('static')}/:layer/:z/:x/:y.(:format)`
], this.middlewares());
}
middlewares () {
return [
tag({ tags: ['tile'] }),
layergroupToken(),
coordinates(),
credentials(),
@@ -88,14 +92,12 @@ function parseFormat (format = '') {
return SUPPORTED_FORMATS[prettyFormat] ? prettyFormat : 'invalid';
}
function getStatusCode(tile, format){
function getStatusCode (tile, format) {
return tile.length === 0 && format === 'mvt' ? 204 : 200;
}
function getTile (tileBackend) {
return function getTileMiddleware (req, res, next) {
req.profiler.start(`windshaft.${req.params.layer ? 'maplayer_tile' : 'map_tile'}`);
const { mapConfigProvider } = res.locals;
const { token } = res.locals;
const { layer, z, x, y, format } = req.params;
@@ -147,13 +149,8 @@ function incrementErrorMetrics (statsClient) {
function tileError () {
return function tileErrorMiddleware (err, req, res, next) {
if (err.message === 'Tile does not exist' && req.params.format === 'mvt') {
res.statusCode = 204;
return next();
}
// See https://github.com/Vizzuality/Windshaft-cartodb/issues/68
let errMsg = err.message ? ( '' + err.message ) : ( '' + err );
let errMsg = err.message ? ('' + err.message) : ('' + err);
// Rewrite mapnik parsing errors to start with layer number
const matches = errMsg.match("(.*) in style 'layer([0-9]+)'");

View File

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

View File

@@ -1,14 +1,14 @@
'use strict';
module.exports = function authorize (authBackend) {
return function authorizeMiddleware (req, res, next) {
authBackend.authorize(req, res, (err, authorized) => {
req.profiler.done('authorize');
if (err) {
return next(err);
}
if(!authorized) {
err = new Error("Sorry, you are unauthorized (permission denied)");
if (!authorized) {
err = new Error('Sorry, you are unauthorized (permission denied)');
err.http_status = 403;
return next(err);
}

View File

@@ -1,14 +1,16 @@
'use strict';
module.exports = function setCacheChannelHeader () {
return function setCacheChannelHeaderMiddleware (req, res, next) {
if (req.method !== 'GET') {
return next();
}
const { mapConfigProvider } = res.locals;
const { mapConfigProvider, logger } = res.locals;
mapConfigProvider.getAffectedTables((err, affectedTables) => {
if (err) {
global.logger.warn('ERROR generating Cache Channel Header:', err);
logger.warn({ exception: err }, 'Error generating Cache Channel Header');
return next();
}

View File

@@ -0,0 +1,85 @@
'use strict';
const ONE_MINUTE_IN_SECONDS = 60;
const THREE_MINUTE_IN_SECONDS = 60 * 3;
const FIVE_MINUTES_IN_SECONDS = ONE_MINUTE_IN_SECONDS * 5;
const TEN_MINUTES_IN_SECONDS = ONE_MINUTE_IN_SECONDS * 10;
const FIFTEEN_MINUTES_IN_SECONDS = ONE_MINUTE_IN_SECONDS * 15;
const THIRTY_MINUTES_IN_SECONDS = ONE_MINUTE_IN_SECONDS * 30;
const ONE_HOUR_IN_SECONDS = ONE_MINUTE_IN_SECONDS * 60;
const ONE_YEAR_IN_SECONDS = ONE_HOUR_IN_SECONDS * 24 * 365;
const FALLBACK_TTL = global.environment.varnish.fallbackTtl || FIVE_MINUTES_IN_SECONDS;
const validFallbackTTL = [
ONE_MINUTE_IN_SECONDS,
THREE_MINUTE_IN_SECONDS,
FIVE_MINUTES_IN_SECONDS,
TEN_MINUTES_IN_SECONDS,
FIFTEEN_MINUTES_IN_SECONDS,
THIRTY_MINUTES_IN_SECONDS,
ONE_HOUR_IN_SECONDS
];
module.exports = function setCacheControlHeader ({
ttl = ONE_YEAR_IN_SECONDS,
fallbackTtl = FALLBACK_TTL,
revalidate = false
} = {}) {
if (!validFallbackTTL.includes(fallbackTtl)) {
const message = [
'Invalid fallback TTL value for Cache-Control header.',
`Got ${fallbackTtl}, expected ${validFallbackTTL.join(', ')}`
].join(' ');
throw new Error(message);
}
return function setCacheControlHeaderMiddleware (req, res, next) {
if (req.method !== 'GET') {
return next();
}
const { mapConfigProvider = { getAffectedTables: callback => callback() }, logger } = res.locals;
mapConfigProvider.getAffectedTables((err, affectedTables) => {
if (err) {
logger.warn({ exception: err }, 'Error generating Cache Control Header');
return next();
}
const directives = ['public'];
if (everyAffectedTableCanBeInvalidated(affectedTables)) {
directives.push(`max-age=${ttl}`);
} else {
directives.push(`max-age=${computeNextTTL({ ttlInSeconds: fallbackTtl })}`);
}
if (revalidate) {
directives.push('must-revalidate');
}
res.set('Cache-Control', directives.join(','));
next();
});
};
};
function everyAffectedTableCanBeInvalidated (affectedTables) {
const skipNotUpdatedAtTables = false;
const skipAnalysisCachedTables = true;
return affectedTables &&
affectedTables.getTables(skipNotUpdatedAtTables, skipAnalysisCachedTables)
.every(table => table.updated_at !== null);
}
function computeNextTTL ({ ttlInSeconds } = {}) {
const nowInSeconds = Math.ceil(Date.now() / 1000);
const secondsAfterPreviousTTLStep = nowInSeconds % ttlInSeconds;
const secondsToReachTheNextTTLStep = ttlInSeconds - secondsAfterPreviousTTLStep;
return secondsToReachTheNextTTLStep;
}

View File

@@ -1,11 +1,11 @@
'use strict';
module.exports = function checkJsonContentType () {
return function checkJsonContentTypeMiddleware(req, res, next) {
return function checkJsonContentTypeMiddleware (req, res, next) {
if (req.method === 'POST' && !req.is('application/json')) {
return next(new Error('POST data must be of type application/json'));
}
req.profiler.done('checkJsonContentTypeMiddleware');
next();
};
};

View File

@@ -1,8 +1,10 @@
'use strict';
const VALID_IMAGE_FORMATS = ['png', 'jpg'];
module.exports = function checkStaticImageFormat () {
return function checkStaticImageFormatMiddleware (req, res, next) {
if(!VALID_IMAGE_FORMATS.includes(req.params.format)) {
if (!VALID_IMAGE_FORMATS.includes(req.params.format)) {
return next(new Error(`Unsupported image format "${req.params.format}"`));
}

View File

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

View File

@@ -0,0 +1,13 @@
'use strict';
module.exports = function clientHeader () {
return function clientHeaderMiddleware (req, res, next) {
const { client } = req.query;
if (client) {
res.set('Carto-Client', client);
}
return next();
};
};

View File

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

View File

@@ -1,17 +1,23 @@
'use strict';
module.exports = function cors () {
return function corsMiddleware (req, res, next) {
const headers = [
'X-Requested-With',
'X-Prototype-Version',
'X-CSRF-Token'
'X-CSRF-Token',
'Authorization',
'Carto-Event',
'Carto-Event-Source',
'Carto-Event-Group-Id'
];
if (req.method === 'OPTIONS') {
headers.push('Content-Type');
}
res.set("Access-Control-Allow-Origin", "*");
res.set("Access-Control-Allow-Headers", headers.join(', '));
res.set('Access-Control-Allow-Origin', '*');
res.set('Access-Control-Allow-Headers', headers.join(', '));
next();
};

View File

@@ -1,24 +1,26 @@
'use strict';
const basicAuth = require('basic-auth');
module.exports = function credentials () {
return function credentialsMiddleware(req, res, next) {
return function credentialsMiddleware (req, res, next) {
const apikeyCredentials = getApikeyCredentialsFromRequest(req);
res.locals.api_key = apikeyCredentials.token;
res.locals.basicAuthUsername = apikeyCredentials.username;
res.set('vary', 'Authorization'); //Honor Authorization header when caching.
res.set('vary', 'Authorization'); // Honor Authorization header when caching.
return next();
};
};
function getApikeyCredentialsFromRequest(req) {
function getApikeyCredentialsFromRequest (req) {
let apikeyCredentials = {
token: null,
username: null,
username: null
};
for (let getter of apikeyGetters) {
for (const getter of apikeyGetters) {
apikeyCredentials = getter(req);
if (apikeyTokenFound(apikeyCredentials)) {
break;
@@ -31,10 +33,10 @@ function getApikeyCredentialsFromRequest(req) {
const apikeyGetters = [
getApikeyTokenFromHeaderAuthorization,
getApikeyTokenFromRequestQueryString,
getApikeyTokenFromRequestBody,
getApikeyTokenFromRequestBody
];
function getApikeyTokenFromHeaderAuthorization(req) {
function getApikeyTokenFromHeaderAuthorization (req) {
const credentials = basicAuth(req);
if (credentials) {
@@ -45,12 +47,12 @@ function getApikeyTokenFromHeaderAuthorization(req) {
} else {
return {
username: null,
token: null,
token: null
};
}
}
function getApikeyTokenFromRequestQueryString(req) {
function getApikeyTokenFromRequestQueryString (req) {
let token = null;
if (req.query && req.query.api_key) {
@@ -61,11 +63,11 @@ function getApikeyTokenFromRequestQueryString(req) {
return {
username: null,
token: token,
token: token
};
}
function getApikeyTokenFromRequestBody(req) {
function getApikeyTokenFromRequestBody (req) {
let token = null;
if (req.body && req.body.api_key) {
@@ -76,10 +78,10 @@ function getApikeyTokenFromRequestBody(req) {
return {
username: null,
token: token,
token: token
};
}
function apikeyTokenFound(apikey) {
function apikeyTokenFound (apikey) {
return !!apikey && !!apikey.token;
}

View File

@@ -1,3 +1,5 @@
'use strict';
const _ = require('underscore');
module.exports = function dbConnSetup (pgConnection) {
@@ -5,10 +7,8 @@ module.exports = function dbConnSetup (pgConnection) {
const { user } = res.locals;
pgConnection.setDBConn(user, res.locals, (err) => {
req.profiler.done('dbConnSetup');
if (err) {
if (err.message && -1 !== err.message.indexOf('name not found')) {
if (err.message && err.message.indexOf('name not found') !== -1) {
err.http_status = 404;
}

View File

@@ -0,0 +1,172 @@
'use strict';
const setCommonHeaders = require('../../utils/common-headers');
module.exports = function errorMiddleware (/* options */) {
return function error (err, req, res, next) {
const { logger } = res.locals;
const errors = populateLimitErrors(Array.isArray(err) ? err : [err]);
errors.forEach((err) => logger.error({ exception: err }, 'Error while handling the request'));
setCommonHeaders(req, res, () => {
const errorResponseBody = {
errors: errors.map(errorMessage),
errors_with_context: errors.map(errorMessageWithContext)
};
// If a callback was requested, force status to 200
res.status(req.query.callback ? 200 : findStatusCode(errors[0]));
if (req.query && req.query.callback) {
res.jsonp(errorResponseBody);
} else {
res.json(errorResponseBody);
}
return next();
});
};
};
function isRenderTimeoutError (err) {
return err.message === 'Render timed out';
}
function isDatasourceTimeoutError (err) {
return err.message && err.message.match(/canceling statement due to statement timeout/i);
}
function isTimeoutError (errorTypes) {
return errorTypes.renderTimeoutError || errorTypes.datasourceTimeoutError;
}
function getErrorTypes (error) {
return {
renderTimeoutError: isRenderTimeoutError(error),
datasourceTimeoutError: isDatasourceTimeoutError(error)
};
}
function isMaxWaitingClientsError (err) {
return err.message === 'max waitingClients count exceeded';
}
function populateLimitErrors (errors) {
return errors.map(function (error) {
if (isMaxWaitingClientsError(error)) {
error.message = 'You are over platform\'s limits: Max render capacity exceeded.' +
' Contact CARTO support for more details.';
error.type = 'limit';
error.subtype = 'render-capacity';
error.http_status = 429;
return error;
}
const errorTypes = getErrorTypes(error);
if (isTimeoutError(errorTypes)) {
error.message = 'You are over platform\'s limits. Please contact us to know more details';
error.type = 'limit';
error.http_status = 429;
}
if (errorTypes.datasourceTimeoutError) {
error.subtype = 'datasource';
error.message = 'You are over platform\'s limits: SQL query timeout error.' +
' Refactor your query before running again or contact CARTO support for more details.';
}
if (errorTypes.renderTimeoutError) {
error.subtype = 'render';
error.message = 'You are over platform\'s limits: Render timeout error.' +
' Contact CARTO support for more details.';
}
return error;
});
}
function findStatusCode (err) {
var statusCode;
if (err.http_status) {
statusCode = err.http_status;
} else {
statusCode = statusFromErrorMessage('' + err);
}
return statusCode;
}
module.exports.findStatusCode = findStatusCode;
function statusFromErrorMessage (errMsg) {
// Find an appropriate statusCode based on message
var statusCode = 400;
if (errMsg.indexOf('permission denied') !== -1) {
statusCode = 403;
} else if (errMsg.indexOf('authentication failed') !== -1) {
statusCode = 403;
} else if (errMsg.match(/Postgis Plugin.*[\s|\n].*column.*does not exist/)) {
statusCode = 400;
} else if (errMsg.indexOf('does not exist') !== -1) {
if (errMsg.indexOf(' role ') !== -1) {
statusCode = 403; // role 'xxx' does not exist
} else if (errMsg.match(/function .* does not exist/)) {
statusCode = 400; // invalid SQL (SQL function does not exist)
} else {
statusCode = 404;
}
}
return statusCode;
}
function errorMessage (err) {
// See https://github.com/Vizzuality/Windshaft-cartodb/issues/68
var message = (typeof err === 'string' ? err : err.message) || 'Unknown error';
return stripConnectionInfo(message);
}
module.exports.errorMessage = errorMessage;
function stripConnectionInfo (message) {
// Strip connection info, if any
return message
// See https://github.com/CartoDB/Windshaft/issues/173
.replace(/Connection string: '[^']*'\n\s/im, '')
// See https://travis-ci.org/CartoDB/Windshaft/jobs/20703062#L1644
.replace(/is the server.*encountered/im, 'encountered');
}
var ERROR_INFO_TO_EXPOSE = {
message: true,
layer: true,
type: true,
analysis: true,
subtype: true
};
function shouldBeExposed (prop) {
return !!ERROR_INFO_TO_EXPOSE[prop];
}
function errorMessageWithContext (err) {
// See https://github.com/Vizzuality/Windshaft-cartodb/issues/68
var message = (typeof err === 'string' ? err : err.message) || 'Unknown error';
var error = {
type: err.type || 'unknown',
message: stripConnectionInfo(message)
};
for (var prop in err) {
// type & message are properties from Error's prototype and will be skipped
if (Object.prototype.hasOwnProperty.call(err, prop) && shouldBeExposed(prop)) {
error[prop] = err[prop];
}
}
return error;
}

View File

@@ -0,0 +1,16 @@
'use strict';
module.exports = function incrementMapViewCount (metadataBackend) {
return function incrementMapViewCountMiddleware (req, res, next) {
const { mapConfig, user, logger } = res.locals;
const statTag = mapConfig.obj().stat_tag;
metadataBackend.incMapviewCount(user, statTag, (err) => {
if (err) {
logger.warn({ exception: err }, 'Failed to increment mapview count');
}
next();
});
};
};

View File

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

View File

@@ -1,14 +1,18 @@
'use strict';
module.exports = function setLastModifiedHeader () {
return function setLastModifiedHeaderMiddleware(req, res, next) {
return function setLastModifiedHeaderMiddleware (req, res, next) {
if (req.method !== 'GET') {
return next();
}
const { mapConfigProvider, cache_buster } = res.locals;
const { mapConfigProvider, cache_buster: cacheBuster, logger } = res.locals;
if (cache_buster) {
const cacheBuster = parseInt(cache_buster, 10);
const lastModifiedDate = Number.isFinite(cacheBuster) ? new Date(cacheBuster) : new Date();
if (cacheBuster) {
const cacheBusterTimestamp = parseInt(cacheBuster, 10);
const lastModifiedDate = Number.isFinite(cacheBusterTimestamp) && cacheBusterTimestamp !== 0
? new Date(cacheBusterTimestamp)
: new Date();
res.set('Last-Modified', lastModifiedDate.toUTCString());
@@ -17,7 +21,7 @@ module.exports = function setLastModifiedHeader () {
mapConfigProvider.getAffectedTables((err, affectedTables) => {
if (err) {
global.logger.warn('ERROR generating Last Modified Header:', err);
logger.warn({ exception: err }, 'Error generating Last Modified Header');
return next();
}
@@ -32,6 +36,8 @@ module.exports = function setLastModifiedHeader () {
res.set('Last-Modified', lastModifiedDate.toUTCString());
res.locals.cache_buster = lastUpdatedAt;
next();
});
};

View File

@@ -1,3 +1,5 @@
'use strict';
module.exports = function setLastUpdatedTimeToLayergroup () {
return function setLastUpdatedTimeToLayergroupMiddleware (req, res, next) {
const { mapConfigProvider, analysesResults } = res.locals;
@@ -9,6 +11,10 @@ module.exports = function setLastUpdatedTimeToLayergroup () {
}
if (!affectedTables) {
res.locals.cache_buster = 0;
layergroup.layergroupid = `${layergroup.layergroupid}:${res.locals.cache_buster}`;
layergroup.last_updated = new Date(res.locals.cache_buster).toISOString();
return next();
}
@@ -20,17 +26,19 @@ module.exports = function setLastUpdatedTimeToLayergroup () {
layergroup.layergroupid = layergroup.layergroupid + ':' + lastUpdateTime;
layergroup.last_updated = new Date(lastUpdateTime).toISOString();
res.locals.cache_buster = lastUpdateTime;
next();
});
};
};
function getLastUpdatedTime(analysesResults, lastUpdateTime) {
function getLastUpdatedTime (analysesResults, lastUpdateTime) {
if (!Array.isArray(analysesResults)) {
return lastUpdateTime;
}
return analysesResults.reduce(function(lastUpdateTime, analysis) {
return analysis.getNodes().reduce(function(lastNodeUpdatedAtTime, node) {
return analysesResults.reduce(function (lastUpdateTime, analysis) {
return analysis.getNodes().reduce(function (lastNodeUpdatedAtTime, node) {
var nodeUpdatedAtDate = node.getUpdatedAt();
var nodeUpdatedTimeAt = (nodeUpdatedAtDate && nodeUpdatedAtDate.getTime()) || 0;
return nodeUpdatedTimeAt > lastNodeUpdatedAtTime ? nodeUpdatedTimeAt : lastNodeUpdatedAtTime;

View File

@@ -1,5 +1,7 @@
'use strict';
module.exports = function setLayerStats (pgConnection, statsBackend) {
return function setLayerStatsMiddleware(req, res, next) {
return function setLayerStatsMiddleware (req, res, next) {
const { user, mapConfig } = res.locals;
const layergroup = res.body;
@@ -8,7 +10,7 @@ module.exports = function setLayerStats (pgConnection, statsBackend) {
return next(err);
}
statsBackend.getStats(mapConfig, connection, function(err, layersStats) {
statsBackend.getStats(mapConfig, connection, function (err, layersStats) {
if (err) {
return next(err);
}

View File

@@ -1,11 +1,14 @@
'use strict';
module.exports = function setLayergroupIdHeader (templateMaps, useTemplateHash) {
return function setLayergroupIdHeaderMiddleware (req, res, next) {
const { user, template } = res.locals;
const layergroup = res.body;
if (useTemplateHash) {
var templateHash = templateMaps.fingerPrint(template).substring(0, 8);
const templateHash = templateMaps.fingerPrint(template).substring(0, 8);
layergroup.layergroupid = `${user}@${templateHash}@${layergroup.layergroupid}`;
res.locals.templateHash = templateHash;
}
res.set('X-Layergroup-Id', layergroup.layergroupid);

View File

@@ -1,3 +1,5 @@
'use strict';
module.exports = function setMetadataToLayergroup (layergroupMetadata, includeQuery) {
return function setMetadataToLayergroupMiddleware (req, res, next) {
const { user, mapConfig, analysesResults = [], context, api_key: userApiKey } = res.locals;
@@ -7,7 +9,7 @@ module.exports = function setMetadataToLayergroup (layergroupMetadata, includeQu
layergroupMetadata.addAnalysesMetadata(user, layergroup, analysesResults, includeQuery);
layergroupMetadata.addTurboCartoContextMetadata(layergroup, mapConfig.obj(), context);
layergroupMetadata.addAggregationContextMetadata(layergroup, mapConfig.obj(), context);
layergroupMetadata.addDateWrappingMetadata (layergroup, mapConfig.obj());
layergroupMetadata.addDateWrappingMetadata(layergroup, mapConfig.obj());
layergroupMetadata.addTileJsonMetadata(layergroup, user, mapConfig, userApiKey);
next();

View File

@@ -1,3 +1,5 @@
'use strict';
const LayergroupToken = require('../../models/layergroup-token');
const authErrorMessageTemplate = function (signer, user) {
return `Cannot use map signature of user "${signer}" on db of user "${user}"`;
@@ -11,13 +13,17 @@ module.exports = function layergroupToken () {
res.locals.token = layergroupToken.token;
res.locals.cache_buster = layergroupToken.cacheBuster;
if (layergroupToken.templateHash) {
res.locals.templateHash = layergroupToken.templateHash;
}
if (layergroupToken.signer) {
res.locals.signer = layergroupToken.signer;
if (res.locals.signer !== user) {
const err = new Error(authErrorMessageTemplate(res.locals.signer, user));
err.type = 'auth';
err.http_status = (req.query && req.query.callback) ? 200: 403;
err.http_status = (req.query && req.query.callback) ? 200 : 403;
return next(err);
}

View File

@@ -0,0 +1,12 @@
'use strict';
const uuid = require('uuid');
module.exports = function initLogger ({ logger }) {
return function initLoggerMiddleware (req, res, next) {
res.locals.logger = logger.child({ request_id: req.get('X-Request-Id') || uuid.v4(), 'cdb-user': res.locals.user });
res.locals.logger.info({ client_request: req }, 'Incoming request');
res.on('finish', () => res.locals.logger.info({ server_response: res, status: res.statusCode }, 'Response sent'));
next();
};
};

View File

@@ -1,29 +1,29 @@
'use strict';
const LZMA = require('lzma').LZMA;
module.exports = function lzma () {
const lzmaWorker = new LZMA();
return function lzmaMiddleware (req, res, next) {
if (!req.query.hasOwnProperty('lzma')) {
if (!Object.prototype.hasOwnProperty.call(req.query, 'lzma')) {
return next();
}
// Decode (from base64)
var lzma = new Buffer(req.query.lzma, 'base64')
var lzma = Buffer.from(req.query.lzma, 'base64')
.toString('binary')
.split('')
.map(function(c) {
.map(function (c) {
return c.charCodeAt(0) - 128;
});
// Decompress
lzmaWorker.decompress(lzma, function(result) {
lzmaWorker.decompress(lzma, function (result) {
try {
delete req.query.lzma;
Object.assign(req.query, JSON.parse(result));
req.profiler.done('lzma');
next();
} catch (err) {
next(new Error('Error parsing lzma as JSON: ' + err));

View File

@@ -1,8 +1,9 @@
'use strict';
module.exports = function mapError (options) {
const { addContext = false, label = 'MAPS CONTROLLER' } = options;
return function mapErrorMiddleware (err, req, res, next) {
req.profiler.done('error');
const { mapConfig } = res.locals;
if (addContext) {
@@ -15,7 +16,7 @@ module.exports = function mapError (options) {
};
};
function populateError(err, mapConfig) {
function populateError (err, mapConfig) {
var error = new Error(err.message);
error.http_status = err.http_status;

View File

@@ -1,3 +1,5 @@
'use strict';
const MapStoreMapConfigProvider = require('../../models/mapconfig/provider/map-store-provider');
module.exports = function createMapStoreMapConfigProvider (
@@ -8,15 +10,27 @@ module.exports = function createMapStoreMapConfigProvider (
forcedFormat = null
) {
return function createMapStoreMapConfigProviderMiddleware (req, res, next) {
const { user, token, cache_buster, api_key } = res.locals;
const { user, token, cache_buster: cacheBuster, api_key: apiKey } = res.locals;
const { dbuser, dbname, dbpassword, dbhost, dbport } = res.locals;
const { layer: layerFromParams, z, x, y, scale_factor, format } = req.params;
const { layer: layerFromParams, z, x, y, scale_factor: scaleFactor, format } = req.params;
const { layer: layerFromQuery } = req.query;
const params = {
user, token, cache_buster, api_key,
dbuser, dbname, dbpassword, dbhost, dbport,
layer: (layerFromQuery || layerFromParams), z, x, y, scale_factor, format
user,
token,
cache_buster: cacheBuster,
api_key: apiKey,
dbuser,
dbname,
dbpassword,
dbhost,
dbport,
layer: (layerFromQuery || layerFromParams),
z,
x,
y,
scale_factor: scaleFactor,
format
};
if (forcedFormat) {

View File

@@ -0,0 +1,181 @@
'use strict';
const EVENT_VERSION = '1';
const MAX_LENGTH = 100;
module.exports = function metrics ({ enabled, tags, metricsBackend }) {
if (!enabled) {
return function metricsDisabledMiddleware (req, res, next) {
next();
};
}
if (!tags || !tags.event) {
throw new Error('Missing required "event" parameter to report metrics');
}
return function metricsMiddleware (req, res, next) {
// FIXME: use parent logger as we don't want bind the error to the request
// but we still want to know if an error is thrown
const { logger } = res.locals;
res.on('finish', () => {
const { event, attributes } = getEventData(req, res, tags);
metricsBackend.send(event, attributes)
.catch((err) => logger.error({ exception: err, event }, 'Failed to publish event'));
});
return next();
};
};
function getEventData (req, res, tags) {
const event = tags.event;
const extra = {};
if (tags.from) {
if (tags.from.req) {
Object.assign(extra, getFromReq(req, tags.from.req));
}
if (tags.from.res) {
Object.assign(extra, getFromRes(res, tags.from.res));
}
}
const attributes = Object.assign({}, {
client_event: normalizedField(req.get('Carto-Event')),
client_event_group_id: normalizedField(req.get('Carto-Event-Group-Id')),
event_source: normalizedField(req.get('Carto-Event-Source')),
event_time: new Date().toISOString(),
user_id: res.locals.userId,
user_agent: req.get('User-Agent'),
map_id: getLayergroupid({ res }),
cache_buster: getCacheBuster({ res }),
template_hash: getTemplateHash({ res }),
stat_tag: getStatTag({ res }),
response_code: res.statusCode.toString(),
response_time: getResponseTime(req),
source_domain: req.hostname,
event_version: EVENT_VERSION
}, tags.attributes, extra);
// remove undefined properties
Object.keys(attributes).forEach(key => attributes[key] === undefined && delete attributes[key]);
return { event, attributes };
}
function normalizedField (field) {
if (!field) {
return undefined;
}
return field.toString().trim().substr(0, MAX_LENGTH);
}
function getLayergroupid ({ res }) {
if (res.locals.token) {
return res.locals.token;
}
if (res.locals.mapConfig) {
return res.locals.mapConfig.id();
}
if (res.locals.mapConfigProvider && res.locals.mapConfigProvider.mapConfig) {
return res.locals.mapConfigProvider.mapConfig.id();
}
}
function getCacheBuster ({ res }) {
if (res.locals.cache_buster !== undefined) {
return `${res.locals.cache_buster}`;
}
if (res.locals.mapConfigProvider) {
return `${res.locals.mapConfigProvider.getCacheBuster()}`;
}
}
function getTemplateHash ({ res }) {
if (res.locals.templateHash) {
return res.locals.templateHash;
}
if (res.locals.mapConfigProvider && res.locals.mapConfigProvider.getTemplateHash) {
let templateHash;
try {
templateHash = res.locals.mapConfigProvider.getTemplateHash().substring(0, 8);
} catch (e) {}
return templateHash;
}
}
function getStatTag ({ res }) {
if (res.locals.mapConfig) {
return res.locals.mapConfig.obj().stat_tag;
}
// FIXME: don't expect that mapConfig is already set
if (res.locals.mapConfigProvider && res.locals.mapConfigProvider.mapConfig) {
return res.locals.mapConfigProvider.mapConfig.obj().stat_tag;
}
}
// FIXME: 'Profiler' might not be accurate enough
function getResponseTime (req) {
let stats;
try {
stats = req.profiler.toJSON();
} catch (e) {
return undefined;
}
return stats && stats.total ? stats.total.toString() : undefined;
}
function getFromReq (req, { query = {}, body = {}, params = {}, headers = {} } = {}) {
const extra = {};
for (const [queryParam, eventName] of Object.entries(query)) {
extra[eventName] = req.query[queryParam];
}
for (const [bodyParam, eventName] of Object.entries(body)) {
extra[eventName] = req.body[bodyParam];
}
for (const [pathParam, eventName] of Object.entries(params)) {
extra[eventName] = req.params[pathParam];
}
for (const [header, eventName] of Object.entries(headers)) {
extra[eventName] = req.get(header);
}
return extra;
}
function getFromRes (res, { body = {}, headers = {}, locals = {} } = {}) {
const extra = {};
if (res.body) {
for (const [bodyParam, eventName] of Object.entries(body)) {
extra[eventName] = res.body[bodyParam];
}
}
for (const [header, eventName] of Object.entries(headers)) {
extra[eventName] = res.get(header);
}
for (const [localParam, eventName] of Object.entries(locals)) {
extra[eventName] = res.locals[localParam];
}
return extra;
}

View File

@@ -0,0 +1,46 @@
'use strict';
module.exports = function getNamedMapProvider ({ namedMapProviderCache, label, forcedFormat = null }) {
return function getNamedMapProviderMiddleware (req, res, next) {
const { user, token, cache_buster: cacheBuster, api_key: apiKey } = res.locals;
const { dbuser, dbname, dbpassword, dbhost, dbport } = res.locals;
const { template_id: templateId, layer: layerFromParams, z, x, y, format } = req.params;
const { layer: layerFromQuery } = req.query;
const params = {
user,
token,
cache_buster: cacheBuster,
api_key: apiKey,
dbuser,
dbname,
dbpassword,
dbhost,
dbport,
template_id: templateId,
layer: (layerFromQuery || layerFromParams),
z,
x,
y,
format
};
if (forcedFormat) {
params.format = forcedFormat;
params.layer = params.layer || 'all';
}
const { config, auth_token: authToken } = req.query;
namedMapProviderCache.get(user, templateId, config, authToken, params, (err, namedMapProvider) => {
if (err) {
err.label = label;
return next(err);
}
res.locals.mapConfigProvider = namedMapProvider;
next();
});
};
};

View File

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

View File

@@ -0,0 +1,37 @@
'use strict';
const Profiler = require('../../stats/profiler-proxy');
const debug = require('debug')('windshaft:cartodb:stats');
const { name: prefix } = require('../../../package.json');
module.exports = function profiler (options) {
const { enabled = true, statsClient } = options;
return function profilerMiddleware (req, res, next) {
const { logger } = res.locals;
// TODO: stop using profiler and log stats instead of adding them to the profiler
req.profiler = new Profiler({
statsd_client: statsClient,
profile: enabled
});
req.profiler.start(prefix);
res.on('finish', () => {
req.profiler.done('response');
req.profiler.end();
const stats = req.profiler.toJSON();
logger.info({ stats, duration: stats.response / 1000, duration_ms: stats.response }, 'Request profiling stats');
try {
// May throw due to dns, see: http://github.com/CartoDB/Windshaft/issues/166
req.profiler.sendStats();
} catch (err) {
debug('error sending profiling stats: ' + err);
}
});
next();
};
};

View File

@@ -19,12 +19,12 @@ const RATE_LIMIT_ENDPOINTS_GROUPS = {
NAMED_TILES: 'named_tiles'
};
function rateLimit(userLimitsBackend, endpointGroup = null) {
function rateLimit (userLimitsBackend, endpointGroup = null) {
if (!isRateLimitEnabled(endpointGroup)) {
return function rateLimitDisabledMiddleware(req, res, next) { next(); };
return function rateLimitDisabledMiddleware (req, res, next) { next(); };
}
return function rateLimitMiddleware(req, res, next) {
return function rateLimitMiddleware (req, res, next) {
userLimitsBackend.getRateLimit(res.locals.user, endpointGroup, function (err, userRateLimit) {
if (err) {
return next(err);
@@ -46,7 +46,7 @@ function rateLimit(userLimitsBackend, endpointGroup = null) {
// retry is floor rounded in seconds by redis-cell
res.set('Retry-After', retry + 1);
let rateLimitError = new Error(
const rateLimitError = new Error(
'You are over platform\'s limits: too many requests.' +
' Please contact us to know more details'
);
@@ -61,8 +61,7 @@ function rateLimit(userLimitsBackend, endpointGroup = null) {
};
}
function isRateLimitEnabled(endpointGroup) {
function isRateLimitEnabled (endpointGroup) {
return global.environment.enabledFeatures.rateLimitsEnabled &&
endpointGroup &&
global.environment.enabledFeatures.rateLimitsByEndpoint[endpointGroup];

View File

@@ -0,0 +1,24 @@
'use strict';
const setCommonHeaders = require('../../utils/common-headers');
module.exports = function sendResponse () {
return function sendResponseMiddleware (req, res, next) {
setCommonHeaders(req, res, () => {
res.status(res.statusCode);
if (Buffer.isBuffer(res.body)) {
res.send(res.body);
return next();
}
if (req.query.callback) {
res.jsonp(res.body);
return next();
}
res.json(res.body);
return next();
});
};
};

View File

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

View File

@@ -1,9 +1,11 @@
const NamedMapsCacheEntry = require('../../cache/model/named_maps_entry');
'use strict';
const NamedMapsCacheEntry = require('../../cache/model/named-maps-entry');
const NamedMapMapConfigProvider = require('../../models/mapconfig/provider/named-map-provider');
module.exports = function setSurrogateKeyHeader ({ surrogateKeysCache }) {
return function setSurrogateKeyHeaderMiddleware(req, res, next) {
const { user, mapConfigProvider } = res.locals;
return function setSurrogateKeyHeaderMiddleware (req, res, next) {
const { user, mapConfigProvider, logger } = res.locals;
if (mapConfigProvider instanceof NamedMapMapConfigProvider) {
surrogateKeysCache.tag(res, new NamedMapsCacheEntry(user, mapConfigProvider.getTemplateName()));
@@ -15,7 +17,7 @@ module.exports = function setSurrogateKeyHeader ({ surrogateKeysCache }) {
mapConfigProvider.getAffectedTables((err, affectedTables) => {
if (err) {
global.logger.warn('ERROR generating Surrogate Key Header:', err);
logger.warn({ exception: err }, 'Error generating Surrogate Key Header');
return next();
}

View File

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

View File

@@ -0,0 +1,15 @@
'use strict';
module.exports = function tag ({ tags }) {
if (!Array.isArray(tags) || !tags.every((tag) => typeof tag === 'string')) {
throw new Error('Required "tags" option must be a valid Array: [string, string, ...]');
}
return function tagMiddleware (req, res, next) {
const { logger } = res.locals;
res.locals.tags = tags;
res.on('finish', () => logger.info({ tags: res.locals.tags }, 'Request tagged'));
next();
};
};

View File

@@ -0,0 +1,29 @@
'use strict';
const CdbRequest = require('../../models/cdb-request');
module.exports = function user (metadataBackend) {
const cdbRequest = new CdbRequest();
return function userMiddleware (req, res, next) {
try {
res.locals.user = getUserNameFromRequest(req, cdbRequest);
} catch (err) {
return next(err);
}
metadataBackend.getUserId(res.locals.user, (err, userId) => {
if (err || !userId) {
return next();
}
res.locals.userId = userId;
return next();
});
};
};
function getUserNameFromRequest (req, cdbRequest) {
return cdbRequest.userByReq(req);
}

View File

@@ -1,10 +1,12 @@
'use strict';
const fs = require('fs');
const timeoutErrorVectorTile = fs.readFileSync(__dirname + '/../../../../assets/render-timeout-fallback.mvt');
module.exports = function vectorError() {
return function vectorErrorMiddleware(err, req, res, next) {
if(req.params.format === 'mvt') {
const path = require('path');
const timeoutErrorVectorTile = fs.readFileSync(path.join(__dirname, '/../../../assets/render-timeout-fallback.mvt'));
module.exports = function vectorError () {
return function vectorErrorMiddleware (err, req, res, next) {
if (req.params.format === 'mvt') {
if (isTimeoutError(err) || isRateLimitError(err)) {
res.set('Content-Type', 'application/x-protobuf');
return res.status(429).send(timeoutErrorVectorTile);
@@ -15,7 +17,6 @@ module.exports = function vectorError() {
};
};
function isRenderTimeoutError (err) {
return err.message === 'Render timed out';
}

View File

@@ -1,4 +1,7 @@
const { templateName } = require('../../backends/template_maps');
'use strict';
const { templateName } = require('../../backends/template-maps');
const tag = require('../middlewares/tag');
const credentials = require('../middlewares/credentials');
const rateLimit = require('../middlewares/rate-limit');
const { RATE_LIMIT_ENDPOINTS_GROUPS } = rateLimit;
@@ -16,8 +19,8 @@ module.exports = class AdminTemplateController {
this.userLimitsBackend = userLimitsBackend;
}
register (templateRouter) {
templateRouter.options(`/:template_id`);
route (templateRouter) {
templateRouter.options('/:template_id');
templateRouter.post('/', this.middlewares({
action: 'create',
@@ -74,6 +77,7 @@ module.exports = class AdminTemplateController {
}
return [
tag({ tags: ['named', 'admin', action] }),
credentials(),
authorizedByAPIKey({ authBackend: this.authBackend, action, label }),
rateLimit(this.userLimitsBackend, rateLimitGroup),
@@ -164,8 +168,6 @@ function updateTemplate ({ templateMaps }) {
function retrieveTemplate ({ templateMaps }) {
return function retrieveTemplateMiddleware (req, res, next) {
req.profiler.start('windshaft-cartodb.get_template');
const { user } = res.locals;
const templateId = templateName(req.params.template_id);
@@ -193,8 +195,6 @@ function retrieveTemplate ({ templateMaps }) {
function destroyTemplate ({ templateMaps }) {
return function destroyTemplateMiddleware (req, res, next) {
req.profiler.start('windshaft-cartodb.delete_template');
const { user } = res.locals;
const templateId = templateName(req.params.template_id);
@@ -213,8 +213,6 @@ function destroyTemplate ({ templateMaps }) {
function listTemplates ({ templateMaps }) {
return function listTemplatesMiddleware (req, res, next) {
req.profiler.start('windshaft-cartodb.get_template_list');
const { user } = res.locals;
templateMaps.listTemplates(user, (err, templateIds) => {

View File

@@ -1,8 +1,10 @@
'use strict';
const tag = require('../middlewares/tag');
const cleanUpQueryParams = require('../middlewares/clean-up-query-params');
const credentials = require('../middlewares/credentials');
const dbConnSetup = require('../middlewares/db-conn-setup');
const authorize = require('../middlewares/authorize');
const initProfiler = require('../middlewares/init-profiler');
const checkJsonContentType = require('../middlewares/check-json-content-type');
const incrementMapViewCount = require('../middlewares/increment-map-view-count');
const augmentLayergroupData = require('../middlewares/augment-layergroup-data');
@@ -19,6 +21,7 @@ const NamedMapMapConfigProvider = require('../../models/mapconfig/provider/named
const CreateLayergroupMapConfigProvider = require('../../models/mapconfig/provider/create-layergroup-provider');
const rateLimit = require('../middlewares/rate-limit');
const { RATE_LIMIT_ENDPOINTS_GROUPS } = rateLimit;
const metrics = require('../middlewares/metrics');
module.exports = class NamedMapController {
/**
@@ -36,6 +39,7 @@ module.exports = class NamedMapController {
* @constructor
*/
constructor (
config,
pgConnection,
templateMaps,
mapBackend,
@@ -46,8 +50,10 @@ module.exports = class NamedMapController {
mapConfigAdapter,
statsBackend,
authBackend,
layergroupMetadata
layergroupMetadata,
metricsBackend
) {
this.config = config;
this.pgConnection = pgConnection;
this.templateMaps = templateMaps;
this.mapBackend = mapBackend;
@@ -59,27 +65,41 @@ module.exports = class NamedMapController {
this.statsBackend = statsBackend;
this.authBackend = authBackend;
this.layergroupMetadata = layergroupMetadata;
this.metricsBackend = metricsBackend;
}
register (templateRouter) {
route (templateRouter) {
templateRouter.get('/:template_id/jsonp', this.middlewares());
templateRouter.post('/:template_id', this.middlewares());
}
middlewares () {
const isTemplateInstantiation = true;
const useTemplateHash = true;
const includeQuery = false;
const label = 'NAMED MAP LAYERGROUP';
const addContext = false;
const metricsTags = {
event: 'map_view',
attributes: { map_type: 'named' },
from: {
req: {
query: { client: 'client' }
}
}
};
return [
tag({ tags: ['map', 'named'] }),
metrics({
enabled: this.config.pubSubMetrics.enabled,
metricsBackend: this.metricsBackend,
tags: metricsTags
}),
credentials(),
authorize(this.authBackend),
dbConnSetup(this.pgConnection),
rateLimit(this.userLimitsBackend, RATE_LIMIT_ENDPOINTS_GROUPS.NAMED),
cleanUpQueryParams(['aggregation']),
initProfiler(isTemplateInstantiation),
checkJsonContentType(),
checkInstantiteLayergroup(),
getTemplate(
@@ -104,7 +124,7 @@ module.exports = class NamedMapController {
lastModifiedHeader(),
lastUpdatedTimeLayergroup(),
layerStats(this.pgConnection, this.statsBackend),
layergroupIdHeader(this.templateMaps ,useTemplateHash),
layergroupIdHeader(this.templateMaps, useTemplateHash),
layergroupMetadata(this.layergroupMetadata, includeQuery),
mapError({ label, addContext })
];
@@ -112,7 +132,7 @@ module.exports = class NamedMapController {
};
function checkInstantiteLayergroup () {
return function checkInstantiteLayergroupMiddleware(req, res, next) {
return function checkInstantiteLayergroupMiddleware (req, res, next) {
if (req.method === 'GET') {
const { callback, config } = req.query;
@@ -123,14 +143,12 @@ function checkInstantiteLayergroup () {
if (config) {
try {
req.body = JSON.parse(config);
} catch(e) {
} catch (e) {
return next(new Error('Invalid config parameter, should be a valid JSON'));
}
}
}
req.profiler.done('checkInstantiteLayergroup');
return next();
};
}
@@ -146,8 +164,8 @@ function getTemplate (
return function getTemplateMiddleware (req, res, next) {
const templateParams = req.body;
const { user, dbuser, dbname, dbpassword, dbhost, dbport } = res.locals;
const { template_id } = req.params;
const { auth_token } = req.query;
const { template_id: templateId } = req.params;
const { auth_token: authToken } = req.query;
const params = Object.assign({ dbuser, dbname, dbpassword, dbhost, dbport }, req.query);
@@ -159,14 +177,17 @@ function getTemplate (
mapConfigAdapter,
affectedTablesCache,
user,
template_id,
templateId,
templateParams,
auth_token,
authToken,
params
);
mapConfigProvider.getMapConfig((err, mapConfig, rendererParams) => {
req.profiler.done('named.getMapConfig');
mapConfigProvider.logger = res.locals.logger;
mapConfigProvider.getMapConfig((err, mapConfig, rendererParams, context, stats = {}) => {
req.profiler.add(stats);
if (err) {
return next(err);
}

View File

@@ -1,3 +1,5 @@
'use strict';
const { Router: router } = require('express');
const NamedMapController = require('./named-template-controller');
@@ -7,6 +9,7 @@ const TileTemplateController = require('./tile-template-controller');
module.exports = class TemplateRouter {
constructor ({ collaborators }) {
const {
config,
pgConnection,
templateMaps,
mapBackend,
@@ -20,9 +23,11 @@ module.exports = class TemplateRouter {
layergroupMetadata,
namedMapProviderCache,
tileBackend,
metricsBackend
} = collaborators;
this.namedMapController = new NamedMapController(
config,
pgConnection,
templateMaps,
mapBackend,
@@ -33,7 +38,8 @@ module.exports = class TemplateRouter {
mapConfigAdapter,
statsBackend,
authBackend,
layergroupMetadata
layergroupMetadata,
metricsBackend
);
this.tileTemplateController = new TileTemplateController(
@@ -52,13 +58,19 @@ module.exports = class TemplateRouter {
);
}
register (apiRouter, templatePaths) {
route (apiRouter, routes) {
const templateRouter = router({ mergeParams: true });
this.namedMapController.register(templateRouter);
this.tileTemplateController.register(templateRouter);
this.adminTemplateController.register(templateRouter);
routes.forEach(route => {
const { paths, middlewares = [] } = route;
templatePaths.forEach(path => apiRouter.use(path, templateRouter));
middlewares.forEach(middleware => templateRouter.use(middleware()));
this.namedMapController.route(templateRouter);
this.tileTemplateController.route(templateRouter);
this.adminTemplateController.route(templateRouter);
paths.forEach(path => apiRouter.use(path, templateRouter));
});
}
};

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