Skip to content
Permalink
Browse files

feat(metrics): add endpoint for emitting amplitude metrics

  • Loading branch information...
philbooth authored and clouserw committed Oct 3, 2019
1 parent 07d51be commit 74355e2f5545697a8a974bd3cb1cfef6eccd4295
@@ -0,0 +1,84 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

'use strict';

const { GROUPS, initialize } = require('../../../fxa-shared/metrics/amplitude');
const log = require('./logging/log')();
const ua = require('./user-agent');
const { version: VERSION } = require('../../package.json');

const FUZZY_EVENTS = new Map([
[
// Emit events from the front-end as `amplitude.${GROUP}.${EVENT}`
/^amplitude\.([\w-]+)\.([\w-]+)$/,
{
group: group => GROUPS[group],
event: (group, event) => event,
},
],
]);

const transform = initialize({}, {}, FUZZY_EVENTS);

module.exports = (event, request, data) => {
if (!event || !request || !data) {
return;
}

const userAgent = ua.parse(request.headers['user-agent']);

const amplitudeEvent = transform(event, {
version: VERSION,
...mapBrowser(userAgent),
...mapOs(userAgent),
...mapFormFactor(userAgent),
...mapLocation(data.location),
...data,
});

if (amplitudeEvent) {
log.info('amplitudeEvent', amplitudeEvent);
}
};

function mapBrowser(userAgent) {
return mapUserAgentProperties(userAgent, 'ua', 'browser', 'browserVersion');
}

function mapOs(userAgent) {
return mapUserAgentProperties(userAgent, 'os', 'os', 'osVersion');
}

function mapUserAgentProperties(
userAgent,
key,
familyProperty,
versionProperty
) {
const group = userAgent[key];
const { family } = group;
if (family && family !== 'Other') {
return {
[familyProperty]: family,
[versionProperty]: group.toVersionString(),
};
}
}

function mapFormFactor(userAgent) {
const { brand, family: formFactor } = userAgent.device;
if (brand && formFactor && brand !== 'Generic') {
return { formFactor };
}
}

function mapLocation(location) {
if (location && (location.country || location.state)) {
return {
country: location.country,
region: location.state,
};
}
}
@@ -2,4 +2,4 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

module.exports = [];
module.exports = [require('./post-metrics')];
@@ -0,0 +1,33 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

'use strict';

const amplitude = require('../amplitude');

module.exports = {
method: 'post',
path: '/metrics',

handler(request, response) {
if (!validate(request.body)) {
return response.status(400).end();
}

const { data, events } = request.body;
events.forEach(event => amplitude(event, request, data));
response.status(200).end();
},
};

function validate(body) {
// TODO: replace this with JSON schema or hapi or whatever
return (
body.data &&
typeof body.data === 'object' &&
!Array.isArray(body.data) &&
Array.isArray(body.events) &&
body.events.every(event => typeof event === 'string')
);
}
@@ -10,6 +10,7 @@ module.exports = () => {

// setup version first for the rest of the modules
const logger = require('./logging/log')('server.main');
const routes = require('./routes');
const version = require('./version');
const config = require('../config');

@@ -190,6 +191,9 @@ module.exports = () => {
res.send(renderedStaticHtml);
});
});
routes.forEach(route => {
app[route.method](route.path, route.handler);
});
app.use(
serveStatic(STATIC_DIRECTORY, {
maxAge: config.get('staticResources.maxAge'),

0 comments on commit 74355e2

Please sign in to comment.
You can’t perform that action at this time.