diff --git a/packages/twenty-server/src/engine/core-modules/approved-access-domain/approved-access-domain.resolver.ts b/packages/twenty-server/src/engine/core-modules/approved-access-domain/approved-access-domain.resolver.ts index 552c86b8e..feb2298d7 100644 --- a/packages/twenty-server/src/engine/core-modules/approved-access-domain/approved-access-domain.resolver.ts +++ b/packages/twenty-server/src/engine/core-modules/approved-access-domain/approved-access-domain.resolver.ts @@ -9,15 +9,18 @@ import { ValidateApprovedAccessDomainInput } from 'src/engine/core-modules/appro import { ApprovedAccessDomainService } from 'src/engine/core-modules/approved-access-domain/services/approved-access-domain.service'; import { User } from 'src/engine/core-modules/user/user.entity'; import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; -import { AuthUser } from 'src/engine/decorators/auth/auth-user.decorator'; import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator'; import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard'; +import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity'; +import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager'; +import { AuthUser } from 'src/engine/decorators/auth/auth-user.decorator'; @UseGuards(WorkspaceAuthGuard) @UseFilters(ApprovedAccessDomainExceptionFilter) @Resolver() export class ApprovedAccessDomainResolver { constructor( + private readonly twentyORMGlobalManager: TwentyORMGlobalManager, private readonly approvedAccessDomainService: ApprovedAccessDomainService, ) {} @@ -27,10 +30,22 @@ export class ApprovedAccessDomainResolver { @AuthWorkspace() currentWorkspace: Workspace, @AuthUser() currentUser: User, ): Promise { + const workspaceMemberRepository = + await this.twentyORMGlobalManager.getRepositoryForWorkspace( + currentWorkspace.id, + 'workspaceMember', + ); + + const workspaceMember = await workspaceMemberRepository.findOneOrFail({ + where: { + userId: currentUser.id, + }, + }); + return this.approvedAccessDomainService.createApprovedAccessDomain( domain, currentWorkspace, - currentUser, + workspaceMember, email, ); } diff --git a/packages/twenty-server/src/engine/core-modules/approved-access-domain/services/approved-access-domain.service.ts b/packages/twenty-server/src/engine/core-modules/approved-access-domain/services/approved-access-domain.service.ts index 995faa6c7..499b395be 100644 --- a/packages/twenty-server/src/engine/core-modules/approved-access-domain/services/approved-access-domain.service.ts +++ b/packages/twenty-server/src/engine/core-modules/approved-access-domain/services/approved-access-domain.service.ts @@ -17,9 +17,9 @@ import { approvedAccessDomainValidator } from 'src/engine/core-modules/approved- import { DomainManagerService } from 'src/engine/core-modules/domain-manager/services/domain-manager.service'; import { EmailService } from 'src/engine/core-modules/email/email.service'; import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service'; -import { User } from 'src/engine/core-modules/user/user.entity'; import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; import { isWorkDomain } from 'src/utils/is-work-email'; +import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity'; @Injectable() // eslint-disable-next-line @nx/workspace-inject-workspace-repository @@ -33,7 +33,7 @@ export class ApprovedAccessDomainService { ) {} async sendApprovedAccessDomainValidationEmail( - sender: User, + sender: WorkspaceMemberWorkspaceEntity, to: string, workspace: Workspace, approvedAccessDomain: ApprovedAccessDomainEntity, @@ -66,9 +66,9 @@ export class ApprovedAccessDomainService { workspace: { name: workspace.displayName, logo: workspace.logo }, domain: approvedAccessDomain.domain, sender: { - email: sender.email, - firstName: sender.firstName, - lastName: sender.lastName, + email: sender.userEmail, + firstName: sender.name.firstName, + lastName: sender.name.lastName, }, serverUrl: this.twentyConfigService.get('SERVER_URL'), locale: 'en' as keyof typeof APP_LOCALES, @@ -79,7 +79,7 @@ export class ApprovedAccessDomainService { }); await this.emailService.send({ - from: `${sender.firstName} ${sender.lastName} (via Twenty) <${this.twentyConfigService.get('EMAIL_FROM_ADDRESS')}>`, + from: `${sender.name.firstName} ${sender.name.lastName} (via Twenty) <${this.twentyConfigService.get('EMAIL_FROM_ADDRESS')}>`, to, subject: 'Approve your access domain', text, @@ -140,7 +140,7 @@ export class ApprovedAccessDomainService { async createApprovedAccessDomain( domain: string, inWorkspace: Workspace, - fromUser: User, + fromWorkspaceMember: WorkspaceMemberWorkspaceEntity, emailToValidateDomain: string, ): Promise { if (!isWorkDomain(domain)) { @@ -170,7 +170,7 @@ export class ApprovedAccessDomainService { ); await this.sendApprovedAccessDomainValidationEmail( - fromUser, + fromWorkspaceMember, emailToValidateDomain, inWorkspace, approvedAccessDomain, diff --git a/packages/twenty-server/src/engine/core-modules/approved-access-domain/services/approved-access-domain.spec.ts b/packages/twenty-server/src/engine/core-modules/approved-access-domain/services/approved-access-domain.spec.ts index e18db65b5..6d6acdfc6 100644 --- a/packages/twenty-server/src/engine/core-modules/approved-access-domain/services/approved-access-domain.spec.ts +++ b/packages/twenty-server/src/engine/core-modules/approved-access-domain/services/approved-access-domain.spec.ts @@ -11,8 +11,8 @@ import { import { DomainManagerService } from 'src/engine/core-modules/domain-manager/services/domain-manager.service'; import { EmailService } from 'src/engine/core-modules/email/email.service'; import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service'; -import { User } from 'src/engine/core-modules/user/user.entity'; import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; +import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity'; import { ApprovedAccessDomainService } from './approved-access-domain.service'; @@ -78,9 +78,8 @@ describe('ApprovedAccessDomainService', () => { isCustomDomainEnabled: false, } as Workspace; const fromUser = { - email: 'user@custom-domain.com', - isEmailVerified: true, - } as User; + userEmail: 'user@custom-domain.com', + } as WorkspaceMemberWorkspaceEntity; const expectedApprovedAccessDomain = { workspaceId: 'workspace-id', @@ -118,7 +117,9 @@ describe('ApprovedAccessDomainService', () => { service.createApprovedAccessDomain( 'gmail.com', { id: 'workspace-id' } as Workspace, - { email: 'user@gmail.com', isEmailVerified: true } as User, + { + userEmail: 'user@gmail.com', + } as WorkspaceMemberWorkspaceEntity, 'user@gmail.com', ), ).rejects.toThrowError( @@ -184,7 +185,7 @@ describe('ApprovedAccessDomainService', () => { describe('sendApprovedAccessDomainValidationEmail', () => { it('should throw an exception if the approved access domain is already validated', async () => { const approvedAccessDomainId = 'approved-access-domain-id'; - const sender = {} as User; + const sender = {} as WorkspaceMemberWorkspaceEntity; const workspace = {} as Workspace; const email = 'validator@example.com'; @@ -214,7 +215,7 @@ describe('ApprovedAccessDomainService', () => { it('should throw an exception if the email does not match the approved access domain', async () => { const approvedAccessDomainId = 'approved-access-domain-id'; - const sender = {} as User; + const sender = {} as WorkspaceMemberWorkspaceEntity; const workspace = {} as Workspace; const email = 'validator@different.com'; const approvedAccessDomain = { @@ -244,10 +245,9 @@ describe('ApprovedAccessDomainService', () => { it('should send a validation email if all conditions are met', async () => { const sender = { - email: 'sender@example.com', - firstName: 'John', - lastName: 'Doe', - } as User; + userEmail: 'sender@example.com', + name: { firstName: 'John', lastName: 'Doe' }, + } as WorkspaceMemberWorkspaceEntity; const workspace = { displayName: 'Test Workspace', logo: '/logo.png', diff --git a/packages/twenty-server/src/engine/core-modules/workflow/resolvers/workflow-trigger.resolver.ts b/packages/twenty-server/src/engine/core-modules/workflow/resolvers/workflow-trigger.resolver.ts index f988a08ba..50256d110 100644 --- a/packages/twenty-server/src/engine/core-modules/workflow/resolvers/workflow-trigger.resolver.ts +++ b/packages/twenty-server/src/engine/core-modules/workflow/resolvers/workflow-trigger.resolver.ts @@ -6,14 +6,17 @@ import { User } from 'src/engine/core-modules/user/user.entity'; import { RunWorkflowVersionInput } from 'src/engine/core-modules/workflow/dtos/run-workflow-version-input.dto'; import { WorkflowRunDTO } from 'src/engine/core-modules/workflow/dtos/workflow-run.dto'; import { WorkflowTriggerGraphqlApiExceptionFilter } from 'src/engine/core-modules/workflow/filters/workflow-trigger-graphql-api-exception.filter'; -import { AuthUser } from 'src/engine/decorators/auth/auth-user.decorator'; -import { AuthWorkspaceMemberId } from 'src/engine/decorators/auth/auth-workspace-member-id.decorator'; import { SettingsPermissionsGuard } from 'src/engine/guards/settings-permissions.guard'; import { UserAuthGuard } from 'src/engine/guards/user-auth.guard'; import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard'; import { SettingPermissionType } from 'src/engine/metadata-modules/permissions/constants/setting-permission-type.constants'; import { PermissionsGraphqlApiExceptionFilter } from 'src/engine/metadata-modules/permissions/utils/permissions-graphql-api-exception.filter'; import { WorkflowTriggerWorkspaceService } from 'src/modules/workflow/workflow-trigger/workspace-services/workflow-trigger.workspace-service'; +import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity'; +import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager'; +import { AuthUser } from 'src/engine/decorators/auth/auth-user.decorator'; +import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; +import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator'; @Resolver() @UseGuards( @@ -27,6 +30,7 @@ import { WorkflowTriggerWorkspaceService } from 'src/modules/workflow/workflow-t ) export class WorkflowTriggerResolver { constructor( + private readonly twentyORMGlobalManager: TwentyORMGlobalManager, private readonly workflowTriggerWorkspaceService: WorkflowTriggerWorkspaceService, ) {} @@ -50,19 +54,31 @@ export class WorkflowTriggerResolver { @Mutation(() => WorkflowRunDTO) async runWorkflowVersion( - @AuthWorkspaceMemberId() workspaceMemberId: string, @AuthUser() user: User, + @AuthWorkspace() workspace: Workspace, @Args('input') { workflowVersionId, payload }: RunWorkflowVersionInput, ) { + const workspaceMemberRepository = + await this.twentyORMGlobalManager.getRepositoryForWorkspace( + workspace.id, + 'workspaceMember', + ); + + const workspaceMember = await workspaceMemberRepository.findOneOrFail({ + where: { + userId: user.id, + }, + }); + return await this.workflowTriggerWorkspaceService.runWorkflowVersion({ workflowVersionId, payload: payload ?? {}, createdBy: buildCreatedByFromFullNameMetadata({ fullNameMetadata: { - firstName: user.firstName, - lastName: user.lastName, + firstName: workspaceMember.name.firstName, + lastName: workspaceMember.name.lastName, }, - workspaceMemberId: workspaceMemberId, + workspaceMemberId: workspaceMember.id, }), }); } diff --git a/packages/twenty-server/src/engine/core-modules/workspace-invitation/services/workspace-invitation.service.spec.ts b/packages/twenty-server/src/engine/core-modules/workspace-invitation/services/workspace-invitation.service.spec.ts index ef1dcd315..78a4d17fd 100644 --- a/packages/twenty-server/src/engine/core-modules/workspace-invitation/services/workspace-invitation.service.spec.ts +++ b/packages/twenty-server/src/engine/core-modules/workspace-invitation/services/workspace-invitation.service.spec.ts @@ -12,10 +12,10 @@ import { EmailService } from 'src/engine/core-modules/email/email.service'; import { OnboardingService } from 'src/engine/core-modules/onboarding/onboarding.service'; import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service'; import { UserWorkspace } from 'src/engine/core-modules/user-workspace/user-workspace.entity'; -import { User } from 'src/engine/core-modules/user/user.entity'; import { WorkspaceInvitationException } from 'src/engine/core-modules/workspace-invitation/workspace-invitation.exception'; import { WorkspaceService } from 'src/engine/core-modules/workspace/services/workspace.service'; import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; +import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity'; import { WorkspaceInvitationService } from './workspace-invitation.service'; @@ -148,7 +148,10 @@ describe('WorkspaceInvitationService', () => { inviteHash: 'invite-hash', displayName: 'Test Workspace', } as Workspace; - const sender = { email: 'sender@example.com', firstName: 'Sender' }; + const sender = { + userEmail: 'sender@example.com', + name: { firstName: 'Sender' }, + }; jest.spyOn(service, 'createWorkspaceInvitation').mockResolvedValue({ context: { email: 'test@example.com' }, @@ -166,7 +169,7 @@ describe('WorkspaceInvitationService', () => { const result = await service.sendInvitations( emails, workspace, - sender as User, + sender as WorkspaceMemberWorkspaceEntity, ); expect(result.success).toBe(true); diff --git a/packages/twenty-server/src/engine/core-modules/workspace-invitation/services/workspace-invitation.service.ts b/packages/twenty-server/src/engine/core-modules/workspace-invitation/services/workspace-invitation.service.ts index 2215e03da..e3318c907 100644 --- a/packages/twenty-server/src/engine/core-modules/workspace-invitation/services/workspace-invitation.service.ts +++ b/packages/twenty-server/src/engine/core-modules/workspace-invitation/services/workspace-invitation.service.ts @@ -25,7 +25,6 @@ import { EmailService } from 'src/engine/core-modules/email/email.service'; import { OnboardingService } from 'src/engine/core-modules/onboarding/onboarding.service'; import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service'; import { UserWorkspace } from 'src/engine/core-modules/user-workspace/user-workspace.entity'; -import { User } from 'src/engine/core-modules/user/user.entity'; import { SendInvitationsOutput } from 'src/engine/core-modules/workspace-invitation/dtos/send-invitations.output'; import { castAppTokenToWorkspaceInvitationUtil } from 'src/engine/core-modules/workspace-invitation/utils/cast-app-token-to-workspace-invitation.util'; import { @@ -33,6 +32,7 @@ import { WorkspaceInvitationExceptionCode, } from 'src/engine/core-modules/workspace-invitation/workspace-invitation.exception'; import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; +import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity'; @Injectable() // eslint-disable-next-line @nx/workspace-inject-workspace-repository @@ -228,7 +228,7 @@ export class WorkspaceInvitationService { async resendWorkspaceInvitation( appTokenId: string, workspace: Workspace, - sender: User, + sender: WorkspaceMemberWorkspaceEntity, ) { const appToken = await this.appTokenRepository.findOne({ where: { @@ -253,7 +253,7 @@ export class WorkspaceInvitationService { async sendInvitations( emails: string[], workspace: Workspace, - sender: User, + sender: WorkspaceMemberWorkspaceEntity, usePersonalInvitation = true, ): Promise { if (!workspace?.inviteHash) { @@ -306,14 +306,13 @@ export class WorkspaceInvitationService { : {}, }); - // Todo: sender name and locale should come from workspace member not user! const emailData = { link: link.toString(), workspace: { name: workspace.displayName, logo: workspace.logo }, sender: { - email: sender.email, - firstName: sender.firstName, - lastName: sender.lastName, + email: sender.userEmail, + firstName: sender.name.firstName, + lastName: sender.name.lastName, }, serverUrl: this.twentyConfigService.get('SERVER_URL'), locale: sender.locale as keyof typeof APP_LOCALES, @@ -328,7 +327,7 @@ export class WorkspaceInvitationService { i18n.activate(sender.locale); await this.emailService.send({ - from: `${sender.firstName} ${sender.lastName} (via Twenty) <${this.twentyConfigService.get('EMAIL_FROM_ADDRESS')}>`, + from: `${sender.name.firstName} ${sender.name.lastName} (via Twenty) <${this.twentyConfigService.get('EMAIL_FROM_ADDRESS')}>`, to: invitation.value.email, subject: t`Join your team on Twenty`, text, diff --git a/packages/twenty-server/src/engine/core-modules/workspace-invitation/workspace-invitation.resolver.ts b/packages/twenty-server/src/engine/core-modules/workspace-invitation/workspace-invitation.resolver.ts index 2da041318..3a4c3dfcd 100644 --- a/packages/twenty-server/src/engine/core-modules/workspace-invitation/workspace-invitation.resolver.ts +++ b/packages/twenty-server/src/engine/core-modules/workspace-invitation/workspace-invitation.resolver.ts @@ -14,6 +14,8 @@ import { UserAuthGuard } from 'src/engine/guards/user-auth.guard'; import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard'; import { SettingPermissionType } from 'src/engine/metadata-modules/permissions/constants/setting-permission-type.constants'; import { PermissionsGraphqlApiExceptionFilter } from 'src/engine/metadata-modules/permissions/utils/permissions-graphql-api-exception.filter'; +import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity'; +import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager'; import { SendInvitationsInput } from './dtos/send-invitations.input'; @@ -25,6 +27,7 @@ import { SendInvitationsInput } from './dtos/send-invitations.input'; @Resolver() export class WorkspaceInvitationResolver { constructor( + private readonly twentyORMGlobalManager: TwentyORMGlobalManager, private readonly workspaceInvitationService: WorkspaceInvitationService, private readonly fileService: FileService, ) {} @@ -47,10 +50,22 @@ export class WorkspaceInvitationResolver { @AuthWorkspace() workspace: Workspace, @AuthUser() user: User, ) { + const workspaceMemberRepository = + await this.twentyORMGlobalManager.getRepositoryForWorkspace( + workspace.id, + 'workspaceMember', + ); + + const workspaceMember = await workspaceMemberRepository.findOneOrFail({ + where: { + userId: user.id, + }, + }); + return this.workspaceInvitationService.resendWorkspaceInvitation( appTokenId, workspace, - user, + workspaceMember, ); } @@ -75,10 +90,22 @@ export class WorkspaceInvitationResolver { }); } + const workspaceMemberRepository = + await this.twentyORMGlobalManager.getRepositoryForWorkspace( + workspace.id, + 'workspaceMember', + ); + + const workspaceMember = await workspaceMemberRepository.findOneOrFail({ + where: { + userId: user.id, + }, + }); + return await this.workspaceInvitationService.sendInvitations( sendInviteLinkInput.emails, { ...workspace, logo: workspaceLogoWithToken }, - user, + workspaceMember, ); } }