Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes 4762 - Content API for Creating, Updating, Deleting Files #6314

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
130 commits
Select commit Hold shift + click to select a range
44d1980
Updates to API for Create/Update/Delete file
richmahn Feb 13, 2019
bac3579
Merge remote-tracking branch 'upstream/master' into feature-4762-api-…
richmahn Feb 13, 2019
57fbd89
Converting to use a model for file creation/update
richmahn Feb 13, 2019
c3c3c0b
Merge remote-tracking branch 'upstream/master' into feature-4762-api-…
richmahn Feb 13, 2019
7985b62
More work on repo file model
richmahn Feb 14, 2019
8455e22
Updates to api
richmahn Feb 15, 2019
41e7d8c
Merge remote-tracking branch 'upstream/master' into feature-4762-api-…
richmahn Feb 15, 2019
6884c4c
Starting to merge with uplaoder
richmahn Feb 16, 2019
71df9e2
Updates to API
richmahn Feb 22, 2019
5f6ad98
Merge remote-tracking branch 'upstream/master' into feature-4762-api-…
richmahn Feb 22, 2019
6b09e19
more work on API
richmahn Feb 23, 2019
7f64043
Merge remote-tracking branch 'upstream/master' into feature-4762-api-…
richmahn Feb 23, 2019
48dc6ef
Adds more API routes for contents
richmahn Feb 27, 2019
4d3a93f
Merge remote-tracking branch 'upstream/master' into feature-4762-api-…
richmahn Feb 27, 2019
2b4a63a
Implemented Delete api
richmahn Feb 27, 2019
7e9252f
Merge remote-tracking branch 'upstream/master' into feature-4762-api-…
richmahn Feb 27, 2019
8ad8188
fmt
richmahn Feb 27, 2019
adb5d74
Fixes create
richmahn Feb 28, 2019
28d6f2a
Fixes file contents
richmahn Feb 28, 2019
c30c550
Fixes file contents
richmahn Feb 28, 2019
9664e48
Fixes checking new files
richmahn Feb 28, 2019
b6da595
Update to delete
richmahn Mar 5, 2019
e18c9b1
Merge remote-tracking branch 'upstream/master' into feature-4762-api-…
richmahn Mar 5, 2019
09b3cca
Adds tests
richmahn Mar 6, 2019
62fd9f8
Merge remote-tracking branch 'upstream/master' into feature-4762-api-…
richmahn Mar 6, 2019
b2e361f
Fixes for author and committer
richmahn Mar 7, 2019
61a8326
Added more tests
richmahn Mar 8, 2019
9e62012
Merge remote-tracking branch 'upstream/master' into feature-4762-api-…
richmahn Mar 8, 2019
538777f
Fixes content tests
richmahn Mar 9, 2019
ba4d43c
Fixes content tests
richmahn Mar 9, 2019
545ad5f
more tests
richmahn Mar 9, 2019
e68e129
Revert sdk
richmahn Mar 9, 2019
5dc52de
Merge remote-tracking branch 'upstream/master' into feature-4762-api-…
richmahn Mar 11, 2019
43ae473
Tests
richmahn Mar 12, 2019
360219c
More tests, formatting
richmahn Mar 12, 2019
be14994
Merge remote-tracking branch 'upstream/master' into feature-4762-api-…
richmahn Mar 12, 2019
570bfc9
Gitea copyright in error.go
richmahn Mar 12, 2019
3d85aa3
Better descriptions of the body for the API calls
richmahn Mar 12, 2019
ffcce77
Better descriptions of the body for the API calls
richmahn Mar 12, 2019
eda770b
Update to swagger
richmahn Mar 12, 2019
be0b4a2
Update to swagger
richmahn Mar 12, 2019
c2ce796
Update to swagger
richmahn Mar 12, 2019
606c130
Update to swagger
richmahn Mar 12, 2019
4a68b21
Update to swagger
richmahn Mar 12, 2019
4f5e758
Uses the default branch rather than master
richmahn Mar 13, 2019
50982fa
Merge remote-tracking branch 'upstream/master' into feature-4762-api-…
richmahn Mar 13, 2019
74a69cb
Updates per review
richmahn Mar 13, 2019
1b9173c
Moves all logic for Create/Update/Delete to module, only input/output…
richmahn Mar 14, 2019
1598f37
Code cleanup
richmahn Mar 14, 2019
19770b5
Code cleanup
richmahn Mar 14, 2019
f7203f4
Fix to DeleteFilePost
richmahn Mar 14, 2019
ce7dd1f
Merge remote-tracking branch 'upstream/master' into feature-4762-api-…
richmahn Mar 14, 2019
f134bf0
Fix to DeleteFilePost
richmahn Mar 14, 2019
81e7485
Fix to DeleteFilePost
richmahn Mar 14, 2019
84438e6
Code cleanup
richmahn Mar 14, 2019
88812e0
Code cleanup
richmahn Mar 14, 2019
3990783
Merge remote-tracking branch 'upstream/master' into feature-4762-api-…
richmahn Mar 15, 2019
fcb6ad1
Updates to swagger
richmahn Mar 15, 2019
3da694b
Uses new Identity in the SDK
richmahn Mar 15, 2019
0d04a6d
Removes unneeded code
richmahn Mar 15, 2019
6f564c3
Properly makes GET for file contents open for public repos
richmahn Mar 15, 2019
3708a21
Revert sdk
richmahn Mar 15, 2019
b76e806
Adds some integration tests for trees and blobs
richmahn Mar 16, 2019
2f4a603
Adds some integration tests for trees and blobs
richmahn Mar 16, 2019
68d25c9
Updates to fix vet and lint, adds API test
richmahn Mar 19, 2019
047f850
Fixes to errors and to match SDK
richmahn Mar 20, 2019
03e73dd
Integration test for updating a file
richmahn Mar 20, 2019
da27d0a
Adds more integration tests
richmahn Mar 22, 2019
d6960a2
Merge remote-tracking branch 'upstream/master' into feature-4762-api-…
richmahn Mar 22, 2019
0802d41
Merge remote-tracking branch 'upstream/master' into feature-4762-api-…
richmahn Mar 22, 2019
12439c3
Updates unit tests
richmahn Mar 22, 2019
73528b8
Fixes import order and swagger
richmahn Mar 22, 2019
d34d1bc
Fixes imports and swagger
richmahn Mar 22, 2019
6be1fbc
Fix to integration tests
richmahn Mar 22, 2019
a31a484
Set GITEA_ROOT for unit tests since used for git push now
richmahn Mar 23, 2019
125edc2
Adds GITEA_ROOT to make unit-test-coverage command
richmahn Mar 24, 2019
419ee2f
Make it so ports are variables based on different DBs
richmahn Mar 24, 2019
5a5ecf4
Make it so ports are variables based on different DBs
richmahn Mar 24, 2019
c47d219
Simplification of port in integration test
richmahn Mar 24, 2019
2ece2dc
Merge remote-tracking branch 'upstream/master' into feature-4762-api-…
richmahn Mar 24, 2019
d45ffd2
Adds interation tests for branch and file rename
richmahn Mar 25, 2019
224f6f4
Adds interation tests for branch and file rename
richmahn Mar 25, 2019
b875e38
Fixes ports in integration test
richmahn Mar 26, 2019
6242d49
Merge remote-tracking branch 'upstream/master' into feature-4762-api-…
richmahn Mar 26, 2019
13a7778
Merge remote-tracking branch 'upstream/master' into feature-4762-api-…
richmahn Mar 28, 2019
8b27d64
Fix of Makefile conflicts
richmahn Mar 28, 2019
b464f95
Package import fix
richmahn Mar 28, 2019
37a598a
Package import fix
richmahn Mar 28, 2019
6d23921
Merge remote-tracking branch 'upstream/master' into feature-4762-api-…
richmahn Mar 29, 2019
4946aeb
Updates swagger
richmahn Mar 29, 2019
14a7788
Merge remote-tracking branch 'upstream/master' into feature-4762-api-…
richmahn Mar 29, 2019
c67ba46
Merge remote-tracking branch 'upstream/master' into feature-4762-api-…
richmahn Mar 30, 2019
0986857
Makes it so GITEA_ROOT isn't needed, splits up integration tests for …
richmahn Apr 1, 2019
dfae8cc
Use setting.AppURL instead of setting.HTTPPort
richmahn Apr 1, 2019
5b3e00d
Reverts Makefile
richmahn Apr 1, 2019
8c68adc
Fix for GITEA_ROOT in migration_test.go
richmahn Apr 1, 2019
4b13ba9
Marshals the Blob content as a base64 string instead of when making s…
richmahn Apr 1, 2019
9a788ca
Now Blobs api uses setting for max size of blob
richmahn Apr 1, 2019
c5af818
Merge remote-tracking branch 'upstream/master' into feature-4762-api-…
richmahn Apr 1, 2019
3af88ee
Updates swagger
richmahn Apr 1, 2019
bb9d5d0
Merge remote-tracking branch 'upstream/master' into feature-4762-api-…
richmahn Apr 2, 2019
4c50b48
Uses Temp Repo with pointer to a Git Repo
richmahn Apr 4, 2019
2fe1e17
Merge remote-tracking branch 'upstream/master' into feature-4762-api-…
richmahn Apr 4, 2019
aed8ba5
Reverts back to not Marshelling GitBlobReponse content
richmahn Apr 9, 2019
536d64e
Adds break to looking for file to delete
richmahn Apr 9, 2019
367d258
Merge remote-tracking branch 'upstream/master' into feature-4762-api-…
richmahn Apr 9, 2019
fad126e
Fixes lint errors
richmahn Apr 9, 2019
aac0ed6
fixes formatting
richmahn Apr 9, 2019
414e6a5
Fixes file changed since last commit
richmahn Apr 11, 2019
dc61e98
Merge remote-tracking branch 'upstream/master' into feature-4762-api-…
richmahn Apr 11, 2019
b4a1030
Remove debugging code
richmahn Apr 11, 2019
f83298a
Fixes misspelling
richmahn Apr 11, 2019
11adde2
Better way to get the GITEA_ROOT if not set
richmahn Apr 11, 2019
11331cf
Remove nil check
richmahn Apr 11, 2019
a9b5eb2
Update tests for nil
richmahn Apr 11, 2019
7517533
Check size of base64 encoded string not raw string
richmahn Apr 11, 2019
11397df
Merge remote-tracking branch 'upstream/master' into feature-4762-api-…
richmahn Apr 12, 2019
cc75c42
makes the default blob size for API 10MiB
richmahn Apr 12, 2019
fcc145b
More changes to Blob API max file size
richmahn Apr 12, 2019
44983ca
Merge remote-tracking branch 'upstream/master' into feature-4762-api-…
richmahn Apr 12, 2019
7270bc0
Makes sub-tests
richmahn Apr 12, 2019
5fe2e52
Fixes spacing
richmahn Apr 12, 2019
6cd4e15
Merge branch 'master' into feature-4762-api-content-create-update-delete
techknowlogick Apr 14, 2019
e19be6a
Merge branch 'master' into feature-4762-api-content-create-update-delete
techknowlogick Apr 15, 2019
604b555
Merge remote-tracking branch 'upstream/master' into feature-4762-api-…
richmahn Apr 15, 2019
a4e7068
Fixes per PR review
richmahn Apr 16, 2019
77422a7
Fixes per PR review
richmahn Apr 16, 2019
ae3ab6f
Imports order
richmahn Apr 17, 2019
4b99f31
Merge remote-tracking branch 'upstream/master' into feature-4762-api-…
richmahn Apr 17, 2019
2d9fe96
Merge branch 'master' into feature-4762-api-content-create-update-delete
techknowlogick Apr 17, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions custom/conf/app.ini.sample
Original file line number Diff line number Diff line change
Expand Up @@ -672,6 +672,8 @@ MAX_RESPONSE_ITEMS = 50
DEFAULT_PAGING_NUM = 30
; Default and maximum number of items per page for git trees api
DEFAULT_GIT_TREES_PER_PAGE = 1000
; Default size of a blob returned by the blobs API (default is 10MiB)
DEFAULT_MAX_BLOB_SIZE = 10485760

