Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
47217909f6 | ||
|
|
3a0a366230 |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -8,6 +8,6 @@ tools/munin/windshaft.conf
|
||||
logs/
|
||||
pids/
|
||||
redis.pid
|
||||
*.log
|
||||
test.log
|
||||
npm-debug.log
|
||||
coverage/
|
||||
.DS_Store
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
"debug" : false, // true: Allow debugger statements e.g. browser breakpoints.
|
||||
// "eqnull" : false, // true: Tolerate use of `== null`
|
||||
// "es5" : false, // true: Allow ES5 syntax (ex: getters and setters)
|
||||
"esnext" : true, // true: Allow ES.next (ES6) syntax (ex: `const`)
|
||||
// "esnext" : false, // true: Allow ES.next (ES6) syntax (ex: `const`)
|
||||
// "moz" : false, // true: Allow Mozilla specific syntax (extends and overrides esnext features)
|
||||
// // (ex: `for each`, multiple try/catch, function expression…)
|
||||
// "evil" : false, // true: Tolerate use of `eval` and `new Function()`
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
1. Test (make clean all check), fix if broken before proceeding
|
||||
2. Ensure proper version in package.json
|
||||
3. Ensure NEWS section exists for the new version, review it, add release date
|
||||
4. If there are modified dependencies in package.json, update them with `yarn upgrade {{package_name}}@{{version}}`
|
||||
4. Recreate yarn.lock with: `yarn upgrade`
|
||||
5. Commit package.json, yarn.lock, NEWS
|
||||
6. git tag -a Major.Minor.Patch # use NEWS section as content
|
||||
7. Stub NEWS/package for next version
|
||||
|
||||
136
NEWS.md
136
NEWS.md
@@ -1,140 +1,10 @@
|
||||
# Changelog
|
||||
|
||||
## 3.12.2
|
||||
Released 2017-08-16
|
||||
|
||||
Bug fixes:
|
||||
- Polygon count problems #725.
|
||||
|
||||
|
||||
## 3.12.1
|
||||
Released 2017-08-13
|
||||
- Upgrades cartodb-psql to [0.10.1](https://github.com/CartoDB/node-cartodb-psql/releases/tag/0.10.1).
|
||||
- Upgrades windshaft to [3.3.1](https://github.com/CartoDB/windshaft/releases/tag/3.3.1).
|
||||
- Upgrades camshaft to [0.55.7](https://github.com/CartoDB/camshaft/releases/tag/0.55.7).
|
||||
|
||||
|
||||
## 3.12.0
|
||||
Released 2017-08-10
|
||||
## 3.6.7
|
||||
Released 2017-09-01
|
||||
|
||||
Announcements:
|
||||
- Apply max tile response time for requests to layergoup, tiles, static maps, attributes and dataviews services #717.
|
||||
- Upgrades windshaft to [3.3.0](https://github.com/CartoDB/windshaft/releases/tag/3.3.0).
|
||||
- Upgrades cartodb-redis to [0.14.0](https://github.com/CartoDB/node-cartodb-redis/releases/tag/0.14.0).
|
||||
|
||||
|
||||
## 3.11.0
|
||||
Released 2017-08-08
|
||||
|
||||
Announcements:
|
||||
- Allow to override with any aggregation for histograms instantiated w/o aggregation.
|
||||
|
||||
Bug fixes:
|
||||
- Apply timezone after truncating the minimun date for each bin to calculate timestamps in time-series.
|
||||
- Support timestamp with timezones to calculate the number of bins in time-series.
|
||||
- Fixed issue related to name collision while building time-series query.
|
||||
|
||||
|
||||
## 3.10.1
|
||||
Released 2017-08-04
|
||||
|
||||
Bug fixes:
|
||||
- Exclude Infinities & NaNs from ramps #719.
|
||||
- Fixed issue in time-series when aggregation starts at 1970-01-01 (epoch) #720.
|
||||
|
||||
|
||||
## 3.10.0
|
||||
Released 2017-08-03
|
||||
|
||||
Announcements:
|
||||
- Improve time-series dataview, now supports date aggregations (e.g: daily, weekly, monthly, etc.) and timezones (UTC by default) #698.
|
||||
- Support special numeric values (±Infinity, NaN) for json responses #706
|
||||
|
||||
|
||||
## 3.9.8
|
||||
Released 2017-07-21
|
||||
|
||||
- Upgrades windshaft to [3.2.2](https://github.com/CartoDB/windshaft/releases/tag/3.2.2).
|
||||
|
||||
|
||||
## 3.9.7
|
||||
Released 2017-07-20
|
||||
|
||||
Bug fixes:
|
||||
- Respond with 204 (No content) when vector tile has no data #712
|
||||
|
||||
Announcements:
|
||||
- Upgrades turbo-carto to [0.19.2](https://github.com/CartoDB/turbo-carto/releases/tag/0.19.2)
|
||||
|
||||
|
||||
## 3.9.6
|
||||
Released 2017-07-11
|
||||
|
||||
- Dataviews: support for aggregation in search results #708
|
||||
|
||||
|
||||
## 3.9.5
|
||||
Released 2017-06-27
|
||||
|
||||
- Dataviews: support special numeric values (±Infinity, NaN) #700
|
||||
|
||||
|
||||
## 3.9.4
|
||||
Released 2017-06-22
|
||||
|
||||
Announcements:
|
||||
- Upgrades camshaft to [0.55.6](https://github.com/CartoDB/camshaft/releases/tag/0.55.6).
|
||||
|
||||
## 3.9.3
|
||||
Released 2017-06-16
|
||||
|
||||
Announcements:
|
||||
- Upgrades camshaft to [0.55.5](https://github.com/CartoDB/camshaft/releases/tag/0.55.5).
|
||||
|
||||
## 3.9.2
|
||||
Released 2017-06-16
|
||||
|
||||
Announcements:
|
||||
- Upgrades camshaft to [0.55.4](https://github.com/CartoDB/camshaft/releases/tag/0.55.4).
|
||||
|
||||
## 3.9.1
|
||||
Released 2017-06-06
|
||||
|
||||
Announcements:
|
||||
- Upgrades camshaft to [0.55.3](https://github.com/CartoDB/camshaft/releases/tag/0.55.3).
|
||||
|
||||
|
||||
## 3.9.0
|
||||
Released 2017-05-31
|
||||
|
||||
Announcements:
|
||||
- Upgrades windshaft to [3.2.1](https://github.com/CartoDB/windshaft/releases/tag/3.2.1).
|
||||
- Add support to retrieve info about layer stats in map instantiation.
|
||||
- Upgrades camshaft to [0.55.2](https://github.com/CartoDB/camshaft/releases/tag/0.55.2).
|
||||
- Remove promise polyfill from turbo-carto adapter
|
||||
|
||||
|
||||
## 3.8.0
|
||||
Released 2017-05-22
|
||||
|
||||
Announcements:
|
||||
- Upgrades camshaft to [0.55.0](https://github.com/CartoDB/camshaft/releases/tag/0.55.0).
|
||||
- Upgrades turbo-carto to [0.19.1](https://github.com/CartoDB/turbo-carto/releases/tag/0.19.1)
|
||||
|
||||
|
||||
## 3.7.1
|
||||
Released 2017-05-18
|
||||
|
||||
Bug fixes:
|
||||
- Fix buffersize assignment when is not defined in requested mapconfig.
|
||||
|
||||
|
||||
## 3.7.0
|
||||
Released 2017-05-18
|
||||
|
||||
Announcements:
|
||||
- Manage multiple values of buffer-size for different formats
|
||||
- Upgrades windshaft to [3.2.0](https://github.com/CartoDB/windshaft/releases/tag/3.2.0).
|
||||
- Adding a new cartodb-psql fixing a pg vulnerability.
|
||||
|
||||
|
||||
## 3.6.6
|
||||
|
||||
@@ -324,7 +324,8 @@ var config = {
|
||||
// whether the affected tables for a given SQL must query directly postgresql or use the SQL API
|
||||
cdbQueryTablesFromPostgres: true,
|
||||
// whether in mapconfig is available stats & metadata for each layer
|
||||
layerStats: true
|
||||
layerMetadata: true
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -324,7 +324,7 @@ var config = {
|
||||
// whether the affected tables for a given SQL must query directly postgresql or use the SQL API
|
||||
cdbQueryTablesFromPostgres: true,
|
||||
// whether in mapconfig is available stats & metadata for each layer
|
||||
layerStats: false
|
||||
layerMetadata: false
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -324,7 +324,7 @@ var config = {
|
||||
// whether the affected tables for a given SQL must query directly postgresql or use the SQL API
|
||||
cdbQueryTablesFromPostgres: true,
|
||||
// whether in mapconfig is available stats & metadata for each layer
|
||||
layerStats: true
|
||||
layerMetadata: true
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -318,7 +318,7 @@ var config = {
|
||||
// whether the affected tables for a given SQL must query directly postgresql or use the SQL API
|
||||
cdbQueryTablesFromPostgres: true,
|
||||
// whether in mapconfig is available stats & metadata for each layer
|
||||
layerStats: true
|
||||
layerMetadata: true
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
var step = require('step');
|
||||
|
||||
/**
|
||||
*
|
||||
* @param metadataBackend
|
||||
@@ -15,65 +13,16 @@ function UserLimitsApi(metadataBackend, options) {
|
||||
|
||||
module.exports = UserLimitsApi;
|
||||
|
||||
UserLimitsApi.prototype.getRenderLimits = function (username, apiKey, callback) {
|
||||
UserLimitsApi.prototype.getRenderLimits = function (username, callback) {
|
||||
var self = this;
|
||||
|
||||
var limits = {
|
||||
cacheOnTimeout: self.options.limits.cacheOnTimeout || false,
|
||||
render: self.options.limits.render || 0
|
||||
};
|
||||
|
||||
self.getTimeoutRenderLimit(username, apiKey, function (err, timeoutRenderLimit) {
|
||||
this.metadataBackend.getTilerRenderLimit(username, function handleTilerLimits(err, renderLimit) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
if (timeoutRenderLimit && timeoutRenderLimit.render) {
|
||||
if (Number.isFinite(timeoutRenderLimit.render)) {
|
||||
limits.render = timeoutRenderLimit.render;
|
||||
}
|
||||
}
|
||||
|
||||
return callback(null, limits);
|
||||
return callback(null, {
|
||||
cacheOnTimeout: self.options.limits.cacheOnTimeout || false,
|
||||
render: renderLimit || self.options.limits.render || 0
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
UserLimitsApi.prototype.getTimeoutRenderLimit = function (username, apiKey, callback) {
|
||||
var self = this;
|
||||
|
||||
step(
|
||||
function isAuthorized() {
|
||||
var next = this;
|
||||
|
||||
if (!apiKey) {
|
||||
return next(null, false);
|
||||
}
|
||||
|
||||
self.metadataBackend.getUserMapKey(username, function (err, userApiKey) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
return next(null, userApiKey === apiKey);
|
||||
});
|
||||
},
|
||||
function getUserTimeoutRenderLimits(err, authorized) {
|
||||
var next = this;
|
||||
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
self.metadataBackend.getUserTimeoutRenderLimits(username, function (err, timeoutRenderLimit) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
next(null, {
|
||||
render: authorized ? timeoutRenderLimit.render : timeoutRenderLimit.renderPublic
|
||||
});
|
||||
});
|
||||
},
|
||||
callback
|
||||
);
|
||||
};
|
||||
|
||||
@@ -43,19 +43,53 @@ DataviewBackend.prototype.getDataview = function (mapConfigProvider, user, param
|
||||
ownFilter = !!ownFilter;
|
||||
|
||||
var query = (ownFilter) ? dataviewDefinition.sql.own_filter_on : dataviewDefinition.sql.own_filter_off;
|
||||
var sourceId = dataviewDefinition.source.id; // node.id
|
||||
var layer = _.find(mapConfig.obj().layers, function(l) {
|
||||
return l.options.source && (l.options.source.id === sourceId);
|
||||
});
|
||||
var queryRewriteData = layer && layer.options.query_rewrite_data;
|
||||
if (queryRewriteData && dataviewDefinition.node.type === 'source') {
|
||||
queryRewriteData = _.extend({}, queryRewriteData, {
|
||||
filters: dataviewDefinition.node.filters,
|
||||
unfiltered_query: dataviewDefinition.sql.own_filter_on
|
||||
});
|
||||
}
|
||||
|
||||
if (params.bbox) {
|
||||
var bboxFilter = new BBoxFilter({column: 'the_geom_webmercator', srid: 3857}, {bbox: params.bbox});
|
||||
query = bboxFilter.sql(query);
|
||||
if ( queryRewriteData ) {
|
||||
var bbox_filter_definition = {
|
||||
type: 'bbox',
|
||||
options: {
|
||||
column: 'the_geom_webmercator',
|
||||
srid: 3857
|
||||
},
|
||||
params: {
|
||||
bbox: params.bbox
|
||||
}
|
||||
};
|
||||
queryRewriteData = _.extend(queryRewriteData, { bbox_filter: bbox_filter_definition });
|
||||
}
|
||||
}
|
||||
|
||||
var queryRewriteData = getQueryRewriteData(mapConfig, dataviewDefinition, params);
|
||||
|
||||
var dataviewFactory = DataviewFactoryWithOverviews.getFactory(
|
||||
overviewsQueryRewriter, queryRewriteData, { bbox: params.bbox }
|
||||
);
|
||||
|
||||
var overrideParams = _.reduce(_.pick(params, 'start', 'end', 'bins'),
|
||||
function castNumbers(overrides, val, k) {
|
||||
if (!Number.isFinite(+val)) {
|
||||
throw new Error('Invalid number format for parameter \'' + k + '\'');
|
||||
}
|
||||
overrides[k] = +val;
|
||||
return overrides;
|
||||
},
|
||||
{ownFilter: ownFilter}
|
||||
);
|
||||
|
||||
var dataview = dataviewFactory.getDataview(query, dataviewDefinition);
|
||||
dataview.getResult(pg, getOverrideParams(params, ownFilter), this);
|
||||
dataview.getResult(pg, overrideParams, this);
|
||||
},
|
||||
function returnCallback(err, result) {
|
||||
return callback(err, result);
|
||||
@@ -63,56 +97,6 @@ DataviewBackend.prototype.getDataview = function (mapConfigProvider, user, param
|
||||
);
|
||||
};
|
||||
|
||||
function getQueryRewriteData(mapConfig, dataviewDefinition, params) {
|
||||
var sourceId = dataviewDefinition.source.id; // node.id
|
||||
var layer = _.find(mapConfig.obj().layers, function(l) {
|
||||
return l.options.source && (l.options.source.id === sourceId);
|
||||
});
|
||||
var queryRewriteData = layer && layer.options.query_rewrite_data;
|
||||
if (queryRewriteData && dataviewDefinition.node.type === 'source') {
|
||||
queryRewriteData = _.extend({}, queryRewriteData, {
|
||||
filters: dataviewDefinition.node.filters,
|
||||
unfiltered_query: dataviewDefinition.sql.own_filter_on
|
||||
});
|
||||
}
|
||||
|
||||
if (params.bbox && queryRewriteData) {
|
||||
var bbox_filter_definition = {
|
||||
type: 'bbox',
|
||||
options: {
|
||||
column: 'the_geom_webmercator',
|
||||
srid: 3857
|
||||
},
|
||||
params: {
|
||||
bbox: params.bbox
|
||||
}
|
||||
};
|
||||
queryRewriteData = _.extend(queryRewriteData, { bbox_filter: bbox_filter_definition });
|
||||
}
|
||||
|
||||
return queryRewriteData;
|
||||
}
|
||||
|
||||
function getOverrideParams(params, ownFilter) {
|
||||
var overrideParams = _.reduce(_.pick(params, 'start', 'end', 'bins', 'offset'),
|
||||
function castNumbers(overrides, val, k) {
|
||||
if (!Number.isFinite(+val)) {
|
||||
throw new Error('Invalid number format for parameter \'' + k + '\'');
|
||||
}
|
||||
overrides[k] = +val;
|
||||
return overrides;
|
||||
},
|
||||
{ownFilter: ownFilter}
|
||||
);
|
||||
|
||||
// validation will be delegated to the proper dataview
|
||||
if (params.aggregation !== undefined) {
|
||||
overrideParams.aggregation = params.aggregation;
|
||||
}
|
||||
|
||||
return overrideParams;
|
||||
}
|
||||
|
||||
DataviewBackend.prototype.search = function (mapConfigProvider, user, params, callback) {
|
||||
var dataviewName = params.dataviewName;
|
||||
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
function EmptyLayerStats(types) {
|
||||
this._types = types || {};
|
||||
}
|
||||
|
||||
EmptyLayerStats.prototype.is = function (type) {
|
||||
return this._types[type] ? this._types[type] : false;
|
||||
};
|
||||
|
||||
EmptyLayerStats.prototype.getStats =
|
||||
function (layer, dbConnection, callback) {
|
||||
setImmediate(function() {
|
||||
callback(null, {});
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = EmptyLayerStats;
|
||||
@@ -1,23 +0,0 @@
|
||||
var LayerStats = require('./layer-stats');
|
||||
var EmptyLayerStats = require('./empty-layer-stats');
|
||||
var MapnikLayerStats = require('./mapnik-layer-stats');
|
||||
var TorqueLayerStats = require('./torque-layer-stats');
|
||||
|
||||
module.exports = function LayerStatsFactory(type) {
|
||||
var layerStatsIterator = [];
|
||||
var selectedType = type || 'ALL';
|
||||
|
||||
if (selectedType === 'ALL') {
|
||||
layerStatsIterator.push(new EmptyLayerStats({ http: true, plain: true }));
|
||||
layerStatsIterator.push(new MapnikLayerStats());
|
||||
layerStatsIterator.push(new TorqueLayerStats());
|
||||
} else if (selectedType === 'mapnik') {
|
||||
layerStatsIterator.push(new EmptyLayerStats({ http: true, plain: true, torque: true }));
|
||||
layerStatsIterator.push(new MapnikLayerStats());
|
||||
} else if (selectedType === 'torque') {
|
||||
layerStatsIterator.push(new EmptyLayerStats({ http: true, plain: true, mapnik: true }));
|
||||
layerStatsIterator.push(new TorqueLayerStats());
|
||||
}
|
||||
|
||||
return new LayerStats(layerStatsIterator);
|
||||
};
|
||||
@@ -1,45 +0,0 @@
|
||||
var queue = require('queue-async');
|
||||
|
||||
function LayerStats(layerStatsIterator) {
|
||||
this.layerStatsIterator = layerStatsIterator;
|
||||
}
|
||||
|
||||
LayerStats.prototype.getStats = function (mapConfig, dbConnection, callback) {
|
||||
var self = this;
|
||||
var stats = [];
|
||||
|
||||
if (!mapConfig.getLayers().length) {
|
||||
return callback(null, stats);
|
||||
}
|
||||
var metaQueue = queue(mapConfig.getLayers().length);
|
||||
mapConfig.getLayers().forEach(function (layer, layerId) {
|
||||
var layerType = mapConfig.layerType(layerId);
|
||||
|
||||
for (var i = 0; i < self.layerStatsIterator.length; i++) {
|
||||
if (self.layerStatsIterator[i].is(layerType)) {
|
||||
var getStats = self.layerStatsIterator[i].getStats.bind(self.layerStatsIterator[i]);
|
||||
metaQueue.defer(getStats, layer, dbConnection);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
metaQueue.awaitAll(function (err, results) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
if (!results) {
|
||||
return callback(null, null);
|
||||
}
|
||||
|
||||
mapConfig.getLayers().forEach(function (layer, layerIndex) {
|
||||
stats[layerIndex] = results[layerIndex];
|
||||
});
|
||||
|
||||
return callback(err, stats);
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
module.exports = LayerStats;
|
||||
@@ -1,28 +0,0 @@
|
||||
var queryUtils = require('../../utils/query-utils');
|
||||
|
||||
function MapnikLayerStats () {
|
||||
this._types = {
|
||||
mapnik: true,
|
||||
cartodb: true
|
||||
};
|
||||
}
|
||||
|
||||
MapnikLayerStats.prototype.is = function (type) {
|
||||
return this._types[type] ? this._types[type] : false;
|
||||
};
|
||||
|
||||
MapnikLayerStats.prototype.getStats =
|
||||
function (layer, dbConnection, callback) {
|
||||
var queryRowCountSql = queryUtils.getQueryRowCount(layer.options.sql);
|
||||
// This query would gather stats for postgresql table if not exists
|
||||
dbConnection.query(queryRowCountSql, function (err, res) {
|
||||
if (err) {
|
||||
return callback(null, {estimatedFeatureCount: -1});
|
||||
} else {
|
||||
// We decided that the relation is 1 row == 1 feature
|
||||
return callback(null, {estimatedFeatureCount: res.rows[0].rows});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = MapnikLayerStats;
|
||||
@@ -1,16 +0,0 @@
|
||||
function TorqueLayerStats() {
|
||||
this._types = {
|
||||
torque: true
|
||||
};
|
||||
}
|
||||
|
||||
TorqueLayerStats.prototype.is = function (type) {
|
||||
return this._types[type] ? this._types[type] : false;
|
||||
};
|
||||
|
||||
TorqueLayerStats.prototype.getStats =
|
||||
function (layer, dbConnection, callback) {
|
||||
return callback(null, {});
|
||||
};
|
||||
|
||||
module.exports = TorqueLayerStats;
|
||||
@@ -1,16 +0,0 @@
|
||||
var layerStats = require('./layer-stats/factory');
|
||||
|
||||
function StatsBackend() {
|
||||
}
|
||||
|
||||
module.exports = StatsBackend;
|
||||
|
||||
StatsBackend.prototype.getStats = function(mapConfig, dbConnection, callback) {
|
||||
var enabledFeatures = global.environment.enabledFeatures;
|
||||
var layerStatsEnabled = enabledFeatures ? enabledFeatures.layerStats: false;
|
||||
if (layerStatsEnabled) {
|
||||
layerStats().getStats(mapConfig, dbConnection, callback);
|
||||
} else {
|
||||
return callback(null, []);
|
||||
}
|
||||
};
|
||||
@@ -296,7 +296,7 @@ TemplateMaps.prototype.delTemplate = function(owner, tpl_id, callback) {
|
||||
// @param callback function(err)
|
||||
//
|
||||
TemplateMaps.prototype.updTemplate = function(owner, tpl_id, template, callback) {
|
||||
|
||||
|
||||
var self = this;
|
||||
|
||||
template = templateDefaults(template);
|
||||
@@ -430,17 +430,13 @@ var _reNumber = /^([-+]?[\d\.]?\d+([eE][+-]?\d+)?)$/,
|
||||
_reCSSColorVal = /^#[0-9a-fA-F]{3,6}$/;
|
||||
|
||||
function _replaceVars (str, params) {
|
||||
// Construct regular expressions for each param
|
||||
//return _.template(str, params); // lazy way, possibly dangerous
|
||||
// Construct regular expressions for each param
|
||||
Object.keys(params).forEach(function(k) {
|
||||
str = str.replace(new RegExp("<%=\\s*" + k + "\\s*%>", "g"), params[k]);
|
||||
});
|
||||
return str;
|
||||
}
|
||||
|
||||
function isObject(val) {
|
||||
return ( _.isObject(val) && !_.isArray(val) && !_.isFunction(val));
|
||||
}
|
||||
|
||||
TemplateMaps.prototype.instance = function(template, params) {
|
||||
var all_params = {};
|
||||
var phold = template.placeholders || {};
|
||||
@@ -478,13 +474,6 @@ TemplateMaps.prototype.instance = function(template, params) {
|
||||
|
||||
// NOTE: we're deep-cloning the layergroup here
|
||||
var layergroup = JSON.parse(JSON.stringify(template.layergroup));
|
||||
|
||||
if (layergroup.buffersize && isObject(layergroup.buffersize)) {
|
||||
Object.keys(layergroup.buffersize).forEach(function(k) {
|
||||
layergroup.buffersize[k] = parseInt(_replaceVars(layergroup.buffersize[k], all_params), 10);
|
||||
});
|
||||
}
|
||||
|
||||
for (var i=0; i<layergroup.layers.length; ++i) {
|
||||
var lyropt = layergroup.layers[i].options;
|
||||
|
||||
|
||||
@@ -10,13 +10,7 @@ function createTemplate(method) {
|
||||
'max({{=it._column}}) max_val,',
|
||||
'avg({{=it._column}}) avg_val,',
|
||||
method,
|
||||
'FROM ({{=it._sql}}) _table_sql WHERE {{=it._column}} IS NOT NULL',
|
||||
'AND',
|
||||
' {{=it._column}} != \'infinity\'::float',
|
||||
'AND',
|
||||
' {{=it._column}} != \'-infinity\'::float',
|
||||
'AND',
|
||||
' {{=it._column}} != \'NaN\'::float'
|
||||
'FROM ({{=it._sql}}) _table_sql WHERE {{=it._column}} IS NOT NULL'
|
||||
].join('\n'));
|
||||
}
|
||||
|
||||
|
||||
@@ -17,8 +17,16 @@ var REQUEST_QUERY_PARAMS_WHITELIST = [
|
||||
'zoom',
|
||||
'lon',
|
||||
'lat',
|
||||
// analysis
|
||||
'filters' // json
|
||||
// widgets & filters
|
||||
'filters', // json
|
||||
'own_filter', // 0, 1
|
||||
'bbox', // w,s,e,n
|
||||
'bins', // number
|
||||
'start', // number
|
||||
'end', // number
|
||||
'column_type', // string
|
||||
// widgets search
|
||||
'q'
|
||||
];
|
||||
|
||||
function BaseController(authApi, pgConnection) {
|
||||
@@ -187,19 +195,12 @@ BaseController.prototype.send = function(req, res, body, status, headers) {
|
||||
|
||||
BaseController.prototype.sendError = function(req, res, err, label) {
|
||||
var allErrors = Array.isArray(err) ? err : [err];
|
||||
|
||||
allErrors = populateTimeoutErrors(allErrors);
|
||||
|
||||
label = label || 'UNKNOWN';
|
||||
err = allErrors[0] || new Error(label);
|
||||
allErrors[0] = err;
|
||||
|
||||
var statusCode = findStatusCode(err);
|
||||
|
||||
if (err.message === 'Tile does not exist' && req.params.format === 'mvt') {
|
||||
statusCode = 204;
|
||||
}
|
||||
|
||||
debug('[%s ERROR] -- %d: %s, %s', label, statusCode, err, err.stack);
|
||||
|
||||
// If a callback was requested, force status to 200
|
||||
@@ -224,18 +225,6 @@ function stripConnectionInfo(message) {
|
||||
.replace(/is the server.*encountered/im, 'encountered');
|
||||
}
|
||||
|
||||
var ERROR_INFO_TO_EXPOSE = {
|
||||
message: true,
|
||||
layer: true,
|
||||
type: true,
|
||||
analysis: true,
|
||||
subtype: true
|
||||
};
|
||||
|
||||
function shouldBeExposed (prop) {
|
||||
return !!ERROR_INFO_TO_EXPOSE[prop];
|
||||
}
|
||||
|
||||
function errorMessage(err) {
|
||||
// See https://github.com/Vizzuality/Windshaft-cartodb/issues/68
|
||||
var message = (_.isString(err) ? err : err.message) || 'Unknown error';
|
||||
@@ -254,7 +243,7 @@ function errorMessageWithContext(err) {
|
||||
|
||||
for (var prop in err) {
|
||||
// type & message are properties from Error's prototype and will be skipped
|
||||
if (err.hasOwnProperty(prop) && shouldBeExposed(prop)) {
|
||||
if (err.hasOwnProperty(prop)) {
|
||||
error[prop] = err[prop];
|
||||
}
|
||||
}
|
||||
@@ -296,38 +285,5 @@ function statusFromErrorMessage(errMsg) {
|
||||
statusCode = 404;
|
||||
}
|
||||
}
|
||||
|
||||
return statusCode;
|
||||
}
|
||||
|
||||
function isRenderTimeoutError (err) {
|
||||
return err.message === 'Render timed out';
|
||||
}
|
||||
|
||||
function isDatasourceTimeoutError (err) {
|
||||
return err.message && err.message.match(/canceling statement due to statement timeout/i);
|
||||
}
|
||||
|
||||
function isTimeoutError (err) {
|
||||
return isRenderTimeoutError(err) || isDatasourceTimeoutError(err);
|
||||
}
|
||||
|
||||
function populateTimeoutErrors (errors) {
|
||||
return errors.map(function (error) {
|
||||
if (isRenderTimeoutError(error)) {
|
||||
error.subtype = 'render';
|
||||
}
|
||||
|
||||
if (isDatasourceTimeoutError(error)) {
|
||||
error.subtype = 'datasource';
|
||||
}
|
||||
|
||||
if (isTimeoutError(error)) {
|
||||
error.message = 'You are over platform\'s limits. Please contact us to know more details';
|
||||
error.type = 'limit';
|
||||
error.http_status = 429;
|
||||
}
|
||||
|
||||
return error;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -79,51 +79,19 @@ LayergroupController.prototype.register = function(app) {
|
||||
|
||||
// Undocumented/non-supported API endpoint methods.
|
||||
// Use at your own peril.
|
||||
app.get(app.base_url_mapconfig +
|
||||
'/:token/dataview/:dataviewName', cors(), userMiddleware,
|
||||
this.dataview.bind(this));
|
||||
app.get(app.base_url_mapconfig +
|
||||
'/:token/:layer/widget/:dataviewName', cors(), userMiddleware,
|
||||
this.dataview.bind(this));
|
||||
|
||||
var allowedDataviewQueryParams = [
|
||||
'filters', // json
|
||||
'own_filter', // 0, 1
|
||||
'bbox', // w,s,e,n
|
||||
'start', // number
|
||||
'end', // number
|
||||
'column_type', // string
|
||||
'bins', // number
|
||||
'aggregation', //string
|
||||
'offset', // number
|
||||
'q' // widgets search
|
||||
];
|
||||
|
||||
app.get(
|
||||
app.base_url_mapconfig + '/:token/dataview/:dataviewName',
|
||||
cors(),
|
||||
userMiddleware,
|
||||
allowQueryParams(allowedDataviewQueryParams),
|
||||
this.dataview.bind(this)
|
||||
);
|
||||
|
||||
app.get(
|
||||
app.base_url_mapconfig + '/:token/:layer/widget/:dataviewName',
|
||||
cors(),
|
||||
userMiddleware,
|
||||
allowQueryParams(allowedDataviewQueryParams),
|
||||
this.dataview.bind(this)
|
||||
);
|
||||
|
||||
app.get(
|
||||
app.base_url_mapconfig + '/:token/dataview/:dataviewName/search',
|
||||
cors(),
|
||||
userMiddleware,
|
||||
allowQueryParams(allowedDataviewQueryParams),
|
||||
this.dataviewSearch.bind(this)
|
||||
);
|
||||
|
||||
app.get(
|
||||
app.base_url_mapconfig + '/:token/:layer/widget/:dataviewName/search',
|
||||
cors(),
|
||||
userMiddleware,
|
||||
allowQueryParams(allowedDataviewQueryParams),
|
||||
this.dataviewSearch.bind(this)
|
||||
);
|
||||
app.get(app.base_url_mapconfig +
|
||||
'/:token/dataview/:dataviewName/search', cors(), userMiddleware,
|
||||
this.dataviewSearch.bind(this));
|
||||
app.get(app.base_url_mapconfig +
|
||||
'/:token/:layer/widget/:dataviewName/search', cors(), userMiddleware,
|
||||
this.dataviewSearch.bind(this));
|
||||
|
||||
app.get(app.base_url_mapconfig +
|
||||
'/:token/analysis/node/:nodeId', cors(), userMiddleware,
|
||||
@@ -285,9 +253,7 @@ LayergroupController.prototype.finalizeGetTileOrGrid = function(err, req, res, t
|
||||
grid_json: true,
|
||||
json_torque: true,
|
||||
torque_json: true,
|
||||
png: true,
|
||||
png32: true,
|
||||
mvt: true
|
||||
png: true
|
||||
};
|
||||
|
||||
var formatStat = 'invalid';
|
||||
|
||||
@@ -20,7 +20,6 @@ var NamedMapsCacheEntry = require('../cache/model/named_maps_entry');
|
||||
var NamedMapMapConfigProvider = require('../models/mapconfig/provider/named-map-provider');
|
||||
var CreateLayergroupMapConfigProvider = require('../models/mapconfig/provider/create-layergroup-provider');
|
||||
|
||||
|
||||
/**
|
||||
* @param {AuthApi} authApi
|
||||
* @param {PgConnection} pgConnection
|
||||
@@ -31,12 +30,10 @@ var CreateLayergroupMapConfigProvider = require('../models/mapconfig/provider/cr
|
||||
* @param {UserLimitsApi} userLimitsApi
|
||||
* @param {LayergroupAffectedTables} layergroupAffectedTables
|
||||
* @param {MapConfigAdapter} mapConfigAdapter
|
||||
* @param {StatsBackend} statsBackend
|
||||
* @constructor
|
||||
*/
|
||||
function MapController(authApi, pgConnection, templateMaps, mapBackend, metadataBackend,
|
||||
surrogateKeysCache, userLimitsApi, layergroupAffectedTables, mapConfigAdapter,
|
||||
statsBackend) {
|
||||
surrogateKeysCache, userLimitsApi, layergroupAffectedTables, mapConfigAdapter) {
|
||||
|
||||
BaseController.call(this, authApi, pgConnection);
|
||||
|
||||
@@ -50,8 +47,6 @@ function MapController(authApi, pgConnection, templateMaps, mapBackend, metadata
|
||||
|
||||
this.mapConfigAdapter = mapConfigAdapter;
|
||||
this.resourceLocator = new ResourceLocator(global.environment);
|
||||
|
||||
this.statsBackend = statsBackend;
|
||||
}
|
||||
|
||||
util.inherits(MapController, BaseController);
|
||||
@@ -219,6 +214,7 @@ MapController.prototype.instantiateTemplate = function(req, res, prepareParamsFn
|
||||
|
||||
var mapConfigProvider;
|
||||
var mapConfig;
|
||||
|
||||
step(
|
||||
function setupParams(){
|
||||
self.req2params(req, this);
|
||||
@@ -253,9 +249,7 @@ MapController.prototype.instantiateTemplate = function(req, res, prepareParamsFn
|
||||
},
|
||||
function afterLayergroupCreate(err, layergroup) {
|
||||
assert.ifError(err);
|
||||
self.afterLayergroupCreate(req, res, mapConfig, layergroup,
|
||||
mapConfigProvider.analysesResults,
|
||||
this);
|
||||
self.afterLayergroupCreate(req, res, mapConfig, layergroup, mapConfigProvider.analysesResults, this);
|
||||
},
|
||||
function finishTemplateInstantiation(err, layergroup) {
|
||||
if (err) {
|
||||
@@ -278,8 +272,7 @@ MapController.prototype.instantiateTemplate = function(req, res, prepareParamsFn
|
||||
);
|
||||
};
|
||||
|
||||
MapController.prototype.afterLayergroupCreate =
|
||||
function(req, res, mapconfig, layergroup, analysesResults, callback) {
|
||||
MapController.prototype.afterLayergroupCreate = function(req, res, mapconfig, layergroup, analysesResults, callback) {
|
||||
var self = this;
|
||||
|
||||
var username = req.context.user;
|
||||
@@ -325,7 +318,6 @@ function(req, res, mapconfig, layergroup, analysesResults, callback) {
|
||||
|
||||
var dbName = req.params.dbname;
|
||||
var layergroupId = layergroup.layergroupid;
|
||||
var dbConnection;
|
||||
|
||||
step(
|
||||
function getPgConnection() {
|
||||
@@ -333,8 +325,7 @@ function(req, res, mapconfig, layergroup, analysesResults, callback) {
|
||||
},
|
||||
function getAffectedTablesAndLastUpdatedTime(err, connection) {
|
||||
assert.ifError(err);
|
||||
dbConnection = connection;
|
||||
QueryTables.getAffectedTablesFromQuery(dbConnection, sql.join(';'), this);
|
||||
QueryTables.getAffectedTablesFromQuery(connection, sql.join(';'), this);
|
||||
},
|
||||
function handleAffectedTablesAndLastUpdatedTime(err, result) {
|
||||
req.profiler.done('queryTablesAndLastUpdated');
|
||||
@@ -361,21 +352,6 @@ function(req, res, mapconfig, layergroup, analysesResults, callback) {
|
||||
|
||||
return null;
|
||||
},
|
||||
function fetchLayersStats(err) {
|
||||
assert.ifError(err);
|
||||
var next = this;
|
||||
self.statsBackend.getStats(mapconfig, dbConnection, function(err, layersStats) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
if (layersStats.length > 0) {
|
||||
layergroup.metadata.layers.forEach(function (layer, index) {
|
||||
layer.meta.stats = layersStats[index];
|
||||
});
|
||||
}
|
||||
return next();
|
||||
});
|
||||
},
|
||||
function finish(err) {
|
||||
done(err);
|
||||
}
|
||||
|
||||
@@ -5,32 +5,11 @@ var debug = require('debug')('windshaft:widget:aggregation');
|
||||
var dot = require('dot');
|
||||
dot.templateSettings.strip = false;
|
||||
|
||||
var filteredQueryTpl = dot.template([
|
||||
'filtered_source AS (',
|
||||
' SELECT *',
|
||||
' FROM ({{=it._query}}) _cdb_filtered_source',
|
||||
' {{?it._aggregationColumn && it._isFloatColumn}}WHERE',
|
||||
' {{=it._aggregationColumn}} != \'infinity\'::float',
|
||||
' AND',
|
||||
' {{=it._aggregationColumn}} != \'-infinity\'::float',
|
||||
' AND',
|
||||
' {{=it._aggregationColumn}} != \'NaN\'::float{{?}}',
|
||||
')'
|
||||
].join(' \n'));
|
||||
|
||||
var summaryQueryTpl = dot.template([
|
||||
'summary AS (',
|
||||
' SELECT',
|
||||
' count(1) AS count,',
|
||||
' sum(CASE WHEN {{=it._column}} IS NULL THEN 1 ELSE 0 END) AS nulls_count',
|
||||
' {{?it._isFloatColumn}},sum(',
|
||||
' CASE',
|
||||
' WHEN {{=it._aggregationColumn}} = \'infinity\'::float OR {{=it._aggregationColumn}} = \'-infinity\'::float',
|
||||
' THEN 1',
|
||||
' ELSE 0',
|
||||
' END',
|
||||
' ) AS infinities_count,',
|
||||
' sum(CASE WHEN {{=it._aggregationColumn}} = \'NaN\'::float THEN 1 ELSE 0 END) AS nans_count{{?}}',
|
||||
' FROM ({{=it._query}}) _cdb_aggregation_nulls',
|
||||
')'
|
||||
].join('\n'));
|
||||
@@ -39,7 +18,7 @@ var rankedCategoriesQueryTpl = dot.template([
|
||||
'categories AS(',
|
||||
' SELECT {{=it._column}} AS category, {{=it._aggregation}} AS value,',
|
||||
' row_number() OVER (ORDER BY {{=it._aggregation}} desc) as rank',
|
||||
' FROM filtered_source',
|
||||
' FROM ({{=it._query}}) _cdb_aggregation_all',
|
||||
' {{?it._aggregationColumn!==null}}WHERE {{=it._aggregationColumn}} IS NOT NULL{{?}}',
|
||||
' GROUP BY {{=it._column}}',
|
||||
' ORDER BY 2 DESC',
|
||||
@@ -65,25 +44,22 @@ var categoriesSummaryCountQueryTpl = dot.template([
|
||||
].join('\n'));
|
||||
|
||||
var rankedAggregationQueryTpl = dot.template([
|
||||
'SELECT CAST(category AS text), value, false as agg, nulls_count, min_val, max_val,',
|
||||
' count, categories_count{{?it._isFloatColumn}}, nans_count, infinities_count{{?}}',
|
||||
'SELECT CAST(category AS text), value, false as agg, nulls_count, min_val, max_val, count, categories_count',
|
||||
' FROM categories, summary, categories_summary_min_max, categories_summary_count',
|
||||
' WHERE rank < {{=it._limit}}',
|
||||
'UNION ALL',
|
||||
'SELECT \'Other\' category, {{=it._aggregationFn}}(value) as value, true as agg, nulls_count,',
|
||||
' min_val, max_val, count, categories_count{{?it._isFloatColumn}}, nans_count, infinities_count{{?}}',
|
||||
'SELECT \'Other\' category, {{=it._aggregationFn}}(value) as value, true as agg, nulls_count, min_val, max_val,',
|
||||
' count, categories_count',
|
||||
' FROM categories, summary, categories_summary_min_max, categories_summary_count',
|
||||
' WHERE rank >= {{=it._limit}}',
|
||||
'GROUP BY nulls_count, min_val, max_val, count,',
|
||||
' categories_count{{?it._isFloatColumn}}, nans_count, infinities_count{{?}}'
|
||||
'GROUP BY nulls_count, min_val, max_val, count, categories_count'
|
||||
].join('\n'));
|
||||
|
||||
var aggregationQueryTpl = dot.template([
|
||||
'SELECT CAST({{=it._column}} AS text) AS category, {{=it._aggregation}} AS value, false as agg,',
|
||||
' nulls_count, min_val, max_val, count, categories_count{{?it._isFloatColumn}}, nans_count, infinities_count{{?}}',
|
||||
' nulls_count, min_val, max_val, count, categories_count',
|
||||
'FROM ({{=it._query}}) _cdb_aggregation_all, summary, categories_summary_min_max, categories_summary_count',
|
||||
'GROUP BY category, nulls_count, min_val, max_val, count,',
|
||||
' categories_count{{?it._isFloatColumn}}, nans_count, infinities_count{{?}}',
|
||||
'GROUP BY category, nulls_count, min_val, max_val, count, categories_count',
|
||||
'ORDER BY value DESC'
|
||||
].join('\n'));
|
||||
|
||||
@@ -108,7 +84,7 @@ var TYPE = 'aggregation';
|
||||
}
|
||||
}
|
||||
*/
|
||||
function Aggregation(query, options, queries) {
|
||||
function Aggregation(query, options) {
|
||||
if (!_.isString(options.column)) {
|
||||
throw new Error('Aggregation expects `column` in widget options');
|
||||
}
|
||||
@@ -132,11 +108,9 @@ function Aggregation(query, options, queries) {
|
||||
BaseWidget.apply(this);
|
||||
|
||||
this.query = query;
|
||||
this.queries = queries;
|
||||
this.column = options.column;
|
||||
this.aggregation = options.aggregation;
|
||||
this.aggregationColumn = options.aggregationColumn;
|
||||
this._isFloatColumn = null;
|
||||
}
|
||||
|
||||
Aggregation.prototype = new BaseWidget();
|
||||
@@ -145,39 +119,19 @@ Aggregation.prototype.constructor = Aggregation;
|
||||
module.exports = Aggregation;
|
||||
|
||||
Aggregation.prototype.sql = function(psql, override, callback) {
|
||||
var self = this;
|
||||
|
||||
if (!callback) {
|
||||
callback = override;
|
||||
override = {};
|
||||
}
|
||||
|
||||
if (this.aggregationColumn && this._isFloatColumn === null) {
|
||||
this._isFloatColumn = false;
|
||||
this.getColumnType(psql, this.aggregationColumn, this.queries.no_filters, function (err, type) {
|
||||
if (!err && !!type) {
|
||||
self._isFloatColumn = type.float;
|
||||
}
|
||||
self.sql(psql, override, callback);
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
var _query = this.query;
|
||||
|
||||
var aggregationSql;
|
||||
|
||||
if (!!override.ownFilter) {
|
||||
aggregationSql = [
|
||||
this.getCategoriesCTESql(
|
||||
_query,
|
||||
this.column,
|
||||
this.aggregation,
|
||||
this.aggregationColumn,
|
||||
this._isFloatColumn
|
||||
),
|
||||
this.getCategoriesCTESql(_query, this.column, this.aggregation, this.aggregationColumn),
|
||||
aggregationQueryTpl({
|
||||
_isFloatColumn: this._isFloatColumn,
|
||||
_query: _query,
|
||||
_column: this.column,
|
||||
_aggregation: this.getAggregationSql(),
|
||||
@@ -186,15 +140,8 @@ Aggregation.prototype.sql = function(psql, override, callback) {
|
||||
].join('\n');
|
||||
} else {
|
||||
aggregationSql = [
|
||||
this.getCategoriesCTESql(
|
||||
_query,
|
||||
this.column,
|
||||
this.aggregation,
|
||||
this.aggregationColumn,
|
||||
this._isFloatColumn
|
||||
),
|
||||
this.getCategoriesCTESql(_query, this.column, this.aggregation, this.aggregationColumn),
|
||||
rankedAggregationQueryTpl({
|
||||
_isFloatColumn: this._isFloatColumn,
|
||||
_query: _query,
|
||||
_column: this.column,
|
||||
_aggregationFn: this.aggregation !== 'count' ? this.aggregation : 'sum',
|
||||
@@ -208,38 +155,30 @@ Aggregation.prototype.sql = function(psql, override, callback) {
|
||||
return callback(null, aggregationSql);
|
||||
};
|
||||
|
||||
Aggregation.prototype.getCategoriesCTESql = function(query, column, aggregation, aggregationColumn, isFloatColumn) {
|
||||
Aggregation.prototype.getCategoriesCTESql = function(query, column, aggregation, aggregationColumn) {
|
||||
return [
|
||||
"WITH",
|
||||
[
|
||||
filteredQueryTpl({
|
||||
_isFloatColumn: isFloatColumn,
|
||||
_query: this.query,
|
||||
_column: this.column,
|
||||
_aggregationColumn: aggregation !== 'count' ? aggregationColumn : null
|
||||
}),
|
||||
summaryQueryTpl({
|
||||
_isFloatColumn: isFloatColumn,
|
||||
_query: query,
|
||||
_column: column,
|
||||
_aggregationColumn: aggregation !== 'count' ? aggregationColumn : null
|
||||
}),
|
||||
rankedCategoriesQueryTpl({
|
||||
_query: query,
|
||||
_column: column,
|
||||
_aggregation: this.getAggregationSql(),
|
||||
_aggregationColumn: aggregation !== 'count' ? aggregationColumn : null
|
||||
}),
|
||||
categoriesSummaryMinMaxQueryTpl({
|
||||
_query: query,
|
||||
_column: column
|
||||
}),
|
||||
categoriesSummaryCountQueryTpl({
|
||||
_query: query,
|
||||
_column: column
|
||||
})
|
||||
].join(',\n')
|
||||
].join('\n');
|
||||
"WITH",
|
||||
[
|
||||
summaryQueryTpl({
|
||||
_query: query,
|
||||
_column: column
|
||||
}),
|
||||
rankedCategoriesQueryTpl({
|
||||
_query: query,
|
||||
_column: column,
|
||||
_aggregation: this.getAggregationSql(),
|
||||
_aggregationColumn: aggregation !== 'count' ? aggregationColumn : null
|
||||
}),
|
||||
categoriesSummaryMinMaxQueryTpl({
|
||||
_query: query,
|
||||
_column: column
|
||||
}),
|
||||
categoriesSummaryCountQueryTpl({
|
||||
_query: query,
|
||||
_column: column
|
||||
})
|
||||
].join(',\n')
|
||||
].join('\n');
|
||||
};
|
||||
|
||||
var aggregationFnQueryTpl = dot.template('{{=it._aggregationFn}}({{=it._aggregationColumn}})');
|
||||
@@ -254,8 +193,6 @@ Aggregation.prototype.format = function(result) {
|
||||
var categories = [];
|
||||
var count = 0;
|
||||
var nulls = 0;
|
||||
var nans = 0;
|
||||
var infinities = 0;
|
||||
var minValue = 0;
|
||||
var maxValue = 0;
|
||||
var categoriesCount = 0;
|
||||
@@ -265,15 +202,12 @@ Aggregation.prototype.format = function(result) {
|
||||
var firstRow = result.rows[0];
|
||||
count = firstRow.count;
|
||||
nulls = firstRow.nulls_count;
|
||||
nans = firstRow.nans_count;
|
||||
infinities = firstRow.infinities_count;
|
||||
minValue = firstRow.min_val;
|
||||
maxValue = firstRow.max_val;
|
||||
categoriesCount = firstRow.categories_count;
|
||||
|
||||
result.rows.forEach(function(row) {
|
||||
categories.push(_.omit(row, 'count', 'nulls_count', 'min_val',
|
||||
'max_val', 'categories_count', 'nans_count', 'infinities_count'));
|
||||
categories.push(_.omit(row, 'count', 'nulls_count', 'min_val', 'max_val', 'categories_count'));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -281,8 +215,6 @@ Aggregation.prototype.format = function(result) {
|
||||
aggregation: this.aggregation,
|
||||
count: count,
|
||||
nulls: nulls,
|
||||
nans: nans,
|
||||
infinities: infinities,
|
||||
min: minValue,
|
||||
max: maxValue,
|
||||
categoriesCount: categoriesCount,
|
||||
@@ -321,8 +253,6 @@ Aggregation.prototype.search = function(psql, userQuery, callback) {
|
||||
var self = this;
|
||||
|
||||
var _userQuery = psql.escapeLiteral('%' + userQuery + '%');
|
||||
var _value = this.aggregation !== 'count' && this.aggregationColumn ?
|
||||
this.aggregation + '(' + this.aggregationColumn + ')' : 'count(1)';
|
||||
|
||||
// TODO unfiltered will be wrong as filters are already applied at this point
|
||||
var query = searchQueryTpl({
|
||||
@@ -335,7 +265,7 @@ Aggregation.prototype.search = function(psql, userQuery, callback) {
|
||||
_searchFiltered: filterCategoriesQueryTpl({
|
||||
_query: this.query,
|
||||
_column: this.column,
|
||||
_value: _value,
|
||||
_value: 'count(1)',
|
||||
_userQuery: _userQuery
|
||||
})
|
||||
});
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
var dot = require('dot');
|
||||
dot.templateSettings.strip = false;
|
||||
|
||||
function BaseDataview() {}
|
||||
|
||||
module.exports = BaseDataview;
|
||||
@@ -8,11 +5,8 @@ module.exports = BaseDataview;
|
||||
BaseDataview.prototype.getResult = function(psql, override, callback) {
|
||||
var self = this;
|
||||
this.sql(psql, override, function(err, query) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
psql.query(query, function(err, result) {
|
||||
|
||||
if (err) {
|
||||
return callback(err, result);
|
||||
}
|
||||
@@ -30,42 +24,3 @@ BaseDataview.prototype.getResult = function(psql, override, callback) {
|
||||
BaseDataview.prototype.search = function(psql, userQuery, callback) {
|
||||
return callback(null, this.format({ rows: [] }));
|
||||
};
|
||||
|
||||
var FLOAT_OIDS = {
|
||||
700: true,
|
||||
701: true,
|
||||
1700: true
|
||||
};
|
||||
|
||||
var DATE_OIDS = {
|
||||
1082: true,
|
||||
1114: true,
|
||||
1184: true
|
||||
};
|
||||
|
||||
var columnTypeQueryTpl = dot.template(
|
||||
'SELECT pg_typeof({{=it.column}})::oid FROM ({{=it.query}}) _cdb_column_type limit 1'
|
||||
);
|
||||
|
||||
BaseDataview.prototype.getColumnType = function (psql, column, query, callback) {
|
||||
var readOnlyTransaction = true;
|
||||
|
||||
var columnTypeQuery = columnTypeQueryTpl({
|
||||
column: column, query: query
|
||||
});
|
||||
|
||||
psql.query(columnTypeQuery, function(err, result) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
var pgType = result.rows[0].pg_typeof;
|
||||
callback(null, getPGTypeName(pgType));
|
||||
}, readOnlyTransaction);
|
||||
};
|
||||
|
||||
function getPGTypeName (pgType) {
|
||||
return {
|
||||
float: FLOAT_OIDS.hasOwnProperty(pgType),
|
||||
date: DATE_OIDS.hasOwnProperty(pgType)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -7,19 +7,9 @@ dot.templateSettings.strip = false;
|
||||
|
||||
var formulaQueryTpl = dot.template([
|
||||
'SELECT',
|
||||
' {{=it._operation}}({{=it._column}}) AS result,',
|
||||
' (SELECT count(1) FROM ({{=it._query}}) _cdb_formula_nulls WHERE {{=it._column}} IS NULL) AS nulls_count',
|
||||
' {{?it._isFloatColumn}},(SELECT count(1) FROM ({{=it._query}}) _cdb_formula_nulls',
|
||||
' WHERE {{=it._column}} = \'infinity\'::float OR {{=it._column}} = \'-infinity\'::float) AS infinities_count',
|
||||
' ,(SELECT count(1) FROM ({{=it._query}}) _cdb_formula_nulls',
|
||||
' WHERE {{=it._column}} = \'NaN\'::float) AS nans_count{{?}}',
|
||||
'FROM ({{=it._query}}) _cdb_formula',
|
||||
'{{?it._isFloatColumn && it._operation !== \'count\'}}WHERE',
|
||||
' {{=it._column}} != \'infinity\'::float',
|
||||
'AND',
|
||||
' {{=it._column}} != \'-infinity\'::float',
|
||||
'AND',
|
||||
' {{=it._column}} != \'NaN\'::float{{?}}'
|
||||
'{{=it._operation}}({{=it._column}}) AS result,',
|
||||
'(SELECT count(1) FROM ({{=it._query}}) _cdb_formula_nulls WHERE {{=it._column}} IS NULL) AS nulls_count',
|
||||
'FROM ({{=it._query}}) _cdb_formula'
|
||||
].join('\n'));
|
||||
|
||||
var VALID_OPERATIONS = {
|
||||
@@ -41,7 +31,7 @@ var TYPE = 'formula';
|
||||
}
|
||||
}
|
||||
*/
|
||||
function Formula(query, options, queries) {
|
||||
function Formula(query, options) {
|
||||
if (!_.isString(options.operation)) {
|
||||
throw new Error('Formula expects `operation` in widget options');
|
||||
}
|
||||
@@ -57,10 +47,8 @@ function Formula(query, options, queries) {
|
||||
BaseWidget.apply(this);
|
||||
|
||||
this.query = query;
|
||||
this.queries = queries;
|
||||
this.column = options.column || '1';
|
||||
this.operation = options.operation;
|
||||
this._isFloatColumn = null;
|
||||
}
|
||||
|
||||
Formula.prototype = new BaseWidget();
|
||||
@@ -69,27 +57,14 @@ Formula.prototype.constructor = Formula;
|
||||
module.exports = Formula;
|
||||
|
||||
Formula.prototype.sql = function(psql, override, callback) {
|
||||
var self = this;
|
||||
|
||||
if (!callback) {
|
||||
callback = override;
|
||||
override = {};
|
||||
}
|
||||
|
||||
if (this._isFloatColumn === null) {
|
||||
this._isFloatColumn = false;
|
||||
this.getColumnType(psql, this.column, this.queries.no_filters, function (err, type) {
|
||||
if (!err && !!type) {
|
||||
self._isFloatColumn = type.float;
|
||||
}
|
||||
self.sql(psql, override, callback);
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
var _query = this.query;
|
||||
var formulaSql = formulaQueryTpl({
|
||||
_isFloatColumn: this._isFloatColumn,
|
||||
_query: this.query,
|
||||
_query: _query,
|
||||
_operation: this.operation,
|
||||
_column: this.column
|
||||
});
|
||||
@@ -103,17 +78,13 @@ Formula.prototype.format = function(result) {
|
||||
var formattedResult = {
|
||||
operation: this.operation,
|
||||
result: 0,
|
||||
nulls: 0,
|
||||
nans: 0,
|
||||
infinities: 0
|
||||
nulls: 0
|
||||
};
|
||||
|
||||
if (result.rows.length) {
|
||||
formattedResult.operation = this.operation;
|
||||
formattedResult.result = result.rows[0].result;
|
||||
formattedResult.nulls = result.rows[0].nulls_count;
|
||||
formattedResult.nans = result.rows[0].nans_count;
|
||||
formattedResult.infinities = result.rows[0].infinities_count;
|
||||
}
|
||||
|
||||
return formattedResult;
|
||||
|
||||
@@ -5,289 +5,108 @@ var debug = require('debug')('windshaft:dataview:histogram');
|
||||
var dot = require('dot');
|
||||
dot.templateSettings.strip = false;
|
||||
|
||||
var columnTypeQueryTpl = dot.template(
|
||||
'SELECT pg_typeof({{=it.column}})::oid FROM ({{=it.query}}) _cdb_histogram_column_type limit 1'
|
||||
);
|
||||
var columnCastTpl = dot.template("date_part('epoch', {{=it.column}})");
|
||||
|
||||
var dateIntervalQueryTpl = dot.template([
|
||||
'WITH',
|
||||
'__cdb_dates AS (',
|
||||
' SELECT',
|
||||
' MAX({{=it.column}}::timestamp) AS __cdb_end,',
|
||||
' MIN({{=it.column}}::timestamp) AS __cdb_start',
|
||||
' FROM ({{=it.query}}) __cdb_source',
|
||||
'),',
|
||||
'__cdb_interval_in_days AS (',
|
||||
' SELECT' ,
|
||||
' DATE_PART(\'day\', __cdb_end - __cdb_start) AS __cdb_days',
|
||||
' FROM __cdb_dates',
|
||||
'),',
|
||||
'__cdb_interval_in_hours AS (',
|
||||
' SELECT',
|
||||
' __cdb_days * 24 + DATE_PART(\'hour\', __cdb_end - __cdb_start) AS __cdb_hours',
|
||||
' FROM __cdb_interval_in_days, __cdb_dates',
|
||||
'),',
|
||||
'__cdb_interval_in_minutes AS (',
|
||||
' SELECT',
|
||||
' __cdb_hours * 60 + DATE_PART(\'minute\', __cdb_end - __cdb_start) AS __cdb_minutes',
|
||||
' FROM __cdb_interval_in_hours, __cdb_dates',
|
||||
'),',
|
||||
'__cdb_interval_in_seconds AS (',
|
||||
' SELECT',
|
||||
' __cdb_minutes * 60 + DATE_PART(\'second\', __cdb_end - __cdb_start) AS __cdb_seconds',
|
||||
' FROM __cdb_interval_in_minutes, __cdb_dates',
|
||||
')',
|
||||
'SELECT',
|
||||
' ROUND(__cdb_days / 365) AS year,',
|
||||
' ROUND(__cdb_days / 90) AS quarter,',
|
||||
' ROUND(__cdb_days / 30) AS month,',
|
||||
' ROUND(__cdb_days / 7) AS week,',
|
||||
' __cdb_days AS day,',
|
||||
' __cdb_hours AS hour,',
|
||||
' __cdb_minutes AS minute,',
|
||||
' __cdb_seconds AS second',
|
||||
'FROM __cdb_interval_in_days, __cdb_interval_in_hours, __cdb_interval_in_minutes, __cdb_interval_in_seconds'
|
||||
].join('\n'));
|
||||
|
||||
var MAX_INTERVAL_VALUE = 366;
|
||||
var BIN_MIN_NUMBER = 6;
|
||||
var BIN_MAX_NUMBER = 48;
|
||||
|
||||
var filteredQueryTpl = dot.template([
|
||||
'__cdb_filtered_source AS (',
|
||||
' SELECT *',
|
||||
' FROM ({{=it._query}}) __cdb_filtered_source_query',
|
||||
' WHERE',
|
||||
' {{=it._column}} IS NOT NULL',
|
||||
' {{?it._isFloatColumn}}AND',
|
||||
' {{=it._column}} != \'infinity\'::float',
|
||||
' AND',
|
||||
' {{=it._column}} != \'-infinity\'::float',
|
||||
' AND',
|
||||
' {{=it._column}} != \'NaN\'::float{{?}}',
|
||||
')'
|
||||
].join(' \n'));
|
||||
|
||||
var basicsQueryTpl = dot.template([
|
||||
'__cdb_basics AS (',
|
||||
'basics AS (',
|
||||
' SELECT',
|
||||
' max({{=it._column}}) AS __cdb_max_val, min({{=it._column}}) AS __cdb_min_val,',
|
||||
' avg({{=it._column}}) AS __cdb_avg_val, count(1) AS __cdb_total_rows',
|
||||
' FROM __cdb_filtered_source',
|
||||
' max({{=it._column}}) AS max_val, min({{=it._column}}) AS min_val,',
|
||||
' avg({{=it._column}}) AS avg_val, count(1) AS total_rows',
|
||||
' FROM ({{=it._query}}) _cdb_basics',
|
||||
')'
|
||||
].join(' \n'));
|
||||
|
||||
var overrideBasicsQueryTpl = dot.template([
|
||||
'__cdb_basics AS (',
|
||||
'basics AS (',
|
||||
' SELECT',
|
||||
' max({{=it._end}}) AS __cdb_max_val, min({{=it._start}}) AS __cdb_min_val,',
|
||||
' avg({{=it._column}}) AS __cdb_avg_val, count(1) AS __cdb_total_rows',
|
||||
' FROM __cdb_filtered_source',
|
||||
' max({{=it._end}}) AS max_val, min({{=it._start}}) AS min_val,',
|
||||
' avg({{=it._column}}) AS avg_val, count(1) AS total_rows',
|
||||
' FROM ({{=it._query}}) _cdb_basics',
|
||||
')'
|
||||
].join('\n'));
|
||||
|
||||
var iqrQueryTpl = dot.template([
|
||||
'__cdb_iqrange AS (',
|
||||
' SELECT max(quartile_max) - min(quartile_max) AS __cdb_iqr',
|
||||
'iqrange AS (',
|
||||
' SELECT max(quartile_max) - min(quartile_max) AS iqr',
|
||||
' FROM (',
|
||||
' SELECT quartile, max(_cdb_iqr_column) AS quartile_max from (',
|
||||
' SELECT {{=it._column}} AS _cdb_iqr_column, ntile(4) over (order by {{=it._column}}',
|
||||
' ) AS quartile',
|
||||
' FROM __cdb_filtered_source) _cdb_quartiles',
|
||||
' FROM ({{=it._query}}) _cdb_rank) _cdb_quartiles',
|
||||
' WHERE quartile = 1 or quartile = 3',
|
||||
' GROUP BY quartile',
|
||||
' ) __cdb_iqr',
|
||||
' ) _cdb_iqr',
|
||||
')'
|
||||
].join('\n'));
|
||||
|
||||
var binsQueryTpl = dot.template([
|
||||
'__cdb_bins AS (',
|
||||
' SELECT CASE WHEN __cdb_total_rows = 0 OR __cdb_iqr = 0',
|
||||
'bins AS (',
|
||||
' SELECT CASE WHEN total_rows = 0 OR iqr = 0',
|
||||
' THEN 1',
|
||||
' ELSE GREATEST(',
|
||||
' LEAST({{=it._minBins}}, CAST(__cdb_total_rows AS INT)),',
|
||||
' LEAST({{=it._minBins}}, CAST(total_rows AS INT)),',
|
||||
' LEAST(',
|
||||
' CAST(((__cdb_max_val - __cdb_min_val) / (2 * __cdb_iqr * power(__cdb_total_rows, 1/3))) AS INT),',
|
||||
' CAST(((max_val - min_val) / (2 * iqr * power(total_rows, 1/3))) AS INT),',
|
||||
' {{=it._maxBins}}',
|
||||
' )',
|
||||
' )',
|
||||
' END AS __cdb_bins_number',
|
||||
' FROM __cdb_basics, __cdb_iqrange, __cdb_filtered_source',
|
||||
' END AS bins_number',
|
||||
' FROM basics, iqrange, ({{=it._query}}) _cdb_bins',
|
||||
' LIMIT 1',
|
||||
')'
|
||||
].join('\n'));
|
||||
|
||||
var overrideBinsQueryTpl = dot.template([
|
||||
'__cdb_bins AS (',
|
||||
' SELECT {{=it._bins}} AS __cdb_bins_number',
|
||||
'bins AS (',
|
||||
' SELECT {{=it._bins}} AS bins_number',
|
||||
')'
|
||||
].join('\n'));
|
||||
|
||||
var nullsQueryTpl = dot.template([
|
||||
'__cdb_nulls AS (',
|
||||
'nulls AS (',
|
||||
' SELECT',
|
||||
' count(*) AS __cdb_nulls_count',
|
||||
' FROM ({{=it._query}}) __cdb_histogram_nulls',
|
||||
' count(*) AS nulls_count',
|
||||
' FROM ({{=it._query}}) _cdb_histogram_nulls',
|
||||
' WHERE {{=it._column}} IS NULL',
|
||||
')'
|
||||
].join('\n'));
|
||||
|
||||
var infinitiesQueryTpl = dot.template([
|
||||
'__cdb_infinities AS (',
|
||||
' SELECT',
|
||||
' count(*) AS __cdb_infinities_count',
|
||||
' FROM ({{=it._query}}) __cdb_infinities_query',
|
||||
' WHERE',
|
||||
' {{=it._column}} = \'infinity\'::float',
|
||||
' OR',
|
||||
' {{=it._column}} = \'-infinity\'::float',
|
||||
')'
|
||||
].join('\n'));
|
||||
|
||||
var nansQueryTpl = dot.template([
|
||||
'__cdb_nans AS (',
|
||||
' SELECT',
|
||||
' count(*) AS __cdb_nans_count',
|
||||
' FROM ({{=it._query}}) __cdb_nans_query',
|
||||
' WHERE {{=it._column}} = \'NaN\'::float',
|
||||
')'
|
||||
].join('\n'));
|
||||
|
||||
var histogramQueryTpl = dot.template([
|
||||
'SELECT',
|
||||
' (__cdb_max_val - __cdb_min_val) / cast(__cdb_bins_number as float) AS bin_width,',
|
||||
' __cdb_bins_number AS bins_number,',
|
||||
' __cdb_nulls_count AS nulls_count,',
|
||||
' {{?it._isFloatColumn}}__cdb_infinities_count AS infinities_count,',
|
||||
' __cdb_nans_count AS nans_count,{{?}}',
|
||||
' __cdb_avg_val AS avg_val,',
|
||||
' CASE WHEN __cdb_min_val = __cdb_max_val',
|
||||
' (max_val - min_val) / cast(bins_number as float) AS bin_width,',
|
||||
' bins_number,',
|
||||
' nulls_count,',
|
||||
' avg_val,',
|
||||
' CASE WHEN min_val = max_val',
|
||||
' THEN 0',
|
||||
' ELSE GREATEST(',
|
||||
' 1,',
|
||||
' LEAST(',
|
||||
' WIDTH_BUCKET({{=it._column}}, __cdb_min_val, __cdb_max_val, __cdb_bins_number),',
|
||||
' __cdb_bins_number',
|
||||
' )',
|
||||
' ) - 1',
|
||||
' ELSE GREATEST(1, LEAST(WIDTH_BUCKET({{=it._column}}, min_val, max_val, bins_number), bins_number)) - 1',
|
||||
' END AS bin,',
|
||||
' min({{=it._column}})::numeric AS min,',
|
||||
' max({{=it._column}})::numeric AS max,',
|
||||
' avg({{=it._column}})::numeric AS avg,',
|
||||
' count(*) AS freq',
|
||||
'FROM __cdb_filtered_source, __cdb_basics, __cdb_nulls,',
|
||||
' __cdb_bins{{?it._isFloatColumn}}, __cdb_infinities, __cdb_nans{{?}}',
|
||||
'GROUP BY bin, bins_number, bin_width, nulls_count,',
|
||||
' avg_val{{?it._isFloatColumn}}, infinities_count, nans_count{{?}}',
|
||||
'FROM ({{=it._query}}) _cdb_histogram, basics, nulls, bins',
|
||||
'WHERE {{=it._column}} IS NOT NULL',
|
||||
'GROUP BY bin, bins_number, bin_width, nulls_count, avg_val',
|
||||
'ORDER BY bin'
|
||||
].join('\n'));
|
||||
|
||||
var dateBasicsQueryTpl = dot.template([
|
||||
'__cdb_basics AS (',
|
||||
' SELECT',
|
||||
' max(date_part(\'epoch\', {{=it._column}})) AS __cdb_max_val,',
|
||||
' min(date_part(\'epoch\', {{=it._column}})) AS __cdb_min_val,',
|
||||
' avg(date_part(\'epoch\', {{=it._column}})) AS __cdb_avg_val,',
|
||||
' min(date_trunc(',
|
||||
' \'{{=it._aggregation}}\', {{=it._column}}::timestamp AT TIME ZONE \'{{=it._offset}}\'',
|
||||
' )) AS __cdb_start_date,',
|
||||
' max({{=it._column}}::timestamp AT TIME ZONE \'{{=it._offset}}\') AS __cdb_end_date,',
|
||||
' count(1) AS __cdb_total_rows',
|
||||
' FROM ({{=it._query}}) __cdb_basics_query',
|
||||
')'
|
||||
].join(' \n'));
|
||||
|
||||
var dateOverrideBasicsQueryTpl = dot.template([
|
||||
'__cdb_basics AS (',
|
||||
' SELECT',
|
||||
' max({{=it._end}}) AS __cdb_max_val,',
|
||||
' min({{=it._start}}) AS __cdb_min_val,',
|
||||
' avg(date_part(\'epoch\', {{=it._column}})) AS __cdb_avg_val,',
|
||||
' min(',
|
||||
' date_trunc(',
|
||||
' \'{{=it._aggregation}}\',',
|
||||
' TO_TIMESTAMP({{=it._start}})::timestamp AT TIME ZONE \'{{=it._offset}}\'',
|
||||
' )',
|
||||
' ) AS __cdb_start_date,',
|
||||
' max(',
|
||||
' TO_TIMESTAMP({{=it._end}})::timestamp AT TIME ZONE \'{{=it._offset}}\'',
|
||||
' ) AS __cdb_end_date,',
|
||||
' count(1) AS __cdb_total_rows',
|
||||
' FROM ({{=it._query}}) __cdb_basics_query',
|
||||
')'
|
||||
].join(' \n'));
|
||||
|
||||
var dateBinsQueryTpl = dot.template([
|
||||
'__cdb_bins AS (',
|
||||
' SELECT',
|
||||
' __cdb_bins_array,',
|
||||
' ARRAY_LENGTH(__cdb_bins_array, 1) AS __cdb_bins_number',
|
||||
' FROM (',
|
||||
' SELECT',
|
||||
' ARRAY(',
|
||||
' SELECT GENERATE_SERIES(',
|
||||
' __cdb_start_date::timestamptz,',
|
||||
' __cdb_end_date::timestamptz,',
|
||||
' {{?it._aggregation==="quarter"}}\'3 month\'{{??}}\'1 {{=it._aggregation}}\'{{?}}::interval',
|
||||
' )',
|
||||
' ) AS __cdb_bins_array',
|
||||
' FROM __cdb_basics',
|
||||
' ) __cdb_bins_array_query',
|
||||
')'
|
||||
].join('\n'));
|
||||
|
||||
var dateHistogramQueryTpl = dot.template([
|
||||
'SELECT',
|
||||
' (__cdb_max_val - __cdb_min_val) / cast(__cdb_bins_number as float) AS bin_width,',
|
||||
' __cdb_bins_number AS bins_number,',
|
||||
' __cdb_nulls_count AS nulls_count,',
|
||||
' CASE WHEN __cdb_min_val = __cdb_max_val',
|
||||
' THEN 0',
|
||||
' ELSE GREATEST(1, LEAST(',
|
||||
' WIDTH_BUCKET(',
|
||||
' {{=it._column}}::timestamp AT TIME ZONE \'{{=it._offset}}\',',
|
||||
' __cdb_bins_array',
|
||||
' ),',
|
||||
' __cdb_bins_number',
|
||||
' )) - 1',
|
||||
' END AS bin,',
|
||||
' min(',
|
||||
' date_part(',
|
||||
' \'epoch\', ',
|
||||
' date_trunc(',
|
||||
' \'{{=it._aggregation}}\', {{=it._column}}::timestamp AT TIME ZONE \'{{=it._offset}}\'',
|
||||
' ) AT TIME ZONE \'{{=it._offset}}\'',
|
||||
' )',
|
||||
' )::numeric AS timestamp,',
|
||||
' date_part(\'epoch\', __cdb_start_date)::numeric AS timestamp_start,',
|
||||
' min(date_part(\'epoch\', {{=it._column}}))::numeric AS min,',
|
||||
' max(date_part(\'epoch\', {{=it._column}}))::numeric AS max,',
|
||||
' avg(date_part(\'epoch\', {{=it._column}}))::numeric AS avg,',
|
||||
' count(*) AS freq',
|
||||
'FROM ({{=it._query}}) __cdb_histogram, __cdb_basics, __cdb_bins, __cdb_nulls',
|
||||
'WHERE date_part(\'epoch\', {{=it._column}}) IS NOT NULL',
|
||||
'GROUP BY bin, bins_number, bin_width, nulls_count, timestamp_start',
|
||||
'ORDER BY bin'
|
||||
].join('\n'));
|
||||
|
||||
var TYPE = 'histogram';
|
||||
|
||||
/**
|
||||
Numeric histogram:
|
||||
{
|
||||
type: 'histogram',
|
||||
options: {
|
||||
column: 'name', // column data type: numeric
|
||||
bins: 10 // OPTIONAL
|
||||
}
|
||||
}
|
||||
|
||||
Time series:
|
||||
{
|
||||
type: 'histogram',
|
||||
options: {
|
||||
column: 'date', // column data type: date
|
||||
aggregation: 'day' // OPTIONAL (if undefined then it'll be built as numeric)
|
||||
offset: -7200 // OPTIONAL (UTC offset in seconds)
|
||||
}
|
||||
{
|
||||
type: 'histogram',
|
||||
options: {
|
||||
column: 'name',
|
||||
bins: 10 // OPTIONAL
|
||||
}
|
||||
}
|
||||
*/
|
||||
function Histogram(query, options, queries) {
|
||||
@@ -299,8 +118,6 @@ function Histogram(query, options, queries) {
|
||||
this.queries = queries;
|
||||
this.column = options.column;
|
||||
this.bins = options.bins;
|
||||
this.aggregation = options.aggregation;
|
||||
this.offset = options.offset;
|
||||
|
||||
this._columnType = null;
|
||||
}
|
||||
@@ -310,55 +127,50 @@ Histogram.prototype.constructor = Histogram;
|
||||
|
||||
module.exports = Histogram;
|
||||
|
||||
Histogram.prototype.sql = function(psql, override, callback) {
|
||||
var self = this;
|
||||
var DATE_OIDS = {
|
||||
1082: true,
|
||||
1114: true,
|
||||
1184: true
|
||||
};
|
||||
|
||||
Histogram.prototype.sql = function(psql, override, callback) {
|
||||
if (!callback) {
|
||||
callback = override;
|
||||
override = {};
|
||||
}
|
||||
|
||||
var self = this;
|
||||
|
||||
var _column = this.column;
|
||||
|
||||
var columnTypeQuery = columnTypeQueryTpl({
|
||||
column: _column, query: this.queries.no_filters
|
||||
});
|
||||
|
||||
if (this._columnType === null) {
|
||||
this.getColumnType(psql, this.column, this.queries.no_filters, function (err, type) {
|
||||
psql.query(columnTypeQuery, function(err, result) {
|
||||
// assume numeric, will fail later
|
||||
self._columnType = 'numeric';
|
||||
if (!err && !!type) {
|
||||
self._columnType = Object.keys(type).find(function (key) {
|
||||
return type[key];
|
||||
});
|
||||
if (!err && !!result.rows[0]) {
|
||||
var pgType = result.rows[0].pg_typeof;
|
||||
if (DATE_OIDS.hasOwnProperty(pgType)) {
|
||||
self._columnType = 'date';
|
||||
}
|
||||
}
|
||||
self.sql(psql, override, callback);
|
||||
}, true); // use read-only transaction
|
||||
return null;
|
||||
}
|
||||
|
||||
this._buildQuery(psql, override, callback);
|
||||
};
|
||||
|
||||
Histogram.prototype.isDateHistogram = function (override) {
|
||||
return this._columnType === 'date' && (this.aggregation !== undefined || override.aggregation !== undefined);
|
||||
};
|
||||
|
||||
Histogram.prototype._buildQuery = function (psql, override, callback) {
|
||||
var filteredQuery, basicsQuery, binsQuery;
|
||||
var _column = this.column;
|
||||
var _query = this.query;
|
||||
|
||||
if (this.isDateHistogram(override)) {
|
||||
return this._buildDateHistogramQuery(psql, override, callback);
|
||||
}
|
||||
|
||||
if (this._columnType === 'date') {
|
||||
_column = columnCastTpl({column: _column});
|
||||
}
|
||||
|
||||
filteredQuery = filteredQueryTpl({
|
||||
_isFloatColumn: this._columnType === 'float',
|
||||
_query: _query,
|
||||
_column: _column
|
||||
});
|
||||
var _query = this.query;
|
||||
|
||||
if (this._shouldOverride(override)) {
|
||||
var basicsQuery, binsQuery;
|
||||
|
||||
if (override && _.has(override, 'start') && _.has(override, 'end') && _.has(override, 'bins')) {
|
||||
debug('overriding with %j', override);
|
||||
basicsQuery = overrideBasicsQueryTpl({
|
||||
_query: _query,
|
||||
@@ -378,7 +190,7 @@ Histogram.prototype._buildQuery = function (psql, override, callback) {
|
||||
_column: _column
|
||||
});
|
||||
|
||||
if (this._shouldOverrideBins(override)) {
|
||||
if (override && _.has(override, 'bins')) {
|
||||
binsQuery = [
|
||||
overrideBinsQueryTpl({
|
||||
_bins: override.bins
|
||||
@@ -399,134 +211,21 @@ Histogram.prototype._buildQuery = function (psql, override, callback) {
|
||||
}
|
||||
}
|
||||
|
||||
var cteSql = [
|
||||
filteredQuery,
|
||||
basicsQuery,
|
||||
binsQuery,
|
||||
nullsQueryTpl({
|
||||
_query: _query,
|
||||
_column: _column
|
||||
})
|
||||
];
|
||||
|
||||
if (this._columnType === 'float') {
|
||||
cteSql.push(
|
||||
infinitiesQueryTpl({
|
||||
_query: _query,
|
||||
_column: _column
|
||||
}),
|
||||
nansQueryTpl({
|
||||
_query: _query,
|
||||
_column: _column
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
var histogramSql = [
|
||||
"WITH",
|
||||
cteSql.join(',\n'),
|
||||
histogramQueryTpl({
|
||||
_isFloatColumn: this._columnType === 'float',
|
||||
_query: _query,
|
||||
_column: _column
|
||||
})
|
||||
].join('\n');
|
||||
|
||||
debug(histogramSql);
|
||||
|
||||
return callback(null, histogramSql);
|
||||
};
|
||||
|
||||
Histogram.prototype._shouldOverride = function (override) {
|
||||
return override && _.has(override, 'start') && _.has(override, 'end') && _.has(override, 'bins');
|
||||
};
|
||||
|
||||
Histogram.prototype._shouldOverrideBins = function (override) {
|
||||
return override && _.has(override, 'bins');
|
||||
};
|
||||
|
||||
var DATE_AGGREGATIONS = {
|
||||
'auto': true,
|
||||
'minute': true,
|
||||
'hour': true,
|
||||
'day': true,
|
||||
'week': true,
|
||||
'month': true,
|
||||
'quarter': true,
|
||||
'year': true
|
||||
};
|
||||
|
||||
Histogram.prototype._buildDateHistogramQuery = function (psql, override, callback) {
|
||||
var _column = this.column;
|
||||
var _query = this.query;
|
||||
var _aggregation = override && override.aggregation ? override.aggregation : this.aggregation;
|
||||
var _offset = override && Number.isFinite(override.offset) ? override.offset : this.offset;
|
||||
|
||||
if (!DATE_AGGREGATIONS.hasOwnProperty(_aggregation)) {
|
||||
return callback(new Error('Invalid aggregation value. Valid ones: ' +
|
||||
Object.keys(DATE_AGGREGATIONS).join(', ')
|
||||
));
|
||||
}
|
||||
|
||||
if (_aggregation === 'auto') {
|
||||
this.getAutomaticAggregation(psql, function (err, aggregation) {
|
||||
if (err || aggregation === 'none') {
|
||||
this.aggregation = 'day';
|
||||
} else {
|
||||
this.aggregation = aggregation;
|
||||
}
|
||||
override.aggregation = this.aggregation;
|
||||
this._buildDateHistogramQuery(psql, override, callback);
|
||||
}.bind(this));
|
||||
return null;
|
||||
}
|
||||
|
||||
var dateBasicsQuery;
|
||||
|
||||
if (override && _.has(override, 'start') && _.has(override, 'end')) {
|
||||
dateBasicsQuery = dateOverrideBasicsQueryTpl({
|
||||
_query: _query,
|
||||
_column: _column,
|
||||
_aggregation: _aggregation,
|
||||
_start: getBinStart(override),
|
||||
_end: getBinEnd(override),
|
||||
_offset: parseOffset(_offset, _aggregation)
|
||||
});
|
||||
} else {
|
||||
dateBasicsQuery = dateBasicsQueryTpl({
|
||||
_query: _query,
|
||||
_column: _column,
|
||||
_aggregation: _aggregation,
|
||||
_offset: parseOffset(_offset, _aggregation)
|
||||
});
|
||||
}
|
||||
|
||||
var dateBinsQuery = [
|
||||
dateBinsQueryTpl({
|
||||
_aggregation: _aggregation
|
||||
})
|
||||
].join(',\n');
|
||||
|
||||
var nullsQuery = nullsQueryTpl({
|
||||
_query: _query,
|
||||
_column: _column
|
||||
});
|
||||
|
||||
var dateHistogramQuery = dateHistogramQueryTpl({
|
||||
_query: _query,
|
||||
_column: _column,
|
||||
_aggregation: _aggregation,
|
||||
_offset: parseOffset(_offset, _aggregation)
|
||||
});
|
||||
|
||||
var histogramSql = [
|
||||
"WITH",
|
||||
[
|
||||
dateBasicsQuery,
|
||||
dateBinsQuery,
|
||||
nullsQuery
|
||||
basicsQuery,
|
||||
binsQuery,
|
||||
nullsQueryTpl({
|
||||
_query: _query,
|
||||
_column: _column
|
||||
})
|
||||
].join(',\n'),
|
||||
dateHistogramQuery
|
||||
histogramQueryTpl({
|
||||
_query: _query,
|
||||
_column: _column
|
||||
})
|
||||
].join('\n');
|
||||
|
||||
debug(histogramSql);
|
||||
@@ -534,46 +233,6 @@ Histogram.prototype._buildDateHistogramQuery = function (psql, override, callbac
|
||||
return callback(null, histogramSql);
|
||||
};
|
||||
|
||||
Histogram.prototype.getAutomaticAggregation = function (psql, callback) {
|
||||
var dateIntervalQuery = dateIntervalQueryTpl({
|
||||
query: this.query,
|
||||
column: this.column
|
||||
});
|
||||
|
||||
debug(dateIntervalQuery);
|
||||
|
||||
psql.query(dateIntervalQuery, function (err, result) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
var aggegations = result.rows[0];
|
||||
var aggregation = Object.keys(aggegations)
|
||||
.map(function (key) {
|
||||
return {
|
||||
name: key,
|
||||
value: aggegations[key]
|
||||
};
|
||||
})
|
||||
.reduce(function (closer, current) {
|
||||
if (current.value > MAX_INTERVAL_VALUE) {
|
||||
return closer;
|
||||
}
|
||||
|
||||
var closerDiff = MAX_INTERVAL_VALUE - closer.value;
|
||||
var currentDiff = MAX_INTERVAL_VALUE - current.value;
|
||||
|
||||
if (Number.isFinite(current.value) && closerDiff > currentDiff) {
|
||||
return current;
|
||||
}
|
||||
|
||||
return closer;
|
||||
}, { name: 'none', value: -1 });
|
||||
|
||||
callback(null, aggregation.name);
|
||||
});
|
||||
};
|
||||
|
||||
Histogram.prototype.format = function(result, override) {
|
||||
override = override || {};
|
||||
var buckets = [];
|
||||
@@ -582,12 +241,7 @@ Histogram.prototype.format = function(result, override) {
|
||||
var width = getWidth(override);
|
||||
var binsStart = getBinStart(override);
|
||||
var nulls = 0;
|
||||
var infinities = 0;
|
||||
var nans = 0;
|
||||
var avg;
|
||||
var timestampStart;
|
||||
var aggregation;
|
||||
var offset;
|
||||
|
||||
if (result.rows.length) {
|
||||
var firstRow = result.rows[0];
|
||||
@@ -595,60 +249,23 @@ Histogram.prototype.format = function(result, override) {
|
||||
width = firstRow.bin_width || width;
|
||||
avg = firstRow.avg_val;
|
||||
nulls = firstRow.nulls_count;
|
||||
timestampStart = firstRow.timestamp_start;
|
||||
infinities = firstRow.infinities_count;
|
||||
nans = firstRow.nans_count;
|
||||
binsStart = populateBinStart(override, firstRow);
|
||||
|
||||
if (Number.isFinite(timestampStart)) {
|
||||
aggregation = getAggregation(override, this.aggregation);
|
||||
offset = getOffset(override, this.offset);
|
||||
}
|
||||
binsStart = override.hasOwnProperty('start') ? getBinStart(override) : firstRow.min;
|
||||
|
||||
buckets = result.rows.map(function(row) {
|
||||
return _.omit(
|
||||
row,
|
||||
'bins_number',
|
||||
'bin_width',
|
||||
'nulls_count',
|
||||
'infinities_count',
|
||||
'nans_count',
|
||||
'avg_val',
|
||||
'timestamp_start'
|
||||
);
|
||||
return _.omit(row, 'bins_number', 'bin_width', 'nulls_count', 'avg_val');
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
aggregation: aggregation,
|
||||
offset: offset,
|
||||
timestamp_start: timestampStart,
|
||||
bin_width: width,
|
||||
bins_count: binsCount,
|
||||
bins_start: binsStart,
|
||||
nulls: nulls,
|
||||
infinities: infinities,
|
||||
nans: nans,
|
||||
avg: avg,
|
||||
bins: buckets
|
||||
};
|
||||
};
|
||||
|
||||
function getAggregation(override, aggregation) {
|
||||
return override && override.aggregation ? override.aggregation : aggregation;
|
||||
}
|
||||
|
||||
function getOffset(override, offset) {
|
||||
if (override && override.offset) {
|
||||
return override.offset;
|
||||
}
|
||||
if (offset) {
|
||||
return offset;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
function getBinStart(override) {
|
||||
if (override.hasOwnProperty('start') && override.hasOwnProperty('end')) {
|
||||
return Math.min(override.start, override.end);
|
||||
@@ -678,32 +295,6 @@ function getWidth(override) {
|
||||
return width;
|
||||
}
|
||||
|
||||
function parseOffset(offset, aggregation) {
|
||||
if (!offset) {
|
||||
return '0';
|
||||
}
|
||||
if (aggregation === 'hour' || aggregation === 'minute') {
|
||||
return '0';
|
||||
}
|
||||
|
||||
var offsetInHours = Math.ceil(offset / 3600);
|
||||
return '' + offsetInHours;
|
||||
}
|
||||
|
||||
function populateBinStart(override, firstRow) {
|
||||
var binStart;
|
||||
|
||||
if (firstRow.hasOwnProperty('timestamp')) {
|
||||
binStart = firstRow.timestamp;
|
||||
} else if (override.hasOwnProperty('start')) {
|
||||
binStart = getBinStart(override);
|
||||
} else {
|
||||
binStart = firstRow.min;
|
||||
}
|
||||
|
||||
return binStart;
|
||||
}
|
||||
|
||||
Histogram.prototype.getType = function() {
|
||||
return TYPE;
|
||||
};
|
||||
|
||||
@@ -1,36 +1,14 @@
|
||||
var BaseOverviewsDataview = require('./base');
|
||||
var BaseDataview = require('../aggregation');
|
||||
var debug = require('debug')('windshaft:widget:aggregation:overview');
|
||||
|
||||
var dot = require('dot');
|
||||
dot.templateSettings.strip = false;
|
||||
|
||||
var filteredQueryTpl = dot.template([
|
||||
'filtered_source AS (',
|
||||
' SELECT *',
|
||||
' FROM ({{=it._query}}) _cdb_filtered_source',
|
||||
' {{?it._aggregationColumn && it._isFloatColumn}}WHERE',
|
||||
' {{=it._aggregationColumn}} != \'infinity\'::float',
|
||||
' AND',
|
||||
' {{=it._aggregationColumn}} != \'-infinity\'::float',
|
||||
' AND',
|
||||
' {{=it._aggregationColumn}} != \'NaN\'::float{{?}}',
|
||||
')'
|
||||
].join(' \n'));
|
||||
|
||||
var summaryQueryTpl = dot.template([
|
||||
'summary AS (',
|
||||
' SELECT',
|
||||
' sum(_feature_count) AS count,',
|
||||
' sum(CASE WHEN {{=it._column}} IS NULL THEN 1 ELSE 0 END) AS nulls_count',
|
||||
' {{?it._isFloatColumn}},sum(',
|
||||
' CASE',
|
||||
' WHEN {{=it._aggregationColumn}} = \'infinity\'::float OR {{=it._aggregationColumn}} = \'-infinity\'::float',
|
||||
' THEN 1',
|
||||
' ELSE 0',
|
||||
' END',
|
||||
' ) AS infinities_count,',
|
||||
' sum(CASE WHEN {{=it._aggregationColumn}} = \'NaN\'::float THEN 1 ELSE 0 END) AS nans_count{{?}}',
|
||||
' FROM ({{=it._query}}) _cdb_aggregation_nulls',
|
||||
')'
|
||||
].join('\n'));
|
||||
@@ -39,7 +17,7 @@ var rankedCategoriesQueryTpl = dot.template([
|
||||
'categories AS(',
|
||||
' SELECT {{=it._column}} AS category, {{=it._aggregation}} AS value,',
|
||||
' row_number() OVER (ORDER BY {{=it._aggregation}} desc) as rank',
|
||||
' FROM filtered_source',
|
||||
' FROM ({{=it._query}}) _cdb_aggregation_all',
|
||||
' {{?it._aggregationColumn!==null}}WHERE {{=it._aggregationColumn}} IS NOT NULL{{?}}',
|
||||
' GROUP BY {{=it._column}}',
|
||||
' ORDER BY 2 DESC',
|
||||
@@ -58,46 +36,40 @@ var categoriesSummaryCountQueryTpl = dot.template([
|
||||
' SELECT count(1) AS categories_count',
|
||||
' FROM (',
|
||||
' SELECT {{=it._column}} AS category',
|
||||
' FROM filtered_source',
|
||||
' FROM ({{=it._query}}) _cdb_categories',
|
||||
' GROUP BY {{=it._column}}',
|
||||
' ) _cdb_categories_count',
|
||||
')'
|
||||
].join('\n'));
|
||||
|
||||
var rankedAggregationQueryTpl = dot.template([
|
||||
'SELECT CAST(category AS text), value, false as agg, nulls_count, min_val, max_val,',
|
||||
' count, categories_count{{?it._isFloatColumn}}, nans_count, infinities_count{{?}}',
|
||||
'SELECT CAST(category AS text), value, false as agg, nulls_count, min_val, max_val, count, categories_count',
|
||||
' FROM categories, summary, categories_summary_min_max, categories_summary_count',
|
||||
' WHERE rank < {{=it._limit}}',
|
||||
'UNION ALL',
|
||||
'SELECT \'Other\' category, sum(value), true as agg, nulls_count, min_val, max_val,',
|
||||
' count, categories_count{{?it._isFloatColumn}}, nans_count, infinities_count{{?}}',
|
||||
'SELECT \'Other\' category, sum(value), true as agg, nulls_count, min_val, max_val, count, categories_count',
|
||||
' FROM categories, summary, categories_summary_min_max, categories_summary_count',
|
||||
' WHERE rank >= {{=it._limit}}',
|
||||
'GROUP BY nulls_count, min_val, max_val, count,',
|
||||
' categories_count{{?it._isFloatColumn}}, nans_count, infinities_count{{?}}'
|
||||
'GROUP BY nulls_count, min_val, max_val, count, categories_count'
|
||||
].join('\n'));
|
||||
|
||||
var aggregationQueryTpl = dot.template([
|
||||
'SELECT CAST({{=it._column}} AS text) AS category, {{=it._aggregation}} AS value, false as agg,',
|
||||
' nulls_count, min_val, max_val, count, categories_count{{?it._isFloatColumn}}, nans_count, infinities_count{{?}}',
|
||||
'FROM filtered_source, summary, categories_summary_min_max, categories_summary_count',
|
||||
'GROUP BY category, nulls_count, min_val, max_val, count,',
|
||||
' categories_count{{?it._isFloatColumn}}, nans_count, infinities_count{{?}}',
|
||||
' nulls_count, min_val, max_val, count, categories_count',
|
||||
'FROM ({{=it._query}}) _cdb_aggregation_all, summary, categories_summary_min_max, categories_summary_count',
|
||||
'GROUP BY category, nulls_count, min_val, max_val, count, categories_count',
|
||||
'ORDER BY value DESC'
|
||||
].join('\n'));
|
||||
|
||||
var CATEGORIES_LIMIT = 6;
|
||||
|
||||
function Aggregation(query, options, queryRewriter, queryRewriteData, params, queries) {
|
||||
BaseOverviewsDataview.call(this, query, options, BaseDataview, queryRewriter, queryRewriteData, params, queries);
|
||||
function Aggregation(query, options, queryRewriter, queryRewriteData, params) {
|
||||
BaseOverviewsDataview.call(this, query, options, BaseDataview, queryRewriter, queryRewriteData, params);
|
||||
|
||||
this.query = query;
|
||||
this.queries = queries;
|
||||
this.column = options.column;
|
||||
this.aggregation = options.aggregation;
|
||||
this.aggregationColumn = options.aggregationColumn;
|
||||
this._isFloatColumn = null;
|
||||
}
|
||||
|
||||
Aggregation.prototype = Object.create(BaseOverviewsDataview.prototype);
|
||||
@@ -106,49 +78,27 @@ Aggregation.prototype.constructor = Aggregation;
|
||||
module.exports = Aggregation;
|
||||
|
||||
Aggregation.prototype.sql = function(psql, override, callback) {
|
||||
var self = this;
|
||||
|
||||
if (!callback) {
|
||||
callback = override;
|
||||
override = {};
|
||||
}
|
||||
|
||||
var _query = this.rewrittenQuery(this.query);
|
||||
var _aggregationColumn = this.aggregation !== 'count' ? this.aggregationColumn : null;
|
||||
|
||||
if (this.aggregationColumn && this._isFloatColumn === null) {
|
||||
this._isFloatColumn = false;
|
||||
this.getColumnType(psql, this.aggregationColumn, this.queries.no_filters, function (err, type) {
|
||||
if (!err && !!type) {
|
||||
self._isFloatColumn = type.float;
|
||||
}
|
||||
self.sql(psql, override, callback);
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
var aggregationSql;
|
||||
if (!!override.ownFilter) {
|
||||
aggregationSql = [
|
||||
"WITH",
|
||||
[
|
||||
filteredQueryTpl({
|
||||
_isFloatColumn: this._isFloatColumn,
|
||||
_query: _query,
|
||||
_column: this.column,
|
||||
_aggregationColumn: _aggregationColumn
|
||||
}),
|
||||
summaryQueryTpl({
|
||||
_isFloatColumn: this._isFloatColumn,
|
||||
_query: _query,
|
||||
_column: this.column,
|
||||
_aggregationColumn: _aggregationColumn
|
||||
_column: this.column
|
||||
}),
|
||||
rankedCategoriesQueryTpl({
|
||||
_query: _query,
|
||||
_column: this.column,
|
||||
_aggregation: this.getAggregationSql(),
|
||||
_aggregationColumn: _aggregationColumn
|
||||
_aggregationColumn: this.aggregation !== 'count' ? this.aggregationColumn : null
|
||||
}),
|
||||
categoriesSummaryMinMaxQueryTpl({
|
||||
_query: _query,
|
||||
@@ -160,7 +110,6 @@ Aggregation.prototype.sql = function(psql, override, callback) {
|
||||
})
|
||||
].join(',\n'),
|
||||
aggregationQueryTpl({
|
||||
_isFloatColumn: this._isFloatColumn,
|
||||
_query: _query,
|
||||
_column: this.column,
|
||||
_aggregation: this.getAggregationSql(),
|
||||
@@ -171,23 +120,15 @@ Aggregation.prototype.sql = function(psql, override, callback) {
|
||||
aggregationSql = [
|
||||
"WITH",
|
||||
[
|
||||
filteredQueryTpl({
|
||||
_isFloatColumn: this._isFloatColumn,
|
||||
_query: _query,
|
||||
_column: this.column,
|
||||
_aggregationColumn: _aggregationColumn
|
||||
}),
|
||||
summaryQueryTpl({
|
||||
_isFloatColumn: this._isFloatColumn,
|
||||
_query: _query,
|
||||
_column: this.column,
|
||||
_aggregationColumn: _aggregationColumn
|
||||
_column: this.column
|
||||
}),
|
||||
rankedCategoriesQueryTpl({
|
||||
_query: _query,
|
||||
_column: this.column,
|
||||
_aggregation: this.getAggregationSql(),
|
||||
_aggregationColumn: _aggregationColumn
|
||||
_aggregationColumn: this.aggregation !== 'count' ? this.aggregationColumn : null
|
||||
}),
|
||||
categoriesSummaryMinMaxQueryTpl({
|
||||
_query: _query,
|
||||
@@ -199,7 +140,6 @@ Aggregation.prototype.sql = function(psql, override, callback) {
|
||||
})
|
||||
].join(',\n'),
|
||||
rankedAggregationQueryTpl({
|
||||
_isFloatColumn: this._isFloatColumn,
|
||||
_query: _query,
|
||||
_column: this.column,
|
||||
_limit: CATEGORIES_LIMIT
|
||||
@@ -207,8 +147,6 @@ Aggregation.prototype.sql = function(psql, override, callback) {
|
||||
].join('\n');
|
||||
}
|
||||
|
||||
debug(aggregationSql);
|
||||
|
||||
return callback(null, aggregationSql);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
var _ = require('underscore');
|
||||
var BaseDataview = require('../base');
|
||||
|
||||
function BaseOverviewsDataview(query, queryOptions, BaseDataview, queryRewriter, queryRewriteData, options, queries) {
|
||||
function BaseOverviewsDataview(query, queryOptions, BaseDataview, queryRewriter, queryRewriteData, options) {
|
||||
this.BaseDataview = BaseDataview;
|
||||
this.query = query;
|
||||
this.queryOptions = queryOptions;
|
||||
this.queryRewriter = queryRewriter;
|
||||
this.queryRewriteData = queryRewriteData;
|
||||
this.options = options;
|
||||
this.queries = queries;
|
||||
this.baseDataview = new this.BaseDataview(this.query, this.queryOptions, this.queries);
|
||||
this.baseDataview = new this.BaseDataview(this.query, this.queryOptions);
|
||||
}
|
||||
|
||||
module.exports = BaseOverviewsDataview;
|
||||
|
||||
@@ -1,61 +1,34 @@
|
||||
var BaseOverviewsDataview = require('./base');
|
||||
var BaseDataview = require('../formula');
|
||||
var debug = require('debug')('windshaft:widget:formula:overview');
|
||||
|
||||
var dot = require('dot');
|
||||
dot.templateSettings.strip = false;
|
||||
|
||||
var formulaQueryTpls = {
|
||||
'count': dot.template([
|
||||
'SELECT',
|
||||
'sum(_feature_count) AS result,',
|
||||
'(SELECT count(1) FROM ({{=it._query}}) _cdb_formula_nulls WHERE {{=it._column}} IS NULL) AS nulls_count',
|
||||
'{{?it._isFloatColumn}},(SELECT count(1) FROM ({{=it._query}}) _cdb_formula_infinities',
|
||||
' WHERE {{=it._column}} = \'infinity\'::float OR {{=it._column}} = \'-infinity\'::float) AS infinities_count,',
|
||||
'(SELECT count(1) FROM ({{=it._query}}) _cdb_formula_nans',
|
||||
' WHERE {{=it._column}} = \'NaN\'::float) AS nans_count{{?}}',
|
||||
'FROM ({{=it._query}}) _cdb_formula'
|
||||
].join('\n')),
|
||||
'sum': dot.template([
|
||||
'SELECT',
|
||||
'sum({{=it._column}}*_feature_count) AS result,',
|
||||
'(SELECT count(1) FROM ({{=it._query}}) _cdb_formula_nulls WHERE {{=it._column}} IS NULL) AS nulls_count',
|
||||
'{{?it._isFloatColumn}},(SELECT count(1) FROM ({{=it._query}}) _cdb_formula_infinities',
|
||||
' WHERE {{=it._column}} = \'infinity\'::float OR {{=it._column}} = \'-infinity\'::float) AS infinities_count',
|
||||
',(SELECT count(1) FROM ({{=it._query}}) _cdb_formula_nans',
|
||||
' WHERE {{=it._column}} = \'NaN\'::float) AS nans_count{{?}}',
|
||||
'FROM ({{=it._query}}) _cdb_formula',
|
||||
'{{?it._isFloatColumn}}WHERE',
|
||||
' {{=it._column}} != \'infinity\'::float',
|
||||
'AND',
|
||||
' {{=it._column}} != \'-infinity\'::float',
|
||||
'AND',
|
||||
' {{=it._column}} != \'NaN\'::float{{?}}'
|
||||
].join('\n')),
|
||||
'avg': dot.template([
|
||||
'SELECT',
|
||||
'sum({{=it._column}}*_feature_count)/sum(_feature_count) AS result,',
|
||||
'(SELECT count(1) FROM ({{=it._query}}) _cdb_formula_nulls WHERE {{=it._column}} IS NULL) AS nulls_count',
|
||||
'{{?it._isFloatColumn}},(SELECT count(1) FROM ({{=it._query}}) _cdb_formula_infinities',
|
||||
' WHERE {{=it._column}} = \'infinity\'::float OR {{=it._column}} = \'-infinity\'::float) AS infinities_count',
|
||||
',(SELECT count(1) FROM ({{=it._query}}) _cdb_formula_nans',
|
||||
' WHERE {{=it._column}} = \'NaN\'::float) AS nans_count{{?}}',
|
||||
'FROM ({{=it._query}}) _cdb_formula',
|
||||
'{{?it._isFloatColumn}}WHERE',
|
||||
' {{=it._column}} != \'infinity\'::float',
|
||||
'AND',
|
||||
' {{=it._column}} != \'-infinity\'::float',
|
||||
'AND',
|
||||
' {{=it._column}} != \'NaN\'::float{{?}}'
|
||||
].join('\n')),
|
||||
'count': dot.template([
|
||||
'SELECT',
|
||||
'sum(_feature_count) AS result,',
|
||||
'(SELECT count(1) FROM ({{=it._query}}) _cdb_formula_nulls WHERE {{=it._column}} IS NULL) AS nulls_count',
|
||||
'FROM ({{=it._query}}) _cdb_formula'
|
||||
].join('\n')),
|
||||
'sum': dot.template([
|
||||
'SELECT',
|
||||
'sum({{=it._column}}*_feature_count) AS result,',
|
||||
'(SELECT count(1) FROM ({{=it._query}}) _cdb_formula_nulls WHERE {{=it._column}} IS NULL) AS nulls_count',
|
||||
'FROM ({{=it._query}}) _cdb_formula'
|
||||
].join('\n')),
|
||||
'avg': dot.template([
|
||||
'SELECT',
|
||||
'sum({{=it._column}}*_feature_count)/sum(_feature_count) AS result,',
|
||||
'(SELECT count(1) FROM ({{=it._query}}) _cdb_formula_nulls WHERE {{=it._column}} IS NULL) AS nulls_count',
|
||||
'FROM ({{=it._query}}) _cdb_formula'
|
||||
].join('\n')),
|
||||
};
|
||||
|
||||
function Formula(query, options, queryRewriter, queryRewriteData, params, queries) {
|
||||
BaseOverviewsDataview.call(this, query, options, BaseDataview, queryRewriter, queryRewriteData, params, queries);
|
||||
function Formula(query, options, queryRewriter, queryRewriteData, params) {
|
||||
BaseOverviewsDataview.call(this, query, options, BaseDataview, queryRewriter, queryRewriteData, params);
|
||||
this.column = options.column || '1';
|
||||
this.operation = options.operation;
|
||||
this._isFloatColumn = null;
|
||||
this.queries = queries;
|
||||
}
|
||||
|
||||
Formula.prototype = Object.create(BaseOverviewsDataview.prototype);
|
||||
@@ -63,38 +36,21 @@ Formula.prototype.constructor = Formula;
|
||||
|
||||
module.exports = Formula;
|
||||
|
||||
Formula.prototype.sql = function (psql, override, callback) {
|
||||
var self = this;
|
||||
Formula.prototype.sql = function(psql, override, callback) {
|
||||
var formulaQueryTpl = formulaQueryTpls[this.operation];
|
||||
|
||||
if (formulaQueryTpl) {
|
||||
if ( formulaQueryTpl ) {
|
||||
// supported formula for use with overviews
|
||||
if (this._isFloatColumn === null) {
|
||||
this._isFloatColumn = false;
|
||||
this.getColumnType(psql, this.column, this.queries.no_filters, function (err, type) {
|
||||
if (!err && !!type) {
|
||||
self._isFloatColumn = type.float;
|
||||
}
|
||||
self.sql(psql, override, callback);
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
var formulaSql = formulaQueryTpl({
|
||||
_isFloatColumn: this._isFloatColumn,
|
||||
_query: this.rewrittenQuery(this.query),
|
||||
_query: this.rewrittenQuery(this.query),
|
||||
_operation: this.operation,
|
||||
_column: this.column
|
||||
_column: this.column
|
||||
});
|
||||
|
||||
callback = callback || override;
|
||||
|
||||
debug(formulaSql);
|
||||
|
||||
return callback(null, formulaSql);
|
||||
}
|
||||
|
||||
|
||||
// default behaviour
|
||||
return this.defaultSql(psql, override, callback);
|
||||
};
|
||||
|
||||
@@ -1,35 +1,23 @@
|
||||
var _ = require('underscore');
|
||||
var BaseOverviewsDataview = require('./base');
|
||||
var BaseDataview = require('../histogram');
|
||||
var debug = require('debug')('windshaft:dataview:histogram:overview');
|
||||
|
||||
var dot = require('dot');
|
||||
dot.templateSettings.strip = false;
|
||||
|
||||
var columnTypeQueryTpl = dot.template(
|
||||
'SELECT pg_typeof({{=it.column}})::oid FROM ({{=it.query}}) _cdb_histogram_column_type limit 1'
|
||||
);
|
||||
|
||||
var BIN_MIN_NUMBER = 6;
|
||||
var BIN_MAX_NUMBER = 48;
|
||||
|
||||
var filteredQueryTpl = dot.template([
|
||||
'filtered_source AS (',
|
||||
' SELECT *',
|
||||
' FROM ({{=it._query}}) _cdb_filtered_source',
|
||||
' WHERE',
|
||||
' {{=it._column}} IS NOT NULL',
|
||||
' {{?it._isFloatColumn}}AND',
|
||||
' {{=it._column}} != \'infinity\'::float',
|
||||
' AND',
|
||||
' {{=it._column}} != \'-infinity\'::float',
|
||||
' AND',
|
||||
' {{=it._column}} != \'NaN\'::float{{?}}',
|
||||
')'
|
||||
].join(' \n'));
|
||||
|
||||
var basicsQueryTpl = dot.template([
|
||||
'basics AS (',
|
||||
' SELECT',
|
||||
' max({{=it._column}}) AS max_val, min({{=it._column}}) AS min_val,',
|
||||
' sum({{=it._column}}*_feature_count)/sum(_feature_count) AS avg_val, sum(_feature_count) AS total_rows',
|
||||
' FROM filtered_source',
|
||||
' FROM ({{=it._query}}) _cdb_basics',
|
||||
')'
|
||||
].join(' \n'));
|
||||
|
||||
@@ -38,7 +26,7 @@ var overrideBasicsQueryTpl = dot.template([
|
||||
' SELECT',
|
||||
' max({{=it._end}}) AS max_val, min({{=it._start}}) AS min_val,',
|
||||
' sum({{=it._column}}*_feature_count)/sum(_feature_count) AS avg_val, sum(_feature_count) AS total_rows',
|
||||
' FROM filtered_source',
|
||||
' FROM ({{=it._query}}) _cdb_basics',
|
||||
')'
|
||||
].join('\n'));
|
||||
|
||||
@@ -49,7 +37,7 @@ var iqrQueryTpl = dot.template([
|
||||
' SELECT quartile, max(_cdb_iqr_column) AS quartile_max from (',
|
||||
' SELECT {{=it._column}} AS _cdb_iqr_column, ntile(4) over (order by {{=it._column}}',
|
||||
' ) AS quartile',
|
||||
' FROM filtered_source) _cdb_quartiles',
|
||||
' FROM ({{=it._query}}) _cdb_rank) _cdb_quartiles',
|
||||
' WHERE quartile = 1 or quartile = 3',
|
||||
' GROUP BY quartile',
|
||||
' ) _cdb_iqr',
|
||||
@@ -68,7 +56,7 @@ var binsQueryTpl = dot.template([
|
||||
' )',
|
||||
' )',
|
||||
' END AS bins_number',
|
||||
' FROM basics, iqrange, filtered_source',
|
||||
' FROM basics, iqrange, ({{=it._query}}) _cdb_bins',
|
||||
' LIMIT 1',
|
||||
')'
|
||||
].join('\n'));
|
||||
@@ -88,34 +76,11 @@ var nullsQueryTpl = dot.template([
|
||||
')'
|
||||
].join('\n'));
|
||||
|
||||
var infinitiesQueryTpl = dot.template([
|
||||
'infinities AS (',
|
||||
' SELECT',
|
||||
' count(*) AS infinities_count',
|
||||
' FROM ({{=it._query}}) _cdb_histogram_infinities',
|
||||
' WHERE',
|
||||
' {{=it._column}} = \'infinity\'::float',
|
||||
' OR',
|
||||
' {{=it._column}} = \'-infinity\'::float',
|
||||
')'
|
||||
].join('\n'));
|
||||
|
||||
var nansQueryTpl = dot.template([
|
||||
'nans AS (',
|
||||
' SELECT',
|
||||
' count(*) AS nans_count',
|
||||
' FROM ({{=it._query}}) _cdb_histogram_infinities',
|
||||
' WHERE {{=it._column}} = \'NaN\'::float',
|
||||
')'
|
||||
].join('\n'));
|
||||
|
||||
var histogramQueryTpl = dot.template([
|
||||
'SELECT',
|
||||
' (max_val - min_val) / cast(bins_number as float) AS bin_width,',
|
||||
' bins_number,',
|
||||
' nulls_count,',
|
||||
' {{?it._isFloatColumn}}infinities_count,',
|
||||
' nans_count,{{?}}',
|
||||
' avg_val,',
|
||||
' CASE WHEN min_val = max_val',
|
||||
' THEN 0',
|
||||
@@ -125,14 +90,14 @@ var histogramQueryTpl = dot.template([
|
||||
' max({{=it._column}})::numeric AS max,',
|
||||
' sum({{=it._column}}*_feature_count)/sum(_feature_count)::numeric AS avg,',
|
||||
' sum(_feature_count) AS freq',
|
||||
'FROM filtered_source, basics, nulls, bins{{?it._isFloatColumn}},infinities, nans{{?}}',
|
||||
'FROM ({{=it._query}}) _cdb_histogram, basics, nulls, bins',
|
||||
'WHERE {{=it._column}} IS NOT NULL',
|
||||
'GROUP BY bin, bins_number, bin_width, nulls_count, avg_val',
|
||||
' {{?it._isFloatColumn}}, infinities_count, nans_count{{?}}',
|
||||
'ORDER BY bin'
|
||||
].join('\n'));
|
||||
|
||||
function Histogram(query, options, queryRewriter, queryRewriteData, params, queries) {
|
||||
BaseOverviewsDataview.call(this, query, options, BaseDataview, queryRewriter, queryRewriteData, params, queries);
|
||||
BaseOverviewsDataview.call(this, query, options, BaseDataview, queryRewriter, queryRewriteData, params);
|
||||
|
||||
this.query = query;
|
||||
this.queries = queries;
|
||||
@@ -147,23 +112,36 @@ Histogram.prototype.constructor = Histogram;
|
||||
|
||||
module.exports = Histogram;
|
||||
|
||||
Histogram.prototype.sql = function(psql, override, callback) {
|
||||
var self = this;
|
||||
|
||||
var DATE_OIDS = {
|
||||
1082: true,
|
||||
1114: true,
|
||||
1184: true
|
||||
};
|
||||
|
||||
Histogram.prototype.sql = function(psql, override, callback) {
|
||||
if (!callback) {
|
||||
callback = override;
|
||||
override = {};
|
||||
}
|
||||
|
||||
var self = this;
|
||||
|
||||
var _column = this.column;
|
||||
|
||||
var columnTypeQuery = columnTypeQueryTpl({
|
||||
column: _column, query: this.rewrittenQuery(this.queries.no_filters)
|
||||
});
|
||||
|
||||
if (this._columnType === null) {
|
||||
this.getColumnType(psql, this.column, this.queries.no_filters, function (err, type) {
|
||||
psql.query(columnTypeQuery, function(err, result) {
|
||||
// assume numeric, will fail later
|
||||
self._columnType = 'numeric';
|
||||
if (!err && !!type) {
|
||||
self._columnType = Object.keys(type).find(function (key) {
|
||||
return type[key];
|
||||
});
|
||||
if (!err && !!result.rows[0]) {
|
||||
var pgType = result.rows[0].pg_typeof;
|
||||
if (DATE_OIDS.hasOwnProperty(pgType)) {
|
||||
self._columnType = 'date';
|
||||
}
|
||||
}
|
||||
self.sql(psql, override, callback);
|
||||
}, true); // use read-only transaction
|
||||
@@ -176,24 +154,11 @@ Histogram.prototype.sql = function(psql, override, callback) {
|
||||
return this.defaultSql(psql, override, callback);
|
||||
}
|
||||
|
||||
var histogramSql = this._buildQuery(override);
|
||||
|
||||
return callback(null, histogramSql);
|
||||
};
|
||||
|
||||
Histogram.prototype._buildQuery = function (override) {
|
||||
var filteredQuery, basicsQuery, binsQuery;
|
||||
var _column = this.column;
|
||||
var _query = this.rewrittenQuery(this.query);
|
||||
|
||||
filteredQuery = filteredQueryTpl({
|
||||
_isFloatColumn: this._columnType === 'float',
|
||||
_query: _query,
|
||||
_column: _column
|
||||
});
|
||||
var basicsQuery, binsQuery;
|
||||
|
||||
if (this._shouldOverride(override)) {
|
||||
debug('overriding with %j', override);
|
||||
if (override && _.has(override, 'start') && _.has(override, 'end') && _.has(override, 'bins')) {
|
||||
basicsQuery = overrideBasicsQueryTpl({
|
||||
_query: _query,
|
||||
_column: _column,
|
||||
@@ -212,7 +177,7 @@ Histogram.prototype._buildQuery = function (override) {
|
||||
_column: _column
|
||||
});
|
||||
|
||||
if (this._shouldOverrideBins(override)) {
|
||||
if (override && _.has(override, 'bins')) {
|
||||
binsQuery = [
|
||||
overrideBinsQueryTpl({
|
||||
_bins: override.bins
|
||||
@@ -233,50 +198,22 @@ Histogram.prototype._buildQuery = function (override) {
|
||||
}
|
||||
}
|
||||
|
||||
var cteSql = [
|
||||
filteredQuery,
|
||||
basicsQuery,
|
||||
binsQuery,
|
||||
nullsQueryTpl({
|
||||
_query: _query,
|
||||
_column: _column
|
||||
})
|
||||
];
|
||||
|
||||
if (this._columnType === 'float') {
|
||||
cteSql.push(
|
||||
infinitiesQueryTpl({
|
||||
_query: _query,
|
||||
_column: _column
|
||||
}),
|
||||
nansQueryTpl({
|
||||
_query: _query,
|
||||
_column: _column
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
var histogramSql = [
|
||||
"WITH",
|
||||
cteSql.join(',\n'),
|
||||
[
|
||||
basicsQuery,
|
||||
binsQuery,
|
||||
nullsQueryTpl({
|
||||
_query: _query,
|
||||
_column: _column
|
||||
})
|
||||
].join(',\n'),
|
||||
histogramQueryTpl({
|
||||
_isFloatColumn: this._columnType === 'float',
|
||||
_query: _query,
|
||||
_column: _column
|
||||
})
|
||||
].join('\n');
|
||||
|
||||
debug(histogramSql);
|
||||
|
||||
return histogramSql;
|
||||
return callback(null, histogramSql);
|
||||
};
|
||||
|
||||
Histogram.prototype._shouldOverride = function (override) {
|
||||
return override && _.has(override, 'start') && _.has(override, 'end') && _.has(override, 'bins');
|
||||
};
|
||||
|
||||
Histogram.prototype._shouldOverrideBins = function (override) {
|
||||
return override && _.has(override, 'bins');
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
var BaseOverviewsDataview = require('./base');
|
||||
var BaseDataview = require('../list');
|
||||
|
||||
function List(query, options, queryRewriter, queryRewriteData, params, queries) {
|
||||
BaseOverviewsDataview.call(this, query, options, BaseDataview, queryRewriter, queryRewriteData, params, queries);
|
||||
function List(query, options, queryRewriter, queryRewriteData, params) {
|
||||
BaseOverviewsDataview.call(this, query, options, BaseDataview, queryRewriter, queryRewriteData, params);
|
||||
}
|
||||
|
||||
List.prototype = Object.create(BaseOverviewsDataview.prototype);
|
||||
|
||||
@@ -8,7 +8,7 @@ var filterQueryTpl = dot.template([
|
||||
].join('\n'));
|
||||
|
||||
var bboxFilterTpl = dot.template(
|
||||
'ST_Intersects({{=it._column}}, ST_Transform(ST_MakeEnvelope({{=it._bbox}}, 4326), {{=it._srid}}))'
|
||||
'{{=it._column}} && ST_Transform(ST_MakeEnvelope({{=it._bbox}}, 4326), {{=it._srid}})'
|
||||
);
|
||||
|
||||
var LATITUDE_MAX_VALUE = 85.0511287798066;
|
||||
@@ -66,8 +66,7 @@ function getBoundingBoxes(west, south, east, north) {
|
||||
bboxes.push([west, south, east, north]);
|
||||
} else {
|
||||
bboxes.push([west, south, 180, north]);
|
||||
// here we assume west,east have been adjusted => west >= -180 => east > 180
|
||||
bboxes.push([-180, south, east - 360, north]);
|
||||
bboxes.push([-180, south, east % 180, north]);
|
||||
}
|
||||
|
||||
return bboxes;
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
function MapConfigBufferSizeAdapter() {
|
||||
this.formats = ['png', 'png32', 'mvt', 'grid.json'];
|
||||
}
|
||||
|
||||
module.exports = MapConfigBufferSizeAdapter;
|
||||
|
||||
MapConfigBufferSizeAdapter.prototype.getMapConfig = function (user, requestMapConfig, params, context, callback) {
|
||||
if (!context.templateParams || !context.templateParams.buffersize) {
|
||||
return callback(null, requestMapConfig);
|
||||
}
|
||||
|
||||
this.formats.forEach(function (format) {
|
||||
if (Number.isFinite(context.templateParams.buffersize[format])) {
|
||||
if (requestMapConfig.buffersize === undefined) {
|
||||
requestMapConfig.buffersize = {};
|
||||
}
|
||||
|
||||
requestMapConfig.buffersize[format] = context.templateParams.buffersize[format];
|
||||
}
|
||||
});
|
||||
|
||||
setImmediate(function () {
|
||||
callback(null, requestMapConfig);
|
||||
});
|
||||
};
|
||||
@@ -43,6 +43,7 @@ MapConfigNamedLayersAdapter.prototype.getMapConfig = function (user, requestMapC
|
||||
|
||||
if (nestedNamedLayers.length > 0) {
|
||||
var nestedNamedMapsError = new Error('Nested named layers are not allowed');
|
||||
// nestedNamedMapsError.http_status = 400;
|
||||
return done(nestedNamedMapsError);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,13 @@ var dot = require('dot');
|
||||
dot.templateSettings.strip = false;
|
||||
var queue = require('queue-async');
|
||||
var PSQL = require('cartodb-psql');
|
||||
/**
|
||||
* cartodb-psql creates `global.Promise` as an empty constructor.
|
||||
* However, `turbo-carto` relies on a polyfil that fails to create the polyfil
|
||||
* as it finds `global.Promise` but it doesn't find `Promise.resolve`.
|
||||
*/
|
||||
global.Promise = global.Promise || function() {};
|
||||
global.Promise.resolve = global.Promise.resolve || function() {};
|
||||
var turboCarto = require('turbo-carto');
|
||||
|
||||
var SubstitutionTokens = require('../../../utils/substitution-tokens');
|
||||
|
||||
@@ -26,7 +26,7 @@ CreateLayergroupMapConfigProvider.prototype.getMapConfig = function(callback) {
|
||||
var context = {};
|
||||
step(
|
||||
function prepareContextLimits() {
|
||||
self.userLimitsApi.getRenderLimits(self.user, self.params.api_key, this);
|
||||
self.userLimitsApi.getRenderLimits(self.user, this);
|
||||
},
|
||||
function handleRenderLimits(err, renderLimits) {
|
||||
assert.ifError(err);
|
||||
|
||||
@@ -27,7 +27,7 @@ MapStoreMapConfigProvider.prototype.getMapConfig = function(callback) {
|
||||
var context = {};
|
||||
step(
|
||||
function prepareContextLimits() {
|
||||
self.userLimitsApi.getRenderLimits(self.user, self.params.api_key, this);
|
||||
self.userLimitsApi.getRenderLimits(self.user, this);
|
||||
},
|
||||
function handleRenderLimits(err, renderLimits) {
|
||||
assert.ifError(err);
|
||||
|
||||
@@ -90,7 +90,6 @@ NamedMapMapConfigProvider.prototype.getMapConfig = function(callback) {
|
||||
},
|
||||
function instantiateTemplate(err, templateParams) {
|
||||
assert.ifError(err);
|
||||
context.templateParams = templateParams;
|
||||
return self.templateMaps.instance(self.template, templateParams);
|
||||
},
|
||||
function prepareAdapterMapConfig(err, requestMapConfig) {
|
||||
@@ -114,7 +113,7 @@ NamedMapMapConfigProvider.prototype.getMapConfig = function(callback) {
|
||||
function prepareContextLimits(err, _mapConfig) {
|
||||
assert.ifError(err);
|
||||
mapConfig = _mapConfig;
|
||||
self.userLimitsApi.getRenderLimits(self.owner, self.params.api_key, this);
|
||||
self.userLimitsApi.getRenderLimits(self.owner, this);
|
||||
},
|
||||
function cacheAndReturnMapConfig(err, renderLimits) {
|
||||
self.err = err;
|
||||
|
||||
@@ -35,15 +35,12 @@ var timeoutErrorTile = require('fs').readFileSync(timeoutErrorTilePath, {encodin
|
||||
|
||||
var SqlWrapMapConfigAdapter = require('./models/mapconfig/adapter/sql-wrap-mapconfig-adapter');
|
||||
var MapConfigNamedLayersAdapter = require('./models/mapconfig/adapter/mapconfig-named-layers-adapter');
|
||||
var MapConfigBufferSizeAdapter = require('./models/mapconfig/adapter/mapconfig-buffer-size-adapter');
|
||||
var AnalysisMapConfigAdapter = require('./models/mapconfig/adapter/analysis-mapconfig-adapter');
|
||||
var MapConfigOverviewsAdapter = require('./models/mapconfig/adapter/mapconfig-overviews-adapter');
|
||||
var TurboCartoAdapter = require('./models/mapconfig/adapter/turbo-carto-adapter');
|
||||
var DataviewsWidgetsAdapter = require('./models/mapconfig/adapter/dataviews-widgets-adapter');
|
||||
var MapConfigAdapter = require('./models/mapconfig/adapter');
|
||||
|
||||
var StatsBackend = require('./backends/stats');
|
||||
|
||||
module.exports = function(serverOptions) {
|
||||
// Make stats client globally accessible
|
||||
global.statsClient = StatsClient.getInstance(serverOptions.statsd);
|
||||
@@ -118,27 +115,8 @@ module.exports = function(serverOptions) {
|
||||
var onTileErrorStrategy;
|
||||
if (global.environment.enabledFeatures.onTileErrorStrategy !== false) {
|
||||
onTileErrorStrategy = function onTileErrorStrategy$TimeoutTile(err, tile, headers, stats, format, callback) {
|
||||
|
||||
function isRenderTimeoutError (err) {
|
||||
return err.message === 'Render timed out';
|
||||
}
|
||||
|
||||
function isDatasourceTimeoutError (err) {
|
||||
return err.message && err.message.match(/canceling statement due to statement timeout/i);
|
||||
}
|
||||
|
||||
function isTimeoutError (err) {
|
||||
return isRenderTimeoutError(err) || isDatasourceTimeoutError(err);
|
||||
}
|
||||
|
||||
function isRasterFormat (format) {
|
||||
return format === 'png' || format === 'jpg';
|
||||
}
|
||||
|
||||
if (isTimeoutError(err) && isRasterFormat(format)) {
|
||||
return callback(null, timeoutErrorTile, {
|
||||
'Content-Type': 'image/png',
|
||||
}, {});
|
||||
if (err && err.message === 'Render timed out' && format === 'png') {
|
||||
return callback(null, timeoutErrorTile, { 'Content-Type': 'image/png' }, {});
|
||||
} else {
|
||||
return callback(err, tile, headers, stats);
|
||||
}
|
||||
@@ -172,14 +150,11 @@ module.exports = function(serverOptions) {
|
||||
|
||||
var analysisBackend = new AnalysisBackend(metadataBackend, serverOptions.analysis);
|
||||
|
||||
var statsBackend = new StatsBackend();
|
||||
|
||||
var layergroupAffectedTablesCache = new LayergroupAffectedTablesCache();
|
||||
app.layergroupAffectedTablesCache = layergroupAffectedTablesCache;
|
||||
|
||||
var mapConfigAdapter = new MapConfigAdapter(
|
||||
new MapConfigNamedLayersAdapter(templateMaps, pgConnection),
|
||||
new MapConfigBufferSizeAdapter(),
|
||||
new SqlWrapMapConfigAdapter(),
|
||||
new DataviewsWidgetsAdapter(),
|
||||
new AnalysisMapConfigAdapter(analysisBackend),
|
||||
@@ -232,8 +207,7 @@ module.exports = function(serverOptions) {
|
||||
surrogateKeysCache,
|
||||
userLimitsApi,
|
||||
layergroupAffectedTablesCache,
|
||||
mapConfigAdapter,
|
||||
statsBackend
|
||||
mapConfigAdapter
|
||||
).register(app);
|
||||
|
||||
new controller.NamedMaps(
|
||||
@@ -329,25 +303,6 @@ function bootstrap(opts) {
|
||||
app.enable('jsonp callback');
|
||||
app.disable('x-powered-by');
|
||||
app.disable('etag');
|
||||
|
||||
// Fix: https://github.com/CartoDB/Windshaft-cartodb/issues/705
|
||||
// See: http://expressjs.com/en/4x/api.html#app.set
|
||||
app.set('json replacer', function (key, value) {
|
||||
if (value !== value) {
|
||||
return 'NaN';
|
||||
}
|
||||
|
||||
if (value === Infinity) {
|
||||
return 'Infinity';
|
||||
}
|
||||
|
||||
if (value === -Infinity) {
|
||||
return '-Infinity';
|
||||
}
|
||||
|
||||
return value;
|
||||
});
|
||||
|
||||
app.use(bodyParser.json());
|
||||
|
||||
app.use(function bootstrap$prepareRequestResponse(req, res, next) {
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
function prepareQuery(sql) {
|
||||
var affectedTableRegexCache = {
|
||||
bbox: /!bbox!/g,
|
||||
scale_denominator: /!scale_denominator!/g,
|
||||
pixel_width: /!pixel_width!/g,
|
||||
pixel_height: /!pixel_height!/g
|
||||
};
|
||||
|
||||
return sql
|
||||
.replace(affectedTableRegexCache.bbox, 'ST_MakeEnvelope(0,0,0,0)')
|
||||
.replace(affectedTableRegexCache.scale_denominator, '0')
|
||||
.replace(affectedTableRegexCache.pixel_width, '1')
|
||||
.replace(affectedTableRegexCache.pixel_height, '1');
|
||||
}
|
||||
|
||||
module.exports.extractTableNames = function extractTableNames(query) {
|
||||
return [
|
||||
'SELECT * FROM CDB_QueryTablesText($windshaft$',
|
||||
prepareQuery(query),
|
||||
'$windshaft$) as tablenames'
|
||||
].join('');
|
||||
};
|
||||
|
||||
module.exports.getQueryRowCount = function getQueryRowEstimation(query) {
|
||||
return 'select CDB_EstimateRowCount(\'' + query + '\') as rows';
|
||||
};
|
||||
19
package.json
19
package.json
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "windshaft-cartodb",
|
||||
"version": "3.12.2",
|
||||
"version": "3.6.7",
|
||||
"description": "A map tile server for CartoDB",
|
||||
"keywords": [
|
||||
"cartodb"
|
||||
@@ -16,15 +16,14 @@
|
||||
"contributors": [
|
||||
"Simon Tokumine <simon@vizzuality.com>",
|
||||
"Javi Santana <jsantana@vizzuality.com>",
|
||||
"Sandro Santilli <strk@vizzuality.com>",
|
||||
"Carlos Matallín <matallo@carto.com>"
|
||||
"Sandro Santilli <strk@vizzuality.com>"
|
||||
],
|
||||
"dependencies": {
|
||||
"body-parser": "~1.14.0",
|
||||
"camshaft": "0.55.7",
|
||||
"camshaft": "0.54.4",
|
||||
"cartodb-psql": "0.10.1",
|
||||
"cartodb-query-tables": "0.2.0",
|
||||
"cartodb-redis": "0.14.0",
|
||||
"cartodb-redis": "0.13.2",
|
||||
"debug": "~2.2.0",
|
||||
"dot": "~1.0.2",
|
||||
"express": "~4.13.3",
|
||||
@@ -38,23 +37,21 @@
|
||||
"request": "~2.79.0",
|
||||
"step": "~0.0.6",
|
||||
"step-profiler": "~0.3.0",
|
||||
"turbo-carto": "0.19.2",
|
||||
"turbo-carto": "0.19.0",
|
||||
"underscore": "~1.6.0",
|
||||
"windshaft": "3.3.1",
|
||||
"windshaft": "3.1.2",
|
||||
"yargs": "~5.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"istanbul": "~0.4.3",
|
||||
"jshint": "~2.9.4",
|
||||
"mocha": "~3.4.1",
|
||||
"moment": "~2.18.1",
|
||||
"jshint": "~2.6.0",
|
||||
"mocha": "~1.21.4",
|
||||
"nock": "~2.11.0",
|
||||
"redis": "~0.12.1",
|
||||
"semver": "~1.1.4",
|
||||
"strftime": "~0.8.2"
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "jshint lib test",
|
||||
"preinstall": "make pre-install",
|
||||
"test": "make test-all"
|
||||
},
|
||||
|
||||
@@ -1,441 +0,0 @@
|
||||
require('../support/test_helper');
|
||||
|
||||
var fs = require('fs');
|
||||
var assert = require('../support/assert');
|
||||
var TestClient = require('../support/test-client');
|
||||
var mapnik = require('windshaft').mapnik;
|
||||
var IMAGE_TOLERANCE_PER_MIL = 5;
|
||||
|
||||
var CARTOCSS_LABELS = [
|
||||
'#layer {',
|
||||
' polygon-fill: #374C70;',
|
||||
' polygon-opacity: 0.9;',
|
||||
' line-width: 1;',
|
||||
' line-color: #FFF;',
|
||||
' line-opacity: 0.5;',
|
||||
'}',
|
||||
'#layer::labels {',
|
||||
' text-name: [name];',
|
||||
' text-face-name: \'DejaVu Sans Book\';',
|
||||
' text-size: 20;',
|
||||
' text-fill: #FFFFFF;',
|
||||
' text-label-position-tolerance: 0;',
|
||||
' text-halo-radius: 1;',
|
||||
' text-halo-fill: #6F808D;',
|
||||
' text-dy: -10;',
|
||||
' text-allow-overlap: true;',
|
||||
' text-placement: point;',
|
||||
' text-placement-type: dummy;',
|
||||
'}'
|
||||
].join('\n');
|
||||
|
||||
function createMapConfig (bufferSize, cartocss) {
|
||||
cartocss = cartocss || CARTOCSS_LABELS;
|
||||
|
||||
return {
|
||||
version: '1.6.0',
|
||||
buffersize: bufferSize,
|
||||
layers: [{
|
||||
type: "cartodb",
|
||||
options: {
|
||||
sql: [
|
||||
'select',
|
||||
' *',
|
||||
'from',
|
||||
' populated_places_simple_reduced',
|
||||
].join('\n'),
|
||||
cartocss: cartocss,
|
||||
cartocss_version: '2.3.0',
|
||||
interactivity: 'cartodb_id'
|
||||
}
|
||||
}]
|
||||
};
|
||||
}
|
||||
|
||||
describe('buffer size per format', function () {
|
||||
var testCases = [
|
||||
{
|
||||
desc: 'should get png tile using buffer-size 0',
|
||||
coords: { z: 7, x: 64, y: 48 },
|
||||
format: 'png',
|
||||
fixturePath: './test/fixtures/buffer-size/tile-7.64.48-buffer-size-0.png',
|
||||
mapConfig: createMapConfig({ png: 0, 'grid.json': 0 }),
|
||||
assert: function (tile, callback) {
|
||||
assert.imageIsSimilarToFile(tile, this.fixturePath, IMAGE_TOLERANCE_PER_MIL, callback);
|
||||
}
|
||||
},
|
||||
{
|
||||
desc: 'should get png tile using buffer-size 128',
|
||||
coords: { z: 7, x: 64, y: 48 },
|
||||
format: 'png',
|
||||
fixturePath: './test/fixtures/buffer-size/tile-7.64.48-buffer-size-128.png',
|
||||
mapConfig: createMapConfig({ png: 128, 'grid.json': 128 }),
|
||||
assert: function (tile, callback) {
|
||||
assert.imageIsSimilarToFile(tile, this.fixturePath, IMAGE_TOLERANCE_PER_MIL, callback);
|
||||
}
|
||||
},
|
||||
{
|
||||
desc: 'should get mvt tile using buffer-size 0',
|
||||
coords: { z: 7, x: 64, y: 48 },
|
||||
format: 'mvt',
|
||||
fixturePath: './test/fixtures/buffer-size/tile-7.64.48-buffer-size-0.mvt',
|
||||
mapConfig: createMapConfig({ mvt: 0 }),
|
||||
assert: function (tile, callback) {
|
||||
var tileJSON = tile.toJSON();
|
||||
var features = tileJSON[0].features;
|
||||
assert.equal(features.length, 1);
|
||||
callback();
|
||||
}
|
||||
},
|
||||
{
|
||||
desc: 'should get mvt tile using buffer-size 128',
|
||||
coords: { z: 7, x: 64, y: 48 },
|
||||
format: 'mvt',
|
||||
fixturePath: './test/fixtures/buffer-size/tile-7.64.48-buffer-size-128.mvt',
|
||||
mapConfig: createMapConfig({ mvt: 128 }),
|
||||
assert: function (tile, callback) {
|
||||
var tileJSON = tile.toJSON();
|
||||
var features = tileJSON[0].features;
|
||||
assert.equal(features.length, 9);
|
||||
callback();
|
||||
}
|
||||
},
|
||||
{
|
||||
desc: 'should get grid.json tile using buffer-size 0 overriden by template params',
|
||||
coords: { z: 7, x: 64, y: 48 },
|
||||
format: 'grid.json',
|
||||
layers: [0],
|
||||
fixturePath: './test/fixtures/buffer-size/tile-grid.json.7.64.48-buffer-size-0.grid.json',
|
||||
mapConfig: createMapConfig({ 'grid.json': 0 }),
|
||||
assert: function (tile, callback) {
|
||||
assert.utfgridEqualsFile(tile, this.fixturePath, 2,callback);
|
||||
}
|
||||
},
|
||||
{
|
||||
desc: 'should get grid.json tile using buffer-size 128 overriden by template params',
|
||||
coords: { z: 7, x: 64, y: 48 },
|
||||
format: 'grid.json',
|
||||
layers: [0],
|
||||
fixturePath: './test/fixtures/buffer-size/tile-7.64.48-buffer-size-128.grid.json',
|
||||
mapConfig: createMapConfig({ 'grid.json': 128 }),
|
||||
assert: function (tile, callback) {
|
||||
assert.utfgridEqualsFile(tile, this.fixturePath, 2, callback);
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
testCases.forEach(function (test) {
|
||||
it(test.desc, function (done) {
|
||||
var testClient = new TestClient(test.mapConfig, 1234);
|
||||
var coords = test.coords;
|
||||
var options = {
|
||||
format: test.format,
|
||||
layers: test.layers
|
||||
};
|
||||
testClient.getTile(coords.z, coords.x, coords.y, options, function (err, res, tile) {
|
||||
assert.ifError(err);
|
||||
// To generate images use:
|
||||
// tile.save(test.fixturePath);
|
||||
test.assert(tile, function (err) {
|
||||
assert.ifError(err);
|
||||
testClient.drain(done);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function createBufferSizeTemplate (name, buffersize, placeholders, cartocss) {
|
||||
cartocss = cartocss || CARTOCSS_LABELS;
|
||||
|
||||
return {
|
||||
"version": "0.0.1",
|
||||
"name": name,
|
||||
"placeholders": placeholders || {
|
||||
"buffersize": {
|
||||
"type": "number",
|
||||
"default": 0
|
||||
}
|
||||
},
|
||||
"layergroup": createMapConfig(buffersize)
|
||||
};
|
||||
}
|
||||
|
||||
describe('buffer size per format for named maps', function () {
|
||||
var testCases = [
|
||||
{
|
||||
desc: 'should get png tile using buffer-size 0 (default value in template)',
|
||||
coords: { z: 7, x: 64, y: 48 },
|
||||
format: 'png',
|
||||
fixturePath: './test/fixtures/buffer-size/tile-7.64.48-buffer-size-0.png',
|
||||
template: createBufferSizeTemplate('named-default-buffer-size', {png: '<%= buffersize %>'}),
|
||||
assert: function (tile, callback) {
|
||||
assert.imageIsSimilarToFile(tile, this.fixturePath, IMAGE_TOLERANCE_PER_MIL, callback);
|
||||
}
|
||||
},
|
||||
{
|
||||
desc: 'should get png tile using buffer-size 128 (placehoder value)',
|
||||
coords: { z: 7, x: 64, y: 48 },
|
||||
format: 'png',
|
||||
placeholders: { buffersize: 128 },
|
||||
fixturePath: './test/fixtures/buffer-size/tile-7.64.48-buffer-size-128.png',
|
||||
template: createBufferSizeTemplate('named-custom-buffer-size', { png: '<%= buffersize %>'}),
|
||||
assert: function (tile, callback) {
|
||||
assert.imageIsSimilarToFile(tile, this.fixturePath, IMAGE_TOLERANCE_PER_MIL, callback);
|
||||
}
|
||||
},
|
||||
{
|
||||
desc: 'should get png tile using buffer-size 0 (default value in template by format)',
|
||||
coords: { z: 7, x: 64, y: 48 },
|
||||
format: 'png',
|
||||
placeholders: { buffersize_png: 0 },
|
||||
fixturePath: './test/fixtures/buffer-size/tile-7.64.48-buffer-size-0.png',
|
||||
template: createBufferSizeTemplate('named-default-buffer-size-by-format', {
|
||||
png: '<%= buffersize_png %>'
|
||||
}, {
|
||||
"buffersize_png": {
|
||||
"type": "number",
|
||||
"default": "0"
|
||||
}
|
||||
}),
|
||||
assert: function (tile, callback) {
|
||||
assert.imageIsSimilarToFile(tile, this.fixturePath, IMAGE_TOLERANCE_PER_MIL, callback);
|
||||
}
|
||||
},
|
||||
{
|
||||
desc: 'should get png tile using buffer-size 128 (placehoder value in template by format)',
|
||||
coords: { z: 7, x: 64, y: 48 },
|
||||
format: 'png',
|
||||
placeholders: { buffersize_png: 128 },
|
||||
fixturePath: './test/fixtures/buffer-size/tile-7.64.48-buffer-size-128.png',
|
||||
template: createBufferSizeTemplate('named-custom-buffer-size-by-format', {
|
||||
png: '<%= buffersize_png %>'
|
||||
}, {
|
||||
"buffersize_png": {
|
||||
"type": "number",
|
||||
"default": "0"
|
||||
}
|
||||
}),
|
||||
assert: function (tile, callback) {
|
||||
assert.imageIsSimilarToFile(tile, this.fixturePath, IMAGE_TOLERANCE_PER_MIL, callback);
|
||||
}
|
||||
},
|
||||
{
|
||||
desc: 'should get grid.json tile using buffer-size 0 overriden by template params',
|
||||
coords: { z: 7, x: 64, y: 48 },
|
||||
format: 'grid.json',
|
||||
layers: [0],
|
||||
placeholders: { buffersize_gridjson: 0 },
|
||||
fixturePath: './test/fixtures/buffer-size/tile-grid.json.7.64.48-buffer-size-0.grid.json',
|
||||
template: createBufferSizeTemplate('named-default-buffer-size-by-format-gridjson', {
|
||||
'grid.json': '<%= buffersize_gridjson %>'
|
||||
}, {
|
||||
"buffersize_gridjson": {
|
||||
"type": "number",
|
||||
"default": "0"
|
||||
}
|
||||
}),
|
||||
assert: function (tile, callback) {
|
||||
assert.utfgridEqualsFile(tile, this.fixturePath, 2,callback);
|
||||
}
|
||||
},
|
||||
{
|
||||
desc: 'should get grid.json tile using buffer-size 128 overriden by template params',
|
||||
coords: { z: 7, x: 64, y: 48 },
|
||||
format: 'grid.json',
|
||||
layers: [0],
|
||||
placeholders: { buffersize_gridjson: 128 },
|
||||
fixturePath: './test/fixtures/buffer-size/tile-7.64.48-buffer-size-128.grid.json',
|
||||
template: createBufferSizeTemplate('named-custom-buffer-size-by-format-gridjson', {
|
||||
'grid.json': '<%= buffersize_gridjson %>'
|
||||
}, {
|
||||
"buffersize_gridjson": {
|
||||
"type": "number",
|
||||
"default": "0"
|
||||
}
|
||||
}),
|
||||
assert: function (tile, callback) {
|
||||
assert.utfgridEqualsFile(tile, this.fixturePath, 2, callback);
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
testCases.forEach(function (test) {
|
||||
it(test.desc, function (done) {
|
||||
var testClient = new TestClient(test.template, 1234);
|
||||
var coords = test.coords;
|
||||
var options = {
|
||||
format: test.format,
|
||||
placeholders: test.placeholders,
|
||||
layers: test.layers
|
||||
};
|
||||
testClient.getTile(coords.z, coords.x, coords.y, options, function (err, res, tile) {
|
||||
assert.ifError(err);
|
||||
// To generate images use:
|
||||
//tile.save('./test/fixtures/buffer-size/tile-7.64.48-buffer-size-0-test.png');
|
||||
test.assert(tile, function (err) {
|
||||
assert.ifError(err);
|
||||
testClient.drain(done);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('buffer size per format for named maps w/o placeholders', function () {
|
||||
var testCases = [
|
||||
{
|
||||
desc: 'should get png tile using buffer-size 0 overriden by template params',
|
||||
coords: { z: 7, x: 64, y: 48 },
|
||||
format: 'png',
|
||||
placeholders: {
|
||||
buffersize: {
|
||||
png: 0
|
||||
}
|
||||
},
|
||||
fixturePath: './test/fixtures/buffer-size/tile-7.64.48-buffer-size-0.png',
|
||||
template: createBufferSizeTemplate('named-no-buffer-size-png-0', {}, {}),
|
||||
assert: function (tile, callback) {
|
||||
assert.imageIsSimilarToFile(tile, this.fixturePath, IMAGE_TOLERANCE_PER_MIL, callback);
|
||||
}
|
||||
},
|
||||
{
|
||||
desc: 'should get png tile using buffer-size 128 overriden by template params',
|
||||
coords: { z: 7, x: 64, y: 48 },
|
||||
format: 'png',
|
||||
placeholders: {
|
||||
buffersize: {
|
||||
png: 128
|
||||
}
|
||||
},
|
||||
fixturePath: './test/fixtures/buffer-size/tile-7.64.48-buffer-size-128.png',
|
||||
template: createBufferSizeTemplate('named-no-buffer-size-png-128', {}, {}),
|
||||
assert: function (tile, callback) {
|
||||
assert.imageIsSimilarToFile(tile, this.fixturePath, IMAGE_TOLERANCE_PER_MIL, callback);
|
||||
}
|
||||
},
|
||||
{
|
||||
desc: 'should get mvt tile using buffer-size 0 overriden by template params',
|
||||
coords: { z: 7, x: 64, y: 48 },
|
||||
format: 'mvt',
|
||||
placeholders: {
|
||||
buffersize: {
|
||||
mvt: 0
|
||||
}
|
||||
},
|
||||
fixturePath: './test/fixtures/buffer-size/tile-mvt-7.64.48-buffer-size-0.mvt',
|
||||
template: createBufferSizeTemplate('named-no-buffer-size-mvt', {}, {}),
|
||||
assert: function (tile, callback) {
|
||||
var tileJSON = tile.toJSON();
|
||||
var features = tileJSON[0].features;
|
||||
|
||||
var dataFixture = fs.readFileSync(this.fixturePath);
|
||||
var vtile = new mapnik.VectorTile(this.coords.z, this.coords.x, this.coords.y);
|
||||
vtile.setDataSync(dataFixture);
|
||||
var vtileJSON = vtile.toJSON();
|
||||
var vtileFeatures = vtileJSON[0].features;
|
||||
|
||||
assert.equal(features.length, vtileFeatures.length);
|
||||
callback();
|
||||
}
|
||||
},
|
||||
{
|
||||
desc: 'should get mvt tile using buffer-size 128 overriden by template params',
|
||||
coords: { z: 7, x: 64, y: 48 },
|
||||
format: 'mvt',
|
||||
placeholders: {
|
||||
buffersize: {
|
||||
mvt: 128
|
||||
}
|
||||
},
|
||||
fixturePath: './test/fixtures/buffer-size/tile-mvt-7.64.48-buffer-size-128.mvt',
|
||||
template: createBufferSizeTemplate('named-no-buffer-size-mvt-128', {}, {}),
|
||||
assert: function (tile, callback) {
|
||||
var tileJSON = tile.toJSON();
|
||||
var features = tileJSON[0].features;
|
||||
|
||||
var dataFixture = fs.readFileSync(this.fixturePath);
|
||||
var vtile = new mapnik.VectorTile(this.coords.z, this.coords.x, this.coords.y);
|
||||
vtile.setDataSync(dataFixture);
|
||||
var vtileJSON = vtile.toJSON();
|
||||
var vtileFeatures = vtileJSON[0].features;
|
||||
|
||||
assert.equal(features.length, vtileFeatures.length);
|
||||
callback();
|
||||
}
|
||||
},
|
||||
{
|
||||
desc: 'should get grid.json tile using buffer-size 0 overriden by template params',
|
||||
coords: { z: 7, x: 64, y: 48 },
|
||||
format: 'grid.json',
|
||||
layers: [0],
|
||||
placeholders: {
|
||||
buffersize: {
|
||||
'grid.json': 0
|
||||
}
|
||||
},
|
||||
fixturePath: './test/fixtures/buffer-size/tile-grid.json.7.64.48-buffer-size-0.grid.json',
|
||||
template: createBufferSizeTemplate('named-no-buffer-size-grid-json-0', {}, {}),
|
||||
assert: function (tile, callback) {
|
||||
assert.utfgridEqualsFile(tile, this.fixturePath, 2,callback);
|
||||
}
|
||||
},
|
||||
{
|
||||
desc: 'should get grid.json tile using buffer-size 128 overriden by template params',
|
||||
coords: { z: 7, x: 64, y: 48 },
|
||||
format: 'grid.json',
|
||||
layers: [0],
|
||||
placeholders: {
|
||||
buffersize: {
|
||||
'grid.json': 128
|
||||
}
|
||||
},
|
||||
fixturePath: './test/fixtures/buffer-size/tile-7.64.48-buffer-size-128.grid.json',
|
||||
template: createBufferSizeTemplate('named-no-buffer-size-grid-json-128', {}, {}),
|
||||
assert: function (tile, callback) {
|
||||
assert.utfgridEqualsFile(tile, this.fixturePath, 2, callback);
|
||||
}
|
||||
},
|
||||
{
|
||||
desc: 'should get png tile using buffer-size 0' +
|
||||
' overriden by template params with no buffersize in mapconfig',
|
||||
coords: { z: 7, x: 64, y: 48 },
|
||||
format: 'png',
|
||||
placeholders: {
|
||||
buffersize: {
|
||||
png: 0
|
||||
}
|
||||
},
|
||||
fixturePath: './test/fixtures/buffer-size/tile-7.64.48-buffer-size-0.png',
|
||||
template: createBufferSizeTemplate('named-no-buffer-size-mapconfig-png-0', undefined, {}),
|
||||
assert: function (tile, callback) {
|
||||
assert.imageIsSimilarToFile(tile, this.fixturePath, IMAGE_TOLERANCE_PER_MIL, callback);
|
||||
}
|
||||
},
|
||||
|
||||
];
|
||||
|
||||
testCases.forEach(function (test) {
|
||||
it(test.desc, function (done) {
|
||||
var testClient = new TestClient(test.template, 1234);
|
||||
var coords = test.coords;
|
||||
var options = {
|
||||
format: test.format,
|
||||
placeholders: test.placeholders,
|
||||
layers: test.layers
|
||||
};
|
||||
testClient.getTile(coords.z, coords.x, coords.y, options, function (err, res, tile) {
|
||||
assert.ifError(err);
|
||||
// To generate images use:
|
||||
//tile.save(test.fixturePath);
|
||||
// require('fs').writeFileSync(test.fixturePath, JSON.stringify(tile));
|
||||
// require('fs').writeFileSync(test.fixturePath, tile.getDataSync());
|
||||
test.assert(tile, function (err) {
|
||||
assert.ifError(err);
|
||||
testClient.drain(done);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -145,182 +145,4 @@ describe('aggregations happy cases', function() {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
var widgetSearchExpects = {
|
||||
'count': [ { category: 'other_a', value: 3 } ],
|
||||
'sum': [ { category: 'other_a', value: 6 } ],
|
||||
'avg': [ { category: 'other_a', value: 2 } ],
|
||||
'max': [ { category: 'other_a', value: 3 } ],
|
||||
'min': [ { category: 'other_a', value: 1 } ]
|
||||
};
|
||||
|
||||
Object.keys(operations_and_values).forEach(function (operation) {
|
||||
var description = 'should search OTHER category using "' + operation + '"';
|
||||
|
||||
it(description, function (done) {
|
||||
this.testClient = new TestClient(aggregationOperationMapConfig(operation, query_other, 'cat', 'val'));
|
||||
this.testClient.widgetSearch('cat', 'other_a', function (err, res, searchResult) {
|
||||
assert.ifError(err);
|
||||
|
||||
assert.ok(searchResult);
|
||||
assert.equal(searchResult.type, 'aggregation');
|
||||
|
||||
assert.equal(searchResult.categories.length, 1);
|
||||
assert.deepEqual(
|
||||
searchResult.categories,
|
||||
widgetSearchExpects[operation]
|
||||
);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('aggregation-dataview: special float values', function() {
|
||||
|
||||
afterEach(function(done) {
|
||||
if (this.testClient) {
|
||||
this.testClient.drain(done);
|
||||
} else {
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
function createMapConfig(layers, dataviews, analysis) {
|
||||
return {
|
||||
version: '1.5.0',
|
||||
layers: layers,
|
||||
dataviews: dataviews || {},
|
||||
analyses: analysis || []
|
||||
};
|
||||
}
|
||||
|
||||
var mapConfig = createMapConfig(
|
||||
[
|
||||
{
|
||||
"type": "cartodb",
|
||||
"options": {
|
||||
"source": {
|
||||
"id": "a0"
|
||||
},
|
||||
"cartocss": "#points { marker-width: 10; marker-fill: red; }",
|
||||
"cartocss_version": "2.3.0"
|
||||
}
|
||||
}
|
||||
],
|
||||
{
|
||||
val_aggregation: {
|
||||
source: {
|
||||
id: 'a0'
|
||||
},
|
||||
type: 'aggregation',
|
||||
options: {
|
||||
column: 'cat',
|
||||
aggregation: 'avg',
|
||||
aggregationColumn: 'val'
|
||||
}
|
||||
},
|
||||
sum_aggregation_numeric: {
|
||||
source: {
|
||||
id: 'a1'
|
||||
},
|
||||
type: 'aggregation',
|
||||
options: {
|
||||
column: 'cat',
|
||||
aggregation: 'sum',
|
||||
aggregationColumn: 'val'
|
||||
}
|
||||
}
|
||||
},
|
||||
[
|
||||
{
|
||||
"id": "a0",
|
||||
"type": "source",
|
||||
"params": {
|
||||
"query": [
|
||||
'SELECT',
|
||||
' null::geometry the_geom_webmercator,',
|
||||
' CASE',
|
||||
' WHEN x % 4 = 0 THEN \'infinity\'::float',
|
||||
' WHEN x % 4 = 1 THEN \'-infinity\'::float',
|
||||
' WHEN x % 4 = 2 THEN \'NaN\'::float',
|
||||
' ELSE x',
|
||||
' END AS val,',
|
||||
' CASE',
|
||||
' WHEN x % 2 = 0 THEN \'category_1\'',
|
||||
' ELSE \'category_2\'',
|
||||
' END AS cat',
|
||||
'FROM generate_series(1, 1000) x'
|
||||
].join('\n')
|
||||
}
|
||||
}, {
|
||||
"id": "a1",
|
||||
"type": "source",
|
||||
"params": {
|
||||
"query": [
|
||||
'SELECT',
|
||||
' null::geometry the_geom_webmercator,',
|
||||
' CASE',
|
||||
' WHEN x % 3 = 0 THEN \'NaN\'::numeric',
|
||||
' WHEN x % 3 = 1 THEN x',
|
||||
' ELSE x',
|
||||
' END AS val,',
|
||||
' CASE',
|
||||
' WHEN x % 2 = 0 THEN \'category_1\'',
|
||||
' ELSE \'category_2\'',
|
||||
' END AS cat',
|
||||
'FROM generate_series(1, 1000) x'
|
||||
].join('\n')
|
||||
}
|
||||
}
|
||||
]
|
||||
);
|
||||
|
||||
// Source a0
|
||||
// -----------------------------------------------
|
||||
// the_geom_webmercator | val | cat
|
||||
// ----------------------+-----------+------------
|
||||
// | -Infinity | category_2
|
||||
// | NaN | category_1
|
||||
// | 3 | category_2
|
||||
// | Infinity | category_1
|
||||
// | -Infinity | category_2
|
||||
// | NaN | category_1
|
||||
// | 7 | category_2
|
||||
// | Infinity | category_1
|
||||
// | -Infinity | category_2
|
||||
// | NaN | category_1
|
||||
// | 11 | category_2
|
||||
// | " | "
|
||||
|
||||
var filters = [{ own_filter: 0 }, {}];
|
||||
filters.forEach(function (filter) {
|
||||
it('should handle special float values using filter: ' + JSON.stringify(filter), function(done) {
|
||||
this.testClient = new TestClient(mapConfig, 1234);
|
||||
this.testClient.getDataview('val_aggregation', { own_filter: 0 }, function(err, dataview) {
|
||||
assert.ifError(err);
|
||||
assert.ok(dataview.infinities === (250 + 250));
|
||||
assert.ok(dataview.nans === 250);
|
||||
assert.ok(dataview.categories.length === 1);
|
||||
dataview.categories.forEach(function (category) {
|
||||
assert.ok(category.category === 'category_2');
|
||||
assert.ok(category.value === 501);
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle special numeric values using filter: ' + JSON.stringify(filter), function(done) {
|
||||
this.testClient = new TestClient(mapConfig, 1234);
|
||||
this.testClient.getDataview('sum_aggregation_numeric', { own_filter: 0 }, function(err, dataview) {
|
||||
assert.ifError(err);
|
||||
assert.ok(dataview.nans === 333);
|
||||
assert.ok(dataview.categories.length === 2);
|
||||
dataview.categories.forEach(function (category) {
|
||||
assert.ok(category.value !== null);
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
require('../../support/test_helper');
|
||||
var assert = require('../../support/assert');
|
||||
var TestClient = require('../../support/test-client');
|
||||
|
||||
function createMapConfig(layers, dataviews, analysis) {
|
||||
return {
|
||||
version: '1.5.0',
|
||||
layers: layers,
|
||||
dataviews: dataviews || {},
|
||||
analyses: analysis || []
|
||||
};
|
||||
}
|
||||
|
||||
describe('formula-dataview: special float values', function() {
|
||||
|
||||
afterEach(function(done) {
|
||||
if (this.testClient) {
|
||||
this.testClient.drain(done);
|
||||
} else {
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
var mapConfig = createMapConfig(
|
||||
[
|
||||
{
|
||||
"type": "cartodb",
|
||||
"options": {
|
||||
"source": {
|
||||
"id": "a0"
|
||||
},
|
||||
"cartocss": "#points { marker-width: 10; marker-fill: red; }",
|
||||
"cartocss_version": "2.3.0"
|
||||
}
|
||||
}
|
||||
],
|
||||
{
|
||||
val_formula: {
|
||||
source: {
|
||||
id: 'a0'
|
||||
},
|
||||
type: 'formula',
|
||||
options: {
|
||||
column: 'val',
|
||||
operation: 'avg'
|
||||
}
|
||||
}
|
||||
},
|
||||
[
|
||||
{
|
||||
"id": "a0",
|
||||
"type": "source",
|
||||
"params": {
|
||||
"query": [
|
||||
'SELECT',
|
||||
' null::geometry the_geom_webmercator,',
|
||||
' CASE',
|
||||
' WHEN x % 4 = 0 THEN \'infinity\'::float',
|
||||
' WHEN x % 4 = 1 THEN \'-infinity\'::float',
|
||||
' WHEN x % 4 = 2 THEN \'NaN\'::float',
|
||||
' ELSE x',
|
||||
' END AS val',
|
||||
'FROM generate_series(1, 1000) x'
|
||||
].join('\n')
|
||||
}
|
||||
}
|
||||
]
|
||||
);
|
||||
|
||||
it('should filter infinities out and count them in the summary', function(done) {
|
||||
this.testClient = new TestClient(mapConfig, 1234);
|
||||
this.testClient.getDataview('val_formula', {}, function(err, dataview) {
|
||||
assert.ok(!err, err);
|
||||
assert.equal(dataview.result, 501);
|
||||
assert.ok(dataview.infinities === (250 + 250));
|
||||
assert.ok(dataview.nans === 250);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
@@ -124,13 +124,6 @@ describe('dataviews using tables with overviews', function() {
|
||||
params: {
|
||||
query: 'select * from test_table_overviews'
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'data-source-special-float-values',
|
||||
type: 'source',
|
||||
params: {
|
||||
query: 'select * from test_special_float_values_table_overviews'
|
||||
}
|
||||
}
|
||||
],
|
||||
dataviews: {
|
||||
@@ -151,17 +144,6 @@ describe('dataviews using tables with overviews', function() {
|
||||
aggregationColumn: 'name',
|
||||
}
|
||||
},
|
||||
test_categories_special_values: {
|
||||
type: 'aggregation',
|
||||
source: {
|
||||
id: 'data-source-special-float-values'
|
||||
},
|
||||
options: {
|
||||
column: 'name',
|
||||
aggregation: 'sum',
|
||||
aggregationColumn: 'value',
|
||||
}
|
||||
},
|
||||
test_histogram: {
|
||||
type: 'histogram',
|
||||
source: {id: 'data-source'},
|
||||
@@ -178,16 +160,6 @@ describe('dataviews using tables with overviews', function() {
|
||||
bins: 2
|
||||
}
|
||||
},
|
||||
test_histogram_special_values: {
|
||||
type: 'histogram',
|
||||
source: {
|
||||
id: 'data-source-special-float-values'
|
||||
},
|
||||
options: {
|
||||
column: 'value',
|
||||
bins: 2
|
||||
}
|
||||
},
|
||||
test_avg: {
|
||||
type: 'formula',
|
||||
source: {id: 'data-source'},
|
||||
@@ -196,16 +168,6 @@ describe('dataviews using tables with overviews', function() {
|
||||
operation: 'avg'
|
||||
}
|
||||
},
|
||||
test_formula_sum_special_values: {
|
||||
type: 'formula',
|
||||
source: {
|
||||
id: 'data-source-special-float-values'
|
||||
},
|
||||
options: {
|
||||
column: 'value',
|
||||
operation: 'sum'
|
||||
}
|
||||
},
|
||||
test_count: {
|
||||
type: 'formula',
|
||||
source: {id: 'data-source'},
|
||||
@@ -240,17 +202,6 @@ describe('dataviews using tables with overviews', function() {
|
||||
cartocss_version: '2.3.0',
|
||||
source: { id: 'data-source' }
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'mapnik',
|
||||
options: {
|
||||
sql: 'select * from test_special_float_values_table_overviews',
|
||||
cartocss: '#layer { marker-fill: red; marker-width: 32; marker-allow-overlap: true; }',
|
||||
cartocss_version: '2.3.0',
|
||||
source: {
|
||||
id: 'data-source-special-float-values'
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
@@ -261,14 +212,7 @@ describe('dataviews using tables with overviews', function() {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
assert.deepEqual(formula_result, {
|
||||
"operation":"sum",
|
||||
"result":15,
|
||||
"infinities": 0,
|
||||
"nans": 0,
|
||||
"nulls":0,
|
||||
"type":"formula"
|
||||
});
|
||||
assert.deepEqual(formula_result, {"operation":"sum","result":15,"nulls":0,"type":"formula"});
|
||||
|
||||
testClient.drain(done);
|
||||
});
|
||||
@@ -280,14 +224,7 @@ describe('dataviews using tables with overviews', function() {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
assert.deepEqual(formula_result, {
|
||||
"operation":"avg",
|
||||
"result":3,
|
||||
"nulls":0,
|
||||
"type":"formula",
|
||||
"infinities": 0,
|
||||
"nans": 0
|
||||
});
|
||||
assert.deepEqual(formula_result, {"operation":"avg","result":3,"nulls":0,"type":"formula"});
|
||||
|
||||
testClient.drain(done);
|
||||
});
|
||||
@@ -299,14 +236,7 @@ describe('dataviews using tables with overviews', function() {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
assert.deepEqual(formula_result, {
|
||||
"operation":"count",
|
||||
"result":5,
|
||||
"nulls":0,
|
||||
"type":"formula",
|
||||
"infinities": 0,
|
||||
"nans": 0
|
||||
});
|
||||
assert.deepEqual(formula_result, {"operation":"count","result":5,"nulls":0,"type":"formula"});
|
||||
|
||||
testClient.drain(done);
|
||||
});
|
||||
@@ -318,14 +248,7 @@ describe('dataviews using tables with overviews', function() {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
assert.deepEqual(formula_result, {
|
||||
"operation": "max",
|
||||
"result": 5,
|
||||
"nulls": 0,
|
||||
"infinities": 0,
|
||||
"nans": 0,
|
||||
"type": "formula"
|
||||
});
|
||||
assert.deepEqual(formula_result, {"operation":"max","result":5,"nulls":0,"type":"formula"});
|
||||
|
||||
testClient.drain(done);
|
||||
});
|
||||
@@ -337,14 +260,7 @@ describe('dataviews using tables with overviews', function() {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
assert.deepEqual(formula_result, {
|
||||
"operation": "min",
|
||||
"result": 1,
|
||||
"nulls": 0,
|
||||
"infinities": 0,
|
||||
"nans": 0,
|
||||
"type": "formula"
|
||||
});
|
||||
assert.deepEqual(formula_result, {"operation":"min","result":1,"nulls":0,"type":"formula"});
|
||||
|
||||
testClient.drain(done);
|
||||
});
|
||||
@@ -359,14 +275,7 @@ describe('dataviews using tables with overviews', function() {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
assert.deepEqual(formula_result, {
|
||||
"operation":"sum",
|
||||
"result":15,
|
||||
"nulls":0,
|
||||
"infinities": 0,
|
||||
"nans": 0,
|
||||
"type":"formula"
|
||||
});
|
||||
assert.deepEqual(formula_result, {"operation":"sum","result":15,"nulls":0,"type":"formula"});
|
||||
|
||||
testClient.drain(done);
|
||||
});
|
||||
@@ -463,14 +372,7 @@ describe('dataviews using tables with overviews', function() {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
assert.deepEqual(formula_result, {
|
||||
"operation":"sum",
|
||||
"result":1,
|
||||
"nulls":0,
|
||||
"infinities": 0,
|
||||
"nans": 0,
|
||||
"type":"formula"
|
||||
});
|
||||
assert.deepEqual(formula_result, {"operation":"sum","result":1,"nulls":0,"type":"formula"});
|
||||
testClient.drain(done);
|
||||
});
|
||||
});
|
||||
@@ -481,14 +383,7 @@ describe('dataviews using tables with overviews', function() {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
assert.deepEqual(formula_result, {
|
||||
"operation":"avg",
|
||||
"result":1,
|
||||
"nulls":0,
|
||||
"infinities": 0,
|
||||
"nans": 0,
|
||||
"type":"formula"
|
||||
});
|
||||
assert.deepEqual(formula_result, {"operation":"avg","result":1,"nulls":0,"type":"formula"});
|
||||
|
||||
testClient.drain(done);
|
||||
});
|
||||
@@ -500,14 +395,7 @@ describe('dataviews using tables with overviews', function() {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
assert.deepEqual(formula_result, {
|
||||
"operation":"count",
|
||||
"result":1,
|
||||
"infinities": 0,
|
||||
"nans": 0,
|
||||
"nulls":0,
|
||||
"type":"formula"
|
||||
});
|
||||
assert.deepEqual(formula_result, {"operation":"count","result":1,"nulls":0,"type":"formula"});
|
||||
|
||||
testClient.drain(done);
|
||||
});
|
||||
@@ -519,14 +407,7 @@ describe('dataviews using tables with overviews', function() {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
assert.deepEqual(formula_result, {
|
||||
"operation": "max",
|
||||
"result": 1,
|
||||
"nulls": 0,
|
||||
"infinities": 0,
|
||||
"nans": 0,
|
||||
"type": "formula"
|
||||
});
|
||||
assert.deepEqual(formula_result, {"operation":"max","result":1,"nulls":0,"type":"formula"});
|
||||
|
||||
testClient.drain(done);
|
||||
});
|
||||
@@ -538,14 +419,7 @@ describe('dataviews using tables with overviews', function() {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
assert.deepEqual(formula_result, {
|
||||
"operation": "min",
|
||||
"result": 1,
|
||||
"nulls": 0,
|
||||
"infinities": 0,
|
||||
"nans": 0,
|
||||
"type": "formula"
|
||||
});
|
||||
assert.deepEqual(formula_result, {"operation":"min","result":1,"nulls":0,"type":"formula"});
|
||||
|
||||
testClient.drain(done);
|
||||
});
|
||||
@@ -563,14 +437,7 @@ describe('dataviews using tables with overviews', function() {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
assert.deepEqual(formula_result, {
|
||||
"operation":"sum",
|
||||
"result":1,
|
||||
"nulls":0,
|
||||
"infinities": 0,
|
||||
"nans": 0,
|
||||
"type":"formula"
|
||||
});
|
||||
assert.deepEqual(formula_result, {"operation":"sum","result":1,"nulls":0,"type":"formula"});
|
||||
testClient.drain(done);
|
||||
});
|
||||
});
|
||||
@@ -578,69 +445,5 @@ describe('dataviews using tables with overviews', function() {
|
||||
|
||||
});
|
||||
|
||||
describe('aggregation special float values', function () {
|
||||
var params = {};
|
||||
|
||||
it("should expose an aggregation dataview filtering special float values out", function (done) {
|
||||
var testClient = new TestClient(overviewsMapConfig);
|
||||
testClient.getDataview('test_categories_special_values', params, function (err, dataview) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
assert.deepEqual(dataview, {
|
||||
aggregation: 'sum',
|
||||
count: 5,
|
||||
nulls: 0,
|
||||
nans: 1,
|
||||
infinities: 1,
|
||||
min: 6,
|
||||
max: 6,
|
||||
categoriesCount: 1,
|
||||
categories: [ { category: 'Hawai', value: 6, agg: false } ],
|
||||
type: 'aggregation'
|
||||
});
|
||||
testClient.drain(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('should expose a histogram dataview filtering special float values out', function (done) {
|
||||
var testClient = new TestClient(overviewsMapConfig);
|
||||
testClient.getDataview('test_histogram_special_values', params, function (err, dataview) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
assert.deepEqual(dataview, {
|
||||
bin_width: 0,
|
||||
bins_count: 1,
|
||||
bins_start: 3,
|
||||
nulls: 0,
|
||||
infinities: 1,
|
||||
nans: 1,
|
||||
avg: 3,
|
||||
bins: [ { bin: 0, min: 3, max: 3, avg: 3, freq: 2 } ],
|
||||
type: 'histogram'
|
||||
});
|
||||
testClient.drain(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('should expose a formula (sum) dataview filtering special float values out', function (done) {
|
||||
var testClient = new TestClient(overviewsMapConfig);
|
||||
testClient.getDataview('test_formula_sum_special_values', params, function (err, dataview) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
assert.deepEqual(dataview, {
|
||||
operation: 'sum',
|
||||
result: 6,
|
||||
nulls: 0,
|
||||
nans: 1,
|
||||
infinities: 1,
|
||||
type: 'formula'
|
||||
});
|
||||
testClient.drain(done);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
311
test/acceptance/limits.js
Normal file
311
test/acceptance/limits.js
Normal file
@@ -0,0 +1,311 @@
|
||||
var testHelper = require('../support/test_helper');
|
||||
|
||||
var assert = require('../support/assert');
|
||||
var _ = require('underscore');
|
||||
var redis = require('redis');
|
||||
|
||||
var CartodbWindshaft = require('../../lib/cartodb/server');
|
||||
var serverOptions = require('../../lib/cartodb/server_options');
|
||||
|
||||
var LayergroupToken = require('../support/layergroup-token');
|
||||
|
||||
describe('render limits', function() {
|
||||
|
||||
var layergroupUrl = '/api/v1/map';
|
||||
|
||||
var redisClient = redis.createClient(global.environment.redis.port);
|
||||
|
||||
var server;
|
||||
var keysToDelete;
|
||||
beforeEach(function() {
|
||||
keysToDelete = {};
|
||||
server = new CartodbWindshaft(serverOptions);
|
||||
server.setMaxListeners(0);
|
||||
});
|
||||
|
||||
afterEach(function(done) {
|
||||
testHelper.deleteRedisKeys(keysToDelete, done);
|
||||
});
|
||||
|
||||
var user = 'localhost';
|
||||
|
||||
var pointSleepSql = "SELECT pg_sleep(0.5)," +
|
||||
" 'SRID=3857;POINT(0 0)'::geometry the_geom_webmercator, 1 cartodb_id";
|
||||
var pointCartoCss = '#layer { marker-fill:red; }';
|
||||
var polygonSleepSql = "SELECT pg_sleep(0.5)," +
|
||||
" ST_Buffer('SRID=3857;POINT(0 0)'::geometry, 100000000) the_geom_webmercator, 1 cartodb_id";
|
||||
var polygonCartoCss = '#layer { polygon-fill:red; }';
|
||||
|
||||
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) {
|
||||
return {
|
||||
url: layergroupUrl,
|
||||
method: 'POST',
|
||||
headers: {
|
||||
host: userHost,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
data: JSON.stringify(layergroup)
|
||||
};
|
||||
}
|
||||
|
||||
function withRenderLimit(user, renderLimit, callback) {
|
||||
redisClient.SELECT(5, function(err) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
var userLimitsKey = 'limits:tiler:' + user;
|
||||
redisClient.HSET(userLimitsKey, 'render', renderLimit, function(err) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
keysToDelete[userLimitsKey] = 5;
|
||||
return callback();
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
describe('with onTileErrorStrategy DISABLED', function() {
|
||||
var onTileErrorStrategyEnabled;
|
||||
before(function() {
|
||||
onTileErrorStrategyEnabled = global.environment.enabledFeatures.onTileErrorStrategy;
|
||||
global.environment.enabledFeatures.onTileErrorStrategy = false;
|
||||
});
|
||||
|
||||
after(function() {
|
||||
global.environment.enabledFeatures.onTileErrorStrategy = onTileErrorStrategyEnabled;
|
||||
});
|
||||
|
||||
it("layergroup creation fails if test tile is slow", function(done) {
|
||||
withRenderLimit(user, 50, function(err) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
var layergroup = singleLayergroupConfig(polygonSleepSql, polygonCartoCss);
|
||||
assert.response(server,
|
||||
createRequest(layergroup, user),
|
||||
{
|
||||
status: 400
|
||||
},
|
||||
function(res) {
|
||||
var parsed = JSON.parse(res.body);
|
||||
assert.deepEqual(parsed.errors, [ 'Render timed out' ]);
|
||||
done();
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it("layergroup creation does not fail if user limit is high enough even if test tile is slow", function(done) {
|
||||
withRenderLimit(user, 5000, function(err) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
var layergroup = singleLayergroupConfig(polygonSleepSql, polygonCartoCss);
|
||||
assert.response(server,
|
||||
createRequest(layergroup, user),
|
||||
{
|
||||
status: 200
|
||||
},
|
||||
function(res) {
|
||||
var parsed = JSON.parse(res.body);
|
||||
assert.ok(parsed.layergroupid);
|
||||
keysToDelete['map_cfg|' + LayergroupToken.parse(parsed.layergroupid).token] = 0;
|
||||
keysToDelete['user:localhost:mapviews:global'] = 5;
|
||||
done();
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it("layergroup creation works if test tile is fast but tile request fails if they are slow", function(done) {
|
||||
withRenderLimit(user, 50, function(err) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
var layergroup = singleLayergroupConfig(pointSleepSql, pointCartoCss);
|
||||
assert.response(server,
|
||||
createRequest(layergroup, user),
|
||||
{
|
||||
status: 200
|
||||
},
|
||||
function(res) {
|
||||
keysToDelete['map_cfg|' + LayergroupToken.parse(JSON.parse(res.body).layergroupid).token] = 0;
|
||||
keysToDelete['user:localhost:mapviews:global'] = 5;
|
||||
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: 400
|
||||
},
|
||||
function(res) {
|
||||
var parsed = JSON.parse(res.body);
|
||||
assert.deepEqual(parsed.errors, ['Render timed out']);
|
||||
done();
|
||||
}
|
||||
);
|
||||
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it("tile request does not fail if user limit is high enough", function(done) {
|
||||
withRenderLimit(user, 5000, function(err) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
var layergroup = singleLayergroupConfig(pointSleepSql, pointCartoCss);
|
||||
assert.response(server,
|
||||
createRequest(layergroup, user),
|
||||
{
|
||||
status: 200
|
||||
},
|
||||
function(res) {
|
||||
keysToDelete['map_cfg|' + LayergroupToken.parse(JSON.parse(res.body).layergroupid).token] = 0;
|
||||
keysToDelete['user:localhost:mapviews:global'] = 5;
|
||||
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,
|
||||
headers: {
|
||||
'Content-Type': 'image/png'
|
||||
}
|
||||
},
|
||||
function(res, err) {
|
||||
done(err);
|
||||
}
|
||||
);
|
||||
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('with onTileErrorStrategy', function() {
|
||||
|
||||
it("layergroup creation works even if test tile is slow", function(done) {
|
||||
withRenderLimit(user, 50, function(err) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
var layergroup = singleLayergroupConfig(polygonSleepSql, polygonCartoCss);
|
||||
assert.response(server,
|
||||
createRequest(layergroup, user),
|
||||
{
|
||||
status: 200
|
||||
},
|
||||
function(res) {
|
||||
var parsed = JSON.parse(res.body);
|
||||
assert.ok(parsed.layergroupid);
|
||||
keysToDelete['map_cfg|' + LayergroupToken.parse(parsed.layergroupid).token] = 0;
|
||||
keysToDelete['user:localhost:mapviews:global'] = 5;
|
||||
done();
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it("layergroup creation and tile requests works even if they are slow but returns fallback", function(done) {
|
||||
withRenderLimit(user, 50, function(err) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
var layergroup = singleLayergroupConfig(pointSleepSql, pointCartoCss);
|
||||
assert.response(server,
|
||||
createRequest(layergroup, user),
|
||||
{
|
||||
status: 200
|
||||
},
|
||||
function(res) {
|
||||
keysToDelete['map_cfg|' + LayergroupToken.parse(JSON.parse(res.body).layergroupid).token] = 0;
|
||||
keysToDelete['user:localhost:mapviews:global'] = 5;
|
||||
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,
|
||||
headers: {
|
||||
'Content-Type': 'image/png'
|
||||
}
|
||||
},
|
||||
function(res, err) {
|
||||
if (err) {
|
||||
done(err);
|
||||
}
|
||||
var referenceImagePath = './test/fixtures/render-timeout-fallback.png';
|
||||
assert.imageBufferIsSimilarToFile(res.body, referenceImagePath, 25,
|
||||
function(imgErr/*, similarity*/) {
|
||||
done(imgErr);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
@@ -1041,7 +1041,7 @@ describe(suiteName, function() {
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// See https://github.com/CartoDB/Windshaft-cartodb/issues/91
|
||||
// and https://github.com/CartoDB/Windshaft-cartodb/issues/38
|
||||
it("tiles for private tables can be fetched with api_key", function(done) {
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
require('../support/test_helper');
|
||||
|
||||
const assert = require('../support/assert');
|
||||
const TestClient = require('../support/test-client');
|
||||
|
||||
function createMapConfig (sql = TestClient.SQL.ONE_POINT) {
|
||||
return {
|
||||
version: '1.6.0',
|
||||
layers: [{
|
||||
type: "cartodb",
|
||||
options: {
|
||||
sql: sql,
|
||||
cartocss: TestClient.CARTOCSS.POINTS,
|
||||
cartocss_version: '2.3.0',
|
||||
interactivity: 'cartodb_id'
|
||||
}
|
||||
}]
|
||||
};
|
||||
}
|
||||
|
||||
describe('mvt', function () {
|
||||
const testCases = [
|
||||
{
|
||||
desc: 'should get empty mvt with code 204 (no content)',
|
||||
coords: { z: 0, x: 0, y: 0 },
|
||||
format: 'mvt',
|
||||
response: {
|
||||
status: 204,
|
||||
headers: {
|
||||
'Content-Type': undefined
|
||||
}
|
||||
},
|
||||
mapConfig: createMapConfig(TestClient.SQL.EMPTY)
|
||||
},
|
||||
{
|
||||
desc: 'should get mvt tile with code 200 (ok)',
|
||||
coords: { z: 0, x: 0, y: 0 },
|
||||
format: 'mvt',
|
||||
response: {
|
||||
status: 200,
|
||||
headers: {
|
||||
'Content-Type': 'application/x-protobuf'
|
||||
}
|
||||
},
|
||||
mapConfig: createMapConfig()
|
||||
}
|
||||
];
|
||||
|
||||
testCases.forEach(function (test) {
|
||||
it(test.desc, done => {
|
||||
const testClient = new TestClient(test.mapConfig, 1234);
|
||||
const { z, x, y } = test.coords;
|
||||
const { format, response } = test;
|
||||
|
||||
testClient.getTile(z, x, y, { format, response }, (err, res) => {
|
||||
assert.ifError(err);
|
||||
|
||||
assert.equal(res.statusCode, test.response.status);
|
||||
testClient.drain(done);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -5,7 +5,6 @@ var step = require('step');
|
||||
var cartodbServer = require('../../../lib/cartodb/server');
|
||||
var ServerOptions = require('./support/ported_server_options');
|
||||
var testClient = require('./support/test_client');
|
||||
var TestClient = require('../../support/test-client');
|
||||
|
||||
var BaseController = require('../../../lib/cartodb/controllers/base');
|
||||
|
||||
@@ -24,14 +23,6 @@ describe('multilayer error cases', function() {
|
||||
BaseController.prototype.req2params = req2paramsFn;
|
||||
});
|
||||
|
||||
// var client = null;
|
||||
afterEach(function(done) {
|
||||
if (this.client) {
|
||||
return this.client.drain(done);
|
||||
}
|
||||
return done();
|
||||
});
|
||||
|
||||
it("post layergroup with wrong Content-Type", function(done) {
|
||||
assert.response(server, {
|
||||
url: '/database/windshaft_test/layergroup',
|
||||
@@ -162,16 +153,24 @@ describe('multilayer error cases', function() {
|
||||
]
|
||||
};
|
||||
ServerOptions.afterLayergroupCreateCalls = 0;
|
||||
this.client = new TestClient(layergroup);
|
||||
this.client.getLayergroup({status: 400}, function(err, parsed) {
|
||||
assert.ok(!err, err);
|
||||
// See http://github.com/CartoDB/Windshaft/issues/159
|
||||
assert.equal(ServerOptions.afterLayergroupCreateCalls, 0);
|
||||
assert.ok(parsed);
|
||||
assert.equal(parsed.errors.length, 1);
|
||||
var error = parsed.errors[0];
|
||||
assert.ok(error.match(/column "missing" does not exist/m), error);
|
||||
done();
|
||||
assert.response(server, {
|
||||
url: '/database/windshaft_test/layergroup',
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json' },
|
||||
data: JSON.stringify(layergroup)
|
||||
}, {}, function(res) {
|
||||
try {
|
||||
assert.equal(res.statusCode, 400, res.statusCode + ': ' + res.body);
|
||||
// See http://github.com/CartoDB/Windshaft/issues/159
|
||||
assert.equal(ServerOptions.afterLayergroupCreateCalls, 0);
|
||||
var parsed = JSON.parse(res.body);
|
||||
assert.ok(parsed);
|
||||
assert.equal(parsed.errors.length, 1);
|
||||
var error = parsed.errors[0];
|
||||
assert.ok(error.match(/column "missing" does not exist/m), error);
|
||||
// TODO: check which layer introduced the problem ?
|
||||
done();
|
||||
} catch (err) { done(err); }
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -16,13 +16,13 @@ describe('server_png8_format', function() {
|
||||
var serverOptionsPng32 = ServerOptions;
|
||||
serverOptionsPng32.grainstore = _.clone(ServerOptions.grainstore);
|
||||
serverOptionsPng32.grainstore.mapnik_tile_format = 'png32';
|
||||
var serverPng32 = cartodbServer(serverOptionsPng32);
|
||||
var serverPng32 = new cartodbServer(serverOptionsPng32);
|
||||
serverPng32.setMaxListeners(0);
|
||||
|
||||
var serverOptionsPng8 = ServerOptions;
|
||||
serverOptionsPng8.grainstore = _.clone(ServerOptions.grainstore);
|
||||
serverOptionsPng8.grainstore.mapnik_tile_format = 'png8:m=h';
|
||||
var serverPng8 = cartodbServer(serverOptionsPng8);
|
||||
var serverPng8 = new cartodbServer(serverOptionsPng8);
|
||||
serverPng8.setMaxListeners(0);
|
||||
|
||||
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
require('../support/test_helper');
|
||||
|
||||
var assert = require('../support/assert');
|
||||
var TestClient = require('../support/test-client');
|
||||
|
||||
describe('special numeric values', function() {
|
||||
|
||||
afterEach(function(done) {
|
||||
if (this.testClient) {
|
||||
this.testClient.drain(done);
|
||||
} else {
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
var ATTRIBUTES_LAYER = 1;
|
||||
|
||||
function createMapConfig(sql, id, columns) {
|
||||
return {
|
||||
version: '1.6.0',
|
||||
layers: [
|
||||
{
|
||||
type: 'mapnik',
|
||||
options: {
|
||||
sql: "select 1 as id, 'SRID=4326;POINT(0 0)'::geometry as the_geom",
|
||||
cartocss: '#style { }',
|
||||
cartocss_version: '2.0.1'
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'mapnik',
|
||||
options: {
|
||||
sql: sql || "select 1 as i, 6 as n, 'SRID=4326;POINT(0 0)'::geometry as the_geom",
|
||||
attributes: {
|
||||
id: id || 'i',
|
||||
columns: columns || ['n']
|
||||
},
|
||||
cartocss: '#style { }',
|
||||
cartocss_version: '2.0.1'
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
it('should retrieve special numeric values', function (done) {
|
||||
var featureId = 1;
|
||||
var sql = [
|
||||
'SELECT',
|
||||
' 1 as cartodb_id,',
|
||||
' null::geometry the_geom_webmercator,',
|
||||
' \'infinity\'::float as infinity,',
|
||||
' \'-infinity\'::float as _infinity,',
|
||||
' \'NaN\'::float as nan'
|
||||
].join('\n');
|
||||
var id = 'cartodb_id';
|
||||
var columns = ['infinity', '_infinity', 'nan'];
|
||||
|
||||
var mapConfig = createMapConfig(sql, id, columns);
|
||||
|
||||
this.testClient = new TestClient(mapConfig, 1234);
|
||||
this.testClient.getFeatureAttributes(featureId, ATTRIBUTES_LAYER, {}, function (err, attributes) {
|
||||
assert.ifError(err);
|
||||
assert.equal(attributes.infinity, 'Infinity');
|
||||
assert.equal(attributes._infinity, '-Infinity');
|
||||
assert.equal(attributes.nan, 'NaN');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,279 +0,0 @@
|
||||
require('../../support/test_helper');
|
||||
|
||||
var assert = require('../../support/assert');
|
||||
var TestClient = require('../../support/test-client');
|
||||
|
||||
describe('Create mapnik layergroup', function() {
|
||||
before(function() {
|
||||
this.layerStatsConfig = global.environment.enabledFeatures.layerStats;
|
||||
global.environment.enabledFeatures.layerStats = true;
|
||||
});
|
||||
|
||||
after(function() {
|
||||
global.environment.enabledFeatures.layerStats = this.layerStatsConfig;
|
||||
});
|
||||
|
||||
var cartocssVersion = '2.3.0';
|
||||
var cartocss = '#layer { line-width:16; }';
|
||||
|
||||
var mapnikLayer1 = {
|
||||
type: 'mapnik',
|
||||
options: {
|
||||
sql: 'select * from test_table limit 1',
|
||||
cartocss_version: cartocssVersion,
|
||||
cartocss: cartocss
|
||||
}
|
||||
};
|
||||
|
||||
var mapnikLayer2 = {
|
||||
type: 'mapnik',
|
||||
options: {
|
||||
sql: 'select * from test_table_2 limit 2',
|
||||
cartocss_version: cartocssVersion,
|
||||
cartocss: cartocss
|
||||
}
|
||||
};
|
||||
|
||||
var mapnikLayer3 = {
|
||||
type: 'mapnik',
|
||||
options: {
|
||||
sql: 'select * from test_table_3 limit 3',
|
||||
cartocss_version: cartocssVersion,
|
||||
cartocss: cartocss
|
||||
}
|
||||
};
|
||||
|
||||
var mapnikLayer4 = {
|
||||
type: 'mapnik',
|
||||
options: {
|
||||
sql: [
|
||||
'select t1.cartodb_id, t1.the_geom, t1.the_geom_webmercator, t2.address',
|
||||
' from test_table t1, test_table_2 t2',
|
||||
' where t1.cartodb_id = t2.cartodb_id;'
|
||||
].join(''),
|
||||
cartocss_version: cartocssVersion,
|
||||
cartocss: cartocss
|
||||
}
|
||||
};
|
||||
|
||||
var httpLayer = {
|
||||
type: 'http',
|
||||
options: {
|
||||
urlTemplate: 'http://{s}.basemaps.cartocdn.com/dark_nolabels/{z}/{x}/{y}.png',
|
||||
subdomains: ['a','b','c']
|
||||
}
|
||||
};
|
||||
|
||||
var mapnikLayerGeomColumn = {
|
||||
type: 'mapnik',
|
||||
options: {
|
||||
sql: 'select *, the_geom as my_geom from test_table_3 limit 2',
|
||||
geom_column: 'my_geom',
|
||||
cartocss_version: cartocssVersion,
|
||||
cartocss: cartocss
|
||||
}
|
||||
};
|
||||
|
||||
function mapnikBasicLayerId(index) {
|
||||
return 'layer' + index;
|
||||
}
|
||||
function typeLayerId(type, index) {
|
||||
return type + '-' + mapnikBasicLayerId(index);
|
||||
}
|
||||
|
||||
it('with one mapnik layer should response with meta-stats for that layer', function(done) {
|
||||
var testClient = new TestClient({
|
||||
version: '1.4.0',
|
||||
layers: [
|
||||
mapnikLayer1
|
||||
]
|
||||
});
|
||||
|
||||
testClient.getLayergroup(function(err, layergroup) {
|
||||
assert.ok(!err);
|
||||
assert.equal(layergroup.metadata.layers[0].id, mapnikBasicLayerId(0));
|
||||
assert.equal(layergroup.metadata.layers[0].meta.stats.estimatedFeatureCount, 1);
|
||||
testClient.drain(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('with two mapnik layer should response with meta-stats for every layer', function(done) {
|
||||
var testClient = new TestClient({
|
||||
version: '1.4.0',
|
||||
layers: [
|
||||
mapnikLayer1,
|
||||
mapnikLayer2
|
||||
]
|
||||
});
|
||||
|
||||
testClient.getLayergroup(function(err, layergroup) {
|
||||
assert.ok(!err);
|
||||
assert.equal(layergroup.metadata.layers[0].id, mapnikBasicLayerId(0));
|
||||
assert.equal(layergroup.metadata.layers[0].meta.stats.estimatedFeatureCount, 1);
|
||||
assert.equal(layergroup.metadata.layers[1].id, mapnikBasicLayerId(1));
|
||||
assert.equal(layergroup.metadata.layers[1].meta.stats.estimatedFeatureCount, 2);
|
||||
testClient.drain(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('with three mapnik layer should response with meta-stats for every layer', function(done) {
|
||||
var testClient = new TestClient({
|
||||
version: '1.4.0',
|
||||
layers: [
|
||||
mapnikLayer1,
|
||||
mapnikLayer2,
|
||||
mapnikLayer3
|
||||
]
|
||||
});
|
||||
|
||||
testClient.getLayergroup(function(err, layergroup) {
|
||||
assert.ok(!err);
|
||||
assert.equal(layergroup.metadata.layers[0].id, mapnikBasicLayerId(0));
|
||||
assert.equal(layergroup.metadata.layers[0].meta.stats.estimatedFeatureCount, 1);
|
||||
assert.equal(layergroup.metadata.layers[1].id, mapnikBasicLayerId(1));
|
||||
assert.equal(layergroup.metadata.layers[1].meta.stats.estimatedFeatureCount, 2);
|
||||
assert.equal(layergroup.metadata.layers[2].id, mapnikBasicLayerId(2));
|
||||
assert.equal(layergroup.metadata.layers[2].meta.stats.estimatedFeatureCount, 3);
|
||||
testClient.drain(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('with one mapnik layer (sql with join) should response with meta-stats for that layer', function(done) {
|
||||
var testClient = new TestClient({
|
||||
version: '1.4.0',
|
||||
layers: [
|
||||
mapnikLayer4
|
||||
]
|
||||
});
|
||||
|
||||
testClient.getLayergroup(function(err, layergroup) {
|
||||
assert.ok(!err);
|
||||
assert.equal(layergroup.metadata.layers[0].id, mapnikBasicLayerId(0));
|
||||
assert.equal(layergroup.metadata.layers[0].meta.stats.estimatedFeatureCount, 5);
|
||||
testClient.drain(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('with two mapnik layer (sql with join) should response with meta-stats for every layer', function(done) {
|
||||
var testClient = new TestClient({
|
||||
version: '1.4.0',
|
||||
layers: [
|
||||
mapnikLayer4,
|
||||
mapnikLayer4
|
||||
]
|
||||
});
|
||||
|
||||
testClient.getLayergroup(function(err, layergroup) {
|
||||
assert.ok(!err);
|
||||
assert.equal(layergroup.metadata.layers[0].id, mapnikBasicLayerId(0));
|
||||
assert.equal(layergroup.metadata.layers[0].meta.stats.estimatedFeatureCount, 5);
|
||||
assert.equal(layergroup.metadata.layers[1].id, mapnikBasicLayerId(1));
|
||||
assert.equal(layergroup.metadata.layers[1].meta.stats.estimatedFeatureCount, 5);
|
||||
testClient.drain(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('with two mapnik layer (with & without join) should response with meta-stats for every layer', function(done) {
|
||||
var testClient = new TestClient({
|
||||
version: '1.4.0',
|
||||
layers: [
|
||||
mapnikLayer3,
|
||||
mapnikLayer4
|
||||
]
|
||||
});
|
||||
|
||||
testClient.getLayergroup(function(err, layergroup) {
|
||||
assert.ok(!err);
|
||||
assert.equal(layergroup.metadata.layers[0].id, mapnikBasicLayerId(0));
|
||||
assert.equal(layergroup.metadata.layers[0].meta.stats.estimatedFeatureCount, 3);
|
||||
assert.ok(!layergroup.metadata.layers[0].meta.stats[1]);
|
||||
assert.equal(layergroup.metadata.layers[1].id, mapnikBasicLayerId(1));
|
||||
assert.equal(layergroup.metadata.layers[1].meta.stats.estimatedFeatureCount, 5);
|
||||
assert.ok(!layergroup.metadata.layers[2]);
|
||||
testClient.drain(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('with mapnik and layer and httplayer should response with layer metadata with same order', function(done) {
|
||||
var testClient = new TestClient({
|
||||
version: '1.4.0',
|
||||
layers: [
|
||||
mapnikLayer1,
|
||||
httpLayer
|
||||
]
|
||||
});
|
||||
|
||||
testClient.getLayergroup(function(err, layergroup) {
|
||||
assert.ok(!err);
|
||||
assert.equal(layergroup.metadata.layers[0].id, mapnikBasicLayerId(0));
|
||||
assert.equal(layergroup.metadata.layers[0].type, 'mapnik');
|
||||
assert.equal(layergroup.metadata.layers[0].meta.stats.estimatedFeatureCount, 1);
|
||||
assert.equal(layergroup.metadata.layers[1].id, typeLayerId('http', 0));
|
||||
assert.equal(layergroup.metadata.layers[1].type, 'http');
|
||||
testClient.drain(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('with httpLayer and mapnik layer should response with layer metadata with same order', function(done) {
|
||||
var testClient = new TestClient({
|
||||
version: '1.4.0',
|
||||
layers: [
|
||||
httpLayer,
|
||||
mapnikLayer1
|
||||
]
|
||||
});
|
||||
|
||||
testClient.getLayergroup(function (err, layergroup) {
|
||||
assert.ok(!err);
|
||||
assert.equal(layergroup.metadata.layers[0].id, typeLayerId('http', 0));
|
||||
assert.equal(layergroup.metadata.layers[0].type, 'http');
|
||||
assert.ok(!layergroup.metadata.layers[0].meta.cartocss);
|
||||
assert.equal(layergroup.metadata.layers[1].meta.stats.estimatedFeatureCount, 1);
|
||||
assert.equal(layergroup.metadata.layers[1].id, mapnikBasicLayerId(0));
|
||||
assert.equal(layergroup.metadata.layers[1].type, 'mapnik');
|
||||
assert.equal(layergroup.metadata.layers[1].meta.cartocss, cartocss);
|
||||
testClient.drain(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('should work with different geom_column', function(done) {
|
||||
var testClient = new TestClient({
|
||||
version: '1.4.0',
|
||||
layers: [
|
||||
mapnikLayerGeomColumn
|
||||
]
|
||||
});
|
||||
|
||||
testClient.getLayergroup(function(err, layergroup) {
|
||||
assert.ok(!err);
|
||||
assert.equal(layergroup.metadata.layers[0].id, mapnikBasicLayerId(0));
|
||||
// we don't care about stats here as is an aliased column
|
||||
assert.ok(layergroup.metadata.layers[0].meta.stats.hasOwnProperty('estimatedFeatureCount'));
|
||||
testClient.drain(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('should not include the stats part if the FF is disabled', function(done) {
|
||||
global.environment.enabledFeatures.layerStats = false;
|
||||
var testClient = new TestClient({
|
||||
version: '1.4.0',
|
||||
layers: [
|
||||
httpLayer,
|
||||
mapnikLayer1,
|
||||
httpLayer
|
||||
]
|
||||
});
|
||||
|
||||
testClient.getLayergroup(function(err, layergroup) {
|
||||
assert.ok(!err);
|
||||
assert.equal(layergroup.metadata.layers[0].id, typeLayerId('http', 0));
|
||||
assert.equal(layergroup.metadata.layers[0].type, 'http');
|
||||
assert.equal(layergroup.metadata.layers[1].id, mapnikBasicLayerId(0));
|
||||
assert.equal(layergroup.metadata.layers[1].type, 'mapnik');
|
||||
assert.ok(!layergroup.metadata.layers[1].meta.hasOwnProperty('stats'));
|
||||
assert.equal(layergroup.metadata.layers[2].id, typeLayerId('http', 1));
|
||||
assert.equal(layergroup.metadata.layers[2].type, 'http');
|
||||
testClient.drain(done);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,225 +0,0 @@
|
||||
require('../../support/test_helper');
|
||||
|
||||
var assert = require('../../support/assert');
|
||||
var TestClient = require('../../support/test-client');
|
||||
|
||||
describe('multilayer stats disabled', function() {
|
||||
|
||||
before(function () {
|
||||
this.layerMetadataConfig = global.environment.enabledFeatures.layerMetadata;
|
||||
this.layerStatsConfig = global.environment.enabledFeatures.layerStats;
|
||||
global.environment.enabledFeatures.layerMetadata = true;
|
||||
global.environment.enabledFeatures.layerStats = false;
|
||||
});
|
||||
|
||||
after(function () {
|
||||
global.environment.enabledFeatures.layerMetadata = this.layerMetadataConfig;
|
||||
global.environment.enabledFeatures.layerStats = this.layerStatsConfig;
|
||||
});
|
||||
|
||||
|
||||
function testLayerMetadataStats(testScenario) {
|
||||
|
||||
it(testScenario.desc, function(done) {
|
||||
var mapConfig = {
|
||||
version: '1.3.0',
|
||||
layers: testScenario.layers
|
||||
};
|
||||
|
||||
var testClient = new TestClient(mapConfig);
|
||||
|
||||
testClient.getLayergroup(function(err, layergroup) {
|
||||
assert.ifError(err);
|
||||
layergroup.metadata.layers.forEach(function (layer) {
|
||||
if (layer.type !== 'torque' && layer.type !== 'mapnik') {
|
||||
assert.ok(!('stats' in layer.meta));
|
||||
} else if (layer.type !== 'torque') {
|
||||
assert.ok(!('stats' in layer.meta));
|
||||
assert.ok('cartocss' in layer.meta);
|
||||
} else {
|
||||
assert.ok('cartocss' in layer.meta);
|
||||
// check torque metadata at least match in number
|
||||
var torqueLayers = mapConfig.layers.filter(function(layer) { return layer.type === 'torque'; });
|
||||
if (torqueLayers.length) {
|
||||
assert.equal(Object.keys(layergroup.metadata.torque).length, torqueLayers.length);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
testClient.drain(done);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
var cartocssVersion = '2.3.0';
|
||||
var cartocss = '#layer { line-width:16; }';
|
||||
var sql = "select 1 as i, st_setsrid('LINESTRING(0 0, 1 0)'::geometry, 4326) as the_geom, " +
|
||||
"st_setsrid('LINESTRING(0 0, 1 0)'::geometry, 3857) as the_geom_webmercator";
|
||||
var sqlWadus = "select 1 as wadus, st_setsrid('LINESTRING(0 0, 1 0)'::geometry, 4326) as the_geom, " +
|
||||
"st_setsrid('LINESTRING(0 0, 1 0)'::geometry, 3857) as the_geom_webmercator";
|
||||
|
||||
var httpLayer = {
|
||||
type: 'http',
|
||||
options: {
|
||||
urlTemplate: 'http://{s}.basemaps.cartocdn.com/dark_nolabels/{z}/{x}/{y}.png',
|
||||
subdomains: ['a','b','c']
|
||||
}
|
||||
};
|
||||
|
||||
var torqueLayer = {
|
||||
type: 'torque',
|
||||
options: {
|
||||
sql: "select 1 id, '1970-01-02'::date d, 'POINT(0 0)'::geometry the_geom_webmercator",
|
||||
cartocss: [
|
||||
"Map {",
|
||||
"-torque-frame-count:2;",
|
||||
"-torque-resolution:3;",
|
||||
"-torque-time-attribute:d;",
|
||||
"-torque-aggregation-function:'count(id)';",
|
||||
"}"
|
||||
].join(' '),
|
||||
cartocss_version: '2.0.1'
|
||||
}
|
||||
};
|
||||
|
||||
var mapnikLayer = {
|
||||
type: 'mapnik',
|
||||
options: {
|
||||
sql: sql,
|
||||
cartocss_version: cartocssVersion,
|
||||
cartocss: cartocss
|
||||
}
|
||||
};
|
||||
|
||||
var mapnikInteractivityLayer = {
|
||||
type: 'mapnik',
|
||||
options: {
|
||||
sql: sql,
|
||||
cartocss_version: cartocssVersion,
|
||||
cartocss: cartocss,
|
||||
interactivity: 'i'
|
||||
}
|
||||
};
|
||||
|
||||
var cartodbLayer = {
|
||||
type: 'cartodb',
|
||||
options: {
|
||||
sql: sql,
|
||||
cartocss_version: cartocssVersion,
|
||||
cartocss: cartocss
|
||||
}
|
||||
};
|
||||
|
||||
var cartodbInteractivityLayer = {
|
||||
type: 'cartodb',
|
||||
options: {
|
||||
sql: sql,
|
||||
cartocss_version: cartocssVersion,
|
||||
cartocss: cartocss,
|
||||
interactivity: 'i'
|
||||
}
|
||||
};
|
||||
|
||||
var cartodbWadusInteractivityLayer = {
|
||||
type: 'cartodb',
|
||||
options: {
|
||||
sql: sqlWadus,
|
||||
cartocss_version: cartocssVersion,
|
||||
cartocss: cartocss,
|
||||
interactivity: 'wadus'
|
||||
}
|
||||
};
|
||||
|
||||
var noTypeLayer = {
|
||||
options: {
|
||||
sql: sql,
|
||||
cartocss_version: cartocssVersion,
|
||||
cartocss: cartocss
|
||||
}
|
||||
};
|
||||
|
||||
var noTypeInteractivityLayer = {
|
||||
options: {
|
||||
sql: sql,
|
||||
cartocss_version: cartocssVersion,
|
||||
cartocss: cartocss,
|
||||
interactivity: 'i'
|
||||
}
|
||||
};
|
||||
|
||||
var testScenarios = [
|
||||
{
|
||||
desc: 'one layer, no interactivity',
|
||||
layers: [cartodbLayer]
|
||||
},
|
||||
{
|
||||
desc: 'two layers, different interactivity columns',
|
||||
layers: [
|
||||
cartodbWadusInteractivityLayer,
|
||||
cartodbInteractivityLayer
|
||||
]
|
||||
},
|
||||
{
|
||||
desc: 'torque + interactivity layers',
|
||||
layers: [
|
||||
torqueLayer,
|
||||
cartodbWadusInteractivityLayer,
|
||||
cartodbInteractivityLayer
|
||||
]
|
||||
},
|
||||
{
|
||||
desc: 'interactivity + torque + interactivity',
|
||||
layers: [
|
||||
cartodbInteractivityLayer,
|
||||
torqueLayer,
|
||||
cartodbInteractivityLayer
|
||||
]
|
||||
},
|
||||
{
|
||||
desc: 'http + interactivity + torque + no interactivity + torque + interactivity',
|
||||
layers: [
|
||||
httpLayer,
|
||||
cartodbInteractivityLayer,
|
||||
torqueLayer,
|
||||
cartodbLayer,
|
||||
torqueLayer,
|
||||
cartodbWadusInteractivityLayer
|
||||
]
|
||||
},
|
||||
{
|
||||
desc: 'mapnik type – two layers, interactivity mix',
|
||||
layers: [
|
||||
mapnikLayer,
|
||||
mapnikInteractivityLayer
|
||||
]
|
||||
},
|
||||
{
|
||||
desc: 'mapnik type – http + interactivity + torque + interactivity',
|
||||
layers: [
|
||||
httpLayer,
|
||||
mapnikInteractivityLayer,
|
||||
torqueLayer,
|
||||
cartodbInteractivityLayer
|
||||
]
|
||||
},
|
||||
{
|
||||
desc: 'no type – two layers, interactivity mix',
|
||||
layers: [
|
||||
noTypeLayer,
|
||||
noTypeInteractivityLayer
|
||||
]
|
||||
},
|
||||
{
|
||||
desc: 'no type – http + interactivity + torque + interactivity',
|
||||
layers: [
|
||||
httpLayer,
|
||||
noTypeInteractivityLayer,
|
||||
torqueLayer,
|
||||
noTypeInteractivityLayer
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
testScenarios.forEach(testLayerMetadataStats);
|
||||
|
||||
});
|
||||
@@ -1,786 +0,0 @@
|
||||
require('../support/test_helper');
|
||||
|
||||
const assert = require('../support/assert');
|
||||
const TestClient = require('../support/test-client');
|
||||
|
||||
const timeoutErrorTilePath = `${process.cwd()}/assets/render-timeout-fallback.png`;
|
||||
|
||||
const pointSleepSql = `
|
||||
SELECT
|
||||
pg_sleep(0.3),
|
||||
'SRID=3857;POINT(0 0)'::geometry the_geom_webmercator,
|
||||
1 cartodb_id,
|
||||
2 val
|
||||
`;
|
||||
|
||||
const validationPointSleepSql = `
|
||||
SELECT
|
||||
pg_sleep(0.3),
|
||||
ST_Transform('SRID=4326;POINT(-180 85.05112877)'::geometry, 3857) the_geom_webmercator,
|
||||
1 cartodb_id,
|
||||
2 val
|
||||
`;
|
||||
|
||||
const createMapConfig = ({
|
||||
version = '1.6.0',
|
||||
type = 'cartodb',
|
||||
sql = pointSleepSql,
|
||||
cartocss = TestClient.CARTOCSS.POINTS,
|
||||
cartocss_version = '2.3.0',
|
||||
interactivity = 'cartodb_id',
|
||||
countBy = 'cartodb_id',
|
||||
attributes
|
||||
} = {}) => ({
|
||||
version,
|
||||
layers: [{
|
||||
type,
|
||||
options: {
|
||||
source: {
|
||||
id: 'a0'
|
||||
},
|
||||
cartocss,
|
||||
cartocss_version,
|
||||
attributes,
|
||||
interactivity
|
||||
}
|
||||
}],
|
||||
analyses: [
|
||||
{
|
||||
id: 'a0',
|
||||
type: 'source',
|
||||
params: {
|
||||
query: sql
|
||||
}
|
||||
}
|
||||
],
|
||||
dataviews: {
|
||||
count: {
|
||||
source: {
|
||||
id: 'a0'
|
||||
},
|
||||
type: 'formula',
|
||||
options: {
|
||||
column: countBy,
|
||||
operation: 'count'
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const DATASOURCE_TIMEOUT_ERROR = {
|
||||
errors: ['You are over platform\'s limits. Please contact us to know more details'],
|
||||
errors_with_context: [{
|
||||
type: 'limit',
|
||||
subtype: 'datasource',
|
||||
message: 'You are over platform\'s limits. Please contact us to know more details'
|
||||
}]
|
||||
};
|
||||
|
||||
describe('user database timeout limit', function () {
|
||||
describe('dataview', function () {
|
||||
beforeEach(function (done) {
|
||||
const mapconfig = createMapConfig();
|
||||
this.testClient = new TestClient(mapconfig, 1234);
|
||||
this.testClient.setUserDatabaseTimeoutLimit(200, done);
|
||||
});
|
||||
|
||||
afterEach(function (done) {
|
||||
this.testClient.setUserDatabaseTimeoutLimit(0, (err) => {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
this.testClient.drain(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('layergroup creation works but dataview request fails due to statement timeout', function (done) {
|
||||
const params = {
|
||||
response: {
|
||||
status: 429,
|
||||
headers: {
|
||||
'Content-Type': 'application/json; charset=utf-8'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.testClient.getDataview('count', params, (err, timeoutError) => {
|
||||
assert.ifError(err);
|
||||
|
||||
assert.deepEqual(timeoutError, DATASOURCE_TIMEOUT_ERROR);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('raster', function () {
|
||||
describe('while validating in layergroup creation', function () {
|
||||
beforeEach(function (done) {
|
||||
const mapconfig = createMapConfig({ sql: validationPointSleepSql });
|
||||
this.testClient = new TestClient(mapconfig, 1234);
|
||||
this.testClient.setUserDatabaseTimeoutLimit(200, done);
|
||||
});
|
||||
|
||||
afterEach(function (done) {
|
||||
this.testClient.setUserDatabaseTimeoutLimit(0, (err) => {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
this.testClient.drain(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('fails due to statement timeout', function (done) {
|
||||
const expectedResponse = {
|
||||
status: 429,
|
||||
headers: {
|
||||
'Content-Type': 'application/json; charset=utf-8'
|
||||
}
|
||||
};
|
||||
|
||||
this.testClient.getLayergroup(expectedResponse, (err, timeoutError) => {
|
||||
assert.deepEqual(timeoutError, {
|
||||
errors: [ 'You are over platform\'s limits. Please contact us to know more details' ],
|
||||
errors_with_context: [{
|
||||
type: 'limit',
|
||||
subtype: 'datasource',
|
||||
message: 'You are over platform\'s limits. Please contact us to know more details',
|
||||
layer: { id: 'layer0', index: 0, type: 'mapnik' }
|
||||
}]
|
||||
});
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('fetching raster tiles', function () {
|
||||
describe('with user\'s timeout of 200 ms', function () {
|
||||
describe('with onTileErrorStrategy ENABLED', function () {
|
||||
let onTileErrorStrategy;
|
||||
|
||||
beforeEach(function (done) {
|
||||
onTileErrorStrategy = global.environment.enabledFeatures.onTileErrorStrategy;
|
||||
global.environment.enabledFeatures.onTileErrorStrategy = true;
|
||||
|
||||
const mapconfig = createMapConfig();
|
||||
this.testClient = new TestClient(mapconfig, 1234);
|
||||
const expectedResponse = {
|
||||
status: 200,
|
||||
headers: {
|
||||
'Content-Type': 'application/json; charset=utf-8'
|
||||
}
|
||||
};
|
||||
|
||||
this.testClient.setUserDatabaseTimeoutLimit(200, (err) => {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
this.testClient.getLayergroup(expectedResponse, (err, res) => {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
this.layergroupid = res.layergroupid;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function (done) {
|
||||
global.environment.enabledFeatures.onTileErrorStrategy = onTileErrorStrategy;
|
||||
this.testClient.setUserDatabaseTimeoutLimit(0, (err) => {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
this.testClient.drain(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('"png" fails due to statement timeout', function (done) {
|
||||
const params = {
|
||||
layergroupid: this.layergroupid,
|
||||
format: 'png',
|
||||
layers: [ 0 ]
|
||||
};
|
||||
|
||||
this.testClient.getTile(0, 0, 0, params, (err, res, tile) => {
|
||||
assert.ifError(err);
|
||||
|
||||
assert.imageIsSimilarToFile(tile, timeoutErrorTilePath, 0.05, (err) => {
|
||||
assert.ifError(err);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('"static png" fails due to statement timeout', function (done) {
|
||||
const params = {
|
||||
layergroupid: this.layergroupid,
|
||||
zoom: 0,
|
||||
lat: 0,
|
||||
lng: 0,
|
||||
width: 256,
|
||||
height: 256,
|
||||
format: 'png'
|
||||
};
|
||||
|
||||
this.testClient.getStaticCenter(params, function (err, res, tile) {
|
||||
assert.ifError(err);
|
||||
|
||||
assert.imageIsSimilarToFile(tile, timeoutErrorTilePath, 0.05, (err) => {
|
||||
assert.ifError(err);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('with onTileErrorStrategy DISABLED', function () {
|
||||
let onTileErrorStrategy;
|
||||
|
||||
beforeEach(function (done) {
|
||||
onTileErrorStrategy = global.environment.enabledFeatures.onTileErrorStrategy;
|
||||
global.environment.enabledFeatures.onTileErrorStrategy = false;
|
||||
|
||||
const mapconfig = createMapConfig();
|
||||
this.testClient = new TestClient(mapconfig, 1234);
|
||||
const expectedResponse = {
|
||||
status: 200,
|
||||
headers: {
|
||||
'Content-Type': 'application/json; charset=utf-8'
|
||||
}
|
||||
};
|
||||
|
||||
this.testClient.setUserDatabaseTimeoutLimit(200, (err) => {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
this.testClient.getLayergroup(expectedResponse, (err, res) => {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
this.layergroupid = res.layergroupid;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function (done) {
|
||||
global.environment.enabledFeatures.onTileErrorStrategy = onTileErrorStrategy;
|
||||
|
||||
this.testClient.setUserDatabaseTimeoutLimit(0, (err) => {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
this.testClient.drain(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('"png" fails due to statement timeout', function (done) {
|
||||
const params = {
|
||||
layergroupid: this.layergroupid,
|
||||
format: 'png',
|
||||
layers: [ 0 ],
|
||||
response: {
|
||||
status: 429,
|
||||
headers: {
|
||||
'Content-Type': 'application/json; charset=utf-8'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.testClient.getTile(0, 0, 0, params, (err, res, timeoutError) => {
|
||||
assert.ifError(err);
|
||||
|
||||
assert.deepEqual(timeoutError, DATASOURCE_TIMEOUT_ERROR);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('"static png" fails due to statement timeout', function (done) {
|
||||
const params = {
|
||||
layergroupid: this.layergroupid,
|
||||
zoom: 0,
|
||||
lat: 0,
|
||||
lng: 0,
|
||||
width: 256,
|
||||
height: 256,
|
||||
format: 'png',
|
||||
response: {
|
||||
status: 429,
|
||||
headers: {
|
||||
'Content-Type': 'application/json; charset=utf-8'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.testClient.getStaticCenter(params, (err, res, timeoutError) => {
|
||||
assert.ifError(err);
|
||||
|
||||
assert.deepEqual(timeoutError, DATASOURCE_TIMEOUT_ERROR);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('vector', function () {
|
||||
describe('while validating in layergroup creation', function () {
|
||||
beforeEach(function (done) {
|
||||
const mapconfig = createMapConfig({ sql: validationPointSleepSql });
|
||||
this.testClient = new TestClient(mapconfig, 1234);
|
||||
this.testClient.setUserDatabaseTimeoutLimit(200, done);
|
||||
});
|
||||
|
||||
afterEach(function (done) {
|
||||
this.testClient.setUserDatabaseTimeoutLimit(0, (err) => {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
this.testClient.drain(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('fails due to statement timeout', function (done) {
|
||||
const expectedResponse = {
|
||||
status: 429,
|
||||
headers: {
|
||||
'Content-Type': 'application/json; charset=utf-8'
|
||||
}
|
||||
};
|
||||
|
||||
this.testClient.getLayergroup(expectedResponse, (err, timeoutError) => {
|
||||
assert.deepEqual(timeoutError, {
|
||||
errors: [ 'You are over platform\'s limits. Please contact us to know more details' ],
|
||||
errors_with_context: [{
|
||||
type: 'limit',
|
||||
subtype: 'datasource',
|
||||
message: 'You are over platform\'s limits. Please contact us to know more details',
|
||||
layer: { id: 'layer0', index: 0, type: 'mapnik' }
|
||||
}]
|
||||
});
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('fetching vector tiles', function () {
|
||||
beforeEach(function (done) {
|
||||
const mapconfig = createMapConfig();
|
||||
this.testClient = new TestClient(mapconfig, 1234);
|
||||
const expectedResponse = {
|
||||
status: 200,
|
||||
headers: {
|
||||
'Content-Type': 'application/json; charset=utf-8'
|
||||
}
|
||||
};
|
||||
|
||||
this.testClient.getLayergroup(expectedResponse, (err, res) => {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
this.layergroupid = res.layergroupid;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function (done) {
|
||||
this.testClient.drain(done);
|
||||
});
|
||||
|
||||
describe('with user\'s timeout of 200 ms', function () {
|
||||
beforeEach(function (done) {
|
||||
this.testClient.setUserDatabaseTimeoutLimit(200, done);
|
||||
});
|
||||
|
||||
afterEach(function (done) {
|
||||
this.testClient.setUserDatabaseTimeoutLimit(0, done);
|
||||
});
|
||||
|
||||
it('"mvt" fails due to statement timeout', function (done) {
|
||||
const params = {
|
||||
layergroupid: this.layergroupid,
|
||||
format: 'mvt',
|
||||
layers: [ 0 ],
|
||||
response: {
|
||||
status: 429,
|
||||
headers: {
|
||||
'Content-Type': 'application/json; charset=utf-8'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.testClient.getTile(0, 0, 0, params, (err, res, timeoutError) => {
|
||||
assert.ifError(err);
|
||||
|
||||
assert.deepEqual(timeoutError, DATASOURCE_TIMEOUT_ERROR);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('interactivity', function () {
|
||||
describe('while validating in layergroup creation', function () {
|
||||
beforeEach(function (done) {
|
||||
const mapconfig = createMapConfig({ sql: validationPointSleepSql, interactivity: 'val' });
|
||||
this.testClient = new TestClient(mapconfig, 1234);
|
||||
this.testClient.setUserDatabaseTimeoutLimit(200, done);
|
||||
});
|
||||
|
||||
afterEach(function (done) {
|
||||
this.testClient.setUserDatabaseTimeoutLimit(0, (err) => {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
this.testClient.drain(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('fails due to statement timeout', function (done) {
|
||||
const expectedResponse = {
|
||||
status: 429,
|
||||
headers: {
|
||||
'Content-Type': 'application/json; charset=utf-8'
|
||||
}
|
||||
};
|
||||
|
||||
this.testClient.getLayergroup(expectedResponse, (err, timeoutError) => {
|
||||
assert.deepEqual(timeoutError, {
|
||||
errors: [ 'You are over platform\'s limits. Please contact us to know more details' ],
|
||||
errors_with_context: [{
|
||||
type: 'limit',
|
||||
subtype: 'datasource',
|
||||
message: 'You are over platform\'s limits. Please contact us to know more details',
|
||||
layer: { id: 'layer0', index: 0, type: 'mapnik' }
|
||||
}]
|
||||
});
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('fetching interactivity tiles', function () {
|
||||
beforeEach(function (done) {
|
||||
const mapconfig = createMapConfig({ interactivity: 'val' });
|
||||
this.testClient = new TestClient(mapconfig, 1234);
|
||||
const expectedResponse = {
|
||||
status: 200,
|
||||
headers: {
|
||||
'Content-Type': 'application/json; charset=utf-8'
|
||||
}
|
||||
};
|
||||
|
||||
this.testClient.getLayergroup(expectedResponse, (err, res) => {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
this.layergroupid = res.layergroupid;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function (done) {
|
||||
this.testClient.drain(done);
|
||||
});
|
||||
|
||||
describe('with user\'s timeout of 200 ms', function () {
|
||||
beforeEach(function (done) {
|
||||
this.testClient.setUserDatabaseTimeoutLimit(200, done);
|
||||
});
|
||||
|
||||
afterEach(function (done) {
|
||||
this.testClient.setUserDatabaseTimeoutLimit(0, done);
|
||||
});
|
||||
|
||||
it('"grid.json" fails due to statement timeout', function (done) {
|
||||
const params = {
|
||||
layergroupid: this.layergroupid,
|
||||
format: 'grid.json',
|
||||
layers: 'mapnik',
|
||||
response: {
|
||||
status: 429,
|
||||
headers: {
|
||||
'Content-Type': 'application/json; charset=utf-8'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.testClient.getTile(0, 0, 0, params, (err, res, timeoutError) => {
|
||||
assert.ifError(err);
|
||||
|
||||
assert.deepEqual(timeoutError, DATASOURCE_TIMEOUT_ERROR);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('torque', function () {
|
||||
describe('while validating in layergroup creation', function () {
|
||||
beforeEach(function (done) {
|
||||
const mapconfig = createMapConfig({
|
||||
type: 'torque',
|
||||
cartocss: TestClient.CARTOCSS.TORQUE
|
||||
});
|
||||
this.testClient = new TestClient(mapconfig, 1234);
|
||||
this.testClient.setUserDatabaseTimeoutLimit(200, done);
|
||||
});
|
||||
|
||||
afterEach(function (done) {
|
||||
this.testClient.setUserDatabaseTimeoutLimit(0, (err) => {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
this.testClient.drain(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('fails due to statement timeout', function (done) {
|
||||
const expectedResponse = {
|
||||
status: 429,
|
||||
headers: {
|
||||
'Content-Type': 'application/json; charset=utf-8'
|
||||
}
|
||||
};
|
||||
|
||||
this.testClient.getLayergroup(expectedResponse, (err, timeoutError) => {
|
||||
assert.deepEqual(timeoutError, {
|
||||
errors: [ 'You are over platform\'s limits. Please contact us to know more details' ],
|
||||
errors_with_context: [{
|
||||
type: 'limit',
|
||||
subtype: 'datasource',
|
||||
message: 'You are over platform\'s limits. Please contact us to know more details',
|
||||
layer: { id: 'torque-layer0', index: 0, type: 'torque' }
|
||||
}]
|
||||
});
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('fetching torque tiles', function () {
|
||||
beforeEach(function (done) {
|
||||
const mapconfig = createMapConfig({
|
||||
type: 'torque',
|
||||
cartocss: TestClient.CARTOCSS.TORQUE
|
||||
});
|
||||
this.testClient = new TestClient(mapconfig, 1234);
|
||||
const expectedResponse = {
|
||||
status: 200,
|
||||
headers: {
|
||||
'Content-Type': 'application/json; charset=utf-8'
|
||||
}
|
||||
};
|
||||
|
||||
this.testClient.getLayergroup(expectedResponse, (err, res) => {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
this.layergroupid = res.layergroupid;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function (done) {
|
||||
this.testClient.drain(done);
|
||||
});
|
||||
|
||||
describe('with user\'s timeout of 200 ms', function () {
|
||||
beforeEach(function (done) {
|
||||
this.testClient.setUserDatabaseTimeoutLimit(200, done);
|
||||
});
|
||||
|
||||
afterEach(function (done) {
|
||||
this.testClient.setUserDatabaseTimeoutLimit(0, done);
|
||||
});
|
||||
|
||||
it('"torque.json" fails due to statement timeout', function (done) {
|
||||
const params = {
|
||||
layergroupid: this.layergroupid,
|
||||
format: 'torque.json',
|
||||
layers: [ 0 ],
|
||||
response: {
|
||||
status: 429,
|
||||
headers: {
|
||||
'Content-Type': 'application/json; charset=utf-8'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.testClient.getTile(0, 0, 0, params, (err, res, timeoutError) => {
|
||||
assert.ifError(err);
|
||||
|
||||
assert.deepEqual(timeoutError, DATASOURCE_TIMEOUT_ERROR);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('".png" fails due to statement timeout', function (done) {
|
||||
const params = {
|
||||
layergroupid: this.layergroupid,
|
||||
format: 'torque.png',
|
||||
layers: [ 0 ],
|
||||
response: {
|
||||
status: 429,
|
||||
headers: {
|
||||
'Content-Type': 'application/json; charset=utf-8'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.testClient.getTile(0, 0, 0, params, (err, res, attributes) => {
|
||||
assert.ifError(err);
|
||||
|
||||
assert.deepEqual(attributes, DATASOURCE_TIMEOUT_ERROR);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('attributes:', function () {
|
||||
describe('while validating in map instatiation', function () {
|
||||
beforeEach(function (done) {
|
||||
const mapconfig = createMapConfig({
|
||||
attributes: {
|
||||
id: 'cartodb_id',
|
||||
columns: [ 'val' ]
|
||||
}
|
||||
});
|
||||
this.testClient = new TestClient(mapconfig, 1234);
|
||||
this.testClient.setUserDatabaseTimeoutLimit(200, done);
|
||||
});
|
||||
|
||||
afterEach(function (done) {
|
||||
this.testClient.setUserDatabaseTimeoutLimit(0, (err) => {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
this.testClient.drain(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('layergroup creation fails due to statement timeout', function (done) {
|
||||
const expectedResponse = {
|
||||
status: 429,
|
||||
headers: {
|
||||
'Content-Type': 'application/json; charset=utf-8'
|
||||
}
|
||||
};
|
||||
|
||||
this.testClient.getLayergroup(expectedResponse, (err, timeoutError) => {
|
||||
assert.deepEqual(timeoutError, {
|
||||
errors: [ 'You are over platform\'s limits. Please contact us to know more details' ],
|
||||
errors_with_context: [{
|
||||
type: 'limit',
|
||||
subtype: 'datasource',
|
||||
message: 'You are over platform\'s limits. Please contact us to know more details',
|
||||
layer: {
|
||||
id: 'layer0',
|
||||
index: 0,
|
||||
type: 'mapnik'
|
||||
}
|
||||
}]
|
||||
});
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('fetching by feature id', function () {
|
||||
beforeEach(function (done) {
|
||||
const mapconfig = createMapConfig({
|
||||
attributes: {
|
||||
id: 'cartodb_id',
|
||||
columns: [ 'val' ]
|
||||
}
|
||||
});
|
||||
|
||||
this.testClient = new TestClient(mapconfig, 1234);
|
||||
|
||||
const expectedResponse = {
|
||||
status: 200,
|
||||
headers: {
|
||||
'Content-Type': 'application/json; charset=utf-8'
|
||||
}
|
||||
};
|
||||
|
||||
this.testClient.getLayergroup(expectedResponse, (err, res) => {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
this.layergroupid = res.layergroupid;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function (done) {
|
||||
this.testClient.drain(done);
|
||||
});
|
||||
|
||||
describe('with user\'s timeout of 200 ms', function () {
|
||||
beforeEach(function (done) {
|
||||
this.testClient.setUserDatabaseTimeoutLimit(200, done);
|
||||
});
|
||||
|
||||
afterEach(function (done) {
|
||||
this.testClient.setUserDatabaseTimeoutLimit(0, done);
|
||||
});
|
||||
|
||||
it('fails due to statement timeout', function (done) {
|
||||
const params = {
|
||||
layergroupid: this.layergroupid,
|
||||
featureId: 1,
|
||||
layer: 0,
|
||||
response: {
|
||||
status: 429,
|
||||
headers: {
|
||||
'Content-Type': 'application/json; charset=utf-8'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.testClient.getAttributes(params, (err, res, timeoutError) => {
|
||||
assert.ifError(err);
|
||||
|
||||
assert.deepEqual(timeoutError, DATASOURCE_TIMEOUT_ERROR);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,394 +0,0 @@
|
||||
require('../support/test_helper');
|
||||
|
||||
const assert = require('../support/assert');
|
||||
const TestClient = require('../support/test-client');
|
||||
|
||||
const timeoutErrorTilePath = `${process.cwd()}/assets/render-timeout-fallback.png`;
|
||||
|
||||
const pointSleepSql = `
|
||||
SELECT
|
||||
pg_sleep(0.5),
|
||||
'SRID=3857;POINT(0 0)'::geometry the_geom_webmercator,
|
||||
1 cartodb_id,
|
||||
2 val
|
||||
`;
|
||||
|
||||
// during instatiation we validate tile 30/0/0, creating a point in that tile `pg_sleep` will throw a timeout
|
||||
const validationPointSleepSql = `
|
||||
SELECT
|
||||
pg_sleep(0.5),
|
||||
ST_Transform('SRID=4326;POINT(-180 85.05112877)'::geometry, 3857) the_geom_webmercator,
|
||||
1 cartodb_id,
|
||||
2 val
|
||||
`;
|
||||
|
||||
const createMapConfig = ({
|
||||
version = '1.6.0',
|
||||
type = 'cartodb',
|
||||
sql = pointSleepSql,
|
||||
cartocss = TestClient.CARTOCSS.POINTS,
|
||||
cartocss_version = '2.3.0',
|
||||
interactivity = 'cartodb_id',
|
||||
countBy = 'cartodb_id'
|
||||
} = {}) => ({
|
||||
version,
|
||||
layers: [{
|
||||
type,
|
||||
options: {
|
||||
source: {
|
||||
id: 'a0'
|
||||
},
|
||||
cartocss,
|
||||
cartocss_version,
|
||||
interactivity
|
||||
}
|
||||
}],
|
||||
analyses: [
|
||||
{
|
||||
id: 'a0',
|
||||
type: 'source',
|
||||
params: {
|
||||
query: sql
|
||||
}
|
||||
}
|
||||
],
|
||||
dataviews: {
|
||||
count: {
|
||||
source: {
|
||||
id: 'a0'
|
||||
},
|
||||
type: 'formula',
|
||||
options: {
|
||||
column: countBy,
|
||||
operation: 'count'
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
describe('user render timeout limit', function () {
|
||||
describe('map instantiation => validation', function () {
|
||||
beforeEach(function (done) {
|
||||
const mapconfig = createMapConfig({ sql: validationPointSleepSql });
|
||||
this.testClient = new TestClient(mapconfig, 1234);
|
||||
this.testClient.setUserRenderTimeoutLimit('localhost', 50, done);
|
||||
});
|
||||
|
||||
afterEach(function (done) {
|
||||
this.testClient.setUserRenderTimeoutLimit('localhost', 0, (err) => {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
this.testClient.drain(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('layergroup creation fails due to statement timeout', function (done) {
|
||||
const expectedResponse = {
|
||||
status: 429,
|
||||
headers: {
|
||||
'Content-Type': 'application/json; charset=utf-8'
|
||||
}
|
||||
};
|
||||
|
||||
this.testClient.getLayergroup(expectedResponse, (err, timeoutError) => {
|
||||
assert.ifError(err);
|
||||
|
||||
assert.deepEqual(timeoutError, {
|
||||
errors: ["You are over platform\'s limits. Please contact us to know more details"],
|
||||
errors_with_context: [{
|
||||
type: 'limit',
|
||||
subtype: 'render',
|
||||
message: "You are over platform\'s limits. Please contact us to know more details",
|
||||
layer: {
|
||||
id: "layer0",
|
||||
index: 0,
|
||||
type: "mapnik"
|
||||
}
|
||||
}]
|
||||
});
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('raster', function () {
|
||||
describe('with onTileErrorStrategy ENABLED', function () {
|
||||
let onTileErrorStrategy;
|
||||
|
||||
beforeEach(function (done) {
|
||||
onTileErrorStrategy = global.environment.enabledFeatures.onTileErrorStrategy;
|
||||
global.environment.enabledFeatures.onTileErrorStrategy = true;
|
||||
|
||||
const mapconfig = createMapConfig();
|
||||
this.testClient = new TestClient(mapconfig, 1234);
|
||||
this.testClient.setUserRenderTimeoutLimit('localhost', 50, done);
|
||||
});
|
||||
|
||||
afterEach(function (done) {
|
||||
global.environment.enabledFeatures.onTileErrorStrategy = onTileErrorStrategy;
|
||||
|
||||
this.testClient.setUserRenderTimeoutLimit('localhost', 0, (err) => {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
this.testClient.drain(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('layergroup creation works but tile request fails due to render timeout', function (done) {
|
||||
this.testClient.getTile(0, 0, 0, {}, (err, res, tile) => {
|
||||
assert.ifError(err);
|
||||
|
||||
assert.imageIsSimilarToFile(tile, timeoutErrorTilePath, 0.05, (err) => {
|
||||
assert.ifError(err);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('with onTileErrorStrategy DISABLED', function() {
|
||||
var onTileErrorStrategy;
|
||||
|
||||
beforeEach(function (done) {
|
||||
onTileErrorStrategy = global.environment.enabledFeatures.onTileErrorStrategy;
|
||||
global.environment.enabledFeatures.onTileErrorStrategy = false;
|
||||
|
||||
const mapconfig = createMapConfig();
|
||||
this.testClient = new TestClient(mapconfig, 1234);
|
||||
this.testClient.setUserRenderTimeoutLimit('localhost', 50, done);
|
||||
});
|
||||
|
||||
afterEach(function (done) {
|
||||
global.environment.enabledFeatures.onTileErrorStrategy = onTileErrorStrategy;
|
||||
|
||||
this.testClient.setUserRenderTimeoutLimit('localhost', 0, (err) => {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
this.testClient.drain(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('layergroup creation works and render tile fails', function (done) {
|
||||
var params = {
|
||||
response: {
|
||||
status: 429,
|
||||
headers: {
|
||||
'Content-Type': 'application/json; charset=utf-8'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.testClient.getTile(0, 0, 0, params, (err, res, timeoutError) => {
|
||||
assert.ifError(err);
|
||||
|
||||
assert.deepEqual(timeoutError, {
|
||||
errors: ["You are over platform\'s limits. Please contact us to know more details"],
|
||||
errors_with_context: [{
|
||||
type: 'limit',
|
||||
subtype: 'render',
|
||||
message: "You are over platform\'s limits. Please contact us to know more details"
|
||||
}]
|
||||
});
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('vector', function () {
|
||||
beforeEach(function (done) {
|
||||
const mapconfig = createMapConfig();
|
||||
this.testClient = new TestClient(mapconfig, 1234);
|
||||
this.testClient.setUserRenderTimeoutLimit('localhost', 50, done);
|
||||
});
|
||||
|
||||
afterEach(function (done) {
|
||||
this.testClient.setUserRenderTimeoutLimit('localhost', 0, (err) => {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
this.testClient.drain(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('layergroup creation works but vector tile request fails due to render timeout', function (done) {
|
||||
const params = {
|
||||
format: 'mvt',
|
||||
response: {
|
||||
status: 429,
|
||||
headers: {
|
||||
'Content-Type': 'application/json; charset=utf-8'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.testClient.getTile(0, 0, 0, params, (err, res, tile) => {
|
||||
assert.ifError(err);
|
||||
|
||||
assert.deepEqual(tile, {
|
||||
errors: ['You are over platform\'s limits. Please contact us to know more details'],
|
||||
errors_with_context: [{
|
||||
type: 'limit',
|
||||
subtype: 'render',
|
||||
message: 'You are over platform\'s limits. Please contact us to know more details'
|
||||
}]
|
||||
});
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('interativity', function () {
|
||||
beforeEach(function (done) {
|
||||
const mapconfig = createMapConfig();
|
||||
this.testClient = new TestClient(mapconfig, 1234);
|
||||
this.testClient.setUserRenderTimeoutLimit('localhost', 50, done);
|
||||
});
|
||||
|
||||
afterEach(function (done) {
|
||||
this.testClient.setUserRenderTimeoutLimit('localhost', 0, (err) => {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
this.testClient.drain(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('layergroup creation works but "grid.json" tile request fails due to render timeout', function (done) {
|
||||
const params = {
|
||||
layers: 'mapnik',
|
||||
format: 'grid.json',
|
||||
response: {
|
||||
status: 429,
|
||||
headers: {
|
||||
'Content-Type': 'application/json; charset=utf-8'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.testClient.getTile(0, 0, 0, params, (err, res, tile) => {
|
||||
assert.ifError(err);
|
||||
|
||||
assert.deepEqual(tile, {
|
||||
errors: ['You are over platform\'s limits. Please contact us to know more details'],
|
||||
errors_with_context: [{
|
||||
type: 'limit',
|
||||
subtype: 'render',
|
||||
message: 'You are over platform\'s limits. Please contact us to know more details'
|
||||
}]
|
||||
});
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('static images', function () {
|
||||
describe('with onTileErrorStrategy ENABLED', function () {
|
||||
let onTileErrorStrategy;
|
||||
|
||||
beforeEach(function (done) {
|
||||
onTileErrorStrategy = global.environment.enabledFeatures.onTileErrorStrategy;
|
||||
global.environment.enabledFeatures.onTileErrorStrategy = true;
|
||||
|
||||
const mapconfig = createMapConfig();
|
||||
this.testClient = new TestClient(mapconfig, 1234);
|
||||
this.testClient.setUserRenderTimeoutLimit('localhost', 50, done);
|
||||
});
|
||||
|
||||
afterEach(function (done) {
|
||||
global.environment.enabledFeatures.onTileErrorStrategy = onTileErrorStrategy;
|
||||
|
||||
this.testClient.setUserRenderTimeoutLimit('localhost', 0, (err) => {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
this.testClient.drain(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('layergroup creation works but static image fails due to render timeout', function (done) {
|
||||
const params = {
|
||||
zoom: 0,
|
||||
lat: 0,
|
||||
lng: 0,
|
||||
width: 256,
|
||||
height: 256,
|
||||
format: 'png'
|
||||
};
|
||||
|
||||
this.testClient.getStaticCenter(params, function (err, res, tile) {
|
||||
assert.ifError(err);
|
||||
|
||||
assert.imageIsSimilarToFile(tile, timeoutErrorTilePath, 0.05, (err) => {
|
||||
assert.ifError(err);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('with onTileErrorStrategy DISABLED', function() {
|
||||
var onTileErrorStrategy;
|
||||
|
||||
beforeEach(function (done) {
|
||||
onTileErrorStrategy = global.environment.enabledFeatures.onTileErrorStrategy;
|
||||
global.environment.enabledFeatures.onTileErrorStrategy = false;
|
||||
|
||||
const mapconfig = createMapConfig();
|
||||
this.testClient = new TestClient(mapconfig, 1234);
|
||||
this.testClient.setUserRenderTimeoutLimit('localhost', 50, done);
|
||||
});
|
||||
|
||||
afterEach(function (done) {
|
||||
global.environment.enabledFeatures.onTileErrorStrategy = onTileErrorStrategy;
|
||||
|
||||
this.testClient.setUserRenderTimeoutLimit('localhost', 0, (err) => {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
this.testClient.drain(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('layergroup creation works and render tile fails', function (done) {
|
||||
const params = {
|
||||
zoom: 0,
|
||||
lat: 0,
|
||||
lng: 0,
|
||||
width: 256,
|
||||
height: 256,
|
||||
format: 'png',
|
||||
response: {
|
||||
status: 429,
|
||||
headers: {
|
||||
'Content-Type': 'application/json; charset=utf-8'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.testClient.getStaticCenter(params, function (err, res, timeoutError) {
|
||||
assert.ifError(err);
|
||||
|
||||
assert.deepEqual(timeoutError, {
|
||||
errors: ["You are over platform\'s limits. Please contact us to know more details"],
|
||||
errors_with_context: [{
|
||||
type: 'limit',
|
||||
subtype: 'render',
|
||||
message: "You are over platform\'s limits. Please contact us to know more details"
|
||||
}]
|
||||
});
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -322,25 +322,6 @@ describe('widgets', function() {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
[adm0name].forEach(function(userQuery) {
|
||||
it('should search with sum aggregation: ' + userQuery, function(done) {
|
||||
this.testClient = new TestClient(aggregationSumMapConfig);
|
||||
this.testClient.widgetSearch('adm0name', userQuery, function (err, res, searchResult) {
|
||||
assert.ok(!err, err);
|
||||
assert.ok(searchResult);
|
||||
assert.equal(searchResult.type, 'aggregation');
|
||||
|
||||
assert.equal(searchResult.categories.length, 1);
|
||||
assert.deepEqual(
|
||||
searchResult.categories,
|
||||
[{ category:"Argentina", value:28015640 }]
|
||||
);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -218,114 +218,6 @@ describe('widgets-regressions', function() {
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should not count the polygons outside the bounding box', function(done) {
|
||||
|
||||
// $ % $ = not intersecting left triangle
|
||||
// $$ **VVVVV** %% % = not intersecting right triangle
|
||||
// $$$ *VVVVV* %%% * = intersecting triangle
|
||||
// $$$$ ***** %%%% V = bounding box
|
||||
// $$$$$ *** %%%%%
|
||||
// $$$$$$ * %%%%%%
|
||||
// $$$$$$$ %%%%%%%
|
||||
// $$$$$$$$ %%%%%%%%
|
||||
|
||||
const notIntersectingLeftTriangle = {
|
||||
type: "Polygon",
|
||||
coordinates:[[
|
||||
[-161.015625,69.28725695167886],
|
||||
[-162.7734375,-7.710991655433217],
|
||||
[-40.78125,-8.059229627200192],
|
||||
[-161.015625,69.28725695167886]
|
||||
]]
|
||||
};
|
||||
|
||||
const notIntersectingRightTriangle = {
|
||||
type: "Polygon",
|
||||
coordinates: [[
|
||||
[-29.179687499999996,-7.01366792756663],
|
||||
[103.71093749999999,-6.664607562172573],
|
||||
[105.46875,69.16255790810501],
|
||||
[-29.179687499999996,-7.01366792756663]
|
||||
]]
|
||||
};
|
||||
|
||||
const intersectingTriangle = {
|
||||
type: "Polygon",
|
||||
coordinates:[[
|
||||
[-117.42187500000001,68.13885164925573],
|
||||
[-35.859375,20.96143961409684],
|
||||
[59.4140625,68.52823492039876],
|
||||
[-117.42187500000001,68.13885164925573]
|
||||
]]
|
||||
};
|
||||
|
||||
const query = `
|
||||
SELECT
|
||||
ST_TRANSFORM(ST_SETSRID(ST_GeomFromGeoJSON(
|
||||
'${JSON.stringify(notIntersectingLeftTriangle)}'
|
||||
), 4326), 3857) AS the_geom_webmercator, 1 AS cartodb_id, 'notIntersectingLeftTriangle' AS name
|
||||
UNION
|
||||
SELECT
|
||||
ST_TRANSFORM(ST_SETSRID(ST_GeomFromGeoJSON(
|
||||
'${JSON.stringify(notIntersectingRightTriangle)}'
|
||||
), 4326), 3857), 2, 'notIntersectingRightTriangle'
|
||||
UNION
|
||||
SELECT
|
||||
ST_TRANSFORM(ST_SETSRID(ST_GeomFromGeoJSON(
|
||||
'${JSON.stringify(intersectingTriangle)}'
|
||||
), 4326), 3857), 3, 'intersectingTriangle'
|
||||
`;
|
||||
|
||||
const mapConfig = {
|
||||
version: '1.5.0',
|
||||
layers: [
|
||||
{
|
||||
"type": "cartodb",
|
||||
"options": {
|
||||
"source": {
|
||||
"id": "a0"
|
||||
},
|
||||
"cartocss": "#points { marker-width: 10; marker-fill: red; }",
|
||||
"cartocss_version": "2.3.0"
|
||||
}
|
||||
}
|
||||
],
|
||||
dataviews: {
|
||||
val_formula: {
|
||||
source: {
|
||||
id: 'a0'
|
||||
},
|
||||
type: 'aggregation',
|
||||
options: {
|
||||
column: "name",
|
||||
aggregation: "count",
|
||||
}
|
||||
}
|
||||
},
|
||||
analyses: [
|
||||
{
|
||||
"id": "a0",
|
||||
"type": "source",
|
||||
"params": {
|
||||
"query": query
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
this.testClient = new TestClient(mapConfig, 1234);
|
||||
const params = {
|
||||
bbox: '-77.34374999999999,45.82879925192134,17.578125,55.97379820507658'
|
||||
};
|
||||
this.testClient.getDataview('val_formula', params, function(err, dataview) {
|
||||
assert.ifError(err);
|
||||
assert.equal(dataview.categories.length, 1);
|
||||
assert.equal(dataview.categories[0].category, 'intersectingTriangle');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.3 KiB |
@@ -1 +0,0 @@
|
||||
{"type":"FeatureCollection","features":[{"type":"Feature","geometry":{"type":"Point","coordinates":[-53839,4629161]},"properties":{"name":"Alicante","cartodb_id":1200}},{"type":"Feature","geometry":{"type":"Point","coordinates":[242835,5069332]},"properties":{"name":"Barcelona","cartodb_id":5330}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-5567,4861644]},"properties":{"name":"Castello","cartodb_id":1201}},{"type":"Feature","geometry":{"type":"Point","coordinates":[272735,5092314]},"properties":{"name":"Mataro","cartodb_id":615}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-125787,4576600]},"properties":{"name":"Murcia","cartodb_id":952}},{"type":"Feature","geometry":{"type":"Point","coordinates":[295469,4804267]},"properties":{"name":"Palma","cartodb_id":5500}},{"type":"Feature","geometry":{"type":"Point","coordinates":[139148,5030112]},"properties":{"name":"Tarragona","cartodb_id":616}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-44746,4791667]},"properties":{"name":"Valencia","cartodb_id":5942}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-99072,5108695]},"properties":{"name":"Zaragoza","cartodb_id":5932}}]}
|
||||
@@ -1 +0,0 @@
|
||||
{"grid":[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," !! ","!!! !!!!! ","!!!!!!! ! ","!!! !!!!! "," !! ! "," "," "," "," "," "," "," "," ### # "," ####### ###"," ####### ## ","$ ## #### ## ","$$ ","$$ ","$$ "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "],"keys":["","9","2","1"],"data":{"1":{"cartodb_id":5942},"2":{"cartodb_id":5500},"9":{"cartodb_id":1201}}}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 2.4 KiB |
@@ -1 +0,0 @@
|
||||
{"grid":[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," !!! ! "," !!!!!!! !!!"," !!!!!!! !! "," !! !!!! !! "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "],"keys":["","1"],"data":{"1":{"cartodb_id":5500}}}
|
||||
@@ -1 +0,0 @@
|
||||
{"type":"FeatureCollection","features":[{"type":"Feature","geometry":{"type":"Point","coordinates":[295469,4804267]},"properties":{"name":"Palma","cartodb_id":5500}}]}
|
||||
Binary file not shown.
Binary file not shown.
@@ -126,25 +126,22 @@ assert.response = function(server, req, res, callback) {
|
||||
// Assert response body
|
||||
if (res.body) {
|
||||
var eql = res.body instanceof RegExp ? res.body.test(response.body) : res.body === response.body;
|
||||
if (!eql) {
|
||||
return callback(response, new Error(colorize(
|
||||
'[red]{Invalid response body.}\n' +
|
||||
assert.ok(
|
||||
eql,
|
||||
colorize('[red]{Invalid response body.}\n' +
|
||||
' Expected: [green]{' + res.body + '}\n' +
|
||||
' Got: [red]{' + response.body + '}'))
|
||||
);
|
||||
}
|
||||
' Got: [red]{' + response.body + '}')
|
||||
);
|
||||
}
|
||||
|
||||
// Assert response status
|
||||
if (typeof status === 'number') {
|
||||
if (response.statusCode != status) {
|
||||
return callback(response, new Error(colorize(
|
||||
'[red]{Invalid response status code.}\n' +
|
||||
assert.equal(response.statusCode, status,
|
||||
colorize('[red]{Invalid response status code.}\n' +
|
||||
' Expected: [green]{' + status + '}\n' +
|
||||
' Got: [red]{' + response.statusCode + '}\n' +
|
||||
' Body: ' + response.body))
|
||||
);
|
||||
}
|
||||
' Body: ' + response.body)
|
||||
);
|
||||
}
|
||||
|
||||
// Assert response headers
|
||||
@@ -155,13 +152,11 @@ assert.response = function(server, req, res, callback) {
|
||||
actual = response.headers[name.toLowerCase()],
|
||||
expected = res.headers[name],
|
||||
headerEql = expected instanceof RegExp ? expected.test(actual) : expected === actual;
|
||||
if (!headerEql) {
|
||||
return callback(response, new Error(colorize(
|
||||
'Invalid response header [bold]{' + name + '}.\n' +
|
||||
assert.ok(headerEql,
|
||||
colorize('Invalid response header [bold]{' + name + '}.\n' +
|
||||
' Expected: [green]{' + expected + '}\n' +
|
||||
' Got: [red]{' + actual + '}'))
|
||||
);
|
||||
}
|
||||
' Got: [red]{' + actual + '}')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@ if test x"$PREPARE_PGSQL" = xyes; then
|
||||
createdb -Ttemplate_postgis -EUTF8 "${TEST_DB}" || die "Could not create test database"
|
||||
|
||||
LOCAL_SQL_SCRIPTS='analysis_catalog windshaft.test gadm4 ported/populated_places_simple_reduced cdb_analysis_check cdb_invalidate_varnish'
|
||||
REMOTE_SQL_SCRIPTS='CDB_QueryStatements CDB_QueryTables CDB_CartodbfyTable CDB_TableMetadata CDB_ForeignTable CDB_UserTables CDB_ColumnNames CDB_ZoomFromScale CDB_OverviewsSupport CDB_Overviews CDB_QuantileBins CDB_JenksBins CDB_HeadsTailsBins CDB_EqualIntervalBins CDB_Hexagon CDB_XYZ CDB_EstimateRowCount'
|
||||
REMOTE_SQL_SCRIPTS='CDB_QueryStatements CDB_QueryTables CDB_CartodbfyTable CDB_TableMetadata CDB_ForeignTable CDB_UserTables CDB_ColumnNames CDB_ZoomFromScale CDB_OverviewsSupport CDB_Overviews CDB_QuantileBins CDB_JenksBins CDB_HeadsTailsBins CDB_EqualIntervalBins CDB_Hexagon CDB_XYZ'
|
||||
|
||||
CURL_ARGS=""
|
||||
for i in ${REMOTE_SQL_SCRIPTS}
|
||||
|
||||
@@ -339,78 +339,6 @@ INSERT INTO _vovw_2_test_table_overviews VALUES
|
||||
INSERT INTO _vovw_1_test_table_overviews VALUES
|
||||
('2011-09-21 14:02:21.358706', '2011-09-21 14:02:21.314252', 1, 'Hawai', 'Calle de Pérez Galdós 9, Madrid, Spain', 3.0, '0101000020E610000000000000000020C00000000000004440', '0101000020110F000076491621312319C122D4663F1DCC5241', 5);
|
||||
|
||||
-- table with overviews whit special float values
|
||||
|
||||
CREATE TABLE test_special_float_values_table_overviews (
|
||||
cartodb_id integer NOT NULL,
|
||||
name character varying,
|
||||
address character varying,
|
||||
value float8,
|
||||
the_geom geometry,
|
||||
the_geom_webmercator geometry,
|
||||
_feature_count integer,
|
||||
CONSTRAINT enforce_dims_the_geom CHECK ((st_ndims(the_geom) = 2)),
|
||||
CONSTRAINT enforce_dims_the_geom_webmercator CHECK ((st_ndims(the_geom_webmercator) = 2)),
|
||||
CONSTRAINT enforce_geotype_the_geom CHECK (((geometrytype(the_geom) = 'POINT'::text) OR (the_geom IS NULL))),
|
||||
CONSTRAINT enforce_geotype_the_geom_webmercator CHECK (((geometrytype(the_geom_webmercator) = 'POINT'::text) OR (the_geom_webmercator IS NULL))),
|
||||
CONSTRAINT enforce_srid_the_geom CHECK ((st_srid(the_geom) = 4326)),
|
||||
CONSTRAINT enforce_srid_the_geom_webmercator CHECK ((st_srid(the_geom_webmercator) = 3857))
|
||||
);
|
||||
|
||||
GRANT ALL ON TABLE test_special_float_values_table_overviews TO :TESTUSER;
|
||||
GRANT SELECT ON TABLE test_special_float_values_table_overviews TO :PUBLICUSER;
|
||||
|
||||
CREATE SEQUENCE test_special_float_values_table_overviews_cartodb_id_seq
|
||||
START WITH 1
|
||||
INCREMENT BY 1
|
||||
NO MINVALUE
|
||||
NO MAXVALUE
|
||||
CACHE 1;
|
||||
|
||||
ALTER SEQUENCE test_special_float_values_table_overviews_cartodb_id_seq OWNED BY test_special_float_values_table_overviews.cartodb_id;
|
||||
|
||||
SELECT pg_catalog.setval('test_special_float_values_table_overviews_cartodb_id_seq', 60, true);
|
||||
|
||||
ALTER TABLE test_special_float_values_table_overviews ALTER COLUMN cartodb_id SET DEFAULT nextval('test_special_float_values_table_overviews_cartodb_id_seq'::regclass);
|
||||
|
||||
INSERT INTO test_special_float_values_table_overviews VALUES
|
||||
(1, 'Hawai', 'Calle de Pérez Galdós 9, Madrid, Spain', 1.0, '0101000020E6100000A6B73F170D990DC064E8D84125364440', '0101000020110F000076491621312319C122D4663F1DCC5241', 1),
|
||||
(2, 'El Estocolmo', 'Calle de la Palma 72, Madrid, Spain', 2.0, '0101000020E6100000C90567F0F7AB0DC0AB07CC43A6364440', '0101000020110F0000C4356B29423319C15DD1092DADCC5241', 1),
|
||||
(3, 'El Rey del Tallarín', 'Plaza Conde de Toreno 2, Madrid, Spain', 'NaN'::float, '0101000020E610000021C8410933AD0DC0CB0EF10F5B364440', '0101000020110F000053E71AC64D3419C10F664E4659CC5241', 1),
|
||||
(4, 'El Lacón', 'Manuel Fernández y González 8, Madrid, Spain', 4.0, '0101000020E6100000BC5983F755990DC07D923B6C22354440', '0101000020110F00005DACDB056F2319C1EC41A980FCCA5241', 1),
|
||||
(5, 'El Pico', 'Calle Divino Pastor 12, Madrid, Spain', 'infinity'::float, '0101000020E61000003B6D8D08C6A10DC0371B2B31CF364440', '0101000020110F00005F716E91992A19C17DAAA4D6DACC5241', 1);
|
||||
|
||||
ALTER TABLE ONLY test_special_float_values_table_overviews ADD CONSTRAINT test_special_float_values_table_overviews_pkey PRIMARY KEY (cartodb_id);
|
||||
|
||||
CREATE INDEX test_special_float_values_table_overviews_the_geom_idx ON test_special_float_values_table_overviews USING gist (the_geom);
|
||||
CREATE INDEX test_special_float_values_table_overviews_the_geom_webmercator_idx ON test_special_float_values_table_overviews USING gist (the_geom_webmercator);
|
||||
|
||||
GRANT ALL ON TABLE test_special_float_values_table_overviews TO :TESTUSER;
|
||||
GRANT SELECT ON TABLE test_special_float_values_table_overviews TO :PUBLICUSER;
|
||||
|
||||
CREATE TABLE _vovw_1_test_special_float_values_table_overviews (
|
||||
cartodb_id integer NOT NULL,
|
||||
name character varying,
|
||||
address character varying,
|
||||
value float8,
|
||||
the_geom geometry,
|
||||
the_geom_webmercator geometry,
|
||||
_feature_count integer,
|
||||
CONSTRAINT enforce_dims_the_geom CHECK ((st_ndims(the_geom) = 2)),
|
||||
CONSTRAINT enforce_dims_the_geom_webmercator CHECK ((st_ndims(the_geom_webmercator) = 2)),
|
||||
CONSTRAINT enforce_geotype_the_geom CHECK (((geometrytype(the_geom) = 'POINT'::text) OR (the_geom IS NULL))),
|
||||
CONSTRAINT enforce_geotype_the_geom_webmercator CHECK (((geometrytype(the_geom_webmercator) = 'POINT'::text) OR (the_geom_webmercator IS NULL))),
|
||||
CONSTRAINT enforce_srid_the_geom CHECK ((st_srid(the_geom) = 4326)),
|
||||
CONSTRAINT enforce_srid_the_geom_webmercator CHECK ((st_srid(the_geom_webmercator) = 3857))
|
||||
);
|
||||
|
||||
GRANT ALL ON TABLE _vovw_1_test_special_float_values_table_overviews TO :TESTUSER;
|
||||
GRANT SELECT ON TABLE _vovw_1_test_special_float_values_table_overviews TO :PUBLICUSER;
|
||||
|
||||
INSERT INTO _vovw_1_test_special_float_values_table_overviews VALUES
|
||||
(1, 'Hawai', 'Calle de Pérez Galdós 9, Madrid, Spain', 3, '0101000020E610000000000000000020C00000000000004440', '0101000020110F000076491621312319C122D4663F1DCC5241', 2),
|
||||
(3, 'El Rey del Tallarín', 'Plaza Conde de Toreno 2, Madrid, Spain', 'NaN'::float, '0101000020E610000021C8410933AD0DC0CB0EF10F5B364440', '0101000020110F000053E71AC64D3419C10F664E4659CC5241', 1),
|
||||
(4, 'El Lacón', 'Manuel Fernández y González 8, Madrid, Spain', 'infinity'::float, '0101000020E6100000BC5983F755990DC07D923B6C22354440', '0101000020110F00005DACDB056F2319C1EC41A980FCCA5241', 2);
|
||||
|
||||
-- analysis tables -----------------------------------------------
|
||||
|
||||
@@ -721,5 +649,3 @@ CREATE OR REPLACE FUNCTION cdb_crankshaft.CDB_KMeans(query text, no_clusters int
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
GRANT ALL ON FUNCTION cdb_crankshaft.CDB_KMeans(text, integer, integer) TO :TESTUSER;
|
||||
|
||||
ANALYZE;
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
var qs = require('querystring');
|
||||
var step = require('step');
|
||||
var urlParser = require('url');
|
||||
var PSQL = require('cartodb-psql');
|
||||
var _ = require('underscore');
|
||||
|
||||
var mapnik = require('windshaft').mapnik;
|
||||
|
||||
var LayergroupToken = require('./layergroup-token');
|
||||
@@ -15,33 +14,16 @@ var helper = require('./test_helper');
|
||||
var CartodbWindshaft = require('../../lib/cartodb/server');
|
||||
var serverOptions = require('../../lib/cartodb/server_options');
|
||||
serverOptions.analysis.batch.inlineExecution = true;
|
||||
var server = new CartodbWindshaft(serverOptions);
|
||||
|
||||
const MAPNIK_SUPPORTED_FORMATS = {
|
||||
'png': true,
|
||||
'png32': true,
|
||||
'grid.json': true,
|
||||
'geojson': true,
|
||||
'mvt': true
|
||||
}
|
||||
|
||||
function TestClient(config, apiKey) {
|
||||
this.mapConfig = isMapConfig(config) ? config : null;
|
||||
this.template = isTemplate(config) ? config : null;
|
||||
function TestClient(mapConfig, apiKey) {
|
||||
this.mapConfig = mapConfig;
|
||||
this.apiKey = apiKey;
|
||||
this.keysToDelete = {};
|
||||
this.server = new CartodbWindshaft(serverOptions);
|
||||
}
|
||||
|
||||
module.exports = TestClient;
|
||||
|
||||
function isMapConfig(config) {
|
||||
return config && config.layers;
|
||||
}
|
||||
|
||||
function isTemplate(config) {
|
||||
return config && config.layergroup;
|
||||
}
|
||||
|
||||
module.exports.RESPONSE = {
|
||||
ERROR: {
|
||||
status: 400,
|
||||
@@ -81,42 +63,9 @@ module.exports.CARTOCSS = {
|
||||
' line-width: 0.5;',
|
||||
' line-opacity: 1;',
|
||||
'}'
|
||||
].join('\n'),
|
||||
|
||||
TORQUE: [
|
||||
'Map {',
|
||||
' -torque-frame-count: 256;',
|
||||
' -torque-animation-duration: 30;',
|
||||
' -torque-time-attribute: "cartodb_id";',
|
||||
' -torque-aggregation-function: "count(1)";',
|
||||
' -torque-resolution: 4;',
|
||||
' -torque-data-aggregation: linear;',
|
||||
'}',
|
||||
'#layer {',
|
||||
' marker-width: 7;',
|
||||
' marker-fill: #FFB927;',
|
||||
' marker-fill-opacity: 0.9;',
|
||||
' marker-line-width: 1;',
|
||||
' marker-line-color: #FFF;',
|
||||
' marker-line-opacity: 1;',
|
||||
' comp-op: lighter;',
|
||||
'}',
|
||||
'#layer[frame-offset=1] {',
|
||||
' marker-width: 9;',
|
||||
' marker-fill-opacity: 0.45;',
|
||||
'}',
|
||||
'#layer[frame-offset=2] {',
|
||||
' marker-width: 11;',
|
||||
' marker-fill-opacity: 0.225;',
|
||||
'}'
|
||||
].join('\n')
|
||||
};
|
||||
|
||||
module.exports.SQL = {
|
||||
EMPTY: 'select 1 as cartodb_id, null::geometry as the_geom_webmercator',
|
||||
ONE_POINT: 'select 1 as cartodb_id, \'SRID=3857;POINT(0 0)\'::geometry the_geom_webmercator'
|
||||
}
|
||||
|
||||
TestClient.prototype.getWidget = function(widgetName, params, callback) {
|
||||
var self = this;
|
||||
|
||||
@@ -134,7 +83,7 @@ TestClient.prototype.getWidget = function(widgetName, params, callback) {
|
||||
step(
|
||||
function createLayergroup() {
|
||||
var next = this;
|
||||
assert.response(self.server,
|
||||
assert.response(server,
|
||||
{
|
||||
url: url,
|
||||
method: 'POST',
|
||||
@@ -193,7 +142,7 @@ TestClient.prototype.getWidget = function(widgetName, params, callback) {
|
||||
|
||||
url = '/api/v1/map/' + layergroupId + '/0/widget/' + widgetName + '?' + qs.stringify(urlParams);
|
||||
|
||||
assert.response(self.server,
|
||||
assert.response(server,
|
||||
{
|
||||
url: url,
|
||||
method: 'GET',
|
||||
@@ -245,7 +194,7 @@ TestClient.prototype.widgetSearch = function(widgetName, userQuery, params, call
|
||||
step(
|
||||
function createLayergroup() {
|
||||
var next = this;
|
||||
assert.response(self.server,
|
||||
assert.response(server,
|
||||
{
|
||||
url: url,
|
||||
method: 'POST',
|
||||
@@ -302,7 +251,7 @@ TestClient.prototype.widgetSearch = function(widgetName, userQuery, params, call
|
||||
}
|
||||
url = '/api/v1/map/' + layergroupId + '/0/widget/' + widgetName + '/search?' + qs.stringify(urlParams);
|
||||
|
||||
assert.response(self.server,
|
||||
assert.response(server,
|
||||
{
|
||||
url: url,
|
||||
method: 'GET',
|
||||
@@ -369,7 +318,7 @@ TestClient.prototype.getDataview = function(dataviewName, params, callback) {
|
||||
step(
|
||||
function createLayergroup() {
|
||||
var next = this;
|
||||
assert.response(self.server,
|
||||
assert.response(server,
|
||||
{
|
||||
url: url,
|
||||
method: 'POST',
|
||||
@@ -411,7 +360,7 @@ TestClient.prototype.getDataview = function(dataviewName, params, callback) {
|
||||
own_filter: params.hasOwnProperty('own_filter') ? params.own_filter : 1
|
||||
};
|
||||
|
||||
['bbox', 'bins', 'start', 'end', 'aggregation', 'offset'].forEach(function(extraParam) {
|
||||
['bbox', 'bins', 'start', 'end'].forEach(function(extraParam) {
|
||||
if (params.hasOwnProperty(extraParam)) {
|
||||
urlParams[extraParam] = params[extraParam];
|
||||
}
|
||||
@@ -422,7 +371,7 @@ TestClient.prototype.getDataview = function(dataviewName, params, callback) {
|
||||
}
|
||||
url = '/api/v1/map/' + layergroupId + '/dataview/' + dataviewName + '?' + qs.stringify(urlParams);
|
||||
|
||||
assert.response(self.server,
|
||||
assert.response(server,
|
||||
{
|
||||
url: url,
|
||||
method: 'GET',
|
||||
@@ -441,115 +390,9 @@ TestClient.prototype.getDataview = function(dataviewName, params, callback) {
|
||||
);
|
||||
},
|
||||
function finish(err, dataview) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
if (layergroupId) {
|
||||
self.keysToDelete['map_cfg|' + LayergroupToken.parse(layergroupId).token] = 0;
|
||||
self.keysToDelete['user:localhost:mapviews:global'] = 5;
|
||||
}
|
||||
|
||||
return callback(null, dataview);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
TestClient.prototype.getFeatureAttributes = function(featureId, layerId, params, callback) {
|
||||
var self = this;
|
||||
|
||||
if (!callback) {
|
||||
callback = params;
|
||||
params = {};
|
||||
}
|
||||
|
||||
var extraParams = {};
|
||||
if (this.apiKey) {
|
||||
extraParams.api_key = this.apiKey;
|
||||
}
|
||||
if (params && params.filters) {
|
||||
extraParams.filters = JSON.stringify(params.filters);
|
||||
}
|
||||
|
||||
var url = '/api/v1/map';
|
||||
if (Object.keys(extraParams).length > 0) {
|
||||
url += '?' + qs.stringify(extraParams);
|
||||
}
|
||||
|
||||
var expectedResponse = params.response || {
|
||||
status: 200,
|
||||
headers: {
|
||||
'Content-Type': 'application/json; charset=utf-8'
|
||||
}
|
||||
};
|
||||
|
||||
var layergroupId;
|
||||
step(
|
||||
function createLayergroup() {
|
||||
var next = this;
|
||||
assert.response(self.server,
|
||||
{
|
||||
url: url,
|
||||
method: 'POST',
|
||||
headers: {
|
||||
host: 'localhost',
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
data: JSON.stringify(self.mapConfig)
|
||||
},
|
||||
{
|
||||
status: 200,
|
||||
headers: {
|
||||
'Content-Type': 'application/json; charset=utf-8'
|
||||
}
|
||||
},
|
||||
function(res, err) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
var parsedBody = JSON.parse(res.body);
|
||||
|
||||
if (parsedBody.layergroupid) {
|
||||
self.keysToDelete['map_cfg|' + LayergroupToken.parse(parsedBody.layergroupid).token] = 0;
|
||||
self.keysToDelete['user:localhost:mapviews:global'] = 5;
|
||||
}
|
||||
|
||||
return next(null, parsedBody.layergroupid);
|
||||
}
|
||||
);
|
||||
},
|
||||
function getFeatureAttributes(err, layergroupId) {
|
||||
assert.ifError(err);
|
||||
|
||||
var next = this;
|
||||
|
||||
url = '/api/v1/map/' + layergroupId + '/' + layerId + '/attributes/' + featureId;
|
||||
|
||||
assert.response(self.server,
|
||||
{
|
||||
url: url,
|
||||
method: 'GET',
|
||||
headers: {
|
||||
host: 'localhost'
|
||||
}
|
||||
},
|
||||
expectedResponse,
|
||||
function(res, err) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
next(null, JSON.parse(res.body));
|
||||
}
|
||||
);
|
||||
},
|
||||
function finish(err, attributes) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
return callback(null, attributes);
|
||||
self.keysToDelete['map_cfg|' + LayergroupToken.parse(layergroupId).token] = 0;
|
||||
self.keysToDelete['user:localhost:mapviews:global'] = 5;
|
||||
return callback(err, dataview);
|
||||
}
|
||||
);
|
||||
};
|
||||
@@ -563,77 +406,24 @@ TestClient.prototype.getTile = function(z, x, y, params, callback) {
|
||||
}
|
||||
|
||||
var url = '/api/v1/map';
|
||||
var urlNamed = url + '/named';
|
||||
|
||||
if (this.apiKey) {
|
||||
url += '?' + qs.stringify({api_key: this.apiKey});
|
||||
}
|
||||
|
||||
var layergroupId;
|
||||
|
||||
if (params.layergroupid) {
|
||||
layergroupId = params.layergroupid
|
||||
}
|
||||
|
||||
step(
|
||||
function createTemplate () {
|
||||
function createLayergroup() {
|
||||
var next = this;
|
||||
|
||||
if (!self.template) {
|
||||
return next();
|
||||
}
|
||||
|
||||
if (!self.apiKey) {
|
||||
return next(new Error('apiKey param is mandatory to create a new template'));
|
||||
}
|
||||
|
||||
params.placeholders = params.placeholders || {};
|
||||
|
||||
assert.response(self.server,
|
||||
assert.response(server,
|
||||
{
|
||||
url: urlNamed + '?' + qs.stringify({ api_key: self.apiKey }),
|
||||
url: url,
|
||||
method: 'POST',
|
||||
headers: {
|
||||
host: 'localhost',
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
data: JSON.stringify(self.template)
|
||||
},
|
||||
{
|
||||
status: 200,
|
||||
headers: {
|
||||
'Content-Type': 'application/json; charset=utf-8'
|
||||
}
|
||||
},
|
||||
function (res, err) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
return next(null, JSON.parse(res.body).template_id);
|
||||
}
|
||||
);
|
||||
},
|
||||
function createLayergroup(err, templateId) {
|
||||
var next = this;
|
||||
|
||||
if (layergroupId) {
|
||||
return next(null, layergroupId);
|
||||
}
|
||||
|
||||
var data = templateId ? params.placeholders : self.mapConfig
|
||||
var path = templateId ?
|
||||
urlNamed + '/' + templateId + '?' + qs.stringify({api_key: self.apiKey}) :
|
||||
url;
|
||||
|
||||
assert.response(self.server,
|
||||
{
|
||||
url: path,
|
||||
method: 'POST',
|
||||
headers: {
|
||||
host: 'localhost',
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
data: JSON.stringify(data)
|
||||
data: JSON.stringify(self.mapConfig)
|
||||
},
|
||||
{
|
||||
status: 200,
|
||||
@@ -666,10 +456,6 @@ TestClient.prototype.getTile = function(z, x, y, params, callback) {
|
||||
|
||||
var format = params.format || 'png';
|
||||
|
||||
if (layers === undefined && !MAPNIK_SUPPORTED_FORMATS[format]) {
|
||||
throw new Error(`Missing layer filter while fetching ${format} tile, review params argument`);
|
||||
}
|
||||
|
||||
url += [z,x,y].join('/');
|
||||
url += '.' + format;
|
||||
|
||||
@@ -685,76 +471,37 @@ TestClient.prototype.getTile = function(z, x, y, params, callback) {
|
||||
}
|
||||
};
|
||||
|
||||
var expectedResponse = Object.assign({}, {
|
||||
var expectedResponse = {
|
||||
status: 200,
|
||||
headers: {
|
||||
'Content-Type': 'image/png'
|
||||
'Content-Type': 'application/json; charset=utf-8'
|
||||
}
|
||||
}, params.response);
|
||||
|
||||
};
|
||||
|
||||
var isPng = format.match(/png$/);
|
||||
|
||||
if (isPng) {
|
||||
request.encoding = 'binary';
|
||||
expectedResponse.headers['Content-Type'] = 'image/png';
|
||||
}
|
||||
|
||||
var isMvt = format.match(/mvt$/);
|
||||
|
||||
if (isMvt) {
|
||||
request.encoding = 'binary';
|
||||
if (expectedResponse.status === 200) {
|
||||
expectedResponse.headers['Content-Type'] = 'application/x-protobuf';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var isGeojson = format.match(/geojson$/);
|
||||
|
||||
if (isGeojson) {
|
||||
request.encoding = 'utf-8';
|
||||
expectedResponse.headers['Content-Type'] = 'application/json; charset=utf-8';
|
||||
}
|
||||
|
||||
var isGridJSON = format.match(/grid.json$/);
|
||||
|
||||
if (isGridJSON) {
|
||||
request.encoding = 'utf-8';
|
||||
expectedResponse.headers['Content-Type'] = 'application/json; charset=utf-8';
|
||||
}
|
||||
|
||||
if (params.contentType) {
|
||||
expectedResponse.headers['Content-Type'] = 'application/json; charset=utf-8';
|
||||
}
|
||||
|
||||
assert.response(self.server, request, expectedResponse, function(res, err) {
|
||||
assert.response(server, request, expectedResponse, function(res, err) {
|
||||
assert.ifError(err);
|
||||
|
||||
var body;
|
||||
switch (res.headers['content-type']) {
|
||||
case 'image/png':
|
||||
body = mapnik.Image.fromBytes(new Buffer(res.body, 'binary'));
|
||||
break;
|
||||
case 'application/x-protobuf':
|
||||
body = new mapnik.VectorTile(z, x, y);
|
||||
body.setDataSync(new Buffer(res.body, 'binary'));
|
||||
break;
|
||||
case 'application/json; charset=utf-8':
|
||||
body = JSON.parse(res.body);
|
||||
break;
|
||||
default:
|
||||
body = res.body
|
||||
break;
|
||||
var obj;
|
||||
|
||||
if (isPng) {
|
||||
obj = mapnik.Image.fromBytes(new Buffer(res.body, 'binary'));
|
||||
} else {
|
||||
obj = JSON.parse(res.body);
|
||||
}
|
||||
|
||||
next(null, res, body);
|
||||
next(null, res, obj);
|
||||
});
|
||||
},
|
||||
function finish(err, res, image) {
|
||||
if (layergroupId) {
|
||||
self.keysToDelete['map_cfg|' + LayergroupToken.parse(layergroupId).token] = 0;
|
||||
self.keysToDelete['user:localhost:mapviews:global'] = 5;
|
||||
}
|
||||
self.keysToDelete['map_cfg|' + LayergroupToken.parse(layergroupId).token] = 0;
|
||||
self.keysToDelete['user:localhost:mapviews:global'] = 5;
|
||||
return callback(err, res, image);
|
||||
}
|
||||
);
|
||||
@@ -779,7 +526,7 @@ TestClient.prototype.getLayergroup = function(expectedResponse, callback) {
|
||||
url += '?' + qs.stringify({api_key: this.apiKey});
|
||||
}
|
||||
|
||||
assert.response(self.server,
|
||||
assert.response(server,
|
||||
{
|
||||
url: url,
|
||||
method: 'POST',
|
||||
@@ -791,124 +538,18 @@ TestClient.prototype.getLayergroup = function(expectedResponse, callback) {
|
||||
},
|
||||
expectedResponse,
|
||||
function(res, err) {
|
||||
// If there is a response, we are still interested in catching the created keys
|
||||
// to be able to delete them on the .drain() method.
|
||||
if (res) {
|
||||
var parsedBody = JSON.parse(res.body);
|
||||
if (parsedBody.layergroupid) {
|
||||
self.keysToDelete['map_cfg|' + LayergroupToken.parse(parsedBody.layergroupid).token] = 0;
|
||||
self.keysToDelete['user:localhost:mapviews:global'] = 5;
|
||||
}
|
||||
}
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
return callback(null, parsedBody);
|
||||
}
|
||||
);
|
||||
};
|
||||
var parsedBody = JSON.parse(res.body);
|
||||
|
||||
TestClient.prototype.getStaticCenter = function (params, callback) {
|
||||
var self = this;
|
||||
|
||||
let { layergroupid, z, lat, lng, width, height, format } = params
|
||||
|
||||
var url = `/api/v1/map/`;
|
||||
|
||||
if (this.apiKey) {
|
||||
url += '?' + qs.stringify({api_key: this.apiKey});
|
||||
}
|
||||
|
||||
step(
|
||||
function createLayergroup() {
|
||||
var next = this;
|
||||
|
||||
if (layergroupid) {
|
||||
return next(null, layergroupid);
|
||||
}
|
||||
|
||||
var data = self.mapConfig
|
||||
var path = url;
|
||||
|
||||
assert.response(self.server,
|
||||
{
|
||||
url: path,
|
||||
method: 'POST',
|
||||
headers: {
|
||||
host: 'localhost',
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
data: JSON.stringify(data)
|
||||
},
|
||||
{
|
||||
status: 200,
|
||||
headers: {
|
||||
'Content-Type': 'application/json; charset=utf-8'
|
||||
}
|
||||
},
|
||||
function(res, err) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
return next(null, JSON.parse(res.body).layergroupid);
|
||||
}
|
||||
);
|
||||
},
|
||||
function getStaticResult(err, _layergroupid) {
|
||||
assert.ifError(err);
|
||||
|
||||
var next = this;
|
||||
|
||||
layergroupid = _layergroupid;
|
||||
|
||||
url = `/api/v1/map/static/center/${layergroupid}/${z}/${lat}/${lng}/${width}/${height}.${format}`
|
||||
|
||||
if (self.apiKey) {
|
||||
url += '?' + qs.stringify({api_key: self.apiKey});
|
||||
}
|
||||
|
||||
var request = {
|
||||
url: url,
|
||||
encoding: 'binary',
|
||||
method: 'GET',
|
||||
headers: {
|
||||
host: 'localhost'
|
||||
}
|
||||
};
|
||||
|
||||
var expectedResponse = Object.assign({}, {
|
||||
status: 200,
|
||||
headers: {
|
||||
'Content-Type': 'image/png'
|
||||
}
|
||||
}, params.response);
|
||||
|
||||
assert.response(self.server, request, expectedResponse, function(res, err) {
|
||||
assert.ifError(err);
|
||||
|
||||
var body;
|
||||
switch (res.headers['content-type']) {
|
||||
case 'image/png':
|
||||
body = mapnik.Image.fromBytes(new Buffer(res.body, 'binary'));
|
||||
break;
|
||||
case 'application/json; charset=utf-8':
|
||||
body = JSON.parse(res.body);
|
||||
break;
|
||||
default:
|
||||
body = res.body
|
||||
break;
|
||||
}
|
||||
|
||||
next(null, res, body);
|
||||
});
|
||||
},
|
||||
function finish(err, res, image) {
|
||||
if (layergroupid) {
|
||||
self.keysToDelete['map_cfg|' + LayergroupToken.parse(layergroupid).token] = 0;
|
||||
if (parsedBody.layergroupid) {
|
||||
self.keysToDelete['map_cfg|' + LayergroupToken.parse(parsedBody.layergroupid).token] = 0;
|
||||
self.keysToDelete['user:localhost:mapviews:global'] = 5;
|
||||
}
|
||||
return callback(err, res, image);
|
||||
|
||||
return callback(null, parsedBody);
|
||||
}
|
||||
);
|
||||
};
|
||||
@@ -927,7 +568,7 @@ TestClient.prototype.getNodeStatus = function(nodeName, callback) {
|
||||
step(
|
||||
function createLayergroup() {
|
||||
var next = this;
|
||||
assert.response(self.server,
|
||||
assert.response(server,
|
||||
{
|
||||
url: url,
|
||||
method: 'POST',
|
||||
@@ -988,7 +629,7 @@ TestClient.prototype.getNodeStatus = function(nodeName, callback) {
|
||||
}
|
||||
};
|
||||
|
||||
assert.response(self.server, request, expectedResponse, function(res, err) {
|
||||
assert.response(server, request, expectedResponse, function(res, err) {
|
||||
assert.ifError(err);
|
||||
next(null, res, JSON.parse(res.body));
|
||||
});
|
||||
@@ -1001,119 +642,11 @@ TestClient.prototype.getNodeStatus = function(nodeName, callback) {
|
||||
);
|
||||
};
|
||||
|
||||
TestClient.prototype.getAttributes = function(params, callback) {
|
||||
var self = this;
|
||||
|
||||
if (!Number.isFinite(params.featureId)) {
|
||||
throw new Error('featureId param must be a number')
|
||||
}
|
||||
|
||||
if (!Number.isFinite(params.layer)) {
|
||||
throw new Error('layer param must be a number')
|
||||
}
|
||||
|
||||
var url = '/api/v1/map';
|
||||
|
||||
if (this.apiKey) {
|
||||
url += '?' + qs.stringify({ api_key: this.apiKey });
|
||||
}
|
||||
|
||||
var layergroupid;
|
||||
|
||||
if (params.layergroupid) {
|
||||
layergroupid = params.layergroupid
|
||||
}
|
||||
|
||||
step(
|
||||
function createLayergroup() {
|
||||
var next = this;
|
||||
|
||||
if (layergroupid) {
|
||||
return next(null, layergroupid);
|
||||
}
|
||||
|
||||
assert.response(self.server,
|
||||
{
|
||||
url: url,
|
||||
method: 'POST',
|
||||
headers: {
|
||||
host: 'localhost',
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
data: JSON.stringify(self.mapConfig)
|
||||
},
|
||||
{
|
||||
status: 200,
|
||||
headers: {
|
||||
'Content-Type': 'application/json; charset=utf-8'
|
||||
}
|
||||
},
|
||||
function(res, err) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
var parsedBody = JSON.parse(res.body);
|
||||
|
||||
return next(null, parsedBody.layergroupid);
|
||||
}
|
||||
);
|
||||
},
|
||||
function getAttributes(err, _layergroupid) {
|
||||
assert.ifError(err);
|
||||
|
||||
var next = this;
|
||||
|
||||
layergroupid = _layergroupid;
|
||||
|
||||
url = `/api/v1/map/${layergroupid}/${params.layer}/attributes/${params.featureId}`;
|
||||
|
||||
if (self.apiKey) {
|
||||
url += '?' + qs.stringify({api_key: self.apiKey});
|
||||
}
|
||||
|
||||
var request = {
|
||||
url: url,
|
||||
method: 'GET',
|
||||
headers: {
|
||||
host: 'localhost'
|
||||
}
|
||||
};
|
||||
|
||||
var expectedResponse = params.response || {
|
||||
status: 200,
|
||||
headers: {
|
||||
'Content-Type': 'application/json; charset=utf-8'
|
||||
}
|
||||
};
|
||||
|
||||
assert.response(self.server, request, expectedResponse, function (res, err) {
|
||||
assert.ifError(err);
|
||||
|
||||
var attributes = JSON.parse(res.body);
|
||||
|
||||
next(null, res, attributes);
|
||||
});
|
||||
},
|
||||
function finish(err, res, attributes) {
|
||||
if (layergroupid) {
|
||||
self.keysToDelete['map_cfg|' + LayergroupToken.parse(layergroupid).token] = 0;
|
||||
self.keysToDelete['user:localhost:mapviews:global'] = 5;
|
||||
}
|
||||
|
||||
return callback(err, res, attributes);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
TestClient.prototype.drain = function(callback) {
|
||||
helper.deleteRedisKeys(this.keysToDelete, callback);
|
||||
};
|
||||
|
||||
module.exports.getStaticMap = function getStaticMap(templateName, params, callback) {
|
||||
var self = this;
|
||||
|
||||
self.server = new CartodbWindshaft(serverOptions);
|
||||
|
||||
if (!callback) {
|
||||
callback = params;
|
||||
params = null;
|
||||
@@ -1144,56 +677,9 @@ module.exports.getStaticMap = function getStaticMap(templateName, params, callba
|
||||
// this could be removed once named maps are invalidated, otherwise you hits the cache
|
||||
var server = new CartodbWindshaft(serverOptions);
|
||||
|
||||
assert.response(self.server, requestOptions, expectedResponse, function (res, err) {
|
||||
assert.response(server, requestOptions, expectedResponse, function (res, err) {
|
||||
helper.deleteRedisKeys({'user:localhost:mapviews:global': 5}, function() {
|
||||
return callback(err, mapnik.Image.fromBytes(new Buffer(res.body, 'binary')));
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
TestClient.prototype.setUserRenderTimeoutLimit = function (user, userTimeoutLimit, callback) {
|
||||
const userTimeoutLimitsKey = `limits:timeout:${user}`;
|
||||
const params = [
|
||||
userTimeoutLimitsKey,
|
||||
'render', userTimeoutLimit,
|
||||
'render_public', userTimeoutLimit
|
||||
];
|
||||
|
||||
this.keysToDelete[userTimeoutLimitsKey] = 5;
|
||||
|
||||
helper.configureMetadata('hmset', params, callback);
|
||||
};
|
||||
|
||||
TestClient.prototype.setUserDatabaseTimeoutLimit = function (timeoutLimit, callback) {
|
||||
const dbname = _.template(global.environment.postgres_auth_user, { user_id: 1 }) + '_db';
|
||||
const dbuser = _.template(global.environment.postgres_auth_user, { user_id: 1 })
|
||||
const pass = _.template(global.environment.postgres_auth_pass, { user_id: 1 })
|
||||
const publicuser = global.environment.postgres.user;
|
||||
|
||||
// we need to guarantee all new connections have the new settings
|
||||
helper.cleanPGPoolConnections()
|
||||
|
||||
const psql = new PSQL({
|
||||
user: 'postgres',
|
||||
dbname: dbname,
|
||||
host: global.environment.postgres.host,
|
||||
port: global.environment.postgres.port
|
||||
});
|
||||
|
||||
step(
|
||||
function configureTimeouts () {
|
||||
const timeoutSQLs = [
|
||||
`ALTER ROLE "${publicuser}" SET STATEMENT_TIMEOUT TO ${timeoutLimit}`,
|
||||
`ALTER ROLE "${dbuser}" SET STATEMENT_TIMEOUT TO ${timeoutLimit}`,
|
||||
`ALTER DATABASE "${dbname}" SET STATEMENT_TIMEOUT TO ${timeoutLimit}`
|
||||
];
|
||||
|
||||
const group = this.group();
|
||||
|
||||
timeoutSQLs.forEach(sql => psql.query(sql, group()));
|
||||
},
|
||||
callback
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@ var lzmaWorker = new LZMA();
|
||||
var redis = require('redis');
|
||||
var nock = require('nock');
|
||||
var log4js = require('log4js');
|
||||
var pg = require('pg');
|
||||
|
||||
// set environment specific variables
|
||||
global.environment = require(__dirname + '/../../config/environments/test');
|
||||
@@ -128,11 +127,6 @@ afterEach(function(done) {
|
||||
});
|
||||
});
|
||||
|
||||
function cleanPGPoolConnections () {
|
||||
// TODO: this method will be replaced by psql.end
|
||||
pg.end();
|
||||
}
|
||||
|
||||
function deleteRedisKeys(keysToDelete, callback) {
|
||||
|
||||
if (Object.keys(keysToDelete).length === 0) {
|
||||
@@ -172,30 +166,12 @@ function rmdirRecursiveSync(dirname) {
|
||||
}
|
||||
}
|
||||
|
||||
function configureMetadata(action, params, callback) {
|
||||
redisClient.SELECT(5, function (err) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
redisClient[action](params, function (err) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
return callback();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
deleteRedisKeys: deleteRedisKeys,
|
||||
lzma_compress_to_base64: lzma_compress_to_base64,
|
||||
checkNoCache: checkNoCache,
|
||||
checkSurrogateKey: checkSurrogateKey,
|
||||
checkCache: checkCache,
|
||||
rmdirRecursiveSync: rmdirRecursiveSync,
|
||||
configureMetadata,
|
||||
cleanPGPoolConnections
|
||||
rmdirRecursiveSync: rmdirRecursiveSync
|
||||
};
|
||||
|
||||
|
||||
@@ -1,153 +0,0 @@
|
||||
var assert = require('assert');
|
||||
var MapnikLayerStats = require('../../../../../lib/cartodb/backends/layer-stats/mapnik-layer-stats');
|
||||
var MapConfig = require('windshaft').model.MapConfig;
|
||||
|
||||
function getDbConnectionMock () {
|
||||
return {
|
||||
query: function(sql, callback) {
|
||||
return callback(null, {
|
||||
rows: [{rows: 1}]
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
describe('mapnik-layer-stats', function() {
|
||||
|
||||
beforeEach(function () {
|
||||
this.dbConnectionMock = getDbConnectionMock();
|
||||
this.rendererCacheMock = {};
|
||||
this.params = {};
|
||||
});
|
||||
|
||||
var testMapConfigOneLayer = {
|
||||
version: '1.5.0',
|
||||
layers: [
|
||||
{
|
||||
type: 'mapnik',
|
||||
options: {
|
||||
sql: 'select * from test_table limit 2',
|
||||
cartocss: '#layer { marker-fill:red; marker-width:32; marker-allow-overlap:true; }',
|
||||
cartocss_version: '2.3.0'
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
var testMapConfigTwoLayers = {
|
||||
version: '1.5.0',
|
||||
layers: [
|
||||
{
|
||||
type: 'mapnik',
|
||||
options: {
|
||||
sql: 'select * from test_table limit 2',
|
||||
cartocss: '#layer { marker-fill:red; marker-width:32; marker-allow-overlap:true; }',
|
||||
cartocss_version: '2.3.0'
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'mapnik',
|
||||
options: {
|
||||
sql: 'select * from test_table limit 2',
|
||||
cartocss: '#layer { marker-fill:red; marker-width:32; marker-allow-overlap:true; }',
|
||||
cartocss_version: '2.3.0'
|
||||
}
|
||||
},
|
||||
]
|
||||
};
|
||||
|
||||
var testMapConfigOneLayerTwoTables = {
|
||||
version: '1.5.0',
|
||||
layers: [
|
||||
{
|
||||
type: 'mapnik',
|
||||
options: {
|
||||
sql: 'select * from test_table limit 2',
|
||||
cartocss: '#layer { marker-fill:red; marker-width:32; marker-allow-overlap:true; }',
|
||||
cartocss_version: '2.3.0',
|
||||
affected_tables: ['test_table_1', 'test_table_2']
|
||||
}
|
||||
},
|
||||
]
|
||||
};
|
||||
|
||||
var testMapConfigTwoLayerTwoTables = {
|
||||
version: '1.5.0',
|
||||
layers: [
|
||||
{
|
||||
type: 'mapnik',
|
||||
options: {
|
||||
sql: 'select * from test_table limit 2',
|
||||
cartocss: '#layer { marker-fill:red; marker-width:32; marker-allow-overlap:true; }',
|
||||
cartocss_version: '2.3.0',
|
||||
affected_tables: ['test_table_1', 'test_table_2']
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'mapnik',
|
||||
options: {
|
||||
sql: 'select * from test_table limit 2',
|
||||
cartocss: '#layer { marker-fill:red; marker-width:32; marker-allow-overlap:true; }',
|
||||
cartocss_version: '2.3.0',
|
||||
affected_tables: ['test_table_3', 'test_table_4']
|
||||
}
|
||||
},
|
||||
]
|
||||
};
|
||||
|
||||
it('should return 1 feature for one layer', function(done) {
|
||||
var mapConfig = MapConfig.create(testMapConfigOneLayer);
|
||||
var layer = mapConfig.getLayer(0);
|
||||
var testSubject = new MapnikLayerStats();
|
||||
testSubject.getStats(layer, this.dbConnectionMock, function (err, result) {
|
||||
assert.ifError(err);
|
||||
assert.equal(result.estimatedFeatureCount, 1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should return 1 feature for two layers', function(done) {
|
||||
var self = this;
|
||||
var mapConfig = MapConfig.create(testMapConfigTwoLayers);
|
||||
var layer0 = mapConfig.getLayer(0);
|
||||
var layer1 = mapConfig.getLayer(1);
|
||||
var testSubject = new MapnikLayerStats();
|
||||
testSubject.getStats(layer0, self.dbConnectionMock, function (err, result) {
|
||||
assert.ifError(err);
|
||||
assert.equal(result.estimatedFeatureCount, 1);
|
||||
testSubject.getStats(layer1, self.dbConnectionMock, function (err, result) {
|
||||
assert.ifError(err);
|
||||
assert.equal(result.estimatedFeatureCount, 1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should return 1 feature for one layers with two tables', function(done) {
|
||||
var mapConfig = MapConfig.create(testMapConfigOneLayerTwoTables);
|
||||
var layer = mapConfig.getLayer(0);
|
||||
var testSubject = new MapnikLayerStats();
|
||||
testSubject.getStats(layer, this.dbConnectionMock, function (err, result) {
|
||||
assert.ifError(err);
|
||||
assert.equal(result.estimatedFeatureCount, 1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should return 1 feature for two layers and two tables', function(done) {
|
||||
var self = this;
|
||||
var mapConfig = MapConfig.create(testMapConfigTwoLayerTwoTables);
|
||||
var layer0 = mapConfig.getLayer(0);
|
||||
var layer1 = mapConfig.getLayer(1);
|
||||
var testSubject = new MapnikLayerStats();
|
||||
testSubject.getStats(layer0, self.dbConnectionMock, function (err, result) {
|
||||
assert.ifError(err);
|
||||
assert.equal(result.estimatedFeatureCount, 1);
|
||||
testSubject.getStats(layer1, self.dbConnectionMock, function (err, result) {
|
||||
assert.ifError(err);
|
||||
assert.equal(result.estimatedFeatureCount, 1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,36 +0,0 @@
|
||||
var assert = require('assert');
|
||||
var TorqueLayerStats = require('../../../../../lib/cartodb/backends/layer-stats/torque-layer-stats');
|
||||
var MapConfig = require('windshaft').model.MapConfig;
|
||||
|
||||
describe('torque-layer-stats', function () {
|
||||
|
||||
beforeEach(function () {
|
||||
this.params = {};
|
||||
});
|
||||
|
||||
var testMapConfigOneLayer = {
|
||||
version: '1.5.0',
|
||||
layers: [
|
||||
{
|
||||
type: 'torque',
|
||||
options: {
|
||||
sql: 'select * from test_table limit 2',
|
||||
cartocss: '#layer { marker-fill:red; marker-width:32; marker-allow-overlap:true; }',
|
||||
cartocss_version: '2.3.0',
|
||||
}
|
||||
},
|
||||
]
|
||||
};
|
||||
|
||||
it('should return torque stats for one layer', function(done) {
|
||||
var mapConfig = MapConfig.create(testMapConfigOneLayer);
|
||||
var layerId = 0;
|
||||
var layer = mapConfig.getLayer(layerId);
|
||||
var testSubject = new TorqueLayerStats();
|
||||
testSubject.getStats(layer, {}, function (err, result) {
|
||||
assert.ifError(err);
|
||||
assert.deepEqual({}, result);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,44 +0,0 @@
|
||||
var PostgresDatasource = require('../../../../lib/cartodb/backends/turbo-carto-postgres-datasource');
|
||||
var PSQL = require('cartodb-psql');
|
||||
var _ = require('underscore');
|
||||
var assert = require('assert');
|
||||
|
||||
describe('turbo-carto-postgres-datasource', function() {
|
||||
|
||||
beforeEach(function () {
|
||||
const dbname = _.template(global.environment.postgres_auth_user, { user_id: 1 }) + '_db';
|
||||
const psql = new PSQL({
|
||||
user: 'postgres',
|
||||
dbname: dbname,
|
||||
host: global.environment.postgres.host,
|
||||
port: global.environment.postgres.port
|
||||
});
|
||||
const sql = [
|
||||
'SELECT',
|
||||
' null::geometry the_geom_webmercator,',
|
||||
' CASE',
|
||||
' WHEN x % 4 = 0 THEN \'infinity\'::float',
|
||||
' WHEN x % 4 = 1 THEN \'-infinity\'::float',
|
||||
' WHEN x % 4 = 2 THEN \'NaN\'::float',
|
||||
' ELSE x',
|
||||
' END AS values',
|
||||
'FROM generate_series(1, 1000) x'
|
||||
].join('\n');
|
||||
this.datasource = new PostgresDatasource(psql, sql);
|
||||
});
|
||||
|
||||
it('should ignore NaNs and Infinities when computing ramps', function(done) {
|
||||
var column = 'values';
|
||||
var buckets = 4;
|
||||
var method = 'equal';
|
||||
this.datasource.getRamp(column, buckets, method, function(err, result) {
|
||||
var expected_result = {
|
||||
ramp: [ 252, 501, 750, 999 ],
|
||||
stats: { min_val: 3, max_val: 999, avg_val: 501 },
|
||||
strategy: undefined
|
||||
};
|
||||
assert.deepEqual(result, expected_result);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -111,23 +111,6 @@ describe('Bounding box filter', function() {
|
||||
createRef([-180, -45, 0, 45])
|
||||
);
|
||||
});
|
||||
|
||||
it('generating multiple bbox', function() {
|
||||
var bbox = [90, -45, 190, 45];
|
||||
var bboxFilter = createFilter(bbox);
|
||||
|
||||
assert.equal(bboxFilter.bboxes.length, 2);
|
||||
|
||||
assert.deepEqual(
|
||||
bboxFilter.bboxes[0],
|
||||
createRef([90, -45, 180, 45])
|
||||
);
|
||||
assert.deepEqual(
|
||||
bboxFilter.bboxes[1],
|
||||
createRef([-180, -45, -170, 45])
|
||||
);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('out of bounds', function() {
|
||||
|
||||
@@ -6,13 +6,10 @@ var LayergroupController = require('../../../../lib/cartodb/controllers/layergro
|
||||
|
||||
describe('tile stats', function() {
|
||||
|
||||
beforeEach(function () {
|
||||
this.statsClient = global.statsClient;
|
||||
after(function() {
|
||||
global.statsClient = null;
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
global.statsClient = this.statsClient;
|
||||
});
|
||||
|
||||
it('finalizeGetTileOrGrid does not call statsClient when format is not supported', function() {
|
||||
var expectedCalls = 2, // it will call increment once for the general error
|
||||
|
||||
420
yarn.lock
420
yarn.lock
@@ -10,7 +10,11 @@
|
||||
mapnik "~3.5.0"
|
||||
sphericalmercator "1.0.x"
|
||||
|
||||
abbrev@1, abbrev@1.0.x:
|
||||
abbrev@1:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.0.tgz#d0554c2256636e2f56e7c2e5ad183f859428d81f"
|
||||
|
||||
abbrev@1.0.x:
|
||||
version "1.0.9"
|
||||
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.0.9.tgz#91b4792588a7738c25f35dd6f63752a2f8776135"
|
||||
|
||||
@@ -48,10 +52,6 @@ ansi-styles@^2.2.1:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe"
|
||||
|
||||
ap@~0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/ap/-/ap-0.2.0.tgz#ae0942600b29912f0d2b14ec60c45e8f330b6110"
|
||||
|
||||
aproba@^1.0.3:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.1.2.tgz#45c6629094de4e96f693ef7eab74ae079c240fc1"
|
||||
@@ -157,10 +157,6 @@ brace-expansion@^1.1.7:
|
||||
balanced-match "^1.0.0"
|
||||
concat-map "0.0.1"
|
||||
|
||||
browser-stdout@1.3.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.0.tgz#f351d32969d32fa5d7a5567154263d928ae3bd1f"
|
||||
|
||||
buffer-writer@1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/buffer-writer/-/buffer-writer-1.0.1.tgz#22a936901e3029afcd7547eb4487ceb697a3bf08"
|
||||
@@ -194,13 +190,13 @@ camelcase@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a"
|
||||
|
||||
camshaft@0.55.7:
|
||||
version "0.55.7"
|
||||
resolved "https://registry.yarnpkg.com/camshaft/-/camshaft-0.55.7.tgz#6f09a0e9618a576ce89946e259189d6adf0c9b51"
|
||||
camshaft@0.54.4:
|
||||
version "0.54.4"
|
||||
resolved "https://registry.yarnpkg.com/camshaft/-/camshaft-0.54.4.tgz#3e1e96cda36661ebc01b64dadcc6387b02d69739"
|
||||
dependencies:
|
||||
async "^1.5.2"
|
||||
bunyan "1.8.1"
|
||||
cartodb-psql "^0.10.1"
|
||||
cartodb-psql "0.8.0"
|
||||
debug "^2.2.0"
|
||||
dot "^1.0.3"
|
||||
request "^2.69.0"
|
||||
@@ -245,7 +241,7 @@ cartocolor@4.0.0:
|
||||
dependencies:
|
||||
colorbrewer "1.0.0"
|
||||
|
||||
cartodb-psql@0.10.1, cartodb-psql@^0.10.1:
|
||||
cartodb-psql@0.10.1:
|
||||
version "0.10.1"
|
||||
resolved "https://registry.yarnpkg.com/cartodb-psql/-/cartodb-psql-0.10.1.tgz#0ac947e62fe10b27916df6b7ba6c461953fe3a23"
|
||||
dependencies:
|
||||
@@ -253,13 +249,22 @@ cartodb-psql@0.10.1, cartodb-psql@^0.10.1:
|
||||
pg cartodb/node-postgres#6.1.6-cdb1
|
||||
underscore "~1.6.0"
|
||||
|
||||
cartodb-psql@0.8.0:
|
||||
version "0.8.0"
|
||||
resolved "https://registry.yarnpkg.com/cartodb-psql/-/cartodb-psql-0.8.0.tgz#d3811f706dae2c3bc82365c5d25af13c4235ba37"
|
||||
dependencies:
|
||||
debug "~2.2.0"
|
||||
pg cartodb/node-postgres#6.1.2-cdb1
|
||||
step "~0.0.6"
|
||||
underscore "~1.6.0"
|
||||
|
||||
cartodb-query-tables@0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/cartodb-query-tables/-/cartodb-query-tables-0.2.0.tgz#b4d672accde04da5b890a5d56a87b761fa7eec44"
|
||||
|
||||
cartodb-redis@0.14.0:
|
||||
version "0.14.0"
|
||||
resolved "https://registry.yarnpkg.com/cartodb-redis/-/cartodb-redis-0.14.0.tgz#6f82fdb3e5b7c8005dbaccd6172c1706c4378df2"
|
||||
cartodb-redis@0.13.2:
|
||||
version "0.13.2"
|
||||
resolved "https://registry.yarnpkg.com/cartodb-redis/-/cartodb-redis-0.13.2.tgz#de5214fa5c3ab336c4da978133efa8f908b3691c"
|
||||
dependencies:
|
||||
dot "~1.0.2"
|
||||
redis-mpool "~0.4.1"
|
||||
@@ -302,12 +307,12 @@ chroma-js@~1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/chroma-js/-/chroma-js-1.1.1.tgz#9bb9434959336ece75700aaadfeedc71806d8c05"
|
||||
|
||||
cli@~1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/cli/-/cli-1.0.1.tgz#22817534f24bfa4950c34d532d48ecbc621b8c14"
|
||||
cli@0.6.x:
|
||||
version "0.6.6"
|
||||
resolved "https://registry.yarnpkg.com/cli/-/cli-0.6.6.tgz#02ad44a380abf27adac5e6f0cdd7b043d74c53e3"
|
||||
dependencies:
|
||||
exit "0.1.2"
|
||||
glob "^7.1.1"
|
||||
glob "~ 3.2.1"
|
||||
|
||||
cliui@^2.1.0:
|
||||
version "2.1.0"
|
||||
@@ -343,11 +348,17 @@ combined-stream@^1.0.5, combined-stream@~1.0.5:
|
||||
dependencies:
|
||||
delayed-stream "~1.0.0"
|
||||
|
||||
commander@2.9.0, commander@^2.9.0:
|
||||
version "2.9.0"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4"
|
||||
dependencies:
|
||||
graceful-readlink ">= 1.0.0"
|
||||
commander@0.6.1:
|
||||
version "0.6.1"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-0.6.1.tgz#fa68a14f6a945d54dbbe50d8cdb3320e9e3b1a06"
|
||||
|
||||
commander@2.3.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-2.3.0.tgz#fd430e889832ec353b9acd1de217c11cb3eef873"
|
||||
|
||||
commander@^2.9.0:
|
||||
version "2.11.0"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-2.11.0.tgz#157152fd1e7a6c8d98a5b715cf376df928004563"
|
||||
|
||||
concat-map@0.0.1:
|
||||
version "0.0.1"
|
||||
@@ -403,24 +414,30 @@ date-now@^0.1.4:
|
||||
version "0.1.4"
|
||||
resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b"
|
||||
|
||||
debug@2.2.0, debug@^2.2.0, debug@~2.2.0:
|
||||
debug@2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-2.0.0.tgz#89bd9df6732b51256bc6705342bba02ed12131ef"
|
||||
dependencies:
|
||||
ms "0.6.2"
|
||||
|
||||
debug@2.2.0, debug@~2.2.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-2.2.0.tgz#f87057e995b1a1f6ae6a4960664137bc56f039da"
|
||||
dependencies:
|
||||
ms "0.7.1"
|
||||
|
||||
debug@2.6.0:
|
||||
version "2.6.0"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.0.tgz#bc596bcabe7617f11d9fa15361eded5608b8499b"
|
||||
dependencies:
|
||||
ms "0.7.2"
|
||||
|
||||
debug@^1.0.4:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-1.0.5.tgz#f7241217430f99dec4c2b473eab92228e874c2ac"
|
||||
dependencies:
|
||||
ms "2.0.0"
|
||||
|
||||
debug@^2.2.0:
|
||||
version "2.6.8"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.8.tgz#e731531ca2ede27d188222427da17821d68ff4fc"
|
||||
dependencies:
|
||||
ms "2.0.0"
|
||||
|
||||
decamelize@^1.0.0, decamelize@^1.1.1:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
|
||||
@@ -459,9 +476,9 @@ destroy@~1.0.4:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80"
|
||||
|
||||
diff@3.2.0:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/diff/-/diff-3.2.0.tgz#c9ce393a4b7cbd0b058a725c93df299027868ff9"
|
||||
diff@1.0.8:
|
||||
version "1.0.8"
|
||||
resolved "https://registry.yarnpkg.com/diff/-/diff-1.0.8.tgz#343276308ec991b7bc82267ed55bc1411f971666"
|
||||
|
||||
dom-serializer@0:
|
||||
version "0.1.0"
|
||||
@@ -491,7 +508,11 @@ domutils@1.5:
|
||||
dom-serializer "0"
|
||||
domelementtype "1"
|
||||
|
||||
dot@^1.0.3, dot@~1.0.2:
|
||||
dot@^1.0.3:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/dot/-/dot-1.1.2.tgz#c7377019fc4e550798928b2b9afeb66abfa1f2f9"
|
||||
|
||||
dot@~1.0.2:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/dot/-/dot-1.0.3.tgz#f8750bfb6b03c7664eb0e6cb1eb4c66419af9427"
|
||||
|
||||
@@ -533,7 +554,11 @@ escape-html@~1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
|
||||
|
||||
escape-string-regexp@1.0.5, escape-string-regexp@^1.0.2:
|
||||
escape-string-regexp@1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.2.tgz#4dbc2fe674e71949caf3fb2695ce7f2dc1d9a8d1"
|
||||
|
||||
escape-string-regexp@^1.0.2:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
|
||||
|
||||
@@ -691,11 +716,11 @@ gauge@~2.7.3:
|
||||
wide-align "^1.1.0"
|
||||
|
||||
gdal@~0.9.2:
|
||||
version "0.9.4"
|
||||
resolved "https://registry.yarnpkg.com/gdal/-/gdal-0.9.4.tgz#6dad4abb8ffb3e0d51150fb7935ad7c622c81818"
|
||||
version "0.9.6"
|
||||
resolved "https://registry.yarnpkg.com/gdal/-/gdal-0.9.6.tgz#0cf75d830d35847b4274368b10e04a925321a0ba"
|
||||
dependencies:
|
||||
nan "~2.5.0"
|
||||
node-pre-gyp "~0.6.27"
|
||||
nan "~2.6.2"
|
||||
node-pre-gyp "~0.6.36"
|
||||
|
||||
generate-function@^2.0.0:
|
||||
version "2.0.0"
|
||||
@@ -733,16 +758,13 @@ getpass@^0.1.1:
|
||||
dependencies:
|
||||
assert-plus "^1.0.0"
|
||||
|
||||
glob@7.1.1, glob@^7.0.5, glob@^7.1.1:
|
||||
version "7.1.1"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8"
|
||||
glob@3.2.3:
|
||||
version "3.2.3"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-3.2.3.tgz#e313eeb249c7affaa5c475286b0e115b59839467"
|
||||
dependencies:
|
||||
fs.realpath "^1.0.0"
|
||||
inflight "^1.0.4"
|
||||
graceful-fs "~2.0.0"
|
||||
inherits "2"
|
||||
minimatch "^3.0.2"
|
||||
once "^1.3.0"
|
||||
path-is-absolute "^1.0.0"
|
||||
minimatch "~0.2.11"
|
||||
|
||||
glob@^5.0.15:
|
||||
version "5.0.15"
|
||||
@@ -764,13 +786,31 @@ glob@^6.0.1:
|
||||
once "^1.3.0"
|
||||
path-is-absolute "^1.0.0"
|
||||
|
||||
glob@^7.0.5:
|
||||
version "7.1.2"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15"
|
||||
dependencies:
|
||||
fs.realpath "^1.0.0"
|
||||
inflight "^1.0.4"
|
||||
inherits "2"
|
||||
minimatch "^3.0.4"
|
||||
once "^1.3.0"
|
||||
path-is-absolute "^1.0.0"
|
||||
|
||||
"glob@~ 3.2.1":
|
||||
version "3.2.11"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-3.2.11.tgz#4a973f635b9190f715d10987d5c00fd2815ebe3d"
|
||||
dependencies:
|
||||
inherits "2"
|
||||
minimatch "0.3"
|
||||
|
||||
graceful-fs@^4.1.2:
|
||||
version "4.1.11"
|
||||
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658"
|
||||
|
||||
"graceful-readlink@>= 1.0.0":
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725"
|
||||
graceful-fs@~2.0.0:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-2.0.3.tgz#7cd2cdb228a4a3f36e95efa6cc142de7d1a136d0"
|
||||
|
||||
grainstore@~1.6.0:
|
||||
version "1.6.3"
|
||||
@@ -786,9 +826,9 @@ grainstore@~1.6.0:
|
||||
semver "~5.0.3"
|
||||
underscore "~1.6.0"
|
||||
|
||||
growl@1.9.2:
|
||||
version "1.9.2"
|
||||
resolved "https://registry.yarnpkg.com/growl/-/growl-1.9.2.tgz#0ea7743715db8d8de2c5ede1775e1b45ac85c02f"
|
||||
growl@1.8.1:
|
||||
version "1.8.1"
|
||||
resolved "https://registry.yarnpkg.com/growl/-/growl-1.8.1.tgz#4b2dec8d907e93db336624dcec0183502f8c9428"
|
||||
|
||||
handlebars@^4.0.1:
|
||||
version "4.0.10"
|
||||
@@ -935,8 +975,8 @@ is-fullwidth-code-point@^1.0.0:
|
||||
number-is-nan "^1.0.0"
|
||||
|
||||
is-my-json-valid@^2.12.4:
|
||||
version "2.16.0"
|
||||
resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.16.0.tgz#f079dd9bfdae65ee2038aae8acbc86ab109e3693"
|
||||
version "2.16.1"
|
||||
resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.16.1.tgz#5a846777e2c2620d1e69104e5d3a03b1f6088f11"
|
||||
dependencies:
|
||||
generate-function "^2.0.0"
|
||||
generate-object-property "^1.1.0"
|
||||
@@ -990,6 +1030,13 @@ istanbul@~0.4.3:
|
||||
which "^1.1.1"
|
||||
wordwrap "^1.0.0"
|
||||
|
||||
jade@0.26.3:
|
||||
version "0.26.3"
|
||||
resolved "https://registry.yarnpkg.com/jade/-/jade-0.26.3.tgz#8f10d7977d8d79f2f6ff862a81b0513ccb25686c"
|
||||
dependencies:
|
||||
commander "0.6.1"
|
||||
mkdirp "0.3.0"
|
||||
|
||||
js-base64@^2.1.9:
|
||||
version "2.1.9"
|
||||
resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.1.9.tgz#f0e80ae039a4bd654b5f281fc93f04a914a7fcce"
|
||||
@@ -1009,18 +1056,18 @@ jsbn@~0.1.0:
|
||||
version "0.1.1"
|
||||
resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
|
||||
|
||||
jshint@~2.9.4:
|
||||
version "2.9.5"
|
||||
resolved "https://registry.yarnpkg.com/jshint/-/jshint-2.9.5.tgz#1e7252915ce681b40827ee14248c46d34e9aa62c"
|
||||
jshint@~2.6.0:
|
||||
version "2.6.3"
|
||||
resolved "https://registry.yarnpkg.com/jshint/-/jshint-2.6.3.tgz#84b470b8e5d5cd7adf0a3bd4975250443c9d311a"
|
||||
dependencies:
|
||||
cli "~1.0.0"
|
||||
cli "0.6.x"
|
||||
console-browserify "1.1.x"
|
||||
exit "0.1.x"
|
||||
htmlparser2 "3.8.x"
|
||||
lodash "3.7.x"
|
||||
minimatch "~3.0.2"
|
||||
minimatch "1.0.x"
|
||||
shelljs "0.3.x"
|
||||
strip-json-comments "1.0.x"
|
||||
underscore "1.6.x"
|
||||
|
||||
json-schema@0.2.3:
|
||||
version "0.2.3"
|
||||
@@ -1036,10 +1083,6 @@ json-stringify-safe@~5.0.1:
|
||||
version "5.0.1"
|
||||
resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
|
||||
|
||||
json3@3.3.2:
|
||||
version "3.3.2"
|
||||
resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.2.tgz#3c0434743df93e2f5c42aee7b19bcb483575f4e1"
|
||||
|
||||
jsonify@~0.0.0:
|
||||
version "0.0.0"
|
||||
resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73"
|
||||
@@ -1090,65 +1133,14 @@ load-json-file@^1.0.0:
|
||||
pinkie-promise "^2.0.0"
|
||||
strip-bom "^2.0.0"
|
||||
|
||||
lodash._baseassign@^3.0.0:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz#8c38a099500f215ad09e59f1722fd0c52bfe0a4e"
|
||||
dependencies:
|
||||
lodash._basecopy "^3.0.0"
|
||||
lodash.keys "^3.0.0"
|
||||
|
||||
lodash._basecopy@^3.0.0:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz#8da0e6a876cf344c0ad8a54882111dd3c5c7ca36"
|
||||
|
||||
lodash._basecreate@^3.0.0:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/lodash._basecreate/-/lodash._basecreate-3.0.3.tgz#1bc661614daa7fc311b7d03bf16806a0213cf821"
|
||||
|
||||
lodash._getnative@^3.0.0:
|
||||
version "3.9.1"
|
||||
resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5"
|
||||
|
||||
lodash._isiterateecall@^3.0.0:
|
||||
version "3.0.9"
|
||||
resolved "https://registry.yarnpkg.com/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz#5203ad7ba425fae842460e696db9cf3e6aac057c"
|
||||
|
||||
lodash.assign@^4.0.3, lodash.assign@^4.0.6, lodash.assign@^4.1.0, lodash.assign@^4.2.0:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-4.2.0.tgz#0d99f3ccd7a6d261d19bdaeb9245005d285808e7"
|
||||
|
||||
lodash.create@3.1.1:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/lodash.create/-/lodash.create-3.1.1.tgz#d7f2849f0dbda7e04682bb8cd72ab022461debe7"
|
||||
dependencies:
|
||||
lodash._baseassign "^3.0.0"
|
||||
lodash._basecreate "^3.0.0"
|
||||
lodash._isiterateecall "^3.0.0"
|
||||
|
||||
lodash.isarguments@^3.0.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a"
|
||||
|
||||
lodash.isarray@^3.0.0:
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-3.0.4.tgz#79e4eb88c36a8122af86f844aa9bcd851b5fbb55"
|
||||
|
||||
lodash.keys@^3.0.0:
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-3.1.2.tgz#4dbc0472b156be50a0b286855d1bd0b0c656098a"
|
||||
dependencies:
|
||||
lodash._getnative "^3.0.0"
|
||||
lodash.isarguments "^3.0.0"
|
||||
lodash.isarray "^3.0.0"
|
||||
|
||||
lodash@2.4.1:
|
||||
version "2.4.1"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-2.4.1.tgz#5b7723034dda4d262e5a46fb2c58d7cc22f71420"
|
||||
|
||||
lodash@3.7.x:
|
||||
version "3.7.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.7.0.tgz#3678bd8ab995057c07ade836ed2ef087da811d45"
|
||||
|
||||
lodash@^4.5.1:
|
||||
version "4.17.4"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae"
|
||||
@@ -1166,6 +1158,10 @@ longest@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097"
|
||||
|
||||
lru-cache@2:
|
||||
version "2.7.3"
|
||||
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.7.3.tgz#6d4524e8b955f95d4f5b58851ce21dd72fb4e952"
|
||||
|
||||
lru-cache@2.6.5:
|
||||
version "2.6.5"
|
||||
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.6.5.tgz#e56d6354148ede8d7707b58d143220fd08df0fd5"
|
||||
@@ -1241,7 +1237,7 @@ mime-types@^2.1.12, mime-types@~2.1.15, mime-types@~2.1.6, mime-types@~2.1.7:
|
||||
dependencies:
|
||||
mime-db "~1.29.0"
|
||||
|
||||
mime@1.3.4, mime@~1.3.4:
|
||||
mime@1.3.4:
|
||||
version "1.3.4"
|
||||
resolved "https://registry.yarnpkg.com/mime/-/mime-1.3.4.tgz#115f9e3b6b3daf2959983cb38f149a2d40eb5d53"
|
||||
|
||||
@@ -1249,13 +1245,38 @@ mime@~1.2.11:
|
||||
version "1.2.11"
|
||||
resolved "https://registry.yarnpkg.com/mime/-/mime-1.2.11.tgz#58203eed86e3a5ef17aed2b7d9ebd47f0a60dd10"
|
||||
|
||||
"minimatch@2 || 3", minimatch@^3.0.0, minimatch@^3.0.2, minimatch@~3.0.2:
|
||||
mime@~1.3.4:
|
||||
version "1.3.6"
|
||||
resolved "https://registry.yarnpkg.com/mime/-/mime-1.3.6.tgz#591d84d3653a6b0b4a3b9df8de5aa8108e72e5e0"
|
||||
|
||||
minimatch@0.3:
|
||||
version "0.3.0"
|
||||
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-0.3.0.tgz#275d8edaac4f1bb3326472089e7949c8394699dd"
|
||||
dependencies:
|
||||
lru-cache "2"
|
||||
sigmund "~1.0.0"
|
||||
|
||||
minimatch@1.0.x:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-1.0.0.tgz#e0dd2120b49e1b724ce8d714c520822a9438576d"
|
||||
dependencies:
|
||||
lru-cache "2"
|
||||
sigmund "~1.0.0"
|
||||
|
||||
"minimatch@2 || 3", minimatch@^3.0.0, minimatch@^3.0.4:
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
|
||||
dependencies:
|
||||
brace-expansion "^1.1.7"
|
||||
|
||||
minimist@0.0.8, minimist@~0.0.1:
|
||||
minimatch@~0.2.11:
|
||||
version "0.2.14"
|
||||
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-0.2.14.tgz#c74e780574f63c6f9a090e90efbe6ef53a6a756a"
|
||||
dependencies:
|
||||
lru-cache "2"
|
||||
sigmund "~1.0.0"
|
||||
|
||||
minimist@0.0.8:
|
||||
version "0.0.8"
|
||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
|
||||
|
||||
@@ -1263,44 +1284,55 @@ minimist@^1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
|
||||
|
||||
minimist@~0.0.1:
|
||||
version "0.0.10"
|
||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf"
|
||||
|
||||
minimist@~0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.2.0.tgz#4dffe525dae2b864c66c2e23c6271d7afdecefce"
|
||||
|
||||
mkdirp@0.5.1, mkdirp@0.5.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1:
|
||||
mkdirp@0.3.0:
|
||||
version "0.3.0"
|
||||
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.3.0.tgz#1bbf5ab1ba827af23575143490426455f481fe1e"
|
||||
|
||||
mkdirp@0.5.0:
|
||||
version "0.5.0"
|
||||
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.0.tgz#1d73076a6df986cd9344e15e71fcc05a4c9abf12"
|
||||
dependencies:
|
||||
minimist "0.0.8"
|
||||
|
||||
mkdirp@0.5.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1:
|
||||
version "0.5.1"
|
||||
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
|
||||
dependencies:
|
||||
minimist "0.0.8"
|
||||
|
||||
mocha@~3.4.1:
|
||||
version "3.4.2"
|
||||
resolved "https://registry.yarnpkg.com/mocha/-/mocha-3.4.2.tgz#d0ef4d332126dbf18d0d640c9b382dd48be97594"
|
||||
mocha@~1.21.4:
|
||||
version "1.21.5"
|
||||
resolved "https://registry.yarnpkg.com/mocha/-/mocha-1.21.5.tgz#7c58b09174df976e434a23b1e8d639873fc529e9"
|
||||
dependencies:
|
||||
browser-stdout "1.3.0"
|
||||
commander "2.9.0"
|
||||
debug "2.6.0"
|
||||
diff "3.2.0"
|
||||
escape-string-regexp "1.0.5"
|
||||
glob "7.1.1"
|
||||
growl "1.9.2"
|
||||
json3 "3.3.2"
|
||||
lodash.create "3.1.1"
|
||||
mkdirp "0.5.1"
|
||||
supports-color "3.1.2"
|
||||
commander "2.3.0"
|
||||
debug "2.0.0"
|
||||
diff "1.0.8"
|
||||
escape-string-regexp "1.0.2"
|
||||
glob "3.2.3"
|
||||
growl "1.8.1"
|
||||
jade "0.26.3"
|
||||
mkdirp "0.5.0"
|
||||
|
||||
moment@^2.10.6, moment@~2.18.1:
|
||||
moment@^2.10.6:
|
||||
version "2.18.1"
|
||||
resolved "https://registry.yarnpkg.com/moment/-/moment-2.18.1.tgz#c36193dd3ce1c2eed2adb7c802dbbc77a81b1c0f"
|
||||
|
||||
ms@0.6.2:
|
||||
version "0.6.2"
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-0.6.2.tgz#d89c2124c6fdc1353d65a8b77bf1aac4b193708c"
|
||||
|
||||
ms@0.7.1:
|
||||
version "0.7.1"
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098"
|
||||
|
||||
ms@0.7.2:
|
||||
version "0.7.2"
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.2.tgz#ae25cf2512b3885a1d95d7f037868d8431124765"
|
||||
|
||||
ms@2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
|
||||
@@ -1313,17 +1345,17 @@ mv@~2:
|
||||
ncp "~2.0.0"
|
||||
rimraf "~2.4.0"
|
||||
|
||||
nan@^2.0.8, nan@^2.3.4, nan@^2.4.0, nan@~2.6.2:
|
||||
version "2.6.2"
|
||||
resolved "https://registry.yarnpkg.com/nan/-/nan-2.6.2.tgz#e4ff34e6c95fdfb5aecc08de6596f43605a7db45"
|
||||
nan@^2.0.8, nan@^2.3.4, nan@^2.4.0:
|
||||
version "2.7.0"
|
||||
resolved "https://registry.yarnpkg.com/nan/-/nan-2.7.0.tgz#d95bf721ec877e08db276ed3fc6eb78f9083ad46"
|
||||
|
||||
nan@~2.4.0:
|
||||
version "2.4.0"
|
||||
resolved "https://registry.yarnpkg.com/nan/-/nan-2.4.0.tgz#fb3c59d45fe4effe215f0b890f8adf6eb32d2232"
|
||||
|
||||
nan@~2.5.0:
|
||||
version "2.5.1"
|
||||
resolved "https://registry.yarnpkg.com/nan/-/nan-2.5.1.tgz#d5b01691253326a97a2bbee9e61c55d8d60351e2"
|
||||
nan@~2.6.2:
|
||||
version "2.6.2"
|
||||
resolved "https://registry.yarnpkg.com/nan/-/nan-2.6.2.tgz#e4ff34e6c95fdfb5aecc08de6596f43605a7db45"
|
||||
|
||||
ncp@~2.0.0:
|
||||
version "2.0.0"
|
||||
@@ -1344,7 +1376,7 @@ nock@~2.11.0:
|
||||
mkdirp "^0.5.0"
|
||||
propagate "0.3.x"
|
||||
|
||||
node-pre-gyp@~0.6.27, node-pre-gyp@~0.6.30, node-pre-gyp@~0.6.36:
|
||||
node-pre-gyp@~0.6.30, node-pre-gyp@~0.6.36:
|
||||
version "0.6.36"
|
||||
resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.6.36.tgz#db604112cb74e0d477554e9b505b17abddfab786"
|
||||
dependencies:
|
||||
@@ -1516,15 +1548,26 @@ pg-pool@1.*:
|
||||
object-assign "4.1.0"
|
||||
|
||||
pg-types@1.*:
|
||||
version "1.12.0"
|
||||
resolved "https://registry.yarnpkg.com/pg-types/-/pg-types-1.12.0.tgz#8ad3b7b897e3fd463e62de241ad5fc640b4a66f0"
|
||||
version "1.12.1"
|
||||
resolved "https://registry.yarnpkg.com/pg-types/-/pg-types-1.12.1.tgz#d64087e3903b58ffaad279e7595c52208a14c3d2"
|
||||
dependencies:
|
||||
ap "~0.2.0"
|
||||
postgres-array "~1.0.0"
|
||||
postgres-bytea "~1.0.0"
|
||||
postgres-date "~1.0.0"
|
||||
postgres-interval "^1.1.0"
|
||||
|
||||
"pg@github:cartodb/node-postgres#6.1.2-cdb1":
|
||||
version "6.1.2"
|
||||
resolved "https://codeload.github.com/cartodb/node-postgres/tar.gz/3c81aea432ce58d20a795786c58bbb14f68f9689"
|
||||
dependencies:
|
||||
buffer-writer "1.0.1"
|
||||
packet-reader "0.2.0"
|
||||
pg-connection-string "0.1.3"
|
||||
pg-pool "1.*"
|
||||
pg-types "1.*"
|
||||
pgpass "1.x"
|
||||
semver "4.3.2"
|
||||
|
||||
"pg@github:cartodb/node-postgres#6.1.6-cdb1":
|
||||
version "6.1.6"
|
||||
resolved "https://codeload.github.com/cartodb/node-postgres/tar.gz/3eef52dd1e655f658a4ee8ac5697688b3ecfed44"
|
||||
@@ -1700,7 +1743,7 @@ read-pkg@^1.0.0:
|
||||
normalize-package-data "^2.3.2"
|
||||
path-type "^1.0.0"
|
||||
|
||||
readable-stream@1.1, readable-stream@~1.1.9:
|
||||
readable-stream@1.1:
|
||||
version "1.1.13"
|
||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.13.tgz#f6eef764f514c89e2b9e23146a75ba106756d23e"
|
||||
dependencies:
|
||||
@@ -1730,6 +1773,15 @@ readable-stream@~1.0.2:
|
||||
isarray "0.0.1"
|
||||
string_decoder "~0.10.x"
|
||||
|
||||
readable-stream@~1.1.9:
|
||||
version "1.1.14"
|
||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9"
|
||||
dependencies:
|
||||
core-util-is "~1.0.0"
|
||||
inherits "~2.0.1"
|
||||
isarray "0.0.1"
|
||||
string_decoder "~0.10.x"
|
||||
|
||||
redis-mpool@0.4.1, redis-mpool@~0.4.1:
|
||||
version "0.4.1"
|
||||
resolved "https://registry.yarnpkg.com/redis-mpool/-/redis-mpool-0.4.1.tgz#d917c0a4ed57a1291a9c6eb35434e6c0b7046f80"
|
||||
@@ -1747,7 +1799,7 @@ repeat-string@^1.5.2:
|
||||
version "1.6.1"
|
||||
resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637"
|
||||
|
||||
request@2.x, request@^2.81.0:
|
||||
request@2.x, request@^2.55.0, request@^2.69.0, request@^2.81.0:
|
||||
version "2.81.0"
|
||||
resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0"
|
||||
dependencies:
|
||||
@@ -1774,7 +1826,7 @@ request@2.x, request@^2.81.0:
|
||||
tunnel-agent "^0.6.0"
|
||||
uuid "^3.0.0"
|
||||
|
||||
request@^2.55.0, request@^2.69.0, request@~2.79.0:
|
||||
request@~2.79.0:
|
||||
version "2.79.0"
|
||||
resolved "https://registry.yarnpkg.com/request/-/request-2.79.0.tgz#4dfe5bf6be8b8cdc37fcf93e04b65577722710de"
|
||||
dependencies:
|
||||
@@ -1907,6 +1959,10 @@ shelljs@0.3.x:
|
||||
version "0.3.0"
|
||||
resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.3.0.tgz#3596e6307a781544f591f37da618360f31db57b1"
|
||||
|
||||
sigmund@~1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/sigmund/-/sigmund-1.0.1.tgz#3ff21f198cad2175f9f3b781853fd94d0d19b590"
|
||||
|
||||
signal-exit@^3.0.0:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
|
||||
@@ -1928,8 +1984,8 @@ source-map@^0.4.4:
|
||||
amdefine ">=0.0.4"
|
||||
|
||||
source-map@^0.5.1, source-map@^0.5.6, source-map@~0.5.1:
|
||||
version "0.5.6"
|
||||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412"
|
||||
version "0.5.7"
|
||||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
|
||||
|
||||
source-map@~0.2.0:
|
||||
version "0.2.0"
|
||||
@@ -1955,10 +2011,14 @@ speedometer@~0.1.2:
|
||||
version "0.1.4"
|
||||
resolved "https://registry.yarnpkg.com/speedometer/-/speedometer-0.1.4.tgz#9876dbd2a169d3115402d48e6ea6329c8816a50d"
|
||||
|
||||
sphericalmercator@1.0.4, sphericalmercator@1.0.x, sphericalmercator@~1.0.1, sphericalmercator@~1.0.4:
|
||||
sphericalmercator@1.0.4:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/sphericalmercator/-/sphericalmercator-1.0.4.tgz#baad4e34187f06e87f2e92fc1280199fa1b01d4e"
|
||||
|
||||
sphericalmercator@1.0.x, sphericalmercator@~1.0.1, sphericalmercator@~1.0.4:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/sphericalmercator/-/sphericalmercator-1.0.5.tgz#ddc5a049e360e000d0fad9fc22c4071882584980"
|
||||
|
||||
split@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/split/-/split-1.0.1.tgz#605bd9be303aa59fb35f9229fbea0ddec9ea07d9"
|
||||
@@ -2060,12 +2120,6 @@ strip-json-comments@~2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
|
||||
|
||||
supports-color@3.1.2:
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.1.2.tgz#72a262894d9d408b956ca05ff37b2ed8a6e2a2d5"
|
||||
dependencies:
|
||||
has-flag "^1.0.0"
|
||||
|
||||
supports-color@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"
|
||||
@@ -2108,17 +2162,17 @@ through@2:
|
||||
version "2.3.8"
|
||||
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
|
||||
|
||||
"tilelive-bridge@github:cartodb/tilelive-bridge#2.3.1-cdb4":
|
||||
version "2.3.1-cdb4"
|
||||
resolved "https://codeload.github.com/cartodb/tilelive-bridge/tar.gz/faa2b638da2d119b78281575d40255cb523f6ca6"
|
||||
"tilelive-bridge@github:cartodb/tilelive-bridge#2.3.1-cdb1":
|
||||
version "2.3.1-cdb1"
|
||||
resolved "https://codeload.github.com/cartodb/tilelive-bridge/tar.gz/3f76c278c782e93d79045870387a0a06bace720b"
|
||||
dependencies:
|
||||
mapnik "~3.5.0"
|
||||
mapnik-pool "~0.1.3"
|
||||
sphericalmercator "1.0.x"
|
||||
|
||||
"tilelive-mapnik@github:cartodb/tilelive-mapnik#0.6.18-cdb3":
|
||||
version "0.6.18-cdb3"
|
||||
resolved "https://codeload.github.com/cartodb/tilelive-mapnik/tar.gz/23bd1c31dd57d0b76c86b9f1eaf62462b3c17d01"
|
||||
"tilelive-mapnik@github:cartodb/tilelive-mapnik#0.6.18-cdb1":
|
||||
version "0.6.18-cdb1"
|
||||
resolved "https://codeload.github.com/cartodb/tilelive-mapnik/tar.gz/cf7e5b4633db653a889a6c6e6a5ddcbcf4ddc3b5"
|
||||
dependencies:
|
||||
generic-pool "~2.4.0"
|
||||
mapnik "3.5.14"
|
||||
@@ -2157,9 +2211,9 @@ tunnel-agent@~0.4.1:
|
||||
version "0.4.3"
|
||||
resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.4.3.tgz#6373db76909fe570e08d73583365ed828a74eeeb"
|
||||
|
||||
turbo-carto@0.19.2:
|
||||
version "0.19.2"
|
||||
resolved "https://registry.yarnpkg.com/turbo-carto/-/turbo-carto-0.19.2.tgz#062d68e59f89377f0cfa69a2717c047fe95e32fd"
|
||||
turbo-carto@0.19.0:
|
||||
version "0.19.0"
|
||||
resolved "https://registry.yarnpkg.com/turbo-carto/-/turbo-carto-0.19.0.tgz#83fb1932acd42acb426312eef216b5f6ac34708e"
|
||||
dependencies:
|
||||
cartocolor "4.0.0"
|
||||
colorbrewer "1.0.0"
|
||||
@@ -2210,6 +2264,10 @@ uid-number@^0.0.6:
|
||||
version "0.0.6"
|
||||
resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81"
|
||||
|
||||
underscore@1.6.x, underscore@~1.6.0:
|
||||
version "1.6.0"
|
||||
resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.6.0.tgz#8b38b10cacdef63337b8b24e4ff86d45aea529a8"
|
||||
|
||||
underscore@1.8.2:
|
||||
version "1.8.2"
|
||||
resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.8.2.tgz#64df2eb590899de950782f3735190ba42ebf311d"
|
||||
@@ -2218,10 +2276,6 @@ underscore@1.8.3:
|
||||
version "1.8.3"
|
||||
resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.8.3.tgz#4f3fb53b106e6097fcf9cb4109f2a5e9bdfa5022"
|
||||
|
||||
underscore@~1.6.0:
|
||||
version "1.6.0"
|
||||
resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.6.0.tgz#8b38b10cacdef63337b8b24e4ff86d45aea529a8"
|
||||
|
||||
unpipe@1.0.0, unpipe@~1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
|
||||
@@ -2281,14 +2335,14 @@ window-size@^0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.2.0.tgz#b4315bb4214a3d7058ebeee892e13fa24d98b075"
|
||||
|
||||
windshaft@3.3.1:
|
||||
version "3.3.1"
|
||||
resolved "https://registry.yarnpkg.com/windshaft/-/windshaft-3.3.1.tgz#b5557fa6b0cfa13920904f57206b86f7aa054f74"
|
||||
windshaft@3.1.2:
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/windshaft/-/windshaft-3.1.2.tgz#78c257bafe63c41df8cd7e4aa7ea6ea72ed1a5bd"
|
||||
dependencies:
|
||||
abaculus cartodb/abaculus#2.0.3-cdb1
|
||||
canvas cartodb/node-canvas#1.6.2-cdb2
|
||||
carto cartodb/carto#0.15.1-cdb3
|
||||
cartodb-psql "^0.10.1"
|
||||
cartodb-psql "0.8.0"
|
||||
debug "~2.2.0"
|
||||
dot "~1.0.2"
|
||||
grainstore "~1.6.0"
|
||||
@@ -2300,8 +2354,8 @@ windshaft@3.3.1:
|
||||
sphericalmercator "1.0.4"
|
||||
step "~0.0.6"
|
||||
tilelive "5.12.2"
|
||||
tilelive-bridge cartodb/tilelive-bridge#2.3.1-cdb4
|
||||
tilelive-mapnik cartodb/tilelive-mapnik#0.6.18-cdb3
|
||||
tilelive-bridge cartodb/tilelive-bridge#2.3.1-cdb1
|
||||
tilelive-mapnik cartodb/tilelive-mapnik#0.6.18-cdb1
|
||||
torque.js "~2.11.0"
|
||||
underscore "~1.6.0"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user