mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-11-03 20:36:07 +01:00 
			
		
		
		
	Add API to support link package to repository and unlink it (#33481)
Fix #21062 --------- Co-authored-by: Zettat123 <zettat123@gmail.com>
This commit is contained in:
		@@ -228,6 +228,11 @@ func SetRepositoryLink(ctx context.Context, packageID, repoID int64) error {
 | 
				
			|||||||
	return err
 | 
						return err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func UnlinkRepository(ctx context.Context, packageID int64) error {
 | 
				
			||||||
 | 
						_, err := db.GetEngine(ctx).ID(packageID).Cols("repo_id").Update(&Package{RepoID: 0})
 | 
				
			||||||
 | 
						return err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// UnlinkRepositoryFromAllPackages unlinks every package from the repository
 | 
					// UnlinkRepositoryFromAllPackages unlinks every package from the repository
 | 
				
			||||||
func UnlinkRepositoryFromAllPackages(ctx context.Context, repoID int64) error {
 | 
					func UnlinkRepositoryFromAllPackages(ctx context.Context, repoID int64) error {
 | 
				
			||||||
	_, err := db.GetEngine(ctx).Where("repo_id = ?", repoID).Cols("repo_id").Update(&Package{})
 | 
						_, err := db.GetEngine(ctx).Where("repo_id = ?", repoID).Cols("repo_id").Update(&Package{})
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1537,13 +1537,19 @@ func Routes() *web.Router {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		// NOTE: these are Gitea package management API - see packages.CommonRoutes and packages.DockerContainerRoutes for endpoints that implement package manager APIs
 | 
							// NOTE: these are Gitea package management API - see packages.CommonRoutes and packages.DockerContainerRoutes for endpoints that implement package manager APIs
 | 
				
			||||||
		m.Group("/packages/{username}", func() {
 | 
							m.Group("/packages/{username}", func() {
 | 
				
			||||||
			m.Group("/{type}/{name}/{version}", func() {
 | 
								m.Group("/{type}/{name}", func() {
 | 
				
			||||||
				m.Get("", reqToken(), packages.GetPackage)
 | 
									m.Group("/{version}", func() {
 | 
				
			||||||
				m.Delete("", reqToken(), reqPackageAccess(perm.AccessModeWrite), packages.DeletePackage)
 | 
										m.Get("", packages.GetPackage)
 | 
				
			||||||
				m.Get("/files", reqToken(), packages.ListPackageFiles)
 | 
										m.Delete("", reqPackageAccess(perm.AccessModeWrite), packages.DeletePackage)
 | 
				
			||||||
 | 
										m.Get("/files", packages.ListPackageFiles)
 | 
				
			||||||
				})
 | 
									})
 | 
				
			||||||
			m.Get("/", reqToken(), packages.ListPackages)
 | 
					
 | 
				
			||||||
		}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryPackage), context.UserAssignmentAPI(), context.PackageAssignmentAPI(), reqPackageAccess(perm.AccessModeRead), checkTokenPublicOnly())
 | 
									m.Post("/-/link/{repo_name}", reqPackageAccess(perm.AccessModeWrite), packages.LinkPackage)
 | 
				
			||||||
 | 
									m.Post("/-/unlink", reqPackageAccess(perm.AccessModeWrite), packages.UnlinkPackage)
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								m.Get("/", packages.ListPackages)
 | 
				
			||||||
 | 
							}, reqToken(), tokenRequiresScopes(auth_model.AccessTokenScopeCategoryPackage), context.UserAssignmentAPI(), context.PackageAssignmentAPI(), reqPackageAccess(perm.AccessModeRead), checkTokenPublicOnly())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Organizations
 | 
							// Organizations
 | 
				
			||||||
		m.Get("/user/orgs", reqToken(), tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser, auth_model.AccessTokenScopeCategoryOrganization), org.ListMyOrgs)
 | 
							m.Get("/user/orgs", reqToken(), tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser, auth_model.AccessTokenScopeCategoryOrganization), org.ListMyOrgs)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,11 +4,14 @@
 | 
				
			|||||||
package packages
 | 
					package packages
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/models/packages"
 | 
						"code.gitea.io/gitea/models/packages"
 | 
				
			||||||
 | 
						repo_model "code.gitea.io/gitea/models/repo"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/optional"
 | 
						"code.gitea.io/gitea/modules/optional"
 | 
				
			||||||
	api "code.gitea.io/gitea/modules/structs"
 | 
						api "code.gitea.io/gitea/modules/structs"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/util"
 | 
				
			||||||
	"code.gitea.io/gitea/routers/api/v1/utils"
 | 
						"code.gitea.io/gitea/routers/api/v1/utils"
 | 
				
			||||||
	"code.gitea.io/gitea/services/context"
 | 
						"code.gitea.io/gitea/services/context"
 | 
				
			||||||
	"code.gitea.io/gitea/services/convert"
 | 
						"code.gitea.io/gitea/services/convert"
 | 
				
			||||||
@@ -213,3 +216,122 @@ func ListPackageFiles(ctx *context.APIContext) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	ctx.JSON(http.StatusOK, apiPackageFiles)
 | 
						ctx.JSON(http.StatusOK, apiPackageFiles)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// LinkPackage sets a repository link for a package
 | 
				
			||||||
 | 
					func LinkPackage(ctx *context.APIContext) {
 | 
				
			||||||
 | 
						// swagger:operation POST /packages/{owner}/{type}/{name}/-/link/{repo_name} package linkPackage
 | 
				
			||||||
 | 
						// ---
 | 
				
			||||||
 | 
						// summary: Link a package to a repository
 | 
				
			||||||
 | 
						// parameters:
 | 
				
			||||||
 | 
						// - name: owner
 | 
				
			||||||
 | 
						//   in: path
 | 
				
			||||||
 | 
						//   description: owner of the package
 | 
				
			||||||
 | 
						//   type: string
 | 
				
			||||||
 | 
						//   required: true
 | 
				
			||||||
 | 
						// - name: type
 | 
				
			||||||
 | 
						//   in: path
 | 
				
			||||||
 | 
						//   description: type of the package
 | 
				
			||||||
 | 
						//   type: string
 | 
				
			||||||
 | 
						//   required: true
 | 
				
			||||||
 | 
						// - name: name
 | 
				
			||||||
 | 
						//   in: path
 | 
				
			||||||
 | 
						//   description: name of the package
 | 
				
			||||||
 | 
						//   type: string
 | 
				
			||||||
 | 
						//   required: true
 | 
				
			||||||
 | 
						// - name: repo_name
 | 
				
			||||||
 | 
						//   in: path
 | 
				
			||||||
 | 
						//   description: name of the repository to link.
 | 
				
			||||||
 | 
						//   type: string
 | 
				
			||||||
 | 
						//   required: true
 | 
				
			||||||
 | 
						// responses:
 | 
				
			||||||
 | 
						//   "201":
 | 
				
			||||||
 | 
						//     "$ref": "#/responses/empty"
 | 
				
			||||||
 | 
						//   "404":
 | 
				
			||||||
 | 
						//     "$ref": "#/responses/notFound"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pkg, err := packages.GetPackageByName(ctx, ctx.ContextUser.ID, packages.Type(ctx.PathParam("type")), ctx.PathParam("name"))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							if errors.Is(err, util.ErrNotExist) {
 | 
				
			||||||
 | 
								ctx.Error(http.StatusNotFound, "GetPackageByName", err)
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								ctx.Error(http.StatusInternalServerError, "GetPackageByName", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						repo, err := repo_model.GetRepositoryByName(ctx, ctx.ContextUser.ID, ctx.PathParam("repo_name"))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							if errors.Is(err, util.ErrNotExist) {
 | 
				
			||||||
 | 
								ctx.Error(http.StatusNotFound, "GetRepositoryByName", err)
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								ctx.Error(http.StatusInternalServerError, "GetRepositoryByName", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = packages_service.LinkToRepository(ctx, pkg, repo, ctx.Doer)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							switch {
 | 
				
			||||||
 | 
							case errors.Is(err, util.ErrInvalidArgument):
 | 
				
			||||||
 | 
								ctx.Error(http.StatusBadRequest, "LinkToRepository", err)
 | 
				
			||||||
 | 
							case errors.Is(err, util.ErrPermissionDenied):
 | 
				
			||||||
 | 
								ctx.Error(http.StatusForbidden, "LinkToRepository", err)
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								ctx.Error(http.StatusInternalServerError, "LinkToRepository", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ctx.Status(http.StatusCreated)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// UnlinkPackage sets a repository link for a package
 | 
				
			||||||
 | 
					func UnlinkPackage(ctx *context.APIContext) {
 | 
				
			||||||
 | 
						// swagger:operation POST /packages/{owner}/{type}/{name}/-/unlink package unlinkPackage
 | 
				
			||||||
 | 
						// ---
 | 
				
			||||||
 | 
						// summary: Unlink a package from a repository
 | 
				
			||||||
 | 
						// parameters:
 | 
				
			||||||
 | 
						// - name: owner
 | 
				
			||||||
 | 
						//   in: path
 | 
				
			||||||
 | 
						//   description: owner of the package
 | 
				
			||||||
 | 
						//   type: string
 | 
				
			||||||
 | 
						//   required: true
 | 
				
			||||||
 | 
						// - name: type
 | 
				
			||||||
 | 
						//   in: path
 | 
				
			||||||
 | 
						//   description: type of the package
 | 
				
			||||||
 | 
						//   type: string
 | 
				
			||||||
 | 
						//   required: true
 | 
				
			||||||
 | 
						// - name: name
 | 
				
			||||||
 | 
						//   in: path
 | 
				
			||||||
 | 
						//   description: name of the package
 | 
				
			||||||
 | 
						//   type: string
 | 
				
			||||||
 | 
						//   required: true
 | 
				
			||||||
 | 
						// responses:
 | 
				
			||||||
 | 
						//   "201":
 | 
				
			||||||
 | 
						//     "$ref": "#/responses/empty"
 | 
				
			||||||
 | 
						//   "404":
 | 
				
			||||||
 | 
						//     "$ref": "#/responses/notFound"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pkg, err := packages.GetPackageByName(ctx, ctx.ContextUser.ID, packages.Type(ctx.PathParam("type")), ctx.PathParam("name"))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							if errors.Is(err, util.ErrNotExist) {
 | 
				
			||||||
 | 
								ctx.Error(http.StatusNotFound, "GetPackageByName", err)
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								ctx.Error(http.StatusInternalServerError, "GetPackageByName", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = packages_service.UnlinkFromRepository(ctx, pkg, ctx.Doer)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							switch {
 | 
				
			||||||
 | 
							case errors.Is(err, util.ErrPermissionDenied):
 | 
				
			||||||
 | 
								ctx.Error(http.StatusForbidden, "UnlinkFromRepository", err)
 | 
				
			||||||
 | 
							case errors.Is(err, util.ErrInvalidArgument):
 | 
				
			||||||
 | 
								ctx.Error(http.StatusBadRequest, "UnlinkFromRepository", err)
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								ctx.Error(http.StatusInternalServerError, "UnlinkFromRepository", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ctx.Status(http.StatusNoContent)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										78
									
								
								services/packages/package_update.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								services/packages/package_update.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,78 @@
 | 
				
			|||||||
 | 
					// Copyright 2025 The Gitea Authors. All rights reserved.
 | 
				
			||||||
 | 
					// SPDX-License-Identifier: MIT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package packages
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						org_model "code.gitea.io/gitea/models/organization"
 | 
				
			||||||
 | 
						packages_model "code.gitea.io/gitea/models/packages"
 | 
				
			||||||
 | 
						access_model "code.gitea.io/gitea/models/perm/access"
 | 
				
			||||||
 | 
						repo_model "code.gitea.io/gitea/models/repo"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/models/unit"
 | 
				
			||||||
 | 
						user_model "code.gitea.io/gitea/models/user"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/util"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func LinkToRepository(ctx context.Context, pkg *packages_model.Package, repo *repo_model.Repository, doer *user_model.User) error {
 | 
				
			||||||
 | 
						if pkg.OwnerID != repo.OwnerID {
 | 
				
			||||||
 | 
							return util.ErrPermissionDenied
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if pkg.RepoID > 0 {
 | 
				
			||||||
 | 
							return util.ErrInvalidArgument
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						perms, err := access_model.GetUserRepoPermission(ctx, repo, doer)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("error getting permissions for user %d on repository %d: %w", doer.ID, repo.ID, err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if !perms.CanWrite(unit.TypePackages) {
 | 
				
			||||||
 | 
							return util.ErrPermissionDenied
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := packages_model.SetRepositoryLink(ctx, pkg.ID, repo.ID); err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("error while linking package '%v' to repo '%v' : %w", pkg.Name, repo.FullName(), err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func UnlinkFromRepository(ctx context.Context, pkg *packages_model.Package, doer *user_model.User) error {
 | 
				
			||||||
 | 
						if pkg.RepoID == 0 {
 | 
				
			||||||
 | 
							return util.ErrInvalidArgument
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						repo, err := repo_model.GetRepositoryByID(ctx, pkg.RepoID)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("error getting repository %d: %w", pkg.RepoID, err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						perms, err := access_model.GetUserRepoPermission(ctx, repo, doer)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("error getting permissions for user %d on repository %d: %w", doer.ID, repo.ID, err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if !perms.CanWrite(unit.TypePackages) {
 | 
				
			||||||
 | 
							return util.ErrPermissionDenied
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						user, err := user_model.GetUserByID(ctx, pkg.OwnerID)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if !doer.IsAdmin {
 | 
				
			||||||
 | 
							if !user.IsOrganization() {
 | 
				
			||||||
 | 
								if doer.ID != pkg.OwnerID {
 | 
				
			||||||
 | 
									return fmt.Errorf("no permission to unlink package '%v' from its repository, or packages are disabled", pkg.Name)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								isOrgAdmin, err := org_model.OrgFromUser(user).IsOrgAdmin(ctx, doer.ID)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								} else if !isOrgAdmin {
 | 
				
			||||||
 | 
									return fmt.Errorf("no permission to unlink package '%v' from its repository, or packages are disabled", pkg.Name)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return packages_model.UnlinkRepository(ctx, pkg.ID)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										87
									
								
								templates/swagger/v1_json.tmpl
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										87
									
								
								templates/swagger/v1_json.tmpl
									
									
									
										generated
									
									
									
								
							@@ -3339,6 +3339,93 @@
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "/packages/{owner}/{type}/{name}/-/link/{repo_name}": {
 | 
				
			||||||
 | 
					      "post": {
 | 
				
			||||||
 | 
					        "tags": [
 | 
				
			||||||
 | 
					          "package"
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "summary": "Link a package to a repository",
 | 
				
			||||||
 | 
					        "operationId": "linkPackage",
 | 
				
			||||||
 | 
					        "parameters": [
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "type": "string",
 | 
				
			||||||
 | 
					            "description": "owner of the package",
 | 
				
			||||||
 | 
					            "name": "owner",
 | 
				
			||||||
 | 
					            "in": "path",
 | 
				
			||||||
 | 
					            "required": true
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "type": "string",
 | 
				
			||||||
 | 
					            "description": "type of the package",
 | 
				
			||||||
 | 
					            "name": "type",
 | 
				
			||||||
 | 
					            "in": "path",
 | 
				
			||||||
 | 
					            "required": true
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "type": "string",
 | 
				
			||||||
 | 
					            "description": "name of the package",
 | 
				
			||||||
 | 
					            "name": "name",
 | 
				
			||||||
 | 
					            "in": "path",
 | 
				
			||||||
 | 
					            "required": true
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "type": "string",
 | 
				
			||||||
 | 
					            "description": "name of the repository to link.",
 | 
				
			||||||
 | 
					            "name": "repo_name",
 | 
				
			||||||
 | 
					            "in": "path",
 | 
				
			||||||
 | 
					            "required": true
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "responses": {
 | 
				
			||||||
 | 
					          "201": {
 | 
				
			||||||
 | 
					            "$ref": "#/responses/empty"
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          "404": {
 | 
				
			||||||
 | 
					            "$ref": "#/responses/notFound"
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "/packages/{owner}/{type}/{name}/-/unlink": {
 | 
				
			||||||
 | 
					      "post": {
 | 
				
			||||||
 | 
					        "tags": [
 | 
				
			||||||
 | 
					          "package"
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "summary": "Unlink a package from a repository",
 | 
				
			||||||
 | 
					        "operationId": "unlinkPackage",
 | 
				
			||||||
 | 
					        "parameters": [
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "type": "string",
 | 
				
			||||||
 | 
					            "description": "owner of the package",
 | 
				
			||||||
 | 
					            "name": "owner",
 | 
				
			||||||
 | 
					            "in": "path",
 | 
				
			||||||
 | 
					            "required": true
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "type": "string",
 | 
				
			||||||
 | 
					            "description": "type of the package",
 | 
				
			||||||
 | 
					            "name": "type",
 | 
				
			||||||
 | 
					            "in": "path",
 | 
				
			||||||
 | 
					            "required": true
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "type": "string",
 | 
				
			||||||
 | 
					            "description": "name of the package",
 | 
				
			||||||
 | 
					            "name": "name",
 | 
				
			||||||
 | 
					            "in": "path",
 | 
				
			||||||
 | 
					            "required": true
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "responses": {
 | 
				
			||||||
 | 
					          "201": {
 | 
				
			||||||
 | 
					            "$ref": "#/responses/empty"
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          "404": {
 | 
				
			||||||
 | 
					            "$ref": "#/responses/notFound"
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "/packages/{owner}/{type}/{name}/{version}": {
 | 
					    "/packages/{owner}/{type}/{name}/{version}": {
 | 
				
			||||||
      "get": {
 | 
					      "get": {
 | 
				
			||||||
        "produces": [
 | 
					        "produces": [
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -23,6 +23,7 @@ import (
 | 
				
			|||||||
	"code.gitea.io/gitea/modules/util"
 | 
						"code.gitea.io/gitea/modules/util"
 | 
				
			||||||
	packages_service "code.gitea.io/gitea/services/packages"
 | 
						packages_service "code.gitea.io/gitea/services/packages"
 | 
				
			||||||
	packages_cleanup_service "code.gitea.io/gitea/services/packages/cleanup"
 | 
						packages_cleanup_service "code.gitea.io/gitea/services/packages/cleanup"
 | 
				
			||||||
 | 
						repo_service "code.gitea.io/gitea/services/repository"
 | 
				
			||||||
	"code.gitea.io/gitea/tests"
 | 
						"code.gitea.io/gitea/tests"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/stretchr/testify/assert"
 | 
						"github.com/stretchr/testify/assert"
 | 
				
			||||||
@@ -34,7 +35,7 @@ func TestPackageAPI(t *testing.T) {
 | 
				
			|||||||
	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4})
 | 
						user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4})
 | 
				
			||||||
	session := loginUser(t, user.Name)
 | 
						session := loginUser(t, user.Name)
 | 
				
			||||||
	tokenReadPackage := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadPackage)
 | 
						tokenReadPackage := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadPackage)
 | 
				
			||||||
	tokenDeletePackage := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWritePackage)
 | 
						tokenWritePackage := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWritePackage)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	packageName := "test-package"
 | 
						packageName := "test-package"
 | 
				
			||||||
	packageVersion := "1.0.3"
 | 
						packageVersion := "1.0.3"
 | 
				
			||||||
@@ -86,7 +87,7 @@ func TestPackageAPI(t *testing.T) {
 | 
				
			|||||||
		t.Run("RepositoryLink", func(t *testing.T) {
 | 
							t.Run("RepositoryLink", func(t *testing.T) {
 | 
				
			||||||
			defer tests.PrintCurrentTest(t)()
 | 
								defer tests.PrintCurrentTest(t)()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			p, err := packages_model.GetPackageByName(db.DefaultContext, user.ID, packages_model.TypeGeneric, packageName)
 | 
								_, err := packages_model.GetPackageByName(db.DefaultContext, user.ID, packages_model.TypeGeneric, packageName)
 | 
				
			||||||
			assert.NoError(t, err)
 | 
								assert.NoError(t, err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// no repository link
 | 
								// no repository link
 | 
				
			||||||
@@ -98,8 +99,15 @@ func TestPackageAPI(t *testing.T) {
 | 
				
			|||||||
			DecodeJSON(t, resp, &ap1)
 | 
								DecodeJSON(t, resp, &ap1)
 | 
				
			||||||
			assert.Nil(t, ap1.Repository)
 | 
								assert.Nil(t, ap1.Repository)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// create a repository
 | 
				
			||||||
 | 
								newRepo, err := repo_service.CreateRepository(db.DefaultContext, user, user, repo_service.CreateRepoOptions{
 | 
				
			||||||
 | 
									Name: "repo4",
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
								assert.NoError(t, err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// link to public repository
 | 
								// link to public repository
 | 
				
			||||||
			assert.NoError(t, packages_model.SetRepositoryLink(db.DefaultContext, p.ID, 1))
 | 
								req = NewRequest(t, "POST", fmt.Sprintf("/api/v1/packages/%s/generic/%s/-/link/%s", user.Name, packageName, newRepo.Name)).AddTokenAuth(tokenWritePackage)
 | 
				
			||||||
 | 
								MakeRequest(t, req, http.StatusCreated)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s/generic/%s/%s", user.Name, packageName, packageVersion)).
 | 
								req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s/generic/%s/%s", user.Name, packageName, packageVersion)).
 | 
				
			||||||
				AddTokenAuth(tokenReadPackage)
 | 
									AddTokenAuth(tokenReadPackage)
 | 
				
			||||||
@@ -108,10 +116,15 @@ func TestPackageAPI(t *testing.T) {
 | 
				
			|||||||
			var ap2 *api.Package
 | 
								var ap2 *api.Package
 | 
				
			||||||
			DecodeJSON(t, resp, &ap2)
 | 
								DecodeJSON(t, resp, &ap2)
 | 
				
			||||||
			assert.NotNil(t, ap2.Repository)
 | 
								assert.NotNil(t, ap2.Repository)
 | 
				
			||||||
			assert.EqualValues(t, 1, ap2.Repository.ID)
 | 
								assert.EqualValues(t, newRepo.ID, ap2.Repository.ID)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// link to private repository
 | 
								// link to repository without write access, should fail
 | 
				
			||||||
			assert.NoError(t, packages_model.SetRepositoryLink(db.DefaultContext, p.ID, 2))
 | 
								req = NewRequest(t, "POST", fmt.Sprintf("/api/v1/packages/%s/generic/%s/-/link/%s", user.Name, packageName, "repo3")).AddTokenAuth(tokenWritePackage)
 | 
				
			||||||
 | 
								MakeRequest(t, req, http.StatusNotFound)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// remove link
 | 
				
			||||||
 | 
								req = NewRequest(t, "POST", fmt.Sprintf("/api/v1/packages/%s/generic/%s/-/unlink", user.Name, packageName)).AddTokenAuth(tokenWritePackage)
 | 
				
			||||||
 | 
								MakeRequest(t, req, http.StatusNoContent)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s/generic/%s/%s", user.Name, packageName, packageVersion)).
 | 
								req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s/generic/%s/%s", user.Name, packageName, packageVersion)).
 | 
				
			||||||
				AddTokenAuth(tokenReadPackage)
 | 
									AddTokenAuth(tokenReadPackage)
 | 
				
			||||||
@@ -121,7 +134,18 @@ func TestPackageAPI(t *testing.T) {
 | 
				
			|||||||
			DecodeJSON(t, resp, &ap3)
 | 
								DecodeJSON(t, resp, &ap3)
 | 
				
			||||||
			assert.Nil(t, ap3.Repository)
 | 
								assert.Nil(t, ap3.Repository)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			assert.NoError(t, packages_model.UnlinkRepositoryFromAllPackages(db.DefaultContext, 2))
 | 
								// force link to a repository the currently logged-in user doesn't have access to
 | 
				
			||||||
 | 
								privateRepoID := int64(6)
 | 
				
			||||||
 | 
								assert.NoError(t, packages_model.SetRepositoryLink(db.DefaultContext, p.ID, privateRepoID))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s/generic/%s/%s", user.Name, packageName, packageVersion)).AddTokenAuth(tokenReadPackage)
 | 
				
			||||||
 | 
								resp = MakeRequest(t, req, http.StatusOK)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								var ap4 *api.Package
 | 
				
			||||||
 | 
								DecodeJSON(t, resp, &ap4)
 | 
				
			||||||
 | 
								assert.Nil(t, ap4.Repository)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								assert.NoError(t, packages_model.UnlinkRepositoryFromAllPackages(db.DefaultContext, privateRepoID))
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -152,11 +176,11 @@ func TestPackageAPI(t *testing.T) {
 | 
				
			|||||||
		defer tests.PrintCurrentTest(t)()
 | 
							defer tests.PrintCurrentTest(t)()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		req := NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/packages/%s/dummy/%s/%s", user.Name, packageName, packageVersion)).
 | 
							req := NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/packages/%s/dummy/%s/%s", user.Name, packageName, packageVersion)).
 | 
				
			||||||
			AddTokenAuth(tokenDeletePackage)
 | 
								AddTokenAuth(tokenWritePackage)
 | 
				
			||||||
		MakeRequest(t, req, http.StatusNotFound)
 | 
							MakeRequest(t, req, http.StatusNotFound)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		req = NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/packages/%s/generic/%s/%s", user.Name, packageName, packageVersion)).
 | 
							req = NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/packages/%s/generic/%s/%s", user.Name, packageName, packageVersion)).
 | 
				
			||||||
			AddTokenAuth(tokenDeletePackage)
 | 
								AddTokenAuth(tokenWritePackage)
 | 
				
			||||||
		MakeRequest(t, req, http.StatusNoContent)
 | 
							MakeRequest(t, req, http.StatusNoContent)
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user