diff --git a/src/AzureAppConfigurationEmulator/Handlers/KeyHandler.cs b/src/AzureAppConfigurationEmulator/Handlers/KeyHandler.cs index 04d1b18..f8682bd 100644 --- a/src/AzureAppConfigurationEmulator/Handlers/KeyHandler.cs +++ b/src/AzureAppConfigurationEmulator/Handlers/KeyHandler.cs @@ -12,6 +12,7 @@ public class KeyHandler public static async Task> List( [FromServices] IConfigurationSettingRepository repository, [FromQuery] string name = KeyFilter.Any, + [FromHeader(Name = "Accept-Datetime")] DateTimeOffset? acceptDatetime = default, CancellationToken cancellationToken = default) { if (name != KeyFilter.Any) @@ -27,11 +28,11 @@ public static async Task setting.Key) .Distinct() .ToListAsync(cancellationToken); - return new KeySetResult(keys); + return new KeySetResult(keys, acceptDatetime); } } diff --git a/src/AzureAppConfigurationEmulator/Handlers/KeyValueHandler.cs b/src/AzureAppConfigurationEmulator/Handlers/KeyValueHandler.cs index 79f1e4a..e8e1b13 100644 --- a/src/AzureAppConfigurationEmulator/Handlers/KeyValueHandler.cs +++ b/src/AzureAppConfigurationEmulator/Handlers/KeyValueHandler.cs @@ -24,7 +24,7 @@ public static async Task> List( [FromServices] IConfigurationSettingRepository repository, [FromQuery] string key = KeyFilter.Any, [FromQuery] string label = LabelFilter.Any, + [FromHeader(Name = "Accept-Datetime")] DateTimeOffset? acceptDatetime = default, CancellationToken cancellationToken = default) { if (key != KeyFilter.Any) @@ -125,9 +127,9 @@ public static async Task> Set( @@ -145,7 +147,7 @@ public static async Task> List( [FromServices] IConfigurationSettingRepository repository, [FromQuery] string name = LabelFilter.Any, + [FromHeader(Name = "Accept-Datetime")] DateTimeOffset? acceptDatetime = default, CancellationToken cancellationToken = default) { if (name != LabelFilter.Any) @@ -27,7 +28,7 @@ public static async Task setting.Label) .Distinct() .ToListAsync(cancellationToken); diff --git a/src/AzureAppConfigurationEmulator/Repositories/ConfigurationSettingRepository.cs b/src/AzureAppConfigurationEmulator/Repositories/ConfigurationSettingRepository.cs index aacaa60..81037d9 100644 --- a/src/AzureAppConfigurationEmulator/Repositories/ConfigurationSettingRepository.cs +++ b/src/AzureAppConfigurationEmulator/Repositories/ConfigurationSettingRepository.cs @@ -48,9 +48,10 @@ public async Task AddAsync( public async IAsyncEnumerable Get( string key = KeyFilter.Any, string label = LabelFilter.Any, + DateTimeOffset? moment = default, [EnumeratorCancellation] CancellationToken cancellationToken = default) { - var text = "SELECT etag, key, label, content_type, value, last_modified, locked, tags FROM configuration_settings"; + var text = $"SELECT etag, key, label, content_type, value, last_modified, locked, tags FROM {(moment is not null ? "configuration_settings_history" : "configuration_settings")}"; var parameters = new List(); @@ -99,6 +100,13 @@ public async IAsyncEnumerable Get( outers.Add($"({string.Join(" OR ", inners)})"); } + if (moment is not null) + { + parameters.Add(ParameterFactory.Create("$moment", moment)); + + outers.Add("(valid_from <= $moment AND valid_to > $moment)"); + } + if (outers.Count > 0) { text += $" WHERE {string.Join(" AND ", outers)}"; diff --git a/src/AzureAppConfigurationEmulator/Repositories/IConfigurationSettingRepository.cs b/src/AzureAppConfigurationEmulator/Repositories/IConfigurationSettingRepository.cs index 9e4f000..daff141 100644 --- a/src/AzureAppConfigurationEmulator/Repositories/IConfigurationSettingRepository.cs +++ b/src/AzureAppConfigurationEmulator/Repositories/IConfigurationSettingRepository.cs @@ -12,6 +12,7 @@ public Task AddAsync( public IAsyncEnumerable Get( string key = KeyFilter.Any, string label = LabelFilter.Any, + DateTimeOffset? moment = default, CancellationToken cancellationToken = default); public Task RemoveAsync( diff --git a/src/AzureAppConfigurationEmulator/Results/KeySetResult.cs b/src/AzureAppConfigurationEmulator/Results/KeySetResult.cs index 34a278b..9aa2581 100644 --- a/src/AzureAppConfigurationEmulator/Results/KeySetResult.cs +++ b/src/AzureAppConfigurationEmulator/Results/KeySetResult.cs @@ -1,6 +1,6 @@ namespace AzureAppConfigurationEmulator.Results; -public class KeySetResult(IEnumerable keys) : +public class KeySetResult(IEnumerable keys, DateTimeOffset? mementoDatetime = default) : IResult, IContentTypeHttpResult, IStatusCodeHttpResult, @@ -9,6 +9,11 @@ public class KeySetResult(IEnumerable keys) : { public async Task ExecuteAsync(HttpContext httpContext) { + if (MementoDatetime.HasValue) + { + httpContext.Response.Headers["Memento-Datetime"] = MementoDatetime.Value.ToString("R"); + } + if (StatusCode.HasValue) { httpContext.Response.StatusCode = StatusCode.Value; @@ -24,6 +29,8 @@ public async Task ExecuteAsync(HttpContext httpContext) object IValueHttpResult.Value => Value; public KeySet Value { get; } = new(keys.Select(key => new Key(key))); + + private DateTimeOffset? MementoDatetime { get; } = mementoDatetime; } public record KeySet(IEnumerable Items); diff --git a/src/AzureAppConfigurationEmulator/Results/KeyValueResult.cs b/src/AzureAppConfigurationEmulator/Results/KeyValueResult.cs index 84d6de2..f70c575 100644 --- a/src/AzureAppConfigurationEmulator/Results/KeyValueResult.cs +++ b/src/AzureAppConfigurationEmulator/Results/KeyValueResult.cs @@ -2,7 +2,7 @@ namespace AzureAppConfigurationEmulator.Results; -public class KeyValueResult(ConfigurationSetting setting) : +public class KeyValueResult(ConfigurationSetting setting, DateTimeOffset? mementoDatetime = default) : IResult, IContentTypeHttpResult, IStatusCodeHttpResult, @@ -14,6 +14,11 @@ public async Task ExecuteAsync(HttpContext httpContext) httpContext.Response.Headers.ETag = Value.Etag; httpContext.Response.Headers.LastModified = Value.LastModified.ToString("R"); + if (MementoDatetime.HasValue) + { + httpContext.Response.Headers["Memento-Datetime"] = MementoDatetime.Value.ToString("R"); + } + if (StatusCode.HasValue) { httpContext.Response.StatusCode = StatusCode.Value; @@ -29,4 +34,6 @@ public async Task ExecuteAsync(HttpContext httpContext) object IValueHttpResult.Value => Value; public ConfigurationSetting Value { get; } = setting; + + private DateTimeOffset? MementoDatetime { get; } = mementoDatetime; } diff --git a/src/AzureAppConfigurationEmulator/Results/KeyValueSetResult.cs b/src/AzureAppConfigurationEmulator/Results/KeyValueSetResult.cs index af3ccb5..9abab81 100644 --- a/src/AzureAppConfigurationEmulator/Results/KeyValueSetResult.cs +++ b/src/AzureAppConfigurationEmulator/Results/KeyValueSetResult.cs @@ -2,7 +2,7 @@ namespace AzureAppConfigurationEmulator.Results; -public class KeyValueSetResult(IEnumerable settings) : +public class KeyValueSetResult(IEnumerable settings, DateTimeOffset? mementoDatetime = default) : IResult, IContentTypeHttpResult, IStatusCodeHttpResult, @@ -11,6 +11,11 @@ public class KeyValueSetResult(IEnumerable settings) : { public async Task ExecuteAsync(HttpContext httpContext) { + if (MementoDatetime.HasValue) + { + httpContext.Response.Headers["Memento-Datetime"] = MementoDatetime.Value.ToString("R"); + } + if (StatusCode.HasValue) { httpContext.Response.StatusCode = StatusCode.Value; @@ -26,6 +31,8 @@ public async Task ExecuteAsync(HttpContext httpContext) object IValueHttpResult.Value => Value; public KeyValueSet Value { get; } = new(settings); + + private DateTimeOffset? MementoDatetime { get; } = mementoDatetime; } public record KeyValueSet(IEnumerable Items); diff --git a/src/AzureAppConfigurationEmulator/Results/LabelSetResult.cs b/src/AzureAppConfigurationEmulator/Results/LabelSetResult.cs index df00599..093ef34 100644 --- a/src/AzureAppConfigurationEmulator/Results/LabelSetResult.cs +++ b/src/AzureAppConfigurationEmulator/Results/LabelSetResult.cs @@ -1,6 +1,6 @@ namespace AzureAppConfigurationEmulator.Results; -public class LabelSetResult(IEnumerable labels) : +public class LabelSetResult(IEnumerable labels, DateTimeOffset? mementoDatetime = default) : IResult, IContentTypeHttpResult, IStatusCodeHttpResult, @@ -9,6 +9,11 @@ public class LabelSetResult(IEnumerable labels) : { public async Task ExecuteAsync(HttpContext httpContext) { + if (MementoDatetime.HasValue) + { + httpContext.Response.Headers["Memento-Datetime"] = MementoDatetime.Value.ToString("R"); + } + if (StatusCode.HasValue) { httpContext.Response.StatusCode = StatusCode.Value; @@ -24,6 +29,8 @@ public async Task ExecuteAsync(HttpContext httpContext) object IValueHttpResult.Value => Value; public LabelSet Value { get; } = new(labels.Select(label => new Label(label))); + + private DateTimeOffset? MementoDatetime { get; } = mementoDatetime; } public record LabelSet(IEnumerable