Skip to content

Commit f3960a7

Browse files
authored
🛠 Jira server call errors now get rethrown as custom JWLC exceptions enriched with call arguments (#17)
🛠 errors in calls to Jira server now rethrown enriched with call arguments 🔧 WADL project now w/o PDBs in Release mode 🧹 whitespace
1 parent 38de056 commit f3960a7

12 files changed

+251
-9
lines changed

‎jwl.infra/HttpClientExt.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
namespace jwl.infra;
2+
23
using System.Text.Json;
34
using System.Xml.Serialization;
45

‎jwl.infra/UriQueryBuilder.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,10 @@ public UriQueryBuilder Add(string? key, string? value)
3838
}
3939

4040
public override string ToString() => this.Any()
41-
? string.Join('&', this
41+
? '?' + string.Join('&', this
4242
.Where(x => !string.IsNullOrEmpty(x.Key) || !string.IsNullOrEmpty(x.Value))
4343
.Select(x => Uri.EscapeDataString(x.Key ?? string.Empty) + "=" + Uri.EscapeDataString(x.Value ?? string.Empty))
44-
.Prepend("?"))
44+
)
4545
: string.Empty;
4646

4747
public static implicit operator string(UriQueryBuilder self) => self.ToString();
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
namespace jwl.jira.Exceptions;
2+
3+
internal class AddWorkLogException
4+
: JiraIssueSpecificException
5+
{
6+
public DateTime Moment { get; }
7+
public int TimeSpentSeconds { get; }
8+
public string? Activity { get; init; }
9+
public string? Comment { get; init; }
10+
11+
public AddWorkLogException(string issueKey, DateTime moment, int timeSpentSeconds)
12+
: base(issueKey, $"Error adding {timeSpentSeconds} seconds on issue {issueKey} at {moment}")
13+
{
14+
Moment = moment;
15+
TimeSpentSeconds = timeSpentSeconds;
16+
}
17+
18+
public AddWorkLogException(string issueKey, DateTime moment, int timeSpentSeconds, Exception innerException)
19+
: base(issueKey, $"Error adding {timeSpentSeconds} seconds on issue {issueKey} at {moment}", innerException)
20+
{
21+
Moment = moment;
22+
TimeSpentSeconds = timeSpentSeconds;
23+
}
24+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
namespace jwl.jira.Exceptions;
2+
3+
[Serializable]
4+
internal class DeleteWorklogException
5+
: JiraIssueSpecificException
6+
{
7+
public long IssueId { get; }
8+
public long WorklogId { get; }
9+
10+
public DeleteWorklogException(long issueId, long worklogId)
11+
: base($"ID {issueId}", $"Error deleting worklog ID {worklogId} on issue ID {issueId}")
12+
{
13+
IssueId = issueId;
14+
WorklogId = worklogId;
15+
}
16+
17+
public DeleteWorklogException(long issueId, long worklogId, Exception innerException)
18+
: base($"ID {issueId}", $"Error deleting worklog ID {worklogId} on issue ID {issueId}", innerException)
19+
{
20+
IssueId = issueId;
21+
WorklogId = worklogId;
22+
}
23+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
namespace jwl.jira.Exceptions;
2+
3+
using System;
4+
5+
public class GetIssueWorkLogsException
6+
: JiraIssueSpecificException
7+
{
8+
public DateTime DateFrom { get; }
9+
public DateTime DateTo { get; }
10+
11+
public GetIssueWorkLogsException(string issueKey, DateTime dateFrom, DateTime dateTo)
12+
: base(issueKey, $"Error retrieving worklogs for {issueKey} and timestamp range from {dateFrom} to {dateTo}")
13+
{
14+
DateFrom = dateFrom;
15+
DateTo = dateTo;
16+
}
17+
18+
public GetIssueWorkLogsException(string issueKey, DateTime dateFrom, DateTime dateTo, Exception innerException)
19+
: base(issueKey, $"Error retrieving worklogs for issue \"{issueKey}\" and period from {dateFrom} to {dateTo}", innerException)
20+
{
21+
DateFrom = dateFrom;
22+
DateTo = dateTo;
23+
}
24+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
namespace jwl.jira.Exceptions;
2+
3+
using System;
4+
5+
public class JiraClientException
6+
: ApplicationException
7+
{
8+
public JiraClientException()
9+
{
10+
}
11+
12+
public JiraClientException(string? message)
13+
: base(message)
14+
{
15+
}
16+
17+
public JiraClientException(string? message, Exception? innerException)
18+
: base(message, innerException)
19+
{
20+
}
21+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
namespace jwl.jira.Exceptions;
2+
3+
public class JiraIssueSpecificException
4+
: JiraClientException
5+
{
6+
public string? IssueKey { get; }
7+
8+
public JiraIssueSpecificException(string issueKey)
9+
: base($"Error on Jira issue {issueKey}")
10+
{
11+
IssueKey = issueKey;
12+
}
13+
14+
public JiraIssueSpecificException(string issueKey, string message)
15+
: base(message)
16+
{
17+
IssueKey = issueKey;
18+
}
19+
20+
public JiraIssueSpecificException(string issueKey, Exception innerException)
21+
: base($"Error on Jira issue {issueKey}", innerException)
22+
{
23+
IssueKey = issueKey;
24+
}
25+
26+
public JiraIssueSpecificException(string issueKey, string message, Exception innerException)
27+
: base(message, innerException)
28+
{
29+
IssueKey = issueKey;
30+
}
31+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
namespace jwl.jira.Exceptions;
2+
3+
public class UpdateWorklogException
4+
: JiraIssueSpecificException
5+
{
6+
public long WorklogId { get; }
7+
public DateTime Moment { get; }
8+
public int TimeSpentSeconds { get; }
9+
public string? Activity { get; init; }
10+
public string? Comment { get; init; }
11+
12+
public UpdateWorklogException(string issueKey, long worklogId, DateTime moment, int timeSpentSeconds)
13+
: base(issueKey, $"Error updating worklog ID {worklogId} with {timeSpentSeconds} seconds on issue {issueKey} at {moment}")
14+
{
15+
WorklogId = worklogId;
16+
Moment = moment;
17+
TimeSpentSeconds = timeSpentSeconds;
18+
}
19+
20+
public UpdateWorklogException(string issueKey, long worklogId, DateTime moment, int timeSpentSeconds, Exception innerException)
21+
: base(issueKey, $"Error updating worklog ID {worklogId} with {timeSpentSeconds} seconds on issue {issueKey} at {moment}", innerException)
22+
{
23+
WorklogId = worklogId;
24+
Moment = moment;
25+
TimeSpentSeconds = timeSpentSeconds;
26+
}
27+
}

‎jwl.jira/Flavours/JiraWithICTimePluginApi.cs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Xml.Serialization;
66
using jwl.infra;
77
using jwl.jira.api.rest.request;
8+
using jwl.jira.Exceptions;
89
using jwl.jira.Flavours;
910
using jwl.wadl;
1011

@@ -216,7 +217,16 @@ public async Task AddWorkLog(string issueKey, DateOnly day, int timeSpentSeconds
216217
Content = new FormUrlEncodedContent(args),
217218
};
218219
httpRequest.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(WadlRepresentation.MediaTypeJson));
219-
HttpResponseMessage response = await _httpClient.SendAsync(httpRequest);
220+
221+
HttpResponseMessage response;
222+
try
223+
{
224+
response = await _httpClient.SendAsync(httpRequest);
225+
}
226+
catch (Exception ex)
227+
{
228+
throw new AddWorkLogException(issueKey, day.ToDateTime(TimeOnly.MinValue), timeSpentSeconds, ex);
229+
}
220230

221231
if (response.Content.Headers.ContentType?.MediaType != WadlRepresentation.MediaTypeJson)
222232
throw new InvalidDataException($"Invalid media type returned ({response.Content.Headers.ContentType?.MediaType ?? string.Empty})");
@@ -257,6 +267,7 @@ private async Task<WadlApplication> GetWADL()
257267
{
258268
Uri uri = new Uri($"{_flavourOptions.PluginBaseUri}/application.wadl", UriKind.Relative);
259269
using Stream response = await _httpClient.GetStreamAsync(uri);
270+
260271
if (response == null)
261272
throw new HttpRequestException($"Empty content received from ${uri}");
262273

‎jwl.jira/Flavours/VanillaJiraClient.cs

Lines changed: 61 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ namespace jwl.jira;
88
using System.Xml;
99
using jwl.infra;
1010
using jwl.jira.api.rest.response;
11+
using jwl.jira.Exceptions;
1112
using jwl.jira.Flavours;
1213

1314
public class VanillaJiraClient
@@ -89,7 +90,17 @@ public async Task<WorkLog[]> GetIssueWorkLogs(DateOnly from, DateOnly to, string
8990
.Add(@"worklog")
9091
};
9192

92-
var response = await _httpClient.GetAsJsonAsync<api.rest.response.JiraIssueWorklogs>(uriBuilder.Uri.PathAndQuery);
93+
string uri = uriBuilder.Uri.PathAndQuery.TrimStart('/');
94+
95+
JiraIssueWorklogs? response;
96+
try
97+
{
98+
response = await _httpClient.GetAsJsonAsync<JiraIssueWorklogs>(uri);
99+
}
100+
catch (Exception ex)
101+
{
102+
throw new GetIssueWorkLogsException(issueKey, from.ToDateTime(TimeOnly.MinValue), to.ToDateTime(TimeOnly.MinValue).AddDays(1), ex);
103+
}
93104

94105
(DateTime minDt, DateTime supDt) = DateOnlyUtils.DateOnlyRangeToDateTimeRange(from, to);
95106

@@ -141,8 +152,10 @@ public async Task AddWorkLog(string issueKey, DateOnly day, int timeSpentSeconds
141152
};
142153

143154
StringBuilder commentBuilder = new StringBuilder();
155+
144156
if (activity != null)
145157
commentBuilder.Append($"({activity}){Environment.NewLine}");
158+
146159
commentBuilder.Append(comment);
147160

148161
var request = new api.rest.request.JiraAddWorklogByIssueKey(
@@ -155,7 +168,20 @@ public async Task AddWorkLog(string issueKey, DateOnly day, int timeSpentSeconds
155168
Comment: commentBuilder.ToString()
156169
);
157170

158-
HttpResponseMessage response = await _httpClient.PostAsJsonAsync(uriBuilder.Uri.PathAndQuery, request);
171+
HttpResponseMessage response;
172+
try
173+
{
174+
response = await _httpClient.PostAsJsonAsync(uriBuilder.Uri.PathAndQuery.TrimStart('/'), request);
175+
}
176+
catch (Exception ex)
177+
{
178+
throw new AddWorkLogException(issueKey, day.ToDateTime(TimeOnly.MinValue), timeSpentSeconds, ex)
179+
{
180+
Activity = activity,
181+
Comment = comment
182+
};
183+
}
184+
159185
await CheckHttpResponseForErrorMessages(response);
160186
}
161187

@@ -190,7 +216,16 @@ public async Task DeleteWorkLog(long issueId, long worklogId, bool notifyUsers =
190216
.Add(@"notifyUsers", notifyUsers.ToString().ToLower())
191217
};
192218

193-
HttpResponseMessage response = await _httpClient.DeleteAsync(uriBuilder.Uri.PathAndQuery);
219+
HttpResponseMessage response;
220+
try
221+
{
222+
response = await _httpClient.DeleteAsync(uriBuilder.Uri.PathAndQuery.TrimStart('/'));
223+
}
224+
catch (Exception ex)
225+
{
226+
throw new DeleteWorklogException(issueId, worklogId, ex);
227+
}
228+
194229
await CheckHttpResponseForErrorMessages(response);
195230
}
196231

@@ -213,7 +248,20 @@ public async Task UpdateWorkLog(string issueKey, long worklogId, DateOnly day, i
213248
Comment: comment
214249
);
215250

216-
HttpResponseMessage response = await _httpClient.PutAsJsonAsync(uriBuilder.Uri.PathAndQuery, request);
251+
HttpResponseMessage response;
252+
try
253+
{
254+
response = await _httpClient.PutAsJsonAsync(uriBuilder.Uri.PathAndQuery.TrimStart('/'), request);
255+
}
256+
catch (Exception ex)
257+
{
258+
throw new UpdateWorklogException(issueKey, worklogId, day.ToDateTime(TimeOnly.MinValue), timeSpentSeconds, ex)
259+
{
260+
Activity = activity,
261+
Comment = comment
262+
};
263+
}
264+
217265
await CheckHttpResponseForErrorMessages(response);
218266
}
219267

@@ -225,6 +273,14 @@ public async Task UpdateWorkLog(string issueKey, long worklogId, DateOnly day, i
225273
Query = new UriQueryBuilder()
226274
.Add(@"username", UserName)
227275
};
228-
return await _httpClient.GetAsJsonAsync<api.rest.common.JiraUserInfo>(uriBuilder.Uri.PathAndQuery);
276+
277+
try
278+
{
279+
return await _httpClient.GetAsJsonAsync<api.rest.common.JiraUserInfo>(uriBuilder.Uri.PathAndQuery.TrimStart('/'));
280+
}
281+
catch (Exception ex)
282+
{
283+
throw new JiraClientException($"Error retrieving user {UserName} info", ex);
284+
}
229285
}
230286
}

‎jwl.wadl/WadlParameter.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ public class WadlParameter
2020

2121
[XmlAttribute("style")]
2222
public string? Style { get; set; }
23-
23+
2424
[XmlAttribute("type")]
2525
public string? Type { get; set; }
2626

‎jwl.wadl/jwl.wadl.csproj

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,28 @@
66
<OutputType>Library</OutputType>
77
</PropertyGroup>
88

9+
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
10+
<DebugType>none</DebugType>
11+
<DebugSymbols>false</DebugSymbols>
12+
</PropertyGroup>
13+
14+
<ItemGroup>
15+
<PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="*">
16+
<PrivateAssets>all</PrivateAssets>
17+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
18+
</PackageReference>
19+
<PackageReference Include="StyleCop.Analyzers" Version="*">
20+
<PrivateAssets>all</PrivateAssets>
21+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
22+
</PackageReference>
23+
<Compile Include="..\GlobalSuppressions.cs" Link="GlobalSuppressions.cs" />
24+
</ItemGroup>
25+
26+
<Target Name="PostClean" AfterTargets="Clean">
27+
<RemoveDir Directories="$(BaseIntermediateOutputPath)" />
28+
<!-- obj -->
29+
<RemoveDir Directories="$(BaseOutputPath)" />
30+
<!-- bin -->
31+
</Target>
32+
933
</Project>

0 commit comments

Comments
 (0)