Skip to content

Commit

Permalink
Also created and updated to TfsWorkItemLinkEnricherOptions
Browse files Browse the repository at this point in the history
  • Loading branch information
MrHinsh committed Jan 25, 2024
1 parent 73d581d commit 14dd7a0
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 29 deletions.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,27 @@
using Microsoft.TeamFoundation.WorkItemTracking.Client;
using MigrationTools._EngineV1.Clients;
using MigrationTools.DataContracts;
using MigrationTools.Enrichers;
using MigrationTools.Exceptions;
using MigrationTools.Processors;

namespace MigrationTools.Enrichers
{
public class TfsWorkItemLinkEnricher : WorkItemProcessorEnricher
{
private bool _save = true;
private bool _filterWorkItemsThatAlreadyExistInTarget = true;
private IMigrationEngine Engine;

public TfsWorkItemLinkEnricherOptions Options { get; private set; }

public TfsWorkItemLinkEnricher(IServiceProvider services, ILogger<TfsWorkItemLinkEnricher> logger)
: base(services, logger)
{
Engine = services.GetRequiredService<IMigrationEngine>();
}

[Obsolete]
public override void Configure(
bool save = true,
bool filterWorkItemsThatAlreadyExistInTarget = true)
public override void Configure(IProcessorEnricherOptions options)
{
_save = save;
_filterWorkItemsThatAlreadyExistInTarget = filterWorkItemsThatAlreadyExistInTarget;
Options = (TfsWorkItemLinkEnricherOptions)options;
}

[Obsolete]
Expand Down Expand Up @@ -136,7 +133,7 @@ public void MigrateSharedSteps(WorkItemData wiSourceL, WorkItemData wiTargetL)
}
}

if (wiTargetL.ToWorkItem().IsDirty && _save)
if (wiTargetL.ToWorkItem().IsDirty && Options.SaveAfterEachLinkIsAdded)
{
wiTargetL.SaveToAzureDevOps();
}
Expand Down Expand Up @@ -168,7 +165,7 @@ public void MigrateSharedParameters(WorkItemData wiSourceL, WorkItemData wiTarge
}
}

