Adds multiple media urls for content

This commit is contained in:
Jonathan Bourdon
2024-07-31 17:38:58 -04:00
parent 042fd53463
commit bbcc7a8a33
19 changed files with 319 additions and 111 deletions

View File

@@ -0,0 +1,114 @@
using System.Collections.Concurrent;
using FastEndpoints;
using FluentValidation;
using Hutopy.Application.AzureBlobStorage.Constants;
using Hutopy.Application.Common.Interfaces;
using Hutopy.Web.Common;
using Hutopy.Web.Features.Contents.Data;
namespace Hutopy.Web.Features.Contents.Handlers;
public record PostContentRequest(
Guid Id,
Guid CreatorId,
string Title,
string Description,
IFormFileCollection Files);
public sealed class PostContentRequestValidator : Validator<PostContentRequest>
{
public PostContentRequestValidator()
{
RuleFor(r => r.Id)
.NotNull().WithMessage("You should specify the Id")
.NotEmpty().WithMessage("You should specify a valid/not empty Id");
RuleFor(r => r.CreatorId)
.NotNull().WithMessage("You should specify the CreatorId")
.NotEmpty().WithMessage("You should specify a valid/not empty CreatorId");
RuleFor(r => r.Title)
.NotNull().WithMessage("You should specify the Title")
.NotEmpty().WithMessage("You should specify a valid/not empty Title");
RuleFor(r => r.Description)
.NotNull().WithMessage("You should specify the Description")
.NotEmpty().WithMessage("You should specify a valid/not empty Description");
}
}
public sealed class PostContent(
IAzureBlobStorageService blobStorage,
ContentDbContext context)
: Endpoint<PostContentRequest>
{
public override void Configure()
{
Post("/api/contents");
Options(o => o.WithTags("Contents"));
AllowFileUploads();
}
public override async Task HandleAsync(
PostContentRequest req,
CancellationToken ct)
{
var urls = new ConcurrentBag<string>();
await Parallel.ForEachAsync(
req.Files,
ct,
async (file, ict) =>
{
try
{
var contentUrl = await SaveFileAsync(
req.CreatorId,
req.Id,
file,
ict);
urls.Add(contentUrl);
}
catch (Exception ex)
{
Logger.LogError("{ErrorMessage}", ex.Message);
}
});
await context.Contents.AddAsync(
new()
{
Id = req.Id,
CreatedBy = User.GetUserId(),
Title = req.Title,
Description = req.Description,
Urls = urls.ToArray()
},
ct);
await context.SaveChangesAsync(ct);
await SendOkAsync(ct);
}
private async Task<string> SaveFileAsync(
Guid creatorId,
Guid contentId,
IFormFile file,
CancellationToken ct = default)
{
var memoryStream = new MemoryStream();
await file.CopyToAsync(memoryStream, ct);
// TODO: I would like us to use ContainerNames.Creators but it seems we are missing configurations @jbourdon
var url = await blobStorage.UploadFileAsync(
ContainerNames.Users,
$"{creatorId}/{SubDirectoryNames.Contents}/{contentId}/{file.FileName}",
memoryStream,
file.ContentType,
ct: ct);
return url;
}
}

View File

@@ -0,0 +1,45 @@
using FastEndpoints;
using FluentValidation;
using Hutopy.Application.Common.Interfaces;
using Hutopy.Application.Common.Models;
namespace Hutopy.Web.Features.Contents.Handlers;
public sealed class GetCreatorByAliasRequest
{
public string CreatorAlias { get; init; }
}
public sealed class GetCreatorByAliasRequestValidator
: Validator<GetCreatorByAliasRequest>
{
public GetCreatorByAliasRequestValidator()
{
RuleFor(r => r.CreatorAlias)
.NotNull().WithMessage("You should specify the CreatorAlias")
.NotEmpty().WithMessage("You should specify a valid/not empty CreatorAlias");
}
}
public class GetCreatorByAlias(
IIdentityService identityService)
: Endpoint<GetCreatorByAliasRequest, UserModel?>
{
public override void Configure()
{
Options((o => o.WithTags("Creators")));
Get("/api/creators/@{CreatorAlias}");
AllowAnonymous();
}
public override async Task HandleAsync(
GetCreatorByAliasRequest req,
CancellationToken ct)
{
var user = await identityService.FindUserByCreatorAliasAsync(
req.CreatorAlias,
ct);
await SendAsync(user, cancellation: ct);
}
}

View File

@@ -1,39 +0,0 @@
using FastEndpoints;
using Hutopy.Web.Common;
using Hutopy.Web.Features.Contents.Data;
namespace Hutopy.Web.Features.Contents.Handlers;
public record struct PostContentRequest(
string? Title,
string? Description,
string? Uri);
public class PostContent(
ContentDbContext context)
: Endpoint<PostContentRequest>
{
public override void Configure()
{
Post("/api/contents");
Options( o => o.WithTags("Contents"));
}
public override async Task HandleAsync(
PostContentRequest req,
CancellationToken ct)
{
await context.Contents.AddAsync(
new Content
{
Id = GuidHelper.GenerateUuidV7(),
CreatedBy = User.GetUserId(),
Title = req.Title,
Description = req.Description,
Uri = req.Uri
},
ct);
await context.SaveChangesAsync(ct);
}
}