diff --git a/Common/Migration/Phase2/Processors/GitCommitLinksProcessor.cs b/Common/Migration/Phase2/Processors/GitCommitLinksProcessor.cs index 2960e88..f0f2d61 100644 --- a/Common/Migration/Phase2/Processors/GitCommitLinksProcessor.cs +++ b/Common/Migration/Phase2/Processors/GitCommitLinksProcessor.cs @@ -8,6 +8,10 @@ using Microsoft.VisualStudio.Services.WebApi.Patch.Json; using Logging; using Common.Config; +using Microsoft.TeamFoundation.SourceControl.WebApi; +using System.Web; +using Microsoft.VisualStudio.Services.WebApi; +using Microsoft.VisualStudio.Services.WebApi.Patch; namespace Common.Migration { @@ -21,10 +25,22 @@ public bool IsEnabled(ConfigJson config) { return config.MoveGitLinks; } + private Dictionary sourceProjectReposDictionary = new Dictionary(); + private Dictionary targetProjectReposDictionary = new Dictionary(); public async Task Preprocess(IMigrationContext migrationContext, IBatchMigrationContext batchContext, IList sourceWorkItems, IList targetWorkItems) { - + //Build source repos dictionary + var sourceGitClient = migrationContext.SourceClient.Connection.GetClient(); + var sourceProjectRepositories = sourceGitClient.GetRepositoriesAsync(migrationContext.Config.SourceConnection.Project).Result; + sourceProjectReposDictionary = sourceProjectRepositories.ToDictionary( + r => r.Id.ToString(), r => r.Name); + + //Build target repos dictionary + var targetGitClient = migrationContext.TargetClient.Connection.GetClient(); + var targetProjectRepositories = targetGitClient.GetRepositoriesAsync(migrationContext.Config.TargetConnection.Project).Result; + targetProjectReposDictionary = targetProjectRepositories.ToDictionary( + r => r.Name, r => r); } public async Task> Process(IMigrationContext migrationContext, IBatchMigrationContext batchContext, WorkItem sourceWorkItem, WorkItem targetWorkItem) @@ -36,71 +52,56 @@ public async Task> Process(IMigrationContext mig { foreach (WorkItemRelation sourceGitCommitLinkRelation in sourceGitCommitLinksRelations) { - string adjustedUrl = ConvertGitCommitLinkToHyperLink(sourceWorkItem.Id.Value, sourceGitCommitLinkRelation.Url, migrationContext.Config.SourceConnection.Account); - WorkItemRelation targetGitCommitHyperlinkRelation = GetGitCommitHyperlinkIfExistsOnTarget(targetWorkItem, adjustedUrl); - - if (targetGitCommitHyperlinkRelation != null) // is on target + if (!sourceGitCommitLinkRelation.Url.Contains("Git/Commit")) { - JsonPatchOperation gitCommitHyperlinkAddOperation = MigrationHelpers.GetRelationAddOperation(targetGitCommitHyperlinkRelation); - jsonPatchOperations.Add(gitCommitHyperlinkAddOperation); + continue; } - else // is not on target - { - string comment = string.Empty; - if (sourceGitCommitLinkRelation.Attributes.ContainsKey(Constants.RelationAttributeComment)) - { - comment = $"{sourceGitCommitLinkRelation.Attributes[Constants.RelationAttributeComment]}"; - } - string adjustedComment = $"{Constants.RelationAttributeGitCommitCommentValue}{comment}"; - WorkItemRelation newGitCommitLinkRelation = new WorkItemRelation(); - newGitCommitLinkRelation.Rel = Constants.Hyperlink; - newGitCommitLinkRelation.Url = adjustedUrl; - newGitCommitLinkRelation.Attributes = new Dictionary(); - newGitCommitLinkRelation.Attributes[Constants.RelationAttributeComment] = adjustedComment; - - JsonPatchOperation gitCommitHyperlinkAddOperation = MigrationHelpers.GetRelationAddOperation(newGitCommitLinkRelation); - jsonPatchOperations.Add(gitCommitHyperlinkAddOperation); - } + //ArtifactLink format: + //vstfs:///Git/Commit/{sourceProjGuid}%2f{sourceRepoGuid}%2f47cb466c1d1f8dd8e1b43b40ed8c3a3fec67e20d + var gitGuidsOnly = HttpUtility.UrlDecode(sourceGitCommitLinkRelation.Url) + .Replace("vstfs:///Git/Commit/", string.Empty); + + //Take the source GUIDs of Project and Repo + var sourceGitLinkProjectGuid = gitGuidsOnly.Split('/')[0]; + var sourceGitLinkRepoGuid = gitGuidsOnly.Split('/')[1]; + + //Convert to Repo name from GUID + var sourceGitLinkRepoName = sourceProjectReposDictionary[sourceGitLinkRepoGuid]; + + //Find the target GUIDs of Project and Repo + var targetGitLinkRepoGuid = targetProjectReposDictionary[sourceGitLinkRepoName].Id.ToString(); + var targetGitLinkProjectGuid = targetProjectReposDictionary[sourceGitLinkRepoName].ProjectReference.Id.ToString(); + + + var newLinks = new Dictionary + { + { + "relations/-", + new + { + rel = "ArtifactLink", + url = sourceGitCommitLinkRelation.Url + .Replace(sourceGitLinkProjectGuid, targetGitLinkProjectGuid) + .Replace(sourceGitLinkRepoGuid, targetGitLinkRepoGuid), + attributes = new + { + name = "Fixed in Commit", + comment = "Added by the migration" + } + } + } + }; + + var gitCommitHyperlinkAddOperation = VssJsonPatchDocumentFactory.ConstructJsonPatchDocument(Operation.Add, newLinks); + jsonPatchOperations.Add(gitCommitHyperlinkAddOperation[0]); } } return jsonPatchOperations; } - private WorkItemRelation GetGitCommitHyperlinkIfExistsOnTarget(WorkItem targetWorkItem, string href) - { - if (targetWorkItem.Relations == null) - { - return null; - } - - foreach (WorkItemRelation targetRelation in targetWorkItem.Relations) - { - if (targetRelation.Rel.Equals(Constants.Hyperlink) && targetRelation.Url.Equals(href, StringComparison.OrdinalIgnoreCase)) - { - return targetRelation; - } - } - - return null; - } - - private object GetIdFromAttributes(WorkItemRelation relation) - { - if (relation.Attributes != null && relation.Attributes.ContainsKeyIgnoringCase(Constants.RelationAttributeId)) - { - // get the key even if its letter case is different but it matches otherwise - string idKeyFromFields = relation.Attributes.GetKeyIgnoringCase(Constants.RelationAttributeId); - return relation.Attributes[idKeyFromFields]; - } - else - { - return null; - } - } - private static IEnumerable GetGitLinksRelationsFromWorkItem(WorkItem workItem, string linkType, string account) { IList result = new List(); @@ -122,6 +123,7 @@ private static IEnumerable GetGitLinksRelationsFromWorkItem(Wo return result; } + /// /// Convert the vstfs git commit link to a hyperlink which works ///