Skip to content

Commit

Permalink
Replace ISystemClock with TimeProvider
Browse files Browse the repository at this point in the history
  • Loading branch information
bkoelman committed Dec 24, 2024
1 parent 74e2940 commit 9384d10
Show file tree
Hide file tree
Showing 34 changed files with 181 additions and 161 deletions.
12 changes: 6 additions & 6 deletions src/Examples/DapperExample/Definitions/TodoItemDefinition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@ namespace DapperExample.Definitions;
[UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)]
public sealed class TodoItemDefinition : JsonApiResourceDefinition<TodoItem, long>
{
private readonly IClock _clock;
private readonly TimeProvider _timeProvider;

public TodoItemDefinition(IResourceGraph resourceGraph, IClock clock)
public TodoItemDefinition(IResourceGraph resourceGraph, TimeProvider timeProvider)
: base(resourceGraph)
{
ArgumentNullException.ThrowIfNull(clock);
ArgumentNullException.ThrowIfNull(timeProvider);

_clock = clock;
_timeProvider = timeProvider;
}

public override SortExpression OnApplySort(SortExpression? existingSort)
Expand All @@ -38,11 +38,11 @@ public override Task OnWritingAsync(TodoItem resource, WriteOperationKind writeO
{
if (writeOperation == WriteOperationKind.CreateResource)
{
resource.CreatedAt = _clock.UtcNow;
resource.CreatedAt = _timeProvider.GetUtcNow();
}
else if (writeOperation == WriteOperationKind.UpdateResource)
{
resource.LastModifiedAt = _clock.UtcNow;
resource.LastModifiedAt = _timeProvider.GetUtcNow();
}

return Task.CompletedTask;
Expand Down
6 changes: 0 additions & 6 deletions src/Examples/DapperExample/IClock.cs

This file was deleted.

3 changes: 1 addition & 2 deletions src/Examples/DapperExample/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,12 @@
using JsonApiDotNetCore.Repositories;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.Extensions.DependencyInjection.Extensions;

WebApplicationBuilder builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.TryAddSingleton<IClock, SystemClock>();
builder.Services.AddSingleton<TimeProvider>();

DatabaseProvider databaseProvider = GetDatabaseProvider(builder.Configuration);
string? connectionString = builder.Configuration.GetConnectionString($"DapperExample{databaseProvider}");
Expand Down
6 changes: 0 additions & 6 deletions src/Examples/DapperExample/SystemClock.cs

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ namespace JsonApiDotNetCoreExample.Definitions;
public sealed class TodoItemDefinition(IResourceGraph resourceGraph, TimeProvider timeProvider)
: JsonApiResourceDefinition<TodoItem, long>(resourceGraph)
{
private readonly Func<DateTimeOffset> _getUtcNow = timeProvider.GetUtcNow;
private readonly TimeProvider _timeProvider = timeProvider;

public override SortExpression OnApplySort(SortExpression? existingSort)
{
Expand All @@ -31,11 +31,11 @@ public override Task OnWritingAsync(TodoItem resource, WriteOperationKind writeO
{
if (writeOperation == WriteOperationKind.CreateResource)
{
resource.CreatedAt = _getUtcNow();
resource.CreatedAt = _timeProvider.GetUtcNow();
}
else if (writeOperation == WriteOperationKind.UpdateResource)
{
resource.LastModifiedAt = _getUtcNow();
resource.LastModifiedAt = _timeProvider.GetUtcNow();
}

return Task.CompletedTask;
Expand Down
11 changes: 3 additions & 8 deletions test/DapperTests/IntegrationTests/DapperTestContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,17 @@
using DapperExample.Models;
using DapperExample.Repositories;
using DapperExample.TranslationToSql.DataModel;
using FluentAssertions.Common;
using FluentAssertions.Extensions;
using JetBrains.Annotations;
using JsonApiDotNetCore.Configuration;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Logging;
using TestBuildingBlocks;
using Xunit.Abstractions;
using IClock = DapperExample.IClock;

namespace DapperTests.IntegrationTests;

Expand All @@ -29,7 +27,7 @@ public sealed class DapperTestContext : IntegrationTest
EXEC sp_MSForEachTable 'ALTER TABLE ? CHECK CONSTRAINT ALL';
""";

public static readonly DateTimeOffset FrozenTime = 29.September(2018).At(16, 41, 56).AsUtc().ToDateTimeOffset();
public static readonly DateTimeOffset FrozenTime = DefaultDateTimeUtc;

private readonly Lazy<WebApplicationFactory<TodoItem>> _lazyFactory;
private ITestOutputHelper? _testOutputHelper;
Expand Down Expand Up @@ -82,10 +80,7 @@ private WebApplicationFactory<TodoItem> CreateFactory()

builder.ConfigureServices(services =>
{
services.AddSingleton<IClock>(new FrozenClock
{
UtcNow = FrozenTime
});
services.Replace(ServiceDescriptor.Singleton<TimeProvider>(new FrozenTimeProvider(FrozenTime)));

ServiceDescriptor scopedCaptureStore = services.Single(descriptor => descriptor.ImplementationType == typeof(SqlCaptureStore));
services.Remove(scopedCaptureStore);
Expand Down
11 changes: 0 additions & 11 deletions test/DapperTests/IntegrationTests/FrozenClock.cs

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@ public AtomicCreateResourceTests(IntegrationTestContext<TestableStartup<Operatio
testContext.UseController<MusicTracksController>();
testContext.UseController<PlaylistsController>();

testContext.ConfigureServices(services => services.AddSingleton<ISystemClock, FrozenSystemClock>());

var options = (JsonApiOptions)testContext.Factory.Services.GetRequiredService<IJsonApiOptions>();
options.AllowUnknownFieldsInRequestBody = false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ public AtomicCreateResourceWithClientGeneratedIdTests(IntegrationTestContext<Tes
services.AddResourceDefinition<AssignIdToTextLanguageDefinition>();

services.AddSingleton<ResourceDefinitionHitCounter>();
services.AddSingleton<ISystemClock, FrozenSystemClock>();
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
using System.Reflection;
using JsonApiDotNetCore.Resources;
using Microsoft.Extensions.DependencyInjection;
using TestBuildingBlocks;

namespace JsonApiDotNetCoreTests.IntegrationTests.AtomicOperations;

Expand All @@ -20,9 +19,11 @@ internal sealed class DateMustBeInThePastAttribute : ValidationAttribute
if (propertyInfo.PropertyType == typeof(DateTimeOffset) || propertyInfo.PropertyType == typeof(DateTimeOffset?))
{
var typedValue = (DateTimeOffset?)propertyInfo.GetValue(validationContext.ObjectInstance);
var systemClock = validationContext.GetRequiredService<ISystemClock>();

if (typedValue >= systemClock.UtcNow)
var timeProvider = validationContext.GetRequiredService<TimeProvider>();
DateTimeOffset utcNow = timeProvider.GetUtcNow();

if (typedValue >= utcNow)
{
return new ValidationResult($"{validationContext.MemberName} must be in the past.");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ public AtomicResourceMetaTests(IntegrationTestContext<TestableStartup<Operations
services.AddResourceDefinition<TextLanguageMetaDefinition>();

services.AddSingleton<ResourceDefinitionHitCounter>();
services.AddSingleton<ISystemClock, FrozenSystemClock>();
});

var hitCounter = _testContext.Factory.Services.GetRequiredService<ResourceDefinitionHitCounter>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ public AtomicModelStateValidationTests(IntegrationTestContext<TestableStartup<Op
{
_testContext = testContext;

_testContext.ConfigureServices(services => services.AddSingleton<ISystemClock, FrozenSystemClock>());

testContext.UseController<OperationsController>();
}

Expand Down Expand Up @@ -74,7 +72,8 @@ public async Task Cannot_create_resource_with_multiple_violations()
public async Task Cannot_create_resource_when_violation_from_custom_ValidationAttribute()
{
// Arrange
var clock = _testContext.Factory.Services.GetRequiredService<ISystemClock>();
var timeProvider = _testContext.Factory.Services.GetRequiredService<TimeProvider>();
DateTimeOffset utcNow = timeProvider.GetUtcNow();

var requestBody = new
{
Expand All @@ -90,7 +89,7 @@ public async Task Cannot_create_resource_when_violation_from_custom_ValidationAt
{
title = "some",
lengthInSeconds = 120,
releasedAt = clock.UtcNow.AddDays(1)
releasedAt = utcNow.AddDays(1)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,7 @@ public AtomicQueryStringTests(IntegrationTestContext<TestableStartup<OperationsD
testContext.UseController<OperationsController>();
testContext.UseController<MusicTracksController>();

testContext.ConfigureServices(services =>
{
services.AddResourceDefinition<MusicTrackReleaseDefinition>();

services.AddSingleton<ISystemClock, FrozenSystemClock>();
});
testContext.ConfigureServices(services => services.AddResourceDefinition<MusicTrackReleaseDefinition>());
}

[Fact]
Expand Down Expand Up @@ -272,12 +267,13 @@ public async Task Cannot_use_sparse_fieldset_at_operations_endpoint()
public async Task Can_use_Queryable_handler_at_resource_endpoint()
{
// Arrange
var clock = _testContext.Factory.Services.GetRequiredService<ISystemClock>();
var timeProvider = _testContext.Factory.Services.GetRequiredService<TimeProvider>();
DateTimeOffset utcNow = timeProvider.GetUtcNow();

List<MusicTrack> musicTracks = _fakers.MusicTrack.GenerateList(3);
musicTracks[0].ReleasedAt = clock.UtcNow.AddMonths(5);
musicTracks[1].ReleasedAt = clock.UtcNow.AddMonths(-5);
musicTracks[2].ReleasedAt = clock.UtcNow.AddMonths(-1);
musicTracks[0].ReleasedAt = utcNow.AddMonths(5);
musicTracks[1].ReleasedAt = utcNow.AddMonths(-5);
musicTracks[2].ReleasedAt = utcNow.AddMonths(-1);

await _testContext.RunOnDatabaseAsync(async dbContext =>
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,20 @@
using JsonApiDotNetCore.Configuration;
using JsonApiDotNetCore.Resources;
using Microsoft.Extensions.Primitives;
using TestBuildingBlocks;

namespace JsonApiDotNetCoreTests.IntegrationTests.AtomicOperations.QueryStrings;

[UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)]
public sealed class MusicTrackReleaseDefinition : JsonApiResourceDefinition<MusicTrack, Guid>
{
private readonly ISystemClock _systemClock;
private readonly TimeProvider _timeProvider;

public MusicTrackReleaseDefinition(IResourceGraph resourceGraph, ISystemClock systemClock)
public MusicTrackReleaseDefinition(IResourceGraph resourceGraph, TimeProvider timeProvider)
: base(resourceGraph)
{
ArgumentNullException.ThrowIfNull(systemClock);
ArgumentNullException.ThrowIfNull(timeProvider);

_systemClock = systemClock;
_timeProvider = timeProvider;
}

public override QueryStringParameterHandlers<MusicTrack> OnRegisterQueryableHandlersForQueryStringParameters()
Expand All @@ -33,7 +32,8 @@ private IQueryable<MusicTrack> FilterOnRecentlyReleased(IQueryable<MusicTrack> s

if (bool.Parse(parameterValue.ToString()))
{
tracks = tracks.Where(musicTrack => musicTrack.ReleasedAt < _systemClock.UtcNow && musicTrack.ReleasedAt > _systemClock.UtcNow.AddMonths(-3));
DateTimeOffset utcNow = _timeProvider.GetUtcNow();
tracks = tracks.Where(musicTrack => musicTrack.ReleasedAt < utcNow && musicTrack.ReleasedAt > utcNow.AddMonths(-3));
}

return tracks;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ public AtomicUpdateResourceTests(IntegrationTestContext<TestableStartup<Operatio
services.AddResourceDefinition<ImplicitlyChangingTextLanguageDefinition>();

services.AddSingleton<ResourceDefinitionHitCounter>();
services.AddSingleton<ISystemClock, FrozenSystemClock>();
});

var options = (JsonApiOptions)testContext.Factory.Services.GetRequiredService<IJsonApiOptions>();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
using System.Net;
using System.Net.Http.Headers;
using FluentAssertions;
using FluentAssertions.Extensions;
using JsonApiDotNetCore.Configuration;
using JsonApiDotNetCore.Middleware;
using JsonApiDotNetCore.Serialization.Objects;
using JsonApiDotNetCore.Serialization.Request.Adapters;
using JsonApiDotNetCore.Serialization.Response;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using TestBuildingBlocks;
using Xunit;

namespace JsonApiDotNetCoreTests.IntegrationTests.ContentNegotiation.CustomExtensions;

public sealed class CustomExtensionsContentTypeTests : IClassFixture<IntegrationTestContext<TestableStartup<PolicyDbContext>, PolicyDbContext>>
{
private static readonly DateTimeOffset CurrentTime = 31.December(2024).At(21, 53, 40).AsUtc();
private readonly IntegrationTestContext<TestableStartup<PolicyDbContext>, PolicyDbContext> _testContext;

public CustomExtensionsContentTypeTests(IntegrationTestContext<TestableStartup<PolicyDbContext>, PolicyDbContext> testContext)
Expand All @@ -39,6 +42,9 @@ public CustomExtensionsContentTypeTests(IntegrationTestContext<TestableStartup<P
});
});

testContext.PostConfigureServices(services => services.Replace(
ServiceDescriptor.Singleton<TimeProvider>(new FrozenTimeProvider(CurrentTime, TimeZoneInfo.FindSystemTimeZoneById("Tokyo Standard Time")))));

var options = (JsonApiOptions)_testContext.Factory.Services.GetRequiredService<IJsonApiOptions>();
options.IncludeExtensions(ServerTimeMediaTypeExtension.ServerTime, ServerTimeMediaTypeExtension.RelaxedServerTime);
}
Expand Down Expand Up @@ -108,7 +114,8 @@ public async Task Permits_JsonApi_ContentType_header_with_ServerTime_extension()
httpResponse.Content.Headers.ContentType.ShouldNotBeNull();
httpResponse.Content.Headers.ContentType.ToString().Should().Be(ServerTimeMediaTypes.ServerTime.ToString());

responseDocument.Meta.ShouldContainKey("localServerTime");
responseDocument.Meta.ShouldContainKey("localServerTime").With(time =>
time.ShouldNotBeNull().ToString().Should().Be("2025-01-01T06:53:40.0000000+09:00"));
}

[Fact]
Expand Down Expand Up @@ -143,7 +150,7 @@ public async Task Permits_JsonApi_ContentType_header_with_relaxed_ServerTime_ext
httpResponse.Content.Headers.ContentType.ShouldNotBeNull();
httpResponse.Content.Headers.ContentType.ToString().Should().Be(ServerTimeMediaTypes.RelaxedServerTime.ToString());

responseDocument.Meta.ShouldContainKey("utcServerTime");
responseDocument.Meta.ShouldContainKey("utcServerTime").With(time => time.ShouldNotBeNull().ToString().Should().Be("2024-12-31T21:53:40.0000000Z"));
}

[Fact]
Expand Down Expand Up @@ -185,7 +192,7 @@ public async Task Permits_JsonApi_ContentType_header_with_AtomicOperations_and_S
httpResponse.Content.Headers.ContentType.ShouldNotBeNull();
httpResponse.Content.Headers.ContentType.ToString().Should().Be(ServerTimeMediaTypes.AtomicOperationsWithServerTime.ToString());

responseDocument.Meta.ShouldContainKey("utcServerTime");
responseDocument.Meta.ShouldContainKey("utcServerTime").With(time => time.ShouldNotBeNull().ToString().Should().Be("2024-12-31T21:53:40.0000000Z"));
}

[Fact]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
using System.Globalization;
using JsonApiDotNetCore.Middleware;
using JsonApiDotNetCore.Serialization.Response;

namespace JsonApiDotNetCoreTests.IntegrationTests.ContentNegotiation.CustomExtensions;

internal sealed class ServerTimeResponseMeta(IJsonApiRequest request, RequestDocumentStore documentStore) : IResponseMeta
internal sealed class ServerTimeResponseMeta(IJsonApiRequest request, RequestDocumentStore documentStore, TimeProvider timeProvider) : IResponseMeta
{
private readonly IJsonApiRequest _request = request;
private readonly RequestDocumentStore _documentStore = documentStore;
private readonly TimeProvider _timeProvider = timeProvider;

public IDictionary<string, object?>? GetMeta()
{
if (request.Extensions.Contains(ServerTimeMediaTypeExtension.ServerTime) || request.Extensions.Contains(ServerTimeMediaTypeExtension.RelaxedServerTime))
if (_request.Extensions.Contains(ServerTimeMediaTypeExtension.ServerTime) ||
_request.Extensions.Contains(ServerTimeMediaTypeExtension.RelaxedServerTime))
{
if (_documentStore.Document is not { Meta: not null } || !_documentStore.Document.Meta.TryGetValue("useLocalTime", out object? useLocalTimeValue) ||
useLocalTimeValue == null || !bool.TryParse(useLocalTimeValue.ToString(), out bool useLocalTime))
Expand All @@ -20,11 +24,11 @@ internal sealed class ServerTimeResponseMeta(IJsonApiRequest request, RequestDoc
return useLocalTime
? new Dictionary<string, object?>
{
["localServerTime"] = DateTime.Now.ToString("O")
["localServerTime"] = _timeProvider.GetLocalNow().ToString("O", CultureInfo.InvariantCulture)
}
: new Dictionary<string, object?>
{
["utcServerTime"] = DateTime.UtcNow.ToString("O")
["utcServerTime"] = _timeProvider.GetUtcNow().UtcDateTime.ToString("O", CultureInfo.InvariantCulture)
};
}

Expand Down
Loading

0 comments on commit 9384d10

Please sign in to comment.