Skip to content

Commit

Permalink
feat: upgrade to mongoose 8
Browse files Browse the repository at this point in the history
  • Loading branch information
emiljanitzek committed Mar 4, 2024
1 parent af645d7 commit 8edad99
Show file tree
Hide file tree
Showing 28 changed files with 1,313 additions and 1,413 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ node_modules

# IDE
.idea
.vscode

# OSX
.DS_Store
Expand Down
205 changes: 97 additions & 108 deletions README.md

Large diffs are not rendered by default.

2,061 changes: 1,034 additions & 1,027 deletions package-lock.json

Large diffs are not rendered by default.

29 changes: 15 additions & 14 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"main": "dist/source/index.js",
"types": "dist/source/index.d.ts",
"scripts": {
"test": "npx ts-mocha --project tsconfig.json --transpile-only --recursive --reporter spec --require test/utils/hooks.ts ./test/*.ts",
"test": "npx ts-mocha --project test/tsconfig.json --transpile-only --recursive --reporter spec --require test/utils/hooks.ts --require chai/register-expect.js ./test/*.ts",
"test:dist": "npx mocha --recursive --reporter spec --require dist/test/utils/hooks.js ./dist/test/*",
"clean": "rm -r dist",
"build": "npx tsc -b source",
Expand All @@ -32,20 +32,21 @@
},
"homepage": "https://github.com/emiljanitzek/mongoose-delete-ts",
"peerDependencies": {
"mongoose": "6.x"
"mongoose": "8.x"
},
"devDependencies": {
"@tsconfig/node18": "~1.0.1",
"@types/chai": "~4.3.1",
"@types/mocha": "~9.1.1",
"@typescript-eslint/eslint-plugin": "~5.30.6",
"@typescript-eslint/parser": "~5.30.6",
"chai": "~4.3.6",
"eslint": "~8.19.0",
"mocha": "~10.0.0",
"mongoose": "~6.5.2",
"@tsconfig/node20": "~20.1.2",
"@types/chai": "~4.3.12",
"@types/mocha": "~10.0.6",
"@types/node": "^20.11.24",
"@typescript-eslint/eslint-plugin": "~7.1.0",
"@typescript-eslint/parser": "~7.1.0",
"chai": "~4.3.10",
"eslint": "~8.57.0",
"mocha": "~10.3.0",
"mongodb": "^6.3.0",
"mongoose": "~8.2.0",
"ts-mocha": "~10.0.0",
"typescript": "~4.7.4"
},
"dependencies": {}
"typescript": "~5.3.3"
}
}
28 changes: 7 additions & 21 deletions source/hooks.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Aggregate, PipelineStage, Query, Schema } from 'mongoose';
import DeletedDocument from './types/DeletedDocument';
import { Aggregate, PipelineStage, Query } from 'mongoose';
import DeletedSchema from './types/DeletedSchema';
import { Methods } from './types/DeleteOptions';
import { Methods } from './types/DeleteSchemaOptions';
import Deleted from './types/Deleted';

