feat(configmanager): add ConfigFileService with tests
Add config file loading and saving service following TDD approach: - IConfigFileService interface for loading/saving config files - ConfigLoadException for descriptive error handling - ConfigFileService implementation with JSON serialization - Unit tests with mocked IFileSystem dependency
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using JdeScoping.ConfigManager.Models;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace JdeScoping.ConfigManager.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Service for loading and saving configuration files.
|
||||
/// </summary>
|
||||
public class ConfigFileService : IConfigFileService
|
||||
{
|
||||
private readonly IFileSystem _fileSystem;
|
||||
private readonly ILogger<ConfigFileService>? _logger;
|
||||
|
||||
private static readonly JsonSerializerOptions JsonOptions = new()
|
||||
{
|
||||
WriteIndented = true,
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||||
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
|
||||
PropertyNameCaseInsensitive = true
|
||||
};
|
||||
|
||||
public ConfigFileService(IFileSystem fileSystem, ILogger<ConfigFileService>? logger = null)
|
||||
{
|
||||
_fileSystem = fileSystem;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<ConfigModel> LoadAppSettingsAsync(string path, CancellationToken ct = default)
|
||||
{
|
||||
_logger?.LogInformation("Loading appsettings from {Path}", path);
|
||||
|
||||
try
|
||||
{
|
||||
var json = await _fileSystem.ReadAllTextAsync(path, ct);
|
||||
var config = JsonSerializer.Deserialize<ConfigModel>(json, JsonOptions);
|
||||
return config ?? new ConfigModel();
|
||||
}
|
||||
catch (JsonException ex)
|
||||
{
|
||||
throw new ConfigLoadException(path, $"Failed to parse appsettings.json: {ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<PipelinesConfigModel> LoadPipelinesAsync(string path, CancellationToken ct = default)
|
||||
{
|
||||
_logger?.LogInformation("Loading pipelines from {Path}", path);
|
||||
|
||||
try
|
||||
{
|
||||
var json = await _fileSystem.ReadAllTextAsync(path, ct);
|
||||
var config = JsonSerializer.Deserialize<PipelinesConfigModel>(json, JsonOptions);
|
||||
return config ?? new PipelinesConfigModel();
|
||||
}
|
||||
catch (JsonException ex)
|
||||
{
|
||||
throw new ConfigLoadException(path, $"Failed to parse pipelines.json: {ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task SaveAppSettingsAsync(string path, ConfigModel config, CancellationToken ct = default)
|
||||
{
|
||||
_logger?.LogInformation("Saving appsettings to {Path}", path);
|
||||
var json = JsonSerializer.Serialize(config, JsonOptions);
|
||||
await _fileSystem.WriteAllTextAsync(path, json, ct);
|
||||
}
|
||||
|
||||
public async Task SavePipelinesAsync(string path, PipelinesConfigModel config, CancellationToken ct = default)
|
||||
{
|
||||
_logger?.LogInformation("Saving pipelines to {Path}", path);
|
||||
var json = JsonSerializer.Serialize(config, JsonOptions);
|
||||
await _fileSystem.WriteAllTextAsync(path, json, ct);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
namespace JdeScoping.ConfigManager.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Exception thrown when configuration file loading fails.
|
||||
/// </summary>
|
||||
public class ConfigLoadException : Exception
|
||||
{
|
||||
public string FilePath { get; }
|
||||
|
||||
public ConfigLoadException(string filePath, string message, Exception? inner = null)
|
||||
: base(message, inner)
|
||||
{
|
||||
FilePath = filePath;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using JdeScoping.ConfigManager.Models;
|
||||
|
||||
namespace JdeScoping.ConfigManager.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Service for loading and saving configuration files.
|
||||
/// </summary>
|
||||
public interface IConfigFileService
|
||||
{
|
||||
Task<ConfigModel> LoadAppSettingsAsync(string path, CancellationToken ct = default);
|
||||
Task<PipelinesConfigModel> LoadPipelinesAsync(string path, CancellationToken ct = default);
|
||||
Task SaveAppSettingsAsync(string path, ConfigModel config, CancellationToken ct = default);
|
||||
Task SavePipelinesAsync(string path, PipelinesConfigModel config, CancellationToken ct = default);
|
||||
}
|
||||
Reference in New Issue
Block a user