Skip to content
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

added vote feature on comments (upvote, downvote) #22

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 63 additions & 8 deletions articles/models.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
package articles

import (
"fmt"
_ "fmt"
"github.com/jinzhu/gorm"
"github.com/wangzitian0/golang-gin-starter-kit/common"
"github.com/wangzitian0/golang-gin-starter-kit/users"
"golang-gin-realworld-example-app/common"
"golang-gin-realworld-example-app/users"
"strconv"
"time"

"github.com/jinzhu/gorm"
// "github.com/wangzitian0/golang-gin-starter-kit/users"
// "github.com/wangzitian0/golang-gin-starter-kit/common"
// "github.com/wangzitian0/golang-gin-starter-kit/users"
)

type ArticleModel struct {
Expand Down Expand Up @@ -44,11 +50,22 @@ type TagModel struct {

type CommentModel struct {
gorm.Model
Article ArticleModel
ArticleID uint
Author ArticleUserModel
AuthorID uint
Body string `gorm:"size:2048"`
Article ArticleModel
ArticleID uint
Author ArticleUserModel
CommentVote []CommentModelVote `gorm:"foreignkey:CommentID"`
AuthorID uint
Body string `gorm:"size:2048"`
}

type CommentModelVote struct {
CreatedAt time.Time
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As we want to provide a best practice, can we use http://gorm.io/docs/conventions.html?
like

UpdatedAt time.Time
DeletedAt *time.Time
UserID uint `gorm:"primary_key;auto_increment:false"`
CommentID uint `gorm:"primary_key;auto_increment:false"`
Comment on lines +61 to +62
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The id might not be uint in some database, as we have used the orm.
We should use orm feature to make it support more database.
http://gorm.io/docs/conventions.html#ID-as-Primary-Key

UpVote bool
DownVote bool
}

func GetArticleUserModel(userModel users.UserModel) ArticleUserModel {
Expand Down Expand Up @@ -267,3 +284,41 @@ func DeleteCommentModel(condition interface{}) error {
err := db.Where(condition).Delete(CommentModel{}).Error
return err
}

func GetCommentVote(vote CommentVoteValidator, userId uint) (CommentModelVote, error) {
db := common.GetDB()
var commentVote CommentModelVote
err := db.Where("user_id = ? AND comment_id = ?", userId, vote.CommentID).First(&commentVote).Error
singh-gursharan marked this conversation as resolved.
Show resolved Hide resolved
return commentVote, err
}

func CreateCommentVote(vote CommentVoteValidator, userId uint) (CommentModelVote, error) {
db := common.GetDB()
commentVote := CommentModelVote{
CommentID: vote.CommentID,
UserID: userId,
UpVote: vote.UpVote,
DownVote: vote.DownVote,
}
err := db.Create(&commentVote).Error
fmt.Printf("\n\nthis is in CreateCommentVote: %+v\n\n", commentVote)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If a error happend, commentVote will be what?
Remove it or

if err != nil {
  ...
}

return commentVote, err
}

func DeleteCommentVote(commentVote CommentModelVote) error {
db := common.GetDB()
err := db.Unscoped().Delete(&commentVote).Error
return err
}

func UpdateCommentVote(vote CommentVoteValidator, userId uint) (CommentModelVote, error) {
db := common.GetDB()
voteModel, err := GetCommentVote(vote, userId)
if err != nil {
return voteModel, err
}
voteModel.UpVote = vote.UpVote
voteModel.DownVote = vote.DownVote
err2 := db.Save(&voteModel).Error
return voteModel, err2
}
79 changes: 76 additions & 3 deletions articles/routers.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,17 @@ package articles

import (
"errors"
"github.com/wangzitian0/golang-gin-starter-kit/common"
"github.com/wangzitian0/golang-gin-starter-kit/users"
"gopkg.in/gin-gonic/gin.v1"
"fmt"
"golang-gin-realworld-example-app/common"
"golang-gin-realworld-example-app/users"
"net/http"
"strconv"

// "github.com/wangzitian0/golang-gin-starter-kit/common"
singh-gursharan marked this conversation as resolved.
Show resolved Hide resolved
// "github.com/wangzitian0/golang-gin-starter-kit/users"

// "gopkg.in/gin-gonic/gin.v1"
"github.com/gin-gonic/gin"
)

func ArticlesRegister(router *gin.RouterGroup) {
Expand All @@ -17,8 +23,74 @@ func ArticlesRegister(router *gin.RouterGroup) {
router.DELETE("/:slug/favorite", ArticleUnfavorite)
router.POST("/:slug/comments", ArticleCommentCreate)
router.DELETE("/:slug/comments/:id", ArticleCommentDelete)
router.POST("/:slug/comments/:id/vote", ArticleCommentVotePost)
router.DELETE("/:slug/comments/:id/vote", ArticleCommentVoteDelete)
router.PUT("/:slug/comments/:id/vote", ArticleCommentVoteUpdate)
}

func ArticleCommentVotePost(c *gin.Context) {
commentVoteValidator := CommentVoteValidator{}
if err := commentVoteValidator.Bind(c); err != nil {
c.JSON(http.StatusBadRequest, err.Error())
return
}
userId := c.MustGet("my_user_id").(uint)
vote, err := CreateCommentVote(commentVoteValidator, userId)
if err != nil {
c.JSON(http.StatusConflict, gin.H{
"error": err.Error(),
})
return
}
serial := VoteSerializer{vote: vote}
c.JSON(http.StatusCreated, gin.H{
"vote": serial.Response(),
})
}
func ArticleCommentVoteUpdate(c *gin.Context) {
commentVoteValidator := CommentVoteValidator{}
if err := commentVoteValidator.Bind(c); err != nil {
c.JSON(http.StatusBadRequest, err.Error())
return
}
userId := c.MustGet("my_user_id").(uint)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use users.UserModel here?
We should not assume the user id is uint, so that someone others will only need change 1 line here and all code could be reuse.

ID uint `gorm:"primary_key"`

We could get the user information by one retrieve of UserModel.
https://github.com/gothinkster/golang-gin-realworld-example-app/blob/master/articles/routers.go#L136

vote, err := UpdateCommentVote(commentVoteValidator, userId)
if err != nil {
c.JSON(http.StatusConflict, gin.H{
"error": err.Error(),
})
return
}
serial := VoteSerializer{vote: vote}
c.JSON(http.StatusOK, gin.H{
"vote": serial.Response(),
})
}
func ArticleCommentVoteDelete(c *gin.Context) {
commentVoteValidator := CommentVoteValidator{}
if err := commentVoteValidator.BindCommentId(c); err != nil {
c.JSON(http.StatusBadRequest, err.Error())
return
}
userId := c.MustGet("my_user_id").(uint)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

vote, err := GetCommentVote(commentVoteValidator, userId)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{
"error": err.Error(),
})
return
}
if err := DeleteCommentVote(vote); err != nil {
c.JSON(http.StatusForbidden, gin.H{
"error": err.Error(),
})
return
}
c.JSON(http.StatusOK, gin.H{
"success": "comment got deleted",
})

}
func ArticlesAnonymousRegister(router *gin.RouterGroup) {
router.GET("/", ArticleList)
router.GET("/:slug", ArticleRetrieve)
Expand All @@ -35,6 +107,7 @@ func ArticleCreate(c *gin.Context) {
c.JSON(http.StatusUnprocessableEntity, common.NewValidatorError(err))
return
}
fmt.Printf("ArticleModelValidator: %+v\n", articleModelValidator)
//fmt.Println(articleModelValidator.articleModel.Author.UserModel)

if err := SaveOne(&articleModelValidator.articleModel); err != nil {
Expand Down
29 changes: 27 additions & 2 deletions articles/serializers.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package articles

import (
"golang-gin-realworld-example-app/users"

"github.com/gosimple/slug"
"github.com/wangzitian0/golang-gin-starter-kit/users"
"gopkg.in/gin-gonic/gin.v1"
// "github.com/wangzitian0/golang-gin-starter-kit/users"
// "gopkg.in/gin-gonic/gin.v1"
"github.com/gin-gonic/gin"
)

type TagSerializer struct {
Expand Down Expand Up @@ -134,3 +137,25 @@ func (s *CommentsSerializer) Response() []CommentResponse {
}
return response
}

type VoteSerializer struct {
vote CommentModelVote
}

type VoteResponse struct {
UserID uint `json:"user_id"`
CommentID uint `json:"comment_id"`
UpVote bool `json:"up_vote"`
DownVote bool `json:"down_vote"`
}

func (self VoteSerializer) Response() VoteResponse {
voteModel := self.vote
voteResponse := VoteResponse{
UserID: voteModel.UserID,
CommentID: voteModel.CommentID,
UpVote: voteModel.UpVote,
DownVote: voteModel.DownVote,
}
return voteResponse
}
55 changes: 51 additions & 4 deletions articles/validators.go
Original file line number Diff line number Diff line change
@@ -1,22 +1,41 @@
package articles

import (
"fmt"
"golang-gin-realworld-example-app/common"
"golang-gin-realworld-example-app/users"

"github.com/gosimple/slug"
"github.com/wangzitian0/golang-gin-starter-kit/common"
"github.com/wangzitian0/golang-gin-starter-kit/users"
"gopkg.in/gin-gonic/gin.v1"
// "github.com/wangzitian0/golang-gin-starter-kit/common"
singh-gursharan marked this conversation as resolved.
Show resolved Hide resolved
// "github.com/wangzitian0/golang-gin-starter-kit/users"
// "gopkg.in/gin-gonic/gin.v1"
"github.com/gin-gonic/gin"
)

type ArticleModelValidator struct {
Article struct {
Title string `form:"title" json:"title" binding:"exists,min=4"`
Title string `form:"title" json:"title" binding:"required"`
Description string `form:"description" json:"description" binding:"max=2048"`
Body string `form:"body" json:"body" binding:"max=2048"`
Tags []string `form:"tagList" json:"tagList"`
} `json:"article"`
articleModel ArticleModel `json:"-"`
}

type CommentVoteValidator struct {
CommentID uint `uri:"id" binding:"required"`
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UpVote bool `json:"up_vote"`
DownVote bool `json:"down_vote"`
}

type CommentVoteError struct {
err string
}

func (e CommentVoteError) Error() string {
return e.err
}

func NewArticleModelValidator() ArticleModelValidator {
return ArticleModelValidator{}
}
Expand Down Expand Up @@ -70,3 +89,31 @@ func (s *CommentModelValidator) Bind(c *gin.Context) error {
s.commentModel.Author = GetArticleUserModel(myUserModel)
return nil
}

func (s *CommentVoteValidator) Bind(c *gin.Context) error {
// commentID := c.Param("id")(int)
// s.CommentID = commentID
if err := c.ShouldBindUri(s); err != nil {
return err
}
err := common.Bind(c, s)
if err != nil {
return err
}
fmt.Printf("\n\n In Bind: %+v \n\n", s)
if (s.DownVote && s.UpVote) || !(s.UpVote || s.DownVote) {
err := CommentVoteError{err: "Either one of the UpVote or DownVote can be true not both."}
return err
}
return nil
}

func (s *CommentVoteValidator) BindCommentId(c *gin.Context) error {
// commentID := c.Param("id")(int)
// s.CommentID = commentID
if err := c.ShouldBindUri(s); err != nil {
return err
}
fmt.Printf("\n\n In Bind: %+v \n\n", s)
return nil
}
11 changes: 8 additions & 3 deletions common/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@ package common

import (
"fmt"

"os"

"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/postgres"
_ "github.com/jinzhu/gorm/dialects/sqlite"
"os"
)

type Database struct {
Expand All @@ -15,13 +18,15 @@ var DB *gorm.DB

// Opening a database and save the reference to `Database` struct.
func Init() *gorm.DB {
db, err := gorm.Open("sqlite3", "./../gorm.db")
// db, err := gorm.Open("sqlite3", "./../gorm.db")
db, err := gorm.Open("postgres", "host=localhost port=5432 user=postgres dbname=realworld password=postgres")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why? The sqlite3 is easy for newcomers.
Can you sure a newcomer of this project could run this code in his laptop?

if err != nil {
fmt.Println("db err: ", err)
}
db.DB().SetMaxIdleConns(10)
//db.LogMode(true)
db.LogMode(true)
DB = db
fmt.Printf("db is: %+v\n", *DB)
return DB
}

Expand Down
10 changes: 6 additions & 4 deletions common/unit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ package common
import (
"bytes"
"errors"
"github.com/stretchr/testify/assert"
"gopkg.in/gin-gonic/gin.v1"

"net/http"
"net/http/httptest"
"os"
"testing"

"github.com/stretchr/testify/assert"
"gopkg.in/gin-gonic/gin.v1"
)

func TestConnectingDatabase(t *testing.T) {
Expand Down Expand Up @@ -83,8 +85,8 @@ func TestNewValidatorError(t *testing.T) {
asserts := assert.New(t)

type Login struct {
Username string `form:"username" json:"username" binding:"exists,alphanum,min=4,max=255"`
Password string `form:"password" json:"password" binding:"exists,min=8,max=255"`
Username string `form:"username" json:"username" binding:"required,alphanum,min=4,max=255"`
Password string `form:"password" json:"password" binding:"required,min=8,max=255"`
}

var requestTests = []struct {
Expand Down
5 changes: 3 additions & 2 deletions common/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@ import (
"math/rand"
"time"

"github.com/dgrijalva/jwt-go"
jwt "github.com/dgrijalva/jwt-go"
"gopkg.in/go-playground/validator.v8"

"github.com/gin-gonic/gin/binding"
"gopkg.in/gin-gonic/gin.v1"
// "gopkg.in/gin-gonic/gin.v1"
"github.com/gin-gonic/gin"
)

var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
Expand Down
Loading