parent
							
								
									738285a4aa
								
							
						
					
					
						commit
						cd96dee982
					
				
					 17 changed files with 963 additions and 335 deletions
				
			
		|  | @ -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() | ||||
|  |  | |||
							
								
								
									
										156
									
								
								integrations/api_repo_get_contents_list_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										156
									
								
								integrations/api_repo_get_contents_list_test.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -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) | ||||
| } | ||||
							
								
								
									
										157
									
								
								integrations/api_repo_get_contents_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										157
									
								
								integrations/api_repo_get_contents_test.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -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) | ||||
| 	} | ||||
| 
 | ||||
| 	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) | ||||
| 	// We are in a directory, so we return a list of FileContentResponse objects
 | ||||
| 	var fileList []*api.ContentsResponse | ||||
| 
 | ||||
| 	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(), | ||||
| 	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 | ||||
| 
 | ||||
| 	// 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 | ||||
| 	} | ||||
| 
 | ||||
| 	// 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,81 @@ 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", func(t *testing.T) { | ||||
| 		fileContentResponse, err := GetFileContents(ctx.Repo.Repository, treePath, ref) | ||||
| 		assert.EqualValues(t, expectedFileContentResponse, fileContentResponse) | ||||
| 	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)", 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 GetContents()", func(t *testing.T) { | ||||
| 		fileContentResponse, err := GetContents(ctx.Repo.Repository, treePath, "", false) | ||||
| 		assert.EqualValues(t, expectedContentsResponse, fileContentResponse) | ||||
| 		assert.Nil(t, err) | ||||
| 	}) | ||||
| } | ||||
| 
 | ||||
| func TestGetFileContentsErrors(t *testing.T) { | ||||
| 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 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) with GetContentsOrList()", func(t *testing.T) { | ||||
| 		fileContentResponse, err := GetContentsOrList(ctx.Repo.Repository, treePath, "") | ||||
| 		assert.EqualValues(t, expectedContentsResponse, fileContentResponse) | ||||
| 		assert.Nil(t, err) | ||||
| 	}) | ||||
| } | ||||
| 
 | ||||
| func TestGetContentsErrors(t *testing.T) { | ||||
| 	models.PrepareTestEnv(t) | ||||
| 	ctx := test.MockContext(t, "user2/repo1") | ||||
| 	ctx.SetParams(":id", "1") | ||||
|  | @ -74,7 +147,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 := 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) | ||||
|  | @ -82,7 +155,36 @@ func TestGetFileContentsErrors(t *testing.T) { | |||
| 
 | ||||
| 	t.Run("bad ref", func(t *testing.T) { | ||||
| 		badRef := "bad_ref" | ||||
| 		fileContentResponse, err := GetFileContents(repo, treePath, badRef) | ||||
| 		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") | ||||
| 	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 := GetContentsOrList(repo, badTreePath, ref) | ||||
| 		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 := 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,7 +1570,7 @@ | |||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "/repos/{owner}/{repo}/contents/{filepath}": { | ||||
|     "/repos/{owner}/{repo}/contents": { | ||||
|       "get": { | ||||
|         "produces": [ | ||||
|           "application/json" | ||||
|  | @ -1578,8 +1578,8 @@ | |||
|         "tags": [ | ||||
|           "repository" | ||||
|         ], | ||||
|         "summary": "Gets the contents of a file or directory in a repository", | ||||
|         "operationId": "repoGetFileContents", | ||||
|         "summary": "Gets the metadata of all the entries of the root dir", | ||||
|         "operationId": "repoGetContentsList", | ||||
|         "parameters": [ | ||||
|           { | ||||
|             "type": "string", | ||||
|  | @ -1597,7 +1597,46 @@ | |||
|           }, | ||||
|           { | ||||
|             "type": "string", | ||||
|             "description": "path of the file to delete", | ||||
|             "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": [ | ||||
|           "application/json" | ||||
|         ], | ||||
|         "tags": [ | ||||
|           "repository" | ||||
|         ], | ||||
|         "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", | ||||
|             "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": "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": { | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue