Giter VIP home page Giter VIP logo

error-or's People

Contributors

amantinband avatar apfohl avatar blouflashdb avatar florentmx avatar i3arnon avatar jobumble avatar kikutano avatar madhon avatar mantinbot avatar martinobordin avatar mopinon avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

error-or's Issues

Add Dictionary to the ErrorOr.Error type

It would be nice to have an optional dictionary for errors, next to the errorCode, message,... So it would be possible to provide addtional informations with an error.

ErrorOr type cannot be deserialized

Hi @amantinband,

Thanks for this great library. I'm currently having an issue when I try to deserialize ErrorOr type. ErrorOr values seem to be serializable but not deserializable.

Sample code:

using ErrorOr;
using System.Text.Json;

ErrorOr<int> e1 = 42;
ErrorOr<int> e2 = Error.NotFound();

Console.WriteLine($"e1: {e1.Value}/{e1.IsError}");
Console.WriteLine($"e2: {e2.Value}/{e2.IsError}");

var j1 = JsonSerializer.Serialize(e1);
var j2 = JsonSerializer.Serialize(e2);

Console.WriteLine("------");

Console.WriteLine($"j1: {j1}");
Console.WriteLine($"j2: {j2}");

e1 = JsonSerializer.Deserialize<ErrorOr<int>>(j1);
e2 = JsonSerializer.Deserialize<ErrorOr<int>>(j2);

Console.WriteLine("------");

Console.WriteLine($"e1: {e1.Value}/{e1.IsError}");
Console.WriteLine($"e2: {e2.Value}/{e2.IsError}");

Output:

e1: 42/False
e2: 0/True
------
j1: {"IsError":false,"Errors":[{"Code":"ErrorOr.NoErrors","Description":"Error list cannot be retrieved from a successful ErrorOr.","Type":1,"NumericType":1}],"ErrorsOrEmptyList":[],"Value":42,"FirstError":{"Code":"ErrorOr.NoFirstError","Description":"First error cannot be retrieved from a successful ErrorOr.","Type":1,"NumericType":1}}
j2: {"IsError":true,"Errors":[{"Code":"General.NotFound","Description":"A \u0027Not Found\u0027 error has occurred.","Type":4,"NumericType":4}],"ErrorsOrEmptyList":[{"Code":"General.NotFound","Description":"A \u0027Not Found\u0027 error has occurred.","Type":4,"NumericType":4}],"Value":0,"FirstError":{"Code":"General.NotFound","Description":"A \u0027Not Found\u0027 error has occurred.","Type":4,"NumericType":4}}
------
e1: 0/False
e2: 0/False

This problem appears to be caused by getter-only properties. I think serializers are not smart enough to understand where the data comes from. I created a pull request by the way. If you have any better solution, feel free to reject it.

Thanks!

Versions
I've encountered this issue on:

ErrorOr version : 1.2.1
.NET version: .NET 7
OS version : Windows 11

Extend ErrorType enum

It would be great to have all the type of errors that could be mapped to 4xx status code series

.NET Standard 2.0 target

Is there any reason you haven't included a .NET Standard 2.0 target as there appears to be no issue compiling it for that target, would also help with more broader usage of the lib?

Adding IsError/Value to IErrorOr

Hi.
I am writing LoggingPipelineBehavior using MediatR.
Here's class:

internal class LoggingPipelineBehavior<TRequest, TResponse> :
        IPipelineBehavior<TRequest, TResponse>
        where TRequest : IRequest<TResponse>
        where TResponse : IErrorOr

And here's my Handle method:

public async Task<TResponse> Handle(TRequest request, CancellationToken _, RequestHandlerDelegate<TResponse> next)
{
    LogRequest(request);
    TResponse response = await next();

    if (response is IErrorOr errorOr && errorOr.Errors?.Count > 0)
    {
        LogErrors(errorOr.Errors);
        return response;
    }

    LogResponse(response);
    return response;
}

Unfortunately, if my handler is returning successful object (not errors), I'm still going inside that if is IErrorOr block, because if ErrorOr.IsError is false, Errors's Count is still getting 1 (NoErrors error).
Would it be possible to add
bool IsError { get; }
to IErrorOr interface, so it can be checked by this flag if this object is successful or failed one?
Edit: Also, would it be possible to add
object Value { get; }
to interface to retrieve successful instance?
If there's other/better solution, please let me know.
By the way - great package!
Thanks,
Sล‚awek

Adding metadata to errors

Hello,

