Skip to content

Commit 07c9cbc

Browse files
committed
Merge api on Create or update file contents
1 parent 17f170e commit 07c9cbc

File tree

10 files changed

+577
-724
lines changed

10 files changed

+577
-724
lines changed

modules/structs/repo_file.go

Lines changed: 7 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -20,20 +20,6 @@ type FileOptions struct {
2020
Signoff bool `json:"signoff"`
2121
}
2222

23-
// CreateFileOptions options for creating files
24-
// 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)
25-
type CreateFileOptions struct {
26-
FileOptions
27-
// content must be base64 encoded
28-
// required: true
29-
ContentBase64 string `json:"content"`
30-
}
31-
32-
// Branch returns branch name
33-
func (o *CreateFileOptions) Branch() string {
34-
return o.FileOptions.BranchName
35-
}
36-
3723
// DeleteFileOptions options for deleting files (used for other File structs below)
3824
// 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)
3925
type DeleteFileOptions struct {
@@ -48,10 +34,13 @@ func (o *DeleteFileOptions) Branch() string {
4834
return o.FileOptions.BranchName
4935
}
5036

51-
// UpdateFileOptions options for updating files
37+
// CreateOrUpdateFileOptions options for creating or updating files
5238
// 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
39+
type CreateOrUpdateFileOptions struct {
40+
FileOptions
41+
// sha is the SHA for the file that already exists
42+
// required only for updating files
43+
SHA string `json:"sha"`
5544
// content must be base64 encoded
5645
// required: true
5746
ContentBase64 string `json:"content"`
@@ -60,7 +49,7 @@ type UpdateFileOptions struct {
6049
}
6150

6251
// Branch returns branch name
63-
func (o *UpdateFileOptions) Branch() string {
52+
func (o *CreateOrUpdateFileOptions) Branch() string {
6453
return o.FileOptions.BranchName
6554
}
6655

routers/api/v1/api.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1257,8 +1257,7 @@ func Routes() *web.Route {
12571257
m.Post("", reqToken(), bind(api.ChangeFilesOptions{}), reqRepoBranchWriter, mustNotBeArchived, repo.ChangeFiles)
12581258
m.Get("/*", repo.GetContents)
12591259
m.Group("/*", func() {
1260-
m.Post("", bind(api.CreateFileOptions{}), reqRepoBranchWriter, mustNotBeArchived, repo.CreateFile)
1261-
m.Put("", bind(api.UpdateFileOptions{}), reqRepoBranchWriter, mustNotBeArchived, repo.UpdateFile)
1260+
m.Put("", bind(api.CreateOrUpdateFileOptions{}), reqRepoBranchWriter, mustNotBeArchived, repo.CreateOrUpdateFile)
12621261
m.Delete("", bind(api.DeleteFileOptions{}), reqRepoBranchWriter, mustNotBeArchived, repo.DeleteFile)
12631262
}, reqToken())
12641263
}, reqRepoReader(unit.TypeCode))

routers/api/v1/repo/file.go

Lines changed: 34 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -513,11 +513,11 @@ func ChangeFiles(ctx *context.APIContext) {
513513
}
514514
}
515515

516-
// CreateFile handles API call for creating a file
517-
func CreateFile(ctx *context.APIContext) {
518-
// swagger:operation POST /repos/{owner}/{repo}/contents/{filepath} repository repoCreateFile
516+
// CreateOrUpdateFile handles API call for creating or updating a file
517+
func CreateOrUpdateFile(ctx *context.APIContext) {
518+
// swagger:operation PUT /repos/{owner}/{repo}/contents/{filepath} repository repoCreateOrUpdateFile
519519
// ---
520-
// summary: Create a file in a repository
520+
// summary: Create or update a file in a repository
521521
// consumes:
522522
// - application/json
523523
// produces:
@@ -535,115 +535,19 @@ func CreateFile(ctx *context.APIContext) {
535535
// required: true
536536
// - name: filepath
537537
// in: path
538-
// description: path of the file to create
538+
// description: path of the file to create or update
539539
// type: string
540540
// required: true
541541
// - name: body
542542
// in: body
543543
// required: true
544544
// schema:
545-
// "$ref": "#/definitions/CreateFileOptions"
546-
// responses:
547-
// "201":
548-
// "$ref": "#/responses/FileResponse"
549-
// "403":
550-
// "$ref": "#/responses/error"
551-
// "404":
552-
// "$ref": "#/responses/notFound"
553-
// "422":
554-
// "$ref": "#/responses/error"
555-
// "423":
556-
// "$ref": "#/responses/repoArchivedError"
557-
558-
apiOpts := web.GetForm(ctx).(*api.CreateFileOptions)
559-
560-
if apiOpts.BranchName == "" {
561-
apiOpts.BranchName = ctx.Repo.Repository.DefaultBranch
562-
}
563-
564-
contentReader, err := base64Reader(apiOpts.ContentBase64)
565-
if err != nil {
566-
ctx.Error(http.StatusUnprocessableEntity, "Invalid base64 content", err)
567-
return
568-
}
569-
570-
opts := &files_service.ChangeRepoFilesOptions{
571-
Files: []*files_service.ChangeRepoFile{
572-
{
573-
Operation: "create",
574-
TreePath: ctx.Params("*"),
575-
ContentReader: contentReader,
576-
},
577-
},
578-
Message: apiOpts.Message,
579-
OldBranch: apiOpts.BranchName,
580-
NewBranch: apiOpts.NewBranchName,
581-
Committer: &files_service.IdentityOptions{
582-
Name: apiOpts.Committer.Name,
583-
Email: apiOpts.Committer.Email,
584-
},
585-
Author: &files_service.IdentityOptions{
586-
Name: apiOpts.Author.Name,
587-
Email: apiOpts.Author.Email,
588-
},
589-
Dates: &files_service.CommitDateOptions{
590-
Author: apiOpts.Dates.Author,
591-
Committer: apiOpts.Dates.Committer,
592-
},
593-
Signoff: apiOpts.Signoff,
594-
}
595-
if opts.Dates.Author.IsZero() {
596-
opts.Dates.Author = time.Now()
597-
}
598-
if opts.Dates.Committer.IsZero() {
599-
opts.Dates.Committer = time.Now()
600-
}
601-
602-
if opts.Message == "" {
603-
opts.Message = changeFilesCommitMessage(ctx, opts.Files)
604-
}
605-
606-
if filesResponse, err := createOrUpdateFiles(ctx, opts); err != nil {
607-
handleCreateOrUpdateFileError(ctx, err)
608-
} else {
609-
fileResponse := files_service.GetFileResponseFromFilesResponse(filesResponse, 0)
610-
ctx.JSON(http.StatusCreated, fileResponse)
611-
}
612-
}
613-
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
617-
// ---
618-
// summary: Update a file in a repository
619-
// consumes:
620-
// - application/json
621-
// produces:
622-
// - application/json
623-
// parameters:
624-
// - name: owner
625-
// in: path
626-
// description: owner of the repo
627-
// type: string
628-
// required: true
629-
// - name: repo
630-
// in: path
631-
// description: name of the repo
632-
// type: string
633-
// required: true
634-
// - name: filepath
635-
// in: path
636-
// description: path of the file to update
637-
// type: string
638-
// required: true
639-
// - name: body
640-
// in: body
641-
// required: true
642-
// schema:
643-
// "$ref": "#/definitions/UpdateFileOptions"
545+
// "$ref": "#/definitions/CreateOrUpdateFileOptions"
644546
// responses:
645547
// "200":
646548
// "$ref": "#/responses/FileResponse"
549+
// "201":
550+
// "$ref": "#/responses/FileResponse"
647551
// "403":
648552
// "$ref": "#/responses/error"
649553
// "404":
@@ -652,7 +556,7 @@ func UpdateFile(ctx *context.APIContext) {
652556
// "$ref": "#/responses/error"
653557
// "423":
654558
// "$ref": "#/responses/repoArchivedError"
655-
apiOpts := web.GetForm(ctx).(*api.UpdateFileOptions)
559+
apiOpts := web.GetForm(ctx).(*api.CreateOrUpdateFileOptions)
656560
if ctx.Repo.Repository.IsEmpty {
657561
ctx.Error(http.StatusUnprocessableEntity, "RepoIsEmpty", fmt.Errorf("repo is empty"))
658562
}
@@ -667,16 +571,25 @@ func UpdateFile(ctx *context.APIContext) {
667571
return
668572
}
669573

574+
var changeRepoFile files_service.ChangeRepoFile
575+
if apiOpts.SHA == "" {
576+
changeRepoFile = files_service.ChangeRepoFile{
577+
Operation: "create",
578+
TreePath: ctx.Params("*"),
579+
ContentReader: contentReader,
580+
}
581+
} else {
582+
changeRepoFile = files_service.ChangeRepoFile{
583+
Operation: "update",
584+
ContentReader: contentReader,
585+
SHA: apiOpts.SHA,
586+
FromTreePath: apiOpts.FromPath,
587+
TreePath: ctx.Params("*"),
588+
}
589+
}
590+
670591
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-
},
592+
Files: []*files_service.ChangeRepoFile{&changeRepoFile},
680593
Message: apiOpts.Message,
681594
OldBranch: apiOpts.BranchName,
682595
NewBranch: apiOpts.NewBranchName,
@@ -709,7 +622,11 @@ func UpdateFile(ctx *context.APIContext) {
709622
handleCreateOrUpdateFileError(ctx, err)
710623
} else {
711624
fileResponse := files_service.GetFileResponseFromFilesResponse(filesResponse, 0)
712-
ctx.JSON(http.StatusOK, fileResponse)
625+
if apiOpts.SHA == "" {
626+
ctx.JSON(http.StatusOK, fileResponse)
627+
} else {
628+
ctx.JSON(http.StatusCreated, fileResponse)
629+
}
713630
}
714631
}
715632

@@ -728,10 +645,10 @@ func handleCreateOrUpdateFileError(ctx *context.APIContext, err error) {
728645
return
729646
}
730647

731-
ctx.Error(http.StatusInternalServerError, "UpdateFile", err)
648+
ctx.Error(http.StatusInternalServerError, "CreateOrUpdateFile", err)
732649
}
733650

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

routers/api/v1/swagger/options.go

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -120,10 +120,7 @@ type swaggerParameterBodies struct {
120120
ChangeFilesOptions api.ChangeFilesOptions
121121

122122
// in:body
123-
CreateFileOptions api.CreateFileOptions
124-
125-
// in:body
126-
UpdateFileOptions api.UpdateFileOptions
123+
CreateOrUpdateFileOptions api.CreateOrUpdateFileOptions
127124

128125
// in:body
129126
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)