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

View File

@ -34,6 +34,7 @@
"./translations/index.ts",
"./types/index.ts",
"./utils/index.ts",
"./workflow/index.ts",
"./workspace/index.ts"
]
},
@ -44,6 +45,7 @@
"translations",
"types",
"utils",
"workflow",
"workspace"
]
}

View File

@ -24,6 +24,8 @@
"{projectRoot}/types/dist",
"{projectRoot}/utils/package.json",
"{projectRoot}/utils/dist",
"{projectRoot}/workflow/package.json",
"{projectRoot}/workflow/dist",
"{projectRoot}/workspace/package.json",
"{projectRoot}/workspace/dist"
]

View File

@ -0,0 +1,15 @@
/*
* _____ _
*|_ _|_ _____ _ __ | |_ _ _
* | | \ \ /\ / / _ \ '_ \| __| | | | Auto-generated file
* | | \ V V / __/ | | | |_| |_| | Any edits to this will be overridden
* |_| \_/\_/ \___|_| |_|\__|\__, |
* |___/
*/
export type {
WorkflowRunStepInfo,
WorkflowRunStepInfos,
} from './types/WorkflowRunStateStepInfos';
export { StepStatus } from './types/WorkflowRunStateStepInfos';
export { getWorkflowRunContext } from './utils/getWorkflowRunContext';

View File

@ -0,0 +1,15 @@
export enum StepStatus {
NOT_STARTED = 'NOT_STARTED',
RUNNING = 'RUNNING',
SUCCESS = 'SUCCESS',
FAILED = 'FAILED',
PENDING = 'PENDING',
}
export type WorkflowRunStepInfo = {
result?: object;
error?: string;
status: StepStatus;
};
export type WorkflowRunStepInfos = Record<string, WorkflowRunStepInfo>;

View File

@ -0,0 +1,37 @@
import { getWorkflowRunContext } from '@/workflow/utils/getWorkflowRunContext';
import {
StepStatus,
WorkflowRunStepInfos,
} from '@/workflow/types/WorkflowRunStateStepInfos';
describe('getWorkflowRunContext', () => {
it('returns a context with only steps that have a defined result', () => {
const stepInfos: WorkflowRunStepInfos = {
step1: { result: { res: 'value1' }, status: StepStatus.SUCCESS },
step2: { result: {}, status: StepStatus.SUCCESS },
step3: { status: StepStatus.NOT_STARTED },
step4: { result: { res: 0 }, status: StepStatus.SUCCESS },
step5: { result: { res: undefined }, status: StepStatus.SUCCESS },
};
const context = getWorkflowRunContext(stepInfos);
expect(context).toEqual({
step1: { res: 'value1' },
step2: {},
step4: { res: 0 },
step5: { res: undefined },
});
});
it('returns an empty object when no step has a defined result', () => {
const stepInfos: WorkflowRunStepInfos = {
step1: { status: StepStatus.NOT_STARTED },
step2: { status: StepStatus.NOT_STARTED },
};
const context = getWorkflowRunContext(stepInfos);
expect(context).toEqual({});
});
});

View File

@ -0,0 +1,12 @@
import { isDefined } from '@/utils';
import { WorkflowRunStepInfos } from '@/workflow/types/WorkflowRunStateStepInfos';
export const getWorkflowRunContext = (
stepInfos: WorkflowRunStepInfos,
): Record<string, unknown> => {
return Object.fromEntries(
Object.entries(stepInfos)
.filter(([, value]) => isDefined(value?.['result']))
.map(([key, value]) => [key, value?.['result']]),
);
};

View File

@ -0,0 +1,4 @@
{
"main": "dist/twenty-shared-workflow.cjs.js",
"module": "dist/twenty-shared-workflow.esm.js"
}