mirror of https://github.com/go-gitea/gitea.git
merge upstream & fix conflicts
This commit is contained in:
commit
57744b54c4
|
@ -0,0 +1,106 @@
|
|||
// Copyright 2020 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 (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestAPINotification(t *testing.T) {
|
||||
defer prepareTestEnv(t)()
|
||||
|
||||
user2 := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
|
||||
repo1 := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
|
||||
thread5 := models.AssertExistsAndLoadBean(t, &models.Notification{ID: 5}).(*models.Notification)
|
||||
assert.NoError(t, thread5.LoadAttributes())
|
||||
session := loginUser(t, user2.Name)
|
||||
token := getTokenForLoggedInUser(t, session)
|
||||
|
||||
// -- GET /notifications --
|
||||
// test filter
|
||||
since := "2000-01-01T00%3A50%3A01%2B00%3A00" //946687801
|
||||
req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/notifications?since=%s&token=%s", since, token))
|
||||
resp := session.MakeRequest(t, req, http.StatusOK)
|
||||
var apiNL []api.NotificationThread
|
||||
DecodeJSON(t, resp, &apiNL)
|
||||
|
||||
assert.Len(t, apiNL, 1)
|
||||
assert.EqualValues(t, 5, apiNL[0].ID)
|
||||
|
||||
// test filter
|
||||
before := "2000-01-01T01%3A06%3A59%2B00%3A00" //946688819
|
||||
|
||||
req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/notifications?all=%s&before=%s&token=%s", "true", before, token))
|
||||
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||
DecodeJSON(t, resp, &apiNL)
|
||||
|
||||
assert.Len(t, apiNL, 3)
|
||||
assert.EqualValues(t, 4, apiNL[0].ID)
|
||||
assert.EqualValues(t, true, apiNL[0].Unread)
|
||||
assert.EqualValues(t, false, apiNL[0].Pinned)
|
||||
assert.EqualValues(t, 3, apiNL[1].ID)
|
||||
assert.EqualValues(t, false, apiNL[1].Unread)
|
||||
assert.EqualValues(t, true, apiNL[1].Pinned)
|
||||
assert.EqualValues(t, 2, apiNL[2].ID)
|
||||
assert.EqualValues(t, false, apiNL[2].Unread)
|
||||
assert.EqualValues(t, false, apiNL[2].Pinned)
|
||||
|
||||
// -- GET /repos/{owner}/{repo}/notifications --
|
||||
req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/%s/notifications?token=%s", user2.Name, repo1.Name, token))
|
||||
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||
DecodeJSON(t, resp, &apiNL)
|
||||
|
||||
assert.Len(t, apiNL, 1)
|
||||
assert.EqualValues(t, 4, apiNL[0].ID)
|
||||
|
||||
// -- GET /notifications/threads/{id} --
|
||||
// get forbidden
|
||||
req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/notifications/threads/%d?token=%s", 1, token))
|
||||
resp = session.MakeRequest(t, req, http.StatusForbidden)
|
||||
|
||||
// get own
|
||||
req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/notifications/threads/%d?token=%s", thread5.ID, token))
|
||||
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||
var apiN api.NotificationThread
|
||||
DecodeJSON(t, resp, &apiN)
|
||||
|
||||
assert.EqualValues(t, 5, apiN.ID)
|
||||
assert.EqualValues(t, false, apiN.Pinned)
|
||||
assert.EqualValues(t, true, apiN.Unread)
|
||||
assert.EqualValues(t, "issue4", apiN.Subject.Title)
|
||||
assert.EqualValues(t, "Issue", apiN.Subject.Type)
|
||||
assert.EqualValues(t, thread5.Issue.APIURL(), apiN.Subject.URL)
|
||||
assert.EqualValues(t, thread5.Repository.HTMLURL(), apiN.Repository.HTMLURL)
|
||||
|
||||
// -- mark notifications as read --
|
||||
req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/notifications?token=%s", token))
|
||||
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||
DecodeJSON(t, resp, &apiNL)
|
||||
assert.Len(t, apiNL, 2)
|
||||
|
||||
lastReadAt := "2000-01-01T00%3A50%3A01%2B00%3A00" //946687801 <- only Notification 4 is in this filter ...
|
||||
req = NewRequest(t, "PUT", fmt.Sprintf("/api/v1/repos/%s/%s/notifications?last_read_at=%s&token=%s", user2.Name, repo1.Name, lastReadAt, token))
|
||||
resp = session.MakeRequest(t, req, http.StatusResetContent)
|
||||
|
||||
req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/notifications?token=%s", token))
|
||||
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||
DecodeJSON(t, resp, &apiNL)
|
||||
assert.Len(t, apiNL, 1)
|
||||
|
||||
// -- PATCH /notifications/threads/{id} --
|
||||
req = NewRequest(t, "PATCH", fmt.Sprintf("/api/v1/notifications/threads/%d?token=%s", thread5.ID, token))
|
||||
resp = session.MakeRequest(t, req, http.StatusResetContent)
|
||||
|
||||
assert.Equal(t, models.NotificationStatusUnread, thread5.Status)
|
||||
thread5 = models.AssertExistsAndLoadBean(t, &models.Notification{ID: 5}).(*models.Notification)
|
||||
assert.Equal(t, models.NotificationStatusRead, thread5.Status)
|
||||
}
|
|
@ -71,19 +71,33 @@ func TestAPITeam(t *testing.T) {
|
|||
teamID := apiTeam.ID
|
||||
|
||||
// Edit team.
|
||||
editDescription := "team 1"
|
||||
editFalse := false
|
||||
teamToEdit := &api.EditTeamOption{
|
||||
Name: "teamone",
|
||||
Description: "team 1",
|
||||
IncludesAllRepositories: false,
|
||||
Description: &editDescription,
|
||||
Permission: "admin",
|
||||
IncludesAllRepositories: &editFalse,
|
||||
Units: []string{"repo.code", "repo.pulls", "repo.releases"},
|
||||
}
|
||||
|
||||
req = NewRequestWithJSON(t, "PATCH", fmt.Sprintf("/api/v1/teams/%d?token=%s", teamID, token), teamToEdit)
|
||||
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||
DecodeJSON(t, resp, &apiTeam)
|
||||
checkTeamResponse(t, &apiTeam, teamToEdit.Name, teamToEdit.Description, teamToEdit.IncludesAllRepositories,
|
||||
checkTeamResponse(t, &apiTeam, teamToEdit.Name, *teamToEdit.Description, *teamToEdit.IncludesAllRepositories,
|
||||
teamToEdit.Permission, teamToEdit.Units)
|
||||
checkTeamBean(t, apiTeam.ID, teamToEdit.Name, teamToEdit.Description, teamToEdit.IncludesAllRepositories,
|
||||
checkTeamBean(t, apiTeam.ID, teamToEdit.Name, *teamToEdit.Description, *teamToEdit.IncludesAllRepositories,
|
||||
teamToEdit.Permission, teamToEdit.Units)
|
||||
|
||||
// Edit team Description only
|
||||
editDescription = "first team"
|
||||
teamToEditDesc := api.EditTeamOption{Description: &editDescription}
|
||||
req = NewRequestWithJSON(t, "PATCH", fmt.Sprintf("/api/v1/teams/%d?token=%s", teamID, token), teamToEditDesc)
|
||||
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||
DecodeJSON(t, resp, &apiTeam)
|
||||
checkTeamResponse(t, &apiTeam, teamToEdit.Name, *teamToEditDesc.Description, *teamToEdit.IncludesAllRepositories,
|
||||
teamToEdit.Permission, teamToEdit.Units)
|
||||
checkTeamBean(t, apiTeam.ID, teamToEdit.Name, *teamToEditDesc.Description, *teamToEdit.IncludesAllRepositories,
|
||||
teamToEdit.Permission, teamToEdit.Units)
|
||||
|
||||
// Read team.
|
||||
|
@ -91,7 +105,7 @@ func TestAPITeam(t *testing.T) {
|
|||
req = NewRequestf(t, "GET", "/api/v1/teams/%d?token="+token, teamID)
|
||||
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||
DecodeJSON(t, resp, &apiTeam)
|
||||
checkTeamResponse(t, &apiTeam, teamRead.Name, teamRead.Description, teamRead.IncludesAllRepositories,
|
||||
checkTeamResponse(t, &apiTeam, teamRead.Name, *teamToEditDesc.Description, teamRead.IncludesAllRepositories,
|
||||
teamRead.Authorize.String(), teamRead.GetUnitNames())
|
||||
|
||||
// Delete team.
|
||||
|
|
126
models/action.go
126
models/action.go
|
@ -13,10 +13,8 @@ import (
|
|||
"time"
|
||||
|
||||
"code.gitea.io/gitea/modules/base"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
|
||||
"github.com/unknwon/com"
|
||||
|
@ -284,130 +282,6 @@ func (a *Action) GetIssueContent() string {
|
|||
return issue.Content
|
||||
}
|
||||
|
||||
// PushCommit represents a commit in a push operation.
|
||||
type PushCommit struct {
|
||||
Sha1 string
|
||||
Message string
|
||||
AuthorEmail string
|
||||
AuthorName string
|
||||
CommitterEmail string
|
||||
CommitterName string
|
||||
Timestamp time.Time
|
||||
}
|
||||
|
||||
// PushCommits represents list of commits in a push operation.
|
||||
type PushCommits struct {
|
||||
Len int
|
||||
Commits []*PushCommit
|
||||
CompareURL string
|
||||
|
||||
avatars map[string]string
|
||||
emailUsers map[string]*User
|
||||
}
|
||||
|
||||
// NewPushCommits creates a new PushCommits object.
|
||||
func NewPushCommits() *PushCommits {
|
||||
return &PushCommits{
|
||||
avatars: make(map[string]string),
|
||||
emailUsers: make(map[string]*User),
|
||||
}
|
||||
}
|
||||
|
||||
// ToAPIPayloadCommits converts a PushCommits object to
|
||||
// api.PayloadCommit format.
|
||||
func (pc *PushCommits) ToAPIPayloadCommits(repoPath, repoLink string) ([]*api.PayloadCommit, error) {
|
||||
commits := make([]*api.PayloadCommit, len(pc.Commits))
|
||||
|
||||
if pc.emailUsers == nil {
|
||||
pc.emailUsers = make(map[string]*User)
|
||||
}
|
||||
var err error
|
||||
for i, commit := range pc.Commits {
|
||||
authorUsername := ""
|
||||
author, ok := pc.emailUsers[commit.AuthorEmail]
|
||||
if !ok {
|
||||
author, err = GetUserByEmail(commit.AuthorEmail)
|
||||
if err == nil {
|
||||
authorUsername = author.Name
|
||||
pc.emailUsers[commit.AuthorEmail] = author
|
||||
}
|
||||
} else {
|
||||
authorUsername = author.Name
|
||||
}
|
||||
|
||||
committerUsername := ""
|
||||
committer, ok := pc.emailUsers[commit.CommitterEmail]
|
||||
if !ok {
|
||||
committer, err = GetUserByEmail(commit.CommitterEmail)
|
||||
if err == nil {
|
||||
// TODO: check errors other than email not found.
|
||||
committerUsername = committer.Name
|
||||
pc.emailUsers[commit.CommitterEmail] = committer
|
||||
}
|
||||
} else {
|
||||
committerUsername = committer.Name
|
||||
}
|
||||
|
||||
fileStatus, err := git.GetCommitFileStatus(repoPath, commit.Sha1)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("FileStatus [commit_sha1: %s]: %v", commit.Sha1, err)
|
||||
}
|
||||
|
||||
commits[i] = &api.PayloadCommit{
|
||||
ID: commit.Sha1,
|
||||
Message: commit.Message,
|
||||
URL: fmt.Sprintf("%s/commit/%s", repoLink, commit.Sha1),
|
||||
Author: &api.PayloadUser{
|
||||
Name: commit.AuthorName,
|
||||
Email: commit.AuthorEmail,
|
||||
UserName: authorUsername,
|
||||
},
|
||||
Committer: &api.PayloadUser{
|
||||
Name: commit.CommitterName,
|
||||
Email: commit.CommitterEmail,
|
||||
UserName: committerUsername,
|
||||
},
|
||||
Added: fileStatus.Added,
|
||||
Removed: fileStatus.Removed,
|
||||
Modified: fileStatus.Modified,
|
||||
Timestamp: commit.Timestamp,
|
||||
}
|
||||
}
|
||||
return commits, nil
|
||||
}
|
||||
|
||||
// AvatarLink tries to match user in database with e-mail
|
||||
// in order to show custom avatar, and falls back to general avatar link.
|
||||
func (pc *PushCommits) AvatarLink(email string) string {
|
||||
if pc.avatars == nil {
|
||||
pc.avatars = make(map[string]string)
|
||||
}
|
||||
avatar, ok := pc.avatars[email]
|
||||
if ok {
|
||||
return avatar
|
||||
}
|
||||
|
||||
u, ok := pc.emailUsers[email]
|
||||
if !ok {
|
||||
var err error
|
||||
u, err = GetUserByEmail(email)
|
||||
if err != nil {
|
||||
pc.avatars[email] = base.AvatarLink(email)
|
||||
if !IsErrUserNotExist(err) {
|
||||
log.Error("GetUserByEmail: %v", err)
|
||||
return ""
|
||||
}
|
||||
} else {
|
||||
pc.emailUsers[email] = u
|
||||
}
|
||||
}
|
||||
if u != nil {
|
||||
pc.avatars[email] = u.RelAvatarLink()
|
||||
}
|
||||
|
||||
return pc.avatars[email]
|
||||
}
|
||||
|
||||
// GetFeedsOptions options for retrieving feeds
|
||||
type GetFeedsOptions struct {
|
||||
RequestedUser *User // the user we want activity for
|
||||
|
|
|
@ -27,106 +27,6 @@ func TestAction_GetRepoLink(t *testing.T) {
|
|||
assert.Equal(t, expected, action.GetRepoLink())
|
||||
}
|
||||
|
||||
func TestPushCommits_ToAPIPayloadCommits(t *testing.T) {
|
||||
pushCommits := NewPushCommits()
|
||||
pushCommits.Commits = []*PushCommit{
|
||||
{
|
||||
Sha1: "69554a6",
|
||||
CommitterEmail: "user2@example.com",
|
||||
CommitterName: "User2",
|
||||
AuthorEmail: "user2@example.com",
|
||||
AuthorName: "User2",
|
||||
Message: "not signed commit",
|
||||
},
|
||||
{
|
||||
Sha1: "27566bd",
|
||||
CommitterEmail: "user2@example.com",
|
||||
CommitterName: "User2",
|
||||
AuthorEmail: "user2@example.com",
|
||||
AuthorName: "User2",
|
||||
Message: "good signed commit (with not yet validated email)",
|
||||
},
|
||||
{
|
||||
Sha1: "5099b81",
|
||||
CommitterEmail: "user2@example.com",
|
||||
CommitterName: "User2",
|
||||
AuthorEmail: "user2@example.com",
|
||||
AuthorName: "User2",
|
||||
Message: "good signed commit",
|
||||
},
|
||||
}
|
||||
pushCommits.Len = len(pushCommits.Commits)
|
||||
|
||||
repo := AssertExistsAndLoadBean(t, &Repository{ID: 16}).(*Repository)
|
||||
payloadCommits, err := pushCommits.ToAPIPayloadCommits(repo.RepoPath(), "/user2/repo16")
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 3, len(payloadCommits))
|
||||
|
||||
assert.Equal(t, "69554a6", payloadCommits[0].ID)
|
||||
assert.Equal(t, "not signed commit", payloadCommits[0].Message)
|
||||
assert.Equal(t, "/user2/repo16/commit/69554a6", payloadCommits[0].URL)
|
||||
assert.Equal(t, "User2", payloadCommits[0].Committer.Name)
|
||||
assert.Equal(t, "user2", payloadCommits[0].Committer.UserName)
|
||||
assert.Equal(t, "User2", payloadCommits[0].Author.Name)
|
||||
assert.Equal(t, "user2", payloadCommits[0].Author.UserName)
|
||||
assert.EqualValues(t, []string{}, payloadCommits[0].Added)
|
||||
assert.EqualValues(t, []string{}, payloadCommits[0].Removed)
|
||||
assert.EqualValues(t, []string{"readme.md"}, payloadCommits[0].Modified)
|
||||
|
||||
assert.Equal(t, "27566bd", payloadCommits[1].ID)
|
||||
assert.Equal(t, "good signed commit (with not yet validated email)", payloadCommits[1].Message)
|
||||
assert.Equal(t, "/user2/repo16/commit/27566bd", payloadCommits[1].URL)
|
||||
assert.Equal(t, "User2", payloadCommits[1].Committer.Name)
|
||||
assert.Equal(t, "user2", payloadCommits[1].Committer.UserName)
|
||||
assert.Equal(t, "User2", payloadCommits[1].Author.Name)
|
||||
assert.Equal(t, "user2", payloadCommits[1].Author.UserName)
|
||||
assert.EqualValues(t, []string{}, payloadCommits[1].Added)
|
||||
assert.EqualValues(t, []string{}, payloadCommits[1].Removed)
|
||||
assert.EqualValues(t, []string{"readme.md"}, payloadCommits[1].Modified)
|
||||
|
||||
assert.Equal(t, "5099b81", payloadCommits[2].ID)
|
||||
assert.Equal(t, "good signed commit", payloadCommits[2].Message)
|
||||
assert.Equal(t, "/user2/repo16/commit/5099b81", payloadCommits[2].URL)
|
||||
assert.Equal(t, "User2", payloadCommits[2].Committer.Name)
|
||||
assert.Equal(t, "user2", payloadCommits[2].Committer.UserName)
|
||||
assert.Equal(t, "User2", payloadCommits[2].Author.Name)
|
||||
assert.Equal(t, "user2", payloadCommits[2].Author.UserName)
|
||||
assert.EqualValues(t, []string{"readme.md"}, payloadCommits[2].Added)
|
||||
assert.EqualValues(t, []string{}, payloadCommits[2].Removed)
|
||||
assert.EqualValues(t, []string{}, payloadCommits[2].Modified)
|
||||
}
|
||||
|
||||
func TestPushCommits_AvatarLink(t *testing.T) {
|
||||
pushCommits := NewPushCommits()
|
||||
pushCommits.Commits = []*PushCommit{
|
||||
{
|
||||
Sha1: "abcdef1",
|
||||
CommitterEmail: "user2@example.com",
|
||||
CommitterName: "User Two",
|
||||
AuthorEmail: "user4@example.com",
|
||||
AuthorName: "User Four",
|
||||
Message: "message1",
|
||||
},
|
||||
{
|
||||
Sha1: "abcdef2",
|
||||
CommitterEmail: "user2@example.com",
|
||||
CommitterName: "User Two",
|
||||
AuthorEmail: "user2@example.com",
|
||||
AuthorName: "User Two",
|
||||
Message: "message2",
|
||||
},
|
||||
}
|
||||
pushCommits.Len = len(pushCommits.Commits)
|
||||
|
||||
assert.Equal(t,
|
||||
"/suburl/user/avatar/user2/-1",
|
||||
pushCommits.AvatarLink("user2@example.com"))
|
||||
|
||||
assert.Equal(t,
|
||||
"https://secure.gravatar.com/avatar/19ade630b94e1e0535b3df7387434154?d=identicon",
|
||||
pushCommits.AvatarLink("nonexistent@example.com"))
|
||||
}
|
||||
|
||||
func TestGetFeeds(t *testing.T) {
|
||||
// test with an individual user
|
||||
assert.NoError(t, PrepareTestDatabase())
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
updated_by: 2
|
||||
issue_id: 1
|
||||
created_unix: 946684800
|
||||
updated_unix: 946684800
|
||||
updated_unix: 946684820
|
||||
|
||||
-
|
||||
id: 2
|
||||
|
@ -17,8 +17,8 @@
|
|||
source: 1 # issue
|
||||
updated_by: 1
|
||||
issue_id: 2
|
||||
created_unix: 946684800
|
||||
updated_unix: 946684800
|
||||
created_unix: 946685800
|
||||
updated_unix: 946685820
|
||||
|
||||
-
|
||||
id: 3
|
||||
|
@ -27,9 +27,9 @@
|
|||
status: 3 # pinned
|
||||
source: 1 # issue
|
||||
updated_by: 1
|
||||
issue_id: 2
|
||||
created_unix: 946684800
|
||||
updated_unix: 946684800
|
||||
issue_id: 3
|
||||
created_unix: 946686800
|
||||
updated_unix: 946686800
|
||||
|
||||
-
|
||||
id: 4
|
||||
|
@ -38,6 +38,17 @@
|
|||
status: 1 # unread
|
||||
source: 1 # issue
|
||||
updated_by: 1
|
||||
issue_id: 2
|
||||
created_unix: 946684800
|
||||
updated_unix: 946684800
|
||||
issue_id: 5
|
||||
created_unix: 946687800
|
||||
updated_unix: 946687800
|
||||
|
||||
-
|
||||
id: 5
|
||||
user_id: 2
|
||||
repo_id: 2
|
||||
status: 1 # unread
|
||||
source: 1 # issue
|
||||
updated_by: 5
|
||||
issue_id: 4
|
||||
created_unix: 946688800
|
||||
updated_unix: 946688820
|
||||
|
|
|
@ -843,6 +843,20 @@ func (issue *Issue) GetLastEventLabel() string {
|
|||
return "repo.issues.opened_by"
|
||||
}
|
||||
|
||||
// GetLastComment return last comment for the current issue.
|
||||
func (issue *Issue) GetLastComment() (*Comment, error) {
|
||||
var c Comment
|
||||
exist, err := x.Where("type = ?", CommentTypeComment).
|
||||
And("issue_id = ?", issue.ID).Desc("id").Get(&c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !exist {
|
||||
return nil, nil
|
||||
}
|
||||
return &c, nil
|
||||
}
|
||||
|
||||
// GetLastEventLabelFake returns the localization label for the current issue without providing a link in the username.
|
||||
func (issue *Issue) GetLastEventLabelFake() string {
|
||||
if issue.IsClosed {
|
||||
|
|
|
@ -8,6 +8,7 @@ package models
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
|
@ -235,6 +236,22 @@ func (c *Comment) HTMLURL() string {
|
|||
return fmt.Sprintf("%s#%s", c.Issue.HTMLURL(), c.HashTag())
|
||||
}
|
||||
|
||||
// APIURL formats a API-string to the issue-comment
|
||||
func (c *Comment) APIURL() string {
|
||||
err := c.LoadIssue()
|
||||
if err != nil { // Silently dropping errors :unamused:
|
||||
log.Error("LoadIssue(%d): %v", c.IssueID, err)
|
||||
return ""
|
||||
}
|
||||
err = c.Issue.loadRepo(x)
|
||||
if err != nil { // Silently dropping errors :unamused:
|
||||
log.Error("loadRepo(%d): %v", c.Issue.RepoID, err)
|
||||
return ""
|
||||
}
|
||||
|
||||
return c.Issue.Repo.APIURL() + "/" + path.Join("issues/comments", fmt.Sprint(c.ID))
|
||||
}
|
||||
|
||||
// IssueURL formats a URL-string to the issue
|
||||
func (c *Comment) IssueURL() string {
|
||||
err := c.LoadIssue()
|
||||
|
|
|
@ -293,6 +293,8 @@ var migrations = []Migration{
|
|||
// v118 -> v119
|
||||
NewMigration("Add commit id and stale to reviews", addReviewCommitAndStale),
|
||||
// v119 -> v120
|
||||
NewMigration("Fix migrated repositories' git service type", fixMigratedRepositoryServiceType),
|
||||
// v120 -> v121
|
||||
NewMigration("add is_restricted column for users table", addIsRestricted),
|
||||
}
|
||||
|
||||
|
|
|
@ -4,14 +4,13 @@
|
|||
|
||||
package migrations
|
||||
|
||||
import "xorm.io/xorm"
|
||||
import (
|
||||
"code.gitea.io/gitea/modules/structs"
|
||||
|
||||
func addIsRestricted(x *xorm.Engine) error {
|
||||
// User see models/user.go
|
||||
type User struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
IsRestricted bool `xorm:"NOT NULL DEFAULT false"`
|
||||
}
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
return x.Sync2(new(User))
|
||||
func fixMigratedRepositoryServiceType(x *xorm.Engine) error {
|
||||
_, err := x.Exec("UPDATE repository SET original_service_type = ? WHERE original_url LIKE 'https://github.com/%'", structs.GithubService)
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
// Copyright 2020 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 migrations
|
||||
|
||||
import "xorm.io/xorm"
|
||||
|
||||
func addIsRestricted(x *xorm.Engine) error {
|
||||
// User see models/user.go
|
||||
type User struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
IsRestricted bool `xorm:"NOT NULL DEFAULT false"`
|
||||
}
|
||||
|
||||
return x.Sync2(new(User))
|
||||
}
|
|
@ -6,8 +6,14 @@ package models
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"path"
|
||||
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
|
||||
"xorm.io/builder"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
type (
|
||||
|
@ -47,17 +53,67 @@ type Notification struct {
|
|||
IssueID int64 `xorm:"INDEX NOT NULL"`
|
||||
CommitID string `xorm:"INDEX"`
|
||||
CommentID int64
|
||||
Comment *Comment `xorm:"-"`
|
||||
|
||||
UpdatedBy int64 `xorm:"INDEX NOT NULL"`
|
||||
|
||||
Issue *Issue `xorm:"-"`
|
||||
Repository *Repository `xorm:"-"`
|
||||
Comment *Comment `xorm:"-"`
|
||||
User *User `xorm:"-"`
|
||||
|
||||
CreatedUnix timeutil.TimeStamp `xorm:"created INDEX NOT NULL"`
|
||||
UpdatedUnix timeutil.TimeStamp `xorm:"updated INDEX NOT NULL"`
|
||||
}
|
||||
|
||||
// FindNotificationOptions represent the filters for notifications. If an ID is 0 it will be ignored.
|
||||
type FindNotificationOptions struct {
|
||||
UserID int64
|
||||
RepoID int64
|
||||
IssueID int64
|
||||
Status NotificationStatus
|
||||
UpdatedAfterUnix int64
|
||||
UpdatedBeforeUnix int64
|
||||
}
|
||||
|
||||
// ToCond will convert each condition into a xorm-Cond
|
||||
func (opts *FindNotificationOptions) ToCond() builder.Cond {
|
||||
cond := builder.NewCond()
|
||||
if opts.UserID != 0 {
|
||||
cond = cond.And(builder.Eq{"notification.user_id": opts.UserID})
|
||||
}
|
||||
if opts.RepoID != 0 {
|
||||
cond = cond.And(builder.Eq{"notification.repo_id": opts.RepoID})
|
||||
}
|
||||
if opts.IssueID != 0 {
|
||||
cond = cond.And(builder.Eq{"notification.issue_id": opts.IssueID})
|
||||
}
|
||||
if opts.Status != 0 {
|
||||
cond = cond.And(builder.Eq{"notification.status": opts.Status})
|
||||
}
|
||||
if opts.UpdatedAfterUnix != 0 {
|
||||
cond = cond.And(builder.Gte{"notification.updated_unix": opts.UpdatedAfterUnix})
|
||||
}
|
||||
if opts.UpdatedBeforeUnix != 0 {
|
||||
cond = cond.And(builder.Lte{"notification.updated_unix": opts.UpdatedBeforeUnix})
|
||||
}
|
||||
return cond
|
||||
}
|
||||
|
||||
// ToSession will convert the given options to a xorm Session by using the conditions from ToCond and joining with issue table if required
|
||||
func (opts *FindNotificationOptions) ToSession(e Engine) *xorm.Session {
|
||||
return e.Where(opts.ToCond())
|
||||
}
|
||||
|
||||
func getNotifications(e Engine, options FindNotificationOptions) (nl NotificationList, err error) {
|
||||
err = options.ToSession(e).OrderBy("notification.updated_unix DESC").Find(&nl)
|
||||
return
|
||||
}
|
||||
|
||||
// GetNotifications returns all notifications that fit to the given options.
|
||||
func GetNotifications(opts FindNotificationOptions) (NotificationList, error) {
|
||||
return getNotifications(x, opts)
|
||||
}
|
||||
|
||||
// CreateOrUpdateIssueNotifications creates an issue notification
|
||||
// for each watcher, or updates it if already exists
|
||||
func CreateOrUpdateIssueNotifications(issueID, commentID int64, notificationAuthorID int64) error {
|
||||
|
@ -238,22 +294,124 @@ func notificationsForUser(e Engine, user *User, statuses []NotificationStatus, p
|
|||
return
|
||||
}
|
||||
|
||||
// APIFormat converts a Notification to api.NotificationThread
|
||||
func (n *Notification) APIFormat() *api.NotificationThread {
|
||||
result := &api.NotificationThread{
|
||||
ID: n.ID,
|
||||
Unread: !(n.Status == NotificationStatusRead || n.Status == NotificationStatusPinned),
|
||||
Pinned: n.Status == NotificationStatusPinned,
|
||||
UpdatedAt: n.UpdatedUnix.AsTime(),
|
||||
URL: n.APIURL(),
|
||||
}
|
||||
|
||||
//since user only get notifications when he has access to use minimal access mode
|
||||
if n.Repository != nil {
|
||||
result.Repository = n.Repository.APIFormat(AccessModeRead)
|
||||
}
|
||||
|
||||
//handle Subject
|
||||
switch n.Source {
|
||||
case NotificationSourceIssue:
|
||||
result.Subject = &api.NotificationSubject{Type: "Issue"}
|
||||
if n.Issue != nil {
|
||||
result.Subject.Title = n.Issue.Title
|
||||
result.Subject.URL = n.Issue.APIURL()
|
||||
comment, err := n.Issue.GetLastComment()
|
||||
if err == nil && comment != nil {
|
||||
result.Subject.LatestCommentURL = comment.APIURL()
|
||||
}
|
||||
}
|
||||
case NotificationSourcePullRequest:
|
||||
result.Subject = &api.NotificationSubject{Type: "Pull"}
|
||||
if n.Issue != nil {
|
||||
result.Subject.Title = n.Issue.Title
|
||||
result.Subject.URL = n.Issue.APIURL()
|
||||
comment, err := n.Issue.GetLastComment()
|
||||
if err == nil && comment != nil {
|
||||
result.Subject.LatestCommentURL = comment.APIURL()
|
||||
}
|
||||
}
|
||||
case NotificationSourceCommit:
|
||||
result.Subject = &api.NotificationSubject{
|
||||
Type: "Commit",
|
||||
Title: n.CommitID,
|
||||
}
|
||||
//unused until now
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// LoadAttributes load Repo Issue User and Comment if not loaded
|
||||
func (n *Notification) LoadAttributes() (err error) {
|
||||
return n.loadAttributes(x)
|
||||
}
|
||||
|
||||
func (n *Notification) loadAttributes(e Engine) (err error) {
|
||||
if err = n.loadRepo(e); err != nil {
|
||||
return
|
||||
}
|
||||
if err = n.loadIssue(e); err != nil {
|
||||
return
|
||||
}
|
||||
if err = n.loadUser(e); err != nil {
|
||||
return
|
||||
}
|
||||
if err = n.loadComment(e); err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (n *Notification) loadRepo(e Engine) (err error) {
|
||||
if n.Repository == nil {
|
||||
n.Repository, err = getRepositoryByID(e, n.RepoID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("getRepositoryByID [%d]: %v", n.RepoID, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *Notification) loadIssue(e Engine) (err error) {
|
||||
if n.Issue == nil {
|
||||
n.Issue, err = getIssueByID(e, n.IssueID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("getIssueByID [%d]: %v", n.IssueID, err)
|
||||
}
|
||||
return n.Issue.loadAttributes(e)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *Notification) loadComment(e Engine) (err error) {
|
||||
if n.Comment == nil && n.CommentID > 0 {
|
||||
n.Comment, err = GetCommentByID(n.CommentID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("GetCommentByID [%d]: %v", n.CommentID, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *Notification) loadUser(e Engine) (err error) {
|
||||
if n.User == nil {
|
||||
n.User, err = getUserByID(e, n.UserID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("getUserByID [%d]: %v", n.UserID, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetRepo returns the repo of the notification
|
||||
func (n *Notification) GetRepo() (*Repository, error) {
|
||||
n.Repository = new(Repository)
|
||||
_, err := x.
|
||||
Where("id = ?", n.RepoID).
|
||||
Get(n.Repository)
|
||||
return n.Repository, err
|
||||
return n.Repository, n.loadRepo(x)
|
||||
}
|
||||
|
||||
// GetIssue returns the issue of the notification
|
||||
func (n *Notification) GetIssue() (*Issue, error) {
|
||||
n.Issue = new(Issue)
|
||||
_, err := x.
|
||||
Where("id = ?", n.IssueID).
|
||||
Get(n.Issue)
|
||||
return n.Issue, err
|
||||
return n.Issue, n.loadIssue(x)
|
||||
}
|
||||
|
||||
// HTMLURL formats a URL-string to the notification
|
||||
|
@ -264,9 +422,34 @@ func (n *Notification) HTMLURL() string {
|
|||
return n.Issue.HTMLURL()
|
||||
}
|
||||
|
||||
// APIURL formats a URL-string to the notification
|
||||
func (n *Notification) APIURL() string {
|
||||
return setting.AppURL + path.Join("api/v1/notifications/threads", fmt.Sprintf("%d", n.ID))
|
||||
}
|
||||
|
||||
// NotificationList contains a list of notifications
|
||||
type NotificationList []*Notification
|
||||
|
||||
// APIFormat converts a NotificationList to api.NotificationThread list
|
||||
func (nl NotificationList) APIFormat() []*api.NotificationThread {
|
||||
var result = make([]*api.NotificationThread, 0, len(nl))
|
||||
for _, n := range nl {
|
||||
result = append(result, n.APIFormat())
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// LoadAttributes load Repo Issue User and Comment if not loaded
|
||||
func (nl NotificationList) LoadAttributes() (err error) {
|
||||
for i := 0; i < len(nl); i++ {
|
||||
err = nl[i].LoadAttributes()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (nl NotificationList) getPendingRepoIDs() []int64 {
|
||||
var ids = make(map[int64]struct{}, len(nl))
|
||||
for _, notification := range nl {
|
||||
|
@ -486,7 +669,7 @@ func setNotificationStatusReadIfUnread(e Engine, userID, issueID int64) error {
|
|||
|
||||
// SetNotificationStatus change the notification status
|
||||
func SetNotificationStatus(notificationID int64, user *User, status NotificationStatus) error {
|
||||
notification, err := getNotificationByID(notificationID)
|
||||
notification, err := getNotificationByID(x, notificationID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -501,9 +684,14 @@ func SetNotificationStatus(notificationID int64, user *User, status Notification
|
|||
return err
|
||||
}
|
||||
|
||||
func getNotificationByID(notificationID int64) (*Notification, error) {
|
||||
// GetNotificationByID return notification by ID
|
||||
func GetNotificationByID(notificationID int64) (*Notification, error) {
|
||||
return getNotificationByID(x, notificationID)
|
||||
}
|
||||
|
||||
func getNotificationByID(e Engine, notificationID int64) (*Notification, error) {
|
||||
notification := new(Notification)
|
||||
ok, err := x.
|
||||
ok, err := e.
|
||||
Where("id = ?", notificationID).
|
||||
Get(notification)
|
||||
|
||||
|
@ -512,7 +700,7 @@ func getNotificationByID(notificationID int64) (*Notification, error) {
|
|||
}
|
||||
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("Notification %d does not exists", notificationID)
|
||||
return nil, ErrNotExist{ID: notificationID}
|
||||
}
|
||||
|
||||
return notification, nil
|
||||
|
|
|
@ -31,11 +31,13 @@ func TestNotificationsForUser(t *testing.T) {
|
|||
statuses := []NotificationStatus{NotificationStatusRead, NotificationStatusUnread}
|
||||
notfs, err := NotificationsForUser(user, statuses, 1, 10)
|
||||
assert.NoError(t, err)
|
||||
if assert.Len(t, notfs, 2) {
|
||||
assert.EqualValues(t, 2, notfs[0].ID)
|
||||
if assert.Len(t, notfs, 3) {
|
||||
assert.EqualValues(t, 5, notfs[0].ID)
|
||||
assert.EqualValues(t, user.ID, notfs[0].UserID)
|
||||
assert.EqualValues(t, 4, notfs[1].ID)
|
||||
assert.EqualValues(t, user.ID, notfs[1].UserID)
|
||||
assert.EqualValues(t, 2, notfs[2].ID)
|
||||
assert.EqualValues(t, user.ID, notfs[2].UserID)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -590,7 +590,8 @@ func UpdateTeam(t *Team, authChanged bool, includeAllChanged bool) (err error) {
|
|||
return ErrTeamAlreadyExist{t.OrgID, t.LowerName}
|
||||
}
|
||||
|
||||
if _, err = sess.ID(t.ID).AllCols().Update(t); err != nil {
|
||||
if _, err = sess.ID(t.ID).Cols("name", "lower_name", "description",
|
||||
"can_create_org_repo", "authorize", "includes_all_repositories").Update(t); err != nil {
|
||||
return fmt.Errorf("update: %v", err)
|
||||
}
|
||||
|
||||
|
@ -605,8 +606,7 @@ func UpdateTeam(t *Team, authChanged bool, includeAllChanged bool) (err error) {
|
|||
Delete(new(TeamUnit)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err = sess.Insert(&t.Units); err != nil {
|
||||
if _, err = sess.Cols("org_id", "team_id", "type").Insert(&t.Units); err != nil {
|
||||
errRollback := sess.Rollback()
|
||||
if errRollback != nil {
|
||||
log.Error("UpdateTeam sess.Rollback: %v", errRollback)
|
||||
|
|
135
models/pull.go
135
models/pull.go
|
@ -13,7 +13,6 @@ import (
|
|||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
)
|
||||
|
||||
|
@ -35,6 +34,7 @@ const (
|
|||
PullRequestStatusChecking
|
||||
PullRequestStatusMergeable
|
||||
PullRequestStatusManuallyMerged
|
||||
PullRequestStatusError
|
||||
)
|
||||
|
||||
// PullRequest represents relation between pull request and repositories.
|
||||
|
@ -396,139 +396,6 @@ func (pr *PullRequest) GetGitRefName() string {
|
|||
return fmt.Sprintf("refs/pull/%d/head", pr.Index)
|
||||
}
|
||||
|
||||
// APIFormat assumes following fields have been assigned with valid values:
|
||||
// Required - Issue
|
||||
// Optional - Merger
|
||||
func (pr *PullRequest) APIFormat() *api.PullRequest {
|
||||
return pr.apiFormat(x)
|
||||
}
|
||||
|
||||
func (pr *PullRequest) apiFormat(e Engine) *api.PullRequest {
|
||||
var (
|
||||
baseBranch *git.Branch
|
||||
headBranch *git.Branch
|
||||
baseCommit *git.Commit
|
||||
headCommit *git.Commit
|
||||
err error
|
||||
)
|
||||
if err = pr.Issue.loadRepo(e); err != nil {
|
||||
log.Error("loadRepo[%d]: %v", pr.ID, err)
|
||||
return nil
|
||||
}
|
||||
apiIssue := pr.Issue.apiFormat(e)
|
||||
if pr.BaseRepo == nil {
|
||||
pr.BaseRepo, err = getRepositoryByID(e, pr.BaseRepoID)
|
||||
if err != nil {
|
||||
log.Error("GetRepositoryById[%d]: %v", pr.ID, err)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if pr.HeadRepo == nil {
|
||||
pr.HeadRepo, err = getRepositoryByID(e, pr.HeadRepoID)
|
||||
if err != nil {
|
||||
log.Error("GetRepositoryById[%d]: %v", pr.ID, err)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if err = pr.Issue.loadRepo(e); err != nil {
|
||||
log.Error("pr.Issue.loadRepo[%d]: %v", pr.ID, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
apiPullRequest := &api.PullRequest{
|
||||
ID: pr.ID,
|
||||
URL: pr.Issue.HTMLURL(),
|
||||
Index: pr.Index,
|
||||
Poster: apiIssue.Poster,
|
||||
Title: apiIssue.Title,
|
||||
Body: apiIssue.Body,
|
||||
Labels: apiIssue.Labels,
|
||||
Milestone: apiIssue.Milestone,
|
||||
Assignee: apiIssue.Assignee,
|
||||
Assignees: apiIssue.Assignees,
|
||||
State: apiIssue.State,
|
||||
Comments: apiIssue.Comments,
|
||||
HTMLURL: pr.Issue.HTMLURL(),
|
||||
DiffURL: pr.Issue.DiffURL(),
|
||||
PatchURL: pr.Issue.PatchURL(),
|
||||
HasMerged: pr.HasMerged,
|
||||
MergeBase: pr.MergeBase,
|
||||
Deadline: apiIssue.Deadline,
|
||||
Created: pr.Issue.CreatedUnix.AsTimePtr(),
|
||||
Updated: pr.Issue.UpdatedUnix.AsTimePtr(),
|
||||
}
|
||||
baseBranch, err = pr.BaseRepo.GetBranch(pr.BaseBranch)
|
||||
if err != nil {
|
||||
if git.IsErrBranchNotExist(err) {
|
||||
apiPullRequest.Base = nil
|
||||
} else {
|
||||
log.Error("GetBranch[%s]: %v", pr.BaseBranch, err)
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
apiBaseBranchInfo := &api.PRBranchInfo{
|
||||
Name: pr.BaseBranch,
|
||||
Ref: pr.BaseBranch,
|
||||
RepoID: pr.BaseRepoID,
|
||||
Repository: pr.BaseRepo.innerAPIFormat(e, AccessModeNone, false),
|
||||
}
|
||||
baseCommit, err = baseBranch.GetCommit()
|
||||
if err != nil {
|
||||
if git.IsErrNotExist(err) {
|
||||
apiBaseBranchInfo.Sha = ""
|
||||
} else {
|
||||
log.Error("GetCommit[%s]: %v", baseBranch.Name, err)
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
apiBaseBranchInfo.Sha = baseCommit.ID.String()
|
||||
}
|
||||
apiPullRequest.Base = apiBaseBranchInfo
|
||||
}
|
||||
|
||||
headBranch, err = pr.HeadRepo.GetBranch(pr.HeadBranch)
|
||||
if err != nil {
|
||||
if git.IsErrBranchNotExist(err) {
|
||||
apiPullRequest.Head = nil
|
||||
} else {
|
||||
log.Error("GetBranch[%s]: %v", pr.HeadBranch, err)
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
apiHeadBranchInfo := &api.PRBranchInfo{
|
||||
Name: pr.HeadBranch,
|
||||
Ref: pr.HeadBranch,
|
||||
RepoID: pr.HeadRepoID,
|
||||
Repository: pr.HeadRepo.innerAPIFormat(e, AccessModeNone, false),
|
||||
}
|
||||
headCommit, err = headBranch.GetCommit()
|
||||
if err != nil {
|
||||
if git.IsErrNotExist(err) {
|
||||
apiHeadBranchInfo.Sha = ""
|
||||
} else {
|
||||
log.Error("GetCommit[%s]: %v", headBranch.Name, err)
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
apiHeadBranchInfo.Sha = headCommit.ID.String()
|
||||
}
|
||||
apiPullRequest.Head = apiHeadBranchInfo
|
||||
}
|
||||
|
||||
if pr.Status != PullRequestStatusChecking {
|
||||
mergeable := pr.Status != PullRequestStatusConflict && !pr.IsWorkInProgress()
|
||||
apiPullRequest.Mergeable = mergeable
|
||||
}
|
||||
if pr.HasMerged {
|
||||
apiPullRequest.Merged = pr.MergedUnix.AsTimePtr()
|
||||
apiPullRequest.MergedCommitID = &pr.MergedCommitID
|
||||
apiPullRequest.MergedBy = pr.Merger.APIFormat()
|
||||
}
|
||||
|
||||
return apiPullRequest
|
||||
}
|
||||
|
||||
func (pr *PullRequest) getHeadRepo(e Engine) (err error) {
|
||||
pr.HeadRepo, err = getRepositoryByID(e, pr.HeadRepoID)
|
||||
if err != nil && !IsErrRepoNotExist(err) {
|
||||
|
|
|
@ -29,16 +29,6 @@ func TestPullRequest_LoadIssue(t *testing.T) {
|
|||
assert.Equal(t, int64(2), pr.Issue.ID)
|
||||
}
|
||||
|
||||
func TestPullRequest_APIFormat(t *testing.T) {
|
||||
assert.NoError(t, PrepareTestDatabase())
|
||||
pr := AssertExistsAndLoadBean(t, &PullRequest{ID: 1}).(*PullRequest)
|
||||
assert.NoError(t, pr.LoadAttributes())
|
||||
assert.NoError(t, pr.LoadIssue())
|
||||
apiPullRequest := pr.APIFormat()
|
||||
assert.NotNil(t, apiPullRequest)
|
||||
assert.Nil(t, apiPullRequest.Head)
|
||||
}
|
||||
|
||||
func TestPullRequest_GetBaseRepo(t *testing.T) {
|
||||
assert.NoError(t, PrepareTestDatabase())
|
||||
pr := AssertExistsAndLoadBean(t, &PullRequest{ID: 1}).(*PullRequest)
|
||||
|
@ -182,7 +172,7 @@ func TestPullRequest_UpdateCols(t *testing.T) {
|
|||
BaseBranch: "baseBranch",
|
||||
HeadBranch: "headBranch",
|
||||
}
|
||||
pr.UpdateCols("head_branch")
|
||||
assert.NoError(t, pr.UpdateCols("head_branch"))
|
||||
|
||||
pr = AssertExistsAndLoadBean(t, &PullRequest{ID: 1}).(*PullRequest)
|
||||
assert.Equal(t, "master", pr.BaseBranch)
|
||||
|
|
|
@ -1071,17 +1071,18 @@ func initRepoCommit(tmpPath string, repo *Repository, u *User) (err error) {
|
|||
|
||||
// CreateRepoOptions contains the create repository options
|
||||
type CreateRepoOptions struct {
|
||||
Name string
|
||||
Description string
|
||||
OriginalURL string
|
||||
Gitignores string
|
||||
IssueLabels string
|
||||
License string
|
||||
Readme string
|
||||
IsPrivate bool
|
||||
IsMirror bool
|
||||
AutoInit bool
|
||||
Status RepositoryStatus
|
||||
Name string
|
||||
Description string
|
||||
OriginalURL string
|
||||
GitServiceType structs.GitServiceType
|
||||
Gitignores string
|
||||
IssueLabels string
|
||||
License string
|
||||
Readme string
|
||||
IsPrivate bool
|
||||
IsMirror bool
|
||||
AutoInit bool
|
||||
Status RepositoryStatus
|
||||
}
|
||||
|
||||
func getRepoInitFile(tp, name string) ([]byte, error) {
|
||||
|
@ -1369,6 +1370,7 @@ func CreateRepository(doer, u *User, opts CreateRepoOptions) (_ *Repository, err
|
|||
LowerName: strings.ToLower(opts.Name),
|
||||
Description: opts.Description,
|
||||
OriginalURL: opts.OriginalURL,
|
||||
OriginalServiceType: opts.GitServiceType,
|
||||
IsPrivate: opts.IsPrivate,
|
||||
IsFsckEnabled: !opts.IsMirror,
|
||||
CloseIssuesViaCommitInAnyBranch: setting.Repository.DefaultCloseIssuesViaCommitsInAnyBranch,
|
||||
|
|
|
@ -194,12 +194,13 @@ func CreateMigrateTask(doer, u *User, opts base.MigrateOptions) (*Task, error) {
|
|||
}
|
||||
|
||||
repo, err := CreateRepository(doer, u, CreateRepoOptions{
|
||||
Name: opts.RepoName,
|
||||
Description: opts.Description,
|
||||
OriginalURL: opts.OriginalURL,
|
||||
IsPrivate: opts.Private,
|
||||
IsMirror: opts.Mirror,
|
||||
Status: RepositoryBeingMigrated,
|
||||
Name: opts.RepoName,
|
||||
Description: opts.Description,
|
||||
OriginalURL: opts.OriginalURL,
|
||||
GitServiceType: opts.GitServiceType,
|
||||
IsPrivate: opts.Private,
|
||||
IsMirror: opts.Mirror,
|
||||
Status: RepositoryBeingMigrated,
|
||||
})
|
||||
if err != nil {
|
||||
task.EndTime = timeutil.TimeStampNow()
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
package models
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
@ -27,33 +26,6 @@ const (
|
|||
EnvIsInternal = "GITEA_INTERNAL_PUSH"
|
||||
)
|
||||
|
||||
// CommitToPushCommit transforms a git.Commit to PushCommit type.
|
||||
func CommitToPushCommit(commit *git.Commit) *PushCommit {
|
||||
return &PushCommit{
|
||||
Sha1: commit.ID.String(),
|
||||
Message: commit.Message(),
|
||||
AuthorEmail: commit.Author.Email,
|
||||
AuthorName: commit.Author.Name,
|
||||
CommitterEmail: commit.Committer.Email,
|
||||
CommitterName: commit.Committer.Name,
|
||||
Timestamp: commit.Author.When,
|
||||
}
|
||||
}
|
||||
|
||||
// ListToPushCommits transforms a list.List to PushCommits type.
|
||||
func ListToPushCommits(l *list.List) *PushCommits {
|
||||
var commits []*PushCommit
|
||||
var actEmail string
|
||||
for e := l.Front(); e != nil; e = e.Next() {
|
||||
commit := e.Value.(*git.Commit)
|
||||
if actEmail == "" {
|
||||
actEmail = commit.Committer.Email
|
||||
}
|
||||
commits = append(commits, CommitToPushCommit(commit))
|
||||
}
|
||||
return &PushCommits{l.Len(), commits, "", make(map[string]string), make(map[string]*User)}
|
||||
}
|
||||
|
||||
// PushUpdateAddDeleteTags updates a number of added and delete tags
|
||||
func PushUpdateAddDeleteTags(repo *Repository, gitRepo *git.Repository, addTags, delTags []string) error {
|
||||
sess := x.NewSession()
|
||||
|
@ -258,75 +230,25 @@ func pushUpdateAddTags(e Engine, repo *Repository, gitRepo *git.Repository, tags
|
|||
return nil
|
||||
}
|
||||
|
||||
// PushUpdateAddTag must be called for any push actions to add tag
|
||||
func PushUpdateAddTag(repo *Repository, gitRepo *git.Repository, tagName string) error {
|
||||
rel, err := GetRelease(repo.ID, tagName)
|
||||
// SaveOrUpdateTag must be called for any push actions to add tag
|
||||
func SaveOrUpdateTag(repo *Repository, newRel *Release) error {
|
||||
rel, err := GetRelease(repo.ID, newRel.TagName)
|
||||
if err != nil && !IsErrReleaseNotExist(err) {
|
||||
return fmt.Errorf("GetRelease: %v", err)
|
||||
}
|
||||
|
||||
tag, err := gitRepo.GetTag(tagName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("GetTag: %v", err)
|
||||
}
|
||||
commit, err := tag.Commit()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Commit: %v", err)
|
||||
}
|
||||
|
||||
sig := tag.Tagger
|
||||
if sig == nil {
|
||||
sig = commit.Author
|
||||
}
|
||||
if sig == nil {
|
||||
sig = commit.Committer
|
||||
}
|
||||
|
||||
var author *User
|
||||
var createdAt = time.Unix(1, 0)
|
||||
|
||||
if sig != nil {
|
||||
author, err = GetUserByEmail(sig.Email)
|
||||
if err != nil && !IsErrUserNotExist(err) {
|
||||
return fmt.Errorf("GetUserByEmail: %v", err)
|
||||
}
|
||||
createdAt = sig.When
|
||||
}
|
||||
|
||||
commitsCount, err := commit.CommitsCount()
|
||||
if err != nil {
|
||||
return fmt.Errorf("CommitsCount: %v", err)
|
||||
}
|
||||
|
||||
if rel == nil {
|
||||
rel = &Release{
|
||||
RepoID: repo.ID,
|
||||
Title: "",
|
||||
TagName: tagName,
|
||||
LowerTagName: strings.ToLower(tagName),
|
||||
Target: "",
|
||||
Sha1: commit.ID.String(),
|
||||
NumCommits: commitsCount,
|
||||
Note: "",
|
||||
IsDraft: false,
|
||||
IsPrerelease: false,
|
||||
IsTag: true,
|
||||
CreatedUnix: timeutil.TimeStamp(createdAt.Unix()),
|
||||
}
|
||||
if author != nil {
|
||||
rel.PublisherID = author.ID
|
||||
}
|
||||
|
||||
if _, err = x.InsertOne(rel); err != nil {
|
||||
rel = newRel
|
||||
if _, err = x.Insert(rel); err != nil {
|
||||
return fmt.Errorf("InsertOne: %v", err)
|
||||
}
|
||||
} else {
|
||||
rel.Sha1 = commit.ID.String()
|
||||
rel.CreatedUnix = timeutil.TimeStamp(createdAt.Unix())
|
||||
rel.NumCommits = commitsCount
|
||||
rel.Sha1 = newRel.Sha1
|
||||
rel.CreatedUnix = newRel.CreatedUnix
|
||||
rel.NumCommits = newRel.NumCommits
|
||||
rel.IsDraft = false
|
||||
if rel.IsTag && author != nil {
|
||||
rel.PublisherID = author.ID
|
||||
if rel.IsTag && newRel.PublisherID > 0 {
|
||||
rel.PublisherID = newRel.PublisherID
|
||||
}
|
||||
if _, err = x.ID(rel.ID).AllCols().Update(rel); err != nil {
|
||||
return fmt.Errorf("Update: %v", err)
|
||||
|
|
|
@ -1,86 +0,0 @@
|
|||
// Copyright 2016 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 models
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestCommitToPushCommit(t *testing.T) {
|
||||
now := time.Now()
|
||||
sig := &git.Signature{
|
||||
Email: "example@example.com",
|
||||
Name: "John Doe",
|
||||
When: now,
|
||||
}
|
||||
const hexString = "0123456789abcdef0123456789abcdef01234567"
|
||||
sha1, err := git.NewIDFromString(hexString)
|
||||
assert.NoError(t, err)
|
||||
pushCommit := CommitToPushCommit(&git.Commit{
|
||||
ID: sha1,
|
||||
Author: sig,
|
||||
Committer: sig,
|
||||
CommitMessage: "Commit Message",
|
||||
})
|
||||
assert.Equal(t, hexString, pushCommit.Sha1)
|
||||
assert.Equal(t, "Commit Message", pushCommit.Message)
|
||||
assert.Equal(t, "example@example.com", pushCommit.AuthorEmail)
|
||||
assert.Equal(t, "John Doe", pushCommit.AuthorName)
|
||||
assert.Equal(t, "example@example.com", pushCommit.CommitterEmail)
|
||||
assert.Equal(t, "John Doe", pushCommit.CommitterName)
|
||||
assert.Equal(t, now, pushCommit.Timestamp)
|
||||
}
|
||||
|
||||
func TestListToPushCommits(t *testing.T) {
|
||||
now := time.Now()
|
||||
sig := &git.Signature{
|
||||
Email: "example@example.com",
|
||||
Name: "John Doe",
|
||||
When: now,
|
||||
}
|
||||
|
||||
const hexString1 = "0123456789abcdef0123456789abcdef01234567"
|
||||
hash1, err := git.NewIDFromString(hexString1)
|
||||
assert.NoError(t, err)
|
||||
const hexString2 = "fedcba9876543210fedcba9876543210fedcba98"
|
||||
hash2, err := git.NewIDFromString(hexString2)
|
||||
assert.NoError(t, err)
|
||||
|
||||
l := list.New()
|
||||
l.PushBack(&git.Commit{
|
||||
ID: hash1,
|
||||
Author: sig,
|
||||
Committer: sig,
|
||||
CommitMessage: "Message1",
|
||||
})
|
||||
l.PushBack(&git.Commit{
|
||||
ID: hash2,
|
||||
Author: sig,
|
||||
Committer: sig,
|
||||
CommitMessage: "Message2",
|
||||
})
|
||||
|
||||
pushCommits := ListToPushCommits(l)
|
||||
assert.Equal(t, 2, pushCommits.Len)
|
||||
if assert.Len(t, pushCommits.Commits, 2) {
|
||||
assert.Equal(t, "Message1", pushCommits.Commits[0].Message)
|
||||
assert.Equal(t, hexString1, pushCommits.Commits[0].Sha1)
|
||||
assert.Equal(t, "example@example.com", pushCommits.Commits[0].AuthorEmail)
|
||||
assert.Equal(t, now, pushCommits.Commits[0].Timestamp)
|
||||
|
||||
assert.Equal(t, "Message2", pushCommits.Commits[1].Message)
|
||||
assert.Equal(t, hexString2, pushCommits.Commits[1].Sha1)
|
||||
assert.Equal(t, "example@example.com", pushCommits.Commits[1].AuthorEmail)
|
||||
assert.Equal(t, now, pushCommits.Commits[1].Timestamp)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO TestPushUpdate
|
|
@ -1,4 +1,5 @@
|
|||
// Copyright 2014 The Gogs Authors. All rights reserved.
|
||||
// Copyright 2020 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.
|
||||
|
||||
|
@ -122,7 +123,7 @@ func (ctx *Context) RedirectToFirst(location ...string) {
|
|||
}
|
||||
|
||||
u, err := url.Parse(loc)
|
||||
if err != nil || (u.Scheme != "" && !strings.HasPrefix(strings.ToLower(loc), strings.ToLower(setting.AppURL))) {
|
||||
if err != nil || ((u.Scheme != "" || u.Host != "") && !strings.HasPrefix(strings.ToLower(loc), strings.ToLower(setting.AppURL))) {
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
// Copyright 2020 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 convert
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
models.MainTest(m, filepath.Join("..", ".."))
|
||||
}
|
|
@ -0,0 +1,141 @@
|
|||
// Copyright 2020 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 convert
|
||||
|
||||
import (
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
)
|
||||
|
||||
// ToAPIPullRequest assumes following fields have been assigned with valid values:
|
||||
// Required - Issue
|
||||
// Optional - Merger
|
||||
func ToAPIPullRequest(pr *models.PullRequest) *api.PullRequest {
|
||||
var (
|
||||
baseBranch *git.Branch
|
||||
headBranch *git.Branch
|
||||
baseCommit *git.Commit
|
||||
headCommit *git.Commit
|
||||
err error
|
||||
)
|
||||
if err = pr.Issue.LoadRepo(); err != nil {
|
||||
log.Error("loadRepo[%d]: %v", pr.ID, err)
|
||||
return nil
|
||||
}
|
||||
apiIssue := pr.Issue.APIFormat()
|
||||
if pr.BaseRepo == nil {
|
||||
pr.BaseRepo, err = models.GetRepositoryByID(pr.BaseRepoID)
|
||||
if err != nil {
|
||||
log.Error("GetRepositoryById[%d]: %v", pr.ID, err)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if pr.HeadRepo == nil {
|
||||
pr.HeadRepo, err = models.GetRepositoryByID(pr.HeadRepoID)
|
||||
if err != nil {
|
||||
log.Error("GetRepositoryById[%d]: %v", pr.ID, err)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if err = pr.Issue.LoadRepo(); err != nil {
|
||||
log.Error("pr.Issue.loadRepo[%d]: %v", pr.ID, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
apiPullRequest := &api.PullRequest{
|
||||
ID: pr.ID,
|
||||
URL: pr.Issue.HTMLURL(),
|
||||
Index: pr.Index,
|
||||
Poster: apiIssue.Poster,
|
||||
Title: apiIssue.Title,
|
||||
Body: apiIssue.Body,
|
||||
Labels: apiIssue.Labels,
|
||||
Milestone: apiIssue.Milestone,
|
||||
Assignee: apiIssue.Assignee,
|
||||
Assignees: apiIssue.Assignees,
|
||||
State: apiIssue.State,
|
||||
Comments: apiIssue.Comments,
|
||||
HTMLURL: pr.Issue.HTMLURL(),
|
||||
DiffURL: pr.Issue.DiffURL(),
|
||||
PatchURL: pr.Issue.PatchURL(),
|
||||
HasMerged: pr.HasMerged,
|
||||
MergeBase: pr.MergeBase,
|
||||
Deadline: apiIssue.Deadline,
|
||||
Created: pr.Issue.CreatedUnix.AsTimePtr(),
|
||||
Updated: pr.Issue.UpdatedUnix.AsTimePtr(),
|
||||
}
|
||||
baseBranch, err = pr.BaseRepo.GetBranch(pr.BaseBranch)
|
||||
if err != nil {
|
||||
if git.IsErrBranchNotExist(err) {
|
||||
apiPullRequest.Base = nil
|
||||
} else {
|
||||
log.Error("GetBranch[%s]: %v", pr.BaseBranch, err)
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
apiBaseBranchInfo := &api.PRBranchInfo{
|
||||
Name: pr.BaseBranch,
|
||||
Ref: pr.BaseBranch,
|
||||
RepoID: pr.BaseRepoID,
|
||||
Repository: pr.BaseRepo.APIFormat(models.AccessModeNone),
|
||||
}
|
||||
baseCommit, err = baseBranch.GetCommit()
|
||||
if err != nil {
|
||||
if git.IsErrNotExist(err) {
|
||||
apiBaseBranchInfo.Sha = ""
|
||||
} else {
|
||||
log.Error("GetCommit[%s]: %v", baseBranch.Name, err)
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
apiBaseBranchInfo.Sha = baseCommit.ID.String()
|
||||
}
|
||||
apiPullRequest.Base = apiBaseBranchInfo
|
||||
}
|
||||
|
||||
headBranch, err = pr.HeadRepo.GetBranch(pr.HeadBranch)
|
||||
if err != nil {
|
||||
if git.IsErrBranchNotExist(err) {
|
||||
apiPullRequest.Head = nil
|
||||
} else {
|
||||
log.Error("GetBranch[%s]: %v", pr.HeadBranch, err)
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
apiHeadBranchInfo := &api.PRBranchInfo{
|
||||
Name: pr.HeadBranch,
|
||||
Ref: pr.HeadBranch,
|
||||
RepoID: pr.HeadRepoID,
|
||||
Repository: pr.HeadRepo.APIFormat(models.AccessModeNone),
|
||||
}
|
||||
headCommit, err = headBranch.GetCommit()
|
||||
if err != nil {
|
||||
if git.IsErrNotExist(err) {
|
||||
apiHeadBranchInfo.Sha = ""
|
||||
} else {
|
||||
log.Error("GetCommit[%s]: %v", headBranch.Name, err)
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
apiHeadBranchInfo.Sha = headCommit.ID.String()
|
||||
}
|
||||
apiPullRequest.Head = apiHeadBranchInfo
|
||||
}
|
||||
|
||||
if pr.Status != models.PullRequestStatusChecking {
|
||||
mergeable := !(pr.Status == models.PullRequestStatusConflict || pr.Status == models.PullRequestStatusError) && !pr.IsWorkInProgress()
|
||||
apiPullRequest.Mergeable = mergeable
|
||||
}
|
||||
if pr.HasMerged {
|
||||
apiPullRequest.Merged = pr.MergedUnix.AsTimePtr()
|
||||
apiPullRequest.MergedCommitID = &pr.MergedCommitID
|
||||
apiPullRequest.MergedBy = pr.Merger.APIFormat()
|
||||
}
|
||||
|
||||
return apiPullRequest
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
// Copyright 2020 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 convert
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestPullRequest_APIFormat(t *testing.T) {
|
||||
assert.NoError(t, models.PrepareTestDatabase())
|
||||
pr := models.AssertExistsAndLoadBean(t, &models.PullRequest{ID: 1}).(*models.PullRequest)
|
||||
assert.NoError(t, pr.LoadAttributes())
|
||||
assert.NoError(t, pr.LoadIssue())
|
||||
apiPullRequest := ToAPIPullRequest(pr)
|
||||
assert.NotNil(t, apiPullRequest)
|
||||
assert.Nil(t, apiPullRequest.Head)
|
||||
}
|
|
@ -101,12 +101,13 @@ func (g *GiteaLocalUploader) CreateRepo(repo *base.Repository, opts base.Migrate
|
|||
var r *models.Repository
|
||||
if opts.MigrateToRepoID <= 0 {
|
||||
r, err = models.CreateRepository(g.doer, owner, models.CreateRepoOptions{
|
||||
Name: g.repoName,
|
||||
Description: repo.Description,
|
||||
OriginalURL: repo.OriginalURL,
|
||||
IsPrivate: opts.Private,
|
||||
IsMirror: opts.Mirror,
|
||||
Status: models.RepositoryBeingMigrated,
|
||||
Name: g.repoName,
|
||||
Description: repo.Description,
|
||||
OriginalURL: repo.OriginalURL,
|
||||
GitServiceType: opts.GitServiceType,
|
||||
IsPrivate: opts.Private,
|
||||
IsMirror: opts.Mirror,
|
||||
Status: models.RepositoryBeingMigrated,
|
||||
})
|
||||
} else {
|
||||
r, err = models.GetRepositoryByID(opts.MigrateToRepoID)
|
||||
|
|
|
@ -14,6 +14,7 @@ import (
|
|||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/notification/base"
|
||||
"code.gitea.io/gitea/modules/repository"
|
||||
)
|
||||
|
||||
type actionNotifier struct {
|
||||
|
@ -266,7 +267,7 @@ func (*actionNotifier) NotifyMergePullRequest(pr *models.PullRequest, doer *mode
|
|||
}
|
||||
}
|
||||
|
||||
func (a *actionNotifier) NotifySyncPushCommits(pusher *models.User, repo *models.Repository, refName, oldCommitID, newCommitID string, commits *models.PushCommits) {
|
||||
func (a *actionNotifier) NotifySyncPushCommits(pusher *models.User, repo *models.Repository, refName, oldCommitID, newCommitID string, commits *repository.PushCommits) {
|
||||
data, err := json.Marshal(commits)
|
||||
if err != nil {
|
||||
log.Error("json.Marshal: %v", err)
|
||||
|
|
|
@ -7,6 +7,7 @@ package base
|
|||
import (
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/repository"
|
||||
)
|
||||
|
||||
// Notifier defines an interface to notify receiver
|
||||
|
@ -45,11 +46,11 @@ type Notifier interface {
|
|||
NotifyUpdateRelease(doer *models.User, rel *models.Release)
|
||||
NotifyDeleteRelease(doer *models.User, rel *models.Release)
|
||||
|
||||
NotifyPushCommits(pusher *models.User, repo *models.Repository, refName, oldCommitID, newCommitID string, commits *models.PushCommits)
|
||||
NotifyPushCommits(pusher *models.User, repo *models.Repository, refName, oldCommitID, newCommitID string, commits *repository.PushCommits)
|
||||
NotifyCreateRef(doer *models.User, repo *models.Repository, refType, refFullName string)
|
||||
NotifyDeleteRef(doer *models.User, repo *models.Repository, refType, refFullName string)
|
||||
|
||||
NotifySyncPushCommits(pusher *models.User, repo *models.Repository, refName, oldCommitID, newCommitID string, commits *models.PushCommits)
|
||||
NotifySyncPushCommits(pusher *models.User, repo *models.Repository, refName, oldCommitID, newCommitID string, commits *repository.PushCommits)
|
||||
NotifySyncCreateRef(doer *models.User, repo *models.Repository, refType, refFullName string)
|
||||
NotifySyncDeleteRef(doer *models.User, repo *models.Repository, refType, refFullName string)
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ package base
|
|||
import (
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/repository"
|
||||
)
|
||||
|
||||
// NullNotifier implements a blank notifier
|
||||
|
@ -116,7 +117,7 @@ func (*NullNotifier) NotifyMigrateRepository(doer *models.User, u *models.User,
|
|||
}
|
||||
|
||||
// NotifyPushCommits notifies commits pushed to notifiers
|
||||
func (*NullNotifier) NotifyPushCommits(pusher *models.User, repo *models.Repository, refName, oldCommitID, newCommitID string, commits *models.PushCommits) {
|
||||
func (*NullNotifier) NotifyPushCommits(pusher *models.User, repo *models.Repository, refName, oldCommitID, newCommitID string, commits *repository.PushCommits) {
|
||||
}
|
||||
|
||||
// NotifyCreateRef notifies branch or tag creation to notifiers
|
||||
|
@ -136,7 +137,7 @@ func (*NullNotifier) NotifyTransferRepository(doer *models.User, repo *models.Re
|
|||
}
|
||||
|
||||
// NotifySyncPushCommits places a place holder function
|
||||
func (*NullNotifier) NotifySyncPushCommits(pusher *models.User, repo *models.Repository, refName, oldCommitID, newCommitID string, commits *models.PushCommits) {
|
||||
func (*NullNotifier) NotifySyncPushCommits(pusher *models.User, repo *models.Repository, refName, oldCommitID, newCommitID string, commits *repository.PushCommits) {
|
||||
}
|
||||
|
||||
// NotifySyncCreateRef places a place holder function
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
issue_indexer "code.gitea.io/gitea/modules/indexer/issues"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/notification/base"
|
||||
"code.gitea.io/gitea/modules/repository"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
)
|
||||
|
||||
|
@ -117,7 +118,7 @@ func (r *indexerNotifier) NotifyMigrateRepository(doer *models.User, u *models.U
|
|||
}
|
||||
}
|
||||
|
||||
func (r *indexerNotifier) NotifyPushCommits(pusher *models.User, repo *models.Repository, refName, oldCommitID, newCommitID string, commits *models.PushCommits) {
|
||||
func (r *indexerNotifier) NotifyPushCommits(pusher *models.User, repo *models.Repository, refName, oldCommitID, newCommitID string, commits *repository.PushCommits) {
|
||||
if setting.Indexer.RepoIndexerEnabled && refName == repo.DefaultBranch {
|
||||
code_indexer.UpdateRepoIndexer(repo)
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ import (
|
|||
"code.gitea.io/gitea/modules/notification/mail"
|
||||
"code.gitea.io/gitea/modules/notification/ui"
|
||||
"code.gitea.io/gitea/modules/notification/webhook"
|
||||
"code.gitea.io/gitea/modules/repository"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
)
|
||||
|
||||
|
@ -215,7 +216,7 @@ func NotifyRenameRepository(doer *models.User, repo *models.Repository, oldName
|
|||
}
|
||||
|
||||
// NotifyPushCommits notifies commits pushed to notifiers
|
||||
func NotifyPushCommits(pusher *models.User, repo *models.Repository, refName, oldCommitID, newCommitID string, commits *models.PushCommits) {
|
||||
func NotifyPushCommits(pusher *models.User, repo *models.Repository, refName, oldCommitID, newCommitID string, commits *repository.PushCommits) {
|
||||
for _, notifier := range notifiers {
|
||||
notifier.NotifyPushCommits(pusher, repo, refName, oldCommitID, newCommitID, commits)
|
||||
}
|
||||
|
@ -236,7 +237,7 @@ func NotifyDeleteRef(pusher *models.User, repo *models.Repository, refType, refF
|
|||
}
|
||||
|
||||
// NotifySyncPushCommits notifies commits pushed to notifiers
|
||||
func NotifySyncPushCommits(pusher *models.User, repo *models.Repository, refName, oldCommitID, newCommitID string, commits *models.PushCommits) {
|
||||
func NotifySyncPushCommits(pusher *models.User, repo *models.Repository, refName, oldCommitID, newCommitID string, commits *repository.PushCommits) {
|
||||
for _, notifier := range notifiers {
|
||||
notifier.NotifySyncPushCommits(pusher, repo, refName, oldCommitID, newCommitID, commits)
|
||||
}
|
||||
|
|
|
@ -6,9 +6,11 @@ package webhook
|
|||
|
||||
import (
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/convert"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/notification/base"
|
||||
"code.gitea.io/gitea/modules/repository"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
webhook_module "code.gitea.io/gitea/modules/webhook"
|
||||
|
@ -49,7 +51,7 @@ func (m *webhookNotifier) NotifyIssueClearLabels(doer *models.User, issue *model
|
|||
err = webhook_module.PrepareWebhooks(issue.Repo, models.HookEventPullRequest, &api.PullRequestPayload{
|
||||
Action: api.HookIssueLabelCleared,
|
||||
Index: issue.Index,
|
||||
PullRequest: issue.PullRequest.APIFormat(),
|
||||
PullRequest: convert.ToAPIPullRequest(issue.PullRequest),
|
||||
Repository: issue.Repo.APIFormat(mode),
|
||||
Sender: doer.APIFormat(),
|
||||
})
|
||||
|
@ -135,7 +137,7 @@ func (m *webhookNotifier) NotifyIssueChangeAssignee(doer *models.User, issue *mo
|
|||
issue.PullRequest.Issue = issue
|
||||
apiPullRequest := &api.PullRequestPayload{
|
||||
Index: issue.Index,
|
||||
PullRequest: issue.PullRequest.APIFormat(),
|
||||
PullRequest: convert.ToAPIPullRequest(issue.PullRequest),
|
||||
Repository: issue.Repo.APIFormat(mode),
|
||||
Sender: doer.APIFormat(),
|
||||
}
|
||||
|
@ -187,7 +189,7 @@ func (m *webhookNotifier) NotifyIssueChangeTitle(doer *models.User, issue *model
|
|||
From: oldTitle,
|
||||
},
|
||||
},
|
||||
PullRequest: issue.PullRequest.APIFormat(),
|
||||
PullRequest: convert.ToAPIPullRequest(issue.PullRequest),
|
||||
Repository: issue.Repo.APIFormat(mode),
|
||||
Sender: doer.APIFormat(),
|
||||
})
|
||||
|
@ -222,7 +224,7 @@ func (m *webhookNotifier) NotifyIssueChangeStatus(doer *models.User, issue *mode
|
|||
// Merge pull request calls issue.changeStatus so we need to handle separately.
|
||||
apiPullRequest := &api.PullRequestPayload{
|
||||
Index: issue.Index,
|
||||
PullRequest: issue.PullRequest.APIFormat(),
|
||||
PullRequest: convert.ToAPIPullRequest(issue.PullRequest),
|
||||
Repository: issue.Repo.APIFormat(mode),
|
||||
Sender: doer.APIFormat(),
|
||||
}
|
||||
|
@ -291,7 +293,7 @@ func (m *webhookNotifier) NotifyNewPullRequest(pull *models.PullRequest) {
|
|||
if err := webhook_module.PrepareWebhooks(pull.Issue.Repo, models.HookEventPullRequest, &api.PullRequestPayload{
|
||||
Action: api.HookIssueOpened,
|
||||
Index: pull.Issue.Index,
|
||||
PullRequest: pull.APIFormat(),
|
||||
PullRequest: convert.ToAPIPullRequest(pull),
|
||||
Repository: pull.Issue.Repo.APIFormat(mode),
|
||||
Sender: pull.Issue.Poster.APIFormat(),
|
||||
}); err != nil {
|
||||
|
@ -312,7 +314,7 @@ func (m *webhookNotifier) NotifyIssueChangeContent(doer *models.User, issue *mod
|
|||
From: oldContent,
|
||||
},
|
||||
},
|
||||
PullRequest: issue.PullRequest.APIFormat(),
|
||||
PullRequest: convert.ToAPIPullRequest(issue.PullRequest),
|
||||
Repository: issue.Repo.APIFormat(mode),
|
||||
Sender: doer.APIFormat(),
|
||||
})
|
||||
|
@ -439,7 +441,7 @@ func (m *webhookNotifier) NotifyIssueChangeLabels(doer *models.User, issue *mode
|
|||
err = webhook_module.PrepareWebhooks(issue.Repo, models.HookEventPullRequest, &api.PullRequestPayload{
|
||||
Action: api.HookIssueLabelUpdated,
|
||||
Index: issue.Index,
|
||||
PullRequest: issue.PullRequest.APIFormat(),
|
||||
PullRequest: convert.ToAPIPullRequest(issue.PullRequest),
|
||||
Repository: issue.Repo.APIFormat(models.AccessModeNone),
|
||||
Sender: doer.APIFormat(),
|
||||
})
|
||||
|
@ -481,7 +483,7 @@ func (m *webhookNotifier) NotifyIssueChangeMilestone(doer *models.User, issue *m
|
|||
err = webhook_module.PrepareWebhooks(issue.Repo, models.HookEventPullRequest, &api.PullRequestPayload{
|
||||
Action: hookAction,
|
||||
Index: issue.Index,
|
||||
PullRequest: issue.PullRequest.APIFormat(),
|
||||
PullRequest: convert.ToAPIPullRequest(issue.PullRequest),
|
||||
Repository: issue.Repo.APIFormat(mode),
|
||||
Sender: doer.APIFormat(),
|
||||
})
|
||||
|
@ -499,7 +501,7 @@ func (m *webhookNotifier) NotifyIssueChangeMilestone(doer *models.User, issue *m
|
|||
}
|
||||
}
|
||||
|
||||
func (m *webhookNotifier) NotifyPushCommits(pusher *models.User, repo *models.Repository, refName, oldCommitID, newCommitID string, commits *models.PushCommits) {
|
||||
func (m *webhookNotifier) NotifyPushCommits(pusher *models.User, repo *models.Repository, refName, oldCommitID, newCommitID string, commits *repository.PushCommits) {
|
||||
apiPusher := pusher.APIFormat()
|
||||
apiCommits, err := commits.ToAPIPayloadCommits(repo.RepoPath(), repo.HTMLURL())
|
||||
if err != nil {
|
||||
|
@ -547,7 +549,7 @@ func (*webhookNotifier) NotifyMergePullRequest(pr *models.PullRequest, doer *mod
|
|||
// Merge pull request calls issue.changeStatus so we need to handle separately.
|
||||
apiPullRequest := &api.PullRequestPayload{
|
||||
Index: pr.Issue.Index,
|
||||
PullRequest: pr.APIFormat(),
|
||||
PullRequest: convert.ToAPIPullRequest(pr),
|
||||
Repository: pr.Issue.Repo.APIFormat(mode),
|
||||
Sender: doer.APIFormat(),
|
||||
Action: api.HookIssueClosed,
|
||||
|
@ -580,7 +582,7 @@ func (m *webhookNotifier) NotifyPullRequestChangeTargetBranch(doer *models.User,
|
|||
From: oldBranch,
|
||||
},
|
||||
},
|
||||
PullRequest: issue.PullRequest.APIFormat(),
|
||||
PullRequest: convert.ToAPIPullRequest(issue.PullRequest),
|
||||
Repository: issue.Repo.APIFormat(mode),
|
||||
Sender: doer.APIFormat(),
|
||||
})
|
||||
|
@ -619,7 +621,7 @@ func (m *webhookNotifier) NotifyPullRequestReview(pr *models.PullRequest, review
|
|||
if err := webhook_module.PrepareWebhooks(review.Issue.Repo, reviewHookType, &api.PullRequestPayload{
|
||||
Action: api.HookIssueSynchronized,
|
||||
Index: review.Issue.Index,
|
||||
PullRequest: pr.APIFormat(),
|
||||
PullRequest: convert.ToAPIPullRequest(pr),
|
||||
Repository: review.Issue.Repo.APIFormat(mode),
|
||||
Sender: review.Reviewer.APIFormat(),
|
||||
Review: &api.ReviewPayload{
|
||||
|
@ -674,7 +676,7 @@ func (m *webhookNotifier) NotifyPullRequestSynchronized(doer *models.User, pr *m
|
|||
if err := webhook_module.PrepareWebhooks(pr.Issue.Repo, models.HookEventPullRequest, &api.PullRequestPayload{
|
||||
Action: api.HookIssueSynchronized,
|
||||
Index: pr.Issue.Index,
|
||||
PullRequest: pr.Issue.PullRequest.APIFormat(),
|
||||
PullRequest: convert.ToAPIPullRequest(pr),
|
||||
Repository: pr.Issue.Repo.APIFormat(models.AccessModeNone),
|
||||
Sender: doer.APIFormat(),
|
||||
}); err != nil {
|
||||
|
@ -727,7 +729,7 @@ func (m *webhookNotifier) NotifyDeleteRelease(doer *models.User, rel *models.Rel
|
|||
sendReleaseHook(doer, rel, api.HookReleaseDeleted)
|
||||
}
|
||||
|
||||
func (m *webhookNotifier) NotifySyncPushCommits(pusher *models.User, repo *models.Repository, refName, oldCommitID, newCommitID string, commits *models.PushCommits) {
|
||||
func (m *webhookNotifier) NotifySyncPushCommits(pusher *models.User, repo *models.Repository, refName, oldCommitID, newCommitID string, commits *repository.PushCommits) {
|
||||
apiPusher := pusher.APIFormat()
|
||||
apiCommits, err := commits.ToAPIPayloadCommits(repo.RepoPath(), repo.HTMLURL())
|
||||
if err != nil {
|
||||
|
|
|
@ -15,6 +15,7 @@ import (
|
|||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/notification"
|
||||
"code.gitea.io/gitea/modules/references"
|
||||
"code.gitea.io/gitea/modules/repository"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
)
|
||||
|
||||
|
@ -59,7 +60,7 @@ func changeIssueStatus(repo *models.Repository, issue *models.Issue, doer *model
|
|||
}
|
||||
|
||||
// UpdateIssuesCommit checks if issues are manipulated by commit message.
|
||||
func UpdateIssuesCommit(doer *models.User, repo *models.Repository, commits []*models.PushCommit, branchName string) error {
|
||||
func UpdateIssuesCommit(doer *models.User, repo *models.Repository, commits []*repository.PushCommit, branchName string) error {
|
||||
// Commits are appended in the reverse order.
|
||||
for i := len(commits) - 1; i >= 0; i-- {
|
||||
c := commits[i]
|
||||
|
@ -154,7 +155,7 @@ type CommitRepoActionOptions struct {
|
|||
RefFullName string
|
||||
OldCommitID string
|
||||
NewCommitID string
|
||||
Commits *models.PushCommits
|
||||
Commits *repository.PushCommits
|
||||
}
|
||||
|
||||
// CommitRepoAction adds new commit action to the repository, and prepare
|
||||
|
@ -216,10 +217,10 @@ func CommitRepoAction(optsList ...*CommitRepoActionOptions) error {
|
|||
if opts.NewCommitID == git.EmptySHA {
|
||||
opType = models.ActionDeleteTag
|
||||
}
|
||||
opts.Commits = &models.PushCommits{}
|
||||
opts.Commits = &repository.PushCommits{}
|
||||
} else if opts.NewCommitID == git.EmptySHA {
|
||||
opType = models.ActionDeleteBranch
|
||||
opts.Commits = &models.PushCommits{}
|
||||
opts.Commits = &repository.PushCommits{}
|
||||
} else {
|
||||
// if not the first commit, set the compare URL.
|
||||
if opts.OldCommitID == git.EmptySHA {
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/repository"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
@ -34,8 +35,8 @@ func TestCommitRepoAction(t *testing.T) {
|
|||
RefFullName: "refName",
|
||||
OldCommitID: "oldCommitID",
|
||||
NewCommitID: "newCommitID",
|
||||
Commits: &models.PushCommits{
|
||||
Commits: []*models.PushCommit{
|
||||
Commits: &repository.PushCommits{
|
||||
Commits: []*repository.PushCommit{
|
||||
{
|
||||
Sha1: "69554a6",
|
||||
CommitterEmail: "user2@example.com",
|
||||
|
@ -68,7 +69,7 @@ func TestCommitRepoAction(t *testing.T) {
|
|||
RefFullName: git.TagPrefix + "v1.1",
|
||||
OldCommitID: git.EmptySHA,
|
||||
NewCommitID: "newCommitID",
|
||||
Commits: &models.PushCommits{},
|
||||
Commits: &repository.PushCommits{},
|
||||
},
|
||||
action: models.Action{
|
||||
OpType: models.ActionPushTag,
|
||||
|
@ -82,7 +83,7 @@ func TestCommitRepoAction(t *testing.T) {
|
|||
RefFullName: git.TagPrefix + "v1.1",
|
||||
OldCommitID: "oldCommitID",
|
||||
NewCommitID: git.EmptySHA,
|
||||
Commits: &models.PushCommits{},
|
||||
Commits: &repository.PushCommits{},
|
||||
},
|
||||
action: models.Action{
|
||||
OpType: models.ActionDeleteTag,
|
||||
|
@ -96,7 +97,7 @@ func TestCommitRepoAction(t *testing.T) {
|
|||
RefFullName: git.BranchPrefix + "feature/1",
|
||||
OldCommitID: "oldCommitID",
|
||||
NewCommitID: git.EmptySHA,
|
||||
Commits: &models.PushCommits{},
|
||||
Commits: &repository.PushCommits{},
|
||||
},
|
||||
action: models.Action{
|
||||
OpType: models.ActionDeleteBranch,
|
||||
|
@ -127,7 +128,7 @@ func TestCommitRepoAction(t *testing.T) {
|
|||
|
||||
func TestUpdateIssuesCommit(t *testing.T) {
|
||||
assert.NoError(t, models.PrepareTestDatabase())
|
||||
pushCommits := []*models.PushCommit{
|
||||
pushCommits := []*repository.PushCommit{
|
||||
{
|
||||
Sha1: "abcdef1",
|
||||
CommitterEmail: "user2@example.com",
|
||||
|
@ -174,7 +175,7 @@ func TestUpdateIssuesCommit(t *testing.T) {
|
|||
models.CheckConsistencyFor(t, &models.Action{})
|
||||
|
||||
// Test that push to a non-default branch closes no issue.
|
||||
pushCommits = []*models.PushCommit{
|
||||
pushCommits = []*repository.PushCommit{
|
||||
{
|
||||
Sha1: "abcdef1",
|
||||
CommitterEmail: "user2@example.com",
|
||||
|
@ -203,7 +204,7 @@ func TestUpdateIssuesCommit(t *testing.T) {
|
|||
|
||||
func TestUpdateIssuesCommit_Colon(t *testing.T) {
|
||||
assert.NoError(t, models.PrepareTestDatabase())
|
||||
pushCommits := []*models.PushCommit{
|
||||
pushCommits := []*repository.PushCommit{
|
||||
{
|
||||
Sha1: "abcdef2",
|
||||
CommitterEmail: "user2@example.com",
|
||||
|
@ -231,7 +232,7 @@ func TestUpdateIssuesCommit_Issue5957(t *testing.T) {
|
|||
user := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
|
||||
|
||||
// Test that push to a non-default branch closes an issue.
|
||||
pushCommits := []*models.PushCommit{
|
||||
pushCommits := []*repository.PushCommit{
|
||||
{
|
||||
Sha1: "abcdef1",
|
||||
CommitterEmail: "user2@example.com",
|
||||
|
@ -266,7 +267,7 @@ func TestUpdateIssuesCommit_AnotherRepo(t *testing.T) {
|
|||
|
||||
// Test that a push to default branch closes issue in another repo
|
||||
// If the user also has push permissions to that repo
|
||||
pushCommits := []*models.PushCommit{
|
||||
pushCommits := []*repository.PushCommit{
|
||||
{
|
||||
Sha1: "abcdef1",
|
||||
CommitterEmail: "user2@example.com",
|
||||
|
@ -301,7 +302,7 @@ func TestUpdateIssuesCommit_AnotherRepoNoPermission(t *testing.T) {
|
|||
|
||||
// Test that a push with close reference *can not* close issue
|
||||
// If the commiter doesn't have push rights in that repo
|
||||
pushCommits := []*models.PushCommit{
|
||||
pushCommits := []*repository.PushCommit{
|
||||
{
|
||||
Sha1: "abcdef3",
|
||||
CommitterEmail: "user10@example.com",
|
||||
|
|
|
@ -18,6 +18,7 @@ import (
|
|||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/lfs"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/repository"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/structs"
|
||||
pull_service "code.gitea.io/gitea/services/pull"
|
||||
|
@ -549,7 +550,7 @@ func createCommitRepoActions(repo *models.Repository, gitRepo *git.Repository, o
|
|||
if isNewRef && isDelRef {
|
||||
return nil, fmt.Errorf("Old and new revisions are both %s", git.EmptySHA)
|
||||
}
|
||||
var commits = &models.PushCommits{}
|
||||
var commits = &repository.PushCommits{}
|
||||
if strings.HasPrefix(opts.RefFullName, git.TagPrefix) {
|
||||
// If is tag reference
|
||||
tagName := opts.RefFullName[len(git.TagPrefix):]
|
||||
|
@ -584,7 +585,7 @@ func createCommitRepoActions(repo *models.Repository, gitRepo *git.Repository, o
|
|||
}
|
||||
}
|
||||
|
||||
commits = models.ListToPushCommits(l)
|
||||
commits = repository.ListToPushCommits(l)
|
||||
}
|
||||
actions = append(actions, &CommitRepoActionOptions{
|
||||
PusherName: opts.PusherName,
|
||||
|
@ -609,7 +610,7 @@ func createCommitRepoActionOption(repo *models.Repository, gitRepo *git.Reposito
|
|||
return nil, fmt.Errorf("Old and new revisions are both %s", git.EmptySHA)
|
||||
}
|
||||
|
||||
var commits = &models.PushCommits{}
|
||||
var commits = &repository.PushCommits{}
|
||||
if strings.HasPrefix(opts.RefFullName, git.TagPrefix) {
|
||||
// If is tag reference
|
||||
tagName := opts.RefFullName[len(git.TagPrefix):]
|
||||
|
@ -620,7 +621,7 @@ func createCommitRepoActionOption(repo *models.Repository, gitRepo *git.Reposito
|
|||
} else {
|
||||
// Clear cache for tag commit count
|
||||
cache.Remove(repo.GetCommitsCountCacheKey(tagName, true))
|
||||
if err := models.PushUpdateAddTag(repo, gitRepo, tagName); err != nil {
|
||||
if err := repository.PushUpdateAddTag(repo, gitRepo, tagName); err != nil {
|
||||
return nil, fmt.Errorf("PushUpdateAddTag: %v", err)
|
||||
}
|
||||
}
|
||||
|
@ -649,7 +650,7 @@ func createCommitRepoActionOption(repo *models.Repository, gitRepo *git.Reposito
|
|||
}
|
||||
}
|
||||
|
||||
commits = models.ListToPushCommits(l)
|
||||
commits = repository.ListToPushCommits(l)
|
||||
}
|
||||
|
||||
return &CommitRepoActionOptions{
|
||||
|
|
|
@ -0,0 +1,168 @@
|
|||
// Copyright 2019 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 repository
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/base"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
)
|
||||
|
||||
// PushCommit represents a commit in a push operation.
|
||||
type PushCommit struct {
|
||||
Sha1 string
|
||||
Message string
|
||||
AuthorEmail string
|
||||
AuthorName string
|
||||
CommitterEmail string
|
||||
CommitterName string
|
||||
Timestamp time.Time
|
||||
}
|
||||
|
||||
// PushCommits represents list of commits in a push operation.
|
||||
type PushCommits struct {
|
||||
Len int
|
||||
Commits []*PushCommit
|
||||
CompareURL string
|
||||
|
||||
avatars map[string]string
|
||||
emailUsers map[string]*models.User
|
||||
}
|
||||
|
||||
// NewPushCommits creates a new PushCommits object.
|
||||
func NewPushCommits() *PushCommits {
|
||||
return &PushCommits{
|
||||
avatars: make(map[string]string),
|
||||
emailUsers: make(map[string]*models.User),
|
||||
}
|
||||
}
|
||||
|
||||
// ToAPIPayloadCommits converts a PushCommits object to
|
||||
// api.PayloadCommit format.
|
||||
func (pc *PushCommits) ToAPIPayloadCommits(repoPath, repoLink string) ([]*api.PayloadCommit, error) {
|
||||
commits := make([]*api.PayloadCommit, len(pc.Commits))
|
||||
|
||||
if pc.emailUsers == nil {
|
||||
pc.emailUsers = make(map[string]*models.User)
|
||||
}
|
||||
var err error
|
||||
for i, commit := range pc.Commits {
|
||||
authorUsername := ""
|
||||
author, ok := pc.emailUsers[commit.AuthorEmail]
|
||||
if !ok {
|
||||
author, err = models.GetUserByEmail(commit.AuthorEmail)
|
||||
if err == nil {
|
||||
authorUsername = author.Name
|
||||
pc.emailUsers[commit.AuthorEmail] = author
|
||||
}
|
||||
} else {
|
||||
authorUsername = author.Name
|
||||
}
|
||||
|
||||
committerUsername := ""
|
||||
committer, ok := pc.emailUsers[commit.CommitterEmail]
|
||||
if !ok {
|
||||
committer, err = models.GetUserByEmail(commit.CommitterEmail)
|
||||
if err == nil {
|
||||
// TODO: check errors other than email not found.
|
||||
committerUsername = committer.Name
|
||||
pc.emailUsers[commit.CommitterEmail] = committer
|
||||
}
|
||||
} else {
|
||||
committerUsername = committer.Name
|
||||
}
|
||||
|
||||
fileStatus, err := git.GetCommitFileStatus(repoPath, commit.Sha1)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("FileStatus [commit_sha1: %s]: %v", commit.Sha1, err)
|
||||
}
|
||||
|
||||
commits[i] = &api.PayloadCommit{
|
||||
ID: commit.Sha1,
|
||||
Message: commit.Message,
|
||||
URL: fmt.Sprintf("%s/commit/%s", repoLink, commit.Sha1),
|
||||
Author: &api.PayloadUser{
|
||||
Name: commit.AuthorName,
|
||||
Email: commit.AuthorEmail,
|
||||
UserName: authorUsername,
|
||||
},
|
||||
Committer: &api.PayloadUser{
|
||||
Name: commit.CommitterName,
|
||||
Email: commit.CommitterEmail,
|
||||
UserName: committerUsername,
|
||||
},
|
||||
Added: fileStatus.Added,
|
||||
Removed: fileStatus.Removed,
|
||||
Modified: fileStatus.Modified,
|
||||
Timestamp: commit.Timestamp,
|
||||
}
|
||||
}
|
||||
return commits, nil
|
||||
}
|
||||
|
||||
// AvatarLink tries to match user in database with e-mail
|
||||
// in order to show custom avatar, and falls back to general avatar link.
|
||||
func (pc *PushCommits) AvatarLink(email string) string {
|
||||
if pc.avatars == nil {
|
||||
pc.avatars = make(map[string]string)
|
||||
}
|
||||
avatar, ok := pc.avatars[email]
|
||||
if ok {
|
||||
return avatar
|
||||
}
|
||||
|
||||
u, ok := pc.emailUsers[email]
|
||||
if !ok {
|
||||
var err error
|
||||
u, err = models.GetUserByEmail(email)
|
||||
if err != nil {
|
||||
pc.avatars[email] = base.AvatarLink(email)
|
||||
if !models.IsErrUserNotExist(err) {
|
||||
log.Error("GetUserByEmail: %v", err)
|
||||
return ""
|
||||
}
|
||||
} else {
|
||||
pc.emailUsers[email] = u
|
||||
}
|
||||
}
|
||||
if u != nil {
|
||||
pc.avatars[email] = u.RelAvatarLink()
|
||||
}
|
||||
|
||||
return pc.avatars[email]
|
||||
}
|
||||
|
||||
// CommitToPushCommit transforms a git.Commit to PushCommit type.
|
||||
func CommitToPushCommit(commit *git.Commit) *PushCommit {
|
||||
return &PushCommit{
|
||||
Sha1: commit.ID.String(),
|
||||
Message: commit.Message(),
|
||||
AuthorEmail: commit.Author.Email,
|
||||
AuthorName: commit.Author.Name,
|
||||
CommitterEmail: commit.Committer.Email,
|
||||
CommitterName: commit.Committer.Name,
|
||||
Timestamp: commit.Author.When,
|
||||
}
|
||||
}
|
||||
|
||||
// ListToPushCommits transforms a list.List to PushCommits type.
|
||||
func ListToPushCommits(l *list.List) *PushCommits {
|
||||
var commits []*PushCommit
|
||||
var actEmail string
|
||||
for e := l.Front(); e != nil; e = e.Next() {
|
||||
commit := e.Value.(*git.Commit)
|
||||
if actEmail == "" {
|
||||
actEmail = commit.Committer.Email
|
||||
}
|
||||
commits = append(commits, CommitToPushCommit(commit))
|
||||
}
|
||||
return &PushCommits{l.Len(), commits, "", make(map[string]string), make(map[string]*models.User)}
|
||||
}
|
|
@ -0,0 +1,190 @@
|
|||
// Copyright 2019 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 repository
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestPushCommits_ToAPIPayloadCommits(t *testing.T) {
|
||||
assert.NoError(t, models.PrepareTestDatabase())
|
||||
|
||||
pushCommits := NewPushCommits()
|
||||
pushCommits.Commits = []*PushCommit{
|
||||
{
|
||||
Sha1: "69554a6",
|
||||
CommitterEmail: "user2@example.com",
|
||||
CommitterName: "User2",
|
||||
AuthorEmail: "user2@example.com",
|
||||
AuthorName: "User2",
|
||||
Message: "not signed commit",
|
||||
},
|
||||
{
|
||||
Sha1: "27566bd",
|
||||
CommitterEmail: "user2@example.com",
|
||||
CommitterName: "User2",
|
||||
AuthorEmail: "user2@example.com",
|
||||
AuthorName: "User2",
|
||||
Message: "good signed commit (with not yet validated email)",
|
||||
},
|
||||
{
|
||||
Sha1: "5099b81",
|
||||
CommitterEmail: "user2@example.com",
|
||||
CommitterName: "User2",
|
||||
AuthorEmail: "user2@example.com",
|
||||
AuthorName: "User2",
|
||||
Message: "good signed commit",
|
||||
},
|
||||
}
|
||||
pushCommits.Len = len(pushCommits.Commits)
|
||||
|
||||
repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 16}).(*models.Repository)
|
||||
payloadCommits, err := pushCommits.ToAPIPayloadCommits(repo.RepoPath(), "/user2/repo16")
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 3, len(payloadCommits))
|
||||
|
||||
assert.Equal(t, "69554a6", payloadCommits[0].ID)
|
||||
assert.Equal(t, "not signed commit", payloadCommits[0].Message)
|
||||
assert.Equal(t, "/user2/repo16/commit/69554a6", payloadCommits[0].URL)
|
||||
assert.Equal(t, "User2", payloadCommits[0].Committer.Name)
|
||||
assert.Equal(t, "user2", payloadCommits[0].Committer.UserName)
|
||||
assert.Equal(t, "User2", payloadCommits[0].Author.Name)
|
||||
assert.Equal(t, "user2", payloadCommits[0].Author.UserName)
|
||||
assert.EqualValues(t, []string{}, payloadCommits[0].Added)
|
||||
assert.EqualValues(t, []string{}, payloadCommits[0].Removed)
|
||||
assert.EqualValues(t, []string{"readme.md"}, payloadCommits[0].Modified)
|
||||
|
||||
assert.Equal(t, "27566bd", payloadCommits[1].ID)
|
||||
assert.Equal(t, "good signed commit (with not yet validated email)", payloadCommits[1].Message)
|
||||
assert.Equal(t, "/user2/repo16/commit/27566bd", payloadCommits[1].URL)
|
||||
assert.Equal(t, "User2", payloadCommits[1].Committer.Name)
|
||||
assert.Equal(t, "user2", payloadCommits[1].Committer.UserName)
|
||||
assert.Equal(t, "User2", payloadCommits[1].Author.Name)
|
||||
assert.Equal(t, "user2", payloadCommits[1].Author.UserName)
|
||||
assert.EqualValues(t, []string{}, payloadCommits[1].Added)
|
||||
assert.EqualValues(t, []string{}, payloadCommits[1].Removed)
|
||||
assert.EqualValues(t, []string{"readme.md"}, payloadCommits[1].Modified)
|
||||
|
||||
assert.Equal(t, "5099b81", payloadCommits[2].ID)
|
||||
assert.Equal(t, "good signed commit", payloadCommits[2].Message)
|
||||
assert.Equal(t, "/user2/repo16/commit/5099b81", payloadCommits[2].URL)
|
||||
assert.Equal(t, "User2", payloadCommits[2].Committer.Name)
|
||||
assert.Equal(t, "user2", payloadCommits[2].Committer.UserName)
|
||||
assert.Equal(t, "User2", payloadCommits[2].Author.Name)
|
||||
assert.Equal(t, "user2", payloadCommits[2].Author.UserName)
|
||||
assert.EqualValues(t, []string{"readme.md"}, payloadCommits[2].Added)
|
||||
assert.EqualValues(t, []string{}, payloadCommits[2].Removed)
|
||||
assert.EqualValues(t, []string{}, payloadCommits[2].Modified)
|
||||
}
|
||||
|
||||
func TestPushCommits_AvatarLink(t *testing.T) {
|
||||
assert.NoError(t, models.PrepareTestDatabase())
|
||||
|
||||
pushCommits := NewPushCommits()
|
||||
pushCommits.Commits = []*PushCommit{
|
||||
{
|
||||
Sha1: "abcdef1",
|
||||
CommitterEmail: "user2@example.com",
|
||||
CommitterName: "User Two",
|
||||
AuthorEmail: "user4@example.com",
|
||||
AuthorName: "User Four",
|
||||
Message: "message1",
|
||||
},
|
||||
{
|
||||
Sha1: "abcdef2",
|
||||
CommitterEmail: "user2@example.com",
|
||||
CommitterName: "User Two",
|
||||
AuthorEmail: "user2@example.com",
|
||||
AuthorName: "User Two",
|
||||
Message: "message2",
|
||||
},
|
||||
}
|
||||
pushCommits.Len = len(pushCommits.Commits)
|
||||
|
||||
assert.Equal(t,
|
||||
"/user/avatar/user2/-1",
|
||||
pushCommits.AvatarLink("user2@example.com"))
|
||||
|
||||
assert.Equal(t,
|
||||
"https://secure.gravatar.com/avatar/19ade630b94e1e0535b3df7387434154?d=identicon",
|
||||
pushCommits.AvatarLink("nonexistent@example.com"))
|
||||
}
|
||||
|
||||
func TestCommitToPushCommit(t *testing.T) {
|
||||
now := time.Now()
|
||||
sig := &git.Signature{
|
||||
Email: "example@example.com",
|
||||
Name: "John Doe",
|
||||
When: now,
|
||||
}
|
||||
const hexString = "0123456789abcdef0123456789abcdef01234567"
|
||||
sha1, err := git.NewIDFromString(hexString)
|
||||
assert.NoError(t, err)
|
||||
pushCommit := CommitToPushCommit(&git.Commit{
|
||||
ID: sha1,
|
||||
Author: sig,
|
||||
Committer: sig,
|
||||
CommitMessage: "Commit Message",
|
||||
})
|
||||
assert.Equal(t, hexString, pushCommit.Sha1)
|
||||
assert.Equal(t, "Commit Message", pushCommit.Message)
|
||||
assert.Equal(t, "example@example.com", pushCommit.AuthorEmail)
|
||||
assert.Equal(t, "John Doe", pushCommit.AuthorName)
|
||||
assert.Equal(t, "example@example.com", pushCommit.CommitterEmail)
|
||||
assert.Equal(t, "John Doe", pushCommit.CommitterName)
|
||||
assert.Equal(t, now, pushCommit.Timestamp)
|
||||
}
|
||||
|
||||
func TestListToPushCommits(t *testing.T) {
|
||||
now := time.Now()
|
||||
sig := &git.Signature{
|
||||
Email: "example@example.com",
|
||||
Name: "John Doe",
|
||||
When: now,
|
||||
}
|
||||
|
||||
const hexString1 = "0123456789abcdef0123456789abcdef01234567"
|
||||
hash1, err := git.NewIDFromString(hexString1)
|
||||
assert.NoError(t, err)
|
||||
const hexString2 = "fedcba9876543210fedcba9876543210fedcba98"
|
||||
hash2, err := git.NewIDFromString(hexString2)
|
||||
assert.NoError(t, err)
|
||||
|
||||
l := list.New()
|
||||
l.PushBack(&git.Commit{
|
||||
ID: hash1,
|
||||
Author: sig,
|
||||
Committer: sig,
|
||||
CommitMessage: "Message1",
|
||||
})
|
||||
l.PushBack(&git.Commit{
|
||||
ID: hash2,
|
||||
Author: sig,
|
||||
Committer: sig,
|
||||
CommitMessage: "Message2",
|
||||
})
|
||||
|
||||
pushCommits := ListToPushCommits(l)
|
||||
assert.Equal(t, 2, pushCommits.Len)
|
||||
if assert.Len(t, pushCommits.Commits, 2) {
|
||||
assert.Equal(t, "Message1", pushCommits.Commits[0].Message)
|
||||
assert.Equal(t, hexString1, pushCommits.Commits[0].Sha1)
|
||||
assert.Equal(t, "example@example.com", pushCommits.Commits[0].AuthorEmail)
|
||||
assert.Equal(t, now, pushCommits.Commits[0].Timestamp)
|
||||
|
||||
assert.Equal(t, "Message2", pushCommits.Commits[1].Message)
|
||||
assert.Equal(t, hexString2, pushCommits.Commits[1].Sha1)
|
||||
assert.Equal(t, "example@example.com", pushCommits.Commits[1].AuthorEmail)
|
||||
assert.Equal(t, now, pushCommits.Commits[1].Timestamp)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO TestPushUpdate
|
|
@ -0,0 +1,16 @@
|
|||
// Copyright 2019 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 repository
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
models.MainTest(m, filepath.Join("..", ".."))
|
||||
}
|
|
@ -214,10 +214,61 @@ func SyncReleasesWithTags(repo *models.Repository, gitRepo *git.Repository) erro
|
|||
}
|
||||
for _, tagName := range tags {
|
||||
if _, ok := existingRelTags[strings.ToLower(tagName)]; !ok {
|
||||
if err := models.PushUpdateAddTag(repo, gitRepo, tagName); err != nil {
|
||||
return fmt.Errorf("pushUpdateAddTag: %s: %v", tagName, err)
|
||||
if err := PushUpdateAddTag(repo, gitRepo, tagName); err != nil {
|
||||
return fmt.Errorf("pushUpdateAddTag: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// PushUpdateAddTag must be called for any push actions to add tag
|
||||
func PushUpdateAddTag(repo *models.Repository, gitRepo *git.Repository, tagName string) error {
|
||||
tag, err := gitRepo.GetTag(tagName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("GetTag: %v", err)
|
||||
}
|
||||
commit, err := tag.Commit()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Commit: %v", err)
|
||||
}
|
||||
|
||||
sig := tag.Tagger
|
||||
if sig == nil {
|
||||
sig = commit.Author
|
||||
}
|
||||
if sig == nil {
|
||||
sig = commit.Committer
|
||||
}
|
||||
|
||||
var author *models.User
|
||||
var createdAt = time.Unix(1, 0)
|
||||
|
||||
if sig != nil {
|
||||
author, err = models.GetUserByEmail(sig.Email)
|
||||
if err != nil && !models.IsErrUserNotExist(err) {
|
||||
return fmt.Errorf("GetUserByEmail: %v", err)
|
||||
}
|
||||
createdAt = sig.When
|
||||
}
|
||||
|
||||
commitsCount, err := commit.CommitsCount()
|
||||
if err != nil {
|
||||
return fmt.Errorf("CommitsCount: %v", err)
|
||||
}
|
||||
|
||||
var rel = models.Release{
|
||||
RepoID: repo.ID,
|
||||
TagName: tagName,
|
||||
LowerTagName: strings.ToLower(tagName),
|
||||
Sha1: commit.ID.String(),
|
||||
NumCommits: commitsCount,
|
||||
CreatedUnix: timeutil.TimeStamp(createdAt.Unix()),
|
||||
IsTag: true,
|
||||
}
|
||||
if author != nil {
|
||||
rel.PublisherID = author.ID
|
||||
}
|
||||
|
||||
return models.SaveOrUpdateTag(repo, &rel)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
// Copyright 2019 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
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// NotificationThread expose Notification on API
|
||||
type NotificationThread struct {
|
||||
ID int64 `json:"id"`
|
||||
Repository *Repository `json:"repository"`
|
||||
Subject *NotificationSubject `json:"subject"`
|
||||
Unread bool `json:"unread"`
|
||||
Pinned bool `json:"pinned"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
URL string `json:"url"`
|
||||
}
|
||||
|
||||
// NotificationSubject contains the notification subject (Issue/Pull/Commit)
|
||||
type NotificationSubject struct {
|
||||
Title string `json:"title"`
|
||||
URL string `json:"url"`
|
||||
LatestCommentURL string `json:"latest_comment_url"`
|
||||
Type string `json:"type" binding:"In(Issue,Pull,Commit)"`
|
||||
}
|
|
@ -35,12 +35,12 @@ type CreateTeamOption struct {
|
|||
// EditTeamOption options for editing a team
|
||||
type EditTeamOption struct {
|
||||
// required: true
|
||||
Name string `json:"name" binding:"Required;AlphaDashDot;MaxSize(30)"`
|
||||
Description string `json:"description" binding:"MaxSize(255)"`
|
||||
IncludesAllRepositories bool `json:"includes_all_repositories"`
|
||||
Name string `json:"name" binding:"AlphaDashDot;MaxSize(30)"`
|
||||
Description *string `json:"description" binding:"MaxSize(255)"`
|
||||
IncludesAllRepositories *bool `json:"includes_all_repositories"`
|
||||
// enum: read,write,admin
|
||||
Permission string `json:"permission"`
|
||||
// example: ["repo.code","repo.issues","repo.ext_issues","repo.wiki","repo.pulls","repo.releases","repo.ext_wiki"]
|
||||
Units []string `json:"units"`
|
||||
CanCreateOrgRepo bool `json:"can_create_org_repo"`
|
||||
CanCreateOrgRepo *bool `json:"can_create_org_repo"`
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ import (
|
|||
"code.gitea.io/gitea/modules/base"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/markup"
|
||||
"code.gitea.io/gitea/modules/repository"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
@ -579,8 +580,8 @@ func ActionIcon(opType models.ActionType) string {
|
|||
}
|
||||
|
||||
// ActionContent2Commits converts action content to push commits
|
||||
func ActionContent2Commits(act Actioner) *models.PushCommits {
|
||||
push := models.NewPushCommits()
|
||||
func ActionContent2Commits(act Actioner) *repository.PushCommits {
|
||||
push := repository.NewPushCommits()
|
||||
if err := json.Unmarshal([]byte(act.GetContent()), push); err != nil {
|
||||
log.Error("json.Unmarshal:\n%s\nERROR: %v", act.GetContent(), err)
|
||||
}
|
||||
|
|
|
@ -2025,6 +2025,20 @@ monitor.execute_time=Ausführungszeit
|
|||
monitor.process.cancel=Prozess abbrechen
|
||||
monitor.process.cancel_desc=Abbrechen eines Prozesses kann Datenverlust verursachen
|
||||
monitor.process.cancel_notices=Abbrechen: <strong>%s</strong>?
|
||||
monitor.queues=Warteschlangen
|
||||
monitor.queue=Warteschlange: %s
|
||||
monitor.queue.name=Name
|
||||
monitor.queue.type=Typ
|
||||
monitor.queue.numberworkers=Anzahl der Worker
|
||||
monitor.queue.maxnumberworkers=Maximale Anzahl der Worker
|
||||
monitor.queue.review=Konfiguration überprüfen
|
||||
monitor.queue.review_add=Worker hinzufügen/prüfen
|
||||
monitor.queue.configuration=Erstkonfiguration
|
||||
monitor.queue.nopool.title=Kein Worker-Pool
|
||||
monitor.queue.nopool.desc=Diese Warteschlange umgibt andere Warteschlangen und hat selbst keinen Worker-Pool.
|
||||
monitor.queue.pool.timeout=Timeout
|
||||
monitor.queue.pool.addworkers.title=Worker hinzufügen
|
||||
monitor.queue.pool.addworkers.submit=Worker hinzufügen
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -1412,6 +1412,8 @@ settings.protect_approvals_whitelist_enabled=Restringir aprovações a usuários
|
|||
settings.protect_approvals_whitelist_enabled_desc=Somente as avaliações de usuários ou equipes da lista permitida serão contadas com as aprovações necessárias. Sem aprovação da lista permitida, as revisões de qualquer pessoa com acesso de escrita contam para as aprovações necessárias.
|
||||
settings.protect_approvals_whitelist_users=Usuários com permissão de revisão:
|
||||
settings.protect_approvals_whitelist_teams=Equipes com permissão de revisão:
|
||||
settings.dismiss_stale_approvals=Descartar aprovações obsoletas
|
||||
settings.dismiss_stale_approvals_desc=Quando novos commits que mudam o conteúdo do pull request são enviados para o branch, as antigas aprovações serão descartadas.
|
||||
settings.add_protected_branch=Habilitar proteção
|
||||
settings.delete_protected_branch=Desabilitar proteção
|
||||
settings.update_protect_branch_success=Proteção do branch '%s' foi atualizada.
|
||||
|
|
File diff suppressed because it is too large
Load Diff
28
package.json
28
package.json
|
@ -5,26 +5,26 @@
|
|||
"node": ">=10"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "7.7.4",
|
||||
"@babel/plugin-transform-runtime": "7.7.4",
|
||||
"@babel/preset-env": "7.7.4",
|
||||
"@babel/runtime": "7.7.4",
|
||||
"@babel/core": "7.7.7",
|
||||
"@babel/plugin-transform-runtime": "7.7.6",
|
||||
"@babel/preset-env": "7.7.7",
|
||||
"@babel/runtime": "7.7.7",
|
||||
"autoprefixer": "9.7.3",
|
||||
"babel-loader": "8.0.6",
|
||||
"core-js": "3.4.7",
|
||||
"css-loader": "3.2.1",
|
||||
"core-js": "3.6.2",
|
||||
"css-loader": "3.4.1",
|
||||
"cssnano": "4.1.10",
|
||||
"eslint": "6.7.2",
|
||||
"eslint": "6.8.0",
|
||||
"eslint-config-airbnb-base": "14.0.0",
|
||||
"eslint-plugin-import": "2.18.2",
|
||||
"eslint-plugin-import": "2.19.1",
|
||||
"less": "3.10.3",
|
||||
"postcss-cli": "6.1.3",
|
||||
"style-loader": "1.0.1",
|
||||
"stylelint": "12.0.0",
|
||||
"postcss-cli": "7.1.0",
|
||||
"style-loader": "1.1.2",
|
||||
"stylelint": "12.0.1",
|
||||
"stylelint-config-standard": "19.0.0",
|
||||
"terser-webpack-plugin": "2.2.1",
|
||||
"updates": "9.3.0",
|
||||
"webpack": "4.41.2",
|
||||
"terser-webpack-plugin": "2.3.2",
|
||||
"updates": "9.3.3",
|
||||
"webpack": "4.41.5",
|
||||
"webpack-cli": "3.3.10"
|
||||
},
|
||||
"browserslist": [
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
* http://opensource.org/licenses/MIT
|
||||
*
|
||||
*/
|
||||
@import url('https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic&subset=latin&display=swap');
|
||||
/*!
|
||||
* # Fomantic-UI - Reset
|
||||
* http://github.com/fomantic/Fomantic-UI/
|
||||
|
|
|
@ -7,8 +7,7 @@
|
|||
* Released under the MIT license
|
||||
* http://opensource.org/licenses/MIT
|
||||
*
|
||||
*/
|
||||
@import url(https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic&subset=latin&display=swap);/*!
|
||||
*//*!
|
||||
* # Fomantic-UI - Reset
|
||||
* http://github.com/fomantic/Fomantic-UI/
|
||||
*
|
||||
|
|
|
@ -56,10 +56,10 @@ func CreateUser(ctx *context.APIContext, form api.CreateUserOption) {
|
|||
// responses:
|
||||
// "201":
|
||||
// "$ref": "#/responses/User"
|
||||
// "403":
|
||||
// "$ref": "#/responses/forbidden"
|
||||
// "400":
|
||||
// "$ref": "#/responses/error"
|
||||
// "403":
|
||||
// "$ref": "#/responses/forbidden"
|
||||
// "422":
|
||||
// "$ref": "#/responses/validationError"
|
||||
|
||||
|
|
|
@ -70,6 +70,7 @@ import (
|
|||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/routers/api/v1/admin"
|
||||
"code.gitea.io/gitea/routers/api/v1/misc"
|
||||
"code.gitea.io/gitea/routers/api/v1/notify"
|
||||
"code.gitea.io/gitea/routers/api/v1/org"
|
||||
"code.gitea.io/gitea/routers/api/v1/repo"
|
||||
_ "code.gitea.io/gitea/routers/api/v1/swagger" // for swagger generation
|
||||
|
@ -512,6 +513,16 @@ func RegisterRoutes(m *macaron.Macaron) {
|
|||
m.Post("/markdown", bind(api.MarkdownOption{}), misc.Markdown)
|
||||
m.Post("/markdown/raw", misc.MarkdownRaw)
|
||||
|
||||
// Notifications
|
||||
m.Group("/notifications", func() {
|
||||
m.Combo("").
|
||||
Get(notify.ListNotifications).
|
||||
Put(notify.ReadNotifications)
|
||||
m.Combo("/threads/:id").
|
||||
Get(notify.GetThread).
|
||||
Patch(notify.ReadThread)
|
||||
}, reqToken())
|
||||
|
||||
// Users
|
||||
m.Group("/users", func() {
|
||||
m.Get("/search", user.Search)
|
||||
|
@ -593,23 +604,24 @@ func RegisterRoutes(m *macaron.Macaron) {
|
|||
}, reqToken())
|
||||
|
||||
// Repositories
|
||||
m.Post("/org/:org/repos", reqToken(), bind(api.CreateRepoOption{}), repo.CreateOrgRepo)
|
||||
|
||||
m.Group("/repos", func() {
|
||||
m.Get("/search", repo.Search)
|
||||
})
|
||||
|
||||
m.Get("/repos/issues/search", repo.SearchIssues)
|
||||
m.Post("/org/:org/repos", reqToken(), bind(api.CreateRepoOption{}), repo.CreateOrgRepoDeprecated)
|
||||
|
||||
m.Combo("/repositories/:id", reqToken()).Get(repo.GetByID)
|
||||
|
||||
m.Group("/repos", func() {
|
||||
m.Get("/search", repo.Search)
|
||||
|
||||
m.Get("/issues/search", repo.SearchIssues)
|
||||
|
||||
m.Post("/migrate", reqToken(), bind(auth.MigrateRepoForm{}), repo.Migrate)
|
||||
|
||||
m.Group("/:username/:reponame", func() {
|
||||
m.Combo("").Get(reqAnyRepoReader(), repo.Get).
|
||||
Delete(reqToken(), reqOwner(), repo.Delete).
|
||||
Patch(reqToken(), reqAdmin(), bind(api.EditRepoOption{}), context.RepoRef(), repo.Edit)
|
||||
m.Combo("/notifications").
|
||||
Get(reqToken(), notify.ListRepoNotifications).
|
||||
Put(reqToken(), notify.ReadRepoNotifications)
|
||||
m.Group("/hooks", func() {
|
||||
m.Combo("").Get(repo.ListHooks).
|
||||
Post(bind(api.CreateHookOption{}), repo.CreateHook)
|
||||
|
@ -810,10 +822,11 @@ func RegisterRoutes(m *macaron.Macaron) {
|
|||
m.Get("/users/:username/orgs", org.ListUserOrgs)
|
||||
m.Post("/orgs", reqToken(), bind(api.CreateOrgOption{}), org.Create)
|
||||
m.Group("/orgs/:orgname", func() {
|
||||
m.Get("/repos", user.ListOrgRepos)
|
||||
m.Combo("").Get(org.Get).
|
||||
Patch(reqToken(), reqOrgOwnership(), bind(api.EditOrgOption{}), org.Edit).
|
||||
Delete(reqToken(), reqOrgOwnership(), org.Delete)
|
||||
m.Combo("/repos").Get(user.ListOrgRepos).
|
||||
Post(reqToken(), bind(api.CreateRepoOption{}), repo.CreateOrgRepo)
|
||||
m.Group("/members", func() {
|
||||
m.Get("", org.ListMembers)
|
||||
m.Combo("/:username").Get(org.IsMember).
|
||||
|
|
|
@ -0,0 +1,151 @@
|
|||
// Copyright 2020 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 notify
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/routers/api/v1/utils"
|
||||
)
|
||||
|
||||
// ListRepoNotifications list users's notification threads on a specific repo
|
||||
func ListRepoNotifications(ctx *context.APIContext) {
|
||||
// swagger:operation GET /repos/{owner}/{repo}/notifications notification notifyGetRepoList
|
||||
// ---
|
||||
// summary: List users's notification threads on a specific repo
|
||||
// consumes:
|
||||
// - application/json
|
||||
// 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: all
|
||||
// in: query
|
||||
// description: If true, show notifications marked as read. Default value is false
|
||||
// type: string
|
||||
// required: false
|
||||
// - name: since
|
||||
// in: query
|
||||
// description: Only show notifications updated after the given time. This is a timestamp in RFC 3339 format
|
||||
// type: string
|
||||
// format: date-time
|
||||
// required: false
|
||||
// - name: before
|
||||
// in: query
|
||||
// description: Only show notifications updated before the given time. This is a timestamp in RFC 3339 format
|
||||
// type: string
|
||||
// format: date-time
|
||||
// required: false
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/NotificationThreadList"
|
||||
|
||||
before, since, err := utils.GetQueryBeforeSince(ctx)
|
||||
if err != nil {
|
||||
ctx.InternalServerError(err)
|
||||
return
|
||||
}
|
||||
opts := models.FindNotificationOptions{
|
||||
UserID: ctx.User.ID,
|
||||
RepoID: ctx.Repo.Repository.ID,
|
||||
UpdatedBeforeUnix: before,
|
||||
UpdatedAfterUnix: since,
|
||||
}
|
||||
qAll := strings.Trim(ctx.Query("all"), " ")
|
||||
if qAll != "true" {
|
||||
opts.Status = models.NotificationStatusUnread
|
||||
}
|
||||
nl, err := models.GetNotifications(opts)
|
||||
if err != nil {
|
||||
ctx.InternalServerError(err)
|
||||
return
|
||||
}
|
||||
err = nl.LoadAttributes()
|
||||
if err != nil {
|
||||
ctx.InternalServerError(err)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, nl.APIFormat())
|
||||
}
|
||||
|
||||
// ReadRepoNotifications mark notification threads as read on a specific repo
|
||||
func ReadRepoNotifications(ctx *context.APIContext) {
|
||||
// swagger:operation PUT /repos/{owner}/{repo}/notifications notification notifyReadRepoList
|
||||
// ---
|
||||
// summary: Mark notification threads as read on a specific repo
|
||||
// consumes:
|
||||
// - application/json
|
||||
// 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: last_read_at
|
||||
// in: query
|
||||
// description: Describes the last point that notifications were checked. Anything updated since this time will not be updated.
|
||||
// type: string
|
||||
// format: date-time
|
||||
// required: false
|
||||
// responses:
|
||||
// "205":
|
||||
// "$ref": "#/responses/empty"
|
||||
|
||||
lastRead := int64(0)
|
||||
qLastRead := strings.Trim(ctx.Query("last_read_at"), " ")
|
||||
if len(qLastRead) > 0 {
|
||||
tmpLastRead, err := time.Parse(time.RFC3339, qLastRead)
|
||||
if err != nil {
|
||||
ctx.InternalServerError(err)
|
||||
return
|
||||
}
|
||||
if !tmpLastRead.IsZero() {
|
||||
lastRead = tmpLastRead.Unix()
|
||||
}
|
||||
}
|
||||
opts := models.FindNotificationOptions{
|
||||
UserID: ctx.User.ID,
|
||||
RepoID: ctx.Repo.Repository.ID,
|
||||
UpdatedBeforeUnix: lastRead,
|
||||
Status: models.NotificationStatusUnread,
|
||||
}
|
||||
nl, err := models.GetNotifications(opts)
|
||||
if err != nil {
|
||||
ctx.InternalServerError(err)
|
||||
return
|
||||
}
|
||||
|
||||
for _, n := range nl {
|
||||
err := models.SetNotificationStatus(n.ID, ctx.User, models.NotificationStatusRead)
|
||||
if err != nil {
|
||||
ctx.InternalServerError(err)
|
||||
return
|
||||
}
|
||||
ctx.Status(http.StatusResetContent)
|
||||
}
|
||||
|
||||
ctx.Status(http.StatusResetContent)
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
// Copyright 2020 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 notify
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
)
|
||||
|
||||
// GetThread get notification by ID
|
||||
func GetThread(ctx *context.APIContext) {
|
||||
// swagger:operation GET /notifications/threads/{id} notification notifyGetThread
|
||||
// ---
|
||||
// summary: Get notification thread by ID
|
||||
// consumes:
|
||||
// - application/json
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: id
|
||||
// in: path
|
||||
// description: id of notification thread
|
||||
// type: string
|
||||
// required: true
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/NotificationThread"
|
||||
// "403":
|
||||
// "$ref": "#/responses/forbidden"
|
||||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
|
||||
n := getThread(ctx)
|
||||
if n == nil {
|
||||
return
|
||||
}
|
||||
if err := n.LoadAttributes(); err != nil {
|
||||
ctx.InternalServerError(err)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, n.APIFormat())
|
||||
}
|
||||
|
||||
// ReadThread mark notification as read by ID
|
||||
func ReadThread(ctx *context.APIContext) {
|
||||
// swagger:operation PATCH /notifications/threads/{id} notification notifyReadThread
|
||||
// ---
|
||||
// summary: Mark notification thread as read by ID
|
||||
// consumes:
|
||||
// - application/json
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: id
|
||||
// in: path
|
||||
// description: id of notification thread
|
||||
// type: string
|
||||
// required: true
|
||||
// responses:
|
||||
// "205":
|
||||
// "$ref": "#/responses/empty"
|
||||
// "403":
|
||||
// "$ref": "#/responses/forbidden"
|
||||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
|
||||
n := getThread(ctx)
|
||||
if n == nil {
|
||||
return
|
||||
}
|
||||
|
||||
err := models.SetNotificationStatus(n.ID, ctx.User, models.NotificationStatusRead)
|
||||
if err != nil {
|
||||
ctx.InternalServerError(err)
|
||||
return
|
||||
}
|
||||
ctx.Status(http.StatusResetContent)
|
||||
}
|
||||
|
||||
func getThread(ctx *context.APIContext) *models.Notification {
|
||||
n, err := models.GetNotificationByID(ctx.ParamsInt64(":id"))
|
||||
if err != nil {
|
||||
if models.IsErrNotExist(err) {
|
||||
ctx.Error(http.StatusNotFound, "GetNotificationByID", err)
|
||||
} else {
|
||||
ctx.InternalServerError(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if n.UserID != ctx.User.ID && !ctx.User.IsAdmin {
|
||||
ctx.Error(http.StatusForbidden, "GetNotificationByID", fmt.Errorf("only user itself and admin are allowed to read/change this thread %d", n.ID))
|
||||
return nil
|
||||
}
|
||||
return n
|
||||
}
|
|
@ -0,0 +1,129 @@
|
|||
// Copyright 2020 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 notify
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/routers/api/v1/utils"
|
||||
)
|
||||
|
||||
// ListNotifications list users's notification threads
|
||||
func ListNotifications(ctx *context.APIContext) {
|
||||
// swagger:operation GET /notifications notification notifyGetList
|
||||
// ---
|
||||
// summary: List users's notification threads
|
||||
// consumes:
|
||||
// - application/json
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: all
|
||||
// in: query
|
||||
// description: If true, show notifications marked as read. Default value is false
|
||||
// type: string
|
||||
// required: false
|
||||
// - name: since
|
||||
// in: query
|
||||
// description: Only show notifications updated after the given time. This is a timestamp in RFC 3339 format
|
||||
// type: string
|
||||
// format: date-time
|
||||
// required: false
|
||||
// - name: before
|
||||
// in: query
|
||||
// description: Only show notifications updated before the given time. This is a timestamp in RFC 3339 format
|
||||
// type: string
|
||||
// format: date-time
|
||||
// required: false
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/NotificationThreadList"
|
||||
|
||||
before, since, err := utils.GetQueryBeforeSince(ctx)
|
||||
if err != nil {
|
||||
ctx.InternalServerError(err)
|
||||
return
|
||||
}
|
||||
opts := models.FindNotificationOptions{
|
||||
UserID: ctx.User.ID,
|
||||
UpdatedBeforeUnix: before,
|
||||
UpdatedAfterUnix: since,
|
||||
}
|
||||
qAll := strings.Trim(ctx.Query("all"), " ")
|
||||
if qAll != "true" {
|
||||
opts.Status = models.NotificationStatusUnread
|
||||
}
|
||||
nl, err := models.GetNotifications(opts)
|
||||
if err != nil {
|
||||
ctx.InternalServerError(err)
|
||||
return
|
||||
}
|
||||
err = nl.LoadAttributes()
|
||||
if err != nil {
|
||||
ctx.InternalServerError(err)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, nl.APIFormat())
|
||||
}
|
||||
|
||||
// ReadNotifications mark notification threads as read
|
||||
func ReadNotifications(ctx *context.APIContext) {
|
||||
// swagger:operation PUT /notifications notification notifyReadList
|
||||
// ---
|
||||
// summary: Mark notification threads as read
|
||||
// consumes:
|
||||
// - application/json
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: last_read_at
|
||||
// in: query
|
||||
// description: Describes the last point that notifications were checked. Anything updated since this time will not be updated.
|
||||
// type: string
|
||||
// format: date-time
|
||||
// required: false
|
||||
// responses:
|
||||
// "205":
|
||||
// "$ref": "#/responses/empty"
|
||||
|
||||
lastRead := int64(0)
|
||||
qLastRead := strings.Trim(ctx.Query("last_read_at"), " ")
|
||||
if len(qLastRead) > 0 {
|
||||
tmpLastRead, err := time.Parse(time.RFC3339, qLastRead)
|
||||
if err != nil {
|
||||
ctx.InternalServerError(err)
|
||||
return
|
||||
}
|
||||
if !tmpLastRead.IsZero() {
|
||||
lastRead = tmpLastRead.Unix()
|
||||
}
|
||||
}
|
||||
opts := models.FindNotificationOptions{
|
||||
UserID: ctx.User.ID,
|
||||
UpdatedBeforeUnix: lastRead,
|
||||
Status: models.NotificationStatusUnread,
|
||||
}
|
||||
nl, err := models.GetNotifications(opts)
|
||||
if err != nil {
|
||||
ctx.InternalServerError(err)
|
||||
return
|
||||
}
|
||||
|
||||
for _, n := range nl {
|
||||
err := models.SetNotificationStatus(n.ID, ctx.User, models.NotificationStatusRead)
|
||||
if err != nil {
|
||||
ctx.InternalServerError(err)
|
||||
return
|
||||
}
|
||||
ctx.Status(http.StatusResetContent)
|
||||
}
|
||||
|
||||
ctx.Status(http.StatusResetContent)
|
||||
}
|
|
@ -192,37 +192,52 @@ func EditTeam(ctx *context.APIContext, form api.EditTeamOption) {
|
|||
// "$ref": "#/responses/Team"
|
||||
|
||||
team := ctx.Org.Team
|
||||
team.Description = form.Description
|
||||
unitTypes := models.FindUnitTypes(form.Units...)
|
||||
team.CanCreateOrgRepo = form.CanCreateOrgRepo
|
||||
if err := team.GetUnits(); err != nil {
|
||||
ctx.InternalServerError(err)
|
||||
return
|
||||
}
|
||||
|
||||
if form.CanCreateOrgRepo != nil {
|
||||
team.CanCreateOrgRepo = *form.CanCreateOrgRepo
|
||||
}
|
||||
|
||||
if len(form.Name) > 0 {
|
||||
team.Name = form.Name
|
||||
}
|
||||
|
||||
if form.Description != nil {
|
||||
team.Description = *form.Description
|
||||
}
|
||||
|
||||
isAuthChanged := false
|
||||
isIncludeAllChanged := false
|
||||
if !team.IsOwnerTeam() {
|
||||
if !team.IsOwnerTeam() && len(form.Permission) != 0 {
|
||||
// Validate permission level.
|
||||
auth := models.ParseAccessMode(form.Permission)
|
||||
|
||||
team.Name = form.Name
|
||||
if team.Authorize != auth {
|
||||
isAuthChanged = true
|
||||
team.Authorize = auth
|
||||
}
|
||||
|
||||
if team.IncludesAllRepositories != form.IncludesAllRepositories {
|
||||
if form.IncludesAllRepositories != nil {
|
||||
isIncludeAllChanged = true
|
||||
team.IncludesAllRepositories = form.IncludesAllRepositories
|
||||
team.IncludesAllRepositories = *form.IncludesAllRepositories
|
||||
}
|
||||
}
|
||||
|
||||
if team.Authorize < models.AccessModeOwner {
|
||||
var units = make([]*models.TeamUnit, 0, len(form.Units))
|
||||
for _, tp := range unitTypes {
|
||||
units = append(units, &models.TeamUnit{
|
||||
OrgID: ctx.Org.Team.OrgID,
|
||||
Type: tp,
|
||||
})
|
||||
if len(form.Units) > 0 {
|
||||
var units = make([]*models.TeamUnit, 0, len(form.Units))
|
||||
unitTypes := models.FindUnitTypes(form.Units...)
|
||||
for _, tp := range unitTypes {
|
||||
units = append(units, &models.TeamUnit{
|
||||
OrgID: ctx.Org.Team.OrgID,
|
||||
Type: tp,
|
||||
})
|
||||
}
|
||||
team.Units = units
|
||||
}
|
||||
team.Units = units
|
||||
}
|
||||
|
||||
if err := models.UpdateTeam(team, isAuthChanged, isIncludeAllChanged); err != nil {
|
||||
|
|
|
@ -13,6 +13,7 @@ import (
|
|||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/auth"
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/modules/convert"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/notification"
|
||||
|
@ -102,7 +103,7 @@ func ListPullRequests(ctx *context.APIContext, form api.ListPullRequestsOptions)
|
|||
ctx.Error(http.StatusInternalServerError, "GetHeadRepo", err)
|
||||
return
|
||||
}
|
||||
apiPrs[i] = prs[i].APIFormat()
|
||||
apiPrs[i] = convert.ToAPIPullRequest(prs[i])
|
||||
}
|
||||
|
||||
ctx.SetLinkHeader(int(maxResults), models.ItemsPerPage)
|
||||
|
@ -136,6 +137,8 @@ func GetPullRequest(ctx *context.APIContext) {
|
|||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/PullRequest"
|
||||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
|
||||
pr, err := models.GetPullRequestByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
|
||||
if err != nil {
|
||||
|
@ -155,7 +158,7 @@ func GetPullRequest(ctx *context.APIContext) {
|
|||
ctx.Error(http.StatusInternalServerError, "GetHeadRepo", err)
|
||||
return
|
||||
}
|
||||
ctx.JSON(http.StatusOK, pr.APIFormat())
|
||||
ctx.JSON(http.StatusOK, convert.ToAPIPullRequest(pr))
|
||||
}
|
||||
|
||||
// CreatePullRequest does what it says
|
||||
|
@ -319,7 +322,7 @@ func CreatePullRequest(ctx *context.APIContext, form api.CreatePullRequestOption
|
|||
notification.NotifyNewPullRequest(pr)
|
||||
|
||||
log.Trace("Pull request created: %d/%d", repo.ID, prIssue.ID)
|
||||
ctx.JSON(http.StatusCreated, pr.APIFormat())
|
||||
ctx.JSON(http.StatusCreated, convert.ToAPIPullRequest(pr))
|
||||
}
|
||||
|
||||
// EditPullRequest does what it says
|
||||
|
@ -477,7 +480,7 @@ func EditPullRequest(ctx *context.APIContext, form api.EditPullRequestOption) {
|
|||
}
|
||||
|
||||
// TODO this should be 200, not 201
|
||||
ctx.JSON(http.StatusCreated, pr.APIFormat())
|
||||
ctx.JSON(http.StatusCreated, convert.ToAPIPullRequest(pr))
|
||||
}
|
||||
|
||||
// IsPullRequestMerged checks if a PR exists given an index
|
||||
|
|
|
@ -282,11 +282,12 @@ func Create(ctx *context.APIContext, opt api.CreateRepoOption) {
|
|||
CreateUserRepo(ctx, ctx.User, opt)
|
||||
}
|
||||
|
||||
// CreateOrgRepo create one repository of the organization
|
||||
func CreateOrgRepo(ctx *context.APIContext, opt api.CreateRepoOption) {
|
||||
// swagger:operation POST /org/{org}/repos organization createOrgRepo
|
||||
// CreateOrgRepoDeprecated create one repository of the organization
|
||||
func CreateOrgRepoDeprecated(ctx *context.APIContext, opt api.CreateRepoOption) {
|
||||
// swagger:operation POST /org/{org}/repos organization createOrgRepoDeprecated
|
||||
// ---
|
||||
// summary: Create a repository in an organization
|
||||
// deprecated: true
|
||||
// consumes:
|
||||
// - application/json
|
||||
// produces:
|
||||
|
@ -309,6 +310,37 @@ func CreateOrgRepo(ctx *context.APIContext, opt api.CreateRepoOption) {
|
|||
// "403":
|
||||
// "$ref": "#/responses/forbidden"
|
||||
|
||||
CreateOrgRepo(ctx, opt)
|
||||
}
|
||||
|
||||
// CreateOrgRepo create one repository of the organization
|
||||
func CreateOrgRepo(ctx *context.APIContext, opt api.CreateRepoOption) {
|
||||
// swagger:operation POST /orgs/{org}/repos organization createOrgRepo
|
||||
// ---
|
||||
// summary: Create a repository in an organization
|
||||
// deprecated: true
|
||||
// consumes:
|
||||
// - application/json
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: org
|
||||
// in: path
|
||||
// description: name of organization
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: body
|
||||
// in: body
|
||||
// schema:
|
||||
// "$ref": "#/definitions/CreateRepoOption"
|
||||
// responses:
|
||||
// "201":
|
||||
// "$ref": "#/responses/Repository"
|
||||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
// "403":
|
||||
// "$ref": "#/responses/forbidden"
|
||||
|
||||
org, err := models.GetOrgByName(ctx.Params(":org"))
|
||||
if err != nil {
|
||||
if models.IsErrOrgNotExist(err) {
|
||||
|
@ -452,12 +484,13 @@ func Migrate(ctx *context.APIContext, form auth.MigrateRepoForm) {
|
|||
}
|
||||
|
||||
repo, err := models.CreateRepository(ctx.User, ctxUser, models.CreateRepoOptions{
|
||||
Name: opts.RepoName,
|
||||
Description: opts.Description,
|
||||
OriginalURL: form.CloneAddr,
|
||||
IsPrivate: opts.Private,
|
||||
IsMirror: opts.Mirror,
|
||||
Status: models.RepositoryBeingMigrated,
|
||||
Name: opts.RepoName,
|
||||
Description: opts.Description,
|
||||
OriginalURL: form.CloneAddr,
|
||||
GitServiceType: gitServiceType,
|
||||
IsPrivate: opts.Private,
|
||||
IsMirror: opts.Mirror,
|
||||
Status: models.RepositoryBeingMigrated,
|
||||
})
|
||||
if err != nil {
|
||||
handleMigrateError(ctx, ctxUser, remoteAddr, err)
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
// Copyright 2019 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 swagger
|
||||
|
||||
import (
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
)
|
||||
|
||||
// NotificationThread
|
||||
// swagger:response NotificationThread
|
||||
type swaggerNotificationThread struct {
|
||||
// in:body
|
||||
Body api.NotificationThread `json:"body"`
|
||||
}
|
||||
|
||||
// NotificationThreadList
|
||||
// swagger:response NotificationThreadList
|
||||
type swaggerNotificationThreadList struct {
|
||||
// in:body
|
||||
Body []api.NotificationThread `json:"body"`
|
||||
}
|
|
@ -6,6 +6,7 @@ package repo
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
@ -18,6 +19,7 @@ import (
|
|||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/migrations"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/task"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
repo_service "code.gitea.io/gitea/services/repository"
|
||||
|
@ -330,22 +332,29 @@ func MigratePost(ctx *context.Context, form auth.MigrateRepoForm) {
|
|||
return
|
||||
}
|
||||
|
||||
var gitServiceType = structs.PlainGitService
|
||||
u, err := url.Parse(form.CloneAddr)
|
||||
if err == nil && strings.EqualFold(u.Host, "github.com") {
|
||||
gitServiceType = structs.GithubService
|
||||
}
|
||||
|
||||
var opts = migrations.MigrateOptions{
|
||||
OriginalURL: form.CloneAddr,
|
||||
CloneAddr: remoteAddr,
|
||||
RepoName: form.RepoName,
|
||||
Description: form.Description,
|
||||
Private: form.Private || setting.Repository.ForcePrivate,
|
||||
Mirror: form.Mirror,
|
||||
AuthUsername: form.AuthUsername,
|
||||
AuthPassword: form.AuthPassword,
|
||||
Wiki: form.Wiki,
|
||||
Issues: form.Issues,
|
||||
Milestones: form.Milestones,
|
||||
Labels: form.Labels,
|
||||
Comments: true,
|
||||
PullRequests: form.PullRequests,
|
||||
Releases: form.Releases,
|
||||
OriginalURL: form.CloneAddr,
|
||||
GitServiceType: gitServiceType,
|
||||
CloneAddr: remoteAddr,
|
||||
RepoName: form.RepoName,
|
||||
Description: form.Description,
|
||||
Private: form.Private || setting.Repository.ForcePrivate,
|
||||
Mirror: form.Mirror,
|
||||
AuthUsername: form.AuthUsername,
|
||||
AuthPassword: form.AuthPassword,
|
||||
Wiki: form.Wiki,
|
||||
Issues: form.Issues,
|
||||
Milestones: form.Milestones,
|
||||
Labels: form.Labels,
|
||||
Comments: true,
|
||||
PullRequests: form.PullRequests,
|
||||
Releases: form.Releases,
|
||||
}
|
||||
if opts.Mirror {
|
||||
opts.Issues = false
|
||||
|
|
|
@ -403,7 +403,7 @@ func syncMirror(repoID string) {
|
|||
continue
|
||||
}
|
||||
|
||||
theCommits := models.ListToPushCommits(commits)
|
||||
theCommits := repository.ListToPushCommits(commits)
|
||||
if len(theCommits.Commits) > setting.UI.FeedMaxCommitNum {
|
||||
theCommits.Commits = theCommits.Commits[:setting.UI.FeedMaxCommitNum]
|
||||
}
|
||||
|
|
|
@ -194,10 +194,16 @@ func TestPullRequests(ctx context.Context) {
|
|||
if err != nil {
|
||||
log.Error("GetPullRequestByID[%s]: %v", prID, err)
|
||||
continue
|
||||
} else if pr.Status != models.PullRequestStatusChecking {
|
||||
continue
|
||||
} else if manuallyMerged(pr) {
|
||||
continue
|
||||
} else if err = TestPatch(pr); err != nil {
|
||||
log.Error("testPatch[%d]: %v", pr.ID, err)
|
||||
pr.Status = models.PullRequestStatusError
|
||||
if err := pr.UpdateCols("status"); err != nil {
|
||||
log.Error("update pr [%d] status to PullRequestStatusError failed: %v", pr.ID, err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
checkAndUpdateStatus(pr)
|
||||
|
|
|
@ -13,6 +13,7 @@ import (
|
|||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/notification"
|
||||
"code.gitea.io/gitea/modules/repository"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
)
|
||||
|
||||
|
@ -43,7 +44,7 @@ func createTag(gitRepo *git.Repository, rel *models.Release) error {
|
|||
}
|
||||
notification.NotifyPushCommits(
|
||||
rel.Publisher, rel.Repo, git.TagPrefix+rel.TagName,
|
||||
git.EmptySHA, commit.ID.String(), models.NewPushCommits())
|
||||
git.EmptySHA, commit.ID.String(), repository.NewPushCommits())
|
||||
notification.NotifyCreateRef(rel.Publisher, rel.Repo, "tag", git.TagPrefix+rel.TagName)
|
||||
}
|
||||
commit, err := gitRepo.GetTagCommit(rel.TagName)
|
||||
|
|
|
@ -153,7 +153,7 @@
|
|||
<input type="text" name="merge_title_field" value="{{.Issue.PullRequest.GetDefaultMergeMessage}}">
|
||||
</div>
|
||||
<div class="field">
|
||||
<textarea name="merge_message_field" rows="5" placeholder="{{$.i18n.Tr "repo.editor.commit_message_desc"}}">{{$approvers}}</textarea>
|
||||
<textarea name="merge_message_field" rows="5" placeholder="{{$.i18n.Tr "repo.editor.commit_message_desc"}}">Reviewed-on: {{$.Issue.HTMLURL}} {{$approvers}}</textarea>
|
||||
</div>
|
||||
<button class="ui green button" type="submit" name="do" value="merge">
|
||||
{{$.i18n.Tr "repo.pulls.merge_pull_request"}}
|
||||
|
@ -185,7 +185,7 @@
|
|||
<input type="text" name="merge_title_field" value="{{.Issue.PullRequest.GetDefaultMergeMessage}}">
|
||||
</div>
|
||||
<div class="field">
|
||||
<textarea name="merge_message_field" rows="5" placeholder="{{$.i18n.Tr "repo.editor.commit_message_desc"}}">{{$approvers}}</textarea>
|
||||
<textarea name="merge_message_field" rows="5" placeholder="{{$.i18n.Tr "repo.editor.commit_message_desc"}}">Reviewed-on: {{$.Issue.HTMLURL}} {{$approvers}}</textarea>
|
||||
</div>
|
||||
<button class="ui green button" type="submit" name="do" value="rebase-merge">
|
||||
{{$.i18n.Tr "repo.pulls.rebase_merge_commit_pull_request"}}
|
||||
|
@ -205,7 +205,7 @@
|
|||
<input type="text" name="merge_title_field" value="{{.Issue.PullRequest.GetDefaultSquashMessage}}">
|
||||
</div>
|
||||
<div class="field">
|
||||
<textarea name="merge_message_field" rows="5" placeholder="{{$.i18n.Tr "repo.editor.commit_message_desc"}}">{{$commitMessages}}{{$approvers}}</textarea>
|
||||
<textarea name="merge_message_field" rows="5" placeholder="{{$.i18n.Tr "repo.editor.commit_message_desc"}}">{{$commitMessages}}Reviewed-on: {{$.Issue.HTMLURL}} {{$approvers}}</textarea>
|
||||
</div>
|
||||
<button class="ui green button" type="submit" name="do" value="squash">
|
||||
{{$.i18n.Tr "repo.pulls.squash_merge_pull_request"}}
|
||||
|
|
|
@ -425,6 +425,143 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"/notifications": {
|
||||
"get": {
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"notification"
|
||||
],
|
||||
"summary": "List users's notification threads",
|
||||
"operationId": "notifyGetList",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "If true, show notifications marked as read. Default value is false",
|
||||
"name": "all",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "Only show notifications updated after the given time. This is a timestamp in RFC 3339 format",
|
||||
"name": "since",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "Only show notifications updated before the given time. This is a timestamp in RFC 3339 format",
|
||||
"name": "before",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"$ref": "#/responses/NotificationThreadList"
|
||||
}
|
||||
}
|
||||
},
|
||||
"put": {
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"notification"
|
||||
],
|
||||
"summary": "Mark notification threads as read",
|
||||
"operationId": "notifyReadList",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "Describes the last point that notifications were checked. Anything updated since this time will not be updated.",
|
||||
"name": "last_read_at",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"205": {
|
||||
"$ref": "#/responses/empty"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/notifications/threads/{id}": {
|
||||
"get": {
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"notification"
|
||||
],
|
||||
"summary": "Get notification thread by ID",
|
||||
"operationId": "notifyGetThread",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "id of notification thread",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"$ref": "#/responses/NotificationThread"
|
||||
},
|
||||
"403": {
|
||||
"$ref": "#/responses/forbidden"
|
||||
},
|
||||
"404": {
|
||||
"$ref": "#/responses/notFound"
|
||||
}
|
||||
}
|
||||
},
|
||||
"patch": {
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"notification"
|
||||
],
|
||||
"summary": "Mark notification thread as read by ID",
|
||||
"operationId": "notifyReadThread",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "id of notification thread",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"205": {
|
||||
"$ref": "#/responses/empty"
|
||||
},
|
||||
"403": {
|
||||
"$ref": "#/responses/forbidden"
|
||||
},
|
||||
"404": {
|
||||
"$ref": "#/responses/notFound"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/org/{org}/repos": {
|
||||
"post": {
|
||||
"consumes": [
|
||||
|
@ -437,7 +574,8 @@
|
|||
"organization"
|
||||
],
|
||||
"summary": "Create a repository in an organization",
|
||||
"operationId": "createOrgRepo",
|
||||
"operationId": "createOrgRepoDeprecated",
|
||||
"deprecated": true,
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
|
@ -1003,6 +1141,47 @@
|
|||
"$ref": "#/responses/RepositoryList"
|
||||
}
|
||||
}
|
||||
},
|
||||
"post": {
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"organization"
|
||||
],
|
||||
"summary": "Create a repository in an organization",
|
||||
"operationId": "createOrgRepo",
|
||||
"deprecated": true,
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "name of organization",
|
||||
"name": "org",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/CreateRepoOption"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"201": {
|
||||
"$ref": "#/responses/Repository"
|
||||
},
|
||||
"403": {
|
||||
"$ref": "#/responses/forbidden"
|
||||
},
|
||||
"404": {
|
||||
"$ref": "#/responses/notFound"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/orgs/{org}/teams": {
|
||||
|
@ -5231,6 +5410,103 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"/repos/{owner}/{repo}/notifications": {
|
||||
"get": {
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"notification"
|
||||
],
|
||||
"summary": "List users's notification threads on a specific repo",
|
||||
"operationId": "notifyGetRepoList",
|
||||
"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": "If true, show notifications marked as read. Default value is false",
|
||||
"name": "all",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "Only show notifications updated after the given time. This is a timestamp in RFC 3339 format",
|
||||
"name": "since",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "Only show notifications updated before the given time. This is a timestamp in RFC 3339 format",
|
||||
"name": "before",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"$ref": "#/responses/NotificationThreadList"
|
||||
}
|
||||
}
|
||||
},
|
||||
"put": {
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"notification"
|
||||
],
|
||||
"summary": "Mark notification threads as read on a specific repo",
|
||||
"operationId": "notifyReadRepoList",
|
||||
"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",
|
||||
"format": "date-time",
|
||||
"description": "Describes the last point that notifications were checked. Anything updated since this time will not be updated.",
|
||||
"name": "last_read_at",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"205": {
|
||||
"$ref": "#/responses/empty"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/repos/{owner}/{repo}/pulls": {
|
||||
"get": {
|
||||
"produces": [
|
||||
|
@ -5397,6 +5673,9 @@
|
|||
"responses": {
|
||||
"200": {
|
||||
"$ref": "#/responses/PullRequest"
|
||||
},
|
||||
"404": {
|
||||
"$ref": "#/responses/notFound"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -10584,6 +10863,64 @@
|
|||
},
|
||||
"x-go-package": "code.gitea.io/gitea/modules/structs"
|
||||
},
|
||||
"NotificationSubject": {
|
||||
"description": "NotificationSubject contains the notification subject (Issue/Pull/Commit)",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"latest_comment_url": {
|
||||
"type": "string",
|
||||
"x-go-name": "LatestCommentURL"
|
||||
},
|
||||
"title": {
|
||||
"type": "string",
|
||||
"x-go-name": "Title"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"x-go-name": "Type"
|
||||
},
|
||||
"url": {
|
||||
"type": "string",
|
||||
"x-go-name": "URL"
|
||||
}
|
||||
},
|
||||
"x-go-package": "code.gitea.io/gitea/modules/structs"
|
||||
},
|
||||
"NotificationThread": {
|
||||
"description": "NotificationThread expose Notification on API",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"x-go-name": "ID"
|
||||
},
|
||||
"pinned": {
|
||||
"type": "boolean",
|
||||
"x-go-name": "Pinned"
|
||||
},
|
||||
"repository": {
|
||||
"$ref": "#/definitions/Repository"
|
||||
},
|
||||
"subject": {
|
||||
"$ref": "#/definitions/NotificationSubject"
|
||||
},
|
||||
"unread": {
|
||||
"type": "boolean",
|
||||
"x-go-name": "Unread"
|
||||
},
|
||||
"updated_at": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"x-go-name": "UpdatedAt"
|
||||
},
|
||||
"url": {
|
||||
"type": "string",
|
||||
"x-go-name": "URL"
|
||||
}
|
||||
},
|
||||
"x-go-package": "code.gitea.io/gitea/modules/structs"
|
||||
},
|
||||
"Organization": {
|
||||
"description": "Organization represents an organization",
|
||||
"type": "object",
|
||||
|
@ -12012,6 +12349,21 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"NotificationThread": {
|
||||
"description": "NotificationThread",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/NotificationThread"
|
||||
}
|
||||
},
|
||||
"NotificationThreadList": {
|
||||
"description": "NotificationThreadList",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/NotificationThread"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Organization": {
|
||||
"description": "Organization",
|
||||
"schema": {
|
||||
|
|
|
@ -774,6 +774,7 @@ footer {
|
|||
.container {
|
||||
width: 100vw !important;
|
||||
padding: 0 0.5rem;
|
||||
max-width: calc(100vw - 1rem) !important;
|
||||
|
||||
.fa {
|
||||
width: 16px;
|
||||
|
|
Loading…
Reference in New Issue