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

Fix error handling #571

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
9 changes: 5 additions & 4 deletions src/auth/authSlice.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { createAsyncThunk, createSlice, PayloadAction } from '@reduxjs/toolkit';
import Config from '../config';
import { AuthError } from '../errors';
import history from '../history';
import type { RootState } from '../redux/store';
import Storage from '../storage';
Expand Down Expand Up @@ -225,12 +226,12 @@ export const fetchUser = createAsyncThunk<void, { uuid: string } | undefined>('a
const url = `${Config.API_URL}${Config.routes.user.user}/${uuid}`;
const data = await fetchService(url, 'GET', 'json', {
requiresAuthorization: true,
onFailCallback: () => dispatch(logoutUser()),
});

dispatch(setUser(data.user));
} catch (error) {
// TODO: Dispatch error message.
if (error instanceof AuthError) dispatch(logoutUser());
}
});

Expand All @@ -243,9 +244,9 @@ export const withLogout = <T extends AsyncFunction>(fn: T, type: string) =>
createAsyncThunk<Awaited<ReturnType<T>>, Parameters<T>[0]>(type, async (args, { dispatch }) => {
try {
return await fn(args);
} catch (err) {
dispatch(logoutUser());
throw err;
} catch (error) {
if (error instanceof AuthError) dispatch(logoutUser());
throw error;
}
});

Expand Down
24 changes: 24 additions & 0 deletions src/errors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// seems reasonable to put all errors in this file
/* eslint-disable max-classes-per-file */

export class UserError extends Error {
public message: string;

constructor(message: string) {
super();

Object.setPrototypeOf(this, UserError.prototype);
this.message = message;
}
}

export class AuthError extends Error {
public message: string;

constructor(message: string) {
super();

Object.setPrototypeOf(this, AuthError.prototype);
this.message = message;
}
}
3 changes: 2 additions & 1 deletion src/event/eventSlice.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';
import { isEqual } from 'lodash';
import { fetchUser, logoutUser, withLogout } from '../auth/authSlice';
import { AuthError } from '../errors';
import type { RootState } from '../redux/store';
import * as utils from './utils';

Expand Down Expand Up @@ -39,7 +40,7 @@ export const checkIn = createAsyncThunk<any, any>('event/checkIn', async (info,
dispatch(fetchFutureEvents());
return data;
} catch (error) {
dispatch(logoutUser());
if (error instanceof AuthError) dispatch(logoutUser());
throw error;
}
});
Expand Down
2 changes: 1 addition & 1 deletion src/event/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export const checkIn = async (info) => {
return data.event;
} catch (error) {
notify('Unable to checkin!', getErrorMessage(error));
throw new Error(getErrorMessage(error));
throw error;
}
};

Expand Down
3 changes: 2 additions & 1 deletion src/leaderboard/leaderboardSlice.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';
import { logoutUser } from '../auth/authSlice';
import { AuthError } from '../errors';
import type { RootState } from '../redux/store';
import { PublicProfile } from '../types';
import { getDefaultProfile } from '../utils';
Expand All @@ -23,7 +24,7 @@ export const fetchLeaderboard = createAsyncThunk<
const data = await utils.fetchLeaderboard(args.offset, args.limit, args.from, args.to);
return data;
} catch (error) {
dispatch(logoutUser());
if (error instanceof AuthError) dispatch(logoutUser());
throw error;
}
});
Expand Down
2 changes: 0 additions & 2 deletions src/leaderboard/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,5 @@ export const fetchLeaderboard = async (offset: number = 0, limit: number, from?:
requiresAuthorization: true,
});

if (!data) throw new Error('Empty response from server');
if (data.error) throw new Error(data.error.message);
return data.leaderboard as PublicProfile[];
};
3 changes: 2 additions & 1 deletion src/profile/profileSlice.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';
import { logoutUser, withLogout } from '../auth/authSlice';
import { AuthError } from '../errors';
import * as utils from './utils';

const initialState = {
Expand All @@ -11,7 +12,7 @@ export const updateProfile = createAsyncThunk('utils/updateProfile', async (valu
try {
await utils.updateProfile(values);
} catch (error) {
dispatch(logoutUser());
if (error instanceof AuthError) dispatch(logoutUser());
}
});

Expand Down
1 change: 0 additions & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ export type MimeType = 'json' | 'image';
export type FetchServiceOptions = {
requiresAuthorization: boolean;
payload?: any;
onFailCallback?: () => void;
};

export enum UserAccessType {
Expand Down
9 changes: 5 additions & 4 deletions src/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { showNotification } from '@mantine/notifications';
import Storage from './storage';
import DiamondDisplay from './store/components/DiamondDisplay';
import { FetchServiceOptions, HttpRequestMethod, MimeType, OrderStatus, PublicMerchItemOption } from './types';
import { AuthError, UserError } from './errors';

export const notify = (title: string, description: string) => {
showNotification({
Expand Down Expand Up @@ -159,7 +160,7 @@ const getServiceErrorMessage = (error) => {
* Fetches data from server with simple error handling
*/
export const fetchService = async (url: string, requestMethod: HttpRequestMethod, mimeType: MimeType, options: FetchServiceOptions) => {
const { payload, requiresAuthorization, onFailCallback } = options;
const { payload, requiresAuthorization } = options;

let Accept;
let ContentType;
Expand Down Expand Up @@ -187,9 +188,9 @@ export const fetchService = async (url: string, requestMethod: HttpRequestMethod
});

const { status } = response;
if (status === 401 || status === 403) onFailCallback?.();
if (status === 401 || status === 403) throw new AuthError('');
Copy link
Member

Choose a reason for hiding this comment

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

Would it be doable to add some kind of message in here for the AuthError? I figure maybe the error field of the response might return something useful?

const data = await response.json();
if (!data) throw new Error('Empty response from server');
if (!data) throw new AuthError('Empty response from server');
if (data.error) {
let { message } = data.error;
if (status === 400 && data.error.errors) {
Expand All @@ -200,7 +201,7 @@ export const fetchService = async (url: string, requestMethod: HttpRequestMethod
}
message = messages;
}
throw new Error(message);
throw new UserError(message);
}

return data;
Expand Down