[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
This commit is contained in:
@@ -64,5 +64,5 @@ export const useSignInUpForm = () => {
|
||||
form.setValue('password', 'tim@apple.dev');
|
||||
}
|
||||
}, [form, isDeveloperDefaultSignInPrefilled, prefilledEmail]);
|
||||
return { form: form };
|
||||
return { form };
|
||||
};
|
||||
|
||||
+15
-3
@@ -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<WorkspaceMemberWorkspaceEntity>;
|
||||
|
||||
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,
|
||||
},
|
||||
|
||||
+1
-14
@@ -102,24 +102,11 @@ export class CreatedByFromAuthContextService {
|
||||
private async buildCreatedBy(
|
||||
authContext: AuthContext,
|
||||
): Promise<ActorMetadata> {
|
||||
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<WorkspaceMemberWorkspaceEntity>(
|
||||
workspace.id,
|
||||
|
||||
@@ -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],
|
||||
}),
|
||||
|
||||
+20
@@ -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>(UserRoleService);
|
||||
fileUploadService = module.get<FileUploadService>(FileUploadService);
|
||||
onboardingService = module.get<OnboardingService>(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 () => {
|
||||
|
||||
+11
@@ -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<UserWorkspaceEntit
|
||||
private readonly userRoleService: UserRoleService,
|
||||
private readonly fileUploadService: FileUploadService,
|
||||
private readonly fileService: FileService,
|
||||
private readonly onboardingService: OnboardingService,
|
||||
) {
|
||||
super(userWorkspaceRepository);
|
||||
}
|
||||
@@ -173,6 +175,15 @@ export class UserWorkspaceService extends TypeOrmQueryService<UserWorkspaceEntit
|
||||
user.email,
|
||||
queryRunner,
|
||||
);
|
||||
|
||||
await this.onboardingService.setOnboardingCreateProfilePending(
|
||||
{
|
||||
userId: user.id,
|
||||
workspaceId: workspace.id,
|
||||
value: true,
|
||||
},
|
||||
queryRunner,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
@@ -4,6 +4,7 @@ import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module';
|
||||
import { UserWorkspaceEntity } from 'src/engine/core-modules/user-workspace/user-workspace.entity';
|
||||
import { UserWorkspaceModule } from 'src/engine/core-modules/user-workspace/user-workspace.module';
|
||||
import { UserModule } from 'src/engine/core-modules/user/user.module';
|
||||
import { PermissionsModule } from 'src/engine/metadata-modules/permissions/permissions.module';
|
||||
import { WorkspaceMemberCreateManyPreQueryHook } from 'src/modules/workspace-member/query-hooks/workspace-member-create-many.pre-query.hook';
|
||||
import { WorkspaceMemberCreateOnePreQueryHook } from 'src/modules/workspace-member/query-hooks/workspace-member-create-one.pre-query.hook';
|
||||
@@ -38,6 +39,7 @@ import { WorkspaceMemberUpdateOnePreQueryHook } from 'src/modules/workspace-memb
|
||||
PermissionsModule,
|
||||
UserWorkspaceModule,
|
||||
TypeOrmModule.forFeature([UserWorkspaceEntity]),
|
||||
UserModule,
|
||||
],
|
||||
})
|
||||
export class WorkspaceMemberQueryHookModule {}
|
||||
|
||||
Reference in New Issue
Block a user