* 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
tags/v1.21.12.1
| @@ -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, user2, issue1, nil, "heart") | |||||
| addReaction(t, user3, issue1, nil, "heart") | |||||
| addReaction(t, user3, issue1, nil, "+1") | |||||
| addReaction(t, user4, issue1, nil, "+1") | |||||
| addReaction(t, user4, issue1, nil, "heart") | |||||
| addReaction(t, ghost, issue1, nil, "-1") | |||||
| err := issue1.loadReactions(x) | |||||
| addReaction(t, user1, issue, nil, "heart") | |||||
| addReaction(t, user2, issue, nil, "heart") | |||||
| addReaction(t, user3, issue, nil, "heart") | |||||
| addReaction(t, user3, issue, nil, "+1") | |||||
| addReaction(t, user4, issue, nil, "+1") | |||||
| addReaction(t, user4, issue, nil, "heart") | |||||
| addReaction(t, ghost, issue, nil, "-1") | |||||
| 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 { | ||||
| @@ -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()). | |||||
| Patch(mustNotBeArchived, bind(api.EditIssueCommentOption{}), repo.EditIssueComment). | |||||
| Delete(repo.DeleteIssueComment) | |||||
| m.Group("/:id", func() { | |||||
| m.Combo("", reqToken()). | |||||
| 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() { | ||||
| @@ -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": { | ||||