mirror of
https://github.com/tgstation/tgstation-server.git
synced 2026-08-22 04:27:19 +01:00
Better support NoContent results in authorities
This commit is contained in:
@@ -53,6 +53,16 @@ namespace Tgstation.Server.Host.Authority.Core
|
||||
new ErrorMessageResponse(),
|
||||
HttpFailureResponse.Unauthorized);
|
||||
|
||||
/// <summary>
|
||||
/// Generates a <see cref="HttpFailureResponse.Forbidden"/> type <see cref="AuthorityResponse{TResult}"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="TResult">The <see cref="Type"/> of the <see cref="AuthorityResponse{TResult}.Result"/>.</typeparam>
|
||||
/// <returns>A new, errored <see cref="AuthorityResponse{TResult}"/>.</returns>
|
||||
protected static AuthorityResponse<TResult> Gone<TResult>()
|
||||
=> new(
|
||||
new ErrorMessageResponse(),
|
||||
HttpFailureResponse.Gone);
|
||||
|
||||
/// <summary>
|
||||
/// Generates a <see cref="HttpFailureResponse.Forbidden"/> type <see cref="AuthorityResponse{TResult}"/>.
|
||||
/// </summary>
|
||||
@@ -73,6 +83,17 @@ namespace Tgstation.Server.Host.Authority.Core
|
||||
new ErrorMessageResponse(),
|
||||
HttpFailureResponse.NotFound);
|
||||
|
||||
/// <summary>
|
||||
/// Generates a <see cref="HttpFailureResponse.Conflict"/> type <see cref="AuthorityResponse{TResult}"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="TResult">The <see cref="Type"/> of the <see cref="AuthorityResponse{TResult}.Result"/>.</typeparam>
|
||||
/// <param name="errorCode">The <see cref="ErrorCode"/>.</param>
|
||||
/// <returns>A new, errored <see cref="AuthorityResponse{TResult}"/>.</returns>
|
||||
protected static AuthorityResponse<TResult> Conflict<TResult>(ErrorCode errorCode)
|
||||
=> new(
|
||||
new ErrorMessageResponse(errorCode),
|
||||
HttpFailureResponse.Conflict);
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AuthorityBase"/> class.
|
||||
/// </summary>
|
||||
|
||||
@@ -13,10 +13,16 @@ namespace Tgstation.Server.Host.Authority.Core
|
||||
public sealed class AuthorityResponse<TResult> : AuthorityResponse
|
||||
{
|
||||
/// <inheritdoc />
|
||||
[MemberNotNullWhen(true, nameof(Result))]
|
||||
[MemberNotNullWhen(true, nameof(SuccessResponse))]
|
||||
[MemberNotNullWhen(true, nameof(IsNoContent))]
|
||||
public override bool Success => base.Success;
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a the <see cref="AuthorityResponse{TResult}"/> is a no content result. Only set on <see cref="Success"/>.
|
||||
/// </summary>
|
||||
[MemberNotNullWhen(false, nameof(Result))]
|
||||
[MemberNotNullWhen(false, nameof(Result))]
|
||||
public bool? IsNoContent => Success ? Result == null : null;
|
||||
|
||||
/// <summary>
|
||||
/// The success <typeparamref name="TResult"/>.
|
||||
/// </summary>
|
||||
@@ -47,5 +53,13 @@ namespace Tgstation.Server.Host.Authority.Core
|
||||
Result = result ?? throw new ArgumentNullException(nameof(result));
|
||||
SuccessResponse = httpResponse;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AuthorityResponse{TResult}"/> class.
|
||||
/// </summary>
|
||||
/// <remarks>This generates an HTTP 204 response.</remarks>
|
||||
public AuthorityResponse()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,11 +13,12 @@ namespace Tgstation.Server.Host.Authority.Core
|
||||
/// Throws a <see cref="ErrorMessageException"/> for errored <paramref name="authorityResponse"/>s.
|
||||
/// </summary>
|
||||
/// <param name="authorityResponse">The potentially errored <paramref name="authorityResponse"/>.</param>
|
||||
static void ThrowGraphQLErrorIfNecessary(AuthorityResponse authorityResponse)
|
||||
/// <param name="errorOnMissing">If an error should be raised for <see cref="HttpFailureResponse.NotFound"/> and <see cref="HttpFailureResponse.Gone"/> failures.</param>
|
||||
static void ThrowGraphQLErrorIfNecessary(AuthorityResponse authorityResponse, bool errorOnMissing)
|
||||
{
|
||||
if (authorityResponse.Success
|
||||
|| authorityResponse.FailureResponse.Value == HttpFailureResponse.NotFound
|
||||
|| authorityResponse.FailureResponse.Value == HttpFailureResponse.Gone)
|
||||
|| ((authorityResponse.FailureResponse.Value == HttpFailureResponse.NotFound
|
||||
|| authorityResponse.FailureResponse.Value == HttpFailureResponse.Gone) && !errorOnMissing))
|
||||
return;
|
||||
|
||||
var fallbackString = authorityResponse.FailureResponse.ToString()!;
|
||||
@@ -39,33 +40,41 @@ namespace Tgstation.Server.Host.Authority.Core
|
||||
ArgumentNullException.ThrowIfNull(authorityInvoker);
|
||||
|
||||
var authorityResponse = await authorityInvoker(Authority);
|
||||
ThrowGraphQLErrorIfNecessary(authorityResponse);
|
||||
ThrowGraphQLErrorIfNecessary(authorityResponse, true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
async ValueTask<TApiModel?> IGraphQLAuthorityInvoker<TAuthority>.Invoke<TResult, TApiModel>(Func<TAuthority, ValueTask<AuthorityResponse<TResult>>> authorityInvoker)
|
||||
async ValueTask<TApiModel?> IGraphQLAuthorityInvoker<TAuthority>.InvokeAllowMissing<TResult, TApiModel>(Func<TAuthority, ValueTask<AuthorityResponse<TResult>>> authorityInvoker)
|
||||
where TApiModel : default
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(authorityInvoker);
|
||||
|
||||
var authorityResponse = await authorityInvoker(Authority);
|
||||
ThrowGraphQLErrorIfNecessary(authorityResponse);
|
||||
ThrowGraphQLErrorIfNecessary(authorityResponse, false);
|
||||
return authorityResponse.Result;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
async ValueTask<TApiModel?> IGraphQLAuthorityInvoker<TAuthority>.InvokeTransformable<TResult, TApiModel, TTransformer>(Func<TAuthority, ValueTask<AuthorityResponse<TResult>>> authorityInvoker)
|
||||
async ValueTask<TApiModel?> IGraphQLAuthorityInvoker<TAuthority>.InvokeTransformableAllowMissing<TResult, TApiModel, TTransformer>(Func<TAuthority, ValueTask<AuthorityResponse<TResult>>> authorityInvoker)
|
||||
where TApiModel : default
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(authorityInvoker);
|
||||
|
||||
var authorityResponse = await authorityInvoker(Authority);
|
||||
ThrowGraphQLErrorIfNecessary(authorityResponse);
|
||||
ThrowGraphQLErrorIfNecessary(authorityResponse, false);
|
||||
var result = authorityResponse.Result;
|
||||
if (result == null)
|
||||
return default;
|
||||
|
||||
return result.ToApi();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
ValueTask<TApiModel> IGraphQLAuthorityInvoker<TAuthority>.Invoke<TResult, TApiModel>(Func<TAuthority, ValueTask<AuthorityResponse<TResult>>> authorityInvoker)
|
||||
=> ((IGraphQLAuthorityInvoker<TAuthority>)this).InvokeAllowMissing<TResult, TApiModel>(authorityInvoker)!;
|
||||
|
||||
/// <inheritdoc />
|
||||
ValueTask<TApiModel> IGraphQLAuthorityInvoker<TAuthority>.InvokeTransformable<TResult, TApiModel, TTransformer>(Func<TAuthority, ValueTask<AuthorityResponse<TResult>>> authorityInvoker)
|
||||
=> ((IGraphQLAuthorityInvoker<TAuthority>)this).InvokeTransformableAllowMissing<TResult, TApiModel, TTransformer>(authorityInvoker)!;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,18 +14,22 @@ namespace Tgstation.Server.Host.Authority.Core
|
||||
where TAuthority : IAuthority
|
||||
{
|
||||
/// <summary>
|
||||
/// Create an <see cref="IActionResult"/> for a given successfuly <paramref name="authorityResponse"/> API <paramref name="result"/>.
|
||||
/// Create an <see cref="IActionResult"/> for a given successfuly<paramref name="authorityResponse"/>.
|
||||
/// </summary>
|
||||
/// <param name="controller">The <see cref="ApiController"/> to use.</param>
|
||||
/// <param name="result">The resulting <typeparamref name="TApiModel"/> from the <paramref name="authorityResponse"/>.</param>
|
||||
/// <param name="resultTransformer">A <see cref="Func{T, TResult}"/> transforming the <typeparamref name="TResult"/> from the <paramref name="authorityResponse"/> into the <typeparamref name="TApiModel"/>.</param>
|
||||
/// <param name="authorityResponse">The <see cref="AuthorityResponse{TResult}"/>.</param>
|
||||
/// <returns>An <see cref="IActionResult"/> for the <paramref name="authorityResponse"/>.</returns>
|
||||
/// <typeparam name="TResult">The result <see cref="Type"/> returned in the <paramref name="authorityResponse"/>.</typeparam>
|
||||
/// <typeparam name="TApiModel">The REST API result model built from <paramref name="authorityResponse"/>.</typeparam>
|
||||
static IActionResult CreateSuccessfulActionResult<TResult, TApiModel>(ApiController controller, TApiModel result, AuthorityResponse<TResult> authorityResponse)
|
||||
static IActionResult CreateSuccessfulActionResult<TResult, TApiModel>(ApiController controller, Func<TResult, TApiModel> resultTransformer, AuthorityResponse<TResult> authorityResponse)
|
||||
where TApiModel : notnull
|
||||
{
|
||||
if (authorityResponse.IsNoContent!.Value)
|
||||
return controller.NoContent();
|
||||
|
||||
var successResponse = authorityResponse.SuccessResponse;
|
||||
var result = resultTransformer(authorityResponse.Result!);
|
||||
return successResponse switch
|
||||
{
|
||||
HttpSuccessResponse.Ok => controller.Json(result),
|
||||
@@ -95,8 +99,7 @@ namespace Tgstation.Server.Host.Authority.Core
|
||||
if (erroredResult != null)
|
||||
return erroredResult;
|
||||
|
||||
var result = authorityResponse.Result!;
|
||||
return CreateSuccessfulActionResult(controller, result, authorityResponse);
|
||||
return CreateSuccessfulActionResult(controller, result => result, authorityResponse);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -110,9 +113,7 @@ namespace Tgstation.Server.Host.Authority.Core
|
||||
if (erroredResult != null)
|
||||
return erroredResult;
|
||||
|
||||
var result = authorityResponse.Result!;
|
||||
var apiModel = result.ToApi();
|
||||
return CreateSuccessfulActionResult(controller, apiModel, authorityResponse);
|
||||
return CreateSuccessfulActionResult(controller, result => result.ToApi(), authorityResponse);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace Tgstation.Server.Host.Authority
|
||||
/// <typeparam name="TApiModel">The resulting <see cref="Type"/> of the return value.</typeparam>
|
||||
/// <param name="authorityInvoker">The <typeparamref name="TAuthority"/> <see cref="Func{T, TResult}"/> returning a <see cref="ValueTask{TResult}"/> resulting in the <see cref="AuthorityResponse{TResult}"/>.</param>
|
||||
/// <returns>A <see cref="ValueTask{TResult}"/> resulting in the <typeparamref name="TApiModel"/> generated for the resulting <see cref="AuthorityResponse{TResult}"/>.</returns>
|
||||
ValueTask<TApiModel?> Invoke<TResult, TApiModel>(Func<TAuthority, ValueTask<AuthorityResponse<TResult>>> authorityInvoker)
|
||||
ValueTask<TApiModel?> InvokeAllowMissing<TResult, TApiModel>(Func<TAuthority, ValueTask<AuthorityResponse<TResult>>> authorityInvoker)
|
||||
where TResult : TApiModel
|
||||
where TApiModel : notnull;
|
||||
|
||||
@@ -39,7 +39,31 @@ namespace Tgstation.Server.Host.Authority
|
||||
/// <typeparam name="TTransformer">The <see cref="ITransformer{TInput, TOutput}"/> for converting <typeparamref name="TResult"/>s to <typeparamref name="TApiModel"/>s.</typeparam>
|
||||
/// <param name="authorityInvoker">The <typeparamref name="TAuthority"/> <see cref="Func{T, TResult}"/> returning a <see cref="ValueTask{TResult}"/> resulting in the <see cref="AuthorityResponse{TResult}"/>.</param>
|
||||
/// <returns>A <see cref="ValueTask{TResult}"/> resulting in the <typeparamref name="TApiModel"/> generated for the resulting <see cref="AuthorityResponse{TResult}"/>.</returns>
|
||||
ValueTask<TApiModel?> InvokeTransformable<TResult, TApiModel, TTransformer>(Func<TAuthority, ValueTask<AuthorityResponse<TResult>>> authorityInvoker)
|
||||
ValueTask<TApiModel?> InvokeTransformableAllowMissing<TResult, TApiModel, TTransformer>(Func<TAuthority, ValueTask<AuthorityResponse<TResult>>> authorityInvoker)
|
||||
where TResult : notnull, IApiTransformable<TResult, TApiModel, TTransformer>
|
||||
where TApiModel : notnull
|
||||
where TTransformer : ITransformer<TResult, TApiModel>, new();
|
||||
|
||||
/// <summary>
|
||||
/// Invoke a <typeparamref name="TAuthority"/> method and get the non-nullable result.
|
||||
/// </summary>
|
||||
/// <typeparam name="TResult">The <see cref="AuthorityResponse{TResult}.Result"/> <see cref="Type"/>.</typeparam>
|
||||
/// <typeparam name="TApiModel">The resulting <see cref="Type"/> of the return value.</typeparam>
|
||||
/// <param name="authorityInvoker">The <typeparamref name="TAuthority"/> <see cref="Func{T, TResult}"/> returning a <see cref="ValueTask{TResult}"/> resulting in the <see cref="AuthorityResponse{TResult}"/>.</param>
|
||||
/// <returns>A <see cref="ValueTask{TResult}"/> resulting in the <typeparamref name="TApiModel"/> generated for the resulting <see cref="AuthorityResponse{TResult}"/>.</returns>
|
||||
ValueTask<TApiModel> Invoke<TResult, TApiModel>(Func<TAuthority, ValueTask<AuthorityResponse<TResult>>> authorityInvoker)
|
||||
where TResult : TApiModel
|
||||
where TApiModel : notnull;
|
||||
|
||||
/// <summary>
|
||||
/// Invoke a <typeparamref name="TAuthority"/> method and get the non-nullable result.
|
||||
/// </summary>
|
||||
/// <typeparam name="TResult">The <see cref="AuthorityResponse{TResult}.Result"/> <see cref="Type"/>.</typeparam>
|
||||
/// <typeparam name="TApiModel">The resulting <see cref="Type"/> of the return value.</typeparam>
|
||||
/// <typeparam name="TTransformer">The <see cref="ITransformer{TInput, TOutput}"/> for converting <typeparamref name="TResult"/>s to <typeparamref name="TApiModel"/>s.</typeparam>
|
||||
/// <param name="authorityInvoker">The <typeparamref name="TAuthority"/> <see cref="Func{T, TResult}"/> returning a <see cref="ValueTask{TResult}"/> resulting in the <see cref="AuthorityResponse{TResult}"/>.</param>
|
||||
/// <returns>A <see cref="ValueTask{TResult}"/> resulting in the <typeparamref name="TApiModel"/> generated for the resulting <see cref="AuthorityResponse{TResult}"/>.</returns>
|
||||
ValueTask<TApiModel> InvokeTransformable<TResult, TApiModel, TTransformer>(Func<TAuthority, ValueTask<AuthorityResponse<TResult>>> authorityInvoker)
|
||||
where TResult : notnull, IApiTransformable<TResult, TApiModel, TTransformer>
|
||||
where TApiModel : notnull
|
||||
where TTransformer : ITransformer<TResult, TApiModel>, new();
|
||||
|
||||
Reference in New Issue
Block a user