Skip to content

Commit 0a3ce0b

Browse files
committed
➕ IJiraClient.GetIssueWorklogs() overload for a single issueKey
1 parent 9c0c5dc commit 0a3ce0b

File tree

4 files changed

+221
-192
lines changed

4 files changed

+221
-192
lines changed

jwl.jira/IJiraClient.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ public interface IJiraClient
1111

1212
Task<WorkLogType[]> GetAvailableActivities();
1313

14+
Task<WorkLog[]> GetIssueWorklogs(DateOnly from, DateOnly to, string issueKey);
15+
1416
Task<WorkLog[]> GetIssueWorklogs(DateOnly from, DateOnly to, IEnumerable<string>? issueKeys);
1517

1618
Task AddWorklog(string issueKey, DateOnly day, int timeSpentSeconds, string? activity, string? comment);

jwl.jira/JiraWithICTimePluginApi.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,11 @@ public async Task<WorkLogType[]> GetAvailableActivities()
3636
return await _vanillaJiraApi.GetAvailableActivities();
3737
}
3838

39+
public async Task<WorkLog[]> GetIssueWorklogs(DateOnly from, DateOnly to, string issueKey)
40+
{
41+
return await _vanillaJiraApi.GetIssueWorklogs(from, to, issueKey);
42+
}
43+
3944
public async Task<WorkLog[]> GetIssueWorklogs(DateOnly from, DateOnly to, IEnumerable<string>? issueKeys)
4045
{
4146
return await _vanillaJiraApi.GetIssueWorklogs(from, to, issueKeys);

jwl.jira/JiraWithTempoPluginApi.cs

Lines changed: 171 additions & 168 deletions
Original file line numberDiff line numberDiff line change
@@ -1,171 +1,174 @@
1-
namespace jwl.jira;
2-
using System.Net.Http.Json;
3-
using jwl.infra;
4-
using jwl.jira.api.rest.common;
5-
6-
// https://www.tempo.io/server-api-documentation/timesheets
7-
public class JiraWithTempoPluginApi
8-
: IJiraClient
9-
{
10-
private const string WorklogTypeAttributeKey = @"_WorklogType_";
11-
12-
private readonly HttpClient _httpClient;
13-
private readonly VanillaJiraClient _vanillaJiraServerApi;
14-
15-
public string UserName { get; }
16-
17-
public JiraWithTempoPluginApi(HttpClient httpClient, string userName)
18-
{
19-
_httpClient = httpClient;
20-
_vanillaJiraServerApi = new VanillaJiraClient(httpClient, userName);
21-
22-
UserName = userName;
23-
}
24-
25-
public async Task<JiraUserInfo> GetUserInfo()
26-
{
27-
return await _vanillaJiraServerApi.GetUserInfo();
28-
}
29-
30-
public async Task<api.rest.response.TempoWorklogAttributeDefinition[]> GetWorklogAttributeDefinitions()
31-
{
32-
return await _httpClient.GetAsJsonAsync<api.rest.response.TempoWorklogAttributeDefinition[]>(@"rest/tempo-core/1/work-attribute");
33-
}
34-
35-
public async Task<WorkLogType[]> GetAvailableActivities()
36-
{
37-
api.rest.response.TempoWorklogAttributeDefinition[] attrEnumDefs = await GetWorklogAttributeDefinitions();
38-
39-
var result = attrEnumDefs
40-
.Where(attrDef => attrDef.Key?.Equals(WorklogTypeAttributeKey) ?? false)
41-
.Where(attrDef => attrDef.Type != null
42-
&& attrDef.Type?.Value == TempoWorklogAttributeTypeIdentifier.StaticList
43-
)
44-
.SelectMany(attrDef => attrDef.StaticListValues)
45-
.Where(staticListItem => !string.IsNullOrEmpty(staticListItem.Name) && !string.IsNullOrEmpty(staticListItem.Value))
46-
.Select(staticListItem => new WorkLogType(
47-
Key: staticListItem.Name ?? string.Empty,
48-
Value: staticListItem.Value ?? string.Empty,
49-
Sequence: staticListItem.Sequence ?? -1
50-
))
51-
.ToArray();
52-
53-
return result;
54-
}
55-
56-
public async Task<WorkLog[]> GetIssueWorklogs(DateOnly from, DateOnly to, IEnumerable<string>? issueKeys)
57-
{
58-
JiraUserInfo userInfo = await GetUserInfo();
59-
string userKey = userInfo.Key ?? throw new ArgumentNullException($"{nameof(userInfo)}.{nameof(userInfo.Key)}");
60-
61-
var request = new api.rest.request.TempoFindWorklogs(from, to)
62-
{
63-
IssueKey = issueKeys?.ToArray(),
64-
UserKey = new string[] { userKey }
65-
};
66-
var response = await _httpClient.PostAsJsonAsync(@"rest/tempo-timesheets/4/worklogs/search", request);
67-
var tempoWorkLogs = await HttpClientJsonExt.DeserializeJsonStreamAsync<api.rest.response.TempoWorklog[]>(await response.Content.ReadAsStreamAsync());
68-
69-
var result = tempoWorkLogs
70-
.Select(wl => new WorkLog(
71-
Id: wl.Id ?? -1,
72-
IssueId: wl.IssueId ?? -1,
73-
AuthorName: wl.WorkerKey == userKey ? UserName : null,
74-
AuthorKey: wl.WorkerKey,
75-
Created: wl.Created?.Value ?? DateTime.MinValue,
76-
Started: wl.Started?.Value ?? DateTime.MinValue,
77-
TimeSpentSeconds: wl.TimeSpentSeconds ?? -1,
78-
Activity: wl.Attributes?[WorklogTypeAttributeKey].Value,
79-
Comment: wl.Comment ?? string.Empty
80-
))
81-
.ToArray();
82-
83-
return result;
84-
}
85-
86-
public async Task AddWorklog(string issueKey, DateOnly day, int timeSpentSeconds, string? activity, string? comment)
87-
{
88-
await AddWorklogPeriod(issueKey, day, day, timeSpentSeconds, activity, comment);
89-
}
90-
91-
public async Task AddWorklogPeriod(string issueKey, DateOnly dayFrom, DateOnly dayTo, int timeSpentSeconds, string? tempoWorklogType, string? comment, bool includeNonWorkingDays = false)
92-
{
93-
JiraUserInfo userInfo = await GetUserInfo();
94-
string userKey = userInfo.Key ?? throw new ArgumentNullException($"{nameof(userInfo)}.{nameof(userInfo.Key)}");
95-
96-
var request = new api.rest.request.TempoAddWorklogByIssueKey()
97-
{
98-
IssueKey = issueKey,
99-
Worker = userKey,
100-
Started = new api.rest.common.TempoDate(dayFrom),
101-
EndDate = new api.rest.common.TempoDate(dayTo),
102-
TimeSpentSeconds = timeSpentSeconds,
103-
BillableSeconds = timeSpentSeconds,
104-
IncludeNonWorkingDays = includeNonWorkingDays,
105-
Comment = comment,
106-
Attributes = new Dictionary<string, api.rest.common.TempoWorklogAttribute>()
107-
{
108-
[WorklogTypeAttributeKey] = new api.rest.common.TempoWorklogAttribute()
109-
{
110-
WorkAttributeId = 1,
111-
Key = WorklogTypeAttributeKey,
112-
Name = @"Worklog Type",
113-
Type = api.rest.common.TempoWorklogAttributeTypeIdentifier.StaticList,
114-
Value = tempoWorklogType
115-
}
116-
}
117-
};
118-
119-
HttpResponseMessage response = await _httpClient.PostAsJsonAsync(@"rest/tempo-timesheets/4/worklogs", request);
120-
await VanillaJiraClient.CheckHttpResponseForErrorMessages(response);
121-
}
122-
123-
public async Task DeleteWorklog(long issueId, long worklogId, bool notifyUsers = false)
124-
{
125-
UriBuilder uriBuilder = new UriBuilder()
126-
{
127-
Path = new UriPathBuilder(@"rest/tempo-timesheets/4/worklogs")
128-
.Add(worklogId.ToString())
129-
};
130-
1+
namespace jwl.jira;
2+
using System.Net.Http.Json;
3+
using jwl.infra;
4+
using jwl.jira.api.rest.common;
5+
6+
// https://www.tempo.io/server-api-documentation/timesheets
7+
public class JiraWithTempoPluginApi
8+
: IJiraClient
9+
{
10+
private const string WorklogTypeAttributeKey = @"_WorklogType_";
11+
12+
private readonly HttpClient _httpClient;
13+
private readonly VanillaJiraClient _vanillaJiraServerApi;
14+
15+
public string UserName { get; }
16+
17+
public JiraWithTempoPluginApi(HttpClient httpClient, string userName)
18+
{
19+
_httpClient = httpClient;
20+
_vanillaJiraServerApi = new VanillaJiraClient(httpClient, userName);
21+
22+
UserName = userName;
23+
}
24+
25+
public async Task<JiraUserInfo> GetUserInfo()
26+
{
27+
return await _vanillaJiraServerApi.GetUserInfo();
28+
}
29+
30+
public async Task<api.rest.response.TempoWorklogAttributeDefinition[]> GetWorklogAttributeDefinitions()
31+
{
32+
return await _httpClient.GetAsJsonAsync<api.rest.response.TempoWorklogAttributeDefinition[]>(@"rest/tempo-core/1/work-attribute");
33+
}
34+
35+
public async Task<WorkLogType[]> GetAvailableActivities()
36+
{
37+
api.rest.response.TempoWorklogAttributeDefinition[] attrEnumDefs = await GetWorklogAttributeDefinitions();
38+
39+
var result = attrEnumDefs
40+
.Where(attrDef => attrDef.Key?.Equals(WorklogTypeAttributeKey) ?? false)
41+
.Where(attrDef => attrDef.Type != null
42+
&& attrDef.Type?.Value == TempoWorklogAttributeTypeIdentifier.StaticList
43+
)
44+
.SelectMany(attrDef => attrDef.StaticListValues)
45+
.Where(staticListItem => !string.IsNullOrEmpty(staticListItem.Name) && !string.IsNullOrEmpty(staticListItem.Value))
46+
.Select(staticListItem => new WorkLogType(
47+
Key: staticListItem.Name ?? string.Empty,
48+
Value: staticListItem.Value ?? string.Empty,
49+
Sequence: staticListItem.Sequence ?? -1
50+
))
51+
.ToArray();
52+
53+
return result;
54+
}
55+
56+
public async Task<WorkLog[]> GetIssueWorklogs(DateOnly from, DateOnly to, string issueKey)
57+
{
58+
return await GetIssueWorklogs(from, to, new string[] { issueKey });
59+
}
60+
61+
public async Task<WorkLog[]> GetIssueWorklogs(DateOnly from, DateOnly to, IEnumerable<string>? issueKeys)
62+
{
63+
string userKey = _vanillaJiraServerApi.UserInfo.Key ?? throw new ArgumentNullException($"{nameof(_vanillaJiraServerApi.UserInfo)}.{nameof(_vanillaJiraServerApi.UserInfo.Key)}");
64+
65+
var request = new api.rest.request.TempoFindWorklogs(from, to)
66+
{
67+
IssueKey = issueKeys?.ToArray(),
68+
UserKey = new string[] { userKey }
69+
};
70+
var response = await _httpClient.PostAsJsonAsync(@"rest/tempo-timesheets/4/worklogs/search", request);
71+
var tempoWorkLogs = await HttpClientJsonExt.DeserializeJsonStreamAsync<api.rest.response.TempoWorklog[]>(await response.Content.ReadAsStreamAsync());
72+
73+
var result = tempoWorkLogs
74+
.Select(wl => new WorkLog(
75+
Id: wl.Id ?? -1,
76+
IssueId: wl.IssueId ?? -1,
77+
AuthorName: wl.WorkerKey == userKey ? UserName : null,
78+
AuthorKey: wl.WorkerKey,
79+
Created: wl.Created?.Value ?? DateTime.MinValue,
80+
Started: wl.Started?.Value ?? DateTime.MinValue,
81+
TimeSpentSeconds: wl.TimeSpentSeconds ?? -1,
82+
Activity: wl.Attributes?[WorklogTypeAttributeKey].Value,
83+
Comment: wl.Comment ?? string.Empty
84+
))
85+
.ToArray();
86+
87+
return result;
88+
}
89+
90+
public async Task AddWorklog(string issueKey, DateOnly day, int timeSpentSeconds, string? activity, string? comment)
91+
{
92+
await AddWorklogPeriod(issueKey, day, day, timeSpentSeconds, activity, comment);
93+
}
94+
95+
public async Task AddWorklogPeriod(string issueKey, DateOnly dayFrom, DateOnly dayTo, int timeSpentSeconds, string? tempoWorklogType, string? comment, bool includeNonWorkingDays = false)
96+
{
97+
string userKey = _vanillaJiraServerApi.UserInfo.Key ?? throw new ArgumentNullException($"{nameof(_vanillaJiraServerApi.UserInfo)}.{nameof(_vanillaJiraServerApi.UserInfo.Key)}");
98+
99+
var request = new api.rest.request.TempoAddWorklogByIssueKey()
100+
{
101+
IssueKey = issueKey,
102+
Worker = userKey,
103+
Started = new api.rest.common.TempoDate(dayFrom),
104+
EndDate = new api.rest.common.TempoDate(dayTo),
105+
TimeSpentSeconds = timeSpentSeconds,
106+
BillableSeconds = timeSpentSeconds,
107+
IncludeNonWorkingDays = includeNonWorkingDays,
108+
Comment = comment,
109+
Attributes = new Dictionary<string, api.rest.common.TempoWorklogAttribute>()
110+
{
111+
[WorklogTypeAttributeKey] = new api.rest.common.TempoWorklogAttribute()
112+
{
113+
WorkAttributeId = 1,
114+
Key = WorklogTypeAttributeKey,
115+
Name = @"Worklog Type",
116+
Type = api.rest.common.TempoWorklogAttributeTypeIdentifier.StaticList,
117+
Value = tempoWorklogType
118+
}
119+
}
120+
};
121+
122+
HttpResponseMessage response = await _httpClient.PostAsJsonAsync(@"rest/tempo-timesheets/4/worklogs", request);
123+
await VanillaJiraClient.CheckHttpResponseForErrorMessages(response);
124+
}
125+
126+
public async Task DeleteWorklog(long issueId, long worklogId, bool notifyUsers = false)
127+
{
128+
UriBuilder uriBuilder = new UriBuilder()
129+
{
130+
Path = new UriPathBuilder(@"rest/tempo-timesheets/4/worklogs")
131+
.Add(worklogId.ToString())
132+
};
133+
131134
HttpResponseMessage response = await _httpClient.DeleteAsync(uriBuilder.Uri.PathAndQuery);
132-
await VanillaJiraClient.CheckHttpResponseForErrorMessages(response);
133-
}
134-
135-
public async Task UpdateWorklog(string issueKey, long worklogId, DateOnly day, int timeSpentSeconds, string? activity, string? comment)
136-
{
137-
await UpdateWorklogPeriod(issueKey, worklogId, day, day, timeSpentSeconds, comment, activity);
138-
}
139-
140-
private async Task UpdateWorklogPeriod(string issueKey, long worklogId, DateOnly dayFrom, DateOnly dayTo, int timeSpentSeconds, string? comment, string? activity, bool includeNonWorkingDays = false)
141-
{
142-
UriBuilder uriBuilder = new UriBuilder()
143-
{
144-
Path = new UriPathBuilder(@"rest/tempo-timesheets/4/worklogs")
145-
.Add(worklogId.ToString())
146-
};
147-
var request = new api.rest.request.TempoUpdateWorklog()
148-
{
149-
Started = new api.rest.common.TempoDate(dayFrom),
150-
EndDate = new api.rest.common.TempoDate(dayTo),
151-
TimeSpentSeconds = timeSpentSeconds,
152-
BillableSeconds = timeSpentSeconds,
153-
IncludeNonWorkingDays = includeNonWorkingDays,
154-
Comment = comment,
155-
Attributes = new Dictionary<string, api.rest.common.TempoWorklogAttribute>()
156-
{
157-
[WorklogTypeAttributeKey] = new api.rest.common.TempoWorklogAttribute()
158-
{
159-
WorkAttributeId = 1,
160-
Key = WorklogTypeAttributeKey,
161-
Name = @"Worklog Type",
162-
Type = api.rest.common.TempoWorklogAttributeTypeIdentifier.StaticList,
163-
Value = activity
164-
}
165-
}
166-
};
167-
135+
await VanillaJiraClient.CheckHttpResponseForErrorMessages(response);
136+
}
137+
138+
public async Task UpdateWorklog(string issueKey, long worklogId, DateOnly day, int timeSpentSeconds, string? activity, string? comment)
139+
{
140+
await UpdateWorklogPeriod(issueKey, worklogId, day, day, timeSpentSeconds, comment, activity);
141+
}
142+
143+
private async Task UpdateWorklogPeriod(string issueKey, long worklogId, DateOnly dayFrom, DateOnly dayTo, int timeSpentSeconds, string? comment, string? activity, bool includeNonWorkingDays = false)
144+
{
145+
UriBuilder uriBuilder = new UriBuilder()
146+
{
147+
Path = new UriPathBuilder(@"rest/tempo-timesheets/4/worklogs")
148+
.Add(worklogId.ToString())
149+
};
150+
var request = new api.rest.request.TempoUpdateWorklog()
151+
{
152+
Started = new api.rest.common.TempoDate(dayFrom),
153+
EndDate = new api.rest.common.TempoDate(dayTo),
154+
TimeSpentSeconds = timeSpentSeconds,
155+
BillableSeconds = timeSpentSeconds,
156+
IncludeNonWorkingDays = includeNonWorkingDays,
157+
Comment = comment,
158+
Attributes = new Dictionary<string, api.rest.common.TempoWorklogAttribute>()
159+
{
160+
[WorklogTypeAttributeKey] = new api.rest.common.TempoWorklogAttribute()
161+
{
162+
WorkAttributeId = 1,
163+
Key = WorklogTypeAttributeKey,
164+
Name = @"Worklog Type",
165+
Type = api.rest.common.TempoWorklogAttributeTypeIdentifier.StaticList,
166+
Value = activity
167+
}
168+
}
169+
};
170+
168171
HttpResponseMessage response = await _httpClient.PutAsJsonAsync(uriBuilder.Uri.PathAndQuery, request);
169172
await VanillaJiraClient.CheckHttpResponseForErrorMessages(response);
170-
}
171-
}
173+
}
174+
}

0 commit comments

Comments
 (0)