diff --git a/packages/backend/src/models/base.ts b/packages/backend/src/models/base.ts index 95c40746..6bfdc60d 100644 --- a/packages/backend/src/models/base.ts +++ b/packages/backend/src/models/base.ts @@ -2,9 +2,15 @@ import { AjvValidator, Model, snakeCaseMappers } from 'objection'; import type { QueryContext, ModelOptions, ColumnNameMappers } from 'objection'; import addFormats from 'ajv-formats'; +import ExtendedQueryBuilder from './query-builder'; + class Base extends Model { createdAt!: string; updatedAt!: string; + deletedAt: string; + + QueryBuilderType!: ExtendedQueryBuilder; + static QueryBuilder = ExtendedQueryBuilder; static get columnNameMappers(): ColumnNameMappers { return snakeCaseMappers(); @@ -30,10 +36,10 @@ class Base extends Model { this.updatedAt = new Date().toISOString(); } - async $beforeUpdate(opt: ModelOptions, queryContext: QueryContext): Promise { + async $beforeUpdate(opts: ModelOptions, queryContext: QueryContext): Promise { this.updatedAt = new Date().toISOString(); - await super.$beforeUpdate(opt, queryContext); + await super.$beforeUpdate(opts, queryContext); } } diff --git a/packages/backend/src/models/query-builder.ts b/packages/backend/src/models/query-builder.ts new file mode 100644 index 00000000..9e89f4bf --- /dev/null +++ b/packages/backend/src/models/query-builder.ts @@ -0,0 +1,43 @@ +import { Model, Page, PartialModelObject, ForClassMethod, AnyQueryBuilder } from "objection"; + +const DELETED_COLUMN_NAME = 'deleted_at'; + +const buildQueryBuidlerForClass = (): ForClassMethod => { + return (modelClass) => { + const qb: AnyQueryBuilder = Model.QueryBuilder.forClass.call(ExtendedQueryBuilder, modelClass); + qb.onBuild((builder) => { + if (!builder.context().withSoftDeleted) { + builder.whereNull(`${qb.modelClass().tableName}.${DELETED_COLUMN_NAME}`); + } + }); + return qb; + }; +}; + +class ExtendedQueryBuilder extends Model.QueryBuilder { + ArrayQueryBuilderType!: ExtendedQueryBuilder; + SingleQueryBuilderType!: ExtendedQueryBuilder; + MaybeSingleQueryBuilderType!: ExtendedQueryBuilder; + NumberQueryBuilderType!: ExtendedQueryBuilder; + PageQueryBuilderType!: ExtendedQueryBuilder>; + + static forClass: ForClassMethod = buildQueryBuidlerForClass(); + + delete() { + return this.patch({ + [DELETED_COLUMN_NAME]: (new Date()).toISOString(), + } as unknown as PartialModelObject); + } + + hardDelete() { + return super.delete(); + } + + restore() { + return this.patch({ + [DELETED_COLUMN_NAME]: null, + } as unknown as PartialModelObject); + } +} + +export default ExtendedQueryBuilder;