Fix authUser decorator usage (#12697)
Solving issue: we don't have `user.firstName` and `user.lastName` set when signin with e-mail/password. CreateBy, invitation emails and validation domain email need those info ## Before ## ExecutedBy <img width="511" alt="image" src="https://github.com/user-attachments/assets/b85bbda5-f26b-4137-a875-0ef926a1eec4" /> ## Invitation email <img width="764" alt="image" src="https://github.com/user-attachments/assets/107c71bf-a6b2-4291-a31b-6ce48b11dd77" /> ### Validate domain email <img width="829" alt="image" src="https://github.com/user-attachments/assets/213ff7c5-f86d-476f-8f4d-74299d7eb13d" /> ## After ## ExecutedBy <img width="500" alt="image" src="https://github.com/user-attachments/assets/b4125e84-b355-4280-8611-b4e36e6033c7" /> ## Invitation email <img width="754" alt="image" src="https://github.com/user-attachments/assets/952fe5bf-f4da-4fef-b765-fc220255dedf" /> ### Validate domain email <img width="709" alt="image" src="https://github.com/user-attachments/assets/6950097c-51ae-469b-a7cf-f561650ee86e" />
This commit is contained in:
@ -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<ApprovedAccessDomain> {
|
||||
const workspaceMemberRepository =
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<WorkspaceMemberWorkspaceEntity>(
|
||||
currentWorkspace.id,
|
||||
'workspaceMember',
|
||||
);
|
||||
|
||||
const workspaceMember = await workspaceMemberRepository.findOneOrFail({
|
||||
where: {
|
||||
userId: currentUser.id,
|
||||
},
|
||||
});
|
||||
|
||||
return this.approvedAccessDomainService.createApprovedAccessDomain(
|
||||
domain,
|
||||
currentWorkspace,
|
||||
currentUser,
|
||||
workspaceMember,
|
||||
email,
|
||||
);
|
||||
}
|
||||
|
||||
@ -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<ApprovedAccessDomainEntity> {
|
||||
if (!isWorkDomain(domain)) {
|
||||
@ -170,7 +170,7 @@ export class ApprovedAccessDomainService {
|
||||
);
|
||||
|
||||
await this.sendApprovedAccessDomainValidationEmail(
|
||||
fromUser,
|
||||
fromWorkspaceMember,
|
||||
emailToValidateDomain,
|
||||
inWorkspace,
|
||||
approvedAccessDomain,
|
||||
|
||||
@ -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',
|
||||
|
||||
@ -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<WorkspaceMemberWorkspaceEntity>(
|
||||
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,
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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<SendInvitationsOutput> {
|
||||
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,
|
||||
|
||||
@ -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<WorkspaceMemberWorkspaceEntity>(
|
||||
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<WorkspaceMemberWorkspaceEntity>(
|
||||
workspace.id,
|
||||
'workspaceMember',
|
||||
);
|
||||
|
||||
const workspaceMember = await workspaceMemberRepository.findOneOrFail({
|
||||
where: {
|
||||
userId: user.id,
|
||||
},
|
||||
});
|
||||
|
||||
return await this.workspaceInvitationService.sendInvitations(
|
||||
sendInviteLinkInput.emails,
|
||||
{ ...workspace, logo: workspaceLogoWithToken },
|
||||
user,
|
||||
workspaceMember,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user