Skip to content

Commit 71145dd

Browse files
CopilotMrHinsh
andcommitted
Implement TfsNodeStructureTool mapping conversion from dictionary to array format
Co-authored-by: MrHinsh <[email protected]>
1 parent f1b6b03 commit 71145dd

File tree

1 file changed

+114
-18
lines changed

1 file changed

+114
-18
lines changed

src/MigrationTools/Options/OptionsConfigurationUpgrader.cs

Lines changed: 114 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ public OptionsConfigurationBuilder UpgradeConfiguration(OptionsConfigurationBuil
8888
optionsBuilder.AddOption(ParseV1FieldMaps(configuration));
8989
optionsBuilder.AddOption(ParseSectionCollectionWithTypePropertyNameToList(configuration, "Processors", "$type"));
9090
optionsBuilder.AddOption(ParseSectionCollectionWithTypePropertyNameToList(configuration, "CommonEnrichersConfig", "$type"));
91+
optionsBuilder.AddOption(ParseSectionCollectionWithTypePropertyNameToList(configuration, "CommonTools", "$type"));
9192
if (!IsSectionNullOrEmpty(configuration.GetSection("Source")) || !IsSectionNullOrEmpty(configuration.GetSection("Target")))
9293
{
9394
optionsBuilder.AddOption(ParseSectionWithTypePropertyNameToOptions(configuration, "Source", "$type"), "Source");
@@ -154,47 +155,120 @@ private List<IOptions> ParseSectionCollectionWithTypePropertyNameToList(IConfigu
154155
var newOptionTypeString = ParseOptionsType(optionTypeString);
155156
_logger.LogDebug("Upgrading {group} item {old} to {new}", path, optionTypeString, newOptionTypeString);
156157
var option = GetOptionWithDefaults(configuration, newOptionTypeString);
157-
childSection.Bind(option);
158-
switch (optionTypeString)
158+
if (option != null)
159159
{
160-
case "TfsNodeStructureOptions":
161-
MapTfsNodeStructureOptions(childSection, option);
162-
_logger.LogWarning("Empty type string found in {path}", path);
163-
break;
164-
default:
165-
break;
160+
childSection.Bind(option);
161+
switch (optionTypeString)
162+
{
163+
case "TfsNodeStructureOptions":
164+
MapTfsNodeStructureOptions(childSection, option);
165+
break;
166+
default:
167+
break;
168+
}
169+
options.Add(option);
170+
}
171+
else
172+
{
173+
_logger.LogWarning("Could not create option for type {newOptionTypeString} (original: {optionTypeString})", newOptionTypeString, optionTypeString);
166174
}
167-
168-
169-
options.Add(option);
170175
}
171176

172177
return options;
173178
}
174179

175180
private void MapTfsNodeStructureOptions(IConfigurationSection section, dynamic option)
176181
{
177-
// Map AreaMaps from the old structure to the new Areas.Mappings
182+
// Handle conversion from old dictionary format to new array format for Areas.Mappings
183+
var areasMappingsSection = section.GetSection("Areas:Mappings");
184+
if (areasMappingsSection.Exists() && areasMappingsSection.GetChildren().Any())
185+
{
186+
// Check if this is a dictionary format (children have Key/Value pairs rather than indexed)
187+
var firstChild = areasMappingsSection.GetChildren().FirstOrDefault();
188+
if (firstChild != null && !int.TryParse(firstChild.Key, out _))
189+
{
190+
// This is dictionary format, convert to NodeMapping objects
191+
option.Areas.Mappings.Clear();
192+
foreach (var mapping in areasMappingsSection.GetChildren())
193+
{
194+
// Create NodeMapping using reflection or dynamic approach
195+
dynamic nodeMapping = CreateNodeMapping(mapping.Key, mapping.Value);
196+
option.Areas.Mappings.Add(nodeMapping);
197+
}
198+
_logger.LogDebug("Converted Areas.Mappings from dictionary format to array format for TfsNodeStructureTool");
199+
}
200+
}
201+
202+
// Handle conversion from old dictionary format to new array format for Iterations.Mappings
203+
var iterationsMappingsSection = section.GetSection("Iterations:Mappings");
204+
if (iterationsMappingsSection.Exists() && iterationsMappingsSection.GetChildren().Any())
205+
{
206+
// Check if this is a dictionary format (children have Key/Value pairs rather than indexed)
207+
var firstChild = iterationsMappingsSection.GetChildren().FirstOrDefault();
208+
if (firstChild != null && !int.TryParse(firstChild.Key, out _))
209+
{
210+
// This is dictionary format, convert to NodeMapping objects
211+
option.Iterations.Mappings.Clear();
212+
foreach (var mapping in iterationsMappingsSection.GetChildren())
213+
{
214+
// Create NodeMapping using reflection or dynamic approach
215+
dynamic nodeMapping = CreateNodeMapping(mapping.Key, mapping.Value);
216+
option.Iterations.Mappings.Add(nodeMapping);
217+
}
218+
_logger.LogDebug("Converted Iterations.Mappings from dictionary format to array format for TfsNodeStructureTool");
219+
}
220+
}
221+
222+
// Legacy support: Map AreaMaps from the old structure to the new Areas.Mappings (if present)
178223
var areaMaps = section.GetSection("AreaMaps").GetChildren();
179224
foreach (var areaMap in areaMaps)
180225
{
181226
var key = areaMap.Key;
182227
var value = areaMap.Value;
183-
option.Areas.Mappings.Add(key, value);
228+
dynamic nodeMapping = CreateNodeMapping(key, value);
229+
option.Areas.Mappings.Add(nodeMapping);
184230
}
185231

186-
// Map IterationMaps from the old structure to the new Iterations.Mappings
232+
// Legacy support: Map IterationMaps from the old structure to the new Iterations.Mappings (if present)
187233
var iterationMaps = section.GetSection("IterationMaps").GetChildren();
188234
foreach (var iterationMap in iterationMaps)
189235
{
190236
var key = iterationMap.Key;
191237
var value = iterationMap.Value;
192-
option.Iterations.Mappings.Add(key, value);
238+
dynamic nodeMapping = CreateNodeMapping(key, value);
239+
option.Iterations.Mappings.Add(nodeMapping);
193240
}
194-
// Now map the intermediate structure back into the original `option` object
241+
195242
_logger.LogDebug("Mapped TfsNodeStructureOptions to TfsNodeStructureTool structure and updated the options object.");
196243
}
197244

245+
private object CreateNodeMapping(string match, string replacement)
246+
{
247+
// Try to find the NodeMapping type from loaded assemblies
248+
var nodeMappingType = AppDomain.CurrentDomain.GetAssemblies()
249+
.SelectMany(a => a.GetTypes())
250+
.FirstOrDefault(t => t.Name == "NodeMapping" && t.Namespace == "MigrationTools.Tools");
251+
252+
if (nodeMappingType != null)
253+
{
254+
var nodeMapping = Activator.CreateInstance(nodeMappingType);
255+
var matchProperty = nodeMappingType.GetProperty("Match");
256+
var replacementProperty = nodeMappingType.GetProperty("Replacement");
257+
258+
if (matchProperty != null && replacementProperty != null)
259+
{
260+
matchProperty.SetValue(nodeMapping, match);
261+
replacementProperty.SetValue(nodeMapping, replacement);
262+
}
263+
264+
return nodeMapping;
265+
}
266+
267+
// Fallback: create a simple object with the properties
268+
_logger.LogWarning("Could not find NodeMapping type, creating fallback object");
269+
return new { Match = match, Replacement = replacement };
270+
}
271+
198272

199273
private List<IOptions> ParseV1FieldMaps(IConfiguration configuration)
200274
{
@@ -247,25 +321,47 @@ private IOptions ParseV1TfsChangeSetMappingToolOptions(IConfiguration configurat
247321
{
248322
_logger.LogInformation("Upgrading {old} to {new}", "ChangeSetMappingFile", "TfsChangeSetMappingTool");
249323
var changeSetMappingOptions = configuration.GetValue<string>("ChangeSetMappingFile");
324+
325+
// Skip if no ChangeSetMappingFile is configured
326+
if (string.IsNullOrEmpty(changeSetMappingOptions))
327+
{
328+
_logger.LogDebug("No ChangeSetMappingFile found, skipping TfsChangeSetMappingTool creation");
329+
return null;
330+
}
331+
250332
var properties = new Dictionary<string, object>
251333
{
252334
{ "ChangeSetMappingFile", changeSetMappingOptions }
253335
};
254336
var option = (IOptions)OptionsBinder.BindToOptions("TfsChangeSetMappingToolOptions", properties, classNameChangeLog);
255-
option.Enabled = true;
337+
if (option != null)
338+
{
339+
option.Enabled = true;
340+
}
256341
return option;
257342
}
258343

259344
private IOptions ParseV1TfsGitRepoMappingOptions(IConfiguration configuration)
260345
{
261346
_logger.LogInformation("Upgrading {old} to {new}", "GitRepoMapping", "TfsGitRepoMappingTool");
262347
var data = configuration.GetValue<Dictionary<string, string>>("GitRepoMapping");
348+
349+
// Skip if no GitRepoMapping is configured
350+
if (data == null || data.Count == 0)
351+
{
352+
_logger.LogDebug("No GitRepoMapping found, skipping TfsGitRepoMappingTool creation");
353+
return null;
354+
}
355+
263356
var properties = new Dictionary<string, object>
264357
{
265358
{ "Mappings", data }
266359
};
267360
var option = (IOptions)OptionsBinder.BindToOptions("TfsGitRepositoryToolOptions", properties, classNameChangeLog);
268-
option.Enabled = true;
361+
if (option != null)
362+
{
363+
option.Enabled = true;
364+
}
269365
return option;
270366
}
271367

0 commit comments

Comments
 (0)