diff --git a/NEWS.md b/NEWS.md index 701cea00..c7fd8afd 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,17 @@ # Changelog +## 2.28.1 + +Released 2016-mm-dd + + +## 2.28.0 + +Released 2016-03-14 + +New features: + - Added [turbo-cartocss](https://github.com/CartoDB/turbo-cartocss) to preprocess CartoCSS. + ## 2.27.0 Released 2016-03-09 diff --git a/lib/cartodb/cache/named_map_provider_cache.js b/lib/cartodb/cache/named_map_provider_cache.js index 0048cb63..7bb8e25c 100644 --- a/lib/cartodb/cache/named_map_provider_cache.js +++ b/lib/cartodb/cache/named_map_provider_cache.js @@ -7,12 +7,13 @@ var queue = require('queue-async'); var LruCache = require("lru-cache"); -function NamedMapProviderCache(templateMaps, pgConnection, userLimitsApi) { +function NamedMapProviderCache(templateMaps, pgConnection, userLimitsApi, turboCartocssAdapter) { this.templateMaps = templateMaps; this.pgConnection = pgConnection; this.userLimitsApi = userLimitsApi; this.namedLayersAdapter = new MapConfigNamedLayersAdapter(templateMaps); + this.turboCartocssAdapter = turboCartocssAdapter; this.providerCache = new LruCache({ max: 2000 }); } @@ -30,6 +31,7 @@ NamedMapProviderCache.prototype.get = function(user, templateId, config, authTok this.pgConnection, this.userLimitsApi, this.namedLayersAdapter, + this.turboCartocssAdapter, user, templateId, config, diff --git a/lib/cartodb/controllers/map.js b/lib/cartodb/controllers/map.js index baed016d..749a10a9 100644 --- a/lib/cartodb/controllers/map.js +++ b/lib/cartodb/controllers/map.js @@ -35,7 +35,7 @@ var MapConfigOverviewsAdapter = require('../models/mapconfig_overviews_adapter') */ function MapController(authApi, pgConnection, templateMaps, mapBackend, metadataBackend, overviewsMetadataApi, - surrogateKeysCache, userLimitsApi, layergroupAffectedTables) { + surrogateKeysCache, userLimitsApi, layergroupAffectedTables, turboCartoCssAdapter) { BaseController.call(this, authApi, pgConnection); @@ -47,6 +47,7 @@ function MapController(authApi, pgConnection, templateMaps, mapBackend, metadata this.surrogateKeysCache = surrogateKeysCache; this.userLimitsApi = userLimitsApi; this.layergroupAffectedTables = layergroupAffectedTables; + this.turboCartoCssAdapter = turboCartoCssAdapter; this.analysisMapConfigAdapter = new AnalysisMapConfigAdapter(); this.namedLayersAdapter = new MapConfigNamedLayersAdapter(templateMaps); @@ -183,6 +184,23 @@ MapController.prototype.create = function(req, res, prepareConfigFn) { if (layers) { requestMapConfig.layers = layers; } + + return next(null, requestMapConfig, datasource); + }); + }, + function parseTurboCartoCss(err, requestMapConfig, datasource) { + assert.ifError(err); + + var next = this; + self.turboCartoCssAdapter.getLayers(req.context.user, requestMapConfig.layers, function (err, layers) { + if (err) { + return next(err); + } + + if (layers) { + requestMapConfig.layers = layers; + } + return next(null, requestMapConfig, datasource); }); }, @@ -234,6 +252,7 @@ MapController.prototype.instantiateTemplate = function(req, res, prepareParamsFn self.pgConnection, self.userLimitsApi, self.namedLayersAdapter, + self.turboCartoCssAdapter, cdbuser, req.params.template_id, templateParams, diff --git a/lib/cartodb/controllers/named_maps_admin.js b/lib/cartodb/controllers/named_maps_admin.js index cd17b5c7..3d201049 100644 --- a/lib/cartodb/controllers/named_maps_admin.js +++ b/lib/cartodb/controllers/named_maps_admin.js @@ -65,6 +65,7 @@ NamedMapsAdminController.prototype.update = function(req, res) { var cdbuser = req.context.user; var template; var tpl_id; + step( function checkPerms(){ self.authApi.authorizedByAPIKey(cdbuser, req, this); diff --git a/lib/cartodb/models/mapconfig/named_map_provider.js b/lib/cartodb/models/mapconfig/named_map_provider.js index 3e09b466..a31bda13 100644 --- a/lib/cartodb/models/mapconfig/named_map_provider.js +++ b/lib/cartodb/models/mapconfig/named_map_provider.js @@ -12,11 +12,12 @@ var QueryTables = require('cartodb-query-tables'); * @type {NamedMapMapConfigProvider} */ function NamedMapMapConfigProvider(templateMaps, pgConnection, userLimitsApi, namedLayersAdapter, - owner, templateId, config, authToken, params) { + turboCartoCssAdapter, owner, templateId, config, authToken, params) { this.templateMaps = templateMaps; this.pgConnection = pgConnection; this.userLimitsApi = userLimitsApi; this.namedLayersAdapter = namedLayersAdapter; + this.turboCartoCssAdapter = turboCartoCssAdapter; this.owner = owner; this.templateName = templateName(templateId); @@ -91,6 +92,18 @@ NamedMapMapConfigProvider.prototype.getMapConfig = function(callback) { } ); }, + function parseTurboCartoCss(err, _mapConfig, datasource) { + assert.ifError(err); + var next = this; + + self.turboCartoCssAdapter.getLayers(self.owner, _mapConfig.layers, function (err, layers) { + if (!err && layers) { + _mapConfig.layers = layers; + } + + return next(null, _mapConfig, datasource); + }); + }, function beforeLayergroupCreate(err, _mapConfig, _datasource) { assert.ifError(err); mapConfig = _mapConfig; diff --git a/lib/cartodb/server.js b/lib/cartodb/server.js index 39884850..28eea3c5 100644 --- a/lib/cartodb/server.js +++ b/lib/cartodb/server.js @@ -30,6 +30,8 @@ var PgConnection = require('./backends/pg_connection'); var timeoutErrorTilePath = __dirname + '/../../assets/render-timeout-fallback.png'; var timeoutErrorTile = require('fs').readFileSync(timeoutErrorTilePath, {encoding: null}); +var TurboCartocssParser = require('./utils/style/turbo-cartocss-parser'); +var TurboCartocssAdapter = require('./utils/style/turbo-cartocss-adapter'); module.exports = function(serverOptions) { // Make stats client globally accessible @@ -140,7 +142,16 @@ module.exports = function(serverOptions) { var layergroupAffectedTablesCache = new LayergroupAffectedTablesCache(); app.layergroupAffectedTablesCache = layergroupAffectedTablesCache; - var namedMapProviderCache = new NamedMapProviderCache(templateMaps, pgConnection, userLimitsApi); + var turboCartoCssParser = new TurboCartocssParser(pgQueryRunner); + var turboCartocssAdapter = new TurboCartocssAdapter(turboCartoCssParser); + + var namedMapProviderCache = new NamedMapProviderCache( + templateMaps, + pgConnection, + userLimitsApi, + turboCartocssAdapter + ); + ['update', 'delete'].forEach(function(eventType) { templateMaps.on(eventType, namedMapProviderCache.invalidate.bind(namedMapProviderCache)); }); @@ -176,7 +187,8 @@ module.exports = function(serverOptions) { overviewsMetadataApi, surrogateKeysCache, userLimitsApi, - layergroupAffectedTablesCache + layergroupAffectedTablesCache, + turboCartocssAdapter ).register(app); new controller.NamedMaps( diff --git a/lib/cartodb/utils/style/postgres-datasource.js b/lib/cartodb/utils/style/postgres-datasource.js new file mode 100644 index 00000000..fd6e02c1 --- /dev/null +++ b/lib/cartodb/utils/style/postgres-datasource.js @@ -0,0 +1,55 @@ +'use strict'; + +var dot = require('dot'); +dot.templateSettings.strip = false; + +function createTemplate(method) { + return dot.template([ + 'SELECT', + method, + 'FROM ({{=it._sql}}) _table_sql WHERE {{=it._column}} IS NOT NULL' + ].join('\n')); +} + +var methods = { + quantiles: 'CDB_QuantileBins(array_agg(distinct({{=it._column}}::numeric)), {{=it._buckets}}) as quantiles', + equal: 'CDB_EqualIntervalBins(array_agg({{=it._column}}::numeric), {{=it._buckets}}) as equal', + jenks: 'CDB_JenksBins(array_agg(distinct({{=it._column}}::numeric)), {{=it._buckets}}) as jenks', + headtails: 'CDB_HeadsTailsBins(array_agg(distinct({{=it._column}}::numeric)), {{=it._buckets}}) as headtails' +}; + +var methodTemplates = Object.keys(methods).reduce(function(methodTemplates, methodName) { + methodTemplates[methodName] = createTemplate(methods[methodName]); + return methodTemplates; +}, {}); + +function PostgresDatasource (pgQueryRunner, username, query) { + this.pgQueryRunner = pgQueryRunner; + this.username = username; + this.query = query; +} + +PostgresDatasource.prototype.getName = function () { + return 'PostgresDatasource'; +}; + +PostgresDatasource.prototype.getRamp = function (column, buckets, method, callback) { + var methodName = methods.hasOwnProperty(method) ? method : 'quantiles'; + var template = methodTemplates[methodName]; + + var query = template({ _column: column, _sql: this.query, _buckets: buckets }); + + this.pgQueryRunner.run(this.username, query, function (err, result) { + if (err) { + return callback(err); + } + + var ramp = result[0][methodName].sort(function(a, b) { + return a - b; + }); + + return callback(null, ramp); + }); +}; + +module.exports = PostgresDatasource; diff --git a/lib/cartodb/utils/style/turbo-cartocss-adapter.js b/lib/cartodb/utils/style/turbo-cartocss-adapter.js new file mode 100644 index 00000000..7f181c4e --- /dev/null +++ b/lib/cartodb/utils/style/turbo-cartocss-adapter.js @@ -0,0 +1,56 @@ +'use strict'; + +var queue = require('queue-async'); + +function TurboCartocssAdapter(turboCartocssParser) { + this.turboCartocssParser = turboCartocssParser; +} + +module.exports = TurboCartocssAdapter; + +TurboCartocssAdapter.prototype.getLayers = function (username, layers, callback) { + var self = this; + + if (!layers || layers.length === 0) { + return callback(null, layers); + } + + var parseCartoCssQueue = queue(layers.length); + + layers.forEach(function(layer) { + parseCartoCssQueue.defer(self._parseCartoCss.bind(self), username, layer); + }); + + parseCartoCssQueue.awaitAll(function (err, layers) { + if (err) { + return callback(err); + } + + return callback(null, layers); + }); +}; + +TurboCartocssAdapter.prototype._parseCartoCss = function (username, layer, callback) { + if (isNotLayerToParseCartocss(layer)) { + return process.nextTick(function () { + callback(null, layer); + }); + } + + this.turboCartocssParser.process(username, layer.options.cartocss, layer.options.sql, function (err, cartocss) { + // Ignore turbo-cartocss errors and continue + if (!err && cartocss) { + layer.options.cartocss = cartocss; + } + + callback(null, layer); + }); +}; + +function isNotLayerToParseCartocss(layer) { + if (!layer || !layer.options || !layer.options.cartocss || !layer.options.sql) { + return true; + } + + return false; +} diff --git a/lib/cartodb/utils/style/turbo-cartocss-parser.js b/lib/cartodb/utils/style/turbo-cartocss-parser.js new file mode 100644 index 00000000..19a7f9c0 --- /dev/null +++ b/lib/cartodb/utils/style/turbo-cartocss-parser.js @@ -0,0 +1,15 @@ +'use strict'; + +var turboCartoCss = require('turbo-cartocss'); +var PostgresDatasource = require('./postgres-datasource'); + +function TurboCartocssParser (pgQueryRunner) { + this.pgQueryRunner = pgQueryRunner; +} + +module.exports = TurboCartocssParser; + +TurboCartocssParser.prototype.process = function (username, cartocss, sql, callback) { + var datasource = new PostgresDatasource(this.pgQueryRunner, username, sql); + turboCartoCss(cartocss, datasource, callback); +}; diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 4e11816a..28389790 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -1,6 +1,6 @@ { "name": "windshaft-cartodb", - "version": "2.27.0", + "version": "2.28.1", "dependencies": { "body-parser": { "version": "1.14.2", @@ -90,7 +90,7 @@ }, "mime-types": { "version": "2.1.10", - "from": "mime-types@>=2.1.2 <2.2.0", + "from": "mime-types@>=2.1.10 <2.2.0", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.10.tgz", "dependencies": { "mime-db": { @@ -111,7 +111,7 @@ "dependencies": { "async": { "version": "1.5.2", - "from": "async@>=1.4.0 <2.0.0", + "from": "async@>=1.5.2 <2.0.0", "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz" }, "cartodb-psql": { @@ -178,9 +178,9 @@ "resolved": "https://registry.npmjs.org/bl/-/bl-1.0.3.tgz", "dependencies": { "readable-stream": { - "version": "2.0.5", + "version": "2.0.6", "from": "readable-stream@>=2.0.5 <2.1.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.5.tgz", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", "dependencies": { "core-util-is": { "version": "1.0.2", @@ -193,9 +193,9 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" }, "isarray": { - "version": "0.0.1", - "from": "isarray@0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + "version": "1.0.0", + "from": "isarray@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" }, "process-nextick-args": { "version": "1.0.6", @@ -631,7 +631,7 @@ "dependencies": { "mime-types": { "version": "2.1.10", - "from": "mime-types@>=2.1.10 <2.2.0", + "from": "mime-types@>=2.1.6 <2.2.0", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.10.tgz", "dependencies": { "mime-db": { @@ -810,7 +810,7 @@ }, "type-is": { "version": "1.6.12", - "from": "type-is@>=1.6.6 <1.7.0", + "from": "type-is@>=1.6.10 <1.7.0", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.12.tgz", "dependencies": { "media-typer": { @@ -820,7 +820,7 @@ }, "mime-types": { "version": "2.1.10", - "from": "mime-types@>=2.1.10 <2.2.0", + "from": "mime-types@>=2.1.6 <2.2.0", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.10.tgz", "dependencies": { "mime-db": { @@ -963,9 +963,9 @@ "resolved": "https://registry.npmjs.org/bl/-/bl-1.0.3.tgz", "dependencies": { "readable-stream": { - "version": "2.0.5", + "version": "2.0.6", "from": "readable-stream@>=2.0.5 <2.1.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.5.tgz", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", "dependencies": { "core-util-is": { "version": "1.0.2", @@ -978,9 +978,9 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" }, "isarray": { - "version": "0.0.1", - "from": "isarray@0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + "version": "1.0.0", + "from": "isarray@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" }, "process-nextick-args": { "version": "1.0.6", @@ -1269,6 +1269,529 @@ "from": "step-profiler@>=0.2.1 <0.3.0", "resolved": "https://registry.npmjs.org/step-profiler/-/step-profiler-0.2.1.tgz" }, + "turbo-cartocss": { + "version": "0.4.0", + "from": "turbo-cartocss@0.4.0", + "resolved": "https://registry.npmjs.org/turbo-cartocss/-/turbo-cartocss-0.4.0.tgz", + "dependencies": { + "browser-request": { + "version": "0.3.3", + "from": "browser-request@>=0.3.3 <0.4.0", + "resolved": "https://registry.npmjs.org/browser-request/-/browser-request-0.3.3.tgz" + }, + "colorbrewer": { + "version": "0.0.2", + "from": "colorbrewer@0.0.2", + "resolved": "https://registry.npmjs.org/colorbrewer/-/colorbrewer-0.0.2.tgz" + }, + "es6-promise": { + "version": "3.1.2", + "from": "es6-promise@>=3.0.2 <4.0.0", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.1.2.tgz" + }, + "less": { + "version": "2.6.1", + "from": "less@>=2.5.3 <3.0.0", + "resolved": "https://registry.npmjs.org/less/-/less-2.6.1.tgz", + "dependencies": { + "errno": { + "version": "0.1.4", + "from": "errno@>=0.1.1 <0.2.0", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.4.tgz", + "dependencies": { + "prr": { + "version": "0.0.0", + "from": "prr@>=0.0.0 <0.1.0", + "resolved": "https://registry.npmjs.org/prr/-/prr-0.0.0.tgz" + } + } + }, + "graceful-fs": { + "version": "4.1.3", + "from": "graceful-fs@>=4.1.2 <5.0.0", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.3.tgz" + }, + "image-size": { + "version": "0.4.0", + "from": "image-size@>=0.4.0 <0.5.0", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.4.0.tgz" + }, + "mime": { + "version": "1.3.4", + "from": "mime@>=1.2.11 <2.0.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.3.4.tgz" + }, + "mkdirp": { + "version": "0.5.1", + "from": "mkdirp@>=0.5.0 <0.6.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "dependencies": { + "minimist": { + "version": "0.0.8", + "from": "minimist@0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" + } + } + }, + "promise": { + "version": "7.1.1", + "from": "promise@>=7.1.1 <8.0.0", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.1.1.tgz", + "dependencies": { + "asap": { + "version": "2.0.3", + "from": "asap@>=2.0.3 <2.1.0", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.3.tgz" + } + } + }, + "source-map": { + "version": "0.5.3", + "from": "source-map@>=0.5.3 <0.6.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.3.tgz" + } + } + }, + "postcss": { + "version": "5.0.19", + "from": "postcss@>=5.0.13 <6.0.0", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.0.19.tgz", + "dependencies": { + "supports-color": { + "version": "3.1.2", + "from": "supports-color@>=3.1.2 <4.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.1.2.tgz", + "dependencies": { + "has-flag": { + "version": "1.0.0", + "from": "has-flag@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz" + } + } + }, + "source-map": { + "version": "0.5.3", + "from": "source-map@>=0.5.1 <0.6.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.3.tgz" + }, + "js-base64": { + "version": "2.1.9", + "from": "js-base64@>=2.1.9 <3.0.0", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.1.9.tgz" + } + } + }, + "postcss-value-parser": { + "version": "3.3.0", + "from": "postcss-value-parser@>=3.2.3 <4.0.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz" + }, + "querystring": { + "version": "0.2.0", + "from": "querystring@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz" + }, + "request": { + "version": "2.69.0", + "from": "request@>=2.67.0 <3.0.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.69.0.tgz", + "dependencies": { + "aws-sign2": { + "version": "0.6.0", + "from": "aws-sign2@>=0.6.0 <0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz" + }, + "aws4": { + "version": "1.3.2", + "from": "aws4@>=1.2.1 <2.0.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.3.2.tgz", + "dependencies": { + "lru-cache": { + "version": "4.0.0", + "from": "lru-cache@>=4.0.0 <5.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.0.0.tgz", + "dependencies": { + "pseudomap": { + "version": "1.0.2", + "from": "pseudomap@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz" + }, + "yallist": { + "version": "2.0.0", + "from": "yallist@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.0.0.tgz" + } + } + } + } + }, + "bl": { + "version": "1.0.3", + "from": "bl@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-1.0.3.tgz", + "dependencies": { + "readable-stream": { + "version": "2.0.6", + "from": "readable-stream@>=2.0.5 <2.1.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.2", + "from": "core-util-is@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz" + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.1 <2.1.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "isarray": { + "version": "1.0.0", + "from": "isarray@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" + }, + "process-nextick-args": { + "version": "1.0.6", + "from": "process-nextick-args@>=1.0.6 <1.1.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.6.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "string_decoder@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + }, + "util-deprecate": { + "version": "1.0.2", + "from": "util-deprecate@>=1.0.1 <1.1.0", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" + } + } + } + } + }, + "caseless": { + "version": "0.11.0", + "from": "caseless@>=0.11.0 <0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.11.0.tgz" + }, + "combined-stream": { + "version": "1.0.5", + "from": "combined-stream@>=1.0.5 <1.1.0", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", + "dependencies": { + "delayed-stream": { + "version": "1.0.0", + "from": "delayed-stream@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" + } + } + }, + "extend": { + "version": "3.0.0", + "from": "extend@>=3.0.0 <3.1.0", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.0.tgz" + }, + "forever-agent": { + "version": "0.6.1", + "from": "forever-agent@>=0.6.1 <0.7.0", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz" + }, + "form-data": { + "version": "1.0.0-rc3", + "from": "form-data@>=1.0.0-rc3 <1.1.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-1.0.0-rc3.tgz", + "dependencies": { + "async": { + "version": "1.5.2", + "from": "async@>=1.4.0 <2.0.0", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz" + } + } + }, + "har-validator": { + "version": "2.0.6", + "from": "har-validator@>=2.0.6 <2.1.0", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-2.0.6.tgz", + "dependencies": { + "chalk": { + "version": "1.1.1", + "from": "chalk@>=1.1.1 <2.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.1.tgz", + "dependencies": { + "ansi-styles": { + "version": "2.2.0", + "from": "ansi-styles@>=2.1.0 <3.0.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.0.tgz", + "dependencies": { + "color-convert": { + "version": "1.0.0", + "from": "color-convert@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.0.0.tgz" + } + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "from": "escape-string-regexp@>=1.0.2 <2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" + }, + "has-ansi": { + "version": "2.0.0", + "from": "has-ansi@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "dependencies": { + "ansi-regex": { + "version": "2.0.0", + "from": "ansi-regex@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.0.0.tgz" + } + } + }, + "strip-ansi": { + "version": "3.0.1", + "from": "strip-ansi@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "dependencies": { + "ansi-regex": { + "version": "2.0.0", + "from": "ansi-regex@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.0.0.tgz" + } + } + }, + "supports-color": { + "version": "2.0.0", + "from": "supports-color@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz" + } + } + }, + "commander": { + "version": "2.9.0", + "from": "commander@>=2.9.0 <3.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", + "dependencies": { + "graceful-readlink": { + "version": "1.0.1", + "from": "graceful-readlink@>=1.0.0", + "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz" + } + } + }, + "is-my-json-valid": { + "version": "2.13.1", + "from": "is-my-json-valid@>=2.12.4 <3.0.0", + "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.13.1.tgz", + "dependencies": { + "generate-function": { + "version": "2.0.0", + "from": "generate-function@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz" + }, + "generate-object-property": { + "version": "1.2.0", + "from": "generate-object-property@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz", + "dependencies": { + "is-property": { + "version": "1.0.2", + "from": "is-property@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz" + } + } + }, + "jsonpointer": { + "version": "2.0.0", + "from": "jsonpointer@2.0.0", + "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-2.0.0.tgz" + }, + "xtend": { + "version": "4.0.1", + "from": "xtend@>=4.0.0 <5.0.0", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz" + } + } + }, + "pinkie-promise": { + "version": "2.0.0", + "from": "pinkie-promise@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.0.tgz", + "dependencies": { + "pinkie": { + "version": "2.0.4", + "from": "pinkie@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz" + } + } + } + } + }, + "hawk": { + "version": "3.1.3", + "from": "hawk@>=3.1.0 <3.2.0", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", + "dependencies": { + "hoek": { + "version": "2.16.3", + "from": "hoek@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz" + }, + "boom": { + "version": "2.10.1", + "from": "boom@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz" + }, + "cryptiles": { + "version": "2.0.5", + "from": "cryptiles@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz" + }, + "sntp": { + "version": "1.0.9", + "from": "sntp@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz" + } + } + }, + "http-signature": { + "version": "1.1.1", + "from": "http-signature@>=1.1.0 <1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", + "dependencies": { + "assert-plus": { + "version": "0.2.0", + "from": "assert-plus@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz" + }, + "jsprim": { + "version": "1.2.2", + "from": "jsprim@>=1.2.2 <2.0.0", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.2.2.tgz", + "dependencies": { + "extsprintf": { + "version": "1.0.2", + "from": "extsprintf@1.0.2", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.0.2.tgz" + }, + "json-schema": { + "version": "0.2.2", + "from": "json-schema@0.2.2", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.2.tgz" + }, + "verror": { + "version": "1.3.6", + "from": "verror@1.3.6", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.3.6.tgz" + } + } + }, + "sshpk": { + "version": "1.7.4", + "from": "sshpk@>=1.7.0 <2.0.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.7.4.tgz", + "dependencies": { + "asn1": { + "version": "0.2.3", + "from": "asn1@>=0.2.3 <0.3.0", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz" + }, + "dashdash": { + "version": "1.13.0", + "from": "dashdash@>=1.10.1 <2.0.0", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.13.0.tgz", + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "from": "assert-plus@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" + } + } + }, + "jsbn": { + "version": "0.1.0", + "from": "jsbn@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.0.tgz" + }, + "tweetnacl": { + "version": "0.14.1", + "from": "tweetnacl@>=0.13.0 <1.0.0", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.1.tgz" + }, + "jodid25519": { + "version": "1.0.2", + "from": "jodid25519@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/jodid25519/-/jodid25519-1.0.2.tgz" + }, + "ecc-jsbn": { + "version": "0.1.1", + "from": "ecc-jsbn@>=0.0.1 <1.0.0", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz" + } + } + } + } + }, + "is-typedarray": { + "version": "1.0.0", + "from": "is-typedarray@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz" + }, + "isstream": { + "version": "0.1.2", + "from": "isstream@>=0.1.2 <0.2.0", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz" + }, + "json-stringify-safe": { + "version": "5.0.1", + "from": "json-stringify-safe@>=5.0.1 <5.1.0", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz" + }, + "mime-types": { + "version": "2.1.10", + "from": "mime-types@>=2.1.7 <2.2.0", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.10.tgz", + "dependencies": { + "mime-db": { + "version": "1.22.0", + "from": "mime-db@>=1.22.0 <1.23.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.22.0.tgz" + } + } + }, + "node-uuid": { + "version": "1.4.7", + "from": "node-uuid@>=1.4.7 <1.5.0", + "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.7.tgz" + }, + "oauth-sign": { + "version": "0.8.1", + "from": "oauth-sign@>=0.8.0 <0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.1.tgz" + }, + "qs": { + "version": "6.0.2", + "from": "qs@>=6.0.2 <6.1.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.0.2.tgz" + }, + "stringstream": { + "version": "0.0.5", + "from": "stringstream@>=0.0.4 <0.1.0", + "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz" + }, + "tough-cookie": { + "version": "2.2.2", + "from": "tough-cookie@>=2.2.0 <2.3.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.2.2.tgz" + }, + "tunnel-agent": { + "version": "0.4.2", + "from": "tunnel-agent@>=0.4.1 <0.5.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.2.tgz" + } + } + } + } + }, "underscore": { "version": "1.6.0", "from": "underscore@>=1.6.0 <1.7.0", @@ -4166,529 +4689,6 @@ "version": "2.11.4", "from": "torque.js@>=2.11.0 <2.12.0", "resolved": "https://registry.npmjs.org/torque.js/-/torque.js-2.11.4.tgz" - }, - "turbo-cartocss": { - "version": "0.4.0", - "from": "turbo-cartocss@>=0.4.0 <0.5.0", - "resolved": "https://registry.npmjs.org/turbo-cartocss/-/turbo-cartocss-0.4.0.tgz", - "dependencies": { - "browser-request": { - "version": "0.3.3", - "from": "browser-request@>=0.3.3 <0.4.0", - "resolved": "https://registry.npmjs.org/browser-request/-/browser-request-0.3.3.tgz" - }, - "colorbrewer": { - "version": "0.0.2", - "from": "colorbrewer@0.0.2", - "resolved": "https://registry.npmjs.org/colorbrewer/-/colorbrewer-0.0.2.tgz" - }, - "es6-promise": { - "version": "3.1.2", - "from": "es6-promise@>=3.0.2 <4.0.0", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.1.2.tgz" - }, - "less": { - "version": "2.6.1", - "from": "less@>=2.5.3 <3.0.0", - "resolved": "https://registry.npmjs.org/less/-/less-2.6.1.tgz", - "dependencies": { - "errno": { - "version": "0.1.4", - "from": "errno@>=0.1.1 <0.2.0", - "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.4.tgz", - "dependencies": { - "prr": { - "version": "0.0.0", - "from": "prr@>=0.0.0 <0.1.0", - "resolved": "https://registry.npmjs.org/prr/-/prr-0.0.0.tgz" - } - } - }, - "graceful-fs": { - "version": "4.1.3", - "from": "graceful-fs@>=4.1.2 <5.0.0", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.3.tgz" - }, - "image-size": { - "version": "0.4.0", - "from": "image-size@>=0.4.0 <0.5.0", - "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.4.0.tgz" - }, - "mime": { - "version": "1.3.4", - "from": "mime@>=1.2.11 <2.0.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.3.4.tgz" - }, - "mkdirp": { - "version": "0.5.1", - "from": "mkdirp@>=0.5.0 <0.6.0", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "dependencies": { - "minimist": { - "version": "0.0.8", - "from": "minimist@0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" - } - } - }, - "promise": { - "version": "7.1.1", - "from": "promise@>=7.1.1 <8.0.0", - "resolved": "https://registry.npmjs.org/promise/-/promise-7.1.1.tgz", - "dependencies": { - "asap": { - "version": "2.0.3", - "from": "asap@>=2.0.3 <2.1.0", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.3.tgz" - } - } - }, - "source-map": { - "version": "0.5.3", - "from": "source-map@>=0.5.3 <0.6.0", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.3.tgz" - } - } - }, - "postcss": { - "version": "5.0.19", - "from": "postcss@>=5.0.13 <6.0.0", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.0.19.tgz", - "dependencies": { - "supports-color": { - "version": "3.1.2", - "from": "supports-color@>=3.1.2 <4.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.1.2.tgz", - "dependencies": { - "has-flag": { - "version": "1.0.0", - "from": "has-flag@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz" - } - } - }, - "source-map": { - "version": "0.5.3", - "from": "source-map@>=0.5.1 <0.6.0", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.3.tgz" - }, - "js-base64": { - "version": "2.1.9", - "from": "js-base64@>=2.1.9 <3.0.0", - "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.1.9.tgz" - } - } - }, - "postcss-value-parser": { - "version": "3.3.0", - "from": "postcss-value-parser@>=3.2.3 <4.0.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz" - }, - "querystring": { - "version": "0.2.0", - "from": "querystring@>=0.2.0 <0.3.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz" - }, - "request": { - "version": "2.69.0", - "from": "request@>=2.67.0 <3.0.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.69.0.tgz", - "dependencies": { - "aws-sign2": { - "version": "0.6.0", - "from": "aws-sign2@>=0.6.0 <0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz" - }, - "aws4": { - "version": "1.3.2", - "from": "aws4@>=1.2.1 <2.0.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.3.2.tgz", - "dependencies": { - "lru-cache": { - "version": "4.0.0", - "from": "lru-cache@>=4.0.0 <5.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.0.0.tgz", - "dependencies": { - "pseudomap": { - "version": "1.0.2", - "from": "pseudomap@>=1.0.1 <2.0.0", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz" - }, - "yallist": { - "version": "2.0.0", - "from": "yallist@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.0.0.tgz" - } - } - } - } - }, - "bl": { - "version": "1.0.3", - "from": "bl@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-1.0.3.tgz", - "dependencies": { - "readable-stream": { - "version": "2.0.5", - "from": "readable-stream@>=2.0.5 <2.1.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.5.tgz", - "dependencies": { - "core-util-is": { - "version": "1.0.2", - "from": "core-util-is@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz" - }, - "inherits": { - "version": "2.0.1", - "from": "inherits@>=2.0.1 <2.1.0", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" - }, - "isarray": { - "version": "0.0.1", - "from": "isarray@0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" - }, - "process-nextick-args": { - "version": "1.0.6", - "from": "process-nextick-args@>=1.0.6 <1.1.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.6.tgz" - }, - "string_decoder": { - "version": "0.10.31", - "from": "string_decoder@>=0.10.0 <0.11.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" - }, - "util-deprecate": { - "version": "1.0.2", - "from": "util-deprecate@>=1.0.1 <1.1.0", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" - } - } - } - } - }, - "caseless": { - "version": "0.11.0", - "from": "caseless@>=0.11.0 <0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.11.0.tgz" - }, - "combined-stream": { - "version": "1.0.5", - "from": "combined-stream@>=1.0.5 <1.1.0", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", - "dependencies": { - "delayed-stream": { - "version": "1.0.0", - "from": "delayed-stream@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" - } - } - }, - "extend": { - "version": "3.0.0", - "from": "extend@>=3.0.0 <3.1.0", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.0.tgz" - }, - "forever-agent": { - "version": "0.6.1", - "from": "forever-agent@>=0.6.1 <0.7.0", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz" - }, - "form-data": { - "version": "1.0.0-rc3", - "from": "form-data@>=1.0.0-rc3 <1.1.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-1.0.0-rc3.tgz", - "dependencies": { - "async": { - "version": "1.5.2", - "from": "async@>=1.4.0 <2.0.0", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz" - } - } - }, - "har-validator": { - "version": "2.0.6", - "from": "har-validator@>=2.0.6 <2.1.0", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-2.0.6.tgz", - "dependencies": { - "chalk": { - "version": "1.1.1", - "from": "chalk@>=1.1.1 <2.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.1.tgz", - "dependencies": { - "ansi-styles": { - "version": "2.2.0", - "from": "ansi-styles@>=2.1.0 <3.0.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.0.tgz", - "dependencies": { - "color-convert": { - "version": "1.0.0", - "from": "color-convert@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.0.0.tgz" - } - } - }, - "escape-string-regexp": { - "version": "1.0.5", - "from": "escape-string-regexp@>=1.0.2 <2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" - }, - "has-ansi": { - "version": "2.0.0", - "from": "has-ansi@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "dependencies": { - "ansi-regex": { - "version": "2.0.0", - "from": "ansi-regex@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.0.0.tgz" - } - } - }, - "strip-ansi": { - "version": "3.0.1", - "from": "strip-ansi@>=3.0.0 <4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "dependencies": { - "ansi-regex": { - "version": "2.0.0", - "from": "ansi-regex@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.0.0.tgz" - } - } - }, - "supports-color": { - "version": "2.0.0", - "from": "supports-color@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz" - } - } - }, - "commander": { - "version": "2.9.0", - "from": "commander@>=2.9.0 <3.0.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", - "dependencies": { - "graceful-readlink": { - "version": "1.0.1", - "from": "graceful-readlink@>=1.0.0", - "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz" - } - } - }, - "is-my-json-valid": { - "version": "2.13.1", - "from": "is-my-json-valid@>=2.12.4 <3.0.0", - "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.13.1.tgz", - "dependencies": { - "generate-function": { - "version": "2.0.0", - "from": "generate-function@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz" - }, - "generate-object-property": { - "version": "1.2.0", - "from": "generate-object-property@>=1.1.0 <2.0.0", - "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz", - "dependencies": { - "is-property": { - "version": "1.0.2", - "from": "is-property@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz" - } - } - }, - "jsonpointer": { - "version": "2.0.0", - "from": "jsonpointer@2.0.0", - "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-2.0.0.tgz" - }, - "xtend": { - "version": "4.0.1", - "from": "xtend@>=4.0.0 <5.0.0", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz" - } - } - }, - "pinkie-promise": { - "version": "2.0.0", - "from": "pinkie-promise@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.0.tgz", - "dependencies": { - "pinkie": { - "version": "2.0.4", - "from": "pinkie@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz" - } - } - } - } - }, - "hawk": { - "version": "3.1.3", - "from": "hawk@>=3.1.0 <3.2.0", - "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", - "dependencies": { - "hoek": { - "version": "2.16.3", - "from": "hoek@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz" - }, - "boom": { - "version": "2.10.1", - "from": "boom@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz" - }, - "cryptiles": { - "version": "2.0.5", - "from": "cryptiles@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz" - }, - "sntp": { - "version": "1.0.9", - "from": "sntp@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz" - } - } - }, - "http-signature": { - "version": "1.1.1", - "from": "http-signature@>=1.1.0 <1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", - "dependencies": { - "assert-plus": { - "version": "0.2.0", - "from": "assert-plus@>=0.2.0 <0.3.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz" - }, - "jsprim": { - "version": "1.2.2", - "from": "jsprim@>=1.2.2 <2.0.0", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.2.2.tgz", - "dependencies": { - "extsprintf": { - "version": "1.0.2", - "from": "extsprintf@1.0.2", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.0.2.tgz" - }, - "json-schema": { - "version": "0.2.2", - "from": "json-schema@0.2.2", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.2.tgz" - }, - "verror": { - "version": "1.3.6", - "from": "verror@1.3.6", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.3.6.tgz" - } - } - }, - "sshpk": { - "version": "1.7.4", - "from": "sshpk@>=1.7.0 <2.0.0", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.7.4.tgz", - "dependencies": { - "asn1": { - "version": "0.2.3", - "from": "asn1@>=0.2.3 <0.3.0", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz" - }, - "dashdash": { - "version": "1.13.0", - "from": "dashdash@>=1.10.1 <2.0.0", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.13.0.tgz", - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "from": "assert-plus@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" - } - } - }, - "jsbn": { - "version": "0.1.0", - "from": "jsbn@>=0.1.0 <0.2.0", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.0.tgz" - }, - "tweetnacl": { - "version": "0.14.1", - "from": "tweetnacl@>=0.13.0 <1.0.0", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.1.tgz" - }, - "jodid25519": { - "version": "1.0.2", - "from": "jodid25519@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/jodid25519/-/jodid25519-1.0.2.tgz" - }, - "ecc-jsbn": { - "version": "0.1.1", - "from": "ecc-jsbn@>=0.0.1 <1.0.0", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz" - } - } - } - } - }, - "is-typedarray": { - "version": "1.0.0", - "from": "is-typedarray@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz" - }, - "isstream": { - "version": "0.1.2", - "from": "isstream@>=0.1.2 <0.2.0", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz" - }, - "json-stringify-safe": { - "version": "5.0.1", - "from": "json-stringify-safe@>=5.0.1 <5.1.0", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz" - }, - "mime-types": { - "version": "2.1.10", - "from": "mime-types@>=2.1.7 <2.2.0", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.10.tgz", - "dependencies": { - "mime-db": { - "version": "1.22.0", - "from": "mime-db@>=1.22.0 <1.23.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.22.0.tgz" - } - } - }, - "node-uuid": { - "version": "1.4.7", - "from": "node-uuid@>=1.4.7 <1.5.0", - "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.7.tgz" - }, - "oauth-sign": { - "version": "0.8.1", - "from": "oauth-sign@>=0.8.0 <0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.1.tgz" - }, - "qs": { - "version": "6.0.2", - "from": "qs@>=6.0.2 <6.1.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.0.2.tgz" - }, - "stringstream": { - "version": "0.0.5", - "from": "stringstream@>=0.0.4 <0.1.0", - "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz" - }, - "tough-cookie": { - "version": "2.2.2", - "from": "tough-cookie@>=2.2.0 <2.3.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.2.2.tgz" - }, - "tunnel-agent": { - "version": "0.4.2", - "from": "tunnel-agent@>=0.4.1 <0.5.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.2.tgz" - } - } - } - } } } } diff --git a/package.json b/package.json index 8b00ea28..100e3667 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "windshaft-cartodb", - "version": "2.27.0", + "version": "2.28.1", "description": "A map tile server for CartoDB", "keywords": [ "cartodb" @@ -38,7 +38,8 @@ "lru-cache": "2.6.5", "lzma": "~1.3.7", "log4js": "https://github.com/CartoDB/log4js-node/tarball/cdb", - "cartodb-query-tables": "~0.1.0" + "cartodb-query-tables": "~0.1.0", + "turbo-cartocss": "0.4.0" }, "devDependencies": { "istanbul": "~0.3.6", diff --git a/test/acceptance/turbo-cartocss-anonymous-maps.js b/test/acceptance/turbo-cartocss-anonymous-maps.js new file mode 100644 index 00000000..32134d94 --- /dev/null +++ b/test/acceptance/turbo-cartocss-anonymous-maps.js @@ -0,0 +1,128 @@ +var assert = require('../support/assert'); +var TestClient = require('../support/test-client'); + +var IMAGE_TOLERANCE_PER_MIL = 20; + +function imageCompareFn(fixture, done) { + return function(err, res, image) { + assert.ok(!err, err); + assert.imageIsSimilarToFile(image, './test/fixtures/' + fixture, IMAGE_TOLERANCE_PER_MIL, done); + }; +} + +function makeMapconfig(cartocss) { + return { + "version": "1.4.0", + "layers": [ + { + "type": 'mapnik', + "options": { + "cartocss_version": '2.3.0', + "sql": [ + 'SELECT test_table.*, _prices.price FROM test_table JOIN (' + + ' SELECT 1 AS cartodb_id, 10.00 AS price', + ' UNION', + ' SELECT 2, 10.50', + ' UNION', + ' SELECT 3, 11.00', + ' UNION', + ' SELECT 4, 12.00', + ' UNION', + ' SELECT 5, 21.00', + ') _prices ON _prices.cartodb_id = test_table.cartodb_id' + ].join('\n'), + "cartocss": cartocss + } + } + ] + }; +} + +describe('turbo-cartocss for anonymous maps', function() { + describe('parsing ramp function with colorbrewer for greens and mapnik renderer', function () { + beforeEach(function () { + var turboCartocss = '#layer { marker-fill: ramp([price], colorbrewer(Greens)); }'; + this.testClient = new TestClient(makeMapconfig(turboCartocss)); + }); + + afterEach(function (done) { + this.testClient.drain(done); + }); + + it('should get a tile with turbo-cartocss parsed properly', function (done) { + var fixturePath = 'test_turbo_cartocss_greens_13_4011_3088.png'; + this.testClient.getTile(13, 4011, 3088, imageCompareFn(fixturePath, done)); + }); + }); + + describe('parsing ramp function with colorbrewer for reds and mapnik renderer', function () { + beforeEach(function () { + var turboCartocss = '#layer { marker-fill: ramp([price], colorbrewer(Reds)); }'; + this.testClient = new TestClient(makeMapconfig(turboCartocss)); + }); + + afterEach(function (done) { + this.testClient.drain(done); + }); + + it('should get a tile with turbo-cartocss parsed properly', function (done) { + var fixtureFileName = 'test_turbo_cartocss_reds_13_4011_3088.png'; + this.testClient.getTile(13, 4011, 3088, imageCompareFn(fixtureFileName, done)); + }); + }); + + describe('parsing ramp function with colorbrewer for greens and toque renderer', function () { + var mapConfig = { + version: '1.2.0', + layers: [ + { + type: 'torque', + options: { + sql: "SELECT * FROM populated_places_simple_reduced where the_geom" + + " && ST_MakeEnvelope(-90, 0, 90, 65)", + cartocss: [ + 'Map {', + ' buffer-size:0;', + ' -torque-frame-count:1;', + ' -torque-animation-duration:30;', + ' -torque-time-attribute:"cartodb_id";', + ' -torque-aggregation-function:"count(cartodb_id)";', + ' -torque-resolution:1;', + ' -torque-data-aggregation:linear;', + '};', + '#populated_places_simple_reduced {', + ' comp-op: multiply;', + ' marker-fill-opacity: 1;', + ' marker-line-color: #FFF;', + ' marker-line-width: 0;', + ' marker-line-opacity: 1;', + ' marker-type: rectangle;', + ' marker-width: 3;', + ' marker-fill: ramp([pop_max], colorbrewer(Greens));', + '};' + ].join(' '), + cartocss_version: '2.3.0' + } + } + ] + }; + + beforeEach(function () { + this.testClient = new TestClient(mapConfig); + }); + + afterEach(function (done) { + this.testClient.drain(done); + }); + + it('should get a tile with turbo-cartocss parsed properly', function (done) { + var z = 2; + var x = 2; + var y = 1; + + var pngFixture = 'torque/populated_places_simple_reduced-turbo-cartocss-' + [z, x, y].join('.') + '.png'; + + this.testClient.getTile(z, x, y, { layers: 0, format: 'torque.png' }, imageCompareFn(pngFixture, done)); + }); + }); +}); diff --git a/test/acceptance/turbo-cartocss-named-maps.js b/test/acceptance/turbo-cartocss-named-maps.js new file mode 100644 index 00000000..3c90a973 --- /dev/null +++ b/test/acceptance/turbo-cartocss-named-maps.js @@ -0,0 +1,236 @@ +var assert = require('../support/assert'); +var step = require('step'); +var LayergroupToken = require('../../lib/cartodb/models/layergroup_token'); +var testHelper = require(__dirname + '/../support/test_helper'); +var CartodbWindshaft = require(__dirname + '/../../lib/cartodb/server'); +var serverOptions = require(__dirname + '/../../lib/cartodb/server_options'); +var server = new CartodbWindshaft(serverOptions); +var mapnik = require('windshaft').mapnik; +var IMAGE_TOLERANCE_PER_MIL = 10; + +describe('turbo-cartocss for named maps', function() { + + var keysToDelete; + + beforeEach(function() { + keysToDelete = {}; + }); + + afterEach(function(done) { + testHelper.deleteRedisKeys(keysToDelete, done); + }); + + var templateId = 'turbo-cartocss-template-1'; + + var template = { + version: '0.0.1', + name: templateId, + auth: { method: 'open' }, + placeholders: { + color: { + type: "css_color", + default: "Reds" + } + }, + layergroup: { + version: '1.0.0', + layers: [{ + options: { + sql: [ + 'SELECT test_table.*, _prices.price FROM test_table JOIN (' + + ' SELECT 1 AS cartodb_id, 10.00 AS price', + ' UNION', + ' SELECT 2, 10.50', + ' UNION', + ' SELECT 3, 11.00', + ' UNION', + ' SELECT 4, 12.00', + ' UNION', + ' SELECT 5, 21.00', + ') _prices ON _prices.cartodb_id = test_table.cartodb_id' + ].join('\n'), + cartocss: [ + '#layer {', + ' marker-fill: ramp([price], colorbrewer(<%= color %>));', + ' marker-allow-overlap:true;', + '}' + ].join('\n'), + cartocss_version: '2.0.2' + } + } + ] + } + }; + + var templateParamsReds = { color: 'Reds' }; + var templateParamsBlues = { color: 'Blues' }; + + it('should create a template with turbo-cartocss parsed properly', function (done) { + step( + function postTemplate() { + var next = this; + + assert.response(server, { + url: '/api/v1/map/named?api_key=1234', + method: 'POST', + headers: { host: 'localhost', 'Content-Type': 'application/json' }, + data: JSON.stringify(template) + }, {}, + function (res, err) { + next(err, res); + }); + }, + function checkTemplate(err, res) { + assert.ifError(err); + assert.equal(res.statusCode, 200); + assert.deepEqual(JSON.parse(res.body), { + template_id: templateId + }); + + return null; + }, + function instantiateTemplateWithReds(err) { + assert.ifError(err); + + var next = this; + assert.response(server, { + url: '/api/v1/map/named/' + templateId, + method: 'POST', + headers: { + host: 'localhost', + 'Content-Type': 'application/json' + }, + data: JSON.stringify(templateParamsReds) + }, {}, + function(res, err) { + return next(err, res); + }); + }, + function checkInstanciationWithReds(err, res) { + assert.ifError(err); + + assert.equal(res.statusCode, 200); + + var parsedBody = JSON.parse(res.body); + + keysToDelete['map_cfg|' + LayergroupToken.parse(parsedBody.layergroupid).token] = 0; + keysToDelete['user:localhost:mapviews:global'] = 5; + + assert.ok(parsedBody.layergroupid); + assert.ok(parsedBody.last_updated); + + return parsedBody.layergroupid; + }, + function requestTileReds(err, layergroupId) { + assert.ifError(err); + + var next = this; + + assert.response(server, { + url: '/api/v1/map/' + layergroupId + '/0/0/0.png', + method: 'GET', + headers: { host: 'localhost' }, + encoding: 'binary' + }, {}, + function(res, err) { + next(err, res); + }); + }, + function checkTileReds(err, res) { + assert.ifError(err); + + var next = this; + + assert.equal(res.statusCode, 200); + assert.equal(res.headers['content-type'], 'image/png'); + + var fixturePath = './test/fixtures/turbo-cartocss-named-maps-reds.png'; + var image = mapnik.Image.fromBytes(new Buffer(res.body, 'binary')); + + assert.imageIsSimilarToFile(image, fixturePath, IMAGE_TOLERANCE_PER_MIL, next); + }, + function instantiateTemplateWithBlues(err) { + assert.ifError(err); + + var next = this; + assert.response(server, { + url: '/api/v1/map/named/' + templateId, + method: 'POST', + headers: { + host: 'localhost', + 'Content-Type': 'application/json' + }, + data: JSON.stringify(templateParamsBlues) + }, {}, + function(res, err) { + return next(err, res); + }); + }, + function checkInstanciationWithBlues(err, res) { + assert.ifError(err); + assert.equal(res.statusCode, 200); + + var parsedBody = JSON.parse(res.body); + + keysToDelete['map_cfg|' + LayergroupToken.parse(parsedBody.layergroupid).token] = 0; + keysToDelete['user:localhost:mapviews:global'] = 5; + + assert.ok(parsedBody.layergroupid); + assert.ok(parsedBody.last_updated); + + return parsedBody.layergroupid; + }, + function requestTileBlues(err, layergroupId) { + assert.ifError(err); + + var next = this; + + assert.response(server, { + url: '/api/v1/map/' + layergroupId + '/0/0/0.png', + method: 'GET', + headers: { host: 'localhost' }, + encoding: 'binary' + }, {}, + function(res, err) { + next(err, res); + }); + }, + function checkTileBlues(err, res) { + assert.ifError(err); + + var next = this; + + assert.equal(res.statusCode, 200); + assert.equal(res.headers['content-type'], 'image/png'); + + var fixturePath = './test/fixtures/turbo-cartocss-named-maps-blues.png'; + var image = mapnik.Image.fromBytes(new Buffer(res.body, 'binary')); + + assert.imageIsSimilarToFile(image, fixturePath, IMAGE_TOLERANCE_PER_MIL, next); + }, + function deleteTemplate(err) { + assert.ifError(err); + + var next = this; + + assert.response(server, { + url: '/api/v1/map/named/' + templateId + '?api_key=1234', + method: 'DELETE', + headers: { host: 'localhost' } + }, {}, function (res, err) { + next(err, res); + }); + }, + function checkDeleteTemplate(err, res) { + assert.ifError(err); + assert.equal(res.statusCode, 204); + assert.ok(!res.body); + + return null; + }, + function finish(err) { + done(err); + } + ); + }); +}); diff --git a/test/acceptance/turbo-cartocss-regressions.js b/test/acceptance/turbo-cartocss-regressions.js new file mode 100644 index 00000000..52d55b35 --- /dev/null +++ b/test/acceptance/turbo-cartocss-regressions.js @@ -0,0 +1,75 @@ +require('../support/test_helper'); + +var assert = require('../support/assert'); +var TestClient = require('../support/test-client'); + +function makeMapconfig(cartocss) { + return { + "version": "1.4.0", + "layers": [ + { + "type": 'mapnik', + "options": { + "cartocss_version": '2.3.0', + "sql": [ + 'SELECT test_table.*, _prices.price FROM test_table JOIN (' + + ' SELECT 1 AS cartodb_id, 10.00 AS price', + ' UNION', + ' SELECT 2, 10.50', + ' UNION', + ' SELECT 3, 11.00', + ' UNION', + ' SELECT 4, 12.00', + ' UNION', + ' SELECT 5, 21.00', + ') _prices ON _prices.cartodb_id = test_table.cartodb_id' + ].join('\n'), + "cartocss": cartocss + } + } + ] + }; +} + +describe('turbo-cartocss regressions', function() { + + var cartocss = [ + "/** simple visualization */", + "", + "Map {", + " buffer-size: 256;", + "}", + "", + "#county_points_with_population{", + " marker-fill-opacity: 0.1;", + " marker-line-color:#FFFFFF;//#CF1C90;", + " marker-line-width: 0;", + " marker-line-opacity: 0.3;", + " marker-placement: point;", + " marker-type: ellipse;", + " //marker-comp-op: overlay;", + " marker-width: [price];", + " [zoom=5]{marker-width: [price]*2;}", + " [zoom=6]{marker-width: [price]*4;}", + " marker-fill: #000000;", + " marker-allow-overlap: true;", + " ", + "", + "}" + ].join('\n'); + + beforeEach(function () { + this.testClient = new TestClient(makeMapconfig(cartocss)); + }); + + afterEach(function (done) { + this.testClient.drain(done); + }); + + it('should accept // comments', function(done) { + this.testClient.getTile(0, 0, 0, function(err) { + assert.ok(!err, err); + done(); + }); + }); +}); diff --git a/test/fixtures/test_turbo_cartocss_greens_13_4011_3088.png b/test/fixtures/test_turbo_cartocss_greens_13_4011_3088.png new file mode 100644 index 00000000..062352d2 Binary files /dev/null and b/test/fixtures/test_turbo_cartocss_greens_13_4011_3088.png differ diff --git a/test/fixtures/test_turbo_cartocss_reds_13_4011_3088.png b/test/fixtures/test_turbo_cartocss_reds_13_4011_3088.png new file mode 100644 index 00000000..a8c93799 Binary files /dev/null and b/test/fixtures/test_turbo_cartocss_reds_13_4011_3088.png differ diff --git a/test/fixtures/torque/populated_places_simple_reduced-turbo-cartocss-2.2.1.png b/test/fixtures/torque/populated_places_simple_reduced-turbo-cartocss-2.2.1.png new file mode 100644 index 00000000..59f0d5c1 Binary files /dev/null and b/test/fixtures/torque/populated_places_simple_reduced-turbo-cartocss-2.2.1.png differ diff --git a/test/fixtures/turbo-cartocss-named-maps-blues.png b/test/fixtures/turbo-cartocss-named-maps-blues.png new file mode 100644 index 00000000..79f0d5d4 Binary files /dev/null and b/test/fixtures/turbo-cartocss-named-maps-blues.png differ diff --git a/test/fixtures/turbo-cartocss-named-maps-reds.png b/test/fixtures/turbo-cartocss-named-maps-reds.png new file mode 100644 index 00000000..65664285 Binary files /dev/null and b/test/fixtures/turbo-cartocss-named-maps-reds.png differ diff --git a/test/support/prepare_db.sh b/test/support/prepare_db.sh index f573e819..f03d52de 100755 --- a/test/support/prepare_db.sh +++ b/test/support/prepare_db.sh @@ -80,7 +80,7 @@ if test x"$PREPARE_PGSQL" = xyes; then cat sql/_CDB_QueryStatements.sql | psql -v ON_ERROR_STOP=1 ${TEST_DB} || exit 1 - SQL_SCRIPTS='CDB_QueryTables CDB_CartodbfyTable CDB_TableMetadata CDB_ForeignTable CDB_UserTables CDB_ColumnNames CDB_ZoomFromScale CDB_Overviews' + SQL_SCRIPTS='CDB_QueryTables CDB_CartodbfyTable CDB_TableMetadata CDB_ForeignTable CDB_UserTables CDB_ColumnNames CDB_ZoomFromScale CDB_Overviews CDB_QuantileBins CDB_JenksBins CDB_HeadsTailsBins CDB_EqualIntervalBins' for i in ${SQL_SCRIPTS} do curl -L -s https://github.com/CartoDB/cartodb-postgresql/raw/master/scripts-available/$i.sql -o sql/$i.sql diff --git a/test/support/test-client.js b/test/support/test-client.js index 1a29f564..02128e36 100644 --- a/test/support/test-client.js +++ b/test/support/test-client.js @@ -168,7 +168,8 @@ TestClient.prototype.getTile = function(z, x, y, params, callback) { url = '/api/v1/map/' + layergroupId + '/'; var layers = params.layers; - if (!!layers) { + + if (layers !== undefined) { layers = Array.isArray(layers) ? layers : [layers]; url += layers.join(',') + '/'; } @@ -190,7 +191,6 @@ TestClient.prototype.getTile = function(z, x, y, params, callback) { } }; - var expectedResponse = { status: 200, headers: { @@ -198,7 +198,7 @@ TestClient.prototype.getTile = function(z, x, y, params, callback) { } }; - var isPng = format === 'png'; + var isPng = format === 'png' || format === 'torque.png'; if (isPng) { request.encoding = 'binary';