Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Eventuous should always return Result<TState> #317

Open
alexeyzimarev opened this issue Mar 10, 2024 · 2 comments
Open

Eventuous should always return Result<TState> #317

alexeyzimarev opened this issue Mar 10, 2024 · 2 comments

Comments

@alexeyzimarev
Copy link
Contributor

alexeyzimarev commented Mar 10, 2024

Follow up #270 (comment)

@alexeyzimarev
Copy link
Contributor Author

I now changed the controller base signature to this:

public abstract class CommandHttpApiBase<TState>(IStateCommandService<TState> service, MessageMap? commandMap = null) : ControllerBase
    where TState : State<TState>, new() {

It allows using both aggregate and functional command services since both of them implement the IStateCommandService interface.

The interface itself looks like this:

public interface IStateCommandService<TState> where TState : State<TState>, new() {
    Task<Result<TState>> Handle<TCommand>(TCommand command, CancellationToken cancellationToken) where TCommand : class;
}

I think it will work for your code, the AsActionResult function remains virtual. If people don't want to use custom result, the controller can return Result<TState> directly, and the OpenAPI will be correct without any additional code.

@LockTar
Copy link
Contributor

LockTar commented Mar 15, 2024

Hi @alexeyzimarev,

I think this can work. I need to test it of course. But if we use the "Users" sample from my previous comment it should look something like this?

Then it's immediately somewhat documented :-)

Out of the box (no custom result):

[Authorize]
[Route("/users")]
public class UsersCommandApi
{
    public UsersCommandApi(CommandHttpApiBase<User> service) { }

    // Actions here...
}

With custom result:

[Authorize]
[Route("/users")]
public class UsersCommandApi
{
    public UsersCommandApi(UserCommandService service) { }

    [HttpPost]
    [Route("add")]
    [SwaggerResponse((int)HttpStatusCode.OK, "OK", typeof(UserResult))]
    [SwaggerResponse((int)HttpStatusCode.BadRequest, "Bad Request", typeof(ValidationProblemDetailsException))]
    public Task<ActionResult<CustomUserResult>> AddUser(
        [FromBody] AddUserCommand cmd, CancellationToken cancellationToken
    )
    {
        return Handle(cmd, cancellationToken);
    }
}

public class CustomUserResult
{
    public string CustomName { get; set; }
    public string CustomEmail { get; set; }
}

// implement interface IStateCommandService<User>
public class UserCommandService : IStateCommandService<User>
{
    public Task<Result<User>> Handle<TCommand>(TCommand command, CancellationToken cancellationToken) where TCommand : class
    {
        // handle command here...
    }

    protected override ActionResult AsActionResult(Result result)
    {
        // If result is ErrorResult, map it to an error response
        // MapValidationExceptionAsValidationProblemDetails is a method that maps a validation exception to a validation problem details response
        // else, map it to a success response of the type CustomUserResult

        // Removed for brevity
    }

    private BadRequestObjectResult MapValidationExceptionAsValidationProblemDetails(ErrorResult error)
    {
        // Map validation exception to a validation problem details response

        // Removed for brevity
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants