Compare commits

..

48 Commits

Author SHA1 Message Date
Daniel García Aubert
2fb8036740 Release 2.37.0 2016-05-03 17:53:08 +02:00
Daniel García Aubert
6902a8c9b3 Upgraded camashaft version to 0.6.0 2016-05-03 17:46:38 +02:00
Raul Ochoa
c19a345f6c Stubs next version 2016-04-29 12:54:47 +02:00
Raul Ochoa
bdc3ecff5a Release 2.36.1 2016-04-29 12:53:58 +02:00
Raul Ochoa
6c4ec29e18 Add test to validate new behaviour in camshaft 0.5.1 2016-04-29 12:51:48 +02:00
Raul Ochoa
412712e62e Upgrades camshaft to 0.5.1 2016-04-29 12:51:39 +02:00
Raul Ochoa
0edcb30f75 Stubs next version 2016-04-28 18:46:49 +02:00
Raul Ochoa
c0f49b3acb Release 2.36.0 2016-04-28 18:45:51 +02:00
Raul Ochoa
b926244085 Upgrades windshaft to 1.19.0 2016-04-28 17:30:57 +02:00
Raul Ochoa
d21136b475 Stubs next version 2016-04-27 18:44:47 +02:00
Raul Ochoa
8788aaaf25 Release 2.35.0 2016-04-27 18:43:50 +02:00
Raul Ochoa
120e800089 Bump version and update news 2016-04-27 18:42:56 +02:00
Raul Ochoa
92dc4b148c Merge pull request #432 from CartoDB/dataviews-columns
Append dataviews related columns to layers
2016-04-27 18:40:33 +02:00
Raul Ochoa
755dfe6822 Append dataviews related columns to layers 2016-04-27 18:30:05 +02:00
Daniel García Aubert
b787ee1033 Stubs next version 2016-04-27 15:12:13 +02:00
Daniel García Aubert
9ee3612da0 Release 2.34.1 2016-04-27 15:09:13 +02:00
Daniel García Aubert
fe0b0a9c50 Upgraded windshaft to 1.17.3 2016-04-27 15:05:45 +02:00
Raul Ochoa
dea19f20dd Stubs next version 2016-04-27 10:24:16 +02:00
Raul Ochoa
f9f70cc6e7 Release 2.34.0 2016-04-27 10:23:24 +02:00
Raul Ochoa
23ef1157cb Merge pull request #431 from CartoDB/dataviews-filters
Dataviews filters
2016-04-26 19:21:43 +02:00
Raul Ochoa
8ca4d537ce Update news 2016-04-26 19:18:04 +02:00
Raul Ochoa
98d5731555 Add test to validate latest windshaft uses dataviews filters
Windshaft is transforming dataview filters into widget filters
2016-04-26 19:13:30 +02:00
Raul Ochoa
5da8929a5d Upgrade windshaft 2016-04-26 19:13:10 +02:00
Raul Ochoa
e198bafac1 Merge pull request #429 from CartoDB/upgrade-turbo-carto
Starts using turbo-carto dependency
2016-04-26 16:56:11 +02:00
Raul Ochoa
dd731399dc Starts using turbo-carto dependency 2016-04-26 16:28:05 +02:00
csobier
66fd899ffb Merge pull request #426 from CartoDB/docs-801-misplaced-text
removed dup text from static api section
2016-04-24 14:16:40 -04:00
csobier
bca8a33417 removed dup text from static api section 2016-04-24 13:59:38 -04:00
Raul Ochoa
a11c8d882e Each error-case will have different expectations 2016-04-21 17:27:20 +02:00
Raul Ochoa
c5bed48d61 Handle missing analyses nodes for layers pointing to them
Fixes #422
2016-04-21 17:24:52 +02:00
Raul Ochoa
98b7d12796 Merge pull request #424 from CartoDB/analysis-named-metadata
Analysis named maps metadata
2016-04-21 17:10:36 +02:00
Raul Ochoa
93dd8a2213 Add analyses metadata for named maps excluding queries 2016-04-21 17:03:41 +02:00
Raul Ochoa
4e4a223f24 Better naming for analysis mapconfig adapter 2016-04-21 16:25:59 +02:00
Raul Ochoa
4a73f3874d Better naming 2016-04-21 16:18:17 +02:00
Raul Ochoa
bc845b2e8d Validate dataviews format before instantiating 2016-04-21 16:16:00 +02:00
Raul Ochoa
83eceb349c Merge pull request #423 from CartoDB/multierror-support
Adds support to return multiple errors in BaseController.sendError
2016-04-21 16:15:28 +02:00
Raul Ochoa
bb518f0744 Update news 2016-04-21 16:09:57 +02:00
Raul Ochoa
08ad961123 Adds support to return multiple errors in BaseController.sendError 2016-04-21 16:05:48 +02:00
Raul Ochoa
146d494cae Adds dataview example in named map 2016-04-21 15:35:45 +02:00
Javier Goizueta
bb40ecf7c2 Stub next version 2016-04-20 18:30:42 +02:00
Javier Goizueta
7de9f64f2a Release 2.33.1 2016-04-20 18:27:06 +02:00
Javier Goizueta
0bb6178d49 Merge pull request #421 from CartoDB/420-overviews-schema
Support unneeded schema names in overviews queries
2016-04-20 18:16:16 +02:00
Javier Goizueta
084b3e94a6 Remove unneeded variable 2016-04-20 18:01:34 +02:00
Javier Goizueta
a0445b5cdd 💄 Fix indentation
2 spaces were used instead of 4 in some places
2016-04-20 17:47:43 +02:00
Javier Goizueta
1d4ddd373b Remove unneeded callback from synchronous tests 2016-04-20 17:27:52 +02:00
Raul Ochoa
92795963ae Stubs next version 2016-04-20 17:26:30 +02:00
Javier Goizueta
ecbae52abe Refactor: use reduce for collecting overviews metadata 2016-04-20 17:24:16 +02:00
Javier Goizueta
57cba3d511 Fix comment 2016-04-20 14:30:13 +02:00
Javier Goizueta
7902b276ad Support unneeded schema names in overviews queries
Fixes #420
Keep table schema of overviews base tables and use it
to support queries that use the schema name when not
strictly needed.
2016-04-19 22:50:05 +02:00
35 changed files with 1463 additions and 1401 deletions

61
NEWS.md
View File

