Skip to content

Commit

Permalink
RSS/Atom support for Repos (#19055)
Browse files Browse the repository at this point in the history
* support for repos
* refactor
* advertise the feeds via meta tags
* allow feed suffix and feed header
* optimize performance
  • Loading branch information
6543 committed Mar 13, 2022
1 parent 780cf76 commit bc0d2c8
Show file tree
Hide file tree
Showing 14 changed files with 188 additions and 110 deletions.
7 changes: 4 additions & 3 deletions models/action.go
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,7 @@ type GetFeedsOptions struct {
}

// GetFeeds returns actions according to the provided options
func GetFeeds(opts GetFeedsOptions) ([]*Action, error) {
func GetFeeds(ctx context.Context, opts GetFeedsOptions) (ActionList, error) {
if opts.RequestedUser == nil && opts.RequestedTeam == nil && opts.RequestedRepo == nil {
return nil, fmt.Errorf("need at least one of these filters: RequestedUser, RequestedTeam, RequestedRepo")
}
Expand All @@ -338,7 +338,8 @@ func GetFeeds(opts GetFeedsOptions) ([]*Action, error) {
return nil, err
}

sess := db.GetEngine(db.DefaultContext).Where(cond)
e := db.GetEngine(ctx)
sess := e.Where(cond)

opts.SetDefaultValues()
sess = db.SetSessionPagination(sess, &opts)
Expand All @@ -349,7 +350,7 @@ func GetFeeds(opts GetFeedsOptions) ([]*Action, error) {
return nil, fmt.Errorf("Find: %v", err)
}

if err := ActionList(actions).LoadAttributes(); err != nil {
if err := ActionList(actions).loadAttributes(e); err != nil {
return nil, fmt.Errorf("LoadAttributes: %v", err)
}

Expand Down
63 changes: 35 additions & 28 deletions models/action_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ func (actions ActionList) getUserIDs() []int64 {
return keysInt64(userIDs)
}

func (actions ActionList) loadUsers(e db.Engine) ([]*user_model.User, error) {
func (actions ActionList) loadUsers(e db.Engine) (map[int64]*user_model.User, error) {
if len(actions) == 0 {
return nil, nil
}
Expand All @@ -42,12 +42,7 @@ func (actions ActionList) loadUsers(e db.Engine) ([]*user_model.User, error) {
for _, action := range actions {
action.ActUser = userMaps[action.ActUserID]
}
return valuesUser(userMaps), nil
}

// LoadUsers loads actions' all users
func (actions ActionList) LoadUsers() ([]*user_model.User, error) {
return actions.loadUsers(db.GetEngine(db.DefaultContext))
return userMaps, nil
}

func (actions ActionList) getRepoIDs() []int64 {
Expand All @@ -60,45 +55,57 @@ func (actions ActionList) getRepoIDs() []int64 {
return keysInt64(repoIDs)
}

func (actions ActionList) loadRepositories(e db.Engine) ([]*repo_model.Repository, error) {
func (actions ActionList) loadRepositories(e db.Engine) error {
if len(actions) == 0 {
return nil, nil
return nil
}

repoIDs := actions.getRepoIDs()
repoMaps := make(map[int64]*repo_model.Repository, len(repoIDs))
err := e.
In("id", repoIDs).
Find(&repoMaps)
err := e.In("id", repoIDs).Find(&repoMaps)
if err != nil {
return nil, fmt.Errorf("find repository: %v", err)
return fmt.Errorf("find repository: %v", err)
}

for _, action := range actions {
action.Repo = repoMaps[action.RepoID]
}
return valuesRepository(repoMaps), nil
}

// LoadRepositories loads actions' all repositories
func (actions ActionList) LoadRepositories() ([]*repo_model.Repository, error) {
return actions.loadRepositories(db.GetEngine(db.DefaultContext))
return nil
}

// loadAttributes loads all attributes
func (actions ActionList) loadAttributes(e db.Engine) (err error) {
if _, err = actions.loadUsers(e); err != nil {
return
func (actions ActionList) loadRepoOwner(e db.Engine, userMap map[int64]*user_model.User) (err error) {
if userMap == nil {
userMap = make(map[int64]*user_model.User)
}

if _, err = actions.loadRepositories(e); err != nil {
return
for _, action := range actions {
repoOwner, ok := userMap[action.Repo.OwnerID]
if !ok {
repoOwner, err = user_model.GetUserByID(action.Repo.OwnerID)
if err != nil {
if user_model.IsErrUserNotExist(err) {
continue
}
return err
}
userMap[repoOwner.ID] = repoOwner
}
action.Repo.Owner = repoOwner
}

return nil
}

// LoadAttributes loads attributes of the actions
func (actions ActionList) LoadAttributes() error {
return actions.loadAttributes(db.GetEngine(db.DefaultContext))
// loadAttributes loads all attributes
func (actions ActionList) loadAttributes(e db.Engine) error {
userMap, err := actions.loadUsers(e)
if err != nil {
return err
}

if err := actions.loadRepositories(e); err != nil {
return err
}

return actions.loadRepoOwner(e, userMap)
}
50 changes: 46 additions & 4 deletions models/action_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"path"
"testing"

"code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
Expand Down Expand Up @@ -39,7 +40,7 @@ func TestGetFeeds(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)

actions, err := GetFeeds(GetFeedsOptions{
actions, err := GetFeeds(db.DefaultContext, GetFeedsOptions{
RequestedUser: user,
Actor: user,
IncludePrivate: true,
Expand All @@ -52,7 +53,7 @@ func TestGetFeeds(t *testing.T) {
assert.EqualValues(t, user.ID, actions[0].UserID)
}

actions, err = GetFeeds(GetFeedsOptions{
actions, err = GetFeeds(db.DefaultContext, GetFeedsOptions{
RequestedUser: user,
Actor: user,
IncludePrivate: false,
Expand All @@ -62,13 +63,54 @@ func TestGetFeeds(t *testing.T) {
assert.Len(t, actions, 0)
}

func TestGetFeedsForRepos(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
privRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}).(*repo_model.Repository)
pubRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 8}).(*repo_model.Repository)

// private repo & no login
actions, err := GetFeeds(db.DefaultContext, GetFeedsOptions{
RequestedRepo: privRepo,
IncludePrivate: true,
})
assert.NoError(t, err)
assert.Len(t, actions, 0)

// public repo & no login
actions, err = GetFeeds(db.DefaultContext, GetFeedsOptions{
RequestedRepo: pubRepo,
IncludePrivate: true,
})
assert.NoError(t, err)
assert.Len(t, actions, 1)

// private repo and login
actions, err = GetFeeds(db.DefaultContext, GetFeedsOptions{
RequestedRepo: privRepo,
IncludePrivate: true,
Actor: user,
})
assert.NoError(t, err)
assert.Len(t, actions, 1)

// public repo & login
actions, err = GetFeeds(db.DefaultContext, GetFeedsOptions{
RequestedRepo: pubRepo,
IncludePrivate: true,
Actor: user,
})
assert.NoError(t, err)
assert.Len(t, actions, 1)
}

func TestGetFeeds2(t *testing.T) {
// test with an organization user
assert.NoError(t, unittest.PrepareTestDatabase())
org := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3}).(*user_model.User)
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)

actions, err := GetFeeds(GetFeedsOptions{
actions, err := GetFeeds(db.DefaultContext, GetFeedsOptions{
RequestedUser: org,
Actor: user,
IncludePrivate: true,
Expand All @@ -82,7 +124,7 @@ func TestGetFeeds2(t *testing.T) {
assert.EqualValues(t, org.ID, actions[0].UserID)
}

actions, err = GetFeeds(GetFeedsOptions{
actions, err = GetFeeds(db.DefaultContext, GetFeedsOptions{
RequestedUser: org,
Actor: user,
IncludePrivate: false,
Expand Down
14 changes: 7 additions & 7 deletions models/fixtures/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
user_id: 2
op_type: 12 # close issue
act_user_id: 2
repo_id: 2
repo_id: 2 # private
is_private: true
created_unix: 1603228283

Expand All @@ -12,7 +12,7 @@
user_id: 3
op_type: 2 # rename repo
act_user_id: 2
repo_id: 3
repo_id: 3 # private
is_private: true
content: oldRepoName

Expand All @@ -21,38 +21,38 @@
user_id: 11
op_type: 1 # create repo
act_user_id: 11
repo_id: 9
repo_id: 9 # public
is_private: false

-
id: 4
user_id: 16
op_type: 12 # close issue
act_user_id: 16
repo_id: 22
repo_id: 22 # private
is_private: true
created_unix: 1603267920

- id: 5
user_id: 10
op_type: 1 # create repo
act_user_id: 10
repo_id: 6
repo_id: 6 # private
is_private: true
created_unix: 1603010100

- id: 6
user_id: 10
op_type: 1 # create repo
act_user_id: 10
repo_id: 7
repo_id: 7 # private
is_private: true
created_unix: 1603011300

- id: 7
user_id: 10
op_type: 1 # create repo
act_user_id: 10
repo_id: 8
repo_id: 8 # public
is_private: false
created_unix: 1603011540 # grouped with id:7
3 changes: 2 additions & 1 deletion models/user_heatmap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"testing"
"time"

"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/json"
Expand Down Expand Up @@ -72,7 +73,7 @@ func TestGetUserHeatmapDataByUser(t *testing.T) {
}

// get the action for comparison
actions, err := GetFeeds(GetFeedsOptions{
actions, err := GetFeeds(db.DefaultContext, GetFeedsOptions{
RequestedUser: user,
Actor: doer,
IncludePrivate: true,
Expand Down
6 changes: 3 additions & 3 deletions modules/auth/pam/pam.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,10 @@ func Auth(serviceName, userName, passwd string) (string, error) {
if err = t.Authenticate(0); err != nil {
return "", err
}

if err = t.AcctMgmt(0); err != nil {
return "", err
}
return "", err
}

// PAM login names might suffer transformations in the PAM stack.
// We should take whatever the PAM stack returns for it.
Expand Down
2 changes: 2 additions & 0 deletions modules/context/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,8 @@ func RepoAssignment(ctx *Context) (cancel context.CancelFunc) {
userName := ctx.Params(":username")
repoName := ctx.Params(":reponame")
repoName = strings.TrimSuffix(repoName, ".git")
repoName = strings.TrimSuffix(repoName, ".rss")
repoName = strings.TrimSuffix(repoName, ".atom")

// Check if the user is the same as the repository owner
if ctx.IsSigned && ctx.User.LowerName == strings.ToLower(userName) {
Expand Down
18 changes: 17 additions & 1 deletion routers/web/feed/convert.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ package feed
import (
"fmt"
"html"
"net/http"
"net/url"
"strconv"
"strings"
Expand Down Expand Up @@ -66,7 +67,7 @@ func renderMarkdown(ctx *context.Context, act *models.Action, content string) st
}

// feedActionsToFeedItems convert gitea's Action feed to feeds Item
func feedActionsToFeedItems(ctx *context.Context, actions []*models.Action) (items []*feeds.Item, err error) {
func feedActionsToFeedItems(ctx *context.Context, actions models.ActionList) (items []*feeds.Item, err error) {
for _, act := range actions {
act.LoadActUser()

Expand Down Expand Up @@ -247,3 +248,18 @@ func feedActionsToFeedItems(ctx *context.Context, actions []*models.Action) (ite
}
return
}

// GetFeedType return if it is a feed request and altered name and feed type.
func GetFeedType(name string, req *http.Request) (bool, string, string) {
if strings.HasSuffix(name, ".rss") ||
strings.Contains(req.Header.Get("Accept"), "application/rss+xml") {
return true, strings.TrimSuffix(name, ".rss"), "rss"
}

if strings.HasSuffix(name, ".atom") ||
strings.Contains(req.Header.Get("Accept"), "application/atom+xml") {
return true, strings.TrimSuffix(name, ".atom"), "atom"
}

return false, name, ""
}

0 comments on commit bc0d2c8

Please sign in to comment.