Added ability to update config via API at runtime. wip on customizing config via UI

This commit is contained in:
Henry Hobbs 2025-05-17 22:26:58 -04:00
parent 2484f6674b
commit 91afb0dd8e
8 changed files with 69 additions and 29 deletions

View File

@ -1,6 +1,7 @@
@page "/"
@using AccessQueuePlayground.Models
@using AccessQueuePlayground.Services
@using AccessQueueService.Models;
@using BlazorBootstrap
@inject IAccessQueueManager Manager
@ -12,7 +13,8 @@
<p>
<b>Expiration Seconds:</b> @Config.ExpirationSeconds,
<b>Activity Seconds:</b> @Config.ActivitySeconds,
<b>Capacity Limit:</b> @Config.CapacityLimit
<b>Capacity Limit:</b> @Config.CapacityLimit,
<b>Rolling Expiration:</b> @Config.RollingExpiration
</p>
}
<p>

View File

@ -1,10 +0,0 @@
namespace AccessQueuePlayground.Models
{
public class AccessQueueConfig
{
public int ActivitySeconds { get; set; }
public int ExpirationSeconds { get; set; }
public int CapacityLimit { get; set; }
}
}

View File

@ -29,12 +29,12 @@ namespace AccessQueuePlayground.Services
public AccessQueueStatus GetStatus() => _status;
public AccessQueueConfig GetConfig() => new AccessQueueConfig
public AccessQueueConfig GetConfig() => _accessService.GetConfiguration();
public void UpdateConfig(AccessQueueConfig config)
{
ActivitySeconds = _config.GetValue<int>("AccessQueue:ActivitySeconds"),
ExpirationSeconds = _config.GetValue<int>("AccessQueue:ExpirationSeconds"),
CapacityLimit = _config.GetValue<int>("AccessQueue:CapacityLimit")
};
_accessService.UpdateConfiguration(config);
}
public Guid AddUser()
{

View File

@ -1,4 +1,5 @@
using AccessQueuePlayground.Models;
using AccessQueueService.Models;
namespace AccessQueuePlayground.Services
{
@ -6,6 +7,7 @@ namespace AccessQueuePlayground.Services
{
public event Action? StatusUpdated;
public AccessQueueConfig GetConfig();
public void UpdateConfig(AccessQueueConfig config);
public Task RecalculateStatus();
public AccessQueueStatus GetStatus();
public Guid AddUser();

View File

@ -28,5 +28,18 @@ namespace AccessQueueService.Controllers
{
return await _accessService.RevokeAccess(id);
}
[HttpGet("configuration")]
public ActionResult<AccessQueueConfig> GetConfiguration()
{
return Ok(_accessService.GetConfiguration());
}
[HttpPost("configuration")]
public IActionResult UpdateConfiguration([FromBody] AccessQueueConfig config)
{
_accessService.PatchConfiguration(config);
return NoContent();
}
}
}

View File

@ -0,0 +1,15 @@
namespace AccessQueueService.Models
{
public class AccessQueueConfig
{
public int? CapacityLimit { get; set; }
public int? ActivitySeconds { get; set; }
public int? ExpirationSeconds { get; set; }
public bool? RollingExpiration { get; set; }
public AccessQueueConfig Clone()
{
return (AccessQueueConfig)this.MemberwiseClone();
}
}
}

View File

@ -12,37 +12,52 @@ namespace AccessQueueService.Services
private readonly ILogger<AccessService> _logger;
private readonly SemaphoreSlim _queueLock = new(1, 1);
private readonly int EXP_SECONDS;
private readonly int ACT_SECONDS;
private readonly int CAPACITY_LIMIT;
private readonly bool ROLLING_EXPIRATION;
private AccessQueueConfig _config;
public AccessService(IConfiguration configuration, IAccessQueueRepo accessQueueRepo, ILogger<AccessService> logger)
{
_configuration = configuration;
_accessQueueRepo = accessQueueRepo;
_logger = logger;
EXP_SECONDS = _configuration.GetValue<int>("AccessQueue:ExpirationSeconds");
ACT_SECONDS = _configuration.GetValue<int>("AccessQueue:ActivitySeconds");
CAPACITY_LIMIT = _configuration.GetValue<int>("AccessQueue:CapacityLimit");
ROLLING_EXPIRATION = _configuration.GetValue<bool>("AccessQueue:RollingExpiration");
_config = new AccessQueueConfig
{
ExpirationSeconds = _configuration.GetValue<int>("AccessQueue:ExpirationSeconds"),
ActivitySeconds = _configuration.GetValue<int>("AccessQueue:ActivitySeconds"),
CapacityLimit = _configuration.GetValue<int>("AccessQueue:CapacityLimit"),
RollingExpiration = _configuration.GetValue<bool>("AccessQueue:RollingExpiration")
};
}
public AccessQueueConfig GetConfiguration() => _config.Clone();
public void UpdateConfiguration(AccessQueueConfig config)
{
_config = config.Clone();
}
public void PatchConfiguration(AccessQueueConfig partialConfig)
{
if (partialConfig.CapacityLimit.HasValue) _config.CapacityLimit = partialConfig.CapacityLimit.Value;
if (partialConfig.ActivitySeconds.HasValue) _config.ActivitySeconds = partialConfig.ActivitySeconds.Value;
if (partialConfig.ExpirationSeconds.HasValue) _config.ExpirationSeconds = partialConfig.ExpirationSeconds.Value;
if (partialConfig.RollingExpiration.HasValue) _config.RollingExpiration = partialConfig.RollingExpiration.Value;
}
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(-_config.ActivitySeconds.Value));
public int QueueCount => _accessQueueRepo.GetQueueCount();
public async Task<AccessResponse> RequestAccess(string userId)
{
await _queueLock.WaitAsync();
try
{
var hasCapacity = !_accessQueueRepo.DidDequeueUntilFull(ACT_SECONDS, EXP_SECONDS, CAPACITY_LIMIT);
var hasCapacity = !_accessQueueRepo.DidDequeueUntilFull(
_config.ActivitySeconds.Value,
_config.ExpirationSeconds.Value,
_config.CapacityLimit.Value);
var existingTicket = _accessQueueRepo.GetTicket(userId);
if (existingTicket != null && existingTicket.ExpiresOn > DateTime.UtcNow)
{
// Already has access
var expiresOn = existingTicket.ExpiresOn;
if (ROLLING_EXPIRATION)
if (_config.RollingExpiration.Value)
{
expiresOn = DateTime.UtcNow.AddSeconds(EXP_SECONDS);
expiresOn = DateTime.UtcNow.AddSeconds(_config.ExpirationSeconds.Value);
}
_accessQueueRepo.UpsertTicket(new AccessTicket
{
@ -62,7 +77,7 @@ namespace AccessQueueService.Services
var accessTicket = new AccessTicket
{
UserId = userId,
ExpiresOn = DateTime.UtcNow.AddSeconds(EXP_SECONDS),
ExpiresOn = DateTime.UtcNow.AddSeconds(_config.ExpirationSeconds.Value),
LastActive = DateTime.UtcNow
};
_accessQueueRepo.UpsertTicket(accessTicket);

View File

@ -7,5 +7,8 @@ namespace AccessQueueService.Services
public Task<AccessResponse> RequestAccess(string userId);
public Task<bool> RevokeAccess(string userId);
public Task<int> DeleteExpiredTickets();
public AccessQueueConfig GetConfiguration();
public void UpdateConfiguration(AccessQueueConfig config);
public void PatchConfiguration(AccessQueueConfig partialConfig);
}
}