Skip to content

Commit a5cb421

Browse files
author
Matteo Bortolazzo
authored
Merge pull request matteobortolazzo#45 from matteobortolazzo/ExceptionHandling
Exception handling
2 parents a17c258 + e10800b commit a5cb421

File tree

7 files changed

+156
-68
lines changed

7 files changed

+156
-68
lines changed

src/CouchDB.Driver/CouchQueryProvider.cs

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
using System.Linq;
1010
using System.Linq.Expressions;
1111
using System.Reflection;
12+
using System.Runtime.ExceptionServices;
1213

1314
namespace CouchDB.Driver
1415
{
@@ -37,14 +38,22 @@ public override object Execute(Expression expression, bool completeResponse)
3738
// Remove from the expressions tree all IQueryable methods not supported by CouchDB and put them into the list
3839
var unsupportedMethodCallExpressions = new List<MethodCallExpression>();
3940
expression = RemoveUnsupportedMethodExpressions(expression, out var hasUnsupportedMethods, unsupportedMethodCallExpressions);
40-
41+
4142
var body = Translate(ref expression);
4243
Type elementType = TypeSystem.GetElementType(expression.Type);
4344

4445
// Create generic GetCouchList method and invoke it, sending the request to CouchDB
4546
MethodInfo method = typeof(CouchQueryProvider).GetMethod(nameof(CouchQueryProvider.GetCouchList));
4647
MethodInfo generic = method.MakeGenericMethod(elementType);
47-
var result = generic.Invoke(this, new[] { body });
48+
object result = null;
49+
try
50+
{
51+
result = generic.Invoke(this, new[] { body });
52+
}
53+
catch (TargetInvocationException ex)
54+
{
55+
ExceptionDispatchInfo.Capture(ex.InnerException).Throw();
56+
}
4857

4958
// If no unsupported methods, return the result
5059
if (!hasUnsupportedMethods)
@@ -73,7 +82,7 @@ private string Translate(ref Expression e)
7382
}
7483

7584
public object GetCouchList<T>(string body)
76-
{
85+
{
7786
FindResult<T> result = _flurlClient
7887
.Request(_connectionString)
7988
.AppendPathSegments(_db, "_find")
@@ -82,7 +91,7 @@ public object GetCouchList<T>(string body)
8291
.SendRequest();
8392

8493
var couchList = new CouchList<T>(result.Docs.ToList(), result.Bookmark, result.ExecutionStats);
85-
return couchList;
94+
return couchList;
8695
}
8796

8897
private Expression RemoveUnsupportedMethodExpressions(Expression expression, out bool hasUnsupportedMethods, IList<MethodCallExpression> unsupportedMethodCallExpressions)
@@ -177,7 +186,7 @@ MethodInfo FindEnumerableMethod()
177186
throw;
178187
}
179188
}
180-
189+
181190
private object GetArgumentValueFromExpression(Expression e)
182191
{
183192
if (e is ConstantExpression c)
@@ -190,7 +199,7 @@ private object GetArgumentValueFromExpression(Expression e)
190199
}
191200
throw new NotImplementedException($"Expression of type {e.NodeType} not supported.");
192201
}
193-
202+
194203
private static MethodInfo FindEnumerableMinMax(MethodInfo queryableMethodInfo)
195204
{
196205
Type[] genericParams = queryableMethodInfo.GetGenericArguments();
@@ -203,6 +212,6 @@ private static MethodInfo FindEnumerableMinMax(MethodInfo queryableMethodInfo)
203212
enumerableMethodInfo.ReturnType == genericParams[1];
204213
});
205214
return finalMethodInfo;
206-
}
215+
}
207216
}
208217
}

