Skip to content

Commit

Permalink
Add error handling to mutations
Browse files Browse the repository at this point in the history
- minor styling changes to make dialogs mutation dialogs similar
  • Loading branch information
joshdimanteto committed Jan 21, 2025
1 parent 798547e commit 1c543c8
Show file tree
Hide file tree
Showing 11 changed files with 130 additions and 74 deletions.
4 changes: 4 additions & 0 deletions src/api/export.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useMutation, UseMutationResult } from '@tanstack/react-query';
import { AxiosError } from 'axios';
import { SearchParams, SortType } from '../app.types';
import handleOG_APIError from '../handleOG_APIError';
import { useAppSelector } from '../state/hooks';
import { selectQueryParams } from '../state/slices/searchSlice';
import { selectSelectedRows } from '../state/slices/selectionSlice';
Expand Down Expand Up @@ -168,5 +169,8 @@ export const useExportData = (): UseMutationResult<void, AxiosError> => {
exportType === 'Selected Rows' ? selectedRows : undefined
);
},
onError: (error) => {
handleOG_APIError(error);
},
});
};
4 changes: 4 additions & 0 deletions src/api/sessions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
} from '@tanstack/react-query';
import { AxiosError } from 'axios';
import { Session, SessionListItem, SessionResponse } from '../app.types';
import handleOG_APIError from '../handleOG_APIError';
import { readSciGatewayToken } from '../parseTokens';
import { ogApi } from './api';

Expand Down Expand Up @@ -59,6 +60,9 @@ export const useEditSession = (): UseMutationResult<
const queryClient = useQueryClient();
return useMutation({
mutationFn: (session: SessionResponse) => editSession(session),
onError: (error) => {
handleOG_APIError(error);
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['sessionList'] });
queryClient.invalidateQueries({ queryKey: ['session'] });
Expand Down
13 changes: 6 additions & 7 deletions src/filtering/deleteFavouriteFilterDialogue.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
DialogTitle,
FormHelperText,
} from '@mui/material';
import React, { useState } from 'react';
import React from 'react';

