Refactor global datasource part 3 (#16447)
## Context Following https://github.com/twentyhq/twenty/pull/16399 Now using the new global orm manager everywhere and returning a GlobalDatasource/WorkspaceDatasource based on a feature flag. This means we now need to wrap all our ORM calls within executeInWorkspaceContext callback (at least for now) so the global datasource can dynamically hydrate its context via the new store (the global datasource does not store anything related to workspaces as it is now a unique singleton). If feature flag is off it still uses local data stored in the workspace datasource.
This commit is contained in:
+10
-7
@@ -2,8 +2,8 @@ import { Test, type TestingModule } from '@nestjs/testing';
|
||||
|
||||
import { TRIGGER_STEP_ID } from 'twenty-shared/workflow';
|
||||
|
||||
import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
|
||||
import { type WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { type WorkflowVersionWorkspaceEntity } from 'src/modules/workflow/common/standard-objects/workflow-version.workspace-entity';
|
||||
import { WorkflowCommonWorkspaceService } from 'src/modules/workflow/common/workspace-services/workflow-common.workspace-service';
|
||||
import { WorkflowVersionEdgeWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-version-edge/workflow-version-edge.workspace-service';
|
||||
@@ -73,7 +73,7 @@ const mockWorkflowVersion = {
|
||||
} as WorkflowVersionWorkspaceEntity;
|
||||
|
||||
describe('WorkflowVersionEdgeWorkspaceService', () => {
|
||||
let twentyORMGlobalManager: jest.Mocked<TwentyORMGlobalManager>;
|
||||
let globalWorkspaceOrmManager: jest.Mocked<GlobalWorkspaceOrmManager>;
|
||||
let workflowCommonWorkspaceService: jest.Mocked<WorkflowCommonWorkspaceService>;
|
||||
let service: WorkflowVersionEdgeWorkspaceService;
|
||||
let mockWorkflowVersionWorkspaceRepository: MockWorkspaceRepository;
|
||||
@@ -88,11 +88,14 @@ describe('WorkflowVersionEdgeWorkspaceService', () => {
|
||||
mockWorkflowVersion,
|
||||
);
|
||||
|
||||
twentyORMGlobalManager = {
|
||||
getRepositoryForWorkspace: jest
|
||||
globalWorkspaceOrmManager = {
|
||||
executeInWorkspaceContext: jest
|
||||
.fn()
|
||||
.mockImplementation(async (_authContext, callback) => callback()),
|
||||
getRepository: jest
|
||||
.fn()
|
||||
.mockResolvedValue(mockWorkflowVersionWorkspaceRepository),
|
||||
} as unknown as jest.Mocked<TwentyORMGlobalManager>;
|
||||
} as unknown as jest.Mocked<GlobalWorkspaceOrmManager>;
|
||||
|
||||
workflowCommonWorkspaceService = {
|
||||
getWorkflowVersionOrFail: jest
|
||||
@@ -104,8 +107,8 @@ describe('WorkflowVersionEdgeWorkspaceService', () => {
|
||||
providers: [
|
||||
WorkflowVersionEdgeWorkspaceService,
|
||||
{
|
||||
provide: TwentyORMGlobalManager,
|
||||
useValue: twentyORMGlobalManager,
|
||||
provide: GlobalWorkspaceOrmManager,
|
||||
useValue: globalWorkspaceOrmManager,
|
||||
},
|
||||
{
|
||||
provide: WorkflowCommonWorkspaceService,
|
||||
|
||||
+99
-84
@@ -4,8 +4,9 @@ import { isDefined } from 'twenty-shared/utils';
|
||||
import { TRIGGER_STEP_ID } from 'twenty-shared/workflow';
|
||||
|
||||
import { type WorkflowVersionStepChangesDTO } from 'src/engine/core-modules/workflow/dtos/workflow-version-step-changes.dto';
|
||||
import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
|
||||
import { WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { buildSystemAuthContext } from 'src/engine/twenty-orm/utils/build-system-auth-context.util';
|
||||
import {
|
||||
WorkflowVersionEdgeException,
|
||||
WorkflowVersionEdgeExceptionCode,
|
||||
@@ -24,7 +25,7 @@ import { type WorkflowTrigger } from 'src/modules/workflow/workflow-trigger/type
|
||||
@Injectable()
|
||||
export class WorkflowVersionEdgeWorkspaceService {
|
||||
constructor(
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
private readonly globalWorkspaceOrmManager: GlobalWorkspaceOrmManager,
|
||||
private readonly workflowCommonWorkspaceService: WorkflowCommonWorkspaceService,
|
||||
) {}
|
||||
|
||||
@@ -41,54 +42,61 @@ export class WorkflowVersionEdgeWorkspaceService {
|
||||
workspaceId: string;
|
||||
sourceConnectionOptions?: WorkflowStepConnectionOptions;
|
||||
}): Promise<WorkflowVersionStepChangesDTO> {
|
||||
const workflowVersionRepository =
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<WorkflowVersionWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'workflowVersion',
|
||||
{ shouldBypassPermissionChecks: true },
|
||||
);
|
||||
const authContext = buildSystemAuthContext(workspaceId);
|
||||
|
||||
const workflowVersion =
|
||||
await this.workflowCommonWorkspaceService.getWorkflowVersionOrFail({
|
||||
workflowVersionId,
|
||||
workspaceId,
|
||||
});
|
||||
return this.globalWorkspaceOrmManager.executeInWorkspaceContext(
|
||||
authContext,
|
||||
async () => {
|
||||
const workflowVersionRepository =
|
||||
await this.globalWorkspaceOrmManager.getRepository<WorkflowVersionWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'workflowVersion',
|
||||
{ shouldBypassPermissionChecks: true },
|
||||
);
|
||||
|
||||
assertWorkflowVersionIsDraft(workflowVersion);
|
||||
const workflowVersion =
|
||||
await this.workflowCommonWorkspaceService.getWorkflowVersionOrFail({
|
||||
workflowVersionId,
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
const trigger = workflowVersion.trigger;
|
||||
const steps = workflowVersion.steps || [];
|
||||
assertWorkflowVersionIsDraft(workflowVersion);
|
||||
|
||||
const targetStep = steps.find((step) => step.id === target);
|
||||
const trigger = workflowVersion.trigger;
|
||||
const steps = workflowVersion.steps || [];
|
||||
|
||||
if (!isDefined(targetStep)) {
|
||||
throw new WorkflowVersionEdgeException(
|
||||
`Target step '${target}' not found in workflowVersion '${workflowVersionId}'`,
|
||||
WorkflowVersionEdgeExceptionCode.NOT_FOUND,
|
||||
);
|
||||
}
|
||||
const targetStep = steps.find((step) => step.id === target);
|
||||
|
||||
const isSourceTrigger = source === TRIGGER_STEP_ID;
|
||||
if (!isDefined(targetStep)) {
|
||||
throw new WorkflowVersionEdgeException(
|
||||
`Target step '${target}' not found in workflowVersion '${workflowVersionId}'`,
|
||||
WorkflowVersionEdgeExceptionCode.NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
if (isSourceTrigger) {
|
||||
return this.createTriggerEdge({
|
||||
trigger,
|
||||
steps,
|
||||
target,
|
||||
workflowVersion,
|
||||
workflowVersionRepository,
|
||||
});
|
||||
} else {
|
||||
return this.createStepEdge({
|
||||
trigger,
|
||||
steps,
|
||||
source,
|
||||
target,
|
||||
sourceConnectionOptions,
|
||||
workflowVersion,
|
||||
workflowVersionRepository,
|
||||
});
|
||||
}
|
||||
const isSourceTrigger = source === TRIGGER_STEP_ID;
|
||||
|
||||
if (isSourceTrigger) {
|
||||
return this.createTriggerEdge({
|
||||
trigger,
|
||||
steps,
|
||||
target,
|
||||
workflowVersion,
|
||||
workflowVersionRepository,
|
||||
});
|
||||
} else {
|
||||
return this.createStepEdge({
|
||||
trigger,
|
||||
steps,
|
||||
source,
|
||||
target,
|
||||
sourceConnectionOptions,
|
||||
workflowVersion,
|
||||
workflowVersionRepository,
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
async deleteWorkflowVersionEdge({
|
||||
@@ -104,54 +112,61 @@ export class WorkflowVersionEdgeWorkspaceService {
|
||||
workspaceId: string;
|
||||
sourceConnectionOptions?: WorkflowStepConnectionOptions;
|
||||
}): Promise<WorkflowVersionStepChangesDTO> {
|
||||
const workflowVersionRepository =
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<WorkflowVersionWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'workflowVersion',
|
||||
{ shouldBypassPermissionChecks: true },
|
||||
);
|
||||
const authContext = buildSystemAuthContext(workspaceId);
|
||||
|
||||
const workflowVersion =
|
||||
await this.workflowCommonWorkspaceService.getWorkflowVersionOrFail({
|
||||
workflowVersionId,
|
||||
workspaceId,
|
||||
});
|
||||
return this.globalWorkspaceOrmManager.executeInWorkspaceContext(
|
||||
authContext,
|
||||
async () => {
|
||||
const workflowVersionRepository =
|
||||
await this.globalWorkspaceOrmManager.getRepository<WorkflowVersionWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'workflowVersion',
|
||||
{ shouldBypassPermissionChecks: true },
|
||||
);
|
||||
|
||||
assertWorkflowVersionIsDraft(workflowVersion);
|
||||
const workflowVersion =
|
||||
await this.workflowCommonWorkspaceService.getWorkflowVersionOrFail({
|
||||
workflowVersionId,
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
const trigger = workflowVersion.trigger;
|
||||
const steps = workflowVersion.steps || [];
|
||||
assertWorkflowVersionIsDraft(workflowVersion);
|
||||
|
||||
const targetStep = steps.find((step) => step.id === target);
|
||||
const trigger = workflowVersion.trigger;
|
||||
const steps = workflowVersion.steps || [];
|
||||
|
||||
if (!isDefined(targetStep)) {
|
||||
throw new WorkflowVersionEdgeException(
|
||||
`Target step '${target}' not found in workflowVersion '${workflowVersionId}'`,
|
||||
WorkflowVersionEdgeExceptionCode.NOT_FOUND,
|
||||
);
|
||||
}
|
||||
const targetStep = steps.find((step) => step.id === target);
|
||||
|
||||
const isSourceTrigger = source === TRIGGER_STEP_ID;
|
||||
if (!isDefined(targetStep)) {
|
||||
throw new WorkflowVersionEdgeException(
|
||||
`Target step '${target}' not found in workflowVersion '${workflowVersionId}'`,
|
||||
WorkflowVersionEdgeExceptionCode.NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
if (isSourceTrigger) {
|
||||
return this.deleteTriggerEdge({
|
||||
trigger,
|
||||
steps,
|
||||
target,
|
||||
workflowVersion,
|
||||
workflowVersionRepository,
|
||||
});
|
||||
} else {
|
||||
return this.deleteStepEdge({
|
||||
trigger,
|
||||
steps,
|
||||
source,
|
||||
target,
|
||||
workflowVersion,
|
||||
workflowVersionRepository,
|
||||
sourceConnectionOptions,
|
||||
});
|
||||
}
|
||||
const isSourceTrigger = source === TRIGGER_STEP_ID;
|
||||
|
||||
if (isSourceTrigger) {
|
||||
return this.deleteTriggerEdge({
|
||||
trigger,
|
||||
steps,
|
||||
target,
|
||||
workflowVersion,
|
||||
workflowVersionRepository,
|
||||
});
|
||||
} else {
|
||||
return this.deleteStepEdge({
|
||||
trigger,
|
||||
steps,
|
||||
source,
|
||||
target,
|
||||
workflowVersion,
|
||||
workflowVersionRepository,
|
||||
sourceConnectionOptions,
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
private async createTriggerEdge({
|
||||
|
||||
+7
-8
@@ -8,7 +8,7 @@ import { RoleTargetEntity } from 'src/engine/metadata-modules/role-target/role-t
|
||||
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
|
||||
import { type ServerlessFunctionEntity } from 'src/engine/metadata-modules/serverless-function/serverless-function.entity';
|
||||
import { ServerlessFunctionService } from 'src/engine/metadata-modules/serverless-function/serverless-function.service';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
|
||||
import { WorkspaceCacheService } from 'src/engine/workspace-cache/services/workspace-cache.service';
|
||||
import { WorkflowCommonWorkspaceService } from 'src/modules/workflow/common/workspace-services/workflow-common.workspace-service';
|
||||
import { WorkflowVersionStepOperationsWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-version-step/workflow-version-step-operations.workspace-service';
|
||||
@@ -21,7 +21,7 @@ const mockWorkspaceId = 'workspace-id';
|
||||
|
||||
describe('WorkflowVersionStepOperationsWorkspaceService', () => {
|
||||
let service: WorkflowVersionStepOperationsWorkspaceService;
|
||||
let twentyORMGlobalManager: jest.Mocked<TwentyORMGlobalManager>;
|
||||
let globalWorkspaceOrmManager: jest.Mocked<GlobalWorkspaceOrmManager>;
|
||||
let serverlessFunctionService: jest.Mocked<ServerlessFunctionService>;
|
||||
let agentRepository: jest.Mocked<any>;
|
||||
let roleTargetRepository: jest.Mocked<any>;
|
||||
@@ -67,20 +67,19 @@ describe('WorkflowVersionStepOperationsWorkspaceService', () => {
|
||||
deleteAgentOnlyRoleIfUnused: jest.fn(),
|
||||
} as unknown as jest.Mocked<AiAgentRoleService>;
|
||||
|
||||
globalWorkspaceOrmManager = {
|
||||
getRepository: jest.fn(),
|
||||
} as unknown as jest.Mocked<GlobalWorkspaceOrmManager>;
|
||||
workspaceCacheService = {
|
||||
flush: jest.fn(),
|
||||
} as unknown as jest.Mocked<WorkspaceCacheService>;
|
||||
|
||||
twentyORMGlobalManager = {
|
||||
getRepositoryForWorkspace: jest.fn(),
|
||||
} as unknown as jest.Mocked<TwentyORMGlobalManager>;
|
||||
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
WorkflowVersionStepOperationsWorkspaceService,
|
||||
{
|
||||
provide: TwentyORMGlobalManager,
|
||||
useValue: twentyORMGlobalManager,
|
||||
provide: GlobalWorkspaceOrmManager,
|
||||
useValue: globalWorkspaceOrmManager,
|
||||
},
|
||||
{
|
||||
provide: ServerlessFunctionService,
|
||||
|
||||
+15
-11
@@ -2,17 +2,17 @@ import { Test, type TestingModule } from '@nestjs/testing';
|
||||
|
||||
import { TRIGGER_STEP_ID } from 'twenty-shared/workflow';
|
||||
|
||||
import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
|
||||
import { type WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { type WorkflowVersionWorkspaceEntity } from 'src/modules/workflow/common/standard-objects/workflow-version.workspace-entity';
|
||||
import { WorkflowCommonWorkspaceService } from 'src/modules/workflow/common/workspace-services/workflow-common.workspace-service';
|
||||
import { WorkflowSchemaWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-schema/workflow-schema.workspace-service';
|
||||
import { WorkflowVersionStepOperationsWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-version-step/workflow-version-step-operations.workspace-service';
|
||||
import { WorkflowVersionStepWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-version-step/workflow-version-step.workspace-service';
|
||||
import { WorkflowVersionStepHelpersWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-version-step/workflow-version-step-helpers.workspace-service';
|
||||
import { WorkflowVersionStepCreationWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-version-step/workflow-version-step-creation.workspace-service';
|
||||
import { WorkflowVersionStepUpdateWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-version-step/workflow-version-step-update.workspace-service';
|
||||
import { WorkflowVersionStepDeletionWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-version-step/workflow-version-step-deletion.workspace-service';
|
||||
import { WorkflowVersionStepHelpersWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-version-step/workflow-version-step-helpers.workspace-service';
|
||||
import { WorkflowVersionStepOperationsWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-version-step/workflow-version-step-operations.workspace-service';
|
||||
import { WorkflowVersionStepUpdateWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-version-step/workflow-version-step-update.workspace-service';
|
||||
import { WorkflowVersionStepWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-version-step/workflow-version-step.workspace-service';
|
||||
import {
|
||||
type WorkflowAction,
|
||||
WorkflowActionType,
|
||||
@@ -86,7 +86,7 @@ const mockWorkflowVersion = {
|
||||
} as WorkflowVersionWorkspaceEntity;
|
||||
|
||||
describe('WorkflowVersionStepWorkspaceService', () => {
|
||||
let twentyORMGlobalManager: jest.Mocked<TwentyORMGlobalManager>;
|
||||
let globalWorkspaceOrmManager: jest.Mocked<GlobalWorkspaceOrmManager>;
|
||||
let service: WorkflowVersionStepWorkspaceService;
|
||||
let mockWorkflowVersionWorkspaceRepository: MockWorkspaceRepository;
|
||||
let mockComputeWorkflowVersionStepChanges: jest.Mock;
|
||||
@@ -108,11 +108,15 @@ describe('WorkflowVersionStepWorkspaceService', () => {
|
||||
mockWorkflowVersion,
|
||||
);
|
||||
|
||||
twentyORMGlobalManager = {
|
||||
getRepositoryForWorkspace: jest
|
||||
globalWorkspaceOrmManager = {
|
||||
getRepository: jest
|
||||
.fn()
|
||||
.mockResolvedValue(mockWorkflowVersionWorkspaceRepository),
|
||||
} as unknown as jest.Mocked<TwentyORMGlobalManager>;
|
||||
executeInWorkspaceContext: jest
|
||||
.fn()
|
||||
|
||||
.mockImplementation((_authContext: any, fn: () => any) => fn()),
|
||||
} as unknown as jest.Mocked<GlobalWorkspaceOrmManager>;
|
||||
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
@@ -122,8 +126,8 @@ describe('WorkflowVersionStepWorkspaceService', () => {
|
||||
WorkflowVersionStepUpdateWorkspaceService,
|
||||
WorkflowVersionStepDeletionWorkspaceService,
|
||||
{
|
||||
provide: TwentyORMGlobalManager,
|
||||
useValue: twentyORMGlobalManager,
|
||||
provide: GlobalWorkspaceOrmManager,
|
||||
useValue: globalWorkspaceOrmManager,
|
||||
},
|
||||
{
|
||||
provide: WorkflowSchemaWorkspaceService,
|
||||
|
||||
+24
-16
@@ -1,6 +1,7 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
|
||||
import { buildSystemAuthContext } from 'src/engine/twenty-orm/utils/build-system-auth-context.util';
|
||||
import { type WorkflowVersionWorkspaceEntity } from 'src/modules/workflow/common/standard-objects/workflow-version.workspace-entity';
|
||||
import { assertWorkflowVersionIsDraft } from 'src/modules/workflow/common/utils/assert-workflow-version-is-draft.util';
|
||||
import { WorkflowCommonWorkspaceService } from 'src/modules/workflow/common/workspace-services/workflow-common.workspace-service';
|
||||
@@ -10,7 +11,7 @@ import { type WorkflowTrigger } from 'src/modules/workflow/workflow-trigger/type
|
||||
@Injectable()
|
||||
export class WorkflowVersionStepHelpersWorkspaceService {
|
||||
constructor(
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
private readonly globalWorkspaceOrmManager: GlobalWorkspaceOrmManager,
|
||||
private readonly workflowCommonWorkspaceService: WorkflowCommonWorkspaceService,
|
||||
) {}
|
||||
|
||||
@@ -43,23 +44,30 @@ export class WorkflowVersionStepHelpersWorkspaceService {
|
||||
steps?: WorkflowAction[] | null;
|
||||
trigger?: WorkflowTrigger | null;
|
||||
}): Promise<void> {
|
||||
const workflowVersionRepository =
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<WorkflowVersionWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'workflowVersion',
|
||||
{ shouldBypassPermissionChecks: true },
|
||||
);
|
||||
const authContext = buildSystemAuthContext(workspaceId);
|
||||
|
||||
const updateData: Partial<WorkflowVersionWorkspaceEntity> = {};
|
||||
await this.globalWorkspaceOrmManager.executeInWorkspaceContext(
|
||||
authContext,
|
||||
async () => {
|
||||
const workflowVersionRepository =
|
||||
await this.globalWorkspaceOrmManager.getRepository<WorkflowVersionWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'workflowVersion',
|
||||
{ shouldBypassPermissionChecks: true },
|
||||
);
|
||||
|
||||
if (steps !== undefined) {
|
||||
updateData.steps = steps;
|
||||
}
|
||||
const updateData: Partial<WorkflowVersionWorkspaceEntity> = {};
|
||||
|
||||
if (trigger !== undefined) {
|
||||
updateData.trigger = trigger;
|
||||
}
|
||||
if (steps !== undefined) {
|
||||
updateData.steps = steps;
|
||||
}
|
||||
|
||||
await workflowVersionRepository.update(workflowVersionId, updateData);
|
||||
if (trigger !== undefined) {
|
||||
updateData.trigger = trigger;
|
||||
}
|
||||
|
||||
await workflowVersionRepository.update(workflowVersionId, updateData);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+118
-98
@@ -15,7 +15,8 @@ import { DEFAULT_SMART_MODEL } from 'src/engine/metadata-modules/ai/ai-models/co
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { RoleTargetEntity } from 'src/engine/metadata-modules/role-target/role-target.entity';
|
||||
import { ServerlessFunctionService } from 'src/engine/metadata-modules/serverless-function/serverless-function.service';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
|
||||
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 {
|
||||
WorkflowVersionStepException,
|
||||
@@ -52,7 +53,7 @@ const ITERATOR_EMPTY_STEP_POSITION_OFFSET = {
|
||||
@Injectable()
|
||||
export class WorkflowVersionStepOperationsWorkspaceService {
|
||||
constructor(
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
private readonly globalWorkspaceOrmManager: GlobalWorkspaceOrmManager,
|
||||
private readonly serverlessFunctionService: ServerlessFunctionService,
|
||||
@InjectRepository(AgentEntity)
|
||||
private readonly agentRepository: Repository<AgentEntity>,
|
||||
@@ -359,7 +360,6 @@ export class WorkflowVersionStepOperationsWorkspaceService {
|
||||
};
|
||||
}
|
||||
case WorkflowActionType.AI_AGENT: {
|
||||
// Get workflow version to use workflow ID and name in agent name
|
||||
const workflowVersion =
|
||||
await this.workflowCommonWorkspaceService.getWorkflowVersionOrFail({
|
||||
workflowVersionId,
|
||||
@@ -465,66 +465,75 @@ export class WorkflowVersionStepOperationsWorkspaceService {
|
||||
step: WorkflowFormAction;
|
||||
response: object;
|
||||
}) {
|
||||
const responseKeys = Object.keys(response);
|
||||
const authContext = buildSystemAuthContext(workspaceId);
|
||||
|
||||
const enrichedResponses = await Promise.all(
|
||||
responseKeys.map(async (key) => {
|
||||
// @ts-expect-error legacy noImplicitAny
|
||||
if (!isDefined(response[key])) {
|
||||
// @ts-expect-error legacy noImplicitAny
|
||||
return { key, value: response[key] };
|
||||
}
|
||||
return this.globalWorkspaceOrmManager.executeInWorkspaceContext(
|
||||
authContext,
|
||||
async () => {
|
||||
const responseKeys = Object.keys(response);
|
||||
|
||||
const field = step.settings.input.find((field) => field.name === key);
|
||||
|
||||
if (
|
||||
field?.type === 'RECORD' &&
|
||||
field?.settings?.objectName &&
|
||||
// @ts-expect-error legacy noImplicitAny
|
||||
isDefined(response[key].id) &&
|
||||
// @ts-expect-error legacy noImplicitAny
|
||||
isValidUuid(response[key].id)
|
||||
) {
|
||||
const { flatObjectMetadata, flatFieldMetadataMaps } =
|
||||
await this.workflowCommonWorkspaceService.getObjectMetadataInfo(
|
||||
field.settings.objectName,
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
const relationFieldsNames = getFlatFieldsFromFlatObjectMetadata(
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
)
|
||||
.filter((field) => field.type === FieldMetadataType.RELATION)
|
||||
.map((field) => field.name);
|
||||
|
||||
const repository =
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace(
|
||||
workspaceId,
|
||||
field.settings.objectName,
|
||||
{ shouldBypassPermissionChecks: true },
|
||||
);
|
||||
|
||||
const record = await repository.findOne({
|
||||
const enrichedResponses = await Promise.all(
|
||||
responseKeys.map(async (key) => {
|
||||
// @ts-expect-error legacy noImplicitAny
|
||||
where: { id: response[key].id },
|
||||
relations: relationFieldsNames,
|
||||
});
|
||||
if (!isDefined(response[key])) {
|
||||
// @ts-expect-error legacy noImplicitAny
|
||||
return { key, value: response[key] };
|
||||
}
|
||||
|
||||
return { key, value: record };
|
||||
} else {
|
||||
const field = step.settings.input.find(
|
||||
(field) => field.name === key,
|
||||
);
|
||||
|
||||
if (
|
||||
field?.type === 'RECORD' &&
|
||||
field?.settings?.objectName &&
|
||||
// @ts-expect-error legacy noImplicitAny
|
||||
isDefined(response[key].id) &&
|
||||
// @ts-expect-error legacy noImplicitAny
|
||||
isValidUuid(response[key].id)
|
||||
) {
|
||||
const { flatObjectMetadata, flatFieldMetadataMaps } =
|
||||
await this.workflowCommonWorkspaceService.getObjectMetadataInfo(
|
||||
field.settings.objectName,
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
const relationFieldsNames = getFlatFieldsFromFlatObjectMetadata(
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
)
|
||||
.filter((field) => field.type === FieldMetadataType.RELATION)
|
||||
.map((field) => field.name);
|
||||
|
||||
const repository =
|
||||
await this.globalWorkspaceOrmManager.getRepository(
|
||||
workspaceId,
|
||||
field.settings.objectName,
|
||||
{ shouldBypassPermissionChecks: true },
|
||||
);
|
||||
|
||||
const record = await repository.findOne({
|
||||
// @ts-expect-error legacy noImplicitAny
|
||||
where: { id: response[key].id },
|
||||
relations: relationFieldsNames,
|
||||
});
|
||||
|
||||
return { key, value: record };
|
||||
} else {
|
||||
// @ts-expect-error legacy noImplicitAny
|
||||
return { key, value: response[key] };
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
return enrichedResponses.reduce((acc, { key, value }) => {
|
||||
// @ts-expect-error legacy noImplicitAny
|
||||
return { key, value: response[key] };
|
||||
}
|
||||
}),
|
||||
acc[key] = value;
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
},
|
||||
);
|
||||
|
||||
return enrichedResponses.reduce((acc, { key, value }) => {
|
||||
// @ts-expect-error legacy noImplicitAny
|
||||
acc[key] = value;
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
}
|
||||
|
||||
async cloneStep({
|
||||
@@ -650,49 +659,60 @@ export class WorkflowVersionStepOperationsWorkspaceService {
|
||||
workspaceId: string;
|
||||
iteratorPosition?: WorkflowStepPositionInput;
|
||||
}): Promise<WorkflowAction> {
|
||||
const workflowVersionRepository =
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<WorkflowVersionWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'workflowVersion',
|
||||
{ shouldBypassPermissionChecks: true },
|
||||
);
|
||||
const authContext = buildSystemAuthContext(workspaceId);
|
||||
|
||||
const workflowVersion = await workflowVersionRepository.findOne({
|
||||
where: {
|
||||
id: workflowVersionId,
|
||||
return this.globalWorkspaceOrmManager.executeInWorkspaceContext(
|
||||
authContext,
|
||||
async () => {
|
||||
const workflowVersionRepository =
|
||||
await this.globalWorkspaceOrmManager.getRepository<WorkflowVersionWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'workflowVersion',
|
||||
{ shouldBypassPermissionChecks: true },
|
||||
);
|
||||
|
||||
const workflowVersion = await workflowVersionRepository.findOne({
|
||||
where: {
|
||||
id: workflowVersionId,
|
||||
},
|
||||
});
|
||||
|
||||
if (!isDefined(workflowVersion)) {
|
||||
throw new WorkflowVersionStepException(
|
||||
'WorkflowVersion not found',
|
||||
WorkflowVersionStepExceptionCode.NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
const existingSteps = workflowVersion.steps ?? [];
|
||||
|
||||
const emptyNodeStep: WorkflowEmptyAction = {
|
||||
id: v4(),
|
||||
name: 'Add an Action',
|
||||
type: WorkflowActionType.EMPTY,
|
||||
valid: true,
|
||||
nextStepIds: [iteratorStepId],
|
||||
settings: {
|
||||
...BASE_STEP_DEFINITION,
|
||||
input: {},
|
||||
},
|
||||
position: {
|
||||
x:
|
||||
(iteratorPosition?.x ?? 0) +
|
||||
ITERATOR_EMPTY_STEP_POSITION_OFFSET.x,
|
||||
y:
|
||||
(iteratorPosition?.y ?? 0) +
|
||||
ITERATOR_EMPTY_STEP_POSITION_OFFSET.y,
|
||||
},
|
||||
};
|
||||
|
||||
await workflowVersionRepository.update(workflowVersion.id, {
|
||||
steps: [...existingSteps, emptyNodeStep],
|
||||
});
|
||||
|
||||
return emptyNodeStep;
|
||||
},
|
||||
});
|
||||
|
||||
if (!isDefined(workflowVersion)) {
|
||||
throw new WorkflowVersionStepException(
|
||||
'WorkflowVersion not found',
|
||||
WorkflowVersionStepExceptionCode.NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
const existingSteps = workflowVersion.steps ?? [];
|
||||
|
||||
const emptyNodeStep: WorkflowEmptyAction = {
|
||||
id: v4(),
|
||||
name: 'Add an Action',
|
||||
type: WorkflowActionType.EMPTY,
|
||||
valid: true,
|
||||
nextStepIds: [iteratorStepId],
|
||||
settings: {
|
||||
...BASE_STEP_DEFINITION,
|
||||
input: {},
|
||||
},
|
||||
position: {
|
||||
x: (iteratorPosition?.x ?? 0) + ITERATOR_EMPTY_STEP_POSITION_OFFSET.x,
|
||||
y: (iteratorPosition?.y ?? 0) + ITERATOR_EMPTY_STEP_POSITION_OFFSET.y,
|
||||
},
|
||||
};
|
||||
|
||||
await workflowVersionRepository.update(workflowVersion.id, {
|
||||
steps: [...existingSteps, emptyNodeStep],
|
||||
});
|
||||
|
||||
return emptyNodeStep;
|
||||
);
|
||||
}
|
||||
|
||||
async createDraftStep({
|
||||
|
||||
+287
-257
@@ -5,7 +5,8 @@ import { TRIGGER_STEP_ID } from 'twenty-shared/workflow';
|
||||
|
||||
import { RecordPositionService } from 'src/engine/core-modules/record-position/services/record-position.service';
|
||||
import { type WorkflowStepPositionUpdateInput } from 'src/engine/core-modules/workflow/dtos/update-workflow-step-position-update-input.dto';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
|
||||
import { buildSystemAuthContext } from 'src/engine/twenty-orm/utils/build-system-auth-context.util';
|
||||
import {
|
||||
WorkflowVersionStepException,
|
||||
WorkflowVersionStepExceptionCode,
|
||||
@@ -31,7 +32,7 @@ import {
|
||||
@Injectable()
|
||||
export class WorkflowVersionWorkspaceService {
|
||||
constructor(
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
private readonly globalWorkspaceOrmManager: GlobalWorkspaceOrmManager,
|
||||
private readonly workflowVersionStepWorkspaceService: WorkflowVersionStepWorkspaceService,
|
||||
private readonly workflowVersionStepOperationsWorkspaceService: WorkflowVersionStepOperationsWorkspaceService,
|
||||
private readonly recordPositionService: RecordPositionService,
|
||||
@@ -46,90 +47,99 @@ export class WorkflowVersionWorkspaceService {
|
||||
workflowId: string;
|
||||
workflowVersionIdToCopy: string;
|
||||
}) {
|
||||
const workflowVersionRepository =
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<WorkflowVersionWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'workflowVersion',
|
||||
{ shouldBypassPermissionChecks: true },
|
||||
);
|
||||
const authContext = buildSystemAuthContext(workspaceId);
|
||||
|
||||
const workflowVersionToCopy = await workflowVersionRepository.findOne({
|
||||
where: {
|
||||
id: workflowVersionIdToCopy,
|
||||
workflowId,
|
||||
},
|
||||
});
|
||||
return this.globalWorkspaceOrmManager.executeInWorkspaceContext(
|
||||
authContext,
|
||||
async () => {
|
||||
const workflowVersionRepository =
|
||||
await this.globalWorkspaceOrmManager.getRepository<WorkflowVersionWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'workflowVersion',
|
||||
{ shouldBypassPermissionChecks: true },
|
||||
);
|
||||
|
||||
if (!isDefined(workflowVersionToCopy)) {
|
||||
throw new WorkflowVersionStepException(
|
||||
'WorkflowVersion to copy not found',
|
||||
WorkflowVersionStepExceptionCode.NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
assertWorkflowVersionTriggerIsDefined(workflowVersionToCopy);
|
||||
assertWorkflowVersionHasSteps(workflowVersionToCopy);
|
||||
|
||||
let draftWorkflowVersion = await workflowVersionRepository.findOne({
|
||||
where: {
|
||||
workflowId,
|
||||
status: WorkflowVersionStatus.DRAFT,
|
||||
},
|
||||
});
|
||||
|
||||
if (!isDefined(draftWorkflowVersion)) {
|
||||
const workflowVersionsCount = await workflowVersionRepository.count({
|
||||
where: {
|
||||
workflowId,
|
||||
},
|
||||
});
|
||||
|
||||
const position = await this.recordPositionService.buildRecordPosition({
|
||||
value: 'first',
|
||||
objectMetadata: {
|
||||
isCustom: false,
|
||||
nameSingular: 'workflowVersion',
|
||||
},
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
const insertResult = await workflowVersionRepository.insert({
|
||||
workflowId,
|
||||
name: `v${workflowVersionsCount + 1}`,
|
||||
status: WorkflowVersionStatus.DRAFT,
|
||||
position,
|
||||
});
|
||||
|
||||
draftWorkflowVersion = insertResult
|
||||
.generatedMaps[0] as WorkflowVersionWorkspaceEntity;
|
||||
}
|
||||
|
||||
assertWorkflowVersionIsDraft(draftWorkflowVersion);
|
||||
|
||||
const newWorkflowVersionTrigger = workflowVersionToCopy.trigger;
|
||||
const newWorkflowVersionSteps: WorkflowAction[] = [];
|
||||
|
||||
for (const step of workflowVersionToCopy.steps) {
|
||||
const duplicatedStep =
|
||||
await this.workflowVersionStepWorkspaceService.createDraftStep({
|
||||
step,
|
||||
workspaceId,
|
||||
const workflowVersionToCopy = await workflowVersionRepository.findOne({
|
||||
where: {
|
||||
id: workflowVersionIdToCopy,
|
||||
workflowId,
|
||||
},
|
||||
});
|
||||
|
||||
newWorkflowVersionSteps.push(duplicatedStep);
|
||||
}
|
||||
if (!isDefined(workflowVersionToCopy)) {
|
||||
throw new WorkflowVersionStepException(
|
||||
'WorkflowVersion to copy not found',
|
||||
WorkflowVersionStepExceptionCode.NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
await workflowVersionRepository.update(draftWorkflowVersion.id, {
|
||||
steps: newWorkflowVersionSteps,
|
||||
trigger: newWorkflowVersionTrigger,
|
||||
});
|
||||
assertWorkflowVersionTriggerIsDefined(workflowVersionToCopy);
|
||||
assertWorkflowVersionHasSteps(workflowVersionToCopy);
|
||||
|
||||
return {
|
||||
...draftWorkflowVersion,
|
||||
name: draftWorkflowVersion.name ?? '',
|
||||
steps: newWorkflowVersionSteps,
|
||||
trigger: newWorkflowVersionTrigger,
|
||||
};
|
||||
let draftWorkflowVersion = await workflowVersionRepository.findOne({
|
||||
where: {
|
||||
workflowId,
|
||||
status: WorkflowVersionStatus.DRAFT,
|
||||
},
|
||||
});
|
||||
|
||||
if (!isDefined(draftWorkflowVersion)) {
|
||||
const workflowVersionsCount = await workflowVersionRepository.count({
|
||||
where: {
|
||||
workflowId,
|
||||
},
|
||||
});
|
||||
|
||||
const position = await this.recordPositionService.buildRecordPosition(
|
||||
{
|
||||
value: 'first',
|
||||
objectMetadata: {
|
||||
isCustom: false,
|
||||
nameSingular: 'workflowVersion',
|
||||
},
|
||||
workspaceId,
|
||||
},
|
||||
);
|
||||
|
||||
const insertResult = await workflowVersionRepository.insert({
|
||||
workflowId,
|
||||
name: `v${workflowVersionsCount + 1}`,
|
||||
status: WorkflowVersionStatus.DRAFT,
|
||||
position,
|
||||
});
|
||||
|
||||
draftWorkflowVersion = insertResult
|
||||
.generatedMaps[0] as WorkflowVersionWorkspaceEntity;
|
||||
}
|
||||
|
||||
assertWorkflowVersionIsDraft(draftWorkflowVersion);
|
||||
|
||||
const newWorkflowVersionTrigger = workflowVersionToCopy.trigger;
|
||||
const newWorkflowVersionSteps: WorkflowAction[] = [];
|
||||
|
||||
for (const step of workflowVersionToCopy.steps) {
|
||||
const duplicatedStep =
|
||||
await this.workflowVersionStepWorkspaceService.createDraftStep({
|
||||
step,
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
newWorkflowVersionSteps.push(duplicatedStep);
|
||||
}
|
||||
|
||||
await workflowVersionRepository.update(draftWorkflowVersion.id, {
|
||||
steps: newWorkflowVersionSteps,
|
||||
trigger: newWorkflowVersionTrigger,
|
||||
});
|
||||
|
||||
return {
|
||||
...draftWorkflowVersion,
|
||||
name: draftWorkflowVersion.name ?? '',
|
||||
steps: newWorkflowVersionSteps,
|
||||
trigger: newWorkflowVersionTrigger,
|
||||
};
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
async duplicateWorkflow({
|
||||
@@ -141,159 +151,167 @@ export class WorkflowVersionWorkspaceService {
|
||||
workflowIdToDuplicate: string;
|
||||
workflowVersionIdToCopy: string;
|
||||
}) {
|
||||
const workflowRepository =
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace(
|
||||
workspaceId,
|
||||
'workflow',
|
||||
{ shouldBypassPermissionChecks: true },
|
||||
);
|
||||
const authContext = buildSystemAuthContext(workspaceId);
|
||||
|
||||
const workflowVersionRepository =
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<WorkflowVersionWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'workflowVersion',
|
||||
{ shouldBypassPermissionChecks: true },
|
||||
);
|
||||
return this.globalWorkspaceOrmManager.executeInWorkspaceContext(
|
||||
authContext,
|
||||
async () => {
|
||||
const workflowRepository =
|
||||
await this.globalWorkspaceOrmManager.getRepository(
|
||||
workspaceId,
|
||||
'workflow',
|
||||
{ shouldBypassPermissionChecks: true },
|
||||
);
|
||||
|
||||
const sourceWorkflow = await workflowRepository.findOne({
|
||||
where: {
|
||||
id: workflowIdToDuplicate,
|
||||
},
|
||||
});
|
||||
const workflowVersionRepository =
|
||||
await this.globalWorkspaceOrmManager.getRepository<WorkflowVersionWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'workflowVersion',
|
||||
{ shouldBypassPermissionChecks: true },
|
||||
);
|
||||
|
||||
if (!isDefined(sourceWorkflow)) {
|
||||
throw new WorkflowVersionStepException(
|
||||
'Source workflow not found',
|
||||
WorkflowVersionStepExceptionCode.NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
const sourceVersion = await workflowVersionRepository.findOne({
|
||||
where: {
|
||||
id: workflowVersionIdToCopy,
|
||||
workflowId: workflowIdToDuplicate,
|
||||
},
|
||||
});
|
||||
|
||||
if (!isDefined(sourceVersion)) {
|
||||
throw new WorkflowVersionStepException(
|
||||
'WorkflowVersion to copy not found',
|
||||
WorkflowVersionStepExceptionCode.NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
assertWorkflowVersionTriggerIsDefined(sourceVersion);
|
||||
assertWorkflowVersionHasSteps(sourceVersion);
|
||||
|
||||
const workflowPosition =
|
||||
await this.recordPositionService.buildRecordPosition({
|
||||
value: 'first',
|
||||
objectMetadata: {
|
||||
isCustom: false,
|
||||
nameSingular: 'workflow',
|
||||
},
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
const insertWorkflowResult = await workflowRepository.insert({
|
||||
name: `${sourceWorkflow.name} (Duplicate)`,
|
||||
statuses: [WorkflowStatus.DRAFT],
|
||||
position: workflowPosition,
|
||||
});
|
||||
|
||||
const newWorkflowId = (
|
||||
insertWorkflowResult.generatedMaps[0] as WorkflowWorkspaceEntity
|
||||
).id;
|
||||
|
||||
const versionPosition =
|
||||
await this.recordPositionService.buildRecordPosition({
|
||||
value: 'first',
|
||||
objectMetadata: {
|
||||
isCustom: false,
|
||||
nameSingular: 'workflowVersion',
|
||||
},
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
const insertVersionResult = await workflowVersionRepository.insert({
|
||||
workflowId: newWorkflowId,
|
||||
name: 'v1',
|
||||
status: WorkflowVersionStatus.DRAFT,
|
||||
position: versionPosition,
|
||||
});
|
||||
|
||||
const newDraftVersion = insertVersionResult
|
||||
.generatedMaps[0] as WorkflowVersionWorkspaceEntity;
|
||||
|
||||
const newTrigger = sourceVersion.trigger;
|
||||
const sourceToClonedPairs: Array<{
|
||||
source: WorkflowAction;
|
||||
duplicated: WorkflowAction;
|
||||
}> = [];
|
||||
const oldToNewIdMap = new Map<string, string>();
|
||||
|
||||
for (const step of sourceVersion.steps ?? []) {
|
||||
const clonedStep =
|
||||
await this.workflowVersionStepOperationsWorkspaceService.cloneStep({
|
||||
step,
|
||||
workspaceId,
|
||||
const sourceWorkflow = await workflowRepository.findOne({
|
||||
where: {
|
||||
id: workflowIdToDuplicate,
|
||||
},
|
||||
});
|
||||
|
||||
sourceToClonedPairs.push({
|
||||
source: step,
|
||||
duplicated: clonedStep,
|
||||
});
|
||||
oldToNewIdMap.set(step.id, clonedStep.id);
|
||||
}
|
||||
|
||||
const remappedTrigger = isDefined(newTrigger)
|
||||
? {
|
||||
...newTrigger,
|
||||
nextStepIds: (newTrigger.nextStepIds ?? []).map(
|
||||
(oldId) => oldToNewIdMap.get(oldId) ?? oldId,
|
||||
),
|
||||
if (!isDefined(sourceWorkflow)) {
|
||||
throw new WorkflowVersionStepException(
|
||||
'Source workflow not found',
|
||||
WorkflowVersionStepExceptionCode.NOT_FOUND,
|
||||
);
|
||||
}
|
||||
: undefined;
|
||||
|
||||
const remappedSteps: WorkflowAction[] = sourceToClonedPairs.map(
|
||||
({ source, duplicated }) => {
|
||||
const remappedStep = {
|
||||
...duplicated,
|
||||
nextStepIds: (source.nextStepIds ?? []).map(
|
||||
(oldId) => oldToNewIdMap.get(oldId) ?? oldId,
|
||||
),
|
||||
};
|
||||
const sourceVersion = await workflowVersionRepository.findOne({
|
||||
where: {
|
||||
id: workflowVersionIdToCopy,
|
||||
workflowId: workflowIdToDuplicate,
|
||||
},
|
||||
});
|
||||
|
||||
if (
|
||||
source.type === WorkflowActionType.ITERATOR &&
|
||||
isDefined(source.settings?.input?.initialLoopStepIds)
|
||||
) {
|
||||
remappedStep.settings = {
|
||||
...remappedStep.settings,
|
||||
input: {
|
||||
...remappedStep.settings.input,
|
||||
initialLoopStepIds: source.settings.input.initialLoopStepIds.map(
|
||||
if (!isDefined(sourceVersion)) {
|
||||
throw new WorkflowVersionStepException(
|
||||
'WorkflowVersion to copy not found',
|
||||
WorkflowVersionStepExceptionCode.NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
assertWorkflowVersionTriggerIsDefined(sourceVersion);
|
||||
assertWorkflowVersionHasSteps(sourceVersion);
|
||||
|
||||
const workflowPosition =
|
||||
await this.recordPositionService.buildRecordPosition({
|
||||
value: 'first',
|
||||
objectMetadata: {
|
||||
isCustom: false,
|
||||
nameSingular: 'workflow',
|
||||
},
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
const insertWorkflowResult = await workflowRepository.insert({
|
||||
name: `${sourceWorkflow.name} (Duplicate)`,
|
||||
statuses: [WorkflowStatus.DRAFT],
|
||||
position: workflowPosition,
|
||||
});
|
||||
|
||||
const newWorkflowId = (
|
||||
insertWorkflowResult.generatedMaps[0] as WorkflowWorkspaceEntity
|
||||
).id;
|
||||
|
||||
const versionPosition =
|
||||
await this.recordPositionService.buildRecordPosition({
|
||||
value: 'first',
|
||||
objectMetadata: {
|
||||
isCustom: false,
|
||||
nameSingular: 'workflowVersion',
|
||||
},
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
const insertVersionResult = await workflowVersionRepository.insert({
|
||||
workflowId: newWorkflowId,
|
||||
name: 'v1',
|
||||
status: WorkflowVersionStatus.DRAFT,
|
||||
position: versionPosition,
|
||||
});
|
||||
|
||||
const newDraftVersion = insertVersionResult
|
||||
.generatedMaps[0] as WorkflowVersionWorkspaceEntity;
|
||||
|
||||
const newTrigger = sourceVersion.trigger;
|
||||
const sourceToClonedPairs: Array<{
|
||||
source: WorkflowAction;
|
||||
duplicated: WorkflowAction;
|
||||
}> = [];
|
||||
const oldToNewIdMap = new Map<string, string>();
|
||||
|
||||
for (const step of sourceVersion.steps ?? []) {
|
||||
const clonedStep =
|
||||
await this.workflowVersionStepOperationsWorkspaceService.cloneStep({
|
||||
step,
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
sourceToClonedPairs.push({
|
||||
source: step,
|
||||
duplicated: clonedStep,
|
||||
});
|
||||
oldToNewIdMap.set(step.id, clonedStep.id);
|
||||
}
|
||||
|
||||
const remappedTrigger = isDefined(newTrigger)
|
||||
? {
|
||||
...newTrigger,
|
||||
nextStepIds: (newTrigger.nextStepIds ?? []).map(
|
||||
(oldId) => oldToNewIdMap.get(oldId) ?? oldId,
|
||||
),
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
: undefined;
|
||||
|
||||
return remappedStep;
|
||||
const remappedSteps: WorkflowAction[] = sourceToClonedPairs.map(
|
||||
({ source, duplicated }) => {
|
||||
const remappedStep = {
|
||||
...duplicated,
|
||||
nextStepIds: (source.nextStepIds ?? []).map(
|
||||
(oldId) => oldToNewIdMap.get(oldId) ?? oldId,
|
||||
),
|
||||
};
|
||||
|
||||
if (
|
||||
source.type === WorkflowActionType.ITERATOR &&
|
||||
isDefined(source.settings?.input?.initialLoopStepIds)
|
||||
) {
|
||||
remappedStep.settings = {
|
||||
...remappedStep.settings,
|
||||
input: {
|
||||
...remappedStep.settings.input,
|
||||
initialLoopStepIds:
|
||||
source.settings.input.initialLoopStepIds.map(
|
||||
(oldId) => oldToNewIdMap.get(oldId) ?? oldId,
|
||||
),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return remappedStep;
|
||||
},
|
||||
);
|
||||
|
||||
await workflowVersionRepository.update(newDraftVersion.id, {
|
||||
steps: remappedSteps,
|
||||
trigger: remappedTrigger,
|
||||
});
|
||||
|
||||
return {
|
||||
...newDraftVersion,
|
||||
name: newDraftVersion.name ?? '',
|
||||
steps: remappedSteps,
|
||||
trigger: remappedTrigger ?? null,
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
await workflowVersionRepository.update(newDraftVersion.id, {
|
||||
steps: remappedSteps,
|
||||
trigger: remappedTrigger,
|
||||
});
|
||||
|
||||
return {
|
||||
...newDraftVersion,
|
||||
name: newDraftVersion.name ?? '',
|
||||
steps: remappedSteps,
|
||||
trigger: remappedTrigger ?? null,
|
||||
};
|
||||
}
|
||||
|
||||
async updateWorkflowVersionPositions({
|
||||
@@ -305,51 +323,63 @@ export class WorkflowVersionWorkspaceService {
|
||||
positions: WorkflowStepPositionUpdateInput[];
|
||||
workspaceId: string;
|
||||
}) {
|
||||
const workflowVersionRepository =
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<WorkflowVersionWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'workflowVersion',
|
||||
{ shouldBypassPermissionChecks: true },
|
||||
);
|
||||
const authContext = buildSystemAuthContext(workspaceId);
|
||||
|
||||
const workflowVersion = await workflowVersionRepository.findOneOrFail({
|
||||
where: {
|
||||
id: workflowVersionId,
|
||||
},
|
||||
});
|
||||
await this.globalWorkspaceOrmManager.executeInWorkspaceContext(
|
||||
authContext,
|
||||
async () => {
|
||||
const workflowVersionRepository =
|
||||
await this.globalWorkspaceOrmManager.getRepository<WorkflowVersionWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'workflowVersion',
|
||||
{ shouldBypassPermissionChecks: true },
|
||||
);
|
||||
|
||||
assertWorkflowVersionIsDraft(workflowVersion);
|
||||
const workflowVersion = await workflowVersionRepository.findOneOrFail({
|
||||
where: {
|
||||
id: workflowVersionId,
|
||||
},
|
||||
});
|
||||
|
||||
const triggerPosition = positions.find(
|
||||
(position) => position.id === TRIGGER_STEP_ID,
|
||||
);
|
||||
assertWorkflowVersionIsDraft(workflowVersion);
|
||||
|
||||
const updatedTrigger =
|
||||
isDefined(triggerPosition) && isDefined(workflowVersion.trigger)
|
||||
? {
|
||||
...workflowVersion.trigger,
|
||||
position: triggerPosition.position,
|
||||
const triggerPosition = positions.find(
|
||||
(position) => position.id === TRIGGER_STEP_ID,
|
||||
);
|
||||
|
||||
const updatedTrigger =
|
||||
isDefined(triggerPosition) && isDefined(workflowVersion.trigger)
|
||||
? {
|
||||
...workflowVersion.trigger,
|
||||
position: triggerPosition.position,
|
||||
}
|
||||
: undefined;
|
||||
|
||||
const updatedSteps = workflowVersion.steps?.map((step) => {
|
||||
const updatedStep = positions.find(
|
||||
(position) => position.id === step.id,
|
||||
);
|
||||
|
||||
if (updatedStep) {
|
||||
return {
|
||||
...step,
|
||||
position: updatedStep.position,
|
||||
};
|
||||
}
|
||||
: undefined;
|
||||
|
||||
const updatedSteps = workflowVersion.steps?.map((step) => {
|
||||
const updatedStep = positions.find((position) => position.id === step.id);
|
||||
return step;
|
||||
});
|
||||
|
||||
if (updatedStep) {
|
||||
return {
|
||||
...step,
|
||||
position: updatedStep.position,
|
||||
const updatePayload = {
|
||||
...(!isDefined(updatedTrigger) ? {} : { trigger: updatedTrigger }),
|
||||
...(!isDefined(updatedSteps) ? {} : { steps: updatedSteps }),
|
||||
};
|
||||
}
|
||||
|
||||
return step;
|
||||
});
|
||||
|
||||
const updatePayload = {
|
||||
...(!isDefined(updatedTrigger) ? {} : { trigger: updatedTrigger }),
|
||||
...(!isDefined(updatedSteps) ? {} : { steps: updatedSteps }),
|
||||
};
|
||||
|
||||
await workflowVersionRepository.update(workflowVersionId, updatePayload);
|
||||
await workflowVersionRepository.update(
|
||||
workflowVersionId,
|
||||
updatePayload,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user