brf/server/lib/knex_rest_queries.ts
2025-12-18 07:31:37 +01:00

317 lines
8.0 KiB
TypeScript

// @ts-nocheck
import { type Knex } from 'knex'
import _ from 'lodash'
import { where } from './knex_helpers.ts'
type QueryName =
| 'create'
| 'createMany'
| 'find'
| 'findOne'
| 'findById'
| 'getAll'
| 'paginate'
| 'remove'
| 'removeById'
| 'replace'
| 'update'
interface Options {
knex: Knex
emitter?: ANY
pick?: QueryName[]
omit?: QueryName[]
columns: string[]
table: string
selects?: Record<string, ANY>
}
export const count = ({ knex, table, columns }: Pick<Options, 'knex' | 'table' | 'columns'>) =>
function count(query: Record<string, ANY>, client = knex) {
let builder = client.table(table)
if (!_.isEmpty(query)) {
builder = where(builder, _.pick(query, columns))
}
return builder.count().then((count) => parseInt(count[0].count, 10))
}
export const create = ({ knex, table, columns, emitter }: Pick<Options, 'knex' | 'table' | 'columns' | 'emitter'>) =>
function create(json, client = knex) {
return client
.table(table)
.insert(_.pickBy(json, (value, key) => value !== undefined && columns.includes(key)))
.returning('*')
.then((result) => {
if (emitter) {
emitter.emit('db', { table, action: 'create', result })
}
return result[0]
})
}
export const createMany = ({
knex,
table,
columns,
emitter,
}: Pick<Options, 'knex' | 'table' | 'columns' | 'emitter'>) =>
function createMany(collection, client = knex) {
if (!Array.isArray(collection)) {
collection = [collection]
}
const values = collection.map((item) =>
_.pickBy(item, (value, key) => value !== undefined && columns.includes(key)),
)
return client
.table(table)
.insert(values)
.returning('*')
.then((result) => {
if (emitter) {
emitter.emit('db', { table, action: 'create', result })
}
return result
})
}
export const find = ({
knex,
table,
columns,
allSelects,
defaults = {},
}: Pick<Options, 'knex' | 'table' | 'columns'> & { allSelects: string[]; defaults: Record<string, any> }) =>
function find(json, select, client: Knex) {
if (!client) {
// TODO figure out if better, eg instanceof, check is possible
if (select && select.andWhereNotBetween) {
client = select
select = null
} else {
client = knex
}
}
const { offset = defaults.offset, limit = defaults.limit, sort = defaults.sort, ...query } = json
let builder = client.table(table).select(select ? _.pick(allSelects, select) : allSelects)
if (!_.isEmpty(query)) {
builder = where(builder, _.pick(query, columns))
}
builder = builder.orderBy(sort || 'id', sort?.startsWith('-') ? 'desc' : 'asc')
if (limit) {
builder = builder.limit(limit)
}
if (offset) {
builder = builder.offset(offset)
}
return builder
}
export const findById = ({ knex, table, allSelects }: Pick<Options, 'knex' | 'table'> & { allSelects: string[] }) =>
function findById(id: number | string, select, client: Knex) {
if (!client) {
// TODO figure out if better, eg instanceof, check is possible
if (select && select.andWhereNotBetween) {
client = select
select = null
} else {
client = knex
}
}
return client
.table(table)
.first(select ? _.pick(allSelects, select) : allSelects)
.where('id', id)
}
export const findOne = ({
knex,
table,
columns,
allSelects,
}: Pick<Options, 'knex' | 'table' | 'columns'> & { allSelects: string[] }) =>
function findOne(query, select, client: Knex) {
if (!client) {
// TODO figure out if better, eg instanceof, check is possible
if (select && select.andWhereNotBetween) {
client = select
select = null
} else {
client = knex
}
}
let builder = client.table(table).first(select ? _.pick(allSelects, select) : allSelects)
if (!_.isEmpty(query)) {
builder = where(builder, _.pick(query, columns))
}
if (query?.sort) {
builder = builder.orderBy(query.sort)
}
return builder
}
export const getAll = ({ knex, table, allSelects }: Pick<Options, 'knex' | 'table'> & { allSelects: string[] }) =>
function getAll(select, client: Knex) {
if (!client) {
// TODO figure out if better, eg instanceof, check is possible
if (select && select.andWhereNotBetween) {
client = select
select = null
} else {
client = knex
}
}
return client.table(table).select(select ? _.pick(allSelects, select) : allSelects)
}
export const remove = ({ knex, table, columns, emitter }: Pick<Options, 'knex' | 'table' | 'columns' | 'emitter'>) =>
function remove(query, client = knex) {
let builder = client.table(table).delete()
if (!_.isEmpty(query)) {
builder = where(builder, _.pick(query, columns))
}
return builder.then((result) => {
if (emitter) {
emitter.emit('db', { table, action: 'delete', result })
}
return result
})
}
export const removeById = ({ knex, table, emitter }: Pick<Options, 'knex' | 'table' | 'emitter'>) =>
function removeById(id: number | string, client = knex) {
return client
.table(table)
.delete()
.where('id', id)
.then((result) => {
if (emitter) {
emitter.emit('db', { table, action: 'delete', result })
}
return result
})
}
// should be used with PUT
export const replace = ({ knex, table, columns, emitter }: Pick<Options, 'knex' | 'table' | 'columns' | 'emitter'>) =>
function replace(id: number | string, object, client = knex) {
object = columns.reduce((result, key) => {
const value = object[key]
result[columns[key]] = value === undefined ? null : value
return result
}, {})
return client
.table(table)
.update(object)
.where('id', id)
.returning('*')
.then((result) => {
if (result.length === 0) return null
if (emitter) {
emitter.emit('db', { table, action: 'update', result })
}
return result[0]
})
}
// should be used with PATCH
export const update = ({ knex, table, columns, emitter }: Pick<Options, 'knex' | 'table' | 'columns' | 'emitter'>) =>
function update(id: number | string, object: Record<string, any>, client = knex) {
object = _.pickBy(object, (value, key) => value !== undefined && columns.includes(key))
if (columns.includes('modifiedAt')) {
object.modifiedAt = knex.fn.now()
}
return client
.table(table)
.update(object)
.where('id', id)
.returning('*')
.then((result) => {
if (result.length === 0) return null
if (emitter) {
emitter.emit('db', { table, action: 'update', result })
}
return result[0]
})
}
export const updateMany = ({ knex, table, emitter }: Pick<Options, 'knex' | 'table' | 'emitter'>) =>
function updateMany(query, object, client = knex) {
const builder = client.table(table).update(typeof object === 'function' ? object(knex) : object)
return where(builder, query)
.returning('*')
.then((result) => {
if (result.length === 0) return null
if (emitter) {
emitter.emit('db', { table, action: 'update', result })
}
return result[0]
})
}
const factories = {
count,
create,
createMany,
find,
findOne,
findById,
getAll,
remove,
removeById,
replace,
update,
updateMany,
}
const queryNames = Object.keys(factories)
export default function restQueriesFactory({ knex, emitter, table, columns, selects, omit, pick }: Options) {
const allSelects = selects ? [...columns, selects] : columns
pick = pick || (_.difference(queryNames, omit || []) as QueryName[])
return pick.reduce((result, value) => {
if (factories[value]) {
result[value] = factories[value]({ columns, selects, allSelects, knex, emitter, table })
}
return result
}, {}) as Record<QueryName, (...args: any) => Promise<ANY>>
}