Skip to content

Commit

Permalink
feat: add support for historical configuration settings (#48)
Browse files Browse the repository at this point in the history
  • Loading branch information
tnc1997 authored Jan 14, 2024
1 parent ee4a32b commit 405db20
Show file tree
Hide file tree
Showing 12 changed files with 277 additions and 149 deletions.
5 changes: 3 additions & 2 deletions src/AzureAppConfigurationEmulator/Handlers/KeyHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public class KeyHandler
public static async Task<Results<KeySetResult, InvalidCharacterResult, TooManyValuesResult>> List(
[FromServices] IConfigurationSettingRepository repository,
[FromQuery] string name = KeyFilter.Any,
[FromHeader(Name = "Accept-Datetime")] DateTimeOffset? acceptDatetime = default,
CancellationToken cancellationToken = default)
{
if (name != KeyFilter.Any)
Expand All @@ -27,11 +28,11 @@ public static async Task<Results<KeySetResult, InvalidCharacterResult, TooManyVa
}
}

var keys = await repository.Get(key: name, cancellationToken: cancellationToken)
var keys = await repository.Get(key: name, moment: acceptDatetime, cancellationToken: cancellationToken)
.Select(setting => setting.Key)
.Distinct()
.ToListAsync(cancellationToken);

return new KeySetResult(keys);
return new KeySetResult(keys, acceptDatetime);
}
}
14 changes: 8 additions & 6 deletions src/AzureAppConfigurationEmulator/Handlers/KeyValueHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public static async Task<Results<KeyValueResult, NoContent, PreconditionFailedRe
ifNoneMatch = ifNoneMatch?.TrimStart('"').TrimEnd('"');
key = Uri.UnescapeDataString(key);

var setting = await repository.Get(key, label, cancellationToken).SingleOrDefaultAsync(cancellationToken);
var setting = await repository.Get(key, label, cancellationToken: cancellationToken).SingleOrDefaultAsync(cancellationToken);

if (setting == null)
{
Expand Down Expand Up @@ -65,6 +65,7 @@ public static async Task<Results<KeyValueResult, NotFound, NotModifiedResult, Pr
[FromServices] IConfigurationSettingRepository repository,
[FromRoute] string key,
[FromQuery] string label = LabelFilter.Null,
[FromHeader(Name = "Accept-Datetime")] DateTimeOffset? acceptDatetime = default,
[FromHeader(Name = "If-Match")] string? ifMatch = default,
[FromHeader(Name = "If-None-Match")] string? ifNoneMatch = default,
CancellationToken cancellationToken = default)
Expand All @@ -73,7 +74,7 @@ public static async Task<Results<KeyValueResult, NotFound, NotModifiedResult, Pr
ifNoneMatch = ifNoneMatch?.TrimStart('"').TrimEnd('"');
key = Uri.UnescapeDataString(key);

var setting = await repository.Get(key, label, cancellationToken).SingleOrDefaultAsync(cancellationToken);
var setting = await repository.Get(key, label, acceptDatetime, cancellationToken).SingleOrDefaultAsync(cancellationToken);

if (setting == null)
{
Expand All @@ -90,13 +91,14 @@ public static async Task<Results<KeyValueResult, NotFound, NotModifiedResult, Pr
return new NotModifiedResult();
}

return new KeyValueResult(setting);
return new KeyValueResult(setting, acceptDatetime);
}

public static async Task<Results<KeyValueSetResult, InvalidCharacterResult, TooManyValuesResult>> 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)
Expand Down Expand Up @@ -125,9 +127,9 @@ public static async Task<Results<KeyValueSetResult, InvalidCharacterResult, TooM
}
}

var settings = await repository.Get(key, label, cancellationToken).ToListAsync(cancellationToken);
var settings = await repository.Get(key, label, acceptDatetime, cancellationToken).ToListAsync(cancellationToken);

return new KeyValueSetResult(settings);
return new KeyValueSetResult(settings, acceptDatetime);
}

