Rework types for logic function (#18074)
## Summary - **Consolidate logic function services**: Remove `LogicFunctionMetadataService` and consolidate all logic function CRUD operations into `LogicFunctionFromSourceService`, with a new `LogicFunctionFromSourceHelperService` for shared validation/migration logic - **Introduce typed conversion utils following the skill pattern**: Add `fromCreateLogicFunctionFromSourceInputToUniversalFlatLogicFunctionToCreate` and `fromUpdateLogicFunctionFromSourceInputToFlatLogicFunctionToUpdate` that convert DTO inputs directly to flat entities (`UniversalFlatLogicFunction` / `FlatLogicFunction`), replacing the previous intermediate `UpdateLogicFunctionMetadataParams` indirection - **Simplify `CodeStepBuildService`**: Remove ~100 lines of manual duplication logic by delegating to `LogicFunctionFromSourceService.duplicateOneWithSource` - **Remove completed 1-17 migration**: Delete `MigrateWorkflowCodeStepsCommand` and associated utils that migrated workflow code steps from serverless functions to logic functions
This commit is contained in:
@@ -4570,13 +4570,11 @@ export type UpdateLogicFunctionFromSourceInput = {
|
||||
};
|
||||
|
||||
export type UpdateLogicFunctionFromSourceInputUpdates = {
|
||||
checksum?: InputMaybe<Scalars['String']>;
|
||||
cronTriggerSettings?: InputMaybe<Scalars['JSON']>;
|
||||
databaseEventTriggerSettings?: InputMaybe<Scalars['JSON']>;
|
||||
description?: InputMaybe<Scalars['String']>;
|
||||
handlerName?: InputMaybe<Scalars['String']>;
|
||||
httpRouteTriggerSettings?: InputMaybe<Scalars['JSON']>;
|
||||
isBuildUpToDate?: InputMaybe<Scalars['Boolean']>;
|
||||
isTool?: InputMaybe<Scalars['Boolean']>;
|
||||
name?: InputMaybe<Scalars['String']>;
|
||||
sourceHandlerCode?: InputMaybe<Scalars['String']>;
|
||||
|
||||
-301
@@ -1,301 +0,0 @@
|
||||
import { Logger } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import crypto from 'crypto';
|
||||
|
||||
import { Command } from 'nest-commander';
|
||||
import { FileFolder, type Sources } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { In, Repository } from 'typeorm';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { ActiveOrSuspendedWorkspacesMigrationCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner';
|
||||
import { RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspaces-migration.command-runner';
|
||||
import {
|
||||
type CodeStepMigrationTarget,
|
||||
collectCodeStepMigrationTargets,
|
||||
migrateWorkflowCodeStepsWithMapping,
|
||||
type ServerlessToLogicFunctionMapping,
|
||||
} from 'src/database/commands/upgrade-version-command/1-17/utils/migrate-workflow-code-step.util';
|
||||
import { ApplicationService } from 'src/engine/core-modules/application/services/application.service';
|
||||
import { FileStorageService } from 'src/engine/core-modules/file-storage/file-storage.service';
|
||||
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
|
||||
import { LogicFunctionEntity } from 'src/engine/metadata-modules/logic-function/logic-function.entity';
|
||||
import { LogicFunctionMetadataService } from 'src/engine/metadata-modules/logic-function/services/logic-function-metadata.service';
|
||||
import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
|
||||
import { WorkflowVersionStatus } from 'src/modules/workflow/common/standard-objects/workflow-version.workspace-entity';
|
||||
|
||||
const OLD_BUILT_FOLDER = 'built-function';
|
||||
const OLD_SOURCE_FOLDER = 'serverless-function';
|
||||
const NEW_WORKFLOW_RESOURCE_PREFIX = 'workflow';
|
||||
|
||||
@Command({
|
||||
name: 'upgrade:1-17:migrate-workflow-code-steps',
|
||||
description:
|
||||
'Migrate workflow code steps from v1.16 (serverless) to v1.17 (logic function): create new logic function per (serverlessFunctionId, version), move files, update steps, delete old logic function.',
|
||||
})
|
||||
export class MigrateWorkflowCodeStepsCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner {
|
||||
protected readonly logger = new Logger(MigrateWorkflowCodeStepsCommand.name);
|
||||
|
||||
constructor(
|
||||
@InjectRepository(WorkspaceEntity)
|
||||
protected readonly workspaceRepository: Repository<WorkspaceEntity>,
|
||||
@InjectRepository(LogicFunctionEntity)
|
||||
private readonly logicFunctionRepository: Repository<LogicFunctionEntity>,
|
||||
protected readonly globalWorkspaceOrmManager: GlobalWorkspaceOrmManager,
|
||||
protected readonly dataSourceService: DataSourceService,
|
||||
private readonly fileStorageService: FileStorageService,
|
||||
private readonly applicationService: ApplicationService,
|
||||
private readonly logicFunctionMetadataService: LogicFunctionMetadataService,
|
||||
) {
|
||||
super(workspaceRepository, globalWorkspaceOrmManager, dataSourceService);
|
||||
}
|
||||
|
||||
override async runOnWorkspace({
|
||||
workspaceId,
|
||||
options,
|
||||
}: RunOnWorkspaceArgs): Promise<void> {
|
||||
const isDryRun = options.dryRun ?? false;
|
||||
|
||||
this.logger.log(
|
||||
`Running MigrateWorkflowCodeStepsCommand for workspace ${workspaceId}`,
|
||||
);
|
||||
|
||||
const workflowVersionRepository =
|
||||
await this.globalWorkspaceOrmManager.getRepository(
|
||||
workspaceId,
|
||||
'workflowVersion',
|
||||
{ shouldBypassPermissionChecks: true },
|
||||
);
|
||||
|
||||
const workflowVersions = await workflowVersionRepository.find({
|
||||
select: ['id', 'steps', 'status'],
|
||||
where: {
|
||||
status: In([WorkflowVersionStatus.DRAFT, WorkflowVersionStatus.ACTIVE]),
|
||||
},
|
||||
});
|
||||
|
||||
const allTargets = new Map<string, CodeStepMigrationTarget>();
|
||||
|
||||
for (const version of workflowVersions) {
|
||||
const targets = collectCodeStepMigrationTargets(version.steps);
|
||||
|
||||
for (const target of targets) {
|
||||
const key = `${target.serverlessFunctionId}:${target.serverlessFunctionVersion}`;
|
||||
|
||||
if (!allTargets.has(key)) {
|
||||
allTargets.set(key, target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const targetsList = Array.from(allTargets.values());
|
||||
|
||||
if (targetsList.length === 0) {
|
||||
this.logger.log(`No code steps to migrate in workspace ${workspaceId}`);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const mapping = new Map<string, string>();
|
||||
|
||||
if (!isDryRun) {
|
||||
for (const target of targetsList) {
|
||||
const newLogicFunctionId =
|
||||
await this.createLogicFunctionAndMigrateFiles(workspaceId, target);
|
||||
|
||||
if (isDefined(newLogicFunctionId)) {
|
||||
const key = `${target.serverlessFunctionId}:${target.serverlessFunctionVersion}`;
|
||||
|
||||
mapping.set(key, newLogicFunctionId);
|
||||
}
|
||||
}
|
||||
|
||||
const serverlessToLogicMapping: ServerlessToLogicFunctionMapping = (
|
||||
serverlessFunctionId,
|
||||
serverlessFunctionVersion,
|
||||
) => {
|
||||
const key = `${serverlessFunctionId}:${serverlessFunctionVersion}`;
|
||||
const newId = mapping.get(key);
|
||||
|
||||
if (!isDefined(newId)) {
|
||||
throw new Error(
|
||||
`Missing mapping for ${serverlessFunctionId}:${serverlessFunctionVersion}`,
|
||||
);
|
||||
}
|
||||
|
||||
return newId;
|
||||
};
|
||||
|
||||
for (const version of workflowVersions) {
|
||||
const { migratedSteps, hasChanges } =
|
||||
migrateWorkflowCodeStepsWithMapping(
|
||||
version.steps,
|
||||
serverlessToLogicMapping,
|
||||
);
|
||||
|
||||
if (!hasChanges) {
|
||||
continue;
|
||||
}
|
||||
|
||||
await workflowVersionRepository.update(
|
||||
{ id: version.id },
|
||||
{ steps: migratedSteps },
|
||||
);
|
||||
|
||||
this.logger.log(
|
||||
`Migrated workflow version ${version.id} in workspace ${workspaceId}`,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
this.logger.log(
|
||||
`[DRY RUN] Would create ${targetsList.length} new logic function(s), migrate files, update workflow steps, and delete ${new Set(targetsList.map((t) => t.serverlessFunctionId)).size} old logic function(s) in workspace ${workspaceId}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private async getApplicationUniversalIdentifier(
|
||||
applicationId: string,
|
||||
): Promise<string | null> {
|
||||
const application = await this.applicationService.findById(applicationId);
|
||||
|
||||
return application?.universalIdentifier ?? null;
|
||||
}
|
||||
|
||||
private async createLogicFunctionAndMigrateFiles(
|
||||
workspaceId: string,
|
||||
target: CodeStepMigrationTarget,
|
||||
): Promise<string | null> {
|
||||
const { serverlessFunctionId, serverlessFunctionVersion } = target;
|
||||
const version = serverlessFunctionVersion ?? 'draft';
|
||||
|
||||
const oldLogicFunction = await this.logicFunctionRepository.findOne({
|
||||
where: { id: serverlessFunctionId, workspaceId },
|
||||
});
|
||||
|
||||
if (!isDefined(oldLogicFunction)) {
|
||||
this.logger.warn(
|
||||
`Logic function ${serverlessFunctionId} not found in workspace ${workspaceId}, skipping`,
|
||||
);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
const newLogicFunctionId = v4();
|
||||
const applicationUniversalIdentifier =
|
||||
await this.getApplicationUniversalIdentifier(
|
||||
oldLogicFunction.applicationId,
|
||||
);
|
||||
|
||||
const { builtContent, sourceContent } = await this.readOldFunctionFiles(
|
||||
workspaceId,
|
||||
serverlessFunctionId,
|
||||
version,
|
||||
);
|
||||
|
||||
const checksum = crypto
|
||||
.createHash('md5')
|
||||
.update(builtContent)
|
||||
.digest('hex');
|
||||
|
||||
if (isDefined(applicationUniversalIdentifier)) {
|
||||
await this.uploadFunctionFiles(
|
||||
workspaceId,
|
||||
applicationUniversalIdentifier,
|
||||
newLogicFunctionId,
|
||||
{ builtContent, sourceContent },
|
||||
);
|
||||
}
|
||||
|
||||
await this.logicFunctionMetadataService.createOne({
|
||||
input: {
|
||||
id: newLogicFunctionId,
|
||||
name: oldLogicFunction.name,
|
||||
description: oldLogicFunction.description ?? undefined,
|
||||
timeoutSeconds: oldLogicFunction.timeoutSeconds ?? 300,
|
||||
toolInputSchema: oldLogicFunction.toolInputSchema ?? {},
|
||||
isTool: oldLogicFunction.isTool ?? false,
|
||||
handlerName: oldLogicFunction.handlerName,
|
||||
sourceHandlerPath: `${NEW_WORKFLOW_RESOURCE_PREFIX}/${newLogicFunctionId}/src/index.ts`,
|
||||
builtHandlerPath: `${NEW_WORKFLOW_RESOURCE_PREFIX}/${newLogicFunctionId}/src/index.mjs`,
|
||||
checksum,
|
||||
isBuildUpToDate: true,
|
||||
},
|
||||
workspaceId,
|
||||
ownerFlatApplication: oldLogicFunction.application,
|
||||
});
|
||||
|
||||
this.logger.log(
|
||||
`Created logic function ${newLogicFunctionId} (from ${serverlessFunctionId}/${version}) and migrated files in workspace ${workspaceId}`,
|
||||
);
|
||||
|
||||
return newLogicFunctionId;
|
||||
}
|
||||
|
||||
private async readOldFunctionFiles(
|
||||
workspaceId: string,
|
||||
serverlessFunctionId: string,
|
||||
version: string,
|
||||
): Promise<{ builtContent: string; sourceContent: string }> {
|
||||
const workspacePrefix = `workspace-${workspaceId}`;
|
||||
|
||||
const builtSources = await this.fileStorageService.readFolderLegacy(
|
||||
`${workspacePrefix}/${OLD_BUILT_FOLDER}/${serverlessFunctionId}/${version}`,
|
||||
);
|
||||
|
||||
const sourceSources = await this.fileStorageService.readFolderLegacy(
|
||||
`${workspacePrefix}/${OLD_SOURCE_FOLDER}/${serverlessFunctionId}/${version}`,
|
||||
);
|
||||
|
||||
// Old source layout may nest files under a `src/` key
|
||||
const sourceRoot =
|
||||
(sourceSources.src as Sources) ?? (sourceSources as Sources);
|
||||
|
||||
const builtContent = builtSources['index.mjs'] as string;
|
||||
const sourceContent = sourceRoot['index.ts'] as string;
|
||||
|
||||
if (!isDefined(builtContent) || !isDefined(sourceContent)) {
|
||||
throw new Error(
|
||||
`Missing index.mjs or index.ts for serverless function ${serverlessFunctionId}/${version} in workspace ${workspaceId}`,
|
||||
);
|
||||
}
|
||||
|
||||
return { builtContent, sourceContent };
|
||||
}
|
||||
|
||||
private async uploadFunctionFiles(
|
||||
workspaceId: string,
|
||||
applicationUniversalIdentifier: string,
|
||||
newLogicFunctionId: string,
|
||||
files: { builtContent: string; sourceContent: string },
|
||||
): Promise<void> {
|
||||
const resourcePath = `${NEW_WORKFLOW_RESOURCE_PREFIX}/${newLogicFunctionId}/src`;
|
||||
|
||||
await this.fileStorageService.writeFile({
|
||||
workspaceId,
|
||||
applicationUniversalIdentifier,
|
||||
fileFolder: FileFolder.BuiltLogicFunction,
|
||||
resourcePath: `${resourcePath}/index.mjs`,
|
||||
sourceFile: Buffer.from(files.builtContent),
|
||||
mimeType: 'application/javascript',
|
||||
settings: {
|
||||
isTemporaryFile: false,
|
||||
toDelete: false,
|
||||
},
|
||||
});
|
||||
|
||||
await this.fileStorageService.writeFile({
|
||||
workspaceId,
|
||||
applicationUniversalIdentifier,
|
||||
fileFolder: FileFolder.Source,
|
||||
resourcePath: `${resourcePath}/index.ts`,
|
||||
sourceFile: Buffer.from(files.sourceContent),
|
||||
mimeType: 'application/typescript',
|
||||
settings: {
|
||||
isTemporaryFile: false,
|
||||
toDelete: false,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
-5
@@ -11,7 +11,6 @@ import { MigrateDateTimeIsFilterValuesCommand } from 'src/database/commands/upgr
|
||||
import { MigrateNoteTargetToMorphRelationsCommand } from 'src/database/commands/upgrade-version-command/1-17/1-17-migrate-note-target-to-morph-relations.command';
|
||||
import { MigrateSendEmailRecipientsCommand } from 'src/database/commands/upgrade-version-command/1-17/1-17-migrate-send-email-recipients.command';
|
||||
import { MigrateTaskTargetToMorphRelationsCommand } from 'src/database/commands/upgrade-version-command/1-17/1-17-migrate-task-target-to-morph-relations.command';
|
||||
import { MigrateWorkflowCodeStepsCommand } from 'src/database/commands/upgrade-version-command/1-17/1-17-migrate-workflow-code-steps.command';
|
||||
import { SeedWorkflowV1_16Command } from 'src/database/commands/upgrade-version-command/1-17/1-17-seed-workflow-v1-16.command';
|
||||
import { ApplicationModule } from 'src/engine/core-modules/application/application.module';
|
||||
import { FeatureFlagEntity } from 'src/engine/core-modules/feature-flag/feature-flag.entity';
|
||||
@@ -25,7 +24,6 @@ import { DataSourceModule } from 'src/engine/metadata-modules/data-source/data-s
|
||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { FieldMetadataModule } from 'src/engine/metadata-modules/field-metadata/field-metadata.module';
|
||||
import { LogicFunctionEntity } from 'src/engine/metadata-modules/logic-function/logic-function.entity';
|
||||
import { LogicFunctionModule } from 'src/engine/metadata-modules/logic-function/logic-function.module';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { ObjectMetadataModule } from 'src/engine/metadata-modules/object-metadata/object-metadata.module';
|
||||
import { ViewFilterEntity } from 'src/engine/metadata-modules/view-filter/entities/view-filter.entity';
|
||||
@@ -65,7 +63,6 @@ import { TaskTargetWorkspaceEntity } from 'src/modules/task/standard-objects/tas
|
||||
ApplicationModule,
|
||||
UserWorkspaceModule,
|
||||
WorkspaceMigrationModule,
|
||||
LogicFunctionModule,
|
||||
RecordPositionModule,
|
||||
GlobalWorkspaceDataSourceModule,
|
||||
],
|
||||
@@ -79,7 +76,6 @@ import { TaskTargetWorkspaceEntity } from 'src/modules/task/standard-objects/tas
|
||||
DeleteFileRecordsAndUpdateTableCommand,
|
||||
MigrateSendEmailRecipientsCommand,
|
||||
MigrateDateTimeIsFilterValuesCommand,
|
||||
MigrateWorkflowCodeStepsCommand,
|
||||
SeedWorkflowV1_16Command,
|
||||
BackfillApplicationPackageFilesCommand,
|
||||
],
|
||||
@@ -93,7 +89,6 @@ import { TaskTargetWorkspaceEntity } from 'src/modules/task/standard-objects/tas
|
||||
MigrateSendEmailRecipientsCommand,
|
||||
MigrateDateTimeIsFilterValuesCommand,
|
||||
DeleteFileRecordsAndUpdateTableCommand,
|
||||
MigrateWorkflowCodeStepsCommand,
|
||||
SeedWorkflowV1_16Command,
|
||||
BackfillApplicationPackageFilesCommand,
|
||||
],
|
||||
|
||||
-125
@@ -1,125 +0,0 @@
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { WorkflowActionType } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action.type';
|
||||
|
||||
type LegacyCodeStepInput = {
|
||||
serverlessFunctionId: string;
|
||||
serverlessFunctionVersion?: string;
|
||||
serverlessFunctionInput?: Record<string, unknown>;
|
||||
};
|
||||
|
||||
type MigratedCodeStepInput = {
|
||||
logicFunctionId: string;
|
||||
logicFunctionInput?: Record<string, unknown>;
|
||||
};
|
||||
|
||||
type WorkflowStep = {
|
||||
id: string;
|
||||
type: string;
|
||||
settings: {
|
||||
input: LegacyCodeStepInput | MigratedCodeStepInput;
|
||||
outputSchema?: unknown;
|
||||
};
|
||||
};
|
||||
|
||||
export const needsCodeStepMigration = (
|
||||
input: LegacyCodeStepInput | MigratedCodeStepInput,
|
||||
): input is LegacyCodeStepInput => {
|
||||
return (
|
||||
'serverlessFunctionId' in input && isDefined(input.serverlessFunctionId)
|
||||
);
|
||||
};
|
||||
|
||||
export type CodeStepMigrationTarget = {
|
||||
serverlessFunctionId: string;
|
||||
serverlessFunctionVersion: string;
|
||||
};
|
||||
|
||||
export type ServerlessToLogicFunctionMapping = (
|
||||
serverlessFunctionId: string,
|
||||
serverlessFunctionVersion: string,
|
||||
) => string;
|
||||
|
||||
export const collectCodeStepMigrationTargets = (
|
||||
steps: unknown,
|
||||
): CodeStepMigrationTarget[] => {
|
||||
if (!isDefined(steps) || !Array.isArray(steps) || steps.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const typedSteps = steps as WorkflowStep[];
|
||||
const seen = new Set<string>();
|
||||
|
||||
const targets: CodeStepMigrationTarget[] = [];
|
||||
|
||||
for (const step of typedSteps) {
|
||||
if (step.type !== WorkflowActionType.CODE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const input = step.settings.input as
|
||||
| LegacyCodeStepInput
|
||||
| MigratedCodeStepInput;
|
||||
|
||||
if (!needsCodeStepMigration(input)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const version = input.serverlessFunctionVersion ?? 'draft';
|
||||
const key = `${input.serverlessFunctionId}:${version}`;
|
||||
|
||||
if (seen.has(key)) {
|
||||
continue;
|
||||
}
|
||||
seen.add(key);
|
||||
targets.push({
|
||||
serverlessFunctionId: input.serverlessFunctionId,
|
||||
serverlessFunctionVersion: version,
|
||||
});
|
||||
}
|
||||
|
||||
return targets;
|
||||
};
|
||||
|
||||
export const migrateWorkflowCodeStepsWithMapping = (
|
||||
steps: unknown,
|
||||
mapping: ServerlessToLogicFunctionMapping,
|
||||
): { migratedSteps: WorkflowStep[]; hasChanges: boolean } => {
|
||||
if (!isDefined(steps) || !Array.isArray(steps) || steps.length === 0) {
|
||||
return { migratedSteps: [], hasChanges: false };
|
||||
}
|
||||
|
||||
const typedSteps = steps as WorkflowStep[];
|
||||
let hasChanges = false;
|
||||
|
||||
const migratedSteps = typedSteps.map((step) => {
|
||||
if (step.type !== WorkflowActionType.CODE) {
|
||||
return step;
|
||||
}
|
||||
|
||||
const input = step.settings.input as
|
||||
| LegacyCodeStepInput
|
||||
| MigratedCodeStepInput;
|
||||
|
||||
if (!needsCodeStepMigration(input)) {
|
||||
return step;
|
||||
}
|
||||
|
||||
hasChanges = true;
|
||||
const version = input.serverlessFunctionVersion ?? 'draft';
|
||||
const newLogicFunctionId = mapping(input.serverlessFunctionId, version);
|
||||
|
||||
return {
|
||||
...step,
|
||||
settings: {
|
||||
...step.settings,
|
||||
input: {
|
||||
logicFunctionId: newLogicFunctionId,
|
||||
logicFunctionInput: input.serverlessFunctionInput ?? {},
|
||||
} as MigratedCodeStepInput,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
return { migratedSteps, hasChanges };
|
||||
};
|
||||
-3
@@ -17,7 +17,6 @@ import { MakeWebhookUniversalIdentifierAndApplicationIdNotNullableMigrationComma
|
||||
import { MigrateAttachmentToMorphRelationsCommand } from 'src/database/commands/upgrade-version-command/1-17/1-17-migrate-attachment-to-morph-relations.command';
|
||||
import { MigrateNoteTargetToMorphRelationsCommand } from 'src/database/commands/upgrade-version-command/1-17/1-17-migrate-note-target-to-morph-relations.command';
|
||||
import { MigrateTaskTargetToMorphRelationsCommand } from 'src/database/commands/upgrade-version-command/1-17/1-17-migrate-task-target-to-morph-relations.command';
|
||||
import { MigrateWorkflowCodeStepsCommand } from 'src/database/commands/upgrade-version-command/1-17/1-17-migrate-workflow-code-steps.command';
|
||||
import { BackfillFileSizeAndMimeTypeCommand } from 'src/database/commands/upgrade-version-command/1-18/1-18-backfill-file-size-and-mime-type.command';
|
||||
import { BackfillMessageChannelThrottleRetryAfterCommand } from 'src/database/commands/upgrade-version-command/1-18/1-18-backfill-message-channel-throttle-retry-after.command';
|
||||
import { BackfillStandardViewsAndFieldMetadataCommand } from 'src/database/commands/upgrade-version-command/1-18/1-18-backfill-standard-views-and-field-metadata.command';
|
||||
@@ -57,7 +56,6 @@ export class UpgradeCommand extends UpgradeCommandRunner {
|
||||
protected readonly migrateTaskTargetToMorphRelationsCommand: MigrateTaskTargetToMorphRelationsCommand,
|
||||
protected readonly identifyWebhookMetadataCommand: IdentifyWebhookMetadataCommand,
|
||||
protected readonly makeWebhookUniversalIdentifierAndApplicationIdNotNullableMigrationCommand: MakeWebhookUniversalIdentifierAndApplicationIdNotNullableMigrationCommand,
|
||||
protected readonly migrateWorkflowCodeStepsCommand: MigrateWorkflowCodeStepsCommand,
|
||||
protected readonly fixMorphRelationFieldNamesCommand: FixMorphRelationFieldNamesCommand,
|
||||
|
||||
// 1.18 Commands
|
||||
@@ -94,7 +92,6 @@ export class UpgradeCommand extends UpgradeCommandRunner {
|
||||
this
|
||||
.makeWebhookUniversalIdentifierAndApplicationIdNotNullableMigrationCommand,
|
||||
this.deleteFileRecordsAndUpdateTableCommand,
|
||||
this.migrateWorkflowCodeStepsCommand,
|
||||
this.backfillApplicationPackageFilesCommand,
|
||||
this.fixMorphRelationFieldNamesCommand,
|
||||
];
|
||||
|
||||
-103
@@ -1,103 +0,0 @@
|
||||
import { Field, InputType } from '@nestjs/graphql';
|
||||
|
||||
import {
|
||||
IsBoolean,
|
||||
IsNotEmpty,
|
||||
IsNumber,
|
||||
IsObject,
|
||||
IsOptional,
|
||||
IsString,
|
||||
IsUUID,
|
||||
Matches,
|
||||
Max,
|
||||
Min,
|
||||
} from 'class-validator';
|
||||
import graphqlTypeJson from 'graphql-type-json';
|
||||
import {
|
||||
CronTriggerSettings,
|
||||
DatabaseEventTriggerSettings,
|
||||
HttpRouteTriggerSettings,
|
||||
} from 'twenty-shared/application';
|
||||
|
||||
import type { InputJsonSchema } from 'twenty-shared/logic-function';
|
||||
|
||||
import type { JsonbProperty } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/jsonb-property.type';
|
||||
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
|
||||
|
||||
@InputType()
|
||||
export class CreateLogicFunction {
|
||||
@IsUUID()
|
||||
@IsOptional()
|
||||
@Field(() => UUIDScalarType, { nullable: true })
|
||||
id?: string;
|
||||
|
||||
@IsUUID()
|
||||
@IsOptional()
|
||||
@Field(() => UUIDScalarType, { nullable: true })
|
||||
universalIdentifier?: string;
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@Field()
|
||||
name: string;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@Field({ nullable: true })
|
||||
description?: string;
|
||||
|
||||
@IsNumber()
|
||||
@Field({ nullable: true })
|
||||
@Min(1)
|
||||
@Max(900)
|
||||
@IsOptional()
|
||||
timeoutSeconds?: number;
|
||||
|
||||
@Field(() => graphqlTypeJson, { nullable: false })
|
||||
@IsObject()
|
||||
toolInputSchema: InputJsonSchema;
|
||||
|
||||
@IsBoolean()
|
||||
@Field({ nullable: true })
|
||||
@IsOptional()
|
||||
isTool?: boolean;
|
||||
|
||||
@IsBoolean()
|
||||
@Field({ nullable: false })
|
||||
isBuildUpToDate: boolean;
|
||||
|
||||
@IsString()
|
||||
@Field({ nullable: true })
|
||||
@IsOptional()
|
||||
checksum?: string;
|
||||
|
||||
@IsString()
|
||||
@Matches(/^[a-zA-Z_$][a-zA-Z0-9_$]*$/, {
|
||||
message: 'handlerName must be a valid JavaScript identifier',
|
||||
})
|
||||
@Field({ nullable: false })
|
||||
handlerName: string;
|
||||
|
||||
@IsString()
|
||||
@Field({ nullable: false })
|
||||
sourceHandlerPath: string;
|
||||
|
||||
@IsString()
|
||||
@Field({ nullable: false })
|
||||
builtHandlerPath: string;
|
||||
|
||||
@IsObject()
|
||||
@Field(() => graphqlTypeJson, { nullable: true })
|
||||
@IsOptional()
|
||||
cronTriggerSettings?: JsonbProperty<CronTriggerSettings>;
|
||||
|
||||
@IsObject()
|
||||
@Field(() => graphqlTypeJson, { nullable: true })
|
||||
@IsOptional()
|
||||
databaseEventTriggerSettings?: JsonbProperty<DatabaseEventTriggerSettings>;
|
||||
|
||||
@IsObject()
|
||||
@Field(() => graphqlTypeJson, { nullable: true })
|
||||
@IsOptional()
|
||||
httpRouteTriggerSettings?: JsonbProperty<HttpRouteTriggerSettings>;
|
||||
}
|
||||
-10
@@ -71,16 +71,6 @@ class UpdateLogicFunctionFromSourceInputUpdates {
|
||||
@IsOptional()
|
||||
isTool?: boolean;
|
||||
|
||||
@IsBoolean()
|
||||
@Field({ nullable: true })
|
||||
@IsOptional()
|
||||
isBuildUpToDate?: boolean;
|
||||
|
||||
@IsString()
|
||||
@Field({ nullable: true })
|
||||
@IsOptional()
|
||||
checksum?: string;
|
||||
|
||||
@IsObject()
|
||||
@Field(() => graphqlTypeJson, { nullable: true })
|
||||
@IsOptional()
|
||||
|
||||
+3
-3
@@ -17,7 +17,7 @@ import { WorkspaceManyOrAllFlatEntityMapsCacheModule } from 'src/engine/metadata
|
||||
import { LogicFunctionLayerModule } from 'src/engine/metadata-modules/logic-function-layer/logic-function-layer.module';
|
||||
import { LogicFunctionEntity } from 'src/engine/metadata-modules/logic-function/logic-function.entity';
|
||||
import { LogicFunctionResolver } from 'src/engine/metadata-modules/logic-function/logic-function.resolver';
|
||||
import { LogicFunctionMetadataService } from 'src/engine/metadata-modules/logic-function/services/logic-function-metadata.service';
|
||||
import { LogicFunctionFromSourceHelperService } from 'src/engine/metadata-modules/logic-function/services/logic-function-from-source-helper.service';
|
||||
import { LogicFunctionFromSourceService } from 'src/engine/metadata-modules/logic-function/services/logic-function-from-source.service';
|
||||
import { WorkspaceFlatLogicFunctionMapCacheService } from 'src/engine/metadata-modules/logic-function/services/workspace-flat-logic-function-map-cache.service';
|
||||
import { PermissionsModule } from 'src/engine/metadata-modules/permissions/permissions.module';
|
||||
@@ -43,11 +43,11 @@ import { WorkspaceMigrationModule } from 'src/engine/workspace-manager/workspace
|
||||
SecretEncryptionModule,
|
||||
],
|
||||
providers: [
|
||||
LogicFunctionMetadataService,
|
||||
LogicFunctionFromSourceHelperService,
|
||||
LogicFunctionFromSourceService,
|
||||
LogicFunctionResolver,
|
||||
WorkspaceFlatLogicFunctionMapCacheService,
|
||||
],
|
||||
exports: [LogicFunctionMetadataService, LogicFunctionFromSourceService],
|
||||
exports: [LogicFunctionFromSourceService],
|
||||
})
|
||||
export class LogicFunctionModule {}
|
||||
|
||||
+70
-20
@@ -14,19 +14,22 @@ import { FeatureFlagGuard } from 'src/engine/guards/feature-flag.guard';
|
||||
import { NoPermissionGuard } from 'src/engine/guards/no-permission.guard';
|
||||
import { SettingsPermissionGuard } from 'src/engine/guards/settings-permission.guard';
|
||||
import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard';
|
||||
import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service';
|
||||
import { CreateLogicFunctionFromSourceInput } from 'src/engine/metadata-modules/logic-function/dtos/create-logic-function-from-source.input';
|
||||
import { ExecuteOneLogicFunctionInput } from 'src/engine/metadata-modules/logic-function/dtos/execute-logic-function.input';
|
||||
import { LogicFunctionExecutionResultDTO } from 'src/engine/metadata-modules/logic-function/dtos/logic-function-execution-result.dto';
|
||||
import { LogicFunctionIdInput } from 'src/engine/metadata-modules/logic-function/dtos/logic-function-id.input';
|
||||
import { LogicFunctionLogsDTO } from 'src/engine/metadata-modules/logic-function/dtos/logic-function-logs.dto';
|
||||
import { LogicFunctionLogsInput } from 'src/engine/metadata-modules/logic-function/dtos/logic-function-logs.input';
|
||||
import { LogicFunctionDTO } from 'src/engine/metadata-modules/logic-function/dtos/logic-function.dto';
|
||||
import { UpdateLogicFunctionFromSourceInput } from 'src/engine/metadata-modules/logic-function/dtos/update-logic-function-from-source.input';
|
||||
import { LogicFunctionFromSourceService } from 'src/engine/metadata-modules/logic-function/services/logic-function-from-source.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 { fromFlatLogicFunctionToLogicFunctionDto } from 'src/engine/metadata-modules/logic-function/utils/from-flat-logic-function-to-logic-function-dto.util';
|
||||
import { logicFunctionGraphQLApiExceptionHandler } from 'src/engine/metadata-modules/logic-function/utils/logic-function-graphql-api-exception-handler.utils';
|
||||
import { SubscriptionChannel } from 'src/engine/subscriptions/enums/subscription-channel.enum';
|
||||
import { SubscriptionService } from 'src/engine/subscriptions/subscription.service';
|
||||
import { CreateLogicFunctionFromSourceInput } from 'src/engine/metadata-modules/logic-function/dtos/create-logic-function-from-source.input';
|
||||
import { UpdateLogicFunctionFromSourceInput } from 'src/engine/metadata-modules/logic-function/dtos/update-logic-function-from-source.input';
|
||||
import { LogicFunctionMetadataService } from 'src/engine/metadata-modules/logic-function/services/logic-function-metadata.service';
|
||||
|
||||
@UseGuards(WorkspaceAuthGuard, FeatureFlagGuard, NoPermissionGuard)
|
||||
@MetadataResolver()
|
||||
@@ -35,7 +38,7 @@ import { LogicFunctionMetadataService } from 'src/engine/metadata-modules/logic-
|
||||
export class LogicFunctionResolver {
|
||||
constructor(
|
||||
private readonly logicFunctionFromSourceService: LogicFunctionFromSourceService,
|
||||
private readonly logicFunctionMetadataService: LogicFunctionMetadataService,
|
||||
private readonly flatEntityMapsCacheService: WorkspaceManyOrAllFlatEntityMapsCacheService,
|
||||
private readonly subscriptionService: SubscriptionService,
|
||||
) {}
|
||||
|
||||
@@ -45,10 +48,20 @@ export class LogicFunctionResolver {
|
||||
@AuthWorkspace() { id: workspaceId }: WorkspaceEntity,
|
||||
): Promise<LogicFunctionDTO> {
|
||||
try {
|
||||
return await this.logicFunctionMetadataService.findOne({
|
||||
const { flatLogicFunctionMaps } =
|
||||
await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatMapsKeys: ['flatLogicFunctionMaps'],
|
||||
},
|
||||
);
|
||||
|
||||
const flatLogicFunction = findFlatLogicFunctionOrThrow({
|
||||
id,
|
||||
workspaceId,
|
||||
flatLogicFunctionMaps,
|
||||
});
|
||||
|
||||
return fromFlatLogicFunctionToLogicFunctionDto({ flatLogicFunction });
|
||||
} catch (error) {
|
||||
return logicFunctionGraphQLApiExceptionHandler(error);
|
||||
}
|
||||
@@ -59,9 +72,23 @@ export class LogicFunctionResolver {
|
||||
@AuthWorkspace() { id: workspaceId }: WorkspaceEntity,
|
||||
): Promise<LogicFunctionDTO[]> {
|
||||
try {
|
||||
return await this.logicFunctionMetadataService.findMany({
|
||||
workspaceId,
|
||||
});
|
||||
const { flatLogicFunctionMaps } =
|
||||
await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatMapsKeys: ['flatLogicFunctionMaps'],
|
||||
},
|
||||
);
|
||||
|
||||
return Object.values(flatLogicFunctionMaps.byUniversalIdentifier)
|
||||
.filter(
|
||||
(flatLogicFunction): flatLogicFunction is FlatLogicFunction =>
|
||||
isDefined(flatLogicFunction) &&
|
||||
!isDefined(flatLogicFunction.deletedAt),
|
||||
)
|
||||
.map((flatLogicFunction) =>
|
||||
fromFlatLogicFunctionToLogicFunctionDto({ flatLogicFunction }),
|
||||
);
|
||||
} catch (error) {
|
||||
return logicFunctionGraphQLApiExceptionHandler(error);
|
||||
}
|
||||
@@ -74,10 +101,33 @@ export class LogicFunctionResolver {
|
||||
@AuthWorkspace() { id: workspaceId }: WorkspaceEntity,
|
||||
) {
|
||||
try {
|
||||
return await this.logicFunctionMetadataService.getAvailablePackages({
|
||||
logicFunctionId: id,
|
||||
workspaceId,
|
||||
});
|
||||
const { flatLogicFunctionMaps, flatApplicationMaps } =
|
||||
await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatMapsKeys: ['flatLogicFunctionMaps', 'flatApplicationMaps'],
|
||||
},
|
||||
);
|
||||
|
||||
const logicFunctionUniversalIdentifier =
|
||||
flatLogicFunctionMaps.universalIdentifierById[id];
|
||||
|
||||
if (!logicFunctionUniversalIdentifier) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const logicFunction =
|
||||
flatLogicFunctionMaps.byUniversalIdentifier[
|
||||
logicFunctionUniversalIdentifier
|
||||
];
|
||||
|
||||
if (!logicFunction) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const application = flatApplicationMaps.byId[logicFunction.applicationId];
|
||||
|
||||
return application?.availablePackages ?? {};
|
||||
} catch (error) {
|
||||
return logicFunctionGraphQLApiExceptionHandler(error);
|
||||
}
|
||||
@@ -90,7 +140,7 @@ export class LogicFunctionResolver {
|
||||
@AuthWorkspace() { id: workspaceId }: WorkspaceEntity,
|
||||
): Promise<LogicFunctionDTO> {
|
||||
try {
|
||||
return await this.logicFunctionFromSourceService.deleteOne({
|
||||
return await this.logicFunctionFromSourceService.deleteOneWithSource({
|
||||
id,
|
||||
workspaceId,
|
||||
});
|
||||
@@ -106,7 +156,7 @@ export class LogicFunctionResolver {
|
||||
@AuthWorkspace() { id: workspaceId }: WorkspaceEntity,
|
||||
): Promise<LogicFunctionDTO> {
|
||||
try {
|
||||
return await this.logicFunctionFromSourceService.createOne({
|
||||
return await this.logicFunctionFromSourceService.createOneFromSource({
|
||||
input,
|
||||
workspaceId,
|
||||
});
|
||||
@@ -122,7 +172,7 @@ export class LogicFunctionResolver {
|
||||
@AuthWorkspace() { id: workspaceId }: WorkspaceEntity,
|
||||
): Promise<LogicFunctionExecutionResultDTO> {
|
||||
try {
|
||||
return await this.logicFunctionFromSourceService.executeOne({
|
||||
return await this.logicFunctionFromSourceService.executeOneFromSource({
|
||||
id,
|
||||
payload,
|
||||
workspaceId,
|
||||
@@ -151,13 +201,13 @@ export class LogicFunctionResolver {
|
||||
@Mutation(() => Boolean)
|
||||
@UseGuards(SettingsPermissionGuard(PermissionFlagType.WORKFLOWS))
|
||||
async updateOneLogicFunction(
|
||||
@Args('input') { id, update }: UpdateLogicFunctionFromSourceInput,
|
||||
@Args('input')
|
||||
updateLogicFunctionFromSourceInput: UpdateLogicFunctionFromSourceInput,
|
||||
@AuthWorkspace() { id: workspaceId }: WorkspaceEntity,
|
||||
): Promise<boolean> {
|
||||
try {
|
||||
await this.logicFunctionFromSourceService.updateOne({
|
||||
id,
|
||||
update,
|
||||
await this.logicFunctionFromSourceService.updateOneFromSource({
|
||||
updateLogicFunctionFromSourceInput,
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
|
||||
+153
@@ -0,0 +1,153 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { join } from 'path';
|
||||
|
||||
import { ApplicationService } from 'src/engine/core-modules/application/services/application.service';
|
||||
import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service';
|
||||
import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
|
||||
import {
|
||||
DEFAULT_BUILT_HANDLER_PATH,
|
||||
DEFAULT_SOURCE_HANDLER_PATH,
|
||||
} from 'src/engine/metadata-modules/logic-function/constants/handler.contant';
|
||||
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 { getLogicFunctionSubfolderForFromSource } from 'src/engine/metadata-modules/logic-function/utils/get-logic-function-subfolder-for-from-source';
|
||||
import { type UniversalFlatLogicFunction } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-logic-function.type';
|
||||
import { WorkspaceMigrationBuilderException } from 'src/engine/workspace-manager/workspace-migration/exceptions/workspace-migration-builder-exception';
|
||||
import { WorkspaceMigrationValidateBuildAndRunService } from 'src/engine/workspace-manager/workspace-migration/services/workspace-migration-validate-build-and-run-service';
|
||||
|
||||
@Injectable()
|
||||
export class LogicFunctionFromSourceHelperService {
|
||||
constructor(
|
||||
private readonly applicationService: ApplicationService,
|
||||
private readonly flatEntityMapsCacheService: WorkspaceManyOrAllFlatEntityMapsCacheService,
|
||||
private readonly workspaceMigrationValidateBuildAndRunService: WorkspaceMigrationValidateBuildAndRunService,
|
||||
) {}
|
||||
|
||||
async findLogicFunctionAndApplicationOrThrow({
|
||||
id,
|
||||
workspaceId,
|
||||
}: {
|
||||
id: string;
|
||||
workspaceId: string;
|
||||
}) {
|
||||
const [{ flatLogicFunctionMaps }, { workspaceCustomFlatApplication }] =
|
||||
await Promise.all([
|
||||
this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps({
|
||||
workspaceId,
|
||||
flatMapsKeys: ['flatLogicFunctionMaps'],
|
||||
}),
|
||||
this.applicationService.findWorkspaceTwentyStandardAndCustomApplicationOrThrow(
|
||||
{ workspaceId },
|
||||
),
|
||||
]);
|
||||
|
||||
const flatLogicFunction = findFlatLogicFunctionOrThrow({
|
||||
id,
|
||||
flatLogicFunctionMaps,
|
||||
});
|
||||
|
||||
return {
|
||||
flatLogicFunction,
|
||||
ownerFlatApplication: workspaceCustomFlatApplication,
|
||||
};
|
||||
}
|
||||
|
||||
buildHandlerPaths(logicFunctionId: string) {
|
||||
const logicFunctionSubfolder =
|
||||
getLogicFunctionSubfolderForFromSource(logicFunctionId);
|
||||
|
||||
return {
|
||||
sourceHandlerPath: join(
|
||||
logicFunctionSubfolder,
|
||||
DEFAULT_SOURCE_HANDLER_PATH,
|
||||
),
|
||||
builtHandlerPath: join(
|
||||
logicFunctionSubfolder,
|
||||
DEFAULT_BUILT_HANDLER_PATH,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
async createOneFromMetadata({
|
||||
universalFlatLogicFunctionToCreate,
|
||||
workspaceId,
|
||||
}: {
|
||||
universalFlatLogicFunctionToCreate: UniversalFlatLogicFunction & {
|
||||
id: string;
|
||||
};
|
||||
workspaceId: string;
|
||||
}): Promise<UniversalFlatLogicFunction & { id: string }> {
|
||||
const validateAndBuildResult =
|
||||
await this.workspaceMigrationValidateBuildAndRunService.validateBuildAndRunWorkspaceMigration(
|
||||
{
|
||||
allFlatEntityOperationByMetadataName: {
|
||||
logicFunction: {
|
||||
flatEntityToCreate: [universalFlatLogicFunctionToCreate],
|
||||
flatEntityToDelete: [],
|
||||
flatEntityToUpdate: [],
|
||||
},
|
||||
},
|
||||
workspaceId,
|
||||
isSystemBuild: false,
|
||||
applicationUniversalIdentifier:
|
||||
universalFlatLogicFunctionToCreate.applicationUniversalIdentifier,
|
||||
},
|
||||
);
|
||||
|
||||
if (validateAndBuildResult.status === 'fail') {
|
||||
throw new WorkspaceMigrationBuilderException(
|
||||
validateAndBuildResult,
|
||||
'Multiple validation errors occurred while creating logic function',
|
||||
);
|
||||
}
|
||||
|
||||
return universalFlatLogicFunctionToCreate;
|
||||
}
|
||||
|
||||
async updateOneFromMetadata({
|
||||
flatLogicFunctionToUpdate,
|
||||
workspaceId,
|
||||
applicationUniversalIdentifier,
|
||||
}: {
|
||||
flatLogicFunctionToUpdate: FlatLogicFunction;
|
||||
workspaceId: string;
|
||||
applicationUniversalIdentifier: string;
|
||||
}): Promise<FlatLogicFunction> {
|
||||
const validateAndBuildResult =
|
||||
await this.workspaceMigrationValidateBuildAndRunService.validateBuildAndRunWorkspaceMigration(
|
||||
{
|
||||
allFlatEntityOperationByMetadataName: {
|
||||
logicFunction: {
|
||||
flatEntityToCreate: [],
|
||||
flatEntityToDelete: [],
|
||||
flatEntityToUpdate: [flatLogicFunctionToUpdate],
|
||||
},
|
||||
},
|
||||
workspaceId,
|
||||
isSystemBuild: false,
|
||||
applicationUniversalIdentifier,
|
||||
},
|
||||
);
|
||||
|
||||
if (validateAndBuildResult.status === 'fail') {
|
||||
throw new WorkspaceMigrationBuilderException(
|
||||
validateAndBuildResult,
|
||||
'Multiple validation errors occurred while updating logic function',
|
||||
);
|
||||
}
|
||||
|
||||
const { flatLogicFunctionMaps: recomputedFlatLogicFunctionMaps } =
|
||||
await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatMapsKeys: ['flatLogicFunctionMaps'],
|
||||
},
|
||||
);
|
||||
|
||||
return findFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityId: flatLogicFunctionToUpdate.id,
|
||||
flatEntityMaps: recomputedFlatLogicFunctionMaps,
|
||||
});
|
||||
}
|
||||
}
|
||||
+249
-128
@@ -1,77 +1,50 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { join } from 'path';
|
||||
|
||||
import { v4 } from 'uuid';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { SEED_LOGIC_FUNCTION_INPUT_SCHEMA } from 'twenty-shared/logic-function';
|
||||
|
||||
import { ApplicationService } from 'src/engine/core-modules/application/services/application.service';
|
||||
import { type FlatApplication } from 'src/engine/core-modules/application/types/flat-application.type';
|
||||
import { LogicFunctionExecutorService } from 'src/engine/core-modules/logic-function/logic-function-executor/logic-function-executor.service';
|
||||
import { LogicFunctionResourceService } from 'src/engine/core-modules/logic-function/logic-function-resource/logic-function-resource.service';
|
||||
import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service';
|
||||
import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
|
||||
import { CreateLogicFunctionFromSourceInput } from 'src/engine/metadata-modules/logic-function/dtos/create-logic-function-from-source.input';
|
||||
import { LogicFunctionExecutionResultDTO } from 'src/engine/metadata-modules/logic-function/dtos/logic-function-execution-result.dto';
|
||||
import { LogicFunctionDTO } from 'src/engine/metadata-modules/logic-function/dtos/logic-function.dto';
|
||||
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 { fromFlatLogicFunctionToLogicFunctionDto } from 'src/engine/metadata-modules/logic-function/utils/from-flat-logic-function-to-logic-function-dto.util';
|
||||
import { CreateLogicFunctionFromSourceInput } from 'src/engine/metadata-modules/logic-function/dtos/create-logic-function-from-source.input';
|
||||
import { UpdateLogicFunctionFromSourceInput } from 'src/engine/metadata-modules/logic-function/dtos/update-logic-function-from-source.input';
|
||||
import { getLogicFunctionSubfolderForFromSource } from 'src/engine/metadata-modules/logic-function/utils/get-logic-function-subfolder-for-from-source';
|
||||
import {
|
||||
DEFAULT_BUILT_HANDLER_PATH,
|
||||
DEFAULT_SOURCE_HANDLER_PATH,
|
||||
} from 'src/engine/metadata-modules/logic-function/constants/handler.contant';
|
||||
LogicFunctionException,
|
||||
LogicFunctionExceptionCode,
|
||||
} from 'src/engine/metadata-modules/logic-function/logic-function.exception';
|
||||
import { LogicFunctionFromSourceHelperService } from 'src/engine/metadata-modules/logic-function/services/logic-function-from-source-helper.service';
|
||||
import { type UpdateLogicFunctionFromSourceInput } from 'src/engine/metadata-modules/logic-function/dtos/update-logic-function-from-source.input';
|
||||
import { buildUniversalFlatLogicFunctionToCreate } from 'src/engine/metadata-modules/logic-function/utils/build-universal-flat-logic-function-to-create.util';
|
||||
import { fromCreateLogicFunctionFromSourceInputToUniversalFlatLogicFunctionToCreate } from 'src/engine/metadata-modules/logic-function/utils/from-create-logic-function-from-source-input-to-universal-flat-logic-function-to-create.util';
|
||||
import { fromFlatLogicFunctionToLogicFunctionDto } from 'src/engine/metadata-modules/logic-function/utils/from-flat-logic-function-to-logic-function-dto.util';
|
||||
import { fromUpdateLogicFunctionFromSourceInputToFlatLogicFunctionToUpdate } from 'src/engine/metadata-modules/logic-function/utils/from-update-logic-function-from-source-input-to-flat-logic-function-to-update.util';
|
||||
import { WorkspaceMigrationBuilderException } from 'src/engine/workspace-manager/workspace-migration/exceptions/workspace-migration-builder-exception';
|
||||
import { WorkspaceMigrationValidateBuildAndRunService } from 'src/engine/workspace-manager/workspace-migration/services/workspace-migration-validate-build-and-run-service';
|
||||
|
||||
@Injectable()
|
||||
export class LogicFunctionFromSourceService {
|
||||
constructor(
|
||||
private readonly logicFunctionExecutorService: LogicFunctionExecutorService,
|
||||
private readonly logicFunctionMetadataService: LogicFunctionMetadataService,
|
||||
private readonly logicFunctionResourceService: LogicFunctionResourceService,
|
||||
private readonly applicationService: ApplicationService,
|
||||
private readonly helperService: LogicFunctionFromSourceHelperService,
|
||||
private readonly flatEntityMapsCacheService: WorkspaceManyOrAllFlatEntityMapsCacheService,
|
||||
private readonly workspaceMigrationValidateBuildAndRunService: WorkspaceMigrationValidateBuildAndRunService,
|
||||
) {}
|
||||
|
||||
async deleteOne({
|
||||
id,
|
||||
workspaceId,
|
||||
}: {
|
||||
id: string;
|
||||
workspaceId: string;
|
||||
}): Promise<LogicFunctionDTO> {
|
||||
const flatLogicFunction =
|
||||
await this.logicFunctionMetadataService.destroyOne({
|
||||
id,
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
return fromFlatLogicFunctionToLogicFunctionDto({ flatLogicFunction });
|
||||
}
|
||||
|
||||
private getHandlerPaths(logicFunctionId: string) {
|
||||
const logicFunctionSubfolder =
|
||||
getLogicFunctionSubfolderForFromSource(logicFunctionId);
|
||||
|
||||
return {
|
||||
sourceHandlerPath: join(
|
||||
logicFunctionSubfolder,
|
||||
DEFAULT_SOURCE_HANDLER_PATH,
|
||||
),
|
||||
builtHandlerPath: join(
|
||||
logicFunctionSubfolder,
|
||||
DEFAULT_BUILT_HANDLER_PATH,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
async createOne({
|
||||
async createOneFromSource({
|
||||
input,
|
||||
workspaceId,
|
||||
}: {
|
||||
input: CreateLogicFunctionFromSourceInput;
|
||||
workspaceId: string;
|
||||
}): Promise<LogicFunctionDTO> {
|
||||
const { workspaceCustomFlatApplication } =
|
||||
const { workspaceCustomFlatApplication: ownerFlatApplication } =
|
||||
await this.applicationService.findWorkspaceTwentyStandardAndCustomApplicationOrThrow(
|
||||
{ workspaceId },
|
||||
);
|
||||
@@ -79,33 +52,44 @@ export class LogicFunctionFromSourceService {
|
||||
const logicFunctionId = input.id ?? v4();
|
||||
|
||||
const { sourceHandlerPath, builtHandlerPath } =
|
||||
this.getHandlerPaths(logicFunctionId);
|
||||
this.helperService.buildHandlerPaths(logicFunctionId);
|
||||
|
||||
if (input.source) {
|
||||
await this.logicFunctionResourceService.uploadSourceFile({
|
||||
sourceHandlerPath,
|
||||
sourceHandlerCode: input.source.sourceHandlerCode,
|
||||
applicationUniversalIdentifier:
|
||||
workspaceCustomFlatApplication.universalIdentifier,
|
||||
ownerFlatApplication.universalIdentifier,
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
const flatLogicFunction =
|
||||
await this.logicFunctionMetadataService.createOne({
|
||||
input: {
|
||||
...input,
|
||||
handlerName: input.source.handlerName,
|
||||
toolInputSchema: input.source.toolInputSchema,
|
||||
const universalFlatLogicFunctionToCreate =
|
||||
fromCreateLogicFunctionFromSourceInputToUniversalFlatLogicFunctionToCreate(
|
||||
{
|
||||
createLogicFunctionFromSourceInput: {
|
||||
...input,
|
||||
id: logicFunctionId,
|
||||
},
|
||||
sourceHandlerPath,
|
||||
builtHandlerPath,
|
||||
id: logicFunctionId,
|
||||
handlerName: input.source.handlerName,
|
||||
checksum: null,
|
||||
toolInputSchema: input.source.toolInputSchema,
|
||||
isBuildUpToDate: false,
|
||||
applicationUniversalIdentifier:
|
||||
ownerFlatApplication.universalIdentifier,
|
||||
},
|
||||
workspaceId,
|
||||
ownerFlatApplication: workspaceCustomFlatApplication,
|
||||
});
|
||||
);
|
||||
|
||||
return fromFlatLogicFunctionToLogicFunctionDto({ flatLogicFunction });
|
||||
await this.helperService.createOneFromMetadata({
|
||||
universalFlatLogicFunctionToCreate,
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
return this.findFlatLogicFunctionByIdAndConvertToDto({
|
||||
id: universalFlatLogicFunctionToCreate.id,
|
||||
workspaceId,
|
||||
});
|
||||
}
|
||||
|
||||
const { handlerName, checksum } =
|
||||
@@ -114,63 +98,194 @@ export class LogicFunctionFromSourceService {
|
||||
builtHandlerPath,
|
||||
workspaceId,
|
||||
applicationUniversalIdentifier:
|
||||
workspaceCustomFlatApplication.universalIdentifier,
|
||||
ownerFlatApplication.universalIdentifier,
|
||||
});
|
||||
|
||||
const flatLogicFunction = await this.logicFunctionMetadataService.createOne(
|
||||
{
|
||||
input: {
|
||||
...input,
|
||||
id: logicFunctionId,
|
||||
const universalFlatLogicFunctionToCreate =
|
||||
fromCreateLogicFunctionFromSourceInputToUniversalFlatLogicFunctionToCreate(
|
||||
{
|
||||
createLogicFunctionFromSourceInput: { ...input, id: logicFunctionId },
|
||||
sourceHandlerPath,
|
||||
builtHandlerPath,
|
||||
handlerName,
|
||||
checksum,
|
||||
toolInputSchema: SEED_LOGIC_FUNCTION_INPUT_SCHEMA,
|
||||
isBuildUpToDate: true,
|
||||
applicationUniversalIdentifier:
|
||||
ownerFlatApplication.universalIdentifier,
|
||||
},
|
||||
workspaceId,
|
||||
ownerFlatApplication: workspaceCustomFlatApplication,
|
||||
},
|
||||
);
|
||||
);
|
||||
|
||||
return fromFlatLogicFunctionToLogicFunctionDto({ flatLogicFunction });
|
||||
await this.helperService.createOneFromMetadata({
|
||||
universalFlatLogicFunctionToCreate,
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
return this.findFlatLogicFunctionByIdAndConvertToDto({
|
||||
id: universalFlatLogicFunctionToCreate.id,
|
||||
workspaceId,
|
||||
});
|
||||
}
|
||||
|
||||
async updateOne({
|
||||
id,
|
||||
update,
|
||||
async duplicateOneWithSource({
|
||||
existingLogicFunctionId,
|
||||
workspaceId,
|
||||
}: {
|
||||
id: string;
|
||||
update: UpdateLogicFunctionFromSourceInput['update'];
|
||||
existingLogicFunctionId: string;
|
||||
workspaceId: string;
|
||||
}): Promise<void> {
|
||||
const { applicationUniversalIdentifier, flatLogicFunction } =
|
||||
await this.getLogicFunctionContext({ id, workspaceId });
|
||||
|
||||
let formattedUpdate: UpdateLogicFunctionFromSourceInput['update'] = {
|
||||
...update,
|
||||
};
|
||||
|
||||
if (update.sourceHandlerCode) {
|
||||
await this.logicFunctionResourceService.uploadSourceFile({
|
||||
sourceHandlerPath: flatLogicFunction.sourceHandlerPath,
|
||||
sourceHandlerCode: update.sourceHandlerCode,
|
||||
applicationUniversalIdentifier,
|
||||
}): Promise<{ id: string }> {
|
||||
const { flatLogicFunction: existingLogicFunction, ownerFlatApplication } =
|
||||
await this.helperService.findLogicFunctionAndApplicationOrThrow({
|
||||
id: existingLogicFunctionId,
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
formattedUpdate = {
|
||||
...formattedUpdate,
|
||||
isBuildUpToDate: false,
|
||||
};
|
||||
const newId = 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,
|
||||
toSourceHandlerPath,
|
||||
fromBuiltHandlerPath: builtHandlerPath,
|
||||
toBuiltHandlerPath,
|
||||
workspaceId,
|
||||
applicationUniversalIdentifier: ownerFlatApplication.universalIdentifier,
|
||||
});
|
||||
|
||||
const universalFlatLogicFunctionToCreate =
|
||||
buildUniversalFlatLogicFunctionToCreate({
|
||||
id: newId,
|
||||
name: existingLogicFunction.name,
|
||||
description: existingLogicFunction.description,
|
||||
timeoutSeconds: existingLogicFunction.timeoutSeconds,
|
||||
toolInputSchema: existingLogicFunction.toolInputSchema,
|
||||
isTool: existingLogicFunction.isTool,
|
||||
isBuildUpToDate: existingLogicFunction.isBuildUpToDate,
|
||||
checksum: existingLogicFunction.checksum,
|
||||
handlerName: existingLogicFunction.handlerName,
|
||||
sourceHandlerPath: toSourceHandlerPath,
|
||||
builtHandlerPath: toBuiltHandlerPath,
|
||||
cronTriggerSettings: existingLogicFunction.cronTriggerSettings,
|
||||
databaseEventTriggerSettings:
|
||||
existingLogicFunction.databaseEventTriggerSettings,
|
||||
httpRouteTriggerSettings:
|
||||
existingLogicFunction.httpRouteTriggerSettings,
|
||||
applicationUniversalIdentifier:
|
||||
ownerFlatApplication.universalIdentifier,
|
||||
});
|
||||
|
||||
const created = await this.helperService.createOneFromMetadata({
|
||||
universalFlatLogicFunctionToCreate,
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
if (!isDefined(created)) {
|
||||
throw new LogicFunctionException(
|
||||
'Failed to duplicate logic function',
|
||||
LogicFunctionExceptionCode.LOGIC_FUNCTION_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
await this.logicFunctionMetadataService.updateOne({
|
||||
id,
|
||||
update: formattedUpdate,
|
||||
return { id: created.id };
|
||||
}
|
||||
|
||||
async updateOneFromSource({
|
||||
updateLogicFunctionFromSourceInput,
|
||||
workspaceId,
|
||||
}: {
|
||||
updateLogicFunctionFromSourceInput: UpdateLogicFunctionFromSourceInput;
|
||||
workspaceId: string;
|
||||
}): Promise<void> {
|
||||
const { flatLogicFunction, ownerFlatApplication } =
|
||||
await this.helperService.findLogicFunctionAndApplicationOrThrow({
|
||||
id: updateLogicFunctionFromSourceInput.id,
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
const sourceHandlerCode =
|
||||
updateLogicFunctionFromSourceInput.update.sourceHandlerCode;
|
||||
|
||||
if (sourceHandlerCode) {
|
||||
await this.logicFunctionResourceService.uploadSourceFile({
|
||||
sourceHandlerPath: flatLogicFunction.sourceHandlerPath,
|
||||
sourceHandlerCode,
|
||||
applicationUniversalIdentifier:
|
||||
ownerFlatApplication.universalIdentifier,
|
||||
workspaceId,
|
||||
});
|
||||
}
|
||||
|
||||
const flatLogicFunctionToUpdate =
|
||||
fromUpdateLogicFunctionFromSourceInputToFlatLogicFunctionToUpdate({
|
||||
updateLogicFunctionFromSourceInput,
|
||||
existingFlatLogicFunction: flatLogicFunction,
|
||||
});
|
||||
|
||||
await this.helperService.updateOneFromMetadata({
|
||||
flatLogicFunctionToUpdate,
|
||||
workspaceId,
|
||||
applicationUniversalIdentifier: ownerFlatApplication.universalIdentifier,
|
||||
});
|
||||
}
|
||||
|
||||
async deleteOneWithSource({
|
||||
id,
|
||||
workspaceId,
|
||||
isSystemBuild = false,
|
||||
ownerFlatApplication,
|
||||
}: {
|
||||
id: string;
|
||||
workspaceId: string;
|
||||
isSystemBuild?: boolean;
|
||||
ownerFlatApplication?: FlatApplication;
|
||||
}): Promise<LogicFunctionDTO> {
|
||||
const {
|
||||
flatLogicFunction: existingFlatLogicFunction,
|
||||
ownerFlatApplication: resolvedOwnerFlatApplication,
|
||||
} = await this.helperService.findLogicFunctionAndApplicationOrThrow({
|
||||
id,
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
const effectiveOwnerFlatApplication =
|
||||
ownerFlatApplication ?? resolvedOwnerFlatApplication;
|
||||
|
||||
const validateAndBuildResult =
|
||||
await this.workspaceMigrationValidateBuildAndRunService.validateBuildAndRunWorkspaceMigration(
|
||||
{
|
||||
allFlatEntityOperationByMetadataName: {
|
||||
logicFunction: {
|
||||
flatEntityToCreate: [],
|
||||
flatEntityToDelete: [existingFlatLogicFunction],
|
||||
flatEntityToUpdate: [],
|
||||
},
|
||||
},
|
||||
workspaceId,
|
||||
isSystemBuild,
|
||||
applicationUniversalIdentifier:
|
||||
effectiveOwnerFlatApplication.universalIdentifier,
|
||||
},
|
||||
);
|
||||
|
||||
if (validateAndBuildResult.status === 'fail') {
|
||||
throw new WorkspaceMigrationBuilderException(
|
||||
validateAndBuildResult,
|
||||
'Multiple validation errors occurred while destroying logic function',
|
||||
);
|
||||
}
|
||||
|
||||
return fromFlatLogicFunctionToLogicFunctionDto({
|
||||
flatLogicFunction: existingFlatLogicFunction,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -181,25 +296,34 @@ export class LogicFunctionFromSourceService {
|
||||
id: string;
|
||||
workspaceId: string;
|
||||
}): Promise<void> {
|
||||
const { flatLogicFunction, applicationUniversalIdentifier } =
|
||||
await this.getLogicFunctionContext({ id, workspaceId });
|
||||
const { flatLogicFunction, ownerFlatApplication } =
|
||||
await this.helperService.findLogicFunctionAndApplicationOrThrow({
|
||||
id,
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
const { checksum } =
|
||||
await this.logicFunctionResourceService.buildFromSourceFile({
|
||||
workspaceId,
|
||||
applicationUniversalIdentifier,
|
||||
applicationUniversalIdentifier:
|
||||
ownerFlatApplication.universalIdentifier,
|
||||
sourceHandlerPath: flatLogicFunction.sourceHandlerPath,
|
||||
builtHandlerPath: flatLogicFunction.builtHandlerPath,
|
||||
});
|
||||
|
||||
await this.logicFunctionMetadataService.updateOne({
|
||||
id,
|
||||
update: { checksum, isBuildUpToDate: true },
|
||||
await this.helperService.updateOneFromMetadata({
|
||||
flatLogicFunctionToUpdate: {
|
||||
...flatLogicFunction,
|
||||
checksum,
|
||||
isBuildUpToDate: true,
|
||||
updatedAt: new Date().toISOString(),
|
||||
},
|
||||
workspaceId,
|
||||
applicationUniversalIdentifier: ownerFlatApplication.universalIdentifier,
|
||||
});
|
||||
}
|
||||
|
||||
async executeOne({
|
||||
async executeOneFromSource({
|
||||
id,
|
||||
payload,
|
||||
workspaceId,
|
||||
@@ -208,10 +332,11 @@ export class LogicFunctionFromSourceService {
|
||||
payload: object;
|
||||
workspaceId: string;
|
||||
}): Promise<LogicFunctionExecutionResultDTO> {
|
||||
const { flatLogicFunction } = await this.getLogicFunctionContext({
|
||||
id,
|
||||
workspaceId,
|
||||
});
|
||||
const { flatLogicFunction } =
|
||||
await this.helperService.findLogicFunctionAndApplicationOrThrow({
|
||||
id,
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
if (!flatLogicFunction.isBuildUpToDate) {
|
||||
await this.buildOneFromSource({ workspaceId, id });
|
||||
@@ -247,43 +372,39 @@ export class LogicFunctionFromSourceService {
|
||||
id: string;
|
||||
workspaceId: string;
|
||||
}): Promise<string | null> {
|
||||
const { flatLogicFunction, applicationUniversalIdentifier } =
|
||||
await this.getLogicFunctionContext({ id, workspaceId });
|
||||
const { flatLogicFunction, ownerFlatApplication } =
|
||||
await this.helperService.findLogicFunctionAndApplicationOrThrow({
|
||||
id,
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
return this.logicFunctionResourceService.getSourceFile({
|
||||
workspaceId,
|
||||
applicationUniversalIdentifier,
|
||||
applicationUniversalIdentifier: ownerFlatApplication.universalIdentifier,
|
||||
sourceHandlerPath: flatLogicFunction.sourceHandlerPath,
|
||||
});
|
||||
}
|
||||
|
||||
private async getLogicFunctionContext({
|
||||
private async findFlatLogicFunctionByIdAndConvertToDto({
|
||||
id,
|
||||
workspaceId,
|
||||
}: {
|
||||
id: string;
|
||||
workspaceId: string;
|
||||
}) {
|
||||
const [{ flatLogicFunctionMaps }, { workspaceCustomFlatApplication }] =
|
||||
await Promise.all([
|
||||
this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps({
|
||||
}): Promise<LogicFunctionDTO> {
|
||||
const { flatLogicFunctionMaps } =
|
||||
await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatMapsKeys: ['flatLogicFunctionMaps'],
|
||||
}),
|
||||
this.applicationService.findWorkspaceTwentyStandardAndCustomApplicationOrThrow(
|
||||
{ workspaceId },
|
||||
),
|
||||
]);
|
||||
},
|
||||
);
|
||||
|
||||
const flatLogicFunction = findFlatLogicFunctionOrThrow({
|
||||
id,
|
||||
flatLogicFunctionMaps,
|
||||
return fromFlatLogicFunctionToLogicFunctionDto({
|
||||
flatLogicFunction: findFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityId: id,
|
||||
flatEntityMaps: flatLogicFunctionMaps,
|
||||
}),
|
||||
});
|
||||
|
||||
return {
|
||||
flatLogicFunction,
|
||||
applicationUniversalIdentifier:
|
||||
workspaceCustomFlatApplication.universalIdentifier,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
-315
@@ -1,315 +0,0 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { ApplicationService } from 'src/engine/core-modules/application/services/application.service';
|
||||
import { type FlatApplication } from 'src/engine/core-modules/application/types/flat-application.type';
|
||||
import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service';
|
||||
import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
|
||||
import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps.util';
|
||||
import {
|
||||
LogicFunctionException,
|
||||
LogicFunctionExceptionCode,
|
||||
} from 'src/engine/metadata-modules/logic-function/logic-function.exception';
|
||||
import { 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-from-source-input-to-flat-logic-function.util';
|
||||
import { fromUpdateLogicFunctionInputToFlatLogicFunctionToUpdateOrThrow } from 'src/engine/metadata-modules/logic-function/utils/from-update-logic-function-input-to-flat-logic-function-to-update-or-throw.util';
|
||||
import { WorkspaceMigrationBuilderException } from 'src/engine/workspace-manager/workspace-migration/exceptions/workspace-migration-builder-exception';
|
||||
import { WorkspaceMigrationValidateBuildAndRunService } from 'src/engine/workspace-manager/workspace-migration/services/workspace-migration-validate-build-and-run-service';
|
||||
import { LogicFunctionDTO } from 'src/engine/metadata-modules/logic-function/dtos/logic-function.dto';
|
||||
import { fromFlatLogicFunctionToLogicFunctionDto } from 'src/engine/metadata-modules/logic-function/utils/from-flat-logic-function-to-logic-function-dto.util';
|
||||
import { CreateLogicFunction } from 'src/engine/metadata-modules/logic-function/dtos/create-logic-function.input';
|
||||
import { UpdateLogicFunctionFromSourceInput } from 'src/engine/metadata-modules/logic-function/dtos/update-logic-function-from-source.input';
|
||||
|
||||
@Injectable()
|
||||
export class LogicFunctionMetadataService {
|
||||
constructor(
|
||||
private readonly flatEntityMapsCacheService: WorkspaceManyOrAllFlatEntityMapsCacheService,
|
||||
private readonly workspaceMigrationValidateBuildAndRunService: WorkspaceMigrationValidateBuildAndRunService,
|
||||
private readonly applicationService: ApplicationService,
|
||||
) {}
|
||||
|
||||
async findOne({
|
||||
id,
|
||||
workspaceId,
|
||||
}: {
|
||||
id: string;
|
||||
workspaceId: string;
|
||||
}): Promise<LogicFunctionDTO> {
|
||||
const { flatLogicFunctionMaps } =
|
||||
await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatMapsKeys: ['flatLogicFunctionMaps'],
|
||||
},
|
||||
);
|
||||
|
||||
const flatLogicFunction = findFlatLogicFunctionOrThrow({
|
||||
id,
|
||||
flatLogicFunctionMaps,
|
||||
});
|
||||
|
||||
return fromFlatLogicFunctionToLogicFunctionDto({ flatLogicFunction });
|
||||
}
|
||||
|
||||
async findMany({
|
||||
workspaceId,
|
||||
}: {
|
||||
workspaceId: string;
|
||||
}): Promise<LogicFunctionDTO[]> {
|
||||
const { flatLogicFunctionMaps } =
|
||||
await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatMapsKeys: ['flatLogicFunctionMaps'],
|
||||
},
|
||||
);
|
||||
|
||||
return Object.values(flatLogicFunctionMaps.byUniversalIdentifier)
|
||||
.filter(
|
||||
(flatLogicFunction): flatLogicFunction is FlatLogicFunction =>
|
||||
isDefined(flatLogicFunction) &&
|
||||
!isDefined(flatLogicFunction.deletedAt),
|
||||
)
|
||||
.map((flatLogicFunction) =>
|
||||
fromFlatLogicFunctionToLogicFunctionDto({ flatLogicFunction }),
|
||||
);
|
||||
}
|
||||
|
||||
async createOne({
|
||||
input,
|
||||
workspaceId,
|
||||
ownerFlatApplication,
|
||||
}: {
|
||||
input: CreateLogicFunction;
|
||||
ownerFlatApplication: FlatApplication;
|
||||
workspaceId: string;
|
||||
}) {
|
||||
const resolvedOwnerFlatApplication =
|
||||
ownerFlatApplication ??
|
||||
(
|
||||
await this.applicationService.findWorkspaceTwentyStandardAndCustomApplicationOrThrow(
|
||||
{ workspaceId },
|
||||
)
|
||||
).workspaceCustomFlatApplication;
|
||||
|
||||
const flatLogicFunctionToCreate =
|
||||
fromCreateLogicFunctionInputToFlatLogicFunction({
|
||||
createLogicFunctionInput: input,
|
||||
workspaceId,
|
||||
ownerFlatApplication: resolvedOwnerFlatApplication,
|
||||
});
|
||||
|
||||
const validateAndBuildResult =
|
||||
await this.workspaceMigrationValidateBuildAndRunService.validateBuildAndRunWorkspaceMigration(
|
||||
{
|
||||
allFlatEntityOperationByMetadataName: {
|
||||
logicFunction: {
|
||||
flatEntityToCreate: [flatLogicFunctionToCreate],
|
||||
flatEntityToDelete: [],
|
||||
flatEntityToUpdate: [],
|
||||
},
|
||||
},
|
||||
workspaceId,
|
||||
isSystemBuild: false,
|
||||
applicationUniversalIdentifier:
|
||||
resolvedOwnerFlatApplication.universalIdentifier,
|
||||
},
|
||||
);
|
||||
|
||||
if (validateAndBuildResult.status === 'fail') {
|
||||
throw new WorkspaceMigrationBuilderException(
|
||||
validateAndBuildResult,
|
||||
'Multiple validation errors occurred while creating logic function',
|
||||
);
|
||||
}
|
||||
|
||||
return flatLogicFunctionToCreate;
|
||||
}
|
||||
|
||||
async updateOne({
|
||||
id,
|
||||
update,
|
||||
workspaceId,
|
||||
ownerFlatApplication,
|
||||
}: {
|
||||
id: string;
|
||||
update: Omit<UpdateLogicFunctionFromSourceInput['update'], 'source'>;
|
||||
workspaceId: string;
|
||||
ownerFlatApplication?: FlatApplication;
|
||||
}) {
|
||||
const resolvedOwnerFlatApplication =
|
||||
ownerFlatApplication ??
|
||||
(
|
||||
await this.applicationService.findWorkspaceTwentyStandardAndCustomApplicationOrThrow(
|
||||
{ workspaceId },
|
||||
)
|
||||
).workspaceCustomFlatApplication;
|
||||
|
||||
const { flatLogicFunctionMaps } =
|
||||
await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatMapsKeys: ['flatLogicFunctionMaps'],
|
||||
},
|
||||
);
|
||||
|
||||
const optimisticallyUpdatedFlatLogicFunction =
|
||||
fromUpdateLogicFunctionInputToFlatLogicFunctionToUpdateOrThrow({
|
||||
flatLogicFunctionMaps,
|
||||
updateLogicFunctionInput: { id, update },
|
||||
});
|
||||
|
||||
const validateAndBuildResult =
|
||||
await this.workspaceMigrationValidateBuildAndRunService.validateBuildAndRunWorkspaceMigration(
|
||||
{
|
||||
allFlatEntityOperationByMetadataName: {
|
||||
logicFunction: {
|
||||
flatEntityToCreate: [],
|
||||
flatEntityToDelete: [],
|
||||
flatEntityToUpdate: [optimisticallyUpdatedFlatLogicFunction],
|
||||
},
|
||||
},
|
||||
workspaceId,
|
||||
isSystemBuild: false,
|
||||
applicationUniversalIdentifier:
|
||||
resolvedOwnerFlatApplication.universalIdentifier,
|
||||
},
|
||||
);
|
||||
|
||||
if (validateAndBuildResult.status === 'fail') {
|
||||
throw new WorkspaceMigrationBuilderException(
|
||||
validateAndBuildResult,
|
||||
'Multiple validation errors occurred while updating logic function',
|
||||
);
|
||||
}
|
||||
|
||||
return this.getFlatLogicFunctionAfterUpdate(
|
||||
optimisticallyUpdatedFlatLogicFunction.id,
|
||||
workspaceId,
|
||||
);
|
||||
}
|
||||
|
||||
private async getFlatLogicFunctionAfterUpdate(
|
||||
id: string,
|
||||
workspaceId: string,
|
||||
) {
|
||||
const { flatLogicFunctionMaps: recomputedExistingFlatLogicFunctionMaps } =
|
||||
await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatMapsKeys: ['flatLogicFunctionMaps'],
|
||||
},
|
||||
);
|
||||
|
||||
return findFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityId: id,
|
||||
flatEntityMaps: recomputedExistingFlatLogicFunctionMaps,
|
||||
});
|
||||
}
|
||||
|
||||
async destroyOne({
|
||||
id,
|
||||
workspaceId,
|
||||
applicationId: _applicationId,
|
||||
isSystemBuild = false,
|
||||
ownerFlatApplication,
|
||||
}: {
|
||||
id: string;
|
||||
workspaceId: string;
|
||||
applicationId?: string;
|
||||
isSystemBuild?: boolean;
|
||||
ownerFlatApplication?: FlatApplication;
|
||||
}): Promise<FlatLogicFunction> {
|
||||
const resolvedOwnerFlatApplication =
|
||||
ownerFlatApplication ??
|
||||
(
|
||||
await this.applicationService.findWorkspaceTwentyStandardAndCustomApplicationOrThrow(
|
||||
{ workspaceId },
|
||||
)
|
||||
).workspaceCustomFlatApplication;
|
||||
|
||||
const { flatLogicFunctionMaps: existingFlatLogicFunctionMaps } =
|
||||
await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatMapsKeys: ['flatLogicFunctionMaps'],
|
||||
},
|
||||
);
|
||||
|
||||
const existingFlatLogicFunction = findFlatEntityByIdInFlatEntityMaps({
|
||||
flatEntityId: id,
|
||||
flatEntityMaps: existingFlatLogicFunctionMaps,
|
||||
});
|
||||
|
||||
if (!isDefined(existingFlatLogicFunction)) {
|
||||
throw new LogicFunctionException(
|
||||
'Logic function to destroy not found',
|
||||
LogicFunctionExceptionCode.LOGIC_FUNCTION_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
const validateAndBuildResult =
|
||||
await this.workspaceMigrationValidateBuildAndRunService.validateBuildAndRunWorkspaceMigration(
|
||||
{
|
||||
allFlatEntityOperationByMetadataName: {
|
||||
logicFunction: {
|
||||
flatEntityToCreate: [],
|
||||
flatEntityToDelete: [existingFlatLogicFunction],
|
||||
flatEntityToUpdate: [],
|
||||
},
|
||||
},
|
||||
workspaceId,
|
||||
isSystemBuild,
|
||||
applicationUniversalIdentifier:
|
||||
resolvedOwnerFlatApplication.universalIdentifier,
|
||||
},
|
||||
);
|
||||
|
||||
if (validateAndBuildResult.status === 'fail') {
|
||||
throw new WorkspaceMigrationBuilderException(
|
||||
validateAndBuildResult,
|
||||
'Multiple validation errors occurred while destroying logic function',
|
||||
);
|
||||
}
|
||||
|
||||
return existingFlatLogicFunction;
|
||||
}
|
||||
|
||||
async getAvailablePackages({
|
||||
logicFunctionId,
|
||||
workspaceId,
|
||||
}: {
|
||||
logicFunctionId: string;
|
||||
workspaceId: string;
|
||||
}) {
|
||||
const { flatLogicFunctionMaps, flatApplicationMaps } =
|
||||
await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatMapsKeys: ['flatLogicFunctionMaps', 'flatApplicationMaps'],
|
||||
},
|
||||
);
|
||||
|
||||
const logicFunctionUniversalIdentifier =
|
||||
flatLogicFunctionMaps.universalIdentifierById[logicFunctionId];
|
||||
|
||||
if (!logicFunctionUniversalIdentifier) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const logicFunction =
|
||||
flatLogicFunctionMaps.byUniversalIdentifier[
|
||||
logicFunctionUniversalIdentifier
|
||||
];
|
||||
|
||||
if (!logicFunction) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const application = flatApplicationMaps.byId[logicFunction.applicationId];
|
||||
|
||||
return application?.availablePackages ?? {};
|
||||
}
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { LogicFunctionRuntime } from 'src/engine/metadata-modules/logic-function/logic-function.entity';
|
||||
import { type UniversalFlatLogicFunction } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-logic-function.type';
|
||||
|
||||
type AutoGeneratedFields = 'createdAt' | 'updatedAt' | 'deletedAt' | 'runtime';
|
||||
|
||||
type RequiredFields =
|
||||
| 'name'
|
||||
| 'handlerName'
|
||||
| 'sourceHandlerPath'
|
||||
| 'builtHandlerPath'
|
||||
| 'isBuildUpToDate'
|
||||
| 'applicationUniversalIdentifier';
|
||||
|
||||
type OptionalFields = Exclude<
|
||||
keyof UniversalFlatLogicFunction,
|
||||
AutoGeneratedFields | RequiredFields
|
||||
>;
|
||||
|
||||
export type BuildUniversalFlatLogicFunctionToCreateInput = Pick<
|
||||
UniversalFlatLogicFunction,
|
||||
RequiredFields
|
||||
> &
|
||||
Partial<Pick<UniversalFlatLogicFunction, OptionalFields>> & {
|
||||
id?: string;
|
||||
};
|
||||
|
||||
export const buildUniversalFlatLogicFunctionToCreate = (
|
||||
input: BuildUniversalFlatLogicFunctionToCreateInput,
|
||||
): UniversalFlatLogicFunction & { id: string } => {
|
||||
const now = new Date().toISOString();
|
||||
|
||||
return {
|
||||
id: input.id ?? v4(),
|
||||
universalIdentifier: input.universalIdentifier ?? v4(),
|
||||
name: input.name,
|
||||
description: input.description ?? null,
|
||||
runtime: LogicFunctionRuntime.NODE22,
|
||||
timeoutSeconds: input.timeoutSeconds ?? 300,
|
||||
checksum: input.checksum ?? null,
|
||||
toolInputSchema: input.toolInputSchema ?? null,
|
||||
isTool: input.isTool ?? false,
|
||||
isBuildUpToDate: input.isBuildUpToDate,
|
||||
handlerName: input.handlerName,
|
||||
sourceHandlerPath: input.sourceHandlerPath,
|
||||
builtHandlerPath: input.builtHandlerPath,
|
||||
cronTriggerSettings: input.cronTriggerSettings ?? null,
|
||||
databaseEventTriggerSettings: input.databaseEventTriggerSettings ?? null,
|
||||
httpRouteTriggerSettings: input.httpRouteTriggerSettings ?? null,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
deletedAt: null,
|
||||
applicationUniversalIdentifier: input.applicationUniversalIdentifier,
|
||||
};
|
||||
};
|
||||
-52
@@ -1,52 +0,0 @@
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { type FlatApplication } from 'src/engine/core-modules/application/types/flat-application.type';
|
||||
import { LogicFunctionRuntime } from 'src/engine/metadata-modules/logic-function/logic-function.entity';
|
||||
import { type FlatLogicFunction } from 'src/engine/metadata-modules/logic-function/types/flat-logic-function.type';
|
||||
import { type CreateLogicFunction } from 'src/engine/metadata-modules/logic-function/dtos/create-logic-function.input';
|
||||
|
||||
export type FromCreateLogicFunctionInputToFlatLogicFunctionArgs = {
|
||||
createLogicFunctionInput: CreateLogicFunction;
|
||||
workspaceId: string;
|
||||
ownerFlatApplication: FlatApplication;
|
||||
};
|
||||
|
||||
export const fromCreateLogicFunctionInputToFlatLogicFunction = ({
|
||||
createLogicFunctionInput: rawCreateLogicFunctionInput,
|
||||
workspaceId,
|
||||
ownerFlatApplication,
|
||||
}: FromCreateLogicFunctionInputToFlatLogicFunctionArgs): FlatLogicFunction => {
|
||||
const id = rawCreateLogicFunctionInput.id ?? v4();
|
||||
const currentDate = new Date();
|
||||
|
||||
const universalIdentifier =
|
||||
rawCreateLogicFunctionInput.universalIdentifier ?? v4();
|
||||
|
||||
return {
|
||||
id,
|
||||
cronTriggerSettings:
|
||||
rawCreateLogicFunctionInput.cronTriggerSettings ?? null,
|
||||
databaseEventTriggerSettings:
|
||||
rawCreateLogicFunctionInput.databaseEventTriggerSettings ?? null,
|
||||
httpRouteTriggerSettings:
|
||||
rawCreateLogicFunctionInput.httpRouteTriggerSettings ?? null,
|
||||
name: rawCreateLogicFunctionInput.name,
|
||||
description: rawCreateLogicFunctionInput.description ?? null,
|
||||
sourceHandlerPath: rawCreateLogicFunctionInput.sourceHandlerPath,
|
||||
handlerName: rawCreateLogicFunctionInput.handlerName,
|
||||
builtHandlerPath: rawCreateLogicFunctionInput.builtHandlerPath,
|
||||
universalIdentifier,
|
||||
createdAt: currentDate.toISOString(),
|
||||
updatedAt: currentDate.toISOString(),
|
||||
deletedAt: null,
|
||||
applicationId: ownerFlatApplication.id,
|
||||
runtime: LogicFunctionRuntime.NODE22,
|
||||
timeoutSeconds: rawCreateLogicFunctionInput.timeoutSeconds ?? 300,
|
||||
workspaceId,
|
||||
checksum: rawCreateLogicFunctionInput.checksum ?? null,
|
||||
toolInputSchema: rawCreateLogicFunctionInput.toolInputSchema ?? null,
|
||||
isTool: rawCreateLogicFunctionInput?.isTool ?? false,
|
||||
isBuildUpToDate: rawCreateLogicFunctionInput.isBuildUpToDate,
|
||||
applicationUniversalIdentifier: ownerFlatApplication.universalIdentifier,
|
||||
};
|
||||
};
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
import { trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties } from 'twenty-shared/utils';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { LogicFunctionRuntime } from 'src/engine/metadata-modules/logic-function/logic-function.entity';
|
||||
import { type CreateLogicFunctionFromSourceInput } from 'src/engine/metadata-modules/logic-function/dtos/create-logic-function-from-source.input';
|
||||
import { type UniversalFlatLogicFunction } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-logic-function.type';
|
||||
|
||||
export const fromCreateLogicFunctionFromSourceInputToUniversalFlatLogicFunctionToCreate =
|
||||
({
|
||||
createLogicFunctionFromSourceInput,
|
||||
sourceHandlerPath,
|
||||
builtHandlerPath,
|
||||
handlerName,
|
||||
checksum,
|
||||
toolInputSchema,
|
||||
isBuildUpToDate,
|
||||
applicationUniversalIdentifier,
|
||||
}: {
|
||||
createLogicFunctionFromSourceInput: CreateLogicFunctionFromSourceInput;
|
||||
sourceHandlerPath: string;
|
||||
builtHandlerPath: string;
|
||||
handlerName: string;
|
||||
checksum: string | null;
|
||||
toolInputSchema: object | null;
|
||||
isBuildUpToDate: boolean;
|
||||
applicationUniversalIdentifier: string;
|
||||
}): UniversalFlatLogicFunction & { id: string } => {
|
||||
const now = new Date().toISOString();
|
||||
|
||||
const { name, description } =
|
||||
trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties(
|
||||
createLogicFunctionFromSourceInput,
|
||||
['name', 'description'],
|
||||
);
|
||||
|
||||
const id = createLogicFunctionFromSourceInput.id ?? v4();
|
||||
|
||||
return {
|
||||
id,
|
||||
universalIdentifier:
|
||||
createLogicFunctionFromSourceInput.universalIdentifier ?? v4(),
|
||||
name,
|
||||
description: description ?? null,
|
||||
runtime: LogicFunctionRuntime.NODE22,
|
||||
timeoutSeconds: createLogicFunctionFromSourceInput.timeoutSeconds ?? 300,
|
||||
checksum,
|
||||
toolInputSchema,
|
||||
isTool: createLogicFunctionFromSourceInput.isTool ?? false,
|
||||
isBuildUpToDate,
|
||||
handlerName,
|
||||
sourceHandlerPath,
|
||||
builtHandlerPath,
|
||||
cronTriggerSettings:
|
||||
createLogicFunctionFromSourceInput.cronTriggerSettings ?? null,
|
||||
databaseEventTriggerSettings:
|
||||
createLogicFunctionFromSourceInput.databaseEventTriggerSettings ?? null,
|
||||
httpRouteTriggerSettings:
|
||||
createLogicFunctionFromSourceInput.httpRouteTriggerSettings ?? null,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
deletedAt: null,
|
||||
applicationUniversalIdentifier,
|
||||
};
|
||||
};
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { FLAT_LOGIC_FUNCTION_EDITABLE_PROPERTIES } from 'src/engine/metadata-modules/logic-function/constants/flat-logic-function-editable-properties.constant';
|
||||
import { type UpdateLogicFunctionFromSourceInput } from 'src/engine/metadata-modules/logic-function/dtos/update-logic-function-from-source.input';
|
||||
import { type FlatLogicFunction } from 'src/engine/metadata-modules/logic-function/types/flat-logic-function.type';
|
||||
import { mergeUpdateInExistingRecord } from 'src/utils/merge-update-in-existing-record.util';
|
||||
|
||||
export const fromUpdateLogicFunctionFromSourceInputToFlatLogicFunctionToUpdate =
|
||||
({
|
||||
updateLogicFunctionFromSourceInput,
|
||||
existingFlatLogicFunction,
|
||||
}: {
|
||||
updateLogicFunctionFromSourceInput: UpdateLogicFunctionFromSourceInput;
|
||||
existingFlatLogicFunction: FlatLogicFunction;
|
||||
}): FlatLogicFunction => {
|
||||
const { sourceHandlerCode, ...metadataUpdates } =
|
||||
updateLogicFunctionFromSourceInput.update;
|
||||
|
||||
return {
|
||||
...mergeUpdateInExistingRecord({
|
||||
existing: existingFlatLogicFunction,
|
||||
properties: [...FLAT_LOGIC_FUNCTION_EDITABLE_PROPERTIES],
|
||||
update: {
|
||||
...metadataUpdates,
|
||||
...(isDefined(sourceHandlerCode) ? { isBuildUpToDate: false } : {}),
|
||||
},
|
||||
}),
|
||||
updatedAt: new Date().toISOString(),
|
||||
};
|
||||
};
|
||||
-33
@@ -1,33 +0,0 @@
|
||||
import { trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties } from 'twenty-shared/utils';
|
||||
|
||||
import { type MetadataFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/metadata-flat-entity-maps.type';
|
||||
import { FLAT_LOGIC_FUNCTION_EDITABLE_PROPERTIES } from 'src/engine/metadata-modules/logic-function/constants/flat-logic-function-editable-properties.constant';
|
||||
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 { mergeUpdateInExistingRecord } from 'src/utils/merge-update-in-existing-record.util';
|
||||
import { type UpdateLogicFunctionFromSourceInput } from 'src/engine/metadata-modules/logic-function/dtos/update-logic-function-from-source.input';
|
||||
|
||||
export const fromUpdateLogicFunctionInputToFlatLogicFunctionToUpdateOrThrow = ({
|
||||
updateLogicFunctionInput: rawUpdateLogicFunctionInput,
|
||||
flatLogicFunctionMaps,
|
||||
}: {
|
||||
updateLogicFunctionInput: UpdateLogicFunctionFromSourceInput;
|
||||
flatLogicFunctionMaps: MetadataFlatEntityMaps<'logicFunction'>;
|
||||
}): FlatLogicFunction => {
|
||||
const { id: logicFunctionToUpdateId } =
|
||||
trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties(
|
||||
rawUpdateLogicFunctionInput,
|
||||
['id'],
|
||||
);
|
||||
|
||||
const existingFlatLogicFunctionToUpdate = findFlatLogicFunctionOrThrow({
|
||||
id: logicFunctionToUpdateId,
|
||||
flatLogicFunctionMaps,
|
||||
});
|
||||
|
||||
return mergeUpdateInExistingRecord({
|
||||
existing: existingFlatLogicFunctionToUpdate,
|
||||
properties: FLAT_LOGIC_FUNCTION_EDITABLE_PROPERTIES,
|
||||
update: rawUpdateLogicFunctionInput.update,
|
||||
});
|
||||
};
|
||||
+3
-3
@@ -8,7 +8,7 @@ import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-m
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { buildObjectIdByNameMaps } from 'src/engine/metadata-modules/flat-object-metadata/utils/build-object-id-by-name-maps.util';
|
||||
import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service';
|
||||
import { LogicFunctionMetadataService } from 'src/engine/metadata-modules/logic-function/services/logic-function-metadata.service';
|
||||
import { LogicFunctionFromSourceService } from 'src/engine/metadata-modules/logic-function/services/logic-function-from-source.service';
|
||||
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 { buildSystemAuthContext } from 'src/engine/twenty-orm/utils/build-system-auth-context.util';
|
||||
@@ -42,7 +42,7 @@ export type ObjectMetadataInfo = {
|
||||
export class WorkflowCommonWorkspaceService {
|
||||
constructor(
|
||||
private readonly globalWorkspaceOrmManager: GlobalWorkspaceOrmManager,
|
||||
private readonly logicFunctionMetadataService: LogicFunctionMetadataService,
|
||||
private readonly logicFunctionFromSourceService: LogicFunctionFromSourceService,
|
||||
private readonly workspaceManyOrAllFlatEntityMapsCacheService: WorkspaceManyOrAllFlatEntityMapsCacheService,
|
||||
) {}
|
||||
|
||||
@@ -322,7 +322,7 @@ export class WorkflowCommonWorkspaceService {
|
||||
for (const workflowVersion of workflowVersions) {
|
||||
for (const step of workflowVersion.steps ?? []) {
|
||||
if (step.type === WorkflowActionType.CODE) {
|
||||
await this.logicFunctionMetadataService.destroyOne({
|
||||
await this.logicFunctionFromSourceService.deleteOneWithSource({
|
||||
id: step.settings.input.logicFunctionId,
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
+12
-114
@@ -6,8 +6,7 @@ import { AgentService } from 'src/engine/metadata-modules/ai/ai-agent/agent.serv
|
||||
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 { 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 { LogicFunctionFromSourceService } from 'src/engine/metadata-modules/logic-function/services/logic-function-from-source.service';
|
||||
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';
|
||||
@@ -25,7 +24,7 @@ const mockWorkspaceId = 'workspace-id';
|
||||
describe('WorkflowVersionStepOperationsWorkspaceService', () => {
|
||||
let service: WorkflowVersionStepOperationsWorkspaceService;
|
||||
let globalWorkspaceOrmManager: jest.Mocked<GlobalWorkspaceOrmManager>;
|
||||
let logicFunctionMetadataService: jest.Mocked<LogicFunctionMetadataService>;
|
||||
let logicFunctionFromSourceService: jest.Mocked<LogicFunctionFromSourceService>;
|
||||
let codeStepBuildService: jest.Mocked<CodeStepBuildService>;
|
||||
let agentService: jest.Mocked<AgentService>;
|
||||
let roleTargetRepository: jest.Mocked<any>;
|
||||
@@ -33,7 +32,6 @@ describe('WorkflowVersionStepOperationsWorkspaceService', () => {
|
||||
let workflowCommonWorkspaceService: jest.Mocked<WorkflowCommonWorkspaceService>;
|
||||
let aiAgentRoleService: jest.Mocked<AiAgentRoleService>;
|
||||
let workspaceCacheService: jest.Mocked<WorkspaceCacheService>;
|
||||
let flatEntityMapsCacheService: jest.Mocked<WorkspaceManyOrAllFlatEntityMapsCacheService>;
|
||||
|
||||
beforeEach(async () => {
|
||||
codeStepBuildService = {
|
||||
@@ -89,10 +87,9 @@ describe('WorkflowVersionStepOperationsWorkspaceService', () => {
|
||||
}),
|
||||
} as unknown as jest.Mocked<CodeStepBuildService>;
|
||||
|
||||
logicFunctionMetadataService = {
|
||||
createOne: jest.fn(),
|
||||
destroyOne: jest.fn(),
|
||||
} as unknown as jest.Mocked<LogicFunctionMetadataService>;
|
||||
logicFunctionFromSourceService = {
|
||||
deleteOneWithSource: jest.fn(),
|
||||
} as unknown as jest.Mocked<LogicFunctionFromSourceService>;
|
||||
|
||||
agentService = {
|
||||
deleteManyAgents: jest.fn().mockResolvedValue([]),
|
||||
@@ -132,8 +129,8 @@ describe('WorkflowVersionStepOperationsWorkspaceService', () => {
|
||||
useValue: globalWorkspaceOrmManager,
|
||||
},
|
||||
{
|
||||
provide: LogicFunctionMetadataService,
|
||||
useValue: logicFunctionMetadataService,
|
||||
provide: LogicFunctionFromSourceService,
|
||||
useValue: logicFunctionFromSourceService,
|
||||
},
|
||||
{
|
||||
provide: CodeStepBuildService,
|
||||
@@ -165,12 +162,12 @@ describe('WorkflowVersionStepOperationsWorkspaceService', () => {
|
||||
},
|
||||
{
|
||||
provide: WorkspaceManyOrAllFlatEntityMapsCacheService,
|
||||
useValue: (flatEntityMapsCacheService = {
|
||||
useValue: {
|
||||
flushFlatEntityMaps: jest.fn(),
|
||||
getOrRecomputeManyOrAllFlatEntityMaps: jest
|
||||
.fn()
|
||||
.mockResolvedValue(createEmptyAllFlatEntityMaps()),
|
||||
} as unknown as jest.Mocked<WorkspaceManyOrAllFlatEntityMapsCacheService>),
|
||||
},
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
@@ -203,7 +200,9 @@ describe('WorkflowVersionStepOperationsWorkspaceService', () => {
|
||||
workspaceId: mockWorkspaceId,
|
||||
});
|
||||
|
||||
expect(logicFunctionMetadataService.destroyOne).toHaveBeenCalledWith({
|
||||
expect(
|
||||
logicFunctionFromSourceService.deleteOneWithSource,
|
||||
).toHaveBeenCalledWith({
|
||||
id: 'function-id',
|
||||
workspaceId: mockWorkspaceId,
|
||||
});
|
||||
@@ -286,35 +285,6 @@ describe('WorkflowVersionStepOperationsWorkspaceService', () => {
|
||||
|
||||
describe('runStepCreationSideEffectsAndBuildStep', () => {
|
||||
it('should create code step with logic function', async () => {
|
||||
const mockFlatLogicFunction: FlatLogicFunction = {
|
||||
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: 'src/index.mjs',
|
||||
handlerName: 'main',
|
||||
checksum: null,
|
||||
toolInputSchema: null,
|
||||
isTool: false,
|
||||
universalIdentifier: 'universal-id',
|
||||
applicationId: 'application-id',
|
||||
applicationUniversalIdentifier: 'application-universal-id',
|
||||
cronTriggerSettings: null,
|
||||
databaseEventTriggerSettings: null,
|
||||
httpRouteTriggerSettings: null,
|
||||
isBuildUpToDate: false,
|
||||
};
|
||||
|
||||
logicFunctionMetadataService.createOne.mockResolvedValue(
|
||||
mockFlatLogicFunction,
|
||||
);
|
||||
|
||||
const result = await service.runStepCreationSideEffectsAndBuildStep({
|
||||
type: WorkflowActionType.CODE,
|
||||
workspaceId: mockWorkspaceId,
|
||||
@@ -360,78 +330,6 @@ describe('WorkflowVersionStepOperationsWorkspaceService', () => {
|
||||
nextStepIds: ['next-step'],
|
||||
} as unknown as WorkflowAction;
|
||||
|
||||
const mockExistingFlatLogicFunction: FlatLogicFunction = {
|
||||
id: 'function-id',
|
||||
name: 'Existing Function',
|
||||
description: 'Existing Description',
|
||||
workspaceId: mockWorkspaceId,
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
deletedAt: null,
|
||||
runtime: LogicFunctionRuntime.NODE22,
|
||||
timeoutSeconds: 30,
|
||||
sourceHandlerPath: 'workflow/function-id/src/index.ts',
|
||||
builtHandlerPath: 'workflow/function-id/src/index.mjs',
|
||||
handlerName: 'main',
|
||||
checksum: 'existing-checksum',
|
||||
toolInputSchema: null,
|
||||
isTool: false,
|
||||
universalIdentifier: 'existing-universal-id',
|
||||
applicationId: 'application-id',
|
||||
applicationUniversalIdentifier: 'application-universal-id',
|
||||
cronTriggerSettings: null,
|
||||
databaseEventTriggerSettings: null,
|
||||
httpRouteTriggerSettings: null,
|
||||
isBuildUpToDate: true,
|
||||
};
|
||||
|
||||
const mockNewFlatLogicFunction: FlatLogicFunction = {
|
||||
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: 'src/index.mjs',
|
||||
handlerName: 'main',
|
||||
checksum: null,
|
||||
toolInputSchema: null,
|
||||
isTool: false,
|
||||
universalIdentifier: 'universal-id',
|
||||
applicationId: 'application-id',
|
||||
applicationUniversalIdentifier: 'application-universal-id',
|
||||
cronTriggerSettings: null,
|
||||
databaseEventTriggerSettings: null,
|
||||
httpRouteTriggerSettings: null,
|
||||
isBuildUpToDate: false,
|
||||
};
|
||||
|
||||
const emptyMaps = createEmptyAllFlatEntityMaps();
|
||||
const flatLogicFunctionMapsKey = 'flatLogicFunctionMaps' as const;
|
||||
|
||||
flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps.mockResolvedValueOnce(
|
||||
{
|
||||
...emptyMaps,
|
||||
[flatLogicFunctionMapsKey]: {
|
||||
byUniversalIdentifier: {
|
||||
'existing-universal-id': mockExistingFlatLogicFunction,
|
||||
},
|
||||
universalIdentifierById: {
|
||||
'function-id': 'existing-universal-id',
|
||||
},
|
||||
universalIdentifiersByApplicationId: {},
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
logicFunctionMetadataService.createOne.mockResolvedValue(
|
||||
mockNewFlatLogicFunction,
|
||||
);
|
||||
|
||||
const clonedStep = await service.cloneStep({
|
||||
step: originalStep,
|
||||
workspaceId: mockWorkspaceId,
|
||||
|
||||
+5
-119
@@ -1,36 +1,20 @@
|
||||
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 { 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 { 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-from-source-input-to-flat-logic-function.util';
|
||||
import { LogicFunctionFromSourceService } from 'src/engine/metadata-modules/logic-function/services/logic-function-from-source.service';
|
||||
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 type { JsonbProperty } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/jsonb-property.type';
|
||||
import { LogicFunctionFromSourceService } from 'src/engine/metadata-modules/logic-function/services/logic-function-from-source.service';
|
||||
|
||||
@Injectable()
|
||||
export class CodeStepBuildService {
|
||||
constructor(
|
||||
private readonly workspaceManyOrAllFlatEntityMapsCacheService: WorkspaceManyOrAllFlatEntityMapsCacheService,
|
||||
private readonly logicFunctionMetadataService: LogicFunctionMetadataService,
|
||||
private readonly logicFunctionFromSourceService: LogicFunctionFromSourceService,
|
||||
private readonly applicationService: ApplicationService,
|
||||
private readonly logicFunctionResourceService: LogicFunctionResourceService,
|
||||
) {}
|
||||
|
||||
async createCodeStepLogicFunction({
|
||||
@@ -40,7 +24,7 @@ export class CodeStepBuildService {
|
||||
logicFunctionId: string;
|
||||
workspaceId: string;
|
||||
}) {
|
||||
return await this.logicFunctionFromSourceService.createOne({
|
||||
return await this.logicFunctionFromSourceService.createOneFromSource({
|
||||
input: {
|
||||
id: logicFunctionId,
|
||||
name: 'A Code Step',
|
||||
@@ -56,109 +40,11 @@ export class CodeStepBuildService {
|
||||
}: {
|
||||
existingLogicFunctionId: string;
|
||||
workspaceId: string;
|
||||
}): Promise<FlatLogicFunction> {
|
||||
const { flatLogicFunctionMaps } =
|
||||
await this.workspaceManyOrAllFlatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatMapsKeys: ['flatLogicFunctionMaps'],
|
||||
},
|
||||
);
|
||||
|
||||
const existingLogicFunction = findFlatLogicFunctionOrThrow({
|
||||
id: existingLogicFunctionId,
|
||||
flatLogicFunctionMaps,
|
||||
});
|
||||
|
||||
const resolvedOwnerFlatApplication = (
|
||||
await this.applicationService.findWorkspaceTwentyStandardAndCustomApplicationOrThrow(
|
||||
{ workspaceId },
|
||||
)
|
||||
).workspaceCustomFlatApplication;
|
||||
|
||||
const applicationUniversalIdentifier =
|
||||
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,
|
||||
toSourceHandlerPath,
|
||||
fromBuiltHandlerPath: builtHandlerPath,
|
||||
toBuiltHandlerPath: toBuiltHandlerPath,
|
||||
}): Promise<{ id: string }> {
|
||||
return this.logicFunctionFromSourceService.duplicateOneWithSource({
|
||||
existingLogicFunctionId,
|
||||
workspaceId,
|
||||
applicationUniversalIdentifier,
|
||||
});
|
||||
|
||||
const newFlatLogicFunction =
|
||||
fromCreateLogicFunctionInputToFlatLogicFunction({
|
||||
createLogicFunctionInput: {
|
||||
...existingLogicFunction,
|
||||
id: newId,
|
||||
universalIdentifier: newUniversalIdentifier,
|
||||
description: existingLogicFunction.description ?? undefined,
|
||||
sourceHandlerPath: toSourceHandlerPath,
|
||||
builtHandlerPath: toBuiltHandlerPath,
|
||||
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,
|
||||
});
|
||||
|
||||
const created = await this.logicFunctionMetadataService.createOne({
|
||||
input: {
|
||||
...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,
|
||||
});
|
||||
|
||||
if (!isDefined(created)) {
|
||||
throw new Error(
|
||||
'Failed to create logic function when duplicating code step',
|
||||
);
|
||||
}
|
||||
|
||||
return created;
|
||||
}
|
||||
|
||||
async buildCodeStepsFromSourceForSteps({
|
||||
|
||||
+3
-3
@@ -21,7 +21,7 @@ import { AiAgentRoleService } from 'src/engine/metadata-modules/ai/ai-agent-role
|
||||
import { AgentService } from 'src/engine/metadata-modules/ai/ai-agent/agent.service';
|
||||
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 { LogicFunctionMetadataService } from 'src/engine/metadata-modules/logic-function/services/logic-function-metadata.service';
|
||||
import { LogicFunctionFromSourceService } from 'src/engine/metadata-modules/logic-function/services/logic-function-from-source.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';
|
||||
@@ -65,7 +65,7 @@ const ITERATOR_EMPTY_STEP_POSITION_OFFSET = {
|
||||
export class WorkflowVersionStepOperationsWorkspaceService {
|
||||
constructor(
|
||||
private readonly globalWorkspaceOrmManager: GlobalWorkspaceOrmManager,
|
||||
private readonly logicFunctionMetadataService: LogicFunctionMetadataService,
|
||||
private readonly logicFunctionFromSourceService: LogicFunctionFromSourceService,
|
||||
private readonly codeStepBuildService: CodeStepBuildService,
|
||||
private readonly agentService: AgentService,
|
||||
@InjectRepository(RoleTargetEntity)
|
||||
@@ -87,7 +87,7 @@ export class WorkflowVersionStepOperationsWorkspaceService {
|
||||
}) {
|
||||
switch (step.type) {
|
||||
case WorkflowActionType.CODE: {
|
||||
await this.logicFunctionMetadataService.destroyOne({
|
||||
await this.logicFunctionFromSourceService.deleteOneWithSource({
|
||||
id: step.settings.input.logicFunctionId,
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
+6
-4
@@ -50,10 +50,12 @@ To find the logicFunctionId, look at the code step's settings.input.logicFunctio
|
||||
const { logicFunctionId, code } = parameters;
|
||||
const { workspaceId } = context;
|
||||
|
||||
await deps.logicFunctionFromSourceService.updateOne({
|
||||
id: logicFunctionId,
|
||||
update: {
|
||||
sourceHandlerCode: code,
|
||||
await deps.logicFunctionFromSourceService.updateOneFromSource({
|
||||
updateLogicFunctionFromSourceInput: {
|
||||
id: logicFunctionId,
|
||||
update: {
|
||||
sourceHandlerCode: code,
|
||||
},
|
||||
},
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user