public async Task Unmatched_PlayerIdentity_returns_new_player_and_identity() { using (var connection = _databaseFixture.ConnectionFactory.CreateDatabaseConnection()) { connection.Open(); using (var transaction = connection.BeginTransaction()) { var playerIdentity = new PlayerIdentity { PlayerIdentityName = $"New player {Guid.NewGuid()}", Team = new Team { TeamId = _databaseFixture.TestData.TeamWithFullDetails.TeamId } }; var playerRoute = $"/players/{playerIdentity.PlayerIdentityName.Kebaberize()}"; var copier = new Mock <IStoolballEntityCopier>(); copier.Setup(x => x.CreateAuditableCopy(playerIdentity)).Returns(playerIdentity); var playerNameFormatter = new Mock <IPlayerNameFormatter>(); playerNameFormatter.Setup(x => x.CapitaliseName(playerIdentity.PlayerIdentityName)).Returns(playerIdentity.PlayerIdentityName); var routeGenerator = new Mock <IRouteGenerator>(); routeGenerator.Setup(x => x.GenerateUniqueRoute("/players", playerIdentity.PlayerIdentityName, NoiseWords.PlayerRoute, It.IsAny <Func <string, Task <int> > >())).Returns(Task.FromResult(playerRoute)); var repo = new SqlServerPlayerRepository(Mock.Of <IAuditRepository>(), Mock.Of <ILogger>(), routeGenerator.Object, copier.Object, playerNameFormatter.Object); var result = await repo.CreateOrMatchPlayerIdentity(playerIdentity, Guid.NewGuid(), "Member name", transaction).ConfigureAwait(false); Assert.NotNull(result); copier.Verify(x => x.CreateAuditableCopy(playerIdentity), Times.Once); playerNameFormatter.Verify(x => x.CapitaliseName(playerIdentity.PlayerIdentityName)); routeGenerator.Verify(x => x.GenerateUniqueRoute("/players", playerIdentity.PlayerIdentityName, NoiseWords.PlayerRoute, It.IsAny <Func <string, Task <int> > >())); var identityResult = await transaction.Connection.QuerySingleAsync <PlayerIdentityResult>( $"SELECT PlayerId, PlayerIdentityName, ComparableName, TeamId FROM {Tables.PlayerIdentity} WHERE PlayerIdentityName = @PlayerIdentityName", new { playerIdentity.PlayerIdentityName }, transaction).ConfigureAwait(false); Assert.NotNull(identityResult); Assert.Equal(playerIdentity.PlayerIdentityName, identityResult.PlayerIdentityName); Assert.Equal(playerIdentity.ComparableName(), identityResult.ComparableName); Assert.Equal(playerIdentity.Team.TeamId, identityResult.TeamId); var playerResult = await transaction.Connection.QuerySingleAsync <Player>( $"SELECT PlayerRoute FROM {Tables.Player} WHERE PlayerId = @PlayerId", new { identityResult.PlayerId }, transaction).ConfigureAwait(false); Assert.NotNull(playerResult); Assert.Equal(playerRoute, playerResult.PlayerRoute); transaction.Rollback(); } } }
/// <summary> /// Finds an existing player identity or creates it if it is not found /// </summary> /// <returns>The <see cref="PlayerIdentity.PlayerIdentityId"/> of the created or matched player identity</returns> public async Task <PlayerIdentity> CreateOrMatchPlayerIdentity(PlayerIdentity playerIdentity, Guid memberKey, string memberName, IDbTransaction transaction) { if (playerIdentity is null) { throw new ArgumentNullException(nameof(playerIdentity)); } if (playerIdentity.PlayerIdentityId.HasValue && playerIdentity.Player.PlayerId.HasValue) { return(playerIdentity); } if (string.IsNullOrWhiteSpace(playerIdentity.PlayerIdentityName)) { throw new ArgumentException($"'{nameof(playerIdentity)}.PlayerIdentityName' cannot be null or whitespace", nameof(playerIdentity)); } if (playerIdentity.Team?.TeamId == null) { throw new ArgumentException($"'{nameof(playerIdentity)}.Team.TeamId' cannot be null", nameof(playerIdentity)); } if (string.IsNullOrWhiteSpace(memberName)) { throw new ArgumentNullException(nameof(memberName)); } if (transaction is null) { throw new ArgumentNullException(nameof(transaction)); } var matchedPlayerIdentity = (await transaction.Connection.QueryAsync <PlayerIdentity, Player, PlayerIdentity>( $"SELECT PlayerIdentityId, PlayerIdentityName, PlayerId FROM {Tables.PlayerIdentity} WHERE ComparableName = @ComparableName AND TeamId = @TeamId", (pi, p) => { pi.Player = p; return(pi); }, new { ComparableName = playerIdentity.ComparableName(), playerIdentity.Team.TeamId }, transaction, splitOn: "PlayerId").ConfigureAwait(false)).FirstOrDefault(); if (matchedPlayerIdentity != null && matchedPlayerIdentity.PlayerIdentityId.HasValue && matchedPlayerIdentity.Player.PlayerId.HasValue) { matchedPlayerIdentity.Team = playerIdentity.Team; return(matchedPlayerIdentity); } var auditablePlayerIdentity = _copier.CreateAuditableCopy(playerIdentity); auditablePlayerIdentity.PlayerIdentityId = Guid.NewGuid(); auditablePlayerIdentity.PlayerIdentityName = _playerNameFormatter.CapitaliseName(auditablePlayerIdentity.PlayerIdentityName); var player = new Player { PlayerId = Guid.NewGuid() }; player.PlayerIdentities.Add(auditablePlayerIdentity); player.PlayerRoute = await _routeGenerator.GenerateUniqueRoute($"/players", auditablePlayerIdentity.PlayerIdentityName, NoiseWords.PlayerRoute, async route => await transaction.Connection.ExecuteScalarAsync <int>($"SELECT COUNT(*) FROM {Tables.Player} WHERE PlayerRoute = @PlayerRoute", new { player.PlayerRoute }, transaction).ConfigureAwait(false) ).ConfigureAwait(false); await transaction.Connection.ExecuteAsync( $@"INSERT INTO {Tables.Player} (PlayerId, PlayerRoute) VALUES (@PlayerId, @PlayerRoute)", new { player.PlayerId, player.PlayerRoute }, transaction).ConfigureAwait(false); await transaction.Connection.ExecuteAsync($@"INSERT INTO {Tables.PlayerIdentity} (PlayerIdentityId, PlayerId, PlayerIdentityName, ComparableName, TeamId) VALUES (@PlayerIdentityId, @PlayerId, @PlayerIdentityName, @ComparableName, @TeamId)", new { auditablePlayerIdentity.PlayerIdentityId, player.PlayerId, auditablePlayerIdentity.PlayerIdentityName, ComparableName = auditablePlayerIdentity.ComparableName(), auditablePlayerIdentity.Team.TeamId }, transaction).ConfigureAwait(false); var serialisedPlayer = JsonConvert.SerializeObject(player); await _auditRepository.CreateAudit(new AuditRecord { Action = AuditAction.Create, MemberKey = memberKey, ActorName = memberName, EntityUri = player.EntityUri, State = serialisedPlayer, RedactedState = serialisedPlayer, AuditDate = DateTime.UtcNow }, transaction).ConfigureAwait(false); _logger.Info(GetType(), LoggingTemplates.Created, player, memberName, memberKey, GetType(), nameof(CreateOrMatchPlayerIdentity)); player.PlayerIdentities.Clear(); auditablePlayerIdentity.Player = player; return(auditablePlayerIdentity); }