mirror of
				https://gitee.com/gitea/gitea
				synced 2025-11-04 16:40:24 +08:00 
			
		
		
		
	[API] Add Reactions (#9220)
* reject reactions wich ar not allowed
* dont duble check CreateReaction now throw ErrForbiddenIssueReaction
* add /repos/{owner}/{repo}/issues/comments/{id}/reactions endpoint
* add Find Functions
* fix some swagger stuff + add issue reaction endpoints + GET ReactionList now use FindReactions...
* explicite Issue Only Reaction for FindReactionsOptions with "-1" commentID
* load issue; load user ...
* return error again
* swagger def canged after LINT
* check if user has ben loaded
* add Tests
* better way of comparing results
* add suggestion
* use different issue for test
(dont interfear with integration test)
* test dont compare Location on timeCompare
* TEST: add forbidden dubble add
* add comments in code to explain
* add settings.UI.ReactionsMap
so if !setting.UI.ReactionsMap[opts.Type] works
			
			
This commit is contained in:
		
							
								
								
									
										145
									
								
								integrations/api_issue_reaction_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										145
									
								
								integrations/api_issue_reaction_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,145 @@
 | 
				
			|||||||
 | 
					// Copyright 2019 The Gitea Authors. All rights reserved.
 | 
				
			||||||
 | 
					// Use of this source code is governed by a MIT-style
 | 
				
			||||||
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package integrations
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"net/http"
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/models"
 | 
				
			||||||
 | 
						api "code.gitea.io/gitea/modules/structs"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/stretchr/testify/assert"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestAPIIssuesReactions(t *testing.T) {
 | 
				
			||||||
 | 
						defer prepareTestEnv(t)()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						issue := models.AssertExistsAndLoadBean(t, &models.Issue{ID: 1}).(*models.Issue)
 | 
				
			||||||
 | 
						_ = issue.LoadRepo()
 | 
				
			||||||
 | 
						owner := models.AssertExistsAndLoadBean(t, &models.User{ID: issue.Repo.OwnerID}).(*models.User)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						session := loginUser(t, owner.Name)
 | 
				
			||||||
 | 
						token := getTokenForLoggedInUser(t, session)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						user1 := models.AssertExistsAndLoadBean(t, &models.User{ID: 1}).(*models.User)
 | 
				
			||||||
 | 
						user2 := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
 | 
				
			||||||
 | 
						urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/reactions?token=%s",
 | 
				
			||||||
 | 
							owner.Name, issue.Repo.Name, issue.Index, token)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//Try to add not allowed reaction
 | 
				
			||||||
 | 
						req := NewRequestWithJSON(t, "POST", urlStr, &api.EditReactionOption{
 | 
				
			||||||
 | 
							Reaction: "wrong",
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						resp := session.MakeRequest(t, req, http.StatusForbidden)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//Delete not allowed reaction
 | 
				
			||||||
 | 
						req = NewRequestWithJSON(t, "DELETE", urlStr, &api.EditReactionOption{
 | 
				
			||||||
 | 
							Reaction: "zzz",
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						resp = session.MakeRequest(t, req, http.StatusOK)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//Add allowed reaction
 | 
				
			||||||
 | 
						req = NewRequestWithJSON(t, "POST", urlStr, &api.EditReactionOption{
 | 
				
			||||||
 | 
							Reaction: "rocket",
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						resp = session.MakeRequest(t, req, http.StatusCreated)
 | 
				
			||||||
 | 
						var apiNewReaction api.ReactionResponse
 | 
				
			||||||
 | 
						DecodeJSON(t, resp, &apiNewReaction)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//Add existing reaction
 | 
				
			||||||
 | 
						resp = session.MakeRequest(t, req, http.StatusForbidden)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//Get end result of reaction list of issue #1
 | 
				
			||||||
 | 
						req = NewRequestf(t, "GET", urlStr)
 | 
				
			||||||
 | 
						resp = session.MakeRequest(t, req, http.StatusOK)
 | 
				
			||||||
 | 
						var apiReactions []*api.ReactionResponse
 | 
				
			||||||
 | 
						DecodeJSON(t, resp, &apiReactions)
 | 
				
			||||||
 | 
						expectResponse := make(map[int]api.ReactionResponse)
 | 
				
			||||||
 | 
						expectResponse[0] = api.ReactionResponse{
 | 
				
			||||||
 | 
							User:     user1.APIFormat(),
 | 
				
			||||||
 | 
							Reaction: "zzz",
 | 
				
			||||||
 | 
							Created:  time.Unix(1573248002, 0),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						expectResponse[1] = api.ReactionResponse{
 | 
				
			||||||
 | 
							User:     user2.APIFormat(),
 | 
				
			||||||
 | 
							Reaction: "eyes",
 | 
				
			||||||
 | 
							Created:  time.Unix(1573248003, 0),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						expectResponse[2] = apiNewReaction
 | 
				
			||||||
 | 
						assert.Len(t, apiReactions, 3)
 | 
				
			||||||
 | 
						for i, r := range apiReactions {
 | 
				
			||||||
 | 
							assert.Equal(t, expectResponse[i].Reaction, r.Reaction)
 | 
				
			||||||
 | 
							assert.Equal(t, expectResponse[i].Created.Unix(), r.Created.Unix())
 | 
				
			||||||
 | 
							assert.Equal(t, expectResponse[i].User.ID, r.User.ID)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestAPICommentReactions(t *testing.T) {
 | 
				
			||||||
 | 
						defer prepareTestEnv(t)()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						comment := models.AssertExistsAndLoadBean(t, &models.Comment{ID: 2}).(*models.Comment)
 | 
				
			||||||
 | 
						_ = comment.LoadIssue()
 | 
				
			||||||
 | 
						issue := comment.Issue
 | 
				
			||||||
 | 
						_ = issue.LoadRepo()
 | 
				
			||||||
 | 
						owner := models.AssertExistsAndLoadBean(t, &models.User{ID: issue.Repo.OwnerID}).(*models.User)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						session := loginUser(t, owner.Name)
 | 
				
			||||||
 | 
						token := getTokenForLoggedInUser(t, session)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						user1 := models.AssertExistsAndLoadBean(t, &models.User{ID: 1}).(*models.User)
 | 
				
			||||||
 | 
						user2 := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
 | 
				
			||||||
 | 
						urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/comments/%d/reactions?token=%s",
 | 
				
			||||||
 | 
							owner.Name, issue.Repo.Name, comment.ID, token)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//Try to add not allowed reaction
 | 
				
			||||||
 | 
						req := NewRequestWithJSON(t, "POST", urlStr, &api.EditReactionOption{
 | 
				
			||||||
 | 
							Reaction: "wrong",
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						resp := session.MakeRequest(t, req, http.StatusForbidden)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//Delete none existing reaction
 | 
				
			||||||
 | 
						req = NewRequestWithJSON(t, "DELETE", urlStr, &api.EditReactionOption{
 | 
				
			||||||
 | 
							Reaction: "eyes",
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						resp = session.MakeRequest(t, req, http.StatusOK)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//Add allowed reaction
 | 
				
			||||||
 | 
						req = NewRequestWithJSON(t, "POST", urlStr, &api.EditReactionOption{
 | 
				
			||||||
 | 
							Reaction: "+1",
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						resp = session.MakeRequest(t, req, http.StatusCreated)
 | 
				
			||||||
 | 
						var apiNewReaction api.ReactionResponse
 | 
				
			||||||
 | 
						DecodeJSON(t, resp, &apiNewReaction)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//Add existing reaction
 | 
				
			||||||
 | 
						resp = session.MakeRequest(t, req, http.StatusForbidden)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//Get end result of reaction list of issue #1
 | 
				
			||||||
 | 
						req = NewRequestf(t, "GET", urlStr)
 | 
				
			||||||
 | 
						resp = session.MakeRequest(t, req, http.StatusOK)
 | 
				
			||||||
 | 
						var apiReactions []*api.ReactionResponse
 | 
				
			||||||
 | 
						DecodeJSON(t, resp, &apiReactions)
 | 
				
			||||||
 | 
						expectResponse := make(map[int]api.ReactionResponse)
 | 
				
			||||||
 | 
						expectResponse[0] = api.ReactionResponse{
 | 
				
			||||||
 | 
							User:     user2.APIFormat(),
 | 
				
			||||||
 | 
							Reaction: "laugh",
 | 
				
			||||||
 | 
							Created:  time.Unix(1573248004, 0),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						expectResponse[1] = api.ReactionResponse{
 | 
				
			||||||
 | 
							User:     user1.APIFormat(),
 | 
				
			||||||
 | 
							Reaction: "laugh",
 | 
				
			||||||
 | 
							Created:  time.Unix(1573248005, 0),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						expectResponse[2] = apiNewReaction
 | 
				
			||||||
 | 
						assert.Len(t, apiReactions, 3)
 | 
				
			||||||
 | 
						for i, r := range apiReactions {
 | 
				
			||||||
 | 
							assert.Equal(t, expectResponse[i].Reaction, r.Reaction)
 | 
				
			||||||
 | 
							assert.Equal(t, expectResponse[i].Created.Unix(), r.Created.Unix())
 | 
				
			||||||
 | 
							assert.Equal(t, expectResponse[i].User.ID, r.User.ID)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1121,6 +1121,21 @@ func (err ErrNewIssueInsert) Error() string {
 | 
				
			|||||||
	return err.OriginalError.Error()
 | 
						return err.OriginalError.Error()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ErrForbiddenIssueReaction is used when a forbidden reaction was try to created
 | 
				
			||||||
 | 
					type ErrForbiddenIssueReaction struct {
 | 
				
			||||||
 | 
						Reaction string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// IsErrForbiddenIssueReaction checks if an error is a ErrForbiddenIssueReaction.
 | 
				
			||||||
 | 
					func IsErrForbiddenIssueReaction(err error) bool {
 | 
				
			||||||
 | 
						_, ok := err.(ErrForbiddenIssueReaction)
 | 
				
			||||||
 | 
						return ok
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (err ErrForbiddenIssueReaction) Error() string {
 | 
				
			||||||
 | 
						return fmt.Sprintf("'%s' is not an allowed reaction", err.Reaction)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// __________      .__  .__ __________                                     __
 | 
					// __________      .__  .__ __________                                     __
 | 
				
			||||||
// \______   \__ __|  | |  |\______   \ ____  ________ __   ____   _______/  |_
 | 
					// \______   \__ __|  | |  |\______   \ ____  ________ __   ____   _______/  |_
 | 
				
			||||||
//  |     ___/  |  \  | |  | |       _// __ \/ ____/  |  \_/ __ \ /  ___/\   __\
 | 
					//  |     ___/  |  \  | |  | |       _// __ \/ ____/  |  \_/ __ \ /  ___/\   __\
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1 +1,39 @@
 | 
				
			|||||||
[] # empty
 | 
					-
 | 
				
			||||||
 | 
					  id: 1 #issue reaction
 | 
				
			||||||
 | 
					  type: zzz # not allowed reaction (added before allowed reaction list has changed)
 | 
				
			||||||
 | 
					  issue_id: 1
 | 
				
			||||||
 | 
					  comment_id: 0
 | 
				
			||||||
 | 
					  user_id: 2
 | 
				
			||||||
 | 
					  created_unix: 1573248001
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-
 | 
				
			||||||
 | 
					  id: 2 #issue reaction
 | 
				
			||||||
 | 
					  type: zzz # not allowed reaction (added before allowed reaction list has changed)
 | 
				
			||||||
 | 
					  issue_id: 1
 | 
				
			||||||
 | 
					  comment_id: 0
 | 
				
			||||||
 | 
					  user_id: 1
 | 
				
			||||||
 | 
					  created_unix: 1573248002
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-
 | 
				
			||||||
 | 
					  id: 3 #issue reaction
 | 
				
			||||||
 | 
					  type: eyes # allowed reaction
 | 
				
			||||||
 | 
					  issue_id: 1
 | 
				
			||||||
 | 
					  comment_id: 0
 | 
				
			||||||
 | 
					  user_id: 2
 | 
				
			||||||
 | 
					  created_unix: 1573248003
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-
 | 
				
			||||||
 | 
					  id: 4 #comment reaction
 | 
				
			||||||
 | 
					  type: laugh # allowed reaction
 | 
				
			||||||
 | 
					  issue_id: 1
 | 
				
			||||||
 | 
					  comment_id: 2
 | 
				
			||||||
 | 
					  user_id: 2
 | 
				
			||||||
 | 
					  created_unix: 1573248004
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-
 | 
				
			||||||
 | 
					  id: 5 #comment reaction
 | 
				
			||||||
 | 
					  type: laugh # allowed reaction
 | 
				
			||||||
 | 
					  issue_id: 1
 | 
				
			||||||
 | 
					  comment_id: 2
 | 
				
			||||||
 | 
					  user_id: 1
 | 
				
			||||||
 | 
					  created_unix: 1573248005
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -33,16 +33,38 @@ type FindReactionsOptions struct {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (opts *FindReactionsOptions) toConds() builder.Cond {
 | 
					func (opts *FindReactionsOptions) toConds() builder.Cond {
 | 
				
			||||||
 | 
						//If Issue ID is set add to Query
 | 
				
			||||||
	var cond = builder.NewCond()
 | 
						var cond = builder.NewCond()
 | 
				
			||||||
	if opts.IssueID > 0 {
 | 
						if opts.IssueID > 0 {
 | 
				
			||||||
		cond = cond.And(builder.Eq{"reaction.issue_id": opts.IssueID})
 | 
							cond = cond.And(builder.Eq{"reaction.issue_id": opts.IssueID})
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						//If CommentID is > 0 add to Query
 | 
				
			||||||
 | 
						//If it is 0 Query ignore CommentID to select
 | 
				
			||||||
 | 
						//If it is -1 it explicit search of Issue Reactions where CommentID = 0
 | 
				
			||||||
	if opts.CommentID > 0 {
 | 
						if opts.CommentID > 0 {
 | 
				
			||||||
		cond = cond.And(builder.Eq{"reaction.comment_id": opts.CommentID})
 | 
							cond = cond.And(builder.Eq{"reaction.comment_id": opts.CommentID})
 | 
				
			||||||
 | 
						} else if opts.CommentID == -1 {
 | 
				
			||||||
 | 
							cond = cond.And(builder.Eq{"reaction.comment_id": 0})
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return cond
 | 
						return cond
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// FindCommentReactions returns a ReactionList of all reactions from an comment
 | 
				
			||||||
 | 
					func FindCommentReactions(comment *Comment) (ReactionList, error) {
 | 
				
			||||||
 | 
						return findReactions(x, FindReactionsOptions{
 | 
				
			||||||
 | 
							IssueID:   comment.IssueID,
 | 
				
			||||||
 | 
							CommentID: comment.ID})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// FindIssueReactions returns a ReactionList of all reactions from an issue
 | 
				
			||||||
 | 
					func FindIssueReactions(issue *Issue) (ReactionList, error) {
 | 
				
			||||||
 | 
						return findReactions(x, FindReactionsOptions{
 | 
				
			||||||
 | 
							IssueID:   issue.ID,
 | 
				
			||||||
 | 
							CommentID: -1,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func findReactions(e Engine, opts FindReactionsOptions) ([]*Reaction, error) {
 | 
					func findReactions(e Engine, opts FindReactionsOptions) ([]*Reaction, error) {
 | 
				
			||||||
	reactions := make([]*Reaction, 0, 10)
 | 
						reactions := make([]*Reaction, 0, 10)
 | 
				
			||||||
	sess := e.Where(opts.toConds())
 | 
						sess := e.Where(opts.toConds())
 | 
				
			||||||
@@ -77,6 +99,10 @@ type ReactionOptions struct {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// CreateReaction creates reaction for issue or comment.
 | 
					// CreateReaction creates reaction for issue or comment.
 | 
				
			||||||
func CreateReaction(opts *ReactionOptions) (reaction *Reaction, err error) {
 | 
					func CreateReaction(opts *ReactionOptions) (reaction *Reaction, err error) {
 | 
				
			||||||
 | 
						if !setting.UI.ReactionsMap[opts.Type] {
 | 
				
			||||||
 | 
							return nil, ErrForbiddenIssueReaction{opts.Type}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	sess := x.NewSession()
 | 
						sess := x.NewSession()
 | 
				
			||||||
	defer sess.Close()
 | 
						defer sess.Close()
 | 
				
			||||||
	if err = sess.Begin(); err != nil {
 | 
						if err = sess.Begin(); err != nil {
 | 
				
			||||||
@@ -160,6 +186,19 @@ func DeleteCommentReaction(doer *User, issue *Issue, comment *Comment, content s
 | 
				
			|||||||
	})
 | 
						})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// LoadUser load user of reaction
 | 
				
			||||||
 | 
					func (r *Reaction) LoadUser() (*User, error) {
 | 
				
			||||||
 | 
						if r.User != nil {
 | 
				
			||||||
 | 
							return r.User, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						user, err := getUserByID(x, r.UserID)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						r.User = user
 | 
				
			||||||
 | 
						return user, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ReactionList represents list of reactions
 | 
					// ReactionList represents list of reactions
 | 
				
			||||||
type ReactionList []*Reaction
 | 
					type ReactionList []*Reaction
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -81,22 +81,22 @@ func TestIssueReactionCount(t *testing.T) {
 | 
				
			|||||||
	user4 := AssertExistsAndLoadBean(t, &User{ID: 4}).(*User)
 | 
						user4 := AssertExistsAndLoadBean(t, &User{ID: 4}).(*User)
 | 
				
			||||||
	ghost := NewGhostUser()
 | 
						ghost := NewGhostUser()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	issue1 := AssertExistsAndLoadBean(t, &Issue{ID: 1}).(*Issue)
 | 
						issue := AssertExistsAndLoadBean(t, &Issue{ID: 2}).(*Issue)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	addReaction(t, user1, issue1, nil, "heart")
 | 
						addReaction(t, user1, issue, nil, "heart")
 | 
				
			||||||
	addReaction(t, user2, issue1, nil, "heart")
 | 
						addReaction(t, user2, issue, nil, "heart")
 | 
				
			||||||
	addReaction(t, user3, issue1, nil, "heart")
 | 
						addReaction(t, user3, issue, nil, "heart")
 | 
				
			||||||
	addReaction(t, user3, issue1, nil, "+1")
 | 
						addReaction(t, user3, issue, nil, "+1")
 | 
				
			||||||
	addReaction(t, user4, issue1, nil, "+1")
 | 
						addReaction(t, user4, issue, nil, "+1")
 | 
				
			||||||
	addReaction(t, user4, issue1, nil, "heart")
 | 
						addReaction(t, user4, issue, nil, "heart")
 | 
				
			||||||
	addReaction(t, ghost, issue1, nil, "-1")
 | 
						addReaction(t, ghost, issue, nil, "-1")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err := issue1.loadReactions(x)
 | 
						err := issue.loadReactions(x)
 | 
				
			||||||
	assert.NoError(t, err)
 | 
						assert.NoError(t, err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	assert.Len(t, issue1.Reactions, 7)
 | 
						assert.Len(t, issue.Reactions, 7)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	reactions := issue1.Reactions.GroupByType()
 | 
						reactions := issue.Reactions.GroupByType()
 | 
				
			||||||
	assert.Len(t, reactions["heart"], 4)
 | 
						assert.Len(t, reactions["heart"], 4)
 | 
				
			||||||
	assert.Equal(t, 2, reactions["heart"].GetMoreUserCount())
 | 
						assert.Equal(t, 2, reactions["heart"].GetMoreUserCount())
 | 
				
			||||||
	assert.Equal(t, user1.DisplayName()+", "+user2.DisplayName(), reactions["heart"].GetFirstUsers())
 | 
						assert.Equal(t, user1.DisplayName()+", "+user2.DisplayName(), reactions["heart"].GetFirstUsers())
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -171,6 +171,7 @@ var (
 | 
				
			|||||||
		DefaultTheme          string
 | 
							DefaultTheme          string
 | 
				
			||||||
		Themes                []string
 | 
							Themes                []string
 | 
				
			||||||
		Reactions             []string
 | 
							Reactions             []string
 | 
				
			||||||
 | 
							ReactionsMap          map[string]bool
 | 
				
			||||||
		SearchRepoDescription bool
 | 
							SearchRepoDescription bool
 | 
				
			||||||
		UseServiceWorker      bool
 | 
							UseServiceWorker      bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -985,6 +986,11 @@ func NewContext() {
 | 
				
			|||||||
	U2F.AppID = sec.Key("APP_ID").MustString(strings.TrimRight(AppURL, "/"))
 | 
						U2F.AppID = sec.Key("APP_ID").MustString(strings.TrimRight(AppURL, "/"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	zip.Verbose = false
 | 
						zip.Verbose = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						UI.ReactionsMap = make(map[string]bool)
 | 
				
			||||||
 | 
						for _, reaction := range UI.Reactions {
 | 
				
			||||||
 | 
							UI.ReactionsMap[reaction] = true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func loadInternalToken(sec *ini.Section) string {
 | 
					func loadInternalToken(sec *ini.Section) string {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										22
									
								
								modules/structs/issue_reaction.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								modules/structs/issue_reaction.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,22 @@
 | 
				
			|||||||
 | 
					// Copyright 2019 The Gitea Authors. All rights reserved.
 | 
				
			||||||
 | 
					// Use of this source code is governed by a MIT-style
 | 
				
			||||||
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package structs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// EditReactionOption contain the reaction type
 | 
				
			||||||
 | 
					type EditReactionOption struct {
 | 
				
			||||||
 | 
						Reaction string `json:"content"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ReactionResponse contain one reaction
 | 
				
			||||||
 | 
					type ReactionResponse struct {
 | 
				
			||||||
 | 
						User     *User  `json:"user"`
 | 
				
			||||||
 | 
						Reaction string `json:"content"`
 | 
				
			||||||
 | 
						// swagger:strfmt date-time
 | 
				
			||||||
 | 
						Created time.Time `json:"created_at"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -657,21 +657,25 @@ func RegisterRoutes(m *macaron.Macaron) {
 | 
				
			|||||||
						Post(reqToken(), mustNotBeArchived, bind(api.CreateIssueOption{}), repo.CreateIssue)
 | 
											Post(reqToken(), mustNotBeArchived, bind(api.CreateIssueOption{}), repo.CreateIssue)
 | 
				
			||||||
					m.Group("/comments", func() {
 | 
										m.Group("/comments", func() {
 | 
				
			||||||
						m.Get("", repo.ListRepoIssueComments)
 | 
											m.Get("", repo.ListRepoIssueComments)
 | 
				
			||||||
						m.Combo("/:id", reqToken()).
 | 
											m.Group("/:id", func() {
 | 
				
			||||||
 | 
												m.Combo("", reqToken()).
 | 
				
			||||||
								Patch(mustNotBeArchived, bind(api.EditIssueCommentOption{}), repo.EditIssueComment).
 | 
													Patch(mustNotBeArchived, bind(api.EditIssueCommentOption{}), repo.EditIssueComment).
 | 
				
			||||||
								Delete(repo.DeleteIssueComment)
 | 
													Delete(repo.DeleteIssueComment)
 | 
				
			||||||
 | 
												m.Combo("/reactions", reqToken()).
 | 
				
			||||||
 | 
													Get(repo.GetIssueCommentReactions).
 | 
				
			||||||
 | 
													Post(bind(api.EditReactionOption{}), repo.PostIssueCommentReaction).
 | 
				
			||||||
 | 
													Delete(bind(api.EditReactionOption{}), repo.DeleteIssueCommentReaction)
 | 
				
			||||||
 | 
											})
 | 
				
			||||||
					})
 | 
										})
 | 
				
			||||||
					m.Group("/:index", func() {
 | 
										m.Group("/:index", func() {
 | 
				
			||||||
						m.Combo("").Get(repo.GetIssue).
 | 
											m.Combo("").Get(repo.GetIssue).
 | 
				
			||||||
							Patch(reqToken(), bind(api.EditIssueOption{}), repo.EditIssue)
 | 
												Patch(reqToken(), bind(api.EditIssueOption{}), repo.EditIssue)
 | 
				
			||||||
 | 
					 | 
				
			||||||
						m.Group("/comments", func() {
 | 
											m.Group("/comments", func() {
 | 
				
			||||||
							m.Combo("").Get(repo.ListIssueComments).
 | 
												m.Combo("").Get(repo.ListIssueComments).
 | 
				
			||||||
								Post(reqToken(), mustNotBeArchived, bind(api.CreateIssueCommentOption{}), repo.CreateIssueComment)
 | 
													Post(reqToken(), mustNotBeArchived, bind(api.CreateIssueCommentOption{}), repo.CreateIssueComment)
 | 
				
			||||||
							m.Combo("/:id", reqToken()).Patch(bind(api.EditIssueCommentOption{}), repo.EditIssueCommentDeprecated).
 | 
												m.Combo("/:id", reqToken()).Patch(bind(api.EditIssueCommentOption{}), repo.EditIssueCommentDeprecated).
 | 
				
			||||||
								Delete(repo.DeleteIssueCommentDeprecated)
 | 
													Delete(repo.DeleteIssueCommentDeprecated)
 | 
				
			||||||
						})
 | 
											})
 | 
				
			||||||
 | 
					 | 
				
			||||||
						m.Group("/labels", func() {
 | 
											m.Group("/labels", func() {
 | 
				
			||||||
							m.Combo("").Get(repo.ListIssueLabels).
 | 
												m.Combo("").Get(repo.ListIssueLabels).
 | 
				
			||||||
								Post(reqToken(), bind(api.IssueLabelsOption{}), repo.AddIssueLabels).
 | 
													Post(reqToken(), bind(api.IssueLabelsOption{}), repo.AddIssueLabels).
 | 
				
			||||||
@@ -679,12 +683,10 @@ func RegisterRoutes(m *macaron.Macaron) {
 | 
				
			|||||||
								Delete(reqToken(), repo.ClearIssueLabels)
 | 
													Delete(reqToken(), repo.ClearIssueLabels)
 | 
				
			||||||
							m.Delete("/:id", reqToken(), repo.DeleteIssueLabel)
 | 
												m.Delete("/:id", reqToken(), repo.DeleteIssueLabel)
 | 
				
			||||||
						})
 | 
											})
 | 
				
			||||||
 | 
					 | 
				
			||||||
						m.Group("/times", func() {
 | 
											m.Group("/times", func() {
 | 
				
			||||||
							m.Combo("").Get(repo.ListTrackedTimes).
 | 
												m.Combo("").Get(repo.ListTrackedTimes).
 | 
				
			||||||
								Post(reqToken(), bind(api.AddTimeOption{}), repo.AddTime)
 | 
													Post(reqToken(), bind(api.AddTimeOption{}), repo.AddTime)
 | 
				
			||||||
						})
 | 
											})
 | 
				
			||||||
 | 
					 | 
				
			||||||
						m.Combo("/deadline").Post(reqToken(), bind(api.EditDeadlineOption{}), repo.UpdateIssueDeadline)
 | 
											m.Combo("/deadline").Post(reqToken(), bind(api.EditDeadlineOption{}), repo.UpdateIssueDeadline)
 | 
				
			||||||
						m.Group("/stopwatch", func() {
 | 
											m.Group("/stopwatch", func() {
 | 
				
			||||||
							m.Post("/start", reqToken(), repo.StartIssueStopwatch)
 | 
												m.Post("/start", reqToken(), repo.StartIssueStopwatch)
 | 
				
			||||||
@@ -695,6 +697,10 @@ func RegisterRoutes(m *macaron.Macaron) {
 | 
				
			|||||||
							m.Put("/:user", reqToken(), repo.AddIssueSubscription)
 | 
												m.Put("/:user", reqToken(), repo.AddIssueSubscription)
 | 
				
			||||||
							m.Delete("/:user", reqToken(), repo.DelIssueSubscription)
 | 
												m.Delete("/:user", reqToken(), repo.DelIssueSubscription)
 | 
				
			||||||
						})
 | 
											})
 | 
				
			||||||
 | 
											m.Combo("/reactions", reqToken()).
 | 
				
			||||||
 | 
												Get(repo.GetIssueReactions).
 | 
				
			||||||
 | 
												Post(bind(api.EditReactionOption{}), repo.PostIssueReaction).
 | 
				
			||||||
 | 
												Delete(bind(api.EditReactionOption{}), repo.DeleteIssueReaction)
 | 
				
			||||||
					})
 | 
										})
 | 
				
			||||||
				}, mustEnableIssuesOrPulls)
 | 
									}, mustEnableIssuesOrPulls)
 | 
				
			||||||
				m.Group("/labels", func() {
 | 
									m.Group("/labels", func() {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										394
									
								
								routers/api/v1/repo/issue_reaction.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										394
									
								
								routers/api/v1/repo/issue_reaction.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,394 @@
 | 
				
			|||||||
 | 
					// Copyright 2019 The Gitea Authors. All rights reserved.
 | 
				
			||||||
 | 
					// Use of this source code is governed by a MIT-style
 | 
				
			||||||
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package repo
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/models"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/context"
 | 
				
			||||||
 | 
						api "code.gitea.io/gitea/modules/structs"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetIssueCommentReactions list reactions of a issue comment
 | 
				
			||||||
 | 
					func GetIssueCommentReactions(ctx *context.APIContext) {
 | 
				
			||||||
 | 
						// swagger:operation GET /repos/{owner}/{repo}/issues/comments/{id}/reactions issue issueGetCommentReactions
 | 
				
			||||||
 | 
						// ---
 | 
				
			||||||
 | 
						// summary: Get a list reactions of a issue comment
 | 
				
			||||||
 | 
						// consumes:
 | 
				
			||||||
 | 
						// - application/json
 | 
				
			||||||
 | 
						// produces:
 | 
				
			||||||
 | 
						// - application/json
 | 
				
			||||||
 | 
						// parameters:
 | 
				
			||||||
 | 
						// - name: owner
 | 
				
			||||||
 | 
						//   in: path
 | 
				
			||||||
 | 
						//   description: owner of the repo
 | 
				
			||||||
 | 
						//   type: string
 | 
				
			||||||
 | 
						//   required: true
 | 
				
			||||||
 | 
						// - name: repo
 | 
				
			||||||
 | 
						//   in: path
 | 
				
			||||||
 | 
						//   description: name of the repo
 | 
				
			||||||
 | 
						//   type: string
 | 
				
			||||||
 | 
						//   required: true
 | 
				
			||||||
 | 
						// - name: id
 | 
				
			||||||
 | 
						//   in: path
 | 
				
			||||||
 | 
						//   description: id of the comment to edit
 | 
				
			||||||
 | 
						//   type: integer
 | 
				
			||||||
 | 
						//   format: int64
 | 
				
			||||||
 | 
						//   required: true
 | 
				
			||||||
 | 
						// responses:
 | 
				
			||||||
 | 
						//   "200":
 | 
				
			||||||
 | 
						//     "$ref": "#/responses/ReactionResponseList"
 | 
				
			||||||
 | 
						comment, err := models.GetCommentByID(ctx.ParamsInt64(":id"))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							if models.IsErrCommentNotExist(err) {
 | 
				
			||||||
 | 
								ctx.NotFound(err)
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								ctx.Error(500, "GetCommentByID", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if !ctx.Repo.CanRead(models.UnitTypeIssues) && !ctx.User.IsAdmin {
 | 
				
			||||||
 | 
							ctx.Error(403, "GetIssueCommentReactions", errors.New("no permission to get reactions"))
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						reactions, err := models.FindCommentReactions(comment)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							ctx.Error(500, "FindIssueReactions", err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						_, err = reactions.LoadUsers()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							ctx.Error(500, "ReactionList.LoadUsers()", err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var result []api.ReactionResponse
 | 
				
			||||||
 | 
						for _, r := range reactions {
 | 
				
			||||||
 | 
							result = append(result, api.ReactionResponse{
 | 
				
			||||||
 | 
								User:     r.User.APIFormat(),
 | 
				
			||||||
 | 
								Reaction: r.Type,
 | 
				
			||||||
 | 
								Created:  r.CreatedUnix.AsTime(),
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ctx.JSON(200, result)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// PostIssueCommentReaction add a reaction to a comment of a issue
 | 
				
			||||||
 | 
					func PostIssueCommentReaction(ctx *context.APIContext, form api.EditReactionOption) {
 | 
				
			||||||
 | 
						// swagger:operation POST /repos/{owner}/{repo}/issues/comments/{id}/reactions issue issuePostCommentReaction
 | 
				
			||||||
 | 
						// ---
 | 
				
			||||||
 | 
						// summary: Add a reaction to a comment of a issue comment
 | 
				
			||||||
 | 
						// consumes:
 | 
				
			||||||
 | 
						// - application/json
 | 
				
			||||||
 | 
						// produces:
 | 
				
			||||||
 | 
						// - application/json
 | 
				
			||||||
 | 
						// parameters:
 | 
				
			||||||
 | 
						// - name: owner
 | 
				
			||||||
 | 
						//   in: path
 | 
				
			||||||
 | 
						//   description: owner of the repo
 | 
				
			||||||
 | 
						//   type: string
 | 
				
			||||||
 | 
						//   required: true
 | 
				
			||||||
 | 
						// - name: repo
 | 
				
			||||||
 | 
						//   in: path
 | 
				
			||||||
 | 
						//   description: name of the repo
 | 
				
			||||||
 | 
						//   type: string
 | 
				
			||||||
 | 
						//   required: true
 | 
				
			||||||
 | 
						// - name: id
 | 
				
			||||||
 | 
						//   in: path
 | 
				
			||||||
 | 
						//   description: id of the comment to edit
 | 
				
			||||||
 | 
						//   type: integer
 | 
				
			||||||
 | 
						//   format: int64
 | 
				
			||||||
 | 
						//   required: true
 | 
				
			||||||
 | 
						// - name: content
 | 
				
			||||||
 | 
						//   in: body
 | 
				
			||||||
 | 
						//   schema:
 | 
				
			||||||
 | 
						//     "$ref": "#/definitions/EditReactionOption"
 | 
				
			||||||
 | 
						// responses:
 | 
				
			||||||
 | 
						//   "201":
 | 
				
			||||||
 | 
						//     "$ref": "#/responses/ReactionResponse"
 | 
				
			||||||
 | 
						changeIssueCommentReaction(ctx, form, true)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// DeleteIssueCommentReaction list reactions of a issue comment
 | 
				
			||||||
 | 
					func DeleteIssueCommentReaction(ctx *context.APIContext, form api.EditReactionOption) {
 | 
				
			||||||
 | 
						// swagger:operation DELETE /repos/{owner}/{repo}/issues/comments/{id}/reactions issue issueDeleteCommentReaction
 | 
				
			||||||
 | 
						// ---
 | 
				
			||||||
 | 
						// summary: Remove a reaction from a comment of a issue comment
 | 
				
			||||||
 | 
						// consumes:
 | 
				
			||||||
 | 
						// - application/json
 | 
				
			||||||
 | 
						// produces:
 | 
				
			||||||
 | 
						// - application/json
 | 
				
			||||||
 | 
						// parameters:
 | 
				
			||||||
 | 
						// - name: owner
 | 
				
			||||||
 | 
						//   in: path
 | 
				
			||||||
 | 
						//   description: owner of the repo
 | 
				
			||||||
 | 
						//   type: string
 | 
				
			||||||
 | 
						//   required: true
 | 
				
			||||||
 | 
						// - name: repo
 | 
				
			||||||
 | 
						//   in: path
 | 
				
			||||||
 | 
						//   description: name of the repo
 | 
				
			||||||
 | 
						//   type: string
 | 
				
			||||||
 | 
						//   required: true
 | 
				
			||||||
 | 
						// - name: id
 | 
				
			||||||
 | 
						//   in: path
 | 
				
			||||||
 | 
						//   description: id of the comment to edit
 | 
				
			||||||
 | 
						//   type: integer
 | 
				
			||||||
 | 
						//   format: int64
 | 
				
			||||||
 | 
						//   required: true
 | 
				
			||||||
 | 
						// - name: content
 | 
				
			||||||
 | 
						//   in: body
 | 
				
			||||||
 | 
						//   schema:
 | 
				
			||||||
 | 
						//     "$ref": "#/definitions/EditReactionOption"
 | 
				
			||||||
 | 
						// responses:
 | 
				
			||||||
 | 
						//   "200":
 | 
				
			||||||
 | 
						//     "$ref": "#/responses/empty"
 | 
				
			||||||
 | 
						changeIssueCommentReaction(ctx, form, false)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func changeIssueCommentReaction(ctx *context.APIContext, form api.EditReactionOption, isCreateType bool) {
 | 
				
			||||||
 | 
						comment, err := models.GetCommentByID(ctx.ParamsInt64(":id"))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							if models.IsErrCommentNotExist(err) {
 | 
				
			||||||
 | 
								ctx.NotFound(err)
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								ctx.Error(500, "GetCommentByID", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = comment.LoadIssue()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							ctx.Error(500, "comment.LoadIssue() failed", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if comment.Issue.IsLocked && !ctx.Repo.CanWrite(models.UnitTypeIssues) && !ctx.User.IsAdmin {
 | 
				
			||||||
 | 
							ctx.Error(403, "ChangeIssueCommentReaction", errors.New("no permission to change reaction"))
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if isCreateType {
 | 
				
			||||||
 | 
							// PostIssueCommentReaction part
 | 
				
			||||||
 | 
							reaction, err := models.CreateCommentReaction(ctx.User, comment.Issue, comment, form.Reaction)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								if models.IsErrForbiddenIssueReaction(err) {
 | 
				
			||||||
 | 
									ctx.Error(403, err.Error(), err)
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									ctx.Error(500, "CreateCommentReaction", err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							_, err = reaction.LoadUser()
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								ctx.Error(500, "Reaction.LoadUser()", err)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							ctx.JSON(201, api.ReactionResponse{
 | 
				
			||||||
 | 
								User:     reaction.User.APIFormat(),
 | 
				
			||||||
 | 
								Reaction: reaction.Type,
 | 
				
			||||||
 | 
								Created:  reaction.CreatedUnix.AsTime(),
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							// DeleteIssueCommentReaction part
 | 
				
			||||||
 | 
							err = models.DeleteCommentReaction(ctx.User, comment.Issue, comment, form.Reaction)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								ctx.Error(500, "DeleteCommentReaction", err)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							ctx.Status(200)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetIssueReactions list reactions of a issue comment
 | 
				
			||||||
 | 
					func GetIssueReactions(ctx *context.APIContext) {
 | 
				
			||||||
 | 
						// swagger:operation GET /repos/{owner}/{repo}/issues/{index}/reactions issue issueGetIssueReactions
 | 
				
			||||||
 | 
						// ---
 | 
				
			||||||
 | 
						// summary: Get a list reactions of a issue
 | 
				
			||||||
 | 
						// consumes:
 | 
				
			||||||
 | 
						// - application/json
 | 
				
			||||||
 | 
						// produces:
 | 
				
			||||||
 | 
						// - application/json
 | 
				
			||||||
 | 
						// parameters:
 | 
				
			||||||
 | 
						// - name: owner
 | 
				
			||||||
 | 
						//   in: path
 | 
				
			||||||
 | 
						//   description: owner of the repo
 | 
				
			||||||
 | 
						//   type: string
 | 
				
			||||||
 | 
						//   required: true
 | 
				
			||||||
 | 
						// - name: repo
 | 
				
			||||||
 | 
						//   in: path
 | 
				
			||||||
 | 
						//   description: name of the repo
 | 
				
			||||||
 | 
						//   type: string
 | 
				
			||||||
 | 
						//   required: true
 | 
				
			||||||
 | 
						// - name: index
 | 
				
			||||||
 | 
						//   in: path
 | 
				
			||||||
 | 
						//   description: index of the issue
 | 
				
			||||||
 | 
						//   type: integer
 | 
				
			||||||
 | 
						//   format: int64
 | 
				
			||||||
 | 
						//   required: true
 | 
				
			||||||
 | 
						// responses:
 | 
				
			||||||
 | 
						//   "200":
 | 
				
			||||||
 | 
						//     "$ref": "#/responses/ReactionResponseList"
 | 
				
			||||||
 | 
						issue, err := models.GetIssueWithAttrsByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							if models.IsErrIssueNotExist(err) {
 | 
				
			||||||
 | 
								ctx.NotFound()
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								ctx.Error(500, "GetIssueByIndex", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if !ctx.Repo.CanRead(models.UnitTypeIssues) && !ctx.User.IsAdmin {
 | 
				
			||||||
 | 
							ctx.Error(403, "GetIssueReactions", errors.New("no permission to get reactions"))
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						reactions, err := models.FindIssueReactions(issue)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							ctx.Error(500, "FindIssueReactions", err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						_, err = reactions.LoadUsers()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							ctx.Error(500, "ReactionList.LoadUsers()", err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var result []api.ReactionResponse
 | 
				
			||||||
 | 
						for _, r := range reactions {
 | 
				
			||||||
 | 
							result = append(result, api.ReactionResponse{
 | 
				
			||||||
 | 
								User:     r.User.APIFormat(),
 | 
				
			||||||
 | 
								Reaction: r.Type,
 | 
				
			||||||
 | 
								Created:  r.CreatedUnix.AsTime(),
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ctx.JSON(200, result)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// PostIssueReaction add a reaction to a comment of a issue
 | 
				
			||||||
 | 
					func PostIssueReaction(ctx *context.APIContext, form api.EditReactionOption) {
 | 
				
			||||||
 | 
						// swagger:operation POST /repos/{owner}/{repo}/issues/{index}/reactions issue issuePostIssueReaction
 | 
				
			||||||
 | 
						// ---
 | 
				
			||||||
 | 
						// summary: Add a reaction to a comment of a issue
 | 
				
			||||||
 | 
						// consumes:
 | 
				
			||||||
 | 
						// - application/json
 | 
				
			||||||
 | 
						// produces:
 | 
				
			||||||
 | 
						// - application/json
 | 
				
			||||||
 | 
						// parameters:
 | 
				
			||||||
 | 
						// - name: owner
 | 
				
			||||||
 | 
						//   in: path
 | 
				
			||||||
 | 
						//   description: owner of the repo
 | 
				
			||||||
 | 
						//   type: string
 | 
				
			||||||
 | 
						//   required: true
 | 
				
			||||||
 | 
						// - name: repo
 | 
				
			||||||
 | 
						//   in: path
 | 
				
			||||||
 | 
						//   description: name of the repo
 | 
				
			||||||
 | 
						//   type: string
 | 
				
			||||||
 | 
						//   required: true
 | 
				
			||||||
 | 
						// - name: index
 | 
				
			||||||
 | 
						//   in: path
 | 
				
			||||||
 | 
						//   description: index of the issue
 | 
				
			||||||
 | 
						//   type: integer
 | 
				
			||||||
 | 
						//   format: int64
 | 
				
			||||||
 | 
						//   required: true
 | 
				
			||||||
 | 
						// - name: content
 | 
				
			||||||
 | 
						//   in: body
 | 
				
			||||||
 | 
						//   schema:
 | 
				
			||||||
 | 
						//     "$ref": "#/definitions/EditReactionOption"
 | 
				
			||||||
 | 
						// responses:
 | 
				
			||||||
 | 
						//   "201":
 | 
				
			||||||
 | 
						//     "$ref": "#/responses/ReactionResponse"
 | 
				
			||||||
 | 
						changeIssueReaction(ctx, form, true)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// DeleteIssueReaction list reactions of a issue comment
 | 
				
			||||||
 | 
					func DeleteIssueReaction(ctx *context.APIContext, form api.EditReactionOption) {
 | 
				
			||||||
 | 
						// swagger:operation DELETE /repos/{owner}/{repo}/issues/{index}/reactions issue issueDeleteIssueReaction
 | 
				
			||||||
 | 
						// ---
 | 
				
			||||||
 | 
						// summary: Remove a reaction from a comment of a issue
 | 
				
			||||||
 | 
						// consumes:
 | 
				
			||||||
 | 
						// - application/json
 | 
				
			||||||
 | 
						// produces:
 | 
				
			||||||
 | 
						// - application/json
 | 
				
			||||||
 | 
						// parameters:
 | 
				
			||||||
 | 
						// - name: owner
 | 
				
			||||||
 | 
						//   in: path
 | 
				
			||||||
 | 
						//   description: owner of the repo
 | 
				
			||||||
 | 
						//   type: string
 | 
				
			||||||
 | 
						//   required: true
 | 
				
			||||||
 | 
						// - name: repo
 | 
				
			||||||
 | 
						//   in: path
 | 
				
			||||||
 | 
						//   description: name of the repo
 | 
				
			||||||
 | 
						//   type: string
 | 
				
			||||||
 | 
						//   required: true
 | 
				
			||||||
 | 
						// - name: index
 | 
				
			||||||
 | 
						//   in: path
 | 
				
			||||||
 | 
						//   description: index of the issue
 | 
				
			||||||
 | 
						//   type: integer
 | 
				
			||||||
 | 
						//   format: int64
 | 
				
			||||||
 | 
						//   required: true
 | 
				
			||||||
 | 
						// - name: content
 | 
				
			||||||
 | 
						//   in: body
 | 
				
			||||||
 | 
						//   schema:
 | 
				
			||||||
 | 
						//     "$ref": "#/definitions/EditReactionOption"
 | 
				
			||||||
 | 
						// responses:
 | 
				
			||||||
 | 
						//   "200":
 | 
				
			||||||
 | 
						//     "$ref": "#/responses/empty"
 | 
				
			||||||
 | 
						changeIssueReaction(ctx, form, false)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func changeIssueReaction(ctx *context.APIContext, form api.EditReactionOption, isCreateType bool) {
 | 
				
			||||||
 | 
						issue, err := models.GetIssueWithAttrsByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							if models.IsErrIssueNotExist(err) {
 | 
				
			||||||
 | 
								ctx.NotFound()
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								ctx.Error(500, "GetIssueByIndex", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if issue.IsLocked && !ctx.Repo.CanWrite(models.UnitTypeIssues) && !ctx.User.IsAdmin {
 | 
				
			||||||
 | 
							ctx.Error(403, "ChangeIssueCommentReaction", errors.New("no permission to change reaction"))
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if isCreateType {
 | 
				
			||||||
 | 
							// PostIssueReaction part
 | 
				
			||||||
 | 
							reaction, err := models.CreateIssueReaction(ctx.User, issue, form.Reaction)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								if models.IsErrForbiddenIssueReaction(err) {
 | 
				
			||||||
 | 
									ctx.Error(403, err.Error(), err)
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									ctx.Error(500, "CreateCommentReaction", err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							_, err = reaction.LoadUser()
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								ctx.Error(500, "Reaction.LoadUser()", err)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							ctx.JSON(201, api.ReactionResponse{
 | 
				
			||||||
 | 
								User:     reaction.User.APIFormat(),
 | 
				
			||||||
 | 
								Reaction: reaction.Type,
 | 
				
			||||||
 | 
								Created:  reaction.CreatedUnix.AsTime(),
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							// DeleteIssueReaction part
 | 
				
			||||||
 | 
							err = models.DeleteIssueReaction(ctx.User, issue, form.Reaction)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								ctx.Error(500, "DeleteIssueReaction", err)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							ctx.Status(200)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -84,3 +84,24 @@ type swaggerIssueDeadline struct {
 | 
				
			|||||||
	// in:body
 | 
						// in:body
 | 
				
			||||||
	Body api.IssueDeadline `json:"body"`
 | 
						Body api.IssueDeadline `json:"body"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// EditReactionOption
 | 
				
			||||||
 | 
					// swagger:response EditReactionOption
 | 
				
			||||||
 | 
					type swaggerEditReactionOption struct {
 | 
				
			||||||
 | 
						// in:body
 | 
				
			||||||
 | 
						Body api.EditReactionOption `json:"body"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ReactionResponse
 | 
				
			||||||
 | 
					// swagger:response ReactionResponse
 | 
				
			||||||
 | 
					type swaggerReactionResponse struct {
 | 
				
			||||||
 | 
						// in:body
 | 
				
			||||||
 | 
						Body api.ReactionResponse `json:"body"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ReactionResponseList
 | 
				
			||||||
 | 
					// swagger:response ReactionResponseList
 | 
				
			||||||
 | 
					type swaggerReactionResponseList struct {
 | 
				
			||||||
 | 
						// in:body
 | 
				
			||||||
 | 
						Body []api.ReactionResponse `json:"body"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1463,14 +1463,12 @@ func ChangeIssueReaction(ctx *context.Context, form auth.ReactionForm) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	switch ctx.Params(":action") {
 | 
						switch ctx.Params(":action") {
 | 
				
			||||||
	case "react":
 | 
						case "react":
 | 
				
			||||||
		if !util.IsStringInSlice(form.Content, setting.UI.Reactions) {
 | 
					 | 
				
			||||||
			err := fmt.Errorf("ChangeIssueReaction: '%s' is not an allowed reaction", form.Content)
 | 
					 | 
				
			||||||
			ctx.ServerError(err.Error(), err)
 | 
					 | 
				
			||||||
			return
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		reaction, err := models.CreateIssueReaction(ctx.User, issue, form.Content)
 | 
							reaction, err := models.CreateIssueReaction(ctx.User, issue, form.Content)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
 | 
								if models.IsErrForbiddenIssueReaction(err) {
 | 
				
			||||||
 | 
									ctx.ServerError("ChangeIssueReaction", err)
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
			log.Info("CreateIssueReaction: %s", err)
 | 
								log.Info("CreateIssueReaction: %s", err)
 | 
				
			||||||
			break
 | 
								break
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -1564,14 +1562,12 @@ func ChangeCommentReaction(ctx *context.Context, form auth.ReactionForm) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	switch ctx.Params(":action") {
 | 
						switch ctx.Params(":action") {
 | 
				
			||||||
	case "react":
 | 
						case "react":
 | 
				
			||||||
		if !util.IsStringInSlice(form.Content, setting.UI.Reactions) {
 | 
					 | 
				
			||||||
			err := fmt.Errorf("ChangeIssueReaction: '%s' is not an allowed reaction", form.Content)
 | 
					 | 
				
			||||||
			ctx.ServerError(err.Error(), err)
 | 
					 | 
				
			||||||
			return
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		reaction, err := models.CreateCommentReaction(ctx.User, comment.Issue, comment, form.Content)
 | 
							reaction, err := models.CreateCommentReaction(ctx.User, comment.Issue, comment, form.Content)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
 | 
								if models.IsErrForbiddenIssueReaction(err) {
 | 
				
			||||||
 | 
									ctx.ServerError("ChangeIssueReaction", err)
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
			log.Info("CreateCommentReaction: %s", err)
 | 
								log.Info("CreateCommentReaction: %s", err)
 | 
				
			||||||
			break
 | 
								break
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3016,6 +3016,148 @@
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "/repos/{owner}/{repo}/issues/comments/{id}/reactions": {
 | 
				
			||||||
 | 
					      "get": {
 | 
				
			||||||
 | 
					        "consumes": [
 | 
				
			||||||
 | 
					          "application/json"
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "produces": [
 | 
				
			||||||
 | 
					          "application/json"
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "tags": [
 | 
				
			||||||
 | 
					          "issue"
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "summary": "Get a list reactions of a issue comment",
 | 
				
			||||||
 | 
					        "operationId": "issueGetCommentReactions",
 | 
				
			||||||
 | 
					        "parameters": [
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "type": "string",
 | 
				
			||||||
 | 
					            "description": "owner of the repo",
 | 
				
			||||||
 | 
					            "name": "owner",
 | 
				
			||||||
 | 
					            "in": "path",
 | 
				
			||||||
 | 
					            "required": true
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "type": "string",
 | 
				
			||||||
 | 
					            "description": "name of the repo",
 | 
				
			||||||
 | 
					            "name": "repo",
 | 
				
			||||||
 | 
					            "in": "path",
 | 
				
			||||||
 | 
					            "required": true
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "type": "integer",
 | 
				
			||||||
 | 
					            "format": "int64",
 | 
				
			||||||
 | 
					            "description": "id of the comment to edit",
 | 
				
			||||||
 | 
					            "name": "id",
 | 
				
			||||||
 | 
					            "in": "path",
 | 
				
			||||||
 | 
					            "required": true
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "responses": {
 | 
				
			||||||
 | 
					          "200": {
 | 
				
			||||||
 | 
					            "$ref": "#/responses/ReactionResponseList"
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "post": {
 | 
				
			||||||
 | 
					        "consumes": [
 | 
				
			||||||
 | 
					          "application/json"
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "produces": [
 | 
				
			||||||
 | 
					          "application/json"
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "tags": [
 | 
				
			||||||
 | 
					          "issue"
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "summary": "Add a reaction to a comment of a issue comment",
 | 
				
			||||||
 | 
					        "operationId": "issuePostCommentReaction",
 | 
				
			||||||
 | 
					        "parameters": [
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "type": "string",
 | 
				
			||||||
 | 
					            "description": "owner of the repo",
 | 
				
			||||||
 | 
					            "name": "owner",
 | 
				
			||||||
 | 
					            "in": "path",
 | 
				
			||||||
 | 
					            "required": true
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "type": "string",
 | 
				
			||||||
 | 
					            "description": "name of the repo",
 | 
				
			||||||
 | 
					            "name": "repo",
 | 
				
			||||||
 | 
					            "in": "path",
 | 
				
			||||||
 | 
					            "required": true
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "type": "integer",
 | 
				
			||||||
 | 
					            "format": "int64",
 | 
				
			||||||
 | 
					            "description": "id of the comment to edit",
 | 
				
			||||||
 | 
					            "name": "id",
 | 
				
			||||||
 | 
					            "in": "path",
 | 
				
			||||||
 | 
					            "required": true
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "name": "content",
 | 
				
			||||||
 | 
					            "in": "body",
 | 
				
			||||||
 | 
					            "schema": {
 | 
				
			||||||
 | 
					              "$ref": "#/definitions/EditReactionOption"
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "responses": {
 | 
				
			||||||
 | 
					          "201": {
 | 
				
			||||||
 | 
					            "$ref": "#/responses/ReactionResponse"
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "delete": {
 | 
				
			||||||
 | 
					        "consumes": [
 | 
				
			||||||
 | 
					          "application/json"
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "produces": [
 | 
				
			||||||
 | 
					          "application/json"
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "tags": [
 | 
				
			||||||
 | 
					          "issue"
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "summary": "Remove a reaction from a comment of a issue comment",
 | 
				
			||||||
 | 
					        "operationId": "issueDeleteCommentReaction",
 | 
				
			||||||
 | 
					        "parameters": [
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "type": "string",
 | 
				
			||||||
 | 
					            "description": "owner of the repo",
 | 
				
			||||||
 | 
					            "name": "owner",
 | 
				
			||||||
 | 
					            "in": "path",
 | 
				
			||||||
 | 
					            "required": true
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "type": "string",
 | 
				
			||||||
 | 
					            "description": "name of the repo",
 | 
				
			||||||
 | 
					            "name": "repo",
 | 
				
			||||||
 | 
					            "in": "path",
 | 
				
			||||||
 | 
					            "required": true
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "type": "integer",
 | 
				
			||||||
 | 
					            "format": "int64",
 | 
				
			||||||
 | 
					            "description": "id of the comment to edit",
 | 
				
			||||||
 | 
					            "name": "id",
 | 
				
			||||||
 | 
					            "in": "path",
 | 
				
			||||||
 | 
					            "required": true
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "name": "content",
 | 
				
			||||||
 | 
					            "in": "body",
 | 
				
			||||||
 | 
					            "schema": {
 | 
				
			||||||
 | 
					              "$ref": "#/definitions/EditReactionOption"
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "responses": {
 | 
				
			||||||
 | 
					          "200": {
 | 
				
			||||||
 | 
					            "$ref": "#/responses/empty"
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "/repos/{owner}/{repo}/issues/{id}/times": {
 | 
					    "/repos/{owner}/{repo}/issues/{id}/times": {
 | 
				
			||||||
      "get": {
 | 
					      "get": {
 | 
				
			||||||
        "produces": [
 | 
					        "produces": [
 | 
				
			||||||
@@ -3688,6 +3830,148 @@
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "/repos/{owner}/{repo}/issues/{index}/reactions": {
 | 
				
			||||||
 | 
					      "get": {
 | 
				
			||||||
 | 
					        "consumes": [
 | 
				
			||||||
 | 
					          "application/json"
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "produces": [
 | 
				
			||||||
 | 
					          "application/json"
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "tags": [
 | 
				
			||||||
 | 
					          "issue"
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "summary": "Get a list reactions of a issue",
 | 
				
			||||||
 | 
					        "operationId": "issueGetIssueReactions",
 | 
				
			||||||
 | 
					        "parameters": [
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "type": "string",
 | 
				
			||||||
 | 
					            "description": "owner of the repo",
 | 
				
			||||||
 | 
					            "name": "owner",
 | 
				
			||||||
 | 
					            "in": "path",
 | 
				
			||||||
 | 
					            "required": true
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "type": "string",
 | 
				
			||||||
 | 
					            "description": "name of the repo",
 | 
				
			||||||
 | 
					            "name": "repo",
 | 
				
			||||||
 | 
					            "in": "path",
 | 
				
			||||||
 | 
					            "required": true
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "type": "integer",
 | 
				
			||||||
 | 
					            "format": "int64",
 | 
				
			||||||
 | 
					            "description": "index of the issue",
 | 
				
			||||||
 | 
					            "name": "index",
 | 
				
			||||||
 | 
					            "in": "path",
 | 
				
			||||||
 | 
					            "required": true
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "responses": {
 | 
				
			||||||
 | 
					          "200": {
 | 
				
			||||||
 | 
					            "$ref": "#/responses/ReactionResponseList"
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "post": {
 | 
				
			||||||
 | 
					        "consumes": [
 | 
				
			||||||
 | 
					          "application/json"
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "produces": [
 | 
				
			||||||
 | 
					          "application/json"
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "tags": [
 | 
				
			||||||
 | 
					          "issue"
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "summary": "Add a reaction to a comment of a issue",
 | 
				
			||||||
 | 
					        "operationId": "issuePostIssueReaction",
 | 
				
			||||||
 | 
					        "parameters": [
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "type": "string",
 | 
				
			||||||
 | 
					            "description": "owner of the repo",
 | 
				
			||||||
 | 
					            "name": "owner",
 | 
				
			||||||
 | 
					            "in": "path",
 | 
				
			||||||
 | 
					            "required": true
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "type": "string",
 | 
				
			||||||
 | 
					            "description": "name of the repo",
 | 
				
			||||||
 | 
					            "name": "repo",
 | 
				
			||||||
 | 
					            "in": "path",
 | 
				
			||||||
 | 
					            "required": true
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "type": "integer",
 | 
				
			||||||
 | 
					            "format": "int64",
 | 
				
			||||||
 | 
					            "description": "index of the issue",
 | 
				
			||||||
 | 
					            "name": "index",
 | 
				
			||||||
 | 
					            "in": "path",
 | 
				
			||||||
 | 
					            "required": true
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "name": "content",
 | 
				
			||||||
 | 
					            "in": "body",
 | 
				
			||||||
 | 
					            "schema": {
 | 
				
			||||||
 | 
					              "$ref": "#/definitions/EditReactionOption"
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "responses": {
 | 
				
			||||||
 | 
					          "201": {
 | 
				
			||||||
 | 
					            "$ref": "#/responses/ReactionResponse"
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "delete": {
 | 
				
			||||||
 | 
					        "consumes": [
 | 
				
			||||||
 | 
					          "application/json"
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "produces": [
 | 
				
			||||||
 | 
					          "application/json"
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "tags": [
 | 
				
			||||||
 | 
					          "issue"
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "summary": "Remove a reaction from a comment of a issue",
 | 
				
			||||||
 | 
					        "operationId": "issueDeleteIssueReaction",
 | 
				
			||||||
 | 
					        "parameters": [
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "type": "string",
 | 
				
			||||||
 | 
					            "description": "owner of the repo",
 | 
				
			||||||
 | 
					            "name": "owner",
 | 
				
			||||||
 | 
					            "in": "path",
 | 
				
			||||||
 | 
					            "required": true
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "type": "string",
 | 
				
			||||||
 | 
					            "description": "name of the repo",
 | 
				
			||||||
 | 
					            "name": "repo",
 | 
				
			||||||
 | 
					            "in": "path",
 | 
				
			||||||
 | 
					            "required": true
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "type": "integer",
 | 
				
			||||||
 | 
					            "format": "int64",
 | 
				
			||||||
 | 
					            "description": "index of the issue",
 | 
				
			||||||
 | 
					            "name": "index",
 | 
				
			||||||
 | 
					            "in": "path",
 | 
				
			||||||
 | 
					            "required": true
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "name": "content",
 | 
				
			||||||
 | 
					            "in": "body",
 | 
				
			||||||
 | 
					            "schema": {
 | 
				
			||||||
 | 
					              "$ref": "#/definitions/EditReactionOption"
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "responses": {
 | 
				
			||||||
 | 
					          "200": {
 | 
				
			||||||
 | 
					            "$ref": "#/responses/empty"
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "/repos/{owner}/{repo}/issues/{index}/stopwatch/start": {
 | 
					    "/repos/{owner}/{repo}/issues/{index}/stopwatch/start": {
 | 
				
			||||||
      "post": {
 | 
					      "post": {
 | 
				
			||||||
        "consumes": [
 | 
					        "consumes": [
 | 
				
			||||||
@@ -8721,6 +9005,17 @@
 | 
				
			|||||||
      },
 | 
					      },
 | 
				
			||||||
      "x-go-package": "code.gitea.io/gitea/modules/structs"
 | 
					      "x-go-package": "code.gitea.io/gitea/modules/structs"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "EditReactionOption": {
 | 
				
			||||||
 | 
					      "description": "EditReactionOption contain the reaction type",
 | 
				
			||||||
 | 
					      "type": "object",
 | 
				
			||||||
 | 
					      "properties": {
 | 
				
			||||||
 | 
					        "content": {
 | 
				
			||||||
 | 
					          "type": "string",
 | 
				
			||||||
 | 
					          "x-go-name": "Reaction"
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "x-go-package": "code.gitea.io/gitea/modules/structs"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "EditReleaseOption": {
 | 
					    "EditReleaseOption": {
 | 
				
			||||||
      "description": "EditReleaseOption options when editing a release",
 | 
					      "description": "EditReleaseOption options when editing a release",
 | 
				
			||||||
      "type": "object",
 | 
					      "type": "object",
 | 
				
			||||||
@@ -10095,6 +10390,25 @@
 | 
				
			|||||||
      },
 | 
					      },
 | 
				
			||||||
      "x-go-package": "code.gitea.io/gitea/modules/structs"
 | 
					      "x-go-package": "code.gitea.io/gitea/modules/structs"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "ReactionResponse": {
 | 
				
			||||||
 | 
					      "description": "ReactionResponse contain one reaction",
 | 
				
			||||||
 | 
					      "type": "object",
 | 
				
			||||||
 | 
					      "properties": {
 | 
				
			||||||
 | 
					        "content": {
 | 
				
			||||||
 | 
					          "type": "string",
 | 
				
			||||||
 | 
					          "x-go-name": "Reaction"
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "created_at": {
 | 
				
			||||||
 | 
					          "type": "string",
 | 
				
			||||||
 | 
					          "format": "date-time",
 | 
				
			||||||
 | 
					          "x-go-name": "Created"
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "user": {
 | 
				
			||||||
 | 
					          "$ref": "#/definitions/User"
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "x-go-package": "code.gitea.io/gitea/modules/structs"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "Reference": {
 | 
					    "Reference": {
 | 
				
			||||||
      "type": "object",
 | 
					      "type": "object",
 | 
				
			||||||
      "title": "Reference represents a Git reference.",
 | 
					      "title": "Reference represents a Git reference.",
 | 
				
			||||||
@@ -10960,6 +11274,12 @@
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "EditReactionOption": {
 | 
				
			||||||
 | 
					      "description": "EditReactionOption",
 | 
				
			||||||
 | 
					      "schema": {
 | 
				
			||||||
 | 
					        "$ref": "#/definitions/EditReactionOption"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "EmailList": {
 | 
					    "EmailList": {
 | 
				
			||||||
      "description": "EmailList",
 | 
					      "description": "EmailList",
 | 
				
			||||||
      "schema": {
 | 
					      "schema": {
 | 
				
			||||||
@@ -11146,6 +11466,21 @@
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "ReactionResponse": {
 | 
				
			||||||
 | 
					      "description": "ReactionResponse",
 | 
				
			||||||
 | 
					      "schema": {
 | 
				
			||||||
 | 
					        "$ref": "#/definitions/ReactionResponse"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "ReactionResponseList": {
 | 
				
			||||||
 | 
					      "description": "ReactionResponseList",
 | 
				
			||||||
 | 
					      "schema": {
 | 
				
			||||||
 | 
					        "type": "array",
 | 
				
			||||||
 | 
					        "items": {
 | 
				
			||||||
 | 
					          "$ref": "#/definitions/ReactionResponse"
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "Reference": {
 | 
					    "Reference": {
 | 
				
			||||||
      "description": "Reference",
 | 
					      "description": "Reference",
 | 
				
			||||||
      "schema": {
 | 
					      "schema": {
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user