Skip to content

Commit 4c3fd25

Browse files
add comment services
1 parent 7291231 commit 4c3fd25

File tree

6 files changed

+615
-27
lines changed

6 files changed

+615
-27
lines changed

internal/handlers/comment.go

Lines changed: 297 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,297 @@
1+
package handlers
2+
3+
import (
4+
"net/http"
5+
"strconv"
6+
7+
"github.com/gin-gonic/gin"
8+
"github.com/go-playground/validator/v10"
9+
"github.com/nevzattalhaozcan/forgotten/internal/models"
10+
"github.com/nevzattalhaozcan/forgotten/internal/services"
11+
)
12+
13+
type CommentHandler struct {
14+
CommentService *services.CommentService
15+
validator *validator.Validate
16+
}
17+
18+
func NewCommentHandler(commentService *services.CommentService) *CommentHandler {
19+
return &CommentHandler{
20+
CommentService: commentService,
21+
validator: validator.New(),
22+
}
23+
}
24+
25+
func (c *CommentHandler) CreateComment(ctx *gin.Context) {
26+
uidRaw, exists := ctx.Get("user_id")
27+
if !exists {
28+
ctx.JSON(http.StatusUnauthorized, gin.H{"error": "user not authenticated"})
29+
return
30+
}
31+
32+
userID, ok := uidRaw.(uint)
33+
if !ok {
34+
ctx.JSON(http.StatusInternalServerError, gin.H{"error": "invalid user ID"})
35+
return
36+
}
37+
38+
postidParam := ctx.Param("id")
39+
postID, err := strconv.ParseUint(postidParam, 10, 32)
40+
if err != nil {
41+
ctx.JSON(http.StatusBadRequest, gin.H{"error": "invalid post ID"})
42+
return
43+
}
44+
45+
var req models.CreateCommentRequest
46+
if err := ctx.ShouldBindJSON(&req); err != nil {
47+
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
48+
return
49+
}
50+
51+
if err := c.validator.Struct(&req); err != nil {
52+
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
53+
return
54+
}
55+
56+
comment, err := c.CommentService.CreateComment(uint(postID), userID, &req)
57+
if err != nil {
58+
ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
59+
return
60+
}
61+
62+
ctx.JSON(http.StatusCreated, gin.H{
63+
"message": "comment created successfully",
64+
"comment": comment,
65+
})
66+
}
67+
68+
func (c *CommentHandler) GetCommentByID(ctx *gin.Context) {
69+
idParam := ctx.Param("id")
70+
id, err := strconv.ParseUint(idParam, 10, 32)
71+
if err != nil {
72+
ctx.JSON(http.StatusBadRequest, gin.H{"error": "invalid comment ID"})
73+
return
74+
}
75+
76+
comment, err := c.CommentService.GetCommentByID(uint(id))
77+
if err != nil {
78+
if err.Error() == "comment not found" {
79+
ctx.JSON(http.StatusNotFound, gin.H{"error": "comment not found"})
80+
return
81+
}
82+
ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
83+
return
84+
}
85+
86+
ctx.JSON(http.StatusOK, gin.H{
87+
"comment": comment,
88+
})
89+
}
90+
91+
func (c *CommentHandler) UpdateComment(ctx *gin.Context) {
92+
idParam := ctx.Param("id")
93+
id, err := strconv.ParseUint(idParam, 10, 32)
94+
if err != nil {
95+
ctx.JSON(http.StatusBadRequest, gin.H{"error": "invalid comment ID"})
96+
return
97+
}
98+
99+
var req models.UpdateCommentRequest
100+
if err := ctx.ShouldBindJSON(&req); err != nil {
101+
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
102+
return
103+
}
104+
105+
if err := c.validator.Struct(&req); err != nil {
106+
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
107+
return
108+
}
109+
110+
comment, err := c.CommentService.UpdateComment(uint(id), &req)
111+
if err != nil {
112+
if err.Error() == "comment not found" {
113+
ctx.JSON(http.StatusNotFound, gin.H{"error": "comment not found"})
114+
return
115+
}
116+
ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
117+
return
118+
}
119+
120+
ctx.JSON(http.StatusOK, gin.H{
121+
"message": "comment updated successfully",
122+
"comment": comment,
123+
})
124+
}
125+
126+
func (c *CommentHandler) DeleteComment(ctx *gin.Context) {
127+
idParam := ctx.Param("id")
128+
id, err := strconv.ParseUint(idParam, 10, 32)
129+
if err != nil {
130+
ctx.JSON(http.StatusBadRequest, gin.H{"error": "invalid comment ID"})
131+
return
132+
}
133+
134+
err = c.CommentService.DeleteComment(uint(id))
135+
if err != nil {
136+
if err.Error() == "comment not found" {
137+
ctx.JSON(http.StatusNotFound, gin.H{"error": "comment not found"})
138+
return
139+
}
140+
ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
141+
return
142+
}
143+
144+
ctx.Status(http.StatusNoContent)
145+
}
146+
147+
func (c *CommentHandler) ListCommentsByPostID(ctx *gin.Context) {
148+
postidParam := ctx.Param("id")
149+
postID, err := strconv.ParseUint(postidParam, 10, 32)
150+
if err != nil {
151+
ctx.JSON(http.StatusBadRequest, gin.H{"error": "invalid post ID"})
152+
return
153+
}
154+
155+
comments, err := c.CommentService.ListCommentsByPostID(uint(postID))
156+
if err != nil {
157+
if err.Error() == "post not found" {
158+
ctx.JSON(http.StatusNotFound, gin.H{"error": "post not found"})
159+
return
160+
}
161+
ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
162+
return
163+
}
164+
165+
ctx.JSON(http.StatusOK, gin.H{
166+
"comments": comments,
167+
})
168+
}
169+
170+
func (c *CommentHandler) ListCommentsByUserID(ctx *gin.Context) {
171+
uidParam := ctx.Param("id")
172+
userID, err := strconv.ParseUint(uidParam, 10, 32)
173+
if err != nil {
174+
ctx.JSON(http.StatusBadRequest, gin.H{"error": "invalid user ID"})
175+
return
176+
}
177+
178+
comments, err := c.CommentService.ListCommentsByUserID(uint(userID))
179+
if err != nil {
180+
if err.Error() == "user not found" {
181+
ctx.JSON(http.StatusNotFound, gin.H{"error": "user not found"})
182+
return
183+
}
184+
ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
185+
return
186+
}
187+
188+
ctx.JSON(http.StatusOK, gin.H{
189+
"comments": comments,
190+
})
191+
}
192+
193+
func (c *CommentHandler) LikeComment(ctx *gin.Context) {
194+
uidRaw, exists := ctx.Get("user_id")
195+
if !exists {
196+
ctx.JSON(http.StatusUnauthorized, gin.H{"error": "user not authenticated"})
197+
return
198+
}
199+
200+
userID, ok := uidRaw.(uint)
201+
if !ok {
202+
ctx.JSON(http.StatusInternalServerError, gin.H{"error": "invalid user ID"})
203+
return
204+
}
205+
206+
idParam := ctx.Param("id")
207+
commentID, err := strconv.ParseUint(idParam, 10, 32)
208+
if err != nil {
209+
ctx.JSON(http.StatusBadRequest, gin.H{"error": "invalid comment ID"})
210+
return
211+
}
212+
213+
err = c.CommentService.LikeComment(userID, uint(commentID))
214+
if err != nil {
215+
if err.Error() == "comment not found" {
216+
ctx.JSON(http.StatusNotFound, gin.H{"error": "comment not found"})
217+
return
218+
}
219+
if err.Error() == "user not found" {
220+
ctx.JSON(http.StatusNotFound, gin.H{"error": "user not found"})
221+
return
222+
}
223+
if err.Error() == "user has already liked this comment" {
224+
ctx.JSON(http.StatusBadRequest, gin.H{"error": "user has already liked this comment"})
225+
return
226+
}
227+
ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
228+
return
229+
}
230+
231+
ctx.JSON(http.StatusOK, gin.H{"message": "comment liked successfully"})
232+
}
233+
234+
//TODO: fix unlike comment
235+
func (c *CommentHandler) UnlikeComment(ctx *gin.Context) {
236+
uidRaw, exists := ctx.Get("user_id")
237+
if !exists {
238+
ctx.JSON(http.StatusUnauthorized, gin.H{"error": "user not authenticated"})
239+
return
240+
}
241+
242+
userID, ok := uidRaw.(uint)
243+
if !ok {
244+
ctx.JSON(http.StatusInternalServerError, gin.H{"error": "invalid user ID"})
245+
return
246+
}
247+
248+
idParam := ctx.Param("id")
249+
commentID, err := strconv.ParseUint(idParam, 10, 32)
250+
if err != nil {
251+
ctx.JSON(http.StatusBadRequest, gin.H{"error": "invalid comment ID"})
252+
return
253+
}
254+
255+
err = c.CommentService.UnlikeComment(userID, uint(commentID))
256+
if err != nil {
257+
if err.Error() == "comment not found" {
258+
ctx.JSON(http.StatusNotFound, gin.H{"error": "comment not found"})
259+
return
260+
}
261+
if err.Error() == "user not found" {
262+
ctx.JSON(http.StatusNotFound, gin.H{"error": "user not found"})
263+
return
264+
}
265+
if err.Error() == "user has not liked this comment" {
266+
ctx.JSON(http.StatusBadRequest, gin.H{"error": "user has not liked this comment"})
267+
return
268+
}
269+
ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
270+
return
271+
}
272+
273+
ctx.JSON(http.StatusOK, gin.H{"message": "comment unliked successfully"})
274+
}
275+
276+
func (c *CommentHandler) ListLikesByCommentID(ctx *gin.Context) {
277+
idParam := ctx.Param("id")
278+
commentID, err := strconv.ParseUint(idParam, 10, 32)
279+
if err != nil {
280+
ctx.JSON(http.StatusBadRequest, gin.H{"error": "invalid comment ID"})
281+
return
282+
}
283+
284+
likes, err := c.CommentService.ListLikesByCommentID(uint(commentID))
285+
if err != nil {
286+
if err.Error() == "comment not found" {
287+
ctx.JSON(http.StatusNotFound, gin.H{"error": "comment not found"})
288+
return
289+
}
290+
ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
291+
return
292+
}
293+
294+
ctx.JSON(http.StatusOK, gin.H{
295+
"likes": likes,
296+
})
297+
}

