Skip to content

Commit

Permalink
🐛 16 fix bug to do with timestamps when running in docker (#17)
Browse files Browse the repository at this point in the history
* Added SydneyTimeProvider

* Remove weather component

* Removed usages of DateTime.Now

* Register SydneyTimeProvider

* Added unit tests

* Refactored get timesheet to use common utc calculation

* Refactored timesheet query to use common UTC logic

* Tidy up from review
  • Loading branch information
danielmackay authored May 5, 2024
1 parent 1c1fefb commit 262c00f
Show file tree
Hide file tree
Showing 17 changed files with 224 additions and 115 deletions.
7 changes: 7 additions & 0 deletions DailyScrumGenerator.sln
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebUI", "src\WebUI\WebUI.cs
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IntegrationTests", "tests\IntegrationTests\IntegrationTests.csproj", "{30037715-AA77-4D4F-B961-8DE69E9B7A28}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTests", "tests\UnitTests\UnitTests.csproj", "{7E4AFE3C-FF99-4E27-8DA5-B0F55F806AA7}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -30,9 +32,14 @@ Global
{30037715-AA77-4D4F-B961-8DE69E9B7A28}.Debug|Any CPU.Build.0 = Debug|Any CPU
{30037715-AA77-4D4F-B961-8DE69E9B7A28}.Release|Any CPU.ActiveCfg = Release|Any CPU
{30037715-AA77-4D4F-B961-8DE69E9B7A28}.Release|Any CPU.Build.0 = Release|Any CPU
{7E4AFE3C-FF99-4E27-8DA5-B0F55F806AA7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7E4AFE3C-FF99-4E27-8DA5-B0F55F806AA7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7E4AFE3C-FF99-4E27-8DA5-B0F55F806AA7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7E4AFE3C-FF99-4E27-8DA5-B0F55F806AA7}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{351FF09F-D517-4930-8D86-607DB4B7904C} = {92C5999C-A447-479E-8630-064E6FDC78DA}
{30037715-AA77-4D4F-B961-8DE69E9B7A28} = {21C89A08-D942-45BE-A302-4EEB543573C0}
{7E4AFE3C-FF99-4E27-8DA5-B0F55F806AA7} = {21C89A08-D942-45BE-A302-4EEB543573C0}
EndGlobalSection
EndGlobal
7 changes: 4 additions & 3 deletions global.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"sdk": {
"version": "8.0.100",
"rollForward": "latestMinor"
"version": "8.0.0",
"rollForward": "latestFeature",
"allowPrerelease": false
}
}
}
10 changes: 10 additions & 0 deletions src/WebUI/Common/Services/SydneyTimeProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace WebUI.Common.Services;

public class SydneyTimeProvider : TimeProvider
{
private const string Timezone = "AUS Eastern Standard Time";

private readonly TimeZoneInfo _sydneyTimeZone = TimeZoneInfo.FindSystemTimeZoneById(Timezone);

public override TimeZoneInfo LocalTimeZone => _sydneyTimeZone;
}
36 changes: 36 additions & 0 deletions src/WebUI/Common/Services/TimeProviderExt.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
namespace WebUI.Common.Services;

public static class TimeProviderExt
{
public static DateOnly GetToday(this TimeProvider timeProvider)
{
var now = timeProvider.GetLocalNow();
return DateOnly.FromDateTime(now.Date);
}

public static DateTime GetStartOfDayUtc(this TimeProvider timeProvider, DateOnly date)
{
var timeZone = timeProvider.LocalTimeZone;

// Find the start of the day in Sydney time
var startOfDaySydney = date.ToDateTime(TimeOnly.MinValue);

// Convert the start of the day to UTC
var startOfDayUtc = TimeZoneInfo.ConvertTimeToUtc(startOfDaySydney, timeZone);

return startOfDayUtc;
}

public static DateTime GetEndOfDayUtc(this TimeProvider timeProvider, DateOnly date)
{
var timeZone = timeProvider.LocalTimeZone;

// Find the start of the day in Sydney time
var endOfDaySydney = date.ToDateTime(TimeOnly.MaxValue);

// Convert the start of the day to UTC
var endOfDayUtc = TimeZoneInfo.ConvertTimeToUtc(endOfDaySydney, timeZone);

return endOfDayUtc;
}
}
66 changes: 0 additions & 66 deletions src/WebUI/Components/Pages/Weather.razor

