diff --git a/src/TsAspectContainer.ts b/src/TsAspectContainer.ts index 480caa1..9526281 100644 --- a/src/TsAspectContainer.ts +++ b/src/TsAspectContainer.ts @@ -1,11 +1,11 @@ import { Advice } from './advice.enum'; import { Aspect } from './aspect.interface'; -type AdviceAspectMap = Map; -type MethodContainer = { +type AdviceAspectMap = Map[]>; +type MethodContainer = { originalMethod: any; - adviceAspectMap: AdviceAspectMap; + adviceAspectMap: AdviceAspectMap; }; -type TsAspectContainer = Record; +type TsAspectContainer = Record>; export { TsAspectContainer, MethodContainer, AdviceAspectMap }; diff --git a/src/addAspect.ts b/src/addAspect.ts index 5a644b4..321fc78 100644 --- a/src/addAspect.ts +++ b/src/addAspect.ts @@ -4,7 +4,12 @@ import { Aspect } from './aspect.interface'; import { proxyFunc, asyncProxyFunc } from './proxyFunc'; import { setTsAspectProp, getTsAspectProp } from './TsAspectProperty'; -export function addAspect(target: any, methodName: string, advice: Advice, aspect: Aspect): void { +export function addAspect( + target: any, + methodName: string, + advice: Advice, + aspect: Aspect, +): void { let tsAspectProp = getTsAspectProp(target); if (!tsAspectProp) { tsAspectProp = {}; @@ -16,7 +21,7 @@ export function addAspect(target: any, methodName: string, advice: Advice, aspec tsAspectProp[methodName] = { originalMethod, - adviceAspectMap: new Map(), + adviceAspectMap: new Map[]>(), }; const wrapperFunc = function (...args: any): any { diff --git a/src/addAspectToPointcut.ts b/src/addAspectToPointcut.ts index e4b33d7..997de05 100644 --- a/src/addAspectToPointcut.ts +++ b/src/addAspectToPointcut.ts @@ -3,11 +3,11 @@ import { Advice } from './advice.enum'; import { Aspect } from './aspect.interface'; import { getPointcutMethods } from './getPointcutMethods'; -export function addAspectToPointcut( +export function addAspectToPointcut( target: any, pointcut: string, advice: Advice, - aspect: Aspect, + aspect: Aspect, ): void { const methods = getPointcutMethods(target, pointcut); methods.forEach(method => { diff --git a/src/aspect.interface.ts b/src/aspect.interface.ts index 40fb6c6..7ba645f 100644 --- a/src/aspect.interface.ts +++ b/src/aspect.interface.ts @@ -1,11 +1,11 @@ -export interface Aspect { - execute(ctx: AspectContext): any; +export interface Aspect { + execute(ctx: AspectContext): any; } -export type AspectContext = { +export type AspectContext = { target: any; methodName: string; - functionParams: any[]; - returnValue: any; + functionParams: Args; + returnValue: Data | null; error: any; }; diff --git a/src/decorator/UseAspect.ts b/src/decorator/UseAspect.ts index 897a867..80c0584 100644 --- a/src/decorator/UseAspect.ts +++ b/src/decorator/UseAspect.ts @@ -1,11 +1,16 @@ import { types } from 'util'; +import { getTsAspectProp, setTsAspectProp } from '../TsAspectProperty'; import { Advice } from '../advice.enum'; import { Aspect } from '../aspect.interface'; -import { proxyFunc, asyncProxyFunc } from '../proxyFunc'; -import { getTsAspectProp, setTsAspectProp } from '../TsAspectProperty'; +import { asyncProxyFunc, proxyFunc } from '../proxyFunc'; -export function UseAspect(advice: Advice, aspect: Aspect | (new () => Aspect)): MethodDecorator { - return function (target, propertyKey: string | symbol, descriptor: PropertyDescriptor) { +export function UseAspect any>( + advice: Advice, + aspect: + | Aspect>, Parameters> + | (new () => Aspect>, Parameters>), +) { + return (target: any, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor) => { let tsAspectProp = getTsAspectProp(target); if (!tsAspectProp) { tsAspectProp = {}; @@ -19,7 +24,7 @@ export function UseAspect(advice: Advice, aspect: Aspect | (new () => Aspect)): tsAspectProp[propertyKeyString] = { originalMethod, - adviceAspectMap: new Map(), + adviceAspectMap: new Map>, Parameters>[]>(), }; descriptor.value = function (...args: any): any { @@ -41,8 +46,8 @@ export function UseAspect(advice: Advice, aspect: Aspect | (new () => Aspect)): ); } } - return originalMethod(...args); - }; + return originalMethod?.(...args); + } as T; } const { adviceAspectMap } = tsAspectProp[propertyKeyString]; diff --git a/src/proxyFunc.ts b/src/proxyFunc.ts index 18aace9..c8a795c 100644 --- a/src/proxyFunc.ts +++ b/src/proxyFunc.ts @@ -2,14 +2,14 @@ import { Advice } from './advice.enum'; import { AspectContext } from './aspect.interface'; import { AdviceAspectMap, MethodContainer } from './TsAspectContainer'; -export async function asyncProxyFunc( +export async function asyncProxyFunc( target: any, methodName: string, - methodContainer: MethodContainer, + methodContainer: MethodContainer, ...args: any ): Promise { const { originalMethod, adviceAspectMap } = methodContainer; - const aspectCtx: AspectContext = { + const aspectCtx: AspectContext = { target: target, methodName: methodName, functionParams: args, @@ -43,14 +43,14 @@ export async function asyncProxyFunc( return aspectCtx.returnValue; } -export function proxyFunc( +export function proxyFunc( target: any, methodName: string, - methodContainer: MethodContainer, + methodContainer: MethodContainer, ...args: any ): any { const { originalMethod, adviceAspectMap } = methodContainer; - const aspectCtx: AspectContext = { + const aspectCtx: AspectContext = { target: target, methodName: methodName, functionParams: args, @@ -84,9 +84,9 @@ export function proxyFunc( return aspectCtx.returnValue; } -function applyPreExecutionAspects( - aspectCtx: AspectContext, - adviceAspectMap: AdviceAspectMap, +function applyPreExecutionAspects( + aspectCtx: AspectContext, + adviceAspectMap: AdviceAspectMap, ): void { if (adviceAspectMap.has(Advice.Before)) { adviceAspectMap.get(Advice.Before)?.forEach(aspect => { @@ -100,9 +100,9 @@ function applyPreExecutionAspects( } } -function applyPostExecutionAspects( - aspectCtx: AspectContext, - adviceAspectMap: AdviceAspectMap, +function applyPostExecutionAspects( + aspectCtx: AspectContext, + adviceAspectMap: AdviceAspectMap, ): void { if (adviceAspectMap.has(Advice.Around)) { adviceAspectMap.get(Advice.Around)?.forEach(aspect => { diff --git a/test/addAspect.test.ts b/test/addAspect.test.ts index e8d0956..dc7d944 100644 --- a/test/addAspect.test.ts +++ b/test/addAspect.test.ts @@ -7,7 +7,7 @@ import { CalculatorCls } from './samples/CalculatorCls.sample'; describe('addAspect', () => { let calculator: CalculatorCls; - const aspect = mock(); + const aspect = mock>(); beforeEach(() => { jest.clearAllMocks(); @@ -63,7 +63,7 @@ describe('addAspect', () => { calculator.add(1, 2); - const expectedCtx: AspectContext = { + const expectedCtx: AspectContext = { target: calculator, methodName: 'add', functionParams: [1, 2], @@ -81,7 +81,7 @@ describe('addAspect', () => { expect(aspect.execute).toHaveBeenCalledTimes(1); - const expectedCtx: AspectContext = { + const expectedCtx: AspectContext = { target: calculator, methodName: 'divide', functionParams: [1, 0], @@ -92,7 +92,7 @@ describe('addAspect', () => { }); it('should pass the returned value to the injected aspect for Advice.AfterReturn', () => { - aspect.execute.mockImplementationOnce((ctx: AspectContext) => { + aspect.execute.mockImplementationOnce((ctx: AspectContext) => { return ctx.returnValue; }); @@ -102,7 +102,7 @@ describe('addAspect', () => { expect(aspect.execute).toHaveBeenCalledTimes(1); - const expectedCtx: AspectContext = { + const expectedCtx: AspectContext = { target: calculator, methodName: 'add', functionParams: [1, 2], @@ -113,8 +113,11 @@ describe('addAspect', () => { }); it('should return the returned value manipulated by the injected aspect for Advice.AfterReturn', () => { - aspect.execute.mockImplementationOnce((ctx: AspectContext) => { + aspect.execute.mockImplementationOnce((ctx: AspectContext) => { const returnValue = ctx.returnValue; + if (returnValue === null) { + return null; + } return returnValue * 42; }); @@ -126,8 +129,8 @@ describe('addAspect', () => { }); it('should execute all injected aspects for the same advice and pointcut', () => { - const secondAspect = mock(); - const thirdAspect = mock(); + const secondAspect = mock>(); + const thirdAspect = mock>(); addAspect(calculator, 'add', Advice.Before, aspect); @@ -160,8 +163,8 @@ describe('addAspect', () => { } } - const aspect = mock(); - aspect.execute.mockImplementation((ctx: AspectContext) => { + const aspect = mock>(); + aspect.execute.mockImplementation((ctx: AspectContext) => { return 42; }); diff --git a/test/addAspectToPointcut.test.ts b/test/addAspectToPointcut.test.ts index 4682a74..1d9de39 100644 --- a/test/addAspectToPointcut.test.ts +++ b/test/addAspectToPointcut.test.ts @@ -7,7 +7,7 @@ import { CalculatorCls } from './samples/CalculatorCls.sample'; describe('addAspectToPointcut', () => { let calculator: CalculatorCls; - const aspect = mock(); + const aspect = mock>(); beforeEach(() => { jest.clearAllMocks(); diff --git a/test/decorator/UseAspect.test.ts b/test/decorator/UseAspect.test.ts index 8881541..67359c8 100644 --- a/test/decorator/UseAspect.test.ts +++ b/test/decorator/UseAspect.test.ts @@ -4,8 +4,17 @@ import { Advice } from '../../src/advice.enum'; import { Aspect, AspectContext } from '../../src/aspect.interface'; import { UseAspect } from '../../src/decorator/UseAspect'; -const beforeAspect = mock(); -const afterAspect = mock(); +const beforeAspect = mock>(); +const afterAspect = mock>(); + +const throwBeforeAspect = mock>(); +const throwAfterAspect = mock>(); + +class TestAspect implements Aspect { + execute(ctx: AspectContext) { + throw new Error('Method not implemented.'); + } +} class SampleClass { public constructor(private sampleId: number) {} @@ -22,8 +31,8 @@ class SampleClass { this.sampleId = sampleId; } - @UseAspect(Advice.Before, beforeAspect) - @UseAspect(Advice.After, afterAspect) + @UseAspect(Advice.Before, throwBeforeAspect) + @UseAspect(Advice.After, throwAfterAspect) public throwError(): void { throw new Error('this is expected!'); } @@ -43,7 +52,7 @@ describe('UseAspect', () => { expect(beforeAspect.execute).toHaveBeenCalledTimes(1); - const expectedCtx: AspectContext = { + const expectedCtx: AspectContext = { target: sample, methodName: 'getSampleId', functionParams: [], @@ -55,8 +64,8 @@ describe('UseAspect', () => { it('should instantiate a new object of the aspect class passed as parameter', () => { let executeHasBeenCalled = false; - class SomeAspect implements Aspect { - execute(ctx: AspectContext) { + class SomeAspect implements Aspect { + execute(ctx: AspectContext) { executeHasBeenCalled = true; } } @@ -79,13 +88,13 @@ describe('UseAspect', () => { it('should not execute the aspect annotated with Advice.After if an error is thrown in method', () => { expect(() => sample.throwError()).toThrow(Error); - expect(beforeAspect.execute).toHaveBeenCalledTimes(1); - expect(afterAspect.execute).toHaveBeenCalledTimes(0); + expect(throwBeforeAspect.execute).toHaveBeenCalledTimes(1); + expect(throwAfterAspect.execute).toHaveBeenCalledTimes(0); }); describe('for async functions', () => { - const aspect = mock(); - aspect.execute.mockImplementation((ctx: AspectContext) => { + const aspect = mock>(); + aspect.execute.mockImplementation((ctx: AspectContext) => { return 42; }); diff --git a/test/resetAllAspects.test.ts b/test/resetAllAspects.test.ts index 121b370..4b523ff 100644 --- a/test/resetAllAspects.test.ts +++ b/test/resetAllAspects.test.ts @@ -8,7 +8,7 @@ import { CalculatorCls } from './samples/CalculatorCls.sample'; describe('resetAllAspects', () => { let calculator: CalculatorCls; - const aspect = mock(); + const aspect = mock>(); beforeEach(() => { jest.clearAllMocks();