todo_service.rb 5.6 KB
Newer Older
1
# TodoService class
2
#
3
# Used for creating todos after certain user actions
4 5
#
# Ex.
6
#   TodoService.new.new_issue(issue, current_user)
7
#
8
class TodoService
9 10
  # When create an issue we should:
  #
11 12
  #  * create a todo for assignee if issue is assigned
  #  * create a todo for each mentioned user on issue
13 14
  #
  def new_issue(issue, current_user)
15
    new_issuable(issue, current_user)
16 17
  end

18 19
  # When update an issue we should:
  #
20
  #  * mark all pending todos related to the issue for the current user as done
21 22
  #
  def update_issue(issue, current_user)
23
    create_mention_todos(issue.project, issue, current_user)
24 25
  end

26 27
  # When close an issue we should:
  #
28
  #  * mark all pending todos related to the target for the current user as done
29 30
  #
  def close_issue(issue, current_user)
31
    mark_pending_todos_as_done(issue, current_user)
32 33
  end

34 35
  # When we reassign an issue we should:
  #
36
  #  * create a pending todo for new assignee if issue is assigned
37 38
  #
  def reassigned_issue(issue, current_user)
39
    create_assignment_todo(issue, current_user)
40 41 42 43
  end

  # When create a merge request we should:
  #
44 45
  #  * creates a pending todo for assignee if merge request is assigned
  #  * create a todo for each mentioned user on merge request
46 47 48 49 50
  #
  def new_merge_request(merge_request, current_user)
    new_issuable(merge_request, current_user)
  end

51 52
  # When update a merge request we should:
  #
53
  #  * create a todo for each mentioned user on merge request
54 55
  #
  def update_merge_request(merge_request, current_user)
56
    create_mention_todos(merge_request.project, merge_request, current_user)
57 58
  end

59 60
  # When close a merge request we should:
  #
61
  #  * mark all pending todos related to the target for the current user as done
62 63
  #
  def close_merge_request(merge_request, current_user)
64
    mark_pending_todos_as_done(merge_request, current_user)
65 66 67
  end

  # When we reassign a merge request we should:
68
  #
69
  #  * creates a pending todo for new assignee if merge request is assigned
70 71
  #
  def reassigned_merge_request(merge_request, current_user)
72
    create_assignment_todo(merge_request, current_user)
73 74
  end

75 76
  # When merge a merge request we should:
  #
77
  #  * mark all pending todos related to the target for the current user as done
78 79
  #
  def merge_merge_request(merge_request, current_user)
80
    mark_pending_todos_as_done(merge_request, current_user)
81 82
  end

83 84
  # When create a note we should:
  #
85 86
  #  * mark all pending todos related to the noteable for the note author as done
  #  * create a todo for each mentioned user on note
87
  #
88 89
  def new_note(note, current_user)
    handle_note(note, current_user)
90 91
  end

92 93
  # When update a note we should:
  #
94 95
  #  * mark all pending todos related to the noteable for the current user as done
  #  * create a todo for each new user mentioned on note
96 97
  #
  def update_note(note, current_user)
98 99 100
    handle_note(note, current_user)
  end

101
  # When marking pending todos as done we should:
102
  #
103
  #  * mark all pending todos related to the target for the current user as done
104
  #
105
  def mark_pending_todos_as_done(target, user)
106 107
    attributes = attributes_for_target(target)
    pending_todos(user, attributes).update_all(state: :done)
108 109 110 111
  end

  private

112
  def create_todos(users, attributes)
113
    Array(users).each do |user|
114 115
      next if pending_todos(user, attributes).exists?
      Todo.create(attributes.merge(user_id: user.id))
116 117 118 119
    end
  end

  def new_issuable(issuable, author)
120 121
    create_assignment_todo(issuable, author)
    create_mention_todos(issuable.project, issuable, author)
122 123 124
  end

  def handle_note(note, author)
125
    # Skip system notes, and notes on project snippet
126
    return if note.system? || note.for_snippet?
127

128 129
    project = note.project
    target  = note.noteable
130

131 132
    mark_pending_todos_as_done(target, author)
    create_mention_todos(project, target, author, note)
133
  end
134

135
  def create_assignment_todo(issuable, author)
136
    if issuable.assignee && issuable.assignee != author
137 138
      attributes = attributes_for_todo(issuable.project, issuable, author, Todo::ASSIGNED)
      create_todos(issuable.assignee, attributes)
139 140 141
    end
  end

142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
  def create_mention_todos(project, target, author, note = nil)
    mentioned_users = filter_mentioned_users(project, note || target, author)
    attributes = attributes_for_todo(project, target, author, Todo::MENTIONED, note)
    create_todos(mentioned_users, attributes)
  end

  def attributes_for_target(target)
    attributes = {
      project_id: target.project.id,
      target_id: target.id,
      target_type: target.class.name,
      commit_id: nil
    }

    if target.is_a?(Commit)
      attributes.merge!(target_id: nil, commit_id: target.id)
    end

    attributes
  end

  def attributes_for_todo(project, target, author, action, note = nil)
    attributes_for_target(target).merge!(
      project_id: project.id,
      author_id: author.id,
      action: action,
      note: note
    )
170
  end
171

172
  def filter_mentioned_users(project, target, author)
173 174
    mentioned_users = target.mentioned_users
    mentioned_users = reject_users_without_access(mentioned_users, project, target)
175 176 177 178
    mentioned_users.delete(author)
    mentioned_users.uniq
  end

179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196
  def reject_users_without_access(users, project, target)
    if target.is_a?(Note) && target.for_issue?
      target = target.noteable
    end

    if target.is_a?(Issue)
      select_users(users, :read_issue, target)
    else
      select_users(users, :read_project, project)
    end
  end

  def select_users(users, ability, subject)
    users.select do |user|
      user.can?(ability.to_sym, subject)
    end
  end

197 198 199
  def pending_todos(user, criteria = {})
    valid_keys = [:project_id, :target_id, :target_type, :commit_id]
    user.todos.pending.where(criteria.slice(*valid_keys))
200
  end
201
end