Skip to content

Commit

Permalink
feat: add getEggObjects API to fetch all instances (#189)
Browse files Browse the repository at this point in the history
<!--
Thank you for your pull request. Please review below requirements.
Bug fixes and new features should include tests and possibly benchmarks.
Contributors guide:
https://github.com/eggjs/egg/blob/master/CONTRIBUTING.md

感谢您贡献代码。请确认下列 checklist 的完成情况。
Bug 修复和新功能必须包含测试,必要时请附上性能测试。
Contributors guide:
https://github.com/eggjs/egg/blob/master/CONTRIBUTING.md
-->

##### Checklist
<!-- Remove items that do not apply. For completed items, change [ ] to
[x]. -->

- [x] `npm test` passes
- [x] tests and/or benchmarks are included
- [x] documentation is changed or added
- [ ] commit message follows commit guidelines

##### Affected core subsystem(s)
<!-- Provide affected core subsystem(s). -->


##### Description of change
<!-- Provide a description of the change below this comment. -->

新增 `eggObjectFactory.getEggObjects` 接口,用于获取抽象类对应的所有实现。

<!--
- any feature?
- close https://github.com/eggjs/egg/ISSUE_URL
-->

---------

Co-authored-by: SangLv <[email protected]>
  • Loading branch information
sang4lv and SangLv authored Jan 31, 2024
1 parent c24ba0c commit f8592c2
Show file tree
Hide file tree
Showing 6 changed files with 98 additions and 10 deletions.
23 changes: 22 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -934,14 +934,35 @@ import { EggObjectFactory } from '@eggjs/tegg';
export class HelloService {
@Inject()
private readonly eggObjectFactory: EggObjectFactory;

async hello(): Promise<string> {
const helloImpl = await this.eggObjectFactory.getEggObject(AbstractHello, HelloType.BAR);
return helloImpl.hello();
}
}
```

动态获取多个实现,通过 for/await 循环获得实例。

```ts
import { EggObjectFactory } from '@eggjs/tegg';

@ContextProto()
export class HelloService {
@Inject()
private readonly eggObjectFactory: EggObjectFactory;

async hello(): Promise<string[]> {
const helloImpls = await this.eggObjectFactory.getEggObjects(AbstractHello);
const messages = [];
for await (const helloImpl of helloImpls) {
messages.push(helloImpl.hello());
}
return messages;
}
}
```

### 配置项目 module

一般情况下,无需在项目中手动声明包含了哪些 module,tegg 会自动在项目目录下进行扫描。但是会存在一些特殊的情况,tegg 无法扫描到,比如说 module 是通过 npm 包的方式来发布。因此 tegg 支持通过 `config/module.json` 的方式来声明包含了哪些 module。
Expand Down
29 changes: 28 additions & 1 deletion core/dynamic-inject-runtime/src/EggObjectFactory.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import type { EggContainerFactory } from '@eggjs/tegg-runtime';
import { EggAbstractClazz, EggObjectFactory as IEggObjectFactory, QualifierImplUtil } from '@eggjs/tegg-dynamic-inject';
import { AccessLevel, PrototypeUtil, QualifierValue, SingletonProto } from '@eggjs/core-decorator';
import {
EggAbstractClazz,
EggObjectFactory as IEggObjectFactory,
QualifierImplUtil,
} from '@eggjs/tegg-dynamic-inject';

import { EGG_OBJECT_FACTORY_PROTO_IMPLE_TYPE } from './EggObjectFactoryPrototype';

@SingletonProto({
Expand All @@ -23,4 +28,26 @@ export class EggObjectFactory implements IEggObjectFactory {
const eggObject = await this.eggContainerFactory.getOrCreateEggObject(protoObj, protoObj.name);
return eggObject.obj as T;
}

async getEggObjects<T extends object>(abstractClazz: EggAbstractClazz<T>) {
const implClazzMap = QualifierImplUtil.getQualifierImpMap(abstractClazz);
const getEggObject = this.getEggObject.bind(this);
const qualifierValues = Array.from(implClazzMap.keys());

return {
[Symbol.asyncIterator]() {
return {
key: 0,
async next() {
if (this.key === qualifierValues.length) {
return { done: true } as IteratorResult<T>;
}

const value = await getEggObject(abstractClazz, qualifierValues[this.key++]);
return { value, done: false } as IteratorResult<T>;
},
};
},
};
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { ContextProto, Inject } from '@eggjs/core-decorator';
import { ContextHelloType, SingletonHelloType } from './FooType';
import { EggObjectFactory } from '@eggjs/tegg-dynamic-inject';

import { AbstractContextHello } from './AbstractContextHello';
import { AbstractSingletonHello } from './AbstractSingletonHello';
import { EggObjectFactory } from '@eggjs/tegg-dynamic-inject';
import { ContextHelloType, SingletonHelloType } from './FooType';

@ContextProto()
export class HelloService {
Expand All @@ -19,4 +20,19 @@ export class HelloService {
const msgs = helloImpls.map(helloImpl => helloImpl.hello());
return msgs;
}

async sayHelloToAll(): Promise<string[]> {
const singletonHellos = await this.eggObjectFactory.getEggObjects(AbstractSingletonHello);
const contextHellos = await this.eggObjectFactory.getEggObjects(AbstractContextHello);

const msgs: string[] = [];
for await (const helloImpl of singletonHellos) {
msgs.push(helloImpl.hello());
}
for await (const helloImpl of contextHellos) {
msgs.push(helloImpl.hello());
}

return msgs;
}
}
22 changes: 19 additions & 3 deletions core/dynamic-inject-runtime/test/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import assert from 'assert';
import path from 'path';
import { LoadUnitInstance, LoadUnitInstanceFactory } from '@eggjs/tegg-runtime';

import { LoadUnitFactory } from '@eggjs/tegg-metadata';
import { LoadUnitInstance, LoadUnitInstanceFactory } from '@eggjs/tegg-runtime';

import { EggTestContext } from '../../test-util';
import { HelloService } from './fixtures/modules/dynamic-inject-module/HelloService';
import { CoreTestHelper } from '../../test-util/CoreTestHelper';
import assert from 'assert';
import { HelloService } from './fixtures/modules/dynamic-inject-module/HelloService';

describe('test/dynamic-inject-runtime.test.ts', () => {
let modules: Array<LoadUnitInstance>;
Expand Down Expand Up @@ -47,4 +49,18 @@ describe('test/dynamic-inject-runtime.test.ts', () => {
]);
});
});

it('should work with getAllEggObjects', async () => {
await EggTestContext.mockContext(async () => {
const helloService = await CoreTestHelper.getObject(HelloService);
const msgs = await helloService.sayHelloToAll();
assert.deepStrictEqual(msgs, [
'hello, bar(singleton:0)',
'hello, foo(singleton:0)',
'hello, bar(context:0)',
'hello, foo(context:0)',
]);
});

});
});
4 changes: 3 additions & 1 deletion core/dynamic-inject/src/EggObjectFactory.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { EggAbstractClazz } from './typing';
import { QualifierValue } from '@eggjs/core-decorator';

import { EggAbstractClazz } from './typing';

export interface EggObjectFactory {
getEggObject<T extends object>(abstractClazz: EggAbstractClazz<T>, qualifierValue: QualifierValue): Promise<T>;
getEggObjects<T extends object>(abstractClazz: EggAbstractClazz<T>): Promise<AsyncIterable<T>>;
}
10 changes: 8 additions & 2 deletions core/dynamic-inject/src/QualifierImplUtil.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { EggAbstractClazz } from './typing';
import { EggProtoImplClass, MetadataUtil, QualifierValue } from '@eggjs/core-decorator';

import { EggAbstractClazz } from './typing';

export const QUALIFIER_IMPL_MAP = Symbol.for('EggPrototype#qualifierImplMap');

export class QualifierImplUtil {
Expand All @@ -9,8 +10,13 @@ export class QualifierImplUtil {
implMap.set(qualifierValue, implClazz);
}

static getQualifierImp(abstractClazz: EggAbstractClazz, qualifierValue: QualifierValue):EggProtoImplClass | undefined {
static getQualifierImp(abstractClazz: EggAbstractClazz, qualifierValue: QualifierValue): EggProtoImplClass | undefined {
const implMap: Map<QualifierValue, EggProtoImplClass> | undefined = MetadataUtil.getMetaData(QUALIFIER_IMPL_MAP, abstractClazz as unknown as EggProtoImplClass);
return implMap?.get(qualifierValue);
}

static getQualifierImpMap(abstractClazz: EggAbstractClazz): Map<QualifierValue, EggProtoImplClass> {
const implMap: Map<QualifierValue, EggProtoImplClass> | undefined = MetadataUtil.getMetaData(QUALIFIER_IMPL_MAP, abstractClazz as unknown as EggProtoImplClass);
return implMap || new Map();
}
}

0 comments on commit f8592c2

Please sign in to comment.