Skip to content

Commit 96c2ae9

Browse files
committed
feat: add like/unlike post endpoint
1 parent c3a5d9c commit 96c2ae9

File tree

15 files changed

+348
-23
lines changed

15 files changed

+348
-23
lines changed

controller/likes_controller.go

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package controller
2+
3+
import (
4+
"net/http"
5+
"strconv"
6+
7+
"github.com/Lab-RPL-ITS/twitter-clone-api/dto"
8+
"github.com/Lab-RPL-ITS/twitter-clone-api/service"
9+
"github.com/Lab-RPL-ITS/twitter-clone-api/utils"
10+
"github.com/gin-gonic/gin"
11+
)
12+
13+
type (
14+
LikesController interface {
15+
LikePostById(ctx *gin.Context)
16+
UnlikePostById(ctx *gin.Context)
17+
}
18+
19+
likesController struct {
20+
likesService service.LikesService
21+
}
22+
)
23+
24+
func NewLikesController(likesService service.LikesService) LikesController {
25+
return &likesController{
26+
likesService: likesService,
27+
}
28+
}
29+
30+
func (c *likesController) LikePostById(ctx *gin.Context) {
31+
postId := ctx.Param("post_id")
32+
userId := ctx.GetString("user_id")
33+
34+
postIdUint, err := strconv.ParseUint(postId, 10, 64)
35+
if err != nil {
36+
response := utils.BuildResponseFailed(dto.MESSAGE_FAILED_GET_POST_ID, err.Error(), nil)
37+
ctx.JSON(http.StatusBadRequest, response)
38+
return
39+
}
40+
41+
err = c.likesService.LikePostById(ctx, postIdUint, userId)
42+
if err != nil {
43+
response := utils.BuildResponseFailed(dto.MESSAGE_FAILED_LIKE_POST, err.Error(), nil)
44+
ctx.JSON(http.StatusBadRequest, response)
45+
return
46+
}
47+
48+
response := utils.BuildResponseSuccess(dto.MESSAGE_SUCCESS_LIKE_POST, nil)
49+
ctx.JSON(http.StatusOK, response)
50+
}
51+
52+
func (c *likesController) UnlikePostById(ctx *gin.Context) {
53+
postId := ctx.Param("post_id")
54+
userId := ctx.GetString("user_id")
55+
56+
postIdUint, err := strconv.ParseUint(postId, 10, 64)
57+
if err != nil {
58+
response := utils.BuildResponseFailed(dto.MESSAGE_FAILED_GET_POST_ID, err.Error(), nil)
59+
ctx.JSON(http.StatusBadRequest, response)
60+
return
61+
}
62+
63+
err = c.likesService.UnLikePostById(ctx, postIdUint, userId)
64+
if err != nil {
65+
response := utils.BuildResponseFailed(dto.MESSAGE_FAILED_UNLIKE_POST, err.Error(), nil)
66+
ctx.JSON(http.StatusBadRequest, response)
67+
return
68+
}
69+
70+
response := utils.BuildResponseSuccess(dto.MESSAGE_SUCCESS_UNLIKE_POST, nil)
71+
ctx.JSON(http.StatusOK, response)
72+
}

dto/likes_dto.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package dto
2+
3+
import "errors"
4+
5+
const (
6+
// Failed
7+
MESSAGE_FAILED_LIKE_POST = "failed like post"
8+
MESSAGE_FAILED_UNLIKE_POST = "failed unlike post"
9+
10+
// Succcess
11+
MESSAGE_SUCCESS_LIKE_POST = "success like post"
12+
MESSAGE_SUCCESS_UNLIKE_POST = "success unlike post"
13+
)
14+
15+
var (
16+
ErrLikePostById = errors.New("failed to like post")
17+
ErrCheckLikedPost = errors.New("failed to check liked post")
18+
ErrUnlikePostById = errors.New("failed to unlike post")
19+
)
20+
21+
type (
22+
LikesRequest struct {
23+
PostID uint64 `json:"post_id" form:"post_id" binding:"required"`
24+
}
25+
)

dto/post_dto.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,12 @@ type (
3838
}
3939

4040
PostResponse struct {
41-
ID uint64 `json:"id"`
42-
Text string `json:"text"`
43-
ParentID *uint64 `json:"parent_id"`
44-
User UserResponse `json:"user"`
45-
Replies []PostResponse `json:"replies,omitempty"`
41+
ID uint64 `json:"id"`
42+
Text string `json:"text"`
43+
TotalLikes uint64 `json:"total_likes"`
44+
ParentID *uint64 `json:"parent_id"`
45+
User UserResponse `json:"user"`
46+
Replies []PostResponse `json:"replies,omitempty"`
4647
}
4748

