From 80fa36853d1a562bdd5c3748e080b97bf40603a7 Mon Sep 17 00:00:00 2001 From: Dan Gowans Date: Fri, 21 Mar 2025 09:12:31 -0400 Subject: [PATCH] ensure burial site names remain distinct --- database/addBurialSite.d.ts | 7 +++ database/addBurialSite.js | 31 ++++++++++- database/addBurialSite.ts | 52 ++++++++++++++++--- database/getBurialSites.js | 6 +-- database/getBurialSites.ts | 6 +-- database/updateBurialSite.d.ts | 7 +++ database/updateBurialSite.js | 20 +++++++ database/updateBurialSite.ts | 32 ++++++++++-- .../burialSites-post/doCreateBurialSite.js | 24 ++++++--- .../burialSites-post/doCreateBurialSite.ts | 29 +++++++---- .../burialSites-post/doUpdateBurialSite.js | 30 +++++++---- .../burialSites-post/doUpdateBurialSite.ts | 37 +++++++------ 12 files changed, 212 insertions(+), 69 deletions(-) diff --git a/database/addBurialSite.d.ts b/database/addBurialSite.d.ts index 3e434f12..0278cda2 100644 --- a/database/addBurialSite.d.ts +++ b/database/addBurialSite.d.ts @@ -13,4 +13,11 @@ export interface AddBurialSiteForm { burialSiteTypeFieldIds?: string; [fieldValue_burialSiteTypeFieldId: string]: unknown; } +/** + * Creates a new burial site. + * @param burialSiteForm - The new burial site's information + * @param user - The user making the request + * @returns The new burial site's id. + * @throws If an active burial site with the same name already exists. + */ export default function addBurialSite(burialSiteForm: AddBurialSiteForm, user: User): Promise; diff --git a/database/addBurialSite.js b/database/addBurialSite.js index d88edcb2..708de21c 100644 --- a/database/addBurialSite.js +++ b/database/addBurialSite.js @@ -2,11 +2,32 @@ import { buildBurialSiteName } from '../helpers/burialSites.helpers.js'; import addOrUpdateBurialSiteField from './addOrUpdateBurialSiteField.js'; import getCemetery from './getCemetery.js'; import { acquireConnection } from './pool.js'; +/** + * Creates a new burial site. + * @param burialSiteForm - The new burial site's information + * @param user - The user making the request + * @returns The new burial site's id. + * @throws If an active burial site with the same name already exists. + */ export default async function addBurialSite(burialSiteForm, user) { const database = await acquireConnection(); const rightNowMillis = Date.now(); - const cemetery = burialSiteForm.cemeteryId === '' ? undefined : await getCemetery(burialSiteForm.cemeteryId, database); + const cemetery = burialSiteForm.cemeteryId === '' + ? undefined + : await getCemetery(burialSiteForm.cemeteryId, database); const burialSiteName = buildBurialSiteName(cemetery?.cemeteryKey, burialSiteForm); + // Ensure no active burial sites share the same name + const existingBurialSite = database + .prepare(`select burialSiteId + from BurialSites + where burialSiteName = ? + and recordDelete_timeMillis is null`) + .pluck() + .get(burialSiteName); + if (existingBurialSite !== undefined) { + database.release(); + throw new Error('An active burial site with that name already exists.'); + } const result = database .prepare(`insert into BurialSites ( burialSiteNameSegment1, @@ -24,7 +45,13 @@ export default async function addBurialSite(burialSiteForm, user) { values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`) - .run(burialSiteForm.burialSiteNameSegment1, burialSiteForm.burialSiteNameSegment2 ?? '', burialSiteForm.burialSiteNameSegment3 ?? '', burialSiteForm.burialSiteNameSegment4 ?? '', burialSiteForm.burialSiteNameSegment5 ?? '', burialSiteName, burialSiteForm.burialSiteTypeId, burialSiteForm.burialSiteStatusId === '' ? undefined : burialSiteForm.burialSiteStatusId, burialSiteForm.cemeteryId === '' ? undefined : burialSiteForm.cemeteryId, burialSiteForm.cemeterySvgId, burialSiteForm.burialSiteLatitude === '' ? undefined : burialSiteForm.burialSiteLatitude, burialSiteForm.burialSiteLongitude === '' ? undefined : burialSiteForm.burialSiteLongitude, user.userName, rightNowMillis, user.userName, rightNowMillis); + .run(burialSiteForm.burialSiteNameSegment1, burialSiteForm.burialSiteNameSegment2 ?? '', burialSiteForm.burialSiteNameSegment3 ?? '', burialSiteForm.burialSiteNameSegment4 ?? '', burialSiteForm.burialSiteNameSegment5 ?? '', burialSiteName, burialSiteForm.burialSiteTypeId, burialSiteForm.burialSiteStatusId === '' + ? undefined + : burialSiteForm.burialSiteStatusId, burialSiteForm.cemeteryId === '' ? undefined : burialSiteForm.cemeteryId, burialSiteForm.cemeterySvgId, burialSiteForm.burialSiteLatitude === '' + ? undefined + : burialSiteForm.burialSiteLatitude, burialSiteForm.burialSiteLongitude === '' + ? undefined + : burialSiteForm.burialSiteLongitude, user.userName, rightNowMillis, user.userName, rightNowMillis); const burialSiteId = result.lastInsertRowid; const burialSiteTypeFieldIds = (burialSiteForm.burialSiteTypeFieldIds ?? '').split(','); for (const burialSiteTypeFieldId of burialSiteTypeFieldIds) { diff --git a/database/addBurialSite.ts b/database/addBurialSite.ts index 28a876ad..a617e29e 100644 --- a/database/addBurialSite.ts +++ b/database/addBurialSite.ts @@ -24,6 +24,13 @@ export interface AddBurialSiteForm { [fieldValue_burialSiteTypeFieldId: string]: unknown } +/** + * Creates a new burial site. + * @param burialSiteForm - The new burial site's information + * @param user - The user making the request + * @returns The new burial site's id. + * @throws If an active burial site with the same name already exists. + */ export default async function addBurialSite( burialSiteForm: AddBurialSiteForm, user: User @@ -32,11 +39,32 @@ export default async function addBurialSite( const rightNowMillis = Date.now() - const cemetery = burialSiteForm.cemeteryId === '' ? undefined : await getCemetery(burialSiteForm.cemeteryId, database) + const cemetery = + burialSiteForm.cemeteryId === '' + ? undefined + : await getCemetery(burialSiteForm.cemeteryId, database) const burialSiteName = buildBurialSiteName( cemetery?.cemeteryKey, - burialSiteForm) + burialSiteForm + ) + + // Ensure no active burial sites share the same name + + const existingBurialSite = database + .prepare( + `select burialSiteId + from BurialSites + where burialSiteName = ? + and recordDelete_timeMillis is null` + ) + .pluck() + .get(burialSiteName) as number | undefined + + if (existingBurialSite !== undefined) { + database.release() + throw new Error('An active burial site with that name already exists.') + } const result = database .prepare( @@ -65,11 +93,17 @@ export default async function addBurialSite( burialSiteForm.burialSiteNameSegment5 ?? '', burialSiteName, burialSiteForm.burialSiteTypeId, - burialSiteForm.burialSiteStatusId === '' ? undefined : burialSiteForm.burialSiteStatusId, + burialSiteForm.burialSiteStatusId === '' + ? undefined + : burialSiteForm.burialSiteStatusId, burialSiteForm.cemeteryId === '' ? undefined : burialSiteForm.cemeteryId, burialSiteForm.cemeterySvgId, - burialSiteForm.burialSiteLatitude === '' ? undefined : burialSiteForm.burialSiteLatitude, - burialSiteForm.burialSiteLongitude === '' ? undefined : burialSiteForm.burialSiteLongitude, + burialSiteForm.burialSiteLatitude === '' + ? undefined + : burialSiteForm.burialSiteLatitude, + burialSiteForm.burialSiteLongitude === '' + ? undefined + : burialSiteForm.burialSiteLongitude, user.userName, rightNowMillis, user.userName, @@ -78,10 +112,14 @@ export default async function addBurialSite( const burialSiteId = result.lastInsertRowid as number - const burialSiteTypeFieldIds = (burialSiteForm.burialSiteTypeFieldIds ?? '').split(',') + const burialSiteTypeFieldIds = ( + burialSiteForm.burialSiteTypeFieldIds ?? '' + ).split(',') for (const burialSiteTypeFieldId of burialSiteTypeFieldIds) { - const fieldValue = burialSiteForm[`burialSiteFieldValue_${burialSiteTypeFieldId}`] as string | undefined + const fieldValue = burialSiteForm[ + `burialSiteFieldValue_${burialSiteTypeFieldId}` + ] as string | undefined if ((fieldValue ?? '') !== '') { await addOrUpdateBurialSiteField( diff --git a/database/getBurialSites.js b/database/getBurialSites.js index e614f1f3..b91670aa 100644 --- a/database/getBurialSites.js +++ b/database/getBurialSites.js @@ -91,11 +91,7 @@ export default async function getBurialSites(filters, options, connectedDatabase group by burialSiteId) o on l.burialSiteId = o.burialSiteId` : ''} ${sqlWhereClause} - order by l.burialSiteNameSegment1, - l.burialSiteNameSegment2, - l.burialSiteNameSegment3, - l.burialSiteNameSegment4, - l.burialSiteNameSegment5, + order by l.burialSiteName, l.burialSiteId ${options.limit === -1 ? '' diff --git a/database/getBurialSites.ts b/database/getBurialSites.ts index fe448d86..9a9fecbe 100644 --- a/database/getBurialSites.ts +++ b/database/getBurialSites.ts @@ -148,11 +148,7 @@ export default async function getBurialSites( : '' } ${sqlWhereClause} - order by l.burialSiteNameSegment1, - l.burialSiteNameSegment2, - l.burialSiteNameSegment3, - l.burialSiteNameSegment4, - l.burialSiteNameSegment5, + order by l.burialSiteName, l.burialSiteId ${ options.limit === -1 diff --git a/database/updateBurialSite.d.ts b/database/updateBurialSite.d.ts index f37a6e58..5bf50bf6 100644 --- a/database/updateBurialSite.d.ts +++ b/database/updateBurialSite.d.ts @@ -14,5 +14,12 @@ export interface UpdateBurialSiteForm { burialSiteTypeFieldIds?: string; [fieldValue_burialSiteTypeFieldId: string]: unknown; } +/** + * Updates a burial site. + * @param updateForm - The burial site's updated information + * @param user - The user making the request + * @returns True if the burial site was updated. + * @throws If an active burial site with the same name already exists. + */ export default function updateBurialSite(updateForm: UpdateBurialSiteForm, user: User): Promise; export declare function updateBurialSiteStatus(burialSiteId: number | string, burialSiteStatusId: number | string, user: User): Promise; diff --git a/database/updateBurialSite.js b/database/updateBurialSite.js index 50ecbb7e..a3e3e22e 100644 --- a/database/updateBurialSite.js +++ b/database/updateBurialSite.js @@ -3,12 +3,32 @@ import addOrUpdateBurialSiteField from './addOrUpdateBurialSiteField.js'; import deleteBurialSiteField from './deleteBurialSiteField.js'; import getCemetery from './getCemetery.js'; import { acquireConnection } from './pool.js'; +/** + * Updates a burial site. + * @param updateForm - The burial site's updated information + * @param user - The user making the request + * @returns True if the burial site was updated. + * @throws If an active burial site with the same name already exists. + */ export default async function updateBurialSite(updateForm, user) { const database = await acquireConnection(); const cemetery = updateForm.cemeteryId === '' ? undefined : await getCemetery(updateForm.cemeteryId, database); const burialSiteName = buildBurialSiteName(cemetery?.cemeteryKey, updateForm); + // Ensure no active burial sites share the same name + const existingBurialSite = database + .prepare(`select burialSiteId + from BurialSites + where burialSiteName = ? + and burialSiteId <> ? + and recordDelete_timeMillis is null`) + .pluck() + .get(burialSiteName, updateForm.burialSiteId); + if (existingBurialSite !== undefined) { + database.release(); + throw new Error('An active burial site with that name already exists.'); + } const result = database .prepare(`update BurialSites set burialSiteNameSegment1 = ?, diff --git a/database/updateBurialSite.ts b/database/updateBurialSite.ts index af77470d..841271e7 100644 --- a/database/updateBurialSite.ts +++ b/database/updateBurialSite.ts @@ -27,6 +27,13 @@ export interface UpdateBurialSiteForm { [fieldValue_burialSiteTypeFieldId: string]: unknown } +/** + * Updates a burial site. + * @param updateForm - The burial site's updated information + * @param user - The user making the request + * @returns True if the burial site was updated. + * @throws If an active burial site with the same name already exists. + */ export default async function updateBurialSite( updateForm: UpdateBurialSiteForm, user: User @@ -34,14 +41,29 @@ export default async function updateBurialSite( const database = await acquireConnection() const cemetery = - updateForm.cemeteryId === '' + updateForm.cemeteryId === '' ? undefined : await getCemetery(updateForm.cemeteryId, database) - const burialSiteName = buildBurialSiteName( - cemetery?.cemeteryKey, - updateForm - ) + const burialSiteName = buildBurialSiteName(cemetery?.cemeteryKey, updateForm) + + // Ensure no active burial sites share the same name + + const existingBurialSite = database + .prepare( + `select burialSiteId + from BurialSites + where burialSiteName = ? + and burialSiteId <> ? + and recordDelete_timeMillis is null` + ) + .pluck() + .get(burialSiteName, updateForm.burialSiteId) as number | undefined + + if (existingBurialSite !== undefined) { + database.release() + throw new Error('An active burial site with that name already exists.') + } const result = database .prepare( diff --git a/handlers/burialSites-post/doCreateBurialSite.js b/handlers/burialSites-post/doCreateBurialSite.js index e996c56e..d78718a2 100644 --- a/handlers/burialSites-post/doCreateBurialSite.js +++ b/handlers/burialSites-post/doCreateBurialSite.js @@ -1,12 +1,20 @@ import addBurialSite from '../../database/addBurialSite.js'; import { clearNextPreviousBurialSiteIdCache } from '../../helpers/burialSites.helpers.js'; export default async function handler(request, response) { - const burialSiteId = await addBurialSite(request.body, request.session.user); - response.json({ - success: true, - burialSiteId - }); - response.on('finish', () => { - clearNextPreviousBurialSiteIdCache(-1); - }); + try { + const burialSiteId = await addBurialSite(request.body, request.session.user); + response.json({ + success: true, + burialSiteId + }); + response.on('finish', () => { + clearNextPreviousBurialSiteIdCache(-1); + }); + } + catch (error) { + response.json({ + success: false, + errorMessage: error.message + }); + } } diff --git a/handlers/burialSites-post/doCreateBurialSite.ts b/handlers/burialSites-post/doCreateBurialSite.ts index 5b820609..d41daf37 100644 --- a/handlers/burialSites-post/doCreateBurialSite.ts +++ b/handlers/burialSites-post/doCreateBurialSite.ts @@ -9,17 +9,24 @@ export default async function handler( request: Request, response: Response ): Promise { - const burialSiteId = await addBurialSite( - request.body, - request.session.user as User - ) + try { + const burialSiteId = await addBurialSite( + request.body, + request.session.user as User + ) - response.json({ - success: true, - burialSiteId - }) + response.json({ + success: true, + burialSiteId + }) - response.on('finish', () => { - clearNextPreviousBurialSiteIdCache(-1) - }) + response.on('finish', () => { + clearNextPreviousBurialSiteIdCache(-1) + }) + } catch (error) { + response.json({ + success: false, + errorMessage: (error as Error).message + }) + } } diff --git a/handlers/burialSites-post/doUpdateBurialSite.js b/handlers/burialSites-post/doUpdateBurialSite.js index 62f55b0c..70dcef1a 100644 --- a/handlers/burialSites-post/doUpdateBurialSite.js +++ b/handlers/burialSites-post/doUpdateBurialSite.js @@ -1,15 +1,23 @@ import updateBurialSite from '../../database/updateBurialSite.js'; import { clearNextPreviousBurialSiteIdCache } from '../../helpers/burialSites.helpers.js'; export default async function handler(request, response) { - const success = await updateBurialSite(request.body, request.session.user); - const burialSiteId = typeof request.body.burialSiteId === 'string' - ? Number.parseInt(request.body.burialSiteId, 10) - : request.body.burialSiteId; - response.json({ - success, - burialSiteId - }); - response.on('finish', () => { - clearNextPreviousBurialSiteIdCache(burialSiteId); - }); + try { + const success = await updateBurialSite(request.body, request.session.user); + const burialSiteId = typeof request.body.burialSiteId === 'string' + ? Number.parseInt(request.body.burialSiteId, 10) + : request.body.burialSiteId; + response.json({ + success, + burialSiteId + }); + response.on('finish', () => { + clearNextPreviousBurialSiteIdCache(burialSiteId); + }); + } + catch (error) { + response.json({ + success: false, + errorMessage: error.message + }); + } } diff --git a/handlers/burialSites-post/doUpdateBurialSite.ts b/handlers/burialSites-post/doUpdateBurialSite.ts index 44567bea..e8406b5e 100644 --- a/handlers/burialSites-post/doUpdateBurialSite.ts +++ b/handlers/burialSites-post/doUpdateBurialSite.ts @@ -9,22 +9,29 @@ export default async function handler( request: Request, response: Response ): Promise { - const success = await updateBurialSite( - request.body, - request.session.user as User - ) + try { + const success = await updateBurialSite( + request.body, + request.session.user as User + ) - const burialSiteId = - typeof request.body.burialSiteId === 'string' - ? Number.parseInt(request.body.burialSiteId, 10) - : request.body.burialSiteId + const burialSiteId = + typeof request.body.burialSiteId === 'string' + ? Number.parseInt(request.body.burialSiteId, 10) + : request.body.burialSiteId - response.json({ - success, - burialSiteId - }) + response.json({ + success, + burialSiteId + }) - response.on('finish', () => { - clearNextPreviousBurialSiteIdCache(burialSiteId) - }) + response.on('finish', () => { + clearNextPreviousBurialSiteIdCache(burialSiteId) + }) + } catch (error) { + response.json({ + success: false, + errorMessage: (error as Error).message + }) + } }