Skip to content
This repository was archived by the owner on Mar 19, 2019. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion loop/auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
var hawk = require('express-hawkauth');

var encrypt = require("./encrypt").encrypt;
var errors = require('./errno.json');
var errors = require('./errno');
var hmac = require('./hmac');
var sendError = require('./utils').sendError;
var fxa = require('./fxa');
Expand Down
11 changes: 11 additions & 0 deletions loop/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,17 @@ var conf = convict({
default: "",
env: "ROOMS_HKDF_SECRET"
}
},
ga: {
activated: {
doc: "Should we send POST /events data to Google Analytics while true.",
default: false,
format: Boolean
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Beware of this if you plan on receiving string representations of booleans:

>>> Boolean("true")
true
>>> Boolean("false")
true

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the default is false, it is unlikely that we will use activated: false but I will double check that because in that case we might have the same problem with all other boolean config parameters.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok I doubled checked.
So the configuration file is a JSON file where the arguments are booleans so we should be fine.

  "hekaMetrics": {
    "activated": true
  },

},
id: {
doc: "Google analytics ID.",
format: String
}
}
});

Expand Down
2 changes: 1 addition & 1 deletion loop/fxa.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ var request = require('request');
var conf = require('./config').conf;
var atob = require('atob');
var sendError = require("./utils").sendError;
var errors = require("./errno.json");
var errors = require("./errno");

// Don't be limited by the default node.js HTTP agent.
var agent = new https.Agent();
Expand Down
3 changes: 3 additions & 0 deletions loop/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,9 @@ var rooms = require("./routes/rooms");
rooms(apiRouter, conf, logError, storage, filestorage, auth, validators, tokBox,
simplePush, notifications);

var analytics = require("./routes/analytics").analytics;
analytics(apiRouter, conf, auth, validators);

var session = require("./routes/session");
session(apiRouter, conf, storage, auth);

Expand Down
2 changes: 1 addition & 1 deletion loop/middlewares.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"use strict";

var conf = require("./config").conf;
var loopPackageData = require('../package.json');
var loopPackageData = require('../package');
var os = require("os");

// Assume the hostname will not change once the server is launched.
Expand Down
29 changes: 29 additions & 0 deletions loop/routes/analytics.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/* 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";

var errors = require('../errno');
var sendError = require('../utils').sendError;

exports.sendAnalytics = require('../utils').sendAnalytics;

exports.analytics = function (app, conf, auth, validators) {
/**
* Delete an account and all data associated with it.
**/
app.post('/event', validators.requireParams('event', 'action', 'label'),
auth.requireHawkSession, function(req, res) {
var ga = conf.get("ga");
if (ga.activated) {
module.exports.sendAnalytics(ga.id, req.user, req.body);
res.status(204).json({});
} else {
sendError(
res, 405, errors.UNDEFINED,
"Google Analytics events are not configured for this server."
);
}
});
};
2 changes: 1 addition & 1 deletion loop/routes/call-url.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

"use strict";

var errors = require('../errno.json');
var errors = require('../errno');
var sendError = require('../utils').sendError;

