Skip to content

Commit 75aa23a

Browse files
wxiaoguangdelvh
andauthored
Refactor "change file" API (#34855)
Follow up the "editor" refactor, use the same approach to simplify code, and fix some docs & comments --------- Signed-off-by: wxiaoguang <[email protected]> Co-authored-by: delvh <[email protected]>
1 parent 1839110 commit 75aa23a

File tree

10 files changed

+199
-357
lines changed

10 files changed

+199
-357
lines changed

modules/structs/repo_file.go

Lines changed: 26 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,23 @@ type FileOptions struct {
2222
Signoff bool `json:"signoff"`
2323
}
2424

25+
type FileOptionsWithSHA struct {
26+
FileOptions
27+
// the blob ID (SHA) for the file that already exists, it is required for changing existing files
28+
// required: true
29+
SHA string `json:"sha" binding:"Required"`
30+
}
31+
32+
func (f *FileOptions) GetFileOptions() *FileOptions {
33+
return f
34+
}
35+
36+
type FileOptionsInterface interface {
37+
GetFileOptions() *FileOptions
38+
}
39+
40+
var _ FileOptionsInterface = (*FileOptions)(nil)
41+
2542
// CreateFileOptions options for creating files
2643
// Note: `author` and `committer` are optional (if only one is given, it will be used for the other, otherwise the authenticated user will be used)
2744
type CreateFileOptions struct {
@@ -31,55 +48,38 @@ type CreateFileOptions struct {
3148
ContentBase64 string `json:"content"`
3249
}
3350

34-
// Branch returns branch name
35-
func (o *CreateFileOptions) Branch() string {
36-
return o.FileOptions.BranchName
37-
}
38-
3951
// DeleteFileOptions options for deleting files (used for other File structs below)
4052
// Note: `author` and `committer` are optional (if only one is given, it will be used for the other, otherwise the authenticated user will be used)
4153
type DeleteFileOptions struct {
42-
FileOptions
43-
// sha is the SHA for the file that already exists
44-
// required: true
45-
SHA string `json:"sha" binding:"Required"`
46-
}
47-
48-
// Branch returns branch name
49-
func (o *DeleteFileOptions) Branch() string {
50-
return o.FileOptions.BranchName
54+
FileOptionsWithSHA
5155
}
5256

5357
// UpdateFileOptions options for updating files
5458
// Note: `author` and `committer` are optional (if only one is given, it will be used for the other, otherwise the authenticated user will be used)
5559
type UpdateFileOptions struct {
56-
DeleteFileOptions
60+
FileOptionsWithSHA
5761
// content must be base64 encoded
5862
// required: true
5963
ContentBase64 string `json:"content"`
6064
// from_path (optional) is the path of the original file which will be moved/renamed to the path in the URL
6165
FromPath string `json:"from_path" binding:"MaxSize(500)"`
6266
}
6367

64-
// Branch returns branch name
65-
func (o *UpdateFileOptions) Branch() string {
66-
return o.FileOptions.BranchName
67-
}
68-
69-
// FIXME: ChangeFileOperation.SHA is NOT required for update or delete if last commit is provided in the options.
68+
// FIXME: there is no LastCommitID in FileOptions, actually it should be an alternative to the SHA in ChangeFileOperation
7069

7170
// ChangeFileOperation for creating, updating or deleting a file
7271
type ChangeFileOperation struct {
73-
// indicates what to do with the file
72+
// indicates what to do with the file: "create" for creating a new file, "update" for updating an existing file,
73+
// "upload" for creating or updating a file, "rename" for renaming a file, and "delete" for deleting an existing file.
7474
// required: true
75-
// enum: create,update,delete
75+
// enum: create,update,upload,rename,delete
7676
Operation string `json:"operation" binding:"Required"`
7777
// path to the existing or new file
7878
// required: true
7979
Path string `json:"path" binding:"Required;MaxSize(500)"`
80-
// new or updated file content, must be base64 encoded
80+
// new or updated file content, it must be base64 encoded
8181
ContentBase64 string `json:"content"`
82-
// sha is the SHA for the file that already exists, required for update or delete
82+
// the blob ID (SHA) for the file that already exists, required for changing existing files
8383
SHA string `json:"sha"`
8484
// old path of the file to move
8585
FromPath string `json:"from_path"`
@@ -94,20 +94,10 @@ type ChangeFilesOptions struct {
9494
Files []*ChangeFileOperation `json:"files" binding:"Required"`
9595
}
9696

97-
// Branch returns branch name
98-
func (o *ChangeFilesOptions) Branch() string {
99-
return o.FileOptions.BranchName
100-
}
101-
102-
// FileOptionInterface provides a unified interface for the different file options
103-
type FileOptionInterface interface {
104-
Branch() string
105-
}
106-
10797
// ApplyDiffPatchFileOptions options for applying a diff patch
10898
// Note: `author` and `committer` are optional (if only one is given, it will be used for the other, otherwise the authenticated user will be used)
10999
type ApplyDiffPatchFileOptions struct {
110-
DeleteFileOptions
100+
FileOptions
111101
// required: true
112102
Content string `json:"content"`
113103
}

routers/api/v1/api.go

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -455,15 +455,6 @@ func reqRepoWriter(unitTypes ...unit.Type) func(ctx *context.APIContext) {
455455
}
456456
}
457457

458-
// reqRepoBranchWriter user should have a permission to write to a branch, or be a site admin
459-
func reqRepoBranchWriter(ctx *context.APIContext) {
460-
options, ok := web.GetForm(ctx).(api.FileOptionInterface)
461-
if !ok || (!ctx.Repo.CanWriteToBranch(ctx, ctx.Doer, options.Branch()) && !ctx.IsUserSiteAdmin()) {
462-
ctx.APIError(http.StatusForbidden, "user should have a permission to write to this branch")
463-
return
464-
}
465-
}
466-
467458
// reqRepoReader user should have specific read permission or be a repo admin or a site admin
468459
func reqRepoReader(unitType unit.Type) func(ctx *context.APIContext) {
469460
return func(ctx *context.APIContext) {
@@ -744,9 +735,17 @@ func mustEnableWiki(ctx *context.APIContext) {
744735
}
745736
}
746737

738+
// FIXME: for consistency, maybe most mustNotBeArchived checks should be replaced with mustEnableEditor
747739
func mustNotBeArchived(ctx *context.APIContext) {
748740
if ctx.Repo.Repository.IsArchived {
749-
ctx.APIError(http.StatusLocked, fmt.Errorf("%s is archived", ctx.Repo.Repository.LogString()))
741+
ctx.APIError(http.StatusLocked, fmt.Errorf("%s is archived", ctx.Repo.Repository.FullName()))
742+
return
743+
}
744+
}
745+
746+
func mustEnableEditor(ctx *context.APIContext) {
747+
if !ctx.Repo.Repository.CanEnableEditor() {
748+
ctx.APIError(http.StatusLocked, fmt.Errorf("%s is not allowed to edit", ctx.Repo.Repository.FullName()))
750749
return
751750
}
752751
}
@@ -1424,24 +1423,27 @@ func Routes() *web.Router {
14241423
m.Get("/tags/{sha}", repo.GetAnnotatedTag)
14251424
m.Get("/notes/{sha}", repo.GetNote)
14261425
}, context.ReferencesGitRepo(true), reqRepoReader(unit.TypeCode))
1427-
m.Post("/diffpatch", reqRepoWriter(unit.TypeCode), reqToken(), bind(api.ApplyDiffPatchFileOptions{}), mustNotBeArchived, repo.ApplyDiffPatch)
14281426
m.Group("/contents", func() {
14291427
m.Get("", repo.GetContentsList)
14301428
m.Get("/*", repo.GetContents)
1431-
m.Post("", reqToken(), bind(api.ChangeFilesOptions{}), reqRepoBranchWriter, mustNotBeArchived, repo.ChangeFiles)
1432-
m.Group("/*", func() {
1433-
m.Post("", bind(api.CreateFileOptions{}), reqRepoBranchWriter, mustNotBeArchived, repo.CreateFile)
1434-
m.Put("", bind(api.UpdateFileOptions{}), reqRepoBranchWriter, mustNotBeArchived, repo.UpdateFile)
1435-
m.Delete("", bind(api.DeleteFileOptions{}), reqRepoBranchWriter, mustNotBeArchived, repo.DeleteFile)
1436-
}, reqToken())
1429+
m.Group("", func() {
1430+
// "change file" operations, need permission to write to the target branch provided by the form
1431+
m.Post("", bind(api.ChangeFilesOptions{}), repo.ReqChangeRepoFileOptionsAndCheck, repo.ChangeFiles)
1432+
m.Group("/*", func() {
1433+
m.Post("", bind(api.CreateFileOptions{}), repo.ReqChangeRepoFileOptionsAndCheck, repo.CreateFile)
1434+
m.Put("", bind(api.UpdateFileOptions{}), repo.ReqChangeRepoFileOptionsAndCheck, repo.UpdateFile)
1435+
m.Delete("", bind(api.DeleteFileOptions{}), repo.ReqChangeRepoFileOptionsAndCheck, repo.DeleteFile)
1436+
})
1437+
m.Post("/diffpatch", bind(api.ApplyDiffPatchFileOptions{}), repo.ReqChangeRepoFileOptionsAndCheck, repo.ApplyDiffPatch)
1438+
}, mustEnableEditor, reqToken())
14371439
}, reqRepoReader(unit.TypeCode), context.ReferencesGitRepo())
14381440
m.Group("/contents-ext", func() {
14391441
m.Get("", repo.GetContentsExt)
14401442
m.Get("/*", repo.GetContentsExt)
14411443
}, reqRepoReader(unit.TypeCode), context.ReferencesGitRepo())
14421444
m.Combo("/file-contents", reqRepoReader(unit.TypeCode), context.ReferencesGitRepo()).
14431445
Get(repo.GetFileContentsGet).
1444-
Post(bind(api.GetFilesOptions{}), repo.GetFileContentsPost) // POST method requires "write" permission, so we also support "GET" method above
1446+
Post(bind(api.GetFilesOptions{}), repo.GetFileContentsPost) // the POST method requires "write" permission, so we also support "GET" method above
14451447
m.Get("/signing-key.gpg", misc.SigningKeyGPG)
14461448
m.Get("/signing-key.pub", misc.SigningKeySSH)
14471449
m.Group("/topics", func() {

0 commit comments

Comments
 (0)