Generate system note after Task item has been updated on Issue or Merge Request. #2296

Everytime the User check or uncheck a Task Item from the Issue or
Merge Request description, a new update is going to be
added to the activity logs of the Issue or Merge Request.

Note that when using the edit form, you can only update the Task item
status or add/delete/modify existing ones. Doing both actions is not
fully supported.
This commit is contained in:
Ruben Davila 2015-10-22 10:18:59 -05:00 committed by Rubén Dávila
parent a8e7bee3a6
commit 97afb84b31
9 changed files with 181 additions and 24 deletions

View file

@ -151,4 +151,9 @@ module Issuable
def notes_with_associations
notes.includes(:author, :project)
end
def updated_tasks
Taskable.get_updated_tasks(old_content: previous_changes['description'].first,
new_content: description)
end
end

View file

@ -7,14 +7,37 @@ require 'task_list/filter'
#
# Used by MergeRequest and Issue
module Taskable
ITEM_PATTERN = /
^
(?:\s*[-+*]|(?:\d+\.))? # optional list prefix
\s* # optional whitespace prefix
(\[\s\]|\[[xX]\]) # checkbox
(\s.+) # followed by whitespace and some text.
/x
def self.get_tasks(content)
content.to_s.scan(ITEM_PATTERN).map do |checkbox, label|
# ITEM_PATTERN strips out the hyphen, but Item requires it. Rabble rabble.
TaskList::Item.new("- #{checkbox}", label.strip)
end
end
def self.get_updated_tasks(old_content:, new_content:)
old_tasks, new_tasks = get_tasks(old_content), get_tasks(new_content)
new_tasks.select.with_index do |new_task, i|
old_task = old_tasks[i]
next unless old_task
new_task.source == new_task.source && new_task.complete? != old_task.complete?
end
end
# Called by `TaskList::Summary`
def task_list_items
return [] if description.blank?
@task_list_items ||= description.scan(TaskList::Filter::ItemPattern).collect do |item|
# ItemPattern strips out the hyphen, but Item requires it. Rabble rabble.
TaskList::Item.new("- #{item}")
end
@task_list_items ||= Taskable.get_tasks(description)
end
def tasks

View file

@ -27,6 +27,12 @@ class IssuableBaseService < BaseService
old_branch, new_branch)
end
def create_task_status_note(issuable)
issuable.updated_tasks.each do |task|
SystemNoteService.change_task_status(issuable, issuable.project, current_user, task)
end
end
def filter_params(issuable_ability_name = :issue)
params[:assignee_id] = "" if params[:assignee_id] == IssuableFinder::NONE
params[:milestone_id] = "" if params[:milestone_id] == IssuableFinder::NONE
@ -55,6 +61,7 @@ class IssuableBaseService < BaseService
old_labels - issuable.labels)
end
handle_common_system_notes(issuable)
handle_changes(issuable)
issuable.create_new_cross_references!(current_user)
execute_hooks(issuable, 'update')
@ -71,4 +78,14 @@ class IssuableBaseService < BaseService
close_service.new(project, current_user, {}).execute(issuable)
end
end
def handle_common_system_notes(issuable)
if issuable.previous_changes.include?('title')
create_title_change_note(issuable, issuable.previous_changes['title'].first)
end
if issuable.previous_changes.include?('description') && issuable.tasks?
create_task_status_note(issuable)
end
end
end

View file

@ -13,10 +13,6 @@ module Issues
create_assignee_note(issue)
notification_service.reassigned_issue(issue, current_user)
end
if issue.previous_changes.include?('title')
create_title_change_note(issue, issue.previous_changes['title'].first)
end
end
def reopen_service

View file

@ -30,10 +30,6 @@ module MergeRequests
notification_service.reassigned_merge_request(merge_request, current_user)
end
if merge_request.previous_changes.include?('title')
create_title_change_note(merge_request, merge_request.previous_changes['title'].first)
end
if merge_request.previous_changes.include?('target_branch') ||
merge_request.previous_changes.include?('source_branch')
merge_request.mark_as_unchecked

View file

@ -341,4 +341,21 @@ class SystemNoteService
"* #{commit_ids} - #{commits_text} from branch `#{branch}`\n"
end
# Called when the status of a Task has changed
#
# noteable - Noteable object.
# project - Project owning noteable
# author - User performing the change
# new_task - TaskList::Item object.
#
# Example Note text:
#
# "Soandso marked the task Whatever as completed."
#
# Returns the created Note object
def self.change_task_status(noteable, project, author, new_task)
body = "Marked the task **#{new_task.source}** as #{new_task.status_label}"
create_note(noteable: noteable, project: project, author: author, note: body)
end
end

