feat: add CalendarEventBuilder with shared event/transport logic
This commit is contained in:
parent
839abe6cf4
commit
10ccd6fce0
2 changed files with 122 additions and 0 deletions
53
Services/CalendarEventBuilder.cs
Normal file
53
Services/CalendarEventBuilder.cs
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
using ShiftScheduler.Shared;
|
||||
|
||||
namespace ShiftScheduler.Services;
|
||||
|
||||
public record CalendarEventData(string Summary, string Description);
|
||||
|
||||
public static class CalendarEventBuilder
|
||||
{
|
||||
public static CalendarEventData BuildEventContent(Shift shift, TransportConnection? transport)
|
||||
{
|
||||
var description = transport != null
|
||||
? $"Transport: {FormatTransportInfo(transport)}"
|
||||
: string.Empty;
|
||||
return new CalendarEventData(shift.Name, description);
|
||||
}
|
||||
|
||||
public static string FormatTransportInfo(TransportConnection transport)
|
||||
{
|
||||
var departure = transport.DepartureTime.ToString("HH:mm");
|
||||
var arrival = transport.ArrivalTime.ToString("HH:mm");
|
||||
return $"{transport.Platform}: {departure} → {arrival} ({transport.Duration.TotalMinutes} Minutes)";
|
||||
}
|
||||
|
||||
public static async Task<T> ExecuteWithRetryAsync<T>(Func<Task<T>> operation, int maxRetries = 3)
|
||||
{
|
||||
var delay = TimeSpan.FromSeconds(1);
|
||||
|
||||
for (var attempt = 0; attempt <= maxRetries; attempt++)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await operation();
|
||||
}
|
||||
catch (HttpRequestException ex) when (ex.Message.Contains("429") && attempt < maxRetries)
|
||||
{
|
||||
await Task.Delay(delay);
|
||||
delay = TimeSpan.FromMilliseconds(delay.TotalMilliseconds * 2);
|
||||
}
|
||||
catch (Exception ex) when (ex.Message.Contains("Rate Limit") && attempt < maxRetries)
|
||||
{
|
||||
await Task.Delay(delay);
|
||||
delay = TimeSpan.FromMilliseconds(delay.TotalMilliseconds * 2);
|
||||
}
|
||||
catch (Exception ex) when (ex.Message.Contains("Forbidden") && ex.Message.Contains("quota") && attempt < maxRetries)
|
||||
{
|
||||
await Task.Delay(delay);
|
||||
delay = TimeSpan.FromMilliseconds(delay.TotalMilliseconds * 2);
|
||||
}
|
||||
}
|
||||
|
||||
return await operation();
|
||||
}
|
||||
}
|
||||
69
ShiftScheduler.Services.Tests/CalendarEventBuilderTests.cs
Normal file
69
ShiftScheduler.Services.Tests/CalendarEventBuilderTests.cs
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
using ShiftScheduler.Services;
|
||||
using ShiftScheduler.Shared;
|
||||
using Shouldly;
|
||||
|
||||
namespace ShiftScheduler.Services.Tests;
|
||||
|
||||
public class CalendarEventBuilderTests
|
||||
{
|
||||
[Fact]
|
||||
public void BuildEventContent_WithTransport_IncludesTransportInDescription()
|
||||
{
|
||||
var shift = new Shift { Name = "Früh" };
|
||||
var transport = new TransportConnection
|
||||
{
|
||||
Platform = "Gleis 3",
|
||||
DepartureTime = DateTimeOffset.Parse("2026-04-01T05:30:00+02:00"),
|
||||
ArrivalTime = DateTimeOffset.Parse("2026-04-01T05:55:00+02:00"),
|
||||
Duration = TimeSpan.FromMinutes(25)
|
||||
};
|
||||
|
||||
var result = CalendarEventBuilder.BuildEventContent(shift, transport);
|
||||
|
||||
result.Summary.ShouldBe("Früh");
|
||||
result.Description.ShouldBe("Transport: Gleis 3: 05:30 → 05:55 (25 Minutes)");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildEventContent_WithoutTransport_EmptyDescription()
|
||||
{
|
||||
var shift = new Shift { Name = "Spät" };
|
||||
|
||||
var result = CalendarEventBuilder.BuildEventContent(shift, null);
|
||||
|
||||
result.Summary.ShouldBe("Spät");
|
||||
result.Description.ShouldBeEmpty();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FormatTransportInfo_FormatsCorrectly()
|
||||
{
|
||||
var transport = new TransportConnection
|
||||
{
|
||||
Platform = "Gleis 3",
|
||||
DepartureTime = DateTimeOffset.Parse("2026-04-01T05:30:00+02:00"),
|
||||
ArrivalTime = DateTimeOffset.Parse("2026-04-01T05:55:00+02:00"),
|
||||
Duration = TimeSpan.FromMinutes(25)
|
||||
};
|
||||
|
||||
var result = CalendarEventBuilder.FormatTransportInfo(transport);
|
||||
|
||||
result.ShouldBe("Gleis 3: 05:30 → 05:55 (25 Minutes)");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FormatTransportInfo_WithNullPlatform_UsesEmptyString()
|
||||
{
|
||||
var transport = new TransportConnection
|
||||
{
|
||||
Platform = null,
|
||||
DepartureTime = DateTimeOffset.Parse("2026-04-01T05:30:00+02:00"),
|
||||
ArrivalTime = DateTimeOffset.Parse("2026-04-01T05:55:00+02:00"),
|
||||
Duration = TimeSpan.FromMinutes(25)
|
||||
};
|
||||
|
||||
var result = CalendarEventBuilder.FormatTransportInfo(transport);
|
||||
|
||||
result.ShouldBe(": 05:30 → 05:55 (25 Minutes)");
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue