Compare commits

..

52 Commits

Author SHA1 Message Date
Raul Ochoa
ea41750a14 Release 2.49.0 2016-06-15 19:26:25 +02:00
Raul Ochoa
458376a665 Merge pull request #506 from CartoDB/upgrade-camshaft
Upgrade camshaft to 0.17.1
2016-06-15 19:24:48 +02:00
Raul Ochoa
f0284907c4 Upgrade camshaft to 0.17.1 2016-06-15 19:13:37 +02:00
Raul Ochoa
ad0385ccf7 Merge pull request #505 from CartoDB/upgrade-camshaft
Upgrade camshaft to 0.17.0
2016-06-15 16:58:23 +02:00
Raul Ochoa
4350fc3c65 Upgrade camshaft to 0.17.0 2016-06-15 16:48:00 +02:00
Raul Ochoa
fc3422b9e5 Merge pull request #503 from CartoDB/upgrade-camshaft
Upgrades camshaft to 0.16.0
2016-06-15 11:13:50 +02:00
Raul Ochoa
9703c19fb4 Upgrades camshaft to 0.16.0 2016-06-15 11:01:11 +02:00
Daniel García Aubert
d36f2fb354 Stubs next version 2016-06-14 17:50:40 +02:00
Daniel García Aubert
c837785314 Release 2.48.0 2016-06-14 17:48:07 +02:00
Daniel
33014a9f45 Merge pull request #500 from CartoDB/478-error-context
Adds more error information when either analysis or turbo-carto is not well formed
2016-06-14 15:56:00 +02:00
Daniel García Aubert
c16d0b8605 Fixed broken tests 2016-06-14 10:50:50 +02:00
Daniel García Aubert
c88e4c5173 Merge branch 'master' into 478-error-context 2016-06-14 10:43:00 +02:00
Daniel García Aubert
0540696c3e Avoid to expose internal naming 2016-06-14 10:27:35 +02:00
Raul Ochoa
5f59a97a02 Merge pull request #502 from CartoDB/upgrade-camshaft
Upgrades camshaft to 0.15.1
2016-06-14 01:41:41 +02:00
Raul Ochoa
d3b815c3c7 Upgrades camshaft to 0.15.1 2016-06-14 01:36:15 +02:00
Raul Ochoa
d2a8dcbede Merge pull request #501 from CartoDB/upgrade-camshaft
Upgrades camshaft to 0.15.0
2016-06-13 22:14:47 +02:00
Daniel García Aubert
4854e879a6 Updated npm-shrinkwrap 2016-06-13 19:49:20 +02:00
Daniel García Aubert
ddc99cebff Upgrades turbo-carto to 0.12.0 2016-06-13 18:40:14 +02:00
Raul Ochoa
47470d4f2b Upgrades camshaft to 0.15.0 2016-06-13 18:04:48 +02:00
Daniel García Aubert
d9297d54de made error_with_context non optional and adapted test's assertion 2016-06-13 16:14:01 +02:00
Daniel García Aubert
2d821f957e Merge branch 'master' into 478-error-context 2016-06-13 12:29:00 +02:00
Daniel García Aubert
c0ce6e7a8a WIP fixes 478, adds more error information when either analysis or turbo-carto is not well formed. 2016-06-13 12:20:56 +02:00
Raul Ochoa
3f620c6cdd Stubs next version 2016-06-13 10:47:15 +02:00
Raul Ochoa
9160d8018d Release 2.47.1 2016-06-13 10:43:45 +02:00
Raul Ochoa
51307bcc69 Upgrades camshaft to 0.14.1 2016-06-10 18:38:49 +02:00
Raul Ochoa
d29651ee80 Stubs next version 2016-06-10 14:41:08 +02:00
Raul Ochoa
18640077aa Release 2.47.0 2016-06-10 14:40:28 +02:00
Raul Ochoa
59563c893b Merge pull request #499 from CartoDB/upgrade-camshaft
Upgrades camshaft to 0.14.0
2016-06-10 14:19:39 +02:00
Raul Ochoa
90fd1786e1 Upgrades camshaft to 0.14.0 2016-06-10 14:05:23 +02:00
Raul Ochoa
4a11115dd0 Improve errors for dataviews validation 2016-06-09 18:13:54 +02:00
Raul Ochoa
baf3e774c5 Stubs next version 2016-06-09 10:33:35 +02:00
Raul Ochoa
ac296411d5 Release 2.46.0 2016-06-09 10:15:26 +02:00
Raul Ochoa
09cea4d6d4 Merge pull request #498 from CartoDB/upgrade-windshaft
Upgrades windshaft to 2.3.0
2016-06-08 19:45:53 +02:00
Raul Ochoa
382ff2416f Upgrades windshaft to 2.3.0 2016-06-08 19:14:48 +02:00
Raul Ochoa
27036379dd Merge pull request #494 from CartoDB/upgrade-windshaft
Upgrades windshaft to 2.2.0
2016-06-07 19:34:35 +02:00
Raul Ochoa
f4e6e140e0 Upgrades windshaft to 2.2.0 2016-06-07 19:26:41 +02:00
Raul Ochoa
b4e5cb88d9 Merge pull request #491 from CartoDB/issue-375
Sort start and end override params to correct bins width
2016-06-06 17:22:28 +02:00
Raul Ochoa
3269fef845 Sort start and end override params
Fixes #375
2016-06-06 17:10:52 +02:00
Raul Ochoa
e797719b41 Append url params for widgets 2016-06-06 17:10:36 +02:00
Raul Ochoa
284a8f2465 Deduplicate and skip falsy column names for geojson queries
Although Windshaft is already removing duplicates and skipping falsy
columns it's better to provide it with good input.

Closes #476
2016-06-06 15:58:16 +02:00
Raul Ochoa
54ea656da2 Merge pull request #490 from CartoDB/geojson-substitution-tokens
Upgrades windshaft to 2.1.0
2016-06-06 15:36:38 +02:00
Raul Ochoa
b4aaadf40b Upgrades windshaft to 2.1.0
Adds support for substitution tokens in geojson tiles

Fixes #484.
2016-06-06 15:29:58 +02:00
Raul Ochoa
74d2e3ef75 Merge pull request #489 from CartoDB/warn-about-deps
Warn on application start about non-matching dependencies
2016-06-06 12:11:38 +02:00
Raul Ochoa
b10e4c11d9 Warn on application start about non-matching dependencies 2016-06-06 12:07:43 +02:00
Raul Ochoa
075e141a9c Merge pull request #488 from CartoDB/upgrade-deps
Upgrades turbo-carto and camshaft deps
2016-06-06 12:04:56 +02:00
Raul Ochoa
04acf895f0 Upgrades turbo-carto and camshaft deps 2016-06-06 11:59:56 +02:00
Raul Ochoa
21608bf2e2 Merge pull request #487 from CartoDB/dataviews-adapter-fixes
Dataviews adapter working with non sql, non source layers
2016-06-06 11:56:31 +02:00
Raul Ochoa
653beb1952 Dataviews/widgets adapter working with non sql, non source, and non widgets layers
Ref #480
2016-06-06 11:46:27 +02:00
Raul Ochoa
050d33ff14 Use the_geom_webmercator; srid=3857 for the bounding box filter 2016-06-02 20:39:15 +02:00
Raul Ochoa
1ae86e039b Dataviews adapter: skip layers not containing SQL or widgets 2016-06-02 20:17:39 +02:00
Raul Ochoa
f75cadf6ba Dataviews adapter should work when there is a mix of layers with and without widgets 2016-06-02 19:51:16 +02:00
Raul Ochoa
93d0fe9176 Stubs next version 2016-06-02 16:21:40 +02:00
32 changed files with 1020 additions and 162 deletions

