import { UseFilters, UseGuards, UsePipes } from '@nestjs/common'; import { Args, Mutation, Query, Subscription } from '@nestjs/graphql'; import graphqlTypeJson from 'graphql-type-json'; import { PermissionFlagType } from 'twenty-shared/constants'; import { isDefined } from 'twenty-shared/utils'; import { MetadataResolver } from 'src/engine/api/graphql/graphql-config/decorators/metadata-resolver.decorator'; import { PreventNestToAutoLogGraphqlErrorsFilter } from 'src/engine/core-modules/graphql/filters/prevent-nest-to-auto-log-graphql-errors.filter'; import { ResolverValidationPipe } from 'src/engine/core-modules/graphql/pipes/resolver-validation.pipe'; import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator'; 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'; @UseGuards(WorkspaceAuthGuard, FeatureFlagGuard, NoPermissionGuard) @MetadataResolver() @UsePipes(ResolverValidationPipe) @UseFilters(PreventNestToAutoLogGraphqlErrorsFilter) export class LogicFunctionResolver { constructor( private readonly logicFunctionFromSourceService: LogicFunctionFromSourceService, private readonly flatEntityMapsCacheService: WorkspaceManyOrAllFlatEntityMapsCacheService, private readonly subscriptionService: SubscriptionService, ) {} @Query(() => LogicFunctionDTO) async findOneLogicFunction( @Args('input') { id }: LogicFunctionIdInput, @AuthWorkspace() { id: workspaceId }: WorkspaceEntity, ): Promise { try { const { flatLogicFunctionMaps } = await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps( { workspaceId, flatMapsKeys: ['flatLogicFunctionMaps'], }, ); const flatLogicFunction = findFlatLogicFunctionOrThrow({ id, flatLogicFunctionMaps, }); return fromFlatLogicFunctionToLogicFunctionDto({ flatLogicFunction }); } catch (error) { return logicFunctionGraphQLApiExceptionHandler(error); } } @Query(() => [LogicFunctionDTO]) async findManyLogicFunctions( @AuthWorkspace() { id: workspaceId }: WorkspaceEntity, ): Promise { try { 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); } } @Query(() => graphqlTypeJson) @UseGuards(SettingsPermissionGuard(PermissionFlagType.WORKFLOWS)) async getAvailablePackages( @Args('input') { id }: LogicFunctionIdInput, @AuthWorkspace() { id: workspaceId }: WorkspaceEntity, ) { try { 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); } } @Mutation(() => LogicFunctionDTO) @UseGuards(SettingsPermissionGuard(PermissionFlagType.WORKFLOWS)) async deleteOneLogicFunction( @Args('input') { id }: LogicFunctionIdInput, @AuthWorkspace() { id: workspaceId }: WorkspaceEntity, ): Promise { try { return await this.logicFunctionFromSourceService.deleteOneWithSource({ id, workspaceId, }); } catch (error) { return logicFunctionGraphQLApiExceptionHandler(error); } } @Mutation(() => LogicFunctionDTO) @UseGuards(SettingsPermissionGuard(PermissionFlagType.WORKFLOWS)) async createOneLogicFunction( @Args('input') input: CreateLogicFunctionFromSourceInput, @AuthWorkspace() { id: workspaceId }: WorkspaceEntity, ): Promise { try { return await this.logicFunctionFromSourceService.createOneFromSource({ input, workspaceId, }); } catch (error) { return logicFunctionGraphQLApiExceptionHandler(error); } } @Mutation(() => LogicFunctionExecutionResultDTO) @UseGuards(SettingsPermissionGuard(PermissionFlagType.WORKFLOWS)) async executeOneLogicFunction( @Args('input') { id, payload }: ExecuteOneLogicFunctionInput, @AuthWorkspace() { id: workspaceId }: WorkspaceEntity, ): Promise { try { return await this.logicFunctionFromSourceService.executeOneFromSource({ id, payload, workspaceId, }); } catch (error) { return logicFunctionGraphQLApiExceptionHandler(error); } } @Query(() => String, { nullable: true }) @UseGuards(SettingsPermissionGuard(PermissionFlagType.WORKFLOWS)) async getLogicFunctionSourceCode( @Args('input') { id }: LogicFunctionIdInput, @AuthWorkspace() { id: workspaceId }: WorkspaceEntity, ) { try { return await this.logicFunctionFromSourceService.getSourceCode({ id, workspaceId, }); } catch (error) { return logicFunctionGraphQLApiExceptionHandler(error); } } @Mutation(() => Boolean) @UseGuards(SettingsPermissionGuard(PermissionFlagType.WORKFLOWS)) async updateOneLogicFunction( @Args('input') updateLogicFunctionFromSourceInput: UpdateLogicFunctionFromSourceInput, @AuthWorkspace() { id: workspaceId }: WorkspaceEntity, ): Promise { try { await this.logicFunctionFromSourceService.updateOneFromSource({ updateLogicFunctionFromSourceInput, workspaceId, }); return true; } catch (error) { return logicFunctionGraphQLApiExceptionHandler(error); } } @Subscription(() => LogicFunctionLogsDTO, { filter: ( payload: { logicFunctionLogs: LogicFunctionLogsDTO }, variables: { input: LogicFunctionLogsInput }, ) => { const { logicFunctionLogs } = payload; const { id, universalIdentifier, applicationId, applicationUniversalIdentifier, name, } = logicFunctionLogs; const { id: inputId, universalIdentifier: inputUniversalIdentifier, name: inputName, applicationId: inputApplicationId, applicationUniversalIdentifier: inputApplicationUniversalIdentifier, } = variables.input; return ( (!isDefined(inputId) || inputId === id) && (!isDefined(inputUniversalIdentifier) || inputUniversalIdentifier === universalIdentifier) && (!isDefined(inputName) || inputName === name) && (!isDefined(inputApplicationId) || inputApplicationId === applicationId) && (!isDefined(inputApplicationUniversalIdentifier) || inputApplicationUniversalIdentifier === applicationUniversalIdentifier) ); }, }) @UseGuards(SettingsPermissionGuard(PermissionFlagType.WORKFLOWS)) logicFunctionLogs( @Args('input') _: LogicFunctionLogsInput, @AuthWorkspace() workspace: WorkspaceEntity, ) { return this.subscriptionService.subscribe({ channel: SubscriptionChannel.LOGIC_FUNCTION_LOGS_CHANNEL, workspaceId: workspace.id, }); } }