feat(backend): add playlist cover endpoint and relax description validation
Adds GET /playlists/{id}/cover (mirrors the track cover endpoint) so
uploaded playlist pictures can actually be served. Also drops the
NotEmpty rule on playlist description, which rejected the blank
description the frontend treats as optional; CreatePlayList now falls
back to the domain's default text instead.
This commit is contained in:
@@ -3,6 +3,7 @@ using Musify.Application.PlayLists;
|
||||
using Musify.Application.PlayLists.Responses;
|
||||
using Musify.Application.Tracks.Responses;
|
||||
using Musify.Application.Shared;
|
||||
using Musify.Application.Contracts;
|
||||
using Musify.Api.Authentication;
|
||||
using Musify.Api.DataTransferObjects.PlayLists;
|
||||
using Musify.Api.Extensions;
|
||||
@@ -39,6 +40,12 @@ public static class PlayListEndpoints
|
||||
.Produces<PaginatedResponse<TrackApplicationResponse>>()
|
||||
.Produces(StatusCodes.Status404NotFound);
|
||||
|
||||
group.MapGet("/{id}/cover", GetPlayListCover)
|
||||
.WithName("GetPlayListCover")
|
||||
.WithSummary("Get A PlayList Cover Image.")
|
||||
.Produces(StatusCodes.Status200OK)
|
||||
.Produces(StatusCodes.Status404NotFound);
|
||||
|
||||
group.MapPost("/", CreatePlayList)
|
||||
.WithName("CreatePlayList")
|
||||
.WithSummary("Create A New PlayList.")
|
||||
@@ -139,14 +146,36 @@ public static class PlayListEndpoints
|
||||
return result.ToHttpResult();
|
||||
}
|
||||
|
||||
private static async Task<IResult> GetPlayListCover(
|
||||
IMediator mediator,
|
||||
IStorageService storageService,
|
||||
HttpResponse response,
|
||||
Guid id,
|
||||
CancellationToken cancellationToken,
|
||||
string size = "medium")
|
||||
{
|
||||
var result = await mediator.Send(new GetPlayListCoverQuery(id, size), cancellationToken);
|
||||
if (result.IsError)
|
||||
return Results.NotFound();
|
||||
|
||||
var location = result.Value;
|
||||
var stream = await storageService.GetFileAsync(location.Bucket, location.Key, cancellationToken);
|
||||
response.Headers.CacheControl = "public, max-age=31536000, immutable";
|
||||
return Results.Stream(stream, location.ContentType);
|
||||
}
|
||||
|
||||
private static async Task<IResult> CreatePlayList(
|
||||
IMediator mediator,
|
||||
CurrentUser currentUser,
|
||||
CreatePlayListRequest request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var description = string.IsNullOrWhiteSpace(request.Description)
|
||||
? "No description was provided."
|
||||
: request.Description;
|
||||
|
||||
var result = await mediator.Send(
|
||||
new CreatePlayListCommand(currentUser.RequiredId, request.Name, request.Description, request.PictureIntentId),
|
||||
new CreatePlayListCommand(currentUser.RequiredId, request.Name, description, request.PictureIntentId),
|
||||
cancellationToken);
|
||||
|
||||
return result.ToCreatedResult(playList => $"/playlists/{playList.Id}");
|
||||
|
||||
@@ -12,7 +12,6 @@ public sealed class CreatePlayListRequestValidator : AbstractValidator<CreatePla
|
||||
.MaximumLength(50);
|
||||
|
||||
RuleFor(x => x.Description)
|
||||
.NotEmpty()
|
||||
.MaximumLength(256);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ public sealed class UpdatePlayListRequestValidator : AbstractValidator<UpdatePla
|
||||
.When(x => x.NewName is not null);
|
||||
|
||||
RuleFor(x => x.NewDescription)
|
||||
.NotEmpty()
|
||||
.MaximumLength(256)
|
||||
.When(x => x.NewDescription is not null);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user