[oauth2]
; Enables OAuth2 provider
Expand Down
5 changes: 3 additions & 2 deletions docs/content/doc/advanced/config-cheat-sheet.en-us.md
Original file line number Diff line number Diff line change
Expand Up @@ -402,8 +402,9 @@ NB: You must `REDIRECT_MACARON_LOG` and have `DISABLE_ROUTER_LOG` set to `false`

- `ENABLE_SWAGGER`: **true**: Enables /api/swagger, /api/v1/swagger etc. endpoints. True or false; default is true.
- `MAX_RESPONSE_ITEMS`: **50**: Max number of items in a page.
- `DEFAULT_PAGING_NUM`: **30**: Default paging number of api.
- `DEFAULT_GIT_TREES_PER_PAGE`: **1000**: Default and maximum number of items per page for git trees api.
- `DEFAULT_PAGING_NUM`: **30**: Default paging number of API.
- `DEFAULT_GIT_TREES_PER_PAGE`: **1000**: Default and maximum number of items per page for git trees API.
- `DEFAULT_MAX_BLOB_SIZE`: **10485760**: Default max size of a blob that can be return by the blobs API.

## OAuth2 (`oauth2`)

Expand Down
3 changes: 2 additions & 1 deletion docs/content/doc/advanced/config-cheat-sheet.zh-cn.md
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,8 @@ menu:
- `ENABLE_SWAGGER`: **true**: 是否启用swagger路由 /api/swagger, /api/v1/swagger etc. endpoints. True 或 false; 默认是 true.
- `MAX_RESPONSE_ITEMS`: **50**: 一个页面最大的项目数。
- `DEFAULT_PAGING_NUM`: **30**: API中默认分页条数。
- `DEFAULT_GIT_TREES_PER_PAGE`: **1000**: GIT TREES API每页的默认和最大项数.
- `DEFAULT_GIT_TREES_PER_PAGE`: **1000**: GIT TREES API每页的默认最大项数.
- `DEFAULT_MAX_BLOB_SIZE`: **10485760**: BLOBS API默认最大大小.

