todo_service.rb 5.2 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_project_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 175 176 177 178 179 180
    mentioned_users = target.mentioned_users.select do |user|
      user.can?(:read_project, project)
    end

    mentioned_users.delete(author)
    mentioned_users.uniq
  end

181 182 183
  def pending_todos(user, criteria = {})
    valid_keys = [:project_id, :target_id, :target_type, :commit_id]
    user.todos.pending.where(criteria.slice(*valid_keys))
184
  end
185
end