| @@ -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": { | |||