Skip to content
This repository was archived by the owner on Feb 13, 2025. It is now read-only.

Commit ac2e2ea

Browse files
authored
Improve exceptions (#9)
1 parent b212763 commit ac2e2ea

File tree

5 files changed

+91
-54
lines changed

5 files changed

+91
-54
lines changed

Bindle.Tests/Integration.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ public async Task CanYankInvoice()
106106
{
107107
var client = new BindleClient(DEMO_SERVER_URL);
108108
await client.YankInvoice("your/fancy/bindle/0.3.0"); // TODO: use one that doesn't conflict with CanFetchInvoice (because Xunit parallelisation)
109-
await Assert.ThrowsAsync<System.Net.WebException>(async () => {
109+
await Assert.ThrowsAsync<BindleYankedException>(async () => {
110110
await client.GetInvoice("your/fancy/bindle/0.3.0");
111111
});
112112
var invoice = await client.GetInvoice("your/fancy/bindle/0.3.0", IncludeYanked);

Bindle/BindleClient.cs

Lines changed: 35 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,20 @@ public async Task<Invoice> GetInvoice(string invoiceId, GetInvoiceOptions option
4040
var query = GetInvoiceQueryString(options);
4141
var uri = new Uri(_baseUri, $"{INVOICE_PATH}/{invoiceId}{query}");
4242
var response = await _httpClient.GetAsync(uri);
43-
if (response == null)
44-
{
45-
throw new Exception("No response from Bindle server");
46-
}
47-
if (response.StatusCode != HttpStatusCode.OK)
43+
ExpectResponseCode(response, HttpStatusCode.OK, HttpStatusCode.Forbidden);
44+
45+
if (response.StatusCode == HttpStatusCode.Forbidden)
4846
{
49-
throw new System.Net.WebException($"Bindle server returned status code {response.StatusCode}");
47+
if ((options & GetInvoiceOptions.IncludeYanked) == 0)
48+
{
49+
throw new BindleYankedException();
50+
}
51+
else
52+
{
53+
throw new BindleProtocolException($"Bindle server returned status code {response.StatusCode}", response);
54+
}
5055
}
56+
5157
var toml = await ReadResponseToml(response);
5258
return Parser.ParseInvoice(toml);
5359
}
@@ -56,49 +62,32 @@ public async Task<CreateInvoiceResult> CreateInvoice(Invoice invoice)
5662
{
5763
var invoiceToml = InvoiceWriter.Write(invoice);
5864

59-
if (invoiceToml == null)
60-
{
61-
throw new Exception("Error serialising invoice to TOML");
62-
}
63-
6465
var uri = new Uri(_baseUri, INVOICE_PATH);
6566
var requestContent = new StringContent(invoiceToml, null, "application/toml");
6667
if (requestContent.Headers.ContentType != null)
6768
{
6869
requestContent.Headers.ContentType.CharSet = null; // The Bindle server is VERY strict about the contents of the Content-Type header
6970
}
7071
var response = await _httpClient.PostAsync(uri, requestContent);
72+
ExpectResponseCode(response, HttpStatusCode.Created, HttpStatusCode.Accepted);
7173

72-
if (response == null)
73-
{
74-
throw new Exception("No response from Bindle server");
75-
}
76-
if (response.StatusCode != HttpStatusCode.Accepted && response.StatusCode != HttpStatusCode.Created)
77-
{
78-
throw new System.Net.WebException($"Bindle server returned status code {response.StatusCode}");
79-
}
8074
var toml = await ReadResponseToml(response);
8175
return Parser.ParseCreateInvoiceResult(toml);
8276
}
8377

8478
public async Task YankInvoice(string invoiceId)
8579
{
8680
var uri = new Uri(_baseUri, $"{INVOICE_PATH}/{invoiceId}");
87-
await _httpClient.DeleteAsync(uri);
81+
var response = await _httpClient.DeleteAsync(uri);
82+
ExpectResponseCode(response, HttpStatusCode.OK);
8883
}
8984

9085
public async Task<HttpContent> GetParcel(string invoiceId, string parcelId)
9186
{
9287
var uri = new Uri(_baseUri, $"{INVOICE_PATH}/{invoiceId}@{parcelId}");
9388
var response = await _httpClient.GetAsync(uri);
94-
if (response == null)
95-
{
96-
throw new Exception("No response from Bindle server");
97-
}
98-
if (response.StatusCode != HttpStatusCode.OK)
99-
{
100-
throw new System.Net.WebException($"Bindle server returned status code {response.StatusCode}");
101-
}
89+
ExpectResponseCode(response, HttpStatusCode.OK);
90+
10291
return response.Content;
10392
}
10493

@@ -120,21 +109,16 @@ public async Task CreateParcel(string invoiceId, string parcelId, byte[] content
120109
public async Task CreateParcel(string invoiceId, string parcelId, HttpContent content)
121110
{
122111
var uri = new Uri(_baseUri, $"{INVOICE_PATH}/{invoiceId}@{parcelId}");
123-
await _httpClient.PostAsync(uri, content);
112+
var response = await _httpClient.PostAsync(uri, content);
113+
ExpectResponseCode(response, HttpStatusCode.OK, HttpStatusCode.Created);
124114
}
125115

126116
public async Task<IEnumerable<Label>> ListMissingParcels(string invoiceId)
127117
{
128118
var uri = new Uri(_baseUri, $"{RELATIONSHIP_PATH}/missing/{invoiceId}");
129119
var response = await _httpClient.GetAsync(uri);
130-
if (response == null)
131-
{
132-
throw new Exception("No response from Bindle server");
133-
}
134-
if (response.StatusCode != HttpStatusCode.OK)
135-
{
136-
throw new System.Net.WebException($"Bindle server returned status code {response.StatusCode}");
137-
}
120+
ExpectResponseCode(response, HttpStatusCode.OK);
121+
138122
var toml = await ReadResponseToml(response);
139123
return Parser.ParseMissingLabels(toml);
140124
}
@@ -148,18 +132,30 @@ private static string SlashSafe(string uri)
148132
return uri + '/';
149133
}
150134

135+
private static void ExpectResponseCode(HttpResponseMessage response, params HttpStatusCode[] codes)
136+
{
137+
if (response == null)
138+
{
139+
throw new NoResponseException();
140+
}
141+
if (!codes.Contains(response.StatusCode))
142+
{
143+
throw new BindleProtocolException($"Bindle server returned status code {response.StatusCode}", response);
144+
}
145+
}
146+
151147
private async static Task<TomlTable> ReadResponseToml(HttpResponseMessage response)
152148
{
153149
var responseText = await response.Content.ReadAsStringAsync();
154150
var responseToml = Tomlyn.Toml.Parse(responseText);
155151
if (responseToml == null)
156152
{
157-
throw new Exception("Empty response from Bindle server");
153+
throw new ResponseContentException("Empty response from Bindle server");
158154
}
159155
if (responseToml.HasErrors)
160156
{
161157
var errors = String.Join(", ", responseToml.Diagnostics.Select(d => d.Message));
162-
throw new Exception($"Invalid response from Bindle server: {errors}");
158+
throw new ResponseContentException($"Invalid response from Bindle server: {errors}");
163159
}
164160
return Tomlyn.Toml.ToModel(responseToml);
165161
}

Bindle/Exceptions.cs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
using System;
2+
using System.Net;
3+
using System.Net.Http;
4+
using System.Threading.Tasks;
5+
6+
namespace Bindle
7+
{
8+
public class BindleProtocolException: Exception
9+
{
10+
public BindleProtocolException(string message, HttpResponseMessage response): base(message)
11+
{
12+
Response = response;
13+
}
14+
15+
public HttpResponseMessage Response { get; }
16+
17+
public HttpStatusCode StatusCode => Response.StatusCode;
18+
public async Task<string?> ResponseText() => await Response.Content.ReadAsStringAsync();
19+
}
20+
21+
public class ResponseContentException: Exception
22+
{
23+
public ResponseContentException(string message): base(message)
24+
{
25+
}
26+
}
27+
28+
public class BindleYankedException: Exception
29+
{
30+
public BindleYankedException(): base("Server returned Forbidden; bindle may have been yanked")
31+
{
32+
}
33+
}
34+
35+
public class NoResponseException: Exception
36+
{
37+
public NoResponseException(): base("No response from Bindle server")
38+
{
39+
}
40+
}
41+
}

Bindle/InvoiceWriter.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ internal static string TomliseSatisfiedBy(SatisfiedBy value)
116116
SatisfiedBy.AllOf => "allOf",
117117
SatisfiedBy.OneOf => "oneOf",
118118
SatisfiedBy.Optional => "optional",
119-
_ => throw new Exception($"Unknown SatisfiedBy value {value}"),
119+
_ => throw new InvalidOperationException($"Unknown SatisfiedBy value {value}"),
120120
};
121121
}
122122
}

Bindle/Parser.cs

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ internal static Invoice ParseInvoice(TomlTable toml)
1313
var bindleVersion = toml.GetString("bindleVersion");
1414
if (bindleVersion != "1.0.0")
1515
{
16-
throw new System.Exception($"Unknown Bindle version {bindleVersion}");
16+
throw new ResponseContentException($"Unknown Bindle version {bindleVersion}");
1717
}
1818

1919
var yanked = toml.TryGetBool("yanked") ?? false;
@@ -165,7 +165,7 @@ internal static class TomlHelpers
165165
}
166166
else
167167
{
168-
throw new System.Exception($"Invalid field {key}: expected string but got ${t.Kind.ToString()}");
168+
throw new ResponseContentException($"Invalid field {key}: expected string but got ${t.Kind.ToString()}");
169169
}
170170
}
171171
return null;
@@ -181,7 +181,7 @@ internal static class TomlHelpers
181181
}
182182
else
183183
{
184-
throw new System.Exception($"Invalid field {key}: expected boolean but got ${t.Kind.ToString()}");
184+
throw new ResponseContentException($"Invalid field {key}: expected boolean but got ${t.Kind.ToString()}");
185185
}
186186
}
187187
return null;
@@ -198,7 +198,7 @@ internal static class TomlHelpers
198198
}
199199
else
200200
{
201-
throw new System.Exception($"Invalid field {key}: expected string array but got ${t.Kind.ToString()}");
201+
throw new ResponseContentException($"Invalid field {key}: expected string array but got ${t.Kind.ToString()}");
202202
}
203203
}
204204
return null;
@@ -214,7 +214,7 @@ internal static class TomlHelpers
214214
}
215215
else
216216
{
217-
throw new System.Exception($"Invalid field {key}: expected section but got ${t.Kind.ToString()}");
217+
throw new ResponseContentException($"Invalid field {key}: expected section but got ${t.Kind.ToString()}");
218218
}
219219
}
220220
return null;
@@ -230,7 +230,7 @@ internal static class TomlHelpers
230230
}
231231
else
232232
{
233-
throw new System.Exception($"Invalid field {key}: expected sections but got ${t.Kind.ToString()}");
233+
throw new ResponseContentException($"Invalid field {key}: expected sections but got ${t.Kind.ToString()}");
234234
}
235235
}
236236
return null;
@@ -242,7 +242,7 @@ internal static TomlObject Get(this TomlTable toml, string key)
242242
{
243243
return t;
244244
}
245-
throw new System.Exception($"Missing field {key}");
245+
throw new ResponseContentException($"Missing field {key}");
246246
}
247247

248248
internal static string GetString(this TomlTable toml, string key)
@@ -255,10 +255,10 @@ internal static string GetString(this TomlTable toml, string key)
255255
}
256256
else
257257
{
258-
throw new System.Exception($"Invalid field {key}: expected string but got ${t.Kind.ToString()}");
258+
throw new ResponseContentException($"Invalid field {key}: expected string but got ${t.Kind.ToString()}");
259259
}
260260
}
261-
throw new System.Exception($"Missing field {key}");
261+
throw new ResponseContentException($"Missing field {key}");
262262
}
263263

264264
internal static long GetLong(this TomlTable toml, string key)
@@ -271,10 +271,10 @@ internal static long GetLong(this TomlTable toml, string key)
271271
}
272272
else
273273
{
274-
throw new System.Exception($"Invalid field {key}: expected integer but got ${t.Kind.ToString()}");
274+
throw new ResponseContentException($"Invalid field {key}: expected integer but got ${t.Kind.ToString()}");
275275
}
276276
}
277-
throw new System.Exception($"Missing field {key}");
277+
throw new ResponseContentException($"Missing field {key}");
278278
}
279279

280280
internal static TomlTable GetTomlTable(this TomlTable toml, string key)
@@ -287,10 +287,10 @@ internal static TomlTable GetTomlTable(this TomlTable toml, string key)
287287
}
288288
else
289289
{
290-
throw new System.Exception($"Invalid field {key}: expected section but got ${t.Kind.ToString()}");
290+
throw new ResponseContentException($"Invalid field {key}: expected section but got ${t.Kind.ToString()}");
291291
}
292292
}
293-
throw new System.Exception($"Missing section {key}");
293+
throw new ResponseContentException($"Missing section {key}");
294294
}
295295
}
296296
}

0 commit comments

Comments
 (0)