Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
11 changes: 11 additions & 0 deletions src/Ardalis.Result.AspNetCore/ActionResultExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,17 @@ internal static ActionResult ToActionResult(this ControllerBase controller, IRes
result.Location).Uri.AbsoluteUri;

return controller.Created(locationUri, result.GetValue());
case ResultStatus.Accepted:
if(string.IsNullOrEmpty(result.Location))
return controller.Accepted((string?)null, result.GetValue());

var httpRequestAccepted = controller.HttpContext.Request;
var locationUriAccepted = new UriBuilder(httpRequestAccepted.Scheme,
httpRequestAccepted.Host.Host,
httpRequestAccepted.Host.Port ?? -1,
result.Location).Uri.AbsoluteUri;

return controller.Accepted(locationUriAccepted, result.GetValue());
default:
return resultStatusOptions.ResponseType == null
? (ActionResult)controller.StatusCode(statusCode)
Expand Down
3 changes: 2 additions & 1 deletion src/Ardalis.Result.AspNetCore/MinimalApiResultExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ internal static Microsoft.AspNetCore.Http.IResult ToMinimalApiResult(this IResul
result.Status switch
{
ResultStatus.Ok => result is Result ? Results.Ok() : Results.Ok(result.GetValue()),
ResultStatus.Created => Results.Created("", result.GetValue()),
ResultStatus.Created => Results.Created(result.Location ?? "", result.GetValue()),
ResultStatus.Accepted => Results.Accepted(result.Location ?? "", result.GetValue()),
ResultStatus.NoContent => Results.NoContent(),
ResultStatus.NotFound => NotFoundEntity(result),
ResultStatus.Unauthorized => UnAuthorized(result),
Expand Down
1 change: 1 addition & 0 deletions src/Ardalis.Result.AspNetCore/ResultStatusMap.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public ResultStatusMap AddDefaultMap()

=> For(ResultStatus.Ok, HttpStatusCode.OK)
.For(ResultStatus.Created, HttpStatusCode.Created)
.For(ResultStatus.Accepted, HttpStatusCode.Accepted)
.For(ResultStatus.Error, (HttpStatusCode)422, resultStatusOptions => resultStatusOptions
.With(UnprocessableEntity))
.For(ResultStatus.Forbidden, HttpStatusCode.Forbidden)
Expand Down
5 changes: 5 additions & 0 deletions src/Ardalis.Result/IResultExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ public static class IResultExtensions
/// </summary>
public static bool IsCreated(this IResult result) => result.Status == ResultStatus.Created;

/// <summary>
/// Returns true if the result is accepted (status is Accepted).
/// </summary>
public static bool IsAccepted(this IResult result) => result.Status == ResultStatus.Accepted;

/// <summary>
/// Returns true if the result is an error (status is Error).
/// </summary>
Expand Down
24 changes: 24 additions & 0 deletions src/Ardalis.Result/Result.Void.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,30 @@ public static Result<T> Created<T>(T value, string location)
return Result<T>.Created(value, location);
}

/// <summary>
/// Represents a successful operation where the request has been accepted for processing, but the processing has not been completed.
/// Accepts a value as the result of the operation.
/// </summary>
/// <param name="value">Sets the Value property</param>
/// <returns>A Result<typeparamref name="T"/></returns>
public static Result<T> Accepted<T>(T value)
{
return Result<T>.Accepted(value);
}

/// <summary>
/// Represents a successful operation where the request has been accepted for processing, but the processing has not been completed.
/// Accepts a value as the result of the operation.
/// Accepts a location for monitoring the status.
/// </summary>
/// <param name="value">Sets the Value property</param>
/// <param name="location">The location where the status of the operation can be monitored</param>
/// <returns>A Result<typeparamref name="T"/></returns>
public static Result<T> Accepted<T>(T value, string location)
{
return Result<T>.Accepted(value, location);
}

/// <summary>
/// Represents an error that occurred during the execution of the service.
/// Error messages may be provided and will be exposed via the Errors property.
Expand Down
19 changes: 18 additions & 1 deletion src/Ardalis.Result/Result.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ protected Result() { }
[JsonInclude]
public ResultStatus Status { get; protected set; } = ResultStatus.Ok;

public bool IsSuccess => Status is ResultStatus.Ok or ResultStatus.NoContent or ResultStatus.Created;
public bool IsSuccess => Status is ResultStatus.Ok or ResultStatus.NoContent or ResultStatus.Created or ResultStatus.Accepted;

[JsonInclude]
public string SuccessMessage { get; protected set; } = string.Empty;
Expand Down Expand Up @@ -106,6 +106,23 @@ public PagedResult<T> ToPagedResult(PagedInfo pagedInfo)
/// <returns>A Result<typeparamref name="T"/> with status Created.</returns>
public static Result<T> Created(T value, string location) => new(ResultStatus.Created) { Value = value, Location = location };

/// <summary>
/// Represents a successful operation where the request has been accepted for processing, but the processing has not been completed.
/// </summary>
/// <typeparam name="T">The type of the value returned.</typeparam>
/// <returns>A Result<typeparamref name="T"/> with status Accepted.</returns>
public static Result<T> Accepted(T value) => new(ResultStatus.Accepted) { Value = value };

/// <summary>
/// Represents a successful operation where the request has been accepted for processing, but the processing has not been completed.
/// Sets the Location property to the provided value.
/// </summary>
/// <typeparam name="T">The type of the value returned.</typeparam>
/// <param name="value">The value to return.</param>
/// <param name="location">The URL indicating where the status of the operation can be monitored.</param>
/// <returns>A Result<typeparamref name="T"/> with status Accepted.</returns>
public static Result<T> Accepted(T value, string location) => new(ResultStatus.Accepted) { Value = value, Location = location };

/// <summary>
/// Represents an error that occurred during the execution of the service.
/// A single error message may be provided and will be exposed via the Errors property.
Expand Down
1 change: 1 addition & 0 deletions src/Ardalis.Result/ResultStatus.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ public enum ResultStatus
{
Ok,
Created,
Accepted,
Error,
Forbidden,
Unauthorized,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ public void ToMinimalApiResultHandlesAllResultStatusValues()
foreach (ResultStatus resultStatus in Enum.GetValues(typeof(ResultStatus)))
{
#if NET7_0
// Results.Created does not accept empty string URI in net7
if (resultStatus == ResultStatus.Created)
// Results.Created and Results.Accepted do not accept empty string URI in net7
if (resultStatus == ResultStatus.Created || resultStatus == ResultStatus.Accepted)
{
continue;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public void TranslateAttributeOnAction()

convention.Apply(actionModel);

Assert.Equal(9, actionModel.Filters.Where(f => f is ProducesResponseTypeAttribute).Count());
Assert.Equal(10, actionModel.Filters.Where(f => f is ProducesResponseTypeAttribute).Count());

Assert.Contains(actionModel.Filters, f => ProducesResponseTypeAttribute(f, 204, typeof(void)));
Assert.Contains(actionModel.Filters, f => ProducesResponseTypeAttribute(f, 404, typeof(ProblemDetails)));
Expand All @@ -29,6 +29,7 @@ public void TranslateAttributeOnAction()
Assert.Contains(actionModel.Filters, f => ProducesResponseTypeAttribute(f, 422, typeof(ProblemDetails)));
Assert.Contains(actionModel.Filters, f => ProducesResponseTypeAttribute(f, 500, typeof(ProblemDetails)));
Assert.Contains(actionModel.Filters, f => ProducesResponseTypeAttribute(f, 503, typeof(ProblemDetails)));
Assert.Contains(actionModel.Filters, f => ProducesResponseTypeAttribute(f, 202, typeof(void)));
}

[Fact]
Expand All @@ -43,7 +44,7 @@ public void TranslateAttributeOnController()

convention.Apply(actionModel);

Assert.Equal(9, actionModel.Filters.Where(f => f is ProducesResponseTypeAttribute).Count());
Assert.Equal(10, actionModel.Filters.Where(f => f is ProducesResponseTypeAttribute).Count());

Assert.Contains(actionModel.Filters, f => ProducesResponseTypeAttribute(f, 204, typeof(void)));
Assert.Contains(actionModel.Filters, f => ProducesResponseTypeAttribute(f, 404, typeof(ProblemDetails)));
Expand All @@ -54,6 +55,7 @@ public void TranslateAttributeOnController()
Assert.Contains(actionModel.Filters, f => ProducesResponseTypeAttribute(f, 422, typeof(ProblemDetails)));
Assert.Contains(actionModel.Filters, f => ProducesResponseTypeAttribute(f, 500, typeof(ProblemDetails)));
Assert.Contains(actionModel.Filters, f => ProducesResponseTypeAttribute(f, 503, typeof(ProblemDetails)));
Assert.Contains(actionModel.Filters, f => ProducesResponseTypeAttribute(f, 202, typeof(void)));
}

[Fact]
Expand Down Expand Up @@ -83,7 +85,7 @@ public void ExistingProducesResponseTypeAttributePreserved()

convention.Apply(actionModel);

Assert.Equal(9, actionModel.Filters.Where(f => f is ProducesResponseTypeAttribute).Count());
Assert.Equal(10, actionModel.Filters.Where(f => f is ProducesResponseTypeAttribute).Count());

Assert.Contains(actionModel.Filters, f => ProducesResponseTypeAttribute(f, 204, typeof(void)));
Assert.Contains(actionModel.Filters, f => ProducesResponseTypeAttribute(f, 404, typeof(ProblemDetails)));
Expand All @@ -94,6 +96,7 @@ public void ExistingProducesResponseTypeAttributePreserved()
Assert.Contains(actionModel.Filters, f => ProducesResponseTypeAttribute(f, 422, typeof(ProblemDetails)));
Assert.Contains(actionModel.Filters, f => ProducesResponseTypeAttribute(f, 500, typeof(ProblemDetails)));
Assert.Contains(actionModel.Filters, f => ProducesResponseTypeAttribute(f, 503, typeof(ProblemDetails)));
Assert.Contains(actionModel.Filters, f => ProducesResponseTypeAttribute(f, 202, typeof(void)));
}

[Theory]
Expand All @@ -110,7 +113,7 @@ public void ResultWithValue(string actionName, Type expectedType)

convention.Apply(actionModel);

Assert.Equal(10, actionModel.Filters.Where(f => f is ProducesResponseTypeAttribute).Count());
Assert.Equal(11, actionModel.Filters.Where(f => f is ProducesResponseTypeAttribute).Count());

Assert.Contains(actionModel.Filters, f => ProducesResponseTypeAttribute(f, 200, expectedType));
Assert.Contains(actionModel.Filters, f => ProducesResponseTypeAttribute(f, 204, typeof(void)));
Expand All @@ -122,5 +125,6 @@ public void ResultWithValue(string actionName, Type expectedType)
Assert.Contains(actionModel.Filters, f => ProducesResponseTypeAttribute(f, 422, typeof(ProblemDetails)));
Assert.Contains(actionModel.Filters, f => ProducesResponseTypeAttribute(f, 500, typeof(ProblemDetails)));
Assert.Contains(actionModel.Filters, f => ProducesResponseTypeAttribute(f, 503, typeof(ProblemDetails)));
Assert.Contains(actionModel.Filters, f => ProducesResponseTypeAttribute(f, 202, typeof(void)));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public void RemoveResultStatus()

convention.Apply(actionModel);

Assert.Equal(7, actionModel.Filters.Where(f => f is ProducesResponseTypeAttribute).Count());
Assert.Equal(8, actionModel.Filters.Where(f => f is ProducesResponseTypeAttribute).Count());

Assert.Contains(actionModel.Filters, f => ProducesResponseTypeAttribute(f, 204, typeof(void)));
Assert.Contains(actionModel.Filters, f => ProducesResponseTypeAttribute(f, 404, typeof(ProblemDetails)));
Expand All @@ -30,6 +30,7 @@ public void RemoveResultStatus()
Assert.Contains(actionModel.Filters, f => ProducesResponseTypeAttribute(f, 422, typeof(ProblemDetails)));
Assert.Contains(actionModel.Filters, f => ProducesResponseTypeAttribute(f, 500, typeof(ProblemDetails)));
Assert.Contains(actionModel.Filters, f => ProducesResponseTypeAttribute(f, 503, typeof(ProblemDetails)));
Assert.Contains(actionModel.Filters, f => ProducesResponseTypeAttribute(f, 202, typeof(void)));
}

