Skip to content

Commit

Permalink
feat(core): create context from async orm instance (#4812)
Browse files Browse the repository at this point in the history
Added support to orm instance which wraps inside of Promise to
`@CreateRequestContext()` decorator.

It now additionally supports `async` callback like the below

```ts
async getOrm(): Promise<MikroORM>{
  //
}

const ORM = getOrm();

@CreateRequestContext(async () => ORM)
```

Or Promise instance
```ts
@CreateRequestContext(ORM)
```

Or async `orm` property

```ts
class H {
  constructor(protected orm: Promise<MikroORM>) {}

  @CreateRequestContext()
  async handle() {

  }
}
```

Closes #4805
  • Loading branch information
mehedimi committed Oct 24, 2023
1 parent 5bc12a9 commit fbf3a4d
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 3 deletions.
14 changes: 11 additions & 3 deletions packages/core/src/decorators/CreateRequestContext.ts
@@ -1,14 +1,22 @@
import { MikroORM } from '../MikroORM';
import { RequestContext } from '../utils/RequestContext';

export function CreateRequestContext<T>(getContext?: MikroORM | ((type?: T) => MikroORM)): MethodDecorator {
export function CreateRequestContext<T>(getContext?: MikroORM | Promise<MikroORM> | ((type?: T) => MikroORM | Promise<MikroORM>)): MethodDecorator {
return function (target: any, propertyKey: string | symbol, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = async function (this: T, ...args: any[]) {
/* istanbul ignore next */
const orm = getContext instanceof MikroORM ? getContext : (getContext?.(this) ?? (this as any).orm);
let orm: unknown;

if (!(orm as unknown instanceof MikroORM)) {
if (typeof getContext === 'function') {
orm = await (getContext(this) ?? (this as any).orm);
} else if (getContext) {
orm = await getContext;
} else {
orm = await (this as any).orm;
}

if (!(orm instanceof MikroORM)) {
throw new Error('@CreateRequestContext() decorator can only be applied to methods of classes with `orm: MikroORM` property, or with a callback parameter like `@CreateRequestContext(() => orm)`');
}

Expand Down
31 changes: 31 additions & 0 deletions tests/decorators.test.ts
Expand Up @@ -24,6 +24,8 @@ const TEST_VALUE = 'expected value';

const DI = {} as Dictionary;

const ASYNC_ORM: Promise<MikroORM> = Promise.resolve(Object.create(MikroORM.prototype, { em: { value: { name: 'default', fork: jest.fn() } } }));

class TestClass {

constructor(private readonly orm: MikroORM) {}
Expand Down Expand Up @@ -53,6 +55,16 @@ class TestClass {
//
}

@CreateRequestContext(async () => ASYNC_ORM)
async methodWithAsyncCallback() {
return TEST_VALUE;
}

@CreateRequestContext(ASYNC_ORM)
async methodWithAsyncOrmInstance() {
return TEST_VALUE;
}

}

class TestClass2 {
Expand Down Expand Up @@ -86,6 +98,17 @@ class TestClass2 {

}

class TestClass3 {

constructor(private readonly orm: Promise<MikroORM>) {}

@CreateRequestContext()
methodWithAsyncOrmPropertyAndReturnsNothing() {
//
}

}


describe('decorators', () => {

Expand Down Expand Up @@ -176,6 +199,14 @@ describe('decorators', () => {

const err = '@CreateRequestContext() decorator can only be applied to methods of classes with `orm: MikroORM` property, or with a callback parameter like `@CreateRequestContext(() => orm)`';
await expect(test2.asyncMethodReturnsValue()).rejects.toThrow(err);
const ret7 = await test.methodWithAsyncCallback();
expect(ret7).toEqual(TEST_VALUE);
const ret8 = await test.methodWithAsyncOrmInstance();
expect(ret8).toEqual(TEST_VALUE);

const test3 = new TestClass3(ASYNC_ORM);
const ret9 = await test3.methodWithAsyncOrmPropertyAndReturnsNothing();
expect(ret9).toBeUndefined();
});

test('UseRequestContext', async () => {
Expand Down

0 comments on commit fbf3a4d

Please sign in to comment.