gitea源码

action_list.go 7.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. // Copyright 2018 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package activities
  4. import (
  5. "context"
  6. "errors"
  7. "fmt"
  8. "strconv"
  9. "code.gitea.io/gitea/models/db"
  10. issues_model "code.gitea.io/gitea/models/issues"
  11. repo_model "code.gitea.io/gitea/models/repo"
  12. user_model "code.gitea.io/gitea/models/user"
  13. "code.gitea.io/gitea/modules/container"
  14. "code.gitea.io/gitea/modules/util"
  15. "xorm.io/builder"
  16. )
  17. // ActionList defines a list of actions
  18. type ActionList []*Action
  19. func (actions ActionList) getUserIDs() []int64 {
  20. return container.FilterSlice(actions, func(action *Action) (int64, bool) {
  21. return action.ActUserID, true
  22. })
  23. }
  24. func (actions ActionList) LoadActUsers(ctx context.Context) (map[int64]*user_model.User, error) {
  25. if len(actions) == 0 {
  26. return nil, nil
  27. }
  28. userIDs := actions.getUserIDs()
  29. userMaps := make(map[int64]*user_model.User, len(userIDs))
  30. err := db.GetEngine(ctx).
  31. In("id", userIDs).
  32. Find(&userMaps)
  33. if err != nil {
  34. return nil, fmt.Errorf("find user: %w", err)
  35. }
  36. for _, action := range actions {
  37. action.ActUser = userMaps[action.ActUserID]
  38. }
  39. return userMaps, nil
  40. }
  41. func (actions ActionList) getRepoIDs() []int64 {
  42. return container.FilterSlice(actions, func(action *Action) (int64, bool) {
  43. return action.RepoID, true
  44. })
  45. }
  46. func (actions ActionList) LoadRepositories(ctx context.Context) error {
  47. if len(actions) == 0 {
  48. return nil
  49. }
  50. repoIDs := actions.getRepoIDs()
  51. repoMaps := make(map[int64]*repo_model.Repository, len(repoIDs))
  52. err := db.GetEngine(ctx).In("id", repoIDs).Find(&repoMaps)
  53. if err != nil {
  54. return fmt.Errorf("find repository: %w", err)
  55. }
  56. for _, action := range actions {
  57. action.Repo = repoMaps[action.RepoID]
  58. }
  59. repos := repo_model.RepositoryList(util.ValuesOfMap(repoMaps))
  60. return repos.LoadUnits(ctx)
  61. }
  62. func (actions ActionList) loadRepoOwner(ctx context.Context, userMap map[int64]*user_model.User) (err error) {
  63. if userMap == nil {
  64. userMap = make(map[int64]*user_model.User)
  65. }
  66. missingUserIDs := container.FilterSlice(actions, func(action *Action) (int64, bool) {
  67. if action.Repo == nil {
  68. return 0, false
  69. }
  70. _, alreadyLoaded := userMap[action.Repo.OwnerID]
  71. return action.Repo.OwnerID, !alreadyLoaded
  72. })
  73. if len(missingUserIDs) == 0 {
  74. return nil
  75. }
  76. if err := db.GetEngine(ctx).
  77. In("id", missingUserIDs).
  78. Find(&userMap); err != nil {
  79. return fmt.Errorf("find user: %w", err)
  80. }
  81. for _, action := range actions {
  82. if action.Repo != nil {
  83. action.Repo.Owner = userMap[action.Repo.OwnerID]
  84. }
  85. }
  86. return nil
  87. }
  88. // LoadAttributes loads all attributes
  89. func (actions ActionList) LoadAttributes(ctx context.Context) error {
  90. // the load sequence cannot be changed because of the dependencies
  91. userMap, err := actions.LoadActUsers(ctx)
  92. if err != nil {
  93. return err
  94. }
  95. if err := actions.LoadRepositories(ctx); err != nil {
  96. return err
  97. }
  98. if err := actions.loadRepoOwner(ctx, userMap); err != nil {
  99. return err
  100. }
  101. if err := actions.LoadIssues(ctx); err != nil {
  102. return err
  103. }
  104. return actions.LoadComments(ctx)
  105. }
  106. func (actions ActionList) LoadComments(ctx context.Context) error {
  107. if len(actions) == 0 {
  108. return nil
  109. }
  110. commentIDs := make([]int64, 0, len(actions))
  111. for _, action := range actions {
  112. if action.CommentID > 0 {
  113. commentIDs = append(commentIDs, action.CommentID)
  114. }
  115. }
  116. if len(commentIDs) == 0 {
  117. return nil
  118. }
  119. commentsMap := make(map[int64]*issues_model.Comment, len(commentIDs))
  120. if err := db.GetEngine(ctx).In("id", commentIDs).Find(&commentsMap); err != nil {
  121. return fmt.Errorf("find comment: %w", err)
  122. }
  123. for _, action := range actions {
  124. if action.CommentID > 0 {
  125. action.Comment = commentsMap[action.CommentID]
  126. if action.Comment != nil {
  127. action.Comment.Issue = action.Issue
  128. }
  129. }
  130. }
  131. return nil
  132. }
  133. func (actions ActionList) LoadIssues(ctx context.Context) error {
  134. if len(actions) == 0 {
  135. return nil
  136. }
  137. conditions := builder.NewCond()
  138. issueNum := 0
  139. for _, action := range actions {
  140. if action.IsIssueEvent() {
  141. infos := action.GetIssueInfos()
  142. if len(infos) == 0 {
  143. continue
  144. }
  145. index, _ := strconv.ParseInt(infos[0], 10, 64)
  146. if index > 0 {
  147. conditions = conditions.Or(builder.Eq{
  148. "repo_id": action.RepoID,
  149. "`index`": index,
  150. })
  151. issueNum++
  152. }
  153. }
  154. }
  155. if !conditions.IsValid() {
  156. return nil
  157. }
  158. issuesMap := make(map[string]*issues_model.Issue, issueNum)
  159. issues := make([]*issues_model.Issue, 0, issueNum)
  160. if err := db.GetEngine(ctx).Where(conditions).Find(&issues); err != nil {
  161. return fmt.Errorf("find issue: %w", err)
  162. }
  163. for _, issue := range issues {
  164. issuesMap[fmt.Sprintf("%d-%d", issue.RepoID, issue.Index)] = issue
  165. }
  166. for _, action := range actions {
  167. if !action.IsIssueEvent() {
  168. continue
  169. }
  170. if index := action.getIssueIndex(); index > 0 {
  171. if issue, ok := issuesMap[fmt.Sprintf("%d-%d", action.RepoID, index)]; ok {
  172. action.Issue = issue
  173. action.Issue.Repo = action.Repo
  174. }
  175. }
  176. }
  177. return nil
  178. }
  179. // GetFeeds returns actions according to the provided options
  180. func GetFeeds(ctx context.Context, opts GetFeedsOptions) (ActionList, int64, error) {
  181. if opts.RequestedUser == nil && opts.RequestedTeam == nil && opts.RequestedRepo == nil {
  182. return nil, 0, errors.New("need at least one of these filters: RequestedUser, RequestedTeam, RequestedRepo")
  183. }
  184. var err error
  185. var cond builder.Cond
  186. // if the actor is the requested user or is an administrator, we can skip the ActivityQueryCondition
  187. if opts.Actor != nil && opts.RequestedUser != nil && (opts.Actor.IsAdmin || opts.Actor.ID == opts.RequestedUser.ID) {
  188. cond = builder.Eq{
  189. "user_id": opts.RequestedUser.ID,
  190. }.And(
  191. FeedDateCond(opts),
  192. )
  193. if !opts.IncludeDeleted {
  194. cond = cond.And(builder.Eq{"is_deleted": false})
  195. }
  196. if !opts.IncludePrivate {
  197. cond = cond.And(builder.Eq{"is_private": false})
  198. }
  199. if opts.OnlyPerformedBy {
  200. cond = cond.And(builder.Eq{"act_user_id": opts.RequestedUser.ID})
  201. }
  202. } else {
  203. cond, err = ActivityQueryCondition(ctx, opts)
  204. if err != nil {
  205. return nil, 0, err
  206. }
  207. }
  208. actions := make([]*Action, 0, opts.PageSize)
  209. var count int64
  210. opts.SetDefaultValues()
  211. if opts.Page < 10 { // TODO: why it's 10 but other values? It's an experience value.
  212. sess := db.GetEngine(ctx).Where(cond)
  213. sess = db.SetSessionPagination(sess, &opts)
  214. if opts.DontCount {
  215. err = sess.Desc("`action`.created_unix").Find(&actions)
  216. } else {
  217. count, err = sess.Desc("`action`.created_unix").FindAndCount(&actions)
  218. }
  219. if err != nil {
  220. return nil, 0, fmt.Errorf("FindAndCount: %w", err)
  221. }
  222. } else {
  223. // First, only query which IDs are necessary, and only then query all actions to speed up the overall query
  224. sess := db.GetEngine(ctx).Where(cond).Select("`action`.id")
  225. sess = db.SetSessionPagination(sess, &opts)
  226. actionIDs := make([]int64, 0, opts.PageSize)
  227. if err := sess.Table("action").Desc("`action`.created_unix").Find(&actionIDs); err != nil {
  228. return nil, 0, fmt.Errorf("Find(actionsIDs): %w", err)
  229. }
  230. if !opts.DontCount {
  231. count, err = db.GetEngine(ctx).Where(cond).
  232. Table("action").
  233. Cols("`action`.id").Count()
  234. if err != nil {
  235. return nil, 0, fmt.Errorf("Count: %w", err)
  236. }
  237. }
  238. if err := db.GetEngine(ctx).In("`action`.id", actionIDs).Desc("`action`.created_unix").Find(&actions); err != nil {
  239. return nil, 0, fmt.Errorf("Find: %w", err)
  240. }
  241. }
  242. if err := ActionList(actions).LoadAttributes(ctx); err != nil {
  243. return nil, 0, fmt.Errorf("LoadAttributes: %w", err)
  244. }
  245. return actions, count, nil
  246. }
  247. func CountUserFeeds(ctx context.Context, userID int64) (int64, error) {
  248. return db.GetEngine(ctx).Where("user_id = ?", userID).
  249. And("is_deleted = ?", false).
  250. Count(&Action{})
  251. }