Skip to content

Commit

Permalink
fix(errorHandling): fix innerException handling (#383)
Browse files Browse the repository at this point in the history
Refs: CPLP-3590
  • Loading branch information
Phil91 authored Dec 14, 2023
1 parent 6f131c2 commit fb41bc4
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -132,20 +132,20 @@ private static (HttpStatusCode StatusCode, Func<Exception, (string?, IEnumerable
return (statusCode, messageFunc, logLevel);
}

private ErrorResponse CreateErrorResponse(HttpStatusCode statusCode, Exception error, string errorId, string message, IEnumerable<ErrorDetails>? details, Func<Exception, (string?, IEnumerable<string>)>? getSourceAndMessages = null)
private ErrorResponse CreateErrorResponse(HttpStatusCode statusCode, Exception error, string errorId, string message, IEnumerable<ErrorDetails>? details, Func<Exception, (string?, IEnumerable<string>)>? getSourceAndMessages)
{
var meta = Metadata.GetValueOrDefault(statusCode, Metadata[HttpStatusCode.InternalServerError]);
var (source, messages) = getSourceAndMessages?.Invoke(error) ?? (error.Source, Enumerable.Repeat(message, 1));

var messageMap = new Dictionary<string, IEnumerable<string>> { { source ?? "unknown", messages } };
while (error.InnerException != null)
{
var inner = error.InnerException;
source = inner.Source ?? "inner";
error = error.InnerException;
source = error.Source ?? "inner";

messageMap[source] = messageMap.TryGetValue(source, out messages)
? messages.Append(GetErrorMessage(inner))
: Enumerable.Repeat(GetErrorMessage(inner), 1);
? messages.Append(GetErrorMessage(error))
: Enumerable.Repeat(GetErrorMessage(error), 1);
}

return new ErrorResponse(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,53 @@ public async Task Invoke_WithDetailedException()
x.Parameters.First(p => p.Name == "second").Value == "bar");
}

[Fact]
public async Task Invoke_WithInnerException()
{
// Arrange
var expectedException = ServiceException.Create(TestErrors.FIRST_ERROR, new ErrorParameter[] { new("first", "foo"), new("second", "bar") }, new ForbiddenException("You don't have access to this resource", new UnauthorizedAccessException("No access")));
Task MockNextMiddleware(HttpContext _) => Task.FromException(expectedException);
using var body = new MemoryStream();
var httpContext = new DefaultHttpContext();
httpContext.Response.Body = body;

var mockLogger = A.Fake<IMockLogger<GeneralHttpErrorHandler>>();
var logger = new MockLogger<GeneralHttpErrorHandler>(mockLogger);

var errorMessageService = A.Fake<IErrorMessageService>();
A.CallTo(() => errorMessageService.GetMessage(A<Type>._, A<int>._))
.ReturnsLazily((Type type, int code) => $"type: {type.Name} code: {code} first: {{first}} second: {{second}}");

var generalHttpErrorHandler = new GeneralHttpErrorHandler(MockNextMiddleware, logger, errorMessageService);

// Act
await generalHttpErrorHandler.Invoke(httpContext);

// Assert
((HttpStatusCode)httpContext.Response.StatusCode).Should().Be(HttpStatusCode.BadGateway);
A.CallTo(() => mockLogger.Log(
A<LogLevel>.That.IsEqualTo(LogLevel.Information),
expectedException,
A<string>.That.Matches(x =>
x.StartsWith("GeneralErrorHandler caught ServiceException with errorId:") &&
x.EndsWith("resulting in response status code 502, message 'type: TestErrors code: 1 first: foo second: bar'"))))
.MustHaveHappenedOnceExactly();

body.Position = 0;
var errorResponse = JsonSerializer.Deserialize<ErrorResponse>(body, Options);
errorResponse.Should().NotBeNull().And.BeOfType<ErrorResponse>();
errorResponse!.Errors.Should().HaveCount(2).And.Satisfy(
x => x.Key == "System.Private.CoreLib",
x => x.Key == "inner");
errorResponse.Details.Should().ContainSingle().Which.Should().Match<ErrorDetails>(x =>
x.Type == "TestErrors" &&
x.ErrorCode == "FIRST_ERROR" &&
x.Message == "type: TestErrors code: 1 first: {first} second: {second}" &&
x.Parameters.Count() == 2 &&
x.Parameters.First(p => p.Name == "first").Value == "foo" &&
x.Parameters.First(p => p.Name == "second").Value == "bar");
}

private enum TestErrors
{
FIRST_ERROR = 1,
Expand Down

0 comments on commit fb41bc4

Please sign in to comment.