Skip to content
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
2fcf849
Added error and response header constants. (bunq/sdk_csharp#63)
Dec 30, 2017
811b35d
Added method to extract response id. (bunq/sdk_csharp#63)
Dec 30, 2017
aff6b20
Added response id field to base exception class. (bunq/sdk_csharp#63)
Dec 30, 2017
03912ab
Refactored the classes that extend ApiException to support the new ba…
Dec 30, 2017
60e18a5
Refactored exception factory to use the response id. (bunq/sdk_csharp…
Dec 30, 2017
e775766
Pass the response id to exception factory from ApiClient on failed re…
Dec 30, 2017
6eddd49
Added test to ensure that the response id is populated with data. (bu…
Dec 30, 2017
961715a
Made INVALID_USER_PERSON_ID a constant. (bunq/sdk_csharp#63)
Dec 30, 2017
4a42f7d
Fixed invalid string format. (bunq/sdk_csharp#63)
Dec 30, 2017
e59fcf3
Fixed typos in ResponseId. (bunq/sdk_csharp#63)
Dec 31, 2017
3d9c9de
Use assert throws in test. (bunq/sdk_csharp#63)
Jan 2, 2018
3d7bb5e
Fixed indentions. (bunq/sdk_csharp#63)
Jan 2, 2018
3698815
Use build in new line separator. (bunq/sdk_csharp#63)
Jan 2, 2018
7429ce0
Changed some method signatures for cleaner code. (bunq/sdk_csharp#63)
Jan 2, 2018
7cb55ae
Changed Ilist to IEnumerable. (bunq/sdk_csharp#63)
Jan 2, 2018
29daba5
Merge branch 'develop' into bunq/sdk_csharp#63-add-response-id-to-fai…
andrederoos Jan 2, 2018
e88274c
Added missing else and moved else if statement to new line. (bunq/sdk…
Jan 2, 2018
5a2a805
Merge branch 'develop' into bunq/sdk_csharp#63-add-response-id-to-fai…
andrederoos Jan 2, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .idea/.idea.BunqSdk/.idea/contentModel.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

31 changes: 31 additions & 0 deletions BunqSdk.Tests/Http/ResponseIdOnBadRequestTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using System;
using Bunq.Sdk.Context;
using Bunq.Sdk.Exception;
using Bunq.Sdk.Model.Generated.Endpoint;
using Xunit;

namespace Bunq.Sdk.Tests.Http
{
public class ResponseIdOnBadRequestTest : BunqSdkTestBase
{
/// <summary>
/// API context to use for the test API calls.
/// </summary>
private static readonly ApiContext API_CONTEXT = GetApiContext();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UpperCamelCase

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will be refactored in #58


/// <summary>
/// Invalid user id to trigger BadRequestException
/// </summary>
private const int INVALID_USER_PERSON_ID = 0;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UpperCamelCase

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will be refactored in #58


[Fact]
public void TestBadRequestWithResponseId()
{
var caughtException = Assert.Throws<BadRequestException>(
() => UserPerson.Get(API_CONTEXT, INVALID_USER_PERSON_ID)
);

Assert.NotNull(caughtException.ResponseId);
}
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Newline at EOF

6 changes: 5 additions & 1 deletion BunqSdk/Exception/ApiException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,16 @@ namespace Bunq.Sdk.Exception
public class ApiException : System.Exception
{
public int ResponseCode { get; private set; }
public string ResponseId { get; private set; }

/// <inheritdoc />
/// <param name="responseCode">The HTTP Response code of the failed request.</param>
/// <param name="message">The error message related to this exception.</param>
public ApiException(int responseCode, string message) : base(message)
/// <param name="responseId"></param>
protected ApiException(int responseCode, string message, string responseId) : base(message)
{
ResponseCode = responseCode;
ResponseId = responseId;
}
}
}
3 changes: 2 additions & 1 deletion BunqSdk/Exception/BadRequestException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
{
public class BadRequestException : ApiException
{
public BadRequestException(int responseCode, string message) : base(responseCode, message)
public BadRequestException(int responseCode, string message, string responseId)
: base(responseCode, message, responseId)
{
}
}
Expand Down
44 changes: 29 additions & 15 deletions BunqSdk/Exception/ExceptionFactory.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;

namespace Bunq.Sdk.Exception
{
/// <summary>
/// This class makes sure that the correct exception is thrown for the given response code.
/// </summary>
public class ExceptionFactory
{
/// <summary>
Expand All @@ -16,39 +20,49 @@ public class ExceptionFactory
private const int HTTP_RESPONSE_CODE_INTERNAL_SERVER_ERROR = 500;

/// <summary>
/// Glue to concatenate the error messages.
/// String format constants.
/// </summary>
private const string GLUE_ERROR_MESSAGES = "\n";
private const string FORMAT_ERROR_MESSAGE = "Response id to help bunq debug: {0}. \n Error message: {1}";

/// <returns>The exception that belongs to this status code.</returns>
public static ApiException CreateExceptionForResponse(int responseCode, IList<string> messages)
public static ApiException CreateExceptionForResponse(
int responseCode,
IEnumerable<string> messages,
string responseId
)
{
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this open brace go on the same line as the close parenthese?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is ? 😁

var errorMessage = ConcatenateMessages(messages);
var errorMessage = FormatExceptionMessage(messages, responseId);

switch (responseCode)
{
case HTTP_RESPONSE_CODE_BAD_REQUEST:
return new BadRequestException(responseCode, errorMessage);
return new BadRequestException(responseCode, errorMessage, responseId);
case HTTP_RESPONSE_CODE_UNAUTHORIZED:
return new UnauthorizedException(responseCode, errorMessage);
return new UnauthorizedException(responseCode, errorMessage, responseId);
case HTTP_RESPONSE_CODE_FORBIDDEN:
return new ForbiddenException(responseCode, errorMessage);
return new ForbiddenException(responseCode, errorMessage, responseId);
case HTTP_RESPONSE_CODE_NOT_FOUND:
return new NotFoundException(responseCode, errorMessage);
return new NotFoundException(responseCode, errorMessage, responseId);
case HTTP_RESPONSE_CODE_METHOD_NOT_ALLOWED:
return new MethodNotAllowedException(responseCode, errorMessage);
return new MethodNotAllowedException(responseCode, errorMessage, responseId);
case HTTP_RESPONSE_CODE_TOO_MANY_REQUESTS:
return new TooManyRequestsException(responseCode, errorMessage);
return new TooManyRequestsException(responseCode, errorMessage, responseId);
case HTTP_RESPONSE_CODE_INTERNAL_SERVER_ERROR:
return new PleaseContactBunqException(responseCode, errorMessage);
return new PleaseContactBunqException(responseCode, errorMessage, responseId);
default:
return new UnknownApiErrorException(responseCode, errorMessage);
return new UnknownApiErrorException(responseCode, errorMessage, responseId);
}
}

private static string ConcatenateMessages(IEnumerable<string> messages)
/// <summary>
/// Formats the exception message accordingly.
/// </summary>
private static string FormatExceptionMessage(IEnumerable<string> messages, string responseId)
{
return string.Join(GLUE_ERROR_MESSAGES, messages);
return string.Format(FORMAT_ERROR_MESSAGE,
responseId,
string.Join(Environment.NewLine, messages)
);
}
}
}
3 changes: 2 additions & 1 deletion BunqSdk/Exception/ForbiddenException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
{
public class ForbiddenException : ApiException
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Docs Same for other exception types

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code has not been modified in this pr. Please create a follow up issue for this. This would need to be refactored in the other SDK's as well.

{
public ForbiddenException(int responseCode, string message) : base(responseCode, message)
public ForbiddenException(int responseCode, string message, string responseId)
: base(responseCode, message, responseId)
{
}
}
Expand Down
3 changes: 2 additions & 1 deletion BunqSdk/Exception/MethodNotAllowedException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
{
public class MethodNotAllowedException : ApiException
{
public MethodNotAllowedException(int responseCode, string message) : base(responseCode, message)
public MethodNotAllowedException(int responseCode, string message, string responseId)
: base(responseCode, message, responseId)
{
}
}
Expand Down
3 changes: 2 additions & 1 deletion BunqSdk/Exception/NotFoundException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
{
public class NotFoundException : ApiException
{
public NotFoundException(int responseCode, string message) : base(responseCode, message)
public NotFoundException(int responseCode, string message, string responseId)
: base(responseCode, message, responseId)
{
}
}
Expand Down
3 changes: 2 additions & 1 deletion BunqSdk/Exception/PleaseContactBunqException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
{
public class PleaseContactBunqException : ApiException
{
public PleaseContactBunqException(int responseCode, string message) : base(responseCode, message)
public PleaseContactBunqException(int responseCode, string message, string responseId)
: base(responseCode, message, responseId)
{
}
}
Expand Down
3 changes: 2 additions & 1 deletion BunqSdk/Exception/TooManyRequestsException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
{
public class TooManyRequestsException : ApiException
{
public TooManyRequestsException(int responseCode, string message) : base(responseCode, message)
public TooManyRequestsException(int responseCode, string message, string responseId)
: base(responseCode, message, responseId)
{
}
}
Expand Down
3 changes: 2 additions & 1 deletion BunqSdk/Exception/UnauthorizedException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
{
public class UnauthorizedException : ApiException
{
public UnauthorizedException(int responseCode, string message) : base(responseCode, message)
public UnauthorizedException(int responseCode, string message, string responseId)
: base(responseCode, message, responseId)
{
}
}
Expand Down
3 changes: 2 additions & 1 deletion BunqSdk/Exception/UnknownApiErrorException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
/// </summary>
public class UnknownApiErrorException : ApiException
{
public UnknownApiErrorException(int responseCode, string message) : base(responseCode, message)
public UnknownApiErrorException(int responseCode, string message, string responseId)
: base(responseCode, message, responseId)
{
}
}
Expand Down
48 changes: 44 additions & 4 deletions BunqSdk/Http/ApiClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ namespace Bunq.Sdk.Http
{
public class ApiClient
{

/// <summary>
/// Error constatns.
/// </summary>
private static string ERROR_COULD_NOT_DETERMINE_RESPONSE_ID_HEADER =
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UpperCamelCase

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will be refactored in #58

"The response header \"X-Bunq-Client-Response-Id\" or \"x-bunq-client-response-id\" could not be found.";

/// <summary>
/// Endpoints not requiring active session for the request to succeed.
Expand Down Expand Up @@ -43,6 +49,8 @@ public class ApiClient
private const string HEADER_GEOLOCATION = "X-Bunq-Geolocation";
private const string HEADER_SIGNATURE = "X-Bunq-Client-Signature";
private const string HEADER_AUTHENTICATION = "X-Bunq-Client-Authentication";
private static string HEADER_RESPONSE_ID_LOWER_CASE = "x-bunq-client-response-id";
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UpperCamelCase

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will be refactored in #58

private static string HEADER_RESPONSE_ID_UPPER_CASE = "X-Bunq-Client-Response-Id";

/// <summary>
/// Field constants.
Expand Down Expand Up @@ -282,18 +290,50 @@ private static void AssertResponseSuccess(HttpResponseMessage responseMessage)
var responseCode = (int) responseMessage.StatusCode;
var responseBody = responseMessage.Content.ReadAsStringAsync().Result;

throw CreateApiExceptionRequestUnsuccessful(responseCode, responseBody);
throw CreateApiExceptionRequestUnsuccessful(
responseCode,
responseBody,
DetermineResponseIdByAllHeader(responseMessage.Headers)
);
}

private static string DetermineResponseIdByAllHeader(HttpHeaders allHeader)
{
if (allHeader.Contains(HEADER_RESPONSE_ID_UPPER_CASE))

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe a method could be extracted here to avoid the conditional expressions.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I cant seem to picture how an extracted method will prevent these conditional lines 🤔. Could you please provide a snippet with what you mean ?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@OGKevin something like:

 private static string GetResponseId(HttpHeaders allHeader, IEnumerable<string> allHeaderResponse)
        {
            foreach (var headerResponse in allHeaderResponse)
            {
                if (allHeader.Contains(headerResponse))
                {
                    return allHeader.GetValues(headerResponse).First();
                }
            }

            throw new BunqException(ERROR_COULD_NOT_DETERMINE_RESPONSE_ID_HEADER);
        }

Actually... the loop brings needed extra complexity, in this case. Maybe it is better to stick with the conditional statements.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I think it is indeed better if we stick to the conditional statements! 👍

{
return allHeader.GetValues(HEADER_RESPONSE_ID_UPPER_CASE).First();
}
else if (allHeader.Contains(HEADER_RESPONSE_ID_LOWER_CASE))
{
return allHeader.GetValues(HEADER_RESPONSE_ID_LOWER_CASE).First();
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing else

else
{
throw new BunqException(ERROR_COULD_NOT_DETERMINE_RESPONSE_ID_HEADER);
}
}

private static ApiException CreateApiExceptionRequestUnsuccessful(int responseCode, string responseBody)
private static ApiException CreateApiExceptionRequestUnsuccessful(
int responseCode,
string responseBody,
string responseId
)
{
try
{
return ExceptionFactory.CreateExceptionForResponse(responseCode, FetchErrorDescriptions(responseBody));
return ExceptionFactory.CreateExceptionForResponse(
responseCode,
FetchErrorDescriptions(responseBody),
responseId
);
}
catch (JsonException)
{
return ExceptionFactory.CreateExceptionForResponse(responseCode, new List<string> {responseBody});
return ExceptionFactory.CreateExceptionForResponse(
responseCode,
new List<string> {responseBody},
responseId
);
}
}

Expand Down