[Fact]
Expand All @@ -46,7 +47,7 @@ public void ChangeResultStatus()

convention.Apply(actionModel);

Assert.Equal(8, actionModel.Filters.Where(f => f is ProducesResponseTypeAttribute).Count());
Assert.Equal(9, actionModel.Filters.Where(f => f is ProducesResponseTypeAttribute).Count());

Assert.Contains(actionModel.Filters, f => ProducesResponseTypeAttribute(f, 204, typeof(void)));
Assert.Contains(actionModel.Filters, f => ProducesResponseTypeAttribute(f, 404, typeof(ProblemDetails)));
Expand All @@ -56,6 +57,7 @@ public void ChangeResultStatus()
Assert.Contains(actionModel.Filters, f => ProducesResponseTypeAttribute(f, 409, typeof(ProblemDetails)));
Assert.Contains(actionModel.Filters, f => ProducesResponseTypeAttribute(f, 500, typeof(void)));
Assert.Contains(actionModel.Filters, f => ProducesResponseTypeAttribute(f, 503, typeof(ProblemDetails)));
Assert.Contains(actionModel.Filters, f => ProducesResponseTypeAttribute(f, 202, typeof(void)));
}

[Theory]
Expand All @@ -80,7 +82,7 @@ public void ChangeResultStatus_ForSpecificMethods(string actionName, Type expect

convention.Apply(actionModel);

Assert.Equal(9, actionModel.Filters.Where(f => f is ProducesResponseTypeAttribute).Count());
Assert.Equal(10, actionModel.Filters.Where(f => f is ProducesResponseTypeAttribute).Count());

Assert.Contains(actionModel.Filters, f => ProducesResponseTypeAttribute(f, expectedStatusCode, expectedType));
Assert.Contains(actionModel.Filters, f => ProducesResponseTypeAttribute(f, 404, typeof(ProblemDetails)));
Expand All @@ -91,6 +93,7 @@ public void ChangeResultStatus_ForSpecificMethods(string actionName, Type expect
Assert.Contains(actionModel.Filters, f => ProducesResponseTypeAttribute(f, 422, typeof(ProblemDetails)));
Assert.Contains(actionModel.Filters, f => ProducesResponseTypeAttribute(f, 500, typeof(ProblemDetails)));
Assert.Contains(actionModel.Filters, f => ProducesResponseTypeAttribute(f, 503, typeof(ProblemDetails)));
Assert.Contains(actionModel.Filters, f => ProducesResponseTypeAttribute(f, 202, typeof(void)));
}

[Theory]
Expand All @@ -114,7 +117,7 @@ public void ChangeResultStatus_ForDeleteMethods(string actionName, Type expected

convention.Apply(actionModel);

Assert.Equal(10, actionModel.Filters.Where(f => f is ProducesResponseTypeAttribute).Count());
Assert.Equal(11, actionModel.Filters.Where(f => f is ProducesResponseTypeAttribute).Count());

Assert.Contains(actionModel.Filters, f => ProducesResponseTypeAttribute(f, expectedStatusCode, expectedType));
Assert.Contains(actionModel.Filters, f => ProducesResponseTypeAttribute(f, 204, typeof(void)));
Expand All @@ -126,5 +129,6 @@ public void ChangeResultStatus_ForDeleteMethods(string actionName, Type expected
Assert.Contains(actionModel.Filters, f => ProducesResponseTypeAttribute(f, 422, typeof(ProblemDetails)));
Assert.Contains(actionModel.Filters, f => ProducesResponseTypeAttribute(f, 500, typeof(ProblemDetails)));
Assert.Contains(actionModel.Filters, f => ProducesResponseTypeAttribute(f, 503, typeof(ProblemDetails)));
Assert.Contains(actionModel.Filters, f => ProducesResponseTypeAttribute(f, 202, typeof(void)));
}
}
11 changes: 11 additions & 0 deletions tests/Ardalis.Result.UnitTests/IResultExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,17 @@ public void IsCreated_ReturnsTrue_WhenStatusIsCreated()
Assert.True(result.IsCreated());
}

