Use optimistic rendering when executing a workflow with manual trigger (#12695)

This PR adds optimistic rendering at two places:

- In the `runWorkflowVersion`, to create a workflow run entry as fast as
possible in the cache and render it immediately in the side panel.
- In the `ListenUpdatesEffect`, to be sure the cache is properly set; we
also need to set the record in the record store that's used in the
fields card.


## Before


https://github.com/user-attachments/assets/8b360ea9-c292-4e05-82a0-d2f12176bb6f

## After


https://github.com/user-attachments/assets/2d11023c-2ceb-4fa3-a951-187b9a0b5743

### With a slowed-down network


https://github.com/user-attachments/assets/7d2a592a-1ea7-455b-856f-bf3d9d905061

## Follow up

I will create next a PR to ensure the viewport is always set when we
know the dimensions of the nodes.
This commit is contained in:
Baptiste Devessier
2025-06-19 14:09:47 +02:00
committed by GitHub
parent a6b8830b91
commit dae282ca0f
13 changed files with 261 additions and 46 deletions

View File

@ -2,6 +2,7 @@ import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { v4 } from 'uuid';
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';
@ -39,9 +40,11 @@ export class WorkflowRunWorkspaceService {
async createWorkflowRun({
workflowVersionId,
createdBy,
workflowRunId,
}: {
workflowVersionId: string;
createdBy: ActorMetadata;
workflowRunId?: string;
}) {
const workspaceId =
this.scopedWorkspaceContextFactory.create()?.workspaceId;
@ -54,7 +57,7 @@ export class WorkflowRunWorkspaceService {
}
const workflowRunRepository =
await this.twentyORMGlobalManager.getRepositoryForWorkspace(
await this.twentyORMGlobalManager.getRepositoryForWorkspace<WorkflowRunWorkspaceEntity>(
workspaceId,
'workflowRun',
{ shouldBypassPermissionChecks: true },
@ -101,16 +104,19 @@ export class WorkflowRunWorkspaceService {
workspaceId,
});
return (
await workflowRunRepository.save({
name: `#${workflowRunCount + 1} - ${workflow.name}`,
workflowVersionId,
createdBy,
workflowId: workflow.id,
status: WorkflowRunStatus.NOT_STARTED,
position,
})
).id;
const workflowRun = workflowRunRepository.create({
id: workflowRunId ?? v4(),
name: `#${workflowRunCount + 1} - ${workflow.name}`,
workflowVersionId,
createdBy,
workflowId: workflow.id,
status: WorkflowRunStatus.NOT_STARTED,
position,
});
await workflowRunRepository.insert(workflowRun);
return workflowRun.id;
}
async startWorkflowRun({

View File

@ -26,11 +26,13 @@ export class WorkflowRunnerWorkspaceService {
workflowVersionId,
payload,
source,
workflowRunId: initialWorkflowRunId,
}: {
workspaceId: string;
workflowVersionId: string;
payload: object;
source: ActorMetadata;
workflowRunId?: string;
}) {
const canFeatureBeUsed =
await this.billingUsageService.canFeatureBeUsed(workspaceId);
@ -43,6 +45,7 @@ export class WorkflowRunnerWorkspaceService {
const workflowRunId =
await this.workflowRunWorkspaceService.createWorkflowRun({
workflowVersionId,
workflowRunId: initialWorkflowRunId,
createdBy: source,
});

View File

@ -63,10 +63,12 @@ export class WorkflowTriggerWorkspaceService {
workflowVersionId,
payload,
createdBy,
workflowRunId,
}: {
workflowVersionId: string;
payload: object;
createdBy: ActorMetadata;
workflowRunId?: string;
}) {
await this.workflowCommonWorkspaceService.getWorkflowVersionOrFail({
workflowVersionId,
@ -75,6 +77,7 @@ export class WorkflowTriggerWorkspaceService {
return this.workflowRunnerWorkspaceService.run({
workspaceId: this.getWorkspaceId(),
workflowRunId,
workflowVersionId,
payload,
source: createdBy,