22 branches data migration (#13006)

This PR does not produce any functional change

First step of the workflow branch feature

- add gather `workflowRun.output` and `workflowRun.context` into one
column `workflowRun.runContext`
- add a command to fill `runContext` from `output` and `context` in
existing records
- maintain `runContext` up to date during workflow runs
This commit is contained in:
martmull
2025-07-03 10:18:33 +02:00
committed by GitHub
parent 29dbd3fc25
commit 51cb35a27a
11 changed files with 387 additions and 6 deletions
@@ -118,6 +118,7 @@ export class RunWorkflowJob {
},
},
},
payload: triggerPayload,
});
await this.throttleExecution(workflowVersion.workflowId);
@@ -3,6 +3,7 @@ import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { v4 } from 'uuid';
import { isDefined } from 'twenty-shared/utils';
import { DatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/enums/database-event-action';
import { objectRecordChangedValues } from 'src/engine/core-modules/event-emitter/utils/object-record-changed-values';
@@ -16,6 +17,7 @@ import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.
import { WorkspaceEventEmitter } from 'src/engine/workspace-event-emitter/workspace-event-emitter';
import {
StepOutput,
WorkflowRunState,
WorkflowRunOutput,
WorkflowRunStatus,
WorkflowRunWorkspaceEntity,
@@ -26,6 +28,8 @@ import {
WorkflowRunException,
WorkflowRunExceptionCode,
} from 'src/modules/workflow/workflow-runner/exceptions/workflow-run.exception';
import { StepStatus } from 'src/modules/workflow/workflow-executor/types/workflow-run-step-info.type';
import { WorkflowVersionWorkspaceEntity } from 'src/modules/workflow/common/standard-objects/workflow-version.workspace-entity';
@Injectable()
export class WorkflowRunWorkspaceService {
@@ -120,6 +124,7 @@ export class WorkflowRunWorkspaceService {
workflowId: workflow.id,
status,
position,
state: this.getInitState(workflowVersion),
context,
});
@@ -132,10 +137,12 @@ export class WorkflowRunWorkspaceService {
workflowRunId,
workspaceId,
output,
payload,
}: {
workflowRunId: string;
workspaceId: string;
output: WorkflowRunOutput;
payload: object;
}) {
const workflowRunRepository =
await this.twentyORMGlobalManager.getRepositoryForWorkspace<WorkflowRunWorkspaceEntity>(
@@ -169,6 +176,17 @@ export class WorkflowRunWorkspaceService {
status: WorkflowRunStatus.RUNNING,
startedAt: new Date().toISOString(),
output,
state: {
...workflowRunToUpdate.state,
stepInfos: {
...workflowRunToUpdate.state?.stepInfos,
trigger: {
...workflowRunToUpdate.state?.stepInfos.trigger,
status: StepStatus.SUCCESS,
result: payload,
},
},
},
};
await workflowRunRepository.update(workflowRunToUpdate.id, partialUpdate);
@@ -215,13 +233,17 @@ export class WorkflowRunWorkspaceService {
...(workflowRunToUpdate.output ?? {}),
error,
},
state: {
...workflowRunToUpdate.state,
workflowRunError: error,
},
};
await workflowRunRepository.update(workflowRunToUpdate.id, partialUpdate);
await this.emitWorkflowRunUpdatedEvent({
workflowRunBefore: workflowRunToUpdate,
updatedFields: ['status', 'endedAt', 'output'],
updatedFields: ['status', 'endedAt', 'output', 'state'],
});
await this.metricsService.incrementCounter({
@@ -238,12 +260,14 @@ export class WorkflowRunWorkspaceService {
stepOutput,
workspaceId,
context,
stepStatus,
}: {
workflowRunId: string;
stepOutput: StepOutput;
workspaceId: string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
context: Record<string, any>;
stepStatus: StepStatus;
}) {
const workflowRunRepository =
await this.twentyORMGlobalManager.getRepositoryForWorkspace<WorkflowRunWorkspaceEntity>(
@@ -274,6 +298,17 @@ export class WorkflowRunWorkspaceService {
[stepOutput.id]: stepOutput.output,
},
},
state: {
...workflowRunToUpdate.state,
stepInfos: {
...workflowRunToUpdate.state?.stepInfos,
[stepOutput.id]: {
result: stepOutput.output?.result,
error: stepOutput.output?.error,
status: stepStatus,
},
},
},
context,
};
@@ -281,7 +316,7 @@ export class WorkflowRunWorkspaceService {
await this.emitWorkflowRunUpdatedEvent({
workflowRunBefore: workflowRunToUpdate,
updatedFields: ['context', 'output'],
updatedFields: ['context', 'output', 'state'],
});
}
@@ -334,13 +369,20 @@ export class WorkflowRunWorkspaceService {
steps: updatedSteps,
},
},
state: {
...workflowRunToUpdate.state,
flow: {
...(workflowRunToUpdate.state?.flow ?? {}),
steps: updatedSteps,
},
},
};
await workflowRunRepository.update(workflowRunToUpdate.id, partialUpdate);
await this.emitWorkflowRunUpdatedEvent({
workflowRunBefore: workflowRunToUpdate,
updatedFields: ['output'],
updatedFields: ['output', 'state'],
});
}
@@ -441,4 +483,31 @@ export class WorkflowRunWorkspaceService {
workspaceId,
});
}
private getInitState(
workflowVersion: WorkflowVersionWorkspaceEntity,
): WorkflowRunState | undefined {
if (
!isDefined(workflowVersion.trigger) ||
!isDefined(workflowVersion.steps)
) {
return undefined;
}
return {
flow: {
trigger: workflowVersion.trigger,
steps: workflowVersion.steps,
},
stepInfos: {
trigger: { status: StepStatus.NOT_STARTED },
...Object.fromEntries(
workflowVersion.steps.map((step) => [
step.id,
{ status: StepStatus.NOT_STARTED },
]),
),
},
};
}
}