mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-31 10:56:10 +01:00 
			
		
		
		
	
							
								
								
									
										39
									
								
								integrations/api_repo_git_notes_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								integrations/api_repo_git_notes_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -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) | ||||
| 	}) | ||||
| } | ||||
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							| @@ -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] | ||||
|   | ||||
							
								
								
									
										11
									
								
								modules/structs/repo_note.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								modules/structs/repo_note.go
									
									
									
									
									
										Normal file
									
								
							| @@ -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) | ||||
|   | ||||
							
								
								
									
										82
									
								
								routers/api/v1/repo/notes.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								routers/api/v1/repo/notes.go
									
									
									
									
									
										Normal file
									
								
							| @@ -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": { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user