diff --git a/docs/Reference/Generated/MigrationTools.xml b/docs/Reference/Generated/MigrationTools.xml index 52872266d..d507badcf 100644 --- a/docs/Reference/Generated/MigrationTools.xml +++ b/docs/Reference/Generated/MigrationTools.xml @@ -263,27 +263,27 @@ - => @"b2f20d07" + => @"cd3dc28e" - => @"b2f20d070b957515450ac797a47507c6f35126fa" + => @"cd3dc28e52a31c168b7b951d34456f95caf338f6" - => @"2024-08-28T14:24:26+01:00" + => @"2024-08-28T17:45:31+01:00" - => @"220" + => @"221" - => @"v15.2.1-220-gb2f20d07" + => @"v15.2.1-221-gcd3dc28e" @@ -318,7 +318,7 @@ - => @"221" + => @"222" diff --git a/src/MigrationTools.ConsoleFull/Properties/launchSettings.json b/src/MigrationTools.ConsoleFull/Properties/launchSettings.json index 5def193ce..38d095386 100644 --- a/src/MigrationTools.ConsoleFull/Properties/launchSettings.json +++ b/src/MigrationTools.ConsoleFull/Properties/launchSettings.json @@ -25,7 +25,7 @@ }, "init Options-Reference": { "commandName": "Project", - "commandLineArgs": "init --options Reference -c configuration-ref.json" + "commandLineArgs": "init --options Reference -c configuration-ref.json --overwrite" }, "Upgrade": { "commandName": "Project", diff --git a/src/MigrationTools.Host/Commands/InitMigrationCommand.cs b/src/MigrationTools.Host/Commands/InitMigrationCommand.cs index 776ec20f1..e5e49b4a5 100644 --- a/src/MigrationTools.Host/Commands/InitMigrationCommand.cs +++ b/src/MigrationTools.Host/Commands/InitMigrationCommand.cs @@ -7,6 +7,7 @@ using Elmah.Io.Client; using Microsoft.ApplicationInsights.DataContracts; using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; @@ -21,15 +22,19 @@ namespace MigrationTools.Host.Commands { internal class InitMigrationCommand : AsyncCommand { + public IServiceProvider Services { get; } + private readonly ILogger _logger; private readonly ITelemetryLogger Telemetery; private readonly IHostApplicationLifetime _appLifetime; public InitMigrationCommand( + IServiceProvider services, ILogger logger, ITelemetryLogger telemetryLogger, IHostApplicationLifetime appLifetime) { + Services = services; _logger = logger; Telemetery = telemetryLogger; _appLifetime = appLifetime; @@ -66,72 +71,45 @@ public override async Task ExecuteAsync(CommandContext context, InitMigrati .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) .Build(); - // get source IOptions bits - List allMigrationTypes = AppDomain.CurrentDomain.GetMigrationToolsTypes().ToList(); - var allOptions = allMigrationTypes.WithInterface(); - JObject configJson = new JObject(); _logger.LogInformation("Populating config with {Options}", settings.Options.ToString()); - List optionsToInclude = null; - Dictionary endpointsToInclude = null; + + OptionsConfigurationBuilder optionsBuilder = Services.GetService(); + switch (settings.Options) { case OptionsMode.Reference: - + optionsBuilder.AddAllOptions(); break; case OptionsMode.Basic: - optionsToInclude = new List() { "TfsWorkItemMigrationProcessor", "FieldMappingTool", "FieldLiteralMap" }; - endpointsToInclude = new Dictionary () { { "Source", "TfsTeamProjectEndpoint" }, { "Target", "TfsTeamProjectEndpoint" } }; + optionsBuilder.AddOption("TfsWorkItemMigrationProcessor"); + optionsBuilder.AddOption("FieldMappingTool"); + optionsBuilder.AddOption("FieldLiteralMap"); + optionsBuilder.AddOption("TfsTeamProjectEndpoint", "Source"); + optionsBuilder.AddOption("TfsTeamProjectEndpoint", "Target"); break; case OptionsMode.WorkItemTracking: - optionsToInclude = new List() { "TfsWorkItemMigrationProcessor", "FieldMappingTool", "FieldLiteralMap" }; - endpointsToInclude = new Dictionary() { { "Source", "TfsTeamProjectEndpoint" }, { "Target", "TfsTeamProjectEndpoint" } }; + optionsBuilder.AddOption("TfsWorkItemMigrationProcessor"); + optionsBuilder.AddOption("FieldMappingTool"); + optionsBuilder.AddOption("FieldLiteralMap"); + optionsBuilder.AddOption("TfsTeamProjectEndpoint", "Source"); + optionsBuilder.AddOption("TfsTeamProjectEndpoint", "Target"); + break; + case OptionsMode.PipelineProcessor: + optionsBuilder.AddOption("AzureDevOpsPipelineProcessor"); + optionsBuilder.AddOption("AzureDevOpsEndpoint", "Source"); + optionsBuilder.AddOption("AzureDevOpsEndpoint", "Target"); break; - case OptionsMode.PipelineProcessor: default: - optionsToInclude = new List() { "AzureDevOpsPipelineProcessor"}; - endpointsToInclude = new Dictionary() { { "Source", "AzureDevOpsEndpoint" }, { "Target", "AzureDevOpsEndpoint" } }; + optionsBuilder.AddAllOptions(); break; } - if (endpointsToInclude !=null) - { - foreach (var item in endpointsToInclude) - { - var item2 = allOptions.WithInterface().FirstOrDefault(x => x.Name.StartsWith(item.Value)); - configJson = AddEndpointOptionToConfig(configuration, configJson, item.Key, item2); - } - } else - { - _logger.LogWarning($"You are adding all of the EndPoints, there may be some that cant be added and will cause an error..."); - int epNo = 1; - foreach (var item in allOptions.WithInterface()) - { - configJson = AddEndpointOptionToConfig(configuration, configJson, $"Endpoint{epNo}", item); - epNo++; - } - } + string json = optionsBuilder.Build(); - if (optionsToInclude != null) - { - foreach (var item in optionsToInclude) - { - var item2 = allOptions.FirstOrDefault(x => x.Name.StartsWith(item)); - configJson = AddOptionToConfig(configuration, configJson, item2); - } - } else - { - _logger.LogWarning($"You are adding all of the Options, there may be some that cant be added and will cause an error..."); - foreach (var item in allOptions) - { - configJson = AddOptionToConfig(configuration, configJson, item); - } - } - - - File.WriteAllText(configFile, configJson.ToString(Formatting.Indented)); + File.WriteAllText(configFile, json); _logger.LogInformation("New {configFile} file has been created", configFile); - _logger.LogInformation(configJson.ToString(Formatting.Indented)); + _logger.LogInformation(json); } _exitCode = 0; @@ -149,45 +127,5 @@ public override async Task ExecuteAsync(CommandContext context, InitMigrati } return _exitCode; } - - private JObject AddEndpointOptionToConfig(IConfigurationRoot configuration, JObject configJson, string key, Type endpointType) - { - IOptions instanceOfOption = (IOptions)Activator.CreateInstance(endpointType); - var section = configuration.GetSection(instanceOfOption.ConfigurationMetadata.PathToInstance); - section.Bind(instanceOfOption); - try - { - //instanceOfOption.ConfigurationMetadata.Path = $"MigrationTools:Endpoints:{key}"; - var hardPath = $"MigrationTools:Endpoints:{key}"; - configJson = Options.OptionsManager.AddOptionsToConfiguration(configJson, instanceOfOption, hardPath, true); - _logger.LogInformation("Adding Option: {item}", endpointType.Name); - } - catch (Exception) - { - - _logger.LogInformation("FAILED!! Adding Option: {item}", endpointType.FullName); - } - - return configJson; - } - - private JObject AddOptionToConfig(IConfigurationRoot configuration, JObject configJson, Type item) - { - IOptions instanceOfOption = (IOptions)Activator.CreateInstance(item); - var section = configuration.GetSection(instanceOfOption.ConfigurationMetadata.PathToInstance); - section.Bind(instanceOfOption); - try - { - configJson = Options.OptionsManager.AddOptionsToConfiguration(configJson, instanceOfOption, false); - _logger.LogInformation("Adding Option: {item}", item.Name); - } - catch (Exception) - { - - _logger.LogInformation("FAILED!! Adding Option: {item}", item.FullName); - } - - return configJson; - } } } diff --git a/src/MigrationTools.Host/ServiceCollectionExtensions.cs b/src/MigrationTools.Host/ServiceCollectionExtensions.cs index 33c0da323..f169f7874 100644 --- a/src/MigrationTools.Host/ServiceCollectionExtensions.cs +++ b/src/MigrationTools.Host/ServiceCollectionExtensions.cs @@ -1,5 +1,6 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using MigrationTools.Options; namespace MigrationTools { @@ -12,6 +13,7 @@ public static IServiceCollection AddConfiguredService(this IServiceCollection co //collection.Configure //collection.Configure(config); //return collection.AddTransient(); + return collection; } } diff --git a/src/MigrationTools/Options/OptionsBuilder.cs b/src/MigrationTools/Options/OptionsBuilder.cs deleted file mode 100644 index b79144a36..000000000 --- a/src/MigrationTools/Options/OptionsBuilder.cs +++ /dev/null @@ -1,116 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Configuration; -using System.Linq; -using System.Text; -using Elmah.Io.Client; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Logging; -using Microsoft.VisualStudio.Services.Audit; -using Microsoft.VisualStudio.Services.Common.CommandLine; -using Newtonsoft.Json.Linq; - -namespace MigrationTools.Options -{ - public class OptionsBuilder - { - readonly ILogger logger; - readonly IConfiguration configuration; - - private List OptionsToInclude { get; } - private Dictionary NamedOptionsToInclude { get; } - - private List catalogue; - - public OptionsBuilder( - IConfigurationRoot configuration, - ILogger logger, - ITelemetryLogger telemetryLogger) - { - this.configuration = configuration; - this.logger = logger; - OptionsToInclude = new List(); - NamedOptionsToInclude = new Dictionary(); - catalogue = AppDomain.CurrentDomain.GetMigrationToolsTypes().WithInterface().ToList(); - } - - public void AddOption(IOptions option) - { - OptionsToInclude.Add(option); - } - - public void AddOption(string optionName) - { - optionName = optionName.Replace("Options", "").Replace("Config", ""); - var optionType = catalogue.FirstOrDefault(x => x.Name.StartsWith(optionName)); - OptionsToInclude.Add(CreateOptionFromType(optionType)); - } - - private IOptions CreateOptionFromType(Type optionType) - { - IOptions instanceOfOption = (IOptions)Activator.CreateInstance(optionType); - var section = configuration.GetSection(instanceOfOption.ConfigurationMetadata.PathToInstance); - section.Bind(instanceOfOption); - return instanceOfOption; - } - - public void AddOption(IOptions option, string key) - { - NamedOptionsToInclude.Add(key, option); - } - - public void AddOption(string optionName, string key) - { - optionName = optionName.Replace("Options", "").Replace("Config", ""); - var optionType = catalogue.FirstOrDefault(x => x.Name.StartsWith(optionName)); - NamedOptionsToInclude.Add(key, CreateOptionFromType(optionType)); - } - - public string Build() - { - JObject configJson = new JObject(); - foreach (var item in OptionsToInclude) - { - configJson = AddOptionToConfig(configuration, configJson, item); - } - foreach (var item in NamedOptionsToInclude) - { - configJson = AddNamedOptionToConfig(configuration, configJson, item.Key, item.Value); - } - return configJson.ToString(Newtonsoft.Json.Formatting.Indented); - } - - private JObject AddNamedOptionToConfig(IConfiguration configuration, JObject configJson, string key, IOptions option) - { - try - { - var hardPath = $"MigrationTools:Endpoints:{key}"; - configJson = Options.OptionsManager.AddOptionsToConfiguration(configJson, option, hardPath, true); - logger.LogInformation("Adding Option: {item}", option.GetType().Name); - } - catch (Exception) - { - - logger.LogInformation("FAILED!! Adding Option: {item}", option.GetType().FullName); - } - - return configJson; - } - - private JObject AddOptionToConfig(IConfiguration configuration, JObject configJson, IOptions option) - { - try - { - configJson = Options.OptionsManager.AddOptionsToConfiguration(configJson, option, false); - logger.LogInformation("Adding Option: {item}", option.GetType().Name); - } - catch (Exception) - { - - logger.LogInformation("FAILED!! Adding Option: {item}", option.GetType().FullName); - } - - return configJson; - } - } -} diff --git a/src/MigrationTools/Options/OptionsConfigurationBuilder.cs b/src/MigrationTools/Options/OptionsConfigurationBuilder.cs new file mode 100644 index 000000000..ea393dbce --- /dev/null +++ b/src/MigrationTools/Options/OptionsConfigurationBuilder.cs @@ -0,0 +1,206 @@ +using System; +using System.Collections.Generic; +using System.Configuration; +using System.Linq; +using System.Reflection; +using System.Text; +using Elmah.Io.Client; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; +using Microsoft.VisualStudio.Services.Audit; +using Microsoft.VisualStudio.Services.Common.CommandLine; +using MigrationTools.EndpointEnrichers; +using MigrationTools.Endpoints.Infrastructure; +using MigrationTools.Enrichers; +using Newtonsoft.Json.Linq; +using Serilog.Core; + +namespace MigrationTools.Options +{ + public class OptionsConfigurationBuilder + { + readonly ILogger logger; + readonly IConfiguration configuration; + + private List OptionsToInclude { get; } + private Dictionary NamedOptionsToInclude { get; } + + private List catalogue; + + public OptionsConfigurationBuilder( + IConfiguration configuration, + ILogger logger, + ITelemetryLogger telemetryLogger) + { + this.configuration = configuration; + this.logger = logger; + OptionsToInclude = new List(); + NamedOptionsToInclude = new Dictionary(); + catalogue = AppDomain.CurrentDomain.GetMigrationToolsTypes().WithInterface().ToList(); + } + + public void AddAllOptions() + { + var keyGen = new KeyGenerator(); + + foreach (var optionType in catalogue) + { + switch (optionType) + { + case Type t when typeof(IEndpointOptions).IsAssignableFrom(t): + AddOption(optionType.Name, keyGen.GetNextKey()); + break; + case Type t when typeof(IProcessorEnricherOptions).IsAssignableFrom(t): + logger.LogInformation("Skipping ProcessorEnricherOptions: {optionType}", optionType.Name); + break; + case Type t when typeof(IEndpointEnricherOptions).IsAssignableFrom(t): + logger.LogInformation("Skipping ProcessorEnricherOptions: {optionType}", optionType.Name); + break; + default: + AddOption(optionType.Name); + break; + } + } + } + + public void AddOption(IOptions option) + { + OptionsToInclude.Add(option); + } + + public void AddOption(string optionName) + { + optionName = optionName.Replace("Options", ""); + var optionType = catalogue.FirstOrDefault(x => x.Name.StartsWith(optionName)); + if (optionType == null) + { + logger.LogWarning("Could not find option type for {optionName}", optionName); + } else + { + logger.LogInformation("Adding {optionName}", optionName); + OptionsToInclude.Add(CreateOptionFromType(optionType)); + } + + } + + private IOptions CreateOptionFromType(Type optionType) + { + IOptions instanceOfOption = (IOptions)Activator.CreateInstance(optionType); + var section = configuration.GetSection(instanceOfOption.ConfigurationMetadata.PathToSample); + section.Bind(instanceOfOption); + return instanceOfOption; + } + + public void AddOption(IOptions option, string key) + { + NamedOptionsToInclude.Add(key, option); + } + + public void AddOption(string optionName, string key) + { + optionName = optionName.Replace("Options", ""); + var optionType = catalogue.FirstOrDefault(x => x.Name.StartsWith(optionName)); + if (optionType == null) + { + logger.LogWarning("Could not find option type for {optionName}", optionName); + } + else + { + logger.LogInformation("Adding {optionName} as {key}", optionName, key); + NamedOptionsToInclude.Add(key, CreateOptionFromType(optionType)); + } + } + + public string Build() + { + logger.LogInformation("Building Configuration"); + JObject configJson = new JObject(); + configJson["MigrationTools"] = new JObject(); + configJson["MigrationTools"]["Endpoints"] = new JObject(); + configJson["MigrationTools"]["Processors"] = new JArray(); + configJson["MigrationTools"]["CommonTools"] = new JObject(); + // Add the version element + AddSerilog(configJson); + AddVersionInfo(configJson); + foreach (var item in OptionsToInclude) + { + configJson = AddOptionToConfig(configuration, configJson, item); + } + foreach (var item in NamedOptionsToInclude) + { + configJson = AddNamedOptionToConfig(configuration, configJson, item.Key, item.Value); + } + return configJson.ToString(Newtonsoft.Json.Formatting.Indented); + } + + private static void AddSerilog(JObject configJson) + { + configJson["Serilog"] = new JObject(); + configJson["Serilog"]["MinimumLevel"] = $"Information"; + } + + private static void AddVersionInfo(JObject configJson) + { + var version = Assembly.GetExecutingAssembly().GetName().Version; + configJson["MigrationTools"]["Version"] = $"{version.Major}.{version.Minor}"; + } + + private JObject AddNamedOptionToConfig(IConfiguration configuration, JObject configJson, string key, IOptions option) + { + if (option.ConfigurationMetadata.PathToInstance == null) + { + logger.LogWarning("Skipping Option: {item} with {key} as it has no PathToInstance", option.GetType().Name, key); + return configJson; + } + try + { + var hardPath = $"MigrationTools:Endpoints:{key}"; + logger.LogInformation("Building Option: {item} to {hardPath}", option.GetType().Name, hardPath); + configJson = Options.OptionsManager.AddOptionsToConfiguration(configJson, option, hardPath, true); + + } + catch (Exception) + { + + logger.LogWarning("FAILED!! Adding Option: {item}", option.GetType().FullName); + } + + return configJson; + } + + private JObject AddOptionToConfig(IConfiguration configuration, JObject configJson, IOptions option) + { + if (option.ConfigurationMetadata.PathToInstance == null) + { + logger.LogWarning("Skipping Option: {item} as it has no PathToInstance", option.GetType().Name); + return configJson; + } + try + { + logger.LogInformation("Building Option: {item} to {path}", option.GetType().Name, option.ConfigurationMetadata.PathToInstance); + configJson = Options.OptionsManager.AddOptionsToConfiguration(configJson, option, false); + + } + catch (Exception) + { + + logger.LogWarning("FAILED!! Adding Option: {item}", option.GetType().FullName); + } + + return configJson; + } + + + } + + public class KeyGenerator + { + private int _counter = 1; + + public string GetNextKey() + { + _counter++; + return $"Key{_counter}"; + } + } +} diff --git a/src/MigrationTools/ServiceCollectionExtensions.cs b/src/MigrationTools/ServiceCollectionExtensions.cs index b2bde4857..47e16bb0a 100644 --- a/src/MigrationTools/ServiceCollectionExtensions.cs +++ b/src/MigrationTools/ServiceCollectionExtensions.cs @@ -28,6 +28,7 @@ public static OptionsBuilder AddMigrationToolsOptions(this I public static void AddMigrationToolServices(this IServiceCollection context, IConfiguration configuration, string configFile = "configuration.json") { + context.AddSingleton(); context.AddConfiguredEndpoints(configuration); //Containers context.AddTransient();