using DigitalData.Core.Exceptions; using MediatR; using System.Collections; namespace EnvelopeGenerator.Application.Common.Extensions; /// /// Extension methods for that provide a fluent API for enforcing non-null responses. /// public static class MediatorExtensions { /// /// Begins a fluent chain that sends and lets you choose how to handle a null or empty response. /// Usage: /// /// await sender.GetOr(query).ThrowNotFound(); /// await sender.GetOr(query, cancel).Throw(() => new MyException()); /// /// public static GetOrContext GetOr(this ISender sender, IRequest request, CancellationToken cancel = default) => new(sender, request, cancel); } /// /// Holds a pending MediatR request and exposes Throw… methods that send the request /// and throw a chosen exception when the response is null or an empty collection. /// /// The expected response type. public readonly struct GetOrContext { private readonly ISender _sender; private readonly IRequest _request; private readonly CancellationToken _cancel; internal GetOrContext(ISender sender, IRequest request, CancellationToken cancel) { _sender = sender; _request = request; _cancel = cancel; } /// /// Sends the request and throws the exception produced by /// when the response is null or an empty collection. /// public async Task Throw(Func exceptionFactory) { if (await _sender.Send(_request, _cancel) is TResponse res) { // string implements IEnumerable, so "" would be treated as an empty collection without this guard. if (res is not string && res is IEnumerable enumerable && !enumerable.Cast().Any()) throw exceptionFactory(); return res; } throw exceptionFactory(); } /// /// Sends the request and throws when the response is null or an empty collection. /// public Task ThrowNotFound(string? message = null) => Throw(() => new NotFoundException(message ?? $"The requested resource of type {typeof(TResponse).Name} was not found.")); /// /// Sends the request and throws when the response is null or an empty collection. /// public Task ThrowInvalidOperation(string? message = null) => Throw(() => new InvalidOperationException(message ?? $"The operation for {typeof(TResponse).Name} returned no result.")); /// /// Sends the request and throws when the response is null or an empty collection. /// public Task ThrowBadRequest(string? message = null) => Throw(() => new BadRequestException(message ?? $"The request for {typeof(TResponse).Name} is invalid.")); }