Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/git artifact linking support #830

Merged
merged 8 commits into from
Aug 21, 2023
1 change: 1 addition & 0 deletions src/WorkItemMigrator/JiraExport/IJiraProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,6 @@ public interface IJiraProvider
string GetCustomId(string propertyName);
Task<List<RevisionAction<JiraAttachment>>> DownloadAttachments(JiraRevision rev);

IEnumerable<JObject> GetCommitRepositories(string issueId);
}
}
4 changes: 3 additions & 1 deletion src/WorkItemMigrator/JiraExport/JiraCommandLine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,9 @@ private void ExecuteMigration(CommandOption user, CommandOption password, Comman
UserMappingFile = config.UserMappingFile != null ? Path.Combine(migrationWorkspace, config.UserMappingFile) : string.Empty,
AttachmentsDir = Path.Combine(migrationWorkspace, config.AttachmentsFolder),
JQL = config.Query,
UsingJiraCloud = config.UsingJiraCloud
UsingJiraCloud = config.UsingJiraCloud,
IncludeCommits = config.IncludeCommits,
RepositoryMap = config.RepositoryMap
};

var jiraServiceWrapper = new JiraServiceWrapper(jiraSettings);
Expand Down
11 changes: 11 additions & 0 deletions src/WorkItemMigrator/JiraExport/JiraCommit.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using System;

namespace JiraExport
{
public class JiraCommit
{
public string Repository { get; set; }
public string Id { get; set; }
public DateTime AuthorTimestamp { get; set; }
}
}
33 changes: 33 additions & 0 deletions src/WorkItemMigrator/JiraExport/JiraItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,38 @@ private static List<JiraRevision> BuildRevisions(JiraItem jiraItem, IJiraProvide

List<JiraRevision> commentRevisions = BuildCommentRevisions(jiraItem, jiraProvider);
listOfRevisions.AddRange(commentRevisions);

var settings = jiraProvider.GetSettings();
if (settings.IncludeCommits)
{
var commitRepositories = jiraProvider.GetCommitRepositories(jiraItem.Id);
foreach (var respository in commitRepositories)
{
var commits = respository.SelectTokens(".commits[*]");
foreach (JToken commit in commits)
{
var commitCreatedOn = commit.ExValue<DateTime>("$.authorTimestamp");
var commitAuthor = GetAuthor(commit as JObject);
var jiraCommit = commit.ToObject<JiraCommit>();
var repositoryName = respository.SelectToken("$.name").Value<string>();
if (string.IsNullOrEmpty(repositoryName))
{
continue;
}

var hasRespositoryTarget = settings.RepositoryMap.Repositories.Exists(r => r.Source == repositoryName && !string.IsNullOrEmpty(r.Target));
if (!hasRespositoryTarget)
{
continue;
}

jiraCommit.Repository = repositoryName;
var commitRevision = new JiraRevision(jiraItem) { Time = commitCreatedOn, Author = commitAuthor, Fields = new Dictionary<string, object>(), Commit = new RevisionAction<JiraCommit>() { ChangeType = RevisionChangeType.Added, Value = jiraCommit } };
listOfRevisions.Add(commitRevision);
}
}
}

listOfRevisions.Sort();

foreach (var revAndI in listOfRevisions.Select((r, i) => (r, i)))
Expand Down Expand Up @@ -524,6 +556,7 @@ private static string[] ParseCustomField(string fieldName, JToken value, IJiraPr

public string Key { get { return RemoteIssue.ExValue<string>("$.key"); } }
public string Type { get { return RemoteIssue.ExValue<string>("$.fields.issuetype.name")?.Trim(); } }
public string Id { get { return RemoteIssue.ExValue<string>("$.id"); } }
public string EpicParent
{
get
Expand Down
39 changes: 37 additions & 2 deletions src/WorkItemMigrator/JiraExport/JiraMapper.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;

using Common.Config;

using Migration.Common;
Expand Down Expand Up @@ -55,6 +54,7 @@ internal WiItem Map(JiraItem issue)
return null;
}
}

return wiItem;
}

Expand Down Expand Up @@ -180,6 +180,39 @@ internal Dictionary<string, FieldMapping<JiraRevision>> InitializeFieldMappings(
return mappingPerWiType;
}

