close #15373tags/v1.16.0-rc1
@@ -0,0 +1,39 @@ | |||
// Copyright 2021 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" | |||
api "code.gitea.io/gitea/modules/structs" | |||
"github.com/stretchr/testify/assert" | |||
) | |||
func TestAPIReposGitNotes(t *testing.T) { | |||
onGiteaRun(t, func(*testing.T, *url.URL) { | |||
user := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User) | |||
// Login as User2. | |||
session := loginUser(t, user.Name) | |||
token := getTokenForLoggedInUser(t, session) | |||
// check invalid requests | |||
req := NewRequestf(t, "GET", "/api/v1/repos/%s/repo1/git/notes/12345?token=%s", user.Name, token) | |||
session.MakeRequest(t, req, http.StatusNotFound) | |||
req = NewRequestf(t, "GET", "/api/v1/repos/%s/repo1/git/notes/..?token=%s", user.Name, token) | |||
session.MakeRequest(t, req, http.StatusUnprocessableEntity) | |||
// check valid request | |||
req = NewRequestf(t, "GET", "/api/v1/repos/%s/repo1/git/notes/65f1bf27bc3bf70f64657658635e66094edbcb4d?token=%s", user.Name, token) | |||
resp := session.MakeRequest(t, req, http.StatusOK) | |||
var apiData api.Note | |||
DecodeJSON(t, resp, &apiData) | |||
assert.Equal(t, "This is a test note\n", apiData.Message) | |||
}) | |||
} |
@@ -0,0 +1 @@ | |||
3fa2f829675543ecfc16b2891aebe8bf0608a8f4 |
@@ -10,19 +10,24 @@ import ( | |||
"context" | |||
"io/ioutil" | |||
"code.gitea.io/gitea/modules/log" | |||
"github.com/go-git/go-git/v5/plumbing/object" | |||
) | |||
// GetNote retrieves the git-notes data for a given commit. | |||
func GetNote(ctx context.Context, repo *Repository, commitID string, note *Note) error { | |||
log.Trace("Searching for git note corresponding to the commit %q in the repository %q", commitID, repo.Path) | |||
notes, err := repo.GetCommit(NotesRef) | |||
if err != nil { | |||
log.Error("Unable to get commit from ref %q. Error: %v", NotesRef, err) | |||
return err | |||
} | |||
remainingCommitID := commitID | |||
path := "" | |||
currentTree := notes.Tree.gogitTree | |||
log.Trace("Found tree with ID %q while searching for git note corresponding to the commit %q", currentTree.Entries[0].Name, commitID) | |||
var file *object.File | |||
for len(remainingCommitID) > 2 { | |||
file, err = currentTree.File(remainingCommitID) | |||
@@ -39,6 +44,7 @@ func GetNote(ctx context.Context, repo *Repository, commitID string, note *Note) | |||
if err == object.ErrDirectoryNotFound { | |||
return ErrNotExist{ID: remainingCommitID, RelPath: path} | |||
} | |||
log.Error("Unable to find git note corresponding to the commit %q. Error: %v", commitID, err) | |||
return err | |||
} | |||
} | |||
@@ -46,12 +52,14 @@ func GetNote(ctx context.Context, repo *Repository, commitID string, note *Note) | |||
blob := file.Blob | |||
dataRc, err := blob.Reader() | |||
if err != nil { | |||
log.Error("Unable to read blob with ID %q. Error: %v", blob.ID, err) | |||
return err | |||
} | |||
defer dataRc.Close() | |||
d, err := ioutil.ReadAll(dataRc) | |||
if err != nil { | |||
log.Error("Unable to read blob with ID %q. Error: %v", blob.ID, err) | |||
return err | |||
} | |||
note.Message = d | |||
@@ -68,6 +76,7 @@ func GetNote(ctx context.Context, repo *Repository, commitID string, note *Note) | |||
lastCommits, err := GetLastCommitForPaths(ctx, commitNode, "", []string{path}) | |||
if err != nil { | |||
log.Error("Unable to get the commit for the path %q. Error: %v", path, err) | |||
return err | |||
} | |||
note.Commit = convertCommit(lastCommits[path]) |
@@ -10,20 +10,26 @@ import ( | |||
"context" | |||
"io/ioutil" | |||
"strings" | |||
"code.gitea.io/gitea/modules/log" | |||
) | |||
// GetNote retrieves the git-notes data for a given commit. | |||
func GetNote(ctx context.Context, repo *Repository, commitID string, note *Note) error { | |||
log.Trace("Searching for git note corresponding to the commit %q in the repository %q", commitID, repo.Path) | |||
notes, err := repo.GetCommit(NotesRef) | |||
if err != nil { | |||
log.Error("Unable to get commit from ref %q. Error: %v", NotesRef, err) | |||
return err | |||
} | |||
path := "" | |||
tree := ¬es.Tree | |||
log.Trace("Found tree with ID %q while searching for git note corresponding to the commit %q", tree.ID, commitID) | |||
var entry *TreeEntry | |||
originalCommitID := commitID | |||
for len(commitID) > 2 { | |||
entry, err = tree.GetTreeEntryByPath(commitID) | |||
if err == nil { | |||
@@ -36,12 +42,15 @@ func GetNote(ctx context.Context, repo *Repository, commitID string, note *Note) | |||
commitID = commitID[2:] | |||
} | |||
if err != nil { | |||
log.Error("Unable to find git note corresponding to the commit %q. Error: %v", originalCommitID, err) | |||
return err | |||
} | |||
} | |||
dataRc, err := entry.Blob().DataAsync() | |||
blob := entry.Blob() | |||
dataRc, err := blob.DataAsync() | |||
if err != nil { | |||
log.Error("Unable to read blob with ID %q. Error: %v", blob.ID, err) | |||
return err | |||
} | |||
closed := false | |||
@@ -52,6 +61,7 @@ func GetNote(ctx context.Context, repo *Repository, commitID string, note *Note) | |||
}() | |||
d, err := ioutil.ReadAll(dataRc) | |||
if err != nil { | |||
log.Error("Unable to read blob with ID %q. Error: %v", blob.ID, err) | |||
return err | |||
} | |||
_ = dataRc.Close() | |||
@@ -66,6 +76,7 @@ func GetNote(ctx context.Context, repo *Repository, commitID string, note *Note) | |||
lastCommits, err := GetLastCommitForPaths(ctx, notes, treePath, []string{path}) | |||
if err != nil { | |||
log.Error("Unable to get the commit for the path %q. Error: %v", treePath, err) | |||
return err | |||
} | |||
note.Commit = lastCommits[path] |
@@ -0,0 +1,11 @@ | |||
// Copyright 2021 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 structs | |||
// Note contains information related to a git note | |||
type Note struct { | |||
Message string `json:"message"` | |||
Commit *Commit `json:"commit"` | |||
} |
@@ -953,6 +953,7 @@ func Routes() *web.Route { | |||
m.Get("/trees/{sha}", context.RepoRefForAPI, repo.GetTree) | |||
m.Get("/blobs/{sha}", context.RepoRefForAPI, repo.GetBlob) | |||
m.Get("/tags/{sha}", context.RepoRefForAPI, repo.GetAnnotatedTag) | |||
m.Get("/notes/{sha}", repo.GetNote) | |||
}, reqRepoReader(models.UnitTypeCode)) | |||
m.Group("/contents", func() { | |||
m.Get("", repo.GetContentsList) |
@@ -0,0 +1,82 @@ | |||
// Copyright 2021 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 repo | |||
import ( | |||
"fmt" | |||
"net/http" | |||
"code.gitea.io/gitea/modules/context" | |||
"code.gitea.io/gitea/modules/convert" | |||
"code.gitea.io/gitea/modules/git" | |||
api "code.gitea.io/gitea/modules/structs" | |||
"code.gitea.io/gitea/modules/validation" | |||
) | |||
// GetNote Get a note corresponding to a single commit from a repository | |||
func GetNote(ctx *context.APIContext) { | |||
// swagger:operation GET /repos/{owner}/{repo}/git/notes/{sha} repository repoGetNote | |||
// --- | |||
// summary: Get a note corresponding to a single commit from 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: a git ref or commit sha | |||
// type: string | |||
// required: true | |||
// responses: | |||
// "200": | |||
// "$ref": "#/responses/Note" | |||
// "422": | |||
// "$ref": "#/responses/validationError" | |||
// "404": | |||
// "$ref": "#/responses/notFound" | |||
sha := ctx.Params(":sha") | |||
if (validation.GitRefNamePatternInvalid.MatchString(sha) || !validation.CheckGitRefAdditionalRulesValid(sha)) && !git.SHAPattern.MatchString(sha) { | |||
ctx.Error(http.StatusUnprocessableEntity, "no valid ref or sha", fmt.Sprintf("no valid ref or sha: %s", sha)) | |||
return | |||
} | |||
getNote(ctx, sha) | |||
} | |||
func getNote(ctx *context.APIContext, identifier string) { | |||
gitRepo, err := git.OpenRepository(ctx.Repo.Repository.RepoPath()) | |||
if err != nil { | |||
ctx.Error(http.StatusInternalServerError, "OpenRepository", err) | |||
return | |||
} | |||
defer gitRepo.Close() | |||
var note git.Note | |||
err = git.GetNote(ctx, gitRepo, identifier, ¬e) | |||
if err != nil { | |||
if git.IsErrNotExist(err) { | |||
ctx.NotFound(identifier) | |||
return | |||
} | |||
ctx.Error(http.StatusInternalServerError, "GetNote", err) | |||
return | |||
} | |||
cmt, err := convert.ToCommit(ctx.Repo.Repository, note.Commit, nil) | |||
if err != nil { | |||
ctx.Error(http.StatusInternalServerError, "ToCommit", err) | |||
return | |||
} | |||
apiNote := api.Note{Message: string(note.Message), Commit: cmt} | |||
ctx.JSON(http.StatusOK, apiNote) | |||
} |
@@ -254,6 +254,13 @@ type swaggerCommitList struct { | |||
Body []api.Commit `json:"body"` | |||
} | |||
// Note | |||
// swagger:response Note | |||
type swaggerNote struct { | |||
// in: body | |||
Body api.Note `json:"body"` | |||
} | |||
// EmptyRepository | |||
// swagger:response EmptyRepository | |||
type swaggerEmptyRepository struct { |
@@ -3569,6 +3569,52 @@ | |||
} | |||
} | |||
}, | |||
"/repos/{owner}/{repo}/git/notes/{sha}": { | |||
"get": { | |||
"produces": [ | |||
"application/json" | |||
], | |||
"tags": [ | |||
"repository" | |||
], | |||
"summary": "Get a note corresponding to a single commit from a repository", | |||
"operationId": "repoGetNote", | |||
"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": "a git ref or commit sha", | |||
"name": "sha", | |||
"in": "path", | |||
"required": true | |||
} | |||
], | |||
"responses": { | |||
"200": { | |||
"$ref": "#/responses/Note" | |||
}, | |||
"404": { | |||
"$ref": "#/responses/notFound" | |||
}, | |||
"422": { | |||
"$ref": "#/responses/validationError" | |||
} | |||
} | |||
} | |||
}, | |||
"/repos/{owner}/{repo}/git/refs": { | |||
"get": { | |||
"produces": [ | |||
@@ -15453,6 +15499,20 @@ | |||
}, | |||
"x-go-package": "code.gitea.io/gitea/modules/structs" | |||
}, | |||
"Note": { | |||
"description": "Note contains information related to a git note", | |||
"type": "object", | |||
"properties": { | |||
"commit": { | |||
"$ref": "#/definitions/Commit" | |||
}, | |||
"message": { | |||
"type": "string", | |||
"x-go-name": "Message" | |||
} | |||
}, | |||
"x-go-package": "code.gitea.io/gitea/modules/structs" | |||
}, | |||
"NotificationCount": { | |||
"description": "NotificationCount number of unread notifications", | |||
"type": "object", | |||
@@ -17412,6 +17472,12 @@ | |||
} | |||
} | |||
}, | |||
"Note": { | |||
"description": "Note", | |||
"schema": { | |||
"$ref": "#/definitions/Note" | |||
} | |||
}, | |||
"NotificationCount": { | |||
"description": "Number of unread notifications", | |||
"schema": { |