View file

@ -0,0 +1,12 @@
require 'task_list'
class TaskList
class Item
COMPLETED = 'completed'.freeze
INCOMPLETE = 'incomplete'.freeze
def status_label
complete? ? COMPLETED : INCOMPLETE
end
end
end

View file

@ -15,6 +15,17 @@ describe Issues::UpdateService do
end
describe 'execute' do
def find_note(starting_with)
@issue.notes.find do |note|
note && note.note.start_with?(starting_with)
end
end
def update_issue(opts)
@issue = Issues::UpdateService.new(project, user, opts).execute(issue)
@issue.reload
end
context "valid params" do
before do
opts = {
@ -44,12 +55,6 @@ describe Issues::UpdateService do
expect(email.subject).to include(issue.title)
end
def find_note(starting_with)
@issue.notes.find do |note|
note && note.note.start_with?(starting_with)
end
end
it 'should create system note about issue reassign' do
note = find_note('Reassigned to')
@ -71,5 +76,52 @@ describe Issues::UpdateService do
expect(note.note).to eq 'Title changed from **Old title** to **New title**'
end
end
context 'when Issue has tasks' do
before { update_issue({ description: "- [ ] Task 1\n- [ ] Task 2" }) }
it { expect(@issue.tasks?).to eq(true) }
context 'when tasks are marked as completed' do
before { update_issue({ description: "- [x] Task 1\n- [X] Task 2" }) }
it 'creates system note about task status change' do
note1 = find_note('Marked the task **Task 1** as completed')
note2 = find_note('Marked the task **Task 2** as completed')
expect(note1).not_to be_nil
expect(note2).not_to be_nil
end
end
context 'when tasks are marked as incomplete' do
before do
update_issue({ description: "- [x] Task 1\n- [X] Task 2" })
update_issue({ description: "- [ ] Task 1\n- [ ] Task 2" })
end
it 'creates system note about task status change' do
note1 = find_note('Marked the task **Task 1** as incomplete')
note2 = find_note('Marked the task **Task 2** as incomplete')
expect(note1).not_to be_nil
expect(note2).not_to be_nil
end
end
context 'when tasks position has been modified' do
before do
update_issue({ description: "- [x] Task 1\n- [X] Task 2" })
update_issue({ description: "- [x] Task 1\n- [ ] Task 3\n- [ ] Task 2" })
end
it 'does not create a system note' do
note = find_note('Marked the task **Task 2** as incomplete')
expect(note).to be_nil
end
end
end
end
end

View file

@ -14,6 +14,17 @@ describe MergeRequests::UpdateService do
end
describe 'execute' do
def find_note(starting_with)
@merge_request.notes.find do |note|
note && note.note.start_with?(starting_with)
end
end
def update_merge_request(opts)
@merge_request = MergeRequests::UpdateService.new(project, user, opts).execute(merge_request)
@merge_request.reload
end
context 'valid params' do
let(:opts) do
{
@ -56,12 +67,6 @@ describe MergeRequests::UpdateService do
expect(email.subject).to include(merge_request.title)
end
def find_note(starting_with)
@merge_request.notes.find do |note|
note && note.note.start_with?(starting_with)
end
end
it 'should create system note about merge_request reassign' do
note = find_note('Reassigned to')
@ -90,5 +95,39 @@ describe MergeRequests::UpdateService do
expect(note.note).to eq 'Target branch changed from `master` to `target`'
end
end
context 'when MergeRequest has tasks' do
before { update_merge_request({ description: "- [ ] Task 1\n- [ ] Task 2" }) }
it { expect(@merge_request.tasks?).to eq(true) }
context 'when tasks are marked as completed' do
before { update_merge_request({ description: "- [x] Task 1\n- [X] Task 2" }) }
it 'creates system note about task status change' do
note1 = find_note('Marked the task **Task 1** as completed')
note2 = find_note('Marked the task **Task 2** as completed')
expect(note1).not_to be_nil
expect(note2).not_to be_nil
end
end
context 'when tasks are marked as incomplete' do
before do
update_merge_request({ description: "- [x] Task 1\n- [X] Task 2" })
update_merge_request({ description: "- [ ] Task 1\n- [ ] Task 2" })
end
it 'creates system note about task status change' do
note1 = find_note('Marked the task **Task 1** as incomplete')
note2 = find_note('Marked the task **Task 2** as incomplete')
expect(note1).not_to be_nil
expect(note2).not_to be_nil
end
end
end
end
end