## Markup (`markup`)

Expand Down
114 changes: 114 additions & 0 deletions integrations/api_repo_file_content_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package integrations

import (
"net/http"
"path/filepath"
"testing"

"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/sdk/gitea"

"github.com/stretchr/testify/assert"
)

func getExpectedFileContentResponseForFileContents(branch string) *api.FileContentResponse {
treePath := "README.md"
sha := "4b4851ad51df6a7d9f25c979345979eaeb5b349f"
return &api.FileContentResponse{
Name: filepath.Base(treePath),
Path: treePath,
SHA: sha,
Size: 30,
URL: setting.AppURL + "api/v1/repos/user2/repo1/contents/" + treePath,
HTMLURL: setting.AppURL + "user2/repo1/blob/" + branch + "/" + treePath,
GitURL: setting.AppURL + "api/v1/repos/user2/repo1/git/blobs/" + sha,
DownloadURL: setting.AppURL + "user2/repo1/raw/branch/" + branch + "/" + treePath,
Type: "blob",
Links: &api.FileLinksResponse{
Self: setting.AppURL + "api/v1/repos/user2/repo1/contents/" + treePath,
GitURL: setting.AppURL + "api/v1/repos/user2/repo1/git/blobs/" + sha,
HTMLURL: setting.AppURL + "user2/repo1/blob/" + branch + "/" + treePath,
},
}
}