export default function(schema: DeletedSchema, methods?: Methods[] | boolean): void {
schema.pre('save', function() {
Expand All @@ -12,10 +12,10 @@ export default function(schema: DeletedSchema, methods?: Methods[] | boolean): v

const allMethods = getMethodsFromOptions(methods);
if (allMethods.length > 0) {
schema.pre(allMethods as any, async function <T extends DeletedDocument>(
schema.pre(allMethods as any, async function <T extends Deleted>(
this: Query<unknown, T>
) {
if (deletedIsNotAlreadyInQuery(this) && notIgnoreDeletedInOptions(this)) {
if (deletedIsNotAlreadyInQuery(this)) {
this.where({ deleted: false });
}
});
Expand All @@ -25,9 +25,7 @@ export default function(schema: DeletedSchema, methods?: Methods[] | boolean): v
schema.pre('aggregate', async function(
this: Aggregate<unknown>
) {
if (onlyDeletedInOptions(this)) {
this.pipeline().unshift({ $match: { deleted: true } });
} else if (deletedIsNotAlreadyInAggregation(this) && notWithDeletedInOptions(this)) {
if (deletedIsNotAlreadyInAggregation(this)) {
this.pipeline().unshift({ $match: { deleted: false } });
}
});
Expand All @@ -40,7 +38,7 @@ function getMethodsFromOptions(methods?: Methods[] | boolean): string[] {
} else if (Array.isArray(methods)) {
return methods;
}
return ['find', 'findOne', 'findOneAndUpdate', 'count', 'update', 'updateOne', 'updateMany', 'countDocuments'];
return ['find', 'findOne', 'findOneAndUpdate', 'update', 'updateOne', 'updateMany', 'countDocuments'];
}

function hasAggregateInOption(methods?: Methods[] | boolean): boolean {
Expand All @@ -56,10 +54,6 @@ function deletedIsNotAlreadyInQuery<T>(query: Query<unknown, T>): boolean {
return typeof query.getQuery().deleted === 'undefined';
}

function notIgnoreDeletedInOptions<T>(query: Query<unknown, T>): boolean {
return query.getOptions().ignoreDeleted !== true;
}

function deletedIsNotAlreadyInAggregation(aggregation: Aggregate<unknown>): boolean {
const matches = aggregation.pipeline().filter(isPipelineMatch);
return !matches.some((match: PipelineStage.Match) => Object.keys(match['$match']).includes('deleted'));
Expand All @@ -68,11 +62,3 @@ function deletedIsNotAlreadyInAggregation(aggregation: Aggregate<unknown>): bool
function isPipelineMatch(pipeline: PipelineStage): pipeline is PipelineStage.Match {
return Object.keys(pipeline).includes('$match');
}

function notWithDeletedInOptions(aggregation: Aggregate<unknown>): boolean {
return (aggregation as any).options?.withDeleted !== true;
}

function onlyDeletedInOptions(aggregation: Aggregate<unknown>): boolean {
return (aggregation as any).options?.onlyDeleted === true;
}
4 changes: 1 addition & 3 deletions source/index.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import { default as deletedPlugin } from './plugin';

export { default as DeletedDocument, DeletedAtDocument, DeletedByDocument } from './types/DeletedDocument';
export { default as DeletedModel, DeletedByModel } from './types/DeletedModel';
export { default as Deleted } from './types/Deleted';
export { default as DeletedAt } from './types/DeletedAt';
export { default as DeletedBy } from './types/DeletedBy';
export { default as DeletedSchema } from './types/DeletedSchema';
export { DeleteOptions } from './types/DeleteOptions';
export { DeleteSchemaOptions as DeleteOptions } from './types/DeleteSchemaOptions';

export { DeletedMethods, DeletedByMethods } from './methods';
export { DeletedStaticMethods, DeletedByStaticMethods } from './statics';
Expand Down
42 changes: 14 additions & 28 deletions source/methods.ts
Original file line number Diff line number Diff line change
@@ -1,50 +1,36 @@
import { Callback, SaveOptions } from 'mongoose';
import { SaveOptions } from 'mongoose';
import DeletedSchema from './types/DeletedSchema';
import deleteDocument from './utils/deleteDocument';
import DeletedFieldOptions from './types/DeletedFieldOptions';
import restoreDocument from './utils/restoreDocument';
import { DeleteOptions } from './types/DeleteOptions';
import { DeleteSchemaOptions } from './types/DeleteSchemaOptions';
import mergeOptions from './utils/mergeOptions';

type DeleteOptions = Omit<SaveOptions, 'timestamps'>;
export interface DeletedMethods {
restore(options?: SaveOptions): Promise<this>
restore(options?: SaveOptions, fn?: Callback<this>): void;
restore(fn?: Callback<this>): void;
delete(options?: DeleteOptions): Promise<this>
restore(options?: DeleteOptions): Promise<this>
}

export interface DeletedByMethods<TUser = any> {
deleteByUser(user: TUser, options?: SaveOptions): Promise<this>
deleteByUser(user: TUser, options?: SaveOptions, fn?: Callback<this>): void
deleteByUser(user: TUser, fn?: Callback<this>): void;
deleteByUser(user: TUser, options?: DeleteOptions): Promise<this>
}

export default function(
schema: DeletedSchema,
options: DeleteOptions,
schemaOptions: DeleteSchemaOptions,
deletedFieldOptions: DeletedFieldOptions
): void {
schema.methods.delete = function(...args: any[]) {
schema.methods.delete = function(options?: SaveOptions) {
this.set(deleteDocument(deletedFieldOptions));
return this.save(...mergeArguments(args, options));
return this.save(mergeOptions(options, schemaOptions));
};
schema.methods.deleteByUser = function<TUser>(user: TUser, ...args: any[]) {
schema.methods.deleteByUser = function<TUser>(user: TUser, options?: SaveOptions) {
this.set(deleteDocument(deletedFieldOptions, user));
return this.save(...mergeArguments(args, options));
return this.save(mergeOptions(options, schemaOptions));
};
schema.methods.restore = function(...args: any[]) {
schema.methods.restore = function(options?: SaveOptions) {
this.set(restoreDocument(deletedFieldOptions));
return this.save(...mergeArguments(args, options));
return this.save(mergeOptions(options, schemaOptions));
};
}

function mergeArguments(
args: any[],
options: DeleteOptions
): unknown[] {
const saveOptions = typeof args[0] === 'object' ? args[0] : {};
const callback = typeof args[0] === 'function' ? args[0] : args[1];
if (typeof options.validateBeforeDelete !== 'undefined') {
Object.assign(saveOptions, { validateBeforeSave: Boolean(options.validateBeforeDelete) });
}
Object.assign(saveOptions, { timestamps: false });
return [saveOptions, callback];
}
6 changes: 3 additions & 3 deletions source/plugin.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { Schema, SchemaTypeOptions } from 'mongoose';
import DeletedFieldOptions from './types/DeletedFieldOptions';
import DeletedSchema from './types/DeletedSchema';
import { DeletedField, DeleteOptions, Properties } from './types/DeleteOptions';
import { DeletedField, DeleteSchemaOptions, Properties } from './types/DeleteSchemaOptions';
import hooks from './hooks';
import methods from './methods';
import query from './query';
import statics from './statics';

export default function<TSchema extends DeletedSchema>(
schema: TSchema,
options: DeleteOptions = {}
options: DeleteSchemaOptions = {}
): void {
const typeKey = schema.get('typeKey') || 'type';
const deletedFieldNames: DeletedFieldOptions = {};
Expand Down Expand Up @@ -48,7 +48,7 @@ export default function<TSchema extends DeletedSchema>(
hooks(schema, options.overrideMethods);
query(schema);
methods(schema, options, deletedFieldNames);
statics(schema, deletedFieldNames);
statics(schema, options, deletedFieldNames);
}

function hasDeletedField(option: DeletedField<unknown> | undefined): option is DeletedField<unknown> {
Expand Down
10 changes: 3 additions & 7 deletions source/query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,11 @@ import DeletedSchema from './types/DeletedSchema';
export default function(
schema: DeletedSchema
): void {
schema.query.withDeleted = function() {
return this.setOptions({ ignoreDeleted: true });
schema.query.allDocuments = function() {
return this.where({ deleted: { $in: [true, false] } });
};

schema.query.notDeleted = function() {
return this.where({ deleted: false });
};

schema.query.onlyDeleted = function() {
schema.query.deletedDocuments = function() {
return this.where({ deleted: true });
};
}
95 changes: 48 additions & 47 deletions source/statics.ts
Original file line number Diff line number Diff line change
@@ -1,94 +1,95 @@
import {
Callback,
FilterQuery,
Model,
MongooseQueryOptions,
QueryOptions,
QueryWithHelpers,
UpdateWriteOpResult
} from 'mongoose';
import DeletedSchema from './types/DeletedSchema';
import getOverloadedArguments from './utils/getOverloadedArguments';
import { staticDelete } from './utils/deleteDocument';
import DeletedFieldOptions from './types/DeletedFieldOptions';
import { staticRestore } from './utils/restoreDocument';
import { DeleteResult } from 'mongodb';
import {
DeleteResult,
DeleteOptions as MongoDbDeleteOptions,
UpdateOptions as MongoDbUpdateOptions
} from 'mongodb';
import { DeleteSchemaOptions } from './types/DeleteSchemaOptions';
import mergeOptions from './utils/mergeOptions';

export interface DeletedStaticMethods<T, TQueryHelpers={}> {
restoreOne(filter?: FilterQuery<T>, options?: QueryOptions | null, callbackArg?: Callback<T>): QueryWithHelpers<UpdateWriteOpResult, T, TQueryHelpers>;
restoreOne(filter?: FilterQuery<T>, callback?: Callback): QueryWithHelpers<UpdateWriteOpResult, T, TQueryHelpers>;
restoreOne(callback?: Callback): QueryWithHelpers<UpdateWriteOpResult, T, TQueryHelpers>;
type DeleteOptions<RawDocType> = MongoDbDeleteOptions & Omit<MongooseQueryOptions<RawDocType>, 'lean' | 'timestamps'>;
type UpdateOptions<RawDocType> = MongoDbUpdateOptions & Omit<MongooseQueryOptions<RawDocType>, 'lean'>;

restoreMany(filter?: FilterQuery<T>, options?: QueryOptions | null, callback?: Callback): QueryWithHelpers<UpdateWriteOpResult, T, TQueryHelpers>;
restoreMany(filter?: FilterQuery<T>, callback?: Callback): QueryWithHelpers<UpdateWriteOpResult, T, TQueryHelpers>;
restoreMany(callback?: Callback): QueryWithHelpers<UpdateWriteOpResult, T, TQueryHelpers>;
export interface DeletedStaticMethods<DocType, THelpers = {}, RawDocType = DocType> {
restoreOne(
filter?: FilterQuery<RawDocType>,
options?: UpdateOptions<RawDocType> | null
): QueryWithHelpers<UpdateWriteOpResult, DocType, THelpers, RawDocType, 'updateOne'>;

restoreMany(
filter?: FilterQuery<RawDocType>,
options?: UpdateOptions<RawDocType> | null
): QueryWithHelpers<UpdateWriteOpResult, DocType, THelpers, RawDocType, 'updateMany'>;
}

export interface DeletedByStaticMethods<T, TUser = any, TQueryHelpers={}> {
deleteManyByUser(user: TUser, filter?: FilterQuery<T>, options?: QueryOptions, callback?: Callback): QueryWithHelpers<DeleteResult, T, TQueryHelpers>;
deleteManyByUser(user: TUser, filter: FilterQuery<T>, callback: Callback): QueryWithHelpers<DeleteResult, T, TQueryHelpers>;
deleteManyByUser(user: TUser, callback: Callback): QueryWithHelpers<DeleteResult, T, TQueryHelpers>;
export interface DeletedByStaticMethods<DocType, TUser = any, THelpers = {}, RawDocType = DocType> {
deleteOneByUser(
user: TUser,
filter?: FilterQuery<RawDocType>,
options?: DeleteOptions<RawDocType> | null
): QueryWithHelpers<DeleteResult, DocType, THelpers, RawDocType, 'deleteOne'>;

deleteOneByUser(user: TUser, filter?: FilterQuery<T>, options?: QueryOptions, callback?: Callback): QueryWithHelpers<DeleteResult, T, TQueryHelpers>;
deleteOneByUser(user: TUser, filter: FilterQuery<T>, callback: Callback): QueryWithHelpers<DeleteResult, T, TQueryHelpers>;
deleteOneByUser(user: TUser, callback: Callback): QueryWithHelpers<DeleteResult, T, TQueryHelpers>;
deleteManyByUser(
user: TUser,
filter?: FilterQuery<RawDocType>,
options?: DeleteOptions<RawDocType> | null
): QueryWithHelpers<DeleteResult, DocType, THelpers, RawDocType, 'deleteMany'>;
}

export default function(
schema: DeletedSchema,
schemaOptions: DeleteSchemaOptions,
deletedFieldOptions: DeletedFieldOptions
): void {
schema.statics.deleteOne = async function<T>(filterArg?: FilterQuery<T>, optionsArg?: QueryOptions | null, callbackArg?: Callback) {
const [filter, options, callback] = getOverloadedArguments(filterArg, optionsArg, callbackArg);

schema.statics.deleteOne = async function<T>(this: Model<any>, filter?: FilterQuery<T>, options?: DeleteOptions<T> | null) {
const update = staticDelete(deletedFieldOptions);
const result = await Model.updateOne.apply(this, [filter, update, options, callback]);
const result = await this.updateOne.call(this, filter, update, mergeOptions(options, schemaOptions));
return convertToDeleteResult(result);
};
schema.statics.deleteMany = async function<T>(filterArg?: FilterQuery<T>, optionsArg?: QueryOptions | null, callbackArg?: Callback) {
const [filter, options, callback] = getOverloadedArguments(filterArg, optionsArg, callbackArg);

schema.statics.deleteMany = async function<T>(this: Model<any>, filter?: FilterQuery<T>, options?: DeleteOptions<T> | null) {
const update = staticDelete(deletedFieldOptions);
const result = await Model.updateMany.apply(this, [filter, update, options, callback]);
const result = await this.updateMany.call(this, filter, update, mergeOptions(options, schemaOptions));
return convertToDeleteResult(result);
};

schema.statics.deleteOneByUser = async function<TUser, T>(user: TUser, filterArg?: FilterQuery<T>, optionsArg?: QueryOptions | null, callbackArg?: Callback) {
const [filter, options, callback] = getOverloadedArguments(filterArg, optionsArg, callbackArg);

schema.statics.deleteOneByUser = async function<TUser, T>(this: Model<any>, user: TUser, filter?: FilterQuery<T>, options?: DeleteOptions<T> | null) {
const update = staticDelete(deletedFieldOptions, user);
const result = await Model.updateOne.apply(this, [filter, update, options, callback]);
const result = await this.updateOne.call(this, filter, update, mergeOptions(options, schemaOptions));
return convertToDeleteResult(result);
};
schema.statics.deleteManyByUser = async function<TUser, T>(user: TUser, filterArg?: FilterQuery<T>, optionsArg?: QueryOptions | null, callbackArg?: Callback) {
const [filter, options, callback] = getOverloadedArguments(filterArg, optionsArg, callbackArg);

schema.statics.deleteManyByUser = async function<TUser, T>(this: Model<any>, user: TUser, filter?: FilterQuery<T>, options?: DeleteOptions<T> | null) {
const update = staticDelete(deletedFieldOptions, user);
const result = await Model.updateMany.apply(this, [filter, update, options, callback]);
const result = await this.updateMany.call(this, filter, update, mergeOptions(options, schemaOptions));
return convertToDeleteResult(result);
};

schema.statics.findOneAndDelete = function<T>(filter?: FilterQuery<T>, options?: QueryOptions | null, callback?: Callback) {
schema.statics.findOneAndDelete = function<T>(this: Model<any>, filter?: FilterQuery<T>, options?: QueryOptions | null) {
const update = staticDelete(deletedFieldOptions);
return Model.findOneAndUpdate.apply(this, [filter, update, options, callback]);
return this.findOneAndUpdate.call(this, filter, update, mergeOptions(options, schemaOptions));
};
schema.statics.findByIdAndDelete = function(id: any, options?: QueryOptions | null, callback?: Callback) {
schema.statics.findByIdAndDelete = function(id: any, options?: QueryOptions | null) {
const update = staticDelete(deletedFieldOptions);
return Model.findByIdAndUpdate.apply(this, [id, update, options, callback] as any);
return this.findByIdAndUpdate.call(this, id, update, mergeOptions(options, schemaOptions));
};

schema.statics.restoreOne = function<T>(filterArg?: FilterQuery<T>, optionsArg?: QueryOptions | null, callbackArg?: Callback) {
const [filter, options, callback] = getOverloadedArguments(filterArg, optionsArg, callbackArg);
Object.assign(options, { ignoreDeleted: true });

schema.statics.restoreOne = function<T>(this: Model<any>, filter?: FilterQuery<T>, options?: UpdateOptions<T> | null) {
const update = staticRestore(deletedFieldOptions);
return Model.updateOne.apply(this, [filter, update, options, callback]) as any;
return this.updateOne.call(this, { ...filter, deleted: true }, update, mergeOptions(options, schemaOptions));
};
schema.statics.restoreMany = function<T>(filterArg?: FilterQuery<T>, optionsArg?: QueryOptions | null, callbackArg?: Callback) {
const [filter, options, callback] = getOverloadedArguments(filterArg, optionsArg, callbackArg);
Object.assign(options, { ignoreDeleted: true });

schema.statics.restoreMany = function<T>(this: Model<any>, filter?: FilterQuery<T>, options?: UpdateOptions<T> | null) {
const update = staticRestore(deletedFieldOptions);
return Model.updateMany.apply(this, [filter, update, options, callback]) as any;
return this.updateMany.call(this, { ...filter, deleted: true }, update, mergeOptions(options, schemaOptions));
};
}

Expand Down

0 comments on commit 8edad99

Please sign in to comment.