feat(workflow): soft-ref core workflow/version (backfill + dual-write) (#22821)

Replaces the shared-UUID model (core row reuses the workspace record id)
with a **soft-ref**: the workspace `workflow`/`workflowVersion` records
carry a nullable `coreWorkflowId`/`coreWorkflowVersionId` pointing to
their **own-id** core rows. This removes the assumption that workspace
record ids are globally unique - which is false, since prefilled/seeded
workflows share ids across workspaces. Supersedes #22776.

## In this PR
**Soft-ref columns (foundation):**
- **twenty-shared** `STANDARD_OBJECTS`:
`workflowVersion.coreWorkflowVersionId` + `workflow.coreWorkflowId` (+
snapshot test).
- **compute utils**: both as system, nullable UUID fields.
- **entity classes**: the bare fields.

**Version soft-ref sync:**
- Core `workflowVersion` rows get their own id, derived
deterministically from `workspaceId + record id` (uuidv5). Deterministic
so the upsert is idempotent: a failed write-back re-derives the same id
and self-heals instead of orphaning rows or colliding on the
one-active-per-workflow index.
- Sync = find-or-create keyed on the workspace record's
`coreWorkflowVersionId`, then write the core id back onto the workspace
record.
- Migrating over pre-soft-ref data: purges any core row whose id equals
the workspace record id before recreating, so old shared-UUID rows
aren't orphaned.
- Version dual-write listener reworked: delete is keyed by the core id
read off `before.coreWorkflowVersionId`.

Verified on a fresh `database:reset` (columns materialize, backfill
produces deterministic own-id rows linked back, idempotent re-run), a
simulated old shared-UUID state (stale rows purged, records re-linked),
and a simulated write-back failure (retry re-links to the same id, no
orphan, active-version index intact).

## Next steps (follow-up work, not in this PR)
1. Workflow-side soft-ref sync mirroring the version side (service,
module, dual-write listener, backfill command).
2. Workspace command to add the two columns to existing workspaces.

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/22821?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
This commit is contained in:
Thomas Trompette
2026-07-15 17:50:00 +02:00
committed by GitHub
parent edd35c79d9
commit f67eb60c57
9 changed files with 469 additions and 331 deletions
@@ -3,18 +3,26 @@ import { InjectRepository } from '@nestjs/typeorm';
import { isDefined } from 'twenty-shared/utils';
import { In, Repository } from 'typeorm';
import { v4 as uuidv4 } from 'uuid';
import { v4 as uuidv4, v5 as uuidv5 } from 'uuid';
import {
WorkflowVersionEntity,
WorkflowVersionStatus,
} from 'src/engine/core-modules/workflow/entities/workflow-version.entity';
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
import { InjectWorkspaceScopedRepository } from 'src/engine/twenty-orm/workspace-scoped-repository/inject-workspace-scoped-repository.decorator';
import { WorkspaceScopedRepository } from 'src/engine/twenty-orm/workspace-scoped-repository/workspace-scoped-repository';
import { buildSystemAuthContext } from 'src/engine/twenty-orm/utils/build-system-auth-context.util';
import { WorkspaceCacheService } from 'src/engine/workspace-cache/services/workspace-cache.service';
import { type WorkflowVersionWorkspaceEntity } from 'src/modules/workflow/common/standard-objects/workflow-version.workspace-entity';
// Deriving the core id deterministically from workspaceId + record id makes the
// upsert idempotent across retries: a failed write-back re-derives the same id
// instead of orphaning a row or hitting the one-active-per-workflow index.
const CORE_WORKFLOW_VERSION_ID_NAMESPACE =
'f4988927-0a5c-453a-a262-0bd136d7fdaf';
@Injectable()
export class WorkflowVersionCoreSyncService {
constructor(
@@ -22,6 +30,7 @@ export class WorkflowVersionCoreSyncService {
private readonly workflowVersionRepository: WorkspaceScopedRepository<WorkflowVersionEntity>,
@InjectRepository(WorkspaceEntity)
private readonly workspaceRepository: Repository<WorkspaceEntity>,
private readonly globalWorkspaceOrmManager: GlobalWorkspaceOrmManager,
private readonly workspaceCacheService: WorkspaceCacheService,
) {}
@@ -35,10 +44,25 @@ export class WorkflowVersionCoreSyncService {
const applicationId = await this.getCustomApplicationIdOrThrow(workspaceId);
await this.workflowVersionRepository.upsert(
workspaceId,
workflowVersions.map((workflowVersion) => ({
id: workflowVersion.id,
const coreVersionIdByWorkspaceRecordId = new Map<string, string>();
const coreRows = workflowVersions.map((workflowVersion) => {
const coreWorkflowVersionId =
workflowVersion.coreWorkflowVersionId ??
uuidv5(
`${workspaceId}:${workflowVersion.id}`,
CORE_WORKFLOW_VERSION_ID_NAMESPACE,
);
if (!isDefined(workflowVersion.coreWorkflowVersionId)) {
coreVersionIdByWorkspaceRecordId.set(
workflowVersion.id,
coreWorkflowVersionId,
);
}
return {
id: coreWorkflowVersionId,
workflowId: workflowVersion.workflowId,
triggers: isDefined(workflowVersion.trigger)
? [workflowVersion.trigger]
@@ -47,13 +71,82 @@ export class WorkflowVersionCoreSyncService {
status: workflowVersion.status as unknown as WorkflowVersionStatus,
universalIdentifier: uuidv4(),
applicationId,
})),
['id'],
};
});
await this.purgeSharedIdCoreRows(
workspaceId,
Array.from(coreVersionIdByWorkspaceRecordId.keys()),
);
await this.workflowVersionRepository.upsert(workspaceId, coreRows, ['id']);
await this.writeBackCoreVersionIds(
workspaceId,
coreVersionIdByWorkspaceRecordId,
);
await this.invalidateAutomatedTriggerMaps(workspaceId);
}
async deleteFromCore(
workspaceId: string,
coreWorkflowVersionIds: string[],
): Promise<void> {
if (coreWorkflowVersionIds.length === 0) {
return;
}
await this.workflowVersionRepository.delete(workspaceId, {
id: In(coreWorkflowVersionIds),
});
await this.invalidateAutomatedTriggerMaps(workspaceId);
}
// The pre-soft-ref model wrote core rows with id === workspace record id.
// Delete those before creating own-id rows, otherwise re-migration orphans
// them and a second active row collides on the one-active-per-workflow index.
private async purgeSharedIdCoreRows(
workspaceId: string,
workspaceRecordIds: string[],
): Promise<void> {
if (workspaceRecordIds.length === 0) {
return;
}
await this.workflowVersionRepository.delete(workspaceId, {
id: In(workspaceRecordIds),
});
}
private async writeBackCoreVersionIds(
workspaceId: string,
coreVersionIdByWorkspaceRecordId: Map<string, string>,
): Promise<void> {
if (coreVersionIdByWorkspaceRecordId.size === 0) {
return;
}
await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => {
const workflowVersionRepository =
await this.globalWorkspaceOrmManager.getRepository<WorkflowVersionWorkspaceEntity>(
workspaceId,
'workflowVersion',
{ shouldBypassPermissionChecks: true },
);
for (const [
workspaceRecordId,
coreWorkflowVersionId,
] of coreVersionIdByWorkspaceRecordId) {
await workflowVersionRepository.update(workspaceRecordId, {
coreWorkflowVersionId,
});
}
}, buildSystemAuthContext(workspaceId));
}
private async getCustomApplicationIdOrThrow(
workspaceId: string,
): Promise<string> {
@@ -71,21 +164,6 @@ export class WorkflowVersionCoreSyncService {
return workspace.workspaceCustomApplicationId;
}
async deleteFromCore(
workspaceId: string,
workflowVersionIds: string[],
): Promise<void> {
if (workflowVersionIds.length === 0) {
return;
}
await this.workflowVersionRepository.delete(workspaceId, {
id: In(workflowVersionIds),
});
await this.invalidateAutomatedTriggerMaps(workspaceId);
}
private async invalidateAutomatedTriggerMaps(
workspaceId: string,
): Promise<void> {
@@ -2514,10 +2514,13 @@ exports[`getStandardObjectMetadataRelatedEntityIds should return standard object
"workflow": {
"fields": {
"attachments": {
"id": "00000000-0000-0000-0000-000000000766",
"id": "00000000-0000-0000-0000-000000000767",
},
"automatedTriggers": {
"id": "00000000-0000-0000-0000-000000000764",
"id": "00000000-0000-0000-0000-000000000765",
},
"coreWorkflowId": {
"id": "00000000-0000-0000-0000-000000000761",
},
"createdAt": {
"id": "00000000-0000-0000-0000-000000000752",
@@ -2541,16 +2544,16 @@ exports[`getStandardObjectMetadataRelatedEntityIds should return standard object
"id": "00000000-0000-0000-0000-000000000757",
},
"runs": {
"id": "00000000-0000-0000-0000-000000000763",
"id": "00000000-0000-0000-0000-000000000764",
},
"searchVector": {
"id": "00000000-0000-0000-0000-000000000758",
},
"statuses": {
"id": "00000000-0000-0000-0000-000000000761",
"id": "00000000-0000-0000-0000-000000000762",
},
"timelineActivities": {
"id": "00000000-0000-0000-0000-000000000765",
"id": "00000000-0000-0000-0000-000000000766",
},
"updatedAt": {
"id": "00000000-0000-0000-0000-000000000753",
@@ -2559,32 +2562,32 @@ exports[`getStandardObjectMetadataRelatedEntityIds should return standard object
"id": "00000000-0000-0000-0000-000000000756",
},
"versions": {
"id": "00000000-0000-0000-0000-000000000762",
"id": "00000000-0000-0000-0000-000000000763",
},
},
"id": "00000000-0000-0000-0000-000000000774",
"id": "00000000-0000-0000-0000-000000000775",
"views": {
"allWorkflows": {
"id": "00000000-0000-0000-0000-000000000773",
"id": "00000000-0000-0000-0000-000000000774",
"viewFieldGroups": {},
"viewFields": {
"createdBy": {
"id": "00000000-0000-0000-0000-000000000770",
"id": "00000000-0000-0000-0000-000000000771",
},
"name": {
"id": "00000000-0000-0000-0000-000000000767",
},
"runs": {
"id": "00000000-0000-0000-0000-000000000772",
},
"statuses": {
"id": "00000000-0000-0000-0000-000000000768",
},
"updatedAt": {
"runs": {
"id": "00000000-0000-0000-0000-000000000773",
},
"statuses": {
"id": "00000000-0000-0000-0000-000000000769",
},
"updatedAt": {
"id": "00000000-0000-0000-0000-000000000770",
},
"versions": {
"id": "00000000-0000-0000-0000-000000000771",
"id": "00000000-0000-0000-0000-000000000772",
},
},
"viewGroups": {},
@@ -2594,79 +2597,79 @@ exports[`getStandardObjectMetadataRelatedEntityIds should return standard object
"workflowAutomatedTrigger": {
"fields": {
"createdAt": {
"id": "00000000-0000-0000-0000-000000000776",
},
"createdBy": {
"id": "00000000-0000-0000-0000-000000000779",
},
"deletedAt": {
"id": "00000000-0000-0000-0000-000000000778",
},
"id": {
"id": "00000000-0000-0000-0000-000000000775",
},
"position": {
"id": "00000000-0000-0000-0000-000000000781",
},
"searchVector": {
"id": "00000000-0000-0000-0000-000000000782",
},
"settings": {
"id": "00000000-0000-0000-0000-000000000784",
},
"type": {
"id": "00000000-0000-0000-0000-000000000783",
},
"updatedAt": {
"id": "00000000-0000-0000-0000-000000000777",
},
"updatedBy": {
"createdBy": {
"id": "00000000-0000-0000-0000-000000000780",
},
"workflow": {
"deletedAt": {
"id": "00000000-0000-0000-0000-000000000779",
},
"id": {
"id": "00000000-0000-0000-0000-000000000776",
},
"position": {
"id": "00000000-0000-0000-0000-000000000782",
},
"searchVector": {
"id": "00000000-0000-0000-0000-000000000783",
},
"settings": {
"id": "00000000-0000-0000-0000-000000000785",
},
"type": {
"id": "00000000-0000-0000-0000-000000000784",
},
"updatedAt": {
"id": "00000000-0000-0000-0000-000000000778",
},
"updatedBy": {
"id": "00000000-0000-0000-0000-000000000781",
},
"workflow": {
"id": "00000000-0000-0000-0000-000000000786",
},
},
"id": "00000000-0000-0000-0000-000000000797",
"id": "00000000-0000-0000-0000-000000000798",
"views": {
"allWorkflowAutomatedTriggers": {
"id": "00000000-0000-0000-0000-000000000789",
"id": "00000000-0000-0000-0000-000000000790",
"viewFieldGroups": {},
"viewFields": {
"createdAt": {
"id": "00000000-0000-0000-0000-000000000788",
"id": "00000000-0000-0000-0000-000000000789",
},
"type": {
"id": "00000000-0000-0000-0000-000000000786",
"id": "00000000-0000-0000-0000-000000000787",
},
"workflow": {
"id": "00000000-0000-0000-0000-000000000787",
"id": "00000000-0000-0000-0000-000000000788",
},
},
"viewGroups": {},
},
"workflowAutomatedTriggerRecordPageFields": {
"id": "00000000-0000-0000-0000-000000000796",
"id": "00000000-0000-0000-0000-000000000797",
"viewFieldGroups": {
"general": {
"id": "00000000-0000-0000-0000-000000000794",
"id": "00000000-0000-0000-0000-000000000795",
},
"system": {
"id": "00000000-0000-0000-0000-000000000795",
"id": "00000000-0000-0000-0000-000000000796",
},
},
"viewFields": {
"createdAt": {
"id": "00000000-0000-0000-0000-000000000792",
},
"createdBy": {
"id": "00000000-0000-0000-0000-000000000793",
},
"createdBy": {
"id": "00000000-0000-0000-0000-000000000794",
},
"type": {
"id": "00000000-0000-0000-0000-000000000790",
"id": "00000000-0000-0000-0000-000000000791",
},
"workflow": {
"id": "00000000-0000-0000-0000-000000000791",
"id": "00000000-0000-0000-0000-000000000792",
},
},
"viewGroups": {},
@@ -2676,134 +2679,134 @@ exports[`getStandardObjectMetadataRelatedEntityIds should return standard object
"workflowRun": {
"fields": {
"createdAt": {
"id": "00000000-0000-0000-0000-000000000799",
},
"createdBy": {
"id": "00000000-0000-0000-0000-000000000802",
},
"deletedAt": {
"id": "00000000-0000-0000-0000-000000000801",
},
"endedAt": {
"id": "00000000-0000-0000-0000-000000000811",
},
"enqueuedAt": {
"id": "00000000-0000-0000-0000-000000000809",
},
"id": {
"id": "00000000-0000-0000-0000-000000000798",
},
"name": {
"id": "00000000-0000-0000-0000-000000000806",
},
"position": {
"id": "00000000-0000-0000-0000-000000000804",
},
"searchVector": {
"id": "00000000-0000-0000-0000-000000000805",
},
"startedAt": {
"id": "00000000-0000-0000-0000-000000000810",
},
"state": {
"id": "00000000-0000-0000-0000-000000000813",
},
"status": {
"id": "00000000-0000-0000-0000-000000000812",
},
"stepLogs": {
"id": "00000000-0000-0000-0000-000000000814",
},
"timelineActivities": {
"id": "00000000-0000-0000-0000-000000000815",
},
"updatedAt": {
"id": "00000000-0000-0000-0000-000000000800",
},
"updatedBy": {
"createdBy": {
"id": "00000000-0000-0000-0000-000000000803",
},
"workflow": {
"id": "00000000-0000-0000-0000-000000000808",
"deletedAt": {
"id": "00000000-0000-0000-0000-000000000802",
},
"workflowVersion": {
"endedAt": {
"id": "00000000-0000-0000-0000-000000000812",
},
"enqueuedAt": {
"id": "00000000-0000-0000-0000-000000000810",
},
"id": {
"id": "00000000-0000-0000-0000-000000000799",
},
"name": {
"id": "00000000-0000-0000-0000-000000000807",
},
"position": {
"id": "00000000-0000-0000-0000-000000000805",
},
"searchVector": {
"id": "00000000-0000-0000-0000-000000000806",
},
"startedAt": {
"id": "00000000-0000-0000-0000-000000000811",
},
"state": {
"id": "00000000-0000-0000-0000-000000000814",
},
"status": {
"id": "00000000-0000-0000-0000-000000000813",
},
"stepLogs": {
"id": "00000000-0000-0000-0000-000000000815",
},
"timelineActivities": {
"id": "00000000-0000-0000-0000-000000000816",
},
"updatedAt": {
"id": "00000000-0000-0000-0000-000000000801",
},
"updatedBy": {
"id": "00000000-0000-0000-0000-000000000804",
},
"workflow": {
"id": "00000000-0000-0000-0000-000000000809",
},
"workflowVersion": {
"id": "00000000-0000-0000-0000-000000000808",
},
},
"id": "00000000-0000-0000-0000-000000000838",
"id": "00000000-0000-0000-0000-000000000839",
"views": {
"allWorkflowRuns": {
"id": "00000000-0000-0000-0000-000000000822",
"id": "00000000-0000-0000-0000-000000000823",
"viewFieldGroups": {},
"viewFields": {
"createdBy": {
"id": "00000000-0000-0000-0000-000000000820",
"id": "00000000-0000-0000-0000-000000000821",
},
"name": {
"id": "00000000-0000-0000-0000-000000000816",
},
"startedAt": {
"id": "00000000-0000-0000-0000-000000000819",
},
"status": {
"id": "00000000-0000-0000-0000-000000000818",
},
"workflow": {
"id": "00000000-0000-0000-0000-000000000817",
},
"startedAt": {
"id": "00000000-0000-0000-0000-000000000820",
},
"status": {
"id": "00000000-0000-0000-0000-000000000819",
},
"workflow": {
"id": "00000000-0000-0000-0000-000000000818",
},
"workflowVersion": {
"id": "00000000-0000-0000-0000-000000000821",
"id": "00000000-0000-0000-0000-000000000822",
},
},
"viewGroups": {},
},
"workflowRunRecordPageFields": {
"id": "00000000-0000-0000-0000-000000000837",
"id": "00000000-0000-0000-0000-000000000838",
"viewFieldGroups": {
"general": {
"id": "00000000-0000-0000-0000-000000000835",
"id": "00000000-0000-0000-0000-000000000836",
},
"system": {
"id": "00000000-0000-0000-0000-000000000836",
"id": "00000000-0000-0000-0000-000000000837",
},
},
"viewFields": {
"createdAt": {
"id": "00000000-0000-0000-0000-000000000828",
},
"createdBy": {
"id": "00000000-0000-0000-0000-000000000829",
},
"endedAt": {
"id": "00000000-0000-0000-0000-000000000827",
},
"enqueuedAt": {
"createdBy": {
"id": "00000000-0000-0000-0000-000000000830",
},
"startedAt": {
"id": "00000000-0000-0000-0000-000000000826",
"endedAt": {
"id": "00000000-0000-0000-0000-000000000828",
},
"state": {
"enqueuedAt": {
"id": "00000000-0000-0000-0000-000000000831",
},
"status": {
"id": "00000000-0000-0000-0000-000000000823",
"startedAt": {
"id": "00000000-0000-0000-0000-000000000827",
},
"timelineActivities": {
"id": "00000000-0000-0000-0000-000000000834",
},
"updatedAt": {
"state": {
"id": "00000000-0000-0000-0000-000000000832",
},
"updatedBy": {
"id": "00000000-0000-0000-0000-000000000833",
},
"workflow": {
"status": {
"id": "00000000-0000-0000-0000-000000000824",
},
"workflowVersion": {
"timelineActivities": {
"id": "00000000-0000-0000-0000-000000000835",
},
"updatedAt": {
"id": "00000000-0000-0000-0000-000000000833",
},
"updatedBy": {
"id": "00000000-0000-0000-0000-000000000834",
},
"workflow": {
"id": "00000000-0000-0000-0000-000000000825",
},
"workflowVersion": {
"id": "00000000-0000-0000-0000-000000000826",
},
},
"viewGroups": {},
},
@@ -2811,116 +2814,119 @@ exports[`getStandardObjectMetadataRelatedEntityIds should return standard object
},
"workflowVersion": {
"fields": {
"coreWorkflowVersionId": {
"id": "00000000-0000-0000-0000-000000000854",
},
"createdAt": {
"id": "00000000-0000-0000-0000-000000000840",
},
"createdBy": {
"id": "00000000-0000-0000-0000-000000000843",
},
"deletedAt": {
"id": "00000000-0000-0000-0000-000000000842",
},
"id": {
"id": "00000000-0000-0000-0000-000000000839",
},
"name": {
"id": "00000000-0000-0000-0000-000000000847",
},
"position": {
"id": "00000000-0000-0000-0000-000000000845",
},
"runs": {
"id": "00000000-0000-0000-0000-000000000851",
},
"searchVector": {
"id": "00000000-0000-0000-0000-000000000846",
},
"status": {
"id": "00000000-0000-0000-0000-000000000850",
},
"steps": {
"id": "00000000-0000-0000-0000-000000000852",
},
"timelineActivities": {
"id": "00000000-0000-0000-0000-000000000853",
},
"trigger": {
"id": "00000000-0000-0000-0000-000000000849",
},
"updatedAt": {
"id": "00000000-0000-0000-0000-000000000841",
},
"updatedBy": {
"createdBy": {
"id": "00000000-0000-0000-0000-000000000844",
},
"workflow": {
"deletedAt": {
"id": "00000000-0000-0000-0000-000000000843",
},
"id": {
"id": "00000000-0000-0000-0000-000000000840",
},
"name": {
"id": "00000000-0000-0000-0000-000000000848",
},
"position": {
"id": "00000000-0000-0000-0000-000000000846",
},
"runs": {
"id": "00000000-0000-0000-0000-000000000852",
},
"searchVector": {
"id": "00000000-0000-0000-0000-000000000847",
},
"status": {
"id": "00000000-0000-0000-0000-000000000851",
},
"steps": {
"id": "00000000-0000-0000-0000-000000000853",
},
"timelineActivities": {
"id": "00000000-0000-0000-0000-000000000855",
},
"trigger": {
"id": "00000000-0000-0000-0000-000000000850",
},
"updatedAt": {
"id": "00000000-0000-0000-0000-000000000842",
},
"updatedBy": {
"id": "00000000-0000-0000-0000-000000000845",
},
"workflow": {
"id": "00000000-0000-0000-0000-000000000849",
},
},
"id": "00000000-0000-0000-0000-000000000873",
"id": "00000000-0000-0000-0000-000000000875",
"views": {
"allWorkflowVersions": {
"id": "00000000-0000-0000-0000-000000000859",
"id": "00000000-0000-0000-0000-000000000861",
"viewFieldGroups": {},
"viewFields": {
"name": {
"id": "00000000-0000-0000-0000-000000000854",
},
"runs": {
"id": "00000000-0000-0000-0000-000000000858",
},
"status": {
"id": "00000000-0000-0000-0000-000000000856",
},
"runs": {
"id": "00000000-0000-0000-0000-000000000860",
},
"status": {
"id": "00000000-0000-0000-0000-000000000858",
},
"updatedAt": {
"id": "00000000-0000-0000-0000-000000000857",
"id": "00000000-0000-0000-0000-000000000859",
},
"workflow": {
"id": "00000000-0000-0000-0000-000000000855",
"id": "00000000-0000-0000-0000-000000000857",
},
},
"viewGroups": {},
},
"workflowVersionRecordPageFields": {
"id": "00000000-0000-0000-0000-000000000872",
"id": "00000000-0000-0000-0000-000000000874",
"viewFieldGroups": {
"general": {
"id": "00000000-0000-0000-0000-000000000870",
"id": "00000000-0000-0000-0000-000000000872",
},
"system": {
"id": "00000000-0000-0000-0000-000000000871",
"id": "00000000-0000-0000-0000-000000000873",
},
},
"viewFields": {
"createdAt": {
"id": "00000000-0000-0000-0000-000000000863",
},
"createdBy": {
"id": "00000000-0000-0000-0000-000000000865",
},
"runs": {
"id": "00000000-0000-0000-0000-000000000868",
},
"status": {
"id": "00000000-0000-0000-0000-000000000860",
},
"steps": {
"id": "00000000-0000-0000-0000-000000000864",
},
"timelineActivities": {
"id": "00000000-0000-0000-0000-000000000869",
},
"trigger": {
"id": "00000000-0000-0000-0000-000000000862",
},
"updatedAt": {
"id": "00000000-0000-0000-0000-000000000866",
},
"updatedBy": {
"createdBy": {
"id": "00000000-0000-0000-0000-000000000867",
},
"runs": {
"id": "00000000-0000-0000-0000-000000000870",
},
"status": {
"id": "00000000-0000-0000-0000-000000000862",
},
"steps": {
"id": "00000000-0000-0000-0000-000000000866",
},
"timelineActivities": {
"id": "00000000-0000-0000-0000-000000000871",
},
"trigger": {
"id": "00000000-0000-0000-0000-000000000864",
},
"updatedAt": {
"id": "00000000-0000-0000-0000-000000000868",
},
"updatedBy": {
"id": "00000000-0000-0000-0000-000000000869",
},
"workflow": {
"id": "00000000-0000-0000-0000-000000000861",
"id": "00000000-0000-0000-0000-000000000863",
},
},
"viewGroups": {},
@@ -2930,125 +2936,125 @@ exports[`getStandardObjectMetadataRelatedEntityIds should return standard object
"workspaceMember": {
"fields": {
"accountOwnerForCompanies": {
"id": "00000000-0000-0000-0000-000000000891",
},
"assignedTasks": {
"id": "00000000-0000-0000-0000-000000000889",
},
"avatarUrl": {
"id": "00000000-0000-0000-0000-000000000885",
},
"blocklist": {
"id": "00000000-0000-0000-0000-000000000893",
},
"calendarEventParticipants": {
"id": "00000000-0000-0000-0000-000000000894",
"assignedTasks": {
"id": "00000000-0000-0000-0000-000000000891",
},
"calendarStartDay": {
"id": "00000000-0000-0000-0000-000000000899",
},
"colorScheme": {
"id": "00000000-0000-0000-0000-000000000883",
},
"createdAt": {
"id": "00000000-0000-0000-0000-000000000875",
},
"createdBy": {
"id": "00000000-0000-0000-0000-000000000878",
},
"dateFormat": {
"id": "00000000-0000-0000-0000-000000000897",
},
"deletedAt": {
"id": "00000000-0000-0000-0000-000000000877",
},
"id": {
"id": "00000000-0000-0000-0000-000000000874",
},
"jobTitle": {
"avatarUrl": {
"id": "00000000-0000-0000-0000-000000000887",
},
"locale": {
"id": "00000000-0000-0000-0000-000000000884",
},
"messageParticipants": {
"id": "00000000-0000-0000-0000-000000000892",
},
"name": {
"id": "00000000-0000-0000-0000-000000000882",
},
"numberFormat": {
"id": "00000000-0000-0000-0000-000000000900",
},
"ownedOpportunities": {
"id": "00000000-0000-0000-0000-000000000890",
},
"position": {
"id": "00000000-0000-0000-0000-000000000880",
},
"searchVector": {
"id": "00000000-0000-0000-0000-000000000881",
},
"timeFormat": {
"id": "00000000-0000-0000-0000-000000000898",
},
"timeZone": {
"id": "00000000-0000-0000-0000-000000000896",
},
"timelineActivities": {
"blocklist": {
"id": "00000000-0000-0000-0000-000000000895",
},
"updatedAt": {
"id": "00000000-0000-0000-0000-000000000876",
"calendarEventParticipants": {
"id": "00000000-0000-0000-0000-000000000896",
},
"updatedBy": {
"calendarStartDay": {
"id": "00000000-0000-0000-0000-000000000901",
},
"colorScheme": {
"id": "00000000-0000-0000-0000-000000000885",
},
"createdAt": {
"id": "00000000-0000-0000-0000-000000000877",
},
"createdBy": {
"id": "00000000-0000-0000-0000-000000000880",
},
"dateFormat": {
"id": "00000000-0000-0000-0000-000000000899",
},
"deletedAt": {
"id": "00000000-0000-0000-0000-000000000879",
},
"userEmail": {
"id": {
"id": "00000000-0000-0000-0000-000000000876",
},
"jobTitle": {
"id": "00000000-0000-0000-0000-000000000889",
},
"locale": {
"id": "00000000-0000-0000-0000-000000000886",
},
"userId": {
"messageParticipants": {
"id": "00000000-0000-0000-0000-000000000894",
},
"name": {
"id": "00000000-0000-0000-0000-000000000884",
},
"numberFormat": {
"id": "00000000-0000-0000-0000-000000000902",
},
"ownedOpportunities": {
"id": "00000000-0000-0000-0000-000000000892",
},
"position": {
"id": "00000000-0000-0000-0000-000000000882",
},
"searchVector": {
"id": "00000000-0000-0000-0000-000000000883",
},
"timeFormat": {
"id": "00000000-0000-0000-0000-000000000900",
},
"timeZone": {
"id": "00000000-0000-0000-0000-000000000898",
},
"timelineActivities": {
"id": "00000000-0000-0000-0000-000000000897",
},
"updatedAt": {
"id": "00000000-0000-0000-0000-000000000878",
},
"updatedBy": {
"id": "00000000-0000-0000-0000-000000000881",
},
"userEmail": {
"id": "00000000-0000-0000-0000-000000000888",
},
"userId": {
"id": "00000000-0000-0000-0000-000000000890",
},
},
"id": "00000000-0000-0000-0000-000000000913",
"id": "00000000-0000-0000-0000-000000000915",
"views": {
"allWorkspaceMembers": {
"id": "00000000-0000-0000-0000-000000000912",
"id": "00000000-0000-0000-0000-000000000914",
"viewFieldGroups": {},
"viewFields": {
"assignedTasks": {
"id": "00000000-0000-0000-0000-000000000911",
"id": "00000000-0000-0000-0000-000000000913",
},
"avatarUrl": {
"id": "00000000-0000-0000-0000-000000000903",
},
"colorScheme": {
"id": "00000000-0000-0000-0000-000000000904",
},
"createdAt": {
"id": "00000000-0000-0000-0000-000000000909",
},
"dateFormat": {
"id": "00000000-0000-0000-0000-000000000907",
},
"locale": {
"id": "00000000-0000-0000-0000-000000000905",
},
"name": {
"id": "00000000-0000-0000-0000-000000000901",
},
"ownedOpportunities": {
"id": "00000000-0000-0000-0000-000000000910",
},
"timeFormat": {
"id": "00000000-0000-0000-0000-000000000908",
},
"timeZone": {
"colorScheme": {
"id": "00000000-0000-0000-0000-000000000906",
},
"createdAt": {
"id": "00000000-0000-0000-0000-000000000911",
},
"dateFormat": {
"id": "00000000-0000-0000-0000-000000000909",
},
"locale": {
"id": "00000000-0000-0000-0000-000000000907",
},
"name": {
"id": "00000000-0000-0000-0000-000000000903",
},
"ownedOpportunities": {
"id": "00000000-0000-0000-0000-000000000912",
},
"timeFormat": {
"id": "00000000-0000-0000-0000-000000000910",
},
"timeZone": {
"id": "00000000-0000-0000-0000-000000000908",
},
"userEmail": {
"id": "00000000-0000-0000-0000-000000000902",
"id": "00000000-0000-0000-0000-000000000904",
},
},
"viewGroups": {},
@@ -136,6 +136,24 @@ export const buildWorkflowStandardFlatFieldMetadatas = ({
twentyStandardApplicationId,
now,
}),
coreWorkflowId: createStandardFieldFlatMetadata({
objectName,
workspaceId,
context: {
fieldName: 'coreWorkflowId',
type: FieldMetadataType.UUID,
label: i18nLabel(msg`Core workflow id`),
description: i18nLabel(msg`Reference to the core workflow row`),
icon: 'IconSettingsAutomation',
isSystem: true,
isNullable: true,
isUIEditable: false,
},
standardObjectMetadataRelatedEntityIds,
dependencyFlatEntityMaps,
twentyStandardApplicationId,
now,
}),
statuses: createStandardFieldFlatMetadata({
objectName,
workspaceId,
@@ -206,6 +206,24 @@ export const buildWorkflowVersionStandardFlatFieldMetadatas = ({
twentyStandardApplicationId,
now,
}),
coreWorkflowVersionId: createStandardFieldFlatMetadata({
objectName,
workspaceId,
context: {
fieldName: 'coreWorkflowVersionId',
type: FieldMetadataType.UUID,
label: i18nLabel(msg`Core workflow version id`),
description: i18nLabel(msg`Reference to the core workflowVersion row`),
icon: 'IconSettingsAutomation',
isSystem: true,
isNullable: true,
isUIEditable: false,
},
standardObjectMetadataRelatedEntityIds,
dependencyFlatEntityMaps,
twentyStandardApplicationId,
now,
}),
status: createStandardFieldFlatMetadata({
objectName,
workspaceId,
@@ -17,6 +17,7 @@ export class WorkflowVersionWorkspaceEntity extends BaseWorkspaceEntity {
name: string | null;
trigger: WorkflowTrigger | null;
steps: WorkflowAction[] | null;
coreWorkflowVersionId: string | null;
status: WorkflowVersionStatus;
position: number;
searchVector: string;
@@ -17,6 +17,7 @@ export enum WorkflowStatus {
export class WorkflowWorkspaceEntity extends BaseWorkspaceEntity {
name: string | null;
lastPublishedVersionId: string | null;
coreWorkflowId: string | null;
statuses: WorkflowStatus[] | null;
position: number;
searchVector: string;
@@ -67,7 +67,9 @@ export class WorkflowVersionCoreDualWriteListener {
): Promise<void> {
await this.deleteFromCore(
batchEvent.workspaceId,
batchEvent.events.map((event) => event.properties.before.id),
batchEvent.events
.map((event) => event.properties.before.coreWorkflowVersionId)
.filter(isDefined),
);
}
@@ -79,7 +81,9 @@ export class WorkflowVersionCoreDualWriteListener {
): Promise<void> {
await this.deleteFromCore(
batchEvent.workspaceId,
batchEvent.events.map((event) => event.properties.before.id),
batchEvent.events
.map((event) => event.properties.before.coreWorkflowVersionId)
.filter(isDefined),
);
}
@@ -105,7 +109,7 @@ export class WorkflowVersionCoreDualWriteListener {
private async deleteFromCore(
workspaceId: string | undefined,
workflowVersionIds: string[],
coreWorkflowVersionIds: string[],
): Promise<void> {
if (!isDefined(workspaceId)) {
return;
@@ -114,7 +118,7 @@ export class WorkflowVersionCoreDualWriteListener {
try {
await this.workflowVersionCoreSyncService.deleteFromCore(
workspaceId,
workflowVersionIds,
coreWorkflowVersionIds,
);
} catch (error) {
this.exceptionHandlerService.captureExceptions([error], {
@@ -2719,6 +2719,9 @@ exports[`STANDARD_OBJECTS universal identifiers should never change without an e
"automatedTriggers": {
"universalIdentifier": "20202020-3319-4234-a34c-117ecad2b8a9",
},
"coreWorkflowId": {
"universalIdentifier": "20202020-058a-42ad-8eb8-0662a5552aad",
},
"createdAt": {
"universalIdentifier": "0be9023c-cf26-56ef-8512-3c2104b849cc",
},
@@ -3024,6 +3027,9 @@ exports[`STANDARD_OBJECTS universal identifiers should never change without an e
},
"workflowVersion": {
"fields": {
"coreWorkflowVersionId": {
"universalIdentifier": "20202020-58b4-46e8-b6d2-f1f3c74cf7f4",
},
"createdAt": {
"universalIdentifier": "97b8d13e-ea41-5da4-90df-886ff017f584",
},
@@ -2162,6 +2162,9 @@ export const STANDARD_OBJECTS = {
lastPublishedVersionId: {
universalIdentifier: '20202020-326a-4fba-8639-3456c0a169e8',
},
coreWorkflowId: {
universalIdentifier: '20202020-058a-42ad-8eb8-0662a5552aad',
},
statuses: { universalIdentifier: '20202020-357c-4432-8c50-8c31b4a552d9' },
versions: { universalIdentifier: '20202020-9432-416e-8f3c-27ee3153d099' },
runs: { universalIdentifier: '20202020-759b-4340-b58b-e73595c4df4f' },
@@ -2402,6 +2405,9 @@ export const STANDARD_OBJECTS = {
},
runs: { universalIdentifier: '20202020-1d08-46df-901a-85045f18099a' },
steps: { universalIdentifier: '20202020-5988-4a64-b94a-1f9b7b989039' },
coreWorkflowVersionId: {
universalIdentifier: '20202020-58b4-46e8-b6d2-f1f3c74cf7f4',
},
timelineActivities: {
universalIdentifier: '20202020-fcb0-4695-b17e-3b43a421c633',
},