-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* add domain logic for invitation management * add necessary logic to user access module (generating users) * add endpoint tests for invitation management * fix endpoint authorization requirement * refactoring (naming, namespaces, ...)
- Loading branch information
1 parent
fa1ad47
commit dd4c976
Showing
47 changed files
with
2,213 additions
and
28 deletions.
There are no files selected for viewing
5 changes: 5 additions & 0 deletions
5
src/Common/TeamUp.Common.Contracts/Errors/EventualConsistencyError.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
using RailwayResult; | ||
|
||
namespace TeamUp.Common.Contracts.Errors; | ||
|
||
public sealed record EventualConsistencyError(string Key, string Message) : Error(Key, Message); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 2 additions & 0 deletions
2
...odules/Notifications/TeamUp.Notifications.Contracts/TeamUp.Notifications.Contracts.csproj
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,7 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<ItemGroup> | ||
<ProjectReference Include="..\..\..\Common\TeamUp.Common.Contracts\TeamUp.Common.Contracts.csproj" /> | ||
</ItemGroup> | ||
|
||
</Project> |
27 changes: 27 additions & 0 deletions
27
...anagement/TeamUp.TeamManagement.Application/Invitations/AcceptInvitationCommandHandler.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
using RailwayResult; | ||
|
||
using TeamUp.Common.Application; | ||
using TeamUp.TeamManagement.Contracts; | ||
using TeamUp.TeamManagement.Contracts.Invitations.AcceptInvitation; | ||
using TeamUp.TeamManagement.Domain.Aggregates.Invitations; | ||
|
||
namespace TeamUp.TeamManagement.Application.Invitations; | ||
|
||
internal sealed class AcceptInvitationCommandHandler : ICommandHandler<AcceptInvitationCommand> | ||
{ | ||
private readonly IInvitationDomainService _invitationDomainService; | ||
private readonly IUnitOfWork<TeamManagementModuleId> _unitOfWork; | ||
|
||
public AcceptInvitationCommandHandler(IInvitationDomainService invitationDomainService, IUnitOfWork<TeamManagementModuleId> unitOfWork) | ||
{ | ||
_invitationDomainService = invitationDomainService; | ||
_unitOfWork = unitOfWork; | ||
} | ||
|
||
public async Task<Result> Handle(AcceptInvitationCommand command, CancellationToken ct) | ||
{ | ||
return await _invitationDomainService | ||
.AcceptInvitationAsync(command.InitiatorId, command.InvitationId, ct) | ||
.TapAsync(() => _unitOfWork.SaveChangesAsync(ct)); | ||
} | ||
} |
70 changes: 70 additions & 0 deletions
70
...amUp.TeamManagement.Application/Invitations/CreateInvitationRequestCreatedEventHandler.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
using Microsoft.Extensions.Logging; | ||
|
||
using RailwayResult; | ||
using RailwayResult.Errors; | ||
using RailwayResult.FunctionalExtensions; | ||
|
||
using TeamUp.Common.Application; | ||
using TeamUp.Common.Contracts.Errors; | ||
using TeamUp.Common.Domain; | ||
using TeamUp.Notifications.Contracts; | ||
using TeamUp.TeamManagement.Contracts; | ||
using TeamUp.TeamManagement.Domain.Aggregates.Invitations; | ||
using TeamUp.TeamManagement.Domain.Aggregates.Invitations.IntegrationEvents; | ||
using TeamUp.TeamManagement.Domain.Aggregates.Users; | ||
|
||
namespace TeamUp.TeamManagement.Application.Invitations; | ||
|
||
internal sealed class CreateInvitationRequestCreatedEventHandler : IIntegrationEventHandler<CreateInvitationRequestCreatedIntegrationEvent> | ||
{ | ||
private readonly IUserRepository _userRepository; | ||
private readonly ILogger<CreateInvitationRequestCreatedEventHandler> _logger; | ||
private readonly InvitationFactory _invitationFactory; | ||
private readonly IUnitOfWork<TeamManagementModuleId> _unitOfWork; | ||
private readonly IIntegrationEventPublisher<TeamManagementModuleId> _publisher; | ||
|
||
public CreateInvitationRequestCreatedEventHandler(IUserRepository userRepository, ILogger<CreateInvitationRequestCreatedEventHandler> logger, InvitationFactory invitationFactory, IUnitOfWork<TeamManagementModuleId> unitOfWork, IIntegrationEventPublisher<TeamManagementModuleId> publisher) | ||
{ | ||
_userRepository = userRepository; | ||
_logger = logger; | ||
_invitationFactory = invitationFactory; | ||
_unitOfWork = unitOfWork; | ||
_publisher = publisher; | ||
} | ||
|
||
public async Task<Result> Handle(CreateInvitationRequestCreatedIntegrationEvent integrationEvent, CancellationToken ct) | ||
{ | ||
var user = await _userRepository.GetUserByIdAsync(integrationEvent.UserId, ct); | ||
if (user is null) | ||
{ | ||
_logger.LogWarning("User {userId} is not yet created for inviting to team {teamId}.", integrationEvent.UserId, integrationEvent.TeamId); | ||
return new EventualConsistencyError("TeamManagement.Users.NotFound", "User not found when inviting."); | ||
} | ||
|
||
var result = await _invitationFactory | ||
.CreateAndAddInvitationAsync(user.Id, integrationEvent.TeamId, ct) | ||
.Tap(invitation => | ||
{ | ||
var message = new EmailCreatedIntegrationEvent | ||
{ | ||
Email = user.Email, | ||
Subject = "You were invited to team!", | ||
Message = $"Accept invitation link for {invitation.Id}." | ||
}; | ||
|
||
_publisher.Publish(message); | ||
}) | ||
.ThenAsync(_ => _unitOfWork.SaveChangesAsync(ct)); | ||
|
||
if (result.IsFailure) | ||
{ | ||
if (result.Error is ConflictError error) | ||
{ | ||
_logger.LogWarning("Invitation of user {userId} to team {teamId} probably already exist. {error}", integrationEvent.UserId, integrationEvent.TeamId, error); | ||
return Result.Success; | ||
} | ||
} | ||
|
||
return result; | ||
} | ||
} |
35 changes: 35 additions & 0 deletions
35
...mManagement/TeamUp.TeamManagement.Application/Invitations/GetMyInvitationsQueryHandler.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
using Microsoft.EntityFrameworkCore; | ||
|
||
using RailwayResult; | ||
|
||
using TeamUp.Common.Application; | ||
using TeamUp.Common.Contracts; | ||
using TeamUp.TeamManagement.Contracts.Invitations; | ||
using TeamUp.TeamManagement.Contracts.Invitations.GetMyInvitations; | ||
|
||
namespace TeamUp.TeamManagement.Application.Invitations; | ||
|
||
internal sealed class GetMyInvitationsQueryHandler : IQueryHandler<GetMyInvitationsQuery, Collection<InvitationResponse>> | ||
{ | ||
private readonly ITeamManagementQueryContext _appQueryContext; | ||
|
||
public GetMyInvitationsQueryHandler(ITeamManagementQueryContext appQueryContext) | ||
{ | ||
_appQueryContext = appQueryContext; | ||
} | ||
|
||
public async Task<Result<Collection<InvitationResponse>>> Handle(GetMyInvitationsQuery query, CancellationToken ct) | ||
{ | ||
var invitations = await _appQueryContext.Invitations | ||
.Where(invitation => invitation.RecipientId == query.InitiatorId) | ||
.Select(invitation => new InvitationResponse | ||
{ | ||
Id = invitation.Id, | ||
TeamName = _appQueryContext.Teams.First(team => team.Id == invitation.TeamId).Name, | ||
CreatedUtc = invitation.CreatedUtc | ||
}) | ||
.ToListAsync(ct); | ||
|
||
return new Collection<InvitationResponse>(invitations); | ||
} | ||
} |
55 changes: 55 additions & 0 deletions
55
...anagement/TeamUp.TeamManagement.Application/Invitations/GetTeamInvitationsQueryHandler.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
using Microsoft.EntityFrameworkCore; | ||
|
||
using RailwayResult; | ||
using RailwayResult.FunctionalExtensions; | ||
|
||
using TeamUp.Common.Application; | ||
using TeamUp.Common.Contracts; | ||
using TeamUp.TeamManagement.Contracts.Invitations; | ||
using TeamUp.TeamManagement.Contracts.Invitations.GetTeamInvitations; | ||
using TeamUp.TeamManagement.Domain.Aggregates.Teams; | ||
|
||
namespace TeamUp.TeamManagement.Application.Invitations; | ||
|
||
internal sealed class GetTeamInvitationsQueryHandler : IQueryHandler<GetTeamInvitationsQuery, Collection<TeamInvitationResponse>> | ||
{ | ||
private readonly ITeamManagementQueryContext _appQueryContext; | ||
|
||
public GetTeamInvitationsQueryHandler(ITeamManagementQueryContext appQueryContext) | ||
{ | ||
_appQueryContext = appQueryContext; | ||
} | ||
|
||
public async Task<Result<Collection<TeamInvitationResponse>>> Handle(GetTeamInvitationsQuery query, CancellationToken ct) | ||
{ | ||
var teamWithInitiator = await _appQueryContext.Teams | ||
.Select(team => new | ||
{ | ||
team.Id, | ||
Initiaotor = team.Members | ||
.Select(member => new { member.UserId, member.Role }) | ||
.FirstOrDefault(member => member.UserId == query.InitiatorId), | ||
}) | ||
.FirstOrDefaultAsync(team => team.Id == query.TeamId, ct); | ||
|
||
return await teamWithInitiator | ||
.EnsureNotNull(TeamErrors.TeamNotFound) | ||
.EnsureNotNull(team => team.Initiaotor, TeamErrors.NotMemberOfTeam) | ||
.Ensure(team => team.Initiaotor!.Role.CanInviteTeamMembers(), TeamErrors.UnauthorizedToReadInvitationList) | ||
.ThenAsync(team => | ||
{ | ||
return _appQueryContext.Invitations | ||
.Where(invitation => invitation.TeamId == query.TeamId) | ||
.Select(invitation => new TeamInvitationResponse | ||
{ | ||
Id = invitation.Id, | ||
Email = _appQueryContext.Users | ||
.Select(user => new { user.Id, user.Email }) | ||
.First(user => user.Id == invitation.RecipientId).Email, | ||
CreatedUtc = invitation.CreatedUtc | ||
}) | ||
.ToListAsync(ct); | ||
}) | ||
.Then(invitations => new Collection<TeamInvitationResponse>(invitations)); | ||
} | ||
} |
27 changes: 27 additions & 0 deletions
27
.../TeamManagement/TeamUp.TeamManagement.Application/Invitations/InviteUserCommandHandler.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
using RailwayResult; | ||
|
||
using TeamUp.Common.Application; | ||
using TeamUp.TeamManagement.Contracts; | ||
using TeamUp.TeamManagement.Contracts.Invitations.InviteUser; | ||
using TeamUp.TeamManagement.Domain.Aggregates.Invitations; | ||
|
||
namespace TeamUp.TeamManagement.Application.Invitations; | ||
|
||
internal sealed class InviteUserCommandHandler : ICommandHandler<InviteUserCommand> | ||
{ | ||
private readonly IInvitationDomainService _invitationDomainService; | ||
private readonly IUnitOfWork<TeamManagementModuleId> _unitOfWork; | ||
|
||
public InviteUserCommandHandler(IInvitationDomainService invitationDomainService, IUnitOfWork<TeamManagementModuleId> unitOfWork) | ||
{ | ||
_invitationDomainService = invitationDomainService; | ||
_unitOfWork = unitOfWork; | ||
} | ||
|
||
public Task<Result> Handle(InviteUserCommand command, CancellationToken ct) | ||
{ | ||
return _invitationDomainService | ||
.InviteUserAsync(command.InitiatorId, command.TeamId, command.Email, ct) | ||
.TapAsync(() => _unitOfWork.SaveChangesAsync(ct)); | ||
} | ||
} |
27 changes: 27 additions & 0 deletions
27
...anagement/TeamUp.TeamManagement.Application/Invitations/RemoveInvitationCommandHandler.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
using RailwayResult; | ||
|
||
using TeamUp.Common.Application; | ||
using TeamUp.TeamManagement.Contracts; | ||
using TeamUp.TeamManagement.Contracts.Invitations.RemoveInvitation; | ||
using TeamUp.TeamManagement.Domain.Aggregates.Invitations; | ||
|
||
namespace TeamUp.TeamManagement.Application.Invitations; | ||
|
||
internal sealed class RemoveInvitationCommandHandler : ICommandHandler<RemoveInvitationCommand> | ||
{ | ||
private readonly IInvitationDomainService _invitationDomainService; | ||
private readonly IUnitOfWork<TeamManagementModuleId> _unitOfWork; | ||
|
||
public RemoveInvitationCommandHandler(IInvitationDomainService invitationDomainService, IUnitOfWork<TeamManagementModuleId> unitOfWork) | ||
{ | ||
_invitationDomainService = invitationDomainService; | ||
_unitOfWork = unitOfWork; | ||
} | ||
|
||
public Task<Result> Handle(RemoveInvitationCommand command, CancellationToken ct) | ||
{ | ||
return _invitationDomainService | ||
.RemoveInvitationAsync(command.InitiatorId, command.InvitationId, ct) | ||
.TapAsync(() => _unitOfWork.SaveChangesAsync(ct)); | ||
} | ||
} |
5 changes: 1 addition & 4 deletions
5
...TeamManagement/TeamUp.TeamManagement.Application/TeamUp.TeamManagement.Application.csproj
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
21 changes: 21 additions & 0 deletions
21
...t/TeamUp.TeamManagement.Contracts/Invitations/AcceptInvitation/AcceptInvitationCommand.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
using FluentValidation; | ||
|
||
using TeamUp.Common.Contracts; | ||
using TeamUp.UserAccess.Contracts; | ||
|
||
namespace TeamUp.TeamManagement.Contracts.Invitations.AcceptInvitation; | ||
|
||
public sealed record AcceptInvitationCommand : ICommand | ||
{ | ||
public required UserId InitiatorId { get; init; } | ||
public required InvitationId InvitationId { get; init; } | ||
|
||
public sealed class Validator : AbstractValidator<AcceptInvitationCommand> | ||
{ | ||
public Validator() | ||
{ | ||
RuleFor(x => x.InitiatorId).NotEmpty(); | ||
RuleFor(x => x.InvitationId).NotEmpty(); | ||
} | ||
} | ||
} |
9 changes: 9 additions & 0 deletions
9
...ent/TeamUp.TeamManagement.Contracts/Invitations/GetMyInvitations/GetMyInvitationsQuery.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
using TeamUp.Common.Contracts; | ||
using TeamUp.UserAccess.Contracts; | ||
|
||
namespace TeamUp.TeamManagement.Contracts.Invitations.GetMyInvitations; | ||
|
||
public sealed record GetMyInvitationsQuery : IQuery<Collection<InvitationResponse>> | ||
{ | ||
public required UserId InitiatorId { get; init; } | ||
} |
11 changes: 11 additions & 0 deletions
11
...TeamUp.TeamManagement.Contracts/Invitations/GetTeamInvitations/GetTeamInvitationsQuery.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
using TeamUp.Common.Contracts; | ||
using TeamUp.TeamManagement.Contracts.Teams; | ||
using TeamUp.UserAccess.Contracts; | ||
|
||
namespace TeamUp.TeamManagement.Contracts.Invitations.GetTeamInvitations; | ||
|
||
public sealed record GetTeamInvitationsQuery : IQuery<Collection<TeamInvitationResponse>> | ||
{ | ||
public required UserId InitiatorId { get; init; } | ||
public required TeamId TeamId { get; init; } | ||
} |
8 changes: 8 additions & 0 deletions
8
src/Modules/TeamManagement/TeamUp.TeamManagement.Contracts/Invitations/InvitationResponse.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
namespace TeamUp.TeamManagement.Contracts.Invitations; | ||
|
||
public sealed class InvitationResponse | ||
{ | ||
public required InvitationId Id { get; init; } | ||
public required string TeamName { get; init; } | ||
public required DateTime CreatedUtc { get; init; } | ||
} |
24 changes: 24 additions & 0 deletions
24
...eamManagement/TeamUp.TeamManagement.Contracts/Invitations/InviteUser/InviteUserCommand.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
using FluentValidation; | ||
|
||
using TeamUp.Common.Contracts; | ||
using TeamUp.TeamManagement.Contracts.Teams; | ||
using TeamUp.UserAccess.Contracts; | ||
|
||
namespace TeamUp.TeamManagement.Contracts.Invitations.InviteUser; | ||
|
||
public sealed record InviteUserCommand : ICommand | ||
{ | ||
public required UserId InitiatorId { get; init; } | ||
public required TeamId TeamId { get; init; } | ||
public required string Email { get; init; } | ||
|
||
public sealed class Validator : AbstractValidator<InviteUserCommand> | ||
{ | ||
public Validator() | ||
{ | ||
RuleFor(x => x.InitiatorId).NotEmpty(); | ||
RuleFor(x => x.TeamId).NotEmpty(); | ||
RuleFor(x => x.Email).EmailAddress(); | ||
} | ||
} | ||
} |
13 changes: 13 additions & 0 deletions
13
...eamManagement/TeamUp.TeamManagement.Contracts/Invitations/InviteUser/InviteUserRequest.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
using System.ComponentModel.DataAnnotations; | ||
|
||
using TeamUp.TeamManagement.Contracts.Teams; | ||
|
||
namespace TeamUp.TeamManagement.Contracts.Invitations.InviteUser; | ||
|
||
public sealed class InviteUserRequest | ||
{ | ||
public required TeamId TeamId { get; init; } | ||
|
||
[DataType(DataType.EmailAddress)] | ||
public required string Email { get; init; } | ||
} |
21 changes: 21 additions & 0 deletions
21
...t/TeamUp.TeamManagement.Contracts/Invitations/RemoveInvitation/RemoveInvitationCommand.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
using FluentValidation; | ||
|
||
using TeamUp.Common.Contracts; | ||
using TeamUp.UserAccess.Contracts; | ||
|
||
namespace TeamUp.TeamManagement.Contracts.Invitations.RemoveInvitation; | ||
|
||
public sealed record RemoveInvitationCommand : ICommand | ||
{ | ||
public required UserId InitiatorId { get; init; } | ||
public required InvitationId InvitationId { get; init; } | ||
|
||
public sealed class Validator : AbstractValidator<RemoveInvitationCommand> | ||
{ | ||
public Validator() | ||
{ | ||
RuleFor(x => x.InitiatorId).NotEmpty(); | ||
RuleFor(x => x.InvitationId).NotEmpty(); | ||
} | ||
} | ||
} |
Oops, something went wrong.