Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate disconnectionErrorCodes into constants. #12583

Merged
merged 1 commit into from
Aug 29, 2024
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: 2 additions & 0 deletions kolibri/core/assets/src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -198,3 +198,5 @@ export const Presets = Object.freeze({
// This should be kept in sync with the value in
// kolibri/core/exams/constants.py
export const MAX_QUESTIONS_PER_QUIZ_SECTION = 25;

export const DisconnectionErrorCodes = [0, 502, 504, 511];
4 changes: 2 additions & 2 deletions kolibri/core/assets/src/core-app/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import heartbeat from 'kolibri.heartbeat';
import logger from 'kolibri.lib.logging';
import { get } from '@vueuse/core';
import useUser from 'kolibri.coreVue.composables.useUser';
import errorCodes from '../disconnectionErrorCodes';
import { DisconnectionErrorCodes } from 'kolibri.coreVue.vuex.constants';
import useConnection from '../composables/useConnection';
import clientFactory from './baseClient';

Expand Down Expand Up @@ -58,7 +58,7 @@ baseClient.interceptors.response.use(
}
// On every error, check to see if the status code is one of our designated
// disconnection status codes.
if (errorCodes.includes(error.response.status)) {
if (DisconnectionErrorCodes.includes(error.response.status)) {
// If so, set our heartbeat module to start monitoring the disconnection state
heartbeat.monitorDisconnect(error.response.status);
}
Expand Down
1 change: 0 additions & 1 deletion kolibri/core/assets/src/disconnectionErrorCodes.js

This file was deleted.

15 changes: 10 additions & 5 deletions kolibri/core/assets/src/heartbeat.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ import Lockr from 'lockr';
import urls from 'kolibri.urls';
import { get, set } from '@vueuse/core';
import useUser from 'kolibri.coreVue.composables.useUser';
import {
DisconnectionErrorCodes,
SIGNED_OUT_DUE_TO_INACTIVITY,
} from 'kolibri.coreVue.vuex.constants';
import useConnection from './composables/useConnection';
import clientFactory from './core-app/baseClient';
import { SIGNED_OUT_DUE_TO_INACTIVITY } from './constants';
import errorCodes from './disconnectionErrorCodes';
import {
createTryingToReconnectSnackbar,
createDisconnectedSnackbar,
Expand Down Expand Up @@ -62,7 +64,10 @@ export class HeartBeat {
function (response) {
// If the response does not have one of the disconnect error codes
// then we have reconnected.
if (!get(heartbeat._connection.connected) && !errorCodes.includes(response.status)) {
if (
!get(heartbeat._connection.connected) &&
!DisconnectionErrorCodes.includes(response.status)
) {
// Not one of our 'disconnected' status codes, so we are connected again
// Set connected and return the response here to prevent any further processing.
heartbeat._setConnected();
Expand All @@ -73,7 +78,7 @@ export class HeartBeat {
if (!get(heartbeat._connection.connected)) {
// If the response does not have one of the disconnect error codes
// then we have reconnected.
if (!errorCodes.includes(error.response.status)) {
if (!DisconnectionErrorCodes.includes(error.response.status)) {
// Not one of our 'disconnected' status codes, so we are connected again
// Set connected and return the response here to prevent any further processing.
heartbeat._setConnected();
Expand Down Expand Up @@ -211,7 +216,7 @@ export class HeartBeat {
.catch(error => {
// An error occurred.
logging.error('Session polling failed, with error: ', error);
if (errorCodes.includes(error.response.status)) {
if (DisconnectionErrorCodes.includes(error.response.status)) {
// We had an error that indicates that we are disconnected, so start to monitor
// the disconnection.
return this.monitorDisconnect(error.response.status);
Expand Down
10 changes: 7 additions & 3 deletions kolibri/core/assets/src/state/modules/core/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,15 @@ import Vue from 'kolibri.lib.vue';
import Lockr from 'lockr';
import { set, get } from '@vueuse/core';
import useUser from 'kolibri.coreVue.composables.useUser';
import {
DisconnectionErrorCodes,
LoginErrors,
ERROR_CONSTANTS,
UPDATE_MODAL_DISMISSED,
} from 'kolibri.coreVue.vuex.constants';
import { baseSessionState } from '../session';
import { LoginErrors, ERROR_CONSTANTS, UPDATE_MODAL_DISMISSED } from '../../../constants';
import { browser, os } from '../../../utils/browserInfo';
import useConnection from '../../../composables/useConnection';
import errorCodes from './../../../disconnectionErrorCodes.js';

const logging = logger.getLogger(__filename);

Expand Down Expand Up @@ -79,7 +83,7 @@ export function handleApiError(store, { error, reloadOnReconnect = false } = {})
if (typeof error === 'object' && !(error instanceof Error)) {
errorString = JSON.stringify(error, null, 2);
} else if (error.response) {
if (errorCodes.includes(error.response.status)) {
if (DisconnectionErrorCodes.includes(error.response.status)) {
// Do not log errors for disconnections, as it disrupts the user experience
// and should already be being handled by our disconnection overlay.
set(useConnection().reloadOnReconnect, reloadOnReconnect);
Expand Down
58 changes: 27 additions & 31 deletions kolibri/core/assets/test/heartbeat.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import * as serverClock from 'kolibri.utils.serverClock';
import { get, set } from '@vueuse/core';
import useSnackbar, { useSnackbarMock } from 'kolibri.coreVue.composables.useSnackbar';
import { ref } from 'kolibri.lib.vueCompositionApi';
import { DisconnectionErrorCodes } from 'kolibri.coreVue.vuex.constants';
import { HeartBeat } from '../src/heartbeat.js';
import disconnectionErrorCodes from '../src/disconnectionErrorCodes';
import { trs } from '../src/disconnection';
import coreModule from '../src/state/modules/core';
import { stubWindowLocation } from 'testUtils'; // eslint-disable-line
Expand Down Expand Up @@ -264,20 +264,18 @@ describe('HeartBeat', function () {
// Rather it is the status code that our request client library returns
// when the connection is refused by the host, or is otherwise unable to connect.
// What happens for a zero code is tested later in this file.
disconnectionErrorCodes
.filter(code => code !== 0)
.forEach(errorCode => {
it('should call monitorDisconnect if it receives error code ' + errorCode, function () {
const monitorStub = jest.spyOn(heartBeat, 'monitorDisconnect');
mock.put(/.*/, {
status: errorCode,
headers: { 'Content-Type': 'application/json' },
});
return heartBeat._checkSession().finally(() => {
expect(monitorStub).toHaveBeenCalledTimes(1);
});
DisconnectionErrorCodes.filter(code => code !== 0).forEach(errorCode => {
it('should call monitorDisconnect if it receives error code ' + errorCode, function () {
const monitorStub = jest.spyOn(heartBeat, 'monitorDisconnect');
mock.put(/.*/, {
status: errorCode,
headers: { 'Content-Type': 'application/json' },
});
return heartBeat._checkSession().finally(() => {
expect(monitorStub).toHaveBeenCalledTimes(1);
});
});
});
});
describe('when not connected', function () {
let snackbar;
Expand All @@ -297,26 +295,24 @@ describe('HeartBeat', function () {
expect(get(snackbar.snackbarIsVisible)).toEqual(true);
expect(get(snackbar.snackbarOptions).text).toEqual(trs.$tr('tryingToReconnect'));
});
disconnectionErrorCodes
.filter(code => code !== 0)
.forEach(errorCode => {
it('should set snackbar to disconnected for error code ' + errorCode, function () {
jest.spyOn(heartBeat, 'monitorDisconnect');
mock.put(/.*/, {
status: errorCode,
headers: { 'Content-Type': 'application/json' },
});
heartBeat._wait = jest.fn();
return heartBeat._checkSession().finally(() => {
expect(get(snackbar.snackbarIsVisible)).toEqual(true);
expect(
get(snackbar.snackbarOptions).text.startsWith(
'Disconnected from server. Will try to reconnect in',
),
).toEqual(true);
});
DisconnectionErrorCodes.filter(code => code !== 0).forEach(errorCode => {
it('should set snackbar to disconnected for error code ' + errorCode, function () {
jest.spyOn(heartBeat, 'monitorDisconnect');
mock.put(/.*/, {
status: errorCode,
headers: { 'Content-Type': 'application/json' },
});
heartBeat._wait = jest.fn();
return heartBeat._checkSession().finally(() => {
expect(get(snackbar.snackbarIsVisible)).toEqual(true);
expect(
get(snackbar.snackbarOptions).text.startsWith(
'Disconnected from server. Will try to reconnect in',
),
).toEqual(true);
});
});
});
it('should set snackbar to disconnected for error code 0', function () {
jest.spyOn(heartBeat, 'monitorDisconnect');
mock.put(/.*/, () => Promise.reject(new Error()));
Expand Down