#8 use string type for ID to make implementation more agnostic to different ID types

This commit is contained in:
henry 2025-05-15 19:52:48 -04:00
parent a540221b7f
commit 18a5a64d4e
8 changed files with 45 additions and 46 deletions

View File

@ -63,7 +63,7 @@ namespace AccessQueuePlayground.Services
{ {
if (user.Active) if (user.Active)
{ {
user.LatestResponse = await _accessService.RequestAccess(user.Id); user.LatestResponse = await _accessService.RequestAccess(user.Id.ToString());
if (user.LatestResponse?.HasAccess ?? false) if (user.LatestResponse?.HasAccess ?? false)
{ {
newStatus.AccessUsers.Add(user); newStatus.AccessUsers.Add(user);
@ -95,7 +95,7 @@ namespace AccessQueuePlayground.Services
var user = _users[userId]; var user = _users[userId];
user.Active = false; user.Active = false;
user.LatestResponse = null; user.LatestResponse = null;
_accessService.RevokeAccess(userId); _accessService.RevokeAccess(userId.ToString());
} }
public void RevokeAllAccess() public void RevokeAllAccess()

View File

@ -17,14 +17,14 @@ namespace AccessQueueService.Controllers
[HttpGet] [HttpGet]
[Route("{id}")] [Route("{id}")]
public async Task<AccessResponse> Get(Guid id) public async Task<AccessResponse> Get(string id)
{ {
return await _accessService.RequestAccess(id); return await _accessService.RequestAccess(id);
} }
[HttpDelete] [HttpDelete]
[Route("{id}")] [Route("{id}")]
public async Task<bool> Delete(Guid id) public async Task<bool> Delete(string id)
{ {
return await _accessService.RevokeAccess(id); return await _accessService.RevokeAccess(id);
} }

View File

@ -8,12 +8,12 @@ namespace AccessQueueService.Data
public int GetUnexpiredTicketsCount(); public int GetUnexpiredTicketsCount();
public int GetActiveTicketsCount(DateTime activeCutoff); public int GetActiveTicketsCount(DateTime activeCutoff);
public int GetQueueCount(); public int GetQueueCount();
public AccessTicket? GetTicket(Guid userId); public AccessTicket? GetTicket(string userId);
public void UpsertTicket(AccessTicket ticket); public void UpsertTicket(AccessTicket ticket);
public int GetRequestsAhead(Guid userId); public int GetRequestsAhead(string userId);
public void Enqueue(AccessTicket ticket); public void Enqueue(AccessTicket ticket);
public int DeleteExpiredTickets(); public int DeleteExpiredTickets();
public bool RemoveUser(Guid userId); public bool RemoveUser(string userId);
public bool DidDequeueUntilFull(int activeSeconds, int expirationSeconds, int capacityLimit); public bool DidDequeueUntilFull(int activeSeconds, int expirationSeconds, int capacityLimit);

View File

@ -5,8 +5,8 @@ namespace AccessQueueService.Data
{ {
public class TakeANumberAccessQueueRepo : IAccessQueueRepo public class TakeANumberAccessQueueRepo : IAccessQueueRepo
{ {
private readonly Dictionary<Guid, AccessTicket> _accessTickets = []; private readonly Dictionary<string, AccessTicket> _accessTickets = [];
private readonly Dictionary<Guid, ulong> _queueNumbers = []; private readonly Dictionary<string, ulong> _queueNumbers = [];
private readonly Dictionary<ulong, AccessTicket> _accessQueue = []; private readonly Dictionary<ulong, AccessTicket> _accessQueue = [];
private ulong _nowServing = 0; private ulong _nowServing = 0;
@ -16,7 +16,7 @@ namespace AccessQueueService.Data
public int GetActiveTicketsCount(DateTime activeCutoff) => _accessTickets public int GetActiveTicketsCount(DateTime activeCutoff) => _accessTickets
.Count(t => t.Value.ExpiresOn > DateTime.UtcNow && t.Value.LastActive > activeCutoff); .Count(t => t.Value.ExpiresOn > DateTime.UtcNow && t.Value.LastActive > activeCutoff);
public int GetQueueCount() => (int)(_nextUnusedTicket - _nowServing); public int GetQueueCount() => (int)(_nextUnusedTicket - _nowServing);
public int GetRequestsAhead(Guid userId) public int GetRequestsAhead(string userId)
{ {
if(_queueNumbers.TryGetValue(userId, out var queueNumber)) if(_queueNumbers.TryGetValue(userId, out var queueNumber))
{ {
@ -50,7 +50,7 @@ namespace AccessQueueService.Data
return count; return count;
} }
public void RemoveUser(Guid userId) public void RemoveUser(string userId)
{ {
_accessTickets.Remove(userId); _accessTickets.Remove(userId);
} }
@ -90,7 +90,7 @@ namespace AccessQueueService.Data
return filledSpots == openSpots; return filledSpots == openSpots;
} }
public AccessTicket? GetTicket(Guid userId) public AccessTicket? GetTicket(string userId)
{ {
return _accessTickets.TryGetValue(userId, out var ticket) ? ticket : null; return _accessTickets.TryGetValue(userId, out var ticket) ? ticket : null;
} }
@ -100,7 +100,7 @@ namespace AccessQueueService.Data
_accessTickets[ticket.UserId] = ticket; _accessTickets[ticket.UserId] = ticket;
} }
bool IAccessQueueRepo.RemoveUser(Guid userId) bool IAccessQueueRepo.RemoveUser(string userId)
{ {
if(_queueNumbers.TryGetValue(userId, out var queueNumber)) if(_queueNumbers.TryGetValue(userId, out var queueNumber))
{ {

View File

@ -2,7 +2,7 @@
{ {
public class AccessTicket public class AccessTicket
{ {
public Guid UserId { get; set; } public string UserId { get; set; }
public DateTime ExpiresOn { get; set; } public DateTime ExpiresOn { get; set; }
public DateTime LastActive { get; set; } public DateTime LastActive { get; set; }
} }

View File

@ -25,7 +25,7 @@ namespace AccessQueueService.Services
public int UnexpiredTicketsCount => _accessQueueRepo.GetUnexpiredTicketsCount(); public int UnexpiredTicketsCount => _accessQueueRepo.GetUnexpiredTicketsCount();
public int ActiveTicketsCount => _accessQueueRepo.GetActiveTicketsCount(DateTime.UtcNow.AddSeconds(-_configuration.GetValue<int>("AccessQueue:ActivitySeconds"))); public int ActiveTicketsCount => _accessQueueRepo.GetActiveTicketsCount(DateTime.UtcNow.AddSeconds(-_configuration.GetValue<int>("AccessQueue:ActivitySeconds")));
public int QueueCount => _accessQueueRepo.GetQueueCount(); public int QueueCount => _accessQueueRepo.GetQueueCount();
public async Task<AccessResponse> RequestAccess(Guid userId) public async Task<AccessResponse> RequestAccess(string userId)
{ {
await _queueLock.WaitAsync(); await _queueLock.WaitAsync();
try try
@ -93,7 +93,7 @@ namespace AccessQueueService.Services
} }
} }
public async Task<bool> RevokeAccess(Guid userId) public async Task<bool> RevokeAccess(string userId)
{ {
await _queueLock.WaitAsync(); await _queueLock.WaitAsync();
try try

View File

@ -4,8 +4,8 @@ namespace AccessQueueService.Services
{ {
public interface IAccessService public interface IAccessService
{ {
public Task<AccessResponse> RequestAccess(Guid userId); public Task<AccessResponse> RequestAccess(string userId);
public Task<bool> RevokeAccess(Guid userId); public Task<bool> RevokeAccess(string userId);
public int DeleteExpiredTickets(); public int DeleteExpiredTickets();
} }
} }

View File

@ -41,7 +41,7 @@ namespace AccessQueueServiceTests
[Fact] [Fact]
public async Task RequestAccess_ShouldGrantAccess_WhenCapacityIsAvailable() public async Task RequestAccess_ShouldGrantAccess_WhenCapacityIsAvailable()
{ {
var userId = Guid.NewGuid(); var userId = "user";
var response = await _accessService.RequestAccess(userId); var response = await _accessService.RequestAccess(userId);
@ -56,7 +56,7 @@ namespace AccessQueueServiceTests
[Fact] [Fact]
public async Task RequestAccess_ShouldReturnAccessResponse_WhenUserAlreadyHasTicket() public async Task RequestAccess_ShouldReturnAccessResponse_WhenUserAlreadyHasTicket()
{ {
var userId = Guid.NewGuid(); var userId = "user";
await _accessService.RequestAccess(userId); await _accessService.RequestAccess(userId);
var response = await _accessService.RequestAccess(userId); var response = await _accessService.RequestAccess(userId);
@ -74,9 +74,9 @@ namespace AccessQueueServiceTests
{ {
for (int i = 0; i < CAP_LIMIT * 2; i++) // Fill double capacity for (int i = 0; i < CAP_LIMIT * 2; i++) // Fill double capacity
{ {
await _accessService.RequestAccess(Guid.NewGuid()); await _accessService.RequestAccess(Guid.NewGuid().ToString());
} }
var userId = Guid.NewGuid(); var userId = "user";
var response = await _accessService.RequestAccess(userId); var response = await _accessService.RequestAccess(userId);
@ -92,7 +92,7 @@ namespace AccessQueueServiceTests
[Fact] [Fact]
public async Task RevokeAccess_ShouldReturnTrue_WhenUserHasAccess() public async Task RevokeAccess_ShouldReturnTrue_WhenUserHasAccess()
{ {
var userId = Guid.NewGuid(); var userId = "user";
await _accessService.RequestAccess(userId); await _accessService.RequestAccess(userId);
var result = await _accessService.RevokeAccess(userId); var result = await _accessService.RevokeAccess(userId);
@ -103,7 +103,7 @@ namespace AccessQueueServiceTests
[Fact] [Fact]
public async Task RevokeAccess_ShouldReturnFalse_WhenUserDoesNotHaveAccess() public async Task RevokeAccess_ShouldReturnFalse_WhenUserDoesNotHaveAccess()
{ {
var userId = Guid.NewGuid(); var userId = "user";
var result = await _accessService.RevokeAccess(userId); var result = await _accessService.RevokeAccess(userId);
@ -113,12 +113,12 @@ namespace AccessQueueServiceTests
[Fact] [Fact]
public async Task RequestAccess_ShouldQueueUser_AfterAccessRevoked() public async Task RequestAccess_ShouldQueueUser_AfterAccessRevoked()
{ {
var userId = Guid.NewGuid(); var userId = "user";
await _accessService.RequestAccess(userId); await _accessService.RequestAccess(userId);
for (int i = 0; i < CAP_LIMIT; i++) // Fill remaining slots for (int i = 0; i < CAP_LIMIT; i++) // Fill remaining slots
{ {
await _accessService.RequestAccess(Guid.NewGuid()); await _accessService.RequestAccess(Guid.NewGuid().ToString());
} }
var response = await _accessService.RequestAccess(userId); // Request access before revoking var response = await _accessService.RequestAccess(userId); // Request access before revoking
@ -136,9 +136,9 @@ namespace AccessQueueServiceTests
{ {
for (int i = 0; i < CAP_LIMIT; i++) // Fill slots without awaiting for (int i = 0; i < CAP_LIMIT; i++) // Fill slots without awaiting
{ {
_ = _accessService.RequestAccess(Guid.NewGuid()); _ = _accessService.RequestAccess(Guid.NewGuid().ToString());
} }
var response = await _accessService.RequestAccess(Guid.NewGuid()); // Request access before revoking var response = await _accessService.RequestAccess(Guid.NewGuid().ToString()); // Request access before revoking
Assert.NotNull(response); Assert.NotNull(response);
Assert.False(response.HasAccess); Assert.False(response.HasAccess);
} }
@ -146,7 +146,7 @@ namespace AccessQueueServiceTests
[Fact] [Fact]
public async Task RequestAccess_ShouldUpdateExpirationTime_WhenRollingExpirationTrue() public async Task RequestAccess_ShouldUpdateExpirationTime_WhenRollingExpirationTrue()
{ {
var userId = Guid.NewGuid(); var userId = "user";
var initialResponse = await _accessService.RequestAccess(userId); var initialResponse = await _accessService.RequestAccess(userId);
await Task.Delay(ACT_MILLIS); await Task.Delay(ACT_MILLIS);
var updatedResponse = await _accessService.RequestAccess(userId); var updatedResponse = await _accessService.RequestAccess(userId);
@ -158,9 +158,9 @@ namespace AccessQueueServiceTests
{ {
for (int i = 0; i < CAP_LIMIT; i++) for (int i = 0; i < CAP_LIMIT; i++)
{ {
await _accessService.RequestAccess(Guid.NewGuid()); await _accessService.RequestAccess(Guid.NewGuid().ToString());
} }
var userId = Guid.NewGuid(); var userId = "user";
var response = await _accessService.RequestAccess(userId); var response = await _accessService.RequestAccess(userId);
Assert.False(response.HasAccess); Assert.False(response.HasAccess);
await Task.Delay(ACT_MILLIS); await Task.Delay(ACT_MILLIS);
@ -171,13 +171,13 @@ namespace AccessQueueServiceTests
[Fact] [Fact]
public async Task RequestAccess_ShouldRevokeAccess_WhenExpired() public async Task RequestAccess_ShouldRevokeAccess_WhenExpired()
{ {
var userId = Guid.NewGuid(); var userId = "user";
var response = await _accessService.RequestAccess(userId); var response = await _accessService.RequestAccess(userId);
Assert.True(response.HasAccess); Assert.True(response.HasAccess);
await Task.Delay(EXP_MILLIS); await Task.Delay(EXP_MILLIS);
for (int i = 0; i < CAP_LIMIT; i++) for (int i = 0; i < CAP_LIMIT; i++)
{ {
await _accessService.RequestAccess(Guid.NewGuid()); await _accessService.RequestAccess(Guid.NewGuid().ToString());
} }
response = await _accessService.RequestAccess(userId); response = await _accessService.RequestAccess(userId);
Assert.False(response.HasAccess); Assert.False(response.HasAccess);
@ -186,13 +186,13 @@ namespace AccessQueueServiceTests
[Fact] [Fact]
public async Task RequestAccess_ShouldRetailAccess_WhenNotExpired() public async Task RequestAccess_ShouldRetailAccess_WhenNotExpired()
{ {
var userId = Guid.NewGuid(); var userId = "user";
var response = await _accessService.RequestAccess(userId); var response = await _accessService.RequestAccess(userId);
Assert.True(response.HasAccess); Assert.True(response.HasAccess);
await Task.Delay(ACT_MILLIS); await Task.Delay(ACT_MILLIS);
for (int i = 0; i < CAP_LIMIT; i++) for (int i = 0; i < CAP_LIMIT; i++)
{ {
response = await _accessService.RequestAccess(Guid.NewGuid()); response = await _accessService.RequestAccess(Guid.NewGuid().ToString());
Assert.True(response.HasAccess); Assert.True(response.HasAccess);
} }
response = await _accessService.RequestAccess(userId); response = await _accessService.RequestAccess(userId);
@ -202,11 +202,11 @@ namespace AccessQueueServiceTests
[Fact] [Fact]
public async Task RequestAccess_ShouldProcessBulkRequests() public async Task RequestAccess_ShouldProcessBulkRequests()
{ {
var userId = Guid.NewGuid(); var userId = "user";
await _accessService.RequestAccess(userId); await _accessService.RequestAccess(userId);
for (int i = 0; i < BULK_COUNT; i++) for (int i = 0; i < BULK_COUNT; i++)
{ {
_ = _accessService.RequestAccess(Guid.NewGuid()); _ = _accessService.RequestAccess(Guid.NewGuid().ToString());
} }
var response = await _accessService.RequestAccess(userId); var response = await _accessService.RequestAccess(userId);
Assert.NotNull(response); Assert.NotNull(response);
@ -220,15 +220,14 @@ namespace AccessQueueServiceTests
for (int i = 0; i < CAP_LIMIT; i++) for (int i = 0; i < CAP_LIMIT; i++)
{ {
var elapsed = DateTime.UtcNow - start; var elapsed = DateTime.UtcNow - start;
Console.WriteLine($"Elapsed time: {elapsed.TotalSeconds} s: Adding {i}"); await _accessService.RequestAccess(Guid.NewGuid().ToString());
await _accessService.RequestAccess(Guid.NewGuid());
await Task.Delay(ACT_MILLIS / CAP_LIMIT); await Task.Delay(ACT_MILLIS / CAP_LIMIT);
} }
var users = new[] var users = new[]
{ {
Guid.NewGuid(), "user1",
Guid.NewGuid(), "user2",
Guid.NewGuid() "user3"
}; };
await _accessService.RequestAccess(users[0]); await _accessService.RequestAccess(users[0]);
@ -250,12 +249,12 @@ namespace AccessQueueServiceTests
{ {
for (int i = 0; i < CAP_LIMIT; i++) for (int i = 0; i < CAP_LIMIT; i++)
{ {
await _accessService.RequestAccess(Guid.NewGuid()); await _accessService.RequestAccess(Guid.NewGuid().ToString());
} }
var id1 = Guid.NewGuid(); var id1 = "user1";
var id2 = Guid.NewGuid(); var id2 = "user2";
var id3 = Guid.NewGuid(); var id3 = "user3";
var response1 = await _accessService.RequestAccess(id1); var response1 = await _accessService.RequestAccess(id1);
var response2 = await _accessService.RequestAccess(id2); var response2 = await _accessService.RequestAccess(id2);