Skip to content

Commit 59cb2c7

Browse files
committed
Use user mapping when migrating team capacities
1 parent 388232c commit 59cb2c7

File tree

5 files changed

+78
-43
lines changed

5 files changed

+78
-43
lines changed

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

Lines changed: 54 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Linq;
4+
using Microsoft.Extensions.DependencyInjection;
45
using Microsoft.Extensions.Logging;
56
using Microsoft.TeamFoundation.Client;
67
using Microsoft.TeamFoundation.Core.WebApi.Types;
78
using Microsoft.TeamFoundation.Framework.Client;
89
using Microsoft.TeamFoundation.Work.WebApi;
910
using Microsoft.VisualStudio.Services.WebApi;
11+
using MigrationTools.Tools;
1012

1113
namespace MigrationTools.Processors
1214
{
@@ -21,9 +23,11 @@ internal static void MigrateCapacities(
2123
TeamFoundationTeam targetTeam,
2224
Dictionary<string, string> iterationMap,
2325
Lazy<List<TeamFoundationIdentity>> identityCache,
26+
bool useUserMapping,
2427
ITelemetryLogger telemetry,
2528
ILogger log,
26-
LogLevel exceptionLogLevel)
29+
LogLevel exceptionLogLevel,
30+
IServiceProvider services)
2731
{
2832
log.LogInformation("Migrating team capacities..");
2933

@@ -48,7 +52,7 @@ internal static void MigrateCapacities(
4852
List<TeamMemberCapacityIdentityRef> sourceCapacities = GetSourceCapacities(
4953
sourceHttpClient, sourceTeamContext, sourceIteration);
5054
List<TeamMemberCapacityIdentityRef> targetCapacities = GetTargetCapacities(
51-
sourceCapacities, targetIteration.Path, identityCache, log);
55+
sourceCapacities, targetIteration.Path, useUserMapping, identityCache, log, services);
5256

5357
if (targetCapacities.Count > 0)
5458
{
@@ -83,13 +87,22 @@ private static List<TeamMemberCapacityIdentityRef> GetSourceCapacities(
8387
private static List<TeamMemberCapacityIdentityRef> GetTargetCapacities(
8488
List<TeamMemberCapacityIdentityRef> sourceCapacities,
8589
string targetIteration,
90+
bool useUserMapping,
8691
Lazy<List<TeamFoundationIdentity>> identityCache,
87-
ILogger log)
92+
ILogger log,
93+
IServiceProvider services)
8894
{
89-
var targetCapacities = new List<TeamMemberCapacityIdentityRef>();
95+
List<TeamMemberCapacityIdentityRef> targetCapacities = [];
96+
Dictionary<string, string> userMapping = null;
97+
if (useUserMapping)
98+
{
99+
TfsCommonTools commonTools = services.GetRequiredService<TfsCommonTools>();
100+
userMapping = commonTools.UserMapping.UserMappings.Value;
101+
}
102+
90103
foreach (var sourceCapacity in sourceCapacities)
91104
{
92-
if (TryMatchIdentity(sourceCapacity, identityCache, out TeamFoundationIdentity targetIdentity))
105+
if (TryMatchIdentity(sourceCapacity, identityCache, userMapping, out TeamFoundationIdentity targetIdentity))
93106
{
94107
targetCapacities.Add(new TeamMemberCapacityIdentityRef
95108
{
@@ -127,51 +140,66 @@ private static void ReplaceTargetCapacities(
127140
private static bool TryMatchIdentity(
128141
TeamMemberCapacityIdentityRef sourceCapacity,
129142
Lazy<List<TeamFoundationIdentity>> identityCache,
143+
Dictionary<string, string> userMapping,
130144
out TeamFoundationIdentity targetIdentity)
131145
{
132-
var sourceDisplayName = sourceCapacity.TeamMember.DisplayName;
133-
var index = sourceDisplayName.IndexOf("<");
146+
var sourceName = sourceCapacity.TeamMember.DisplayName;
147+
var index = sourceName.IndexOf("<");
134148
if (index > 0)
135149
{
136-
sourceDisplayName = sourceDisplayName.Substring(0, index).Trim();
150+
sourceName = sourceName.Substring(0, index).Trim();
137151
}
138152

153+
targetIdentity = MatchIdentity(sourceName, identityCache);
154+
if ((targetIdentity is null) && (userMapping is not null))
155+
{
156+
if (userMapping.TryGetValue(sourceName, out var mappedName) && !string.IsNullOrEmpty(mappedName))
157+
{
158+
targetIdentity = MatchIdentity(mappedName, identityCache);
159+
}
160+
}
161+
162+
// last attempt to match on unique name
163+
// Match: "John Michael Bolden" to Bolden, "John Michael" on "[email protected]" unique name
164+
if (targetIdentity is null)
165+
{
166+
var sourceUniqueName = sourceCapacity.TeamMember.UniqueName;
167+
targetIdentity = identityCache.Value.FirstOrDefault(i => i.UniqueName == sourceUniqueName);
168+
}
169+
170+
return targetIdentity is not null;
171+
}
172+
173+
private static TeamFoundationIdentity MatchIdentity(string sourceName, Lazy<List<TeamFoundationIdentity>> identityCache)
174+
{
139175
// Match:
140176
// "Doe, John" to "Doe, John"
141177
// "John Doe" to "John Doe"
142-
targetIdentity = identityCache.Value.FirstOrDefault(i => i.DisplayName == sourceDisplayName);
178+
TeamFoundationIdentity targetIdentity = identityCache.Value.FirstOrDefault(i => i.DisplayName == sourceName);
143179
if (targetIdentity == null)
144180
{
145-
if (sourceDisplayName.Contains(", "))
181+
if (sourceName.Contains(", "))
146182
{
147183
// Match:
148184
// "Doe, John" to "John Doe"
149-
var splitName = sourceDisplayName.Split(',');
150-
sourceDisplayName = $"{splitName[1].Trim()} {splitName[0].Trim()}";
151-
targetIdentity = identityCache.Value.FirstOrDefault(i => i.DisplayName == sourceDisplayName);
185+
var splitName = sourceName.Split(',');
186+
sourceName = $"{splitName[1].Trim()} {splitName[0].Trim()}";
187+
targetIdentity = identityCache.Value.FirstOrDefault(i => i.DisplayName == sourceName);
152188
}
153189
else
154190
{
155-
if (sourceDisplayName.Contains(' '))
191+
if (sourceName.Contains(' '))
156192
{
157193
// Match:
158194
// "John Doe" to "Doe, John"
159-
var splitName = sourceDisplayName.Split(' ');
160-
sourceDisplayName = $"{splitName[1].Trim()}, {splitName[0].Trim()}";
161-
targetIdentity = identityCache.Value.FirstOrDefault(i => i.DisplayName == sourceDisplayName);
195+
var splitName = sourceName.Split(' ');
196+
sourceName = $"{splitName[1].Trim()}, {splitName[0].Trim()}";
197+
targetIdentity = identityCache.Value.FirstOrDefault(i => i.DisplayName == sourceName);
162198
}
163199
}
164-
165-
// last attempt to match on unique name
166-
// Match: "John Michael Bolden" to Bolden, "John Michael" on "[email protected]" unique name
167-
if (targetIdentity == null)
168-
{
169-
var sourceUniqueName = sourceCapacity.TeamMember.UniqueName;
170-
targetIdentity = identityCache.Value.FirstOrDefault(i => i.UniqueName == sourceUniqueName);
171-
}
172200
}
173201

174-
return targetIdentity is not null;
202+
return targetIdentity;
175203
}
176204
}
177205
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -272,8 +272,8 @@ private void MigrateCapacities(
272272
TfsTeamSettingsCore.MigrateCapacities(
273273
sourceHttpClient, Source.TfsProject.Guid, sourceTeam,
274274
targetHttpClient, Target.TfsProject.Guid, targetTeam,
275-
iterationMap, _targetTeamFoundationIdentitiesLazyCache,
276-
Telemetry, Log, exceptionLogLevel: LogLevel.Warning);
275+
iterationMap, _targetTeamFoundationIdentitiesLazyCache, Options.UseUserMapping,
276+
Telemetry, Log, exceptionLogLevel: LogLevel.Warning, Services);
277277
}
278278
}
279279
}

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

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
1-
using System;
2-
using System.Collections.Generic;
1+
using System.Collections.Generic;
32
using MigrationTools.Processors.Infrastructure;
43

54
namespace MigrationTools.Processors
65
{
76
public class TfsTeamSettingsProcessorOptions : ProcessorOptions
87
{
9-
108
/// <summary>
119
/// Migrate original team settings after their creation on target team project
1210
/// </summary>
@@ -36,5 +34,11 @@ public class TfsTeamSettingsProcessorOptions : ProcessorOptions
3634
/// </summary>
3735
public List<string> Teams { get; set; }
3836

37+
/// <summary>
38+
/// Use user mapping file from TfsTeamSettingsTool when matching users when migrating capacities.
39+
/// By default, users in source are matched in target users by current display name. When this is set to `true`,
40+
/// users are matched also by mapped name from user mapping file.
41+
/// </summary>
42+
public bool UseUserMapping { get; set; }
3943
}
40-
}
44+
}

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using System;
1+
using System;
22
using System.Collections.Generic;
33
using System.Diagnostics;
44
using System.Linq;
@@ -231,8 +231,8 @@ private void MigrateCapacities(
231231
TfsTeamSettingsCore.MigrateCapacities(
232232
_processor.Source.GetClient<WorkHttpClient>(), _processor.Source.WorkItems.Project.Guid, sourceTeam,
233233
_processor.Target.GetClient<WorkHttpClient>(), _processor.Target.WorkItems.Project.Guid, targetTeam,
234-
iterationMap, _targetTeamFoundationIdentitiesLazyCache,
235-
Telemetry, Log, exceptionLogLevel: LogLevel.Error);
234+
iterationMap, _targetTeamFoundationIdentitiesLazyCache, Options.UseUserMapping,
235+
Telemetry, Log, exceptionLogLevel: LogLevel.Error, Services);
236236
}
237237
}
238238
}

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

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,10 @@
1-
using System;
2-
using System.Collections.Generic;
3-
using Microsoft.TeamFoundation.Build.Client;
4-
using MigrationTools.Enrichers;
1+
using System.Collections.Generic;
52
using MigrationTools.Tools.Infrastructure;
63

