WIP, edit writeHandler and write locality species stuff

This commit is contained in:
ShootingStar91
2024-08-24 17:56:22 +03:00
parent ca7f16a619
commit 1c70a3df65
6 changed files with 56 additions and 30 deletions
+20 -7
View File
@@ -1,10 +1,14 @@
import { EditDataType, LocalityDetailsType, LocalitySpecies } from '../../../../frontend/src/backendTypes'
import {
EditDataType,
LocalityDetailsType,
LocalitySpeciesDetailsType,
SpeciesDetailsType,
} from '../../../../frontend/src/backendTypes'
import { NOW_DB_NAME } from '../../utils/config'
import { getItemList } from '../writeOperations/utils'
import { WriteHandler } from '../writeOperations/writeHandler'
import { DbValue } from './writeUtils'
export const writeLocality = async (locality: EditDataType<LocalityDetailsType>) => {
const isNew: boolean = !!locality.lid
const writeHandler = new WriteHandler(NOW_DB_NAME, 'now_loc')
await writeHandler.start()
for (const ls of locality.now_ls) {
@@ -17,12 +21,21 @@ export const writeLocality = async (locality: EditDataType<LocalityDetailsType>)
}
/* Writes now_ls entries, and also any new species inside. */
const writeLocalitySpecies = async (writeHandler: WriteHandler, localitySpecies: EditDataType<LocalitySpecies>) => {
const writeLocalitySpecies = async (
writeHandler: WriteHandler,
localitySpecies: EditDataType<LocalitySpeciesDetailsType>
) => {
let species_id: DbValue | undefined = localitySpecies.com_species!.species_id
if (!localitySpecies.species_id) {
// const { species_id } = await writeHandler.writeTable('com_species', )
species_id = await writeSpecies(writeHandler, localitySpecies.com_species!)
}
const items = getItemList(localitySpecies)
return await writeHandler.writeTable('now_ls', items, [], ['lid', 'species_id'])
if (species_id) localitySpecies.com_species!.species_id = species_id
return await writeHandler.createObject('now_ls', localitySpecies, ['lid', 'species_id'])
}
const writeSpecies = async (writeHandler: WriteHandler, species: EditDataType<SpeciesDetailsType>) => {
const { species_id } = await writeHandler.createObject('com_species', species, ['species_id'])
return species_id as number
}
const deleteLocalitySpecies = async (writeHandler: WriteHandler, lid: number, species_id: number) => {
+6 -9
View File
@@ -14,13 +14,13 @@ import {
getFieldTypes,
getOldData,
ids,
isEmptyValue,
logAllUpdates,
printJSON,
tablesToNotLog,
} from './writeUtils'
import { NOW_DB_NAME } from '../../utils/config'
import { PoolConnection } from 'mariadb'
import { valueIsDifferent } from '../writeOperations/utils'
const query = async (queryString: string, values: Array<DbValue>, conn: PoolConnection) => {
const result: object = await conn.query(queryString, values)
@@ -35,7 +35,7 @@ export type WriteContext = {
type: ActionType
}
const writeTable = async <T extends CustomObject>(obj: T, tableName: AllowedTables, writeContext: WriteContext) => {
const writeTable = async <T extends CustomObject>(obj: T, tableName: AllowedTables, writeHandler: WriteHandler) => {
const oldObj = await getOldData(obj, tableName)
const allFields = Object.keys(obj).filter(f => allowedFields[f])
@@ -57,10 +57,10 @@ const writeTable = async <T extends CustomObject>(obj: T, tableName: AllowedTabl
We will first recourse into that, as we cannot write the parent table itself if we don't have the id of the possibly newly-created relation.
So we write all objectFields and take their id's to be written later */
for (const objectField of objectFields) {
const newId = await writeTable(obj[objectField] as CustomObject, objectField, writeContext)
const lastItem = ids[objectField].length - 1
const newId = await writeTable(obj[objectField] as CustomObject, objectField, writeHandler)
const lastIdIndex = ids[objectField].length - 1
// get the relevant id name from ids: the order is so that last item is the relevant id. e.g. now_ls
const idFieldName = ids[objectField][lastItem]
const idFieldName = ids[objectField][lastIdIndex]
relationIds[idFieldName] = newId
}
@@ -75,10 +75,7 @@ const writeTable = async <T extends CustomObject>(obj: T, tableName: AllowedTabl
const newValue = relationIds[field] ?? (obj[field as keyof object] as CustomObject)
const oldValue = oldObj?.[field as keyof object]
// Skipping the values that aren't changed: including differences in empty values (etc. null and "") or bigint/number different type
if (newValue === oldValue) continue
if (isEmptyValue(newValue) && isEmptyValue(oldValue)) continue
if (typeof oldValue === 'bigint' && typeof newValue === 'number' && BigInt(newValue) === oldValue) continue
if (!valueIsDifferent(newValue, oldValue)) continue
// Add the fields to be written, we will actually write them at the end.
if (!isRelationField || newValue !== undefined)
+2 -2
View File
@@ -97,7 +97,7 @@ export const logAllUpdates = async (
// if we are editing now_loc and this entry is triggered by now_ls (entry for com_species),
// the id will be "species_id", not "lid".
const idColumn = getIdColumn(targetTable)
const secondaryId = writeListItem.items.find(item => item.column === idColumn)?.value
const secondaryId = writeListItem.items.find(item => item.column === idColumn)?.value as string | number
if (!secondaryId)
throw new Error(`Error when creating update entries: id not found for targetTable: ${targetTable}`)
if (!secondaryUpdateEntries[targetTable]) secondaryUpdateEntries[targetTable] = {}
@@ -352,7 +352,7 @@ export type WriteItem = {
}
// Possible types of values that are read from db or written in there. BigInt is handled as number.
export type DbValue = string | number | null
export type DbValue = string | number | null | boolean
export const allowedFields: Record<string, boolean | undefined> = {}
const addFieldsToAllowed = (fields: string[]) =>
@@ -28,11 +28,11 @@ export class DatabaseHandler {
return { values: items.map(item => item.value), columns: items.map(item => item.column) }
}
async insert(table: AllowedTables, items: DbWriteItem[], returnColumns?: string[]) {
async insert<T>(table: AllowedTables, items: DbWriteItem[], returnColumns?: string[]) {
const returnString = returnColumns ? `RETURNING ${returnColumns.join(', ')}` : ''
const { columns, values } = this.getValuesAndColumns(items)
return await this.executeQuery(`INSERT INTO ${this.dbName}.${table} (?) VALUES (?) ${returnString}`, [
return await this.executeQuery<T>(`INSERT INTO ${this.dbName}.${table} (?) VALUES (?) ${returnString}`, [
columns,
values,
])
@@ -43,7 +43,7 @@ export class DatabaseHandler {
const columnsString = columns.join(', ')
const { columns: idColumns, values: idValues } = this.getValuesAndColumns(ids)
const whereClause = `${idColumns.map(idColumn => `${idColumn} = ?`).join(' AND ')}`
const whereClause = createWhereClause(idColumns)
await this.executeQuery(`UPDATE ${this.dbName}.${table} (${columnsString}) WHERE ${whereClause}`, [
values,
@@ -1,6 +1,6 @@
import { AllowedTables, PrimaryTables, WriteItem } from '../write/writeUtils'
import { AllowedTables, DbValue, PrimaryTables, WriteItem } from '../write/writeUtils'
import { DatabaseHandler, DbWriteItem } from './databaseHandler'
import { valueIsDifferent } from './utils'
import { getItemList, valueIsDifferent } from './utils'
/* Handles writing logic that are agnostic of datatype, but not directly operations to db. */
export class WriteHandler extends DatabaseHandler {
@@ -12,17 +12,21 @@ export class WriteHandler extends DatabaseHandler {
this.table = table
}
async createRow<T extends Record<string, DbValue>>(
table: AllowedTables,
items: DbWriteItem[],
idsToReturn: string[]
) {
this.writeList.push({ table, type: 'add', items: items.map(item => ({ ...item, table })) })
return await super.insert<T>(table, items, idsToReturn)
}
/* Checks each field for their old value and if they did not (meaningfully) change,
they are skipped, otherwise they are written to database.
table: Table to write to
items: Column & value pairs that should be written
ids: Column & value pairs that define which item to write to. Empty if new item. */
async writeTable<T>(table: AllowedTables, items: DbWriteItem[], ids: DbWriteItem[], idsToReturn: string[]) {
if (ids.length === 0) {
this.writeList.push({ table, type: 'add', items: items.map(item => ({ ...item, table })) })
return await super.insert(table, items, idsToReturn)
}
async updateRow<T extends Record<string, DbValue>>(table: AllowedTables, items: DbWriteItem[], ids: DbWriteItem[]) {
const oldObject = await super.select<T>(table, [], ids)
const fieldsToWrite: DbWriteItem[] = []
@@ -38,4 +42,15 @@ export class WriteHandler extends DatabaseHandler {
return await super.update(table, fieldsToWrite, ids)
}
async createObject<T extends Record<string, unknown>>(table: AllowedTables, object: T, idColumns: string[]) {
const itemsToWrite = getItemList(object)
return await this.createRow<Record<keyof T, DbValue>>(table, itemsToWrite, idColumns)
}
async updateObject<T extends Record<string, unknown>>(table: AllowedTables, object: T, idColumns: string[]) {
const ids = idColumns.map(column => ({ column, value: object[column] as DbValue }))
const itemsToWrite = getItemList(object)
return await this.updateRow<Record<keyof T, DbValue>>(table, itemsToWrite, ids)
}
}
+2 -1
View File
@@ -39,6 +39,7 @@ export type SedimentaryStructureValues = Prisma.now_ss_values
export type CollectingMethod = Prisma.now_coll_meth
export type LocalityProject = Prisma.now_plr & { now_proj: Prisma.now_proj }
export type LocalitySpecies = FixBigInt<Prisma.now_ls> & { com_species: SpeciesType }
export type LocalitySpeciesDetailsType = FixBigInt<Prisma.now_ls> & { com_species: SpeciesDetailsType }
export type SpeciesLocality = FixBigInt<Prisma.now_ls> & { now_loc: Prisma.now_loc }
export type LocalityUpdate = Prisma.now_lau & { now_lr: LocalityReference[] } & { updates: UpdateLog[] }
export type SpeciesUpdate = Prisma.now_sau & { now_sr: Prisma.now_sr }
@@ -59,7 +60,7 @@ export type LocalityDetailsType = Prisma.now_loc & {
} & {
projects: Array<Project>
} & {
now_ls: Array<LocalitySpecies>
now_ls: Array<LocalitySpeciesDetailsType>
} & { now_plr: Array<LocalityProject> } & { now_syn_loc: Array<LocalitySynonym> } & {
now_ss: SedimentaryStructure[]
} & {