From cda61ea8264a9a6c70c0b0200111d9b8e6e888db Mon Sep 17 00:00:00 2001 From: Richard Mahn Date: Fri, 10 May 2019 16:19:27 -0400 Subject: [PATCH 01/30] Feature - #5960 - API Endpoint for Repo Editing --- models/org.go | 4 +- models/repo.go | 29 ++- models/unit.go | 8 +- routers/api/v1/api.go | 3 +- routers/api/v1/repo/repo.go | 403 ++++++++++++++++++++++++++---- routers/api/v1/swagger/options.go | 2 + templates/swagger/v1_json.tmpl | 137 +++++++++- 7 files changed, 514 insertions(+), 72 deletions(-) diff --git a/models/org.go b/models/org.go index b7db32ef1669..6511072e2b7b 100644 --- a/models/org.go +++ b/models/org.go @@ -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, diff --git a/models/repo.go b/models/repo.go index e8af9aa2db6d..3cb07c4982b4 100644 --- a/models/repo.go +++ b/models/repo.go @@ -377,10 +377,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"Unit does not exist" +} // MustGetUnit always returns a RepoUnit object func (repo *Repository) MustGetUnit(tp UnitType) *RepoUnit { @@ -404,6 +414,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, @@ -425,7 +440,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) { @@ -1320,8 +1335,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, diff --git a/models/unit.go b/models/unit.go index 697df696bc11..9f5c8d3cbbf6 100644 --- a/models/unit.go +++ b/models/unit.go @@ -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, diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index b68bf4b0b8ee..4c9f1f9f7b16 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -601,7 +601,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(), reqOwner(), bind(api.EditRepoOption{}), repo.Edit) m.Group("/hooks", func() { m.Combo("").Get(repo.ListHooks). Post(bind(api.CreateHookOption{}), repo.CreateHook) diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go index 35fea20d4978..92b8b6c2f531 100644 --- a/routers/api/v1/repo/repo.go +++ b/routers/api/v1/repo/repo.go @@ -7,6 +7,7 @@ package repo import ( "fmt" + "github.com/davecgh/go-spew/spew" "net/http" "strings" @@ -147,7 +148,7 @@ func Search(ctx *context.APIContext) { } else { repoOwner, err = models.GetUserByID(opts.OwnerID) if err != nil { - ctx.JSON(500, api.SearchError{ + ctx.Error(http.StatusInternalServerError, err.Error(), api.SearchError{ OK: false, Error: err.Error(), }) @@ -166,7 +167,7 @@ func Search(ctx *context.APIContext) { } else if repoOwner.IsOrganization() { opts.Private, err = repoOwner.IsOwnedBy(ctx.User.ID) if err != nil { - ctx.JSON(500, api.SearchError{ + ctx.Error(http.StatusInternalServerError, err.Error(), api.SearchError{ OK: false, Error: err.Error(), }) @@ -178,7 +179,7 @@ func Search(ctx *context.APIContext) { repos, count, err := models.SearchRepositoryByName(opts) if err != nil { - ctx.JSON(500, api.SearchError{ + ctx.Error(http.StatusInternalServerError, err.Error(), api.SearchError{ OK: false, Error: err.Error(), }) @@ -188,7 +189,7 @@ func Search(ctx *context.APIContext) { results := make([]*api.Repository, len(repos)) for i, repo := range repos { if err = repo.GetOwner(); err != nil { - ctx.JSON(500, api.SearchError{ + ctx.Error(http.StatusInternalServerError, err.Error(), api.SearchError{ OK: false, Error: err.Error(), }) @@ -196,7 +197,7 @@ func Search(ctx *context.APIContext) { } accessMode, err := models.AccessLevel(ctx.User, repo) if err != nil { - ctx.JSON(500, api.SearchError{ + ctx.Error(http.StatusInternalServerError, err.Error(), api.SearchError{ OK: false, Error: err.Error(), }) @@ -206,7 +207,7 @@ func Search(ctx *context.APIContext) { ctx.SetLinkHeader(int(count), setting.API.MaxResponseItems) ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) - ctx.JSON(200, api.SearchResults{ + ctx.Error(http.StatusOK, err.Error(), api.SearchResults{ OK: true, Data: results, }) @@ -228,22 +229,22 @@ func CreateUserRepo(ctx *context.APIContext, owner *models.User, opt api.CreateR }) if err != nil { if models.IsErrRepoAlreadyExist(err) { - ctx.Error(409, "", "The repository with the same name already exists.") + ctx.Error(http.StatusConflict, "", "The repository with the same name already exists.") } else if models.IsErrNameReserved(err) || models.IsErrNamePatternNotAllowed(err) { - ctx.Error(422, "", err) + ctx.Error(http.StatusUnprocessableEntity, "", err) } else { if repo != nil { if err = models.DeleteRepository(ctx.User, ctx.User.ID, repo.ID); err != nil { log.Error("DeleteRepository: %v", err) } } - ctx.Error(500, "CreateRepository", err) + ctx.Error(http.StatusInternalServerError, "CreateRepository", err) } return } - ctx.JSON(201, repo.APIFormat(models.AccessModeOwner)) + ctx.JSON(http.StatusCreated, repo.APIFormat(models.AccessModeOwner)) } // Create one repository of mine @@ -265,7 +266,7 @@ func Create(ctx *context.APIContext, opt api.CreateRepoOption) { // "$ref": "#/responses/Repository" if ctx.User.IsOrganization() { // Shouldn't reach this condition, but just in case. - ctx.Error(422, "", "not allowed creating repository for organization") + ctx.Error(http.StatusUnprocessableEntity, "", "not allowed creating repository for organization") return } CreateUserRepo(ctx, ctx.User, opt) @@ -300,9 +301,9 @@ func CreateOrgRepo(ctx *context.APIContext, opt api.CreateRepoOption) { org, err := models.GetOrgByName(ctx.Params(":org")) if err != nil { if models.IsErrOrgNotExist(err) { - ctx.Error(422, "", err) + ctx.Error(http.StatusUnprocessableEntity, "", err) } else { - ctx.Error(500, "GetOrgByName", err) + ctx.Error(http.StatusInternalServerError, "GetOrgByName", err) } return } @@ -315,10 +316,10 @@ func CreateOrgRepo(ctx *context.APIContext, opt api.CreateRepoOption) { if !ctx.User.IsAdmin { isOwner, err := org.IsOwnedBy(ctx.User.ID) if err != nil { - ctx.ServerError("IsOwnedBy", err) + ctx.Error(http.StatusInternalServerError, "IsOwnedBy", err) return } else if !isOwner { - ctx.Error(403, "", "Given user is not owner of organization.") + ctx.Error(http.StatusForbidden, "", "Given user is not owner of organization.") return } } @@ -349,9 +350,9 @@ func Migrate(ctx *context.APIContext, form auth.MigrateRepoForm) { org, err := models.GetUserByID(form.UID) if err != nil { if models.IsErrUserNotExist(err) { - ctx.Error(422, "", err) + ctx.Error(http.StatusUnprocessableEntity, "", err) } else { - ctx.Error(500, "GetUserByID", err) + ctx.Error(http.StatusInternalServerError, "GetUserByID", err) } return } @@ -359,13 +360,13 @@ func Migrate(ctx *context.APIContext, form auth.MigrateRepoForm) { } if ctx.HasError() { - ctx.Error(422, "", ctx.GetErrMsg()) + ctx.Error(http.StatusUnprocessableEntity, "", ctx.GetErrMsg()) return } if !ctx.User.IsAdmin { if !ctxUser.IsOrganization() && ctx.User.ID != ctxUser.ID { - ctx.Error(403, "", "Given user is not an organization.") + ctx.Error(http.StatusForbidden, "", "Given user is not an organization.") return } @@ -373,10 +374,10 @@ func Migrate(ctx *context.APIContext, form auth.MigrateRepoForm) { // Check ownership of organization. isOwner, err := ctxUser.IsOwnedBy(ctx.User.ID) if err != nil { - ctx.Error(500, "IsOwnedBy", err) + ctx.Error(http.StatusInternalServerError, "IsOwnedBy", err) return } else if !isOwner { - ctx.Error(403, "", "Given user is not owner of organization.") + ctx.Error(http.StatusForbidden, "", "Given user is not owner of organization.") return } } @@ -388,16 +389,16 @@ func Migrate(ctx *context.APIContext, form auth.MigrateRepoForm) { addrErr := err.(models.ErrInvalidCloneAddr) switch { case addrErr.IsURLError: - ctx.Error(422, "", err) + ctx.Error(http.StatusUnprocessableEntity, "", err) case addrErr.IsPermissionDenied: - ctx.Error(422, "", "You are not allowed to import local repositories.") + ctx.Error(http.StatusUnprocessableEntity, "", "You are not allowed to import local repositories.") case addrErr.IsInvalidPath: - ctx.Error(422, "", "Invalid local path, it does not exist or not a directory.") + ctx.Error(http.StatusUnprocessableEntity, "", "Invalid local path, it does not exist or not a directory.") default: - ctx.Error(500, "ParseRemoteAddr", "Unknown error type (ErrInvalidCloneAddr): "+err.Error()) + ctx.Error(http.StatusInternalServerError, "ParseRemoteAddr", "Unknown error type (ErrInvalidCloneAddr): "+err.Error()) } } else { - ctx.Error(500, "ParseRemoteAddr", err) + ctx.Error(http.StatusInternalServerError, "ParseRemoteAddr", err) } return } @@ -430,33 +431,33 @@ func Migrate(ctx *context.APIContext, form auth.MigrateRepoForm) { repo, err := migrations.MigrateRepository(ctx.User, ctxUser.Name, opts) if err == nil { log.Trace("Repository migrated: %s/%s", ctxUser.Name, form.RepoName) - ctx.JSON(201, repo.APIFormat(models.AccessModeAdmin)) + ctx.JSON(http.StatusCreated, repo.APIFormat(models.AccessModeAdmin)) return } switch { case models.IsErrRepoAlreadyExist(err): - ctx.Error(409, "", "The repository with the same name already exists.") + ctx.Error(http.StatusConflict, "", "The repository with the same name already exists.") case migrations.IsRateLimitError(err): - ctx.Error(422, "", "Remote visit addressed rate limitation.") + ctx.Error(http.StatusUnprocessableEntity, "", "Remote visit addressed rate limitation.") case migrations.IsTwoFactorAuthError(err): - ctx.Error(422, "", "Remote visit required two factors authentication.") + ctx.Error(http.StatusUnprocessableEntity, "", "Remote visit required two factors authentication.") case models.IsErrReachLimitOfRepo(err): - ctx.Error(422, "", fmt.Sprintf("You have already reached your limit of %d repositories.", ctxUser.MaxCreationLimit())) + ctx.Error(http.StatusUnprocessableEntity, "", fmt.Sprintf("You have already reached your limit of %d repositories.", ctxUser.MaxCreationLimit())) case models.IsErrNameReserved(err): - ctx.Error(422, "", fmt.Sprintf("The username '%s' is reserved.", err.(models.ErrNameReserved).Name)) + ctx.Error(http.StatusUnprocessableEntity, "", fmt.Sprintf("The username '%s' is reserved.", err.(models.ErrNameReserved).Name)) case models.IsErrNamePatternNotAllowed(err): - ctx.Error(422, "", fmt.Sprintf("The pattern '%s' is not allowed in a username.", err.(models.ErrNamePatternNotAllowed).Pattern)) + ctx.Error(http.StatusUnprocessableEntity, "", fmt.Sprintf("The pattern '%s' is not allowed in a username.", err.(models.ErrNamePatternNotAllowed).Pattern)) default: err = util.URLSanitizedError(err, remoteAddr) if strings.Contains(err.Error(), "Authentication failed") || strings.Contains(err.Error(), "Bad credentials") || strings.Contains(err.Error(), "could not read Username") { - ctx.Error(422, "", fmt.Sprintf("Authentication failed: %v.", err)) + ctx.Error(http.StatusUnprocessableEntity, "", fmt.Sprintf("Authentication failed: %v.", err)) } else if strings.Contains(err.Error(), "fatal:") { - ctx.Error(422, "", fmt.Sprintf("Migration failed: %v.", err)) + ctx.Error(http.StatusUnprocessableEntity, "", fmt.Sprintf("Migration failed: %v.", err)) } else { - ctx.Error(500, "MigrateRepository", err) + ctx.Error(http.StatusInternalServerError, "MigrateRepository", err) } } } @@ -482,7 +483,7 @@ func Get(ctx *context.APIContext) { // responses: // "200": // "$ref": "#/responses/Repository" - ctx.JSON(200, ctx.Repo.Repository.APIFormat(ctx.Repo.AccessMode)) + ctx.JSON(http.StatusOK, ctx.Repo.Repository.APIFormat(ctx.Repo.AccessMode)) } // GetByID returns a single Repository @@ -507,20 +508,316 @@ func GetByID(ctx *context.APIContext) { if models.IsErrRepoNotExist(err) { ctx.NotFound() } else { - ctx.Error(500, "GetRepositoryByID", err) + ctx.Error(http.StatusInternalServerError, "GetRepositoryByID", err) } return } perm, err := models.GetUserRepoPermission(repo, ctx.User) if err != nil { - ctx.Error(500, "AccessLevel", err) + ctx.Error(http.StatusInternalServerError, "AccessLevel", err) return } else if !perm.HasAccess() { ctx.NotFound() return } - ctx.JSON(200, repo.APIFormat(perm.AccessMode)) + ctx.JSON(http.StatusOK, 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" + owner := ctx.Repo.Owner + + if owner.IsOrganization() && !ctx.User.IsAdmin { + isOwner, err := owner.IsOwnedBy(ctx.User.ID) + if err != nil { + ctx.Error(http.StatusInternalServerError, "IsOwnedBy", err) + return + } else if !isOwner { + ctx.Error(http.StatusForbidden, "", "Given user is not owner of organization.") + return + } + } + + 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 + + spew.Dump(opts) + + if opts.Name != nil { + oldRepoName := repo.Name + 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 ther 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 + var unitTypesProcessed []models.UnitType + + if opts.EnableIssues != nil { + if *opts.EnableIssues { + // 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: unit.Type, + Config: config, + }) + } + unitTypesProcessed = append(unitTypesProcessed, models.UnitTypeIssues) + } + + if opts.EnableWiki != nil { + if *opts.EnableWiki { + // 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, + }) + } + unitTypesProcessed = append(unitTypesProcessed, models.UnitTypeWiki) + } + + if opts.EnablePullRequests != nil { + if *opts.EnablePullRequests { + // 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 + } + + units = append(units, models.RepoUnit{ + RepoID: repo.ID, + Type: models.UnitTypePullRequests, + Config: config, + }) + } + unitTypesProcessed = append(unitTypesProcessed, models.UnitTypePullRequests) + } + + // Put back onto the units array the unit types we didn't process due to not being in the API payload (if they + // exist). + // This will also get the types that "must" always exist, and makes sure they exist + for _, tp := range models.AllRepoUnitTypes { + if ! unitTypeInTypes(tp, unitTypesProcessed) { + if unit, _ := repo.GetUnit(tp); unit != nil { + units = append(units, *unit) + } else { + if unitTypeInTypes(tp, models.MustRepoUnits) { + units = append(units, models.RepoUnit{ + RepoID: repo.ID, + Type: tp, + Config: new(models.UnitConfig), + }) + } + } + } + } + + 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 @@ -552,21 +849,21 @@ func Delete(ctx *context.APIContext) { if owner.IsOrganization() && !ctx.User.IsAdmin { isOwner, err := owner.IsOwnedBy(ctx.User.ID) if err != nil { - ctx.Error(500, "IsOwnedBy", err) + ctx.Error(http.StatusInternalServerError, "IsOwnedBy", err) return } else if !isOwner { - ctx.Error(403, "", "Given user is not owner of organization.") + ctx.Error(http.StatusForbidden, "", "Given user is not owner of organization.") return } } if err := models.DeleteRepository(ctx.User, owner.ID, repo.ID); err != nil { - ctx.Error(500, "DeleteRepository", err) + ctx.Error(http.StatusInternalServerError, "DeleteRepository", err) return } log.Trace("Repository deleted: %s/%s", owner.Name, repo.Name) - ctx.Status(204) + ctx.Status(http.StatusNoContent) } // MirrorSync adds a mirrored repository to the sync queue @@ -593,15 +890,15 @@ func MirrorSync(ctx *context.APIContext) { repo := ctx.Repo.Repository if !ctx.Repo.CanWrite(models.UnitTypeCode) { - ctx.Error(403, "MirrorSync", "Must have write access") + ctx.Error(http.StatusForbidden, "MirrorSync", "Must have write access") } go models.MirrorQueue.Add(repo.ID) - ctx.Status(200) + ctx.Status(http.StatusOK) } // TopicSearch search for creating topic -func TopicSearch(ctx *context.Context) { +func TopicSearch(ctx *context.APIContext) { // swagger:operation GET /topics/search repository topicSearch // --- // summary: search topics via keyword @@ -617,9 +914,8 @@ func TopicSearch(ctx *context.Context) { // "200": // "$ref": "#/responses/Repository" if ctx.User == nil { - ctx.JSON(403, map[string]interface{}{ - "message": "Only owners could change the topics.", - }) + err := fmt.Errorf("only owners could change the topics") + ctx.Error(http.StatusForbidden, err.Error(), err) return } @@ -631,13 +927,12 @@ func TopicSearch(ctx *context.Context) { }) if err != nil { log.Error("SearchTopics failed: %v", err) - ctx.JSON(500, map[string]interface{}{ - "message": "Search topics failed.", - }) + err := fmt.Errorf("search topics failed") + ctx.Error(http.StatusInternalServerError, err.Error(), err) return } - ctx.JSON(200, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]interface{}{ "topics": topics, }) } diff --git a/routers/api/v1/swagger/options.go b/routers/api/v1/swagger/options.go index 83f7cfec8dc8..9fc2cf8a6ec6 100644 --- a/routers/api/v1/swagger/options.go +++ b/routers/api/v1/swagger/options.go @@ -82,6 +82,8 @@ type swaggerParameterBodies struct { // in:body CreateRepoOption api.CreateRepoOption // in:body + EditRepoOption api.EditRepoOption + // in:body CreateForkOption api.CreateForkOption // in:body diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index bc095f723741..8f2bd7f9d452 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -1197,6 +1197,45 @@ "$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" + } + } } }, "/repos/{owner}/{repo}/archive/{archive}": { @@ -7126,6 +7165,9 @@ "type": "string", "x-go-name": "UserName" }, + "visibility": { + "$ref": "#/definitions/VisibleType" + }, "website": { "type": "string", "x-go-name": "Website" @@ -7722,6 +7764,87 @@ }, "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" }, + "EditRepoOption": { + "description": "EditRepoOption options when editing a repository's properties", + "type": "object", + "required": [ + "name" + ], + "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. `enabled_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. `enabled_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. `enabled_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. `enabled_pull_requests` must be `true`.", + "type": "boolean", + "x-go-name": "AllowSquashMerge" + }, + "archived": { + "description": "`true` to archive this repository. Note: You cannot unarchive repositories through the API.", + "type": "boolean", + "x-go-name": "Archived" + }, + "default_branch": { + "description": "Updates 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" + }, + "enable_issues": { + "description": "Either `true` to enable issues for this repository or `false` to disable them.", + "type": "boolean", + "x-go-name": "EnableIssues" + }, + "enable_pull_requests": { + "description": "Either `true` to allow pull requests, or `false` to prevent pull request.", + "type": "boolean", + "x-go-name": "EnablePullRequests" + }, + "enable_wiki": { + "description": "Either `true` to enable the wiki for this repository or `false` to disable it.", + "type": "boolean", + "x-go-name": "EnableWiki" + }, + "ignore_whitespace": { + "description": "Either `true` to ignore whitepace for conflicts, or `false` to not ignore whitespace. `enabled_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/vendor/code.gitea.io/sdk/gitea" + }, "EditTeamOption": { "description": "EditTeamOption options for editing a team", "type": "object", @@ -8595,6 +8718,9 @@ "type": "string", "x-go-name": "UserName" }, + "visibility": { + "$ref": "#/definitions/VisibleType" + }, "website": { "type": "string", "x-go-name": "Website" @@ -9447,6 +9573,12 @@ }, "x-go-package": "code.gitea.io/gitea/models" }, + "VisibleType": { + "description": "VisibleType defines the visibility (Organization only)", + "type": "integer", + "format": "int64", + "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + }, "WatchInfo": { "description": "WatchInfo represents an API watch status of one repository", "type": "object", @@ -9484,9 +9616,6 @@ "AccessToken": { "description": "AccessToken represents an API access token.", "headers": { - "hashed_token": { - "type": "string" - }, "id": { "type": "integer", "format": "int64" @@ -9494,7 +9623,7 @@ "name": { "type": "string" }, - "token": { + "sha1": { "type": "string" }, "token_last_eight": { From 3e88b7a011cb40435a15f50c3a3a766682a00fe5 Mon Sep 17 00:00:00 2001 From: Richard Mahn Date: Fri, 17 May 2019 15:24:58 -0400 Subject: [PATCH 02/30] Revert from merge --- routers/api/v1/repo/repo.go | 1 - 1 file changed, 1 deletion(-) diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go index 166b357d8a04..45306d1dc642 100644 --- a/routers/api/v1/repo/repo.go +++ b/routers/api/v1/repo/repo.go @@ -154,7 +154,6 @@ func Search(ctx *context.APIContext) { } var err error - repos, count, err := models.SearchRepositoryByName(opts) if err != nil { ctx.Error(http.StatusInternalServerError, err.Error(), api.SearchError{ From cd6773c8ebcabfd75d8833586b9d4b0490f1d2a0 Mon Sep 17 00:00:00 2001 From: Richard Mahn Date: Fri, 17 May 2019 17:45:40 -0400 Subject: [PATCH 03/30] Adds integration testing --- integrations/api_repo_edit_test.go | 224 ++++++++++++++++++++++ integrations/api_repo_file_delete_test.go | 2 +- models/repo.go | 4 +- modules/structs/repo.go | 6 +- routers/api/v1/api.go | 2 +- routers/api/v1/repo/repo.go | 85 ++++---- routers/api/v1/repo/repo_test.go | 81 ++++++++ templates/swagger/v1_json.tmpl | 5 +- 8 files changed, 355 insertions(+), 54 deletions(-) create mode 100644 integrations/api_repo_edit_test.go create mode 100644 routers/api/v1/repo/repo_test.go diff --git a/integrations/api_repo_edit_test.go b/integrations/api_repo_edit_test.go new file mode 100644 index 000000000000..24ff0310018f --- /dev/null +++ b/integrations/api_repo_edit_test.go @@ -0,0 +1,224 @@ +// 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 + enableIssues := false + if _, err := repo.GetUnit(models.UnitTypeIssues); err != nil { + enableIssues = true + } + enableWiki := false + if _, err := repo.GetUnit(models.UnitTypeWiki); err != nil { + enableWiki = true + } + defaultBranch := repo.DefaultBranch + enablePullRequests := false + ignoreWhitespaceConflicts := false + allowMerge := false + allowRebase := false + allowRebaseMerge := false + allowSquash := false + if unit, err := repo.GetUnit(models.UnitTypePullRequests); err == nil { + config := unit.PullRequestsConfig() + 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, + EnableIssues: &enableIssues, + EnableWiki: &enableWiki, + DefaultBranch: &defaultBranch, + EnablePullRequests: &enablePullRequests, + 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 + enableIssues := !*opts.EnableIssues + enableWiki := !*opts.EnableWiki + defaultBranch := "master" + enablePullRequests := !*opts.EnablePullRequests + 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, + EnableIssues: &enableIssues, + EnableWiki: &enableWiki, + DefaultBranch: &defaultBranch, + EnablePullRequests: &enablePullRequests, + 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.True(t, *repo1editedOption.Archived) + assert.True(t, *repo1editedOption.Private) + assert.False(t, *repo1editedOption.EnableWiki) + // 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) + fmt.Print(url) + 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 + 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) + }) +} diff --git a/integrations/api_repo_file_delete_test.go b/integrations/api_repo_file_delete_test.go index 57e2539e1918..e9029a669b97 100644 --- a/integrations/api_repo_file_delete_test.go +++ b/integrations/api_repo_file_delete_test.go @@ -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) diff --git a/models/repo.go b/models/repo.go index 990034e91a03..04a750c19619 100644 --- a/models/repo.go +++ b/models/repo.go @@ -389,7 +389,7 @@ func IsErrUnitTypeNotExist(err error) bool { } func (err ErrUnitTypeNotExist) Error() string { - return"Unit does not exist" + return "Unit does not exist" } // MustGetUnit always returns a RepoUnit object @@ -416,7 +416,7 @@ func (repo *Repository) MustGetUnit(tp UnitType) *RepoUnit { } } else if tp == UnitTypeIssues { return &RepoUnit{ - Type: tp, + Type: tp, Config: new(IssuesConfig), } } diff --git a/modules/structs/repo.go b/modules/structs/repo.go index b5283beeaa41..71ab3501bf25 100644 --- a/modules/structs/repo.go +++ b/modules/structs/repo.go @@ -71,10 +71,8 @@ type CreateRepoOption struct { // swagger:model type EditRepoOption struct { // Name of the repository - // - // required: 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. Description *string `json:"description,omitempty" binding:"MaxSize(255)"` // A URL with more information about the repository. @@ -100,7 +98,7 @@ type EditRepoOption struct { // 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`. 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"` + AllowSquash *bool `json:"allow_squash_merge,omitempty"` // `true` to archive this repository. Note: You cannot unarchive repositories through the API. Archived *bool `json:"archived,omitempty"` } diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index 4a345c2cdeeb..c1561200cdbc 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -609,7 +609,7 @@ func RegisterRoutes(m *macaron.Macaron) { m.Group("/:username/:reponame", func() { m.Combo("").Get(reqAnyRepoReader(), repo.Get). Delete(reqToken(), reqOwner(), repo.Delete). - Patch(reqToken(), reqOwner(), bind(api.EditRepoOption{}), repo.Edit) + Patch(reqToken(), reqAdmin(), bind(api.EditRepoOption{}), repo.Edit) m.Group("/hooks", func() { m.Combo("").Get(repo.ListHooks). Post(bind(api.CreateHookOption{}), repo.CreateHook) diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go index 8dd2a4870e2e..9f8586c39df9 100644 --- a/routers/api/v1/repo/repo.go +++ b/routers/api/v1/repo/repo.go @@ -7,7 +7,6 @@ package repo import ( "fmt" - "github.com/davecgh/go-spew/spew" "net/http" "strings" @@ -586,45 +585,44 @@ func updateBasicProperties(ctx *context.APIContext, opts api.EditRepoOption) err owner := ctx.Repo.Owner repo := ctx.Repo.Repository - spew.Dump(opts) - + oldRepoName := repo.Name + newRepoName := repo.Name if opts.Name != nil { - oldRepoName := repo.Name - 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 + 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 + } - if err := models.RenameRepoAction(ctx.User, oldRepoName, repo); err != nil { - log.Error("RenameRepoAction: %v", err) - ctx.Error(http.StatusInternalServerError, "RenameRepoActions", 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 + } - log.Trace("Repository name changed: %s/%s -> %s", ctx.Repo.Owner.Name, repo.Name, newRepoName) + if err := models.RenameRepoAction(ctx.User, oldRepoName, repo); err != nil { + log.Error("RenameRepoAction: %v", err) + ctx.Error(http.StatusInternalServerError, "RenameRepoActions", err) + return err } - // Update the name in the repo object for ther response - repo.Name = newRepoName - repo.LowerName = strings.ToLower(newRepoName) + + log.Trace("Repository name changed: %s/%s -> %s", ctx.Repo.Owner.Name, repo.Name, newRepoName) } + // Update the name in the repo object for ther response + repo.Name = newRepoName + repo.LowerName = strings.ToLower(newRepoName) if opts.Description != nil { repo.Description = *opts.Description @@ -689,16 +687,16 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error { if err != nil { // Unit type doesn't exist so we make a new config file with default values config = &models.IssuesConfig{ - EnableTimetracker: true, + EnableTimetracker: true, AllowOnlyContributorsToTrackTime: true, - EnableDependencies: true, + EnableDependencies: true, } } else { config = unit.IssuesConfig() } units = append(units, models.RepoUnit{ RepoID: repo.ID, - Type: unit.Type, + Type: models.UnitTypeIssues, Config: config, }) } @@ -732,10 +730,10 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error { // 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, + AllowMerge: true, + AllowRebase: true, + AllowRebaseMerge: true, + AllowSquash: true, } } else { config = unit.PullRequestsConfig() @@ -753,6 +751,9 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error { if opts.AllowRebaseMerge != nil { config.AllowRebaseMerge = *opts.AllowRebaseMerge } + if opts.AllowSquash != nil { + config.AllowSquash = *opts.AllowSquash + } units = append(units, models.RepoUnit{ RepoID: repo.ID, @@ -767,7 +768,7 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error { // exist). // This will also get the types that "must" always exist, and makes sure they exist for _, tp := range models.AllRepoUnitTypes { - if ! unitTypeInTypes(tp, unitTypesProcessed) { + if !unitTypeInTypes(tp, unitTypesProcessed) { if unit, _ := repo.GetUnit(tp); unit != nil { units = append(units, *unit) } else { diff --git a/routers/api/v1/repo/repo_test.go b/routers/api/v1/repo/repo_test.go new file mode 100644 index 000000000000..a459432ee41f --- /dev/null +++ b/routers/api/v1/repo/repo_test.go @@ -0,0 +1,81 @@ +// Copyright 2018 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 ( + "code.gitea.io/gitea/modules/context" + "github.com/stretchr/testify/assert" + "net/http" + "testing" + + "code.gitea.io/gitea/models" + api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/test" +) + +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 + enableIssues := false + enableWiki := false + defaultBranch := "master" + enablePullRequests := 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, + EnableIssues: &enableIssues, + EnableWiki: &enableWiki, + DefaultBranch: &defaultBranch, + EnablePullRequests: &enablePullRequests, + 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)) +} diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index e71dbcf1d639..4eef4b4e26c0 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -7767,9 +7767,6 @@ "EditRepoOption": { "description": "EditRepoOption options when editing a repository's properties", "type": "object", - "required": [ - "name" - ], "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. `enabled_pull_requests` must be `true`.", @@ -7789,7 +7786,7 @@ "allow_squash_merge": { "description": "Either `true` to allow squash-merging pull requests, or `false` to prevent squash-merging. `enabled_pull_requests` must be `true`.", "type": "boolean", - "x-go-name": "AllowSquashMerge" + "x-go-name": "AllowSquash" }, "archived": { "description": "`true` to archive this repository. Note: You cannot unarchive repositories through the API.", From 68cc8c0a62bd6a29a44fd0dfcf8e60528c5e2e2d Mon Sep 17 00:00:00 2001 From: Richard Mahn Date: Mon, 20 May 2019 16:37:54 -0400 Subject: [PATCH 04/30] Updates to integration tests --- integrations/api_repo_edit_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/integrations/api_repo_edit_test.go b/integrations/api_repo_edit_test.go index 24ff0310018f..51f0ff0420d6 100644 --- a/integrations/api_repo_edit_test.go +++ b/integrations/api_repo_edit_test.go @@ -204,6 +204,7 @@ func TestAPIRepoEdit(t *testing.T) { 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) From e62b38a058ec7c836dc0e89d63cf4ebdf2d96b71 Mon Sep 17 00:00:00 2001 From: Richard Mahn Date: Mon, 20 May 2019 16:50:12 -0400 Subject: [PATCH 05/30] Revert changes --- routers/api/v1/repo/repo.go | 104 ++++++++++++++++++------------------ 1 file changed, 53 insertions(+), 51 deletions(-) diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go index d38e131ff85a..287b054e9d18 100644 --- a/routers/api/v1/repo/repo.go +++ b/routers/api/v1/repo/repo.go @@ -1,5 +1,5 @@ // Copyright 2014 The Gogs Authors. All rights reserved. -// Copyright 2018 The Gitea Authors. All rights reserved. +// 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. @@ -155,7 +155,7 @@ func Search(ctx *context.APIContext) { var err error repos, count, err := models.SearchRepositoryByName(opts) if err != nil { - ctx.Error(http.StatusInternalServerError, err.Error(), api.SearchError{ + ctx.JSON(500, api.SearchError{ OK: false, Error: err.Error(), }) @@ -165,7 +165,7 @@ func Search(ctx *context.APIContext) { results := make([]*api.Repository, len(repos)) for i, repo := range repos { if err = repo.GetOwner(); err != nil { - ctx.Error(http.StatusInternalServerError, err.Error(), api.SearchError{ + ctx.JSON(500, api.SearchError{ OK: false, Error: err.Error(), }) @@ -173,7 +173,7 @@ func Search(ctx *context.APIContext) { } accessMode, err := models.AccessLevel(ctx.User, repo) if err != nil { - ctx.Error(http.StatusInternalServerError, err.Error(), api.SearchError{ + ctx.JSON(500, api.SearchError{ OK: false, Error: err.Error(), }) @@ -183,7 +183,7 @@ func Search(ctx *context.APIContext) { ctx.SetLinkHeader(int(count), setting.API.MaxResponseItems) ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) - ctx.Error(http.StatusOK, err.Error(), api.SearchResults{ + ctx.JSON(200, api.SearchResults{ OK: true, Data: results, }) @@ -205,22 +205,22 @@ func CreateUserRepo(ctx *context.APIContext, owner *models.User, opt api.CreateR }) if err != nil { if models.IsErrRepoAlreadyExist(err) { - ctx.Error(http.StatusConflict, "", "The repository with the same name already exists.") + ctx.Error(409, "", "The repository with the same name already exists.") } else if models.IsErrNameReserved(err) || models.IsErrNamePatternNotAllowed(err) { - ctx.Error(http.StatusUnprocessableEntity, "", err) + ctx.Error(422, "", err) } else { if repo != nil { if err = models.DeleteRepository(ctx.User, ctx.User.ID, repo.ID); err != nil { log.Error("DeleteRepository: %v", err) } } - ctx.Error(http.StatusInternalServerError, "CreateRepository", err) + ctx.Error(500, "CreateRepository", err) } return } - ctx.JSON(http.StatusCreated, repo.APIFormat(models.AccessModeOwner)) + ctx.JSON(201, repo.APIFormat(models.AccessModeOwner)) } // Create one repository of mine @@ -242,7 +242,7 @@ func Create(ctx *context.APIContext, opt api.CreateRepoOption) { // "$ref": "#/responses/Repository" if ctx.User.IsOrganization() { // Shouldn't reach this condition, but just in case. - ctx.Error(http.StatusUnprocessableEntity, "", "not allowed creating repository for organization") + ctx.Error(422, "", "not allowed creating repository for organization") return } CreateUserRepo(ctx, ctx.User, opt) @@ -277,9 +277,9 @@ func CreateOrgRepo(ctx *context.APIContext, opt api.CreateRepoOption) { org, err := models.GetOrgByName(ctx.Params(":org")) if err != nil { if models.IsErrOrgNotExist(err) { - ctx.Error(http.StatusUnprocessableEntity, "", err) + ctx.Error(422, "", err) } else { - ctx.Error(http.StatusInternalServerError, "GetOrgByName", err) + ctx.Error(500, "GetOrgByName", err) } return } @@ -292,10 +292,10 @@ func CreateOrgRepo(ctx *context.APIContext, opt api.CreateRepoOption) { if !ctx.User.IsAdmin { isOwner, err := org.IsOwnedBy(ctx.User.ID) if err != nil { - ctx.Error(http.StatusInternalServerError, "IsOwnedBy", err) + ctx.ServerError("IsOwnedBy", err) return } else if !isOwner { - ctx.Error(http.StatusForbidden, "", "Given user is not owner of organization.") + ctx.Error(403, "", "Given user is not owner of organization.") return } } @@ -326,9 +326,9 @@ func Migrate(ctx *context.APIContext, form auth.MigrateRepoForm) { org, err := models.GetUserByID(form.UID) if err != nil { if models.IsErrUserNotExist(err) { - ctx.Error(http.StatusUnprocessableEntity, "", err) + ctx.Error(422, "", err) } else { - ctx.Error(http.StatusInternalServerError, "GetUserByID", err) + ctx.Error(500, "GetUserByID", err) } return } @@ -336,13 +336,13 @@ func Migrate(ctx *context.APIContext, form auth.MigrateRepoForm) { } if ctx.HasError() { - ctx.Error(http.StatusUnprocessableEntity, "", ctx.GetErrMsg()) + ctx.Error(422, "", ctx.GetErrMsg()) return } if !ctx.User.IsAdmin { if !ctxUser.IsOrganization() && ctx.User.ID != ctxUser.ID { - ctx.Error(http.StatusForbidden, "", "Given user is not an organization.") + ctx.Error(403, "", "Given user is not an organization.") return } @@ -350,10 +350,10 @@ func Migrate(ctx *context.APIContext, form auth.MigrateRepoForm) { // Check ownership of organization. isOwner, err := ctxUser.IsOwnedBy(ctx.User.ID) if err != nil { - ctx.Error(http.StatusInternalServerError, "IsOwnedBy", err) + ctx.Error(500, "IsOwnedBy", err) return } else if !isOwner { - ctx.Error(http.StatusForbidden, "", "Given user is not owner of organization.") + ctx.Error(403, "", "Given user is not owner of organization.") return } } @@ -365,16 +365,16 @@ func Migrate(ctx *context.APIContext, form auth.MigrateRepoForm) { addrErr := err.(models.ErrInvalidCloneAddr) switch { case addrErr.IsURLError: - ctx.Error(http.StatusUnprocessableEntity, "", err) + ctx.Error(422, "", err) case addrErr.IsPermissionDenied: - ctx.Error(http.StatusUnprocessableEntity, "", "You are not allowed to import local repositories.") + ctx.Error(422, "", "You are not allowed to import local repositories.") case addrErr.IsInvalidPath: - ctx.Error(http.StatusUnprocessableEntity, "", "Invalid local path, it does not exist or not a directory.") + ctx.Error(422, "", "Invalid local path, it does not exist or not a directory.") default: - ctx.Error(http.StatusInternalServerError, "ParseRemoteAddr", "Unknown error type (ErrInvalidCloneAddr): "+err.Error()) + ctx.Error(500, "ParseRemoteAddr", "Unknown error type (ErrInvalidCloneAddr): "+err.Error()) } } else { - ctx.Error(http.StatusInternalServerError, "ParseRemoteAddr", err) + ctx.Error(500, "ParseRemoteAddr", err) } return } @@ -407,33 +407,33 @@ func Migrate(ctx *context.APIContext, form auth.MigrateRepoForm) { repo, err := migrations.MigrateRepository(ctx.User, ctxUser.Name, opts) if err == nil { log.Trace("Repository migrated: %s/%s", ctxUser.Name, form.RepoName) - ctx.JSON(http.StatusCreated, repo.APIFormat(models.AccessModeAdmin)) + ctx.JSON(201, repo.APIFormat(models.AccessModeAdmin)) return } switch { case models.IsErrRepoAlreadyExist(err): - ctx.Error(http.StatusConflict, "", "The repository with the same name already exists.") + ctx.Error(409, "", "The repository with the same name already exists.") case migrations.IsRateLimitError(err): - ctx.Error(http.StatusUnprocessableEntity, "", "Remote visit addressed rate limitation.") + ctx.Error(422, "", "Remote visit addressed rate limitation.") case migrations.IsTwoFactorAuthError(err): - ctx.Error(http.StatusUnprocessableEntity, "", "Remote visit required two factors authentication.") + ctx.Error(422, "", "Remote visit required two factors authentication.") case models.IsErrReachLimitOfRepo(err): - ctx.Error(http.StatusUnprocessableEntity, "", fmt.Sprintf("You have already reached your limit of %d repositories.", ctxUser.MaxCreationLimit())) + ctx.Error(422, "", fmt.Sprintf("You have already reached your limit of %d repositories.", ctxUser.MaxCreationLimit())) case models.IsErrNameReserved(err): - ctx.Error(http.StatusUnprocessableEntity, "", fmt.Sprintf("The username '%s' is reserved.", err.(models.ErrNameReserved).Name)) + ctx.Error(422, "", fmt.Sprintf("The username '%s' is reserved.", err.(models.ErrNameReserved).Name)) case models.IsErrNamePatternNotAllowed(err): - ctx.Error(http.StatusUnprocessableEntity, "", fmt.Sprintf("The pattern '%s' is not allowed in a username.", err.(models.ErrNamePatternNotAllowed).Pattern)) + ctx.Error(422, "", fmt.Sprintf("The pattern '%s' is not allowed in a username.", err.(models.ErrNamePatternNotAllowed).Pattern)) default: err = util.URLSanitizedError(err, remoteAddr) if strings.Contains(err.Error(), "Authentication failed") || strings.Contains(err.Error(), "Bad credentials") || strings.Contains(err.Error(), "could not read Username") { - ctx.Error(http.StatusUnprocessableEntity, "", fmt.Sprintf("Authentication failed: %v.", err)) + ctx.Error(422, "", fmt.Sprintf("Authentication failed: %v.", err)) } else if strings.Contains(err.Error(), "fatal:") { - ctx.Error(http.StatusUnprocessableEntity, "", fmt.Sprintf("Migration failed: %v.", err)) + ctx.Error(422, "", fmt.Sprintf("Migration failed: %v.", err)) } else { - ctx.Error(http.StatusInternalServerError, "MigrateRepository", err) + ctx.Error(500, "MigrateRepository", err) } } } @@ -459,7 +459,7 @@ func Get(ctx *context.APIContext) { // responses: // "200": // "$ref": "#/responses/Repository" - ctx.JSON(http.StatusOK, ctx.Repo.Repository.APIFormat(ctx.Repo.AccessMode)) + ctx.JSON(200, ctx.Repo.Repository.APIFormat(ctx.Repo.AccessMode)) } // GetByID returns a single Repository @@ -484,20 +484,20 @@ func GetByID(ctx *context.APIContext) { if models.IsErrRepoNotExist(err) { ctx.NotFound() } else { - ctx.Error(http.StatusInternalServerError, "GetRepositoryByID", err) + ctx.Error(500, "GetRepositoryByID", err) } return } perm, err := models.GetUserRepoPermission(repo, ctx.User) if err != nil { - ctx.Error(http.StatusInternalServerError, "AccessLevel", err) + ctx.Error(500, "AccessLevel", err) return } else if !perm.HasAccess() { ctx.NotFound() return } - ctx.JSON(http.StatusOK, repo.APIFormat(perm.AccessMode)) + ctx.JSON(200, repo.APIFormat(perm.AccessMode)) } // Edit edit repository properties @@ -827,21 +827,21 @@ func Delete(ctx *context.APIContext) { if owner.IsOrganization() && !ctx.User.IsAdmin { isOwner, err := owner.IsOwnedBy(ctx.User.ID) if err != nil { - ctx.Error(http.StatusInternalServerError, "IsOwnedBy", err) + ctx.Error(500, "IsOwnedBy", err) return } else if !isOwner { - ctx.Error(http.StatusForbidden, "", "Given user is not owner of organization.") + ctx.Error(403, "", "Given user is not owner of organization.") return } } if err := models.DeleteRepository(ctx.User, owner.ID, repo.ID); err != nil { - ctx.Error(http.StatusInternalServerError, "DeleteRepository", err) + ctx.Error(500, "DeleteRepository", err) return } log.Trace("Repository deleted: %s/%s", owner.Name, repo.Name) - ctx.Status(http.StatusNoContent) + ctx.Status(204) } // MirrorSync adds a mirrored repository to the sync queue @@ -868,15 +868,15 @@ func MirrorSync(ctx *context.APIContext) { repo := ctx.Repo.Repository if !ctx.Repo.CanWrite(models.UnitTypeCode) { - ctx.Error(http.StatusForbidden, "MirrorSync", "Must have write access") + ctx.Error(403, "MirrorSync", "Must have write access") } go models.MirrorQueue.Add(repo.ID) - ctx.Status(http.StatusOK) + ctx.Status(200) } // TopicSearch search for creating topic -func TopicSearch(ctx *context.APIContext) { +func TopicSearch(ctx *context.Context) { // swagger:operation GET /topics/search repository topicSearch // --- // summary: search topics via keyword @@ -892,8 +892,9 @@ func TopicSearch(ctx *context.APIContext) { // "200": // "$ref": "#/responses/Repository" if ctx.User == nil { - err := fmt.Errorf("only owners could change the topics") - ctx.Error(http.StatusForbidden, err.Error(), err) + ctx.JSON(403, map[string]interface{}{ + "message": "Only owners could change the topics.", + }) return } @@ -905,12 +906,13 @@ func TopicSearch(ctx *context.APIContext) { }) if err != nil { log.Error("SearchTopics failed: %v", err) - err := fmt.Errorf("search topics failed") - ctx.Error(http.StatusInternalServerError, err.Error(), err) + ctx.JSON(500, map[string]interface{}{ + "message": "Search topics failed.", + }) return } - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(200, map[string]interface{}{ "topics": topics, }) } From baf74b440ddf841799cb1a71d6baa2535e2335e0 Mon Sep 17 00:00:00 2001 From: Richard Mahn Date: Mon, 20 May 2019 17:10:27 -0400 Subject: [PATCH 06/30] Update year in file header --- routers/api/v1/repo/repo_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routers/api/v1/repo/repo_test.go b/routers/api/v1/repo/repo_test.go index a459432ee41f..20f0d6b491a5 100644 --- a/routers/api/v1/repo/repo_test.go +++ b/routers/api/v1/repo/repo_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 The Gitea Authors. All rights reserved. +// 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. From d0934f8e9062cb19ac515067bf27cb3bd6692a15 Mon Sep 17 00:00:00 2001 From: Richard Mahn Date: Mon, 20 May 2019 21:12:47 -0400 Subject: [PATCH 07/30] Misspell fix --- routers/api/v1/repo/repo.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go index 287b054e9d18..2db01754010a 100644 --- a/routers/api/v1/repo/repo.go +++ b/routers/api/v1/repo/repo.go @@ -597,7 +597,7 @@ func updateBasicProperties(ctx *context.APIContext, opts api.EditRepoOption) err log.Trace("Repository name changed: %s/%s -> %s", ctx.Repo.Owner.Name, repo.Name, newRepoName) } - // Update the name in the repo object for ther response + // Update the name in the repo object for the response repo.Name = newRepoName repo.LowerName = strings.ToLower(newRepoName) From 312f4e000bf1a6fbcb3b1088111f6a464257541a Mon Sep 17 00:00:00 2001 From: Richard Mahn Date: Tue, 21 May 2019 11:21:59 -0400 Subject: [PATCH 08/30] XORM = test --- integrations/mssql.ini.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integrations/mssql.ini.tmpl b/integrations/mssql.ini.tmpl index a8e6d332f0d3..420195b0832a 100644 --- a/integrations/mssql.ini.tmpl +++ b/integrations/mssql.ini.tmpl @@ -65,7 +65,7 @@ ROOT_PATH = sqlite-log REDIRECT_MACARON_LOG = true ROUTER = , MACARON = , -XORM = file +XORM = test [log.test] LEVEL = Info From 2aadd423f5fafd30a4450602be6eea474123500d Mon Sep 17 00:00:00 2001 From: Richard Mahn Date: Tue, 21 May 2019 11:38:56 -0400 Subject: [PATCH 09/30] XORM = test --- integrations/mssql.ini.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integrations/mssql.ini.tmpl b/integrations/mssql.ini.tmpl index 420195b0832a..9c2698ac77b2 100644 --- a/integrations/mssql.ini.tmpl +++ b/integrations/mssql.ini.tmpl @@ -60,7 +60,7 @@ PROVIDER = file PROVIDER_CONFIG = data/sessions-mssql [log] -MODE = test,file +MODE = info,file ROOT_PATH = sqlite-log REDIRECT_MACARON_LOG = true ROUTER = , From 48d63215be5f02c20c4046ba2d0683c3dcc5817a Mon Sep 17 00:00:00 2001 From: Richard Mahn Date: Tue, 21 May 2019 11:42:24 -0400 Subject: [PATCH 10/30] revert XORM = file --- integrations/mssql.ini.tmpl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/integrations/mssql.ini.tmpl b/integrations/mssql.ini.tmpl index 9c2698ac77b2..a8e6d332f0d3 100644 --- a/integrations/mssql.ini.tmpl +++ b/integrations/mssql.ini.tmpl @@ -60,12 +60,12 @@ PROVIDER = file PROVIDER_CONFIG = data/sessions-mssql [log] -MODE = info,file +MODE = test,file ROOT_PATH = sqlite-log REDIRECT_MACARON_LOG = true ROUTER = , MACARON = , -XORM = test +XORM = file [log.test] LEVEL = Info From 8d1754111adc36ee2e9f61c1c14edfd966c11dc8 Mon Sep 17 00:00:00 2001 From: Richard Mahn Date: Tue, 21 May 2019 12:35:00 -0400 Subject: [PATCH 11/30] Makes RepoUnit.ID be pk and autoincr --- models/repo_unit.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/repo_unit.go b/models/repo_unit.go index 430f5a242ff5..b1890a54e660 100644 --- a/models/repo_unit.go +++ b/models/repo_unit.go @@ -16,7 +16,7 @@ import ( // RepoUnit describes all units of a repository type RepoUnit struct { - ID int64 + ID int64 `xorm:"pk autoincr"` RepoID int64 `xorm:"INDEX(s)"` Type UnitType `xorm:"INDEX(s)"` Config core.Conversion `xorm:"TEXT"` From 5443da858199d72231bb38172f9f18be88f1b746 Mon Sep 17 00:00:00 2001 From: Richard Mahn Date: Wed, 22 May 2019 12:26:56 -0400 Subject: [PATCH 12/30] Fix to units --- integrations/api_repo_edit_test.go | 10 +++++----- routers/api/v1/repo/repo.go | 31 ++++++++---------------------- 2 files changed, 13 insertions(+), 28 deletions(-) diff --git a/integrations/api_repo_edit_test.go b/integrations/api_repo_edit_test.go index 51f0ff0420d6..1f3403fea10c 100644 --- a/integrations/api_repo_edit_test.go +++ b/integrations/api_repo_edit_test.go @@ -23,11 +23,11 @@ func getRepoEditOptionFromRepo(repo *models.Repository) *api.EditRepoOption { website := repo.Website private := repo.IsPrivate enableIssues := false - if _, err := repo.GetUnit(models.UnitTypeIssues); err != nil { + if _, err := repo.GetUnit(models.UnitTypeIssues); err == nil { enableIssues = true } enableWiki := false - if _, err := repo.GetUnit(models.UnitTypeWiki); err != nil { + if _, err := repo.GetUnit(models.UnitTypeWiki); err == nil { enableWiki = true } defaultBranch := repo.DefaultBranch @@ -139,9 +139,9 @@ func TestAPIRepoEdit(t *testing.T) { assert.Equal(t, *repoEditOption.Name, *repo1editedOption.Name) assert.Equal(t, *repoEditOption.Description, *repo1editedOption.Description) assert.Equal(t, *repoEditOption.Website, *repo1editedOption.Website) - assert.True(t, *repo1editedOption.Archived) - assert.True(t, *repo1editedOption.Private) - assert.False(t, *repo1editedOption.EnableWiki) + assert.Equal(t, *repoEditOption.Archived, *repo1editedOption.Archived) + assert.Equal(t, *repoEditOption.Private, *repo1editedOption.Private) + assert.Equal(t, *repoEditOption.EnableWiki, *repo1editedOption.EnableWiki) // 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) diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go index 2db01754010a..75d533d8709f 100644 --- a/routers/api/v1/repo/repo.go +++ b/routers/api/v1/repo/repo.go @@ -651,7 +651,14 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error { repo := ctx.Repo.Repository var units []models.RepoUnit - var unitTypesProcessed []models.UnitType + + for _, tp := range models.MustRepoUnits { + units = append(units, models.RepoUnit{ + RepoID: repo.ID, + Type: tp, + Config: new(models.UnitConfig), + }) + } if opts.EnableIssues != nil { if *opts.EnableIssues { @@ -677,7 +684,6 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error { Config: config, }) } - unitTypesProcessed = append(unitTypesProcessed, models.UnitTypeIssues) } if opts.EnableWiki != nil { @@ -693,7 +699,6 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error { Config: config, }) } - unitTypesProcessed = append(unitTypesProcessed, models.UnitTypeWiki) } if opts.EnablePullRequests != nil { @@ -738,26 +743,6 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error { Config: config, }) } - unitTypesProcessed = append(unitTypesProcessed, models.UnitTypePullRequests) - } - - // Put back onto the units array the unit types we didn't process due to not being in the API payload (if they - // exist). - // This will also get the types that "must" always exist, and makes sure they exist - for _, tp := range models.AllRepoUnitTypes { - if !unitTypeInTypes(tp, unitTypesProcessed) { - if unit, _ := repo.GetUnit(tp); unit != nil { - units = append(units, *unit) - } else { - if unitTypeInTypes(tp, models.MustRepoUnits) { - units = append(units, models.RepoUnit{ - RepoID: repo.ID, - Type: tp, - Config: new(models.UnitConfig), - }) - } - } - } } if err := models.UpdateRepositoryUnits(repo, units); err != nil { From c0ea7ad4c5ea5cbe2c6a44b3e2c457b244a52bc3 Mon Sep 17 00:00:00 2001 From: Richard Mahn Date: Wed, 22 May 2019 12:33:02 -0400 Subject: [PATCH 13/30] revert header --- routers/api/v1/repo/repo.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go index 75d533d8709f..3c6d1eed014b 100644 --- a/routers/api/v1/repo/repo.go +++ b/routers/api/v1/repo/repo.go @@ -1,5 +1,5 @@ // Copyright 2014 The Gogs Authors. All rights reserved. -// Copyright 2019 The Gitea Authors. All rights reserved. +// Copyright 2018 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. From 666fea7c74040867e3a0c2dbb0bd4323e45378ff Mon Sep 17 00:00:00 2001 From: Richard Mahn Date: Wed, 22 May 2019 15:08:06 -0400 Subject: [PATCH 14/30] Remove print statement --- integrations/api_repo_edit_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/integrations/api_repo_edit_test.go b/integrations/api_repo_edit_test.go index 1f3403fea10c..17adcfa78aa7 100644 --- a/integrations/api_repo_edit_test.go +++ b/integrations/api_repo_edit_test.go @@ -175,7 +175,6 @@ func TestAPIRepoEdit(t *testing.T) { 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) - fmt.Print(url) req = NewRequestWithJSON(t, "PATCH", url, &origRepoEditOption) resp = session.MakeRequest(t, req, http.StatusOK) From f51bd8ccf8005823ae10bd6c52bf7ad180ddeeff Mon Sep 17 00:00:00 2001 From: Richard Mahn Date: Wed, 22 May 2019 17:03:37 -0400 Subject: [PATCH 15/30] Adds other responses --- routers/api/v1/repo/repo.go | 4 ++++ templates/swagger/v1_json.tmpl | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go index 3c6d1eed014b..a48fc6790a6e 100644 --- a/routers/api/v1/repo/repo.go +++ b/routers/api/v1/repo/repo.go @@ -527,6 +527,10 @@ func Edit(ctx *context.APIContext, opts api.EditRepoOption) { // responses: // "200": // "$ref": "#/responses/Repository" + // "403": + // "$ref": "#/responses/forbidden" + // "422": + // "$ref": "#/responses/validationError" owner := ctx.Repo.Owner if owner.IsOrganization() && !ctx.User.IsAdmin { diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 4d807a98c06d..824afb4a1ee3 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -1247,6 +1247,12 @@ "responses": { "200": { "$ref": "#/responses/Repository" + }, + "403": { + "$ref": "#/responses/forbidden" + }, + "422": { + "$ref": "#/responses/validationError" } } } From c5889a5af91dfa19ddee8f9f26dd760c698579e2 Mon Sep 17 00:00:00 2001 From: Richard Mahn Date: Thu, 23 May 2019 10:43:30 -0400 Subject: [PATCH 16/30] Improves swagger for creating repo --- routers/api/v1/repo/repo.go | 4 ++++ templates/swagger/v1_json.tmpl | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go index a48fc6790a6e..9db685f66a0c 100644 --- a/routers/api/v1/repo/repo.go +++ b/routers/api/v1/repo/repo.go @@ -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") diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 824afb4a1ee3..4353eed340b7 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -6082,6 +6082,12 @@ "responses": { "201": { "$ref": "#/responses/Repository" + }, + "409": { + "description": "The repository with the same name already exists." + }, + "422": { + "$ref": "#/responses/validationError" } } } From dad4f470787c5529f34648d5540b9a2b942fbc31 Mon Sep 17 00:00:00 2001 From: Richard Mahn Date: Fri, 24 May 2019 21:39:45 -0400 Subject: [PATCH 17/30] Fixes import order --- routers/api/v1/repo/repo_test.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/routers/api/v1/repo/repo_test.go b/routers/api/v1/repo/repo_test.go index 20f0d6b491a5..8a69da5aa790 100644 --- a/routers/api/v1/repo/repo_test.go +++ b/routers/api/v1/repo/repo_test.go @@ -5,14 +5,15 @@ package repo import ( - "code.gitea.io/gitea/modules/context" - "github.com/stretchr/testify/assert" "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) { From 4923135334aa4aa65a98a5ccf64fc6f38f536b9d Mon Sep 17 00:00:00 2001 From: Richard Mahn Date: Sat, 25 May 2019 14:32:30 -0400 Subject: [PATCH 18/30] Better Unit Type does not exist error --- models/repo.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/repo.go b/models/repo.go index 02066da09411..5fb94a295a5d 100644 --- a/models/repo.go +++ b/models/repo.go @@ -348,7 +348,7 @@ func IsErrUnitTypeNotExist(err error) bool { } func (err ErrUnitTypeNotExist) Error() string { - return "Unit does not exist" + return fmt.Sprintf("Unit type does not exist: %s", err.UT.String()) } // MustGetUnit always returns a RepoUnit object From c4407baca4d591ee97184df2c3e48c8ef4fcb9e1 Mon Sep 17 00:00:00 2001 From: Richard Mahn Date: Sat, 25 May 2019 15:16:52 -0400 Subject: [PATCH 19/30] Adds editable repo properties to the response repo structure --- integrations/api_repo_edit_test.go | 21 ++++---- models/repo.go | 80 +++++++++++++++++++++--------- modules/structs/repo.go | 20 +++++--- routers/api/v1/repo/repo.go | 12 ++--- templates/swagger/v1_json.tmpl | 52 +++++++++++++++---- 5 files changed, 129 insertions(+), 56 deletions(-) diff --git a/integrations/api_repo_edit_test.go b/integrations/api_repo_edit_test.go index 17adcfa78aa7..06a811bdb221 100644 --- a/integrations/api_repo_edit_test.go +++ b/integrations/api_repo_edit_test.go @@ -39,6 +39,7 @@ func getRepoEditOptionFromRepo(repo *models.Repository) *api.EditRepoOption { allowSquash := false if unit, err := repo.GetUnit(models.UnitTypePullRequests); err == nil { config := unit.PullRequestsConfig() + enablePullRequests = true ignoreWhitespaceConflicts = config.IgnoreWhitespaceConflicts allowMerge = config.AllowMerge allowRebase = config.AllowRebase @@ -51,10 +52,10 @@ func getRepoEditOptionFromRepo(repo *models.Repository) *api.EditRepoOption { Description: &description, Website: &website, Private: &private, - EnableIssues: &enableIssues, - EnableWiki: &enableWiki, + HasIssues: &enableIssues, + HasWiki: &enableWiki, DefaultBranch: &defaultBranch, - EnablePullRequests: &enablePullRequests, + AllowPullRequests: &enablePullRequests, IgnoreWhitespaceConflicts: &ignoreWhitespaceConflicts, AllowMerge: &allowMerge, AllowRebase: &allowRebase, @@ -72,10 +73,10 @@ func getNewRepoEditOption(opts *api.EditRepoOption) *api.EditRepoOption { description := "new description" website := "http://wwww.newwebsite.com" private := !*opts.Private - enableIssues := !*opts.EnableIssues - enableWiki := !*opts.EnableWiki + enableIssues := !*opts.HasIssues + enableWiki := !*opts.HasWiki defaultBranch := "master" - enablePullRequests := !*opts.EnablePullRequests + enablePullRequests := !*opts.AllowPullRequests ignoreWhitespaceConflicts := !*opts.IgnoreWhitespaceConflicts allowMerge := !*opts.AllowMerge allowRebase := !*opts.AllowRebase @@ -88,10 +89,10 @@ func getNewRepoEditOption(opts *api.EditRepoOption) *api.EditRepoOption { Description: &description, Website: &website, Private: &private, - EnableIssues: &enableIssues, - EnableWiki: &enableWiki, + HasIssues: &enableIssues, + HasWiki: &enableWiki, DefaultBranch: &defaultBranch, - EnablePullRequests: &enablePullRequests, + AllowPullRequests: &enablePullRequests, IgnoreWhitespaceConflicts: &ignoreWhitespaceConflicts, AllowMerge: &allowMerge, AllowRebase: &allowRebase, @@ -141,7 +142,7 @@ func TestAPIRepoEdit(t *testing.T) { 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.EnableWiki, *repo1editedOption.EnableWiki) + 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) diff --git a/models/repo.go b/models/repo.go index 5fb94a295a5d..28cf7c21f050 100644 --- a/models/repo.go +++ b/models/repo.go @@ -265,31 +265,63 @@ func (repo *Repository) innerAPIFormat(e Engine, mode AccessMode, isParent bool) parent = repo.BaseRepo.innerAPIFormat(e, mode, true) } } + hasIssues := false + if _, err := repo.GetUnit(UnitTypeIssues); err == nil { + hasIssues = true + } + hasWiki := false + if _, err := repo.GetUnit(UnitTypeWiki); err == nil { + hasWiki = true + } + allowPullRequests := false + ignoreWhitespaceConflicts := false + allowMerge := false + allowRebase := false + allowRebaseMerge := false + allowSquash := false + if unit, err := repo.GetUnit(UnitTypePullRequests); err == nil { + config := unit.PullRequestsConfig() + allowPullRequests = 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, + 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, + AllowPullRequests: allowPullRequests, + IgnoreWhitespaceConflicts: ignoreWhitespaceConflicts, + AllowMerge: allowMerge, + AllowRebase: allowRebase, + AllowRebaseMerge: allowRebaseMerge, + AllowSquash: allowSquash, } } diff --git a/modules/structs/repo.go b/modules/structs/repo.go index 71ab3501bf25..e5d1cae31f9b 100644 --- a/modules/structs/repo.go +++ b/modules/structs/repo.go @@ -41,8 +41,16 @@ 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"` + Updated time.Time `json:"updated_at"` + Permissions *Permission `json:"permissions,omitempty"` + HasIssues bool `json:"has_issues"` + HasWiki bool `json:"has_wiki"` + AllowPullRequests bool `json:"allow_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"` } // CreateRepoOption options when creating repository @@ -82,15 +90,15 @@ type EditRepoOption struct { // 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"` + HasIssues *bool `json:"has_issues,omitempty"` // 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. 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"` + AllowPullRequests *bool `json:"allow_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"` + 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`. 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`. diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go index 9db685f66a0c..3c04e411b7e5 100644 --- a/routers/api/v1/repo/repo.go +++ b/routers/api/v1/repo/repo.go @@ -668,8 +668,8 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error { }) } - if opts.EnableIssues != nil { - if *opts.EnableIssues { + 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, @@ -694,8 +694,8 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error { } } - if opts.EnableWiki != nil { - if *opts.EnableWiki { + 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, @@ -709,8 +709,8 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error { } } - if opts.EnablePullRequests != nil { - if *opts.EnablePullRequests { + if opts.AllowPullRequests != nil { + if *opts.AllowPullRequests { // 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. diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 4353eed340b7..d14129a75536 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -7798,6 +7798,11 @@ "type": "boolean", "x-go-name": "AllowMerge" }, + "allow_pull_requests": { + "description": "Either `true` to allow pull requests, or `false` to prevent pull request.", + "type": "boolean", + "x-go-name": "AllowPullRequests" + }, "allow_rebase": { "description": "Either `true` to allow rebase-merging pull requests, or `false` to prevent rebase-merging. `enabled_pull_requests` must be `true`.", "type": "boolean", @@ -7828,22 +7833,17 @@ "type": "string", "x-go-name": "Description" }, - "enable_issues": { + "has_issues": { "description": "Either `true` to enable issues for this repository or `false` to disable them.", "type": "boolean", - "x-go-name": "EnableIssues" - }, - "enable_pull_requests": { - "description": "Either `true` to allow pull requests, or `false` to prevent pull request.", - "type": "boolean", - "x-go-name": "EnablePullRequests" + "x-go-name": "HasIssues" }, - "enable_wiki": { + "has_wiki": { "description": "Either `true` to enable the wiki for this repository or `false` to disable it.", "type": "boolean", - "x-go-name": "EnableWiki" + "x-go-name": "HasWiki" }, - "ignore_whitespace": { + "ignore_whitespace_conflicts": { "description": "Either `true` to ignore whitepace for conflicts, or `false` to not ignore whitespace. `enabled_pull_requests` must be `true`.", "type": "boolean", "x-go-name": "IgnoreWhitespaceConflicts" @@ -9191,6 +9191,26 @@ "description": "Repository represents a repository", "type": "object", "properties": { + "allow_merge_commits": { + "type": "boolean", + "x-go-name": "AllowMerge" + }, + "allow_pull_requests": { + "type": "boolean", + "x-go-name": "AllowPullRequests" + }, + "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" @@ -9229,6 +9249,14 @@ "type": "string", "x-go-name": "FullName" }, + "has_issues": { + "type": "boolean", + "x-go-name": "HasIssues" + }, + "has_wiki": { + "type": "boolean", + "x-go-name": "HasWiki" + }, "html_url": { "type": "string", "x-go-name": "HTMLURL" @@ -9238,6 +9266,10 @@ "format": "int64", "x-go-name": "ID" }, + "ignore_whitespace_conflicts": { + "type": "boolean", + "x-go-name": "IgnoreWhitespaceConflicts" + }, "mirror": { "type": "boolean", "x-go-name": "Mirror" From ec39b0d07d93eddc4917841a123f68fadbe5667b Mon Sep 17 00:00:00 2001 From: Richard Mahn Date: Sat, 25 May 2019 15:18:36 -0400 Subject: [PATCH 20/30] Fix to api_repo_edit_test.go --- integrations/api_repo_edit_test.go | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/integrations/api_repo_edit_test.go b/integrations/api_repo_edit_test.go index 06a811bdb221..df168452ee8f 100644 --- a/integrations/api_repo_edit_test.go +++ b/integrations/api_repo_edit_test.go @@ -22,16 +22,16 @@ func getRepoEditOptionFromRepo(repo *models.Repository) *api.EditRepoOption { description := repo.Description website := repo.Website private := repo.IsPrivate - enableIssues := false + hasIssues := false if _, err := repo.GetUnit(models.UnitTypeIssues); err == nil { - enableIssues = true + hasIssues = true } - enableWiki := false + hasWiki := false if _, err := repo.GetUnit(models.UnitTypeWiki); err == nil { - enableWiki = true + hasWiki = true } defaultBranch := repo.DefaultBranch - enablePullRequests := false + allowPullRequests := false ignoreWhitespaceConflicts := false allowMerge := false allowRebase := false @@ -39,7 +39,7 @@ func getRepoEditOptionFromRepo(repo *models.Repository) *api.EditRepoOption { allowSquash := false if unit, err := repo.GetUnit(models.UnitTypePullRequests); err == nil { config := unit.PullRequestsConfig() - enablePullRequests = true + allowPullRequests = true ignoreWhitespaceConflicts = config.IgnoreWhitespaceConflicts allowMerge = config.AllowMerge allowRebase = config.AllowRebase @@ -52,10 +52,10 @@ func getRepoEditOptionFromRepo(repo *models.Repository) *api.EditRepoOption { Description: &description, Website: &website, Private: &private, - HasIssues: &enableIssues, - HasWiki: &enableWiki, + HasIssues: &hasIssues, + HasWiki: &hasWiki, DefaultBranch: &defaultBranch, - AllowPullRequests: &enablePullRequests, + AllowPullRequests: &allowPullRequests, IgnoreWhitespaceConflicts: &ignoreWhitespaceConflicts, AllowMerge: &allowMerge, AllowRebase: &allowRebase, @@ -73,10 +73,10 @@ func getNewRepoEditOption(opts *api.EditRepoOption) *api.EditRepoOption { description := "new description" website := "http://wwww.newwebsite.com" private := !*opts.Private - enableIssues := !*opts.HasIssues - enableWiki := !*opts.HasWiki + hasIssues := !*opts.HasIssues + hasWiki := !*opts.HasWiki defaultBranch := "master" - enablePullRequests := !*opts.AllowPullRequests + allowPullRequests := !*opts.AllowPullRequests ignoreWhitespaceConflicts := !*opts.IgnoreWhitespaceConflicts allowMerge := !*opts.AllowMerge allowRebase := !*opts.AllowRebase @@ -89,10 +89,10 @@ func getNewRepoEditOption(opts *api.EditRepoOption) *api.EditRepoOption { Description: &description, Website: &website, Private: &private, - HasIssues: &enableIssues, - HasWiki: &enableWiki, + HasIssues: &hasIssues, + HasWiki: &hasWiki, DefaultBranch: &defaultBranch, - AllowPullRequests: &enablePullRequests, + AllowPullRequests: &allowPullRequests, IgnoreWhitespaceConflicts: &ignoreWhitespaceConflicts, AllowMerge: &allowMerge, AllowRebase: &allowRebase, From 3ed8a819e4fe2444e159de52324e545cb9f8d7d1 Mon Sep 17 00:00:00 2001 From: Richard Mahn Date: Sat, 25 May 2019 16:19:58 -0400 Subject: [PATCH 21/30] Fixes repo test --- routers/api/v1/repo/repo_test.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/routers/api/v1/repo/repo_test.go b/routers/api/v1/repo/repo_test.go index 8a69da5aa790..ff59a9094020 100644 --- a/routers/api/v1/repo/repo_test.go +++ b/routers/api/v1/repo/repo_test.go @@ -26,10 +26,10 @@ func TestRepoEdit(t *testing.T) { description := "new description" website := "http://wwww.newwebsite.com" private := true - enableIssues := false - enableWiki := false + hasIssues := false + hasWiki := false defaultBranch := "master" - enablePullRequests := true + allowPullRequests := true ignoreWhitespaceConflicts := true allowMerge := false allowRebase := false @@ -41,10 +41,10 @@ func TestRepoEdit(t *testing.T) { Description: &description, Website: &website, Private: &private, - EnableIssues: &enableIssues, - EnableWiki: &enableWiki, + HasIssues: &hasIssues, + HasWiki: &hasWiki, DefaultBranch: &defaultBranch, - EnablePullRequests: &enablePullRequests, + AllowPullRequests: &allowPullRequests, IgnoreWhitespaceConflicts: &ignoreWhitespaceConflicts, AllowMerge: &allowMerge, AllowRebase: &allowRebase, From 73165491db7f95f7afa1b8e9a41e944eb2729b6e Mon Sep 17 00:00:00 2001 From: Richard Mahn Date: Sun, 26 May 2019 16:19:08 -0400 Subject: [PATCH 22/30] Changes per review --- models/repo.go | 6 +++--- models/repo_unit.go | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/models/repo.go b/models/repo.go index 28cf7c21f050..928a940e5e42 100644 --- a/models/repo.go +++ b/models/repo.go @@ -266,11 +266,11 @@ func (repo *Repository) innerAPIFormat(e Engine, mode AccessMode, isParent bool) } } hasIssues := false - if _, err := repo.GetUnit(UnitTypeIssues); err == nil { + if _, err := repo.getUnit(e, UnitTypeIssues); err == nil { hasIssues = true } hasWiki := false - if _, err := repo.GetUnit(UnitTypeWiki); err == nil { + if _, err := repo.getUnit(e, UnitTypeWiki); err == nil { hasWiki = true } allowPullRequests := false @@ -279,7 +279,7 @@ func (repo *Repository) innerAPIFormat(e Engine, mode AccessMode, isParent bool) allowRebase := false allowRebaseMerge := false allowSquash := false - if unit, err := repo.GetUnit(UnitTypePullRequests); err == nil { + if unit, err := repo.getUnit(e, UnitTypePullRequests); err == nil { config := unit.PullRequestsConfig() allowPullRequests = true ignoreWhitespaceConflicts = config.IgnoreWhitespaceConflicts diff --git a/models/repo_unit.go b/models/repo_unit.go index b1890a54e660..430f5a242ff5 100644 --- a/models/repo_unit.go +++ b/models/repo_unit.go @@ -16,7 +16,7 @@ import ( // RepoUnit describes all units of a repository type RepoUnit struct { - ID int64 `xorm:"pk autoincr"` + ID int64 RepoID int64 `xorm:"INDEX(s)"` Type UnitType `xorm:"INDEX(s)"` Config core.Conversion `xorm:"TEXT"` From 32be744415cf2d33f07947c94f409dd3e1bd7ef9 Mon Sep 17 00:00:00 2001 From: Richard Mahn Date: Tue, 28 May 2019 10:37:19 -0400 Subject: [PATCH 23/30] Fixes typo and standardizes comments in the EditRepoOption struct for swagger --- modules/structs/repo.go | 30 +++++++++++++++--------------- templates/swagger/v1_json.tmpl | 28 ++++++++++++++-------------- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/modules/structs/repo.go b/modules/structs/repo.go index e5d1cae31f9b..b7ab990eb463 100644 --- a/modules/structs/repo.go +++ b/modules/structs/repo.go @@ -78,36 +78,36 @@ type CreateRepoOption struct { // EditRepoOption options when editing a repository's properties // swagger:model type EditRepoOption struct { - // Name of the repository + // name of the repository // unique: true 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)"` - // 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. + // 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. + // either `true` to enable the wiki for this repository or `false` to disable it. HasWiki *bool `json:"has_wiki,omitempty"` - // Updates the default branch for this repository. + // ipdates the default branch for this repository. 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. AllowPullRequests *bool `json:"allow_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. `enabled_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. `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. `enabled_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. `enabled_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. `enabled_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`. + // either `true` to allow squash-merging pull requests, or `false` to prevent squash-merging. `enabled_pull_requests` must be `true`. 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. Note: You cannot unarchive repositories through the API. Archived *bool `json:"archived,omitempty"` } diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index d14129a75536..df5026ddbf33 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -7794,73 +7794,73 @@ "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. `enabled_pull_requests` must be `true`.", + "description": "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`.", "type": "boolean", "x-go-name": "AllowMerge" }, "allow_pull_requests": { - "description": "Either `true` to allow pull requests, or `false` to prevent pull request.", + "description": "either `true` to allow pull requests, or `false` to prevent pull request.", "type": "boolean", "x-go-name": "AllowPullRequests" }, "allow_rebase": { - "description": "Either `true` to allow rebase-merging pull requests, or `false` to prevent rebase-merging. `enabled_pull_requests` must be `true`.", + "description": "either `true` to allow rebase-merging pull requests, or `false` to prevent rebase-merging. `enabled_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. `enabled_pull_requests` must be `true`.", + "description": "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`.", "type": "boolean", "x-go-name": "AllowRebaseMerge" }, "allow_squash_merge": { - "description": "Either `true` to allow squash-merging pull requests, or `false` to prevent squash-merging. `enabled_pull_requests` must be `true`.", + "description": "either `true` to allow squash-merging pull requests, or `false` to prevent squash-merging. `enabled_pull_requests` must be `true`.", "type": "boolean", "x-go-name": "AllowSquash" }, "archived": { - "description": "`true` to archive this repository. Note: You cannot unarchive repositories through the API.", + "description": "set to `true` to archive this repository. Note: You cannot unarchive repositories through the API.", "type": "boolean", "x-go-name": "Archived" }, "default_branch": { - "description": "Updates the default branch for this repository.", + "description": "ipdates the default branch for this repository.", "type": "string", "x-go-name": "DefaultBranch" }, "description": { - "description": "A short description of the repository.", + "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.", + "description": "either `true` to enable issues for this repository or `false` to disable them.", "type": "boolean", "x-go-name": "HasIssues" }, "has_wiki": { - "description": "Either `true` to enable the wiki for this repository or `false` to disable it.", + "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 whitepace for conflicts, or `false` to not ignore whitespace. `enabled_pull_requests` must be `true`.", + "description": "either `true` to ignore whitespace for conflicts, or `false` to not ignore whitespace. `enabled_pull_requests` must be `true`.", "type": "boolean", "x-go-name": "IgnoreWhitespaceConflicts" }, "name": { - "description": "Name of the repository", + "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.", + "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.", + "description": "a URL with more information about the repository.", "type": "string", "x-go-name": "Website" } From 2dc0a90fbebf6cc85b15b192e25efed6ad185abc Mon Sep 17 00:00:00 2001 From: Richard Mahn Date: Tue, 28 May 2019 10:41:07 -0400 Subject: [PATCH 24/30] Fixes typo and standardizes comments in the EditRepoOption struct for swagger --- modules/structs/repo.go | 2 +- templates/swagger/v1_json.tmpl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/structs/repo.go b/modules/structs/repo.go index b7ab990eb463..ae7cc6f43216 100644 --- a/modules/structs/repo.go +++ b/modules/structs/repo.go @@ -107,7 +107,7 @@ type EditRepoOption struct { 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`. AllowSquash *bool `json:"allow_squash_merge,omitempty"` - // set to `true` to archive this repository. Note: You cannot unarchive repositories through the API. + // set to `true` to archive this repository. Note: you cannot unarchive repositories through the API. Archived *bool `json:"archived,omitempty"` } diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index df5026ddbf33..10592e7fc8ad 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -7819,7 +7819,7 @@ "x-go-name": "AllowSquash" }, "archived": { - "description": "set to `true` to archive this repository. Note: You cannot unarchive repositories through the API.", + "description": "set to `true` to archive this repository. Note: you cannot unarchive repositories through the API.", "type": "boolean", "x-go-name": "Archived" }, From e641151173ad18bf393a6c29ded286b47b080db5 Mon Sep 17 00:00:00 2001 From: Richard Mahn Date: Tue, 28 May 2019 10:43:26 -0400 Subject: [PATCH 25/30] Actually can unarchive through the API --- modules/structs/repo.go | 2 +- templates/swagger/v1_json.tmpl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/structs/repo.go b/modules/structs/repo.go index ae7cc6f43216..9855d70d7c13 100644 --- a/modules/structs/repo.go +++ b/modules/structs/repo.go @@ -107,7 +107,7 @@ type EditRepoOption struct { 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`. AllowSquash *bool `json:"allow_squash_merge,omitempty"` - // set to `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"` } diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 10592e7fc8ad..542d5f75047b 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -7819,7 +7819,7 @@ "x-go-name": "AllowSquash" }, "archived": { - "description": "set to `true` to archive this repository. Note: you cannot unarchive repositories through the API.", + "description": "set to `true` to archive this repository.", "type": "boolean", "x-go-name": "Archived" }, From e89a3262fbbaf0e1a93b663e26de9ef88e47908c Mon Sep 17 00:00:00 2001 From: Richard Mahn Date: Tue, 28 May 2019 10:50:57 -0400 Subject: [PATCH 26/30] Unlike delete, user doesn't have to be the owner of the org, just admin to the repo --- routers/api/v1/repo/repo.go | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go index 3c04e411b7e5..29e30591b120 100644 --- a/routers/api/v1/repo/repo.go +++ b/routers/api/v1/repo/repo.go @@ -535,19 +535,6 @@ func Edit(ctx *context.APIContext, opts api.EditRepoOption) { // "$ref": "#/responses/forbidden" // "422": // "$ref": "#/responses/validationError" - owner := ctx.Repo.Owner - - if owner.IsOrganization() && !ctx.User.IsAdmin { - isOwner, err := owner.IsOwnedBy(ctx.User.ID) - if err != nil { - ctx.Error(http.StatusInternalServerError, "IsOwnedBy", err) - return - } else if !isOwner { - ctx.Error(http.StatusForbidden, "", "Given user is not owner of organization.") - return - } - } - if err := updateBasicProperties(ctx, opts); err != nil { return } From f831557ad6468d8a10d1e92827b5823885f9d711 Mon Sep 17 00:00:00 2001 From: Richard Mahn Date: Tue, 28 May 2019 11:00:39 -0400 Subject: [PATCH 27/30] Fix to swagger comments for field name change --- modules/structs/repo.go | 10 +++++----- templates/swagger/v1_json.tmpl | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/modules/structs/repo.go b/modules/structs/repo.go index 9855d70d7c13..8594ed852371 100644 --- a/modules/structs/repo.go +++ b/modules/structs/repo.go @@ -97,15 +97,15 @@ type EditRepoOption struct { DefaultBranch *string `json:"default_branch,omitempty"` // either `true` to allow pull requests, or `false` to prevent pull request. AllowPullRequests *bool `json:"allow_pull_requests,omitempty"` - // either `true` to ignore whitespace 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. `allow_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. `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. `allow_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. `allow_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. `allow_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`. + // either `true` to allow squash-merging pull requests, or `false` to prevent squash-merging. `allow_pull_requests` must be `true`. AllowSquash *bool `json:"allow_squash_merge,omitempty"` // set to `true` to archive this repository. Archived *bool `json:"archived,omitempty"` diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 542d5f75047b..1d8c020f4bf5 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -7794,7 +7794,7 @@ "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. `enabled_pull_requests` must be `true`.", + "description": "either `true` to allow merging pull requests with a merge commit, or `false` to prevent merging pull requests with merge commits. `allow_pull_requests` must be `true`.", "type": "boolean", "x-go-name": "AllowMerge" }, @@ -7804,17 +7804,17 @@ "x-go-name": "AllowPullRequests" }, "allow_rebase": { - "description": "either `true` to allow rebase-merging pull requests, or `false` to prevent rebase-merging. `enabled_pull_requests` must be `true`.", + "description": "either `true` to allow rebase-merging pull requests, or `false` to prevent rebase-merging. `allow_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. `enabled_pull_requests` must be `true`.", + "description": "either `true` to allow rebase with explicit merge commits (--no-ff), or `false` to prevent rebase with explicit merge commits. `allow_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. `enabled_pull_requests` must be `true`.", + "description": "either `true` to allow squash-merging pull requests, or `false` to prevent squash-merging. `allow_pull_requests` must be `true`.", "type": "boolean", "x-go-name": "AllowSquash" }, @@ -7844,7 +7844,7 @@ "x-go-name": "HasWiki" }, "ignore_whitespace_conflicts": { - "description": "either `true` to ignore whitespace for conflicts, or `false` to not ignore whitespace. `enabled_pull_requests` must be `true`.", + "description": "either `true` to ignore whitespace for conflicts, or `false` to not ignore whitespace. `allow_pull_requests` must be `true`.", "type": "boolean", "x-go-name": "IgnoreWhitespaceConflicts" }, From 1b7bd5313085fb32b68ff0f0d753601d0c7f6c82 Mon Sep 17 00:00:00 2001 From: Richard Mahn Date: Tue, 28 May 2019 12:23:27 -0400 Subject: [PATCH 28/30] Update to swagger docs --- modules/structs/repo.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/structs/repo.go b/modules/structs/repo.go index 8594ed852371..7ab1003fb9d4 100644 --- a/modules/structs/repo.go +++ b/modules/structs/repo.go @@ -93,7 +93,7 @@ type EditRepoOption struct { 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"` - // ipdates the default branch for this repository. + // 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. AllowPullRequests *bool `json:"allow_pull_requests,omitempty"` From 1236fda922e9699a669c43767562970e5c94a52e Mon Sep 17 00:00:00 2001 From: Richard Mahn Date: Tue, 28 May 2019 12:42:09 -0400 Subject: [PATCH 29/30] Update swagger --- templates/swagger/v1_json.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 1d8c020f4bf5..af72decb329c 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -7824,7 +7824,7 @@ "x-go-name": "Archived" }, "default_branch": { - "description": "ipdates the default branch for this repository.", + "description": "sets the default branch for this repository.", "type": "string", "x-go-name": "DefaultBranch" }, From 3f44c1c419deebd343e8af9810413f7a3943dec0 Mon Sep 17 00:00:00 2001 From: Richard Mahn Date: Thu, 30 May 2019 09:27:10 -0400 Subject: [PATCH 30/30] Changes allow_pull_requests to has_pull_requests --- integrations/api_repo_edit_test.go | 12 ++++++------ models/repo.go | 6 +++--- modules/structs/repo.go | 14 +++++++------- routers/api/v1/repo/repo.go | 4 ++-- routers/api/v1/repo/repo_test.go | 4 ++-- templates/swagger/v1_json.tmpl | 28 ++++++++++++++-------------- 6 files changed, 34 insertions(+), 34 deletions(-) diff --git a/integrations/api_repo_edit_test.go b/integrations/api_repo_edit_test.go index df168452ee8f..3b2c916ab0c7 100644 --- a/integrations/api_repo_edit_test.go +++ b/integrations/api_repo_edit_test.go @@ -31,7 +31,7 @@ func getRepoEditOptionFromRepo(repo *models.Repository) *api.EditRepoOption { hasWiki = true } defaultBranch := repo.DefaultBranch - allowPullRequests := false + hasPullRequests := false ignoreWhitespaceConflicts := false allowMerge := false allowRebase := false @@ -39,7 +39,7 @@ func getRepoEditOptionFromRepo(repo *models.Repository) *api.EditRepoOption { allowSquash := false if unit, err := repo.GetUnit(models.UnitTypePullRequests); err == nil { config := unit.PullRequestsConfig() - allowPullRequests = true + hasPullRequests = true ignoreWhitespaceConflicts = config.IgnoreWhitespaceConflicts allowMerge = config.AllowMerge allowRebase = config.AllowRebase @@ -55,7 +55,7 @@ func getRepoEditOptionFromRepo(repo *models.Repository) *api.EditRepoOption { HasIssues: &hasIssues, HasWiki: &hasWiki, DefaultBranch: &defaultBranch, - AllowPullRequests: &allowPullRequests, + HasPullRequests: &hasPullRequests, IgnoreWhitespaceConflicts: &ignoreWhitespaceConflicts, AllowMerge: &allowMerge, AllowRebase: &allowRebase, @@ -76,7 +76,7 @@ func getNewRepoEditOption(opts *api.EditRepoOption) *api.EditRepoOption { hasIssues := !*opts.HasIssues hasWiki := !*opts.HasWiki defaultBranch := "master" - allowPullRequests := !*opts.AllowPullRequests + hasPullRequests := !*opts.HasPullRequests ignoreWhitespaceConflicts := !*opts.IgnoreWhitespaceConflicts allowMerge := !*opts.AllowMerge allowRebase := !*opts.AllowRebase @@ -89,10 +89,10 @@ func getNewRepoEditOption(opts *api.EditRepoOption) *api.EditRepoOption { Description: &description, Website: &website, Private: &private, + DefaultBranch: &defaultBranch, HasIssues: &hasIssues, HasWiki: &hasWiki, - DefaultBranch: &defaultBranch, - AllowPullRequests: &allowPullRequests, + HasPullRequests: &hasPullRequests, IgnoreWhitespaceConflicts: &ignoreWhitespaceConflicts, AllowMerge: &allowMerge, AllowRebase: &allowRebase, diff --git a/models/repo.go b/models/repo.go index 18469e518951..16684bdeef6d 100644 --- a/models/repo.go +++ b/models/repo.go @@ -282,7 +282,7 @@ func (repo *Repository) innerAPIFormat(e Engine, mode AccessMode, isParent bool) if _, err := repo.getUnit(e, UnitTypeWiki); err == nil { hasWiki = true } - allowPullRequests := false + hasPullRequests := false ignoreWhitespaceConflicts := false allowMerge := false allowRebase := false @@ -290,7 +290,7 @@ func (repo *Repository) innerAPIFormat(e Engine, mode AccessMode, isParent bool) allowSquash := false if unit, err := repo.getUnit(e, UnitTypePullRequests); err == nil { config := unit.PullRequestsConfig() - allowPullRequests = true + hasPullRequests = true ignoreWhitespaceConflicts = config.IgnoreWhitespaceConflicts allowMerge = config.AllowMerge allowRebase = config.AllowRebase @@ -325,7 +325,7 @@ func (repo *Repository) innerAPIFormat(e Engine, mode AccessMode, isParent bool) Permissions: permission, HasIssues: hasIssues, HasWiki: hasWiki, - AllowPullRequests: allowPullRequests, + HasPullRequests: hasPullRequests, IgnoreWhitespaceConflicts: ignoreWhitespaceConflicts, AllowMerge: allowMerge, AllowRebase: allowRebase, diff --git a/modules/structs/repo.go b/modules/structs/repo.go index 1c4f3632bfb3..b4d162b776fa 100644 --- a/modules/structs/repo.go +++ b/modules/structs/repo.go @@ -45,7 +45,7 @@ type Repository struct { Permissions *Permission `json:"permissions,omitempty"` HasIssues bool `json:"has_issues"` HasWiki bool `json:"has_wiki"` - AllowPullRequests bool `json:"allow_pull_requests"` + HasPullRequests bool `json:"has_pull_requests"` IgnoreWhitespaceConflicts bool `json:"ignore_whitespace_conflicts"` AllowMerge bool `json:"allow_merge_commits"` AllowRebase bool `json:"allow_rebase"` @@ -97,16 +97,16 @@ type EditRepoOption struct { // 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. - AllowPullRequests *bool `json:"allow_pull_requests,omitempty"` - // either `true` to ignore whitespace for conflicts, or `false` to not ignore whitespace. `allow_pull_requests` must be `true`. + 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. `allow_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"` - // either `true` to allow rebase-merging pull requests, or `false` to prevent rebase-merging. `allow_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. `allow_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. `allow_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`. AllowSquash *bool `json:"allow_squash_merge,omitempty"` // set to `true` to archive this repository. Archived *bool `json:"archived,omitempty"` diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go index 29e30591b120..f8df3e9fa12f 100644 --- a/routers/api/v1/repo/repo.go +++ b/routers/api/v1/repo/repo.go @@ -696,8 +696,8 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error { } } - if opts.AllowPullRequests != nil { - if *opts.AllowPullRequests { + 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. diff --git a/routers/api/v1/repo/repo_test.go b/routers/api/v1/repo/repo_test.go index ff59a9094020..053134ec6199 100644 --- a/routers/api/v1/repo/repo_test.go +++ b/routers/api/v1/repo/repo_test.go @@ -29,7 +29,7 @@ func TestRepoEdit(t *testing.T) { hasIssues := false hasWiki := false defaultBranch := "master" - allowPullRequests := true + hasPullRequests := true ignoreWhitespaceConflicts := true allowMerge := false allowRebase := false @@ -44,7 +44,7 @@ func TestRepoEdit(t *testing.T) { HasIssues: &hasIssues, HasWiki: &hasWiki, DefaultBranch: &defaultBranch, - AllowPullRequests: &allowPullRequests, + HasPullRequests: &hasPullRequests, IgnoreWhitespaceConflicts: &ignoreWhitespaceConflicts, AllowMerge: &allowMerge, AllowRebase: &allowRebase, diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 4069187f55f0..a3090d1d52eb 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -7794,27 +7794,22 @@ "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. `allow_pull_requests` must be `true`.", + "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_pull_requests": { - "description": "either `true` to allow pull requests, or `false` to prevent pull request.", - "type": "boolean", - "x-go-name": "AllowPullRequests" - }, "allow_rebase": { - "description": "either `true` to allow rebase-merging pull requests, or `false` to prevent rebase-merging. `allow_pull_requests` must be `true`.", + "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. `allow_pull_requests` must be `true`.", + "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. `allow_pull_requests` must be `true`.", + "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" }, @@ -7838,13 +7833,18 @@ "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. `allow_pull_requests` must be `true`.", + "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" }, @@ -9195,10 +9195,6 @@ "type": "boolean", "x-go-name": "AllowMerge" }, - "allow_pull_requests": { - "type": "boolean", - "x-go-name": "AllowPullRequests" - }, "allow_rebase": { "type": "boolean", "x-go-name": "AllowRebase" @@ -9257,6 +9253,10 @@ "type": "boolean", "x-go-name": "HasIssues" }, + "has_pull_requests": { + "type": "boolean", + "x-go-name": "HasPullRequests" + }, "has_wiki": { "type": "boolean", "x-go-name": "HasWiki"