Skip to content

Commit

Permalink
Fixes go-gitea#7292 - API File Contents bug
Browse files Browse the repository at this point in the history
  • Loading branch information
richmahn committed Jun 26, 2019
1 parent 42729b7 commit 3dfafaf
Show file tree
Hide file tree
Showing 9 changed files with 169 additions and 54 deletions.
83 changes: 59 additions & 24 deletions integrations/api_repo_file_content_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,29 +12,32 @@ import (

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

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

func getExpectedFileContentResponseForFileContents(branch string) *api.FileContentResponse {
func getExpectedFileContentResponseForFileContents(ref, refType 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,
URL: setting.AppURL + "api/v1/repos/user2/repo1/contents/" + treePath + "?ref=" + ref,
HTMLURL: setting.AppURL + "user2/repo1/src/" + refType + "/" + ref + "/" + treePath,
GitURL: setting.AppURL + "api/v1/repos/user2/repo1/git/blobs/" + sha,
DownloadURL: setting.AppURL + "user2/repo1/raw/branch/" + branch + "/" + treePath,
DownloadURL: setting.AppURL + "user2/repo1/raw/" + refType + "/" + ref + "/" + treePath,
Type: "blob",
Encoding: "base64",
Content: "IyByZXBvMQoKRGVzY3JpcHRpb24gZm9yIHJlcG8x",
Links: &api.FileLinksResponse{
Self: setting.AppURL + "api/v1/repos/user2/repo1/contents/" + treePath,
Self: setting.AppURL + "api/v1/repos/user2/repo1/contents/" + treePath + "?ref=" + ref,
GitURL: setting.AppURL + "api/v1/repos/user2/repo1/git/blobs/" + sha,
HTMLURL: setting.AppURL + "user2/repo1/blob/" + branch + "/" + treePath,
HTMLURL: setting.AppURL + "user2/repo1/src/" + refType + "/" + ref + "/" + treePath,
},
}
}
Expand All @@ -44,6 +47,7 @@ func TestAPIGetFileContents(t *testing.T) {
}

func testAPIGetFileContents(t *testing.T, u *url.URL) {
/*** SETUP ***/
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
Expand All @@ -61,53 +65,84 @@ func testAPIGetFileContents(t *testing.T, u *url.URL) {
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)
// Make a new branch in repo1
newBranch := "test_branch"
repo1.CreateNewBranch(user2, repo1.DefaultBranch, newBranch)
// Get the commit ID of the default branch
gitRepo, _ := git.OpenRepository(repo1.RepoPath())
commitID, _ := gitRepo.GetBranchCommitID(repo1.DefaultBranch)
// Make a new tag in repo1
newTag := "test_tag"
gitRepo.CreateTag(newTag, commitID)
/*** END SETUP ***/

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

// No ref
refType = "branch"
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)
expectedFileContentResponse = getExpectedFileContentResponseForFileContents(repo1.DefaultBranch, refType)
assert.EqualValues(t, *expectedFileContentResponse, fileContentResponse)

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

// ref is the new tag we created above in setup
ref = newTag
refType = "tag"
req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/%s?ref=%s", user2.Name, repo1.Name, treePath, ref)
resp = session.MakeRequest(t, req, http.StatusOK)
DecodeJSON(t, resp, &fileContentResponse)
assert.NotNil(t, fileContentResponse)
expectedFileContentResponse = getExpectedFileContentResponseForFileContents(ref, refType)
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)
// ref is a commit
ref = commitID
refType = "commit"
req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/%s?ref=%s", user2.Name, repo1.Name, treePath, ref)
resp = session.MakeRequest(t, req, http.StatusOK)
DecodeJSON(t, resp, &fileContentResponse)
assert.NotNil(t, fileContentResponse)
expectedFileContentResponse = getExpectedFileContentResponseForFileContents("master2")
expectedFileContentResponse = getExpectedFileContentResponseForFileContents(ref, refType)
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)
// Test file contents a file with a bad ref
ref = "badref"
req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/%s?ref=%s", user2.Name, repo1.Name, treePath, ref)
resp = session.MakeRequest(t, req, http.StatusInternalServerError)
expectedAPIError := context.APIError{
Message: "object does not exist [id: " + branch + ", rel_path: ]",
Message: "object does not exist [id: " + ref + ", rel_path: ]",
URL: setting.API.SwaggerURL,
}
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
// Test accessing private ref 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
// Test access private ref 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)

