2018-02-21 13:55:34 +03:00
// Copyright 2018 The Gitea Authors. All rights reserved.
2022-11-27 21:20:29 +03:00
// SPDX-License-Identifier: MIT
2018-02-21 13:55:34 +03:00
2022-08-25 05:31:57 +03:00
package activities
2018-02-21 13:55:34 +03:00
2021-09-19 14:49:59 +03:00
import (
2022-05-20 17:08:52 +03:00
"context"
2021-09-19 14:49:59 +03:00
"fmt"
2024-03-12 07:57:19 +03:00
"strconv"
2021-09-19 14:49:59 +03:00
"code.gitea.io/gitea/models/db"
2024-03-12 07:57:19 +03:00
issues_model "code.gitea.io/gitea/models/issues"
2021-12-10 04:27:50 +03:00
repo_model "code.gitea.io/gitea/models/repo"
2021-11-24 12:49:20 +03:00
user_model "code.gitea.io/gitea/models/user"
2022-03-31 12:20:39 +03:00
"code.gitea.io/gitea/modules/container"
2024-03-12 07:57:19 +03:00
"code.gitea.io/gitea/modules/util"
"xorm.io/builder"
2021-09-19 14:49:59 +03:00
)
2018-02-21 13:55:34 +03:00
// ActionList defines a list of actions
type ActionList [ ] * Action
func ( actions ActionList ) getUserIDs ( ) [ ] int64 {
2024-04-09 15:27:30 +03:00
return container . FilterSlice ( actions , func ( action * Action ) ( int64 , bool ) {
return action . ActUserID , true
} )
2018-02-21 13:55:34 +03:00
}
2024-03-12 07:57:19 +03:00
func ( actions ActionList ) LoadActUsers ( ctx context . Context ) ( map [ int64 ] * user_model . User , error ) {
2018-02-21 13:55:34 +03:00
if len ( actions ) == 0 {
return nil , nil
}
userIDs := actions . getUserIDs ( )
2021-11-24 12:49:20 +03:00
userMaps := make ( map [ int64 ] * user_model . User , len ( userIDs ) )
2022-05-20 17:08:52 +03:00
err := db . GetEngine ( ctx ) .
2018-02-21 13:55:34 +03:00
In ( "id" , userIDs ) .
Find ( & userMaps )
if err != nil {
2022-10-24 22:29:17 +03:00
return nil , fmt . Errorf ( "find user: %w" , err )
2018-02-21 13:55:34 +03:00
}
for _ , action := range actions {
action . ActUser = userMaps [ action . ActUserID ]
}
2022-03-13 19:40:47 +03:00
return userMaps , nil
2018-02-21 13:55:34 +03:00
}
func ( actions ActionList ) getRepoIDs ( ) [ ] int64 {
2024-04-09 15:27:30 +03:00
return container . FilterSlice ( actions , func ( action * Action ) ( int64 , bool ) {
return action . RepoID , true
} )
2018-02-21 13:55:34 +03:00
}
2024-03-12 07:57:19 +03:00
func ( actions ActionList ) LoadRepositories ( ctx context . Context ) error {
2018-02-21 13:55:34 +03:00
if len ( actions ) == 0 {
2022-03-13 19:40:47 +03:00
return nil
2018-02-21 13:55:34 +03:00
}
repoIDs := actions . getRepoIDs ( )
2021-12-10 04:27:50 +03:00
repoMaps := make ( map [ int64 ] * repo_model . Repository , len ( repoIDs ) )
2022-05-20 17:08:52 +03:00
err := db . GetEngine ( ctx ) . In ( "id" , repoIDs ) . Find ( & repoMaps )
2018-02-21 13:55:34 +03:00
if err != nil {
2022-10-24 22:29:17 +03:00
return fmt . Errorf ( "find repository: %w" , err )
2018-02-21 13:55:34 +03:00
}
for _ , action := range actions {
action . Repo = repoMaps [ action . RepoID ]
}
2024-03-12 07:57:19 +03:00
repos := repo_model . RepositoryList ( util . ValuesOfMap ( repoMaps ) )
return repos . LoadUnits ( ctx )
2018-02-21 13:55:34 +03:00
}
2022-05-20 17:08:52 +03:00
func ( actions ActionList ) loadRepoOwner ( ctx context . Context , userMap map [ int64 ] * user_model . User ) ( err error ) {
2022-03-13 19:40:47 +03:00
if userMap == nil {
userMap = make ( map [ int64 ] * user_model . User )
2018-02-21 13:55:34 +03:00
}
2024-04-09 15:27:30 +03:00
missingUserIDs := container . FilterSlice ( actions , func ( action * Action ) ( int64 , bool ) {
2022-05-05 18:39:26 +03:00
if action . Repo == nil {
2024-04-09 15:27:30 +03:00
return 0 , false
2024-03-12 07:57:19 +03:00
}
2024-04-09 15:27:30 +03:00
_ , alreadyLoaded := userMap [ action . Repo . OwnerID ]
return action . Repo . OwnerID , ! alreadyLoaded
} )
2024-04-17 04:25:03 +03:00
if len ( missingUserIDs ) == 0 {
return nil
}
2024-03-12 07:57:19 +03:00
if err := db . GetEngine ( ctx ) .
2024-04-09 15:27:30 +03:00
In ( "id" , missingUserIDs ) .
2024-03-12 07:57:19 +03:00
Find ( & userMap ) ; err != nil {
return fmt . Errorf ( "find user: %w" , err )
}
for _ , action := range actions {
if action . Repo != nil {
action . Repo . Owner = userMap [ action . Repo . OwnerID ]
2022-03-13 19:40:47 +03:00
}
2018-02-21 13:55:34 +03:00
}
return nil
}
2024-03-12 07:57:19 +03:00
// LoadAttributes loads all attributes
func ( actions ActionList ) LoadAttributes ( ctx context . Context ) error {
// the load sequence cannot be changed because of the dependencies
userMap , err := actions . LoadActUsers ( ctx )
2022-03-13 19:40:47 +03:00
if err != nil {
return err
}
2024-03-12 07:57:19 +03:00
if err := actions . LoadRepositories ( ctx ) ; err != nil {
return err
}
if err := actions . loadRepoOwner ( ctx , userMap ) ; err != nil {
return err
}
if err := actions . LoadIssues ( ctx ) ; err != nil {
2022-03-13 19:40:47 +03:00
return err
}
2024-03-12 07:57:19 +03:00
return actions . LoadComments ( ctx )
}
func ( actions ActionList ) LoadComments ( ctx context . Context ) error {
if len ( actions ) == 0 {
return nil
}
commentIDs := make ( [ ] int64 , 0 , len ( actions ) )
for _ , action := range actions {
if action . CommentID > 0 {
commentIDs = append ( commentIDs , action . CommentID )
}
}
2024-04-17 04:25:03 +03:00
if len ( commentIDs ) == 0 {
return nil
}
2022-03-13 19:40:47 +03:00
2024-03-12 07:57:19 +03:00
commentsMap := make ( map [ int64 ] * issues_model . Comment , len ( commentIDs ) )
if err := db . GetEngine ( ctx ) . In ( "id" , commentIDs ) . Find ( & commentsMap ) ; err != nil {
return fmt . Errorf ( "find comment: %w" , err )
}
for _ , action := range actions {
if action . CommentID > 0 {
action . Comment = commentsMap [ action . CommentID ]
if action . Comment != nil {
action . Comment . Issue = action . Issue
}
}
}
return nil
}
func ( actions ActionList ) LoadIssues ( ctx context . Context ) error {
if len ( actions ) == 0 {
return nil
}
conditions := builder . NewCond ( )
issueNum := 0
for _ , action := range actions {
if action . IsIssueEvent ( ) {
infos := action . GetIssueInfos ( )
if len ( infos ) == 0 {
continue
}
index , _ := strconv . ParseInt ( infos [ 0 ] , 10 , 64 )
if index > 0 {
conditions = conditions . Or ( builder . Eq {
"repo_id" : action . RepoID ,
"`index`" : index ,
} )
issueNum ++
}
}
}
if ! conditions . IsValid ( ) {
return nil
}
issuesMap := make ( map [ string ] * issues_model . Issue , issueNum )
issues := make ( [ ] * issues_model . Issue , 0 , issueNum )
if err := db . GetEngine ( ctx ) . Where ( conditions ) . Find ( & issues ) ; err != nil {
return fmt . Errorf ( "find issue: %w" , err )
}
for _ , issue := range issues {
issuesMap [ fmt . Sprintf ( "%d-%d" , issue . RepoID , issue . Index ) ] = issue
}
for _ , action := range actions {
if ! action . IsIssueEvent ( ) {
continue
}
if index := action . getIssueIndex ( ) ; index > 0 {
if issue , ok := issuesMap [ fmt . Sprintf ( "%d-%d" , action . RepoID , index ) ] ; ok {
action . Issue = issue
action . Issue . Repo = action . Repo
}
}
}
return nil
2018-02-21 13:55:34 +03:00
}
2024-11-29 20:53:49 +03:00
// GetFeeds returns actions according to the provided options
func GetFeeds ( ctx context . Context , opts GetFeedsOptions ) ( ActionList , int64 , error ) {
if opts . RequestedUser == nil && opts . RequestedTeam == nil && opts . RequestedRepo == nil {
return nil , 0 , fmt . Errorf ( "need at least one of these filters: RequestedUser, RequestedTeam, RequestedRepo" )
}
cond , err := ActivityQueryCondition ( ctx , opts )
if err != nil {
return nil , 0 , err
}
actions := make ( [ ] * Action , 0 , opts . PageSize )
var count int64
opts . SetDefaultValues ( )
if opts . Page < 10 { // TODO: why it's 10 but other values? It's an experience value.
sess := db . GetEngine ( ctx ) . Where ( cond )
sess = db . SetSessionPagination ( sess , & opts )
count , err = sess . Desc ( "`action`.created_unix" ) . FindAndCount ( & actions )
if err != nil {
return nil , 0 , fmt . Errorf ( "FindAndCount: %w" , err )
}
} else {
// First, only query which IDs are necessary, and only then query all actions to speed up the overall query
sess := db . GetEngine ( ctx ) . Where ( cond ) . Select ( "`action`.id" )
sess = db . SetSessionPagination ( sess , & opts )
actionIDs := make ( [ ] int64 , 0 , opts . PageSize )
if err := sess . Table ( "action" ) . Desc ( "`action`.created_unix" ) . Find ( & actionIDs ) ; err != nil {
return nil , 0 , fmt . Errorf ( "Find(actionsIDs): %w" , err )
}
count , err = db . GetEngine ( ctx ) . Where ( cond ) .
Table ( "action" ) .
Cols ( "`action`.id" ) . Count ( )
if err != nil {
return nil , 0 , fmt . Errorf ( "Count: %w" , err )
}
if err := db . GetEngine ( ctx ) . In ( "`action`.id" , actionIDs ) . Desc ( "`action`.created_unix" ) . Find ( & actions ) ; err != nil {
return nil , 0 , fmt . Errorf ( "Find: %w" , err )
}
}
if err := ActionList ( actions ) . LoadAttributes ( ctx ) ; err != nil {
return nil , 0 , fmt . Errorf ( "LoadAttributes: %w" , err )
}
return actions , count , nil
}