4849
PostUpdateRequest struct {

entity/likes_entity.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package entity
2+
3+
import "github.com/google/uuid"
4+
5+
type Like struct {
6+
PostID uint64 `gorm:"primaryKey;not null" json:"post_id"`
7+
Post Post `gorm:"foreignkey:PostID" json:"post"`
8+
9+
UserID uuid.UUID `gorm:"primaryKey;not null" json:"user_id"`
10+
User User `gorm:"foreignkey:UserID" json:"user"`
11+
12+
Timestamp
13+
}

entity/post_entity.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@ package entity
33
import "github.com/google/uuid"
44

55
type Post struct {
6-
ID uint64 `gorm:"primary_key;autoIncrement" json:"id"`
7-
Text string `gorm:"not null" json:"text"`
6+
ID uint64 `gorm:"primaryKey;autoIncrement" json:"id"`
7+
Text string `gorm:"not null" json:"text"`
8+
TotalLikes uint64 `gorm:"default:0" json:"total_likes"`
89

910
Parent *Post `gorm:"foreignkey:ParentID" json:"parent,omitempty"`
1011
ParentID *uint64 `json:"parent_id,omitempty"`

entity/user_entity.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import (
77
)
88

99
type User struct {
10-
ID uuid.UUID `gorm:"type:uuid;primary_key;default:uuid_generate_v4()" json:"id"`
10+
ID uuid.UUID `gorm:"type:uuid;primaryKey;default:uuid_generate_v4()" json:"id"`
1111
Name string `gorm:"not null" json:"name"`
1212
Username string `gorm:"not null" gorm:"unique" json:"username"`
1313
Bio *string `json:"bio"`

migrations/migrate.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ func Migrate(db *gorm.DB) error {
99
if err := db.AutoMigrate(
1010
&entity.User{},
1111
&entity.Post{},
12+
&entity.Like{},
1213
); err != nil {
1314
return err
1415
}

provider/core.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,5 @@ func RegisterDependencies(injector *do.Injector) {
2323

2424
ProvideUserDependencies(injector)
2525
ProvidePostDependencies(injector)
26+
ProvideLikesDependencies(injector)
2627
}

provider/likes_provider.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package provider
2+
3+
import (
4+
"github.com/Lab-RPL-ITS/twitter-clone-api/constants"
5+
"github.com/Lab-RPL-ITS/twitter-clone-api/controller"
6+
"github.com/Lab-RPL-ITS/twitter-clone-api/repository"
7+
"github.com/Lab-RPL-ITS/twitter-clone-api/service"
8+
"github.com/samber/do"
9+
"gorm.io/gorm"
10+
)
11+
12+
func ProvideLikesDependencies(injector *do.Injector) {
13+
db := do.MustInvokeNamed[*gorm.DB](injector, constants.DB)
14+
jwtService := do.MustInvokeNamed[service.JWTService](injector, constants.JWTService)
15+
16+
// Repository
17+
likesRepository := repository.NewLikesRepository(db)
18+
postRepository := repository.NewPostRepository(db)
19+
20+
// Service
21+
likesService := service.NewLikesService(likesRepository, postRepository, jwtService)
22+
23+
// Controller
24+
do.Provide(injector, func(i *do.Injector) (controller.LikesController, error) {
25+
return controller.NewLikesController(likesService), nil
26+
})
27+
}

repository/likes_repository.go

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package repository
2+
3+
import (
4+
"context"
5+
6+
"github.com/Lab-RPL-ITS/twitter-clone-api/entity"
7+
"github.com/google/uuid"
8+
"gorm.io/gorm"
9+
)
10+
11+
type (
12+
LikesRepository interface {
13+
LikePostById(ctx context.Context, tx *gorm.DB, postId uint64, userId string) error
14+
CheckLikedPost(ctx context.Context, tx *gorm.DB, postId uint64, userId string) error
15+
ErrUnlikePostById(ctx context.Context, tx *gorm.DB, postId uint64, userId string) error
16+
}
17+
18+
likesRepository struct {
19+
db *gorm.DB
20+
}
21+
)
22+
23+
func NewLikesRepository(db *gorm.DB) LikesRepository {
24+
return &likesRepository{
25+
db: db,
26+
}
27+
}
28+
29+
func (r *likesRepository) LikePostById(ctx context.Context, tx *gorm.DB, postId uint64, userId string) error {
30+
if tx == nil {
31+
tx = r.db
32+
}
33+
34+
likes := &entity.Like{
35+
UserID: uuid.MustParse(userId),
36+
PostID: postId,
37+
}
38+
39+
if err := tx.WithContext(ctx).Create(&likes).Error; err != nil {
40+
return err
41+
}
42+
43+
return nil
44+
}
45+
46+
func (r *likesRepository) CheckLikedPost(ctx context.Context, tx *gorm.DB, postId uint64, userId string) error {
47+
if tx == nil {
48+
tx = r.db
49+
}
50+
51+
likes := &entity.Like{
52+
UserID: uuid.MustParse(userId),
53+
PostID: postId,
54+
}
55+
56+
if err := tx.WithContext(ctx).Where("user_id = ? AND post_id = ?", userId, postId).First(&likes).Error; err != nil {
57+
return err
58+
}
59+
return nil
60+
}
61+
62+
func (r *likesRepository) ErrUnlikePostById(ctx context.Context, tx *gorm.DB, postId uint64, userId string) error {
63+
if tx == nil {
64+
tx = r.db
65+
}
66+
67+
likes := &entity.Like{
68+
UserID: uuid.MustParse(userId),
69+
PostID: postId,
70+
}
71+
72+
if err := tx.WithContext(ctx).Where("user_id = ? AND post_id = ?", userId, postId).Delete(&likes).Error; err != nil {
73+
return err
74+
}
75+
76+
return nil
77+
}

0 commit comments

Comments
 (0)