import (
"fmt"
"net/http"
+ "net/url"
"testing"
"code.gitea.io/gitea/models"
"github.com/stretchr/testify/assert"
)
+func TestAPIListReleases(t *testing.T) {
+ defer prepareTestEnv(t)()
+
+ repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
+ user2 := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
+ session := loginUser(t, user2.LowerName)
+ token := getTokenForLoggedInUser(t, session)
+
+ link, _ := url.Parse(fmt.Sprintf("/api/v1/repos/%s/%s/releases", user2.Name, repo.Name))
+ link.RawQuery = url.Values{"token": {token}}.Encode()
+ resp := session.MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK)
+ var apiReleases []*api.Release
+ DecodeJSON(t, resp, &apiReleases)
+ if assert.Len(t, apiReleases, 3) {
+ for _, release := range apiReleases {
+ switch release.ID {
+ case 1:
+ assert.False(t, release.IsDraft)
+ assert.False(t, release.IsPrerelease)
+ case 4:
+ assert.True(t, release.IsDraft)
+ assert.False(t, release.IsPrerelease)
+ case 5:
+ assert.False(t, release.IsDraft)
+ assert.True(t, release.IsPrerelease)
+ default:
+ assert.NoError(t, fmt.Errorf("unexpected release: %v", release))
+ }
+ }
+ }
+
+ // test filter
+ testFilterByLen := func(auth bool, query url.Values, expectedLength int, msgAndArgs ...string) {
+ link.RawQuery = query.Encode()
+ if auth {
+ query.Set("token", token)
+ resp = session.MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK)
+ } else {
+ resp = MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK)
+ }
+ DecodeJSON(t, resp, &apiReleases)
+ assert.Len(t, apiReleases, expectedLength, msgAndArgs)
+ }
+
+ testFilterByLen(false, url.Values{"draft": {"true"}}, 0, "anon should not see drafts")
+ testFilterByLen(true, url.Values{"draft": {"true"}}, 1, "repo owner should see drafts")
+ testFilterByLen(true, url.Values{"draft": {"false"}}, 2, "exclude drafts")
+ testFilterByLen(true, url.Values{"draft": {"false"}, "pre-release": {"false"}}, 1, "exclude drafts and pre-releases")
+ testFilterByLen(true, url.Values{"pre-release": {"true"}}, 1, "only get pre-release")
+ testFilterByLen(true, url.Values{"draft": {"true"}, "pre-release": {"true"}}, 0, "there is no pre-release draft")
+}
+
func createNewReleaseUsingAPI(t *testing.T, session *TestSession, token string, owner *models.User, repo *models.Repository, name, target, title, desc string) *api.Release {
urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/releases?token=%s",
owner.Name, repo.Name, token)
DecodeJSON(t, resp, &repo)
assert.EqualValues(t, 1, repo.ID)
assert.EqualValues(t, "repo1", repo.Name)
- assert.EqualValues(t, 2, repo.Releases)
+ assert.EqualValues(t, 3, repo.Releases)
assert.EqualValues(t, 1, repo.OpenIssues)
assert.EqualValues(t, 3, repo.OpenPulls)
session := loginUser(t, "user2")
createNewRelease(t, session, "/user2/repo1", "v0.0.1", "v0.0.1", false, false)
- checkLatestReleaseAndCount(t, session, "/user2/repo1", "v0.0.1", i18n.Tr("en", "repo.release.stable"), 3)
+ checkLatestReleaseAndCount(t, session, "/user2/repo1", "v0.0.1", i18n.Tr("en", "repo.release.stable"), 4)
}
func TestCreateReleasePreRelease(t *testing.T) {
session := loginUser(t, "user2")
createNewRelease(t, session, "/user2/repo1", "v0.0.1", "v0.0.1", true, false)
- checkLatestReleaseAndCount(t, session, "/user2/repo1", "v0.0.1", i18n.Tr("en", "repo.release.prerelease"), 3)
+ checkLatestReleaseAndCount(t, session, "/user2/repo1", "v0.0.1", i18n.Tr("en", "repo.release.prerelease"), 4)
}
func TestCreateReleaseDraft(t *testing.T) {
session := loginUser(t, "user2")
createNewRelease(t, session, "/user2/repo1", "v0.0.1", "v0.0.1", false, true)
- checkLatestReleaseAndCount(t, session, "/user2/repo1", "v0.0.1", i18n.Tr("en", "repo.release.draft"), 3)
+ checkLatestReleaseAndCount(t, session, "/user2/repo1", "v0.0.1", i18n.Tr("en", "repo.release.draft"), 4)
}
func TestCreateReleasePaging(t *testing.T) {
htmlDoc := NewHTMLParser(t, rsp.Body)
releases := htmlDoc.Find("#release-list li.ui.grid")
- assert.Equal(t, 1, releases.Length())
+ assert.Equal(t, 2, releases.Length())
links := make([]string, 0, 5)
releases.Each(func(i int, s *goquery.Selection) {
links = append(links, link)
})
- assert.EqualValues(t, []string{"/user2/repo1/releases/tag/v1.1"}, links)
+ assert.EqualValues(t, []string{"/user2/repo1/releases/tag/v1.0", "/user2/repo1/releases/tag/v1.1"}, links)
}
func TestViewReleaseListLogin(t *testing.T) {
htmlDoc := NewHTMLParser(t, rsp.Body)
releases := htmlDoc.Find("#release-list li.ui.grid")
- assert.Equal(t, 2, releases.Length())
+ assert.Equal(t, 3, releases.Length())
links := make([]string, 0, 5)
releases.Each(func(i int, s *goquery.Selection) {
links = append(links, link)
})
- assert.EqualValues(t, []string{"/user2/repo1/releases/tag/draft-release",
- "/user2/repo1/releases/tag/v1.1"}, links)
+ assert.EqualValues(t, []string{
+ "/user2/repo1/releases/tag/draft-release",
+ "/user2/repo1/releases/tag/v1.0",
+ "/user2/repo1/releases/tag/v1.1",
+ }, links)
}
func TestViewTagsList(t *testing.T) {
htmlDoc := NewHTMLParser(t, rsp.Body)
tags := htmlDoc.Find(".tag-list tr")
- assert.Equal(t, 2, tags.Length())
+ assert.Equal(t, 3, tags.Length())
tagNames := make([]string, 0, 5)
tags.Each(func(i int, s *goquery.Selection) {
tagNames = append(tagNames, s.Find(".tag a.df.ac").Text())
})
- assert.EqualValues(t, []string{"delete-tag", "v1.1"}, tagNames)
+ assert.EqualValues(t, []string{"v1.0", "delete-tag", "v1.1"}, tagNames)
}
--
- id: 1
+- id: 1
repo_id: 1
publisher_id: 2
tag_name: "v1.1"
is_tag: false
created_unix: 946684800
--
- id: 2
+- id: 2
repo_id: 40
publisher_id: 2
tag_name: "v1.1"
is_tag: false
created_unix: 946684800
--
- id: 3
+- id: 3
repo_id: 1
publisher_id: 2
tag_name: "delete-tag"
is_tag: true
created_unix: 946684800
--
- id: 4
+- id: 4
repo_id: 1
publisher_id: 2
tag_name: "draft-release"
is_prerelease: false
is_tag: false
created_unix: 1619524806
+
+- id: 5
+ repo_id: 1
+ publisher_id: 2
+ tag_name: "v1.0"
+ lower_tag_name: "v1.0"
+ target: "master"
+ title: "pre-release"
+ note: "some text for a pre release"
+ sha1: "65f1bf27bc3bf70f64657658635e66094edbcb4d"
+ num_commits: 1
+ is_draft: false
+ is_prerelease: true
+ is_tag: false
+ created_unix: 946684800
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/timeutil"
+ "code.gitea.io/gitea/modules/util"
"xorm.io/builder"
)
ListOptions
IncludeDrafts bool
IncludeTags bool
+ IsPreRelease util.OptionalBool
+ IsDraft util.OptionalBool
TagNames []string
}
if len(opts.TagNames) > 0 {
cond = cond.And(builder.In("tag_name", opts.TagNames))
}
+ if !opts.IsPreRelease.IsNone() {
+ cond = cond.And(builder.Eq{"is_prerelease": opts.IsPreRelease.IsTrue()})
+ }
+ if !opts.IsDraft.IsNone() {
+ cond = cond.And(builder.Eq{"is_draft": opts.IsDraft.IsTrue()})
+ }
return cond
}
return rels, sess.Find(&rels)
}
+// CountReleasesByRepoID returns a number of releases matching FindReleaseOptions and RepoID.
+func CountReleasesByRepoID(repoID int64, opts FindReleasesOptions) (int64, error) {
+ return x.Where(opts.toConds(repoID)).Count(new(Release))
+}
+
// GetLatestReleaseByRepoID returns the latest release for a repository
func GetLatestReleaseByRepoID(repoID int64) (*Release, error) {
cond := builder.NewCond().
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/templates"
"code.gitea.io/gitea/modules/translation"
+ "code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web/middleware"
"code.gitea.io/gitea/services/auth"
return (*Forms)(ctx.Req).MustBool(key, defaults...)
}
+// QueryOptionalBool returns request form as OptionalBool with default
+func (ctx *Context) QueryOptionalBool(key string, defaults ...util.OptionalBool) util.OptionalBool {
+ return (*Forms)(ctx.Req).MustOptionalBool(key, defaults...)
+}
+
// HandleText handles HTTP status code
func (ctx *Context) HandleText(status int, title string) {
if (status/100 == 4) || (status/100 == 5) {
"text/template"
"code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/util"
)
// Forms a new enhancement of http.Request
}
return v
}
+
+// MustOptionalBool returns request form as OptionalBool with default
+func (f *Forms) MustOptionalBool(key string, defaults ...util.OptionalBool) util.OptionalBool {
+ value := (*http.Request)(f).FormValue(key)
+ if len(value) == 0 {
+ return util.OptionalBoolNone
+ }
+ v, err := strconv.ParseBool((*http.Request)(f).FormValue(key))
+ if len(defaults) > 0 && err != nil {
+ return defaults[0]
+ }
+ return util.OptionalBoolOf(v)
+}
package repo
import (
+ "fmt"
"net/http"
"code.gitea.io/gitea/models"
// description: name of the repo
// type: string
// required: true
+ // - name: draft
+ // in: query
+ // description: filter (exclude / include) drafts, if you dont have repo write access none will show
+ // type: boolean
+ // - name: pre-release
+ // in: query
+ // description: filter (exclude / include) pre-releases
+ // type: boolean
// - name: per_page
// in: query
// description: page size of results, deprecated - use limit
// "200":
// "$ref": "#/responses/ReleaseList"
listOptions := utils.GetListOptions(ctx)
- if ctx.QueryInt("per_page") != 0 {
+ if listOptions.PageSize == 0 && ctx.QueryInt("per_page") != 0 {
listOptions.PageSize = ctx.QueryInt("per_page")
}
- releases, err := models.GetReleasesByRepoID(ctx.Repo.Repository.ID, models.FindReleasesOptions{
+ opts := models.FindReleasesOptions{
ListOptions: listOptions,
IncludeDrafts: ctx.Repo.AccessMode >= models.AccessModeWrite,
IncludeTags: false,
- })
+ IsDraft: ctx.QueryOptionalBool("draft"),
+ IsPreRelease: ctx.QueryOptionalBool("pre-release"),
+ }
+
+ releases, err := models.GetReleasesByRepoID(ctx.Repo.Repository.ID, opts)
if err != nil {
ctx.Error(http.StatusInternalServerError, "GetReleasesByRepoID", err)
return
}
rels[i] = convert.ToRelease(release)
}
+
+ filteredCount, err := models.CountReleasesByRepoID(ctx.Repo.Repository.ID, opts)
+ if err != nil {
+ ctx.InternalServerError(err)
+ return
+ }
+
+ ctx.SetLinkHeader(int(filteredCount), listOptions.PageSize)
+ ctx.Header().Set("X-Total-Count", fmt.Sprint(filteredCount))
+ ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count, Link")
ctx.JSON(http.StatusOK, rels)
}
"in": "path",
"required": true
},
+ {
+ "type": "boolean",
+ "description": "filter (exclude / include) drafts, if you dont have repo write access none will show",
+ "name": "draft",
+ "in": "query"
+ },
+ {
+ "type": "boolean",
+ "description": "filter (exclude / include) pre-releases",
+ "name": "pre-release",
+ "in": "query"
+ },
{
"type": "integer",
"description": "page size of results, deprecated - use limit",