Skip to content

Commit

Permalink
feat: adding comments creation - deletion
Browse files Browse the repository at this point in the history
  • Loading branch information
felipebrsk committed Nov 2, 2024
1 parent 0477088 commit a3faff4
Show file tree
Hide file tree
Showing 12 changed files with 625 additions and 2 deletions.
2 changes: 2 additions & 0 deletions cmd/server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ func main() {
adminTagService,
adminGameService,
heartService,
commentService,
db := di.InitDependencies()

// Setup routes with dependency injection
Expand All @@ -58,6 +59,7 @@ func main() {
adminTagService,
adminGameService,
heartService,
commentService,
db,
)

Expand Down
3 changes: 3 additions & 0 deletions cmd/server/routes/auth_protected_routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,7 @@ func RegisterProtectedRoutes(
r.POST("/missions/:id/complete", handlers.MissionHandler.CompleteMission)

r.POST("/hearts", handlers.HeartHandler.ToggleHeartable)

r.POST("/comments", handlers.CommentHandler.Create)
r.DELETE("/comments/:id", handlers.CommentHandler.Delete)
}
3 changes: 3 additions & 0 deletions cmd/server/routes/init_handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ type Handlers struct {
GameHandler *api.GameHandler
HomeHandler *api.HomeHandler
HeartHandler *api.HeartHandler
CommentHandler *api.CommentHandler
}

type AdminHandlers struct {
Expand Down Expand Up @@ -54,6 +55,7 @@ func InitHandlers(
adminTagService *usecases_admin.AdminTagService,
adminGameService *usecases_admin.AdminGameService,
heartService *usecases.HeartService,
commentService *usecases.CommentService,
db *gorm.DB,
) (*Handlers, *AdminHandlers) {
return &Handlers{
Expand All @@ -69,6 +71,7 @@ func InitHandlers(
GameHandler: api.NewGameHandler(gameService, userService),
HomeHandler: api.NewHomeHandler(userService, gameService, bannerService),
HeartHandler: api.NewHeartHandler(userService, heartService),
CommentHandler: api.NewCommentHandler(userService, commentService),
},
&AdminHandlers{
AdminAuthHandler: api_admin.NewAuthHandler(authService, userService),
Expand Down
2 changes: 2 additions & 0 deletions cmd/server/routes/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ func SetupRouter(
adminTagService *usecases_admin.AdminTagService,
adminGameService *usecases_admin.AdminGameService,
heartService *usecases.HeartService,
commentService *usecases.CommentService,
db *gorm.DB,
) *gin.Engine {
r := gin.Default()
Expand Down Expand Up @@ -71,6 +72,7 @@ func SetupRouter(
adminTagService,
adminGameService,
heartService,
commentService,
db,
)

Expand Down
5 changes: 4 additions & 1 deletion di/di.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ func InitDependencies() (
*usecases_admin.AdminTagService,
*usecases_admin.AdminGameService,
*usecases.HeartService,
*usecases.CommentService,
*gorm.DB,
) {
cfg := config.LoadConfig()
Expand Down Expand Up @@ -67,7 +68,8 @@ func InitDependencies() (
adminPlatformService,
adminTagService,
adminGameService,
heartService := Setup(dbConn)
heartService,
commentService := Setup(dbConn)

// Setup clients for non-test environment
if cfg.ENV != "testing" {
Expand Down Expand Up @@ -109,5 +111,6 @@ func InitDependencies() (
adminTagService,
adminGameService,
heartService,
commentService,
dbConn
}
6 changes: 5 additions & 1 deletion di/setup_dependencies.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ func Setup(dbConn *gorm.DB) (
*usecases_admin.AdminTagService,
*usecases_admin.AdminGameService,
*usecases.HeartService,
*usecases.CommentService,
) {
// Create repository instances
userRepo := db.NewUserRepositoryMySQL(dbConn)
Expand All @@ -49,6 +50,7 @@ func Setup(dbConn *gorm.DB) (
adminTagRepo := db_admin.NewAdminTagRepositoryMySQL(dbConn)
adminGameRepo := db_admin.NewAdminGameRepositoryMySQL(dbConn)
heartRepo := db.NewHeartRepositoryMySQL(dbConn)
commentRepo := db.NewCommentRepositoryMySQL(dbConn)

// Create service instances
userService := usecases.NewUserService(userRepo)
Expand All @@ -70,6 +72,7 @@ func Setup(dbConn *gorm.DB) (
adminTagService := usecases_admin.NewAdminTagService(adminTagRepo)
adminGameService := usecases_admin.NewAdminGameService(adminGameRepo)
heartService := usecases.NewHeartService(heartRepo)
commentService := usecases.NewCommentService(commentRepo)

return userService,
authService,
Expand All @@ -89,5 +92,6 @@ func Setup(dbConn *gorm.DB) (
adminPlatformService,
adminTagService,
adminGameService,
heartService
heartService,
commentService
}
97 changes: 97 additions & 0 deletions internal/adapters/api/comment_handler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package api

import (
"gcstatus/internal/domain"
"gcstatus/internal/errors"
"gcstatus/internal/resources"
"gcstatus/internal/usecases"
"gcstatus/internal/utils"
"gcstatus/pkg/s3"
"net/http"
"strconv"

"github.com/gin-gonic/gin"
)

type CommentHandler struct {
userService *usecases.UserService
commentService *usecases.CommentService
}

func NewCommentHandler(
userServuce *usecases.UserService,
commentService *usecases.CommentService,
) *CommentHandler {
return &CommentHandler{
userService: userServuce,
commentService: commentService,
}
}

func (h *CommentHandler) Create(c *gin.Context) {
user, err := utils.Auth(c, h.userService.GetUserByID)
if err != nil {
RespondWithError(c, http.StatusUnauthorized, "Unauthorized: "+err.Error())
return
}

var request struct {
ParentID *uint `json:"parent_id"`
Comment string `json:"comment" binding:"required"`
CommentableID uint `json:"commentable_id" binding:"required"`
CommentableType string `json:"commentable_type" binding:"required"`
}

if err := c.ShouldBindJSON(&request); err != nil {
RespondWithError(c, http.StatusUnprocessableEntity, "Invalid request data")
return
}

commentable := domain.Commentable{
UserID: user.ID,
Comment: request.Comment,
CommentableID: request.CommentableID,
CommentableType: request.CommentableType,
ParentID: request.ParentID,
}

comment, err := h.commentService.Create(commentable)
if err != nil {
RespondWithError(c, http.StatusInternalServerError, "Failed to create comment.")
return
}

transformedComment := resources.TransformCommentable(*comment, s3.GlobalS3Client, user.ID)

response := resources.Response{
Data: transformedComment,
}

c.JSON(http.StatusCreated, response)
}

func (h *CommentHandler) Delete(c *gin.Context) {
commentIDStr := c.Param("id")
user, err := utils.Auth(c, h.userService.GetUserByID)
if err != nil {
RespondWithError(c, http.StatusUnauthorized, "Unauthorized: "+err.Error())
return
}

commentID, err := strconv.ParseUint(commentIDStr, 10, 32)
if err != nil {
RespondWithError(c, http.StatusBadRequest, "Invalid comment ID: "+err.Error())
return
}

if err := h.commentService.Delete(uint(commentID), user.ID); err != nil {
if httpErr, ok := err.(*errors.HttpError); ok {
RespondWithError(c, httpErr.Code, httpErr.Error())
} else {
RespondWithError(c, http.StatusInternalServerError, "Failed to delete comment: "+err.Error())
}
return
}

c.JSON(http.StatusOK, gin.H{"message": "Your comment was successfully removed!"})
}
44 changes: 44 additions & 0 deletions internal/adapters/db/comment_repository_mysql.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package db

import (
"gcstatus/internal/domain"
"gcstatus/internal/ports"

"gorm.io/gorm"
)

type CommentRepositoryMySQL struct {
db *gorm.DB
}

func NewCommentRepositoryMySQL(db *gorm.DB) ports.CommentRepository {
return &CommentRepositoryMySQL{db: db}
}

func (h *CommentRepositoryMySQL) Create(commentable domain.Commentable) (*domain.Commentable, error) {
if err := h.db.Create(&commentable).Error; err != nil {
return nil, err
}

if err := h.db.Preload("User").Preload("Replies.User").Preload("Hearts").First(&commentable, commentable.ID).Error; err != nil {
return nil, err
}

return &commentable, nil
}

func (h *CommentRepositoryMySQL) Delete(id uint) error {
if err := h.db.Delete(&domain.Commentable{}, id).Error; err != nil {
return err
}

return nil
}

func (h *CommentRepositoryMySQL) FindByID(id uint) (*domain.Commentable, error) {
var comment domain.Commentable
if err := h.db.First(&comment, id).Error; err != nil {
return nil, err
}
return &comment, nil
}
9 changes: 9 additions & 0 deletions internal/ports/comment_repository.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package ports

import "gcstatus/internal/domain"

type CommentRepository interface {
FindByID(id uint) (*domain.Commentable, error)
Create(commentable domain.Commentable) (*domain.Commentable, error)
Delete(id uint) error
}
37 changes: 37 additions & 0 deletions internal/usecases/comment_service.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package usecases

import (
"gcstatus/internal/domain"
"gcstatus/internal/errors"
"gcstatus/internal/ports"
"net/http"
)

type CommentService struct {
repo ports.CommentRepository
}

func NewCommentService(repo ports.CommentRepository) *CommentService {
return &CommentService{repo: repo}
}

func (h *CommentService) Create(commentable domain.Commentable) (*domain.Commentable, error) {
return h.repo.Create(commentable)
}

func (h *CommentService) Delete(id uint, userID uint) error {
comment, err := h.repo.FindByID(id)
if err != nil {
return err
}

if comment.UserID != userID {
return errors.NewHttpError(http.StatusForbidden, "This comment does not belongs to you user!")
}

if err := h.repo.Delete(id); err != nil {
return err
}

return nil
}
Loading

0 comments on commit a3faff4

Please sign in to comment.