Skip to content

Commit

Permalink
Use a forked EntityManager for each transaction
Browse files Browse the repository at this point in the history
Currently, a global EntityManager is used for every operation. This causes an error that can be resolved by setting `allowGlobalContext: true`. But that isn't a great fix, because if you ever make an edit to an entity that fails, you'll never be able to make another change because the global context caches the failed change and never releases it.
  • Loading branch information
corbt committed May 13, 2022
1 parent d7a704e commit 268f5a9
Show file tree
Hide file tree
Showing 2 changed files with 14 additions and 7 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"scripts": {
"clean": "rm -fR lib",
"build": "tsc",
"prepack": "yarn clean && yarn build",
"dev": "yarn clean && tsc -w",
"test": "dotenv -e .env -- jest --config ./jest.json --runInBand --forceExit --detectOpenHandles --verbose --silent",
"ts-node": "ts-node",
Expand Down
20 changes: 13 additions & 7 deletions src/Resource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ export class Resource extends BaseResource {
}

public async count(filter: Filter): Promise<number> {
return this.orm.em.getRepository(this.model).count(
return this.orm.em.fork().getRepository(this.model).count(
convertFilter(filter),
);
}
Expand All @@ -84,6 +84,7 @@ export class Resource extends BaseResource {
const { direction, sortBy } = sort as { direction: 'asc' | 'desc', sortBy: string };

const results = await this.orm.em
.fork()
.getRepository(this.model)
.find(
convertFilter(filter), {
Expand All @@ -100,6 +101,7 @@ export class Resource extends BaseResource {

public async findOne(id: string | number): Promise<BaseRecord | null> {
const result = await this.orm.em
.fork()
.getRepository(this.model)
.findOne(id as any); // mikroorm has incorrect types for `findOne`

Expand All @@ -115,34 +117,37 @@ export class Resource extends BaseResource {
if (!pk) return [];

const results = await this.orm.em
.fork()
.getRepository(this.model)
.find({ [pk]: { $in: ids } });

return results.map((result) => new BaseRecord(wrap(result).toJSON(), this));
}

public async create(params: Record<string, any>): Promise<Record<string, any>> {
const instance = this.orm.em
const em = this.orm.em.fork();
const instance = em
.getRepository(this.model)
.create(flat.unflatten(params));

await this.validateAndSave(instance);
await this.validateAndSave(instance, em);

const returnedParams: Record<string, any> = flat.flatten(wrap(instance).toJSON());

return returnedParams;
}

public async update(pk: string | number, params: Record<string, any> = {}): Promise<Record<string, any>> {
const instance = await this.orm.em
const em = this.orm.em.fork();
const instance = await em
.getRepository(this.model)
.findOne(pk as any); // mikroorm has incorrect types for findOneOrFail

if (!instance) throw new Error('Record to update not found');

const updatedInstance = wrap(instance).assign(flat.unflatten(params));

await this.validateAndSave(updatedInstance);
await this.validateAndSave(updatedInstance, em);

const returnedParams: Record<string, any> = flat.flatten(wrap(updatedInstance).toJSON());

Expand All @@ -151,6 +156,7 @@ export class Resource extends BaseResource {

public async delete(id: string | number): Promise<void> {
await this.orm.em
.fork()
.getRepository(this.model)
.nativeDelete(id as any); // mikroorm has incorrect types for nativeDelete
}
Expand All @@ -161,7 +167,7 @@ export class Resource extends BaseResource {
return !!model?.name && !!orm?.getMetadata?.().find?.(model.name);
}

async validateAndSave(instance: Loaded<AnyEntity>): Promise<void> {
async validateAndSave(instance: Loaded<AnyEntity>, em: EntityManager): Promise<void> {
if (Resource.validate) {
const errors = await Resource.validate(instance);
if (errors && errors.length) {
Expand All @@ -179,7 +185,7 @@ export class Resource extends BaseResource {
}
}
try {
await this.orm.em.persistAndFlush(instance);
await em.persistAndFlush(instance);
} catch (error) {
// TODO: figure out how to get column name from MikroORM's error metadata
// It currently seems to return only whole Entity
Expand Down

0 comments on commit 268f5a9

Please sign in to comment.