Skip to content

Commit

Permalink
Merge pull request #1 from deveel/release/0.2.0
Browse files Browse the repository at this point in the history
Release Candidate 0.2.0
  • Loading branch information
tsutomi authored Jul 3, 2024
2 parents 86cf67e + 8a78f98 commit ada0a0d
Show file tree
Hide file tree
Showing 8 changed files with 275 additions and 5 deletions.
16 changes: 16 additions & 0 deletions src/Deveel.Results/IValidationError.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using System.ComponentModel.DataAnnotations;

namespace Deveel
{
/// <summary>
/// Represents an error that occurred while validating a
/// a command or operation.
/// </summary>
public interface IValidationError : IOperationError
{
/// <summary>
/// Gets the list of validation results that caused the error.
/// </summary>
IReadOnlyList<ValidationResult> ValidationResults { get; }
}
}
24 changes: 23 additions & 1 deletion src/Deveel.Results/OperationResult.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
namespace Deveel
using System.ComponentModel.DataAnnotations;

namespace Deveel
{
/// <summary>
/// An operation result that has no value returned.
Expand Down Expand Up @@ -71,6 +73,26 @@ public static OperationResult Fail(IOperationError error)
public static OperationResult Fail(string code, string domain, string? message = null, IOperationError? inner = null)
=> Fail(new OperationError(code, domain, message, inner));

/// <summary>
/// Creates a new instance of an operation result that has failed
/// because of a validation error.
/// </summary>
/// <param name="code">
/// The code of the error that has caused the operation to fail.
/// </param>
/// <param name="domain">
/// The domain where the error has occurred.
/// </param>
/// <param name="validationResults">
/// The list of validation results that have caused the operation to fail.
/// </param>
/// <returns>
/// Returns an instance of <see cref="OperationResult"/> that represents
/// a failed operation.
/// </returns>
public static OperationResult ValidationFailed(string code, string domain, IEnumerable<ValidationResult> validationResults)
=> Fail(new OperationValidationError(code, domain, validationResults.ToList()));

/// <summary>
/// Implicitly converts an instance of an error to an operation result.
/// </summary>
Expand Down
73 changes: 72 additions & 1 deletion src/Deveel.Results/OperationResultExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,22 +1,93 @@
namespace Deveel
using System.ComponentModel.DataAnnotations;

namespace Deveel
{
/// <summary>
/// Extensions for the <see cref="IOperationResult"/> contract.
/// </summary>
public static class OperationResultExtensions
{
/// <summary>
/// Determines if the operation result is a success.
/// </summary>
/// <param name="result">
/// The operation result to check.
/// </param>
/// <returns>
/// Returns <see langword="true"/> if the operation result is a success,
/// otherwise <see langword="false"/>.
/// </returns>
public static bool IsSuccess(this IOperationResult result)
=> result.ResultType == OperationResultType.Success;

/// <summary>
/// Determines if the operation result is an error.
/// </summary>
/// <param name="result">
/// The operation result to check.
/// </param>
/// <returns>
/// Returns <see langword="true"/> if the operation result is an error,
/// otherwise <see langword="false"/>.
/// </returns>
public static bool IsError(this IOperationResult result)
=> result.ResultType == OperationResultType.Error;

/// <summary>
/// Determines if the operation result is cancelled.
/// </summary>
/// <param name="result">
/// The operation result to check.
/// </param>
/// <returns>
/// Returns <see langword="true"/> if the operation result is cancelled,
/// otherwise <see langword="false"/>.
/// </returns>
public static bool IsCancelled(this IOperationResult result)
=> result.ResultType == OperationResultType.Cancelled;

/// <summary>
/// Determines if the operation has caused no changes
/// to the state of an object.
/// </summary>
/// <param name="result">
/// The operation result to check.
/// </param>
/// <returns>
/// Returns <see langword="true"/> if the operation has not
/// caused any changes to the object, otherwise <see langword="false"/>.
/// </returns>
public static bool IsUnchanged(this IOperationResult result)
=> result.ResultType == OperationResultType.Unchanged;

/// <summary>
/// Checks if the operation result has validation errors.
/// </summary>
/// <param name="result">
/// The operation result to check.
/// </param>
/// <returns>
/// Returns <see langword="true"/> if the operation result is
/// an error and the type of the error is <see cref="IValidationError"/>,
/// otherwise <see langword="false"/>.
/// </returns>
public static bool HasValidationErrors(this IOperationResult result)
=> result.IsError() && (result.Error is IValidationError);

/// <summary>
/// Gets the list of validation results that caused an operation
/// to fail.
/// </summary>
/// <param name="result">
/// The result that contains the validation errors.
/// </param>
/// <returns>
/// Returns a list of <see cref="ValidationResult"/> objects that
/// describe the validation errors that caused the operation to fail.
/// </returns>
public static IReadOnlyList<ValidationResult> ValidationResults(this IOperationResult result)
=> result.HasValidationErrors() ? ((IValidationError)result.Error!).ValidationResults : Array.Empty<ValidationResult>();

public static TResult Match<TResult>(this IOperationResult result,

Check warning on line 91 in src/Deveel.Results/OperationResultExtensions.cs

View workflow job for this annotation

GitHub Actions / Build and Test (6.0.x)

Missing XML comment for publicly visible type or member 'OperationResultExtensions.Match<TResult>(IOperationResult, Func<TResult>?, Func<IOperationError?, TResult>?, Func<TResult>?)'

Check warning on line 91 in src/Deveel.Results/OperationResultExtensions.cs

View workflow job for this annotation

GitHub Actions / Build and Test (6.0.x)

Missing XML comment for publicly visible type or member 'OperationResultExtensions.Match<TResult>(IOperationResult, Func<TResult>?, Func<IOperationError?, TResult>?, Func<TResult>?)'

Check warning on line 91 in src/Deveel.Results/OperationResultExtensions.cs

View workflow job for this annotation

GitHub Actions / Build and Test (7.0.x)

Missing XML comment for publicly visible type or member 'OperationResultExtensions.Match<TResult>(IOperationResult, Func<TResult>?, Func<IOperationError?, TResult>?, Func<TResult>?)'

Check warning on line 91 in src/Deveel.Results/OperationResultExtensions.cs

View workflow job for this annotation

GitHub Actions / Build and Test (7.0.x)

Missing XML comment for publicly visible type or member 'OperationResultExtensions.Match<TResult>(IOperationResult, Func<TResult>?, Func<IOperationError?, TResult>?, Func<TResult>?)'

Check warning on line 91 in src/Deveel.Results/OperationResultExtensions.cs

View workflow job for this annotation

GitHub Actions / Build and Test (8.0.x)

Missing XML comment for publicly visible type or member 'OperationResultExtensions.Match<TResult>(IOperationResult, Func<TResult>?, Func<IOperationError?, TResult>?, Func<TResult>?)'

Check warning on line 91 in src/Deveel.Results/OperationResultExtensions.cs

View workflow job for this annotation

GitHub Actions / Build and Test (8.0.x)

Missing XML comment for publicly visible type or member 'OperationResultExtensions.Match<TResult>(IOperationResult, Func<TResult>?, Func<IOperationError?, TResult>?, Func<TResult>?)'

Check warning on line 91 in src/Deveel.Results/OperationResultExtensions.cs

View workflow job for this annotation

GitHub Actions / Publish Package

Missing XML comment for publicly visible type or member 'OperationResultExtensions.Match<TResult>(IOperationResult, Func<TResult>?, Func<IOperationError?, TResult>?, Func<TResult>?)'

Check warning on line 91 in src/Deveel.Results/OperationResultExtensions.cs

View workflow job for this annotation

GitHub Actions / Publish Package

Missing XML comment for publicly visible type or member 'OperationResultExtensions.Match<TResult>(IOperationResult, Func<TResult>?, Func<IOperationError?, TResult>?, Func<TResult>?)'
Func<TResult>? ifSuccess = null,
Func<IOperationError?, TResult>? ifError = null,
Expand Down
58 changes: 57 additions & 1 deletion src/Deveel.Results/OperationResult_T.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
namespace Deveel
using System.ComponentModel.DataAnnotations;

namespace Deveel
{
public readonly struct OperationResult<T> : IOperationResult<T>

Check warning on line 5 in src/Deveel.Results/OperationResult_T.cs

View workflow job for this annotation

GitHub Actions / Build and Test (6.0.x)

Missing XML comment for publicly visible type or member 'OperationResult<T>'

Check warning on line 5 in src/Deveel.Results/OperationResult_T.cs

View workflow job for this annotation

GitHub Actions / Build and Test (6.0.x)

Missing XML comment for publicly visible type or member 'OperationResult<T>'

Check warning on line 5 in src/Deveel.Results/OperationResult_T.cs

View workflow job for this annotation

GitHub Actions / Build and Test (7.0.x)

Missing XML comment for publicly visible type or member 'OperationResult<T>'

Check warning on line 5 in src/Deveel.Results/OperationResult_T.cs

View workflow job for this annotation

GitHub Actions / Build and Test (7.0.x)

Missing XML comment for publicly visible type or member 'OperationResult<T>'

Check warning on line 5 in src/Deveel.Results/OperationResult_T.cs

View workflow job for this annotation

GitHub Actions / Build and Test (8.0.x)

Missing XML comment for publicly visible type or member 'OperationResult<T>'

Check warning on line 5 in src/Deveel.Results/OperationResult_T.cs

View workflow job for this annotation

GitHub Actions / Build and Test (8.0.x)

Missing XML comment for publicly visible type or member 'OperationResult<T>'

Check warning on line 5 in src/Deveel.Results/OperationResult_T.cs

View workflow job for this annotation

GitHub Actions / Publish Package

Missing XML comment for publicly visible type or member 'OperationResult<T>'

Check warning on line 5 in src/Deveel.Results/OperationResult_T.cs

View workflow job for this annotation

GitHub Actions / Publish Package

Missing XML comment for publicly visible type or member 'OperationResult<T>'
{
Expand Down Expand Up @@ -42,19 +44,73 @@ public static OperationResult<T> Success(T? value)
return new OperationResult<T>(OperationResultType.Success, value, null);
}

/// <summary>
/// Creates a new instance of an operation result that has failed
/// because of an error.
/// </summary>
/// <param name="error">
/// The error that caused the operation to fail.
/// </param>
/// <returns>
/// Returns an instance of <see cref="OperationResult{T}"/> that
/// represents a failure in the operation.
/// </returns>
/// <exception cref="ArgumentNullException">
/// Thrown when the <paramref name="error"/> is <see langword="null"/>.
/// </exception>
public static OperationResult<T> Fail(IOperationError error)
{
ArgumentNullException.ThrowIfNull(error, nameof(error));
return new OperationResult<T>(OperationResultType.Error, default, error);
}

/// <summary>
/// Creates a new instance of an operation result that has failed
/// because of an error.
/// </summary>
/// <param name="code">
/// The code of the error that caused the operation to fail.
/// </param>
/// <param name="domain">
/// The domain where the error occurred.
/// </param>
/// <param name="message">
/// A message that describes the error.
/// </param>
/// <param name="inner">
/// An inner error that caused the error.
/// </param>
/// <returns>
/// Returns an instance of <see cref="OperationResult{T}"/> that
/// represents a failure in the operation.
/// </returns>
public static OperationResult<T> Fail(string code, string domain, string? message = null, IOperationError? inner = null)
{
ArgumentNullException.ThrowIfNull(code, nameof(code));
ArgumentNullException.ThrowIfNull(domain, nameof(domain));
return new OperationResult<T>(OperationResultType.Error, default, new OperationError(code, domain, message, inner));
}

/// <summary>
/// Creates a new instance of an operation result that has failed
/// because of a validation error.
/// </summary>
/// <param name="code">
/// The code of the error that caused the operation to fail.
/// </param>
/// <param name="domain">
/// The domain where the error occurred.
/// </param>
/// <param name="results">
/// The list of validation results that caused the error.
/// </param>
/// <returns>
/// Returns an instance of <see cref="OperationResult{T}"/> that
/// represents a failure in the operation.
/// </returns>
public static OperationResult<T> ValidationFailed(string code, string domain, IEnumerable<ValidationResult> results)
=> Fail(new OperationValidationError(code, domain, results.ToList()));

public static implicit operator OperationResult<T>(OperationResult result)

Check warning on line 114 in src/Deveel.Results/OperationResult_T.cs

View workflow job for this annotation

GitHub Actions / Build and Test (6.0.x)

Missing XML comment for publicly visible type or member 'OperationResult<T>.implicit operator OperationResult<T>(OperationResult)'

Check warning on line 114 in src/Deveel.Results/OperationResult_T.cs

View workflow job for this annotation

GitHub Actions / Build and Test (6.0.x)

Missing XML comment for publicly visible type or member 'OperationResult<T>.implicit operator OperationResult<T>(OperationResult)'

Check warning on line 114 in src/Deveel.Results/OperationResult_T.cs

View workflow job for this annotation

GitHub Actions / Build and Test (7.0.x)

Missing XML comment for publicly visible type or member 'OperationResult<T>.implicit operator OperationResult<T>(OperationResult)'

Check warning on line 114 in src/Deveel.Results/OperationResult_T.cs

View workflow job for this annotation

GitHub Actions / Build and Test (7.0.x)

Missing XML comment for publicly visible type or member 'OperationResult<T>.implicit operator OperationResult<T>(OperationResult)'

Check warning on line 114 in src/Deveel.Results/OperationResult_T.cs

View workflow job for this annotation

GitHub Actions / Build and Test (8.0.x)

Missing XML comment for publicly visible type or member 'OperationResult<T>.implicit operator OperationResult<T>(OperationResult)'

Check warning on line 114 in src/Deveel.Results/OperationResult_T.cs

View workflow job for this annotation

GitHub Actions / Build and Test (8.0.x)

Missing XML comment for publicly visible type or member 'OperationResult<T>.implicit operator OperationResult<T>(OperationResult)'

Check warning on line 114 in src/Deveel.Results/OperationResult_T.cs

View workflow job for this annotation

GitHub Actions / Publish Package

Missing XML comment for publicly visible type or member 'OperationResult<T>.implicit operator OperationResult<T>(OperationResult)'

Check warning on line 114 in src/Deveel.Results/OperationResult_T.cs

View workflow job for this annotation

GitHub Actions / Publish Package

Missing XML comment for publicly visible type or member 'OperationResult<T>.implicit operator OperationResult<T>(OperationResult)'
=> new OperationResult<T>(result.ResultType, default, result.Error);

Expand Down
50 changes: 50 additions & 0 deletions src/Deveel.Results/OperationValidationError.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
using System.ComponentModel.DataAnnotations;
using System.Diagnostics.CodeAnalysis;

namespace Deveel
{
/// <summary>
/// An error that occurred during the validation of an operation.
/// </summary>
public readonly struct OperationValidationError : IValidationError
{
/// <summary>
/// Constructs an instance of an <see cref="OperationValidationError"/> object.
/// </summary>
/// <param name="code">
/// The code of the error, that is unique within the
/// given domain.
/// </param>
/// <param name="domain">
/// The domain where the error occurred.
/// </param>
/// <param name="validationResults">
/// A list of validation results that caused the error.
/// </param>
public OperationValidationError(string code, string domain, IReadOnlyList<ValidationResult> validationResults)
{
ArgumentNullException.ThrowIfNull(code, nameof(code));
ArgumentNullException.ThrowIfNull(domain, nameof(domain));
ArgumentNullException.ThrowIfNull(validationResults, nameof(validationResults));

Code = code;
Domain = domain;
ValidationResults = validationResults;
}

/// <inheritdoc/>
public IReadOnlyList<ValidationResult> ValidationResults { get; }

/// <inheritdoc/>
public string Code { get; }

/// <inheritdoc/>
public string Domain { get; }

[ExcludeFromCodeCoverage]
string? IOperationError.Message => null;

[ExcludeFromCodeCoverage]
IOperationError? IOperationError.InnerError => null;
}
}
19 changes: 18 additions & 1 deletion test/Deveel.Results.XUnit/OperationErrorTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
namespace Deveel
using System.ComponentModel.DataAnnotations;

namespace Deveel
{
public static class OperationErrorTests
{
Expand Down Expand Up @@ -94,5 +96,20 @@ public static void OperationException_WithMessageAndInner()
Assert.Equal("err.2", innerError.Code);
Assert.Equal("biz", innerError.Domain);
}

[Fact]
public static void ValidationError_WithResults()
{
var results = new[] {
new ValidationResult("First error of the validation", new []{ "Member1" }),
new ValidationResult("Second error of the validation", new []{"Member2"})
};

var error = new OperationValidationError("err.1", "biz", results);

Assert.Equal("err.1", error.Code);
Assert.Equal("biz", error.Domain);
Assert.Same(results, error.ValidationResults);
}
}
}
23 changes: 22 additions & 1 deletion test/Deveel.Results.XUnit/OperationResultOfTTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
namespace Deveel
using System.ComponentModel.DataAnnotations;

namespace Deveel
{
public static class OperationResultOfTTests
{
Expand Down Expand Up @@ -136,6 +138,25 @@ public static void ImplicitlyConvertFromOperationResult_Success()

}

[Fact]
public static void OperationResult_FailForValidation()
{
var validations = new[] {
new ValidationResult("err.1", new []{ "Member1" }),
new ValidationResult("err.2", new []{ "Member2" })
};

var result = OperationResult<int>.ValidationFailed("err.1", "biz", validations);

Assert.Equal(OperationResultType.Error, result.ResultType);
Assert.NotNull(result.Error);
Assert.Equal("err.1", result.Error.Code);
Assert.Equal("biz", result.Error.Domain);
var opError = Assert.IsType<OperationValidationError>(result.Error);

Assert.Equal(validations.Length, opError.ValidationResults.Count);
}

class DomainException : OperationException
{
public DomainException(string code)
Expand Down
17 changes: 17 additions & 0 deletions test/Deveel.Results.XUnit/OperationResultTests.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using Deveel;

using System.ComponentModel.DataAnnotations;

namespace Deveel;

public static class OperationResultTests
Expand Down Expand Up @@ -184,4 +186,19 @@ public static void OperationResult_IsUnchanged()
var result = OperationResult.NotChanged;
Assert.True(result.IsUnchanged());
}

[Fact]
public static void OperationResult_ValidationErrors()
{
var errors = new List<ValidationResult> {
new ValidationResult("The value is required", new[] { "value" }),
new ValidationResult("The value must be greater than 0", new[] { "value" })
};

var error = new OperationValidationError("err.1", "biz", errors);
var result = OperationResult.Fail(error);

Assert.True(result.HasValidationErrors());
Assert.Equal(errors, result.ValidationResults());
}
}

0 comments on commit ada0a0d

Please sign in to comment.