import { AxiosError } from 'axios';
import { useDeleteFavouriteFilter } from '../api/favouriteFilters';
Expand All @@ -24,14 +24,12 @@ const DeleteFavouriteFilterDialogue = (
) => {
const { open, onClose, favouriteFilter } = props;

const [error, setError] = useState(false);
const [errorMessage, setErrorMessage] = React.useState<string | undefined>(
undefined
);

const handleClose = React.useCallback(() => {
onClose();
setError(false);
setErrorMessage(undefined);
}, [onClose]);

Expand All @@ -44,11 +42,9 @@ const DeleteFavouriteFilterDialogue = (
handleClose();
})
.catch((error: AxiosError) => {
setError(true);
setErrorMessage((error.response?.data as { detail: string }).detail);
});
} else {
setError(true);
setErrorMessage('No data provided, Please refresh and try again');
}
}, [deleteFavouriteFilter, handleClose, favouriteFilter]);
Expand All @@ -65,11 +61,14 @@ const DeleteFavouriteFilterDialogue = (
</DialogContent>
<DialogActions>
<Button onClick={handleClose}>Close</Button>
<Button disabled={error} onClick={handleDeleteFavouriteFilter}>
<Button
disabled={errorMessage !== undefined}
onClick={handleDeleteFavouriteFilter}
>
Continue
</Button>
</DialogActions>
{error && (
{errorMessage !== undefined && (
<Box
sx={{
mx: 3,
Expand Down
22 changes: 16 additions & 6 deletions src/filtering/favouriteFilterDialogue.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
Grid,
TextField,
} from '@mui/material';
import type { AxiosError } from 'axios';
import React from 'react';
import {
useAddFavouriteFilter,
Expand All @@ -20,6 +21,7 @@ import {
FavouriteFilterPatch,
FavouriteFilterPost,
} from '../app.types';
import handleOG_APIError from '../handleOG_APIError';
import { FilterPageHelp } from './filterDialogue.component';
import FilterInput from './filterInput.component';
import { Token } from './filterParser';
Expand Down Expand Up @@ -138,9 +140,13 @@ const FavouriteFilterDialogue = (props: FavouriteFilterDialogueProps) => {
const hasError = handleDuplicateNameError(data.name);
if (hasError) return;

addFavouriteFilter(data).then(() => {
handleClose();
});
addFavouriteFilter(data)
.then(() => {
handleClose();
})
.catch((error: AxiosError) => {
handleOG_APIError(error);
});
}, [
addFavouriteFilter,
favouriteFilter.filter,
Expand Down Expand Up @@ -172,9 +178,13 @@ const FavouriteFilterDialogue = (props: FavouriteFilterDialogueProps) => {
editFavouriteFilter({
id: selectedFavouriteFilter._id,
favouriteFilter: editData,
}).then(() => {
handleClose();
});
})
.then(() => {
handleClose();
})
.catch((error: AxiosError) => {
handleOG_APIError(error);
});
} else {
setErrorMessage(
"There have been no changes made. Please change a field's value or press Close to exit."
Expand Down
6 changes: 5 additions & 1 deletion src/functions/functionsDialog.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
ValidateFunctionState,
} from '../app.types';
import { Heading } from '../filtering/filterDialogue.component';
import handleOG_APIError from '../handleOG_APIError';
import { useAppDispatch, useAppSelector } from '../state/hooks';
import {
changeAppliedFunctions,
Expand Down Expand Up @@ -218,7 +219,10 @@ const FunctionsDialog = (props: FunctionsDialogProps) => {
(error: AxiosError) => {
const errorCode = (error.response?.data as APIError).detail;

if (typeof errorCode === 'string' && !errorCode.includes(':')) return;
if (typeof errorCode === 'string' && !errorCode.includes(':')) {
handleOG_APIError(error);
return;
}
const parsedErrors = parseErrorCode(errorCode);
parsedErrors.forEach((error) => {
const { index, errorMessage, isNameError } = error;
Expand Down
12 changes: 7 additions & 5 deletions src/mocks/handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ import experimentsJson from './experiments.json';
import favouriteFiltersJson from './favouriteFilters.json';
import functionsTokensJson from './functionTokens.json';
import functionsJson from './functions.json';
import imageCrosshairJson from './imageCrosshair.json';
import recordsJson from './records.json';
import sessionsJson from './sessionsList.json';
import imageCrosshairJson from './imageCrosshair.json';

// have to add undefined here due to how TS JSON parsing works
type RecordsJSONType = (Omit<Record, 'channels'> & {
Expand Down Expand Up @@ -67,10 +67,12 @@ export const handlers = [
http.delete('/sessions/:id', async ({ params }) => {
const { id } = params;

const validId = [1, 2, 3, 4];
if (validId.includes(Number(id))) {
return new HttpResponse(null, { status: 204 });
} else HttpResponse.json(null, { status: 422 });
if (id === sessionsJson[3]._id)
return HttpResponse.json(
{ detail: 'User session cannot be found' },
{ status: 404 }
);
return new HttpResponse(null, { status: 204 });
}),
http.get('/sessions/list', async () => {
return HttpResponse.json(sessionsJson, { status: 200 });
Expand Down
13 changes: 13 additions & 0 deletions src/session/deleteSessionDialogue.component.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,19 @@ describe('delete session dialogue', () => {
expect(onClose).not.toHaveBeenCalled();
});

it('displays error message if the User session is no found', async () => {
props = {
...props,
sessionData: { ...sessionData, _id: '4' },
};
createView();
const continueButton = screen.getByRole('button', { name: 'Continue' });
await user.click(continueButton);
const helperTexts = screen.getByText('User session cannot be found');
expect(helperTexts).toBeInTheDocument();
expect(onClose).not.toHaveBeenCalled();
});

it('calls handleDeleteSession when continue button is clicked with a valid session name', async () => {
createView();
const continueButton = screen.getByRole('button', { name: 'Continue' });
Expand Down
32 changes: 22 additions & 10 deletions src/session/deleteSessionDialogue.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import {
DialogTitle,
FormHelperText,
} from '@mui/material';
import React, { useState } from 'react';
import type { AxiosError } from 'axios';
import React from 'react';
import { useDeleteSession } from '../api/sessions';
import { SessionResponse } from '../app.types';

Expand All @@ -22,7 +23,6 @@ const DeleteSessionDialogue = (props: DeleteSessionDialogueProps) => {
const { open, onClose, sessionData, loadedSessionId, onDeleteLoadedSession } =
props;

const [error, setError] = useState(false);
const [errorMessage, setErrorMessage] = React.useState<string | undefined>(
undefined
);
Expand All @@ -38,12 +38,10 @@ const DeleteSessionDialogue = (props: DeleteSessionDialogueProps) => {
}
onClose();
})
.catch((error) => {
setError(true);
setErrorMessage(error.message);
.catch((error: AxiosError) => {
setErrorMessage((error.response?.data as { detail: string }).detail);
});
} else {
setError(true);
setErrorMessage('No data provided, Please refresh and try again');
}
}, [
Expand All @@ -54,18 +52,32 @@ const DeleteSessionDialogue = (props: DeleteSessionDialogueProps) => {
sessionData,
]);

const handleClose = React.useCallback(() => {
onClose();
setErrorMessage(undefined);
}, [onClose]);

return (
<Dialog open={open} onClose={onClose} maxWidth="lg">
<Dialog open={open} onClose={handleClose} maxWidth="lg">
<DialogTitle>Delete Session</DialogTitle>
<DialogContent>
Are you sure you want to delete{' '}
<strong data-testid="delete-session-name">{sessionData?.name}</strong>?
</DialogContent>
<DialogActions>
<Button onClick={onClose}>Close</Button>
<Button onClick={handleDeleteSession}>Continue</Button>
{error && <FormHelperText error>{errorMessage}</FormHelperText>}
<Button onClick={handleClose}>Close</Button>
<Button
disabled={errorMessage !== undefined}
onClick={handleDeleteSession}
>
Continue
</Button>
</DialogActions>
{errorMessage !== undefined && (
<FormHelperText sx={{ textAlign: 'center' }} error>
{errorMessage}
</FormHelperText>
)}
</Dialog>
);
};
Expand Down
32 changes: 14 additions & 18 deletions src/session/sessionDialogue.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ import {
DialogTitle,
TextField,
} from '@mui/material';
import React, { useState } from 'react';
import type { AxiosError } from 'axios';
import React from 'react';
import { shallowEqual } from 'react-redux';
import { useEditSession, useSaveSession } from '../api/sessions';
import { SessionResponse } from '../app.types';
import handleOG_APIError from '../handleOG_APIError';
import { useUpdateWindowPositions } from '../hooks';
import { sessionSelector, useAppSelector } from '../state/hooks';

Expand Down Expand Up @@ -45,7 +47,6 @@ const SessionDialogue = (props: SessionDialogueProps) => {
const { mutateAsync: saveSession } = useSaveSession();
const { mutateAsync: editSession } = useEditSession();

const [error, setError] = useState(false);
const [errorMessage, setErrorMessage] = React.useState<string | undefined>(
undefined
);
Expand Down Expand Up @@ -73,13 +74,10 @@ const SessionDialogue = (props: SessionDialogueProps) => {
onChangeLoadedSessionId(response);
handleClose();
})
.catch((error) => {
setError(true);
console.log(error.message);
setErrorMessage(error.message);
.catch((error: AxiosError) => {
handleOG_APIError(error);
});
} else {
setError(true);
setErrorMessage('Please enter a name');
}
}, [
Expand All @@ -106,40 +104,37 @@ const SessionDialogue = (props: SessionDialogueProps) => {

editSession(session)
.then(() => handleClose())
.catch((error) => {
setError(true);
console.log(error.message);
setErrorMessage(error.message);
.catch((error: AxiosError) => {
handleOG_APIError(error);
});
} else {
setError(true);
setErrorMessage('Please enter a name');
}
}, [sessionName, sessionData, sessionSummary, editSession, handleClose]);

return (
<Dialog open={open} onClose={handleClose} maxWidth="lg">
<Dialog open={open} onClose={handleClose} fullWidth maxWidth="sm">
<DialogTitle>
{requestType === 'create' ? 'Save Session' : 'Edit Session'}
</DialogTitle>
<DialogContent>
<TextField
label="Name"
required={true}
sx={{ width: '100%', margin: '4px' }}
sx={{ width: '100%', mt: 1 }}
value={sessionName ?? ''}
error={error}
helperText={error && errorMessage}
error={!!errorMessage}
helperText={errorMessage}
onChange={(event) => {
onChangeSessionName(
event.target.value ? event.target.value : undefined
);
setError(false); // Reset the error when the user makes changes
setErrorMessage(undefined); // Reset the error when the user makes changes
}}
/>
<TextField
label="Summary"
sx={{ width: '100%', margin: '4px' }}
sx={{ width: '100%', mt: 1 }}
multiline
value={sessionSummary}
onChange={(event) => {
Expand All @@ -152,6 +147,7 @@ const SessionDialogue = (props: SessionDialogueProps) => {
<DialogActions>
<Button onClick={handleClose}>Close</Button>
<Button
disabled={errorMessage !== undefined}
onClick={
requestType === 'create'
? handleExportCreateSession
Expand Down
Loading

0 comments on commit 1c543c8

Please sign in to comment.