From 0122f805a4dda675a1ba4c29c22a20cc8206ef98 Mon Sep 17 00:00:00 2001 From: Weiko Date: Tue, 21 Oct 2025 11:43:56 +0200 Subject: [PATCH] Fix serverless update not saving new code with migration v2 (#15221) ## Context Regression introduced in https://github.com/twentyhq/twenty/pull/15032 With the new code, we don't have access to the from/to from the specialised builder anymore and we now rely on diffing result and cache to create the action which broke serverless update because "code" is not part of the cache nor part of the diffing (checksum is). To maintain the existing architecture and keep it generic (by only modifying the specialized builder), the serverless builder overrides the parent validateAndBuild method --- ...erless-function-actions-builder.service.ts | 59 ++++++++++++++----- 1 file changed, 43 insertions(+), 16 deletions(-) diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/serverless-function/workspace-migration-v2-serverless-function-actions-builder.service.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/serverless-function/workspace-migration-v2-serverless-function-actions-builder.service.ts index ffe4d966fb..4e797c83d1 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/serverless-function/workspace-migration-v2-serverless-function-actions-builder.service.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/serverless-function/workspace-migration-v2-serverless-function-actions-builder.service.ts @@ -1,14 +1,17 @@ import { Injectable } from '@nestjs/common'; import { ALL_METADATA_NAME } from 'src/engine/metadata-modules/flat-entity/constant/all-metadata-name.constant'; -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 { UpdateServerlessFunctionAction } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/serverless-function/types/workspace-migration-serverless-function-action-v2.type'; -import { WorkspaceEntityMigrationBuilderV2Service } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/services/workspace-entity-migration-builder-v2.service'; +import { + ValidateAndBuildArgs, + ValidateAndBuildReturnType, + WorkspaceEntityMigrationBuilderV2Service, +} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/services/workspace-entity-migration-builder-v2.service'; import { FlatEntityUpdateValidationArgs } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/flat-entity-update-validation-args.type'; import { FlatEntityValidationArgs } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/flat-entity-validation-args.type'; import { FlatEntityValidationReturnType } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/flat-entity-validation-result.type'; import { FlatServerlessFunctionValidatorService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-serverless-function-validator.service'; -import { fromFlatEntityPropertiesUpdatesToPartialFlatEntity } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/utils/from-flat-entity-properties-updates-to-partial-flat-entity'; @Injectable() export class WorkspaceMigrationV2ServerlessFunctionActionsBuilderService extends WorkspaceEntityMigrationBuilderV2Service< @@ -20,6 +23,43 @@ export class WorkspaceMigrationV2ServerlessFunctionActionsBuilderService extends super(ALL_METADATA_NAME.serverlessFunction); } + public async validateAndBuild( + args: ValidateAndBuildArgs, + ): Promise< + ValidateAndBuildReturnType + > { + const { to: toFlatEntityMaps } = args; + const baseResult = await super.validateAndBuild(args); + + if (baseResult.status === 'fail') { + return baseResult; + } + + const updatedActions = baseResult.actions.updated.map((action) => { + if (action.type !== 'update_serverless_function') { + return action; + } + + const toServerlessFunction = findFlatEntityByIdInFlatEntityMaps({ + flatEntityId: action.serverlessFunctionId, + flatEntityMaps: toFlatEntityMaps, + }); + + return { + ...action, + code: toServerlessFunction?.code, + }; + }); + + return { + ...baseResult, + actions: { + ...baseResult.actions, + updated: updatedActions, + }, + }; + } + protected async validateFlatEntityCreation( args: FlatEntityValidationArgs, ): Promise< @@ -116,25 +156,12 @@ export class WorkspaceMigrationV2ServerlessFunctionActionsBuilderService extends dependencyOptimisticFlatEntityMaps, flatEntityId, flatEntityUpdates, - optimisticFlatEntityMaps, } = args; - const existingEntity = findFlatEntityByIdInFlatEntityMapsOrThrow({ - flatEntityId, - flatEntityMaps: optimisticFlatEntityMaps, - }); - const updatedEntity = { - ...existingEntity, - ...fromFlatEntityPropertiesUpdatesToPartialFlatEntity({ - updates: flatEntityUpdates, - }), - }; - const updateServerlessFunctionAction: UpdateServerlessFunctionAction = { type: 'update_serverless_function', serverlessFunctionId: flatEntityId, updates: flatEntityUpdates, - code: updatedEntity.code, }; return {