func TestAPIGetFileContents(t *testing.T) {
prepareTestEnv(t)
user2 := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User) // owner of the repo1 & repo16
user3 := models.AssertExistsAndLoadBean(t, &models.User{ID: 3}).(*models.User) // owner of the repo3, is an org
user4 := models.AssertExistsAndLoadBean(t, &models.User{ID: 4}).(*models.User) // owner of neither repos
repo1 := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository) // public repo
repo3 := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 3}).(*models.Repository) // public repo
repo16 := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 16}).(*models.Repository) // private repo
treePath := "README.md"

// Get user2's token
session := loginUser(t, user2.Name)
token2 := getTokenForLoggedInUser(t, session)
session = emptyTestSession(t)
// Get user4's token
session = loginUser(t, user4.Name)
token4 := getTokenForLoggedInUser(t, session)
session = emptyTestSession(t)

// Make a second master branch in repo1
repo1.CreateNewBranch(user2, repo1.DefaultBranch, "master2")

// ref is default branch
branch := repo1.DefaultBranch
req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/%s?ref=%s", user2.Name, repo1.Name, treePath, branch)
resp := session.MakeRequest(t, req, http.StatusOK)
var fileContentResponse api.FileContentResponse
DecodeJSON(t, resp, &fileContentResponse)
assert.NotNil(t, fileContentResponse)
expectedFileContentResponse := getExpectedFileContentResponseForFileContents(branch)
assert.EqualValues(t, *expectedFileContentResponse, fileContentResponse)

