diff --git a/AccessQueuePlayground/Components/Layout/MainLayout.razor b/AccessQueuePlayground/Components/Layout/MainLayout.razor
index e21aaad..ee658cd 100644
--- a/AccessQueuePlayground/Components/Layout/MainLayout.razor
+++ b/AccessQueuePlayground/Components/Layout/MainLayout.razor
@@ -14,6 +14,9 @@
About
+
+ Config
+
Source
diff --git a/AccessQueuePlayground/Components/Pages/Config.razor b/AccessQueuePlayground/Components/Pages/Config.razor
new file mode 100644
index 0000000..05288e2
--- /dev/null
+++ b/AccessQueuePlayground/Components/Pages/Config.razor
@@ -0,0 +1,96 @@
+@page "/config"
+@inject AccessQueuePlayground.Services.IAccessQueueManager QueueManager
+@using BlazorBootstrap
+
+Access Queue Configuration
+
+
+
+
+
+
+
+ @if (!isCapacityLimitValid)
+ {
+
Please enter a positive integer.
+ }
+
+
+
+
+ @if (!isActivitySecondsValid)
+ {
+
Please enter a positive integer.
+ }
+
+
+
+
+ @if (!isExpirationSecondsValid)
+ {
+
Please enter a positive integer.
+ }
+
+
+
+
+
+ @if (successMessage != null)
+ {
+ @successMessage
+ }
+
+
+@code {
+ private ConfigModel config = new();
+ private bool isCapacityLimitValid = true;
+ private bool isActivitySecondsValid = true;
+ private bool isExpirationSecondsValid = true;
+ private string? successMessage;
+
+ protected override void OnInitialized()
+ {
+ var current = QueueManager.GetConfig();
+ config = new ConfigModel
+ {
+ ActivitySeconds = (current.ActivitySeconds ?? 0).ToString(),
+ CapacityLimit = (current.CapacityLimit ?? 0).ToString(),
+ ExpirationSeconds = (current.ExpirationSeconds ?? 0).ToString(),
+ RollingExpiration = current.RollingExpiration ?? false
+ };
+ ValidateInputs();
+ }
+
+ private bool IsFormValid => isCapacityLimitValid && isActivitySecondsValid && isExpirationSecondsValid;
+
+ private void ValidateInputs()
+ {
+ isCapacityLimitValid = int.TryParse(config.CapacityLimit, out var cap) && cap > 0;
+ isActivitySecondsValid = int.TryParse(config.ActivitySeconds, out var act) && act > 0;
+ isExpirationSecondsValid = int.TryParse(config.ExpirationSeconds, out var exp) && exp > 0;
+ }
+
+ private async Task HandleValidSubmit()
+ {
+ successMessage = null;
+ ValidateInputs();
+ if (!IsFormValid)
+ return;
+ await Task.Run(() => QueueManager.UpdateConfig(new ()
+ {
+ ActivitySeconds = int.Parse(config.ActivitySeconds),
+ CapacityLimit = int.Parse(config.CapacityLimit),
+ ExpirationSeconds = int.Parse(config.ExpirationSeconds),
+ RollingExpiration = config.RollingExpiration
+ }));
+ successMessage = "Configuration updated successfully.";
+ }
+
+ public class ConfigModel
+ {
+ public string CapacityLimit { get; set; } = "";
+ public string ActivitySeconds { get; set; } = "";
+ public string ExpirationSeconds { get; set; } = "";
+ public bool RollingExpiration { get; set; }
+ }
+}
diff --git a/AccessQueuePlayground/Components/Pages/Home.razor b/AccessQueuePlayground/Components/Pages/Home.razor
index d29a360..2533d95 100644
--- a/AccessQueuePlayground/Components/Pages/Home.razor
+++ b/AccessQueuePlayground/Components/Pages/Home.razor
@@ -1,6 +1,7 @@
@page "/"
@using AccessQueuePlayground.Models
@using AccessQueuePlayground.Services
+@using AccessQueueService.Models;
@using BlazorBootstrap
@inject IAccessQueueManager Manager
@@ -12,7 +13,8 @@
Expiration Seconds: @Config.ExpirationSeconds,
Activity Seconds: @Config.ActivitySeconds,
- Capacity Limit: @Config.CapacityLimit
+ Capacity Limit: @Config.CapacityLimit,
+ Rolling Expiration: @Config.RollingExpiration
}
diff --git a/AccessQueuePlayground/Models/AccessQueueConfig.cs b/AccessQueuePlayground/Models/AccessQueueConfig.cs
deleted file mode 100644
index 3bf62ef..0000000
--- a/AccessQueuePlayground/Models/AccessQueueConfig.cs
+++ /dev/null
@@ -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; }
-
- }
-}
diff --git a/AccessQueuePlayground/Services/AccessQueueManager.cs b/AccessQueuePlayground/Services/AccessQueueManager.cs
index 5412821..4f17277 100644
--- a/AccessQueuePlayground/Services/AccessQueueManager.cs
+++ b/AccessQueuePlayground/Services/AccessQueueManager.cs
@@ -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("AccessQueue:ActivitySeconds"),
- ExpirationSeconds = _config.GetValue("AccessQueue:ExpirationSeconds"),
- CapacityLimit = _config.GetValue("AccessQueue:CapacityLimit")
- };
+ _accessService.UpdateConfiguration(config);
+ }
public Guid AddUser(bool isActive)
{
diff --git a/AccessQueuePlayground/Services/IAccessQueueManager.cs b/AccessQueuePlayground/Services/IAccessQueueManager.cs
index fcdfd52..362955f 100644
--- a/AccessQueuePlayground/Services/IAccessQueueManager.cs
+++ b/AccessQueuePlayground/Services/IAccessQueueManager.cs
@@ -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(bool isActive);
diff --git a/AccessQueueService/Controllers/AccessController.cs b/AccessQueueService/Controllers/AccessController.cs
index f9fcc05..d1f4786 100644
--- a/AccessQueueService/Controllers/AccessController.cs
+++ b/AccessQueueService/Controllers/AccessController.cs
@@ -28,5 +28,18 @@ namespace AccessQueueService.Controllers
{
return await _accessService.RevokeAccess(id);
}
+
+ [HttpGet("configuration")]
+ public ActionResult GetConfiguration()
+ {
+ return Ok(_accessService.GetConfiguration());
+ }
+
+ [HttpPost("configuration")]
+ public IActionResult UpdateConfiguration([FromBody] AccessQueueConfig config)
+ {
+ _accessService.PatchConfiguration(config);
+ return NoContent();
+ }
}
}
diff --git a/AccessQueueService/Models/AccessQueueConfig.cs b/AccessQueueService/Models/AccessQueueConfig.cs
new file mode 100644
index 0000000..413caca
--- /dev/null
+++ b/AccessQueueService/Models/AccessQueueConfig.cs
@@ -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();
+ }
+ }
+}
diff --git a/AccessQueueService/Services/AccessService.cs b/AccessQueueService/Services/AccessService.cs
index 65d0f9a..891ab88 100644
--- a/AccessQueueService/Services/AccessService.cs
+++ b/AccessQueueService/Services/AccessService.cs
@@ -12,37 +12,52 @@ namespace AccessQueueService.Services
private readonly ILogger _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 logger)
{
_configuration = configuration;
_accessQueueRepo = accessQueueRepo;
_logger = logger;
- EXP_SECONDS = _configuration.GetValue("AccessQueue:ExpirationSeconds");
- ACT_SECONDS = _configuration.GetValue("AccessQueue:ActivitySeconds");
- CAPACITY_LIMIT = _configuration.GetValue("AccessQueue:CapacityLimit");
- ROLLING_EXPIRATION = _configuration.GetValue("AccessQueue:RollingExpiration");
+ _config = new AccessQueueConfig
+ {
+ ExpirationSeconds = _configuration.GetValue("AccessQueue:ExpirationSeconds"),
+ ActivitySeconds = _configuration.GetValue("AccessQueue:ActivitySeconds"),
+ CapacityLimit = _configuration.GetValue("AccessQueue:CapacityLimit"),
+ RollingExpiration = _configuration.GetValue("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("AccessQueue:ActivitySeconds")));
+ public int ActiveTicketsCount => _accessQueueRepo.GetActiveTicketsCount(DateTime.UtcNow.AddSeconds(-_config.ActivitySeconds.Value));
public int QueueCount => _accessQueueRepo.GetQueueCount();
public async Task 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);
diff --git a/AccessQueueService/Services/IAccessService.cs b/AccessQueueService/Services/IAccessService.cs
index 6b94042..35cdf3c 100644
--- a/AccessQueueService/Services/IAccessService.cs
+++ b/AccessQueueService/Services/IAccessService.cs
@@ -7,5 +7,8 @@ namespace AccessQueueService.Services
public Task RequestAccess(string userId);
public Task RevokeAccess(string userId);
public Task DeleteExpiredTickets();
+ public AccessQueueConfig GetConfiguration();
+ public void UpdateConfiguration(AccessQueueConfig config);
+ public void PatchConfiguration(AccessQueueConfig partialConfig);
}
}