diff options
author | ᴜɴᴋɴᴡᴏɴ <u@gogs.io> | 2020-03-05 16:15:38 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-03-05 16:15:38 +0800 |
commit | 5ad2fdcf0b3334a7b7d5aff8cf57ae49d7f911f8 (patch) | |
tree | ca875a6fffabcfced8bcc16b3468ab2d68d65e3b /internal/route/api/v1/repo | |
parent | 8d37d418e71979835de69d86563142befa4ea511 (diff) |
api: `GET /repos/:owner/:repo/contents/:path` (#5963)
* support API `GET /repos/:owner/:repo/contents/:path`
This PR adds support to #5949:
`GET /repos/:owner/:repo/contents/:path`
Curl:
```bash
curl -H "Authorization: token REDACTED"
http://localhost:3000/api/v1/repos/root/testrepo/contents//master/README.md
-X GET | jq .
```
Curl Response:
```bash
{
"type": "blob",
"size": 12,
"name": "README.md",
"path": "README.md",
"sha": "70fcb456d436f08462602f26df6fb7e167e7a916",
"url": "http://localhost:3000/api/v1/repos/root/testrepo/contents/README.md",
"git_url": "http://localhost:3000/api/v1/repos/root/testrepo/trees/70fcb456d436f08462602f26df6fb7e167e7a916",
"html_url": "http://localhost:3000/api/v1/repos/root/testrepo/tree/70fcb456d436f08462602f26df6fb7e167e7a916",
"download_url": "http://localhost:3000/api/v1/root/testrepo/raw/README.md",
"_links": {
"git": "http://localhost:3000/api/v1/repos/root/testrepo/trees/70fcb456d436f08462602f26df6fb7e167e7a916",
"self": "http://localhost:3000/api/v1/repos/root/testrepo/contents/README.md",
"html": "http://localhost:3000/api/v1/repos/root/testrepo/tree/70fcb456d436f08462602f26df6fb7e167e7a916"
},
"content": "IyB0ZXN0cmVwbwoK"
}
```
* rename - path.go to contents.go
* reorder imports
Co-Authored-By: ᴜɴᴋɴᴡᴏɴ <u@gogs.io>
* rename struct to repoContents and fix field order
Co-Authored-By: ᴜɴᴋɴᴡᴏɴ <u@gogs.io>
* rename variable
Co-Authored-By: ᴜɴᴋɴᴡᴏɴ <u@gogs.io>
* rename GetPathContents to GetContents
Co-Authored-By: ᴜɴᴋɴᴡᴏɴ <u@gogs.io>
* return on server error
Co-Authored-By: ᴜɴᴋɴᴡᴏɴ <u@gogs.io>
* resolve conflicts introduced via git web ui
* make constants as method variables
* handle dir type case last
* fix func and var names
* implement suggested changes in review
* refactor smaller funcs to be part of GetContent
* fix content type check for blob after refactoring
* changes based on suggestions
* read full file, return empty json array
* don't set submoduleURL
* set server err msg to method name
* set target to be blob data for symlinks
* Update contents.go
Co-authored-by: ᴜɴᴋɴᴡᴏɴ <u@gogs.io>
Diffstat (limited to 'internal/route/api/v1/repo')
-rw-r--r-- | internal/route/api/v1/repo/contents.go | 203 |
1 files changed, 203 insertions, 0 deletions
diff --git a/internal/route/api/v1/repo/contents.go b/internal/route/api/v1/repo/contents.go new file mode 100644 index 00000000..7f7ec18e --- /dev/null +++ b/internal/route/api/v1/repo/contents.go @@ -0,0 +1,203 @@ +// Copyright 2020 The Gogs 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 repo + +import ( + "encoding/base64" + "fmt" + "io/ioutil" + + "github.com/gogs/git-module" + + "gogs.io/gogs/internal/context" +) + +type repoContent struct { + Type string `json:"type"` + Target string `json:"target,omitempty"` + SubmoduleGitURL string `json:"submodule_git_url,omitempty"` + Encoding string `json:"encoding,omitempty"` + Size int64 `json:"size"` + Name string `json:"name"` + Path string `json:"path"` + Content string `json:"content,omitempty"` + Sha string `json:"sha"` + URL string `json:"url"` + GitURL string `json:"git_url"` + HTMLURL string `json:"html_url"` + DownloadURL string `json:"download_url"` + Links Links `json:"_links"` +} + +type Links struct { + Git string `json:"git"` + Self string `json:"self"` + HTML string `json:"html"` +} + +func GetContents(c *context.APIContext) { + treeEntry, err := c.Repo.Commit.GetTreeEntryByPath(c.Repo.TreePath) + if err != nil { + c.NotFoundOrServerError("GetTreeEntryByPath", git.IsErrNotExist, err) + return + } + username := c.Params(":username") + reponame := c.Params(":reponame") + + // TODO: figure out the best way to do this + // :base-url/:username/:project/raw/:refs/:path + templateDownloadURL := "%s/%s/%s/raw/%s" + // :base-url/repos/:username/:project/contents/:path + templateSelfLink := "%s/repos/%s/%s/contents/%s" + // :baseurl/repos/:username/:project/git/trees/:sha + templateGitURLLink := "%s/repos/%s/%s/trees/%s" + // :baseurl/repos/:username/:project/tree/:sha + templateHTMLLLink := "%s/repos/%s/%s/tree/%s" + + gitURL := fmt.Sprintf(templateGitURLLink, c.BaseURL, username, reponame, treeEntry.ID.String()) + htmlURL := fmt.Sprintf(templateHTMLLLink, c.BaseURL, username, reponame, treeEntry.ID.String()) + selfURL := fmt.Sprintf(templateSelfLink, c.BaseURL, username, reponame, c.Repo.TreePath) + + // TODO(unknwon): Make a treeEntryToRepoContent helper. + contents := &repoContent{ + Size: treeEntry.Size(), + Name: treeEntry.Name(), + Path: c.Repo.TreePath, + Sha: treeEntry.ID.String(), + URL: selfURL, + GitURL: gitURL, + HTMLURL: htmlURL, + DownloadURL: fmt.Sprintf(templateDownloadURL, c.BaseURL, username, reponame, c.Repo.TreePath), + Links: Links{ + Git: gitURL, + Self: selfURL, + HTML: htmlURL, + }, + } + + // A tree entry can only be one of the following types: + // 1. Tree (directory) + // 2. SubModule + // 3. SymLink + // 4. Blob (file) + if treeEntry.IsSubModule() { + // TODO(unknwon): submoduleURL is not set as current git-module doesn't handle it properly + contents.Type = "submodule" + c.JSONSuccess(contents) + return + + } else if treeEntry.IsLink() { + contents.Type = "symlink" + blob, err := c.Repo.Commit.GetBlobByPath(c.Repo.TreePath) + if err != nil { + c.ServerError("GetBlobByPath", err) + return + } + b, err := blob.Data() + if err != nil { + c.ServerError("Data", err) + return + } + buf, err := ioutil.ReadAll(b) + if err != nil { + c.ServerError("ReadAll", err) + return + } + contents.Target = string(buf) + c.JSONSuccess(contents) + return + + } else if treeEntry.Type == "blob" { + blob, err := c.Repo.Commit.GetBlobByPath(c.Repo.TreePath) + if err != nil { + c.ServerError("GetBlobByPath", err) + return + } + b, err := blob.Data() + if err != nil { + c.ServerError("Data", err) + return + } + buf, err := ioutil.ReadAll(b) + if err != nil { + c.ServerError("ReadAll", err) + return + } + contents.Content = base64.StdEncoding.EncodeToString(buf) + contents.Type = "file" + c.JSONSuccess(contents) + return + } + + // treeEntry is a directory + dirTree, err := c.Repo.GitRepo.GetTree(treeEntry.ID.String()) + if err != nil { + c.NotFoundOrServerError("GetTree", git.IsErrNotExist, err) + return + } + + entries, err := dirTree.ListEntries() + if err != nil { + c.NotFoundOrServerError("ListEntries", git.IsErrNotExist, err) + return + } + + if len(entries) == 0 { + c.JSONSuccess([]string{}) + return + } + + var results = make([]*repoContent, 0, len(entries)) + for _, entry := range entries { + gitURL := fmt.Sprintf(templateGitURLLink, c.BaseURL, username, reponame, entry.ID.String()) + htmlURL := fmt.Sprintf(templateHTMLLLink, c.BaseURL, username, reponame, entry.ID.String()) + selfURL := fmt.Sprintf(templateSelfLink, c.BaseURL, username, reponame, c.Repo.TreePath) + var contentType string + if entry.IsDir() { + contentType = "dir" + } else if entry.IsSubModule() { + // TODO(unknwon): submoduleURL is not set as current git-module doesn't handle it properly + contentType = "submodule" + } else if entry.IsLink() { + contentType = "symlink" + blob, err := c.Repo.Commit.GetBlobByPath(c.Repo.TreePath) + if err != nil { + c.ServerError("GetBlobByPath", err) + return + } + b, err := blob.Data() + if err != nil { + c.ServerError("Data", err) + return + } + buf, err := ioutil.ReadAll(b) + if err != nil { + c.ServerError("ReadAll", err) + return + } + contents.Target = string(buf) + } else { + contentType = "file" + } + + results = append(results, &repoContent{ + Type: contentType, + Size: entry.Size(), + Name: entry.Name(), + Path: c.Repo.TreePath, + Sha: entry.ID.String(), + URL: selfURL, + GitURL: gitURL, + HTMLURL: htmlURL, + DownloadURL: fmt.Sprintf(templateDownloadURL, c.BaseURL, username, reponame, c.Repo.TreePath), + Links: Links{ + Git: gitURL, + Self: selfURL, + HTML: htmlURL, + }, + }) + } + c.JSONSuccess(results) +} |