-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
a924fc5
commit 4b0aa9f
Showing
7 changed files
with
319 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
namespace Linker.Core.V2.ApiModels; | ||
|
||
using Linker.Core.V2.Models; | ||
|
||
public sealed class CreatePlaylistRequest | ||
{ | ||
public string Name { get; set; } | ||
|
||
public string Description { get; set; } | ||
|
||
public Visibility Visibility { get; set; } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
namespace Linker.Core.V2.Repositories; | ||
|
||
using Linker.Core.V2.Models; | ||
using System.Collections.Generic; | ||
using System.Threading.Tasks; | ||
|
||
/// <summary> | ||
/// The abstraction for the <see cref="Playlist"/> repository. | ||
/// </summary> | ||
public interface IPlaylistRepository | ||
{ | ||
/// <summary> | ||
/// Gets all playlists for a certain user. | ||
/// </summary> | ||
/// <param name="userId">The user ID.</param> | ||
/// <param name="cancellationToken">The cancellation token.</param> | ||
/// <returns>The list of playlists.</returns> | ||
Task<IEnumerable<Playlist>> GetAllByUserAsync(string userId, CancellationToken cancellationToken); | ||
|
||
/// <summary> | ||
/// Gets the playlist by Id. | ||
/// </summary> | ||
/// <param name="id">The playlist Id.</param> | ||
/// <param name="cancellationToken">The cancellation token.</param> | ||
/// <returns>The found playlist.</returns> | ||
Task<Playlist> GetByIdAsync(string id, CancellationToken cancellationToken); | ||
|
||
/// <summary> | ||
/// Creates a new playlist. | ||
/// </summary> | ||
/// <param name="playlist">The playlist to be created.</param> | ||
/// <param name="cancellationToken">The cancellation token.</param> | ||
/// <returns>The task.</returns> | ||
Task AddAsync(Playlist playlist, CancellationToken cancellationToken); | ||
|
||
/// <summary> | ||
/// Updates an existing playlist. | ||
/// </summary> | ||
/// <param name="playlist">The playlist to be updated.</param> | ||
/// <param name="cancellationToken">The cancellation token.</param> | ||
/// <returns>The task.</returns> | ||
Task UpdateAsync(Playlist playlist, CancellationToken cancellationToken); | ||
|
||
/// <summary> | ||
/// Removes a playlist by Id. | ||
/// </summary> | ||
/// <param name="id">The id of the playlist to be removed.</param> | ||
/// <param name="cancellationToken">The cancellation token.</param> | ||
/// <returns>The task.</returns> | ||
Task RemoveAsync(string id, CancellationToken cancellationToken); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
namespace Linker.Data.SqlServer; | ||
|
||
using Dapper; | ||
using Linker.Common.Helpers; | ||
using Linker.Core.V2.Models; | ||
using Linker.Core.V2.Repositories; | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Data; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
|
||
/// <summary> | ||
/// The repository layer for <see cref="Playlist"/>. | ||
/// </summary> | ||
public sealed class PlaylistRepository : IPlaylistRepository | ||
{ | ||
private readonly IDbConnection connection; | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="PlaylistRepository"/> class. | ||
/// </summary> | ||
/// <param name="connection">The database connection.</param> | ||
public PlaylistRepository(IDbConnection connection) | ||
{ | ||
this.connection = Guard.ThrowIfNull(connection); | ||
} | ||
|
||
/// <inheritdoc/> | ||
public Task AddAsync(Playlist playlist, CancellationToken cancellationToken) | ||
{ | ||
cancellationToken.ThrowIfCancellationRequested(); | ||
|
||
var statement = @" | ||
INSERT INTO [dbo].[Playlists] | ||
( | ||
[Id], | ||
[OwnerId], | ||
[Name], | ||
[Description], | ||
[Visibility], | ||
[CreatedAt], | ||
[ModifiedAt] | ||
) | ||
VALUES | ||
( | ||
@Id, | ||
@OwnerId, | ||
@Name, | ||
@Description, | ||
@Visibility, | ||
@CreatedAt, | ||
@ModifiedAt | ||
); | ||
"; | ||
|
||
return this.connection.ExecuteAsync(statement, new | ||
{ | ||
playlist.Id, | ||
playlist.OwnerId, | ||
playlist.Name, | ||
playlist.Description, | ||
Visibility = playlist.Visibility.ToString(), | ||
CreatedAt = DateTime.UtcNow, | ||
ModifiedAt = DateTime.UtcNow, | ||
}); | ||
} | ||
|
||
/// <inheritdoc/> | ||
public Task<IEnumerable<Playlist>> GetAllByUserAsync(string userId, CancellationToken cancellationToken) | ||
{ | ||
cancellationToken.ThrowIfCancellationRequested(); | ||
|
||
var query = "SELECT * FROM [dbo].[Playlists] WHERE [OwnerId] = @UserId;"; | ||
|
||
return this.connection.QueryAsync<Playlist>(query, new { UserId = userId }); | ||
} | ||
|
||
/// <inheritdoc/> | ||
public Task<Playlist> GetByIdAsync(string id, CancellationToken cancellationToken) | ||
{ | ||
cancellationToken.ThrowIfCancellationRequested(); | ||
|
||
var query = "SELECT * FROM [dbo].[Playlists] WHERE [Id] = @Id;"; | ||
|
||
return this.connection.QueryFirstAsync<Playlist>(query, new { Id = id }); | ||
} | ||
|
||
/// <inheritdoc/> | ||
public Task RemoveAsync(string id, CancellationToken cancellationToken) | ||
{ | ||
cancellationToken.ThrowIfCancellationRequested(); | ||
|
||
var command = "DELETE FROM [dbo].[Playlists] WHERE [Id] = @Id;"; | ||
|
||
return this.connection.ExecuteAsync(command, new { Id = id }); | ||
} | ||
|
||
/// <inheritdoc/> | ||
public Task UpdateAsync(Playlist playlist, CancellationToken cancellationToken) | ||
{ | ||
cancellationToken.ThrowIfCancellationRequested(); | ||
|
||
var command = @" | ||
UPDATE [dbo].[Playlists] | ||
SET | ||
[Name] = @Name, | ||
[Description] = @Description, | ||
[Visibility] = @Visibility, | ||
[ModifiedAt] = @ModifiedAt | ||
WHERE [Id] = @Id; | ||
"; | ||
|
||
return this.connection.ExecuteAsync(command, new | ||
{ | ||
playlist.Id, | ||
playlist.Name, | ||
playlist.Description, | ||
Visibility = playlist.Visibility.ToString(), | ||
ModifiedAt = DateTime.UtcNow, | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,79 @@ | ||
namespace Linker.Mvc.Controllers; | ||
|
||
using Linker.Common.Helpers; | ||
using Linker.Core.V2.ApiModels; | ||
using Linker.Core.V2.Models; | ||
using Linker.Core.V2.Repositories; | ||
using Microsoft.AspNetCore.Mvc; | ||
using Serilog; | ||
using System.Security.Claims; | ||
|
||
public class PlaylistController : Controller | ||
public sealed class PlaylistController : Controller | ||
{ | ||
public IActionResult Index() | ||
private readonly IPlaylistRepository repository; | ||
private readonly ILogger logger; | ||
|
||
public PlaylistController(IPlaylistRepository repository, ILogger logger) | ||
{ | ||
this.repository = Guard.ThrowIfNull(repository); | ||
this.logger = Guard.ThrowIfNull(logger); | ||
} | ||
|
||
public string UserId => | ||
this.HttpContext.User?.FindFirst(ClaimTypes.NameIdentifier)?.Value ?? string.Empty; | ||
|
||
public async Task<IActionResult> Index() | ||
{ | ||
return View(); | ||
var playlists = await this.repository.GetAllByUserAsync(this.UserId, default); | ||
|
||
return this.View(playlists); | ||
} | ||
|
||
// GET: PlaylistController/Create | ||
public IActionResult Create() | ||
{ | ||
return this.View(); | ||
} | ||
|
||
// POST: PlaylistController/Create | ||
[HttpPost] | ||
[ValidateAntiForgeryToken] | ||
public async Task<IActionResult> Create(CreatePlaylistRequest request) | ||
{ | ||
ArgumentNullException.ThrowIfNull(request); | ||
|
||
var playlist = new Playlist | ||
{ | ||
Id = Guid.NewGuid().ToString(), | ||
OwnerId = this.UserId, | ||
Name = request.Name, | ||
Description = request.Description, | ||
Visibility = request.Visibility, | ||
}; | ||
|
||
try | ||
{ | ||
if (this.ModelState.IsValid) | ||
{ | ||
await this.repository | ||
.AddAsync(playlist, this.HttpContext.RequestAborted) | ||
.ConfigureAwait(false); | ||
|
||
this.TempData[Constants.Success] = "Playlist created successfully"; | ||
|
||
return this.RedirectToAction(nameof(this.Index)); | ||
} | ||
|
||
this.logger.Warning("The model is invalid. {@model}.", this.ModelState); | ||
|
||
return this.View(request); | ||
} | ||
catch (Exception ex) | ||
{ | ||
this.TempData[Constants.Error] = "Something failed: " + ex.Message; | ||
this.logger.Error(ex, "Exception occurred."); | ||
|
||
return this.View(request); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
@using Linker.Core.V2.Models | ||
@model Linker.Core.V2.ApiModels.CreatePlaylistRequest | ||
@{ | ||
ViewData["Title"] = "Create playlist"; | ||
} | ||
|
||
<form asp-action="Create" class="max-w-screen-md"> | ||
<div class="form-group"> | ||
<label asp-for="Name" class="control-label"></label> | ||
<input asp-for="Name" class="form-control" /> | ||
<span asp-validation-for="Name" class="text-danger"></span> | ||
</div> | ||
|
||
<div class="form-group"> | ||
<label asp-for="Description" class="control-label"></label> | ||
<input asp-for="Description" class="form-control" /> | ||
<span asp-validation-for="Description" class="text-danger"></span> | ||
</div> | ||
|
||
<div class="form-group"> | ||
<label asp-for="Visibility" class="control-label"></label> | ||
<select asp-for="Visibility" asp-items="Html.GetEnumSelectList<Visibility>()"> | ||
<option selected="selected" value="">Please select</option> | ||
</select> | ||
<span asp-validation-for="Visibility" class="text-danger"></span> | ||
</div> | ||
|
||
<div class="my-3 flex"> | ||
<button class="px-4 py-2 bg-black text-white rounded block"> | ||
Create | ||
</button> | ||
</div> | ||
</form> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,31 @@ | ||
@* | ||
For more information on enabling MVC for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860 | ||
*@ | ||
@using Linker.Core.V2.Models | ||
@model IEnumerable<Playlist> | ||
@{ | ||
ViewData["Title"] = "Playlist"; | ||
var playlistCounts = Model.Count(); | ||
} | ||
|
||
<partial name="_Notification" /> | ||
|
||
<a asp-action="Create">+</a> | ||
|
||
@if (Model.Any()) | ||
{ | ||
@foreach (var playlist in Model) | ||
{ | ||
<div> | ||
<p>@playlist.Id</p> | ||
<p>@playlist.Name</p> | ||
<p>@playlist.Description</p> | ||
<p>@playlist.Visibility.ToString()</p> | ||
<p>@playlist.CreatedAt.ToLongTimeString()</p> | ||
<p>@playlist.ModifiedAt.ToLongTimeString()</p> | ||
</div> | ||
} | ||
} | ||
else | ||
{ | ||
<p>No playlist for now.</p> | ||
<button>Create a new playlist.</button> | ||
} | ||
|
||
<p>Stub Playlist Page</p> |