Refactor to inject IConfigurationService directly instead of using factory singletons

Co-authored-by: clayschaad <11883505+clayschaad@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot] 2025-08-26 04:21:18 +00:00
parent 98fc96af90
commit 38096910d0
7 changed files with 30 additions and 16 deletions

View file

@ -12,20 +12,20 @@ namespace ShiftScheduler.Server.Controllers
private readonly IcsExportService _icsService;
private readonly PdfExportService _pdfExportService;
private readonly ITransportService _transportService;
private readonly TransportConfiguration _transportConfig;
private readonly IConfigurationService _configurationService;
public ShiftController(
ShiftService shiftService,
IcsExportService icsService,
PdfExportService pdfExportService,
ITransportService transportService,
TransportConfiguration transportConfig)
IConfigurationService configurationService)
{
_shiftService = shiftService;
_icsService = icsService;
_pdfExportService = pdfExportService;
_transportService = transportService;
_transportConfig = transportConfig;
_configurationService = configurationService;
}
[HttpGet("shifts")]
@ -43,6 +43,7 @@ namespace ShiftScheduler.Server.Controllers
return NotFound($"Shift '{request.ShiftName}' not found");
}
var transportConfig = _configurationService.GetTransportConfiguration();
TransportConnection? morningTransport = null;
TransportConnection? afternoonTransport = null;
@ -71,7 +72,7 @@ namespace ShiftScheduler.Server.Controllers
if (morningEndTime.HasValue && afternoonStartTime.HasValue)
{
var breakDurationMinutes = (afternoonStartTime.Value - morningEndTime.Value).TotalMinutes;
shouldLoadAfternoonTransport = breakDurationMinutes >= _transportConfig.MinBreakMinutes;
shouldLoadAfternoonTransport = breakDurationMinutes >= transportConfig.MinBreakMinutes;
}
}

View file

@ -16,8 +16,6 @@ var appConfiguration = new ApplicationConfiguration
// Register services
builder.Services.AddSingleton<IConfigurationService>(new ConfigurationService(appConfiguration));
builder.Services.AddSingleton<List<Shift>>(provider => provider.GetRequiredService<IConfigurationService>().GetShifts());
builder.Services.AddSingleton<TransportConfiguration>(provider => provider.GetRequiredService<IConfigurationService>().GetTransportConfiguration());
builder.Services.AddMemoryCache();
builder.Services.AddHttpClient<TransportApiService>();
builder.Services.AddSingleton<ShiftService>();

View file

@ -5,13 +5,13 @@ namespace ShiftScheduler.Services
{
public class ShiftService
{
private readonly List<Shift> _shifts;
private readonly IConfigurationService _configurationService;
public ShiftService(List<Shift> shifts)
public ShiftService(IConfigurationService configurationService)
{
_shifts = shifts;
_configurationService = configurationService;
}
public List<Shift> GetShifts() => _shifts;
public List<Shift> GetShifts() => _configurationService.GetShifts();
}
}

View file

@ -3,10 +3,12 @@ using ShiftScheduler.Shared;
namespace ShiftScheduler.Services
{
public class TransportApiService(HttpClient httpClient, TransportConfiguration config) : ITransportApiService
public class TransportApiService(HttpClient httpClient, IConfigurationService configurationService) : ITransportApiService
{
public async Task<TransportConnection?> GetConnectionAsync(DateTime shiftStartTime)
{
var config = configurationService.GetTransportConfiguration();
// To allow connections that arrive after shift starts, we search from earlier time
// and request more connections to cover the full range
var searchDate = shiftStartTime.ToString("yyyy-MM-dd");

View file

@ -3,15 +3,16 @@ using ShiftScheduler.Shared;
namespace ShiftScheduler.Services
{
public class TransportService(ITransportApiService transportService, TransportConfiguration config, IMemoryCache cache) : ITransportService
public class TransportService(ITransportApiService transportService, IConfigurationService configurationService, IMemoryCache cache) : ITransportService
{
public async Task<TransportConnection?> GetConnectionAsync(DateTime shiftStartTime)
{
var config = configurationService.GetTransportConfiguration();
var searchDate = shiftStartTime.ToString("yyyy-MM-dd");
var searchTime = shiftStartTime.AddMinutes(config.MaxLateArrivalMinutes).ToString("HH:mm");
// Generate cache key based on request parameters
var cacheKey = GenerateCacheKey(searchDate, searchTime);
var cacheKey = GenerateCacheKey(config, searchDate, searchTime);
// Try to get from cache first
if (cache.TryGetValue(cacheKey, out TransportConnection? cachedConnection) && cachedConnection != null)
@ -35,7 +36,7 @@ namespace ShiftScheduler.Services
return connection;
}
private string GenerateCacheKey(string searchDate, string searchTime)
private string GenerateCacheKey(TransportConfiguration config, string searchDate, string searchTime)
{
return $"transport_{config.StartStation}_{config.EndStation}_{searchDate}_{searchTime}";
}

View file

@ -11,6 +11,7 @@ namespace ShiftScheduler.Services.Tests;
public class TransportApiServiceTests
{
private readonly Mock<HttpMessageHandler> _httpMessageHandlerMock;
private readonly Mock<IConfigurationService> _configurationServiceMock;
private readonly HttpClient _httpClient;
private readonly TransportApiService _transportApiService;
@ -18,6 +19,7 @@ public class TransportApiServiceTests
{
_httpMessageHandlerMock = new Mock<HttpMessageHandler>();
_httpClient = new HttpClient(_httpMessageHandlerMock.Object);
_configurationServiceMock = new Mock<IConfigurationService>();
var config = new TransportConfiguration
{
@ -31,7 +33,11 @@ public class TransportApiServiceTests
CacheDurationDays = 1
};
_transportApiService = new TransportApiService(_httpClient, config);
_configurationServiceMock
.Setup(x => x.GetTransportConfiguration())
.Returns(config);
_transportApiService = new TransportApiService(_httpClient, _configurationServiceMock.Object);
}
[Fact]

View file

@ -9,6 +9,7 @@ namespace ShiftScheduler.Services.Tests;
public class TransportServiceTests
{
private readonly Mock<ITransportApiService> _transportServiceMock;
private readonly Mock<IConfigurationService> _configurationServiceMock;
private readonly IMemoryCache _memoryCache;
private readonly TransportService _transportService;
private readonly TransportConfiguration _config;
@ -16,6 +17,7 @@ public class TransportServiceTests
public TransportServiceTests()
{
_transportServiceMock = new Mock<ITransportApiService>();
_configurationServiceMock = new Mock<IConfigurationService>();
_memoryCache = new MemoryCache(new MemoryCacheOptions());
_config = new TransportConfiguration
@ -30,7 +32,11 @@ public class TransportServiceTests
CacheDurationDays = 1
};
_transportService = new TransportService(_transportServiceMock.Object, _config, _memoryCache);
_configurationServiceMock
.Setup(x => x.GetTransportConfiguration())
.Returns(_config);
_transportService = new TransportService(_transportServiceMock.Object, _configurationServiceMock.Object, _memoryCache);
}
[Fact]