Windshaft-cartodb/test/acceptance/ported/support/test-client.js

519 lines
15 KiB
JavaScript

'use strict';
var testHelper = require('../../../support/test-helper');
var LayergroupToken = require('../../../../lib/models/layergroup-token');
var step = require('step');
var assert = require('../../../support/assert');
var _ = require('underscore');
var querystring = require('querystring');
const mapnik = require('@carto/mapnik');
var CartodbServer = require('../../../../lib/server');
var PortedServerOptions = require('./ported-server-options');
var DEFAULT_POINT_STYLE = [
'#layer {',
' marker-fill: #FF6600;',
' marker-opacity: 1;',
' marker-width: 16;',
' marker-line-color: white;',
' marker-line-width: 3;',
' marker-line-opacity: 0.9;',
' marker-placement: point;',
' marker-type: ellipse;',
' marker-allow-overlap: true;',
'}'
].join('');
module.exports = {
createLayergroup: createLayergroup,
withLayergroup: withLayergroup,
singleLayerMapConfig: singleLayerMapConfig,
defaultTableMapConfig: defaultTableMapConfig,
getStaticBbox: getStaticBbox,
getStaticCenter: getStaticCenter,
getGrid: getGrid,
getGridJsonp: getGridJsonp,
getTorque: getTorque,
getTile: getTile,
getTileLayer: getTileLayer
};
var server;
function getServer () {
if (server) {
return server;
}
server = new CartodbServer(PortedServerOptions);
server.setMaxListeners(0);
return server;
}
var jsonContentType = 'application/json; charset=utf-8';
var jsContentType = 'text/javascript; charset=utf-8';
var pngContentType = 'image/png';
function createLayergroup (layergroupConfig, options, callback) {
if (!callback) {
callback = options;
options = {
method: 'POST',
statusCode: 200
};
}
var expectedResponse = {
status: options.statusCode || 200,
headers: options.headers || {
'Content-Type': 'application/json; charset=utf-8'
}
};
step(
function requestLayergroup () {
var next = this;
var request = layergroupRequest(layergroupConfig, options.method, options.callbackName, options.params);
assert.response(serverInstance(options), request, expectedResponse, function (res, err) {
next(err, res);
});
},
function validateLayergroup (err, res) {
assert.ifError(err);
var parsedBody;
var layergroupid;
if (options.callbackName) {
global[options.callbackName] = function (layergroup) {
layergroupid = layergroup.layergroupid;
};
eval(res.body); // eslint-disable-line no-eval
delete global[options.callbackName];
} else {
parsedBody = JSON.parse(res.body);
layergroupid = parsedBody.layergroupid;
if (layergroupid) {
layergroupid = LayergroupToken.parse(layergroupid).token;
}
}
if (layergroupid) {
var keysToDelete = {
'user:localhost:mapviews:global': 5
};
var redisKey = 'map_cfg|' + layergroupid;
keysToDelete[redisKey] = 0;
testHelper.deleteRedisKeys(keysToDelete, function () {
return callback(err, res, parsedBody);
});
} else {
return callback(err, res, parsedBody);
}
}
);
}
function serverInstance (options) {
if (options.server) {
return options.server;
}
if (options.serverOptions) {
var otherServer = new CartodbServer(options.serverOptions);
otherServer.req2params = options.serverOptions.req2params;
otherServer.setMaxListeners(0);
return otherServer;
}
return getServer();
}
function layergroupRequest (layergroupConfig, method, callbackName, extraParams) {
method = method || 'POST';
var request = {
url: '/api/v1/map',
headers: {
host: 'localhost',
'Content-Type': 'application/json'
}
};
var urlParams = _.extend({}, extraParams);
if (callbackName) {
urlParams.callback = callbackName;
}
if (method.toUpperCase() === 'GET') {
request.method = 'GET';
urlParams.config = JSON.stringify(layergroupConfig);
} else {
request.method = 'POST';
request.data = JSON.stringify(layergroupConfig);
}
if (Object.keys(urlParams).length) {
request.url += '?' + querystring.stringify(urlParams);
}
return request;
}
function singleLayerMapConfig (sql, cartocss, cartocssVersion, interactivity) {
return {
version: '1.3.0',
layers: [
{
type: 'mapnik',
options: {
sql: sql,
cartocss: cartocss || DEFAULT_POINT_STYLE,
cartocss_version: cartocssVersion || '2.3.0',
interactivity: interactivity,
geom_column: 'the_geom'
}
}
]
};
}
function defaultTableMapConfig (tableName, cartocss, cartocssVersion, interactivity) {
return singleLayerMapConfig(defaultTableQuery(tableName), cartocss, cartocssVersion, interactivity);
}
function defaultTableQuery (tableName) {
return _.template('SELECT * FROM <%= tableName %>', { tableName: tableName });
}
function getStaticBbox (layergroupConfig, west, south, east, north, width, height, expectedResponse, callback) {
if (!callback) {
callback = expectedResponse;
expectedResponse = pngContentType;
}
var url = [
'static',
'bbox',
'<%= layergroupid %>',
[west, south, east, north].join(','),
width,
height
].join('/') + '.png';
return getGeneric(layergroupConfig, url, expectedResponse, callback);
}
function getStaticCenter (layergroupConfig, zoom, lat, lon, width, height, expectedResponse, callback) {
if (!callback) {
callback = expectedResponse;
expectedResponse = pngContentType;
}
var url = [
'static',
'center',
'<%= layergroupid %>',
zoom,
lat,
lon,
width,
height
].join('/') + '.png';
return getGeneric(layergroupConfig, url, expectedResponse, callback);
}
function getGrid (layergroupConfig, layer, z, x, y, expectedResponse, callback) {
if (!callback) {
callback = expectedResponse;
expectedResponse = jsonContentType;
}
var options = {
layer: layer,
z: z,
x: x,
y: y,
format: 'grid.json'
};
return getLayer(layergroupConfig, options, expectedResponse, callback);
}
function getGridJsonp (layergroupConfig, layer, z, x, y, jsonpCallbackName, expectedResponse, callback) {
if (!callback) {
callback = expectedResponse;
expectedResponse = jsContentType;
}
var options = {
layer: layer,
z: z,
x: x,
y: y,
format: 'grid.json',
jsonpCallbackName: jsonpCallbackName
};
return getLayer(layergroupConfig, options, expectedResponse, callback);
}
function getTorque (layergroupConfig, layer, z, x, y, expectedResponse, callback) {
if (!callback) {
callback = expectedResponse;
expectedResponse = jsonContentType;
}
var options = {
layer: layer,
z: z,
x: x,
y: y,
format: 'torque.json'
};
return getLayer(layergroupConfig, options, expectedResponse, callback);
}
function getTile (layergroupConfig, z, x, y, expectedResponse, callback) {
if (!callback) {
callback = expectedResponse;
expectedResponse = pngContentType;
}
var options = {
z: z,
x: x,
y: y,
format: 'png'
};
return getLayer(layergroupConfig, options, expectedResponse, callback);
}
function getTileLayer (layergroupConfig, options, expectedResponse, callback) {
if (!callback) {
callback = expectedResponse;
expectedResponse = pngContentType;
}
return getLayer(layergroupConfig, options, expectedResponse, callback);
}
function getLayer (layergroupConfig, options, expectedResponse, callback) {
return getGeneric(layergroupConfig, tileUrlStrategy(options), expectedResponse, callback);
}
function tileUrlStrategy (options) {
var urlLayerPattern = [
'<%= layer %>',
'<%= z %>',
'<%= x %>',
'<%= y %>'
].join('/') + '.<%= format %>';
if (options.jsonpCallbackName) {
urlLayerPattern += '?callback=<%= jsonpCallbackName %>';
}
var urlNoLayerPattern = [
'<%= z %>',
'<%= x %>',
'<%= y %>'
].join('/') + '.<%= format %>';
var urlTemplate = _.template((options.layer === undefined) ? urlNoLayerPattern : urlLayerPattern);
options.format = options.format || 'png';
return '<%= layergroupid %>/' + urlTemplate(_.defaults(options, { z: 0, x: 0, y: 0, layer: 0 }));
}
function getGeneric (layergroupConfig, url, expectedResponse, callback) {
if (_.isString(expectedResponse)) {
expectedResponse = {
status: 200,
headers: {
'Content-Type': expectedResponse
}
};
}
var contentType = expectedResponse.headers['Content-Type'];
var layergroupid = null;
step(
function requestLayergroup () {
var next = this;
var request = {
url: '/api/v1/map',
method: 'POST',
headers: {
host: 'localhost',
'Content-Type': 'application/json'
},
data: JSON.stringify(layergroupConfig)
};
var expectedResponse = {
status: 200,
headers: {
'Content-Type': 'application/json; charset=utf-8'
}
};
assert.response(getServer(), request, expectedResponse, function (res, err) {
next(err, res);
});
},
function validateLayergroup (err, res) {
assert.ok(!err, 'Failed to create layergroup');
var parsedBody = JSON.parse(res.body);
layergroupid = parsedBody.layergroupid;
assert.ok(layergroupid);
return res;
},
function requestTile (err, res) {
assert.ok(!err, 'Invalid layergroup response: ' + res.body);
var next = this;
var finalUrl = '/api/v1/map/' + _.template(url, {
layergroupid: layergroupid
});
var request = {
url: finalUrl,
method: 'GET',
headers: {
host: 'localhost'
}
};
if (contentType === pngContentType) {
request.encoding = 'binary';
}
assert.response(getServer(), request, expectedResponse, function (res, err) {
next(err, res);
});
},
function validateTile (err, res) {
assert.ok(!err, 'Failed to get tile');
var img;
if (contentType === pngContentType) {
img = mapnik.Image.fromBytesSync(Buffer.from(res.body, 'binary'));
}
var keysToDelete = {
'user:localhost:mapviews:global': 5
};
var redisKey = 'map_cfg|' + LayergroupToken.parse(layergroupid).token;
keysToDelete[redisKey] = 0;
testHelper.deleteRedisKeys(keysToDelete, function () {
return callback(err, res, img);
});
}
);
}
function withLayergroup (layergroupConfig, options, callback) {
var validationLayergroupFn = function () {};
if (!callback) {
callback = options;
options = {};
}
if (_.isFunction(options)) {
validationLayergroupFn = options;
options = {};
}
var layergroupExpectedResponse = {
status: 200,
headers: {
'Content-Type': 'application/json; charset=utf-8'
}
};
step(
function requestLayergroup () {
var next = this;
var request = layergroupRequest(layergroupConfig, 'POST');
assert.response(serverInstance(options), request, layergroupExpectedResponse, function (res, err) {
next(err, res);
});
},
function validateLayergroup (err, res) {
assert.ok(!err, 'Failed to request layergroup');
var parsedBody = JSON.parse(res.body);
var layergroupid = parsedBody.layergroupid;
assert.ok(layergroupid, 'No layergroup was created');
validationLayergroupFn(res);
function requestTile (layergroupUrl, options, callback) {
if (!callback) {
callback = options;
options = {
statusCode: 200,
contentType: pngContentType
};
}
const signerTpl = function ({ signer }) {
return `${signer ? `:${signer}@` : ''}`;
};
const cacheTpl = function ({ cacheBuster }) {
return `${cacheBuster ? `:${cacheBuster}` : ''}`;
};
const urlTpl = function ({ layergroupid, cacheBuster, tile }) {
const { signer, token, cacheBuster: cb } = LayergroupToken.parse(layergroupid);
const base = '/api/v1/map/';
return `${base}${signerTpl({ signer })}${token}${cacheTpl({ cacheBuster: (cacheBuster || cb) })}${tile}`;
};
const finalUrl = urlTpl({ layergroupid, cacheBuster: options.cache_buster, tile: layergroupUrl });
var request = {
url: finalUrl,
method: 'GET',
headers: {
host: 'localhost'
}
};
if (options.contentType === pngContentType) {
request.encoding = 'binary';
}
var tileExpectedResponse = {
status: options.statusCode || 200,
headers: {
'Content-Type': options.contentType || pngContentType
}
};
assert.response(serverInstance(options), request, tileExpectedResponse, function (res, err) {
callback(err, res);
});
}
function finish (done) {
var keysToDelete = {
'user:localhost:mapviews:global': 5
};
var redisKey = 'map_cfg|' + LayergroupToken.parse(layergroupid).token;
keysToDelete[redisKey] = 0;
testHelper.deleteRedisKeys(keysToDelete, done);
}
return callback(err, requestTile, finish);
}
);
}