feat(workflow): scaffold core workflowVersion entity + trigger cache (phase 0) (#21674)

## What

**Phase 0 (scaffold)** of migrating `workflowVersion` data to **core**.
Gated by `IS_WORKFLOW_VERSION_IN_CORE_ENABLED` with **no behavior
change** — nothing reads or writes the new core entity yet.

## Plan

`workflowVersion` becomes a thin **workspace shell** over a core entity
(the `dashboard`/`pageLayout` pattern), so navigation, the metadata
relations, and the record UI keep working while the heavy data
(`triggers`, `steps`) lives in core. Trigger dispatch will derive from
active core versions via a per-workspace cache, letting us **eliminate**
the denormalized `workflowAutomatedTrigger` object. `workflow` and
`workflowRun` stay as workspace objects.

Phases: **0 — scaffold (this PR)** → A — backfill + dual-write → B —
switch reads to core → C — drop the workspace `trigger`/`steps` columns
+ the `workflowAutomatedTrigger` object.

## Included
- **Core `WorkflowVersionEntity`** (`extends WorkspaceRelatedEntity`) —
stores version data, with triggers as an **array** (`triggers:
WorkflowTrigger[]`), a long-due shape change. Storage only: dispatch
reads the primary trigger, so behavior stays single-trigger for now.
- **Fast create-table instance command** for `core."workflowVersion"`
(v2.19.0).
- **`IS_WORKFLOW_VERSION_IN_CORE_ENABLED`** feature flag.
- **Per-workspace automated-trigger cache provider** deriving
CRON/DATABASE_EVENT dispatch from the active version's trigger —
groundwork for removing `workflowAutomatedTrigger`.

## Notes
- `WorkspaceRelatedEntity`, **not** `SyncableEntity`: this is user
runtime data (like `connectedAccount`/`apiKey`/`file`), not
application-manifest metadata.
- No frontend behavior; the generated `FeatureFlagKey` enums are updated
to include the new flag.
This commit is contained in:
Thomas Trompette
2026-07-08 14:50:03 +02:00
committed by GitHub
parent 6554440bb1
commit 48730df0d2
16 changed files with 238 additions and 4 deletions
@@ -0,0 +1,45 @@
import { QueryRunner } from 'typeorm';
import { RegisteredInstanceCommand } from 'src/engine/core-modules/upgrade/decorators/registered-instance-command.decorator';
import { FastInstanceCommand } from 'src/engine/core-modules/upgrade/interfaces/fast-instance-command.interface';
@RegisteredInstanceCommand('2.20.0', 1783512000000)
export class CreateWorkflowVersionCoreTableFastInstanceCommand
implements FastInstanceCommand
{
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`DO $$ BEGIN CREATE TYPE "core"."workflowVersion_status_enum" AS ENUM ('DRAFT', 'ACTIVE', 'DEACTIVATED', 'ARCHIVED'); EXCEPTION WHEN duplicate_object THEN null; END $$`,
);
await queryRunner.query(
`CREATE TABLE IF NOT EXISTS "core"."workflowVersion" (
"id" uuid NOT NULL DEFAULT uuid_generate_v4(),
"createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
"updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
"triggers" jsonb,
"steps" jsonb,
"status" "core"."workflowVersion_status_enum" NOT NULL DEFAULT 'DRAFT',
"workflowId" uuid NOT NULL,
"workspaceId" uuid NOT NULL,
CONSTRAINT "PK_workflowVersion_id" PRIMARY KEY ("id"),
CONSTRAINT "FK_4316468725741b8e11e02b144f3" FOREIGN KEY ("workspaceId") REFERENCES "core"."workspace"("id") ON DELETE CASCADE
)`,
);
await queryRunner.query(
`CREATE INDEX IF NOT EXISTS "IDX_WORKFLOW_VERSION_WORKSPACE_ID"
ON "core"."workflowVersion" ("workspaceId")`,
);
await queryRunner.query(
`CREATE UNIQUE INDEX IF NOT EXISTS "IDX_WORKFLOW_VERSION_ONE_ACTIVE_PER_WORKFLOW"
ON "core"."workflowVersion" ("workflowId")
WHERE "status" = 'ACTIVE'`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`DROP TABLE IF EXISTS "core"."workflowVersion"`);
await queryRunner.query(
`DROP TYPE IF EXISTS "core"."workflowVersion_status_enum"`,
);
}
}
@@ -0,0 +1,4 @@
// Referenced by @WasIntroducedInUpgrade on WorkflowVersionEntity so upgrade
// steps running below 2.20.0 don't query the table before this command creates it.
export const CREATE_WORKFLOW_VERSION_CORE_TABLE_UPGRADE_COMMAND_NAME =
'2.20.0_CreateWorkflowVersionCoreTableFastInstanceCommand_1783512000000';
@@ -100,6 +100,7 @@ import { AddDisplayFieldsToApplicationRegistrationFastInstanceCommand } from './
import { BackfillDisplayFieldsOnApplicationRegistrationSlowInstanceCommand } from './2-19/2-19-instance-command-slow-1783073776591-backfill-display-fields-on-application-registration';
import { BackfillIsFeaturedOnApplicationRegistrationSlowInstanceCommand } from './2-19/2-19-instance-command-slow-1783120000000-backfill-is-featured-on-application-registration';
import { AllowServerScopedFileFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-20/2-20-instance-command-fast-1783499671541-allow-server-scoped-file';
import { CreateWorkflowVersionCoreTableFastInstanceCommand } from './2-20/2-20-instance-command-fast-1783512000000-create-workflow-version-core-table';
export const INSTANCE_COMMANDS = [
AddViewFieldGroupIdIndexOnViewFieldFastInstanceCommand,
@@ -202,4 +203,5 @@ export const INSTANCE_COMMANDS = [
AddPendingMimeCheckToFileFastInstanceCommand,
BackfillIsFeaturedOnApplicationRegistrationSlowInstanceCommand,
AllowServerScopedFileFastInstanceCommand,
CreateWorkflowVersionCoreTableFastInstanceCommand,
];