99 lines
2.3 KiB
TypeScript
Raw Normal View History

2023-01-18 14:59:10 +02:00
import { EventEmitter } from 'events';
import { randomUUID } from 'crypto';
import type { LoadedStrapi } from '@strapi/typings';
2023-01-18 14:59:10 +02:00
2023-01-24 16:35:06 +02:00
import { Transaction, TransactionCallback } from '../../types/utils';
2023-01-23 17:50:53 +02:00
2023-09-06 11:37:26 +02:00
export const createTransaction = (strapi: LoadedStrapi): Transaction => {
2023-01-23 17:50:53 +02:00
const fns: { fn: TransactionCallback; uuid: string }[] = [];
2023-01-18 14:59:10 +02:00
let done = false;
2023-01-23 17:50:53 +02:00
let resume: null | (() => void) = null;
2023-01-18 14:59:10 +02:00
const e = new EventEmitter();
e.on('spawn', (uuid, cb) => {
fns.push({ fn: cb, uuid });
2023-01-23 17:50:53 +02:00
resume?.();
2023-01-18 14:59:10 +02:00
});
e.on('close', () => {
e.removeAllListeners('rollback');
e.removeAllListeners('spawn');
2023-01-18 14:59:10 +02:00
done = true;
2023-01-23 17:50:53 +02:00
resume?.();
2023-01-18 14:59:10 +02:00
});
strapi.db.transaction(async ({ trx, rollback }) => {
e.once('rollback', async () => {
e.removeAllListeners('close');
e.removeAllListeners('spawn');
try {
await rollback();
e.emit('rollback_completed');
} catch {
e.emit('rollback_failed');
} finally {
done = true;
resume?.();
}
2023-01-23 17:50:53 +02:00
});
2023-01-18 14:59:10 +02:00
while (!done) {
while (fns.length) {
const item = fns.shift();
if (item) {
const { fn, uuid } = item;
try {
const res = await fn(trx);
e.emit(uuid, { data: res });
} catch (error) {
e.emit(uuid, { error });
}
}
}
if (!done && !fns.length) {
// eslint-disable-next-line @typescript-eslint/no-loop-func
await new Promise<void>((resolve) => {
resume = resolve;
});
}
}
});
return {
2023-01-23 17:50:53 +02:00
async attach<T = undefined>(callback: TransactionCallback): Promise<T | undefined> {
2023-01-18 14:59:10 +02:00
const uuid = randomUUID();
e.emit('spawn', uuid, callback);
return new Promise<T | undefined>((resolve, reject) => {
e.on(uuid, ({ data, error }) => {
if (data) {
resolve(data);
}
if (error) {
2023-01-23 12:38:30 +02:00
reject(error);
2023-01-18 14:59:10 +02:00
}
resolve(undefined);
});
});
},
2023-01-23 17:50:53 +02:00
end() {
2023-01-18 14:59:10 +02:00
return e.emit('close');
},
2023-01-23 17:50:53 +02:00
rollback() {
return new Promise<boolean>((resolve) => {
e.emit('rollback');
e.once('rollback_failed', () => resolve(false));
e.once('rollback_completed', () => resolve(true));
});
2023-01-23 17:50:53 +02:00
},
2023-01-18 14:59:10 +02:00
};
};