Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion models/packages/package_file.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ func TryInsertFile(ctx context.Context, pf *PackageFile) (*PackageFile, error) {
// GetFilesByVersionID gets all files of a version
func GetFilesByVersionID(ctx context.Context, versionID int64) ([]*PackageFile, error) {
pfs := make([]*PackageFile, 0, 10)
return pfs, db.GetEngine(ctx).Where("version_id = ?", versionID).Find(&pfs)
return pfs, db.GetEngine(ctx).OrderBy("id ASC").Where("version_id = ?", versionID).Find(&pfs)
}

// GetFileForVersionByID gets a file of a version by id
Expand Down
1 change: 1 addition & 0 deletions routers/api/packages/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,7 @@ func CommonRoutes() *web.Router {
})
}, reqPackageAccess(perm.AccessModeRead))
r.Group("/generic", func() {
r.Get("/{packagename}/list", generic.ListPackageVersions)
r.Group("/{packagename}/{packageversion}", func() {
r.Delete("", reqPackageAccess(perm.AccessModeWrite), generic.DeletePackage)
r.Group("/{filename}", func() {
Expand Down
60 changes: 60 additions & 0 deletions routers/api/packages/generic/generic.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (

packages_model "code.gitea.io/gitea/models/packages"
packages_module "code.gitea.io/gitea/modules/packages"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/routers/api/packages/helper"
"code.gitea.io/gitea/services/context"
packages_service "code.gitea.io/gitea/services/packages"
Expand All @@ -22,11 +23,70 @@ var (
filenameRegex = regexp.MustCompile(`\A[-_+=:;.()\[\]{}~!@#$%^& \w]+\z`)
)

// PackageFileInfo represents information about an existing package file
// swagger:model
type PackageFileInfo struct {
// Name of package file
Name string `json:"name"`
// swagger:strfmt date-time
// Date when package file was created/uploaded
CreatedUnix timeutil.TimeStamp `json:"created"`
}

// PackageInfo represents information about an existing package file
// swagger:model
type PackageInfo struct {
/// Version linked to package information
Version string `json:"version"`
/// Download count for files within version
DownloadCount int64 `json:"downloads"`
/// Files uploaded for package version
Files []PackageFileInfo `json:"files"`
}

func apiError(ctx *context.Context, status int, obj any) {
message := helper.ProcessErrorForUser(ctx, status, obj)
ctx.PlainText(status, message)
}

// ListPackageVersions lists upload versions and their associated files
func ListPackageVersions(ctx *context.Context) {
pvs, err := packages_model.GetVersionsByPackageName(ctx, ctx.Package.Owner.ID, packages_model.TypeGeneric, ctx.PathParam("packagename"))
if err != nil {
apiError(ctx, http.StatusInternalServerError, err)
return
}
if len(pvs) == 0 {
apiError(ctx, http.StatusNotFound, err)
return
}

var info []PackageInfo
for _, pv := range pvs {
packageFiles, err := packages_model.GetFilesByVersionID(ctx, pv.ID)
if err != nil {
apiError(ctx, http.StatusInternalServerError, err)
return
}

var files []PackageFileInfo
for _, file := range packageFiles {
files = append(files, PackageFileInfo{
Name: file.Name,
CreatedUnix: file.CreatedUnix,
})
}

info = append(info, PackageInfo{
Version: pv.Version,
DownloadCount: pv.DownloadCount,
Files: files,
})
}

ctx.JSON(http.StatusOK, info)
}

// DownloadPackageFile serves the specific generic package.
func DownloadPackageFile(ctx *context.Context) {
s, u, pf, err := packages_service.OpenFileForDownloadByPackageNameAndVersion(
Expand Down
31 changes: 31 additions & 0 deletions tests/integration/api_packages_generic_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,11 @@ import (
"code.gitea.io/gitea/models/packages"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/test"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/routers/api/packages/generic"
"code.gitea.io/gitea/tests"

"github.com/stretchr/testify/assert"
Expand All @@ -30,7 +33,9 @@ func TestPackageGeneric(t *testing.T) {
filename := "fi-le_na.me"
content := []byte{1, 2, 3}

timestamp := timeutil.TimeStampNow().AsTime().Unix()
url := fmt.Sprintf("/api/packages/%s/generic/%s/%s", user.Name, packageName, packageVersion)
listURL := fmt.Sprintf("/api/packages/%s/generic/%s/list", user.Name, packageName)

t.Run("Upload", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
Expand Down Expand Up @@ -98,6 +103,32 @@ func TestPackageGeneric(t *testing.T) {
})
})

t.Run("List", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()

req := NewRequest(t, "GET", listURL)
resp := MakeRequest(t, req, http.StatusOK)

var expected []generic.PackageInfo
err := json.Unmarshal(resp.Body.Bytes(), &expected)
assert.NoError(t, err)

assert.Len(t, expected, 1)
assert.Len(t, expected[0].Files, 2)

resPkg := expected[0]
assert.Equal(t, packageVersion, resPkg.Version)
assert.Equal(t, int64(0), resPkg.DownloadCount)

resFile1 := resPkg.Files[0]
assert.Equal(t, filename, resFile1.Name)
assert.LessOrEqual(t, timestamp, resFile1.CreatedUnix)

resFile2 := resPkg.Files[1]
assert.Equal(t, "dummy.bin", resFile2.Name)
assert.LessOrEqual(t, timestamp, resFile2.CreatedUnix)
})

t.Run("Download", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()

Expand Down
12 changes: 11 additions & 1 deletion tests/integration/api_packages_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,15 @@ func TestPackageAccess(t *testing.T) {
MakeRequest(t, req, expectedStatus)
}

listPackage := func(doer, owner *user_model.User, expectedStatus int) {
url := fmt.Sprintf("/api/packages/%s/generic/test-package/list", owner.Name)
req := NewRequest(t, "GET", url)
if doer != nil {
req.AddBasicAuth(doer.Name)
}
MakeRequest(t, req, expectedStatus)
}

type Target struct {
Owner *user_model.User
ExpectedStatus int
Expand Down Expand Up @@ -339,7 +348,7 @@ func TestPackageAccess(t *testing.T) {
}
})

t.Run("Download", func(t *testing.T) {
t.Run("List/Download", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()

cases := []struct {
Expand Down Expand Up @@ -416,6 +425,7 @@ func TestPackageAccess(t *testing.T) {
for _, c := range cases {
for _, target := range c.Targets {
downloadPackage(c.Doer, target.Owner, target.ExpectedStatus)
listPackage(c.Doer, target.Owner, target.ExpectedStatus)
}
}
})
Expand Down