Changes
9 changed files (+577/-0)
-
-
@@ -180,3 +180,25 @@ func TestAPIDeleteComment(t *testing.T) {unittest.AssertNotExistsBean(t, &models.Comment{ID: comment.ID}) } func TestAPIListIssueTimeline(t *testing.T) { defer prepareTestEnv(t)() // load comment issue := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: 1}).(*models.Issue) repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID}).(*repo_model.Repository) repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User) // make request session := loginUser(t, repoOwner.Name) req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/issues/%d/timeline", repoOwner.Name, repo.Name, issue.Index) resp := session.MakeRequest(t, req, http.StatusOK) // check if lens of list returned by API and // lists extracted directly from DB are the same var comments []*api.TimelineComment DecodeJSON(t, resp, &comments) expectedCount := unittest.GetCount(t, &models.Comment{IssueID: issue.ID}) assert.EqualValues(t, expectedCount, len(comments)) }
-
-
-
@@ -110,6 +110,47 @@ const (CommentTypeChangeIssueRef ) var commentStrings = []string{ "comment", "reopen", "close", "issue_ref", "commit_ref", "comment_ref", "pull_ref", "label", "milestone", "assignees", "change_title", "delete_branch", "start_tracking", "stop_tracking", "add_time_manual", "cancel_tracking", "added_deadline", "modified_deadline", "removed_deadline", "add_dependency", "remove_dependency", "code", "review", "lock", "unlock", "change_target_branch", "delete_time_manual", "review_request", "merge_pull", "pull_push", "project", "project_board", "dismiss_review", "change_issue_ref", } func (t CommentType) String() string { return commentStrings[t] } // RoleDescriptor defines comment tag type type RoleDescriptor int
-
-
-
@@ -6,6 +6,9 @@ package convertimport ( "code.gitea.io/gitea/models" repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/log" api "code.gitea.io/gitea/modules/structs" )
-
@@ -22,3 +25,143 @@ func ToComment(c *models.Comment) *api.Comment {Updated: c.UpdatedUnix.AsTime(), } } // ToTimelineComment converts a models.Comment to the api.TimelineComment format func ToTimelineComment(c *models.Comment, doer *user_model.User) *api.TimelineComment { err := c.LoadMilestone() if err != nil { log.Error("LoadMilestone: %v", err) return nil } err = c.LoadAssigneeUserAndTeam() if err != nil { log.Error("LoadAssigneeUserAndTeam: %v", err) return nil } err = c.LoadResolveDoer() if err != nil { log.Error("LoadResolveDoer: %v", err) return nil } err = c.LoadDepIssueDetails() if err != nil { log.Error("LoadDepIssueDetails: %v", err) return nil } err = c.LoadTime() if err != nil { log.Error("LoadTime: %v", err) return nil } err = c.LoadLabel() if err != nil { log.Error("LoadLabel: %v", err) return nil } comment := &api.TimelineComment{ ID: c.ID, Type: c.Type.String(), Poster: ToUser(c.Poster, nil), HTMLURL: c.HTMLURL(), IssueURL: c.IssueURL(), PRURL: c.PRURL(), Body: c.Content, Created: c.CreatedUnix.AsTime(), Updated: c.UpdatedUnix.AsTime(), OldProjectID: c.OldProjectID, ProjectID: c.ProjectID, OldTitle: c.OldTitle, NewTitle: c.NewTitle, OldRef: c.OldRef, NewRef: c.NewRef, RefAction: c.RefAction.String(), RefCommitSHA: c.CommitSHA, ReviewID: c.ReviewID, RemovedAssignee: c.RemovedAssignee, } if c.OldMilestone != nil { comment.OldMilestone = ToAPIMilestone(c.OldMilestone) } if c.Milestone != nil { comment.Milestone = ToAPIMilestone(c.Milestone) } if c.Time != nil { comment.TrackedTime = ToTrackedTime(c.Time) } if c.RefIssueID != 0 { issue, err := models.GetIssueByID(c.RefIssueID) if err != nil { log.Error("GetIssueByID(%d): %v", c.RefIssueID, err) return nil } comment.RefIssue = ToAPIIssue(issue) } if c.RefCommentID != 0 { com, err := models.GetCommentByID(c.RefCommentID) if err != nil { log.Error("GetCommentByID(%d): %v", c.RefCommentID, err) return nil } err = com.LoadPoster() if err != nil { log.Error("LoadPoster: %v", err) return nil } comment.RefComment = ToComment(com) } if c.Label != nil { var org *user_model.User var repo *repo_model.Repository if c.Label.BelongsToOrg() { var err error org, err = user_model.GetUserByID(c.Label.OrgID) if err != nil { log.Error("GetUserByID(%d): %v", c.Label.OrgID, err) return nil } } if c.Label.BelongsToRepo() { var err error repo, err = repo_model.GetRepositoryByID(c.Label.RepoID) if err != nil { log.Error("GetRepositoryByID(%d): %v", c.Label.RepoID, err) return nil } } comment.Label = ToLabel(c.Label, repo, org) } if c.Assignee != nil { comment.Assignee = ToUser(c.Assignee, nil) } if c.AssigneeTeam != nil { comment.AssigneeTeam = ToTeam(c.AssigneeTeam) } if c.ResolveDoer != nil { comment.ResolveDoer = ToUser(c.ResolveDoer, nil) } if c.DependentIssue != nil { comment.DependentIssue = ToAPIIssue(c.DependentIssue) } return comment }
-
-
-
@@ -49,6 +49,13 @@ var (giteaHostInit sync.Once giteaHost string giteaIssuePullPattern *regexp.Regexp actionStrings = []string{ "none", "closes", "reopens", "neutered", } ) // XRefAction represents the kind of effect a cross reference has once is resolved
-
@@ -65,6 +72,10 @@ const (XRefActionNeutered // 3 ) func (a XRefAction) String() string { return actionStrings[a] } // IssueReference contains an unverified cross-reference to a local issue or pull request type IssueReference struct { Index int64
-
-
-
@@ -35,3 +35,48 @@ type EditIssueCommentOption struct {// required: true Body string `json:"body" binding:"Required"` } // TimelineComment represents a timeline comment (comment of any type) on a commit or issue type TimelineComment struct { ID int64 `json:"id"` Type string `json:"type"` HTMLURL string `json:"html_url"` PRURL string `json:"pull_request_url"` IssueURL string `json:"issue_url"` Poster *User `json:"user"` Body string `json:"body"` // swagger:strfmt date-time Created time.Time `json:"created_at"` // swagger:strfmt date-time Updated time.Time `json:"updated_at"` OldProjectID int64 `json:"old_project_id"` ProjectID int64 `json:"project_id"` OldMilestone *Milestone `json:"old_milestone"` Milestone *Milestone `json:"milestone"` TrackedTime *TrackedTime `json:"tracked_time"` OldTitle string `json:"old_title"` NewTitle string `json:"new_title"` OldRef string `json:"old_ref"` NewRef string `json:"new_ref"` RefIssue *Issue `json:"ref_issue"` RefComment *Comment `json:"ref_comment"` RefAction string `json:"ref_action"` // commit SHA where issue/PR was referenced RefCommitSHA string `json:"ref_commit_sha"` ReviewID int64 `json:"review_id"` Label *Label `json:"label"` Assignee *User `json:"assignee"` AssigneeTeam *Team `json:"assignee_team"` // whether the assignees were removed or added RemovedAssignee bool `json:"removed_assignee"` ResolveDoer *User `json:"resolve_doer"` DependentIssue *Issue `json:"dependent_issue"` }
-
-
-
@@ -842,6 +842,7 @@ func Routes(sessioner func(http.Handler) http.Handler) *web.Route {m.Combo("/{id}", reqToken()).Patch(bind(api.EditIssueCommentOption{}), repo.EditIssueCommentDeprecated). Delete(repo.DeleteIssueCommentDeprecated) }) m.Get("/timeline", repo.ListIssueCommentsAndTimeline) m.Group("/labels", func() { m.Combo("").Get(repo.ListIssueLabels). Post(reqToken(), bind(api.IssueLabelsOption{}), repo.AddIssueLabels).
-
-
-
@@ -10,6 +10,8 @@ import ("net/http" "code.gitea.io/gitea/models" repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/convert" api "code.gitea.io/gitea/modules/structs"
-
@@ -102,6 +104,115 @@ func ListIssueComments(ctx *context.APIContext) {ctx.JSON(http.StatusOK, &apiComments) } // ListIssueCommentsAndTimeline list all the comments and events of an issue func ListIssueCommentsAndTimeline(ctx *context.APIContext) { // swagger:operation GET /repos/{owner}/{repo}/issues/{index}/timeline issue issueGetCommentsAndTimeline // --- // summary: List all comments and events on an issue // 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: since // in: query // description: if provided, only comments updated since the specified time are returned. // type: string // format: date-time // - name: page // in: query // description: page number of results to return (1-based) // type: integer // - name: limit // in: query // description: page size of results // type: integer // - name: before // in: query // description: if provided, only comments updated before the provided time are returned. // type: string // format: date-time // responses: // "200": // "$ref": "#/responses/TimelineList" before, since, err := utils.GetQueryBeforeSince(ctx) if err != nil { ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err) return } issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index")) if err != nil { ctx.Error(http.StatusInternalServerError, "GetRawIssueByIndex", err) return } issue.Repo = ctx.Repo.Repository opts := &models.FindCommentsOptions{ ListOptions: utils.GetListOptions(ctx), IssueID: issue.ID, Since: since, Before: before, Type: models.CommentTypeUnknown, } comments, err := models.FindComments(opts) if err != nil { ctx.Error(http.StatusInternalServerError, "FindComments", err) return } if err := models.CommentList(comments).LoadPosters(); err != nil { ctx.Error(http.StatusInternalServerError, "LoadPosters", err) return } var apiComments []*api.TimelineComment for _, comment := range comments { if comment.Type != models.CommentTypeCode && isXRefCommentAccessible(ctx.User, comment, issue.RepoID) { comment.Issue = issue apiComments = append(apiComments, convert.ToTimelineComment(comment, ctx.User)) } } ctx.SetTotalCountHeader(int64(len(apiComments))) ctx.JSON(http.StatusOK, &apiComments) } func isXRefCommentAccessible(user *user_model.User, c *models.Comment, issueRepoID int64) bool { // Remove comments that the user has no permissions to see if models.CommentTypeIsRef(c.Type) && c.RefRepoID != issueRepoID && c.RefRepoID != 0 { var err error // Set RefRepo for description in template c.RefRepo, err = repo_model.GetRepositoryByID(c.RefRepoID) if err != nil { return false } perm, err := models.GetUserRepoPermission(c.RefRepo, user) if err != nil { return false } if !perm.CanReadIssuesOrPulls(c.RefIsPull) { return false } } return true } // ListRepoIssueComments returns all issue-comments for a repo func ListRepoIssueComments(ctx *context.APIContext) { // swagger:operation GET /repos/{owner}/{repo}/issues/comments issue issueGetRepoComments
-
-
-
@@ -36,6 +36,13 @@ type swaggerResponseCommentList struct {Body []api.Comment `json:"body"` } // TimelineList // swagger:response TimelineList type swaggerResponseTimelineList struct { // in:body Body []api.TimelineComment `json:"body"` } // Label // swagger:response Label type swaggerResponseLabel struct {
-
-
-
@@ -6057,6 +6057,73 @@} } }, "/repos/{owner}/{repo}/issues/{index}/timeline": { "get": { "produces": [ "application/json" ], "tags": [ "issue" ], "summary": "List all comments and events on an issue", "operationId": "issueGetCommentsAndTimeline", "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 }, { "type": "string", "format": "date-time", "description": "if provided, only comments updated since the specified time are returned.", "name": "since", "in": "query" }, { "type": "integer", "description": "page number of results to return (1-based)", "name": "page", "in": "query" }, { "type": "integer", "description": "page size of results", "name": "limit", "in": "query" }, { "type": "string", "format": "date-time", "description": "if provided, only comments updated before the provided time are returned.", "name": "before", "in": "query" } ], "responses": { "200": { "$ref": "#/responses/TimelineList" } } } }, "/repos/{owner}/{repo}/issues/{index}/times": { "get": { "produces": [
-
@@ -17396,6 +17463,126 @@"format": "int64", "x-go-package": "code.gitea.io/gitea/modules/timeutil" }, "TimelineComment": { "description": "TimelineComment represents a timeline comment (comment of any type) on a commit or issue", "type": "object", "properties": { "assignee": { "$ref": "#/definitions/User" }, "assignee_team": { "$ref": "#/definitions/Team" }, "body": { "type": "string", "x-go-name": "Body" }, "created_at": { "type": "string", "format": "date-time", "x-go-name": "Created" }, "dependent_issue": { "$ref": "#/definitions/Issue" }, "html_url": { "type": "string", "x-go-name": "HTMLURL" }, "id": { "type": "integer", "format": "int64", "x-go-name": "ID" }, "issue_url": { "type": "string", "x-go-name": "IssueURL" }, "label": { "$ref": "#/definitions/Label" }, "milestone": { "$ref": "#/definitions/Milestone" }, "new_ref": { "type": "string", "x-go-name": "NewRef" }, "new_title": { "type": "string", "x-go-name": "NewTitle" }, "old_milestone": { "$ref": "#/definitions/Milestone" }, "old_project_id": { "type": "integer", "format": "int64", "x-go-name": "OldProjectID" }, "old_ref": { "type": "string", "x-go-name": "OldRef" }, "old_title": { "type": "string", "x-go-name": "OldTitle" }, "project_id": { "type": "integer", "format": "int64", "x-go-name": "ProjectID" }, "pull_request_url": { "type": "string", "x-go-name": "PRURL" }, "ref_action": { "type": "string", "x-go-name": "RefAction" }, "ref_comment": { "$ref": "#/definitions/Comment" }, "ref_commit_sha": { "description": "commit SHA where issue/PR was referenced", "type": "string", "x-go-name": "RefCommitSHA" }, "ref_issue": { "$ref": "#/definitions/Issue" }, "removed_assignee": { "description": "whether the assignees were removed or added", "type": "boolean", "x-go-name": "RemovedAssignee" }, "resolve_doer": { "$ref": "#/definitions/User" }, "review_id": { "type": "integer", "format": "int64", "x-go-name": "ReviewID" }, "tracked_time": { "$ref": "#/definitions/TrackedTime" }, "type": { "type": "string", "x-go-name": "Type" }, "updated_at": { "type": "string", "format": "date-time", "x-go-name": "Updated" }, "user": { "$ref": "#/definitions/User" } }, "x-go-package": "code.gitea.io/gitea/modules/structs" }, "TopicName": { "description": "TopicName a list of repo topic names", "type": "object",
-
@@ -18525,6 +18712,15 @@} } }, "TimelineList": { "description": "TimelineList", "schema": { "type": "array", "items": { "$ref": "#/definitions/TimelineComment" } } }, "TopicListResponse": { "description": "TopicListResponse", "schema": {
-