feat(workflow): add universalIdentifier + applicationId to core workflowVersion (#22747)
## workflowVersion core: syncable columns Adds `universalIdentifier` + `applicationId` (nullable) to `core.workflowVersion`, plus the FK to `core.application` and the `(workspaceId, universalIdentifier)` unique index, via a 2.20 add-columns fast command gated with `@WasIntroducedInUpgrade`. Nullable for now: the already-merged Phase A backfill (#22663) inserts version rows without these columns, so `applicationId` can't be NOT NULL yet. Flipping to NOT NULL + `extends SyncableEntity` comes once they're populated (backfill + dual-write follow-ups). Schema captured and verified via `migrate:generate` (zero drift). Independent of the core-workflow PR, but both add 2.20 upgrade commands, so this one (ts `…480`) must merge **after** the core-workflow PR (ts `…479`), or it gets re-timestamped on rebase. <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/22747?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:
+49
@@ -0,0 +1,49 @@
|
||||
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', 1783603454480)
|
||||
export class AddWorkflowVersionSyncableColumnsFastInstanceCommand
|
||||
implements FastInstanceCommand
|
||||
{
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."workflowVersion" ADD COLUMN IF NOT EXISTS "universalIdentifier" uuid NOT NULL`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."workflowVersion" ADD COLUMN IF NOT EXISTS "applicationId" uuid NOT NULL`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE UNIQUE INDEX IF NOT EXISTS "IDX_c67a90cc29887078286205457e"
|
||||
ON "core"."workflowVersion" ("workspaceId", "universalIdentifier")`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`DO $$ BEGIN
|
||||
ALTER TABLE "core"."workflowVersion" ADD CONSTRAINT "FK_29f62766c5b109981244b97060d" FOREIGN KEY ("applicationId") REFERENCES "core"."application"("id") ON DELETE CASCADE;
|
||||
EXCEPTION WHEN duplicate_object THEN null; END $$`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE INDEX IF NOT EXISTS "IDX_WORKFLOW_VERSION_APPLICATION_ID"
|
||||
ON "core"."workflowVersion" ("applicationId")`,
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."workflowVersion" DROP CONSTRAINT IF EXISTS "FK_29f62766c5b109981244b97060d"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`DROP INDEX IF EXISTS "core"."IDX_c67a90cc29887078286205457e"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`DROP INDEX IF EXISTS "core"."IDX_WORKFLOW_VERSION_APPLICATION_ID"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."workflowVersion" DROP COLUMN IF EXISTS "applicationId"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."workflowVersion" DROP COLUMN IF EXISTS "universalIdentifier"`,
|
||||
);
|
||||
}
|
||||
}
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
export const ADD_WORKFLOW_VERSION_SYNCABLE_COLUMNS_UPGRADE_COMMAND_NAME =
|
||||
'2.20.0_AddWorkflowVersionSyncableColumnsFastInstanceCommand_1783603454480';
|
||||
+2
@@ -105,6 +105,7 @@ import { BackfillNameFieldIsSystemSideEffectSlowInstanceCommand } from './2-20/2
|
||||
import { RenameIsFeaturedToIsVettedOnApplicationRegistrationFastInstanceCommand } from './2-20/2-20-instance-command-fast-1783527064000-rename-is-featured-to-is-vetted-on-application-registration';
|
||||
import { AddIsSystemSideEffectToSearchFieldMetadataFastInstanceCommand } from './2-20/2-20-instance-command-fast-1783580127637-add-is-system-side-effect-to-search-field-metadata';
|
||||
import { CreateWorkflowCoreTableFastInstanceCommand } from './2-20/2-20-instance-command-fast-1783603454479-create-workflow-core-table';
|
||||
import { AddWorkflowVersionSyncableColumnsFastInstanceCommand } from './2-20/2-20-instance-command-fast-1783603454480-add-workflow-version-syncable-columns';
|
||||
|
||||
export const INSTANCE_COMMANDS = [
|
||||
AddViewFieldGroupIdIndexOnViewFieldFastInstanceCommand,
|
||||
@@ -212,4 +213,5 @@ export const INSTANCE_COMMANDS = [
|
||||
RenameIsFeaturedToIsVettedOnApplicationRegistrationFastInstanceCommand,
|
||||
AddIsSystemSideEffectToSearchFieldMetadataFastInstanceCommand,
|
||||
CreateWorkflowCoreTableFastInstanceCommand,
|
||||
AddWorkflowVersionSyncableColumnsFastInstanceCommand,
|
||||
];
|
||||
|
||||
+3
-2
@@ -9,7 +9,7 @@ import {
|
||||
|
||||
import { CREATE_WORKFLOW_VERSION_CORE_TABLE_UPGRADE_COMMAND_NAME } from 'src/database/commands/upgrade-version-command/2-20/create-workflow-version-core-table-upgrade-command-name.constant';
|
||||
import { WasIntroducedInUpgrade } from 'src/engine/core-modules/upgrade/decorators/was-introduced-in-upgrade.decorator';
|
||||
import { WorkspaceRelatedEntity } from 'src/engine/workspace-manager/types/workspace-related-entity';
|
||||
import { SyncableEntity } from 'src/engine/workspace-manager/types/syncable-entity.interface';
|
||||
import { type WorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action.type';
|
||||
import { type WorkflowTrigger } from 'src/modules/workflow/workflow-trigger/types/workflow-trigger.type';
|
||||
|
||||
@@ -29,7 +29,8 @@ export enum WorkflowVersionStatus {
|
||||
unique: true,
|
||||
where: `"status" = 'ACTIVE'`,
|
||||
})
|
||||
export class WorkflowVersionEntity extends WorkspaceRelatedEntity {
|
||||
@Index('IDX_WORKFLOW_VERSION_APPLICATION_ID', ['applicationId'])
|
||||
export class WorkflowVersionEntity extends SyncableEntity {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
|
||||
|
||||
+27
-1
@@ -1,12 +1,15 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { In } from 'typeorm';
|
||||
import { In, Repository } from 'typeorm';
|
||||
import { v4 as uuidv4 } 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 { 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 { WorkspaceCacheService } from 'src/engine/workspace-cache/services/workspace-cache.service';
|
||||
@@ -17,6 +20,8 @@ export class WorkflowVersionCoreSyncService {
|
||||
constructor(
|
||||
@InjectWorkspaceScopedRepository(WorkflowVersionEntity)
|
||||
private readonly workflowVersionRepository: WorkspaceScopedRepository<WorkflowVersionEntity>,
|
||||
@InjectRepository(WorkspaceEntity)
|
||||
private readonly workspaceRepository: Repository<WorkspaceEntity>,
|
||||
private readonly workspaceCacheService: WorkspaceCacheService,
|
||||
) {}
|
||||
|
||||
@@ -28,6 +33,8 @@ export class WorkflowVersionCoreSyncService {
|
||||
return;
|
||||
}
|
||||
|
||||
const applicationId = await this.getCustomApplicationIdOrThrow(workspaceId);
|
||||
|
||||
await this.workflowVersionRepository.upsert(
|
||||
workspaceId,
|
||||
workflowVersions.map((workflowVersion) => ({
|
||||
@@ -38,6 +45,8 @@ export class WorkflowVersionCoreSyncService {
|
||||
: null,
|
||||
steps: workflowVersion.steps ?? null,
|
||||
status: workflowVersion.status as unknown as WorkflowVersionStatus,
|
||||
universalIdentifier: uuidv4(),
|
||||
applicationId,
|
||||
})),
|
||||
['id'],
|
||||
);
|
||||
@@ -45,6 +54,23 @@ export class WorkflowVersionCoreSyncService {
|
||||
await this.invalidateAutomatedTriggerMaps(workspaceId);
|
||||
}
|
||||
|
||||
private async getCustomApplicationIdOrThrow(
|
||||
workspaceId: string,
|
||||
): Promise<string> {
|
||||
const workspace = await this.workspaceRepository.findOne({
|
||||
where: { id: workspaceId },
|
||||
select: ['id', 'workspaceCustomApplicationId'],
|
||||
});
|
||||
|
||||
if (!isDefined(workspace?.workspaceCustomApplicationId)) {
|
||||
throw new Error(
|
||||
`Workspace custom application not found for workspace ${workspaceId}`,
|
||||
);
|
||||
}
|
||||
|
||||
return workspace.workspaceCustomApplicationId;
|
||||
}
|
||||
|
||||
async deleteFromCore(
|
||||
workspaceId: string,
|
||||
workflowVersionIds: string[],
|
||||
|
||||
+2
-1
@@ -4,12 +4,13 @@ import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { WorkflowVersionEntity } from 'src/engine/core-modules/workflow/entities/workflow-version.entity';
|
||||
import { WorkflowVersionCoreSyncService } from 'src/engine/core-modules/workflow/services/workflow-version-core-sync.service';
|
||||
import { WorkspaceWorkflowAutomatedTriggerMapCacheService } from 'src/engine/core-modules/workflow/services/workspace-workflow-automated-trigger-map-cache.service';
|
||||
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { provideWorkspaceScopedRepository } from 'src/engine/twenty-orm/workspace-scoped-repository/provide-workspace-scoped-repository';
|
||||
import { WorkspaceCacheModule } from 'src/engine/workspace-cache/workspace-cache.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
TypeOrmModule.forFeature([WorkflowVersionEntity]),
|
||||
TypeOrmModule.forFeature([WorkflowVersionEntity, WorkspaceEntity]),
|
||||
WorkspaceCacheModule,
|
||||
],
|
||||
providers: [
|
||||
|
||||
Reference in New Issue
Block a user