loading up the forgejo repo on tangled to test page performance
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

add user rename endpoint to admin api (#22789)

this is a simple endpoint that adds the ability to rename users to the
admin API.

Note: this is not in a mergeable state. It would be better if this was
handled by a PATCH/POST to the /api/v1/admin/users/{username} endpoint
and the username is modified.

---------

Co-authored-by: Jason Song <i@wolfogre.com>

authored by

techknowlogick
Jason Song
and committed by
GitHub
03591f0f aac07d01

+206 -44
+2 -2
models/issues/pull.go
··· 660 660 661 661 // GetAllUnmergedAgitPullRequestByPoster get all unmerged agit flow pull request 662 662 // By poster id. 663 - func GetAllUnmergedAgitPullRequestByPoster(uid int64) ([]*PullRequest, error) { 663 + func GetAllUnmergedAgitPullRequestByPoster(ctx context.Context, uid int64) ([]*PullRequest, error) { 664 664 pulls := make([]*PullRequest, 0, 10) 665 665 666 - err := db.GetEngine(db.DefaultContext). 666 + err := db.GetEngine(ctx). 667 667 Where("has_merged=? AND flow = ? AND issue.is_closed=? AND issue.poster_id=?", 668 668 false, PullRequestFlowAGit, false, uid). 669 669 Join("INNER", "issue", "issue.id=pull_request.issue_id").
+2 -2
models/user/user.go
··· 742 742 } 743 743 744 744 // ChangeUserName changes all corresponding setting from old user name to new one. 745 - func ChangeUserName(u *User, newUserName string) (err error) { 745 + func ChangeUserName(ctx context.Context, u *User, newUserName string) (err error) { 746 746 oldUserName := u.Name 747 747 if err = IsUsableUsername(newUserName); err != nil { 748 748 return err 749 749 } 750 750 751 - ctx, committer, err := db.TxContext(db.DefaultContext) 751 + ctx, committer, err := db.TxContext(ctx) 752 752 if err != nil { 753 753 return err 754 754 }
+9
modules/structs/user.go
··· 93 93 HideEmail *bool `json:"hide_email"` 94 94 HideActivity *bool `json:"hide_activity"` 95 95 } 96 + 97 + // RenameUserOption options when renaming a user 98 + type RenameUserOption struct { 99 + // New username for this user. This name cannot be in use yet by any other user. 100 + // 101 + // required: true 102 + // unique: true 103 + NewName string `json:"new_username" binding:"Required"` 104 + }
+58
routers/api/v1/admin/user.go
··· 461 461 ctx.SetTotalCountHeader(maxResults) 462 462 ctx.JSON(http.StatusOK, &results) 463 463 } 464 + 465 + // RenameUser api for renaming a user 466 + func RenameUser(ctx *context.APIContext) { 467 + // swagger:operation POST /admin/users/{username}/rename admin adminRenameUser 468 + // --- 469 + // summary: Rename a user 470 + // produces: 471 + // - application/json 472 + // parameters: 473 + // - name: username 474 + // in: path 475 + // description: existing username of user 476 + // type: string 477 + // required: true 478 + // - name: body 479 + // in: body 480 + // required: true 481 + // schema: 482 + // "$ref": "#/definitions/RenameUserOption" 483 + // responses: 484 + // "204": 485 + // "$ref": "#/responses/empty" 486 + // "403": 487 + // "$ref": "#/responses/forbidden" 488 + // "422": 489 + // "$ref": "#/responses/validationError" 490 + 491 + if ctx.ContextUser.IsOrganization() { 492 + ctx.Error(http.StatusUnprocessableEntity, "", fmt.Errorf("%s is an organization not a user", ctx.ContextUser.Name)) 493 + return 494 + } 495 + 496 + newName := web.GetForm(ctx).(*api.RenameUserOption).NewName 497 + 498 + if strings.EqualFold(newName, ctx.ContextUser.Name) { 499 + // Noop as username is not changed 500 + ctx.Status(http.StatusNoContent) 501 + return 502 + } 503 + 504 + // Check if user name has been changed 505 + if err := user_service.RenameUser(ctx, ctx.ContextUser, newName); err != nil { 506 + switch { 507 + case user_model.IsErrUserAlreadyExist(err): 508 + ctx.Error(http.StatusUnprocessableEntity, "", ctx.Tr("form.username_been_taken")) 509 + case db.IsErrNameReserved(err): 510 + ctx.Error(http.StatusUnprocessableEntity, "", ctx.Tr("user.form.name_reserved", newName)) 511 + case db.IsErrNamePatternNotAllowed(err): 512 + ctx.Error(http.StatusUnprocessableEntity, "", ctx.Tr("user.form.name_pattern_not_allowed", newName)) 513 + case db.IsErrNameCharsNotAllowed(err): 514 + ctx.Error(http.StatusUnprocessableEntity, "", ctx.Tr("user.form.name_chars_not_allowed", newName)) 515 + default: 516 + ctx.ServerError("ChangeUserName", err) 517 + } 518 + return 519 + } 520 + ctx.Status(http.StatusNoContent) 521 + }
+1
routers/api/v1/api.go
··· 1257 1257 m.Get("/orgs", org.ListUserOrgs) 1258 1258 m.Post("/orgs", bind(api.CreateOrgOption{}), admin.CreateOrg) 1259 1259 m.Post("/repos", bind(api.CreateRepoOption{}), admin.CreateRepo) 1260 + m.Post("/rename", bind(api.RenameUserOption{}), admin.RenameUser) 1260 1261 }, context_service.UserAssignmentAPI()) 1261 1262 }) 1262 1263 m.Group("/unadopted", func() {
+3
routers/api/v1/swagger/options.go
··· 49 49 CreateKeyOption api.CreateKeyOption 50 50 51 51 // in:body 52 + RenameUserOption api.RenameUserOption 53 + 54 + // in:body 52 55 CreateLabelOption api.CreateLabelOption 53 56 // in:body 54 57 EditLabelOption api.EditLabelOption
+1 -1
routers/web/org/setting.go
··· 79 79 ctx.Data["OrgName"] = true 80 80 ctx.RenderWithErr(ctx.Tr("form.username_been_taken"), tplSettingsOptions, &form) 81 81 return 82 - } else if err = user_model.ChangeUserName(org.AsUser(), form.Name); err != nil { 82 + } else if err = user_model.ChangeUserName(ctx, org.AsUser(), form.Name); err != nil { 83 83 switch { 84 84 case db.IsErrNameReserved(err): 85 85 ctx.Data["OrgName"] = true
+15 -37
routers/web/user/setting/profile.go
··· 27 27 "code.gitea.io/gitea/modules/util" 28 28 "code.gitea.io/gitea/modules/web" 29 29 "code.gitea.io/gitea/modules/web/middleware" 30 - "code.gitea.io/gitea/services/agit" 31 30 "code.gitea.io/gitea/services/forms" 32 - container_service "code.gitea.io/gitea/services/packages/container" 33 31 user_service "code.gitea.io/gitea/services/user" 34 32 ) 35 33 ··· 57 55 return fmt.Errorf(ctx.Tr("form.username_change_not_local_user")) 58 56 } 59 57 60 - // Check if user name has been changed 61 - if user.LowerName != strings.ToLower(newName) { 62 - if err := user_model.ChangeUserName(user, newName); err != nil { 63 - switch { 64 - case user_model.IsErrUserAlreadyExist(err): 65 - ctx.Flash.Error(ctx.Tr("form.username_been_taken")) 66 - case user_model.IsErrEmailAlreadyUsed(err): 67 - ctx.Flash.Error(ctx.Tr("form.email_been_used")) 68 - case db.IsErrNameReserved(err): 69 - ctx.Flash.Error(ctx.Tr("user.form.name_reserved", newName)) 70 - case db.IsErrNamePatternNotAllowed(err): 71 - ctx.Flash.Error(ctx.Tr("user.form.name_pattern_not_allowed", newName)) 72 - case db.IsErrNameCharsNotAllowed(err): 73 - ctx.Flash.Error(ctx.Tr("user.form.name_chars_not_allowed", newName)) 74 - default: 75 - ctx.ServerError("ChangeUserName", err) 76 - } 77 - return err 78 - } 79 - } else { 80 - if err := repo_model.UpdateRepositoryOwnerNames(user.ID, newName); err != nil { 81 - ctx.ServerError("UpdateRepository", err) 82 - return err 58 + // rename user 59 + if err := user_service.RenameUser(ctx, user, newName); err != nil { 60 + switch { 61 + case user_model.IsErrUserAlreadyExist(err): 62 + ctx.Flash.Error(ctx.Tr("form.username_been_taken")) 63 + case user_model.IsErrEmailAlreadyUsed(err): 64 + ctx.Flash.Error(ctx.Tr("form.email_been_used")) 65 + case db.IsErrNameReserved(err): 66 + ctx.Flash.Error(ctx.Tr("user.form.name_reserved", newName)) 67 + case db.IsErrNamePatternNotAllowed(err): 68 + ctx.Flash.Error(ctx.Tr("user.form.name_pattern_not_allowed", newName)) 69 + case db.IsErrNameCharsNotAllowed(err): 70 + ctx.Flash.Error(ctx.Tr("user.form.name_chars_not_allowed", newName)) 71 + default: 72 + ctx.ServerError("ChangeUserName", err) 83 73 } 84 - } 85 - 86 - // update all agit flow pull request header 87 - err := agit.UserNameChanged(user, newName) 88 - if err != nil { 89 - ctx.ServerError("agit.UserNameChanged", err) 90 74 return err 91 75 } 92 76 93 - if err := container_service.UpdateRepositoryNames(ctx, user, newName); err != nil { 94 - ctx.ServerError("UpdateRepositoryNames", err) 95 - return err 96 - } 97 - 98 - log.Trace("User name changed: %s -> %s", user.Name, newName) 99 77 return nil 100 78 } 101 79
+2 -2
services/agit/agit.go
··· 226 226 } 227 227 228 228 // UserNameChanged handle user name change for agit flow pull 229 - func UserNameChanged(user *user_model.User, newName string) error { 230 - pulls, err := issues_model.GetAllUnmergedAgitPullRequestByPoster(user.ID) 229 + func UserNameChanged(ctx context.Context, user *user_model.User, newName string) error { 230 + pulls, err := issues_model.GetAllUnmergedAgitPullRequestByPoster(ctx, user.ID) 231 231 if err != nil { 232 232 return err 233 233 }
+41
services/user/rename.go
··· 1 + // Copyright 2023 The Gitea Authors. All rights reserved. 2 + // SPDX-License-Identifier: MIT 3 + 4 + package user 5 + 6 + import ( 7 + "context" 8 + "fmt" 9 + "strings" 10 + 11 + user_model "code.gitea.io/gitea/models/user" 12 + "code.gitea.io/gitea/modules/log" 13 + "code.gitea.io/gitea/services/agit" 14 + container_service "code.gitea.io/gitea/services/packages/container" 15 + ) 16 + 17 + func renameUser(ctx context.Context, u *user_model.User, newUserName string) error { 18 + if u.IsOrganization() { 19 + return fmt.Errorf("cannot rename organization") 20 + } 21 + 22 + if err := user_model.ChangeUserName(ctx, u, newUserName); err != nil { 23 + return err 24 + } 25 + 26 + if err := agit.UserNameChanged(ctx, u, newUserName); err != nil { 27 + return err 28 + } 29 + if err := container_service.UpdateRepositoryNames(ctx, u, newUserName); err != nil { 30 + return err 31 + } 32 + 33 + u.Name = newUserName 34 + u.LowerName = strings.ToLower(newUserName) 35 + if err := user_model.UpdateUser(ctx, u, false); err != nil { 36 + return err 37 + } 38 + 39 + log.Trace("User name changed: %s -> %s", u.Name, newUserName) 40 + return nil 41 + }
+16
services/user/user.go
··· 27 27 "code.gitea.io/gitea/services/packages" 28 28 ) 29 29 30 + // RenameUser renames a user 31 + func RenameUser(ctx context.Context, u *user_model.User, newUserName string) error { 32 + ctx, committer, err := db.TxContext(ctx) 33 + if err != nil { 34 + return err 35 + } 36 + defer committer.Close() 37 + if err := renameUser(ctx, u, newUserName); err != nil { 38 + return err 39 + } 40 + if err := committer.Commit(); err != nil { 41 + return err 42 + } 43 + return err 44 + } 45 + 30 46 // DeleteUser completely and permanently deletes everything of a user, 31 47 // but issues/comments/pulls will be kept and shown as someone has been deleted, 32 48 // unless the user is younger than USER_DELETE_WITH_COMMENTS_MAX_DAYS.
+56
templates/swagger/v1_json.tmpl
··· 679 679 } 680 680 } 681 681 }, 682 + "/admin/users/{username}/rename": { 683 + "post": { 684 + "produces": [ 685 + "application/json" 686 + ], 687 + "tags": [ 688 + "admin" 689 + ], 690 + "summary": "Rename a user", 691 + "operationId": "adminRenameUser", 692 + "parameters": [ 693 + { 694 + "type": "string", 695 + "description": "existing username of user", 696 + "name": "username", 697 + "in": "path", 698 + "required": true 699 + }, 700 + { 701 + "name": "body", 702 + "in": "body", 703 + "required": true, 704 + "schema": { 705 + "$ref": "#/definitions/RenameUserOption" 706 + } 707 + } 708 + ], 709 + "responses": { 710 + "204": { 711 + "$ref": "#/responses/empty" 712 + }, 713 + "403": { 714 + "$ref": "#/responses/forbidden" 715 + }, 716 + "422": { 717 + "$ref": "#/responses/validationError" 718 + } 719 + } 720 + } 721 + }, 682 722 "/admin/users/{username}/repos": { 683 723 "post": { 684 724 "consumes": [ ··· 19101 19141 "zipball_url": { 19102 19142 "type": "string", 19103 19143 "x-go-name": "ZipURL" 19144 + } 19145 + }, 19146 + "x-go-package": "code.gitea.io/gitea/modules/structs" 19147 + }, 19148 + "RenameUserOption": { 19149 + "description": "RenameUserOption options when renaming a user", 19150 + "type": "object", 19151 + "required": [ 19152 + "new_username" 19153 + ], 19154 + "properties": { 19155 + "new_username": { 19156 + "description": "New username for this user. This name cannot be in use yet by any other user.", 19157 + "type": "string", 19158 + "uniqueItems": true, 19159 + "x-go-name": "NewName" 19104 19160 } 19105 19161 }, 19106 19162 "x-go-package": "code.gitea.io/gitea/modules/structs"