Convert widgets from layers into dataviews
It also converts filters so full dataviews backend is reusable, that removes widgets backend dependency.
This commit is contained in:
parent
da6870cf1e
commit
f602ea88e2
@ -21,7 +21,6 @@ var QueryTables = require('cartodb-query-tables');
|
||||
* @param {TileBackend} tileBackend
|
||||
* @param {PreviewBackend} previewBackend
|
||||
* @param {AttributesBackend} attributesBackend
|
||||
* @param {WidgetBackend} widgetBackend
|
||||
* @param {SurrogateKeysCache} surrogateKeysCache
|
||||
* @param {UserLimitsApi} userLimitsApi
|
||||
* @param {LayergroupAffectedTables} layergroupAffectedTables
|
||||
@ -29,7 +28,7 @@ var QueryTables = require('cartodb-query-tables');
|
||||
* @constructor
|
||||
*/
|
||||
function LayergroupController(authApi, pgConnection, mapStore, tileBackend, previewBackend, attributesBackend,
|
||||
widgetBackend, surrogateKeysCache, userLimitsApi, layergroupAffectedTables, analysisBackend) {
|
||||
surrogateKeysCache, userLimitsApi, layergroupAffectedTables, analysisBackend) {
|
||||
BaseController.call(this, authApi, pgConnection);
|
||||
|
||||
this.pgConnection = pgConnection;
|
||||
@ -37,7 +36,6 @@ function LayergroupController(authApi, pgConnection, mapStore, tileBackend, prev
|
||||
this.tileBackend = tileBackend;
|
||||
this.previewBackend = previewBackend;
|
||||
this.attributesBackend = attributesBackend;
|
||||
this.widgetBackend = widgetBackend;
|
||||
this.surrogateKeysCache = surrogateKeysCache;
|
||||
this.userLimitsApi = userLimitsApi;
|
||||
this.layergroupAffectedTables = layergroupAffectedTables;
|
||||
@ -78,21 +76,19 @@ LayergroupController.prototype.register = function(app) {
|
||||
|
||||
// Undocumented/non-supported API endpoint methods.
|
||||
// Use at your own peril.
|
||||
app.get(app.base_url_mapconfig +
|
||||
'/:token/:layer/widget/:widgetName', cors(), userMiddleware,
|
||||
this.widget.bind(this));
|
||||
|
||||
app.get(app.base_url_mapconfig +
|
||||
'/:token/:layer/widget/:widgetName/search', cors(), userMiddleware,
|
||||
this.widgetSearch.bind(this));
|
||||
|
||||
app.get(app.base_url_mapconfig +
|
||||
'/:token/dataview/:dataviewName', cors(), userMiddleware,
|
||||
this.dataview.bind(this));
|
||||
app.get(app.base_url_mapconfig +
|
||||
'/:token/:layer/widget/:dataviewName', cors(), userMiddleware,
|
||||
this.dataview.bind(this));
|
||||
|
||||
app.get(app.base_url_mapconfig +
|
||||
'/:token/dataview/:dataviewName/search', cors(), userMiddleware,
|
||||
this.dataviewSearch.bind(this));
|
||||
app.get(app.base_url_mapconfig +
|
||||
'/:token/:layer/widget/:dataviewName/search', cors(), userMiddleware,
|
||||
this.dataviewSearch.bind(this));
|
||||
|
||||
app.get(app.base_url_mapconfig +
|
||||
'/:token/analysis/node/:nodeId', cors(), userMiddleware,
|
||||
@ -181,62 +177,6 @@ LayergroupController.prototype.dataviewSearch = function(req, res) {
|
||||
|
||||
};
|
||||
|
||||
LayergroupController.prototype.widget = function(req, res) {
|
||||
var self = this;
|
||||
|
||||
step(
|
||||
function setupParams() {
|
||||
self.req2params(req, this);
|
||||
},
|
||||
function retrieveList(err) {
|
||||
assert.ifError(err);
|
||||
|
||||
var mapConfigProvider = new MapStoreMapConfigProvider(
|
||||
self.mapStore, req.context.user, self.userLimitsApi, req.params
|
||||
);
|
||||
self.widgetBackend.getWidget(mapConfigProvider, req.params, this);
|
||||
},
|
||||
function finish(err, widget, stats) {
|
||||
req.profiler.add(stats || {});
|
||||
|
||||
if (err) {
|
||||
self.sendError(req, res, err, 'GET WIDGET');
|
||||
} else {
|
||||
self.sendResponse(req, res, widget, 200);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
};
|
||||
|
||||
LayergroupController.prototype.widgetSearch = function(req, res) {
|
||||
var self = this;
|
||||
|
||||
step(
|
||||
function setupParams() {
|
||||
self.req2params(req, this);
|
||||
},
|
||||
function retrieveList(err) {
|
||||
assert.ifError(err);
|
||||
|
||||
var mapConfigProvider = new MapStoreMapConfigProvider(
|
||||
self.mapStore, req.context.user, self.userLimitsApi, req.params
|
||||
);
|
||||
self.widgetBackend.search(mapConfigProvider, req.params, this);
|
||||
},
|
||||
function finish(err, searchResult, stats) {
|
||||
req.profiler.add(stats || {});
|
||||
|
||||
if (err) {
|
||||
self.sendError(req, res, err, 'GET WIDGET');
|
||||
} else {
|
||||
self.sendResponse(req, res, searchResult, 200);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
};
|
||||
|
||||
LayergroupController.prototype.attributes = function(req, res) {
|
||||
var self = this;
|
||||
|
||||
|
@ -5,5 +5,86 @@ module.exports = DataviewsWidgetsMapConfigAdapter;
|
||||
|
||||
|
||||
DataviewsWidgetsMapConfigAdapter.prototype.getMapConfig = function(user, requestMapConfig, params, context, callback) {
|
||||
if (!shouldAdapt(requestMapConfig)) {
|
||||
return callback(null, requestMapConfig);
|
||||
}
|
||||
|
||||
// prepare placeholders for new dataviews created from widgets
|
||||
requestMapConfig.analyses = requestMapConfig.analyses || [];
|
||||
requestMapConfig.dataviews = requestMapConfig.dataviews || {};
|
||||
|
||||
requestMapConfig.layers.forEach(function(layer, index) {
|
||||
var layerSourceId = getLayerSourceId(layer);
|
||||
var dataviewSourceId = layerSourceId || 'cdb-layer-source-' + index;
|
||||
// Append a new analysis if layer has no source id but sql.
|
||||
if (!layerSourceId) {
|
||||
requestMapConfig.analyses.push(
|
||||
{
|
||||
id: dataviewSourceId,
|
||||
type: 'source',
|
||||
params: {
|
||||
query: layer.options.sql
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
var source = { id: dataviewSourceId };
|
||||
var layerWidgets = layer.options.widgets;
|
||||
Object.keys(layerWidgets).forEach(function(widgetId) {
|
||||
var dataview = layerWidgets[widgetId];
|
||||
requestMapConfig.dataviews[widgetId] = {
|
||||
source: source,
|
||||
type: dataview.type,
|
||||
options: dataview.options
|
||||
};
|
||||
});
|
||||
|
||||
layer.options.source = source;
|
||||
|
||||
delete layer.options.sql;
|
||||
// don't delete widgets for now as it might be useful for old clients
|
||||
//delete layer.options.widgets;
|
||||
});
|
||||
|
||||
// filters have to be rewritten also
|
||||
var filters = getFilters(params);
|
||||
var layersFilters = filters.layers || [];
|
||||
filters.dataviews = filters.dataviews || {};
|
||||
|
||||
layersFilters.forEach(function(layerFilters) {
|
||||
Object.keys(layerFilters).forEach(function(filterName) {
|
||||
if (!filters.dataviews.hasOwnProperty(filterName)) {
|
||||
filters.dataviews[filterName] = layerFilters[filterName];
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
delete filters.layers;
|
||||
|
||||
params.filters = JSON.stringify(filters);
|
||||
|
||||
return callback(null, requestMapConfig);
|
||||
};
|
||||
|
||||
function shouldAdapt(requestMapConfig) {
|
||||
// return false;
|
||||
return Array.isArray(requestMapConfig.layers) && requestMapConfig.layers.some(function hasWidgets(layer) {
|
||||
return layer.options && layer.options.widgets && Object.keys(layer.options.widgets).length > 0;
|
||||
});
|
||||
}
|
||||
|
||||
function getLayerSourceId(layer) {
|
||||
return layer.options.source && layer.options.source.id;
|
||||
}
|
||||
|
||||
function getFilters(params) {
|
||||
var filters = {};
|
||||
if (params.filters) {
|
||||
try {
|
||||
filters = JSON.parse(params.filters);
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
return filters;
|
||||
}
|
||||
|
@ -194,7 +194,6 @@ module.exports = function(serverOptions) {
|
||||
tileBackend,
|
||||
previewBackend,
|
||||
attributesBackend,
|
||||
new windshaft.backend.Widget(),
|
||||
surrogateKeysCache,
|
||||
userLimitsApi,
|
||||
layergroupAffectedTablesCache,
|
||||
|
@ -62,6 +62,7 @@ TestClient.prototype.getWidget = function(widgetName, params, callback) {
|
||||
return next(err);
|
||||
}
|
||||
var parsedBody = JSON.parse(res.body);
|
||||
|
||||
var expectedWidgetURLS = {
|
||||
http: "/api/v1/map/" + parsedBody.layergroupid + "/0/widget/" + widgetName
|
||||
};
|
||||
@ -69,6 +70,15 @@ TestClient.prototype.getWidget = function(widgetName, params, callback) {
|
||||
assert.ok(
|
||||
parsedBody.metadata.layers[0].widgets[widgetName].url.http.match(expectedWidgetURLS.http)
|
||||
);
|
||||
|
||||
var expectedDataviewsURLS = {
|
||||
http: "/api/v1/map/" + parsedBody.layergroupid + "/dataview/" + widgetName
|
||||
};
|
||||
assert.ok(parsedBody.metadata.dataviews[widgetName]);
|
||||
assert.ok(
|
||||
parsedBody.metadata.dataviews[widgetName].url.http.match(expectedDataviewsURLS.http)
|
||||
);
|
||||
|
||||
return next(null, parsedBody.layergroupid);
|
||||
}
|
||||
);
|
||||
|
331
test/unit/cartodb/mapconfig/dataviews-widgets-adapter.test.js
Normal file
331
test/unit/cartodb/mapconfig/dataviews-widgets-adapter.test.js
Normal file
@ -0,0 +1,331 @@
|
||||
//require('../../../support/test_helper');
|
||||
var assert = require('assert');
|
||||
|
||||
var DataviewsMapConfigAdapter = require('../../../../lib/cartodb/models/mapconfig/adapter/dataviews-widgets-adapter');
|
||||
|
||||
describe('dataviews-widgets-adapter', function() {
|
||||
|
||||
var widgetsMapConfigs = [
|
||||
{
|
||||
"input": {
|
||||
"version": "1.4.0",
|
||||
"layers": [
|
||||
{
|
||||
"type": "mapnik",
|
||||
"options": {
|
||||
"sql": "select * from populated_places_simple_reduced",
|
||||
"cartocss": "#layer { marker-fill: red; marker-width: 32; marker-allow-overlap: true; }",
|
||||
"cartocss_version": "2.3.0",
|
||||
"widgets": {
|
||||
"country_places_count": {
|
||||
"type": "aggregation",
|
||||
"options": {
|
||||
"column": "adm0_a3",
|
||||
"aggregation": "count"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"expected": {
|
||||
"version": "1.4.0",
|
||||
"layers": [
|
||||
{
|
||||
"type": "mapnik",
|
||||
"options": {
|
||||
"source": {
|
||||
"id": "cdb-layer-source-0"
|
||||
},
|
||||
"cartocss": "#layer { marker-fill: red; marker-width: 32; marker-allow-overlap: true; }",
|
||||
"cartocss_version": "2.3.0",
|
||||
// keep them for now
|
||||
"widgets": {
|
||||
"country_places_count": {
|
||||
"type": "aggregation",
|
||||
"options": {
|
||||
"column": "adm0_a3",
|
||||
"aggregation": "count"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"analyses": [
|
||||
{
|
||||
"id": "cdb-layer-source-0",
|
||||
"type": "source",
|
||||
"params": {
|
||||
"query": "select * from populated_places_simple_reduced"
|
||||
}
|
||||
}
|
||||
],
|
||||
"dataviews": {
|
||||
"country_places_count": {
|
||||
"source": {
|
||||
"id": "cdb-layer-source-0"
|
||||
},
|
||||
"type": "aggregation",
|
||||
"options": {
|
||||
"column": "adm0_a3",
|
||||
"aggregation": "count"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"input": {
|
||||
"version": "1.4.0",
|
||||
"layers": [
|
||||
{
|
||||
"type": "mapnik",
|
||||
"options": {
|
||||
"sql": "select * from populated_places_simple_reduced",
|
||||
"cartocss": "#layer { marker-fill: red; marker-width: 32; marker-allow-overlap: true; }",
|
||||
"cartocss_version": "2.3.0",
|
||||
"widgets": {
|
||||
"pop_max": {
|
||||
"type": "histogram",
|
||||
"options": {
|
||||
"column": "pop_max"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"expected": {
|
||||
"version": "1.4.0",
|
||||
"layers": [
|
||||
{
|
||||
"type": "mapnik",
|
||||
"options": {
|
||||
"source": {
|
||||
"id": "cdb-layer-source-0"
|
||||
},
|
||||
"cartocss": "#layer { marker-fill: red; marker-width: 32; marker-allow-overlap: true; }",
|
||||
"cartocss_version": "2.3.0",
|
||||
// keep them for now
|
||||
"widgets": {
|
||||
"pop_max": {
|
||||
"type": "histogram",
|
||||
"options": {
|
||||
"column": "pop_max"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"analyses": [
|
||||
{
|
||||
"id": "cdb-layer-source-0",
|
||||
"type": "source",
|
||||
"params": {
|
||||
"query": "select * from populated_places_simple_reduced"
|
||||
}
|
||||
}
|
||||
],
|
||||
"dataviews": {
|
||||
"pop_max": {
|
||||
"source": {
|
||||
"id": "cdb-layer-source-0"
|
||||
},
|
||||
"type": "histogram",
|
||||
"options": {
|
||||
"column": "pop_max"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"input": {
|
||||
"version": "1.4.0",
|
||||
"layers": [
|
||||
{
|
||||
"type": "mapnik",
|
||||
"options": {
|
||||
"sql": "select * from test_table",
|
||||
"cartocss": "#layer { marker-fill: red; marker-width: 32; marker-allow-overlap: true; }",
|
||||
"cartocss_version": "2.3.0",
|
||||
"widgets": {
|
||||
"names": {
|
||||
"type": "list",
|
||||
"options": {
|
||||
"columns": [
|
||||
"name"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"expected": {
|
||||
"version": "1.4.0",
|
||||
"layers": [
|
||||
{
|
||||
"type": "mapnik",
|
||||
"options": {
|
||||
"source": {
|
||||
"id": "cdb-layer-source-0"
|
||||
},
|
||||
"cartocss": "#layer { marker-fill: red; marker-width: 32; marker-allow-overlap: true; }",
|
||||
"cartocss_version": "2.3.0",
|
||||
// keep them for now
|
||||
"widgets": {
|
||||
"names": {
|
||||
"type": "list",
|
||||
"options": {
|
||||
"columns": [
|
||||
"name"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"analyses": [
|
||||
{
|
||||
"id": "cdb-layer-source-0",
|
||||
"type": "source",
|
||||
"params": {
|
||||
"query": "select * from test_table"
|
||||
}
|
||||
}
|
||||
],
|
||||
"dataviews": {
|
||||
"names": {
|
||||
"source": {
|
||||
"id": "cdb-layer-source-0"
|
||||
},
|
||||
"type": "list",
|
||||
"options": {
|
||||
"columns": [
|
||||
"name"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"input": {
|
||||
"version": "1.4.0",
|
||||
"layers": [
|
||||
{
|
||||
"type": "mapnik",
|
||||
"options": {
|
||||
"sql": "select * from populated_places_simple_reduced",
|
||||
"cartocss": "#layer { marker-fill: red; marker-width: 32; marker-allow-overlap: true; }",
|
||||
"cartocss_version": "2.3.0",
|
||||
"widgets": {
|
||||
"country_places_count": {
|
||||
"type": "aggregation",
|
||||
"options": {
|
||||
"column": "adm0_a3",
|
||||
"aggregation": "count"
|
||||
}
|
||||
},
|
||||
"country_places_histogram": {
|
||||
"type": "histogram",
|
||||
"options": {
|
||||
"column": "pop_max"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"expected": {
|
||||
"version": "1.4.0",
|
||||
"layers": [
|
||||
{
|
||||
"type": "mapnik",
|
||||
"options": {
|
||||
"source": {
|
||||
"id": "cdb-layer-source-0"
|
||||
},
|
||||
"cartocss": "#layer { marker-fill: red; marker-width: 32; marker-allow-overlap: true; }",
|
||||
"cartocss_version": "2.3.0",
|
||||
// keep them for now
|
||||
"widgets": {
|
||||
"country_places_count": {
|
||||
"type": "aggregation",
|
||||
"options": {
|
||||
"column": "adm0_a3",
|
||||
"aggregation": "count"
|
||||
}
|
||||
},
|
||||
"country_places_histogram": {
|
||||
"type": "histogram",
|
||||
"options": {
|
||||
"column": "pop_max"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"analyses": [
|
||||
{
|
||||
"id": "cdb-layer-source-0",
|
||||
"type": "source",
|
||||
"params": {
|
||||
"query": "select * from populated_places_simple_reduced"
|
||||
}
|
||||
}
|
||||
],
|
||||
"dataviews": {
|
||||
"country_places_count": {
|
||||
"source": {
|
||||
"id": "cdb-layer-source-0"
|
||||
},
|
||||
"type": "aggregation",
|
||||
"options": {
|
||||
"column": "adm0_a3",
|
||||
"aggregation": "count"
|
||||
}
|
||||
},
|
||||
"country_places_histogram": {
|
||||
"source": {
|
||||
"id": "cdb-layer-source-0"
|
||||
},
|
||||
"type": "histogram",
|
||||
"options": {
|
||||
"column": "pop_max"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
var user = 'wadus';
|
||||
function params() {
|
||||
return {};
|
||||
}
|
||||
function context() {
|
||||
return {};
|
||||
}
|
||||
|
||||
var dataviewsMapConfigAdapter = new DataviewsMapConfigAdapter();
|
||||
|
||||
widgetsMapConfigs.forEach(function(mapConfig, index) {
|
||||
it('should adapt widgets ' + index, function(done) {
|
||||
dataviewsMapConfigAdapter.getMapConfig(user, mapConfig.input, params(), context(), function(err, result) {
|
||||
assert.deepEqual(result, mapConfig.expected);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
Loading…
Reference in New Issue
Block a user