A lot of changes

This commit is contained in:
2026-09-24 17:54:16 +02:00
parent ece6ce2a43
commit e84c16cf92
35 changed files with 1815 additions and 222 deletions
@@ -0,0 +1,4 @@
namespace Musify.Api.DataTransferObjects.Albums
{
public record UpdateAlbumPictureRequest(Guid PictureIntentId);
}
@@ -3,6 +3,5 @@ namespace Musify.Api.DataTransferObjects.Albums
public record UpdateAlbumRequest(
string Title,
string? Description,
int? ReleaseYear,
Guid? PictureIntentId);
int? ReleaseYear);
}
@@ -0,0 +1,4 @@
namespace Musify.Api.DataTransferObjects.PlayLists
{
public record UpdatePlayListPictureRequest(Guid PictureIntentId);
}
@@ -3,8 +3,7 @@ using Musify.Domain.ValueObjects;
namespace Musify.Api.DataTransferObjects.PlayLists
{
public record UpdatePlayListRequest(
string? Name,
string Name,
string? Description,
Guid? PictureIntentId,
PlayListVisibility? Visibility = null);
PlayListVisibility? Visibility);
}
@@ -94,6 +94,17 @@ namespace Musify.Api.Endpoints
.Produces(StatusCodes.Status403Forbidden)
.Produces(StatusCodes.Status404NotFound);
group.MapPut("/{id:guid}/picture", UpdateAlbumPicture)
.WithName("UpdateAlbumPicture")
.WithSummary("Replace an album picture with an uploaded image")
.AddEndpointFilter<ValidationFilter<UpdateAlbumPictureRequest>>()
.RequireAuthorization()
.Produces<AlbumApplicationResponse>()
.ProducesValidationProblem()
.Produces(StatusCodes.Status401Unauthorized)
.Produces(StatusCodes.Status403Forbidden)
.Produces(StatusCodes.Status404NotFound);
group.MapDelete("/{id:guid}", DeleteAlbum)
.WithName("DeleteAlbum")
.WithSummary("Delete an album")
@@ -229,7 +240,21 @@ namespace Musify.Api.Endpoints
CancellationToken cancellationToken)
{
var result = await mediator.Send(
new UpdateAlbumCommand(currentUser.RequiredId, id, request.Title, request.Description, request.ReleaseYear, request.PictureIntentId),
new UpdateAlbumCommand(currentUser.RequiredId, id, request.Title, request.Description, request.ReleaseYear),
cancellationToken);
return result.ToHttpResult();
}
private static async Task<IResult> UpdateAlbumPicture(
IMediator mediator,
CurrentUser currentUser,
Guid id,
UpdateAlbumPictureRequest request,
CancellationToken cancellationToken)
{
var result = await mediator.Send(
new UpdateAlbumPictureCommand(currentUser.RequiredId, id, request.PictureIntentId),
cancellationToken);
return result.ToHttpResult();
@@ -68,6 +68,17 @@ namespace Musify.Api.Endpoints
.Produces(StatusCodes.Status403Forbidden)
.Produces(StatusCodes.Status404NotFound);
group.MapPut("/{id:guid}/picture", UpdatePlayListPicture)
.WithName("UpdatePlayListPicture")
.WithSummary("Replace a playlist picture with an uploaded image")
.AddEndpointFilter<ValidationFilter<UpdatePlayListPictureRequest>>()
.RequireAuthorization()
.Produces<PlayListApplicationResponse>()
.ProducesValidationProblem()
.Produces(StatusCodes.Status401Unauthorized)
.Produces(StatusCodes.Status403Forbidden)
.Produces(StatusCodes.Status404NotFound);
group.MapDelete("/{id:guid}", DeletePlayList)
.WithName("DeletePlayList")
.WithSummary("Delete a playlist")
@@ -185,7 +196,21 @@ namespace Musify.Api.Endpoints
CancellationToken cancellationToken)
{
var result = await mediator.Send(
new UpdatePlayListCommand(currentUser.RequiredId, id, request.Name, request.Description, request.PictureIntentId, request.Visibility),
new UpdatePlayListCommand(currentUser.RequiredId, id, request.Name, request.Description, request.Visibility!.Value),
cancellationToken);
return result.ToHttpResult();
}
private static async Task<IResult> UpdatePlayListPicture(
IMediator mediator,
CurrentUser currentUser,
Guid id,
UpdatePlayListPictureRequest request,
CancellationToken cancellationToken)
{
var result = await mediator.Send(
new UpdatePlayListPictureCommand(currentUser.RequiredId, id, request.PictureIntentId),
cancellationToken);
return result.ToHttpResult();
@@ -7,7 +7,7 @@ namespace Musify.Api.Models
{
public static bool TryParse(string? value, out GenreParameter result)
{
if (Enum.TryParse<Genre>(value, ignoreCase: true, out var genre) && Enum.IsDefined(genre))
if (Enum.TryParse<Genre>(value, ignoreCase: true, out var genre) && genre != Genre.None && Enum.IsDefined(genre))
{
result = new GenreParameter(genre);
return true;
@@ -0,0 +1,14 @@
using FluentValidation;
using Musify.Api.DataTransferObjects.Albums;
namespace Musify.Api.Validators.Albums
{
public sealed class UpdateAlbumPictureRequestValidator : AbstractValidator<UpdateAlbumPictureRequest>
{
public UpdateAlbumPictureRequestValidator()
{
RuleFor(x => x.PictureIntentId)
.NotEmpty();
}
}
}
@@ -0,0 +1,14 @@
using FluentValidation;
using Musify.Api.DataTransferObjects.PlayLists;
namespace Musify.Api.Validators.PlayLists
{
public sealed class UpdatePlayListPictureRequestValidator : AbstractValidator<UpdatePlayListPictureRequest>
{
public UpdatePlayListPictureRequestValidator()
{
RuleFor(x => x.PictureIntentId)
.NotEmpty();
}
}
}
@@ -9,16 +9,14 @@ namespace Musify.Api.Validators.PlayLists
{
RuleFor(x => x.Name)
.NotEmpty()
.MaximumLength(Limits.PlayListName)
.When(x => x.Name != null);
.MaximumLength(Limits.PlayListName);
RuleFor(x => x.Description)
.MaximumLength(Limits.Description)
.When(x => x.Description != null);
.MaximumLength(Limits.Description);
RuleFor(x => x.Visibility)
.IsInEnum()
.When(x => x.Visibility != null);
.NotNull()
.IsInEnum();
}
}
}
@@ -22,6 +22,11 @@ namespace Musify.Api.Validators.Tracks
.NotEmpty()
.WithMessage("At least one tag is required.");
RuleFor(x => x.Tags)
.Must(tags => !tags.Contains(Genre.None))
.When(x => x.Tags is { Count: > 0 })
.WithMessage("'None' is not a valid tag.");
RuleFor(x => x.Tags)
.Must(tags => GenreCompatibility.FindConflicts(tags).Count == 0)
.When(x => x.Tags is { Count: > 0 })