Skip to content

Commit bb6f4ec

Browse files
authored
Validation of reflected work item ID is mandatory (#2963)
This PR solves the first item in [improve work item type validation list](#2951). Reflected work item ID is mandatory for proper work item migration. So validation of this field is performed always, even if the validator tool itself is disabled. This field was validated always before my addition to validate full work item types, so I just returned back the old behavior.
2 parents 131fde8 + 9e99ced commit bb6f4ec

File tree

3 files changed

+91
-40
lines changed

3 files changed

+91
-40
lines changed

src/MigrationTools.Clients.TfsObjectModel/Processors/TfsWorkItemMigrationProcessor.cs

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -254,24 +254,25 @@ private void ValidateWorkItemTypes()
254254
Log.LogInformation("Starting Pre-Validation: Validating work item types and fields with `WorkItemTypeValidatorTool`.");
255255
Log.LogInformation("Refer to https://devopsmigration.io/TfsWorkItemTypeValidatorTool/ for configuration.");
256256
Log.LogInformation("------------------------------------");
257+
258+
var sourceWits = Source.WorkItems.Project
259+
.ToProject()
260+
.WorkItemTypes
261+
.Cast<WorkItemType>()
262+
.ToList();
263+
var targetWits = Target.WorkItems.Project
264+
.ToProject()
265+
.WorkItemTypes
266+
.Cast<WorkItemType>()
267+
.ToList();
268+
269+
// Reflected work item ID field is mandatory for migration, so it is validated even if the validator tool is disabled.
270+
bool containsReflectedWorkItemId = CommonTools.WorkItemTypeValidatorTool.ValidateReflectedWorkItemIdField(
271+
sourceWits, targetWits, Target.Options.ReflectedWorkItemIdField);
272+
bool validationResult = true;
257273
if (CommonTools.WorkItemTypeValidatorTool.Enabled)
258274
{
259-
var sourceWits = Source.WorkItems.Project
260-
.ToProject()
261-
.WorkItemTypes
262-
.Cast<WorkItemType>()
263-
.ToList();
264-
var targetWits = Target.WorkItems.Project
265-
.ToProject()
266-
.WorkItemTypes
267-
.Cast<WorkItemType>()
268-
.ToList();
269-
if (!CommonTools.WorkItemTypeValidatorTool.ValidateWorkItemTypes(
270-
sourceWits, targetWits, Target.Options.ReflectedWorkItemIdField))
271-
{
272-
Log.LogError("Validation of work item types failed.");
273-
Environment.Exit(-1);
274-
}
275+
validationResult = CommonTools.WorkItemTypeValidatorTool.ValidateWorkItemTypes(sourceWits, targetWits);
275276
}
276277
else
277278
{
@@ -281,6 +282,11 @@ private void ValidateWorkItemTypes()
281282
+ " or migration may not work at all.";
282283
Log.LogWarning(msg);
283284
}
285+
if (!containsReflectedWorkItemId || !validationResult)
286+
{
287+
Log.LogError("Validation of work item types failed.");
288+
Environment.Exit(-1);
289+
}
284290
Log.LogInformation("------------------------------------");
285291
Log.LogInformation("[/ValidateWorkItemTypes]");
286292
}

src/MigrationTools.Clients.TfsObjectModel/Processors/TfsWorkItemTypeValidatorProcessor.cs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,16 @@ protected override void InternalExecute()
4747
.WorkItemTypes
4848
.Cast<WorkItemType>()
4949
.ToList();
50+
bool containsReflectedWorkItemId = CommonTools.WorkItemTypeValidatorTool
51+
.ValidateReflectedWorkItemIdField(sourceWits, targetWits, Target.Options.ReflectedWorkItemIdField);
5052
bool validationResult = CommonTools.WorkItemTypeValidatorTool
51-
.ValidateWorkItemTypes(sourceWits, targetWits, Target.Options.ReflectedWorkItemIdField);
52-
if (Options.StopIfValidationFails && !validationResult)
53+
.ValidateWorkItemTypes(sourceWits, targetWits);
54+
if ((Options.StopIfValidationFails && !validationResult) || (!containsReflectedWorkItemId))
5355
{
54-
Log.LogInformation($"'{nameof(Options.StopIfValidationFails)}' is set to 'true', so migration process will stop now.");
56+
const string message =
57+
"Either the reflected work item type ID field '{ReflectedWorkItemIdField}' is missing in some of the target work item types"
58+
+ $" or '{nameof(Options.StopIfValidationFails)}' is set to 'true', so migration process will stop now.";
59+
Log.LogInformation(message, Target.Options.ReflectedWorkItemIdField);
5560
Environment.Exit(-1);
5661
}
5762
}

src/MigrationTools.Clients.TfsObjectModel/Tools/TfsWorkItemTypeValidatorTool.cs

Lines changed: 61 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,55 @@ public TfsWorkItemTypeValidatorTool(
3030
Options.Normalize();
3131
}
3232

