Fixes #2738 - Adds the /git/tags API endpoint (#7138)

* Fixes #2738 - /git/tags API

* proper URLs

* Adds function comments

* Updates swagger

* Removes newline from tag message

* Removes trailing newline from commit message

* Adds integration test

* Removed debugging

* Adds tests

* Fixes bug where multiple tags of same commit show wrong tag name

* Fix formatting

* Removes unused varaible

* Fix to annotated tag function names and response

* Update modules/git/repo_tag.go

Co-Authored-By: Lauris BH <lauris@nix.lv>

* Uses TagPrefix

* Changes per review, better error handling for getting tag and commit IDs

* Fix to getting commit ID

* Fix to getting commit ID

* Fix to getting commit ID

* Fix to getting commit ID
This commit is contained in:
Richard Mahn 2019-06-08 10:31:11 -04:00 committed by Lauris BH
parent 23a2ee3510
commit 8de0b0a3f0
16 changed files with 551 additions and 85 deletions

View file

@ -751,6 +751,7 @@ func RegisterRoutes(m *macaron.Macaron) {
Post(reqToken(), bind(api.CreateStatusOption{}), repo.NewCommitStatus)
}, reqRepoReader(models.UnitTypeCode))
m.Group("/commits/:ref", func() {
// TODO: Add m.Get("") for single commit (https://developer.github.com/v3/repos/commits/#get-a-single-commit)
m.Get("/status", repo.GetCombinedCommitStatusByRef)
m.Get("/statuses", repo.GetCommitStatusesByRef)
}, reqRepoReader(models.UnitTypeCode))
@ -762,6 +763,7 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Get("/refs/*", repo.GetGitRefs)
m.Get("/trees/:sha", context.RepoRef(), repo.GetTree)
m.Get("/blobs/:sha", context.RepoRef(), repo.GetBlob)
m.Get("/tags/:sha", context.RepoRef(), repo.GetTag)
}, reqRepoReader(models.UnitTypeCode))
m.Group("/contents", func() {
m.Get("/*", repo.GetFileContents)

View file

@ -6,6 +6,7 @@ package convert
import (
"fmt"
"time"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/git"
@ -26,7 +27,7 @@ func ToEmail(email *models.EmailAddress) *api.Email {
}
}
// ToBranch convert a commit and branch to an api.Branch
// ToBranch convert a git.Commit and git.Branch to an api.Branch
func ToBranch(repo *models.Repository, b *git.Branch, c *git.Commit) *api.Branch {
return &api.Branch{
Name: b.Name,
@ -34,23 +35,18 @@ func ToBranch(repo *models.Repository, b *git.Branch, c *git.Commit) *api.Branch
}
}
// ToTag convert a tag to an api.Tag
// ToTag convert a git.Tag to an api.Tag
func ToTag(repo *models.Repository, t *git.Tag) *api.Tag {
return &api.Tag{
Name: t.Name,
Commit: struct {
SHA string `json:"sha"`
URL string `json:"url"`
}{
SHA: t.ID.String(),
URL: util.URLJoin(repo.Link(), "commit", t.ID.String()),
},
ZipballURL: util.URLJoin(repo.Link(), "archive", t.Name+".zip"),
TarballURL: util.URLJoin(repo.Link(), "archive", t.Name+".tar.gz"),
Name: t.Name,
ID: t.ID.String(),
Commit: ToCommitMeta(repo, t),
ZipballURL: util.URLJoin(repo.HTMLURL(), "archive", t.Name+".zip"),
TarballURL: util.URLJoin(repo.HTMLURL(), "archive", t.Name+".tar.gz"),
}
}
// ToCommit convert a commit to api.PayloadCommit
// ToCommit convert a git.Commit to api.PayloadCommit
func ToCommit(repo *models.Repository, c *git.Commit) *api.PayloadCommit {
authorUsername := ""
if author, err := models.GetUserByEmail(c.Author.Email); err == nil {
@ -66,17 +62,10 @@ func ToCommit(repo *models.Repository, c *git.Commit) *api.PayloadCommit {
log.Error("GetUserByEmail: %v", err)
}
verif := models.ParseCommitWithSignature(c)
var signature, payload string
if c.Signature != nil {
signature = c.Signature.Signature
payload = c.Signature.Payload
}
return &api.PayloadCommit{
ID: c.ID.String(),
Message: c.Message(),
URL: util.URLJoin(repo.Link(), "commit", c.ID.String()),
URL: util.URLJoin(repo.HTMLURL(), "commit", c.ID.String()),
Author: &api.PayloadUser{
Name: c.Author.Name,
Email: c.Author.Email,
@ -87,13 +76,24 @@ func ToCommit(repo *models.Repository, c *git.Commit) *api.PayloadCommit {
Email: c.Committer.Email,
UserName: committerUsername,
},
Timestamp: c.Author.When,
Verification: &api.PayloadCommitVerification{
Verified: verif.Verified,
Reason: verif.Reason,
Signature: signature,
Payload: payload,
},
Timestamp: c.Author.When,
Verification: ToVerification(c),
}
}
// ToVerification convert a git.Commit.Signature to an api.PayloadCommitVerification
func ToVerification(c *git.Commit) *api.PayloadCommitVerification {
verif := models.ParseCommitWithSignature(c)
var signature, payload string
if c.Signature != nil {
signature = c.Signature.Signature
payload = c.Signature.Payload
}
return &api.PayloadCommitVerification{
Verified: verif.Verified,
Reason: verif.Reason,
Signature: signature,
Payload: payload,
}
}
@ -242,3 +242,45 @@ func ToUser(user *models.User, signed, admin bool) *api.User {
}
return result
}
// ToAnnotatedTag convert git.Tag to api.AnnotatedTag
func ToAnnotatedTag(repo *models.Repository, t *git.Tag, c *git.Commit) *api.AnnotatedTag {
return &api.AnnotatedTag{
Tag: t.Name,
SHA: t.ID.String(),
Object: ToAnnotatedTagObject(repo, c),
Message: t.Message,
URL: util.URLJoin(repo.APIURL(), "git/tags", t.ID.String()),
Tagger: ToCommitUser(t.Tagger),
Verification: ToVerification(c),
}
}
// ToAnnotatedTagObject convert a git.Commit to an api.AnnotatedTagObject
func ToAnnotatedTagObject(repo *models.Repository, commit *git.Commit) *api.AnnotatedTagObject {
return &api.AnnotatedTagObject{
SHA: commit.ID.String(),
Type: string(git.ObjectCommit),
URL: util.URLJoin(repo.APIURL(), "git/commits", commit.ID.String()),
}
}
// ToCommitUser convert a git.Signature to an api.CommitUser
func ToCommitUser(sig *git.Signature) *api.CommitUser {
return &api.CommitUser{
Identity: api.Identity{
Name: sig.Name,
Email: sig.Email,
},
Date: sig.When.UTC().Format(time.RFC3339),
}
}
// ToCommitMeta convert a git.Tag to an api.CommitMeta
func ToCommitMeta(repo *models.Repository, tag *git.Tag) *api.CommitMeta {
return &api.CommitMeta{
SHA: tag.ID.String(),
// TODO: Add the /commits API endpoint and use it here (https://developer.github.com/v3/repos/commits/#get-a-single-commit)
URL: util.URLJoin(repo.APIURL(), "git/commits", tag.ID.String()),
}
}

View file

@ -100,8 +100,7 @@ func getGitRefsInternal(ctx *context.APIContext, filter string) {
Object: &api.GitObject{
SHA: refs[i].Object.String(),
Type: refs[i].Type,
// TODO: Add commit/tag info URL
//URL: ctx.Repo.Repository.APIURL() + "/git/" + refs[i].Type + "s/" + refs[i].Object.String(),
URL: ctx.Repo.Repository.APIURL() + "/git/" + refs[i].Type + "s/" + refs[i].Object.String(),
},
}
}

View file

@ -7,6 +7,7 @@ package repo
import (
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/routers/api/v1/convert"
"net/http"
api "code.gitea.io/gitea/modules/structs"
)
@ -45,3 +46,47 @@ func ListTags(ctx *context.APIContext) {
ctx.JSON(200, &apiTags)
}
// GetTag get the tag of a repository.
func GetTag(ctx *context.APIContext) {
// swagger:operation GET /repos/{owner}/{repo}/git/tags/{sha} repository GetTag
// ---
// summary: Gets the tag of a repository.
// 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: sha
// in: path
// description: sha of the tag
// type: string
// required: true
// responses:
// "200":
// "$ref": "#/responses/AnnotatedTag"
sha := ctx.Params("sha")
if len(sha) == 0 {
ctx.Error(http.StatusBadRequest, "", "SHA not provided")
return
}
if tag, err := ctx.Repo.GitRepo.GetAnnotatedTag(sha); err != nil {
ctx.Error(http.StatusBadRequest, "GetTag", err)
} else {
commit, err := tag.Commit()
if err != nil {
ctx.Error(http.StatusBadRequest, "GetTag", err)
}
ctx.JSON(http.StatusOK, convert.ToAnnotatedTag(ctx.Repo.Repository, tag, commit))
}
}

View file

@ -38,11 +38,25 @@ type swaggerResponseBranchList struct {
// TagList
// swagger:response TagList
type swaggerReponseTagList struct {
type swaggerResponseTagList struct {
// in:body
Body []api.Tag `json:"body"`
}
// Tag
// swagger:response Tag
type swaggerResponseTag struct {
// in:body
Body api.Tag `json:"body"`
}
// AnnotatedTag
// swagger:response AnnotatedTag
type swaggerResponseAnnotatedTag struct {
// in:body
Body api.AnnotatedTag `json:"body"`
}
// Reference
// swagger:response Reference
type swaggerResponseReference struct {