mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-31 21:28:11 +09:00 
			
		
		
		
	Add API to get issue/pull comments and events (timeline) (#17403)
* Add API to get issue/pull comments and events (timeline) Adds an API to get both comments and events in one endpoint with all required data. Closes go-gitea/gitea#13250 * Fix swagger * Don't show code comments (use review api instead) * fmt * Fix comment * Time -> TrackedTime * Use var directly * Add logger * Fix lint * Fix test * Add comments * fmt * [test] get issue directly by ID * Update test * Add description for changed refs * Fix build issues + lint * Fix build * Use string enums * Update swagger * Support `page` and `limit` params * fmt + swagger * Use global slices Co-authored-by: zeripath <art27@cantab.net> Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
This commit is contained in:
		| @@ -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 convert | ||||
|  | ||||
| import ( | ||||
| 	"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": { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user