internal/handlers/server.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ func (s *Server) setupRoutes() {
5151
var eventRepo repository.EventRepository = repository.NewEventRepository(s.db)
5252
var bookRepo repository.BookRepository = repository.NewBookRepository(s.db)
5353
var postRepo repository.PostRepository = repository.NewPostRepository(s.db)
54+
var commentRepo repository.CommentRepository = repository.NewCommentRepository(s.db)
5455

5556
var rdbAvailable bool
5657
var ttl time.Duration
@@ -87,6 +88,9 @@ func (s *Server) setupRoutes() {
8788
postService := services.NewPostService(postRepo, userRepo, clubRepo, s.config)
8889
postHandler := NewPostHandler(postService)
8990

91+
commentService := services.NewCommentService(commentRepo, postRepo, userRepo, s.config)
92+
commentHandler := NewCommentHandler(commentService)
93+
9094
if s.config.Server.Environment != "production" {
9195
s.router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
9296
s.router.GET("/metrics", gin.WrapH(promhttp.Handler()))
@@ -144,6 +148,17 @@ func (s *Server) setupRoutes() {
144148
protected.POST("/posts/:id/like", postHandler.LikePost)
145149
protected.POST("/posts/:id/unlike", postHandler.UnlikePost)
146150
protected.GET("/posts/:id/likes", postHandler.ListLikesByPostID)
151+
152+
protected.POST("/posts/:id/comments", commentHandler.CreateComment)
153+
protected.GET("/comments/:id", commentHandler.GetCommentByID)
154+
protected.PUT("/comments/:id", commentHandler.UpdateComment)
155+
protected.DELETE("/comments/:id", commentHandler.DeleteComment)
156+
protected.GET("/posts/:id/comments", commentHandler.ListCommentsByPostID)
157+
protected.GET("/users/:id/comments", commentHandler.ListCommentsByUserID)
158+
159+
protected.POST("/comments/:id/like", commentHandler.LikeComment)
160+
protected.POST("/comments/:id/unlike", commentHandler.UnlikeComment)
161+
protected.GET("/comments/:id/likes", commentHandler.ListLikesByCommentID)
147162
}
148163
}
149164

internal/models/comment.go

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -32,22 +32,13 @@ type Comment struct {
3232
}
3333

3434
type CreateCommentRequest struct {
35-
PostID uint `json:"post_id" validate:"required"`
3635
Content string `json:"content" validate:"required,min=1"`
3736
}
3837

3938
type UpdateCommentRequest struct {
4039
Content *string `json:"content,omitempty" validate:"omitempty,min=1"`
4140
}
4241

43-
type LikeCommentRequest struct {
44-
CommentID uint `json:"comment_id" validate:"required"`
45-
}
46-
47-
type UnlikeCommentRequest struct {
48-
CommentID uint `json:"comment_id" validate:"required"`
49-
}
50-
5142
type CommentResponse struct {
5243
ID uint `json:"id"`
5344
PostID uint `json:"post_id"`

0 commit comments

Comments
 (0)