diff --git a/package-lock.json b/package-lock.json index fd2d5ff..8610fab 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3887,14 +3887,12 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -3909,20 +3907,17 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "core-util-is": { "version": "1.0.2", @@ -4051,7 +4046,6 @@ "version": "1.0.0", "bundled": true, "dev": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -4066,7 +4060,6 @@ "version": "3.0.4", "bundled": true, "dev": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -4074,14 +4067,12 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "minipass": { "version": "2.3.5", "bundled": true, "dev": true, - "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -4100,7 +4091,6 @@ "version": "0.5.1", "bundled": true, "dev": true, - "optional": true, "requires": { "minimist": "0.0.8" } @@ -4181,8 +4171,7 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "object-assign": { "version": "4.1.1", @@ -4194,7 +4183,6 @@ "version": "1.4.0", "bundled": true, "dev": true, - "optional": true, "requires": { "wrappy": "1" } @@ -4316,7 +4304,6 @@ "version": "1.0.2", "bundled": true, "dev": true, - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", diff --git a/src/model-config.ts b/src/model-config.ts new file mode 100644 index 0000000..cc90283 --- /dev/null +++ b/src/model-config.ts @@ -0,0 +1,4 @@ +export interface ModelConfiguration { + allowSoftDelete?: boolean; + softDeleteReturnEntities?: boolean; +} diff --git a/src/model-creator.ts b/src/model-creator.ts index 7c995aa..b79ac65 100644 --- a/src/model-creator.ts +++ b/src/model-creator.ts @@ -1,6 +1,7 @@ import { PolarisBaseContext, PolarisRequestHeaders } from '@enigmatis/utills'; import * as Joi from 'joi'; import { Model, model, models, Schema } from 'mongoose'; +import { ModelConfiguration } from './model-config'; import { getCollectionName } from './schema-helpers/middleware-functions'; import { addDocumentMiddleware, @@ -29,6 +30,7 @@ export declare type SchemaCreator = (refNameCreator: (name: string) => string) = export const getModelCreator = ( collectionPrefix: string, schemaOrCreator: Schema | SchemaCreator, + modelConfiguration?: ModelConfiguration, ): ModelCreator => { return ({ headers }: PolarisBaseContext): Model> => { const collectionName = getCollectionName(collectionPrefix, headers); @@ -36,7 +38,12 @@ export const getModelCreator = ( models[collectionName] || model>( collectionName, - createSchemaForModel(collectionPrefix, schemaOrCreator, headers), + createSchemaForModel( + collectionPrefix, + schemaOrCreator, + headers, + modelConfiguration, + ), ) ); }; @@ -49,6 +56,7 @@ const createSchemaForModel = ( collectionPrefix: string, schemaOrCreator: Schema | SchemaCreator, headers: PolarisRequestHeaders, + modelConfiguration?: ModelConfiguration, ) => { const schema = schemaOrCreator instanceof Function @@ -56,7 +64,7 @@ const createSchemaForModel = ( : schemaOrCreator.clone(); headers = checkHeaders(headers); addFields(schema); - addQueryMiddleware(schema, headers); + addQueryMiddleware(schema, headers, modelConfiguration); addDocumentMiddleware(schema, headers); addModelMiddleware(schema, headers); return schema; diff --git a/src/schema-helpers/middleware-functions.ts b/src/schema-helpers/middleware-functions.ts index 62ec964..3a38b1c 100644 --- a/src/schema-helpers/middleware-functions.ts +++ b/src/schema-helpers/middleware-functions.ts @@ -1,5 +1,6 @@ import { PolarisRequestHeaders } from '@enigmatis/utills'; import { Aggregate, HookNextFunction, Model } from 'mongoose'; +import { ModelConfiguration } from '../model-config'; import { RepositoryModel } from '../model-creator'; import { InnerModelType } from '../types'; import { deleted, notDeleted } from './constants'; @@ -33,9 +34,15 @@ export const getPreInsertMany = (headers: PolarisRequestHeaders) => { }; }; -export const getFindHandler = (headers: PolarisRequestHeaders) => { +export const getFindHandler = ( + headers: PolarisRequestHeaders, + modelConfig?: ModelConfiguration, +) => { return function findHandler(this: any) { const conditions = this._conditions; + if (modelConfig && modelConfig.softDeleteReturnEntities) { + conditions.deleted = true; + } const realityId = headers.realityId !== undefined && conditions.realityId === undefined && @@ -51,7 +58,7 @@ export const getFindHandler = (headers: PolarisRequestHeaders) => { }; }; -export function softRemoveFunc( +export function softRemove( this: Model, query: any, optionsOrCallback: any, @@ -72,13 +79,13 @@ export function softRemoveFunc( } } -export function singleSoftRemove( +export function softRemoveOne( this: Model, query: any, callback?: (err: any, raw: any) => void, ) { // using thisModule to be abale to mock softRemoveFunc in tests - return thisModule.softRemoveFunc.call(this, query, { single: true }, callback); + return thisModule.softRemove.call(this, query, { single: true }, callback); } export function findOneAndSoftDelete( diff --git a/src/schema-helpers/middleware-setters.ts b/src/schema-helpers/middleware-setters.ts index 70e62c0..a30843e 100644 --- a/src/schema-helpers/middleware-setters.ts +++ b/src/schema-helpers/middleware-setters.ts @@ -1,31 +1,41 @@ import { PolarisRequestHeaders } from '@enigmatis/utills'; -import { Aggregate, HookNextFunction, Model, Schema } from 'mongoose'; +import { Schema } from 'mongoose'; +import { ModelConfiguration } from '../model-config'; import { findOneAndSoftDelete, getFindHandler, getPreInsertMany, getPreSave, preAggregate, - singleSoftRemove, - softRemoveFunc, + softRemove, + softRemoveOne, } from './middleware-functions'; -export const addQueryMiddleware = (schema: Schema, headers: PolarisRequestHeaders) => { - const findHandlerFunc = getFindHandler(headers); +export const addQueryMiddleware = ( + schema: Schema, + headers: PolarisRequestHeaders, + modelConfiguration?: ModelConfiguration, +) => { + const findHandlerFunc = getFindHandler(headers, modelConfiguration); ['find', 'findOne', 'findOneAndUpdate', 'update', 'count', 'updateOne', 'updateMany'].forEach( middleware => { schema.pre(middleware, findHandlerFunc as any); }, ); schema.pre('aggregate', preAggregate); - schema.statics = { - ...schema.statics, - remove: softRemoveFunc, - deleteOne: singleSoftRemove, - deleteMany: softRemoveFunc, - findOneAndDelete: findOneAndSoftDelete, - findOneAndRemove: findOneAndSoftDelete, - }; + if ( + !modelConfiguration || + (modelConfiguration && modelConfiguration.allowSoftDelete !== false) + ) { + schema.statics = { + ...schema.statics, + remove: softRemove, + deleteOne: softRemoveOne, + deleteMany: softRemove, + findOneAndDelete: findOneAndSoftDelete, + findOneAndRemove: findOneAndSoftDelete, + }; + } }; export const addModelMiddleware = (schema: Schema, headers: PolarisRequestHeaders) => { diff --git a/src/schema-helpers/query-with-irrelevant-wrapper.ts b/src/schema-helpers/query-with-irrelevant-wrapper.ts index 0d638bf..fc8e730 100644 --- a/src/schema-helpers/query-with-irrelevant-wrapper.ts +++ b/src/schema-helpers/query-with-irrelevant-wrapper.ts @@ -1,6 +1,6 @@ -import {QueryIrrelevantResult} from '@enigmatis/utills'; -import {Model} from 'mongoose'; -import {InnerModelType} from '../types'; +import { QueryIrrelevantResult } from '@enigmatis/utills'; +import { Model } from 'mongoose'; +import { InnerModelType } from '../types'; export const QueryWithIrrelevant = async ( model: Model>, @@ -12,11 +12,11 @@ export const QueryWithIrrelevant = async ( } const irrelevant = await model.find( { - _id: {$nin: result.map(x => x._id)}, - dataVersion: {$gt: dataVersion}, - deleted: {$in: [true, false]} + _id: { $nin: result.map(x => x._id) }, + dataVersion: { $gt: dataVersion }, + deleted: { $in: [true, false] }, }, - {_id: true}, + { _id: true }, ); return new QueryIrrelevantResult(result, irrelevant.map(x => x._id)); diff --git a/test/model-creator.test.ts b/test/model-creator.test.ts index 79fc846..86c381e 100644 --- a/test/model-creator.test.ts +++ b/test/model-creator.test.ts @@ -1,5 +1,6 @@ import { PolarisBaseContext, PolarisRequestHeaders } from '@enigmatis/utills'; -import { Document, Schema } from 'mongoose'; +import { Schema } from 'mongoose'; +import { ModelConfiguration } from '../src/model-config'; import { getModelCreator } from '../src/model-creator'; import { deleted, notDeleted } from '../src/schema-helpers/constants'; import * as MiddlewareFunctions from '../src/schema-helpers/middleware-functions'; @@ -149,12 +150,30 @@ describe('module creator', () => { expect(preMiddlewareMap.get('aggregate').map((x: any) => x.fn)).toContain( MiddlewareFunctions.preAggregate, ); - expect(model.remove).toBe(MiddlewareFunctions.softRemoveFunc); - expect(model.deleteOne).toBe(MiddlewareFunctions.singleSoftRemove); - expect(model.deleteMany).toBe(MiddlewareFunctions.softRemoveFunc); + expect(model.remove).toBe(MiddlewareFunctions.softRemove); + expect(model.deleteOne).toBe(MiddlewareFunctions.softRemoveOne); + expect(model.deleteMany).toBe(MiddlewareFunctions.softRemove); expect(model.findOneAndDelete).toBe(MiddlewareFunctions.findOneAndSoftDelete); expect(model.findOneAndRemove).toBe(MiddlewareFunctions.findOneAndSoftDelete); }); + test('soft delete not allowed', () => { + const softDeleteNotAllowedModelConfig: ModelConfiguration = { + allowSoftDelete: false, + }; + const modelCreator2 = getModelCreator( + 'testing2', + personSchema, + softDeleteNotAllowedModelConfig, + ); + const model2 = modelCreator2(context); + expect(model2.schema.statics).not.toEqual({ + deleteMany: MiddlewareFunctions.softRemove, + deleteOne: MiddlewareFunctions.softRemoveOne, + findOneAndDelete: MiddlewareFunctions.findOneAndSoftDelete, + findOneAndRemove: MiddlewareFunctions.findOneAndSoftDelete, + remove: MiddlewareFunctions.softRemove, + }); + }); test("model middleware's added", () => { const preMiddlewareMap = (model as any).hooks._pres; @@ -172,6 +191,28 @@ describe('module creator', () => { }); describe("middleware's functions", () => { + test('findHandlerFunc - soft delete return entities true', () => { + const softDeleteReturnEntitiesModelConfig: ModelConfiguration = { + softDeleteReturnEntities: true, + }; + const modelCreator2 = getModelCreator( + 'testing2', + personSchema, + softDeleteReturnEntitiesModelConfig, + ); + const model2 = modelCreator2(context); + const where = jest.fn(); + const conditions = { name: 'Dazdraperma' }; + const headers = { realityId: testReality }; + const findHandler = MiddlewareFunctions.getFindHandler( + headers, + softDeleteReturnEntitiesModelConfig, + ); + findHandler.call({ where, _conditions: conditions }); + expect(where).toHaveBeenCalledTimes(1); + expect(where).toHaveBeenCalledWith(expect.not.objectContaining(notDeleted)); + }); + test('findHandlerFunc - add not deleted options to query', () => { const where = jest.fn(); const conditions = { name: 'Dazdraperma' }; @@ -280,7 +321,7 @@ describe('module creator', () => { expect(pipeArr[pipeArr.length - 1]).toEqual({ $match: notDeleted }); }); - test('softRemoveFunc - calling updateOne with right params when single', () => { + test('softRemove - calling updateOne with right params when single', () => { const scope: any = { updateOne: jest.fn(), updateMany: jest.fn(), @@ -289,12 +330,12 @@ describe('module creator', () => { name: 'Dazdraperma', }; const options = { single: true }; - MiddlewareFunctions.softRemoveFunc.call(scope, query, options); + MiddlewareFunctions.softRemove.call(scope, query, options); expect(scope.updateOne).toHaveBeenCalledTimes(1); expect(scope.updateOne).toHaveBeenLastCalledWith(query, deleted, options, undefined); }); - test('softRemoveFunc - calling updateMany with right params when not single', () => { + test('softRemove - calling updateMany with right params when not single', () => { const scope: any = { updateOne: jest.fn(), updateMany: jest.fn(), @@ -303,12 +344,12 @@ describe('module creator', () => { name: 'Dazdraperma', }; const options = { skip: 1 }; - MiddlewareFunctions.softRemoveFunc.call(scope, query, options); + MiddlewareFunctions.softRemove.call(scope, query, options); expect(scope.updateMany).toHaveBeenCalledTimes(1); expect(scope.updateMany).toHaveBeenLastCalledWith(query, deleted, options, undefined); }); - test('softRemoveFunc - passing callback when callback is second argument', () => { + test('softRemove - passing callback when callback is second argument', () => { const scope: any = { updateOne: jest.fn(), updateMany: jest.fn(), @@ -317,12 +358,12 @@ describe('module creator', () => { name: 'Dazdraperma', }; const callback = jest.fn(); - MiddlewareFunctions.softRemoveFunc.call(scope, query, callback); + MiddlewareFunctions.softRemove.call(scope, query, callback); expect(scope.updateMany).toHaveBeenCalledTimes(1); expect(scope.updateMany).toHaveBeenLastCalledWith(query, deleted, {}, callback); }); - test('softRemoveFunc - passing callback when callback is last argument', () => { + test('softRemove - passing callback when callback is last argument', () => { const scope: any = { updateOne: jest.fn(), updateMany: jest.fn(), @@ -332,13 +373,13 @@ describe('module creator', () => { }; const options = { skip: 1 }; const callback = jest.fn(); - MiddlewareFunctions.softRemoveFunc.call(scope, query, options, callback); + MiddlewareFunctions.softRemove.call(scope, query, options, callback); expect(scope.updateMany).toHaveBeenCalledTimes(1); expect(scope.updateMany).toHaveBeenLastCalledWith(query, deleted, options, callback); }); - test('singleSoftRemove - calling soft remove with right params and bind this', () => { - const softRemoveSpy = jest.spyOn(MiddlewareFunctions, 'softRemoveFunc'); + test('softRemoveOne - calling soft remove with right params and bind this', () => { + const softRemoveSpy = jest.spyOn(MiddlewareFunctions, 'softRemove'); const scope: any = { updateOne: jest.fn(), updateMany: jest.fn(), @@ -347,7 +388,7 @@ describe('module creator', () => { name: 'Dazdraperma', }; const callback = jest.fn(); - MiddlewareFunctions.singleSoftRemove.call(scope, query, callback); + MiddlewareFunctions.softRemoveOne.call(scope, query, callback); expect(softRemoveSpy).toHaveBeenCalledTimes(1); expect(softRemoveSpy).toHaveBeenLastCalledWith(query, { single: true }, callback); // checking updateOne called to know that this binded corretly @@ -355,7 +396,7 @@ describe('module creator', () => { }); test('findOneAndSoftDelete - calling findOneAndUpdate with right params when first arg is not a callback', () => { - const softRemoveSpy = jest.spyOn(MiddlewareFunctions, 'softRemoveFunc'); + const softRemoveSpy = jest.spyOn(MiddlewareFunctions, 'softRemove'); const scope: any = { findOneAndUpdate: jest.fn(), };