src/CouchDB.Driver/Exceptions/CouchConflictException.cs

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,14 @@
1-
namespace CouchDB.Driver.Exceptions
1+
using CouchDB.Driver.DTOs;
2+
using System;
3+
4+
namespace CouchDB.Driver.Exceptions
25
{
36
/// <summary>
47
/// The exception that is thrown when there is a conflict.
58
/// </summary>
69
public class CouchConflictException : CouchException
710
{
8-
/// <summary>
9-
/// Creates a new instance of CouchConflictException.
10-
/// </summary>
11-
/// <param name="message">Error message</param>
12-
/// <param name="reason">Error reason</param>
13-
public CouchConflictException(string message, string reason) : base(message, reason) { }
11+
internal CouchConflictException(CouchError couchError, Exception innerException) : base(couchError, innerException) { }
1412

1513
public CouchConflictException()
1614
{
@@ -20,7 +18,7 @@ public CouchConflictException(string message) : base(message)
2018
{
2119
}
2220

23-
public CouchConflictException(string message, System.Exception innerException) : base(message, innerException)
21+
public CouchConflictException(string message, Exception innerException) : base(message, innerException)
2422
{
2523
}
2624
}

src/CouchDB.Driver/Exceptions/CouchException.cs

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System;
1+
using CouchDB.Driver.DTOs;
2+
using System;
23

34
namespace CouchDB.Driver.Exceptions
45
{
@@ -12,18 +13,31 @@ public class CouchException : Exception
1213
/// </summary>
1314
/// <param name="message">Error message</param>
1415
/// <param name="reason">Error reason</param>
15-
public CouchException(string message, string reason) : base(message, new Exception(reason)) { }
16+
public CouchException(string message, string reason) : this(message, reason, null)
17+
{
18+
}
19+
20+
public CouchException() : this(null, null, null)
21+
{
22+
}
1623

17-
public CouchException()
24+
public CouchException(string message) : this(message, null, null)
1825
{
1926
}
2027

21-
public CouchException(string message) : base(message)
28+
public CouchException(string message, Exception innerException) : this(message, null, innerException)
2229
{
2330
}
2431

25-
public CouchException(string message, Exception innerException) : base(message, innerException)
32+
internal CouchException(CouchError couchError, Exception innerException) : this(couchError?.Error, couchError?.Reason, innerException)
2633
{
2734
}
35+
36+
public CouchException(string message, string reason, Exception innerException) : base(message, innerException)
37+
{
38+
Reason = reason;
39+
}
40+
41+
public string Reason { get; }
2842
}
2943
}

src/CouchDB.Driver/Exceptions/CouchNoIndexException.cs

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,14 @@
1-
namespace CouchDB.Driver.Exceptions
1+
using CouchDB.Driver.DTOs;
2+
using System;
3+
4+
namespace CouchDB.Driver.Exceptions
25
{
36
/// <summary>
47
/// The exception that is thrown when there is no index for the query.
58
/// </summary>
69
public class CouchNoIndexException : CouchException
710
{
8-
/// <summary>
9-
/// Creates a new instance of CouchNoIndexException.
10-
/// </summary>
11-
/// <param name="message">Error message</param>
12-
/// <param name="reason">Error reason</param>
13-
public CouchNoIndexException(string message, string reason) : base(message, reason) { }
11+
internal CouchNoIndexException(CouchError couchError, Exception innerException) : base(couchError, innerException) { }
1412

1513
public CouchNoIndexException()
1614
{
@@ -20,7 +18,7 @@ public CouchNoIndexException(string message) : base(message)
2018
{
2119
}
2220

23-
public CouchNoIndexException(string message, System.Exception innerException) : base(message, innerException)
21+
public CouchNoIndexException(string message, Exception innerException) : base(message, innerException)
2422
{
2523
}
2624
}

src/CouchDB.Driver/Exceptions/CouchNotFoundException.cs

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,14 @@
1-
namespace CouchDB.Driver.Exceptions
1+
using CouchDB.Driver.DTOs;
2+
using System;
3+
4+
namespace CouchDB.Driver.Exceptions
25
{
36
/// <summary>
47
/// The exception that is thrown when something is not found.
58
/// </summary>
69
public class CouchNotFoundException : CouchException
710
{
8-
/// <summary>
9-
/// Creates a new instance of CouchNotFoundException.
10-
/// </summary>
11-
/// <param name="message">Error message</param>
12-
/// <param name="reason">Error reason</param>
13-
public CouchNotFoundException(string message, string reason) : base(message, reason) { }
11+
internal CouchNotFoundException(CouchError couchError, Exception innerException) : base(couchError, innerException) { }
1412

1513
public CouchNotFoundException()
1614
{
@@ -20,7 +18,7 @@ public CouchNotFoundException(string message) : base(message)
2018
{
2119
}
2220

23-
public CouchNotFoundException(string message, System.Exception innerException) : base(message, innerException)
21+
public CouchNotFoundException(string message, Exception innerException) : base(message, innerException)
2422
{
2523
}
2624
}

src/CouchDB.Driver/Helpers/RequestsHelper.cs

Lines changed: 13 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -23,41 +23,25 @@ public static async Task<T> SendRequestAsync<T>(this Task<T> asyncRequest)
2323
}
2424
catch (FlurlHttpException ex)
2525
{
26-
CouchError couchError;
27-
try
28-
{
29-
couchError = await ex.GetResponseJsonAsync<CouchError>().ConfigureAwait(false);
30-
}
31-
catch
26+
CouchError couchError = await ex.GetResponseJsonAsync<CouchError>().ConfigureAwait(false);
27+
28+
if (couchError == null)
3229
{
33-
throw;
30+
couchError = new CouchError();
3431
}
3532

36-
if (couchError != null)
33+
switch (ex.Call.HttpStatus)
3734
{
38-
switch (ex.Call.HttpStatus)
39-
{
40-
case HttpStatusCode.Conflict:
41-
throw couchError.NewCouchExteption(typeof(CouchConflictException));
42-
case HttpStatusCode.NotFound:
43-
throw couchError.NewCouchExteption(typeof(CouchNotFoundException));
44-
case HttpStatusCode.BadRequest:
45-
if (couchError.Error == "no_usable_index")
46-
{
47-
throw couchError.NewCouchExteption(typeof(CouchNoIndexException));
48-
}
49-
break;
50-
}
35+
case HttpStatusCode.Conflict:
36+
throw new CouchConflictException(couchError, ex);
37+
case HttpStatusCode.NotFound:
38+
throw new CouchNotFoundException(couchError, ex);
39+
case HttpStatusCode.BadRequest when couchError.Error == "no_usable_index":
40+
throw new CouchNoIndexException(couchError, ex);
41+
default:
42+
throw new CouchException(couchError, ex);
5143
}
52-
throw new CouchException(couchError.Error, couchError.Reason);
5344
}
5445
}
55-
56-
private static Exception NewCouchExteption(this CouchError e, Type type)
57-
{
58-
ConstructorInfo ctor = type.GetConstructor(new[] { typeof(string), typeof(string) });
59-
var exception = (CouchException)ctor.Invoke(new string[] { e.Error, e.Reason });
60-
return exception;
61-
}
6246
}
6347
}

tests/CouchDB.Driver.UnitTests/Client_Tests.cs

Lines changed: 90 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1-
using CouchDB.Driver.Types;
1+
using CouchDB.Driver.Exceptions;
2+
using CouchDB.Driver.Types;
23
using CouchDB.Driver.UnitTests.Models;
34
using Flurl.Http.Testing;
45
using System.Collections.Generic;
6+
using System.Linq;
7+
using System.Net;
58
using System.Net.Http;
69
using System.Threading.Tasks;
710
using Xunit;
@@ -110,7 +113,7 @@ public async Task IsUp()
110113

111114
using (var client = new CouchClient("http://localhost"))
112115
{
113-
var result = await client.IsUpAsync();
116+
var result = await client.IsUpAsync();
114117
Assert.True(result);
115118
}
116119
}
@@ -127,7 +130,7 @@ public async Task IsNotUp()
127130

