From d5a8a93abad570f69881f9fa42f39d7b5cd436be Mon Sep 17 00:00:00 2001 From: killa Date: Mon, 14 Oct 2024 21:28:31 +0800 Subject: [PATCH] fix: fix merge qualifier (#250) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ##### Checklist - [ ] `npm test` passes - [ ] tests and/or benchmarks are included - [ ] documentation is changed or added - [ ] commit message follows commit guidelines ##### Affected core subsystem(s) ##### Description of change --- core/aop-runtime/src/PointCutGraphHook.ts | 8 ++-- core/core-decorator/src/util/QualifierUtil.ts | 17 ++++++++ .../src/EggObjectFactoryPrototype.ts | 8 ++-- core/metadata/src/impl/EggPrototypeBuilder.ts | 40 +++++++++---------- .../src/model/ProtoDescriptorHelper.ts | 16 ++++---- core/metadata/src/model/graph/GlobalGraph.ts | 19 ++++----- core/runtime/src/impl/EggObjectUtil.ts | 6 ++- plugin/dal/lib/TransactionalAOP.ts | 5 ++- plugin/tegg/lib/EggCompatibleProtoImpl.ts | 8 ++-- .../tegg/test/ConstructorModuleConfig.test.ts | 6 +++ .../modules/module-with-config/foo.ts | 10 ++++- 11 files changed, 90 insertions(+), 53 deletions(-) diff --git a/core/aop-runtime/src/PointCutGraphHook.ts b/core/aop-runtime/src/PointCutGraphHook.ts index 7c229e8b..c79986a5 100644 --- a/core/aop-runtime/src/PointCutGraphHook.ts +++ b/core/aop-runtime/src/PointCutGraphHook.ts @@ -40,10 +40,10 @@ function findPointCutAdvice(globalGraph: GlobalGraph, protoNode: GraphNode = {}; + for (const qualifierList of qualifiers) { + for (const { attribute, value } of qualifierList) { + temp[attribute] = value; + } + } + for (const key of Reflect.ownKeys(temp)) { + result.push({ + attribute: key, + value: temp[key], + }); + } + return result; + } } diff --git a/core/dynamic-inject-runtime/src/EggObjectFactoryPrototype.ts b/core/dynamic-inject-runtime/src/EggObjectFactoryPrototype.ts index d80ccee4..19ffd7a5 100644 --- a/core/dynamic-inject-runtime/src/EggObjectFactoryPrototype.ts +++ b/core/dynamic-inject-runtime/src/EggObjectFactoryPrototype.ts @@ -36,10 +36,10 @@ export class EggObjectFactoryPrototype implements EggPrototype { constructor(clazz: EggProtoImplClass, loadUnit: LoadUnit, prototypeInfo: EggPrototypeInfo) { this.clazz = clazz; - this.qualifiers = [ - ...QualifierUtil.getProtoQualifiers(clazz), - ...(prototypeInfo.qualifiers ?? []), - ]; + this.qualifiers = QualifierUtil.mergeQualifiers( + QualifierUtil.getProtoQualifiers(clazz), + (prototypeInfo.qualifiers ?? []), + ); this.id = IdenticalUtil.createProtoId(loadUnit.id, NameUtil.getClassName(this.clazz)); this.initType = prototypeInfo.initType; this.accessLevel = prototypeInfo.accessLevel; diff --git a/core/metadata/src/impl/EggPrototypeBuilder.ts b/core/metadata/src/impl/EggPrototypeBuilder.ts index 22cd3528..c74da8a7 100644 --- a/core/metadata/src/impl/EggPrototypeBuilder.ts +++ b/core/metadata/src/impl/EggPrototypeBuilder.ts @@ -53,10 +53,10 @@ export class EggPrototypeBuilder { builder.injectType = PrototypeUtil.getInjectType(clazz); builder.injectObjects = PrototypeUtil.getInjectObjects(clazz) || []; builder.loadUnit = loadUnit; - builder.qualifiers = [ - ...QualifierUtil.getProtoQualifiers(clazz), - ...(ctx.prototypeInfo.qualifiers ?? []), - ]; + builder.qualifiers = QualifierUtil.mergeQualifiers( + QualifierUtil.getProtoQualifiers(clazz), + (ctx.prototypeInfo.qualifiers ?? []), + ); builder.properQualifiers = ctx.prototypeInfo.properQualifiers ?? {}; builder.multiInstanceConstructorIndex = PrototypeUtil.getMultiInstanceConstructorIndex(clazz); builder.multiInstanceConstructorAttributes = PrototypeUtil.getMultiInstanceConstructorAttributes(clazz); @@ -66,36 +66,36 @@ export class EggPrototypeBuilder { private tryFindDefaultPrototype(injectObject: InjectObject): EggPrototype { const propertyQualifiers = QualifierUtil.getProperQualifiers(this.clazz, injectObject.refName); const multiInstancePropertyQualifiers = this.properQualifiers[injectObject.refName as string] ?? []; - return EggPrototypeFactory.instance.getPrototype(injectObject.objName, this.loadUnit, [ - ...propertyQualifiers, - ...multiInstancePropertyQualifiers, - ]); + return EggPrototypeFactory.instance.getPrototype(injectObject.objName, this.loadUnit, QualifierUtil.mergeQualifiers( + propertyQualifiers, + multiInstancePropertyQualifiers, + )); } private tryFindContextPrototype(injectObject: InjectObject): EggPrototype { const propertyQualifiers = QualifierUtil.getProperQualifiers(this.clazz, injectObject.refName); const multiInstancePropertyQualifiers = this.properQualifiers[injectObject.refName as string] ?? []; - return EggPrototypeFactory.instance.getPrototype(injectObject.objName, this.loadUnit, [ - ...propertyQualifiers, - ...multiInstancePropertyQualifiers, - { + return EggPrototypeFactory.instance.getPrototype(injectObject.objName, this.loadUnit, QualifierUtil.mergeQualifiers( + propertyQualifiers, + multiInstancePropertyQualifiers, + [{ attribute: InitTypeQualifierAttribute, value: ObjectInitType.CONTEXT, - }, - ]); + }], + )); } private tryFindSelfInitTypePrototype(injectObject: InjectObject): EggPrototype { const propertyQualifiers = QualifierUtil.getProperQualifiers(this.clazz, injectObject.refName); const multiInstancePropertyQualifiers = this.properQualifiers[injectObject.refName as string] ?? []; - return EggPrototypeFactory.instance.getPrototype(injectObject.objName, this.loadUnit, [ - ...propertyQualifiers, - ...multiInstancePropertyQualifiers, - { + return EggPrototypeFactory.instance.getPrototype(injectObject.objName, this.loadUnit, QualifierUtil.mergeQualifiers( + propertyQualifiers, + multiInstancePropertyQualifiers, + [{ attribute: InitTypeQualifierAttribute, value: this.initType, - }, - ]); + }], + )); } private findInjectObjectPrototype(injectObject: InjectObject): EggPrototype { diff --git a/core/metadata/src/model/ProtoDescriptorHelper.ts b/core/metadata/src/model/ProtoDescriptorHelper.ts index 6dd21af4..ff8e0d4f 100644 --- a/core/metadata/src/model/ProtoDescriptorHelper.ts +++ b/core/metadata/src/model/ProtoDescriptorHelper.ts @@ -96,10 +96,10 @@ export class ProtoDescriptorHelper { const res: ProtoDescriptor[] = []; for (const obj of instanceProperty.objects) { - let qualifiers = [ - ...QualifierUtil.getProtoQualifiers(clazz), - ...obj.qualifiers, - ]; + let qualifiers = QualifierUtil.mergeQualifiers( + QualifierUtil.getProtoQualifiers(clazz), + obj.qualifiers, + ); qualifiers = ProtoDescriptorHelper.addDefaultQualifier(qualifiers, instanceProperty.initType, options.instanceModuleName); const injectObjects: InjectObjectDescriptor[] = PrototypeUtil.getInjectObjects(clazz) .map(t => { @@ -107,10 +107,10 @@ export class ProtoDescriptorHelper { const instanceQualifier = obj.properQualifiers?.[t.refName] ?? []; return { ...t, - qualifiers: [ - ...qualifiers, - ...instanceQualifier, - ], + qualifiers: QualifierUtil.mergeQualifiers( + qualifiers, + instanceQualifier, + ), }; }); res.push(new ClassProtoDescriptor({ diff --git a/core/metadata/src/model/graph/GlobalGraph.ts b/core/metadata/src/model/graph/GlobalGraph.ts index ff767a3a..4dd412e3 100644 --- a/core/metadata/src/model/graph/GlobalGraph.ts +++ b/core/metadata/src/model/graph/GlobalGraph.ts @@ -12,6 +12,7 @@ import { FrameworkErrorFormater } from 'egg-errors'; import { EggPrototypeNotFound, MultiPrototypeFound } from '../../errors'; import { GlobalModuleNodeBuilder } from './GlobalModuleNodeBuilder'; import { ModuleDescriptor } from '../ModuleDescriptor'; +import { QualifierUtil } from '@eggjs/core-decorator'; export interface GlobalGraphOptions { // TODO next major version refactor to force strict @@ -145,10 +146,10 @@ export class GlobalGraph { for (const node of this.protoGraph.nodes.values()) { if (node.val.selectProto({ name: injectObject.objName, - qualifiers: [ - ...injectObject.qualifiers, - ...qualifiers, - ], + qualifiers: QualifierUtil.mergeQualifiers( + injectObject.qualifiers, + qualifiers, + ), moduleName: proto.instanceModuleName, })) { result.push(node); @@ -189,13 +190,13 @@ export class GlobalGraph { if (!loadUnitQualifier) { return this.findDependencyProtoNode(proto, { ...injectObject, - qualifiers: [ - ...injectObject.qualifiers, - { + qualifiers: QualifierUtil.mergeQualifiers( + injectObject.qualifiers, + [{ attribute: LoadUnitNameQualifierAttribute, value: proto.instanceModuleName, - }, - ], + }], + ), }); } throw FrameworkErrorFormater.formatError(new MultiPrototypeFound(injectObject.objName, injectObject.qualifiers)); diff --git a/core/runtime/src/impl/EggObjectUtil.ts b/core/runtime/src/impl/EggObjectUtil.ts index e79f3c12..fd6e5119 100644 --- a/core/runtime/src/impl/EggObjectUtil.ts +++ b/core/runtime/src/impl/EggObjectUtil.ts @@ -54,7 +54,11 @@ export class EggObjectUtil { return target[p]; } const obj = getObj(); - return obj[p]; + const val = obj[p]; + if (typeof val === 'function') { + return val.bind(obj); + } + return val; }, getOwnPropertyDescriptor(_target: {}, p: string | symbol): PropertyDescriptor | undefined { const obj = getObj(); diff --git a/plugin/dal/lib/TransactionalAOP.ts b/plugin/dal/lib/TransactionalAOP.ts index 65e7d05c..fad23331 100644 --- a/plugin/dal/lib/TransactionalAOP.ts +++ b/plugin/dal/lib/TransactionalAOP.ts @@ -1,7 +1,6 @@ import { Advice, AdviceContext, IAdvice } from '@eggjs/tegg/aop'; import { AccessLevel, EggProtoImplClass, ObjectInitType } from '@eggjs/tegg'; import { PropagationType } from '@eggjs/tegg/transaction'; -import assert from 'node:assert'; import { MysqlDataSource } from '@eggjs/dal-runtime'; export interface TransactionalParams { @@ -17,7 +16,9 @@ export class TransactionalAOP implements IAdvice, next: () => Promise): Promise { const { propagation, dataSourceGetter } = ctx.adviceParams!; const dataSource = dataSourceGetter(); - assert(propagation === PropagationType.REQUIRED, '事务注解目前只支持 REQUIRED 机制'); + if (propagation === PropagationType.ALWAYS_NEW) { + return await dataSource.beginTransactionScope(next); + } return await dataSource.beginTransactionScope(next); } } diff --git a/plugin/tegg/lib/EggCompatibleProtoImpl.ts b/plugin/tegg/lib/EggCompatibleProtoImpl.ts index 3ec25aba..a94b88a4 100644 --- a/plugin/tegg/lib/EggCompatibleProtoImpl.ts +++ b/plugin/tegg/lib/EggCompatibleProtoImpl.ts @@ -79,10 +79,10 @@ export class EggCompatibleProtoImpl implements EggPrototype { const name = ctx.prototypeInfo.name; const id = IdenticalUtil.createProtoId(loadUnit.id, name); const proto = new EggCompatibleProtoImpl( - id, name, clazz, ctx.prototypeInfo.initType, loadUnit.id, [ - ...QualifierUtil.getProtoQualifiers(clazz), - ...(ctx.prototypeInfo.qualifiers ?? []), - ], + id, name, clazz, ctx.prototypeInfo.initType, loadUnit.id, QualifierUtil.mergeQualifiers( + QualifierUtil.getProtoQualifiers(clazz), + (ctx.prototypeInfo.qualifiers ?? []), + ), ); return proto; } diff --git a/plugin/tegg/test/ConstructorModuleConfig.test.ts b/plugin/tegg/test/ConstructorModuleConfig.test.ts index ecf63524..eac3a4f1 100644 --- a/plugin/tegg/test/ConstructorModuleConfig.test.ts +++ b/plugin/tegg/test/ConstructorModuleConfig.test.ts @@ -1,6 +1,7 @@ import mm from 'egg-mock'; import assert from 'assert'; import path from 'path'; +import { Foo } from './fixtures/apps/constructor-module-config/modules/module-with-config/foo'; describe('plugin/tegg/test/ModuleConfig.test.ts', () => { let app; @@ -37,4 +38,9 @@ describe('plugin/tegg/test/ModuleConfig.test.ts', () => { }); }); }); + + it('construct proxy should work', async () => { + const foo: Foo = await app.getEggObject(Foo); + foo.log(); + }); }); diff --git a/plugin/tegg/test/fixtures/apps/constructor-module-config/modules/module-with-config/foo.ts b/plugin/tegg/test/fixtures/apps/constructor-module-config/modules/module-with-config/foo.ts index 80b8a28a..9a9075d2 100644 --- a/plugin/tegg/test/fixtures/apps/constructor-module-config/modules/module-with-config/foo.ts +++ b/plugin/tegg/test/fixtures/apps/constructor-module-config/modules/module-with-config/foo.ts @@ -1,4 +1,5 @@ import { AccessLevel, Inject, SingletonProto } from '@eggjs/tegg'; +import { EggLogger } from 'egg-logger'; @SingletonProto({ accessLevel: AccessLevel.PUBLIC, @@ -7,8 +8,15 @@ export class Foo { readonly foo: string; readonly bar: string; - constructor(@Inject() moduleConfig: Record) { + constructor( + @Inject() moduleConfig: Record, + @Inject() readonly logger: EggLogger, + ) { this.foo = moduleConfig.features.dynamic.foo; this.bar = moduleConfig.features.dynamic.bar; } + + log() { + this.logger.info('foo'); + } }