This file was deleted.

19 changes: 19 additions & 0 deletions src/WebUI/Features/DailyScrum/Components/Pages/Debug.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
@page "/daily-scrum/debug"
@using Microsoft.AspNetCore.Components.Web
@inject TimeProvider TimeProvider

<PageTitle>Daily Scrum</PageTitle>

<h1>Daily Scrum - Debug</h1>

<p>Now - @_now</p>

@code {
private DateTimeOffset _now;

protected override void OnInitialized()
{
_now = TimeProvider.GetLocalNow();
}

}
10 changes: 9 additions & 1 deletion src/WebUI/Features/DailyScrum/Components/Pages/Index.razor
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.AspNetCore.Components.Forms
@using System.ComponentModel.DataAnnotations
@using WebUI.Common.Services
@inject NavigationManager NavigationManager
@inject TimeProvider TimeProvider

<PageTitle>Daily Scrum</PageTitle>

Expand Down Expand Up @@ -54,6 +56,12 @@
// https://github.com/dotnet/aspnetcore/issues/52195
public string ClientDays { get; set; } = String.Empty;

public DateOnly LastWorkingDay { get; set; } = DateOnly.FromDateTime(DateTime.Now.AddDays(-1));
public DateOnly LastWorkingDay { get; set; }
}

protected override void OnInitialized()
{
if (Model.LastWorkingDay == default)
Model.LastWorkingDay = TimeProvider.GetToday().AddDays(-1);
}
}
2 changes: 2 additions & 0 deletions src/WebUI/Features/DailyScrum/DailyScrumFeature.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using WebUI.Common.Features;
using WebUI.Common.Services;
using WebUI.Features.DailyScrum.Infrastructure;

