Add name filter to API for GetMilestoneList (#12336)
Adds a name filter to the API for GetMilestoneList Includes a small refactor: merge GetMilestones and GetMilestonesByRepoID Close #12260 Needed for https://gitea.com/gitea/go-sdk/issues/383 and https://gitea.com/gitea/tea/pulls/149
This commit is contained in:
		
							parent
							
								
									78cbd0ca72
								
							
						
					
					
						commit
						8bdc9795d8
					
				
					 8 changed files with 115 additions and 36 deletions
				
			
		|  | @ -55,6 +55,18 @@ func TestAPIIssuesMilestone(t *testing.T) { | |||
| 	assert.Equal(t, "wow", apiMilestone.Title) | ||||
| 	assert.Equal(t, structs.StateClosed, apiMilestone.State) | ||||
| 
 | ||||
| 	var apiMilestones []structs.Milestone | ||||
| 	req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/%s/milestones?state=%s&token=%s", owner.Name, repo.Name, "all", token)) | ||||
| 	resp = session.MakeRequest(t, req, http.StatusOK) | ||||
| 	DecodeJSON(t, resp, &apiMilestones) | ||||
| 	assert.Len(t, apiMilestones, 4) | ||||
| 
 | ||||
| 	req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/%s/milestones?state=%s&name=%s&token=%s", owner.Name, repo.Name, "all", "milestone2", token)) | ||||
| 	resp = session.MakeRequest(t, req, http.StatusOK) | ||||
| 	DecodeJSON(t, resp, &apiMilestones) | ||||
| 	assert.Len(t, apiMilestones, 1) | ||||
| 	assert.Equal(t, int64(2), apiMilestones[0].ID) | ||||
| 
 | ||||
| 	req = NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/repos/%s/%s/milestones/%d?token=%s", owner.Name, repo.Name, apiMilestone.ID, token)) | ||||
| 	resp = session.MakeRequest(t, req, http.StatusNoContent) | ||||
| } | ||||
|  |  | |||
|  | @ -330,41 +330,38 @@ func (milestones MilestoneList) getMilestoneIDs() []int64 { | |||
| 	return ids | ||||
| } | ||||
| 
 | ||||
| // GetMilestonesByRepoID returns all opened milestones of a repository.
 | ||||
| func GetMilestonesByRepoID(repoID int64, state api.StateType, listOptions ListOptions) (MilestoneList, error) { | ||||
| 	sess := x.Where("repo_id = ?", repoID) | ||||
| // GetMilestonesOption contain options to get milestones
 | ||||
| type GetMilestonesOption struct { | ||||
| 	ListOptions | ||||
| 	RepoID   int64 | ||||
| 	State    api.StateType | ||||
| 	Name     string | ||||
| 	SortType string | ||||
| } | ||||
| 
 | ||||
| 	switch state { | ||||
| // GetMilestones returns milestones filtered by GetMilestonesOption's
 | ||||
| func GetMilestones(opts GetMilestonesOption) (MilestoneList, error) { | ||||
| 	sess := x.Where("repo_id = ?", opts.RepoID) | ||||
| 
 | ||||
| 	switch opts.State { | ||||
| 	case api.StateClosed: | ||||
| 		sess = sess.And("is_closed = ?", true) | ||||
| 
 | ||||
| 	case api.StateAll: | ||||
| 		break | ||||
| 
 | ||||
| 	case api.StateOpen: | ||||
| 		fallthrough | ||||
| 
 | ||||
| 	// api.StateOpen:
 | ||||
| 	default: | ||||
| 		sess = sess.And("is_closed = ?", false) | ||||
| 	} | ||||
| 
 | ||||
| 	if listOptions.Page != 0 { | ||||
| 		sess = listOptions.setSessionPagination(sess) | ||||
| 	if len(opts.Name) != 0 { | ||||
| 		sess = sess.And(builder.Like{"name", opts.Name}) | ||||
| 	} | ||||
| 
 | ||||
| 	miles := make([]*Milestone, 0, listOptions.PageSize) | ||||
| 	return miles, sess.Asc("deadline_unix").Asc("id").Find(&miles) | ||||
| } | ||||
| 
 | ||||
| // GetMilestones returns a list of milestones of given repository and status.
 | ||||
| func GetMilestones(repoID int64, page int, isClosed bool, sortType string) (MilestoneList, error) { | ||||
| 	miles := make([]*Milestone, 0, setting.UI.IssuePagingNum) | ||||
| 	sess := x.Where("repo_id = ? AND is_closed = ?", repoID, isClosed) | ||||
| 	if page > 0 { | ||||
| 		sess = sess.Limit(setting.UI.IssuePagingNum, (page-1)*setting.UI.IssuePagingNum) | ||||
| 	if opts.Page != 0 { | ||||
| 		sess = opts.setSessionPagination(sess) | ||||
| 	} | ||||
| 
 | ||||
| 	switch sortType { | ||||
| 	switch opts.SortType { | ||||
| 	case "furthestduedate": | ||||
| 		sess.Desc("deadline_unix") | ||||
| 	case "leastcomplete": | ||||
|  | @ -375,9 +372,13 @@ func GetMilestones(repoID int64, page int, isClosed bool, sortType string) (Mile | |||
| 		sess.Asc("num_issues") | ||||
| 	case "mostissues": | ||||
| 		sess.Desc("num_issues") | ||||
| 	case "id": | ||||
| 		sess.Asc("id") | ||||
| 	default: | ||||
| 		sess.Asc("deadline_unix") | ||||
| 		sess.Asc("deadline_unix").Asc("id") | ||||
| 	} | ||||
| 
 | ||||
| 	miles := make([]*Milestone, 0, opts.PageSize) | ||||
| 	return miles, sess.Find(&miles) | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -8,6 +8,7 @@ import ( | |||
| 	"sort" | ||||
| 	"testing" | ||||
| 
 | ||||
| 	"code.gitea.io/gitea/modules/setting" | ||||
| 	api "code.gitea.io/gitea/modules/structs" | ||||
| 	"code.gitea.io/gitea/modules/timeutil" | ||||
| 
 | ||||
|  | @ -49,7 +50,10 @@ func TestGetMilestonesByRepoID(t *testing.T) { | |||
| 	assert.NoError(t, PrepareTestDatabase()) | ||||
| 	test := func(repoID int64, state api.StateType) { | ||||
| 		repo := AssertExistsAndLoadBean(t, &Repository{ID: repoID}).(*Repository) | ||||
| 		milestones, err := GetMilestonesByRepoID(repo.ID, state, ListOptions{}) | ||||
| 		milestones, err := GetMilestones(GetMilestonesOption{ | ||||
| 			RepoID: repo.ID, | ||||
| 			State:  state, | ||||
| 		}) | ||||
| 		assert.NoError(t, err) | ||||
| 
 | ||||
| 		var n int | ||||
|  | @ -83,7 +87,10 @@ func TestGetMilestonesByRepoID(t *testing.T) { | |||
| 	test(3, api.StateClosed) | ||||
| 	test(3, api.StateAll) | ||||
| 
 | ||||
| 	milestones, err := GetMilestonesByRepoID(NonexistentID, api.StateOpen, ListOptions{}) | ||||
| 	milestones, err := GetMilestones(GetMilestonesOption{ | ||||
| 		RepoID: NonexistentID, | ||||
| 		State:  api.StateOpen, | ||||
| 	}) | ||||
| 	assert.NoError(t, err) | ||||
| 	assert.Len(t, milestones, 0) | ||||
| } | ||||
|  | @ -93,7 +100,15 @@ func TestGetMilestones(t *testing.T) { | |||
| 	repo := AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository) | ||||
| 	test := func(sortType string, sortCond func(*Milestone) int) { | ||||
| 		for _, page := range []int{0, 1} { | ||||
| 			milestones, err := GetMilestones(repo.ID, page, false, sortType) | ||||
| 			milestones, err := GetMilestones(GetMilestonesOption{ | ||||
| 				ListOptions: ListOptions{ | ||||
| 					Page:     page, | ||||
| 					PageSize: setting.UI.IssuePagingNum, | ||||
| 				}, | ||||
| 				RepoID:   repo.ID, | ||||
| 				State:    api.StateOpen, | ||||
| 				SortType: sortType, | ||||
| 			}) | ||||
| 			assert.NoError(t, err) | ||||
| 			assert.Len(t, milestones, repo.NumMilestones-repo.NumClosedMilestones) | ||||
| 			values := make([]int, len(milestones)) | ||||
|  | @ -102,7 +117,16 @@ func TestGetMilestones(t *testing.T) { | |||
| 			} | ||||
| 			assert.True(t, sort.IntsAreSorted(values)) | ||||
| 
 | ||||
| 			milestones, err = GetMilestones(repo.ID, page, true, sortType) | ||||
| 			milestones, err = GetMilestones(GetMilestonesOption{ | ||||
| 				ListOptions: ListOptions{ | ||||
| 					Page:     page, | ||||
| 					PageSize: setting.UI.IssuePagingNum, | ||||
| 				}, | ||||
| 				RepoID:   repo.ID, | ||||
| 				State:    api.StateClosed, | ||||
| 				Name:     "", | ||||
| 				SortType: sortType, | ||||
| 			}) | ||||
| 			assert.NoError(t, err) | ||||
| 			assert.Len(t, milestones, repo.NumClosedMilestones) | ||||
| 			values = make([]int, len(milestones)) | ||||
|  |  | |||
|  | @ -51,11 +51,17 @@ func TestGiteaUploadRepo(t *testing.T) { | |||
| 	repo := models.AssertExistsAndLoadBean(t, &models.Repository{OwnerID: user.ID, Name: repoName}).(*models.Repository) | ||||
| 	assert.True(t, repo.HasWiki()) | ||||
| 
 | ||||
| 	milestones, err := models.GetMilestones(repo.ID, 0, false, "") | ||||
| 	milestones, err := models.GetMilestones(models.GetMilestonesOption{ | ||||
| 		RepoID: repo.ID, | ||||
| 		State:  structs.StateOpen, | ||||
| 	}) | ||||
| 	assert.NoError(t, err) | ||||
| 	assert.EqualValues(t, 1, len(milestones)) | ||||
| 
 | ||||
| 	milestones, err = models.GetMilestones(repo.ID, 0, true, "") | ||||
| 	milestones, err = models.GetMilestones(models.GetMilestonesOption{ | ||||
| 		RepoID: repo.ID, | ||||
| 		State:  structs.StateClosed, | ||||
| 	}) | ||||
| 	assert.NoError(t, err) | ||||
| 	assert.EqualValues(t, 0, len(milestones)) | ||||
| 
 | ||||
|  |  | |||
|  | @ -39,6 +39,10 @@ func ListMilestones(ctx *context.APIContext) { | |||
| 	//   in: query
 | ||||
| 	//   description: Milestone state, Recognised values are open, closed and all. Defaults to "open"
 | ||||
| 	//   type: string
 | ||||
| 	// - name: name
 | ||||
| 	//   in: query
 | ||||
| 	//   description: filter by milestone name
 | ||||
| 	//   type: string
 | ||||
| 	// - name: page
 | ||||
| 	//   in: query
 | ||||
| 	//   description: page number of results to return (1-based)
 | ||||
|  | @ -51,9 +55,14 @@ func ListMilestones(ctx *context.APIContext) { | |||
| 	//   "200":
 | ||||
| 	//     "$ref": "#/responses/MilestoneList"
 | ||||
| 
 | ||||
| 	milestones, err := models.GetMilestonesByRepoID(ctx.Repo.Repository.ID, api.StateType(ctx.Query("state")), utils.GetListOptions(ctx)) | ||||
| 	milestones, err := models.GetMilestones(models.GetMilestonesOption{ | ||||
| 		ListOptions: utils.GetListOptions(ctx), | ||||
| 		RepoID:      ctx.Repo.Repository.ID, | ||||
| 		State:       api.StateType(ctx.Query("state")), | ||||
| 		Name:        ctx.Query("name"), | ||||
| 	}) | ||||
| 	if err != nil { | ||||
| 		ctx.Error(http.StatusInternalServerError, "GetMilestonesByRepoID", err) | ||||
| 		ctx.Error(http.StatusInternalServerError, "GetMilestones", err) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
|  |  | |||
|  | @ -360,8 +360,11 @@ func Issues(ctx *context.Context) { | |||
| 	issues(ctx, ctx.QueryInt64("milestone"), util.OptionalBoolOf(isPullList)) | ||||
| 
 | ||||
| 	var err error | ||||
| 	// Get milestones.
 | ||||
| 	ctx.Data["Milestones"], err = models.GetMilestonesByRepoID(ctx.Repo.Repository.ID, api.StateType(ctx.Query("state")), models.ListOptions{}) | ||||
| 	// Get milestones
 | ||||
| 	ctx.Data["Milestones"], err = models.GetMilestones(models.GetMilestonesOption{ | ||||
| 		RepoID: ctx.Repo.Repository.ID, | ||||
| 		State:  api.StateType(ctx.Query("state")), | ||||
| 	}) | ||||
| 	if err != nil { | ||||
| 		ctx.ServerError("GetAllRepoMilestones", err) | ||||
| 		return | ||||
|  | @ -375,12 +378,18 @@ func Issues(ctx *context.Context) { | |||
| // RetrieveRepoMilestonesAndAssignees find all the milestones and assignees of a repository
 | ||||
| func RetrieveRepoMilestonesAndAssignees(ctx *context.Context, repo *models.Repository) { | ||||
| 	var err error | ||||
| 	ctx.Data["OpenMilestones"], err = models.GetMilestones(repo.ID, -1, false, "") | ||||
| 	ctx.Data["OpenMilestones"], err = models.GetMilestones(models.GetMilestonesOption{ | ||||
| 		RepoID: repo.ID, | ||||
| 		State:  api.StateOpen, | ||||
| 	}) | ||||
| 	if err != nil { | ||||
| 		ctx.ServerError("GetMilestones", err) | ||||
| 		return | ||||
| 	} | ||||
| 	ctx.Data["ClosedMilestones"], err = models.GetMilestones(repo.ID, -1, true, "") | ||||
| 	ctx.Data["ClosedMilestones"], err = models.GetMilestones(models.GetMilestonesOption{ | ||||
| 		RepoID: repo.ID, | ||||
| 		State:  api.StateClosed, | ||||
| 	}) | ||||
| 	if err != nil { | ||||
| 		ctx.ServerError("GetMilestones", err) | ||||
| 		return | ||||
|  |  | |||
|  | @ -13,6 +13,7 @@ import ( | |||
| 	"code.gitea.io/gitea/modules/context" | ||||
| 	"code.gitea.io/gitea/modules/markup/markdown" | ||||
| 	"code.gitea.io/gitea/modules/setting" | ||||
| 	"code.gitea.io/gitea/modules/structs" | ||||
| 	"code.gitea.io/gitea/modules/timeutil" | ||||
| 	"code.gitea.io/gitea/modules/util" | ||||
| 
 | ||||
|  | @ -47,13 +48,24 @@ func Milestones(ctx *context.Context) { | |||
| 	} | ||||
| 
 | ||||
| 	var total int | ||||
| 	var state structs.StateType | ||||
| 	if !isShowClosed { | ||||
| 		total = int(stats.OpenCount) | ||||
| 		state = structs.StateOpen | ||||
| 	} else { | ||||
| 		total = int(stats.ClosedCount) | ||||
| 		state = structs.StateClosed | ||||
| 	} | ||||
| 
 | ||||
| 	miles, err := models.GetMilestones(ctx.Repo.Repository.ID, page, isShowClosed, sortType) | ||||
| 	miles, err := models.GetMilestones(models.GetMilestonesOption{ | ||||
| 		ListOptions: models.ListOptions{ | ||||
| 			Page:     page, | ||||
| 			PageSize: setting.UI.IssuePagingNum, | ||||
| 		}, | ||||
| 		RepoID:   ctx.Repo.Repository.ID, | ||||
| 		State:    state, | ||||
| 		SortType: sortType, | ||||
| 	}) | ||||
| 	if err != nil { | ||||
| 		ctx.ServerError("GetMilestones", err) | ||||
| 		return | ||||
|  |  | |||
|  | @ -6164,6 +6164,12 @@ | |||
|             "name": "state", | ||||
|             "in": "query" | ||||
|           }, | ||||
|           { | ||||
|             "type": "string", | ||||
|             "description": "filter by milestone name", | ||||
|             "name": "name", | ||||
|             "in": "query" | ||||
|           }, | ||||
|           { | ||||
|             "type": "integer", | ||||
|             "description": "page number of results to return (1-based)", | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue