-
Notifications
You must be signed in to change notification settings - Fork 2
Finishing Comment Section in API Design #21
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
Changes from all commits
001fed2
2564ac2
b48fc7f
40c3c7e
d8cfb1b
c57f1a2
f2b42a1
c30d997
59b594b
1a2aa44
19ef650
0ef96ce
eae0ee6
90943a9
e814936
e8d5cea
81d4655
de36d6f
fcce8fd
462e88c
89a363d
bca8625
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,161 @@ | ||||||||||||||||||
| using AskFm.BLL.DTO; | ||||||||||||||||||
| using AskFm.BLL.Services; | ||||||||||||||||||
| using AskFm.BLL.Services.UserIdentityService; | ||||||||||||||||||
| using AskFm.DAL.Interfaces; | ||||||||||||||||||
| using AutoMapper; | ||||||||||||||||||
| using Microsoft.AspNetCore.Authorization; | ||||||||||||||||||
| using Microsoft.AspNetCore.Mvc; | ||||||||||||||||||
|
|
||||||||||||||||||
| namespace AskFm.API.Controllers; | ||||||||||||||||||
|
|
||||||||||||||||||
| [ApiController] | ||||||||||||||||||
| [Route("api/[controller]")] | ||||||||||||||||||
| [Authorize(AuthenticationSchemes = "Bearer")] | ||||||||||||||||||
| public class CommentController : ControllerBase | ||||||||||||||||||
| { | ||||||||||||||||||
|
|
||||||||||||||||||
| private readonly ICommentLikeService _commentLikeService; | ||||||||||||||||||
| private readonly ICommentService _commentService; | ||||||||||||||||||
| private readonly ILogger<CommentController> _logger; | ||||||||||||||||||
| private readonly IUserService _userService; | ||||||||||||||||||
|
|
||||||||||||||||||
|
|
||||||||||||||||||
| public CommentController( | ||||||||||||||||||
| ICommentLikeService commentLikeService, | ||||||||||||||||||
| ICommentService commentService, | ||||||||||||||||||
| IUserService userService, | ||||||||||||||||||
| ILogger<CommentController> logger) | ||||||||||||||||||
| { | ||||||||||||||||||
| _commentLikeService = commentLikeService; | ||||||||||||||||||
| _logger = logger; | ||||||||||||||||||
| _commentService = commentService; | ||||||||||||||||||
| _userService = userService; | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
|
|
||||||||||||||||||
|
|
||||||||||||||||||
|
|
||||||||||||||||||
|
|
||||||||||||||||||
| // GET api/comment/{id}/likes -> get all the likes for a Comment with id = id | ||||||||||||||||||
| [HttpGet("{id}/likes")] | ||||||||||||||||||
| public async Task<IActionResult> GetAllLikes(int id) | ||||||||||||||||||
| { | ||||||||||||||||||
| try | ||||||||||||||||||
| { | ||||||||||||||||||
| var likes = await _commentLikeService.GetLikesForCommentAsync(id); | ||||||||||||||||||
| if (!likes.success) | ||||||||||||||||||
| { | ||||||||||||||||||
| return BadRequest(likes.Errors); | ||||||||||||||||||
| } | ||||||||||||||||||
| return Ok(likes); | ||||||||||||||||||
| } | ||||||||||||||||||
| catch (ArgumentException ex) | ||||||||||||||||||
| { | ||||||||||||||||||
| _logger.LogWarning(ex, "Comment not found with id: {CommentId}", id); | ||||||||||||||||||
| return NotFound(new | ||||||||||||||||||
| { | ||||||||||||||||||
| message = ex.Message, | ||||||||||||||||||
|
|
||||||||||||||||||
| }); | ||||||||||||||||||
| } | ||||||||||||||||||
| catch (Exception ex) | ||||||||||||||||||
| { | ||||||||||||||||||
| _logger.LogError(ex, "Error retrieving likes for comment id: {CommentId}", id); | ||||||||||||||||||
| return StatusCode(500, new { message = "An error occurred while retrieving likes" }); | ||||||||||||||||||
| } | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
|
|
||||||||||||||||||
|
|
||||||||||||||||||
|
|
||||||||||||||||||
| // POST api/comment/{id}/likes -> add a like for a Comment with id = id | ||||||||||||||||||
| [HttpPost("{id}/likes")] | ||||||||||||||||||
| public async Task<IActionResult> AddLike(int id) | ||||||||||||||||||
| { | ||||||||||||||||||
| try | ||||||||||||||||||
| { | ||||||||||||||||||
| var user = await _userService.GetCurrentUserAsync(); | ||||||||||||||||||
|
|
||||||||||||||||||
| if (!user.success) | ||||||||||||||||||
| { | ||||||||||||||||||
| return BadRequest(user.Errors); | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| var createdLike = await _commentLikeService.AddLikeAsync(id, user.Data.Id); | ||||||||||||||||||
|
|
||||||||||||||||||
| if (!createdLike.success) | ||||||||||||||||||
| { | ||||||||||||||||||
| return BadRequest(createdLike.Errors); | ||||||||||||||||||
| } | ||||||||||||||||||
| return CreatedAtAction( | ||||||||||||||||||
| nameof(GetAllLikes), | ||||||||||||||||||
| new { id = id }, | ||||||||||||||||||
| createdLike.Data); | ||||||||||||||||||
| } | ||||||||||||||||||
| catch (ArgumentException ex) | ||||||||||||||||||
| { | ||||||||||||||||||
| _logger.LogWarning(ex, "Invalid request to add like to comment id: {CommentId}", id); | ||||||||||||||||||
| return BadRequest(new { message = ex.Message }); | ||||||||||||||||||
| } | ||||||||||||||||||
| catch (InvalidOperationException ex) | ||||||||||||||||||
| { | ||||||||||||||||||
| _logger.LogWarning(ex, "Cannot add like to comment id: {CommentId}", id); | ||||||||||||||||||
| return Conflict(new { message = ex.Message }); | ||||||||||||||||||
| } | ||||||||||||||||||
| catch (Exception ex) | ||||||||||||||||||
| { | ||||||||||||||||||
| _logger.LogError(ex, "Error adding like to comment id: {CommentId}", id); | ||||||||||||||||||
| return StatusCode(500, new { message = "An error occurred while adding like" }); | ||||||||||||||||||
| } | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
|
|
||||||||||||||||||
|
|
||||||||||||||||||
|
|
||||||||||||||||||
| [HttpDelete("{id}/likes")] | ||||||||||||||||||
| public async Task<IActionResult> DeleteLike(int id) | ||||||||||||||||||
| { | ||||||||||||||||||
| int userId = 0; | ||||||||||||||||||
| try | ||||||||||||||||||
| { | ||||||||||||||||||
|
|
||||||||||||||||||
| var user = await _userService.GetCurrentUserAsync(); | ||||||||||||||||||
|
|
||||||||||||||||||
| if (user==null || !user.success) | ||||||||||||||||||
| return BadRequest(user.Errors); | ||||||||||||||||||
|
|
||||||||||||||||||
|
Comment on lines
+124
to
+126
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. NRE risk when You dereference - if (user==null || !user.success)
- return BadRequest(user.Errors);
+ if (user is null)
+ return Unauthorized();
+ if (!user.success)
+ return BadRequest(new { errors = user.Errors });📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||
|
|
||||||||||||||||||
| userId = user.Data.Id; | ||||||||||||||||||
| var comment = await _commentService.GetCommentAsync(id); | ||||||||||||||||||
|
|
||||||||||||||||||
| if (comment == null || !user.success) | ||||||||||||||||||
| return BadRequest(user.Errors); | ||||||||||||||||||
|
|
||||||||||||||||||
|
Comment on lines
+131
to
+133
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Incorrect condition; You re-check - if (comment == null || !user.success)
- return BadRequest(user.Errors);
+ if (comment is null)
+ return NotFound(new { message = $"Comment with id {id} not found" });
🤖 Prompt for AI Agents |
||||||||||||||||||
|
|
||||||||||||||||||
| var result = await _commentLikeService.DeleteLikeAsync(id, userId); | ||||||||||||||||||
|
|
||||||||||||||||||
| if (!result.success) | ||||||||||||||||||
| { | ||||||||||||||||||
| return BadRequest(result.Errors); | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| return NoContent(); | ||||||||||||||||||
| } | ||||||||||||||||||
| catch (ArgumentException ex) | ||||||||||||||||||
| { | ||||||||||||||||||
| _logger.LogWarning(ex, "Like not found for comment id: {CommentId} and user {UserId}", id, userId); | ||||||||||||||||||
| return NotFound(new { message = ex.Message }); | ||||||||||||||||||
| } | ||||||||||||||||||
| catch (UnauthorizedAccessException ex) | ||||||||||||||||||
| { | ||||||||||||||||||
| _logger.LogWarning(ex, "Unauthorized delete attempt for comment id: {CommentId} by user {UserId}", id, userId); | ||||||||||||||||||
| return Forbid(); | ||||||||||||||||||
| } | ||||||||||||||||||
| catch (Exception ex) | ||||||||||||||||||
| { | ||||||||||||||||||
| _logger.LogError(ex, "Error deleting like from comment id: {CommentId} by user {UserId}", id, userId); | ||||||||||||||||||
| return StatusCode(500, new { message = "An error occurred while deleting like" }); | ||||||||||||||||||
| } | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| } | ||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,5 @@ | ||
| using System.Text; | ||
| using AskFm.BLL.Services; | ||
| using Microsoft.EntityFrameworkCore; | ||
| using Microsoft.AspNetCore.Identity; | ||
| using AskFm.DAL; | ||
|
|
@@ -44,6 +45,8 @@ public static void Main(string[] args) | |
| builder.Services.AddScoped<IUnitOfWork, UnitOfWork>(); | ||
| builder.Services.AddScoped<IAuthService, AuthService>(); | ||
| builder.Services.AddScoped<IUserService, UserService>(); | ||
| builder.Services.AddScoped<ICommentLikeService, CommentLikeService>(); | ||
| builder.Services.AddScoped<ICommentService, CommentService>(); | ||
|
Comment on lines
+48
to
+49
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Verification agent 🧩 Analysis chainGood DI registrations; ensure AutoMapper (if used) is registered too. If Search for If found, add: +builder.Services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());The scoped registrations for the services look correct alongside 🏁 Script executed: #!/bin/bash
rg -nP --type cs -C2 '\bIMapper\b|AutoMapper'Length of output: 1565 Ensure AutoMapper is registered and injected for all IMapper usages We’ve confirmed
Please apply the following updates: • In Program.cs (AskFm/AskFm.API/Program.cs), before building the host, register AutoMapper: builder.Services.AddScoped<ICommentLikeService, CommentLikeService>();
builder.Services.AddScoped<ICommentService, CommentService>();
+builder.Services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());• Update CommentLikeService constructor: - public CommentLikeService(IUnitOfWork unitOfWork, ILogger<CommentLikeService> logger)
+ public CommentLikeService(IUnitOfWork unitOfWork,
+ ILogger<CommentLikeService> logger,
+ IMapper mapper)
{
_unitOfWork = unitOfWork;
_logger = logger;
+ _mapper = mapper;
}• Update CommentController constructor similarly to accept and assign These changes will ensure AutoMapper is available in DI and that each consuming class receives its 🤖 Prompt for AI Agents |
||
| builder.Services.AddControllers(); | ||
| builder.Services.AddEndpointsApiExplorer(); | ||
| builder.Services.AddSwaggerGen(); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -12,6 +12,7 @@ | |
| </ItemGroup> | ||
|
|
||
| <ItemGroup> | ||
| <PackageReference Include="AutoMapper" Version="15.0.1" /> | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Verification agent 🧩 Analysis chainConfirm AutoMapper DI integration point; add DI package in the API if needed. Adding AutoMapper here is fine if BLL uses IMapper internally. If the mapping is configured/registered in the API via Run to verify usage and registration points: 🏁 Script executed: #!/bin/bash
# Find AutoMapper registrations and usages
rg -n --type=cs '\bAddAutoMapper\b|IMapper\b|MapperConfiguration\b|Profile\b' -C3Length of output: 702 🏁 Script executed: #!/bin/bash
# Search for DI package reference in all csproj files
rg -n "AutoMapper.Extensions.Microsoft.DependencyInjection" -g "*.csproj"
# Search for DI registration code (services.AddAutoMapper) in C# files
rg -n --type=cs "AddAutoMapper" -C3Length of output: 109 Add AutoMapper DI registration in the API project The BLL layer is injecting Please address the following:
This will ensure that 🤖 Prompt for AI Agents |
||
| <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.0" /> | ||
| </ItemGroup> | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| namespace AskFm.BLL.DTO; | ||
|
|
||
| public class CommentLikeDto | ||
| { | ||
| public int CommentId { get; set; } | ||
| public int UserId { get; set; } | ||
| public string UserName { get; set; } | ||
| public DateTime CreatedAt { get; set; } | ||
| } |
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.
🛠️ Refactor suggestion
Map "no current user" to 401, not 400
If the current user cannot be resolved, respond with Unauthorized.
📝 Committable suggestion
🤖 Prompt for AI Agents