Skip to content

Commit

Permalink
feat: impl MultiInstanceProto
Browse files Browse the repository at this point in the history
  • Loading branch information
killagu committed Aug 24, 2023
1 parent 0862655 commit f5031a6
Show file tree
Hide file tree
Showing 41 changed files with 621 additions and 104 deletions.
2 changes: 2 additions & 0 deletions core/core-decorator/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export * from './src/decorator/ModuleQualifier';
export * from './src/decorator/ContextProto';
export * from './src/decorator/SingletonProto';
export * from './src/decorator/EggQualifier';
export * from './src/decorator/MultiInstanceProto';

export * from './src/enum/AccessLevel';
export * from './src/enum/ObjectInitType';
Expand All @@ -13,6 +14,7 @@ export * from './src/enum/EggType';
export * from './src/model/EggPrototypeInfo';
export * from './src/model/InjectObjectInfo';
export * from './src/model/QualifierInfo';
export * from './src/model/EggMultiInstancePrototypeInfo';

export * from './src/util/MetadataUtil';
export * from './src/util/PrototypeUtil';
Expand Down
73 changes: 73 additions & 0 deletions core/core-decorator/src/decorator/MultiInstanceProto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { ObjectInitType, ObjectInitTypeLike } from '../enum/ObjectInitType';
import { AccessLevel } from '../enum/AccessLevel';
import { DEFAULT_PROTO_IMPL_TYPE } from './Prototype';
import {
EggMultiInstanceCallbackPrototypeInfo,
EggMultiInstancePrototypeInfo,
MultiInstancePrototypeGetObjectsContext,
ObjectInfo,
} from '../model/EggMultiInstancePrototypeInfo';
import { EggProtoImplClass } from '../model/EggPrototypeInfo';
import { PrototypeUtil } from '../util/PrototypeUtil';
import { StackUtil } from '@eggjs/tegg-common-util';

const DEFAULT_PARAMS = {
initType: ObjectInitType.SINGLETON,
accessLevel: AccessLevel.PRIVATE,
protoImplType: DEFAULT_PROTO_IMPL_TYPE,
};

export interface BaseMultiInstancePrototypeCallbackParams {
/**
* obj init type
*/
initType?: ObjectInitTypeLike;
/**
* access level
*/
accessLevel?: AccessLevel;
/**
* EggPrototype implement type
*/
protoImplType?: string;
}

export interface MultiInstancePrototypeCallbackParams extends BaseMultiInstancePrototypeCallbackParams {
getObjects(ctx: MultiInstancePrototypeGetObjectsContext): ObjectInfo[];
}

export interface MultiInstancePrototypeStaticParams extends BaseMultiInstancePrototypeCallbackParams {
/**
* object info list
*/
objects: ObjectInfo[];
}

export type MultiInstancePrototypeParams = MultiInstancePrototypeCallbackParams | MultiInstancePrototypeStaticParams;

export function MultiInstanceProto(param: MultiInstancePrototypeParams) {
return function(clazz: EggProtoImplClass) {
PrototypeUtil.setIsEggMultiInstancePrototype(clazz);
if ((param as MultiInstancePrototypeStaticParams).objects) {
const property: EggMultiInstancePrototypeInfo = {
...DEFAULT_PARAMS,
...param as MultiInstancePrototypeStaticParams,
};
PrototypeUtil.setMultiInstanceStaticProperty(clazz, property);
} else if ((param as MultiInstancePrototypeCallbackParams).getObjects) {
const property: EggMultiInstanceCallbackPrototypeInfo = {
...DEFAULT_PARAMS,
...param as MultiInstancePrototypeCallbackParams,
};
PrototypeUtil.setMultiInstanceCallbackProperty(clazz, property);
}


// './tegg/core/common-util/src/StackUtil.ts',
// './tegg/core/core-decorator/src/decorator/Prototype.ts',
// './tegg/core/core-decorator/node_modules/[email protected]@reflect-metadata/Reflect.js',
// './tegg/core/core-decorator/node_modules/[email protected]@reflect-metadata/Reflect.js',
// './tegg/core/core-decorator/test/fixtures/decators/CacheService.ts',
PrototypeUtil.setFilePath(clazz, StackUtil.getCalleeFromStack(false, 4));
};
}
54 changes: 54 additions & 0 deletions core/core-decorator/src/model/EggMultiInstancePrototypeInfo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { ObjectInitTypeLike } from '../enum/ObjectInitType';
import { AccessLevel } from '../enum/AccessLevel';
import { EggPrototypeName } from './EggPrototypeInfo';
import { QualifierInfo } from './QualifierInfo';

