Skip to content

Commit f6a1290

Browse files
authored
Feature/user mapping with account (#204)
* #184 - Added IsCustomField check to MapRendered mapper * Get account id from jira cloud * Update documentaions on user mapping
1 parent fd39e60 commit f6a1290

File tree

6 files changed

+49
-40
lines changed

6 files changed

+49
-40
lines changed

docs/config.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ Currently the tool has a rather naive implementation for mapping certain constru
9393
|---|---|
9494
|MapTitle|Maps summary on the format [id] summary|
9595
|MapTitleWithoutKey|Maps summary field without [id]|
96-
|MapUser|Maps users based on email or name by lookup in the users.txt if specified.|
96+
|MapUser|Maps users based on email or name by lookup in the users.txt if specified, this applies only for Jira Server. When using Jira Cloud mapping can be done email if email is allowed to be displayed on the user profile or by accountId|
9797
|MapSprint|Maps a sprint by matching the Azure DevOps iteration tree|
9898
|MapTags|Maps tags by replacing space with semi-colon|
9999
|MapArray|Maps an array by replacing comma with semi-colon|

src/WorkItemMigrator/JiraExport/App.config

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,6 @@
33
<startup>
44
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.1" />
55
</startup>
6-
<runtime>
7-
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
8-
<dependentAssembly>
9-
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
10-
<bindingRedirect oldVersion="0.0.0.0-11.0.0.0" newVersion="11.0.0.0" />
11-
</dependentAssembly>
12-
<dependentAssembly>
13-
<assemblyIdentity name="RestSharp" publicKeyToken="598062e77f915f75" culture="neutral" />
14-
<bindingRedirect oldVersion="0.0.0.0-106.6.10.0" newVersion="106.6.10.0" />
15-
</dependentAssembly>
16-
</assemblyBinding>
17-
</runtime>
186
<appSettings>
197
<add key="applicationInsightsKey" value="584b887e-5d80-41c3-8303-9f421d46c421" />
208
</appSettings>

src/WorkItemMigrator/JiraExport/JiraItem.cs

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ private static List<JiraRevision> BuildRevisions(JiraItem jiraItem, JiraProvider
3939
List<JiraLink> links = ExtractLinks(issueKey, remoteIssue.SelectTokens("$.fields.issuelinks[*]").Cast<JObject>()) ?? new List<JiraLink>();
4040

4141
// save these field since these might be removed in the loop
42-
var reporter = fields.TryGetValue("reporter", out object rep) ? (string)rep : null;
42+
string reporter = GetAuthor(fields);
4343
var createdOn = fields.TryGetValue("created", out object crdate) ? (DateTime)crdate : default(DateTime);
4444
if (createdOn == DateTime.MinValue)
4545
Logger.Log(LogLevel.Debug, "created key was not found, using DateTime default value");
@@ -54,13 +54,14 @@ private static List<JiraRevision> BuildRevisions(JiraItem jiraItem, JiraProvider
5454
foreach (var change in changelog)
5555
{
5656
DateTime created = change.ExValue<DateTime>("$.created");
57-
string author = change.ExValue<string>("$.author.name");
57+
string author = GetAuthor(change);
5858

5959
List<RevisionAction<JiraLink>> linkChanges = new List<RevisionAction<JiraLink>>();
6060
List<RevisionAction<JiraAttachment>> attachmentChanges = new List<RevisionAction<JiraAttachment>>();
6161
Dictionary<string, object> fieldChanges = new Dictionary<string, object>(StringComparer.InvariantCultureIgnoreCase);
6262

6363
var items = change.SelectTokens("$.items[*]")?.Cast<JObject>()?.Select(i => new JiraChangeItem(i));
64+
6465
foreach (var item in items)
6566
{
6667
if (item.Field == "Link")
@@ -129,7 +130,7 @@ private static List<JiraRevision> BuildCommentRevisions(JiraItem jiraItem, JiraP
129130
var rc = renderedFields.SelectToken($"$.[{i}].body");
130131
return new JiraRevision(jiraItem)
131132
{
132-
Author = c.Author,
133+
Author = c.AuthorUser.Username ?? ExtractAuthorIdentity(c.AuthorUser.AccountId),
133134
Time = c.CreatedDate.Value,
134135
Fields = new Dictionary<string, object>() { { "comment", c.Body }, { "comment$Rendered", rc.Value<string>() } },
135136
AttachmentActions = new List<RevisionAction<JiraAttachment>>(),
@@ -323,6 +324,7 @@ private static Dictionary<string, object> ExtractFields(string key, JObject remo
323324
var renderedFields = (JObject)remoteIssue.SelectToken("$.renderedFields");
324325

325326
var extractName = new Func<JToken, object>((t) => t.ExValue<string>("$.name"));
327+
var extractAccountIdOrUsername = new Func<JToken, object>((t) => t.ExValue<string>("$.name") ?? t.ExValue<string>("$.accountId"));
326328

327329
if (_fieldExtractionMapping == null)
328330
{
@@ -332,7 +334,7 @@ private static Dictionary<string, object> ExtractFields(string key, JObject remo
332334
{ "labels", t => t.Values<string>().Any() ? string.Join(" ", t.Values<string>()) : null },
333335
{ "assignee", extractName },
334336
{ "creator", extractName },
335-
{ "reporter", extractName},
337+
{ "reporter", extractAccountIdOrUsername},
336338
{ jira.Settings.SprintField, t => string.Join(", ", ParseCustomField(jira.Settings.SprintField, t, jira)) },
337339
{ "status", extractName },
338340
{ "parent", t => t.ExValue<string>("$.key") }
@@ -391,6 +393,31 @@ private static Dictionary<string, object> ExtractFields(string key, JObject remo
391393

392394
return fields;
393395
}
396+
private static string GetAuthor(Dictionary<string, object> fields)
397+
{
398+
var reporter = fields.TryGetValue("reporter", out object rep) ? (string)rep : null;
399+
400+
return ExtractAuthorIdentity(reporter);
401+
}
402+
private static string GetAuthor(JObject change)
403+
{
404+
var author = change.ExValue<string>("$.author.name") ?? change.ExValue<string>("$.author.accountId");
405+
return ExtractAuthorIdentity(author);
406+
407+
}
408+
409+
private static string ExtractAuthorIdentity(string author)
410+
{
411+
if (string.IsNullOrEmpty(author))
412+
return default(string);
413+
414+
if (!author.Contains(':'))
415+
return author;
416+
417+
var startIndex = author.IndexOf(':') + 1;
418+
419+
return author.Substring(startIndex, author.Length - startIndex).Replace("-", "");
420+
}
394421

395422
private static string[] ParseCustomField(string fieldName, JToken value, JiraProvider provider)
396423
{
@@ -412,7 +439,6 @@ private static string[] ParseCustomField(string fieldName, JToken value, JiraPro
412439

413440
public string Key { get { return RemoteIssue.ExValue<string>("$.key"); } }
414441
public string Type { get { return RemoteIssue.ExValue<string>("$.fields.issuetype.name")?.Trim(); } }
415-
416442
public string EpicParent
417443
{
418444
get
@@ -426,18 +452,13 @@ public string EpicParent
426452
public string Parent { get { return RemoteIssue.ExValue<string>("$.fields.parent.key"); } }
427453
public List<string> SubItems { get { return GetSubTasksKey(); } }
428454

429-
430-
431455
public JObject RemoteIssue { get; private set; }
432-
433456
public List<JiraRevision> Revisions { get; set; }
434-
435457
private JiraItem(JiraProvider provider, JObject remoteIssue)
436458
{
437459
this._provider = provider;
438460
RemoteIssue = remoteIssue;
439461
}
440-
441462
internal string GetUserEmail(string author)
442463
{
443464
return _provider.GetUserEmail(author);

src/WorkItemMigrator/JiraExport/JiraProvider.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -319,30 +319,30 @@ public int GetNumberOfComments(string key)
319319
return Jira.Issues.GetCommentsAsync(key).Result.Count();
320320
}
321321

322-
public string GetUserEmail(string username)
322+
public string GetUserEmail(string usernameOrAccountId)
323323
{
324-
if (_userEmailCache.TryGetValue(username, out string email))
324+
if (_userEmailCache.TryGetValue(usernameOrAccountId, out string email))
325325
{
326326
return email;
327327
}
328328
else
329329
{
330330
try
331331
{
332-
var user = Jira.Users.GetUserAsync(username).Result;
332+
var user = Jira.Users.GetUserAsync(usernameOrAccountId).Result;
333333
if (string.IsNullOrEmpty(user.Email))
334334
{
335-
Logger.Log(LogLevel.Warning, $"Email for user '{username}' not found in Jira, using username '{username}' for mapping.");
336-
return username;
335+
Logger.Log(LogLevel.Warning, $"Email for user '{usernameOrAccountId}' not found in Jira, using usernameOrAccountId '{usernameOrAccountId}' for mapping.");
336+
return usernameOrAccountId;
337337
}
338338
email = user.Email;
339-
_userEmailCache.Add(username, email);
339+
_userEmailCache.Add(usernameOrAccountId, email);
340340
return email;
341341
}
342342
catch (Exception)
343343
{
344-
Logger.Log(LogLevel.Warning, $"User '{username}' not found in Jira, using username '{username}' for mapping.");
345-
return username;
344+
Logger.Log(LogLevel.Warning, $"User with '{usernameOrAccountId}' not found in Jira, using usernameOrAccountId '{usernameOrAccountId}' for mapping. Using accountId instead");
345+
return usernameOrAccountId;
346346
}
347347
}
348348
}

src/WorkItemMigrator/JiraExport/jira-export.csproj

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,8 @@
7373
<ApplicationManifest>Properties\app.manifest</ApplicationManifest>
7474
</PropertyGroup>
7575
<ItemGroup>
76-
<Reference Include="Atlassian.Jira, Version=11.2.0.0, Culture=neutral, processorArchitecture=MSIL">
77-
<HintPath>..\packages\Atlassian.SDK.11.2.0\lib\net452\Atlassian.Jira.dll</HintPath>
76+
<Reference Include="Atlassian.Jira, Version=12.1.0.0, Culture=neutral, processorArchitecture=MSIL">
77+
<HintPath>..\packages\Atlassian.SDK.12.1.0\lib\net452\Atlassian.Jira.dll</HintPath>
7878
</Reference>
7979
<Reference Include="Microsoft.AI.Agent.Intercept, Version=2.4.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
8080
<HintPath>..\packages\Microsoft.ApplicationInsights.Agent.Intercept.2.4.0\lib\net45\Microsoft.AI.Agent.Intercept.dll</HintPath>
@@ -88,11 +88,11 @@
8888
<Reference Include="Microsoft.Extensions.CommandLineUtils, Version=1.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
8989
<HintPath>..\packages\Microsoft.Extensions.CommandLineUtils.1.1.1\lib\net451\Microsoft.Extensions.CommandLineUtils.dll</HintPath>
9090
</Reference>
91-
<Reference Include="Newtonsoft.Json, Version=11.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
92-
<HintPath>..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll</HintPath>
91+
<Reference Include="Newtonsoft.Json, Version=12.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
92+
<HintPath>..\packages\Newtonsoft.Json.12.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
9393
</Reference>
94-
<Reference Include="RestSharp, Version=106.6.10.0, Culture=neutral, PublicKeyToken=598062e77f915f75, processorArchitecture=MSIL">
95-
<HintPath>..\packages\RestSharp.106.6.10\lib\net452\RestSharp.dll</HintPath>
94+
<Reference Include="RestSharp, Version=106.0.0.0, Culture=neutral, PublicKeyToken=598062e77f915f75, processorArchitecture=MSIL">
95+
<HintPath>..\packages\RestSharp.106.10.1\lib\net452\RestSharp.dll</HintPath>
9696
</Reference>
9797
<Reference Include="System" />
9898
<Reference Include="System.Configuration" />
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<packages>
3-
<package id="Atlassian.SDK" version="11.2.0" targetFramework="net471" />
3+
<package id="Atlassian.SDK" version="12.1.0" targetFramework="net471" />
44
<package id="Microsoft.ApplicationInsights" version="2.9.1" targetFramework="net471" />
55
<package id="Microsoft.ApplicationInsights.Agent.Intercept" version="2.4.0" targetFramework="net471" />
66
<package id="Microsoft.ApplicationInsights.DependencyCollector" version="2.9.1" targetFramework="net471" />
77
<package id="Microsoft.Extensions.CommandLineUtils" version="1.1.1" targetFramework="net461" />
8-
<package id="Newtonsoft.Json" version="11.0.2" targetFramework="net471" />
9-
<package id="RestSharp" version="106.6.10" targetFramework="net471" />
8+
<package id="Newtonsoft.Json" version="12.0.3" targetFramework="net471" />
9+
<package id="RestSharp" version="106.10.1" targetFramework="net471" />
1010
<package id="System.Diagnostics.DiagnosticSource" version="4.5.0" targetFramework="net471" />
1111
</packages>

0 commit comments

Comments
 (0)