namespace WebUI.Features.DailyScrum;
Expand All @@ -10,5 +11,6 @@ public static void ConfigureServices(IServiceCollection services, IConfiguration
//services.AddOptionsWithValidation<MicrosoftGraphOptions>(MicrosoftGraphOptions.Section);
//services.AddScoped<IGraphService, MockGraphService>();
services.AddScoped<IGraphService, GraphService>();
services.AddScoped<TimeProvider, SydneyTimeProvider>();
}
}
7 changes: 5 additions & 2 deletions src/WebUI/Features/DailyScrum/Infrastructure/GraphService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@ public interface IGraphService
public class GraphService : IGraphService
{
private readonly ICurrentUserService _currentUserService;
private readonly ILogger<GraphService> _logger;

public GraphService(ICurrentUserService currentUserService)
public GraphService(ICurrentUserService currentUserService, ILogger<GraphService> logger)
{
_currentUserService = currentUserService;
_logger = logger;
}

// public async Task<List<TodoTaskList>?> GetTodoLists()
Expand Down Expand Up @@ -46,6 +48,8 @@ public GraphService(ICurrentUserService currentUserService)

public async Task<List<Project>> GetTasks(DateTime utcStart, DateTime utcEnd)
{
_logger.LogInformation("Getting tasks from {UtcStart} to {UtcEnd}", utcStart, utcEnd);

var graphClient = GetGraphServiceClient();

// NOTE: SHOULD be able to use OData to expand the child tasks, but I haven't been able to get this to work
Expand All @@ -55,7 +59,6 @@ public async Task<List<Project>> GetTasks(DateTime utcStart, DateTime utcEnd)

var tasks = new Dictionary<TodoTaskList, Task<TodoTaskCollectionResponse?>>();


foreach (var list in lists.Value)

Check warning on line 62 in src/WebUI/Features/DailyScrum/Infrastructure/GraphService.cs

View workflow job for this annotation

GitHub Actions / build

Dereference of a possibly null reference.

Check warning on line 62 in src/WebUI/Features/DailyScrum/Infrastructure/GraphService.cs

View workflow job for this annotation

GitHub Actions / build

Dereference of a possibly null reference.
{
var task = graphClient.Me.Todo
Expand Down
37 changes: 15 additions & 22 deletions src/WebUI/Features/DailyScrum/Queries/GetDailyScrumQuery.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using MediatR;
using WebUI.Common.Services;
using WebUI.Common.ViewModels;
using WebUI.Features.DailyScrum.Infrastructure;

Expand All @@ -10,10 +11,14 @@ public record GetDailyScrumQuery(string Name, int? ClientDays, DateOnly? LastWor
public class GetDailyScrumQueryHandler : IRequestHandler<GetDailyScrumQuery, DailyScrumViewModel>
{
private readonly IGraphService _graphService;
private readonly TimeProvider _timeProvider;
private readonly ILogger<GetDailyScrumQueryHandler> _logger;

public GetDailyScrumQueryHandler(IGraphService graphService)
public GetDailyScrumQueryHandler(IGraphService graphService, TimeProvider timeProvider, ILogger<GetDailyScrumQueryHandler> logger)
{
_graphService = graphService;
_timeProvider = timeProvider;
_logger = logger;
}

public async Task<DailyScrumViewModel> Handle(GetDailyScrumQuery request, CancellationToken cancellationToken)
Expand All @@ -22,7 +27,9 @@ public async Task<DailyScrumViewModel> Handle(GetDailyScrumQuery request, Cancel

var userSummary = await GetUserSummary(request.ClientDays);

var today = GetToday();
var today = _timeProvider.GetToday();
_logger.LogInformation("Getting projects for {Today}", today);

var todaysProjects = await GetProjects(today);

var yesterday = GetLastWorkingDay(request.LastWorkingDay);
Expand Down Expand Up @@ -52,7 +59,11 @@ private async Task<UserSummaryViewModel> GetUserSummary(int? clientDays)
// TODO: Consider refactoring into a common service
private async Task<List<ProjectViewModel>> GetProjects(DateOnly date)
{
var (startOfDayUtc, endOfDayUtc) = GetTimeStamps(date);
// TODO: This is not returning the correct timestamps on local vs docker
var startOfDayUtc = _timeProvider.GetStartOfDayUtc(date);
var endOfDayUtc = _timeProvider.GetEndOfDayUtc(date);

_logger.LogInformation("Getting projects for {Date} ({StartOfDayUtc} to {EndOfDayUtc})", date, startOfDayUtc, endOfDayUtc);

var graphTasks = await _graphService.GetTasks(startOfDayUtc, endOfDayUtc);

Expand Down Expand Up @@ -91,24 +102,6 @@ private EmailViewModel GetEmail(string name)
};
}

// TODO: Consider refactoring into a common service
private (DateTime StartOfDayUtc, DateTime EndOfDayUtc) GetTimeStamps(DateOnly localDate)
{
// Find the start of the day
var startOfDayLocal = localDate.ToDateTime(TimeOnly.MinValue);

// Find the end of the day
var endOfDayLocal = localDate.ToDateTime(TimeOnly.MaxValue);

// Convert to UTC
var startOfDayUtc = startOfDayLocal.ToUniversalTime();
var endOfDayUtc = endOfDayLocal.ToUniversalTime();

return (startOfDayUtc, endOfDayUtc);
}

private DateOnly GetToday() => DateOnly.FromDateTime(DateTime.Now);

private DateOnly GetLastWorkingDay(DateOnly? lastWorkingDay) =>
lastWorkingDay ?? DateOnly.FromDateTime(DateTime.Now.AddDays(-1));
lastWorkingDay ?? _timeProvider.GetToday().AddDays(-1);
}
11 changes: 10 additions & 1 deletion src/WebUI/Features/Timesheet/Components/Pages/Index.razor
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.AspNetCore.Components.Forms
@using System.ComponentModel.DataAnnotations
@using WebUI.Common.Services
@inject NavigationManager NavigationManager
@inject TimeProvider TimeProvider

<PageTitle>Timesheet Notes</PageTitle>

Expand Down Expand Up @@ -37,6 +39,13 @@
public class InputModel
{
[Required]
public DateOnly Date { get; set; } = DateOnly.FromDateTime(DateTime.Now);
public DateOnly Date { get; set; }
}

protected override void OnInitialized()
{
if (Model.Date == default)
Model.Date = TimeProvider.GetToday();
}

}
Loading

0 comments on commit 262c00f

Please sign in to comment.