internal WiCommit MapCommit(JiraRevision jiraRevision)
{
if (jiraRevision == null)
throw new ArgumentNullException(nameof(jiraRevision));

if (jiraRevision.Commit == null)
{
return null;
}

var jiraCommit = jiraRevision.Commit.Value;
var respositoryTarget = jiraCommit.Repository;

var respositoryOverride = _config
.RepositoryMap
.Repositories?
.Find(r => r.Source == respositoryTarget)?
.Target;

if (!string.IsNullOrEmpty(respositoryOverride))
{
respositoryTarget = respositoryOverride;
}

var commit = new WiCommit()
{
Id = jiraCommit.Id,
Repository = respositoryTarget,
};

return commit;
}

internal List<WiLink> MapLinks(JiraRevision r)
{
if (r == null)
Expand Down Expand Up @@ -303,6 +336,7 @@ internal WiRevision MapRevision(JiraRevision r)
List<WiAttachment> attachments = MapAttachments(r);
List<WiField> fields = MapFields(r);
List<WiLink> links = MapLinks(r);
var commit = MapCommit(r);

return new WiRevision()
{
Expand All @@ -313,7 +347,8 @@ internal WiRevision MapRevision(JiraRevision r)
Attachments = attachments,
Fields = fields,
Links = links,
AttachmentReferences = attachments.Any()
AttachmentReferences = attachments.Any(),
Commit = commit
};
}

Expand Down
6 changes: 6 additions & 0 deletions src/WorkItemMigrator/JiraExport/JiraProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -441,5 +441,11 @@ private string GetItemFromFieldCache(string propertyName, ILookup<string, string
}
return customId;
}

public IEnumerable<JObject> GetCommitRepositories(string issueId)
{
var response = (JObject)_jiraServiceWrapper.RestClient.ExecuteRequestAsync(Method.GET, $"/rest/dev-status/latest/issue/detail?issueId={issueId}&applicationType=stash&dataType=repository").Result;
return response.SelectTokens("$.detail[*].repositories[*]").Cast<JObject>();
}
}
}
1 change: 1 addition & 0 deletions src/WorkItemMigrator/JiraExport/JiraRevision.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public class JiraRevision : ISourceRevision, IComparable<JiraRevision>
public List<RevisionAction<JiraLink>> LinkActions { get; set; }

public List<RevisionAction<JiraAttachment>> AttachmentActions { get; set; }
public RevisionAction<JiraCommit> Commit { get; set; }
public JiraItem ParentItem { get; private set; }
public int Index { get; set; }

Expand Down
4 changes: 4 additions & 0 deletions src/WorkItemMigrator/JiraExport/JiraSettings.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@

using Migration.Common.Config;

