From 86863dc69a97f6115f8df679afe17e663bfd3367 Mon Sep 17 00:00:00 2001 From: Stian Forren Date: Thu, 18 Sep 2025 14:05:14 +0200 Subject: [PATCH 01/10] Added findById for posts --- exercise.wwwapi/Endpoints/PostEndpoints.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/exercise.wwwapi/Endpoints/PostEndpoints.cs b/exercise.wwwapi/Endpoints/PostEndpoints.cs index 241e4b8..4531460 100644 --- a/exercise.wwwapi/Endpoints/PostEndpoints.cs +++ b/exercise.wwwapi/Endpoints/PostEndpoints.cs @@ -8,6 +8,7 @@ using FluentValidation; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; using System.Security.Claims; using Post = exercise.wwwapi.Models.Post; @@ -21,6 +22,7 @@ public static void ConfigurePostEndpoints(this WebApplication app) posts.MapPost("/", CreatePost).WithSummary("Create post"); posts.MapGet("/", GetAllPosts).WithSummary("Get all posts"); posts.MapGet("/v2", GetAllPostsVol2).WithSummary("Get all posts with the required info"); + posts.MapGet("v2/{id}", GetPostById).WithSummary("Get post by id"); posts.MapPatch("/{id}", UpdatePost).RequireAuthorization().WithSummary("Update a post"); posts.MapDelete("/{id}", DeletePost).RequireAuthorization().WithSummary("Delete a post"); } @@ -132,6 +134,20 @@ private static async Task GetAllPostsVol2(IRepository postReposit return TypedResults.Ok(response); } + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + private static async Task GetPostById(IRepository postRepository, int id, ClaimsPrincipal user) + { + var response = await postRepository.GetByIdWithIncludes(p => p.Include(a => a.Author) + .Include(c => c.Comments) + .Include(l => l.Likes), id); + var result = new ResponseDTO() + { + Status = "success", + Data = new PostDTOVol2(response) + }; + return TypedResults.Ok(result); + } [Authorize] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] From 58f2c7d941fb018e8481384457085bfcbc8819e7 Mon Sep 17 00:00:00 2001 From: Stian Forren Date: Thu, 18 Sep 2025 14:26:58 +0200 Subject: [PATCH 02/10] Updated PostDTO to version 2. Made PsotEndpoint utilize new getterfunctions from Repository --- .../DTOs/GetObjects/PostsSuccessDTOVol2.cs | 2 +- .../DTOs/Posts/CreatePostSuccessDTO.cs | 3 +- .../GetPosts/{PostDTOVol2.cs => PostDTO.cs} | 6 +- .../DTOs/Posts/{PostDTO.cs => PostDTO_old.cs} | 2 +- exercise.wwwapi/Endpoints/PostEndpoints.cs | 61 +++++-------------- 5 files changed, 21 insertions(+), 53 deletions(-) rename exercise.wwwapi/DTOs/Posts/GetPosts/{PostDTOVol2.cs => PostDTO.cs} (91%) rename exercise.wwwapi/DTOs/Posts/{PostDTO.cs => PostDTO_old.cs} (93%) diff --git a/exercise.wwwapi/DTOs/GetObjects/PostsSuccessDTOVol2.cs b/exercise.wwwapi/DTOs/GetObjects/PostsSuccessDTOVol2.cs index 4944b5b..20b76eb 100644 --- a/exercise.wwwapi/DTOs/GetObjects/PostsSuccessDTOVol2.cs +++ b/exercise.wwwapi/DTOs/GetObjects/PostsSuccessDTOVol2.cs @@ -7,7 +7,7 @@ namespace exercise.wwwapi.DTOs.GetObjects public class PostsSuccessDTOVol2 { [JsonPropertyName("posts")] - public List Posts { get; set; } = []; + public List Posts { get; set; } = []; } } diff --git a/exercise.wwwapi/DTOs/Posts/CreatePostSuccessDTO.cs b/exercise.wwwapi/DTOs/Posts/CreatePostSuccessDTO.cs index 26d80e4..72003e2 100644 --- a/exercise.wwwapi/DTOs/Posts/CreatePostSuccessDTO.cs +++ b/exercise.wwwapi/DTOs/Posts/CreatePostSuccessDTO.cs @@ -1,4 +1,5 @@ -using System.ComponentModel.DataAnnotations.Schema; +using exercise.wwwapi.DTOs.Posts.GetPosts; +using System.ComponentModel.DataAnnotations.Schema; namespace exercise.wwwapi.DTOs.Posts { diff --git a/exercise.wwwapi/DTOs/Posts/GetPosts/PostDTOVol2.cs b/exercise.wwwapi/DTOs/Posts/GetPosts/PostDTO.cs similarity index 91% rename from exercise.wwwapi/DTOs/Posts/GetPosts/PostDTOVol2.cs rename to exercise.wwwapi/DTOs/Posts/GetPosts/PostDTO.cs index 64d6918..ad8a09d 100644 --- a/exercise.wwwapi/DTOs/Posts/GetPosts/PostDTOVol2.cs +++ b/exercise.wwwapi/DTOs/Posts/GetPosts/PostDTO.cs @@ -3,7 +3,7 @@ namespace exercise.wwwapi.DTOs.Posts.GetPosts { - public class PostDTOVol2 + public class PostDTO { public int Id { get; set; } public int AuthorId { get; set; } @@ -14,11 +14,11 @@ public class PostDTOVol2 public List Comments { get; set; } = new List(); public List Likes { get; set; } = new List(); - public PostDTOVol2() + public PostDTO() { } - public PostDTOVol2(Post model) + public PostDTO(Post model) { Id = model.Id; AuthorId = model.AuthorId; diff --git a/exercise.wwwapi/DTOs/Posts/PostDTO.cs b/exercise.wwwapi/DTOs/Posts/PostDTO_old.cs similarity index 93% rename from exercise.wwwapi/DTOs/Posts/PostDTO.cs rename to exercise.wwwapi/DTOs/Posts/PostDTO_old.cs index 0b3aaa3..8f08342 100644 --- a/exercise.wwwapi/DTOs/Posts/PostDTO.cs +++ b/exercise.wwwapi/DTOs/Posts/PostDTO_old.cs @@ -2,7 +2,7 @@ namespace exercise.wwwapi.DTOs.Posts { - public class PostDTO + public class PostDTO_old { [JsonPropertyName("id")] public int Id { get; set; } diff --git a/exercise.wwwapi/Endpoints/PostEndpoints.cs b/exercise.wwwapi/Endpoints/PostEndpoints.cs index 4531460..8cfaeea 100644 --- a/exercise.wwwapi/Endpoints/PostEndpoints.cs +++ b/exercise.wwwapi/Endpoints/PostEndpoints.cs @@ -20,9 +20,8 @@ public static void ConfigurePostEndpoints(this WebApplication app) { var posts = app.MapGroup("posts"); posts.MapPost("/", CreatePost).WithSummary("Create post"); - posts.MapGet("/", GetAllPosts).WithSummary("Get all posts"); - posts.MapGet("/v2", GetAllPostsVol2).WithSummary("Get all posts with the required info"); - posts.MapGet("v2/{id}", GetPostById).WithSummary("Get post by id"); + posts.MapGet("/", GetAllPosts).WithSummary("Get all posts with the required info"); + posts.MapGet("/{id}", GetPostById).WithSummary("Get post by id"); posts.MapPatch("/{id}", UpdatePost).RequireAuthorization().WithSummary("Update a post"); posts.MapDelete("/{id}", DeletePost).RequireAuthorization().WithSummary("Delete a post"); } @@ -83,46 +82,18 @@ public static async Task CreatePost( return Results.Created($"/posts/{post.Id}", response); } - [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] private static async Task GetAllPosts(IRepository postRepository, - ClaimsPrincipal user) - { - var results = (await postRepository.GetAllAsync( - p => p.Author, - p => p.Comments - )).ToList(); - - var postData = new PostsSuccessDTO - { - Posts = results - }; - - var response = new ResponseDTO - { - Status = "success", - Data = postData - }; - - return TypedResults.Ok(response); - } - - [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status401Unauthorized)] - - private static async Task GetAllPostsVol2(IRepository postRepository, ClaimsPrincipal user) { - var results = (await postRepository.GetAllAsync( - p => p.Author, - p => p.Comments, - p => p.Likes - )).ToList(); + var results = await postRepository.GetWithIncludes(q => q.Include(p => p.Author) + .Include(c => c.Comments) + .Include(l => l.Likes)); var postData = new PostsSuccessDTOVol2 { - Posts = results.Select(r => new PostDTOVol2(r)).ToList() + Posts = results.Select(r => new PostDTO(r)).ToList() }; var response = new ResponseDTO @@ -141,10 +112,10 @@ private static async Task GetPostById(IRepository postRepository, var response = await postRepository.GetByIdWithIncludes(p => p.Include(a => a.Author) .Include(c => c.Comments) .Include(l => l.Likes), id); - var result = new ResponseDTO() + var result = new ResponseDTO() { Status = "success", - Data = new PostDTOVol2(response) + Data = new PostDTO(response) }; return TypedResults.Ok(result); } @@ -162,11 +133,9 @@ public static async Task UpdatePost(IRepository postRepository, i return Results.Unauthorized(); } - var post = await postRepository.GetByIdAsync( - id, - p => p.Author, - p => p.Comments - ); + var post = await postRepository.GetByIdWithIncludes(p => p.Include(a => a.Author) + .Include(c => c.Comments) + .Include(l => l.Likes), id); if (post == null) { @@ -230,11 +199,9 @@ public static async Task DeletePost(IRepository postRepository, i return Results.Unauthorized(); } - var post = await postRepository.GetByIdAsync( - id, - p => p.Author, - p => p.Comments - ); + var post = await postRepository.GetByIdWithIncludes(p => p.Include(a => a.Author) + .Include(c => c.Comments) + .Include(l => l.Likes), id); if (post == null) { From 3e77f52dc9cd01ae06b718d3b9c0375573b6e44b Mon Sep 17 00:00:00 2001 From: Stian Forren Date: Fri, 19 Sep 2025 09:17:45 +0200 Subject: [PATCH 03/10] updated the Comment endpoint --- exercise.wwwapi/Endpoints/CommentEndpoints.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/exercise.wwwapi/Endpoints/CommentEndpoints.cs b/exercise.wwwapi/Endpoints/CommentEndpoints.cs index 5a6b93a..3c0e7b3 100644 --- a/exercise.wwwapi/Endpoints/CommentEndpoints.cs +++ b/exercise.wwwapi/Endpoints/CommentEndpoints.cs @@ -7,6 +7,7 @@ using FluentValidation; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; using System.Security.Claims; using Post = exercise.wwwapi.Models.Post; @@ -23,17 +24,17 @@ public static async Task ConfigureCommentEndpoints(this WebApplication app) app.MapGet("/posts/{postId}/comments", GetCommentsPerPost).WithSummary("Get all comments for a post"); app.MapPost("/posts/{postId}/comments", CreateComment).RequireAuthorization().WithSummary("Create a comment"); } - [ProducesResponseType(StatusCodes.Status200OK)] private static async Task GetCommentsPerPost(IRepository commentRepository, ClaimsPrincipal comment, int postId) { - var results = await commentRepository.GetAllAsync(c => c.Post); - var filtered = results.Where(c => c.PostId == postId).ToList(); + //var results = await commentRepository.GetAllAsync(c => c.Post); + //var filtered = results.Where(c => c.PostId == postId).ToList(); + var commentsForPost = await commentRepository.GetWithIncludes(c => c.Where(c => c.PostId == postId)); var commentData = new CommentsSuccessDTO { - Comments = filtered.Select(c => new CommentDTO + Comments = commentsForPost.Select(c => new CommentDTO { Id = c.Id, PostId = postId, From 5bf99b3cea0981e6d8db936b2f71c8c9c2426703 Mon Sep 17 00:00:00 2001 From: Stian Forren Date: Fri, 19 Sep 2025 09:18:30 +0200 Subject: [PATCH 04/10] removed unused lines --- exercise.wwwapi/Endpoints/CommentEndpoints.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/exercise.wwwapi/Endpoints/CommentEndpoints.cs b/exercise.wwwapi/Endpoints/CommentEndpoints.cs index 3c0e7b3..f6c24a9 100644 --- a/exercise.wwwapi/Endpoints/CommentEndpoints.cs +++ b/exercise.wwwapi/Endpoints/CommentEndpoints.cs @@ -28,8 +28,6 @@ public static async Task ConfigureCommentEndpoints(this WebApplication app) private static async Task GetCommentsPerPost(IRepository commentRepository, ClaimsPrincipal comment, int postId) { - //var results = await commentRepository.GetAllAsync(c => c.Post); - //var filtered = results.Where(c => c.PostId == postId).ToList(); var commentsForPost = await commentRepository.GetWithIncludes(c => c.Where(c => c.PostId == postId)); var commentData = new CommentsSuccessDTO From 34c096d97f9dba371b9ed516eeb5b67eff6736d5 Mon Sep 17 00:00:00 2001 From: Stian Forren Date: Fri, 19 Sep 2025 09:36:16 +0200 Subject: [PATCH 05/10] updated rule for comments. Are now able to create a comment that is 1-1000 character long --- .../Validators/CommentValidators/CreateCommentsValidator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercise.wwwapi/Validators/CommentValidators/CreateCommentsValidator.cs b/exercise.wwwapi/Validators/CommentValidators/CreateCommentsValidator.cs index edddc1c..78e4475 100644 --- a/exercise.wwwapi/Validators/CommentValidators/CreateCommentsValidator.cs +++ b/exercise.wwwapi/Validators/CommentValidators/CreateCommentsValidator.cs @@ -12,7 +12,7 @@ public CreateCommentsValidator() RuleFor(x => x.Body) .NotEmpty().WithMessage("Comment body cannot be empty.") .MaximumLength(1000).WithMessage("Comment body cannot exceed 1000 characters.") - .MinimumLength(10).WithMessage("Comment body must be at least 10 characters long."); + .MinimumLength(1).WithMessage("Comment body must be at least 10 characters long."); } } } From e2e491b0b9411e6e9861efcf76c4dcb9ce53224a Mon Sep 17 00:00:00 2001 From: Stian Forren Date: Fri, 19 Sep 2025 09:37:36 +0200 Subject: [PATCH 06/10] updated rules for post- create and update. and comment -update. can now be 1-1000 characters --- .../Validators/CommentValidators/UpdateCommentsValidator.cs | 2 +- .../Validators/PostValidators/CreatePostValidator.cs | 2 +- .../Validators/PostValidators/UpdatePostValidator.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/exercise.wwwapi/Validators/CommentValidators/UpdateCommentsValidator.cs b/exercise.wwwapi/Validators/CommentValidators/UpdateCommentsValidator.cs index 357e33e..9566767 100644 --- a/exercise.wwwapi/Validators/CommentValidators/UpdateCommentsValidator.cs +++ b/exercise.wwwapi/Validators/CommentValidators/UpdateCommentsValidator.cs @@ -12,7 +12,7 @@ public UpdateCommentsValidator() .NotEmpty().When(x => x.Body != null) .WithMessage("Comment body cannot be empty if provided.") .MaximumLength(1000).WithMessage("Comment body cannot exceed 1000 characters.") - .MinimumLength(10).When(x => !string.IsNullOrWhiteSpace(x.Body)) + .MinimumLength(1).When(x => !string.IsNullOrWhiteSpace(x.Body)) .WithMessage("Comment body must be at least 10 characters long."); } } diff --git a/exercise.wwwapi/Validators/PostValidators/CreatePostValidator.cs b/exercise.wwwapi/Validators/PostValidators/CreatePostValidator.cs index 333775c..85c6ea5 100644 --- a/exercise.wwwapi/Validators/PostValidators/CreatePostValidator.cs +++ b/exercise.wwwapi/Validators/PostValidators/CreatePostValidator.cs @@ -14,7 +14,7 @@ public CreatePostValidator() RuleFor(x => x.Body) .NotEmpty().WithMessage("Post body cannot be empty.") .MaximumLength(1000).WithMessage("Post body cannot exceed 1000 characters.") - .MinimumLength(10).WithMessage("Post body must be at least 10 characters long."); + .MinimumLength(1).WithMessage("Post body must be at least 10 characters long."); } } } diff --git a/exercise.wwwapi/Validators/PostValidators/UpdatePostValidator.cs b/exercise.wwwapi/Validators/PostValidators/UpdatePostValidator.cs index f409653..ca208a7 100644 --- a/exercise.wwwapi/Validators/PostValidators/UpdatePostValidator.cs +++ b/exercise.wwwapi/Validators/PostValidators/UpdatePostValidator.cs @@ -11,7 +11,7 @@ public UpdatePostValidator() .NotEmpty().When(x => x.Body != null) .WithMessage("Post body cannot be empty if provided.") .MaximumLength(1000).WithMessage("Post body cannot exceed 1000 characters.") - .MinimumLength(10).When(x => !string.IsNullOrWhiteSpace(x.Body)) + .MinimumLength(1).When(x => !string.IsNullOrWhiteSpace(x.Body)) .WithMessage("Post body must be at least 10 characters long."); } } From cc4f7de668ac6b2b9fe6a1fa4bfb45ca93936efb Mon Sep 17 00:00:00 2001 From: Stian Forren Date: Fri, 19 Sep 2025 12:22:51 +0200 Subject: [PATCH 07/10] made likeEndpoint --> users are now able to like/unlike posts --- exercise.wwwapi/DTOs/Likes/LikeDTO.cs | 9 +++ exercise.wwwapi/Endpoints/LikeEndpoint.cs | 75 +++++++++++++++++++++++ exercise.wwwapi/Program.cs | 2 + 3 files changed, 86 insertions(+) create mode 100644 exercise.wwwapi/DTOs/Likes/LikeDTO.cs create mode 100644 exercise.wwwapi/Endpoints/LikeEndpoint.cs diff --git a/exercise.wwwapi/DTOs/Likes/LikeDTO.cs b/exercise.wwwapi/DTOs/Likes/LikeDTO.cs new file mode 100644 index 0000000..4a1e863 --- /dev/null +++ b/exercise.wwwapi/DTOs/Likes/LikeDTO.cs @@ -0,0 +1,9 @@ +namespace exercise.wwwapi.DTOs.Likes +{ + public class LikeDTO + { + public int Id { get; set; } + public int PostId { get; set; } + public int UserId { get; set; } + } +} diff --git a/exercise.wwwapi/Endpoints/LikeEndpoint.cs b/exercise.wwwapi/Endpoints/LikeEndpoint.cs new file mode 100644 index 0000000..5e545f3 --- /dev/null +++ b/exercise.wwwapi/Endpoints/LikeEndpoint.cs @@ -0,0 +1,75 @@ +using exercise.wwwapi.DTOs; +using exercise.wwwapi.DTOs.Likes; +using exercise.wwwapi.Helpers; +using exercise.wwwapi.Models; +using exercise.wwwapi.Repository; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using System.Security.Claims; + +namespace exercise.wwwapi.Endpoints +{ + public static class LikeEndpoint + { + public static async Task ConfigureLikeEndpoints(this WebApplication app) + { + var likes = app.MapGroup("likes"); + likes.MapPost("/{postId}", toggleLike).RequireAuthorization().WithSummary("toggle between liked and unliked"); + + } + [ProducesResponseType(StatusCodes.Status201Created)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public static async Task toggleLike( + IRepository likeRepository, + IRepository postRepository, + ClaimsPrincipal claimsPrincipal, + int postId) + { + var userIdClaim = claimsPrincipal.UserRealId(); + if (userIdClaim == null) return Results.Unauthorized(); + + var post = await postRepository.GetByIdWithIncludes(p => p.Include(l => l.Likes), postId); + if (post == null) return TypedResults.NotFound(); + var isLiked = post.Likes.FirstOrDefault(l => l.UserId == userIdClaim); + + if (isLiked != null) + { + var deleteResponse = new ResponseDTO + { + Status = "Success", + Data = new LikeDTO() + { + Id = isLiked.Id, + PostId = isLiked.PostId, + UserId = isLiked.UserId + } + }; + likeRepository.Delete(isLiked); + await likeRepository.SaveAsync(); + return Results.Ok(deleteResponse); + } + + var like = new Like + { + PostId = postId, + UserId = userIdClaim.Value, + }; + var response = new ResponseDTO + { + Status = "success", + Data = new LikeDTO + { + Id = like.Id, + PostId = like.PostId, + UserId = like.UserId + } + }; + + likeRepository.Insert(like); + await likeRepository.SaveAsync(); + + return Results.Created($"/likes/{postId}", response); + } + } +} diff --git a/exercise.wwwapi/Program.cs b/exercise.wwwapi/Program.cs index 0a6ee92..02c8831 100644 --- a/exercise.wwwapi/Program.cs +++ b/exercise.wwwapi/Program.cs @@ -34,6 +34,7 @@ builder.Services.AddScoped, Repository>(); builder.Services.AddScoped, Repository>(); builder.Services.AddScoped, Repository>(); +builder.Services.AddScoped, Repository>(); builder.Services.AddScoped, Repository>(); builder.Services.AddScoped, Repository>(); builder.Services.AddScoped, Repository>(); @@ -189,6 +190,7 @@ app.ConfigureCohortEndpoints(); app.ConfigurePostEndpoints(); app.ConfigureCommentEndpoints(); +app.ConfigureLikeEndpoints(); app.Run(); public partial class Program From 12421e0cf781dd24fe125bf304194c9f8b80d2f4 Mon Sep 17 00:00:00 2001 From: Stian Forren Date: Fri, 19 Sep 2025 15:31:03 +0200 Subject: [PATCH 08/10] added functionality for teacher to update others post --- exercise.wwwapi/DTOs/Comments/CommentDTO.cs | 23 ----------- .../DTOs/Comments/CommentsSuccessDTO.cs | 13 +++--- .../DTOs/Posts/GetPosts/CommentDTO.cs | 11 +++-- .../Posts/UpdatePost/UpdatePostSuccessDTO.cs | 2 + exercise.wwwapi/Endpoints/CommentEndpoints.cs | 41 ++++--------------- exercise.wwwapi/Endpoints/PostEndpoints.cs | 22 ++++++++-- exercise.wwwapi/Models/Post.cs | 3 ++ 7 files changed, 44 insertions(+), 71 deletions(-) delete mode 100644 exercise.wwwapi/DTOs/Comments/CommentDTO.cs diff --git a/exercise.wwwapi/DTOs/Comments/CommentDTO.cs b/exercise.wwwapi/DTOs/Comments/CommentDTO.cs deleted file mode 100644 index b633271..0000000 --- a/exercise.wwwapi/DTOs/Comments/CommentDTO.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Text.Json.Serialization; - -namespace exercise.wwwapi.DTOs.Comments -{ - public class CommentDTO - { - [JsonPropertyName("id")] - public int Id { get; set; } - - [JsonPropertyName("post_id")] - public int PostId { get; set; } - - [JsonPropertyName("user_id")] - public int UserId { get; set; } - - [JsonPropertyName("body")] - public string Body { get; set; } - - [JsonPropertyName("created_at")] - public DateTime CreatedAt { get; set; } - - } -} diff --git a/exercise.wwwapi/DTOs/Comments/CommentsSuccessDTO.cs b/exercise.wwwapi/DTOs/Comments/CommentsSuccessDTO.cs index 4be959c..30e3586 100644 --- a/exercise.wwwapi/DTOs/Comments/CommentsSuccessDTO.cs +++ b/exercise.wwwapi/DTOs/Comments/CommentsSuccessDTO.cs @@ -1,10 +1,9 @@ -using System.Text.Json.Serialization; +using exercise.wwwapi.DTOs.Posts.GetPosts; +using System.Text.Json.Serialization; -namespace exercise.wwwapi.DTOs.Comments + +public class CommentsSuccessDTO { - public class CommentsSuccessDTO - { - [JsonPropertyName("comments")] - public List Comments { get; set; } = new(); - } + [JsonPropertyName("comments")] + public List Comments { get; set; } = new(); } diff --git a/exercise.wwwapi/DTOs/Posts/GetPosts/CommentDTO.cs b/exercise.wwwapi/DTOs/Posts/GetPosts/CommentDTO.cs index c8a1821..532304f 100644 --- a/exercise.wwwapi/DTOs/Posts/GetPosts/CommentDTO.cs +++ b/exercise.wwwapi/DTOs/Posts/GetPosts/CommentDTO.cs @@ -8,8 +8,8 @@ public class CommentDTO public int UserId { get; set; } public string Body { get; set; } = string.Empty; public DateTime CreatedAt { get; set; } - public string firstName { get; set; } - public string lastName { get; set; } + public string? firstName { get; set; } + public string? lastName { get; set; } public CommentDTO() { } @@ -19,8 +19,11 @@ public CommentDTO(Comment model) UserId = model.UserId; Body = model.Body; CreatedAt = model.CreatedAt; - firstName = model.User.FirstName; - lastName = model.User.LastName; + if (model.User != null) + { + firstName = model.User.FirstName; + lastName = model.User.LastName; + } } } } diff --git a/exercise.wwwapi/DTOs/Posts/UpdatePost/UpdatePostSuccessDTO.cs b/exercise.wwwapi/DTOs/Posts/UpdatePost/UpdatePostSuccessDTO.cs index 4057d13..e5eee27 100644 --- a/exercise.wwwapi/DTOs/Posts/UpdatePost/UpdatePostSuccessDTO.cs +++ b/exercise.wwwapi/DTOs/Posts/UpdatePost/UpdatePostSuccessDTO.cs @@ -18,5 +18,7 @@ public class UpdatePostSuccessDTO [JsonPropertyName("created_at")] public DateTime CreatedAt { get; set; } + public DateTime? UpdatedAt { get; set; } + public int? UpdatedById { get; set; } } } diff --git a/exercise.wwwapi/Endpoints/CommentEndpoints.cs b/exercise.wwwapi/Endpoints/CommentEndpoints.cs index f6c24a9..3cf2155 100644 --- a/exercise.wwwapi/Endpoints/CommentEndpoints.cs +++ b/exercise.wwwapi/Endpoints/CommentEndpoints.cs @@ -1,6 +1,7 @@ using exercise.wwwapi.DTOs; using exercise.wwwapi.DTOs.Comments; using exercise.wwwapi.DTOs.Comments.UpdateComment; +using exercise.wwwapi.DTOs.Posts.GetPosts; using exercise.wwwapi.Helpers; using exercise.wwwapi.Models; using exercise.wwwapi.Repository; @@ -28,18 +29,11 @@ public static async Task ConfigureCommentEndpoints(this WebApplication app) private static async Task GetCommentsPerPost(IRepository commentRepository, ClaimsPrincipal comment, int postId) { - var commentsForPost = await commentRepository.GetWithIncludes(c => c.Where(c => c.PostId == postId)); + var commentsForPost = await commentRepository.GetWithIncludes(c => c.Where(c => c.PostId == postId).Include(p => p.User)); var commentData = new CommentsSuccessDTO { - Comments = commentsForPost.Select(c => new CommentDTO - { - Id = c.Id, - PostId = postId, - UserId = c.UserId, - Body = c.Body, - CreatedAt = c.CreatedAt - }).ToList() + Comments = commentsForPost.Select(c => new CommentDTO(c)).ToList() }; var response = new ResponseDTO @@ -97,14 +91,7 @@ public static async Task CreateComment( commentRepository.Insert(comment); await commentRepository.SaveAsync(); - var commentData = new CommentDTO - { - Id = comment.Id, - PostId = comment.PostId, - UserId = comment.UserId, - Body = comment.Body, - CreatedAt = comment.CreatedAt - }; + var commentData = new CommentDTO(comment); var response = new ResponseDTO { @@ -133,7 +120,7 @@ public static async Task UpdateComment( return Results.Unauthorized(); } - var comment = await commentRepository.GetByIdAsync(id); + var comment = await commentRepository.GetByIdWithIncludes(c => c.Include(u => u.User), id); if (comment == null) { @@ -171,14 +158,7 @@ public static async Task UpdateComment( var response = new ResponseDTO { Status = "success", - Data = new CommentDTO - { - Id = comment.Id, - PostId = comment.PostId, - UserId = comment.UserId, - Body = comment.Body, - CreatedAt = comment.CreatedAt, - } + Data = new CommentDTO(comment) }; return TypedResults.Ok(response); @@ -219,14 +199,7 @@ public static async Task DeleteComment( var response = new ResponseDTO { Status = "success", - Data = new CommentDTO - { - Id = comment.Id, - PostId = comment.PostId, - UserId = comment.UserId, - Body = comment.Body, - CreatedAt = comment.CreatedAt - } + Data = new CommentDTO(comment) }; return TypedResults.Ok(response); diff --git a/exercise.wwwapi/Endpoints/PostEndpoints.cs b/exercise.wwwapi/Endpoints/PostEndpoints.cs index 8cfaeea..862768b 100644 --- a/exercise.wwwapi/Endpoints/PostEndpoints.cs +++ b/exercise.wwwapi/Endpoints/PostEndpoints.cs @@ -110,7 +110,7 @@ private static async Task GetAllPosts(IRepository postRepository, private static async Task GetPostById(IRepository postRepository, int id, ClaimsPrincipal user) { var response = await postRepository.GetByIdWithIncludes(p => p.Include(a => a.Author) - .Include(c => c.Comments) + .Include(c => c.Comments).ThenInclude(c => c.User) .Include(l => l.Likes), id); var result = new ResponseDTO() { @@ -144,7 +144,20 @@ public static async Task UpdatePost(IRepository postRepository, i if (post.AuthorId != userIdClaim) { - return Results.Unauthorized(); + if (claimsPrincipal.IsInRole("Teacher")) + { + post.UpdatedAt = DateTime.UtcNow; + post.UpdatedById = userIdClaim; + } + else + { + return Results.Unauthorized(); + } + } + else + { + post.UpdatedAt = DateTime.UtcNow; + post.UpdatedById = userIdClaim; } var validation = await validator.ValidateAsync(request); @@ -178,7 +191,10 @@ public static async Task UpdatePost(IRepository postRepository, i Id = post.Id, AuthorId = post.AuthorId, Body = post.Body, - CreatedAt = post.CreatedAt + CreatedAt = post.CreatedAt, + UpdatedAt = post.UpdatedAt, + UpdatedById = post.UpdatedById + } }; diff --git a/exercise.wwwapi/Models/Post.cs b/exercise.wwwapi/Models/Post.cs index 3be3e0c..16f3127 100644 --- a/exercise.wwwapi/Models/Post.cs +++ b/exercise.wwwapi/Models/Post.cs @@ -23,6 +23,9 @@ public class Post : IEntity [Column("created_at", TypeName = "date")] public DateTime CreatedAt { get; set; } + public DateTime? UpdatedAt { get; set; } + public int? UpdatedById { get; set; } + public User Author { get; set; } public ICollection Comments { get; set; } = new List(); public ICollection Likes { get; set; } = new List(); From 30cb2a685d79bc8bd9656641da1c18dded8e6c62 Mon Sep 17 00:00:00 2001 From: Stian Forren Date: Fri, 19 Sep 2025 15:33:23 +0200 Subject: [PATCH 09/10] updated getbyidfunction in commentendpoint --- exercise.wwwapi/Endpoints/CommentEndpoints.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercise.wwwapi/Endpoints/CommentEndpoints.cs b/exercise.wwwapi/Endpoints/CommentEndpoints.cs index 3cf2155..34072ac 100644 --- a/exercise.wwwapi/Endpoints/CommentEndpoints.cs +++ b/exercise.wwwapi/Endpoints/CommentEndpoints.cs @@ -181,7 +181,7 @@ public static async Task DeleteComment( return Results.Unauthorized(); } - var comment = await commentRepository.GetByIdAsync(id); + var comment = await commentRepository.GetByIdWithIncludes(c => c.Include(u => u.User), id); if (comment == null) { From 942497712d3070c123fb915b03c1910ffb4590e5 Mon Sep 17 00:00:00 2001 From: Stian Forren Date: Fri, 19 Sep 2025 15:44:39 +0200 Subject: [PATCH 10/10] added functionallity for teachers to delete all posts --- exercise.wwwapi/Endpoints/PostEndpoints.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercise.wwwapi/Endpoints/PostEndpoints.cs b/exercise.wwwapi/Endpoints/PostEndpoints.cs index 862768b..ecba8ae 100644 --- a/exercise.wwwapi/Endpoints/PostEndpoints.cs +++ b/exercise.wwwapi/Endpoints/PostEndpoints.cs @@ -224,7 +224,7 @@ public static async Task DeletePost(IRepository postRepository, i return TypedResults.NotFound(); } - if (post.AuthorId != userIdClaim) + if (post.AuthorId != userIdClaim && !claimsPrincipal.IsInRole("Teacher")) { return Results.Unauthorized(); }