33-
public bool ValidateWorkItemTypes(
33+
public bool ValidateReflectedWorkItemIdField(
3434
List<WorkItemType> sourceWits,
3535
List<WorkItemType> targetWits,
3636
string reflectedWorkItemIdField)
37+
{
38+
Log.LogInformation("Validating presence of reflected work item ID field '{reflectedWorkItemIdField}'"
39+
+ " in target work item types.", reflectedWorkItemIdField);
40+
bool isValid = true;
41+
List<WorkItemType> wits = GetTargetWitsToValidate(sourceWits, targetWits);
42+
foreach (WorkItemType targetWit in wits)
43+
{
44+
if (targetWit.FieldDefinitions.Contains(reflectedWorkItemIdField))
45+
{
46+
Log.LogDebug(" '{targetWit}' contains reflected work item ID field '{fieldName}'.",
47+
targetWit.Name, reflectedWorkItemIdField);
48+
}
49+
else
50+
{
51+
Log.LogError(" '{targetWit}' does not contain reflected work item ID field '{fieldName}'.",
52+
targetWit.Name, reflectedWorkItemIdField);
53+
isValid = false;
54+
}
55+
}
56+
LogReflectedWorkItemIdValidationResult(isValid, reflectedWorkItemIdField);
57+
return isValid;
58+
}
59+
60+
private List<WorkItemType> GetTargetWitsToValidate(List<WorkItemType> sourceWits, List<WorkItemType> targetWits)
61+
{
62+
List<WorkItemType> targetWitsToValidate = [];
63+
foreach (WorkItemType sourceWit in sourceWits)
64+
{
65+
string sourceWitName = sourceWit.Name;
66+
if (!ShouldValidateWorkItemType(sourceWitName))
67+
{
68+
continue;
69+
}
70+
string targetWitName = GetTargetWorkItemType(sourceWitName);
71+
WorkItemType targetWit = targetWits
72+
.FirstOrDefault(wit => wit.Name.Equals(targetWitName, StringComparison.OrdinalIgnoreCase));
73+
if (targetWit is not null)
74+
{
75+
targetWitsToValidate.Add(targetWit);
76+
}
77+
}
78+
return targetWitsToValidate;
79+
}
80+
81+
public bool ValidateWorkItemTypes(List<WorkItemType> sourceWits, List<WorkItemType> targetWits)
3782
{
3883
LogWorkItemTypes(sourceWits, targetWits);
3984

@@ -61,10 +106,6 @@ public bool ValidateWorkItemTypes(
61106
}
62107
else
63108
{
64-
if (!ValidateReflectedWorkItemIdField(targetWit, reflectedWorkItemIdField))
65-
{
66-
isValid = false;
67-
}
68109
if (!ValidateWorkItemTypeFields(sourceWit, targetWit))
69110
{
70111
isValid = false;
@@ -75,22 +116,6 @@ public bool ValidateWorkItemTypes(
75116
return isValid;
76117
}
77118

78-
private bool ValidateReflectedWorkItemIdField(WorkItemType targetWit, string reflectedWorkItemIdField)
79-
{
80-
if (targetWit.FieldDefinitions.Contains(reflectedWorkItemIdField))
81-
{
82-
Log.LogDebug(" '{targetWit}' contains reflected work item ID field '{fieldName}'.",
83-
targetWit.Name, reflectedWorkItemIdField);
84-
}
85-
else
86-
{
87-
Log.LogWarning(" '{targetWit}' does not contain reflected work item ID field '{fieldName}'.",
88-
targetWit.Name, reflectedWorkItemIdField);
89-
return false;
90-
}
91-
return true;
92-
}
93-
94119
private bool ValidateWorkItemTypeFields(WorkItemType sourceWit, WorkItemType targetWit)
95120
{
96121
bool result = true;
@@ -266,6 +291,21 @@ private void LogWorkItemTypes(ICollection<WorkItemType> sourceWits, ICollection<
266291
string.Join(", ", targetWits.Select(wit => wit.Name)));
267292
}
268293

294+
private void LogReflectedWorkItemIdValidationResult(bool isValid, string reflectedWorkItemIdField)
295+
{
296+
if (isValid)
297+
{
298+
Log.LogInformation("All work item types have reflected work item ID field '{reflectedWorkItemIdField}'.",
299+
reflectedWorkItemIdField);
300+
return;
301+
}
302+
303+
const string message = "Reflected work item ID field is mandatory for work item migration."
304+
+ " You can configure name of this field in target TFS endpoint settings as 'ReflectedWorkItemIdField' property."
305+
+ " Your current configured name of the field is '{reflectedWorkItemIdField}'.";
306+
Log.LogError(message, reflectedWorkItemIdField);
307+
}
308+
269309
private void LogValidationResult(bool isValid)
270310
{
271311
if (isValid)

0 commit comments

Comments
 (0)