* Feature - #5960 - API Endpoint for Repo Editing * Revert from merge * Adds integration testing * Updates to integration tests * Revert changes * Update year in file header * Misspell fix * XORM = test * XORM = test * revert XORM = file * Makes RepoUnit.ID be pk and autoincr * Fix to units * revert header * Remove print statement * Adds other responses * Improves swagger for creating repo * Fixes import order * Better Unit Type does not exist error * Adds editable repo properties to the response repo structure * Fix to api_repo_edit_test.go * Fixes repo test * Changes per review * Fixes typo and standardizes comments in the EditRepoOption struct for swagger * Fixes typo and standardizes comments in the EditRepoOption struct for swagger * Actually can unarchive through the API * Unlike delete, user doesn't have to be the owner of the org, just admin to the repo * Fix to swagger comments for field name change * Update to swagger docs * Update swagger * Changes allow_pull_requests to has_pull_requests
This commit is contained in:
parent
cdd10f145b
commit
1831b3b571
11 changed files with 868 additions and 66 deletions
225
integrations/api_repo_edit_test.go
Normal file
225
integrations/api_repo_edit_test.go
Normal file
|
@ -0,0 +1,225 @@
|
||||||
|
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package integrations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models"
|
||||||
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
// getRepoEditOptionFromRepo gets the options for an existing repo exactly as is
|
||||||
|
func getRepoEditOptionFromRepo(repo *models.Repository) *api.EditRepoOption {
|
||||||
|
name := repo.Name
|
||||||
|
description := repo.Description
|
||||||
|
website := repo.Website
|
||||||
|
private := repo.IsPrivate
|
||||||
|
hasIssues := false
|
||||||
|
if _, err := repo.GetUnit(models.UnitTypeIssues); err == nil {
|
||||||
|
hasIssues = true
|
||||||
|
}
|
||||||
|
hasWiki := false
|
||||||
|
if _, err := repo.GetUnit(models.UnitTypeWiki); err == nil {
|
||||||
|
hasWiki = true
|
||||||
|
}
|
||||||
|
defaultBranch := repo.DefaultBranch
|
||||||
|
hasPullRequests := false
|
||||||
|
ignoreWhitespaceConflicts := false
|
||||||
|
allowMerge := false
|
||||||
|
allowRebase := false
|
||||||
|
allowRebaseMerge := false
|
||||||
|
allowSquash := false
|
||||||
|
if unit, err := repo.GetUnit(models.UnitTypePullRequests); err == nil {
|
||||||
|
config := unit.PullRequestsConfig()
|
||||||
|
hasPullRequests = true
|
||||||
|
ignoreWhitespaceConflicts = config.IgnoreWhitespaceConflicts
|
||||||
|
allowMerge = config.AllowMerge
|
||||||
|
allowRebase = config.AllowRebase
|
||||||
|
allowRebaseMerge = config.AllowRebaseMerge
|
||||||
|
allowSquash = config.AllowSquash
|
||||||
|
}
|
||||||
|
archived := repo.IsArchived
|
||||||
|
return &api.EditRepoOption{
|
||||||
|
Name: &name,
|
||||||
|
Description: &description,
|
||||||
|
Website: &website,
|
||||||
|
Private: &private,
|
||||||
|
HasIssues: &hasIssues,
|
||||||
|
HasWiki: &hasWiki,
|
||||||
|
DefaultBranch: &defaultBranch,
|
||||||
|
HasPullRequests: &hasPullRequests,
|
||||||
|
IgnoreWhitespaceConflicts: &ignoreWhitespaceConflicts,
|
||||||
|
AllowMerge: &allowMerge,
|
||||||
|
AllowRebase: &allowRebase,
|
||||||
|
AllowRebaseMerge: &allowRebaseMerge,
|
||||||
|
AllowSquash: &allowSquash,
|
||||||
|
Archived: &archived,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// getNewRepoEditOption Gets the options to change everything about an existing repo by adding to strings or changing
|
||||||
|
// the boolean
|
||||||
|
func getNewRepoEditOption(opts *api.EditRepoOption) *api.EditRepoOption {
|
||||||
|
// Gives a new property to everything
|
||||||
|
name := *opts.Name + "renamed"
|
||||||
|
description := "new description"
|
||||||
|
website := "http://wwww.newwebsite.com"
|
||||||
|
private := !*opts.Private
|
||||||
|
hasIssues := !*opts.HasIssues
|
||||||
|
hasWiki := !*opts.HasWiki
|
||||||
|
defaultBranch := "master"
|
||||||
|
hasPullRequests := !*opts.HasPullRequests
|
||||||
|
ignoreWhitespaceConflicts := !*opts.IgnoreWhitespaceConflicts
|
||||||
|
allowMerge := !*opts.AllowMerge
|
||||||
|
allowRebase := !*opts.AllowRebase
|
||||||
|
allowRebaseMerge := !*opts.AllowRebaseMerge
|
||||||
|
allowSquash := !*opts.AllowSquash
|
||||||
|
archived := !*opts.Archived
|
||||||
|
|
||||||
|
return &api.EditRepoOption{
|
||||||
|
Name: &name,
|
||||||
|
Description: &description,
|
||||||
|
Website: &website,
|
||||||
|
Private: &private,
|
||||||
|
DefaultBranch: &defaultBranch,
|
||||||
|
HasIssues: &hasIssues,
|
||||||
|
HasWiki: &hasWiki,
|
||||||
|
HasPullRequests: &hasPullRequests,
|
||||||
|
IgnoreWhitespaceConflicts: &ignoreWhitespaceConflicts,
|
||||||
|
AllowMerge: &allowMerge,
|
||||||
|
AllowRebase: &allowRebase,
|
||||||
|
AllowRebaseMerge: &allowRebaseMerge,
|
||||||
|
AllowSquash: &allowSquash,
|
||||||
|
Archived: &archived,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAPIRepoEdit(t *testing.T) {
|
||||||
|
onGiteaRun(t, func(t *testing.T, u *url.URL) {
|
||||||
|
user2 := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User) // owner of the repo1 & repo16
|
||||||
|
user3 := models.AssertExistsAndLoadBean(t, &models.User{ID: 3}).(*models.User) // owner of the repo3, is an org
|
||||||
|
user4 := models.AssertExistsAndLoadBean(t, &models.User{ID: 4}).(*models.User) // owner of neither repos
|
||||||
|
repo1 := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository) // public repo
|
||||||
|
repo3 := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 3}).(*models.Repository) // public repo
|
||||||
|
repo16 := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 16}).(*models.Repository) // private repo
|
||||||
|
|
||||||
|
// Get user2's token
|
||||||
|
session := loginUser(t, user2.Name)
|
||||||
|
token2 := getTokenForLoggedInUser(t, session)
|
||||||
|
session = emptyTestSession(t)
|
||||||
|
// Get user4's token
|
||||||
|
session = loginUser(t, user4.Name)
|
||||||
|
token4 := getTokenForLoggedInUser(t, session)
|
||||||
|
session = emptyTestSession(t)
|
||||||
|
|
||||||
|
// Test editing a repo1 which user2 owns, changing name and many properties
|
||||||
|
origRepoEditOption := getRepoEditOptionFromRepo(repo1)
|
||||||
|
repoEditOption := getNewRepoEditOption(origRepoEditOption)
|
||||||
|
url := fmt.Sprintf("/api/v1/repos/%s/%s?token=%s", user2.Name, repo1.Name, token2)
|
||||||
|
req := NewRequestWithJSON(t, "PATCH", url, &repoEditOption)
|
||||||
|
resp := session.MakeRequest(t, req, http.StatusOK)
|
||||||
|
var repo api.Repository
|
||||||
|
DecodeJSON(t, resp, &repo)
|
||||||
|
assert.NotNil(t, repo)
|
||||||
|
// check response
|
||||||
|
assert.Equal(t, *repoEditOption.Name, repo.Name)
|
||||||
|
assert.Equal(t, *repoEditOption.Description, repo.Description)
|
||||||
|
assert.Equal(t, *repoEditOption.Website, repo.Website)
|
||||||
|
assert.Equal(t, *repoEditOption.Archived, repo.Archived)
|
||||||
|
// check repo1 from database
|
||||||
|
repo1edited := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
|
||||||
|
repo1editedOption := getRepoEditOptionFromRepo(repo1edited)
|
||||||
|
assert.Equal(t, *repoEditOption.Name, *repo1editedOption.Name)
|
||||||
|
assert.Equal(t, *repoEditOption.Description, *repo1editedOption.Description)
|
||||||
|
assert.Equal(t, *repoEditOption.Website, *repo1editedOption.Website)
|
||||||
|
assert.Equal(t, *repoEditOption.Archived, *repo1editedOption.Archived)
|
||||||
|
assert.Equal(t, *repoEditOption.Private, *repo1editedOption.Private)
|
||||||
|
assert.Equal(t, *repoEditOption.HasWiki, *repo1editedOption.HasWiki)
|
||||||
|
// reset repo in db
|
||||||
|
url = fmt.Sprintf("/api/v1/repos/%s/%s?token=%s", user2.Name, *repoEditOption.Name, token2)
|
||||||
|
req = NewRequestWithJSON(t, "PATCH", url, &origRepoEditOption)
|
||||||
|
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||||
|
|
||||||
|
// Test editing a non-existing repo
|
||||||
|
name := "repodoesnotexist"
|
||||||
|
url = fmt.Sprintf("/api/v1/repos/%s/%s?token=%s", user2.Name, name, token2)
|
||||||
|
req = NewRequestWithJSON(t, "PATCH", url, &api.EditRepoOption{Name: &name})
|
||||||
|
resp = session.MakeRequest(t, req, http.StatusNotFound)
|
||||||
|
|
||||||
|
// Test editing repo16 by user4 who does not have write access
|
||||||
|
origRepoEditOption = getRepoEditOptionFromRepo(repo16)
|
||||||
|
repoEditOption = getNewRepoEditOption(origRepoEditOption)
|
||||||
|
url = fmt.Sprintf("/api/v1/repos/%s/%s?token=%s", user2.Name, repo16.Name, token4)
|
||||||
|
req = NewRequestWithJSON(t, "PATCH", url, &repoEditOption)
|
||||||
|
session.MakeRequest(t, req, http.StatusNotFound)
|
||||||
|
|
||||||
|
// Tests a repo with no token given so will fail
|
||||||
|
origRepoEditOption = getRepoEditOptionFromRepo(repo16)
|
||||||
|
repoEditOption = getNewRepoEditOption(origRepoEditOption)
|
||||||
|
url = fmt.Sprintf("/api/v1/repos/%s/%s", user2.Name, repo16.Name)
|
||||||
|
req = NewRequestWithJSON(t, "PATCH", url, &repoEditOption)
|
||||||
|
resp = session.MakeRequest(t, req, http.StatusNotFound)
|
||||||
|
|
||||||
|
// Test using access token for a private repo that the user of the token owns
|
||||||
|
origRepoEditOption = getRepoEditOptionFromRepo(repo16)
|
||||||
|
repoEditOption = getNewRepoEditOption(origRepoEditOption)
|
||||||
|
url = fmt.Sprintf("/api/v1/repos/%s/%s?token=%s", user2.Name, repo16.Name, token2)
|
||||||
|
req = NewRequestWithJSON(t, "PATCH", url, &repoEditOption)
|
||||||
|
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||||
|
// reset repo in db
|
||||||
|
url = fmt.Sprintf("/api/v1/repos/%s/%s?token=%s", user2.Name, *repoEditOption.Name, token2)
|
||||||
|
req = NewRequestWithJSON(t, "PATCH", url, &origRepoEditOption)
|
||||||
|
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||||
|
|
||||||
|
// Test making a repo public that is private
|
||||||
|
repo16 = models.AssertExistsAndLoadBean(t, &models.Repository{ID: 16}).(*models.Repository)
|
||||||
|
assert.True(t, repo16.IsPrivate)
|
||||||
|
private := false
|
||||||
|
repoEditOption = &api.EditRepoOption{
|
||||||
|
Private: &private,
|
||||||
|
}
|
||||||
|
url = fmt.Sprintf("/api/v1/repos/%s/%s?token=%s", user2.Name, repo16.Name, token2)
|
||||||
|
req = NewRequestWithJSON(t, "PATCH", url, &repoEditOption)
|
||||||
|
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||||
|
repo16 = models.AssertExistsAndLoadBean(t, &models.Repository{ID: 16}).(*models.Repository)
|
||||||
|
assert.False(t, repo16.IsPrivate)
|
||||||
|
// Make it private again
|
||||||
|
private = true
|
||||||
|
repoEditOption.Private = &private
|
||||||
|
req = NewRequestWithJSON(t, "PATCH", url, &repoEditOption)
|
||||||
|
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||||
|
|
||||||
|
// Test using org repo "user3/repo3" where user2 is a collaborator
|
||||||
|
origRepoEditOption = getRepoEditOptionFromRepo(repo3)
|
||||||
|
repoEditOption = getNewRepoEditOption(origRepoEditOption)
|
||||||
|
url = fmt.Sprintf("/api/v1/repos/%s/%s?token=%s", user3.Name, repo3.Name, token2)
|
||||||
|
req = NewRequestWithJSON(t, "PATCH", url, &repoEditOption)
|
||||||
|
session.MakeRequest(t, req, http.StatusOK)
|
||||||
|
// reset repo in db
|
||||||
|
url = fmt.Sprintf("/api/v1/repos/%s/%s?token=%s", user3.Name, *repoEditOption.Name, token2)
|
||||||
|
req = NewRequestWithJSON(t, "PATCH", url, &origRepoEditOption)
|
||||||
|
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||||
|
|
||||||
|
// Test using org repo "user3/repo3" with no user token
|
||||||
|
origRepoEditOption = getRepoEditOptionFromRepo(repo3)
|
||||||
|
repoEditOption = getNewRepoEditOption(origRepoEditOption)
|
||||||
|
url = fmt.Sprintf("/api/v1/repos/%s/%s", user3.Name, repo3.Name)
|
||||||
|
req = NewRequestWithJSON(t, "PATCH", url, &repoEditOption)
|
||||||
|
session.MakeRequest(t, req, http.StatusNotFound)
|
||||||
|
|
||||||
|
// Test using repo "user2/repo1" where user4 is a NOT collaborator
|
||||||
|
origRepoEditOption = getRepoEditOptionFromRepo(repo1)
|
||||||
|
repoEditOption = getNewRepoEditOption(origRepoEditOption)
|
||||||
|
url = fmt.Sprintf("/api/v1/repos/%s/%s?token=%s", user2.Name, repo1.Name, token4)
|
||||||
|
req = NewRequestWithJSON(t, "PATCH", url, &repoEditOption)
|
||||||
|
session.MakeRequest(t, req, http.StatusForbidden)
|
||||||
|
})
|
||||||
|
}
|
|
@ -108,7 +108,7 @@ func TestAPIDeleteFile(t *testing.T) {
|
||||||
DecodeJSON(t, resp, &apiError)
|
DecodeJSON(t, resp, &apiError)
|
||||||
assert.Equal(t, expectedAPIError, apiError)
|
assert.Equal(t, expectedAPIError, apiError)
|
||||||
|
|
||||||
// Test creating a file in repo1 by user4 who does not have write access
|
// Test creating a file in repo16 by user4 who does not have write access
|
||||||
fileID++
|
fileID++
|
||||||
treePath = fmt.Sprintf("delete/file%d.txt", fileID)
|
treePath = fmt.Sprintf("delete/file%d.txt", fileID)
|
||||||
createFile(user2, repo16, treePath)
|
createFile(user2, repo16, treePath)
|
||||||
|
|
|
@ -162,8 +162,8 @@ func CreateOrganization(org, owner *User) (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// insert units for team
|
// insert units for team
|
||||||
var units = make([]TeamUnit, 0, len(allRepUnitTypes))
|
var units = make([]TeamUnit, 0, len(AllRepoUnitTypes))
|
||||||
for _, tp := range allRepUnitTypes {
|
for _, tp := range AllRepoUnitTypes {
|
||||||
units = append(units, TeamUnit{
|
units = append(units, TeamUnit{
|
||||||
OrgID: org.ID,
|
OrgID: org.ID,
|
||||||
TeamID: t.ID,
|
TeamID: t.ID,
|
||||||
|
|
|
@ -274,6 +274,30 @@ func (repo *Repository) innerAPIFormat(e Engine, mode AccessMode, isParent bool)
|
||||||
parent = repo.BaseRepo.innerAPIFormat(e, mode, true)
|
parent = repo.BaseRepo.innerAPIFormat(e, mode, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
hasIssues := false
|
||||||
|
if _, err := repo.getUnit(e, UnitTypeIssues); err == nil {
|
||||||
|
hasIssues = true
|
||||||
|
}
|
||||||
|
hasWiki := false
|
||||||
|
if _, err := repo.getUnit(e, UnitTypeWiki); err == nil {
|
||||||
|
hasWiki = true
|
||||||
|
}
|
||||||
|
hasPullRequests := false
|
||||||
|
ignoreWhitespaceConflicts := false
|
||||||
|
allowMerge := false
|
||||||
|
allowRebase := false
|
||||||
|
allowRebaseMerge := false
|
||||||
|
allowSquash := false
|
||||||
|
if unit, err := repo.getUnit(e, UnitTypePullRequests); err == nil {
|
||||||
|
config := unit.PullRequestsConfig()
|
||||||
|
hasPullRequests = true
|
||||||
|
ignoreWhitespaceConflicts = config.IgnoreWhitespaceConflicts
|
||||||
|
allowMerge = config.AllowMerge
|
||||||
|
allowRebase = config.AllowRebase
|
||||||
|
allowRebaseMerge = config.AllowRebaseMerge
|
||||||
|
allowSquash = config.AllowSquash
|
||||||
|
}
|
||||||
|
|
||||||
return &api.Repository{
|
return &api.Repository{
|
||||||
ID: repo.ID,
|
ID: repo.ID,
|
||||||
Owner: repo.Owner.APIFormat(),
|
Owner: repo.Owner.APIFormat(),
|
||||||
|
@ -299,6 +323,14 @@ func (repo *Repository) innerAPIFormat(e Engine, mode AccessMode, isParent bool)
|
||||||
Created: repo.CreatedUnix.AsTime(),
|
Created: repo.CreatedUnix.AsTime(),
|
||||||
Updated: repo.UpdatedUnix.AsTime(),
|
Updated: repo.UpdatedUnix.AsTime(),
|
||||||
Permissions: permission,
|
Permissions: permission,
|
||||||
|
HasIssues: hasIssues,
|
||||||
|
HasWiki: hasWiki,
|
||||||
|
HasPullRequests: hasPullRequests,
|
||||||
|
IgnoreWhitespaceConflicts: ignoreWhitespaceConflicts,
|
||||||
|
AllowMerge: allowMerge,
|
||||||
|
AllowRebase: allowRebase,
|
||||||
|
AllowRebaseMerge: allowRebaseMerge,
|
||||||
|
AllowSquash: allowSquash,
|
||||||
AvatarURL: repo.AvatarLink(),
|
AvatarURL: repo.AvatarLink(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -346,10 +378,20 @@ func (repo *Repository) UnitEnabled(tp UnitType) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
// ErrUnitTypeNotExist represents a "UnitTypeNotExist" kind of error.
|
||||||
// ErrUnitNotExist organization does not exist
|
type ErrUnitTypeNotExist struct {
|
||||||
ErrUnitNotExist = errors.New("Unit does not exist")
|
UT UnitType
|
||||||
)
|
}
|
||||||
|
|
||||||
|
// IsErrUnitTypeNotExist checks if an error is a ErrUnitNotExist.
|
||||||
|
func IsErrUnitTypeNotExist(err error) bool {
|
||||||
|
_, ok := err.(ErrUnitTypeNotExist)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err ErrUnitTypeNotExist) Error() string {
|
||||||
|
return fmt.Sprintf("Unit type does not exist: %s", err.UT.String())
|
||||||
|
}
|
||||||
|
|
||||||
// MustGetUnit always returns a RepoUnit object
|
// MustGetUnit always returns a RepoUnit object
|
||||||
func (repo *Repository) MustGetUnit(tp UnitType) *RepoUnit {
|
func (repo *Repository) MustGetUnit(tp UnitType) *RepoUnit {
|
||||||
|
@ -373,6 +415,11 @@ func (repo *Repository) MustGetUnit(tp UnitType) *RepoUnit {
|
||||||
Type: tp,
|
Type: tp,
|
||||||
Config: new(PullRequestsConfig),
|
Config: new(PullRequestsConfig),
|
||||||
}
|
}
|
||||||
|
} else if tp == UnitTypeIssues {
|
||||||
|
return &RepoUnit{
|
||||||
|
Type: tp,
|
||||||
|
Config: new(IssuesConfig),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return &RepoUnit{
|
return &RepoUnit{
|
||||||
Type: tp,
|
Type: tp,
|
||||||
|
@ -394,7 +441,7 @@ func (repo *Repository) getUnit(e Engine, tp UnitType) (*RepoUnit, error) {
|
||||||
return unit, nil
|
return unit, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil, ErrUnitNotExist
|
return nil, ErrUnitTypeNotExist{tp}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (repo *Repository) getOwner(e Engine) (err error) {
|
func (repo *Repository) getOwner(e Engine) (err error) {
|
||||||
|
@ -1232,8 +1279,8 @@ func createRepository(e *xorm.Session, doer, u *User, repo *Repository) (err err
|
||||||
}
|
}
|
||||||
|
|
||||||
// insert units for repo
|
// insert units for repo
|
||||||
var units = make([]RepoUnit, 0, len(defaultRepoUnits))
|
var units = make([]RepoUnit, 0, len(DefaultRepoUnits))
|
||||||
for _, tp := range defaultRepoUnits {
|
for _, tp := range DefaultRepoUnits {
|
||||||
if tp == UnitTypeIssues {
|
if tp == UnitTypeIssues {
|
||||||
units = append(units, RepoUnit{
|
units = append(units, RepoUnit{
|
||||||
RepoID: repo.ID,
|
RepoID: repo.ID,
|
||||||
|
|
|
@ -58,8 +58,8 @@ func (u UnitType) ColorFormat(s fmt.State) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// allRepUnitTypes contains all the unit types
|
// AllRepoUnitTypes contains all the unit types
|
||||||
allRepUnitTypes = []UnitType{
|
AllRepoUnitTypes = []UnitType{
|
||||||
UnitTypeCode,
|
UnitTypeCode,
|
||||||
UnitTypeIssues,
|
UnitTypeIssues,
|
||||||
UnitTypePullRequests,
|
UnitTypePullRequests,
|
||||||
|
@ -69,8 +69,8 @@ var (
|
||||||
UnitTypeExternalTracker,
|
UnitTypeExternalTracker,
|
||||||
}
|
}
|
||||||
|
|
||||||
// defaultRepoUnits contains the default unit types
|
// DefaultRepoUnits contains the default unit types
|
||||||
defaultRepoUnits = []UnitType{
|
DefaultRepoUnits = []UnitType{
|
||||||
UnitTypeCode,
|
UnitTypeCode,
|
||||||
UnitTypeIssues,
|
UnitTypeIssues,
|
||||||
UnitTypePullRequests,
|
UnitTypePullRequests,
|
||||||
|
|
|
@ -43,6 +43,14 @@ type Repository struct {
|
||||||
// swagger:strfmt date-time
|
// swagger:strfmt date-time
|
||||||
Updated time.Time `json:"updated_at"`
|
Updated time.Time `json:"updated_at"`
|
||||||
Permissions *Permission `json:"permissions,omitempty"`
|
Permissions *Permission `json:"permissions,omitempty"`
|
||||||
|
HasIssues bool `json:"has_issues"`
|
||||||
|
HasWiki bool `json:"has_wiki"`
|
||||||
|
HasPullRequests bool `json:"has_pull_requests"`
|
||||||
|
IgnoreWhitespaceConflicts bool `json:"ignore_whitespace_conflicts"`
|
||||||
|
AllowMerge bool `json:"allow_merge_commits"`
|
||||||
|
AllowRebase bool `json:"allow_rebase"`
|
||||||
|
AllowRebaseMerge bool `json:"allow_rebase_explicit"`
|
||||||
|
AllowSquash bool `json:"allow_squash_merge"`
|
||||||
AvatarURL string `json:"avatar_url"`
|
AvatarURL string `json:"avatar_url"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,38 +79,36 @@ type CreateRepoOption struct {
|
||||||
// EditRepoOption options when editing a repository's properties
|
// EditRepoOption options when editing a repository's properties
|
||||||
// swagger:model
|
// swagger:model
|
||||||
type EditRepoOption struct {
|
type EditRepoOption struct {
|
||||||
// Name of the repository
|
// name of the repository
|
||||||
//
|
|
||||||
// required: true
|
|
||||||
// unique: true
|
// unique: true
|
||||||
Name *string `json:"name" binding:"Required;AlphaDashDot;MaxSize(100)"`
|
Name *string `json:"name,omitempty" binding:"OmitEmpty;AlphaDashDot;MaxSize(100);"`
|
||||||
// A short description of the repository.
|
// a short description of the repository.
|
||||||
Description *string `json:"description,omitempty" binding:"MaxSize(255)"`
|
Description *string `json:"description,omitempty" binding:"MaxSize(255)"`
|
||||||
// A URL with more information about the repository.
|
// a URL with more information about the repository.
|
||||||
Website *string `json:"website,omitempty" binding:"MaxSize(255)"`
|
Website *string `json:"website,omitempty" binding:"MaxSize(255)"`
|
||||||
// Either `true` to make the repository private or `false` to make it public.
|
// either `true` to make the repository private or `false` to make it public.
|
||||||
// Note: You will get a 422 error if the organization restricts changing repository visibility to organization
|
// Note: you will get a 422 error if the organization restricts changing repository visibility to organization
|
||||||
// owners and a non-owner tries to change the value of private.
|
// owners and a non-owner tries to change the value of private.
|
||||||
Private *bool `json:"private,omitempty"`
|
Private *bool `json:"private,omitempty"`
|
||||||
// Either `true` to enable issues for this repository or `false` to disable them.
|
// either `true` to enable issues for this repository or `false` to disable them.
|
||||||
EnableIssues *bool `json:"enable_issues,omitempty"`
|
HasIssues *bool `json:"has_issues,omitempty"`
|
||||||
// Either `true` to enable the wiki for this repository or `false` to disable it.
|
// either `true` to enable the wiki for this repository or `false` to disable it.
|
||||||
EnableWiki *bool `json:"enable_wiki,omitempty"`
|
HasWiki *bool `json:"has_wiki,omitempty"`
|
||||||
// Updates the default branch for this repository.
|
// sets the default branch for this repository.
|
||||||
DefaultBranch *string `json:"default_branch,omitempty"`
|
DefaultBranch *string `json:"default_branch,omitempty"`
|
||||||
// Either `true` to allow pull requests, or `false` to prevent pull request.
|
// either `true` to allow pull requests, or `false` to prevent pull request.
|
||||||
EnablePullRequests *bool `json:"enable_pull_requests,omitempty"`
|
HasPullRequests *bool `json:"has_pull_requests,omitempty"`
|
||||||
// Either `true` to ignore whitepace for conflicts, or `false` to not ignore whitespace. `enabled_pull_requests` must be `true`.
|
// either `true` to ignore whitespace for conflicts, or `false` to not ignore whitespace. `has_pull_requests` must be `true`.
|
||||||
IgnoreWhitespaceConflicts *bool `json:"ignore_whitespace,omitempty"`
|
IgnoreWhitespaceConflicts *bool `json:"ignore_whitespace_conflicts,omitempty"`
|
||||||
// Either `true` to allow merging pull requests with a merge commit, or `false` to prevent merging pull requests with merge commits. `enabled_pull_requests` must be `true`.
|
// either `true` to allow merging pull requests with a merge commit, or `false` to prevent merging pull requests with merge commits. `has_pull_requests` must be `true`.
|
||||||
AllowMerge *bool `json:"allow_merge_commits,omitempty"`
|
AllowMerge *bool `json:"allow_merge_commits,omitempty"`
|
||||||
// Either `true` to allow rebase-merging pull requests, or `false` to prevent rebase-merging. `enabled_pull_requests` must be `true`.
|
// either `true` to allow rebase-merging pull requests, or `false` to prevent rebase-merging. `has_pull_requests` must be `true`.
|
||||||
AllowRebase *bool `json:"allow_rebase,omitempty"`
|
AllowRebase *bool `json:"allow_rebase,omitempty"`
|
||||||
// Either `true` to allow rebase with explicit merge commits (--no-ff), or `false` to prevent rebase with explicit merge commits. `enabled_pull_requests` must be `true`.
|
// either `true` to allow rebase with explicit merge commits (--no-ff), or `false` to prevent rebase with explicit merge commits. `has_pull_requests` must be `true`.
|
||||||
AllowRebaseMerge *bool `json:"allow_rebase_explicit,omitempty"`
|
AllowRebaseMerge *bool `json:"allow_rebase_explicit,omitempty"`
|
||||||
// Either `true` to allow squash-merging pull requests, or `false` to prevent squash-merging. `enabled_pull_requests` must be `true`.
|
// either `true` to allow squash-merging pull requests, or `false` to prevent squash-merging. `has_pull_requests` must be `true`.
|
||||||
AllowSquashMerge *bool `json:"allow_squash_merge,omitempty"`
|
AllowSquash *bool `json:"allow_squash_merge,omitempty"`
|
||||||
// `true` to archive this repository. Note: You cannot unarchive repositories through the API.
|
// set to `true` to archive this repository.
|
||||||
Archived *bool `json:"archived,omitempty"`
|
Archived *bool `json:"archived,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -608,7 +608,8 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||||
|
|
||||||
m.Group("/:username/:reponame", func() {
|
m.Group("/:username/:reponame", func() {
|
||||||
m.Combo("").Get(reqAnyRepoReader(), repo.Get).
|
m.Combo("").Get(reqAnyRepoReader(), repo.Get).
|
||||||
Delete(reqToken(), reqOwner(), repo.Delete)
|
Delete(reqToken(), reqOwner(), repo.Delete).
|
||||||
|
Patch(reqToken(), reqAdmin(), bind(api.EditRepoOption{}), repo.Edit)
|
||||||
m.Group("/hooks", func() {
|
m.Group("/hooks", func() {
|
||||||
m.Combo("").Get(repo.ListHooks).
|
m.Combo("").Get(repo.ListHooks).
|
||||||
Post(bind(api.CreateHookOption{}), repo.CreateHook)
|
Post(bind(api.CreateHookOption{}), repo.CreateHook)
|
||||||
|
|
|
@ -240,6 +240,10 @@ func Create(ctx *context.APIContext, opt api.CreateRepoOption) {
|
||||||
// responses:
|
// responses:
|
||||||
// "201":
|
// "201":
|
||||||
// "$ref": "#/responses/Repository"
|
// "$ref": "#/responses/Repository"
|
||||||
|
// "409":
|
||||||
|
// description: The repository with the same name already exists.
|
||||||
|
// "422":
|
||||||
|
// "$ref": "#/responses/validationError"
|
||||||
if ctx.User.IsOrganization() {
|
if ctx.User.IsOrganization() {
|
||||||
// Shouldn't reach this condition, but just in case.
|
// Shouldn't reach this condition, but just in case.
|
||||||
ctx.Error(422, "", "not allowed creating repository for organization")
|
ctx.Error(422, "", "not allowed creating repository for organization")
|
||||||
|
@ -500,6 +504,280 @@ func GetByID(ctx *context.APIContext) {
|
||||||
ctx.JSON(200, repo.APIFormat(perm.AccessMode))
|
ctx.JSON(200, repo.APIFormat(perm.AccessMode))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Edit edit repository properties
|
||||||
|
func Edit(ctx *context.APIContext, opts api.EditRepoOption) {
|
||||||
|
// swagger:operation PATCH /repos/{owner}/{repo} repository repoEdit
|
||||||
|
// ---
|
||||||
|
// summary: Edit a repository's properties. Only fields that are set will be changed.
|
||||||
|
// produces:
|
||||||
|
// - application/json
|
||||||
|
// parameters:
|
||||||
|
// - name: owner
|
||||||
|
// in: path
|
||||||
|
// description: owner of the repo to edit
|
||||||
|
// type: string
|
||||||
|
// required: true
|
||||||
|
// - name: repo
|
||||||
|
// in: path
|
||||||
|
// description: name of the repo to edit
|
||||||
|
// type: string
|
||||||
|
// required: true
|
||||||
|
// required: true
|
||||||
|
// - name: body
|
||||||
|
// in: body
|
||||||
|
// description: "Properties of a repo that you can edit"
|
||||||
|
// schema:
|
||||||
|
// "$ref": "#/definitions/EditRepoOption"
|
||||||
|
// responses:
|
||||||
|
// "200":
|
||||||
|
// "$ref": "#/responses/Repository"
|
||||||
|
// "403":
|
||||||
|
// "$ref": "#/responses/forbidden"
|
||||||
|
// "422":
|
||||||
|
// "$ref": "#/responses/validationError"
|
||||||
|
if err := updateBasicProperties(ctx, opts); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := updateRepoUnits(ctx, opts); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.Archived != nil {
|
||||||
|
if err := updateRepoArchivedState(ctx, opts); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.JSON(http.StatusOK, ctx.Repo.Repository.APIFormat(ctx.Repo.AccessMode))
|
||||||
|
}
|
||||||
|
|
||||||
|
// updateBasicProperties updates the basic properties of a repo: Name, Description, Website and Visibility
|
||||||
|
func updateBasicProperties(ctx *context.APIContext, opts api.EditRepoOption) error {
|
||||||
|
owner := ctx.Repo.Owner
|
||||||
|
repo := ctx.Repo.Repository
|
||||||
|
|
||||||
|
oldRepoName := repo.Name
|
||||||
|
newRepoName := repo.Name
|
||||||
|
if opts.Name != nil {
|
||||||
|
newRepoName = *opts.Name
|
||||||
|
}
|
||||||
|
// Check if repository name has been changed and not just a case change
|
||||||
|
if repo.LowerName != strings.ToLower(newRepoName) {
|
||||||
|
if err := models.ChangeRepositoryName(ctx.Repo.Owner, repo.Name, newRepoName); err != nil {
|
||||||
|
switch {
|
||||||
|
case models.IsErrRepoAlreadyExist(err):
|
||||||
|
ctx.Error(http.StatusUnprocessableEntity, fmt.Sprintf("repo name is already taken [name: %s]", newRepoName), err)
|
||||||
|
case models.IsErrNameReserved(err):
|
||||||
|
ctx.Error(http.StatusUnprocessableEntity, fmt.Sprintf("repo name is reserved [name: %s]", newRepoName), err)
|
||||||
|
case models.IsErrNamePatternNotAllowed(err):
|
||||||
|
ctx.Error(http.StatusUnprocessableEntity, fmt.Sprintf("repo name's pattern is not allowed [name: %s, pattern: %s]", newRepoName, err.(models.ErrNamePatternNotAllowed).Pattern), err)
|
||||||
|
default:
|
||||||
|
ctx.Error(http.StatusUnprocessableEntity, "ChangeRepositoryName", err)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err := models.NewRepoRedirect(ctx.Repo.Owner.ID, repo.ID, repo.Name, newRepoName)
|
||||||
|
if err != nil {
|
||||||
|
ctx.Error(http.StatusUnprocessableEntity, "NewRepoRedirect", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := models.RenameRepoAction(ctx.User, oldRepoName, repo); err != nil {
|
||||||
|
log.Error("RenameRepoAction: %v", err)
|
||||||
|
ctx.Error(http.StatusInternalServerError, "RenameRepoActions", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Trace("Repository name changed: %s/%s -> %s", ctx.Repo.Owner.Name, repo.Name, newRepoName)
|
||||||
|
}
|
||||||
|
// Update the name in the repo object for the response
|
||||||
|
repo.Name = newRepoName
|
||||||
|
repo.LowerName = strings.ToLower(newRepoName)
|
||||||
|
|
||||||
|
if opts.Description != nil {
|
||||||
|
repo.Description = *opts.Description
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.Website != nil {
|
||||||
|
repo.Website = *opts.Website
|
||||||
|
}
|
||||||
|
|
||||||
|
visibilityChanged := false
|
||||||
|
if opts.Private != nil {
|
||||||
|
// Visibility of forked repository is forced sync with base repository.
|
||||||
|
if repo.IsFork {
|
||||||
|
*opts.Private = repo.BaseRepo.IsPrivate
|
||||||
|
}
|
||||||
|
|
||||||
|
visibilityChanged = repo.IsPrivate != *opts.Private
|
||||||
|
// when ForcePrivate enabled, you could change public repo to private, but only admin users can change private to public
|
||||||
|
if visibilityChanged && setting.Repository.ForcePrivate && !*opts.Private && !ctx.User.IsAdmin {
|
||||||
|
err := fmt.Errorf("cannot change private repository to public")
|
||||||
|
ctx.Error(http.StatusUnprocessableEntity, "Force Private enabled", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
repo.IsPrivate = *opts.Private
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := models.UpdateRepository(repo, visibilityChanged); err != nil {
|
||||||
|
ctx.Error(http.StatusInternalServerError, "UpdateRepository", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Trace("Repository basic settings updated: %s/%s", owner.Name, repo.Name)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func unitTypeInTypes(unitType models.UnitType, unitTypes []models.UnitType) bool {
|
||||||
|
for _, tp := range unitTypes {
|
||||||
|
if unitType == tp {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// updateRepoUnits updates repo units: Issue settings, Wiki settings, PR settings
|
||||||
|
func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error {
|
||||||
|
owner := ctx.Repo.Owner
|
||||||
|
repo := ctx.Repo.Repository
|
||||||
|
|
||||||
|
var units []models.RepoUnit
|
||||||
|
|
||||||
|
for _, tp := range models.MustRepoUnits {
|
||||||
|
units = append(units, models.RepoUnit{
|
||||||
|
RepoID: repo.ID,
|
||||||
|
Type: tp,
|
||||||
|
Config: new(models.UnitConfig),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.HasIssues != nil {
|
||||||
|
if *opts.HasIssues {
|
||||||
|
// We don't currently allow setting individual issue settings through the API,
|
||||||
|
// only can enable/disable issues, so when enabling issues,
|
||||||
|
// we either get the existing config which means it was already enabled,
|
||||||
|
// or create a new config since it doesn't exist.
|
||||||
|
unit, err := repo.GetUnit(models.UnitTypeIssues)
|
||||||
|
var config *models.IssuesConfig
|
||||||
|
if err != nil {
|
||||||
|
// Unit type doesn't exist so we make a new config file with default values
|
||||||
|
config = &models.IssuesConfig{
|
||||||
|
EnableTimetracker: true,
|
||||||
|
AllowOnlyContributorsToTrackTime: true,
|
||||||
|
EnableDependencies: true,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
config = unit.IssuesConfig()
|
||||||
|
}
|
||||||
|
units = append(units, models.RepoUnit{
|
||||||
|
RepoID: repo.ID,
|
||||||
|
Type: models.UnitTypeIssues,
|
||||||
|
Config: config,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.HasWiki != nil {
|
||||||
|
if *opts.HasWiki {
|
||||||
|
// We don't currently allow setting individual wiki settings through the API,
|
||||||
|
// only can enable/disable the wiki, so when enabling the wiki,
|
||||||
|
// we either get the existing config which means it was already enabled,
|
||||||
|
// or create a new config since it doesn't exist.
|
||||||
|
config := &models.UnitConfig{}
|
||||||
|
units = append(units, models.RepoUnit{
|
||||||
|
RepoID: repo.ID,
|
||||||
|
Type: models.UnitTypeWiki,
|
||||||
|
Config: config,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.HasPullRequests != nil {
|
||||||
|
if *opts.HasPullRequests {
|
||||||
|
// We do allow setting individual PR settings through the API, so
|
||||||
|
// we get the config settings and then set them
|
||||||
|
// if those settings were provided in the opts.
|
||||||
|
unit, err := repo.GetUnit(models.UnitTypePullRequests)
|
||||||
|
var config *models.PullRequestsConfig
|
||||||
|
if err != nil {
|
||||||
|
// Unit type doesn't exist so we make a new config file with default values
|
||||||
|
config = &models.PullRequestsConfig{
|
||||||
|
IgnoreWhitespaceConflicts: false,
|
||||||
|
AllowMerge: true,
|
||||||
|
AllowRebase: true,
|
||||||
|
AllowRebaseMerge: true,
|
||||||
|
AllowSquash: true,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
config = unit.PullRequestsConfig()
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.IgnoreWhitespaceConflicts != nil {
|
||||||
|
config.IgnoreWhitespaceConflicts = *opts.IgnoreWhitespaceConflicts
|
||||||
|
}
|
||||||
|
if opts.AllowMerge != nil {
|
||||||
|
config.AllowMerge = *opts.AllowMerge
|
||||||
|
}
|
||||||
|
if opts.AllowRebase != nil {
|
||||||
|
config.AllowRebase = *opts.AllowRebase
|
||||||
|
}
|
||||||
|
if opts.AllowRebaseMerge != nil {
|
||||||
|
config.AllowRebaseMerge = *opts.AllowRebaseMerge
|
||||||
|
}
|
||||||
|
if opts.AllowSquash != nil {
|
||||||
|
config.AllowSquash = *opts.AllowSquash
|
||||||
|
}
|
||||||
|
|
||||||
|
units = append(units, models.RepoUnit{
|
||||||
|
RepoID: repo.ID,
|
||||||
|
Type: models.UnitTypePullRequests,
|
||||||
|
Config: config,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := models.UpdateRepositoryUnits(repo, units); err != nil {
|
||||||
|
ctx.Error(http.StatusInternalServerError, "UpdateRepositoryUnits", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Trace("Repository advanced settings updated: %s/%s", owner.Name, repo.Name)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// updateRepoArchivedState updates repo's archive state
|
||||||
|
func updateRepoArchivedState(ctx *context.APIContext, opts api.EditRepoOption) error {
|
||||||
|
repo := ctx.Repo.Repository
|
||||||
|
// archive / un-archive
|
||||||
|
if opts.Archived != nil {
|
||||||
|
if repo.IsMirror {
|
||||||
|
err := fmt.Errorf("repo is a mirror, cannot archive/un-archive")
|
||||||
|
ctx.Error(http.StatusUnprocessableEntity, err.Error(), err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if *opts.Archived {
|
||||||
|
if err := repo.SetArchiveRepoState(*opts.Archived); err != nil {
|
||||||
|
log.Error("Tried to archive a repo: %s", err)
|
||||||
|
ctx.Error(http.StatusInternalServerError, "ArchiveRepoState", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Trace("Repository was archived: %s/%s", ctx.Repo.Owner.Name, repo.Name)
|
||||||
|
} else {
|
||||||
|
if err := repo.SetArchiveRepoState(*opts.Archived); err != nil {
|
||||||
|
log.Error("Tried to un-archive a repo: %s", err)
|
||||||
|
ctx.Error(http.StatusInternalServerError, "ArchiveRepoState", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Trace("Repository was un-archived: %s/%s", ctx.Repo.Owner.Name, repo.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Delete one repository
|
// Delete one repository
|
||||||
func Delete(ctx *context.APIContext) {
|
func Delete(ctx *context.APIContext) {
|
||||||
// swagger:operation DELETE /repos/{owner}/{repo} repository repoDelete
|
// swagger:operation DELETE /repos/{owner}/{repo} repository repoDelete
|
||||||
|
|
82
routers/api/v1/repo/repo_test.go
Normal file
82
routers/api/v1/repo/repo_test.go
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package repo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models"
|
||||||
|
"code.gitea.io/gitea/modules/context"
|
||||||
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
|
"code.gitea.io/gitea/modules/test"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRepoEdit(t *testing.T) {
|
||||||
|
models.PrepareTestEnv(t)
|
||||||
|
|
||||||
|
ctx := test.MockContext(t, "user2/repo1")
|
||||||
|
test.LoadRepo(t, ctx, 1)
|
||||||
|
test.LoadUser(t, ctx, 2)
|
||||||
|
ctx.Repo.Owner = ctx.User
|
||||||
|
description := "new description"
|
||||||
|
website := "http://wwww.newwebsite.com"
|
||||||
|
private := true
|
||||||
|
hasIssues := false
|
||||||
|
hasWiki := false
|
||||||
|
defaultBranch := "master"
|
||||||
|
hasPullRequests := true
|
||||||
|
ignoreWhitespaceConflicts := true
|
||||||
|
allowMerge := false
|
||||||
|
allowRebase := false
|
||||||
|
allowRebaseMerge := false
|
||||||
|
allowSquashMerge := false
|
||||||
|
archived := true
|
||||||
|
opts := api.EditRepoOption{
|
||||||
|
Name: &ctx.Repo.Repository.Name,
|
||||||
|
Description: &description,
|
||||||
|
Website: &website,
|
||||||
|
Private: &private,
|
||||||
|
HasIssues: &hasIssues,
|
||||||
|
HasWiki: &hasWiki,
|
||||||
|
DefaultBranch: &defaultBranch,
|
||||||
|
HasPullRequests: &hasPullRequests,
|
||||||
|
IgnoreWhitespaceConflicts: &ignoreWhitespaceConflicts,
|
||||||
|
AllowMerge: &allowMerge,
|
||||||
|
AllowRebase: &allowRebase,
|
||||||
|
AllowRebaseMerge: &allowRebaseMerge,
|
||||||
|
AllowSquash: &allowSquashMerge,
|
||||||
|
Archived: &archived,
|
||||||
|
}
|
||||||
|
|
||||||
|
Edit(&context.APIContext{Context: ctx, Org: nil}, opts)
|
||||||
|
|
||||||
|
assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
|
||||||
|
models.AssertExistsAndLoadBean(t, &models.Repository{
|
||||||
|
ID: 1,
|
||||||
|
}, models.Cond("name = ? AND is_archived = 1", *opts.Name))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRepoEditNameChange(t *testing.T) {
|
||||||
|
models.PrepareTestEnv(t)
|
||||||
|
|
||||||
|
ctx := test.MockContext(t, "user2/repo1")
|
||||||
|
test.LoadRepo(t, ctx, 1)
|
||||||
|
test.LoadUser(t, ctx, 2)
|
||||||
|
ctx.Repo.Owner = ctx.User
|
||||||
|
name := "newname"
|
||||||
|
opts := api.EditRepoOption{
|
||||||
|
Name: &name,
|
||||||
|
}
|
||||||
|
|
||||||
|
Edit(&context.APIContext{Context: ctx, Org: nil}, opts)
|
||||||
|
assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
|
||||||
|
|
||||||
|
models.AssertExistsAndLoadBean(t, &models.Repository{
|
||||||
|
ID: 1,
|
||||||
|
}, models.Cond("name = ?", opts.Name))
|
||||||
|
}
|
|
@ -82,6 +82,8 @@ type swaggerParameterBodies struct {
|
||||||
// in:body
|
// in:body
|
||||||
CreateRepoOption api.CreateRepoOption
|
CreateRepoOption api.CreateRepoOption
|
||||||
// in:body
|
// in:body
|
||||||
|
EditRepoOption api.EditRepoOption
|
||||||
|
// in:body
|
||||||
CreateForkOption api.CreateForkOption
|
CreateForkOption api.CreateForkOption
|
||||||
|
|
||||||
// in:body
|
// in:body
|
||||||
|
|
|
@ -1210,6 +1210,51 @@
|
||||||
"$ref": "#/responses/forbidden"
|
"$ref": "#/responses/forbidden"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"patch": {
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"repository"
|
||||||
|
],
|
||||||
|
"summary": "Edit a repository's properties. Only fields that are set will be changed.",
|
||||||
|
"operationId": "repoEdit",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "owner of the repo to edit",
|
||||||
|
"name": "owner",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "name of the repo to edit",
|
||||||
|
"name": "repo",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Properties of a repo that you can edit",
|
||||||
|
"name": "body",
|
||||||
|
"in": "body",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/EditRepoOption"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"$ref": "#/responses/Repository"
|
||||||
|
},
|
||||||
|
"403": {
|
||||||
|
"$ref": "#/responses/forbidden"
|
||||||
|
},
|
||||||
|
"422": {
|
||||||
|
"$ref": "#/responses/validationError"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/repos/{owner}/{repo}/archive/{archive}": {
|
"/repos/{owner}/{repo}/archive/{archive}": {
|
||||||
|
@ -6037,6 +6082,12 @@
|
||||||
"responses": {
|
"responses": {
|
||||||
"201": {
|
"201": {
|
||||||
"$ref": "#/responses/Repository"
|
"$ref": "#/responses/Repository"
|
||||||
|
},
|
||||||
|
"409": {
|
||||||
|
"description": "The repository with the same name already exists."
|
||||||
|
},
|
||||||
|
"422": {
|
||||||
|
"$ref": "#/responses/validationError"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7738,6 +7789,84 @@
|
||||||
},
|
},
|
||||||
"x-go-package": "code.gitea.io/gitea/modules/structs"
|
"x-go-package": "code.gitea.io/gitea/modules/structs"
|
||||||
},
|
},
|
||||||
|
"EditRepoOption": {
|
||||||
|
"description": "EditRepoOption options when editing a repository's properties",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"allow_merge_commits": {
|
||||||
|
"description": "either `true` to allow merging pull requests with a merge commit, or `false` to prevent merging pull requests with merge commits. `has_pull_requests` must be `true`.",
|
||||||
|
"type": "boolean",
|
||||||
|
"x-go-name": "AllowMerge"
|
||||||
|
},
|
||||||
|
"allow_rebase": {
|
||||||
|
"description": "either `true` to allow rebase-merging pull requests, or `false` to prevent rebase-merging. `has_pull_requests` must be `true`.",
|
||||||
|
"type": "boolean",
|
||||||
|
"x-go-name": "AllowRebase"
|
||||||
|
},
|
||||||
|
"allow_rebase_explicit": {
|
||||||
|
"description": "either `true` to allow rebase with explicit merge commits (--no-ff), or `false` to prevent rebase with explicit merge commits. `has_pull_requests` must be `true`.",
|
||||||
|
"type": "boolean",
|
||||||
|
"x-go-name": "AllowRebaseMerge"
|
||||||
|
},
|
||||||
|
"allow_squash_merge": {
|
||||||
|
"description": "either `true` to allow squash-merging pull requests, or `false` to prevent squash-merging. `has_pull_requests` must be `true`.",
|
||||||
|
"type": "boolean",
|
||||||
|
"x-go-name": "AllowSquash"
|
||||||
|
},
|
||||||
|
"archived": {
|
||||||
|
"description": "set to `true` to archive this repository.",
|
||||||
|
"type": "boolean",
|
||||||
|
"x-go-name": "Archived"
|
||||||
|
},
|
||||||
|
"default_branch": {
|
||||||
|
"description": "sets the default branch for this repository.",
|
||||||
|
"type": "string",
|
||||||
|
"x-go-name": "DefaultBranch"
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"description": "a short description of the repository.",
|
||||||
|
"type": "string",
|
||||||
|
"x-go-name": "Description"
|
||||||
|
},
|
||||||
|
"has_issues": {
|
||||||
|
"description": "either `true` to enable issues for this repository or `false` to disable them.",
|
||||||
|
"type": "boolean",
|
||||||
|
"x-go-name": "HasIssues"
|
||||||
|
},
|
||||||
|
"has_pull_requests": {
|
||||||
|
"description": "either `true` to allow pull requests, or `false` to prevent pull request.",
|
||||||
|
"type": "boolean",
|
||||||
|
"x-go-name": "HasPullRequests"
|
||||||
|
},
|
||||||
|
"has_wiki": {
|
||||||
|
"description": "either `true` to enable the wiki for this repository or `false` to disable it.",
|
||||||
|
"type": "boolean",
|
||||||
|
"x-go-name": "HasWiki"
|
||||||
|
},
|
||||||
|
"ignore_whitespace_conflicts": {
|
||||||
|
"description": "either `true` to ignore whitespace for conflicts, or `false` to not ignore whitespace. `has_pull_requests` must be `true`.",
|
||||||
|
"type": "boolean",
|
||||||
|
"x-go-name": "IgnoreWhitespaceConflicts"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"description": "name of the repository",
|
||||||
|
"type": "string",
|
||||||
|
"uniqueItems": true,
|
||||||
|
"x-go-name": "Name"
|
||||||
|
},
|
||||||
|
"private": {
|
||||||
|
"description": "either `true` to make the repository private or `false` to make it public.\nNote: you will get a 422 error if the organization restricts changing repository visibility to organization\nowners and a non-owner tries to change the value of private.",
|
||||||
|
"type": "boolean",
|
||||||
|
"x-go-name": "Private"
|
||||||
|
},
|
||||||
|
"website": {
|
||||||
|
"description": "a URL with more information about the repository.",
|
||||||
|
"type": "string",
|
||||||
|
"x-go-name": "Website"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-go-package": "code.gitea.io/gitea/modules/structs"
|
||||||
|
},
|
||||||
"EditTeamOption": {
|
"EditTeamOption": {
|
||||||
"description": "EditTeamOption options for editing a team",
|
"description": "EditTeamOption options for editing a team",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
|
@ -9062,6 +9191,22 @@
|
||||||
"description": "Repository represents a repository",
|
"description": "Repository represents a repository",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"allow_merge_commits": {
|
||||||
|
"type": "boolean",
|
||||||
|
"x-go-name": "AllowMerge"
|
||||||
|
},
|
||||||
|
"allow_rebase": {
|
||||||
|
"type": "boolean",
|
||||||
|
"x-go-name": "AllowRebase"
|
||||||
|
},
|
||||||
|
"allow_rebase_explicit": {
|
||||||
|
"type": "boolean",
|
||||||
|
"x-go-name": "AllowRebaseMerge"
|
||||||
|
},
|
||||||
|
"allow_squash_merge": {
|
||||||
|
"type": "boolean",
|
||||||
|
"x-go-name": "AllowSquash"
|
||||||
|
},
|
||||||
"archived": {
|
"archived": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"x-go-name": "Archived"
|
"x-go-name": "Archived"
|
||||||
|
@ -9104,6 +9249,18 @@
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"x-go-name": "FullName"
|
"x-go-name": "FullName"
|
||||||
},
|
},
|
||||||
|
"has_issues": {
|
||||||
|
"type": "boolean",
|
||||||
|
"x-go-name": "HasIssues"
|
||||||
|
},
|
||||||
|
"has_pull_requests": {
|
||||||
|
"type": "boolean",
|
||||||
|
"x-go-name": "HasPullRequests"
|
||||||
|
},
|
||||||
|
"has_wiki": {
|
||||||
|
"type": "boolean",
|
||||||
|
"x-go-name": "HasWiki"
|
||||||
|
},
|
||||||
"html_url": {
|
"html_url": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"x-go-name": "HTMLURL"
|
"x-go-name": "HTMLURL"
|
||||||
|
@ -9113,6 +9270,10 @@
|
||||||
"format": "int64",
|
"format": "int64",
|
||||||
"x-go-name": "ID"
|
"x-go-name": "ID"
|
||||||
},
|
},
|
||||||
|
"ignore_whitespace_conflicts": {
|
||||||
|
"type": "boolean",
|
||||||
|
"x-go-name": "IgnoreWhitespaceConflicts"
|
||||||
|
},
|
||||||
"mirror": {
|
"mirror": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"x-go-name": "Mirror"
|
"x-go-name": "Mirror"
|
||||||
|
|
Reference in a new issue