* 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_requeststags/v1.21.12.1
| @@ -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) | |||
| 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++ | |||
| treePath = fmt.Sprintf("delete/file%d.txt", fileID) | |||
| createFile(user2, repo16, treePath) | |||
| @@ -162,8 +162,8 @@ func CreateOrganization(org, owner *User) (err error) { | |||
| } | |||
| // insert units for team | |||
| var units = make([]TeamUnit, 0, len(allRepUnitTypes)) | |||
| for _, tp := range allRepUnitTypes { | |||
| var units = make([]TeamUnit, 0, len(AllRepoUnitTypes)) | |||
| for _, tp := range AllRepoUnitTypes { | |||
| units = append(units, TeamUnit{ | |||
| OrgID: org.ID, | |||
| TeamID: t.ID, | |||
| @@ -274,32 +274,64 @@ func (repo *Repository) innerAPIFormat(e Engine, mode AccessMode, isParent bool) | |||
| 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{ | |||
| ID: repo.ID, | |||
| Owner: repo.Owner.APIFormat(), | |||
| Name: repo.Name, | |||
| FullName: repo.FullName(), | |||
| Description: repo.Description, | |||
| Private: repo.IsPrivate, | |||
| Empty: repo.IsEmpty, | |||
| Archived: repo.IsArchived, | |||
| Size: int(repo.Size / 1024), | |||
| Fork: repo.IsFork, | |||
| Parent: parent, | |||
| Mirror: repo.IsMirror, | |||
| HTMLURL: repo.HTMLURL(), | |||
| SSHURL: cloneLink.SSH, | |||
| CloneURL: cloneLink.HTTPS, | |||
| Website: repo.Website, | |||
| Stars: repo.NumStars, | |||
| Forks: repo.NumForks, | |||
| Watchers: repo.NumWatches, | |||
| OpenIssues: repo.NumOpenIssues, | |||
| DefaultBranch: repo.DefaultBranch, | |||
| Created: repo.CreatedUnix.AsTime(), | |||
| Updated: repo.UpdatedUnix.AsTime(), | |||
| Permissions: permission, | |||
| AvatarURL: repo.AvatarLink(), | |||
| ID: repo.ID, | |||
| Owner: repo.Owner.APIFormat(), | |||
| Name: repo.Name, | |||
| FullName: repo.FullName(), | |||
| Description: repo.Description, | |||
| Private: repo.IsPrivate, | |||
| Empty: repo.IsEmpty, | |||
| Archived: repo.IsArchived, | |||
| Size: int(repo.Size / 1024), | |||
| Fork: repo.IsFork, | |||
| Parent: parent, | |||
| Mirror: repo.IsMirror, | |||
| HTMLURL: repo.HTMLURL(), | |||
| SSHURL: cloneLink.SSH, | |||
| CloneURL: cloneLink.HTTPS, | |||
| Website: repo.Website, | |||
| Stars: repo.NumStars, | |||
| Forks: repo.NumForks, | |||
| Watchers: repo.NumWatches, | |||
| OpenIssues: repo.NumOpenIssues, | |||
| DefaultBranch: repo.DefaultBranch, | |||
| Created: repo.CreatedUnix.AsTime(), | |||
| Updated: repo.UpdatedUnix.AsTime(), | |||
| Permissions: permission, | |||
| HasIssues: hasIssues, | |||
| HasWiki: hasWiki, | |||
| HasPullRequests: hasPullRequests, | |||
| IgnoreWhitespaceConflicts: ignoreWhitespaceConflicts, | |||
| AllowMerge: allowMerge, | |||
| AllowRebase: allowRebase, | |||
| AllowRebaseMerge: allowRebaseMerge, | |||
| AllowSquash: allowSquash, | |||
| AvatarURL: repo.AvatarLink(), | |||
| } | |||
| } | |||
| @@ -346,10 +378,20 @@ func (repo *Repository) UnitEnabled(tp UnitType) bool { | |||
| return false | |||
| } | |||
| var ( | |||
| // ErrUnitNotExist organization does not exist | |||
| ErrUnitNotExist = errors.New("Unit does not exist") | |||
| ) | |||
| // ErrUnitTypeNotExist represents a "UnitTypeNotExist" kind of error. | |||
| type ErrUnitTypeNotExist struct { | |||
| 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 | |||
| func (repo *Repository) MustGetUnit(tp UnitType) *RepoUnit { | |||
| @@ -373,6 +415,11 @@ func (repo *Repository) MustGetUnit(tp UnitType) *RepoUnit { | |||
| Type: tp, | |||
| Config: new(PullRequestsConfig), | |||
| } | |||
| } else if tp == UnitTypeIssues { | |||
| return &RepoUnit{ | |||
| Type: tp, | |||
| Config: new(IssuesConfig), | |||
| } | |||
| } | |||
| return &RepoUnit{ | |||
| Type: tp, | |||
| @@ -394,7 +441,7 @@ func (repo *Repository) getUnit(e Engine, tp UnitType) (*RepoUnit, error) { | |||
| return unit, nil | |||
| } | |||
| } | |||
| return nil, ErrUnitNotExist | |||
| return nil, ErrUnitTypeNotExist{tp} | |||
| } | |||
| 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 | |||
| var units = make([]RepoUnit, 0, len(defaultRepoUnits)) | |||
| for _, tp := range defaultRepoUnits { | |||
| var units = make([]RepoUnit, 0, len(DefaultRepoUnits)) | |||
| for _, tp := range DefaultRepoUnits { | |||
| if tp == UnitTypeIssues { | |||
| units = append(units, RepoUnit{ | |||
| RepoID: repo.ID, | |||
| @@ -58,8 +58,8 @@ func (u UnitType) ColorFormat(s fmt.State) { | |||
| } | |||
| var ( | |||
| // allRepUnitTypes contains all the unit types | |||
| allRepUnitTypes = []UnitType{ | |||
| // AllRepoUnitTypes contains all the unit types | |||
| AllRepoUnitTypes = []UnitType{ | |||
| UnitTypeCode, | |||
| UnitTypeIssues, | |||
| UnitTypePullRequests, | |||
| @@ -69,8 +69,8 @@ var ( | |||
| UnitTypeExternalTracker, | |||
| } | |||
| // defaultRepoUnits contains the default unit types | |||
| defaultRepoUnits = []UnitType{ | |||
| // DefaultRepoUnits contains the default unit types | |||
| DefaultRepoUnits = []UnitType{ | |||
| UnitTypeCode, | |||
| UnitTypeIssues, | |||
| UnitTypePullRequests, | |||
| @@ -41,9 +41,17 @@ type Repository struct { | |||
| // swagger:strfmt date-time | |||
| Created time.Time `json:"created_at"` | |||
| // swagger:strfmt date-time | |||
| Updated time.Time `json:"updated_at"` | |||
| Permissions *Permission `json:"permissions,omitempty"` | |||
| AvatarURL string `json:"avatar_url"` | |||
| Updated time.Time `json:"updated_at"` | |||
| 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"` | |||
| } | |||
| // CreateRepoOption options when creating repository | |||
| @@ -71,38 +79,36 @@ type CreateRepoOption struct { | |||
| // EditRepoOption options when editing a repository's properties | |||
| // swagger:model | |||
| type EditRepoOption struct { | |||
| // Name of the repository | |||
| // | |||
| // required: true | |||
| // name of the repository | |||
| // unique: true | |||
| Name *string `json:"name" binding:"Required;AlphaDashDot;MaxSize(100)"` | |||
| // A short description of the repository. | |||
| Name *string `json:"name,omitempty" binding:"OmitEmpty;AlphaDashDot;MaxSize(100);"` | |||
| // a short description of the repository. | |||
| 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)"` | |||
| // 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 | |||
| // 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 | |||
| // owners and a non-owner tries to change the value of private. | |||
| Private *bool `json:"private,omitempty"` | |||
| // Either `true` to enable issues for this repository or `false` to disable them. | |||
| EnableIssues *bool `json:"enable_issues,omitempty"` | |||
| // Either `true` to enable the wiki for this repository or `false` to disable it. | |||
| EnableWiki *bool `json:"enable_wiki,omitempty"` | |||
| // Updates the default branch for this repository. | |||
| // either `true` to enable issues for this repository or `false` to disable them. | |||
| HasIssues *bool `json:"has_issues,omitempty"` | |||
| // either `true` to enable the wiki for this repository or `false` to disable it. | |||
| HasWiki *bool `json:"has_wiki,omitempty"` | |||
| // sets the default branch for this repository. | |||
| DefaultBranch *string `json:"default_branch,omitempty"` | |||
| // Either `true` to allow pull requests, or `false` to prevent pull request. | |||
| EnablePullRequests *bool `json:"enable_pull_requests,omitempty"` | |||
| // Either `true` to ignore whitepace for conflicts, or `false` to not ignore whitespace. `enabled_pull_requests` must be `true`. | |||
| IgnoreWhitespaceConflicts *bool `json:"ignore_whitespace,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 pull requests, or `false` to prevent pull request. | |||
| HasPullRequests *bool `json:"has_pull_requests,omitempty"` | |||
| // either `true` to ignore whitespace for conflicts, or `false` to not ignore whitespace. `has_pull_requests` must be `true`. | |||
| 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. `has_pull_requests` must be `true`. | |||
| 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"` | |||
| // 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"` | |||
| // Either `true` to allow squash-merging pull requests, or `false` to prevent squash-merging. `enabled_pull_requests` must be `true`. | |||
| AllowSquashMerge *bool `json:"allow_squash_merge,omitempty"` | |||
| // `true` to archive this repository. Note: You cannot unarchive repositories through the API. | |||
| // either `true` to allow squash-merging pull requests, or `false` to prevent squash-merging. `has_pull_requests` must be `true`. | |||
| AllowSquash *bool `json:"allow_squash_merge,omitempty"` | |||
| // set to `true` to archive this repository. | |||
| Archived *bool `json:"archived,omitempty"` | |||
| } | |||
| @@ -608,7 +608,8 @@ func RegisterRoutes(m *macaron.Macaron) { | |||
| m.Group("/:username/:reponame", func() { | |||
| 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.Combo("").Get(repo.ListHooks). | |||
| Post(bind(api.CreateHookOption{}), repo.CreateHook) | |||
| @@ -240,6 +240,10 @@ func Create(ctx *context.APIContext, opt api.CreateRepoOption) { | |||
| // responses: | |||
| // "201": | |||
| // "$ref": "#/responses/Repository" | |||
| // "409": | |||
| // description: The repository with the same name already exists. | |||
| // "422": | |||
| // "$ref": "#/responses/validationError" | |||
| if ctx.User.IsOrganization() { | |||
| // Shouldn't reach this condition, but just in case. | |||
| 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)) | |||
| } | |||
| // 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 | |||
| func Delete(ctx *context.APIContext) { | |||
| // swagger:operation DELETE /repos/{owner}/{repo} repository repoDelete | |||
| @@ -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 | |||
| CreateRepoOption api.CreateRepoOption | |||
| // in:body | |||
| EditRepoOption api.EditRepoOption | |||
| // in:body | |||
| CreateForkOption api.CreateForkOption | |||
| // in:body | |||
| @@ -1210,6 +1210,51 @@ | |||
| "$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}": { | |||
| @@ -6037,6 +6082,12 @@ | |||
| "responses": { | |||
| "201": { | |||
| "$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" | |||
| }, | |||
| "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": { | |||
| "description": "EditTeamOption options for editing a team", | |||
| "type": "object", | |||
| @@ -9062,6 +9191,22 @@ | |||
| "description": "Repository represents a repository", | |||
| "type": "object", | |||
| "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": { | |||
| "type": "boolean", | |||
| "x-go-name": "Archived" | |||
| @@ -9104,6 +9249,18 @@ | |||
| "type": "string", | |||
| "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": { | |||
| "type": "string", | |||
| "x-go-name": "HTMLURL" | |||
| @@ -9113,6 +9270,10 @@ | |||
| "format": "int64", | |||
| "x-go-name": "ID" | |||
| }, | |||
| "ignore_whitespace_conflicts": { | |||
| "type": "boolean", | |||
| "x-go-name": "IgnoreWhitespaceConflicts" | |||
| }, | |||
| "mirror": { | |||
| "type": "boolean", | |||
| "x-go-name": "Mirror" | |||