one thing FluentResults has is the ability to add metadata. After switching to ErrorOr, I have to add the metadata inside ProblemDetailsFactory, which is very cumbersome... Due to this libraries implementation I have not find a elegant way to attach metadata to the error directly.

Do you know a way to add metadata in a similar way like in FluentResults or another generic way I can use to add metadata to Error. Or is it maybe even possible to add metadata to ErrorOr (maybe via generic Error instead of hardcoded one...).

Thanks in advance ๐Ÿ˜€

Smart way to use ErrorOr in a similar style to with-Expressions

This is more of a question than an issue. I use ErrorOr to guard my calculation engine from getting faulty data and it works great. I usually do this by making constructors private and my objects immutable and allow object creation only in form of

public static ErrorOr<T> TryCreate(parameters...);

I now have some cases where I would like to change one or two properties of the object. For C# record classes this is solved very elegantly by using with expresisons:

var person2 = person1 with {Lastname='Doe' };

The drawback of with-expressions is that in this case I cannot run all my validation logic.
In some cases I decided to create a copy constructor-like version of TryCreate() manually to still get an ErrorOr and all it's validation logic but writing copy constructors is not something I enjoy doing. This looks something like this:

 public static ErrorOr<MyClass> TryCreate(MyClass orig, int? id= null, string? name= null)
 {
     id ??= orig.Id;
     name ??= orig.Name;
     return TryCreate(id.Value, name.Value);
 }

// can be called with named parameters like so:
 var y = TryCreate(myClassObj, name:"Bill");

While this consuming part is okay, the writing part of the copy constructors is something I would want to avoid.

I wonder if there is a more elegant solution (which does not involve Reflections). I think it could be done with code generation but that's something I havent't done before.

would appreciate ideas on this topic.

Errors: Count = 1; IsError: False

I noticed when returning an Error.NotFound using ErrorOr, it sets the count to 1, but it does not flag IsError as true.

Function
public async Task<ErrorOr> Process

Return value
return Error.NotFound(code: "", description: $"Nothing found with the specified id: {Id}.");

image

Change Interface to covariant IError<out TValue>

It would be great to have covariant interface instead of non-generic like this:

public interface IErrorOr<out TValue>
{
    List<Error>? Errors { get; }
    bool IsError { get; }
    List<Error> ErrorsOrEmptyList { get; }
    Error FirstError { get; }
    TValue Value { get; }
}

Then it allows us to upcast ErrorOr objects like this:

ErrorOr<TChild> childResult = new TChild();
IErrorOr<TParent> parentResult = childResult;

Cannot implicitly convert type IQueryable<T> to ErrorOr.ErrorOr<IQueryable<T>>

Hi, thank You for this awesome library.
I'm having trouble implementing a get request handler, the code as follow:

public class GetQueryHandler :
    IRequestHandler<GetQuery, ErrorOr<IQueryable<User>>>
{
    private readonly IApplicationDbContext _context;

    public GetQueryHandler(IApplicationDbContext context)
    {
        _context = context;
    }

    public async Task<ErrorOr<IQueryable<User>>> Handle(
        GetQuery request,
        CancellationToken cancellationToken)
    {
        await Task.CompletedTask;
        return _context.Users.AsQueryable();
    }
}

Throws the error cannot implicitly convert type IQueryable to ErrorOr.ErrorOr<IQueryable>.
If I change the IQueryable to IList and change the return to _context.Users.ToList() it works.

How can I create an Error type that has exception data?

What I want to achieve is something similar as:

problemdetails-json-exception-dev-3

from Hellang.Middleware.ProblemDetails

Now, the problem is I should only expose the exception data with certain environments so set the exception on an Error type in the repository and choose to show it in ... the ApiProblemDetailsFactory?

        {
            var response = await _orderClient.SummaryAsync(orderNumber);
            
            if(_logger.IsEnabled(LogLevel.Debug))
                _logger.LogDebug("Retrieved shipping information for order {orderNumber}", orderNumber);
            
            return _mapper.Map<Domain.Shipping.ShippingInformation.ShippingInformation>(response);
        }
        catch (ApiClientCallException apiClientCallException) when (apiClientCallException.StatusCode == 404)
        {
            if(_logger.IsEnabled(LogLevel.Information))
                _logger.LogInformation("Order {orderNumber} not found", orderNumber);
            
            return Errors.Shipping.OrderNotFound;
        }
        catch (Exception e)
        {
            if(_logger.IsEnabled(LogLevel.Error))
                _logger.LogError(e, "Failed to get shipping information for order {orderNumber}", orderNumber);
            
            return Error.Unexpected(); // Add exception here!
        }