export interface ObjectInfo {
name: EggPrototypeName;
qualifiers: QualifierInfo[];
}

export interface MultiInstancePrototypeGetObjectsContext {
unitPath: string;
}

export interface EggMultiInstancePrototypeInfo {
/**
* obj init type
*/
initType: ObjectInitTypeLike;
/**
* access level
*/
accessLevel: AccessLevel;
/**
* EggPrototype implement type
*/
protoImplType: string;

/**
* object info list
*/
objects: ObjectInfo[];
}

export interface EggMultiInstanceCallbackPrototypeInfo {
/**
* obj init type
*/
initType: ObjectInitTypeLike;
/**
* access level
*/
accessLevel: AccessLevel;
/**
* EggPrototype implement type
*/
protoImplType: string;

/**
* get object callback
* @param ctx
*/
getObjects(ctx: MultiInstancePrototypeGetObjectsContext): ObjectInfo[];
}
5 changes: 5 additions & 0 deletions core/core-decorator/src/model/EggPrototypeInfo.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { ObjectInitTypeLike } from '../enum/ObjectInitType';
import { AccessLevel } from '../enum/AccessLevel';
import { QualifierInfo } from './QualifierInfo';

export type EggProtoImplClass<T = object> = new(...args: any[]) => T;
export type EggPrototypeName = PropertyKey;
Expand All @@ -21,4 +22,8 @@ export interface EggPrototypeInfo {
* EggPrototype implement type
*/
protoImplType: string;
/**
* EggPrototype qualifiers
*/
qualifiers?: QualifierInfo[];
}
82 changes: 81 additions & 1 deletion core/core-decorator/src/util/PrototypeUtil.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
import { MetadataUtil } from './MetadataUtil';
import { EggProtoImplClass, EggPrototypeInfo } from '../model/EggPrototypeInfo';
import { EggProtoImplClass, EggPrototypeInfo, EggPrototypeName } from '../model/EggPrototypeInfo';
import { InjectObjectInfo } from '../model/InjectObjectInfo';
import {
EggMultiInstanceCallbackPrototypeInfo,
EggMultiInstancePrototypeInfo, MultiInstancePrototypeGetObjectsContext,
} from '../model/EggMultiInstancePrototypeInfo';

