| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291 |
- // Copyright 2018 The Gitea Authors. All rights reserved.
- // SPDX-License-Identifier: MIT
-
- package activities
-
- import (
- "context"
- "errors"
- "fmt"
- "strconv"
-
- "code.gitea.io/gitea/models/db"
- issues_model "code.gitea.io/gitea/models/issues"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/container"
- "code.gitea.io/gitea/modules/util"
-
- "xorm.io/builder"
- )
-
- // ActionList defines a list of actions
- type ActionList []*Action
-
- func (actions ActionList) getUserIDs() []int64 {
- return container.FilterSlice(actions, func(action *Action) (int64, bool) {
- return action.ActUserID, true
- })
- }
-
- func (actions ActionList) LoadActUsers(ctx context.Context) (map[int64]*user_model.User, error) {
- if len(actions) == 0 {
- return nil, nil
- }
-
- userIDs := actions.getUserIDs()
- userMaps := make(map[int64]*user_model.User, len(userIDs))
- err := db.GetEngine(ctx).
- In("id", userIDs).
- Find(&userMaps)
- if err != nil {
- return nil, fmt.Errorf("find user: %w", err)
- }
-
- for _, action := range actions {
- action.ActUser = userMaps[action.ActUserID]
- }
- return userMaps, nil
- }
-
- func (actions ActionList) getRepoIDs() []int64 {
- return container.FilterSlice(actions, func(action *Action) (int64, bool) {
- return action.RepoID, true
- })
- }
-
- func (actions ActionList) LoadRepositories(ctx context.Context) error {
- if len(actions) == 0 {
- return nil
- }
-
- repoIDs := actions.getRepoIDs()
- repoMaps := make(map[int64]*repo_model.Repository, len(repoIDs))
- err := db.GetEngine(ctx).In("id", repoIDs).Find(&repoMaps)
- if err != nil {
- return fmt.Errorf("find repository: %w", err)
- }
- for _, action := range actions {
- action.Repo = repoMaps[action.RepoID]
- }
- repos := repo_model.RepositoryList(util.ValuesOfMap(repoMaps))
- return repos.LoadUnits(ctx)
- }
-
- func (actions ActionList) loadRepoOwner(ctx context.Context, userMap map[int64]*user_model.User) (err error) {
- if userMap == nil {
- userMap = make(map[int64]*user_model.User)
- }
-
- missingUserIDs := container.FilterSlice(actions, func(action *Action) (int64, bool) {
- if action.Repo == nil {
- return 0, false
- }
- _, alreadyLoaded := userMap[action.Repo.OwnerID]
- return action.Repo.OwnerID, !alreadyLoaded
- })
- if len(missingUserIDs) == 0 {
- return nil
- }
-
- if err := db.GetEngine(ctx).
- In("id", missingUserIDs).
- 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]
- }
- }
-
- return nil
- }
-
- // 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)
- if err != nil {
- return err
- }
- 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 {
- return err
- }
- 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)
- }
- }
- if len(commentIDs) == 0 {
- return nil
- }
-
- 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
- }
-
- // 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, errors.New("need at least one of these filters: RequestedUser, RequestedTeam, RequestedRepo")
- }
-
- var err error
- var cond builder.Cond
- // if the actor is the requested user or is an administrator, we can skip the ActivityQueryCondition
- if opts.Actor != nil && opts.RequestedUser != nil && (opts.Actor.IsAdmin || opts.Actor.ID == opts.RequestedUser.ID) {
- cond = builder.Eq{
- "user_id": opts.RequestedUser.ID,
- }.And(
- FeedDateCond(opts),
- )
-
- if !opts.IncludeDeleted {
- cond = cond.And(builder.Eq{"is_deleted": false})
- }
-
- if !opts.IncludePrivate {
- cond = cond.And(builder.Eq{"is_private": false})
- }
- if opts.OnlyPerformedBy {
- cond = cond.And(builder.Eq{"act_user_id": opts.RequestedUser.ID})
- }
- } else {
- 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)
-
- if opts.DontCount {
- err = sess.Desc("`action`.created_unix").Find(&actions)
- } else {
- 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)
- }
-
- if !opts.DontCount {
- 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
- }
-
- func CountUserFeeds(ctx context.Context, userID int64) (int64, error) {
- return db.GetEngine(ctx).Where("user_id = ?", userID).
- And("is_deleted = ?", false).
- Count(&Action{})
- }
|