Commit 27e3c96c authored by Robert Knight's avatar Robert Knight

Use raven-js for client-side error capture and reporting

Capture exceptions reported via Angular's $exceptionHandler
service and window.onerror and report them via Raven.

The client currently uses the same Sentry DSN as the main
application, which gets the DSN from the SENTRY_DSN
environment variable.

 * Add a <script> tag to all pages on the site which
   defines a window.RAVEN_CONFIG variable which provides the DSN
   and release string needed to configure Raven JS on the client

 * Configure and setup RavenJS in the main site JS, Chrome
   extension and the sidebar.

   For the sidebar, Angular integration is configured
   using Raven's Angular plugin.

 * Configure the user ID associated with Sentry reports
   when the session state is received

 * Extract the code which builds the sidebar/extension
   config dictionary out of the template into
   its own module for easier sharing between the Chrome
   extension and the sidebar's code.
parent 07bc28fd
# initialize Raven. This is required at the top of this file
# so that it happens early in the app's startup flow
if window.RAVEN_CONFIG
require('./raven').init(window.RAVEN_CONFIG)
require('autofill-event')
baseURI = require('document-base-uri')
angular = require('angular')
......@@ -79,6 +85,7 @@ setupHttp = ['$http', ($http) ->
setupHost = ['host', (host) -> ]
module.exports = angular.module('h', [
require('./raven').angularModule().name
'angulartics'
'angulartics.google.analytics'
'angular-jwt'
......@@ -159,6 +166,7 @@ module.exports = angular.module('h', [
.value('AnnotationSync', require('./annotation-sync'))
.value('AnnotationUISync', require('./annotation-ui-sync'))
.value('Discovery', require('./discovery'))
.value('raven', require('./raven'))
.config(configureDocument)
.config(configureLocation)
......
/**
* This module configures Raven for reporting crashes
* to Sentry.
*
* Logging requires the Sentry DSN and Hypothesis
* version to be provided via the app's settings object.
*
* It also exports an Angular module via angularModule() which integrates
* error logging into any Angular application that it is added to
* as a dependency.
*/
var Raven = require('raven-js')
var enabled = false;
function init(config) {
Raven.config(config.dsn, {
release: config.release,
}).install();
enabled = true;
}
function setUserInfo(info) {
if (info) {
Raven.setUserContext(info);
} else {
Raven.setUserContext();
}
}
/**
* Initializes and returns the Angular module which provides
* a custom wrapper around Angular's $exceptionHandler service,
* logging any exceptions passed to it using Sentry
*/
function angularModule() {
if (enabled) {
if (window.angular) {
var angularPlugin = require('raven-js/plugins/angular');
angularPlugin(Raven, angular);
}
} else {
// define a stub module in environments where Raven is not enabled
angular.module('ngRaven', []);
}
return angular.module('ngRaven');
}
module.exports = {
init: init,
angularModule: angularModule,
setUserInfo: setUserInfo,
};
......@@ -62,7 +62,7 @@ function sessionActions(options) {
*
* @ngInject
*/
function session($document, $http, $resource, $rootScope, flash) {
function session($document, $http, $resource, $rootScope, flash, raven) {
// Headers sent by every request made by the session service.
var headers = {};
// TODO: Move accounts data management (e.g. profile, edit_profile,
......@@ -143,7 +143,17 @@ function session($document, $http, $resource, $rootScope, flash) {
$rootScope.$broadcast(events.USER_CHANGED, {
initialLoad: isInitialLoad,
});
// associate error reports with the current user in Sentry
if (resource.state.userid) {
raven.setUserInfo({
id: resource.state.userid,
})
} else {
raven.setUserInfo(undefined);
}
}
if (groupsChanged) {
$rootScope.$broadcast(events.GROUPS_CHANGED, {
initialLoad: isInitialLoad,
......
'use strict';
var angular = require('angular');
/**
* @ngdoc factory
* @name settings
......@@ -17,7 +15,7 @@ function settings($document) {
'script[type="application/json"]#hypothesis-settings');
if (settingsElement) {
return angular.fromJson(settingsElement.textContent);
return JSON.parse(settingsElement.textContent);
}
return {};
......
// configure error reporting
if (window.RAVEN_CONFIG) {
require('./raven').init(window.RAVEN_CONFIG);
}
var CreateGroupFormController = require('./create-group-form');
var DropdownMenuController = require('./dropdown-menu');
var InstallerController = require('./installer-controller');
......
......@@ -9,6 +9,7 @@ describe('h:session', function () {
var $rootScope;
var fakeFlash;
var fakeRaven;
var fakeXsrf;
var sandbox;
var session;
......@@ -28,9 +29,13 @@ describe('h:session', function () {
};
fakeDocument.prop.withArgs('baseURI').returns('http://foo.com/');
fakeFlash = {error: sandbox.spy()};
fakeRaven = {
setUserInfo: sandbox.spy(),
};
$provide.value('$document', fakeDocument);
$provide.value('flash', fakeFlash);
$provide.value('raven', fakeRaven);
}));
......@@ -48,7 +53,7 @@ describe('h:session', function () {
// There's little point testing every single route here, as they're
// declarative and ultimately we'd be testing ngResource.
describe('#login()', function () {
describe('.login()', function () {
var url = 'http://foo.com/app?__formid__=login';
it('should send an HTTP POST to the action', function () {
......@@ -127,7 +132,7 @@ describe('h:session', function () {
});
});
describe('#load()', function () {
describe('.load()', function () {
var url = 'http://foo.com/app';
it('should fetch the session data', function () {
......@@ -157,7 +162,7 @@ describe('h:session', function () {
});
});
describe('#update()', function () {
describe('.update()', function () {
it('broadcasts SESSION_CHANGED when the session changes', function () {
var sessionChangeCallback = sinon.stub();
......@@ -206,5 +211,15 @@ describe('h:session', function () {
});
assert.calledOnce(userChangeCallback);
});
it('updates the user ID for Sentry error reports', function () {
session.update({
userid: 'anne',
csrf: 'dummytoken',
});
assert.calledWith(fakeRaven.setUserInfo, {
id: 'anne',
});
});
});
});
......@@ -42,6 +42,7 @@
"node-uuid": "^1.4.3",
"postcss": "^5.0.6",
"raf": "^3.1.0",
"raven-js": "^2.0.2",
"retry": "^0.8.0",
"scroll-into-view": "^1.3.1",
"showdown": "^1.2.1",
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment