fix(workflow): make prefilled workflow ids unique per workspace (#22800)
## Problem \`prefillWorkflows\` (run for every workspace on \`activateWorkspace\`) inserts workflows and versions with **hardcoded ids** (\`QUICK_LEAD_WORKFLOW_ID = 8b213cac...\`, etc.). So every workspace carries the same workflow/version record ids. Within a workspace schema that's harmless, but it means workspace record ids are **not unique across workspaces**, which: - breaks the workflowVersion backfill on the shared core table (surfaced as the \`IDX_WORKFLOW_VERSION_ONE_ACTIVE_PER_WORKFLOW\` duplicate-key error, since multiple workspaces claim the same active \`workflowId\`), and - collides on \`core.workflow\`/\`core.workflowVersion\` PKs once workflows migrate to core (the core row reuses the workspace record id), causing cross-workspace clobbering. ## Fix Derive the prefill ids **deterministically per workspace**: \`getWorkflowPrefillIds(workspaceId)\` returns \`v5(label:workspaceId, namespace)\` for each of the workflow/version/trigger ids. Deterministic (stable across the idempotent \`orIgnore\` re-runs) but unique per workspace. The command-menu-item prefill uses the same helper so its \`workflowVersionId\` reference stays consistent. Only affects **new** workspaces; existing workspaces keep their current ids (prefill is skipped on re-activation). ## Test Reset seeds two workspaces; both now get a Quick Lead workflow with a **distinct** v5-derived id (not the old \`8b213cac\`), and internal references stay consistent (\`version.workflowId == workflow.id\`, \`lastPublishedVersionId == version.id\`). Typecheck + lint clean. Companion to #22795 (which scopes the active index to workspace). Together they fix the backfill duplicate-id failures. <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/22800?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:
+4
-2
@@ -6,7 +6,7 @@ import { CommandMenuItemAvailabilityType } from 'src/engine/metadata-modules/com
|
||||
import { EngineComponentKey } from 'src/engine/metadata-modules/command-menu-item/enums/engine-component-key.enum';
|
||||
import { type FlatCommandMenuItem } from 'src/engine/metadata-modules/flat-command-menu-item/types/flat-command-menu-item.type';
|
||||
import { type WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service';
|
||||
import { QUICK_LEAD_WORKFLOW_VERSION_ID } from 'src/engine/workspace-manager/standard-objects-prefill-data/utils/prefill-workflows.util';
|
||||
import { getWorkflowPrefillIds } from 'src/engine/workspace-manager/standard-objects-prefill-data/utils/prefill-workflows.util';
|
||||
import { type WorkspaceMigrationValidateBuildAndRunService } from 'src/engine/workspace-manager/workspace-migration/services/workspace-migration-validate-build-and-run-service';
|
||||
|
||||
const QUICK_LEAD_COMMAND_MENU_ITEM_UNIVERSAL_IDENTIFIER =
|
||||
@@ -46,6 +46,8 @@ export const prefillWorkflowCommandMenuItems = async ({
|
||||
|
||||
const now = new Date().toISOString();
|
||||
|
||||
const { quickLeadWorkflowVersionId } = getWorkflowPrefillIds(workspaceId);
|
||||
|
||||
const quickLeadFlatCommandMenuItem: FlatCommandMenuItem = {
|
||||
id: v4(),
|
||||
universalIdentifier: QUICK_LEAD_COMMAND_MENU_ITEM_UNIVERSAL_IDENTIFIER,
|
||||
@@ -53,7 +55,7 @@ export const prefillWorkflowCommandMenuItems = async ({
|
||||
applicationUniversalIdentifier:
|
||||
workspaceCustomFlatApplication.universalIdentifier,
|
||||
workspaceId,
|
||||
workflowVersionId: QUICK_LEAD_WORKFLOW_VERSION_ID,
|
||||
workflowVersionId: quickLeadWorkflowVersionId,
|
||||
frontComponentId: null,
|
||||
frontComponentUniversalIdentifier: null,
|
||||
engineComponentKey: EngineComponentKey.TRIGGER_WORKFLOW_VERSION,
|
||||
|
||||
+43
-20
@@ -1,6 +1,7 @@
|
||||
import { FieldActorSource } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { type EntityManager } from 'typeorm';
|
||||
import { v5 } from 'uuid';
|
||||
|
||||
import { DatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/enums/database-event-action';
|
||||
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
@@ -13,15 +14,30 @@ import { generateFakeObjectRecordEvent } from 'src/modules/workflow/workflow-bui
|
||||
import { generateObjectRecordFields } from 'src/modules/workflow/workflow-builder/workflow-schema/utils/generate-object-record-fields';
|
||||
import { getCreateCompanyWhenAddingNewPersonCodeStepLogicFunctionIds } from 'src/engine/workspace-manager/standard-objects-prefill-data/utils/prefill-workflow-code-step-logic-functions.util';
|
||||
|
||||
export const QUICK_LEAD_WORKFLOW_ID = '8b213cac-a68b-4ffe-817a-3ec994e9932d';
|
||||
export const QUICK_LEAD_WORKFLOW_VERSION_ID =
|
||||
'ac67974f-c524-4288-9d88-af8515400b68';
|
||||
export const CREATE_COMPANY_WHEN_ADDING_NEW_PERSON_WORKFLOW_ID =
|
||||
'887c6c06-fbc5-4b45-8d6b-f7b6b0f40b12';
|
||||
export const CREATE_COMPANY_WHEN_ADDING_NEW_PERSON_WORKFLOW_VERSION_ID =
|
||||
'0f276d7e-a950-41ab-ad98-35e80753dc58';
|
||||
export const CREATE_COMPANY_WHEN_ADDING_NEW_PERSON_AUTOMATED_TRIGGER_ID =
|
||||
'c54f5990-13a3-4c3b-b75d-df09e7843036';
|
||||
const WORKFLOW_PREFILL_ID_NAMESPACE = '8b213cac-a68b-4ffe-817a-3ec994e9932d';
|
||||
|
||||
export const getWorkflowPrefillIds = (workspaceId: string) => ({
|
||||
quickLeadWorkflowId: v5(
|
||||
`quickLeadWorkflow:${workspaceId}`,
|
||||
WORKFLOW_PREFILL_ID_NAMESPACE,
|
||||
),
|
||||
quickLeadWorkflowVersionId: v5(
|
||||
`quickLeadWorkflowVersion:${workspaceId}`,
|
||||
WORKFLOW_PREFILL_ID_NAMESPACE,
|
||||
),
|
||||
createCompanyWorkflowId: v5(
|
||||
`createCompanyWorkflow:${workspaceId}`,
|
||||
WORKFLOW_PREFILL_ID_NAMESPACE,
|
||||
),
|
||||
createCompanyWorkflowVersionId: v5(
|
||||
`createCompanyWorkflowVersion:${workspaceId}`,
|
||||
WORKFLOW_PREFILL_ID_NAMESPACE,
|
||||
),
|
||||
createCompanyAutomatedTriggerId: v5(
|
||||
`createCompanyAutomatedTrigger:${workspaceId}`,
|
||||
WORKFLOW_PREFILL_ID_NAMESPACE,
|
||||
),
|
||||
});
|
||||
|
||||
export const prefillWorkflows = async (
|
||||
entityManager: EntityManager,
|
||||
@@ -30,6 +46,14 @@ export const prefillWorkflows = async (
|
||||
flatObjectMetadataMaps: FlatEntityMaps<FlatObjectMetadata>,
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>,
|
||||
) => {
|
||||
const {
|
||||
quickLeadWorkflowId,
|
||||
quickLeadWorkflowVersionId,
|
||||
createCompanyWorkflowId,
|
||||
createCompanyWorkflowVersionId,
|
||||
createCompanyAutomatedTriggerId,
|
||||
} = getWorkflowPrefillIds(workspaceId);
|
||||
|
||||
const {
|
||||
extractDomainLogicFunctionId,
|
||||
findMatchingCompanyByDomainLogicFunctionId,
|
||||
@@ -112,9 +136,9 @@ export const prefillWorkflows = async (
|
||||
.orIgnore()
|
||||
.values([
|
||||
{
|
||||
id: QUICK_LEAD_WORKFLOW_ID,
|
||||
id: quickLeadWorkflowId,
|
||||
name: 'Quick Lead',
|
||||
lastPublishedVersionId: QUICK_LEAD_WORKFLOW_VERSION_ID,
|
||||
lastPublishedVersionId: quickLeadWorkflowVersionId,
|
||||
statuses: ['ACTIVE'],
|
||||
position: 1,
|
||||
createdBySource: FieldActorSource.SYSTEM,
|
||||
@@ -126,10 +150,9 @@ export const prefillWorkflows = async (
|
||||
updatedByName: 'System',
|
||||
},
|
||||
{
|
||||
id: CREATE_COMPANY_WHEN_ADDING_NEW_PERSON_WORKFLOW_ID,
|
||||
id: createCompanyWorkflowId,
|
||||
name: 'Create company when adding a new person',
|
||||
lastPublishedVersionId:
|
||||
CREATE_COMPANY_WHEN_ADDING_NEW_PERSON_WORKFLOW_VERSION_ID,
|
||||
lastPublishedVersionId: createCompanyWorkflowVersionId,
|
||||
statuses: ['ACTIVE'],
|
||||
position: 2,
|
||||
createdBySource: FieldActorSource.SYSTEM,
|
||||
@@ -159,7 +182,7 @@ export const prefillWorkflows = async (
|
||||
.orIgnore()
|
||||
.values([
|
||||
{
|
||||
id: QUICK_LEAD_WORKFLOW_VERSION_ID,
|
||||
id: quickLeadWorkflowVersionId,
|
||||
name: 'v1',
|
||||
trigger: JSON.stringify({
|
||||
name: 'Launch manually',
|
||||
@@ -354,10 +377,10 @@ export const prefillWorkflows = async (
|
||||
]),
|
||||
status: 'ACTIVE',
|
||||
position: 1,
|
||||
workflowId: QUICK_LEAD_WORKFLOW_ID,
|
||||
workflowId: quickLeadWorkflowId,
|
||||
},
|
||||
{
|
||||
id: CREATE_COMPANY_WHEN_ADDING_NEW_PERSON_WORKFLOW_VERSION_ID,
|
||||
id: createCompanyWorkflowVersionId,
|
||||
name: 'v1',
|
||||
trigger: JSON.stringify({
|
||||
name: 'Record is created or updated',
|
||||
@@ -749,7 +772,7 @@ export const prefillWorkflows = async (
|
||||
]),
|
||||
status: 'ACTIVE',
|
||||
position: 2,
|
||||
workflowId: CREATE_COMPANY_WHEN_ADDING_NEW_PERSON_WORKFLOW_ID,
|
||||
workflowId: createCompanyWorkflowId,
|
||||
},
|
||||
])
|
||||
.returning('*')
|
||||
@@ -767,8 +790,8 @@ export const prefillWorkflows = async (
|
||||
.orIgnore()
|
||||
.values([
|
||||
{
|
||||
id: CREATE_COMPANY_WHEN_ADDING_NEW_PERSON_AUTOMATED_TRIGGER_ID,
|
||||
workflowId: CREATE_COMPANY_WHEN_ADDING_NEW_PERSON_WORKFLOW_ID,
|
||||
id: createCompanyAutomatedTriggerId,
|
||||
workflowId: createCompanyWorkflowId,
|
||||
type: 'DATABASE_EVENT',
|
||||
settings: {
|
||||
eventName: 'person.upserted',
|
||||
|
||||
+7
-3
@@ -8,11 +8,15 @@ import {
|
||||
} from 'test/integration/graphql/suites/workflow/utils/workflow-run-test.util';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import { SEED_APPLE_WORKSPACE_ID } from 'src/engine/workspace-manager/dev-seeder/core/constants/seeder-workspaces.constant';
|
||||
import { getWorkflowPrefillIds } from 'src/engine/workspace-manager/standard-objects-prefill-data/utils/prefill-workflows.util';
|
||||
|
||||
const client = request(`http://localhost:${APP_PORT}`);
|
||||
|
||||
// Quick Lead workflow IDs from prefill-workflows.ts
|
||||
const QUICK_LEAD_WORKFLOW_ID = '8b213cac-a68b-4ffe-817a-3ec994e9932d';
|
||||
const QUICK_LEAD_WORKFLOW_VERSION_ID = 'ac67974f-c524-4288-9d88-af8515400b68';
|
||||
const {
|
||||
quickLeadWorkflowId: QUICK_LEAD_WORKFLOW_ID,
|
||||
quickLeadWorkflowVersionId: QUICK_LEAD_WORKFLOW_VERSION_ID,
|
||||
} = getWorkflowPrefillIds(SEED_APPLE_WORKSPACE_ID);
|
||||
const FORM_STEP_ID = '6e089bc9-aabd-435f-865f-f31c01c8f4a7';
|
||||
|
||||
describe('Quick Lead Workflow (e2e)', () => {
|
||||
|
||||
Reference in New Issue
Block a user