Exemple #1
0
        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();
        }
Exemple #2
0
        /// <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());
    }
Exemple #5
0
        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>();
        }
Exemple #6
0
        //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));
        }
Exemple #7
0
        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);
        }
Exemple #8
0
        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>();
        }
Exemple #9
0
        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();
            });
        }
Exemple #10
0
        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();
        }
Exemple #12
0
        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 }
            });
        }
Exemple #13
0
        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());
        }
Exemple #14
0
        /// <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;
        }
Exemple #15
0
        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
                });
            }
        }
Exemple #17
0
        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);
            }
        }