upvotes
This commit is contained in:
@@ -36,7 +36,7 @@ paths:
|
|||||||
403:
|
403:
|
||||||
description: "Forbidden"
|
description: "Forbidden"
|
||||||
|
|
||||||
/feedback/{feedback_uuid}/upvote:
|
/feedback/{feedback_uuid}/vote:
|
||||||
post:
|
post:
|
||||||
summary: "Upvote a feedback"
|
summary: "Upvote a feedback"
|
||||||
tags:
|
tags:
|
||||||
@@ -54,12 +54,20 @@ paths:
|
|||||||
description: "Session UUID"
|
description: "Session UUID"
|
||||||
required: true
|
required: true
|
||||||
type: string
|
type: string
|
||||||
|
- in: query
|
||||||
|
type: string
|
||||||
|
name: action
|
||||||
|
enum:
|
||||||
|
- "upvote"
|
||||||
|
- "downvote"
|
||||||
|
required: true
|
||||||
responses:
|
responses:
|
||||||
200:
|
200:
|
||||||
description: "successful operation"
|
description: "successful operation"
|
||||||
403:
|
403:
|
||||||
description: "Forbidden"
|
description: "Forbidden"
|
||||||
|
|
||||||
|
|
||||||
/feedbacks:
|
/feedbacks:
|
||||||
get:
|
get:
|
||||||
summary: "Get all feedbacks"
|
summary: "Get all feedbacks"
|
||||||
@@ -128,6 +136,10 @@ definitions:
|
|||||||
- support
|
- support
|
||||||
text:
|
text:
|
||||||
type: string
|
type: string
|
||||||
|
upvoted:
|
||||||
|
type: boolean
|
||||||
|
upvotes:
|
||||||
|
type: integer
|
||||||
created_at:
|
created_at:
|
||||||
type: string
|
type: string
|
||||||
format: date-time
|
format: date-time
|
||||||
@@ -49,8 +49,8 @@ func (h *FeedbackHandler) GetFeedback(params feedback.GetFeedbacksForProjectPara
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *FeedbackHandler) UpvoteFeedback(params feedback.UpvoteFeedbackParams) middleware.Responder {
|
func (h *FeedbackHandler) VoteFeedback(params feedback.UpvoteFeedbackParams) middleware.Responder {
|
||||||
err := h.feedback.Upvote(params.SessionUUID, params.FeedbackUUID)
|
err := h.feedback.Vote(params.SessionUUID, params.FeedbackUUID, params.Action)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return feedback.NewUpvoteFeedbackForbidden()
|
return feedback.NewUpvoteFeedbackForbidden()
|
||||||
}
|
}
|
||||||
@@ -77,6 +77,14 @@ func feedbacksToAPI(feedbacks []*dmodels.Feedback) []*models.Feedback {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func feedbackToAPI(feedback *dmodels.Feedback) *models.Feedback {
|
func feedbackToAPI(feedback *dmodels.Feedback) *models.Feedback {
|
||||||
|
var upvoted bool
|
||||||
|
for _, upvote := range feedback.Upvote {
|
||||||
|
if upvote.SessionUUID == feedback.SessionUUID {
|
||||||
|
upvoted = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return &models.Feedback{
|
return &models.Feedback{
|
||||||
FeedbackUUID: feedback.UUID,
|
FeedbackUUID: feedback.UUID,
|
||||||
SessionUUID: feedback.SessionUUID,
|
SessionUUID: feedback.SessionUUID,
|
||||||
@@ -84,6 +92,8 @@ func feedbackToAPI(feedback *dmodels.Feedback) *models.Feedback {
|
|||||||
Text: feedback.Text,
|
Text: feedback.Text,
|
||||||
Type: string(feedback.Type),
|
Type: string(feedback.Type),
|
||||||
UserID: feedback.UserID,
|
UserID: feedback.UserID,
|
||||||
|
Upvotes: int64(len(feedback.Upvote)),
|
||||||
|
Upvoted: upvoted,
|
||||||
CreatedAt: strfmt.DateTime(feedback.CreatedAt),
|
CreatedAt: strfmt.DateTime(feedback.CreatedAt),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,6 +40,12 @@ type Feedback struct {
|
|||||||
// Enum: [feature feedback support]
|
// Enum: [feature feedback support]
|
||||||
Type string `json:"type,omitempty"`
|
Type string `json:"type,omitempty"`
|
||||||
|
|
||||||
|
// upvoted
|
||||||
|
Upvoted bool `json:"upvoted,omitempty"`
|
||||||
|
|
||||||
|
// upvotes
|
||||||
|
Upvotes int64 `json:"upvotes,omitempty"`
|
||||||
|
|
||||||
// to have more context about the user
|
// to have more context about the user
|
||||||
UserID string `json:"user_id,omitempty"`
|
UserID string `json:"user_id,omitempty"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ func init() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/feedback/{feedback_uuid}/upvote": {
|
"/feedback/{feedback_uuid}/vote": {
|
||||||
"post": {
|
"post": {
|
||||||
"description": "Upvote a feedback",
|
"description": "Upvote a feedback",
|
||||||
"tags": [
|
"tags": [
|
||||||
@@ -90,6 +90,16 @@ func init() {
|
|||||||
"name": "session_uuid",
|
"name": "session_uuid",
|
||||||
"in": "query",
|
"in": "query",
|
||||||
"required": true
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"enum": [
|
||||||
|
"upvote",
|
||||||
|
"downvote"
|
||||||
|
],
|
||||||
|
"type": "string",
|
||||||
|
"name": "action",
|
||||||
|
"in": "query",
|
||||||
|
"required": true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
@@ -198,6 +208,12 @@ func init() {
|
|||||||
"support"
|
"support"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"upvoted": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"upvotes": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
"user_id": {
|
"user_id": {
|
||||||
"description": "to have more context about the user",
|
"description": "to have more context about the user",
|
||||||
"type": "string"
|
"type": "string"
|
||||||
@@ -257,7 +273,7 @@ func init() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/feedback/{feedback_uuid}/upvote": {
|
"/feedback/{feedback_uuid}/vote": {
|
||||||
"post": {
|
"post": {
|
||||||
"description": "Upvote a feedback",
|
"description": "Upvote a feedback",
|
||||||
"tags": [
|
"tags": [
|
||||||
@@ -279,6 +295,16 @@ func init() {
|
|||||||
"name": "session_uuid",
|
"name": "session_uuid",
|
||||||
"in": "query",
|
"in": "query",
|
||||||
"required": true
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"enum": [
|
||||||
|
"upvote",
|
||||||
|
"downvote"
|
||||||
|
],
|
||||||
|
"type": "string",
|
||||||
|
"name": "action",
|
||||||
|
"in": "query",
|
||||||
|
"required": true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
@@ -387,6 +413,12 @@ func init() {
|
|||||||
"support"
|
"support"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"upvoted": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"upvotes": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
"user_id": {
|
"user_id": {
|
||||||
"description": "to have more context about the user",
|
"description": "to have more context about the user",
|
||||||
"type": "string"
|
"type": "string"
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ func NewUpvoteFeedback(ctx *middleware.Context, handler UpvoteFeedbackHandler) *
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
UpvoteFeedback swagger:route POST /feedback/{feedback_uuid}/upvote feedback upvoteFeedback
|
UpvoteFeedback swagger:route POST /feedback/{feedback_uuid}/vote feedback upvoteFeedback
|
||||||
|
|
||||||
# Upvote a feedback
|
# Upvote a feedback
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,11 @@ type UpvoteFeedbackParams struct {
|
|||||||
// HTTP Request Object
|
// HTTP Request Object
|
||||||
HTTPRequest *http.Request `json:"-"`
|
HTTPRequest *http.Request `json:"-"`
|
||||||
|
|
||||||
|
/*
|
||||||
|
Required: true
|
||||||
|
In: query
|
||||||
|
*/
|
||||||
|
Action string
|
||||||
/*Feedback UUID
|
/*Feedback UUID
|
||||||
Required: true
|
Required: true
|
||||||
In: path
|
In: path
|
||||||
@@ -55,6 +60,11 @@ func (o *UpvoteFeedbackParams) BindRequest(r *http.Request, route *middleware.Ma
|
|||||||
|
|
||||||
qs := runtime.Values(r.URL.Query())
|
qs := runtime.Values(r.URL.Query())
|
||||||
|
|
||||||
|
qAction, qhkAction, _ := qs.GetOK("action")
|
||||||
|
if err := o.bindAction(qAction, qhkAction, route.Formats); err != nil {
|
||||||
|
res = append(res, err)
|
||||||
|
}
|
||||||
|
|
||||||
rFeedbackUUID, rhkFeedbackUUID, _ := route.Params.GetOK("feedback_uuid")
|
rFeedbackUUID, rhkFeedbackUUID, _ := route.Params.GetOK("feedback_uuid")
|
||||||
if err := o.bindFeedbackUUID(rFeedbackUUID, rhkFeedbackUUID, route.Formats); err != nil {
|
if err := o.bindFeedbackUUID(rFeedbackUUID, rhkFeedbackUUID, route.Formats); err != nil {
|
||||||
res = append(res, err)
|
res = append(res, err)
|
||||||
@@ -70,6 +80,41 @@ func (o *UpvoteFeedbackParams) BindRequest(r *http.Request, route *middleware.Ma
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// bindAction binds and validates parameter Action from query.
|
||||||
|
func (o *UpvoteFeedbackParams) bindAction(rawData []string, hasKey bool, formats strfmt.Registry) error {
|
||||||
|
if !hasKey {
|
||||||
|
return errors.Required("action", "query", rawData)
|
||||||
|
}
|
||||||
|
var raw string
|
||||||
|
if len(rawData) > 0 {
|
||||||
|
raw = rawData[len(rawData)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Required: true
|
||||||
|
// AllowEmptyValue: false
|
||||||
|
|
||||||
|
if err := validate.RequiredString("action", "query", raw); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
o.Action = raw
|
||||||
|
|
||||||
|
if err := o.validateAction(formats); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// validateAction carries on validations for parameter Action
|
||||||
|
func (o *UpvoteFeedbackParams) validateAction(formats strfmt.Registry) error {
|
||||||
|
|
||||||
|
if err := validate.EnumCase("action", "query", o.Action, []interface{}{"upvote", "downvote"}, true); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// bindFeedbackUUID binds and validates parameter FeedbackUUID from path.
|
// bindFeedbackUUID binds and validates parameter FeedbackUUID from path.
|
||||||
func (o *UpvoteFeedbackParams) bindFeedbackUUID(rawData []string, hasKey bool, formats strfmt.Registry) error {
|
func (o *UpvoteFeedbackParams) bindFeedbackUUID(rawData []string, hasKey bool, formats strfmt.Registry) error {
|
||||||
var raw string
|
var raw string
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import (
|
|||||||
type UpvoteFeedbackURL struct {
|
type UpvoteFeedbackURL struct {
|
||||||
FeedbackUUID string
|
FeedbackUUID string
|
||||||
|
|
||||||
|
Action string
|
||||||
SessionUUID string
|
SessionUUID string
|
||||||
|
|
||||||
_basePath string
|
_basePath string
|
||||||
@@ -42,7 +43,7 @@ func (o *UpvoteFeedbackURL) SetBasePath(bp string) {
|
|||||||
func (o *UpvoteFeedbackURL) Build() (*url.URL, error) {
|
func (o *UpvoteFeedbackURL) Build() (*url.URL, error) {
|
||||||
var _result url.URL
|
var _result url.URL
|
||||||
|
|
||||||
var _path = "/feedback/{feedback_uuid}/upvote"
|
var _path = "/feedback/{feedback_uuid}/vote"
|
||||||
|
|
||||||
feedbackUUID := o.FeedbackUUID
|
feedbackUUID := o.FeedbackUUID
|
||||||
if feedbackUUID != "" {
|
if feedbackUUID != "" {
|
||||||
@@ -59,6 +60,11 @@ func (o *UpvoteFeedbackURL) Build() (*url.URL, error) {
|
|||||||
|
|
||||||
qs := make(url.Values)
|
qs := make(url.Values)
|
||||||
|
|
||||||
|
actionQ := o.Action
|
||||||
|
if actionQ != "" {
|
||||||
|
qs.Set("action", actionQ)
|
||||||
|
}
|
||||||
|
|
||||||
sessionUUIDQ := o.SessionUUID
|
sessionUUIDQ := o.SessionUUID
|
||||||
if sessionUUIDQ != "" {
|
if sessionUUIDQ != "" {
|
||||||
qs.Set("session_uuid", sessionUUIDQ)
|
qs.Set("session_uuid", sessionUUIDQ)
|
||||||
|
|||||||
@@ -292,7 +292,7 @@ func (o *GeraldAPI) initHandlerCache() {
|
|||||||
if o.handlers["POST"] == nil {
|
if o.handlers["POST"] == nil {
|
||||||
o.handlers["POST"] = make(map[string]http.Handler)
|
o.handlers["POST"] = make(map[string]http.Handler)
|
||||||
}
|
}
|
||||||
o.handlers["POST"]["/feedback/{feedback_uuid}/upvote"] = feedback.NewUpvoteFeedback(o.context, o.FeedbackUpvoteFeedbackHandler)
|
o.handlers["POST"]["/feedback/{feedback_uuid}/vote"] = feedback.NewUpvoteFeedback(o.context, o.FeedbackUpvoteFeedbackHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Serve creates a http handler to serve the API over HTTP
|
// Serve creates a http handler to serve the API over HTTP
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ func StartHTTPServer(s *Server, lifecycle fx.Lifecycle) {
|
|||||||
// Feedback
|
// Feedback
|
||||||
api.FeedbackCreateFeedbackHandler = feedback.CreateFeedbackHandlerFunc(s.feedback.CreateFeedback)
|
api.FeedbackCreateFeedbackHandler = feedback.CreateFeedbackHandlerFunc(s.feedback.CreateFeedback)
|
||||||
api.FeedbackGetFeedbacksHandler = feedback.GetFeedbacksHandlerFunc(s.feedback.GetFeedbacks)
|
api.FeedbackGetFeedbacksHandler = feedback.GetFeedbacksHandlerFunc(s.feedback.GetFeedbacks)
|
||||||
api.FeedbackUpvoteFeedbackHandler = feedback.UpvoteFeedbackHandlerFunc(s.feedback.UpvoteFeedback)
|
api.FeedbackUpvoteFeedbackHandler = feedback.UpvoteFeedbackHandlerFunc(s.feedback.VoteFeedback)
|
||||||
api.FeedbackGetFeedbacksForProjectHandler = feedback.GetFeedbacksForProjectHandlerFunc(s.feedback.GetFeedbacksForProject)
|
api.FeedbackGetFeedbacksForProjectHandler = feedback.GetFeedbacksForProjectHandlerFunc(s.feedback.GetFeedbacksForProject)
|
||||||
|
|
||||||
// Add global middleware here
|
// Add global middleware here
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ func (f *FeedbackService) GetByProjectID(projectID string) ([]*models.Feedback,
|
|||||||
return f.db.Feedback.GetByProjectID(projectID)
|
return f.db.Feedback.GetByProjectID(projectID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *FeedbackService) Upvote(sessionID, feedbackUUID string) error {
|
func (f *FeedbackService) Vote(sessionID, feedbackUUID, action string) error {
|
||||||
feedback, err := f.db.Feedback.GetByUUID(feedbackUUID)
|
feedback, err := f.db.Feedback.GetByUUID(feedbackUUID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -44,15 +44,27 @@ func (f *FeedbackService) Upvote(sessionID, feedbackUUID string) error {
|
|||||||
|
|
||||||
// check if project type is feature
|
// check if project type is feature
|
||||||
if feedback.Type != models.FeedbackTypeFeature {
|
if feedback.Type != models.FeedbackTypeFeature {
|
||||||
return errors.New("only feature tickets can be upvoted")
|
return errors.New("only feature tickets can be voted")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch action {
|
||||||
|
case "upvote":
|
||||||
// check if user has already upvoted
|
// check if user has already upvoted
|
||||||
for _, upvote := range feedback.Upvote {
|
for _, upvote := range feedback.Upvote {
|
||||||
if upvote.SessionUUID == sessionID {
|
if upvote.SessionUUID == sessionID {
|
||||||
return errors.New("user has already upvoted")
|
return errors.New("user has already upvoted")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return f.db.Feedback.CreateUpvote(models.NewUpvote(sessionID, feedbackUUID))
|
return f.db.Feedback.CreateUpvote(models.NewUpvote(sessionID, feedbackUUID))
|
||||||
|
|
||||||
|
case "downvote":
|
||||||
|
// check if user has already upvoted
|
||||||
|
for _, upvote := range feedback.Upvote {
|
||||||
|
if upvote.SessionUUID == sessionID {
|
||||||
|
return f.db.Feedback.DeleteUpvoteBySessionIDAndFeedbackUUID(sessionID, feedbackUUID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ func (t *Feedback) GetByUUID(uuid string) (*models.Feedback, error) {
|
|||||||
var ticket models.Feedback
|
var ticket models.Feedback
|
||||||
|
|
||||||
res := t.db.
|
res := t.db.
|
||||||
Preload("Upvote").
|
Preload("Vote").
|
||||||
Where("uuid = ?", uuid).
|
Where("uuid = ?", uuid).
|
||||||
First(&ticket)
|
First(&ticket)
|
||||||
if res.Error != nil {
|
if res.Error != nil {
|
||||||
@@ -63,3 +63,10 @@ func (t *Feedback) GetByProjectID(projectID string) ([]*models.Feedback, error)
|
|||||||
func (t *Feedback) CreateUpvote(upvote *models.Upvote) error {
|
func (t *Feedback) CreateUpvote(upvote *models.Upvote) error {
|
||||||
return t.db.Create(upvote).Error
|
return t.db.Create(upvote).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *Feedback) DeleteUpvoteBySessionIDAndFeedbackUUID(sessionID, feedbackUUID string) error {
|
||||||
|
return t.db.
|
||||||
|
Where("session_uuid = ?", sessionID).
|
||||||
|
Where("feedback_uuid = ?", feedbackUUID).
|
||||||
|
Delete(&models.Upvote{}).Error
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user