Merge branch 'backend-refactor-rev' into audit-merge-three
Adapt the refactored web player to the new backend API (id path params, bulk track adds, flat list items, mix kind) and port AddAlbumToPlayList to the backend conventions.
This commit is contained in:
@@ -1,262 +1,276 @@
|
||||
using Mediator;
|
||||
using Musify.Api.Authentication;
|
||||
using Musify.Api.DataTransferObjects;
|
||||
using Musify.Api.DataTransferObjects.Albums;
|
||||
using Musify.Api.Extensions;
|
||||
using Musify.Api.Filters;
|
||||
using Musify.Api.Models;
|
||||
using Musify.Application.Albums;
|
||||
using Musify.Application.Albums.Responses;
|
||||
using Musify.Application.Contracts;
|
||||
using Musify.Application.Pictures;
|
||||
using Musify.Application.Pictures.Responses;
|
||||
using Musify.Application.Shared;
|
||||
using Musify.Application.Tracks.Responses;
|
||||
|
||||
namespace Musify.Api.Endpoints;
|
||||
|
||||
public static class AlbumEndpoints
|
||||
namespace Musify.Api.Endpoints
|
||||
{
|
||||
public static IEndpointRouteBuilder MapAlbumEndpoints(this IEndpointRouteBuilder app)
|
||||
public static class AlbumEndpoints
|
||||
{
|
||||
var group = app.MapGroup("/albums")
|
||||
.WithTags("Albums");
|
||||
private const int MaxRecentAlbumsLimit = 50;
|
||||
|
||||
group.MapGet("/", GetAlbums)
|
||||
.WithName("GetAlbums")
|
||||
.WithSummary("Get Paginated Albums, Optionally Filtered By Title.")
|
||||
.Produces<AlbumsSearchResponse>();
|
||||
public static IEndpointRouteBuilder MapAlbumEndpoints(this IEndpointRouteBuilder app)
|
||||
{
|
||||
var group = app.MapGroup("/albums")
|
||||
.WithTags("Albums");
|
||||
|
||||
group.MapGet("/{id}", GetAlbumById)
|
||||
.WithName("GetAlbumById")
|
||||
.WithSummary("Get An Album By Id.")
|
||||
.Produces<AlbumApplicationResponse>()
|
||||
.Produces(StatusCodes.Status404NotFound);
|
||||
group.MapGet("/", GetAlbums)
|
||||
.AddEndpointFilter<ValidationFilter<PageQuery>>()
|
||||
.WithName("GetAlbums")
|
||||
.WithSummary("Get paginated albums, optionally filtered by title")
|
||||
.Produces<PaginatedResponse<AlbumApplicationResponse>>();
|
||||
|
||||
group.MapGet("/users/{userId}", GetAlbumsByUserId)
|
||||
.WithName("GetAlbumsByUserId")
|
||||
.WithSummary("Get Paginated Albums For A User.")
|
||||
.Produces<PaginatedResponse<AlbumApplicationResponse>>()
|
||||
.Produces(StatusCodes.Status404NotFound);
|
||||
group.MapGet("/{id:guid}", GetAlbumById)
|
||||
.WithName("GetAlbumById")
|
||||
.WithSummary("Get an album by id")
|
||||
.Produces<AlbumApplicationResponse>()
|
||||
.Produces(StatusCodes.Status404NotFound);
|
||||
|
||||
group.MapGet("/recent", GetRecentlyListenedAlbums)
|
||||
.WithName("GetRecentlyListenedAlbums")
|
||||
.WithSummary("Get The Albums The Current User Listened To Most Recently.")
|
||||
.RequireAuthorization()
|
||||
.Produces<IReadOnlyList<AlbumApplicationResponse>>()
|
||||
.Produces(StatusCodes.Status401Unauthorized);
|
||||
group.MapGet("/users/{userId:long}", GetAlbumsByUserId)
|
||||
.AddEndpointFilter<ValidationFilter<PageQuery>>()
|
||||
.WithName("GetAlbumsByUserId")
|
||||
.WithSummary("Get paginated albums for a user")
|
||||
.Produces<PaginatedResponse<AlbumApplicationResponse>>()
|
||||
.Produces(StatusCodes.Status404NotFound);
|
||||
|
||||
group.MapGet("/{albumId}/tracks", GetAlbumTracks)
|
||||
.WithName("GetAlbumTracks")
|
||||
.WithSummary("Get The Tracks Of An Album Ordered By Track Number.")
|
||||
.Produces<PaginatedResponse<TrackApplicationResponse>>()
|
||||
.Produces(StatusCodes.Status404NotFound);
|
||||
group.MapGet("/recent", GetRecentlyListenedAlbums)
|
||||
.WithName("GetRecentlyListenedAlbums")
|
||||
.WithSummary("Get the albums the current user listened to most recently")
|
||||
.RequireAuthorization()
|
||||
.Produces<IReadOnlyList<AlbumApplicationResponse>>()
|
||||
.Produces(StatusCodes.Status401Unauthorized);
|
||||
|
||||
group.MapGet("/{id}/cover", GetAlbumCover)
|
||||
.WithName("GetAlbumCover")
|
||||
.WithSummary("Get An Album Cover Image.")
|
||||
.Produces(StatusCodes.Status200OK)
|
||||
.Produces(StatusCodes.Status404NotFound);
|
||||
group.MapGet("/{id:guid}/tracks", GetAlbumTracks)
|
||||
.AddEndpointFilter<ValidationFilter<PageQuery>>()
|
||||
.WithName("GetAlbumTracks")
|
||||
.WithSummary("Get the tracks of an album ordered by track number")
|
||||
.Produces<PaginatedResponse<TrackApplicationResponse>>()
|
||||
.Produces(StatusCodes.Status404NotFound);
|
||||
|
||||
group.MapPost("/upload-picture", RequestAlbumPictureUpload)
|
||||
.WithName("RequestAlbumPictureUpload")
|
||||
.WithSummary("Request A Pre-Signed URL To Upload An Album Picture.")
|
||||
.AddEndpointFilter<ValidationFilter<RequestAlbumPictureUploadRequest>>()
|
||||
.RequireAuthorization()
|
||||
.Produces<AlbumPictureUploadResponse>()
|
||||
.ProducesValidationProblem()
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces(StatusCodes.Status404NotFound);
|
||||
group.MapGet("/{id:guid}/cover", GetAlbumCover)
|
||||
.WithName("GetAlbumCover")
|
||||
.WithSummary("Get an album cover image")
|
||||
.Produces(StatusCodes.Status200OK)
|
||||
.Produces(StatusCodes.Status404NotFound);
|
||||
|
||||
group.MapPost("/", CreateAlbum)
|
||||
.WithName("CreateAlbum")
|
||||
.WithSummary("Create A New Album.")
|
||||
.AddEndpointFilter<ValidationFilter<CreateAlbumRequest>>()
|
||||
.RequireAuthorization()
|
||||
.Produces<AlbumApplicationResponse>(StatusCodes.Status201Created)
|
||||
.ProducesValidationProblem()
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces(StatusCodes.Status404NotFound);
|
||||
group.MapPost("/upload-picture", RequestAlbumPictureUpload)
|
||||
.WithName("RequestAlbumPictureUpload")
|
||||
.WithSummary("Request a pre-signed URL to upload an album picture")
|
||||
.AddEndpointFilter<ValidationFilter<PictureUploadRequest>>()
|
||||
.RequireAuthorization()
|
||||
.Produces<PictureUploadResponse>()
|
||||
.ProducesValidationProblem()
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces(StatusCodes.Status404NotFound);
|
||||
|
||||
group.MapPut("/{albumId}", UpdateAlbum)
|
||||
.WithName("UpdateAlbum")
|
||||
.WithSummary("Update An Album.")
|
||||
.AddEndpointFilter<ValidationFilter<UpdateAlbumRequest>>()
|
||||
.RequireAuthorization()
|
||||
.Produces<AlbumApplicationResponse>()
|
||||
.ProducesValidationProblem()
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces(StatusCodes.Status404NotFound);
|
||||
group.MapPost("/", CreateAlbum)
|
||||
.WithName("CreateAlbum")
|
||||
.WithSummary("Create a new album")
|
||||
.AddEndpointFilter<ValidationFilter<CreateAlbumRequest>>()
|
||||
.RequireAuthorization()
|
||||
.Produces<AlbumApplicationResponse>(StatusCodes.Status201Created)
|
||||
.ProducesValidationProblem()
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces(StatusCodes.Status404NotFound);
|
||||
|
||||
group.MapDelete("/{albumId}", DeleteAlbum)
|
||||
.WithName("DeleteAlbum")
|
||||
.WithSummary("Delete An Album.")
|
||||
.RequireAuthorization()
|
||||
.Produces(StatusCodes.Status204NoContent)
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces(StatusCodes.Status404NotFound);
|
||||
group.MapPut("/{id:guid}", UpdateAlbum)
|
||||
.WithName("UpdateAlbum")
|
||||
.WithSummary("Update an album")
|
||||
.AddEndpointFilter<ValidationFilter<UpdateAlbumRequest>>()
|
||||
.RequireAuthorization()
|
||||
.Produces<AlbumApplicationResponse>()
|
||||
.ProducesValidationProblem()
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces(StatusCodes.Status403Forbidden)
|
||||
.Produces(StatusCodes.Status404NotFound);
|
||||
|
||||
group.MapPost("/{albumId}/tracks/{trackId}", AddTrackToAlbum)
|
||||
.WithName("AddTrackToAlbum")
|
||||
.WithSummary("Add One Of Your Uploaded Tracks To An Album.")
|
||||
.RequireAuthorization()
|
||||
.Produces(StatusCodes.Status204NoContent)
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces(StatusCodes.Status404NotFound)
|
||||
.Produces(StatusCodes.Status409Conflict);
|
||||
group.MapDelete("/{id:guid}", DeleteAlbum)
|
||||
.WithName("DeleteAlbum")
|
||||
.WithSummary("Delete an album")
|
||||
.RequireAuthorization()
|
||||
.Produces(StatusCodes.Status204NoContent)
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces(StatusCodes.Status403Forbidden)
|
||||
.Produces(StatusCodes.Status404NotFound);
|
||||
|
||||
group.MapDelete("/{albumId}/tracks/{trackId}", RemoveTrackFromAlbum)
|
||||
.WithName("RemoveTrackFromAlbum")
|
||||
.WithSummary("Remove A Track From An Album.")
|
||||
.RequireAuthorization()
|
||||
.Produces(StatusCodes.Status204NoContent)
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces(StatusCodes.Status404NotFound);
|
||||
group.MapPost("/{id:guid}/tracks", AddAlbumTracks)
|
||||
.WithName("AddAlbumTracks")
|
||||
.WithSummary("Add your uploaded tracks to an album, skipping the ones it already has")
|
||||
.AddEndpointFilter<ValidationFilter<AddTracksRequest>>()
|
||||
.RequireAuthorization()
|
||||
.Produces(StatusCodes.Status204NoContent)
|
||||
.ProducesValidationProblem()
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces(StatusCodes.Status403Forbidden)
|
||||
.Produces(StatusCodes.Status404NotFound)
|
||||
.Produces(StatusCodes.Status409Conflict);
|
||||
|
||||
return app;
|
||||
}
|
||||
group.MapDelete("/{id:guid}/tracks/{trackId:guid}", RemoveTrackFromAlbum)
|
||||
.WithName("RemoveTrackFromAlbum")
|
||||
.WithSummary("Remove a track from an album")
|
||||
.RequireAuthorization()
|
||||
.Produces(StatusCodes.Status204NoContent)
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces(StatusCodes.Status403Forbidden)
|
||||
.Produces(StatusCodes.Status404NotFound);
|
||||
|
||||
private static async Task<IResult> GetAlbums(
|
||||
IMediator mediator,
|
||||
CancellationToken cancellationToken,
|
||||
string? title,
|
||||
int pageNumber = 1,
|
||||
int pageSize = 10)
|
||||
{
|
||||
var result = await mediator.Send(new GetAlbumsQuery(title, pageNumber, pageSize), cancellationToken);
|
||||
return result.ToHttpResult();
|
||||
}
|
||||
return app;
|
||||
}
|
||||
|
||||
private static async Task<IResult> GetAlbumById(
|
||||
IMediator mediator,
|
||||
Guid id,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(new GetAlbumByIdQuery(id), cancellationToken);
|
||||
return result.ToHttpResult();
|
||||
}
|
||||
private static async Task<IResult> GetAlbums(
|
||||
IMediator mediator,
|
||||
string? title,
|
||||
[AsParameters] PageQuery page,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(new GetAlbumsQuery(title, page.PageNumber, page.PageSize), cancellationToken);
|
||||
return Results.Ok(result);
|
||||
}
|
||||
|
||||
private static async Task<IResult> GetAlbumsByUserId(
|
||||
IMediator mediator,
|
||||
long userId,
|
||||
CancellationToken cancellationToken,
|
||||
int pageNumber = 1,
|
||||
int pageSize = 10)
|
||||
{
|
||||
var result = await mediator.Send(new GetAlbumsByUserIdQuery(userId, pageNumber, pageSize), cancellationToken);
|
||||
return result.ToHttpResult();
|
||||
}
|
||||
private static async Task<IResult> GetAlbumById(
|
||||
IMediator mediator,
|
||||
Guid id,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(new GetAlbumByIdQuery(id), cancellationToken);
|
||||
return result.ToHttpResult();
|
||||
}
|
||||
|
||||
private static async Task<IResult> GetRecentlyListenedAlbums(
|
||||
IMediator mediator,
|
||||
CurrentUser currentUser,
|
||||
CancellationToken cancellationToken,
|
||||
int limit = 12)
|
||||
{
|
||||
var result = await mediator.Send(new GetRecentlyListenedAlbumsQuery(currentUser.RequiredId, limit), cancellationToken);
|
||||
return result.ToHttpResult();
|
||||
}
|
||||
private static async Task<IResult> GetAlbumsByUserId(
|
||||
IMediator mediator,
|
||||
long userId,
|
||||
[AsParameters] PageQuery page,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(new GetAlbumsByUserIdQuery(userId, page.PageNumber, page.PageSize), cancellationToken);
|
||||
return result.ToHttpResult();
|
||||
}
|
||||
|
||||
private static async Task<IResult> GetAlbumTracks(
|
||||
IMediator mediator,
|
||||
Guid albumId,
|
||||
CancellationToken cancellationToken,
|
||||
int pageNumber = 1,
|
||||
int pageSize = 50)
|
||||
{
|
||||
var result = await mediator.Send(new GetAlbumTracksQuery(albumId, pageNumber, pageSize), cancellationToken);
|
||||
return result.ToHttpResult();
|
||||
}
|
||||
private static async Task<IResult> GetRecentlyListenedAlbums(
|
||||
IMediator mediator,
|
||||
CurrentUser currentUser,
|
||||
CancellationToken cancellationToken,
|
||||
int limit = 12)
|
||||
{
|
||||
if (limit is < 1 or > MaxRecentAlbumsLimit)
|
||||
{
|
||||
return Results.ValidationProblem(new Dictionary<string, string[]>
|
||||
{
|
||||
[nameof(limit)] = [$"'{nameof(limit)}' must be between 1 and {MaxRecentAlbumsLimit}."]
|
||||
});
|
||||
}
|
||||
|
||||
private static async Task<IResult> GetAlbumCover(
|
||||
IMediator mediator,
|
||||
IStorageService storageService,
|
||||
HttpResponse response,
|
||||
Guid id,
|
||||
CancellationToken cancellationToken,
|
||||
string size = "medium")
|
||||
{
|
||||
var result = await mediator.Send(new GetAlbumCoverQuery(id, size), cancellationToken);
|
||||
if (result.IsError)
|
||||
return Results.NotFound();
|
||||
var result = await mediator.Send(new GetRecentlyListenedAlbumsQuery(currentUser.RequiredId, limit), cancellationToken);
|
||||
return Results.Ok(result);
|
||||
}
|
||||
|
||||
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> GetAlbumTracks(
|
||||
IMediator mediator,
|
||||
Guid id,
|
||||
[AsParameters] PageQuery page,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(new GetAlbumTracksQuery(id, page.PageNumber, page.PageSize), cancellationToken);
|
||||
return result.ToHttpResult();
|
||||
}
|
||||
|
||||
private static async Task<IResult> RequestAlbumPictureUpload(
|
||||
IMediator mediator,
|
||||
CurrentUser currentUser,
|
||||
RequestAlbumPictureUploadRequest request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(
|
||||
new RequestAlbumPictureUploadCommand(currentUser.RequiredId, request.FileType, request.ContentType, request.ExpectedSizeBytes),
|
||||
cancellationToken);
|
||||
private static Task<IResult> GetAlbumCover(
|
||||
IMediator mediator,
|
||||
IStorageService storageService,
|
||||
HttpResponse response,
|
||||
Guid id,
|
||||
CancellationToken cancellationToken,
|
||||
string size = "medium")
|
||||
{
|
||||
return CoverEndpoint.Stream(mediator, storageService, response, CoverOwner.Album, id, size, cancellationToken);
|
||||
}
|
||||
|
||||
return result.ToHttpResult();
|
||||
}
|
||||
private static async Task<IResult> RequestAlbumPictureUpload(
|
||||
IMediator mediator,
|
||||
CurrentUser currentUser,
|
||||
PictureUploadRequest request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(
|
||||
new RequestAlbumPictureUploadCommand(currentUser.RequiredId, request.FileType, request.ContentType, request.ExpectedSizeBytes),
|
||||
cancellationToken);
|
||||
|
||||
private static async Task<IResult> CreateAlbum(
|
||||
IMediator mediator,
|
||||
CurrentUser currentUser,
|
||||
CreateAlbumRequest request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(
|
||||
new CreateAlbumCommand(currentUser.RequiredId, request.Title, request.Description, request.ReleaseYear, request.PictureIntentId),
|
||||
cancellationToken);
|
||||
return result.ToHttpResult();
|
||||
}
|
||||
|
||||
return result.ToCreatedResult(album => $"/albums/{album.Id}");
|
||||
}
|
||||
private static async Task<IResult> CreateAlbum(
|
||||
IMediator mediator,
|
||||
CurrentUser currentUser,
|
||||
CreateAlbumRequest request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(
|
||||
new CreateAlbumCommand(currentUser.RequiredId, request.Title, request.Description, request.ReleaseYear, request.PictureIntentId),
|
||||
cancellationToken);
|
||||
|
||||
private static async Task<IResult> UpdateAlbum(
|
||||
IMediator mediator,
|
||||
CurrentUser currentUser,
|
||||
Guid albumId,
|
||||
UpdateAlbumRequest request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(
|
||||
new UpdateAlbumCommand(currentUser.RequiredId, albumId, request.NewTitle, request.NewDescription, request.NewReleaseYear, request.NewPictureIntentId),
|
||||
cancellationToken);
|
||||
return result.ToCreatedResult(album => $"/albums/{album.Id}");
|
||||
}
|
||||
|
||||
return result.ToHttpResult();
|
||||
}
|
||||
private static async Task<IResult> UpdateAlbum(
|
||||
IMediator mediator,
|
||||
CurrentUser currentUser,
|
||||
Guid id,
|
||||
UpdateAlbumRequest request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(
|
||||
new UpdateAlbumCommand(currentUser.RequiredId, id, request.Title, request.Description, request.ReleaseYear, request.PictureIntentId),
|
||||
cancellationToken);
|
||||
|
||||
private static async Task<IResult> DeleteAlbum(
|
||||
IMediator mediator,
|
||||
CurrentUser currentUser,
|
||||
Guid albumId,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(new DeleteAlbumCommand(currentUser.RequiredId, albumId), cancellationToken);
|
||||
return result.ToHttpResult();
|
||||
}
|
||||
return result.ToHttpResult();
|
||||
}
|
||||
|
||||
private static async Task<IResult> AddTrackToAlbum(
|
||||
IMediator mediator,
|
||||
CurrentUser currentUser,
|
||||
Guid albumId,
|
||||
Guid trackId,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(
|
||||
new AddTrackToAlbumCommand(currentUser.RequiredId, albumId, trackId),
|
||||
cancellationToken);
|
||||
private static async Task<IResult> DeleteAlbum(
|
||||
IMediator mediator,
|
||||
CurrentUser currentUser,
|
||||
Guid id,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(new DeleteAlbumCommand(currentUser.RequiredId, id), cancellationToken);
|
||||
return result.ToHttpResult();
|
||||
}
|
||||
|
||||
return result.ToHttpResult();
|
||||
}
|
||||
private static async Task<IResult> AddAlbumTracks(
|
||||
IMediator mediator,
|
||||
CurrentUser currentUser,
|
||||
Guid id,
|
||||
AddTracksRequest request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(
|
||||
new AddTracksToAlbumCommand(currentUser.RequiredId, id, request.TrackIds),
|
||||
cancellationToken);
|
||||
|
||||
private static async Task<IResult> RemoveTrackFromAlbum(
|
||||
IMediator mediator,
|
||||
CurrentUser currentUser,
|
||||
Guid albumId,
|
||||
Guid trackId,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(
|
||||
new RemoveTrackFromAlbumCommand(currentUser.RequiredId, albumId, trackId),
|
||||
cancellationToken);
|
||||
return result.ToHttpResult();
|
||||
}
|
||||
|
||||
return result.ToHttpResult();
|
||||
private static async Task<IResult> RemoveTrackFromAlbum(
|
||||
IMediator mediator,
|
||||
CurrentUser currentUser,
|
||||
Guid id,
|
||||
Guid trackId,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(
|
||||
new RemoveTrackFromAlbumCommand(currentUser.RequiredId, id, trackId),
|
||||
cancellationToken);
|
||||
|
||||
return result.ToHttpResult();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,27 +2,29 @@ using Mediator;
|
||||
using Musify.Application.Configuration;
|
||||
using Musify.Application.Configuration.Responses;
|
||||
|
||||
namespace Musify.Api.Endpoints;
|
||||
|
||||
public static class ConfigEndpoints
|
||||
namespace Musify.Api.Endpoints
|
||||
{
|
||||
public static IEndpointRouteBuilder MapConfigEndpoints(this IEndpointRouteBuilder app)
|
||||
public static class ConfigEndpoints
|
||||
{
|
||||
var group = app.MapGroup("/config")
|
||||
.WithTags("Config");
|
||||
public static IEndpointRouteBuilder MapConfigEndpoints(this IEndpointRouteBuilder app)
|
||||
{
|
||||
var group = app.MapGroup("/config")
|
||||
.WithTags("Config");
|
||||
|
||||
group.MapGet("/playback", GetPlaybackPublicConfig)
|
||||
.WithName("GetPlaybackPublicConfig")
|
||||
.WithSummary("Get The Public Playback Configuration (Whether Anonymous Listening Is Allowed). Works Anonymously.")
|
||||
.Produces<PlaybackPublicConfigResponse>();
|
||||
group.MapGet("/playback", GetPlaybackPublicConfig)
|
||||
.WithName("GetPlaybackPublicConfig")
|
||||
.WithSummary("Get the public playback configuration (whether anonymous listening is allowed). Works anonymously")
|
||||
.Produces<PlaybackPublicConfigResponse>();
|
||||
|
||||
return app;
|
||||
}
|
||||
return app;
|
||||
}
|
||||
|
||||
private static async Task<PlaybackPublicConfigResponse> GetPlaybackPublicConfig(
|
||||
IMediator mediator,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
return await mediator.Send(new GetPlaybackPublicConfigQuery(), cancellationToken);
|
||||
private static async Task<IResult> GetPlaybackPublicConfig(
|
||||
IMediator mediator,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(new GetPlaybackPublicConfigQuery(), cancellationToken);
|
||||
return Results.Ok(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
using Mediator;
|
||||
using Musify.Application.Configuration;
|
||||
using Musify.Application.Contracts;
|
||||
using Musify.Application.Pictures;
|
||||
|
||||
namespace Musify.Api.Endpoints
|
||||
{
|
||||
internal static class CoverEndpoint
|
||||
{
|
||||
public static async Task<IResult> Stream(
|
||||
IMediator mediator,
|
||||
IStorageService storageService,
|
||||
HttpResponse response,
|
||||
CoverOwner owner,
|
||||
Guid id,
|
||||
string size,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(new GetCoverQuery(owner, id, PictureSizeParser.Parse(size)), cancellationToken);
|
||||
if (result.IsError)
|
||||
return Results.NotFound();
|
||||
|
||||
var location = result.Value;
|
||||
var stream = await storageService.GetFileAsync(location.Bucket, location.Key, cancellationToken);
|
||||
if (stream == null)
|
||||
return Results.NotFound();
|
||||
|
||||
response.Headers.CacheControl = "public, max-age=31536000, immutable";
|
||||
return Results.Stream(stream, location.ContentType);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,39 +1,43 @@
|
||||
using Mediator;
|
||||
using Musify.Application.Genres;
|
||||
using Musify.Application.Genres.Responses;
|
||||
|
||||
namespace Musify.Api.Endpoints;
|
||||
|
||||
public static class GenreEndpoints
|
||||
namespace Musify.Api.Endpoints
|
||||
{
|
||||
public static IEndpointRouteBuilder MapGenreEndpoints(this IEndpointRouteBuilder app)
|
||||
public static class GenreEndpoints
|
||||
{
|
||||
var group = app.MapGroup("/genres")
|
||||
.WithTags("Genres");
|
||||
public static IEndpointRouteBuilder MapGenreEndpoints(this IEndpointRouteBuilder app)
|
||||
{
|
||||
var group = app.MapGroup("/genres")
|
||||
.WithTags("Genres");
|
||||
|
||||
group.MapGet("/", GetGenres)
|
||||
.WithName("GetGenres")
|
||||
.WithSummary("Get The Genres That Have At Least One Track, Most Populated First. Works Anonymously.")
|
||||
.Produces<IReadOnlyList<GenreResponse>>();
|
||||
group.MapGet("/", GetGenres)
|
||||
.WithName("GetGenres")
|
||||
.WithSummary("Get the genres that have at least one track, most populated first. Works anonymously")
|
||||
.Produces<IReadOnlyList<GenreResponse>>();
|
||||
|
||||
group.MapGet("/available", GetAvailableGenres)
|
||||
.WithName("GetAvailableGenres")
|
||||
.WithSummary("Get Every Genre A Track Can Be Tagged With, Including The Genres It Cannot Be Combined With. Works Anonymously.")
|
||||
.Produces<IReadOnlyList<AvailableGenreResponse>>();
|
||||
group.MapGet("/available", GetAvailableGenres)
|
||||
.WithName("GetAvailableGenres")
|
||||
.WithSummary("Get every genre a track can be tagged with, including the genres it cannot be combined with. Works anonymously")
|
||||
.Produces<IReadOnlyList<AvailableGenreResponse>>();
|
||||
|
||||
return app;
|
||||
}
|
||||
return app;
|
||||
}
|
||||
|
||||
private static async Task<IReadOnlyList<AvailableGenreResponse>> GetAvailableGenres(
|
||||
IMediator mediator,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
return await mediator.Send(new GetAvailableGenresQuery(), cancellationToken);
|
||||
}
|
||||
private static async Task<IResult> GetAvailableGenres(
|
||||
IMediator mediator,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(new GetAvailableGenresQuery(), cancellationToken);
|
||||
return Results.Ok(result);
|
||||
}
|
||||
|
||||
private static async Task<IReadOnlyList<GenreResponse>> GetGenres(
|
||||
IMediator mediator,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
return await mediator.Send(new GetGenresQuery(), cancellationToken);
|
||||
private static async Task<IResult> GetGenres(
|
||||
IMediator mediator,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(new GetGenresQuery(), cancellationToken);
|
||||
return Results.Ok(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,59 +1,61 @@
|
||||
using Mediator;
|
||||
using Musify.Application.Likes;
|
||||
using Musify.Application.Shared;
|
||||
using Musify.Application.Tracks.Responses;
|
||||
using Musify.Api.Authentication;
|
||||
using Musify.Api.DataTransferObjects.Likes;
|
||||
using Musify.Api.Extensions;
|
||||
using Musify.Api.Filters;
|
||||
using Musify.Api.Models;
|
||||
using Musify.Application.Likes;
|
||||
using Musify.Application.Shared;
|
||||
using Musify.Application.Tracks.Responses;
|
||||
|
||||
namespace Musify.Api.Endpoints;
|
||||
|
||||
public static class LikeEndpoints
|
||||
namespace Musify.Api.Endpoints
|
||||
{
|
||||
public static IEndpointRouteBuilder MapLikeEndpoints(this IEndpointRouteBuilder app)
|
||||
public static class LikeEndpoints
|
||||
{
|
||||
var group = app.MapGroup("/likes")
|
||||
.WithTags("Likes")
|
||||
.RequireAuthorization();
|
||||
public static IEndpointRouteBuilder MapLikeEndpoints(this IEndpointRouteBuilder app)
|
||||
{
|
||||
var group = app.MapGroup("/likes")
|
||||
.WithTags("Likes")
|
||||
.RequireAuthorization();
|
||||
|
||||
group.MapGet("/", GetLikedTracks)
|
||||
.WithName("GetLikedTracks")
|
||||
.WithSummary("Get The Current User'S Liked Tracks.")
|
||||
.Produces<PaginatedResponse<TrackApplicationResponse>>()
|
||||
.Produces(StatusCodes.Status401Unauthorized);
|
||||
group.MapGet("/", GetLikedTracks)
|
||||
.AddEndpointFilter<ValidationFilter<PageQuery>>()
|
||||
.WithName("GetLikedTracks")
|
||||
.WithSummary("Get the current user's liked tracks")
|
||||
.Produces<PaginatedResponse<TrackApplicationResponse>>()
|
||||
.Produces(StatusCodes.Status401Unauthorized);
|
||||
|
||||
group.MapPost("/toggle", ToggleTrackLike)
|
||||
.WithName("ToggleTrackLike")
|
||||
.WithSummary("Like Or Unlike A Track By Id.")
|
||||
.Produces<bool>()
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces(StatusCodes.Status404NotFound);
|
||||
group.MapPost("/toggle", ToggleTrackLike)
|
||||
.WithName("ToggleTrackLike")
|
||||
.WithSummary("Like or unlike a track by id")
|
||||
.Produces<bool>()
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces(StatusCodes.Status404NotFound);
|
||||
|
||||
return app;
|
||||
}
|
||||
return app;
|
||||
}
|
||||
|
||||
private static async Task<IResult> GetLikedTracks(
|
||||
IMediator mediator,
|
||||
CurrentUser currentUser,
|
||||
CancellationToken cancellationToken,
|
||||
int pageNumber = 1,
|
||||
int pageSize = 10)
|
||||
{
|
||||
var result = await mediator.Send(new GetLikedTracksQuery(currentUser.RequiredId, pageNumber, pageSize), cancellationToken);
|
||||
return result.ToHttpResult();
|
||||
}
|
||||
private static async Task<IResult> GetLikedTracks(
|
||||
IMediator mediator,
|
||||
CurrentUser currentUser,
|
||||
[AsParameters] PageQuery page,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(new GetLikedTracksQuery(currentUser.RequiredId, page.PageNumber, page.PageSize), cancellationToken);
|
||||
return Results.Ok(result);
|
||||
}
|
||||
|
||||
private static async Task<IResult> ToggleTrackLike(
|
||||
IMediator mediator,
|
||||
CurrentUser currentUser,
|
||||
ToggleLikeRequest request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(
|
||||
new ToggleTrackLikeCommand(currentUser.RequiredId, request.TrackId),
|
||||
cancellationToken);
|
||||
private static async Task<IResult> ToggleTrackLike(
|
||||
IMediator mediator,
|
||||
CurrentUser currentUser,
|
||||
ToggleLikeRequest request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(
|
||||
new ToggleTrackLikeCommand(currentUser.RequiredId, request.TrackId),
|
||||
cancellationToken);
|
||||
|
||||
return result.ToHttpResult();
|
||||
return result.ToHttpResult();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,48 +4,49 @@ using Musify.Api.Extensions;
|
||||
using Musify.Application.Mixes;
|
||||
using Musify.Application.Mixes.Responses;
|
||||
|
||||
namespace Musify.Api.Endpoints;
|
||||
|
||||
public static class MixEndpoints
|
||||
namespace Musify.Api.Endpoints
|
||||
{
|
||||
public static IEndpointRouteBuilder MapMixEndpoints(this IEndpointRouteBuilder app)
|
||||
public static class MixEndpoints
|
||||
{
|
||||
var group = app.MapGroup("/mixes")
|
||||
.WithTags("Mixes")
|
||||
.RequireAuthorization();
|
||||
public static IEndpointRouteBuilder MapMixEndpoints(this IEndpointRouteBuilder app)
|
||||
{
|
||||
var group = app.MapGroup("/mixes")
|
||||
.WithTags("Mixes")
|
||||
.RequireAuthorization();
|
||||
|
||||
group.MapGet("/", GetMixes)
|
||||
.WithName("GetMixes")
|
||||
.WithSummary("Get The Server-Generated Mixes Of The Current User.")
|
||||
.Produces<IReadOnlyList<MixApplicationResponse>>()
|
||||
.Produces(StatusCodes.Status401Unauthorized);
|
||||
group.MapGet("/", GetMixes)
|
||||
.WithName("GetMixes")
|
||||
.WithSummary("Get the server-generated mixes of the current user")
|
||||
.Produces<IReadOnlyList<MixApplicationResponse>>()
|
||||
.Produces(StatusCodes.Status401Unauthorized);
|
||||
|
||||
group.MapGet("/{id}", GetMixById)
|
||||
.WithName("GetMixById")
|
||||
.WithSummary("Get A Mix With All Of Its Songs.")
|
||||
.Produces<MixApplicationResponse>()
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces(StatusCodes.Status404NotFound);
|
||||
group.MapGet("/{id:guid}", GetMixById)
|
||||
.WithName("GetMixById")
|
||||
.WithSummary("Get a mix with all of its songs")
|
||||
.Produces<MixApplicationResponse>()
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces(StatusCodes.Status404NotFound);
|
||||
|
||||
return app;
|
||||
}
|
||||
return app;
|
||||
}
|
||||
|
||||
private static async Task<IResult> GetMixes(
|
||||
IMediator mediator,
|
||||
CurrentUser currentUser,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(new GetMixesByUserIdQuery(currentUser.RequiredId), cancellationToken);
|
||||
return result.ToHttpResult();
|
||||
}
|
||||
private static async Task<IResult> GetMixes(
|
||||
IMediator mediator,
|
||||
CurrentUser currentUser,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(new GetMixesByUserIdQuery(currentUser.RequiredId), cancellationToken);
|
||||
return Results.Ok(result);
|
||||
}
|
||||
|
||||
private static async Task<IResult> GetMixById(
|
||||
IMediator mediator,
|
||||
CurrentUser currentUser,
|
||||
Guid id,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(new GetMixByIdQuery(currentUser.RequiredId, id), cancellationToken);
|
||||
return result.ToHttpResult();
|
||||
private static async Task<IResult> GetMixById(
|
||||
IMediator mediator,
|
||||
CurrentUser currentUser,
|
||||
Guid id,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(new GetMixByIdQuery(currentUser.RequiredId, id), cancellationToken);
|
||||
return result.ToHttpResult();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,272 +1,259 @@
|
||||
using Mediator;
|
||||
using Musify.Application.PlayLists;
|
||||
using Musify.Application.PlayLists.Responses;
|
||||
using Musify.Application.Tracks;
|
||||
using Musify.Application.Tracks.Responses;
|
||||
using Musify.Application.Shared;
|
||||
using Musify.Application.Contracts;
|
||||
using Musify.Api.Authentication;
|
||||
using Musify.Api.DataTransferObjects;
|
||||
using Musify.Api.DataTransferObjects.PlayLists;
|
||||
using Musify.Api.Extensions;
|
||||
using Musify.Api.Filters;
|
||||
using Musify.Api.Models;
|
||||
using Musify.Application.Contracts;
|
||||
using Musify.Application.Pictures;
|
||||
using Musify.Application.Pictures.Responses;
|
||||
using Musify.Application.PlayLists;
|
||||
using Musify.Application.PlayLists.Responses;
|
||||
using Musify.Application.Shared;
|
||||
using Musify.Application.Tracks.Responses;
|
||||
|
||||
namespace Musify.Api.Endpoints;
|
||||
|
||||
public static class PlayListEndpoints
|
||||
namespace Musify.Api.Endpoints
|
||||
{
|
||||
public static IEndpointRouteBuilder MapPlayListEndpoints(this IEndpointRouteBuilder app)
|
||||
public static class PlayListEndpoints
|
||||
{
|
||||
var group = app.MapGroup("/playlists")
|
||||
.WithTags("PlayLists");
|
||||
public static IEndpointRouteBuilder MapPlayListEndpoints(this IEndpointRouteBuilder app)
|
||||
{
|
||||
var group = app.MapGroup("/playlists")
|
||||
.WithTags("PlayLists");
|
||||
|
||||
group.MapGet("/", GetPlayLists)
|
||||
.WithName("GetPlayLists")
|
||||
.WithSummary("Get Paginated PlayLists.")
|
||||
.Produces<PaginatedResponse<PlayListApplicationResponse>>();
|
||||
group.MapGet("/{id:guid}", GetPlayListById)
|
||||
.WithName("GetPlayListById")
|
||||
.WithSummary("Get a playlist by id")
|
||||
.Produces<PlayListApplicationResponse>()
|
||||
.Produces(StatusCodes.Status404NotFound);
|
||||
|
||||
group.MapGet("/{id}", GetPlayListById)
|
||||
.WithName("GetPlayListById")
|
||||
.WithSummary("Get A PlayList By Id.")
|
||||
.Produces<PlayListApplicationResponse>()
|
||||
.Produces(StatusCodes.Status404NotFound);
|
||||
group.MapGet("/users/{userId:long}", GetPlayListsByUserId)
|
||||
.AddEndpointFilter<ValidationFilter<PageQuery>>()
|
||||
.WithName("GetPlayListsByUserId")
|
||||
.WithSummary("Get paginated playlists for a user. Private playlists are only returned to their owner")
|
||||
.Produces<PaginatedResponse<PlayListApplicationResponse>>();
|
||||
|
||||
group.MapGet("/users/{userId}", GetPlayListsByUserId)
|
||||
.WithName("GetPlayListsByUserId")
|
||||
.WithSummary("Get Paginated PlayLists For A User.")
|
||||
.Produces<PaginatedResponse<PlayListApplicationResponse>>();
|
||||
group.MapGet("/{id:guid}/tracks", GetPlayListTracks)
|
||||
.AddEndpointFilter<ValidationFilter<PageQuery>>()
|
||||
.WithName("GetPlayListTracks")
|
||||
.WithSummary("Get paginated tracks of a playlist")
|
||||
.Produces<PaginatedResponse<TrackApplicationResponse>>()
|
||||
.Produces(StatusCodes.Status404NotFound);
|
||||
|
||||
group.MapGet("/{playlistId}/tracks", GetPlayListTracks)
|
||||
.WithName("GetPlayListTracks")
|
||||
.WithSummary("Get Paginated Tracks Of A PlayList.")
|
||||
.Produces<PaginatedResponse<TrackApplicationResponse>>()
|
||||
.Produces(StatusCodes.Status404NotFound);
|
||||
group.MapGet("/{id:guid}/cover", GetPlayListCover)
|
||||
.WithName("GetPlayListCover")
|
||||
.WithSummary("Get a playlist cover image")
|
||||
.Produces(StatusCodes.Status200OK)
|
||||
.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")
|
||||
.AddEndpointFilter<ValidationFilter<CreatePlayListRequest>>()
|
||||
.RequireAuthorization()
|
||||
.Produces<PlayListApplicationResponse>(StatusCodes.Status201Created)
|
||||
.ProducesValidationProblem()
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces(StatusCodes.Status404NotFound);
|
||||
|
||||
group.MapPost("/", CreatePlayList)
|
||||
.WithName("CreatePlayList")
|
||||
.WithSummary("Create A New PlayList.")
|
||||
.AddEndpointFilter<ValidationFilter<CreatePlayListRequest>>()
|
||||
.RequireAuthorization()
|
||||
.Produces<PlayListApplicationResponse>(StatusCodes.Status201Created)
|
||||
.ProducesValidationProblem()
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces(StatusCodes.Status404NotFound);
|
||||
group.MapPut("/{id:guid}", UpdatePlayList)
|
||||
.WithName("UpdatePlayList")
|
||||
.WithSummary("Update an existing playlist")
|
||||
.AddEndpointFilter<ValidationFilter<UpdatePlayListRequest>>()
|
||||
.RequireAuthorization()
|
||||
.Produces<PlayListApplicationResponse>()
|
||||
.ProducesValidationProblem()
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces(StatusCodes.Status403Forbidden)
|
||||
.Produces(StatusCodes.Status404NotFound);
|
||||
|
||||
group.MapPut("/{playlistId}", UpdatePlayList)
|
||||
.WithName("UpdatePlayList")
|
||||
.WithSummary("Update An Existing PlayList.")
|
||||
.AddEndpointFilter<ValidationFilter<UpdatePlayListRequest>>()
|
||||
.RequireAuthorization()
|
||||
.Produces<PlayListApplicationResponse>()
|
||||
.ProducesValidationProblem()
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces(StatusCodes.Status404NotFound);
|
||||
group.MapDelete("/{id:guid}", DeletePlayList)
|
||||
.WithName("DeletePlayList")
|
||||
.WithSummary("Delete a playlist")
|
||||
.RequireAuthorization()
|
||||
.Produces(StatusCodes.Status204NoContent)
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces(StatusCodes.Status403Forbidden)
|
||||
.Produces(StatusCodes.Status404NotFound);
|
||||
|
||||
group.MapDelete("/{playlistId}", DeletePlayList)
|
||||
.WithName("DeletePlayList")
|
||||
.WithSummary("Delete A PlayList.")
|
||||
.RequireAuthorization()
|
||||
.Produces(StatusCodes.Status204NoContent)
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces(StatusCodes.Status404NotFound);
|
||||
group.MapPost("/upload-picture", RequestPlayListPictureUpload)
|
||||
.WithName("RequestPlayListPictureUpload")
|
||||
.WithSummary("Request a pre-signed URL to upload a playlist picture")
|
||||
.AddEndpointFilter<ValidationFilter<PictureUploadRequest>>()
|
||||
.RequireAuthorization()
|
||||
.Produces<PictureUploadResponse>()
|
||||
.ProducesValidationProblem()
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces(StatusCodes.Status404NotFound);
|
||||
|
||||
group.MapPost("/upload-picture", RequestPlayListPictureUpload)
|
||||
.WithName("RequestPlayListPictureUpload")
|
||||
.WithSummary("Request A Pre-Signed URL To Upload A PlayList Picture.")
|
||||
.AddEndpointFilter<ValidationFilter<RequestPlayListPictureUploadRequest>>()
|
||||
.RequireAuthorization()
|
||||
.Produces<PlayListPictureUploadResponse>()
|
||||
.ProducesValidationProblem()
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces(StatusCodes.Status404NotFound);
|
||||
group.MapPost("/{id:guid}/tracks", AddPlayListTracks)
|
||||
.WithName("AddPlayListTracks")
|
||||
.WithSummary("Add tracks to a playlist, skipping the ones it already has")
|
||||
.AddEndpointFilter<ValidationFilter<AddTracksRequest>>()
|
||||
.RequireAuthorization()
|
||||
.Produces(StatusCodes.Status204NoContent)
|
||||
.ProducesValidationProblem()
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces(StatusCodes.Status403Forbidden)
|
||||
.Produces(StatusCodes.Status404NotFound)
|
||||
.Produces(StatusCodes.Status409Conflict);
|
||||
|
||||
group.MapPost("/{playlistId}/tracks", AddPlayListTrack)
|
||||
.WithName("AddPlayListTrack")
|
||||
.WithSummary("Add One Of Your Tracks To A PlayList.")
|
||||
.RequireAuthorization()
|
||||
.Produces<TrackApplicationResponse>(StatusCodes.Status201Created)
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces(StatusCodes.Status404NotFound)
|
||||
.Produces(StatusCodes.Status409Conflict);
|
||||
group.MapPost("/{id:guid}/albums/{albumId:guid}", AddAlbumToPlayList)
|
||||
.WithName("AddAlbumToPlayList")
|
||||
.WithSummary("Add all the tracks of an album to a playlist, skipping the ones it already has")
|
||||
.RequireAuthorization()
|
||||
.Produces<AddAlbumToPlayListResponse>()
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces(StatusCodes.Status403Forbidden)
|
||||
.Produces(StatusCodes.Status404NotFound);
|
||||
|
||||
group.MapPost("/{playlistId}/albums/{albumId}", AddAlbumToPlayList)
|
||||
.WithName("AddAlbumToPlayList")
|
||||
.WithSummary("Add All The Tracks Of An Album To One Of Your PlayLists, Skipping Those Already In It.")
|
||||
.RequireAuthorization()
|
||||
.Produces<AddAlbumToPlayListResponse>()
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces(StatusCodes.Status404NotFound);
|
||||
group.MapDelete("/{id:guid}/tracks/{trackId:guid}", RemoveTrackFromPlayList)
|
||||
.WithName("RemoveTrackFromPlayList")
|
||||
.WithSummary("Remove a track from a playlist")
|
||||
.RequireAuthorization()
|
||||
.Produces(StatusCodes.Status204NoContent)
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces(StatusCodes.Status403Forbidden)
|
||||
.Produces(StatusCodes.Status404NotFound);
|
||||
|
||||
group.MapDelete("/{playlistId}/tracks/{trackId}", RemoveTrackFromPlayList)
|
||||
.WithName("RemoveTrackFromPlayList")
|
||||
.WithSummary("Remove A Track From A PlayList.")
|
||||
.RequireAuthorization()
|
||||
.Produces(StatusCodes.Status204NoContent)
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces(StatusCodes.Status404NotFound);
|
||||
return app;
|
||||
}
|
||||
|
||||
return app;
|
||||
}
|
||||
private static async Task<IResult> GetPlayListById(
|
||||
IMediator mediator,
|
||||
CurrentUser currentUser,
|
||||
Guid id,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(new GetPlayListByIdQuery(id, currentUser.Id), cancellationToken);
|
||||
return result.ToHttpResult();
|
||||
}
|
||||
|
||||
private static async Task<IResult> GetPlayLists(
|
||||
IMediator mediator,
|
||||
CancellationToken cancellationToken,
|
||||
int pageNumber = 1,
|
||||
int pageSize = 10)
|
||||
{
|
||||
var result = await mediator.Send(new GetPlayListsQuery(pageNumber, pageSize), cancellationToken);
|
||||
return result.ToHttpResult();
|
||||
}
|
||||
private static async Task<IResult> GetPlayListsByUserId(
|
||||
IMediator mediator,
|
||||
CurrentUser currentUser,
|
||||
long userId,
|
||||
string? name,
|
||||
[AsParameters] PageQuery page,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(new GetPlayListsByUserIdQuery(userId, name, page.PageNumber, page.PageSize, currentUser.Id), cancellationToken);
|
||||
return Results.Ok(result);
|
||||
}
|
||||
|
||||
private static async Task<IResult> GetPlayListById(
|
||||
IMediator mediator,
|
||||
CurrentUser currentUser,
|
||||
Guid id,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(new GetPlayListByIdQuery(id, currentUser.Id), cancellationToken);
|
||||
return result.ToHttpResult();
|
||||
}
|
||||
private static async Task<IResult> GetPlayListTracks(
|
||||
IMediator mediator,
|
||||
CurrentUser currentUser,
|
||||
Guid id,
|
||||
[AsParameters] PageQuery page,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(new GetPlayListTracksQuery(id, page.PageNumber, page.PageSize, currentUser.Id), cancellationToken);
|
||||
return result.ToHttpResult();
|
||||
}
|
||||
|
||||
private static async Task<IResult> GetPlayListsByUserId(
|
||||
IMediator mediator,
|
||||
long userId,
|
||||
CancellationToken cancellationToken,
|
||||
string? name,
|
||||
int pageNumber = 1,
|
||||
int pageSize = 10,
|
||||
bool onlyPublic = false)
|
||||
{
|
||||
var result = await mediator.Send(new GetPlayListsByUserIdQuery(userId, name, pageNumber, pageSize, onlyPublic), cancellationToken);
|
||||
return result.ToHttpResult();
|
||||
}
|
||||
private static Task<IResult> GetPlayListCover(
|
||||
IMediator mediator,
|
||||
IStorageService storageService,
|
||||
HttpResponse response,
|
||||
Guid id,
|
||||
CancellationToken cancellationToken,
|
||||
string size = "medium")
|
||||
{
|
||||
return CoverEndpoint.Stream(mediator, storageService, response, CoverOwner.PlayList, id, size, cancellationToken);
|
||||
}
|
||||
|
||||
private static async Task<IResult> GetPlayListTracks(
|
||||
IMediator mediator,
|
||||
Guid playlistId,
|
||||
CancellationToken cancellationToken,
|
||||
int pageNumber = 1,
|
||||
int pageSize = 10)
|
||||
{
|
||||
var result = await mediator.Send(new GetPlayListTracksQuery(playlistId, pageNumber, pageSize), cancellationToken);
|
||||
return result.ToHttpResult();
|
||||
}
|
||||
private static async Task<IResult> CreatePlayList(
|
||||
IMediator mediator,
|
||||
CurrentUser currentUser,
|
||||
CreatePlayListRequest request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(
|
||||
new CreatePlayListCommand(currentUser.RequiredId, request.Name, request.Description, request.PictureIntentId, request.Visibility),
|
||||
cancellationToken);
|
||||
|
||||
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();
|
||||
return result.ToCreatedResult(playList => $"/playlists/{playList.Id}");
|
||||
}
|
||||
|
||||
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> UpdatePlayList(
|
||||
IMediator mediator,
|
||||
CurrentUser currentUser,
|
||||
Guid id,
|
||||
UpdatePlayListRequest request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(
|
||||
new UpdatePlayListCommand(currentUser.RequiredId, id, request.Name, request.Description, request.PictureIntentId, request.Visibility),
|
||||
cancellationToken);
|
||||
|
||||
private static async Task<IResult> CreatePlayList(
|
||||
IMediator mediator,
|
||||
CurrentUser currentUser,
|
||||
CreatePlayListRequest request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(
|
||||
new CreatePlayListCommand(currentUser.RequiredId, request.Name, request.Description, request.PictureIntentId, request.Visibility),
|
||||
cancellationToken);
|
||||
return result.ToHttpResult();
|
||||
}
|
||||
|
||||
return result.ToCreatedResult(playList => $"/playlists/{playList.Id}");
|
||||
}
|
||||
private static async Task<IResult> DeletePlayList(
|
||||
IMediator mediator,
|
||||
CurrentUser currentUser,
|
||||
Guid id,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(new DeletePlayListCommand(currentUser.RequiredId, id), cancellationToken);
|
||||
return result.ToHttpResult();
|
||||
}
|
||||
|
||||
private static async Task<IResult> UpdatePlayList(
|
||||
IMediator mediator,
|
||||
CurrentUser currentUser,
|
||||
Guid playlistId,
|
||||
UpdatePlayListRequest request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(
|
||||
new UpdatePlayListCommand(currentUser.RequiredId, playlistId, request.NewName, request.NewDescription, request.NewPictureIntentId, request.NewVisibility),
|
||||
cancellationToken);
|
||||
private static async Task<IResult> RequestPlayListPictureUpload(
|
||||
IMediator mediator,
|
||||
CurrentUser currentUser,
|
||||
PictureUploadRequest request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(
|
||||
new RequestPlayListPictureUploadCommand(currentUser.RequiredId, request.FileType, request.ContentType, request.ExpectedSizeBytes),
|
||||
cancellationToken);
|
||||
|
||||
return result.ToHttpResult();
|
||||
}
|
||||
return result.ToHttpResult();
|
||||
}
|
||||
|
||||
private static async Task<IResult> DeletePlayList(
|
||||
IMediator mediator,
|
||||
CurrentUser currentUser,
|
||||
Guid playlistId,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(new DeletePlayListCommand(currentUser.RequiredId, playlistId), cancellationToken);
|
||||
return result.ToHttpResult();
|
||||
}
|
||||
private static async Task<IResult> AddPlayListTracks(
|
||||
IMediator mediator,
|
||||
CurrentUser currentUser,
|
||||
Guid id,
|
||||
AddTracksRequest request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(
|
||||
new AddTracksToPlayListCommand(currentUser.RequiredId, id, request.TrackIds),
|
||||
cancellationToken);
|
||||
|
||||
private static async Task<IResult> RequestPlayListPictureUpload(
|
||||
IMediator mediator,
|
||||
CurrentUser currentUser,
|
||||
RequestPlayListPictureUploadRequest request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(
|
||||
new RequestPlayListPictureUploadCommand(currentUser.RequiredId, request.FileType, request.ContentType, request.ExpectedSizeBytes),
|
||||
cancellationToken);
|
||||
return result.ToHttpResult();
|
||||
}
|
||||
|
||||
return result.ToHttpResult();
|
||||
}
|
||||
private static async Task<IResult> AddAlbumToPlayList(
|
||||
IMediator mediator,
|
||||
CurrentUser currentUser,
|
||||
Guid id,
|
||||
Guid albumId,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(
|
||||
new AddAlbumToPlayListCommand(currentUser.RequiredId, id, albumId),
|
||||
cancellationToken);
|
||||
|
||||
private static async Task<IResult> AddPlayListTrack(
|
||||
IMediator mediator,
|
||||
CurrentUser currentUser,
|
||||
Guid playlistId,
|
||||
AddPlayListTrackRequest request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var addResult = await mediator.Send(
|
||||
new AddTrackToPlayListCommand(currentUser.RequiredId, playlistId, request.TrackId),
|
||||
cancellationToken);
|
||||
if (addResult.IsError)
|
||||
return addResult.ToHttpResult();
|
||||
return result.ToHttpResult();
|
||||
}
|
||||
|
||||
var trackResult = await mediator.Send(new GetTrackByIdQuery(request.TrackId), cancellationToken);
|
||||
return trackResult.ToCreatedResult(track => $"/tracks/{track.Id}");
|
||||
}
|
||||
private static async Task<IResult> RemoveTrackFromPlayList(
|
||||
IMediator mediator,
|
||||
CurrentUser currentUser,
|
||||
Guid id,
|
||||
Guid trackId,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(
|
||||
new RemoveTrackFromPlayListCommand(currentUser.RequiredId, id, trackId),
|
||||
cancellationToken);
|
||||
|
||||
private static async Task<IResult> AddAlbumToPlayList(
|
||||
IMediator mediator,
|
||||
CurrentUser currentUser,
|
||||
Guid playlistId,
|
||||
Guid albumId,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(
|
||||
new AddAlbumToPlayListCommand(currentUser.RequiredId, playlistId, albumId),
|
||||
cancellationToken);
|
||||
|
||||
return result.ToHttpResult();
|
||||
}
|
||||
|
||||
private static async Task<IResult> RemoveTrackFromPlayList(
|
||||
IMediator mediator,
|
||||
CurrentUser currentUser,
|
||||
Guid playlistId,
|
||||
Guid trackId,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(
|
||||
new RemoveTrackFromPlayListCommand(currentUser.RequiredId, playlistId, trackId),
|
||||
cancellationToken);
|
||||
|
||||
return result.ToHttpResult();
|
||||
return result.ToHttpResult();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,223 +1,206 @@
|
||||
using Mediator;
|
||||
using Musify.Domain.ValueObjects;
|
||||
using Musify.Application.Tracks;
|
||||
using Musify.Application.Tracks.Responses;
|
||||
using Musify.Application.Shared;
|
||||
using Musify.Application.Contracts;
|
||||
using Musify.Api.Authentication;
|
||||
using Musify.Api.DataTransferObjects.Tracks;
|
||||
using Musify.Api.Extensions;
|
||||
using Musify.Api.Filters;
|
||||
using Musify.Api.Models;
|
||||
using Musify.Application.Contracts;
|
||||
using Musify.Application.Pictures;
|
||||
using Musify.Application.Shared;
|
||||
using Musify.Application.Tracks;
|
||||
using Musify.Application.Tracks.Responses;
|
||||
|
||||
namespace Musify.Api.Endpoints;
|
||||
|
||||
public static class TrackEndpoints
|
||||
namespace Musify.Api.Endpoints
|
||||
{
|
||||
public static IEndpointRouteBuilder MapTrackEndpoints(this IEndpointRouteBuilder app)
|
||||
public static class TrackEndpoints
|
||||
{
|
||||
var group = app.MapGroup("/tracks")
|
||||
.WithTags("Tracks");
|
||||
|
||||
group.MapGet("/", GetTracks)
|
||||
.WithName("GetTracks")
|
||||
.WithSummary("Get Paginated Tracks, Optionally Filtered By Name And Genre.")
|
||||
.Produces<TracksSearchResponse>()
|
||||
.ProducesValidationProblem();
|
||||
|
||||
group.MapGet("/{id}", GetTrackById)
|
||||
.WithName("GetTrackById")
|
||||
.WithSummary("Get A Track By Id.")
|
||||
.Produces<TrackApplicationResponse>()
|
||||
.Produces(StatusCodes.Status404NotFound);
|
||||
|
||||
group.MapGet("/{id}/cover", GetTrackCover)
|
||||
.WithName("GetTrackCover")
|
||||
.WithSummary("Get A Track Cover Image.")
|
||||
.Produces(StatusCodes.Status200OK)
|
||||
.Produces(StatusCodes.Status404NotFound);
|
||||
|
||||
group.MapGet("/{id}/stream", GetTrackStream)
|
||||
.WithName("GetTrackStream")
|
||||
.WithSummary("Get A Streaming Manifest URL And Ticket For A Track. Works Anonymously When The Playback Configuration Allows It.")
|
||||
.Produces<TrackStreamResponse>()
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces(StatusCodes.Status404NotFound);
|
||||
|
||||
group.MapPut("/listens/{listenId}/progress", RecordListeningProgress)
|
||||
.WithName("RecordListeningProgress")
|
||||
.WithSummary("Report The Total Seconds Actually Played For A Listen Started By The Stream Endpoint.")
|
||||
.RequireAuthorization()
|
||||
.Produces(StatusCodes.Status204NoContent)
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces(StatusCodes.Status404NotFound);
|
||||
|
||||
group.MapGet("/users/{userId}", GetTracksByUserId)
|
||||
.WithName("GetTracksByUserId")
|
||||
.WithSummary("Get Paginated Tracks For A User.")
|
||||
.Produces<PaginatedResponse<TrackApplicationResponse>>();
|
||||
|
||||
group.MapPost("/", CreateTrack)
|
||||
.WithName("CreateTrack")
|
||||
.WithSummary("Create A New Track.")
|
||||
.AddEndpointFilter<ValidationFilter<CreateTrackRequest>>()
|
||||
.RequireAuthorization()
|
||||
.Produces<TrackApplicationResponse>(StatusCodes.Status201Created)
|
||||
.ProducesValidationProblem()
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces(StatusCodes.Status404NotFound)
|
||||
.Produces(StatusCodes.Status409Conflict);
|
||||
|
||||
group.MapDelete("/{trackId}", DeleteTrack)
|
||||
.WithName("DeleteTrack")
|
||||
.WithSummary("Delete A Track.")
|
||||
.RequireAuthorization()
|
||||
.Produces(StatusCodes.Status204NoContent)
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces(StatusCodes.Status404NotFound)
|
||||
.Produces(StatusCodes.Status409Conflict);
|
||||
|
||||
group.MapPost("/upload-urls", RequestTrackUploadUrls)
|
||||
.WithName("RequestTrackUploadUrls")
|
||||
.WithSummary("Request Pre-Signed URLs To Upload Track Picture And Audio.")
|
||||
.AddEndpointFilter<ValidationFilter<RequestTrackUploadUrlsRequest>>()
|
||||
.RequireAuthorization()
|
||||
.Produces<TrackUploadUrlsResponse>()
|
||||
.ProducesValidationProblem()
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces(StatusCodes.Status404NotFound)
|
||||
.Produces(StatusCodes.Status409Conflict);
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
private static async Task<IResult> GetTracks(
|
||||
IMediator mediator,
|
||||
CancellationToken cancellationToken,
|
||||
string? name,
|
||||
string? genre,
|
||||
int pageNumber = 1,
|
||||
int pageSize = 10)
|
||||
{
|
||||
Genre? parsedGenre = null;
|
||||
if (!string.IsNullOrWhiteSpace(genre))
|
||||
public static IEndpointRouteBuilder MapTrackEndpoints(this IEndpointRouteBuilder app)
|
||||
{
|
||||
if (!Enum.TryParse<Genre>(genre, ignoreCase: true, out var value) || !Enum.IsDefined(value))
|
||||
{
|
||||
return Results.ValidationProblem(new Dictionary<string, string[]>
|
||||
{
|
||||
[nameof(genre)] = [$"'{genre}' is not a valid genre."]
|
||||
});
|
||||
}
|
||||
var group = app.MapGroup("/tracks")
|
||||
.WithTags("Tracks");
|
||||
|
||||
parsedGenre = value;
|
||||
group.MapGet("/", GetTracks)
|
||||
.AddEndpointFilter<ValidationFilter<PageQuery>>()
|
||||
.WithName("GetTracks")
|
||||
.WithSummary("Get paginated tracks, optionally filtered by name and genre")
|
||||
.Produces<PaginatedResponse<TrackApplicationResponse>>()
|
||||
.ProducesValidationProblem();
|
||||
|
||||
group.MapGet("/{id:guid}", GetTrackById)
|
||||
.WithName("GetTrackById")
|
||||
.WithSummary("Get a track by id")
|
||||
.Produces<TrackApplicationResponse>()
|
||||
.Produces(StatusCodes.Status404NotFound);
|
||||
|
||||
group.MapGet("/{id:guid}/cover", GetTrackCover)
|
||||
.WithName("GetTrackCover")
|
||||
.WithSummary("Get a track cover image")
|
||||
.Produces(StatusCodes.Status200OK)
|
||||
.Produces(StatusCodes.Status404NotFound);
|
||||
|
||||
group.MapGet("/{id:guid}/stream", GetTrackStream)
|
||||
.WithName("GetTrackStream")
|
||||
.WithSummary("Get a streaming manifest URL and ticket for a track. Works anonymously when the playback configuration allows it")
|
||||
.Produces<TrackStreamResponse>()
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces(StatusCodes.Status404NotFound);
|
||||
|
||||
group.MapPut("/listens/{listenId:guid}/progress", RecordListeningProgress)
|
||||
.AddEndpointFilter<ValidationFilter<RecordListeningProgressRequest>>()
|
||||
.WithName("RecordListeningProgress")
|
||||
.WithSummary("Report the total seconds actually played for a listen started by the stream endpoint")
|
||||
.RequireAuthorization()
|
||||
.Produces(StatusCodes.Status204NoContent)
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces(StatusCodes.Status404NotFound);
|
||||
|
||||
group.MapGet("/users/{userId:long}", GetTracksByUserId)
|
||||
.AddEndpointFilter<ValidationFilter<PageQuery>>()
|
||||
.WithName("GetTracksByUserId")
|
||||
.WithSummary("Get paginated tracks for a user")
|
||||
.Produces<PaginatedResponse<TrackApplicationResponse>>();
|
||||
|
||||
group.MapPost("/", CreateTrack)
|
||||
.WithName("CreateTrack")
|
||||
.WithSummary("Create a new track")
|
||||
.AddEndpointFilter<ValidationFilter<CreateTrackRequest>>()
|
||||
.RequireAuthorization()
|
||||
.Produces<TrackApplicationResponse>(StatusCodes.Status201Created)
|
||||
.ProducesValidationProblem()
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces(StatusCodes.Status404NotFound)
|
||||
.Produces(StatusCodes.Status409Conflict);
|
||||
|
||||
group.MapDelete("/{id:guid}", DeleteTrack)
|
||||
.WithName("DeleteTrack")
|
||||
.WithSummary("Delete a track")
|
||||
.RequireAuthorization()
|
||||
.Produces(StatusCodes.Status204NoContent)
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces(StatusCodes.Status403Forbidden)
|
||||
.Produces(StatusCodes.Status404NotFound)
|
||||
.Produces(StatusCodes.Status409Conflict);
|
||||
|
||||
group.MapPost("/upload-urls", RequestTrackUploadUrls)
|
||||
.WithName("RequestTrackUploadUrls")
|
||||
.WithSummary("Request pre-signed URLs to upload track picture and audio")
|
||||
.AddEndpointFilter<ValidationFilter<RequestTrackUploadUrlsRequest>>()
|
||||
.RequireAuthorization()
|
||||
.Produces<TrackUploadUrlsResponse>()
|
||||
.ProducesValidationProblem()
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces(StatusCodes.Status404NotFound)
|
||||
.Produces(StatusCodes.Status409Conflict);
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
var result = await mediator.Send(new GetTracksQuery(name, pageNumber, pageSize, parsedGenre), cancellationToken);
|
||||
return result.ToHttpResult();
|
||||
}
|
||||
private static async Task<IResult> GetTracks(
|
||||
IMediator mediator,
|
||||
string? name,
|
||||
GenreParameter? genre,
|
||||
[AsParameters] PageQuery page,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(new GetTracksQuery(name, page.PageNumber, page.PageSize, genre?.Value), cancellationToken);
|
||||
return Results.Ok(result);
|
||||
}
|
||||
|
||||
private static async Task<IResult> GetTrackById(
|
||||
IMediator mediator,
|
||||
Guid id,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(new GetTrackByIdQuery(id), cancellationToken);
|
||||
return result.ToHttpResult();
|
||||
}
|
||||
private static async Task<IResult> GetTrackById(
|
||||
IMediator mediator,
|
||||
Guid id,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(new GetTrackByIdQuery(id), cancellationToken);
|
||||
return result.ToHttpResult();
|
||||
}
|
||||
|
||||
private static async Task<IResult> GetTrackCover(
|
||||
IMediator mediator,
|
||||
IStorageService storageService,
|
||||
HttpResponse response,
|
||||
Guid id,
|
||||
CancellationToken cancellationToken,
|
||||
string size = "medium")
|
||||
{
|
||||
var result = await mediator.Send(new GetTrackCoverQuery(id, size), cancellationToken);
|
||||
if (result.IsError)
|
||||
return Results.NotFound();
|
||||
private static Task<IResult> GetTrackCover(
|
||||
IMediator mediator,
|
||||
IStorageService storageService,
|
||||
HttpResponse response,
|
||||
Guid id,
|
||||
CancellationToken cancellationToken,
|
||||
string size = "medium")
|
||||
{
|
||||
return CoverEndpoint.Stream(mediator, storageService, response, CoverOwner.Track, id, size, cancellationToken);
|
||||
}
|
||||
|
||||
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> GetTrackStream(
|
||||
IMediator mediator,
|
||||
CurrentUser currentUser,
|
||||
Guid id,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(new StartListeningCommand(id, currentUser.Id), cancellationToken);
|
||||
return result.ToHttpResult();
|
||||
}
|
||||
|
||||
private static async Task<IResult> GetTrackStream(
|
||||
IMediator mediator,
|
||||
CurrentUser currentUser,
|
||||
Guid id,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(new GetTrackStreamQuery(id, currentUser.Id), cancellationToken);
|
||||
return result.ToHttpResult();
|
||||
}
|
||||
private static async Task<IResult> RecordListeningProgress(
|
||||
IMediator mediator,
|
||||
CurrentUser currentUser,
|
||||
Guid listenId,
|
||||
RecordListeningProgressRequest request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(
|
||||
new RecordListeningProgressCommand(currentUser.RequiredId, listenId, request.PlayedSeconds),
|
||||
cancellationToken);
|
||||
return result.ToHttpResult();
|
||||
}
|
||||
|
||||
private static async Task<IResult> RecordListeningProgress(
|
||||
IMediator mediator,
|
||||
CurrentUser currentUser,
|
||||
Guid listenId,
|
||||
RecordListeningProgressRequest request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(
|
||||
new RecordListeningProgressCommand(currentUser.RequiredId, listenId, request.PlayedSeconds),
|
||||
cancellationToken);
|
||||
return result.ToHttpResult();
|
||||
}
|
||||
private static async Task<IResult> GetTracksByUserId(
|
||||
IMediator mediator,
|
||||
long userId,
|
||||
string? name,
|
||||
[AsParameters] PageQuery page,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(new GetTracksByUserIdQuery(userId, name, page.PageNumber, page.PageSize), cancellationToken);
|
||||
return result.ToHttpResult();
|
||||
}
|
||||
|
||||
private static async Task<IResult> GetTracksByUserId(
|
||||
IMediator mediator,
|
||||
long userId,
|
||||
CancellationToken cancellationToken,
|
||||
string? name,
|
||||
int pageNumber = 1,
|
||||
int pageSize = 10)
|
||||
{
|
||||
var result = await mediator.Send(new GetTracksByUserIdQuery(userId, name, pageNumber, pageSize), cancellationToken);
|
||||
return result.ToHttpResult();
|
||||
}
|
||||
private static async Task<IResult> CreateTrack(
|
||||
IMediator mediator,
|
||||
CurrentUser currentUser,
|
||||
CreateTrackRequest request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(
|
||||
new CreateTrackCommand(currentUser.RequiredId, request.Title, request.PictureIntentId, request.AudioIntentId, request.Tags, request.IsExplicit),
|
||||
cancellationToken);
|
||||
|
||||
private static async Task<IResult> CreateTrack(
|
||||
IMediator mediator,
|
||||
CurrentUser currentUser,
|
||||
CreateTrackRequest request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(
|
||||
new CreateTrackCommand(currentUser.RequiredId, request.Title, request.PictureIntentId, request.AudioIntentId, request.Tags, request.IsExplicit),
|
||||
cancellationToken);
|
||||
return result.ToCreatedResult(track => $"/tracks/{track.Id}");
|
||||
}
|
||||
|
||||
return result.ToCreatedResult(track => $"/tracks/{track.Id}");
|
||||
}
|
||||
private static async Task<IResult> DeleteTrack(
|
||||
IMediator mediator,
|
||||
CurrentUser currentUser,
|
||||
Guid id,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(new DeleteTrackCommand(currentUser.RequiredId, id), cancellationToken);
|
||||
return result.ToHttpResult();
|
||||
}
|
||||
|
||||
private static async Task<IResult> DeleteTrack(
|
||||
IMediator mediator,
|
||||
CurrentUser currentUser,
|
||||
Guid trackId,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(new DeleteTrackCommand(currentUser.RequiredId, trackId), cancellationToken);
|
||||
return result.ToHttpResult();
|
||||
}
|
||||
private static async Task<IResult> RequestTrackUploadUrls(
|
||||
IMediator mediator,
|
||||
CurrentUser currentUser,
|
||||
RequestTrackUploadUrlsRequest request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(
|
||||
new RequestTrackUploadUrlsCommand(
|
||||
currentUser.RequiredId,
|
||||
request.PictureFileType,
|
||||
request.PictureContentType,
|
||||
request.AudioFileType,
|
||||
request.AudioContentType,
|
||||
request.ExpectedPictureSizeBytes,
|
||||
request.ExpectedAudioSizeBytes),
|
||||
cancellationToken);
|
||||
|
||||
private static async Task<IResult> RequestTrackUploadUrls(
|
||||
IMediator mediator,
|
||||
CurrentUser currentUser,
|
||||
RequestTrackUploadUrlsRequest request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(
|
||||
new RequestTrackUploadUrlsCommand(
|
||||
currentUser.RequiredId,
|
||||
request.PictureFileType,
|
||||
request.PictureContentType,
|
||||
request.AudioFileType,
|
||||
request.AudioContentType,
|
||||
request.ExpectedPictureSizeBytes,
|
||||
request.ExpectedAudioSizeBytes),
|
||||
cancellationToken);
|
||||
|
||||
return result.ToHttpResult();
|
||||
return result.ToHttpResult();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,222 +1,213 @@
|
||||
using Mediator;
|
||||
using Musify.Api.Authentication;
|
||||
using Musify.Api.DataTransferObjects.Users;
|
||||
using Musify.Api.Extensions;
|
||||
using Musify.Api.Filters;
|
||||
using Musify.Api.Models;
|
||||
using Musify.Application.Shared;
|
||||
using Musify.Application.Tracks;
|
||||
using Musify.Application.Tracks.Responses;
|
||||
using Musify.Application.Users;
|
||||
using Musify.Application.Users.Responses;
|
||||
|
||||
namespace Musify.Api.Endpoints;
|
||||
|
||||
public static class UserEndpoints
|
||||
namespace Musify.Api.Endpoints
|
||||
{
|
||||
public static IEndpointRouteBuilder MapUserEndpoints(this IEndpointRouteBuilder app)
|
||||
public static class UserEndpoints
|
||||
{
|
||||
var group = app.MapGroup("/users")
|
||||
.WithTags("Users");
|
||||
public static IEndpointRouteBuilder MapUserEndpoints(this IEndpointRouteBuilder app)
|
||||
{
|
||||
var group = app.MapGroup("/users")
|
||||
.WithTags("Users");
|
||||
|
||||
group.MapGet("/", GetUsers)
|
||||
.WithName("GetUsers")
|
||||
.WithSummary("Get Paginated Users.")
|
||||
.Produces<PaginatedResponse<UserSummaryResponse>>();
|
||||
group.MapGet("/", GetUsers)
|
||||
.AddEndpointFilter<ValidationFilter<PageQuery>>()
|
||||
.WithName("GetUsers")
|
||||
.WithSummary("Get paginated users")
|
||||
.Produces<PaginatedResponse<UserSummaryResponse>>();
|
||||
|
||||
group.MapGet("/{id}", GetUserById)
|
||||
.WithName("GetUserById")
|
||||
.WithSummary("Get A User By Id.")
|
||||
.Produces<UserApplicationResponse>()
|
||||
.Produces(StatusCodes.Status404NotFound);
|
||||
group.MapGet("/{id:long}", GetUserById)
|
||||
.WithName("GetUserById")
|
||||
.WithSummary("Get a user by id")
|
||||
.Produces<UserApplicationResponse>()
|
||||
.Produces(StatusCodes.Status404NotFound);
|
||||
|
||||
group.MapGet("/{id}/listening-history", GetListeningHistory)
|
||||
.WithName("GetListeningHistory")
|
||||
.WithSummary("Get A User'S Listening History.")
|
||||
.Produces<IEnumerable<TrackApplicationResponse>>()
|
||||
.Produces(StatusCodes.Status404NotFound);
|
||||
group.MapGet("/{id:long}/listening-history", GetListeningHistory)
|
||||
.WithName("GetListeningHistory")
|
||||
.WithSummary("Get a user's listening history")
|
||||
.Produces<IEnumerable<TrackApplicationResponse>>()
|
||||
.Produces(StatusCodes.Status404NotFound);
|
||||
|
||||
group.MapGet("/{id}/listening-stats", GetListeningStats)
|
||||
.WithName("GetListeningStats")
|
||||
.WithSummary("Get A User'S Listening Stats For The Current Week And Streak.")
|
||||
.Produces<ListeningStatsResponse>();
|
||||
group.MapGet("/{id:long}/listening-stats", GetListeningStats)
|
||||
.WithName("GetListeningStats")
|
||||
.WithSummary("Get a user's listening stats for the current week and streak")
|
||||
.RequireAuthorization()
|
||||
.Produces<ListeningStatsResponse>()
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces(StatusCodes.Status403Forbidden);
|
||||
|
||||
group.MapGet("/{id}/last-listened-track", GetLastTrackListenedByUserId)
|
||||
.WithName("GetLastTrackListenedByUserId")
|
||||
.WithSummary("Get The Last Track A User Listened To.")
|
||||
.Produces<TrackApplicationResponse>()
|
||||
.Produces(StatusCodes.Status404NotFound);
|
||||
group.MapGet("/{id:long}/last-listened-track", GetLastTrackListenedByUserId)
|
||||
.WithName("GetLastTrackListenedByUserId")
|
||||
.WithSummary("Get the last track a user listened to")
|
||||
.Produces<TrackApplicationResponse>()
|
||||
.Produces(StatusCodes.Status204NoContent);
|
||||
|
||||
group.MapGet("/{id}/profile", GetUserProfile)
|
||||
.WithName("GetUserProfile")
|
||||
.WithSummary("Get A User'S Public Profile, Including Follower Counts.")
|
||||
.Produces<UserProfileResponse>()
|
||||
.Produces(StatusCodes.Status404NotFound);
|
||||
group.MapGet("/{id:long}/profile", GetUserProfile)
|
||||
.WithName("GetUserProfile")
|
||||
.WithSummary("Get a user's public profile, including follower counts")
|
||||
.Produces<UserProfileResponse>()
|
||||
.Produces(StatusCodes.Status404NotFound);
|
||||
|
||||
group.MapGet("/{id}/followers", GetFollowers)
|
||||
.WithName("GetUserFollowers")
|
||||
.WithSummary("Get The Users Following A User. Visible To The User And To Mutual Followers.")
|
||||
.Produces<PaginatedResponse<UserSummaryResponse>>()
|
||||
.Produces(StatusCodes.Status403Forbidden)
|
||||
.Produces(StatusCodes.Status404NotFound);
|
||||
group.MapGet("/{id:long}/followers", GetFollowers)
|
||||
.AddEndpointFilter<ValidationFilter<PageQuery>>()
|
||||
.WithName("GetUserFollowers")
|
||||
.WithSummary("Get the users following a user. Visible to the user and to mutual followers")
|
||||
.Produces<PaginatedResponse<UserSummaryResponse>>()
|
||||
.Produces(StatusCodes.Status403Forbidden)
|
||||
.Produces(StatusCodes.Status404NotFound);
|
||||
|
||||
group.MapGet("/{id}/following", GetFollowing)
|
||||
.WithName("GetUserFollowing")
|
||||
.WithSummary("Get The Users A User Follows.")
|
||||
.Produces<PaginatedResponse<UserSummaryResponse>>()
|
||||
.Produces(StatusCodes.Status404NotFound);
|
||||
group.MapGet("/{id:long}/following", GetFollowing)
|
||||
.AddEndpointFilter<ValidationFilter<PageQuery>>()
|
||||
.WithName("GetUserFollowing")
|
||||
.WithSummary("Get the users a user follows")
|
||||
.Produces<PaginatedResponse<UserSummaryResponse>>()
|
||||
.Produces(StatusCodes.Status404NotFound);
|
||||
|
||||
group.MapGet("/{id}/is-following", IsFollowing)
|
||||
.WithName("IsFollowingUser")
|
||||
.WithSummary("Check Whether The Current User Follows Another User.")
|
||||
.RequireAuthorization()
|
||||
.Produces<bool>()
|
||||
.Produces(StatusCodes.Status401Unauthorized);
|
||||
group.MapGet("/{id:long}/is-following", IsFollowing)
|
||||
.WithName("IsFollowingUser")
|
||||
.WithSummary("Check whether the current user follows another user")
|
||||
.RequireAuthorization()
|
||||
.Produces<bool>()
|
||||
.Produces(StatusCodes.Status401Unauthorized);
|
||||
|
||||
group.MapPost("/{id}/follow", FollowUser)
|
||||
.WithName("FollowUser")
|
||||
.WithSummary("Follow A User.")
|
||||
.RequireAuthorization()
|
||||
.Produces(StatusCodes.Status204NoContent)
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces(StatusCodes.Status404NotFound)
|
||||
.ProducesValidationProblem();
|
||||
group.MapPost("/{id:long}/follow", FollowUser)
|
||||
.WithName("FollowUser")
|
||||
.WithSummary("Follow a user")
|
||||
.RequireAuthorization()
|
||||
.Produces(StatusCodes.Status204NoContent)
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces(StatusCodes.Status404NotFound)
|
||||
.ProducesValidationProblem();
|
||||
|
||||
group.MapDelete("/{id}/follow", UnfollowUser)
|
||||
.WithName("UnfollowUser")
|
||||
.WithSummary("Unfollow A User.")
|
||||
.RequireAuthorization()
|
||||
.Produces(StatusCodes.Status204NoContent)
|
||||
.Produces(StatusCodes.Status401Unauthorized);
|
||||
group.MapDelete("/{id:long}/follow", UnfollowUser)
|
||||
.WithName("UnfollowUser")
|
||||
.WithSummary("Unfollow a user")
|
||||
.RequireAuthorization()
|
||||
.Produces(StatusCodes.Status204NoContent)
|
||||
.Produces(StatusCodes.Status401Unauthorized);
|
||||
|
||||
group.MapPost("/", CreateUser)
|
||||
.WithName("CreateUser")
|
||||
.WithSummary("Create A New User.")
|
||||
.AddEndpointFilter<ValidationFilter<CreateUserRequest>>()
|
||||
.Produces<UserApplicationResponse>(StatusCodes.Status201Created)
|
||||
.ProducesValidationProblem()
|
||||
.Produces(StatusCodes.Status409Conflict);
|
||||
return app;
|
||||
}
|
||||
|
||||
return app;
|
||||
}
|
||||
private static async Task<IResult> GetUsers(
|
||||
IMediator mediator,
|
||||
CurrentUser currentUser,
|
||||
string? usernameSearch,
|
||||
[AsParameters] PageQuery page,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(new GetUsersQuery(page.PageNumber, page.PageSize, usernameSearch, currentUser.Id), cancellationToken);
|
||||
return Results.Ok(result);
|
||||
}
|
||||
|
||||
private static async Task<IResult> GetUsers(
|
||||
IMediator mediator,
|
||||
CurrentUser currentUser,
|
||||
CancellationToken cancellationToken,
|
||||
string? usernameSearch,
|
||||
int pageNumber = 1,
|
||||
int pageSize = 10)
|
||||
{
|
||||
var result = await mediator.Send(new GetUsersQuery(pageNumber, pageSize, usernameSearch, currentUser.Id), cancellationToken);
|
||||
return result.ToHttpResult();
|
||||
}
|
||||
private static async Task<IResult> GetUserById(
|
||||
IMediator mediator,
|
||||
long id,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(new GetUserByIdQuery(id), cancellationToken);
|
||||
return result.ToHttpResult();
|
||||
}
|
||||
|
||||
private static async Task<IResult> GetUserById(
|
||||
IMediator mediator,
|
||||
long id,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(new GetUserByIdQuery(id), cancellationToken);
|
||||
return result.ToHttpResult();
|
||||
}
|
||||
private static async Task<IResult> GetListeningHistory(
|
||||
IMediator mediator,
|
||||
long id,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(new GetListeningHistoryQuery(id), cancellationToken);
|
||||
return Results.Ok(result);
|
||||
}
|
||||
|
||||
private static async Task<IEnumerable<TrackApplicationResponse>> GetListeningHistory(
|
||||
IMediator mediator,
|
||||
long id,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
return await mediator.Send(new GetListeningHistoryQuery(id), cancellationToken);
|
||||
}
|
||||
private static async Task<IResult> GetListeningStats(
|
||||
IMediator mediator,
|
||||
CurrentUser currentUser,
|
||||
long id,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
if (currentUser.RequiredId != id)
|
||||
return Results.Problem(statusCode: StatusCodes.Status403Forbidden);
|
||||
|
||||
private static async Task<ListeningStatsResponse> GetListeningStats(
|
||||
IMediator mediator,
|
||||
long id,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
return await mediator.Send(new GetListeningStatsQuery(id), cancellationToken);
|
||||
}
|
||||
var result = await mediator.Send(new GetListeningStatsQuery(id), cancellationToken);
|
||||
return Results.Ok(result);
|
||||
}
|
||||
|
||||
private static async Task<IResult> GetLastTrackListenedByUserId(
|
||||
IMediator mediator,
|
||||
long id,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(new GetLastTrackListenedByUserIdQuery(id), cancellationToken);
|
||||
return result.ToHttpResult();
|
||||
}
|
||||
private static async Task<IResult> GetLastTrackListenedByUserId(
|
||||
IMediator mediator,
|
||||
long id,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var track = await mediator.Send(new GetLastTrackListenedByUserIdQuery(id), cancellationToken);
|
||||
return track == null ? Results.NoContent() : Results.Ok(track);
|
||||
}
|
||||
|
||||
private static async Task<IResult> GetUserProfile(
|
||||
IMediator mediator,
|
||||
CurrentUser currentUser,
|
||||
long id,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(new GetUserProfileQuery(id, currentUser.Id), cancellationToken);
|
||||
return result.ToHttpResult();
|
||||
}
|
||||
private static async Task<IResult> GetUserProfile(
|
||||
IMediator mediator,
|
||||
CurrentUser currentUser,
|
||||
long id,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(new GetUserProfileQuery(id, currentUser.Id), cancellationToken);
|
||||
return result.ToHttpResult();
|
||||
}
|
||||
|
||||
private static async Task<IResult> GetFollowers(
|
||||
IMediator mediator,
|
||||
CurrentUser currentUser,
|
||||
long id,
|
||||
CancellationToken cancellationToken,
|
||||
int pageNumber = 1,
|
||||
int pageSize = 20)
|
||||
{
|
||||
var result = await mediator.Send(new GetUserFollowersQuery(currentUser.Id, id, pageNumber, pageSize), cancellationToken);
|
||||
return result.ToHttpResult();
|
||||
}
|
||||
private static async Task<IResult> GetFollowers(
|
||||
IMediator mediator,
|
||||
CurrentUser currentUser,
|
||||
long id,
|
||||
[AsParameters] PageQuery page,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(new GetUserFollowersQuery(currentUser.Id, id, page.PageNumber, page.PageSize), cancellationToken);
|
||||
return result.ToHttpResult();
|
||||
}
|
||||
|
||||
private static async Task<IResult> GetFollowing(
|
||||
IMediator mediator,
|
||||
CurrentUser currentUser,
|
||||
long id,
|
||||
CancellationToken cancellationToken,
|
||||
int pageNumber = 1,
|
||||
int pageSize = 20)
|
||||
{
|
||||
var result = await mediator.Send(new GetUserFollowingQuery(currentUser.Id, id, pageNumber, pageSize), cancellationToken);
|
||||
return result.ToHttpResult();
|
||||
}
|
||||
private static async Task<IResult> GetFollowing(
|
||||
IMediator mediator,
|
||||
CurrentUser currentUser,
|
||||
long id,
|
||||
[AsParameters] PageQuery page,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(new GetUserFollowingQuery(currentUser.Id, id, page.PageNumber, page.PageSize), cancellationToken);
|
||||
return result.ToHttpResult();
|
||||
}
|
||||
|
||||
private static async Task<bool> IsFollowing(
|
||||
IMediator mediator,
|
||||
CurrentUser currentUser,
|
||||
long id,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
return await mediator.Send(new IsFollowingUserQuery(currentUser.RequiredId, id), cancellationToken);
|
||||
}
|
||||
private static async Task<IResult> IsFollowing(
|
||||
IMediator mediator,
|
||||
CurrentUser currentUser,
|
||||
long id,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(new IsFollowingUserQuery(currentUser.RequiredId, id), cancellationToken);
|
||||
return Results.Ok(result);
|
||||
}
|
||||
|
||||
private static async Task<IResult> FollowUser(
|
||||
IMediator mediator,
|
||||
CurrentUser currentUser,
|
||||
long id,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(new FollowUserCommand(currentUser.RequiredId, id), cancellationToken);
|
||||
return result.ToHttpResult();
|
||||
}
|
||||
private static async Task<IResult> FollowUser(
|
||||
IMediator mediator,
|
||||
CurrentUser currentUser,
|
||||
long id,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(new FollowUserCommand(currentUser.RequiredId, id), cancellationToken);
|
||||
return result.ToHttpResult();
|
||||
}
|
||||
|
||||
private static async Task<IResult> UnfollowUser(
|
||||
IMediator mediator,
|
||||
CurrentUser currentUser,
|
||||
long id,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(new UnfollowUserCommand(currentUser.RequiredId, id), cancellationToken);
|
||||
return result.ToHttpResult();
|
||||
}
|
||||
|
||||
private static async Task<IResult> CreateUser(
|
||||
IMediator mediator,
|
||||
CreateUserRequest request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(
|
||||
new CreateUserCommand(request.Id, request.Name, request.FirstName, request.SecondName),
|
||||
cancellationToken);
|
||||
|
||||
return result.ToCreatedResult(user => $"/users/{user.Id}");
|
||||
private static async Task<IResult> UnfollowUser(
|
||||
IMediator mediator,
|
||||
CurrentUser currentUser,
|
||||
long id,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(new UnfollowUserCommand(currentUser.RequiredId, id), cancellationToken);
|
||||
return result.ToHttpResult();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user