22 branches 3 (#13181)

This PR does not produce any functional changes for our users. It
prepares the branches for workflows by:

- decommissioning `output` and `context` fields or `workflowRun` records
and use newly created `state` field from front-end and back-end
- use `stepStatus` computed by `back-end` in `front-end`
- add utils and types in `twenty-shared/workflow` (not completed, a
follow-up is scheduled
https://github.com/twentyhq/core-team-issues/issues/1211)
- add concurrency to `workflowQueue` message queue to avoid weird branch
execution when using forms in workflow branches
- add a WithLock decorator for better dev experience of
`CacheLockService.withLock` usage

Here is an example of such a workflow running (front branch display is
not yet done that's why it looks ugly) ->
https://discord.com/channels/1130383047699738754/1258024460238192691/1392897615171158098
This commit is contained in:
martmull
2025-07-16 11:16:04 +02:00
committed by GitHub
parent bf6330469b
commit 47386e92a3
47 changed files with 5124 additions and 6380 deletions
@@ -0,0 +1,48 @@
import { Inject } from '@nestjs/common';
import {
CacheLockOptions,
CacheLockService,
} from 'src/engine/core-modules/cache-lock/cache-lock.service';
export const WithLock = (
lockKeyParamPath: string,
options?: CacheLockOptions,
): MethodDecorator => {
const injectCacheLockService = Inject(CacheLockService);
return function (target, propertyKey, descriptor: PropertyDescriptor) {
injectCacheLockService(target, 'cacheLockService');
const originalMethod = descriptor.value;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
descriptor.value = async function (...args: any[]) {
const self = this as { cacheLockService: CacheLockService };
if (!self.cacheLockService) {
throw new Error('cacheLockService not available on instance');
}
if (typeof args[0] !== 'object') {
throw new Error(
`You must use one object parameter to use @WithLock decorator. Received ${args}`,
);
}
const key = args[0][lockKeyParamPath];
if (typeof key !== 'string') {
throw new Error(
`Could not resolve lock key from path "${lockKeyParamPath}" on first argument`,
);
}
return await self.cacheLockService.withLock(
() => originalMethod.apply(self, args),
key,
options,
);
};
};
};