Skip to content

Commit 44a49f5

Browse files
committed
Move notifywatch to service layer
1 parent 1b2dfff commit 44a49f5

File tree

5 files changed

+180
-182
lines changed

5 files changed

+180
-182
lines changed

models/activities/action.go

Lines changed: 0 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,7 @@ import (
1616
"code.gitea.io/gitea/models/db"
1717
issues_model "code.gitea.io/gitea/models/issues"
1818
"code.gitea.io/gitea/models/organization"
19-
access_model "code.gitea.io/gitea/models/perm/access"
2019
repo_model "code.gitea.io/gitea/models/repo"
21-
"code.gitea.io/gitea/models/unit"
2220
user_model "code.gitea.io/gitea/models/user"
2321
"code.gitea.io/gitea/modules/git"
2422
"code.gitea.io/gitea/modules/log"
@@ -567,130 +565,6 @@ func DeleteOldActions(ctx context.Context, olderThan time.Duration) (err error)
567565
return err
568566
}
569567

570-
// NotifyWatchers creates batch of actions for every watcher.
571-
// It could insert duplicate actions for a repository action, like this:
572-
// * Original action: UserID=1 (the real actor), ActUserID=1
573-
// * Organization action: UserID=100 (the repo's org), ActUserID=1
574-
// * Watcher action: UserID=20 (a user who is watching a repo), ActUserID=1
575-
func NotifyWatchers(ctx context.Context, actions ...*Action) error {
576-
var watchers []*repo_model.Watch
577-
var repo *repo_model.Repository
578-
var err error
579-
var permCode []bool
580-
var permIssue []bool
581-
var permPR []bool
582-
583-
e := db.GetEngine(ctx)
584-
585-
for _, act := range actions {
586-
repoChanged := repo == nil || repo.ID != act.RepoID
587-
588-
if repoChanged {
589-
// Add feeds for user self and all watchers.
590-
watchers, err = repo_model.GetWatchers(ctx, act.RepoID)
591-
if err != nil {
592-
return fmt.Errorf("get watchers: %w", err)
593-
}
594-
}
595-
596-
// Add feed for actioner.
597-
act.UserID = act.ActUserID
598-
if _, err = e.Insert(act); err != nil {
599-
return fmt.Errorf("insert new actioner: %w", err)
600-
}
601-
602-
if repoChanged {
603-
act.LoadRepo(ctx)
604-
repo = act.Repo
605-
606-
// check repo owner exist.
607-
if err := act.Repo.LoadOwner(ctx); err != nil {
608-
return fmt.Errorf("can't get repo owner: %w", err)
609-
}
610-
} else if act.Repo == nil {
611-
act.Repo = repo
612-
}
613-
614-
// Add feed for organization
615-
if act.Repo.Owner.IsOrganization() && act.ActUserID != act.Repo.Owner.ID {
616-
act.ID = 0
617-
act.UserID = act.Repo.Owner.ID
618-
if err = db.Insert(ctx, act); err != nil {
619-
return fmt.Errorf("insert new actioner: %w", err)
620-
}
621-
}
622-
623-
if repoChanged {
624-
permCode = make([]bool, len(watchers))
625-
permIssue = make([]bool, len(watchers))
626-
permPR = make([]bool, len(watchers))
627-
for i, watcher := range watchers {
628-
user, err := user_model.GetUserByID(ctx, watcher.UserID)
629-
if err != nil {
630-
permCode[i] = false
631-
permIssue[i] = false
632-
permPR[i] = false
633-
continue
634-
}
635-
perm, err := access_model.GetUserRepoPermission(ctx, repo, user)
636-
if err != nil {
637-
permCode[i] = false
638-
permIssue[i] = false
639-
permPR[i] = false
640-
continue
641-
}
642-
permCode[i] = perm.CanRead(unit.TypeCode)
643-
permIssue[i] = perm.CanRead(unit.TypeIssues)
644-
permPR[i] = perm.CanRead(unit.TypePullRequests)
645-
}
646-
}
647-
648-
for i, watcher := range watchers {
649-
if act.ActUserID == watcher.UserID {
650-
continue
651-
}
652-
act.ID = 0
653-
act.UserID = watcher.UserID
654-
act.Repo.Units = nil
655-
656-
switch act.OpType {
657-
case ActionCommitRepo, ActionPushTag, ActionDeleteTag, ActionPublishRelease, ActionDeleteBranch:
658-
if !permCode[i] {
659-
continue
660-
}
661-
case ActionCreateIssue, ActionCommentIssue, ActionCloseIssue, ActionReopenIssue:
662-
if !permIssue[i] {
663-
continue
664-
}
665-
case ActionCreatePullRequest, ActionCommentPull, ActionMergePullRequest, ActionClosePullRequest, ActionReopenPullRequest, ActionAutoMergePullRequest:
666-
if !permPR[i] {
667-
continue
668-
}
669-
}
670-
671-
if err = db.Insert(ctx, act); err != nil {
672-
return fmt.Errorf("insert new action: %w", err)
673-
}
674-
}
675-
}
676-
return nil
677-
}
678-
679-
// NotifyWatchersActions creates batch of actions for every watcher.
680-
func NotifyWatchersActions(ctx context.Context, acts []*Action) error {
681-
ctx, committer, err := db.TxContext(ctx)
682-
if err != nil {
683-
return err
684-
}
685-
defer committer.Close()
686-
for _, act := range acts {
687-
if err := NotifyWatchers(ctx, act); err != nil {
688-
return err
689-
}
690-
}
691-
return committer.Commit()
692-
}
693-
694568
// DeleteIssueActions delete all actions related with issueID
695569
func DeleteIssueActions(ctx context.Context, repoID, issueID, issueIndex int64) error {
696570
// delete actions assigned to this issue

models/activities/action_test.go

Lines changed: 0 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -82,43 +82,6 @@ func TestActivityReadable(t *testing.T) {
8282
}
8383
}
8484

