update types for occurrence backend routes, and update more unit tests

This commit is contained in:
juhanikat
2026-09-19 14:10:48 +03:00
parent 83ef8cf15c
commit 70e479e470
5 changed files with 37 additions and 20 deletions
@@ -1,8 +1,8 @@
import { Request, Response } from 'express'
import { EditDataType, EditMetaData, OccurrenceDetailsType } from '../../../frontend/src/shared/types'
import { getOccurrenceByCompositeKey, parseOccurrenceRouteParams } from '../services/occurrenceService'
import { fixBigInt } from '../utils/common'
import { EditMetaData, EditableOccurrenceData } from '../../../frontend/src/shared/types'
import { updateOccurrenceByCompositeKey } from '../services/write/occurrence'
import { fixBigInt } from '../utils/common'
type OccurrenceRouteParams = {
lid: string
@@ -21,12 +21,12 @@ export const getOccurrenceDetail = async (req: Request<OccurrenceRouteParams>, r
}
export const updateOccurrenceDetail = async (
req: Request<OccurrenceRouteParams, object, { occurrence?: EditableOccurrenceData & EditMetaData }>,
req: Request<OccurrenceRouteParams, object, { occurrence: EditDataType<OccurrenceDetailsType> & EditMetaData }>,
res: Response
) => {
const { lid, speciesId } = parseOccurrenceRouteParams(req.params.lid, req.params.speciesId)
const occurrencePayload: EditableOccurrenceData & EditMetaData = req.body.occurrence ?? {}
const occurrencePayload = req.body.occurrence ?? {}
const updatedOccurrence = await updateOccurrenceByCompositeKey(lid, speciesId, occurrencePayload, req.user)
if (!updatedOccurrence) {
+10 -3
View File
@@ -1,9 +1,16 @@
import { AnyReference, EditableOccurrenceData, Role, User } from '../../../frontend/src/shared/types'
import {
AnyReference,
EditableOccurrenceData,
EditDataType,
OccurrenceDetailsType,
Role,
User,
} from '../../../frontend/src/shared/types'
import { validateOccurrence } from '../../../frontend/src/shared/validators/occurrence'
import { AccessError } from '../middlewares/authorizer'
import { logDb, nowDb } from '../utils/db'
import { buildPersonLookupByInitials, getPersonDisplayName, getPersonFromLookup } from './utils/person'
import { generateOccurrenceDetailSql } from './queries/crossSearchQuery'
import { buildPersonLookupByInitials, getPersonDisplayName, getPersonFromLookup } from './utils/person'
import { addNullExactDateToReferenceJoins, referenceWithoutExactDateSelect } from './utils/referenceDate'
const getAllowedLocalities = async (user: User) => {
@@ -47,7 +54,7 @@ export const ensureOccurrenceEditAccess = async (lid: number, user: User) => {
throw new AccessError()
}
export const validateOccurrencePayload = (payload: EditableOccurrenceData) => {
export const validateOccurrencePayload = (payload: EditDataType<OccurrenceDetailsType>) => {
const validationErrors = (Object.keys(payload) as Array<keyof EditableOccurrenceData>)
.map(fieldName => validateOccurrence(payload, fieldName))
.filter(validation => !!validation.error)
+3 -1
View File
@@ -1,6 +1,8 @@
import {
EditDataType,
EditMetaData,
EditableOccurrenceData,
OccurrenceDetailsType,
OccurrenceEditableField,
Reference,
Role,
@@ -93,7 +95,7 @@ const buildOccurrenceUpdateData = (
export const updateOccurrenceByCompositeKey = async (
lid: number,
speciesId: number,
editedOccurrence: EditableOccurrenceData & EditMetaData,
editedOccurrence: EditDataType<OccurrenceDetailsType> & EditMetaData,
user?: User
) => {
if (!user || ![Role.Admin, Role.EditUnrestricted, Role.EditRestricted].includes(user.role)) {
@@ -1,17 +1,23 @@
import { describe, expect, it } from '@jest/globals'
import { validateOccurrence, occurrenceDropdownValues } from '../../../../frontend/src/shared/validators/occurrence'
import { EditableOccurrenceData } from '../../../../frontend/src/shared/types'
import { EditableOccurrenceData, EditDataType, OccurrenceDetailsType } from '../../../../frontend/src/shared/types'
const createOccurrencePayload = (values: EditableOccurrenceData) => {
return {
...values,
} as EditDataType<OccurrenceDetailsType>
}
describe('validateOccurrence', () => {
it('accepts valid dropdown values', () => {
const payload: EditableOccurrenceData = {
const payload = createOccurrencePayload({
id_status: occurrenceDropdownValues.idStatus[0],
qua: occurrenceDropdownValues.quantity[0],
mesowear: occurrenceDropdownValues.mesowear[0],
microwear: occurrenceDropdownValues.microwear[0],
}
})
const errors = (Object.keys(payload) as Array<keyof EditableOccurrenceData>)
const errors = (Object.keys(payload) as Array<keyof OccurrenceDetailsType>)
.map(field => validateOccurrence(payload, field))
.filter(validation => validation.error)
@@ -19,12 +25,12 @@ describe('validateOccurrence', () => {
})
it('returns errors for invalid dropdown values', () => {
const payload: EditableOccurrenceData = {
const payload = createOccurrencePayload({
id_status: 'invalid',
qua: 'x',
mesowear: 'foo',
microwear: 'bar',
}
})
const errors = (Object.keys(payload) as Array<keyof EditableOccurrenceData>)
.map(field => validateOccurrence(payload, field))
@@ -35,11 +41,11 @@ describe('validateOccurrence', () => {
})
it('enforces mesowear scale consistency', () => {
const payload: EditableOccurrenceData = {
const payload = createOccurrencePayload({
mw_scale_min: 5,
mw_scale_max: 3,
mw_value: 1,
}
})
expect(validateOccurrence(payload, 'mw_scale_min').error).toBe(
'Scale Minimum cannot be greater than Scale Maximum.'
@@ -51,12 +57,12 @@ describe('validateOccurrence', () => {
})
it('enforces isotope min/max ordering', () => {
const payload: EditableOccurrenceData = {
const payload = createOccurrencePayload({
dc13_min: 5,
dc13_max: 4,
do18_min: 9,
do18_max: 8,
}
})
expect(validateOccurrence(payload, 'dc13_min').error).toBe('δ13C min cannot be greater than δ13C max.')
expect(validateOccurrence(payload, 'dc13_max').error).toBe('δ13C min cannot be greater than δ13C max.')
@@ -65,13 +71,13 @@ describe('validateOccurrence', () => {
})
it('requires positive decimals for requested fields', () => {
const payload: EditableOccurrenceData = {
const payload = createOccurrencePayload({
mw_value: -1,
dc13_mean: -0.1,
dc13_stdev: 0,
do18_mean: -2.5,
do18_stdev: 0,
}
})
expect(validateOccurrence(payload, 'mw_value').error).toBe('MW value must be a positive decimal number.')
expect(validateOccurrence(payload, 'dc13_mean').error).toBe('δ13C Mean must be a positive decimal number.')
@@ -44,6 +44,8 @@ describe('TableToolBar export menu', () => {
<TableToolBar
table={{} as MRT_TableInstance<MRT_RowData>}
tableName="Localities"
showContactButton={false}
showNewButton={false}
kmlExport={kmlExport}
svgExport={svgExport}
/>