[Fact]
public void IsAccepted_ReturnsTrue_WhenStatusIsAccepted()
{
// Arrange & Act
var foo = new Foo("Bar");
var result = Result<Foo>.Accepted(foo);

// Assert
Assert.True(result.IsAccepted());
}

[Fact]
public void IsError_ReturnsTrue_WhenStatusIsError()
{
Expand Down
43 changes: 43 additions & 0 deletions tests/Ardalis.Result.UnitTests/ResultConstructor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -361,4 +361,47 @@ public void InitializedIsSuccessTrueForCreatedWithLocationFactoryCall()

Assert.True(result.IsSuccess);
}

[Theory]
[InlineData(null)]
[InlineData(123)]
[InlineData("test value")]
public void InitializesStatusToAcceptedGivenAcceptedFactoryCall(object value)
{
var result = Result<object>.Accepted(value);

Assert.Equal(ResultStatus.Accepted, result.Status);
Assert.Equal(result.Location, string.Empty);
Assert.True(result.IsSuccess);
}

[Theory]
[InlineData(null)]
[InlineData(123)]
[InlineData("test value")]
public void InitializesStatusToAcceptedAndSetLocationGivenAcceptedFactoryCall(object value)
{
string location = "status/12345";
var result = Result<object>.Accepted(value, location);

Assert.Equal(ResultStatus.Accepted, result.Status);
Assert.Equal(location, result.Location);
Assert.True(result.IsSuccess);
}

[Fact]
public void InitializedIsSuccessTrueForAcceptedFactoryCall()
{
var result = Result<object>.Accepted(new object());

Assert.True(result.IsSuccess);
}

[Fact]
public void InitializedIsSuccessTrueForAcceptedWithLocationFactoryCall()
{
var result = Result<object>.Accepted(new object(), "status/endpoint");

Assert.True(result.IsSuccess);
}
}