-
Notifications
You must be signed in to change notification settings - Fork 3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Admin notes/tournament endpoints #433
Open
myssto
wants to merge
8
commits into
master
Choose a base branch
from
admin-notes/tournament-endpoints
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+358
−3
Open
Changes from 7 commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
8169cbf
API add service layer for admin note CRUD actions
myssto f744a22
typeparamref :deepstonecrypt:
myssto cf398fa
API register admin note repository and service
myssto 39c49f8
API some service changes to accomodate endpoints
myssto d9fc50e
API CRUD endpoints for tournament admin notes
myssto c716030
API add missing automapper definition
myssto bb585cb
API change endpoint method
myssto 81a8af9
Update unusually short docstring
hburn7 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
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,162 @@ | ||
using API.DTOs; | ||
using API.Utilities; | ||
using API.Utilities.Extensions; | ||
using Database.Entities; | ||
using Microsoft.AspNetCore.Authorization; | ||
using Microsoft.AspNetCore.Mvc; | ||
|
||
namespace API.Controllers; | ||
|
||
public partial class TournamentsController | ||
{ | ||
/// <summary> | ||
/// Creates an admin note for a tournament | ||
/// </summary> | ||
/// <param name="id">Tournament id</param> | ||
/// <response code="401">If the requester is not properly authorized</response> | ||
/// <response code="404">If a tournament matching the given id does not exist</response> | ||
/// <response code="400">If the authorized user does not exist</response> | ||
/// <response code="200">Returns the created admin note</response> | ||
[HttpPost("{id:int}/notes")] | ||
[Authorize(Roles = $"{OtrClaims.Admin}")] | ||
[ProducesResponseType(StatusCodes.Status401Unauthorized)] | ||
[ProducesResponseType(StatusCodes.Status404NotFound)] | ||
[ProducesResponseType(StatusCodes.Status400BadRequest)] | ||
[ProducesResponseType<AdminNoteDTO>(StatusCodes.Status200OK)] | ||
public async Task<IActionResult> CreateAdminNoteAsync(int id, [FromBody] string note) | ||
{ | ||
var adminUserId = User.AuthorizedIdentity(); | ||
if (!adminUserId.HasValue) | ||
{ | ||
return Unauthorized(); | ||
} | ||
|
||
if (!await tournamentsService.ExistsAsync(id)) | ||
{ | ||
return NotFound(); | ||
} | ||
|
||
AdminNoteDTO? result = await adminNoteService.CreateAsync<TournamentAdminNote>(id, adminUserId.Value, note); | ||
return result is not null | ||
? Ok(result) | ||
: BadRequest(); | ||
} | ||
|
||
/// <summary> | ||
/// List all admin notes from a tournament | ||
/// </summary> | ||
/// <param name="id">Tournament id</param> | ||
/// <response code="404">If a tournament matching the given id does not exist</response> | ||
/// <response code="200">Returns all admin notes from a tournament</response> | ||
[HttpGet("{id:int}/notes")] | ||
[Authorize(Roles = $"{OtrClaims.Admin}")] | ||
[ProducesResponseType(StatusCodes.Status404NotFound)] | ||
[ProducesResponseType<IEnumerable<AdminNoteDTO>>(StatusCodes.Status200OK)] | ||
public async Task<IActionResult> ListAdminNotesAsync(int id) | ||
{ | ||
if (!await tournamentsService.ExistsAsync(id)) | ||
{ | ||
return NotFound(); | ||
} | ||
|
||
return Ok(await adminNoteService.GetAsync<TournamentAdminNote>(id)); | ||
} | ||
|
||
/// <summary> | ||
/// Updates an admin note for a tournament | ||
/// </summary> | ||
/// <param name="id">Tournament id</param> | ||
/// <param name="noteId">Admin note id</param> | ||
/// <response code="401">If the requester is not properly authorized</response> | ||
/// <response code="404"> | ||
/// If a tournament matching the given id does not exist. | ||
/// If an admin note matching the given noteId does not exist | ||
/// </response> | ||
/// <response code="403">If the requester did not create the admin note</response> | ||
/// <response code="200">Returns the updated admin note</response> | ||
[HttpPatch("{id:int}/notes/{noteId:int}")] | ||
[Authorize(Roles = $"{OtrClaims.Admin}")] | ||
[ProducesResponseType(StatusCodes.Status401Unauthorized)] | ||
[ProducesResponseType(StatusCodes.Status404NotFound)] | ||
[ProducesResponseType(StatusCodes.Status403Forbidden)] | ||
[ProducesResponseType<AdminNoteDTO>(StatusCodes.Status200OK)] | ||
public async Task<IActionResult> UpdateAdminNoteAsync(int id, int noteId, [FromBody] string note) | ||
{ | ||
var adminUserId = User.AuthorizedIdentity(); | ||
if (!adminUserId.HasValue) | ||
{ | ||
return Unauthorized(); | ||
} | ||
|
||
if (!await tournamentsService.ExistsAsync(id)) | ||
{ | ||
return NotFound(); | ||
} | ||
|
||
AdminNoteDTO? existingNote = await adminNoteService.GetAsync<TournamentAdminNote>(noteId); | ||
if (existingNote is null) | ||
{ | ||
return NotFound(); | ||
} | ||
|
||
if (adminUserId != existingNote.AdminUserId) | ||
{ | ||
return Forbid(); | ||
} | ||
|
||
existingNote.Note = note; | ||
AdminNoteDTO? result = await adminNoteService.UpdateAsync<TournamentAdminNote>(existingNote); | ||
|
||
return result is not null | ||
? Ok(result) | ||
: NotFound(); | ||
} | ||
|
||
/// <summary> | ||
/// Deletes an admin note for a tournament | ||
/// </summary> | ||
/// <param name="id">Tournament id</param> | ||
/// <param name="noteId">Admin note id</param> | ||
/// <response code="401">If the requester is not properly authorized</response> | ||
/// <response code="404"> | ||
/// If a tournament matching the given id does not exist. | ||
/// If an admin note matching the given noteId does not exist | ||
/// </response> | ||
/// <response code="403">If the requester did not create the admin note</response> | ||
/// <response code="200">Returns the updated admin note</response> | ||
[HttpDelete("{id:int}/notes/{noteId:int}")] | ||
[Authorize(Roles = $"{OtrClaims.Admin}")] | ||
[ProducesResponseType(StatusCodes.Status401Unauthorized)] | ||
[ProducesResponseType(StatusCodes.Status404NotFound)] | ||
[ProducesResponseType(StatusCodes.Status403Forbidden)] | ||
[ProducesResponseType<AdminNoteDTO>(StatusCodes.Status200OK)] | ||
public async Task<IActionResult> DeleteAdminNoteAsync(int id, int noteId) | ||
{ | ||
var adminUserId = User.AuthorizedIdentity(); | ||
if (!adminUserId.HasValue) | ||
{ | ||
return Unauthorized(); | ||
} | ||
|
||
if (!await tournamentsService.ExistsAsync(id)) | ||
{ | ||
return NotFound(); | ||
} | ||
|
||
AdminNoteDTO? existingNote = await adminNoteService.GetAsync<TournamentAdminNote>(noteId); | ||
if (existingNote is null) | ||
{ | ||
return NotFound(); | ||
} | ||
|
||
if (adminUserId != existingNote.AdminUserId) | ||
{ | ||
return Forbid(); | ||
} | ||
|
||
var result = await adminNoteService.DeleteAsync<TournamentAdminNote>(noteId); | ||
return result | ||
? Ok() | ||
: NotFound(); | ||
} | ||
} |
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,42 @@ | ||
namespace API.DTOs; | ||
|
||
/// <summary> | ||
/// Represents a note for an entity created by an admin | ||
/// </summary> | ||
public class AdminNoteDTO | ||
{ | ||
/// <summary> | ||
/// Id | ||
hburn7 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/// </summary> | ||
public int Id { get; init; } | ||
|
||
/// <summary> | ||
/// Timestamp of creation | ||
/// </summary> | ||
public DateTime Created { get; init; } | ||
|
||
/// <summary> | ||
/// Timestamp of the last update if available | ||
/// </summary> | ||
public DateTime? Updated { get; init; } | ||
|
||
/// <summary> | ||
/// Id of the parent entity | ||
/// </summary> | ||
public int ReferenceId { get; init; } | ||
|
||
/// <summary> | ||
/// Id of the admin user that created the note | ||
/// </summary> | ||
public int AdminUserId { get; set; } | ||
|
||
/// <summary> | ||
/// Username of the admin user that created the note | ||
/// </summary> | ||
public string? AdminUsername { get; init; } | ||
|
||
/// <summary> | ||
/// Content of the note | ||
/// </summary> | ||
public string Note { get; set; } = string.Empty; | ||
} |
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,67 @@ | ||
using API.DTOs; | ||
using API.Services.Interfaces; | ||
using AutoMapper; | ||
using Database.Entities; | ||
using Database.Repositories.Interfaces; | ||
|
||
namespace API.Services.Implementations; | ||
|
||
public class AdminNoteService( | ||
IAdminNoteRepository adminNoteRepository, | ||
IUserRepository userRepository, | ||
IMapper mapper | ||
) : IAdminNoteService | ||
{ | ||
public async Task<AdminNoteDTO?> CreateAsync<TAdminNote>(int referenceId, int adminUserId, string note) | ||
where TAdminNote : AdminNoteEntityBase, new() | ||
{ | ||
if (!await userRepository.ExistsAsync(adminUserId)) | ||
{ | ||
return null; | ||
} | ||
|
||
var entity = new TAdminNote | ||
{ | ||
ReferenceId = referenceId, | ||
AdminUserId = adminUserId, | ||
Note = note | ||
}; | ||
|
||
await adminNoteRepository.CreateAsync(entity); | ||
return mapper.Map<AdminNoteDTO>(entity); | ||
} | ||
|
||
public async Task<AdminNoteDTO?> GetAsync<TAdminNote>(int id) where TAdminNote : AdminNoteEntityBase => | ||
mapper.Map<AdminNoteDTO>(await adminNoteRepository.GetAsync<TAdminNote>(id)); | ||
|
||
public async Task<IEnumerable<AdminNoteDTO>> ListAsync<TAdminNote>(int referenceId) where TAdminNote : AdminNoteEntityBase => | ||
mapper.Map<IEnumerable<AdminNoteDTO>>(await adminNoteRepository.ListAsync<TAdminNote>(referenceId)); | ||
|
||
public async Task<AdminNoteDTO?> UpdateAsync<TAdminNote>(AdminNoteDTO updatedNote) where TAdminNote : AdminNoteEntityBase | ||
{ | ||
TAdminNote? adminNote = await adminNoteRepository.GetAsync<TAdminNote>(updatedNote.Id); | ||
|
||
if (adminNote is null) | ||
{ | ||
return null; | ||
} | ||
|
||
adminNote.Note = updatedNote.Note; | ||
await adminNoteRepository.UpdateAsync(adminNote); | ||
|
||
return mapper.Map<AdminNoteDTO>(adminNote); | ||
} | ||
|
||
public async Task<bool> DeleteAsync<TAdminNote>(int id) where TAdminNote : AdminNoteEntityBase | ||
{ | ||
TAdminNote? adminNote = await adminNoteRepository.GetAsync<TAdminNote>(id); | ||
|
||
if (adminNote is null) | ||
{ | ||
return false; | ||
} | ||
|
||
await adminNoteRepository.DeleteAsync(adminNote); | ||
return true; | ||
} | ||
} |
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,59 @@ | ||
using API.DTOs; | ||
using Database.Entities; | ||
|
||
namespace API.Services.Interfaces; | ||
|
||
public interface IAdminNoteService | ||
{ | ||
/// <summary> | ||
/// Creates a <typeparamref name="TAdminNote"/> | ||
/// </summary> | ||
/// <param name="referenceId">Id of the parent entity</param> | ||
/// <param name="adminUserId">Id of the admin <see cref="User"/> creating the <typeparamref name="TAdminNote"/></param> | ||
/// <param name="note">Content of the <typeparamref name="TAdminNote"/></param> | ||
/// <typeparam name="TAdminNote">The type of admin note being created</typeparam> | ||
/// <returns> | ||
/// The created <see cref="AdminNoteDTO"/> if successful, or null if a <see cref="User"/> for the | ||
/// given <paramref name="adminUserId"/> does not exist | ||
/// </returns> | ||
/// <remarks> | ||
/// This method checks for existence of the admin <see cref="User"/>, but checking for existence of the parent | ||
/// entity should be handled by the caller | ||
/// </remarks> | ||
Task<AdminNoteDTO?> CreateAsync<TAdminNote>(int referenceId, int adminUserId, string note) | ||
where TAdminNote : AdminNoteEntityBase, new(); | ||
|
||
/// <summary> | ||
/// Gets an <see cref="AdminNoteDTO"/> | ||
/// </summary> | ||
/// <param name="id">Id of the <typeparamref name="TAdminNote"/></param> | ||
/// <typeparam name="TAdminNote">The type of admin note being retrieved</typeparam> | ||
/// <returns>The <see cref="AdminNoteDTO"/>, or null if not found</returns> | ||
Task<AdminNoteDTO?> GetAsync<TAdminNote>(int id) where TAdminNote : AdminNoteEntityBase; | ||
|
||
/// <summary> | ||
/// Gets a collection of <see cref="AdminNoteDTO"/>s by their parent reference Id. | ||
/// </summary> | ||
/// <param name="referenceId">Id of the parent entity</param> | ||
/// <typeparam name="TAdminNote">The type of admin note being retrieved</typeparam> | ||
/// <returns>A collection of <see cref="AdminNoteDTO"/>s for the given referenceId</returns> | ||
Task<IEnumerable<AdminNoteDTO>> ListAsync<TAdminNote>(int referenceId) where TAdminNote : AdminNoteEntityBase; | ||
|
||
/// <summary> | ||
/// Updates the <see cref="AdminNoteEntityBase.Note"/> of a <typeparamref name="TAdminNote"/> | ||
/// </summary> | ||
/// <param name="updatedNote">The updated admin note</param> | ||
/// <typeparam name="TAdminNote">The type of admin note being updated</typeparam> | ||
/// <returns>The updated <see cref="AdminNoteDTO"/>, or null if not found</returns> | ||
Task<AdminNoteDTO?> UpdateAsync<TAdminNote>(AdminNoteDTO updatedNote) where TAdminNote : AdminNoteEntityBase; | ||
|
||
/// <summary> | ||
/// Deletes a <typeparamref name="TAdminNote"/> | ||
/// </summary> | ||
/// <param name="id">Id of the <typeparamref name="TAdminNote"/></param> | ||
/// <typeparam name="TAdminNote">The type of admin note being deleted</typeparam> | ||
/// <returns> | ||
/// True if successful, false if a <see cref="AdminNoteDTO"/> for the given id does not exist | ||
/// </returns> | ||
Task<bool> DeleteAsync<TAdminNote>(int id) where TAdminNote : AdminNoteEntityBase; | ||
} |
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
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In testing, I hard-coded my user id (20). The
adminUsername
field returned an empty string. From what I can see in the current code, there isn't a way for theCreateAsync()
method to link back to a user.