public static async Task<Results<KeyValueResult, PreconditionFailedResult, ReadOnlyResult>> Set(
Expand All @@ -145,7 +147,7 @@ public static async Task<Results<KeyValueResult, PreconditionFailedResult, ReadO

var date = DateTimeOffset.UtcNow;

var setting = await repository.Get(key, label, cancellationToken).SingleOrDefaultAsync(cancellationToken);
var setting = await repository.Get(key, label, cancellationToken: cancellationToken).SingleOrDefaultAsync(cancellationToken);

if (setting == null)
{
Expand Down
3 changes: 2 additions & 1 deletion src/AzureAppConfigurationEmulator/Handlers/LabelHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public class LabelHandler
public static async Task<Results<LabelSetResult, InvalidCharacterResult, TooManyValuesResult>> List(
[FromServices] IConfigurationSettingRepository repository,
[FromQuery] string name = LabelFilter.Any,
[FromHeader(Name = "Accept-Datetime")] DateTimeOffset? acceptDatetime = default,
CancellationToken cancellationToken = default)
{
if (name != LabelFilter.Any)
Expand All @@ -27,7 +28,7 @@ public static async Task<Results<LabelSetResult, InvalidCharacterResult, TooMany
}
}

var labels = await repository.Get(label: name, cancellationToken: cancellationToken)
var labels = await repository.Get(label: name, moment: acceptDatetime, cancellationToken: cancellationToken)
.Select(setting => setting.Label)
.Distinct()
.ToListAsync(cancellationToken);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,10 @@ public async Task AddAsync(
public async IAsyncEnumerable<ConfigurationSetting> 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<DbParameter>();

Expand Down Expand Up @@ -99,6 +100,13 @@ public async IAsyncEnumerable<ConfigurationSetting> 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)}";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public Task AddAsync(
public IAsyncEnumerable<ConfigurationSetting> Get(
string key = KeyFilter.Any,
string label = LabelFilter.Any,
DateTimeOffset? moment = default,
CancellationToken cancellationToken = default);

public Task RemoveAsync(
Expand Down
9 changes: 8 additions & 1 deletion src/AzureAppConfigurationEmulator/Results/KeySetResult.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace AzureAppConfigurationEmulator.Results;

public class KeySetResult(IEnumerable<string> keys) :
public class KeySetResult(IEnumerable<string> keys, DateTimeOffset? mementoDatetime = default) :
IResult,
IContentTypeHttpResult,
IStatusCodeHttpResult,
Expand All @@ -9,6 +9,11 @@ public class KeySetResult(IEnumerable<string> 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;
Expand All @@ -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<Key> Items);
Expand Down
9 changes: 8 additions & 1 deletion src/AzureAppConfigurationEmulator/Results/KeyValueResult.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace AzureAppConfigurationEmulator.Results;

public class KeyValueResult(ConfigurationSetting setting) :
public class KeyValueResult(ConfigurationSetting setting, DateTimeOffset? mementoDatetime = default) :
IResult,
IContentTypeHttpResult,
IStatusCodeHttpResult,
Expand All @@ -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;
Expand All @@ -29,4 +34,6 @@ public async Task ExecuteAsync(HttpContext httpContext)
object IValueHttpResult.Value => Value;

public ConfigurationSetting Value { get; } = setting;

private DateTimeOffset? MementoDatetime { get; } = mementoDatetime;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace AzureAppConfigurationEmulator.Results;

public class KeyValueSetResult(IEnumerable<ConfigurationSetting> settings) :
public class KeyValueSetResult(IEnumerable<ConfigurationSetting> settings, DateTimeOffset? mementoDatetime = default) :
IResult,
IContentTypeHttpResult,
IStatusCodeHttpResult,
Expand All @@ -11,6 +11,11 @@ public class KeyValueSetResult(IEnumerable<ConfigurationSetting> 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;
Expand All @@ -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<ConfigurationSetting> Items);
9 changes: 8 additions & 1 deletion src/AzureAppConfigurationEmulator/Results/LabelSetResult.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace AzureAppConfigurationEmulator.Results;

public class LabelSetResult(IEnumerable<string?> labels) :
public class LabelSetResult(IEnumerable<string?> labels, DateTimeOffset? mementoDatetime = default) :
IResult,
IContentTypeHttpResult,
IStatusCodeHttpResult,
Expand All @@ -9,6 +9,11 @@ public class LabelSetResult(IEnumerable<string?> 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;
Expand All @@ -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<Label> Items);
Expand Down
Loading

0 comments on commit 405db20

Please sign in to comment.