Compare commits
87 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2143e87401 | ||
|
|
f0a536ee1e | ||
|
|
dde4b63c6b | ||
|
|
0e7bcc4b56 | ||
|
|
e4816b4322 | ||
|
|
af4f29c538 | ||
|
|
7cedccedcd | ||
|
|
a9c12d4534 | ||
|
|
77f71b1978 | ||
|
|
1c029fbc7b | ||
|
|
2bc0d8d145 | ||
|
|
4c2af88f92 | ||
|
|
ddd5d2a0b0 | ||
|
|
d5cb59dc84 | ||
|
|
f21581630a | ||
|
|
3fef37d06b | ||
|
|
a8b93896ed | ||
|
|
6c1e9bf0ca | ||
|
|
1d8947d404 | ||
|
|
834377b342 | ||
|
|
c2e0eb05e5 | ||
|
|
256032ca4a | ||
|
|
d80f2b9566 | ||
|
|
9e2f0371ba | ||
|
|
a2e74a3e1b | ||
|
|
f04a5a1ab9 | ||
|
|
7114311b75 | ||
|
|
1e9e092dc3 | ||
|
|
98b3a5ba23 | ||
|
|
200966e806 | ||
|
|
407430b81e | ||
|
|
c4b1fc039c | ||
|
|
d00379af6b | ||
|
|
7cee0f3ee3 | ||
|
|
2588346f1b | ||
|
|
863128013d | ||
|
|
51eb8eb67f | ||
|
|
28e01fd8ac | ||
|
|
726e153ad5 | ||
|
|
e8df09c85b | ||
|
|
2b4fb2971d | ||
|
|
933b36cca0 | ||
|
|
37a7cfb6ba | ||
|
|
8a1cda159c | ||
|
|
403dcbebcd | ||
|
|
373ad69306 | ||
|
|
b2029e09f5 | ||
|
|
4f37d2d0c2 | ||
|
|
a5b07bc2a8 | ||
|
|
1544a5622d | ||
|
|
d49a877771 | ||
|
|
0f4747743c | ||
|
|
368e4522e7 | ||
|
|
cb1d1bb115 | ||
|
|
612cc3dd41 | ||
|
|
bff082e577 | ||
|
|
ea41750a14 | ||
|
|
458376a665 | ||
|
|
f0284907c4 | ||
|
|
ad0385ccf7 | ||
|
|
4350fc3c65 | ||
|
|
fc3422b9e5 | ||
|
|
9703c19fb4 | ||
|
|
d36f2fb354 | ||
|
|
c837785314 | ||
|
|
33014a9f45 | ||
|
|
c16d0b8605 | ||
|
|
c88e4c5173 | ||
|
|
0540696c3e | ||
|
|
5f59a97a02 | ||
|
|
d3b815c3c7 | ||
|
|
d2a8dcbede | ||
|
|
4854e879a6 | ||
|
|
ddc99cebff | ||
|
|
47470d4f2b | ||
|
|
d9297d54de | ||
|
|
2d821f957e | ||
|
|
c0ce6e7a8a | ||
|
|
3f620c6cdd | ||
|
|
9160d8018d | ||
|
|
51307bcc69 | ||
|
|
d29651ee80 | ||
|
|
18640077aa | ||
|
|
59563c893b | ||
|
|
90fd1786e1 | ||
|
|
4a11115dd0 | ||
|
|
baf3e774c5 |
103
NEWS.md
103
NEWS.md
@@ -1,5 +1,108 @@
|
||||
# Changelog
|
||||
|
||||
## 2.53.2
|
||||
|
||||
Released 2016-06-28
|
||||
|
||||
Announcements:
|
||||
- Upgrades camshaft to [0.22.2](https://github.com/CartoDB/camshaft/releases/tag/0.22.2)
|
||||
|
||||
|
||||
## 2.53.1
|
||||
|
||||
Released 2016-06-28
|
||||
|
||||
Announcements:
|
||||
- Upgrades camshaft to [0.22.1](https://github.com/CartoDB/camshaft/releases/tag/0.22.1)
|
||||
|
||||
|
||||
## 2.53.0
|
||||
|
||||
Released 2016-06-24
|
||||
|
||||
Announcements:
|
||||
- Upgrades camshaft to [0.22.0](https://github.com/CartoDB/camshaft/releases/tag/0.22.0)
|
||||
|
||||
|
||||
## 2.52.0
|
||||
|
||||
Released 2016-06-23
|
||||
|
||||
Announcements:
|
||||
- Upgrades camshaft to [0.21.0](https://github.com/CartoDB/camshaft/releases/tag/0.21.0)
|
||||
|
||||
|
||||
## 2.51.0
|
||||
|
||||
Released 2016-06-21
|
||||
|
||||
Enhancements:
|
||||
- Split turbo-carto adapter substitutions tokens query.
|
||||
- Now errors with context have the same schema. #519
|
||||
|
||||
Announcements:
|
||||
- Upgrades camshaft to [0.20.0](https://github.com/CartoDB/camshaft/releases/tag/0.20.0)
|
||||
|
||||
|
||||
## 2.50.0
|
||||
|
||||
Released 2016-06-21
|
||||
|
||||
Bug fixes:
|
||||
- Pixel size query for turbo-carto adapter using radians and degrees instead of meters.
|
||||
|
||||
New features:
|
||||
- Add support for min, max, and avg operations in aggregation dataview #513.
|
||||
|
||||
Announcements:
|
||||
- Upgrades camshaft to [0.19.0](https://github.com/CartoDB/camshaft/releases/tag/0.19.0)
|
||||
|
||||
|
||||
## 2.49.1
|
||||
|
||||
Released 2016-06-20
|
||||
|
||||
Announcements:
|
||||
- Upgrades turbo-carto to [0.12.1](https://github.com/CartoDB/turbo-carto/releases/tag/0.12.1).
|
||||
|
||||
Bug fixes:
|
||||
- Use an empty array as default value for falsy ramps #512.
|
||||
- Use the_geom for intermediate dataviews #511.
|
||||
- Pick last update time for layergroupid from analyses results #510.
|
||||
|
||||
|
||||
## 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
|
||||
|
||||
@@ -16,6 +16,9 @@ var overviewsQueryRewriter = new OverviewsQueryRewriter({
|
||||
zoom_level: 'CDB_ZoomFromScale(!scale_denominator!)'
|
||||
});
|
||||
|
||||
var dot = require('dot');
|
||||
dot.templateSettings.strip = false;
|
||||
|
||||
function DataviewBackend(analysisBackend) {
|
||||
this.analysisBackend = analysisBackend;
|
||||
}
|
||||
@@ -105,15 +108,7 @@ DataviewBackend.prototype.getDataview = function (mapConfigProvider, user, param
|
||||
var ownFilter = +params.own_filter;
|
||||
ownFilter = !!ownFilter;
|
||||
|
||||
var query;
|
||||
|
||||
if (ownFilter) {
|
||||
query = node.getQuery();
|
||||
} else {
|
||||
var applyFilters = {};
|
||||
applyFilters[dataviewName] = false;
|
||||
query = node.getQuery(applyFilters);
|
||||
}
|
||||
var query = layerQuery(node, dataviewName, ownFilter);
|
||||
|
||||
var sourceId = dataviewDefinition.source.id; // node.id
|
||||
var layer = _.find(
|
||||
@@ -260,14 +255,7 @@ DataviewBackend.prototype.search = function (mapConfigProvider, user, params, ca
|
||||
var ownFilter = +params.own_filter;
|
||||
ownFilter = !!ownFilter;
|
||||
|
||||
var query;
|
||||
if (ownFilter) {
|
||||
query = node.getQuery();
|
||||
} else {
|
||||
var applyFilters = {};
|
||||
applyFilters[dataviewName] = false;
|
||||
query = node.getQuery(applyFilters);
|
||||
}
|
||||
var query = layerQuery(node, dataviewName, ownFilter);
|
||||
|
||||
if (params.bbox) {
|
||||
var bboxFilter = new BBoxFilter({column: 'the_geom', srid: 4326}, {bbox: params.bbox});
|
||||
@@ -321,3 +309,31 @@ function dbParamsFromReqParams(params) {
|
||||
}
|
||||
return dbParams;
|
||||
}
|
||||
|
||||
var SKIP_COLUMNS = {
|
||||
'the_geom': true,
|
||||
'the_geom_webmercator': true
|
||||
};
|
||||
|
||||
function skipColumns(columnNames) {
|
||||
return columnNames
|
||||
.filter(function(columnName) { return !SKIP_COLUMNS[columnName]; });
|
||||
}
|
||||
|
||||
var layerQueryTemplate = dot.template([
|
||||
'SELECT {{=it._columns}}',
|
||||
'FROM ({{=it._query}}) _cdb_analysis_query'
|
||||
].join('\n'));
|
||||
|
||||
function layerQuery(node, dataviewName, ownFilter) {
|
||||
var applyFilters = {};
|
||||
if (!ownFilter) {
|
||||
applyFilters[dataviewName] = false;
|
||||
}
|
||||
|
||||
if (node.type === 'source') {
|
||||
return node.getQuery(applyFilters);
|
||||
}
|
||||
var _columns = ['ST_Transform(the_geom, 3857) the_geom_webmercator'].concat(skipColumns(node.getColumns()));
|
||||
return layerQueryTemplate({ _query: node.getQuery(applyFilters), _columns: _columns.join(', ') });
|
||||
}
|
||||
|
||||
@@ -54,9 +54,8 @@ var method2strategy = {
|
||||
category: STRATEGY.EXACT
|
||||
};
|
||||
|
||||
function PostgresDatasource (pgQueryRunner, username, query) {
|
||||
this.pgQueryRunner = pgQueryRunner;
|
||||
this.username = username;
|
||||
function PostgresDatasource (psql, query) {
|
||||
this.psql = psql;
|
||||
this.query = query;
|
||||
}
|
||||
|
||||
@@ -75,13 +74,16 @@ PostgresDatasource.prototype.getRamp = function (column, buckets, method, callba
|
||||
|
||||
var query = template({ _column: column, _sql: this.query, _buckets: buckets });
|
||||
|
||||
this.pgQueryRunner.run(this.username, query, function (err, result) {
|
||||
this.psql.query(query, function (err, resultSet) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
resultSet = resultSet || {};
|
||||
var result = resultSet.rows || [];
|
||||
|
||||
var strategy = method2strategy[methodName];
|
||||
var ramp = result[0][methodName];
|
||||
var ramp = result[0][methodName] || [];
|
||||
if (strategy !== STRATEGY.EXACT) {
|
||||
ramp = ramp.sort(function(a, b) {
|
||||
return a - b;
|
||||
@@ -89,7 +91,7 @@ PostgresDatasource.prototype.getRamp = function (column, buckets, method, callba
|
||||
}
|
||||
|
||||
return callback(null, { ramp: ramp, strategy: strategy });
|
||||
});
|
||||
}, true); // use read-only transaction
|
||||
};
|
||||
|
||||
module.exports = PostgresDatasource;
|
||||
@@ -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) {
|
||||
|
||||
@@ -161,7 +161,7 @@ MapController.prototype.create = function(req, res, prepareConfigFn) {
|
||||
},
|
||||
function afterLayergroupCreate(err, layergroup) {
|
||||
assert.ifError(err);
|
||||
self.afterLayergroupCreate(req, res, mapConfig, layergroup, this);
|
||||
self.afterLayergroupCreate(req, res, mapConfig, layergroup, context.analysesResults, this);
|
||||
},
|
||||
function finish(err, layergroup) {
|
||||
if (err) {
|
||||
@@ -219,7 +219,7 @@ MapController.prototype.instantiateTemplate = function(req, res, prepareParamsFn
|
||||
},
|
||||
function afterLayergroupCreate(err, layergroup) {
|
||||
assert.ifError(err);
|
||||
self.afterLayergroupCreate(req, res, mapConfig, layergroup, this);
|
||||
self.afterLayergroupCreate(req, res, mapConfig, layergroup, mapConfigProvider.analysesResults, this);
|
||||
},
|
||||
function finishTemplateInstantiation(err, layergroup) {
|
||||
if (err) {
|
||||
@@ -240,7 +240,7 @@ MapController.prototype.instantiateTemplate = function(req, res, prepareParamsFn
|
||||
);
|
||||
};
|
||||
|
||||
MapController.prototype.afterLayergroupCreate = function(req, res, mapconfig, layergroup, callback) {
|
||||
MapController.prototype.afterLayergroupCreate = function(req, res, mapconfig, layergroup, analysesResults, callback) {
|
||||
var self = this;
|
||||
|
||||
var username = req.context.user;
|
||||
@@ -299,9 +299,12 @@ MapController.prototype.afterLayergroupCreate = function(req, res, mapconfig, la
|
||||
// feed affected tables cache so it can be reused from, for instance, layergroup controller
|
||||
self.layergroupAffectedTables.set(dbName, layergroupId, result);
|
||||
|
||||
var lastUpdateTime = result.getLastUpdatedAt();
|
||||
lastUpdateTime = getLastUpdatedTime(analysesResults, lastUpdateTime) || lastUpdateTime;
|
||||
|
||||
// last update for layergroup cache buster
|
||||
layergroup.layergroupid = layergroup.layergroupid + ':' + result.getLastUpdatedAt();
|
||||
layergroup.last_updated = new Date(result.getLastUpdatedAt()).toISOString();
|
||||
layergroup.layergroupid = layergroup.layergroupid + ':' + lastUpdateTime;
|
||||
layergroup.last_updated = new Date(lastUpdateTime).toISOString();
|
||||
|
||||
if (req.method === 'GET') {
|
||||
var ttl = global.environment.varnish.layergroupTtl || 86400;
|
||||
@@ -321,6 +324,19 @@ MapController.prototype.afterLayergroupCreate = function(req, res, mapconfig, la
|
||||
);
|
||||
};
|
||||
|
||||
function getLastUpdatedTime(analysesResults, lastUpdateTime) {
|
||||
if (!Array.isArray(analysesResults)) {
|
||||
return lastUpdateTime;
|
||||
}
|
||||
return analysesResults.reduce(function(lastUpdateTime, analysis) {
|
||||
return analysis.getSortedNodes().reduce(function(lastNodeUpdatedAtTime, node) {
|
||||
var nodeUpdatedAtDate = node.getUpdatedAt();
|
||||
var nodeUpdatedTimeAt = (nodeUpdatedAtDate && nodeUpdatedAtDate.getTime()) || 0;
|
||||
return nodeUpdatedTimeAt > lastNodeUpdatedAtTime ? nodeUpdatedTimeAt : lastNodeUpdatedAtTime;
|
||||
}, lastUpdateTime);
|
||||
}, lastUpdateTime);
|
||||
}
|
||||
|
||||
function addAnalysesMetadata(username, layergroup, analysesResults, includeQuery) {
|
||||
includeQuery = includeQuery || false;
|
||||
analysesResults = analysesResults || [];
|
||||
|
||||
@@ -54,7 +54,10 @@ var CATEGORIES_LIMIT = 6;
|
||||
|
||||
var VALID_OPERATIONS = {
|
||||
count: [],
|
||||
sum: ['aggregationColumn']
|
||||
sum: ['aggregationColumn'],
|
||||
avg: ['aggregationColumn'],
|
||||
min: ['aggregationColumn'],
|
||||
max: ['aggregationColumn']
|
||||
};
|
||||
|
||||
var TYPE = 'aggregation';
|
||||
@@ -198,6 +201,7 @@ Aggregation.prototype.format = function(result) {
|
||||
}
|
||||
|
||||
return {
|
||||
aggregation: this.aggregation,
|
||||
count: count,
|
||||
nulls: nulls,
|
||||
min: minValue,
|
||||
|
||||
@@ -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) {
|
||||
var error = new Error(err.message);
|
||||
error.type = 'analysis';
|
||||
error.context = {};
|
||||
error.context.layer = {
|
||||
index: index,
|
||||
id: analysisDefinition.id,
|
||||
type: analysisDefinition.type
|
||||
};
|
||||
return done(error);
|
||||
}
|
||||
|
||||
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) {
|
||||
@@ -111,10 +125,12 @@ AnalysisMapConfigAdapter.prototype.getMapConfig = function(user, requestMapConfi
|
||||
return layer;
|
||||
});
|
||||
|
||||
|
||||
debug('mapconfig output', JSON.stringify(requestMapConfig, null, 4));
|
||||
|
||||
if (missingNodesErrors.length > 0) {
|
||||
return callback(missingNodesErrors);
|
||||
var missingDataviewsNodesErrors = getMissingDataviewsSourceIds(dataviews, sourceId2Node);
|
||||
if (missingNodesErrors.length > 0 || missingDataviewsNodesErrors.length > 0) {
|
||||
return callback(missingNodesErrors.concat(missingDataviewsNodesErrors));
|
||||
}
|
||||
|
||||
context.analysesResults = analysesResults;
|
||||
@@ -163,8 +179,9 @@ function appendFiltersToNodes(requestMapConfig, dataviewsFiltersBySourceId) {
|
||||
}
|
||||
|
||||
function shouldAdaptLayers(requestMapConfig) {
|
||||
return Array.isArray(requestMapConfig.layers) &&
|
||||
Array.isArray(requestMapConfig.analyses) && requestMapConfig.analyses.length > 0;
|
||||
return Array.isArray(requestMapConfig.layers) && requestMapConfig.layers.some(getLayerSourceId) ||
|
||||
(Array.isArray(requestMapConfig.analyses) && requestMapConfig.analyses.length > 0) ||
|
||||
requestMapConfig.dataviews;
|
||||
}
|
||||
|
||||
var DATAVIEW_TYPE_2_FILTER_TYPE = {
|
||||
@@ -232,6 +249,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) {
|
||||
@@ -247,3 +273,26 @@ function getDataviewsErrors(dataviews) {
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
function getMissingDataviewsSourceIds(dataviews, sourceId2Node) {
|
||||
var missingDataviewsSourceIds = [];
|
||||
Object.keys(dataviews).forEach(function(dataviewName) {
|
||||
var dataview = dataviews[dataviewName];
|
||||
var dataviewSourceId = getDataviewSourceId(dataview);
|
||||
if (!sourceId2Node.hasOwnProperty(dataviewSourceId)) {
|
||||
missingDataviewsSourceIds.push(new AnalysisError('Node with `source.id="' + dataviewSourceId +'"`' +
|
||||
' not found in analyses for dataview "' + dataviewName + '"'));
|
||||
}
|
||||
});
|
||||
|
||||
return missingDataviewsSourceIds;
|
||||
}
|
||||
|
||||
function AnalysisError(message) {
|
||||
Error.captureStackTrace(this, this.constructor);
|
||||
this.name = this.constructor.name;
|
||||
this.type = 'analysis';
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
require('util').inherits(AnalysisError, Error);
|
||||
|
||||
@@ -3,10 +3,13 @@
|
||||
var dot = require('dot');
|
||||
dot.templateSettings.strip = false;
|
||||
var queue = require('queue-async');
|
||||
var SubstitutionTokens = require('../../../utils/substitution-tokens');
|
||||
var PSQL = require('cartodb-psql');
|
||||
var turboCarto = require('turbo-carto');
|
||||
|
||||
function TurboCartoAdapter(turboCartoParser) {
|
||||
this.turboCartoParser = turboCartoParser;
|
||||
var SubstitutionTokens = require('../../../utils/substitution-tokens');
|
||||
var PostgresDatasource = require('../../../backends/turbo-carto-postgres-datasource');
|
||||
|
||||
function TurboCartoAdapter() {
|
||||
}
|
||||
|
||||
module.exports = TurboCartoAdapter;
|
||||
@@ -22,8 +25,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, params, layer, index);
|
||||
});
|
||||
|
||||
parseCartoQueue.awaitAll(function (err, layers) {
|
||||
@@ -37,25 +40,87 @@ TurboCartoAdapter.prototype.getMapConfig = function (user, requestMapConfig, par
|
||||
});
|
||||
};
|
||||
|
||||
var bboxTemplate = dot.template('(select ST_SetSRID(st_extent(the_geom_webmercator), 3857) from ({{=it._sql}}) __c)');
|
||||
var zoomTemplate = dot.template([
|
||||
'GREATEST(',
|
||||
'ceil(log(40075017000 / 256 / GREATEST(',
|
||||
' st_xmax({{=it._bbox}}) - st_xmin({{=it._bbox}}),',
|
||||
' st_ymax({{=it._bbox}}) - st_ymin({{=it._bbox}})',
|
||||
'))/log(2)),',
|
||||
'0',
|
||||
')'
|
||||
var tokensQueryTpl = dot.template([
|
||||
'WITH input_query AS (',
|
||||
' {{=it._sql}}',
|
||||
'),',
|
||||
'bbox_query AS (',
|
||||
' SELECT ST_SetSRID(ST_Extent(the_geom_webmercator), 3857) as bbox from input_query',
|
||||
'),',
|
||||
'zoom_query as (',
|
||||
' SELECT GREATEST(',
|
||||
' ceil(log(40075017000 / 256 / GREATEST(ST_XMax(bbox) - ST_XMin(bbox), ST_YMax(bbox) - ST_YMin(bbox)))/log(2)),',
|
||||
' 0) as zoom',
|
||||
' FROM bbox_query',
|
||||
'),',
|
||||
'pixel_size_query as (',
|
||||
' SELECT 40075017 * cos(radians(ST_Y(ST_Transform(ST_Centroid(bbox), 4326)))) / 2 ^ ((zoom) + 8) as pixel_size',
|
||||
' FROM bbox_query, zoom_query',
|
||||
'),',
|
||||
'scale_denominator_query as (',
|
||||
' SELECT (pixel_size / 0.00028)::numeric as scale_denominator',
|
||||
' FROM pixel_size_query',
|
||||
')',
|
||||
'select ST_AsText(bbox) bbox, pixel_size, scale_denominator, zoom',
|
||||
'from bbox_query, pixel_size_query, scale_denominator_query, zoom_query'
|
||||
].join('\n'));
|
||||
var pixelSizeTemplate = dot.template('40075017 * cos(ST_Y(ST_Centroid({{=it._bbox}}))) / 2 ^ (({{=it._zoom}}) + 8)');
|
||||
var scaleDenominatorTemplate = dot.template('({{=it._pixelSize}} / 0.00028)::numeric');
|
||||
|
||||
|
||||
TurboCartoAdapter.prototype._parseCartoCss = function (username, layer, callback) {
|
||||
TurboCartoAdapter.prototype._parseCartoCss = function (username, params, layer, index, callback) {
|
||||
if (!shouldParseLayerCartocss(layer)) {
|
||||
return callback(null, layer);
|
||||
}
|
||||
|
||||
var pg = new PSQL(dbParamsFromReqParams(params));
|
||||
function processCallback(err, cartocss) {
|
||||
// Only return turbo-carto errors
|
||||
if (err && err.name === 'TurboCartoError') {
|
||||
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
|
||||
if (cartocss) {
|
||||
layer.options.cartocss = cartocss;
|
||||
}
|
||||
return callback(null, layer);
|
||||
}
|
||||
|
||||
var layerSql = layer.options.sql;
|
||||
var layerRawSql = layer.options.sql_raw;
|
||||
if (SubstitutionTokens.hasTokens(layerSql) && layerRawSql) {
|
||||
var self = this;
|
||||
var tokensQuery = tokensQueryTpl({_sql: layerRawSql});
|
||||
return pg.query(tokensQuery, function(err, resultSet) {
|
||||
if (err) {
|
||||
var error = new Error('turbo-carto: ' + err.message);
|
||||
error.type = 'turbo-carto';
|
||||
return callback(error);
|
||||
}
|
||||
|
||||
resultSet = resultSet || {};
|
||||
var rows = resultSet.rows || [];
|
||||
var result = rows[0] || {};
|
||||
|
||||
var tokens = {
|
||||
bbox: 'ST_SetSRID(ST_GeomFromText(\'' + result.bbox + '\'), 3857)',
|
||||
scale_denominator: result.scale_denominator,
|
||||
pixel_width: result.pixel_size,
|
||||
pixel_height: result.pixel_size
|
||||
};
|
||||
|
||||
var sql = SubstitutionTokens.replace(layerSql, tokens);
|
||||
self.process(pg, layer.options.cartocss, sql, processCallback);
|
||||
}, true); // use read-only transaction
|
||||
}
|
||||
|
||||
var tokens = {
|
||||
bbox: 'ST_MakeEnvelope(-20037508.34,-20037508.34,20037508.34,20037508.34,3857)',
|
||||
scale_denominator: '500000001',
|
||||
@@ -63,40 +128,35 @@ TurboCartoAdapter.prototype._parseCartoCss = function (username, layer, callback
|
||||
pixel_height: '156412'
|
||||
};
|
||||
|
||||
var layerSql = layer.options.sql;
|
||||
var layerRawSql = layer.options.sql_raw;
|
||||
if (SubstitutionTokens.hasTokens(layerSql) && layerRawSql) {
|
||||
var bbox = bboxTemplate({ _sql: layerRawSql });
|
||||
var zoom = zoomTemplate({ _bbox: bbox });
|
||||
var pixelSize = pixelSizeTemplate({ _bbox: bbox, _zoom: zoom });
|
||||
var scaleDenominator = scaleDenominatorTemplate({ _pixelSize: pixelSize });
|
||||
|
||||
tokens = {
|
||||
bbox: bbox,
|
||||
scale_denominator: scaleDenominator,
|
||||
pixel_width: pixelSize,
|
||||
pixel_height: pixelSize
|
||||
};
|
||||
}
|
||||
|
||||
var sql = SubstitutionTokens.replace(layerSql, tokens);
|
||||
this.process(pg, layer.options.cartocss, sql, processCallback);
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// Try to continue in the rest of the cases
|
||||
if (cartocss) {
|
||||
layer.options.cartocss = cartocss;
|
||||
}
|
||||
return callback(null, layer);
|
||||
});
|
||||
TurboCartoAdapter.prototype.process = function (psql, cartocss, sql, callback) {
|
||||
var datasource = new PostgresDatasource(psql, sql);
|
||||
turboCarto(cartocss, datasource, callback);
|
||||
};
|
||||
|
||||
function shouldParseLayerCartocss(layer) {
|
||||
return layer && layer.options && layer.options.cartocss && layer.options.sql;
|
||||
}
|
||||
|
||||
function dbParamsFromReqParams(params) {
|
||||
var dbParams = {};
|
||||
if ( params.dbuser ) {
|
||||
dbParams.user = params.dbuser;
|
||||
}
|
||||
if ( params.dbpassword ) {
|
||||
dbParams.pass = params.dbpassword;
|
||||
}
|
||||
if ( params.dbhost ) {
|
||||
dbParams.host = params.dbhost;
|
||||
}
|
||||
if ( params.dbport ) {
|
||||
dbParams.port = params.dbport;
|
||||
}
|
||||
if ( params.dbname ) {
|
||||
dbParams.dbname = params.dbname;
|
||||
}
|
||||
return dbParams;
|
||||
}
|
||||
|
||||
@@ -33,8 +33,6 @@ var AnalysisBackend = require('./backends/analysis');
|
||||
var timeoutErrorTilePath = __dirname + '/../../assets/render-timeout-fallback.png';
|
||||
var timeoutErrorTile = require('fs').readFileSync(timeoutErrorTilePath, {encoding: null});
|
||||
|
||||
var TurboCartoParser = require('./utils/style/turbo-carto-parser');
|
||||
|
||||
var SqlWrapMapConfigAdapter = require('./models/mapconfig/adapter/sql-wrap-mapconfig-adapter');
|
||||
var MapConfigNamedLayersAdapter = require('./models/mapconfig/adapter/mapconfig-named-layers-adapter');
|
||||
var AnalysisMapConfigAdapter = require('./models/mapconfig/adapter/analysis-mapconfig-adapter');
|
||||
@@ -154,15 +152,13 @@ module.exports = function(serverOptions) {
|
||||
var layergroupAffectedTablesCache = new LayergroupAffectedTablesCache();
|
||||
app.layergroupAffectedTablesCache = layergroupAffectedTablesCache;
|
||||
|
||||
var turboCartoParser = new TurboCartoParser(pgQueryRunner);
|
||||
|
||||
var mapConfigAdapter = new MapConfigAdapter(
|
||||
new MapConfigNamedLayersAdapter(templateMaps, pgConnection),
|
||||
new SqlWrapMapConfigAdapter(),
|
||||
new DataviewsWidgetsAdapter(),
|
||||
new AnalysisMapConfigAdapter(analysisBackend),
|
||||
new MapConfigOverviewsAdapter(overviewsMetadataApi, filterStatsApi),
|
||||
new TurboCartoAdapter(turboCartoParser)
|
||||
new TurboCartoAdapter()
|
||||
);
|
||||
|
||||
var namedMapProviderCache = new NamedMapProviderCache(
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
var turboCarto = require('turbo-carto');
|
||||
var PostgresDatasource = require('./postgres-datasource');
|
||||
|
||||
function TurboCartoParser (pgQueryRunner) {
|
||||
this.pgQueryRunner = pgQueryRunner;
|
||||
}
|
||||
|
||||
module.exports = TurboCartoParser;
|
||||
|
||||
TurboCartoParser.prototype.process = function (username, cartocss, sql, callback) {
|
||||
var datasource = new PostgresDatasource(this.pgQueryRunner, username, sql);
|
||||
turboCarto(cartocss, datasource, callback);
|
||||
};
|
||||
190
npm-shrinkwrap.json
generated
190
npm-shrinkwrap.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "windshaft-cartodb",
|
||||
"version": "2.46.0",
|
||||
"version": "2.53.2",
|
||||
"dependencies": {
|
||||
"body-parser": {
|
||||
"version": "1.14.2",
|
||||
@@ -62,14 +62,14 @@
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-5.2.0.tgz"
|
||||
},
|
||||
"raw-body": {
|
||||
"version": "2.1.6",
|
||||
"version": "2.1.7",
|
||||
"from": "raw-body@>=2.1.5 <2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.1.6.tgz",
|
||||
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.1.7.tgz",
|
||||
"dependencies": {
|
||||
"bytes": {
|
||||
"version": "2.3.0",
|
||||
"from": "bytes@2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-2.3.0.tgz"
|
||||
"version": "2.4.0",
|
||||
"from": "bytes@2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-2.4.0.tgz"
|
||||
},
|
||||
"unpipe": {
|
||||
"version": "1.0.0",
|
||||
@@ -105,9 +105,9 @@
|
||||
}
|
||||
},
|
||||
"camshaft": {
|
||||
"version": "0.13.0",
|
||||
"from": "camshaft@0.13.0",
|
||||
"resolved": "https://registry.npmjs.org/camshaft/-/camshaft-0.13.0.tgz",
|
||||
"version": "0.22.2",
|
||||
"from": "camshaft@0.22.2",
|
||||
"resolved": "https://registry.npmjs.org/camshaft/-/camshaft-0.22.2.tgz",
|
||||
"dependencies": {
|
||||
"async": {
|
||||
"version": "1.5.2",
|
||||
@@ -146,7 +146,7 @@
|
||||
},
|
||||
"inherits": {
|
||||
"version": "2.0.1",
|
||||
"from": "inherits@>=2.0.0 <3.0.0",
|
||||
"from": "inherits@>=2.0.1 <2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
|
||||
},
|
||||
"isarray": {
|
||||
@@ -354,9 +354,9 @@
|
||||
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz"
|
||||
},
|
||||
"jsprim": {
|
||||
"version": "1.2.2",
|
||||
"version": "1.3.0",
|
||||
"from": "jsprim@>=1.2.2 <2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.2.2.tgz",
|
||||
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.3.0.tgz",
|
||||
"dependencies": {
|
||||
"extsprintf": {
|
||||
"version": "1.0.2",
|
||||
@@ -804,7 +804,7 @@
|
||||
},
|
||||
"type-is": {
|
||||
"version": "1.6.13",
|
||||
"from": "type-is@>=1.6.10 <1.7.0",
|
||||
"from": "type-is@>=1.6.6 <1.7.0",
|
||||
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.13.tgz",
|
||||
"dependencies": {
|
||||
"media-typer": {
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -1257,9 +1257,9 @@
|
||||
"resolved": "https://registry.npmjs.org/step-profiler/-/step-profiler-0.3.0.tgz"
|
||||
},
|
||||
"turbo-carto": {
|
||||
"version": "0.11.0",
|
||||
"from": "turbo-carto@0.11.0",
|
||||
"resolved": "https://registry.npmjs.org/turbo-carto/-/turbo-carto-0.11.0.tgz",
|
||||
"version": "0.12.1",
|
||||
"from": "turbo-carto@0.12.1",
|
||||
"resolved": "https://registry.npmjs.org/turbo-carto/-/turbo-carto-0.12.1.tgz",
|
||||
"dependencies": {
|
||||
"colorbrewer": {
|
||||
"version": "1.0.0",
|
||||
@@ -2976,11 +2976,6 @@
|
||||
"from": "ansi@>=0.3.1 <0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi/-/ansi-0.3.1.tgz"
|
||||
},
|
||||
"ansi-regex": {
|
||||
"version": "2.0.0",
|
||||
"from": "ansi-regex@>=2.0.0 <3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.0.0.tgz"
|
||||
},
|
||||
"ansi-styles": {
|
||||
"version": "2.2.1",
|
||||
"from": "ansi-styles@>=2.2.1 <3.0.0",
|
||||
@@ -2991,6 +2986,11 @@
|
||||
"from": "are-we-there-yet@>=1.1.2 <1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.2.tgz"
|
||||
},
|
||||
"ansi-regex": {
|
||||
"version": "2.0.0",
|
||||
"from": "ansi-regex@>=2.0.0 <3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.0.0.tgz"
|
||||
},
|
||||
"asn1": {
|
||||
"version": "0.2.3",
|
||||
"from": "asn1@>=0.2.3 <0.3.0",
|
||||
@@ -3051,26 +3051,26 @@
|
||||
"from": "combined-stream@>=1.0.5 <1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz"
|
||||
},
|
||||
"commander": {
|
||||
"version": "2.9.0",
|
||||
"from": "commander@>=2.9.0 <3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz"
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"from": "concat-map@0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz"
|
||||
},
|
||||
"core-util-is": {
|
||||
"version": "1.0.2",
|
||||
"from": "core-util-is@>=1.0.0 <1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz"
|
||||
"commander": {
|
||||
"version": "2.9.0",
|
||||
"from": "commander@>=2.9.0 <3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz"
|
||||
},
|
||||
"cryptiles": {
|
||||
"version": "2.0.5",
|
||||
"from": "cryptiles@>=2.0.0 <3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz"
|
||||
},
|
||||
"core-util-is": {
|
||||
"version": "1.0.2",
|
||||
"from": "core-util-is@>=1.0.0 <1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz"
|
||||
},
|
||||
"debug": {
|
||||
"version": "2.2.0",
|
||||
"from": "debug@>=2.2.0 <2.3.0",
|
||||
@@ -3101,26 +3101,26 @@
|
||||
"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"
|
||||
"forever-agent": {
|
||||
"version": "0.6.1",
|
||||
"from": "forever-agent@>=0.6.1 <0.7.0",
|
||||
"resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz"
|
||||
},
|
||||
"extsprintf": {
|
||||
"version": "1.0.2",
|
||||
"from": "extsprintf@1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.0.2.tgz"
|
||||
},
|
||||
"forever-agent": {
|
||||
"version": "0.6.1",
|
||||
"from": "forever-agent@>=0.6.1 <0.7.0",
|
||||
"resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz"
|
||||
},
|
||||
"form-data": {
|
||||
"version": "1.0.0-rc4",
|
||||
"from": "form-data@>=1.0.0-rc3 <1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-1.0.0-rc4.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"
|
||||
},
|
||||
"fstream": {
|
||||
"version": "1.0.9",
|
||||
"from": "fstream@>=1.0.2 <2.0.0",
|
||||
@@ -3136,16 +3136,16 @@
|
||||
"from": "gauge@>=1.2.5 <1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/gauge/-/gauge-1.2.7.tgz"
|
||||
},
|
||||
"generate-object-property": {
|
||||
"version": "1.2.0",
|
||||
"from": "generate-object-property@>=1.1.0 <2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz"
|
||||
},
|
||||
"generate-function": {
|
||||
"version": "2.0.0",
|
||||
"from": "generate-function@>=2.0.0 <3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz"
|
||||
},
|
||||
"generate-object-property": {
|
||||
"version": "1.2.0",
|
||||
"from": "generate-object-property@>=1.1.0 <2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz"
|
||||
},
|
||||
"glob": {
|
||||
"version": "7.0.3",
|
||||
"from": "glob@>=7.0.0 <8.0.0",
|
||||
@@ -3236,26 +3236,26 @@
|
||||
"from": "jodid25519@>=1.0.0 <2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jodid25519/-/jodid25519-1.0.2.tgz"
|
||||
},
|
||||
"json-schema": {
|
||||
"version": "0.2.2",
|
||||
"from": "json-schema@0.2.2",
|
||||
"resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.2.tgz"
|
||||
},
|
||||
"jsbn": {
|
||||
"version": "0.1.0",
|
||||
"from": "jsbn@>=0.1.0 <0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.0.tgz"
|
||||
},
|
||||
"json-stringify-safe": {
|
||||
"version": "5.0.1",
|
||||
"from": "json-stringify-safe@>=5.0.1 <5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz"
|
||||
"json-schema": {
|
||||
"version": "0.2.2",
|
||||
"from": "json-schema@0.2.2",
|
||||
"resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.2.tgz"
|
||||
},
|
||||
"jsonpointer": {
|
||||
"version": "2.0.0",
|
||||
"from": "jsonpointer@2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-2.0.0.tgz"
|
||||
},
|
||||
"json-stringify-safe": {
|
||||
"version": "5.0.1",
|
||||
"from": "json-stringify-safe@>=5.0.1 <5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz"
|
||||
},
|
||||
"jsprim": {
|
||||
"version": "1.2.2",
|
||||
"from": "jsprim@>=1.2.2 <2.0.0",
|
||||
@@ -3291,11 +3291,6 @@
|
||||
"from": "lodash.tostring@>=4.0.0 <5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.tostring/-/lodash.tostring-4.1.3.tgz"
|
||||
},
|
||||
"mime-db": {
|
||||
"version": "1.23.0",
|
||||
"from": "mime-db@>=1.23.0 <1.24.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.23.0.tgz"
|
||||
},
|
||||
"mime-types": {
|
||||
"version": "2.1.11",
|
||||
"from": "mime-types@>=2.1.7 <2.2.0",
|
||||
@@ -3311,6 +3306,11 @@
|
||||
"from": "minimist@0.0.8",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz"
|
||||
},
|
||||
"mime-db": {
|
||||
"version": "1.23.0",
|
||||
"from": "mime-db@>=1.23.0 <1.24.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.23.0.tgz"
|
||||
},
|
||||
"mkdirp": {
|
||||
"version": "0.5.1",
|
||||
"from": "mkdirp@>=0.5.0 <0.6.0",
|
||||
@@ -3326,16 +3326,16 @@
|
||||
"from": "node-uuid@>=1.4.7 <1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.7.tgz"
|
||||
},
|
||||
"nopt": {
|
||||
"version": "3.0.6",
|
||||
"from": "nopt@>=3.0.1 <3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz"
|
||||
},
|
||||
"npmlog": {
|
||||
"version": "2.0.3",
|
||||
"from": "npmlog@>=2.0.0 <2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-2.0.3.tgz"
|
||||
},
|
||||
"nopt": {
|
||||
"version": "3.0.6",
|
||||
"from": "nopt@>=3.0.1 <3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz"
|
||||
},
|
||||
"oauth-sign": {
|
||||
"version": "0.8.2",
|
||||
"from": "oauth-sign@>=0.8.1 <0.9.0",
|
||||
@@ -3386,26 +3386,26 @@
|
||||
"from": "rimraf@>=2.5.0 <2.6.0",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.5.2.tgz"
|
||||
},
|
||||
"semver": {
|
||||
"version": "5.1.0",
|
||||
"from": "semver@>=5.1.0 <5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.1.0.tgz"
|
||||
},
|
||||
"sntp": {
|
||||
"version": "1.0.9",
|
||||
"from": "sntp@>=1.0.0 <2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz"
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "0.10.31",
|
||||
"from": "string_decoder@>=0.10.0 <0.11.0",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz"
|
||||
"semver": {
|
||||
"version": "5.1.0",
|
||||
"from": "semver@>=5.1.0 <5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.1.0.tgz"
|
||||
},
|
||||
"stringstream": {
|
||||
"version": "0.0.5",
|
||||
"from": "stringstream@>=0.0.4 <0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz"
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "0.10.31",
|
||||
"from": "string_decoder@>=0.10.0 <0.11.0",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz"
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "3.0.1",
|
||||
"from": "strip-ansi@>=3.0.0 <4.0.0",
|
||||
@@ -3416,31 +3416,31 @@
|
||||
"from": "strip-json-comments@>=1.0.4 <1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.4.tgz"
|
||||
},
|
||||
"tar": {
|
||||
"version": "2.2.1",
|
||||
"from": "tar@>=2.2.0 <2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz"
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "2.0.0",
|
||||
"from": "supports-color@>=2.0.0 <3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz"
|
||||
},
|
||||
"tar": {
|
||||
"version": "2.2.1",
|
||||
"from": "tar@>=2.2.0 <2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz"
|
||||
},
|
||||
"tough-cookie": {
|
||||
"version": "2.2.2",
|
||||
"from": "tough-cookie@>=2.2.0 <2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.2.2.tgz"
|
||||
},
|
||||
"tweetnacl": {
|
||||
"version": "0.13.3",
|
||||
"from": "tweetnacl@>=0.13.0 <0.14.0",
|
||||
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.13.3.tgz"
|
||||
},
|
||||
"tunnel-agent": {
|
||||
"version": "0.4.3",
|
||||
"from": "tunnel-agent@>=0.4.1 <0.5.0",
|
||||
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.3.tgz"
|
||||
},
|
||||
"tweetnacl": {
|
||||
"version": "0.13.3",
|
||||
"from": "tweetnacl@>=0.13.0 <0.14.0",
|
||||
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.13.3.tgz"
|
||||
},
|
||||
"uid-number": {
|
||||
"version": "0.0.6",
|
||||
"from": "uid-number@>=0.0.6 <0.1.0",
|
||||
@@ -3490,18 +3490,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"rc": {
|
||||
"version": "1.1.6",
|
||||
"from": "rc@>=1.1.0 <1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/rc/-/rc-1.1.6.tgz",
|
||||
"dependencies": {
|
||||
"minimist": {
|
||||
"version": "1.2.0",
|
||||
"from": "minimist@>=1.2.0 <2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz"
|
||||
}
|
||||
}
|
||||
},
|
||||
"getpass": {
|
||||
"version": "0.1.6",
|
||||
"from": "getpass@>=0.1.1 <0.2.0",
|
||||
@@ -3514,6 +3502,18 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"rc": {
|
||||
"version": "1.1.6",
|
||||
"from": "rc@>=1.1.0 <1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/rc/-/rc-1.1.6.tgz",
|
||||
"dependencies": {
|
||||
"minimist": {
|
||||
"version": "1.2.0",
|
||||
"from": "minimist@>=1.2.0 <2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sshpk": {
|
||||
"version": "1.8.3",
|
||||
"from": "sshpk@>=1.7.0 <2.0.0",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "windshaft-cartodb",
|
||||
"version": "2.46.0",
|
||||
"version": "2.53.2",
|
||||
"description": "A map tile server for CartoDB",
|
||||
"keywords": [
|
||||
"cartodb"
|
||||
@@ -20,7 +20,7 @@
|
||||
],
|
||||
"dependencies": {
|
||||
"body-parser": "~1.14.0",
|
||||
"camshaft": "0.13.0",
|
||||
"camshaft": "0.22.2",
|
||||
"cartodb-psql": "~0.6.1",
|
||||
"cartodb-query-tables": "~0.1.0",
|
||||
"cartodb-redis": "~0.13.0",
|
||||
@@ -37,7 +37,7 @@
|
||||
"request": "~2.62.0",
|
||||
"step": "~0.0.6",
|
||||
"step-profiler": "~0.3.0",
|
||||
"turbo-carto": "0.11.0",
|
||||
"turbo-carto": "0.12.1",
|
||||
"underscore": "~1.6.0",
|
||||
"windshaft": "2.3.0"
|
||||
},
|
||||
|
||||
@@ -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,200 @@ describe('analysis-layers error cases', function() {
|
||||
testClient.drain(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle missing analyses when layers point to nonexistent one', function(done) {
|
||||
var mapConfig = createMapConfig(
|
||||
[
|
||||
{
|
||||
"type": "http",
|
||||
"options": {
|
||||
"urlTemplate": "http://{s}.basemaps.cartocdn.com/light_only_labels/{z}/{x}/{y}.png",
|
||||
"subdomains": "abcd"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "cartodb",
|
||||
"options": {
|
||||
"source": {
|
||||
"id": "ID-FOR-NONEXISTENT-ANALYSIS"
|
||||
},
|
||||
"cartocss": '#polygons { polygon-fill: red; }',
|
||||
"cartocss_version": "2.3.0"
|
||||
}
|
||||
}
|
||||
]
|
||||
);
|
||||
|
||||
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 analysis node.id="ID-FOR-NONEXISTENT-ANALYSIS" for layer=1'
|
||||
);
|
||||
|
||||
testClient.drain(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle missing analyses when dataviews point to nonexistent one', function(done) {
|
||||
var mapConfig = createMapConfig(
|
||||
[
|
||||
{
|
||||
"type": "http",
|
||||
"options": {
|
||||
"urlTemplate": "http://{s}.basemaps.cartocdn.com/light_only_labels/{z}/{x}/{y}.png",
|
||||
"subdomains": "abcd"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "cartodb",
|
||||
"options": {
|
||||
"sql": "select * from populated_places_simple_reduced",
|
||||
"cartocss": '#polygons { polygon-fill: red; }',
|
||||
"cartocss_version": "2.3.0"
|
||||
}
|
||||
}
|
||||
],
|
||||
{
|
||||
pop_max_histogram: {
|
||||
source: {
|
||||
id: 'ID-FOR-NONEXISTENT-ANALYSIS'
|
||||
},
|
||||
type: 'histogram',
|
||||
options: {
|
||||
column: 'pop_max'
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
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], 'Node with `source.id="ID-FOR-NONEXISTENT-ANALYSIS"`' +
|
||||
' not found in analyses for dataview "pop_max_histogram"');
|
||||
|
||||
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].type, 'analysis');
|
||||
assert.equal(
|
||||
layergroupResult.errors_with_context[0].message,
|
||||
'Analysis requires authentication with API key: permission denied.'
|
||||
);
|
||||
assert.equal(layergroupResult.errors_with_context[0].context.layer.index, 0);
|
||||
assert.equal(layergroupResult.errors_with_context[0].context.layer.id, 'HEAD');
|
||||
assert.equal(layergroupResult.errors_with_context[0].context.layer.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].type, 'analysis');
|
||||
assert.equal(layergroupResult.errors_with_context[0].message, 'Missing required param "radius"');
|
||||
assert.equal(layergroupResult.errors_with_context[0].context.layer.index, 0);
|
||||
assert.equal(layergroupResult.errors_with_context[0].context.layer.id, 'HEAD');
|
||||
assert.equal(layergroupResult.errors_with_context[0].context.layer.type, 'buffer');
|
||||
|
||||
testClient.drain(done);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
59
test/acceptance/dataviews/aggregation.js
Normal file
59
test/acceptance/dataviews/aggregation.js
Normal file
@@ -0,0 +1,59 @@
|
||||
require('../../support/test_helper');
|
||||
|
||||
var assert = require('../../support/assert');
|
||||
var TestClient = require('../../support/test-client');
|
||||
|
||||
describe('aggregations', function() {
|
||||
|
||||
afterEach(function(done) {
|
||||
if (this.testClient) {
|
||||
this.testClient.drain(done);
|
||||
} else {
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
function aggregationOperationMapConfig(operation) {
|
||||
return {
|
||||
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: operation,
|
||||
aggregationColumn: 'pop_max'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
var operations = ['count', 'sum', 'avg', 'max', 'min'];
|
||||
|
||||
operations.forEach(function(operation) {
|
||||
it('should be able to use "' + operation + '" as aggregation operation', function(done) {
|
||||
|
||||
this.testClient = new TestClient(aggregationOperationMapConfig(operation));
|
||||
this.testClient.getDataview('adm0name', { own_filter: 0 }, function (err, aggregation) {
|
||||
assert.ok(!err, err);
|
||||
assert.ok(aggregation);
|
||||
|
||||
assert.equal(aggregation.type, 'aggregation');
|
||||
assert.equal(aggregation.aggregation, operation);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
87
test/acceptance/dataviews/error-cases.js
Normal file
87
test/acceptance/dataviews/error-cases.js
Normal 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();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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();
|
||||
}
|
||||
);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
},
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
},
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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();
|
||||
};
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -22,215 +22,231 @@ describe('turbo-carto for named maps', function() {
|
||||
|
||||
var templateId = 'turbo-carto-template-1';
|
||||
|
||||
var template = {
|
||||
version: '0.0.1',
|
||||
name: templateId,
|
||||
auth: { method: 'open' },
|
||||
placeholders: {
|
||||
color: {
|
||||
type: "css_color",
|
||||
default: "Reds"
|
||||
}
|
||||
},
|
||||
layergroup: {
|
||||
version: '1.0.0',
|
||||
layers: [{
|
||||
options: {
|
||||
sql: [
|
||||
'SELECT test_table.*, _prices.price FROM test_table JOIN (' +
|
||||
' SELECT 1 AS cartodb_id, 10.00 AS price',
|
||||
' UNION',
|
||||
' SELECT 2, 10.50',
|
||||
' UNION',
|
||||
' SELECT 3, 11.00',
|
||||
' UNION',
|
||||
' SELECT 4, 12.00',
|
||||
' UNION',
|
||||
' SELECT 5, 21.00',
|
||||
') _prices ON _prices.cartodb_id = test_table.cartodb_id'
|
||||
].join('\n'),
|
||||
cartocss: [
|
||||
'#layer {',
|
||||
' marker-fill: ramp([price], colorbrewer(<%= color %>));',
|
||||
' marker-allow-overlap:true;',
|
||||
'}'
|
||||
].join('\n'),
|
||||
cartocss_version: '2.0.2'
|
||||
}
|
||||
function template(table) {
|
||||
return {
|
||||
version: '0.0.1',
|
||||
name: templateId,
|
||||
auth: { method: 'open' },
|
||||
placeholders: {
|
||||
color: {
|
||||
type: "css_color",
|
||||
default: "Reds"
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
},
|
||||
layergroup: {
|
||||
version: '1.0.0',
|
||||
layers: [
|
||||
{
|
||||
options: {
|
||||
sql: [
|
||||
'SELECT ' + table + '.*, _prices.price FROM ' + table + ' JOIN (' +
|
||||
' SELECT 1 AS cartodb_id, 10.00 AS price',
|
||||
' UNION',
|
||||
' SELECT 2, 10.50',
|
||||
' UNION',
|
||||
' SELECT 3, 11.00',
|
||||
' UNION',
|
||||
' SELECT 4, 12.00',
|
||||
' UNION',
|
||||
' SELECT 5, 21.00',
|
||||
') _prices ON _prices.cartodb_id = ' + table + '.cartodb_id'
|
||||
].join('\n'),
|
||||
cartocss: [
|
||||
'#layer {',
|
||||
' marker-fill: ramp([price], colorbrewer(<%= color %>));',
|
||||
' marker-allow-overlap:true;',
|
||||
'}'
|
||||
].join('\n'),
|
||||
cartocss_version: '2.0.2'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
var templateParamsReds = { color: 'Reds' };
|
||||
var templateParamsBlues = { color: 'Blues' };
|
||||
|
||||
it('should create a template with turbo-carto parsed properly', function (done) {
|
||||
step(
|
||||
function postTemplate() {
|
||||
var next = this;
|
||||
var scenarios = [
|
||||
{
|
||||
desc: 'with public tables',
|
||||
table: 'test_table'
|
||||
},
|
||||
{
|
||||
desc: 'with private tables',
|
||||
table: 'test_table_private_1'
|
||||
}
|
||||
];
|
||||
|
||||
assert.response(server, {
|
||||
url: '/api/v1/map/named?api_key=1234',
|
||||
method: 'POST',
|
||||
headers: { host: 'localhost', 'Content-Type': 'application/json' },
|
||||
data: JSON.stringify(template)
|
||||
}, {},
|
||||
function (res, err) {
|
||||
next(err, res);
|
||||
});
|
||||
},
|
||||
function checkTemplate(err, res) {
|
||||
assert.ifError(err);
|
||||
assert.equal(res.statusCode, 200);
|
||||
assert.deepEqual(JSON.parse(res.body), {
|
||||
template_id: templateId
|
||||
});
|
||||
scenarios.forEach(function(scenario) {
|
||||
it('should create a template with turbo-carto parsed properly: ' + scenario.desc, function (done) {
|
||||
step(
|
||||
function postTemplate() {
|
||||
var next = this;
|
||||
|
||||
return null;
|
||||
},
|
||||
function instantiateTemplateWithReds(err) {
|
||||
assert.ifError(err);
|
||||
assert.response(server, {
|
||||
url: '/api/v1/map/named?api_key=1234',
|
||||
method: 'POST',
|
||||
headers: { host: 'localhost', 'Content-Type': 'application/json' },
|
||||
data: JSON.stringify(template(scenario.table))
|
||||
}, {},
|
||||
function (res, err) {
|
||||
next(err, res);
|
||||
});
|
||||
},
|
||||
function checkTemplate(err, res) {
|
||||
assert.ifError(err);
|
||||
assert.equal(res.statusCode, 200);
|
||||
assert.deepEqual(JSON.parse(res.body), {
|
||||
template_id: templateId
|
||||
});
|
||||
|
||||
var next = this;
|
||||
assert.response(server, {
|
||||
url: '/api/v1/map/named/' + templateId,
|
||||
method: 'POST',
|
||||
headers: {
|
||||
host: 'localhost',
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
data: JSON.stringify(templateParamsReds)
|
||||
}, {},
|
||||
function(res, err) {
|
||||
return next(err, res);
|
||||
});
|
||||
},
|
||||
function checkInstanciationWithReds(err, res) {
|
||||
assert.ifError(err);
|
||||
return null;
|
||||
},
|
||||
function instantiateTemplateWithReds(err) {
|
||||
assert.ifError(err);
|
||||
|
||||
assert.equal(res.statusCode, 200);
|
||||
var next = this;
|
||||
assert.response(server, {
|
||||
url: '/api/v1/map/named/' + templateId,
|
||||
method: 'POST',
|
||||
headers: {
|
||||
host: 'localhost',
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
data: JSON.stringify(templateParamsReds)
|
||||
}, {},
|
||||
function(res, err) {
|
||||
return next(err, res);
|
||||
});
|
||||
},
|
||||
function checkInstanciationWithReds(err, res) {
|
||||
assert.ifError(err);
|
||||
|
||||
var parsedBody = JSON.parse(res.body);
|
||||
assert.equal(res.statusCode, 200);
|
||||
|
||||
keysToDelete['map_cfg|' + LayergroupToken.parse(parsedBody.layergroupid).token] = 0;
|
||||
keysToDelete['user:localhost:mapviews:global'] = 5;
|
||||
var parsedBody = JSON.parse(res.body);
|
||||
|
||||
assert.ok(parsedBody.layergroupid);
|
||||
assert.ok(parsedBody.last_updated);
|
||||
keysToDelete['map_cfg|' + LayergroupToken.parse(parsedBody.layergroupid).token] = 0;
|
||||
keysToDelete['user:localhost:mapviews:global'] = 5;
|
||||
|
||||
return parsedBody.layergroupid;
|
||||
},
|
||||
function requestTileReds(err, layergroupId) {
|
||||
assert.ifError(err);
|
||||
assert.ok(parsedBody.layergroupid);
|
||||
assert.ok(parsedBody.last_updated);
|
||||
|
||||
var next = this;
|
||||
return parsedBody.layergroupid;
|
||||
},
|
||||
function requestTileReds(err, layergroupId) {
|
||||
assert.ifError(err);
|
||||
|
||||
assert.response(server, {
|
||||
url: '/api/v1/map/' + layergroupId + '/0/0/0.png',
|
||||
method: 'GET',
|
||||
headers: { host: 'localhost' },
|
||||
encoding: 'binary'
|
||||
}, {},
|
||||
function(res, err) {
|
||||
next(err, res);
|
||||
});
|
||||
},
|
||||
function checkTileReds(err, res) {
|
||||
assert.ifError(err);
|
||||
var next = this;
|
||||
|
||||
var next = this;
|
||||
assert.response(server, {
|
||||
url: '/api/v1/map/' + layergroupId + '/0/0/0.png',
|
||||
method: 'GET',
|
||||
headers: { host: 'localhost' },
|
||||
encoding: 'binary'
|
||||
}, {},
|
||||
function(res, err) {
|
||||
next(err, res);
|
||||
});
|
||||
},
|
||||
function checkTileReds(err, res) {
|
||||
assert.ifError(err);
|
||||
|
||||
assert.equal(res.statusCode, 200);
|
||||
assert.equal(res.headers['content-type'], 'image/png');
|
||||
var next = this;
|
||||
|
||||
var fixturePath = './test/fixtures/turbo-carto-named-maps-reds.png';
|
||||
var image = mapnik.Image.fromBytes(new Buffer(res.body, 'binary'));
|
||||
assert.equal(res.statusCode, 200);
|
||||
assert.equal(res.headers['content-type'], 'image/png');
|
||||
|
||||
assert.imageIsSimilarToFile(image, fixturePath, IMAGE_TOLERANCE_PER_MIL, next);
|
||||
},
|
||||
function instantiateTemplateWithBlues(err) {
|
||||
assert.ifError(err);
|
||||
var fixturePath = './test/fixtures/turbo-carto-named-maps-reds.png';
|
||||
var image = mapnik.Image.fromBytes(new Buffer(res.body, 'binary'));
|
||||
|
||||
var next = this;
|
||||
assert.response(server, {
|
||||
url: '/api/v1/map/named/' + templateId,
|
||||
method: 'POST',
|
||||
headers: {
|
||||
host: 'localhost',
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
data: JSON.stringify(templateParamsBlues)
|
||||
}, {},
|
||||
function(res, err) {
|
||||
return next(err, res);
|
||||
});
|
||||
},
|
||||
function checkInstanciationWithBlues(err, res) {
|
||||
assert.ifError(err);
|
||||
assert.equal(res.statusCode, 200);
|
||||
assert.imageIsSimilarToFile(image, fixturePath, IMAGE_TOLERANCE_PER_MIL, next);
|
||||
},
|
||||
function instantiateTemplateWithBlues(err) {
|
||||
assert.ifError(err);
|
||||
|
||||
var parsedBody = JSON.parse(res.body);
|
||||
var next = this;
|
||||
assert.response(server, {
|
||||
url: '/api/v1/map/named/' + templateId,
|
||||
method: 'POST',
|
||||
headers: {
|
||||
host: 'localhost',
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
data: JSON.stringify(templateParamsBlues)
|
||||
}, {},
|
||||
function(res, err) {
|
||||
return next(err, res);
|
||||
});
|
||||
},
|
||||
function checkInstanciationWithBlues(err, res) {
|
||||
assert.ifError(err);
|
||||
assert.equal(res.statusCode, 200);
|
||||
|
||||
keysToDelete['map_cfg|' + LayergroupToken.parse(parsedBody.layergroupid).token] = 0;
|
||||
keysToDelete['user:localhost:mapviews:global'] = 5;
|
||||
var parsedBody = JSON.parse(res.body);
|
||||
|
||||
assert.ok(parsedBody.layergroupid);
|
||||
assert.ok(parsedBody.last_updated);
|
||||
keysToDelete['map_cfg|' + LayergroupToken.parse(parsedBody.layergroupid).token] = 0;
|
||||
keysToDelete['user:localhost:mapviews:global'] = 5;
|
||||
|
||||
return parsedBody.layergroupid;
|
||||
},
|
||||
function requestTileBlues(err, layergroupId) {
|
||||
assert.ifError(err);
|
||||
assert.ok(parsedBody.layergroupid);
|
||||
assert.ok(parsedBody.last_updated);
|
||||
|
||||
var next = this;
|
||||
return parsedBody.layergroupid;
|
||||
},
|
||||
function requestTileBlues(err, layergroupId) {
|
||||
assert.ifError(err);
|
||||
|
||||
assert.response(server, {
|
||||
url: '/api/v1/map/' + layergroupId + '/0/0/0.png',
|
||||
method: 'GET',
|
||||
headers: { host: 'localhost' },
|
||||
encoding: 'binary'
|
||||
}, {},
|
||||
function(res, err) {
|
||||
next(err, res);
|
||||
});
|
||||
},
|
||||
function checkTileBlues(err, res) {
|
||||
assert.ifError(err);
|
||||
var next = this;
|
||||
|
||||
var next = this;
|
||||
assert.response(server, {
|
||||
url: '/api/v1/map/' + layergroupId + '/0/0/0.png',
|
||||
method: 'GET',
|
||||
headers: { host: 'localhost' },
|
||||
encoding: 'binary'
|
||||
}, {},
|
||||
function(res, err) {
|
||||
next(err, res);
|
||||
});
|
||||
},
|
||||
function checkTileBlues(err, res) {
|
||||
assert.ifError(err);
|
||||
|
||||
assert.equal(res.statusCode, 200);
|
||||
assert.equal(res.headers['content-type'], 'image/png');
|
||||
var next = this;
|
||||
|
||||
var fixturePath = './test/fixtures/turbo-carto-named-maps-blues.png';
|
||||
var image = mapnik.Image.fromBytes(new Buffer(res.body, 'binary'));
|
||||
assert.equal(res.statusCode, 200);
|
||||
assert.equal(res.headers['content-type'], 'image/png');
|
||||
|
||||
assert.imageIsSimilarToFile(image, fixturePath, IMAGE_TOLERANCE_PER_MIL, next);
|
||||
},
|
||||
function deleteTemplate(err) {
|
||||
assert.ifError(err);
|
||||
var fixturePath = './test/fixtures/turbo-carto-named-maps-blues.png';
|
||||
var image = mapnik.Image.fromBytes(new Buffer(res.body, 'binary'));
|
||||
|
||||
var next = this;
|
||||
assert.imageIsSimilarToFile(image, fixturePath, IMAGE_TOLERANCE_PER_MIL, next);
|
||||
},
|
||||
function deleteTemplate(err) {
|
||||
assert.ifError(err);
|
||||
|
||||
assert.response(server, {
|
||||
url: '/api/v1/map/named/' + templateId + '?api_key=1234',
|
||||
method: 'DELETE',
|
||||
headers: { host: 'localhost' }
|
||||
}, {}, function (res, err) {
|
||||
next(err, res);
|
||||
});
|
||||
},
|
||||
function checkDeleteTemplate(err, res) {
|
||||
assert.ifError(err);
|
||||
assert.equal(res.statusCode, 204);
|
||||
assert.ok(!res.body);
|
||||
var next = this;
|
||||
|
||||
return null;
|
||||
},
|
||||
function finish(err) {
|
||||
done(err);
|
||||
}
|
||||
);
|
||||
assert.response(server, {
|
||||
url: '/api/v1/map/named/' + templateId + '?api_key=1234',
|
||||
method: 'DELETE',
|
||||
headers: { host: 'localhost' }
|
||||
}, {}, function (res, err) {
|
||||
next(err, res);
|
||||
});
|
||||
},
|
||||
function checkDeleteTemplate(err, res) {
|
||||
assert.ifError(err);
|
||||
assert.equal(res.statusCode, 204);
|
||||
assert.ok(!res.body);
|
||||
|
||||
return null;
|
||||
},
|
||||
function finish(err) {
|
||||
done(err);
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -24,6 +24,8 @@ describe('turbo-carto regressions', function() {
|
||||
afterEach(function (done) {
|
||||
if (this.testClient) {
|
||||
this.testClient.drain(done);
|
||||
} else {
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -64,6 +66,57 @@ describe('turbo-carto regressions', function() {
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail for private tables', function(done) {
|
||||
var cartocss = [
|
||||
"#private_table {",
|
||||
" marker-placement: point;",
|
||||
" marker-allow-overlap: true;",
|
||||
" marker-line-width: 0;",
|
||||
" marker-fill-opacity: 1.0;",
|
||||
" marker-width: ramp([cartodb_id], 10, 20);",
|
||||
" marker-fill: red;",
|
||||
"}"
|
||||
].join('\n');
|
||||
|
||||
this.testClient = new TestClient(makeMapconfig('SELECT * FROM test_table_private_1', cartocss));
|
||||
this.testClient.getLayergroup(TestClient.RESPONSE.ERROR, function(err, layergroup) {
|
||||
assert.ok(!err, err);
|
||||
|
||||
assert.ok(!layergroup.hasOwnProperty('layergroupid'));
|
||||
assert.ok(layergroup.hasOwnProperty('errors'));
|
||||
|
||||
var turboCartoError = layergroup.errors_with_context[0];
|
||||
assert.ok(turboCartoError);
|
||||
assert.equal(turboCartoError.type, 'turbo-carto');
|
||||
assert.ok(turboCartoError.message.match(/permission\sdenied\sfor\srelation\stest_table_private_1/));
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should work for private tables with api key', function(done) {
|
||||
var cartocss = [
|
||||
"#private_table {",
|
||||
" marker-placement: point;",
|
||||
" marker-allow-overlap: true;",
|
||||
" marker-line-width: 0;",
|
||||
" marker-fill-opacity: 1.0;",
|
||||
" marker-width: ramp([cartodb_id], 10, 20);",
|
||||
" marker-fill: red;",
|
||||
"}"
|
||||
].join('\n');
|
||||
|
||||
this.testClient = new TestClient(makeMapconfig('SELECT * FROM test_table_private_1', cartocss), 1234);
|
||||
this.testClient.getLayergroup(function(err, layergroup) {
|
||||
assert.ok(!err, err);
|
||||
|
||||
assert.ok(layergroup.hasOwnProperty('layergroupid'));
|
||||
assert.ok(!layergroup.hasOwnProperty('errors'));
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should work with mapnik substitution tokens', function(done) {
|
||||
var cartocss = [
|
||||
"#layer {",
|
||||
@@ -167,4 +220,83 @@ describe('turbo-carto regressions', function() {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('empty datasource results', function() {
|
||||
|
||||
afterEach(function (done) {
|
||||
if (this.testClient) {
|
||||
this.testClient.drain(done);
|
||||
} else {
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
function emptyResultMapConfig(markerFillRule) {
|
||||
var cartocss = [
|
||||
"#county_points_with_population {",
|
||||
" marker-placement: point;",
|
||||
" marker-allow-overlap: true;",
|
||||
" marker-fill-opacity: 1.0;",
|
||||
" marker-fill: " + markerFillRule + ';',
|
||||
" marker-line-width: 0;",
|
||||
"}"
|
||||
].join('\n');
|
||||
|
||||
return {
|
||||
"version": "1.5.0",
|
||||
"layers": [
|
||||
{
|
||||
"type": 'mapnik',
|
||||
"options": {
|
||||
"cartocss_version": '2.3.0',
|
||||
"source": {
|
||||
"id": "head"
|
||||
},
|
||||
"cartocss": cartocss
|
||||
}
|
||||
}
|
||||
],
|
||||
"analyses": [
|
||||
{
|
||||
"id": "head",
|
||||
"type": "source",
|
||||
"params": {
|
||||
"query": "SELECT * FROM populated_places_simple_reduced limit 0"
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
it('should work for numeric ramps', function(done) {
|
||||
|
||||
var makerFillRule = 'ramp([pop_max], (#E5F5F9,#99D8C9,#2CA25F), jenks)';
|
||||
|
||||
this.testClient = new TestClient(emptyResultMapConfig(makerFillRule), 1234);
|
||||
this.testClient.getLayergroup(function(err, layergroup) {
|
||||
assert.ok(!err, err);
|
||||
|
||||
assert.ok(layergroup.hasOwnProperty('layergroupid'));
|
||||
assert.ok(!layergroup.hasOwnProperty('errors'));
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should work for category ramps', function(done) {
|
||||
|
||||
var makerFillRule = 'ramp([adm0name], (#E5F5F9,#99D8C9,#2CA25F), category)';
|
||||
|
||||
this.testClient = new TestClient(emptyResultMapConfig(makerFillRule), 1234);
|
||||
this.testClient.getLayergroup(function(err, layergroup) {
|
||||
assert.ok(!err, err);
|
||||
|
||||
assert.ok(layergroup.hasOwnProperty('layergroupid'));
|
||||
assert.ok(!layergroup.hasOwnProperty('errors'));
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -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='windshaft.test gadm4 ported/populated_places_simple_reduced'
|
||||
REMOTE_SQL_SCRIPTS='CDB_QueryStatements CDB_QueryTables CDB_CartodbfyTable CDB_TableMetadata CDB_ForeignTable CDB_UserTables CDB_ColumnNames CDB_AnalysisCatalog CDB_ZoomFromScale CDB_Overviews CDB_QuantileBins CDB_JenksBins CDB_HeadsTailsBins CDB_EqualIntervalBins CDB_Hexagon CDB_XYZ'
|
||||
REMOTE_SQL_SCRIPTS='CDB_QueryStatements CDB_QueryTables CDB_CartodbfyTable CDB_TableMetadata CDB_ForeignTable CDB_UserTables CDB_ColumnNames CDB_AnalysisCatalog 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}
|
||||
|
||||
@@ -24,6 +24,15 @@ function TestClient(mapConfig, apiKey) {
|
||||
|
||||
module.exports = TestClient;
|
||||
|
||||
module.exports.RESPONSE = {
|
||||
ERROR: {
|
||||
status: 400,
|
||||
headers: {
|
||||
'Content-Type': 'application/json; charset=utf-8'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TestClient.prototype.getWidget = function(widgetName, params, callback) {
|
||||
var self = this;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user