// @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 } export const count = ({ knex, table, columns }: Pick) => function count(query: Record, 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) => 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) => 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 & { allSelects: string[]; defaults: Record }) => 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 & { 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 & { 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 & { 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) => 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) => 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) => 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) => function update(id: number | string, object: Record, 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) => 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 Promise> }