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.

Refactor rename user and rename organization (#24052)

This PR is a refactor at the beginning. And now it did 4 things.
- [x] Move renaming organizaiton and user logics into services layer and
merged as one function
- [x] Support rename a user capitalization only. For example, rename the
user from `Lunny` to `lunny`. We just need to change one table `user`
and others should not be touched.
- [x] Before this PR, some renaming were missed like `agit`
- [x] Fix bug the API reutrned from `http.StatusNoContent` to `http.StatusOK`

authored by

Lunny Xiao and committed by
GitHub
c59a0572 64f6a5d1

+267 -188
+8
models/repo/repo.go
··· 832 832 IsArchived: false, 833 833 }) 834 834 } 835 + 836 + // UpdateRepositoryOwnerName updates the owner name of all repositories owned by the user 837 + func UpdateRepositoryOwnerName(ctx context.Context, oldUserName, newUserName string) error { 838 + if _, err := db.GetEngine(ctx).Exec("UPDATE `repository` SET owner_name=? WHERE owner_name=?", newUserName, oldUserName); err != nil { 839 + return fmt.Errorf("change repo owner name: %w", err) 840 + } 841 + return nil 842 + }
+31 -7
models/user/error.go
··· 9 9 "code.gitea.io/gitea/modules/util" 10 10 ) 11 11 12 - // ____ ___ 13 - // | | \______ ___________ 14 - // | | / ___// __ \_ __ \ 15 - // | | /\___ \\ ___/| | \/ 16 - // |______//____ >\___ >__| 17 - // \/ \/ 18 - 19 12 // ErrUserAlreadyExist represents a "user already exists" error. 20 13 type ErrUserAlreadyExist struct { 21 14 Name string ··· 99 92 func (err ErrUserInactive) Unwrap() error { 100 93 return util.ErrPermissionDenied 101 94 } 95 + 96 + // ErrUserIsNotLocal represents a "ErrUserIsNotLocal" kind of error. 97 + type ErrUserIsNotLocal struct { 98 + UID int64 99 + Name string 100 + } 101 + 102 + func (err ErrUserIsNotLocal) Error() string { 103 + return fmt.Sprintf("user is not local type [uid: %d, name: %s]", err.UID, err.Name) 104 + } 105 + 106 + // IsErrUserIsNotLocal 107 + func IsErrUserIsNotLocal(err error) bool { 108 + _, ok := err.(ErrUserIsNotLocal) 109 + return ok 110 + } 111 + 112 + type ErrUsernameNotChanged struct { 113 + UID int64 114 + Name string 115 + } 116 + 117 + func (err ErrUsernameNotChanged) Error() string { 118 + return fmt.Sprintf("username hasn't been changed[uid: %d, name: %s]", err.UID, err.Name) 119 + } 120 + 121 + // IsErrUsernameNotChanged 122 + func IsErrUsernameNotChanged(err error) bool { 123 + _, ok := err.(ErrUsernameNotChanged) 124 + return ok 125 + }
-45
models/user/user.go
··· 9 9 "encoding/hex" 10 10 "fmt" 11 11 "net/url" 12 - "os" 13 12 "path/filepath" 14 13 "strings" 15 14 "time" ··· 753 752 return user 754 753 } 755 754 } 756 - return nil 757 - } 758 - 759 - // ChangeUserName changes all corresponding setting from old user name to new one. 760 - func ChangeUserName(ctx context.Context, u *User, newUserName string) (err error) { 761 - oldUserName := u.Name 762 - if err = IsUsableUsername(newUserName); err != nil { 763 - return err 764 - } 765 - 766 - ctx, committer, err := db.TxContext(ctx) 767 - if err != nil { 768 - return err 769 - } 770 - defer committer.Close() 771 - 772 - isExist, err := IsUserExist(ctx, 0, newUserName) 773 - if err != nil { 774 - return err 775 - } else if isExist { 776 - return ErrUserAlreadyExist{newUserName} 777 - } 778 - 779 - if _, err = db.GetEngine(ctx).Exec("UPDATE `repository` SET owner_name=? WHERE owner_name=?", newUserName, oldUserName); err != nil { 780 - return fmt.Errorf("Change repo owner name: %w", err) 781 - } 782 - 783 - // Do not fail if directory does not exist 784 - if err = util.Rename(UserPath(oldUserName), UserPath(newUserName)); err != nil && !os.IsNotExist(err) { 785 - return fmt.Errorf("Rename user directory: %w", err) 786 - } 787 - 788 - if err = NewUserRedirect(ctx, u.ID, oldUserName, newUserName); err != nil { 789 - return err 790 - } 791 - 792 - if err = committer.Commit(); err != nil { 793 - if err2 := util.Rename(UserPath(newUserName), UserPath(oldUserName)); err2 != nil && !os.IsNotExist(err2) { 794 - log.Critical("Unable to rollback directory change during failed username change from: %s to: %s. DB Error: %v. Filesystem Error: %v", oldUserName, newUserName, err, err2) 795 - return fmt.Errorf("failed to rollback directory change during failed username change from: %s to: %s. DB Error: %w. Filesystem Error: %v", oldUserName, newUserName, err, err2) 796 - } 797 - return err 798 - } 799 - 800 755 return nil 801 756 } 802 757
+1
options/locale/locale_en-US.ini
··· 520 520 521 521 username_been_taken = The username is already taken. 522 522 username_change_not_local_user = Non-local users are not allowed to change their username. 523 + username_has_not_been_changed = Username has not been changed 523 524 repo_name_been_taken = The repository name is already used. 524 525 repository_force_private = Force Private is enabled: private repositories cannot be made public. 525 526 repository_files_already_exist = Files already exist for this repository. Contact the system administrator.
+7 -7
routers/api/v1/admin/user.go
··· 502 502 return 503 503 } 504 504 505 + oldName := ctx.ContextUser.Name 505 506 newName := web.GetForm(ctx).(*api.RenameUserOption).NewName 506 507 507 - if strings.EqualFold(newName, ctx.ContextUser.Name) { 508 - // Noop as username is not changed 509 - ctx.Status(http.StatusNoContent) 510 - return 511 - } 512 - 513 508 // Check if user name has been changed 514 509 if err := user_service.RenameUser(ctx, ctx.ContextUser, newName); err != nil { 515 510 switch { 511 + case user_model.IsErrUsernameNotChanged(err): 512 + // Noop as username is not changed 513 + ctx.Status(http.StatusNoContent) 516 514 case user_model.IsErrUserAlreadyExist(err): 517 515 ctx.Error(http.StatusUnprocessableEntity, "", ctx.Tr("form.username_been_taken")) 518 516 case db.IsErrNameReserved(err): ··· 526 524 } 527 525 return 528 526 } 529 - ctx.Status(http.StatusNoContent) 527 + 528 + log.Trace("User name changed: %s -> %s", oldName, newName) 529 + ctx.Status(http.StatusOK) 530 530 }
+15 -24
routers/web/org/setting.go
··· 22 22 "code.gitea.io/gitea/modules/web" 23 23 user_setting "code.gitea.io/gitea/routers/web/user/setting" 24 24 "code.gitea.io/gitea/services/forms" 25 - "code.gitea.io/gitea/services/org" 26 - container_service "code.gitea.io/gitea/services/packages/container" 25 + org_service "code.gitea.io/gitea/services/org" 27 26 repo_service "code.gitea.io/gitea/services/repository" 28 27 user_service "code.gitea.io/gitea/services/user" 29 28 ) ··· 67 66 nameChanged := org.Name != form.Name 68 67 69 68 // Check if organization name has been changed. 70 - if org.LowerName != strings.ToLower(form.Name) { 71 - isExist, err := user_model.IsUserExist(ctx, org.ID, form.Name) 72 - if err != nil { 73 - ctx.ServerError("IsUserExist", err) 74 - return 75 - } else if isExist { 69 + if nameChanged { 70 + err := org_service.RenameOrganization(ctx, org, form.Name) 71 + switch { 72 + case user_model.IsErrUserAlreadyExist(err): 76 73 ctx.Data["OrgName"] = true 77 74 ctx.RenderWithErr(ctx.Tr("form.username_been_taken"), tplSettingsOptions, &form) 78 75 return 79 - } else if err = user_model.ChangeUserName(ctx, org.AsUser(), form.Name); err != nil { 80 - switch { 81 - case db.IsErrNameReserved(err): 82 - ctx.Data["OrgName"] = true 83 - ctx.RenderWithErr(ctx.Tr("repo.form.name_reserved", err.(db.ErrNameReserved).Name), tplSettingsOptions, &form) 84 - case db.IsErrNamePatternNotAllowed(err): 85 - ctx.Data["OrgName"] = true 86 - ctx.RenderWithErr(ctx.Tr("repo.form.name_pattern_not_allowed", err.(db.ErrNamePatternNotAllowed).Pattern), tplSettingsOptions, &form) 87 - default: 88 - ctx.ServerError("ChangeUserName", err) 89 - } 76 + case db.IsErrNameReserved(err): 77 + ctx.Data["OrgName"] = true 78 + ctx.RenderWithErr(ctx.Tr("repo.form.name_reserved", err.(db.ErrNameReserved).Name), tplSettingsOptions, &form) 79 + return 80 + case db.IsErrNamePatternNotAllowed(err): 81 + ctx.Data["OrgName"] = true 82 + ctx.RenderWithErr(ctx.Tr("repo.form.name_pattern_not_allowed", err.(db.ErrNamePatternNotAllowed).Pattern), tplSettingsOptions, &form) 90 83 return 91 - } 92 - 93 - if err := container_service.UpdateRepositoryNames(ctx, org.AsUser(), form.Name); err != nil { 94 - ctx.ServerError("UpdateRepositoryNames", err) 84 + case err != nil: 85 + ctx.ServerError("org_service.RenameOrganization", err) 95 86 return 96 87 } 97 88 ··· 186 177 return 187 178 } 188 179 189 - if err := org.DeleteOrganization(ctx.Org.Organization); err != nil { 180 + if err := org_service.DeleteOrganization(ctx.Org.Organization); err != nil { 190 181 if models.IsErrUserOwnRepos(err) { 191 182 ctx.Flash.Error(ctx.Tr("form.org_still_own_repo")) 192 183 ctx.Redirect(ctx.Org.OrgLink + "/settings/delete")
+8 -7
routers/web/user/setting/profile.go
··· 49 49 50 50 // HandleUsernameChange handle username changes from user settings and admin interface 51 51 func HandleUsernameChange(ctx *context.Context, user *user_model.User, newName string) error { 52 - // Non-local users are not allowed to change their username. 53 - if !user.IsLocal() { 54 - ctx.Flash.Error(ctx.Tr("form.username_change_not_local_user")) 55 - return fmt.Errorf(ctx.Tr("form.username_change_not_local_user")) 56 - } 57 - 52 + oldName := user.Name 58 53 // rename user 59 54 if err := user_service.RenameUser(ctx, user, newName); err != nil { 60 55 switch { 56 + // Noop as username is not changed 57 + case user_model.IsErrUsernameNotChanged(err): 58 + ctx.Flash.Error(ctx.Tr("form.username_has_not_been_changed")) 59 + // Non-local users are not allowed to change their username. 60 + case user_model.IsErrUserIsNotLocal(err): 61 + ctx.Flash.Error(ctx.Tr("form.username_change_not_local_user")) 61 62 case user_model.IsErrUserAlreadyExist(err): 62 63 ctx.Flash.Error(ctx.Tr("form.username_been_taken")) 63 64 case user_model.IsErrEmailAlreadyUsed(err): ··· 73 74 } 74 75 return err 75 76 } 76 - 77 + log.Trace("User name changed: %s -> %s", oldName, newName) 77 78 return nil 78 79 } 79 80
+12 -5
services/org/org.go
··· 4 4 package org 5 5 6 6 import ( 7 + "context" 7 8 "fmt" 8 9 9 10 "code.gitea.io/gitea/models" 10 11 "code.gitea.io/gitea/models/db" 11 - "code.gitea.io/gitea/models/organization" 12 + org_model "code.gitea.io/gitea/models/organization" 12 13 packages_model "code.gitea.io/gitea/models/packages" 13 14 repo_model "code.gitea.io/gitea/models/repo" 14 15 user_model "code.gitea.io/gitea/models/user" 15 16 "code.gitea.io/gitea/modules/storage" 16 17 "code.gitea.io/gitea/modules/util" 18 + user_service "code.gitea.io/gitea/services/user" 17 19 ) 18 20 19 21 // DeleteOrganization completely and permanently deletes everything of organization. 20 - func DeleteOrganization(org *organization.Organization) error { 22 + func DeleteOrganization(org *org_model.Organization) error { 21 23 ctx, commiter, err := db.TxContext(db.DefaultContext) 22 24 if err != nil { 23 25 return err ··· 39 41 return models.ErrUserOwnPackages{UID: org.ID} 40 42 } 41 43 42 - if err := organization.DeleteOrganization(ctx, org); err != nil { 44 + if err := org_model.DeleteOrganization(ctx, org); err != nil { 43 45 return fmt.Errorf("DeleteOrganization: %w", err) 44 46 } 45 47 ··· 53 55 path := user_model.UserPath(org.Name) 54 56 55 57 if err := util.RemoveAll(path); err != nil { 56 - return fmt.Errorf("Failed to RemoveAll %s: %w", path, err) 58 + return fmt.Errorf("failed to RemoveAll %s: %w", path, err) 57 59 } 58 60 59 61 if len(org.Avatar) > 0 { 60 62 avatarPath := org.CustomAvatarRelativePath() 61 63 if err := storage.Avatars.Delete(avatarPath); err != nil { 62 - return fmt.Errorf("Failed to remove %s: %w", avatarPath, err) 64 + return fmt.Errorf("failed to remove %s: %w", avatarPath, err) 63 65 } 64 66 } 65 67 66 68 return nil 67 69 } 70 + 71 + // RenameOrganization renames an organization. 72 + func RenameOrganization(ctx context.Context, org *org_model.Organization, newName string) error { 73 + return user_service.RenameUser(ctx, org.AsUser(), newName) 74 + }
+62
services/user/avatar.go
··· 1 + // Copyright 2023 The Gitea Authors. All rights reserved. 2 + // SPDX-License-Identifier: MIT 3 + 4 + package user 5 + 6 + import ( 7 + "fmt" 8 + "io" 9 + 10 + "code.gitea.io/gitea/models/db" 11 + user_model "code.gitea.io/gitea/models/user" 12 + "code.gitea.io/gitea/modules/avatar" 13 + "code.gitea.io/gitea/modules/log" 14 + "code.gitea.io/gitea/modules/storage" 15 + ) 16 + 17 + // UploadAvatar saves custom avatar for user. 18 + func UploadAvatar(u *user_model.User, data []byte) error { 19 + avatarData, err := avatar.ProcessAvatarImage(data) 20 + if err != nil { 21 + return err 22 + } 23 + 24 + ctx, committer, err := db.TxContext(db.DefaultContext) 25 + if err != nil { 26 + return err 27 + } 28 + defer committer.Close() 29 + 30 + u.UseCustomAvatar = true 31 + u.Avatar = avatar.HashAvatar(u.ID, data) 32 + if err = user_model.UpdateUserCols(ctx, u, "use_custom_avatar", "avatar"); err != nil { 33 + return fmt.Errorf("updateUser: %w", err) 34 + } 35 + 36 + if err := storage.SaveFrom(storage.Avatars, u.CustomAvatarRelativePath(), func(w io.Writer) error { 37 + _, err := w.Write(avatarData) 38 + return err 39 + }); err != nil { 40 + return fmt.Errorf("Failed to create dir %s: %w", u.CustomAvatarRelativePath(), err) 41 + } 42 + 43 + return committer.Commit() 44 + } 45 + 46 + // DeleteAvatar deletes the user's custom avatar. 47 + func DeleteAvatar(u *user_model.User) error { 48 + aPath := u.CustomAvatarRelativePath() 49 + log.Trace("DeleteAvatar[%d]: %s", u.ID, aPath) 50 + if len(u.Avatar) > 0 { 51 + if err := storage.Avatars.Delete(aPath); err != nil { 52 + return fmt.Errorf("Failed to remove %s: %w", aPath, err) 53 + } 54 + } 55 + 56 + u.UseCustomAvatar = false 57 + u.Avatar = "" 58 + if _, err := db.GetEngine(db.DefaultContext).ID(u.ID).Cols("avatar, use_custom_avatar").Update(u); err != nil { 59 + return fmt.Errorf("UpdateUser: %w", err) 60 + } 61 + return nil 62 + }
-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 - }
+82 -52
services/user/user.go
··· 6 6 import ( 7 7 "context" 8 8 "fmt" 9 - "io" 9 + "os" 10 + "strings" 10 11 "time" 11 12 12 13 "code.gitea.io/gitea/models" ··· 17 18 repo_model "code.gitea.io/gitea/models/repo" 18 19 system_model "code.gitea.io/gitea/models/system" 19 20 user_model "code.gitea.io/gitea/models/user" 20 - "code.gitea.io/gitea/modules/avatar" 21 21 "code.gitea.io/gitea/modules/eventsource" 22 22 "code.gitea.io/gitea/modules/log" 23 23 "code.gitea.io/gitea/modules/setting" 24 24 "code.gitea.io/gitea/modules/storage" 25 25 "code.gitea.io/gitea/modules/util" 26 + "code.gitea.io/gitea/services/agit" 26 27 "code.gitea.io/gitea/services/packages" 28 + container_service "code.gitea.io/gitea/services/packages/container" 27 29 ) 28 30 29 31 // RenameUser renames a user 30 32 func RenameUser(ctx context.Context, u *user_model.User, newUserName string) error { 33 + // Non-local users are not allowed to change their username. 34 + if !u.IsOrganization() && !u.IsLocal() { 35 + return user_model.ErrUserIsNotLocal{ 36 + UID: u.ID, 37 + Name: u.Name, 38 + } 39 + } 40 + 41 + if newUserName == u.Name { 42 + return user_model.ErrUsernameNotChanged{ 43 + UID: u.ID, 44 + Name: u.Name, 45 + } 46 + } 47 + 48 + if err := user_model.IsUsableUsername(newUserName); err != nil { 49 + return err 50 + } 51 + 52 + onlyCapitalization := strings.EqualFold(newUserName, u.Name) 53 + oldUserName := u.Name 54 + 55 + if onlyCapitalization { 56 + u.Name = newUserName 57 + if err := user_model.UpdateUserCols(ctx, u, "name"); err != nil { 58 + u.Name = oldUserName 59 + return err 60 + } 61 + return nil 62 + } 63 + 31 64 ctx, committer, err := db.TxContext(ctx) 32 65 if err != nil { 33 66 return err 34 67 } 35 68 defer committer.Close() 36 - if err := renameUser(ctx, u, newUserName); err != nil { 69 + 70 + isExist, err := user_model.IsUserExist(ctx, u.ID, newUserName) 71 + if err != nil { 72 + return err 73 + } 74 + if isExist { 75 + return user_model.ErrUserAlreadyExist{ 76 + Name: newUserName, 77 + } 78 + } 79 + 80 + if err = repo_model.UpdateRepositoryOwnerName(ctx, oldUserName, newUserName); err != nil { 81 + return err 82 + } 83 + 84 + if err = user_model.NewUserRedirect(ctx, u.ID, oldUserName, newUserName); err != nil { 85 + return err 86 + } 87 + 88 + if err := agit.UserNameChanged(ctx, u, newUserName); err != nil { 89 + return err 90 + } 91 + if err := container_service.UpdateRepositoryNames(ctx, u, newUserName); err != nil { 92 + return err 93 + } 94 + 95 + u.Name = newUserName 96 + u.LowerName = strings.ToLower(newUserName) 97 + if err := user_model.UpdateUserCols(ctx, u, "name", "lower_name"); err != nil { 98 + u.Name = oldUserName 99 + u.LowerName = strings.ToLower(oldUserName) 37 100 return err 38 101 } 39 - if err := committer.Commit(); err != nil { 102 + 103 + // Do not fail if directory does not exist 104 + if err = util.Rename(user_model.UserPath(oldUserName), user_model.UserPath(newUserName)); err != nil && !os.IsNotExist(err) { 105 + u.Name = oldUserName 106 + u.LowerName = strings.ToLower(oldUserName) 107 + return fmt.Errorf("rename user directory: %w", err) 108 + } 109 + 110 + if err = committer.Commit(); err != nil { 111 + u.Name = oldUserName 112 + u.LowerName = strings.ToLower(oldUserName) 113 + if err2 := util.Rename(user_model.UserPath(newUserName), user_model.UserPath(oldUserName)); err2 != nil && !os.IsNotExist(err2) { 114 + log.Critical("Unable to rollback directory change during failed username change from: %s to: %s. DB Error: %v. Filesystem Error: %v", oldUserName, newUserName, err, err2) 115 + return fmt.Errorf("failed to rollback directory change during failed username change from: %s to: %s. DB Error: %w. Filesystem Error: %v", oldUserName, newUserName, err, err2) 116 + } 40 117 return err 41 118 } 42 - return err 119 + return nil 43 120 } 44 121 45 122 // DeleteUser completely and permanently deletes everything of a user, ··· 240 317 241 318 return user_model.DeleteInactiveEmailAddresses(ctx) 242 319 } 243 - 244 - // UploadAvatar saves custom avatar for user. 245 - func UploadAvatar(u *user_model.User, data []byte) error { 246 - avatarData, err := avatar.ProcessAvatarImage(data) 247 - if err != nil { 248 - return err 249 - } 250 - 251 - ctx, committer, err := db.TxContext(db.DefaultContext) 252 - if err != nil { 253 - return err 254 - } 255 - defer committer.Close() 256 - 257 - u.UseCustomAvatar = true 258 - u.Avatar = avatar.HashAvatar(u.ID, data) 259 - if err = user_model.UpdateUserCols(ctx, u, "use_custom_avatar", "avatar"); err != nil { 260 - return fmt.Errorf("updateUser: %w", err) 261 - } 262 - 263 - if err := storage.SaveFrom(storage.Avatars, u.CustomAvatarRelativePath(), func(w io.Writer) error { 264 - _, err := w.Write(avatarData) 265 - return err 266 - }); err != nil { 267 - return fmt.Errorf("Failed to create dir %s: %w", u.CustomAvatarRelativePath(), err) 268 - } 269 - 270 - return committer.Commit() 271 - } 272 - 273 - // DeleteAvatar deletes the user's custom avatar. 274 - func DeleteAvatar(u *user_model.User) error { 275 - aPath := u.CustomAvatarRelativePath() 276 - log.Trace("DeleteAvatar[%d]: %s", u.ID, aPath) 277 - if len(u.Avatar) > 0 { 278 - if err := storage.Avatars.Delete(aPath); err != nil { 279 - return fmt.Errorf("Failed to remove %s: %w", aPath, err) 280 - } 281 - } 282 - 283 - u.UseCustomAvatar = false 284 - u.Avatar = "" 285 - if _, err := db.GetEngine(db.DefaultContext).ID(u.ID).Cols("avatar, use_custom_avatar").Update(u); err != nil { 286 - return fmt.Errorf("UpdateUser: %w", err) 287 - } 288 - return nil 289 - }
+41
tests/integration/api_admin_test.go
··· 241 241 ) 242 242 MakeRequest(t, req, http.StatusCreated) 243 243 } 244 + 245 + func TestAPIRenameUser(t *testing.T) { 246 + defer tests.PrepareTestEnv(t)() 247 + adminUsername := "user1" 248 + token := getUserToken(t, adminUsername, auth_model.AccessTokenScopeSudo) 249 + urlStr := fmt.Sprintf("/api/v1/admin/users/%s/rename?token=%s", "user2", token) 250 + req := NewRequestWithValues(t, "POST", urlStr, map[string]string{ 251 + // required 252 + "new_name": "User2", 253 + }) 254 + MakeRequest(t, req, http.StatusOK) 255 + 256 + urlStr = fmt.Sprintf("/api/v1/admin/users/%s/rename?token=%s", "User2", token) 257 + req = NewRequestWithValues(t, "POST", urlStr, map[string]string{ 258 + // required 259 + "new_name": "User2-2-2", 260 + }) 261 + MakeRequest(t, req, http.StatusOK) 262 + 263 + urlStr = fmt.Sprintf("/api/v1/admin/users/%s/rename?token=%s", "User2", token) 264 + req = NewRequestWithValues(t, "POST", urlStr, map[string]string{ 265 + // required 266 + "new_name": "user1", 267 + }) 268 + // the old user name still be used by with a redirect 269 + MakeRequest(t, req, http.StatusTemporaryRedirect) 270 + 271 + urlStr = fmt.Sprintf("/api/v1/admin/users/%s/rename?token=%s", "User2-2-2", token) 272 + req = NewRequestWithValues(t, "POST", urlStr, map[string]string{ 273 + // required 274 + "new_name": "user1", 275 + }) 276 + MakeRequest(t, req, http.StatusUnprocessableEntity) 277 + 278 + urlStr = fmt.Sprintf("/api/v1/admin/users/%s/rename?token=%s", "User2-2-2", token) 279 + req = NewRequestWithValues(t, "POST", urlStr, map[string]string{ 280 + // required 281 + "new_name": "user2", 282 + }) 283 + MakeRequest(t, req, http.StatusOK) 284 + }