From 3c7e34bce87b25de093b8e67a77b212fe382e50f Mon Sep 17 00:00:00 2001 From: ilitirit Date: Fri, 14 Apr 2023 01:41:19 +0200 Subject: [PATCH] Refactor user name generation --- .../Consumers/DutyCompletionResultConsumer.cs | 65 +++++++++++++++++-- .../Controllers/ExpedienceController.cs | 59 ++++------------- Expedience.Api/UserNameGenerator.cs | 14 +++- 3 files changed, 83 insertions(+), 55 deletions(-) diff --git a/Expedience.Api/Consumers/DutyCompletionResultConsumer.cs b/Expedience.Api/Consumers/DutyCompletionResultConsumer.cs index fe829e5..6fcc175 100644 --- a/Expedience.Api/Consumers/DutyCompletionResultConsumer.cs +++ b/Expedience.Api/Consumers/DutyCompletionResultConsumer.cs @@ -1,5 +1,6 @@ using System; using Expedience.Infrastructure; +using Expedience.Infrastructure.Concurrency; using Expedience.Infrastructure.Models; using MassTransit; @@ -9,12 +10,14 @@ namespace Expedience.Api.Consumers { private readonly ILogger _logger; private readonly IServiceScopeFactory _serviceScopeFactory; - + private readonly IDistributedLock _distributedLock; public DutyCompletionResultConsumer(ILogger logger, + IDistributedLock distributedLock, IServiceScopeFactory serviceScopeFactory) { _logger = logger; _serviceScopeFactory = serviceScopeFactory; + _distributedLock = distributedLock; } public async Task Consume(ConsumeContext context) @@ -33,12 +36,11 @@ namespace Expedience.Api.Consumers using var scope = _serviceScopeFactory.CreateScope(); using var dbContext = scope.ServiceProvider.GetRequiredService(); - var user = dbContext.Users.FirstOrDefault(x => x.UserHash == userHash && x.WorldId == worldId); + var user = dbContext.Users.FirstOrDefault(x => x.UserHash == userHash && x.WorldId == worldId) ?? + await CreateUser(dbContext, worldId, userHash, CancellationToken.None); + if (user == null) - { - _logger.LogError("No user found for World {worldId} and Hash {userHash}", worldId, userHash); return; - } var completionResult = new DutyCompletionResult { @@ -89,5 +91,58 @@ namespace Expedience.Api.Consumers _logger.LogInformation("Consumed message {uploadId} from user {userHash}", message.UploadId, userHash); } + + private async Task CreateUser(ExpedienceContext dbContext, int worldId, string userHash, CancellationToken cancellationToken) + { + var lockKey = $"{worldId}-{userHash}"; + if (_distributedLock.AcquireLock(lockKey, TimeSpan.FromSeconds(10), TimeSpan.FromMinutes(4))) + { + try + { + var user = dbContext.Users.FirstOrDefault(x => x.UserHash == userHash && x.WorldId == worldId); + if (user == null) + { + string userName; + var isDuplicate = false; + do + { + userName = UserNameGenerator.Generate(); + isDuplicate = dbContext.Users.Any(x => x.WorldId == worldId && x.UserName == userName); + await Task.Delay(20, cancellationToken); // Don't hog the CPU + } + while (isDuplicate == true); + + user = new User + { + UserHash = userHash, + WorldId = worldId, + UserName = userName, + CreatedAt = DateTime.UtcNow, + }; + + dbContext.Users.Add(user); + await dbContext.SaveChangesAsync(cancellationToken); + _logger.LogInformation("Created user for World {worldId} and Hash {userHash}: {userName}", worldId, userHash, user.UserName); + } + + return user; + + } + catch (Exception ex) + { + _logger.LogError(ex, "Error obtaining user name for World Id {worldId} and hash {userHash}: {errorMessage}", worldId, userHash, ex.Message); + } + finally + { + _distributedLock.ReleaseLock(lockKey); + } + } + else + { + _logger.LogError("Could not acquire lock for {lockKey}", lockKey); + } + + return null; + } } } diff --git a/Expedience.Api/Controllers/ExpedienceController.cs b/Expedience.Api/Controllers/ExpedienceController.cs index 5c918b7..067f574 100644 --- a/Expedience.Api/Controllers/ExpedienceController.cs +++ b/Expedience.Api/Controllers/ExpedienceController.cs @@ -1,12 +1,11 @@ using System; -using System.Net; using System.Text.Json; using Expedience.Api.Encryption; using Expedience.Infrastructure; using Expedience.Infrastructure.Concurrency; -using Expedience.Infrastructure.Models; using MassTransit; using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; namespace Expedience.Api.Controllers { @@ -20,8 +19,8 @@ namespace Expedience.Api.Controllers private readonly IDistributedLock _distributedLock; private readonly IServiceScopeFactory _serviceScopeFactory; - public ExpedienceController(ILogger logger, - IPublishEndpoint publisher, + public ExpedienceController(ILogger logger, + IPublishEndpoint publisher, IDecryptor decrypytor, IDistributedLock distributedLock, IServiceScopeFactory serviceScopeFactory) @@ -58,54 +57,20 @@ namespace Expedience.Api.Controllers [HttpGet("UserName/{worldId}/{userHash}")] public async Task GetUserName(int worldId, string userHash, CancellationToken cancellationToken) { - using var scope = _serviceScopeFactory.CreateScope(); - using var dbContext = scope.ServiceProvider.GetRequiredService(); - - var lockKey = $"{worldId}-{userHash}"; - if (_distributedLock.AcquireLock(lockKey, TimeSpan.FromSeconds(10), TimeSpan.FromMinutes(4))) + try { - try - { - var user = dbContext.Users.FirstOrDefault(x => x.UserHash == userHash && x.WorldId == worldId); - if (user == null) - { - string userName; - var isDuplicate = false; - do - { - userName = UserNameGenerator.Generate(); - isDuplicate = dbContext.Users.Any(x => x.WorldId == worldId && x.UserName == userName); - await Task.Delay(20, cancellationToken); // Don't hog the CPU - } - while (isDuplicate == true); - - user = new User - { - UserHash = userHash, - WorldId = worldId, - UserName = userName, - CreatedAt = DateTime.UtcNow, - }; - - dbContext.Users.Add(user); - await dbContext.SaveChangesAsync(cancellationToken); - } + using var scope = _serviceScopeFactory.CreateScope(); + using var dbContext = scope.ServiceProvider.GetRequiredService(); + var user = await dbContext.Users.FirstOrDefaultAsync(x => x.UserHash == userHash && x.WorldId == worldId, cancellationToken); + if (user != null) return Ok(user.UserName); - - } - catch (Exception ex) - { - _logger.LogError(ex, "Error obtaining user name for World Id {worldId} and hash {userHash}: {errorMessage}", worldId, userHash, ex.Message); - } - finally - { - _distributedLock.ReleaseLock(lockKey); - } + else + return NotFound(); } - else + catch (Exception ex) { - _logger.LogError("Could not acquire lock for {lockKey}", lockKey); + _logger.LogError(ex, "Error obtaining user name for World Id {worldId} and hash {userHash}: {errorMessage}", worldId, userHash, ex.Message); } return StatusCode(500); diff --git a/Expedience.Api/UserNameGenerator.cs b/Expedience.Api/UserNameGenerator.cs index 4815bb5..6e7c25c 100644 --- a/Expedience.Api/UserNameGenerator.cs +++ b/Expedience.Api/UserNameGenerator.cs @@ -15,12 +15,18 @@ namespace Expedience.Api { var firstIndex = _random.Next(_adjectives.Count); var lastIndex = _random.Next(_names.Count); + var suffixIndex = _random.Next(_romanNumerals.Count); var firstName = _adjectives[firstIndex]; var lastName = _names[lastIndex]; - return $"{firstName} {lastName}"; + var suffix = _romanNumerals[suffixIndex]; + + var name = $"{firstName} {lastName}"; + if (suffix != string.Empty) name += " " + suffix; + + return name; } - public static readonly List _names = new List() + private static readonly List _names = new List() { "Strife", "Valentine", @@ -224,7 +230,7 @@ namespace Expedience.Api "Cidolfus" }; - public static List _adjectives = new List + private static List _adjectives = new List { "Adventurous", "Affectionate", @@ -476,5 +482,7 @@ namespace Expedience.Api "Zealous", "Zest" }; + + private static List _romanNumerals = new List { "", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X", "XI", "XII", "XIII", "XIV", "XV", "XVI", "XVII", "XVIII", "XIX", "XX" }; } }