Skip to content

Commit

Permalink
feat: impl MultiInstanceInfo decorator
Browse files Browse the repository at this point in the history
Add multi instances info for constructor inject mode.
  • Loading branch information
killagu committed Sep 29, 2024
1 parent 6cbdfc7 commit 311bc39
Show file tree
Hide file tree
Showing 9 changed files with 138 additions and 4 deletions.
1 change: 1 addition & 0 deletions core/core-decorator/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export * from './src/decorator/ContextProto';
export * from './src/decorator/SingletonProto';
export * from './src/decorator/EggQualifier';
export * from './src/decorator/MultiInstanceProto';
export * from './src/decorator/MultiInstanceInfo';
export * from './src/decorator/ConfigSource';

export * from './src/util/MetadataUtil';
Expand Down
9 changes: 9 additions & 0 deletions core/core-decorator/src/decorator/MultiInstanceInfo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { PrototypeUtil } from '../util/PrototypeUtil';
import { QualifierAttribute } from '@eggjs/tegg-types';

export function MultiInstanceInfo(attributes: QualifierAttribute[]) {
return function(target: any, _propertyKey: PropertyKey | undefined, parameterIndex: number) {
PrototypeUtil.setMultiInstanceConstructorIndex(target, parameterIndex);
PrototypeUtil.setMultiInstanceConstructorAttributes(target, attributes);
};
}
20 changes: 19 additions & 1 deletion core/core-decorator/src/util/PrototypeUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
InjectConstructorInfo,
InjectObjectInfo,
InjectType,
MultiInstancePrototypeGetObjectsContext,
MultiInstancePrototypeGetObjectsContext, QualifierAttribute,
} from '@eggjs/tegg-types';
import { MetadataUtil } from './MetadataUtil';