128131
using (var client = new CouchClient("http://localhost"))
129132
{
130-
httpTest.RespondWith("Not found", 404);
133+
httpTest.RespondWith("Not found", 404);
131134
var result = await client.IsUpAsync();
132135
Assert.False(result);
133136
}
@@ -175,5 +178,89 @@ public async Task ActiveTasks()
175178
}
176179

177180
#endregion
181+
182+
#region Error Handling
183+
[Fact]
184+
public async Task ConflictException()
185+
{
186+
using (var httpTest = new HttpTest())
187+
{
188+
httpTest.RespondWith(status: (int)HttpStatusCode.Conflict);
189+
190+
using (var client = new CouchClient("http://localhost"))
191+
{
192+
var couchException = await Assert.ThrowsAsync<CouchConflictException>(() => client.CreateDatabaseAsync<Rebel>());
193+
Assert.IsType<Flurl.Http.FlurlHttpException>(couchException.InnerException);
194+
}
195+
}
196+
}
197+
198+
[Fact]
199+
public async Task NotFoundException()
200+
{
201+
using (var httpTest = new HttpTest())
202+
{
203+
httpTest.RespondWith(status: (int)HttpStatusCode.NotFound);
204+
205+
using (var client = new CouchClient("http://localhost"))
206+
{
207+
var couchException = await Assert.ThrowsAsync<CouchNotFoundException>(() => client.DeleteDatabaseAsync<Rebel>());
208+
Assert.IsType<Flurl.Http.FlurlHttpException>(couchException.InnerException);
209+
}
210+
}
211+
}
212+
213+
[Fact]
214+
public void BadRequestException()
215+
{
216+
using (var httpTest = new HttpTest())
217+
{
218+
httpTest.RespondWith(@"{error: ""no_usable_index""}", (int)HttpStatusCode.BadRequest);
219+
220+
using (var client = new CouchClient("http://localhost"))
221+
{
222+
var db = client.GetDatabase<Rebel>();
223+
var couchException = Assert.Throws<CouchNoIndexException>(() => db.UseIndex("aoeu").ToList());
224+
Assert.IsType<Flurl.Http.FlurlHttpException>(couchException.InnerException);
225+
}
226+
}
227+
}
228+
229+
[Fact]
230+
public async Task GenericExceptionWithMessage()
231+
{
232+
using (var httpTest = new HttpTest())
233+
{
234+
string message = "message text";
235+
string reason = "reason text";
236+
httpTest.RespondWith($"{{error: \"{message}\", reason: \"{reason}\"}}", (int)HttpStatusCode.InternalServerError);
237+
238+
using (var client = new CouchClient("http://localhost"))
239+
{
240+
var db = client.GetDatabase<Rebel>();
241+
var couchException = await Assert.ThrowsAsync<CouchException>(() => db.FindAsync("aoeu"));
242+
Assert.Equal(message, couchException.Message);
243+
Assert.Equal(reason, couchException.Reason);
244+
Assert.IsType<Flurl.Http.FlurlHttpException>(couchException.InnerException);
245+
}
246+
}
247+
}
248+
249+
[Fact]
250+
public async Task GenericExceptionNoMessage()
251+
{
252+
using (var httpTest = new HttpTest())
253+
{
254+
httpTest.RespondWith(status: (int)HttpStatusCode.InternalServerError);
255+
256+
using (var client = new CouchClient("http://localhost"))
257+
{
258+
var db = client.GetDatabase<Rebel>();
259+
var couchException = await Assert.ThrowsAsync<CouchException>(() => db.FindAsync("aoeu"));
260+
Assert.IsType<Flurl.Http.FlurlHttpException>(couchException.InnerException);
261+
}
262+
}
263+
}
264+
#endregion
178265
}
179266
}

0 commit comments

Comments
 (0)