From 6c1c78ea3d94ac6dc3031c00bd67fa28aa5d16db Mon Sep 17 00:00:00 2001 From: Marie <51697796+ijreilly@users.noreply.github.com> Date: Tue, 25 Nov 2025 18:23:43 +0100 Subject: [PATCH] [fix] User have no firstName nor lastName (#16057) Closes https://github.com/twentyhq/twenty/issues/15692, fixes https://github.com/twentyhq/twenty/issues/14678 The issue was: - When a user is signing up, they are asked for their first name and last name with which we fulfill their workspaceMember entity for the workspace they are signing up to (which is the one they are creating if they are not joining an existing workspace). user.firstName and user.lastName remain empty - When we create a record, we are using user.firstName and user.lastName to fulfill the text field "createdByName", which intends to save the name of the creator at the moment of the creation. This field is then use 1) when filtering on createdBy (as a trick to be able to filter by this, since we don't have filters on nested fields, ie we dont have filters on person.createdByWorkspaceMember) 2) to display the user creator when a user has left a workspace - otherwise we rely on the dynamic createdByWorkspaceMember.firstName and lastName - So filtering on createdBy was not working as createdByName is empty. We did not see that in dev mode because we have seeded users with names The fix - When we create a record, we use workspaceMember.firstName and workspaceMember.lastName as we should. - When an existing user joins a workspace, they are asked to give their name again so that we set their workspaceMember name - This will not fix the history of the records (they will still have createdByName empty so the filter will not work properly), but a command to fix it would be very long as it would iterate over all the records of each active workspace The long-term view is to get rid of user and to store the name in userWorkspace --- .../auth/sign-in-up/hooks/useSignInUpForm.ts | 2 +- ...eated-by-from-auth-context.service.spec.ts | 18 ++++++++++++++--- .../created-by-from-auth-context.service.ts | 15 +------------- .../user-workspace/user-workspace.module.ts | 2 ++ .../user-workspace.service.spec.ts | 20 +++++++++++++++++++ .../user-workspace/user-workspace.service.ts | 11 ++++++++++ .../workspace-member-query-hook.module.ts | 2 ++ 7 files changed, 52 insertions(+), 18 deletions(-) diff --git a/packages/twenty-front/src/modules/auth/sign-in-up/hooks/useSignInUpForm.ts b/packages/twenty-front/src/modules/auth/sign-in-up/hooks/useSignInUpForm.ts index cf71ceee0c..b0c2b5d20f 100644 --- a/packages/twenty-front/src/modules/auth/sign-in-up/hooks/useSignInUpForm.ts +++ b/packages/twenty-front/src/modules/auth/sign-in-up/hooks/useSignInUpForm.ts @@ -64,5 +64,5 @@ export const useSignInUpForm = () => { form.setValue('password', 'tim@apple.dev'); } }, [form, isDeveloperDefaultSignInPrefilled, prefilledEmail]); - return { form: form }; + return { form }; }; diff --git a/packages/twenty-server/src/engine/core-modules/actor/services/__tests__/created-by-from-auth-context.service.spec.ts b/packages/twenty-server/src/engine/core-modules/actor/services/__tests__/created-by-from-auth-context.service.spec.ts index 16dfa9a136..b532f78729 100644 --- a/packages/twenty-server/src/engine/core-modules/actor/services/__tests__/created-by-from-auth-context.service.spec.ts +++ b/packages/twenty-server/src/engine/core-modules/actor/services/__tests__/created-by-from-auth-context.service.spec.ts @@ -106,13 +106,25 @@ describe('CreatedByFromAuthContextService', () => { const authContext = { workspaceMemberId: '20202020-0b5c-4178-bed7-d371f6411eaa', user: { - firstName: 'John', - lastName: 'Doe', + firstName: '', + lastName: '', id: '20202020-9aae-49a8-bafc-ac44bae62d6d', }, workspace: { id: '20202020-bdec-497f-847a-1bb334fefe58' }, } as const satisfies TestingAuthContext; + const mockedWorkspaceMember = { + id: '20202020-0b5c-4178-bed7-d371f6411eaa', + name: { + firstName: 'John', + lastName: 'Doe', + }, + } as const satisfies Partial; + + mockWorkspaceMemberRepository.findOneOrFail.mockResolvedValueOnce( + mockedWorkspaceMember, + ); + const result = await service.injectCreatedBy( [{}], 'person', @@ -123,7 +135,7 @@ describe('CreatedByFromAuthContextService', () => { { createdBy: { context: {}, - name: fromFullNameMetadataToName(authContext.user), + name: fromFullNameMetadataToName(mockedWorkspaceMember.name), workspaceMemberId: authContext.workspaceMemberId, source: FieldActorSource.MANUAL, }, diff --git a/packages/twenty-server/src/engine/core-modules/actor/services/created-by-from-auth-context.service.ts b/packages/twenty-server/src/engine/core-modules/actor/services/created-by-from-auth-context.service.ts index e7e23715fc..5da432f910 100644 --- a/packages/twenty-server/src/engine/core-modules/actor/services/created-by-from-auth-context.service.ts +++ b/packages/twenty-server/src/engine/core-modules/actor/services/created-by-from-auth-context.service.ts @@ -102,24 +102,11 @@ export class CreatedByFromAuthContextService { private async buildCreatedBy( authContext: AuthContext, ): Promise { - const { workspace, workspaceMemberId, user, apiKey } = authContext; + const { workspace, user, apiKey } = authContext; assertIsDefinedOrThrow(workspace, WorkspaceNotFoundDefaultError); - // TODO: remove that code once we have the workspace member id in all tokens - if (isDefined(workspaceMemberId) && isDefined(user)) { - return buildCreatedByFromFullNameMetadata({ - fullNameMetadata: { - firstName: user.firstName, - lastName: user.lastName, - }, - workspaceMemberId, - }); - } - if (isDefined(user)) { - this.logger.warn("User doesn't have a workspace member id in the token"); - const workspaceMemberRepository = await this.twentyORMGlobalManager.getRepositoryForWorkspace( workspace.id, diff --git a/packages/twenty-server/src/engine/core-modules/user-workspace/user-workspace.module.ts b/packages/twenty-server/src/engine/core-modules/user-workspace/user-workspace.module.ts index dc137c3dc6..47aecbc226 100644 --- a/packages/twenty-server/src/engine/core-modules/user-workspace/user-workspace.module.ts +++ b/packages/twenty-server/src/engine/core-modules/user-workspace/user-workspace.module.ts @@ -9,6 +9,7 @@ import { TokenModule } from 'src/engine/core-modules/auth/token/token.module'; import { WorkspaceDomainsModule } from 'src/engine/core-modules/domain/workspace-domains/workspace-domains.module'; import { FileUploadModule } from 'src/engine/core-modules/file/file-upload/file-upload.module'; import { FileModule } from 'src/engine/core-modules/file/file.module'; +import { OnboardingModule } from 'src/engine/core-modules/onboarding/onboarding.module'; import { UserWorkspaceEntity } from 'src/engine/core-modules/user-workspace/user-workspace.entity'; import { UserWorkspaceService } from 'src/engine/core-modules/user-workspace/user-workspace.service'; import { UserEntity } from 'src/engine/core-modules/user/user.entity'; @@ -43,6 +44,7 @@ import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/works FileUploadModule, FileModule, TokenModule, + OnboardingModule, ], services: [UserWorkspaceService], }), diff --git a/packages/twenty-server/src/engine/core-modules/user-workspace/user-workspace.service.spec.ts b/packages/twenty-server/src/engine/core-modules/user-workspace/user-workspace.service.spec.ts index 8f58d09b8a..b42450254b 100644 --- a/packages/twenty-server/src/engine/core-modules/user-workspace/user-workspace.service.spec.ts +++ b/packages/twenty-server/src/engine/core-modules/user-workspace/user-workspace.service.spec.ts @@ -16,6 +16,7 @@ import { type SignedFilesResult, } from 'src/engine/core-modules/file/file-upload/services/file-upload.service'; import { FileService } from 'src/engine/core-modules/file/services/file.service'; +import { OnboardingService } from 'src/engine/core-modules/onboarding/onboarding.service'; import { UserWorkspaceEntity } from 'src/engine/core-modules/user-workspace/user-workspace.entity'; import { UserWorkspaceService } from 'src/engine/core-modules/user-workspace/user-workspace.service'; import { UserEntity } from 'src/engine/core-modules/user/user.entity'; @@ -38,6 +39,7 @@ describe('UserWorkspaceService', () => { let userRoleService: UserRoleService; let fileService: FileService; let fileUploadService: FileUploadService; + let onboardingService: OnboardingService; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ @@ -133,6 +135,12 @@ describe('UserWorkspaceService', () => { copyFileFromWorkspaceToWorkspace: jest.fn(), }, }, + { + provide: OnboardingService, + useValue: { + setOnboardingCreateProfilePending: jest.fn(), + }, + }, ], }).compile(); @@ -153,6 +161,7 @@ describe('UserWorkspaceService', () => { ); userRoleService = module.get(UserRoleService); fileUploadService = module.get(FileUploadService); + onboardingService = module.get(OnboardingService); }); it('should be defined', () => { @@ -444,6 +453,17 @@ describe('UserWorkspaceService', () => { expect( workspaceInvitationService.invalidateWorkspaceInvitation, ).toHaveBeenCalledWith(workspace.id, user.email, undefined); + + expect( + onboardingService.setOnboardingCreateProfilePending, + ).toHaveBeenCalledWith( + { + userId: user.id, + workspaceId: workspace.id, + value: true, + }, + undefined, + ); }); it('should not add user to workspace if already in workspace', async () => { diff --git a/packages/twenty-server/src/engine/core-modules/user-workspace/user-workspace.service.ts b/packages/twenty-server/src/engine/core-modules/user-workspace/user-workspace.service.ts index 6e1dabc845..f23b8320cf 100644 --- a/packages/twenty-server/src/engine/core-modules/user-workspace/user-workspace.service.ts +++ b/packages/twenty-server/src/engine/core-modules/user-workspace/user-workspace.service.ts @@ -19,6 +19,7 @@ import { LoginTokenService } from 'src/engine/core-modules/auth/token/services/l import { WorkspaceDomainsService } from 'src/engine/core-modules/domain/workspace-domains/services/workspace-domains.service'; import { FileUploadService } from 'src/engine/core-modules/file/file-upload/services/file-upload.service'; import { FileService } from 'src/engine/core-modules/file/services/file.service'; +import { OnboardingService } from 'src/engine/core-modules/onboarding/onboarding.service'; import { UserWorkspaceEntity } from 'src/engine/core-modules/user-workspace/user-workspace.entity'; import { UserEntity } from 'src/engine/core-modules/user/user.entity'; import { WorkspaceInvitationService } from 'src/engine/core-modules/workspace-invitation/services/workspace-invitation.service'; @@ -53,6 +54,7 @@ export class UserWorkspaceService extends TypeOrmQueryService