Reorganize logic function files (#17766)

reorganize according to

<img width="1243" height="725" alt="Pasted Graphic"
src="https://github.com/user-attachments/assets/ba65dd10-8eec-4b13-ad49-9726edd3b79c"
/>

Not working yet
This commit is contained in:
martmull
2026-02-09 12:36:39 +01:00
committed by GitHub
parent bf4c348c8b
commit 9162685b2e
149 changed files with 1715 additions and 5272 deletions
@@ -1,13 +1,12 @@
import { Test, type TestingModule } from '@nestjs/testing';
import { getRepositoryToken } from '@nestjs/typeorm';
import { ApplicationService } from 'src/engine/core-modules/application/services/application.service';
import { AiAgentRoleService } from 'src/engine/metadata-modules/ai/ai-agent-role/ai-agent-role.service';
import { AgentEntity } from 'src/engine/metadata-modules/ai/ai-agent/entities/agent.entity';
import { createEmptyAllFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/constant/create-empty-all-flat-entity-maps.constant';
import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service';
import { LogicFunctionRuntime } from 'src/engine/metadata-modules/logic-function/logic-function.entity';
import { LogicFunctionService } from 'src/engine/metadata-modules/logic-function/services/logic-function.service';
import { LogicFunctionMetadataService } from 'src/engine/metadata-modules/logic-function/services/logic-function-metadata.service';
import { type FlatLogicFunction } from 'src/engine/metadata-modules/logic-function/types/flat-logic-function.type';
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import { RoleTargetEntity } from 'src/engine/metadata-modules/role-target/role-target.entity';
@@ -27,8 +26,7 @@ const mockWorkspaceId = 'workspace-id';
describe('WorkflowVersionStepOperationsWorkspaceService', () => {
let service: WorkflowVersionStepOperationsWorkspaceService;
let globalWorkspaceOrmManager: jest.Mocked<GlobalWorkspaceOrmManager>;
let logicFunctionService: jest.Mocked<LogicFunctionService>;
let applicationService: jest.Mocked<ApplicationService>;
let logicFunctionMetadataService: jest.Mocked<LogicFunctionMetadataService>;
let codeStepBuildService: jest.Mocked<CodeStepBuildService>;
let agentRepository: jest.Mocked<any>;
let roleTargetRepository: jest.Mocked<any>;
@@ -40,22 +38,34 @@ describe('WorkflowVersionStepOperationsWorkspaceService', () => {
let flatEntityMapsCacheService: jest.Mocked<WorkspaceManyOrAllFlatEntityMapsCacheService>;
beforeEach(async () => {
applicationService = {
findWorkspaceTwentyStandardAndCustomApplicationOrThrow: jest
.fn()
.mockResolvedValue({
workspaceCustomFlatApplication: {
universalIdentifier: 'app-universal-id',
},
}),
} as unknown as jest.Mocked<ApplicationService>;
codeStepBuildService = {
seedCodeStepFiles: jest.fn().mockResolvedValue({
sourceHandlerPath: 'workflow/logic-fn-id/src/index.ts',
builtHandlerPath: 'workflow/logic-fn-id/src/index.mjs',
checksum: 'seed-checksum',
}),
createCodeStepLogicFunction: jest.fn().mockResolvedValue({
id: 'new-function-id',
name: 'Test Function',
description: 'Test Description',
workspaceId: mockWorkspaceId,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
runtime: LogicFunctionRuntime.NODE22,
timeoutSeconds: 30,
sourceHandlerPath: 'src/index.ts',
builtHandlerPath: 'index.mjs',
handlerName: 'main',
checksum: null,
toolInputSchema: null,
isTool: false,
universalIdentifier: 'universal-id',
applicationId: 'application-id',
cronTriggerSettings: null,
databaseEventTriggerSettings: null,
httpRouteTriggerSettings: null,
}),
copySourceAndBuiltForNewCodeStep: jest.fn().mockResolvedValue(undefined),
duplicateCodeStepLogicFunction: jest.fn().mockResolvedValue({
id: 'new-function-id',
@@ -81,10 +91,10 @@ describe('WorkflowVersionStepOperationsWorkspaceService', () => {
}),
} as unknown as jest.Mocked<CodeStepBuildService>;
logicFunctionService = {
logicFunctionMetadataService = {
createOne: jest.fn(),
destroyOne: jest.fn(),
} as unknown as jest.Mocked<LogicFunctionService>;
} as unknown as jest.Mocked<LogicFunctionMetadataService>;
agentRepository = {
findOne: jest.fn(),
@@ -128,12 +138,8 @@ describe('WorkflowVersionStepOperationsWorkspaceService', () => {
useValue: globalWorkspaceOrmManager,
},
{
provide: LogicFunctionService,
useValue: logicFunctionService,
},
{
provide: ApplicationService,
useValue: applicationService,
provide: LogicFunctionMetadataService,
useValue: logicFunctionMetadataService,
},
{
provide: CodeStepBuildService,
@@ -207,7 +213,7 @@ describe('WorkflowVersionStepOperationsWorkspaceService', () => {
workspaceId: mockWorkspaceId,
});
expect(logicFunctionService.destroyOne).toHaveBeenCalledWith({
expect(logicFunctionMetadataService.destroyOne).toHaveBeenCalledWith({
id: 'function-id',
workspaceId: mockWorkspaceId,
});
@@ -317,7 +323,9 @@ describe('WorkflowVersionStepOperationsWorkspaceService', () => {
httpRouteTriggerSettings: null,
};
logicFunctionService.createOne.mockResolvedValue(mockFlatLogicFunction);
logicFunctionMetadataService.createOne.mockResolvedValue(
mockFlatLogicFunction,
);
const result = await service.runStepCreationSideEffectsAndBuildStep({
type: WorkflowActionType.CODE,
@@ -430,7 +438,7 @@ describe('WorkflowVersionStepOperationsWorkspaceService', () => {
},
);
logicFunctionService.createOne.mockResolvedValue(
logicFunctionMetadataService.createOne.mockResolvedValue(
mockNewFlatLogicFunction,
);
@@ -1,4 +0,0 @@
export const CODE_STEP_DEFAULT_INPUT_SCHEMA = {
a: null,
b: null,
};
@@ -1,8 +0,0 @@
var main = async (params) => {
const { a, b } = params;
const message = `Hello, input: ${a} and ${b}`;
return { message };
};
export {
main
};
@@ -1,12 +0,0 @@
export const main = async (params: {
a: string;
b: number;
}): Promise<object> => {
const { a, b } = params;
// Rename the parameters and code below with your own logic
// This is just an example
const message = `Hello, input: ${a} and ${b}`;
return { message };
};
@@ -2,12 +2,16 @@ import { Injectable } from '@nestjs/common';
import { isDefined } from 'twenty-shared/utils';
import { v4 } from 'uuid';
import {
CronTriggerSettings,
DatabaseEventTriggerSettings,
HttpRouteTriggerSettings,
} from 'twenty-shared/application';
import { ApplicationService } from 'src/engine/core-modules/application/services/application.service';
import { LogicFunctionSourceBuilderService } from 'src/engine/core-modules/logic-function/logic-function-source-builder/logic-function-source-builder.service';
import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service';
import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps.util';
import { LogicFunctionService } from 'src/engine/metadata-modules/logic-function/services/logic-function.service';
import { LogicFunctionMetadataService } from 'src/engine/metadata-modules/logic-function/services/logic-function-metadata.service';
import { type FlatLogicFunction } from 'src/engine/metadata-modules/logic-function/types/flat-logic-function.type';
import { findFlatLogicFunctionOrThrow } from 'src/engine/metadata-modules/logic-function/utils/find-flat-logic-function-or-throw.util';
import { fromCreateLogicFunctionInputToFlatLogicFunction } from 'src/engine/metadata-modules/logic-function/utils/from-create-logic-function-input-to-flat-logic-function.util';
@@ -15,6 +19,9 @@ import {
WorkflowActionType,
type WorkflowAction,
} from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action.type';
import { LogicFunctionResourceService } from 'src/engine/core-modules/logic-function/logic-function-resource/logic-function-resource.service';
import { SEED_LOGIC_FUNCTION_INPUT_SCHEMA } from 'src/engine/core-modules/logic-function/logic-function-resource/constants/seed-logic-function-input-schema';
import type { JsonbProperty } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/jsonb-property.type';
const WORKFLOW_BASE_FOLDER_PREFIX = 'workflow';
@@ -22,11 +29,52 @@ const WORKFLOW_BASE_FOLDER_PREFIX = 'workflow';
export class CodeStepBuildService {
constructor(
private readonly workspaceManyOrAllFlatEntityMapsCacheService: WorkspaceManyOrAllFlatEntityMapsCacheService,
private readonly logicFunctionService: LogicFunctionService,
private readonly logicFunctionMetadataService: LogicFunctionMetadataService,
private readonly applicationService: ApplicationService,
private readonly logicFunctionSourceBuilderService: LogicFunctionSourceBuilderService,
private readonly logicFunctionResourceService: LogicFunctionResourceService,
) {}
private getSourceSubfolderForCodeStep(logicFunctionId: string) {
return `${WORKFLOW_BASE_FOLDER_PREFIX}/${logicFunctionId}`;
}
async createCodeStepLogicFunction({
logicFunctionId,
workspaceId,
}: {
logicFunctionId: string;
workspaceId: string;
}) {
const { workspaceCustomFlatApplication } =
await this.applicationService.findWorkspaceTwentyStandardAndCustomApplicationOrThrow(
{
workspaceId,
},
);
const { sourceHandlerPath, builtHandlerPath, handlerName, checksum } =
await this.logicFunctionResourceService.seedSourceFiles({
sourceSubfolder: this.getSourceSubfolderForCodeStep(logicFunctionId),
workspaceId,
applicationUniversalIdentifier:
workspaceCustomFlatApplication.universalIdentifier,
});
return await this.logicFunctionMetadataService.createOne({
input: {
id: logicFunctionId,
name: 'A Logic Function Code Workflow Step',
description: '',
sourceHandlerPath,
builtHandlerPath,
handlerName,
checksum,
toolInputSchema: SEED_LOGIC_FUNCTION_INPUT_SCHEMA,
},
workspaceId,
ownerFlatApplication: workspaceCustomFlatApplication,
});
}
async duplicateCodeStepLogicFunction({
existingLogicFunctionId,
workspaceId,
@@ -57,36 +105,74 @@ export class CodeStepBuildService {
resolvedOwnerFlatApplication.universalIdentifier;
const newId = v4();
const newUniversalIdentifier = v4();
const { sourceHandlerPath, builtHandlerPath } = existingLogicFunction;
const toSourceHandlerPath = sourceHandlerPath.replace(
existingLogicFunction.id,
newId,
);
const toBuiltHandlerPath = builtHandlerPath.replace(
existingLogicFunction.id,
newId,
);
await this.logicFunctionResourceService.copyResources({
fromSourceHandlerPath: sourceHandlerPath,
fromBuiltHandlerPath: builtHandlerPath,
toSourceHandlerPath,
toBuiltHandlerPath,
workspaceId,
applicationUniversalIdentifier,
});
const newFlatLogicFunction =
fromCreateLogicFunctionInputToFlatLogicFunction({
createLogicFunctionInput: {
name: existingLogicFunction.name,
description: existingLogicFunction.description ?? undefined,
timeoutSeconds: existingLogicFunction.timeoutSeconds,
...existingLogicFunction,
id: newId,
universalIdentifier: newUniversalIdentifier,
description: existingLogicFunction.description ?? undefined,
builtHandlerPath: toBuiltHandlerPath,
sourceHandlerPath: toSourceHandlerPath,
toolInputSchema: existingLogicFunction.toolInputSchema ?? {},
checksum: existingLogicFunction.checksum ?? '[default-checksum]', // TODO: checksum should never be null, update column in logicFunction entity to set it non nullable
cronTriggerSettings: existingLogicFunction.cronTriggerSettings as
| JsonbProperty<CronTriggerSettings>
| undefined,
databaseEventTriggerSettings:
existingLogicFunction.databaseEventTriggerSettings as
| JsonbProperty<DatabaseEventTriggerSettings>
| undefined,
httpRouteTriggerSettings:
existingLogicFunction.httpRouteTriggerSettings as
| JsonbProperty<HttpRouteTriggerSettings>
| undefined,
},
workspaceId,
ownerFlatApplication: resolvedOwnerFlatApplication,
});
await this.logicFunctionSourceBuilderService.copySourceAndBuilt({
fromSourceHandlerPath: existingLogicFunction.sourceHandlerPath,
fromBuiltHandlerPath: existingLogicFunction.builtHandlerPath,
toSourceHandlerPath: newFlatLogicFunction.sourceHandlerPath,
toBuiltHandlerPath: newFlatLogicFunction.builtHandlerPath,
workspaceId,
applicationUniversalIdentifier,
});
const created = await this.logicFunctionService.createOne({
const created = await this.logicFunctionMetadataService.createOne({
input: {
name: existingLogicFunction.name,
description: existingLogicFunction.description ?? undefined,
timeoutSeconds: existingLogicFunction.timeoutSeconds,
id: newFlatLogicFunction.id,
sourceHandlerPath: newFlatLogicFunction.sourceHandlerPath,
builtHandlerPath: newFlatLogicFunction.builtHandlerPath,
checksum: existingLogicFunction.checksum ?? undefined,
...newFlatLogicFunction,
description: newFlatLogicFunction.description ?? undefined,
checksum: newFlatLogicFunction.checksum ?? '[default-checksum]',
toolInputSchema: newFlatLogicFunction.toolInputSchema ?? {},
cronTriggerSettings: newFlatLogicFunction.cronTriggerSettings as
| JsonbProperty<CronTriggerSettings>
| undefined,
databaseEventTriggerSettings:
newFlatLogicFunction.databaseEventTriggerSettings as
| JsonbProperty<DatabaseEventTriggerSettings>
| undefined,
httpRouteTriggerSettings:
newFlatLogicFunction.httpRouteTriggerSettings as
| JsonbProperty<HttpRouteTriggerSettings>
| undefined,
},
workspaceId,
ownerFlatApplication: resolvedOwnerFlatApplication,
@@ -161,14 +247,14 @@ export class CodeStepBuildService {
}
const { checksum } =
await this.logicFunctionSourceBuilderService.buildFromSource({
await this.logicFunctionResourceService.buildFromSource({
sourceHandlerPath: flatLogicFunction.sourceHandlerPath,
builtHandlerPath: flatLogicFunction.builtHandlerPath,
workspaceId,
applicationUniversalIdentifier,
});
await this.logicFunctionService.updateChecksum({
await this.logicFunctionMetadataService.updateChecksum({
id: flatLogicFunction.id,
checksum,
workspaceId,
@@ -15,20 +15,18 @@ import { Repository } from 'typeorm';
import { v4 } from 'uuid';
import { getFlatFieldsFromFlatObjectMetadata } from 'src/engine/api/graphql/workspace-schema-builder/utils/get-flat-fields-for-flat-object-metadata.util';
import { ApplicationService } from 'src/engine/core-modules/application/services/application.service';
import { type WorkflowStepPositionInput } from 'src/engine/core-modules/workflow/dtos/update-workflow-step-position-input.dto';
import { AiAgentRoleService } from 'src/engine/metadata-modules/ai/ai-agent-role/ai-agent-role.service';
import { AgentEntity } from 'src/engine/metadata-modules/ai/ai-agent/entities/agent.entity';
import { DEFAULT_SMART_MODEL } from 'src/engine/metadata-modules/ai/ai-models/constants/ai-models.const';
import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service';
import { LogicFunctionService } from 'src/engine/metadata-modules/logic-function/services/logic-function.service';
import { LogicFunctionMetadataService } from 'src/engine/metadata-modules/logic-function/services/logic-function-metadata.service';
import { findFlatLogicFunctionOrThrow } from 'src/engine/metadata-modules/logic-function/utils/find-flat-logic-function-or-throw.util';
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 { 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 { CODE_STEP_DEFAULT_INPUT_SCHEMA } from 'src/modules/workflow/workflow-builder/workflow-version-step/code-step/constants/seed-project/code-step-default-input-schema';
import { CodeStepBuildService } from 'src/modules/workflow/workflow-builder/workflow-version-step/code-step/services/code-step-build.service';
import {
WorkflowVersionStepException,
@@ -66,8 +64,7 @@ const ITERATOR_EMPTY_STEP_POSITION_OFFSET = {
export class WorkflowVersionStepOperationsWorkspaceService {
constructor(
private readonly globalWorkspaceOrmManager: GlobalWorkspaceOrmManager,
private readonly logicFunctionService: LogicFunctionService,
private readonly applicationService: ApplicationService,
private readonly logicFunctionMetadataService: LogicFunctionMetadataService,
private readonly codeStepBuildService: CodeStepBuildService,
@InjectRepository(AgentEntity)
private readonly agentRepository: Repository<AgentEntity>,
@@ -90,7 +87,7 @@ export class WorkflowVersionStepOperationsWorkspaceService {
}) {
switch (step.type) {
case WorkflowActionType.CODE: {
await this.logicFunctionService.destroyOne({
await this.logicFunctionMetadataService.destroyOne({
id: step.settings.input.logicFunctionId,
workspaceId,
});
@@ -157,15 +154,11 @@ export class WorkflowVersionStepOperationsWorkspaceService {
case WorkflowActionType.CODE: {
const logicFunctionId = id ?? v4();
// createOne handles seeding source files with default seed project
const newLogicFunction = await this.logicFunctionService.createOne({
input: {
id: logicFunctionId,
name: 'A Logic Function Code Workflow Step',
description: '',
},
workspaceId,
});
const newLogicFunction =
await this.codeStepBuildService.createCodeStepLogicFunction({
logicFunctionId,
workspaceId,
});
if (!isDefined(newLogicFunction)) {
throw new WorkflowVersionStepException(
@@ -192,7 +185,7 @@ export class WorkflowVersionStepOperationsWorkspaceService {
},
input: {
logicFunctionId: newLogicFunction.id,
logicFunctionInput: CODE_STEP_DEFAULT_INPUT_SCHEMA,
logicFunctionInput: newLogicFunction.toolInputSchema ?? {},
},
},
},
@@ -2,7 +2,6 @@ import { Module } from '@nestjs/common';
import { NestjsQueryTypeOrmModule } from '@ptc-org/nestjs-query-typeorm';
import { ApplicationModule } from 'src/engine/core-modules/application/application.module';
import { AiAgentRoleModule } from 'src/engine/metadata-modules/ai/ai-agent-role/ai-agent-role.module';
import { AgentEntity } from 'src/engine/metadata-modules/ai/ai-agent/entities/agent.entity';
import { WorkspaceManyOrAllFlatEntityMapsCacheModule } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.module';
@@ -26,7 +25,6 @@ import { WorkflowVersionStepWorkspaceService } from 'src/modules/workflow/workfl
WorkflowSchemaModule,
LogicFunctionModule,
WorkflowCommonModule,
ApplicationModule,
CodeStepBuildModule,
AiAgentRoleModule,
WorkspaceCacheModule,