// No ref
req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/%s", user2.Name, repo1.Name, treePath)
resp = session.MakeRequest(t, req, http.StatusOK)
DecodeJSON(t, resp, &fileContentResponse)
assert.NotNil(t, fileContentResponse)
expectedFileContentResponse = getExpectedFileContentResponseForFileContents(repo1.DefaultBranch)
assert.EqualValues(t, *expectedFileContentResponse, fileContentResponse)

// ref is master2
branch = "master2"
req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/%s?ref=%s", user2.Name, repo1.Name, treePath, branch)
resp = session.MakeRequest(t, req, http.StatusOK)
DecodeJSON(t, resp, &fileContentResponse)
assert.NotNil(t, fileContentResponse)
expectedFileContentResponse = getExpectedFileContentResponseForFileContents("master2")
assert.EqualValues(t, *expectedFileContentResponse, fileContentResponse)

// Test file contents a file with the wrong branch
branch = "badbranch"
req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/%s?ref=%s", user2.Name, repo1.Name, treePath, branch)
resp = session.MakeRequest(t, req, http.StatusInternalServerError)
expectedAPIError := context.APIError{
Message: "object does not exist [id: " + branch + ", rel_path: ]",
URL: base.DocURL,
}
var apiError context.APIError
DecodeJSON(t, resp, &apiError)
assert.Equal(t, expectedAPIError, apiError)

// Test accessing private branch with user token that does not have access - should fail
req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo16.Name, treePath, token4)
session.MakeRequest(t, req, http.StatusNotFound)

// Test access private branch of owner of token
req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/readme.md?token=%s", user2.Name, repo16.Name, token2)
session.MakeRequest(t, req, http.StatusOK)

// Test access of org user3 private repo file by owner user2
req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/%s?token=%s", user3.Name, repo3.Name, treePath, token2)
session.MakeRequest(t, req, http.StatusOK)
}
215 changes: 215 additions & 0 deletions integrations/api_repo_file_create_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package integrations

import (
"encoding/base64"
"fmt"
"net/http"
"path/filepath"
"testing"

"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/sdk/gitea"

"github.com/stretchr/testify/assert"
)

func getCreateFileOptions() api.CreateFileOptions {
content := "This is new text"
contentEncoded := base64.StdEncoding.EncodeToString([]byte(content))
return api.CreateFileOptions{
FileOptions: api.FileOptions{
BranchName: "master",
NewBranchName: "master",
Message: "Creates new/file.txt",
Author: api.Identity{
Name: "John Doe",
Email: "johndoe@example.com",
},
Committer: api.Identity{
Name: "Jane Doe",
Email: "janedoe@example.com",
},
},
Content: contentEncoded,
}
}

