Files
components/LiteCharms.Features.MidrandBooks/Authors/AuthorService.cs
T
Khwezi Mngoma 91ede2d568
continuous-integration/drone/pr Build is passing
Added a way to get the Author by productId
2026-05-30 18:17:55 +02:00

171 lines
6.9 KiB
C#

using LiteCharms.Features.MidrandBooks.Abstractions;
using LiteCharms.Features.MidrandBooks.Authors.Models;
using LiteCharms.Features.MidrandBooks.Extensions;
using LiteCharms.Features.MidrandBooks.Postgres;
using LiteCharms.Features.Models;
namespace LiteCharms.Features.MidrandBooks.Authors;
public sealed class AuthorService(IDbContextFactory<MidrandBooksDbContext> contextFactory) : IService
{
public async ValueTask<Result<Author>> GetAuthorByProductIdAsync(long productId, CancellationToken cancellationToken = default)
{
try
{
await using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
var author = await context.Books
.AsNoTracking()
.Include(i => i.Author)
.Where(b => b.ProductId == productId)
.FirstOrDefaultAsync(cancellationToken);
if (author is null)
return Result.Fail<Author>(new Error($"No author association discovered for Product ID {productId}"));
return Result.Ok(author.Author!.ToModel());
}
catch (Exception ex)
{
return Result.Fail<Author>(new Error(ex.Message).CausedBy(ex));
}
}
public async ValueTask<Result> UpdateAuthorStatusAsync(long authorId, bool isEnabled, CancellationToken cancellationToken = default)
{
try
{
await using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
var rowsUpdated = await context.Authors
.Where(a => a.Id == authorId)
.ExecuteUpdateAsync(setters => setters
.SetProperty(a => a.Enabled, isEnabled)
.SetProperty(a => a.UpdatedAt, DateTime.UtcNow), cancellationToken);
return rowsUpdated > 0
? Result.Ok()
: Result.Fail(new Error($"Author with ID {authorId} not found"));
}
catch (Exception ex)
{
return Result.Fail(new Error(ex.Message).CausedBy(ex));
}
}
public async ValueTask<Result<Author>> GetAuthorAsync(long authorId, CancellationToken cancellationToken = default)
{
try
{
using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
var author = await context.Authors.FirstOrDefaultAsync(a => a.Id == authorId, cancellationToken);
return author is not null
? Result.Ok(author.ToModel())
: Result.Fail<Author>(new Error($"Author with ID {authorId} not found"));
}
catch (Exception ex)
{
return Result.Fail<Author>(new Error(ex.Message).CausedBy(ex));
}
}
public async ValueTask<Result<Author[]>> GetAuthorsAsync(DateRange range, CancellationToken cancellationToken = default)
{
try
{
var fromDate = range.From.ToDateTime(TimeOnly.MinValue, DateTimeKind.Utc);
var toDate = range.To.ToDateTime(TimeOnly.MaxValue, DateTimeKind.Utc);
await using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
var authors = await context.Authors.AsNoTracking()
.OrderByDescending(o => o.CreatedAt)
.ThenByDescending(o => o.UpdatedAt)
.Where(a => a.CreatedAt >= fromDate && a.CreatedAt <= toDate)
.Take(range.MaxRecords)
.ToArrayAsync(cancellationToken);
return authors?.Length > 0
? Result.Ok(authors.Select(a => a.ToModel()).ToArray())
: Result.Fail<Author[]>(new Error("No authors found in the specified date range."));
}
catch (Exception ex)
{
return Result.Fail<Author[]>(new Error(ex.Message).CausedBy(ex));
}
}
public async ValueTask<Result> UpdateAuthorAsync(long authorId, UpdateAuthor request, CancellationToken cancellationToken = default)
{
try
{
await using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
var author = await context.Authors.FirstOrDefaultAsync(a => a.Id == authorId, cancellationToken);
if (author is null)
return Result.Fail(new Error($"Author with ID {authorId} not found"));
author.UpdatedAt = DateTime.UtcNow;
author.PublisherType = request.PublisherType;
author.Company = request.Company;
author.VatNumber = request.VatNumber;
author.Name = request.Name;
author.LastName = request.LastName;
author.Biography = request.Biography;
author.Email = request.Email;
author.Website = request.Website;
author.ImageUrl = request.ImageUrl;
author.ThumbnailImageUrl = request.ThumbnailImageUrl;
author.SocialMedia = request.SocialMedia;
return await context.SaveChangesAsync(cancellationToken) > 0
? Result.Ok()
: Result.Fail(new Error($"Failed to update author with ID {authorId}"));
}
catch (Exception ex)
{
return Result.Fail(new Error(ex.Message).CausedBy(ex));
}
}
public async ValueTask<Result<long>> CreateAuthorAsync(CreateAuthor request, CancellationToken cancellationToken = default)
{
try
{
await using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
if (await context.Authors.AnyAsync(a => a.Name == request.Name && a.LastName == request.LastName, cancellationToken))
return Result.Fail<long>(new Error($"An author with the name {request.Name} {request.LastName} already exists"));
if (await context.Authors.AnyAsync(a => a.Email == request.Email, cancellationToken))
return Result.Fail<long>(new Error($"An author with the email {request.Email} already exists"));
var newAuthor = context.Authors.Add(new Entities.Author
{
Company = request.Company,
VatNumber = request.VatNumber,
PublisherType = request.PublisherType,
Name = request.Name,
LastName = request.LastName,
Biography = request.Biography,
Email = request.Email,
Website = request.Website,
ImageUrl = request.ImageUrl,
ThumbnailImageUrl = request.ThumbnailImageUrl,
SocialMedia = request.SocialMedia
});
return await context.SaveChangesAsync(cancellationToken) > 0
? Result.Ok(newAuthor.Entity.Id)
: Result.Fail<long>(new Error($"Failed to create author {request.Name} {request.LastName}"));
}
catch (Exception ex)
{
return Result.Fail<long>(new Error(ex.Message).CausedBy(ex));
}
}
}