@@ -1,117 +0,0 @@ | |||
// 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" | |||
"net/url" | |||
"path/filepath" | |||
"testing" | |||
"code.gitea.io/gitea/models" | |||
"code.gitea.io/gitea/modules/context" | |||
"code.gitea.io/gitea/modules/setting" | |||
api "code.gitea.io/gitea/modules/structs" | |||
"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) { | |||
onGiteaRun(t, testAPIGetFileContents) | |||
} | |||
func testAPIGetFileContents(t *testing.T, u *url.URL) { | |||
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: 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 | |||
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) | |||
} |
@@ -44,21 +44,29 @@ func getCreateFileOptions() api.CreateFileOptions { | |||
func getExpectedFileResponseForCreate(commitID, treePath string) *api.FileResponse { | |||
sha := "a635aa942442ddfdba07468cf9661c08fbdf0ebf" | |||
encoding := "base64" | |||
content := "VGhpcyBpcyBuZXcgdGV4dA==" | |||
selfURL := 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 | |||
return &api.FileResponse{ | |||
Content: &api.FileContentResponse{ | |||
Content: &api.ContentsResponse{ | |||
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", | |||
Type: "file", | |||
Encoding: &encoding, | |||
Content: &content, | |||
URL: &selfURL, | |||
HTMLURL: &htmlURL, | |||
GitURL: &gitURL, | |||
DownloadURL: &downloadURL, | |||
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, | |||
Self: &selfURL, | |||
GitURL: &gitURL, | |||
HTMLURL: &htmlURL, | |||
}, | |||
}, | |||
Commit: &api.FileCommitResponse{ | |||
@@ -145,11 +153,11 @@ 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) | |||
assert.EqualValues(t, expectedDownloadURL, fileResponse.Content.DownloadURL) | |||
assert.EqualValues(t, expectedHTMLURL, *fileResponse.Content.HTMLURL) | |||
assert.EqualValues(t, expectedDownloadURL, *fileResponse.Content.DownloadURL) | |||
assert.EqualValues(t, createFileOptions.Message+"\n", fileResponse.Commit.Message) | |||
// Test creating a file without a message |
@@ -47,21 +47,29 @@ func getUpdateFileOptions() *api.UpdateFileOptions { | |||
func getExpectedFileResponseForUpdate(commitID, treePath string) *api.FileResponse { | |||
sha := "08bd14b2e2852529157324de9c226b3364e76136" | |||
encoding := "base64" | |||
content := "VGhpcyBpcyB1cGRhdGVkIHRleHQ=" | |||
selfURL := 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 | |||
return &api.FileResponse{ | |||
Content: &api.FileContentResponse{ | |||
Content: &api.ContentsResponse{ | |||
Name: filepath.Base(treePath), | |||
Path: treePath, | |||
SHA: sha, | |||
Type: "file", | |||
Size: 20, | |||
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", | |||
Encoding: &encoding, | |||
Content: &content, | |||
URL: &selfURL, | |||
HTMLURL: &htmlURL, | |||
GitURL: &gitURL, | |||
DownloadURL: &downloadURL, | |||
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, | |||
Self: &selfURL, | |||
GitURL: &gitURL, | |||
HTMLURL: &htmlURL, | |||
}, | |||
}, | |||
Commit: &api.FileCommitResponse{ | |||
@@ -150,11 +158,11 @@ 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) | |||
assert.EqualValues(t, expectedDownloadURL, fileResponse.Content.DownloadURL) | |||
assert.EqualValues(t, expectedHTMLURL, *fileResponse.Content.HTMLURL) | |||
assert.EqualValues(t, expectedDownloadURL, *fileResponse.Content.DownloadURL) | |||
assert.EqualValues(t, updateFileOptions.Message+"\n", fileResponse.Commit.Message) | |||
// Test updating a file and renaming it | |||
@@ -170,11 +178,11 @@ 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) | |||
assert.EqualValues(t, expectedDownloadURL, fileResponse.Content.DownloadURL) | |||
assert.EqualValues(t, expectedHTMLURL, *fileResponse.Content.HTMLURL) | |||
assert.EqualValues(t, expectedDownloadURL, *fileResponse.Content.DownloadURL) | |||
// Test updating a file without a message | |||
updateFileOptions = getUpdateFileOptions() |
@@ -0,0 +1,156 @@ | |||
// 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" | |||
"net/url" | |||
"path/filepath" | |||
"testing" | |||
"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 getExpectedContentsListResponseForContents(ref, refType string) []*api.ContentsResponse { | |||
treePath := "README.md" | |||
sha := "4b4851ad51df6a7d9f25c979345979eaeb5b349f" | |||
selfURL := 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/" + refType + "/" + ref + "/" + treePath | |||
return []*api.ContentsResponse{ | |||
{ | |||
Name: filepath.Base(treePath), | |||
Path: treePath, | |||
SHA: sha, | |||
Type: "file", | |||
Size: 30, | |||
URL: &selfURL, | |||
HTMLURL: &htmlURL, | |||
GitURL: &gitURL, | |||
DownloadURL: &downloadURL, | |||
Links: &api.FileLinksResponse{ | |||
Self: &selfURL, | |||
GitURL: &gitURL, | |||
HTMLURL: &htmlURL, | |||
}, | |||
}, | |||
} | |||
} | |||
func TestAPIGetContentsList(t *testing.T) { | |||
onGiteaRun(t, testAPIGetContentsList) | |||
} | |||
func testAPIGetContentsList(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 | |||
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 := "" // root dir | |||
// 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 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 contentsListResponse []*api.ContentsResponse | |||
DecodeJSON(t, resp, &contentsListResponse) | |||
assert.NotNil(t, contentsListResponse) | |||
expectedContentsListResponse := getExpectedContentsListResponseForContents(ref, refType) | |||
assert.EqualValues(t, expectedContentsListResponse, contentsListResponse) | |||
// 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, &contentsListResponse) | |||
assert.NotNil(t, contentsListResponse) | |||
expectedContentsListResponse = getExpectedContentsListResponseForContents(repo1.DefaultBranch, refType) | |||
assert.EqualValues(t, expectedContentsListResponse, contentsListResponse) | |||
// 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, &contentsListResponse) | |||
assert.NotNil(t, contentsListResponse) | |||
expectedContentsListResponse = getExpectedContentsListResponseForContents(ref, refType) | |||
assert.EqualValues(t, expectedContentsListResponse, contentsListResponse) | |||
// 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, &contentsListResponse) | |||
assert.NotNil(t, contentsListResponse) | |||
expectedContentsListResponse = getExpectedContentsListResponseForContents(ref, refType) | |||
assert.EqualValues(t, expectedContentsListResponse, contentsListResponse) | |||
// 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, &contentsListResponse) | |||
assert.NotNil(t, contentsListResponse) | |||
expectedContentsListResponse = getExpectedContentsListResponseForContents(ref, refType) | |||
assert.EqualValues(t, expectedContentsListResponse, contentsListResponse) | |||
// 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: " + ref + ", rel_path: ]", | |||
URL: setting.API.SwaggerURL, | |||
} | |||
var apiError context.APIError | |||
DecodeJSON(t, resp, &apiError) | |||
assert.Equal(t, expectedAPIError, apiError) | |||
// 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 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) | |||
// 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) | |||
} |
@@ -0,0 +1,157 @@ | |||
// 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" | |||
"net/url" | |||
"testing" | |||
"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 getExpectedContentsResponseForContents(ref, refType string) *api.ContentsResponse { | |||
treePath := "README.md" | |||
sha := "4b4851ad51df6a7d9f25c979345979eaeb5b349f" | |||
encoding := "base64" | |||
content := "IyByZXBvMQoKRGVzY3JpcHRpb24gZm9yIHJlcG8x" | |||
selfURL := 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/" + refType + "/" + ref + "/" + treePath | |||
return &api.ContentsResponse{ | |||
Name: treePath, | |||
Path: treePath, | |||
SHA: sha, | |||
Type: "file", | |||
Size: 30, | |||
Encoding: &encoding, | |||
Content: &content, | |||
URL: &selfURL, | |||
HTMLURL: &htmlURL, | |||
GitURL: &gitURL, | |||
DownloadURL: &downloadURL, | |||
Links: &api.FileLinksResponse{ | |||
Self: &selfURL, | |||
GitURL: &gitURL, | |||
HTMLURL: &htmlURL, | |||
}, | |||
} | |||
} | |||
func TestAPIGetContents(t *testing.T) { | |||
onGiteaRun(t, testAPIGetContents) | |||
} | |||
func testAPIGetContents(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 | |||
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 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 contentsResponse api.ContentsResponse | |||
DecodeJSON(t, resp, &contentsResponse) | |||
assert.NotNil(t, contentsResponse) | |||
expectedContentsResponse := getExpectedContentsResponseForContents(ref, refType) | |||
assert.EqualValues(t, *expectedContentsResponse, contentsResponse) | |||
// 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, &contentsResponse) | |||
assert.NotNil(t, contentsResponse) | |||
expectedContentsResponse = getExpectedContentsResponseForContents(repo1.DefaultBranch, refType) | |||
assert.EqualValues(t, *expectedContentsResponse, contentsResponse) | |||
// 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, &contentsResponse) | |||
assert.NotNil(t, contentsResponse) | |||
expectedContentsResponse = getExpectedContentsResponseForContents(ref, refType) | |||
assert.EqualValues(t, *expectedContentsResponse, contentsResponse) | |||
// 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, &contentsResponse) | |||
assert.NotNil(t, contentsResponse) | |||
expectedContentsResponse = getExpectedContentsResponseForContents(ref, refType) | |||
assert.EqualValues(t, *expectedContentsResponse, contentsResponse) | |||
// 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, &contentsResponse) | |||
assert.NotNil(t, contentsResponse) | |||
expectedContentsResponse = getExpectedContentsResponseForContents(ref, refType) | |||
assert.EqualValues(t, *expectedContentsResponse, contentsResponse) | |||
// 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: " + ref + ", rel_path: ]", | |||
URL: setting.API.SwaggerURL, | |||
} | |||
var apiError context.APIError | |||
DecodeJSON(t, resp, &apiError) | |||
assert.Equal(t, expectedAPIError, apiError) | |||
// 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 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) | |||
// 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) | |||
} |
@@ -6,6 +6,7 @@ package integrations | |||
import ( | |||
"net/url" | |||
"path/filepath" | |||
"testing" | |||
"time" | |||
@@ -47,21 +48,30 @@ func getUpdateRepoFileOptions(repo *models.Repository) *repofiles.UpdateRepoFile | |||
} | |||
func getExpectedFileResponseForRepofilesCreate(commitID string) *api.FileResponse { | |||
treePath := "new/file.txt" | |||
encoding := "base64" | |||
content := "VGhpcyBpcyBhIE5FVyBmaWxl" | |||
selfURL := 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/103ff9234cefeee5ec5361d22b49fbb04d385885" | |||
downloadURL := setting.AppURL + "user2/repo1/raw/branch/master/" + treePath | |||
return &api.FileResponse{ | |||
Content: &api.FileContentResponse{ | |||
Name: "file.txt", | |||
Path: "new/file.txt", | |||
Content: &api.ContentsResponse{ | |||
Name: filepath.Base(treePath), | |||
Path: treePath, | |||
SHA: "103ff9234cefeee5ec5361d22b49fbb04d385885", | |||
Type: "file", | |||
Size: 18, | |||
URL: setting.AppURL + "api/v1/repos/user2/repo1/contents/new/file.txt", | |||
HTMLURL: setting.AppURL + "user2/repo1/blob/master/new/file.txt", | |||
GitURL: setting.AppURL + "api/v1/repos/user2/repo1/git/blobs/103ff9234cefeee5ec5361d22b49fbb04d385885", | |||
DownloadURL: setting.AppURL + "user2/repo1/raw/branch/master/new/file.txt", | |||
Type: "blob", | |||
Encoding: &encoding, | |||
Content: &content, | |||
URL: &selfURL, | |||
HTMLURL: &htmlURL, | |||
GitURL: &gitURL, | |||
DownloadURL: &downloadURL, | |||
Links: &api.FileLinksResponse{ | |||
Self: setting.AppURL + "api/v1/repos/user2/repo1/contents/new/file.txt", | |||
GitURL: setting.AppURL + "api/v1/repos/user2/repo1/git/blobs/103ff9234cefeee5ec5361d22b49fbb04d385885", | |||
HTMLURL: setting.AppURL + "user2/repo1/blob/master/new/file.txt", | |||
Self: &selfURL, | |||
GitURL: &gitURL, | |||
HTMLURL: &htmlURL, | |||
}, | |||
}, | |||
Commit: &api.FileCommitResponse{ | |||
@@ -105,22 +115,30 @@ func getExpectedFileResponseForRepofilesCreate(commitID string) *api.FileRespons | |||
} | |||
} | |||
func getExpectedFileResponseForRepofilesUpdate(commitID string) *api.FileResponse { | |||
func getExpectedFileResponseForRepofilesUpdate(commitID, filename string) *api.FileResponse { | |||
encoding := "base64" | |||
content := "VGhpcyBpcyBVUERBVEVEIGNvbnRlbnQgZm9yIHRoZSBSRUFETUUgZmlsZQ==" | |||
selfURL := setting.AppURL + "api/v1/repos/user2/repo1/contents/" + filename + "?ref=master" | |||
htmlURL := setting.AppURL + "user2/repo1/src/branch/master/" + filename | |||
gitURL := setting.AppURL + "api/v1/repos/user2/repo1/git/blobs/dbf8d00e022e05b7e5cf7e535de857de57925647" | |||
downloadURL := setting.AppURL + "user2/repo1/raw/branch/master/" + filename | |||
return &api.FileResponse{ | |||
Content: &api.FileContentResponse{ | |||
Name: "README.md", | |||
Path: "README.md", | |||
Content: &api.ContentsResponse{ | |||
Name: filename, | |||
Path: filename, | |||
SHA: "dbf8d00e022e05b7e5cf7e535de857de57925647", | |||
Type: "file", | |||
Size: 43, | |||
URL: setting.AppURL + "api/v1/repos/user2/repo1/contents/README.md", | |||
HTMLURL: setting.AppURL + "user2/repo1/blob/master/README.md", | |||
GitURL: setting.AppURL + "api/v1/repos/user2/repo1/git/blobs/dbf8d00e022e05b7e5cf7e535de857de57925647", | |||
DownloadURL: setting.AppURL + "user2/repo1/raw/branch/master/README.md", | |||
Type: "blob", | |||
Encoding: &encoding, | |||
Content: &content, | |||
URL: &selfURL, | |||
HTMLURL: &htmlURL, | |||
GitURL: &gitURL, | |||
DownloadURL: &downloadURL, | |||
Links: &api.FileLinksResponse{ | |||
Self: setting.AppURL + "api/v1/repos/user2/repo1/contents/README.md", | |||
GitURL: setting.AppURL + "api/v1/repos/user2/repo1/git/blobs/dbf8d00e022e05b7e5cf7e535de857de57925647", | |||
HTMLURL: setting.AppURL + "user2/repo1/blob/master/README.md", | |||
Self: &selfURL, | |||
GitURL: &gitURL, | |||
HTMLURL: &htmlURL, | |||
}, | |||
}, | |||
Commit: &api.FileCommitResponse{ | |||
@@ -213,7 +231,7 @@ func TestCreateOrUpdateRepoFileForUpdate(t *testing.T) { | |||
assert.Nil(t, err) | |||
gitRepo, _ := git.OpenRepository(repo.RepoPath()) | |||
commitID, _ := gitRepo.GetBranchCommitID(opts.NewBranch) | |||
expectedFileResponse := getExpectedFileResponseForRepofilesUpdate(commitID) | |||
expectedFileResponse := getExpectedFileResponseForRepofilesUpdate(commitID, opts.TreePath) | |||
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) | |||
@@ -234,9 +252,8 @@ func TestCreateOrUpdateRepoFileForUpdateWithFileMove(t *testing.T) { | |||
repo := ctx.Repo.Repository | |||
doer := ctx.User | |||
opts := getUpdateRepoFileOptions(repo) | |||
suffix := "_new" | |||
opts.FromTreePath = "README.md" | |||
opts.TreePath = "README.md" + suffix // new file name, README.md_new | |||
opts.TreePath = "README_new.md" // new file name, README_new.md | |||
// test | |||
fileResponse, err := repofiles.CreateOrUpdateRepoFile(repo, doer, opts) | |||
@@ -245,7 +262,7 @@ func TestCreateOrUpdateRepoFileForUpdateWithFileMove(t *testing.T) { | |||
assert.Nil(t, err) | |||
gitRepo, _ := git.OpenRepository(repo.RepoPath()) | |||
commit, _ := gitRepo.GetBranchCommit(opts.NewBranch) | |||
expectedFileResponse := getExpectedFileResponseForRepofilesUpdate(commit.ID.String()) | |||
expectedFileResponse := getExpectedFileResponseForRepofilesUpdate(commit.ID.String(), opts.TreePath) | |||
// assert that the old file no longer exists in the last commit of the branch | |||
fromEntry, err := commit.GetTreeEntryByPath(opts.FromTreePath) | |||
toEntry, err := commit.GetTreeEntryByPath(opts.TreePath) | |||
@@ -253,9 +270,9 @@ func TestCreateOrUpdateRepoFileForUpdateWithFileMove(t *testing.T) { | |||
assert.NotNil(t, toEntry) // Should exist here | |||
// assert SHA has remained the same but paths use the new file name | |||
assert.EqualValues(t, expectedFileResponse.Content.SHA, fileResponse.Content.SHA) | |||
assert.EqualValues(t, expectedFileResponse.Content.Name+suffix, fileResponse.Content.Name) | |||
assert.EqualValues(t, expectedFileResponse.Content.Path+suffix, fileResponse.Content.Path) | |||
assert.EqualValues(t, expectedFileResponse.Content.URL+suffix, fileResponse.Content.URL) | |||
assert.EqualValues(t, expectedFileResponse.Content.Name, fileResponse.Content.Name) | |||
assert.EqualValues(t, expectedFileResponse.Content.Path, fileResponse.Content.Path) | |||
assert.EqualValues(t, expectedFileResponse.Content.URL, fileResponse.Content.URL) | |||
assert.EqualValues(t, expectedFileResponse.Commit.SHA, fileResponse.Commit.SHA) | |||
assert.EqualValues(t, expectedFileResponse.Commit.HTMLURL, fileResponse.Commit.HTMLURL) | |||
}) | |||
@@ -284,7 +301,7 @@ func TestCreateOrUpdateRepoFileWithoutBranchNames(t *testing.T) { | |||
assert.Nil(t, err) | |||
gitRepo, _ := git.OpenRepository(repo.RepoPath()) | |||
commitID, _ := gitRepo.GetBranchCommitID(repo.DefaultBranch) | |||
expectedFileResponse := getExpectedFileResponseForRepofilesUpdate(commitID) | |||
expectedFileResponse := getExpectedFileResponseForRepofilesUpdate(commitID, opts.TreePath) | |||
assert.EqualValues(t, expectedFileResponse.Content, fileResponse.Content) | |||
}) | |||
} |
@@ -37,6 +37,19 @@ func (b *Blob) Name() string { | |||
return b.name | |||
} | |||
// GetBlobContent Gets the content of the blob as raw text | |||
func (b *Blob) GetBlobContent() (string, error) { | |||
dataRc, err := b.DataAsync() | |||
if err != nil { | |||
return "", err | |||
} | |||
defer dataRc.Close() | |||
buf := make([]byte, 1024) | |||
n, _ := dataRc.Read(buf) | |||
buf = buf[:n] | |||
return string(buf), nil | |||
} | |||
// GetBlobContentBase64 Reads the content of the blob with a base64 encode and returns the encoded string | |||
func (b *Blob) GetBlobContentBase64() (string, error) { | |||
dataRc, err := b.DataAsync() |
@@ -1,4 +1,5 @@ | |||
// Copyright 2014 The Gogs Authors. All rights reserved. | |||
// 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. | |||
@@ -22,6 +23,8 @@ const ( | |||
ObjectBlob ObjectType = "blob" | |||
// ObjectTag tag object type | |||
ObjectTag ObjectType = "tag" | |||
// ObjectBranch branch object type | |||
ObjectBranch ObjectType = "branch" | |||
) | |||
// HashObject takes a reader and returns SHA1 hash for that reader | |||
@@ -44,3 +47,17 @@ func (repo *Repository) hashObject(reader io.Reader) (string, error) { | |||
} | |||
return strings.TrimSpace(stdout.String()), nil | |||
} | |||
// GetRefType gets the type of the ref based on the string | |||
func (repo *Repository) GetRefType(ref string) ObjectType { | |||
if repo.IsTagExist(ref) { | |||
return ObjectTag | |||
} else if repo.IsBranchExist(ref) { | |||
return ObjectBranch | |||
} else if repo.IsCommitExist(ref) { | |||
return ObjectCommit | |||
} else if _, err := repo.GetBlob(ref); err == nil { | |||
return ObjectBlob | |||
} | |||
return ObjectType("invalid") | |||
} |
@@ -5,26 +5,52 @@ | |||
package repofiles | |||
import ( | |||
"fmt" | |||
"net/url" | |||
"path" | |||
"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 | |||
func GetFileContents(repo *models.Repository, treePath, ref string) (*api.FileContentResponse, error) { | |||
// ContentType repo content type | |||
type ContentType string | |||
// The string representations of different content types | |||
const ( | |||
// ContentTypeRegular regular content type (file) | |||
ContentTypeRegular ContentType = "file" | |||
// ContentTypeDir dir content type (dir) | |||
ContentTypeDir ContentType = "dir" | |||
// ContentLink link content type (symlink) | |||
ContentTypeLink ContentType = "symlink" | |||
// ContentTag submodule content type (submodule) | |||
ContentTypeSubmodule ContentType = "submodule" | |||
) | |||
// String gets the string of ContentType | |||
func (ct *ContentType) String() string { | |||
return string(*ct) | |||
} | |||
// GetContentsOrList gets the meta data 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(repo *models.Repository, treePath, ref string) (interface{}, 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) | |||
if treePath == "" { | |||
cleanTreePath := CleanUploadFileName(treePath) | |||
if cleanTreePath == "" && treePath != "" { | |||
return nil, models.ErrFilenameInvalid{ | |||
Path: treePath, | |||
} | |||
} | |||
treePath = cleanTreePath | |||
gitRepo, err := git.OpenRepository(repo.RepoPath()) | |||
if err != nil { | |||
@@ -42,32 +68,145 @@ func GetFileContents(repo *models.Repository, treePath, ref string) (*api.FileCo | |||
return nil, err | |||
} | |||
urlRef := ref | |||
if _, err := gitRepo.GetBranchCommit(ref); err == nil { | |||
urlRef = "branch/" + ref | |||
if entry.Type() != "tree" { | |||
return GetContents(repo, treePath, origRef, false) | |||
} | |||
// We are in a directory, so we return a list of FileContentResponse objects | |||
var fileList []*api.ContentsResponse | |||
gitTree, err := commit.SubTree(treePath) | |||
if err != nil { | |||
return nil, err | |||
} | |||
entries, err := gitTree.ListEntries() | |||
if err != nil { | |||
return nil, err | |||
} | |||
for _, e := range entries { | |||
subTreePath := path.Join(treePath, e.Name()) | |||
fileContentResponse, err := GetContents(repo, subTreePath, origRef, true) | |||
if err != nil { | |||
return nil, err | |||
} | |||
fileList = append(fileList, fileContentResponse) | |||
} | |||
return fileList, nil | |||
} | |||
// GetContents gets the meta data on a file's contents. Ref can be a branch, commit or tag | |||
func GetContents(repo *models.Repository, treePath, ref string, forList bool) (*api.ContentsResponse, error) { | |||
if ref == "" { | |||
ref = repo.DefaultBranch | |||
} | |||
origRef := 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) | |||
// Check that the path given in opts.treePath is valid (not a git path) | |||
cleanTreePath := CleanUploadFileName(treePath) | |||
if cleanTreePath == "" && treePath != "" { | |||
return nil, models.ErrFilenameInvalid{ | |||
Path: treePath, | |||
} | |||
} | |||
treePath = cleanTreePath | |||
gitRepo, err := git.OpenRepository(repo.RepoPath()) | |||
if err != nil { | |||
return nil, err | |||
} | |||
fileContent := &api.FileContentResponse{ | |||
Name: entry.Name(), | |||
Path: treePath, | |||
SHA: entry.ID.String(), | |||
Size: entry.Size(), | |||
URL: selfURL.String(), | |||
HTMLURL: htmlURL.String(), | |||
GitURL: gitURL.String(), | |||
DownloadURL: downloadURL.String(), | |||
Type: entry.Type(), | |||
// Get the commit object for the ref | |||
commit, err := gitRepo.GetCommit(ref) | |||
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 | |||
} | |||
refType := gitRepo.GetRefType(ref) | |||
if refType == "invalid" { | |||
return nil, fmt.Errorf("no commit found for the ref [ref: %s]", ref) | |||
} | |||
selfURL, err := url.Parse(fmt.Sprintf("%s/contents/%s?ref=%s", repo.APIURL(), treePath, origRef)) | |||
if err != nil { | |||
return nil, err | |||
} | |||
selfURLString := selfURL.String() | |||
// All content types have these fields in populated | |||
contentsResponse := &api.ContentsResponse{ | |||
Name: entry.Name(), | |||
Path: treePath, | |||
SHA: entry.ID.String(), | |||
Size: entry.Size(), | |||
URL: &selfURLString, | |||
Links: &api.FileLinksResponse{ | |||
Self: selfURL.String(), | |||
GitURL: gitURL.String(), | |||
HTMLURL: htmlURL.String(), | |||
Self: &selfURLString, | |||
}, | |||
} | |||
return fileContent, nil | |||
// Now populate the rest of the ContentsResponse based on entry type | |||
if entry.IsRegular() { | |||
contentsResponse.Type = string(ContentTypeRegular) | |||
if blobResponse, err := GetBlobBySHA(repo, entry.ID.String()); err != nil { | |||
return nil, err | |||
} else if !forList { | |||
// We don't show the content if we are getting a list of FileContentResponses | |||
contentsResponse.Encoding = &blobResponse.Encoding | |||
contentsResponse.Content = &blobResponse.Content | |||
} | |||
} else if entry.IsDir() { | |||
contentsResponse.Type = string(ContentTypeDir) | |||
} else if entry.IsLink() { | |||
contentsResponse.Type = string(ContentTypeLink) | |||
// The target of a symlink file is the content of the file | |||
targetFromContent, err := entry.Blob().GetBlobContent() | |||
if err != nil { | |||
return nil, err | |||
} | |||
contentsResponse.Target = &targetFromContent | |||
} else if entry.IsSubModule() { | |||
contentsResponse.Type = string(ContentTypeSubmodule) | |||
submodule, err := commit.GetSubModule(treePath) | |||
if err != nil { | |||
return nil, err | |||
} | |||
contentsResponse.SubmoduleGitURL = &submodule.URL | |||
} | |||
// Handle links | |||
if entry.IsRegular() || entry.IsLink() { | |||
downloadURL, err := url.Parse(fmt.Sprintf("%s/raw/%s/%s/%s", repo.HTMLURL(), refType, ref, treePath)) | |||
if err != nil { | |||
return nil, err | |||
} | |||
downloadURLString := downloadURL.String() | |||
contentsResponse.DownloadURL = &downloadURLString | |||
} | |||
if !entry.IsSubModule() { | |||
htmlURL, err := url.Parse(fmt.Sprintf("%s/src/%s/%s/%s", repo.HTMLURL(), refType, ref, treePath)) | |||
if err != nil { | |||
return nil, err | |||
} | |||
htmlURLString := htmlURL.String() | |||
contentsResponse.HTMLURL = &htmlURLString | |||
contentsResponse.Links.HTMLURL = &htmlURLString | |||
gitURL, err := url.Parse(fmt.Sprintf("%s/git/blobs/%s", repo.APIURL(), entry.ID.String())) | |||
if err != nil { | |||
return nil, err | |||
} | |||
gitURLString := gitURL.String() | |||
contentsResponse.GitURL = &gitURLString | |||
contentsResponse.Links.GitURL = &gitURLString | |||
} | |||
return contentsResponse, nil | |||
} |
@@ -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" | |||
@@ -19,7 +19,36 @@ func TestMain(m *testing.M) { | |||
models.MainTest(m, filepath.Join("..", "..")) | |||
} | |||
func TestGetFileContents(t *testing.T) { | |||
func getExpectedReadmeContentsResponse() *api.ContentsResponse { | |||
treePath := "README.md" | |||
sha := "4b4851ad51df6a7d9f25c979345979eaeb5b349f" | |||
encoding := "base64" | |||
content := "IyByZXBvMQoKRGVzY3JpcHRpb24gZm9yIHJlcG8x" | |||
selfURL := "https://try.gitea.io/api/v1/repos/user2/repo1/contents/" + treePath + "?ref=master" | |||
htmlURL := "https://try.gitea.io/user2/repo1/src/branch/master/" + treePath | |||
gitURL := "https://try.gitea.io/api/v1/repos/user2/repo1/git/blobs/" + sha | |||
downloadURL := "https://try.gitea.io/user2/repo1/raw/branch/master/" + treePath | |||
return &api.ContentsResponse{ | |||
Name: treePath, | |||
Path: treePath, | |||
SHA: "4b4851ad51df6a7d9f25c979345979eaeb5b349f", | |||
Type: "file", | |||
Size: 30, | |||
Encoding: &encoding, | |||
Content: &content, | |||
URL: &selfURL, | |||
HTMLURL: &htmlURL, | |||
GitURL: &gitURL, | |||
DownloadURL: &downloadURL, | |||
Links: &api.FileLinksResponse{ | |||
Self: &selfURL, | |||
GitURL: &gitURL, | |||
HTMLURL: &htmlURL, | |||
}, | |||
} | |||
} | |||
func TestGetContents(t *testing.T) { | |||
models.PrepareTestEnv(t) | |||
ctx := test.MockContext(t, "user2/repo1") | |||
ctx.SetParams(":id", "1") | |||
@@ -30,37 +59,110 @@ func TestGetFileContents(t *testing.T) { | |||
treePath := "README.md" | |||
ref := ctx.Repo.Repository.DefaultBranch | |||
expectedFileContentResponse := &structs.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", | |||
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", | |||
GitURL: "https://try.gitea.io/api/v1/repos/user2/repo1/git/blobs/4b4851ad51df6a7d9f25c979345979eaeb5b349f", | |||
HTMLURL: "https://try.gitea.io/user2/repo1/blob/master/README.md", | |||
}, | |||
expectedContentsResponse := getExpectedReadmeContentsResponse() | |||
t.Run("Get README.md contents with GetContents()", func(t *testing.T) { | |||
fileContentResponse, err := GetContents(ctx.Repo.Repository, treePath, ref, false) | |||
assert.EqualValues(t, expectedContentsResponse, fileContentResponse) | |||
assert.Nil(t, err) | |||
}) | |||
t.Run("Get REAMDE.md contents with ref as empty string (should then use the repo's default branch) with GetContents()", func(t *testing.T) { | |||
fileContentResponse, err := GetContents(ctx.Repo.Repository, treePath, "", false) | |||
assert.EqualValues(t, expectedContentsResponse, fileContentResponse) | |||
assert.Nil(t, err) | |||
}) | |||
} | |||
func TestGetContentsOrListForDir(t *testing.T) { | |||
models.PrepareTestEnv(t) | |||
ctx := test.MockContext(t, "user2/repo1") | |||
ctx.SetParams(":id", "1") | |||
test.LoadRepo(t, ctx, 1) | |||
test.LoadRepoCommit(t, ctx) | |||
test.LoadUser(t, ctx, 2) | |||
test.LoadGitRepo(t, ctx) | |||
treePath := "" // root dir | |||
ref := ctx.Repo.Repository.DefaultBranch | |||
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 README.md contents", func(t *testing.T) { | |||
fileContentResponse, err := GetFileContents(ctx.Repo.Repository, treePath, ref) | |||
assert.EqualValues(t, expectedFileContentResponse, fileContentResponse) | |||
t.Run("Get root dir contents with GetContentsOrList()", func(t *testing.T) { | |||
fileContentResponse, err := GetContentsOrList(ctx.Repo.Repository, treePath, ref) | |||
assert.EqualValues(t, expectedContentsListResponse, fileContentResponse) | |||
assert.Nil(t, err) | |||
}) | |||
t.Run("Get root dir contents with ref as empty string (should then use the repo's default branch) with GetContentsOrList()", func(t *testing.T) { | |||
fileContentResponse, err := GetContentsOrList(ctx.Repo.Repository, treePath, "") | |||
assert.EqualValues(t, expectedContentsListResponse, fileContentResponse) | |||
assert.Nil(t, err) | |||
}) | |||
} | |||
func TestGetContentsOrListForFile(t *testing.T) { | |||
models.PrepareTestEnv(t) | |||
ctx := test.MockContext(t, "user2/repo1") | |||
ctx.SetParams(":id", "1") | |||
test.LoadRepo(t, ctx, 1) | |||
test.LoadRepoCommit(t, ctx) | |||
test.LoadUser(t, ctx, 2) | |||
test.LoadGitRepo(t, ctx) | |||
treePath := "README.md" | |||
ref := ctx.Repo.Repository.DefaultBranch | |||
expectedContentsResponse := getExpectedReadmeContentsResponse() | |||
t.Run("Get README.md contents with GetContentsOrList()", func(t *testing.T) { | |||
fileContentResponse, err := GetContentsOrList(ctx.Repo.Repository, treePath, ref) | |||
assert.EqualValues(t, expectedContentsResponse, fileContentResponse) | |||
assert.Nil(t, err) | |||
}) | |||
t.Run("Get REAMDE.md contents with ref as empty string (should then use the repo's default branch)", func(t *testing.T) { | |||
fileContentResponse, err := GetFileContents(ctx.Repo.Repository, treePath, "") | |||
assert.EqualValues(t, expectedFileContentResponse, fileContentResponse) | |||
t.Run("Get REAMDE.md contents with ref as empty string (should then use the repo's default branch) with GetContentsOrList()", func(t *testing.T) { | |||
fileContentResponse, err := GetContentsOrList(ctx.Repo.Repository, treePath, "") | |||
assert.EqualValues(t, expectedContentsResponse, fileContentResponse) | |||
assert.Nil(t, err) | |||
}) | |||
} | |||
func TestGetFileContentsErrors(t *testing.T) { | |||
func TestGetContentsErrors(t *testing.T) { | |||
models.PrepareTestEnv(t) | |||
ctx := test.MockContext(t, "user2/repo1") | |||
ctx.SetParams(":id", "1") | |||
test.LoadRepo(t, ctx, 1) | |||
test.LoadRepoCommit(t, ctx) | |||
test.LoadUser(t, ctx, 2) | |||
test.LoadGitRepo(t, ctx) | |||
repo := ctx.Repo.Repository | |||
treePath := "README.md" | |||
ref := repo.DefaultBranch | |||
t.Run("bad treePath", func(t *testing.T) { | |||
badTreePath := "bad/tree.md" | |||
fileContentResponse, err := GetContents(repo, badTreePath, ref, false) | |||
assert.Error(t, err) | |||
assert.EqualError(t, err, "object does not exist [id: , rel_path: bad]") | |||
assert.Nil(t, fileContentResponse) | |||
}) | |||
t.Run("bad ref", func(t *testing.T) { | |||
badRef := "bad_ref" | |||
fileContentResponse, err := GetContents(repo, treePath, badRef, false) | |||
assert.Error(t, err) | |||
assert.EqualError(t, err, "object does not exist [id: "+badRef+", rel_path: ]") | |||
assert.Nil(t, fileContentResponse) | |||
}) | |||
} | |||
func TestGetContentsOrListErrors(t *testing.T) { | |||
models.PrepareTestEnv(t) | |||
ctx := test.MockContext(t, "user2/repo1") | |||
ctx.SetParams(":id", "1") | |||
@@ -74,7 +176,7 @@ func TestGetFileContentsErrors(t *testing.T) { | |||
t.Run("bad treePath", func(t *testing.T) { | |||
badTreePath := "bad/tree.md" | |||
fileContentResponse, err := GetFileContents(repo, badTreePath, ref) | |||
fileContentResponse, err := GetContentsOrList(repo, badTreePath, ref) | |||
assert.Error(t, err) | |||
assert.EqualError(t, err, "object does not exist [id: , rel_path: bad]") | |||
assert.Nil(t, fileContentResponse) | |||
@@ -82,7 +184,7 @@ func TestGetFileContentsErrors(t *testing.T) { | |||
t.Run("bad ref", func(t *testing.T) { | |||
badRef := "bad_ref" | |||
fileContentResponse, err := GetFileContents(repo, treePath, badRef) | |||
fileContentResponse, err := GetContentsOrList(repo, treePath, badRef) | |||
assert.Error(t, err) | |||
assert.EqualError(t, err, "object does not exist [id: "+badRef+", rel_path: ]") | |||
assert.Nil(t, fileContentResponse) |
@@ -17,8 +17,8 @@ import ( | |||
// GetFileResponseFromCommit Constructs a FileResponse from a Commit object | |||
func GetFileResponseFromCommit(repo *models.Repository, commit *git.Commit, branch, treeName string) (*api.FileResponse, error) { | |||
fileContents, _ := GetFileContents(repo, treeName, branch) // ok if fails, then will be nil | |||
fileCommitResponse, _ := GetFileCommitResponse(repo, commit) // ok if fails, then will be nil | |||
fileContents, _ := GetContents(repo, treeName, branch, false) // ok if fails, then will be nil | |||
fileCommitResponse, _ := GetFileCommitResponse(repo, commit) // ok if fails, then will be nil | |||
verification := GetPayloadCommitVerification(commit) | |||
fileResponse := &api.FileResponse{ | |||
Content: fileContents, |
@@ -5,6 +5,7 @@ | |||
package repofiles | |||
import ( | |||
"code.gitea.io/gitea/modules/setting" | |||
"testing" | |||
"code.gitea.io/gitea/models" | |||
@@ -16,21 +17,31 @@ import ( | |||
) | |||
func getExpectedFileResponse() *api.FileResponse { | |||
treePath := "README.md" | |||
sha := "4b4851ad51df6a7d9f25c979345979eaeb5b349f" | |||
encoding := "base64" | |||
content := "IyByZXBvMQoKRGVzY3JpcHRpb24gZm9yIHJlcG8x" | |||
selfURL := 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 | |||
return &api.FileResponse{ | |||
Content: &api.FileContentResponse{ | |||
Name: "README.md", | |||
Path: "README.md", | |||
SHA: "4b4851ad51df6a7d9f25c979345979eaeb5b349f", | |||
Content: &api.ContentsResponse{ | |||
Name: treePath, | |||
Path: treePath, | |||
SHA: sha, | |||
Type: "file", | |||
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", | |||
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", | |||
Encoding: &encoding, | |||
Content: &content, | |||
URL: &selfURL, | |||
HTMLURL: &htmlURL, | |||
GitURL: &gitURL, | |||
DownloadURL: &downloadURL, | |||
Links: &api.FileLinksResponse{ | |||
Self: "https://try.gitea.io/api/v1/repos/user2/repo1/contents/README.md", | |||
GitURL: "https://try.gitea.io/api/v1/repos/user2/repo1/git/blobs/4b4851ad51df6a7d9f25c979345979eaeb5b349f", | |||
HTMLURL: "https://try.gitea.io/user2/repo1/blob/master/README.md", | |||
Self: &selfURL, | |||
GitURL: &gitURL, | |||
HTMLURL: &htmlURL, | |||
}, | |||
}, | |||
Commit: &api.FileCommitResponse{ |
@@ -49,23 +49,32 @@ type UpdateFileOptions struct { | |||
// FileLinksResponse contains the links for a repo's file | |||
type FileLinksResponse struct { | |||
Self string `json:"url"` | |||
GitURL string `json:"git_url"` | |||
HTMLURL string `json:"html_url"` | |||
Self *string `json:"self"` | |||
GitURL *string `json:"git"` | |||
HTMLURL *string `json:"html"` | |||
} | |||
// FileContentResponse contains information about a repo's file stats and content | |||
type FileContentResponse struct { | |||
Name string `json:"name"` | |||
Path string `json:"path"` | |||
SHA string `json:"sha"` | |||
Size int64 `json:"size"` | |||
URL string `json:"url"` | |||
HTMLURL string `json:"html_url"` | |||
GitURL string `json:"git_url"` | |||
DownloadURL string `json:"download_url"` | |||
Type string `json:"type"` | |||
Links *FileLinksResponse `json:"_links"` | |||
// ContentsResponse contains information about a repo's entry's (dir, file, symlink, submodule) metadata and content | |||
type ContentsResponse struct { | |||
Name string `json:"name"` | |||
Path string `json:"path"` | |||
SHA string `json:"sha"` | |||
// `type` will be `file`, `dir`, `symlink`, or `submodule` | |||
Type string `json:"type"` | |||
Size int64 `json:"size"` | |||
// `encoding` is populated when `type` is `file`, otherwise null | |||
Encoding *string `json:"encoding"` | |||
// `content` is populated when `type` is `file`, otherwise null | |||
Content *string `json:"content"` | |||
// `target` is populated when `type` is `symlink`, otherwise null | |||
Target *string `json:"target"` | |||
URL *string `json:"url"` | |||
HTMLURL *string `json:"html_url"` | |||
GitURL *string `json:"git_url"` | |||
DownloadURL *string `json:"download_url"` | |||
// `submodule_git_url` is populated when `type` is `submodule`, otherwise null | |||
SubmoduleGitURL *string `json:"submodule_git_url"` | |||
Links *FileLinksResponse `json:"_links"` | |||
} | |||
// FileCommitResponse contains information generated from a Git commit for a repo's file. | |||
@@ -81,7 +90,7 @@ type FileCommitResponse struct { | |||
// FileResponse contains information about a repo's file | |||
type FileResponse struct { | |||
Content *FileContentResponse `json:"content"` | |||
Content *ContentsResponse `json:"content"` | |||
Commit *FileCommitResponse `json:"commit"` | |||
Verification *PayloadCommitVerification `json:"verification"` | |||
} |
@@ -766,7 +766,8 @@ func RegisterRoutes(m *macaron.Macaron) { | |||
m.Get("/tags/:sha", context.RepoRef(), repo.GetTag) | |||
}, reqRepoReader(models.UnitTypeCode)) | |||
m.Group("/contents", func() { | |||
m.Get("/*", repo.GetFileContents) | |||
m.Get("", repo.GetContentsList) | |||
m.Get("/*", repo.GetContents) | |||
m.Group("/*", func() { | |||
m.Post("", bind(api.CreateFileOptions{}), repo.CreateFile) | |||
m.Put("", bind(api.UpdateFileOptions{}), repo.UpdateFile) |
@@ -366,11 +366,11 @@ func DeleteFile(ctx *context.APIContext, apiOpts api.DeleteFileOptions) { | |||
} | |||
} | |||
// GetFileContents Get the contents of a fle in a repository | |||
func GetFileContents(ctx *context.APIContext) { | |||
// swagger:operation GET /repos/{owner}/{repo}/contents/{filepath} repository repoGetFileContents | |||
// GetContents Get the metadata and contents (if a file) of an entry in a repository, or a list of entries if a dir | |||
func GetContents(ctx *context.APIContext) { | |||
// swagger:operation GET /repos/{owner}/{repo}/contents/{filepath} repository repoGetContents | |||
// --- | |||
// summary: Gets the contents of a file or directory in a repository | |||
// summary: Gets the metadata and contents (if a file) of an entry in a repository, or a list of entries if a dir | |||
// produces: | |||
// - application/json | |||
// parameters: | |||
@@ -386,20 +386,20 @@ func GetFileContents(ctx *context.APIContext) { | |||
// required: true | |||
// - name: filepath | |||
// in: path | |||
// description: path of the file to delete | |||
// description: path of the dir, file, symlink or submodule in the repo | |||
// type: string | |||
// required: true | |||
// - name: ref | |||
// in: query | |||
// description: "The name of the commit/branch/tag. Default the repository’s default branch (usually master)" | |||
// required: false | |||
// type: string | |||
// required: false | |||
// responses: | |||
// "200": | |||
// "$ref": "#/responses/FileContentResponse" | |||
// "$ref": "#/responses/ContentsResponse" | |||
if !CanReadFiles(ctx.Repo) { | |||
ctx.Error(http.StatusInternalServerError, "GetFileContents", models.ErrUserDoesNotHaveAccessToRepo{ | |||
ctx.Error(http.StatusInternalServerError, "GetContentsOrList", models.ErrUserDoesNotHaveAccessToRepo{ | |||
UserID: ctx.User.ID, | |||
RepoName: ctx.Repo.Repository.LowerName, | |||
}) | |||
@@ -409,9 +409,40 @@ func GetFileContents(ctx *context.APIContext) { | |||
treePath := ctx.Params("*") | |||
ref := ctx.QueryTrim("ref") | |||
if fileContents, err := repofiles.GetFileContents(ctx.Repo.Repository, treePath, ref); err != nil { | |||
ctx.Error(http.StatusInternalServerError, "GetFileContents", err) | |||
if fileList, err := repofiles.GetContentsOrList(ctx.Repo.Repository, treePath, ref); err != nil { | |||
ctx.Error(http.StatusInternalServerError, "GetContentsOrList", err) | |||
} else { | |||
ctx.JSON(http.StatusOK, fileContents) | |||
ctx.JSON(http.StatusOK, fileList) | |||
} | |||
} | |||
// GetContentsList Get the metadata of all the entries of the root dir | |||
func GetContentsList(ctx *context.APIContext) { | |||
// swagger:operation GET /repos/{owner}/{repo}/contents repository repoGetContentsList | |||
// --- | |||
// summary: Gets the metadata of all the entries of the root dir | |||
// produces: | |||
// - application/json | |||
// parameters: | |||
// - name: owner | |||
// in: path | |||
// description: owner of the repo | |||
// type: string | |||
// required: true | |||
// - name: repo | |||
// in: path | |||
// description: name of the repo | |||
// type: string | |||
// required: true | |||
// - name: ref | |||
// in: query | |||
// description: "The name of the commit/branch/tag. Default the repository’s default branch (usually master)" | |||
// type: string | |||
// required: false | |||
// responses: | |||
// "200": | |||
// "$ref": "#/responses/ContentsListResponse" | |||
// same as GetContents(), this function is here because swagger fails if path is empty in GetContents() interface | |||
GetContents(ctx) | |||
} |
@@ -197,11 +197,18 @@ type swaggerFileResponse struct { | |||
Body api.FileResponse `json:"body"` | |||
} | |||
// FileContentResponse | |||
// swagger:response FileContentResponse | |||
type swaggerFileContentResponse struct { | |||
// ContentsResponse | |||
// swagger:response ContentsResponse | |||
type swaggerContentsResponse struct { | |||
//in: body | |||
Body api.FileContentResponse `json:"body"` | |||
Body api.ContentsResponse `json:"body"` | |||
} | |||
// ContentsListResponse | |||
// swagger:response ContentsListResponse | |||
type swaggerContentsListResponse struct { | |||
// in:body | |||
Body []api.ContentsResponse `json:"body"` | |||
} | |||
// FileDeleteResponse |
@@ -1570,6 +1570,45 @@ | |||
} | |||
} | |||
}, | |||
"/repos/{owner}/{repo}/contents": { | |||
"get": { | |||
"produces": [ | |||
"application/json" | |||
], | |||
"tags": [ | |||
"repository" | |||
], | |||
"summary": "Gets the metadata of all the entries of the root dir", | |||
"operationId": "repoGetContentsList", | |||
"parameters": [ | |||
{ | |||
"type": "string", | |||
"description": "owner of the repo", | |||
"name": "owner", | |||
"in": "path", | |||
"required": true | |||
}, | |||
{ | |||
"type": "string", | |||
"description": "name of the repo", | |||
"name": "repo", | |||
"in": "path", | |||
"required": true | |||
}, | |||
{ | |||
"type": "string", | |||
"description": "The name of the commit/branch/tag. Default the repository’s default branch (usually master)", | |||
"name": "ref", | |||
"in": "query" | |||
} | |||
], | |||
"responses": { | |||
"200": { | |||
"$ref": "#/responses/ContentsListResponse" | |||
} | |||
} | |||
} | |||
}, | |||
"/repos/{owner}/{repo}/contents/{filepath}": { | |||
"get": { | |||
"produces": [ | |||
@@ -1578,8 +1617,8 @@ | |||
"tags": [ | |||
"repository" | |||
], | |||
"summary": "Gets the contents of a file or directory in a repository", | |||
"operationId": "repoGetFileContents", | |||
"summary": "Gets the metadata and contents (if a file) of an entry in a repository, or a list of entries if a dir", | |||
"operationId": "repoGetContents", | |||
"parameters": [ | |||
{ | |||
"type": "string", | |||
@@ -1597,7 +1636,7 @@ | |||
}, | |||
{ | |||
"type": "string", | |||
"description": "path of the file to delete", | |||
"description": "path of the dir, file, symlink or submodule in the repo", | |||
"name": "filepath", | |||
"in": "path", | |||
"required": true | |||
@@ -1611,7 +1650,7 @@ | |||
], | |||
"responses": { | |||
"200": { | |||
"$ref": "#/responses/FileContentResponse" | |||
"$ref": "#/responses/ContentsResponse" | |||
} | |||
} | |||
}, | |||
@@ -7017,6 +7056,74 @@ | |||
}, | |||
"x-go-package": "code.gitea.io/gitea/modules/structs" | |||
}, | |||
"ContentsResponse": { | |||
"description": "ContentsResponse contains information about a repo's entry's (dir, file, symlink, submodule) metadata and content", | |||
"type": "object", | |||
"properties": { | |||
"_links": { | |||
"$ref": "#/definitions/FileLinksResponse" | |||
}, | |||
"content": { | |||
"description": "`content` is populated when `type` is `file`, otherwise null", | |||
"type": "string", | |||
"x-go-name": "Content" | |||
}, | |||
"download_url": { | |||
"type": "string", | |||
"x-go-name": "DownloadURL" | |||
}, | |||
"encoding": { | |||
"description": "`encoding` is populated when `type` is `file`, otherwise null", | |||
"type": "string", | |||
"x-go-name": "Encoding" | |||
}, | |||
"git_url": { | |||
"type": "string", | |||
"x-go-name": "GitURL" | |||
}, | |||
"html_url": { | |||
"type": "string", | |||
"x-go-name": "HTMLURL" | |||
}, | |||
"name": { | |||
"type": "string", | |||
"x-go-name": "Name" | |||
}, | |||
"path": { | |||
"type": "string", | |||
"x-go-name": "Path" | |||
}, | |||
"sha": { | |||
"type": "string", | |||
"x-go-name": "SHA" | |||
}, | |||
"size": { | |||
"type": "integer", | |||
"format": "int64", | |||
"x-go-name": "Size" | |||
}, | |||
"submodule_git_url": { | |||
"description": "`submodule_git_url` is populated when `type` is `submodule`, otherwise null", | |||
"type": "string", | |||
"x-go-name": "SubmoduleGitURL" | |||
}, | |||
"target": { | |||
"description": "`target` is populated when `type` is `symlink`, otherwise null", | |||
"type": "string", | |||
"x-go-name": "Target" | |||
}, | |||
"type": { | |||
"description": "`type` will be `file`, `dir`, `symlink`, or `submodule`", | |||
"type": "string", | |||
"x-go-name": "Type" | |||
}, | |||
"url": { | |||
"type": "string", | |||
"x-go-name": "URL" | |||
} | |||
}, | |||
"x-go-package": "code.gitea.io/gitea/modules/structs" | |||
}, | |||
"CreateEmailOption": { | |||
"description": "CreateEmailOption options when creating email addresses", | |||
"type": "object", | |||
@@ -8179,53 +8286,6 @@ | |||
}, | |||
"x-go-package": "code.gitea.io/gitea/modules/structs" | |||
}, | |||
"FileContentResponse": { | |||
"description": "FileContentResponse contains information about a repo's file stats and content", | |||
"type": "object", | |||
"properties": { | |||
"_links": { | |||
"$ref": "#/definitions/FileLinksResponse" | |||
}, | |||
"download_url": { | |||
"type": "string", | |||
"x-go-name": "DownloadURL" | |||
}, | |||
"git_url": { | |||
"type": "string", | |||
"x-go-name": "GitURL" | |||
}, | |||
"html_url": { | |||
"type": "string", | |||
"x-go-name": "HTMLURL" | |||
}, | |||
"name": { | |||
"type": "string", | |||
"x-go-name": "Name" | |||
}, | |||
"path": { | |||
"type": "string", | |||
"x-go-name": "Path" | |||
}, | |||
"sha": { | |||
"type": "string", | |||
"x-go-name": "SHA" | |||
}, | |||
"size": { | |||
"type": "integer", | |||
"format": "int64", | |||
"x-go-name": "Size" | |||
}, | |||
"type": { | |||
"type": "string", | |||
"x-go-name": "Type" | |||
}, | |||
"url": { | |||
"type": "string", | |||
"x-go-name": "URL" | |||
} | |||
}, | |||
"x-go-package": "code.gitea.io/gitea/modules/structs" | |||
}, | |||
"FileDeleteResponse": { | |||
"description": "FileDeleteResponse contains information about a repo's file that was deleted", | |||
"type": "object", | |||
@@ -8247,15 +8307,15 @@ | |||
"description": "FileLinksResponse contains the links for a repo's file", | |||
"type": "object", | |||
"properties": { | |||
"git_url": { | |||
"git": { | |||
"type": "string", | |||
"x-go-name": "GitURL" | |||
}, | |||
"html_url": { | |||
"html": { | |||
"type": "string", | |||
"x-go-name": "HTMLURL" | |||
}, | |||
"url": { | |||
"self": { | |||
"type": "string", | |||
"x-go-name": "Self" | |||
} | |||
@@ -8270,7 +8330,7 @@ | |||
"$ref": "#/definitions/FileCommitResponse" | |||
}, | |||
"content": { | |||
"$ref": "#/definitions/FileContentResponse" | |||
"$ref": "#/definitions/ContentsResponse" | |||
}, | |||
"verification": { | |||
"$ref": "#/definitions/PayloadCommitVerification" | |||
@@ -9898,6 +9958,21 @@ | |||
"$ref": "#/definitions/Commit" | |||
} | |||
}, | |||
"ContentsListResponse": { | |||
"description": "ContentsListResponse", | |||
"schema": { | |||
"type": "array", | |||
"items": { | |||
"$ref": "#/definitions/ContentsResponse" | |||
} | |||
} | |||
}, | |||
"ContentsResponse": { | |||
"description": "ContentsResponse", | |||
"schema": { | |||
"$ref": "#/definitions/ContentsResponse" | |||
} | |||
}, | |||
"DeployKey": { | |||
"description": "DeployKey", | |||
"schema": { | |||
@@ -9922,12 +9997,6 @@ | |||
} | |||
} | |||
}, | |||
"FileContentResponse": { | |||
"description": "FileContentResponse", | |||
"schema": { | |||
"$ref": "#/definitions/FileContentResponse" | |||
} | |||
}, | |||
"FileDeleteResponse": { | |||
"description": "FileDeleteResponse", | |||
"schema": { |