Skip to content

Commit

Permalink
feat: extend exception and expose execution errors (#2)
Browse files Browse the repository at this point in the history
  • Loading branch information
hermogenes authored Jul 30, 2024
1 parent a8096e4 commit 8c22311
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 36 deletions.
29 changes: 1 addition & 28 deletions src/LibSql.Http.Client/Exceptions/LibSqlHttpClientException.cs
Original file line number Diff line number Diff line change
@@ -1,34 +1,7 @@
using LibSql.Http.Client.Response;

namespace LibSql.Http.Client.Exceptions;

/// <inheritdoc />
public class LibSqlClientException(string message, Exception? innerException = default)
: Exception(message, innerException)
{
private const string TabConstant = "\t";

/// <summary>
/// Create exception using errors from pipeline request
/// </summary>
/// <param name="executionErrors">Errors from pipeline response</param>
public LibSqlClientException(IReadOnlyCollection<ExecutionError> executionErrors) : this(
FormatErrorMessage(executionErrors))
{
}

private static string FormatErrorMessage(IReadOnlyCollection<ExecutionError> executionErrors)
{
var joinedMessages = string.Join(
Environment.NewLine,
executionErrors.Select(
(error, index) =>
{
var codePart = error.Code is null ? string.Empty : $"({error.Code}) ";

return $"{TabConstant}[{index}]: {codePart}{error.Message}";
}));

return $"[LibSqlPipelineError] The request failed.{Environment.NewLine}{joinedMessages}";
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using LibSql.Http.Client.Response;

namespace LibSql.Http.Client.Exceptions;

/// <inheritdoc />
public class LibSqlClientExecutionException : LibSqlClientException
{
private const string TabConstant = "\t";

/// <summary>
/// Create exception using errors from pipeline request
/// </summary>
/// <param name="executionErrors">Errors from pipeline response</param>
public LibSqlClientExecutionException(IReadOnlyCollection<ExecutionError> executionErrors) : base(
FormatErrorMessage(executionErrors))
{
ExecutionErrors = executionErrors;
}

/// <summary>
/// Errors from pipeline response
/// </summary>
public IReadOnlyCollection<ExecutionError> ExecutionErrors { get; }

/// <summary>
/// First error from pipeline response
/// </summary>
public ExecutionError Error => ExecutionErrors.First();

private static string FormatErrorMessage(IReadOnlyCollection<ExecutionError> executionErrors)
{
var joinedMessages = string.Join(
Environment.NewLine,
executionErrors.Select(
(error, index) =>
{
var codePart = error.Code is null ? string.Empty : $"({error.Code}) ";

return $"{TabConstant}[{index}]: {codePart}{error.Message}";
}));

return $"[LibSqlPipelineError] The request failed.{Environment.NewLine}{joinedMessages}";
}
}
2 changes: 1 addition & 1 deletion src/LibSql.Http.Client/Response/ResultReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public void Dispose()
public void ThrowIfError()
{
if (errors.Length > 0)
throw new LibSqlClientException(errors);
throw new LibSqlClientExecutionException(errors);
}

public bool HasMoreResults()
Expand Down
2 changes: 1 addition & 1 deletion test/LibSql.Http.Client.Tests/Integration/RollbackTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public async Task CheckRollbackWhenOneCommandIsInvalid()

var failingAction = () => LibSqlClient.ExecuteMultipleAsync(statements, TransactionMode.WriteImmediate);

await failingAction.Should().ThrowExactlyAsync<LibSqlClientException>();
await failingAction.Should().ThrowExactlyAsync<LibSqlClientExecutionException>();

var afterCount = await LibSqlClient.ExecuteScalarAsync(TestData.CountSql);

Expand Down
15 changes: 9 additions & 6 deletions test/LibSql.Http.Client.Tests/LibSqlHttpClientTestsDefault.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public void ConstructorShouldFailIfUrlIsNull()

action.Should().ThrowExactly<ArgumentNullException>();
}

[Theory]
[JsonFileData("Data/execute-response-no-error-no-result.json", true)]
public void ShouldAllowUseDifferentCredentials(JsonElement response)
Expand All @@ -29,7 +29,7 @@ public void ShouldAllowUseDifferentCredentials(JsonElement response)

handler.LastSentRequest!.Uri.Should().Be("https://another.libsql.test/v3/pipeline");
}

[Theory]
[JsonFileData("Data/execute-response-with-error.json", true)]
public async Task ShouldThrowExceptionIfResultContainsError(
Expand All @@ -43,7 +43,7 @@ public async Task ShouldThrowExceptionIfResultContainsError(
sql,
TestDataJsonSerializerContext.Default.ResultSetTestModel);

await action.Should().ThrowExactlyAsync<LibSqlClientException>();
await action.Should().ThrowExactlyAsync<LibSqlClientExecutionException>();
handler.LastSentRequest.Should().NotBeNull();
handler.LastSentRequest!.Body.Should().BeEquivalentTo(JsonSerializer.Serialize(request));
}
Expand Down Expand Up @@ -240,14 +240,17 @@ public async Task ShouldParseMultipleResults(JsonElement response, ResultSetTest
{
var (_, client) = CreateClient(response);

var result = await client.QueryMultipleAsync(["SELECT * FROM table1", "SELECT * FROM TABLE 2"], TransactionMode.WriteImmediate);
var result = await client.QueryMultipleAsync(
["SELECT * FROM table1", "SELECT * FROM TABLE 2"],
TransactionMode.WriteImmediate);

result.Count.Should().Be(expected.Length);

for (var i = 0; i < expected.Length; i++)
{
result.HasMoreResults().Should().BeTrue();
result.Read(TestDataJsonSerializerContext.Default.ResultSetTestModel).ToList().Should().BeEquivalentTo(expected[i]);
result.Read(TestDataJsonSerializerContext.Default.ResultSetTestModel).ToList().Should()
.BeEquivalentTo(expected[i]);
}

result.HasMoreResults().Should().BeFalse();
Expand All @@ -258,4 +261,4 @@ private static (MockedJsonHttpHandler, LibSqlHttpClient) CreateClient(JsonElemen
var handler = new MockedJsonHttpHandler(response);
return (handler, new LibSqlHttpClient(handler, null, "token"));
}
}
}

0 comments on commit 8c22311

Please sign in to comment.