Files
EnvelopeGenerator/EnvelopeGenerator.Application/ThirdPartyModules/Queries/ReadThirdPartyModuleLicenseQuery.cs
TekH b4be718994 Add query/handler to fetch third-party module license text
Introduced ReadThirdPartyModuleLicenseQuery and its handler to retrieve the license text of a third-party module by Id or Name (mutually exclusive), with optional filtering for active modules. Includes validation and exception handling for invalid input, not found, or multiple matches.
2026-04-14 12:39:31 +02:00

93 lines
3.6 KiB
C#

using DigitalData.Core.Abstraction.Application.Repository;
using DigitalData.Core.Exceptions;
using EnvelopeGenerator.Domain.Entities;
using MediatR;
using Microsoft.EntityFrameworkCore;
namespace EnvelopeGenerator.Application.ThirdPartyModules.Queries;
/// <summary>
/// Query to read the license text of a third-party module.
/// Either <see cref="Id"/> or <see cref="Name"/> must be provided, but not both.
/// </summary>
public record ReadThirdPartyModuleLicenseQuery : IRequest<string>
{
/// <summary>
/// The unique identifier of the third-party module (optional).
/// </summary>
public int? Id { get; init; }
/// <summary>
/// The name of the third-party module (optional).
/// </summary>
public string? Name { get; init; }
/// <summary>
/// Whether to filter only active modules. Defaults to <c>true</c>.
/// </summary>
public bool Active { get; init; } = true;
}
/// <summary>
/// Handles <see cref="ReadThirdPartyModuleLicenseQuery"/> and returns the license text of a third-party module.
/// </summary>
public class ReadThirdPartyModuleLicenseQueryHandler : IRequestHandler<ReadThirdPartyModuleLicenseQuery, string>
{
private readonly IRepository<ThirdPartyModule> _repository;
/// <summary>
/// Initializes a new instance of the <see cref="ReadThirdPartyModuleLicenseQueryHandler"/> class.
/// </summary>
/// <param name="repository">The repository for accessing third-party modules.</param>
public ReadThirdPartyModuleLicenseQueryHandler(IRepository<ThirdPartyModule> repository)
{
_repository = repository;
}
/// <summary>
/// Handles the query by filtering on Id or Name and returning the license text.
/// </summary>
/// <param name="request">The query parameters.</param>
/// <param name="cancel">A cancellation token.</param>
/// <returns>The license text of the matching third-party module.</returns>
/// <exception cref="BadRequestException">
/// Thrown when neither Id nor Name is provided, or when both are provided.
/// </exception>
/// <exception cref="InvalidOperationException">
/// Thrown when multiple modules match the given criteria, indicating a data integrity issue.
/// </exception>
/// <exception cref="NotFoundException">
/// Thrown when no module matches the given criteria.
/// </exception>
public async Task<string> Handle(ReadThirdPartyModuleLicenseQuery request, CancellationToken cancel)
{
if (request.Id is null && request.Name is null)
throw new BadRequestException("Either Id or Name must be provided.");
if (request.Id is not null && request.Name is not null)
throw new BadRequestException("Only one of Id or Name must be provided, not both.");
var query = _repository.Query
.Where(m => m.Active == request.Active);
if (request.Id is int id)
query = query.Where(m => m.Id == id);
if (request.Name is string name)
query = query.Where(m => m.Name == name);
var modules = await query
.Take(2)
.ToListAsync(cancel);
if (modules.Count > 1)
throw new InvalidOperationException(
$"Data integrity violation: multiple third-party modules found for the given criteria (Id={request.Id}, Name={request.Name}, Active={request.Active}).");
return modules.SingleOrDefault() is ThirdPartyModule module
? module.License
: throw new NotFoundException(
$"Third-party module not found for the given criteria (Id={request.Id}, Name={request.Name}, Active={request.Active}).");
}
}