Expand All @@ -22,6 +22,8 @@ export class PrototypeUtil {
static readonly INJECT_TYPE = Symbol.for('EggPrototype.injectType');
static readonly INJECT_CONSTRUCTOR_NAME_SET = Symbol.for('EggPrototype.injectConstructorNames');
static readonly CLAZZ_PROTO = Symbol.for('EggPrototype.clazzProto');
static readonly MULTI_INSTANCE_CONSTRUCTOR_INDEX = Symbol.for('EggPrototype#multiInstanceConstructorIndex');
static readonly MULTI_INSTANCE_CONSTRUCTOR_ATTRIBUTES = Symbol.for('EggPrototype#multiInstanceConstructorAttributes');

/**
* Mark class is egg object prototype
Expand Down Expand Up @@ -146,6 +148,22 @@ export class PrototypeUtil {
}
}

static setMultiInstanceConstructorAttributes(clazz: EggProtoImplClass, attributes: QualifierAttribute[]) {
MetadataUtil.defineMetaData(PrototypeUtil.MULTI_INSTANCE_CONSTRUCTOR_ATTRIBUTES, attributes, clazz);
}

static getMultiInstanceConstructorAttributes(clazz: EggProtoImplClass): QualifierAttribute[] {
return MetadataUtil.getMetaData(PrototypeUtil.MULTI_INSTANCE_CONSTRUCTOR_ATTRIBUTES, clazz) || [];
}

static setMultiInstanceConstructorIndex(clazz: EggProtoImplClass, index: number) {
MetadataUtil.defineMetaData(PrototypeUtil.MULTI_INSTANCE_CONSTRUCTOR_INDEX, index, clazz);
}

static getMultiInstanceConstructorIndex(clazz: EggProtoImplClass): number | undefined {
return MetadataUtil.getMetaData(PrototypeUtil.MULTI_INSTANCE_CONSTRUCTOR_INDEX, clazz);
}

static setInjectType(clazz: EggProtoImplClass, type: InjectType) {
const injectType: InjectType | undefined = MetadataUtil.getMetaData(PrototypeUtil.INJECT_TYPE, clazz);
if (!injectType) {
Expand Down
8 changes: 7 additions & 1 deletion core/metadata/src/impl/EggPrototypeBuilder.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import assert from 'node:assert';
import { InjectType, PrototypeUtil, QualifierUtil } from '@eggjs/core-decorator';
import { InjectType, PrototypeUtil, QualifierAttribute, QualifierUtil } from '@eggjs/core-decorator';
import type {
AccessLevel,
EggProtoImplClass,
Expand Down Expand Up @@ -35,6 +35,8 @@ export class EggPrototypeBuilder {
private loadUnit: LoadUnit;
private qualifiers: QualifierInfo[] = [];
private className?: string;
private multiInstanceConstructorIndex?: number;
private multiInstanceConstructorAttributes?: QualifierAttribute[];

static create(ctx: EggPrototypeLifecycleContext): EggPrototype {
const { clazz, loadUnit } = ctx;
Expand All @@ -54,6 +56,8 @@ export class EggPrototypeBuilder {
...QualifierUtil.getProtoQualifiers(clazz),
...(ctx.prototypeInfo.qualifiers ?? []),
];
builder.multiInstanceConstructorIndex = PrototypeUtil.getMultiInstanceConstructorIndex(clazz);
builder.multiInstanceConstructorAttributes = PrototypeUtil.getMultiInstanceConstructorAttributes(clazz);
return builder.build();
}

Expand Down Expand Up @@ -140,6 +144,8 @@ export class EggPrototypeBuilder {
this.qualifiers,
this.className,
this.injectType,
this.multiInstanceConstructorIndex,
this.multiInstanceConstructorAttributes,
);
}
}
Expand Down
8 changes: 7 additions & 1 deletion core/metadata/src/impl/EggPrototypeImpl.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { InjectType, MetadataUtil } from '@eggjs/core-decorator';
import { InjectType, MetadataUtil, QualifierAttribute } from '@eggjs/core-decorator';
import type {
AccessLevel,
EggProtoImplClass,
Expand Down Expand Up @@ -26,6 +26,8 @@ export class EggPrototypeImpl implements EggPrototype {
readonly injectType: InjectType;
readonly loadUnitId: Id;
readonly className?: string;
readonly multiInstanceConstructorIndex?: number;
readonly multiInstanceConstructorAttributes?: QualifierAttribute[];

constructor(
id: string,
Expand All @@ -39,6 +41,8 @@ export class EggPrototypeImpl implements EggPrototype {
qualifiers: QualifierInfo[],
className?: string,
injectType?: InjectType,
multiInstanceConstructorIndex?: number,
multiInstanceConstructorAttributes?: QualifierAttribute[],
) {
this.id = id;
this.clazz = clazz;
Expand All @@ -51,6 +55,8 @@ export class EggPrototypeImpl implements EggPrototype {
this.qualifiers = qualifiers;
this.className = className;
this.injectType = injectType || InjectType.PROPERTY;
this.multiInstanceConstructorIndex = multiInstanceConstructorIndex;
this.multiInstanceConstructorAttributes = multiInstanceConstructorAttributes;
}

verifyQualifiers(qualifiers: QualifierInfo[]): boolean {
Expand Down
15 changes: 14 additions & 1 deletion core/runtime/src/impl/EggObjectImpl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ export default class EggObjectImpl implements EggObject {
// 4. call obj lifecycle postCreate
// 5. success create
try {
const constructArgs = await Promise.all(this.proto.injectObjects!.map(async injectObject => {
const constructArgs: any[] = await Promise.all(this.proto.injectObjects!.map(async injectObject => {
const proto = injectObject.proto;
const loadUnit = LoadUnitFactory.getLoadUnitById(proto.loadUnitId);
if (!loadUnit) {
Expand All @@ -104,6 +104,19 @@ export default class EggObjectImpl implements EggObject {
const injectObj = await EggContainerFactory.getOrCreateEggObject(proto, injectObject.objName);
return EggObjectUtil.eggObjectProxy(injectObj);
}));
if (this.proto.multiInstanceConstructorIndex) {
const qualifiers = this.proto.multiInstanceConstructorAttributes?.map(t => {
return {
attribute: t,
value: this.proto.getQualifier(t),
};
});
const objInfo = {
name: this.proto.name,
qualifiers,
};
constructArgs.splice(this.proto.multiInstanceConstructorIndex, 0, objInfo);
}

this._obj = this.proto.constructEggObject(...constructArgs);
const objLifecycleHook = this._obj as EggObjectLifecycle;
Expand Down
38 changes: 38 additions & 0 deletions core/runtime/test/LoadUnitInstance.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { Bar, Foo } from './fixtures/modules/extends-module/Base';
import { ContextHandler } from '../src/model/ContextHandler';
import { EggContextStorage } from './fixtures/EggContextStorage';
import { FOO_ATTRIBUTE, FooLogger } from './fixtures/modules/multi-instance-module/MultiInstance';
import { FooLoggerConstructor } from './fixtures/modules/multi-instance-module/MultiInstanceConstructor';

describe('test/LoadUnit/LoadUnitInstance.test.ts', () => {
describe('ModuleLoadUnitInstance', () => {
Expand Down Expand Up @@ -101,6 +102,43 @@ describe('test/LoadUnit/LoadUnitInstance.test.ts', () => {

await TestUtil.destroyLoadUnitInstance(instance);
});

it('should load multi instance with constructor', async () => {
const instance = await TestUtil.createLoadUnitInstance('multi-instance-module');
const foo1Proto = EggPrototypeFactory.instance.getPrototype('fooConstructor', instance.loadUnit, [{
attribute: FOO_ATTRIBUTE,
value: 'foo1',
}]);
const foo1Obj = await EggContainerFactory.getOrCreateEggObject(foo1Proto, foo1Proto.name);
const foo1 = foo1Obj.obj as FooLoggerConstructor;

const foo2Proto = EggPrototypeFactory.instance.getPrototype('fooConstructor', instance.loadUnit, [{
attribute: FOO_ATTRIBUTE,
value: 'foo2',
}]);
const foo2Obj = await EggContainerFactory.getOrCreateEggObject(foo2Proto, foo2Proto.name);
const foo2 = foo2Obj.obj as FooLoggerConstructor;
assert(foo1);
assert(foo2);
assert(foo1 !== foo2);
assert(foo1.foo === 'foo1');
assert(foo2.foo === 'foo2');
assert(foo1.bar === 'bar');
assert(foo2.foo === 'foo2');

const obj1 = await EggContainerFactory.getOrCreateEggObjectFromClazz(FooLogger, 'fooConstructor', [{
attribute: FOO_ATTRIBUTE,
value: 'foo1',
}]);
const obj2 = await EggContainerFactory.getOrCreateEggObjectFromClazz(FooLogger, 'fooConstructor', [{
attribute: FOO_ATTRIBUTE,
value: 'foo2',
}]);
assert(foo1Obj === obj1);
assert(foo2Obj === obj2);

await TestUtil.destroyLoadUnitInstance(instance);
});
});

describe('MultiModule', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import {
AccessLevel,
ObjectInitType,
QualifierValue,
ObjectInfo
} from '@eggjs/tegg-types';
import { Inject, MultiInstanceInfo, MultiInstanceProto, SingletonProto } from '@eggjs/core-decorator';

export const FOO_ATTRIBUTE = Symbol.for('FOO_ATTRIBUTE');

@SingletonProto()
export class Bar {
bar = 'bar';
}

@MultiInstanceProto({
accessLevel: AccessLevel.PUBLIC,
initType: ObjectInitType.SINGLETON,
objects: [{
name: 'fooConstructor',
qualifiers: [{
attribute: FOO_ATTRIBUTE,
value: 'foo1',
}],
}, {
name: 'fooConstructor',
qualifiers: [{
attribute: FOO_ATTRIBUTE,
value: 'foo2',
}],
}],
})
export class FooLoggerConstructor {
foo: QualifierValue | undefined;
bar: string;

constructor(@Inject() bar: Bar, @MultiInstanceInfo([ FOO_ATTRIBUTE ]) objInfo: ObjectInfo) {
this.foo = objInfo.qualifiers.find(t => t.attribute === FOO_ATTRIBUTE)?.value;
this.bar = bar.bar;
}
}
2 changes: 2 additions & 0 deletions core/types/metadata/model/EggPrototype.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ export interface EggPrototype extends LifecycleObject<EggPrototypeLifecycleConte
readonly injectObjects: Array<InjectObjectProto | InjectConstructorProto>;
readonly injectType?: InjectType;
readonly className?: string;
readonly multiInstanceConstructorIndex?: number;
readonly multiInstanceConstructorAttributes?: QualifierAttribute[];

/**
* get metedata for key
Expand Down

0 comments on commit 311bc39

Please sign in to comment.