diff --git a/lib/cartodb/controllers/map.js b/lib/cartodb/controllers/map.js index 0985530d..34660ce0 100644 --- a/lib/cartodb/controllers/map.js +++ b/lib/cartodb/controllers/map.js @@ -314,6 +314,9 @@ MapController.prototype.augmentLayergroupData = function () { }; }; +function getTemplateUrl(url) { + return url.https || url.http; +} function getTilejson(tiles, grids) { const tilejson = { @@ -364,19 +367,26 @@ MapController.prototype.setTilejsonMetadataToLayergroup = function () { }); const tilejson = {}; + const url = {}; if (hasMapnikLayers) { + const vectorResource = `${layergroup.layergroupid}/{z}/{x}/{y}.mvt`; tilejson.vector = getTilejson( - this.resourceLocator.getTileUrls(user, `${layergroup.layergroupid}/{z}/{x}/{y}.mvt`) + this.resourceLocator.getTileUrls(user, vectorResource) ); + url.vector = getTemplateUrl(this.resourceLocator.getTemplateUrls(user, vectorResource)); + if (!isVectorOnlyMapConfig) { + const rasterResource = `${layergroup.layergroupid}/{z}/{x}/{y}.png`; tilejson.raster = getTilejson( - this.resourceLocator.getTileUrls(user, `${layergroup.layergroupid}/{z}/{x}/{y}.png`) + this.resourceLocator.getTileUrls(user, rasterResource) ); + url.raster = getTemplateUrl(this.resourceLocator.getTemplateUrls(user, rasterResource)); } } layergroup.metadata.tilejson = tilejson; + layergroup.metadata.url = url; next(); }.bind(this); diff --git a/lib/cartodb/models/resource-locator.js b/lib/cartodb/models/resource-locator.js index af777985..f9b1c1ad 100644 --- a/lib/cartodb/models/resource-locator.js +++ b/lib/cartodb/models/resource-locator.js @@ -40,6 +40,24 @@ ResourceLocator.prototype.getTileUrls = function(username, resourcePath) { } }; +ResourceLocator.prototype.getTemplateUrls = function(username, resourcePath) { + if (this.resourcesUrlTemplates) { + return this.getUrlsFromTemplate(username, new TemplateResource(resourcePath), true); + } + var cdnUrls = getCdnUrls(this.environment.serverMetadata, username, new TemplateResource(resourcePath)); + if (cdnUrls) { + return cdnUrls; + } else { + var port = this.environment.port; + return { + http: { + urlTemplate: `http://${username}.localhost.lan:${port}/api/v1/map/${resourcePath}`, + subdomains: [] + } + }; + } +}; + ResourceLocator.prototype.getUrls = function(username, resourcePath) { if (this.resourcesUrlTemplates) { return this.getUrlsFromTemplate(username, new Resource(resourcePath)); @@ -55,45 +73,44 @@ ResourceLocator.prototype.getUrls = function(username, resourcePath) { } }; +function urlForTemplate(tpl, username, cdnDomain, resource, templated) { + cdnDomain = cdnDomain || {}; + if (templated) { + return { + urlTemplate: tpl({ + cdn_url: (cdnDomain.hasOwnProperty('urlTemplate') ? cdnDomain.urlTemplate : cdnDomain), + user: username, + port: this.environment.port, + resource: resource.getPath() + }), + subdomains: cdnDomain.subdomains || [] + }; + } + if (Array.isArray(cdnDomain)) { + return cdnDomain.map(d => tpl({ + cdn_url: d, + user: username, + port: this.environment.port, + resource: resource.getPath() + })); + } else { + return tpl({ + cdn_url: cdnDomain, + user: username, + port: this.environment.port, + resource: resource.getPath() + }); + } +} -ResourceLocator.prototype.getUrlsFromTemplate = function(username, resource) { +ResourceLocator.prototype.getUrlsFromTemplate = function(username, resource, templated) { var urls = {}; var cdnDomain = getCdnDomain(this.environment.serverMetadata, resource) || {}; - if (this.resourcesUrlTemplates.http) { - 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() - }); - } + urls.http = urlForTemplate(this.resourcesUrlTemplates.http, username, cdnDomain.http, resource, templated); } - if (this.resourcesUrlTemplates.https) { - 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() - }); - } + urls.https = urlForTemplate(this.resourcesUrlTemplates.https, username, cdnDomain.https, resource, templated); } return urls; @@ -109,6 +126,9 @@ class Resource { } getDomain (domain, subdomains) { + if (!subdomains) { + return domain; + } return domain.replace('{s}', subdomain(subdomains, this.resourcePath)); } @@ -127,6 +147,9 @@ class TileResource extends Resource { } getDomain (domain, subdomains) { + if (!subdomains) { + return domain; + } return subdomains.map(s => domain.replace('{s}', s)); } @@ -141,6 +164,26 @@ class TileResource extends Resource { } } +class TemplateResource extends Resource { + constructor (resourcePath) { + super(resourcePath); + } + + getDomain (domain, subdomains) { + return { + urlTemplate: domain, + subdomains: subdomains || [] + }; + } + + getUrl (baseUrl, username, subdomains) { + return { + urlTemplate: getUrl(baseUrl, username, this.resourcePath), + subdomains: subdomains || [] + }; + } +} + function getUrl(baseUrl, username, path) { return `${baseUrl}/${username}/api/v1/map/${path}`; } @@ -166,8 +209,8 @@ function getCdnUrls(serverMetadata, username, resource) { function getCdnDomain(serverMetadata, resource) { if (serverMetadata && serverMetadata.cdn_url) { var cdnUrl = serverMetadata.cdn_url; - var httpDomain = cdnUrl.http; - var httpsDomain = cdnUrl.https; + var httpDomain = resource.getDomain(cdnUrl.http); + var httpsDomain = resource.getDomain(cdnUrl.https); if (cdnUrl.templates) { var templates = cdnUrl.templates; var httpUrlTemplate = templates.http.url; diff --git a/test/acceptance/tilejson.js b/test/acceptance/tilejson.js index 56070e42..0d3bc133 100644 --- a/test/acceptance/tilejson.js +++ b/test/acceptance/tilejson.js @@ -8,7 +8,7 @@ describe('tilejson', function() { function tilejsonValidation(tilejson, shouldHaveGrid = false) { assert.equal(tilejson.tilejson, '2.2.0'); - assert.ok(Array.isArray(tilejson.tiles)); + assert.ok(Array.isArray(tilejson.tiles), JSON.stringify(tilejson)); assert.ok(tilejson.tiles.length > 0); if (shouldHaveGrid) { @@ -161,7 +161,7 @@ describe('tilejson', function() { describe('root tilejson', function() { - it('should expose just the `vector` tilejson when for vector only mapnik layers', function(done) { + it('should expose just the `vector` tilejson and URL when for vector only mapnik layers', function(done) { var testClient = new TestClient(mapConfig(VECTOR_LAYER)); testClient.getLayergroup(function(err, layergroupResult) { @@ -176,11 +176,17 @@ describe('tilejson', function() { tilejsonValidation(tilejson[k]); }); + const url = metadata.url; + assert.deepEqual(Object.keys(url), ['vector']); + + assert.ok(url.vector.urlTemplate); + assert.ok(url.vector.subdomains); + testClient.drain(done); }); }); - it('should expose just the `vector` and `raster` tilejson for mapnik layers', function(done) { + it('should expose just the `vector` and `raster` tilejson and urls for mapnik layers', function(done) { var testClient = new TestClient(mapConfig(RASTER_LAYER)); testClient.getLayergroup(function(err, layergroupResult) { @@ -195,6 +201,15 @@ describe('tilejson', function() { tilejsonValidation(tilejson[k]); }); + const url = metadata.url; + assert.deepEqual(Object.keys(url), ['vector', 'raster']); + + assert.ok(url.vector.urlTemplate); + assert.ok(url.vector.subdomains); + + assert.ok(url.raster.urlTemplate); + assert.ok(url.raster.subdomains); + testClient.drain(done); }); }); diff --git a/test/unit/cartodb/model/resource-locator.test.js b/test/unit/cartodb/model/resource-locator.test.js index 2f788d33..13ad5203 100644 --- a/test/unit/cartodb/model/resource-locator.test.js +++ b/test/unit/cartodb/model/resource-locator.test.js @@ -54,6 +54,24 @@ describe('ResourceLocator', function() { ); }); }); + + describe('getTemplateUrls', function() { + it('should return default urls when basic http and https domains are provided', function() { + var resourceLocator = new ResourceLocator(BASIC_ENVIRONMENT); + var urls = resourceLocator.getTemplateUrls(USERNAME, TILE_RESOURCE); + assert.ok(urls); + + assert.deepEqual(urls.http, { + urlTemplate: `http://cdn.carto.com/${USERNAME}/api/v1/map/${TILE_RESOURCE}`, + subdomains: [] + }); + assert.deepEqual(urls.https, { + urlTemplate: `https://cdn.ssl.carto.com/${USERNAME}/api/v1/map/${TILE_RESOURCE}`, + subdomains: [] + }); + }); + }); + }); describe('resource templates', function() { @@ -104,6 +122,24 @@ describe('ResourceLocator', function() { ); }); }); + + describe('getTemplateUrls', function() { + it('resources_url_templates should take precedence over http and https domains', function() { + var resourceLocator = new ResourceLocator(RESOURCE_TEMPLATES_ENVIRONMENT); + var urls = resourceLocator.getTemplateUrls(USERNAME, TILE_RESOURCE); + assert.ok(urls); + + assert.deepEqual(urls.http, { + urlTemplate: `http://${USERNAME}.localhost.lan/api/v1/map/${TILE_RESOURCE}`, + subdomains: [] + }); + assert.deepEqual(urls.https, { + urlTemplate: `https://${USERNAME}.ssl.localhost.lan/api/v1/map/${TILE_RESOURCE}`, + subdomains: [] + }); + }); + }); + }); describe('cdn templates', function() { @@ -126,6 +162,7 @@ describe('ResourceLocator', function() { } } }; + describe('getUrls', function() { it('cdn_url templates should take precedence over http and https domains', function() { var resourceLocator = new ResourceLocator(CDN_TEMPLATES_ENVIRONMENT); @@ -165,6 +202,23 @@ describe('ResourceLocator', function() { }); }); + describe('getTemplateUrls', function() { + it('cdn_url templates should take precedence over http and https domains', function() { + var resourceLocator = new ResourceLocator(CDN_TEMPLATES_ENVIRONMENT); + var urls = resourceLocator.getTemplateUrls(USERNAME, TILE_RESOURCE); + assert.ok(urls); + + assert.deepEqual(urls.http, { + urlTemplate: `http://{s}.cdn.carto.com/${USERNAME}/api/v1/map/${TILE_RESOURCE}`, + subdomains: HTTP_SUBDOMAINS + }); + assert.deepEqual(urls.https, { + urlTemplate: `https://cdn_{s}.ssl.cdn.carto.com/${USERNAME}/api/v1/map/${TILE_RESOURCE}`, + subdomains: HTTPS_SUBDOMAINS + }); + }); + }); + }); describe('cdn and resource templates', function() { @@ -231,6 +285,23 @@ describe('ResourceLocator', function() { }); }); + describe('getTemplateUrls', 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.getTemplateUrls(USERNAME, TILE_RESOURCE); + assert.ok(urls); + + assert.deepEqual(urls.http, { + urlTemplate: `http://{s}.cdn.carto.com/u/${USERNAME}/api/v1/map/${TILE_RESOURCE}`, + subdomains: HTTP_SUBDOMAINS + }); + assert.deepEqual(urls.https, { + urlTemplate: `https://cdn_{s}.ssl.cdn.carto.com/u/${USERNAME}/api/v1/map/${TILE_RESOURCE}`, + subdomains: HTTPS_SUBDOMAINS + }); + }); + }); + }); });