diff --git a/docs/Reference/Generated/MigrationTools.xml b/docs/Reference/Generated/MigrationTools.xml index d507badcf..66d112224 100644 --- a/docs/Reference/Generated/MigrationTools.xml +++ b/docs/Reference/Generated/MigrationTools.xml @@ -263,27 +263,27 @@ - => @"cd3dc28e" + => @"d11381cb" - => @"cd3dc28e52a31c168b7b951d34456f95caf338f6" + => @"d11381cbf462b62fad3a9813d37bf264d8296cf6" - => @"2024-08-28T17:45:31+01:00" + => @"2024-08-28T18:43:28+01:00" - => @"221" + => @"222" - => @"v15.2.1-221-gcd3dc28e" + => @"v15.2.1-222-gd11381cb" @@ -318,7 +318,7 @@ - => @"222" + => @"223" diff --git a/src/MigrationTools.Host/Commands/InitMigrationCommand.cs b/src/MigrationTools.Host/Commands/InitMigrationCommand.cs index e5e49b4a5..5a83c9787 100644 --- a/src/MigrationTools.Host/Commands/InitMigrationCommand.cs +++ b/src/MigrationTools.Host/Commands/InitMigrationCommand.cs @@ -74,7 +74,7 @@ public override async Task ExecuteAsync(CommandContext context, InitMigrati _logger.LogInformation("Populating config with {Options}", settings.Options.ToString()); - OptionsConfigurationBuilder optionsBuilder = Services.GetService(); + OptionsConfiguration optionsBuilder = Services.GetService(); switch (settings.Options) { diff --git a/src/MigrationTools/Options/OptionsConfiguration.cs b/src/MigrationTools/Options/OptionsConfiguration.cs new file mode 100644 index 000000000..65a70a894 --- /dev/null +++ b/src/MigrationTools/Options/OptionsConfiguration.cs @@ -0,0 +1,195 @@ +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 OptionsConfiguration + { + readonly ILogger logger; + readonly IConfiguration configuration; + + private List OptionsToInclude { get; } + private Dictionary NamedOptionsToInclude { get; } + + private List catalogue; + + public OptionsConfiguration( + 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["Serilog"] = new JObject(); + configJson["Serilog"]["MinimumLevel"] = $"Information"; + var version = Assembly.GetExecutingAssembly().GetName().Version; + configJson["MigrationTools"]["Version"] = $"{version.Major}.{version.Minor}"; + configJson["MigrationTools"] = new JObject(); + configJson["MigrationTools"]["Endpoints"] = new JObject(); + configJson["MigrationTools"]["Processors"] = new JArray(); + configJson["MigrationTools"]["CommonTools"] = 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) + { + 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 = OptionsConfigurationBuilder.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 = OptionsConfigurationBuilder.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/Options/OptionsConfigurationBuilder.cs b/src/MigrationTools/Options/OptionsConfigurationBuilder.cs index ea393dbce..2d294bf4c 100644 --- a/src/MigrationTools/Options/OptionsConfigurationBuilder.cs +++ b/src/MigrationTools/Options/OptionsConfigurationBuilder.cs @@ -1,206 +1,246 @@ using System; using System.Collections.Generic; -using System.Configuration; +using System.IO; 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 + + +public static class OptionsConfigurationBuilder +{ + public static IOptions LoadConfiguration(string filePath, IOptions options, bool isCollection = false) { - readonly ILogger logger; - readonly IConfiguration configuration; + var optionsConfig = options.ConfigurationMetadata; + JObject json = File.Exists(filePath) ? JObject.Parse(File.ReadAllText(filePath)) : new JObject(); - private List OptionsToInclude { get; } - private Dictionary NamedOptionsToInclude { get; } + // Determine the path based on whether this is a collection or a section + string path = optionsConfig.PathToInstance; + + if (isCollection) + { + // Load from a collection + var collection = json.SelectToken(path.Replace(":", ".")) as JArray; - private List catalogue; + var item = collection?.FirstOrDefault(p => p[optionsConfig.ObjectName]?.ToString() == optionsConfig.OptionFor); - public OptionsConfigurationBuilder( - IConfiguration configuration, - ILogger logger, - ITelemetryLogger telemetryLogger) + return item != null ? item.ToObject(options.GetType()) as IOptions : Activator.CreateInstance(options.GetType()) as IOptions; + } + else { - this.configuration = configuration; - this.logger = logger; - OptionsToInclude = new List(); - NamedOptionsToInclude = new Dictionary(); - catalogue = AppDomain.CurrentDomain.GetMigrationToolsTypes().WithInterface().ToList(); + // Load from a section + var section = json.SelectToken(path.Replace(":", ".")); + + return section != null ? section.ToObject(options.GetType()) as IOptions : Activator.CreateInstance(options.GetType()) as IOptions; } + } - public void AddAllOptions() - { - var keyGen = new KeyGenerator(); + public static void SaveConfiguration(string filePath, IOptions options, bool isCollection = false) + { + JObject json = File.Exists(filePath) ? JObject.Parse(File.ReadAllText(filePath)) : new JObject(); - foreach (var optionType in catalogue) + // Determine the path based on whether this is a collection or a section + string path = options.ConfigurationMetadata.PathToInstance; + + string[] pathParts = path.Split(':'); + JObject currentSection = json; + + // Build the JSON structure for the section or collection + for (int i = 0; i < pathParts.Length; i++) + { + if (i == pathParts.Length - 1 && isCollection) { - switch (optionType) + // If it's a collection, create or find the JArray + if (currentSection[pathParts[i]] == null) { - 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; + currentSection[pathParts[i]] = new JArray(); } - } - } - public void AddOption(IOptions option) - { - OptionsToInclude.Add(option); - } + var collectionArray = (JArray)currentSection[pathParts[i]]; - 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 + // Check if the object already exists in the collection + var existingItem = collectionArray.FirstOrDefault(p => p[options.ConfigurationMetadata.ObjectName]?.ToString() == options.ConfigurationMetadata.OptionFor); + + if (existingItem != null) + { + // Update the existing item + var index = collectionArray.IndexOf(existingItem); + collectionArray[index] = JObject.FromObject(options); + } + else + { + // Add the new item to the collection + var newItem = JObject.FromObject(options); + newItem[options.ConfigurationMetadata.ObjectName] = options.ConfigurationMetadata.OptionFor; + collectionArray.Add(newItem); + } + } + else { - logger.LogInformation("Adding {optionName}", optionName); - OptionsToInclude.Add(CreateOptionFromType(optionType)); + // Create or navigate to the JObject for the section + if (currentSection[pathParts[i]] == null) + { + currentSection[pathParts[i]] = new JObject(); + } + currentSection = (JObject)currentSection[pathParts[i]]; } - } - private IOptions CreateOptionFromType(Type optionType) + // If it's not a collection, replace the content directly in the final section + if (!isCollection) { - IOptions instanceOfOption = (IOptions)Activator.CreateInstance(optionType); - var section = configuration.GetSection(instanceOfOption.ConfigurationMetadata.PathToSample); - section.Bind(instanceOfOption); - return instanceOfOption; + currentSection.Replace(JObject.FromObject(options)); } - public void AddOption(IOptions option, string key) - { - NamedOptionsToInclude.Add(key, option); - } + // Save the updated JSON file + File.WriteAllText(filePath, json.ToString(Newtonsoft.Json.Formatting.Indented)); + } + + public static List LoadAll(string filePath, IOptions templateOption) + { + var optionsConfig = templateOption.ConfigurationMetadata; + JObject json = File.Exists(filePath) ? JObject.Parse(File.ReadAllText(filePath)) : new JObject(); + + var foundOptions = new List(); + + // Recursively search through the entire JSON hierarchy + SearchForOptions(json, optionsConfig, foundOptions, templateOption.GetType()); - public void AddOption(string optionName, string key) + return foundOptions; + } + + private static void SearchForOptions(JToken token, ConfigurationMetadata config, List foundTools, Type optionType) + { + if (token is JObject obj) { - optionName = optionName.Replace("Options", ""); - var optionType = catalogue.FirstOrDefault(x => x.Name.StartsWith(optionName)); - if (optionType == null) + // Check if this object has the appropriate property with the value matching the config + if (obj.TryGetValue(config.ObjectName, out JToken fieldTypeToken) && fieldTypeToken.ToString() == config.OptionFor) { - logger.LogWarning("Could not find option type for {optionName}", optionName); + // Deserialize the JObject into an IOptions object + var options = obj.ToObject(optionType) as IOptions; + foundTools.Add(options); } - 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) + // Recursively search child objects + foreach (var property in obj.Properties()) { - configJson = AddOptionToConfig(configuration, configJson, item); + SearchForOptions(property.Value, config, foundTools, optionType); } - foreach (var item in NamedOptionsToInclude) + } + else if (token is JArray array) + { + // Recursively search elements in the array + foreach (var item in array) { - configJson = AddNamedOptionToConfig(configuration, configJson, item.Key, item.Value); + SearchForOptions(item, config, foundTools, optionType); } - return configJson.ToString(Newtonsoft.Json.Formatting.Indented); } + } + + public static string CreateNewConfigurationJson(IOptions options, string path, string objectName, string optionFor, bool isCollection = false, bool shouldAddObjectName = false) + { + // Load existing configuration from a file or create a new JObject if necessary + JObject configJson = new JObject(); + + // Add or update the options in the configuration using the new method signature + configJson = AddOptionsToConfiguration(configJson, options, path, objectName, optionFor, isCollection, shouldAddObjectName); + + // Return the updated JSON as a formatted string + return configJson.ToString(Newtonsoft.Json.Formatting.Indented); + } + - private static void AddSerilog(JObject configJson) + public static JObject AddOptionsToConfiguration(JObject configJson, IOptions iOption, bool shouldAddObjectName = false) { - configJson["Serilog"] = new JObject(); - configJson["Serilog"]["MinimumLevel"] = $"Information"; + return AddOptionsToConfiguration(configJson, iOption, iOption.ConfigurationMetadata.PathToInstance, shouldAddObjectName); } - private static void AddVersionInfo(JObject configJson) + public static JObject AddOptionsToConfiguration(JObject configJson, IOptions iOption, string sectionPath, bool shouldAddObjectName = false) { - var version = Assembly.GetExecutingAssembly().GetName().Version; - configJson["MigrationTools"]["Version"] = $"{version.Major}.{version.Minor}"; + return AddOptionsToConfiguration(configJson, iOption, sectionPath, iOption.ConfigurationMetadata.ObjectName, iOption.ConfigurationMetadata.OptionFor, iOption.ConfigurationMetadata.IsCollection, shouldAddObjectName); } - private JObject AddNamedOptionToConfig(IConfiguration configuration, JObject configJson, string key, IOptions option) + public static JObject AddOptionsToConfiguration( + JObject configJson, + IOptions options, + string path, + string objectName, + string optionFor, + bool isCollection = false, + bool shouldAddObjectName = false) + { + // Initialize the JObject if it was null + if (configJson == null) { - 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; + configJson = new JObject(); } - private JObject AddOptionToConfig(IConfiguration configuration, JObject configJson, IOptions option) + // Split the path into its components + string[] pathParts = path.Split(':'); + JObject currentSection = configJson; + + // Traverse or create the JSON structure for the section or collection + for (int i = 0; i < pathParts.Length; i++) { - if (option.ConfigurationMetadata.PathToInstance == null) + // If this is the last part of the path + if (i == pathParts.Length - 1) { - 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); - + if (isCollection) + { + // Ensure we have a JArray at this position + if (currentSection[pathParts[i]] == null) + { + currentSection[pathParts[i]] = new JArray(); + } + + // Add the options object as part of the collection + var collectionArray = (JArray)currentSection[pathParts[i]]; + var optionsObject = JObject.FromObject(options); + + // Add the object name if required + if (shouldAddObjectName) + { + optionsObject.AddFirst(new JProperty(objectName, optionFor)); + } + + collectionArray.Add(optionsObject); + } + else + { + // We're at the last part of the path, so add the options object here + var optionsObject = new JObject(); + + // Add the object name and options + if (shouldAddObjectName) + { + optionsObject[objectName] = optionFor; + } + + // Add the other properties from the options object + optionsObject.Merge(JObject.FromObject(options), new JsonMergeSettings + { + MergeArrayHandling = MergeArrayHandling.Concat + }); + + // Replace or add the object in the current section + currentSection[pathParts[i]] = optionsObject; + } } - catch (Exception) + else { - - logger.LogWarning("FAILED!! Adding Option: {item}", option.GetType().FullName); + // Traverse or create the JObject for the current section + if (currentSection[pathParts[i]] == null) + { + currentSection[pathParts[i]] = new JObject(); + } + currentSection = (JObject)currentSection[pathParts[i]]; } - - return configJson; } - - } - - public class KeyGenerator - { - private int _counter = 1; - - public string GetNextKey() - { - _counter++; - return $"Key{_counter}"; - } + // Return the modified JObject + return configJson; } } +} \ No newline at end of file diff --git a/src/MigrationTools/Options/OptionsManager.cs b/src/MigrationTools/Options/OptionsManager.cs deleted file mode 100644 index 2165568e0..000000000 --- a/src/MigrationTools/Options/OptionsManager.cs +++ /dev/null @@ -1,326 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Security.AccessControl; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; - -namespace MigrationTools.Options -{ - public class OptionsManager - { - - public static dynamic GetOptionsManager(Type option) - { - Type optionsManagerType = typeof(OptionsManager<>); - Type specificOptionsManagerType = optionsManagerType.MakeGenericType(option); - - object optionsManagerInstance = Activator.CreateInstance( - specificOptionsManagerType - ); - return optionsManagerInstance; - } - - public static string CreateNewConfigurationJson(IOptions iOption, bool isCollection = false) - { - Type optionsManagerType = typeof(OptionsManager<>).MakeGenericType(iOption.GetType()); - - // Create an instance of OptionsManager - object optionsManagerInstance = Activator.CreateInstance(optionsManagerType); - - // Get the method information for CreateNewConfigurationJson - MethodInfo createMethod = optionsManagerType.GetMethod("CreateNewConfigurationJson"); - - // Prepare parameters for the method - object[] parameters = { iOption, isCollection }; - - // Invoke the method dynamically - string result = (string)createMethod.Invoke(optionsManagerInstance, parameters); - - // Output the result - return result; - } - - public static JObject AddOptionsToConfiguration(JObject configJson, IOptions iOption, bool shouldAddObjectName = false) - { - //JObject configJson, TOptions options, string path, string objectName, string optionFor, bool isCollection = false, bool shouldAddObjectName = false - return AddOptionsToConfiguration(configJson, iOption, iOption.ConfigurationMetadata.PathToInstance,shouldAddObjectName); - } - - public static JObject AddOptionsToConfiguration(JObject configJson, IOptions iOption, string sectionPath, bool shouldAddObjectName = false) - { - Type optionsManagerType = typeof(OptionsManager<>).MakeGenericType(iOption.GetType()); - - // Create an instance of OptionsManager - object optionsManagerInstance = Activator.CreateInstance(optionsManagerType); - - // Get the method information for CreateNewConfigurationJson - MethodInfo createMethod = optionsManagerType.GetMethod("AddOptionsToConfiguration"); - - // Prepare parameters for the method - object[] parameters = { configJson, iOption, sectionPath, iOption.ConfigurationMetadata.ObjectName, iOption.ConfigurationMetadata.OptionFor, iOption.ConfigurationMetadata.IsCollection, shouldAddObjectName }; - - // Invoke the method dynamically - JObject result = (JObject)createMethod.Invoke(optionsManagerInstance, parameters); - - // Output the result - return result; - } - - public static ConfigurationMetadata GetOptionsConfiguration(Type option) - { - // ActivatorUtilities.CreateInstance(option); - IOptions optionInsance = (IOptions)Activator.CreateInstance(option); - return optionInsance.ConfigurationMetadata; - } - - } - - public class OptionsManager where TOptions : class, IOptions, new() - { - public TOptions LoadConfiguration(string filePath, bool isCollection = false) - { - var optionsConfig = GetOptionsConfiguration(); - JObject json = File.Exists(filePath) ? JObject.Parse(File.ReadAllText(filePath)) : new JObject(); - - // Determine the path based on whether this is a collection or a section - string path = optionsConfig.PathToInstance; - - if (isCollection) - { - // Load from a collection - var collection = json.SelectToken(path.Replace(":", ".")) as JArray; - - var item = collection?.FirstOrDefault(p => p[optionsConfig.ObjectName]?.ToString() == optionsConfig.OptionFor); - - return item != null ? item.ToObject() : new TOptions(); - } - else - { - // Load from a section - var section = json.SelectToken(path.Replace(":", ".")); - - return section != null ? section.ToObject() : new TOptions(); - } - } - - public void SaveConfiguration(string filePath, TOptions options, bool isCollection = false) - { - JObject json = File.Exists(filePath) ? JObject.Parse(File.ReadAllText(filePath)) : new JObject(); - - // Determine the path based on whether this is a collection or a section - string path = options.ConfigurationMetadata.PathToInstance; - - string[] pathParts = path.Split(':'); - JObject currentSection = json; - - // Build the JSON structure for the section or collection - for (int i = 0; i < pathParts.Length; i++) - { - if (i == pathParts.Length - 1 && isCollection) - { - // If it's a collection, create or find the JArray - if (currentSection[pathParts[i]] == null) - { - currentSection[pathParts[i]] = new JArray(); - } - - var collectionArray = (JArray)currentSection[pathParts[i]]; - - // Check if the object already exists in the collection - var existingItem = collectionArray.FirstOrDefault(p => p[options.ConfigurationMetadata.ObjectName]?.ToString() == options.ConfigurationMetadata.OptionFor); - - if (existingItem != null) - { - // Update the existing item - var index = collectionArray.IndexOf(existingItem); - collectionArray[index] = JObject.FromObject(options); - } - else - { - // Add the new item to the collection - var newItem = JObject.FromObject(options); - newItem[options.ConfigurationMetadata.ObjectName] = options.ConfigurationMetadata.OptionFor; - collectionArray.Add(newItem); - } - } - else - { - // Create or navigate to the JObject for the section - if (currentSection[pathParts[i]] == null) - { - currentSection[pathParts[i]] = new JObject(); - } - currentSection = (JObject)currentSection[pathParts[i]]; - } - } - - // If it's not a collection, replace the content directly in the final section - if (!isCollection) - { - currentSection.Replace(JObject.FromObject(options)); - } - - // Save the updated JSON file - File.WriteAllText(filePath, json.ToString(Formatting.Indented)); - } - - public List LoadAll(string filePath) - { - var optionsConfig = GetOptionsConfiguration(); - JObject json = File.Exists(filePath) ? JObject.Parse(File.ReadAllText(filePath)) : new JObject(); - - var foundOptions = new List(); - - // Recursively search through the entire JSON hierarchy - SearchForOptions(json, optionsConfig, foundOptions); - - return foundOptions; - } - - - private void SearchForOptions(JToken token, ConfigurationMetadata config, List foundTools) - { - if (token is JObject obj) - { - // Check if this object has a "FieldType" property with the value "FieldMappingTool" - if (obj.TryGetValue(config.ObjectName, out JToken fieldTypeToken) && fieldTypeToken.ToString() == config.OptionFor) - { - // Deserialize the JObject into a FieldMappingToolOptions object - var options = obj.ToObject(); - foundTools.Add(options); - } - - // Recursively search child objects - foreach (var property in obj.Properties()) - { - SearchForOptions(property.Value, config, foundTools); - } - } - else if (token is JArray array) - { - // Recursively search elements in the array - foreach (var item in array) - { - SearchForOptions(item, config, foundTools); - } - } - } - - public string CreateNewConfigurationJson(TOptions options, string path, string objectName, string optionFor, bool isCollection = false, bool shouldAddObjectName = false) - { - // Load existing configuration from a file or create a new JObject if necessary - JObject configJson = new JObject(); - - // Add or update the options in the configuration using the new method signature - configJson = AddOptionsToConfiguration(configJson, options, path, objectName, optionFor, isCollection, shouldAddObjectName); - - // Return the updated JSON as a formatted string - return configJson.ToString(Formatting.Indented); - } - - // New method that updates the configuration - public JObject AddOptionsToConfiguration( - JObject configJson, - TOptions options, - string path, - string objectName, - string optionFor, - bool isCollection = false, - bool shouldAddObjectName = false) - { - // Initialize the JObject if it was null - if (configJson == null) - { - configJson = new JObject(); - } - - // Split the path into its components - string[] pathParts = path.Split(':'); - JObject currentSection = configJson; - - // Traverse or create the JSON structure for the section or collection - for (int i = 0; i < pathParts.Length; i++) - { - // If this is the last part of the path - if (i == pathParts.Length - 1) - { - if (isCollection) - { - // Ensure we have a JArray at this position - if (currentSection[pathParts[i]] == null) - { - currentSection[pathParts[i]] = new JArray(); - } - - // Add the options object as part of the collection - var collectionArray = (JArray)currentSection[pathParts[i]]; - var optionsObject = JObject.FromObject(options); - - // Add the object name if required - if (shouldAddObjectName) - { - optionsObject.AddFirst(new JProperty(objectName, optionFor)); - } - - collectionArray.Add(optionsObject); - } - else - { - // We're at the last part of the path, so add the options object here - var optionsObject = new JObject(); - - // Add the object name and options - if (shouldAddObjectName) - { - optionsObject[objectName] = optionFor; - } - - // Add the other properties from the options object - optionsObject.Merge(JObject.FromObject(options), new JsonMergeSettings - { - MergeArrayHandling = MergeArrayHandling.Concat - }); - - // Replace or add the object in the current section - currentSection[pathParts[i]] = optionsObject; - } - } - else - { - // Traverse or create the JObject for the current section - if (currentSection[pathParts[i]] == null) - { - currentSection[pathParts[i]] = new JObject(); - } - currentSection = (JObject)currentSection[pathParts[i]]; - } - } - - // Return the modified JObject - return configJson; - } - - - - - - - private ConfigurationMetadata GetOptionsConfiguration() - { - TOptions options = new TOptions(); - return options.ConfigurationMetadata; - } - - - - - - - } -} diff --git a/src/MigrationTools/ServiceCollectionExtensions.cs b/src/MigrationTools/ServiceCollectionExtensions.cs index 47e16bb0a..d937eb13c 100644 --- a/src/MigrationTools/ServiceCollectionExtensions.cs +++ b/src/MigrationTools/ServiceCollectionExtensions.cs @@ -28,7 +28,7 @@ public static OptionsBuilder AddMigrationToolsOptions(this I public static void AddMigrationToolServices(this IServiceCollection context, IConfiguration configuration, string configFile = "configuration.json") { - context.AddSingleton(); + context.AddSingleton(); context.AddConfiguredEndpoints(configuration); //Containers context.AddTransient();