50
NEWS.md
View File

@@ -1,5 +1,55 @@
# Changelog
## 2.49.0
Released 2016-06-15
Announcements:
- Upgrades camshaft to [0.17.1](https://github.com/CartoDB/camshaft/releases/tag/0.17.1)
## 2.48.0
Released 2016-06-14
Announcements:
- Upgrades camshaft to [0.15.1](https://github.com/CartoDB/camshaft/releases/tag/0.15.1)
- Responses with more context info if analysis or turbo-carto fails during map creation.
## 2.47.1
Released 2016-06-13
Announcements:
- Upgrades camshaft to [0.14.1](https://github.com/CartoDB/camshaft/releases/tag/0.14.1)
## 2.47.0
Released 2016-06-10
Announcements:
- Upgrades camshaft to [0.14.0](https://github.com/CartoDB/camshaft/releases/tag/0.14.0)
## 2.46.0
Released 2016-06-09
Improvements:
- Support for substitution tokens in geojson tiles
- Warn on application start about non-matching dependencies
Announcements:
- Upgrades windshaft to [2.3.0](https://github.com/CartoDB/camshaft/releases/tag/2.3.0)
- Upgrades camshaft to [0.13.0](https://github.com/CartoDB/camshaft/releases/tag/0.13.0)
- Upgrades turbo-carto to [0.11.0](https://github.com/CartoDB/turbo-carto/releases/tag/0.11.0)
Bug fixes:
- Column provided for geojson renderer should not be null #476
- Dataviews/widgets adapter working with non sql, non source, and non widgets layers
## 2.45.0
Released 2016-06-02

View File

@@ -137,14 +137,14 @@ DataviewBackend.prototype.getDataview = function (mapConfigProvider, user, param
}
if (params.bbox) {
var bboxFilter = new BBoxFilter({column: 'the_geom', srid: 4326}, {bbox: 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',
srid: 4326,
column: 'the_geom_webmercator',
srid: 3857
},
params: {
bbox: params.bbox

View File

@@ -214,15 +214,15 @@ BaseController.prototype.sendError = function(req, res, err, label) {
statusCode = 200;
}
var errorResponseBody = { errors: allErrors.map(errorMessage) };
var errorResponseBody = {
errors: allErrors.map(errorMessage),
errors_with_context: allErrors.map(errorMessageWithContext)
};
this.send(req, res, errorResponseBody, statusCode);
};
function errorMessage(err) {
// See https://github.com/Vizzuality/Windshaft-cartodb/issues/68
var message = (_.isString(err) ? err : err.message) || 'Unknown error';
function stripConnectionInfo(message) {
// Strip connection info, if any
return message
// See https://github.com/CartoDB/Windshaft/issues/173
@@ -230,6 +230,24 @@ function errorMessage(err) {
// See https://travis-ci.org/CartoDB/Windshaft/jobs/20703062#L1644
.replace(/is the server.*encountered/im, 'encountered');
}
function errorMessage(err) {
// See https://github.com/Vizzuality/Windshaft-cartodb/issues/68
var message = (_.isString(err) ? err : err.message) || 'Unknown error';
return stripConnectionInfo(message);
}
function errorMessageWithContext(err) {
// See https://github.com/Vizzuality/Windshaft-cartodb/issues/68
var message = (_.isString(err) ? err : err.message) || 'Unknown error';
return {
type: err.type || 'unknown',
message: stripConnectionInfo(message),
context: err.context || 'unknown'
};
}
module.exports.errorMessage = errorMessage;
function findStatusCode(err) {

View File

@@ -1,21 +1,13 @@
var windshaft = require('windshaft');
var HealthCheck = require('../monitoring/health_check');
var WELCOME_MSG = "This is the CartoDB Maps API, " +
"see the documentation at http://docs.cartodb.com/cartodb-platform/maps-api.html";
var versions = {
windshaft: windshaft.version,
grainstore: windshaft.grainstore.version(),
node_mapnik: windshaft.mapnik.version,
mapnik: windshaft.mapnik.versions.mapnik,
windshaft_cartodb: require('../../../package.json').version
};
function ServerInfoController() {
function ServerInfoController(versions) {
this.healthConfig = global.environment.health || {};
this.healthCheck = new HealthCheck(global.environment.disabled_file);
this.versions = versions || {};
}
module.exports = ServerInfoController;
@@ -31,7 +23,7 @@ ServerInfoController.prototype.welcome = function(req, res) {
};
ServerInfoController.prototype.version = function(req, res) {
res.status(200).send(versions);
res.status(200).send(this.versions);
};
ServerInfoController.prototype.health = function(req, res) {

View File

@@ -174,8 +174,8 @@ Histogram.prototype.sql = function(psql, override, callback) {
basicsQuery = overrideBasicsQueryTpl({
_query: _query,
_column: _column,
_start: override.start,
_end: override.end
_start: getBinStart(override),
_end: getBinEnd(override)
});
binsQuery = [
@@ -248,7 +248,7 @@ Histogram.prototype.format = function(result, override) {
width = firstRow.bin_width || width;
avg = firstRow.avg_val;
nulls = firstRow.nulls_count;
binsStart = override.hasOwnProperty('start') ? override.start : firstRow.min;
binsStart = override.hasOwnProperty('start') ? getBinStart(override) : firstRow.min;
buckets = result.rows.map(function(row) {
return _.omit(row, 'bins_number', 'bin_width', 'nulls_count', 'avg_val');
@@ -266,9 +266,19 @@ Histogram.prototype.format = function(result, override) {
};
function getBinStart(override) {
if (override.hasOwnProperty('start') && override.hasOwnProperty('end')) {
return Math.min(override.start, override.end);
}
return override.start || 0;
}
function getBinEnd(override) {
if (override.hasOwnProperty('start') && override.hasOwnProperty('end')) {
return Math.max(override.start, override.end);
}
return override.end || 0;
}
function getBinsCount(override) {
return override.bins || 0;
}

View File

@@ -58,13 +58,27 @@ AnalysisMapConfigAdapter.prototype.getMapConfig = function(user, requestMapConfi
requestMapConfig = appendFiltersToNodes(requestMapConfig, dataviewsFiltersBySourceId);
function createAnalysis(analysisDefinition, done) {
self.analysisBackend.create(analysisConfiguration, analysisDefinition, done);
function createAnalysis(analysisDefinition, index, done) {
self.analysisBackend.create(analysisConfiguration, analysisDefinition, function (err, analysis) {
if (err) {
err.context = {
type: 'analysis',
analysis: {
index: index,
id: analysisDefinition.id,
type: analysisDefinition.type
}
};
return done(err);
}
done(null, analysis);
});
}
var analysesQueue = queue(requestMapConfig.analyses.length);
requestMapConfig.analyses.forEach(function(analysis) {
analysesQueue.defer(createAnalysis, analysis);
requestMapConfig.analyses.forEach(function(analysis, index) {
analysesQueue.defer(createAnalysis, analysis, index);
});
analysesQueue.awaitAll(function(err, analysesResults) {
@@ -101,10 +115,7 @@ AnalysisMapConfigAdapter.prototype.getMapConfig = function(user, requestMapConfi
analysisSql = sqlQueryWrap.replace(/<%=\s*sql\s*%>/g, analysisSql);
}
layer.options.sql = analysisSql;
var layerDataviews = getLayerDataviews(layer, dataviews);
layer.options.columns = layerDataviews.reduce(function(columns, dataview) {
return columns.concat(getDataviewColumns(dataview));
}, []);
layer.options.columns = getDataviewsColumns(getLayerDataviews(layer, dataviews));
} else {
missingNodesErrors.push(
new Error('Missing analysis node.id="' + layerSourceId +'" for layer='+layerIndex)
@@ -208,11 +219,22 @@ function getLayerDataviews(layer, dataviews) {
return layerDataviews;
}
function getDataviewsColumns(dataviews) {
return Object.keys(dataviews.reduce(function(columnsDict, dataview) {
getDataviewColumns(dataview).forEach(function(columnName) {
if (!!columnName) {
columnsDict[columnName] = true;
}
});
return columnsDict;
}, {}));
}
function getDataviewColumns(dataview) {
var columns = [];
var options = dataview.options;
['column', 'aggregationColumn'].forEach(function(opt) {
if (options.hasOwnProperty(opt)) {
if (options.hasOwnProperty(opt) && !!options[opt]) {
columns.push(options[opt]);
}
});
@@ -224,6 +246,15 @@ function getDataviewsList(dataviews) {
}
function getDataviewsErrors(dataviews) {
var dataviewType = typeof dataviews;
if (dataviewType !== 'object') {
return [new Error('"dataviews" must be a valid JSON object: "' + dataviewType + '" type found')];
}
if (Array.isArray(dataviews)) {
return [new Error('"dataviews" must be a valid JSON object: "array" type found')];
}
var errors = [];
Object.keys(dataviews).forEach(function(dataviewName) {

View File

@@ -15,6 +15,15 @@ DataviewsWidgetsMapConfigAdapter.prototype.getMapConfig = function(user, request
requestMapConfig.layers.forEach(function(layer, index) {
var layerSourceId = getLayerSourceId(layer);
if (!layer.options.widgets) {
return;
}
if (!layerSourceId && !layer.options.sql) {
return;
}
var dataviewSourceId = layerSourceId || 'cdb-layer-source-' + index;
// Append a new analysis if layer has no source id but sql.
if (!layerSourceId) {
@@ -29,7 +38,7 @@ DataviewsWidgetsMapConfigAdapter.prototype.getMapConfig = function(user, request
);
}
var source = { id: dataviewSourceId };
var layerWidgets = layer.options.widgets;
var layerWidgets = layer.options.widgets || {};
Object.keys(layerWidgets).forEach(function(widgetId) {
var dataview = layerWidgets[widgetId];
requestMapConfig.dataviews[widgetId] = {
@@ -67,7 +76,6 @@ DataviewsWidgetsMapConfigAdapter.prototype.getMapConfig = function(user, request
};
function shouldAdapt(requestMapConfig) {
// return false;
return Array.isArray(requestMapConfig.layers) && requestMapConfig.layers.some(function hasWidgets(layer) {
return layer.options && layer.options.widgets && Object.keys(layer.options.widgets).length > 0;
});

View File

@@ -22,8 +22,8 @@ TurboCartoAdapter.prototype.getMapConfig = function (user, requestMapConfig, par
var parseCartoQueue = queue(layers.length);
layers.forEach(function(layer) {
parseCartoQueue.defer(self._parseCartoCss.bind(self), user, layer);
layers.forEach(function(layer, index) {
parseCartoQueue.defer(self._parseCartoCss.bind(self), user, layer, index);
});
parseCartoQueue.awaitAll(function (err, layers) {
@@ -51,7 +51,7 @@ var pixelSizeTemplate = dot.template('40075017 * cos(ST_Y(ST_Centroid({{=it._bbo
var scaleDenominatorTemplate = dot.template('({{=it._pixelSize}} / 0.00028)::numeric');
TurboCartoAdapter.prototype._parseCartoCss = function (username, layer, callback) {
TurboCartoAdapter.prototype._parseCartoCss = function (username, layer, index, callback) {
if (!shouldParseLayerCartocss(layer)) {
return callback(null, layer);
}
@@ -84,9 +84,16 @@ TurboCartoAdapter.prototype._parseCartoCss = function (username, layer, callback
this.turboCartoParser.process(username, layer.options.cartocss, sql, function (err, cartocss) {
// Only return turbo-carto errors
if (err && err.name === 'TurboCartoError') {
err = new Error('turbo-carto: ' + err.message);
err.http_status = 400;
return callback(err);
var error = new Error('turbo-carto: ' + err.message);
error.http_status = 400;
error.type = 'turbo-carto';
error.context = err.context;
error.context.layer = {
index: index,
type: layer.type
};
return callback(error);
}
// Try to continue in the rest of the cases

View File

@@ -3,7 +3,6 @@ var bodyParser = require('body-parser');
var RedisPool = require('redis-mpool');
var cartodbRedis = require('cartodb-redis');
var _ = require('underscore');
var debug = require('debug')('windshaft:cartodb');
var controller = require('./controllers');
@@ -183,6 +182,8 @@ module.exports = function(serverOptions) {
var TablesExtentApi = require('./api/tables_extent_api');
var tablesExtentApi = new TablesExtentApi(pgQueryRunner);
var versions = getAndValidateVersions(serverOptions);
/*******************************************************************************************************************
* Routing
******************************************************************************************************************/
@@ -225,7 +226,7 @@ module.exports = function(serverOptions) {
new controller.NamedMapsAdmin(authApi, pgConnection, templateMaps).register(app);
new controller.ServerInfo().register(app);
new controller.ServerInfo(versions).register(app);
/*******************************************************************************************************************
* END Routing
@@ -238,12 +239,45 @@ function validateOptions(opts) {
if (!_.isString(opts.base_url) || !_.isString(opts.base_url_mapconfig) || !_.isString(opts.base_url_templated)) {
throw new Error("Must initialise server with: 'base_url'/'base_url_mapconfig'/'base_url_templated' URLs");
}
}
// Be nice and warn if configured mapnik version is != instaled mapnik version
if (mapnik.versions.mapnik !== opts.grainstore.mapnik_version) {
debug('WARNING: detected mapnik version (' + mapnik.versions.mapnik + ')' +
' != configured mapnik version (' + opts.grainstore.mapnik_version + ')');
function getAndValidateVersions(options) {
// jshint undef:false
var warn = console.warn.bind(console);
// jshint undef:true
var packageDefinition = require('../../package.json');
var declaredDependencies = packageDefinition.dependencies || {};
var installedDependenciesVersions = {
camshaft: require('camshaft').version,
grainstore: windshaft.grainstore.version(),
mapnik: windshaft.mapnik.versions.mapnik,
node_mapnik: windshaft.mapnik.version,
'turbo-carto': require('turbo-carto').version,
windshaft: windshaft.version,
windshaft_cartodb: packageDefinition.version
};
var dependenciesToValidate = ['camshaft', 'turbo-carto', 'windshaft'];
dependenciesToValidate.forEach(function(depName) {
var declaredDependencyVersion = declaredDependencies[depName];
var installedDependencyVersion = installedDependenciesVersions[depName];
if (declaredDependencyVersion !== installedDependencyVersion) {
warn(
'Dependency="%s" installed version="%s" does not match declared version="%s". Check your installation.',
depName, installedDependencyVersion, declaredDependencyVersion
);
}
});
// Be nice and warn if configured mapnik version is != installed mapnik version
if (mapnik.versions.mapnik !== options.grainstore.mapnik_version) {
warn('WARNING: detected mapnik version (' + mapnik.versions.mapnik + ')' +
' != configured mapnik version (' + options.grainstore.mapnik_version + ')');
}
return installedDependenciesVersions;
}
function bootstrapFonts(opts) {

126
npm-shrinkwrap.json generated
View File

@@ -1,6 +1,6 @@
{
"name": "windshaft-cartodb",
"version": "2.45.0",
"version": "2.49.0",
"dependencies": {
"body-parser": {
"version": "1.14.2",
@@ -105,9 +105,9 @@
}
},
"camshaft": {
"version": "0.12.1",
"from": "camshaft@0.12.1",
"resolved": "https://registry.npmjs.org/camshaft/-/camshaft-0.12.1.tgz",
"version": "0.17.1",
"from": "camshaft@0.17.1",
"resolved": "https://registry.npmjs.org/camshaft/-/camshaft-0.17.1.tgz",
"dependencies": {
"async": {
"version": "1.5.2",
@@ -648,7 +648,7 @@
"dependencies": {
"unpipe": {
"version": "1.0.0",
"from": "unpipe@1.0.0",
"from": "unpipe@>=1.0.0 <1.1.0",
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz"
}
}
@@ -1017,7 +1017,7 @@
"dependencies": {
"async": {
"version": "1.5.2",
"from": "async@>=1.5.2 <2.0.0",
"from": "async@>=1.0.0 <2.0.0",
"resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz"
}
}
@@ -1029,7 +1029,7 @@
},
"mime-types": {
"version": "2.1.11",
"from": "mime-types@>=2.1.11 <2.2.0",
"from": "mime-types@>=2.1.2 <2.2.0",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.11.tgz",
"dependencies": {
"mime-db": {
@@ -1257,9 +1257,9 @@
"resolved": "https://registry.npmjs.org/step-profiler/-/step-profiler-0.3.0.tgz"
},
"turbo-carto": {
"version": "0.10.1",
"from": "turbo-carto@0.10.1",
"resolved": "https://registry.npmjs.org/turbo-carto/-/turbo-carto-0.10.1.tgz",
"version": "0.12.0",
"from": "turbo-carto@0.12.0",
"resolved": "https://registry.npmjs.org/turbo-carto/-/turbo-carto-0.12.0.tgz",
"dependencies": {
"colorbrewer": {
"version": "1.0.0",
@@ -1318,9 +1318,9 @@
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz"
},
"windshaft": {
"version": "2.0.1",
"from": "windshaft@2.0.1",
"resolved": "https://registry.npmjs.org/windshaft/-/windshaft-2.0.1.tgz",
"version": "2.3.0",
"from": "windshaft@2.3.0",
"resolved": "https://registry.npmjs.org/windshaft/-/windshaft-2.3.0.tgz",
"dependencies": {
"abaculus": {
"version": "1.1.0-cdb5",
@@ -1369,9 +1369,9 @@
}
},
"grainstore": {
"version": "1.1.1",
"from": "grainstore@1.1.1",
"resolved": "https://registry.npmjs.org/grainstore/-/grainstore-1.1.1.tgz",
"version": "1.2.0",
"from": "grainstore@1.2.0",
"resolved": "https://registry.npmjs.org/grainstore/-/grainstore-1.2.0.tgz",
"dependencies": {
"carto": {
"version": "0.9.5-cdb2",
@@ -3041,16 +3041,16 @@
"from": "caseless@>=0.11.0 <0.12.0",
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.11.0.tgz"
},
"chalk": {
"version": "1.1.3",
"from": "chalk@>=1.1.1 <2.0.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz"
},
"combined-stream": {
"version": "1.0.5",
"from": "combined-stream@>=1.0.5 <1.1.0",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz"
},
"chalk": {
"version": "1.1.3",
"from": "chalk@>=1.1.1 <2.0.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz"
},
"commander": {
"version": "2.9.0",
"from": "commander@>=2.9.0 <3.0.0",
@@ -3096,16 +3096,16 @@
"from": "ecc-jsbn@>=0.1.1 <0.2.0",
"resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz"
},
"escape-string-regexp": {
"version": "1.0.5",
"from": "escape-string-regexp@>=1.0.2 <2.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz"
},
"extend": {
"version": "3.0.0",
"from": "extend@>=3.0.0 <3.1.0",
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.0.tgz"
},
"escape-string-regexp": {
"version": "1.0.5",
"from": "escape-string-regexp@>=1.0.2 <2.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz"
},
"extsprintf": {
"version": "1.0.2",
"from": "extsprintf@1.0.2",
@@ -3146,6 +3146,11 @@
"from": "generate-function@>=2.0.0 <3.0.0",
"resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz"
},
"graceful-fs": {
"version": "4.1.4",
"from": "graceful-fs@>=4.1.2 <5.0.0",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.4.tgz"
},
"glob": {
"version": "7.0.3",
"from": "glob@>=7.0.0 <8.0.0",
@@ -3161,11 +3166,6 @@
"from": "har-validator@>=2.0.6 <2.1.0",
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-2.0.6.tgz"
},
"graceful-fs": {
"version": "4.1.4",
"from": "graceful-fs@>=4.1.2 <5.0.0",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.4.tgz"
},
"has-ansi": {
"version": "2.0.0",
"from": "has-ansi@>=2.0.0 <3.0.0",
@@ -3186,16 +3186,16 @@
"from": "hoek@>=2.0.0 <3.0.0",
"resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz"
},
"inflight": {
"version": "1.0.4",
"from": "inflight@>=1.0.4 <2.0.0",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.4.tgz"
},
"http-signature": {
"version": "1.1.1",
"from": "http-signature@>=1.1.0 <1.2.0",
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz"
},
"inflight": {
"version": "1.0.4",
"from": "inflight@>=1.0.4 <2.0.0",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.4.tgz"
},
"inherits": {
"version": "2.0.1",
"from": "inherits@>=2.0.1 <2.1.0",
@@ -3206,16 +3206,16 @@
"from": "ini@>=1.3.0 <1.4.0",
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.4.tgz"
},
"is-my-json-valid": {
"version": "2.13.1",
"from": "is-my-json-valid@>=2.12.4 <3.0.0",
"resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.13.1.tgz"
},
"is-property": {
"version": "1.0.2",
"from": "is-property@>=1.0.0 <2.0.0",
"resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz"
},
"is-my-json-valid": {
"version": "2.13.1",
"from": "is-my-json-valid@>=2.12.4 <3.0.0",
"resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.13.1.tgz"
},
"is-typedarray": {
"version": "1.0.0",
"from": "is-typedarray@>=1.0.0 <1.1.0",
@@ -3301,26 +3301,26 @@
"from": "mime-types@>=2.1.7 <2.2.0",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.11.tgz"
},
"minimatch": {
"version": "3.0.0",
"from": "minimatch@>=2.0.0 <3.0.0||>=3.0.0 <4.0.0",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.0.tgz"
},
"minimist": {
"version": "0.0.8",
"from": "minimist@0.0.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz"
},
"mkdirp": {
"version": "0.5.1",
"from": "mkdirp@>=0.5.0 <0.6.0",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz"
"minimatch": {
"version": "3.0.0",
"from": "minimatch@>=2.0.0 <3.0.0||>=3.0.0 <4.0.0",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.0.tgz"
},
"ms": {
"version": "0.7.1",
"from": "ms@0.7.1",
"resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz"
},
"mkdirp": {
"version": "0.5.1",
"from": "mkdirp@>=0.5.0 <0.6.0",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz"
},
"node-uuid": {
"version": "1.4.7",
"from": "node-uuid@>=1.4.7 <1.5.0",
@@ -3456,16 +3456,16 @@
"from": "verror@1.3.6",
"resolved": "https://registry.npmjs.org/verror/-/verror-1.3.6.tgz"
},
"xtend": {
"version": "4.0.1",
"from": "xtend@>=4.0.0 <5.0.0",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz"
},
"wrappy": {
"version": "1.0.1",
"from": "wrappy@>=1.0.0 <2.0.0",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz"
},
"xtend": {
"version": "4.0.1",
"from": "xtend@>=4.0.0 <5.0.0",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz"
},
"bl": {
"version": "1.1.2",
"from": "bl@>=1.1.2 <1.2.0",
@@ -3478,10 +3478,10 @@
}
}
},
"getpass": {
"version": "0.1.6",
"from": "getpass@>=0.1.1 <0.2.0",
"resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.6.tgz",
"dashdash": {
"version": "1.13.1",
"from": "dashdash@>=1.12.0 <2.0.0",
"resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.13.1.tgz",
"dependencies": {
"assert-plus": {
"version": "1.0.0",
@@ -3490,10 +3490,10 @@
}
}
},
"dashdash": {
"version": "1.13.1",
"from": "dashdash@>=1.12.0 <2.0.0",
"resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.13.1.tgz",
"getpass": {
"version": "0.1.6",
"from": "getpass@>=0.1.1 <0.2.0",
"resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.6.tgz",
"dependencies": {
"assert-plus": {
"version": "1.0.0",

View File

@@ -1,7 +1,7 @@
{
"private": true,
"name": "windshaft-cartodb",
"version": "2.45.0",
"version": "2.49.0",
"description": "A map tile server for CartoDB",
"keywords": [
"cartodb"
@@ -20,7 +20,7 @@
],
"dependencies": {
"body-parser": "~1.14.0",
"camshaft": "0.12.1",
"camshaft": "0.17.1",
"cartodb-psql": "~0.6.1",
"cartodb-query-tables": "~0.1.0",
"cartodb-redis": "~0.13.0",
@@ -37,9 +37,9 @@
"request": "~2.62.0",
"step": "~0.0.6",
"step-profiler": "~0.3.0",
"turbo-carto": "0.10.1",
"turbo-carto": "0.12.0",
"underscore": "~1.6.0",
"windshaft": "2.0.1"
"windshaft": "2.3.0"
},
"devDependencies": {
"istanbul": "~0.4.3",

View File

@@ -20,6 +20,13 @@ describe('analysis-layers error cases', function() {
}
};
var AUTH_ERROR_RESPONSE = {
status: 403,
headers: {
'Content-Type': 'application/json; charset=utf-8'
}
};
it('should handle missing analysis nodes for layers', function(done) {
var mapConfig = createMapConfig(
[
@@ -64,4 +71,112 @@ describe('analysis-layers error cases', function() {
testClient.drain(done);
});
});
it('camshaft: should return error missing analysis nodes for layers with some context', function(done) {
var mapConfig = createMapConfig(
[
{
"type": "cartodb",
"options": {
"source": {
"id": "HEAD"
},
"cartocss": '#polygons { polygon-fill: red; }',
"cartocss_version": "2.3.0"
}
}
],
{},
[
{
"id": "HEAD",
"type": "buffer",
"params": {
"source": {
"id": "HEAD",
"type": "source",
"params": {
"query": "select * from populated_places_simple_reduced"
}
},
"radius": 50000
}
}
]
);
var testClient = new TestClient(mapConfig, 11111);
testClient.getLayergroup(AUTH_ERROR_RESPONSE, function(err, layergroupResult) {
assert.ok(!err, err);
assert.equal(layergroupResult.errors.length, 1);
assert.equal(
layergroupResult.errors[0],
'Analysis requires authentication with API key: permission denied.'
);
assert.equal(layergroupResult.errors_with_context[0].context.type, 'analysis');
assert.equal(layergroupResult.errors_with_context[0].context.analysis.index, 0);
assert.equal(layergroupResult.errors_with_context[0].context.analysis.id, 'HEAD');
assert.equal(layergroupResult.errors_with_context[0].context.analysis.type, 'buffer');
testClient.drain(done);
});
});
it('camshaft: should return error: Missing required param "radius"; with context', function(done) {
var mapConfig = createMapConfig(
[
{
"type": "cartodb",
"options": {
"source": {
"id": "HEAD"
},
"cartocss": '#polygons { polygon-fill: red; }',
"cartocss_version": "2.3.0"
}
}
],
{},
[
{
"id": "HEAD",
"type": "buffer",
"params": {
"source": {
"id": "HEAD",
"type": "source",
"params": {
"query": "select * from populated_places_simple_reduced"
}
}
}
}
]
);
var testClient = new TestClient(mapConfig, 1234);
testClient.getLayergroup(ERROR_RESPONSE, function(err, layergroupResult) {
assert.ok(!err, err);
assert.equal(layergroupResult.errors.length, 1);
assert.equal(
layergroupResult.errors[0],
'Missing required param "radius"'
);
assert.equal(layergroupResult.errors_with_context[0].context.type, 'analysis');
assert.equal(layergroupResult.errors_with_context[0].context.analysis.index, 0);
assert.equal(layergroupResult.errors_with_context[0].context.analysis.id, 'HEAD');
assert.equal(layergroupResult.errors_with_context[0].context.analysis.type, 'buffer');
testClient.drain(done);
});
});
});

View File

@@ -0,0 +1,87 @@
require('../../support/test_helper');
var assert = require('../../support/assert');
var TestClient = require('../../support/test-client');
describe('histogram-dataview', function() {
afterEach(function(done) {
if (this.testClient) {
this.testClient.drain(done);
} else {
done();
}
});
var ERROR_RESPONSE = {
status: 400,
headers: {
'Content-Type': 'application/json; charset=utf-8'
}
};
function createMapConfig(dataviews) {
return {
version: '1.5.0',
layers: [
{
"type": "cartodb",
"options": {
"source": {
"id": "HEAD"
},
"cartocss": "#points { marker-width: 10; marker-fill: red; }",
"cartocss_version": "2.3.0"
}
}
],
dataviews: dataviews,
analyses: [
{
"id": "HEAD",
"type": "source",
"params": {
"query": "select null::geometry the_geom_webmercator, x from generate_series(0,1000) x"
}
}
]
};
}
it('should fail when invalid dataviews object is provided, string case', function(done) {
var mapConfig = createMapConfig("wadus-string");
this.testClient = new TestClient(mapConfig, 1234);
this.testClient.getLayergroup(ERROR_RESPONSE, function(err, errObj) {
assert.ok(!err, err);
assert.deepEqual(errObj.errors, [ '"dataviews" must be a valid JSON object: "string" type found' ]);
done();
});
});
it('should fail when invalid dataviews object is provided, array case', function(done) {
var mapConfig = createMapConfig([]);
this.testClient = new TestClient(mapConfig, 1234);
this.testClient.getLayergroup(ERROR_RESPONSE, function(err, errObj) {
assert.ok(!err, err);
assert.deepEqual(errObj.errors, [ '"dataviews" must be a valid JSON object: "array" type found' ]);
done();
});
});
it('should work with empty but valid objects', function(done) {
var mapConfig = createMapConfig({});
this.testClient = new TestClient(mapConfig, 1234);
this.testClient.getLayergroup(function(err, layergroup) {
assert.ok(!err, err);
assert.ok(layergroup);
assert.ok(layergroup.layergroupid);
done();
});
});
});

View File

@@ -0,0 +1,80 @@
require('../../support/test_helper');
var assert = require('../../support/assert');
var TestClient = require('../../support/test-client');
describe('histogram-dataview', 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": "2570e105-7b37-40d2-bdf4-1af889598745"
},
"cartocss": "#points { marker-width: 10; marker-fill: red; }",
"cartocss_version": "2.3.0"
}
}
],
{
pop_max_histogram: {
source: {
id: '2570e105-7b37-40d2-bdf4-1af889598745'
},
type: 'histogram',
options: {
column: 'x'
}
}
},
[
{
"id": "2570e105-7b37-40d2-bdf4-1af889598745",
"type": "source",
"params": {
"query": "select null::geometry the_geom_webmercator, x from generate_series(0,1000) x"
}
}
]
);
it('should get bin_width right when max > min in filter', function(done) {
var params = {
bins: 10,
start: 1e3,
end: 0
};
this.testClient = new TestClient(mapConfig, 1234);
this.testClient.getDataview('pop_max_histogram', params, function(err, dataview) {
assert.ok(!err, err);
assert.equal(dataview.type, 'histogram');
assert.ok(dataview.bin_width > 0, 'Unexpected bin width: ' + dataview.bin_width);
dataview.bins.forEach(function(bin) {
assert.ok(bin.min <= bin.max, 'bin min < bin max: ' + JSON.stringify(bin));
});
done();
});
});
});

View File

@@ -168,4 +168,176 @@ describe('use only needed columns', function() {
});
});
});
it('should work with mapnik substitution tokens', function(done) {
var cartocss = [
"#layer {",
" line-width: 2;",
" line-color: #3B3B58;",
" line-opacity: 1;",
" polygon-opacity: 0.7;",
" polygon-fill: ramp([points_count], (#E5F5F9,#99D8C9,#2CA25F))",
"}"
].join('\n');
var sql = [
'WITH hgrid AS (',
' SELECT CDB_HexagonGrid(',
' ST_Expand(!bbox!, greatest(!pixel_width!,!pixel_height!) * 100),',
' greatest(!pixel_width!,!pixel_height!) * 100',
' ) as cell',
')',
'SELECT',
' hgrid.cell as the_geom_webmercator,',
' count(1) as points_count,',
' count(1)/power(100 * CDB_XYZ_Resolution(CDB_ZoomFromScale(!scale_denominator!)), 2) as points_density,',
' 1 as cartodb_id',
'FROM hgrid, (SELECT * FROM populated_places_simple_reduced) i',
'where ST_Intersects(i.the_geom_webmercator, hgrid.cell)',
'GROUP BY hgrid.cell'
].join('\n');
var mapConfig = {
"version": "1.4.0",
"layers": [
{
"type": 'mapnik',
"options": {
"cartocss_version": '2.3.0',
"sql": sql,
"cartocss": cartocss
}
}
]
};
this.testClient = new TestClient(mapConfig);
this.testClient.getTile(0, 0, 0, { format: 'geojson', layer: 0 }, function(err, res, geojson) {
assert.ok(!err, err);
assert.ok(geojson);
assert.equal(geojson.features.length, 5);
done();
});
});
it('should skip empty and null columns for geojson tiles', function(done) {
var mapConfig = {
"analyses": [
{
"id": "a0",
"params": {
"query": "SELECT * FROM test_table"
},
"type": "source"
}
],
"dataviews": {
"4e7b0e07-6d21-4b83-9adb-6d7e17eea6ca": {
"options": {
"aggregationColumn": null,
"column": "cartodb_id",
"operation": "avg"
},
"source": {
"id": "a0"
},
"type": "formula"
},
"74f590f8-625c-4e95-922f-34ad3e9919c0": {
"options": {
"aggregation": "sum",
"aggregationColumn": "cartodb_id",
"column": "name"
},
"source": {
"id": "a0"
},
"type": "aggregation"
},
"98a75757-3006-400a-b028-fb613a6c0b69": {
"options": {
"aggregationColumn": null,
"column": "cartodb_id",
"operation": "sum"
},
"source": {
"id": "a0"
},
"type": "formula"
},
"ebbc97b2-87d2-4895-9e1f-2f012df3679d": {
"options": {
"aggregationColumn": null,
"bins": "12",
"column": "cartodb_id"
},
"source": {
"id": "a0"
},
"type": "histogram"
},
"ebc0653f-3581-469c-8b31-c969e440a865": {
"options": {
"aggregationColumn": null,
"column": "cartodb_id",
"operation": "avg"
},
"source": {
"id": "a0"
},
"type": "formula"
}
},
"layers": [
{
"options": {
"subdomains": "abcd",
"urlTemplate": "http://{s}.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}.png"
},
"type": "http"
},
{
"options": {
"attributes": {
"columns": [
"name",
"address"
],
"id": "cartodb_id"
},
"cartocss": "#layer { marker-width: 10; marker-fill: red; }",
"cartocss_version": "2.3.0",
"interactivity": "cartodb_id",
"layer_name": "wadus",
"source": {
"id": "a0"
}
},
"type": "cartodb"
},
{
"options": {
"subdomains": "abcd",
"urlTemplate": "http://{s}.basemaps.cartocdn.com/light_only_labels/{z}/{x}/{y}.png"
},
"type": "http"
}
]
};
this.testClient = new TestClient(mapConfig);
this.testClient.getTile(0, 0, 0, { format: 'geojson', layer: 0 }, function(err, res, geojson) {
assert.ok(!err, err);
assert.ok(geojson);
assert.equal(geojson.features.length, 5);
assert.deepEqual(Object.keys(geojson.features[0].properties), ['cartodb_id', 'name']);
done();
});
});
});

View File

@@ -106,7 +106,7 @@ describe('render limits', function() {
},
function(res) {
var parsed = JSON.parse(res.body);
assert.deepEqual(parsed, { errors: [ 'Render timed out' ] });
assert.deepEqual(parsed.errors, [ 'Render timed out' ]);
done();
}
);
@@ -171,7 +171,7 @@ describe('render limits', function() {
},
function(res) {
var parsed = JSON.parse(res.body);
assert.deepEqual(parsed, { errors: ['Render timed out'] });
assert.deepEqual(parsed.errors, ['Render timed out']);
done();
}
);

View File

@@ -228,7 +228,7 @@ describe('tests from old api translated to multilayer', function() {
},
function(res) {
var parsed = JSON.parse(res.body);
assert.deepEqual(parsed, { errors: [ 'Unexpected token W' ] });
assert.deepEqual(parsed.errors, [ 'Unexpected token W' ]);
done();
}
@@ -334,9 +334,7 @@ describe('tests from old api translated to multilayer', function() {
assert.ok(!res.headers.hasOwnProperty('x-cache-channel'));
var parsed = JSON.parse(res.body);
assert.deepEqual(parsed, {
errors: ["fake error message"]
});
assert.deepEqual(parsed.errors, ["fake error message"]);
done();
}

View File

@@ -181,7 +181,7 @@ describe('named_layers', function() {
}
var parsedBody = JSON.parse(response.body);
assert.deepEqual(parsedBody, { errors: ["Template 'nonexistent' of user 'localhost' not found"] });
assert.deepEqual(parsedBody.errors, ["Template 'nonexistent' of user 'localhost' not found"]);
return null;
},
@@ -234,10 +234,7 @@ describe('named_layers', function() {
}
var parsedBody = JSON.parse(response.body);
assert.deepEqual(
parsedBody,
{ errors: [ "Unauthorized 'auth_valid_template' template instantiation" ] }
);
assert.deepEqual(parsedBody.errors, [ "Unauthorized 'auth_valid_template' template instantiation" ]);
return null;
},
@@ -347,7 +344,7 @@ describe('named_layers', function() {
}
var parsedBody = JSON.parse(response.body);
assert.deepEqual(parsedBody, { errors: [ 'Nested named layers are not allowed' ] });
assert.deepEqual(parsedBody.errors, ['Nested named layers are not allowed' ]);
return null;
},

View File

@@ -169,8 +169,8 @@ describe('named maps authentication', function() {
getNamedTile(nonexistentName, 0, 0, 0, { status: 404 }, function(err, res) {
assert.ok(!err);
assert.deepEqual(
JSON.parse(res.body),
{ errors: ["Template '" + nonexistentName + "' of user '" + username + "' not found"] }
JSON.parse(res.body).errors,
["Template '" + nonexistentName + "' of user '" + username + "' not found"]
);
done();
});
@@ -179,7 +179,7 @@ describe('named maps authentication', function() {
it('should return 403 if not properly authorized', function(done) {
getNamedTile(tokenAuthTemplateName, 0, 0, 0, { status: 403 }, function(err, res) {
assert.ok(!err);
assert.deepEqual(JSON.parse(res.body), { errors: ['Unauthorized template instantiation'] });
assert.deepEqual(JSON.parse(res.body).errors, ['Unauthorized template instantiation']);
done();
});
});
@@ -238,8 +238,8 @@ describe('named maps authentication', function() {
getStaticMap(nonexistentName, { status: 404 }, function(err, res) {
assert.ok(!err);
assert.deepEqual(
JSON.parse(res.body),
{ errors: ["Template '" + nonexistentName + "' of user '" + username + "' not found"] }
JSON.parse(res.body).errors,
["Template '" + nonexistentName + "' of user '" + username + "' not found"]
);
done();
});
@@ -248,7 +248,7 @@ describe('named maps authentication', function() {
it('should return 403 if not properly authorized', function(done) {
getStaticMap(tokenAuthTemplateName, { status: 403 }, function(err, res) {
assert.ok(!err);
assert.deepEqual(JSON.parse(res.body), { errors: ['Unauthorized template instantiation'] });
assert.deepEqual(JSON.parse(res.body).errors, ['Unauthorized template instantiation']);
done();
});
});

View File

@@ -124,8 +124,8 @@ describe('named maps provider cache', function() {
getNamedTile({ statusCode: 404 }, function(err, res) {
assert.ok(!err);
assert.deepEqual(
JSON.parse(res.body),
{ errors: ["Template 'template_with_color' of user 'localhost' not found"] }
JSON.parse(res.body).errors,
["Template 'template_with_color' of user 'localhost' not found"]
);
// add template again so it's clean in afterEach

View File

@@ -231,7 +231,11 @@ describe('attributes', function() {
assert.equal(res.statusCode, 200, res.statusCode + ': ' + res.body);
assert.equal(
res.body,
'/**/ typeof test === \'function\' && test({"errors":["Layer 0 has no exposed attributes"]});'
'/**/ typeof test === \'function\' && ' +
'test({"errors":["Layer 0 has no exposed attributes"],' +
'"errors_with_context":[{' +
'"type":"unknown","message":"Layer 0 has no exposed attributes","context":"unknown"' +
'}]});'
);
return null;
},

View File

@@ -138,11 +138,9 @@ describe('blend http fallback', function() {
testClient.getTileLayer(mapConfig, tileRequest, expectedResponse, function(err, res) {
assert.ok(!err);
var parsedBody = JSON.parse(res.body);
assert.deepEqual(parsedBody, {
errors: [
"Unable to fetch http tile: http://127.0.0.1:8033/error404/1/0/0.png [404]"
]
});
assert.deepEqual(parsedBody.errors, [
"Unable to fetch http tile: http://127.0.0.1:8033/error404/1/0/0.png [404]"
]);
done();
});
});

View File

@@ -119,12 +119,11 @@ describe('external resources', function() {
var mapConfig = testClient.defaultTableMapConfig('test_table_3', style);
testClient.createLayergroup(mapConfig, { statusCode: 400 }, function(err, res) {
assert.deepEqual(JSON.parse(res.body), {
errors: ["Unable to download '" + url + "' for 'style0' (server returned 404)"]
});
assert.deepEqual(JSON.parse(res.body).errors, [
"Unable to download '" + url + "' for 'style0' (server returned 404)"]
);
done();
});
});
});

View File

@@ -31,7 +31,7 @@ describe('multilayer error cases', function() {
}, {}, function(res) {
assert.equal(res.statusCode, 400, res.body);
var parsedBody = JSON.parse(res.body);
assert.deepEqual(parsedBody, {"errors":["layergroup POST data must be of type application/json"]});
assert.deepEqual(parsedBody.errors, ["layergroup POST data must be of type application/json"]);
done();
});
});
@@ -44,7 +44,7 @@ describe('multilayer error cases', function() {
}, {}, function(res) {
assert.equal(res.statusCode, 400, res.body);
var parsedBody = JSON.parse(res.body);
assert.deepEqual(parsedBody, {"errors":["Missing layers array from layergroup config"]});
assert.deepEqual(parsedBody.errors, ["Missing layers array from layergroup config"]);
done();
});
});
@@ -58,7 +58,10 @@ describe('multilayer error cases', function() {
assert.equal(res.statusCode, 200);
assert.equal(
res.body,
'/**/ typeof test === \'function\' && test({"errors":["Missing layers array from layergroup config"]});'
'/**/ typeof test === \'function\' && ' +
'test({"errors":["Missing layers array from layergroup config"],' +
'"errors_with_context":[{"type":"unknown",' +
'"message":"Missing layers array from layergroup config","context":"unknown"}]});'
);
done();
});
@@ -83,7 +86,7 @@ describe('multilayer error cases', function() {
}, {}, function(res) {
assert.equal(res.statusCode, 400, res.body);
var parsedBody = JSON.parse(res.body);
assert.deepEqual(parsedBody, {errors:["Missing cartocss_version for layer 0 options"]});
assert.deepEqual(parsedBody.errors, ["Missing cartocss_version for layer 0 options"]);
done();
});
});
@@ -355,7 +358,7 @@ describe('multilayer error cases', function() {
var mapConfig = testClient.singleLayerMapConfig('select * from test_table', null, null, 'name');
testClient.getGrid(mapConfig, 1, 13, 4011, 3088, defaultErrorExpectedResponse, function(err, res) {
assert.deepEqual(JSON.parse(res.body), { errors: ["Layer '1' not found in layergroup"] });
assert.deepEqual(JSON.parse(res.body).errors, ["Layer '1' not found in layergroup"]);
done();
});
});
@@ -383,7 +386,7 @@ describe('multilayer error cases', function() {
// FIXME: should be 404
assert.equal(res.statusCode, 400, res.statusCode + ':' + res.body);
var parsed = JSON.parse(res.body);
assert.deepEqual(parsed, {"errors": ["Invalid or nonexistent map configuration token 'deadbeef'"]});
assert.deepEqual(parsed.errors, ["Invalid or nonexistent map configuration token 'deadbeef'"]);
return null;
},
function finish(err) {

View File

@@ -148,11 +148,10 @@ describe('raster', function() {
assert.ok(!err);
checkCORSHeaders(res);
var parsedBody = JSON.parse(res.body);
assert.deepEqual(parsedBody, { errors: [ 'Mapnik raster layers do not support interactivity' ] });
assert.deepEqual(parsedBody.errors, [ 'Mapnik raster layers do not support interactivity' ]);
done();
}
);
});
});

View File

@@ -29,7 +29,7 @@ describe('regressions', function() {
contentType: 'application/json; charset=utf-8'
};
requestTile('/0/0/0.png?testUnexpectedError=1', options, function(err, res) {
assert.deepEqual(JSON.parse(res.body), { "errors": ["test unexpected error"] });
assert.deepEqual(JSON.parse(res.body).errors, ["test unexpected error"]);
finish(done);
});
});

View File

@@ -129,7 +129,7 @@ describe('retina support', function() {
},
function(res, err) {
assert.ok(!err, 'Failed to request 0/0/0' + scaleFactor + '.png tile');
assert.deepEqual(JSON.parse(res.body), { errors: ["Tile with specified resolution not found"] } );
assert.deepEqual(JSON.parse(res.body).errors, ["Tile with specified resolution not found"]);
done();
}

View File

@@ -112,7 +112,7 @@ describe('server', function() {
}
};
testClient.getGrid(mapConfig, 0, 13, 4011, 3088, expectedResponse, function(err, res) {
assert.deepEqual(JSON.parse(res.body), {"errors":["Tileset has no interactivity"]});
assert.deepEqual(JSON.parse(res.body).errors, ["Tileset has no interactivity"]);
done();
});
});

View File

@@ -1927,9 +1927,8 @@ describe('template_api', function() {
if (err) {
return done(err);
}
assert.deepEqual(JSON.parse(res.body), {
errors: ["Invalid or nonexistent map configuration token '" + nonexistentToken + "'"]
});
assert.deepEqual(JSON.parse(res.body).errors,
["Invalid or nonexistent map configuration token '" + nonexistentToken + "'"]);
done();
};

View File

@@ -106,4 +106,34 @@ describe('turbo-carto error cases', function() {
done();
});
});
it('turbo-carto: should return error invalid column from datasource with some context', function(done) {
this.testClient = new TestClient(makeMapconfig(null, 'ramp([wadus_column], (red, green, blue))'));
this.testClient.getLayergroup(ERROR_RESPONSE, function(err, layergroup) {
assert.ok(!err, err);
assert.ok(layergroup.hasOwnProperty('errors'));
assert.equal(layergroup.errors_with_context.length, 1);
assert.equal(layergroup.errors_with_context[0].type, 'turbo-carto');
assert.ok(layergroup.errors_with_context[0].message.match(/^turbo-carto/));
assert.ok(layergroup.errors_with_context[0].message.match(/unable\sto\scompute\sramp/i));
assert.ok(layergroup.errors_with_context[0].message.match(/wadus_column/));
assert.equal(layergroup.errors_with_context[0].context.layer.index, 0);
assert.equal(layergroup.errors_with_context[0].context.layer.type, 'mapnik');
assert.equal(layergroup.errors_with_context[0].context.selector, '#populated_places_simple_reduced');
assert.deepEqual(layergroup.errors_with_context[0].context.source, {
start: {
line: 10,
column: 3
},
end: {
line: 10,
column: 56
}
});
done();
});
});
});

View File

@@ -0,0 +1,223 @@
require('../../support/test_helper');
var assert = require('../../support/assert');
var TestClient = require('../../support/test-client');
describe('widgets-regressions', function() {
describe('aggregations', function() {
afterEach(function(done) {
if (this.testClient) {
this.testClient.drain(done);
} else {
done();
}
});
it('should work when there is a mix of layers with and without widgets', function(done) {
var layersWithNoWidgetsMapConfig = {
version: '1.5.0',
layers: [
{
type: 'mapnik',
options: {
sql: 'select * from populated_places_simple_reduced',
cartocss: '#layer0 { marker-fill: red; marker-width: 10; }',
cartocss_version: '2.0.1',
widgets: {
adm0name: {
type: 'aggregation',
options: {
column: 'adm0name',
aggregation: 'sum',
aggregationColumn: 'pop_max'
}
}
}
}
},
{
type: 'mapnik',
options: {
sql: 'select * from populated_places_simple_reduced limit 100',
cartocss: '#layer0 { marker-fill: red; marker-width: 10; }',
cartocss_version: '2.0.1'
}
}
]
};
this.testClient = new TestClient(layersWithNoWidgetsMapConfig);
this.testClient.getWidget('adm0name', { own_filter: 0 }, function (err, res, aggregation) {
assert.ok(!err, err);
assert.ok(aggregation);
assert.equal(aggregation.type, 'aggregation');
assert.equal(aggregation.categories.length, 6);
assert.deepEqual(
aggregation.categories[0],
{ category: 'China', value: 374537585, agg: false }
);
assert.deepEqual(
aggregation.categories[aggregation.categories.length - 1],
{ category: 'Other', value: 1412626289, agg: true }
);
done();
});
});
it('should work when there is a mix of layers with and without widgets, source and sql', function(done) {
var mixOfLayersMapConfig = {
version: '1.5.0',
layers: [
{
type: 'mapnik',
options: {
sql: 'select * from populated_places_simple_reduced',
cartocss: '#layer0 { marker-fill: red; marker-width: 10; }',
cartocss_version: '2.0.1',
widgets: {
adm0name_categories: {
type: 'aggregation',
options: {
column: 'adm0name',
aggregation: 'sum',
aggregationColumn: 'pop_max'
}
},
adm1name_categories: {
type: 'aggregation',
options: {
column: 'adm1name',
aggregation: 'sum',
aggregationColumn: 'pop_max'
}
}
}
}
},
{
type: 'mapnik',
options: {
source: {id: 'head-limited'},
cartocss: '#layer0 { marker-fill: red; marker-width: 10; }',
cartocss_version: '2.0.1',
widgets: {
pop_max_histogram: {
type: 'histogram',
options: {
column: 'pop_max'
}
}
}
}
},
{
"type": "http",
"options": {
"urlTemplate": "http://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png",
"subdomains": "abcd"
}
}
],
analyses: [
{
id: 'head-limited',
type: 'source',
params: {
query: 'select * from populated_places_simple_reduced limit 100'
}
}
],
dataviews: {
wadus: {
type: 'histogram',
source: {
id: 'head-limited'
},
options: {
column: 'population'
}
}
}
};
this.testClient = new TestClient(mixOfLayersMapConfig);
this.testClient.getLayergroup(function(err, layergroup) {
assert.ok(!err, err);
assert.ok(layergroup.metadata);
var metadata = layergroup.metadata;
assert.equal(metadata.layers.length, 3);
assert.equal(metadata.analyses.length, 2);
assert.equal(Object.keys(metadata.dataviews).length, 4);
assert.deepEqual(
Object.keys(metadata.dataviews),
['wadus', 'adm0name_categories', 'adm1name_categories', 'pop_max_histogram']
);
done();
});
});
it('should work with layers not containing sql', function(done) {
var nonSqlLayersMapConfig = {
version: '1.5.0',
layers: [
{
type: 'mapnik',
options: {
sql: 'select * from populated_places_simple_reduced',
cartocss: '#layer0 { marker-fill: red; marker-width: 10; }',
cartocss_version: '2.0.1',
widgets: {
adm0name: {
type: 'aggregation',
options: {
column: 'adm0name',
aggregation: 'sum',
aggregationColumn: 'pop_max'
}
}
}
}
},
{
"type": "http",
"options": {
"urlTemplate": "http://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png",
"subdomains": "abcd"
}
}
]
};
this.testClient = new TestClient(nonSqlLayersMapConfig);
this.testClient.getWidget('adm0name', { own_filter: 0 }, function (err, res, aggregation) {
assert.ok(!err, err);
assert.ok(aggregation);
assert.equal(aggregation.type, 'aggregation');
assert.equal(aggregation.categories.length, 6);
assert.deepEqual(
aggregation.categories[0],
{ category: 'China', value: 374537585, agg: false }
);
assert.deepEqual(
aggregation.categories[aggregation.categories.length - 1],
{ category: 'Other', value: 1412626289, agg: true }
);
done();
});
});
});
});

View File

@@ -310,9 +310,13 @@ TestClient.prototype.getDataview = function(dataviewName, params, callback) {
var urlParams = {
own_filter: params.hasOwnProperty('own_filter') ? params.own_filter : 1
};
if (params && params.bbox) {
urlParams.bbox = params.bbox;
}
['bbox', 'bins', 'start', 'end'].forEach(function(extraParam) {
if (params.hasOwnProperty(extraParam)) {
urlParams[extraParam] = params[extraParam];
}
});
if (self.apiKey) {
urlParams.api_key = self.apiKey;
}
@@ -341,10 +345,10 @@ TestClient.prototype.getDataview = function(dataviewName, params, callback) {
}
);
},
function finish(err, res) {
function finish(err, dataview) {
self.keysToDelete['map_cfg|' + LayergroupToken.parse(layergroupId).token] = 0;
self.keysToDelete['user:localhost:mapviews:global'] = 5;
return callback(err, res);
return callback(err, dataview);
}
);
};