Skip to content

Commit

Permalink
fix: init all context advice if root proto miss
Browse files Browse the repository at this point in the history
  • Loading branch information
killagu committed Aug 14, 2023
1 parent 40d6627 commit 067869b
Show file tree
Hide file tree
Showing 7 changed files with 115 additions and 2 deletions.
2 changes: 1 addition & 1 deletion core/aop-runtime/src/LoadUnitAopHook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ export class LoadUnitAopHook implements LifecycleHook<LoadUnitLifecycleContext,
crosscutAdviceFactory: this.crosscutAdviceFactory,
});
const aspectList = builder.build();
AspectInfoUtil.setAspectList(aspectList, clazz);
for (const aspect of aspectList) {
AspectInfoUtil.setAspectList(aspectList, clazz);
for (const advice of aspect.adviceList) {
const adviceProto = PrototypeUtil.getClazzProto(advice.clazz);
if (!adviceProto) {
Expand Down
9 changes: 9 additions & 0 deletions plugin/aop/app.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Application } from 'egg';
import { CrosscutAdviceFactory } from '@eggjs/tegg/aop';
import { EggObjectAopHook, EggPrototypeCrossCutHook, LoadUnitAopHook } from '@eggjs/tegg-aop-runtime';
import { AopContextHook } from './lib/AopContextHook';

export default class AopAppHook {
private readonly app: Application;
Expand All @@ -9,6 +10,7 @@ export default class AopAppHook {
private readonly loadUnitAopHook: LoadUnitAopHook;
private readonly eggPrototypeCrossCutHook: EggPrototypeCrossCutHook;
private readonly eggObjectAopHook: EggObjectAopHook;
private aopContextHook: AopContextHook;

constructor(app) {
this.app = app;
Expand All @@ -24,9 +26,16 @@ export default class AopAppHook {
this.app.eggObjectLifecycleUtil.registerLifecycle(this.eggObjectAopHook);
}

async didLoad() {
await this.app.moduleHandler.ready();
this.aopContextHook = new AopContextHook(this.app.moduleHandler);
this.app.eggContextLifecycleUtil.registerLifecycle(this.aopContextHook);
}

beforeClose() {
this.app.eggPrototypeLifecycleUtil.deleteLifecycle(this.eggPrototypeCrossCutHook);
this.app.loadUnitLifecycleUtil.deleteLifecycle(this.loadUnitAopHook);
this.app.eggObjectLifecycleUtil.deleteLifecycle(this.eggObjectAopHook);
this.app.eggContextLifecycleUtil.deleteLifecycle(this.aopContextHook);
}
}
57 changes: 57 additions & 0 deletions plugin/aop/lib/AopContextHook.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { Application } from 'egg';
import { EggProtoImplClass, LifecycleHook, ObjectInitType, PrototypeUtil } from '@eggjs/tegg';
import { EggContext, EggContextLifecycleContext } from '@eggjs/tegg-runtime';
import { AspectInfoUtil } from '@eggjs/aop-decorator';
import { EggPrototype, TeggError } from '@eggjs/tegg-metadata';
import { ROOT_PROTO } from '@eggjs/egg-module-common';

export interface EggPrototypeWithClazz extends EggPrototype {
clazz?: EggProtoImplClass;
}

export interface ProtoToCreate {
name: string;
proto: EggPrototype;
}

export class AopContextHook implements LifecycleHook<EggContextLifecycleContext, EggContext> {
private readonly moduleHandler: Application['moduleHandler'];
private requestProtoList: Array<ProtoToCreate> = [];

constructor(moduleHandler: Application['moduleHandler']) {
this.moduleHandler = moduleHandler;
for (const loadUnitInstance of this.moduleHandler.loadUnitInstances) {
const iterator = loadUnitInstance.loadUnit.iterateEggPrototype();
for (const proto of iterator) {
const protoWithClazz = proto as EggPrototypeWithClazz;
const clazz = protoWithClazz.clazz;
if (!clazz) continue;
const aspects = AspectInfoUtil.getAspectList(clazz);
for (const aspect of aspects) {
for (const advice of aspect.adviceList) {
const adviceProto = PrototypeUtil.getClazzProto(advice.clazz) as EggPrototype | undefined;
if (!adviceProto) {
throw TeggError.create(`Aop Advice(${advice.clazz.name}) not found in loadUnits`, 'advice_not_found');

Check warning on line 34 in plugin/aop/lib/AopContextHook.ts

View check run for this annotation

Codecov / codecov/patch

plugin/aop/lib/AopContextHook.ts#L34

Added line #L34 was not covered by tests
}
if (adviceProto.initType === ObjectInitType.CONTEXT) {
this.requestProtoList.push({
name: advice.name,
proto: adviceProto,
});
}
}
}
}
}
}

async preCreate(_, ctx: EggContext): Promise<void> {
// compatible with egg controller
// add context aspect to ctx
if (!ctx.get(ROOT_PROTO)) {
for (const proto of this.requestProtoList) {
ctx.addProtoToCreate(proto.name, proto.proto);
}
}
}
}
10 changes: 10 additions & 0 deletions plugin/aop/test/aop.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,14 @@ describe('test/aop.test.ts', () => {
msg: 'withCrossAroundResult(withPointAroundResult(hello withPointAroundParam(withCrosscutAroundParam(foo))))',
});
});

it('module aop should work', async () => {
app.mockCsrf();
const res = await app.httpRequest()
.get('/singletonAop')
.expect(200);
assert.deepStrictEqual(res.body, {
msg: 'withContextPointAroundResult(hello withContextPointAroundParam(foo))',
});
});
});
7 changes: 7 additions & 0 deletions plugin/aop/test/fixtures/apps/aop-app/app/controller/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,11 @@ export default class App extends Controller {
this.ctx.status = 200;
this.ctx.body = { msg };
}

async contextAdviceWithSingleton() {
const hello: Hello = await (this.app.module as any).aopModule.singletonHello;
const msg = await hello.hello('foo');
this.ctx.status = 200;
this.ctx.body = { msg };
}
}
1 change: 1 addition & 0 deletions plugin/aop/test/fixtures/apps/aop-app/app/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ import { Application } from 'egg';

module.exports = (app: Application) => {
app.router.get('/aop', app.controller.app.aop);
app.router.get('/singletonAop', app.controller.app.contextAdviceWithSingleton);
};
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { AccessLevel, ContextProto, Inject } from '@eggjs/tegg';
import { AccessLevel, ContextProto, Inject, SingletonProto } from '@eggjs/tegg';
import { Advice, AdviceContext, Crosscut, IAdvice, Pointcut, PointcutType } from '@eggjs/tegg/aop';
import { EggLogger } from 'egg';

Expand Down Expand Up @@ -43,3 +43,32 @@ export class CrosscutAdvice implements IAdvice<Hello> {
return `withCrossAroundResult(${result})`;
}
}


@Advice()
export class ContextPointcutAdvice implements IAdvice<SingletonHello> {
async around(ctx: AdviceContext<Hello>, next: () => Promise<any>): Promise<any> {
ctx.args[0] = `withContextPointAroundParam(${ctx.args[0]})`;
const result = await next();
return `withContextPointAroundResult(${result})`;
}
}

@SingletonProto({
accessLevel: AccessLevel.PUBLIC,
})
export class SingletonHello {
id = 233;

@Inject()
logger: EggLogger;

@Pointcut(ContextPointcutAdvice)
async hello(name: string) {
return `hello ${name}`;
}

async helloEggObjectAop() {
this.logger.info('foo');
}
}

0 comments on commit 067869b

Please sign in to comment.