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 IcsExportService _icsService;
private readonly PdfExportService _pdfExportService; private readonly PdfExportService _pdfExportService;
private readonly ITransportService _transportService; private readonly ITransportService _transportService;
private readonly TransportConfiguration _transportConfig; private readonly IConfigurationService _configurationService;
public ShiftController( public ShiftController(
ShiftService shiftService, ShiftService shiftService,
IcsExportService icsService, IcsExportService icsService,
PdfExportService pdfExportService, PdfExportService pdfExportService,
ITransportService transportService, ITransportService transportService,
TransportConfiguration transportConfig) IConfigurationService configurationService)
{ {
_shiftService = shiftService; _shiftService = shiftService;
_icsService = icsService; _icsService = icsService;
_pdfExportService = pdfExportService; _pdfExportService = pdfExportService;
_transportService = transportService; _transportService = transportService;
_transportConfig = transportConfig; _configurationService = configurationService;
} }
[HttpGet("shifts")] [HttpGet("shifts")]
@ -43,6 +43,7 @@ namespace ShiftScheduler.Server.Controllers
return NotFound($"Shift '{request.ShiftName}' not found"); return NotFound($"Shift '{request.ShiftName}' not found");
} }
var transportConfig = _configurationService.GetTransportConfiguration();
TransportConnection? morningTransport = null; TransportConnection? morningTransport = null;
TransportConnection? afternoonTransport = null; TransportConnection? afternoonTransport = null;
@ -71,7 +72,7 @@ namespace ShiftScheduler.Server.Controllers
if (morningEndTime.HasValue && afternoonStartTime.HasValue) if (morningEndTime.HasValue && afternoonStartTime.HasValue)
{ {
var breakDurationMinutes = (afternoonStartTime.Value - morningEndTime.Value).TotalMinutes; 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 // Register services
builder.Services.AddSingleton<IConfigurationService>(new ConfigurationService(appConfiguration)); 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.AddMemoryCache();
builder.Services.AddHttpClient<TransportApiService>(); builder.Services.AddHttpClient<TransportApiService>();
builder.Services.AddSingleton<ShiftService>(); builder.Services.AddSingleton<ShiftService>();

View file

@ -5,13 +5,13 @@ namespace ShiftScheduler.Services
{ {
public class ShiftService 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 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) public async Task<TransportConnection?> GetConnectionAsync(DateTime shiftStartTime)
{ {
var config = configurationService.GetTransportConfiguration();
// To allow connections that arrive after shift starts, we search from earlier time // To allow connections that arrive after shift starts, we search from earlier time
// and request more connections to cover the full range // and request more connections to cover the full range
var searchDate = shiftStartTime.ToString("yyyy-MM-dd"); var searchDate = shiftStartTime.ToString("yyyy-MM-dd");

View file

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

View file

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

View file

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