85-
func TestNotifyWatchers(t *testing.T) {
86-
assert.NoError(t, unittest.PrepareTestDatabase())
87-
88-
action := &activities_model.Action{
89-
ActUserID: 8,
90-
RepoID: 1,
91-
OpType: activities_model.ActionStarRepo,
92-
}
93-
assert.NoError(t, activities_model.NotifyWatchers(db.DefaultContext, action))
94-
95-
// One watchers are inactive, thus action is only created for user 8, 1, 4, 11
96-
unittest.AssertExistsAndLoadBean(t, &activities_model.Action{
97-
ActUserID: action.ActUserID,
98-
UserID: 8,
99-
RepoID: action.RepoID,
100-
OpType: action.OpType,
101-
})
102-
unittest.AssertExistsAndLoadBean(t, &activities_model.Action{
103-
ActUserID: action.ActUserID,
104-
UserID: 1,
105-
RepoID: action.RepoID,
106-
OpType: action.OpType,
107-
})
108-
unittest.AssertExistsAndLoadBean(t, &activities_model.Action{
109-
ActUserID: action.ActUserID,
110-
UserID: 4,
111-
RepoID: action.RepoID,
112-
OpType: action.OpType,
113-
})
114-
unittest.AssertExistsAndLoadBean(t, &activities_model.Action{
115-
ActUserID: action.ActUserID,
116-
UserID: 11,
117-
RepoID: action.RepoID,
118-
OpType: action.OpType,
119-
})
120-
}
121-
12285
func TestConsistencyUpdateAction(t *testing.T) {
12386
if !setting.Database.Type.IsSQLite3() {
12487
t.Skip("Test is only for SQLite database.")

services/feed/feed.go

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,135 @@ package feed
55

66
import (
77
"context"
8+
"fmt"
89

910
activities_model "code.gitea.io/gitea/models/activities"
11+
"code.gitea.io/gitea/models/db"
12+
access_model "code.gitea.io/gitea/models/perm/access"
13+
repo_model "code.gitea.io/gitea/models/repo"
14+
"code.gitea.io/gitea/models/unit"
15+
user_model "code.gitea.io/gitea/models/user"
1016
)
1117

1218
// GetFeeds returns actions according to the provided options
1319
func GetFeeds(ctx context.Context, opts activities_model.GetFeedsOptions) (activities_model.ActionList, int64, error) {
1420
return activities_model.GetFeeds(ctx, opts)
1521
}
22+
23+
// NotifyWatchers creates batch of actions for every watcher.
24+
// It could insert duplicate actions for a repository action, like this:
25+
// * Original action: UserID=1 (the real actor), ActUserID=1
26+
// * Organization action: UserID=100 (the repo's org), ActUserID=1
27+
// * Watcher action: UserID=20 (a user who is watching a repo), ActUserID=1
28+
func notifyWatchers(ctx context.Context, act *activities_model.Action) error {
29+
var watchers []*repo_model.Watch
30+
var repo *repo_model.Repository
31+
var err error
32+
var permCode []bool
33+
var permIssue []bool
34+
var permPR []bool
35+
36+
repoChanged := repo == nil || repo.ID != act.RepoID
37+
38+
if repoChanged {
39+
// Add feeds for user self and all watchers.
40+
watchers, err = repo_model.GetWatchers(ctx, act.RepoID)
41+
if err != nil {
42+
return fmt.Errorf("get watchers: %w", err)
43+
}
44+
}
45+
46+
// Add feed for actioner.
47+
act.UserID = act.ActUserID
48+
if err = db.Insert(ctx, act); err != nil {
49+
return fmt.Errorf("insert new actioner: %w", err)
50+
}
51+
52+
if repoChanged {
53+
act.LoadRepo(ctx)
54+
repo = act.Repo
55+
56+
// check repo owner exist.
57+
if err := act.Repo.LoadOwner(ctx); err != nil {
58+
return fmt.Errorf("can't get repo owner: %w", err)
59+
}
60+
} else if act.Repo == nil {
61+
act.Repo = repo
62+
}
63+
64+
// Add feed for organization
65+
if act.Repo.Owner.IsOrganization() && act.ActUserID != act.Repo.Owner.ID {
66+
act.ID = 0
67+
act.UserID = act.Repo.Owner.ID
68+
if err = db.Insert(ctx, act); err != nil {
69+
return fmt.Errorf("insert new actioner: %w", err)
70+
}
71+
}
72+
73+
if repoChanged {
74+
permCode = make([]bool, len(watchers))
75+
permIssue = make([]bool, len(watchers))
76+
permPR = make([]bool, len(watchers))
77+
for i, watcher := range watchers {
78+
user, err := user_model.GetUserByID(ctx, watcher.UserID)
79+
if err != nil {
80+
permCode[i] = false
81+
permIssue[i] = false
82+
permPR[i] = false
83+
continue
84+
}
85+
perm, err := access_model.GetUserRepoPermission(ctx, repo, user)
86+
if err != nil {
87+
permCode[i] = false
88+
permIssue[i] = false
89+
permPR[i] = false
90+
continue
91+
}
92+
permCode[i] = perm.CanRead(unit.TypeCode)
93+
permIssue[i] = perm.CanRead(unit.TypeIssues)
94+
permPR[i] = perm.CanRead(unit.TypePullRequests)
95+
}
96+
}
97+
98+
for i, watcher := range watchers {
99+
if act.ActUserID == watcher.UserID {
100+
continue
101+
}
102+
act.ID = 0
103+
act.UserID = watcher.UserID
104+
act.Repo.Units = nil
105+
106+
switch act.OpType {
107+
case activities_model.ActionCommitRepo, activities_model.ActionPushTag, activities_model.ActionDeleteTag, activities_model.ActionPublishRelease, activities_model.ActionDeleteBranch:
108+
if !permCode[i] {
109+
continue
110+
}
111+
case activities_model.ActionCreateIssue, activities_model.ActionCommentIssue, activities_model.ActionCloseIssue, activities_model.ActionReopenIssue:
112+
if !permIssue[i] {
113+
continue
114+
}
115+
case activities_model.ActionCreatePullRequest, activities_model.ActionCommentPull, activities_model.ActionMergePullRequest, activities_model.ActionClosePullRequest, activities_model.ActionReopenPullRequest, activities_model.ActionAutoMergePullRequest:
116+
if !permPR[i] {
117+
continue
118+
}
119+
}
120+
121+
if err = db.Insert(ctx, act); err != nil {
122+
return fmt.Errorf("insert new action: %w", err)
123+
}
124+
}
125+
126+
return nil
127+
}
128+
129+
// NotifyWatchersActions creates batch of actions for every watcher.
130+
func NotifyWatchers(ctx context.Context, acts ...*activities_model.Action) error {
131+
return db.WithTx(ctx, func(ctx context.Context) error {
132+
for _, act := range acts {
133+
if err := notifyWatchers(ctx, act); err != nil {
134+
return err
135+
}
136+
}
137+
return nil
138+
})
139+
}

services/feed/feed_test.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,3 +163,40 @@ func TestRepoActions(t *testing.T) {
163163
assert.NoError(t, err)
164164
assert.Len(t, actions, 1)
165165
}
166+
167+
func TestNotifyWatchers(t *testing.T) {
168+
assert.NoError(t, unittest.PrepareTestDatabase())
169+
170+
action := &activities_model.Action{
171+
ActUserID: 8,
172+
RepoID: 1,
173+
OpType: activities_model.ActionStarRepo,
174+
}
175+
assert.NoError(t, NotifyWatchers(db.DefaultContext, action))
176+
177+
// One watchers are inactive, thus action is only created for user 8, 1, 4, 11
178+
unittest.AssertExistsAndLoadBean(t, &activities_model.Action{
179+
ActUserID: action.ActUserID,
180+
UserID: 8,
181+
RepoID: action.RepoID,
182+
OpType: action.OpType,
183+
})
184+
unittest.AssertExistsAndLoadBean(t, &activities_model.Action{
185+
ActUserID: action.ActUserID,
186+
UserID: 1,
187+
RepoID: action.RepoID,
188+
OpType: action.OpType,
189+
})
190+
unittest.AssertExistsAndLoadBean(t, &activities_model.Action{
191+
ActUserID: action.ActUserID,
192+
UserID: 4,
193+
RepoID: action.RepoID,
194+
OpType: action.OpType,
195+
})
196+
unittest.AssertExistsAndLoadBean(t, &activities_model.Action{
197+
ActUserID: action.ActUserID,
198+
UserID: 11,
199+
RepoID: action.RepoID,
200+
OpType: action.OpType,
201+
})
202+
}

0 commit comments

Comments
 (0)