From 9d20f6f3e4dd83587de867ec02f9cea94a52236a Mon Sep 17 00:00:00 2001 From: rickyma Date: Mon, 30 Jun 2025 11:16:24 +0800 Subject: [PATCH 1/6] Support getting lastCommitMessage using contents-ext API --- modules/structs/repo_file.go | 3 ++- services/repository/files/content.go | 1 + services/repository/files/content_test.go | 1 + templates/swagger/v1_json.tmpl | 4 ++++ tests/integration/api_repo_file_create_test.go | 6 ++++-- tests/integration/api_repo_get_contents_list_test.go | 1 + tests/integration/api_repo_get_contents_test.go | 1 + 7 files changed, 14 insertions(+), 3 deletions(-) diff --git a/modules/structs/repo_file.go b/modules/structs/repo_file.go index 91ee060d500ac..8417ee9f5e9a6 100644 --- a/modules/structs/repo_file.go +++ b/modules/structs/repo_file.go @@ -123,7 +123,8 @@ type ContentsResponse struct { // swagger:strfmt date-time LastCommitterDate time.Time `json:"last_committer_date"` // swagger:strfmt date-time - LastAuthorDate time.Time `json:"last_author_date"` + LastAuthorDate time.Time `json:"last_author_date"` + LastCommitMessage string `json:"last_commit_message"` // `type` will be `file`, `dir`, `symlink`, or `submodule` Type string `json:"type"` Size int64 `json:"size"` diff --git a/services/repository/files/content.go b/services/repository/files/content.go index beef38169492d..19816f275eff3 100644 --- a/services/repository/files/content.go +++ b/services/repository/files/content.go @@ -163,6 +163,7 @@ func getFileContentsByEntryInternal(_ context.Context, repo *repo_model.Reposito if lastCommit.Author != nil { contentsResponse.LastAuthorDate = lastCommit.Author.When } + contentsResponse.LastCommitMessage = lastCommit.Message() // Now populate the rest of the ContentsResponse based on entry type if entry.IsRegular() || entry.IsExecutable() { diff --git a/services/repository/files/content_test.go b/services/repository/files/content_test.go index 9357c52ea8210..59440fb0f4110 100644 --- a/services/repository/files/content_test.go +++ b/services/repository/files/content_test.go @@ -39,6 +39,7 @@ func getExpectedReadmeContentsResponse() *api.ContentsResponse { LastCommitSHA: "65f1bf27bc3bf70f64657658635e66094edbcb4d", LastCommitterDate: time.Date(2017, time.March, 19, 16, 47, 59, 0, time.FixedZone("", -14400)), LastAuthorDate: time.Date(2017, time.March, 19, 16, 47, 59, 0, time.FixedZone("", -14400)), + LastCommitMessage: "Initial commit", Type: "file", Size: 30, Encoding: &encoding, diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index ff66bebfda68c..85dd536e9d396 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -22368,6 +22368,10 @@ "format": "date-time", "x-go-name": "LastAuthorDate" }, + "last_commit_message": { + "type": "string", + "x-go-name": "LastCommitMessage" + }, "last_commit_sha": { "type": "string", "x-go-name": "LastCommitSHA" diff --git a/tests/integration/api_repo_file_create_test.go b/tests/integration/api_repo_file_create_test.go index df0fc3dd0563f..f4fbcd164300d 100644 --- a/tests/integration/api_repo_file_create_test.go +++ b/tests/integration/api_repo_file_create_test.go @@ -57,8 +57,8 @@ func normalizeFileContentResponseCommitTime(c *api.ContentsResponse) { } type apiFileResponseInfo struct { - repoFullName, commitID, treePath, lastCommitSHA string - lastCommitterWhen, lastAuthorWhen time.Time + repoFullName, commitID, treePath, lastCommitSHA, lastCommitMessage string + lastCommitterWhen, lastAuthorWhen time.Time } func getExpectedFileResponseForCreate(info apiFileResponseInfo) *api.FileResponse { @@ -77,6 +77,7 @@ func getExpectedFileResponseForCreate(info apiFileResponseInfo) *api.FileRespons LastCommitSHA: info.lastCommitSHA, LastCommitterDate: info.lastCommitterWhen, LastAuthorDate: info.lastAuthorWhen, + LastCommitMessage: info.lastCommitMessage, Size: 16, Type: "file", Encoding: &encoding, @@ -193,6 +194,7 @@ func TestAPICreateFile(t *testing.T) { lastCommitSHA: lastCommit.ID.String(), lastCommitterWhen: lastCommit.Committer.When, lastAuthorWhen: lastCommit.Author.When, + lastCommitMessage: lastCommit.Message(), }) var fileResponse api.FileResponse DecodeJSON(t, resp, &fileResponse) diff --git a/tests/integration/api_repo_get_contents_list_test.go b/tests/integration/api_repo_get_contents_list_test.go index 9b192c63040c5..0a97de21fc42f 100644 --- a/tests/integration/api_repo_get_contents_list_test.go +++ b/tests/integration/api_repo_get_contents_list_test.go @@ -38,6 +38,7 @@ func getExpectedContentsListResponseForContents(ref, refType, lastCommitSHA stri LastCommitSHA: lastCommitSHA, LastCommitterDate: time.Date(2017, time.March, 19, 16, 47, 59, 0, time.FixedZone("", -14400)), LastAuthorDate: time.Date(2017, time.March, 19, 16, 47, 59, 0, time.FixedZone("", -14400)), + LastCommitMessage: "Initial commit", Type: "file", Size: 30, URL: &selfURL, diff --git a/tests/integration/api_repo_get_contents_test.go b/tests/integration/api_repo_get_contents_test.go index 9517db4c87cee..f32b376138ef9 100644 --- a/tests/integration/api_repo_get_contents_test.go +++ b/tests/integration/api_repo_get_contents_test.go @@ -38,6 +38,7 @@ func getExpectedContentsResponseForContents(ref, refType, lastCommitSHA string) LastCommitSHA: lastCommitSHA, LastCommitterDate: time.Date(2017, time.March, 19, 16, 47, 59, 0, time.FixedZone("", -14400)), LastAuthorDate: time.Date(2017, time.March, 19, 16, 47, 59, 0, time.FixedZone("", -14400)), + LastCommitMessage: "Initial commit", Type: "file", Size: 30, Encoding: util.ToPointer("base64"), From 99d6934f394d15e6dcf024ad85d6f452adce567c Mon Sep 17 00:00:00 2001 From: rickyma Date: Mon, 30 Jun 2025 14:42:47 +0800 Subject: [PATCH 2/6] Fix test --- services/repository/files/content_test.go | 2 +- tests/integration/repofiles_change_test.go | 29 ++++++++++++---------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/services/repository/files/content_test.go b/services/repository/files/content_test.go index 59440fb0f4110..ef0a4d96189c3 100644 --- a/services/repository/files/content_test.go +++ b/services/repository/files/content_test.go @@ -39,7 +39,7 @@ func getExpectedReadmeContentsResponse() *api.ContentsResponse { LastCommitSHA: "65f1bf27bc3bf70f64657658635e66094edbcb4d", LastCommitterDate: time.Date(2017, time.March, 19, 16, 47, 59, 0, time.FixedZone("", -14400)), LastAuthorDate: time.Date(2017, time.March, 19, 16, 47, 59, 0, time.FixedZone("", -14400)), - LastCommitMessage: "Initial commit", + LastCommitMessage: "Initial commit\n", Type: "file", Size: 30, Encoding: &encoding, diff --git a/tests/integration/repofiles_change_test.go b/tests/integration/repofiles_change_test.go index b63d06a866932..6e7f2b0a0bafb 100644 --- a/tests/integration/repofiles_change_test.go +++ b/tests/integration/repofiles_change_test.go @@ -158,6 +158,7 @@ func getExpectedFileResponseForRepoFilesCreate(commitID string, lastCommit *git. LastCommitSHA: lastCommit.ID.String(), LastCommitterDate: lastCommit.Committer.When, LastAuthorDate: lastCommit.Author.When, + LastCommitMessage: "Creates new/file.txt\n", Type: "file", Size: 18, Encoding: &encoding, @@ -198,7 +199,7 @@ func getExpectedFileResponseForRepoFilesCreate(commitID string, lastCommit *git. SHA: "65f1bf27bc3bf70f64657658635e66094edbcb4d", }, }, - Message: "Updates README.md\n", + Message: "Creates new/file.txt\n", Tree: &api.CommitMeta{ URL: setting.AppURL + "api/v1/repos/user2/repo1/git/trees/f93e3a1a1525fb5b91020da86e44810c87a2d7bc", SHA: "f93e3a1a1525fb5b91020git dda86e44810c87a2d7bc", @@ -228,6 +229,7 @@ func getExpectedFileResponseForRepoFilesUpdate(commitID, filename, lastCommitSHA LastCommitSHA: lastCommitSHA, LastCommitterDate: lastCommitterWhen, LastAuthorDate: lastAuthorWhen, + LastCommitMessage: "Updates README.md\n", Type: "file", Size: 43, Encoding: &encoding, @@ -328,18 +330,19 @@ func getExpectedFileResponseForRepoFilesUpdateRename(commitID, lastCommitSHA str downloadURL := setting.AppURL + "user2/lfs/raw/branch/master/" + detail.filename // don't set time related fields because there might be different time in one operation responses = append(responses, &api.ContentsResponse{ - Name: detail.filename, - Path: detail.filename, - SHA: detail.sha, - LastCommitSHA: lastCommitSHA, - Type: "file", - Size: detail.size, - Encoding: util.ToPointer("base64"), - Content: &detail.content, - URL: &selfURL, - HTMLURL: &htmlURL, - GitURL: &gitURL, - DownloadURL: &downloadURL, + Name: detail.filename, + Path: detail.filename, + SHA: detail.sha, + LastCommitSHA: lastCommitSHA, + LastCommitMessage: "Rename files\n", + Type: "file", + Size: detail.size, + Encoding: util.ToPointer("base64"), + Content: &detail.content, + URL: &selfURL, + HTMLURL: &htmlURL, + GitURL: &gitURL, + DownloadURL: &downloadURL, Links: &api.FileLinksResponse{ Self: &selfURL, GitURL: &gitURL, From 7a13e5a58a4712009b03e962d72c98da0a5622a3 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Wed, 2 Jul 2025 19:15:04 +0800 Subject: [PATCH 3/6] fix --- modules/structs/repo_file.go | 20 ++++--- routers/api/v1/repo/file.go | 15 ++++- services/repository/files/content.go | 58 +++++++++++-------- services/repository/files/content_test.go | 8 +-- templates/swagger/v1_json.tmpl | 5 +- .../integration/api_repo_file_create_test.go | 13 +++-- .../integration/api_repo_file_update_test.go | 7 ++- .../api_repo_get_contents_list_test.go | 9 +-- .../integration/api_repo_get_contents_test.go | 17 ++++-- tests/integration/repofiles_change_test.go | 22 +++---- 10 files changed, 101 insertions(+), 73 deletions(-) diff --git a/modules/structs/repo_file.go b/modules/structs/repo_file.go index 8417ee9f5e9a6..5a86db868b79d 100644 --- a/modules/structs/repo_file.go +++ b/modules/structs/repo_file.go @@ -116,15 +116,17 @@ type ContentsExtResponse struct { // 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"` - LastCommitSHA string `json:"last_commit_sha"` + Name string `json:"name"` + Path string `json:"path"` + SHA string `json:"sha"` + + LastCommitSHA *string `json:"last_commit_sha,omitempty"` // swagger:strfmt date-time - LastCommitterDate time.Time `json:"last_committer_date"` + LastCommitterDate *time.Time `json:"last_committer_date,omitempty"` // swagger:strfmt date-time - LastAuthorDate time.Time `json:"last_author_date"` - LastCommitMessage string `json:"last_commit_message"` + LastAuthorDate *time.Time `json:"last_author_date,omitempty"` + LastCommitMessage *string `json:"last_commit_message,omitempty"` + // `type` will be `file`, `dir`, `symlink`, or `submodule` Type string `json:"type"` Size int64 `json:"size"` @@ -142,8 +144,8 @@ type ContentsResponse struct { SubmoduleGitURL *string `json:"submodule_git_url"` Links *FileLinksResponse `json:"_links"` - LfsOid *string `json:"lfs_oid"` - LfsSize *int64 `json:"lfs_size"` + LfsOid *string `json:"lfs_oid,omitempty"` + LfsSize *int64 `json:"lfs_size,omitempty"` } // FileCommitResponse contains information generated from a Git commit for a repo's file. diff --git a/routers/api/v1/repo/file.go b/routers/api/v1/repo/file.go index 8ce52c2cd4db3..588223c30d5e0 100644 --- a/routers/api/v1/repo/file.go +++ b/routers/api/v1/repo/file.go @@ -814,7 +814,7 @@ func GetContentsExt(ctx *context.APIContext) { // in: path // description: path of the dir, file, symlink or submodule in the repo // type: string - // required: true + // required: false // - name: ref // in: query // description: the name of the commit/branch/tag, default to the repository’s default branch. @@ -823,7 +823,8 @@ func GetContentsExt(ctx *context.APIContext) { // - name: includes // in: query // description: By default this API's response only contains file's metadata. Use comma-separated "includes" options to retrieve more fields. - // Option "file_content" will try to retrieve the file content, option "lfs_metadata" will try to retrieve LFS metadata. + // Option "file_content" will try to retrieve the file content, "lfs_metadata" will try to retrieve LFS metadata, + // "commit_metadata" will try to retrieve commit metadata, and "commit_message" will try to retrieve commit message. // type: string // required: false // responses: @@ -842,6 +843,10 @@ func GetContentsExt(ctx *context.APIContext) { opts.IncludeSingleFileContent = true case "lfs_metadata": opts.IncludeLfsMetadata = true + case "commit_metadata": + opts.IncludeCommitMetadata = true + case "commit_message": + opts.IncludeCommitMessage = true default: ctx.APIError(http.StatusBadRequest, fmt.Sprintf("unknown include option %q", includeOpt)) return @@ -883,7 +888,11 @@ func GetContents(ctx *context.APIContext) { // "$ref": "#/responses/ContentsResponse" // "404": // "$ref": "#/responses/notFound" - ret := getRepoContents(ctx, files_service.GetContentsOrListOptions{TreePath: ctx.PathParam("*"), IncludeSingleFileContent: true}) + ret := getRepoContents(ctx, files_service.GetContentsOrListOptions{ + TreePath: ctx.PathParam("*"), + IncludeSingleFileContent: true, + IncludeCommitMetadata: true, + }) if ctx.Written() { return } diff --git a/services/repository/files/content.go b/services/repository/files/content.go index 19816f275eff3..2c1e88bb59ecb 100644 --- a/services/repository/files/content.go +++ b/services/repository/files/content.go @@ -39,6 +39,8 @@ type GetContentsOrListOptions struct { TreePath string IncludeSingleFileContent bool // include the file's content when the tree path is a file IncludeLfsMetadata bool + IncludeCommitMetadata bool + IncludeCommitMessage bool } // GetContentsOrList gets the metadata of a file's contents (*ContentsResponse) if treePath not a tree @@ -132,40 +134,46 @@ func getFileContentsByEntryInternal(_ context.Context, repo *repo_model.Reposito } selfURLString := selfURL.String() - err = gitRepo.AddLastCommitCache(repo.GetCommitsCountCacheKey(refCommit.InputRef, refType != git.RefTypeCommit), repo.FullName(), refCommit.CommitID) - if err != nil { - return nil, err - } - - lastCommit, err := refCommit.Commit.GetCommitByPath(opts.TreePath) - if err != nil { - return nil, err - } - // All content types have these fields in populated contentsResponse := &api.ContentsResponse{ - Name: entry.Name(), - Path: opts.TreePath, - SHA: entry.ID.String(), - LastCommitSHA: lastCommit.ID.String(), - Size: entry.Size(), - URL: &selfURLString, + Name: entry.Name(), + Path: opts.TreePath, + SHA: entry.ID.String(), + Size: entry.Size(), + URL: &selfURLString, Links: &api.FileLinksResponse{ Self: &selfURLString, }, } - // GitHub doesn't have these fields in the response, but we could follow other similar APIs to name them - // https://docs.github.com/en/rest/commits/commits?apiVersion=2022-11-28#list-commits - if lastCommit.Committer != nil { - contentsResponse.LastCommitterDate = lastCommit.Committer.When - } - if lastCommit.Author != nil { - contentsResponse.LastAuthorDate = lastCommit.Author.When + if opts.IncludeCommitMetadata || opts.IncludeCommitMessage { + err = gitRepo.AddLastCommitCache(repo.GetCommitsCountCacheKey(refCommit.InputRef, refType != git.RefTypeCommit), repo.FullName(), refCommit.CommitID) + if err != nil { + return nil, err + } + + lastCommit, err := refCommit.Commit.GetCommitByPath(opts.TreePath) + if err != nil { + return nil, err + } + + if opts.IncludeCommitMetadata { + contentsResponse.LastCommitSHA = util.ToPointer(lastCommit.ID.String()) + // GitHub doesn't have these fields in the response, but we could follow other similar APIs to name them + // https://docs.github.com/en/rest/commits/commits?apiVersion=2022-11-28#list-commits + if lastCommit.Committer != nil { + contentsResponse.LastCommitterDate = util.ToPointer(lastCommit.Committer.When) + } + if lastCommit.Author != nil { + contentsResponse.LastAuthorDate = util.ToPointer(lastCommit.Author.When) + } + } + if opts.IncludeCommitMessage { + contentsResponse.LastCommitMessage = util.ToPointer(lastCommit.Message()) + } } - contentsResponse.LastCommitMessage = lastCommit.Message() - // Now populate the rest of the ContentsResponse based on entry type + // Now populate the rest of the ContentsResponse based on the entry type if entry.IsRegular() || entry.IsExecutable() { contentsResponse.Type = string(ContentTypeRegular) // if it is listing the repo root dir, don't waste system resources on reading content diff --git a/services/repository/files/content_test.go b/services/repository/files/content_test.go index ef0a4d96189c3..76eeb390e6619 100644 --- a/services/repository/files/content_test.go +++ b/services/repository/files/content_test.go @@ -36,10 +36,10 @@ func getExpectedReadmeContentsResponse() *api.ContentsResponse { Name: treePath, Path: treePath, SHA: "4b4851ad51df6a7d9f25c979345979eaeb5b349f", - LastCommitSHA: "65f1bf27bc3bf70f64657658635e66094edbcb4d", - LastCommitterDate: time.Date(2017, time.March, 19, 16, 47, 59, 0, time.FixedZone("", -14400)), - LastAuthorDate: time.Date(2017, time.March, 19, 16, 47, 59, 0, time.FixedZone("", -14400)), - LastCommitMessage: "Initial commit\n", + LastCommitSHA: util.ToPointer("65f1bf27bc3bf70f64657658635e66094edbcb4d"), + LastCommitterDate: util.ToPointer(time.Date(2017, time.March, 19, 16, 47, 59, 0, time.FixedZone("", -14400))), + LastAuthorDate: util.ToPointer(time.Date(2017, time.March, 19, 16, 47, 59, 0, time.FixedZone("", -14400))), + LastCommitMessage: util.ToPointer("Initial commit\n"), Type: "file", Size: 30, Encoding: &encoding, diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index a0027906d2a95..62fbda5611bbb 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -7549,8 +7549,7 @@ "type": "string", "description": "path of the dir, file, symlink or submodule in the repo", "name": "filepath", - "in": "path", - "required": true + "in": "path" }, { "type": "string", @@ -7560,7 +7559,7 @@ }, { "type": "string", - "description": "By default this API's response only contains file's metadata. Use comma-separated \"includes\" options to retrieve more fields. Option \"file_content\" will try to retrieve the file content, option \"lfs_metadata\" will try to retrieve LFS metadata.", + "description": "By default this API's response only contains file's metadata. Use comma-separated \"includes\" options to retrieve more fields. Option \"file_content\" will try to retrieve the file content, \"lfs_metadata\" will try to retrieve LFS metadata, \"commit_metadata\" will try to retrieve commit metadata, and \"commit_message\" will try to retrieve commit message.", "name": "includes", "in": "query" } diff --git a/tests/integration/api_repo_file_create_test.go b/tests/integration/api_repo_file_create_test.go index f4fbcd164300d..1233eecbabd98 100644 --- a/tests/integration/api_repo_file_create_test.go +++ b/tests/integration/api_repo_file_create_test.go @@ -19,6 +19,7 @@ import ( "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/services/context" "github.com/stretchr/testify/assert" @@ -52,8 +53,8 @@ func getCreateFileOptions() api.CreateFileOptions { func normalizeFileContentResponseCommitTime(c *api.ContentsResponse) { // decoded JSON response may contain different timezone from the one parsed by git commit // so we need to normalize the time to UTC to make "assert.Equal" pass - c.LastCommitterDate = c.LastCommitterDate.UTC() - c.LastAuthorDate = c.LastAuthorDate.UTC() + c.LastCommitterDate = util.ToPointer(c.LastCommitterDate.UTC()) + c.LastAuthorDate = util.ToPointer(c.LastAuthorDate.UTC()) } type apiFileResponseInfo struct { @@ -74,10 +75,10 @@ func getExpectedFileResponseForCreate(info apiFileResponseInfo) *api.FileRespons Name: path.Base(info.treePath), Path: info.treePath, SHA: sha, - LastCommitSHA: info.lastCommitSHA, - LastCommitterDate: info.lastCommitterWhen, - LastAuthorDate: info.lastAuthorWhen, - LastCommitMessage: info.lastCommitMessage, + LastCommitSHA: util.ToPointer(info.lastCommitSHA), + LastCommitterDate: util.ToPointer(info.lastCommitterWhen), + LastAuthorDate: util.ToPointer(info.lastAuthorWhen), + LastCommitMessage: util.ToPointer(info.lastCommitMessage), Size: 16, Type: "file", Encoding: &encoding, diff --git a/tests/integration/api_repo_file_update_test.go b/tests/integration/api_repo_file_update_test.go index 6a7f7529a0a27..9a56711da6a1d 100644 --- a/tests/integration/api_repo_file_update_test.go +++ b/tests/integration/api_repo_file_update_test.go @@ -18,6 +18,7 @@ import ( "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/services/context" "github.com/stretchr/testify/assert" @@ -60,9 +61,9 @@ func getExpectedFileResponseForUpdate(info apiFileResponseInfo) *api.FileRespons Name: path.Base(info.treePath), Path: info.treePath, SHA: sha, - LastCommitSHA: info.lastCommitSHA, - LastCommitterDate: info.lastCommitterWhen, - LastAuthorDate: info.lastAuthorWhen, + LastCommitSHA: util.ToPointer(info.lastCommitSHA), + LastCommitterDate: util.ToPointer(info.lastCommitterWhen), + LastAuthorDate: util.ToPointer(info.lastAuthorWhen), Type: "file", Size: 20, Encoding: &encoding, diff --git a/tests/integration/api_repo_get_contents_list_test.go b/tests/integration/api_repo_get_contents_list_test.go index 0a97de21fc42f..0203f5b0db0d8 100644 --- a/tests/integration/api_repo_get_contents_list_test.go +++ b/tests/integration/api_repo_get_contents_list_test.go @@ -18,6 +18,7 @@ import ( "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/util" repo_service "code.gitea.io/gitea/services/repository" "github.com/stretchr/testify/assert" @@ -35,10 +36,10 @@ func getExpectedContentsListResponseForContents(ref, refType, lastCommitSHA stri Name: path.Base(treePath), Path: treePath, SHA: sha, - LastCommitSHA: lastCommitSHA, - LastCommitterDate: time.Date(2017, time.March, 19, 16, 47, 59, 0, time.FixedZone("", -14400)), - LastAuthorDate: time.Date(2017, time.March, 19, 16, 47, 59, 0, time.FixedZone("", -14400)), - LastCommitMessage: "Initial commit", + LastCommitSHA: util.ToPointer(lastCommitSHA), + LastCommitterDate: util.ToPointer(time.Date(2017, time.March, 19, 16, 47, 59, 0, time.FixedZone("", -14400))), + LastAuthorDate: util.ToPointer(time.Date(2017, time.March, 19, 16, 47, 59, 0, time.FixedZone("", -14400))), + LastCommitMessage: util.ToPointer("Initial commit"), Type: "file", Size: 30, URL: &selfURL, diff --git a/tests/integration/api_repo_get_contents_test.go b/tests/integration/api_repo_get_contents_test.go index f32b376138ef9..d95a049b48ba3 100644 --- a/tests/integration/api_repo_get_contents_test.go +++ b/tests/integration/api_repo_get_contents_test.go @@ -35,10 +35,9 @@ func getExpectedContentsResponseForContents(ref, refType, lastCommitSHA string) Name: treePath, Path: treePath, SHA: "4b4851ad51df6a7d9f25c979345979eaeb5b349f", - LastCommitSHA: lastCommitSHA, - LastCommitterDate: time.Date(2017, time.March, 19, 16, 47, 59, 0, time.FixedZone("", -14400)), - LastAuthorDate: time.Date(2017, time.March, 19, 16, 47, 59, 0, time.FixedZone("", -14400)), - LastCommitMessage: "Initial commit", + LastCommitSHA: util.ToPointer(lastCommitSHA), + LastCommitterDate: util.ToPointer(time.Date(2017, time.March, 19, 16, 47, 59, 0, time.FixedZone("", -14400))), + LastAuthorDate: util.ToPointer(time.Date(2017, time.March, 19, 16, 47, 59, 0, time.FixedZone("", -14400))), Type: "file", Size: 30, Encoding: util.ToPointer("base64"), @@ -215,6 +214,8 @@ func testAPIGetContentsExt(t *testing.T) { assert.Equal(t, "README.md", contentsResponse.DirContents[0].Name) assert.Nil(t, contentsResponse.DirContents[0].Encoding) assert.Nil(t, contentsResponse.DirContents[0].Content) + assert.Nil(t, contentsResponse.DirContents[0].LastCommitSHA) + assert.Nil(t, contentsResponse.DirContents[0].LastCommitMessage) // "includes=file_content" shouldn't affect directory listing req = NewRequestf(t, "GET", "/api/v1/repos/user2/repo1/contents-ext/docs?ref=sub-home-md-img-check&includes=file_content") @@ -250,9 +251,11 @@ func testAPIGetContentsExt(t *testing.T) { assert.Equal(t, "README.md", contentsResponse.FileContents.Name) assert.Nil(t, contentsResponse.FileContents.Encoding) assert.Nil(t, contentsResponse.FileContents.Content) + assert.Nil(t, contentsResponse.FileContents.LastCommitSHA) + assert.Nil(t, contentsResponse.FileContents.LastCommitMessage) // file content is only returned when `includes=file_content` - req = NewRequestf(t, "GET", "/api/v1/repos/user2/repo1/contents-ext/docs/README.md?ref=sub-home-md-img-check&includes=file_content") + req = NewRequestf(t, "GET", "/api/v1/repos/user2/repo1/contents-ext/docs/README.md?ref=sub-home-md-img-check&includes=file_content,commit_metadata,commit_message") resp = MakeRequest(t, req, http.StatusOK) contentsResponse = api.ContentsExtResponse{} DecodeJSON(t, resp, &contentsResponse) @@ -260,6 +263,8 @@ func testAPIGetContentsExt(t *testing.T) { assert.Equal(t, "README.md", contentsResponse.FileContents.Name) assert.NotNil(t, contentsResponse.FileContents.Encoding) assert.NotNil(t, contentsResponse.FileContents.Content) + assert.NotNil(t, contentsResponse.FileContents.LastCommitSHA) + assert.NotNil(t, contentsResponse.FileContents.LastCommitMessage) req = NewRequestf(t, "GET", "/api/v1/repos/user2/lfs/contents-ext/jpeg.jpg?includes=file_content").AddTokenAuth(token2) resp = session.MakeRequest(t, req, http.StatusOK) @@ -271,6 +276,8 @@ func testAPIGetContentsExt(t *testing.T) { assert.Equal(t, "jpeg.jpg", respFile.Name) assert.NotNil(t, respFile.Encoding) assert.NotNil(t, respFile.Content) + assert.Nil(t, contentsResponse.FileContents.LastCommitSHA) + assert.Nil(t, contentsResponse.FileContents.LastCommitMessage) assert.Equal(t, util.ToPointer(int64(107)), respFile.LfsSize) assert.Equal(t, util.ToPointer("0b8d8b5f15046343fd32f451df93acc2bdd9e6373be478b968e4cad6b6647351"), respFile.LfsOid) }) diff --git a/tests/integration/repofiles_change_test.go b/tests/integration/repofiles_change_test.go index 6e7f2b0a0bafb..1af633e517d8c 100644 --- a/tests/integration/repofiles_change_test.go +++ b/tests/integration/repofiles_change_test.go @@ -155,10 +155,10 @@ func getExpectedFileResponseForRepoFilesCreate(commitID string, lastCommit *git. Name: path.Base(treePath), Path: treePath, SHA: "103ff9234cefeee5ec5361d22b49fbb04d385885", - LastCommitSHA: lastCommit.ID.String(), - LastCommitterDate: lastCommit.Committer.When, - LastAuthorDate: lastCommit.Author.When, - LastCommitMessage: "Creates new/file.txt\n", + LastCommitSHA: util.ToPointer(lastCommit.ID.String()), + LastCommitterDate: util.ToPointer(lastCommit.Committer.When), + LastAuthorDate: util.ToPointer(lastCommit.Author.When), + LastCommitMessage: util.ToPointer("Creates new/file.txt\n"), Type: "file", Size: 18, Encoding: &encoding, @@ -226,10 +226,10 @@ func getExpectedFileResponseForRepoFilesUpdate(commitID, filename, lastCommitSHA Name: filename, Path: filename, SHA: "dbf8d00e022e05b7e5cf7e535de857de57925647", - LastCommitSHA: lastCommitSHA, - LastCommitterDate: lastCommitterWhen, - LastAuthorDate: lastAuthorWhen, - LastCommitMessage: "Updates README.md\n", + LastCommitSHA: util.ToPointer(lastCommitSHA), + LastCommitterDate: util.ToPointer(lastCommitterWhen), + LastAuthorDate: util.ToPointer(lastAuthorWhen), + LastCommitMessage: util.ToPointer("Updates README.md\n"), Type: "file", Size: 43, Encoding: &encoding, @@ -333,8 +333,8 @@ func getExpectedFileResponseForRepoFilesUpdateRename(commitID, lastCommitSHA str Name: detail.filename, Path: detail.filename, SHA: detail.sha, - LastCommitSHA: lastCommitSHA, - LastCommitMessage: "Rename files\n", + LastCommitSHA: util.ToPointer(lastCommitSHA), + LastCommitMessage: util.ToPointer("Rename files\n"), Type: "file", Size: detail.size, Encoding: util.ToPointer("base64"), @@ -540,7 +540,7 @@ func TestChangeRepoFilesForUpdateWithFileRename(t *testing.T) { lastCommit, _ := commit.GetCommitByPath(opts.Files[0].TreePath) expectedFileResponse := getExpectedFileResponseForRepoFilesUpdateRename(commit.ID.String(), lastCommit.ID.String()) for _, file := range filesResponse.Files { - file.LastCommitterDate, file.LastAuthorDate = time.Time{}, time.Time{} // there might be different time in one operation, so we ignore them + file.LastCommitterDate, file.LastAuthorDate = nil, nil // there might be different time in one operation, so we ignore them } assert.Len(t, filesResponse.Files, 4) assert.Equal(t, expectedFileResponse.Files, filesResponse.Files) From fd1958576003d3269d88a480bd48efef098dbf28 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Wed, 2 Jul 2025 19:26:45 +0800 Subject: [PATCH 4/6] fix swagger --- routers/api/v1/repo/file.go | 10 ++++++++-- templates/swagger/v1_json.tmpl | 5 +++-- tests/integration/api_repo_get_contents_test.go | 16 +++++++++++++++- 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/routers/api/v1/repo/file.go b/routers/api/v1/repo/file.go index 588223c30d5e0..bcef9930111dd 100644 --- a/routers/api/v1/repo/file.go +++ b/routers/api/v1/repo/file.go @@ -812,9 +812,10 @@ func GetContentsExt(ctx *context.APIContext) { // required: true // - name: filepath // in: path - // description: path of the dir, file, symlink or submodule in the repo + // description: path of the dir, file, symlink or submodule in the repo. Swagger requires path parameter to be "required", + // you can leave it empty or pass a single dot (".") to get the root directory. // type: string - // required: false + // required: true // - name: ref // in: query // description: the name of the commit/branch/tag, default to the repository’s default branch. @@ -833,6 +834,11 @@ func GetContentsExt(ctx *context.APIContext) { // "404": // "$ref": "#/responses/notFound" + treePath := ctx.PathParam("*") + if treePath == "." || treePath == "/" { + treePath = "" + ctx.SetPathParam("*", treePath) + } opts := files_service.GetContentsOrListOptions{TreePath: ctx.PathParam("*")} for includeOpt := range strings.SplitSeq(ctx.FormString("includes"), ",") { if includeOpt == "" { diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 62fbda5611bbb..879f59df2e541 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -7547,9 +7547,10 @@ }, { "type": "string", - "description": "path of the dir, file, symlink or submodule in the repo", + "description": "path of the dir, file, symlink or submodule in the repo. Swagger requires path parameter to be \"required\", you can leave it empty or pass a single dot (\".\") to get the root directory.", "name": "filepath", - "in": "path" + "in": "path", + "required": true }, { "type": "string", diff --git a/tests/integration/api_repo_get_contents_test.go b/tests/integration/api_repo_get_contents_test.go index d95a049b48ba3..0bd378e9cdbda 100644 --- a/tests/integration/api_repo_get_contents_test.go +++ b/tests/integration/api_repo_get_contents_test.go @@ -206,11 +206,25 @@ func testAPIGetContentsExt(t *testing.T) { session := loginUser(t, "user2") token2 := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) t.Run("DirContents", func(t *testing.T) { - req := NewRequestf(t, "GET", "/api/v1/repos/user2/repo1/contents-ext/docs?ref=sub-home-md-img-check") + req := NewRequestf(t, "GET", "/api/v1/repos/user2/repo1/contents-ext?ref=sub-home-md-img-check") resp := MakeRequest(t, req, http.StatusOK) var contentsResponse api.ContentsExtResponse DecodeJSON(t, resp, &contentsResponse) assert.Nil(t, contentsResponse.FileContents) + assert.NotNil(t, contentsResponse.DirContents) + + req = NewRequestf(t, "GET", "/api/v1/repos/user2/repo1/contents-ext/.?ref=sub-home-md-img-check") + resp = MakeRequest(t, req, http.StatusOK) + contentsResponse = api.ContentsExtResponse{} + DecodeJSON(t, resp, &contentsResponse) + assert.Nil(t, contentsResponse.FileContents) + assert.NotNil(t, contentsResponse.DirContents) + + req = NewRequestf(t, "GET", "/api/v1/repos/user2/repo1/contents-ext/docs?ref=sub-home-md-img-check") + resp = MakeRequest(t, req, http.StatusOK) + contentsResponse = api.ContentsExtResponse{} + DecodeJSON(t, resp, &contentsResponse) + assert.Nil(t, contentsResponse.FileContents) assert.Equal(t, "README.md", contentsResponse.DirContents[0].Name) assert.Nil(t, contentsResponse.DirContents[0].Encoding) assert.Nil(t, contentsResponse.DirContents[0].Content) From 08bf2d64fd12fc2117afee231dafb9ad8f062f6a Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Wed, 2 Jul 2025 19:57:05 +0800 Subject: [PATCH 5/6] fix --- routers/api/v1/repo/file.go | 6 ++---- services/repository/files/file.go | 7 ++++++- tests/integration/api_repo_file_create_test.go | 6 ++---- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/routers/api/v1/repo/file.go b/routers/api/v1/repo/file.go index bcef9930111dd..69b59962229d7 100644 --- a/routers/api/v1/repo/file.go +++ b/routers/api/v1/repo/file.go @@ -834,10 +834,8 @@ func GetContentsExt(ctx *context.APIContext) { // "404": // "$ref": "#/responses/notFound" - treePath := ctx.PathParam("*") - if treePath == "." || treePath == "/" { - treePath = "" - ctx.SetPathParam("*", treePath) + if treePath := ctx.PathParam("*"); treePath == "." || treePath == "/" { + ctx.SetPathParam("*", "") // workaround for swagger, it requires path parameter to be "required", but we need to list root directory } opts := files_service.GetContentsOrListOptions{TreePath: ctx.PathParam("*")} for includeOpt := range strings.SplitSeq(ctx.FormString("includes"), ",") { diff --git a/services/repository/files/file.go b/services/repository/files/file.go index 2a63a0a5b9704..13d171d1394b3 100644 --- a/services/repository/files/file.go +++ b/services/repository/files/file.go @@ -22,7 +22,12 @@ import ( func GetContentsListFromTreePaths(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, refCommit *utils.RefCommit, treePaths []string) (files []*api.ContentsResponse) { var size int64 for _, treePath := range treePaths { - fileContents, _ := GetFileContents(ctx, repo, gitRepo, refCommit, GetContentsOrListOptions{TreePath: treePath, IncludeSingleFileContent: true}) // ok if fails, then will be nil + // ok if fails, then will be nil + fileContents, _ := GetFileContents(ctx, repo, gitRepo, refCommit, GetContentsOrListOptions{ + TreePath: treePath, + IncludeSingleFileContent: true, + IncludeCommitMetadata: true, + }) if fileContents != nil && fileContents.Content != nil && *fileContents.Content != "" { // if content isn't empty (e.g., due to the single blob being too large), add file size to response size size += int64(len(*fileContents.Content)) diff --git a/tests/integration/api_repo_file_create_test.go b/tests/integration/api_repo_file_create_test.go index 1233eecbabd98..af3bc546803a1 100644 --- a/tests/integration/api_repo_file_create_test.go +++ b/tests/integration/api_repo_file_create_test.go @@ -58,8 +58,8 @@ func normalizeFileContentResponseCommitTime(c *api.ContentsResponse) { } type apiFileResponseInfo struct { - repoFullName, commitID, treePath, lastCommitSHA, lastCommitMessage string - lastCommitterWhen, lastAuthorWhen time.Time + repoFullName, commitID, treePath, lastCommitSHA string + lastCommitterWhen, lastAuthorWhen time.Time } func getExpectedFileResponseForCreate(info apiFileResponseInfo) *api.FileResponse { @@ -78,7 +78,6 @@ func getExpectedFileResponseForCreate(info apiFileResponseInfo) *api.FileRespons LastCommitSHA: util.ToPointer(info.lastCommitSHA), LastCommitterDate: util.ToPointer(info.lastCommitterWhen), LastAuthorDate: util.ToPointer(info.lastAuthorWhen), - LastCommitMessage: util.ToPointer(info.lastCommitMessage), Size: 16, Type: "file", Encoding: &encoding, @@ -195,7 +194,6 @@ func TestAPICreateFile(t *testing.T) { lastCommitSHA: lastCommit.ID.String(), lastCommitterWhen: lastCommit.Committer.When, lastAuthorWhen: lastCommit.Author.When, - lastCommitMessage: lastCommit.Message(), }) var fileResponse api.FileResponse DecodeJSON(t, resp, &fileResponse) From 58bc9cb1074da95e4e6adc9b286ae075d3ed9b88 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Wed, 2 Jul 2025 20:30:42 +0800 Subject: [PATCH 6/6] fix --- services/repository/files/content_test.go | 75 +------------------ .../api_repo_get_contents_list_test.go | 20 +++-- .../integration/api_repo_get_contents_test.go | 17 +++-- tests/integration/repofiles_change_test.go | 27 +++---- 4 files changed, 33 insertions(+), 106 deletions(-) diff --git a/services/repository/files/content_test.go b/services/repository/files/content_test.go index 76eeb390e6619..d72f918074d16 100644 --- a/services/repository/files/content_test.go +++ b/services/repository/files/content_test.go @@ -5,57 +5,21 @@ package files import ( "testing" - "time" "code.gitea.io/gitea/models/unittest" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" - "code.gitea.io/gitea/routers/api/v1/utils" "code.gitea.io/gitea/services/contexttest" _ "code.gitea.io/gitea/models/actions" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) func TestMain(m *testing.M) { unittest.MainTest(m) } -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", - LastCommitSHA: util.ToPointer("65f1bf27bc3bf70f64657658635e66094edbcb4d"), - LastCommitterDate: util.ToPointer(time.Date(2017, time.March, 19, 16, 47, 59, 0, time.FixedZone("", -14400))), - LastAuthorDate: util.ToPointer(time.Date(2017, time.March, 19, 16, 47, 59, 0, time.FixedZone("", -14400))), - LastCommitMessage: util.ToPointer("Initial commit\n"), - 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) { unittest.PrepareTestEnv(t) ctx, _ := contexttest.MockContext(t, "user2/repo1") @@ -64,45 +28,8 @@ func TestGetContents(t *testing.T) { contexttest.LoadRepoCommit(t, ctx) contexttest.LoadUser(t, ctx, 2) contexttest.LoadGitRepo(t, ctx) - defer ctx.Repo.GitRepo.Close() - repo, gitRepo := ctx.Repo.Repository, ctx.Repo.GitRepo - refCommit, err := utils.ResolveRefCommit(ctx, ctx.Repo.Repository, ctx.Repo.Repository.DefaultBranch) - require.NoError(t, err) - - t.Run("GetContentsOrList(README.md)-MetaOnly", func(t *testing.T) { - expectedContentsResponse := getExpectedReadmeContentsResponse() - expectedContentsResponse.Encoding = nil // because will be in a list, doesn't have encoding and content - expectedContentsResponse.Content = nil - extResp, err := GetContentsOrList(ctx, repo, gitRepo, refCommit, GetContentsOrListOptions{TreePath: "README.md", IncludeSingleFileContent: false}) - assert.Equal(t, expectedContentsResponse, extResp.FileContents) - assert.NoError(t, err) - }) - - t.Run("GetContentsOrList(README.md)", func(t *testing.T) { - expectedContentsResponse := getExpectedReadmeContentsResponse() - extResp, err := GetContentsOrList(ctx, repo, gitRepo, refCommit, GetContentsOrListOptions{TreePath: "README.md", IncludeSingleFileContent: true}) - assert.Equal(t, expectedContentsResponse, extResp.FileContents) - assert.NoError(t, err) - }) - - t.Run("GetContentsOrList(RootDir)", func(t *testing.T) { - readmeContentsResponse := getExpectedReadmeContentsResponse() - readmeContentsResponse.Encoding = nil // because will be in a list, doesn't have encoding and content - readmeContentsResponse.Content = nil - expectedContentsListResponse := []*api.ContentsResponse{readmeContentsResponse} - // even if IncludeFileContent is true, it has no effect for directory listing - extResp, err := GetContentsOrList(ctx, repo, gitRepo, refCommit, GetContentsOrListOptions{TreePath: "", IncludeSingleFileContent: true}) - assert.Equal(t, expectedContentsListResponse, extResp.DirContents) - assert.NoError(t, err) - }) - t.Run("GetContentsOrList(NoSuchTreePath)", func(t *testing.T) { - extResp, err := GetContentsOrList(ctx, repo, gitRepo, refCommit, GetContentsOrListOptions{TreePath: "no-such/file.md"}) - assert.Error(t, err) - assert.EqualError(t, err, "object does not exist [id: , rel_path: no-such]") - assert.Nil(t, extResp.DirContents) - assert.Nil(t, extResp.FileContents) - }) + // GetContentsOrList's behavior is fully tested in integration tests, so we don't need to test it here. t.Run("GetBlobBySHA", func(t *testing.T) { sha := "65f1bf27bc3bf70f64657658635e66094edbcb4d" diff --git a/tests/integration/api_repo_get_contents_list_test.go b/tests/integration/api_repo_get_contents_list_test.go index 0203f5b0db0d8..563d6fcc10dca 100644 --- a/tests/integration/api_repo_get_contents_list_test.go +++ b/tests/integration/api_repo_get_contents_list_test.go @@ -39,7 +39,6 @@ func getExpectedContentsListResponseForContents(ref, refType, lastCommitSHA stri LastCommitSHA: util.ToPointer(lastCommitSHA), LastCommitterDate: util.ToPointer(time.Date(2017, time.March, 19, 16, 47, 59, 0, time.FixedZone("", -14400))), LastAuthorDate: util.ToPointer(time.Date(2017, time.March, 19, 16, 47, 59, 0, time.FixedZone("", -14400))), - LastCommitMessage: util.ToPointer("Initial commit"), Type: "file", Size: 30, URL: &selfURL, @@ -67,7 +66,6 @@ func testAPIGetContentsList(t *testing.T, u *url.URL) { repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) // public repo repo3 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3}) // public repo repo16 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 16}) // private repo - treePath := "" // root dir // Get user2's token session := loginUser(t, user2.Name) @@ -96,7 +94,7 @@ func testAPIGetContentsList(t *testing.T, u *url.URL) { // 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) + req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents?ref=%s", user2.Name, repo1.Name, ref) resp := MakeRequest(t, req, http.StatusOK) var contentsListResponse []*api.ContentsResponse DecodeJSON(t, resp, &contentsListResponse) @@ -108,7 +106,7 @@ func testAPIGetContentsList(t *testing.T, u *url.URL) { // No ref refType = "branch" - req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/%s", user2.Name, repo1.Name, treePath) + req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/", user2.Name, repo1.Name) resp = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &contentsListResponse) assert.NotNil(t, contentsListResponse) @@ -119,7 +117,7 @@ func testAPIGetContentsList(t *testing.T, u *url.URL) { // 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) + req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents?ref=%s", user2.Name, repo1.Name, ref) resp = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &contentsListResponse) assert.NotNil(t, contentsListResponse) @@ -133,7 +131,7 @@ func testAPIGetContentsList(t *testing.T, u *url.URL) { // 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) + req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/?ref=%s", user2.Name, repo1.Name, ref) resp = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &contentsListResponse) assert.NotNil(t, contentsListResponse) @@ -147,7 +145,7 @@ func testAPIGetContentsList(t *testing.T, u *url.URL) { // 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) + req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/?ref=%s", user2.Name, repo1.Name, ref) resp = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &contentsListResponse) assert.NotNil(t, contentsListResponse) @@ -156,21 +154,21 @@ func testAPIGetContentsList(t *testing.T, u *url.URL) { // 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) + req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/?ref=%s", user2.Name, repo1.Name, ref) MakeRequest(t, req, http.StatusNotFound) // 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", user2.Name, repo16.Name, treePath). + req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/", user2.Name, repo16.Name). AddTokenAuth(token4) 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", user2.Name, repo16.Name). + req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/", user2.Name, repo16.Name). AddTokenAuth(token2) MakeRequest(t, req, http.StatusOK) // Test access of org org3 private repo file by owner user2 - req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/%s", org3.Name, repo3.Name, treePath). + req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/", org3.Name, repo3.Name). AddTokenAuth(token2) MakeRequest(t, req, http.StatusOK) } diff --git a/tests/integration/api_repo_get_contents_test.go b/tests/integration/api_repo_get_contents_test.go index 0bd378e9cdbda..33df74f6eeb74 100644 --- a/tests/integration/api_repo_get_contents_test.go +++ b/tests/integration/api_repo_get_contents_test.go @@ -97,11 +97,16 @@ func testAPIGetContents(t *testing.T, u *url.URL) { require.NoError(t, err) /*** END SETUP ***/ + // not found + req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/no-such/file.md", user2.Name, repo1.Name) + resp := MakeRequest(t, req, http.StatusNotFound) + assert.Contains(t, resp.Body.String(), "object does not exist [id: , rel_path: no-such]") + // 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 := MakeRequest(t, req, http.StatusOK) + req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/%s?ref=%s", user2.Name, repo1.Name, treePath, ref) + resp = MakeRequest(t, req, http.StatusOK) var contentsResponse api.ContentsResponse DecodeJSON(t, resp, &contentsResponse) lastCommit, _ := gitRepo.GetCommitByPath("README.md") @@ -116,7 +121,7 @@ func testAPIGetContents(t *testing.T, u *url.URL) { expectedContentsResponse = getExpectedContentsResponseForContents(repo1.DefaultBranch, refType, lastCommit.ID.String()) assert.Equal(t, *expectedContentsResponse, contentsResponse) - // ref is the branch we created above in setup + // 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) @@ -256,7 +261,7 @@ func testAPIGetContentsExt(t *testing.T) { assert.Equal(t, util.ToPointer("0b8d8b5f15046343fd32f451df93acc2bdd9e6373be478b968e4cad6b6647351"), respFile.LfsOid) }) t.Run("FileContents", func(t *testing.T) { - // by default, no file content is returned + // by default, no file content or commit info is returned req := NewRequestf(t, "GET", "/api/v1/repos/user2/repo1/contents-ext/docs/README.md?ref=sub-home-md-img-check") resp := MakeRequest(t, req, http.StatusOK) var contentsResponse api.ContentsExtResponse @@ -277,8 +282,8 @@ func testAPIGetContentsExt(t *testing.T) { assert.Equal(t, "README.md", contentsResponse.FileContents.Name) assert.NotNil(t, contentsResponse.FileContents.Encoding) assert.NotNil(t, contentsResponse.FileContents.Content) - assert.NotNil(t, contentsResponse.FileContents.LastCommitSHA) - assert.NotNil(t, contentsResponse.FileContents.LastCommitMessage) + assert.Equal(t, "4649299398e4d39a5c09eb4f534df6f1e1eb87cc", *contentsResponse.FileContents.LastCommitSHA) + assert.Equal(t, "Test how READMEs render images when found in a subfolder\n", *contentsResponse.FileContents.LastCommitMessage) req = NewRequestf(t, "GET", "/api/v1/repos/user2/lfs/contents-ext/jpeg.jpg?includes=file_content").AddTokenAuth(token2) resp = session.MakeRequest(t, req, http.StatusOK) diff --git a/tests/integration/repofiles_change_test.go b/tests/integration/repofiles_change_test.go index 1af633e517d8c..dc389f568057d 100644 --- a/tests/integration/repofiles_change_test.go +++ b/tests/integration/repofiles_change_test.go @@ -158,7 +158,6 @@ func getExpectedFileResponseForRepoFilesCreate(commitID string, lastCommit *git. LastCommitSHA: util.ToPointer(lastCommit.ID.String()), LastCommitterDate: util.ToPointer(lastCommit.Committer.When), LastAuthorDate: util.ToPointer(lastCommit.Author.When), - LastCommitMessage: util.ToPointer("Creates new/file.txt\n"), Type: "file", Size: 18, Encoding: &encoding, @@ -229,7 +228,6 @@ func getExpectedFileResponseForRepoFilesUpdate(commitID, filename, lastCommitSHA LastCommitSHA: util.ToPointer(lastCommitSHA), LastCommitterDate: util.ToPointer(lastCommitterWhen), LastAuthorDate: util.ToPointer(lastAuthorWhen), - LastCommitMessage: util.ToPointer("Updates README.md\n"), Type: "file", Size: 43, Encoding: &encoding, @@ -330,19 +328,18 @@ func getExpectedFileResponseForRepoFilesUpdateRename(commitID, lastCommitSHA str downloadURL := setting.AppURL + "user2/lfs/raw/branch/master/" + detail.filename // don't set time related fields because there might be different time in one operation responses = append(responses, &api.ContentsResponse{ - Name: detail.filename, - Path: detail.filename, - SHA: detail.sha, - LastCommitSHA: util.ToPointer(lastCommitSHA), - LastCommitMessage: util.ToPointer("Rename files\n"), - Type: "file", - Size: detail.size, - Encoding: util.ToPointer("base64"), - Content: &detail.content, - URL: &selfURL, - HTMLURL: &htmlURL, - GitURL: &gitURL, - DownloadURL: &downloadURL, + Name: detail.filename, + Path: detail.filename, + SHA: detail.sha, + LastCommitSHA: util.ToPointer(lastCommitSHA), + Type: "file", + Size: detail.size, + Encoding: util.ToPointer("base64"), + Content: &detail.content, + URL: &selfURL, + HTMLURL: &htmlURL, + GitURL: &gitURL, + DownloadURL: &downloadURL, Links: &api.FileLinksResponse{ Self: &selfURL, GitURL: &gitURL,