From eab9e8846e82903c64e8b6b83e478500a1741759 Mon Sep 17 00:00:00 2001 From: Raul Ochoa Date: Wed, 3 Jan 2018 12:57:01 +0000 Subject: [PATCH 1/7] Reorg suite to accommodate getTileURLs --- .../cartodb/model/resource-locator.test.js | 215 ++++++++++-------- 1 file changed, 120 insertions(+), 95 deletions(-) diff --git a/test/unit/cartodb/model/resource-locator.test.js b/test/unit/cartodb/model/resource-locator.test.js index 2f4c5b88..5c8f4bab 100644 --- a/test/unit/cartodb/model/resource-locator.test.js +++ b/test/unit/cartodb/model/resource-locator.test.js @@ -3,7 +3,7 @@ require('../../../support/test_helper'); var assert = require('../../../support/assert'); var ResourceLocator = require('../../../../lib/cartodb/models/resource-locator'); -describe('ResourceLocator.getUrls', function() { +describe('ResourceLocator', function() { var USERNAME = 'username'; var RESOURCE = 'wadus'; var HTTP_SUBDOMAINS = ['1', '2', '3', '4']; @@ -15,118 +15,143 @@ describe('ResourceLocator.getUrls', function() { assert.ok(urls); }); - var BASIC_ENVIRONMENT = { - serverMetadata: { - cdn_url: { - http: 'cdn.carto.com', - https: 'cdn.ssl.carto.com' - } - } - }; - it('should return default urls when basic http and https domains are provided', function() { - var resourceLocator = new ResourceLocator(BASIC_ENVIRONMENT); - var urls = resourceLocator.getUrls(USERNAME, RESOURCE); - assert.ok(urls); + describe('basic', function() { - assert.equal(urls.http, ['http://cdn.carto.com', USERNAME, 'api/v1/map', RESOURCE].join('/')); - assert.equal(urls.https, ['https://cdn.ssl.carto.com', USERNAME, 'api/v1/map', RESOURCE].join('/')); + var BASIC_ENVIRONMENT = { + serverMetadata: { + cdn_url: { + http: 'cdn.carto.com', + https: 'cdn.ssl.carto.com' + } + } + }; + + describe('getUrls', function() { + it('should return default urls when basic http and https domains are provided', function() { + var resourceLocator = new ResourceLocator(BASIC_ENVIRONMENT); + var urls = resourceLocator.getUrls(USERNAME, RESOURCE); + assert.ok(urls); + + assert.equal(urls.http, ['http://cdn.carto.com', USERNAME, 'api/v1/map', RESOURCE].join('/')); + assert.equal(urls.https, ['https://cdn.ssl.carto.com', USERNAME, 'api/v1/map', RESOURCE].join('/')); + }); + }); }); - var RESOURCE_TEMPLATES_ENVIRONMENT = { - serverMetadata: { - cdn_url: { - http: 'cdn.carto.com', - https: 'cdn.ssl.carto.com' - } - }, - resources_url_templates: { - http: 'http://{{=it.user}}.localhost.lan/api/v1/map', - https: 'https://{{=it.user}}.ssl.localhost.lan/api/v1/map' - } - }; - it('resources_url_templates should take precedence over http and https domains', function() { - var resourceLocator = new ResourceLocator(RESOURCE_TEMPLATES_ENVIRONMENT); - var urls = resourceLocator.getUrls(USERNAME, RESOURCE); - assert.ok(urls); + describe('resource templates', function() { - assert.equal(urls.http, ['http://' + USERNAME + '.localhost.lan', 'api/v1/map', RESOURCE].join('/')); - assert.equal(urls.https, ['https://' + USERNAME + '.ssl.localhost.lan', 'api/v1/map', RESOURCE].join('/')); + var RESOURCE_TEMPLATES_ENVIRONMENT = { + serverMetadata: { + cdn_url: { + http: 'cdn.carto.com', + https: 'cdn.ssl.carto.com' + } + }, + resources_url_templates: { + http: 'http://{{=it.user}}.localhost.lan/api/v1/map', + https: 'https://{{=it.user}}.ssl.localhost.lan/api/v1/map' + } + }; + + describe('getUrls', function() { + it('resources_url_templates should take precedence over http and https domains', function() { + var resourceLocator = new ResourceLocator(RESOURCE_TEMPLATES_ENVIRONMENT); + var urls = resourceLocator.getUrls(USERNAME, RESOURCE); + assert.ok(urls); + + assert.equal(urls.http, ['http://' + USERNAME + '.localhost.lan', 'api/v1/map', RESOURCE].join('/')); + assert.equal(urls.https, ['https://' + USERNAME + '.ssl.localhost.lan', 'api/v1/map', RESOURCE].join('/')); + }); + }); }); - var CDN_TEMPLATES_ENVIRONMENT = { - serverMetadata: { - cdn_url: { - http: 'cdn.carto.com', - https: 'cdn.ssl.carto.com', - templates: { - http: { - url: "http://{s}.cdn.carto.com", - subdomains: HTTP_SUBDOMAINS - }, - https: { - url: "https://cdn_{s}.ssl.cdn.carto.com", - subdomains: HTTPS_SUBDOMAINS + describe('cdn templates', function() { + + var CDN_TEMPLATES_ENVIRONMENT = { + serverMetadata: { + cdn_url: { + http: 'cdn.carto.com', + https: 'cdn.ssl.carto.com', + templates: { + http: { + url: "http://{s}.cdn.carto.com", + subdomains: HTTP_SUBDOMAINS + }, + https: { + url: "https://cdn_{s}.ssl.cdn.carto.com", + subdomains: HTTPS_SUBDOMAINS + } } } } - } - }; - it('cdn_url templates should take precedence over http and https domains', function() { - var resourceLocator = new ResourceLocator(CDN_TEMPLATES_ENVIRONMENT); - var urls = resourceLocator.getUrls(USERNAME, RESOURCE); - assert.ok(urls); + }; + describe('getUrls', function() { + it('cdn_url templates should take precedence over http and https domains', function() { + var resourceLocator = new ResourceLocator(CDN_TEMPLATES_ENVIRONMENT); + var urls = resourceLocator.getUrls(USERNAME, RESOURCE); + assert.ok(urls); - var httpSubdomain = ResourceLocator.subdomain(HTTP_SUBDOMAINS, RESOURCE); - var httpsSubdomain = ResourceLocator.subdomain(HTTPS_SUBDOMAINS, RESOURCE); + var httpSubdomain = ResourceLocator.subdomain(HTTP_SUBDOMAINS, RESOURCE); + var httpsSubdomain = ResourceLocator.subdomain(HTTPS_SUBDOMAINS, RESOURCE); + + assert.equal( + urls.http, + ['http://' + httpSubdomain + '.cdn.carto.com', USERNAME, 'api/v1/map', RESOURCE].join('/') + ); + assert.equal( + urls.https, + ['https://cdn_' + httpsSubdomain + '.ssl.cdn.carto.com', USERNAME, 'api/v1/map', RESOURCE].join('/') + ); + }); + }); - assert.equal( - urls.http, - ['http://' + httpSubdomain + '.cdn.carto.com', USERNAME, 'api/v1/map', RESOURCE].join('/') - ); - assert.equal( - urls.https, - ['https://cdn_' + httpsSubdomain + '.ssl.cdn.carto.com', USERNAME, 'api/v1/map', RESOURCE].join('/') - ); }); - var CDN_URL_AND_RESOURCE_TEMPLATES_ENVIRONMENT = { - serverMetadata: { - cdn_url: { - http: 'cdn.carto.com', - https: 'cdn.ssl.carto.com', - templates: { - http: { - url: "http://{s}.cdn.carto.com", - subdomains: HTTP_SUBDOMAINS - }, - https: { - url: "https://cdn_{s}.ssl.cdn.carto.com", - subdomains: HTTPS_SUBDOMAINS + describe('cdn and resource templates', function() { + + var CDN_URL_AND_RESOURCE_TEMPLATES_ENVIRONMENT = { + serverMetadata: { + cdn_url: { + http: 'cdn.carto.com', + https: 'cdn.ssl.carto.com', + templates: { + http: { + url: "http://{s}.cdn.carto.com", + subdomains: HTTP_SUBDOMAINS + }, + https: { + url: "https://cdn_{s}.ssl.cdn.carto.com", + subdomains: HTTPS_SUBDOMAINS + } } } + }, + resources_url_templates: { + http: 'http://{{=it.cdn_url}}/u/{{=it.user}}/api/v1/map', + https: 'https://{{=it.cdn_url}}/u/{{=it.user}}/api/v1/map' } - }, - resources_url_templates: { - http: 'http://{{=it.cdn_url}}/u/{{=it.user}}/api/v1/map', - https: 'https://{{=it.cdn_url}}/u/{{=it.user}}/api/v1/map' - } - }; - it('should mix cdn_url templates and resources_url_templates', function() { - var resourceLocator = new ResourceLocator(CDN_URL_AND_RESOURCE_TEMPLATES_ENVIRONMENT); - var urls = resourceLocator.getUrls(USERNAME, RESOURCE); - assert.ok(urls); + }; - var httpSubdomain = ResourceLocator.subdomain(HTTP_SUBDOMAINS, RESOURCE); - var httpsSubdomain = ResourceLocator.subdomain(HTTPS_SUBDOMAINS, RESOURCE); + describe('getUrls', function() { + it('should mix cdn_url templates and resources_url_templates', function() { + var resourceLocator = new ResourceLocator(CDN_URL_AND_RESOURCE_TEMPLATES_ENVIRONMENT); + var urls = resourceLocator.getUrls(USERNAME, RESOURCE); + assert.ok(urls); + + var httpSubdomain = ResourceLocator.subdomain(HTTP_SUBDOMAINS, RESOURCE); + var httpsSubdomain = ResourceLocator.subdomain(HTTPS_SUBDOMAINS, RESOURCE); + + assert.equal( + urls.http, + ['http://' + httpSubdomain + '.cdn.carto.com', 'u', USERNAME, 'api/v1/map', RESOURCE].join('/') + ); + assert.equal( + urls.https, + ['https://cdn_' + httpsSubdomain + '.ssl.cdn.carto.com', 'u', USERNAME, 'api/v1/map', RESOURCE].join('/') + ); + }); + }); - assert.equal( - urls.http, - ['http://' + httpSubdomain + '.cdn.carto.com', 'u', USERNAME, 'api/v1/map', RESOURCE].join('/') - ); - assert.equal( - urls.https, - ['https://cdn_' + httpsSubdomain + '.ssl.cdn.carto.com', 'u', USERNAME, 'api/v1/map', RESOURCE].join('/') - ); }); }); From 95dfd87c96295517ce0fb046ccebcc29729a947f Mon Sep 17 00:00:00 2001 From: Raul Ochoa Date: Wed, 3 Jan 2018 16:04:31 +0100 Subject: [PATCH 2/7] Add test cases for getTilesUrls --- .../cartodb/model/resource-locator.test.js | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/test/unit/cartodb/model/resource-locator.test.js b/test/unit/cartodb/model/resource-locator.test.js index 5c8f4bab..78bdde88 100644 --- a/test/unit/cartodb/model/resource-locator.test.js +++ b/test/unit/cartodb/model/resource-locator.test.js @@ -6,6 +6,7 @@ var ResourceLocator = require('../../../../lib/cartodb/models/resource-locator') describe('ResourceLocator', function() { var USERNAME = 'username'; var RESOURCE = 'wadus'; + var TILE_RESOURCE = 'wadus/{z}/{x}/{y}.png'; var HTTP_SUBDOMAINS = ['1', '2', '3', '4']; var HTTPS_SUBDOMAINS = ['a', 'b', 'c', 'd']; @@ -36,6 +37,23 @@ describe('ResourceLocator', function() { assert.equal(urls.https, ['https://cdn.ssl.carto.com', USERNAME, 'api/v1/map', RESOURCE].join('/')); }); }); + + describe('getTileUrls', function() { + it('should return default urls when basic http and https domains are provided', function() { + var resourceLocator = new ResourceLocator(BASIC_ENVIRONMENT); + var urls = resourceLocator.getTileUrls(USERNAME, TILE_RESOURCE); + assert.ok(urls); + + assert.deepEqual( + urls.http, + [`http://cdn.carto.com/${USERNAME}/api/v1/map/${TILE_RESOURCE}`] + ); + assert.deepEqual( + urls.https, + [`https://cdn.ssl.carto.com/${USERNAME}/api/v1/map/${TILE_RESOURCE}`] + ); + }); + }); }); describe('resource templates', function() { @@ -63,6 +81,23 @@ describe('ResourceLocator', function() { assert.equal(urls.https, ['https://' + USERNAME + '.ssl.localhost.lan', 'api/v1/map', RESOURCE].join('/')); }); }); + + describe('getTileUrls', function() { + it('resources_url_templates should take precedence over http and https domains', function() { + var resourceLocator = new ResourceLocator(RESOURCE_TEMPLATES_ENVIRONMENT); + var urls = resourceLocator.getTileUrls(USERNAME, TILE_RESOURCE); + assert.ok(urls); + + assert.deepEqual( + urls.http, + [`http://${USERNAME}.localhost.lan/api/v1/map/${TILE_RESOURCE}`] + ); + assert.deepEqual( + urls.https, + [`https://${USERNAME}.ssl.localhost.lan/api/v1/map/${TILE_RESOURCE}`] + ); + }); + }); }); describe('cdn templates', function() { @@ -105,6 +140,25 @@ describe('ResourceLocator', function() { }); }); + describe('getTileUrls', function() { + it('cdn_url templates should take precedence over http and https domains', function() { + var resourceLocator = new ResourceLocator(CDN_TEMPLATES_ENVIRONMENT); + var urls = resourceLocator.getTileUrls(USERNAME, TILE_RESOURCE); + assert.ok(urls); + + assert.deepEqual( + urls.http, + HTTP_SUBDOMAINS + .map(s => `http://${s}.cdn.carto.com/${USERNAME}/api/v1/map/${TILE_RESOURCE}`) + ); + assert.deepEqual( + urls.https, + HTTPS_SUBDOMAINS + .map(s => `https://cdn_${s}.ssl.cdn.carto.com/${USERNAME}/api/v1/map/${TILE_RESOURCE}`) + ); + }); + }); + }); describe('cdn and resource templates', function() { @@ -152,6 +206,25 @@ describe('ResourceLocator', function() { }); }); + describe('getTileUrls', function() { + it('should mix cdn_url templates and resources_url_templates', function() { + var resourceLocator = new ResourceLocator(CDN_URL_AND_RESOURCE_TEMPLATES_ENVIRONMENT); + var urls = resourceLocator.getTileUrls(USERNAME, TILE_RESOURCE); + assert.ok(urls); + + assert.deepEqual( + urls.http, + HTTP_SUBDOMAINS + .map(s => `http://${s}.cdn.carto.com/u/${USERNAME}/api/v1/map/${TILE_RESOURCE}`) + ); + assert.deepEqual( + urls.https, + HTTPS_SUBDOMAINS + .map(s => `https://cdn_${s}.ssl.cdn.carto.com/u/${USERNAME}/api/v1/map/${TILE_RESOURCE}`) + ); + }); + }); + }); }); From 5d4d2bddd62c2672d873acf571e45c7d95f6abbb Mon Sep 17 00:00:00 2001 From: Raul Ochoa Date: Wed, 3 Jan 2018 16:05:19 +0100 Subject: [PATCH 3/7] Implementation for getTilesUrls This will be useful for generating the tilejson in the metadata --- lib/cartodb/models/resource-locator.js | 156 ++++++++++++++++++++----- 1 file changed, 126 insertions(+), 30 deletions(-) diff --git a/lib/cartodb/models/resource-locator.js b/lib/cartodb/models/resource-locator.js index e12ef2b4..af777985 100644 --- a/lib/cartodb/models/resource-locator.js +++ b/lib/cartodb/models/resource-locator.js @@ -21,20 +21,36 @@ function ResourceLocator(environment) { module.exports = ResourceLocator; -ResourceLocator.prototype.getUrls = function(username, resource) { +ResourceLocator.prototype.getTileUrls = function(username, resourcePath) { if (this.resourcesUrlTemplates) { - return this.getUrlsFromTemplate(username, resource); - } - var cdnDomain = getCdnDomain(this.environment.serverMetadata, resource); - if (cdnDomain) { + const urls = this.getUrlsFromTemplate(username, new TileResource(resourcePath)); return { - http: 'http://' + cdnDomain.http + '/' + username + '/api/v1/map/' + resource, - https: 'https://' + cdnDomain.https + '/' + username + '/api/v1/map/' + resource + http: Array.isArray(urls.http) ? urls.http : [urls.http], + https: Array.isArray(urls.https) ? urls.https : [urls.https] }; + } + var cdnUrls = getCdnUrls(this.environment.serverMetadata, username, new TileResource(resourcePath)); + if (cdnUrls) { + return cdnUrls; } else { var port = this.environment.port; return { - http: 'http://' + username + '.' + 'localhost.lan:' + port + '/api/v1/map/' + resource + http: [`http://${username}.localhost.lan:${port}/api/v1/map/${resourcePath}`] + }; + } +}; + +ResourceLocator.prototype.getUrls = function(username, resourcePath) { + if (this.resourcesUrlTemplates) { + return this.getUrlsFromTemplate(username, new Resource(resourcePath)); + } + var cdnUrls = getCdnUrls(this.environment.serverMetadata, username, new Resource(resourcePath)); + if (cdnUrls) { + return cdnUrls; + } else { + var port = this.environment.port; + return { + http: `http://${username}.localhost.lan:${port}/api/v1/map/${resourcePath}` }; } }; @@ -45,45 +61,125 @@ ResourceLocator.prototype.getUrlsFromTemplate = function(username, resource) { var cdnDomain = getCdnDomain(this.environment.serverMetadata, resource) || {}; if (this.resourcesUrlTemplates.http) { - urls.http = this.resourcesUrlTemplates.http({ - cdn_url: cdnDomain.http, - user: username, - port: this.environment.port, - resource: resource - }); + if (Array.isArray(cdnDomain.http)) { + urls.http = cdnDomain.http.map(d => this.resourcesUrlTemplates.http({ + cdn_url: d, + user: username, + port: this.environment.port, + resource: resource.getPath() + })); + } else { + urls.http = this.resourcesUrlTemplates.http({ + cdn_url: cdnDomain.http, + user: username, + port: this.environment.port, + resource: resource.getPath() + }); + } } if (this.resourcesUrlTemplates.https) { - urls.https = this.resourcesUrlTemplates.https({ - cdn_url: cdnDomain.https, - user: username, - port: this.environment.port, - resource: resource - }); + if (Array.isArray(cdnDomain.https)) { + urls.https = cdnDomain.https.map(d => this.resourcesUrlTemplates.https({ + cdn_url: d, + user: username, + port: this.environment.port, + resource: resource.getPath() + })); + } else { + urls.https = this.resourcesUrlTemplates.https({ + cdn_url: cdnDomain.https, + user: username, + port: this.environment.port, + resource: resource.getPath() + }); + } } return urls; }; +class Resource { + constructor (resourcePath) { + this.resourcePath = resourcePath; + } + + getPath () { + return this.resourcePath; + } + + getDomain (domain, subdomains) { + return domain.replace('{s}', subdomain(subdomains, this.resourcePath)); + } + + getUrl (baseUrl, username, subdomains) { + let urls = getUrl(baseUrl, username, this.resourcePath); + if (subdomains) { + urls = urls.replace('{s}', subdomain(subdomains, this.resourcePath)); + } + return urls; + } +} + +class TileResource extends Resource { + constructor (resourcePath) { + super(resourcePath); + } + + getDomain (domain, subdomains) { + return subdomains.map(s => domain.replace('{s}', s)); + } + + getUrl (baseUrl, username, subdomains) { + if (!subdomains) { + return [super.getUrl(baseUrl, username)]; + } + return subdomains.map(subdomain => { + return getUrl(baseUrl, username, this.resourcePath) + .replace('{s}', subdomain); + }); + } +} + +function getUrl(baseUrl, username, path) { + return `${baseUrl}/${username}/api/v1/map/${path}`; +} + +function getCdnUrls(serverMetadata, username, resource) { + if (serverMetadata && serverMetadata.cdn_url) { + var cdnUrl = serverMetadata.cdn_url; + var httpUrls = resource.getUrl(`http://${cdnUrl.http}`, username); + var httpsUrls = resource.getUrl(`https://${cdnUrl.https}`, username); + if (cdnUrl.templates) { + var templates = cdnUrl.templates; + httpUrls = resource.getUrl(templates.http.url, username, templates.http.subdomains); + httpsUrls = resource.getUrl(templates.https.url, username, templates.https.subdomains); + } + return { + http: httpUrls, + https: httpsUrls, + }; + } + return null; +} + function getCdnDomain(serverMetadata, resource) { if (serverMetadata && serverMetadata.cdn_url) { var cdnUrl = serverMetadata.cdn_url; - var http = cdnUrl.http; - var https = cdnUrl.https; + var httpDomain = cdnUrl.http; + var httpsDomain = cdnUrl.https; if (cdnUrl.templates) { var templates = cdnUrl.templates; var httpUrlTemplate = templates.http.url; var httpsUrlTemplate = templates.https.url; - http = httpUrlTemplate - .replace(/^(http[s]*:\/\/)/, '') - .replace('{s}', subdomain(templates.http.subdomains, resource)); - https = httpsUrlTemplate - .replace(/^(http[s]*:\/\/)/, '') - .replace('{s}', subdomain(templates.https.subdomains, resource)); + httpDomain = httpUrlTemplate.replace(/^(http[s]*:\/\/)/, ''); + httpDomain = resource.getDomain(httpDomain, templates.http.subdomains); + httpsDomain = httpsUrlTemplate.replace(/^(http[s]*:\/\/)/, ''); + httpsDomain = resource.getDomain(httpsDomain, templates.https.subdomains); } return { - http: http, - https: https, + http: httpDomain, + https: httpsDomain, }; } return null; From bf40b240d3d796241fa18f7404e52cab09d9bdb0 Mon Sep 17 00:00:00 2001 From: Raul Ochoa Date: Wed, 3 Jan 2018 16:54:45 +0100 Subject: [PATCH 4/7] Return tilejson in metadata It returns tilejson for each individual layer and also for all vector and raster layers. --- lib/cartodb/controllers/map.js | 69 +++++++++++ test/acceptance/tilejson.js | 204 +++++++++++++++++++++++++++++++++ 2 files changed, 273 insertions(+) create mode 100644 test/acceptance/tilejson.js diff --git a/lib/cartodb/controllers/map.js b/lib/cartodb/controllers/map.js index 185a4f8d..3e17f7bc 100644 --- a/lib/cartodb/controllers/map.js +++ b/lib/cartodb/controllers/map.js @@ -90,6 +90,7 @@ MapController.prototype.composeCreateMapMiddleware = function (useTemplate = fal this.setAnalysesMetadataToLayergroup(includeQuery), this.setTurboCartoMetadataToLayergroup(), this.setAggregationMetadataToLayergroup(), + this.setTilejsonMetadataToLayergroup(), this.setSurrogateKeyHeader(), this.sendResponse(), this.augmentError({ label, addContext }) @@ -313,6 +314,74 @@ MapController.prototype.augmentLayergroupData = function () { }; }; + +function getTilejson(tiles, grids) { + const tilejson = { + tilejson: '2.2.0', + tiles: tiles.https || tiles.http + }; + + if (grids) { + tilejson.grids = grids.https || grids.http; + } + + return tilejson; +} + +MapController.prototype.setTilejsonMetadataToLayergroup = function () { + return function augmentLayergroupDataMiddleware (req, res, next) { + const { layergroup, user, mapconfig } = res.locals; + + const isVectorOnlyMapConfig = mapconfig.isVectorOnlyMapConfig(); + let hasMapnikLayers = false; + layergroup.metadata.layers.forEach((layerMetadata, index) => { + const layerId = mapconfig.getLayerId(index); + const rasterResource = `${layergroup.layergroupid}/${layerId}/{z}/{x}/{y}.png`; + if (mapconfig.layerType(index) === 'mapnik') { + hasMapnikLayers = true; + const vectorResource = `${layergroup.layergroupid}/${layerId}/{z}/{x}/{y}.mvt`; + const layerTilejson = { + vector: getTilejson(this.resourceLocator.getTileUrls(user, vectorResource)) + }; + if (!isVectorOnlyMapConfig) { + let grids = null; + const layer = mapconfig.getLayer(index); + if (layer.options.interactivity) { + const gridResource = `${layergroup.layergroupid}/${layerId}/{z}/{x}/{y}.grid.json`; + grids = this.resourceLocator.getTileUrls(user, gridResource); + } + layerTilejson.raster = getTilejson( + this.resourceLocator.getTileUrls(user, rasterResource), + grids + ); + } + layerMetadata.tilejson = layerTilejson; + } else { + layerMetadata.tilejson = { + raster: getTilejson(this.resourceLocator.getTileUrls(user, rasterResource)) + }; + } + }); + + const tilejson = {}; + + if (hasMapnikLayers) { + tilejson.vector = getTilejson( + this.resourceLocator.getTileUrls(user, `${layergroup.layergroupid}/{z}/{x}/{y}.mvt`) + ); + if (!isVectorOnlyMapConfig) { + tilejson.raster = getTilejson( + this.resourceLocator.getTileUrls(user, `${layergroup.layergroupid}/{z}/{x}/{y}.png`) + ); + } + } + + layergroup.metadata.tilejson = tilejson; + + next(); + }.bind(this); +}; + MapController.prototype.getAffectedTables = function () { return function getAffectedTablesMiddleware (req, res, next) { const { dbname, layergroup, user, mapconfig } = res.locals; diff --git a/test/acceptance/tilejson.js b/test/acceptance/tilejson.js new file mode 100644 index 00000000..56070e42 --- /dev/null +++ b/test/acceptance/tilejson.js @@ -0,0 +1,204 @@ +require('../support/test_helper'); + +const assert = require('../support/assert'); +const TestClient = require('../support/test-client'); + +describe('tilejson', function() { + + function tilejsonValidation(tilejson, shouldHaveGrid = false) { + assert.equal(tilejson.tilejson, '2.2.0'); + + assert.ok(Array.isArray(tilejson.tiles)); + assert.ok(tilejson.tiles.length > 0); + + if (shouldHaveGrid) { + assert.ok(Array.isArray(tilejson.grids)); + assert.ok(tilejson.grids.length > 0); + } + + } + + const sql = 'SELECT * FROM populated_places_simple_reduced'; + const cartocss = TestClient.CARTOCSS.POINTS; + const cartocss_version = '3.0.12'; + + const RASTER_LAYER = { + options: { + sql, cartocss, cartocss_version + } + }; + const RASTER_INTERACTIVITY_LAYER = { + options: { + sql, cartocss, cartocss_version, + interactivity: ['cartodb_id'] + } + }; + const VECTOR_LAYER = { + options: { + sql + } + }; + const PLAIN_LAYER = { + type: 'plain', + options: { + color: '#000000' + } + }; + + function mapConfig(layers) { + return { + version: '1.7.0', + layers: Array.isArray(layers) ? layers : [layers] + }; + } + + describe('per layer', function() { + it('should expose raster + vector tilejson for raster layers', function(done) { + var testClient = new TestClient(mapConfig(RASTER_LAYER)); + + testClient.getLayergroup(function(err, layergroupResult) { + assert.ok(!err, err); + const metadata = layergroupResult.metadata; + assert.ok(metadata); + + assert.equal(metadata.layers.length, 1); + + const layer = metadata.layers[0]; + assert.deepEqual(Object.keys(layer.tilejson), ['vector', 'raster']); + + Object.keys(layer.tilejson).forEach(k => { + tilejsonValidation(layer.tilejson[k]); + }); + + testClient.drain(done); + }); + }); + + it('should expose just the vector tilejson vector only layers', function(done) { + var testClient = new TestClient(mapConfig(VECTOR_LAYER)); + + testClient.getLayergroup(function(err, layergroupResult) { + assert.ok(!err, err); + const metadata = layergroupResult.metadata; + assert.ok(metadata); + + assert.equal(metadata.layers.length, 1); + + const layer = metadata.layers[0]; + assert.deepEqual(Object.keys(layer.tilejson), ['vector']); + + Object.keys(layer.tilejson).forEach(k => { + tilejsonValidation(layer.tilejson[k]); + }); + + testClient.drain(done); + }); + }); + + it('should expose just the raster tilejson plain layers', function(done) { + var testClient = new TestClient(mapConfig(PLAIN_LAYER)); + + testClient.getLayergroup(function(err, layergroupResult) { + assert.ok(!err, err); + const metadata = layergroupResult.metadata; + assert.ok(metadata); + + assert.equal(metadata.layers.length, 1); + + const layer = metadata.layers[0]; + assert.deepEqual(Object.keys(layer.tilejson), ['raster']); + + Object.keys(layer.tilejson).forEach(k => { + tilejsonValidation(layer.tilejson[k]); + }); + + testClient.drain(done); + }); + }); + + it('should expose grids for the raster layer with interactivity', function(done) { + var testClient = new TestClient(mapConfig(RASTER_INTERACTIVITY_LAYER)); + + testClient.getLayergroup(function(err, layergroupResult) { + assert.ok(!err, err); + const metadata = layergroupResult.metadata; + assert.ok(metadata); + + assert.equal(metadata.layers.length, 1); + + const layer = metadata.layers[0]; + assert.deepEqual(Object.keys(layer.tilejson), ['vector', 'raster']); + + tilejsonValidation(layer.tilejson.vector); + tilejsonValidation(layer.tilejson.raster, true); + + testClient.drain(done); + }); + }); + + it('should work with several layers', function(done) { + var testClient = new TestClient(mapConfig([RASTER_LAYER, RASTER_INTERACTIVITY_LAYER])); + + testClient.getLayergroup(function(err, layergroupResult) { + assert.ok(!err, err); + const metadata = layergroupResult.metadata; + assert.ok(metadata); + + assert.equal(metadata.layers.length, 2); + + assert.deepEqual(Object.keys(metadata.layers[0].tilejson), ['vector', 'raster']); + tilejsonValidation(metadata.layers[0].tilejson.vector); + tilejsonValidation(metadata.layers[0].tilejson.raster); + + assert.deepEqual(Object.keys(metadata.layers[1].tilejson), ['vector', 'raster']); + tilejsonValidation(metadata.layers[1].tilejson.vector); + tilejsonValidation(metadata.layers[1].tilejson.raster, true); + + testClient.drain(done); + }); + }); + }); + + describe('root tilejson', function() { + + it('should expose just the `vector` tilejson when for vector only mapnik layers', function(done) { + var testClient = new TestClient(mapConfig(VECTOR_LAYER)); + + testClient.getLayergroup(function(err, layergroupResult) { + assert.ok(!err, err); + const metadata = layergroupResult.metadata; + assert.ok(metadata); + + const tilejson = metadata.tilejson; + assert.deepEqual(Object.keys(tilejson), ['vector']); + + Object.keys(tilejson).forEach(k => { + tilejsonValidation(tilejson[k]); + }); + + testClient.drain(done); + }); + }); + + it('should expose just the `vector` and `raster` tilejson for mapnik layers', function(done) { + var testClient = new TestClient(mapConfig(RASTER_LAYER)); + + testClient.getLayergroup(function(err, layergroupResult) { + assert.ok(!err, err); + const metadata = layergroupResult.metadata; + assert.ok(metadata); + + const tilejson = metadata.tilejson; + assert.deepEqual(Object.keys(tilejson), ['vector', 'raster']); + + Object.keys(tilejson).forEach(k => { + tilejsonValidation(tilejson[k]); + }); + + testClient.drain(done); + }); + }); + + }); + +}); From c6ba9e6102a89cc4b03aa5a36896033e89fb4e48 Mon Sep 17 00:00:00 2001 From: Raul Ochoa Date: Wed, 3 Jan 2018 16:10:09 +0000 Subject: [PATCH 5/7] Fix linting --- test/unit/cartodb/model/resource-locator.test.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/test/unit/cartodb/model/resource-locator.test.js b/test/unit/cartodb/model/resource-locator.test.js index 78bdde88..073e7c62 100644 --- a/test/unit/cartodb/model/resource-locator.test.js +++ b/test/unit/cartodb/model/resource-locator.test.js @@ -77,8 +77,14 @@ describe('ResourceLocator', function() { var urls = resourceLocator.getUrls(USERNAME, RESOURCE); assert.ok(urls); - assert.equal(urls.http, ['http://' + USERNAME + '.localhost.lan', 'api/v1/map', RESOURCE].join('/')); - assert.equal(urls.https, ['https://' + USERNAME + '.ssl.localhost.lan', 'api/v1/map', RESOURCE].join('/')); + assert.equal( + urls.http, + ['http://' + USERNAME + '.localhost.lan', 'api/v1/map', RESOURCE].join('/') + ); + assert.equal( + urls.https, + ['https://' + USERNAME + '.ssl.localhost.lan', 'api/v1/map', RESOURCE].join('/') + ); }); }); @@ -201,7 +207,7 @@ describe('ResourceLocator', function() { ); assert.equal( urls.https, - ['https://cdn_' + httpsSubdomain + '.ssl.cdn.carto.com', 'u', USERNAME, 'api/v1/map', RESOURCE].join('/') + `https://cdn_${httpsSubdomain}'.ssl.cdn.carto.com/u/${USERNAME}/api/v1/map/${RESOURCE}` ); }); }); From 3ceeaedf02e97b9dbdef53ae40266b0579a7b26c Mon Sep 17 00:00:00 2001 From: Raul Ochoa Date: Wed, 3 Jan 2018 16:19:14 +0000 Subject: [PATCH 6/7] Fix test after breaking it with linting changes --- test/unit/cartodb/model/resource-locator.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/cartodb/model/resource-locator.test.js b/test/unit/cartodb/model/resource-locator.test.js index 073e7c62..2f788d33 100644 --- a/test/unit/cartodb/model/resource-locator.test.js +++ b/test/unit/cartodb/model/resource-locator.test.js @@ -207,7 +207,7 @@ describe('ResourceLocator', function() { ); assert.equal( urls.https, - `https://cdn_${httpsSubdomain}'.ssl.cdn.carto.com/u/${USERNAME}/api/v1/map/${RESOURCE}` + `https://cdn_${httpsSubdomain}.ssl.cdn.carto.com/u/${USERNAME}/api/v1/map/${RESOURCE}` ); }); }); From 060b93c314e147d85b83a0f407dcc615748a0581 Mon Sep 17 00:00:00 2001 From: Raul Ochoa Date: Wed, 3 Jan 2018 18:44:10 +0000 Subject: [PATCH 7/7] Rename middleware fn name --- lib/cartodb/controllers/map.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/cartodb/controllers/map.js b/lib/cartodb/controllers/map.js index 3e17f7bc..0985530d 100644 --- a/lib/cartodb/controllers/map.js +++ b/lib/cartodb/controllers/map.js @@ -329,7 +329,7 @@ function getTilejson(tiles, grids) { } MapController.prototype.setTilejsonMetadataToLayergroup = function () { - return function augmentLayergroupDataMiddleware (req, res, next) { + return function augmentLayergroupTilejsonMiddleware (req, res, next) { const { layergroup, user, mapconfig } = res.locals; const isVectorOnlyMapConfig = mapconfig.isVectorOnlyMapConfig();