2.1 Configuration POCOs (Mqtt, Storage, UrlFetch, ApiKey)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -71,7 +71,7 @@ Each type lives in `src/FrameProcessor/Domain/`. Tests in `tests/FrameProcessor.
|
|||||||
|
|
||||||
## Phase 2 — Configuration binding
|
## Phase 2 — Configuration binding
|
||||||
|
|
||||||
### [ ] 2.1 `MqttOptions`, `StorageOptions`, `UrlFetchOptions`, `ApiKeyOptions`
|
### [x] 2.1 `MqttOptions`, `StorageOptions`, `UrlFetchOptions`, `ApiKeyOptions`
|
||||||
- POCOs in `src/FrameProcessor/Configuration/`.
|
- POCOs in `src/FrameProcessor/Configuration/`.
|
||||||
- Bound from `appsettings.json` via `builder.Services.Configure<T>(...)`.
|
- Bound from `appsettings.json` via `builder.Services.Configure<T>(...)`.
|
||||||
- Validate on startup (`ValidateOnStart` + `IValidateOptions<T>` or DataAnnotations).
|
- Validate on startup (`ValidateOnStart` + `IValidateOptions<T>` or DataAnnotations).
|
||||||
|
|||||||
20
src/FrameProcessor/Configuration/ApiKeyOptions.cs
Normal file
20
src/FrameProcessor/Configuration/ApiKeyOptions.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using FrameProcessor.Domain;
|
||||||
|
|
||||||
|
namespace FrameProcessor.Configuration;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Wraps the shared API key from <c>appsettings.json</c>. The configured value is bound
|
||||||
|
/// from the top-level <c>ApiKey</c> key (which is a plain string in the JSON schema —
|
||||||
|
/// see SPEC.md §6.1) into <see cref="Value"/>; consumers should call <see cref="ToApiKey"/>
|
||||||
|
/// to obtain the constant-time-comparing <see cref="ApiKey"/> value type.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class ApiKeyOptions
|
||||||
|
{
|
||||||
|
public const string SectionName = "ApiKey";
|
||||||
|
|
||||||
|
[Required(AllowEmptyStrings = false)]
|
||||||
|
public string Value { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public ApiKey ToApiKey() => new(Value);
|
||||||
|
}
|
||||||
35
src/FrameProcessor/Configuration/MqttOptions.cs
Normal file
35
src/FrameProcessor/Configuration/MqttOptions.cs
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace FrameProcessor.Configuration;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Bound from the <c>Mqtt</c> section of <c>appsettings.json</c>. See SPEC.md §6.1.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class MqttOptions
|
||||||
|
{
|
||||||
|
public const string SectionName = "Mqtt";
|
||||||
|
|
||||||
|
[Required(AllowEmptyStrings = false)]
|
||||||
|
public string Host { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[Range(1, 65535)]
|
||||||
|
public int Port { get; set; } = 1883;
|
||||||
|
|
||||||
|
[Required(AllowEmptyStrings = false)]
|
||||||
|
public string ClientId { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public string? Username { get; set; }
|
||||||
|
|
||||||
|
public string? Password { get; set; }
|
||||||
|
|
||||||
|
public bool UseTls { get; set; }
|
||||||
|
|
||||||
|
[Required(AllowEmptyStrings = false)]
|
||||||
|
public string BaseTopic { get; set; } = "frames";
|
||||||
|
|
||||||
|
[Range(0, 2)]
|
||||||
|
public int PublishQos { get; set; } = 1;
|
||||||
|
|
||||||
|
[MinLength(1)]
|
||||||
|
public int[] RetryBackoffSeconds { get; set; } = Array.Empty<int>();
|
||||||
|
}
|
||||||
14
src/FrameProcessor/Configuration/StorageOptions.cs
Normal file
14
src/FrameProcessor/Configuration/StorageOptions.cs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace FrameProcessor.Configuration;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Bound from the <c>Storage</c> section of <c>appsettings.json</c>. See SPEC.md §6.1, §7.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class StorageOptions
|
||||||
|
{
|
||||||
|
public const string SectionName = "Storage";
|
||||||
|
|
||||||
|
[Required(AllowEmptyStrings = false)]
|
||||||
|
public string ImageDirectory { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
20
src/FrameProcessor/Configuration/UrlFetchOptions.cs
Normal file
20
src/FrameProcessor/Configuration/UrlFetchOptions.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace FrameProcessor.Configuration;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Bound from the <c>UrlFetch</c> section of <c>appsettings.json</c>. See SPEC.md §6.1, §8.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class UrlFetchOptions
|
||||||
|
{
|
||||||
|
public const string SectionName = "UrlFetch";
|
||||||
|
|
||||||
|
[Range(1, long.MaxValue)]
|
||||||
|
public long MaxBytes { get; set; } = 52_428_800L;
|
||||||
|
|
||||||
|
[Range(1, int.MaxValue)]
|
||||||
|
public int TimeoutSeconds { get; set; } = 30;
|
||||||
|
|
||||||
|
[Range(0, int.MaxValue)]
|
||||||
|
public int MaxRedirects { get; set; } = 3;
|
||||||
|
}
|
||||||
@@ -1,7 +1,29 @@
|
|||||||
|
using FrameProcessor.Configuration;
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
builder.Services.AddControllers();
|
builder.Services.AddControllers();
|
||||||
|
|
||||||
|
builder.Services.AddOptions<MqttOptions>()
|
||||||
|
.Bind(builder.Configuration.GetSection(MqttOptions.SectionName))
|
||||||
|
.ValidateDataAnnotations()
|
||||||
|
.ValidateOnStart();
|
||||||
|
|
||||||
|
builder.Services.AddOptions<StorageOptions>()
|
||||||
|
.Bind(builder.Configuration.GetSection(StorageOptions.SectionName))
|
||||||
|
.ValidateDataAnnotations()
|
||||||
|
.ValidateOnStart();
|
||||||
|
|
||||||
|
builder.Services.AddOptions<UrlFetchOptions>()
|
||||||
|
.Bind(builder.Configuration.GetSection(UrlFetchOptions.SectionName))
|
||||||
|
.ValidateDataAnnotations()
|
||||||
|
.ValidateOnStart();
|
||||||
|
|
||||||
|
builder.Services.AddOptions<ApiKeyOptions>()
|
||||||
|
.Configure<IConfiguration>((opts, cfg) => opts.Value = cfg[ApiKeyOptions.SectionName] ?? string.Empty)
|
||||||
|
.ValidateDataAnnotations()
|
||||||
|
.ValidateOnStart();
|
||||||
|
|
||||||
var app = builder.Build();
|
var app = builder.Build();
|
||||||
|
|
||||||
app.MapControllers();
|
app.MapControllers();
|
||||||
|
|||||||
Reference in New Issue
Block a user