Keep simplifying logic functions (#17595)
## Summary Refactors the `LogicFunctionService` API by consolidating v1 and v2 services: In metadata-module (presentation layer module) - **Renamed methods**: `deleteOneLogicFunction` → `destroyOne`, `updateOneLogicFunction` → `updateOne`, `createOneLogicFunction` → `createOne` - **Added duplicate methods**: `duplicateLogicFunction`, `createLogicFunctionFromExistingLogicFunctionById` - **Removed soft delete/restore** functionality - only hard delete (`destroyOne`) is supported In core-module (lower level module) - **Moved execution methods** to `LogicFunctionExecutorService` which is lower level: `executeOneLogicFunction`, `getAvailablePackages`, `getLogicFunctionSourceCode`
This commit is contained in:
+27
-78
@@ -3,16 +3,15 @@ import { Injectable, Logger } from '@nestjs/common';
|
||||
import { parse } from 'path';
|
||||
|
||||
import {
|
||||
Manifest,
|
||||
FieldManifest,
|
||||
LogicFunctionManifest,
|
||||
LogicFunctionTriggerManifest,
|
||||
ObjectManifest,
|
||||
RoleManifest,
|
||||
Manifest,
|
||||
ObjectFieldManifest,
|
||||
ObjectManifest,
|
||||
RelationFieldManifest,
|
||||
RoleManifest,
|
||||
} from 'twenty-shared/application';
|
||||
import { FieldMetadataType, HTTPMethod, Sources } from 'twenty-shared/types';
|
||||
import { FieldMetadataType, Sources } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { ApplicationEntity } from 'src/engine/core-modules/application/application.entity';
|
||||
@@ -23,25 +22,20 @@ import {
|
||||
import { ApplicationInput } from 'src/engine/core-modules/application/dtos/application.input';
|
||||
import { ApplicationService } from 'src/engine/core-modules/application/services/application.service';
|
||||
import { ApplicationVariableEntityService } from 'src/engine/core-modules/applicationVariable/application-variable.service';
|
||||
import { LogicFunctionLayerService } from 'src/engine/core-modules/logic-function/logic-function-layer/services/logic-function-layer.service';
|
||||
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
|
||||
import { CreateFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/create-field.input';
|
||||
import { FieldMetadataService } from 'src/engine/metadata-modules/field-metadata/services/field-metadata.service';
|
||||
import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service';
|
||||
import { findFlatEntitiesByApplicationId } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entities-by-application-id.util';
|
||||
import { FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { LogicFunctionService } from 'src/engine/metadata-modules/logic-function/services/logic-function.service';
|
||||
import { FlatLogicFunction } from 'src/engine/metadata-modules/logic-function/types/flat-logic-function.type';
|
||||
import { ObjectMetadataService } from 'src/engine/metadata-modules/object-metadata/object-metadata.service';
|
||||
import { FieldPermissionService } from 'src/engine/metadata-modules/object-permission/field-permission/field-permission.service';
|
||||
import { ObjectPermissionService } from 'src/engine/metadata-modules/object-permission/object-permission.service';
|
||||
import { PermissionFlagService } from 'src/engine/metadata-modules/permission-flag/permission-flag.service';
|
||||
import { RoleService } from 'src/engine/metadata-modules/role/role.service';
|
||||
import { LogicFunctionLayerService } from 'src/engine/core-modules/logic-function/logic-function-layer/services/logic-function-layer.service';
|
||||
import {
|
||||
CronTriggerSettings,
|
||||
DatabaseEventTriggerSettings,
|
||||
HttpRouteTriggerSettings,
|
||||
} from 'src/engine/metadata-modules/logic-function/logic-function.entity';
|
||||
import { LogicFunctionV2Service } from 'src/engine/metadata-modules/logic-function/services/logic-function-v2.service';
|
||||
import { FlatLogicFunction } from 'src/engine/metadata-modules/logic-function/types/flat-logic-function.type';
|
||||
import { computeMetadataNameFromLabelOrThrow } from 'src/engine/metadata-modules/utils/compute-metadata-name-from-label-or-throw.util';
|
||||
import { WorkspaceMigrationValidateBuildAndRunService } from 'src/engine/workspace-manager/workspace-migration/services/workspace-migration-validate-build-and-run-service';
|
||||
|
||||
@@ -55,7 +49,7 @@ export class ApplicationSyncService {
|
||||
private readonly logicFunctionLayerService: LogicFunctionLayerService,
|
||||
private readonly objectMetadataService: ObjectMetadataService,
|
||||
private readonly fieldMetadataService: FieldMetadataService,
|
||||
private readonly logicFunctionV2Service: LogicFunctionV2Service,
|
||||
private readonly logicFunctionService: LogicFunctionService,
|
||||
private readonly flatEntityMapsCacheService: WorkspaceManyOrAllFlatEntityMapsCacheService,
|
||||
private readonly dataSourceService: DataSourceService,
|
||||
private readonly workspaceMigrationValidateBuildAndRunService: WorkspaceMigrationValidateBuildAndRunService,
|
||||
@@ -844,9 +838,10 @@ export class ApplicationSyncService {
|
||||
);
|
||||
|
||||
for (const logicFunctionToDelete of logicFunctionsToDelete) {
|
||||
await this.logicFunctionV2Service.destroyOne({
|
||||
destroyLogicFunctionInput: { id: logicFunctionToDelete.id },
|
||||
await this.logicFunctionService.destroyOne({
|
||||
id: logicFunctionToDelete.id,
|
||||
workspaceId,
|
||||
applicationId,
|
||||
isSystemBuild: true,
|
||||
});
|
||||
}
|
||||
@@ -868,7 +863,7 @@ export class ApplicationSyncService {
|
||||
const name =
|
||||
logicFunctionToSync.name ?? parse(logicFunctionToSync.handlerName).name;
|
||||
|
||||
const updateLogicFunctionInput = {
|
||||
await this.logicFunctionService.updateOne({
|
||||
id: logicFunctionToUpdate.id,
|
||||
update: {
|
||||
name,
|
||||
@@ -880,12 +875,9 @@ export class ApplicationSyncService {
|
||||
toolInputSchema: logicFunctionToSync.toolInputSchema,
|
||||
isTool: logicFunctionToSync.isTool,
|
||||
},
|
||||
};
|
||||
|
||||
await this.logicFunctionV2Service.updateOne(
|
||||
updateLogicFunctionInput,
|
||||
workspaceId,
|
||||
);
|
||||
applicationId,
|
||||
});
|
||||
|
||||
// Trigger settings are now embedded in the logic function entity
|
||||
// They are handled through the update input
|
||||
@@ -896,68 +888,25 @@ export class ApplicationSyncService {
|
||||
logicFunctionToCreate.name ??
|
||||
parse(logicFunctionToCreate.handlerName).name;
|
||||
|
||||
const createLogicFunctionInput = {
|
||||
name,
|
||||
code,
|
||||
universalIdentifier: logicFunctionToCreate.universalIdentifier,
|
||||
timeoutSeconds: logicFunctionToCreate.timeoutSeconds,
|
||||
sourceHandlerPath: logicFunctionToCreate.sourceHandlerPath,
|
||||
handlerName: logicFunctionToCreate.handlerName,
|
||||
builtHandlerPath: logicFunctionToCreate.builtHandlerPath,
|
||||
applicationId,
|
||||
logicFunctionLayerId,
|
||||
toolInputSchema: logicFunctionToCreate.toolInputSchema,
|
||||
isTool: logicFunctionToCreate.isTool,
|
||||
};
|
||||
|
||||
await this.logicFunctionV2Service.createOne({
|
||||
createLogicFunctionInput,
|
||||
await this.logicFunctionService.createOne({
|
||||
input: {
|
||||
name,
|
||||
code,
|
||||
universalIdentifier: logicFunctionToCreate.universalIdentifier,
|
||||
timeoutSeconds: logicFunctionToCreate.timeoutSeconds,
|
||||
sourceHandlerPath: logicFunctionToCreate.sourceHandlerPath,
|
||||
handlerName: logicFunctionToCreate.handlerName,
|
||||
builtHandlerPath: logicFunctionToCreate.builtHandlerPath,
|
||||
logicFunctionLayerId,
|
||||
toolInputSchema: logicFunctionToCreate.toolInputSchema,
|
||||
isTool: logicFunctionToCreate.isTool,
|
||||
},
|
||||
workspaceId,
|
||||
applicationId,
|
||||
});
|
||||
|
||||
// Trigger settings are now embedded in the logic function entity
|
||||
// They are handled through the create input
|
||||
}
|
||||
}
|
||||
|
||||
private extractTriggerSettingsFromManifest(
|
||||
triggers: LogicFunctionTriggerManifest[] = [],
|
||||
): {
|
||||
cronTriggerSettings: CronTriggerSettings | null;
|
||||
databaseEventTriggerSettings: DatabaseEventTriggerSettings | null;
|
||||
httpRouteTriggerSettings: HttpRouteTriggerSettings | null;
|
||||
} {
|
||||
let cronTriggerSettings: CronTriggerSettings | null = null;
|
||||
let databaseEventTriggerSettings: DatabaseEventTriggerSettings | null =
|
||||
null;
|
||||
let httpRouteTriggerSettings: HttpRouteTriggerSettings | null = null;
|
||||
|
||||
for (const trigger of triggers) {
|
||||
if (trigger.type === 'cron') {
|
||||
cronTriggerSettings = { pattern: trigger.pattern };
|
||||
} else if (trigger.type === 'databaseEvent') {
|
||||
databaseEventTriggerSettings = {
|
||||
eventName: trigger.eventName,
|
||||
updatedFields: trigger.updatedFields,
|
||||
};
|
||||
} else if (trigger.type === 'route') {
|
||||
httpRouteTriggerSettings = {
|
||||
path: trigger.path,
|
||||
httpMethod: trigger.httpMethod as HTTPMethod,
|
||||
isAuthRequired: trigger.isAuthRequired,
|
||||
forwardedRequestHeaders: trigger.forwardedRequestHeaders,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
cronTriggerSettings,
|
||||
databaseEventTriggerSettings,
|
||||
httpRouteTriggerSettings,
|
||||
};
|
||||
}
|
||||
|
||||
public async uninstallApplication({
|
||||
workspaceId,
|
||||
applicationUniversalIdentifier,
|
||||
|
||||
+5
@@ -1,12 +1,15 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
|
||||
import { AuditModule } from 'src/engine/core-modules/audit/audit.module';
|
||||
import { TokenModule } from 'src/engine/core-modules/auth/token/token.module';
|
||||
import { FileModule } from 'src/engine/core-modules/file/file.module';
|
||||
import { LogicFunctionBuildModule } from 'src/engine/core-modules/logic-function/logic-function-build/logic-function-build.module';
|
||||
import { AddPackagesCommand } from 'src/engine/core-modules/logic-function/logic-function-executor/commands/add-packages.command';
|
||||
import { LogicFunctionExecutorService } from 'src/engine/core-modules/logic-function/logic-function-executor/services/logic-function-executor.service';
|
||||
import { SecretEncryptionModule } from 'src/engine/core-modules/secret-encryption/secret-encryption.module';
|
||||
import { ThrottlerModule } from 'src/engine/core-modules/throttler/throttler.module';
|
||||
import { LogicFunctionEntity } from 'src/engine/metadata-modules/logic-function/logic-function.entity';
|
||||
import { SubscriptionsModule } from 'src/engine/subscriptions/subscriptions.module';
|
||||
import { WorkspaceCacheModule } from 'src/engine/workspace-cache/workspace-cache.module';
|
||||
|
||||
@@ -19,6 +22,8 @@ import { WorkspaceCacheModule } from 'src/engine/workspace-cache/workspace-cache
|
||||
SubscriptionsModule,
|
||||
WorkspaceCacheModule,
|
||||
LogicFunctionBuildModule,
|
||||
FileModule,
|
||||
TypeOrmModule.forFeature([LogicFunctionEntity]),
|
||||
],
|
||||
providers: [LogicFunctionExecutorService, AddPackagesCommand],
|
||||
exports: [LogicFunctionExecutorService],
|
||||
|
||||
+89
@@ -1,28 +1,36 @@
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import {
|
||||
DEFAULT_API_KEY_NAME,
|
||||
DEFAULT_API_URL_NAME,
|
||||
} from 'twenty-shared/application';
|
||||
import { FileFolder } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
import {
|
||||
LogicFunctionExecutorDriver,
|
||||
type LogicFunctionExecuteParams,
|
||||
type LogicFunctionExecuteResult,
|
||||
} from 'src/engine/core-modules/logic-function/logic-function-drivers/interfaces/logic-function-executor-driver.interface';
|
||||
import { FileStorageExceptionCode } from 'src/engine/core-modules/file-storage/interfaces/file-storage-exception';
|
||||
|
||||
import { AuditService } from 'src/engine/core-modules/audit/services/audit.service';
|
||||
import { LOGIC_FUNCTION_EXECUTED_EVENT } from 'src/engine/core-modules/audit/utils/events/workspace-event/logic-function/logic-function-executed';
|
||||
import { ApplicationTokenService } from 'src/engine/core-modules/auth/token/services/application-token.service';
|
||||
import { FileStorageService } from 'src/engine/core-modules/file-storage/file-storage.service';
|
||||
import { LogicFunctionBuildService } from 'src/engine/core-modules/logic-function/logic-function-build/services/logic-function-build.service';
|
||||
import { getLogicFunctionBaseFolderPath } from 'src/engine/core-modules/logic-function/logic-function-build/utils/get-logic-function-base-folder-path.util';
|
||||
import { buildEnvVar } from 'src/engine/core-modules/logic-function/logic-function-drivers/utils/build-env-var';
|
||||
import { LOGIC_FUNCTION_EXECUTOR_DRIVER } from 'src/engine/core-modules/logic-function/logic-function-executor/constants/logic-function-executor.constants';
|
||||
import { SecretEncryptionService } from 'src/engine/core-modules/secret-encryption/secret-encryption.service';
|
||||
import { ThrottlerService } from 'src/engine/core-modules/throttler/throttler.service';
|
||||
import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service';
|
||||
import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps.util';
|
||||
import { LogicFunctionEntity } 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 { findFlatLogicFunctionOrThrow } from 'src/engine/metadata-modules/logic-function/utils/find-flat-logic-function-or-throw.util';
|
||||
import { SubscriptionChannel } from 'src/engine/subscriptions/enums/subscription-channel.enum';
|
||||
import { SubscriptionService } from 'src/engine/subscriptions/subscription.service';
|
||||
import { WorkspaceCacheService } from 'src/engine/workspace-cache/services/workspace-cache.service';
|
||||
@@ -60,6 +68,9 @@ export class LogicFunctionExecutorService
|
||||
private readonly functionBuildService: LogicFunctionBuildService,
|
||||
private readonly subscriptionService: SubscriptionService,
|
||||
private readonly auditService: AuditService,
|
||||
private readonly fileStorageService: FileStorageService,
|
||||
@InjectRepository(LogicFunctionEntity)
|
||||
private readonly logicFunctionRepository: Repository<LogicFunctionEntity>,
|
||||
) {}
|
||||
|
||||
async delete(flatLogicFunction: FlatLogicFunction): Promise<void> {
|
||||
@@ -228,6 +239,84 @@ export class LogicFunctionExecutorService
|
||||
return resultLogicFunction;
|
||||
}
|
||||
|
||||
async getLogicFunctionSourceCode(workspaceId: string, id: string) {
|
||||
try {
|
||||
const { flatLogicFunctionMaps, flatApplicationMaps } =
|
||||
await this.workspaceCacheService.getOrRecompute(workspaceId, [
|
||||
'flatLogicFunctionMaps',
|
||||
'flatApplicationMaps',
|
||||
]);
|
||||
|
||||
const flatLogicFunction = findFlatLogicFunctionOrThrow({
|
||||
id,
|
||||
flatLogicFunctionMaps,
|
||||
});
|
||||
|
||||
const applicationUniversalIdentifier = isDefined(
|
||||
flatLogicFunction.applicationId,
|
||||
)
|
||||
? flatApplicationMaps.byId[flatLogicFunction.applicationId]
|
||||
?.universalIdentifier
|
||||
: undefined;
|
||||
|
||||
if (!isDefined(applicationUniversalIdentifier)) {
|
||||
throw new LogicFunctionExecutionException(
|
||||
`Application universal identifier not found for logic function ${id}`,
|
||||
LogicFunctionExecutionExceptionCode.LOGIC_FUNCTION_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
const baseFolderPath = getLogicFunctionBaseFolderPath(
|
||||
flatLogicFunction.sourceHandlerPath,
|
||||
);
|
||||
|
||||
return await this.fileStorageService.readFolder_v2({
|
||||
workspaceId,
|
||||
applicationUniversalIdentifier,
|
||||
fileFolder: FileFolder.Source,
|
||||
resourcePath: baseFolderPath,
|
||||
});
|
||||
} catch (error) {
|
||||
if (
|
||||
isDefined(error) &&
|
||||
'code' in error &&
|
||||
error.code === FileStorageExceptionCode.FILE_NOT_FOUND
|
||||
) {
|
||||
return;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async getAvailablePackages(logicFunctionId: string) {
|
||||
const logicFunction = await this.logicFunctionRepository.findOneOrFail({
|
||||
where: { id: logicFunctionId },
|
||||
relations: ['logicFunctionLayer'],
|
||||
});
|
||||
|
||||
const packageJson = logicFunction.logicFunctionLayer.packageJson;
|
||||
|
||||
const yarnLock = logicFunction.logicFunctionLayer.yarnLock;
|
||||
|
||||
const packageVersionRegex = /^"([^@]+)@.*?":\n\s+version: (.+)$/gm;
|
||||
|
||||
const versions: Record<string, string> = {};
|
||||
|
||||
let match: RegExpExecArray | null;
|
||||
|
||||
while ((match = packageVersionRegex.exec(yarnLock)) !== null) {
|
||||
const packageName = match[1].split('@', 1)[0];
|
||||
const version = match[2];
|
||||
|
||||
// @ts-expect-error legacy noImplicitAny
|
||||
if (packageJson.dependencies?.[packageName]) {
|
||||
versions[packageName] = version;
|
||||
}
|
||||
}
|
||||
|
||||
return versions;
|
||||
}
|
||||
|
||||
private async throttleExecution(workspaceId: string) {
|
||||
try {
|
||||
await this.throttlerService.tokenBucketThrottleOrThrow(
|
||||
|
||||
+3
-3
@@ -8,10 +8,10 @@ import {
|
||||
type ToolProviderContext,
|
||||
} from 'src/engine/core-modules/tool-provider/interfaces/tool-provider.interface';
|
||||
|
||||
import { LogicFunctionExecutorService } from 'src/engine/core-modules/logic-function/logic-function-executor/services/logic-function-executor.service';
|
||||
import { ToolCategory } from 'src/engine/core-modules/tool-provider/enums/tool-category.enum';
|
||||
import { wrapJsonSchemaForExecution } from 'src/engine/core-modules/tool/utils/wrap-tool-for-execution.util';
|
||||
import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service';
|
||||
import { LogicFunctionService } from 'src/engine/metadata-modules/logic-function/logic-function.service';
|
||||
import { type FlatLogicFunction } from 'src/engine/metadata-modules/logic-function/types/flat-logic-function.type';
|
||||
|
||||
@Injectable()
|
||||
@@ -19,7 +19,7 @@ export class LogicFunctionToolProvider implements ToolProvider {
|
||||
readonly category = ToolCategory.LOGIC_FUNCTION;
|
||||
|
||||
constructor(
|
||||
private readonly logicFunctionService: LogicFunctionService,
|
||||
private readonly logicFunctionExecutorService: LogicFunctionExecutorService,
|
||||
private readonly flatEntityMapsCacheService: WorkspaceManyOrAllFlatEntityMapsCacheService,
|
||||
) {}
|
||||
|
||||
@@ -63,7 +63,7 @@ export class LogicFunctionToolProvider implements ToolProvider {
|
||||
const { loadingMessage: _, ...actualParams } = parameters;
|
||||
|
||||
const result =
|
||||
await this.logicFunctionService.executeOneLogicFunction({
|
||||
await this.logicFunctionExecutorService.executeOneLogicFunction({
|
||||
id: logicFunction.id,
|
||||
workspaceId: context.workspaceId,
|
||||
payload: actualParams,
|
||||
|
||||
Reference in New Issue
Block a user