74
namespace MigrationTools.Tools
85
{
96
public class TfsTeamSettingsToolOptions : ToolOptions, ITfsTeamSettingsToolOptions
107
{
11-
128
/// <summary>
139
/// Migrate original team settings after their creation on target team project
1410
/// </summary>
@@ -27,22 +23,29 @@ public class TfsTeamSettingsToolOptions : ToolOptions, ITfsTeamSettingsToolOptio
2723
/// <default>false</default>
2824
public bool MigrateTeamCapacities { get; set; }
2925

26+
/// <summary>
27+
/// Use user mapping file from TfsTeamSettingsTool when matching users when migrating capacities.
28+
/// By default, users in source are matched in target users by current display name. When this is set to `true`,
29+
/// users are matched also by mapped name from user mapping file.
30+
/// </summary>
31+
public bool UseUserMapping { get; set; }
32+
3033
/// <summary>
3134
/// List of Teams to process. If this is `null` then all teams will be processed.
3235
/// </summary>
3336
public List<string> Teams { get; set; }
34-
3537
}
3638

3739
public interface ITfsTeamSettingsToolOptions
3840
{
39-
4041
public bool MigrateTeamSettings { get; set; }
4142

4243
public bool UpdateTeamSettings { get; set; }
4344

4445
public bool MigrateTeamCapacities { get; set; }
4546

4647
public List<string> Teams { get; set; }
48+
49+
public bool UseUserMapping { get; set; }
4750
}
48-
}
51+
}

0 commit comments

Comments
 (0)