export class PrototypeUtil {
static readonly IS_EGG_OBJECT_PROTOTYPE = Symbol.for('EggPrototype#isEggPrototype');
static readonly IS_EGG_OBJECT_MULTI_INSTANCE_PROTOTYPE = Symbol.for('EggPrototype#isEggMultiInstancePrototype');
static readonly FILE_PATH = Symbol.for('EggPrototype.filePath');
static readonly PROTOTYPE_PROPERTY = Symbol.for('EggPrototype.Property');
static readonly MULTI_INSTANCE_PROTOTYPE_STATIC_PROPERTY = Symbol.for('EggPrototype.MultiInstanceStaticProperty');
static readonly MULTI_INSTANCE_PROTOTYPE_CALLBACK_PROPERTY = Symbol.for('EggPrototype.MultiInstanceCallbackProperty');
static readonly INJECT_OBJECT_NAME_SET = Symbol.for('EggPrototype.injectObjectNames');
static readonly CLAZZ_PROTO = Symbol.for('EggPrototype.clazzProto');

Expand All @@ -25,6 +32,22 @@ export class PrototypeUtil {
return MetadataUtil.getBooleanMetaData(this.IS_EGG_OBJECT_PROTOTYPE, clazz);
}

/**
* Mark class is egg object multi instance prototype
* @param {Function} clazz -
*/
static setIsEggMultiInstancePrototype(clazz: EggProtoImplClass) {
MetadataUtil.defineMetaData(this.IS_EGG_OBJECT_MULTI_INSTANCE_PROTOTYPE, true, clazz);
}

/**
* If class is egg object multi instance prototype, return true
* @param {Function} clazz -
*/
static isEggMultiInstancePrototype(clazz: EggProtoImplClass): boolean {
return MetadataUtil.getBooleanMetaData(this.IS_EGG_OBJECT_MULTI_INSTANCE_PROTOTYPE, clazz);
}

/**
* set class file path
* @param {Function} clazz -
Expand Down Expand Up @@ -60,6 +83,63 @@ export class PrototypeUtil {
return MetadataUtil.getMetaData(this.PROTOTYPE_PROPERTY, clazz);
}

static getInitType(clazz: EggProtoImplClass, ctx: MultiInstancePrototypeGetObjectsContext): string | undefined {
const property = this.getProperty(clazz) ?? this.getMultiInstanceProperty(clazz, ctx);
return property?.initType;
}

static getAccessLevel(clazz: EggProtoImplClass, ctx: MultiInstancePrototypeGetObjectsContext): string | undefined {
const property = this.getProperty(clazz) ?? this.getMultiInstanceProperty(clazz, ctx);
return property?.accessLevel;
}

static getObjNames(clazz: EggProtoImplClass, ctx: MultiInstancePrototypeGetObjectsContext): EggPrototypeName[] {
const property = this.getProperty(clazz);
if (property) {
return [ property.name ];
}
const multiInstanceProperty = this.getMultiInstanceProperty(clazz, ctx);
return multiInstanceProperty?.objects.map(t => t.name) || [];
}

/**
* set class property
* @param {EggProtoImplClass} clazz -
* @param {EggPrototypeInfo} property -
*/
static setMultiInstanceStaticProperty(clazz: EggProtoImplClass, property: EggMultiInstancePrototypeInfo) {
MetadataUtil.defineMetaData(this.MULTI_INSTANCE_PROTOTYPE_STATIC_PROPERTY, property, clazz);
}

/**
* set class property
* @param {EggProtoImplClass} clazz -
* @param {EggPrototypeInfo} property -
*/
static setMultiInstanceCallbackProperty(clazz: EggProtoImplClass, property: EggMultiInstanceCallbackPrototypeInfo) {
MetadataUtil.defineMetaData(this.MULTI_INSTANCE_PROTOTYPE_CALLBACK_PROPERTY, property, clazz);
}

/**
* get class property
* @param {EggProtoImplClass} clazz -
* @param {MultiInstancePrototypeGetObjectsContext} ctx -
* @return {EggPrototypeInfo} -
*/
static getMultiInstanceProperty(clazz: EggProtoImplClass, ctx: MultiInstancePrototypeGetObjectsContext): EggMultiInstancePrototypeInfo | undefined {
const metadata = MetadataUtil.getMetaData<EggMultiInstancePrototypeInfo>(this.MULTI_INSTANCE_PROTOTYPE_STATIC_PROPERTY, clazz);
if (metadata) {
return metadata;
}
const callBackMetadata = MetadataUtil.getMetaData<EggMultiInstanceCallbackPrototypeInfo>(this.MULTI_INSTANCE_PROTOTYPE_CALLBACK_PROPERTY, clazz);
if (callBackMetadata) {
return {
...callBackMetadata,
objects: callBackMetadata.getObjects(ctx),
};
}
}

static addInjectObject(clazz: EggProtoImplClass, injectObject: InjectObjectInfo) {
const objs: InjectObjectInfo[] = MetadataUtil.initOwnArrayMetaData(this.INJECT_OBJECT_NAME_SET, clazz, []);
objs.push(injectObject);
Expand Down
29 changes: 29 additions & 0 deletions core/core-decorator/test/decorators.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import {
DEFAULT_PROTO_IMPL_TYPE,
} from '..';
import QualifierCacheService from './fixtures/decators/QualifierCacheService';
import { FOO_ATTRIBUTE, FooLogger } from './fixtures/decators/FooLogger';
import { EggMultiInstancePrototypeInfo } from '../src/model/EggMultiInstancePrototypeInfo';

describe('test/decorator.test.ts', () => {
describe('ContextProto', () => {
Expand Down Expand Up @@ -90,6 +92,33 @@ describe('test/decorator.test.ts', () => {
});
});

describe('MultiInstanceProto', () => {
it('should work', () => {
assert(PrototypeUtil.isEggMultiInstancePrototype(FooLogger));
const expectObjectProperty: EggMultiInstancePrototypeInfo = {
initType: ObjectInitType.SINGLETON,
accessLevel: AccessLevel.PUBLIC,
protoImplType: 'foo',
objects: [{
name: 'foo',
qualifiers: [{
attribute: FOO_ATTRIBUTE,
value: 'foo1',
}],
}, {
name: 'foo',
qualifiers: [{
attribute: FOO_ATTRIBUTE,
value: 'foo2',
}],
}],
};
assert.deepStrictEqual(PrototypeUtil.getMultiInstanceProperty(FooLogger, {
unitPath: 'foo',
}), expectObjectProperty);
});
});

it('should get the right file path', () => {
assert(PrototypeUtil.getFilePath(CacheService) === CacheService.fileName);
});
Expand Down
27 changes: 27 additions & 0 deletions core/core-decorator/test/fixtures/decators/FooLogger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { MultiInstanceProto } from '../../../src/decorator/MultiInstanceProto';
import { AccessLevel } from '../../../src/enum/AccessLevel';
import { ObjectInitType } from '../../../src/enum/ObjectInitType';

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

@MultiInstanceProto({
accessLevel: AccessLevel.PUBLIC,
initType: ObjectInitType.SINGLETON,
protoImplType: 'foo',
objects: [{
name: 'foo',
qualifiers: [{
attribute: FOO_ATTRIBUTE,
value: 'foo1',
}],
}, {
name: 'foo',
qualifiers: [{
attribute: FOO_ATTRIBUTE,
value: 'foo2',
}],
}],
})
export class FooLogger {

}
26 changes: 16 additions & 10 deletions core/dynamic-inject-runtime/src/EggObjectFactoryPrototype.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ import {
InjectObjectProto, LoadUnit,
} from '@eggjs/tegg-metadata';
import {
AccessLevel, EggProtoImplClass, EggPrototypeName,
AccessLevel, EggProtoImplClass, EggPrototypeInfo, EggPrototypeName,
MetaDataKey,
MetadataUtil,
ObjectInitTypeLike, PrototypeUtil,
ObjectInitTypeLike,
QualifierInfo,
QualifierUtil,
QualifierUtil, QualifierValue,
} from '@eggjs/core-decorator';
import { NameUtil } from '@eggjs/tegg-common-util';
import { Id, IdenticalUtil } from '@eggjs/tegg-lifecycle';
Expand All @@ -28,15 +28,17 @@ export class EggObjectFactoryPrototype implements EggPrototype {
readonly name: EggPrototypeName;
readonly qualifiers: QualifierInfo[];

constructor(clazz: EggProtoImplClass<EggObjectFactory>, loadUnit: LoadUnit) {
constructor(clazz: EggProtoImplClass<EggObjectFactory>, loadUnit: LoadUnit, prototypeInfo: EggPrototypeInfo) {
this.clazz = clazz;
this.qualifiers = QualifierUtil.getProtoQualifiers(clazz);
this.qualifiers = [
...QualifierUtil.getProtoQualifiers(clazz),
...(prototypeInfo.qualifiers ?? []),
];
this.id = IdenticalUtil.createProtoId(loadUnit.id, NameUtil.getClassName(this.clazz));
const property = PrototypeUtil.getProperty(clazz)!;
this.initType = property.initType;
this.accessLevel = property.accessLevel;
this.initType = prototypeInfo.initType;
this.accessLevel = prototypeInfo.accessLevel;
this.loadUnitId = loadUnit.id;
this.name = property.name || NameUtil.getClassName(this.clazz);
this.name = prototypeInfo.name || NameUtil.getClassName(this.clazz);
this.injectObjects = [];
}

Expand All @@ -53,6 +55,10 @@ export class EggObjectFactoryPrototype implements EggPrototype {
return selfQualifiers?.value === qualifier.value;
}

getQualifier(attribute: string): QualifierValue | undefined {
return this.qualifiers.find(t => t.attribute === attribute)?.value;
}

verifyQualifiers(qualifiers: QualifierInfo[]): boolean {
for (const qualifier of qualifiers) {
if (!this.verifyQualifier(qualifier)) {
Expand All @@ -63,7 +69,7 @@ export class EggObjectFactoryPrototype implements EggPrototype {
}

static create(ctx: EggPrototypeLifecycleContext) {
return new EggObjectFactoryPrototype(ctx.clazz as EggProtoImplClass<EggObjectFactory>, ctx.loadUnit);
return new EggObjectFactoryPrototype(ctx.clazz as EggProtoImplClass<EggObjectFactory>, ctx.loadUnit, ctx.prototypeInfo);
}
}

Expand Down
Loading

0 comments on commit f5031a6

Please sign in to comment.