mirror of
https://github.com/nowcommunity/nowdatabase.git
synced 2026-10-12 19:13:52 +03:00
92 lines
4.0 KiB
TypeScript
92 lines
4.0 KiB
TypeScript
import { RowState } from '../../../../frontend/src/backendTypes'
|
|
import { AllowedTables, DbValue, PrimaryTables, WriteItem } from '../write/writeUtils'
|
|
import { DatabaseHandler, DbWriteItem } from './databaseHandler'
|
|
import { fixBoolean, getItemList, valueIsDifferent } from './utils'
|
|
|
|
/* Handles writing logic that are agnostic of datatype, but not directly operations to db. */
|
|
/* Usage: after constructor, initialize with writeHandler.start. Assign value to idValue whever you have it. */
|
|
export class WriteHandler extends DatabaseHandler {
|
|
table: PrimaryTables
|
|
writeList: WriteItem[] = []
|
|
idColumn: string
|
|
idValue?: string | number
|
|
allowedColumns: string[]
|
|
|
|
constructor(dbName: string, table: PrimaryTables, idColumn: string, allowedColumns: string[]) {
|
|
super(dbName)
|
|
this.table = table
|
|
this.idColumn = idColumn
|
|
this.allowedColumns = allowedColumns
|
|
}
|
|
|
|
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 updateRow<T extends Record<string, DbValue>>(table: AllowedTables, items: DbWriteItem[], ids: DbWriteItem[]) {
|
|
const oldObject = await super.select<T>(table, [], ids)
|
|
const fieldsToWrite: DbWriteItem[] = []
|
|
// TODO put oldValue here too for logging
|
|
for (const { column, value } of items) {
|
|
const oldValue = oldObject[column as keyof T]
|
|
|
|
const fixedValue = fixBoolean(value)
|
|
if (!valueIsDifferent(fixedValue, oldValue)) continue
|
|
|
|
fieldsToWrite.push({ column, value: fixedValue })
|
|
}
|
|
if (items.find(item => !this.allowedColumns.includes(item.column))) {
|
|
throw new Error('Unallowed column in SQL')
|
|
}
|
|
this.writeList.push({ table, type: 'update', items: items.map(item => ({ ...item, table })) })
|
|
if (fieldsToWrite.length === 0) return
|
|
return await super.update(table, fieldsToWrite, ids)
|
|
}
|
|
|
|
async createObject<T extends Record<string, unknown>>(table: AllowedTables, object: T, idColumns: string[]) {
|
|
if (idColumns.includes(this.idColumn) && this.idValue && !object[this.idColumn]) {
|
|
;(object as Record<string, unknown>)[this.idColumn] = this.idValue
|
|
}
|
|
const itemsToWrite = getItemList({ ...object }, true)
|
|
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, true)
|
|
return await this.updateRow<Record<keyof T, DbValue>>(table, itemsToWrite, ids)
|
|
}
|
|
|
|
async upsertObject<T extends Record<string, unknown>>(table: AllowedTables, object: T, idColumns: string[]) {
|
|
if (idColumns.includes(this.idColumn) && this.idValue && !object[this.idColumn]) {
|
|
;(object as Record<string, unknown>)[this.idColumn] = this.idValue
|
|
}
|
|
if (idColumns.find(idCol => !object[idCol])) return await this.createObject(table, object, idColumns)
|
|
return await this.updateObject<T>(table, object, idColumns)
|
|
}
|
|
|
|
async upsertList<T extends { rowState?: RowState }>(table: AllowedTables, objects: T[], idColumns: string[]) {
|
|
for (const object of objects) {
|
|
const rowState = object.rowState
|
|
if (rowState === 'new') {
|
|
await this.createObject(table, object, idColumns)
|
|
} else if (rowState === 'removed') {
|
|
await this.delete(
|
|
table,
|
|
idColumns.map(idColumn => ({ column: idColumn, value: object[idColumn as keyof T] as DbValue }))
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|