func getExpectedFileResponseForCreate(commitID, treePath string) *api.FileResponse {
sha := "a635aa942442ddfdba07468cf9661c08fbdf0ebf"
return &api.FileResponse{
Content: &api.FileContentResponse{
Name: filepath.Base(treePath),
Path: treePath,
SHA: sha,
Size: 16,
URL: setting.AppURL + "api/v1/repos/user2/repo1/contents/" + treePath,
HTMLURL: setting.AppURL + "user2/repo1/blob/master/" + treePath,
GitURL: setting.AppURL + "api/v1/repos/user2/repo1/git/blobs/" + sha,
DownloadURL: setting.AppURL + "user2/repo1/raw/branch/master/" + treePath,
Type: "blob",
Links: &api.FileLinksResponse{
Self: setting.AppURL + "api/v1/repos/user2/repo1/contents/" + treePath,
GitURL: setting.AppURL + "api/v1/repos/user2/repo1/git/blobs/" + sha,
HTMLURL: setting.AppURL + "user2/repo1/blob/master/" + treePath,
},
},
Commit: &api.FileCommitResponse{
CommitMeta: api.CommitMeta{
URL: setting.AppURL + "api/v1/repos/user2/repo1/git/commits/" + commitID,
SHA: commitID,
},
HTMLURL: setting.AppURL + "user2/repo1/commit/" + commitID,
Author: &api.CommitUser{
Identity: api.Identity{
Name: "Jane Doe",
Email: "janedoe@example.com",
},
},
Committer: &api.CommitUser{
Identity: api.Identity{
Name: "John Doe",
Email: "johndoe@example.com",
},
},
Message: "Updates README.md\n",
},
Verification: &api.PayloadCommitVerification{
Verified: false,
Reason: "unsigned",
Signature: "",
Payload: "",
},
}
}

func TestAPICreateFile(t *testing.T) {
prepareTestEnv(t)
user2 := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User) // owner of the repo1 & repo16
user3 := models.AssertExistsAndLoadBean(t, &models.User{ID: 3}).(*models.User) // owner of the repo3, is an org
user4 := models.AssertExistsAndLoadBean(t, &models.User{ID: 4}).(*models.User) // owner of neither repos
repo1 := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository) // public repo
repo3 := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 3}).(*models.Repository) // public repo
repo16 := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 16}).(*models.Repository) // private repo
fileID := 0

// Get user2's token
session := loginUser(t, user2.Name)
token2 := getTokenForLoggedInUser(t, session)
session = emptyTestSession(t)
// Get user4's token
session = loginUser(t, user4.Name)
token4 := getTokenForLoggedInUser(t, session)
session = emptyTestSession(t)

// Test creating a file in repo1 which user2 owns, try both with branch and empty branch
for _, branch := range [...]string{
"master", // Branch
"", // Empty branch
} {
createFileOptions := getCreateFileOptions()
createFileOptions.BranchName = branch
fileID++
treePath := fmt.Sprintf("new/file%d.txt", fileID)
url := fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo1.Name, treePath, token2)
req := NewRequestWithJSON(t, "POST", url, &createFileOptions)
resp := session.MakeRequest(t, req, http.StatusCreated)
gitRepo, _ := git.OpenRepository(repo1.RepoPath())
commitID, _ := gitRepo.GetBranchCommitID(createFileOptions.NewBranchName)
expectedFileResponse := getExpectedFileResponseForCreate(commitID, treePath)
var fileResponse api.FileResponse
DecodeJSON(t, resp, &fileResponse)
assert.EqualValues(t, expectedFileResponse.Content, fileResponse.Content)
assert.EqualValues(t, expectedFileResponse.Commit.SHA, fileResponse.Commit.SHA)
assert.EqualValues(t, expectedFileResponse.Commit.HTMLURL, fileResponse.Commit.HTMLURL)
assert.EqualValues(t, expectedFileResponse.Commit.Author.Email, fileResponse.Commit.Author.Email)
assert.EqualValues(t, expectedFileResponse.Commit.Author.Name, fileResponse.Commit.Author.Name)
}

