diff --git a/lib/api/api-router.js b/lib/api/api-router.js index ab2bf332..466a6b01 100644 --- a/lib/api/api-router.js +++ b/lib/api/api-router.js @@ -57,6 +57,7 @@ const user = require('./middlewares/user'); const sendResponse = require('./middlewares/send-response'); const syntaxError = require('./middlewares/syntax-error'); const errorMiddleware = require('./middlewares/error-middleware'); +const clientHeader = require('./middlewares/client-header'); const MapRouter = require('./map/map-router'); const TemplateRouter = require('./template/template-router'); @@ -208,6 +209,7 @@ module.exports = class ApiRouter { apiRouter.use(initializeStatusCode()); apiRouter.use(bodyParser.json()); apiRouter.use(servedByHostHeader()); + apiRouter.use(clientHeader()); apiRouter.use(stats({ enabled: this.serverOptions.useProfiler, statsClient: global.statsClient diff --git a/lib/api/map/preview-template-controller.js b/lib/api/map/preview-template-controller.js index d9a64707..10a40b93 100644 --- a/lib/api/map/preview-template-controller.js +++ b/lib/api/map/preview-template-controller.js @@ -353,6 +353,8 @@ function incrementMapViews ({ metadataBackend }) { const statTag = mapConfig.obj().stat_tag; + res.set('Carto-Stat-Tag', `${statTag}`); + metadataBackend.incMapviewCount(user, statTag, (err) => { if (err) { global.logger.log(incrementMapViewsError({ user, err })); diff --git a/lib/api/middlewares/client-header.js b/lib/api/middlewares/client-header.js new file mode 100644 index 00000000..de750aed --- /dev/null +++ b/lib/api/middlewares/client-header.js @@ -0,0 +1,13 @@ +'use strict'; + +module.exports = function clientHeader () { + return function clientHeaderMiddleware (req, res, next) { + const { client } = req.query; + + if (client) { + res.set('Carto-Client', client); + } + + return next(); + }; +}; diff --git a/lib/api/middlewares/increment-map-view-count.js b/lib/api/middlewares/increment-map-view-count.js index 00bb4673..20ddcf42 100644 --- a/lib/api/middlewares/increment-map-view-count.js +++ b/lib/api/middlewares/increment-map-view-count.js @@ -3,9 +3,12 @@ module.exports = function incrementMapViewCount (metadataBackend) { return function incrementMapViewCountMiddleware (req, res, next) { const { mapConfig, user } = res.locals; + const statTag = mapConfig.obj().stat_tag; + + res.set('Carto-Stat-Tag', `${statTag}`); // Error won't blow up, just be logged. - metadataBackend.incMapviewCount(user, mapConfig.obj().stat_tag, (err) => { + metadataBackend.incMapviewCount(user, statTag, (err) => { req.profiler.done('incMapviewCount'); if (err) { diff --git a/lib/api/middlewares/user.js b/lib/api/middlewares/user.js index 57c9b45d..ce8c73bf 100644 --- a/lib/api/middlewares/user.js +++ b/lib/api/middlewares/user.js @@ -14,6 +14,7 @@ module.exports = function user (metadataBackend) { } res.locals.userId = userId; + res.set('Carto-User-Id', `${userId}`); return next(); }); }; diff --git a/test/acceptance/layergroup-metadata-test.js b/test/acceptance/layergroup-metadata-test.js index 1543f230..d20fc0a5 100644 --- a/test/acceptance/layergroup-metadata-test.js +++ b/test/acceptance/layergroup-metadata-test.js @@ -17,7 +17,7 @@ describe('layergroup metadata', function () { serverOptions.renderer.mvt.usePostGIS = originalUsePostGIS; }); - [1234, 'default_public', false].forEach(apiKey => { + [1234, 'default_public', undefined].forEach(apiKey => { it(`tiles base urls ${apiKey ? `with api key: ${apiKey}` : 'without api key'}`, function (done) { const mapConfig = { version: '1.7.0', diff --git a/test/acceptance/map-view-headers-test.js b/test/acceptance/map-view-headers-test.js new file mode 100644 index 00000000..858f7715 --- /dev/null +++ b/test/acceptance/map-view-headers-test.js @@ -0,0 +1,97 @@ +'use strict'; + +require('../support/test-helper'); + +const assert = require('../support/assert'); +const TestClient = require('../support/test-client'); + +const defaultLayers = [{ + type: 'cartodb', + options: { + sql: TestClient.SQL.ONE_POINT + } +}]; +const defaultStatTag = 'wadus'; + +function createMapConfig (layers = defaultLayers, statTag = defaultStatTag) { + return { + version: '1.8.0', + layers: layers, + stat_tag: defaultStatTag + }; +} + +describe('map view headers', function () { + it('anonymous map instantiation should respond with map-view headers', function (done) { + const mapConfig = createMapConfig(); + const testClient = new TestClient(mapConfig); + const params = { client: 'test' }; + + testClient.getLayergroup(params, (err, body, res) => { + if (err) { + return done(err); + } + + assert.strictEqual(res.headers['carto-stat-tag'], defaultStatTag); + assert.strictEqual(res.headers['carto-client'], params.client); + assert.strictEqual(res.headers['carto-user-id'], '1'); + + testClient.drain(done); + }); + }); + + it('named map instantiation should respond with map-view headers', function (done) { + const templateid = `map-view-headers-test-${Date.now()}`; + const template = { + version: '0.0.1', + name: templateid, + layergroup: createMapConfig() + }; + + const testClient = new TestClient(template, 1234); + const params = { client: 'test' }; + + testClient.getLayergroup(params, (err, body, res) => { + if (err) { + return done(err); + } + + assert.strictEqual(res.headers['carto-stat-tag'], defaultStatTag); + assert.strictEqual(res.headers['carto-client'], params.client); + assert.strictEqual(res.headers['carto-user-id'], '1'); + + testClient.drain(done); + }); + }); + + it('preview should respond with map-view headers', function (done) { + const templateid = `map-view-headers-test-${Date.now()}`; + const template = { + version: '0.0.1', + name: templateid, + layergroup: createMapConfig([{ + type: 'cartodb', + options: { + sql: TestClient.SQL.ONE_POINT, + cartocss: TestClient.CARTOCSS.POINTS, + cartocss_version: '2.3.0' + } + }]) + }; + + const testClient = new TestClient(template, 1234); + const params = { client: 'test' }; + + testClient.getPreview(640, 480, params, (err, res) => { + if (err) { + return done(err); + } + + assert.strictEqual(res.headers['carto-stat-tag'], defaultStatTag); + assert.strictEqual(res.headers['carto-client'], params.client); + assert.strictEqual(res.headers['carto-user-id'], '1'); + + testClient.drain(done); + }); + }); +}); diff --git a/test/support/test-client.js b/test/support/test-client.js index 36183a0d..45fb198e 100644 --- a/test/support/test-client.js +++ b/test/support/test-client.js @@ -942,24 +942,8 @@ TestClient.prototype.getLayergroup = function (params, callback) { params = {}; } - let url = '/api/v1/map'; - const urlNamed = url + '/named'; const headers = Object.assign({ host: 'localhost', 'Content-Type': 'application/json' }, self.extraHeaders); - const queryParams = {}; - - if (self.apiKey) { - queryParams.api_key = self.apiKey; - } - - if (params.aggregation !== undefined) { - queryParams.aggregation = params.aggregation; - } - - if (Object.keys(queryParams).length) { - url += '?' + qs.stringify(queryParams); - } - var layergroupId; if (params.layergroupid) { @@ -982,7 +966,7 @@ TestClient.prototype.getLayergroup = function (params, callback) { assert.response(self.server, { - url: urlNamed + '?' + qs.stringify({ api_key: self.apiKey }), + url: `/api/v1/map/named?${qs.stringify({ api_key: self.apiKey })}`, method: 'POST', headers, data: JSON.stringify(self.template) @@ -1023,9 +1007,10 @@ TestClient.prototype.getLayergroup = function (params, callback) { }; } + const url = '/api/v1/map'; const queryParams = {}; - if (self.apiKey) { + if (self.apiKey !== undefined) { queryParams.api_key = self.apiKey; } @@ -1033,9 +1018,14 @@ TestClient.prototype.getLayergroup = function (params, callback) { queryParams.aggregation = params.aggregation; } + if (params.client !== undefined) { + queryParams.client = params.client; + } + + const query = Object.keys(queryParams).length ? `?${qs.stringify(queryParams)}` : ''; const path = templateId - ? urlNamed + '/' + templateId + '?' + qs.stringify(queryParams) - : url; + ? `${url}/named/${templateId}${query}` + : `${url}${query}`; assert.response(self.server, { @@ -1058,12 +1048,15 @@ TestClient.prototype.getLayergroup = function (params, callback) { if (res.statusCode === 200 && self.template && self.template.layergroup && self.template.layergroup.stat_tag) { self.keysToDelete[`user:localhost:mapviews:stat_tag:${self.template.layergroup.stat_tag}`] = 5; } + if (res.statusCode === 200 && self.mapConfig && self.mapConfig.stat_tag) { + self.keysToDelete[`user:localhost:mapviews:stat_tag:${self.mapConfig.stat_tag}`] = 5; + } } if (err) { return callback(err); } - return callback(null, parsedBody); + return callback(null, parsedBody, res); } ); }