Merge branch 'master' into overviews-widgets-2
This commit is contained in:
commit
48415fb1f3
29
NEWS.md
29
NEWS.md
@ -1,11 +1,36 @@
|
||||
# Changelog
|
||||
|
||||
## 2.42.0
|
||||
## 2.42.3
|
||||
|
||||
Released 2016-mm-dd
|
||||
|
||||
|
||||
## 2.42.2
|
||||
|
||||
Released 2016-05-17
|
||||
|
||||
New features:
|
||||
- turbo-carto: mapnik substitution tokens support #455
|
||||
|
||||
|
||||
## 2.42.1
|
||||
|
||||
Released 2016-05-17
|
||||
- Upgraded turbo-carto to fix reversed color scales
|
||||
|
||||
|
||||
## 2.42.0
|
||||
|
||||
Released 2016-05-16
|
||||
|
||||
Bug fixes:
|
||||
- Fix named maps with analysis #453
|
||||
|
||||
Enhancements:
|
||||
- Use split strategy for head/tails turbo-carto quantification
|
||||
|
||||
Announcements:
|
||||
- Upgrades turbo-carto to [0.8.0](https://github.com/CartoDB/turbo-carto/releases/tag/0.8.0)
|
||||
- Upgrades turbo-carto to [0.9.0](https://github.com/CartoDB/turbo-carto/releases/tag/0.9.0)
|
||||
|
||||
|
||||
## 2.41.1
|
||||
|
@ -1,24 +1,18 @@
|
||||
var SubstitutionTokens = require('../utils/substitution-tokens');
|
||||
|
||||
function OverviewsMetadataApi(pgQueryRunner) {
|
||||
this.pgQueryRunner = pgQueryRunner;
|
||||
}
|
||||
|
||||
module.exports = OverviewsMetadataApi;
|
||||
|
||||
// TODO: share this with QueryTablesApi? ... or maintain independence?
|
||||
var affectedTableRegexCache = {
|
||||
bbox: /!bbox!/g,
|
||||
scale_denominator: /!scale_denominator!/g,
|
||||
pixel_width: /!pixel_width!/g,
|
||||
pixel_height: /!pixel_height!/g
|
||||
};
|
||||
|
||||
function prepareSql(sql) {
|
||||
return sql && sql
|
||||
.replace(affectedTableRegexCache.bbox, 'ST_MakeEnvelope(0,0,0,0)')
|
||||
.replace(affectedTableRegexCache.scale_denominator, '0')
|
||||
.replace(affectedTableRegexCache.pixel_width, '1')
|
||||
.replace(affectedTableRegexCache.pixel_height, '1')
|
||||
;
|
||||
return sql && SubstitutionTokens.replace(sql, {
|
||||
bbox: 'ST_MakeEnvelope(0,0,0,0)',
|
||||
scale_denominator: '0',
|
||||
pixel_width: '1',
|
||||
pixel_height: '1'
|
||||
});
|
||||
}
|
||||
|
||||
OverviewsMetadataApi.prototype.getOverviewsMetadata = function (username, sql, callback) {
|
||||
|
@ -8,15 +8,15 @@ var queue = require('queue-async');
|
||||
|
||||
var LruCache = require("lru-cache");
|
||||
|
||||
function NamedMapProviderCache(templateMaps, pgConnection, metadataBackend, userLimitsApi, overviewsAdapter,
|
||||
turboCartoAdapter) {
|
||||
function NamedMapProviderCache(templateMaps, pgConnection, metadataBackend, analysisBackend, userLimitsApi,
|
||||
overviewsAdapter, turboCartoAdapter) {
|
||||
this.templateMaps = templateMaps;
|
||||
this.pgConnection = pgConnection;
|
||||
this.metadataBackend = metadataBackend;
|
||||
this.userLimitsApi = userLimitsApi;
|
||||
|
||||
this.namedLayersAdapter = new MapConfigNamedLayersAdapter(templateMaps);
|
||||
this.analysisMapConfigAdapter = new AnalysisMapConfigAdapter();
|
||||
this.analysisMapConfigAdapter = new AnalysisMapConfigAdapter(analysisBackend);
|
||||
this.overviewsAdapter = overviewsAdapter;
|
||||
this.turboCartoAdapter = turboCartoAdapter;
|
||||
|
||||
|
@ -159,6 +159,7 @@ module.exports = function(serverOptions) {
|
||||
templateMaps,
|
||||
pgConnection,
|
||||
metadataBackend,
|
||||
analysisBackend,
|
||||
userLimitsApi,
|
||||
overviewsAdapter,
|
||||
turboCartoAdapter
|
||||
|
@ -23,6 +23,10 @@ var methodTemplates = Object.keys(methods).reduce(function(methodTemplates, meth
|
||||
return methodTemplates;
|
||||
}, {});
|
||||
|
||||
var method2strategy = {
|
||||
headtails: 'split'
|
||||
};
|
||||
|
||||
function PostgresDatasource (pgQueryRunner, username, query) {
|
||||
this.pgQueryRunner = pgQueryRunner;
|
||||
this.username = username;
|
||||
@ -48,7 +52,7 @@ PostgresDatasource.prototype.getRamp = function (column, buckets, method, callba
|
||||
return a - b;
|
||||
});
|
||||
|
||||
return callback(null, ramp);
|
||||
return callback(null, { ramp: ramp, strategy: method2strategy[methodName] });
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
var queue = require('queue-async');
|
||||
var SubstitutionTokens = require('../substitution-tokens');
|
||||
|
||||
function TurboCartoAdapter(turboCartoParser) {
|
||||
this.turboCartoParser = turboCartoParser;
|
||||
@ -37,13 +38,26 @@ TurboCartoAdapter.prototype._parseCartoCss = function (username, layer, callback
|
||||
});
|
||||
}
|
||||
|
||||
this.turboCartoParser.process(username, layer.options.cartocss, layer.options.sql, function (err, cartocss) {
|
||||
// Ignore turbo-carto errors and continue
|
||||
if (!err && cartocss) {
|
||||
layer.options.cartocss = cartocss;
|
||||
var sql = SubstitutionTokens.replace(layer.options.sql, {
|
||||
bbox: 'ST_MakeEnvelope(-20037508.34,-20037508.34,20037508.34,20037508.34,3857)',
|
||||
scale_denominator: '500000001',
|
||||
pixel_width: '156412',
|
||||
pixel_height: '156412'
|
||||
});
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
callback(null, layer);
|
||||
// Try to continue in the rest of the cases
|
||||
if (cartocss) {
|
||||
layer.options.cartocss = cartocss;
|
||||
}
|
||||
return callback(null, layer);
|
||||
});
|
||||
};
|
||||
|
||||
|
19
lib/cartodb/utils/substitution-tokens.js
Normal file
19
lib/cartodb/utils/substitution-tokens.js
Normal file
@ -0,0 +1,19 @@
|
||||
var SUBSTITUTION_TOKENS = {
|
||||
bbox: /!bbox!/g,
|
||||
scale_denominator: /!scale_denominator!/g,
|
||||
pixel_width: /!pixel_width!/g,
|
||||
pixel_height: /!pixel_height!/g
|
||||
};
|
||||
|
||||
var SubstitutionTokens = {
|
||||
replace: function(sql, replaceValues) {
|
||||
Object.keys(replaceValues).forEach(function(token) {
|
||||
if (SUBSTITUTION_TOKENS[token]) {
|
||||
sql = sql.replace(SUBSTITUTION_TOKENS[token], replaceValues[token]);
|
||||
}
|
||||
});
|
||||
return sql;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = SubstitutionTokens;
|
1404
npm-shrinkwrap.json
generated
1404
npm-shrinkwrap.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,7 +1,7 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "windshaft-cartodb",
|
||||
"version": "2.42.0",
|
||||
"version": "2.42.3",
|
||||
"description": "A map tile server for CartoDB",
|
||||
"keywords": [
|
||||
"cartodb"
|
||||
@ -37,7 +37,7 @@
|
||||
"request": "~2.62.0",
|
||||
"step": "~0.0.6",
|
||||
"step-profiler": "~0.3.0",
|
||||
"turbo-carto": "0.8.0",
|
||||
"turbo-carto": "0.9.2",
|
||||
"underscore": "~1.6.0",
|
||||
"windshaft": "1.19.0"
|
||||
},
|
||||
|
@ -1,11 +1,11 @@
|
||||
var assert = require('../../support/assert');
|
||||
var step = require('step');
|
||||
|
||||
var helper = require('../../support/test_helper');
|
||||
|
||||
var CartodbWindshaft = require('../../../lib/cartodb/server');
|
||||
var serverOptions = require('../../../lib/cartodb/server_options');
|
||||
var server = new CartodbWindshaft(serverOptions);
|
||||
var TestClient = require('../../support/test-client');
|
||||
|
||||
var LayergroupToken = require('../../../lib/cartodb/models/layergroup_token');
|
||||
|
||||
@ -16,230 +16,259 @@ describe('named-maps analysis', function() {
|
||||
var username = 'localhost';
|
||||
var widgetsTemplateName = 'widgets-template';
|
||||
|
||||
var layergroupid;
|
||||
var layergroup;
|
||||
var keysToDelete;
|
||||
|
||||
beforeEach(function(done) {
|
||||
keysToDelete = {};
|
||||
|
||||
var widgetsTemplate = {
|
||||
version: '0.0.1',
|
||||
name: widgetsTemplateName,
|
||||
layergroup: {
|
||||
version: '1.5.0',
|
||||
layers: [
|
||||
{
|
||||
"type": "cartodb",
|
||||
"options": {
|
||||
"source": {
|
||||
"id": "HEAD"
|
||||
},
|
||||
"cartocss": '#buffer { polygon-fill: red; }',
|
||||
"cartocss_version": "2.3.0"
|
||||
}
|
||||
}
|
||||
],
|
||||
dataviews: {
|
||||
pop_max_histogram: {
|
||||
source: {
|
||||
id: 'HEAD'
|
||||
var widgetsTemplate = {
|
||||
version: '0.0.1',
|
||||
name: widgetsTemplateName,
|
||||
layergroup: {
|
||||
version: '1.5.0',
|
||||
layers: [
|
||||
{
|
||||
"type": "cartodb",
|
||||
"options": {
|
||||
"source": {
|
||||
"id": "HEAD"
|
||||
},
|
||||
type: 'histogram',
|
||||
options: {
|
||||
column: 'pop_max'
|
||||
}
|
||||
"cartocss": '#buffer { polygon-fill: red; }',
|
||||
"cartocss_version": "2.3.0"
|
||||
}
|
||||
},
|
||||
analyses: [
|
||||
{
|
||||
"id": "HEAD",
|
||||
"type": "buffer",
|
||||
"params": {
|
||||
"source": {
|
||||
"id": "2570e105-7b37-40d2-bdf4-1af889598745",
|
||||
"type": "source",
|
||||
"params": {
|
||||
"query": "select * from populated_places_simple_reduced"
|
||||
}
|
||||
},
|
||||
"radius": 50000
|
||||
}
|
||||
}
|
||||
],
|
||||
dataviews: {
|
||||
pop_max_histogram: {
|
||||
source: {
|
||||
id: 'HEAD'
|
||||
},
|
||||
type: 'histogram',
|
||||
options: {
|
||||
column: 'pop_max'
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
var template_params = {};
|
||||
|
||||
step(
|
||||
function createTemplate()
|
||||
{
|
||||
var next = this;
|
||||
assert.response(
|
||||
server,
|
||||
{
|
||||
url: '/api/v1/map/named?api_key=1234',
|
||||
method: 'POST',
|
||||
headers: {
|
||||
host: username,
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
},
|
||||
analyses: [
|
||||
{
|
||||
"id": "HEAD",
|
||||
"type": "buffer",
|
||||
"params": {
|
||||
"source": {
|
||||
"id": "2570e105-7b37-40d2-bdf4-1af889598745",
|
||||
"type": "source",
|
||||
"params": {
|
||||
"query": "select * from populated_places_simple_reduced"
|
||||
}
|
||||
},
|
||||
data: JSON.stringify(widgetsTemplate)
|
||||
},
|
||||
{
|
||||
status: 200
|
||||
},
|
||||
function(res, err) {
|
||||
next(err, res);
|
||||
"radius": 50000
|
||||
}
|
||||
);
|
||||
},
|
||||
function instantiateTemplate(err, res) {
|
||||
assert.ifError(err);
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
assert.deepEqual(JSON.parse(res.body), { template_id: widgetsTemplateName });
|
||||
var next = this;
|
||||
assert.response(
|
||||
server,
|
||||
{
|
||||
url: '/api/v1/map/named/' + widgetsTemplateName,
|
||||
method: 'POST',
|
||||
headers: {
|
||||
host: username,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
data: JSON.stringify(template_params)
|
||||
},
|
||||
{
|
||||
status: 200
|
||||
},
|
||||
function(res) {
|
||||
next(null, res);
|
||||
}
|
||||
);
|
||||
},
|
||||
function finish(err, res) {
|
||||
assert.ifError(err);
|
||||
|
||||
layergroup = JSON.parse(res.body);
|
||||
assert.ok(layergroup.hasOwnProperty('layergroupid'), "Missing 'layergroupid' from: " + res.body);
|
||||
layergroupid = layergroup.layergroupid;
|
||||
|
||||
assert.ok(
|
||||
Array.isArray(layergroup.metadata.analyses),
|
||||
'Missing "analyses" array metadata from: ' + res.body
|
||||
);
|
||||
var analyses = layergroup.metadata.analyses;
|
||||
assert.equal(analyses.length, 1, 'Invalid number of analyses in metadata');
|
||||
var nodes = analyses[0].nodes;
|
||||
var nodesIds = Object.keys(nodes);
|
||||
assert.deepEqual(nodesIds, ['2570e105-7b37-40d2-bdf4-1af889598745', 'HEAD']);
|
||||
nodesIds.forEach(function(nodeId) {
|
||||
var node = nodes[nodeId];
|
||||
assert.ok(node.hasOwnProperty('url'), 'Missing "url" attribute in node');
|
||||
assert.ok(node.hasOwnProperty('status'), 'Missing "status" attribute in node');
|
||||
assert.ok(!node.hasOwnProperty('query'), 'Unexpected "query" attribute in node');
|
||||
});
|
||||
|
||||
keysToDelete['map_cfg|' + LayergroupToken.parse(layergroup.layergroupid).token] = 0;
|
||||
keysToDelete['user:localhost:mapviews:global'] = 5;
|
||||
|
||||
return done();
|
||||
}
|
||||
);
|
||||
|
||||
});
|
||||
|
||||
afterEach(function(done) {
|
||||
step(
|
||||
function deleteTemplate(err) {
|
||||
assert.ifError(err);
|
||||
var next = this;
|
||||
assert.response(
|
||||
server,
|
||||
{
|
||||
url: '/api/v1/map/named/' + widgetsTemplateName + '?api_key=1234',
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
host: username
|
||||
}
|
||||
},
|
||||
{
|
||||
status: 204
|
||||
},
|
||||
function(res, err) {
|
||||
next(err, res);
|
||||
}
|
||||
);
|
||||
},
|
||||
function deleteRedisKeys(err) {
|
||||
assert.ifError(err);
|
||||
helper.deleteRedisKeys(keysToDelete, done);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('should be able to retrieve images from analysis', function(done) {
|
||||
beforeEach(function createTemplate(done) {
|
||||
assert.response(
|
||||
server,
|
||||
{
|
||||
url: '/api/v1/map/' + layergroupid + '/6/31/24.png',
|
||||
method: 'GET',
|
||||
encoding: 'binary',
|
||||
url: '/api/v1/map/named?api_key=1234',
|
||||
method: 'POST',
|
||||
headers: {
|
||||
host: username,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
data: JSON.stringify(widgetsTemplate)
|
||||
},
|
||||
{
|
||||
status: 200
|
||||
},
|
||||
function(res, err) {
|
||||
assert.deepEqual(JSON.parse(res.body), { template_id: widgetsTemplateName });
|
||||
return done(err);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
afterEach(function deleteTemplate(done) {
|
||||
assert.response(
|
||||
server,
|
||||
{
|
||||
url: '/api/v1/map/named/' + widgetsTemplateName + '?api_key=1234',
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
host: username
|
||||
}
|
||||
},
|
||||
{
|
||||
status: 200,
|
||||
headers: {
|
||||
'Content-Type': 'image/png'
|
||||
}
|
||||
status: 204
|
||||
},
|
||||
function(res, err) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
return done(err);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
var fixturePath = './test/fixtures/analysis/named-map-buffer.png';
|
||||
assert.imageBufferIsSimilarToFile(res.body, fixturePath, IMAGE_TOLERANCE_PER_MIL, function(err) {
|
||||
describe('layergroup', function() {
|
||||
var layergroupid;
|
||||
var layergroup;
|
||||
var keysToDelete;
|
||||
|
||||
beforeEach(function(done) {
|
||||
keysToDelete = {};
|
||||
|
||||
assert.response(
|
||||
server,
|
||||
{
|
||||
url: '/api/v1/map/named/' + widgetsTemplateName,
|
||||
method: 'POST',
|
||||
headers: {
|
||||
host: username,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
data: JSON.stringify({})
|
||||
},
|
||||
{
|
||||
status: 200
|
||||
},
|
||||
function(res, err) {
|
||||
assert.ifError(err);
|
||||
|
||||
layergroup = JSON.parse(res.body);
|
||||
assert.ok(layergroup.hasOwnProperty('layergroupid'), "Missing 'layergroupid' from: " + res.body);
|
||||
layergroupid = layergroup.layergroupid;
|
||||
|
||||
assert.ok(
|
||||
Array.isArray(layergroup.metadata.analyses),
|
||||
'Missing "analyses" array metadata from: ' + res.body
|
||||
);
|
||||
var analyses = layergroup.metadata.analyses;
|
||||
assert.equal(analyses.length, 1, 'Invalid number of analyses in metadata');
|
||||
var nodes = analyses[0].nodes;
|
||||
var nodesIds = Object.keys(nodes);
|
||||
assert.deepEqual(nodesIds, ['2570e105-7b37-40d2-bdf4-1af889598745', 'HEAD']);
|
||||
nodesIds.forEach(function(nodeId) {
|
||||
var node = nodes[nodeId];
|
||||
assert.ok(node.hasOwnProperty('url'), 'Missing "url" attribute in node');
|
||||
assert.ok(node.hasOwnProperty('status'), 'Missing "status" attribute in node');
|
||||
assert.ok(!node.hasOwnProperty('query'), 'Unexpected "query" attribute in node');
|
||||
});
|
||||
|
||||
keysToDelete['map_cfg|' + LayergroupToken.parse(layergroup.layergroupid).token] = 0;
|
||||
keysToDelete['user:localhost:mapviews:global'] = 5;
|
||||
|
||||
return done();
|
||||
}
|
||||
);
|
||||
|
||||
});
|
||||
|
||||
afterEach(function(done) {
|
||||
helper.deleteRedisKeys(keysToDelete, done);
|
||||
});
|
||||
|
||||
it('should be able to retrieve images from analysis', function(done) {
|
||||
assert.response(
|
||||
server,
|
||||
{
|
||||
url: '/api/v1/map/' + layergroupid + '/6/31/24.png',
|
||||
method: 'GET',
|
||||
encoding: 'binary',
|
||||
headers: {
|
||||
host: username
|
||||
}
|
||||
},
|
||||
{
|
||||
status: 200,
|
||||
headers: {
|
||||
'Content-Type': 'image/png'
|
||||
}
|
||||
},
|
||||
function(res, err) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
var fixturePath = './test/fixtures/analysis/named-map-buffer.png';
|
||||
assert.imageBufferIsSimilarToFile(res.body, fixturePath, IMAGE_TOLERANCE_PER_MIL, function(err) {
|
||||
assert.ok(!err, err);
|
||||
done();
|
||||
});
|
||||
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('should be able to retrieve dataviews from analysis', function(done) {
|
||||
assert.response(
|
||||
server,
|
||||
{
|
||||
url: '/api/v1/map/' + layergroupid + '/dataview/pop_max_histogram',
|
||||
method: 'GET',
|
||||
headers: {
|
||||
host: username
|
||||
}
|
||||
},
|
||||
{
|
||||
status: 200,
|
||||
headers: {
|
||||
'Content-Type': 'application/json; charset=utf-8'
|
||||
}
|
||||
},
|
||||
function(res, err) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
var dataview = JSON.parse(res.body);
|
||||
assert.equal(dataview.type, 'histogram');
|
||||
assert.equal(dataview.bins_start, 0);
|
||||
|
||||
done();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('should be able to retrieve static map preview via layergroup', function(done) {
|
||||
assert.response(
|
||||
server,
|
||||
{
|
||||
url: '/api/v1/map/static/center/' + layergroupid + '/4/42/-3/320/240.png',
|
||||
method: 'GET',
|
||||
encoding: 'binary',
|
||||
headers: {
|
||||
host: username
|
||||
}
|
||||
},
|
||||
{
|
||||
status: 200,
|
||||
headers: {
|
||||
'Content-Type': 'image/png'
|
||||
}
|
||||
},
|
||||
function(res, err) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
var fixturePath = './test/fixtures/analysis/named-map-buffer-layergroup-static-preview.png';
|
||||
assert.imageBufferIsSimilarToFile(res.body, fixturePath, IMAGE_TOLERANCE_PER_MIL, function(err) {
|
||||
assert.ok(!err, err);
|
||||
done();
|
||||
});
|
||||
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('auto-instantiation', function() {
|
||||
it('should be able to retrieve static map preview via fixed url', function(done) {
|
||||
TestClient.getStaticMap(widgetsTemplateName, function(err, image) {
|
||||
assert.ok(!err, err);
|
||||
var fixturePath = './test/fixtures/analysis/named-map-buffer-static-preview.png';
|
||||
assert.imageIsSimilarToFile(image, fixturePath, IMAGE_TOLERANCE_PER_MIL, function(err) {
|
||||
assert.ok(!err, err);
|
||||
done();
|
||||
});
|
||||
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('should be able to retrieve dataviews from analysis', function(done) {
|
||||
assert.response(
|
||||
server,
|
||||
{
|
||||
url: '/api/v1/map/' + layergroupid + '/dataview/pop_max_histogram',
|
||||
method: 'GET',
|
||||
headers: {
|
||||
host: username
|
||||
}
|
||||
},
|
||||
{
|
||||
status: 200,
|
||||
headers: {
|
||||
'Content-Type': 'application/json; charset=utf-8'
|
||||
}
|
||||
},
|
||||
function(res, err) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
var dataview = JSON.parse(res.body);
|
||||
assert.equal(dataview.type, 'histogram');
|
||||
assert.equal(dataview.bins_start, 0);
|
||||
|
||||
done();
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
94
test/acceptance/turbo-cartocss/error-cases.js
Normal file
94
test/acceptance/turbo-cartocss/error-cases.js
Normal file
@ -0,0 +1,94 @@
|
||||
require('../../support/test_helper');
|
||||
|
||||
var assert = require('../../support/assert');
|
||||
var TestClient = require('../../support/test-client');
|
||||
|
||||
function makeMapconfig(markerWidth, markerFill) {
|
||||
return {
|
||||
"version": "1.4.0",
|
||||
"layers": [
|
||||
{
|
||||
"type": 'mapnik',
|
||||
"options": {
|
||||
"cartocss_version": '2.3.0',
|
||||
"sql": 'SELECT * FROM populated_places_simple_reduced',
|
||||
"cartocss": createCartocss(markerWidth, markerFill)
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
function createCartocss(markerWidth, markerFill) {
|
||||
return [
|
||||
"#populated_places_simple_reduced {",
|
||||
" marker-fill-opacity: 0.9;",
|
||||
" marker-line-color: #FFF;",
|
||||
" marker-line-width: 1;",
|
||||
" marker-line-opacity: 1;",
|
||||
" marker-placement: point;",
|
||||
" marker-type: ellipse;",
|
||||
" marker-allow-overlap: true;",
|
||||
" marker-width: " + (markerWidth || '10') + ";",
|
||||
" marker-fill: " + (markerFill || 'red') + ";",
|
||||
"}"
|
||||
].join('\n');
|
||||
}
|
||||
|
||||
var ERROR_RESPONSE = {
|
||||
status: 400,
|
||||
headers: {
|
||||
'Content-Type': 'application/json; charset=utf-8'
|
||||
}
|
||||
};
|
||||
|
||||
describe('turbo-carto error cases', function() {
|
||||
afterEach(function (done) {
|
||||
if (this.testClient) {
|
||||
this.testClient.drain(done);
|
||||
}
|
||||
});
|
||||
|
||||
it('should return invalid number of ramp error', function(done) {
|
||||
this.testClient = new TestClient(makeMapconfig('ramp([pop_max], (8,24,96), (8,24,96,128))'));
|
||||
this.testClient.getLayergroup(ERROR_RESPONSE, function(err, layergroup) {
|
||||
assert.ok(!err, err);
|
||||
|
||||
assert.ok(layergroup.hasOwnProperty('errors'));
|
||||
assert.equal(layergroup.errors.length, 1);
|
||||
assert.ok(layergroup.errors[0].match(/^turbo-carto/));
|
||||
assert.ok(layergroup.errors[0].match(/invalid\sramp\slength/i));
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should return invalid column from datasource', 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.length, 1);
|
||||
assert.ok(layergroup.errors[0].match(/^turbo-carto/));
|
||||
assert.ok(layergroup.errors[0].match(/unable\sto\scompute\sramp/i));
|
||||
assert.ok(layergroup.errors[0].match(/wadus_column/));
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail by falling back to normal carto parser', function(done) {
|
||||
this.testClient = new TestClient(makeMapconfig('ramp([price], (8,24,96), (8,24,96));//(red, green, blue))'));
|
||||
this.testClient.getLayergroup(ERROR_RESPONSE, function(err, layergroup) {
|
||||
assert.ok(!err, err);
|
||||
|
||||
assert.ok(layergroup.hasOwnProperty('errors'));
|
||||
assert.equal(layergroup.errors.length, 1);
|
||||
assert.ok(!layergroup.errors[0].match(/^turbo-carto/));
|
||||
assert.ok(layergroup.errors[0].match(/invalid\scode/i));
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
@ -3,7 +3,7 @@ require('../../support/test_helper');
|
||||
var assert = require('../../support/assert');
|
||||
var TestClient = require('../../support/test-client');
|
||||
|
||||
function makeMapconfig(cartocss) {
|
||||
function makeMapconfig(sql, cartocss) {
|
||||
return {
|
||||
"version": "1.4.0",
|
||||
"layers": [
|
||||
@ -11,19 +11,7 @@ function makeMapconfig(cartocss) {
|
||||
"type": 'mapnik',
|
||||
"options": {
|
||||
"cartocss_version": '2.3.0',
|
||||
"sql": [
|
||||
'SELECT test_table.*, _prices.price FROM test_table JOIN (' +
|
||||
' SELECT 1 AS cartodb_id, 10.00 AS price',
|
||||
' UNION',
|
||||
' SELECT 2, 10.50',
|
||||
' UNION',
|
||||
' SELECT 3, 11.00',
|
||||
' UNION',
|
||||
' SELECT 4, 12.00',
|
||||
' UNION',
|
||||
' SELECT 5, 21.00',
|
||||
') _prices ON _prices.cartodb_id = test_table.cartodb_id'
|
||||
].join('\n'),
|
||||
"sql": sql,
|
||||
"cartocss": cartocss
|
||||
}
|
||||
}
|
||||
@ -33,42 +21,84 @@ function makeMapconfig(cartocss) {
|
||||
|
||||
describe('turbo-carto regressions', function() {
|
||||
|
||||
var cartocss = [
|
||||
"/** simple visualization */",
|
||||
"",
|
||||
"Map {",
|
||||
" buffer-size: 256;",
|
||||
"}",
|
||||
"",
|
||||
"#county_points_with_population{",
|
||||
" marker-fill-opacity: 0.1;",
|
||||
" marker-line-color:#FFFFFF;//#CF1C90;",
|
||||
" marker-line-width: 0;",
|
||||
" marker-line-opacity: 0.3;",
|
||||
" marker-placement: point;",
|
||||
" marker-type: ellipse;",
|
||||
" //marker-comp-op: overlay;",
|
||||
" marker-width: [price];",
|
||||
" [zoom=5]{marker-width: [price]*2;}",
|
||||
" [zoom=6]{marker-width: [price]*4;}",
|
||||
" marker-fill: #000000;",
|
||||
" marker-allow-overlap: true;",
|
||||
" ",
|
||||
"",
|
||||
"}"
|
||||
].join('\n');
|
||||
|
||||
beforeEach(function () {
|
||||
this.testClient = new TestClient(makeMapconfig(cartocss));
|
||||
});
|
||||
|
||||
afterEach(function (done) {
|
||||
this.testClient.drain(done);
|
||||
if (this.testClient) {
|
||||
this.testClient.drain(done);
|
||||
}
|
||||
});
|
||||
|
||||
it('should accept // comments', function(done) {
|
||||
this.testClient.getTile(0, 0, 0, function(err) {
|
||||
var cartocss = [
|
||||
"/** simple visualization */",
|
||||
"",
|
||||
"Map {",
|
||||
" buffer-size: 256;",
|
||||
"}",
|
||||
"",
|
||||
"#county_points_with_population{",
|
||||
" marker-fill-opacity: 0.1;",
|
||||
" marker-line-color:#FFFFFF;//#CF1C90;",
|
||||
" marker-line-width: 0;",
|
||||
" marker-line-opacity: 0.3;",
|
||||
" marker-placement: point;",
|
||||
" marker-type: ellipse;",
|
||||
" //marker-comp-op: overlay;",
|
||||
" marker-width: [cartodb_id];",
|
||||
" [zoom=5]{marker-width: [cartodb_id]*2;}",
|
||||
" [zoom=6]{marker-width: [cartodb_id]*4;}",
|
||||
" marker-fill: #000000;",
|
||||
" marker-allow-overlap: true;",
|
||||
" ",
|
||||
"",
|
||||
"}"
|
||||
].join('\n');
|
||||
|
||||
this.testClient = new TestClient(makeMapconfig('SELECT * FROM populated_places_simple_reduced', cartocss));
|
||||
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 {",
|
||||
" 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');
|
||||
|
||||
this.testClient = new TestClient(makeMapconfig(sql, cartocss));
|
||||
this.testClient.getLayergroup(function(err, layergroup) {
|
||||
assert.ok(!err, err);
|
||||
|
||||
assert.ok(layergroup.hasOwnProperty('layergroupid'));
|
||||
assert.ok(!layergroup.hasOwnProperty('errors'));
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
BIN
test/fixtures/analysis/named-map-buffer-layergroup-static-preview.png
vendored
Normal file
BIN
test/fixtures/analysis/named-map-buffer-layergroup-static-preview.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 13 KiB |
BIN
test/fixtures/analysis/named-map-buffer-static-preview.png
vendored
Normal file
BIN
test/fixtures/analysis/named-map-buffer-static-preview.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 44 KiB |
@ -83,7 +83,7 @@ if test x"$PREPARE_PGSQL" = xyes; then
|
||||
|
||||
cat sql/_CDB_QueryStatements.sql | psql -v ON_ERROR_STOP=1 ${TEST_DB} || exit 1
|
||||
|
||||
SQL_SCRIPTS='CDB_QueryTables CDB_CartodbfyTable CDB_TableMetadata CDB_ForeignTable CDB_UserTables CDB_ColumnNames CDB_ZoomFromScale CDB_Overviews CDB_QuantileBins CDB_JenksBins CDB_HeadsTailsBins CDB_EqualIntervalBins'
|
||||
SQL_SCRIPTS='CDB_QueryTables CDB_CartodbfyTable CDB_TableMetadata CDB_ForeignTable CDB_UserTables CDB_ColumnNames CDB_ZoomFromScale CDB_Overviews CDB_QuantileBins CDB_JenksBins CDB_HeadsTailsBins CDB_EqualIntervalBins CDB_Hexagon CDB_XYZ'
|
||||
for i in ${SQL_SCRIPTS}
|
||||
do
|
||||
curl -L -s https://github.com/CartoDB/cartodb-postgresql/raw/master/scripts-available/$i.sql -o sql/$i.sql
|
||||
|
@ -471,3 +471,41 @@ TestClient.prototype.getNodeStatus = function(nodeName, callback) {
|
||||
TestClient.prototype.drain = function(callback) {
|
||||
helper.deleteRedisKeys(this.keysToDelete, callback);
|
||||
};
|
||||
|
||||
module.exports.getStaticMap = function getStaticMap(templateName, params, callback) {
|
||||
if (!callback) {
|
||||
callback = params;
|
||||
params = null;
|
||||
}
|
||||
|
||||
var url = '/api/v1/map/static/named/' + templateName + '/640/480.png';
|
||||
|
||||
if (params !== null) {
|
||||
url += '?' + qs.stringify(params);
|
||||
}
|
||||
|
||||
var requestOptions = {
|
||||
url: url,
|
||||
method: 'GET',
|
||||
headers: {
|
||||
host: 'localhost'
|
||||
},
|
||||
encoding: 'binary'
|
||||
};
|
||||
|
||||
var expectedResponse = {
|
||||
status: 200,
|
||||
headers: {
|
||||
'Content-Type': 'image/png'
|
||||
}
|
||||
};
|
||||
|
||||
// this could be removed once named maps are invalidated, otherwise you hits the cache
|
||||
var server = new CartodbWindshaft(serverOptions);
|
||||
|
||||
assert.response(server, requestOptions, expectedResponse, function (res, err) {
|
||||
helper.deleteRedisKeys({'user:localhost:mapviews:global': 5}, function() {
|
||||
return callback(err, mapnik.Image.fromBytes(new Buffer(res.body, 'binary')));
|
||||
});
|
||||
});
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user