Expand Down
12 changes: 7 additions & 5 deletions integrations/api_repo_file_create_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,15 +50,17 @@ func getExpectedFileResponseForCreate(commitID, treePath string) *api.FileRespon
Path: treePath,
SHA: sha,
Size: 16,
URL: setting.AppURL + "api/v1/repos/user2/repo1/contents/" + treePath,
HTMLURL: setting.AppURL + "user2/repo1/blob/master/" + treePath,
URL: setting.AppURL + "api/v1/repos/user2/repo1/contents/" + treePath + "?ref=master",
HTMLURL: setting.AppURL + "user2/repo1/src/branch/master/" + treePath,
GitURL: setting.AppURL + "api/v1/repos/user2/repo1/git/blobs/" + sha,
DownloadURL: setting.AppURL + "user2/repo1/raw/branch/master/" + treePath,
Type: "blob",
Encoding: "base64",
Content: "VGhpcyBpcyBuZXcgdGV4dA==",
Links: &api.FileLinksResponse{
Self: setting.AppURL + "api/v1/repos/user2/repo1/contents/" + treePath,
Self: setting.AppURL + "api/v1/repos/user2/repo1/contents/" + treePath + "?ref=master",
GitURL: setting.AppURL + "api/v1/repos/user2/repo1/git/blobs/" + sha,
HTMLURL: setting.AppURL + "user2/repo1/blob/master/" + treePath,
HTMLURL: setting.AppURL + "user2/repo1/src/branch/master/" + treePath,
},
},
Commit: &api.FileCommitResponse{
Expand Down Expand Up @@ -145,7 +147,7 @@ func TestAPICreateFile(t *testing.T) {
var fileResponse api.FileResponse
DecodeJSON(t, resp, &fileResponse)
expectedSHA := "a635aa942442ddfdba07468cf9661c08fbdf0ebf"
expectedHTMLURL := fmt.Sprintf(setting.AppURL+"user2/repo1/blob/new_branch/new/file%d.txt", fileID)
expectedHTMLURL := fmt.Sprintf(setting.AppURL+"user2/repo1/src/branch/new_branch/new/file%d.txt", fileID)
expectedDownloadURL := fmt.Sprintf(setting.AppURL+"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)
Expand Down
14 changes: 8 additions & 6 deletions integrations/api_repo_file_update_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,17 @@ func getExpectedFileResponseForUpdate(commitID, treePath string) *api.FileRespon
Path: treePath,
SHA: sha,
Size: 20,
URL: setting.AppURL + "api/v1/repos/user2/repo1/contents/" + treePath,
HTMLURL: setting.AppURL + "user2/repo1/blob/master/" + treePath,
URL: setting.AppURL + "api/v1/repos/user2/repo1/contents/" + treePath + "?ref=master",
HTMLURL: setting.AppURL + "user2/repo1/src/branch/master/" + treePath,
GitURL: setting.AppURL + "api/v1/repos/user2/repo1/git/blobs/" + sha,
DownloadURL: setting.AppURL + "user2/repo1/raw/branch/master/" + treePath,
Type: "blob",
Encoding: "base64",
Content: "VGhpcyBpcyB1cGRhdGVkIHRleHQ=",
Links: &api.FileLinksResponse{
Self: setting.AppURL + "api/v1/repos/user2/repo1/contents/" + treePath,
Self: setting.AppURL + "api/v1/repos/user2/repo1/contents/" + treePath + "?ref=master",
GitURL: setting.AppURL + "api/v1/repos/user2/repo1/git/blobs/" + sha,
HTMLURL: setting.AppURL + "user2/repo1/blob/master/" + treePath,
HTMLURL: setting.AppURL + "user2/repo1/src/branch/master/" + treePath,
},
},
Commit: &api.FileCommitResponse{
Expand Down Expand Up @@ -135,7 +137,7 @@ func TestAPIUpdateFile(t *testing.T) {
var fileResponse api.FileResponse
DecodeJSON(t, resp, &fileResponse)
expectedSHA := "08bd14b2e2852529157324de9c226b3364e76136"
expectedHTMLURL := fmt.Sprintf(setting.AppURL+"user2/repo1/blob/new_branch/update/file%d.txt", fileID)
expectedHTMLURL := fmt.Sprintf(setting.AppURL+"user2/repo1/src/branch/new_branch/update/file%d.txt", fileID)
expectedDownloadURL := fmt.Sprintf(setting.AppURL+"user2/repo1/raw/branch/new_branch/update/file%d.txt", fileID)
assert.EqualValues(t, expectedSHA, fileResponse.Content.SHA)
assert.EqualValues(t, expectedHTMLURL, fileResponse.Content.HTMLURL)
Expand All @@ -154,7 +156,7 @@ func TestAPIUpdateFile(t *testing.T) {
resp = session.MakeRequest(t, req, http.StatusOK)
DecodeJSON(t, resp, &fileResponse)
expectedSHA = "08bd14b2e2852529157324de9c226b3364e76136"
expectedHTMLURL = fmt.Sprintf(setting.AppURL+"user2/repo1/blob/master/rename/update/file%d.txt", fileID)
expectedHTMLURL = fmt.Sprintf(setting.AppURL+"user2/repo1/src/branch/master/rename/update/file%d.txt", fileID)
expectedDownloadURL = fmt.Sprintf(setting.AppURL+"user2/repo1/raw/branch/master/rename/update/file%d.txt", fileID)
assert.EqualValues(t, expectedSHA, fileResponse.Content.SHA)
assert.EqualValues(t, expectedHTMLURL, fileResponse.Content.HTMLURL)
Expand Down
48 changes: 48 additions & 0 deletions modules/git/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,54 @@ func (repo *Repository) parsePrettyFormatLogToList(logs []byte) (*list.List, err
return l, nil
}

// RepoRefType type of repo reference
type RepoRefType uint8

const (
// RepoRefLegacy unknown type, make educated guess and redirect.
// for backward compatibility with previous URL scheme
RepoRefTypeInvalid RepoRefType = iota
// RepoRefTypeBranch branch
RepoRefTypeBranch
// RepoRefTypeTag tag
RepoRefTypeTag
// RepoRefTypeCommit commit
RepoRefTypeCommit
// RepoRefTypeBlob blob
RepoRefTypeBlob
)

// String the string representation of the ref type
func (t RepoRefType) String() string {
switch t {
case RepoRefTypeBlob:
return "blob"
case RepoRefTypeBranch:
return "branch"
case RepoRefTypeCommit:
return "commit"
case RepoRefTypeTag:
return "tag"
case RepoRefTypeInvalid:
return ""
default:
return ""
}
}

func (r *Repository) GetRefType(ref string) RepoRefType {
if r.IsTagExist(ref) {
return RepoRefTypeTag
} else if r.IsBranchExist(ref) {
return RepoRefTypeBranch
} else if r.IsCommitExist(ref) {
return RepoRefTypeCommit
} else if _, err := r.GetBlob(ref); err == nil {
return RepoRefTypeBlob
}
return RepoRefTypeInvalid
}

// IsRepoURLAccessible checks if given repository URL is accessible.
func IsRepoURLAccessible(url string) bool {
_, err := NewCommand("ls-remote", "-q", "-h", url, "HEAD").Run()
Expand Down
30 changes: 22 additions & 8 deletions modules/repofiles/content.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,21 @@
package repofiles

import (
"fmt"
"net/url"
"strings"

"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/git"
api "code.gitea.io/gitea/modules/structs"
)

// GetFileContents gets the meta data on a file's contents
// GetFileContents gets the meta data on a file's contents. Ref can be a branch, commit or tag
func GetFileContents(repo *models.Repository, treePath, ref string) (*api.FileContentResponse, error) {
if ref == "" {
ref = repo.DefaultBranch
}
origRef := ref

// Check that the path given in opts.treePath is valid (not a git path)
treePath = CleanUploadFileName(treePath)
Expand All @@ -36,21 +39,30 @@ func GetFileContents(repo *models.Repository, treePath, ref string) (*api.FileCo
if err != nil {
return nil, err
}
commitID := commit.ID.String()
if len(ref) >= 4 && strings.HasPrefix(commitID, ref) {
ref = commit.ID.String()
}

entry, err := commit.GetTreeEntryByPath(treePath)
if err != nil {
return nil, err
}

urlRef := ref
if _, err := gitRepo.GetBranchCommit(ref); err == nil {
urlRef = "branch/" + ref
blobResponse, err := GetBlobBySHA(repo, entry.ID.String())
if err != nil {
return nil, err
}

refType := gitRepo.GetRefType(ref)
if refType == git.RepoRefTypeInvalid {
return nil, fmt.Errorf("no commit found for the ref [ref: %s]", ref)
}

selfURL, _ := url.Parse(repo.APIURL() + "/contents/" + treePath)
gitURL, _ := url.Parse(repo.APIURL() + "/git/blobs/" + entry.ID.String())
downloadURL, _ := url.Parse(repo.HTMLURL() + "/raw/" + urlRef + "/" + treePath)
htmlURL, _ := url.Parse(repo.HTMLURL() + "/blob/" + ref + "/" + treePath)
selfURL, _ := url.Parse(fmt.Sprintf("%s/contents/%s?ref=%s", repo.APIURL(), treePath, origRef))
gitURL, _ := url.Parse(fmt.Sprintf("%s/git/blobs/%s", repo.APIURL(), entry.ID.String()))
downloadURL, _ := url.Parse(fmt.Sprintf("%s/raw/%s/%s/%s", repo.HTMLURL(), refType.String(), ref, treePath))
htmlURL, _ := url.Parse(fmt.Sprintf("%s/src/%s/%s/%s", repo.HTMLURL(), refType.String(), ref, treePath))

fileContent := &api.FileContentResponse{
Name: entry.Name(),
Expand All @@ -62,6 +74,8 @@ func GetFileContents(repo *models.Repository, treePath, ref string) (*api.FileCo
GitURL: gitURL.String(),
DownloadURL: downloadURL.String(),
Type: entry.Type(),
Encoding: blobResponse.Encoding,
Content: blobResponse.Content,
Links: &api.FileLinksResponse{
Self: selfURL.String(),
GitURL: gitURL.String(),
Expand Down
16 changes: 9 additions & 7 deletions modules/repofiles/content_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
"testing"

"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/structs"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/test"

"github.com/stretchr/testify/assert"
Expand All @@ -30,20 +30,22 @@ func TestGetFileContents(t *testing.T) {
treePath := "README.md"
ref := ctx.Repo.Repository.DefaultBranch

expectedFileContentResponse := &structs.FileContentResponse{
expectedFileContentResponse := &api.FileContentResponse{
Name: treePath,
Path: treePath,
SHA: "4b4851ad51df6a7d9f25c979345979eaeb5b349f",
Size: 30,
URL: "https://try.gitea.io/api/v1/repos/user2/repo1/contents/README.md",
HTMLURL: "https://try.gitea.io/user2/repo1/blob/master/README.md",
URL: "https://try.gitea.io/api/v1/repos/user2/repo1/contents/README.md?ref=master",
HTMLURL: "https://try.gitea.io/user2/repo1/src/branch/master/README.md",
GitURL: "https://try.gitea.io/api/v1/repos/user2/repo1/git/blobs/4b4851ad51df6a7d9f25c979345979eaeb5b349f",
DownloadURL: "https://try.gitea.io/user2/repo1/raw/branch/master/README.md",
Type: "blob",
Links: &structs.FileLinksResponse{
Self: "https://try.gitea.io/api/v1/repos/user2/repo1/contents/README.md",
Encoding: "base64",
Content: "IyByZXBvMQoKRGVzY3JpcHRpb24gZm9yIHJlcG8x",
Links: &api.FileLinksResponse{
Self: "https://try.gitea.io/api/v1/repos/user2/repo1/contents/README.md?ref=master",
GitURL: "https://try.gitea.io/api/v1/repos/user2/repo1/git/blobs/4b4851ad51df6a7d9f25c979345979eaeb5b349f",
HTMLURL: "https://try.gitea.io/user2/repo1/blob/master/README.md",
HTMLURL: "https://try.gitea.io/user2/repo1/src/branch/master/README.md",
},
}

Expand Down
Loading

0 comments on commit 3dfafaf

Please sign in to comment.