Skip to content

Commit 0e047e0

Browse files
committed
Merge api on Create or update file contents
1 parent f8c1efe commit 0e047e0

File tree

10 files changed

+643
-409
lines changed

10 files changed

+643
-409
lines changed

modules/structs/repo_file.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ type FileOptions struct {
2121
}
2222

2323
// CreateFileOptions options for creating files
24+
// Deprecated: Use CreateOrUpdateFileOptions instead
2425
// 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)
2526
type CreateFileOptions struct {
2627
FileOptions
@@ -48,10 +49,13 @@ func (o *DeleteFileOptions) Branch() string {
4849
return o.FileOptions.BranchName
4950
}
5051

51-
// UpdateFileOptions options for updating files
52+
// CreateOrUpdateFileOptions options for creating or updating files
5253
// 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)
53-
type UpdateFileOptions struct {
54-
DeleteFileOptions
54+
type CreateOrUpdateFileOptions struct {
55+
FileOptions
56+
// sha is the SHA for the file that already exists
57+
// required only for updating files
58+
SHA string `json:"sha"`
5559
// content must be base64 encoded
5660
// required: true
5761
ContentBase64 string `json:"content"`
@@ -60,7 +64,7 @@ type UpdateFileOptions struct {
6064
}
6165

6266
// Branch returns branch name
63-
func (o *UpdateFileOptions) Branch() string {
67+
func (o *CreateOrUpdateFileOptions) Branch() string {
6468
return o.FileOptions.BranchName
6569
}
6670

routers/api/v1/api.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1258,7 +1258,7 @@ func Routes() *web.Route {
12581258
m.Get("/*", repo.GetContents)
12591259
m.Group("/*", func() {
12601260
m.Post("", bind(api.CreateFileOptions{}), reqRepoBranchWriter, mustNotBeArchived, repo.CreateFile)
1261-
m.Put("", bind(api.UpdateFileOptions{}), reqRepoBranchWriter, mustNotBeArchived, repo.UpdateFile)
1261+
m.Put("", bind(api.CreateOrUpdateFileOptions{}), reqRepoBranchWriter, mustNotBeArchived, repo.CreateOrUpdateFile)
12621262
m.Delete("", bind(api.DeleteFileOptions{}), reqRepoBranchWriter, mustNotBeArchived, repo.DeleteFile)
12631263
}, reqToken())
12641264
}, reqRepoReader(unit.TypeCode))

routers/api/v1/repo/file.go

Lines changed: 48 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -514,6 +514,7 @@ func ChangeFiles(ctx *context.APIContext) {
514514
}
515515

516516
// CreateFile handles API call for creating a file
517+
// Deprecated: Use CreateOrUpdateFileOptions instead
517518
func CreateFile(ctx *context.APIContext) {
518519
// swagger:operation POST /repos/{owner}/{repo}/contents/{filepath} repository repoCreateFile
519520
// ---
@@ -611,11 +612,11 @@ func CreateFile(ctx *context.APIContext) {
611612
}
612613
}
613614

614-
// UpdateFile handles API call for updating a file
615-
func UpdateFile(ctx *context.APIContext) {
616-
// swagger:operation PUT /repos/{owner}/{repo}/contents/{filepath} repository repoUpdateFile
615+
// CreateOrUpdateFile handles API call for creating or updating a file
616+
func CreateOrUpdateFile(ctx *context.APIContext) {
617+
// swagger:operation PUT /repos/{owner}/{repo}/contents/{filepath} repository repoCreateOrUpdateFile
617618
// ---
618-
// summary: Update a file in a repository
619+
// summary: Create or update a file in a repository
619620
// consumes:
620621
// - application/json
621622
// produces:
@@ -633,17 +634,19 @@ func UpdateFile(ctx *context.APIContext) {
633634
// required: true
634635
// - name: filepath
635636
// in: path
636-
// description: path of the file to update
637+
// description: path of the file to create or update
637638
// type: string
638639
// required: true
639640
// - name: body
640641
// in: body
641642
// required: true
642643
// schema:
643-
// "$ref": "#/definitions/UpdateFileOptions"
644+
// "$ref": "#/definitions/CreateOrUpdateFileOptions"
644645
// responses:
645646
// "200":
646647
// "$ref": "#/responses/FileResponse"
648+
// "201":
649+
// "$ref": "#/responses/FileResponse"
647650
// "403":
648651
// "$ref": "#/responses/error"
649652
// "404":
@@ -652,10 +655,7 @@ func UpdateFile(ctx *context.APIContext) {
652655
// "$ref": "#/responses/error"
653656
// "423":
654657
// "$ref": "#/responses/repoArchivedError"
655-
apiOpts := web.GetForm(ctx).(*api.UpdateFileOptions)
656-
if ctx.Repo.Repository.IsEmpty {
657-
ctx.Error(http.StatusUnprocessableEntity, "RepoIsEmpty", fmt.Errorf("repo is empty"))
658-
}
658+
apiOpts := web.GetForm(ctx).(*api.CreateOrUpdateFileOptions)
659659

660660
if apiOpts.BranchName == "" {
661661
apiOpts.BranchName = ctx.Repo.Repository.DefaultBranch
@@ -667,16 +667,38 @@ func UpdateFile(ctx *context.APIContext) {
667667
return
668668
}
669669

670+
treePath := ctx.Params("*")
671+
672+
_, err = files_service.GetContents(ctx, ctx.Repo.Repository, treePath, ctx.Repo.BranchName, true)
673+
if err != nil && !git.IsErrNotExist(err) {
674+
ctx.Error(http.StatusUnprocessableEntity, "Invalid base64 content", err)
675+
return
676+
}
677+
678+
var changeRepoFile files_service.ChangeRepoFile
679+
if apiOpts.SHA == "" {
680+
changeRepoFile = files_service.ChangeRepoFile{
681+
Operation: "create",
682+
TreePath: ctx.Params("*"),
683+
ContentReader: contentReader,
684+
}
685+
} else {
686+
if ctx.Repo.Repository.IsEmpty {
687+
ctx.Error(http.StatusUnprocessableEntity, "RepoIsEmpty", fmt.Errorf("repo is empty"))
688+
return
689+
}
690+
691+
changeRepoFile = files_service.ChangeRepoFile{
692+
Operation: "update",
693+
TreePath: ctx.Params("*"),
694+
ContentReader: contentReader,
695+
SHA: apiOpts.SHA,
696+
FromTreePath: apiOpts.FromPath,
697+
}
698+
}
699+
670700
opts := &files_service.ChangeRepoFilesOptions{
671-
Files: []*files_service.ChangeRepoFile{
672-
{
673-
Operation: "update",
674-
ContentReader: contentReader,
675-
SHA: apiOpts.SHA,
676-
FromTreePath: apiOpts.FromPath,
677-
TreePath: ctx.Params("*"),
678-
},
679-
},
701+
Files: []*files_service.ChangeRepoFile{&changeRepoFile},
680702
Message: apiOpts.Message,
681703
OldBranch: apiOpts.BranchName,
682704
NewBranch: apiOpts.NewBranchName,
@@ -709,7 +731,11 @@ func UpdateFile(ctx *context.APIContext) {
709731
handleCreateOrUpdateFileError(ctx, err)
710732
} else {
711733
fileResponse := files_service.GetFileResponseFromFilesResponse(filesResponse, 0)
712-
ctx.JSON(http.StatusOK, fileResponse)
734+
if apiOpts.SHA == "" {
735+
ctx.JSON(http.StatusCreated, fileResponse)
736+
} else {
737+
ctx.JSON(http.StatusOK, fileResponse)
738+
}
713739
}
714740
}
715741

@@ -728,10 +754,10 @@ func handleCreateOrUpdateFileError(ctx *context.APIContext, err error) {
728754
return
729755
}
730756

731-
ctx.Error(http.StatusInternalServerError, "UpdateFile", err)
757+
ctx.Error(http.StatusInternalServerError, "CreateOrUpdateFile", err)
732758
}
733759

734-
// Called from both CreateFile or UpdateFile to handle both
760+
// Called from both CreateFile or CreateOrUpdateFile to handle both
735761
func createOrUpdateFiles(ctx *context.APIContext, opts *files_service.ChangeRepoFilesOptions) (*api.FilesResponse, error) {
736762
if !canWriteFiles(ctx, opts.OldBranch) {
737763
return nil, repo_model.ErrUserDoesNotHaveAccessToRepo{

routers/api/v1/swagger/options.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ type swaggerParameterBodies struct {
123123
CreateFileOptions api.CreateFileOptions
124124

125125
// in:body
126-
UpdateFileOptions api.UpdateFileOptions
126+
CreateOrUpdateFileOptions api.CreateOrUpdateFileOptions
127127

128128
// in:body
129129
DeleteFileOptions api.DeleteFileOptions

tests/integration/api_helper_for_declarative_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -353,9 +353,9 @@ func doAPIGetBranch(ctx APITestContext, branch string, callback ...func(*testing
353353
}
354354
}
355355

356-
func doAPICreateFile(ctx APITestContext, treepath string, options *api.CreateFileOptions, callback ...func(*testing.T, api.FileResponse)) func(*testing.T) {
356+
func doAPICreateFile(ctx APITestContext, treepath string, options *api.CreateOrUpdateFileOptions, callback ...func(*testing.T, api.FileResponse)) func(*testing.T) {
357357
return func(t *testing.T) {
358-
req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s", ctx.Username, ctx.Reponame, treepath), &options).
358+
req := NewRequestWithJSON(t, "PUT", fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s", ctx.Username, ctx.Reponame, treepath), &options).
359359
AddTokenAuth(ctx.Token)
360360
if ctx.ExpectedCode != 0 {
361361
ctx.Session.MakeRequest(t, req, ctx.ExpectedCode)

0 commit comments

Comments
 (0)