namespace JiraExport
{
public class JiraSettings
Expand All @@ -14,6 +16,8 @@ public class JiraSettings
public string AttachmentsDir { get; set; }
public string JQL { get; set; }
public bool UsingJiraCloud { get; set; }
public bool IncludeCommits { get; set; }
public RepositoryMap RepositoryMap { get; set; }

public JiraSettings(string userID, string pass, string url, string project)
{
Expand Down
1 change: 1 addition & 0 deletions src/WorkItemMigrator/JiraExport/jira-export.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@
<Compile Include="IJiraProvider.cs" />
<Compile Include="IJiraServiceWrapper.cs" />
<Compile Include="JiraCommandLine.cs" />
<Compile Include="JiraCommit.cs" />
<Compile Include="JiraServiceWrapper.cs" />
<Compile Include="JiraMapper.cs" />
<Compile Include="JiraAttachment.cs" />
Expand Down
8 changes: 7 additions & 1 deletion src/WorkItemMigrator/Migration.Common/Config/ConfigJson.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ public class ConfigJson
[JsonProperty(PropertyName = "process-template")]
public string ProcessTemplate { get; set; } = "Scrum";

[JsonProperty(PropertyName = "repository-map")]
public RepositoryMap RepositoryMap { get; set; }

[JsonProperty(PropertyName = "type-map", Required = Required.Always)]
public TypeMap TypeMap { get; set; }

Expand All @@ -63,13 +66,16 @@ public class ConfigJson
[JsonProperty(PropertyName = "rendered-fields")]
public string[] RenderedFields { get; set; } = new string[] { "description", "comment" };


[JsonProperty(PropertyName = "using-jira-cloud")]
public bool UsingJiraCloud { get; set; } = true;

[JsonProperty(PropertyName = "include-link-comments")]
public bool IncludeLinkComments { get; set; } = true;

[JsonProperty(PropertyName = "sleep-time-between-revision-import-milliseconds")]
public int SleepTimeBetweenRevisionImportMilliseconds { get; set; } = 0;

[JsonProperty(PropertyName = "include-commits")]
public bool IncludeCommits { get; set; } = true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,15 @@ public ConfigJson DeserializeText(string input)
}
result.TypeMap.Types.AddRange(types);

if(obj.ContainsKey("repository-map"))
{
var repositories = obj.SelectToken("repository-map.repository").Select(li => li.ToObject<Repository>()).ToList();
if (result.RepositoryMap.Repositories == null)
{
result.RepositoryMap.Repositories = new List<Repository>();
}
result.RepositoryMap.Repositories.AddRange(repositories);
}
}
catch (Exception)
{
Expand Down
13 changes: 13 additions & 0 deletions src/WorkItemMigrator/Migration.Common/Config/Repository.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using Newtonsoft.Json;

namespace Migration.Common.Config
{
public class Repository
{
[JsonProperty("target", Required = Required.Always)]
public string Target { get; set; }

[JsonProperty("source", Required = Required.Always)]
public string Source { get; set; }
}
}
9 changes: 9 additions & 0 deletions src/WorkItemMigrator/Migration.Common/Config/RepositoryMap.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using System.Collections.Generic;

namespace Migration.Common.Config
{
public class RepositoryMap
{
public List<Repository> Repositories { get; set; }
}
}
2 changes: 2 additions & 0 deletions src/WorkItemMigrator/Migration.Common/Migration.Common.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@
<Compile Include="Config\Link.cs" />
<Compile Include="Config\LinkMap.cs" />
<Compile Include="Config\Mapping.cs" />
<Compile Include="Config\RepositoryMap.cs" />
<Compile Include="Config\Repository.cs" />
<Compile Include="Config\Value.cs" />
<Compile Include="Config\Type.cs" />
<Compile Include="Config\TypeMap.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
<ItemGroup>
<Compile Include="WiAttachment.cs" />
<Compile Include="WiField.cs" />
<Compile Include="WiCommit.cs" />
<Compile Include="WiItem.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="WiItemProvider.cs" />
Expand Down
13 changes: 13 additions & 0 deletions src/WorkItemMigrator/Migration.WIContract/WiCommit.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace Migration.WIContract
{
public class WiCommit
{
public string Id { get; set; }
public string Repository { get; set; }

public override string ToString()
{
return $"[{Repository}]{Id}";
}
}
}
1 change: 1 addition & 0 deletions src/WorkItemMigrator/Migration.WIContract/WiRevision.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public WiRevision()
public List<WiField> Fields { get; set; }
public List<WiLink> Links { get; set; }
public List<WiAttachment> Attachments { get; set; }
public WiCommit Commit { get; set; }

[DefaultValue(false)]
public bool AttachmentReferences { get; set; } = false;
Expand Down
14 changes: 12 additions & 2 deletions src/WorkItemMigrator/WorkItemImport/Agent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,18 @@ public bool ImportRevision(WiRevision rev, WorkItem wi, Settings settings)
}
}

_witClientUtils.SaveWorkItemFields(wi);
// rev with a commit won't have meaningful information, skip saving fields
if (rev.Commit != null)
{
if (settings.IncludeCommits)
{
_witClientUtils.SaveWorkItemArtifacts(rev, wi, settings);
}
}
else
{
_witClientUtils.SaveWorkItemFields(wi);
}

if (wi.Id.HasValue)
{
Expand Down Expand Up @@ -579,7 +590,6 @@ private bool ApplyAndSaveLinks(WiRevision rev, WorkItem wi, bool addLinkComments

return success;
}

#endregion
}
}
3 changes: 2 additions & 1 deletion src/WorkItemMigrator/WorkItemImport/ImportCommandLine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,8 @@ private void ExecuteMigration(CommandOption token, CommandOption url, CommandOpt
BaseIterationPath = config.BaseIterationPath ?? string.Empty, // Root iteration path that will prefix each iteration
IgnoreFailedLinks = config.IgnoreFailedLinks,
ProcessTemplate = config.ProcessTemplate,
IncludeLinkComments = config.IncludeLinkComments
IncludeLinkComments = config.IncludeLinkComments,
IncludeCommits = config.IncludeCommits
};

// initialize Azure DevOps/TFS connection. Creates/fetches project, fills area and iteration caches.
Expand Down
1 change: 1 addition & 0 deletions src/WorkItemMigrator/WorkItemImport/Settings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,6 @@ public Settings(string account, string project, string pat)
public bool IgnoreFailedLinks { get; internal set; }
public string ProcessTemplate { get; internal set; }
public bool IncludeLinkComments { get; internal set; }
public bool IncludeCommits { get; internal set; }
}
}
Loading
Loading