From 1a3dd84e021dc2b28e60b101954e50c6050f9cd7 Mon Sep 17 00:00:00 2001 From: ShootingStar91 Date: Sun, 1 Sep 2024 15:25:39 +0300 Subject: [PATCH] Add delete routes & services for rest of data types. --- backend/src/routes/timeBound.ts | 8 +++- backend/src/routes/timeUnit.ts | 16 +++++--- backend/src/services/write/species.ts | 2 +- backend/src/services/write/timeBound.ts | 36 ++++++++++++++---- backend/src/services/write/timeUnit.ts | 38 ++++++++++++++----- .../services/writeOperations/writeHandler.ts | 2 - .../components/Locality/LocalityDetails.tsx | 2 + .../src/components/Species/SpeciesDetails.tsx | 24 +++++++++++- .../components/TimeBound/TimeBoundDetails.tsx | 35 ++++++++++++++--- .../components/TimeUnit/TimeUnitDetails.tsx | 35 ++++++++++++++--- frontend/src/redux/speciesReducer.ts | 9 ++++- frontend/src/redux/timeBoundReducer.ts | 7 ++++ frontend/src/redux/timeUnitReducer.ts | 8 ++++ 13 files changed, 183 insertions(+), 39 deletions(-) diff --git a/backend/src/routes/timeBound.ts b/backend/src/routes/timeBound.ts index cc18d7de..092d91ff 100644 --- a/backend/src/routes/timeBound.ts +++ b/backend/src/routes/timeBound.ts @@ -2,7 +2,7 @@ import { Request, Router } from 'express' import { getAllTimeBounds, getTimeBoundDetails, getTimeBoundTimeUnits } from '../services/timeBound' import { fixBigInt } from '../utils/common' import { EditDataType, EditMetaData, TimeBoundDetailsType } from '../../../frontend/src/backendTypes' -import { writeTimeBound } from '../services/write/timeBound' +import { deleteTimeUnit, writeTimeBound } from '../services/write/timeBound' import { requireOneOf } from '../middlewares/authorizer' import { Role } from '../../../frontend/src/types' @@ -36,4 +36,10 @@ router.put( } ) +router.delete('/:id', requireOneOf([Role.Admin, Role.EditUnrestricted]), async (req, res) => { + const id = parseInt(req.params.id) + await deleteTimeUnit(id, req.user!) + res.status(200).send() +}) + export default router diff --git a/backend/src/routes/timeUnit.ts b/backend/src/routes/timeUnit.ts index 92b05264..b7a256b6 100644 --- a/backend/src/routes/timeUnit.ts +++ b/backend/src/routes/timeUnit.ts @@ -1,11 +1,11 @@ import { Request, Router } from 'express' -import { getAllTimeUnits, getTimeUnitDetails, getTimeUnitLocalities } from '../services/timeUnit' -import { fixBigInt } from '../utils/common' import { EditDataType, EditMetaData, TimeUnitDetailsType } from '../../../frontend/src/backendTypes' -import { getAllSequences } from '../services/sequence' -import { writeTimeUnit } from '../services/write/timeUnit' -import { requireOneOf } from '../middlewares/authorizer' import { Role } from '../../../frontend/src/types' +import { requireOneOf } from '../middlewares/authorizer' +import { getAllSequences } from '../services/sequence' +import { getAllTimeUnits, getTimeUnitDetails, getTimeUnitLocalities } from '../services/timeUnit' +import { deleteTimeUnit, writeTimeUnit } from '../services/write/timeUnit' +import { fixBigInt } from '../utils/common' const router = Router() @@ -42,4 +42,10 @@ router.put( } ) +router.delete('/:id', requireOneOf([Role.Admin, Role.EditUnrestricted]), async (req, res) => { + const id = req.params.id + await deleteTimeUnit(id, req.user!) + res.status(200).send() +}) + export default router diff --git a/backend/src/services/write/species.ts b/backend/src/services/write/species.ts index a48f657a..d09e24da 100644 --- a/backend/src/services/write/species.ts +++ b/backend/src/services/write/species.ts @@ -12,7 +12,7 @@ const getSpeciesWriteHandler = (type: ActionType) => { table: 'com_species', idColumn: 'species_id', allowedColumns: getFieldsOfTables(['com_species', 'now_ls']), - type: type, + type, }) } diff --git a/backend/src/services/write/timeBound.ts b/backend/src/services/write/timeBound.ts index d7ef2c21..3bde7683 100644 --- a/backend/src/services/write/timeBound.ts +++ b/backend/src/services/write/timeBound.ts @@ -1,7 +1,19 @@ -import { EditDataType, Reference, TimeBoundDetailsType } from '../../../../frontend/src/backendTypes' +import { EditDataType, Reference, TimeBoundDetailsType, User } from '../../../../frontend/src/backendTypes' import { NOW_DB_NAME } from '../../utils/config' import { WriteHandler } from '../writeOperations/writeHandler' import { getFieldsOfTables } from '../../utils/db' +import { ActionType } from '../writeOperations/types' +import { getTimeBoundDetails } from '../timeBound' + +const getTimeBoundWriteHandler = (type: ActionType) => { + return new WriteHandler({ + dbName: NOW_DB_NAME, + table: 'now_tu_bound', + idColumn: 'bid', + allowedColumns: getFieldsOfTables(['now_tu_bound']), + type: type, + }) +} export const writeTimeBound = async ( timeBound: EditDataType, @@ -9,13 +21,7 @@ export const writeTimeBound = async ( references: Reference[] | undefined, authorizer: string ) => { - const writeHandler = new WriteHandler({ - dbName: NOW_DB_NAME, - table: 'now_tu_bound', - idColumn: 'bid', - allowedColumns: getFieldsOfTables(['now_tu_bound']), - type: timeBound.bid ? 'update' : 'add', - }) + const writeHandler = getTimeBoundWriteHandler(timeBound.bid ? 'update' : 'add') try { await writeHandler.start() @@ -31,3 +37,17 @@ export const writeTimeBound = async ( throw e } } + +export const deleteTimeUnit = async (id: number, user: User) => { + const timeBound = await getTimeBoundDetails(id) + if (!timeBound) throw new Error('Time bound not found') + const writeHandler = getTimeBoundWriteHandler('delete') + try { + await writeHandler.start() + await writeHandler.deleteObject('now_tu_bound', timeBound, ['bid']) + await writeHandler.logUpdatesAndComplete(user.initials) + } catch (e) { + await writeHandler.end() + throw e + } +} diff --git a/backend/src/services/write/timeUnit.ts b/backend/src/services/write/timeUnit.ts index 7b8ede85..4376cf42 100644 --- a/backend/src/services/write/timeUnit.ts +++ b/backend/src/services/write/timeUnit.ts @@ -1,7 +1,21 @@ -import { EditDataType, Reference, TimeUnitDetailsType } from '../../../../frontend/src/backendTypes' +import { EditDataType, Reference, TimeUnitDetailsType, User } from '../../../../frontend/src/backendTypes' import { NOW_DB_NAME } from '../../utils/config' import { WriteHandler } from '../writeOperations/writeHandler' import { getFieldsOfTables } from '../../utils/db' +import { ActionType } from '../writeOperations/types' +import { getTimeUnitDetails } from '../timeUnit' + +const getTimeUnitWriteHandler = (type: ActionType) => { + return new WriteHandler({ + dbName: NOW_DB_NAME, + table: 'now_time_unit', + idColumn: 'tu_name', + allowedColumns: getFieldsOfTables(['now_time_unit', 'now_tu_sequence']), + type, + }) +} + +const createTimeUnitId = (displayName: string) => displayName.toLowerCase().replace(' ', '') export const writeTimeUnit = async ( timeUnit: EditDataType, @@ -9,13 +23,7 @@ export const writeTimeUnit = async ( references: Reference[] | undefined, authorizer: string ) => { - const writeHandler = new WriteHandler({ - dbName: NOW_DB_NAME, - table: 'now_time_unit', - idColumn: 'tu_name', - allowedColumns: getFieldsOfTables(['now_time_unit', 'now_tu_sequence']), - type: timeUnit.tu_name ? 'update' : 'add', - }) + const writeHandler = getTimeUnitWriteHandler(timeUnit.tu_name ? 'update' : 'add') const createdId = createTimeUnitId(timeUnit.tu_display_name!) @@ -39,4 +47,16 @@ export const writeTimeUnit = async ( } } -const createTimeUnitId = (displayName: string) => displayName.toLowerCase().replace(' ', '') +export const deleteTimeUnit = async (id: string, user: User) => { + const timeUnit = await getTimeUnitDetails(id) + if (!timeUnit) throw new Error('Time unit not found') + const writeHandler = getTimeUnitWriteHandler('delete') + try { + await writeHandler.start() + await writeHandler.deleteObject('now_time_unit', timeUnit, ['tu_name']) + await writeHandler.logUpdatesAndComplete(user.initials) + } catch (e) { + await writeHandler.end() + throw e + } +} diff --git a/backend/src/services/writeOperations/writeHandler.ts b/backend/src/services/writeOperations/writeHandler.ts index 4598ad00..8bfc9e96 100644 --- a/backend/src/services/writeOperations/writeHandler.ts +++ b/backend/src/services/writeOperations/writeHandler.ts @@ -119,8 +119,6 @@ export class WriteHandler extends DatabaseHandler { await this.createObject(table, object, idColumns) } else if (rowState === 'removed') { await this.deleteObject(table, object, idColumns) - } else { - throw new Error('Invalid rowState in list item object while writing') } } } diff --git a/frontend/src/components/Locality/LocalityDetails.tsx b/frontend/src/components/Locality/LocalityDetails.tsx index 047ff323..1b16cc77 100644 --- a/frontend/src/components/Locality/LocalityDetails.tsx +++ b/frontend/src/components/Locality/LocalityDetails.tsx @@ -33,6 +33,7 @@ export const LocalityDetails = () => { }) const notify = useNotify() const [deleteMutation, { isSuccess: deleteSuccess, isError: deleteError }] = useDeleteLocalityMutation() + useEffect(() => { if (deleteSuccess) { notify('Deleted item successfully.') @@ -41,6 +42,7 @@ export const LocalityDetails = () => { notify('Could not delete item. Error happened.', 'error') } }, [deleteSuccess, deleteError, notify, navigate]) + if (isError) return
Error loading data
if (isFetching || (!data && !isNew) || mutationLoading) return diff --git a/frontend/src/components/Species/SpeciesDetails.tsx b/frontend/src/components/Species/SpeciesDetails.tsx index 5c4a2138..63ca2477 100644 --- a/frontend/src/components/Species/SpeciesDetails.tsx +++ b/frontend/src/components/Species/SpeciesDetails.tsx @@ -1,5 +1,5 @@ -import { useParams } from 'react-router-dom' -import { useEditSpeciesMutation, useGetSpeciesDetailsQuery } from '../../redux/speciesReducer' +import { useNavigate, useParams } from 'react-router-dom' +import { useDeleteSpeciesMutation, useEditSpeciesMutation, useGetSpeciesDetailsQuery } from '../../redux/speciesReducer' import { CircularProgress } from '@mui/material' import { DetailView, TabType } from '../DetailView/DetailView' import { DietTab } from './Tabs/DietTab' @@ -13,15 +13,34 @@ import { TeethTab } from './Tabs/TeethTab' import { UpdateTab } from '../DetailView/common/UpdateTab' import { EditDataType, SpeciesDetailsType } from '@/backendTypes' import { emptySpecies } from '../DetailView/common/defaultValues' +import { useNotify } from '@/hooks/notification' +import { useEffect } from 'react' export const SpeciesDetails = () => { const { id } = useParams() const isNew = id === 'new' const { isLoading, isError, isFetching, data } = useGetSpeciesDetailsQuery(id!, { skip: isNew }) const [editSpeciesRequest] = useEditSpeciesMutation() + const notify = useNotify() + const navigate = useNavigate() + const [deleteMutation, { isSuccess: deleteSuccess, isError: deleteError }] = useDeleteSpeciesMutation() + + useEffect(() => { + if (deleteSuccess) { + notify('Deleted item successfully.') + navigate('/species') + } else if (deleteError) { + notify('Could not delete item. Error happened.', 'error') + } + }, [deleteSuccess, deleteError, notify, navigate]) + if (isError) return
Error loading data
if (isLoading || isFetching || (!data && !isNew)) return + const deleteFunction = async () => { + await deleteMutation(parseInt(id!)).unwrap() + } + const onWrite = async (editData: EditDataType) => { await editSpeciesRequest(editData) } @@ -76,6 +95,7 @@ export const SpeciesDetails = () => { name: 'unknown', error: null, })} + deleteFunction={deleteFunction} /> ) } diff --git a/frontend/src/components/TimeBound/TimeBoundDetails.tsx b/frontend/src/components/TimeBound/TimeBoundDetails.tsx index 61d62efa..d3ce8f7b 100644 --- a/frontend/src/components/TimeBound/TimeBoundDetails.tsx +++ b/frontend/src/components/TimeBound/TimeBoundDetails.tsx @@ -1,12 +1,18 @@ -import { useParams } from 'react-router-dom' -import { useEditTimeBoundMutation, useGetTimeBoundDetailsQuery } from '../../redux/timeBoundReducer' +import { EditDataType, TimeBoundDetailsType } from '@/backendTypes.js' +import { useNotify } from '@/hooks/notification.ts' import { CircularProgress } from '@mui/material' +import { useEffect } from 'react' +import { useNavigate, useParams } from 'react-router-dom' +import { + useDeleteTimeBoundMutation, + useEditTimeBoundMutation, + useGetTimeBoundDetailsQuery, +} from '../../redux/timeBoundReducer' +import { emptyTimeBound } from '../DetailView/common/defaultValues.ts' +import { UpdateTab } from '../DetailView/common/UpdateTab.tsx' import { DetailView, TabType } from '../DetailView/DetailView' import { BoundTab } from './Tabs/BoundTab' import { TimeUnitTab } from './Tabs/TimeUnitTab.tsx' -import { UpdateTab } from '../DetailView/common/UpdateTab.tsx' -import { EditDataType, TimeBoundDetailsType } from '@/backendTypes.js' -import { emptyTimeBound } from '../DetailView/common/defaultValues.ts' export const TimeBoundDetails = () => { const { id } = useParams() @@ -15,9 +21,27 @@ export const TimeBoundDetails = () => { skip: isNew, }) const [editTimeBoundRequest] = useEditTimeBoundMutation() + + const notify = useNotify() + const navigate = useNavigate() + const [deleteMutation, { isSuccess: deleteSuccess, isError: deleteError }] = useDeleteTimeBoundMutation() + + useEffect(() => { + if (deleteSuccess) { + notify('Deleted item successfully.') + navigate('/time-bound') + } else if (deleteError) { + notify('Could not delete item. Error happened.', 'error') + } + }, [deleteSuccess, deleteError, notify, navigate]) + if (isError) return
Error loading data
if (isLoading || isFetching || (!data && !isNew)) return + const deleteFunction = async () => { + await deleteMutation(parseInt(id!)).unwrap() + } + const onWrite = async (editedTimeBound: EditDataType) => { await editTimeBoundRequest(editedTimeBound) } @@ -48,6 +72,7 @@ export const TimeBoundDetails = () => { name: 'unknown', error: null, })} + deleteFunction={deleteFunction} /> ) } diff --git a/frontend/src/components/TimeUnit/TimeUnitDetails.tsx b/frontend/src/components/TimeUnit/TimeUnitDetails.tsx index 7853d43d..e50268aa 100644 --- a/frontend/src/components/TimeUnit/TimeUnitDetails.tsx +++ b/frontend/src/components/TimeUnit/TimeUnitDetails.tsx @@ -1,21 +1,45 @@ -import { useParams } from 'react-router-dom' -import { useEditTimeUnitMutation, useGetTimeUnitDetailsQuery } from '../../redux/timeUnitReducer' +import { EditDataType, TimeUnitDetailsType } from '@/backendTypes' +import { useNotify } from '@/hooks/notification' import { CircularProgress } from '@mui/material' +import { useEffect } from 'react' +import { useNavigate, useParams } from 'react-router-dom' +import { + useDeleteTimeUnitMutation, + useEditTimeUnitMutation, + useGetTimeUnitDetailsQuery, +} from '../../redux/timeUnitReducer' +import { emptyTimeUnit } from '../DetailView/common/defaultValues' +import { UpdateTab } from '../DetailView/common/UpdateTab' import { DetailView, TabType } from '../DetailView/DetailView' import { LocalityTab } from './Tabs/LocalityTab' import { TimeUnitTab } from './Tabs/TimeUnitTab' -import { UpdateTab } from '../DetailView/common/UpdateTab' -import { EditDataType, TimeUnitDetailsType } from '@/backendTypes' -import { emptyTimeUnit } from '../DetailView/common/defaultValues' export const TimeUnitDetails = () => { const { id } = useParams() const isNew = id === 'new' const { isLoading, isError, isFetching, data } = useGetTimeUnitDetailsQuery(encodeURIComponent(id!), { skip: isNew }) const [editTimeUnitRequest] = useEditTimeUnitMutation() + + const notify = useNotify() + const navigate = useNavigate() + const [deleteMutation, { isSuccess: deleteSuccess, isError: deleteError }] = useDeleteTimeUnitMutation() + + useEffect(() => { + if (deleteSuccess) { + notify('Deleted item successfully.') + navigate('/time-unit') + } else if (deleteError) { + notify('Could not delete item. Error happened.', 'error') + } + }, [deleteSuccess, deleteError, notify, navigate]) + if (isError) return
Error loading data
if (isLoading || isFetching || (!data && !isNew)) return + const deleteFunction = async () => { + await deleteMutation(id!).unwrap() + } + const onWrite = async (editData: EditDataType) => { await editTimeUnitRequest(editData) } @@ -43,6 +67,7 @@ export const TimeUnitDetails = () => { data={isNew ? emptyTimeUnit : data!} onWrite={onWrite} validator={() => ({ name: '', error: null })} + deleteFunction={deleteFunction} /> ) } diff --git a/frontend/src/redux/speciesReducer.ts b/frontend/src/redux/speciesReducer.ts index 21dc50ef..952b5666 100644 --- a/frontend/src/redux/speciesReducer.ts +++ b/frontend/src/redux/speciesReducer.ts @@ -24,7 +24,14 @@ const speciesApi = api.injectEndpoints({ invalidatesTags: (result, _error, { species_id }) => result ? [{ type: 'species', id: species_id }, 'specieslist'] : [], }), + deleteSpecies: builder.mutation({ + query: id => ({ + url: `/species/${id}`, + method: 'DELETE', + }), + }), }), }) -export const { useGetAllSpeciesQuery, useGetSpeciesDetailsQuery, useEditSpeciesMutation } = speciesApi +export const { useGetAllSpeciesQuery, useGetSpeciesDetailsQuery, useEditSpeciesMutation, useDeleteSpeciesMutation } = + speciesApi diff --git a/frontend/src/redux/timeBoundReducer.ts b/frontend/src/redux/timeBoundReducer.ts index 807dd506..827c5330 100644 --- a/frontend/src/redux/timeBoundReducer.ts +++ b/frontend/src/redux/timeBoundReducer.ts @@ -28,6 +28,12 @@ const timeboundsApi = api.injectEndpoints({ }), invalidatesTags: (result, _error, { bid }) => (result ? [{ type: 'timebound', id: bid }, 'timebounds'] : []), }), + deleteTimeBound: builder.mutation({ + query: id => ({ + url: `/time-bound/${id}`, + method: 'DELETE', + }), + }), }), }) @@ -36,4 +42,5 @@ export const { useGetTimeBoundDetailsQuery, useGetTimeBoundTimeUnitsQuery, useEditTimeBoundMutation, + useDeleteTimeBoundMutation, } = timeboundsApi diff --git a/frontend/src/redux/timeUnitReducer.ts b/frontend/src/redux/timeUnitReducer.ts index 334b6c5d..73b5eda9 100644 --- a/frontend/src/redux/timeUnitReducer.ts +++ b/frontend/src/redux/timeUnitReducer.ts @@ -34,6 +34,13 @@ const timeunitsApi = api.injectEndpoints({ invalidatesTags: (result, _error, { tu_name }) => result ? [{ type: 'timeunit', id: tu_name }, 'timeunits'] : [], }), + deleteTimeUnit: builder.mutation({ + query: id => ({ + url: `/time-unit/${id}`, + method: 'DELETE', + }), + invalidatesTags: ['timeunits'], + }), }), }) @@ -43,4 +50,5 @@ export const { useGetTimeUnitDetailsQuery, useEditTimeUnitMutation, useGetSequencesQuery, + useDeleteTimeUnitMutation, } = timeunitsApi