// Test creating a file in a new branch
createFileOptions := getCreateFileOptions()
createFileOptions.BranchName = repo1.DefaultBranch
createFileOptions.NewBranchName = "new_branch"
fileID++
treePath := fmt.Sprintf("new/file%d.txt", fileID)
url := fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo1.Name, treePath, token2)
req := NewRequestWithJSON(t, "POST", url, &createFileOptions)
resp := session.MakeRequest(t, req, http.StatusCreated)
var fileResponse api.FileResponse
DecodeJSON(t, resp, &fileResponse)
expectedSHA := "a635aa942442ddfdba07468cf9661c08fbdf0ebf"
expectedHTMLURL := fmt.Sprintf("http://localhost:"+setting.HTTPPort+"/user2/repo1/blob/new_branch/new/file%d.txt", fileID)
expectedDownloadURL := fmt.Sprintf("http://localhost:"+setting.HTTPPort+"/user2/repo1/raw/branch/new_branch/new/file%d.txt", fileID)
assert.EqualValues(t, expectedSHA, fileResponse.Content.SHA)
assert.EqualValues(t, expectedHTMLURL, fileResponse.Content.HTMLURL)
assert.EqualValues(t, expectedDownloadURL, fileResponse.Content.DownloadURL)

// Test trying to create a file that already exists, should fail
createFileOptions = getCreateFileOptions()
treePath = "README.md"
url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo1.Name, treePath, token2)
req = NewRequestWithJSON(t, "POST", url, &createFileOptions)
resp = session.MakeRequest(t, req, http.StatusInternalServerError)
expectedAPIError := context.APIError{
Message: "repository file already exists [path: " + treePath + "]",
URL: base.DocURL,
}
var apiError context.APIError
DecodeJSON(t, resp, &apiError)
assert.Equal(t, expectedAPIError, apiError)

// Test creating a file in repo1 by user4 who does not have write access
createFileOptions = getCreateFileOptions()
fileID++
treePath = fmt.Sprintf("new/file%d.txt", fileID)
url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo16.Name, treePath, token4)
req = NewRequestWithJSON(t, "POST", url, &createFileOptions)
session.MakeRequest(t, req, http.StatusNotFound)

// Tests a repo with no token given so will fail
createFileOptions = getCreateFileOptions()
fileID++
treePath = fmt.Sprintf("new/file%d.txt", fileID)
url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s", user2.Name, repo16.Name, treePath)
req = NewRequestWithJSON(t, "POST", url, &createFileOptions)
session.MakeRequest(t, req, http.StatusNotFound)

// Test using access token for a private repo that the user of the token owns
createFileOptions = getCreateFileOptions()
fileID++
treePath = fmt.Sprintf("new/file%d.txt", fileID)
url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo16.Name, treePath, token2)
req = NewRequestWithJSON(t, "POST", url, &createFileOptions)
session.MakeRequest(t, req, http.StatusCreated)

// Test using org repo "user3/repo3" where user2 is a collaborator
createFileOptions = getCreateFileOptions()
fileID++
treePath = fmt.Sprintf("new/file%d.txt", fileID)
url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user3.Name, repo3.Name, treePath, token2)
req = NewRequestWithJSON(t, "POST", url, &createFileOptions)
session.MakeRequest(t, req, http.StatusCreated)

// Test using org repo "user3/repo3" with no user token
createFileOptions = getCreateFileOptions()
fileID++
treePath = fmt.Sprintf("new/file%d.txt", fileID)
url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s", user3.Name, repo3.Name, treePath)
req = NewRequestWithJSON(t, "POST", url, &createFileOptions)
session.MakeRequest(t, req, http.StatusNotFound)

// Test using repo "user2/repo1" where user4 is a NOT collaborator
createFileOptions = getCreateFileOptions()
fileID++
treePath = fmt.Sprintf("new/file%d.txt", fileID)
url = fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", user2.Name, repo1.Name, treePath, token4)
req = NewRequestWithJSON(t, "POST", url, &createFileOptions)
session.MakeRequest(t, req, http.StatusForbidden)
}
Loading