Merge remote-tracking branch 'blessed/master' into 1073-aggregation-false

This commit is contained in:
Raul Marin
2019-03-14 17:01:07 +01:00
59 changed files with 3968 additions and 3636 deletions
+8 -9
View File
@@ -6,17 +6,16 @@ const assert = require('../support/assert');
const TestClient = require('../support/test-client');
const serverOptions = require('../../lib/cartodb/server_options');
const suites = [{
desc: 'mvt (mapnik)',
usePostGIS: false
}];
if (process.env.POSTGIS_VERSION >= '20400') {
suites.push({
const suites = [
{
desc: 'mvt (mapnik)',
usePostGIS: false
},
{
desc: 'mvt (postgis)',
usePostGIS: true
});
}
}
];
// Generate points with values and times.
// The point location is spanned over a given length, by default it is 0 so
+2 -4
View File
@@ -171,8 +171,7 @@ describe('buffer size per format', function () {
});
});
const describe_pg = process.env.POSTGIS_VERSION >= '20400' ? describe : describe.skip;
describe_pg('using postgis mvt renderer', function() {
describe('using postgis mvt renderer', function() {
before(function () {
serverOptions.renderer.mvt.usePostGIS = true;
});
@@ -505,8 +504,7 @@ describe('buffer size per format for named maps w/o placeholders', function () {
});
});
const describe_pg = process.env.POSTGIS_VERSION >= '20400' ? describe : describe.skip;
describe_pg('using postgis mvt renderer', function() {
describe('using postgis mvt renderer', function() {
before(function () {
serverOptions.renderer.mvt.usePostGIS = true;
});
+835
View File
@@ -0,0 +1,835 @@
'use strict';
require('../support/test_helper');
const assert = require('../support/assert');
const TestClient = require('../support/test-client');
const POINTS_SQL_1 = `
select
x + 4 as cartodb_id,
st_setsrid(st_makepoint(x*10 + 18, x*10 + 5), 4326) as the_geom,
st_transform(st_setsrid(st_makepoint(x*10 + 18, x*10 + 5), 4326), 3857) as the_geom_webmercator,
x as value,
CASE
WHEN x % 2 = 0 THEN 'even'
ELSE 'odd'
END AS type
from generate_series(-3, 3) x
`;
const defaultLayers = [{
type: 'cartodb',
options: {
sql: POINTS_SQL_1,
aggregation: {
threshold: 1
}
}
}];
function createVectorMapConfig (layers = defaultLayers) {
return {
version: '1.8.0',
layers: layers
};
}
describe('cluster', function () {
describe('w/o aggregation', function () {
it('should return error while fetching disaggregated features', function (done) {
const mapConfig = createVectorMapConfig([{
type: 'cartodb',
options: {
sql: POINTS_SQL_1,
cartocss: TestClient.CARTOCSS.POINTS,
cartocss_version: '2.3.0'
}
}]);
const testClient = new TestClient(mapConfig);
const zoom = 0;
const cartodb_id = 1;
const layerId = 0;
const params = {
response: {
status: 400
}
};
testClient.getClusterFeatures(zoom, cartodb_id, layerId, params, (err, body) => {
if (err) {
return done(err);
}
assert.deepStrictEqual(body, {
errors:[ 'Map f697fb370c6479559ae2f66d684e8227 has no aggregation defined for layer 0' ],
errors_with_context:[
{
layer: {
index: '0',
type: 'cartodb'
},
message: 'Map f697fb370c6479559ae2f66d684e8227 has no aggregation defined for layer 0',
subtype: 'aggregation',
type: 'layer'
}
]
});
testClient.drain(done);
});
});
it('with aggregation disabled should return error while fetching disaggregated features', function (done) {
const mapConfig = createVectorMapConfig([{
type: 'cartodb',
options: {
sql: POINTS_SQL_1,
aggregation: false
}
}]);
const testClient = new TestClient(mapConfig);
const zoom = 0;
const cartodb_id = 1;
const layerId = 0;
const params = {
response: {
status: 400
}
};
testClient.getClusterFeatures(zoom, cartodb_id, layerId, params, (err, body) => {
if (err) {
return done(err);
}
assert.deepStrictEqual(body, {
errors:[ 'Map 7521bcd1029c401289dd651ce91d5d9d has no aggregation defined for layer 0' ],
errors_with_context:[
{
layer: {
index: '0',
type: 'cartodb'
},
message: 'Map 7521bcd1029c401289dd651ce91d5d9d has no aggregation defined for layer 0',
subtype: 'aggregation',
type: 'layer'
}
]
});
testClient.drain(done);
});
});
});
describe('fetch features within a cluster grid', function () {
const suite = [
{
zoom: 0,
cartodb_id: 1,
resolution: 0.5,
expected: [ { cartodb_id: 1, value: -3, type: 'odd' } ]
},
{
zoom: 0,
cartodb_id: 2,
resolution: 0.5,
expected: [ { cartodb_id: 2, value: -2, type: 'even' } ]
},
{
zoom: 0,
cartodb_id: 3,
resolution: 0.5,
expected: [ { cartodb_id: 3, value: -1, type: 'odd' } ]
},
{
zoom: 0,
cartodb_id: 4,
resolution: 0.5,
expected: [ { cartodb_id: 4, value: 0, type: 'even' } ]
},
{
zoom: 0,
cartodb_id: 5,
resolution: 0.5,
expected: [ { cartodb_id: 5, value: 1, type: 'odd' } ]
},
{
zoom: 0,
cartodb_id: 6,
resolution: 0.5,
expected: [ { cartodb_id: 6, value: 2, type: 'even' } ]
},
{
zoom: 0,
cartodb_id: 7,
resolution: 0.5,
expected: [ { cartodb_id: 7, value: 3, type: 'odd' } ]
},
{
zoom: 0,
cartodb_id: 1,
resolution: 1,
expected: [ { cartodb_id: 1, value: -3, type: 'odd' } ]
},
{
zoom: 0,
cartodb_id: 2,
resolution: 1,
expected: [ { cartodb_id: 2, value: -2, type: 'even' } ]
},
{
zoom: 0,
cartodb_id: 3,
resolution: 1,
expected: [ { cartodb_id: 3, value: -1, type: 'odd' } ]
},
{
zoom: 0,
cartodb_id: 4,
resolution: 1,
expected: [ { cartodb_id: 4, value: 0, type: 'even' } ]
},
{
zoom: 0,
cartodb_id: 5,
resolution: 1,
expected: [ { cartodb_id: 5, value: 1, type: 'odd' } ]
},
{
zoom: 0,
cartodb_id: 6,
resolution: 1,
expected: [ { cartodb_id: 6, value: 2, type: 'even' } ]
},
{
zoom: 0,
cartodb_id: 7,
resolution: 1,
expected: [ { cartodb_id: 7, value: 3, type: 'odd' } ]
},
{
zoom: 0,
cartodb_id: 1,
resolution: 50,
expected: [
{ cartodb_id: 1, value: -3, type: 'odd' },
{ cartodb_id: 2, value: -2, type: 'even' }
]
},
{
zoom: 0,
cartodb_id: 5,
resolution: 50,
expected: [
{ cartodb_id: 4, value: 0, type: 'even' },
{ cartodb_id: 5, value: 1, type: 'odd' },
{ cartodb_id: 6, value: 2, type: 'even' },
{ cartodb_id: 7, value: 3, type: 'odd' }
]
},
{
zoom: 1,
cartodb_id: 1,
resolution: 1,
expected: [ { cartodb_id: 1, value: -3, type: 'odd' } ]
},
{
zoom: 1,
cartodb_id: 2,
resolution: 1,
expected: [ { cartodb_id: 2, value: -2, type: 'even' } ]
},
{
zoom: 1,
cartodb_id: 3,
resolution: 1,
expected: [ { cartodb_id: 3, value: -1, type: 'odd' } ]
},
{
zoom: 1,
cartodb_id: 4,
resolution: 1,
expected: [ { cartodb_id: 4, value: 0, type: 'even' } ]
},
{
zoom: 1,
cartodb_id: 5,
resolution: 1,
expected: [ { cartodb_id: 5, value: 1, type: 'odd' } ]
},
{
zoom: 1,
cartodb_id: 6,
resolution: 1,
expected: [ { cartodb_id: 6, value: 2, type: 'even' } ]
},
{
zoom: 1,
cartodb_id: 7,
resolution: 1,
expected: [ { cartodb_id: 7, value: 3, type: 'odd' } ]
},
{
zoom: 1,
cartodb_id: 1,
resolution: 50,
expected: [
{ cartodb_id: 1, value: -3, type: 'odd' },
{ cartodb_id: 2, value: -2, type: 'even'}
]
},
{
zoom: 1,
cartodb_id: 5,
resolution: 50,
expected: [
{ cartodb_id: 4, value: 0, type: 'even' },
{ cartodb_id: 5, value: 1, type: 'odd' }
]
}
];
suite.forEach(({ zoom, cartodb_id, resolution, expected }) => {
const description = `should get features for z: ${zoom} cartodb_id: ${cartodb_id}, res: ${resolution}`;
it(description, function (done) {
const mapConfig = createVectorMapConfig([{
type: 'cartodb',
options: {
sql: POINTS_SQL_1,
aggregation: {
threshold: 1,
resolution: resolution
}
}
}]);
const testClient = new TestClient(mapConfig);
const layerId = 0;
const params = {};
testClient.getClusterFeatures(zoom, cartodb_id, layerId, params, (err, body) => {
if (err) {
return done(err);
}
assert.deepStrictEqual(body.rows, expected);
testClient.drain(done);
});
});
});
});
describe('valid aggregation input', function () {
const suite = [
{
zoom: 0,
cartodb_id: 1,
resolution: 1,
aggregation: { columns: ['type'] },
expected: [ { _cdb_feature_count: 1, type: 'odd' } ]
},
{
zoom: 0,
cartodb_id: 2,
resolution: 1,
aggregation: { columns: ['type'] },
expected: [ { _cdb_feature_count: 1, type: 'even' } ]
},
{
zoom: 0,
cartodb_id: 3,
resolution: 1,
aggregation: { columns: ['type'] },
expected: [ { _cdb_feature_count: 1, type: 'odd' } ]
},
{
zoom: 0,
cartodb_id: 4,
resolution: 1,
aggregation: { columns: ['type'] },
expected: [ { _cdb_feature_count: 1, type: 'even' } ]
},
{
zoom: 0,
cartodb_id: 5,
resolution: 1,
aggregation: { columns: ['type'] },
expected: [ { _cdb_feature_count: 1, type: 'odd' } ]
},
{
zoom: 0,
cartodb_id: 6,
resolution: 1,
aggregation: { columns: ['type'] },
expected: [ { _cdb_feature_count: 1, type: 'even' } ]
},
{
zoom: 0,
cartodb_id: 7,
resolution: 1,
aggregation: { columns: ['type'] },
expected: [ { _cdb_feature_count: 1, type: 'odd' } ]
},
{
zoom: 0,
cartodb_id: 1,
resolution: 50,
aggregation: { columns: ['type'] },
expected: [
{ _cdb_feature_count: 1, type: 'even' },
{ _cdb_feature_count: 1, type: 'odd' }
]
},
{
zoom: 0,
cartodb_id: 5,
resolution: 50,
aggregation: { columns: ['type'] },
expected: [
{ _cdb_feature_count: 2, type: 'even' },
{ _cdb_feature_count: 2, type: 'odd' }
]
},
{
zoom: 0,
cartodb_id: 1,
resolution: 1,
aggregation: {
columns: [ 'type' ],
expressions: {
max_value: {
aggregate_function: 'max',
aggregated_column: 'value',
}
}
},
expected: [ { _cdb_feature_count: 1, type: 'odd', max_value: -3 } ]
},
{
zoom: 0,
cartodb_id: 2,
resolution: 1,
aggregation: {
columns: [ 'type' ],
expressions: {
max_value: {
aggregate_function: 'max',
aggregated_column: 'value',
}
}
},
expected: [ { _cdb_feature_count: 1, type: 'even', max_value: -2 } ]
},
{
zoom: 0,
cartodb_id: 3,
resolution: 1,
aggregation: {
columns: [ 'type' ],
expressions: {
max_value: {
aggregate_function: 'max',
aggregated_column: 'value',
}
}
},
expected: [ { _cdb_feature_count: 1, type: 'odd', max_value: -1 } ]
},
{
zoom: 0,
cartodb_id: 4,
resolution: 1,
aggregation: {
columns: [ 'type' ],
expressions: {
max_value: {
aggregate_function: 'max',
aggregated_column: 'value',
}
}
},
expected: [ { _cdb_feature_count: 1, type: 'even', max_value: 0 } ]
},
{
zoom: 0,
cartodb_id: 5,
resolution: 1,
aggregation: {
columns: [ 'type' ],
expressions: {
max_value: {
aggregate_function: 'max',
aggregated_column: 'value',
}
}
},
expected: [ { _cdb_feature_count: 1, type: 'odd', max_value: 1 } ]
},
{
zoom: 0,
cartodb_id: 6,
resolution: 1,
aggregation: {
columns: [ 'type' ],
expressions: {
max_value: {
aggregate_function: 'max',
aggregated_column: 'value',
}
}
},
expected: [ { _cdb_feature_count: 1, type: 'even', max_value: 2 } ]
},
{
zoom: 0,
cartodb_id: 7,
resolution: 1,
aggregation: {
columns: [ 'type' ],
expressions: {
max_value: {
aggregate_function: 'max',
aggregated_column: 'value',
}
}
},
expected: [ { _cdb_feature_count: 1, type: 'odd', max_value: 3 } ]
},
{
zoom: 0,
cartodb_id: 1,
resolution: 50,
aggregation: {
columns: [ 'type' ],
expressions: {
max_value: {
aggregate_function: 'max',
aggregated_column: 'value',
}
}
},
expected: [
{ _cdb_feature_count: 1, type: 'even', max_value: -2 },
{ _cdb_feature_count: 1, type: 'odd', max_value: -3 }
]
},
{
zoom: 0,
cartodb_id: 5,
resolution: 50,
aggregation: {
columns: [ 'type' ],
expressions: {
max_value: {
aggregate_function: 'max',
aggregated_column: 'value',
}
}
},
expected: [
{ _cdb_feature_count: 2, type: 'even', max_value: 2 },
{ _cdb_feature_count: 2, type: 'odd', max_value: 3 }
]
}
];
suite.forEach(({ zoom, cartodb_id, resolution, aggregation, expected }) => {
it(`should aggregate by type; z: ${zoom}, cartodb_id: ${cartodb_id}, res: ${resolution}`, function (done) {
const mapConfig = createVectorMapConfig([{
type: 'cartodb',
options: {
sql: POINTS_SQL_1,
aggregation: {
threshold: 1,
resolution
}
}
}]);
const testClient = new TestClient(mapConfig);
const layerId = 0;
const params = { aggregation };
testClient.getClusterFeatures(zoom, cartodb_id, layerId, params, (err, body) => {
if (err) {
return done(err);
}
assert.deepStrictEqual(body.rows, expected);
testClient.drain(done);
});
});
});
});
describe('invalid aggregation input', function () {
const expectedColumnsError = {
errors:[ 'Invalid aggregation input, columns should be and array of column names' ],
errors_with_context:[
{
layer: {
index: '0',
type: 'cartodb'
},
message: 'Invalid aggregation input, columns should be and array of column names',
subtype: 'aggregation',
type: 'layer'
}
]
};
const expectedExpressionsError = {
errors:[ 'Invalid aggregation input, expressions should be and object with valid functions' ],
errors_with_context:[
{
layer: {
index: '0',
type: 'cartodb'
},
message: 'Invalid aggregation input, expressions should be and object with valid functions',
subtype: 'aggregation',
type: 'layer'
}
]
};
const invalidFunctionExpressionsError = {
errors:[ 'function wadus(integer) does not exist' ],
errors_with_context:[
{
message: 'function wadus(integer) does not exist',
type: 'unknown'
}
]
};
const invalidColumnExpressionsError = {
errors:[ 'column \"wadus\" does not exist' ],
errors_with_context:[
{
message: 'column \"wadus\" does not exist',
type: 'unknown'
}
]
};
const expectedAggregatedColumnError = {
errors:[ 'Invalid aggregation input, aggregated column should be an string' ],
errors_with_context:[
{
layer: {
index: '0',
type: 'cartodb'
},
message: 'Invalid aggregation input, aggregated column should be an string',
subtype: 'aggregation',
type: 'layer'
}
]
};
const expectedAggregateFunctionError = {
errors:[ 'Invalid aggregation input, aggregate function should be an string' ],
errors_with_context:[
{
layer: {
index: '0',
type: 'cartodb'
},
message: 'Invalid aggregation input, aggregate function should be an string',
subtype: 'aggregation',
type: 'layer'
}
]
};
const suite = [
{
description: 'empty aggregation object should respond with error',
zoom: 0,
cartodb_id: 1,
resolution: 1,
aggregation: {},
expected: expectedColumnsError
},
{
description: 'empty aggregation array should respond with error',
zoom: 0,
cartodb_id: 1,
resolution: 1,
aggregation: [],
expected: expectedColumnsError
},
{
description: 'aggregation as string should respond with error',
zoom: 0,
cartodb_id: 1,
resolution: 1,
aggregation: 'wadus',
expected: expectedColumnsError
},
{
description: 'empty columns array should respond with error',
zoom: 0,
cartodb_id: 1,
resolution: 1,
aggregation: { columns: [] },
expected: expectedColumnsError
},
{
description: 'empty columns object should respond with error',
zoom: 0,
cartodb_id: 1,
resolution: 1,
aggregation: { columns: {} },
expected: expectedColumnsError
},
{
description: 'columns as string should respond with error',
zoom: 0,
cartodb_id: 1,
resolution: 1,
aggregation: { columns: 'wadus' },
expected: expectedColumnsError
},
{
description: 'columns as null should respond with error',
zoom: 0,
cartodb_id: 1,
resolution: 1,
aggregation: { columns: null },
expected: expectedColumnsError
},
{
description: 'empty expressions array should respond with error',
zoom: 0,
cartodb_id: 1,
resolution: 1,
aggregation: { columns: [ 'type' ], expressions: [] },
expected: expectedExpressionsError
},
{
description: 'empty expressions number should respond with error',
zoom: 0,
cartodb_id: 1,
resolution: 1,
aggregation: { columns: [ 'type' ], expressions: 1 },
expected: expectedExpressionsError
},
{
description: 'expressions as string should respond with error',
zoom: 0,
cartodb_id: 1,
resolution: 1,
aggregation: { columns: [ 'type' ], expressions: 'wadus' },
expected: expectedExpressionsError
},
{
description: 'expressions as null should respond with error',
zoom: 0,
cartodb_id: 1,
resolution: 1,
aggregation: { columns: [ 'type' ], expressions: null },
expected: expectedExpressionsError
},
{
description: 'invalid aggregation function should respond with error',
zoom: 0,
cartodb_id: 1,
resolution: 1,
aggregation: {
columns: [ 'type' ],
expressions: {
max_value: {
aggregate_function: 'wadus',
aggregated_column: 'value'
}
}
},
expected: invalidFunctionExpressionsError
},
{
description: 'invalid aggregation column should respond with error',
zoom: 0,
cartodb_id: 1,
resolution: 1,
aggregation: {
columns: [ 'type' ],
expressions: {
max_value: {
aggregate_function: 'max',
aggregated_column: 'wadus'
}
}
},
status: 404,
expected: invalidColumnExpressionsError
},
{
description: 'aggregated column as non string should respond with error',
zoom: 0,
cartodb_id: 1,
resolution: 1,
aggregation: {
columns: [ 'type' ],
expressions: {
max_value: {
aggregate_function: 'max',
aggregated_column: 1
}
}
},
expected: expectedAggregatedColumnError
},
{
description: 'aggregate function as non string should respond with error',
zoom: 0,
cartodb_id: 1,
resolution: 1,
aggregation: {
columns: [ 'type' ],
expressions: {
max_value: {
aggregate_function: 1,
aggregated_column: 'value'
}
}
},
expected: expectedAggregateFunctionError
}
];
suite.forEach(({ description, zoom, cartodb_id, resolution, aggregation, expected, status = 400 }) => {
it(description, function (done) {
const mapConfig = createVectorMapConfig([{
type: 'cartodb',
options: {
sql: POINTS_SQL_1,
aggregation: {
threshold: 1,
resolution
}
}
}]);
const testClient = new TestClient(mapConfig);
const layerId = 0;
const params = {
response: { status },
aggregation
};
testClient.getClusterFeatures(zoom, cartodb_id, layerId, params, (err, body) => {
if (err) {
return done(err);
}
assert.deepStrictEqual(body, expected);
testClient.drain(done);
});
});
});
});
});
+20
View File
@@ -90,6 +90,26 @@ describe('histogram-dataview', function() {
});
});
it('should work with min >= start and max <= end, autodetect bins', function(done) {
var params = {
start: 50,
end: 500
};
this.testClient = new TestClient(mapConfig, 1234);
this.testClient.getDataview('pop_max_histogram', params, function(err, dataview) {
assert.ok(!err, err);
assert.ok(6 === dataview.bins_count, 'Unexpected bin count: ' + dataview.bins_count);
assert.ok(6 === dataview.bins.length, 'Unexpected number of bins: ' + dataview.bins.length);
dataview.bins.forEach(function(bin) {
assert.ok(bin.min >= params.start, 'bin min < start: ' + JSON.stringify(bin));
assert.ok(bin.max <= params.end, 'bin max > end: ' + JSON.stringify(bin));
});
done();
});
});
it('should get bin_width right when max > min in filter', function(done) {
var params = {
bins: 10,
+1 -2
View File
@@ -7,8 +7,7 @@ const mapConfigFactory = require('../fixtures/test_mapconfigFactory');
const serverOptions = require('../../lib/cartodb/server_options');
const usePgMvtRenderer = serverOptions.renderer.mvt.usePostGIS;
const postgisVersion = process.env.POSTGIS_VERSION;
const describe_mvt = postgisVersion >= '20400' || !usePgMvtRenderer ? describe : describe.skip;
const describe_mvt = !usePgMvtRenderer ? describe : describe.skip;
describe_mvt('date-wrapping', () => {
let testClient;
+1 -3
View File
@@ -7,12 +7,10 @@ const TestClient = require('../support/test-client');
const serverOptions = require('../../lib/cartodb/server_options');
describe('layergroup metadata', function () {
const usePgMvtRenderer = process.env.POSTGIS_VERSION >= '20400';
const originalUsePostGIS = serverOptions.renderer.mvt.usePostGIS;
before(function () {
serverOptions.renderer.mvt.usePostGIS = usePgMvtRenderer;
serverOptions.renderer.mvt.usePostGIS = true;
});
after(function () {
+8 -9
View File
@@ -6,17 +6,16 @@ const assert = require('../support/assert');
const TestClient = require('../support/test-client');
const serverOptions = require('../../lib/cartodb/server_options');
const suites = [{
desc: 'mapnik',
usePostGIS: false
}];
if (process.env.POSTGIS_VERSION >= '20400') {
suites.push({
const suites = [
{
desc: 'mapnik',
usePostGIS: false
},
{
desc: 'postgis',
usePostGIS: true
});
}
}
];
describe('mvt regressions', function () {
+240 -242
View File
@@ -22,273 +22,271 @@ function createMapConfig(sql = TestClient.SQL.ONE_POINT) {
}
describe('mvt (mapnik)', mvt(false));
if (process.env.POSTGIS_VERSION >= '20400') {
describe('mvt (postgis)', mvt(true));
}
describe('mvt (postgis)', mvt(true));
function mvt(usePostGIS) {
return function () {
const originalUsePostGIS = serverOptions.renderer.mvt.usePostGIS;
before(function () {
serverOptions.renderer.mvt.usePostGIS = usePostGIS;
});
after(function (){
serverOptions.renderer.mvt.usePostGIS = originalUsePostGIS;
});
return function () {
const originalUsePostGIS = serverOptions.renderer.mvt.usePostGIS;
before(function () {
serverOptions.renderer.mvt.usePostGIS = usePostGIS;
});
after(function (){
serverOptions.renderer.mvt.usePostGIS = originalUsePostGIS;
});
describe('named map tile', function () {
it('should get default named vector tile', function (done) {
const apikeyToken = 1234;
const templateName = `mvt-template-${usePostGIS ? 'postgis' : 'mapnik'}`;
const template = {
version: '0.0.1',
name: templateName,
placeholders: {
buffersize: {
type: 'number',
default: 0
describe('named map tile', function () {
it('should get default named vector tile', function (done) {
const apikeyToken = 1234;
const templateName = `mvt-template-${usePostGIS ? 'postgis' : 'mapnik'}`;
const template = {
version: '0.0.1',
name: templateName,
placeholders: {
buffersize: {
type: 'number',
default: 0
}
},
layergroup: {
version: '1.7.0',
layers: [{
type: 'cartodb',
options: {
sql: 'select * from populated_places_simple_reduced limit 10',
cartocss: TestClient.CARTOCSS.POINTS,
cartocss_version: '2.3.0',
}
}]
}
};
const testClient = new TestClient(template, apikeyToken);
testClient.keysToDelete['map_tpl|localhost'] = 0;
testClient.getNamedTile(templateName, 0, 0, 0, 'mvt', {}, (err, res, tile) => {
if (err) {
return done(err);
}
const tileJSON = tile.toJSON();
assert.equal(tileJSON[0].features.length, 10);
testClient.drain(done);
});
});
});
describe('analysis-layers-dataviews-mvt', function () {
function createMapConfig(layers, dataviews, analysis) {
return {
version: '1.5.0',
layers: layers,
dataviews: dataviews || {},
analyses: analysis || []
};
}
var CARTOCSS = [
"#points {",
" marker-fill-opacity: 1.0;",
" marker-line-color: #FFF;",
" marker-line-width: 0.5;",
" marker-line-opacity: 1.0;",
" marker-placement: point;",
" marker-type: ellipse;",
" marker-width: 8;",
" marker-fill: red;",
" marker-allow-overlap: true;",
"}"
].join('\n');
var mapConfig = createMapConfig(
[
{
"type": "cartodb",
"options": {
"source": {
"id": "2570e105-7b37-40d2-bdf4-1af889598745"
},
"cartocss": CARTOCSS,
"cartocss_version": "2.3.0"
}
}
],
{
pop_max_histogram: {
source: {
id: '2570e105-7b37-40d2-bdf4-1af889598745'
},
type: 'histogram',
options: {
column: 'pop_max'
}
}
},
layergroup: {
version: '1.7.0',
layers: [{
type: 'cartodb',
options: {
sql: 'select * from populated_places_simple_reduced limit 10',
cartocss: TestClient.CARTOCSS.POINTS,
cartocss_version: '2.3.0',
[
{
"id": "2570e105-7b37-40d2-bdf4-1af889598745",
"type": "source",
"params": {
"query": "select * from populated_places_simple_reduced"
}
}]
}
};
const testClient = new TestClient(template, apikeyToken);
testClient.keysToDelete['map_tpl|localhost'] = 0;
testClient.getNamedTile(templateName, 0, 0, 0, 'mvt', {}, (err, res, tile) => {
if (err) {
return done(err);
}
const tileJSON = tile.toJSON();
assert.equal(tileJSON[0].features.length, 10);
testClient.drain(done);
});
});
});
describe('analysis-layers-dataviews-mvt', function () {
function createMapConfig(layers, dataviews, analysis) {
return {
version: '1.5.0',
layers: layers,
dataviews: dataviews || {},
analyses: analysis || []
};
}
var CARTOCSS = [
"#points {",
" marker-fill-opacity: 1.0;",
" marker-line-color: #FFF;",
" marker-line-width: 0.5;",
" marker-line-opacity: 1.0;",
" marker-placement: point;",
" marker-type: ellipse;",
" marker-width: 8;",
" marker-fill: red;",
" marker-allow-overlap: true;",
"}"
].join('\n');
var mapConfig = createMapConfig(
[
{
"type": "cartodb",
"options": {
"source": {
"id": "2570e105-7b37-40d2-bdf4-1af889598745"
},
"cartocss": CARTOCSS,
"cartocss_version": "2.3.0"
}
}
],
]
);
it('should get pop_max column from dataview', function (done) {
var testClient = new TestClient(mapConfig);
testClient.getTile(0, 0, 0, { format: 'mvt', layers: 0 }, function (err, res, MVT) {
var geojsonTile = JSON.parse(MVT.toGeoJSONSync(0));
assert.ok(!err, err);
assert.ok(Array.isArray(geojsonTile.features));
assert.ok(geojsonTile.features.length > 0);
var feature = geojsonTile.features[0];
assert.ok(feature.properties.hasOwnProperty('pop_max'), 'Missing pop_max property');
testClient.drain(done);
});
});
});
const testCases = [
{
pop_max_histogram: {
source: {
id: '2570e105-7b37-40d2-bdf4-1af889598745'
},
type: 'histogram',
options: {
column: 'pop_max'
desc: 'should get empty mvt with code 204 (no content)',
coords: { z: 0, x: 0, y: 0 },
format: 'mvt',
response: {
status: 204,
headers: {
'Content-Type': undefined
}
}
},
mapConfig: createMapConfig(TestClient.SQL.EMPTY)
},
[
{
"id": "2570e105-7b37-40d2-bdf4-1af889598745",
"type": "source",
"params": {
"query": "select * from populated_places_simple_reduced"
{
desc: 'should get mvt tile with code 200 (ok)',
coords: { z: 0, x: 0, y: 0 },
format: 'mvt',
response: {
status: 200,
headers: {
'Content-Type': 'application/x-protobuf'
}
}
]
);
},
mapConfig: createMapConfig()
}
];
it('should get pop_max column from dataview', function (done) {
var testClient = new TestClient(mapConfig);
testCases.forEach(function (test) {
it(test.desc, done => {
var testClient = new TestClient(test.mapConfig);
const { z, x, y } = test.coords;
const { format, response } = test;
testClient.getTile(0, 0, 0, { format: 'mvt', layers: 0 }, function (err, res, MVT) {
var geojsonTile = JSON.parse(MVT.toGeoJSONSync(0));
assert.ok(!err, err);
assert.ok(Array.isArray(geojsonTile.features));
assert.ok(geojsonTile.features.length > 0);
var feature = geojsonTile.features[0];
assert.ok(feature.properties.hasOwnProperty('pop_max'), 'Missing pop_max property');
testClient.drain(done);
testClient.getTile(z, x, y, { format, response }, err => {
assert.ifError(err);
testClient.drain(done);
});
});
});
});
describe('overviews', function () {
function createMapConfig(layers, dataviews, analysis) {
return {
version: '1.8.0',
layers: layers,
dataviews: dataviews || {},
analyses: analysis || []
};
}
const testCases = [
{
desc: 'should get empty mvt with code 204 (no content)',
coords: { z: 0, x: 0, y: 0 },
format: 'mvt',
response: {
status: 204,
headers: {
'Content-Type': undefined
}
},
mapConfig: createMapConfig(TestClient.SQL.EMPTY)
},
{
desc: 'should get mvt tile with code 200 (ok)',
coords: { z: 0, x: 0, y: 0 },
format: 'mvt',
response: {
status: 200,
headers: {
'Content-Type': 'application/x-protobuf'
}
},
mapConfig: createMapConfig()
}
];
testCases.forEach(function (test) {
it(test.desc, done => {
var testClient = new TestClient(test.mapConfig);
const { z, x, y } = test.coords;
const { format, response } = test;
testClient.getTile(z, x, y, { format, response }, err => {
assert.ifError(err);
testClient.drain(done);
});
});
});
describe('overviews', function () {
function createMapConfig(layers, dataviews, analysis) {
return {
version: '1.8.0',
layers: layers,
dataviews: dataviews || {},
analyses: analysis || []
};
}
it('should use overviews to fetch mvt data', function (done) {
const mapConfig = createMapConfig(
[
{
"type": "cartodb",
"options": {
"sql": 'SELECT * FROM test_table_overviews',
"cartocss": TestClient.CARTOCSS.POINTS,
"cartocss_version": "2.3.0"
it('should use overviews to fetch mvt data', function (done) {
const mapConfig = createMapConfig(
[
{
"type": "cartodb",
"options": {
"sql": 'SELECT * FROM test_table_overviews',
"cartocss": TestClient.CARTOCSS.POINTS,
"cartocss_version": "2.3.0"
}
}
}
]
);
]
);
const testClient = new TestClient(mapConfig);
const [ z, x, y ] = [ 0, 0, 0 ];
const options = { format: 'mvt' };
const testClient = new TestClient(mapConfig);
const [ z, x, y ] = [ 0, 0, 0 ];
const options = { format: 'mvt' };
testClient.getTile(z, x, y, options, function (err, res, mvt) {
assert.ifError(err);
testClient.getTile(z, x, y, options, function (err, res, mvt) {
assert.ifError(err);
const geojsonTile = JSON.parse(mvt.toGeoJSONSync(0));
const geojsonTile = JSON.parse(mvt.toGeoJSONSync(0));
assert.ok(Array.isArray(geojsonTile.features));
assert.ok(geojsonTile.features.length > 0);
assert.ok(Array.isArray(geojsonTile.features));
assert.ok(geojsonTile.features.length > 0);
const feature = geojsonTile.features[0];
const feature = geojsonTile.features[0];
assert.ok(feature.properties.hasOwnProperty('_feature_count'), 'Missing _feature_count property');
assert.equal(feature.properties.cartodb_id, 1);
assert.equal(feature.properties.name, 'Hawai');
assert.equal(feature.properties._feature_count, 5); // original table has _feature_count = 1
assert.equal(feature.properties.value, 3); // original table has value = 1.0
assert.ok(feature.properties.hasOwnProperty('_feature_count'), 'Missing _feature_count property');
assert.equal(feature.properties.cartodb_id, 1);
assert.equal(feature.properties.name, 'Hawai');
assert.equal(feature.properties._feature_count, 5); // original table has _feature_count = 1
assert.equal(feature.properties.value, 3); // original table has value = 1.0
testClient.drain(done);
testClient.drain(done);
});
});
it('first layer should use overviews, second layer shouldn\'t', function (done) {
const mapConfig = createMapConfig(
[
{
"type": "cartodb",
"options": {
"sql": 'SELECT * FROM test_table_overviews',
"cartocss": TestClient.CARTOCSS.POINTS,
"cartocss_version": "2.3.0"
}
},
{
"type": "cartodb",
"options": {
"sql": 'SELECT * FROM test_table',
"cartocss": TestClient.CARTOCSS.POINTS,
"cartocss_version": "2.3.0"
}
}
]
);
const testClient = new TestClient(mapConfig);
const [ z, x, y ] = [ 0, 0, 0 ];
const options = { format: 'mvt' };
testClient.getTile(z, x, y, options, function (err, res, mvt) {
assert.ifError(err);
const tileWithOverviews = JSON.parse(mvt.toGeoJSONSync(0));
const tileWithoutOverviews = JSON.parse(mvt.toGeoJSONSync(1));
assert.ok(Array.isArray(tileWithOverviews.features));
assert.equal(tileWithOverviews.features.length, 1);
assert.equal(tileWithOverviews.features[0].properties._feature_count, 5);
assert.ok(Array.isArray(tileWithoutOverviews.features));
assert.equal(tileWithoutOverviews.features.length, 5);
assert.equal(tileWithoutOverviews.features[0].properties._feature_count, undefined);
testClient.drain(done);
});
});
});
it('first layer should use overviews, second layer shouldn\'t', function (done) {
const mapConfig = createMapConfig(
[
{
"type": "cartodb",
"options": {
"sql": 'SELECT * FROM test_table_overviews',
"cartocss": TestClient.CARTOCSS.POINTS,
"cartocss_version": "2.3.0"
}
},
{
"type": "cartodb",
"options": {
"sql": 'SELECT * FROM test_table',
"cartocss": TestClient.CARTOCSS.POINTS,
"cartocss_version": "2.3.0"
}
}
]
);
const testClient = new TestClient(mapConfig);
const [ z, x, y ] = [ 0, 0, 0 ];
const options = { format: 'mvt' };
testClient.getTile(z, x, y, options, function (err, res, mvt) {
assert.ifError(err);
const tileWithOverviews = JSON.parse(mvt.toGeoJSONSync(0));
const tileWithoutOverviews = JSON.parse(mvt.toGeoJSONSync(1));
assert.ok(Array.isArray(tileWithOverviews.features));
assert.equal(tileWithOverviews.features.length, 1);
assert.equal(tileWithOverviews.features[0].properties._feature_count, 5);
assert.ok(Array.isArray(tileWithoutOverviews.features));
assert.equal(tileWithoutOverviews.features.length, 5);
assert.equal(tileWithoutOverviews.features[0].properties._feature_count, undefined);
testClient.drain(done);
});
});
});
};
};
}
+3 -3
View File
@@ -276,11 +276,11 @@ describe('rate limit middleware', function () {
});
});
const describe_pg = process.env.POSTGIS_VERSION >= '20400' ? describe : describe.skip;
const originalUsePostGIS = serverOptions.renderer.mvt.usePostGIS;
describe('rate limit and vector tiles (mapnik)', () => { rateLimitAndVectorTilesTest(false); });
describe_pg('rate limit and vector tiles (postgis)', () => { rateLimitAndVectorTilesTest(true); });
describe('rate limit and vector tiles (mapnik)', () => rateLimitAndVectorTilesTest(false));
describe('rate limit and vector tiles (postgis)', () => rateLimitAndVectorTilesTest(true));
function rateLimitAndVectorTilesTest(usePostGIS) {
@@ -6,17 +6,16 @@ var assert = require('../../support/assert');
var TestClient = require('../../support/test-client');
const serverOptions = require('../../../lib/cartodb/server_options');
const suites = [{
desc: 'mvt (mapnik)',
usePostGIS: false
}];
if (process.env.POSTGIS_VERSION >= '20400') {
suites.push({
const suites = [
{
desc: 'mvt (mapnik)',
usePostGIS: false
},
{
desc: 'mvt (postgis)',
usePostGIS: true
});
}
}
];
suites.forEach(({desc, usePostGIS}) => {
describe(`[${desc}] Create mapnik layergroup`, function() {
+2 -3
View File
@@ -6,11 +6,10 @@ const assert = require('../support/assert');
const TestClient = require('../support/test-client');
const serverOptions = require('../../lib/cartodb/server_options');
const describe_pg = process.env.POSTGIS_VERSION >= '20400' ? describe : describe.skip;
const originalUsePostGIS = serverOptions.renderer.mvt.usePostGIS;
describe('tilejson via mapnik renderer', () => { tileJsonSuite(false); });
describe_pg('tilejson via postgis renderer', () => { tileJsonSuite(true); });
describe('tilejson via mapnik renderer', () => tileJsonSuite(false));
describe('tilejson via postgis renderer', () => tileJsonSuite(true));
function tileJsonSuite(usePostGIS) {
@@ -394,9 +394,8 @@ describe('user database timeout limit', function () {
});
});
const describe_pg = process.env.POSTGIS_VERSION >= '20400' ? describe : describe.skip;
describe('fetching vector tiles via mapnik renderer', () => { testFetchingVectorTiles(false); });
describe_pg('fetching vector tiles via postgis renderer', () => { testFetchingVectorTiles(true); });
describe('fetching vector tiles via mapnik renderer', () => testFetchingVectorTiles(false));
describe('fetching vector tiles via postgis renderer', () => testFetchingVectorTiles(true));
function testFetchingVectorTiles(usePostGIS) {
const originalUsePostGIS = serverOptions.renderer.mvt.usePostGIS;
+8 -9
View File
@@ -69,17 +69,16 @@ const INVALID_FORMAT_ERROR = {
]
};
const suites = [{
desc: 'mvt (mapnik)',
usePostGIS: false
}];
if (process.env.POSTGIS_VERSION >= '20400') {
suites.push({
const suites = [
{
desc: 'mvt (mapnik)',
usePostGIS: false
},
{
desc: 'mvt (postgis)',
usePostGIS: true
});
}
}
];
suites.forEach((suite) => {
const { desc, usePostGIS } = suite;
-21
View File
@@ -73,20 +73,6 @@ echo "PUBLICPASS: ${PUBLICPASS}"
echo "TESTUSER: ${TESTUSER}"
echo "TESTPASS: ${TESTPASS}"
# Sets the env variable POSTGIS_VERSION as Major * 10000 + Minor * 100 + Patch
# For example, for 2.4.5 ~> 20405
auto_postgis_version() {
local POSTGIS_STR=$(psql -c "Select default_version from pg_available_extensions WHERE name = 'postgis';" -t);
local pg_version=$(echo $POSTGIS_STR | awk -F '.' '{print $1 * 10000 + $2 * 100 + $3}')
echo $pg_version
}
if [ -z "$POSTGIS_VERSION" ]; then
export POSTGIS_VERSION=$(auto_postgis_version)
echo "POSTGIS_VERSION: ${POSTGIS_VERSION}"
fi
if test x"$PREPARE_PGSQL" = xyes; then
echo "preparing postgres..."
@@ -110,13 +96,6 @@ if test x"$PREPARE_PGSQL" = xyes; then
ALL_SQL_SCRIPTS="${REMOTE_SQL_SCRIPTS} ${LOCAL_SQL_SCRIPTS}"
for i in ${ALL_SQL_SCRIPTS}
do
# Strip PARALLEL labels for PostgreSQL releases before 9.6
if [ $PG_PARALLEL -eq 0 ]; then
TMPFILE=$(mktemp /tmp/$(basename $0).XXXXXXXX)
sed -e 's/PARALLEL \= [A-Z]*,/''/g' \
-e 's/PARALLEL [A-Z]*/''/g' sql/$i.sql > $TMPFILE
mv $TMPFILE sql/$i.sql
fi
cat sql/${i}.sql |
sed -e 's/cartodb\./public./g' -e "s/''cartodb''/''public''/g" |
sed "s/:PUBLICUSER/${PUBLICUSER}/" |
+105
View File
@@ -620,6 +620,111 @@ TestClient.prototype.getFeatureAttributes = function(featureId, layerId, params,
);
};
TestClient.prototype.getClusterFeatures = function (zoom, clusterId, layerId, params, callback) {
var self = this;
if (!callback) {
callback = params;
params = {};
}
var extraParams = {};
if (this.apiKey) {
extraParams.api_key = this.apiKey;
}
// if (params && params.filters) {
// extraParams.filters = JSON.stringify(params.filters);
// }
var url = '/api/v1/map';
if (Object.keys(extraParams).length > 0) {
url += '?' + qs.stringify(extraParams);
}
var expectedResponse = params.response || {
status: 200,
headers: {
'Content-Type': 'application/json; charset=utf-8'
}
};
step(
function createLayergroup() {
var next = this;
assert.response(self.server,
{
url: url,
method: 'POST',
headers: {
host: 'localhost',
'Content-Type': 'application/json'
},
data: JSON.stringify(self.mapConfig)
},
{
status: 200,
headers: {
'Content-Type': 'application/json; charset=utf-8'
}
},
function(res, err) {
if (err) {
return next(err);
}
var parsedBody = JSON.parse(res.body);
if (parsedBody.layergroupid) {
self.keysToDelete['map_cfg|' + LayergroupToken.parse(parsedBody.layergroupid).token] = 0;
self.keysToDelete['user:localhost:mapviews:global'] = 5;
}
return next(null, parsedBody.layergroupid);
}
);
},
function getCLusterFeatures (err, layergroupId) {
assert.ifError(err);
var next = this;
let queryParams = '';
if (params.aggregation) {
queryParams = qs.stringify({ aggregation: JSON.stringify(params.aggregation) });
}
url = `/api/v1/map/${layergroupId}/${layerId}/${zoom}/cluster/${clusterId}?${queryParams}`;
assert.response(self.server,
{
url: url,
method: 'GET',
headers: {
host: 'localhost'
}
},
expectedResponse,
function(res, err) {
if (err) {
return next(err);
}
next(null, JSON.parse(res.body));
}
);
},
function finish(err, attributes) {
if (err) {
return callback(err);
}
return callback(null, attributes);
}
);
};
TestClient.prototype.getTile = function(z, x, y, params, callback) {
var self = this;