[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:
parent
ee7df7ba8c
commit
37e10d4543
12 changed files with 1048 additions and 31 deletions
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() {
|
||||||
Patch(mustNotBeArchived, bind(api.EditIssueCommentOption{}), repo.EditIssueComment).
|
m.Combo("", reqToken()).
|
||||||
Delete(repo.DeleteIssueComment)
|
Patch(mustNotBeArchived, bind(api.EditIssueCommentOption{}), repo.EditIssueComment).
|
||||||
|
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": {
|
||||||
|
|
Loading…
Reference in a new issue