How do I disable ErrorOr if I set my environment to development and only enable it when it is changed to Production?

I have this code from my RecipeService.cs.

public async Task<ErrorOr<Recipe>> GetAsync(string id)
{
    var recipe = await _recipeRepo.GetAsync(id);

    if (recipe is null)
    {
        return Errors.Recipe.NotFound;
    }

    return recipe;

}

And also I have this code from my RecipeController.cs.

public async Task<IActionResult> GetRecipes([FromQuery][Required] string id)
{
    ErrorOr<Recipe> recipes = await _recipeService.GetAsync(id);

    return recipes.Match(
        recipe => Ok(recipe),
        errors => Problem(errors)
    );

}

This is my startup configuration.

var app = builder.Build();
{
    app.UseSwagger();
    app.UseSwaggerUI();

    if (!app.Environment.IsDevelopment())
    {
        app.UseExceptionHandler("/error"); // I want this to activate if the environment is not in Development.
    }

    app.UseHttpsRedirection();

    app.UseAuthorization();

    app.MapControllers();

    app.Run();
}

What I want to achieve is for the REST API to return a stack trace for a specific error instead of returning a custom error. I only want it to return a custom error when I set my environment to Production.

ErrorOr<ResultObj>IsError Always returns as "false"

ErrorOr<Dictionary<int, int>> priorityResult = ..... where inside I return Error.Failure("... and method returns

after checking
if (priorityResult.IsError)
return priorityResult.Errors;

IsError always false!

How do you catch error out side the domain

Hi,
many thanks for this tool,

Please see the code the below from your examples:

ErrorOr DeleteUser(Guid id)
{
var user = await _userRepository.GetByIdAsync(id);
if (user is null)
{
return Error.NotFound(code: "User.NotFound", description: "User not found.");
}

await _userRepository.DeleteAsync(user);
return Result.Deleted;

}

How do you capture an error from this line: await _userRepository.DeleteAsync(user); Using your tool so that you can include it in the returned results to the front end to inform the user that the record can not be deleted?

Many thanks
Zak

Add error levels (Warning, Fatal, Error...)

Say that you are processing a collection of items: for each item, you need to contact an external service accessible under authentication.

There are then different levels of errors: "fatal", for errors as "authentication failed" (this error will occur for each item in the list), and "warning", such as "this item does not exist".

We should be able to handle errors based on their levels

Please add a strong name for the assembly

Hi,
Please add strong name for the assembly. Without it, in 4.x .NET projects without strong name it is not possible to use this library, getting exception:

Could not load file or assembly 'ErrorOr, Version=1.2.1.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. A strongly-named assembly is required. (Exception from HRESULT: 0x80131044)
System.IO.FileLoadException: Could not load file or assembly 'ErrorOr, Version=1.2.1.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. A strongly-named assembly is required. (Exception from HRESULT: 0x80131044)

[Enhancement] Add extension methods for to convert to IActionResult (mvc) IResult (minimal API)

First of all, I love this extension.
I would like to see extension methods to convert an ErrorOr (domain result) to an IActionResult or IResult.

The conversion should send 200OK or 204NoContent if the operation is successfull.
If the result is an Error the conversion would take the type of error and return an appropriate ProblemDetails with the correct status Code.

This enhancement was inspired by this repository https://github.com/AKlaus/DomainResult

Consider 'Forbidden' as built-in error type

First of all, thank you very much for creating and maintaining this repository.

I would like to suggest adding an additional built-in error type, namely 'Forbidden,' because I believe that in some situations, it provides a better description than the existing 'Unauthorized.'

I understand that error types don't necessarily have to directly align with HTTP standards, but in this case, it might make more sense. This is what the specifications state:

Unauthorized

The 401 (Unauthorized) status code indicates that the request has not
been applied because it lacks valid authentication credentials for
the target resource. The server generating a 401 response MUST send
a WWW-Authenticate header field (Section 4.1) containing at least one
challenge applicable to the target resource.

If the request included authentication credentials, then the 401
response indicates that authorization has been refused for those
credentials. The user agent MAY repeat the request with a new or
replaced Authorization header field (Section 4.2). If the 401
response contains the same challenge as the prior response, and the
user agent has already attempted authentication at least once, then
the user agent SHOULD present the enclosed representation to the
user, since it usually contains relevant diagnostic information.

Forbidden:

The 403 (Forbidden) status code indicates that the server understood
the request but refuses to authorize it. A server that wishes to
make public why the request has been forbidden can describe that
reason in the response payload (if any).

If authentication credentials were provided in the request, the
server considers them insufficient to grant access. The client
SHOULD NOT automatically repeat the request with the same
credentials. The client MAY repeat the request with new or different
credentials. However, a request might be forbidden for reasons
unrelated to the credentials.

If you agree with my reasoning, I could open a PR to add Forbidden as a built-in type.

I look forward to hearing from you.

edit:
I know I can potentially add a custom error type myself, but it seemed to me like a valuable addition as a built-in

Error Type Of Void

Thanks for your nice videos.
How i can use this if i dont have to return anything except errors?

Use Errors for SwaggerResponse attributes?

Hi, I like this library since it allows me to pull more and more logic into service classes rather than bloat my controllers. I also want my clients to be able to see possible outcomes of API endpoint calls by utilizing OpenAPI spec "Response" information. Currently I am using the SwaggerResponse attribute, but I watched your video about flow control and now I am trying to incorporate the idea of centralized errors into my app.

I was wondering if one can use the defined errors to annotate controller functions, e.g. like this:
[SwaggerResponseByError(Errors.Authentication.WrongCredentials)]

Do you think something like this can be achieved? Would you consider it sensible doing so? Thanks!

Suggestion: Adding of ErrorType.Domain (or ErrorType.UseCase or ErrorType.BusinessRule)

Hi @amantinband ,
Thank you very much for developing this library. Got to know about it from your video series.
Let me know your thoughts on adding a special error type ErrorType.Domain to represent errors related specific to business rules instead of just ErrorType.Custom. This is because, if its an Error it indeed means a type of Failure so instead of just ErrorType.Failure if we use ErrorType.Domain, it signifies failure in some use case / business rule validation.
Example, consider this business rule: User cannot add/upload more than 3 products to sell under his/her current FREE subscription.
Can you please share your thoughts on this.

โŒ ErrorType NotAuthorized

Hi,
it would be nice to have a new value on the enum ErrorType to track down the NotAuthorized operations.

What is your thought? ๐Ÿ˜Š

IsError = true; Errors.Length = 0;

For some reason, when I return an empty list of Errors, I get IsError = true.

Maybe there's a better way to design this on my end, but I'd like to use ErrorOr methods to also check for null values and return null or at least IsError = false and Value = null when working with domain methods that do things like TryCreate.

Code Snippet:

/// <summary>
/// Ensures Positive Integer.
/// </summary>
public class PositiveInt : ValueObject
{
	public int Value { get; }

	public PositiveInt(int value, string propertyName)
	{
		value.Throw(paramName: propertyName).IfNegativeOrZero();
		Value = value;
	}

	/// <summary>
	/// Trys to create <see cref="PositiveInt"/>. Returns Error if it fails.
	/// <remarks>When value is null, will return IsError = true with empty list of errors.</remarks>
	/// </summary>
	/// <param name="value">Value of <see cref="PositiveInt"/>.</param>
	/// <param name="propertyName">Property <see cref="PositiveInt"/> is being assigned to.</param>
	/// <returns><see cref="PositiveInt"/> or Validation Error.</returns>
	public static ErrorOr<PositiveInt> TryCreate(int? value, string propertyName)
	{
		if (!value.HasValue)
		{
			return new List<Error>();
		}

		if (!int.IsPositive(value.Value))
		{
			return Error.Validation("PositiveInt.Invalid", $"{propertyName} must be larger than 0.");
		}

		return new PositiveInt(value.Value, propertyName);
	}

	/// <inheritdoc />
	protected override IEnumerable<object> GetEqualityComponents()
	{
		yield return Value;
	}
}

When I create a test where the value property of TryCreate is null, I get IsError = true and actual.ErrorsOrEmptyList.Should().BeEmpty() = true.

What I'd ideally like to do is just return null, but then I'd have to make the return value for the method ErrorOr<PositiveInt>?, which results in a lot of hassle. However, I also feel like I should be able to return an empty list of Error and IsError should still be false?

I'm somewhat new to this library, so maybe there's a better way to do what I'm trying to accomplish. I'm open to suggestions.

Thanks!

Cannot implicitly convert type with IEnumerable

Hi Amichai, I have a problem when trying to wrap an IEnumerable to an ErrorOr<IEnumerable>:

using ErrorOr;

class Foo {
    public ErrorOr<IEnumerable<int>> wrap() {
        IEnumerable<int> nums = new List<int>(){ 1, 2 };
        return nums;   
    }
}

VSCode gives me an error:

Cannot implicitly convert type 'System.Collections.Generic.IEnumerable<int>' to 'ErrorOr.ErrorOr<System.Collections.Generic.IEnumerable<int>>' [BuberDinner.Api]csharp(CS0029)
No quick fixes available

I find a workaround: changes IEnumerable<int> tools to List<int> tools:

using ErrorOr;

class Foo {
    public ErrorOr<IEnumerable<int>> wrap() {
        List<int> nums = new List<int>(){ 1, 2 };
        return nums;   
    }
}

ErrorType of Cancelled

Awesome videos and NuGet package.
It would be nice to have a cancelled error type to handle the user cancelled scenarios for APIs etc

Question: `ErrorOr<>.Errors` as readonly collection

Is ErrorOr designed so that you can mutate its state (List<Error> Errors), or is there any other reason for this design choice?

Using IReadOnlyList<> (or even IReadOnlyCollection<> if accessing very first error is not needed) would improve (imo):

  1. Signature - giving hint that this is immutable data structure
  2. No need for allocating new List<Error> when accessing errors (though this should be relatively cheap, but still additional allocation)
    • both caching no errors list and empty one
  3. Only drawback I can see is that to ensure no one tempers with input, copy of input list is needed (in ctor ErrorOr(List<Error> errors))

I'm really interested knowing specific reason (of course changing it to IReadOnly* is breaking, obviously)

Non generic ErrorOr possible?

Scattered throughout our code base I see return types of ErrorOr. I think this comes from legacy code (pre the use of ErrorOr) where the bool indicated success or failure. It strikes me that the presence/absence of errors indicates the same. Is it possible to return ErrorOr (none-generic) so I can just test the IsError status to indicate success or failure? I guess it may be an issue with how to return the discriminated union for success because it then becomes ErrorOr!

Add ability to show result and errors

Unless I'm missing something, it would be nice to be able to use both value and errors. In the specific scenario I'm imagining, one would try to "BatchUpdate" via the API:

The command handler runs through and verifies that each object exists in the database exists (or not). For each item that doesn't exist in the database, generate an error, and for each item that does exist, go ahead and update. The handler then returns both the successes and failures (errors), that can be mapped to a response.

Similar to another issue, this would allow us to use IsError to set the Success to false on the response, but show errors and success values.

How to Register IPipelineBehavior Validation with MediatR using ErrorOr<TResult> instead TResult

Hi,

I tried to implement MeaditR Commands/Queries Validator with ErrorOr library. Follow your documentation [https://github.com/amantinband/error-or#dropping-the-exceptions-throwing-logic] with some little changes(in bold) ValidationBehavior class is below:

public class ValidationBehavior<TRequest, TResult> : IPipelineBehavior<TRequest, ErrorOr>
where TRequest : IRequest<ErrorOr>
{
private readonly IValidator? _validator;

public ValidationBehavior(IValidator<TRequest>? validator = null)
{
    _validator = validator;
}

public async Task<ErrorOr<TResult>> Handle(
    TRequest request,
    CancellationToken cancellationToken,
    RequestHandlerDelegate<ErrorOr<TResult>> next)
{
    if (_validator == null)
    {
        return await next();
    }

    var validationResult = **await** _validator.**ValidateAsync**(request);

    if (validationResult.**IsValid**)
    {
        return validationResult.Errors
           .ConvertAll(validationFailure => Error.Validation(
               code: validationFailure.PropertyName,
               description: validationFailure.ErrorMessage));
    }

    return await next();
}

}

But above mentioned class never triggered.

Static class ConfigureServices.cs where is services registered is below:

using System.Reflection;
using FluentValidation;
using MediatR;

namespace Microsoft.Extensions.DependencyInjection;
public static class ConfigureServices
{
public static IServiceCollection AddApplicationServices(this IServiceCollection services)
{
services.AddHttpContextAccessor();

    services.AddValidatorsFromAssembly(Assembly.GetExecutingAssembly());
    services.AddMediatR(Assembly.GetExecutingAssembly());

    services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>));

    return services;
}

}

Please can you give me advice how to properly register ValidationBehavior class ?

Thanks in advance. :-)

Usage of Library to Replace null return type

Hi,

I think it's more an overall question/suggestion :-).
Was this library also created to replace null return types in my application or just to prevent to throw exceptions and get better Error Results?
Because if I would use this to prevent null return types I think the basic naming "ErrorOr" is not the best, when you read "everywhere" in your ValueObjects oder DomainLogic "ErrorOr<>" return type looks strange.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.