mirror of
https://github.com/go-gitea/gitea
synced 2025-07-23 02:38:35 +00:00
Refactor repo contents API and add "contents-ext" API (#34822)
See the updated swagger document for details.
This commit is contained in:
@@ -5,13 +5,14 @@ package files
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/url"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/gitrepo"
|
||||
"code.gitea.io/gitea/modules/lfs"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
@@ -34,54 +35,50 @@ func (ct *ContentType) String() string {
|
||||
return string(*ct)
|
||||
}
|
||||
|
||||
type GetContentsOrListOptions struct {
|
||||
TreePath string
|
||||
IncludeSingleFileContent bool // include the file's content when the tree path is a file
|
||||
IncludeLfsMetadata bool
|
||||
}
|
||||
|
||||
// GetContentsOrList gets the metadata of a file's contents (*ContentsResponse) if treePath not a tree
|
||||
// directory, otherwise a listing of file contents ([]*ContentsResponse). Ref can be a branch, commit or tag
|
||||
func GetContentsOrList(ctx context.Context, repo *repo_model.Repository, refCommit *utils.RefCommit, treePath string) (any, error) {
|
||||
if repo.IsEmpty {
|
||||
return make([]any, 0), nil
|
||||
func GetContentsOrList(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, refCommit *utils.RefCommit, opts GetContentsOrListOptions) (ret api.ContentsExtResponse, _ error) {
|
||||
entry, err := prepareGetContentsEntry(refCommit, &opts.TreePath)
|
||||
if repo.IsEmpty && opts.TreePath == "" {
|
||||
return api.ContentsExtResponse{DirContents: make([]*api.ContentsResponse, 0)}, nil
|
||||
}
|
||||
|
||||
// Check that the path given in opts.treePath is valid (not a git path)
|
||||
cleanTreePath := CleanGitTreePath(treePath)
|
||||
if cleanTreePath == "" && treePath != "" {
|
||||
return nil, ErrFilenameInvalid{
|
||||
Path: treePath,
|
||||
}
|
||||
}
|
||||
treePath = cleanTreePath
|
||||
|
||||
// Get the commit object for the ref
|
||||
commit := refCommit.Commit
|
||||
|
||||
entry, err := commit.GetTreeEntryByPath(treePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return ret, err
|
||||
}
|
||||
|
||||
// get file contents
|
||||
if entry.Type() != "tree" {
|
||||
return GetContents(ctx, repo, refCommit, treePath, false)
|
||||
ret.FileContents, err = getFileContentsByEntryInternal(ctx, repo, gitRepo, refCommit, entry, opts)
|
||||
return ret, err
|
||||
}
|
||||
|
||||
// We are in a directory, so we return a list of FileContentResponse objects
|
||||
var fileList []*api.ContentsResponse
|
||||
|
||||
gitTree, err := commit.SubTree(treePath)
|
||||
// list directory contents
|
||||
gitTree, err := refCommit.Commit.SubTree(opts.TreePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return ret, err
|
||||
}
|
||||
entries, err := gitTree.ListEntries()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return ret, err
|
||||
}
|
||||
ret.DirContents = make([]*api.ContentsResponse, 0, len(entries))
|
||||
for _, e := range entries {
|
||||
subTreePath := path.Join(treePath, e.Name())
|
||||
fileContentResponse, err := GetContents(ctx, repo, refCommit, subTreePath, true)
|
||||
subOpts := opts
|
||||
subOpts.TreePath = path.Join(opts.TreePath, e.Name())
|
||||
subOpts.IncludeSingleFileContent = false // never include file content when listing a directory
|
||||
fileContentResponse, err := GetFileContents(ctx, repo, gitRepo, refCommit, subOpts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return ret, err
|
||||
}
|
||||
fileList = append(fileList, fileContentResponse)
|
||||
ret.DirContents = append(ret.DirContents, fileContentResponse)
|
||||
}
|
||||
return fileList, nil
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// GetObjectTypeFromTreeEntry check what content is behind it
|
||||
@@ -100,35 +97,36 @@ func GetObjectTypeFromTreeEntry(entry *git.TreeEntry) ContentType {
|
||||
}
|
||||
}
|
||||
|
||||
// GetContents gets the metadata on a file's contents. Ref can be a branch, commit or tag
|
||||
func GetContents(ctx context.Context, repo *repo_model.Repository, refCommit *utils.RefCommit, treePath string, forList bool) (*api.ContentsResponse, error) {
|
||||
func prepareGetContentsEntry(refCommit *utils.RefCommit, treePath *string) (*git.TreeEntry, error) {
|
||||
// Check that the path given in opts.treePath is valid (not a git path)
|
||||
cleanTreePath := CleanGitTreePath(treePath)
|
||||
if cleanTreePath == "" && treePath != "" {
|
||||
return nil, ErrFilenameInvalid{
|
||||
Path: treePath,
|
||||
}
|
||||
}
|
||||
treePath = cleanTreePath
|
||||
|
||||
gitRepo, closer, err := gitrepo.RepositoryFromContextOrOpen(ctx, repo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer closer.Close()
|
||||
|
||||
commit := refCommit.Commit
|
||||
entry, err := commit.GetTreeEntryByPath(treePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
cleanTreePath := CleanGitTreePath(*treePath)
|
||||
if cleanTreePath == "" && *treePath != "" {
|
||||
return nil, ErrFilenameInvalid{Path: *treePath}
|
||||
}
|
||||
*treePath = cleanTreePath
|
||||
|
||||
// Only allow safe ref types
|
||||
refType := refCommit.RefName.RefType()
|
||||
if refType != git.RefTypeBranch && refType != git.RefTypeTag && refType != git.RefTypeCommit {
|
||||
return nil, fmt.Errorf("no commit found for the ref [ref: %s]", refCommit.RefName)
|
||||
return nil, util.NewNotExistErrorf("no commit found for the ref [ref: %s]", refCommit.RefName)
|
||||
}
|
||||
|
||||
selfURL, err := url.Parse(repo.APIURL() + "/contents/" + util.PathEscapeSegments(treePath) + "?ref=" + url.QueryEscape(refCommit.InputRef))
|
||||
return refCommit.Commit.GetTreeEntryByPath(*treePath)
|
||||
}
|
||||
|
||||
// GetFileContents gets the metadata on a file's contents. Ref can be a branch, commit or tag
|
||||
func GetFileContents(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, refCommit *utils.RefCommit, opts GetContentsOrListOptions) (*api.ContentsResponse, error) {
|
||||
entry, err := prepareGetContentsEntry(refCommit, &opts.TreePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return getFileContentsByEntryInternal(ctx, repo, gitRepo, refCommit, entry, opts)
|
||||
}
|
||||
|
||||
func getFileContentsByEntryInternal(_ context.Context, repo *repo_model.Repository, gitRepo *git.Repository, refCommit *utils.RefCommit, entry *git.TreeEntry, opts GetContentsOrListOptions) (*api.ContentsResponse, error) {
|
||||
refType := refCommit.RefName.RefType()
|
||||
commit := refCommit.Commit
|
||||
selfURL, err := url.Parse(repo.APIURL() + "/contents/" + util.PathEscapeSegments(opts.TreePath) + "?ref=" + url.QueryEscape(refCommit.InputRef))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -139,7 +137,7 @@ func GetContents(ctx context.Context, repo *repo_model.Repository, refCommit *ut
|
||||
return nil, err
|
||||
}
|
||||
|
||||
lastCommit, err := commit.GetCommitByPath(treePath)
|
||||
lastCommit, err := refCommit.Commit.GetCommitByPath(opts.TreePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -147,7 +145,7 @@ func GetContents(ctx context.Context, repo *repo_model.Repository, refCommit *ut
|
||||
// All content types have these fields in populated
|
||||
contentsResponse := &api.ContentsResponse{
|
||||
Name: entry.Name(),
|
||||
Path: treePath,
|
||||
Path: opts.TreePath,
|
||||
SHA: entry.ID.String(),
|
||||
LastCommitSHA: lastCommit.ID.String(),
|
||||
Size: entry.Size(),
|
||||
@@ -170,13 +168,18 @@ func GetContents(ctx context.Context, repo *repo_model.Repository, refCommit *ut
|
||||
if entry.IsRegular() || entry.IsExecutable() {
|
||||
contentsResponse.Type = string(ContentTypeRegular)
|
||||
// if it is listing the repo root dir, don't waste system resources on reading content
|
||||
if !forList {
|
||||
blobResponse, err := GetBlobBySHA(ctx, repo, gitRepo, entry.ID.String())
|
||||
if opts.IncludeSingleFileContent {
|
||||
blobResponse, err := GetBlobBySHA(repo, gitRepo, entry.ID.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
contentsResponse.Encoding, contentsResponse.Content = blobResponse.Encoding, blobResponse.Content
|
||||
contentsResponse.LfsOid, contentsResponse.LfsSize = blobResponse.LfsOid, blobResponse.LfsSize
|
||||
} else if opts.IncludeLfsMetadata {
|
||||
contentsResponse.LfsOid, contentsResponse.LfsSize, err = parsePossibleLfsPointerBlob(gitRepo, entry.ID.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
contentsResponse.Encoding = blobResponse.Encoding
|
||||
contentsResponse.Content = blobResponse.Content
|
||||
}
|
||||
} else if entry.IsDir() {
|
||||
contentsResponse.Type = string(ContentTypeDir)
|
||||
@@ -190,7 +193,7 @@ func GetContents(ctx context.Context, repo *repo_model.Repository, refCommit *ut
|
||||
contentsResponse.Target = &targetFromContent
|
||||
} else if entry.IsSubModule() {
|
||||
contentsResponse.Type = string(ContentTypeSubmodule)
|
||||
submodule, err := commit.GetSubModule(treePath)
|
||||
submodule, err := commit.GetSubModule(opts.TreePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -200,7 +203,7 @@ func GetContents(ctx context.Context, repo *repo_model.Repository, refCommit *ut
|
||||
}
|
||||
// Handle links
|
||||
if entry.IsRegular() || entry.IsLink() || entry.IsExecutable() {
|
||||
downloadURL, err := url.Parse(repo.HTMLURL() + "/raw/" + refCommit.RefName.RefWebLinkPath() + "/" + util.PathEscapeSegments(treePath))
|
||||
downloadURL, err := url.Parse(repo.HTMLURL() + "/raw/" + refCommit.RefName.RefWebLinkPath() + "/" + util.PathEscapeSegments(opts.TreePath))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -208,7 +211,7 @@ func GetContents(ctx context.Context, repo *repo_model.Repository, refCommit *ut
|
||||
contentsResponse.DownloadURL = &downloadURLString
|
||||
}
|
||||
if !entry.IsSubModule() {
|
||||
htmlURL, err := url.Parse(repo.HTMLURL() + "/src/" + refCommit.RefName.RefWebLinkPath() + "/" + util.PathEscapeSegments(treePath))
|
||||
htmlURL, err := url.Parse(repo.HTMLURL() + "/src/" + refCommit.RefName.RefWebLinkPath() + "/" + util.PathEscapeSegments(opts.TreePath))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -228,8 +231,7 @@ func GetContents(ctx context.Context, repo *repo_model.Repository, refCommit *ut
|
||||
return contentsResponse, nil
|
||||
}
|
||||
|
||||
// GetBlobBySHA get the GitBlobResponse of a repository using a sha hash.
|
||||
func GetBlobBySHA(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, sha string) (*api.GitBlobResponse, error) {
|
||||
func GetBlobBySHA(repo *repo_model.Repository, gitRepo *git.Repository, sha string) (*api.GitBlobResponse, error) {
|
||||
gitBlob, err := gitRepo.GetBlob(sha)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -239,12 +241,49 @@ func GetBlobBySHA(ctx context.Context, repo *repo_model.Repository, gitRepo *git
|
||||
URL: repo.APIURL() + "/git/blobs/" + url.PathEscape(gitBlob.ID.String()),
|
||||
Size: gitBlob.Size(),
|
||||
}
|
||||
if gitBlob.Size() <= setting.API.DefaultMaxBlobSize {
|
||||
content, err := gitBlob.GetBlobContentBase64()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret.Encoding, ret.Content = util.ToPointer("base64"), &content
|
||||
|
||||
blobSize := gitBlob.Size()
|
||||
if blobSize > setting.API.DefaultMaxBlobSize {
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
var originContent *strings.Builder
|
||||
if 0 < blobSize && blobSize < lfs.MetaFileMaxSize {
|
||||
originContent = &strings.Builder{}
|
||||
}
|
||||
|
||||
content, err := gitBlob.GetBlobContentBase64(originContent)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret.Encoding, ret.Content = util.ToPointer("base64"), &content
|
||||
if originContent != nil {
|
||||
ret.LfsOid, ret.LfsSize = parsePossibleLfsPointerBuffer(strings.NewReader(originContent.String()))
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func parsePossibleLfsPointerBuffer(r io.Reader) (*string, *int64) {
|
||||
p, _ := lfs.ReadPointer(r)
|
||||
if p.IsValid() {
|
||||
return &p.Oid, &p.Size
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func parsePossibleLfsPointerBlob(gitRepo *git.Repository, sha string) (*string, *int64, error) {
|
||||
gitBlob, err := gitRepo.GetBlob(sha)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if gitBlob.Size() > lfs.MetaFileMaxSize {
|
||||
return nil, nil, nil // not a LFS pointer
|
||||
}
|
||||
buf, err := gitBlob.GetBlobContent(lfs.MetaFileMaxSize)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
oid, size := parsePossibleLfsPointerBuffer(strings.NewReader(buf))
|
||||
return oid, size, nil
|
||||
}
|
||||
|
@@ -8,7 +8,6 @@ import (
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
"code.gitea.io/gitea/modules/gitrepo"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
"code.gitea.io/gitea/routers/api/v1/utils"
|
||||
@@ -65,145 +64,58 @@ func TestGetContents(t *testing.T) {
|
||||
contexttest.LoadUser(t, ctx, 2)
|
||||
contexttest.LoadGitRepo(t, ctx)
|
||||
defer ctx.Repo.GitRepo.Close()
|
||||
|
||||
treePath := "README.md"
|
||||
repo, gitRepo := ctx.Repo.Repository, ctx.Repo.GitRepo
|
||||
refCommit, err := utils.ResolveRefCommit(ctx, ctx.Repo.Repository, ctx.Repo.Repository.DefaultBranch)
|
||||
require.NoError(t, err)
|
||||
|
||||
expectedContentsResponse := getExpectedReadmeContentsResponse()
|
||||
|
||||
t.Run("Get README.md contents with GetContents(ctx, )", func(t *testing.T) {
|
||||
fileContentResponse, err := GetContents(ctx, ctx.Repo.Repository, refCommit, treePath, false)
|
||||
assert.Equal(t, expectedContentsResponse, fileContentResponse)
|
||||
t.Run("GetContentsOrList(README.md)-MetaOnly", func(t *testing.T) {
|
||||
expectedContentsResponse := getExpectedReadmeContentsResponse()
|
||||
expectedContentsResponse.Encoding = nil // because will be in a list, doesn't have encoding and content
|
||||
expectedContentsResponse.Content = nil
|
||||
extResp, err := GetContentsOrList(ctx, repo, gitRepo, refCommit, GetContentsOrListOptions{TreePath: "README.md", IncludeSingleFileContent: false})
|
||||
assert.Equal(t, expectedContentsResponse, extResp.FileContents)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetContentsOrListForDir(t *testing.T) {
|
||||
unittest.PrepareTestEnv(t)
|
||||
ctx, _ := contexttest.MockContext(t, "user2/repo1")
|
||||
ctx.SetPathParam("id", "1")
|
||||
contexttest.LoadRepo(t, ctx, 1)
|
||||
contexttest.LoadRepoCommit(t, ctx)
|
||||
contexttest.LoadUser(t, ctx, 2)
|
||||
contexttest.LoadGitRepo(t, ctx)
|
||||
defer ctx.Repo.GitRepo.Close()
|
||||
|
||||
treePath := "" // root dir
|
||||
refCommit, err := utils.ResolveRefCommit(ctx, ctx.Repo.Repository, ctx.Repo.Repository.DefaultBranch)
|
||||
require.NoError(t, err)
|
||||
|
||||
readmeContentsResponse := getExpectedReadmeContentsResponse()
|
||||
// because will be in a list, doesn't have encoding and content
|
||||
readmeContentsResponse.Encoding = nil
|
||||
readmeContentsResponse.Content = nil
|
||||
|
||||
expectedContentsListResponse := []*api.ContentsResponse{
|
||||
readmeContentsResponse,
|
||||
}
|
||||
|
||||
t.Run("Get root dir contents with GetContentsOrList(ctx, )", func(t *testing.T) {
|
||||
fileContentResponse, err := GetContentsOrList(ctx, ctx.Repo.Repository, refCommit, treePath)
|
||||
assert.EqualValues(t, expectedContentsListResponse, fileContentResponse)
|
||||
t.Run("GetContentsOrList(README.md)", func(t *testing.T) {
|
||||
expectedContentsResponse := getExpectedReadmeContentsResponse()
|
||||
extResp, err := GetContentsOrList(ctx, repo, gitRepo, refCommit, GetContentsOrListOptions{TreePath: "README.md", IncludeSingleFileContent: true})
|
||||
assert.Equal(t, expectedContentsResponse, extResp.FileContents)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetContentsOrListForFile(t *testing.T) {
|
||||
unittest.PrepareTestEnv(t)
|
||||
ctx, _ := contexttest.MockContext(t, "user2/repo1")
|
||||
ctx.SetPathParam("id", "1")
|
||||
contexttest.LoadRepo(t, ctx, 1)
|
||||
contexttest.LoadRepoCommit(t, ctx)
|
||||
contexttest.LoadUser(t, ctx, 2)
|
||||
contexttest.LoadGitRepo(t, ctx)
|
||||
defer ctx.Repo.GitRepo.Close()
|
||||
|
||||
treePath := "README.md"
|
||||
refCommit, err := utils.ResolveRefCommit(ctx, ctx.Repo.Repository, ctx.Repo.Repository.DefaultBranch)
|
||||
require.NoError(t, err)
|
||||
|
||||
expectedContentsResponse := getExpectedReadmeContentsResponse()
|
||||
|
||||
t.Run("Get README.md contents with GetContentsOrList(ctx, )", func(t *testing.T) {
|
||||
fileContentResponse, err := GetContentsOrList(ctx, ctx.Repo.Repository, refCommit, treePath)
|
||||
assert.EqualValues(t, expectedContentsResponse, fileContentResponse)
|
||||
t.Run("GetContentsOrList(RootDir)", func(t *testing.T) {
|
||||
readmeContentsResponse := getExpectedReadmeContentsResponse()
|
||||
readmeContentsResponse.Encoding = nil // because will be in a list, doesn't have encoding and content
|
||||
readmeContentsResponse.Content = nil
|
||||
expectedContentsListResponse := []*api.ContentsResponse{readmeContentsResponse}
|
||||
// even if IncludeFileContent is true, it has no effect for directory listing
|
||||
extResp, err := GetContentsOrList(ctx, repo, gitRepo, refCommit, GetContentsOrListOptions{TreePath: "", IncludeSingleFileContent: true})
|
||||
assert.Equal(t, expectedContentsListResponse, extResp.DirContents)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetContentsErrors(t *testing.T) {
|
||||
unittest.PrepareTestEnv(t)
|
||||
ctx, _ := contexttest.MockContext(t, "user2/repo1")
|
||||
ctx.SetPathParam("id", "1")
|
||||
contexttest.LoadRepo(t, ctx, 1)
|
||||
contexttest.LoadRepoCommit(t, ctx)
|
||||
contexttest.LoadUser(t, ctx, 2)
|
||||
contexttest.LoadGitRepo(t, ctx)
|
||||
defer ctx.Repo.GitRepo.Close()
|
||||
|
||||
repo := ctx.Repo.Repository
|
||||
refCommit, err := utils.ResolveRefCommit(ctx, ctx.Repo.Repository, ctx.Repo.Repository.DefaultBranch)
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Run("bad treePath", func(t *testing.T) {
|
||||
badTreePath := "bad/tree.md"
|
||||
fileContentResponse, err := GetContents(ctx, repo, refCommit, badTreePath, false)
|
||||
t.Run("GetContentsOrList(NoSuchTreePath)", func(t *testing.T) {
|
||||
extResp, err := GetContentsOrList(ctx, repo, gitRepo, refCommit, GetContentsOrListOptions{TreePath: "no-such/file.md"})
|
||||
assert.Error(t, err)
|
||||
assert.EqualError(t, err, "object does not exist [id: , rel_path: bad]")
|
||||
assert.Nil(t, fileContentResponse)
|
||||
assert.EqualError(t, err, "object does not exist [id: , rel_path: no-such]")
|
||||
assert.Nil(t, extResp.DirContents)
|
||||
assert.Nil(t, extResp.FileContents)
|
||||
})
|
||||
|
||||
t.Run("GetBlobBySHA", func(t *testing.T) {
|
||||
sha := "65f1bf27bc3bf70f64657658635e66094edbcb4d"
|
||||
ctx.SetPathParam("id", "1")
|
||||
ctx.SetPathParam("sha", sha)
|
||||
gbr, err := GetBlobBySHA(ctx.Repo.Repository, ctx.Repo.GitRepo, ctx.PathParam("sha"))
|
||||
expectedGBR := &api.GitBlobResponse{
|
||||
Content: util.ToPointer("dHJlZSAyYTJmMWQ0NjcwNzI4YTJlMTAwNDllMzQ1YmQ3YTI3NjQ2OGJlYWI2CmF1dGhvciB1c2VyMSA8YWRkcmVzczFAZXhhbXBsZS5jb20+IDE0ODk5NTY0NzkgLTA0MDAKY29tbWl0dGVyIEV0aGFuIEtvZW5pZyA8ZXRoYW50a29lbmlnQGdtYWlsLmNvbT4gMTQ4OTk1NjQ3OSAtMDQwMAoKSW5pdGlhbCBjb21taXQK"),
|
||||
Encoding: util.ToPointer("base64"),
|
||||
URL: "https://try.gitea.io/api/v1/repos/user2/repo1/git/blobs/65f1bf27bc3bf70f64657658635e66094edbcb4d",
|
||||
SHA: "65f1bf27bc3bf70f64657658635e66094edbcb4d",
|
||||
Size: 180,
|
||||
}
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expectedGBR, gbr)
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetContentsOrListErrors(t *testing.T) {
|
||||
unittest.PrepareTestEnv(t)
|
||||
ctx, _ := contexttest.MockContext(t, "user2/repo1")
|
||||
ctx.SetPathParam("id", "1")
|
||||
contexttest.LoadRepo(t, ctx, 1)
|
||||
contexttest.LoadRepoCommit(t, ctx)
|
||||
contexttest.LoadUser(t, ctx, 2)
|
||||
contexttest.LoadGitRepo(t, ctx)
|
||||
defer ctx.Repo.GitRepo.Close()
|
||||
|
||||
repo := ctx.Repo.Repository
|
||||
refCommit, err := utils.ResolveRefCommit(ctx, ctx.Repo.Repository, ctx.Repo.Repository.DefaultBranch)
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Run("bad treePath", func(t *testing.T) {
|
||||
badTreePath := "bad/tree.md"
|
||||
fileContentResponse, err := GetContentsOrList(ctx, repo, refCommit, badTreePath)
|
||||
assert.Error(t, err)
|
||||
assert.EqualError(t, err, "object does not exist [id: , rel_path: bad]")
|
||||
assert.Nil(t, fileContentResponse)
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetBlobBySHA(t *testing.T) {
|
||||
unittest.PrepareTestEnv(t)
|
||||
ctx, _ := contexttest.MockContext(t, "user2/repo1")
|
||||
contexttest.LoadRepo(t, ctx, 1)
|
||||
contexttest.LoadRepoCommit(t, ctx)
|
||||
contexttest.LoadUser(t, ctx, 2)
|
||||
contexttest.LoadGitRepo(t, ctx)
|
||||
defer ctx.Repo.GitRepo.Close()
|
||||
|
||||
sha := "65f1bf27bc3bf70f64657658635e66094edbcb4d"
|
||||
ctx.SetPathParam("id", "1")
|
||||
ctx.SetPathParam("sha", sha)
|
||||
|
||||
gitRepo, err := gitrepo.OpenRepository(ctx, ctx.Repo.Repository)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
gbr, err := GetBlobBySHA(ctx, ctx.Repo.Repository, gitRepo, ctx.PathParam("sha"))
|
||||
expectedGBR := &api.GitBlobResponse{
|
||||
Content: util.ToPointer("dHJlZSAyYTJmMWQ0NjcwNzI4YTJlMTAwNDllMzQ1YmQ3YTI3NjQ2OGJlYWI2CmF1dGhvciB1c2VyMSA8YWRkcmVzczFAZXhhbXBsZS5jb20+IDE0ODk5NTY0NzkgLTA0MDAKY29tbWl0dGVyIEV0aGFuIEtvZW5pZyA8ZXRoYW50a29lbmlnQGdtYWlsLmNvbT4gMTQ4OTk1NjQ3OSAtMDQwMAoKSW5pdGlhbCBjb21taXQK"),
|
||||
Encoding: util.ToPointer("base64"),
|
||||
URL: "https://try.gitea.io/api/v1/repos/user2/repo1/git/blobs/65f1bf27bc3bf70f64657658635e66094edbcb4d",
|
||||
SHA: "65f1bf27bc3bf70f64657658635e66094edbcb4d",
|
||||
Size: 180,
|
||||
}
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expectedGBR, gbr)
|
||||
}
|
||||
|
@@ -19,12 +19,12 @@ import (
|
||||
"code.gitea.io/gitea/routers/api/v1/utils"
|
||||
)
|
||||
|
||||
func GetContentsListFromTreePaths(ctx context.Context, repo *repo_model.Repository, refCommit *utils.RefCommit, treePaths []string) (files []*api.ContentsResponse) {
|
||||
func GetContentsListFromTreePaths(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, refCommit *utils.RefCommit, treePaths []string) (files []*api.ContentsResponse) {
|
||||
var size int64
|
||||
for _, treePath := range treePaths {
|
||||
fileContents, _ := GetContents(ctx, repo, refCommit, treePath, false) // ok if fails, then will be nil
|
||||
fileContents, _ := GetFileContents(ctx, repo, gitRepo, refCommit, GetContentsOrListOptions{TreePath: treePath, IncludeSingleFileContent: true}) // ok if fails, then will be nil
|
||||
if fileContents != nil && fileContents.Content != nil && *fileContents.Content != "" {
|
||||
// if content isn't empty (e.g. due to the single blob being too large), add file size to response size
|
||||
// if content isn't empty (e.g., due to the single blob being too large), add file size to response size
|
||||
size += int64(len(*fileContents.Content))
|
||||
}
|
||||
if size > setting.API.DefaultMaxResponseSize {
|
||||
@@ -38,8 +38,8 @@ func GetContentsListFromTreePaths(ctx context.Context, repo *repo_model.Reposito
|
||||
return files
|
||||
}
|
||||
|
||||
func GetFilesResponseFromCommit(ctx context.Context, repo *repo_model.Repository, refCommit *utils.RefCommit, treeNames []string) (*api.FilesResponse, error) {
|
||||
files := GetContentsListFromTreePaths(ctx, repo, refCommit, treeNames)
|
||||
func GetFilesResponseFromCommit(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, refCommit *utils.RefCommit, treeNames []string) (*api.FilesResponse, error) {
|
||||
files := GetContentsListFromTreePaths(ctx, repo, gitRepo, refCommit, treeNames)
|
||||
fileCommitResponse, _ := GetFileCommitResponse(repo, refCommit.Commit) // ok if fails, then will be nil
|
||||
verification := GetPayloadCommitVerification(ctx, refCommit.Commit)
|
||||
filesResponse := &api.FilesResponse{
|
||||
|
@@ -315,7 +315,7 @@ func ChangeRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *use
|
||||
|
||||
// FIXME: this call seems not right, why it needs to read the file content again
|
||||
// FIXME: why it uses the NewBranch as "ref", it should use the commit ID because the response is only for this commit
|
||||
filesResponse, err := GetFilesResponseFromCommit(ctx, repo, utils.NewRefCommit(git.RefNameFromBranch(opts.NewBranch), commit), treePaths)
|
||||
filesResponse, err := GetFilesResponseFromCommit(ctx, repo, gitRepo, utils.NewRefCommit(git.RefNameFromBranch(opts.NewBranch), commit), treePaths)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
Reference in New Issue
Block a user