module.exports = function (app, conf, logError, storage, auth, validators) {
Expand Down
2 changes: 1 addition & 1 deletion loop/routes/calls.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

var async = require('async');
var randomBytes = require('crypto').randomBytes;
var errors = require('../errno.json');
var errors = require('../errno');
var hmac = require('../hmac');
var getProgressURL = require('../utils').getProgressURL;
var sendError = require('../utils').sendError;
Expand Down
2 changes: 1 addition & 1 deletion loop/routes/fxa-oauth.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
var randomBytes = require('crypto').randomBytes;
var request = require('request');
var sendError = require('../utils').sendError;
var errors = require('../errno.json');
var errors = require('../errno');
var hmac = require('../hmac');
var encrypt = require('../encrypt').encrypt;

Expand Down
2 changes: 1 addition & 1 deletion loop/routes/registration.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

"use strict";

var errors = require("../errno.json");
var errors = require("../errno");
var sendError = require('../utils').sendError;
var getSimplePushURLS = require('../utils').getSimplePushURLS;

Expand Down
2 changes: 1 addition & 1 deletion loop/routes/rooms.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ var uuid = require('node-uuid');

var decrypt = require('../encrypt').decrypt;
var encrypt = require('../encrypt').encrypt;
var errors = require('../errno.json');
var errors = require('../errno');
var getUserAccount = require('../utils').getUserAccount;
var sendError = require('../utils').sendError;
var tokenlib = require('../tokenlib');
Expand Down
2 changes: 1 addition & 1 deletion loop/routes/validators.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

"use strict";

var errors = require("../errno.json");
var errors = require("../errno");
var sendError = require('../utils').sendError;
var getSimplePushURLS = require('../utils').getSimplePushURLS;
var tokenlib = require('../tokenlib');
Expand Down
11 changes: 11 additions & 0 deletions loop/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

var conf = require('./config').conf;
var decrypt = require('./encrypt').decrypt;
var ua = require('universal-analytics');


function sendError(res, code, errno, error, message, info) {
var errmap = {};
Expand Down Expand Up @@ -92,6 +94,14 @@ function getSimplePushURLS(req, callback) {
callback(null, simplePushURLs);
}

/**
* Create a UA instance and sent an event to it.
**/
function sendAnalytics(gaID, userID, data) {
var userAnalytics = ua(gaID, userID, {strictCidFormat: false, https: true});
userAnalytics.event(data.event, data.action, data.label).send();
}

/**
* Return a unix timestamp in seconds.
**/
Expand Down Expand Up @@ -140,6 +150,7 @@ module.exports = {
time: time,
getUserAccount: getUserAccount,
getSimplePushURLS: getSimplePushURLS,
sendAnalytics: sendAnalytics,
dedupeArray: dedupeArray,
encode: encode,
decode: decode,
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
"sodium": "1.0.13",
"statsd-node": "0.2.3",
"strftime": "0.8.2",
"universal-analytics": "0.3.10",
"urlsafe-base64": "1.0.0",
"ws": "1.0.1",
"yargs": "3.0.4"
Expand Down
100 changes: 96 additions & 4 deletions test/functional_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,10 @@ var conf = loop.conf;
var tokBox = loop.tokBox;
var storage = loop.storage;
var statsdClient = loop.statsdClient;
var getProgressURL = require("../loop/utils").getProgressURL;
var time = require('../loop/utils').time;

var utils = require("../loop/utils");
var getProgressURL = utils.getProgressURL;
var time = utils.time;

var Token = require("express-hawkauth").Token;
var tokenlib = require("../loop/tokenlib");
Expand All @@ -32,6 +34,7 @@ var tokBoxConfig = conf.get("tokBox");
var hmac = require("../loop/hmac");
var pjson = require("../package.json");

var analytics = require("../loop/routes/analytics");
var getMiddlewares = require("./support").getMiddlewares;
var expectFormattedError = require("./support").expectFormattedError;
var errors = require("../loop/errno.json");
Expand Down Expand Up @@ -1720,6 +1723,97 @@ function runOnPrefix(apiPrefix) {
});
});
});

describe("POST /event", function() {
it("should fail if does not have event/label/action.", function(done) {
supertest(app)
.post('/event')
.type('json')
.expect('Content-Type', /json/)
.expect(400)
.end(function(err, res) {
if (err) throw err;
expectFormattedError(res, 400, errors.MISSING_PARAMETERS,
"Missing: event, action, label");
done();
});
});

it("should returns a 401 if no hawk session.", function(done) {
supertest(app)
.post('/event')
.type('json')
.expect('Content-Type', /json/)
.send({'event': 'tab_shared',
'action': 'clicked',
'label': 'Tab shared'})
.expect(401)
.end(done);
});

it("should returns a 405 if disabled in settings.", function(done) {
supertest(app)
.post('/event')
.type('json')
.expect('Content-Type', /json/)
.send({'event': 'tab_shared',
'action': 'clicked',
'label': 'Tab shared'})
.hawk(hawkCredentials)
.expect(405)
.end(done);
});

context("enabled", function() {
var sendAnalyticsStub;
beforeEach(function() {
sendAnalyticsStub = sandbox.stub(analytics, "sendAnalytics");
conf.set('ga', {
activated: true,
id: "fake-ga-id"
});
});

afterEach(function() {
conf.set('ga', {
activated: false,
id: null
});
});

it("should returns a 204 if everything went well.", function(done) {
supertest(app)
.post('/event')
.type('json')
.send({'event': 'tab_shared',
'action': 'clicked',
'label': 'Tab shared'})
.hawk(hawkCredentials)
.expect(204)
.end(done);
});

it("should pass gaID, userID and body to sendAnalytics.", function(done) {
supertest(app)
.post('/event')
.type('json')
.send({'event': 'tab_shared',
'action': 'clicked',
'label': 'Tab shared'})
.hawk(hawkCredentials)
.expect(204)
.end(function(err) {
if (err) throw err;
assert.calledWithExactly(sendAnalyticsStub, "fake-ga-id", userHmac, {
event: 'tab_shared',
action: 'clicked',
label: 'Tab shared'
});
done();
});
});
});
});
});

describe("GET /api-specs", function() {
Expand Down Expand Up @@ -1750,8 +1844,6 @@ function runOnPrefix(apiPrefix) {
});
});
});


}

describe("HTTP API exposed by the server", function() {
Expand Down