@@ -1,5 +1,66 @@
# Changelog
## 2.37.0
Released 2016-05-03
Announcements:
- Upgrades camshaft to [0.6.0](https://github.com/CartoDB/camshaft/releases/tag/0.6.0)
## 2.36.1
Released 2016-04-29
Announcements:
- Upgrades camshaft to [0.5.1](https://github.com/CartoDB/camshaft/releases/tag/0.5.1)
## 2.36.0
Released 2016-04-28
Announcements:
- Upgrades windshaft to [1.19.0](https://github.com/CartoDB/Windshaft/releases/tag/1.19.0)
## 2.35.0
Released 2016-04-27
Announcements:
- Upgrades windshaft to [1.18.0](https://github.com/CartoDB/Windshaft/releases/tag/1.18.0)
- Appends columns to layers from associated dataviews
## 2.34.1
Released 2016-04-27
Announcements:
- Upgrades windshaft to [1.17.3](https://github.com/CartoDB/Windshaft/releases/tag/1.17.3)
## 2.34.0
Released 2016-04-27
Enhancements:
- Adds support to return multiple errors in BaseController.sendError #423
- Starts using turbo-carto dependency
Announcements:
- Upgrades windshaft to [1.17.2](https://github.com/CartoDB/Windshaft/releases/tag/1.17.2)
## 2.33.1
Released 2016-04-20
Bug fixes:
- Support unneeded schema names in overviews queries #421
## 2.33.0
Released 2016-04-20

View File

@@ -49,8 +49,6 @@ bbox | the bounding box in WGS 84 (EPSG:4326), comma separated values for:
width | the width in pixels for the output image
height | the height in pixels for the output image
format | the format for the image, supported types: `png`, `jpg`
format | the bounding box in WGS 84 (EPSG:4326), comma separated values for:
--- | ---
|_ jpg | will have a default quality of 85.

View File

@@ -1,5 +1,5 @@
function OverviewsMetadataApi(pgQueryRunner) {
this.pgQueryRunner = pgQueryRunner;
this.pgQueryRunner = pgQueryRunner;
}
module.exports = OverviewsMetadataApi;
@@ -22,22 +22,25 @@ function prepareSql(sql) {
}
OverviewsMetadataApi.prototype.getOverviewsMetadata = function (username, sql, callback) {
var query = 'SELECT * FROM CDB_Overviews(CDB_QueryTablesText($windshaft$' + prepareSql(sql) + '$windshaft$))';
// FIXME: Currently using internal function _cdb_schema_name
// CDB_Overviews should provide the schema information directly.
var query = 'SELECT *, _cdb_schema_name(base_table)' +
' FROM CDB_Overviews(CDB_QueryTablesText($windshaft$' + prepareSql(sql) + '$windshaft$))';
this.pgQueryRunner.run(username, query, function handleOverviewsRows(err, rows) {
if (err){
callback(err);
return;
}
var metadata = {};
rows.forEach(function(row) {
var metadata = rows.reduce(function(metadata, row){
var table = row.base_table;
var table_metadata = metadata[table];
if ( !table_metadata ) {
table_metadata = metadata[table] = {};
var schema = row._cdb_schema_name;
if ( !metadata[table] ) {
metadata[table] = {};
}
table_metadata[row.z] = { table: row.overview_table };
});
metadata[table][row.z] = { table: row.overview_table };
metadata[table].schema = schema;
return metadata;
}, {});
return callback(null, metadata);
});
};

View File

@@ -9,7 +9,7 @@ var queue = require('queue-async');
var LruCache = require("lru-cache");
function NamedMapProviderCache(templateMaps, pgConnection, metadataBackend, userLimitsApi, overviewsAdapter,
turboCartocssAdapter) {
turboCartoAdapter) {
this.templateMaps = templateMaps;
this.pgConnection = pgConnection;
this.metadataBackend = metadataBackend;
@@ -18,7 +18,7 @@ function NamedMapProviderCache(templateMaps, pgConnection, metadataBackend, user
this.namedLayersAdapter = new MapConfigNamedLayersAdapter(templateMaps);
this.analysisMapConfigAdapter = new AnalysisMapConfigAdapter();
this.overviewsAdapter = overviewsAdapter;
this.turboCartocssAdapter = turboCartocssAdapter;
this.turboCartoAdapter = turboCartoAdapter;
this.providerCache = new LruCache({ max: 2000 });
}
@@ -38,7 +38,7 @@ NamedMapProviderCache.prototype.get = function(user, templateId, config, authTok
this.userLimitsApi,
this.namedLayersAdapter,
this.overviewsAdapter,
this.turboCartocssAdapter,
this.turboCartoAdapter,
this.analysisMapConfigAdapter,
user,
templateId,

View File

@@ -197,7 +197,10 @@ BaseController.prototype.send = function(req, res, body, status, headers) {
// jshint maxcomplexity:6
BaseController.prototype.sendError = function(req, res, err, label) {
var allErrors = Array.isArray(err) ? err : [err];
label = label || 'UNKNOWN';
err = allErrors[0] || new Error(label);
allErrors[0] = err;
var statusCode = findStatusCode(err);
@@ -208,7 +211,7 @@ BaseController.prototype.sendError = function(req, res, err, label) {
statusCode = 200;
}
var errorResponseBody = { errors: [errorMessage(err)] };
var errorResponseBody = { errors: allErrors.map(errorMessage) };
this.send(req, res, errorResponseBody, statusCode);
};

View File

@@ -30,13 +30,13 @@ var CreateLayergroupMapConfigProvider = require('../models/mapconfig/create_laye
* @param {UserLimitsApi} userLimitsApi
* @param {LayergroupAffectedTables} layergroupAffectedTables
* @param {MapConfigOverviewsAdapter} overviewsAdapter
* @param {TurboCartocssAdapter} turboCartoCssAdapter
* @param {TurboCartoAdapter} turboCartoAdapter
* @param {AnalysisBackend} analysisBackend
* @constructor
*/
function MapController(authApi, pgConnection, templateMaps, mapBackend, metadataBackend,
surrogateKeysCache, userLimitsApi, layergroupAffectedTables,
overviewsAdapter, turboCartoCssAdapter, analysisBackend) {
overviewsAdapter, turboCartoAdapter, analysisBackend) {
BaseController.call(this, authApi, pgConnection);
@@ -47,7 +47,7 @@ function MapController(authApi, pgConnection, templateMaps, mapBackend, metadata
this.surrogateKeysCache = surrogateKeysCache;
this.userLimitsApi = userLimitsApi;
this.layergroupAffectedTables = layergroupAffectedTables;
this.turboCartoCssAdapter = turboCartoCssAdapter;
this.turboCartoAdapter = turboCartoAdapter;
this.analysisMapConfigAdapter = new AnalysisMapConfigAdapter(analysisBackend);
this.namedLayersAdapter = new MapConfigNamedLayersAdapter(templateMaps);
@@ -163,7 +163,7 @@ MapController.prototype.create = function(req, res, prepareConfigFn) {
// ignore
}
}
self.analysisMapConfigAdapter.getLayers(analysisConfiguration, requestMapConfig, filters, this);
self.analysisMapConfigAdapter.getMapConfig(analysisConfiguration, requestMapConfig, filters, this);
},
function beforeLayergroupCreate(err, requestMapConfig, _analysesResults) {
assert.ifError(err);
@@ -197,11 +197,11 @@ MapController.prototype.create = function(req, res, prepareConfigFn) {
return next(null, requestMapConfig, datasource);
});
},
function parseTurboCartoCss(err, requestMapConfig, datasource) {
function parseTurboCarto(err, requestMapConfig, datasource) {
assert.ifError(err);
var next = this;
self.turboCartoCssAdapter.getLayers(req.context.user, requestMapConfig.layers, function (err, layers) {
self.turboCartoAdapter.getLayers(req.context.user, requestMapConfig.layers, function (err, layers) {
if (err) {
return next(err);
}
@@ -263,7 +263,7 @@ MapController.prototype.instantiateTemplate = function(req, res, prepareParamsFn
self.userLimitsApi,
self.namedLayersAdapter,
self.overviewsAdapter,
self.turboCartoCssAdapter,
self.turboCartoAdapter,
self.analysisMapConfigAdapter,
cdbuser,
req.params.template_id,
@@ -293,7 +293,9 @@ MapController.prototype.instantiateTemplate = function(req, res, prepareParamsFn
var templateHash = self.templateMaps.fingerPrint(mapConfigProvider.template).substring(0, 8);
layergroup.layergroupid = cdbuser + '@' + templateHash + '@' + layergroup.layergroupid;
addWidgetsUrl(req.context.user, layergroup);
addWidgetsUrl(cdbuser, layergroup);
addDataviewsUrls(cdbuser, layergroup, mapConfig.obj());
addAnalysesMetadata(cdbuser, layergroup, mapConfigProvider.analysesResults);
res.set('X-Layergroup-Id', layergroup.layergroupid);
self.surrogateKeysCache.tag(res, new NamedMapsCacheEntry(cdbuser, mapConfigProvider.getTemplateName()));
@@ -371,7 +373,7 @@ MapController.prototype.afterLayergroupCreate = function(req, res, mapconfig, an
// TODO this should take into account several URL patterns
addWidgetsUrl(username, layergroup);
addDataviewsUrls(username, layergroup, mapconfig.obj());
addAnalysesMetadata(username, layergroup, analysesResults);
addAnalysesMetadata(username, layergroup, analysesResults, true);
if (req.method === 'GET') {
var ttl = global.environment.varnish.layergroupTtl || 86400;
res.set('Cache-Control', 'public,max-age='+ttl+',must-revalidate');
@@ -390,9 +392,10 @@ MapController.prototype.afterLayergroupCreate = function(req, res, mapconfig, an
);
};
function addAnalysesMetadata(username, layergroup, analysesResults) {
function addAnalysesMetadata(username, layergroup, analysesResults, includeQuery) {
includeQuery = includeQuery || false;
analysesResults = analysesResults || [];
layergroup.metadata.analyses = layergroup.metadata.analyses || [];
layergroup.metadata.analyses = [];
analysesResults.forEach(function(analysis) {
var nodes = analysis.getSortedNodes();
@@ -402,9 +405,11 @@ function addAnalysesMetadata(username, layergroup, analysesResults) {
var nodeResource = layergroup.layergroupid + '/analysis/node/' + node.id();
nodesIdMap[node.params.id] = {
status: node.getStatus(),
query: node.getQuery(),
url: getUrls(username, nodeResource)
};
if (includeQuery) {
nodesIdMap[node.params.id].query = node.getQuery();
}
}
return nodesIdMap;

View File

@@ -66,7 +66,7 @@ function getFilter(dataview, params) {
};
}
AnalysisMapConfigAdapter.prototype.getLayers = function(analysisConfiguration, requestMapConfig, filters, callback) {
AnalysisMapConfigAdapter.prototype.getMapConfig = function(analysisConfiguration, requestMapConfig, filters, callback) {
// jshint maxcomplexity:7
var self = this;
filters = filters || {};
@@ -79,6 +79,11 @@ AnalysisMapConfigAdapter.prototype.getLayers = function(analysisConfiguration, r
debug(dataviewsFilters);
var dataviews = requestMapConfig.dataviews || {};
var errors = getDataviewsErrors(dataviews);
if (errors.length > 0) {
return callback(errors);
}
var dataviewsFiltersBySourceId = Object.keys(dataviewsFilters).reduce(function(bySourceId, dataviewName) {
var dataview = dataviews[dataviewName];
if (dataview) {
@@ -127,16 +132,89 @@ AnalysisMapConfigAdapter.prototype.getLayers = function(analysisConfiguration, r
return sourceId2Query;
}, {});
requestMapConfig.layers = requestMapConfig.layers.map(function(layer) {
if (layer.options.source && layer.options.source.id) {
var layerNode = sourceId2Node[layer.options.source.id];
layer.options.sql = layerQuery(layerNode.getQuery(), layerNode.getColumns());
var missingNodesErrors = [];
requestMapConfig.layers = requestMapConfig.layers.map(function(layer, layerIndex) {
if (getLayerSourceId(layer)) {
var layerSourceId = getLayerSourceId(layer);
var layerNode = sourceId2Node[layerSourceId];
if (layerNode) {
layer.options.sql = layerQuery(layerNode.getQuery(), layerNode.getColumns());
var layerDataviews = getLayerDataviews(layer, dataviews);
layer.options.columns = layerDataviews.reduce(function(columns, dataview) {
return columns.concat(getDataviewColumns(dataview));
}, []);
} else {
missingNodesErrors.push(
new Error('Missing analysis node.id="' + layerSourceId +'" for layer='+layerIndex)
);
}
}
return layer;
});
debug('mapconfig output', JSON.stringify(requestMapConfig, null, 4));
if (missingNodesErrors.length > 0) {
return callback(missingNodesErrors);
}
return callback(null, requestMapConfig, analysesResults);
});
};
function getLayerSourceId(layer) {
return layer.options.source && layer.options.source.id;
}
function getDataviewSourceId(dataview) {
return dataview.source && dataview.source.id;
}
function getLayerDataviews(layer, dataviews) {
var layerDataviews = [];
var layerSourceId = getLayerSourceId(layer);
if (layerSourceId) {
var dataviewsList = getDataviewsList(dataviews);
dataviewsList.forEach(function(dataview) {
if (getDataviewSourceId(dataview) === layerSourceId) {
layerDataviews.push(dataview);
}
});
}
return layerDataviews;
}
function getDataviewColumns(dataview) {
var columns = [];
var options = dataview.options;
['column', 'aggregationColumn'].forEach(function(opt) {
if (options.hasOwnProperty(opt)) {
columns.push(options[opt]);
}
});
return columns;
}
function getDataviewsList(dataviews) {
return Object.keys(dataviews).map(function(dataviewKey) { return dataviews[dataviewKey]; });
}
function getDataviewsErrors(dataviews) {
var errors = [];
Object.keys(dataviews).forEach(function(dataviewName) {
var dataview = dataviews[dataviewName];
if (!dataview.hasOwnProperty('source') || !dataview.source.id) {
errors.push(new Error('Dataview "' + dataviewName + '" is missing `source.id` attribute'));
}
if (!dataview.type) {
errors.push(new Error('Dataview "' + dataviewName + '" is missing `type` attribute'));
}
});
return errors;
}

View File

@@ -12,14 +12,14 @@ var QueryTables = require('cartodb-query-tables');
* @type {NamedMapMapConfigProvider}
*/
function NamedMapMapConfigProvider(templateMaps, pgConnection, metadataBackend, userLimitsApi,
namedLayersAdapter, overviewsAdapter, turboCartoCssAdapter, analysisMapConfigAdapter,
namedLayersAdapter, overviewsAdapter, turboCartoAdapter, analysisMapConfigAdapter,
owner, templateId, config, authToken, params) {
this.templateMaps = templateMaps;
this.pgConnection = pgConnection;
this.metadataBackend = metadataBackend;
this.userLimitsApi = userLimitsApi;
this.namedLayersAdapter = namedLayersAdapter;
this.turboCartoCssAdapter = turboCartoCssAdapter;
this.turboCartoAdapter = turboCartoAdapter;
this.analysisMapConfigAdapter = analysisMapConfigAdapter;
this.overviewsAdapter = overviewsAdapter;
@@ -41,6 +41,7 @@ function NamedMapMapConfigProvider(templateMaps, pgConnection, metadataBackend,
this.mapConfig = null;
this.rendererParams = null;
this.context = {};
this.analysesResults = [];
}
module.exports = NamedMapMapConfigProvider;
@@ -118,11 +119,12 @@ NamedMapMapConfigProvider.prototype.getMapConfig = function(callback) {
// ignore
}
}
self.analysisMapConfigAdapter.getLayers(analysisConfiguration, requestMapConfig, filters, this);
self.analysisMapConfigAdapter.getMapConfig(analysisConfiguration, requestMapConfig, filters, this);
},
function prepareLayergroup(err, _mapConfig) {
function prepareLayergroup(err, _mapConfig, analysesResults) {
assert.ifError(err);
var next = this;
self.analysesResults = analysesResults || [];
self.namedLayersAdapter.getLayers(self.owner, _mapConfig.layers, self.pgConnection,
function(err, layers, datasource) {
if (err) {
@@ -152,11 +154,11 @@ NamedMapMapConfigProvider.prototype.getMapConfig = function(callback) {
return next(null, _mapConfig, datasource);
});
},
function parseTurboCartoCss(err, _mapConfig, datasource) {
function parseTurboCarto(err, _mapConfig, datasource) {
assert.ifError(err);
var next = this;
self.turboCartoCssAdapter.getLayers(self.owner, _mapConfig.layers, function (err, layers) {
self.turboCartoAdapter.getLayers(self.owner, _mapConfig.layers, function (err, layers) {
if (err) {
return next(err);
}

View File

@@ -34,8 +34,8 @@ var timeoutErrorTile = require('fs').readFileSync(timeoutErrorTilePath, {encodin
var MapConfigOverviewsAdapter = require('./models/mapconfig_overviews_adapter');
var TurboCartocssParser = require('./utils/style/turbo-cartocss-parser');
var TurboCartocssAdapter = require('./utils/style/turbo-cartocss-adapter');
var TurboCartoParser = require('./utils/style/turbo-carto-parser');
var TurboCartoAdapter = require('./utils/style/turbo-carto-adapter');
module.exports = function(serverOptions) {
// Make stats client globally accessible
@@ -149,8 +149,8 @@ module.exports = function(serverOptions) {
var overviewsAdapter = new MapConfigOverviewsAdapter(overviewsMetadataApi);
var turboCartoCssParser = new TurboCartocssParser(pgQueryRunner);
var turboCartocssAdapter = new TurboCartocssAdapter(turboCartoCssParser);
var turboCartoParser = new TurboCartoParser(pgQueryRunner);
var turboCartoAdapter = new TurboCartoAdapter(turboCartoParser);
var namedMapProviderCache = new NamedMapProviderCache(
templateMaps,
@@ -158,7 +158,7 @@ module.exports = function(serverOptions) {
metadataBackend,
userLimitsApi,
overviewsAdapter,
turboCartocssAdapter
turboCartoAdapter
);
['update', 'delete'].forEach(function(eventType) {
@@ -198,7 +198,7 @@ module.exports = function(serverOptions) {
userLimitsApi,
layergroupAffectedTablesCache,
overviewsAdapter,
turboCartocssAdapter,
turboCartoAdapter,
analysisBackend
).register(app);

View File

@@ -22,7 +22,7 @@ function overviews_view_for_table(table, overviews_metadata, indent) {
indent = indent || ' ';
for (var z in overviews_metadata) {
if (overviews_metadata.hasOwnProperty(z)) {
if (overviews_metadata.hasOwnProperty(z) && z !== 'schema') {
sorted_overviews.push([z, overviews_metadata[z].table]);
}
}
@@ -39,11 +39,11 @@ function overviews_view_for_table(table, overviews_metadata, indent) {
overview_layers.push(["_vovw_z > " + z_lo, table]);
selects = overview_layers.map(function(condition_table) {
condition = condition_table[0];
ov_table = TableNameParser.parse(condition_table[1]);
ov_table.schema = ov_table.schema || parsed_table.schema;
var ov_identifier = TableNameParser.table_identifier(ov_table);
return indent + "SELECT * FROM " + ov_identifier + ", _vovw_scale WHERE " + condition;
condition = condition_table[0];
ov_table = TableNameParser.parse(condition_table[1]);
ov_table.schema = ov_table.schema || parsed_table.schema;
var ov_identifier = TableNameParser.table_identifier(ov_table);
return indent + "SELECT * FROM " + ov_identifier + ", _vovw_scale WHERE " + condition;
});
return selects.join("\n"+indent+"UNION ALL\n");
@@ -90,13 +90,13 @@ function replace_table_in_query(sql, old_table_name, replacement) {
return '';
}
} else {
// to match a table name without schema
// name should not begin right after a dot (i.e. have a explicit schema)
// nor be part of another name
// since the pattern matches the first character of the table
// it must be put back in the replacement text
replacement = '$01'+replacement;
return '([^\.a-z0-9_]|^)';
// to match a table name without schema
// name should not begin right after a dot (i.e. have a explicit schema)
// nor be part of another name
// since the pattern matches the first character of the table
// it must be put back in the replacement text
replacement = '$01'+replacement;
return '([^\.a-z0-9_]|^)';
}
}
@@ -127,8 +127,16 @@ function overviews_query(query, overviews, zoom_level_expression) {
if (overviews.hasOwnProperty(table)) {
var table_overviews = overviews[table];
var table_view = overviews_view_name(table);
var schema = table_overviews.schema;
replacement = "(\n" + overviews_view_for_table(table, table_overviews) + "\n ) AS " + table_view;
replaced_query = replace_table_in_query(replaced_query, table, replacement);
var parsed_table = TableNameParser.parse(table);
if (!parsed_table.schema && schema) {
// replace also the qualified table name, if the table wasn't qualified
parsed_table.schema = schema;
table = TableNameParser.table_identifier(parsed_table);
replaced_query = replace_table_in_query(replaced_query, table, replacement);
}
}
}
if ( replaced_query !== query ) {
@@ -168,18 +176,19 @@ OverviewsQueryRewriter.prototype.query = function(query, data) {
};
OverviewsQueryRewriter.prototype.is_supported_query = function(sql) {
var basic_query = /\s*SELECT\s+[\*a-z0-9_,\s]+?\s+FROM\s+((\"[^"]+\"|[a-z0-9_]+)\.)?(\"[^"]+\"|[a-z0-9_]+)\s*;?\s*/i;
var unwrapped_query = new RegExp("^"+basic_query.source+"$", 'i');
// queries for named maps are wrapped like this:
var wrapped_query = new RegExp(
"^\\s*SELECT\\s+\\*\\s+FROM\\s+\\(" +
basic_query.source +
"\\)\\s+AS\\s+wrapped_query\\s+WHERE\\s+\\d+=1\\s*$",
'i'
);
return !!(sql.match(unwrapped_query) || sql.match(wrapped_query));
var basic_query =
/\s*SELECT\s+[\*a-z0-9_,\s]+?\s+FROM\s+((\"[^"]+\"|[a-z0-9_]+)\.)?(\"[^"]+\"|[a-z0-9_]+)\s*;?\s*/i;
var unwrapped_query = new RegExp("^"+basic_query.source+"$", 'i');
// queries for named maps are wrapped like this:
var wrapped_query = new RegExp(
"^\\s*SELECT\\s+\\*\\s+FROM\\s+\\(" +
basic_query.source +
"\\)\\s+AS\\s+wrapped_query\\s+WHERE\\s+\\d+=1\\s*$",
'i'
);
return !!(sql.match(unwrapped_query) || sql.match(wrapped_query));
};
OverviewsQueryRewriter.prototype.overviews_metadata = function(data) {
return data && data.overviews;
return data && data.overviews;
};

View File

@@ -2,26 +2,26 @@
var queue = require('queue-async');
function TurboCartocssAdapter(turboCartocssParser) {
this.turboCartocssParser = turboCartocssParser;
function TurboCartoAdapter(turboCartoParser) {
this.turboCartoParser = turboCartoParser;
}
module.exports = TurboCartocssAdapter;
module.exports = TurboCartoAdapter;
TurboCartocssAdapter.prototype.getLayers = function (username, layers, callback) {
TurboCartoAdapter.prototype.getLayers = function (username, layers, callback) {
var self = this;
if (!layers || layers.length === 0) {
return callback(null, layers);
}
var parseCartoCssQueue = queue(layers.length);
var parseCartoQueue = queue(layers.length);
layers.forEach(function(layer) {
parseCartoCssQueue.defer(self._parseCartoCss.bind(self), username, layer);
parseCartoQueue.defer(self._parseCartoCss.bind(self), username, layer);
});
parseCartoCssQueue.awaitAll(function (err, layers) {
parseCartoQueue.awaitAll(function (err, layers) {
if (err) {
return callback(err);
}
@@ -30,15 +30,15 @@ TurboCartocssAdapter.prototype.getLayers = function (username, layers, callback)
});
};
TurboCartocssAdapter.prototype._parseCartoCss = function (username, layer, callback) {
TurboCartoAdapter.prototype._parseCartoCss = function (username, layer, callback) {
if (isNotLayerToParseCartocss(layer)) {
return process.nextTick(function () {
callback(null, layer);
});
}
this.turboCartocssParser.process(username, layer.options.cartocss, layer.options.sql, function (err, cartocss) {
// Ignore turbo-cartocss errors and continue
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;
}

View File

@@ -0,0 +1,15 @@
'use strict';
var turboCarto = require('turbo-carto');
var PostgresDatasource = require('./postgres-datasource');
function TurboCartoParser (pgQueryRunner) {
this.pgQueryRunner = pgQueryRunner;
}
module.exports = TurboCartoParser;
TurboCartoParser.prototype.process = function (username, cartocss, sql, callback) {
var datasource = new PostgresDatasource(this.pgQueryRunner, username, sql);
turboCarto(cartocss, datasource, callback);
};

View File

@@ -1,15 +0,0 @@
'use strict';
var turboCartoCss = require('turbo-cartocss');
var PostgresDatasource = require('./postgres-datasource');
function TurboCartocssParser (pgQueryRunner) {
this.pgQueryRunner = pgQueryRunner;
}
module.exports = TurboCartocssParser;
TurboCartocssParser.prototype.process = function (username, cartocss, sql, callback) {
var datasource = new PostgresDatasource(this.pgQueryRunner, username, sql);
turboCartoCss(cartocss, datasource, callback);
};

988
npm-shrinkwrap.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
{
"private": true,
"name": "windshaft-cartodb",
"version": "2.33.0",
"version": "2.37.0",
"description": "A map tile server for CartoDB",
"keywords": [
"cartodb"
@@ -26,20 +26,20 @@
"node-statsd": "~0.0.7",
"underscore" : "~1.6.0",
"dot": "~1.0.2",
"windshaft": "1.17.1",
"windshaft": "1.19.0",
"step": "~0.0.6",
"queue-async": "~1.0.7",
"request": "~2.62.0",
"cartodb-redis": "~0.13.0",
"cartodb-psql": "~0.6.1",
"camshaft": "0.5.0",
"camshaft": "0.6.0",
"fastly-purge": "~1.0.1",
"redis-mpool": "~0.4.0",
"lru-cache": "2.6.5",
"lzma": "~1.3.7",
"log4js": "https://github.com/CartoDB/log4js-node/tarball/cdb",
"cartodb-query-tables": "~0.1.0",
"turbo-cartocss": "0.4.0"
"turbo-carto": "0.6.0"
},
"devDependencies": {
"istanbul": "~0.3.6",

View File

@@ -0,0 +1,81 @@
require('../../support/test_helper');
var assert = require('../../support/assert');
var TestClient = require('../../support/test-client');
describe('analysis-layers-dataviews-geojson', 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'
}
}
},
[
{
"id": "2570e105-7b37-40d2-bdf4-1af889598745",
"type": "source",
"params": {
"query": "select * from populated_places_simple_reduced"
}
}
]
);
it('should get pop_max column from dataview', function(done) {
var testClient = new TestClient(mapConfig, 1234);
testClient.getTile(0, 0, 0, {format: 'geojson', layers: 0}, function(err, res, geojson) {
assert.ok(!err, err);
assert.ok(Array.isArray(geojson.features));
assert.ok(geojson.features.length > 0);
var feature = geojson.features[0];
assert.ok(feature.properties.hasOwnProperty('pop_max'), 'Missing pop_max property');
testClient.drain(done);
});
});
});

View File

@@ -184,4 +184,124 @@ describe('analysis-layers', function() {
testClient.drain(done);
});
});
it('should retrieve enough metadata about analyses', function(done) {
var useCase = useCases[1];
// No API key here
var testClient = new TestClient(useCase.mapConfig, 1234);
testClient.getLayergroup(function(err, layergroupResult) {
assert.ok(!err, err);
assert.ok(
Array.isArray(layergroupResult.metadata.analyses),
'Missing "analyses" array metadata from: ' + JSON.stringify(layergroupResult)
);
var analyses = layergroupResult.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'), 'Missing "status" attribute in node');
});
testClient.drain(done);
});
});
it('should have analysis metadata when dataviews point to source node', function(done) {
var useCase = {
desc: 'basic source-id mapnik layer',
fixture: 'basic-source-id-mapnik-layer.png',
mapConfig: mapConfig(
[
{
"type": "cartodb",
"options": {
"sql": "select * from populated_places_simple_reduced",
"cartocss": DEFAULT_MULTITYPE_STYLE,
"cartocss_version": "2.3.0"
}
},
{
"type": "cartodb",
"options": {
"source": {
"id": "2570e105-7b37-40d2-bdf4-1af889598745"
},
"cartocss": DEFAULT_MULTITYPE_STYLE,
"cartocss_version": "2.3.0"
}
},
{
"type": "cartodb",
"options": {
"sql": "select * from populated_places_simple_reduced",
"cartocss": DEFAULT_MULTITYPE_STYLE,
"cartocss_version": "2.3.0"
}
}
],
{
pop_max_histogram: {
source: {
id: '2570e105-7b37-40d2-bdf4-1af889598745'
},
type: 'histogram',
options: {
column: 'pop_max'
}
},
scalerank_histogram: {
source: {
id: '2570e105-7b37-40d2-bdf4-1af889598745'
},
type: 'histogram',
options: {
column: 'scalerank'
}
}
},
[
{
"id": "2570e105-7b37-40d2-bdf4-1af889598745",
"type": "source",
"params": {
"query": "select * from populated_places_simple_reduced"
}
}
]
)
};
var testClient = new TestClient(useCase.mapConfig, 1234);
testClient.getLayergroup(function(err, layergroupResult) {
assert.ok(!err, err);
assert.ok(
Array.isArray(layergroupResult.metadata.analyses),
'Missing "analyses" array metadata from: ' + JSON.stringify(layergroupResult)
);
var analyses = layergroupResult.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']);
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'), 'Missing "status" attribute in node');
});
testClient.drain(done);
});
});
});

View File

@@ -0,0 +1,67 @@
require('../../support/test_helper');
var assert = require('../../support/assert');
var TestClient = require('../../support/test-client');
describe('analysis-layers error cases', function() {
function createMapConfig(layers, dataviews, analysis) {
return {
version: '1.5.0',
layers: layers,
dataviews: dataviews || {},
analyses: analysis || []
};
}
var ERROR_RESPONSE = {
status: 400,
headers: {
'Content-Type': 'application/json; charset=utf-8'
}
};
it('should handle missing analysis nodes for layers', function(done) {
var mapConfig = createMapConfig(
[
{
"type": "cartodb",
"options": {
"source": {
"id": "INVALID-SOURCE-ID"
},
"cartocss": '#polygons { polygon-fill: red; }',
"cartocss_version": "2.3.0"
}
}
],
{},
[
{
"id": "HEAD",
"type": "buffer",
"params": {
"source": {
"id": "2570e105-7b37-40d2-bdf4-1af889598745",
"type": "source",
"params": {
"query": "select * from populated_places_simple_reduced"
}
},
"radius": 50000
}
}
]
);
var testClient = new TestClient(mapConfig, 1234);
testClient.getLayergroup(ERROR_RESPONSE, function(err, layergroupResult) {
assert.ok(!err, err);
assert.equal(layergroupResult.errors.length, 1);
assert.equal(layergroupResult.errors[0], 'Missing analysis node.id="INVALID-SOURCE-ID" for layer=0');
testClient.drain(done);
});
});
});

View File

@@ -40,6 +40,17 @@ describe('named-maps analysis', function() {
}
}
],
dataviews: {
pop_max_histogram: {
source: {
id: 'HEAD'
},
type: 'histogram',
options: {
column: 'pop_max'
}
}
},
analyses: [
{
"id": "HEAD",
@@ -115,6 +126,22 @@ describe('named-maps analysis', function() {
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;
@@ -185,4 +212,34 @@ describe('named-maps analysis', function() {
);
});
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();
}
);
});
});

View File

@@ -48,63 +48,64 @@ describe('overviews metadata', function() {
it("layers with and without overviews", function(done) {
var layergroup = {
version: '1.0.0',
layers: [overviews_layer, non_overviews_layer]
};
var layergroup = {
version: '1.0.0',
layers: [overviews_layer, non_overviews_layer]
};
var layergroup_url = '/api/v1/map';
var layergroup_url = '/api/v1/map';
var expected_token;
step(
function do_post()
{
var next = this;
assert.response(server, {
url: layergroup_url,
method: 'POST',
headers: {host: 'localhost', 'Content-Type': 'application/json' },
data: JSON.stringify(layergroup)
}, {}, function(res) {
assert.equal(res.statusCode, 200, res.body);
var parsedBody = JSON.parse(res.body);
assert.equal(res.headers['x-layergroup-id'], parsedBody.layergroupid);
expected_token = parsedBody.layergroupid;
next(null, res);
});
},
function do_get_mapconfig(err)
{
assert.ifError(err);
var next = this;
var expected_token;
step(
function do_post()
{
var next = this;
assert.response(server, {
url: layergroup_url,
method: 'POST',
headers: {host: 'localhost', 'Content-Type': 'application/json' },
data: JSON.stringify(layergroup)
}, {}, function(res) {
assert.equal(res.statusCode, 200, res.body);
var parsedBody = JSON.parse(res.body);
assert.equal(res.headers['x-layergroup-id'], parsedBody.layergroupid);
expected_token = parsedBody.layergroupid;
next(null, res);
});
},
function do_get_mapconfig(err)
{
assert.ifError(err);
var next = this;
var mapStore = new windshaft.storage.MapStore({
pool: redisPool,
expire_time: 500000
});
mapStore.load(LayergroupToken.parse(expected_token).token, function(err, mapConfig) {
assert.ifError(err);
assert.deepEqual(non_overviews_layer, mapConfig._cfg.layers[1]);
assert.equal(mapConfig._cfg.layers[0].type, 'cartodb');
assert.ok(mapConfig._cfg.layers[0].options.query_rewrite_data);
var expected_data = {
overviews: {
test_table_overviews: {
1: { table: '_vovw_1_test_table_overviews' },
2: { table: '_vovw_2_test_table_overviews' }
}
}
};
assert.deepEqual(mapConfig._cfg.layers[0].options.query_rewrite_data, expected_data);
});
var mapStore = new windshaft.storage.MapStore({
pool: redisPool,
expire_time: 500000
});
mapStore.load(LayergroupToken.parse(expected_token).token, function(err, mapConfig) {
assert.ifError(err);
assert.deepEqual(non_overviews_layer, mapConfig._cfg.layers[1]);
assert.equal(mapConfig._cfg.layers[0].type, 'cartodb');
assert.ok(mapConfig._cfg.layers[0].options.query_rewrite_data);
var expected_data = {
overviews: {
test_table_overviews: {
schema: 'public',
1: { table: '_vovw_1_test_table_overviews' },
2: { table: '_vovw_2_test_table_overviews' }
}
}
};
assert.deepEqual(mapConfig._cfg.layers[0].options.query_rewrite_data, expected_data);
});
next(err);
},
function finish(err) {
keysToDelete['map_cfg|' + LayergroupToken.parse(expected_token).token] = 0;
keysToDelete['user:localhost:mapviews:global'] = 5;
done(err);
}
);
next(err);
},
function finish(err) {
keysToDelete['map_cfg|' + LayergroupToken.parse(expected_token).token] = 0;
keysToDelete['user:localhost:mapviews:global'] = 5;
done(err);
}
);
});
});

View File

@@ -59,116 +59,117 @@ describe('overviews metadata for named maps', function() {
};
it("should add overviews data to layers", function(done) {
step(
function postTemplate()
{
var next = this;
step(
function postTemplate()
{
var next = this;
assert.response(server, {
url: '/api/v1/map/named?api_key=1234',
method: 'POST',
headers: {host: 'localhost', 'Content-Type': 'application/json' },
data: JSON.stringify(template)
}, {}, function(res, err) {
next(err, res);
});
},
function checkTemplate(err, res) {
assert.ifError(err);
assert.response(server, {
url: '/api/v1/map/named?api_key=1234',
method: 'POST',
headers: {host: 'localhost', 'Content-Type': 'application/json' },
data: JSON.stringify(template)
}, {}, function(res, err) {
next(err, res);
});
},
function checkTemplate(err, res) {
assert.ifError(err);
var next = this;
assert.equal(res.statusCode, 200);
assert.deepEqual(JSON.parse(res.body), {
template_id: templateId
});
next(null);
},
function instantiateTemplate(err) {
assert.ifError(err);
var next = this;
assert.equal(res.statusCode, 200);
assert.deepEqual(JSON.parse(res.body), {
template_id: templateId
});
next(null);
},
function instantiateTemplate(err) {
assert.ifError(err);
var next = this;
assert.response(server, {
url: '/api/v1/map/named/' + templateId,
method: 'POST',
headers: {
host: 'localhost',
'Content-Type': 'application/json'
}
}, {},
function(res, err) {
return next(err, res);
});
var next = this;
assert.response(server, {
url: '/api/v1/map/named/' + templateId,
method: 'POST',
headers: {
host: 'localhost',
'Content-Type': 'application/json'
}
}, {},
function(res, err) {
return next(err, res);
});
},
function checkInstanciation(err, res) {
assert.ifError(err);
},
function checkInstanciation(err, res) {
assert.ifError(err);
var next = this;
var next = this;
assert.equal(res.statusCode, 200);
assert.equal(res.statusCode, 200);
var parsedBody = JSON.parse(res.body);
var parsedBody = JSON.parse(res.body);
keysToDelete['map_cfg|' + LayergroupToken.parse(parsedBody.layergroupid).token] = 0;
keysToDelete['user:localhost:mapviews:global'] = 5;
keysToDelete['map_cfg|' + LayergroupToken.parse(parsedBody.layergroupid).token] = 0;
keysToDelete['user:localhost:mapviews:global'] = 5;
assert.ok(parsedBody.layergroupid);
assert.ok(parsedBody.last_updated);
assert.ok(parsedBody.layergroupid);
assert.ok(parsedBody.last_updated);
next(null, parsedBody.layergroupid);
},
next(null, parsedBody.layergroupid);
},
function checkMapconfig(err, layergroupId)
{
assert.ifError(err);
function checkMapconfig(err, layergroupId)
{
assert.ifError(err);
var next = this;
var next = this;
var mapStore = new windshaft.storage.MapStore({
pool: redisPool,
expire_time: 500000
});
mapStore.load(LayergroupToken.parse(layergroupId).token, function(err, mapConfig) {
assert.ifError(err);
assert.deepEqual(non_overviews_layer, mapConfig._cfg.layers[1]);
assert.equal(mapConfig._cfg.layers[0].type, 'cartodb');
assert.ok(mapConfig._cfg.layers[0].options.query_rewrite_data);
var expected_data = {
overviews: {
test_table_overviews: {
1: { table: '_vovw_1_test_table_overviews' },
2: { table: '_vovw_2_test_table_overviews' }
}
}
};
assert.deepEqual(mapConfig._cfg.layers[0].options.query_rewrite_data, expected_data);
});
var mapStore = new windshaft.storage.MapStore({
pool: redisPool,
expire_time: 500000
});
mapStore.load(LayergroupToken.parse(layergroupId).token, function(err, mapConfig) {
assert.ifError(err);
assert.deepEqual(non_overviews_layer, mapConfig._cfg.layers[1]);
assert.equal(mapConfig._cfg.layers[0].type, 'cartodb');
assert.ok(mapConfig._cfg.layers[0].options.query_rewrite_data);
var expected_data = {
overviews: {
test_table_overviews: {
schema: 'public',
1: { table: '_vovw_1_test_table_overviews' },
2: { table: '_vovw_2_test_table_overviews' }
}
}
};
assert.deepEqual(mapConfig._cfg.layers[0].options.query_rewrite_data, expected_data);
});
next(err);
},
function deleteTemplate(err) {
assert.ifError(err);
next(err);
},
function deleteTemplate(err) {
assert.ifError(err);
var next = this;
var next = this;
assert.response(server, {
url: '/api/v1/map/named/' + templateId + '?api_key=1234',
method: 'DELETE',
headers: { host: 'localhost' }
}, {}, function (res, err) {
next(err, res);
});
},
function checkDeleteTemplate(err, res) {
assert.ifError(err);
assert.equal(res.statusCode, 204);
assert.ok(!res.body);
assert.response(server, {
url: '/api/v1/map/named/' + templateId + '?api_key=1234',
method: 'DELETE',
headers: { host: 'localhost' }
}, {}, function (res, err) {
next(err, res);
});
},
function checkDeleteTemplate(err, res) {
assert.ifError(err);
assert.equal(res.statusCode, 204);
assert.ok(!res.body);
return null;
},
function finish(err) {
done(err);
}
);
return null;
},
function finish(err) {
done(err);
}
);
});
});

View File

@@ -1,5 +1,5 @@
var assert = require('../support/assert');
var TestClient = require('../support/test-client');
var assert = require('../../support/assert');
var TestClient = require('../../support/test-client');
var IMAGE_TOLERANCE_PER_MIL = 20;
@@ -38,7 +38,7 @@ function makeMapconfig(cartocss) {
};
}
describe('turbo-cartocss for anonymous maps', function() {
describe('turbo-carto for anonymous maps', function() {
describe('parsing ramp function with colorbrewer for greens and mapnik renderer', function () {
beforeEach(function () {
var turboCartocss = '#layer { marker-fill: ramp([price], colorbrewer(Greens)); }';
@@ -49,8 +49,8 @@ describe('turbo-cartocss for anonymous maps', function() {
this.testClient.drain(done);
});
it('should get a tile with turbo-cartocss parsed properly', function (done) {
var fixturePath = 'test_turbo_cartocss_greens_13_4011_3088.png';
it('should get a tile with turbo-carto parsed properly', function (done) {
var fixturePath = 'test_turbo_carto_greens_13_4011_3088.png';
this.testClient.getTile(13, 4011, 3088, imageCompareFn(fixturePath, done));
});
});
@@ -65,8 +65,8 @@ describe('turbo-cartocss for anonymous maps', function() {
this.testClient.drain(done);
});
it('should get a tile with turbo-cartocss parsed properly', function (done) {
var fixtureFileName = 'test_turbo_cartocss_reds_13_4011_3088.png';
it('should get a tile with turbo-carto parsed properly', function (done) {
var fixtureFileName = 'test_turbo_carto_reds_13_4011_3088.png';
this.testClient.getTile(13, 4011, 3088, imageCompareFn(fixtureFileName, done));
});
});
@@ -115,12 +115,12 @@ describe('turbo-cartocss for anonymous maps', function() {
this.testClient.drain(done);
});
it('should get a tile with turbo-cartocss parsed properly', function (done) {
it('should get a tile with turbo-carto parsed properly', function (done) {
var z = 2;
var x = 2;
var y = 1;
var pngFixture = 'torque/populated_places_simple_reduced-turbo-cartocss-' + [z, x, y].join('.') + '.png';
var pngFixture = 'torque/populated_places_simple_reduced-turbo-carto-' + [z, x, y].join('.') + '.png';
this.testClient.getTile(z, x, y, { layers: 0, format: 'torque.png' }, imageCompareFn(pngFixture, done));
});

View File

@@ -1,14 +1,14 @@
var assert = require('../support/assert');
var assert = require('../../support/assert');
var step = require('step');
var LayergroupToken = require('../../lib/cartodb/models/layergroup_token');
var testHelper = require(__dirname + '/../support/test_helper');
var CartodbWindshaft = require(__dirname + '/../../lib/cartodb/server');
var serverOptions = require(__dirname + '/../../lib/cartodb/server_options');
var LayergroupToken = require('../../../lib/cartodb/models/layergroup_token');
var testHelper = require('../../support/test_helper');
var CartodbWindshaft = require('../../../lib/cartodb/server');
var serverOptions = require('../../../lib/cartodb/server_options');
var server = new CartodbWindshaft(serverOptions);
var mapnik = require('windshaft').mapnik;
var IMAGE_TOLERANCE_PER_MIL = 10;
describe('turbo-cartocss for named maps', function() {
describe('turbo-carto for named maps', function() {
var keysToDelete;
@@ -20,7 +20,7 @@ describe('turbo-cartocss for named maps', function() {
testHelper.deleteRedisKeys(keysToDelete, done);
});
var templateId = 'turbo-cartocss-template-1';
var templateId = 'turbo-carto-template-1';
var template = {
version: '0.0.1',
@@ -65,7 +65,7 @@ describe('turbo-cartocss for named maps', function() {
var templateParamsReds = { color: 'Reds' };
var templateParamsBlues = { color: 'Blues' };
it('should create a template with turbo-cartocss parsed properly', function (done) {
it('should create a template with turbo-carto parsed properly', function (done) {
step(
function postTemplate() {
var next = this;
@@ -144,7 +144,7 @@ describe('turbo-cartocss for named maps', function() {
assert.equal(res.statusCode, 200);
assert.equal(res.headers['content-type'], 'image/png');
var fixturePath = './test/fixtures/turbo-cartocss-named-maps-reds.png';
var fixturePath = './test/fixtures/turbo-carto-named-maps-reds.png';
var image = mapnik.Image.fromBytes(new Buffer(res.body, 'binary'));
assert.imageIsSimilarToFile(image, fixturePath, IMAGE_TOLERANCE_PER_MIL, next);
@@ -203,7 +203,7 @@ describe('turbo-cartocss for named maps', function() {
assert.equal(res.statusCode, 200);
assert.equal(res.headers['content-type'], 'image/png');
var fixturePath = './test/fixtures/turbo-cartocss-named-maps-blues.png';
var fixturePath = './test/fixtures/turbo-carto-named-maps-blues.png';
var image = mapnik.Image.fromBytes(new Buffer(res.body, 'binary'));
assert.imageIsSimilarToFile(image, fixturePath, IMAGE_TOLERANCE_PER_MIL, next);

View File

@@ -1,7 +1,7 @@
require('../support/test_helper');
require('../../support/test_helper');
var assert = require('../support/assert');
var TestClient = require('../support/test-client');
var assert = require('../../support/assert');
var TestClient = require('../../support/test-client');
function makeMapconfig(cartocss) {
return {
@@ -31,7 +31,7 @@ function makeMapconfig(cartocss) {
};
}
describe('turbo-cartocss regressions', function() {
describe('turbo-carto regressions', function() {
var cartocss = [
"/** simple visualization */",

View File

@@ -114,6 +114,34 @@ describe('histogram widgets', function() {
testClient.drain(done);
});
});
it("should expose a filtered histogram using dataviews for filtering", function(done) {
var params = {
filters: {
dataviews: {
country_places_histogram: { min: 4000000 }
}
}
};
var testClient = new TestClient(histogramMapConfig);
testClient.getWidget('country_places_histogram', params, function(err, res) {
if (err) {
return done(err);
}
var histogram = JSON.parse(res.body);
// notice min value
assert.deepEqual(histogram.bins[0], {
bin: 0,
freq: 62,
min: 4000000,
max: 9276403,
avg: 5815009.596774193
});
testClient.drain(done);
});
});
});
});

View File

@@ -113,7 +113,7 @@ describe('named-maps widgets', function() {
}
);
},
function fetchTile(err, res) {
function finish(err, res) {
assert.ifError(err);
layergroup = JSON.parse(res.body);

View File

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 52 KiB

View File

Before

Width:  |  Height:  |  Size: 356 B

After

Width:  |  Height:  |  Size: 356 B

View File

Before

Width:  |  Height:  |  Size: 349 B

After

Width:  |  Height:  |  Size: 349 B

View File

@@ -73,12 +73,13 @@ describe('MapConfigOverviewsAdapter', function() {
assert.equal(layers[0].options.cartocss_version, cartocss_version);
assert.ok(layers[0].options.query_rewrite_data);
var expected_data = {
overviews: {
test_table_overviews: {
1: { table: '_vovw_1_test_table_overviews' },
2: { table: '_vovw_2_test_table_overviews' }
overviews: {
test_table_overviews: {
schema: 'public',
1: { table: '_vovw_1_test_table_overviews' },
2: { table: '_vovw_2_test_table_overviews' }
}
}
}
};
assert.deepEqual(layers[0].options.query_rewrite_data, expected_data);
done();

View File

@@ -40,8 +40,9 @@ describe('OverviewsMetadataApi', function() {
assert.deepEqual(result, {
'test_table_overviews': {
1: { table: '_vovw_1_test_table_overviews' },
2: { table: '_vovw_2_test_table_overviews' }
schema: 'public',
1: { table: '_vovw_1_test_table_overviews' },
2: { table: '_vovw_2_test_table_overviews' }
}
});

View File

@@ -313,13 +313,15 @@ TestClient.prototype.getTile = function(z, x, y, params, callback) {
assert.response(server, request, expectedResponse, function(res, err) {
assert.ifError(err);
var image;
var obj;
if (isPng) {
image = mapnik.Image.fromBytes(new Buffer(res.body, 'binary'));
obj = mapnik.Image.fromBytes(new Buffer(res.body, 'binary'));
} else {
obj = JSON.parse(res.body);
}
next(null, res, image);
next(null, res, obj);
});
},
function finish(err, res, image) {

View File

@@ -3,11 +3,11 @@ require('../../support/test_helper');
var assert = require('assert');
var OverviewsQueryRewriter = require('../../../lib/cartodb/utils/overviews_query_rewriter');
var overviewsQueryRewriter = new OverviewsQueryRewriter({
zoom_level: 'ZoomLevel()'
zoom_level: 'ZoomLevel()'
});
function normalize_whitespace(txt) {
return txt.replace(/\s+/g, " ").trim();
return txt.replace(/\s+/g, " ").trim();
}
// compare SQL statements ignoring whitespace
@@ -17,410 +17,462 @@ function assertSameSql(sql1, sql2) {
describe('Overviews query rewriter', function() {
it('does not alter queries if no overviews data is present', function(done){
var sql = "SELECT * FROM table1";
var overviews_sql = overviewsQueryRewriter.query(sql);
assert.equal(overviews_sql, sql);
overviews_sql = overviewsQueryRewriter.query(sql, {});
assert.equal(overviews_sql, sql);
overviews_sql = overviewsQueryRewriter.query(sql, { overviews: {} });
assert.equal(overviews_sql, sql);
done();
});
it('does not alter queries if no overviews data is present', function(){
var sql = "SELECT * FROM table1";
var overviews_sql = overviewsQueryRewriter.query(sql);
assert.equal(overviews_sql, sql);
overviews_sql = overviewsQueryRewriter.query(sql, {});
assert.equal(overviews_sql, sql);
overviews_sql = overviewsQueryRewriter.query(sql, { overviews: {} });
assert.equal(overviews_sql, sql);
});
it('does not alter queries which don\'t use overviews', function(done){
var sql = "SELECT * FROM table1";
var data = {
overviews: {
table2: {
0: { table: 'table2_ov0' },
1: { table: 'table2_ov1' },
4: { table: 'table2_ov4' }
it('does not alter queries which don\'t use overviews', function(){
var sql = "SELECT * FROM table1";
var data = {
overviews: {
table2: {
0: { table: 'table2_ov0' },
1: { table: 'table2_ov1' },
4: { table: 'table2_ov4' }
}
}
}
};
var overviews_sql = overviewsQueryRewriter.query(sql, data);
assert.equal(overviews_sql, sql);
done();
});
};
var overviews_sql = overviewsQueryRewriter.query(sql, data);
assert.equal(overviews_sql, sql);
});
// jshint multistr:true
// jshint multistr:true
it('generates query with single overview layer for level 0', function(done){
var sql = "SELECT * FROM table1";
var data = {
overviews: {
table1: {
0: { table: 'table1_ov0' }
it('generates query with single overview layer for level 0', function(){
var sql = "SELECT * FROM table1";
var data = {
overviews: {
table1: {
0: { table: 'table1_ov0' }
}
}
}
};
var overviews_sql = overviewsQueryRewriter.query(sql, data);
};
var overviews_sql = overviewsQueryRewriter.query(sql, data);
var expected_sql = "\
WITH\
_vovw_scale AS ( SELECT ZoomLevel() AS _vovw_z )\
SELECT * FROM (\
SELECT * FROM table1_ov0, _vovw_scale WHERE _vovw_z = 0\
UNION ALL\
SELECT * FROM table1, _vovw_scale WHERE _vovw_z > 0\
) AS _vovw_table1\
";
assertSameSql(overviews_sql, expected_sql);
done();
});
var expected_sql = "\
WITH\
_vovw_scale AS ( SELECT ZoomLevel() AS _vovw_z )\
SELECT * FROM (\
SELECT * FROM table1_ov0, _vovw_scale WHERE _vovw_z = 0\
UNION ALL\
SELECT * FROM table1, _vovw_scale WHERE _vovw_z > 0\
) AS _vovw_table1\
";
assertSameSql(overviews_sql, expected_sql);
});
it('generates query with single overview layer for level >0', function(done){
var sql = "SELECT * FROM table1";
var data = {
overviews: {
table1: {
2: { table: 'table1_ov2' }
it('generates query with single overview layer for level >0', function(){
var sql = "SELECT * FROM table1";
var data = {
overviews: {
table1: {
2: { table: 'table1_ov2' }
}
}
}
};
var overviews_sql = overviewsQueryRewriter.query(sql, data);
var expected_sql = "\
WITH\
_vovw_scale AS ( SELECT ZoomLevel() AS _vovw_z )\
SELECT * FROM (\
SELECT * FROM table1_ov2, _vovw_scale WHERE _vovw_z <= 2\
UNION ALL\
SELECT * FROM table1, _vovw_scale WHERE _vovw_z > 2\
) AS _vovw_table1\
";
assertSameSql(overviews_sql, expected_sql);
done();
});
};
var overviews_sql = overviewsQueryRewriter.query(sql, data);
var expected_sql = "\
WITH\
_vovw_scale AS ( SELECT ZoomLevel() AS _vovw_z )\
SELECT * FROM (\
SELECT * FROM table1_ov2, _vovw_scale WHERE _vovw_z <= 2\
UNION ALL\
SELECT * FROM table1, _vovw_scale WHERE _vovw_z > 2\
) AS _vovw_table1\
";
assertSameSql(overviews_sql, expected_sql);
});
it('generates query with multiple overview layers for all levels up to N', function(done){
var sql = "SELECT * FROM table1";
var data = {
overviews: {
table1: {
0: { table: 'table1_ov0' },
1: { table: 'table1_ov1' },
2: { table: 'table1_ov2' },
3: { table: 'table1_ov3' }
it('generates query with multiple overview layers for all levels up to N', function(){
var sql = "SELECT * FROM table1";
var data = {
overviews: {
table1: {
0: { table: 'table1_ov0' },
1: { table: 'table1_ov1' },
2: { table: 'table1_ov2' },
3: { table: 'table1_ov3' }
}
}
}
};
var overviews_sql = overviewsQueryRewriter.query(sql, data);
var expected_sql = "\
WITH\
_vovw_scale AS ( SELECT ZoomLevel() AS _vovw_z )\
SELECT * FROM (\
SELECT * FROM table1_ov0, _vovw_scale WHERE _vovw_z = 0\
UNION ALL\
SELECT * FROM table1_ov1, _vovw_scale WHERE _vovw_z = 1\
UNION ALL\
SELECT * FROM table1_ov2, _vovw_scale WHERE _vovw_z = 2\
UNION ALL\
SELECT * FROM table1_ov3, _vovw_scale WHERE _vovw_z = 3\
UNION ALL\
SELECT * FROM table1, _vovw_scale WHERE _vovw_z > 3\
) AS _vovw_table1\
";
assertSameSql(overviews_sql, expected_sql);
done();
});
};
var overviews_sql = overviewsQueryRewriter.query(sql, data);
var expected_sql = "\
WITH\
_vovw_scale AS ( SELECT ZoomLevel() AS _vovw_z )\
SELECT * FROM (\
SELECT * FROM table1_ov0, _vovw_scale WHERE _vovw_z = 0\
UNION ALL\
SELECT * FROM table1_ov1, _vovw_scale WHERE _vovw_z = 1\
UNION ALL\
SELECT * FROM table1_ov2, _vovw_scale WHERE _vovw_z = 2\
UNION ALL\
SELECT * FROM table1_ov3, _vovw_scale WHERE _vovw_z = 3\
UNION ALL\
SELECT * FROM table1, _vovw_scale WHERE _vovw_z > 3\
) AS _vovw_table1\
";
assertSameSql(overviews_sql, expected_sql);
});
it('generates query with multiple overview layers for random levels', function(done){
var sql = "SELECT * FROM table1";
var data = {
overviews: {
table1: {
0: { table: 'table1_ov0' },
1: { table: 'table1_ov1' },
6: { table: 'table1_ov6' }
it('generates query with multiple overview layers for random levels', function(){
var sql = "SELECT * FROM table1";
var data = {
overviews: {
table1: {
0: { table: 'table1_ov0' },
1: { table: 'table1_ov1' },
6: { table: 'table1_ov6' }
}
}
}
};
var overviews_sql = overviewsQueryRewriter.query(sql, data);
var expected_sql = "\
WITH\
_vovw_scale AS ( SELECT ZoomLevel() AS _vovw_z )\
SELECT * FROM (\
SELECT * FROM table1_ov0, _vovw_scale WHERE _vovw_z = 0\
UNION ALL\
SELECT * FROM table1_ov1, _vovw_scale WHERE _vovw_z = 1\
UNION ALL\
SELECT * FROM table1_ov6, _vovw_scale WHERE _vovw_z > 1 AND _vovw_z <= 6\
UNION ALL\
SELECT * FROM table1, _vovw_scale WHERE _vovw_z > 6\
) AS _vovw_table1\
";
assertSameSql(overviews_sql, expected_sql);
done();
});
};
var overviews_sql = overviewsQueryRewriter.query(sql, data);
var expected_sql = "\
WITH\
_vovw_scale AS ( SELECT ZoomLevel() AS _vovw_z )\
SELECT * FROM (\
SELECT * FROM table1_ov0, _vovw_scale WHERE _vovw_z = 0\
UNION ALL\
SELECT * FROM table1_ov1, _vovw_scale WHERE _vovw_z = 1\
UNION ALL\
SELECT * FROM table1_ov6, _vovw_scale WHERE _vovw_z > 1 AND _vovw_z <= 6\
UNION ALL\
SELECT * FROM table1, _vovw_scale WHERE _vovw_z > 6\
) AS _vovw_table1\
";
assertSameSql(overviews_sql, expected_sql);
});
it('generates query for a table with explicit schema', function(done){
var sql = "SELECT * FROM public.table1";
var data = {
overviews: {
'public.table1': {
2: { table: 'table1_ov2' }
it('generates query for a table with explicit schema', function(){
var sql = "SELECT * FROM public.table1";
var data = {
overviews: {
'public.table1': {
2: { table: 'table1_ov2' }
}
}
}
};
var overviews_sql = overviewsQueryRewriter.query(sql, data);
var expected_sql = "\
WITH\
_vovw_scale AS ( SELECT ZoomLevel() AS _vovw_z )\
SELECT * FROM (\
SELECT * FROM public.table1_ov2, _vovw_scale WHERE _vovw_z <= 2\
UNION ALL\
SELECT * FROM public.table1, _vovw_scale WHERE _vovw_z > 2\
) AS _vovw_table1\
";
assertSameSql(overviews_sql, expected_sql);
done();
});
};
var overviews_sql = overviewsQueryRewriter.query(sql, data);
var expected_sql = "\
WITH\
_vovw_scale AS ( SELECT ZoomLevel() AS _vovw_z )\
SELECT * FROM (\
SELECT * FROM public.table1_ov2, _vovw_scale WHERE _vovw_z <= 2\
UNION ALL\
SELECT * FROM public.table1, _vovw_scale WHERE _vovw_z > 2\
) AS _vovw_table1\
";
assertSameSql(overviews_sql, expected_sql);
});
it('generates query for a table with explicit schema in the overviews info', function(done){
var sql = "SELECT * FROM public.table1";
var data = {
overviews: {
'public.table1': {
2: { table: 'table1_ov2' }
it('generates query for a table with explicit schema in the overviews info', function(){
var sql = "SELECT * FROM public.table1";
var data = {
overviews: {
'public.table1': {
2: { table: 'table1_ov2' }
}
}
}
};
var overviews_sql = overviewsQueryRewriter.query(sql, data);
var expected_sql = "\
WITH\
_vovw_scale AS ( SELECT ZoomLevel() AS _vovw_z )\
SELECT * FROM (\
SELECT * FROM public.table1_ov2, _vovw_scale WHERE _vovw_z <= 2\
UNION ALL\
SELECT * FROM public.table1, _vovw_scale WHERE _vovw_z > 2\
) AS _vovw_table1\
";
};
var overviews_sql = overviewsQueryRewriter.query(sql, data);
var expected_sql = "\
WITH\
_vovw_scale AS ( SELECT ZoomLevel() AS _vovw_z )\
SELECT * FROM (\
SELECT * FROM public.table1_ov2, _vovw_scale WHERE _vovw_z <= 2\
UNION ALL\
SELECT * FROM public.table1, _vovw_scale WHERE _vovw_z > 2\
) AS _vovw_table1\
";
assertSameSql(overviews_sql, expected_sql);
done();
});
assertSameSql(overviews_sql, expected_sql);
});
it('generates query for a table that needs quoting with explicit schema', function(done){
var sql = "SELECT * FROM public.\"table 1\"";
var data = {
overviews: {
'public."table 1"': {
2: { table: '"table 1_ov2"' }
it('uses schema name from overviews', function(){
var sql = "SELECT * FROM public.table1";
var data = {
overviews: {
'table1': {
schema: 'public',
2: { table: 'table1_ov2' }
}
}
}
};
var overviews_sql = overviewsQueryRewriter.query(sql, data);
var expected_sql = "\
WITH\
_vovw_scale AS ( SELECT ZoomLevel() AS _vovw_z )\
SELECT * FROM (\
SELECT * FROM public.\"table 1_ov2\", _vovw_scale WHERE _vovw_z <= 2\
UNION ALL\
SELECT * FROM public.\"table 1\", _vovw_scale WHERE _vovw_z > 2\
) AS \"_vovw_table 1\"\
";
assertSameSql(overviews_sql, expected_sql);
done();
});
};
var overviews_sql = overviewsQueryRewriter.query(sql, data);
var expected_sql = "\
WITH\
_vovw_scale AS ( SELECT ZoomLevel() AS _vovw_z )\
SELECT * FROM (\
SELECT * FROM table1_ov2, _vovw_scale WHERE _vovw_z <= 2\
UNION ALL\
SELECT * FROM table1, _vovw_scale WHERE _vovw_z > 2\
) AS _vovw_table1\
";
assertSameSql(overviews_sql, expected_sql);
});
it('generates query for a table with explicit schema that needs quoting', function(done){
var sql = "SELECT * FROM \"user-1\".table1";
var data = {
overviews: {
'"user-1".table1': {
2: { table: 'table1_ov2' }
it('ignores schema name from overviews if not necessary', function(){
var sql = "SELECT * FROM table1";
var data = {
overviews: {
'table1': {
schema: 'public',
2: { table: 'table1_ov2' }
}
}
}
};
var overviews_sql = overviewsQueryRewriter.query(sql, data);
var expected_sql = "\
WITH\
_vovw_scale AS ( SELECT ZoomLevel() AS _vovw_z )\
SELECT * FROM (\
SELECT * FROM \"user-1\".table1_ov2, _vovw_scale WHERE _vovw_z <= 2\
UNION ALL\
SELECT * FROM \"user-1\".table1, _vovw_scale WHERE _vovw_z > 2\
) AS _vovw_table1\
";
assertSameSql(overviews_sql, expected_sql);
done();
});
it('generates query for a table with explicit schema both needing quoting', function(done){
var sql = "SELECT * FROM \"user-1\".\"table 1\"";
var data = {
overviews: {
'"user-1"."table 1"': {
2: { table: '"table 1_ov2"' }
};
var overviews_sql = overviewsQueryRewriter.query(sql, data);
var expected_sql = "\
WITH\
_vovw_scale AS ( SELECT ZoomLevel() AS _vovw_z )\
SELECT * FROM (\
SELECT * FROM table1_ov2, _vovw_scale WHERE _vovw_z <= 2\
UNION ALL\
SELECT * FROM table1, _vovw_scale WHERE _vovw_z > 2\
) AS _vovw_table1\
";
assertSameSql(overviews_sql, expected_sql);
});
it('uses redundant schema information', function(){
var sql = "SELECT * FROM public.table1";
var data = {
overviews: {
'public.table1': {
schema: 'public',
2: { table: 'table1_ov2' }
}
}
}
};
var overviews_sql = overviewsQueryRewriter.query(sql, data);
var expected_sql = "\
WITH\
_vovw_scale AS ( SELECT ZoomLevel() AS _vovw_z )\
SELECT * FROM (\
SELECT * FROM \"user-1\".\"table 1_ov2\", _vovw_scale WHERE _vovw_z <= 2\
UNION ALL\
SELECT * FROM \"user-1\".\"table 1\", _vovw_scale WHERE _vovw_z > 2\
) AS \"_vovw_table 1\"\
";
assertSameSql(overviews_sql, expected_sql);
done();
});
};
var overviews_sql = overviewsQueryRewriter.query(sql, data);
var expected_sql = "\
WITH\
_vovw_scale AS ( SELECT ZoomLevel() AS _vovw_z )\
SELECT * FROM (\
SELECT * FROM public.table1_ov2, _vovw_scale WHERE _vovw_z <= 2\
UNION ALL\
SELECT * FROM public.table1, _vovw_scale WHERE _vovw_z > 2\
) AS _vovw_table1\
";
assertSameSql(overviews_sql, expected_sql);
});
it('generates query using overviews for queries with selected columns', function(done){
var sql = "SELECT column1, column2, column3 FROM table1";
var data = {
overviews: {
table1: {
2: { table: 'table1_ov2' }
it('generates query for a table that needs quoting with explicit schema', function(){
var sql = "SELECT * FROM public.\"table 1\"";
var data = {
overviews: {
'public."table 1"': {
2: { table: '"table 1_ov2"' }
}
}
}
};
var overviews_sql = overviewsQueryRewriter.query(sql, data);
var expected_sql = "\
WITH\
_vovw_scale AS ( SELECT ZoomLevel() AS _vovw_z )\
SELECT column1, column2, column3 FROM (\
SELECT * FROM table1_ov2, _vovw_scale WHERE _vovw_z <= 2\
UNION ALL\
SELECT * FROM table1, _vovw_scale WHERE _vovw_z > 2\
) AS _vovw_table1\
";
assertSameSql(overviews_sql, expected_sql);
done();
});
};
var overviews_sql = overviewsQueryRewriter.query(sql, data);
var expected_sql = "\
WITH\
_vovw_scale AS ( SELECT ZoomLevel() AS _vovw_z )\
SELECT * FROM (\
SELECT * FROM public.\"table 1_ov2\", _vovw_scale WHERE _vovw_z <= 2\
UNION ALL\
SELECT * FROM public.\"table 1\", _vovw_scale WHERE _vovw_z > 2\
) AS \"_vovw_table 1\"\
";
assertSameSql(overviews_sql, expected_sql);
});
it('generates query using overviews for queries with a semicolon', function(done){
var sql = "SELECT column1, column2, column3 FROM table1;";
var data = {
overviews: {
table1: {
2: { table: 'table1_ov2' }
it('generates query for a table with explicit schema that needs quoting', function(){
var sql = "SELECT * FROM \"user-1\".table1";
var data = {
overviews: {
'"user-1".table1': {
2: { table: 'table1_ov2' }
}
}
}
};
var overviews_sql = overviewsQueryRewriter.query(sql, data);
};
var overviews_sql = overviewsQueryRewriter.query(sql, data);
var expected_sql = "\
WITH\
_vovw_scale AS ( SELECT ZoomLevel() AS _vovw_z )\
SELECT * FROM (\
SELECT * FROM \"user-1\".table1_ov2, _vovw_scale WHERE _vovw_z <= 2\
UNION ALL\
SELECT * FROM \"user-1\".table1, _vovw_scale WHERE _vovw_z > 2\
) AS _vovw_table1\
";
assertSameSql(overviews_sql, expected_sql);
});
var expected_sql = "\
WITH\
_vovw_scale AS ( SELECT ZoomLevel() AS _vovw_z )\
SELECT column1, column2, column3 FROM (\
SELECT * FROM table1_ov2, _vovw_scale WHERE _vovw_z <= 2\
UNION ALL\
SELECT * FROM table1, _vovw_scale WHERE _vovw_z > 2\
) AS _vovw_table1;\
";
assertSameSql(overviews_sql, expected_sql);
done();
});
it('generates query using overviews for queries with extra whitespace', function(done){
var sql = " SELECT column1,column2, column3 FROM table1 ";
var data = {
overviews: {
table1: {
2: { table: 'table1_ov2' }
it('generates query for a table with explicit schema both needing quoting', function(){
var sql = "SELECT * FROM \"user-1\".\"table 1\"";
var data = {
overviews: {
'"user-1"."table 1"': {
2: { table: '"table 1_ov2"' }
}
}
}
};
var overviews_sql = overviewsQueryRewriter.query(sql, data);
var expected_sql = "\
WITH\
_vovw_scale AS ( SELECT ZoomLevel() AS _vovw_z )\
SELECT column1,column2, column3 FROM (\
SELECT * FROM table1_ov2, _vovw_scale WHERE _vovw_z <= 2\
UNION ALL\
SELECT * FROM table1, _vovw_scale WHERE _vovw_z > 2\
) AS _vovw_table1\
";
assertSameSql(overviews_sql, expected_sql);
done();
});
};
var overviews_sql = overviewsQueryRewriter.query(sql, data);
var expected_sql = "\
WITH\
_vovw_scale AS ( SELECT ZoomLevel() AS _vovw_z )\
SELECT * FROM (\
SELECT * FROM \"user-1\".\"table 1_ov2\", _vovw_scale WHERE _vovw_z <= 2\
UNION ALL\
SELECT * FROM \"user-1\".\"table 1\", _vovw_scale WHERE _vovw_z > 2\
) AS \"_vovw_table 1\"\
";
assertSameSql(overviews_sql, expected_sql);
});
it('does not alter queries which have not the simple supported form', function(done){
var sql = "SELECT * FROM table1 WHERE column1='x'";
var data = {
overviews: {
table1: {
2: { table: 'table1_ov2' }
it('generates query using overviews for queries with selected columns', function(){
var sql = "SELECT column1, column2, column3 FROM table1";
var data = {
overviews: {
table1: {
2: { table: 'table1_ov2' }
}
}
}
};
var overviews_sql = overviewsQueryRewriter.query(sql, data);
assert.equal(overviews_sql, sql);
};
var overviews_sql = overviewsQueryRewriter.query(sql, data);
var expected_sql = "\
WITH\
_vovw_scale AS ( SELECT ZoomLevel() AS _vovw_z )\
SELECT column1, column2, column3 FROM (\
SELECT * FROM table1_ov2, _vovw_scale WHERE _vovw_z <= 2\
UNION ALL\
SELECT * FROM table1, _vovw_scale WHERE _vovw_z > 2\
) AS _vovw_table1\
";
assertSameSql(overviews_sql, expected_sql);
});
sql = "SELECT * FROM table1 JOIN table2 ON (table1.col1=table2.col1)";
overviews_sql = overviewsQueryRewriter.query(sql, data);
assert.equal(overviews_sql, sql);
sql = "SELECT a+b AS c FROM table1";
overviews_sql = overviewsQueryRewriter.query(sql, data);
assert.equal(overviews_sql, sql);
sql = "SELECT f(a) AS b FROM table1";
overviews_sql = overviewsQueryRewriter.query(sql, data);
assert.equal(overviews_sql, sql);
sql = "SELECT * FROM table1 AS x";
overviews_sql = overviewsQueryRewriter.query(sql, data);
assert.equal(overviews_sql, sql);
sql = "WITH a AS (1) SELECT * FROM table1";
overviews_sql = overviewsQueryRewriter.query(sql, data);
assert.equal(overviews_sql, sql);
sql = "SELECT * FROM table1 WHERE a=1";
overviews_sql = overviewsQueryRewriter.query(sql, data);
assert.equal(overviews_sql, sql);
sql = "\
SELECT table1.* FROM table1 \
JOIN areas ON ST_Intersects(table1.the_geom, areas.the_geom) \
WHERE areas.name='A' \
";
overviews_sql = overviewsQueryRewriter.query(sql, data);
assert.equal(overviews_sql, sql);
sql = "SELECT table1.*, column1, column2, column3 FROM table1";
overviews_sql = overviewsQueryRewriter.query(sql, data);
assert.equal(overviews_sql, sql);
done();
});
it('generates overviews for wrapped query', function(done){
var sql = "SELECT * FROM (SELECT * FROM table1) AS wrapped_query WHERE 1=1";
var data = {
overviews: {
table1: {
0: { table: 'table1_ov0' },
1: { table: 'table1_ov1' },
2: { table: 'table1_ov2' }
it('generates query using overviews for queries with a semicolon', function(){
var sql = "SELECT column1, column2, column3 FROM table1;";
var data = {
overviews: {
table1: {
2: { table: 'table1_ov2' }
}
}
}
};
var overviews_sql = overviewsQueryRewriter.query(sql, data);
var expected_sql = "\
WITH\
_vovw_scale AS ( SELECT ZoomLevel() AS _vovw_z )\
SELECT * FROM (SELECT * FROM (\
SELECT * FROM table1_ov0, _vovw_scale WHERE _vovw_z = 0\
UNION ALL\
SELECT * FROM table1_ov1, _vovw_scale WHERE _vovw_z = 1\
UNION ALL\
SELECT * FROM table1_ov2, _vovw_scale WHERE _vovw_z = 2\
UNION ALL\
SELECT * FROM table1, _vovw_scale WHERE _vovw_z > 2\
) AS _vovw_table1) AS wrapped_query WHERE 1=1\
";
assertSameSql(overviews_sql, expected_sql);
done();
});
};
var overviews_sql = overviewsQueryRewriter.query(sql, data);
var expected_sql = "\
WITH\
_vovw_scale AS ( SELECT ZoomLevel() AS _vovw_z )\
SELECT column1, column2, column3 FROM (\
SELECT * FROM table1_ov2, _vovw_scale WHERE _vovw_z <= 2\
UNION ALL\
SELECT * FROM table1, _vovw_scale WHERE _vovw_z > 2\
) AS _vovw_table1;\
";
assertSameSql(overviews_sql, expected_sql);
});
it('generates query using overviews for queries with extra whitespace', function(){
var sql = " SELECT column1,column2, column3 FROM table1 ";
var data = {
overviews: {
table1: {
2: { table: 'table1_ov2' }
}
}
};
var overviews_sql = overviewsQueryRewriter.query(sql, data);
var expected_sql = "\
WITH\
_vovw_scale AS ( SELECT ZoomLevel() AS _vovw_z )\
SELECT column1,column2, column3 FROM (\
SELECT * FROM table1_ov2, _vovw_scale WHERE _vovw_z <= 2\
UNION ALL\
SELECT * FROM table1, _vovw_scale WHERE _vovw_z > 2\
) AS _vovw_table1\
";
assertSameSql(overviews_sql, expected_sql);
});
it('does not alter queries which have not the simple supported form', function(){
var sql = "SELECT * FROM table1 WHERE column1='x'";
var data = {
overviews: {
table1: {
2: { table: 'table1_ov2' }
}
}
};
var overviews_sql = overviewsQueryRewriter.query(sql, data);
assert.equal(overviews_sql, sql);
sql = "SELECT * FROM table1 JOIN table2 ON (table1.col1=table2.col1)";
overviews_sql = overviewsQueryRewriter.query(sql, data);
assert.equal(overviews_sql, sql);
sql = "SELECT a+b AS c FROM table1";
overviews_sql = overviewsQueryRewriter.query(sql, data);
assert.equal(overviews_sql, sql);
sql = "SELECT f(a) AS b FROM table1";
overviews_sql = overviewsQueryRewriter.query(sql, data);
assert.equal(overviews_sql, sql);
sql = "SELECT * FROM table1 AS x";
overviews_sql = overviewsQueryRewriter.query(sql, data);
assert.equal(overviews_sql, sql);
sql = "WITH a AS (1) SELECT * FROM table1";
overviews_sql = overviewsQueryRewriter.query(sql, data);
assert.equal(overviews_sql, sql);
sql = "SELECT * FROM table1 WHERE a=1";
overviews_sql = overviewsQueryRewriter.query(sql, data);
assert.equal(overviews_sql, sql);
sql = "\
SELECT table1.* FROM table1 \
JOIN areas ON ST_Intersects(table1.the_geom, areas.the_geom) \
WHERE areas.name='A' \
";
overviews_sql = overviewsQueryRewriter.query(sql, data);
assert.equal(overviews_sql, sql);
sql = "SELECT table1.*, column1, column2, column3 FROM table1";
overviews_sql = overviewsQueryRewriter.query(sql, data);
assert.equal(overviews_sql, sql);
});
it('generates overviews for wrapped query', function(){
var sql = "SELECT * FROM (SELECT * FROM table1) AS wrapped_query WHERE 1=1";
var data = {
overviews: {
table1: {
0: { table: 'table1_ov0' },
1: { table: 'table1_ov1' },
2: { table: 'table1_ov2' }
}
}
};
var overviews_sql = overviewsQueryRewriter.query(sql, data);
var expected_sql = "\
WITH\
_vovw_scale AS ( SELECT ZoomLevel() AS _vovw_z )\
SELECT * FROM (SELECT * FROM (\
SELECT * FROM table1_ov0, _vovw_scale WHERE _vovw_z = 0\
UNION ALL\
SELECT * FROM table1_ov1, _vovw_scale WHERE _vovw_z = 1\
UNION ALL\
SELECT * FROM table1_ov2, _vovw_scale WHERE _vovw_z = 2\
UNION ALL\
SELECT * FROM table1, _vovw_scale WHERE _vovw_z > 2\
) AS _vovw_table1) AS wrapped_query WHERE 1=1\
";
assertSameSql(overviews_sql, expected_sql);
});
});