public void CreateInviteAsync_WhenFromCharacter_EnsureUserIsOwnerOfCharacter() { const int groupId = 42; const int characterId = 24; var naheulbookExecutionContext = new NaheulbookExecutionContext { UserId = 10 }; var request = new CreateInviteRequest { CharacterId = characterId, FromGroup = false }; var group = new Group { Id = groupId }; var character = new Character { Id = characterId }; _unitOfWorkFactory.GetUnitOfWork().Groups.GetAsync(groupId) .Returns(group); _unitOfWorkFactory.GetUnitOfWork().Characters.GetWithOriginWithJobsAsync(characterId) .Returns(character); _authorizationUtil.When(x => x.EnsureIsCharacterOwner(naheulbookExecutionContext, character)) .Throw(new TestException()); Func <Task> act = () => _service.CreateInviteAsync(naheulbookExecutionContext, groupId, request); act.Should().Throw <TestException>(); _unitOfWorkFactory.GetUnitOfWork().DidNotReceive().SaveChangesAsync(); }
/// <summary> Creates a new invite to this channel. </summary> /// <param name="maxAge"> Time (in seconds) until the invite expires. Set to null to never expire. </param> /// <param name="maxUses"> The max amount of times this invite may be used. Set to null to have unlimited uses. </param> /// <param name="tempMembership"> If true, a user accepting this invite will be kicked from the server after closing their client. </param> /// <param name="withXkcd"> If true, creates a human-readable link. Not supported if maxAge is set to null. </param> public async Task <Invite> CreateInvite(int?maxAge = 1800, int?maxUses = null, bool tempMembership = false, bool withXkcd = false) { if (maxAge < 0) { throw new ArgumentOutOfRangeException(nameof(maxAge)); } if (maxUses < 0) { throw new ArgumentOutOfRangeException(nameof(maxUses)); } var request = new CreateInviteRequest(Id) { MaxAge = maxAge ?? 0, MaxUses = maxUses ?? 0, IsTemporary = tempMembership, WithXkcdPass = withXkcd }; var response = await Client.ClientAPI.Send(request).ConfigureAwait(false); var invite = new Invite(Client, response.Code, response.XkcdPass); return(invite); }
public async Task <CreatedActionResult <GroupInviteResponse> > PostCreateInviteAsync( [FromServices] NaheulbookExecutionContext executionContext, [FromRoute] int groupId, CreateInviteRequest request ) { try { var invite = await _groupService.CreateInviteAsync(executionContext, groupId, request); return(_mapper.Map <GroupInviteResponse>(invite)); } catch (ForbiddenAccessException ex) { throw new HttpErrorException(StatusCodes.Status403Forbidden, ex); } catch (GroupNotFoundException ex) { throw new HttpErrorException(StatusCodes.Status404NotFound, ex); } catch (CharacterNotFoundException ex) { throw new HttpErrorException(StatusCodes.Status400BadRequest, ex); } catch (CharacterAlreadyInAGroupException ex) { throw new HttpErrorException(StatusCodes.Status400BadRequest, ex); } }
public async Task <IActionResult> InvitePlayer([FromBody] CreateInviteRequest request) { var leagueRecord = await GetExistingLeague(request.LeagueID, RequiredRelationship.LeagueManager); if (leagueRecord.FailedResult is not null) { return(leagueRecord.FailedResult); } var validResult = leagueRecord.ValidResult !; var league = validResult.League; string baseURL = $"{Request.Scheme}://{Request.Host.Value}"; FantasyCriticUser inviteUser; if (!request.IsDisplayNameInvite()) { if (request.InviteEmail is null) { return(BadRequest()); } string inviteEmail = request.InviteEmail.ToLower(); inviteUser = await _userManager.FindByEmailAsync(inviteEmail); if (inviteUser is null) { Result userlessInviteResult = await _leagueMemberService.InviteUserByEmail(league, inviteEmail); if (userlessInviteResult.IsFailure) { return(BadRequest(userlessInviteResult.Error)); } await _emailSendingService.SendSiteInviteEmail(inviteEmail, league, baseURL); return(Ok()); } } else { inviteUser = await _userManager.FindByDisplayName(request.InviteDisplayName !, request.InviteDisplayNumber !.Value); } if (inviteUser is null) { return(BadRequest("No user is found with that information.")); } Result userInviteResult = await _leagueMemberService.InviteUserByUserID(league, inviteUser); if (userInviteResult.IsFailure) { return(BadRequest(userInviteResult.Error)); } await _emailSendingService.SendLeagueInviteEmail(inviteUser.Email, league, baseURL); return(Ok()); }
public void CreateInviteAsync_ShouldThrowWhenGroupNotFound() { const int groupId = 42; var request = new CreateInviteRequest(); var executionContext = new NaheulbookExecutionContext(); _unitOfWorkFactory.GetUnitOfWork().Groups.GetAsync(groupId) .Returns((Group)null); Func <Task> act = () => _service.CreateInviteAsync(executionContext, groupId, request); act.Should().Throw <GroupNotFoundException>(); }
//Invites public Task <CreateInviteResponse> CreateInvite(string channelId, int maxAge, int maxUses, bool tempMembership, bool hasXkcd) { if (channelId == null) { throw new ArgumentNullException(nameof(channelId)); } var request = new CreateInviteRequest { MaxAge = maxAge, MaxUses = maxUses, IsTemporary = tempMembership, WithXkcdPass = hasXkcd }; return(_rest.Post <CreateInviteResponse>(Endpoints.ChannelInvites(channelId), request)); }
public async Task PostCreateInviteAsync_ShouldCreateInvite_ThenReturnInviteResponse() { const int groupId = 8; var createInviteRequest = new CreateInviteRequest(); var createdInvite = new GroupInvite(); var groupInviteResponse = new GroupInviteResponse(); _groupService.CreateInviteAsync(_executionContext, groupId, createInviteRequest) .Returns(createdInvite); _mapper.Map <GroupInviteResponse>(createdInvite) .Returns(groupInviteResponse); var result = await _controller.PostCreateInviteAsync(_executionContext, groupId, createInviteRequest); result.Value.Should().BeSameAs(groupInviteResponse); result.StatusCode.Should().Be(StatusCodes.Status201Created); }
public void CreateInviteAsync_ShouldThrowWhenCharacterNotFound() { const int groupId = 42; const int characterId = 24; var request = new CreateInviteRequest { CharacterId = characterId }; var executionContext = new NaheulbookExecutionContext(); _unitOfWorkFactory.GetUnitOfWork().Groups.GetAsync(groupId) .Returns(new Group()); _unitOfWorkFactory.GetUnitOfWork().Characters.GetWithOriginWithJobsAsync(characterId) .Returns((Character)null); Func <Task> act = () => _service.CreateInviteAsync(executionContext, groupId, request); act.Should().Throw <CharacterNotFoundException>(); }
public async Task CreateInviteAsync_ShouldCreateANewGroupInviteInDatabase() { const int groupId = 42; const int characterId = 24; var request = new CreateInviteRequest { CharacterId = characterId, FromGroup = true }; var group = new Group(); var character = new Character(); _unitOfWorkFactory.GetUnitOfWork().Groups.GetAsync(groupId) .Returns(group); _unitOfWorkFactory.GetUnitOfWork().Characters.GetWithOriginWithJobsAsync(characterId) .Returns(character); await _service.CreateInviteAsync(new NaheulbookExecutionContext(), groupId, request); Received.InOrder(() => { _unitOfWorkFactory.GetUnitOfWork().GroupInvites.Add(Arg.Is <GroupInvite>(gi => gi.FromGroup && gi.Group == group && gi.Character == character)); _unitOfWorkFactory.GetUnitOfWork().SaveChangesAsync(); }); }
public async Task CreateInviteAsync_ShouldNotifyChange() { const int groupId = 42; const int characterId = 24; var request = new CreateInviteRequest { CharacterId = characterId, FromGroup = true }; var group = new Group(); var character = new Character(); _unitOfWorkFactory.GetUnitOfWork().Groups.GetAsync(groupId) .Returns(group); _unitOfWorkFactory.GetUnitOfWork().Characters.GetWithOriginWithJobsAsync(characterId) .Returns(character); var groupInvite = await _service.CreateInviteAsync(new NaheulbookExecutionContext(), groupId, request); Received.InOrder(() => { _notificationSessionFactory.NotificationSession.NotifyCharacterGroupInvite(characterId, groupInvite); _notificationSessionFactory.NotificationSession.NotifyGroupCharacterInvite(groupId, groupInvite); _notificationSessionFactory.NotificationSession.CommitAsync(); }); }
public void ReturnInviteIdWhenCreatingInviteSuccessfullyWithExistingPlayerInvites() { var expectedCreatedInvite = new InviteDataModel(SenderPlayerId, ReceiverPlayerId, _party.Id, _metadata); // Setup the client so that it will successfully create an invite. _mockMemoryStoreClient.Setup(client => client.GetAsync <Member>(SenderPlayerId)) .ReturnsAsync(_party.GetMember(SenderPlayerId)); _mockMemoryStoreClient.Setup(client => client.GetAsync <PartyDataModel>(_party.Id)).ReturnsAsync(_party); _mockMemoryStoreClient.Setup(client => client.GetAsync <InviteDataModel>(expectedCreatedInvite.Id)) .ReturnsAsync((InviteDataModel)null); _mockMemoryStoreClient.Setup(client => client.GetAsync <PlayerInvites>(SenderPlayerId)) .ReturnsAsync(new PlayerInvites(SenderPlayerId)); _mockMemoryStoreClient.Setup(client => client.GetAsync <PlayerInvites>(ReceiverPlayerId)) .ReturnsAsync(new PlayerInvites(ReceiverPlayerId)); var entriesCreated = new List <Entry>(); var entriesUpdated = new List <Entry>(); _mockTransaction.Setup(tr => tr.CreateAll(It.IsAny <IEnumerable <Entry> >())) .Callback <IEnumerable <Entry> >(entries => entriesCreated.AddRange(entries)); _mockTransaction.Setup(tr => tr.UpdateAll(It.IsAny <IEnumerable <Entry> >())) .Callback <IEnumerable <Entry> >(entries => entriesUpdated.AddRange(entries)); _mockTransaction.Setup(tr => tr.Dispose()); SetupAnalyticsExpectation(expectedCreatedInvite); // Check that the RPC has completely successfully and that an empty response was returned. var context = Util.CreateFakeCallContext(SenderPlayerId, ""); var request = new CreateInviteRequest { ReceiverPlayerId = ReceiverPlayerId }; request.Metadata.Add(_metadata); var response = _inviteService.CreateInvite(request, context).Result; Assert.AreEqual(expectedCreatedInvite.Id, response.InviteId); // Check that an Invite entry has been created. Assert.AreEqual(1, entriesCreated.Count); Assert.IsInstanceOf <InviteDataModel>(entriesCreated[0]); var inviteCreated = (InviteDataModel)entriesCreated[0]; Assert.AreEqual(SenderPlayerId, inviteCreated.SenderId); Assert.AreEqual(ReceiverPlayerId, inviteCreated.ReceiverId); Assert.AreEqual(_party.Id, inviteCreated.PartyId); Assert.AreEqual(InviteDataModel.Status.Pending, inviteCreated.CurrentStatus); CollectionAssert.AreEquivalent(_metadata, inviteCreated.Metadata); // Verify that the player invites for the sender and receiver have been updated since they already existed // in the memory store. Assert.AreEqual(2, entriesUpdated.Count); var senderPlayerInvites = (PlayerInvites)entriesUpdated[0]; Assert.AreEqual(SenderPlayerId, senderPlayerInvites.Id); Assert.That(senderPlayerInvites.OutboundInviteIds, Contains.Item(expectedCreatedInvite.Id)); Assert.IsInstanceOf <PlayerInvites>(entriesUpdated[1]); var receiverPlayerInvites = (PlayerInvites)entriesUpdated[1]; Assert.AreEqual(ReceiverPlayerId, receiverPlayerInvites.Id); Assert.That(receiverPlayerInvites.InboundInviteIds, Contains.Item(expectedCreatedInvite.Id)); _mockAnalyticsSender.VerifyAll(); }
public void AllowThoseInvolvedInInvitesToDeleteThem() { // Create a party. var senderPit = CreatePlayerIdentityTokenForPlayer(LeaderPlayerId); _partyClient.CreateParty(new CreatePartyRequest(), new Metadata { { PitRequestHeaderName, senderPit } }); // Send invites to other players. var createInviteRequest = new CreateInviteRequest { ReceiverPlayerId = PlayerId1 }; var inviteId1 = _inviteClient .CreateInvite(createInviteRequest, new Metadata { { PitRequestHeaderName, senderPit } }) .InviteId; Assert.NotNull(inviteId1); createInviteRequest = new CreateInviteRequest { ReceiverPlayerId = PlayerId2 }; var inviteId2 = _inviteClient .CreateInvite(createInviteRequest, new Metadata { { PitRequestHeaderName, senderPit } }) .InviteId; Assert.NotNull(inviteId2); // Verify that the receiver can delete invites. var receiverPit1 = CreatePlayerIdentityTokenForPlayer(PlayerId1); _inviteClient.DeleteInvite(new DeleteInviteRequest { InviteId = inviteId1 }, new Metadata { { PitRequestHeaderName, receiverPit1 } }); var exception = Assert.Throws <RpcException>(() => _inviteClient.GetInvite( new GetInviteRequest { InviteId = inviteId1 }, new Metadata { { PitRequestHeaderName, receiverPit1 } })); Assert.AreEqual(StatusCode.NotFound, exception.StatusCode); // Verify that the sender can delete invites. _inviteClient.DeleteInvite(new DeleteInviteRequest { InviteId = inviteId2 }, new Metadata { { PitRequestHeaderName, senderPit } }); exception = Assert.Throws <RpcException>(() => _inviteClient.GetInvite( new GetInviteRequest { InviteId = inviteId1 }, new Metadata { { PitRequestHeaderName, senderPit } })); Assert.AreEqual(StatusCode.NotFound, exception.StatusCode); // Cleanup. _partyClient.DeleteParty(new DeletePartyRequest(), new Metadata { { PitRequestHeaderName, senderPit } }); }
public async Task <IActionResult> InvitePlayer([FromBody] CreateInviteRequest request) { var currentUser = await _userManager.FindByNameAsync(User.Identity.Name); if (!ModelState.IsValid) { return(BadRequest()); } var league = await _fantasyCriticService.GetLeagueByID(request.LeagueID); if (league.HasNoValue) { return(BadRequest()); } if (league.Value.LeagueManager.UserID != currentUser.UserID) { return(Forbid()); } string baseURL = $"{Request.Scheme}://{Request.Host.Value}"; FantasyCriticUser inviteUser; if (!request.IsDisplayNameInvite()) { string inviteEmail = request.InviteEmail.ToLower(); inviteUser = await _userManager.FindByEmailAsync(inviteEmail); if (inviteUser is null) { Result userlessInviteResult = await _leagueMemberService.InviteUserByEmail(league.Value, inviteEmail); if (userlessInviteResult.IsFailure) { return(BadRequest(userlessInviteResult.Error)); } await _emailSender.SendSiteInviteEmail(inviteEmail, league.Value, baseURL); return(Ok()); } } else { inviteUser = await _userManager.FindByDisplayName(request.InviteDisplayName, request.InviteDisplayNumber.Value); } if (inviteUser is null) { return(BadRequest("No user is found with that information.")); } Result userInviteResult = await _leagueMemberService.InviteUserByUserID(league.Value, inviteUser); if (userInviteResult.IsFailure) { return(BadRequest(userInviteResult.Error)); } await _emailSender.SendLeagueInviteEmail(inviteUser.EmailAddress, league.Value, baseURL); return(Ok()); }
/// <summary> Creates a new invite to this channel. </summary> /// <param name="maxAge"> Time (in seconds) until the invite expires. Set to null to never expire. </param> /// <param name="maxUses"> The max amount of times this invite may be used. Set to null to have unlimited uses. </param> /// <param name="tempMembership"> If true, a user accepting this invite will be kicked from the server after closing their client. </param> /// <param name="withXkcd"> If true, creates a human-readable link. Not supported if maxAge is set to null. </param> public async Task<Invite> CreateInvite(int? maxAge = 1800, int? maxUses = null, bool tempMembership = false, bool withXkcd = false) { if (maxAge < 0) throw new ArgumentOutOfRangeException(nameof(maxAge)); if (maxUses < 0) throw new ArgumentOutOfRangeException(nameof(maxUses)); var request = new CreateInviteRequest(Id) { MaxAge = maxAge ?? 0, MaxUses = maxUses ?? 0, IsTemporary = tempMembership, WithXkcdPass = withXkcd }; var response = await Client.ClientAPI.Send(request).ConfigureAwait(false); var invite = new Invite(Client, response.Code, response.XkcdPass); return invite; }
public void AllowTheReceiverToAcceptTheInvite() { // Create a party. var senderPit = CreatePlayerIdentityTokenForPlayer(LeaderPlayerId); _partyClient.CreateParty(new CreatePartyRequest(), new Metadata { { PitRequestHeaderName, senderPit } }); // Send invites to other players. var createInviteRequest = new CreateInviteRequest { ReceiverPlayerId = PlayerId1 }; var inviteId = _inviteClient .CreateInvite(createInviteRequest, new Metadata { { PitRequestHeaderName, senderPit } }) .InviteId; Assert.NotNull(inviteId); // Receiver accepts the invite. var receiverPit = CreatePlayerIdentityTokenForPlayer(PlayerId1); var getInviteRequest = new GetInviteRequest { InviteId = inviteId }; var invite = _inviteClient.GetInvite(getInviteRequest, new Metadata { { PitRequestHeaderName, receiverPit } }) .Invite; invite.CurrentStatus = Invite.Types.Status.Accepted; var updatedInvite = _inviteClient.UpdateInvite(new UpdateInviteRequest { UpdatedInvite = invite }, new Metadata { { PitRequestHeaderName, receiverPit } }).Invite; Assert.AreEqual(Invite.Types.Status.Accepted, updatedInvite.CurrentStatus); // Verify this change has propagated to the sender as well. var senderPlayerInvites = _inviteClient.ListAllInvites(new ListAllInvitesRequest(), new Metadata { { PitRequestHeaderName, senderPit } }); Assert.AreEqual(1, senderPlayerInvites.OutboundInvites.Count); Assert.AreEqual(updatedInvite, senderPlayerInvites.OutboundInvites[0]); // Clean up. _inviteClient.DeleteInvite(new DeleteInviteRequest { InviteId = inviteId }, new Metadata { { PitRequestHeaderName, receiverPit } }); _partyClient.DeleteParty(new DeletePartyRequest(), new Metadata { { PitRequestHeaderName, senderPit } }); }
public override async Task <CreateInviteResponse> CreateInvite(CreateInviteRequest request, ServerCallContext context) { var playerId = AuthHeaders.ExtractPlayerId(context); if (string.IsNullOrEmpty(request.ReceiverPlayerId)) { throw new RpcException( new Status(StatusCode.InvalidArgument, "Expected a non-empty receiver player id")); } using (var memClient = _memoryStoreClientManager.GetClient()) { var party = await GetPartyByPlayerId(memClient, playerId); // This extra check is necessary because the player might have meanwhile left the party (between the // Get<Member> and Get<PartyDataModel> calls). if (party?.GetMember(playerId) == null) { throw new RpcException(new Status(StatusCode.FailedPrecondition, "The player creating this invite is not a member of any party")); } if (party.GetMember(request.ReceiverPlayerId) != null) { throw new RpcException(new Status(StatusCode.FailedPrecondition, "The receiving player is already a member of the party")); } var entitiesToCreate = new List <Entry>(); var entitiesToUpdate = new List <Entry>(); var invite = new InviteDataModel(playerId, request.ReceiverPlayerId, party.Id, request.Metadata); entitiesToCreate.Add(invite); var senderPlayerInvites = await memClient.GetAsync <PlayerInvites>(playerId); if (senderPlayerInvites == null) { senderPlayerInvites = new PlayerInvites(playerId); entitiesToCreate.Add(senderPlayerInvites); } else { entitiesToUpdate.Add(senderPlayerInvites); } senderPlayerInvites.OutboundInviteIds.Add(invite.Id); var receiverPlayerInvites = await memClient.GetAsync <PlayerInvites>(request.ReceiverPlayerId); if (receiverPlayerInvites == null) { receiverPlayerInvites = new PlayerInvites(request.ReceiverPlayerId); entitiesToCreate.Add(receiverPlayerInvites); } else { entitiesToUpdate.Add(receiverPlayerInvites); } receiverPlayerInvites.InboundInviteIds.Add(invite.Id); using (var transaction = memClient.CreateTransaction()) { transaction.CreateAll(entitiesToCreate); transaction.UpdateAll(entitiesToUpdate); } return(new CreateInviteResponse { InviteId = invite.Id }); } }
public void AllowPlayersToSendInvites() { // Create a party. var senderPit = CreatePlayerIdentityTokenForPlayer(LeaderPlayerId); var partyId = _partyClient .CreateParty(new CreatePartyRequest(), new Metadata { { PitRequestHeaderName, senderPit } }).PartyId; // Send invites to other players. var createInviteRequest = new CreateInviteRequest { ReceiverPlayerId = PlayerId1 }; var inviteId1 = _inviteClient .CreateInvite(createInviteRequest, new Metadata { { PitRequestHeaderName, senderPit } }) .InviteId; Assert.NotNull(inviteId1); createInviteRequest = new CreateInviteRequest { ReceiverPlayerId = PlayerId2 }; var inviteId2 = _inviteClient .CreateInvite(createInviteRequest, new Metadata { { PitRequestHeaderName, senderPit } }) .InviteId; Assert.NotNull(inviteId2); // Verify that the invites were successfully stored. var invite1 = _inviteClient.GetInvite(new GetInviteRequest { InviteId = inviteId1 }, new Metadata { { PitRequestHeaderName, senderPit } }).Invite; Assert.AreEqual(inviteId1, invite1.Id); Assert.AreEqual(LeaderPlayerId, invite1.SenderPlayerId); Assert.AreEqual(PlayerId1, invite1.ReceiverPlayerId); Assert.AreEqual(partyId, invite1.PartyId); Assert.AreEqual(Invite.Types.Status.Pending, invite1.CurrentStatus); var invite2 = _inviteClient.GetInvite(new GetInviteRequest { InviteId = inviteId2 }, new Metadata { { PitRequestHeaderName, senderPit } }).Invite; Assert.AreEqual(inviteId2, invite2.Id); Assert.AreEqual(LeaderPlayerId, invite2.SenderPlayerId); Assert.AreEqual(PlayerId2, invite2.ReceiverPlayerId); Assert.AreEqual(partyId, invite2.PartyId); Assert.AreEqual(Invite.Types.Status.Pending, invite2.CurrentStatus); // Verify that the sender has the invites in its outbound invites. var senderPlayerInvites = _inviteClient.ListAllInvites(new ListAllInvitesRequest(), new Metadata { { PitRequestHeaderName, senderPit } }); Assert.AreEqual(2, senderPlayerInvites.OutboundInvites.Count); Assert.AreEqual(invite1, senderPlayerInvites.OutboundInvites[0]); Assert.AreEqual(invite2, senderPlayerInvites.OutboundInvites[1]); // Verify that both receivers have the invite in their inbound invites. var receiverPit1 = CreatePlayerIdentityTokenForPlayer(PlayerId1); var receiverPlayerInvites1 = _inviteClient.ListAllInvites(new ListAllInvitesRequest(), new Metadata { { PitRequestHeaderName, receiverPit1 } }); Assert.AreEqual(1, receiverPlayerInvites1.InboundInvites.Count); Assert.AreEqual(invite1, receiverPlayerInvites1.InboundInvites[0]); var receiverPit2 = CreatePlayerIdentityTokenForPlayer(PlayerId2); var receiverPlayerInvites2 = _inviteClient.ListAllInvites(new ListAllInvitesRequest(), new Metadata { { PitRequestHeaderName, receiverPit2 } }); Assert.AreEqual(1, receiverPlayerInvites2.InboundInvites.Count); Assert.AreEqual(invite2, receiverPlayerInvites2.InboundInvites[0]); // Cleanup. _inviteClient.DeleteInvite(new DeleteInviteRequest { InviteId = inviteId1 }, new Metadata { { PitRequestHeaderName, senderPit } }); _inviteClient.DeleteInvite(new DeleteInviteRequest { InviteId = inviteId2 }, new Metadata { { PitRequestHeaderName, senderPit } }); _partyClient.DeleteParty(new DeletePartyRequest(), new Metadata { { PitRequestHeaderName, senderPit } }); }
public async Task <GroupInvite> CreateInviteAsync(NaheulbookExecutionContext executionContext, int groupId, CreateInviteRequest request) { using (var uow = _unitOfWorkFactory.CreateUnitOfWork()) { var group = await uow.Groups.GetAsync(groupId); if (group == null) { throw new GroupNotFoundException(groupId); } var character = await uow.Characters.GetWithOriginWithJobsAsync(request.CharacterId); if (character == null) { throw new CharacterNotFoundException(request.CharacterId); } if (character.GroupId != null) { throw new CharacterAlreadyInAGroupException(request.CharacterId); } if (request.FromGroup) { _authorizationUtil.EnsureIsGroupOwner(executionContext, group); } else { _authorizationUtil.EnsureIsCharacterOwner(executionContext, character); } var groupInvite = new GroupInvite { Character = character, Group = group, FromGroup = request.FromGroup, }; uow.GroupInvites.Add(groupInvite); await uow.SaveChangesAsync(); var session = _notificationSessionFactory.CreateSession(); session.NotifyCharacterGroupInvite(request.CharacterId, groupInvite); session.NotifyGroupCharacterInvite(groupId, groupInvite); await session.CommitAsync(); return(groupInvite); } }