diff --git a/src/WorkItemMigrator/WorkItemImport/WitClient/IWitClientWrapper.cs b/src/WorkItemMigrator/WorkItemImport/WitClient/IWitClientWrapper.cs index 486e270e..77c45bbf 100644 --- a/src/WorkItemMigrator/WorkItemImport/WitClient/IWitClientWrapper.cs +++ b/src/WorkItemMigrator/WorkItemImport/WitClient/IWitClientWrapper.cs @@ -1,4 +1,5 @@ using Microsoft.TeamFoundation.Core.WebApi; +using Microsoft.TeamFoundation.SourceControl.WebApi; using Microsoft.TeamFoundation.WorkItemTracking.WebApi.Models; using Microsoft.VisualStudio.Services.WebApi.Patch.Json; using Migration.WIContract; @@ -13,6 +14,7 @@ public interface IWitClientWrapper WorkItem GetWorkItem(int wiId); WorkItem UpdateWorkItem(JsonPatchDocument patchDocument, int workItemId); TeamProject GetProject(string projectId); + GitRepository GetRepository(string project, string repository); List GetRelationTypes(); AttachmentReference CreateAttachment(WiAttachment attachment); } diff --git a/src/WorkItemMigrator/WorkItemImport/WitClient/JsonPatchDocUtils.cs b/src/WorkItemMigrator/WorkItemImport/WitClient/JsonPatchDocUtils.cs index 93c633fb..04e845b4 100644 --- a/src/WorkItemMigrator/WorkItemImport/WitClient/JsonPatchDocUtils.cs +++ b/src/WorkItemMigrator/WorkItemImport/WitClient/JsonPatchDocUtils.cs @@ -33,21 +33,21 @@ public static JsonPatchOperation CreateJsonFieldPatchOp(Operation op, string key }; } - public static JsonPatchOperation CreateJsonArtifactLinkPatchOp(Operation op, string project, string repository, string commitId) + public static JsonPatchOperation CreateJsonArtifactLinkPatchOp(Operation op, string projectId, string repositoryId, string commitId) { if (string.IsNullOrEmpty(commitId)) { throw new ArgumentException(nameof(commitId)); } - if (string.IsNullOrEmpty(project)) + if (string.IsNullOrEmpty(projectId)) { - throw new ArgumentException(nameof(project)); + throw new ArgumentException(nameof(projectId)); } - if (string.IsNullOrEmpty(repository)) + if (string.IsNullOrEmpty(repositoryId)) { - throw new ArgumentException(nameof(repository)); + throw new ArgumentException(nameof(repositoryId)); } return new JsonPatchOperation() @@ -57,7 +57,7 @@ public static JsonPatchOperation CreateJsonArtifactLinkPatchOp(Operation op, str Value = new PatchOperationValue { Rel = "ArtifactLink", - Url = $"vstfs:///Git/Commit/{project}/{repository}/{commitId}", + Url = $"vstfs:///Git/Commit/{projectId}%2F{repositoryId}%2F{commitId}", Attributes = new Attributes { Name = "Fixed in Commit" diff --git a/src/WorkItemMigrator/WorkItemImport/WitClient/WitClientUtils.cs b/src/WorkItemMigrator/WorkItemImport/WitClient/WitClientUtils.cs index a107dc3f..59b2ac96 100644 --- a/src/WorkItemMigrator/WorkItemImport/WitClient/WitClientUtils.cs +++ b/src/WorkItemMigrator/WorkItemImport/WitClient/WitClientUtils.cs @@ -682,9 +682,12 @@ public void SaveWorkItemArtifacts(WiRevision rev, WorkItem wi, Settings settings return; } + Guid projectId = _witClientWrapper.GetProject(settings.Project).Id; + Guid repositoryId = _witClientWrapper.GetRepository(settings.Project, rev.Commit.Repository).Id; + var patchDocument = new JsonPatchDocument { - JsonPatchDocUtils.CreateJsonArtifactLinkPatchOp(Operation.Add, settings.Project, rev.Commit.Repository, rev.Commit.Id), + JsonPatchDocUtils.CreateJsonArtifactLinkPatchOp(Operation.Add, projectId.ToString(), repositoryId.ToString(), rev.Commit.Id), JsonPatchDocUtils.CreateJsonFieldPatchOp(Operation.Add, WiFieldReference.ChangedDate, rev.Time), JsonPatchDocUtils.CreateJsonFieldPatchOp(Operation.Add, WiFieldReference.ChangedBy, rev.Author) }; diff --git a/src/WorkItemMigrator/WorkItemImport/WitClient/WitClientWrapper.cs b/src/WorkItemMigrator/WorkItemImport/WitClient/WitClientWrapper.cs index e09c2a20..9dbc960a 100644 --- a/src/WorkItemMigrator/WorkItemImport/WitClient/WitClientWrapper.cs +++ b/src/WorkItemMigrator/WorkItemImport/WitClient/WitClientWrapper.cs @@ -1,4 +1,5 @@ using Microsoft.TeamFoundation.Core.WebApi; +using Microsoft.TeamFoundation.SourceControl.WebApi; using Microsoft.TeamFoundation.WorkItemTracking.WebApi; using Microsoft.TeamFoundation.WorkItemTracking.WebApi.Models; using Microsoft.VisualStudio.Services.Common; @@ -8,6 +9,7 @@ using Migration.Common.Log; using Migration.WIContract; using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Threading; @@ -17,10 +19,15 @@ namespace WorkItemImport { public class WitClientWrapper : IWitClientWrapper { + // Cache fields + private ConcurrentDictionary _projectCache = new ConcurrentDictionary(); + private ConcurrentDictionary _repositoryCache = new ConcurrentDictionary(); + private WorkItemTrackingHttpClient WitClient { get; } private ProjectHttpClient ProjectClient { get; } private VssConnection Connection { get; } private TeamProjectReference TeamProject { get; } + private GitHttpClient GitClient { get; } public WitClientWrapper(string collectionUri, string project, string personalAccessToken) { @@ -29,6 +36,7 @@ public WitClientWrapper(string collectionUri, string project, string personalAcc WitClient = Connection.GetClient(); ProjectClient = Connection.GetClient(); TeamProject = ProjectClient.GetProject(project).Result; + GitClient = Connection.GetClient(); } public WorkItem CreateWorkItem(string wiType, DateTime? createdDate = null, string createdBy = "") @@ -101,7 +109,32 @@ public WorkItem UpdateWorkItem(JsonPatchDocument patchDocument, int workItemId) public TeamProject GetProject(string projectId) { - return ProjectClient.GetProject(projectId).Result; + // Check cache first + if (_projectCache.TryGetValue(projectId, out var cachedProject)) + { + return cachedProject; + } + + // If not in cache, fetch and store in cache + var project = ProjectClient.GetProject(projectId).Result; + _projectCache[projectId] = project; + return project; + } + + public GitRepository GetRepository(string project, string repository) + { + string cacheKey = $"{project}-{repository}"; + + // Check cache first + if (_repositoryCache.TryGetValue(cacheKey, out var cachedRepository)) + { + return cachedRepository; + } + + // If not in cache, fetch and store in cache + var repo = GitClient.GetRepositoryAsync(project, repository).Result; + _repositoryCache[cacheKey] = repo; + return repo; } public List GetRelationTypes() diff --git a/src/WorkItemMigrator/tests/Migration.Wi-Import.Tests/WitClient/JsonPatchDocUtilsTests.cs b/src/WorkItemMigrator/tests/Migration.Wi-Import.Tests/WitClient/JsonPatchDocUtilsTests.cs index 36dd3e15..cbcd0fc0 100644 --- a/src/WorkItemMigrator/tests/Migration.Wi-Import.Tests/WitClient/JsonPatchDocUtilsTests.cs +++ b/src/WorkItemMigrator/tests/Migration.Wi-Import.Tests/WitClient/JsonPatchDocUtilsTests.cs @@ -58,10 +58,10 @@ public void When_calling_create_json_field_patch_op_Then_a_correct_op_is_returne [Test] public void When_calling_create_json_artifact_link_field_patch_op_Then_a_correct_op_is_returned() { - string project = "project"; - string repository = "repository"; - string commitId = "commitId"; - JsonPatchOperation jsonPatchOp = JsonPatchDocUtils.CreateJsonArtifactLinkPatchOp(Operation.Add, project, repository, commitId); + string projectId = Guid.NewGuid().ToString(); + string repositoryId = Guid.NewGuid().ToString(); + string commitId = Guid.NewGuid().ToString(); + JsonPatchOperation jsonPatchOp = JsonPatchDocUtils.CreateJsonArtifactLinkPatchOp(Operation.Add, projectId, repositoryId, commitId); PatchOperationValue artifactLink = jsonPatchOp.Value as PatchOperationValue; Assert.Multiple(() => @@ -69,7 +69,7 @@ public void When_calling_create_json_artifact_link_field_patch_op_Then_a_correct Assert.AreEqual(Operation.Add, jsonPatchOp.Operation); Assert.AreEqual("/relations/-", jsonPatchOp.Path); Assert.AreEqual("ArtifactLink", artifactLink.Rel); - Assert.AreEqual($"vstfs:///Git/Commit/{project}/{repository}/{commitId}", artifactLink.Url); + Assert.AreEqual($"vstfs:///Git/Commit/{projectId}%2F{repositoryId}%2F{commitId}", artifactLink.Url); }); } } diff --git a/src/WorkItemMigrator/tests/Migration.Wi-Import.Tests/WitClient/WitClientUtilsTests.cs b/src/WorkItemMigrator/tests/Migration.Wi-Import.Tests/WitClient/WitClientUtilsTests.cs index ab6b306c..823165f9 100644 --- a/src/WorkItemMigrator/tests/Migration.Wi-Import.Tests/WitClient/WitClientUtilsTests.cs +++ b/src/WorkItemMigrator/tests/Migration.Wi-Import.Tests/WitClient/WitClientUtilsTests.cs @@ -1,6 +1,7 @@ using AutoFixture; using AutoFixture.AutoNSubstitute; using Microsoft.TeamFoundation.Core.WebApi; +using Microsoft.TeamFoundation.SourceControl.WebApi; using Microsoft.TeamFoundation.WorkItemTracking.WebApi.Models; using Microsoft.VisualStudio.Services.WebApi; using Microsoft.VisualStudio.Services.WebApi.Patch; @@ -12,6 +13,8 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; +using System.Security.Cryptography; +using System.Text; using WorkItemImport; namespace Migration.Wi_Import.Tests @@ -124,6 +127,25 @@ public TeamProject GetProject(string projectId) return tp; } + public GitRepository GetRepository(string project, string repository) + { + GitRepository gr = new GitRepository(); + Guid repoGuid; + + // Create a new instance of the MD5CryptoServiceProvider object. + MD5 md5Hasher = MD5.Create(); + + // Convert the input string to a byte array and compute the hash. + byte[] data = md5Hasher.ComputeHash(Encoding.Default.GetBytes(project)); + + // Create a new Guid using the hash value. + repoGuid = new Guid(md5Hasher.ComputeHash(Encoding.Default.GetBytes(project))); + gr.Id = repoGuid; + gr.Name = repository; + + return gr; + } + public List GetRelationTypes() { WorkItemRelationType hierarchyForward = new WorkItemRelationType