import { buildTableHelper } from '@/modules/core/dbSchema' import { GetObjectPreviewInfo, GetPaginatedObjectPreviewsPage, GetPaginatedObjectPreviewsTotalCount, GetPreviewImage, PaginatedObjectPreviewsParams, StoreObjectPreview, StorePreview, UpdateObjectPreview } from '@/modules/previews/domain/operations' import { ObjectPreview as ObjectPreviewRecord, Preview } from '@/modules/previews/domain/types' import type { Knex } from 'knex' import { SetOptional } from 'type-fest' import { PreviewStatus } from '@/modules/previews/domain/consts' import { compositeCursorTools } from '@/modules/shared/helpers/dbHelper' import { omit } from 'lodash-es' const ObjectPreview = buildTableHelper('object_preview', [ 'streamId', 'objectId', 'previewStatus', 'priority', 'lastUpdate', 'preview', 'attempts' ]) const Previews = buildTableHelper('previews', ['id', 'data']) const tables = { objectPreview: (db: Knex) => db(ObjectPreview.name), previews: (db: Knex) => db(Previews.name) } export const getObjectPreviewInfoFactory = (deps: { db: Knex }): GetObjectPreviewInfo => async ({ streamId, objectId }: { streamId: string; objectId: string }) => { return await tables .objectPreview(deps.db) .select('*') .where({ streamId, objectId }) .first() } export const getPaginatedObjectsPreviewsBaseQueryFactory = (deps: { db: Knex }) => (params: Omit) => { const query = tables.objectPreview(deps.db).select('*') if (params.filter?.status) { query.where('previewStatus', params.filter.status) } if (params.filter?.maxNumberOfAttempts) { query.where('attempts', '<', params.filter.maxNumberOfAttempts) } return query } const getCursorTools = () => compositeCursorTools({ schema: ObjectPreview, cols: ['lastUpdate', 'objectId'] }) export const getPaginatedObjectPreviewsPageFactory = (deps: { db: Knex }): GetPaginatedObjectPreviewsPage => async (params) => { const { limit, cursor } = params const { applyCursorSortAndFilter, resolveNewCursor } = getCursorTools() const query = getPaginatedObjectsPreviewsBaseQueryFactory(deps)(params) if (cursor) { applyCursorSortAndFilter({ query, cursor, sort: 'desc' }) } query.limit(limit) const items = await query const newCursor = resolveNewCursor(items) return { items, cursor: newCursor } } export const getPaginatedObjectPreviewsTotalCountFactory = (deps: { db: Knex }): GetPaginatedObjectPreviewsTotalCount => async (params) => { const baseQ = getPaginatedObjectsPreviewsBaseQueryFactory(deps)(params) const q = deps.db.count<{ count: string }[]>().from(baseQ.as('sq1')) const [row] = await q return parseInt(row.count || '0') } /** * @throws {Error} if the preview already exists */ export const storeObjectPreviewFactory = ({ db }: { db: Knex }): StoreObjectPreview => async ({ streamId, objectId, priority }: Pick) => { const insertionObject: SetOptional< ObjectPreviewRecord, 'lastUpdate' | 'preview' | 'attempts' > = { streamId, objectId, priority, previewStatus: PreviewStatus.PENDING } const sqlQuery = tables.objectPreview(db).insert(insertionObject) await sqlQuery } export const storePreviewFactory = ({ db }: { db: Knex }): StorePreview => async ({ preview }) => { await tables.previews(db).insert(preview).onConflict().ignore() } export const updateObjectPreviewFactory = ({ db }: { db: Knex }): UpdateObjectPreview => async ({ objectPreview }) => { const q = tables .objectPreview(db) .where({ streamId: objectPreview.streamId, objectId: objectPreview.objectId }) .update({ ...omit(objectPreview, 'streamId', 'objectId', 'incrementAttempts'), lastUpdate: db.fn.now(), // always update the lastUpdate field ...(objectPreview.incrementAttempts ? { attempts: db.raw('attempts + 1') } : {}) }) .returning('*') return await q } export const getPreviewImageFactory = (deps: { db: Knex }): GetPreviewImage => async ({ previewId }: { previewId: string }) => { const previewRow = await tables .previews(deps.db) .where({ id: previewId }) .first() .select('*') if (!previewRow) { return null } return previewRow.data }