diff --git a/.gitignore b/.gitignore index 10478d7..674e6e2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,2 @@ node_modules -tests/test.db -dist \ No newline at end of file +tests/test.db \ No newline at end of file diff --git a/README.md b/README.md index 527e8da..937d0b6 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,14 @@ const softDelete = objectionSoftDelete({ columnName: 'deleted_at', deletedValue: new Date(), notDeletedValue: null, -}); + // You can optionally specify custom rules for whereDeleted or whereNotDeleted + whereNotDeleted: () => { + return Model.query().where('deleted_at', '>', moment().toISOString()); + }, + whereDeleted: () => { + return Model.query().whereNot('deleted_at', '>', moment().toISOString()); + }, +})(Model); // Inject the plugin to the model class User extends softDelete(Model) { diff --git a/dist/softDelete.js b/dist/softDelete.js new file mode 100644 index 0000000..2f50b6e --- /dev/null +++ b/dist/softDelete.js @@ -0,0 +1,93 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +const softDelete = incomingOptions => { + const options = { + columnName: 'deleted_at', + deletedValue: () => new Date(), + notDeletedValue: () => null, + ...incomingOptions + }; + return Model => { + class SDQueryBuilder extends Model.QueryBuilder { + // override the normal delete function with one that patches the row's "deleted" column + delete() { + this.context({ + softDelete: true + }); + const patch = {}; + patch[options.columnName] = options.deletedValue(); + return this.patch(patch); + } + + // provide a way to actually delete the row if necessary + hardDelete() { + return super.delete(); + } + + // provide a way to undo the delete + undelete() { + this.context({ + undelete: true + }); + const patch = {}; + patch[options.columnName] = options.notDeletedValue(); + return this.patch(patch); + } + + // provide a way to filter to ONLY deleted records without having to remember the column name + whereDeleted() { + const columnRef = this.modelClass().ref(options.columnName); + // this if is for backwards compatibility, to protect those that used a nullable `deleted` field + if (options.deletedValue === true) { + return this.where(columnRef, options.deletedValue()); + } + + // use custom whereDeleted function if specified + if (typeof options.whereDeleted === 'function') { + return options.whereDeleted(this, columnRef); + } else { + // qualify the column name + return this.whereNot(columnRef, options.notDeletedValue()); + } + } + + // provide a way to filter out deleted records without having to remember the column name + whereNotDeleted() { + const columnRef = this.modelClass().ref(options.columnName); + + // use custom whereDeleted function if specified + if (typeof options.whereNotDeleted === 'function') { + return options.whereNotDeleted(this, columnRef); + } else { + // qualify the column name + return this.where(columnRef, options.notDeletedValue()); + } + } + } + return class extends Model { + static get QueryBuilder() { + return SDQueryBuilder; + } + static get modifiers() { + return { + ...super.modifiers, + notDeleted(builder) { + builder.whereNotDeleted(); + }, + deleted(builder) { + builder.whereDeleted(); + } + }; + } + static get isSoftDelete() { + return true; + } + }; + }; +}; +var _default = softDelete; +exports.default = _default; \ No newline at end of file diff --git a/index.d.ts b/index.d.ts index de985bc..aa5c921 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1,35 +1,37 @@ -declare module 'objection-js-soft-delete' { - import { Model, QueryBuilder } from 'objection'; - + declare module 'objection-js-soft-delete' { + import { Model, QueryBuilder, ReferenceBuilder, ReferenceFunction } from 'objection'; + class SoftDeleteQueryBuilder extends QueryBuilder { ArrayQueryBuilderType: SoftDeleteQueryBuilder; - + NumberQueryBuilderType: SoftDeleteQueryBuilder; - + delete(): this['NumberQueryBuilderType']; hardDelete(): this['NumberQueryBuilderType']; undelete(): this['NumberQueryBuilderType']; whereDeleted(): this['ArrayQueryBuilderType']; whereNotDeleted(): this['ArrayQueryBuilderType']; } - + interface SoftDeleteInstance { //@ts-ignore QueryBuilderType: SoftDeleteQueryBuilder; } - + interface SoftDeleteStatic { QueryBuilder: typeof SoftDeleteQueryBuilder; isSoftDelete: boolean; namedFilters(): object; new (): SoftDeleteInstance & T['prototype']; } - + export default function softDelete( options?: Partial<{ columnName: string; deletedValue: () => Date | boolean | number; notDeletedValue: () => boolean | null; + whereDeleted: (qb: QueryBuilder>, columnRef: ReferenceBuilder ) => QueryBuilder>; + whereNotDeleted: (qb: QueryBuilder>, columnRef: ReferenceBuilder ) => QueryBuilder>; }>, ): (model: T) => Omit & SoftDeleteStatic; } \ No newline at end of file diff --git a/package.json b/package.json index 481b096..9e9e6f3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "objection-js-soft-delete", - "version": "4.0.0-beta.0", + "version": "4.0.3-beta.0", "description": "A plugin for objection js that supports soft delete", "main": "dist/softDelete.js", "types": "./index.d.ts", diff --git a/src/softDelete.js b/src/softDelete.js index 635dda2..7a70012 100644 --- a/src/softDelete.js +++ b/src/softDelete.js @@ -5,7 +5,6 @@ const softDelete = (incomingOptions) => { notDeletedValue: () => null, ...incomingOptions, }; - return (Model) => { class SDQueryBuilder extends Model.QueryBuilder { // override the normal delete function with one that patches the row's "deleted" column @@ -40,15 +39,27 @@ const softDelete = (incomingOptions) => { if (options.deletedValue === true) { return this.where(columnRef, options.deletedValue()); } - // qualify the column name - return this.whereNot(columnRef, options.notDeletedValue()); + + // use custom whereDeleted function if specified + if (typeof options.whereDeleted === 'function') { + return options.whereDeleted(this, columnRef); + } else { + // qualify the column name + return this.whereNot(columnRef, options.notDeletedValue()); + } } // provide a way to filter out deleted records without having to remember the column name whereNotDeleted() { const columnRef = this.modelClass().ref(options.columnName); - // qualify the column name - return this.where(columnRef, options.notDeletedValue()); + + // use custom whereDeleted function if specified + if (typeof options.whereNotDeleted === 'function') { + return options.whereNotDeleted(this, columnRef); + } else { + // qualify the column name + return this.where(columnRef, options.notDeletedValue()); + } } } return class extends Model { @@ -66,7 +77,6 @@ const softDelete = (incomingOptions) => { }, }; } - static get isSoftDelete() { return true; }