Merge pull request #278 from CartoDB/257-remove-old-api

Remove old API
This commit is contained in:
Raul Ochoa
2015-03-30 19:04:27 +02:00
29 changed files with 1344 additions and 2648 deletions
+4
View File
@@ -84,6 +84,10 @@
// Custom Globals
"globals" : { // additional predefined global variables
"describe": true,
"before": true,
"after": true,
"it": true,
"suite": true,
"suiteSetup": true,
"test": true,
+1 -1
View File
@@ -38,4 +38,4 @@ coverage:
check: test
.PHONY: pre-install test
.PHONY: pre-install test jshint coverage
+275 -168
View File
@@ -1,58 +1,90 @@
1.30.1 -- 2015-mm-dd
--------------------
# Changelog
1.30.0 -- 2015-03-11
--------------------
## 2.0.0
Released 2015-mm-dd
Announcements:
- Major release with **BREAKING CHANGES**:
* Removes `/:table/infowindow`, `/:table/map_metadata` and `/:table/flush_cache` endpoints
* Sample configuration removes `/tiles/template` and `/tiles/layergroup`
* URLs to use from now on are: `/api/v1/map/named` and `/api/v1/map`
* No more state changes for styles
* No more dump stats for renderers: SIGUSR1 and SIGUSR2 signals
* Removes query params:
- sql
- geom_type
- cache_buster
- cache_policy
- interactivity
- style
- style_version
- style_convert
- scale_factor
* Affected tables for x-cache-channel will use direct connection to postgresql
* Removes some metrics: authorized times ones
## 1.30.0
Released 2015-03-11
Announcements:
- Upgrades windshaft to [0.40.0](https://github.com/CartoDB/Windshaft/releases/tag/0.40.0)
1.29.0 -- 2015-03-09
--------------------
## 1.29.0
Released 2015-03-09
Announcements:
- Upgrades windshaft to [0.39.0](https://github.com/CartoDB/Windshaft/releases/tag/0.39.0)
1.28.5 -- 2015-02-20
--------------------
## 1.28.5
Released 2015-02-20
Announcements:
- Upgrades windshaft to [0.37.5](https://github.com/CartoDB/Windshaft/releases/tag/0.37.5)
1.28.4 -- 2015-02-18
--------------------
## 1.28.4
Released 2015-02-18
Announcements:
- Upgrades windshaft to [0.37.4](https://github.com/CartoDB/Windshaft/releases/tag/0.37.4)
1.28.3 -- 2015-02-17
--------------------
## 1.28.3
Released 2015-02-17
Announcements:
- Upgrades windshaft to [0.37.3](https://github.com/CartoDB/Windshaft/releases/tag/0.37.3)
1.28.2 -- 2015-02-17
--------------------
## 1.28.2
Released 2015-02-17
Announcements:
- Upgrades windshaft to [0.37.2](https://github.com/CartoDB/Windshaft/releases/tag/0.37.2)
1.28.1 -- 2015-02-17
--------------------
## 1.28.1
Released 2015-02-17
Announcements:
- Upgrades windshaft to [0.37.1](https://github.com/CartoDB/Windshaft/releases/tag/0.37.1)
1.28.0 -- 2015-02-17
--------------------
## 1.28.0
Released 2015-02-17
Announcements:
- Upgrades windshaft to [0.37.0](https://github.com/CartoDB/Windshaft/releases/tag/0.37.0)
@@ -62,8 +94,9 @@ New features:
tables last update (#253)
1.27.0 -- 2015-02-16
--------------------
## 1.27.0
Released 2015-02-16
Announcements:
- Adds default image placeholder for http renderer to use as fallback
@@ -76,29 +109,33 @@ Bugfixes:
- Fixes tests with beforeEach and afterEach triggers
1.26.2 -- 2015-01-28
--------------------
## 1.26.2
Released 2015-01-28
Bugfixes:
- Accept 'open' string in templates' `auth` as authorized.
1.26.1 -- 2015-01-28
--------------------
## 1.26.1
Released 2015-01-28
Announcements:
- Upgrades windshaft to 0.35.1, see https://github.com/CartoDB/Windshaft/pull/254
1.26.0 -- 2015-01-27
--------------------
## 1.26.0
Released 2015-01-27
Announcements:
- Upgrades windshaft to 0.35.0, supports mapconfig version `1.3.0`
1.25.0 -- 2015-01-26
--------------------
## 1.25.0
Released 2015-01-26
Announcements:
- No more signed maps (#227 and #238)
@@ -134,22 +171,25 @@ New features:
- SurrogateKeysCache is subscribed to TemplateMaps events to do the invalidations
1.24.0 -- 2015-01-15
--------------------
## 1.24.0
Released 2015-01-15
Announcements:
- Upgrades windshaft to 0.34.0 for retina support
1.23.1 -- 2015-01-14
--------------------
## 1.23.1
Released 2015-01-14
Announcements:
- Regenerate npm-shrinkwrap.json
1.23.0 -- 2015-01-14
--------------------
## 1.23.0
Released 2015-01-14
Announcements:
- Upgrades windshaft to 0.33.0
@@ -158,22 +198,25 @@ New features:
- Sets HTTP renderer configuration in server_options
1.22.0 -- 2015-01-13
--------------------
## 1.22.0
Released 2015-01-13
New features:
- Health check endpoint
1.21.2 -- 2014-12-15
--------------------
## 1.21.2
Released 2014-12-15
Announcements:
- Upgrades windshaft to 0.32.4
1.21.1 -- 2014-12-11
--------------------
## 1.21.1
Released 2014-12-11
Announcements:
- Upgrades windshaft to 0.32.2
@@ -183,29 +226,33 @@ Bugfixes:
1.21.0 -- 2014-10-24
--------------------
## 1.21.0
Released 2014-10-24
New features:
- Allow a different cache-control max-age for layergroup responses
1.20.2 -- 2014-10-20
--------------------
## 1.20.2
Released 2014-10-20
Announcements:
- Upgrades windshaft to 0.31.0
1.20.1 -- 2014-10-17
--------------------
## 1.20.1
Released 2014-10-17
Announcements:
- Upgrades redis-mpool to 0.3.0
1.20.0 -- 2014-10-15
--------------------
## 1.20.0
Released 2014-10-15
New features:
- Report to statsd the status of redis pools
@@ -215,8 +262,9 @@ Enhancements:
- Share one redis-mpool across the application
1.19.0 -- 2014-10-14
--------------------
## 1.19.0
Released 2014-10-14
Announcements:
- Dropping support for npm <1.2.1
@@ -225,8 +273,9 @@ Announcements:
- Generates npm-shrinkwrap.json with npm >1.2.0
1.18.2 -- 2014-10-13
--------------------
## 1.18.2
Released 2014-10-13
Bug fixes:
- Defaults resultSet to object if undefined in QueryTablesApi
@@ -235,29 +284,33 @@ Announcements:
- Upgrades windshaft to 0.28.1
1.18.1 -- 2014-10-13
--------------------
## 1.18.1
Released 2014-10-13
New features:
- Allow to add more node.js' threadpool workers via process.env.UV_THREADPOOL_SIZE
1.18.0 -- 2014-10-03
--------------------
## 1.18.0
Released 2014-10-03
Announcements:
- Comes back to use mapnik 2.3.x based on cartodb/node-mapnik@1.4.15-cdb from windshaft@0.28.0
1.17.2 -- 2014-10-01
--------------------
## 1.17.2
Released 2014-10-01
Announcements:
- Upgrades windshaft to 0.27.2 which downgrades node-mapnik to 0.7.26-cdb1
1.17.1 -- 2014-09-30
--------------------
## 1.17.1
Released 2014-09-30
Announcements:
- Upgrades windshaft to 0.27.1 which downgrades node-mapnik to 1.4.10
@@ -267,8 +320,9 @@ Enhancements:
- Upgrades mocha
1.17.0 -- 2014-09-25
--------------------
## 1.17.0
Released 2014-09-25
New features:
- Starts using mapnik 2.3.x
@@ -279,14 +333,16 @@ Enhancements:
- Metrics revamp: removes and adds some metrics
- Adds poolSize configuration for mapnik
1.16.1 -- 2014-08-19
--------------------
## 1.16.1
Released 2014-08-19
Enhancements:
- Upgrades cartodb-redis
1.16.0 -- 2014-08-18
--------------------
## 1.16.0
Released 2014-08-18
New features:
- Configurable QueryTablesAPI to call directly postgresql using cartodb-psql
@@ -302,8 +358,9 @@ Enhancements:
- windshaft
- request
1.15.0 -- 2014-08-13
--------------------
## 1.15.0
Released 2014-08-13
Enhancements:
- Upgrades dependencies:
- redis-mpool
@@ -313,8 +370,9 @@ Enhancements:
- Slow pool configuration in example configurations
1.14.0 -- 2014-08-07
--------------------
## 1.14.0
Released 2014-08-07
Enhancements:
- SQL API requests moved to its own entity
@@ -326,43 +384,49 @@ New features:
dependencies for this matter
1.13.1 -- 2014-08-04
--------------------
## 1.13.1
Released 2014-08-04
Enhancements:
- Profiler header sent as JSON string
1.13.0 -- 2014-07-30
--------------------
## 1.13.0
Released 2014-07-30
New features:
- Support for postgresql schemas
- Use public user from redis
- Support for several auth tokens
1.12.1 -- 2014-06-24
--------------------
## 1.12.1
Released 2014-06-24
Enhancements:
- Caches layergroup and sets X-Cache-Channel in GET requests also in named maps
1.12.0 -- 2014-06-24
--------------------
## 1.12.0
Released 2014-06-24
New features:
- Caches layergroup and sets X-Cache-Channel in GET requests
1.11.1 -- 2014-05-07
--------------------
## 1.11.1
Released 2014-05-07
Enhancements:
- Upgrade Windshaft to 0.21.0, see
http://github.com/CartoDB/Windshaft/blob/0.21.0/NEWS
1.11.0 -- 2014-04-28
--------------------
## 1.11.0
Released 2014-04-28
New features:
@@ -373,23 +437,26 @@ Enhancements:
- Set default PostgreSQL application name to "cartodb_tiler"
1.10.2 -- 2014-04-08
--------------------
## 1.10.2
Released 2014-04-08
Bug fixes:
- Fix show_style tool broken since 1.8.1
- Fix X-Cache-Channel of tiles accessed via signed token (#188)
1.10.1 -- 2014-03-21
--------------------
## 1.10.1
Released 2014-03-21
Bug fixes:
- Do not cache non-success jsonp responses (#186)
1.10.0 -- 2014-03-20
--------------------
## 1.10.0
Released 2014-03-20
New features:
@@ -410,15 +477,17 @@ Other changes:
- Switch to 3-clause BSD license (#184)
1.9.0 -- 2014-03-10
-------------------
## 1.9.0
Released 2014-03-10
New features:
- Allow to set server related configuration in serverMetadata (#182)
1.8.5 -- 2014-03-10
-------------------
## 1.8.5
Released 2014-03-10
Enhancements:
@@ -436,8 +505,9 @@ Bug fixes:
- Do not cache map creation responses (#176)
1.8.4 -- 2014-03-03
-------------------
## 1.8.4
Released 2014-03-03
Enhancements:
@@ -454,8 +524,9 @@ Bug fixes:
- Fix database connection settings on template instanciation (#174)
1.8.3 -- 2014-02-27
-------------------
## 1.8.3
Released 2014-02-27
Enhancements:
@@ -468,8 +539,9 @@ Enhancements:
[ new sqlapi.timeout directive, defaults to 100 ms ]
- Do not query CDB_TableMetadata for queries affected by no tables (#168)
1.8.2 -- 2014-02-25
-------------------
## 1.8.2
Released 2014-02-25
Enhancements:
@@ -483,8 +555,9 @@ Bug fixes:
* Fix munin plugin after log format changes (#154)
1.8.1 -- 2014-02-19
-------------------
## 1.8.1
Released 2014-02-19
Enhancements:
@@ -494,8 +567,9 @@ Bug fixes:
* Always generate X-Cache-Channel for token-based tile responses (#152)
1.8.0 -- 2014-02-18
-------------------
## 1.8.0
Released 2014-02-18
Enhancements:
@@ -510,16 +584,18 @@ Enhancements:
* Allow limiting number of templates for each user (#136)
* Allow configuring TTL of mapConfigs via "mapConfigTTL"
1.7.1 -- 2014-02-11
-------------------
## 1.7.1
Released 2014-02-11
Enhancements:
* Disable debug logging unless "debug" config param evaluates to true (#137)
* Require windshaft 0.17.2 for further reducing log noise (#137)
1.7.0 -- 2014-02-11
-------------------
## 1.7.0
Released 2014-02-11
New features:
@@ -540,8 +616,9 @@ Bug fixes:
* Allow passing numbers as values for numeric template variables (#130)
1.6.3 -- 2014-01-30
-------------------
## 1.6.3
Released 2014-01-30
Bug fixes:
@@ -556,8 +633,9 @@ Enhancements:
* Stop processing XML on renderer creation, not needed anymore since 1.6.1
introduced on-demand XML generation.
1.6.2 -- 2014-01-23
-------------------
## 1.6.2
Released 2014-01-23
Bug fixes:
@@ -569,16 +647,18 @@ Enhancements:
print XML style now it is not in redis anymore (#110)
* Support CORS in template instanciation endpoint (#113)
1.6.1 -- 2014-01-15
-------------------
## 1.6.1
Released 2014-01-15
Bug fixes:
* Drop cache headers from error responses (#107)
* Localize external CartoCSS resources at renderer creation time (#108)
1.6.0 -- 2014-01-10
-------------------
## 1.6.0
Released 2014-01-10
New features:
@@ -591,24 +671,27 @@ Other changes:
* Update cartodb-redis dependency to "~0.3.0"
* Update redis-server dependency to "2.4.0+"
1.5.2 -- 2013-12-05
-------------------
## 1.5.2
Released 2013-12-05
Bug fixes:
* Fix configuration-level compatibility with versions prior to 1.5 (#96)
* Fix use of old layergroups on mapnik upgrade (#97)
1.5.1 -- 2013-11-28
-------------------
## 1.5.1
Released 2013-11-28
Bug fixes:
* Survive presence of malformed CartoCSS in redis (#94)
* Accept useless point-transform:scale directives (#93)
1.5.0 -- 2013-11-19
-------------------
## 1.5.0
Released 2013-11-19
NOTE: new configuration directives `postgres_auth_pass` and
`postgres.password` added; see config/environments/*.example
@@ -632,24 +715,28 @@ Other changes:
* CartoDB redis interaction delegated to "cartodb-redis" module
1.4.1 -- 2013-11-08
-------------------
## 1.4.1
Released 2013-11-08
* Fix support for exponential notation in CartoCSS filter values (#87)
1.4.0 -- 2013-10-31
-------------------
## 1.4.0
Released 2013-10-31
* Add Support for Mapnik-2.2.0 (#78)
1.3.6 -- 2013-10-11
-------------------
## 1.3.6
Released 2013-10-11
* Restore support for node-0.8.9 accidentally dropped by 1.3.5
NOTE: needs removing node_modules/windshaft and re-running npm install
1.3.5 -- 2013-10-03
-------------------
## 1.3.5
Released 2013-10-03
* Fixing apostrophes in CartoCSS
* Fix "sql/table must contain zoom variable" error when using
@@ -658,8 +745,8 @@ Other changes:
* Fix error for invalid text-name in CartoCSS (#81)
* Do not let anonymous requests use authorized renderer caches
1.3.4
------
## 1.3.4
NOTE: configuration sqlapi.host renamed to sqlapi.domain
(support for "sqlapi.host" is retained for backward compatibility)
@@ -668,38 +755,38 @@ NOTE: configuration sqlapi.host renamed to sqlapi.domain
* Improve invalid mapnik-geometry-type CSS error message
* Fix race condition in localization of network resources
1.3.3
------
## 1.3.3
* Set Last-Modified header to allow for 304 responses
* Add profiling support (needs useProfiler in env config file)
* Fix double-checking for layergroups with no interactivity
* Log full layergroup config at creation time (#76)
1.3.2
------
## 1.3.2
* Set default layergroup TTL to 2 hours
* Serve multilayer tiles and grid with persistent cache control
1.3.1
------
## 1.3.1
* Fix deadlock on new style creation
* Fix database authentication with multi-table layergroups
* Add tile and grid fetching checks at layergroup creation time
* Fix SQL error reporting to NOT split on newline
* Fix support for CartoCSS attachments
1.3.0
------
## 1.3.0
* Change stats format for multilayer map token request, see
http://github.com/Vizzuality/Windshaft-cartodb/wiki/Redis-stats-format
1.2.1
------
## 1.2.1
* Fix multilayer post from firefox
* Fix multilayer cartocss layer name handling
1.2.0
------
## 1.2.0
* Multilayer API changes
* Layers passed by index in grid fetching url
* Interactivity only specified in layergroup config
@@ -707,14 +794,14 @@ NOTE: configuration sqlapi.host renamed to sqlapi.domain
* Use ISO format for last_modified timestamp
* Expected LZMA encoding changed to base64
1.1.10
------
## 1.1.10
* Fix regression with default interactivity parameter (#74)
* More verbose logging for SQL api connection errors
* Write stats for multilayer map token request
1.1.9
-----
## 1.1.9
* Handle SQL API errors by requesting no Varnish cache
* Fix X-Cache-Channel for multilayer (by token) responses
* Add last_modified field to layergroup creation response (#72)
@@ -723,18 +810,22 @@ NOTE: configuration sqlapi.host renamed to sqlapi.domain
* Add support for LZMA compressed GET parameters
* Add support for creating layergroups via GET
1.1.8
-----
## 1.1.8
* Require Windshaft-0.9.1, to reduce harmfulness of cache_buster param
1.1.7 (DD//MM//YY)
-----
## 1.1.7
Released DD//MM//YY
* Do not let /etc/services confuse FD checker (munin plugin)
* Multilayer support (#72)
* Expose renderer settings in the environment config files
1.1.6 (19//02//13)
-----
## 1.1.6
Released 19//02//13
* Require windshaft 0.8.5, fixing some stability issues
and providing cache info on request
* Require grainstore 0.10.9, fixing an issue with multi-geom markers
@@ -743,20 +834,26 @@ NOTE: configuration sqlapi.host renamed to sqlapi.domain
* Survive connection refusals from redis
* Add maxConnection environment configuration, default to 128
1.1.5 (DD//MM//YY)
-----
## 1.1.5
Released DD//MM//YY
* Fix bogus cached return of utf grid for fully contained tiles (#67)
1.1.4 (DD//MM//YY)
-----
## 1.1.4
Released DD//MM//YY
* Reduce default extent to allow for consistent proj4 round-tripping
* Enhance reset_styles script to use full configuration (#62)
* Have reset_styles script also drop extended keys (#58)
* Fix example postgis parameter for simplifying input geoms (#63)
* Add row_limit to example config (#64)
1.1.3 (30//11//12)
-----
## 1.1.3
Released 30//11//12
* Fix reset_styles script to really skip extended keys
* CartoCSS versioning
* Mapnik-version dependent default styles
@@ -764,16 +861,20 @@ NOTE: configuration sqlapi.host renamed to sqlapi.domain
* styles with conditional markers
* scale arrow markers by 50%
1.1.2 (DD//MM//YY)
-----
## 1.1.2
Released DD//MM//YY
* CartoCSS versioning
* Fix use of "style_version" with GET (inline styles)
* Enhance 2.0 -> 2.1 transforms:
* styles with no semicolon
* markers shift due to geometry clipping
1.1.1 (DD//MM//YY)
-----
## 1.1.1
Released DD//MM//YY
* Add support for persistent client cache headers
* Fix crash on unknown user (#55)
* Add /version entry point
@@ -782,8 +883,10 @@ NOTE: configuration sqlapi.host renamed to sqlapi.domain
* Support style_version and style_convert parameters in POST /style request
* Support style_version in GET /:z/:x/:y request
1.1.0 (30/10/12)
=======
## 1.1.0
Released (30/10/12)
* Add /version entry point
* CartoCSS versioning
* Include version in GET /style response
@@ -800,12 +903,16 @@ NOTE: configuration sqlapi.host renamed to sqlapi.domain
* Replaced environment configs by .example ones
* Fixed some issues with cluster2
1.0.0 (03/10/12)
-----
## 1.0.0
Released 03/10/12
* Migrated to node 0.8.x.
0.9.0 (25/09/12)
-----
## 0.9.0
Released 25/09/12
* External resources in CartoCSS
* Added X-Cache-Channel header in all the tiler GET requests
* Small fixes
-10
View File
@@ -105,16 +105,6 @@ ws.on('listening', function() {
+ " (" + ENV + ")");
});
// DEPRECATED, use SIGUSR2
process.on('SIGUSR1', function() {
console.log('WARNING: handling of SIGUSR1 by Windshaft-CartoDB is deprecated, please send SIGUSR2 instead');
ws.dumpCacheStats();
});
process.on('SIGUSR2', function() {
ws.dumpCacheStats();
});
process.on('SIGHUP', function() {
global.log4js.clearAndShutdownAppenders(function() {
global.log4js.configure(log4js_config);
+2 -24
View File
@@ -14,13 +14,11 @@ var config = {
// Base url for the Templated Maps API
// "/api/v1/map/named" is the new API,
// "/tiles/template" is for compatibility with versions up to 1.6.x
,base_url_templated: '(?:/api/v1/map/named|/tiles/template|/u/:user/api/v1/map/named|/u/:user/tiles/template)'
,base_url_templated: '(?:/api/v1/map/named|/u/:user/api/v1/map/named)'
// Base url for the Detached Maps API
// "maps" is the the new API,
// "tiles/layergroup" is for compatibility with versions up to 1.6.x
,base_url_detached: '(?:/api/v1/map|/tiles/layergroup|/u/:user/api/v1/map|/u/:user/tiles/layergroup)'
// Base url for the Inline Maps and Table Maps API
,base_url_legacy: '/tiles/:table'
,base_url_detached: '(?:/api/v1/map|/u/:user/api/v1/map)'
// Maximum number of connections for one process
// 128 is a good value with a limit of 1024 open file descriptors
@@ -133,26 +131,6 @@ var config = {
statusInterval: 5000 // time, in ms, between each status report is emitted from the pool, status is sent to statsd
}
}
,sqlapi: {
protocol: 'http',
// If "host" is given, it will be used
// to connect to the SQL-API without a
// DNS lookup
host: '127.0.0.1',
port: 8080,
// The "domain" part will be appended to
// the cartodb username and passed to
// SQL-API requests in the Host HTTP header
domain: 'localhost.lan',
version: 'v1',
// Maximum lenght of SQL query for GET
// requests. Longer queries will be sent
// using POST. Defaults to 2048
max_get_sql_length: 2048,
// Maximum time to wait for a response,
// in milliseconds. Defaults to 100.
timeout: 100
}
,varnish: {
host: 'localhost',
port: 6082, // the por for the telnet interface where varnish is listening to
+2 -24
View File
@@ -14,13 +14,11 @@ var config = {
// Base url for the Templated Maps API
// "/api/v1/map/named" is the new API,
// "/tiles/template" is for compatibility with versions up to 1.6.x
,base_url_templated: '(?:/api/v1/map/named|/tiles/template|/u/:user/api/v1/map/named|/u/:user/tiles/template)'
,base_url_templated: '(?:/api/v1/map/named|/u/:user/api/v1/map/named)'
// Base url for the Detached Maps API
// "maps" is the the new API,
// "tiles/layergroup" is for compatibility with versions up to 1.6.x
,base_url_detached: '(?:/api/v1/map|/tiles/layergroup|/u/:user/api/v1/map|/u/:user/tiles/layergroup)'
// Base url for the Inline Maps and Table Maps API
,base_url_legacy: '/tiles/:table'
,base_url_detached: '(?:/api/v1/map|/u/:user/api/v1/map)'
// Maximum number of connections for one process
// 128 is a good value with a limit of 1024 open file descriptors
@@ -127,26 +125,6 @@ var config = {
statusInterval: 5000 // time, in ms, between each status report is emitted from the pool, status is sent to statsd
}
}
,sqlapi: {
protocol: 'https',
// If "host" is given, it will be used
// to connect to the SQL-API without a
// DNS lookup
//host: '127.0.0.1',
port: 8080,
// The "domain" part will be appended to
// the cartodb username and passed to
// SQL-API requests in the Host HTTP header
domain: 'cartodb.com',
version: 'v2',
// Maximum lenght of SQL query for GET
// requests. Longer queries will be sent
// using POST. Defaults to 2048
max_get_sql_length: 2048,
// Maximum time to wait for a response,
// in milliseconds. Defaults to 100.
timeout: 100
}
,varnish: {
host: 'localhost',
port: 6082, // the por for the telnet interface where varnish is listening to
+2 -24
View File
@@ -14,13 +14,11 @@ var config = {
// Base url for the Templated Maps API
// "/api/v1/maps/named" is the new API,
// "/tiles/template" is for compatibility with versions up to 1.6.x
,base_url_templated: '(?:/api/v1/map/named|/tiles/template|/u/:user/api/v1/map/named|/u/:user/tiles/template)'
,base_url_templated: '(?:/api/v1/map/named|/u/:user/api/v1/map/named)'
// Base url for the Detached Maps API
// "/api/v1/maps" is the the new API,
// "/tiles/layergroup" is for compatibility with versions up to 1.6.x
,base_url_detached: '(?:/api/v1/map|/tiles/layergroup|/u/:user/api/v1/map|/u/:user/tiles/layergroup)'
// Base url for the Inline Maps and Table Maps API
,base_url_legacy: '/tiles/:table'
,base_url_detached: '(?:/api/v1/map|/u/:user/api/v1/map)'
// Maximum number of connections for one process
// 128 is a good value with a limit of 1024 open file descriptors
@@ -127,26 +125,6 @@ var config = {
statusInterval: 5000 // time, in ms, between each status report is emitted from the pool, status is sent to statsd
}
}
,sqlapi: {
protocol: 'https',
// If "host" is given, it will be used
// to connect to the SQL-API without a
// DNS lookup
//host: '127.0.0.1',
port: 8080,
// The "domain" part will be appended to
// the cartodb username and passed to
// SQL-API requests in the Host HTTP header
domain: 'cartodb.com',
version: 'v2',
// Maximum lenght of SQL query for GET
// requests. Longer queries will be sent
// using POST. Defaults to 2048
max_get_sql_length: 2048,
// Maximum time to wait for a response,
// in milliseconds. Defaults to 100.
timeout: 100
}
,varnish: {
host: 'localhost',
port: 6082, // the por for the telnet interface where varnish is listening to
+2 -26
View File
@@ -14,13 +14,11 @@ var config = {
// Base url for the Templated Maps API
// "/api/v1/map/named" is the new API,
// "/tiles/template" is for compatibility with versions up to 1.6.x
,base_url_templated: '(?:/api/v1/map/named|/tiles/template|/u/:user/api/v1/map/named|/u/:user/tiles/template)'
,base_url_templated: '(?:/api/v1/map/named|/u/:user/api/v1/map/named)'
// Base url for the Detached Maps API
// "maps" is the the new API,
// "tiles/layergroup" is for compatibility with versions up to 1.6.x
,base_url_detached: '(?:/api/v1/map|/tiles/layergroup|/u/:user/api/v1/map|/u/:user/tiles/layergroup)'
// Base url for the Inline Maps and Table Maps API
,base_url_legacy: '/tiles/:table'
,base_url_detached: '(?:/api/v1/map|/u/:user/api/v1/map)'
// Maximum number of connections for one process
// 128 is a good value with a limit of 1024 open file descriptors
@@ -129,28 +127,6 @@ var config = {
statusInterval: 5000 // time, in ms, between each status report is emitted from the pool, status is sent to statsd
}
}
,sqlapi: {
protocol: 'http',
// If "host" is given, it will be used
// to connect to the SQL-API without a
// DNS lookup
host: '127.0.0.1',
port: 1080,
// The "domain" part will be appended to
// the cartodb username and passed to
// SQL-API requests in the Host HTTP header
domain: 'donot_look_this_up',
// This port will be used by "make check" for testing purposes
// It must be available
version: 'v1',
// Maximum lenght of SQL query for GET
// requests. Longer queries will be sent
// using POST. Defaults to 2048
max_get_sql_length: 2048,
// Maximum time to wait for a response,
// in milliseconds. Defaults to 100.
timeout: 100
}
,varnish: {
host: '',
port: null, // the por for the telnet interface where varnish is listening to
-2
View File
@@ -25,9 +25,7 @@ Again, each inner timer may have several inner timers.
- **TemplateMaps_instance**: time to retrieve a map template instance, see *getTemplate* and *authorizedByCert*
- **affectedTables**: time to check what are the affected tables for adding the cache channel, see *addCacheChannel*
- **authorize**: time to authorize a request, see *authorizedByAPIKey*, *authorizedByCert*, *authorizedBySigner*
- **authorizedByAPIKey**: time to authorize using an API KEY
- **authorizedByCert**: time to authorize a template instantiation
- **authorizedBySigner**: time to authorize a request with auth_token
- **findLastUpdated**: time to retrieve the last update time for a list of tables, see *affectedTables*
- **generateCacheChannel**: time to generate the headers for the cache channel based on the request, see *addCacheChannel*
- **getSignerMapKey**: time to retrieve from redis the authorized user for a template map
+27 -55
View File
@@ -1,4 +1,3 @@
var sqlApi = require('../sql/sql_api');
var PSQL = require('cartodb-psql');
var step = require('step');
@@ -74,56 +73,36 @@ function handleAffectedTablesAndLastUpdatedTimeRows(err, rows, callback) {
QueryTablesApi.prototype.runQuery = function(username, query, queryHandler, callback) {
var self = this;
if (shouldQueryPostgresDirectly()) {
var params = {};
var params = {};
step(
function setAuth() {
self.pgConnection.setDBAuth(username, params, this);
},
function setConn(err) {
if (err) {
throw err;
}
self.pgConnection.setDBConn(username, params, this);
},
function executeQuery(err) {
if (err) {
throw err;
}
var psql = new PSQL({
user: params.dbuser,
pass: params.dbpass,
host: params.dbhost,
port: params.dbport,
dbname: params.dbname
});
psql.query(query, function(err, resultSet) {
resultSet = resultSet || {};
var rows = resultSet.rows || [];
queryHandler(err, rows, callback);
});
step(
function setAuth() {
self.pgConnection.setDBAuth(username, params, this);
},
function setConn(err) {
if (err) {
throw err;
}
);
} else {
step(
function getApiKey() {
self.metadataBackend.getUserMapKey(username, this);
},
function executeQuery(err, apiKey) {
if (err) {
throw err;
}
sqlApi.query(username, apiKey, query, function(err, rows) {
queryHandler(err, rows, callback);
});
self.pgConnection.setDBConn(username, params, this);
},
function executeQuery(err) {
if (err) {
throw err;
}
);
}
var psql = new PSQL({
user: params.dbuser,
pass: params.dbpass,
host: params.dbhost,
port: params.dbport,
dbname: params.dbname
});
psql.query(query, function(err, resultSet) {
resultSet = resultSet || {};
var rows = resultSet.rows || [];
queryHandler(err, rows, callback);
});
}
);
};
@@ -135,10 +114,3 @@ function prepareSql(sql) {
.replace(affectedTableRegexCache.pixel_height, '1')
;
}
function shouldQueryPostgresDirectly() {
return global.environment &&
global.environment.enabledFeatures &&
global.environment.enabledFeatures.cdbQueryTablesFromPostgres;
}
-25
View File
@@ -1,25 +0,0 @@
var Varnish = require('node-varnish');
var varnish_queue = null;
function init(host, port, secret) {
varnish_queue = new Varnish.VarnishQueue(host, port, secret);
varnish_queue.on('error', function(e) {
console.log("[CACHE VALIDATOR ERROR] " + e);
});
}
function invalidate_db(dbname, table) {
var cmd = 'purge obj.http.X-Cache-Channel ~ "^' + dbname +
':(.*'+ table +'.*)|(table)$"';
try{
varnish_queue.run_cmd(cmd, false);
} catch (e) {
console.log("[CACHE VALIDATOR ERROR] could not queue command " +
cmd + " -- " + e);
}
}
module.exports = {
init: init,
invalidate_db: invalidate_db
};
-69
View File
@@ -1,7 +1,6 @@
var _ = require('underscore');
var step = require('step');
var Windshaft = require('windshaft');
var Cache = require('./cache_validator');
var os = require('os');
var HealthCheck = require('./monitoring/health_check');
@@ -25,24 +24,6 @@ var CartodbWindshaft = function(serverOptions) {
var templateMaps = serverOptions.templateMaps;
if(serverOptions.cache_enabled) {
console.log("cache invalidation enabled, varnish on ", serverOptions.varnish_host, ' ',
serverOptions.varnish_port);
Cache.init(serverOptions.varnish_host, serverOptions.varnish_port, serverOptions.varnish_secret);
serverOptions.afterStateChange = function(req, data, callback) {
Cache.invalidate_db(req.params.dbname, req.params.table);
callback(null, data);
};
}
serverOptions.beforeStateChange = function(req, callback) {
var err = null;
if ( ! req.params.hasOwnProperty('_authorizedByApiKey') ) {
err = new Error("map state cannot be changed by unauthenticated request!");
}
callback(err, req);
};
// This is for Templated maps
//
// "named" is the official, "template" is for backward compatibility up to 1.6.x
@@ -172,56 +153,6 @@ var CartodbWindshaft = function(serverOptions) {
* END Routing
******************************************************************************************************************/
/**
* Helper to allow access to the layer to be used in the maps infowindow popup.
*/
ws.get(serverOptions.base_url + '/infowindow', function(req, res){
ws.doCORS(res);
step(
function(){
serverOptions.getInfowindow(req, this);
},
function(err, data){
if (err){
ws.sendError(res, {error: err.message}, 500, 'GET INFOWINDOW', err);
//ws.sendResponse(res, [{error: err.message}, 500]);
} else {
ws.sendResponse(res, [{infowindow: data}, 200]);
}
}
);
});
/**
* Helper to allow access to metadata to be used in embedded maps.
*/
ws.get(serverOptions.base_url + '/map_metadata', function(req, res){
ws.doCORS(res);
step(
function(){
serverOptions.getMapMetadata(req, this);
},
function(err, data){
if (err){
ws.sendError(res, {error: err.message}, 500, 'GET MAP_METADATA', err);
//ws.sendResponse(res, [err.message, 500]);
} else {
ws.sendResponse(res, [{map_metadata: data}, 200]);
}
}
);
});
/**
* Helper API to allow per table tile cache (and sql cache) to be invalidated remotely.
* Keep endpoint for backwards compatibility
*/
ws.del(serverOptions.base_url + '/flush_cache', function(req, res){
ws.doCORS(res);
ws.sendResponse(res, [{status: 'ok'}, 200]);
});
var healthCheck = new HealthCheck(cartoData, Windshaft.tilelive);
ws.get('/health', function(req, res) {
var healthConfig = global.environment.health || {};
+8 -6
View File
@@ -1,5 +1,6 @@
var step = require('step');
var _ = require('underscore');
var CdbRequest = require('../models/cdb_request');
function TemplateMapsController(app, serverOptions, templateMaps, metadataBackend, templateBaseUrl, surrogateKeysCache,
NamedMapsCacheEntry, pgConnection) {
@@ -15,6 +16,7 @@ function TemplateMapsController(app, serverOptions, templateMaps, metadataBacken
module.exports = TemplateMapsController;
var cdbRequest = new CdbRequest();
TemplateMapsController.prototype.register = function(app) {
app.get(this.templateBaseUrl + '/:template_id/jsonp', this.jsonp.bind(this));
@@ -33,7 +35,7 @@ TemplateMapsController.prototype.create = function(req, res) {
this.app.doCORS(res);
var cdbuser = self.serverOptions.userByReq(req);
var cdbuser = cdbRequest.userByReq(req);
step(
function checkPerms(){
@@ -80,7 +82,7 @@ TemplateMapsController.prototype.update = function(req, res) {
this.app.doCORS(res);
var cdbuser = this.serverOptions.userByReq(req);
var cdbuser = cdbRequest.userByReq(req);
var template;
var tpl_id;
step(
@@ -140,7 +142,7 @@ TemplateMapsController.prototype.retrieve = function(req, res) {
this.app.doCORS(res);
var cdbuser = this.serverOptions.userByReq(req);
var cdbuser = cdbRequest.userByReq(req);
var tpl_id;
step(
function checkPerms(){
@@ -201,7 +203,7 @@ TemplateMapsController.prototype.destroy = function(req, res) {
}
this.app.doCORS(res);
var cdbuser = this.serverOptions.userByReq(req);
var cdbuser = cdbRequest.userByReq(req);
var tpl_id;
step(
function checkPerms(){
@@ -254,7 +256,7 @@ TemplateMapsController.prototype.list = function(req, res) {
}
this.app.doCORS(res);
var cdbuser = this.serverOptions.userByReq(req);
var cdbuser = cdbRequest.userByReq(req);
step(
function checkPerms(){
@@ -353,7 +355,7 @@ TemplateMapsController.prototype.instantiateTemplate = function(req, res, templa
var template;
var layergroup;
var fakereq; // used for call to createLayergroup
var cdbuser = self.serverOptions.userByReq(req);
var cdbuser = cdbRequest.userByReq(req);
// Format of template_id: [<template_owner>]@<template_id>
var tpl_id = req.params.template_id.split('@');
if ( tpl_id.length > 1 ) {
+26
View File
@@ -0,0 +1,26 @@
function CdbRequest() {
this.RE_USER_FROM_HOST = new RegExp(global.environment.user_from_host ||
'^([^\\.]+)\\.' // would extract "strk" from "strk.cartodb.com"
);
}
module.exports = CdbRequest;
CdbRequest.prototype.userByReq = function(req) {
var host = req.headers.host;
if (req.params.user) {
return req.params.user;
}
var mat = host.match(this.RE_USER_FROM_HOST);
if ( ! mat ) {
console.error("Pattern '" + this.RE_USER_FROM_HOST + "' does not match hostname '" + host + "'");
return;
}
// console.log("Matches: "); console.dir(mat);
if ( mat.length !== 2 ) {
console.error("Pattern '" + this.RE_USER_FROM_HOST + "' gave unexpected matches against '" + host + "': ", mat);
return;
}
return mat[1];
};
+151 -311
View File
@@ -2,37 +2,19 @@ var _ = require('underscore');
var step = require('step');
var QueryTablesApi = require('./api/query_tables_api');
var PgConnection = require('./backends/pg_connection');
var crypto = require('crypto');
var LZMA = require('lzma').LZMA;
var TemplateMaps = require('./template_maps.js');
var MapConfigNamedLayersAdapter = require('./models/mapconfig_named_layers_adapter');
// This is for backward compatibility with 1.3.3
if ( _.isUndefined(global.environment.sqlapi.domain) ) {
// Only use "host" as "domain" if it contains alphanumeric characters
var host = global.environment.sqlapi.host;
if ( host && host.match(/[a-zA-Z]/) ) {
global.environment.sqlapi.domain = host;
}
}
var CdbRequest = require('./models/cdb_request');
var assert = require('assert');
// Whitelist query parameters and attach format
var REQUEST_QUERY_PARAMS_WHITELIST = [
'user',
'sql',
'geom_type',
'cache_buster',
'cache_policy',
'callback',
'interactivity',
'config',
'map_key',
'api_key',
'auth_token',
'style',
'style_version',
'style_convert',
'config',
'scale_factor'
'callback'
];
module.exports = function(redisPool) {
@@ -42,7 +24,8 @@ module.exports = function(redisPool) {
var cartoData = require('cartodb-redis')({ pool: redisPool }),
lzmaWorker = new LZMA(),
pgConnection = new PgConnection(cartoData),
queryTablesApi = new QueryTablesApi(pgConnection, cartoData);
queryTablesApi = new QueryTablesApi(pgConnection, cartoData),
cdbRequest = new CdbRequest();
var rendererConfig = _.defaults(global.environment.renderer || {}, {
cache_ttl: 60000, // milliseconds
@@ -74,8 +57,7 @@ module.exports = function(redisPool) {
cachedir: global.environment.millstone.cache_basedir,
mapnik_version: global.environment.mapnik_version,
mapnik_tile_format: global.environment.mapnik_tile_format || 'png',
default_layergroup_ttl: global.environment.mapConfigTTL || 7200,
gc_prob: 0.01 // @deprecated since Windshaft-1.8.0
default_layergroup_ttl: global.environment.mapConfigTTL || 7200
},
mapnik: {
poolSize: rendererConfig.poolSize,
@@ -131,106 +113,81 @@ module.exports = function(redisPool) {
return dbName + ':' + tableNames.join(',');
};
me.generateMD5 = function(data){
var hash = crypto.createHash('md5');
hash.update(data);
return hash.digest('hex');
};
me.generateCacheChannel = function(app, req, callback){
// Build channelCache key
var dbName = req.params.dbname;
var cacheKey = [ dbName, req.params.token ].join(':');
// Build channelCache key
var dbName = req.params.dbname;
var cacheKey = [ dbName ];
if ( req.params.token ) cacheKey.push(req.params.token);
else if ( req.params.sql ) cacheKey.push( me.generateMD5(req.params.sql) );
cacheKey = cacheKey.join(':');
var that = this;
step (
function checkCached() {
if ( me.channelCache.hasOwnProperty(cacheKey) ) {
callback(null, me.channelCache[cacheKey]);
return;
}
return null;
},
function extractSQL(err) {
if ( err ) throw err;
if ( req.params.token ) {
// TODO: cached cache channel for token-based access should
// be constructed at renderer cache creation time
// See http://github.com/CartoDB/Windshaft-cartodb/issues/152
if ( ! app.mapStore ) {
throw new Error('missing channel cache for token ' + req.params.token);
}
var next = this;
var mapStore = app.mapStore;
step(
function loadFromStore() {
mapStore.load(req.params.token, this);
},
function getSQL(err, mapConfig) {
if (req.profiler) req.profiler.done('mapStore_load');
if ( err ) throw err;
var sql = [];
_.each(mapConfig.obj().layers, function(lyr) {
sql.push(lyr.options.sql);
});
sql = sql.join(';');
return sql;
},
function finish(err, sql) {
next(err, sql);
}
);
return;
}
if ( ! req.params.sql ) {
return null; // no sql
}
// We have sql, and no token...
// strip out windshaft/mapnik inserted sql if present
var sql = req.params.sql.match(/^\((.*)\)\sas\scdbq$/);
sql = (sql !== null) ? sql[1] : req.params.sql;
return sql;
},
function findAffectedTables(err, sql) {
if ( err ) throw err;
if ( ! sql ) {
if ( ! req.params.table ) {
throw new Error("this request doesn't need an X-Cache-Channel generated");
}
return [req.params.table];
}
queryTablesApi.getAffectedTablesInQuery(that.userByReq(req), sql, this); // in addCacheChannel
},
function buildCacheChannel(err, tableNames) {
if ( err ) throw err;
if (req.profiler && ! req.params.table ) {
req.profiler.done('affectedTables');
}
var dbName = req.params.dbname;
var cacheChannel = me.buildCacheChannel(dbName,tableNames);
// store for caching from me.generateCacheChannel
// (not worth when table was specified in params)
if ( ! req.params.table ) {
me.channelCache[cacheKey] = cacheChannel;
}
return cacheChannel;
},
function finish(err, cacheChannel) {
callback(err, cacheChannel);
// no token means no tables associated
if (!req.params.token) {
return callback(null, this.buildCacheChannel(dbName, []));
}
);
step(
function checkCached() {
if ( me.channelCache.hasOwnProperty(cacheKey) ) {
return callback(null, me.channelCache[cacheKey]);
}
return null;
},
function extractSQL(err) {
assert.ifError(err);
// TODO: cached cache channel for token-based access should
// be constructed at renderer cache creation time
// See http://github.com/CartoDB/Windshaft-cartodb/issues/152
if ( ! app.mapStore ) {
throw new Error('missing channel cache for token ' + req.params.token);
}
var mapStore = app.mapStore;
step(
function loadFromStore() {
mapStore.load(req.params.token, this);
},
function getSQL(err, mapConfig) {
if (req.profiler) {
req.profiler.done('mapStore_load');
}
assert.ifError(err);
var queries = mapConfig.getLayers()
.map(function(lyr) {
return lyr.options.sql;
})
.filter(function(sql) {
return !!sql;
});
return queries.length ? queries.join(';') : null;
},
this
);
},
function findAffectedTables(err, sql) {
assert.ifError(err);
if ( ! sql ) {
throw new Error("this request doesn't need an X-Cache-Channel generated");
}
queryTablesApi.getAffectedTablesInQuery(cdbRequest.userByReq(req), sql, this); // in addCacheChannel
},
function buildCacheChannel(err, tableNames) {
assert.ifError(err);
if (req.profiler) {
req.profiler.done('affectedTables');
}
var cacheChannel = me.buildCacheChannel(dbName,tableNames);
me.channelCache[cacheKey] = cacheChannel;
return cacheChannel;
},
function finish(err, cacheChannel) {
callback(err, cacheChannel);
}
);
};
// Set the cache chanel info to invalidate the cache on the frontend server
@@ -245,11 +202,11 @@ module.exports = function(redisPool) {
me.addCacheChannel = function(app, req, cb) {
// skip non-GET requests, or requests for which there's no response
if ( req.method != 'GET' || ! req.res ) { cb(null, null); return; }
if (req.profiler) req.profiler.start('addCacheChannel');
if (req.profiler) {
req.profiler.start('addCacheChannel');
}
var res = req.res;
var cache_policy = req.query.cache_policy;
if ( req.params.token ) cache_policy = 'persist';
if ( cache_policy == 'persist' ) {
if ( req.params.token ) {
res.header('Cache-Control', 'public,max-age=31536000'); // 1 year
} else {
var ttl = global.environment.varnish.ttl || 86400;
@@ -268,8 +225,10 @@ module.exports = function(redisPool) {
res.header('Last-Modified', lastUpdated.toUTCString());
me.generateCacheChannel(app, req, function(err, channel){
if (req.profiler) req.profiler.done('generateCacheChannel');
if (req.profiler) req.profiler.end();
if (req.profiler) {
req.profiler.done('generateCacheChannel');
req.profiler.end();
}
if ( ! err ) {
res.header('X-Cache-Channel', channel);
cb(null, channel);
@@ -282,7 +241,7 @@ module.exports = function(redisPool) {
};
me.beforeLayergroupCreate = function(req, requestMapConfig, callback) {
mapConfigNamedLayersAdapter.getLayers(this.userByReq(req), requestMapConfig.layers, pgConnection,
mapConfigNamedLayersAdapter.getLayers(cdbRequest.userByReq(req), requestMapConfig.layers, pgConnection,
function(err, layers, datasource) {
if (err) {
return callback(err);
@@ -297,7 +256,7 @@ module.exports = function(redisPool) {
me.afterLayergroupCreate = function(req, mapconfig, response, callback) {
var token = response.layergroupid;
var username = this.userByReq(req);
var username = cdbRequest.userByReq(req);
var tasksleft = 2; // redis key and affectedTables
var errors = [];
@@ -324,8 +283,12 @@ module.exports = function(redisPool) {
// take place before proceeding. Error will be logged
// asyncronously
cartoData.incMapviewCount(username, mapconfig.stat_tag, function(err) {
if (req.profiler) req.profiler.done('incMapviewCount');
if ( err ) console.log("ERROR: failed to increment mapview count for user '" + username + "': " + err);
if (req.profiler) {
req.profiler.done('incMapviewCount');
}
if ( err ) {
console.log("ERROR: failed to increment mapview count for user '" + username + "': " + err);
}
done();
});
@@ -341,19 +304,17 @@ module.exports = function(redisPool) {
queryTablesApi.getAffectedTablesAndLastUpdatedTime(username, sql, this);
},
function handleAffectedTablesAndLastUpdatedTime(err, result) {
if (req.profiler) req.profiler.done('queryTablesAndLastUpdated');
if ( err ) throw err;
if (req.profiler) {
req.profiler.done('queryTablesAndLastUpdated');
}
assert.ifError(err);
var cacheChannel = me.buildCacheChannel(dbName, result.affectedTables);
me.channelCache[cacheKey] = cacheChannel;
if (req.res && req.method == 'GET') {
var res = req.res;
if ( req.query && req.query.cache_policy == 'persist' ) {
res.header('Cache-Control', 'public,max-age=31536000'); // 1 year
} else {
var ttl = global.environment.varnish.layergroupTtl || 86400;
res.header('Cache-Control', 'public,max-age='+ttl+',must-revalidate');
}
var ttl = global.environment.varnish.layergroupTtl || 86400;
res.header('Cache-Control', 'public,max-age='+ttl+',must-revalidate');
res.header('Last-Modified', (new Date()).toUTCString());
res.header('X-Cache-Channel', cacheChannel);
}
@@ -371,30 +332,6 @@ module.exports = function(redisPool) {
/* X-Cache-Channel generation } */
me.re_userFromHost = new RegExp(
global.environment.user_from_host ||
'^([^\\.]+)\\.' // would extract "strk" from "strk.cartodb.com"
);
me.userByReq = function(req) {
var host = req.headers.host;
if (req.params.user) {
return req.params.user;
}
var mat = host.match(this.re_userFromHost);
if ( ! mat ) {
console.error("ERROR: user pattern '" + this.re_userFromHost + "' does not match hostname '" + host + "'");
return;
}
// console.log("Matches: "); console.dir(mat);
if ( mat.length !== 2 ) {
console.error("ERROR: pattern '" + this.re_userFromHost + "' gave unexpected matches against '" + host + "': " +
mat);
return;
}
return mat[1];
};
// Check if a request is authorized by a signer
//
// @param req express request object
@@ -402,34 +339,27 @@ module.exports = function(redisPool) {
// null if the request is not signed by anyone
// or will be a string cartodb username otherwise.
//
me.authorizedBySigner = function(req, callback)
{
if ( ! req.params.token || ! req.params.signer ) {
//console.log("No signature provided"); // debugging
callback(null, null); // no signer requested
return;
}
var signer = req.params.signer;
var layergroup_id = req.params.token;
var auth_token = req.params.auth_token;
//console.log("Checking authorization from signer " + signer + " for resource " + layergroup_id +
// " with auth_token " + auth_token);
var mapStore = req.app.mapStore;
if (!mapStore) {
throw new Error('Unable to retrieve map configuration token');
}
mapStore.load(layergroup_id, function(err, mapConfig) {
if (err) {
throw err;
me.authorizedBySigner = function(req, callback) {
if ( ! req.params.token || ! req.params.signer ) {
return callback(null, null); // no signer requested
}
var authorized = me.templateMaps.isAuthorized(mapConfig.obj().template, auth_token);
callback(null, authorized ? signer : null);
});
var signer = req.params.signer;
var layergroup_id = req.params.token;
var auth_token = req.params.auth_token;
var mapStore = req.app.mapStore;
if (!mapStore) {
throw new Error('Unable to retrieve map configuration token');
}
mapStore.load(layergroup_id, function(err, mapConfig) {
assert.ifError(err);
var authorized = me.templateMaps.isAuthorized(mapConfig.obj().template, auth_token);
return callback(null, authorized ? signer : null);
});
};
// Check if a request is authorized by api_key
@@ -450,13 +380,13 @@ module.exports = function(redisPool) {
return;
}
//console.log("given ApiKey: " + givenKey);
var user = me.userByReq(req);
var user = cdbRequest.userByReq(req);
step(
function (){
cartoData.getUserMapKey(user, this);
},
function checkApiKey(err, val){
if (err) throw err;
assert.ifError(err);
return ( val && givenKey == val ) ? 1 : 0;
},
function finish(err, authorized) {
@@ -473,15 +403,17 @@ module.exports = function(redisPool) {
*/
me.authorize = function(req, callback) {
var that = this;
var user = me.userByReq(req);
var user = cdbRequest.userByReq(req);
step(
function (){
that.authorizedByAPIKey(req, this);
},
function checkApiKey(err, authorized){
if (req.profiler) req.profiler.done('authorizedByAPIKey');
if (err) throw err;
if (req.profiler) {
req.profiler.done('authorizedByAPIKey');
}
assert.ifError(err);
// if not authorized by api_key, continue
if (authorized !== 1) {
@@ -491,28 +423,16 @@ module.exports = function(redisPool) {
return;
}
_.extend(req.params, { _authorizedByApiKey: true });
// authorized by api key, login as the given username and stop
pgConnection.setDBAuth(user, req.params, function(err) {
callback(err, true); // authorized (or error)
});
},
function checkSignAuthorized(err, signed_by){
if (err) throw err;
if (req.profiler) {
if ( req.params._authorizedByApiKey ) {
req.profiler.done('setDBAuth');
} else {
req.profiler.done('authorizedBySigner');
}
}
assert.ifError(err);
if ( ! signed_by ) {
// request not authorized by signer.
// if table was given, continue to check table privacy
if ( req.params.table ) return null;
// if no signer name was given, let dbparams and
// PostgreSQL do the rest.
@@ -527,26 +447,12 @@ module.exports = function(redisPool) {
return;
}
// Authorized by "signed_by" !
_.extend(req.params, { _authorizedBySigner: signed_by });
pgConnection.setDBAuth(signed_by, req.params, function(err) {
if (req.profiler) req.profiler.done('setDBAuth');
if (req.profiler) {
req.profiler.done('setDBAuth');
}
callback(err, true); // authorized (or error)
});
},
function getDatabase(err){
if (err) throw err;
// NOTE: only used to get to table privacy
cartoData.getUserDBName(user, this);
},
function getPrivacy(err, dbname){
if (err) throw err;
if (req.profiler) req.profiler.done('tablePrivacy_getUserDBName');
cartoData.getTablePrivacy(dbname, req.params.table, this);
},
function(err, privacy){
if (req.profiler) req.profiler.done('getTablePrivacy');
callback(err, privacy !== "0");
}
);
};
@@ -562,9 +468,6 @@ module.exports = function(redisPool) {
if ( req.query.lzma ) {
// TODO: check ?
//console.log("type of req.query.lzma is " + typeof(req.query.lzma));
// Decode (from base64)
var lzma = new Buffer(req.query.lzma, 'base64')
.toString('binary')
@@ -577,7 +480,9 @@ module.exports = function(redisPool) {
lzmaWorker.decompress(
lzma,
function(result) {
if (req.profiler) req.profiler.done('LZMA decompress');
if (req.profiler) {
req.profiler.done('lzma');
}
try {
delete req.query.lzma;
_.extend(req.query, JSON.parse(result));
@@ -585,30 +490,29 @@ module.exports = function(redisPool) {
} catch (err) {
callback(new Error('Error parsing lzma as JSON: ' + err));
}
},
function(/*percent*/) { // progress
//console.log("LZMA decompression " + percent + "%");
}
);
return;
}
var bad_query = _.difference(_.keys(req.query), REQUEST_QUERY_PARAMS_WHITELIST);
req.query = _.pick(req.query, REQUEST_QUERY_PARAMS_WHITELIST);
req.params = _.extend({}, req.params); // shuffle things as request is a strange array/object
_.each(bad_query, function(key){ delete req.query[key]; });
req.params = _.extend({}, req.params); // shuffle things as request is a strange array/object
var user = me.userByReq(req);
var user = cdbRequest.userByReq(req);
if ( req.params.token ) {
//console.log("Request parameters include token " + req.params.token);
var tksplit = req.params.token.split(':');
req.params.token = tksplit[0];
if ( tksplit.length > 1 ) req.params.cache_buster= tksplit[1];
if ( tksplit.length > 1 ) {
req.params.cache_buster= tksplit[1];
}
tksplit = req.params.token.split('@');
if ( tksplit.length > 1 ) {
req.params.signer = tksplit.shift();
if ( ! req.params.signer ) req.params.signer = user;
if ( ! req.params.signer ) {
req.params.signer = user;
}
else if ( req.params.signer !== user ) {
var err = new Error('Cannot use map signature of user "' + req.params.signer + '" on database of user "' +
user + '"');
@@ -627,18 +531,19 @@ module.exports = function(redisPool) {
// bring all query values onto req.params object
_.extend(req.params, req.query);
// for cartodb, ensure interactivity is cartodb_id or user specified
req.params.interactivity = req.params.interactivity || 'cartodb_id';
if (req.profiler) req.profiler.done('req2params.setup');
if (req.profiler) {
req.profiler.done('req2params.setup');
}
step(
function getPrivacy(){
me.authorize(req, this);
},
function gatekeep(err, authorized){
if (req.profiler) req.profiler.done('authorize');
if(err) throw err;
if (req.profiler) {
req.profiler.done('authorize');
}
assert.ifError(err);
if(!authorized) {
err = new Error("Sorry, you are unauthorized (permission denied)");
err.http_status = 403;
@@ -647,21 +552,12 @@ module.exports = function(redisPool) {
return null;
},
function getDatabase(err){
if(err) throw err;
assert.ifError(err);
pgConnection.setDBConn(user, req.params, this);
},
function getGeometryType(err){
if (req.profiler) req.profiler.done('setDBConn');
if (err) throw err;
if ( ! req.params.table ) return null;
cartoData.getTableGeometryType(req.params.dbname, req.params.table, this);
},
function finishSetup(err, data){
function finishSetup(err) {
if ( err ) { callback(err, req); return; }
if (!_.isNull(data))
_.extend(req.params, {geom_type: data});
// Add default database connection parameters
// if none given
_.defaults(req.params, {
@@ -676,61 +572,5 @@ module.exports = function(redisPool) {
);
};
/**
* Little helper method to get the current list of infowindow variables and return to client
* @param req
* @param callback
*/
me.getInfowindow = function(req, callback){
var that = this;
var user = me.userByReq(req);
step(
function(){
// TODO: if this step really needed ?
that.req2params(req, this);
},
function getDatabase(err){
if (err) throw err;
cartoData.getUserDBName(user, this);
},
function getInfowindow(err, dbname){
if (err) throw err;
cartoData.getTableInfowindow(dbname, req.params.table, this);
},
function(err, data){
callback(err, data);
}
);
};
/**
* Little helper method to get map metadata and return to client
* @param req
* @param callback
*/
me.getMapMetadata = function(req, callback){
var that = this;
var user = me.userByReq(req);
step(
function(){
// TODO: if this step really needed ?
that.req2params(req, this);
},
function getDatabase(err){
if (err) throw err;
cartoData.getUserDBName(user, this);
},
function getMapMetadata(err, dbname){
if (err) throw err;
cartoData.getTableMapMetadata(dbname, req.params.table, this);
},
function(err, data){
callback(err, data);
}
);
};
return me;
};
-66
View File
@@ -1,66 +0,0 @@
var _ = require('underscore'),
request = require('request');
module.exports.query = function (username, api_key, sql, callback) {
var api = global.environment.sqlapi;
// build up api string
var sqlapihostname = username;
if ( api.domain ) sqlapihostname += '.' + api.domain;
var sqlapi = api.protocol + '://';
if ( api.host && api.host != api.domain ) sqlapi += api.host;
else sqlapi += sqlapihostname;
sqlapi += ':' + api.port + '/api/' + api.version + '/sql';
var qs = { q: sql };
// add api_key if given
if (_.isString(api_key) && api_key !== '') { qs.api_key = api_key; }
// call sql api
//
// NOTE: using POST to avoid size limits:
// See http://github.com/CartoDB/Windshaft-cartodb/issues/111
//
// NOTE: uses "host" header to allow IP based specification
// of sqlapi address (and avoid a DNS lookup)
//
// NOTE: allows for keeping up to "maxConnections" concurrent
// sockets opened per SQL-API host.
// See http://nodejs.org/api/http.html#http_agent_maxsockets
//
var maxSockets = global.environment.maxConnections || 128;
var maxGetLen = api.max_get_sql_length || 2048;
var maxSQLTime = api.timeout || 100; // 1/10 of a second by default
var reqSpec = {
url:sqlapi,
json:true,
headers:{host: sqlapihostname},
// http://nodejs.org/api/http.html#http_agent_maxsockets
pool:{maxSockets:maxSockets},
// timeout in milliseconds
timeout:maxSQLTime
};
if ( sql.length > maxGetLen ) {
reqSpec.method = 'POST';
reqSpec.body = qs;
} else {
reqSpec.method = 'GET';
reqSpec.qs = qs;
}
request(reqSpec, function(err, res, body) {
if (err){
console.log('ERROR connecting to SQL API on ' + sqlapi + ': ' + err);
callback(err);
return;
}
if (res.statusCode != 200) {
var msg = res.body.error ? res.body.error : res.body;
callback(new Error(msg));
console.log('unexpected response status (' + res.statusCode + ') for sql query: ' + sql + ': ' + msg);
return;
}
callback(null, body.rows);
});
};
+106 -56
View File
@@ -1,6 +1,6 @@
{
"name": "windshaft-cartodb",
"version": "1.30.1",
"version": "2.0.0",
"dependencies": {
"cartodb-psql": {
"version": "0.4.0",
@@ -14,11 +14,13 @@
"dependencies": {
"generic-pool": {
"version": "2.0.3",
"from": "generic-pool@2.0.3"
"from": "generic-pool@2.0.3",
"resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-2.0.3.tgz"
},
"buffer-writer": {
"version": "1.0.0",
"from": "buffer-writer@1.0.0"
"from": "buffer-writer@1.0.0",
"resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-1.0.0.tgz"
}
}
}
@@ -36,19 +38,23 @@
"dependencies": {
"generic-pool": {
"version": "2.1.1",
"from": "generic-pool@~2.1.1"
"from": "generic-pool@~2.1.1",
"resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-2.1.1.tgz"
},
"redis": {
"version": "0.12.1",
"from": "redis@~0.12.1"
"from": "redis@~0.12.1",
"resolved": "https://registry.npmjs.org/redis/-/redis-0.12.1.tgz"
},
"hiredis": {
"version": "0.1.17",
"from": "hiredis@~0.1.17",
"resolved": "https://registry.npmjs.org/hiredis/-/hiredis-0.1.17.tgz",
"dependencies": {
"bindings": {
"version": "1.2.1",
"from": "bindings@*"
"from": "bindings@*",
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.2.1.tgz"
},
"nan": {
"version": "1.1.2",
@@ -83,7 +89,8 @@
"dependencies": {
"core-util-is": {
"version": "1.0.1",
"from": "core-util-is@~1.0.0"
"from": "core-util-is@~1.0.0",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz"
},
"isarray": {
"version": "0.0.1",
@@ -92,11 +99,13 @@
},
"string_decoder": {
"version": "0.10.31",
"from": "string_decoder@~0.10.x"
"from": "string_decoder@~0.10.x",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz"
},
"inherits": {
"version": "2.0.1",
"from": "inherits@2"
"from": "inherits@~2.0.1",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
}
}
}
@@ -107,11 +116,6 @@
"from": "lzma@~1.3.7",
"resolved": "https://registry.npmjs.org/lzma/-/lzma-1.3.7.tgz"
},
"node-varnish": {
"version": "0.3.0",
"from": "https://github.com/Vizzuality/node-varnish/tarball/0.3.0",
"resolved": "https://github.com/Vizzuality/node-varnish/tarball/0.3.0"
},
"queue-async": {
"version": "1.0.7",
"from": "queue-async@~1.0.7",
@@ -124,19 +128,23 @@
"dependencies": {
"generic-pool": {
"version": "2.1.1",
"from": "generic-pool@~2.1.1"
"from": "generic-pool@~2.1.1",
"resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-2.1.1.tgz"
},
"redis": {
"version": "0.12.1",
"from": "redis@~0.12.1"
"from": "redis@~0.12.1",
"resolved": "https://registry.npmjs.org/redis/-/redis-0.12.1.tgz"
},
"hiredis": {
"version": "0.1.17",
"from": "hiredis@~0.1.17",
"resolved": "https://registry.npmjs.org/hiredis/-/hiredis-0.1.17.tgz",
"dependencies": {
"bindings": {
"version": "1.2.1",
"from": "bindings@*"
"from": "bindings@*",
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.2.1.tgz"
},
"nan": {
"version": "1.1.2",
@@ -164,17 +172,20 @@
},
"lru-cache": {
"version": "2.2.4",
"from": "lru-cache@~2.2.1"
"from": "lru-cache@~2.2.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.2.4.tgz"
},
"json-stringify-safe": {
"version": "5.0.0",
"from": "json-stringify-safe@~5.0.0"
"from": "json-stringify-safe@~5.0.0",
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.0.tgz"
}
}
},
"step": {
"version": "0.0.5",
"from": "step@~0.0.5"
"from": "step@~0.0.5",
"resolved": "https://registry.npmjs.org/step/-/step-0.0.5.tgz"
},
"underscore": {
"version": "1.6.0",
@@ -182,8 +193,9 @@
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz"
},
"windshaft": {
"version": "0.40.0",
"from": "windshaft@~0.40.0",
"version": "0.41.0",
"from": "https://github.com/CartoDB/Windshaft/tarball/master",
"resolved": "https://github.com/CartoDB/Windshaft/tarball/master",
"dependencies": {
"chronograph": {
"version": "0.1.0",
@@ -193,6 +205,7 @@
"grainstore": {
"version": "0.23.0",
"from": "grainstore@~0.23.0",
"resolved": "https://registry.npmjs.org/grainstore/-/grainstore-0.23.0.tgz",
"dependencies": {
"redis-mpool": {
"version": "0.1.0",
@@ -201,19 +214,23 @@
"dependencies": {
"generic-pool": {
"version": "2.1.1",
"from": "generic-pool@~2.1.1"
"from": "generic-pool@~2.1.1",
"resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-2.1.1.tgz"
},
"redis": {
"version": "0.12.1",
"from": "redis@~0.12.1"
"from": "redis@~0.12.1",
"resolved": "https://registry.npmjs.org/redis/-/redis-0.12.1.tgz"
},
"hiredis": {
"version": "0.1.17",
"from": "hiredis@~0.1.17",
"resolved": "https://registry.npmjs.org/hiredis/-/hiredis-0.1.17.tgz",
"dependencies": {
"bindings": {
"version": "1.2.1",
"from": "bindings@*"
"from": "bindings@*",
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.2.1.tgz"
},
"nan": {
"version": "1.1.2",
@@ -246,7 +263,8 @@
"dependencies": {
"sax": {
"version": "0.5.8",
"from": "sax@0.5.x"
"from": "sax@0.5.x",
"resolved": "https://registry.npmjs.org/sax/-/sax-0.5.8.tgz"
}
}
},
@@ -262,7 +280,8 @@
},
"minimist": {
"version": "0.0.10",
"from": "minimist@~0.0.1"
"from": "minimist@~0.0.1",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz"
}
}
}
@@ -275,7 +294,8 @@
"dependencies": {
"generic-pool": {
"version": "2.0.4",
"from": "generic-pool@~2.0.3"
"from": "generic-pool@~2.0.3",
"resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-2.0.4.tgz"
},
"request": {
"version": "2.34.0",
@@ -284,15 +304,18 @@
"dependencies": {
"qs": {
"version": "0.6.6",
"from": "qs@~0.6.0"
"from": "qs@~0.6.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-0.6.6.tgz"
},
"json-stringify-safe": {
"version": "5.0.0",
"from": "json-stringify-safe@~5.0.0"
"from": "json-stringify-safe@~5.0.0",
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.0.tgz"
},
"forever-agent": {
"version": "0.5.2",
"from": "forever-agent@~0.5.0"
"from": "forever-agent@~0.5.0",
"resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.5.2.tgz"
},
"node-uuid": {
"version": "1.4.3",
@@ -302,6 +325,7 @@
"tough-cookie": {
"version": "0.12.1",
"from": "tough-cookie@>=0.12.0",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-0.12.1.tgz",
"dependencies": {
"punycode": {
"version": "1.3.2",
@@ -313,6 +337,7 @@
"form-data": {
"version": "0.1.4",
"from": "form-data@~0.1.0",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-0.1.4.tgz",
"dependencies": {
"combined-stream": {
"version": "0.0.7",
@@ -335,7 +360,8 @@
},
"tunnel-agent": {
"version": "0.3.0",
"from": "tunnel-agent@~0.3.0"
"from": "tunnel-agent@~0.3.0",
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.3.0.tgz"
},
"http-signature": {
"version": "0.10.1",
@@ -361,7 +387,8 @@
},
"oauth-sign": {
"version": "0.3.0",
"from": "oauth-sign@~0.3.0"
"from": "oauth-sign@~0.3.0",
"resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.3.0.tgz"
},
"hawk": {
"version": "1.0.0",
@@ -380,17 +407,20 @@
},
"cryptiles": {
"version": "0.2.2",
"from": "cryptiles@0.2.x"
"from": "cryptiles@0.2.x",
"resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-0.2.2.tgz"
},
"sntp": {
"version": "0.2.4",
"from": "sntp@0.2.x"
"from": "sntp@0.2.x",
"resolved": "https://registry.npmjs.org/sntp/-/sntp-0.2.4.tgz"
}
}
},
"aws-sign2": {
"version": "0.5.0",
"from": "aws-sign2@~0.5.0"
"from": "aws-sign2@~0.5.0",
"resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.5.0.tgz"
}
}
},
@@ -1631,7 +1661,8 @@
},
"mkdirp": {
"version": "0.3.5",
"from": "mkdirp@~0.3.3"
"from": "mkdirp@~0.3.3",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz"
},
"optimist": {
"version": "0.6.1",
@@ -1645,7 +1676,8 @@
},
"minimist": {
"version": "0.0.10",
"from": "minimist@~0.0.1"
"from": "minimist@~0.0.1",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz"
}
}
}
@@ -1704,7 +1736,8 @@
},
"minimist": {
"version": "0.0.10",
"from": "minimist@~0.0.1"
"from": "minimist@~0.0.1",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz"
}
}
}
@@ -1717,7 +1750,8 @@
"dependencies": {
"generic-pool": {
"version": "2.1.1",
"from": "generic-pool@~2.1.1"
"from": "generic-pool@~2.1.1",
"resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-2.1.1.tgz"
},
"mime": {
"version": "1.2.11",
@@ -1733,7 +1767,8 @@
"dependencies": {
"nan": {
"version": "1.2.0",
"from": "nan@~1.2.0"
"from": "nan@~1.2.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-1.2.0.tgz"
},
"mapnik-vector-tile": {
"version": "0.5.5",
@@ -2144,7 +2179,8 @@
},
"minimist": {
"version": "0.0.10",
"from": "minimist@~0.0.1"
"from": "minimist@~0.0.1",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz"
}
}
}
@@ -2152,11 +2188,13 @@
},
"step-profiler": {
"version": "0.2.1",
"from": "step-profiler@~0.2.1"
"from": "step-profiler@~0.2.1",
"resolved": "https://registry.npmjs.org/step-profiler/-/step-profiler-0.2.1.tgz"
},
"torque.js": {
"version": "2.11.0",
"from": "torque.js@~2.11.0"
"from": "torque.js@~2.11.0",
"resolved": "https://registry.npmjs.org/torque.js/-/torque.js-2.11.0.tgz"
},
"request": {
"version": "2.48.0",
@@ -2175,7 +2213,8 @@
"dependencies": {
"core-util-is": {
"version": "1.0.1",
"from": "core-util-is@~1.0.0"
"from": "core-util-is@~1.0.0",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz"
},
"isarray": {
"version": "0.0.1",
@@ -2184,11 +2223,13 @@
},
"string_decoder": {
"version": "0.10.31",
"from": "string_decoder@~0.10.x"
"from": "string_decoder@~0.10.x",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz"
},
"inherits": {
"version": "2.0.1",
"from": "inherits@~2.0.1"
"from": "inherits@~2.0.1",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
}
}
}
@@ -2201,11 +2242,13 @@
},
"forever-agent": {
"version": "0.5.2",
"from": "forever-agent@~0.5.0"
"from": "forever-agent@~0.5.0",
"resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.5.2.tgz"
},
"form-data": {
"version": "0.1.4",
"from": "form-data@~0.1.0",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-0.1.4.tgz",
"dependencies": {
"mime": {
"version": "1.2.11",
@@ -2221,7 +2264,8 @@
},
"json-stringify-safe": {
"version": "5.0.0",
"from": "json-stringify-safe@~5.0.0"
"from": "json-stringify-safe@~5.0.0",
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.0.tgz"
},
"mime-types": {
"version": "1.0.2",
@@ -2240,11 +2284,13 @@
},
"tunnel-agent": {
"version": "0.4.0",
"from": "tunnel-agent@~0.4.0"
"from": "tunnel-agent@~0.4.0",
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.0.tgz"
},
"tough-cookie": {
"version": "0.12.1",
"from": "tough-cookie@>=0.12.0",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-0.12.1.tgz",
"dependencies": {
"punycode": {
"version": "1.3.2",
@@ -2297,21 +2343,25 @@
},
"cryptiles": {
"version": "0.2.2",
"from": "cryptiles@0.2.x"
"from": "cryptiles@0.2.x",
"resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-0.2.2.tgz"
},
"sntp": {
"version": "0.2.4",
"from": "sntp@0.2.x"
"from": "sntp@0.2.x",
"resolved": "https://registry.npmjs.org/sntp/-/sntp-0.2.4.tgz"
}
}
},
"aws-sign2": {
"version": "0.5.0",
"from": "aws-sign2@~0.5.0"
"from": "aws-sign2@~0.5.0",
"resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.5.0.tgz"
},
"stringstream": {
"version": "0.0.4",
"from": "stringstream@~0.0.4"
"from": "stringstream@~0.0.4",
"resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.4.tgz"
},
"combined-stream": {
"version": "0.0.7",
@@ -2329,8 +2379,8 @@
},
"abaculus": {
"version": "1.1.0",
"from": "https://github.com/CartoDB/abaculus/tarball/cdb",
"resolved": "https://github.com/CartoDB/abaculus/tarball/cdb"
"from": "https://github.com/CartoDB/abaculus/tarball/cdb-stats",
"resolved": "https://github.com/CartoDB/abaculus/tarball/cdb-stats"
},
"sphericalmercator": {
"version": "1.0.2",
+3 -3
View File
@@ -1,7 +1,7 @@
{
"private": true,
"name": "windshaft-cartodb",
"version": "1.30.1",
"version": "2.0.0",
"description": "A map tile server for CartoDB",
"keywords": [
"cartodb"
@@ -22,10 +22,9 @@
"Sandro Santilli <strk@vizzuality.com>"
],
"dependencies": {
"node-varnish": "https://github.com/Vizzuality/node-varnish/tarball/0.3.0",
"underscore" : "~1.6.0",
"dot": "~1.0.2",
"windshaft": "~0.40.0",
"windshaft": "https://github.com/CartoDB/Windshaft/tarball/master",
"step": "~0.0.5",
"queue-async": "~1.0.7",
"request": "~2.9.203",
@@ -39,6 +38,7 @@
"devDependencies": {
"istanbul": "~0.3.6",
"mocha": "~1.21.4",
"nock": "~1.3.0",
"jshint": "~2.6.0",
"redis": "~0.8.6",
"strftime": "~0.8.2",
+105 -45
View File
@@ -1,27 +1,24 @@
require(__dirname + '/../../support/test_helper');
var assert = require('../../support/assert');
var redis = require('redis');
var Step = require('step');
var helper = require(__dirname + '/../../support/test_helper');
var SqlApiEmulator = require(__dirname + '/../../support/SQLAPIEmu.js');
var step = require('step');
var NamedMapsCacheEntry = require(__dirname + '/../../../lib/cartodb/cache/model/named_maps_entry');
var SurrogateKeysCache = require(__dirname + '/../../../lib/cartodb/cache/surrogate_keys_cache');
var CartodbWindshaft = require(__dirname + '/../../../lib/cartodb/cartodb_windshaft');
var ServerOptions = require(__dirname + '/../../../lib/cartodb/server_options');
var serverOptions = ServerOptions();
suite('templates surrogate keys', function() {
describe('templates surrogate keys', function() {
var sqlApiServer;
var redisClient = redis.createClient(global.environment.redis.port);
// Enable Varnish purge for tests
serverOptions.varnish_purge_enabled = true;
var varnishHost = global.environment.varnish.host;
global.environment.varnish.host = '127.0.0.1';
var varnishPurgeEnabled = global.environment.varnish.purge_enabled;
global.environment.varnish.purge_enabled = true;
var serverOptions = require('../../../lib/cartodb/server_options')();
var server = new CartodbWindshaft(serverOptions);
var templateOwner = 'localhost',
@@ -48,15 +45,28 @@ suite('templates surrogate keys', function() {
},
expectedBody = { template_id: expectedTemplateId };
suiteSetup(function(done) {
sqlApiServer = new SqlApiEmulator(global.environment.sqlapi.port, done);
});
var varnishHttpUrl = [
'http://', global.environment.varnish.host, ':', global.environment.varnish.http_port
].join('');
var surrogateKeysCacheInvalidateFn = SurrogateKeysCache.prototype.invalidate;
var cacheEntryKey = new NamedMapsCacheEntry(templateOwner, templateName).key();
var invalidationMatchHeader = '\\b' + cacheEntryKey + '\\b';
var nock = require('nock');
nock.enableNetConnect(/(127.0.0.1:5555|cartocdn.com)/);
after(function(done) {
serverOptions.varnish_purge_enabled = false;
global.environment.varnish.host = varnishHost;
global.environment.varnish.purge_enabled = varnishPurgeEnabled;
nock.restore();
done();
});
function createTemplate(callback) {
var postTemplateRequest = {
url: '/tiles/template?api_key=1234',
url: '/api/v1/map/named?api_key=1234',
method: 'POST',
headers: {
host: templateOwner,
@@ -65,7 +75,7 @@ suite('templates surrogate keys', function() {
data: JSON.stringify(template)
};
Step(
step(
function postTemplate() {
var next = this;
assert.response(server,
@@ -92,15 +102,14 @@ suite('templates surrogate keys', function() {
);
}
test("update template calls surrogate keys invalidation", function(done) {
var cacheEntryKey;
var surrogateKeysCacheInvalidateMethodInvoked = false;
SurrogateKeysCache.prototype.invalidate = function(cacheEntry) {
cacheEntryKey = cacheEntry.key();
surrogateKeysCacheInvalidateMethodInvoked = true;
};
it("invalidates surrogate keys on template update", function(done) {
Step(
var scope = nock(varnishHttpUrl)
.intercept('/key', 'PURGE')
.matchHeader('Invalidation-Match', invalidationMatchHeader)
.reply(204, '');
step(
function createTemplateToUpdate() {
createTemplate(this);
},
@@ -109,7 +118,7 @@ suite('templates surrogate keys', function() {
throw err;
}
var updateTemplateRequest = {
url: '/tiles/template/' + expectedTemplateId + '/?api_key=1234',
url: '/api/v1/map/named/' + expectedTemplateId + '/?api_key=1234',
method: 'PUT',
headers: {
host: templateOwner,
@@ -135,8 +144,7 @@ suite('templates surrogate keys', function() {
var parsedBody = JSON.parse(res.body);
assert.deepEqual(parsedBody, expectedBody);
assert.ok(surrogateKeysCacheInvalidateMethodInvoked);
assert.equal(cacheEntryKey, new NamedMapsCacheEntry(templateOwner, templateName).key());
assert.equal(scope.pendingMocks().length, 0);
return null;
},
@@ -156,16 +164,14 @@ suite('templates surrogate keys', function() {
);
});
test("delete template calls surrogate keys invalidation", function(done) {
it("invalidates surrogate on template deletion", function(done) {
var cacheEntryKey;
var surrogateKeysCacheInvalidateMethodInvoked = false;
SurrogateKeysCache.prototype.invalidate = function(cacheEntry) {
cacheEntryKey = cacheEntry.key();
surrogateKeysCacheInvalidateMethodInvoked = true;
};
var scope = nock(varnishHttpUrl)
.intercept('/key', 'PURGE')
.matchHeader('Invalidation-Match', invalidationMatchHeader)
.reply(204, '');
Step(
step(
function createTemplateToDelete() {
createTemplate(this);
},
@@ -174,7 +180,7 @@ suite('templates surrogate keys', function() {
throw err;
}
var deleteTemplateRequest = {
url: '/tiles/template/' + expectedTemplateId + '/?api_key=1234',
url: '/api/v1/map/named/' + expectedTemplateId + '/?api_key=1234',
method: 'DELETE',
headers: {
host: templateOwner,
@@ -197,8 +203,7 @@ suite('templates surrogate keys', function() {
throw err;
}
assert.ok(surrogateKeysCacheInvalidateMethodInvoked);
assert.equal(cacheEntryKey, new NamedMapsCacheEntry(templateOwner, templateName).key());
assert.equal(scope.pendingMocks().length, 0);
return null;
},
@@ -208,11 +213,66 @@ suite('templates surrogate keys', function() {
);
});
suiteTeardown(function(done) {
SurrogateKeysCache.prototype.invalidate = surrogateKeysCacheInvalidateFn;
// Enable Varnish purge for tests
serverOptions.varnish_purge_enabled = false;
sqlApiServer.close(done);
it("should update template even if surrogate key invalidation fails", function(done) {
var scope = nock(varnishHttpUrl)
.intercept('/key', 'PURGE')
.matchHeader('Invalidation-Match', invalidationMatchHeader)
.reply(503, '');
step(
function createTemplateToUpdate() {
createTemplate(this);
},
function putValidTemplate(err) {
if (err) {
throw err;
}
var updateTemplateRequest = {
url: '/api/v1/map/named/' + expectedTemplateId + '/?api_key=1234',
method: 'PUT',
headers: {
host: templateOwner,
'Content-Type': 'application/json'
},
data: JSON.stringify(template)
};
var next = this;
assert.response(server,
updateTemplateRequest,
{
status: 200
},
function(res) {
next(null, res);
}
);
},
function checkValidUpdate(err, res) {
if (err) {
throw err;
}
var parsedBody = JSON.parse(res.body);
assert.deepEqual(parsedBody, expectedBody);
assert.equal(scope.pendingMocks().length, 0);
return null;
},
function finish(err) {
if ( err ) {
return done(err);
}
redisClient.keys("map_*|localhost", function(err, keys) {
if ( err ) {
return done(err);
}
redisClient.del(keys, function(err) {
return done(err);
});
});
}
);
});
});
-21
View File
@@ -1,21 +0,0 @@
var assert = require('../support/assert');
require(__dirname + '/../support/test_helper');
var CacheValidator = require(__dirname + '/../../lib/cartodb/cache_validator');
var VarnishEmu = require('../support/VarnishEmu');
suite('cache_validator', function() {
test('should call purge on varnish when invalidate database', function(done) {
var varnish = new VarnishEmu(function(cmds) {
assert.ok(cmds.length == 1);
assert.equal('purge obj.http.X-Cache-Channel ~ \"^test_db:(.*test_cache.*)|(table)$\"\n', cmds[0].toString('utf8'));
done();
},
function() {
CacheValidator.init('localhost', 1337);
CacheValidator.invalidate_db('test_db', 'test_cache');
});
});
});
+110 -159
View File
@@ -1,12 +1,8 @@
var assert = require('../support/assert');
var tests = module.exports = {};
var _ = require('underscore');
var redis = require('redis');
var querystring = require('querystring');
var semver = require('semver');
var Step = require('step');
var step = require('step');
var strftime = require('strftime');
var SQLAPIEmu = require(__dirname + '/../support/SQLAPIEmu.js');
var redis_stats_db = 5;
var helper = require(__dirname + '/../support/test_helper');
@@ -17,44 +13,39 @@ var IMAGE_EQUALS_TOLERANCE_PER_MIL = 20;
var IMAGE_EQUALS_HIGHER_TOLERANCE_PER_MIL = 25;
var CartodbWindshaft = require(__dirname + '/../../lib/cartodb/cartodb_windshaft');
var ServerOptions = require(__dirname + '/../../lib/cartodb/server_options');
serverOptions = ServerOptions();
var server = new CartodbWindshaft(serverOptions);
var serverOptions = require(__dirname + '/../../lib/cartodb/server_options');
var server = new CartodbWindshaft(serverOptions());
server.setMaxListeners(0);
['/tiles/layergroup', '/u/localhost/tiles/layergroup'].forEach(function(layergroup_url) {
[true, false].forEach(function(cdbQueryTablesFromPostgresEnabledValue) {
['/api/v1/map', '/u/localhost/api/v1/map'].forEach(function(layergroup_url) {
var suiteName = 'multilayer:postgres=' + cdbQueryTablesFromPostgresEnabledValue + ";layergroup_url=" + layergroup_url;
var suiteName = 'multilayer:postgres=layergroup_url=' + layergroup_url;
suite(suiteName, function() {
var cdbQueryTablesFromPostgresEnabledValue = true;
var redis_client = redis.createClient(global.environment.redis.port);
var sqlapi_server;
var expected_last_updated_epoch = 1234567890123; // this is hard-coded into SQLAPIEmu
var expected_last_updated = new Date(expected_last_updated_epoch).toISOString();
var test_user = _.template(global.environment.postgres_auth_user, {user_id:1});
var test_pubuser = global.environment.postgres.user;
var test_database = test_user + '_db';
suiteSetup(function(done){
global.environment.enabledFeatures = { cdbQueryTablesFromPostgres: cdbQueryTablesFromPostgresEnabledValue };
sqlapi_server = new SQLAPIEmu(global.environment.sqlapi.port, done);
});
test("layergroup with 2 layers, each with its style", function(done) {
var layergroup = {
version: '1.0.0',
layers: [
{ options: {
sql: 'select cartodb_id, ST_Translate(the_geom_webmercator, 5e6, 0) as the_geom_webmercator from test_table limit 2',
sql: 'select cartodb_id, ST_Translate(the_geom_webmercator, 5e6, 0) as the_geom_webmercator' +
' from test_table limit 2',
cartocss: '#layer { marker-fill:red; marker-width:32; marker-allow-overlap:true; }',
cartocss_version: '2.0.1',
interactivity: 'cartodb_id'
} },
{ options: {
sql: 'select cartodb_id, ST_Translate(the_geom_webmercator, -5e6, 0) as the_geom_webmercator from test_table limit 2 offset 2',
sql: 'select cartodb_id, ST_Translate(the_geom_webmercator, -5e6, 0) as the_geom_webmercator' +
' from test_table limit 2 offset 2',
cartocss: '#layer { marker-fill:blue; marker-allow-overlap:true; }',
cartocss_version: '2.0.2',
interactivity: 'cartodb_id'
@@ -63,7 +54,7 @@ suite(suiteName, function() {
};
var expected_token; // = "e34dd7e235138a062f8ba7ad051aa3a7";
Step(
step(
function do_post()
{
var next = this;
@@ -75,14 +66,6 @@ suite(suiteName, function() {
}, {}, function(res) {
assert.equal(res.statusCode, 200, res.body);
var parsedBody = JSON.parse(res.body);
var expectedBody = { layergroupid: expected_token };
// check last modified
var qTables = JSON.stringify({
'q': 'SELECT CDB_QueryTables($windshaft$'
+ layergroup.layers[0].options.sql + ';'
+ layergroup.layers[1].options.sql
+ '$windshaft$)'
});
assert.equal(parsedBody.last_updated, expected_last_updated);
if ( expected_token ) {
assert.equal(parsedBody.layergroupid, expected_token + ':' + expected_last_updated_epoch);
@@ -116,19 +99,23 @@ suite(suiteName, function() {
if (!cdbQueryTablesFromPostgresEnabledValue) { // only test if it was using the SQL API
var jsonquery = cc.substring(dbname.length + 1);
var sentquery = JSON.parse(jsonquery);
var expectedQuery = [layergroup.layers[0].options.sql, ';', layergroup.layers[1].options.sql].join('');
assert.equal(sentquery.q, 'WITH querytables AS ( SELECT * FROM CDB_QueryTables($windshaft$'
+ expectedQuery
+ '$windshaft$) as tablenames )'
+ ' SELECT (SELECT tablenames FROM querytables), EXTRACT(EPOCH FROM max(updated_at)) as max'
+ ' FROM CDB_TableMetadata m'
+ ' WHERE m.tabname = any ((SELECT tablenames from querytables)::regclass[])');
var expectedQuery = [
layergroup.layers[0].options.sql, ';',
layergroup.layers[1].options.sql
].join('');
assert.equal(sentquery.q, 'WITH querytables AS ( SELECT * FROM CDB_QueryTables($windshaft$' +
expectedQuery +
'$windshaft$) as tablenames )' +
' SELECT (SELECT tablenames FROM querytables), EXTRACT(EPOCH FROM max(updated_at)) as max' +
' FROM CDB_TableMetadata m' +
' WHERE m.tabname = any ((SELECT tablenames from querytables)::regclass[])');
}
assert.imageEqualsFile(res.body, 'test/fixtures/test_table_0_0_0_multilayer1.png', IMAGE_EQUALS_HIGHER_TOLERANCE_PER_MIL,
function(err, similarity) {
next(err);
});
assert.imageEqualsFile(res.body, 'test/fixtures/test_table_0_0_0_multilayer1.png',
IMAGE_EQUALS_HIGHER_TOLERANCE_PER_MIL, function(err/*, similarity*/) {
next(err);
}
);
});
},
// See https://github.com/CartoDB/Windshaft-cartodb/issues/170
@@ -154,15 +141,14 @@ suite(suiteName, function() {
if ( err ) throw err;
var next = this;
assert.response(server, {
url: layergroup_url + "/" + expected_token
+ '/0/0/0/0.grid.json',
url: layergroup_url + "/" + expected_token + '/0/0/0/0.grid.json',
headers: {host: 'localhost' },
method: 'GET'
}, {}, function(res) {
assert.equal(res.statusCode, 200, res.body);
assert.equal(res.headers['content-type'], "application/json; charset=utf-8");
assert.utfgridEqualsFile(res.body, 'test/fixtures/test_table_0_0_0_multilayer1.layer0.grid.json', 2,
function(err, similarity) {
function(err/*, similarity*/) {
next(err);
});
});
@@ -172,15 +158,14 @@ suite(suiteName, function() {
if ( err ) throw err;
var next = this;
assert.response(server, {
url: layergroup_url + "/" + expected_token
+ '/1/0/0/0.grid.json',
url: layergroup_url + "/" + expected_token + '/1/0/0/0.grid.json',
headers: {host: 'localhost' },
method: 'GET'
}, {}, function(res) {
assert.equal(res.statusCode, 200, res.body);
assert.equal(res.headers['content-type'], "application/json; charset=utf-8");
assert.utfgridEqualsFile(res.body, 'test/fixtures/test_table_0_0_0_multilayer1.layer1.grid.json', 2,
function(err, similarity) {
function(err/*, similarity*/) {
next(err);
});
});
@@ -206,20 +191,20 @@ suite(suiteName, function() {
test("should include serverMedata in the response", function(done) {
global.environment.serverMetadata = { cdn_url : { http:'test', https: 'tests' } }
global.environment.serverMetadata = { cdn_url : { http:'test', https: 'tests' } };
var layergroup = {
version: '1.0.0',
layers: [
{ options: {
sql: 'select cartodb_id, ST_Translate(the_geom_webmercator, 5e6, 0) as the_geom_webmercator from test_table limit 2',
sql: 'select cartodb_id, ST_Translate(the_geom_webmercator, 5e6, 0) as the_geom_webmercator' +
' from test_table limit 2',
cartocss: '#layer { marker-fill:red; marker-width:32; marker-allow-overlap:true; }',
cartocss_version: '2.0.1'
} }
]
};
var expected_token;
Step(
step(
function do_create_get()
{
var next = this;
@@ -234,7 +219,7 @@ suite(suiteName, function() {
assert.ok(_.isEqual(parsed.cdn_url, global.environment.serverMetadata.cdn_url));
done();
}
)
);
});
@@ -244,7 +229,8 @@ suite(suiteName, function() {
version: '1.0.0',
layers: [
{ options: {
sql: 'select cartodb_id, ST_Translate(the_geom_webmercator, 5e6, 0) as the_geom_webmercator from test_table limit 2',
sql: 'select cartodb_id, ST_Translate(the_geom_webmercator, 5e6, 0) as the_geom_webmercator' +
' from test_table limit 2',
cartocss: '#layer { marker-fill:red; marker-width:32; marker-allow-overlap:true; }',
cartocss_version: '2.0.1'
} }
@@ -252,7 +238,7 @@ suite(suiteName, function() {
};
var expected_token;
Step(
step(
function do_create_get()
{
var next = this;
@@ -316,7 +302,8 @@ suite(suiteName, function() {
version: '1.0.0',
layers: [
{ options: {
sql: 'select cartodb_id, ST_Translate(the_geom_webmercator, 5e6, 0) as the_geom_webmercator from test_table limit 2',
sql: 'select cartodb_id, ST_Translate(the_geom_webmercator, 5e6, 0) as the_geom_webmercator' +
' from test_table limit 2',
cartocss: '#layer { invalid-rule:red; }',
cartocss_version: '2.0.1'
} }
@@ -339,8 +326,8 @@ suite(suiteName, function() {
version: '1.0.0',
layers: [
{ options: {
sql: 'select 1 as cartodb_id, '
+ 'ST_Buffer(!bbox!, -32*greatest(!pixel_width!,!pixel_height!)) as the_geom_webmercator from test_table limit 1',
sql: 'select 1 as cartodb_id, ST_Buffer(!bbox!, -32*greatest(!pixel_width!,!pixel_height!))' +
' as the_geom_webmercator from test_table limit 1',
cartocss: '#layer { polygon-fill:red; }',
cartocss_version: '2.0.1',
interactivity: 'cartodb_id'
@@ -349,7 +336,7 @@ suite(suiteName, function() {
};
var expected_token; // = "6d8e4ad5458e2d25cf0eef38e38717a6";
Step(
step(
function do_post()
{
var next = this;
@@ -361,13 +348,6 @@ suite(suiteName, function() {
}, {}, function(res) {
assert.equal(res.statusCode, 200, res.body);
var parsedBody = JSON.parse(res.body);
var expectedBody = { layergroupid: expected_token };
// check last modified
var qTables = JSON.stringify({
'q': 'SELECT CDB_QueryTables($windshaft$'
+ layergroup.layers[0].options.sql
+ '$windshaft$)'
});
assert.equal(parsedBody.last_updated, expected_last_updated);
if ( expected_token ) {
assert.equal(parsedBody.layergroupid, expected_token + ':' + expected_last_updated_epoch);
@@ -401,16 +381,16 @@ suite(suiteName, function() {
.replace(/!bbox!/g, 'ST_MakeEnvelope(0,0,0,0)')
.replace(/!pixel_width!/g, '1')
.replace(/!pixel_height!/g, '1');
assert.equal(sentquery.q, 'WITH querytables AS ( SELECT * FROM CDB_QueryTables($windshaft$'
+ expectedQuery
+ '$windshaft$) as tablenames )'
+ ' SELECT (SELECT tablenames FROM querytables), EXTRACT(EPOCH FROM max(updated_at)) as max'
+ ' FROM CDB_TableMetadata m'
+ ' WHERE m.tabname = any ((SELECT tablenames from querytables)::regclass[])');
assert.equal(sentquery.q, 'WITH querytables AS ( SELECT * FROM CDB_QueryTables($windshaft$' +
expectedQuery +
'$windshaft$) as tablenames )' +
' SELECT (SELECT tablenames FROM querytables), EXTRACT(EPOCH FROM max(updated_at)) as max' +
' FROM CDB_TableMetadata m' +
' WHERE m.tabname = any ((SELECT tablenames from querytables)::regclass[])');
}
assert.imageEqualsFile(res.body, 'test/fixtures/test_multilayer_bbox.png', IMAGE_EQUALS_TOLERANCE_PER_MIL,
function(err, similarity) {
function(err/*, similarity*/) {
next(err);
});
});
@@ -440,16 +420,16 @@ suite(suiteName, function() {
.replace('!bbox!', 'ST_MakeEnvelope(0,0,0,0)')
.replace('!pixel_width!', '1')
.replace('!pixel_height!', '1');
assert.equal(sentquery.q, 'WITH querytables AS ( SELECT * FROM CDB_QueryTables($windshaft$'
+ expectedQuery
+ '$windshaft$) as tablenames )'
+ ' SELECT (SELECT tablenames FROM querytables), EXTRACT(EPOCH FROM max(updated_at)) as max'
+ ' FROM CDB_TableMetadata m'
+ ' WHERE m.tabname = any ((SELECT tablenames from querytables)::regclass[])');
assert.equal(sentquery.q, 'WITH querytables AS ( SELECT * FROM CDB_QueryTables($windshaft$' +
expectedQuery +
'$windshaft$) as tablenames )' +
' SELECT (SELECT tablenames FROM querytables), EXTRACT(EPOCH FROM max(updated_at)) as max' +
' FROM CDB_TableMetadata m' +
' WHERE m.tabname = any ((SELECT tablenames from querytables)::regclass[])');
}
assert.imageEqualsFile(res.body, 'test/fixtures/test_multilayer_bbox.png', IMAGE_EQUALS_TOLERANCE_PER_MIL,
function(err, similarity) {
function(err/*, similarity*/) {
next(err);
});
});
@@ -459,15 +439,14 @@ suite(suiteName, function() {
if ( err ) throw err;
var next = this;
assert.response(server, {
url: layergroup_url + "/" + expected_token
+ '/0/1/0/0.grid.json',
url: layergroup_url + "/" + expected_token + '/0/1/0/0.grid.json',
headers: {host: 'localhost' },
method: 'GET'
}, {}, function(res) {
assert.equal(res.statusCode, 200, res.body);
assert.equal(res.headers['content-type'], "application/json; charset=utf-8");
assert.utfgridEqualsFile(res.body, 'test/fixtures/test_multilayer_bbox.grid.json', 2,
function(err, similarity) {
function(err/*, similarity*/) {
next(err);
});
});
@@ -477,15 +456,14 @@ suite(suiteName, function() {
if ( err ) throw err;
var next = this;
assert.response(server, {
url: layergroup_url + "/" + expected_token
+ '/0/4/0/0.grid.json',
url: layergroup_url + "/" + expected_token + '/0/4/0/0.grid.json',
headers: {host: 'localhost' },
method: 'GET'
}, {}, function(res) {
assert.equal(res.statusCode, 200, res.body);
assert.equal(res.headers['content-type'], "application/json; charset=utf-8");
assert.utfgridEqualsFile(res.body, 'test/fixtures/test_multilayer_bbox.grid.json', 2,
function(err, similarity) {
function(err/*, similarity*/) {
next(err);
});
});
@@ -515,8 +493,8 @@ suite(suiteName, function() {
version: '1.0.0',
layers: [
{ options: {
sql: 'select 1 as cartodb_id, !pixel_height! as h, '
+ 'ST_Buffer(!bbox!, -32*greatest(!pixel_width!,!pixel_height!)) as the_geom_webmercator',
sql: 'select 1 as cartodb_id, !pixel_height! as h,' +
' ST_Buffer(!bbox!, -32*greatest(!pixel_width!,!pixel_height!)) as the_geom_webmercator',
cartocss: '#layer { polygon-fill:red; }',
cartocss_version: '2.0.1'
} }
@@ -527,7 +505,7 @@ suite(suiteName, function() {
var expected_token; // will be set on first post and checked on second
var now = strftime("%Y%m%d", new Date());
var errors = [];
Step(
step(
function clean_stats()
{
var next = this;
@@ -553,14 +531,13 @@ suite(suiteName, function() {
},
function check_global_stats_1(err, val) {
if ( err ) throw err;
assert.equal(val, 1, "Expected score of " + now + " in "
+ statskey + ":global to be 1, got " + val);
assert.equal(val, 1, "Expected score of " + now + " in " + statskey + ":global to be 1, got " + val);
redis_stats_client.zscore(statskey+':stat_tag:random_tag', now, this);
},
function check_tag_stats_1_do_post_2(err, val) {
if ( err ) throw err;
assert.equal(val, 1, "Expected score of " + now + " in "
+ statskey + ":stat_tag:" + layergroup.stat_tag + " to be 1, got " + val);
assert.equal(val, 1, "Expected score of " + now + " in " + statskey + ":stat_tag:" + layergroup.stat_tag +
" to be 1, got " + val);
var next = this;
assert.response(server, {
url: layergroup_url,
@@ -576,15 +553,14 @@ suite(suiteName, function() {
function check_global_stats_2(err, val)
{
if ( err ) throw err;
assert.equal(val, 2, "Expected score of " + now + " in "
+ statskey + ":global to be 2, got " + val);
assert.equal(val, 2, "Expected score of " + now + " in " + statskey + ":global to be 2, got " + val);
redis_stats_client.zscore(statskey+':stat_tag:' + layergroup.stat_tag, now, this);
},
function check_tag_stats_2(err, val)
{
if ( err ) throw err;
assert.equal(val, 2, "Expected score of " + now + " in "
+ statskey + ":stat_tag:" + layergroup.stat_tag + " to be 2, got " + val);
assert.equal(val, 2, "Expected score of " + now + " in " + statskey + ":stat_tag:" + layergroup.stat_tag +
" to be 2, got " + val);
return 1;
},
function cleanup_map_style(err) {
@@ -614,8 +590,8 @@ suite(suiteName, function() {
version: '1.0.0',
layers: [
{ options: {
sql: 'select 1 as cartodb_id, !pixel_height! as h'
+ 'ST_Buffer(!bbox!, -32*greatest(!pixel_width!,!pixel_height!)) as the_geom_webmercator',
sql: 'select 1 as cartodb_id, !pixel_height! as h' +
'ST_Buffer(!bbox!, -32*greatest(!pixel_width!,!pixel_height!)) as the_geom_webmercator',
cartocss: '#layer { polygon-fit:red; }',
cartocss_version: '2.0.1'
} }
@@ -685,7 +661,7 @@ suite(suiteName, function() {
};
var expected_token; // = "b4ed64d93a411a59f330ab3d798e4009";
Step(
step(
function do_post()
{
var next = this;
@@ -697,14 +673,6 @@ suite(suiteName, function() {
}, {}, function(res) {
assert.equal(res.statusCode, 200, res.body);
var parsedBody = JSON.parse(res.body);
var expectedBody = { layergroupid: expected_token };
// check last modified
var qTables = JSON.stringify({
'q': 'SELECT CDB_QueryTables($windshaft$'
+ layergroup.layers[0].options.sql + ';'
+ layergroup.layers[1].options.sql
+ '$windshaft$)'
});
assert.equal(parsedBody.last_updated, expected_last_updated);
if ( expected_token ) {
assert.equal(parsedBody.layergroupid, expected_token + ':' + expected_last_updated_epoch);
@@ -739,8 +707,7 @@ suite(suiteName, function() {
if ( err ) throw err;
var next = this;
assert.response(server, {
url: layergroup_url + "/" + expected_token
+ '/0/0/0/0.grid.json?map_key=1234',
url: layergroup_url + "/" + expected_token + '/0/0/0/0.grid.json?map_key=1234',
headers: {host: 'localhost' },
method: 'GET'
}, {}, function(res) {
@@ -753,8 +720,7 @@ suite(suiteName, function() {
if ( err ) throw err;
var next = this;
assert.response(server, {
url: layergroup_url + "/" + expected_token
+ '/1/0/0/0.grid.json?map_key=1234',
url: layergroup_url + "/" + expected_token + '/1/0/0/0.grid.json?map_key=1234',
headers: {host: 'localhost' },
method: 'GET'
}, {}, function(res) {
@@ -774,7 +740,7 @@ suite(suiteName, function() {
encoding: 'binary'
}, {}, function(res) {
assert.equal(res.statusCode, 403);
var re = RegExp('permission denied');
var re = new RegExp('permission denied');
assert.ok(res.body.match(re), 'No "permission denied" error: ' + res.body);
next(err);
});
@@ -784,13 +750,12 @@ suite(suiteName, function() {
if ( err ) throw err;
var next = this;
assert.response(server, {
url: layergroup_url + "/" + expected_token
+ '/0/0/0/0.grid.json',
url: layergroup_url + "/" + expected_token + '/0/0/0/0.grid.json',
headers: {host: 'localhost' },
method: 'GET'
}, {}, function(res) {
assert.equal(res.statusCode, 403);
var re = RegExp('permission denied');
var re = new RegExp('permission denied');
assert.ok(res.body.match(re), 'No "permission denied" error: ' + res.body);
next(err);
});
@@ -800,13 +765,12 @@ suite(suiteName, function() {
if ( err ) throw err;
var next = this;
assert.response(server, {
url: layergroup_url + "/" + expected_token
+ '/1/0/0/0.grid.json',
url: layergroup_url + "/" + expected_token + '/1/0/0/0.grid.json',
headers: {host: 'localhost' },
method: 'GET'
}, {}, function(res) {
assert.equal(res.statusCode, 403);
var re = RegExp('permission denied');
var re = new RegExp('permission denied');
assert.ok(res.body.match(re), 'No "permission denied" error: ' + res.body);
next(err);
});
@@ -846,7 +810,7 @@ suite(suiteName, function() {
};
var expected_token; // = "b4ed64d93a411a59f330ab3d798e4009";
Step(
step(
function do_post()
{
var next = this;
@@ -861,13 +825,6 @@ suite(suiteName, function() {
if ( err ) throw err;
assert.equal(res.statusCode, 200, res.body);
var parsedBody = JSON.parse(res.body);
var expectedBody = { layergroupid: expected_token };
// check last modified
var qTables = JSON.stringify({
'q': 'SELECT CDB_QueryTables($windshaft$'
+ layergroup.layers[0].options.sql
+ '$windshaft$)'
});
assert.equal(parsedBody.last_updated, expected_last_updated);
if ( expected_token ) {
assert.equal(parsedBody.layergroupid, expected_token + ':' + expected_last_updated_epoch);
@@ -898,11 +855,10 @@ suite(suiteName, function() {
assert.equal(cc.substring(0, dbname.length), dbname);
return null;
},
function do_restart_server(err, res) {
function do_restart_server(err/*, res*/) {
if ( err ) throw err;
// hack simulating restart...
serverOptions = ServerOptions();
server = new CartodbWindshaft(serverOptions);
server = new CartodbWindshaft(serverOptions());
return null;
},
function do_get1(err)
@@ -956,13 +912,13 @@ suite(suiteName, function() {
{ options: {
sql: "select 1 as cartodb_id, 'SRID=3857;POINT(0 0)'::geometry as the_geom_webmercator",
cartocss: '#sample { text-name: cartodb_id; text-face-name: "Dejagnu"; }',
cartocss_version: '2.1.0',
cartocss_version: '2.1.0'
} }
]
};
assert.response(server, {
url: '/tiles/layergroup?',
url: layergroup_url,
method: 'POST',
headers: {host: 'localhost', 'Content-Type': 'application/json' },
data: JSON.stringify(layergroup)
@@ -984,18 +940,18 @@ suite(suiteName, function() {
{ options: {
sql: "select 'single''quote' as n, 'SRID=3857;POINT(0 0)'::geometry as the_geom_webmercator",
cartocss: '#s [n="single\'quote" ] { marker-fill:red; }',
cartocss_version: '2.1.0',
cartocss_version: '2.1.0'
} },
{ options: {
sql: "select 'double\"quote' as n, 'SRID=3857;POINT(2 0)'::geometry as the_geom_webmercator",
cartocss: '#s [n="double\\"quote" ] { marker-fill:red; }',
cartocss_version: '2.1.0',
cartocss_version: '2.1.0'
} }
]
};
assert.response(server, {
url: '/tiles/layergroup?',
url: layergroup_url,
method: 'POST',
headers: {host: 'localhost', 'Content-Type': 'application/json' },
data: JSON.stringify(layergroup)
@@ -1013,12 +969,12 @@ suite(suiteName, function() {
{ options: {
sql: "select .4 as n, 'SRID=3857;POINT(0 0)'::geometry as the_geom_webmercator",
cartocss: '#s [n<=.2e-2] { marker-fill:red; }',
cartocss_version: '2.1.0',
cartocss_version: '2.1.0'
} }
]
};
assert.response(server, {
url: '/tiles/layergroup?',
url: layergroup_url,
method: 'POST',
headers: {host: 'localhost', 'Content-Type': 'application/json' },
data: JSON.stringify(layergroup)
@@ -1041,7 +997,7 @@ suite(suiteName, function() {
]
};
var expected_token; // = "e34dd7e235138a062f8ba7ad051aa3a7";
Step(
step(
function do_post()
{
var next = this;
@@ -1053,7 +1009,6 @@ suite(suiteName, function() {
}, {}, function(res) {
assert.equal(res.statusCode, 200, res.body);
var parsedBody = JSON.parse(res.body);
var expectedBody = { layergroupid: expected_token };
if ( expected_token ) {
assert.equal(parsedBody.layergroupid, expected_token + ':' + expected_last_updated_epoch);
}
@@ -1077,10 +1032,11 @@ suite(suiteName, function() {
}, {}, function(res) {
assert.equal(res.statusCode, 200, res.body);
assert.equal(res.headers['content-type'], "image/png");
assert.imageEqualsFile(res.body, windshaft_fixtures + '/test_default_mapnik_point.png', IMAGE_EQUALS_TOLERANCE_PER_MIL,
function(err, similarity) {
next(err);
});
assert.imageEqualsFile(res.body, windshaft_fixtures + '/test_default_mapnik_point.png',
IMAGE_EQUALS_TOLERANCE_PER_MIL, function(err/*, similarity*/) {
next(err);
}
);
});
},
function finish(err) {
@@ -1117,7 +1073,7 @@ suite(suiteName, function() {
]
};
var expected_token; // = "e34dd7e235138a062f8ba7ad051aa3a7";
Step(
step(
function do_post()
{
var next = this;
@@ -1156,7 +1112,6 @@ suite(suiteName, function() {
},
function check_get_tile(err, res) {
if ( err ) throw err;
var next = this;
assert.equal(res.statusCode, 200, res.body);
return null;
},
@@ -1188,10 +1143,10 @@ suite(suiteName, function() {
// See https://github.com/CartoDB/Windshaft-cartodb/issues/111
test("sql string can be very long", function(done){
var long_val = 'pretty';
for (var i=0; i<1024; ++i) long_val += ' long'
for (var i=0; i<1024; ++i) long_val += ' long';
long_val += ' string';
var sql = "SELECT ";
for (var i=0; i<16; ++i)
for (i=0; i<16; ++i)
sql += "'" + long_val + "'::text as pretty_long_field_name_" + i + ", ";
sql += "cartodb_id, the_geom_webmercator FROM gadm4 g";
var layergroup = {
@@ -1206,7 +1161,7 @@ suite(suiteName, function() {
};
var errors = [];
var expected_token;
Step(
step(
function do_post()
{
var data = JSON.stringify(layergroup);
@@ -1225,10 +1180,6 @@ suite(suiteName, function() {
var parsedBody = JSON.parse(res.body);
var token_components = parsedBody.layergroupid.split(':');
expected_token = token_components[0];
if (!cdbQueryTablesFromPostgresEnabledValue) { // only test if it was using the SQL API
var last_request = sqlapi_server.getLastRequest();
assert.equal(last_request.method, 'POST');
}
return null;
},
function cleanup(err) {
@@ -1259,13 +1210,14 @@ suite(suiteName, function() {
version: '1.0.0',
layers: [
{ options: {
sql: 'select cartodb_id, ST_Translate(the_geom_webmercator, 5e6, 0) as the_geom_webmercator from test_table limit 2',
sql: 'select cartodb_id, ST_Translate(the_geom_webmercator, 5e6, 0) as the_geom_webmercator' +
' from test_table limit 2',
interactivity: 'cartodb_id'
} }
]
};
Step(
step(
function do_post()
{
var next = this;
@@ -1307,7 +1259,7 @@ suite(suiteName, function() {
]
};
Step(
step(
function do_post()
{
var next = this;
@@ -1415,7 +1367,7 @@ suite(suiteName, function() {
{
status: 403
},
function(res, err) {
function(res) {
assert.ok(res.body.match(/permission denied for relation test_table_private_1/));
done();
}
@@ -1428,11 +1380,11 @@ suite(suiteName, function() {
// This test will add map_style records, like
// 'map_style|null|publicuser|my_table',
redis_client.keys("map_style|*", function(err, matches) {
redis_client.del(matches, function(err) {
redis_client.select(5, function(err, matches) {
redis_client.del(matches, function() {
redis_client.select(5, function() {
redis_client.keys("user:localhost:mapviews*", function(err, matches) {
redis_client.del(matches, function(err) {
sqlapi_server.close(done);
redis_client.del(matches, function() {
done();
});
});
});
@@ -1444,4 +1396,3 @@ suite(suiteName, function() {
});
});
});
+338
View File
@@ -0,0 +1,338 @@
var testHelper = require('../support/test_helper');
var assert = require('../support/assert');
var redis = require('redis');
var _ = require('underscore');
var QueryTablesApi = require('../../lib/cartodb/api/query_tables_api');
var CartodbWindshaft = require('../../lib/cartodb/cartodb_windshaft');
var serverOptions = require('../../lib/cartodb/server_options')();
var server = new CartodbWindshaft(serverOptions);
server.setMaxListeners(0);
describe('tests from old api translated to multilayer', function() {
var layergroupUrl = '/api/v1/map';
var redisClient = redis.createClient(global.environment.redis.port);
after(function(done) {
// This test will add map_style records, like
// 'map_style|null|publicuser|my_table',
redisClient.keys("map_style|*", function(err, matches) {
redisClient.del(matches, function() {
done();
});
});
});
var wadusSql = 'select 1 as cartodb_id, null::geometry as the_geom_webmercator';
var pointSql = "SELECT 'SRID=3857;POINT(0 0)'::geometry as the_geom_webmercator, 1::int as cartodb_id";
function singleLayergroupConfig(sql, cartocss) {
return {
version: '1.0.0',
layers: [
{
type: 'mapnik',
options: {
sql: sql,
cartocss: cartocss,
cartocss_version: '2.0.1'
}
}
]
};
}
function createRequest(layergroup, userHost, apiKey) {
var url = layergroupUrl;
if (apiKey) {
url += '?api_key=' + apiKey;
}
return {
url: url,
method: 'POST',
headers: {
host: userHost || 'localhost',
'Content-Type': 'application/json'
},
data: JSON.stringify(layergroup)
};
}
it("layergroup creation fails if CartoCSS is bogus", function(done) {
var layergroup = singleLayergroupConfig(wadusSql, '#my_table3{');
assert.response(server,
createRequest(layergroup),
{
status: 400
},
function(res) {
var parsed = JSON.parse(res.body);
assert.ok(parsed.errors[0].match(/^style0/));
assert.ok(parsed.errors[0].match(/missing closing/));
done();
}
);
});
it("multiple bad styles returns 400 with all errors", function(done) {
var layergroup = singleLayergroupConfig(wadusSql, '#my_table4{backgxxxxxround-color:#fff;foo:bar}');
assert.response(server,
createRequest(layergroup),
{
status: 400
},
function(res) {
var parsed = JSON.parse(res.body);
assert.equal(parsed.errors.length, 1);
assert.ok(parsed.errors[0].match(/^style0/));
assert.ok(parsed.errors[0].match(/Unrecognized rule: backgxxxxxround-color/));
assert.ok(parsed.errors[0].match(/Unrecognized rule: foo/));
done();
}
);
});
// Zoom is a special variable
it("Specifying zoom level in CartoCSS does not need a 'zoom' variable in SQL output", function(done) {
var layergroup = singleLayergroupConfig(pointSql, '#gadm4 [ zoom>=3] { marker-fill:red; }');
assert.response(server,
createRequest(layergroup),
{
status: 200
},
function(res) {
var parsed = JSON.parse(res.body);
assert.ok(parsed.layergroupid);
done();
}
);
});
// See https://github.com/CartoDB/Windshaft-cartodb/issues/88
it("getting a tile from a user-specific database should return an expected tile", function(done) {
var layergroup = singleLayergroupConfig(pointSql, '#layer { marker-fill:red; }');
var backupDBHost = global.environment.postgres.host;
global.environment.postgres.host = '6.6.6.6';
assert.response(server,
createRequest(layergroup, 'cartodb250user'),
{
status: 200
},
function(res) {
var parsed = JSON.parse(res.body);
assert.ok(parsed.layergroupid);
global.environment.postgres.host = backupDBHost;
done();
}
);
});
// See https://github.com/CartoDB/Windshaft-cartodb/issues/89
it("getting a tile with a user-specific database password", function(done) {
var layergroup = singleLayergroupConfig(pointSql, '#layer { marker-fill:red; }');
var backupDBPass = global.environment.postgres_auth_pass;
global.environment.postgres_auth_pass = '<%= user_password %>';
assert.response(server,
createRequest(layergroup, 'cartodb250user', '4321'),
{
status: 200
},
function(res) {
var parsed = JSON.parse(res.body);
assert.ok(parsed.layergroupid);
global.environment.postgres_auth_pass = backupDBPass;
done();
}
);
});
it("creating a layergroup from lzma param", function(done){
var params = {
config: JSON.stringify(singleLayergroupConfig(pointSql, '#layer { marker-fill:red; }'))
};
testHelper.lzma_compress_to_base64(JSON.stringify(params), 1, function(err, lzma) {
if (err) {
return done(err);
}
assert.response(server,
{
url: layergroupUrl + '?lzma=' + encodeURIComponent(lzma),
method: 'GET',
headers: {
host: 'localhost'
},
encoding: 'binary'
},
{
status: 200
},
function(res) {
var parsed = JSON.parse(res.body);
assert.ok(parsed.layergroupid);
done();
}
);
});
});
it("creating a layergroup from lzma param, invalid json input", function(done) {
var params = {
config: 'WADUS'
};
testHelper.lzma_compress_to_base64(JSON.stringify(params), 1, function(err, lzma) {
if (err) {
return done(err);
}
assert.response(server,
{
url: layergroupUrl + '?lzma=' + encodeURIComponent(lzma),
method: 'GET',
headers: {
host: 'localhost'
},
encoding: 'binary'
},
{
status: 400
},
function(res) {
var parsed = JSON.parse(res.body);
assert.deepEqual(parsed, { errors: [ 'Unexpected token W' ] });
done();
}
);
});
});
it("uses queries postgresql to figure affected tables in query", function(done) {
var tableName = 'gadm4';
var expectedCacheChannel = _.template('<%= databaseName %>:public.<%= tableName %>', {
databaseName: _.template(global.environment.postgres_auth_user, {user_id:1}) + '_db',
tableName: tableName
});
var layergroup = singleLayergroupConfig('select * from ' + tableName, '#gadm4 { marker-fill: red; }');
assert.response(server,
{
url: layergroupUrl + '?config=' + encodeURIComponent(JSON.stringify(layergroup)),
method: 'GET',
headers: {
host: 'localhost'
}
},
{
status: 200
},
function(res) {
var parsed = JSON.parse(res.body);
assert.ok(parsed.layergroupid);
assert.ok(res.headers.hasOwnProperty('x-cache-channel'));
assert.equal(res.headers['x-cache-channel'], expectedCacheChannel);
done();
}
);
});
it("creates layergroup fails when postgresql queries fail to figure affected tables in query", function(done) {
var runQueryFn = QueryTablesApi.prototype.runQuery;
QueryTablesApi.prototype.runQuery = function(username, query, queryHandler, callback) {
return queryHandler(new Error('fake error message'), [], callback);
};
var layergroup = singleLayergroupConfig('select * from gadm4', '#gadm4 { marker-fill: red; }');
assert.response(server,
{
url: layergroupUrl + '?config=' + encodeURIComponent(JSON.stringify(layergroup)),
method: 'GET',
headers: {
host: 'localhost'
}
},
{
status: 400
},
function(res) {
QueryTablesApi.prototype.runQuery = runQueryFn;
assert.ok(!res.headers.hasOwnProperty('x-cache-channel'));
var parsed = JSON.parse(res.body);
assert.deepEqual(parsed, {
errors: ["Error: could not fetch affected tables and last updated time: fake error message"]
});
done();
}
);
});
it("tile requests works when postgresql queries fail to figure affected tables in query", function(done) {
var layergroup = singleLayergroupConfig('select * from gadm4', '#gadm4 { marker-fill: red; }');
assert.response(server,
{
url: layergroupUrl + '?config=' + encodeURIComponent(JSON.stringify(layergroup)),
method: 'GET',
headers: {
host: 'localhost'
}
},
{
status: 200
},
function(res) {
var runQueryFn = QueryTablesApi.prototype.runQuery;
QueryTablesApi.prototype.runQuery = function(username, query, queryHandler, callback) {
return queryHandler(new Error('failed to query database for affected tables'), [], callback);
};
// reset internal cacheChannel cache
serverOptions.channelCache = {};
assert.response(server,
{
url: layergroupUrl + _.template('/<%= layergroupId %>/<%= z %>/<%= x %>/<%= y %>.png', {
layergroupId: JSON.parse(res.body).layergroupid,
z: 0,
x: 0,
y: 0
}),
method: 'GET',
headers: {
host: 'localhost'
},
encoding: 'binary'
},
{
status: 200
},
function(res) {
assert.ok(!res.headers.hasOwnProperty('x-cache-channel'));
QueryTablesApi.prototype.runQuery = runQueryFn;
done();
}
);
}
);
});
});
+9 -9
View File
@@ -130,7 +130,7 @@ suite('named_layers', function() {
var next = this;
assert.response(server,
{
url: '/tiles/layergroup',
url: '/api/v1/map',
method: 'POST',
headers: {
host: 'localhost',
@@ -183,7 +183,7 @@ suite('named_layers', function() {
var next = this;
assert.response(server,
{
url: '/tiles/layergroup',
url: '/api/v1/map',
method: 'POST',
headers: {
host: 'localhost',
@@ -240,7 +240,7 @@ suite('named_layers', function() {
var next = this;
assert.response(server,
{
url: '/tiles/layergroup',
url: '/api/v1/map',
method: 'POST',
headers: {
host: 'localhost',
@@ -293,7 +293,7 @@ suite('named_layers', function() {
var next = this;
assert.response(server,
{
url: '/tiles/layergroup',
url: '/api/v1/map',
method: 'POST',
headers: {
host: 'localhost',
@@ -373,7 +373,7 @@ suite('named_layers', function() {
var next = this;
assert.response(server,
{
url: '/tiles/layergroup',
url: '/api/v1/map',
method: 'POST',
headers: {
host: 'localhost',
@@ -408,7 +408,7 @@ suite('named_layers', function() {
var next = this;
assert.response(server,
{
url: '/tiles/layergroup/' + layergroupId + '/0/0/0.png',
url: '/api/v1/map/' + layergroupId + '/0/0/0.png',
method: 'GET',
headers: {
host: 'localhost'
@@ -501,7 +501,7 @@ suite('named_layers', function() {
var next = this;
assert.response(server,
{
url: '/tiles/layergroup',
url: '/api/v1/map',
method: 'POST',
headers: {
host: 'localhost',
@@ -536,7 +536,7 @@ suite('named_layers', function() {
var next = this;
assert.response(server,
{
url: '/tiles/layergroup/' + layergroupId + '/0/0/0.png',
url: '/api/v1/map/' + layergroupId + '/0/0/0.png',
method: 'GET',
headers: {
host: 'localhost'
@@ -602,7 +602,7 @@ suite('named_layers', function() {
var next = this;
assert.response(server,
{
url: '/tiles/layergroup',
url: '/api/v1/map',
method: 'POST',
headers: {
host: 'localhost',
+9 -1331
View File
File diff suppressed because it is too large Load Diff
+70 -82
View File
@@ -6,7 +6,6 @@ var semver = require('semver');
var Step = require('step');
var strftime = require('strftime');
var NamedMapsCacheEntry = require(__dirname + '/../../lib/cartodb/cache/model/named_maps_entry');
var SQLAPIEmu = require(__dirname + '/../support/SQLAPIEmu.js');
var redis_stats_db = 5;
// Pollute the PG environment to make sure
@@ -25,22 +24,13 @@ var serverOptions = ServerOptions();
var server = new CartodbWindshaft(serverOptions);
server.setMaxListeners(0);
[true, false].forEach(function(cdbQueryTablesFromPostgresEnabledValue) {
suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, function() {
suite('template_api', function() {
serverOptions.channelCache = {};
var redis_client = redis.createClient(global.environment.redis.port);
var sqlapi_server;
var expected_last_updated_epoch = 1234567890123; // this is hard-coded into SQLAPIEmu
var expected_last_updated = new Date(expected_last_updated_epoch).toISOString();
suiteSetup(function(done){
global.environment.enabledFeatures = { cdbQueryTablesFromPostgres: cdbQueryTablesFromPostgresEnabledValue };
sqlapi_server = new SQLAPIEmu(global.environment.sqlapi.port, done);
// TODO: check redis is clean ?
});
var template_acceptance1 = {
version: '0.0.1',
name: 'acceptance1',
@@ -87,7 +77,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio
var expected_failure = false;
var expected_tpl_id = "localhost@acceptance1";
var post_request_1 = {
url: '/tiles/template',
url: '/api/v1/map/named',
method: 'POST',
headers: {host: 'localhost', 'Content-Type': 'application/json' },
data: JSON.stringify(template_acceptance1)
@@ -174,7 +164,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio
broken_template.auth.method = 'token';
delete broken_template.auth.tokens;
var post_request_1 = {
url: '/tiles/template?api_key=1234',
url: '/api/v1/map/named?api_key=1234',
method: 'POST',
headers: {host: 'localhost', 'Content-Type': 'application/json' },
data: JSON.stringify(broken_template)
@@ -204,7 +194,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio
broken_template.auth.method = 'token';
broken_template.auth.tokens = [];
var post_request_1 = {
url: '/tiles/template?api_key=1234',
url: '/api/v1/map/named?api_key=1234',
method: 'POST',
headers: {host: 'localhost', 'Content-Type': 'application/json' },
data: JSON.stringify(broken_template)
@@ -231,7 +221,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio
var broken_template = JSON.parse(JSON.stringify(template_acceptance1));
broken_template.name = 'broken1';
var post_request_1 = {
url: '/tiles/template?api_key=1234',
url: '/api/v1/map/named?api_key=1234',
method: 'POST',
headers: {host: 'localhost', 'Content-Type': 'application/json' },
data: JSON.stringify(broken_template)
@@ -255,7 +245,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio
broken_template.auth.method = 'token';
broken_template.auth.tokens = [];
var put_request_1 = {
url: '/tiles/template/' + tpl_id + '/?api_key=1234',
url: '/api/v1/map/named/' + tpl_id + '/?api_key=1234',
method: 'PUT',
headers: {host: 'localhost', 'Content-Type': 'application/json' },
data: JSON.stringify(broken_template)
@@ -276,7 +266,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio
'Error for invalid authentication on PUT does not match ' +
re + ': ' + parsed.error);
var del_request = {
url: '/tiles/template/' + tpl_id + '?api_key=1234',
url: '/api/v1/map/named/' + tpl_id + '?api_key=1234',
method: 'DELETE',
headers: {host: 'localhost'}
}
@@ -312,7 +302,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio
Step(function postTemplate1(err, res) {
var next = this;
var post_request = {
url: '/tiles/template?api_key=1234',
url: '/api/v1/map/named?api_key=1234',
method: 'POST',
headers: {host: 'localhost.localhost', 'Content-Type': 'application/json' },
data: JSON.stringify(template_acceptance1)
@@ -321,7 +311,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio
},
function testCORS() {
assert.response(server, {
url: '/tiles/template/acceptance1',
url: '/api/v1/map/named/acceptance1',
method: 'OPTIONS'
},{
status: 200,
@@ -341,7 +331,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio
Step(function postTemplate1(err, res) {
var next = this;
var post_request = {
url: '/tiles/template?api_key=1234',
url: '/api/v1/map/named?api_key=1234',
method: 'POST',
headers: {host: 'localhost', 'Content-Type': 'application/json' },
data: JSON.stringify(tmpl)
@@ -353,7 +343,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio
function testCORS() {
var next = this;
assert.response(server, {
url: '/tiles/template/' + tmpl.name,
url: '/api/v1/map/named/' + tmpl.name,
method: 'POST',
headers: {host: 'localhost', 'Content-Type': 'application/json' },
},{
@@ -367,7 +357,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio
function deleteTemplate(err) {
if ( err ) throw err;
var del_request = {
url: '/tiles/template/' + tmpl.name + '?api_key=1234',
url: '/api/v1/map/named/' + tmpl.name + '?api_key=1234',
method: 'DELETE',
headers: {host: 'localhost', 'Content-Type': 'application/json' }
}
@@ -390,7 +380,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio
{
var next = this;
var post_request = {
url: '/tiles/template?api_key=1234',
url: '/api/v1/map/named?api_key=1234',
method: 'POST',
headers: {host: 'localhost', 'Content-Type': 'application/json' },
data: JSON.stringify(template_acceptance1)
@@ -411,7 +401,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio
var backup_name = template_acceptance1.name;
template_acceptance1.name += '_new';
var post_request = {
url: '/tiles/template?api_key=1234',
url: '/api/v1/map/named?api_key=1234',
method: 'POST',
headers: {host: 'localhost', 'Content-Type': 'application/json' },
data: JSON.stringify(template_acceptance1)
@@ -430,7 +420,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio
tplid2 = parsed.template_id;
var next = this;
var get_request = {
url: '/tiles/template',
url: '/api/v1/map/named',
method: 'GET',
headers: {host: 'localhost'}
}
@@ -448,7 +438,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio
assert.ok(err.match(/authenticated user/), err);
var next = this;
var get_request = {
url: '/tiles/template?api_key=1234',
url: '/api/v1/map/named?api_key=1234',
method: 'GET',
headers: {host: 'localhost'}
}
@@ -507,7 +497,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio
{
var next = this;
var post_request = {
url: '/tiles/template?api_key=1234',
url: '/api/v1/map/named?api_key=1234',
method: 'POST',
headers: {host: 'localhost', 'Content-Type': 'application/json' },
data: JSON.stringify(makeTemplate())
@@ -526,7 +516,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio
var backup_name = template_acceptance1.name;
template_acceptance1.name = 'changed_name';
var put_request = {
url: '/tiles/template/' + tpl_id + '/?api_key=1234',
url: '/api/v1/map/named/' + tpl_id + '/?api_key=1234',
method: 'PUT',
headers: {host: 'localhost', 'Content-Type': 'application/json' },
data: JSON.stringify(template_acceptance1)
@@ -545,7 +535,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio
assert.ok(parsedBody.error.match(/cannot update name/i),
'Unexpected error for invalid update: ' + parsedBody.error);
var put_request = {
url: '/tiles/template/unexistent/?api_key=1234',
url: '/api/v1/map/named/unexistent/?api_key=1234',
method: 'PUT',
headers: {host: 'localhost', 'Content-Type': 'application/json' },
data: JSON.stringify(makeTemplate())
@@ -563,7 +553,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio
assert.ok(parsedBody.error.match(/cannot update name/i),
'Unexpected error for invalid update: ' + parsedBody.error);
var put_request = {
url: '/tiles/template/' + tpl_id + '/?api_key=1234',
url: '/api/v1/map/named/' + tpl_id + '/?api_key=1234',
method: 'PUT',
headers: {host: 'localhost', 'Content-Type': 'application/json' },
data: JSON.stringify(makeTemplate())
@@ -619,7 +609,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio
{
var next = this;
var post_request = {
url: '/tiles/template?api_key=1234',
url: '/api/v1/map/named?api_key=1234',
method: 'POST',
headers: {host: 'localhost', 'Content-Type': 'application/json' },
data: JSON.stringify(makeTemplate())
@@ -636,7 +626,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio
"Missing 'template_id' from response body: " + res.body);
tpl_id = parsed.template_id;
var get_request = {
url: '/tiles/template/' + tpl_id,
url: '/api/v1/map/named/' + tpl_id,
method: 'GET',
headers: {host: 'localhost'}
}
@@ -653,7 +643,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio
assert.ok(parsedBody.error.match(/only.*authenticated.*user/i),
'Unexpected error for unauthenticated template get: ' + parsedBody.error);
var get_request = {
url: '/tiles/template/' + tpl_id + '?api_key=1234',
url: '/api/v1/map/named/' + tpl_id + '?api_key=1234',
method: 'GET',
headers: {host: 'localhost'}
}
@@ -708,7 +698,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio
{
var next = this;
var post_request = {
url: '/tiles/template?api_key=1234',
url: '/api/v1/map/named?api_key=1234',
method: 'POST',
headers: {host: 'localhost', 'Content-Type': 'application/json' },
data: JSON.stringify(makeTemplate())
@@ -725,7 +715,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio
"Missing 'template_id' from response body: " + res.body);
tpl_id = parsed.template_id;
var get_request = {
url: '/tiles/template/' + tpl_id + '?api_key=1234',
url: '/api/v1/map/named/' + tpl_id + '?api_key=1234',
method: 'GET',
headers: {host: 'localhost'}
}
@@ -742,7 +732,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio
"Missing 'template' from response body: " + res.body);
assert.deepEqual(extendDefaultsTemplate(makeTemplate()), parsed.template);
var del_request = {
url: '/tiles/template/' + tpl_id,
url: '/api/v1/map/named/' + tpl_id,
method: 'DELETE',
headers: {host: 'localhost'}
}
@@ -760,7 +750,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio
assert.ok(parsed.error.match(/only.*authenticated.*user/i),
'Unexpected error for unauthenticated template get: ' + parsed.error);
var del_request = {
url: '/tiles/template/' + tpl_id + '?api_key=1234',
url: '/api/v1/map/named/' + tpl_id + '?api_key=1234',
method: 'DELETE',
headers: {host: 'localhost'}
}
@@ -774,7 +764,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio
assert.equal(res.statusCode, 204, res.statusCode + ': ' + res.body);
assert.ok(!res.body, 'Unexpected body in DELETE /template response');
var get_request = {
url: '/tiles/template/' + tpl_id + '?api_key=1234',
url: '/api/v1/map/named/' + tpl_id + '?api_key=1234',
method: 'GET',
headers: {host: 'localhost'}
}
@@ -852,7 +842,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio
{
var next = this;
var post_request = {
url: '/tiles/template?api_key=1234',
url: '/api/v1/map/named?api_key=1234',
method: 'POST',
headers: {host: 'localhost', 'Content-Type': 'application/json' },
data: JSON.stringify(template_acceptance2)
@@ -869,7 +859,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio
"Missing 'template_id' from response body: " + res.body);
tpl_id = parsed.template_id;
var post_request = {
url: '/tiles/template/' + tpl_id,
url: '/api/v1/map/named/' + tpl_id,
method: 'POST',
headers: {host: 'localhost', 'Content-Type': 'application/json' },
data: JSON.stringify(template_params)
@@ -890,7 +880,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio
assert.ok(parsed.error.match(/unauthorized/i),
'Unexpected error for unauthorized instance : ' + parsed.error);
var post_request = {
url: '/tiles/template/' + tpl_id + '?auth_token=valid2',
url: '/api/v1/map/named/' + tpl_id + '?auth_token=valid2',
method: 'POST',
headers: {host: 'foreign', 'Content-Type': 'application/json' },
data: JSON.stringify(template_params)
@@ -909,7 +899,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio
assert.ok(parsed.error.match(/cannot instanciate/i),
'Unexpected error for forbidden instance : ' + parsed.error);
var post_request = {
url: '/tiles/template/' + tpl_id + '?auth_token=valid2',
url: '/api/v1/map/named/' + tpl_id + '?auth_token=valid2',
method: 'POST',
headers: {host: 'localhost', 'Content-Type': 'application/json' },
data: JSON.stringify(template_params)
@@ -933,7 +923,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio
"Missing 'last_updated' from response body: " + res.body);
// TODO: check value of last_updated ?
var get_request = {
url: '/tiles/layergroup/' + layergroupid + ':cb0/0/0/0.png',
url: '/api/v1/map/' + layergroupid + ':cb0/0/0/0.png',
method: 'GET',
headers: {host: 'localhost' },
encoding: 'binary'
@@ -953,7 +943,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio
'Unexpected error for unauthorized instance '
+ '(expected /permission denied/): ' + parsed.error);
var get_request = {
url: '/tiles/layergroup/' + layergroupid + '/0/0/0.png?auth_token=valid1',
url: '/api/v1/map/' + layergroupid + '/0/0/0.png?auth_token=valid1',
method: 'GET',
headers: {host: 'localhost' },
encoding: 'binary'
@@ -975,7 +965,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio
if ( err ) throw err;
var foreignsigned = layergroupid.replace(/[^@]*@/, 'foreign@');
var get_request = {
url: '/tiles/layergroup/' + foreignsigned + '/0/0/0.png?auth_token=valid1',
url: '/api/v1/map/' + foreignsigned + '/0/0/0.png?auth_token=valid1',
method: 'GET',
headers: {host: 'localhost' },
encoding: 'binary'
@@ -1001,7 +991,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio
{
if ( err ) throw err;
var del_request = {
url: '/tiles/template/' + tpl_id + '?api_key=1234',
url: '/api/v1/map/named/' + tpl_id + '?api_key=1234',
method: 'DELETE',
headers: {host: 'localhost'}
}
@@ -1014,7 +1004,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio
assert.equal(res.statusCode, 204,
'Deleting template: ' + res.statusCode + ':' + res.body);
var get_request = {
url: '/tiles/layergroup/' + layergroupid + '/0/0/0.png?auth_token=valid1',
url: '/api/v1/map/' + layergroupid + '/0/0/0.png?auth_token=valid1',
method: 'GET',
headers: {host: 'localhost' },
encoding: 'binary'
@@ -1086,7 +1076,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio
{
var next = this;
var post_request = {
url: '/tiles/template?api_key=1234',
url: '/api/v1/map/named?api_key=1234',
method: 'POST',
headers: {host: 'localhost', 'Content-Type': 'application/json' },
data: JSON.stringify(template)
@@ -1103,7 +1093,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio
"Missing 'template_id' from response body: " + res.body);
tpl_id = parsed.template_id;
var post_request = {
url: '/tiles/template/' + tpl_id,
url: '/api/v1/map/named/' + tpl_id,
method: 'POST',
headers: {host: 'localhost', 'Content-Type': 'application/json' },
data: JSON.stringify(template_params)
@@ -1124,7 +1114,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio
assert.ok(parsed.error.match(/unauthorized/i),
'Unexpected error for unauthorized instance : ' + parsed.error);
var post_request = {
url: '/tiles/template/' + tpl_id + '?auth_token=valid2',
url: '/api/v1/map/named/' + tpl_id + '?auth_token=valid2',
method: 'POST',
headers: {host: 'localhost', 'Content-Type': 'application/json' },
data: JSON.stringify(template_params)
@@ -1148,7 +1138,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio
"Missing 'last_updated' from response body: " + res.body);
// TODO: check value of last_updated ?
var get_request = {
url: '/tiles/layergroup/' + layergroupid + ':cb0/0/0/0/0.json.torque',
url: '/api/v1/map/' + layergroupid + ':cb0/0/0/0/0.json.torque',
method: 'GET',
headers: {host: 'localhost' },
encoding: 'binary'
@@ -1168,7 +1158,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio
'Unexpected error for unauthorized instance '
+ '(expected /permission denied): ' + parsed.error);
var get_request = {
url: '/tiles/layergroup/' + layergroupid + ':cb1/0/0/0/0.json.torque?auth_token=valid1',
url: '/api/v1/map/' + layergroupid + ':cb1/0/0/0/0.json.torque?auth_token=valid1',
method: 'GET',
headers: {host: 'localhost' },
encoding: 'binary'
@@ -1190,7 +1180,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio
serverOptions = ServerOptions(); // need to clean channel cache
server = new CartodbWindshaft(serverOptions);
var get_request = {
url: '/tiles/layergroup/' + layergroupid + ':cb1/0/0/0/1.json.torque?auth_token=valid1',
url: '/api/v1/map/' + layergroupid + ':cb1/0/0/0/1.json.torque?auth_token=valid1',
method: 'GET',
headers: {host: 'localhost' },
encoding: 'binary'
@@ -1214,7 +1204,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio
{
if ( err ) throw err;
var del_request = {
url: '/tiles/template/' + tpl_id + '?api_key=1234',
url: '/api/v1/map/named/' + tpl_id + '?api_key=1234',
method: 'DELETE',
headers: {host: 'localhost'}
}
@@ -1227,7 +1217,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio
assert.equal(res.statusCode, 204,
'Deleting template: ' + res.statusCode + ':' + res.body);
var get_request = {
url: '/tiles/layergroup/' + layergroupid + ':cb2/0/0/0/0.json.torque?auth_token=valid1',
url: '/api/v1/map/' + layergroupid + ':cb2/0/0/0/0.json.torque?auth_token=valid1',
method: 'GET',
headers: {host: 'localhost' },
encoding: 'binary'
@@ -1301,7 +1291,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio
{
var next = this;
var post_request = {
url: '/tiles/template?api_key=1234',
url: '/api/v1/map/named?api_key=1234',
method: 'POST',
headers: {host: 'localhost', 'Content-Type': 'application/json' },
data: JSON.stringify(template)
@@ -1318,7 +1308,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio
"Missing 'template_id' from response body: " + res.body);
tpl_id = parsed.template_id;
var post_request = {
url: '/tiles/template/' + tpl_id,
url: '/api/v1/map/named/' + tpl_id,
method: 'POST',
headers: {host: 'localhost', 'Content-Type': 'application/json' },
data: JSON.stringify(template_params)
@@ -1339,7 +1329,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio
assert.ok(parsed.error.match(/unauthorized/i),
'Unexpected error for unauthorized instance : ' + parsed.error);
var post_request = {
url: '/tiles/template/' + tpl_id + '?auth_token=valid2',
url: '/api/v1/map/named/' + tpl_id + '?auth_token=valid2',
method: 'POST',
headers: {host: 'localhost', 'Content-Type': 'application/json' },
data: JSON.stringify(template_params)
@@ -1363,7 +1353,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio
"Missing 'last_updated' from response body: " + res.body);
// TODO: check value of last_updated ?
var get_request = {
url: '/tiles/layergroup/' + layergroupid + ':cb0/0/attributes/5',
url: '/api/v1/map/' + layergroupid + ':cb0/0/attributes/5',
method: 'GET',
headers: {host: 'localhost' },
encoding: 'binary'
@@ -1383,7 +1373,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio
'Unexpected error for unauthorized getAttributes '
+ '(expected /permission denied/): ' + parsed.error);
var get_request = {
url: '/tiles/layergroup/' + layergroupid + ':cb1/0/attributes/5?auth_token=valid2',
url: '/api/v1/map/' + layergroupid + ':cb1/0/attributes/5?auth_token=valid2',
method: 'GET',
headers: {host: 'localhost' },
encoding: 'binary'
@@ -1404,7 +1394,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio
{
if ( err ) throw err;
var del_request = {
url: '/tiles/template/' + tpl_id + '?api_key=1234',
url: '/api/v1/map/named/' + tpl_id + '?api_key=1234',
method: 'DELETE',
headers: {host: 'localhost'}
}
@@ -1417,7 +1407,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio
assert.equal(res.statusCode, 204,
'Deleting template: ' + res.statusCode + ':' + res.body);
var get_request = {
url: '/tiles/layergroup/' + layergroupid + ':cb2/0/attributes/5?auth_token=valid2',
url: '/api/v1/map/' + layergroupid + ':cb2/0/attributes/5?auth_token=valid2',
method: 'GET',
headers: {host: 'localhost' },
encoding: 'binary'
@@ -1491,7 +1481,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio
{
var next = this;
var post_request = {
url: '/tiles/template?api_key=1234',
url: '/api/v1/map/named?api_key=1234',
method: 'POST',
headers: {host: 'localhost', 'Content-Type': 'application/json' },
data: JSON.stringify(template_acceptance_open)
@@ -1508,7 +1498,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio
"Missing 'template_id' from response body: " + res.body);
tpl_id = parsed.template_id;
var post_request = {
url: '/tiles/template/' + tpl_id,
url: '/api/v1/map/named/' + tpl_id,
method: 'POST',
headers: {host: 'localhost', 'Content-Type': 'application/json' },
data: JSON.stringify(template_params)
@@ -1560,7 +1550,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio
{
var next = this;
var post_request = {
url: '/tiles/template?api_key=1234',
url: '/api/v1/map/named?api_key=1234',
method: 'POST',
headers: {host: 'localhost', 'Content-Type': 'application/json' },
data: JSON.stringify(template_acceptance_open)
@@ -1577,7 +1567,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio
"Missing 'template_id' from response body: " + res.body);
tpl_id = parsed.template_id;
var post_request = {
url: '/tiles/template/' + tpl_id + "/jsonp?callback=test",
url: '/api/v1/map/named/' + tpl_id + "/jsonp?callback=test",
method: 'GET',
headers: {host: 'localhost' }
}
@@ -1634,7 +1624,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio
{
var next = this;
var post_request = {
url: '/tiles/template?api_key=1234',
url: '/api/v1/map/named?api_key=1234',
method: 'POST',
headers: {host: 'localhost', 'Content-Type': 'application/json' },
data: JSON.stringify(template_acceptance_open)
@@ -1651,7 +1641,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio
"Missing 'template_id' from response body: " + res.body);
tpl_id = parsed.template_id;
var post_request = {
url: '/tiles/template/' + tpl_id + "/jsonp?callback=test%config=" + JSON.stringify('{color:blue}'),
url: '/api/v1/map/named/' + tpl_id + "/jsonp?callback=test%config=" + JSON.stringify('{color:blue}'),
method: 'GET',
headers: {host: 'localhost' }
}
@@ -1712,7 +1702,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio
{
if ( err ) throw err;
var post_request = {
url: '/tiles/template?api_key=1234',
url: '/api/v1/map/named?api_key=1234',
method: 'POST',
headers: {host: 'localhost', 'Content-Type': 'application/json' },
data: JSON.stringify(template)
@@ -1726,7 +1716,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio
assert.equal(res.statusCode, 200, res.body);
template_id = JSON.parse(res.body).template_id;
var post_request = {
url: '/tiles/template/' + template_id,
url: '/api/v1/map/named/' + template_id,
method: 'POST',
headers: {host: 'localhost', 'Content-Type': 'application/json' },
data: JSON.stringify({})
@@ -1761,7 +1751,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio
{
if ( err ) throw err;
var del_request = {
url: '/tiles/template/' + template_id + '?api_key=1234',
url: '/api/v1/map/named/' + template_id + '?api_key=1234',
method: 'DELETE',
headers: {host: 'localhost'}
}
@@ -1814,7 +1804,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio
{
var next = this;
var post_request = {
url: '/tiles/template?api_key=1234',
url: '/api/v1/map/named?api_key=1234',
method: 'POST',
headers: {host: 'localhost', 'Content-Type': 'application/json' },
data: JSON.stringify(template_acceptance2)
@@ -1831,7 +1821,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio
"Missing 'template_id' from response body: " + res.body);
tpl_id = parsed.template_id;
var post_request = {
url: '/tiles/template/' + tpl_id + '?auth_token=valid2',
url: '/api/v1/map/named/' + tpl_id + '?auth_token=valid2',
method: 'POST',
headers: {host: 'localhost', 'Content-Type': 'application/json' },
data: JSON.stringify(template_params)
@@ -1858,7 +1848,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio
var changedTemplate = JSON.parse(JSON.stringify(template_acceptance2));
changedTemplate.auth.method = 'open';
var post_request = {
url: '/tiles/template/' + tpl_id + '/?api_key=1234',
url: '/api/v1/map/named/' + tpl_id + '/?api_key=1234',
method: 'PUT',
headers: {host: 'localhost', 'Content-Type': 'application/json' },
data: JSON.stringify(changedTemplate)
@@ -1876,7 +1866,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio
"Missing 'template_id' from response body: " + res.body);
assert.equal(tpl_id, parsed.template_id);
var post_request = {
url: '/tiles/template/' + tpl_id + '?auth_token=valid2',
url: '/api/v1/map/named/' + tpl_id + '?auth_token=valid2',
method: 'POST',
headers: {host: 'localhost', 'Content-Type': 'application/json' },
data: JSON.stringify(template_params)
@@ -1971,7 +1961,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio
assert.response(
server,
{
url: '/tiles/template?api_key=1234',
url: '/api/v1/map/named?api_key=1234',
method: 'POST',
headers: {
host: username,
@@ -1996,7 +1986,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio
assert.response(
server,
{
url: '/tiles/template/' + expectedTemplateId,
url: '/api/v1/map/named/' + expectedTemplateId,
method: 'POST',
headers: {
host: username,
@@ -2025,7 +2015,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio
assert.response(
server,
{
url: '/tiles/layergroup/' + layergroupid + '/all/0/0/0.png',
url: '/api/v1/map/' + layergroupid + '/all/0/0/0.png',
method: 'GET',
headers: {
host: username
@@ -2055,7 +2045,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio
assert.response(
server,
{
url: '/tiles/template/' + expectedTemplateId + '?api_key=1234',
url: '/api/v1/map/named/' + expectedTemplateId + '?api_key=1234',
method: 'DELETE',
headers: {
host: username
@@ -2111,7 +2101,7 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio
redis_client.select(5, function(err) {
redis_client.keys("user:localhost:mapviews*", function(err, keys) {
redis_client.del(keys, function(err) {
sqlapi_server.close(done);
done();
});
});
});
@@ -2121,5 +2111,3 @@ suite('template_api:postgres=' + cdbQueryTablesFromPostgresEnabledValue, functio
});
});
});
-81
View File
@@ -1,81 +0,0 @@
var http = require('http');
var url = require('url');
var _ = require('underscore');
var SQLAPIEmulator = function(port, cb) {
this.queries = [];
var that = this;
this.requests = [];
this.sqlapi_server = http.createServer(function(req,res) {
//console.log("server got request with method " + req.method);
var query;
that.requests.push(req);
if ( req.method == 'GET' ) {
query = url.parse(req.url, true).query;
that.handleQuery(query, res);
}
else if ( req.method == 'POST') {
var data = '';
req.on('data', function(chunk) {
//console.log("GOT Chunk " + chunk);
data += chunk;
});
req.on('end', function() {
//console.log("Data is: "); console.dir(data);
query = JSON.parse(data);
//console.log("handleQuery is " + that.handleQuery);
that.handleQuery(query, res);
});
}
else {
that.handleQuery('SQLAPIEmu does not support method' + req.method, res);
}
}).listen(port, cb);
};
SQLAPIEmulator.prototype.handleQuery = function(query, res) {
this.queries.push(query);
if ( query.q.match('SQLAPIERROR') ) {
res.statusCode = 400;
res.write(JSON.stringify({'error':'Some error occurred'}));
} else if ( query.q.match('SQLAPINOANSWER') ) {
console.log("SQLAPIEmulator will never respond, on request");
return;
} else if (query.q.match('tablenames')) {
var tableNames = JSON.stringify(query);
res.write(queryResult({tablenames: '{' + tableNames + '}', max: 1234567890.123}));
} else if ( query.q.match('EPOCH.* as max') ) {
// This is the structure of the known query sent by tiler
res.write(queryResult({max: 1234567890.123}));
} else {
if ( query.q.match('_private_') && query.api_key === undefined) {
res.statusCode = 403;
res.write(JSON.stringify({'error':'forbidden: ' + JSON.stringify(query)}));
} else {
var qs = JSON.stringify(query);
res.write(queryResult({cdb_querytables: '{' + qs + '}', max: 1234567890.123}));
}
}
res.end();
};
SQLAPIEmulator.prototype.close = function(cb) {
this.sqlapi_server.close(cb);
};
SQLAPIEmulator.prototype.getLastRequest = function() {
return this.requests.pop();
};
function queryResult(row) {
return JSON.stringify({
rows: [row]
});
}
module.exports = SQLAPIEmulator;
-25
View File
@@ -1,25 +0,0 @@
var net = require('net');
module.exports = function(on_cmd_recieved, test_callback) {
var self = this;
var welcome_msg = 'hi, im a varnish emu, right?';
self.commands_recieved = [];
var server = net.createServer(function (socket) {
var command = '';
socket.write("200 " + welcome_msg.length + "\n");
socket.write(welcome_msg);
socket.on('data', function(data) {
self.commands_recieved.push(data);
on_cmd_recieved && on_cmd_recieved(self.commands_recieved);
socket.write('200 0\n');
});
});
server.listen(1337, "127.0.0.1");
server.on('listening', function(){
test_callback();
});
};
+60
View File
@@ -0,0 +1,60 @@
require('../../support/test_helper');
var assert = require('assert');
var CdbRequest = require('../../../lib/cartodb/models/cdb_request');
describe('req2params', function() {
function createRequest(host, userParam) {
var req = {
params: {},
headers: {
host: host
}
};
if (userParam) {
req.params.user = userParam;
}
return req;
}
it('extracts name from host header', function() {
var cdbRequest = new CdbRequest();
var user = cdbRequest.userByReq(createRequest('localhost'));
assert.equal(user, 'localhost');
});
it('extracts name from subdomain host header in case of no config', function() {
var userFromHostConfig = global.environment.user_from_host;
global.environment.user_from_host = null;
var cdbRequest = new CdbRequest();
var user = cdbRequest.userByReq(createRequest('development.localhost.lan'));
global.environment.user_from_host = userFromHostConfig;
assert.equal(user, 'development');
});
it('considers user param before headers', function() {
var cdbRequest = new CdbRequest();
var user = cdbRequest.userByReq(createRequest('localhost', 'development'));
assert.equal(user, 'development');
});
it('returns undefined when it cannot extract username', function() {
var userFromHostConfig = global.environment.user_from_host;
global.environment.user_from_host = null;
var cdbRequest = new CdbRequest();
var user = cdbRequest.userByReq(createRequest('localhost'));
global.environment.user_from_host = userFromHostConfig;
assert.equal(user, undefined);
});
});
+34 -25
View File
@@ -1,8 +1,6 @@
var assert = require('assert')
, _ = require('underscore')
, redis = require('redis')
, test_helper = require('../../support/test_helper')
, tests = module.exports = {};
var assert = require('assert');
var _ = require('underscore');
var test_helper = require('../../support/test_helper');
suite('req2params', function() {
@@ -24,7 +22,7 @@ suite('req2params', function() {
assert.ok(_.isObject(req.query), 'request has query');
assert.ok(!req.query.hasOwnProperty('dbuser'), 'dbuser was removed from query');
assert.ok(req.hasOwnProperty('params'), 'request has params');
assert.ok(req.params.hasOwnProperty('interactivity'), 'request params have interactivity');
assert.ok(!req.params.hasOwnProperty('interactivity'), 'request params do not have interactivity');
assert.equal(req.params.dbname, test_database, 'could forge dbname: '+ req.params.dbname);
assert.ok(req.params.dbuser === test_pubuser, 'could inject dbuser ('+req.params.dbuser+')');
done();
@@ -38,7 +36,7 @@ suite('req2params', function() {
assert.ok(_.isObject(req.query), 'request has query');
assert.ok(!req.query.hasOwnProperty('dbuser'), 'dbuser was removed from query');
assert.ok(req.hasOwnProperty('params'), 'request has params');
assert.ok(req.params.hasOwnProperty('interactivity'), 'request params have interactivity');
assert.ok(!req.params.hasOwnProperty('interactivity'), 'request params do not have interactivity');
assert.equal(req.params.dbname, test_database);
assert.ok(req.params.dbuser === test_pubuser, 'could inject dbuser ('+req.params.dbuser+')');
done();
@@ -52,7 +50,7 @@ suite('req2params', function() {
assert.ok(_.isObject(req.query), 'request has query');
assert.ok(!req.query.hasOwnProperty('dbuser'), 'dbuser was removed from query');
assert.ok(req.hasOwnProperty('params'), 'request has params');
assert.ok(req.params.hasOwnProperty('interactivity'), 'request params have interactivity');
assert.ok(!req.params.hasOwnProperty('interactivity'), 'request params do not have interactivity');
assert.equal(req.params.dbname, test_database);
assert.equal(req.params.dbuser, test_user);
@@ -65,23 +63,34 @@ suite('req2params', function() {
});
test('it should extend params with decoded lzma', function(done) {
var qo = {
style: 'test',
style_version: '2.1.0',
cache_buster: 5
};
test_helper.lzma_compress_to_base64(JSON.stringify(qo), 1, function(err, data) {
opts.req2params({ headers: { host:'localhost' }, query: { non_included: 'toberemoved', api_key: 'test', style: 'override', lzma: data }}, function(err, req) {
if ( err ) { done(err); return; }
var query = req.params
assert.equal(qo.style, query.style)
assert.equal(qo.style_version, query.style_version)
assert.equal(qo.cache_buster, query.cache_buster)
assert.equal('test', query.api_key)
assert.equal(undefined, query.non_included)
done();
var qo = {
config: {
version: '1.3.0'
}
};
test_helper.lzma_compress_to_base64(JSON.stringify(qo), 1, function(err, data) {
var req = {
headers: {
host:'localhost'
},
query: {
non_included: 'toberemoved',
api_key: 'test',
style: 'override',
lzma: data
}
};
opts.req2params(req, function(err, req) {
if ( err ) {
return done(err);
}
var query = req.params;
assert.deepEqual(qo.config, query.config);
assert.equal('test', query.api_key);
assert.equal(undefined, query.non_included);
done();
});
});
});
});
});