if (wiTargetL.ToWorkItem().IsDirty && _save)
if (wiTargetL.ToWorkItem().IsDirty && Options.SaveAfterEachLinkIsAdded)
{
wiTargetL.SaveToAzureDevOps();
}
Expand All @@ -177,7 +174,7 @@ public void MigrateSharedParameters(WorkItemData wiSourceL, WorkItemData wiTarge
private void CreateExternalLink(ExternalLink sourceLink, WorkItemData target)
{
var exist = (from Link l in target.ToWorkItem().Links
where l is ExternalLink && ((ExternalLink)l).LinkedArtifactUri == ((ExternalLink)sourceLink).LinkedArtifactUri
where l is ExternalLink && ((ExternalLink)l).LinkedArtifactUri == sourceLink.LinkedArtifactUri
select (ExternalLink)l).SingleOrDefault();
if (exist == null)
{
Expand All @@ -191,7 +188,7 @@ private void CreateExternalLink(ExternalLink sourceLink, WorkItemData target)
// DevOps doesn't seem to take impersonation very nicely here - as of 13.05.22 the following line would get you an error:
// System.FormatException: The string 'Microsoft.TeamFoundation.WorkItemTracking.Common.ServerDefaultFieldValue' is not a valid AllXsd value.
// target.ToWorkItem().Fields["System.ModifiedBy"].Value = "Migration";
if (_save)
if (Options.SaveAfterEachLinkIsAdded)
{
try
{
Expand All @@ -200,7 +197,7 @@ private void CreateExternalLink(ExternalLink sourceLink, WorkItemData target)
catch (Exception ex)
{
// Ignore this link because the TFS server didn't recognize its type (There's no point in crashing the rest of the migration due to a link)
if(ex.Message.Contains("Unrecognized Resource link"))
if (ex.Message.Contains("Unrecognized Resource link"))
{
Log.LogError(ex, "[{ExceptionType}] Failed to save link {SourceLinkType} on {TargetId}", ex.GetType().Name, sourceLink.GetType().Name, target.Id);
// Remove the link from the target so it doesn't cause problems downstream
Expand Down Expand Up @@ -238,7 +235,7 @@ private void CreateRelatedLink(WorkItemData wiSourceL, RelatedLink item, WorkIte
WorkItemData wiSourceR = null;
WorkItemData wiTargetR = null;

Log.LogDebug("RelatedLink is of ArtifactLinkType='{ArtifactLinkType}':LinkTypeEnd='{LinkTypeEndImmutableName}' on WorkItemId s:{ids} t:{idt}", rl.ArtifactLinkType.Name, rl.LinkTypeEnd == null? "null" : rl.LinkTypeEnd.ImmutableName, wiSourceL.Id, wiTargetL.Id);
Log.LogDebug("RelatedLink is of ArtifactLinkType='{ArtifactLinkType}':LinkTypeEnd='{LinkTypeEndImmutableName}' on WorkItemId s:{ids} t:{idt}", rl.ArtifactLinkType.Name, rl.LinkTypeEnd == null ? "null" : rl.LinkTypeEnd.ImmutableName, wiSourceL.Id, wiTargetL.Id);

if (rl.LinkTypeEnd != null) // On a registered link type these will for sure fail as target is not in the system.
{
Expand All @@ -261,7 +258,7 @@ private void CreateRelatedLink(WorkItemData wiSourceL, RelatedLink item, WorkIte
return;
}
}

if (wiTargetR != null)
{
bool IsExisting = false;
Expand All @@ -273,11 +270,11 @@ where l is RelatedLink
&& ((RelatedLink)l).RelatedWorkItemId.ToString() == wiTargetR.Id
&& ((RelatedLink)l).LinkTypeEnd.ImmutableName == item.LinkTypeEnd.ImmutableName
select (RelatedLink)l).SingleOrDefault();
IsExisting = (exist != null);
IsExisting = exist != null;
}
catch (Exception ex)
{
Log.LogError(ex, " [SKIP] Unable to migrate links where wiSourceL={0}, wiSourceR={1}, wiTargetL={2}", ((wiSourceL != null) ? wiSourceL.Id.ToString() : "NotFound"), ((wiSourceR != null) ? wiSourceR.Id.ToString() : "NotFound"), ((wiTargetL != null) ? wiTargetL.Id.ToString() : "NotFound"));
Log.LogError(ex, " [SKIP] Unable to migrate links where wiSourceL={0}, wiSourceR={1}, wiTargetL={2}", wiSourceL != null ? wiSourceL.Id.ToString() : "NotFound", wiSourceR != null ? wiSourceR.Id.ToString() : "NotFound", wiTargetL != null ? wiTargetL.Id.ToString() : "NotFound");
return;
}

Expand Down Expand Up @@ -334,7 +331,7 @@ where l is RelatedLink
// DevOps doesn't seem to take impersonation very nicely here - as of 13.05.22 the following line would get you an error:
// System.FormatException: The string 'Microsoft.TeamFoundation.WorkItemTracking.Common.ServerDefaultFieldValue' is not a valid AllXsd value.
// wiTargetL.ToWorkItem().Fields["System.ModifiedBy"].Value = "Migration";
if (_save)
if (Options.SaveAfterEachLinkIsAdded)
{
wiTargetL.SaveToAzureDevOps();
}
Expand Down Expand Up @@ -364,7 +361,7 @@ where l is RelatedLink
}
else
{
Log.LogWarning("[SKIP] [LINK_CAPTURE_RELATED] [{RegisteredLinkType}] target not found. wiSourceL={wiSourceL}, wiSourceR={wiSourceR}, wiTargetL={wiTargetL}", rl.ArtifactLinkType.GetType().Name, wiSourceL == null ? "null" : wiSourceL.Id , wiSourceR == null ? "null" : wiSourceR.Id, wiTargetL == null? "null": wiTargetL.Id);
Log.LogWarning("[SKIP] [LINK_CAPTURE_RELATED] [{RegisteredLinkType}] target not found. wiSourceL={wiSourceL}, wiSourceR={wiSourceR}, wiTargetL={wiTargetL}", rl.ArtifactLinkType.GetType().Name, wiSourceL == null ? "null" : wiSourceL.Id, wiSourceR == null ? "null" : wiSourceR.Id, wiTargetL == null ? "null" : wiTargetL.Id);
}
}

Expand Down Expand Up @@ -428,7 +425,7 @@ where string.Equals(sourceLinkAbsoluteUri, absoluteUri, StringComparison.Ordinal
// DevOps doesn't seem to take impersonation very nicely here - as of 13.05.22 the following line would get you an error:
// System.FormatException: The string 'Microsoft.TeamFoundation.WorkItemTracking.Common.ServerDefaultFieldValue' is not a valid AllXsd value.
// target.ToWorkItem().Fields["System.ModifiedBy"].Value = "Migration";
if (_save)
if (Options.SaveAfterEachLinkIsAdded)
{
target.SaveToAzureDevOps();
}
Expand All @@ -449,7 +446,7 @@ private string GetAbsoluteUri(Hyperlink hyperlink)

private bool ShouldCopyLinks(WorkItemData sourceWorkItemLinkStart, WorkItemData targetWorkItemLinkStart)
{
if (_filterWorkItemsThatAlreadyExistInTarget)
if (Options.FilterIfLinkCountMatches)
{
if (targetWorkItemLinkStart.ToWorkItem().Links.Count == sourceWorkItemLinkStart.ToWorkItem().Links.Count) // we should never have this as the target should not have existed in this path
{
Expand All @@ -465,12 +462,6 @@ private bool IsHyperlink(Link item)
return item is Hyperlink;
}

[Obsolete("v2 Archtecture: use Configure(bool save = true, bool filter = true) instead", true)]
public override void Configure(IProcessorEnricherOptions options)
{
throw new NotImplementedException();
}

protected override void RefreshForProcessorType(IProcessor processor)
{
throw new NotImplementedException();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using System;
using System.Collections.Generic;
using Microsoft.TeamFoundation.Build.Client;

namespace MigrationTools.Enrichers
{
public class TfsWorkItemLinkEnricherOptions : ProcessorEnricherOptions, ITfsWorkItemLinkEnricherOptions
{
public override Type ToConfigure => typeof(TfsNodeStructure);

/// <summary>
/// Skip validating links if the number of links in the source and the target matches!
/// </summary>
public bool FilterIfLinkCountMatches { get; set; }


/// <summary>
/// Save the work item after each link is added. This will slow the migration as it will cause many saves to the TFS database.
/// </summary>
public bool SaveAfterEachLinkIsAdded { get; set; }


public override void SetDefaults()
{
Enabled = true;
FilterIfLinkCountMatches = true;
SaveAfterEachLinkIsAdded = false;
}

static public TfsNodeStructureOptions GetDefaults()
{
var result = new TfsNodeStructureOptions();
result.SetDefaults();
return result;
}
}

public interface ITfsWorkItemLinkEnricherOptions
{
public bool FilterIfLinkCountMatches { get; set; }
public bool SaveAfterEachLinkIsAdded { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,6 @@ public override void Configure(IProcessorConfig config)
_config = (WorkItemMigrationConfig)config;

ImportCommonEnricherConfigs();

_workItemLinkEnricher.Configure(_config.LinkMigrationSaveEachAsAdded, _config.FilterWorkItemsThatAlreadyExistInTarget);
}

private void ImportCommonEnricherConfigs()
Expand Down Expand Up @@ -136,6 +134,17 @@ private void ImportCommonEnricherConfigs()
{
_revisionManager.Configure(revmanConfig);
}
// TfsNodeStructureOptions
var wileConfig = _engineConfig.CommonEnrichersConfig.OfType<TfsWorkItemLinkEnricherOptions>().FirstOrDefault();
if (revmanConfig == null)
{
_workItemLinkEnricher.Configure(TfsWorkItemLinkEnricherOptions.GetDefaults());
Log.LogWarning("Default `TfsWorkItemLinkEnricherOptions` used... add a `TfsWorkItemLinkEnricherOptions` entry to `CommonEnrichersConfig` to customise the settings.");
}
else
{
_workItemLinkEnricher.Configure(wileConfig);
}
}

internal void TraceWriteLine(LogEventLevel level, string message, Dictionary<string, object> properties = null)
Expand Down

0 comments on commit 14dd7a0

Please sign in to comment.