Skip to content

Commit

Permalink
Merge pull request #12291 from thesujai/distributed-error-reporting-t…
Browse files Browse the repository at this point in the history
…ask5

Distributed Error Reporting: Frontend Error reporting
  • Loading branch information
akolson authored Jun 20, 2024
2 parents 53ae2ad + a0ea7b4 commit cb259fa
Show file tree
Hide file tree
Showing 5 changed files with 170 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import urls from 'kolibri.urls';
import Resource from '../errorReport';
import {
VueErrorReport,
JavascriptErrorReport,
UnhandledRejectionErrorReport,
} from '../../utils/errorReportUtils';

/* eslint-env jest */
jest.mock('kolibri.urls', () => ({
'kolibri:core:report': jest.fn(),
}));

describe('Error Report', () => {
beforeEach(() => {
urls['kolibri:core:report'].mockReturnValue('/api/core/report');
});

afterEach(() => {
jest.clearAllMocks();
});

it('should call api/core/report with VueErrorReport data', () => {
const vueError = new Error('Vue error');
vueError.stack = 'My stack trace';
const errorReport = new VueErrorReport(vueError);

const expectedData = {
error_message: 'Vue error',
traceback: 'My stack trace',
};

Resource.client = jest.fn();

Resource.report(errorReport);

expect(Resource.client).toHaveBeenCalledWith({
url: '/api/core/report',
method: 'post',
data: expectedData,
});
});

it('should call api/core/report with JavascriptErrorReport data', () => {
const jsErrorEvent = {
error: new Error('Javascript error'),
};
jsErrorEvent.error.stack = 'My stack trace';

const errorReport = new JavascriptErrorReport(jsErrorEvent);

const expectedData = {
error_message: 'Javascript error',
traceback: 'My stack trace',
};

Resource.client = jest.fn();

Resource.report(errorReport);

expect(Resource.client).toHaveBeenCalledWith({
url: '/api/core/report',
method: 'post',
data: expectedData,
});
});

it('should call api/core/report with UnhandledRejectionErrorReport data', () => {
const rejectionEvent = {
reason: new Error('Unhandled rejection'),
};
rejectionEvent.reason.stack = 'My stack trace';

const errorReport = new UnhandledRejectionErrorReport(rejectionEvent);

const expectedData = {
error_message: 'Unhandled rejection',
traceback: 'My stack trace',
};

Resource.client = jest.fn();

Resource.report(errorReport);

expect(Resource.client).toHaveBeenCalledWith({
url: '/api/core/report',
method: 'post',
data: expectedData,
});
});
});
15 changes: 15 additions & 0 deletions kolibri/core/assets/src/api-resources/errorReport.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Resource } from 'kolibri.lib.apiResource';
import urls from 'kolibri.urls';

export default new Resource({
name: 'errorreports',
report(error) {
const url = urls['kolibri:core:report']();
const data = error.getErrorReport();
return this.client({
url,
method: 'post',
data: data,
});
},
});
1 change: 1 addition & 0 deletions kolibri/core/assets/src/api-resources/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export { default as UserSyncStatusResource } from './userSyncStatus';
export { default as ContentRequestResource } from './contentRequest';
export { default as ContentNodeProgressResource } from './contentNodeProgress';
export { default as DevicePermissionsResource } from './devicePermissions';
export { default as ErrorReportResource } from './errorReport';
export { default as RemoteChannelResource } from './remoteChannel';
export { default as LessonResource } from './lesson';
export { default as AttemptLogResource } from './attemptLog';
Expand Down
28 changes: 27 additions & 1 deletion kolibri/core/assets/src/core-app/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,14 @@ import heartbeat from 'kolibri.heartbeat';
import ContentRenderer from '../views/ContentRenderer';
import initializeTheme from '../styles/initializeTheme';
import { i18nSetup } from '../utils/i18n';
import setupPluginMediator from './pluginMediator';
import { ErrorReportResource } from '../api-resources';
import {
VueErrorReport,
JavascriptErrorReport,
UnhandledRejectionErrorReport,
} from '../utils/errorReportUtils';
import apiSpec from './apiSpec';
import setupPluginMediator from './pluginMediator';

// Do this before any async imports to ensure that public paths
// are set correctly
Expand Down Expand Up @@ -71,6 +77,26 @@ heartbeat.startPolling();

i18nSetup().then(coreApp.ready);

// these shall be responsibe for catching runtime errors
Vue.config.errorHandler = function(err) {
logging.error(`Unexpected Error: ${err}`);
const error = new VueErrorReport(err);
ErrorReportResource.report(error);
};

window.addEventListener('error', e => {
logging.error(`Unexpected Error: ${e.error}`);
const error = new JavascriptErrorReport(e);
ErrorReportResource.report(error);
});

window.addEventListener('unhandledrejection', event => {
event.preventDefault();
logging.error(`Unhandled Rejection: ${event.reason}`);
const error = new UnhandledRejectionErrorReport(event);
ErrorReportResource.report(error);
});

// This is exported by webpack as the kolibriCoreAppGlobal object, due to the 'output.library' flag
// which exports the coreApp at the bottom of this file as a named global variable:
// https://webpack.github.io/docs/configuration.html#output-library
Expand Down
36 changes: 36 additions & 0 deletions kolibri/core/assets/src/utils/errorReportUtils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
class ErrorReport {
constructor(e) {
this.e = e;
}

getErrorReport() {
throw new Error('getErrorReport() method must be implemented.');
}
}

export class VueErrorReport extends ErrorReport {
getErrorReport() {
return {
error_message: this.e.message,
traceback: this.e.stack,
};
}
}

export class JavascriptErrorReport extends ErrorReport {
getErrorReport() {
return {
error_message: this.e.error.message,
traceback: this.e.error.stack,
};
}
}

export class UnhandledRejectionErrorReport extends ErrorReport {
getErrorReport() {
return {
error_message: this.e.reason.message,
traceback: this.e.reason.stack,
};
}
}

0 comments on commit cb259fa

Please sign in to comment.