feat: soft delete database entities
This commit is contained in:
@@ -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<this>;
|
||||
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<void> {
|
||||
async $beforeUpdate(opts: ModelOptions, queryContext: QueryContext): Promise<void> {
|
||||
this.updatedAt = new Date().toISOString();
|
||||
|
||||
await super.$beforeUpdate(opt, queryContext);
|
||||
await super.$beforeUpdate(opts, queryContext);
|
||||
}
|
||||
}
|
||||
|
||||
|
43
packages/backend/src/models/query-builder.ts
Normal file
43
packages/backend/src/models/query-builder.ts
Normal file
@@ -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<M extends Model, R = M[]> extends Model.QueryBuilder<M, R> {
|
||||
ArrayQueryBuilderType!: ExtendedQueryBuilder<M, M[]>;
|
||||
SingleQueryBuilderType!: ExtendedQueryBuilder<M, M>;
|
||||
MaybeSingleQueryBuilderType!: ExtendedQueryBuilder<M, M | undefined>;
|
||||
NumberQueryBuilderType!: ExtendedQueryBuilder<M, number>;
|
||||
PageQueryBuilderType!: ExtendedQueryBuilder<M, Page<M>>;
|
||||
|
||||
static forClass: ForClassMethod = buildQueryBuidlerForClass();
|
||||
|
||||
delete() {
|
||||
return this.patch({
|
||||
[DELETED_COLUMN_NAME]: (new Date()).toISOString(),
|
||||
} as unknown as PartialModelObject<M>);
|
||||
}
|
||||
|
||||
hardDelete() {
|
||||
return super.delete();
|
||||
}
|
||||
|
||||
restore() {
|
||||
return this.patch({
|
||||
[DELETED_COLUMN_NAME]: null,
|
||||
} as unknown as PartialModelObject<M>);
|
||||
}
|
||||
}
|
||||
|
||||
export default ExtendedQueryBuilder;
|
Reference in New Issue
Block a user