From a1544b9dade6caf4a1567f2b50b8670a79bcbc54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20Gr=C3=BCnwald?= Date: Mon, 6 Jun 2022 22:36:33 +0200 Subject: [PATCH] Start implementing CompositionRoot Add CompositionRoot which will be responsible for creating all commands and required types. Currently, the implementation is incomplete and composition is still split between CompositionRoot and GenerateCommand (which uses the Autofac DI container) --- src/ChangeLog/Commands/GenerateCommand.cs | 114 +++++++++++----------- src/ChangeLog/CompositionRoot.cs | 52 ++++++++++ src/ChangeLog/Program.cs | 9 +- 3 files changed, 113 insertions(+), 62 deletions(-) create mode 100644 src/ChangeLog/CompositionRoot.cs diff --git a/src/ChangeLog/Commands/GenerateCommand.cs b/src/ChangeLog/Commands/GenerateCommand.cs index 5977249f..605c629a 100644 --- a/src/ChangeLog/Commands/GenerateCommand.cs +++ b/src/ChangeLog/Commands/GenerateCommand.cs @@ -3,6 +3,7 @@ using System.IO; using System.Threading.Tasks; using Autofac; +using FluentValidation; using Grynwald.ChangeLog.CommandLine; using Grynwald.ChangeLog.Configuration; using Grynwald.ChangeLog.Filtering; @@ -13,7 +14,6 @@ using Grynwald.ChangeLog.Tasks; using Grynwald.ChangeLog.Templates; using Grynwald.Utilities.Configuration; -using Grynwald.Utilities.Logging; using Microsoft.Extensions.Logging; @@ -39,59 +39,42 @@ private class DynamicallyDeterminedSettings private readonly GenerateCommandLineParameters m_CommandLineParameters; + private readonly ILogger m_Logger; + private readonly IValidator m_CommandLineValidator; + private readonly IValidator m_ConfigurationValidator; - - public GenerateCommand(GenerateCommandLineParameters commandLineParameters) + public GenerateCommand(GenerateCommandLineParameters commandLine, ILogger logger, IValidator commandLineValidator, IValidator configurationValidator) { - m_CommandLineParameters = commandLineParameters ?? throw new ArgumentNullException(nameof(commandLineParameters)); + m_CommandLineParameters = commandLine ?? throw new ArgumentNullException(nameof(commandLine)); + m_Logger = logger ?? throw new ArgumentNullException(nameof(logger)); + m_CommandLineValidator = commandLineValidator ?? throw new ArgumentNullException(nameof(commandLineValidator)); + m_ConfigurationValidator = configurationValidator ?? throw new ArgumentNullException(nameof(configurationValidator)); } public async Task RunAsync() { - var loggerOptions = m_CommandLineParameters.Verbose - ? new SimpleConsoleLoggerConfiguration(LogLevel.Debug, true, true) - : new SimpleConsoleLoggerConfiguration(LogLevel.Information, false, true); - - // for validation of command line parameters, directly create a console logger - // bypassing the DI container because we need to validate the parameters - // before setting up DI - var logger = new SimpleConsoleLogger(loggerOptions, ""); - - if (!ValidateCommandlineParameters(m_CommandLineParameters, logger)) + if (!ValidateCommandlineParameters(m_CommandLineParameters)) return 1; - if (!TryGetRepositoryPath(m_CommandLineParameters, logger, out var repositoryPath)) + if (!TryGetRepositoryPath(m_CommandLineParameters, out var repositoryPath)) return 1; - if (!TryOpenRepository(repositoryPath, logger, out var gitRepository)) + if (!TryLoadConfiguration(repositoryPath, out var configuration)) return 1; - var configurationFilePath = GetConfigurationFilePath(m_CommandLineParameters, repositoryPath); - if (File.Exists(configurationFilePath)) - logger.LogDebug($"Using configuration file '{configurationFilePath}'"); - else - logger.LogDebug("Continuing without loading a configuration file, because no configuration file was wound"); - - - // pass repository path to configuration loader to make it available through the configuration system - var dynamicSettings = new DynamicallyDeterminedSettings() - { - RepositoryPath = repositoryPath - }; - - var configuration = ChangeLogConfigurationLoader.GetConfiguration(configurationFilePath, m_CommandLineParameters, dynamicSettings); + if (!TryOpenRepository(repositoryPath, out var gitRepository)) + return 1; using (gitRepository) { var containerBuilder = new ContainerBuilder(); - containerBuilder.RegisterType(); containerBuilder.RegisterInstance(configuration).SingleInstance(); containerBuilder.RegisterInstance(gitRepository).SingleInstance().As(); - containerBuilder.RegisterLogging(loggerOptions); + containerBuilder.RegisterLogging(CompositionRoot.CreateLoggerConfiguration(m_CommandLineParameters.Verbose)); containerBuilder.RegisterType(); @@ -115,20 +98,6 @@ public async Task RunAsync() using (var container = containerBuilder.Build()) { - var configurationValidator = container.Resolve(); - var validationResult = configurationValidator.Validate(configuration); - - if (!validationResult.IsValid) - { - foreach (var error in validationResult.Errors) - { - logger.LogError($"Invalid configuration: {error.ErrorMessage}"); - } - - logger.LogError($"Validation of configuration failed"); - return 1; - } - var pipeline = container.Resolve(); var result = await pipeline.RunAsync(); @@ -137,6 +106,7 @@ public async Task RunAsync() } } + private static string? GetConfigurationFilePath(GenerateCommandLineParameters commandlineParameters, string repositoryPath) { if (!String.IsNullOrEmpty(commandlineParameters.ConfigurationFilePath)) @@ -153,20 +123,19 @@ public async Task RunAsync() return null; } - private static bool ValidateCommandlineParameters(GenerateCommandLineParameters parameters, ILogger logger) + private bool ValidateCommandlineParameters(GenerateCommandLineParameters parameters) { - var validator = new GenerateCommandLineParametersValidator(); - var result = validator.Validate(parameters); + var result = m_CommandLineValidator.Validate(parameters); foreach (var error in result.Errors) { - logger.LogError(error.ToString()); + m_Logger.LogError(error.ToString()); } return result.IsValid; } - private static bool TryGetRepositoryPath(GenerateCommandLineParameters parameters, ILogger logger, [NotNullWhen(true)] out string? repositoryPath) + private bool TryGetRepositoryPath(GenerateCommandLineParameters parameters, [NotNullWhen(true)] out string? repositoryPath) { if (!String.IsNullOrEmpty(parameters.RepositoryPath)) { @@ -176,18 +145,18 @@ private static bool TryGetRepositoryPath(GenerateCommandLineParameters parameter if (RepositoryLocator.TryGetRepositoryPath(Environment.CurrentDirectory, out repositoryPath)) { - logger.LogInformation($"Found git repository at '{repositoryPath}'"); + m_Logger.LogInformation($"Found git repository at '{repositoryPath}'"); return true; } else { - logger.LogError($"No git repository found in '{Environment.CurrentDirectory}' or any of its parent directories"); + m_Logger.LogError($"No git repository found in '{Environment.CurrentDirectory}' or any of its parent directories"); repositoryPath = default; return false; } } - private static bool TryOpenRepository(string repositoryPath, ILogger logger, [NotNullWhen(true)] out IGitRepository? repository) + private bool TryOpenRepository(string repositoryPath, [NotNullWhen(true)] out IGitRepository? repository) { try { @@ -196,12 +165,45 @@ private static bool TryOpenRepository(string repositoryPath, ILogger logger, [No } catch (RepositoryNotFoundException ex) { - logger.LogDebug(ex, $"Failed to open repository at '{repositoryPath}'"); - logger.LogError($"'{repositoryPath}' is not a git repository"); + m_Logger.LogDebug(ex, $"Failed to open repository at '{repositoryPath}'"); + m_Logger.LogError($"'{repositoryPath}' is not a git repository"); repository = default; return false; } } + private bool TryLoadConfiguration(string repositoryPath, [NotNullWhen(true)] out ChangeLogConfiguration? configuration) + { + var configurationFilePath = GetConfigurationFilePath(m_CommandLineParameters, repositoryPath); + if (File.Exists(configurationFilePath)) + m_Logger.LogDebug($"Using configuration file '{configurationFilePath}'"); + else + m_Logger.LogDebug("Continuing without loading a configuration file, because no configuration file was wound"); + + + // pass repository path to configuration loader to make it available through the configuration system + var dynamicSettings = new DynamicallyDeterminedSettings() + { + RepositoryPath = repositoryPath + }; + + configuration = ChangeLogConfigurationLoader.GetConfiguration(configurationFilePath, m_CommandLineParameters, dynamicSettings); + + var validationResult = m_ConfigurationValidator.Validate(configuration); + + if (!validationResult.IsValid) + { + foreach (var error in validationResult.Errors) + { + m_Logger.LogError($"Invalid configuration: {error.ErrorMessage}"); + } + + m_Logger.LogError($"Validation of configuration failed"); + return false; + } + + return true; + + } } } diff --git a/src/ChangeLog/CompositionRoot.cs b/src/ChangeLog/CompositionRoot.cs new file mode 100644 index 00000000..cbb3e1d1 --- /dev/null +++ b/src/ChangeLog/CompositionRoot.cs @@ -0,0 +1,52 @@ +using System; +using Grynwald.ChangeLog.CommandLine; +using Grynwald.ChangeLog.Commands; +using Grynwald.ChangeLog.Configuration; +using Grynwald.Utilities.Logging; +using Microsoft.Extensions.Logging; + +namespace Grynwald.ChangeLog +{ + internal class CompositionRoot : IDisposable + { + + public GenerateCommand CreateGenerateCommand(GenerateCommandLineParameters commandLine) + { + if (commandLine is null) + throw new ArgumentNullException(nameof(commandLine)); + + return new GenerateCommand( + commandLine: commandLine, + logger: CreateLogger(commandLine.Verbose), + commandLineValidator: new GenerateCommandLineParametersValidator(), + configurationValidator: new ConfigurationValidator() + ); + } + + + + public ILogger CreateLogger(bool verbose) + { + var loggerFactory = new LoggerFactory(); + var provider = new SimpleConsoleLoggerProvider(CreateLoggerConfiguration(verbose)); + loggerFactory.AddProvider(provider); + + return new Logger(loggerFactory); + } + + + //TODO: Temporary workaround + public static SimpleConsoleLoggerConfiguration CreateLoggerConfiguration(bool verbose) + { + return verbose + ? new SimpleConsoleLoggerConfiguration(LogLevel.Debug, true, true) + : new SimpleConsoleLoggerConfiguration(LogLevel.Information, false, true); + } + + + public void Dispose() + { + //TODO + } + } +} diff --git a/src/ChangeLog/Program.cs b/src/ChangeLog/Program.cs index c9773eda..c3ad01c7 100644 --- a/src/ChangeLog/Program.cs +++ b/src/ChangeLog/Program.cs @@ -4,7 +4,6 @@ using System.Threading.Tasks; using CommandLine; using Grynwald.ChangeLog.CommandLine; -using Grynwald.ChangeLog.Commands; namespace Grynwald.ChangeLog { @@ -12,13 +11,11 @@ internal static class Program { private static async Task Main(string[] args) { + using var compositionRoot = new CompositionRoot(); + return await CommandLineParser.Parse(args) .MapResult( - (GenerateCommandLineParameters parsed) => - { - var command = new GenerateCommand(parsed); - return command.RunAsync(); - }, + async (GenerateCommandLineParameters generateParameters) => await compositionRoot.CreateGenerateCommand(generateParameters).RunAsync(), (DummyCommandLineParameters dummy) => { Console.Error.WriteLine("Invalid arguments");